pi-subagents 0.54.0 → 0.56.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +59 -0
- package/agents/reviewer.md +12 -2
- package/docs/agents.md +4 -4
- package/docs/configuration.md +4 -4
- package/docs/extension-api.md +14 -4
- package/docs/models.md +30 -3
- package/docs/observability.md +14 -3
- package/docs/tool-reference.md +21 -6
- package/docs/workflows.md +7 -1
- package/package.json +1 -1
- package/prompts/council.md +9 -0
- package/prompts/parallel-review.md +5 -1
- package/prompts/review-loop.md +9 -5
- package/skills/council-mode/SKILL.md +33 -10
- package/skills/pi-subagents/references/constraints-and-recipes.md +16 -1
- package/skills/pi-subagents/references/execution-controls.md +11 -4
- package/skills/pi-subagents/references/multi-lane-orchestration.md +3 -3
- package/skills/pi-subagents/references/prompting-and-roles.md +8 -8
- package/src/agents/agent-management.ts +30 -9
- package/src/agents/agent-serializer.ts +4 -2
- package/src/agents/agents.ts +168 -41
- package/src/api/external-job-provider.ts +10 -1
- package/src/api/preflight.ts +45 -17
- package/src/api/project-panes.ts +2 -0
- package/src/extension/index.ts +20 -1
- package/src/extension/public-execution.ts +12 -9
- package/src/extension/rpc.ts +77 -3
- package/src/extension/schemas.ts +7 -1
- package/src/extension/tool-description.ts +9 -6
- package/src/inspectors/herdr/focus.ts +55 -0
- package/src/inspectors/herdr/project-panes.ts +228 -44
- package/src/integrations/herdr-status.ts +26 -4
- package/src/runs/background/async-execution.ts +137 -13
- package/src/runs/background/async-job-tracker.ts +33 -14
- package/src/runs/background/async-resume.ts +23 -2
- package/src/runs/background/async-status.ts +10 -0
- package/src/runs/background/control-channel.ts +98 -10
- package/src/runs/background/notify.ts +62 -1
- package/src/runs/background/run-status.ts +15 -1
- package/src/runs/background/subagent-runner.ts +308 -47
- package/src/runs/foreground/async-stop-action.ts +23 -2
- package/src/runs/foreground/execution.ts +115 -12
- package/src/runs/foreground/foreground-history.ts +3 -2
- package/src/runs/foreground/subagent-executor.ts +404 -45
- package/src/runs/foreground/workflow-detach-reconcile.ts +18 -6
- package/src/runs/shared/acceptance.ts +11 -5
- package/src/runs/shared/agent-contract.ts +1 -1
- package/src/runs/shared/child-identity.ts +36 -0
- package/src/runs/shared/completion-guard.ts +52 -2
- package/src/runs/shared/dynamic-fanout.ts +1 -1
- package/src/runs/shared/extension-bindings.ts +78 -0
- package/src/runs/shared/external-cli-runner.ts +2 -0
- package/src/runs/shared/external-job-bridge.ts +53 -37
- package/src/runs/shared/external-job-runner.ts +126 -23
- package/src/runs/shared/fast-mode-extension.ts +10 -0
- package/src/runs/shared/model-exclusions.ts +2 -1
- package/src/runs/shared/model-fallback.ts +12 -7
- package/src/runs/shared/mutation-evidence.ts +145 -0
- package/src/runs/shared/orca-progress-tabs.ts +71 -9
- package/src/runs/shared/parallel-utils.ts +15 -0
- package/src/runs/shared/pi-args.ts +56 -12
- package/src/runs/shared/structured-output.ts +18 -4
- package/src/runs/shared/subagent-prompt-runtime.ts +12 -6
- package/src/runs/shared/tool-availability.ts +1 -3
- package/src/shared/launch-contract.ts +12 -5
- package/src/shared/settings.ts +6 -1
- package/src/shared/thinking-ceiling.ts +52 -0
- package/src/shared/types.ts +107 -2
- package/src/tui/fleet-status.ts +66 -13
- package/src/tui/render.ts +5 -4
- package/src/workflows/scripted-workflow.ts +156 -12
|
@@ -11,7 +11,7 @@ import { createCapacityResilientJsonWriter } from "../../shared/capacity-resilie
|
|
|
11
11
|
import { isStorageCapacityError } from "../../shared/file-system-retry.ts";
|
|
12
12
|
import { updateActiveRunIndex } from "./active-run-index.ts";
|
|
13
13
|
import { createChildTranscriptWriter, type ChildTranscriptWriter } from "../../shared/child-transcript.ts";
|
|
14
|
-
import { closeSteerInbox, consumeInterruptRequest, consumeSteerRequests, deliverInterruptRequest, deliverStopRequest, deliverTimeoutRequest, enqueueStepSteer, steerAcksDir, steerCapabilityPath, stepSteerInboxDir, watchAsyncControlInbox, type SteerAck, type SteerCapability, type SteerRequest } from "./control-channel.ts";
|
|
14
|
+
import { closeSteerInbox, consumeInterruptRequest, consumeSteerRequests, deliverInterruptRequest, deliverStopRequest, deliverTimeoutRequest, enqueueStepSteer, steerAcksDir, steerCapabilityPath, stepSteerInboxDir, watchAsyncControlInbox, type SteerAck, type SteerCapability, type SteerRequest, type StopRequest } from "./control-channel.ts";
|
|
15
15
|
import { appendJsonl as appendRawJsonl, formatOutputArtifactContent, getArtifactPaths, writeArtifact, writeMetadata } from "../../shared/artifacts.ts";
|
|
16
16
|
import { PI_CODING_AGENT_PACKAGE, getPiSpawnCommand, resolveInstalledPiPackageRoot } from "../shared/pi-spawn.ts";
|
|
17
17
|
import { captureSingleOutputSnapshot, extractChildWrittenOutput, finalizeSingleOutput, formatSavedOutputReference, injectOutputPathSystemPrompt, injectSingleOutputInstruction, resolveSingleOutput, type SingleOutputSnapshot } from "../shared/single-output.ts";
|
|
@@ -48,6 +48,7 @@ import {
|
|
|
48
48
|
type SteeringStatus,
|
|
49
49
|
type SteeringTargetState,
|
|
50
50
|
type SteeringTargetStatus,
|
|
51
|
+
type SubagentChildStatusEvent,
|
|
51
52
|
DEFAULT_MAX_OUTPUT,
|
|
52
53
|
type MaxOutputConfig,
|
|
53
54
|
SUBAGENT_LIFECYCLE_ARTIFACT_VERSION,
|
|
@@ -79,13 +80,14 @@ import {
|
|
|
79
80
|
import { applyThinkingSuffix, buildPiArgs, cleanupTempDir, projectLaunchResolvedChildExtensions, resolvePiLaunchToolPlan, type SubagentTaskDelivery } from "../shared/pi-args.ts";
|
|
80
81
|
import { readRuntimeAcknowledgedExtensions } from "../shared/runtime-acknowledged-extensions.ts";
|
|
81
82
|
import { outputEntryFromAsyncResult, resolveOutputReferences } from "../shared/chain-outputs.ts";
|
|
82
|
-
import { createStructuredOutputRuntime, MISSING_STRUCTURED_OUTPUT_CALL_ERROR, readStructuredOutput } from "../shared/structured-output.ts";
|
|
83
|
+
import { createStructuredOutputRuntime, MISSING_STRUCTURED_OUTPUT_CALL_ERROR, readStructuredOutput, readStructuredOutputAcceptanceReport } from "../shared/structured-output.ts";
|
|
83
84
|
import { formatProcessSignalError, isUnexplainedProcessSignal } from "../shared/process-signal.ts";
|
|
84
85
|
import { readChildToolDiagnosticError } from "../shared/tool-availability.ts";
|
|
86
|
+
import { buildTimeoutRecoverySummary, collectTrackedMutationEvidence, snapshotTrackedMutations } from "../shared/mutation-evidence.ts";
|
|
85
87
|
import { collectDynamicResults, DynamicFanoutError, materializeDynamicParallelStep, validateDynamicCollection } from "../shared/dynamic-fanout.ts";
|
|
86
88
|
import { claimRunFanoutBatch, getRunFanoutBudgetSnapshot } from "../shared/run-fanout-budget.ts";
|
|
87
89
|
import { nestedSummaryFromAsyncStatus, projectNestedEvents, resolveNestedAsyncDir, writeNestedEvent } from "../shared/nested-events.ts";
|
|
88
|
-
import { formatModelAttemptNote, isContextOverflow, isRetryableModelFailure, recordRetryableModelFailure } from "../shared/model-fallback.ts";
|
|
90
|
+
import { formatModelAttemptNote, formatSubagentModelVerificationError, isContextOverflow, isRetryableModelFailure, recordRetryableModelFailure } from "../shared/model-fallback.ts";
|
|
89
91
|
import {
|
|
90
92
|
SUBAGENT_STARTUP_RETRY_DELAYS_MS,
|
|
91
93
|
formatSubagentExtensionConflictError,
|
|
@@ -99,7 +101,7 @@ import { createOwnedProcessTreeController, type OwnedProcessTreeController } fro
|
|
|
99
101
|
import { createSteeringStatus, recordSteeringRequest, steeringStatus, terminalSteeringNoticeState, updateSteeringTarget } from "./steering.ts";
|
|
100
102
|
import { attachPostExitStdioGuard, trySignalChild } from "../../shared/post-exit-stdio-guard.ts";
|
|
101
103
|
import { PROMPT_REDACTED, detectSubagentError, extractTextFromContent, extractToolArgsPreview, getFinalOutput, hasEmptyTerminalAssistantResponse, readStatus } from "../../shared/utils.ts";
|
|
102
|
-
import { evaluateCompletionMutationGuard } from "../shared/completion-guard.ts";
|
|
104
|
+
import { evaluateCompletionMutationGuard, validateImplementationToolContract } from "../shared/completion-guard.ts";
|
|
103
105
|
import {
|
|
104
106
|
createMutatingFailureState,
|
|
105
107
|
didMutatingToolFail,
|
|
@@ -123,13 +125,16 @@ import {
|
|
|
123
125
|
type WorktreeSetup,
|
|
124
126
|
} from "../shared/worktree.ts";
|
|
125
127
|
import { resolveEffectiveThinking } from "../../shared/model-info.ts";
|
|
128
|
+
import { assertThinkingWithinCeiling, decodeThinkingCeiling, SUBAGENT_THINKING_CEILING_ENV } from "../../shared/thinking-ceiling.ts";
|
|
126
129
|
import { launchBindingDigest } from "../../shared/launch-contract.ts";
|
|
127
130
|
import { writeInitialProgressFile } from "../../shared/settings.ts";
|
|
128
131
|
import { resolveSubagentIntercomTarget } from "../../intercom/intercom-bridge.ts";
|
|
129
132
|
import { acceptanceFailureMessage, aggregateAcceptanceReport, buildSkippedAcceptanceLedger, evaluateAcceptance, formatAcceptancePrompt, resolveEffectiveAcceptance, stripAcceptanceReport } from "../shared/acceptance.ts";
|
|
130
133
|
import { attachContractProjections, isAgentContractV1 } from "../shared/agent-contract.ts";
|
|
131
134
|
import { waitForImportedAsyncRoot } from "./chain-root-attachment.ts";
|
|
135
|
+
import { normalizeExtensionBindings } from "../shared/extension-bindings.ts";
|
|
132
136
|
import { appendRunnerStepsToStatus, consumeChainAppendRequests, countPendingChainAppendRequests, statusStepDescription } from "./chain-append.ts";
|
|
137
|
+
import { asyncStatusChildIdentity } from "../shared/child-identity.ts";
|
|
133
138
|
import { appendTurnBudgetSystemPrompt, formatTurnBudgetOutput, initialTurnBudgetState, turnBudgetDecision, turnBudgetDeferredNote, turnBudgetDeferredState, turnBudgetExceededMessage, turnBudgetSoftNote, turnBudgetState } from "../shared/turn-budget.ts";
|
|
134
139
|
import { initialToolBudgetState, toolBudgetState } from "../shared/tool-budget.ts";
|
|
135
140
|
import { effectiveToolTimeoutMs, formatToolTimeoutMessage, toolTimeoutCallKey } from "../shared/tool-timeout.ts";
|
|
@@ -226,6 +231,7 @@ interface StepResult {
|
|
|
226
231
|
timedOut?: boolean;
|
|
227
232
|
stopped?: boolean;
|
|
228
233
|
processSignal?: string | null;
|
|
234
|
+
timeoutRecovery?: import("../../shared/types.ts").TimeoutRecoverySummary;
|
|
229
235
|
turnBudget?: TurnBudgetState;
|
|
230
236
|
turnBudgetExceeded?: boolean;
|
|
231
237
|
wrapUpRequested?: boolean;
|
|
@@ -537,6 +543,9 @@ interface RunPiStreamingResult {
|
|
|
537
543
|
processCloseObservedAt?: number;
|
|
538
544
|
processSignal?: string | null;
|
|
539
545
|
processTree: ProcessTreeTerminalV1;
|
|
546
|
+
currentTool?: string;
|
|
547
|
+
currentToolArgs?: string;
|
|
548
|
+
currentPath?: string;
|
|
540
549
|
}
|
|
541
550
|
|
|
542
551
|
function runPiStreaming(
|
|
@@ -560,6 +569,8 @@ function runPiStreaming(
|
|
|
560
569
|
toolTimeoutMs?: number,
|
|
561
570
|
runDeadlineAt?: number,
|
|
562
571
|
orcaProgressTab?: OrcaProgressTab,
|
|
572
|
+
expectedModelForVerification?: string,
|
|
573
|
+
modelVerificationRegistry?: Array<{ provider: string; id: string; fullId: string }>,
|
|
563
574
|
): Promise<RunPiStreamingResult> {
|
|
564
575
|
return new Promise((resolve) => {
|
|
565
576
|
const startedAt = Date.now();
|
|
@@ -605,6 +616,9 @@ function runPiStreaming(
|
|
|
605
616
|
let observedMutationAttempt = false;
|
|
606
617
|
let structuredOutputToolInvoked = false;
|
|
607
618
|
let structuredOutputMessageStartIndex: number | undefined;
|
|
619
|
+
let currentTool: string | undefined;
|
|
620
|
+
let currentToolArgs: string | undefined;
|
|
621
|
+
let currentPath: string | undefined;
|
|
608
622
|
let toolCount = 0;
|
|
609
623
|
const childWatchdogConfig = decodeChildWatchdogConfig(env?.[CHILD_WATCHDOG_CONFIG_ENV]);
|
|
610
624
|
let childWatchdogState: ChildWatchdogStateSnapshot | undefined;
|
|
@@ -696,12 +710,18 @@ function runPiStreaming(
|
|
|
696
710
|
|
|
697
711
|
if (event.type === "tool_execution_end") {
|
|
698
712
|
clearActiveToolTimeout(event);
|
|
713
|
+
currentTool = undefined;
|
|
714
|
+
currentToolArgs = undefined;
|
|
715
|
+
currentPath = undefined;
|
|
699
716
|
return;
|
|
700
717
|
}
|
|
701
718
|
|
|
702
719
|
if (event.type === "tool_execution_start" && event.toolName) {
|
|
703
720
|
toolCount += 1;
|
|
704
721
|
armToolTimeout({ toolCallId: (event as { toolCallId?: unknown }).toolCallId, toolName: event.toolName });
|
|
722
|
+
currentTool = event.toolName;
|
|
723
|
+
currentToolArgs = extractToolArgsPreview(event.args ?? {});
|
|
724
|
+
currentPath = resolveCurrentPath(event.toolName, event.args);
|
|
705
725
|
if (event.toolName === "structured_output") {
|
|
706
726
|
structuredOutputToolInvoked = true;
|
|
707
727
|
structuredOutputMessageStartIndex = messages.length;
|
|
@@ -718,7 +738,14 @@ function runPiStreaming(
|
|
|
718
738
|
if (text) writeOutputText(text);
|
|
719
739
|
|
|
720
740
|
if (event.type !== "message_end" || event.message.role !== "assistant") return;
|
|
721
|
-
|
|
741
|
+
const hasToolCall = assistantStartsToolCall(event.message);
|
|
742
|
+
if (event.message.model) {
|
|
743
|
+
model = event.message.model;
|
|
744
|
+
if (expectedModelForVerification && !hasToolCall) {
|
|
745
|
+
const modelVerificationError = formatSubagentModelVerificationError(expectedModelForVerification, event.message.model, modelVerificationRegistry);
|
|
746
|
+
if (modelVerificationError && !error) error = modelVerificationError;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
722
749
|
if (event.message.errorMessage) assistantError = event.message.errorMessage;
|
|
723
750
|
const eventUsage = event.message.usage;
|
|
724
751
|
if (eventUsage) {
|
|
@@ -1017,6 +1044,9 @@ function runPiStreaming(
|
|
|
1017
1044
|
processCloseObservedAt,
|
|
1018
1045
|
processSignal: signal,
|
|
1019
1046
|
processTree,
|
|
1047
|
+
currentTool,
|
|
1048
|
+
currentToolArgs,
|
|
1049
|
+
currentPath,
|
|
1020
1050
|
}));
|
|
1021
1051
|
});
|
|
1022
1052
|
|
|
@@ -1195,6 +1225,8 @@ interface SingleStepContext {
|
|
|
1195
1225
|
onExternalProcess?: (process: ExternalProcessStatus) => void;
|
|
1196
1226
|
onExternalJob?: (status: ExternalJobStatus) => void;
|
|
1197
1227
|
skipAcceptance?: () => boolean;
|
|
1228
|
+
/** False when sibling work in the same Git worktree could have caused the tracked diff. */
|
|
1229
|
+
trackedMutationEvidenceForCompletionGuard?: boolean;
|
|
1198
1230
|
orcaProgressTab?: OrcaProgressTab;
|
|
1199
1231
|
}
|
|
1200
1232
|
|
|
@@ -1279,8 +1311,48 @@ async function runSingleStepInner(
|
|
|
1279
1311
|
let task = step.task.replace(placeholderRegex, () => ctx.previousOutput);
|
|
1280
1312
|
if (ctx.outputs) task = resolveOutputReferences(task, ctx.outputs);
|
|
1281
1313
|
const taskForCompletionGuard = task;
|
|
1314
|
+
let resolvedTaskToolPlan: ReturnType<typeof resolvePiLaunchToolPlan> | undefined;
|
|
1315
|
+
if (!step.runner) {
|
|
1316
|
+
resolvedTaskToolPlan = resolvePiLaunchToolPlan(omitUndefinedProperties({
|
|
1317
|
+
tools: step.tools,
|
|
1318
|
+
extensions: step.extensions,
|
|
1319
|
+
subagentOnlyExtensions: step.subagentOnlyExtensions,
|
|
1320
|
+
fast: step.fast,
|
|
1321
|
+
model: step.model,
|
|
1322
|
+
modelCandidates: step.modelCandidates,
|
|
1323
|
+
mcpDirectTools: step.mcpDirectTools,
|
|
1324
|
+
cwd: step.cwd ?? ctx.cwd,
|
|
1325
|
+
requireReadTool: Boolean(step.skills?.length),
|
|
1326
|
+
structuredOutput: Boolean(effectiveStructuredOutput),
|
|
1327
|
+
capabilityCeiling: step.capabilityCeiling ?? ctx.capabilityCeiling,
|
|
1328
|
+
inheritedCapabilityCeiling: decodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV]),
|
|
1329
|
+
permissionRules: step.permissionRules,
|
|
1330
|
+
}));
|
|
1331
|
+
const contractTools = resolvedTaskToolPlan.explicitToolAllowlist ? resolvedTaskToolPlan.effectiveToolAllowlist : undefined;
|
|
1332
|
+
const contractError = validateImplementationToolContract({
|
|
1333
|
+
agent: step.agent,
|
|
1334
|
+
task: taskForCompletionGuard,
|
|
1335
|
+
tools: contractTools,
|
|
1336
|
+
mcpDirectTools: resolvedTaskToolPlan.effectiveMcpTools,
|
|
1337
|
+
configuredExtensions: resolvedTaskToolPlan.configuredExtensions,
|
|
1338
|
+
requestedTools: resolvedTaskToolPlan.requestedBuiltinTools,
|
|
1339
|
+
acceptanceRole: step.acceptanceRole,
|
|
1340
|
+
completionGuard: step.completionGuard,
|
|
1341
|
+
});
|
|
1342
|
+
if (contractError) {
|
|
1343
|
+
return omitUndefinedProperties({
|
|
1344
|
+
agent: step.agent,
|
|
1345
|
+
context: step.context,
|
|
1346
|
+
output: contractError,
|
|
1347
|
+
error: contractError,
|
|
1348
|
+
exitCode: 1,
|
|
1349
|
+
capabilityCeiling: resolvedTaskToolPlan.capabilityCeiling,
|
|
1350
|
+
capabilityAudit: resolvedTaskToolPlan.capabilityAudit,
|
|
1351
|
+
});
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1282
1354
|
if (step.effectiveAcceptance) {
|
|
1283
|
-
const acceptancePrompt = formatAcceptancePrompt(step.effectiveAcceptance, { reportOptional: isAgentContractV1(step.agentContract) });
|
|
1355
|
+
const acceptancePrompt = formatAcceptancePrompt(step.effectiveAcceptance, { reportOptional: isAgentContractV1(step.agentContract), structuredOutput: Boolean(step.structuredOutput?.acceptanceReportPath) });
|
|
1284
1356
|
if (acceptancePrompt) task = `${task}\n${acceptancePrompt}`;
|
|
1285
1357
|
}
|
|
1286
1358
|
const sessionEnabled = Boolean(step.sessionFile) || ctx.sessionEnabled;
|
|
@@ -1395,6 +1467,7 @@ async function runSingleStepInner(
|
|
|
1395
1467
|
timeoutMessage: ctx.timeoutMessage,
|
|
1396
1468
|
stopMessage: ctx.stopMessage,
|
|
1397
1469
|
onExternalJob: ctx.onExternalJob,
|
|
1470
|
+
followUp: step.externalJobFollowUp,
|
|
1398
1471
|
}));
|
|
1399
1472
|
try { fs.writeFileSync(ctx.outputFile, external.output, "utf-8"); } catch { /* Observability output is best-effort. */ }
|
|
1400
1473
|
const resolvedOutput = step.outputPath && external.exitCode === 0
|
|
@@ -1450,11 +1523,15 @@ async function runSingleStepInner(
|
|
|
1450
1523
|
const eventsPath = path.join(path.dirname(ctx.outputFile), "events.jsonl");
|
|
1451
1524
|
let finalResult: RunPiStreamingResult | undefined;
|
|
1452
1525
|
let finalOutputSnapshot: SingleOutputSnapshot | undefined;
|
|
1526
|
+
let structuredAcceptanceReport: unknown;
|
|
1527
|
+
let structuredAcceptanceReportError: string | undefined;
|
|
1453
1528
|
let completionGuardTriggeredFinal = false;
|
|
1454
1529
|
let turnBudget = ctx.turnBudget ? initialTurnBudgetState(ctx.turnBudget) : undefined;
|
|
1455
1530
|
let toolBudget = step.toolBudget ? initialToolBudgetState(step.toolBudget) : undefined;
|
|
1456
1531
|
let toolBudgetBlocked = false;
|
|
1457
1532
|
let actualLaunchContractDigest = step.launchContractDigest;
|
|
1533
|
+
const mutationSnapshot = snapshotTrackedMutations(step.cwd ?? ctx.cwd);
|
|
1534
|
+
let finalMutationEvidence = collectTrackedMutationEvidence(mutationSnapshot, step.cwd ?? ctx.cwd);
|
|
1458
1535
|
|
|
1459
1536
|
let modelIndex = 0;
|
|
1460
1537
|
let startupAttemptIndex = 0;
|
|
@@ -1466,6 +1543,13 @@ async function runSingleStepInner(
|
|
|
1466
1543
|
modelAttemptsLoop: while (modelIndex < candidates.length) {
|
|
1467
1544
|
if (ctx.timeoutSignal?.aborted || ctx.stopSignal?.aborted || ctx.skipAcceptance?.()) break;
|
|
1468
1545
|
const candidate = candidates[modelIndex];
|
|
1546
|
+
const expectedModelForVerification = candidate && !(step.skipPrimaryModelVerification && modelIndex === 0) ? candidate : undefined;
|
|
1547
|
+
try {
|
|
1548
|
+
assertThinkingWithinCeiling({ model: candidate, configThinking: step.thinking, ceiling: step.thinkingCeiling ?? decodeThinkingCeiling(process.env[SUBAGENT_THINKING_CEILING_ENV]), agent: step.agent, runId: ctx.id });
|
|
1549
|
+
} catch (error) {
|
|
1550
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1551
|
+
return omitUndefinedProperties({ agent: step.agent, output: message, error: message, exitCode: 1, context: step.context, thinkingCeiling: step.thinkingCeiling });
|
|
1552
|
+
}
|
|
1469
1553
|
ctx.onAttemptStart?.(omitUndefinedProperties({ model: candidate, thinking: resolveEffectiveThinking(candidate, step.thinking) }));
|
|
1470
1554
|
const outputSnapshot = captureSingleOutputSnapshot(step.outputPath);
|
|
1471
1555
|
if (effectiveStructuredOutput) {
|
|
@@ -1476,6 +1560,7 @@ async function runSingleStepInner(
|
|
|
1476
1560
|
}
|
|
1477
1561
|
}
|
|
1478
1562
|
const watchdogConfig = resolveWatchdogConfig(step.cwd ?? ctx.cwd);
|
|
1563
|
+
const extensionBindings = normalizeExtensionBindings(step.extensionBindings)?.value;
|
|
1479
1564
|
const childWatchdog = watchdogConfig.ok
|
|
1480
1565
|
? resolveChildWatchdogConfig({
|
|
1481
1566
|
config: watchdogConfig.config,
|
|
@@ -1499,6 +1584,8 @@ async function runSingleStepInner(
|
|
|
1499
1584
|
tools: step.tools,
|
|
1500
1585
|
extensions: step.extensions,
|
|
1501
1586
|
subagentOnlyExtensions: step.subagentOnlyExtensions,
|
|
1587
|
+
fast: step.fast,
|
|
1588
|
+
modelCandidates: step.modelCandidates,
|
|
1502
1589
|
systemPrompt: appendTurnBudgetSystemPrompt(step.systemPrompt ?? "", ctx.turnBudget),
|
|
1503
1590
|
systemPromptMode: step.systemPromptMode,
|
|
1504
1591
|
mcpDirectTools: step.mcpDirectTools,
|
|
@@ -1529,16 +1616,21 @@ async function runSingleStepInner(
|
|
|
1529
1616
|
: undefined,
|
|
1530
1617
|
childWatchdog,
|
|
1531
1618
|
waitToolEnabled: step.waitToolEnabled,
|
|
1619
|
+
thinkingCeiling: step.thinkingCeiling,
|
|
1620
|
+
extensionBindings,
|
|
1532
1621
|
}));
|
|
1533
1622
|
if (!launchWarningsEmitted && warnings.length > 0) {
|
|
1534
1623
|
for (const warning of warnings) console.warn(`[pi-subagents] ${warning}`);
|
|
1535
1624
|
launchWarningsEmitted = true;
|
|
1536
1625
|
}
|
|
1537
1626
|
if (step.definitionDigest) {
|
|
1538
|
-
const toolPlan = resolvePiLaunchToolPlan(omitUndefinedProperties({
|
|
1627
|
+
const toolPlan = resolvedTaskToolPlan ?? resolvePiLaunchToolPlan(omitUndefinedProperties({
|
|
1539
1628
|
tools: step.tools,
|
|
1540
1629
|
extensions: step.extensions,
|
|
1541
1630
|
subagentOnlyExtensions: step.subagentOnlyExtensions,
|
|
1631
|
+
fast: step.fast,
|
|
1632
|
+
model: step.model,
|
|
1633
|
+
modelCandidates: step.modelCandidates,
|
|
1542
1634
|
mcpDirectTools: step.mcpDirectTools,
|
|
1543
1635
|
cwd: step.cwd ?? ctx.cwd,
|
|
1544
1636
|
requireReadTool: Boolean(step.skills?.length),
|
|
@@ -1553,7 +1645,9 @@ async function runSingleStepInner(
|
|
|
1553
1645
|
task: step.launchBindingTask ?? task,
|
|
1554
1646
|
...(candidate ? { model: candidate } : {}),
|
|
1555
1647
|
modelCandidates: candidates as string[],
|
|
1648
|
+
...(step.fast !== undefined ? { fast: step.fast } : {}),
|
|
1556
1649
|
...(resolveEffectiveThinking(candidate, step.thinking) ? { thinking: resolveEffectiveThinking(candidate, step.thinking) } : {}),
|
|
1650
|
+
...(step.thinkingCeiling ? { thinkingCeiling: step.thinkingCeiling } : {}),
|
|
1557
1651
|
systemPrompt: appendTurnBudgetSystemPrompt(step.systemPrompt ?? "", ctx.turnBudget),
|
|
1558
1652
|
systemPromptMode: step.systemPromptMode,
|
|
1559
1653
|
inheritProjectContext: step.inheritProjectContext,
|
|
@@ -1565,6 +1659,7 @@ async function runSingleStepInner(
|
|
|
1565
1659
|
...(step.outputPath ? { outputPath: step.outputPath } : {}),
|
|
1566
1660
|
...(step.outputMode ? { outputMode: step.outputMode } : {}),
|
|
1567
1661
|
...(step.structuredOutputSchema ? { structuredOutputSchema: step.structuredOutputSchema } : {}),
|
|
1662
|
+
...(extensionBindings ? { extensionBindings } : {}),
|
|
1568
1663
|
}));
|
|
1569
1664
|
}
|
|
1570
1665
|
capabilityAudit = attemptCapabilityAudit;
|
|
@@ -1590,6 +1685,8 @@ async function runSingleStepInner(
|
|
|
1590
1685
|
ctx.toolTimeoutMs,
|
|
1591
1686
|
ctx.deadlineAt,
|
|
1592
1687
|
ctx.orcaProgressTab,
|
|
1688
|
+
expectedModelForVerification,
|
|
1689
|
+
step.modelVerificationRegistry,
|
|
1593
1690
|
);
|
|
1594
1691
|
if (run.processCloseObservedAt !== undefined) {
|
|
1595
1692
|
writerProcesses.push({
|
|
@@ -1642,6 +1739,9 @@ async function runSingleStepInner(
|
|
|
1642
1739
|
if (structured.error) structuredError = structured.error;
|
|
1643
1740
|
else {
|
|
1644
1741
|
structuredOutput = structured.value;
|
|
1742
|
+
const acceptanceReport = readStructuredOutputAcceptanceReport(effectiveStructuredOutput);
|
|
1743
|
+
structuredAcceptanceReport = acceptanceReport.value;
|
|
1744
|
+
structuredAcceptanceReportError = acceptanceReport.error;
|
|
1645
1745
|
validatedStructuredOutput = true;
|
|
1646
1746
|
}
|
|
1647
1747
|
}
|
|
@@ -1662,21 +1762,31 @@ async function runSingleStepInner(
|
|
|
1662
1762
|
? "Subagent produced no output (possible model cold-start or empty response)."
|
|
1663
1763
|
: undefined;
|
|
1664
1764
|
const completionGuardEnabled = isAgentContractV1(step.agentContract) ? step.completionGuard === true : step.completionGuard !== false;
|
|
1665
|
-
const
|
|
1765
|
+
const completionToolPlan = resolvedTaskToolPlan;
|
|
1766
|
+
const mutationEvidence = collectTrackedMutationEvidence(mutationSnapshot, step.cwd ?? ctx.cwd);
|
|
1767
|
+
finalMutationEvidence = mutationEvidence;
|
|
1768
|
+
const completionMutationEvidence = ctx.trackedMutationEvidenceForCompletionGuard === false ? undefined : mutationEvidence;
|
|
1769
|
+
const completionGuard = run.exitCode === 0 && !run.error && !structuredError && !hiddenError?.hasError && !emptyOutputError && completionGuardEnabled
|
|
1666
1770
|
? evaluateCompletionMutationGuard(omitUndefinedProperties({
|
|
1667
1771
|
agent: step.agent,
|
|
1668
1772
|
task: taskForCompletionGuard,
|
|
1669
1773
|
messages: run.messages,
|
|
1670
|
-
tools: step.tools,
|
|
1671
|
-
mcpDirectTools: step.mcpDirectTools,
|
|
1774
|
+
tools: completionToolPlan ? (completionToolPlan.explicitToolAllowlist ? completionToolPlan.effectiveToolAllowlist : undefined) : step.tools,
|
|
1775
|
+
mcpDirectTools: completionToolPlan?.effectiveMcpTools ?? step.mcpDirectTools,
|
|
1776
|
+
toolAvailabilityError,
|
|
1777
|
+
mutationEvidence: completionMutationEvidence,
|
|
1672
1778
|
}))
|
|
1673
1779
|
: undefined;
|
|
1674
|
-
const
|
|
1780
|
+
const mutationAttemptObserved = run.observedMutationAttempt === true || completionMutationEvidence?.attemptedMutation === true;
|
|
1781
|
+
const completionGuardTriggered = completionGuard?.triggered === true && !mutationAttemptObserved;
|
|
1782
|
+
const completionGuardBlocked = completionGuard?.blocked === true;
|
|
1675
1783
|
const fileMutationEffect = completionGuard
|
|
1676
1784
|
? {
|
|
1677
|
-
status: completionGuard.expectedMutation ? completionGuardTriggered ? "missing" as const : "observed" as const : "not-applicable" as const,
|
|
1785
|
+
status: completionGuardBlocked ? "blocked" as const : completionGuard.expectedMutation ? completionGuardTriggered ? "missing" as const : "observed" as const : "not-applicable" as const,
|
|
1678
1786
|
expected: completionGuard.expectedMutation,
|
|
1679
|
-
attempted: completionGuard.attemptedMutation ||
|
|
1787
|
+
attempted: completionGuardBlocked ? false : completionGuard.attemptedMutation || mutationAttemptObserved,
|
|
1788
|
+
...(completionMutationEvidence ? { evidence: completionMutationEvidence } : {}),
|
|
1789
|
+
...(completionGuardBlocked && completionGuard.message ? { message: completionGuard.message } : {}),
|
|
1680
1790
|
...(completionGuardTriggered ? { message: "Subagent completed without making edits for an implementation task." } : {}),
|
|
1681
1791
|
}
|
|
1682
1792
|
: undefined;
|
|
@@ -1825,6 +1935,21 @@ async function runSingleStepInner(
|
|
|
1825
1935
|
: resolvedOutput.savedPath
|
|
1826
1936
|
? "unknown"
|
|
1827
1937
|
: finalResult?.outputState ?? "unknown";
|
|
1938
|
+
const timeoutRecovery = finalResult?.timedOut === true || ctx.timeoutSignal?.aborted === true
|
|
1939
|
+
? buildTimeoutRecoverySummary({
|
|
1940
|
+
termination: "timed-out",
|
|
1941
|
+
evidence: finalMutationEvidence,
|
|
1942
|
+
currentTool: finalResult?.currentTool,
|
|
1943
|
+
currentToolArgs: finalResult?.currentToolArgs,
|
|
1944
|
+
currentPath: finalResult?.currentPath,
|
|
1945
|
+
sessionFile: step.sessionFile,
|
|
1946
|
+
transcriptPath: transcriptWriter ? artifactPaths?.transcriptPath : undefined,
|
|
1947
|
+
artifactPaths,
|
|
1948
|
+
})
|
|
1949
|
+
: undefined;
|
|
1950
|
+
if (timeoutRecovery) outputForSummary = outputForSummary.trim()
|
|
1951
|
+
? `${outputForSummary}\n\n${timeoutRecovery.message}`
|
|
1952
|
+
: timeoutRecovery.message;
|
|
1828
1953
|
const finalizedOutput = finalizeSingleOutput(omitUndefinedProperties({
|
|
1829
1954
|
fullOutput: outputForSummary,
|
|
1830
1955
|
outputPath: step.outputPath,
|
|
@@ -1839,6 +1964,8 @@ async function runSingleStepInner(
|
|
|
1839
1964
|
? await evaluateAcceptance(omitUndefinedProperties({
|
|
1840
1965
|
acceptance: step.effectiveAcceptance,
|
|
1841
1966
|
output: outputForAcceptance,
|
|
1967
|
+
report: structuredAcceptanceReport as import("../../shared/types.ts").AcceptanceReport | undefined,
|
|
1968
|
+
reportError: structuredAcceptanceReportError,
|
|
1842
1969
|
fileOutput: childWrittenOutput !== undefined && step.outputPath
|
|
1843
1970
|
? { content: childWrittenOutput, path: step.outputPath, authoritative: step.outputMode === "file-only" }
|
|
1844
1971
|
: undefined,
|
|
@@ -1934,6 +2061,7 @@ async function runSingleStepInner(
|
|
|
1934
2061
|
timedOut: timedOutAfterAcceptance ? true : finalResult?.timedOut,
|
|
1935
2062
|
stopped: stoppedAfterAcceptance ? true : finalResult?.stopped,
|
|
1936
2063
|
processSignal: finalResult?.processSignal,
|
|
2064
|
+
timeoutRecovery,
|
|
1937
2065
|
turnBudget,
|
|
1938
2066
|
turnBudgetExceeded: turnBudgetExceeded || undefined,
|
|
1939
2067
|
wrapUpRequested: finalResult?.wrapUpRequested || turnBudget?.outcome === "wrap-up-requested" || turnBudget?.outcome === "termination-deferred" || turnBudgetExceeded || undefined,
|
|
@@ -1959,21 +2087,8 @@ async function runSingleStep(
|
|
|
1959
2087
|
step: SubagentStep,
|
|
1960
2088
|
ctx: SingleStepContext,
|
|
1961
2089
|
): Promise<StepResult & { completionGuardTriggered?: boolean }> {
|
|
1962
|
-
if (step.importAsyncRoot)
|
|
1963
|
-
|
|
1964
|
-
cwd: step.cwd ?? ctx.cwd,
|
|
1965
|
-
runId: ctx.id,
|
|
1966
|
-
agent: step.agent,
|
|
1967
|
-
index: ctx.flatIndex,
|
|
1968
|
-
});
|
|
1969
|
-
try {
|
|
1970
|
-
const result = await runSingleStepInner(step, { ...ctx, orcaProgressTab });
|
|
1971
|
-
orcaProgressTab?.finish(result.stopped ? "stopped" : result.exitCode === 0 && !result.error ? "completed" : "failed", result.sessionFile);
|
|
1972
|
-
return result;
|
|
1973
|
-
} catch (error) {
|
|
1974
|
-
orcaProgressTab?.finish("failed");
|
|
1975
|
-
throw error;
|
|
1976
|
-
}
|
|
2090
|
+
if (!step.importAsyncRoot) ctx.orcaProgressTab?.section({ agent: step.agent, index: ctx.flatIndex, count: ctx.flatStepCount });
|
|
2091
|
+
return runSingleStepInner(step, ctx);
|
|
1977
2092
|
}
|
|
1978
2093
|
|
|
1979
2094
|
type RunnerStatusStep = NonNullable<AsyncStatus["steps"]>[number] & {
|
|
@@ -2242,6 +2357,7 @@ async function runSubagent(
|
|
|
2242
2357
|
const activeChildInterrupts = new Map<number, () => void>();
|
|
2243
2358
|
const activeChildTimeouts = new Map<number, () => void>();
|
|
2244
2359
|
const activeChildStops = new Map<number, () => void>();
|
|
2360
|
+
const childStopRequests = new Map<number, { childId: string; requestedAt: number }>();
|
|
2245
2361
|
const activeChildTurnBudgetAborts = new Map<number, (message: string, state?: TurnBudgetState) => void>();
|
|
2246
2362
|
const pendingStepSteers: SteerRequest[] = [];
|
|
2247
2363
|
const steeringCapabilities = new Map<number, SteerCapability>();
|
|
@@ -2285,6 +2401,7 @@ async function runSubagent(
|
|
|
2285
2401
|
...(task.launchContractDigest ? { launchContractDigest: task.launchContractDigest } : {}),
|
|
2286
2402
|
...(task.launchResolvedExtensions ? { launchResolvedExtensions: task.launchResolvedExtensions } : {}),
|
|
2287
2403
|
...(task.capabilityCeiling ? { capabilityCeiling: task.capabilityCeiling } : {}),
|
|
2404
|
+
...(task.thinkingCeiling ? { thinkingCeiling: task.thinkingCeiling } : {}),
|
|
2288
2405
|
status: "pending",
|
|
2289
2406
|
...(task.toolBudget ? { toolBudget: initialToolBudgetState(task.toolBudget) } : {}),
|
|
2290
2407
|
...(task.sessionFile ? { sessionFile: task.sessionFile } : {}),
|
|
@@ -2310,6 +2427,7 @@ async function runSubagent(
|
|
|
2310
2427
|
structured: Boolean(step.collect.outputSchema),
|
|
2311
2428
|
...(step.agentContract ? { agentContract: step.agentContract } : {}),
|
|
2312
2429
|
...(step.capabilityCeiling ? { capabilityCeiling: step.capabilityCeiling } : {}),
|
|
2430
|
+
...(step.thinkingCeiling ? { thinkingCeiling: step.thinkingCeiling } : {}),
|
|
2313
2431
|
status: "pending",
|
|
2314
2432
|
...(step.parallel.toolBudget ? { toolBudget: initialToolBudgetState(step.parallel.toolBudget) } : {}),
|
|
2315
2433
|
recentTools: [],
|
|
@@ -2332,6 +2450,7 @@ async function runSubagent(
|
|
|
2332
2450
|
...(step.launchContractDigest ? { launchContractDigest: step.launchContractDigest } : {}),
|
|
2333
2451
|
...(step.launchResolvedExtensions ? { launchResolvedExtensions: step.launchResolvedExtensions } : {}),
|
|
2334
2452
|
...(step.capabilityCeiling ? { capabilityCeiling: step.capabilityCeiling } : {}),
|
|
2453
|
+
...(step.thinkingCeiling ? { thinkingCeiling: step.thinkingCeiling } : {}),
|
|
2335
2454
|
status: "pending",
|
|
2336
2455
|
...(step.toolBudget ? { toolBudget: initialToolBudgetState(step.toolBudget) } : {}),
|
|
2337
2456
|
...(step.sessionFile ? { sessionFile: step.sessionFile } : {}),
|
|
@@ -2354,6 +2473,19 @@ async function runSubagent(
|
|
|
2354
2473
|
step.processTerminal = { version: 1, state: "pending", runId: id, runnerProcessInstanceId: config.runnerProcessInstanceId };
|
|
2355
2474
|
}
|
|
2356
2475
|
}
|
|
2476
|
+
const initialAgentLabel = initialStatusSteps.length === 1
|
|
2477
|
+
? initialStatusSteps[0]!.agent
|
|
2478
|
+
: (config.resultMode ?? (flatSteps.length > 1 ? "chain" : "single")) === "parallel"
|
|
2479
|
+
? `parallel:${initialStatusSteps.map((step) => step.agent).join("+")}`
|
|
2480
|
+
: `chain:${initialStatusSteps.map((step) => step.agent).join("->")}`;
|
|
2481
|
+
const orcaProgressTab = flatSteps.every((step) => step.importAsyncRoot) ? undefined : createOrcaProgressTab({
|
|
2482
|
+
cwd,
|
|
2483
|
+
runId: id,
|
|
2484
|
+
agent: initialAgentLabel,
|
|
2485
|
+
index: 0,
|
|
2486
|
+
stepCount: Math.max(initialStatusSteps.length, 1),
|
|
2487
|
+
});
|
|
2488
|
+
|
|
2357
2489
|
const statusPayload: RunnerStatusPayload = omitUndefinedProperties({
|
|
2358
2490
|
lifecycleArtifactVersion: SUBAGENT_LIFECYCLE_ARTIFACT_VERSION,
|
|
2359
2491
|
runId: id,
|
|
@@ -2572,6 +2704,83 @@ async function runSubagent(
|
|
|
2572
2704
|
statusPayload.lastUpdate = Date.now();
|
|
2573
2705
|
writeStatusPayload();
|
|
2574
2706
|
};
|
|
2707
|
+
const childStopTargetId = (index: number): string => asyncStatusChildIdentity(requiredStatusStep(statusPayload, index), index);
|
|
2708
|
+
const appendChildStatusEvent = (index: number, childId: string, status: "stopping" | "stopped", now = Date.now()): void => {
|
|
2709
|
+
const step = requiredStatusStep(statusPayload, index);
|
|
2710
|
+
appendJsonl(eventsPath, JSON.stringify({
|
|
2711
|
+
type: "subagent.child-status",
|
|
2712
|
+
version: 1,
|
|
2713
|
+
ts: now,
|
|
2714
|
+
runId: id,
|
|
2715
|
+
childId,
|
|
2716
|
+
status,
|
|
2717
|
+
reason: "user",
|
|
2718
|
+
source: "async",
|
|
2719
|
+
stepIndex: index,
|
|
2720
|
+
agent: step.agent,
|
|
2721
|
+
...(step.runId ? { childRunId: step.runId } : {}),
|
|
2722
|
+
...(step.workflowKey ? { workflowKey: step.workflowKey } : {}),
|
|
2723
|
+
...(step.phase ? { phase: step.phase } : {}),
|
|
2724
|
+
...(step.label ? { label: step.label } : {}),
|
|
2725
|
+
} satisfies SubagentChildStatusEvent));
|
|
2726
|
+
};
|
|
2727
|
+
const appendTerminalChildStatusEvent = (index: number, now = Date.now()): void => {
|
|
2728
|
+
const request = childStopRequests.get(index);
|
|
2729
|
+
if (request) appendChildStatusEvent(index, request.childId, "stopped", now);
|
|
2730
|
+
};
|
|
2731
|
+
const markChildStopRequested = (index: number, childId: string, now = Date.now()): boolean => {
|
|
2732
|
+
const step = statusPayload.steps[index];
|
|
2733
|
+
if (!step || (step.status !== "pending" && step.status !== "running")) return false;
|
|
2734
|
+
childStopRequests.set(index, { childId, requestedAt: now });
|
|
2735
|
+
step.stopRequested = true;
|
|
2736
|
+
step.stopRequestedAt = now;
|
|
2737
|
+
delete step.activityState;
|
|
2738
|
+
statusPayload.lastUpdate = now;
|
|
2739
|
+
writeStatusPayload();
|
|
2740
|
+
appendJsonl(eventsPath, JSON.stringify({ type: "subagent.step.stop_requested", ts: now, runId: id, stepIndex: index, childId, agent: step.agent }));
|
|
2741
|
+
appendChildStatusEvent(index, childId, "stopping", now);
|
|
2742
|
+
return true;
|
|
2743
|
+
};
|
|
2744
|
+
const markChildStopped = (index: number, now = Date.now()): void => {
|
|
2745
|
+
const step = requiredStatusStep(statusPayload, index);
|
|
2746
|
+
if (step.status === "stopped") return;
|
|
2747
|
+
step.status = "stopped";
|
|
2748
|
+
step.error = stopMessage;
|
|
2749
|
+
step.exitCode = 1;
|
|
2750
|
+
step.stopped = true;
|
|
2751
|
+
step.stopRequested = true;
|
|
2752
|
+
step.stopRequestedAt = childStopRequests.get(index)?.requestedAt ?? step.stopRequestedAt ?? now;
|
|
2753
|
+
delete step.activityState;
|
|
2754
|
+
step.endedAt = now;
|
|
2755
|
+
step.durationMs = step.startedAt ? now - step.startedAt : 0;
|
|
2756
|
+
step.lastActivityAt = now;
|
|
2757
|
+
statusPayload.lastUpdate = now;
|
|
2758
|
+
writeStatusPayload();
|
|
2759
|
+
const childId = childStopRequests.get(index)?.childId ?? childStopTargetId(index);
|
|
2760
|
+
appendJsonl(eventsPath, JSON.stringify({ type: "subagent.step.stopped", ts: now, runId: id, stepIndex: index, childId, agent: step.agent, exitCode: 1, durationMs: step.durationMs }));
|
|
2761
|
+
appendChildStatusEvent(index, childId, "stopped", now);
|
|
2762
|
+
};
|
|
2763
|
+
const childStopResult = (index: number, agent: string, context?: "fresh" | "fork"): SingleStepResult => {
|
|
2764
|
+
markChildStopped(index);
|
|
2765
|
+
return stoppedStepResult(agent, context);
|
|
2766
|
+
};
|
|
2767
|
+
const stopChildStep = (request: StopRequest): void => {
|
|
2768
|
+
if (request.targetIndex === undefined) {
|
|
2769
|
+
stopRunner();
|
|
2770
|
+
return;
|
|
2771
|
+
}
|
|
2772
|
+
const childId = request.childId ?? childStopTargetId(request.targetIndex);
|
|
2773
|
+
const now = Date.now();
|
|
2774
|
+
if (!markChildStopRequested(request.targetIndex, childId, now)) {
|
|
2775
|
+
appendJsonl(eventsPath, JSON.stringify({ type: "subagent.step.stop_failed", ts: now, runId: id, stepIndex: request.targetIndex, childId, message: "Child is not pending or running." }));
|
|
2776
|
+
return;
|
|
2777
|
+
}
|
|
2778
|
+
const stop = activeChildStops.get(request.targetIndex);
|
|
2779
|
+
if (stop) stop();
|
|
2780
|
+
else if (requiredStatusStep(statusPayload, request.targetIndex).status === "pending") {
|
|
2781
|
+
appendJsonl(eventsPath, JSON.stringify({ type: "subagent.step.stop_queued", ts: now, runId: id, stepIndex: request.targetIndex, childId }));
|
|
2782
|
+
}
|
|
2783
|
+
};
|
|
2575
2784
|
const registerStepInterrupt = (flatIndex: number, interrupt: (() => void) | undefined): void => {
|
|
2576
2785
|
if (!interrupt) {
|
|
2577
2786
|
activeChildInterrupts.delete(flatIndex);
|
|
@@ -2594,7 +2803,7 @@ async function runSubagent(
|
|
|
2594
2803
|
return;
|
|
2595
2804
|
}
|
|
2596
2805
|
activeChildStops.set(flatIndex, stop);
|
|
2597
|
-
if (stopped) stop();
|
|
2806
|
+
if (stopped || childStopRequests.has(flatIndex)) stop();
|
|
2598
2807
|
};
|
|
2599
2808
|
const registerStepTurnBudgetAbort = (flatIndex: number, abort: ((message: string, state?: TurnBudgetState) => void) | undefined): void => {
|
|
2600
2809
|
if (!abort) {
|
|
@@ -3499,7 +3708,7 @@ async function runSubagent(
|
|
|
3499
3708
|
const disposeControlInbox = watchAsyncControlInbox(asyncDir, {
|
|
3500
3709
|
onInterrupt: interruptRunner,
|
|
3501
3710
|
onTimeout: timeoutRunner,
|
|
3502
|
-
onStop:
|
|
3711
|
+
onStop: stopChildStep,
|
|
3503
3712
|
onSteer: (request) => {
|
|
3504
3713
|
const targetStep = request.targetIndex !== undefined ? statusPayload.steps[request.targetIndex] : undefined;
|
|
3505
3714
|
if (targetStep?.status === "pending") {
|
|
@@ -3575,6 +3784,19 @@ async function runSubagent(
|
|
|
3575
3784
|
if (materialized.parallel.length > 1 && step.parallel.outputPath && !step.parallel.namespaceOutputPath) {
|
|
3576
3785
|
throw new DynamicFanoutError(`Dynamic chain step ${stepIndex + 1} materialized ${materialized.parallel.length} items that resolve output to the same path: ${step.parallel.outputPath}. Remove the explicit output path or use an inherited relative agent output so each item can be isolated.`);
|
|
3577
3786
|
}
|
|
3787
|
+
for (const [itemIndex] of materialized.parallel.entries()) {
|
|
3788
|
+
const thinkingOverride = step.thinkingOverrides?.[itemIndex];
|
|
3789
|
+
const model = thinkingOverride ? applyThinkingSuffix(step.parallel.model, thinkingOverride, true) : step.parallel.model;
|
|
3790
|
+
const configThinking = thinkingOverride ? thinkingOverride : step.parallel.thinking;
|
|
3791
|
+
const candidates = step.parallel.modelCandidates !== undefined
|
|
3792
|
+
? step.parallel.modelCandidates.length > 0
|
|
3793
|
+
? step.parallel.modelCandidates.map((candidate) => thinkingOverride ? applyThinkingSuffix(candidate, thinkingOverride, true) ?? candidate : candidate)
|
|
3794
|
+
: [undefined]
|
|
3795
|
+
: model ? [model] : [undefined];
|
|
3796
|
+
for (const candidate of candidates) {
|
|
3797
|
+
assertThinkingWithinCeiling({ model: candidate, configThinking, ceiling: step.parallel.thinkingCeiling ?? decodeThinkingCeiling(process.env[SUBAGENT_THINKING_CEILING_ENV]), agent: step.parallel.agent, runId: id });
|
|
3798
|
+
}
|
|
3799
|
+
}
|
|
3578
3800
|
if (materialized.collectedOnEmpty) await validateDynamicCollection(step.collect.outputSchema, materialized.collectedOnEmpty);
|
|
3579
3801
|
if (!config.runFanoutBudget) throw new Error("Async runner is missing its run fan-out budget identity.");
|
|
3580
3802
|
const runFanoutBudget = claimRunFanoutBatch(config.runFanoutBudget, materialized.parallel.map((_, itemIndex) => `chain[${stepIndex}].expand[${itemIndex}]`));
|
|
@@ -3731,6 +3953,7 @@ async function runSubagent(
|
|
|
3731
3953
|
...(task.skills ? { skills: task.skills } : {}),
|
|
3732
3954
|
...(task.model ? { model: task.model } : {}),
|
|
3733
3955
|
...(task.thinking ? { thinking: task.thinking } : {}),
|
|
3956
|
+
...(task.thinkingCeiling ? { thinkingCeiling: task.thinkingCeiling } : {}),
|
|
3734
3957
|
...(task.modelCandidates && task.modelCandidates.length > 0 ? { attemptedModels: task.modelCandidates } : task.model ? { attemptedModels: [task.model] } : {}),
|
|
3735
3958
|
recentTools: [],
|
|
3736
3959
|
recentOutput: [],
|
|
@@ -3802,6 +4025,7 @@ async function runSubagent(
|
|
|
3802
4025
|
}
|
|
3803
4026
|
if (timedOut) return timedOutStepResult(task.agent, task.context);
|
|
3804
4027
|
if (stopped) return stoppedStepResult(task.agent, task.context);
|
|
4028
|
+
if (childStopRequests.has(fi)) return childStopResult(fi, task.agent, task.context);
|
|
3805
4029
|
if (interrupted) return pausedStepResult(task.agent, task.context);
|
|
3806
4030
|
if (aborted && failFast) {
|
|
3807
4031
|
const skippedAt = Date.now();
|
|
@@ -3852,6 +4076,7 @@ async function runSubagent(
|
|
|
3852
4076
|
registerTurnBudgetAbort: (abort) => registerStepTurnBudgetAbort(fi, abort),
|
|
3853
4077
|
timeoutSignal: timeoutAbortController.signal,
|
|
3854
4078
|
stopSignal: stopAbortController.signal,
|
|
4079
|
+
trackedMutationEvidenceForCompletionGuard: false,
|
|
3855
4080
|
timeoutMessage,
|
|
3856
4081
|
stopMessage,
|
|
3857
4082
|
turnBudget: config.turnBudget,
|
|
@@ -3861,7 +4086,8 @@ async function runSubagent(
|
|
|
3861
4086
|
onWriterProcess,
|
|
3862
4087
|
onExternalProcess: (process) => updateExternalProcess(fi, process),
|
|
3863
4088
|
onExternalJob: (externalJob) => updateExternalJob(fi, externalJob),
|
|
3864
|
-
skipAcceptance: () => timedOut || stopped,
|
|
4089
|
+
skipAcceptance: () => timedOut || stopped || childStopRequests.has(fi),
|
|
4090
|
+
orcaProgressTab,
|
|
3865
4091
|
}), config.deadlineAt);
|
|
3866
4092
|
const taskEndTime = Date.now();
|
|
3867
4093
|
const childInterrupted = singleResult.interrupted === true;
|
|
@@ -3910,6 +4136,7 @@ async function runSubagent(
|
|
|
3910
4136
|
setOptionalProperty(requiredStatusStep(statusPayload, fi), "structuredOutputPath", singleResult.structuredOutputPath);
|
|
3911
4137
|
setOptionalProperty(requiredStatusStep(statusPayload, fi), "structuredOutputSchemaPath", singleResult.structuredOutputSchemaPath);
|
|
3912
4138
|
setOptionalProperty(requiredStatusStep(statusPayload, fi), "acceptance", singleResult.acceptance);
|
|
4139
|
+
setOptionalProperty(requiredStatusStep(statusPayload, fi), "timeoutRecovery", singleResult.timeoutRecovery);
|
|
3913
4140
|
setOptionalProperty(requiredStatusStep(statusPayload, fi), "watchdog", singleResult.watchdog);
|
|
3914
4141
|
setOptionalProperty(requiredStatusStep(statusPayload, fi), "capabilityCeiling", singleResult.capabilityCeiling);
|
|
3915
4142
|
setOptionalProperty(requiredStatusStep(statusPayload, fi), "capabilityAudit", singleResult.capabilityAudit);
|
|
@@ -3918,13 +4145,14 @@ async function runSubagent(
|
|
|
3918
4145
|
statusPayload.lastUpdate = taskEndTime;
|
|
3919
4146
|
writeStatusPayload();
|
|
3920
4147
|
appendCapabilityCeilingAppliedEvent(eventsPath, id, fi, task.agent, singleResult);
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
4148
|
+
appendJsonl(eventsPath, JSON.stringify({
|
|
4149
|
+
type: stopped || childStopped ? "subagent.step.stopped" : timedOut ? "subagent.step.failed" : childInterrupted ? "subagent.step.paused" : singleResult.exitCode === 0 ? "subagent.step.completed" : "subagent.step.failed",
|
|
4150
|
+
ts: taskEndTime, runId: id, stepIndex: fi, agent: task.agent,
|
|
4151
|
+
exitCode: stopped || childStopped ? 1 : timedOut ? 1 : childInterrupted ? 0 : singleResult.exitCode, durationMs: taskEndTime - taskStartTime,
|
|
4152
|
+
}));
|
|
4153
|
+
if (stopped || childStopped) appendTerminalChildStatusEvent(fi, taskEndTime);
|
|
4154
|
+
if (singleResult.exitCode !== 0 && failFast && !childStopped) aborted = true;
|
|
4155
|
+
return stopped || childStopped ? { ...singleResult, output: stopMessage, error: stopMessage, exitCode: 1, interrupted: false, timedOut: false, stopped: true, skipped: false } : timedOut ? { ...singleResult, output: singleResult.output || (timeoutMessage ?? "Subagent timed out."), error: singleResult.error ?? timeoutMessage ?? "Subagent timed out.", exitCode: 1, interrupted: false, timedOut: true, skipped: false } : { ...singleResult, skipped: false };
|
|
3928
4156
|
}, globalSemaphore);
|
|
3929
4157
|
|
|
3930
4158
|
flatIndex += dynamicSteps.length;
|
|
@@ -3964,6 +4192,7 @@ async function runSubagent(
|
|
|
3964
4192
|
effects: pr.effects,
|
|
3965
4193
|
execution: pr.execution,
|
|
3966
4194
|
review: pr.review,
|
|
4195
|
+
timeoutRecovery: pr.timeoutRecovery,
|
|
3967
4196
|
structuredOutput: pr.structuredOutput,
|
|
3968
4197
|
structuredOutputPath: pr.structuredOutputPath,
|
|
3969
4198
|
structuredOutputSchemaPath: pr.structuredOutputSchemaPath,
|
|
@@ -4176,6 +4405,7 @@ async function runSubagent(
|
|
|
4176
4405
|
}
|
|
4177
4406
|
if (timedOut) return timedOutStepResult(task.agent, task.context);
|
|
4178
4407
|
if (stopped) return stoppedStepResult(task.agent, task.context);
|
|
4408
|
+
if (childStopRequests.has(fi)) return childStopResult(fi, task.agent, task.context);
|
|
4179
4409
|
if (interrupted) return pausedStepResult(task.agent, task.context);
|
|
4180
4410
|
if (aborted && failFast) {
|
|
4181
4411
|
const skippedAt = Date.now();
|
|
@@ -4242,6 +4472,7 @@ async function runSubagent(
|
|
|
4242
4472
|
registerTurnBudgetAbort: (abort) => registerStepTurnBudgetAbort(fi, abort),
|
|
4243
4473
|
timeoutSignal: timeoutAbortController.signal,
|
|
4244
4474
|
stopSignal: stopAbortController.signal,
|
|
4475
|
+
trackedMutationEvidenceForCompletionGuard: Boolean(worktreeSetup),
|
|
4245
4476
|
timeoutMessage,
|
|
4246
4477
|
stopMessage,
|
|
4247
4478
|
turnBudget: config.turnBudget,
|
|
@@ -4251,7 +4482,8 @@ async function runSubagent(
|
|
|
4251
4482
|
onWriterProcess,
|
|
4252
4483
|
onExternalProcess: (process) => updateExternalProcess(fi, process),
|
|
4253
4484
|
onExternalJob: (externalJob) => updateExternalJob(fi, externalJob),
|
|
4254
|
-
skipAcceptance: () => timedOut || stopped,
|
|
4485
|
+
skipAcceptance: () => timedOut || stopped || childStopRequests.has(fi),
|
|
4486
|
+
orcaProgressTab,
|
|
4255
4487
|
}), config.deadlineAt);
|
|
4256
4488
|
if (task.sessionFile) {
|
|
4257
4489
|
latestSessionFile = task.sessionFile;
|
|
@@ -4305,6 +4537,7 @@ async function runSubagent(
|
|
|
4305
4537
|
setOptionalProperty(requiredStatusStep(statusPayload, fi), "structuredOutputPath", singleResult.structuredOutputPath);
|
|
4306
4538
|
setOptionalProperty(requiredStatusStep(statusPayload, fi), "structuredOutputSchemaPath", singleResult.structuredOutputSchemaPath);
|
|
4307
4539
|
setOptionalProperty(requiredStatusStep(statusPayload, fi), "acceptance", singleResult.acceptance);
|
|
4540
|
+
setOptionalProperty(requiredStatusStep(statusPayload, fi), "timeoutRecovery", singleResult.timeoutRecovery);
|
|
4308
4541
|
setOptionalProperty(requiredStatusStep(statusPayload, fi), "watchdog", singleResult.watchdog);
|
|
4309
4542
|
setOptionalProperty(requiredStatusStep(statusPayload, fi), "capabilityCeiling", singleResult.capabilityCeiling);
|
|
4310
4543
|
setOptionalProperty(requiredStatusStep(statusPayload, fi), "capabilityAudit", singleResult.capabilityAudit);
|
|
@@ -4319,6 +4552,7 @@ async function runSubagent(
|
|
|
4319
4552
|
ts: taskEndTime, runId: id, stepIndex: fi, agent: task.agent,
|
|
4320
4553
|
exitCode: stopped || childStopped ? 1 : timedOut ? 1 : childInterrupted ? 0 : singleResult.exitCode, durationMs: taskDuration,
|
|
4321
4554
|
}));
|
|
4555
|
+
if (stopped || childStopped) appendTerminalChildStatusEvent(fi, taskEndTime);
|
|
4322
4556
|
if (singleResult.completionGuardTriggered) {
|
|
4323
4557
|
const event = buildControlEvent(omitUndefinedProperties({
|
|
4324
4558
|
from: requiredStatusStep(statusPayload, fi).activityState,
|
|
@@ -4333,8 +4567,8 @@ async function runSubagent(
|
|
|
4333
4567
|
appendControlEvent(event);
|
|
4334
4568
|
}
|
|
4335
4569
|
|
|
4336
|
-
if (singleResult.exitCode !== 0 && failFast) aborted = true;
|
|
4337
|
-
return stopped || childStopped ? { ...singleResult, output: stopMessage, error: stopMessage, exitCode: 1, interrupted: false, timedOut: false, stopped: true, skipped: false } : timedOut ? { ...singleResult, output: timeoutMessage ?? "Subagent timed out.", error: timeoutMessage ?? "Subagent timed out.", exitCode: 1, interrupted: false, timedOut: true, skipped: false } : { ...singleResult, skipped: false };
|
|
4570
|
+
if (singleResult.exitCode !== 0 && failFast && !childStopped) aborted = true;
|
|
4571
|
+
return stopped || childStopped ? { ...singleResult, output: stopMessage, error: stopMessage, exitCode: 1, interrupted: false, timedOut: false, stopped: true, skipped: false } : timedOut ? { ...singleResult, output: singleResult.output || (timeoutMessage ?? "Subagent timed out."), error: singleResult.error ?? timeoutMessage ?? "Subagent timed out.", exitCode: 1, interrupted: false, timedOut: true, skipped: false } : { ...singleResult, skipped: false };
|
|
4338
4572
|
},
|
|
4339
4573
|
globalSemaphore,
|
|
4340
4574
|
);
|
|
@@ -4391,10 +4625,11 @@ async function runSubagent(
|
|
|
4391
4625
|
artifactPaths: pr.artifactPaths,
|
|
4392
4626
|
transcriptPath: pr.transcriptPath,
|
|
4393
4627
|
transcriptError: pr.transcriptError,
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4628
|
+
effects: pr.effects,
|
|
4629
|
+
execution: pr.execution,
|
|
4630
|
+
review: pr.review,
|
|
4631
|
+
timeoutRecovery: pr.timeoutRecovery,
|
|
4632
|
+
structuredOutput: pr.structuredOutput,
|
|
4398
4633
|
structuredOutputPath: pr.structuredOutputPath,
|
|
4399
4634
|
structuredOutputSchemaPath: pr.structuredOutputSchemaPath,
|
|
4400
4635
|
acceptance: pr.acceptance,
|
|
@@ -4490,6 +4725,26 @@ async function runSubagent(
|
|
|
4490
4725
|
}
|
|
4491
4726
|
} else {
|
|
4492
4727
|
const seqStep = step as SubagentStep;
|
|
4728
|
+
if (timedOut) {
|
|
4729
|
+
results.push(timedOutStepResult(seqStep.agent, seqStep.context));
|
|
4730
|
+
flatIndex++;
|
|
4731
|
+
continue;
|
|
4732
|
+
}
|
|
4733
|
+
if (stopped) {
|
|
4734
|
+
results.push(stoppedStepResult(seqStep.agent, seqStep.context));
|
|
4735
|
+
flatIndex++;
|
|
4736
|
+
continue;
|
|
4737
|
+
}
|
|
4738
|
+
if (childStopRequests.has(flatIndex)) {
|
|
4739
|
+
results.push(childStopResult(flatIndex, seqStep.agent, seqStep.context));
|
|
4740
|
+
flatIndex++;
|
|
4741
|
+
continue;
|
|
4742
|
+
}
|
|
4743
|
+
if (interrupted) {
|
|
4744
|
+
results.push(pausedStepResult(seqStep.agent, seqStep.context));
|
|
4745
|
+
flatIndex++;
|
|
4746
|
+
continue;
|
|
4747
|
+
}
|
|
4493
4748
|
let singleWorktreeSetup: WorktreeSetup | undefined;
|
|
4494
4749
|
if (seqStep.worktree) {
|
|
4495
4750
|
try {
|
|
@@ -4574,7 +4829,8 @@ async function runSubagent(
|
|
|
4574
4829
|
onWriterProcess,
|
|
4575
4830
|
onExternalProcess: (process) => updateExternalProcess(flatIndex, process),
|
|
4576
4831
|
onExternalJob: (externalJob) => updateExternalJob(flatIndex, externalJob),
|
|
4577
|
-
skipAcceptance: () => timedOut || stopped,
|
|
4832
|
+
skipAcceptance: () => timedOut || stopped || childStopRequests.has(flatIndex),
|
|
4833
|
+
orcaProgressTab,
|
|
4578
4834
|
}), config.deadlineAt);
|
|
4579
4835
|
} catch (error) {
|
|
4580
4836
|
if (singleWorktreeSetup) cleanupWorktrees(singleWorktreeSetup);
|
|
@@ -4593,7 +4849,7 @@ async function runSubagent(
|
|
|
4593
4849
|
launchContractDigest: singleResult.launchContractDigest,
|
|
4594
4850
|
launchResolvedExtensions: singleResult.launchResolvedExtensions,
|
|
4595
4851
|
runtimeAcknowledgedExtensions: singleResult.runtimeAcknowledgedExtensions,
|
|
4596
|
-
output: stopped || childStopped ? stopMessage : timedOut ? (timeoutMessage ?? "Subagent timed out.") : singleResult.output,
|
|
4852
|
+
output: stopped || childStopped ? stopMessage : timedOut ? singleResult.output || (timeoutMessage ?? "Subagent timed out.") : singleResult.output,
|
|
4597
4853
|
outputState: singleResult.outputState,
|
|
4598
4854
|
error: stopped || childStopped ? stopMessage : timedOut ? (timeoutMessage ?? "Subagent timed out.") : singleResult.error,
|
|
4599
4855
|
protocolError: singleResult.protocolError,
|
|
@@ -4612,6 +4868,7 @@ async function runSubagent(
|
|
|
4612
4868
|
effects: singleResult.effects,
|
|
4613
4869
|
execution: singleResult.execution,
|
|
4614
4870
|
review: singleResult.review,
|
|
4871
|
+
timeoutRecovery: singleResult.timeoutRecovery,
|
|
4615
4872
|
structuredOutput: singleResult.structuredOutput,
|
|
4616
4873
|
structuredOutputPath: singleResult.structuredOutputPath,
|
|
4617
4874
|
structuredOutputSchemaPath: singleResult.structuredOutputSchemaPath,
|
|
@@ -4698,6 +4955,7 @@ async function runSubagent(
|
|
|
4698
4955
|
setOptionalProperty(requiredStatusStep(statusPayload, flatIndex), "structuredOutputPath", singleResult.structuredOutputPath);
|
|
4699
4956
|
setOptionalProperty(requiredStatusStep(statusPayload, flatIndex), "structuredOutputSchemaPath", singleResult.structuredOutputSchemaPath);
|
|
4700
4957
|
setOptionalProperty(requiredStatusStep(statusPayload, flatIndex), "acceptance", singleResult.acceptance);
|
|
4958
|
+
setOptionalProperty(requiredStatusStep(statusPayload, flatIndex), "timeoutRecovery", singleResult.timeoutRecovery);
|
|
4701
4959
|
setOptionalProperty(requiredStatusStep(statusPayload, flatIndex), "watchdog", singleResult.watchdog);
|
|
4702
4960
|
setOptionalProperty(requiredStatusStep(statusPayload, flatIndex), "capabilityCeiling", singleResult.capabilityCeiling);
|
|
4703
4961
|
setOptionalProperty(requiredStatusStep(statusPayload, flatIndex), "capabilityAudit", singleResult.capabilityAudit);
|
|
@@ -4721,6 +4979,7 @@ async function runSubagent(
|
|
|
4721
4979
|
durationMs: stepEndTime - stepStartTime,
|
|
4722
4980
|
tokens: stepTokens,
|
|
4723
4981
|
}));
|
|
4982
|
+
if (stopped || childStopped) appendTerminalChildStatusEvent(flatIndex, stepEndTime);
|
|
4724
4983
|
if (singleWorktreeSetup && !singleResult.detached) {
|
|
4725
4984
|
const diffs = diffWorktrees(singleWorktreeSetup, [seqStep.agent], path.join(asyncDir, "worktree-diffs", `step-${stepIndex}`));
|
|
4726
4985
|
const diffSummary = formatWorktreeDiffSummary(diffs);
|
|
@@ -4981,6 +5240,7 @@ async function runSubagent(
|
|
|
4981
5240
|
structuredOutputSchemaPath: r.structuredOutputSchemaPath,
|
|
4982
5241
|
acceptance: r.acceptance,
|
|
4983
5242
|
watchdog: r.watchdog,
|
|
5243
|
+
timeoutRecovery: r.timeoutRecovery,
|
|
4984
5244
|
capabilityCeiling: r.capabilityCeiling,
|
|
4985
5245
|
capabilityAudit: r.capabilityAudit,
|
|
4986
5246
|
})),
|
|
@@ -5023,6 +5283,7 @@ async function runSubagent(
|
|
|
5023
5283
|
statusPayload.lastUpdate = Date.now();
|
|
5024
5284
|
}
|
|
5025
5285
|
writeStatusPayload();
|
|
5286
|
+
orcaProgressTab?.finish(statusPayload.state === "complete" ? "completed" : statusPayload.state === "stopped" ? "stopped" : "failed", effectiveSessionFile);
|
|
5026
5287
|
appendJsonl(
|
|
5027
5288
|
eventsPath,
|
|
5028
5289
|
JSON.stringify({
|