pi-subagents 0.67.0 → 0.69.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 +90 -0
- package/README.md +1 -1
- package/docs/agents.md +41 -12
- package/docs/configuration.md +61 -19
- package/docs/extension-api.md +5 -1
- package/docs/missions.md +2 -2
- package/docs/models.md +11 -79
- package/docs/observability.md +18 -8
- package/docs/standalone-background.md +13 -3
- package/docs/tool-reference.md +38 -14
- package/docs/watchdog.md +10 -12
- package/docs/workflows.md +59 -1
- package/index.ts +5 -2
- package/package.json +4 -2
- package/runner-peer-loader.mjs +24 -0
- package/runner-peer-preload.mjs +25 -11
- package/skills/pi-subagents/SKILL.md +18 -21
- package/skills/pi-subagents/references/constraints-and-recipes.md +3 -2
- package/skills/pi-subagents/references/execution-controls.md +6 -4
- package/skills/pi-subagents/references/management-authoring-rpc.md +0 -1
- package/skills/pi-subagents/references/multi-lane-orchestration.md +1 -1
- package/skills/pi-subagents/references/prompting-and-roles.md +16 -12
- package/skills/pi-subagents/references/review-and-validation.md +3 -3
- package/src/agents/agent-management.ts +57 -58
- package/src/agents/agent-serializer.ts +4 -3
- package/src/agents/agents.ts +185 -72
- package/src/agents/chain-serializer.ts +5 -0
- package/src/agents/runtime-agent-registry.ts +7 -6
- package/src/agents/skills.ts +1 -1
- package/src/api/preflight.ts +20 -16
- package/src/api/required-child-extensions.ts +6 -0
- package/src/extension/config.ts +10 -37
- package/src/extension/fanout-child.ts +3 -0
- package/src/extension/herdr-pi-bridge.ts +160 -0
- package/src/extension/index.ts +42 -31
- package/src/extension/public-execution.ts +3 -3
- package/src/extension/schemas.ts +23 -6
- package/src/extension/tool-description.ts +8 -7
- package/src/inspectors/ghostty/plugin.ts +13 -1
- package/src/intercom/native-supervisor-channel.ts +22 -18
- package/src/policy/authority.ts +4 -0
- package/src/profiles/profiles.ts +12 -6
- package/src/runs/background/active-run-index.ts +17 -1
- package/src/runs/background/async-execution.ts +309 -126
- package/src/runs/background/async-job-tracker.ts +8 -6
- package/src/runs/background/async-resume.ts +13 -4
- package/src/runs/background/async-status.ts +15 -4
- package/src/runs/background/auto-drain.ts +20 -10
- package/src/runs/background/binary-bootstrap.ts +5 -0
- package/src/runs/background/chain-append.ts +1 -1
- package/src/runs/background/chain-root-attachment.ts +14 -33
- package/src/runs/background/notify.ts +74 -6
- package/src/runs/background/result-files.ts +8 -4
- package/src/runs/background/result-watcher.ts +19 -2
- package/src/runs/background/run-child-session.ts +20 -29
- package/src/runs/background/runner-aliases.ts +4 -33
- package/src/runs/background/runner-child-launch.ts +4 -1
- package/src/runs/background/runner-child-sessions.ts +2 -2
- package/src/runs/background/runner-http-dispatcher.ts +119 -0
- package/src/runs/background/scheduled-runs.ts +11 -5
- package/src/runs/background/stale-run-reconciler.ts +35 -11
- package/src/runs/background/subagent-runner.ts +413 -276
- package/src/runs/background/subagent-wait.ts +128 -23
- package/src/runs/background/wait-completions.ts +75 -27
- package/src/runs/background/wait-subscriptions.ts +9 -3
- package/src/runs/background/wait-tool.ts +4 -2
- package/src/runs/foreground/async-stop-action.ts +93 -3
- package/src/runs/foreground/execution.ts +115 -219
- package/src/runs/foreground/foreground-history.ts +2 -1
- package/src/runs/foreground/subagent-executor.ts +281 -80
- package/src/runs/shared/acceptance.ts +194 -37
- package/src/runs/shared/async-status-projection.ts +123 -33
- package/src/runs/shared/child-launch-plan.ts +15 -3
- package/src/runs/shared/child-launch.ts +19 -6
- package/src/runs/shared/child-runtime-config.ts +5 -0
- package/src/runs/shared/child-session.ts +94 -50
- package/src/runs/shared/child-tool-plan.ts +28 -16
- package/src/runs/shared/dynamic-fanout.ts +2 -2
- package/src/runs/shared/external-cli-contract.ts +11 -1
- package/src/runs/shared/external-cli-preflight.ts +6 -2
- package/src/runs/shared/herdr-connection.ts +134 -0
- package/src/runs/shared/herdr-external-adapters.ts +169 -0
- package/src/runs/shared/herdr-machine.ts +279 -0
- package/src/runs/shared/herdr-pi-protocol.ts +59 -0
- package/src/runs/shared/herdr-placed-run.ts +263 -0
- package/src/runs/shared/model-resolution-diagnostic.ts +76 -0
- package/src/runs/shared/{model-fallback.ts → model-resolution.ts} +22 -237
- package/src/runs/shared/model-scope.ts +1 -1
- package/src/runs/shared/nested-events.ts +11 -2
- package/src/runs/shared/parallel-utils.ts +7 -2
- package/src/runs/shared/pi-spawn.ts +1 -1
- package/src/runs/shared/subagent-prompt-runtime.ts +4 -2
- package/src/runs/shared/worktree-setup-command.ts +27 -4
- package/src/runs/shared/worktree.ts +30 -8
- package/src/shared/child-cache-retention.ts +43 -0
- package/src/shared/launch-contract.ts +6 -9
- package/src/shared/pruned-fork.ts +1 -1
- package/src/shared/required-child-extensions.ts +81 -0
- package/src/shared/settings.ts +5 -2
- package/src/shared/shortcuts.ts +0 -4
- package/src/shared/types.ts +81 -29
- package/src/slash/slash-commands.ts +0 -6
- package/src/slash/subagents-admin.ts +13 -9
- package/src/tui/render.ts +20 -10
- package/src/watchdog/child-status.ts +28 -36
- package/src/watchdog/lsp-diagnostics.ts +1 -1
- package/src/watchdog/model-selection.ts +1 -1
- package/src/watchdog/register-child.ts +10 -3
- package/src/watchdog/register-main.ts +20 -20
- package/src/watchdog/render.ts +1 -1
- package/src/watchdog/review.ts +14 -30
- package/src/watchdog/rules.ts +1 -1
- package/src/watchdog/runtime.ts +23 -12
- package/src/watchdog/settings.ts +3 -6
- package/src/watchdog/types.ts +3 -5
- package/src/watchdog/warning-format.ts +1 -1
- package/src/workflows/scripted-workflow.ts +68 -7
- package/src/workflows/workflow-receipt.ts +21 -3
- package/src/workflows/workflow-resources.ts +13 -2
- package/src/runs/shared/model-exclusions.ts +0 -374
- package/src/runs/shared/readonly-model-continuation.ts +0 -69
- package/src/runs/shared/readonly-session-evidence.ts +0 -307
|
@@ -20,7 +20,6 @@ import {
|
|
|
20
20
|
type AgentProgress,
|
|
21
21
|
type ArtifactPaths,
|
|
22
22
|
type ControlEvent,
|
|
23
|
-
type ModelAttempt,
|
|
24
23
|
type RunSyncOptions,
|
|
25
24
|
type SingleResult,
|
|
26
25
|
type Usage,
|
|
@@ -56,10 +55,6 @@ import { resolveSkillsWithFallback } from "../../agents/skills.ts";
|
|
|
56
55
|
import { effectiveToolTimeoutMs, formatToolTimeoutMessage, resolveToolTimeoutMs, toolTimeoutCallKey, toolTimeoutFromEnv } from "../shared/tool-timeout.ts";
|
|
57
56
|
import { evaluateCompletionMutationGuard, expectsImplementationMutation, hasMutationToolCapability, validateImplementationToolContract } from "../shared/completion-guard.ts";
|
|
58
57
|
import { planCompletionEvidence } from "../shared/completion-evidence.ts";
|
|
59
|
-
import { planAbortRecovery } from "../shared/abort-recovery.ts";
|
|
60
|
-
import { planReadonlyModelContinuation, type LogicalRecoveryState } from "../shared/readonly-model-continuation.ts";
|
|
61
|
-
import { getReadonlySessionEvidence, requestReadonlySessionEvidence, type SettledReadonlyEvidence } from "../shared/readonly-session-evidence.ts";
|
|
62
|
-
import { getReadonlyChildModels } from "../shared/child-session.ts";
|
|
63
58
|
import { arbitrateCompletionGuardRescue } from "../shared/llm-intent-arbiter.ts";
|
|
64
59
|
import { preflightLaunchCwd } from "../shared/launch-cwd.ts";
|
|
65
60
|
import { createJsonlWriter } from "../../shared/jsonl-writer.ts";
|
|
@@ -73,16 +68,15 @@ import { assertThinkingWithinCeiling, intersectThinkingCeilings } from "../../sh
|
|
|
73
68
|
import { MISSING_STRUCTURED_ACCEPTANCE_REPORT_ERROR, MISSING_STRUCTURED_OUTPUT_CALL_ERROR } from "../shared/structured-output.ts";
|
|
74
69
|
import { formatMidToolExitError, isOrdinaryToolForMidToolExit } from "../shared/process-signal.ts";
|
|
75
70
|
import { formatChildToolDiagnostic } from "../shared/tool-availability.ts";
|
|
71
|
+
import { formatChildModelResolutionDiagnostic, isChildModelResolutionFailure } from "../shared/model-resolution-diagnostic.ts";
|
|
72
|
+
import { planAbortRecovery } from "../shared/abort-recovery.ts";
|
|
76
73
|
import { buildTimeoutRecoverySummary, collectTrackedMutationEvidence, snapshotTrackedMutations } from "../shared/mutation-evidence.ts";
|
|
77
74
|
import { captureSingleOutputSnapshot, extractChildWrittenOutput, finalizeSingleOutput, formatSavedOutputReference, hasSingleOutputChangedSinceSnapshot, resolveSingleOutput, validateFileOnlyOutputMode, type SingleOutputSnapshot } from "../shared/single-output.ts";
|
|
78
75
|
import {
|
|
79
|
-
buildModelCandidates,
|
|
80
76
|
formatSubagentModelVerificationError,
|
|
81
|
-
formatModelAttemptNote,
|
|
82
77
|
isContextOverflow,
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
} from "../shared/model-fallback.ts";
|
|
78
|
+
resolveModelSelection,
|
|
79
|
+
} from "../shared/model-resolution.ts";
|
|
86
80
|
import {
|
|
87
81
|
createMutatingFailureState,
|
|
88
82
|
didMutatingToolFail,
|
|
@@ -94,7 +88,7 @@ import {
|
|
|
94
88
|
shouldEscalateMutatingFailures,
|
|
95
89
|
summarizeRecentMutatingFailures,
|
|
96
90
|
} from "../shared/long-running-guard.ts";
|
|
97
|
-
import { acceptanceFailureMessage, buildSkippedAcceptanceLedger, evaluateAcceptance, formatAcceptancePrompt, resolveEffectiveAcceptance, stripAcceptanceReport, validateAcceptanceInput } from "../shared/acceptance.ts";
|
|
91
|
+
import { acceptanceFailureMessage, buildSkippedAcceptanceLedger, captureStagedIndexBaseline, evaluateAcceptance, formatAcceptancePrompt, resolveEffectiveAcceptance, stripAcceptanceReport, validateAcceptanceInput, typedVerifyOutput } from "../shared/acceptance.ts";
|
|
98
92
|
import { PROMPT_REDACTED } from "../../shared/utils.ts";
|
|
99
93
|
import { attachContractProjections, isAgentContract } from "../shared/agent-contract.ts";
|
|
100
94
|
import { initialToolBudgetState, toolBudgetState } from "../shared/tool-budget.ts";
|
|
@@ -160,8 +154,7 @@ function persistSingleResultMetadata(input: {
|
|
|
160
154
|
processSignal: target.processSignal,
|
|
161
155
|
usage: target.usage,
|
|
162
156
|
model: target.model,
|
|
163
|
-
|
|
164
|
-
modelAttempts: target.modelAttempts,
|
|
157
|
+
requestedModel: target.requestedModel,
|
|
165
158
|
durationMs: target.progressSummary?.durationMs,
|
|
166
159
|
toolCount: target.progressSummary?.toolCount,
|
|
167
160
|
error: target.error,
|
|
@@ -261,13 +254,6 @@ function snapshotResult(result: SingleResult, progress: AgentProgress): SingleRe
|
|
|
261
254
|
messages: result.outputMode === "file-only" && result.savedOutputPath ? undefined : result.messages ? [...result.messages] : undefined,
|
|
262
255
|
usage: { ...result.usage },
|
|
263
256
|
skills: result.skills ? [...result.skills] : undefined,
|
|
264
|
-
attemptedModels: result.attemptedModels ? [...result.attemptedModels] : undefined,
|
|
265
|
-
modelAttempts: result.modelAttempts
|
|
266
|
-
? result.modelAttempts.map((attempt) => ({
|
|
267
|
-
...attempt,
|
|
268
|
-
usage: attempt.usage ? { ...attempt.usage } : undefined,
|
|
269
|
-
}))
|
|
270
|
-
: undefined,
|
|
271
257
|
controlEvents: result.controlEvents ? result.controlEvents.map((event) => ({ ...event })) : undefined,
|
|
272
258
|
progress,
|
|
273
259
|
progressSummary: result.progressSummary ? { ...result.progressSummary } : undefined,
|
|
@@ -340,11 +326,10 @@ function structuredDelegationProgressChanged(
|
|
|
340
326
|
return false;
|
|
341
327
|
}
|
|
342
328
|
|
|
343
|
-
const AFTER_COMPACTION_SETTLEMENT = Symbol("afterCompactionSettlement");
|
|
344
|
-
type AbortRecoverySingleResult = SingleResult & { [AFTER_COMPACTION_SETTLEMENT]?: true };
|
|
345
|
-
const settledReadonlySource = new WeakMap<SingleResult, ChildSession>();
|
|
346
329
|
|
|
347
330
|
const STOPPED_BEFORE_COMPLETION_ERROR = "Subagent stopped before completion.";
|
|
331
|
+
const AFTER_COMPACTION_SETTLEMENT = Symbol("afterCompactionSettlement");
|
|
332
|
+
type AbortRecoverySingleResult = SingleResult & { [AFTER_COMPACTION_SETTLEMENT]?: true };
|
|
348
333
|
|
|
349
334
|
|
|
350
335
|
async function runSingleAttempt(
|
|
@@ -358,7 +343,6 @@ async function runSingleAttempt(
|
|
|
358
343
|
systemPrompt: string;
|
|
359
344
|
acceptancePrompt: string;
|
|
360
345
|
resolvedSkillNames?: string[];
|
|
361
|
-
modelCandidates?: string[];
|
|
362
346
|
skillsWarning?: string;
|
|
363
347
|
jsonlPath?: string;
|
|
364
348
|
artifactPaths?: ArtifactPaths;
|
|
@@ -369,9 +353,6 @@ async function runSingleAttempt(
|
|
|
369
353
|
orcaProgressTab?: OrcaProgressTab;
|
|
370
354
|
launchWarnings: { emitted: boolean };
|
|
371
355
|
verifyModel: boolean;
|
|
372
|
-
readonlyExpected?: SettledReadonlyEvidence;
|
|
373
|
-
readonlyModel?: string;
|
|
374
|
-
readonlyHandoffAllowed?: () => boolean;
|
|
375
356
|
},
|
|
376
357
|
): Promise<SingleResult> {
|
|
377
358
|
const effectiveThinking = options.thinkingOverride ?? agent.thinking;
|
|
@@ -398,7 +379,11 @@ async function runSingleAttempt(
|
|
|
398
379
|
: undefined;
|
|
399
380
|
let onWatchdogStatus: ((event: ChildWatchdogStatusEvent) => void) | undefined;
|
|
400
381
|
const launch = buildInProcessChildLaunch({
|
|
382
|
+
machine: options.machine,
|
|
383
|
+
remoteSkillNames: options.skills ?? agent.skills,
|
|
384
|
+
remoteReads: options.remoteReads,
|
|
401
385
|
extensionBindings: options.extensionBindings,
|
|
386
|
+
requiredExtensions: options.requiredExtensions,
|
|
402
387
|
sessionEnabled: shared.sessionEnabled,
|
|
403
388
|
sessionDir: options.sessionDir,
|
|
404
389
|
sessionFile: options.sessionFile,
|
|
@@ -429,7 +414,6 @@ async function runSingleAttempt(
|
|
|
429
414
|
forkCacheKey: options.context === "fork" ? deriveForkPromptCacheKey(options.parentSessionId) : undefined,
|
|
430
415
|
structuredOutput: options.structuredOutput,
|
|
431
416
|
fast: options.fast ?? agent.fast,
|
|
432
|
-
modelCandidates: shared.modelCandidates,
|
|
433
417
|
toolBudget: options.toolBudget,
|
|
434
418
|
permissionRules,
|
|
435
419
|
permissionAuditPath,
|
|
@@ -446,6 +430,7 @@ async function runSingleAttempt(
|
|
|
446
430
|
inherited: options.childRuntime,
|
|
447
431
|
host: "parent",
|
|
448
432
|
});
|
|
433
|
+
if (!options.machine && options.parentProviderRegistry) launch.session.parentProviderRegistry = options.parentProviderRegistry;
|
|
449
434
|
const { toolPlan, capabilityAudit, warnings, launchResolvedExtensions, capture } = launch;
|
|
450
435
|
if (!shared.launchWarnings.emitted && warnings.length > 0) {
|
|
451
436
|
for (const warning of warnings) console.warn(`[pi-subagents] ${warning}`);
|
|
@@ -476,8 +461,6 @@ async function runSingleAttempt(
|
|
|
476
461
|
error: contractError,
|
|
477
462
|
usage: emptyUsage(),
|
|
478
463
|
model: modelArg,
|
|
479
|
-
modelAttempts: [],
|
|
480
|
-
attemptedModels: [],
|
|
481
464
|
progressSummary: { status: "failed", toolCount: 0, tokens: 0, durationMs: 0 },
|
|
482
465
|
...(toolPlan.capabilityCeiling ? { capabilityCeiling: toolPlan.capabilityCeiling } : {}),
|
|
483
466
|
...(toolPlan.capabilityAudit ? { capabilityAudit: toolPlan.capabilityAudit } : {}),
|
|
@@ -487,7 +470,7 @@ async function runSingleAttempt(
|
|
|
487
470
|
const { launchContractDigest } = resolveLaunchBinding({
|
|
488
471
|
agent,
|
|
489
472
|
task: shared.originalTask ?? task,
|
|
490
|
-
|
|
473
|
+
model: modelArg,
|
|
491
474
|
...(fast !== undefined ? { fast } : {}),
|
|
492
475
|
...(resolvedThinking ? { thinking: resolvedThinking } : {}),
|
|
493
476
|
systemPrompt: effectiveSystemPrompt,
|
|
@@ -568,12 +551,13 @@ async function runSingleAttempt(
|
|
|
568
551
|
};
|
|
569
552
|
return result;
|
|
570
553
|
}
|
|
571
|
-
const mutationSnapshot = snapshotTrackedMutations(options.cwd ?? runtimeCwd);
|
|
554
|
+
const mutationSnapshot = options.machine ? { source: "tracked-files" as const, trackedOnly: true as const, cwd: options.cwd ?? runtimeCwd, dirtyFiles: [], fingerprints: {}, unavailable: "Local Git evidence is not authoritative for a pane-native remote run." } : snapshotTrackedMutations(options.cwd ?? runtimeCwd);
|
|
572
555
|
let observedMutationAttempt = false;
|
|
573
556
|
let structuredOutputToolInvoked = false;
|
|
574
557
|
let structuredOutputMessageStartIndex: number | undefined;
|
|
575
558
|
let toolAvailabilityError: string | undefined;
|
|
576
559
|
let abortedBySignal = options.signal?.aborted === true;
|
|
560
|
+
let afterCompactionSettlement = false;
|
|
577
561
|
|
|
578
562
|
if (options.workflowChildPermitLaunch) {
|
|
579
563
|
const permitError = consumeWorkflowChildPermit(options.workflowChildPermitLaunch.permit, {
|
|
@@ -594,7 +578,6 @@ async function runSingleAttempt(
|
|
|
594
578
|
}
|
|
595
579
|
}
|
|
596
580
|
const childSessions = options.childSessionFactory ?? childSessionFactory();
|
|
597
|
-
let afterCompactionSettlement = false;
|
|
598
581
|
const exitCode = await new Promise<number>((resolve) => {
|
|
599
582
|
const jsonlWriter = createJsonlWriter(shared.jsonlPath, { pause() {}, resume() {} });
|
|
600
583
|
let session: ChildSession | undefined;
|
|
@@ -801,7 +784,6 @@ async function runSingleAttempt(
|
|
|
801
784
|
});
|
|
802
785
|
// Report the run only after the child's extensions have shut down.
|
|
803
786
|
void Promise.resolve().then(() => session?.dispose()).catch(() => undefined).then(() => {
|
|
804
|
-
if (session && getReadonlySessionEvidence(session)) settledReadonlySource.set(result, session);
|
|
805
787
|
resolve(code);
|
|
806
788
|
});
|
|
807
789
|
};
|
|
@@ -1308,9 +1290,23 @@ async function runSingleAttempt(
|
|
|
1308
1290
|
const toolDiagnosticError = diagnostic ? formatChildToolDiagnostic(diagnostic, { host: "parent" }) : undefined;
|
|
1309
1291
|
toolAvailabilityError = toolDiagnosticError;
|
|
1310
1292
|
result.runtimeAcknowledgedExtensions = capture.runtimeAcknowledgedExtensions();
|
|
1293
|
+
if (session?.machineEvidence) result.nativeMachine = { provider: "herdr", machineId: session.machineEvidence.machineId, ...(session.machineEvidence.initial ? { initialGit: session.machineEvidence.initial } : {}), ...(session.machineEvidence.final ? { finalGit: session.machineEvidence.final } : {}) };
|
|
1311
1294
|
let closeError = result.error ?? toolDiagnosticError ?? assistantError;
|
|
1312
|
-
|
|
1313
|
-
|
|
1295
|
+
const promptErrorMessage = promptError === undefined ? undefined : promptError instanceof Error ? promptError.message : String(promptError);
|
|
1296
|
+
if (!closeError && promptErrorMessage !== undefined) {
|
|
1297
|
+
closeError = promptErrorMessage;
|
|
1298
|
+
}
|
|
1299
|
+
// A foreground child never loads the parent's ambient extensions, so a
|
|
1300
|
+
// provider one registers resolves as "not found" before the child starts.
|
|
1301
|
+
// Annotate only a creation/prompt failure that produced no turn; keep the
|
|
1302
|
+
// core error and add the host rule and both remedies after it.
|
|
1303
|
+
if (promptErrorMessage !== undefined
|
|
1304
|
+
&& closeError === promptErrorMessage
|
|
1305
|
+
&& isChildModelResolutionFailure(promptErrorMessage)
|
|
1306
|
+
&& (result.messages?.length ?? 0) === 0
|
|
1307
|
+
&& result.usage.turns === 0
|
|
1308
|
+
&& !launch.session.ambientExtensions) {
|
|
1309
|
+
closeError = `${promptErrorMessage}\n\n${formatChildModelResolutionDiagnostic({ agent: agent.name, model: launch.session.model, host: "parent", capabilityCeiling: launch.toolPlan.capabilityCeiling })}`;
|
|
1314
1310
|
}
|
|
1315
1311
|
const forcedDrainAfterFinalSuccess = (forced || forcedTermination) && (cleanTerminalAssistantStopReceived || agentSettledReceived) && !closeError;
|
|
1316
1312
|
const forcedDrainAfterEmptyTerminal = forcedDrainAfterFinalSuccess && hasEmptyTerminalAssistantResponse(result.messages ?? []);
|
|
@@ -1383,8 +1379,6 @@ async function runSingleAttempt(
|
|
|
1383
1379
|
void (async () => {
|
|
1384
1380
|
try {
|
|
1385
1381
|
const input = createReportedChildSessionInput(launch, shared.transcriptWriter);
|
|
1386
|
-
requestReadonlySessionEvidence(input, shared.readonlyExpected);
|
|
1387
|
-
if (shared.readonlyHandoffAllowed && !shared.readonlyHandoffAllowed()) throw new Error("Read-only continuation handoff vetoed.");
|
|
1388
1382
|
const created = await childSessions.create(input);
|
|
1389
1383
|
if (lifecycleFinished) {
|
|
1390
1384
|
void created.dispose();
|
|
@@ -1407,10 +1401,6 @@ async function runSingleAttempt(
|
|
|
1407
1401
|
abortChild();
|
|
1408
1402
|
}
|
|
1409
1403
|
options.onChildSession?.({ steer: (text) => created.steer(text), followUp: (text) => created.followUp(text) });
|
|
1410
|
-
const actualReadonlyModel = shared.readonlyExpected && getReadonlyChildModels(created)?.current;
|
|
1411
|
-
if (shared.readonlyExpected && (!actualReadonlyModel || actualReadonlyModel.fullId !== shared.readonlyModel
|
|
1412
|
-
|| actualReadonlyModel.api !== shared.readonlyExpected.api || created.modelId !== shared.readonlyModel || abortedBySignal || interruptedByControl || result.timedOut
|
|
1413
|
-
|| !shared.readonlyHandoffAllowed?.())) throw new Error("Read-only continuation handoff vetoed.");
|
|
1414
1404
|
await created.prompt(`Task: ${task}`);
|
|
1415
1405
|
settle(undefined);
|
|
1416
1406
|
} catch (error) {
|
|
@@ -1509,7 +1499,8 @@ async function runSingleAttempt(
|
|
|
1509
1499
|
tokens: progress.tokens,
|
|
1510
1500
|
durationMs: progress.durationMs,
|
|
1511
1501
|
};
|
|
1512
|
-
const
|
|
1502
|
+
const remoteGitChanged = result.nativeMachine?.initialGit && result.nativeMachine.finalGit ? result.nativeMachine.initialGit.head !== result.nativeMachine.finalGit.head || result.nativeMachine.initialGit.dirty !== result.nativeMachine.finalGit.dirty : undefined;
|
|
1503
|
+
const mutationEvidence = result.nativeMachine ? { source: "tracked-files" as const, trackedOnly: true as const, changedFiles: [], attemptedMutation: remoteGitChanged === true, ...(remoteGitChanged === undefined ? { unavailable: "Remote Git before/after evidence was incomplete." } : {}) } : collectTrackedMutationEvidence(mutationSnapshot, options.cwd ?? runtimeCwd);
|
|
1513
1504
|
|
|
1514
1505
|
const acceptanceOutput = getFinalOutput(result.messages ?? []);
|
|
1515
1506
|
let fullOutput = stripAcceptanceReport(acceptanceOutput);
|
|
@@ -1804,9 +1795,8 @@ async function runSyncCompletionInner(
|
|
|
1804
1795
|
}
|
|
1805
1796
|
const systemPrompt = buildEffectiveSystemPrompt({ agent, resolvedSkills, cwd: skillCwd, ...(options.outputPath ? { outputPath: options.outputPath } : {}) });
|
|
1806
1797
|
|
|
1807
|
-
const
|
|
1798
|
+
const { model: selectedModel, requestedModel } = resolveModelSelection(
|
|
1808
1799
|
options.modelOverride ?? agent.model,
|
|
1809
|
-
agent.fallbackModels,
|
|
1810
1800
|
options.availableModels,
|
|
1811
1801
|
agent.modelProvider ?? options.preferredModelProvider,
|
|
1812
1802
|
{
|
|
@@ -1815,23 +1805,9 @@ async function runSyncCompletionInner(
|
|
|
1815
1805
|
origin: options.modelOrigin ?? (options.modelOverrideFromParent ? "inherited" : "configured"),
|
|
1816
1806
|
},
|
|
1817
1807
|
);
|
|
1818
|
-
if (options.workflowChildPermitLaunch && candidates.length > 1) {
|
|
1819
|
-
const error = "Workflow child permit does not support model fallback.";
|
|
1820
|
-
return redactResultPrompt(withRunContext({
|
|
1821
|
-
index: options.index ?? 0,
|
|
1822
|
-
agent: agent.name,
|
|
1823
|
-
task,
|
|
1824
|
-
exitCode: 1,
|
|
1825
|
-
messages: [],
|
|
1826
|
-
usage: emptyUsage(),
|
|
1827
|
-
error,
|
|
1828
|
-
}, options.context));
|
|
1829
|
-
}
|
|
1830
1808
|
try {
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
assertThinkingWithinCeiling({ model, configThinking: options.thinkingOverride ?? agent.thinking, ceiling: options.thinkingCeiling, agent: agent.name, runId: options.runId });
|
|
1834
|
-
}
|
|
1809
|
+
const model = applyThinkingSuffix(selectedModel, options.thinkingOverride ?? agent.thinking, options.thinkingOverride !== undefined);
|
|
1810
|
+
assertThinkingWithinCeiling({ model, configThinking: options.thinkingOverride ?? agent.thinking, ceiling: options.thinkingCeiling, agent: agent.name, runId: options.runId });
|
|
1835
1811
|
} catch (error) {
|
|
1836
1812
|
return redactResultPrompt(withRunContext({
|
|
1837
1813
|
index: options.index ?? 0,
|
|
@@ -1843,8 +1819,6 @@ async function runSyncCompletionInner(
|
|
|
1843
1819
|
error: error instanceof Error ? error.message : String(error),
|
|
1844
1820
|
}, options.context));
|
|
1845
1821
|
}
|
|
1846
|
-
const attemptedModels: string[] = [];
|
|
1847
|
-
const modelAttempts: ModelAttempt[] = [];
|
|
1848
1822
|
const aggregateUsage = emptyUsage();
|
|
1849
1823
|
const attemptNotes: string[] = [];
|
|
1850
1824
|
const launchWarnings = { emitted: false };
|
|
@@ -1895,10 +1869,11 @@ async function runSyncCompletionInner(
|
|
|
1895
1869
|
});
|
|
1896
1870
|
};
|
|
1897
1871
|
|
|
1898
|
-
let intercomDetached = false;
|
|
1899
1872
|
let detachedReason: string | undefined;
|
|
1873
|
+
const logicalDeadline = options.deadlineAt ?? (options.timeoutMs === undefined ? undefined : Date.now() + options.timeoutMs);
|
|
1900
1874
|
const attemptOptions: RunSyncOptions = {
|
|
1901
1875
|
...options,
|
|
1876
|
+
deadlineAt: logicalDeadline,
|
|
1902
1877
|
onDetachReceipt: (receipt) => {
|
|
1903
1878
|
receipt.acceptance = buildPendingAcceptanceLedger(effectiveAcceptance);
|
|
1904
1879
|
try {
|
|
@@ -1909,165 +1884,81 @@ async function runSyncCompletionInner(
|
|
|
1909
1884
|
const accepted = options.onDetachReceipt?.(receipt) === true;
|
|
1910
1885
|
if (accepted) {
|
|
1911
1886
|
detachedReason = receipt.detachedReason;
|
|
1912
|
-
if (receipt.detachedReason === "intercom coordination") intercomDetached = true;
|
|
1913
1887
|
}
|
|
1914
1888
|
return accepted;
|
|
1915
1889
|
},
|
|
1916
1890
|
};
|
|
1891
|
+
const candidate = selectedModel;
|
|
1892
|
+
const verifyModel = Boolean(candidate) && !options.modelOverrideFromParent;
|
|
1917
1893
|
let lastResult: SingleResult | undefined;
|
|
1918
|
-
|
|
1919
|
-
let
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
usage: { ...result.usage },
|
|
1979
|
-
};
|
|
1980
|
-
modelAttempts.push(attempt);
|
|
1981
|
-
// A consumed retained continuation is terminal even on a startup error or abort.
|
|
1982
|
-
if (recoveryState === "readonly-continuation") break modelAttemptsLoop;
|
|
1983
|
-
const source = settledReadonlySource.get(result);
|
|
1984
|
-
const evidence = source && getReadonlySessionEvidence(source);
|
|
1985
|
-
const models = source && getReadonlyChildModels(source);
|
|
1986
|
-
// Deny non-text retained inputs, including unknown blocks. Count bytes once,
|
|
1987
|
-
// not restored usage (the latter belongs to earlier attempts/runs).
|
|
1988
|
-
const textOnly = evidence && (JSON.parse(evidence.contextJson) as Array<{ role: string; content: unknown }>).every((message) =>
|
|
1989
|
-
typeof message.content === "string" || Array.isArray(message.content) && message.content.every((block) =>
|
|
1990
|
-
block?.type === "text" || message.role === "assistant" && block?.type === "toolCall"));
|
|
1991
|
-
const retainedBytes = evidence && models ? Buffer.byteLength(evidence.contextJson) + models.requestBytes : Infinity;
|
|
1992
|
-
const resolvedCandidates = modelsToTry.map((reference, index) => index === modelIndex ? models?.current : reference ? models?.resolve(reference) : undefined);
|
|
1993
|
-
const continuation = planReadonlyModelContinuation({
|
|
1994
|
-
source, recoveryState, currentIndex: modelIndex,
|
|
1995
|
-
candidates: resolvedCandidates.map((resolved, index) => {
|
|
1996
|
-
// A conservative byte ceiling includes serialized history, actual system
|
|
1997
|
-
// prompt/tools, framing/continuation headroom and full output allowance.
|
|
1998
|
-
const required = resolved?.maxTokens ? retainedBytes + 4096 + resolved.maxTokens : Infinity;
|
|
1999
|
-
return {
|
|
2000
|
-
resolved: resolved?.api ? { provider: resolved.provider, model: resolved.id, api: resolved.api } : undefined,
|
|
2001
|
-
tried: index <= modelIndex,
|
|
2002
|
-
compatibility: textOnly && resolved?.input?.includes("text") && (resolved.contextWindow ?? 0) >= required ? "compatible" as const : "unknown" as const,
|
|
2003
|
-
};
|
|
2004
|
-
}),
|
|
2005
|
-
lifecycleAllowsContinuation: !attemptSucceeded && readonlyHandoffAllowed() && !result.stopped && !result.detached && !result.interrupted && !result.timedOut,
|
|
2006
|
-
effectsAllowContinuation: !result.structuredOutputFailed && !result.toolBudgetBlocked && !result.progress?.currentTool
|
|
2007
|
-
&& !result.outputSaveError && (!result.effects?.fileMutation || result.effects.fileMutation.status === "not-applicable"),
|
|
2008
|
-
budget: options.toolBudget ? "tool-budget-configured" : options.usageBudget ? "unknown" : "unconfigured",
|
|
2009
|
-
knownContextOverflow: Boolean(result.contextOverflow || isContextOverflow(result.error)),
|
|
2010
|
-
});
|
|
2011
|
-
if (continuation.kind === "continue") {
|
|
2012
|
-
recoveryState = continuation.recoveryState; // consume BEFORE any sibling creation
|
|
2013
|
-
readonlyExpected = continuation.expected;
|
|
2014
|
-
readonlySource = source;
|
|
2015
|
-
readonlyModel = resolvedCandidates[continuation.candidateIndex]?.fullId;
|
|
2016
|
-
nextAttemptTask = continuation.prompt;
|
|
2017
|
-
attemptNotes.push(`[readonly-continuation] ${attempt.model} failed with HTTP 429 after read-only progress; continuing retained session once with ${readonlyModel}.`);
|
|
2018
|
-
modelIndex = continuation.candidateIndex - 1;
|
|
2019
|
-
continue modelAttemptsLoop;
|
|
2020
|
-
}
|
|
2021
|
-
if (!attemptSucceeded) {
|
|
2022
|
-
const afterCompactionSettlement = (result as AbortRecoverySingleResult)[AFTER_COMPACTION_SETTLEMENT];
|
|
2023
|
-
const abortRecovery = planAbortRecovery({
|
|
2024
|
-
messages: result.messages ?? [],
|
|
2025
|
-
error: result.error,
|
|
2026
|
-
processSignal: result.processSignal,
|
|
2027
|
-
sessionAvailable: Boolean(options.sessionFile && existsSync(options.sessionFile)),
|
|
2028
|
-
alreadyResumed: recoveryState !== "unused",
|
|
2029
|
-
stopped: result.stopped || result.detached || options.signal?.aborted,
|
|
2030
|
-
interrupted: result.interrupted || intercomDetached || options.interruptSignal?.aborted,
|
|
2031
|
-
timedOut: result.timedOut,
|
|
2032
|
-
toolBudgetExhausted: result.toolBudgetBlocked,
|
|
2033
|
-
usageBudgetExhausted: false,
|
|
2034
|
-
structuredOutputFailed: result.structuredOutputFailed,
|
|
2035
|
-
acceptanceFailed: false,
|
|
2036
|
-
currentTool: result.progress?.currentTool,
|
|
2037
|
-
afterCompactionSettlement,
|
|
2038
|
-
});
|
|
2039
|
-
if (abortRecovery.action === "resume") {
|
|
2040
|
-
recoveryState = "abort-recovery";
|
|
2041
|
-
nextAttemptTask = abortRecovery.prompt;
|
|
2042
|
-
attemptNotes.push("[abort-recovery] provider/transport abort after useful progress; resuming the retained child session once.");
|
|
2043
|
-
continue;
|
|
2044
|
-
}
|
|
2045
|
-
if (abortRecovery.diagnostic) {
|
|
2046
|
-
result.error = result.error ? `${result.error}\n${abortRecovery.diagnostic}` : abortRecovery.diagnostic;
|
|
2047
|
-
attempt.error = result.error;
|
|
2048
|
-
break modelAttemptsLoop;
|
|
2049
|
-
}
|
|
2050
|
-
}
|
|
2051
|
-
if (recoveringAbort && !attemptSucceeded) break modelAttemptsLoop;
|
|
2052
|
-
if (options.workflowChildPermitLaunch && !attemptSucceeded) break modelAttemptsLoop;
|
|
2053
|
-
// Preserve the legacy intercom handoff contract: once this logical run has
|
|
2054
|
-
// been handed to a supervisor, terminating that attempt must not launch a
|
|
2055
|
-
// model fallback. Explicit user detach retains fallback.
|
|
2056
|
-
if (intercomDetached || result.timedOut) break modelAttemptsLoop;
|
|
2057
|
-
if (attemptSucceeded) break modelAttemptsLoop;
|
|
2058
|
-
|
|
2059
|
-
const retryableModelFailure = isRetryableModelFailureAttempt({ error: result.error, messages: result.messages, toolCount: result.progressSummary?.toolCount });
|
|
2060
|
-
if (retryableModelFailure) recordRetryableModelFailure(result.model ?? candidate, result.error);
|
|
2061
|
-
if (isContextOverflow(result.error)) {
|
|
2062
|
-
result.contextOverflow = true;
|
|
2063
|
-
attemptNotes.push(`[fallback] ${attempt.model} failed: context overflow — the input exceeds this model's context window. Reduce the task input or use a model with a larger context window.`);
|
|
2064
|
-
break modelAttemptsLoop;
|
|
2065
|
-
}
|
|
2066
|
-
if (!retryableModelFailure || modelIndex === modelsToTry.length - 1) break modelAttemptsLoop;
|
|
2067
|
-
attemptNotes.push(formatModelAttemptNote(attempt, modelsToTry[modelIndex + 1]));
|
|
2068
|
-
break;
|
|
1894
|
+
let recoveryPrompt = task;
|
|
1895
|
+
let stagedIndexBaseline: string | undefined;
|
|
1896
|
+
if (effectiveAcceptance.preserveStagedIndex) {
|
|
1897
|
+
try {
|
|
1898
|
+
stagedIndexBaseline = captureStagedIndexBaseline(options.cwd ?? runtimeCwd);
|
|
1899
|
+
} catch (error) {
|
|
1900
|
+
return redactResultPrompt(withRunContext({
|
|
1901
|
+
index: options.index ?? 0,
|
|
1902
|
+
agent: agentName,
|
|
1903
|
+
task,
|
|
1904
|
+
exitCode: 1,
|
|
1905
|
+
messages: [],
|
|
1906
|
+
usage: emptyUsage(),
|
|
1907
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1908
|
+
}, options.context));
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
for (let attemptIndex = 0; attemptIndex < 2; attemptIndex++) {
|
|
1912
|
+
const outputSnapshot = captureSingleOutputSnapshot(options.outputPath);
|
|
1913
|
+
const attemptResult = await runSingleAttempt(runtimeCwd, agent, recoveryPrompt, candidate, attemptOptions, {
|
|
1914
|
+
sessionEnabled,
|
|
1915
|
+
systemPrompt,
|
|
1916
|
+
acceptancePrompt,
|
|
1917
|
+
resolvedSkillNames: resolvedSkills.length > 0 ? resolvedSkills.map((skill) => skill.name) : undefined,
|
|
1918
|
+
skillsWarning: missingSkills.length > 0 ? `Skills not found: ${missingSkills.join(", ")}` : undefined,
|
|
1919
|
+
jsonlPath,
|
|
1920
|
+
artifactPaths: artifactPathsResult,
|
|
1921
|
+
transcriptWriter,
|
|
1922
|
+
attemptNotes,
|
|
1923
|
+
outputSnapshot,
|
|
1924
|
+
originalTask: task,
|
|
1925
|
+
orcaProgressTab,
|
|
1926
|
+
launchWarnings,
|
|
1927
|
+
verifyModel,
|
|
1928
|
+
});
|
|
1929
|
+
lastResult = attemptResult;
|
|
1930
|
+
sumUsage(aggregateUsage, attemptResult.usage);
|
|
1931
|
+
totalToolCount += attemptResult.progressSummary?.toolCount ?? 0;
|
|
1932
|
+
totalDurationMs += attemptResult.progressSummary?.durationMs ?? 0;
|
|
1933
|
+
if (attemptResult.exitCode === 0 && !attemptResult.error) break;
|
|
1934
|
+
const recovery = planAbortRecovery({
|
|
1935
|
+
messages: attemptResult.messages ?? [],
|
|
1936
|
+
error: attemptResult.error,
|
|
1937
|
+
processSignal: attemptResult.processSignal,
|
|
1938
|
+
sessionAvailable: Boolean(options.sessionFile && existsSync(options.sessionFile)),
|
|
1939
|
+
alreadyResumed: attemptIndex > 0,
|
|
1940
|
+
stopped: attemptResult.stopped || attemptResult.detached || Boolean(detachedReason) || Boolean(options.workflowChildPermitLaunch) || options.signal?.aborted,
|
|
1941
|
+
interrupted: attemptResult.interrupted || options.interruptSignal?.aborted,
|
|
1942
|
+
timedOut: attemptResult.timedOut,
|
|
1943
|
+
toolBudgetExhausted: attemptResult.toolBudgetBlocked,
|
|
1944
|
+
usageBudgetExhausted: false,
|
|
1945
|
+
structuredOutputFailed: attemptResult.structuredOutputFailed,
|
|
1946
|
+
acceptanceFailed: false,
|
|
1947
|
+
currentTool: attemptResult.progress?.currentTool,
|
|
1948
|
+
afterCompactionSettlement: (attemptResult as AbortRecoverySingleResult)[AFTER_COMPACTION_SETTLEMENT],
|
|
1949
|
+
});
|
|
1950
|
+
if (recovery.action === "resume") {
|
|
1951
|
+
recoveryPrompt = recovery.prompt;
|
|
1952
|
+
attemptNotes.push("[abort-recovery] compaction abort after useful progress; resuming the retained child session once on the same model.");
|
|
1953
|
+
continue;
|
|
2069
1954
|
}
|
|
1955
|
+
if (recovery.diagnostic) {
|
|
1956
|
+
attemptResult.error = attemptResult.error ? `${attemptResult.error}\n${recovery.diagnostic}` : recovery.diagnostic;
|
|
1957
|
+
}
|
|
1958
|
+
break;
|
|
2070
1959
|
}
|
|
1960
|
+
if (!lastResult) throw new Error("Subagent did not produce a result.");
|
|
1961
|
+
if (isContextOverflow(lastResult.error)) lastResult.contextOverflow = true;
|
|
2071
1962
|
|
|
2072
1963
|
const result = withRunContext(lastResult ?? {
|
|
2073
1964
|
index: options.index ?? 0,
|
|
@@ -2081,8 +1972,7 @@ async function runSyncCompletionInner(
|
|
|
2081
1972
|
result.task = task;
|
|
2082
1973
|
|
|
2083
1974
|
result.usage = aggregateUsage;
|
|
2084
|
-
result.
|
|
2085
|
-
result.modelAttempts = modelAttempts.length > 0 ? modelAttempts : undefined;
|
|
1975
|
+
result.requestedModel = requestedModel;
|
|
2086
1976
|
result.progressSummary = {
|
|
2087
1977
|
...(childSessionName ? { sessionName: childSessionName } : {}),
|
|
2088
1978
|
toolCount: totalToolCount,
|
|
@@ -2148,6 +2038,7 @@ async function runSyncCompletionInner(
|
|
|
2148
2038
|
? { content: childWrittenOutput, path: options.outputPath, authoritative: options.outputMode === "file-only", durable: result.savedOutputPath !== undefined }
|
|
2149
2039
|
: undefined,
|
|
2150
2040
|
cwd: options.cwd ?? runtimeCwd,
|
|
2041
|
+
stagedIndexBaseline,
|
|
2151
2042
|
reportOptional: isAgentContract(options.agentContract),
|
|
2152
2043
|
artifactsDir: options.artifactsDir,
|
|
2153
2044
|
runId: options.runId,
|
|
@@ -2160,6 +2051,12 @@ async function runSyncCompletionInner(
|
|
|
2160
2051
|
}
|
|
2161
2052
|
const acceptanceFailure = acceptanceFailureMessage(result.acceptance);
|
|
2162
2053
|
stripAcceptanceReportsFromMessages(result.messages);
|
|
2054
|
+
// A passing typed gate supplies the structured output for runs that have no
|
|
2055
|
+
// outputSchema of their own; preflight rejects the combination.
|
|
2056
|
+
const typedGate = typedVerifyOutput(result.acceptance);
|
|
2057
|
+
if (typedGate && result.structuredOutput === undefined && !acceptanceFailure && result.exitCode === 0) {
|
|
2058
|
+
result.structuredOutput = typedGate.value;
|
|
2059
|
+
}
|
|
2163
2060
|
if (acceptanceFailure && result.acceptance.explicit && result.exitCode === 0 && !result.interrupted && !result.timedOut && !isAgentContract(options.agentContract)) {
|
|
2164
2061
|
result.exitCode = 1;
|
|
2165
2062
|
if (result.savedOutputPath) {
|
|
@@ -2172,7 +2069,6 @@ async function runSyncCompletionInner(
|
|
|
2172
2069
|
savedPath: result.savedOutputPath,
|
|
2173
2070
|
outputReference: result.outputReference,
|
|
2174
2071
|
}).displayOutput;
|
|
2175
|
-
artifactOutputByResult.set(result, result.finalOutput);
|
|
2176
2072
|
}
|
|
2177
2073
|
result.error = result.error ? `${result.error}\n${acceptanceFailure}` : acceptanceFailure;
|
|
2178
2074
|
if (artifactPathsResult && options.artifactConfig?.enabled !== false && options.artifactConfig?.includeOutput !== false) {
|
|
@@ -58,6 +58,7 @@ function compactChild(child: ForegroundResumeChild): ForegroundResumeChild {
|
|
|
58
58
|
...(child.resumeContract ? { resumeContract: child.resumeContract } : {}),
|
|
59
59
|
...(child.launchContractDigest ? { launchContractDigest: child.launchContractDigest } : {}),
|
|
60
60
|
...(child.extensionBindings ? { extensionBindings: child.extensionBindings } : {}),
|
|
61
|
+
...(child.requiredExtensions ? { requiredExtensions: child.requiredExtensions } : {}),
|
|
61
62
|
...(child.capabilityCeiling ? { capabilityCeiling: child.capabilityCeiling } : {}),
|
|
62
63
|
...(child.updatedAt !== undefined ? { updatedAt: child.updatedAt } : {}),
|
|
63
64
|
};
|
|
@@ -78,7 +79,7 @@ function isRestorableResumeContract(value: unknown): boolean {
|
|
|
78
79
|
}
|
|
79
80
|
const contract = value as NonNullable<ForegroundResumeChild["resumeContract"]>;
|
|
80
81
|
if (Object.keys(contract).some((key) => !["modelResponseAliases", "outputSchema", "agentContract", "acceptance", "output", "outputMode"].includes(key))) return false;
|
|
81
|
-
if (contract.outputSchema !== undefined && (!contract.outputSchema || typeof contract.outputSchema !== "object" || Array.isArray(contract.outputSchema))) return false;
|
|
82
|
+
if (contract.outputSchema !== undefined && contract.outputSchema !== false && (!contract.outputSchema || typeof contract.outputSchema !== "object" || Array.isArray(contract.outputSchema))) return false;
|
|
82
83
|
if (contract.agentContract !== undefined && (!contract.agentContract || typeof contract.agentContract !== "object" || Array.isArray(contract.agentContract) || contract.agentContract.version !== 1)) return false;
|
|
83
84
|
if (validateAcceptanceInput(contract.acceptance).length > 0) return false;
|
|
84
85
|
if (contract.output !== undefined && typeof contract.output !== "string" && typeof contract.output !== "boolean") return false;
|