diff options
| author | Adam Malczewski <[email protected]> | 2026-05-23 16:59:20 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-05-23 16:59:20 +0900 |
| commit | 236beefb708a6cd91b673978ddf4ebf045a9844c (patch) | |
| tree | 6522c65a0d490b41cc01297f2444f160f8dbb7f8 /packages/frontend/src/lib | |
| parent | 225d3ea65cfc35d211fc66e30cf05cbc693d37e4 (diff) | |
| download | dispatch-236beefb708a6cd91b673978ddf4ebf045a9844c.tar.gz dispatch-236beefb708a6cd91b673978ddf4ebf045a9844c.zip | |
feat: key fallback using agent models[] hierarchy, background tool modes, copy truncation
- Agent rate-limit fallback now iterates through agent's configured models[] in strict order
- Frontend sends agentModels with each /chat request; backend uses buildFallbackSequence()
- Emits notice event on fallback so chat shows which key failed and what's being tried next
- Child agents inherit parent's agentModels for fallback
- Added statusCode propagation from AI SDK errors for programmatic 429 detection
- Copy button truncates all tool results at 300 chars (was 200 for 4 specific tools)
- run_shell, summon, youtube_transcribe: background mode support
- summon: blocking mode by default with getResult callback
Diffstat (limited to 'packages/frontend/src/lib')
| -rw-r--r-- | packages/frontend/src/lib/components/Header.svelte | 2 | ||||
| -rw-r--r-- | packages/frontend/src/lib/tabs.svelte.ts | 47 | ||||
| -rw-r--r-- | packages/frontend/src/lib/types.ts | 2 |
3 files changed, 48 insertions, 3 deletions
diff --git a/packages/frontend/src/lib/components/Header.svelte b/packages/frontend/src/lib/components/Header.svelte index 06b8ac1..713e916 100644 --- a/packages/frontend/src/lib/components/Header.svelte +++ b/packages/frontend/src/lib/components/Header.svelte @@ -26,7 +26,7 @@ async function handleCopy() { } </script> -<header class="navbar bg-base-200 border-b border-base-300 px-4 min-h-14 flex-shrink-0"> +<header class="navbar bg-base-200 px-4 min-h-14 flex-shrink-0"> <div class="navbar-start"> <button class="text-xl font-bold tracking-tight btn btn-ghost px-0" onclick={() => router.navigate("dashboard")}>Dispatch</button> </div> diff --git a/packages/frontend/src/lib/tabs.svelte.ts b/packages/frontend/src/lib/tabs.svelte.ts index c488a92..a48be64 100644 --- a/packages/frontend/src/lib/tabs.svelte.ts +++ b/packages/frontend/src/lib/tabs.svelte.ts @@ -50,6 +50,8 @@ export interface Tab { agentSlug: string | null; /** Scope of the selected agent */ agentScope: string | null; + /** Ordered key+model fallback hierarchy from the selected agent */ + agentModels: Array<{ key_id: string; model_id: string }> | null; /** Custom working directory override for this tab */ workingDirectory: string | null; /** Messages queued to be sent once the agent finishes its current run */ @@ -118,6 +120,7 @@ function createTabStore() { persistent: true, agentSlug: null, agentScope: null, + agentModels: null, workingDirectory: null, queuedMessages: [], }; @@ -210,6 +213,7 @@ function createTabStore() { persistent: true, agentSlug: null, agentScope: null, + agentModels: null, workingDirectory: null, queuedMessages: [], }; @@ -408,6 +412,25 @@ function createTabStore() { } break; } + case "notice": { + if (tabId) { + const noticeMsg: ChatMessage = { + id: generateId(), + role: "assistant", + content: [{ type: "text", text: event.message }], + isStreaming: false, + debugInfo: makeDebugInfo({ notice: event.message }), + }; + const tabN = getTabById(tabId); + if (tabN) { + updateTab(tabId, { + messages: [...tabN.messages, noticeMsg], + currentAssistantId: null, + }); + } + } + break; + } case "permission-prompt": { pendingPermissions = event.pending; break; @@ -481,6 +504,7 @@ function createTabStore() { persistent: newTabEvent.parentTabId == null, agentSlug: null, agentScope: null, + agentModels: null, workingDirectory: newTabEvent.workingDirectory ?? null, queuedMessages: [], }; @@ -644,6 +668,7 @@ function createTabStore() { const patch: Partial<Tab> = { agentSlug: defaultAgent.slug, agentScope: defaultAgent.scope, + agentModels: defaultAgent.models, workingDirectory: defaultAgent.cwd || null, }; if (firstModel) { @@ -805,6 +830,7 @@ function createTabStore() { message: messageToSend, ...(tab.keyId ? { keyId: tab.keyId } : {}), ...(tab.modelId ? { modelId: tab.modelId } : {}), + ...(tab.agentModels ? { agentModels: tab.agentModels } : {}), reasoningEffort: tab.reasoningEffort, ...(tab.workingDirectory ? { workingDirectory: tab.workingDirectory } : {}), ...(queueId ? { queueId } : {}), @@ -935,7 +961,12 @@ function createTabStore() { if (!agent) { // Switch back to manual mode — clear agent and reset working directory - updateTab(tab.id, { agentSlug: null, agentScope: null, workingDirectory: null }); + updateTab(tab.id, { + agentSlug: null, + agentScope: null, + agentModels: null, + workingDirectory: null, + }); return; } @@ -944,6 +975,7 @@ function createTabStore() { const patch: Partial<Tab> = { agentSlug: agent.slug, agentScope: agent.scope, + agentModels: agent.models, workingDirectory: agent.cwd || null, }; if (firstModel) { @@ -1039,6 +1071,8 @@ function createTabStore() { `All tab IDs: ${tabs.map((t) => t.id).join(", ")}`, "", ]; + const TOOL_RESULT_MAX = 300; + for (const msg of tab.messages) { const role = msg.role === "user" ? "User" : msg.role === "system" ? "System" : "Assistant"; lines.push(`--- ${role} ---`); @@ -1047,7 +1081,16 @@ function createTabStore() { if (seg.type === "text") lines.push(seg.text); else if (seg.type === "tool-call") { lines.push(` [Tool: ${seg.name}]`); - if (seg.result !== undefined) lines.push(` Result: ${seg.result}`); + if (seg.result !== undefined) { + const result = String(seg.result); + if (result.length > TOOL_RESULT_MAX) { + lines.push( + ` Result: ${result.slice(0, TOOL_RESULT_MAX)}... [truncated, ${result.length} chars total]`, + ); + } else { + lines.push(` Result: ${result}`); + } + } } } lines.push(""); diff --git a/packages/frontend/src/lib/types.ts b/packages/frontend/src/lib/types.ts index 79b2867..95ffb2b 100644 --- a/packages/frontend/src/lib/types.ts +++ b/packages/frontend/src/lib/types.ts @@ -10,6 +10,7 @@ export interface ToolCallDisplay { export interface DebugInfo { timestamp: string; error?: string; + notice?: string; model?: string; apiBase?: string; connectionStatus?: string; @@ -51,6 +52,7 @@ export type AgentEvent = toolResult: { toolCallId: string; result: string; isError: boolean }; } | { type: "error"; error: string } + | { type: "notice"; message: string } | { type: "task-list-update"; tasks: TaskItem[] } | { type: "config-reload" } | { |
