summaryrefslogtreecommitdiffhomepage
path: root/packages/core/src/agent
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-05-23 05:06:12 +0900
committerAdam Malczewski <[email protected]>2026-05-23 05:06:12 +0900
commit9287cccb29d135ea19f2612c26f3090c94820d8c (patch)
tree2d68e8cacf6d71786f305d5f4a512a68f19137c5 /packages/core/src/agent
parentef427d3eae77fca716c203dd8bd84939710c518a (diff)
downloaddispatch-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.ts102
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 };