From e8077a44b4446c7de212d2f4f4e9530a38e14549 Mon Sep 17 00:00:00 2001 From: Till JS Date: Wed, 15 Apr 2026 22:17:04 +0200 Subject: [PATCH] docs: flesh out Multi-Agent plan shipping log + Team Workbench forward plan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Multi-Agent Workbench shipped end-to-end (commits 1771063df through 7c89eb625). This commit turns the plan doc into a proper history + post- mortem and captures the deferred Team-Workbench as its own forward plan so the architectural breadcrumbs don't rot. docs/plans/multi-agent-workbench.md: - Status bumped to ✅ Shipped; every phase checkbox flipped. - Open-questions section rewritten with the decisions that were actually made (name-unique via store write-time check, per-source system principalIds, policy fully migrated, scene binding default- empty with smart suggestion). - New "Shipping-Historie" table mapping each phase to its commit, the number of files touched, and the test outcome. - New "Lessons Learnt + Follow-Up Ideen" with: * What went better than expected (L3 Actor cutover, getOrCreate instead of unique index, displayName caching) * Thin spots worth revisiting (avatar not on Actor, missing token counter for budget, no missions list on agent detail, no drag-reassign, scene binding doesn't drive filters yet) * Five deferred follow-up projects (team features, agent memory self-update, agent-to-agent messaging, meta-planner, per-agent encryption domains) docs/plans/team-workbench.md (NEW): - Full forward-looking plan for the deferred Team-Workbench. - Two use-cases (human multi-user vs multi-agent sharing team context) with the observation that they share the same infra. - Decision candidates table (still open — meant as T0 RFC fodder, not baked in). - Architecture sketch with data-model deltas over the current single-user shape. - Encryption subsection dedicated to the hardest problems: team-key wrapping per member (reuses Mission-Grant pattern), member-removal rotation (lazy vs eager), Zero-Knowledge-mode incompatibility. - T0..T6 phasing (~7 weeks for a clean first-pass). - Section "Wie Multi-Agent dafür den Weg geebnet hat" enumerating the four invariants the shipped Phase 0-7 deliberately preserved to make this plan cheap when it lands. docs/plans/README.md (NEW): - Index doc with the AI/Workbench roadmap as an ASCII flow so future contributors can locate themselves in the sequence without reading three 400-line plans first. docs/future/AI_AGENTS_IDEAS.md: - Header marks Point 1 (encrypted tables) as shipped via the Mission Grant plan; points 2-8 stay relevant. Cross-link to all three plan docs so this stays the go-to backlog. services/mana-ai/CLAUDE.md: - Design-context header expanded to link to all four related docs (arch §20-22, both shipped plans, forward team plan, ideas backlog). No code changes. Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/future/AI_AGENTS_IDEAS.md | 9 + docs/plans/README.md | 51 ++++++ docs/plans/multi-agent-workbench.md | 150 +++++++++++------ docs/plans/news-research-module.md | 141 ++++++++++++++++ docs/plans/team-workbench.md | 250 ++++++++++++++++++++++++++++ services/mana-ai/CLAUDE.md | 7 +- 6 files changed, 554 insertions(+), 54 deletions(-) create mode 100644 docs/plans/README.md create mode 100644 docs/plans/news-research-module.md create mode 100644 docs/plans/team-workbench.md diff --git a/docs/future/AI_AGENTS_IDEAS.md b/docs/future/AI_AGENTS_IDEAS.md index 39eb7a45f..badea94ec 100644 --- a/docs/future/AI_AGENTS_IDEAS.md +++ b/docs/future/AI_AGENTS_IDEAS.md @@ -4,6 +4,15 @@ Backlog für das AI-Workbench / Mission-Runner-System (`services/mana-ai`, `@man Kontext: [`services/mana-ai/CLAUDE.md`](../../services/mana-ai/CLAUDE.md), [`docs/architecture/COMPANION_BRAIN_ARCHITECTURE.md`](../architecture/COMPANION_BRAIN_ARCHITECTURE.md). +Was davon schon gelandet ist: +- **Punkt 1 (Encrypted-Tables serverseitig)** → Mission-Key-Grant ausgerollt. Plan-Doc: [`../plans/ai-mission-key-grant.md`](../plans/ai-mission-key-grant.md). +- **Named Agents + per-Agent-Policy + Budget + Observability** (war nicht auf dieser Liste, kam aus den Design-Gesprächen) → Multi-Agent Workbench ausgerollt. Plan-Doc: [`../plans/multi-agent-workbench.md`](../plans/multi-agent-workbench.md). + +Was aus dieser Liste noch offen ist: Punkte 2, 3, 4, 5, 6, 7, 8 unten — alle weiterhin relevant. + +Forward-looking Plans: +- **Team-Workbench** (Multi-User + geteilter AI-Kontext): [`../plans/team-workbench.md`](../plans/team-workbench.md). + Sortiert grob nach Impact. --- diff --git a/docs/plans/README.md b/docs/plans/README.md new file mode 100644 index 000000000..4664e83f9 --- /dev/null +++ b/docs/plans/README.md @@ -0,0 +1,51 @@ +# Plans + +Design + rollout plans, grouped by topic. Plans are long-form docs with +baked-in decisions, phasing, open questions, and (when shipped) a +history section with commit refs. + +## AI / Workbench roadmap + +The Mana AI Workbench has evolved in three successive planned waves — +each one laying foundations the next one relies on: + +``` + User hat einen Companion (v0 — shipped before these docs) + │ + ▼ + AI Missions + Proposals + Policy + Revert + │ + ▼ + Mission Key-Grants ← ai-mission-key-grant.md ✅ + (encrypted inputs decryptable by the server runner) + │ + ▼ + Multi-Agent Workbench ← multi-agent-workbench.md ✅ + (named agents, per-agent policy/memory/budget, + identity-aware Actor, scene→agent lens) + │ + ▼ + Team Workbench ← team-workbench.md 📝 (not started) + (multi-user + shared AI context, + admin lens on team members) +``` + +| Plan | Status | Scope | +|---|---|---| +| [`ai-mission-key-grant.md`](./ai-mission-key-grant.md) | ✅ Shipped | Per-mission RSA-wrapped key grant so `mana-ai` can decrypt allowlisted encrypted records when user opts in. | +| [`multi-agent-workbench.md`](./multi-agent-workbench.md) | ✅ Shipped | Identity-aware Actor + named AI agents owning missions + per-agent policy + scene lens. | +| [`team-workbench.md`](./team-workbench.md) | 📝 Forward-looking | TeamSpace with membership, team-encrypted records, admin lens on team members. Reuses Actor.principalId + key-wrapping patterns from the two above. | + +Cross-references: + +- Architecture narrative: [`docs/architecture/COMPANION_BRAIN_ARCHITECTURE.md`](../architecture/COMPANION_BRAIN_ARCHITECTURE.md) §20 (AI Workbench base), §21 (Mission Grants), §22 (Multi-Agent) +- Non-plan ideas backlog: [`docs/future/AI_AGENTS_IDEAS.md`](../future/AI_AGENTS_IDEAS.md) +- Service-internal notes: [`services/mana-ai/CLAUDE.md`](../../services/mana-ai/CLAUDE.md) +- Webapp-internal notes: [`apps/mana/CLAUDE.md`](../../apps/mana/CLAUDE.md) → "AI Workbench" section + +## Other plans + +| Plan | Topic | +|---|---| +| [`mail-module-plan.md`](./mail-module-plan.md) | Mail module — IMAP/SMTP integration | +| [`news-research-module.md`](./news-research-module.md) | News + research pipeline | diff --git a/docs/plans/multi-agent-workbench.md b/docs/plans/multi-agent-workbench.md index ddd35d778..8c0c4cbe5 100644 --- a/docs/plans/multi-agent-workbench.md +++ b/docs/plans/multi-agent-workbench.md @@ -1,9 +1,9 @@ # Plan: Multi-Agent Workbench — benannte KI-Agenten als erstklassige Bürger -**Status:** Draft, 2026-04-15 -**Scope:** Upgrade vom Single-User-Workbench zum "Orchestration-Cockpit" mit mehreren benannten AI-Agenten, die autonom auf den Daten des einen Users arbeiten. Keine Team-Features (anderer User) in dieser Iteration. +**Status:** ✅ **Shipped** (Phase 0–7 code-complete, 2026-04-15). Follow-up ideas + Team extension: siehe unten + [`team-workbench.md`](./team-workbench.md). +**Scope:** Upgrade vom Single-User-Workbench zum "Orchestration-Cockpit" mit mehreren benannten AI-Agenten, die autonom auf den Daten des einen Users arbeiten. Keine Team-Features (anderer User) in dieser Iteration — das ist bewusst der nächste Plan. **Motivation:** Heute sind Missionen "nackte Arbeitsaufträge" ohne Identität. Bei 10 laufenden Missionen fehlt die ordnende Identität. Agenten geben jedem Bündel Missionen + Persönlichkeit + Memory ein Zuhause und machen die Workbench zu einem echten Control-Room. -**Verwandte Docs:** [`docs/future/AI_AGENTS_IDEAS.md`](../future/AI_AGENTS_IDEAS.md), [`docs/architecture/COMPANION_BRAIN_ARCHITECTURE.md`](../architecture/COMPANION_BRAIN_ARCHITECTURE.md) §20, [`docs/plans/ai-mission-key-grant.md`](./ai-mission-key-grant.md). +**Verwandte Docs:** [`docs/future/AI_AGENTS_IDEAS.md`](../future/AI_AGENTS_IDEAS.md), [`docs/architecture/COMPANION_BRAIN_ARCHITECTURE.md`](../architecture/COMPANION_BRAIN_ARCHITECTURE.md) §20–§22, [`docs/plans/ai-mission-key-grant.md`](./ai-mission-key-grant.md), [`docs/plans/team-workbench.md`](./team-workbench.md) (Forward-Plan). --- @@ -108,68 +108,70 @@ export interface Actor { ## Phasen -### Phase 0 — RFC + Datenmodell fixieren (0.5 Tag) +Alle Phasen sind ✅ abgeschlossen. Die ursprüngliche Gesamtschätzung war ~8–9 Tage; tatsächlicher Durchlauf an einem Abend dank L3-Cutover-Entscheidung (keine Doppel-Implementierung). Commit-Map unten in "Shipping-Historie". -- [ ] Dieses Dokument durchsprechen, Decision-Table ist Einsatzpunkt. -- [ ] Datenmodell in `packages/shared-ai/src/agents/types.ts` anlegen. -- [ ] Encryption-Registry-Eintrag vorbereiten: `agents: { enabled: true, fields: ['systemPrompt', 'memory'] }`. +### Phase 0 — RFC + Datenmodell fixieren (0.5 Tag) ✅ -### Phase 1 — Actor-Identität (L3-Cutover) (2 Tage) +- [x] Dieses Dokument durchsprechen, Decision-Table ist Einsatzpunkt. +- [x] Datenmodell in `packages/shared-ai/src/agents/types.ts` anlegen. +- [x] Encryption-Registry-Eintrag vorbereiten: `agents: { enabled: true, fields: ['systemPrompt', 'memory'] }`. + +### Phase 1 — Actor-Identität (L3-Cutover) (2 Tage) ✅ Der zentrale Refactor. Alles andere hängt davon ab. -- [ ] `Actor` in `@mana/shared-ai/src/actor.ts` erweitern um `principalId` + `displayName`. Compat-Layer: bei Read, alte Events ohne Felder → `principalId = 'legacy:user'` / `'legacy:ai-default'`, `displayName = 'Unbekannt'`. -- [ ] `USER_ACTOR` Helper: `makeUserActor(userId, displayName)`. -- [ ] Neue Helpers: `makeAgentActor(agent, mission, iteration, rationale)` und `SYSTEM_ACTOR` mit definierten `principalId`-Strings (`system:projection`, `system:mission-runner`, `system:stream`). -- [ ] **Touch-Points im Webapp** — `data/events/`, `data/ai/proposals/`, `data/ai/missions/runner.ts`, `data/ai/revert/`, alle Module-Stores die `USER_ACTOR` nutzen. Grep-Lauf, dann systematischer Rewrite. -- [ ] **Touch-Points im mana-ai** — `iteration-writer.ts` schreibt heute `{kind: 'system', source: 'mission-runner'}` → wird zu `{kind: 'ai', principalId: agentId, displayName: agent.name, missionId, iterationId}`. -- [ ] **Touch-Points in mana-sync** — keine. `sync_changes.actor` ist JSONB, akzeptiert neues Schema transparent. -- [ ] Tests anpassen: `packages/shared-ai/src/actor.test.ts`, alle Event-bezogenen Webapp-Tests. +- [x] `Actor` in `@mana/shared-ai/src/actor.ts` erweitern um `principalId` + `displayName`. Compat-Layer: bei Read, alte Events ohne Felder → `principalId = 'legacy:user'` / `'legacy:ai-default'`, `displayName = 'Unbekannt'`. +- [x] `USER_ACTOR` Helper: `makeUserActor(userId, displayName)`. +- [x] Neue Helpers: `makeAgentActor(agent, mission, iteration, rationale)` und `SYSTEM_ACTOR` mit definierten `principalId`-Strings (`system:projection`, `system:mission-runner`, `system:stream`). +- [x] **Touch-Points im Webapp** — `data/events/`, `data/ai/proposals/`, `data/ai/missions/runner.ts`, `data/ai/revert/`, alle Module-Stores die `USER_ACTOR` nutzen. Grep-Lauf, dann systematischer Rewrite. +- [x] **Touch-Points im mana-ai** — `iteration-writer.ts` schreibt heute `{kind: 'system', source: 'mission-runner'}` → wird zu `{kind: 'ai', principalId: agentId, displayName: agent.name, missionId, iterationId}`. +- [x] **Touch-Points in mana-sync** — keine. `sync_changes.actor` ist JSONB, akzeptiert neues Schema transparent. +- [x] Tests anpassen: `packages/shared-ai/src/actor.test.ts`, alle Event-bezogenen Webapp-Tests. -### Phase 2 — Agent CRUD + Daten-Layer (1.5 Tage) +### Phase 2 — Agent CRUD + Daten-Layer (1.5 Tage) ✅ -- [ ] Neue Dexie-Tabelle `agents` in `apps/mana/apps/web/src/lib/data/database.ts`. Indizes: `by-userId`, `by-name`, `by-state`. -- [ ] `apps/mana/apps/web/src/lib/data/ai/agents/store.ts` — CRUD: `createAgent`, `updateAgent`, `archiveAgent`, `deleteAgent`, `useAgents()` liveQuery-Hook, `useAgent(id)`. -- [ ] Encryption Registry + Dexie-Hooks fürs `systemPrompt` + `memory` Feld. -- [ ] Sync-Appregistry: `appId='ai-agents'` für die Tabelle. -- [ ] **Default-Agent-Bootstrap** — Layout-Effect beim Login: wenn 0 Agents existieren, lege "Mana" (Emoji `🤖`) an mit der aktuellen User-Level-AiPolicy. -- [ ] **Mission-Migration** — beim ersten Boot nach Rollout: alle `missions.agentId === undefined` kriegen `agentId = defaultAgent.id` (einmaliger Backfill, idempotent). +- [x] Neue Dexie-Tabelle `agents` in `apps/mana/apps/web/src/lib/data/database.ts`. Indizes: `by-userId`, `by-name`, `by-state`. +- [x] `apps/mana/apps/web/src/lib/data/ai/agents/store.ts` — CRUD: `createAgent`, `updateAgent`, `archiveAgent`, `deleteAgent`, `useAgents()` liveQuery-Hook, `useAgent(id)`. +- [x] Encryption Registry + Dexie-Hooks fürs `systemPrompt` + `memory` Feld. +- [x] Sync-Appregistry: `appId='ai-agents'` für die Tabelle. +- [x] **Default-Agent-Bootstrap** — Layout-Effect beim Login: wenn 0 Agents existieren, lege "Mana" (Emoji `🤖`) an mit der aktuellen User-Level-AiPolicy. +- [x] **Mission-Migration** — beim ersten Boot nach Rollout: alle `missions.agentId === undefined` kriegen `agentId = defaultAgent.id` (einmaliger Backfill, idempotent). -### Phase 3 — mana-ai runner verstehhagent-bewusst (1 Tag) +### Phase 3 — mana-ai runner agent-bewusst (1 Tag) ✅ -- [ ] `ServerMission` bekommt `agentId`. Projektion liest das Feld aus. -- [ ] **Agent-Projektion** serverseitig — analog zu `mission_snapshots` bauen wir `agent_snapshots` (LWW über `sync_changes` für `table='agents'`), scoped auf `mana_ai` Schema. -- [ ] `planOneMission` lädt den Agent, injiziert `systemPrompt + memory` in die Planner-Messages vor der Mission-Instruction. Budget-Check: wenn Agent-Budget überschritten → Mission skip mit `state='budget-exceeded'`, Metrik `mana_ai_budget_exceeded_total{agent=}`. -- [ ] **Per-Agent Concurrency-Guard** — der Tick tracked `activeMissionsByAgent` in memory, weiter nur wenn unter `maxConcurrentMissions`. -- [ ] **Audit + Metriken** — `mana_ai_agent_decisions_total{agent, decision}` (decision = `ran | skipped-budget | skipped-concurrency | skipped-paused`). -- [ ] Server-iteration-writer: Actor-JSON bekommt `principalId = agentId`, `displayName = agent.name`. +- [x] `ServerMission` bekommt `agentId`. Projektion liest das Feld aus. +- [x] **Agent-Projektion** serverseitig — analog zu `mission_snapshots` bauen wir `agent_snapshots` (LWW über `sync_changes` für `table='agents'`), scoped auf `mana_ai` Schema. +- [x] `planOneMission` lädt den Agent, injiziert `systemPrompt + memory` in die Planner-Messages vor der Mission-Instruction. Budget-Check: wenn Agent-Budget überschritten → Mission skip mit `state='budget-exceeded'`, Metrik `mana_ai_budget_exceeded_total{agent=}`. +- [x] **Per-Agent Concurrency-Guard** — der Tick tracked `activeMissionsByAgent` in memory, weiter nur wenn unter `maxConcurrentMissions`. +- [x] **Audit + Metriken** — `mana_ai_agent_decisions_total{agent, decision}` (decision = `ran | skipped-budget | skipped-concurrency | skipped-paused`). +- [x] Server-iteration-writer: Actor-JSON bekommt `principalId = agentId`, `displayName = agent.name`. -### Phase 4 — Policy pro Agent (1 Tag) +### Phase 4 — Policy pro Agent (1 Tag) ✅ -- [ ] `AiPolicy` wandert von `$lib/data/ai/policy.ts` (user-scoped Store) auf ein Feld am Agent. Store bleibt als Helper, nimmt aber Agent als Argument. -- [ ] `pendingProposals` Writer: liest Policy vom auslösenden Agent, nicht mehr global. -- [ ] `mana-ai`s tools.ts filtert die Tool-Allowlist per Agent-Policy vor jedem Tick. -- [ ] Settings-Page "Automatisierungs-Einstellungen" wandert zur Agent-Detail-Seite (jeder Agent hat seine eigene Policy-Tabelle). Legacy-Settings-Route redirected zum Default-Agent. +- [x] `AiPolicy` wandert von `$lib/data/ai/policy.ts` (user-scoped Store) auf ein Feld am Agent. Store bleibt als Helper, nimmt aber Agent als Argument. +- [x] `pendingProposals` Writer: liest Policy vom auslösenden Agent, nicht mehr global. +- [x] `mana-ai`s tools.ts filtert die Tool-Allowlist per Agent-Policy vor jedem Tick. +- [x] Settings-Page "Automatisierungs-Einstellungen" wandert zur Agent-Detail-Seite (jeder Agent hat seine eigene Policy-Tabelle). Legacy-Settings-Route redirected zum Default-Agent. -### Phase 5 — UI: Agents-Modul + Scene-Binding (2 Tage) +### Phase 5 — UI: Agents-Modul + Scene-Binding (2 Tage) ✅ -- [ ] Neues Modul `apps/mana/apps/web/src/lib/modules/ai-agents/ListView.svelte` — `/companion/agents` oder als App-Tab "Agents". CRUD + Policy-Editor + Memory-Editor + Budget/Concurrency-Felder. -- [ ] `AgentPicker.svelte` Komponente — Dropdown mit Avatar + Name, einsetzbar in Mission-Create-Flow + Scene-Settings. -- [ ] Mission-Create-Flow (`ai-missions/ListView.svelte`): neuer Schritt "Welcher Agent führt das aus?". Default: letzter-verwendeter oder "Mana". -- [ ] `SceneAppBar.svelte` — wenn `scene.viewingAsAgentId` gesetzt: Agent-Avatar-Dot auf dem Tab, Tooltip mit Name. -- [ ] Scene-Settings-Dialog: "An Agent binden" (optional) + "Bindung lösen". +- [x] Neues Modul `apps/mana/apps/web/src/lib/modules/ai-agents/ListView.svelte` — `/companion/agents` oder als App-Tab "Agents". CRUD + Policy-Editor + Memory-Editor + Budget/Concurrency-Felder. +- [x] `AgentPicker.svelte` Komponente — Dropdown mit Avatar + Name, einsetzbar in Mission-Create-Flow + Scene-Settings. +- [x] Mission-Create-Flow (`ai-missions/ListView.svelte`): neuer Schritt "Welcher Agent führt das aus?". Default: letzter-verwendeter oder "Mana". +- [x] `SceneAppBar.svelte` — wenn `scene.viewingAsAgentId` gesetzt: Agent-Avatar-Dot auf dem Tab, Tooltip mit Name. +- [x] Scene-Settings-Dialog: "An Agent binden" (optional) + "Bindung lösen". -### Phase 6 — Observability (0.5 Tag) +### Phase 6 — Observability (0.5 Tag) ✅ -- [ ] AI-Workbench-Timeline (`ai-workbench/ListView.svelte`): Filter-Dropdown "Alle Agents | [Agent1] | [Agent2] …". Bucket-Header zeigt Agent-Avatar + -Name statt nur `missionId`. -- [ ] `AiProposalInbox`-Card: Agent-Avatar + -Name oben links, Tooltip mit Mission-Titel + Rationale. -- [ ] Budget-Anzeige: mini-Fortschrittsbalken im Agent-Tile ("23% Budget heute"). +- [x] AI-Workbench-Timeline (`ai-workbench/ListView.svelte`): Filter-Dropdown "Alle Agents | [Agent1] | [Agent2] …". Bucket-Header zeigt Agent-Avatar + -Name statt nur `missionId`. +- [x] `AiProposalInbox`-Card: Agent-Avatar + -Name oben links, Tooltip mit Mission-Titel + Rationale. +- [x] Budget-Anzeige: mini-Fortschrittsbalken im Agent-Tile ("23% Budget heute"). -### Phase 7 — Rollout (0.5 Tag) +### Phase 7 — Rollout (0.5 Tag) ✅ -- [ ] Feature-Flag `PUBLIC_MULTI_AGENT_WORKBENCH=true` default (sind wir pre-live). Setting kann genutzt werden falls wir graduelle Migration im Webapp wollen — aktuell voll an. -- [ ] Docs-Update: [`apps/mana/CLAUDE.md`](../../apps/mana/CLAUDE.md) — AI-Workbench-Abschnitt erweitern. `services/mana-ai/CLAUDE.md` → Agent-Projektion + per-Agent-Metriken. -- [ ] User-Doc in `apps/docs/src/content/docs/architecture/security.mdx` — Abschnitt zu Agenten-Scope (ein Agent sieht deine Daten genau wie du; Mission-Key-Grants pro Agent sichtbar). +- [x] Feature-Flag `PUBLIC_MULTI_AGENT_WORKBENCH=true` default (sind wir pre-live). Setting kann genutzt werden falls wir graduelle Migration im Webapp wollen — aktuell voll an. +- [x] Docs-Update: [`apps/mana/CLAUDE.md`](../../apps/mana/CLAUDE.md) — AI-Workbench-Abschnitt erweitern. `services/mana-ai/CLAUDE.md` → Agent-Projektion + per-Agent-Metriken. +- [x] User-Doc in `apps/docs/src/content/docs/architecture/security.mdx` — Abschnitt zu Agenten-Scope (ein Agent sieht deine Daten genau wie du; Mission-Key-Grants pro Agent sichtbar). **Gesamtaufwand:** ~8–9 Arbeitstage. @@ -231,9 +233,51 @@ Der zentrale Refactor. Alles andere hängt davon ab. --- -## Offene Fragen (vor Phase 1) +## Offene Fragen (vor Phase 1) — ✅ beantwortet -1. **Agent-Name-Uniqueness:** erzwingen oder erlauben? → Empfehlung: erzwingen (Dexie-Unique-Index), UI-Error bei Duplikat. -2. **"system"-Actor-Renaming:** heutige `{kind:'system', source:'projection'}` Actors — kriegen `principalId = 'system:projection'`? Oder je-System-Source eigener principalId? → Empfehlung: je Source (`system:projection`, `system:stream`, `system:mission-runner`, `system:migration`). Einfacher filterbar. -3. **Legacy-User-Policy-Migration:** eine einmalige Wanderung zur Default-Agent-Policy, und danach ist die User-Setting-UI weg? Oder behalten wir einen "User-wide override"? → Empfehlung: wandern lassen, UI weg. Sauber. -4. **Scene-Agent-Binding-Default:** wenn User eine neue Scene anlegt, bind sie an den "aktuellen Agent" oder explicit leer? → Empfehlung: explicit leer. User bindet manuell wenn er will. +1. **Agent-Name-Uniqueness:** → **Erzwungen** — aber im Store (write-time) statt via Dexie-Unique-Index, damit der Default-Agent-Bootstrap zwischen zwei parallel geöffneten Tabs nicht auf `ConstraintError` läuft. `DuplicateAgentNameError` aus `agents/store.ts`. +2. **"system"-Actor-Renaming:** → **Je-Source eigener principalId** (`system:projection`, `system:rule`, `system:migration`, `system:stream`, `system:mission-runner`). Konstanten in `@mana/shared-ai/src/actor.ts`. Gibt uns Forever-Filter im Workbench + saubere Revert-Scope-Unterscheidung. +3. **Legacy-User-Policy-Migration:** → **Voll gewandert**. User-Level-Policy-Singleton entfernt; jeder Agent trägt seine eigene Policy am Record. Default-Agent erbt die vorher gültige Policy einmalig beim Bootstrap. UI am Settings-Pfad ist weg. Ein Mini-Template-Picker (Standard / Cautious / Aggressive) im Agent-Detail ersetzt sie ergonomisch. +4. **Scene-Agent-Binding-Default:** → **Explicit leer**. Neue Scenes starten ohne Bindung. User bindet manuell via Scene-Context-Menü → "An Agent binden…". Hat die natürliche Folge dass "Agents sind Bürger, Scenes sind Fenster" in der UX durchhält. + +--- + +## Shipping-Historie + +| Phase | Commit | Files | Tests | +|---|---|---|---| +| 0 — Plan | (dieses Dokument) | `docs/plans/multi-agent-workbench.md` | n/a | +| 1 — Actor-Identität | `1771063df` | 13 files, 571+/116- | 26 shared-ai, 21 webapp vitest, 35 mana-ai | +| 2 — Agent CRUD | `bc77b3623` | 9 files, ~400 LOC | vitest green | +| 3 — mana-ai agent-aware | `0af50f016` | 7 files, 560+/17- | +6 mana-ai tests → 41 green | +| 4 — Policy pro Agent | `f7426ab40` | 3 files, 49+/2- | svelte-check clean | +| 5 — Agent UI + Scene-Binding | `51e6a20da` | 11 files | svelte-check clean | +| 6+7 — Observability + Docs | `7c89eb625` | 6 files, 162+/15- | svelte-check clean | + +--- + +## Lessons Learnt + Follow-Up Ideen + +### Was besser lief als erwartet + +- **L3-Cutover war billiger als Plan B.** Actor als identitätsfähige discriminated union umzubauen hat 2 Tage gedauert inkl. aller Call-Sites. Die Alternative "zwei parallele Actor-Shapes durch Adapter" hätte Monate gekostet und drift-fällig sein. Nicht-live-sein war der entscheidende Enabler. +- **Default-Agent-Bootstrap als write-time `getOrCreate`** statt Dexie-Unique-Index hat die Tab-Race-Problematik ohne weiteren Code eliminiert. Lektion: "idempotent statt einzigartig" wo es geht. +- **displayName-Caching am Actor** hat sich in der Praxis bewährt. Timeline und Proposal-Inbox bleiben historisch stabil ohne Join auf die `agents`-Tabelle. Preis: displayName ist redundant — akzeptable Speicher-Kosten (~20 Bytes pro Event). + +### Was dünn ist und später Nacharbeit verdient + +- **Avatar nicht auf Actor gecached.** Im Proposal-Inbox hängt 🤖 hart drin; bei umbenanntem Agent fehlt der richtige Avatar in History. Fix: `avatar` ins BaseActor-Shape mitaufnehmen (low-cost additiv). Nicht dringend, weil der Avatar sowieso ein schwaches Signal ist; Name trägt die Identität. +- **Budget-Enforcement existiert im Datenmodell (`maxTokensPerDay`) aber ohne Counter.** Runner zählt noch keine Tokens. Vollständige Budget-Enforcement braucht LLM-Client-Token-Counts → Folgeprojekt. Bis dahin ist das Feld UI-Theater. +- **Agent-Detail-View zeigt keine Missions-Liste.** User sieht Agent A, fragt sich "welche Missions gehören diesem?" — müsste zurück in ai-missions navigieren + filtern. Simple Ergänzung: reverse-lookup-Sektion. +- **Kein Drag-to-Reassign.** Man kann eine Mission nicht von Agent A auf Agent B ziehen. Heute: edit-Mission-dialog mit AgentPicker. Besser: drag vom Mission-Item auf einen Agent-Tile. +- **Scene-Agent-Binding hat keinen Effekt auf Mission-Filter.** Plan sagt "wenn Scene an Agent gebunden, Workbench-Timeline + Mission-Create sollten Agent vorselektieren." Heute noch nicht durchgereicht — Scene-Binding ist nur Avatar-Deko. + +### Größere Folgeprojekte + +Die fünf expliziten Nicht-Ziele des Plans sind alle eigene Plans wert: + +1. **Team-Features** → [`team-workbench.md`](./team-workbench.md) — separater forward-looking Plan, baut direkt auf `Actor.principalId` auf. Siehe dort. +2. **Agent-Memory-Self-Modification** — Evals, Drift-Detection, Safe-Update. Eigenes ML-Safety-Projekt. +3. **Agent-to-Agent Messaging** — benötigt event-bus-Konzept, Semantik "wann blockiert A auf B?", mental model für den User. +4. **Meta-Planner auf Agent-Ebene** — Agent-Objective statt Mission-Objective, Agent generiert sich Missionen selbst. Braucht sauberen Stop-Criteria-Mechanismus. +5. **Per-Agent-Encryption-Domains** — analog zum Mission-Grant, aber agent-scoped. Relevant wenn ein Agent "nur Notes lesen darf" als Policy + Crypto kombiniert werden soll. diff --git a/docs/plans/news-research-module.md b/docs/plans/news-research-module.md new file mode 100644 index 000000000..02f219563 --- /dev/null +++ b/docs/plans/news-research-module.md @@ -0,0 +1,141 @@ +# News Research — Module Plan + +## Status (2026-04-15) + +Planung. Noch kein Code. Geschwistermodul zu `news/`, teilt Low-Level-Primitives, eigene Pipeline. + +## Ziel + +Ein **Research-Tool**, das zu einem Thema passende RSS-Feeds findet, sie filtert, die Artikel nach Relevanz durchsucht und das Ergebnis als **KI-Kontext-Paket** ausliefert. Agent-facing, nicht consumer-facing. + +**Abgrenzung zum bestehenden `news/`-Modul:** +- `news/` = kuratierter Consumer-Feed, persistent, verschlüsselt, synced, Lern-Reaktionen. +- `news-research/` = Discovery + Query, ephemer pro Session, eigenes Scoring (Relevanz statt Recency). +- **Brücke**: "Save article" → landet in der bestehenden `newsArticles`-Tabelle (type='saved'). Einzige Reading-List bleibt dort. +- **Brücke**: "Pin feed" → persistiert einen entdeckten Feed in `customFeeds` (Preferences-Erweiterung) — damit ist gleichzeitig die eigene RSS-Abo-Funktion abgedeckt, die zuvor im `news/`-Modul fehlte. + +## Shared Primitives zuerst + +Vor dem Modul: **`packages/shared-rss`** (neues Workspace-Package) extrahieren. + +- `parseFeed(url | xml)` — RSS/Atom Parser (wandert aus `services/news-ingester`) +- `extractArticle(url, html?)` — Readability-Fallback (wandert aus `apps/api/src/modules/news/routes.ts`) +- `discoverFeeds(siteUrl)` — neu: ``-Discovery + Sitemap-Heuristik +- `validateFeed(url)` — Health-Check (fetchbar, parsebar, min. N Items) +- Typen: `FeedMeta`, `FeedItem`, `ExtractedArticle` + +`news-ingester` und der `news/extract`-Endpoint werden migriert, damit nur eine Parser-Implementation existiert. + +## Discovery-Strategie (einfache Variante) + +Kein Feedly/RSS.app. Alles in-house, zwei Quellen: + +1. **Direkte URL**: User pastet Site-URL → Server holt HTML → sucht `` → validiert → gibt Feed-Metadaten zurück. +2. **Thema-Suche**: Query `"klimawandel" rss` oder ähnlich an eine Websuche (reuse was bei `mana-search`/crawler vorhanden ist), Top-N Ergebnisse filtern → für jedes Top-Result Discovery-Schritt 1 ausführen. + +Phase 2 kann später externe APIs hinzufügen. + +## Modul-Struktur + +``` +apps/mana/apps/web/src/lib/modules/news-research/ +├── types.ts # ResearchSession, DiscoveredFeed, ResearchArticle +├── stores/ +│ ├── session.svelte.ts # Aktuelle Research-Session (ephemer, kein Dexie) +│ └── pinned.svelte.ts # customFeeds-Verwaltung (persistiert über news-Preferences) +├── engine/ +│ ├── relevance.ts # BM25 / Keyword-Scoring Artikel vs. Query +│ └── filter.ts # Thema-Filter Feed + Artikel +├── api.ts # Client für /api/v1/news-research/* +└── components/ # UI +``` + +``` +apps/api/src/modules/news-research/routes.ts +├── POST /discover # { query | siteUrl } → DiscoveredFeed[] +├── POST /validate # { url } → FeedMeta + Sample-Items +├── POST /search # { feeds[], query, filters } → ScoredArticle[] +└── POST /extract # { url } → ExtractedArticle (reuse shared-rss) +``` + +Kein eigener Background-Service. Alles on-demand request/response. Session läuft im Browser-Memory + SessionStorage. + +## Data Model + +**Ephemer (nur Session):** +```ts +type ResearchSession = { + id: string + query: string + topics: string[] + discoveredFeeds: DiscoveredFeed[] + selectedFeedIds: string[] + results: ScoredArticle[] + createdAt: number +} +``` + +**Persistent — Erweiterung von `newsPreferences`:** +```ts +customFeeds: Array<{ + id: string + url: string + title: string + topic?: NewsTopic + pinnedAt: number +}> +``` + +Kein neues Dexie-Table. Reading-List-Saves gehen in `newsArticles` wie bisher. + +## UI-Flow + +1. `/news-research` Landing: Query-Feld + Site-URL-Feld. +2. **Discovery-Ergebnis**: Liste gefundener Feeds (Name, Item-Count, Sample-Topics). User hakt relevante an, kann pinnen. +3. **Filter**: Thema-Chips, Keyword-Filter, Zeitraum. +4. **Ergebnis**: Artikel-Liste sortiert nach Relevanz. Actions pro Artikel: `Save` (→ `newsArticles`), `Open`, `Add to Context`. +5. **Context-Paket**: Button "Export as AI Context" → erzeugt einen strukturierten Markdown-/JSON-Block, der in eine Mission/Chat übernommen werden kann. + +## KI-Integration + +- Neues LLM-Tool `research_news(query, options)` in `tools.ts` (Geschwister zu `save_news_article`). +- Rückgabe: Context-Paket (Top-N Artikel mit Excerpt + URL + Publish-Date). +- Agent kann eigenständig eine Research-Session fahren, ohne UI. + +## Milestones + +### M1 — `packages/shared-rss` extrahieren +- [ ] Neues Workspace-Package anlegen +- [ ] Parser aus `news-ingester` verschieben, Service migrieren +- [ ] `extractArticle` aus `apps/api` verschieben, Endpoint migrieren +- [ ] `discoverFeeds` neu implementieren (rel=alternate) +- [ ] `validateFeed` neu + +### M2 — API Routes +- [ ] `apps/api/src/modules/news-research/routes.ts` mit `/discover`, `/validate`, `/extract` +- [ ] Websuche-Integration für Query-basierte Discovery +- [ ] `/search` — lädt Feeds parallel, wendet Relevanz-Scoring an + +### M3 — Frontend-Modul +- [ ] Modulstruktur + types +- [ ] Session-Store, Pinned-Store +- [ ] `/news-research` Route + UI (Discovery → Filter → Ergebnis) +- [ ] Save-Action in `newsArticles` (reuse `articlesStore.saveFromUrl`) +- [ ] "Export as AI Context" + +### M4 — `customFeeds` als Abo-Funktion +- [ ] `newsPreferences.customFeeds` Field + Encryption-Allowlist +- [ ] UI in `news/` zur Verwaltung +- [ ] Optional: `feed-cache` lädt auch `customFeeds` und merged im globalen Feed-Engine + +### M5 — KI-Tool +- [ ] `research_news` LLM-Tool +- [ ] Context-Paket-Format dokumentieren +- [ ] Mission-Beispiel in `missions/debug.ts` oder neue Mission + +## Offene Fragen + +- **Websuche-Backend**: `mana-search` nutzen oder direkt SearXNG/Brave-API? → Entscheidung vor M2. +- **Relevanz-Scoring**: Reicht lokales BM25 über Excerpts, oder Embedding-basiert via `mana-llm`? → Lokal starten, Embeddings als Phase 2. +- **Feed-Caching serverseitig**: Feeds pro Query erneut fetchen (Kaltstart) oder kurzlebiger Redis-Cache? → Ohne Cache starten, messen. +- **Topic-Taxonomie**: Die 7 News-Topics übernehmen oder frei (Tags)? → Frei, mit Vorschlägen aus News-Topics. diff --git a/docs/plans/team-workbench.md b/docs/plans/team-workbench.md new file mode 100644 index 000000000..7ad8b9470 --- /dev/null +++ b/docs/plans/team-workbench.md @@ -0,0 +1,250 @@ +# Plan: Team Workbench — Multi-User-Workbench mit geteiltem AI-Kontext + +**Status:** Forward-looking Plan, nicht gestartet. 2026-04-15. +**Scope:** Erweitere den bestehenden Single-User-Multi-Agent-Workbench so, dass mehrere User einen gemeinsamen "TeamSpace" bewohnen: geteilte Daten, geteilte Agenten, geteilte Missions-Queue. Admin-Sicht auf Team-Mitglieder. +**Abhängigkeiten:** Baut direkt auf [`multi-agent-workbench.md`](./multi-agent-workbench.md) auf — insbesondere die identity-aware `Actor`-Shape und die Agent-als-Bürger-Abstraktion. Ohne diese beiden Foundations wäre dieser Plan 3× so groß. +**Verwandte Docs:** [`docs/architecture/COMPANION_BRAIN_ARCHITECTURE.md`](../architecture/COMPANION_BRAIN_ARCHITECTURE.md) §20–§22, [`ai-mission-key-grant.md`](./ai-mission-key-grant.md) (Key-Wrapping-Muster), [`docs/future/AI_AGENTS_IDEAS.md`](../future/AI_AGENTS_IDEAS.md). + +--- + +## Die zwei Use-Cases + +Der Plan deckt zwei verwandte Szenarien, die sich denselben Unterbau teilen: + +### A) Team-Workspace (menschliche Mitbenutzer) +Mehrere User teilen einen Workspace. Ein Admin kann durch die Workbench-Scenes _seiner Teammitglieder_ blättern und sieht, woran jeder gerade arbeitet. Gemeinsame Notizen / Tasks / Kalender synchronisieren team-weit. + +**Mini-Beispiel:** Till + Anna im gleichen TeamSpace. Anna schreibt ein Note. Till sieht es in seinem Notes-Modul mit Autor-Avatar "Anna". Till hat eine Admin-Lens "Workbench von Anna" und sieht, was sie gerade offen hat. + +### B) Multi-Agent mit geteiltem Team-Kontext (AI-Agenten als Team-Mitglieder) +10 benannte Agenten arbeiten autonom, teilen sich aber ein gemeinsames Wissen. Jeder Agent kann Team-scope Dinge lesen/schreiben; manche Dinge bleiben privat pro Agent. + +**Mini-Beispiel:** "Cashflow Watcher" legt einen Event "Rechnung fällig am 15." ins Team-Kalender. "Travel Planner" sieht beim nächsten Tick die Rechnung, plant Reisekosten drumherum. Beide greifen auf denselben Team-Kontext zu. + +**Der Trick:** A und B sind die gleiche Infrastruktur, nur mit unterschiedlichen `Principal.kind` Werten. Das ist möglich weil Phase 1 des Multi-Agent-Plans die `Actor.principalId`-Abstraktion eingeführt hat. + +--- + +## Entscheidungen zum Diskutieren (noch nicht fix) + +Diese Tabelle ist bewusst **nicht** "baked in" — sie ist Diskussionsgrundlage für einen RFC vor Implementierung. Im Unterschied zum Multi-Agent-Plan, wo wir vor-live-Freiraum hatten, geht dieser Plan dann produktiv und bricht teils existierende Crypto-Garantien auf. + +| Frage | Kandidat | Risiko / Trade-off | +|---|---|---| +| **TeamSpace-Objekt** | Neues `teamSpaces` Dexie-Table + pgSchema mit RLS auf `team_id` | Neue Entität → Touch-Points in Sync, Modul-Registry, UI-Modell. Aber: klares Schema, eindeutige Eigentumsverhältnisse. | +| **Mitgliedschaft** | `teamMembers (teamId, userId, role, addedAt)` + server-side enforce | Auth-Service braucht Team-Awareness. Memoro hat schon das Muster. | +| **Rollen** | `admin` / `member` / `viewer` Minimal-Set | Mehr Rollen = mehr RLS-Komplexität. 3 reichen für die meisten echten Fälle. | +| **Principal-Kind-Erweiterung** | `Actor.kind = 'user'\|'ai'\|'system'\|'team'`? oder `kind` bleibt, `teamId` wird ein separates Feld? | Bevorzugen: `teamId?: string` als zusätzliches Feld am Actor statt neuer kind. Compat-leicht. | +| **Record-Scoping** | `record.teamId?: string` neben `record.userId` — Sync-Router dispatcht basierend auf Scope | Braucht Mode-Flag pro Modul ("ist diese Tabelle team-fähig?"). Notes, Tasks, Calendar sind offensichtliche Kandidaten; Playground-Snippets wahrscheinlich nicht. | +| **Encryption** | Per-Team-Key, wrapped-per-member analog zu Mission-Grants | Größter Risiko-Punkt. Key-Rotation beim Mitglied-Ausschluss ist non-trivial. Siehe Detail-Abschnitt unten. | +| **Mission-Ownership** | `mission.teamId?` + `mission.ownerUserId` + `mission.agentId` | Frage: können _nur_ Admins team-Missions anlegen, oder jedes Mitglied? Empfehlung: jedes Mitglied kann team-Missions anlegen, Agent-Grants pro Mission wie gehabt. | +| **Agent-Scope** | Agent hat optional `teamId` — ein Agent existiert entweder privat (User-scope) oder team-weit | Alternativen wären komplizierter (Agent in mehreren Teams) und lösen keine echten User-Needs. | +| **Scene-Scope** | Scenes bleiben per-User. Admin-Lens ist eine read-only Projektion auf andere Mitgliedspezifische Scenes. | Scenes team-weit shared hat keinen klaren UX-Wert — jeder hat seinen Arbeitsplatz. | +| **Admin-Lens** | Neuer "Team-Workbench" App-Tab. Admin sieht Tile-Wall der aktiven Scenes aller Mitglieder (Overview + Click-to-Inspect). | Kein versteckter Lens-Modus — immer klar wessen Sicht man gerade hat (Breadcrumb). | + +--- + +## Architektur-Skizze + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Webapp (Dexie) — scopes multiplied │ +│ │ +│ Records carry {userId, teamId?} — sync router fan-out │ +│ │ +│ ┌──────────────────┐ ┌──────────────────┐ │ +│ │ Private scope │ │ Team scope │ │ +│ │ userId=me │ │ teamId=team-xyz │ │ +│ │ my notes/tasks │ │ shared notes │ │ +│ └──────────────────┘ └──────────────────┘ │ +└────────────────┬───────────────────┬────────────────────────┘ + │ │ + ▼ ▼ + ┌────────────┐ ┌────────────┐ + │ mana-sync │ │ mana-sync │ + │ user-scope │ │ team-scope │ ← RLS on team_id + │ RLS │ │ + team- │ + │ │ │ membership │ + └────────────┘ └────────────┘ + │ + ▼ + ┌────────────────┐ + │ mana-auth │ + │ teams + │ + │ memberships + │ + │ team-KEK │ + └────────────────┘ +``` + +### Datenmodell (Skizze) + +```ts +// mana-auth +interface TeamSpace { + id: string; + ownerUserId: string; + name: string; + createdAt: string; + // Team KEK: ein Master-Key pro Team, gewrapped mit jedem Mitglied- + // UserKey. Analog zu Mission-Grants aber auf Team-Ebene. + teamMasterKeyWrapped: Record; +} + +interface TeamMember { + teamId: string; + userId: string; + role: 'admin' | 'member' | 'viewer'; + addedAt: string; + addedBy: string; +} + +// Shared-AI: Actor gets a third dimension +interface Actor { + kind: 'user' | 'ai' | 'system'; + principalId: string; + displayName: string; + teamId?: string; // NEW — present when the write is + // team-scoped; absent for private writes. + // ... rest as before +} + +// Webapp: Mission + Agent optionally team-scoped +interface Agent { + // ... existing fields + teamId?: string; // NEW +} + +interface Mission { + // ... existing fields + teamId?: string; // NEW +} + +// Webapp: Scenes stay per-user, but gain an admin-lens feature +interface WorkbenchScene { + // ... existing fields (NOT changed) +} + +interface AdminLensState { // NEW — per-device localStorage + activeView: 'self' | { kind: 'member'; userId: string; teamId: string }; +} +``` + +--- + +## Encryption-Design — der härteste Teil + +Das Encryption-Modell ist der größte Posten und verdient einen eigenen Unter-Plan. Drei Hauptfragen: + +### Frage E1 — Wie teilen Mitglieder einen Schlüssel? + +**Kandidat: Team-KEK, wrapped-per-member.** +- Team hat einen symmetrischen `teamKey` (AES-GCM-256). +- Jedes Mitglied hat ihre eigenen `teamKeyWrappedWith_userPubKey` — eine Kopie pro Mitglied, via RSA-OAEP oder X25519-ECDH gewrapped. +- Mitglied-Hinzufügen = existierender Admin wrapped den Team-Key mit dem Public-Key des neuen Mitglieds, pusht das ins `teamMasterKeyWrapped` Map. +- Mitglied-Entfernen = **komplette Key-Rotation** (siehe E3). + +Das ist exakt das Muster aus [Mission-Grants](./ai-mission-key-grant.md), eine Ebene höher. Wir haben das Wrapping-Toolkit schon. + +### Frage E2 — Welche Records werden per Team-Key verschlüsselt? + +Die bestehende `crypto/registry.ts` definiert User-scope Verschlüsselung. Für Team-Records kommt eine **zweite Registry-Ebene**: `teamEncryptionRegistry` mit den Feldern die unter Team-Scope encrypted sind. In der Praxis oft identisch mit der User-Registry (gleiche Tabellen, gleiche Felder), aber unter dem Team-Key statt User-Key. + +### Frage E3 — Mitglied-Entfernung + Key-Rotation + +Das ist der Kletterberg. Wenn Bob rausgeworfen wird: +- Alles was er _bisher_ gesehen hat, kann er Cache-seitig behalten (unfixable without TPM). +- Neue Writes dürfen nicht mehr mit dem alten Team-Key verschlüsselt werden. +- → Neuer `teamKey` generieren, alle existierenden Team-Records re-encrypten. + +**Das ist teuer.** Bei einem Team mit 10k Notes + 5k Tasks ist Re-Encryption eine Minutes-lange Operation. Optionen: +- **Eager**: synchron im Hintergrund nach Mitglied-Entfernung, mana-ai service macht die Arbeit. +- **Lazy**: Team-Key-Version wird hochgezählt. Alte Records bleiben encrypted unter alter Version; bei jedem Read mit neuem Key failt es, client re-encrypts und sync-pusht. Über Zeit wandert alles auf die neue Version. Mitglied-Ausschluss wirkt "sofort" für Writes, "eventually" für alte Records. + +Empfehlung: **Lazy mit Fortschritts-Anzeige**. Stoppt die UI nicht, ist aber für Admin erkennbar ("23% noch auf alter Schlüsselversion"). + +### Frage E4 — Zero-Knowledge-Mode im Team-Kontext? + +Nicht supported. Sobald ein User in einem Team ist, muss mana-auth den Team-Key wrappen können — das geht nur wenn der User _nicht_ Zero-Knowledge ist. Team-Beitritt fragt explizit: "Für Team-Beitritt wird dein Vault aus Zero-Knowledge genommen." Vermutung: akzeptabel. Teams sind per se geteiltes Modell. + +--- + +## Phasen (grob) + +Diese Phasierung ist eine erste Skizze — vor Implementierung braucht's einen eigenen RFC. + +### T0 — RFC + ADR (1 Woche) +- Dieses Dokument schärfen; Encryption-Frage E1-E4 durch-entscheiden +- ADR "Team-Scope-Data" in `docs/decisions/` +- Schema-Entwürfe in `services/mana-auth/sql/` + `services/mana-sync/` + +### T1 — Team-Entität + Membership (1 Woche) +- `teams` Tabelle in mana-auth mit RLS +- `POST /api/v1/teams` create + `POST /api/v1/teams/:id/members` invite +- Minimal-UI: Settings → Teams → "Team anlegen" + Member-List + +### T2 — Team-Scope Records, ohne Encryption (1 Woche) +- `record.teamId?` an Records der team-fähigen Tabellen (notes, tasks, events, …) +- Sync-Router fan-out: ein Record mit `teamId` → POST /sync/team/:teamId statt /sync/user/:userId +- `mana-sync` Backend: neue Sync-Channel für Team, RLS auf team-membership +- UI: "Als Team-Note speichern" Toggle in den Create-Flows von notes, tasks, events + +An diesem Punkt haben wir Team-geteilte Daten in Plaintext — bewusst ohne Crypto zuerst, um das Datenmodell zu validieren bevor Encryption-Komplexität dazukommt. + +### T3 — Team-Encryption (2 Wochen) +- Team-KEK in mana-auth mit wrapped-per-member +- webapp: `getTeamKey(teamId)` Cache, `encryptTeamRecord` / `decryptTeamRecord` in crypto-Layer +- Registry: `teamEncryptionRegistry` mit den encrypted Feldern +- Member-Einladung-Flow: admin wraps team-key with new member's pub-key +- Member-Removal-Flow: lazy-rotation mit Version-Bumping + Background-Re-Encrypt + +### T4 — Actor-Erweiterung + AI-Integration (1 Woche) +- `Actor.teamId?` Feld in shared-ai (additiv, compat-leicht) +- Mission-runner checkt bei team-scope mission ob owning agent team-scope ist +- Proposal + Workbench-Timeline zeigen "von User X im Team Y" statt nur "von Agent Z" + +### T5 — Admin-Lens (1 Woche) +- Neues Modul `team-workbench` — Admin sieht Tile-Wall der aktiven Scenes der Teammitglieder (ohne Mutations-Rechte standardmäßig) +- Per-Member "View as Alice" Modus für Admins (breadcrumb klar sichtbar: "Du siehst die Scene von Alice") +- Context-Menu auf Members: "Message", "Remove from team" + +### T6 — Polish + Rollout (1 Woche) +- Feature-Flag `PUBLIC_TEAM_WORKBENCH` +- Docs: security.mdx Abschnitt "Teams und geteilte Verschlüsselung" +- Prometheus alerts auf team-level failures +- Onboarding-Flow: "Willst du ein Team erstellen?" + +**Gesamtaufwand:** ~7 Wochen für eine saubere Erstfassung. Die Hälfte davon ist Encryption + Migration-Robustheit. + +--- + +## Wie Multi-Agent dafür den Weg geebnet hat + +Die Multi-Agent-Phase hat bewusst vier Invarianten etabliert, die Team-Features später billig machen: + +1. **`Actor.principalId` ist universell.** User, Agent, System sitzen alle auf derselben Achse. Team wird zu einem _context-Feld_ am Actor, nicht zu einem vierten `kind`. Keine discriminated-union-Explosion. +2. **`Actor.displayName` ist cached.** Wenn Alice das Team verlässt, zeigen historische Events trotzdem "Alice". Keine Need-To-Join auf aktuelle Membership-Tabellen. +3. **Scene ↔ Agent ist orthogonal (Lens, nicht Scope).** Die gleiche Idee lässt sich auf Team-Membership anwenden: "Admin-View auf Alice" ist eine Lens, keine Scope-Änderung. Lässt sich oben draufsetzen ohne Scene-Modell zu ändern. +4. **Agent-Memory ist encrypted-at-rest wie Notes.** Das Pattern ist etabliert. Team-Encryption nutzt denselben Pfad (wrapValue/unwrapValue), nur mit einem anderen Key. + +Konkret: das L3-Actor-Cutover-Refactor würden wir für Team-Features nochmal machen müssen, wenn wir's nicht jetzt gemacht hätten. Jetzt ist es ein 3-zeiliger Change (`teamId?: string` dazu). + +--- + +## Explizite Nicht-Ziele (auch für T-Phasen) + +- **Keine public/open Teams.** Invite-only, auch langfristig. Public-Feeds sind ein völlig anderes Produkt. +- **Keine Real-Time-Presence.** "Alice tippt gerade in Note X" ist ein Follow-Up — Team-Sync bleibt beim etablierten pull/push-Modell. +- **Keine Team-weiten Agent-Mutations durch Admin.** Admin kann sehen was Alice's Agents tun, aber nicht deren Policy ändern. Respektiert Autonomie. +- **Keine Cross-Team-Migration.** Eine Mission/Note ist entweder privat oder team-scoped. Umhängen von Team zu Team ist nicht vorgesehen — wenn User's es brauchen, ist's ein Duplicate-Create. + +--- + +## Offene Fragen (für den T0 RFC) + +1. **Owner-Konzept:** Wer besitzt ein Team? Kann Ownership übertragen werden? Was passiert wenn der Owner geht? +2. **Abrechnung:** Mana-Credits sind heute per-User. Teams haben geteilte LLM-Kosten — wer zahlt? Pro-Team-Subscription vs. Pro-Member-Subscription? +3. **Viewer-Rolle:** hat Viewer Write-Access auf Proposals (approve reject)? Oder nur Read? Vermutlich nur Read. +4. **Mission-Rights:** Darf ein Member eine Mission auf einem shared agent starten, die der Admin nicht approved hat? Oder brauchen alle Team-Missions Admin-Approval? +5. **Guest-Mode:** Team-Einladung per Magic-Link für noch-nicht-registrierte User — supported oder nicht? (Vermutlich in T1 nicht, später optional.) +6. **Conflict-Resolution auf Team-Records:** Alice editiert Note A, Bob auch. LWW wie bei User-Records oder expliziter Merge-Editor? Vermutlich LWW fürs Erste; Merge-Editor ist ein Folgeprojekt. +7. **Mobile App:** Mobile läuft hinter der Webapp. Wie lange ist mobile ohne Team-Features akzeptabel? Oder Hand-off via Magic-Link auf Web? diff --git a/services/mana-ai/CLAUDE.md b/services/mana-ai/CLAUDE.md index 63f531b84..05c0d03fc 100644 --- a/services/mana-ai/CLAUDE.md +++ b/services/mana-ai/CLAUDE.md @@ -2,7 +2,12 @@ Background runner for the AI Workbench. Picks up due Missions from the `mana_sync` Postgres and plans/proposes next steps without requiring an open browser tab. Complements the foreground `startMissionTick` in the webapp (`apps/mana/apps/web/src/lib/data/ai/missions/setup.ts`). -Design context: [`docs/architecture/COMPANION_BRAIN_ARCHITECTURE.md` §20](../../docs/architecture/COMPANION_BRAIN_ARCHITECTURE.md). +Design context: +- [`docs/architecture/COMPANION_BRAIN_ARCHITECTURE.md`](../../docs/architecture/COMPANION_BRAIN_ARCHITECTURE.md) §20 (AI Workbench base), §21 (Mission Key-Grants), §22 (Multi-Agent Workbench) +- [`docs/plans/ai-mission-key-grant.md`](../../docs/plans/ai-mission-key-grant.md) — Shipped (per-mission key-grant for encrypted inputs) +- [`docs/plans/multi-agent-workbench.md`](../../docs/plans/multi-agent-workbench.md) — Shipped (named agents, per-agent policy/memory, scene lens) +- [`docs/plans/team-workbench.md`](../../docs/plans/team-workbench.md) — Forward-looking (multi-user + shared team context) +- [`docs/future/AI_AGENTS_IDEAS.md`](../../docs/future/AI_AGENTS_IDEAS.md) — Unshipped improvement backlog ## Status: v0.3 (full close-the-loop)