@sema-agent/core 7.5.1 → 7.6.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 +61 -0
- package/dist/agents/cascade.d.ts +2 -2
- package/dist/agents/cascade.js +12 -10
- package/dist/agents/repair-loop.d.ts +5 -3
- package/dist/agents/repair-loop.js +13 -15
- package/dist/agents/subagent.d.ts +24 -42
- 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.js +9 -9
- package/dist/agents/team.d.ts +4 -3
- package/dist/agents/team.js +10 -8
- package/dist/agents/verify.d.ts +3 -3
- package/dist/agents/verify.js +17 -17
- package/dist/core/a2a.js +2 -1
- 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/gate-outcome.d.ts +189 -0
- package/dist/core/gate-outcome.js +70 -0
- package/dist/core/hooks.d.ts +18 -92
- package/dist/core/hooks.js +88 -85
- package/dist/core/mcp-failure.d.ts +104 -0
- package/dist/core/mcp-failure.js +128 -0
- package/dist/core/mcp.d.ts +21 -77
- package/dist/core/mcp.js +76 -150
- package/dist/core/pause-registry.d.ts +131 -0
- package/dist/core/pause-registry.js +27 -0
- package/dist/core/protocol-table.d.ts +5 -0
- package/dist/core/protocol-table.js +1 -0
- package/dist/core/runner/abort-race.d.ts +41 -0
- package/dist/core/runner/abort-race.js +38 -0
- package/dist/core/runner/assemble-result.d.ts +32 -41
- package/dist/core/runner/assemble-result.js +55 -74
- package/dist/core/runner/checkpoint-scope.d.ts +15 -3
- package/dist/core/runner/checkpoint-scope.js +3 -0
- package/dist/core/runner/compaction-call-options.d.ts +1 -1
- package/dist/core/runner/content-ask-bindings.d.ts +27 -0
- package/dist/core/runner/content-ask-bindings.js +1 -0
- package/dist/core/runner/contracts.d.ts +144 -50
- package/dist/core/runner/denial-limit-arms.d.ts +24 -1
- package/dist/core/runner/denial-limit-arms.js +24 -3
- package/dist/core/runner/gate-exit.d.ts +74 -0
- package/dist/core/runner/gate-exit.js +55 -0
- package/dist/core/runner/inherited-ask-grants.d.ts +46 -0
- package/dist/core/runner/inherited-ask-grants.js +29 -0
- package/dist/core/runner/park-commit.d.ts +102 -0
- package/dist/core/runner/park-commit.js +31 -0
- package/dist/core/runner/{prepare-permission-rules.d.ts → permission-rule-lanes.d.ts} +109 -3
- package/dist/core/runner/{prepare-permission-rules.js → permission-rule-lanes.js} +47 -1
- package/dist/core/runner/prepare-ask-lane.d.ts +107 -0
- package/dist/core/runner/prepare-ask-lane.js +131 -0
- package/dist/core/runner/prepare-boundary-parks.d.ts +102 -0
- package/dist/core/runner/prepare-boundary-parks.js +169 -0
- package/dist/core/runner/prepare-caps-and-workflow.js +1 -1
- package/dist/core/runner/prepare-context-lane.d.ts +119 -0
- package/dist/core/runner/prepare-context-lane.js +230 -0
- package/dist/core/runner/prepare-gate-stations.d.ts +174 -0
- package/dist/core/runner/prepare-gate-stations.js +265 -0
- package/dist/core/runner/prepare-hands-readface.d.ts +4 -4
- package/dist/core/runner/prepare-inherited-gate.d.ts +4 -4
- package/dist/core/runner/prepare-inherited-gate.js +1 -1
- package/dist/core/runner/prepare-memory-engine-session.d.ts +84 -0
- package/dist/core/runner/prepare-memory-engine-session.js +233 -0
- package/dist/core/runner/prepare-memory.d.ts +44 -26
- package/dist/core/runner/prepare-park-ask.d.ts +162 -0
- package/dist/core/runner/prepare-park-ask.js +377 -0
- package/dist/core/runner/prepare-policy-chain.d.ts +208 -0
- package/dist/core/runner/prepare-policy-chain.js +584 -0
- package/dist/core/runner/prepare-project-context.d.ts +1 -13
- package/dist/core/runner/prepare-project-context.js +1 -3
- package/dist/core/runner/prepare-prompt-assembly.d.ts +95 -0
- package/dist/core/runner/prepare-prompt-assembly.js +162 -0
- package/dist/core/runner/prepare-prompt-inputs.d.ts +1 -20
- package/dist/core/runner/prepare-protocol-tools.d.ts +3 -3
- package/dist/core/runner/prepare-protocol-tools.js +0 -3
- package/dist/core/runner/prepare-question-face.d.ts +3 -21
- package/dist/core/runner/prepare-question-face.js +2 -1
- package/dist/core/runner/prepare-safety-scan.d.ts +0 -5
- package/dist/core/runner/prepare-safety-scan.js +1 -2
- package/dist/core/runner/prepare-suspend-saga.d.ts +170 -0
- package/dist/core/runner/prepare-suspend-saga.js +308 -0
- package/dist/core/runner/prepare-task.d.ts +9 -136
- package/dist/core/runner/prepare-task.js +47 -2745
- package/dist/core/runner/prepare-turn-wiring.d.ts +154 -0
- package/dist/core/runner/prepare-turn-wiring.js +201 -0
- package/dist/core/runner/prepare-wiring-manifest.d.ts +17 -17
- package/dist/core/runner/prepare-wiring-manifest.js +16 -10
- package/dist/core/runner/prepare-workspace-restore.d.ts +2 -29
- package/dist/core/runner/prepare-workspace-restore.js +3 -16
- package/dist/core/runner/prompt-hash-salt.d.ts +1 -0
- package/dist/core/runner/prompt-hash-salt.js +2 -0
- package/dist/core/runner/remote-env-retry.d.ts +29 -0
- package/dist/core/runner/remote-env-retry.js +16 -0
- package/dist/core/runner/runtask.d.ts +16 -31
- package/dist/core/runner/runtask.js +110 -121
- package/dist/core/runner/terminal-projection.d.ts +22 -0
- package/dist/core/runner/terminal-projection.js +28 -0
- package/dist/core/session.d.ts +12 -0
- package/dist/core/session.js +3 -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/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/types.d.ts +67 -164
- 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 +10 -7
- package/dist/index.js +8 -5
- package/dist/orchestration/builtin-workflows.d.ts +2 -2
- package/dist/orchestration/builtin-workflows.js +1 -1
- package/dist/orchestration/goal.js +8 -7
- package/dist/orchestration/run-spec.js +5 -3
- package/dist/orchestration/run-workflow-tool.d.ts +1 -1
- 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 +11 -0
- 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.js +1 -1
- package/package.json +3 -1
- package/test/export-surface.snapshot.json +74 -22
- /package/dist/core/runner/{prepare-announce-once.d.ts → announce-once-ledger.d.ts} +0 -0
- /package/dist/core/runner/{prepare-announce-once.js → announce-once-ledger.js} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AssistantMessage } from "../../internal/llm.js";
|
|
2
|
-
import type { NestedUsage, TaskResult, TaskSpec } from "../types.js";
|
|
2
|
+
import type { NestedUsage, PausedCause, TaskResult, TaskSpec, TerminalCause } from "../types.js";
|
|
3
3
|
/** Per-task usage/cost accumulator (filled across the run loop, surfaced as `TaskResult.stats`). */
|
|
4
4
|
export interface Stats {
|
|
5
5
|
turns: number;
|
|
@@ -194,40 +194,16 @@ export interface ResultFlags {
|
|
|
194
194
|
* NOT `suspended` — slotted ABOVE `suspendRef` and the aborted branches (this suspend never minted a
|
|
195
195
|
* checkpoint, so `suspendRef` is unset anyway; the ordering makes the intent explicit). */
|
|
196
196
|
suspendLoop?: boolean;
|
|
197
|
-
/**
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
/** RB-439-b: how the paused workspace comes back (`"park_only"` = a non-suspendable target that was
|
|
208
|
-
* never actually paused). Echoed on `TaskResult.workspaceRestoreMode`; absent for a process-local
|
|
209
|
-
* suspend, which captured no remote workspace. */
|
|
210
|
-
restoreMode?: "snapshot" | "park_only";
|
|
211
|
-
};
|
|
212
|
-
/** design/76 §2.5 (dry-run / shadow) + design/80 D-B (plan-gate): the task deliberately paused at a
|
|
213
|
-
* REVIEW-PAUSE gate — `{kind:"needs_review"}` (a profile's dry-run interception produced a predicted
|
|
214
|
-
* state-diff a human/judge must REVIEW) OR `{kind:"plan_review"}` (a profile's plan-gate produced a
|
|
215
|
-
* proposed PLAN a human must approve/edit/reject). When set, the result is `status:"needs_review"` +
|
|
216
|
-
* errorCode `"review.pending"`, carrying this token/gate (keyed off ref-PRESENCE, not `gate.kind`, so it
|
|
217
|
-
* serves both review kinds uniformly). It is the DUAL of `suspendRef` but for DIFFERENT semantics (a human
|
|
218
|
-
* REVIEW, not a pre-action tool-call APPROVAL), so it has its OWN flag and its OWN assemble slot (8.6,
|
|
219
|
-
* between `suspendRef` 8.5 and the aborted branches 9) — **never both set at once** (the commit-side
|
|
220
|
-
* discriminant in prepare-task branches on `gate.kind` and sets exactly one). Disjoint from `suspendRef`
|
|
221
|
-
* so this branch is reachable: keying status off `suspendRef` alone (reusing it) would report `"suspended"`
|
|
222
|
-
* and make this dead code (v4 MAJOR-A). Like `suspendRef`, it used an abort to stop the loop (no `threw`),
|
|
223
|
-
* so it never hits the failure branches above. */
|
|
224
|
-
reviewRef?: {
|
|
225
|
-
token: import("../checkpoint-store.js").CheckpointToken;
|
|
226
|
-
checkpointId?: string;
|
|
227
|
-
gate: import("../checkpoint-store.js").CheckpointGate;
|
|
228
|
-
/** RB-439-b: the review pause pauses the same workspace the approval pause does — same discriminant. */
|
|
229
|
-
restoreMode?: "snapshot" | "park_only";
|
|
230
|
-
};
|
|
197
|
+
/** The run's ONE committed durable pause (the holder's `pausedRef.current.cause`): the run stopped at a
|
|
198
|
+
* gate — an approval or content ask, a resource slice, a platform pause, a plan or dry-run review —
|
|
199
|
+
* and a checkpoint is durably committed. It used an abort to stop the loop (`final.stopReason ===
|
|
200
|
+
* "aborted"`) but set no `threw`/timeout/turns, so it is slotted ABOVE the `!final`/aborted arms (a
|
|
201
|
+
* clean pause, not a failure) and BELOW the real budget/threw failures (a limit that coincided still
|
|
202
|
+
* owns the ending). WHICH terminal status it reads (`suspended` / `needs_review`) is the pause's own
|
|
203
|
+
* `gate.kind` registry row, not a second flag: the two same-shaped refs that used to split the approval
|
|
204
|
+
* family from the review family — and the slot order between them that only ever mattered if both were
|
|
205
|
+
* somehow set — are gone with the commit-side routing that fed them. */
|
|
206
|
+
paused?: PausedCause;
|
|
231
207
|
}
|
|
232
208
|
/**
|
|
233
209
|
* Extract a machine-readable code from a thrown error (e.g. `SessionError.code`, a Node error code).
|
|
@@ -236,6 +212,23 @@ export interface ResultFlags {
|
|
|
236
212
|
* prefer the first specific code over a generic `"unknown"` wrapper.
|
|
237
213
|
*/
|
|
238
214
|
export declare function errorCodeOf(err: unknown): string | undefined;
|
|
215
|
+
/**
|
|
216
|
+
* WHY the run ended — the ONE terminal arbitration ({@link TerminalCause}). First match wins, in the
|
|
217
|
+
* slot order documented on {@link assembleResult} (a public contract: do not reorder). Every plane
|
|
218
|
+
* field a result shows (`status`, `errorCode`, `errorMessage`, `blockedReason`, the checkpoint seats)
|
|
219
|
+
* is projected from the value this returns by {@link terminalProjection}, so a terminal that carries a
|
|
220
|
+
* token cannot be reported under a status that does not, and a review pause cannot be reported as an
|
|
221
|
+
* approval pause: the pairing is the cause's shape, not the arbitration's discipline.
|
|
222
|
+
*
|
|
223
|
+
* `apiFailure` rides beside the cause rather than inside it on purpose: it is the message's own
|
|
224
|
+
* provider-failure disclosure, filled only when a provider failure IS the terminal (the same
|
|
225
|
+
* in-presence rule the usage-window wait hint states for itself), and it is not part of the cause's
|
|
226
|
+
* public projection.
|
|
227
|
+
*/
|
|
228
|
+
export declare function terminalCauseOf(final: AssistantMessage | undefined, stats: Pick<Stats, "humanReview">, flags: ResultFlags): {
|
|
229
|
+
terminal: TerminalCause;
|
|
230
|
+
apiFailure: TaskResult["apiFailure"];
|
|
231
|
+
};
|
|
239
232
|
/**
|
|
240
233
|
* Build the final {@link TaskResult} from the run's terminal state. **The errorCode priority chain is a
|
|
241
234
|
* public contract** (callers `switch` on these strings) — first match wins, in this exact order
|
|
@@ -252,12 +245,10 @@ export declare function errorCodeOf(err: unknown): string | undefined;
|
|
|
252
245
|
* 6. `threw` + conflict→ `"conflict"`
|
|
253
246
|
* 7. `threw` (other) → `errorCodeOf(threw)`
|
|
254
247
|
* 8. `blockedReason` → status `"blocked"` (no errorCode)
|
|
255
|
-
* 8.5 `
|
|
256
|
-
*
|
|
257
|
-
*
|
|
258
|
-
*
|
|
259
|
-
* a `suspendRef` at 8.5 still wins if both were somehow set, proving slot order — but the commit-side
|
|
260
|
-
* discriminant guarantees only one is ever set)
|
|
248
|
+
* 8.5 `paused` → status by the pause's registry row (`suspended` for an approval / resource
|
|
249
|
+
* slice / platform pause, `needs_review` + `"review.pending"` for a plan or dry-run review) +
|
|
250
|
+
* checkpointToken/gate (design/45 B4 — a deliberate durable pause beats the abort it used, but
|
|
251
|
+
* yields to a real budget/threw failure above)
|
|
261
252
|
* 9. `abortedLive` | stopReason aborted → `"limits.max_walltime_exceeded"` | `"limits.max_turns_exceeded"` | undefined
|
|
262
253
|
* (audit A-1: hoisted ABOVE no-final — the loop exits cleanly on abort, so `final` may be a
|
|
263
254
|
* normal message or absent; a plain user interrupt carries NO errorCode — switch on `status`),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { isDegenerateCutMessage } from "../../brain/terminal-cause.js";
|
|
2
2
|
import { extractErrorCode, stripErrorCodePrefix } from "../../brain/errors.js";
|
|
3
|
+
import { terminalProjection } from "./terminal-projection.js";
|
|
3
4
|
const SALVAGE_ELIGIBLE_TERMINALS = new Set([
|
|
4
5
|
"output.degenerate",
|
|
5
6
|
"limits.max_tokens_exceeded",
|
|
@@ -40,30 +41,9 @@ function assistantText(msg) {
|
|
|
40
41
|
.join("");
|
|
41
42
|
return stripThinkTags(text);
|
|
42
43
|
}
|
|
43
|
-
export function
|
|
44
|
-
|
|
45
|
-
const text = final ? assistantText(final) : "";
|
|
46
|
-
if (!flags.unpricedSpend) {
|
|
47
|
-
const compactionMicroUsd = stats.compactionMicroUsd ?? 0;
|
|
48
|
-
const nestedSubagentMicroUsd = stats.nested?.costMicroUsd ?? 0;
|
|
49
|
-
stats.costBreakdown = {
|
|
50
|
-
llmRootMicroUsd: Math.max(0, stats.costMicroUsd - compactionMicroUsd),
|
|
51
|
-
nestedSubagentMicroUsd,
|
|
52
|
-
memoryConsolidationMicroUsd: 0,
|
|
53
|
-
compactionMicroUsd,
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
let status;
|
|
57
|
-
const result = text;
|
|
58
|
-
let errorMessage;
|
|
59
|
-
let errorCode;
|
|
44
|
+
export function terminalCauseOf(final, stats, flags) {
|
|
45
|
+
let terminal;
|
|
60
46
|
let apiFailure;
|
|
61
|
-
let blockedReason;
|
|
62
|
-
let salvagedOutput;
|
|
63
|
-
let checkpointToken;
|
|
64
|
-
let checkpointId;
|
|
65
|
-
let checkpointGate;
|
|
66
|
-
let workspaceRestoreMode;
|
|
67
47
|
const haltOwnsAbortedFinal = flags.userHalted === true &&
|
|
68
48
|
final?.stopReason === "aborted" &&
|
|
69
49
|
!flags.abortedLive &&
|
|
@@ -71,107 +51,108 @@ export function assembleResult(spec, sessionId, final, stats, flags) {
|
|
|
71
51
|
flags.abortedForTurns !== true;
|
|
72
52
|
const isDegenerate = final?.stopReason === "error" && isDegenerateCutMessage(final);
|
|
73
53
|
if (flags.outputInvalid) {
|
|
74
|
-
|
|
75
|
-
errorCode = "output.invalid";
|
|
76
|
-
errorMessage = "model did not produce a valid structured output within the retry limit";
|
|
54
|
+
terminal = { kind: "failed", code: "output.invalid", message: "model did not produce a valid structured output within the retry limit" };
|
|
77
55
|
}
|
|
78
56
|
else if (isDegenerate) {
|
|
79
|
-
|
|
80
|
-
errorCode = "output.degenerate";
|
|
81
|
-
errorMessage = final?.errorMessage;
|
|
57
|
+
terminal = { kind: "failed", code: "output.degenerate", ...(final?.errorMessage !== undefined ? { message: final.errorMessage } : {}) };
|
|
82
58
|
}
|
|
83
59
|
else if (flags.budgetHit) {
|
|
84
|
-
status = "failed";
|
|
85
|
-
errorCode = flags.budgetAxis === "tokens" ? "limits.max_tokens_exceeded" : "limits.max_cost_exceeded";
|
|
86
60
|
const axisName = flags.budgetAxis === "tokens" ? "limits.maxTokens" : "limits.maxCostUsd";
|
|
87
|
-
|
|
88
|
-
|
|
61
|
+
terminal = {
|
|
62
|
+
kind: "failed",
|
|
63
|
+
code: flags.budgetAxis === "tokens" ? "limits.max_tokens_exceeded" : "limits.max_cost_exceeded",
|
|
64
|
+
message: flags.budgetHit === "precall"
|
|
89
65
|
? `the estimated cost of the first call already exceeds ${axisName}; the task was not started`
|
|
90
|
-
: `cumulative usage exceeded ${axisName}
|
|
66
|
+
: `cumulative usage exceeded ${axisName}`,
|
|
67
|
+
};
|
|
91
68
|
}
|
|
92
69
|
else if (flags.suspendLoop) {
|
|
93
|
-
|
|
94
|
-
errorCode = "suspend.loop";
|
|
95
|
-
errorMessage = "task suspended too many times (resume/restart loop) — exceeded the suspend limit";
|
|
70
|
+
let message = "task suspended too many times (resume/restart loop) — exceeded the suspend limit";
|
|
96
71
|
const gates = stats.humanReview?.gates ?? [];
|
|
97
72
|
if (gates.length > 0 && gates.every((g) => g.decision === "allow")) {
|
|
98
|
-
|
|
73
|
+
message +=
|
|
99
74
|
`. Diagnosis: all ${gates.length} recorded gate decision(s) on this run were ALLOW — a gate that keeps ` +
|
|
100
75
|
`asking combined with an approver that keeps approving consumes the suspend allowance on legitimate ` +
|
|
101
76
|
`work. Remedies: answer asks at a LIVE onAsk (a synchronous allow parks nothing and consumes no ` +
|
|
102
77
|
`suspend), keep provably-benign commands inside the read boundary so the classifier auto-allows them, ` +
|
|
103
78
|
`or raise maxSuspends for genuinely approval-heavy tasks.`;
|
|
104
79
|
}
|
|
80
|
+
terminal = { kind: "failed", code: "suspend.loop", message };
|
|
105
81
|
}
|
|
106
82
|
else if (flags.threw) {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
errorCode = flags.abortedForTimeout
|
|
83
|
+
const message = flags.threw instanceof Error ? flags.threw.message : String(flags.threw);
|
|
84
|
+
const code = flags.abortedForTimeout
|
|
110
85
|
? "limits.max_walltime_exceeded"
|
|
111
86
|
: flags.abortedForTurns
|
|
112
87
|
? "limits.max_turns_exceeded"
|
|
113
88
|
: flags.conflict
|
|
114
89
|
? "conflict"
|
|
115
90
|
: errorCodeOf(flags.threw);
|
|
91
|
+
terminal = { kind: "failed", ...(code !== undefined ? { code } : {}), message };
|
|
116
92
|
}
|
|
117
93
|
else if (flags.blockedReason) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
status = "suspended";
|
|
123
|
-
checkpointToken = flags.suspendRef.token;
|
|
124
|
-
checkpointId = flags.suspendRef.checkpointId;
|
|
125
|
-
checkpointGate = flags.suspendRef.gate;
|
|
126
|
-
workspaceRestoreMode = flags.suspendRef.restoreMode;
|
|
127
|
-
}
|
|
128
|
-
else if (flags.reviewRef) {
|
|
129
|
-
status = "needs_review";
|
|
130
|
-
errorCode = "review.pending";
|
|
131
|
-
checkpointToken = flags.reviewRef.token;
|
|
132
|
-
checkpointId = flags.reviewRef.checkpointId;
|
|
133
|
-
checkpointGate = flags.reviewRef.gate;
|
|
134
|
-
workspaceRestoreMode = flags.reviewRef.restoreMode;
|
|
94
|
+
terminal = { kind: "blocked", reason: flags.blockedReason };
|
|
95
|
+
}
|
|
96
|
+
else if (flags.paused) {
|
|
97
|
+
terminal = flags.paused;
|
|
135
98
|
}
|
|
136
99
|
else if ((flags.abortedLive || final?.stopReason === "aborted") && !haltOwnsAbortedFinal) {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
errorMessage = final?.errorMessage ?? (flags.abortedForTurns ? "max turns exceeded" : "run aborted");
|
|
100
|
+
const code = flags.abortedForTimeout ? "limits.max_walltime_exceeded" : flags.abortedForTurns ? "limits.max_turns_exceeded" : undefined;
|
|
101
|
+
terminal = { kind: "failed", ...(code !== undefined ? { code } : {}), message: final?.errorMessage ?? (flags.abortedForTurns ? "max turns exceeded" : "run aborted") };
|
|
140
102
|
}
|
|
141
103
|
else if (flags.userHalted === true && (!final || haltOwnsAbortedFinal)) {
|
|
142
|
-
|
|
104
|
+
terminal = { kind: "completed" };
|
|
143
105
|
}
|
|
144
106
|
else if (!final) {
|
|
145
|
-
|
|
146
|
-
errorMessage = "no assistant message produced";
|
|
107
|
+
terminal = { kind: "failed", message: "no assistant message produced" };
|
|
147
108
|
}
|
|
148
109
|
else if (final.stopReason === "error") {
|
|
149
|
-
|
|
150
|
-
|
|
110
|
+
let message = final.errorMessage ?? "model error";
|
|
111
|
+
let code;
|
|
151
112
|
if (final.isApiErrorMessage === true) {
|
|
152
113
|
apiFailure = {
|
|
153
114
|
...(final.apiErrorStatus !== undefined ? { status: final.apiErrorStatus } : {}),
|
|
154
115
|
...(final.requestId !== undefined ? { requestId: final.requestId } : {}),
|
|
155
116
|
};
|
|
156
117
|
}
|
|
157
|
-
const lifted = extractErrorCode(
|
|
118
|
+
const lifted = extractErrorCode(message);
|
|
158
119
|
if (lifted) {
|
|
159
|
-
|
|
160
|
-
|
|
120
|
+
code = lifted;
|
|
121
|
+
message = stripErrorCodePrefix(message);
|
|
161
122
|
}
|
|
162
123
|
else if (flags.gitCoreOverBudget) {
|
|
163
|
-
|
|
124
|
+
code = "irreducible_core_over_budget";
|
|
164
125
|
}
|
|
165
126
|
else if (flags.conflict) {
|
|
166
|
-
|
|
127
|
+
code = "conflict";
|
|
167
128
|
}
|
|
168
129
|
else {
|
|
169
|
-
|
|
130
|
+
code = "provider.error";
|
|
170
131
|
}
|
|
132
|
+
terminal = { kind: "failed", code, message };
|
|
171
133
|
}
|
|
172
134
|
else {
|
|
173
|
-
|
|
135
|
+
terminal = { kind: "completed" };
|
|
174
136
|
}
|
|
137
|
+
return { terminal, apiFailure };
|
|
138
|
+
}
|
|
139
|
+
export function assembleResult(spec, sessionId, final, stats, flags) {
|
|
140
|
+
const taskId = spec.taskId ?? sessionId;
|
|
141
|
+
const text = final ? assistantText(final) : "";
|
|
142
|
+
if (!flags.unpricedSpend) {
|
|
143
|
+
const compactionMicroUsd = stats.compactionMicroUsd ?? 0;
|
|
144
|
+
const nestedSubagentMicroUsd = stats.nested?.costMicroUsd ?? 0;
|
|
145
|
+
stats.costBreakdown = {
|
|
146
|
+
llmRootMicroUsd: Math.max(0, stats.costMicroUsd - compactionMicroUsd),
|
|
147
|
+
nestedSubagentMicroUsd,
|
|
148
|
+
memoryConsolidationMicroUsd: 0,
|
|
149
|
+
compactionMicroUsd,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
const result = text;
|
|
153
|
+
let salvagedOutput;
|
|
154
|
+
const { terminal, apiFailure } = terminalCauseOf(final, stats, flags);
|
|
155
|
+
const { status, errorCode } = terminalProjection(terminal);
|
|
175
156
|
if (errorCode !== undefined && SALVAGE_ELIGIBLE_TERMINALS.has(errorCode)) {
|
|
176
157
|
salvagedOutput = text.trim() || undefined;
|
|
177
158
|
}
|
|
@@ -181,5 +162,5 @@ export function assembleResult(spec, sessionId, final, stats, flags) {
|
|
|
181
162
|
if (flags.unpricedSpend)
|
|
182
163
|
delete publicStats.costMicroUsd;
|
|
183
164
|
const stampHaltedByUser = flags.userHalted === true && status !== "suspended" && status !== "needs_review";
|
|
184
|
-
return { taskId, ...(flags.runId !== undefined ? { runId: flags.runId } : {}), sessionId,
|
|
165
|
+
return { taskId, ...(flags.runId !== undefined ? { runId: flags.runId } : {}), sessionId, terminal, ...(flags.model !== undefined ? { model: flags.model } : {}), result: result.trim(), salvagedOutput, ...(apiFailure !== undefined ? { apiFailure } : {}), ...(retryAfterMs !== undefined ? { retryAfterMs } : {}), ...(flags.rewindNotes !== undefined && flags.rewindNotes.length > 0 ? { rewindNotes: flags.rewindNotes } : {}), ...(flags.editedFiles !== undefined && flags.editedFiles.length > 0 ? { editedFiles: flags.editedFiles } : {}), ...(flags.haltedOnUserRejection === true ? { haltedOnUserRejection: true } : {}), ...(stampHaltedByUser ? { haltedByUser: true } : {}), ...(flags.remoteEnvFailures !== undefined && flags.remoteEnvFailures.length > 0 ? { remoteEnvFailures: [...flags.remoteEnvFailures] } : {}), ...(flags.strandedHumanAnswers !== undefined && flags.strandedHumanAnswers.length > 0 ? { strandedHumanAnswers: flags.strandedHumanAnswers } : {}), ...(flags.effectiveReadFace !== undefined ? { effectiveReadFace: flags.effectiveReadFace } : {}), ...(flags.effectiveReadDenyPatterns !== undefined && flags.effectiveReadDenyPatterns.length > 0 ? { effectiveReadDenyPatterns: flags.effectiveReadDenyPatterns } : {}), ...(flags.effectiveMemoryScopes !== undefined ? { effectiveMemoryScopes: flags.effectiveMemoryScopes } : {}), ...(flags.effectiveReasoning !== undefined ? { effectiveReasoning: flags.effectiveReasoning } : {}), stats: publicStats };
|
|
185
166
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The durable-suspend checkpoint
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* The durable-suspend checkpoint ROW-IDENTITY vocabulary — the default isolation scope, the ONE derivation of a row's
|
|
3
|
+
* scope, and the ONE reading of the placement fields' empty string — moved whole out of the orchestrator so the
|
|
4
|
+
* wiring-manifest phase (the synthesized durableApproval), the park-lane phases (the mint sites and the suspend stamp),
|
|
5
|
+
* the driver (the restore fold) and runtask (the resume rung) reach DOWN for the same names. Pure: a constant and two
|
|
6
|
+
* string functions.
|
|
5
7
|
*/
|
|
6
8
|
/** design/77 §4.4: the multi-tenant scope used when a durable suspend fires for an IRREVERSIBLE tool in an
|
|
7
9
|
* unattended deployment that did NOT opt into `durableApproval` (so there is no caller-supplied scope). The
|
|
@@ -30,3 +32,13 @@ export declare function checkpointScopeOf(spec: {
|
|
|
30
32
|
};
|
|
31
33
|
principal?: string;
|
|
32
34
|
}): string;
|
|
35
|
+
/**
|
|
36
|
+
* rescan C5 — the ONE spelling of the placement fields' empty-string discipline: `""` is absence
|
|
37
|
+
* wearing clothes (the resume entry's principal-rung posture), and every placement read that must
|
|
38
|
+
* treat it so — the resume rung's two sides (runtask), the restore fold's seed and the suspend
|
|
39
|
+
* stamp — goes through THIS helper, so three sites cannot drift into three readings. Deliberately
|
|
40
|
+
* NOT applied to the live `internals.placementRoot`/`rootSessionId` reads of the RESOLUTION fold:
|
|
41
|
+
* an empty supplied claim there is an assembly error the execution-env phase's `mintPlacementRootSessionId` refuses
|
|
42
|
+
* loudly on the factory path, and normalizing it away would silently repair what should be loud.
|
|
43
|
+
*/
|
|
44
|
+
export declare function placementValueOrAbsent(value: string | undefined): string | undefined;
|
|
@@ -2,3 +2,6 @@ export const DEFAULT_IRREVERSIBLE_SCOPE = "irreversible";
|
|
|
2
2
|
export function checkpointScopeOf(spec) {
|
|
3
3
|
return spec.durableApproval?.scope || spec.principal || DEFAULT_IRREVERSIBLE_SCOPE;
|
|
4
4
|
}
|
|
5
|
+
export function placementValueOrAbsent(value) {
|
|
6
|
+
return value || undefined;
|
|
7
|
+
}
|
|
@@ -3,7 +3,7 @@ import type { MaybeCompactOptions } from "../auto-compaction.js";
|
|
|
3
3
|
import type { StaleToolResultOffloadOptions, TaskSpec } from "../types.js";
|
|
4
4
|
import type { Context } from "../../internal/llm.js";
|
|
5
5
|
import { type ToolResultStore } from "../tool-result-store.js";
|
|
6
|
-
import type { Prepared } from "./
|
|
6
|
+
import type { Prepared } from "./contracts.js";
|
|
7
7
|
/**
|
|
8
8
|
* REF-A2 (car R11) — the maybeCompact call arguments that MUST be assembled identically on all three
|
|
9
9
|
* of runtask's compaction lanes (turn boundary / prompt-too-long recovery / end-of-task).
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The content-ask BINDING vocabulary: the leak bound the routing lane checks against and the one entry shape the
|
|
3
|
+
* binding table holds. It lived in the question-face phase (which mints the table) while the routing lane was still
|
|
4
|
+
* orchestrator code; the lane is a phase now, and a phase reaching SIDEWAYS into a sibling for a constant and a type
|
|
5
|
+
* is the wrong reading — both phases reach DOWN for them here.
|
|
6
|
+
*/
|
|
7
|
+
import type { QuestionAnswer } from "../ask-question.js";
|
|
8
|
+
export declare const CONTENT_ASK_BINDING_CAP = 32;
|
|
9
|
+
/** One gate-routed content-ask outcome, keyed by the call id it was produced for (see the table's own note). */
|
|
10
|
+
export type ContentAskBinding = ({
|
|
11
|
+
kind: "answered";
|
|
12
|
+
answer: QuestionAnswer;
|
|
13
|
+
} | {
|
|
14
|
+
kind: "failed";
|
|
15
|
+
error: unknown;
|
|
16
|
+
}) & {
|
|
17
|
+
/** Digest of the exact question batch this outcome belongs to. The call id alone is not enough
|
|
18
|
+
* to redeem a binding: it comes from the model/provider and can repeat, so an entry that
|
|
19
|
+
* outlived its call (its call was allowed but never executed) could otherwise be handed to a
|
|
20
|
+
* LATER call that happens to reuse the id. Matching on the batch as well means an outcome can
|
|
21
|
+
* only ever be delivered for the question it was produced for. */
|
|
22
|
+
questionsHash: string;
|
|
23
|
+
/** The engine-minted per-delivery identity (see AskQuestionRequest.deliveryId) — carried so a
|
|
24
|
+
* stranded-answer disclosure names DELIVERIES, not call ids: call ids can repeat, and a
|
|
25
|
+
* disclosure keyed on them would collapse two lost answers into one. */
|
|
26
|
+
deliveryId: string;
|
|
27
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const CONTENT_ASK_BINDING_CAP = 32;
|