@sema-agent/core 7.5.2 → 7.6.1
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 +67 -0
- package/dist/agents/agent-transcript-tool.d.ts +2 -2
- package/dist/agents/cascade.d.ts +4 -5
- package/dist/agents/cascade.js +12 -10
- package/dist/agents/repair-loop.d.ts +7 -5
- package/dist/agents/repair-loop.js +13 -15
- package/dist/agents/retain-ledger.d.ts +2 -3
- package/dist/agents/send-message-tool.d.ts +2 -2
- package/dist/agents/session-util.d.ts +2 -2
- package/dist/agents/subagent.d.ts +27 -46
- package/dist/agents/subagent.js +119 -105
- package/dist/agents/suspend-guard.d.ts +31 -19
- package/dist/agents/suspend-guard.js +14 -8
- package/dist/agents/teacher.d.ts +2 -2
- package/dist/agents/teacher.js +9 -9
- package/dist/agents/team.d.ts +6 -5
- package/dist/agents/team.js +10 -8
- package/dist/agents/verify.d.ts +8 -9
- package/dist/agents/verify.js +17 -17
- package/dist/core/a2a.js +2 -1
- package/dist/core/agent-definition.d.ts +172 -0
- package/dist/core/agent-definition.js +1 -0
- package/dist/core/ask-origin.d.ts +60 -7
- package/dist/core/ask-origin.js +26 -1
- package/dist/core/checkpoint-store.d.ts +78 -76
- package/dist/core/checkpoint-store.js +17 -1
- package/dist/core/delegation-frames.d.ts +298 -0
- package/dist/core/delegation-frames.js +21 -0
- package/dist/core/engine-notice.d.ts +555 -0
- package/dist/core/engine-notice.js +55 -0
- package/dist/core/gate-fold.d.ts +12 -0
- package/dist/core/gate-fold.js +158 -0
- package/dist/core/gate-lanes.d.ts +93 -0
- package/dist/core/gate-lanes.js +626 -0
- package/dist/core/gate-outcome.d.ts +189 -0
- package/dist/core/gate-outcome.js +70 -0
- package/dist/core/hands-band.d.ts +134 -0
- package/dist/core/hands-band.js +1 -0
- package/dist/core/hooks.d.ts +22 -177
- package/dist/core/hooks.js +53 -851
- package/dist/core/mcp-failure.d.ts +142 -0
- package/dist/core/mcp-failure.js +145 -0
- package/dist/core/mcp-server-spec.d.ts +217 -0
- package/dist/core/mcp-server-spec.js +1 -0
- package/dist/core/mcp.d.ts +21 -77
- package/dist/core/mcp.js +76 -150
- package/dist/core/model-seat.d.ts +99 -0
- package/dist/core/model-seat.js +1 -0
- package/dist/core/pause-registry.d.ts +131 -0
- package/dist/core/pause-registry.js +27 -0
- package/dist/core/reminder-mint.d.ts +10 -0
- package/dist/core/reminder-mint.js +3 -0
- package/dist/core/runner/assemble-result.d.ts +32 -41
- package/dist/core/runner/assemble-result.js +55 -74
- package/dist/core/runner/contracts.d.ts +427 -69
- package/dist/core/runner/denial-limit-arms.d.ts +1 -1
- package/dist/core/runner/denial-limit-arms.js +3 -3
- package/dist/core/runner/gate-exit.d.ts +242 -0
- package/dist/core/runner/gate-exit.js +124 -0
- package/dist/core/runner/park-commit.d.ts +17 -23
- package/dist/core/runner/park-commit.js +14 -15
- package/dist/core/runner/prepare-ask-lane.d.ts +0 -3
- package/dist/core/runner/prepare-ask-lane.js +3 -5
- package/dist/core/runner/prepare-boundary-parks.d.ts +3 -6
- package/dist/core/runner/prepare-boundary-parks.js +3 -3
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +2 -7
- package/dist/core/runner/prepare-caps-and-workflow.js +1 -1
- package/dist/core/runner/prepare-delegation-surface.d.ts +2 -7
- package/dist/core/runner/prepare-gate-stations.d.ts +4 -7
- package/dist/core/runner/prepare-gate-stations.js +29 -54
- package/dist/core/runner/prepare-inherited-gate.js +1 -1
- package/dist/core/runner/prepare-memory.d.ts +44 -26
- package/dist/core/runner/prepare-park-ask.d.ts +2 -4
- package/dist/core/runner/prepare-park-ask.js +5 -5
- package/dist/core/runner/prepare-task.d.ts +2 -2
- package/dist/core/runner/prepare-task.js +8 -9
- package/dist/core/runner/prepare-wiring-manifest.d.ts +7 -15
- package/dist/core/runner/prepare-wiring-manifest.js +9 -10
- package/dist/core/runner/runtask.d.ts +20 -102
- package/dist/core/runner/runtask.js +119 -121
- package/dist/core/runner/terminal-projection.d.ts +22 -0
- package/dist/core/runner/terminal-projection.js +28 -0
- package/dist/core/runner-deps.d.ts +1416 -0
- package/dist/core/runner-deps.js +1 -0
- package/dist/core/runtime-caps.d.ts +164 -0
- package/dist/core/runtime-caps.js +1 -0
- package/dist/core/store-contracts/checkpoint-store-contract.d.ts +4 -1
- package/dist/core/store-contracts/checkpoint-store-contract.js +8 -2
- package/dist/core/task-event.d.ts +910 -0
- package/dist/core/task-event.js +1 -0
- package/dist/core/task-limits.d.ts +110 -0
- package/dist/core/task-limits.js +1 -0
- package/dist/core/task-result.d.ts +809 -0
- package/dist/core/task-result.js +1 -0
- package/dist/core/task-spec.d.ts +1370 -0
- package/dist/core/task-spec.js +1 -0
- package/dist/core/task-stream.d.ts +382 -0
- package/dist/core/task-stream.js +1 -0
- package/dist/core/terminal-cause.d.ts +137 -0
- package/dist/core/terminal-cause.js +9 -0
- package/dist/core/tool-policy.d.ts +43 -139
- package/dist/core/tool-policy.js +79 -112
- package/dist/core/tool-spec.d.ts +1174 -0
- package/dist/core/tool-spec.js +1 -0
- package/dist/core/types.d.ts +27 -7789
- package/dist/core/types.js +2 -76
- package/dist/core/warm-resume.d.ts +2 -2
- package/dist/core/wiring-manifest.d.ts +6 -3
- package/dist/core/workflow-journal-store.js +3 -4
- package/dist/engine/harness/agent-harness.d.ts +1 -1
- package/dist/index.d.ts +12 -8
- package/dist/index.js +9 -6
- package/dist/orchestration/builtin-workflows.d.ts +2 -2
- package/dist/orchestration/builtin-workflows.js +1 -1
- package/dist/orchestration/goal.d.ts +2 -2
- package/dist/orchestration/goal.js +8 -7
- package/dist/orchestration/run-spec.d.ts +2 -2
- package/dist/orchestration/run-spec.js +5 -3
- package/dist/orchestration/run-workflow-tool.d.ts +4 -4
- package/dist/orchestration/run-workflow-tool.js +4 -4
- package/dist/orchestration/workflow-governance.d.ts +4 -4
- package/dist/orchestration/workflow-governance.js +4 -2
- package/dist/orchestration/workflow-primitives.d.ts +1 -1
- package/dist/orchestration/workflow-primitives.js +1 -1
- package/dist/orchestration/workflow.d.ts +15 -4
- package/dist/orchestration/workflow.js +64 -39
- package/dist/prompts/supervisor.d.ts +1 -1
- package/dist/prompts/supervisor.js +3 -3
- package/dist/scenarios/scenario-registry.d.ts +3 -3
- package/dist/scenarios/scenario-registry.js +1 -1
- package/dist/scenarios/teacher-quickstart.d.ts +2 -2
- package/dist/server/http.d.ts +2 -2
- package/dist/stores/file/fs-atomic.d.ts +88 -12
- package/dist/stores/file/fs-atomic.js +184 -55
- package/dist/stores/file/index.d.ts +1 -0
- package/dist/stores/file/index.js +1 -0
- package/package.json +3 -1
- package/test/export-surface.snapshot.json +82 -22
|
@@ -6,7 +6,7 @@ import { deliverDelegationLifecycle, deliverEngineNotice, undrainedUserInputNoti
|
|
|
6
6
|
import { planRejectionClears, resolveTriggerWindow } from "../context-edit.js";
|
|
7
7
|
import { AgentHarness, DEFAULT_COMPACTION_SETTINGS, isSyntheticApiErrorMessage, uuidv7 } from "../../internal/harness.js";
|
|
8
8
|
import { snapshotActorAssertion } from "../../internal/llm.js";
|
|
9
|
-
import { CheckpointError, BINDING_CHECKPOINT_VERSION, checkpointVersionOf, F012_CHECKPOINT_VERSION, MAX_SUPPORTED_CHECKPOINT_VERSION, REAL_APPROVAL_CHECKPOINT_VERSION, FACE_CHECKPOINT_VERSION, remainingBudgetMicroUsd, readPendingSteerQueue, remainingTokens, LEGACY_PENDING_STEER_INPUT_ID, MAX_STEER_INPUT_ID_CHARS, validatePendingSteer, winnerFromOutcome, } from "../checkpoint-store.js";
|
|
9
|
+
import { CheckpointError, BINDING_CHECKPOINT_VERSION, checkpointVersionOf, F012_CHECKPOINT_VERSION, MAX_SUPPORTED_CHECKPOINT_VERSION, REAL_APPROVAL_CHECKPOINT_VERSION, FACE_CHECKPOINT_VERSION, remainingBudgetMicroUsd, readPendingSteerQueue, remainingTokens, resolveCheckpointStore, LEGACY_PENDING_STEER_INPUT_ID, MAX_STEER_INPUT_ID_CHARS, validatePendingSteer, winnerFromOutcome, realApprovalOrgFact } from "../checkpoint-store.js";
|
|
10
10
|
import { GIT_STATUS_ECHO_PREVIEW, branchCarriesVisiblePositiveGitFrame, newestEngineGitFrame, stripGitStatusUnits } from "./git-status-frame.js";
|
|
11
11
|
import { gitFrameContextVisible, normalizeGitAnnouncement } from "../../internal/harness.js";
|
|
12
12
|
import { engineVersion } from "../version.js";
|
|
@@ -35,9 +35,12 @@ import { toImageContent } from "./image.js";
|
|
|
35
35
|
import { OUTPUT_TOOL_NAME, SKILLS_LISTING_PROBE_HEADER, resolveOutputRetries } from "./synthetic-tools.js";
|
|
36
36
|
import { cacheFamilyOf, usageCostMicroUsd } from "./usage-accounting.js";
|
|
37
37
|
import { assembleResult, errorCodeOf } from "./assemble-result.js";
|
|
38
|
+
import { amendTerminal, terminalProjection } from "./terminal-projection.js";
|
|
38
39
|
import { ATTACHMENT_BYTE_CAP, CHANGED_FILES_MAX, TOTAL_TOKENS_REMINDER_DEFAULT_MODE, AGENT_LISTING_REMOVED_HEADER, SKILLS_LISTING_DELTA_HEADER, SKILLS_LISTING_REMOVED_HEADER, advanceCadenceClock, agentListingDeltaHeader, attachmentEnvelopeTags, agentListingInitialHeader, replayAnnouncedListing, replayAnnouncedModels, clipToBytes, collectDateChange, collectDueAttachments, collectInstructionsChange, commitAgentListing, commitInstructionsChange, commitSkillsListing, createAttachmentState, rebaseCadenceWindows, reduceToolEnd, renderAgentListingDelta, renderMcpDroppedTools, renderMcpInstructionsDelta, renderOrphanedBackgroundTasks, selectMcpDroppedBatch, renderSkillsListingDelta, renderToolsDelta, stampWriteAnchor } from "./turn-attachments.js";
|
|
39
40
|
import { buildWorkingFileAttachments, centerAdoptionOption, contextInstructionFilesOption, emitInputTruncated, forkContextOption } from "./compaction-call-options.js";
|
|
40
|
-
import { effectiveDelegationFacts,
|
|
41
|
+
import { effectiveDelegationFacts, prepareTask } from "./prepare-task.js";
|
|
42
|
+
import { gatedCallIdOf } from "./park-commit.js";
|
|
43
|
+
import { placementValueOrAbsent } from "./checkpoint-scope.js";
|
|
41
44
|
import { SessionReadFileStates } from "./prepare-hands-readface.js";
|
|
42
45
|
import { settleTeardownLeg } from "./teardown-bounded.js";
|
|
43
46
|
import { TOOL_SEARCH_NAME } from "./tool-disclosure.js";
|
|
@@ -51,7 +54,11 @@ import { droppedToolDigest, withAnnounceDigests } from "./announce-once-ledger.j
|
|
|
51
54
|
import { appendInterruptionMarker, reconcileInterruptedSession } from "../session-reconcile.js";
|
|
52
55
|
import { RunnerSharedToolResultStore } from "../tool-result-store.js";
|
|
53
56
|
import { formatDiagnosticsBlock } from "../lsp-diagnostics.js";
|
|
54
|
-
import {
|
|
57
|
+
import { isAskOrigin } from "../ask-origin.js";
|
|
58
|
+
import { isGateKind, PAUSE_REGISTRY, resumeGateMatches } from "../pause-registry.js";
|
|
59
|
+
import { checkToolPolicyProjection, constraintChainDigest, constraintChainEntryOfSuppliedLayer, refuseOutOfContractDecision, screenApproverAttribution, toolPolicyNameSets } from "../tool-policy.js";
|
|
60
|
+
import { mintGateOutcome } from "./gate-exit.js";
|
|
61
|
+
import { MCP_DELIVERY_VERDICTS } from "../mcp-failure.js";
|
|
55
62
|
import { defaultTaskRegistry } from "../task-registry.js";
|
|
56
63
|
import { discloseDroppedPending, isDelegatedAgentTerminal, isSystemInjectionPriority, isTerminalTaskNotification, PendingSessionNotifications, renderTaskNotificationXml, SYSTEM_INJECTION_PRIORITIES, SystemInjectionQueue, taskNotificationDedupKey } from "../task-notification.js";
|
|
57
64
|
import { ToolDetachHub } from "../tool-detach.js";
|
|
@@ -147,23 +154,23 @@ function resumeDecisionWasNegative(resume) {
|
|
|
147
154
|
}
|
|
148
155
|
const DEFERRED_REISSUE = "[DEFERRED] This tool call shared a batch with a call that suspended for durable approval, so it was " +
|
|
149
156
|
"NOT executed on resume. If you still need it, issue it again now.";
|
|
150
|
-
function toolEndBodyFrom(result, isError,
|
|
157
|
+
function toolEndBodyFrom(result, isError, gate, gatedCallIdOfRun) {
|
|
151
158
|
const o = toolOutputFrom(result);
|
|
152
159
|
const st = structuredFrom(result);
|
|
153
160
|
const det = isError ? result?.details : undefined;
|
|
154
161
|
const codeRaw = det?.code;
|
|
155
162
|
const kindRaw = det?.errorKind;
|
|
156
163
|
const code = typeof codeRaw === "string" ? codeRaw : typeof kindRaw === "string" ? kindRaw : undefined;
|
|
164
|
+
const deliveredRaw = det?.delivered;
|
|
165
|
+
const delivered = typeof deliveredRaw === "string" && MCP_DELIVERY_VERDICTS.includes(deliveredRaw) ? deliveredRaw : undefined;
|
|
157
166
|
const gatedCallId = code === "gate.parked" ? gatedCallIdOfRun : undefined;
|
|
158
167
|
return {
|
|
159
168
|
...(o !== undefined ? { output: o.output, ...(o.truncated ? { truncated: true } : {}), ...(o.totalChars !== undefined ? { totalChars: o.totalChars } : {}) } : {}),
|
|
160
169
|
...(st !== undefined ? { structured: st } : {}),
|
|
161
170
|
...(typeof code === "string" ? { errorCode: code } : {}),
|
|
171
|
+
...(delivered !== undefined ? { delivered } : {}),
|
|
162
172
|
...(gatedCallId !== undefined ? { gatedCallId } : {}),
|
|
163
|
-
...(
|
|
164
|
-
...(approver !== undefined ? { approver } : {}),
|
|
165
|
-
...(resolution !== undefined ? { resolution } : {}),
|
|
166
|
-
...(autoDenied !== undefined ? { autoDenied } : {}),
|
|
173
|
+
...(gate !== undefined ? { gate } : {}),
|
|
167
174
|
};
|
|
168
175
|
}
|
|
169
176
|
export function reconciledToolEndBody(orphan) {
|
|
@@ -393,7 +400,7 @@ function makeTurnBoundary(prepared, stats, rs, deps) {
|
|
|
393
400
|
let platformCause;
|
|
394
401
|
let platformRetryAfterMs;
|
|
395
402
|
const preemptWillSuspend = preemptHit && prepared.suspendForResource !== undefined;
|
|
396
|
-
if (!preemptWillSuspend && prepared.
|
|
403
|
+
if (!preemptWillSuspend && prepared.pausedRef.current === undefined && !prepared.abortController.signal.aborted) {
|
|
397
404
|
const ledgerReadDeadline = prepared.envLifetimeSuspendAt !== undefined ? prepared.envLifetimeSuspendAt + ENV_DUE_GOVERNANCE_READ_BUDGET_MS : undefined;
|
|
398
405
|
if (prepared.usageGovernance !== undefined) {
|
|
399
406
|
const governance = prepared.usageGovernance;
|
|
@@ -461,7 +468,7 @@ function makeTurnBoundary(prepared, stats, rs, deps) {
|
|
|
461
468
|
}
|
|
462
469
|
if (prepared.suspendForResource &&
|
|
463
470
|
(rs.limits.turnsExceeded || rs.limits.budgetHit !== undefined || walltimeHit || preemptHit) &&
|
|
464
|
-
prepared.
|
|
471
|
+
prepared.pausedRef.current === undefined) {
|
|
465
472
|
const reason = preemptHit
|
|
466
473
|
? "preempt"
|
|
467
474
|
: rs.limits.budgetHit !== undefined
|
|
@@ -540,8 +547,7 @@ function makeTurnBoundary(prepared, stats, rs, deps) {
|
|
|
540
547
|
prepared.reviewRequestRef.pending = undefined;
|
|
541
548
|
const honorable = prepared.suspendForReview !== undefined &&
|
|
542
549
|
!prepared.abortController.signal.aborted &&
|
|
543
|
-
prepared.
|
|
544
|
-
prepared.reviewRef.token === undefined;
|
|
550
|
+
prepared.pausedRef.current === undefined;
|
|
545
551
|
if (honorable && prepared.suspendForReview) {
|
|
546
552
|
await prepared.suspendForReview(reason);
|
|
547
553
|
return undefined;
|
|
@@ -838,8 +844,7 @@ function makeTurnBoundary(prepared, stats, rs, deps) {
|
|
|
838
844
|
const forceManual = manualCompactRef.requested;
|
|
839
845
|
if ((!withinTaskCompaction && !forceManual) ||
|
|
840
846
|
prepared.abortController.signal.aborted ||
|
|
841
|
-
prepared.
|
|
842
|
-
prepared.reviewRef.token !== undefined) {
|
|
847
|
+
prepared.pausedRef.current !== undefined) {
|
|
843
848
|
if (forceManual) {
|
|
844
849
|
emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.mooted", version: 1, taskId: rs.telemetry.taskId, reason: "task_ending", ts: Date.now() }));
|
|
845
850
|
queue.push({ type: "compaction_outcome", outcome: "mooted", trigger: "manual", reason: "task_ending", ...ident() });
|
|
@@ -1386,16 +1391,16 @@ function makeHarnessHandlers(prepared, stats, rs, deps) {
|
|
|
1386
1391
|
prepared.lspDiagnostics.nudge(p);
|
|
1387
1392
|
}
|
|
1388
1393
|
}
|
|
1389
|
-
const
|
|
1390
|
-
if (
|
|
1391
|
-
prepared.
|
|
1394
|
+
const gate = prepared.gateOutcomes.get(event.toolCallId);
|
|
1395
|
+
if (gate !== undefined)
|
|
1396
|
+
prepared.gateOutcomes.delete(event.toolCallId);
|
|
1392
1397
|
pushContent({
|
|
1393
1398
|
type: "tool_end",
|
|
1394
1399
|
toolCallId: event.toolCallId,
|
|
1395
1400
|
toolName: event.toolName,
|
|
1396
1401
|
...(toolLabels.get(event.toolName) !== undefined ? { label: toolLabels.get(event.toolName) } : {}),
|
|
1397
1402
|
isError: event.isError,
|
|
1398
|
-
...toolEndBodyFrom(event.result, event.isError,
|
|
1403
|
+
...toolEndBodyFrom(event.result, event.isError, gate, prepared.abortController.signal.aborted ? gatedCallIdOf(prepared) : undefined),
|
|
1399
1404
|
...ident(),
|
|
1400
1405
|
});
|
|
1401
1406
|
announceWorkspaceMove();
|
|
@@ -1477,6 +1482,11 @@ function makeHarnessHandlers(prepared, stats, rs, deps) {
|
|
|
1477
1482
|
};
|
|
1478
1483
|
return { onMessageUpdate, onMessageEnd, onToolStart, onToolEnd, onTurnEnd, announceWorkspaceMove };
|
|
1479
1484
|
}
|
|
1485
|
+
const REFUSED_DECIDE_KEYS = [
|
|
1486
|
+
["gateOutcome", "the settlement record is minted by core from the host's decision facts and the row's origin, never supplied"],
|
|
1487
|
+
["settledBy", 'a retired key — the host supplies decision FACTS as hostDecision{decidedBy: "person" | "sla_timeout", approver?} and core mints the settlement word from them'],
|
|
1488
|
+
["approver", "a retired key — the attribution rides the facts it belongs to, as hostDecision.approver"],
|
|
1489
|
+
];
|
|
1480
1490
|
export class Runner {
|
|
1481
1491
|
deps;
|
|
1482
1492
|
sessions;
|
|
@@ -1698,17 +1708,18 @@ export class Runner {
|
|
|
1698
1708
|
const pcs = internals?.inheritedGate?.parentConstraints;
|
|
1699
1709
|
if (resume !== undefined)
|
|
1700
1710
|
this.locallyClaimedTokens.delete(resume.cp.token);
|
|
1701
|
-
|
|
1711
|
+
const rToken = r.terminal.kind === "paused" ? r.terminal.token : undefined;
|
|
1712
|
+
if (resume !== undefined && rToken !== resume.cp.token) {
|
|
1702
1713
|
this.parentConstraintRegistry.delete(resume.cp.token);
|
|
1703
1714
|
this.suspendedEnvReaps.delete(resume.cp.token);
|
|
1704
1715
|
}
|
|
1705
|
-
if (r.
|
|
1716
|
+
if (r.terminal.kind === "paused" && PAUSE_REGISTRY[r.terminal.gate.kind].taskStatus === "suspended" && pcs !== undefined && pcs.length > 0) {
|
|
1706
1717
|
if (this.parentConstraintRegistry.size >= Runner.PARENT_CONSTRAINT_REGISTRY_CAP) {
|
|
1707
1718
|
const oldest = this.parentConstraintRegistry.keys().next().value;
|
|
1708
1719
|
if (oldest !== undefined)
|
|
1709
1720
|
this.parentConstraintRegistry.delete(oldest);
|
|
1710
1721
|
}
|
|
1711
|
-
this.parentConstraintRegistry.set(r.
|
|
1722
|
+
this.parentConstraintRegistry.set(r.terminal.token, pcs);
|
|
1712
1723
|
}
|
|
1713
1724
|
}, (p) => {
|
|
1714
1725
|
suggestionsDone = p;
|
|
@@ -1790,10 +1801,8 @@ export class Runner {
|
|
|
1790
1801
|
taskId: taskIdRef.current ?? "unknown",
|
|
1791
1802
|
sessionId: taskIdRef.sessionId ?? "unknown",
|
|
1792
1803
|
...(taskIdRef.runId !== undefined ? { runId: taskIdRef.runId } : {}),
|
|
1793
|
-
|
|
1804
|
+
terminal: { kind: "failed", ...(code !== undefined ? { code } : {}), message: err instanceof Error ? err.message : String(err) },
|
|
1794
1805
|
result: "",
|
|
1795
|
-
errorMessage: err instanceof Error ? err.message : String(err),
|
|
1796
|
-
errorCode: code,
|
|
1797
1806
|
...(remoteEnvFailure !== undefined ? { remoteEnvFailures: remoteEnvFailure } : {}),
|
|
1798
1807
|
...(taskIdRef.effectiveMemoryScopes !== undefined ? { effectiveMemoryScopes: taskIdRef.effectiveMemoryScopes } : {}),
|
|
1799
1808
|
...(taskIdRef.effectiveReasoning !== undefined ? { effectiveReasoning: taskIdRef.effectiveReasoning } : {}),
|
|
@@ -2404,7 +2413,7 @@ export class Runner {
|
|
|
2404
2413
|
prepared.harness.engineInjectionsHeld = () => prepared.batchHaltRef.current !== undefined;
|
|
2405
2414
|
let undrainedUserAtEnd;
|
|
2406
2415
|
prepared.harness.onUndrainedUserInputs = (counts) => {
|
|
2407
|
-
if (prepared.
|
|
2416
|
+
if (prepared.pausedRef.current !== undefined) {
|
|
2408
2417
|
undrainedUserAtEnd = counts;
|
|
2409
2418
|
return;
|
|
2410
2419
|
}
|
|
@@ -3059,7 +3068,7 @@ export class Runner {
|
|
|
3059
3068
|
if (stopHook || finalVerificationOn) {
|
|
3060
3069
|
let consecutiveBlocks = 0;
|
|
3061
3070
|
prepared.harness.setStopGate(async () => {
|
|
3062
|
-
if (prepared.abortController.signal.aborted || prepared.
|
|
3071
|
+
if (prepared.abortController.signal.aborted || prepared.pausedRef.current !== undefined || prepared.batchHaltRef.current !== undefined)
|
|
3063
3072
|
return [];
|
|
3064
3073
|
if (finalVerificationOn &&
|
|
3065
3074
|
(rs.counters.finalVerifyInjections === 0 || (rs.counters.finalVerifyInjections === 1 && rs.counters.groundingSignalPreR9 && !rs.counters.groundingSignalPostR9)) &&
|
|
@@ -3300,7 +3309,7 @@ export class Runner {
|
|
|
3300
3309
|
degenerateOutput: { detect: (m) => isDegenerateCutMessage(m) },
|
|
3301
3310
|
promptTooLong: {
|
|
3302
3311
|
recover: async (attempt, turnSignal) => {
|
|
3303
|
-
if (prepared.abortController.signal.aborted || prepared.
|
|
3312
|
+
if (prepared.abortController.signal.aborted || prepared.pausedRef.current !== undefined)
|
|
3304
3313
|
return false;
|
|
3305
3314
|
if (turnSignal?.aborted === true)
|
|
3306
3315
|
return false;
|
|
@@ -3345,7 +3354,7 @@ export class Runner {
|
|
|
3345
3354
|
},
|
|
3346
3355
|
});
|
|
3347
3356
|
prepared.microCompact.inTurnCompactionRef.current = async (turnSignal, anchoredEstimate) => {
|
|
3348
|
-
if (prepared.abortController.signal.aborted || prepared.
|
|
3357
|
+
if (prepared.abortController.signal.aborted || prepared.pausedRef.current !== undefined)
|
|
3349
3358
|
return false;
|
|
3350
3359
|
if (turnSignal?.aborted === true)
|
|
3351
3360
|
return false;
|
|
@@ -3926,9 +3935,8 @@ export class Runner {
|
|
|
3926
3935
|
this.deps.onError?.(flushErr, { phase: "config", sessionId: prepared.sessionId });
|
|
3927
3936
|
}
|
|
3928
3937
|
}
|
|
3929
|
-
const
|
|
3930
|
-
const
|
|
3931
|
-
const durablyPaused = suspended || reviewPaused;
|
|
3938
|
+
const committedPause = prepared.pausedRef.current;
|
|
3939
|
+
const durablyPaused = committedPause !== undefined;
|
|
3932
3940
|
const interrupted = !durablyPaused &&
|
|
3933
3941
|
(threw !== undefined || timeout.fired || rs.limits.turnsExceeded || rs.limits.budgetHit !== undefined || abortedLive || final?.stopReason === "aborted");
|
|
3934
3942
|
let orphansClosed = 0;
|
|
@@ -3972,9 +3980,9 @@ export class Runner {
|
|
|
3972
3980
|
}
|
|
3973
3981
|
}
|
|
3974
3982
|
let migratedParked = { steer: 0, followUp: 0 };
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3983
|
+
if (committedPause !== undefined && this.deps.checkpointStore !== undefined) {
|
|
3984
|
+
const parkToken = committedPause.cause.token;
|
|
3985
|
+
const parkScope = committedPause.scope;
|
|
3978
3986
|
const store = this.deps.checkpointStore;
|
|
3979
3987
|
const carried = [];
|
|
3980
3988
|
for (const record of prepared.harness.readParkableUserInputs()) {
|
|
@@ -4067,7 +4075,7 @@ export class Runner {
|
|
|
4067
4075
|
}
|
|
4068
4076
|
}
|
|
4069
4077
|
if (prepared.resourceLedger &&
|
|
4070
|
-
(resume?.cp.gate.kind === "resource_limit" || prepared.
|
|
4078
|
+
(resume?.cp.gate.kind === "resource_limit" || prepared.pausedRef.current?.cause.gate.kind === "resource_limit")) {
|
|
4071
4079
|
stats.costMicroUsd += prepared.resourceLedger.spentMicroUsd;
|
|
4072
4080
|
stats.tokens += prepared.resourceLedger.spentTokens;
|
|
4073
4081
|
stats.turns += prepared.resourceLedger.spentTurns;
|
|
@@ -4118,46 +4126,26 @@ export class Runner {
|
|
|
4118
4126
|
gitCoreOverBudget: prepared.gitStatusRef.terminalCode === "irreducible_core_over_budget",
|
|
4119
4127
|
outputInvalid: rs.degrade.outputInvalid,
|
|
4120
4128
|
suspendLoop: prepared.suspendLoopRef.hit,
|
|
4121
|
-
|
|
4122
|
-
? {
|
|
4123
|
-
token: prepared.suspendRef.token,
|
|
4124
|
-
...(prepared.suspendRef.checkpointId !== undefined ? { checkpointId: prepared.suspendRef.checkpointId } : {}),
|
|
4125
|
-
gate: prepared.suspendRef.gate,
|
|
4126
|
-
...(prepared.suspendRef.restoreMode !== undefined ? { restoreMode: prepared.suspendRef.restoreMode } : {}),
|
|
4127
|
-
}
|
|
4128
|
-
: undefined,
|
|
4129
|
-
reviewRef: prepared.reviewRef.token !== undefined && prepared.reviewRef.gate !== undefined
|
|
4130
|
-
? {
|
|
4131
|
-
token: prepared.reviewRef.token,
|
|
4132
|
-
...(prepared.reviewRef.checkpointId !== undefined ? { checkpointId: prepared.reviewRef.checkpointId } : {}),
|
|
4133
|
-
gate: prepared.reviewRef.gate,
|
|
4134
|
-
...(prepared.reviewRef.restoreMode !== undefined ? { restoreMode: prepared.reviewRef.restoreMode } : {}),
|
|
4135
|
-
}
|
|
4136
|
-
: undefined,
|
|
4129
|
+
...(prepared.pausedRef.current !== undefined ? { paused: prepared.pausedRef.current.cause } : {}),
|
|
4137
4130
|
});
|
|
4138
4131
|
if (resume !== undefined &&
|
|
4139
4132
|
resume.pendingActionStarted !== true &&
|
|
4140
|
-
result.
|
|
4141
|
-
result.
|
|
4142
|
-
result.status !== "needs_review" &&
|
|
4133
|
+
result.terminal.kind !== "completed" &&
|
|
4134
|
+
result.terminal.kind !== "paused" &&
|
|
4143
4135
|
!(resumeDecisionWasNegative(resume) && resume.decisionDelivered === true) &&
|
|
4144
4136
|
resume.onEnvRestoreFailed !== undefined) {
|
|
4145
4137
|
try {
|
|
4146
4138
|
await resume.onEnvRestoreFailed("env_failed");
|
|
4147
|
-
|
|
4148
|
-
r.errorCode ??= "resume.reopened_unstarted";
|
|
4149
|
-
r.errorMessage = `${r.errorMessage ?? "the resume leg did not complete"} — the approved action never started, so the checkpoint was REOPENED (pending): the same approval can be resumed again, do not ask for a fresh one`;
|
|
4139
|
+
amendTerminal(result, "resume.reopened_unstarted", (m) => `${m ?? "the resume leg did not complete"} — the approved action never started, so the checkpoint was REOPENED (pending): the same approval can be resumed again, do not ask for a fresh one`);
|
|
4150
4140
|
}
|
|
4151
4141
|
catch {
|
|
4152
|
-
|
|
4153
|
-
r.errorCode ??= "resume.reopen_failed_unstarted";
|
|
4154
|
-
r.errorMessage = `${r.errorMessage ?? "the resume leg did not complete"} — the approved action never started, and reopening the checkpoint FAILED: its state is unprovable from here. Re-read it before deciding; do NOT issue a fresh approval on the assumption the old one is dead`;
|
|
4142
|
+
amendTerminal(result, "resume.reopen_failed_unstarted", (m) => `${m ?? "the resume leg did not complete"} — the approved action never started, and reopening the checkpoint FAILED: its state is unprovable from here. Re-read it before deciding; do NOT issue a fresh approval on the assumption the old one is dead`);
|
|
4155
4143
|
}
|
|
4156
4144
|
}
|
|
4157
4145
|
prepared.sealReadStateSeat?.(ownCommittedTailRef.current);
|
|
4158
4146
|
{
|
|
4159
4147
|
const wss = prepared.workspaceStateSettle;
|
|
4160
|
-
if (wss !== undefined && result.
|
|
4148
|
+
if (wss !== undefined && result.terminal.kind !== "paused") {
|
|
4161
4149
|
const curCwd = prepared.cwdRef?.current !== undefined && prepared.cwdRef.current !== wss.rootCanonical ? prepared.cwdRef.current : undefined;
|
|
4162
4150
|
const curWt = prepared.worktreeSessionRef?.current;
|
|
4163
4151
|
const changed = curCwd !== wss.restoredHandsCwd || curWt?.worktreeDir !== wss.restoredWorktreeDir;
|
|
@@ -4172,7 +4160,7 @@ export class Runner {
|
|
|
4172
4160
|
}
|
|
4173
4161
|
const stopFailureHook = (spec.hooks ?? this.deps.hooks)?.stopFailure;
|
|
4174
4162
|
if (stopFailureHook !== undefined &&
|
|
4175
|
-
result.
|
|
4163
|
+
result.terminal.kind === "failed" &&
|
|
4176
4164
|
final?.stopReason === "error" &&
|
|
4177
4165
|
!isDegenerateCutMessage(final) &&
|
|
4178
4166
|
threw === undefined &&
|
|
@@ -4180,12 +4168,13 @@ export class Runner {
|
|
|
4180
4168
|
!rs.degrade.outputInvalid &&
|
|
4181
4169
|
!prepared.suspendLoopRef.hit &&
|
|
4182
4170
|
!abortedLive &&
|
|
4183
|
-
result.
|
|
4171
|
+
result.terminal.code !== "conflict") {
|
|
4172
|
+
const failedCause = result.terminal;
|
|
4184
4173
|
try {
|
|
4185
4174
|
const failureSeat = await runHookSeat("stopFailure", { timeoutMs: prepared.hookTimeoutMs }, (sig) => stopFailureHook({
|
|
4186
4175
|
identity: prepared.hookIdentity,
|
|
4187
|
-
error:
|
|
4188
|
-
...(
|
|
4176
|
+
error: failedCause.message ?? "model error",
|
|
4177
|
+
...(failedCause.code !== undefined ? { errorKind: failedCause.code } : {}),
|
|
4189
4178
|
turns: stats.turns,
|
|
4190
4179
|
signal: sig,
|
|
4191
4180
|
}));
|
|
@@ -4205,12 +4194,13 @@ export class Runner {
|
|
|
4205
4194
|
}
|
|
4206
4195
|
}
|
|
4207
4196
|
}
|
|
4197
|
+
const terminalFace = terminalProjection(result.terminal);
|
|
4208
4198
|
emitDelegationLifecycle({
|
|
4209
4199
|
phase: "terminal",
|
|
4210
4200
|
identity: prepared.hookIdentity,
|
|
4211
|
-
status:
|
|
4201
|
+
status: terminalFace.status,
|
|
4212
4202
|
turns: stats.turns,
|
|
4213
|
-
...(
|
|
4203
|
+
...(terminalFace.errorCode !== undefined ? { errorCode: terminalFace.errorCode } : {}),
|
|
4214
4204
|
});
|
|
4215
4205
|
if (taskIdRef !== undefined)
|
|
4216
4206
|
taskIdRef.delegationTerminalOwed = undefined;
|
|
@@ -4218,8 +4208,8 @@ export class Runner {
|
|
|
4218
4208
|
result.degraded = rs.degrade.degraded;
|
|
4219
4209
|
if (prepared.outputRef.set)
|
|
4220
4210
|
result.structuredOutput = prepared.outputRef.value;
|
|
4221
|
-
const committedToken = prepared.
|
|
4222
|
-
const committedScope = prepared.
|
|
4211
|
+
const committedToken = prepared.pausedRef.current?.cause.token;
|
|
4212
|
+
const committedScope = prepared.pausedRef.current?.scope;
|
|
4223
4213
|
if (committedToken !== undefined) {
|
|
4224
4214
|
const store = resolveCheckpointStore(spec, this.deps);
|
|
4225
4215
|
if (store && committedScope !== undefined) {
|
|
@@ -4244,8 +4234,8 @@ export class Runner {
|
|
|
4244
4234
|
version: 1,
|
|
4245
4235
|
taskId: rs.telemetry.taskId,
|
|
4246
4236
|
runId: rs.telemetry.runId,
|
|
4247
|
-
status:
|
|
4248
|
-
errorCode:
|
|
4237
|
+
status: terminalFace.status,
|
|
4238
|
+
errorCode: terminalFace.errorCode,
|
|
4249
4239
|
turns: stats.turns,
|
|
4250
4240
|
tokens: stats.tokens,
|
|
4251
4241
|
...(rs.telemetry.unpricedSpend ? {} : { costMicroUsd: stats.costMicroUsd }),
|
|
@@ -4261,7 +4251,7 @@ export class Runner {
|
|
|
4261
4251
|
ts: Date.now(),
|
|
4262
4252
|
}));
|
|
4263
4253
|
setResult(result);
|
|
4264
|
-
if (parentToolCallId !== undefined &&
|
|
4254
|
+
if (parentToolCallId !== undefined && result.terminal.kind !== "paused") {
|
|
4265
4255
|
const terminalTick = {
|
|
4266
4256
|
type: "task_progress",
|
|
4267
4257
|
taskId: rs.telemetry.taskId,
|
|
@@ -4271,7 +4261,7 @@ export class Runner {
|
|
|
4271
4261
|
...(subagentName ? { name: subagentName } : {}),
|
|
4272
4262
|
model: prepared.model.id,
|
|
4273
4263
|
usage: { totalTokens: stats.tokens, toolUses: stats.toolCalls, durationMs: Date.now() - rs.telemetry.taskStart },
|
|
4274
|
-
status: result.
|
|
4264
|
+
status: result.terminal.kind === "completed" ? "completed" : "failed",
|
|
4275
4265
|
...ident(),
|
|
4276
4266
|
};
|
|
4277
4267
|
queue.push(terminalTick);
|
|
@@ -4289,7 +4279,7 @@ export class Runner {
|
|
|
4289
4279
|
queue.push({ type: "done", result });
|
|
4290
4280
|
queue.close();
|
|
4291
4281
|
await prepared.fileHistoryBoundary?.settle();
|
|
4292
|
-
if (spec.suggestNextPrompts && result.
|
|
4282
|
+
if (spec.suggestNextPrompts && result.terminal.kind === "completed") {
|
|
4293
4283
|
onSuggestions(this.suggestNextPrompts(spec, prepared, result));
|
|
4294
4284
|
}
|
|
4295
4285
|
}
|
|
@@ -4297,15 +4287,13 @@ export class Runner {
|
|
|
4297
4287
|
const ownedEnvDying = prepared.ownedEnv !== undefined &&
|
|
4298
4288
|
hasDestroy(prepared.ownedEnv) &&
|
|
4299
4289
|
isIsolated(prepared.ownedEnv) &&
|
|
4300
|
-
prepared.
|
|
4301
|
-
prepared.reviewRef.token === undefined;
|
|
4290
|
+
prepared.pausedRef.current === undefined;
|
|
4302
4291
|
await settleTeardownLeg(() => defaultTaskRegistry.settleKilledForOwner({ owner: spec.taskId ?? prepared.sessionId, scope: spec.principal ?? "default", sessionId: prepared.sessionId }, {
|
|
4303
4292
|
source: abortedLive &&
|
|
4304
4293
|
!timeout.fired &&
|
|
4305
4294
|
!rs.limits.turnsExceeded &&
|
|
4306
4295
|
rs.limits.budgetHit === undefined &&
|
|
4307
|
-
prepared.
|
|
4308
|
-
prepared.reviewRef.token === undefined
|
|
4296
|
+
prepared.pausedRef.current === undefined
|
|
4309
4297
|
? "user"
|
|
4310
4298
|
: "parent",
|
|
4311
4299
|
skipSessionScoped: true,
|
|
@@ -4485,6 +4473,7 @@ export class Runner {
|
|
|
4485
4473
|
break;
|
|
4486
4474
|
case "tool_approval":
|
|
4487
4475
|
case "resource_limit":
|
|
4476
|
+
case "review":
|
|
4488
4477
|
case "plan_review": {
|
|
4489
4478
|
throw new CheckpointError("wake.gate_pending", `cannot wake: this checkpoint still carries a pending gate-typed action (pendingAction "${pa.kind}" ` +
|
|
4490
4479
|
`on a "${gk}" gate — inconsistent row) — wake is not a decision channel; resolve it through its ` +
|
|
@@ -4505,14 +4494,12 @@ export class Runner {
|
|
|
4505
4494
|
"wake would burn the checkpoint on a blank continuation; supply `message` or park a steer first");
|
|
4506
4495
|
}
|
|
4507
4496
|
}
|
|
4508
|
-
const gateMatch =
|
|
4509
|
-
(cp.gate.kind === "human" && outcomeGate === "policy_ask") ||
|
|
4510
|
-
(cp.gate.kind === "irreversible_ask" && outcomeGate === "policy_ask") ||
|
|
4511
|
-
(cp.gate.kind === "resource_limit" && outcomeGate === "resource_limit") ||
|
|
4512
|
-
(cp.gate.kind === "needs_review" && outcomeGate === "dry_run_review") ||
|
|
4513
|
-
(cp.gate.kind === "plan_review" && outcomeGate === "plan_review");
|
|
4497
|
+
const gateMatch = isGateKind(cp.gate.kind) && resumeGateMatches(cp.gate.kind, outcomeGate);
|
|
4514
4498
|
if (!gateMatch) {
|
|
4515
|
-
throw new CheckpointError("checkpoint.gate_mismatch", `resume outcome (gate "${describeSuppliedValue(outcomeGate)}") does not match checkpoint gate "${cp.gate.kind}" — resume serves
|
|
4499
|
+
throw new CheckpointError("checkpoint.gate_mismatch", `resume outcome (gate "${describeSuppliedValue(outcomeGate)}") does not match checkpoint gate "${cp.gate.kind}" — resume serves ${Object.entries(PAUSE_REGISTRY)
|
|
4500
|
+
.filter(([, row]) => !row.terminal)
|
|
4501
|
+
.map(([k, row]) => `${k}/${row.resume}`)
|
|
4502
|
+
.join(", ")}`);
|
|
4516
4503
|
}
|
|
4517
4504
|
let plainReviewOutcome;
|
|
4518
4505
|
if (cp.gate.kind === "plan_review") {
|
|
@@ -4619,25 +4606,29 @@ export class Runner {
|
|
|
4619
4606
|
if (decision !== "allow" && decision !== "deny") {
|
|
4620
4607
|
throw new CheckpointError("checkpoint.invalid_outcome", `resume decision "${describeSuppliedValue(decision)}" is outside the policy_ask domain — a decide is exactly "allow" or "deny"; refusing pre-CAS, the checkpoint stays pending`);
|
|
4621
4608
|
}
|
|
4622
|
-
const
|
|
4623
|
-
|
|
4624
|
-
|
|
4609
|
+
for (const [key, why] of REFUSED_DECIDE_KEYS) {
|
|
4610
|
+
if (Reflect.get(decide, key) !== undefined) {
|
|
4611
|
+
throw new CheckpointError("checkpoint.invalid_outcome", `resume carries \`${key}\` — ${why}; refusing pre-CAS, the checkpoint stays pending`, { field: "hostDecision" });
|
|
4612
|
+
}
|
|
4613
|
+
}
|
|
4614
|
+
const hostDecisionRaw = decide.hostDecision;
|
|
4615
|
+
if (typeof hostDecisionRaw !== "object" || hostDecisionRaw === null) {
|
|
4616
|
+
throw new CheckpointError("checkpoint.invalid_outcome", `resume carries no hostDecision — a policy_ask decide names who decided it ({ decidedBy: "person" | "sla_timeout", approver? }); refusing pre-CAS, the checkpoint stays pending`, { field: "hostDecision" });
|
|
4617
|
+
}
|
|
4618
|
+
const decidedBy = hostDecisionRaw.decidedBy;
|
|
4619
|
+
if (decidedBy !== "person" && decidedBy !== "sla_timeout") {
|
|
4620
|
+
throw new CheckpointError("checkpoint.invalid_outcome", `resume hostDecision.decidedBy "${describeSuppliedValue(decidedBy)}" is outside the closed set — it is exactly "person" or "sla_timeout"; refusing pre-CAS, the checkpoint stays pending`, { field: "hostDecision" });
|
|
4625
4621
|
}
|
|
4626
|
-
if (
|
|
4627
|
-
throw new CheckpointError("checkpoint.invalid_outcome", `resume carries decision "allow"
|
|
4628
|
-
"supply \"human\", or omit the field if the allow came from configuration rather than a person; refusing pre-CAS, the checkpoint stays pending", { field: "settledBy" });
|
|
4622
|
+
if (decidedBy === "sla_timeout" && decision === "allow") {
|
|
4623
|
+
throw new CheckpointError("checkpoint.invalid_outcome", `resume carries decision "allow" decided by "sla_timeout" — an elapsed SLA window cannot be what approved an action that EXECUTES; refusing pre-CAS, the checkpoint stays pending`, { field: "hostDecision" });
|
|
4629
4624
|
}
|
|
4630
|
-
const
|
|
4631
|
-
const attribution = screenApproverAttribution(approver);
|
|
4625
|
+
const attribution = screenApproverAttribution(hostDecisionRaw.approver);
|
|
4632
4626
|
if (attribution.defect !== undefined) {
|
|
4633
4627
|
throw new CheckpointError("checkpoint.invalid_outcome", `resume carries an approver attribution this engine refuses: ${attribution.defect}; refusing pre-CAS, the checkpoint stays pending`, { field: "approver" });
|
|
4634
4628
|
}
|
|
4635
|
-
if (attribution.approver !== undefined && settledBy === undefined) {
|
|
4636
|
-
throw new CheckpointError("checkpoint.invalid_outcome", `resume names an approver but no settledBy — an attribution says WHOSE settlement it was and needs the word that says what KIND of end it was beside it; ` +
|
|
4637
|
-
`supply settledBy ("human" / "timeout" / "aborted"), or omit the approver; refusing pre-CAS, the checkpoint stays pending`, { field: "approver" });
|
|
4638
|
-
}
|
|
4639
4629
|
const reason = decide.reason;
|
|
4640
4630
|
assertOutcomeText(reason, "reason");
|
|
4631
|
+
const approverCell = attribution.approver !== undefined ? { approver: attribution.approver } : {};
|
|
4641
4632
|
plainPolicyOutcome = {
|
|
4642
4633
|
gate: "policy_ask",
|
|
4643
4634
|
boundCallId: decide.boundCallId,
|
|
@@ -4646,8 +4637,7 @@ export class Runner {
|
|
|
4646
4637
|
...(updatedInput !== undefined ? { updatedInput } : {}),
|
|
4647
4638
|
...(reason !== undefined ? { reason } : {}),
|
|
4648
4639
|
...(redeemedAnswer !== undefined ? { answer: redeemedAnswer } : {}),
|
|
4649
|
-
|
|
4650
|
-
...(attribution.approver !== undefined ? { approver: attribution.approver } : {}),
|
|
4640
|
+
hostDecision: { decidedBy, ...approverCell },
|
|
4651
4641
|
};
|
|
4652
4642
|
if (plainPolicyOutcome.decision === "deny" && plainPolicyOutcome.reason && sanitizeUntrustedText(plainPolicyOutcome.reason) !== plainPolicyOutcome.reason) {
|
|
4653
4643
|
throw new CheckpointError("checkpoint.invalid_outcome", "resume deny/reject reason must not contain a </system-reminder> tag");
|
|
@@ -4701,11 +4691,7 @@ export class Runner {
|
|
|
4701
4691
|
throw new CheckpointError("checkpoint.unsupported_version", `checkpoint version ${checkpointVersionOf(cp)} is newer than this worker supports (max ${MAX_SUPPORTED_CHECKPOINT_VERSION})`, { reason: "version_newer" });
|
|
4702
4692
|
}
|
|
4703
4693
|
const preCasGateBit = cp.gate.kind === "irreversible_ask" ? cp.gate.realApproval : undefined;
|
|
4704
|
-
const preCasBitWellFormed = preCasGateBit !== undefined &&
|
|
4705
|
-
typeof preCasGateBit === "object" &&
|
|
4706
|
-
(preCasGateBit.origin === "org_rule" ||
|
|
4707
|
-
preCasGateBit.origin === "org_unavailable" ||
|
|
4708
|
-
preCasGateBit.origin === "policy");
|
|
4694
|
+
const preCasBitWellFormed = preCasGateBit !== undefined && typeof preCasGateBit === "object" && isAskOrigin(preCasGateBit.origin);
|
|
4709
4695
|
const faceSaysGoverned = (cp.state.readFace?.realApproval) === true;
|
|
4710
4696
|
const preCasBitViolation = checkpointVersionOf(cp) === REAL_APPROVAL_CHECKPOINT_VERSION
|
|
4711
4697
|
? !preCasBitWellFormed
|
|
@@ -4716,7 +4702,7 @@ export class Runner {
|
|
|
4716
4702
|
: preCasGateBit !== undefined && !preCasBitWellFormed;
|
|
4717
4703
|
if (preCasBitViolation) {
|
|
4718
4704
|
throw new CheckpointError("checkpoint.invalid_outcome", checkpointVersionOf(cp) >= REAL_APPROVAL_CHECKPOINT_VERSION
|
|
4719
|
-
? `a v${checkpointVersionOf(cp)} checkpoint's realApproval gate bit must be well-formed (origin
|
|
4705
|
+
? `a v${checkpointVersionOf(cp)} checkpoint's realApproval gate bit must be well-formed (origin a member of the closed ask-origin set)${checkpointVersionOf(cp) === REAL_APPROVAL_CHECKPOINT_VERSION ? " and present on an irreversible_ask gate" : ""} — this row's is not; refusing to resume a damaged real-approval row (corruption / downgrade guard), the checkpoint stays pending`
|
|
4720
4706
|
: `a v${checkpointVersionOf(cp)} checkpoint carries a realApproval gate bit no release of that version ever minted — refusing to honor a fabricated origin (corruption / forgery guard), the checkpoint stays pending`, { reason: checkpointVersionOf(cp) >= REAL_APPROVAL_CHECKPOINT_VERSION ? "real_approval_damaged" : "real_approval_forged" });
|
|
4721
4707
|
}
|
|
4722
4708
|
{
|
|
@@ -4736,9 +4722,8 @@ export class Runner {
|
|
|
4736
4722
|
: `a v${checkpointVersionOf(cp)} checkpoint carries a read-face section no release of that version ever minted — refusing to honor a fabricated read-face state (corruption / forgery guard), the checkpoint stays pending`, { reason: checkpointVersionOf(cp) >= FACE_CHECKPOINT_VERSION ? "read_face_damaged" : "read_face_forged" });
|
|
4737
4723
|
}
|
|
4738
4724
|
}
|
|
4739
|
-
if ((preCasGateBit
|
|
4740
|
-
this
|
|
4741
|
-
throw new CheckpointError("checkpoint.unsupported_version", `this checkpoint's approval was minted under organization governance (${preCasGateBit.origin}) and this worker has no org adjudication wiring (its permission-rule store carries no org partition) — a governed approval may only be redeemed where governance can be enforced; the checkpoint stays pending, resume it on an org-wired worker`, { reason: "governed_unwired" });
|
|
4725
|
+
if (realApprovalOrgFact(preCasGateBit) !== undefined && this.deps.permissionRuleStore?.partitions.org !== true) {
|
|
4726
|
+
throw new CheckpointError("checkpoint.unsupported_version", `this checkpoint's approval was minted under organization governance (org ${String(realApprovalOrgFact(preCasGateBit))}, routed as ${String(preCasGateBit?.origin)}) and this worker has no org adjudication wiring (its permission-rule store carries no org partition) — a governed approval may only be redeemed where governance can be enforced; the checkpoint stays pending, resume it on an org-wired worker`, { reason: "governed_unwired" });
|
|
4742
4727
|
}
|
|
4743
4728
|
const retiredWalltimeTotal = cp.resourceLedger?.totalWalltimeSec;
|
|
4744
4729
|
if (retiredWalltimeTotal !== undefined) {
|
|
@@ -4995,6 +4980,18 @@ export class Runner {
|
|
|
4995
4980
|
}
|
|
4996
4981
|
}
|
|
4997
4982
|
}
|
|
4983
|
+
if (plainPolicyOutcome !== undefined) {
|
|
4984
|
+
const parkedOrigin = cp.pendingAction.kind === "tool_approval" ? cp.pendingAction.origin : undefined;
|
|
4985
|
+
if (!isAskOrigin(parkedOrigin)) {
|
|
4986
|
+
throw new CheckpointError("checkpoint.unsupported_version", "this checkpoint's pending approval carries no origin word (a row minted before the ask's origin was persisted on the park) — its settlement cannot be recorded; the checkpoint stays pending");
|
|
4987
|
+
}
|
|
4988
|
+
const facts = plainPolicyOutcome.hostDecision;
|
|
4989
|
+
const approverCell = facts.approver !== undefined ? { approver: facts.approver } : {};
|
|
4990
|
+
const settlement = facts.decidedBy === "sla_timeout"
|
|
4991
|
+
? { kind: "park_sla_expired", who: { party: "host", ...approverCell }, when: Date.now() }
|
|
4992
|
+
: { kind: plainPolicyOutcome.decision === "allow" ? "human_allowed" : "human_refused", who: { party: "person", ...approverCell }, when: Date.now(), ...(plainPolicyOutcome.decision === "deny" && plainPolicyOutcome.reason !== undefined ? { note: plainPolicyOutcome.reason } : {}) };
|
|
4993
|
+
plainPolicyOutcome = { ...plainPolicyOutcome, gateOutcome: mintGateOutcome({ ...(plainPolicyOutcome.decision === "deny" ? { deniedBy: "ask_resolution" } : {}), settled: { settlement, origin: parkedOrigin } }) };
|
|
4994
|
+
}
|
|
4998
4995
|
const outcomeForStore = plainPolicyOutcome ?? plainReviewOutcome ?? plainParkOutcome;
|
|
4999
4996
|
if (outcomeForStore === undefined) {
|
|
5000
4997
|
throw new CheckpointError("checkpoint.invalid_outcome", `resume outcome (gate "${describeSuppliedValue(outcomeGate)}") matched the checkpoint gate but no lane captured it — refusing pre-CAS rather than passing the caller's live object to the store and the resumed run`);
|
|
@@ -5237,13 +5234,15 @@ export class Runner {
|
|
|
5237
5234
|
const resolvedArgs = pendingAction.toolName === ASK_USER_QUESTION_TOOL_NAME ? pendingAction.args : (outcome.updatedInput !== undefined ? outcome.updatedInput : pendingAction.args);
|
|
5238
5235
|
const pendingLabel = (() => { const l = prepared.tools.find((t) => t.name === pendingAction.toolName)?.label; return l !== undefined && l !== pendingAction.toolName ? { label: l } : {}; })();
|
|
5239
5236
|
emit({ type: "tool_start", toolCallId: pendingAction.toolCallId, toolName: pendingAction.toolName, ...pendingLabel, args: resolvedArgs });
|
|
5240
|
-
const emitEnd = (isError, result) => emit({ type: "tool_end", toolCallId: pendingAction.toolCallId, toolName: pendingAction.toolName, ...pendingLabel, isError, ...toolEndBodyFrom(result, isError,
|
|
5237
|
+
const emitEnd = (isError, result, gate = outcome.gateOutcome) => emit({ type: "tool_end", toolCallId: pendingAction.toolCallId, toolName: pendingAction.toolName, ...pendingLabel, isError, ...toolEndBodyFrom(result, isError, gate) });
|
|
5238
|
+
const vetoed = (layer) => mintGateOutcome({
|
|
5239
|
+
deniedBy: layer,
|
|
5240
|
+
...(outcome.gateOutcome?.settlement !== undefined && outcome.gateOutcome.origin !== undefined ? { settled: { settlement: outcome.gateOutcome.settlement, origin: outcome.gateOutcome.origin } } : {}),
|
|
5241
|
+
});
|
|
5241
5242
|
if (outcome.decision === "deny") {
|
|
5242
|
-
const defaultDenial = outcome.
|
|
5243
|
+
const defaultDenial = outcome.gateOutcome?.settlement?.kind === "park_sla_expired"
|
|
5243
5244
|
? `No one answered the approval request for the pending tool call "${pendingAction.toolName}" — the approval window elapsed with no answer, so it was not executed.`
|
|
5244
|
-
:
|
|
5245
|
-
? `The approval for the pending tool call "${pendingAction.toolName}" ended without anyone deciding it (it was cancelled or could not be delivered), so it was not executed.`
|
|
5246
|
-
: `The pending tool call "${pendingAction.toolName}" was denied by an approver.`;
|
|
5245
|
+
: `The pending tool call "${pendingAction.toolName}" was denied by an approver.`;
|
|
5247
5246
|
const reason = outcome.reason ? delimitUntrusted("reviewer note", outcome.reason, REVIEWER_NOTE_MAX_BODY) : defaultDenial;
|
|
5248
5247
|
emitEnd(true, { content: formatHookFeedback(reason, prepared.reminderMark) });
|
|
5249
5248
|
const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, formatHookFeedback(reason, prepared.reminderMark), true));
|
|
@@ -5270,17 +5269,17 @@ export class Runner {
|
|
|
5270
5269
|
(rechecked.updatedInput !== undefined && !deepJsonEqual(rechecked.updatedInput, resolvedArgs));
|
|
5271
5270
|
if (beltUnsafe) {
|
|
5272
5271
|
const editedDenial = formatHookFeedback(`The approver EDITED this call's input; the edited call is refused by the deployment's tool policy and was not executed${rechecked.action === "deny" && rechecked.message ? `: ${rechecked.message}` : " (the policy rewrote the edit to a value that was not approved)"}.`, prepared.reminderMark);
|
|
5273
|
-
emitEnd(true, { content: editedDenial });
|
|
5272
|
+
emitEnd(true, { content: editedDenial }, vetoed("policy"));
|
|
5274
5273
|
const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, editedDenial, true));
|
|
5275
5274
|
emitCommitted(eid, "toolResult", pendingAction.toolCallId);
|
|
5276
5275
|
return;
|
|
5277
5276
|
}
|
|
5278
5277
|
}
|
|
5279
5278
|
const gateRealApproval = resume.cp.gate.kind === "irreversible_ask" ? resume.cp.gate.realApproval : undefined;
|
|
5280
|
-
const gateOrgGoverned = gateRealApproval
|
|
5279
|
+
const gateOrgGoverned = realApprovalOrgFact(gateRealApproval) !== undefined;
|
|
5281
5280
|
if (gateOrgGoverned && prepared.permissionRuleOrg === undefined) {
|
|
5282
5281
|
const unwiredDenial = formatHookFeedback(`The approved tool call "${pendingAction.toolName}" was not executed: its approval was minted under organization governance (${gateRealApproval.origin}), and this worker has no org adjudication wiring — a governed approval may only be redeemed where governance can be enforced. This approval is spent; re-issue the call on an org-wired worker.`, prepared.reminderMark);
|
|
5283
|
-
emitEnd(true, { content: unwiredDenial });
|
|
5282
|
+
emitEnd(true, { content: unwiredDenial }, vetoed("org"));
|
|
5284
5283
|
const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, unwiredDenial, true));
|
|
5285
5284
|
emitCommitted(eid, "toolResult", pendingAction.toolCallId);
|
|
5286
5285
|
return;
|
|
@@ -5298,7 +5297,7 @@ export class Runner {
|
|
|
5298
5297
|
},
|
|
5299
5298
|
});
|
|
5300
5299
|
const blocked = org.status === "unavailable"
|
|
5301
|
-
? gateRealApproval
|
|
5300
|
+
? realApprovalOrgFact(gateRealApproval) === "unavailable"
|
|
5302
5301
|
? undefined
|
|
5303
5302
|
: orgWaitCancelled
|
|
5304
5303
|
? "this deployment is org-governed and the task was cancelled before the organization policy snapshot could be adjudicated"
|
|
@@ -5311,7 +5310,7 @@ export class Runner {
|
|
|
5311
5310
|
? ` Governance disclosures: ${inlineUntrusted(org.disclosures.join("; "), ORG_DISCLOSURE_MAX_CHARS)}.`
|
|
5312
5311
|
: "";
|
|
5313
5312
|
const orgDenial = formatHookFeedback(`The approved tool call "${pendingAction.toolName}" was not executed: ${blocked}.${orgDisclosures} This approval is spent — the call has to be re-issued and approved again once organization policy permits it.`, prepared.reminderMark);
|
|
5314
|
-
emitEnd(true, { content: orgDenial });
|
|
5313
|
+
emitEnd(true, { content: orgDenial }, vetoed("org"));
|
|
5315
5314
|
const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, orgDenial, true));
|
|
5316
5315
|
emitCommitted(eid, "toolResult", pendingAction.toolCallId);
|
|
5317
5316
|
return;
|
|
@@ -5327,7 +5326,7 @@ export class Runner {
|
|
|
5327
5326
|
}, prepared.abortController.signal));
|
|
5328
5327
|
if (narrowed.action === "deny") {
|
|
5329
5328
|
const narrowedDenial = formatHookFeedback(`The approved tool call "${pendingAction.toolName}" is now denied by a session rule and was not executed${narrowed.message ? `: ${narrowed.message}` : ""}.`, prepared.reminderMark);
|
|
5330
|
-
emitEnd(true, { content: narrowedDenial });
|
|
5329
|
+
emitEnd(true, { content: narrowedDenial }, vetoed("policy"));
|
|
5331
5330
|
const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, narrowedDenial, true));
|
|
5332
5331
|
emitCommitted(eid, "toolResult", pendingAction.toolCallId);
|
|
5333
5332
|
return;
|
|
@@ -5535,8 +5534,7 @@ export class Runner {
|
|
|
5535
5534
|
async teardownOwnedEnv(prepared) {
|
|
5536
5535
|
if (prepared.ownedEnv &&
|
|
5537
5536
|
hasDestroy(prepared.ownedEnv) &&
|
|
5538
|
-
prepared.
|
|
5539
|
-
prepared.reviewRef.token === undefined) {
|
|
5537
|
+
prepared.pausedRef.current === undefined) {
|
|
5540
5538
|
try {
|
|
5541
5539
|
await prepared.ownedEnv.destroy();
|
|
5542
5540
|
}
|