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:
can1357
2026-04-26 00:29:10 +02:00
parent 6bd4cf24fd
commit ecd1554eba
30 changed files with 200 additions and 221 deletions
+11 -13
View File
@@ -101,12 +101,12 @@ function formatTaskId(id: string): string {
return `${indices} ${labels}`;
}
const MISSING_SUBMIT_RESULT_WARNING_PREFIX = "SYSTEM WARNING: Subagent exited without calling submit_result tool";
const MISSING_YIELD_WARNING_PREFIX = "SYSTEM WARNING: Subagent exited without calling yield tool";
function extractMissingSubmitResultWarning(output: string): { warning?: string; rest: string } {
function extractMissingYieldWarning(output: string): { warning?: string; rest: string } {
const lines = output.split("\n");
const firstLine = lines[0]?.trim() ?? "";
if (!firstLine.startsWith(MISSING_SUBMIT_RESULT_WARNING_PREFIX)) {
if (!firstLine.startsWith(MISSING_YIELD_WARNING_PREFIX)) {
return { rest: output };
}
const rest = lines
@@ -572,9 +572,9 @@ function renderAgentProgress(
// Render extracted tool data inline (e.g., review findings)
if (progress.extractedToolData) {
// For completed tasks, check for review verdict from submit_result tool
// For completed tasks, check for review verdict from yield tool
if (progress.status === "completed") {
const completeData = progress.extractedToolData.submit_result as Array<{ data: unknown }> | 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) {