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
|
@@ -3,6 +3,7 @@ import type { AgentToolResult } from "@earendil-works/pi-agent-core";
|
|
|
3
3
|
import type { Details, SubagentState } from "../../shared/types.ts";
|
|
4
4
|
import { deliverStopRequest } from "../background/control-channel.ts";
|
|
5
5
|
import { reconcileAsyncRun } from "../background/stale-run-reconciler.ts";
|
|
6
|
+
import { isStoppableAsyncStatusStep, resolveAsyncStatusChild, type ResolvedAsyncStatusChild } from "../shared/child-identity.ts";
|
|
6
7
|
|
|
7
8
|
function getAsyncStopTarget(
|
|
8
9
|
state: SubagentState,
|
|
@@ -25,6 +26,7 @@ export function stopAsyncRun(
|
|
|
25
26
|
runId: string | undefined,
|
|
26
27
|
kill?: (pid: number, signal?: NodeJS.Signals | 0) => boolean,
|
|
27
28
|
location?: { asyncDir: string | null; resolvedId?: string },
|
|
29
|
+
childId?: string,
|
|
28
30
|
): AgentToolResult<Details> | null {
|
|
29
31
|
const target = getAsyncStopTarget(state, runId, location);
|
|
30
32
|
if (!target) return null;
|
|
@@ -43,15 +45,34 @@ export function stopAsyncRun(
|
|
|
43
45
|
details: { mode: "management", results: [] },
|
|
44
46
|
};
|
|
45
47
|
}
|
|
48
|
+
let child: ResolvedAsyncStatusChild | undefined;
|
|
49
|
+
if (childId !== undefined) {
|
|
50
|
+
const resolution = resolveAsyncStatusChild(status, childId);
|
|
51
|
+
if (!resolution.ok) {
|
|
52
|
+
return {
|
|
53
|
+
content: [{ type: "text", text: resolution.message }],
|
|
54
|
+
isError: true,
|
|
55
|
+
details: { mode: "management", results: [] },
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
child = resolution.child;
|
|
59
|
+
if (!isStoppableAsyncStatusStep(child.step)) {
|
|
60
|
+
return {
|
|
61
|
+
content: [{ type: "text", text: `Child '${childId}' in async run '${status.runId}' is ${child.step.status}; stop only supports pending or running children.` }],
|
|
62
|
+
isError: true,
|
|
63
|
+
details: { mode: "management", results: [] },
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
46
67
|
try {
|
|
47
|
-
deliverStopRequest({ asyncDir: target.asyncDir, pid: typeof status.pid === "number" ? status.pid : undefined, kill, source: "stop-action" });
|
|
68
|
+
deliverStopRequest({ asyncDir: target.asyncDir, pid: typeof status.pid === "number" ? status.pid : undefined, kill, source: "stop-action", targetIndex: child?.index, childId: child?.id ?? childId });
|
|
48
69
|
const tracked = state.asyncJobs.get(target.asyncId);
|
|
49
70
|
if (tracked) {
|
|
50
71
|
tracked.activityState = undefined;
|
|
51
72
|
tracked.updatedAt = Date.now();
|
|
52
73
|
}
|
|
53
74
|
return {
|
|
54
|
-
content: [{ type: "text", text: `Stop requested for async run ${target.asyncId}.` }],
|
|
75
|
+
content: [{ type: "text", text: child ? `Stop requested for child ${child.id} in async run ${target.asyncId}.` : `Stop requested for async run ${target.asyncId}.` }],
|
|
55
76
|
details: { mode: "management", results: [] },
|
|
56
77
|
};
|
|
57
78
|
} catch (error) {
|
|
@@ -55,7 +55,7 @@ import {
|
|
|
55
55
|
import { buildSkillInjection, resolveSkillsWithFallback } from "../../agents/skills.ts";
|
|
56
56
|
import { buildAgentMemoryInjection } from "../../agents/agent-memory.ts";
|
|
57
57
|
import { effectiveToolTimeoutMs, formatToolTimeoutMessage, resolveToolTimeoutMs, toolTimeoutCallKey, toolTimeoutFromEnv } from "../shared/tool-timeout.ts";
|
|
58
|
-
import { evaluateCompletionMutationGuard } from "../shared/completion-guard.ts";
|
|
58
|
+
import { evaluateCompletionMutationGuard, validateImplementationToolContract } from "../shared/completion-guard.ts";
|
|
59
59
|
import { arbitrateCompletionGuardRescue } from "../shared/llm-intent-arbiter.ts";
|
|
60
60
|
import { getPiSpawnCommand } from "../shared/pi-spawn.ts";
|
|
61
61
|
import { createJsonlWriter } from "../../shared/jsonl-writer.ts";
|
|
@@ -66,12 +66,15 @@ import { applyThinkingSuffix, buildPiArgs, cleanupTempDir, projectLaunchResolved
|
|
|
66
66
|
import { readRuntimeAcknowledgedExtensions } from "../shared/runtime-acknowledged-extensions.ts";
|
|
67
67
|
import { assertAgentAllowedByCapabilityCeiling, decodeSubagentCapabilityCeiling, intersectSubagentCapabilityCeilings, resolveCurrentSubagentCapabilityCeiling, SUBAGENT_CAPABILITY_CEILING_ENV } from "../shared/capability-ceiling.ts";
|
|
68
68
|
import { resolveEffectiveThinking } from "../../shared/model-info.ts";
|
|
69
|
-
import {
|
|
69
|
+
import { assertThinkingWithinCeiling, decodeThinkingCeiling, intersectThinkingCeilings, SUBAGENT_THINKING_CEILING_ENV } from "../../shared/thinking-ceiling.ts";
|
|
70
|
+
import { MISSING_STRUCTURED_OUTPUT_CALL_ERROR, readStructuredOutput, readStructuredOutputAcceptanceReport } from "../shared/structured-output.ts";
|
|
70
71
|
import { formatProcessSignalError, isUnexplainedProcessSignal } from "../shared/process-signal.ts";
|
|
71
72
|
import { readChildToolDiagnosticError } from "../shared/tool-availability.ts";
|
|
73
|
+
import { buildTimeoutRecoverySummary, collectTrackedMutationEvidence, snapshotTrackedMutations } from "../shared/mutation-evidence.ts";
|
|
72
74
|
import { captureSingleOutputSnapshot, extractChildWrittenOutput, finalizeSingleOutput, formatSavedOutputReference, injectOutputPathSystemPrompt, resolveSingleOutput, validateFileOnlyOutputMode, type SingleOutputSnapshot } from "../shared/single-output.ts";
|
|
73
75
|
import {
|
|
74
76
|
buildModelCandidates,
|
|
77
|
+
formatSubagentModelVerificationError,
|
|
75
78
|
formatModelAttemptNote,
|
|
76
79
|
isContextOverflow,
|
|
77
80
|
isRetryableModelFailure,
|
|
@@ -310,10 +313,13 @@ async function runSingleAttempt(
|
|
|
310
313
|
taskDelivery?: SubagentTaskDelivery;
|
|
311
314
|
orcaProgressTab?: OrcaProgressTab;
|
|
312
315
|
launchWarnings: { emitted: boolean };
|
|
316
|
+
verifyModel: boolean;
|
|
313
317
|
},
|
|
314
318
|
): Promise<SingleResult> {
|
|
315
319
|
const effectiveThinking = options.thinkingOverride ?? agent.thinking;
|
|
316
320
|
const modelArg = applyThinkingSuffix(model, effectiveThinking, options.thinkingOverride !== undefined);
|
|
321
|
+
assertThinkingWithinCeiling({ model: modelArg, configThinking: effectiveThinking, ceiling: options.thinkingCeiling, agent: agent.name, runId: options.runId });
|
|
322
|
+
const expectedModelForVerification = shared.verifyModel ? modelArg : undefined;
|
|
317
323
|
const resolvedThinking = resolveEffectiveThinking(modelArg, effectiveThinking);
|
|
318
324
|
const watchdogConfig = resolveWatchdogConfig(options.cwd ?? runtimeCwd);
|
|
319
325
|
const childWatchdog = watchdogConfig.ok
|
|
@@ -363,6 +369,8 @@ async function runSingleAttempt(
|
|
|
363
369
|
steerCapabilityPath: options.steerCapabilityPath,
|
|
364
370
|
steerAckDir: options.steerAckDir,
|
|
365
371
|
structuredOutput: options.structuredOutput,
|
|
372
|
+
fast: options.fast ?? agent.fast,
|
|
373
|
+
modelCandidates: shared.modelCandidates,
|
|
366
374
|
toolBudget: options.toolBudget,
|
|
367
375
|
allowZeroToolBudget: options.allowZeroToolBudget,
|
|
368
376
|
permissionRules,
|
|
@@ -370,6 +378,8 @@ async function runSingleAttempt(
|
|
|
370
378
|
childWatchdog,
|
|
371
379
|
waitToolEnabled: options.waitToolEnabled,
|
|
372
380
|
capabilityCeiling: options.capabilityCeiling,
|
|
381
|
+
thinkingCeiling: options.thinkingCeiling,
|
|
382
|
+
extensionBindings: options.extensionBindings,
|
|
373
383
|
});
|
|
374
384
|
if (!shared.launchWarnings.emitted && warnings.length > 0) {
|
|
375
385
|
for (const warning of warnings) console.warn(`[pi-subagents] ${warning}`);
|
|
@@ -385,18 +395,53 @@ async function runSingleAttempt(
|
|
|
385
395
|
cwd: options.cwd ?? runtimeCwd,
|
|
386
396
|
requireReadTool: Boolean(shared.resolvedSkillNames?.length),
|
|
387
397
|
structuredOutput: Boolean(options.structuredOutput),
|
|
398
|
+
fast: options.fast ?? agent.fast,
|
|
399
|
+
model: modelArg,
|
|
400
|
+
modelCandidates: shared.modelCandidates,
|
|
388
401
|
capabilityCeiling: options.capabilityCeiling,
|
|
389
402
|
inheritedCapabilityCeiling: decodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV]),
|
|
390
403
|
agentName: agent.name,
|
|
391
404
|
permissionRules,
|
|
392
405
|
});
|
|
406
|
+
const contractTools = toolPlan.explicitToolAllowlist ? toolPlan.effectiveToolAllowlist : undefined;
|
|
407
|
+
const contractError = validateImplementationToolContract({
|
|
408
|
+
agent: agent.name,
|
|
409
|
+
task: shared.originalTask ?? task,
|
|
410
|
+
tools: contractTools,
|
|
411
|
+
mcpDirectTools: toolPlan.effectiveMcpTools,
|
|
412
|
+
configuredExtensions: toolPlan.configuredExtensions,
|
|
413
|
+
requestedTools: toolPlan.requestedBuiltinTools,
|
|
414
|
+
acceptanceRole: agent.acceptanceRole,
|
|
415
|
+
completionGuard: agent.completionGuard,
|
|
416
|
+
});
|
|
417
|
+
if (contractError) {
|
|
418
|
+
cleanupTempDir(tempDir);
|
|
419
|
+
return {
|
|
420
|
+
index: options.index ?? 0,
|
|
421
|
+
agent: agent.name,
|
|
422
|
+
task,
|
|
423
|
+
messages: [],
|
|
424
|
+
finalOutput: "",
|
|
425
|
+
exitCode: 1,
|
|
426
|
+
error: contractError,
|
|
427
|
+
usage: emptyUsage(),
|
|
428
|
+
model: modelArg,
|
|
429
|
+
modelAttempts: [],
|
|
430
|
+
attemptedModels: [],
|
|
431
|
+
progressSummary: { status: "failed", toolCount: 0, tokens: 0, durationMs: 0 },
|
|
432
|
+
...(toolPlan.capabilityCeiling ? { capabilityCeiling: toolPlan.capabilityCeiling } : {}),
|
|
433
|
+
...(toolPlan.capabilityAudit ? { capabilityAudit: toolPlan.capabilityAudit } : {}),
|
|
434
|
+
};
|
|
435
|
+
}
|
|
393
436
|
const launchResolvedExtensions = projectLaunchResolvedChildExtensions(toolPlan);
|
|
394
437
|
const launchContractDigest = launchBindingDigest({
|
|
395
438
|
definitionDigest: agentDefinitionDigest(agent),
|
|
396
439
|
task: shared.originalTask ?? task,
|
|
397
440
|
...(modelArg ? { model: modelArg } : {}),
|
|
398
441
|
modelCandidates: shared.modelCandidates,
|
|
442
|
+
...((options.fast ?? agent.fast) !== undefined ? { fast: options.fast ?? agent.fast } : {}),
|
|
399
443
|
...(resolvedThinking ? { thinking: resolvedThinking } : {}),
|
|
444
|
+
...(options.thinkingCeiling ? { thinkingCeiling: options.thinkingCeiling } : {}),
|
|
400
445
|
systemPrompt: effectiveSystemPrompt,
|
|
401
446
|
systemPromptMode: agent.systemPromptMode,
|
|
402
447
|
inheritProjectContext: agent.inheritProjectContext,
|
|
@@ -408,6 +453,7 @@ async function runSingleAttempt(
|
|
|
408
453
|
...(options.outputPath ? { outputPath: options.outputPath } : {}),
|
|
409
454
|
outputMode: options.outputMode ?? "inline",
|
|
410
455
|
...(options.structuredOutput ? { structuredOutputSchema: options.structuredOutput.schema } : {}),
|
|
456
|
+
...(options.extensionBindings ? { extensionBindings: options.extensionBindings } : {}),
|
|
411
457
|
});
|
|
412
458
|
const result: SingleResult = withRunContext({
|
|
413
459
|
index: options.index ?? 0,
|
|
@@ -487,9 +533,11 @@ async function runSingleAttempt(
|
|
|
487
533
|
return result;
|
|
488
534
|
}
|
|
489
535
|
const spawnEnv = { ...process.env, ...sharedEnv, ...getSubagentDepthEnv(options.maxSubagentDepth) };
|
|
536
|
+
const mutationSnapshot = snapshotTrackedMutations(options.cwd ?? runtimeCwd);
|
|
490
537
|
let observedMutationAttempt = false;
|
|
491
538
|
let structuredOutputToolInvoked = false;
|
|
492
539
|
let structuredOutputMessageStartIndex: number | undefined;
|
|
540
|
+
let toolAvailabilityError: string | undefined;
|
|
493
541
|
|
|
494
542
|
const exitCode = await new Promise<number>((resolve) => {
|
|
495
543
|
const spawnSpec = getPiSpawnCommand(args);
|
|
@@ -1042,6 +1090,10 @@ async function runSingleAttempt(
|
|
|
1042
1090
|
if (evt.message.model) {
|
|
1043
1091
|
progress.model = evt.message.model;
|
|
1044
1092
|
if (!result.model) result.model = evt.message.model;
|
|
1093
|
+
if (expectedModelForVerification && !hasToolCall) {
|
|
1094
|
+
const modelVerificationError = formatSubagentModelVerificationError(expectedModelForVerification, evt.message.model, options.availableModels);
|
|
1095
|
+
if (modelVerificationError && !result.error) result.error = modelVerificationError;
|
|
1096
|
+
}
|
|
1045
1097
|
}
|
|
1046
1098
|
if (evt.message.errorMessage) assistantError = evt.message.errorMessage;
|
|
1047
1099
|
const assistantText = extractTextFromContent(evt.message.content);
|
|
@@ -1267,6 +1319,7 @@ async function runSingleAttempt(
|
|
|
1267
1319
|
// JSONL artifact flush is best effort.
|
|
1268
1320
|
});
|
|
1269
1321
|
const toolDiagnosticError = readChildToolDiagnosticError(toolDiagnosticPath);
|
|
1322
|
+
toolAvailabilityError = toolDiagnosticError;
|
|
1270
1323
|
result.runtimeAcknowledgedExtensions = readRuntimeAcknowledgedExtensions(runtimeAcknowledgedExtensionsPath);
|
|
1271
1324
|
cleanupTempDir(tempDir);
|
|
1272
1325
|
stdoutReader.end();
|
|
@@ -1405,6 +1458,9 @@ async function runSingleAttempt(
|
|
|
1405
1458
|
result.structuredOutputFailed = true;
|
|
1406
1459
|
} else {
|
|
1407
1460
|
result.structuredOutput = structured.value;
|
|
1461
|
+
const acceptanceReport = readStructuredOutputAcceptanceReport(options.structuredOutput);
|
|
1462
|
+
(result as SingleResult & { structuredAcceptanceReport?: unknown; structuredAcceptanceReportError?: string }).structuredAcceptanceReport = acceptanceReport.value;
|
|
1463
|
+
(result as SingleResult & { structuredAcceptanceReport?: unknown; structuredAcceptanceReportError?: string }).structuredAcceptanceReportError = acceptanceReport.error;
|
|
1408
1464
|
validatedStructuredOutput = true;
|
|
1409
1465
|
}
|
|
1410
1466
|
}
|
|
@@ -1442,6 +1498,7 @@ async function runSingleAttempt(
|
|
|
1442
1498
|
tokens: progress.tokens,
|
|
1443
1499
|
durationMs: progress.durationMs,
|
|
1444
1500
|
};
|
|
1501
|
+
const mutationEvidence = collectTrackedMutationEvidence(mutationSnapshot, options.cwd ?? runtimeCwd);
|
|
1445
1502
|
|
|
1446
1503
|
const acceptanceOutput = getFinalOutput(result.messages ?? []);
|
|
1447
1504
|
let fullOutput = stripAcceptanceReport(acceptanceOutput);
|
|
@@ -1449,9 +1506,19 @@ async function runSingleAttempt(
|
|
|
1449
1506
|
result.outputState = fullOutput.trim() || result.structuredOutput !== undefined ? "present" : "absent";
|
|
1450
1507
|
if (result.timedOut) {
|
|
1451
1508
|
const timeoutMessage = formatTimeoutMessage(options.timeoutMs ?? 0);
|
|
1509
|
+
result.timeoutRecovery = buildTimeoutRecoverySummary({
|
|
1510
|
+
termination: "timed-out",
|
|
1511
|
+
evidence: mutationEvidence,
|
|
1512
|
+
currentTool: progress.currentTool,
|
|
1513
|
+
currentToolArgs: progress.currentToolArgs,
|
|
1514
|
+
currentPath: progress.currentPath,
|
|
1515
|
+
sessionFile: options.sessionFile,
|
|
1516
|
+
transcriptPath: shared.transcriptWriter ? shared.artifactPaths?.transcriptPath : undefined,
|
|
1517
|
+
artifactPaths: shared.artifactPaths,
|
|
1518
|
+
});
|
|
1452
1519
|
fullOutput = fullOutput.trim()
|
|
1453
|
-
? `${timeoutMessage}\n\nPartial output before timeout:\n${fullOutput}`
|
|
1454
|
-
: timeoutMessage
|
|
1520
|
+
? `${timeoutMessage}\n\n${result.timeoutRecovery.message}\n\nPartial output before timeout:\n${fullOutput}`
|
|
1521
|
+
: `${timeoutMessage}\n\n${result.timeoutRecovery.message}`;
|
|
1455
1522
|
} else if (result.turnBudgetExceeded && result.turnBudget) {
|
|
1456
1523
|
fullOutput = formatTurnBudgetOutput(turnBudgetExceededMessage(result.turnBudget, result.turnBudget.turnCount), fullOutput);
|
|
1457
1524
|
} else if (result.turnBudget?.outcome === "termination-deferred") {
|
|
@@ -1462,16 +1529,20 @@ async function runSingleAttempt(
|
|
|
1462
1529
|
fullOutput = fullOutput.trim() ? `${note}\n\n${fullOutput}` : note;
|
|
1463
1530
|
}
|
|
1464
1531
|
const completionGuardEnabled = isAgentContractV1(options.agentContract) ? agent.completionGuard === true : agent.completionGuard !== false;
|
|
1465
|
-
const completionGuard = result.exitCode === 0 && !result.error && completionGuardEnabled
|
|
1532
|
+
const completionGuard = ((result.exitCode === 0 && !result.error) || toolAvailabilityError) && completionGuardEnabled
|
|
1466
1533
|
? evaluateCompletionMutationGuard({
|
|
1467
1534
|
agent: agent.name,
|
|
1468
1535
|
task: shared.originalTask ?? task,
|
|
1469
1536
|
messages: result.messages ?? [],
|
|
1470
|
-
tools:
|
|
1471
|
-
mcpDirectTools:
|
|
1537
|
+
tools: contractTools,
|
|
1538
|
+
mcpDirectTools: toolPlan.effectiveMcpTools,
|
|
1539
|
+
toolAvailabilityError,
|
|
1540
|
+
mutationEvidence,
|
|
1472
1541
|
})
|
|
1473
1542
|
: undefined;
|
|
1474
|
-
|
|
1543
|
+
const mutationAttemptObserved = observedMutationAttempt || mutationEvidence.attemptedMutation;
|
|
1544
|
+
let completionGuardTriggered = completionGuard?.triggered === true && !mutationAttemptObserved;
|
|
1545
|
+
const completionGuardBlocked = completionGuard?.blocked === true;
|
|
1475
1546
|
// The classifier is deliberately narrow, so a read-only review task can
|
|
1476
1547
|
// still be misread as implementation. Arbitrate BEFORE any failure side
|
|
1477
1548
|
// effect is published (effects, exit code, progress, notifications,
|
|
@@ -1492,7 +1563,9 @@ async function runSingleAttempt(
|
|
|
1492
1563
|
result.effects = {
|
|
1493
1564
|
...(result.effects ?? {}),
|
|
1494
1565
|
fileMutation: {
|
|
1495
|
-
status:
|
|
1566
|
+
status: completionGuardBlocked
|
|
1567
|
+
? "blocked"
|
|
1568
|
+
: completionGuard.expectedMutation
|
|
1496
1569
|
? completionGuardTriggered
|
|
1497
1570
|
? "missing"
|
|
1498
1571
|
: arbiterRescued
|
|
@@ -1500,7 +1573,9 @@ async function runSingleAttempt(
|
|
|
1500
1573
|
: "observed"
|
|
1501
1574
|
: "not-applicable",
|
|
1502
1575
|
expected: completionGuard.expectedMutation,
|
|
1503
|
-
attempted: completionGuard.attemptedMutation ||
|
|
1576
|
+
attempted: completionGuardBlocked ? false : completionGuard.attemptedMutation || mutationAttemptObserved,
|
|
1577
|
+
evidence: mutationEvidence,
|
|
1578
|
+
...(completionGuardBlocked && completionGuard.message ? { message: completionGuard.message } : {}),
|
|
1504
1579
|
...(completionGuardTriggered ? { message: "Subagent completed without making edits for an implementation task." } : {}),
|
|
1505
1580
|
...(arbiterRescued ? { resolvedBy: "llm-intent-arbiter" } : {}),
|
|
1506
1581
|
},
|
|
@@ -1582,6 +1657,14 @@ async function runSyncCompletionInner(
|
|
|
1582
1657
|
error: `Unknown agent: ${agentName}`,
|
|
1583
1658
|
}, options.context));
|
|
1584
1659
|
}
|
|
1660
|
+
options = {
|
|
1661
|
+
...options,
|
|
1662
|
+
thinkingCeiling: intersectThinkingCeilings(
|
|
1663
|
+
options.thinkingCeiling,
|
|
1664
|
+
agent.maxThinking,
|
|
1665
|
+
decodeThinkingCeiling(process.env[SUBAGENT_THINKING_CEILING_ENV]),
|
|
1666
|
+
),
|
|
1667
|
+
};
|
|
1585
1668
|
try {
|
|
1586
1669
|
assertAgentAllowedByCapabilityCeiling(agent.name, options.capabilityCeiling);
|
|
1587
1670
|
} catch (error) {
|
|
@@ -1652,7 +1735,7 @@ async function runSyncCompletionInner(
|
|
|
1652
1735
|
dynamicGroup: options.acceptanceContext?.dynamicGroup,
|
|
1653
1736
|
agentContract: options.agentContract,
|
|
1654
1737
|
});
|
|
1655
|
-
const acceptancePrompt = formatAcceptancePrompt(effectiveAcceptance, { reportOptional: isAgentContractV1(options.agentContract) });
|
|
1738
|
+
const acceptancePrompt = formatAcceptancePrompt(effectiveAcceptance, { reportOptional: isAgentContractV1(options.agentContract), structuredOutput: Boolean(options.structuredOutput?.acceptanceReportPath) });
|
|
1656
1739
|
const taskWithAcceptance = acceptancePrompt ? `${task}\n${acceptancePrompt}` : task;
|
|
1657
1740
|
options.onEffectivePrompt?.(taskWithAcceptance);
|
|
1658
1741
|
const sessionEnabled = Boolean(options.sessionFile || options.sessionDir) || shareEnabled;
|
|
@@ -1695,9 +1778,25 @@ async function runSyncCompletionInner(
|
|
|
1695
1778
|
options.modelOverride ?? agent.model,
|
|
1696
1779
|
agent.fallbackModels,
|
|
1697
1780
|
options.availableModels,
|
|
1698
|
-
options.preferredModelProvider,
|
|
1781
|
+
agent.modelProvider ?? options.preferredModelProvider,
|
|
1699
1782
|
{ scope: options.modelScope, primaryModelFromParent: options.modelOverrideFromParent },
|
|
1700
1783
|
);
|
|
1784
|
+
try {
|
|
1785
|
+
for (const candidate of candidates) {
|
|
1786
|
+
const model = applyThinkingSuffix(candidate, options.thinkingOverride ?? agent.thinking, options.thinkingOverride !== undefined);
|
|
1787
|
+
assertThinkingWithinCeiling({ model, configThinking: options.thinkingOverride ?? agent.thinking, ceiling: options.thinkingCeiling, agent: agent.name, runId: options.runId });
|
|
1788
|
+
}
|
|
1789
|
+
} catch (error) {
|
|
1790
|
+
return redactResultPrompt(withRunContext({
|
|
1791
|
+
index: options.index ?? 0,
|
|
1792
|
+
agent: agent.name,
|
|
1793
|
+
task,
|
|
1794
|
+
exitCode: 1,
|
|
1795
|
+
messages: [],
|
|
1796
|
+
usage: emptyUsage(),
|
|
1797
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1798
|
+
}, options.context));
|
|
1799
|
+
}
|
|
1701
1800
|
const attemptedModels: string[] = [];
|
|
1702
1801
|
const modelAttempts: ModelAttempt[] = [];
|
|
1703
1802
|
const aggregateUsage = emptyUsage();
|
|
@@ -1777,6 +1876,7 @@ async function runSyncCompletionInner(
|
|
|
1777
1876
|
modelAttemptsLoop: for (let modelIndex = 0; modelIndex < modelsToTry.length; modelIndex++) {
|
|
1778
1877
|
const candidate = modelsToTry[modelIndex];
|
|
1779
1878
|
for (let startupAttemptIndex = 0; ; startupAttemptIndex++) {
|
|
1879
|
+
const verifyModel = Boolean(candidate) && !(options.modelOverrideFromParent && modelIndex === 0);
|
|
1780
1880
|
const outputSnapshot = captureSingleOutputSnapshot(options.outputPath);
|
|
1781
1881
|
const result = await runSingleAttempt(runtimeCwd, agent, taskWithAcceptance, candidate, attemptOptions, {
|
|
1782
1882
|
sessionEnabled,
|
|
@@ -1795,6 +1895,7 @@ async function runSyncCompletionInner(
|
|
|
1795
1895
|
taskDelivery: taskDeliveryOverride,
|
|
1796
1896
|
orcaProgressTab,
|
|
1797
1897
|
launchWarnings,
|
|
1898
|
+
verifyModel,
|
|
1798
1899
|
});
|
|
1799
1900
|
lastResult = result;
|
|
1800
1901
|
if (startupAttemptIndex === 0) {
|
|
@@ -1971,6 +2072,8 @@ async function runSyncCompletionInner(
|
|
|
1971
2072
|
result.acceptance = await evaluateAcceptance({
|
|
1972
2073
|
acceptance: effectiveAcceptance,
|
|
1973
2074
|
output: acceptanceOutputByResult.get(result) ?? result.finalOutput ?? "",
|
|
2075
|
+
report: (result as SingleResult & { structuredAcceptanceReport?: import("../../shared/types.ts").AcceptanceReport; structuredAcceptanceReportError?: string }).structuredAcceptanceReport,
|
|
2076
|
+
reportError: (result as SingleResult & { structuredAcceptanceReport?: import("../../shared/types.ts").AcceptanceReport; structuredAcceptanceReportError?: string }).structuredAcceptanceReportError,
|
|
1974
2077
|
fileOutput: childWrittenOutput !== undefined && options.outputPath
|
|
1975
2078
|
? { content: childWrittenOutput, path: options.outputPath, authoritative: options.outputMode === "file-only" }
|
|
1976
2079
|
: undefined,
|
|
@@ -2,7 +2,7 @@ import * as fs from "node:fs";
|
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import type { ForegroundResumeChild, ForegroundResumeRun, SubagentState } from "../../shared/types.ts";
|
|
4
4
|
import { DIRS } from "../../shared/types.ts";
|
|
5
|
-
import {
|
|
5
|
+
import { writePrivateAtomicJson } from "../../shared/atomic-json.ts";
|
|
6
6
|
import { utf8Tail } from "../../shared/utf8.ts";
|
|
7
7
|
|
|
8
8
|
export const MAX_REMEMBERED_FOREGROUND_RUNS = 50;
|
|
@@ -52,6 +52,7 @@ function compactChild(child: ForegroundResumeChild): ForegroundResumeChild {
|
|
|
52
52
|
...(child.transcriptError ? { transcriptError: child.transcriptError } : {}),
|
|
53
53
|
...(child.acceptance ? { acceptance: child.acceptance } : {}),
|
|
54
54
|
...(child.launchContractDigest ? { launchContractDigest: child.launchContractDigest } : {}),
|
|
55
|
+
...(child.extensionBindings ? { extensionBindings: child.extensionBindings } : {}),
|
|
55
56
|
...(child.capabilityCeiling ? { capabilityCeiling: child.capabilityCeiling } : {}),
|
|
56
57
|
...(child.updatedAt !== undefined ? { updatedAt: child.updatedAt } : {}),
|
|
57
58
|
};
|
|
@@ -118,7 +119,7 @@ export function persistForegroundRunHistory(state: SubagentState, options: { res
|
|
|
118
119
|
if (compact) merged.set(compact.runId, compact);
|
|
119
120
|
}
|
|
120
121
|
const runs = sortAndBound([...merged.values()], limit);
|
|
121
|
-
|
|
122
|
+
writePrivateAtomicJson(historyPath(resultsDir), { version: HISTORY_VERSION, runs });
|
|
122
123
|
}
|
|
123
124
|
|
|
124
125
|
export function restoreForegroundRunHistory(state: SubagentState, options: { resultsDir?: string; sessionId?: string | null; limit?: number } = {}): number {
|