feat: renamed subagent handoff flow to use yield instead of submit_result
- Renamed subagent completion flow from `submit_result` to `yield` across SDK tools, prompts, and docs. - Updated executor/task handling to require and parse `yield` calls, replacing legacy submit-result extraction and state flags. - Added `subagent-yield-reminder` and updated system prompts to require `yield` with `result.data` or `result.error`. - Renamed hidden-tool and registration plumbing to `yield`, including discovery helpers and renderer/test surface.
This commit is contained in:
@@ -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<any, any, any>;
|
||||
@@ -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<string, ToolFactory> = {
|
||||
};
|
||||
|
||||
export const HIDDEN_TOOLS: Record<string, ToolFactory> = {
|
||||
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<Tool[]> {
|
||||
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),
|
||||
];
|
||||
|
||||
|
||||
@@ -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<typeof ReportFindingParams, ReportFindingDetails, Theme> = {
|
||||
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<typeof ReportFindingParams, ReportFind
|
||||
},
|
||||
};
|
||||
|
||||
/** SubmitReviewDetails - used for rendering review results from submit_result tool */
|
||||
/** SubmitReviewDetails - used for rendering review results from yield tool */
|
||||
export interface SubmitReviewDetails {
|
||||
overall_correctness: "correct" | "incorrect";
|
||||
explanation: string;
|
||||
|
||||
+7
-7
@@ -12,7 +12,7 @@ import { subprocessToolRegistry } from "../task/subprocess-tool-registry";
|
||||
import type { ToolSession } from ".";
|
||||
import { jtdToJsonSchema, normalizeSchema } from "./jtd-to-json-schema";
|
||||
|
||||
export interface SubmitResultDetails {
|
||||
export interface YieldDetails {
|
||||
data: unknown;
|
||||
status: "success" | "aborted";
|
||||
error?: string;
|
||||
@@ -40,8 +40,8 @@ function formatAjvErrors(errors: ErrorObject[] | null | undefined): string {
|
||||
.join("; ");
|
||||
}
|
||||
|
||||
export class SubmitResultTool implements AgentTool<TSchema, SubmitResultDetails> {
|
||||
readonly name = "submit_result";
|
||||
export class YieldTool implements AgentTool<TSchema, YieldDetails> {
|
||||
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<TSchema, SubmitResultDetails>
|
||||
_toolCallId: string,
|
||||
params: Static<TSchema>,
|
||||
_signal?: AbortSignal,
|
||||
_onUpdate?: AgentToolUpdateCallback<SubmitResultDetails>,
|
||||
_onUpdate?: AgentToolUpdateCallback<YieldDetails>,
|
||||
_context?: AgentToolContext,
|
||||
): Promise<AgentToolResult<SubmitResultDetails>> {
|
||||
): Promise<AgentToolResult<YieldDetails>> {
|
||||
const raw = params as Record<string, unknown>;
|
||||
const rawResult = raw.result;
|
||||
if (!rawResult || typeof rawResult !== "object" || Array.isArray(rawResult)) {
|
||||
@@ -169,7 +169,7 @@ export class SubmitResultTool implements AgentTool<TSchema, SubmitResultDetails>
|
||||
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<TSchema, SubmitResultDetails>
|
||||
}
|
||||
|
||||
// Register subprocess tool handler for extraction + termination.
|
||||
subprocessToolRegistry.register<SubmitResultDetails>("submit_result", {
|
||||
subprocessToolRegistry.register<YieldDetails>("yield", {
|
||||
extractData: event => {
|
||||
const details = event.result?.details;
|
||||
if (!details || typeof details !== "object") return undefined;
|
||||
Reference in New Issue
Block a user