): ParsedAg
let tools = parseArrayOrCSV(frontmatter.tools)?.map(tool => tool.toLowerCase());
- // Subagents with explicit tool lists always need submit_result
- if (tools && !tools.includes("submit_result")) {
- tools = [...tools, "submit_result"];
+ // Subagents with explicit tool lists always need yield
+ if (tools && !tools.includes("yield")) {
+ tools = [...tools, "yield"];
}
// Parse spawns field (array, "*", or CSV)
diff --git a/packages/coding-agent/src/export/html/template.generated.ts b/packages/coding-agent/src/export/html/template.generated.ts
index 8ed8ba91e..2aae86fb9 100644
--- a/packages/coding-agent/src/export/html/template.generated.ts
+++ b/packages/coding-agent/src/export/html/template.generated.ts
@@ -1,2 +1,2 @@
// Auto-generated by scripts/generate-template.ts - DO NOT EDIT
-export const TEMPLATE = "\n\n\n \n \n Session Export \n \n \n\n\n \n \n \n \n \n
\n \n
\n \n
\n
\n
\n
\n\n \n \n \n \n\n\n";
+export const TEMPLATE = "\n\n\n \n \n Session Export \n \n \n\n\n \n \n \n \n \n
\n \n
\n \n
\n
\n
\n
\n\n \n \n \n \n\n\n";
diff --git a/packages/coding-agent/src/export/html/template.js b/packages/coding-agent/src/export/html/template.js
index f2f9c88d9..69c8129cd 100644
--- a/packages/coding-agent/src/export/html/template.js
+++ b/packages/coding-agent/src/export/html/template.js
@@ -1178,8 +1178,8 @@
return html;
}
- function renderSubmitResult(name, args, result, ctx) {
- let html = toolHead('submit_result');
+ function renderYield(name, args, result, ctx) {
+ let html = toolHead('yield');
if (args.data !== undefined) {
html += '';
}
@@ -1287,7 +1287,7 @@
gh_search_issues: renderGh,
gh_search_prs: renderGh,
render_mermaid: renderMermaid,
- submit_result: renderSubmitResult,
+ yield: renderYield,
report_finding: renderReportFinding,
report_tool_issue: renderReportToolIssue,
calc: renderCalc,
diff --git a/packages/coding-agent/src/prompts/agents/librarian.md b/packages/coding-agent/src/prompts/agents/librarian.md
index 3098e7dfd..e4af7fc7a 100644
--- a/packages/coding-agent/src/prompts/agents/librarian.md
+++ b/packages/coding-agent/src/prompts/agents/librarian.md
@@ -98,7 +98,7 @@ Before acting, determine what kind of question this is:
- For API signatures: copy verbatim from source. You **MUST NOT** paraphrase or reconstruct from memory.
## 5. Report
-- Call `submit_result` with structured findings.
+- Call `yield` with structured findings.
- Every `sources` entry **MUST** include a verbatim excerpt.
- The `api` array **MUST** contain exact signatures copied from source.
- Clean up cloned repos: `rm -rf /tmp/librarian-*`.
diff --git a/packages/coding-agent/src/prompts/agents/reviewer.md b/packages/coding-agent/src/prompts/agents/reviewer.md
index 804eca52f..9d9916450 100644
--- a/packages/coding-agent/src/prompts/agents/reviewer.md
+++ b/packages/coding-agent/src/prompts/agents/reviewer.md
@@ -63,7 +63,7 @@ Your goal is to identify bugs the author would want fixed before merge.
1. Run `git diff` (or `gh pr diff `) to view patch
2. Read modified files for full context
3. Call `report_finding` per issue
-4. Call `submit_result` with verdict
+4. Call `yield` with verdict
Bash is read-only: `git diff`, `git log`, `git show`, `gh pr diff`. You **MUST NOT** make file edits or trigger builds.
@@ -111,7 +111,7 @@ Each `report_finding` requires:
- `file_path`: Absolute path
- `line_start`, `line_end`: Range ≤10 lines, must overlap diff
-Final `submit_result` call (payload under `result.data`):
+Final `yield` call (payload under `result.data`):
- `result.data.overall_correctness`: "correct" (no bugs/blockers) or "incorrect"
- `result.data.explanation`: Plain text, 1-3 sentences summarizing verdict. Don't repeat findings (captured via `report_finding`).
- `result.data.confidence`: 0.0-1.0
diff --git a/packages/coding-agent/src/prompts/review-request.md b/packages/coding-agent/src/prompts/review-request.md
index 5f82bb52a..b355ed0fd 100644
--- a/packages/coding-agent/src/prompts/review-request.md
+++ b/packages/coding-agent/src/prompts/review-request.md
@@ -40,7 +40,7 @@ Reviewer **MUST**:
2. {{#if skipDiff}}**MUST** run `git diff`/`git show` for assigned files{{else}}**MUST** use diff hunks below (**MUST NOT** re-run git diff){{/if}}
3. **MAY** read full file context as needed via `read`
4. Call `report_finding` per issue
-5. Call `submit_result` with verdict when done
+5. Call `yield` with verdict when done
{{#if skipDiff}}
### Diff Previews
diff --git a/packages/coding-agent/src/prompts/system/subagent-submit-reminder.md b/packages/coding-agent/src/prompts/system/subagent-submit-reminder.md
deleted file mode 100644
index 038eed18b..000000000
--- a/packages/coding-agent/src/prompts/system/subagent-submit-reminder.md
+++ /dev/null
@@ -1,11 +0,0 @@
-
-You stopped without calling submit_result. This is reminder {{retryCount}} of {{maxRetries}}.
-
-You **MUST** call submit_result as your only action now. Choose one:
-- If task is complete: call submit_result with your result in `result.data`
-- If task failed: call submit_result with `result.error` describing what happened
-
-You **MUST NOT** give up if you can still complete the task through exploration (using available tools or repo context). If you submit an error, you **MUST** include what you tried and the exact blocker.
-
-You **MUST NOT** output text without a tool call. You **MUST** call submit_result to finish.
-
diff --git a/packages/coding-agent/src/prompts/system/subagent-system-prompt.md b/packages/coding-agent/src/prompts/system/subagent-system-prompt.md
index 1e7e9b9b0..09b2266c6 100644
--- a/packages/coding-agent/src/prompts/system/subagent-system-prompt.md
+++ b/packages/coding-agent/src/prompts/system/subagent-system-prompt.md
@@ -15,9 +15,9 @@ If you need additional information, you can find your conversation with the user
{{/if}}
{{SECTION_SEPARATOR "Closure"}}
-No TODO tracking, no progress updates. Execute, call `submit_result`, done.
+No TODO tracking, no progress updates. Execute, call `yield`, done.
-When finished, you **MUST** call `submit_result` exactly once. This is like writing to a ticket, provide what is required, and close it.
+When finished, you **MUST** call `yield` exactly once. This is like writing to a ticket, provide what is required, and close it.
This is your only way to return a result. You **MUST NOT** put JSON in plain text, and you **MUST NOT** substitute a text summary for the structured `result.data` parameter.
@@ -29,7 +29,7 @@ Your result **MUST** match this TypeScript interface:
{{/if}}
{{SECTION_SEPARATOR "Giving Up"}}
-Giving up is a last resort. If truly blocked, you **MUST** call `submit_result` exactly once with `result.error` describing what you tried and the exact blocker.
+Giving up is a last resort. If truly blocked, you **MUST** call `yield` exactly once with `result.error` describing what you tried and the exact blocker.
You **MUST NOT** give up due to uncertainty, missing information obtainable via tools or repo context, or needing a design decision you can derive yourself.
You **MUST** keep going until this ticket is closed. This matters.
diff --git a/packages/coding-agent/src/prompts/system/subagent-yield-reminder.md b/packages/coding-agent/src/prompts/system/subagent-yield-reminder.md
new file mode 100644
index 000000000..d13b73f22
--- /dev/null
+++ b/packages/coding-agent/src/prompts/system/subagent-yield-reminder.md
@@ -0,0 +1,11 @@
+
+You stopped without calling yield. This is reminder {{retryCount}} of {{maxRetries}}.
+
+You **MUST** call yield as your only action now. Choose one:
+- If task is complete: call yield with your result in `result.data`
+- If task failed: call yield with `result.error` describing what happened
+
+You **MUST NOT** give up if you can still complete the task through exploration (using available tools or repo context). If you submit an error, you **MUST** include what you tried and the exact blocker.
+
+You **MUST NOT** output text without a tool call. You **MUST** call yield to finish.
+
diff --git a/packages/coding-agent/src/sdk.ts b/packages/coding-agent/src/sdk.ts
index 5a1e5b6f3..a095c0087 100644
--- a/packages/coding-agent/src/sdk.ts
+++ b/packages/coding-agent/src/sdk.ts
@@ -209,8 +209,8 @@ export interface CreateAgentSessionOptions {
/** Output schema for structured completion (subagents) */
outputSchema?: unknown;
- /** Whether to include the submit_result tool by default */
- requireSubmitResultTool?: boolean;
+ /** Whether to include the yield tool by default */
+ requireYieldTool?: boolean;
/** Task recursion depth (for subagent sessions). Default: 0 */
taskDepth?: number;
/** Parent task ID prefix for nested artifact naming (e.g., "6-Extensions") */
@@ -916,7 +916,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
skills,
eventBus,
outputSchema: options.outputSchema,
- requireSubmitResultTool: options.requireSubmitResultTool,
+ requireYieldTool: options.requireYieldTool,
taskDepth: options.taskDepth ?? 0,
getSessionFile: () => sessionManager.getSessionFile() ?? null,
getPythonKernelOwnerId: () => pythonKernelOwnerId,
diff --git a/packages/coding-agent/src/task/executor.ts b/packages/coding-agent/src/task/executor.ts
index b933e61d9..50fd08062 100644
--- a/packages/coding-agent/src/task/executor.ts
+++ b/packages/coding-agent/src/task/executor.ts
@@ -18,8 +18,8 @@ import { runExtensionCompact, runExtensionSetModel } from "../extensibility/exte
import type { Skill } from "../extensibility/skills";
import { callTool } from "../mcp/client";
import type { MCPManager } from "../mcp/manager";
-import submitReminderTemplate from "../prompts/system/subagent-submit-reminder.md" with { type: "text" };
import subagentSystemPromptTemplate from "../prompts/system/subagent-system-prompt.md" with { type: "text" };
+import submitReminderTemplate from "../prompts/system/subagent-yield-reminder.md" with { type: "text" };
import { createAgentSession, discoverAuthStorage } from "../sdk";
import type { AgentSession, AgentSessionEvent } from "../session/agent-session";
import type { AuthStorage } from "../session/auth-storage";
@@ -223,7 +223,7 @@ function resolveFallbackCompletion(rawOutput: string, outputSchema: unknown): {
return { data: candidate };
}
-export interface SubmitResultItem {
+export interface YieldItem {
data?: unknown;
status?: "success" | "aborted";
error?: string;
@@ -235,7 +235,7 @@ interface FinalizeSubprocessOutputArgs {
stderr: string;
doneAborted: boolean;
signalAborted: boolean;
- submitResultItems?: SubmitResultItem[];
+ yieldItems?: YieldItem[];
reportFindings?: ReviewFinding[];
outputSchema: unknown;
}
@@ -244,44 +244,42 @@ interface FinalizeSubprocessOutputResult {
rawOutput: string;
exitCode: number;
stderr: string;
- abortedViaSubmitResult: boolean;
- hasSubmitResult: boolean;
+ abortedViaYield: boolean;
+ hasYield: boolean;
}
-export const SUBAGENT_WARNING_NULL_SUBMIT_RESULT = "SYSTEM WARNING: Subagent called submit_result with null data.";
-export const SUBAGENT_WARNING_MISSING_SUBMIT_RESULT =
- "SYSTEM WARNING: Subagent exited without calling submit_result tool after 3 reminders.";
+export const SUBAGENT_WARNING_NULL_YIELD = "SYSTEM WARNING: Subagent called yield with null data.";
+export const SUBAGENT_WARNING_MISSING_YIELD =
+ "SYSTEM WARNING: Subagent exited without calling yield tool after 3 reminders.";
export function finalizeSubprocessOutput(args: FinalizeSubprocessOutputArgs): FinalizeSubprocessOutputResult {
let { rawOutput, exitCode, stderr } = args;
- const { submitResultItems, reportFindings, doneAborted, signalAborted, outputSchema } = args;
- let abortedViaSubmitResult = false;
- const hasSubmitResult = Array.isArray(submitResultItems) && submitResultItems.length > 0;
+ const { yieldItems, reportFindings, doneAborted, signalAborted, outputSchema } = args;
+ let abortedViaYield = false;
+ const hasYield = Array.isArray(yieldItems) && yieldItems.length > 0;
- if (hasSubmitResult) {
- const lastSubmitResult = submitResultItems[submitResultItems.length - 1];
- if (lastSubmitResult?.status === "aborted") {
- abortedViaSubmitResult = true;
+ if (hasYield) {
+ const lastYield = yieldItems[yieldItems.length - 1];
+ if (lastYield?.status === "aborted") {
+ abortedViaYield = true;
exitCode = 0;
- stderr = lastSubmitResult.error || "Subagent aborted task";
+ stderr = lastYield.error || "Subagent aborted task";
try {
- rawOutput = JSON.stringify({ aborted: true, error: lastSubmitResult.error }, null, 2);
+ rawOutput = JSON.stringify({ aborted: true, error: lastYield.error }, null, 2);
} catch {
- rawOutput = `{"aborted":true,"error":"${lastSubmitResult.error || "Unknown error"}"}`;
+ rawOutput = `{"aborted":true,"error":"${lastYield.error || "Unknown error"}"}`;
}
} else {
- const submitData = lastSubmitResult?.data;
+ const submitData = lastYield?.data;
if (submitData === null || submitData === undefined) {
- rawOutput = rawOutput
- ? `${SUBAGENT_WARNING_NULL_SUBMIT_RESULT}\n\n${rawOutput}`
- : SUBAGENT_WARNING_NULL_SUBMIT_RESULT;
+ rawOutput = rawOutput ? `${SUBAGENT_WARNING_NULL_YIELD}\n\n${rawOutput}` : SUBAGENT_WARNING_NULL_YIELD;
} else {
const completeData = normalizeCompleteData(submitData, reportFindings);
try {
rawOutput = JSON.stringify(completeData, null, 2) ?? "null";
} catch (err) {
const errorMessage = err instanceof Error ? err.message : String(err);
- rawOutput = `{"error":"Failed to serialize submit_result data: ${errorMessage}"}`;
+ rawOutput = `{"error":"Failed to serialize yield data: ${errorMessage}"}`;
}
exitCode = 0;
stderr = "";
@@ -307,17 +305,15 @@ export function finalizeSubprocessOutput(args: FinalizeSubprocessOutputArgs): Fi
stderr = "";
} else if (exitCode === 0) {
const hasRawOutput = rawOutput.trim().length > 0;
- rawOutput = rawOutput
- ? `${SUBAGENT_WARNING_MISSING_SUBMIT_RESULT}\n\n${rawOutput}`
- : SUBAGENT_WARNING_MISSING_SUBMIT_RESULT;
+ rawOutput = rawOutput ? `${SUBAGENT_WARNING_MISSING_YIELD}\n\n${rawOutput}` : SUBAGENT_WARNING_MISSING_YIELD;
if (hasOutputSchema || !hasRawOutput) {
exitCode = 1;
- stderr = SUBAGENT_WARNING_MISSING_SUBMIT_RESULT;
+ stderr = SUBAGENT_WARNING_MISSING_YIELD;
}
}
}
- return { rawOutput, exitCode, stderr, abortedViaSubmitResult, hasSubmitResult };
+ return { rawOutput, exitCode, stderr, abortedViaYield, hasYield };
}
/**
@@ -564,7 +560,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise void) | null = null;
- let submitResultCalled = false;
+ let yieldCalled = false;
// Accumulate usage incrementally from message_end events (no memory for streaming events)
const accumulatedUsage = {
@@ -789,8 +785,8 @@ export async function runSubprocess(options: ExecutorOptions): Promise {
if (isAgentEvent(event)) {
try {
@@ -1087,15 +1083,15 @@ export async function runSubprocess(options: ExecutorOptions): Promise 0 ? finalOutputChunks.join("") : outputChunks.join("");
- const submitResultItems = progress.extractedToolData?.submit_result as SubmitResultItem[] | undefined;
+ const yieldItems = progress.extractedToolData?.yield as YieldItem[] | undefined;
const reportFindings = progress.extractedToolData?.report_finding as ReviewFinding[] | undefined;
const finalized = finalizeSubprocessOutput({
rawOutput,
@@ -1194,17 +1190,16 @@ export async function runSubprocess(options: ExecutorOptions): Promise | undefined;
+ const completeData = progress.extractedToolData.yield as Array<{ data: unknown }> | undefined;
const reportFindingData = normalizeReportFindings(progress.extractedToolData.report_finding);
const reviewData = completeData
?.map(c => c.data as SubmitReviewDetails)
@@ -731,9 +731,7 @@ function renderAgentResult(result: SingleResult, isLast: boolean, expanded: bool
const prefix = isLast ? theme.fg("dim", theme.tree.last) : theme.fg("dim", theme.tree.branch);
const continuePrefix = isLast ? " " : `${theme.fg("dim", theme.tree.vertical)} `;
- const { warning: missingCompleteWarning, rest: outputWithoutWarning } = extractMissingSubmitResultWarning(
- result.output,
- );
+ const { warning: missingCompleteWarning, rest: outputWithoutWarning } = extractMissingYieldWarning(result.output);
const aborted = result.aborted ?? false;
const mergeFailed = !aborted && result.exitCode === 0 && !!result.error;
const success = !aborted && result.exitCode === 0 && !result.error;
@@ -783,11 +781,11 @@ function renderAgentResult(result: SingleResult, isLast: boolean, expanded: bool
`${continuePrefix}${theme.fg("error", theme.status.aborted)} ${theme.fg("dim", truncateToWidth(replaceTabs(result.abortReason), 80))}`,
);
}
- // Check for review result (submit_result with review schema + report_finding)
- const completeData = result.extractedToolData?.submit_result as Array<{ data: unknown }> | undefined;
+ // Check for review result (yield with review schema + report_finding)
+ const completeData = result.extractedToolData?.yield as Array<{ data: unknown }> | undefined;
const reportFindingData = normalizeReportFindings(result.extractedToolData?.report_finding);
- // Extract review verdict from submit_result tool's data field if it matches SubmitReviewDetails
+ // Extract review verdict from yield tool's data field if it matches SubmitReviewDetails
const reviewData = completeData
?.map(c => c.data as SubmitReviewDetails)
.filter(d => d && typeof d === "object" && "overall_correctness" in d);
@@ -804,7 +802,7 @@ function renderAgentResult(result: SingleResult, isLast: boolean, expanded: bool
const hasCompleteData = completeData && completeData.length > 0;
const message = hasCompleteData
? "Review verdict missing expected fields"
- : "Review incomplete (submit_result not called)";
+ : "Review incomplete (yield not called)";
lines.push(`${continuePrefix}${theme.fg("warning", theme.status.warning)} ${theme.fg("dim", message)}`);
lines.push(`${continuePrefix}${formatFindingSummary(reportFindingData, theme)}`);
lines.push(...renderFindings(reportFindingData, continuePrefix, expanded, theme));
@@ -817,7 +815,7 @@ function renderAgentResult(result: SingleResult, isLast: boolean, expanded: bool
if (result.extractedToolData) {
for (const [toolName, dataArray] of Object.entries(result.extractedToolData)) {
// Skip review tools - handled above
- if (toolName === "submit_result" || toolName === "report_finding") continue;
+ if (toolName === "yield" || toolName === "report_finding") continue;
const handler = subprocessToolRegistry.getHandler(toolName);
if (handler?.renderFinal && (dataArray as unknown[]).length > 0) {
diff --git a/packages/coding-agent/src/tools/index.ts b/packages/coding-agent/src/tools/index.ts
index 72a58832d..24f6da2d6 100644
--- a/packages/coding-agent/src/tools/index.ts
+++ b/packages/coding-agent/src/tools/index.ts
@@ -53,9 +53,9 @@ import { ResolveTool } from "./resolve";
import { reportFindingTool } from "./review";
import { SearchToolBm25Tool } from "./search-tool-bm25";
import { loadSshTool } from "./ssh";
-import { SubmitResultTool } from "./submit-result";
import { type TodoPhase, TodoWriteTool } from "./todo-write";
import { WriteTool } from "./write";
+import { YieldTool } from "./yield";
// Exa MCP tools (22 tools)
@@ -91,10 +91,10 @@ export * from "./resolve";
export * from "./review";
export * from "./search-tool-bm25";
export * from "./ssh";
-export * from "./submit-result";
export * from "./todo-write";
export * from "./vim";
export * from "./write";
+export * from "./yield";
/** Tool type (AgentTool from pi-ai) */
export type Tool = AgentTool;
@@ -131,8 +131,8 @@ export interface ToolSession {
eventBus?: EventBus;
/** Output schema for structured completion (subagents) */
outputSchema?: unknown;
- /** Whether to include the submit_result tool by default */
- requireSubmitResultTool?: boolean;
+ /** Whether to include the yield tool by default */
+ requireYieldTool?: boolean;
/** Task recursion depth (0 = top-level, 1 = first child, etc.) */
taskDepth?: number;
/** Get session file */
@@ -245,7 +245,7 @@ export const BUILTIN_TOOLS: Record = {
};
export const HIDDEN_TOOLS: Record = {
- submit_result: s => new SubmitResultTool(s),
+ yield: s => new YieldTool(s),
report_finding: () => reportFindingTool,
report_tool_issue: s => createReportToolIssueTool(s),
exit_plan_mode: s => new ExitPlanModeTool(s),
@@ -288,7 +288,7 @@ function getPythonModeFromEnv(): PythonToolMode | null {
* Create tools from BUILTIN_TOOLS registry.
*/
export async function createTools(session: ToolSession, toolNames?: string[]): Promise {
- const includeSubmitResult = session.requireSubmitResultTool === true;
+ const includeYield = session.requireYieldTool === true;
const enableLsp = session.enableLsp ?? true;
const requestedTools =
toolNames && toolNames.length > 0 ? [...new Set(toolNames.map(name => name.toLowerCase()))] : undefined;
@@ -390,7 +390,7 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
if (name === "bash") return allowBash;
if (name === "python") return allowPython;
if (name === "debug") return session.settings.get("debug.enabled");
- if (name === "todo_write") return !includeSubmitResult && session.settings.get("todo.enabled");
+ if (name === "todo_write") return !includeYield && session.settings.get("todo.enabled");
if (name === "find") return session.settings.get("find.enabled");
if (name === "grep") return session.settings.get("grep.enabled");
if (name.startsWith("gh_")) return session.settings.get("github.enabled");
@@ -411,8 +411,8 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
}
return true;
};
- if (includeSubmitResult && requestedTools && !requestedTools.includes("submit_result")) {
- requestedTools.push("submit_result");
+ if (includeYield && requestedTools && !requestedTools.includes("yield")) {
+ requestedTools.push("yield");
}
const filteredRequestedTools = requestedTools?.filter(name => name in allTools && isToolAllowed(name));
@@ -421,7 +421,7 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
? filteredRequestedTools.filter(name => name !== "resolve").map(name => [name, allTools[name]] as const)
: [
...Object.entries(BUILTIN_TOOLS).filter(([name]) => isToolAllowed(name)),
- ...(includeSubmitResult ? ([["submit_result", HIDDEN_TOOLS.submit_result]] as const) : []),
+ ...(includeYield ? ([["yield", HIDDEN_TOOLS.yield]] as const) : []),
...([["exit_plan_mode", HIDDEN_TOOLS.exit_plan_mode]] as const),
];
diff --git a/packages/coding-agent/src/tools/review.ts b/packages/coding-agent/src/tools/review.ts
index 5269c1ed5..17300e731 100644
--- a/packages/coding-agent/src/tools/review.ts
+++ b/packages/coding-agent/src/tools/review.ts
@@ -3,7 +3,7 @@
*
* Used by the reviewer agent to report findings in a structured way.
* Hidden by default - only enabled when explicitly listed in agent's tools.
- * Reviewers finish via `submit_result` tool with SubmitReviewDetails schema.
+ * Reviewers finish via `yield` tool with SubmitReviewDetails schema.
*/
// ─────────────────────────────────────────────────────────────────────────────
// Subprocess tool handlers - registered for extraction/rendering in task tool
@@ -131,7 +131,7 @@ export function parseReportFindingDetails(value: unknown): ReportFindingDetails
export const reportFindingTool: AgentTool = {
name: "report_finding",
label: "Report Finding",
- description: "Report a code review finding. Use this for each issue found. Call submit_result when done.",
+ description: "Report a code review finding. Use this for each issue found. Call yield when done.",
parameters: ReportFindingParams,
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
const { title, body, priority, confidence, file_path, line_start, line_end } = params;
@@ -186,7 +186,7 @@ export const reportFindingTool: AgentTool {
- readonly name = "submit_result";
+export class YieldTool implements AgentTool {
+ readonly name = "yield";
readonly label = "Submit Result";
readonly description =
"Finish the task with structured JSON output. Call exactly once at the end of the task.\n\n" +
@@ -143,9 +143,9 @@ export class SubmitResultTool implements AgentTool
_toolCallId: string,
params: Static,
_signal?: AbortSignal,
- _onUpdate?: AgentToolUpdateCallback,
+ _onUpdate?: AgentToolUpdateCallback,
_context?: AgentToolContext,
- ): Promise> {
+ ): Promise> {
const raw = params as Record;
const rawResult = raw.result;
if (!rawResult || typeof rawResult !== "object" || Array.isArray(rawResult)) {
@@ -169,7 +169,7 @@ export class SubmitResultTool implements AgentTool
let schemaValidationOverridden = false;
if (status === "success") {
if (data === undefined || data === null) {
- throw new Error("data is required when submit_result indicates success");
+ throw new Error("data is required when yield indicates success");
}
if (this.#validate && !this.#validate(data)) {
this.#schemaValidationFailures++;
@@ -194,7 +194,7 @@ export class SubmitResultTool implements AgentTool
}
// Register subprocess tool handler for extraction + termination.
-subprocessToolRegistry.register("submit_result", {
+subprocessToolRegistry.register("yield", {
extractData: event => {
const details = event.result?.details;
if (!details || typeof details !== "object") return undefined;
diff --git a/packages/coding-agent/test/discovery/agent-fields.test.ts b/packages/coding-agent/test/discovery/agent-fields.test.ts
index 4491400a9..f5adc9cba 100644
--- a/packages/coding-agent/test/discovery/agent-fields.test.ts
+++ b/packages/coding-agent/test/discovery/agent-fields.test.ts
@@ -64,6 +64,6 @@ describe("parseAgentFields", () => {
tools: ["Read", "Grep"],
});
- expect(fields?.tools).toEqual(["read", "grep", "submit_result"]);
+ expect(fields?.tools).toEqual(["read", "grep", "yield"]);
});
});
diff --git a/packages/coding-agent/test/model-registry-runtime-provider.test.ts b/packages/coding-agent/test/model-registry-runtime-provider.test.ts
index c6b9af499..f0af769c0 100644
--- a/packages/coding-agent/test/model-registry-runtime-provider.test.ts
+++ b/packages/coding-agent/test/model-registry-runtime-provider.test.ts
@@ -234,7 +234,6 @@ describe("ModelRegistry runtime provider registration", () => {
expect(model?.api).toBe("openai-completions");
});
-
test("headers-only runtime override preserves existing baseUrl across refresh", async () => {
const registry = new ModelRegistry(authStorage, modelsJsonPath);
const modelId = "runtime-headers-only-baseurl-survivor";
@@ -292,11 +291,7 @@ describe("ModelRegistry runtime provider registration", () => {
const registry = new ModelRegistry(authStorage, modelsJsonPath);
expect(registry.find("anthropic", modelId)?.headers?.[sharedHeader]).toBe(configHeaderValue);
- registry.registerProvider(
- "anthropic",
- { headers: { [sharedHeader]: runtimeHeaderValue } },
- "ext://runtime",
- );
+ registry.registerProvider("anthropic", { headers: { [sharedHeader]: runtimeHeaderValue } }, "ext://runtime");
await expectProviderHeaderAcrossRefresh(registry, "anthropic", sharedHeader, runtimeHeaderValue);
registry.clearSourceRegistrations("ext://runtime");
@@ -433,18 +428,10 @@ describe("ModelRegistry runtime provider registration", () => {
const sourceAHeader = "X-Source-A-Header";
const sourceBHeader = "X-Source-B-Header";
- registry.registerProvider(
- providerName,
- { headers: { [sourceAHeader]: "from-source-a" } },
- "ext://a",
- );
+ registry.registerProvider(providerName, { headers: { [sourceAHeader]: "from-source-a" } }, "ext://a");
expectProviderHeader(registry, providerName, sourceAHeader, "from-source-a");
- registry.registerProvider(
- providerName,
- { headers: { [sourceBHeader]: "from-source-b" } },
- "ext://b",
- );
+ registry.registerProvider(providerName, { headers: { [sourceBHeader]: "from-source-b" } }, "ext://b");
await expectProviderHeaderAcrossRefresh(registry, providerName, sourceAHeader, undefined);
expectProviderHeader(registry, providerName, sourceBHeader, "from-source-b");
});
diff --git a/packages/coding-agent/test/task/executor-subagent-reminders.test.ts b/packages/coding-agent/test/task/executor-subagent-reminders.test.ts
index a16017a19..3e3194849 100644
--- a/packages/coding-agent/test/task/executor-subagent-reminders.test.ts
+++ b/packages/coding-agent/test/task/executor-subagent-reminders.test.ts
@@ -6,7 +6,7 @@ import type { CreateAgentSessionResult } from "../../src/sdk";
import * as sdkModule from "../../src/sdk";
import type { AgentSession, AgentSessionEvent, PromptOptions } from "../../src/session/agent-session";
import type { AuthStorage } from "../../src/session/auth-storage";
-import { runSubprocess, SUBAGENT_WARNING_MISSING_SUBMIT_RESULT } from "../../src/task/executor";
+import { runSubprocess, SUBAGENT_WARNING_MISSING_YIELD } from "../../src/task/executor";
import type { AgentDefinition } from "../../src/task/types";
import { EventBus } from "../../src/utils/event-bus";
@@ -55,7 +55,7 @@ function createMockSession(
sessionManager: {
appendSessionInit: () => {},
},
- getActiveToolNames: () => ["read", "submit_result"],
+ getActiveToolNames: () => ["read", "yield"],
setActiveToolsByName: async (_toolNames: string[]) => {},
subscribe: (listener: (event: AgentSessionEvent) => void) => {
listeners.push(listener);
@@ -90,7 +90,7 @@ function mockCreateAgentSession(session: AgentSession) {
return vi.spyOn(sdkModule, "createAgentSession").mockResolvedValue(createSessionResult(session));
}
-describe("runSubprocess submit_result reminders", () => {
+describe("runSubprocess yield reminders", () => {
afterEach(() => {
vi.restoreAllMocks();
});
@@ -114,7 +114,7 @@ describe("runSubprocess submit_result reminders", () => {
enableLsp: false,
};
- it("sends reminder prompt when subagent stops without submit_result", async () => {
+ it("sends reminder prompt when subagent stops without yield", async () => {
const prompts: string[] = [];
const promptOptions: Array = [];
const session = createMockSession(({ text, options, promptIndex, emit, state }) => {
@@ -129,7 +129,7 @@ describe("runSubprocess submit_result reminders", () => {
emit({
type: "tool_execution_end",
toolCallId: "tool-1",
- toolName: "submit_result",
+ toolName: "yield",
result: {
content: [{ type: "text", text: "Result submitted." }],
details: { status: "success", data: { done: true } },
@@ -145,12 +145,12 @@ describe("runSubprocess submit_result reminders", () => {
expect(promptOptions).toHaveLength(2);
expect(promptOptions[0]?.attribution).toBe("agent");
expect(promptOptions[1]?.attribution).toBe("agent");
- expect(prompts[1]).toContain("You stopped without calling submit_result");
+ expect(prompts[1]).toContain("You stopped without calling yield");
expect(result.output).toContain('"done": true');
expect(result.output.includes("SYSTEM WARNING")).toBe(false);
});
- it("keeps null submit_result warning when subagent submits success without data", async () => {
+ it("keeps null yield warning when subagent submits success without data", async () => {
const session = createMockSession(({ promptIndex, emit, state }) => {
if (promptIndex === 1) {
const assistant = createAssistantStopMessage("partial output");
@@ -161,7 +161,7 @@ describe("runSubprocess submit_result reminders", () => {
emit({
type: "tool_execution_end",
toolCallId: "tool-2",
- toolName: "submit_result",
+ toolName: "yield",
result: {
content: [{ type: "text", text: "Result submitted." }],
details: { status: "success" },
@@ -173,21 +173,21 @@ describe("runSubprocess submit_result reminders", () => {
mockCreateAgentSession(session);
const result = await runSubprocess({ ...baseOptions, id: "subagent-2" });
- expect(result.output).toContain("SYSTEM WARNING: Subagent called submit_result with null data.");
+ expect(result.output).toContain("SYSTEM WARNING: Subagent called yield with null data.");
});
- it("retries when submit_result tool returns an error before succeeding", async () => {
+ it("retries when yield tool returns an error before succeeding", async () => {
const prompts: string[] = [];
const session = createMockSession(({ text, promptIndex, emit, state }) => {
prompts.push(text);
if (promptIndex === 1) {
- const assistant = createAssistantStopMessage("attempted submit_result");
+ const assistant = createAssistantStopMessage("attempted yield");
state.messages.push(assistant);
emit({ type: "message_end", message: assistant });
emit({
type: "tool_execution_end",
toolCallId: "tool-error",
- toolName: "submit_result",
+ toolName: "yield",
result: {
content: [{ type: "text", text: "Output does not match schema" }],
details: { status: "error", error: "Output does not match schema" },
@@ -199,7 +199,7 @@ describe("runSubprocess submit_result reminders", () => {
emit({
type: "tool_execution_end",
toolCallId: "tool-success",
- toolName: "submit_result",
+ toolName: "yield",
result: {
content: [{ type: "text", text: "Result submitted." }],
details: { status: "success", data: { ok: true } },
@@ -221,7 +221,7 @@ describe("runSubprocess submit_result reminders", () => {
emit({
type: "tool_execution_end",
toolCallId: "tool-thinking-fallback",
- toolName: "submit_result",
+ toolName: "yield",
result: {
content: [{ type: "text", text: "Result submitted." }],
details: { status: "success", data: { ok: true } },
@@ -268,7 +268,7 @@ describe("runSubprocess submit_result reminders", () => {
emit({
type: "tool_execution_end",
toolCallId: `tool-thinking-override-${index}`,
- toolName: "submit_result",
+ toolName: "yield",
result: {
content: [{ type: "text", text: "Result submitted." }],
details: { status: "success", data: { ok: true } },
@@ -292,11 +292,11 @@ describe("runSubprocess submit_result reminders", () => {
expect(createAgentSessionSpy.mock.calls[0]?.[0]?.thinkingLevel).toBe(cases[0].expectedThinkingLevel);
expect(createAgentSessionSpy.mock.calls[1]?.[0]?.thinkingLevel).toBe(cases[1].expectedThinkingLevel);
});
- it("fails after 3 reminders when submit_result is never called for a structured task", async () => {
+ it("fails after 3 reminders when yield is never called for a structured task", async () => {
const prompts: string[] = [];
const session = createMockSession(({ text, promptIndex, emit, state }) => {
prompts.push(text);
- const assistant = createAssistantStopMessage(promptIndex === 1 ? "did work" : "still no submit_result");
+ const assistant = createAssistantStopMessage(promptIndex === 1 ? "did work" : "still no yield");
state.messages.push(assistant);
emit({ type: "message_end", message: assistant });
});
@@ -311,11 +311,11 @@ describe("runSubprocess submit_result reminders", () => {
expect(prompts).toHaveLength(4);
expect(result.exitCode).toBe(1);
expect(result.aborted).toBe(false);
- expect(result.stderr).toBe(SUBAGENT_WARNING_MISSING_SUBMIT_RESULT);
+ expect(result.stderr).toBe(SUBAGENT_WARNING_MISSING_YIELD);
expect(result.abortReason).toBeUndefined();
});
- it("surfaces abort reason when submit_result reports aborted status", async () => {
+ it("surfaces abort reason when yield reports aborted status", async () => {
const session = createMockSession(({ promptIndex, emit, state }) => {
if (promptIndex === 1) {
const assistant = createAssistantStopMessage("cannot proceed");
@@ -325,7 +325,7 @@ describe("runSubprocess submit_result reminders", () => {
emit({
type: "tool_execution_end",
toolCallId: "tool-abort",
- toolName: "submit_result",
+ toolName: "yield",
result: {
content: [{ type: "text", text: "Task aborted: blocked by permissions" }],
details: { status: "aborted", error: "blocked by permissions" },
@@ -336,7 +336,7 @@ describe("runSubprocess submit_result reminders", () => {
mockCreateAgentSession(session);
- const result = await runSubprocess({ ...baseOptions, id: "subagent-aborted-submit-result" });
+ const result = await runSubprocess({ ...baseOptions, id: "subagent-aborted-yield" });
expect(result.aborted).toBe(true);
expect(result.abortReason).toBe("blocked by permissions");
});
diff --git a/packages/coding-agent/test/task/executor-warnings.test.ts b/packages/coding-agent/test/task/executor-warnings.test.ts
index db13f5fce..385151b69 100644
--- a/packages/coding-agent/test/task/executor-warnings.test.ts
+++ b/packages/coding-agent/test/task/executor-warnings.test.ts
@@ -1,39 +1,39 @@
import { describe, expect, it } from "bun:test";
import {
finalizeSubprocessOutput,
- SUBAGENT_WARNING_MISSING_SUBMIT_RESULT,
- SUBAGENT_WARNING_NULL_SUBMIT_RESULT,
+ SUBAGENT_WARNING_MISSING_YIELD,
+ SUBAGENT_WARNING_NULL_YIELD,
} from "../../src/task/executor";
describe("subagent warning injection", () => {
- it("injects null-data warning when submit_result is success without data", () => {
+ it("injects null-data warning when yield is success without data", () => {
const result = finalizeSubprocessOutput({
rawOutput: "partial output",
exitCode: 0,
stderr: "",
doneAborted: false,
signalAborted: false,
- submitResultItems: [{ status: "success" }],
+ yieldItems: [{ status: "success" }],
outputSchema: undefined,
});
- expect(result.rawOutput).toBe(`${SUBAGENT_WARNING_NULL_SUBMIT_RESULT}\n\npartial output`);
- expect(result.hasSubmitResult).toBe(true);
+ expect(result.rawOutput).toBe(`${SUBAGENT_WARNING_NULL_YIELD}\n\npartial output`);
+ expect(result.hasYield).toBe(true);
});
- it("injects missing-submit warning when subagent exits cleanly without submit_result", () => {
+ it("injects missing-submit warning when subagent exits cleanly without yield", () => {
const result = finalizeSubprocessOutput({
rawOutput: "",
exitCode: 0,
stderr: "",
doneAborted: false,
signalAborted: false,
- submitResultItems: undefined,
+ yieldItems: undefined,
outputSchema: { properties: { ok: { type: "boolean" } } },
});
- expect(result.rawOutput).toBe(SUBAGENT_WARNING_MISSING_SUBMIT_RESULT);
- expect(result.hasSubmitResult).toBe(false);
+ expect(result.rawOutput).toBe(SUBAGENT_WARNING_MISSING_YIELD);
+ expect(result.hasYield).toBe(false);
});
it("does not inject missing-submit warning when fallback completion is recoverable", () => {
@@ -43,7 +43,7 @@ describe("subagent warning injection", () => {
stderr: "",
doneAborted: false,
signalAborted: false,
- submitResultItems: undefined,
+ yieldItems: undefined,
outputSchema: { type: "object", properties: { ok: { type: "boolean" } }, required: ["ok"] },
});
@@ -58,13 +58,11 @@ describe("subagent warning injection", () => {
stderr: "",
doneAborted: false,
signalAborted: false,
- submitResultItems: undefined,
+ yieldItems: undefined,
outputSchema: { type: "object", properties: { ok: { type: "boolean" } }, required: ["ok"] },
});
- expect(result.rawOutput).toBe(
- `${SUBAGENT_WARNING_MISSING_SUBMIT_RESULT}\n\nagent stopped after writing analysis`,
- );
+ expect(result.rawOutput).toBe(`${SUBAGENT_WARNING_MISSING_YIELD}\n\nagent stopped after writing analysis`);
});
it("does not inject missing-submit warning when execution exits non-zero", () => {
@@ -74,7 +72,7 @@ describe("subagent warning injection", () => {
stderr: "subagent terminated",
doneAborted: true,
signalAborted: false,
- submitResultItems: undefined,
+ yieldItems: undefined,
outputSchema: { type: "object", properties: { ok: { type: "boolean" } }, required: ["ok"] },
});
@@ -83,32 +81,32 @@ describe("subagent warning injection", () => {
expect(result.exitCode).toBe(1);
});
- it("normalizes explicit aborted submit_result into aborted payload", () => {
+ it("normalizes explicit aborted yield into aborted payload", () => {
const result = finalizeSubprocessOutput({
rawOutput: "partial output",
exitCode: 1,
stderr: "old error",
doneAborted: false,
signalAborted: false,
- submitResultItems: [{ status: "aborted", error: "blocked by permissions" }],
+ yieldItems: [{ status: "aborted", error: "blocked by permissions" }],
outputSchema: undefined,
});
- expect(result.abortedViaSubmitResult).toBe(true);
+ expect(result.abortedViaYield).toBe(true);
expect(result.exitCode).toBe(0);
expect(result.stderr).toBe("blocked by permissions");
expect(result.rawOutput).toContain('"aborted": true');
expect(result.rawOutput).toContain('"blocked by permissions"');
});
- it("accepts successful submit_result data without warning", () => {
+ it("accepts successful yield data without warning", () => {
const result = finalizeSubprocessOutput({
rawOutput: "should be replaced",
exitCode: 1,
stderr: "should clear",
doneAborted: false,
signalAborted: false,
- submitResultItems: [{ status: "success", data: { ok: true } }],
+ yieldItems: [{ status: "success", data: { ok: true } }],
outputSchema: undefined,
});
@@ -125,7 +123,7 @@ describe("subagent warning injection", () => {
stderr: "",
doneAborted: false,
signalAborted: false,
- submitResultItems: undefined,
+ yieldItems: undefined,
outputSchema: undefined,
});
diff --git a/packages/coding-agent/test/tools/index.test.ts b/packages/coding-agent/test/tools/index.test.ts
index 34ea76f29..a3a583377 100644
--- a/packages/coding-agent/test/tools/index.test.ts
+++ b/packages/coding-agent/test/tools/index.test.ts
@@ -177,12 +177,12 @@ describe("createTools", () => {
expect(names).toEqual(["report_finding", "exit_plan_mode"]);
});
- it("includes submit_result tool when required", async () => {
- const session = createTestSession({ requireSubmitResultTool: true });
+ it("includes yield tool when required", async () => {
+ const session = createTestSession({ requireYieldTool: true });
const tools = await createTools(session);
const names = tools.map(t => t.name);
- expect(names).toContain("submit_result");
+ expect(names).toContain("yield");
});
it("excludes ask tool when hasUI is false", async () => {
@@ -250,7 +250,7 @@ describe("createTools", () => {
"report_finding",
"report_tool_issue",
"resolve",
- "submit_result",
+ "yield",
]);
});
});
diff --git a/packages/coding-agent/test/tools/submit-result-extraction.test.ts b/packages/coding-agent/test/tools/yield-extraction.test.ts
similarity index 67%
rename from packages/coding-agent/test/tools/submit-result-extraction.test.ts
rename to packages/coding-agent/test/tools/yield-extraction.test.ts
index 8a9d8ca89..899240a11 100644
--- a/packages/coding-agent/test/tools/submit-result-extraction.test.ts
+++ b/packages/coding-agent/test/tools/yield-extraction.test.ts
@@ -1,14 +1,14 @@
import { describe, expect, it } from "bun:test";
-import "../../src/tools/submit-result";
+import "../../src/tools/yield";
import { subprocessToolRegistry } from "../../src/task/subprocess-tool-registry";
-describe("submit_result subprocess extraction", () => {
- const handler = subprocessToolRegistry.getHandler("submit_result");
+describe("yield subprocess extraction", () => {
+ const handler = subprocessToolRegistry.getHandler("yield");
- it("extracts valid submit_result payloads", () => {
+ it("extracts valid yield payloads", () => {
expect(handler?.extractData).toBeDefined();
const data = handler?.extractData?.({
- toolName: "submit_result",
+ toolName: "yield",
toolCallId: "call-1",
result: {
content: [{ type: "text", text: "Result submitted." }],
@@ -19,9 +19,9 @@ describe("submit_result subprocess extraction", () => {
expect(data).toEqual({ status: "success", data: { ok: true }, error: undefined });
});
- it("ignores malformed submit_result details without status", () => {
+ it("ignores malformed yield details without status", () => {
const data = handler?.extractData?.({
- toolName: "submit_result",
+ toolName: "yield",
toolCallId: "call-2",
result: {
content: [{ type: "text", text: "Tool execution was aborted." }],
diff --git a/packages/coding-agent/test/tools/submit-result.test.ts b/packages/coding-agent/test/tools/yield.test.ts
similarity index 91%
rename from packages/coding-agent/test/tools/submit-result.test.ts
rename to packages/coding-agent/test/tools/yield.test.ts
index 8ebfee9da..1a285cb3b 100644
--- a/packages/coding-agent/test/tools/submit-result.test.ts
+++ b/packages/coding-agent/test/tools/yield.test.ts
@@ -4,7 +4,7 @@ import { enforceStrictSchema } from "@oh-my-pi/pi-ai/utils/schema";
import { validateToolArguments } from "@oh-my-pi/pi-ai/utils/validation";
import { Settings } from "@oh-my-pi/pi-coding-agent/config/settings";
import type { ToolSession } from "@oh-my-pi/pi-coding-agent/tools";
-import { SubmitResultTool } from "@oh-my-pi/pi-coding-agent/tools/submit-result";
+import { YieldTool } from "@oh-my-pi/pi-coding-agent/tools/yield";
function createSession(overrides: Partial = {}): ToolSession {
return {
@@ -34,9 +34,9 @@ function getSuccessDataSchema(parameters: Record): Record {
+describe("YieldTool", () => {
it("exposes top-level object parameters with required result union", () => {
- const tool = new SubmitResultTool(createSession());
+ const tool = new YieldTool(createSession());
const schema = tool.parameters as {
type?: string;
properties?: Record;
@@ -48,19 +48,19 @@ describe("SubmitResultTool", () => {
});
it("accepts success payload with data", async () => {
- const tool = new SubmitResultTool(createSession());
+ const tool = new YieldTool(createSession());
const result = await tool.execute("call-1", { result: { data: { ok: true } } } as never);
expect(result.details).toEqual({ data: { ok: true }, status: "success", error: undefined });
});
it("accepts aborted payload with error only", async () => {
- const tool = new SubmitResultTool(createSession());
+ const tool = new YieldTool(createSession());
const result = await tool.execute("call-2", { result: { error: "blocked" } } as never);
expect(result.details).toEqual({ data: undefined, status: "aborted", error: "blocked" });
});
it("accepts arbitrary data when outputSchema is null", async () => {
- const tool = new SubmitResultTool(createSession({ outputSchema: null }));
+ const tool = new YieldTool(createSession({ outputSchema: null }));
const result = await tool.execute("call-null", { result: { data: { nested: { x: 1 }, ok: true } } } as never);
expect(result.details).toEqual({
data: { nested: { x: 1 }, ok: true },
@@ -70,7 +70,7 @@ describe("SubmitResultTool", () => {
});
it("treats outputSchema true as unconstrained and accepts primitive and array data", async () => {
- const tool = new SubmitResultTool(createSession({ outputSchema: true }));
+ const tool = new YieldTool(createSession({ outputSchema: true }));
const dataSchema = getSuccessDataSchema(tool.parameters as unknown as Record);
expect(dataSchema.type).toBeUndefined();
@@ -85,7 +85,7 @@ describe("SubmitResultTool", () => {
});
});
it("repairs strict schema generation for required-only object output schemas", () => {
- const tool = new SubmitResultTool(
+ const tool = new YieldTool(
createSession({
outputSchema: {
type: "object",
@@ -103,7 +103,7 @@ describe("SubmitResultTool", () => {
});
it("normalizes object/null type arrays into strict-compatible data variants", () => {
- const tool = new SubmitResultTool(
+ const tool = new YieldTool(
createSession({
outputSchema: {
type: ["object", "null"],
@@ -129,7 +129,7 @@ describe("SubmitResultTool", () => {
});
it("converts mixed JTD and JSON Schema output definitions into provider-valid schemas", async () => {
- const tool = new SubmitResultTool(
+ const tool = new YieldTool(
createSession({
outputSchema: {
type: "object",
@@ -189,7 +189,7 @@ describe("SubmitResultTool", () => {
},
],
};
- const tool = new SubmitResultTool(createSession({ outputSchema }));
+ const tool = new YieldTool(createSession({ outputSchema }));
const parametersRecord = tool.parameters as unknown as Record;
// $defs should NOT be in parameters — refs are inlined
expect(parametersRecord.$defs).toBeUndefined();
@@ -232,7 +232,7 @@ describe("SubmitResultTool", () => {
]);
});
it("falls back to unconstrained object data when output schema is invalid", async () => {
- const tool = new SubmitResultTool(
+ const tool = new YieldTool(
createSession({
outputSchema: {
type: "object",
@@ -263,7 +263,7 @@ describe("SubmitResultTool", () => {
const circularSchema: Record = { type: "object" };
circularSchema.self = circularSchema;
- const tool = new SubmitResultTool(createSession({ outputSchema: circularSchema }));
+ const tool = new YieldTool(createSession({ outputSchema: circularSchema }));
const dataSchema = getSuccessDataSchema(tool.parameters as unknown as Record);
expect(tool.strict).toBe(false);
@@ -299,7 +299,7 @@ describe("SubmitResultTool", () => {
return root;
};
- const tool = new SubmitResultTool(createSession({ outputSchema: buildDeepSchema(20_000) }));
+ const tool = new YieldTool(createSession({ outputSchema: buildDeepSchema(20_000) }));
const dataSchema = getSuccessDataSchema(tool.parameters as unknown as Record);
expect(tool.strict).toBe(false);
@@ -311,7 +311,7 @@ describe("SubmitResultTool", () => {
it("handles non-object output schemas without blocking successful result submission", async () => {
for (const outputSchema of [[], 123, false]) {
- const tool = new SubmitResultTool(createSession({ outputSchema }));
+ const tool = new YieldTool(createSession({ outputSchema }));
const result = await tool.execute("call-non-object-schema", {
result: { data: { value: outputSchema } },
} as never);
@@ -333,7 +333,7 @@ describe("SubmitResultTool", () => {
},
required: ["token"],
};
- const tool = new SubmitResultTool(createSession({ outputSchema }));
+ const tool = new YieldTool(createSession({ outputSchema }));
const dataSchema = getSuccessDataSchema(tool.parameters as unknown as Record);
const tokenSchema = toRecord(toRecord(dataSchema.properties).token);
@@ -357,7 +357,7 @@ describe("SubmitResultTool", () => {
},
required: ["token"],
};
- const tool = new SubmitResultTool(createSession({ outputSchema }));
+ const tool = new YieldTool(createSession({ outputSchema }));
await expect(tool.execute("call-short-1", { result: { data: { token: "ab" } } } as never)).rejects.toThrow(
"Output does not match schema",
@@ -384,7 +384,7 @@ describe("SubmitResultTool", () => {
},
required: ["token"],
};
- const tool = new SubmitResultTool(createSession({ outputSchema }));
+ const tool = new YieldTool(createSession({ outputSchema }));
const firstResult = await tool.execute("call-valid-1", { result: { data: { token: "abcd" } } } as never);
expect(firstResult.content).toEqual([{ type: "text", text: "Result submitted." }]);
@@ -408,7 +408,7 @@ describe("SubmitResultTool", () => {
},
required: ["token"],
};
- const tool = new SubmitResultTool(createSession({ outputSchema }));
+ const tool = new YieldTool(createSession({ outputSchema }));
await expect(tool.execute("call-struct-1", { result: { data: { token: "ab" } } } as never)).rejects.toThrow(
"Output does not match schema",
@@ -422,13 +422,13 @@ describe("SubmitResultTool", () => {
);
});
it("rejects submissions without a result object", async () => {
- const tool = new SubmitResultTool(createSession());
+ const tool = new YieldTool(createSession());
await expect(tool.execute("call-3", {} as never)).rejects.toThrow(
"result must be an object containing either data or error",
);
});
it("sets lenientArgValidation so agent-loop bypasses validation errors", () => {
- const tool = new SubmitResultTool(createSession());
+ const tool = new YieldTool(createSession());
expect(tool.lenientArgValidation).toBe(true);
});
});