185 lines
5.7 KiB
Markdown
185 lines
5.7 KiB
Markdown
# 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'`)
|