From f06ca2c7c35410d3cbf2105b203a7ac6bb8698e2 Mon Sep 17 00:00:00 2001 From: Till JS Date: Wed, 15 Apr 2026 21:33:06 +0200 Subject: [PATCH] feat(ai-missions): inline AiProposalInbox in mission detail (cross-module) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mission detail now renders all pending proposals for that mission, across every module, directly above the iteration list. No more jumping between /todo, /news, /calendar to approve what the agent staged. AiProposalInbox.module is now optional; when omitted, every card grows a small lowercase module badge (e.g. "news", "todo") so the user knows where each proposal will land on approve. The existing per-module inboxes on /todo, /news, /calendar still work — proposals show in both places via the same live query, so approving in one auto-clears the other. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../lib/components/ai/AiProposalInbox.svelte | 31 ++++++++++++++++--- .../lib/modules/ai-missions/ListView.svelte | 4 +++ 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/apps/mana/apps/web/src/lib/components/ai/AiProposalInbox.svelte b/apps/mana/apps/web/src/lib/components/ai/AiProposalInbox.svelte index 00fdd118f..6a3ef41ab 100644 --- a/apps/mana/apps/web/src/lib/components/ai/AiProposalInbox.svelte +++ b/apps/mana/apps/web/src/lib/components/ai/AiProposalInbox.svelte @@ -23,13 +23,22 @@ import type { Proposal } from '$lib/data/ai/proposals/types'; interface Props { - /** Filter proposals to tools belonging to this module (e.g. 'todo'). */ - module: string; + /** Filter proposals to tools belonging to this module (e.g. 'todo'). + * Omit when filtering by mission only — the inbox will then render + * every pending proposal across modules and add a module badge to + * each card so the user knows where it'll land on approve. */ + module?: string; + /** Filter to proposals from a specific mission. Combine with `module` + * to scope to that mission's proposals for a single module. */ + missionId?: string; } - let { module }: Props = $props(); + let { module, missionId }: Props = $props(); - const proposals = $derived(useAiProposals({ status: 'pending', module })); + const proposals = $derived(useAiProposals({ status: 'pending', module, missionId })); + /** Show module badge whenever the inbox is cross-module (i.e. the + * caller didn't pin it to a single module). */ + const showModuleBadge = $derived(!module); let busyId = $state(null); /** Proposal whose reject-feedback textarea is currently open. */ @@ -92,6 +101,10 @@
KI schlägt vor + {#if showModuleBadge && p.intent.kind === 'toolCall'} + {@const mod = getTool(p.intent.toolName)?.module ?? '?'} + {mod} + {/if}

{formatIntent(p)}

@@ -181,6 +194,16 @@ text-transform: uppercase; letter-spacing: 0.04em; } + .module-badge { + margin-left: auto; + padding: 0.0625rem 0.375rem; + border-radius: 0.25rem; + background: color-mix(in oklab, var(--color-primary, #6b5bff) 18%, transparent); + color: color-mix(in oklab, var(--color-primary, #6b5bff) 90%, var(--color-fg, #000)); + font-size: 0.6875rem; + letter-spacing: 0.02em; + text-transform: lowercase; + } .intent { margin: 0.375rem 0 0; diff --git a/apps/mana/apps/web/src/lib/modules/ai-missions/ListView.svelte b/apps/mana/apps/web/src/lib/modules/ai-missions/ListView.svelte index 976ef823c..057d38cea 100644 --- a/apps/mana/apps/web/src/lib/modules/ai-missions/ListView.svelte +++ b/apps/mana/apps/web/src/lib/modules/ai-missions/ListView.svelte @@ -22,6 +22,7 @@ import MissionInputPicker from '$lib/components/ai/MissionInputPicker.svelte'; import MissionGrantDialog from '$lib/components/ai/MissionGrantDialog.svelte'; import AiDebugBlock from '$lib/components/ai/AiDebugBlock.svelte'; + import AiProposalInbox from '$lib/components/ai/AiProposalInbox.svelte'; import { isAiDebugEnabled, setAiDebugEnabled } from '$lib/data/ai/missions/debug'; import { isMissionGrantsEnabled } from '$lib/api/config'; import type { Mission, MissionCadence, MissionInputRef } from '$lib/data/ai/missions/types'; @@ -392,6 +393,9 @@ {/if} +

Vorschläge zur Review

+ +

Iterationen

{#if selected.iterations.length === 0}

Noch keine Iteration gelaufen.