diff options
| author | Adam Malczewski <[email protected]> | 2026-05-23 05:06:12 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-05-23 05:06:12 +0900 |
| commit | 9287cccb29d135ea19f2612c26f3090c94820d8c (patch) | |
| tree | 2d68e8cacf6d71786f305d5f4a512a68f19137c5 /packages/core/src/agent | |
| parent | ef427d3eae77fca716c203dd8bd84939710c518a (diff) | |
| download | dispatch-9287cccb29d135ea19f2612c26f3090c94820d8c.tar.gz dispatch-9287cccb29d135ea19f2612c26f3090c94820d8c.zip | |
feat: add is_subagent flag to agents, fix all lint/type/test issues
- Add is_subagent checkbox to agent editor; subagents are hidden from Chat Settings
- Add is_subagent field to AgentDefinition type, TOML serialization, and API route
- Filter subagents from ModelSelector agent list
- Fix all biome lint/format errors across codebase (useLiteralKeys, noNonNullAssertion, noExplicitAny, formatting, import sorting)
- Fix svelte-check errors (type narrowing in SkillsBrowser, ToolPermissions, SidebarPanel)
- Fix a11y warnings in App.svelte (label-control associations)
- Fix test mocks missing BackgroundShellStore, BackgroundTranscriptStore, createWebSearchTool, createYoutubeTranscribeTool
- Update stale 409 test to match current message-queuing behavior
- Exclude packaging/ and release/ dirs from biome to avoid linting stale build artifacts
Diffstat (limited to 'packages/core/src/agent')
| -rw-r--r-- | packages/core/src/agent/agent.ts | 102 |
1 files changed, 49 insertions, 53 deletions
diff --git a/packages/core/src/agent/agent.ts b/packages/core/src/agent/agent.ts index 64a602b..6f1a5a4 100644 --- a/packages/core/src/agent/agent.ts +++ b/packages/core/src/agent/agent.ts @@ -188,12 +188,12 @@ export class Agent { } try { - const execPromise = tool.execute(tc.arguments, { - onOutput: (data: string, stream: "stdout" | "stderr") => { - shellOutputQueue.push({ data, stream }); - }, - queueCallbacks: this.queueCallbacks, - }); + const execPromise = tool.execute(tc.arguments, { + onOutput: (data: string, stream: "stdout" | "stderr") => { + shellOutputQueue.push({ data, stream }); + }, + queueCallbacks: this.queueCallbacks, + }); const rawResult = await execPromise; const resultStr = typeof rawResult === "string" ? rawResult : JSON.stringify(rawResult); @@ -320,9 +320,7 @@ export class Agent { } } catch (streamErr) { const errMsg = streamErr instanceof Error ? streamErr.message : String(streamErr); - const unavailMatch = errMsg.match( - /tried to call unavailable tool '([^']+)'/i, - ); + const unavailMatch = errMsg.match(/tried to call unavailable tool '([^']+)'/i); if (!unavailMatch) throw streamErr; // Model tried to call an unavailable tool. @@ -391,8 +389,8 @@ export class Agent { let toolResult: ToolResult | undefined; while (toolResult === undefined) { if (shellOutputQueue.length > 0) { - const item = shellOutputQueue.shift()!; - yield { type: "shell-output", data: item.data, stream: item.stream }; + const item = shellOutputQueue.shift(); + if (item) yield { type: "shell-output", data: item.data, stream: item.stream }; continue; } const raceResult = await Promise.race([ @@ -406,30 +404,28 @@ export class Agent { } } - // Drain any remaining shell output emitted before we read the result - while (shellOutputQueue.length > 0) { - const item = shellOutputQueue.shift()!; - yield { type: "shell-output", data: item.data, stream: item.stream }; - } + // Drain any remaining shell output emitted before we read the result + while (shellOutputQueue.length > 0) { + const item = shellOutputQueue.shift(); + if (item) yield { type: "shell-output", data: item.data, stream: item.stream }; + } - // Check for queued user messages and append them to the tool result - let finalToolResult = toolResult; - if (this.queueCallbacks) { - const queuedMsgs = this.queueCallbacks.dequeueMessages(); - if (queuedMsgs.length > 0) { - const userMessages = queuedMsgs - .map((m) => m.message) - .join("\n---\n"); - finalToolResult = { - ...toolResult, - result: `${toolResult.result}\n\n[USER INTERRUPT]\nThe user has sent you message(s) while you were working. You MUST address these before continuing with your current task:\n\n${userMessages}`, - }; + // Check for queued user messages and append them to the tool result + let finalToolResult = toolResult; + if (this.queueCallbacks) { + const queuedMsgs = this.queueCallbacks.dequeueMessages(); + if (queuedMsgs.length > 0) { + const userMessages = queuedMsgs.map((m) => m.message).join("\n---\n"); + finalToolResult = { + ...toolResult, + result: `${toolResult.result}\n\n[USER INTERRUPT]\nThe user has sent you message(s) while you were working. You MUST address these before continuing with your current task:\n\n${userMessages}`, + }; + } } - } - stepToolResults.push(finalToolResult); - allToolResults.push(finalToolResult); - yield { type: "tool-result", toolResult: finalToolResult }; + stepToolResults.push(finalToolResult); + allToolResults.push(finalToolResult); + yield { type: "tool-result", toolResult: finalToolResult }; } // Add tool results back to step messages so LLM can see them @@ -451,29 +447,29 @@ export class Agent { } } - const assistantMessage: ChatMessage = { - role: "assistant", - content: finalText, - toolCalls: allToolCalls.length > 0 ? allToolCalls : undefined, - toolResults: allToolResults.length > 0 ? allToolResults : undefined, - }; - this.messages.push(assistantMessage); - - // Drain any remaining queued messages that arrived after the last tool call - if (this.queueCallbacks) { - const remaining = this.queueCallbacks.dequeueMessages(); - if (remaining.length > 0) { - // These messages arrived too late to be injected into a tool result. - // Append them as a user message to the conversation so they're not lost. - const userMessages = remaining.map(m => m.message).join("\n---\n"); - this.messages.push({ - role: "user", - content: userMessages, - }); + const assistantMessage: ChatMessage = { + role: "assistant", + content: finalText, + toolCalls: allToolCalls.length > 0 ? allToolCalls : undefined, + toolResults: allToolResults.length > 0 ? allToolResults : undefined, + }; + this.messages.push(assistantMessage); + + // Drain any remaining queued messages that arrived after the last tool call + if (this.queueCallbacks) { + const remaining = this.queueCallbacks.dequeueMessages(); + if (remaining.length > 0) { + // These messages arrived too late to be injected into a tool result. + // Append them as a user message to the conversation so they're not lost. + const userMessages = remaining.map((m) => m.message).join("\n---\n"); + this.messages.push({ + role: "user", + content: userMessages, + }); + } } - } - yield { type: "done", message: assistantMessage }; + yield { type: "done", message: assistantMessage }; } catch (err) { const errorMsg = formatError(err, this.config); yield { type: "error", error: errorMsg }; |
