Changeset 0.17.0 (#75)
This commit is contained in:
184
docs/DESIGN-0.17.0.md
Normal file
184
docs/DESIGN-0.17.0.md
Normal file
@@ -0,0 +1,184 @@
|
||||
# DESIGN-0.17.0 — Persona-KB Binding + Enterprise KB Mode
|
||||
|
||||
## Overview
|
||||
|
||||
Personas become **gateways** to knowledge. In enterprise deployments,
|
||||
users don't interact with KBs directly — they talk to Personas that
|
||||
have KBs attached. This is the core differentiator for enterprise use.
|
||||
|
||||
Depends on: knowledge bases (v0.14.0), user groups (v0.16.0).
|
||||
|
||||
**Design principle: Personas carry context, not users.** When a user
|
||||
selects a Persona with bound KBs, the KB context flows automatically —
|
||||
no manual KB selection, no toggle management, no confusion about which
|
||||
KBs are in scope. Admins curate the KB↔Persona relationships; users
|
||||
just pick a Persona.
|
||||
|
||||
---
|
||||
|
||||
## 1. Schema
|
||||
|
||||
### `persona_knowledge_bases` (new join table)
|
||||
|
||||
```sql
|
||||
CREATE TABLE persona_knowledge_bases (
|
||||
persona_id UUID NOT NULL REFERENCES personas(id) ON DELETE CASCADE,
|
||||
kb_id UUID NOT NULL REFERENCES knowledge_bases(id) ON DELETE CASCADE,
|
||||
auto_search BOOLEAN NOT NULL DEFAULT false,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
PRIMARY KEY (persona_id, kb_id)
|
||||
);
|
||||
```
|
||||
|
||||
`auto_search` controls whether the KB is searched automatically on every
|
||||
message (top-K results prepended to context) or only via explicit
|
||||
`kb_search` tool calls. Default `false` = tool-only.
|
||||
|
||||
### `knowledge_bases.discoverable` (new column)
|
||||
|
||||
```sql
|
||||
ALTER TABLE knowledge_bases ADD COLUMN discoverable BOOLEAN NOT NULL DEFAULT true;
|
||||
```
|
||||
|
||||
When `false`, the KB does not appear in user-facing listings
|
||||
(`ListDiscoverableKBs`). It remains searchable through Persona bindings.
|
||||
|
||||
### `platform_policies.kb_direct_access` (new row)
|
||||
|
||||
Seeded as `'true'` (permissive default). When set to `'false'`, the
|
||||
channel KB popup is hidden — users access KBs exclusively through
|
||||
Persona bindings.
|
||||
|
||||
---
|
||||
|
||||
## 2. Store Layer
|
||||
|
||||
### `PersonaStore` additions
|
||||
|
||||
- `SetKBs(ctx, personaID, kbIDs, autoSearch)` — UPSERT join table
|
||||
- `GetKBs(ctx, personaID)` — returns `[]PersonaKB` with KB metadata
|
||||
|
||||
### `KnowledgeBaseStore` additions
|
||||
|
||||
- `ListDiscoverable(ctx, userID, teamIDs)` — KBs where `discoverable=true`
|
||||
and user has access via ownership, team, or global scope
|
||||
- `SetDiscoverable(ctx, kbID, discoverable)` — toggle visibility
|
||||
- `UpdateDocumentStorageKey(ctx, docID, key)` — moved from raw SQL
|
||||
|
||||
---
|
||||
|
||||
## 3. Completion Pipeline
|
||||
|
||||
### KB scoping in `BuildKBHint`
|
||||
|
||||
When a Persona has bound KBs, `BuildKBHint` merges them with any
|
||||
channel-attached KBs:
|
||||
|
||||
1. Load channel KBs (existing path)
|
||||
2. Load Persona KBs via `PersonaStore.GetKBs()`
|
||||
3. Union the two sets (deduplicated by KB ID)
|
||||
4. Build the hint string with listing
|
||||
|
||||
### Persona ID threading
|
||||
|
||||
The `personaID` is threaded through the completion handler methods:
|
||||
`Complete()` → `streamCompletion(…, personaID)` / `syncCompletion(…, personaID)`
|
||||
→ `loadConversation(…, personaID)`.
|
||||
|
||||
The `ExecutionContext` for tool calls carries the Persona ID so
|
||||
`kb_search` can auto-include Persona-bound KBs in its search scope.
|
||||
|
||||
---
|
||||
|
||||
## 4. Role Fallback Alerts (issue #69)
|
||||
|
||||
When a role's primary provider fails:
|
||||
|
||||
1. **Attempt fallback** — existing behavior, unchanged.
|
||||
2. **Emit event** — `role.fallback` on EventBus (DirToClient).
|
||||
3. **Cooldown** — 5-minute per-role TTL prevents flooding.
|
||||
4. **Admin toast** — frontend listens for `role.fallback` events,
|
||||
shows warning/error toast to admin users only.
|
||||
|
||||
### Resolver changes
|
||||
|
||||
```go
|
||||
type Resolver struct {
|
||||
stores store.Stores
|
||||
vault *crypto.KeyResolver
|
||||
bus *events.Bus
|
||||
mu sync.Mutex
|
||||
cooldown map[string]time.Time // role → last alert time
|
||||
}
|
||||
```
|
||||
|
||||
`WithBus(bus)` builder method. Nil-safe — if no bus is attached,
|
||||
alerts are suppressed (log only).
|
||||
|
||||
---
|
||||
|
||||
## 5. Security Fixes
|
||||
|
||||
### ResolvePreset bypass
|
||||
|
||||
`ResolvePreset()` in `presets.go` used raw SQL that skipped the
|
||||
`resource_grants` table added in v0.16.0. A Persona accessible only
|
||||
via group grant would fail at completion time. Fixed to use
|
||||
`PersonaStore.GetByID()` + `UserCanAccess()`.
|
||||
|
||||
### KB create scope authorization
|
||||
|
||||
`POST /api/v1/knowledge-bases` now enforces:
|
||||
- `scope=global` → requires admin role
|
||||
- `scope=team` → requires team admin role for the target team
|
||||
- `scope=personal` → open to all authenticated users
|
||||
|
||||
---
|
||||
|
||||
## 6. Frontend
|
||||
|
||||
### KB Picker (`persona-kb.js`)
|
||||
|
||||
Standalone component: `renderPersonaKBPicker(container, opts)` → control
|
||||
object with `getValues()`, `setValues()`, `clear()`, `refresh()`.
|
||||
|
||||
Options: `scope` ('admin'|'team'|'personal'), `teamId`, `prefix`.
|
||||
|
||||
Shows checkboxes for available KBs with doc/chunk counts. Selected KBs
|
||||
show an "Auto-inject" toggle for the `auto_search` flag.
|
||||
|
||||
### Integration points
|
||||
|
||||
- Admin preset form: KB picker below the form, load/save bindings on
|
||||
edit/create via `/api/v1/admin/presets/:id/knowledge-bases`.
|
||||
- Team preset form: same pattern via team API prefix.
|
||||
- User preset form: shows discoverable KBs only.
|
||||
- Admin settings: `kb_direct_access` toggle checkbox.
|
||||
|
||||
### Fallback alert
|
||||
|
||||
Admin users receive `UI.toast()` notifications when `role.fallback`
|
||||
events arrive via WebSocket.
|
||||
|
||||
---
|
||||
|
||||
## 7. API Endpoints (new)
|
||||
|
||||
| Method | Path | Description |
|
||||
|--------|------|-------------|
|
||||
| GET | `/api/v1/presets/:id/knowledge-bases` | List KBs bound to a persona |
|
||||
| PUT | `/api/v1/presets/:id/knowledge-bases` | Set persona-KB bindings |
|
||||
| GET | `/api/v1/admin/presets/:id/knowledge-bases` | Admin: list persona KBs |
|
||||
| PUT | `/api/v1/admin/presets/:id/knowledge-bases` | Admin: set persona-KB bindings |
|
||||
| GET | `/api/v1/knowledge-bases-discoverable` | List discoverable KBs for user |
|
||||
| PUT | `/api/v1/knowledge-bases/:id/discoverable` | Toggle KB discoverability |
|
||||
|
||||
---
|
||||
|
||||
## 8. Migration
|
||||
|
||||
Single migration file: `002_v017_persona_kb.sql`
|
||||
|
||||
- Creates `persona_knowledge_bases` table
|
||||
- Adds `discoverable` column to `knowledge_bases`
|
||||
- Seeds `kb_direct_access` platform policy (default `'true'`)
|
||||
Reference in New Issue
Block a user