@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
|
@@ -47,11 +47,21 @@ import type { PermissionResult } from "./tool-policy.js";
|
|
|
47
47
|
* an instruction rather than hesitation.
|
|
48
48
|
* - `denial_limit_fallback` — the auto-mode classifier reached its denial bound and handed the call
|
|
49
49
|
* to a person; the classifier stands BEFORE this ask, never after it.
|
|
50
|
-
* - `
|
|
51
|
-
*
|
|
52
|
-
*
|
|
50
|
+
* - `shell_gate_tighten` — the gate's own irreversibility tighten raised the ask over a tier the COARSE
|
|
51
|
+
* `shellGate` doctrine installed (a shell command under `shellGate: "classify"`/`"always"`), not an
|
|
52
|
+
* explicit per-tool mark. The gate records that its own tighten spoke; a policy stamping
|
|
53
|
+
* `decisionReason: "safety"` on its own ask does not become one.
|
|
54
|
+
* - `safety_tighten` — the gate's own post-fold tightens over an EXPLICIT fact of the call: the tool's
|
|
55
|
+
* egress mark, its irreversibility mark, a peer-message referral in auto mode, a write onto the
|
|
56
|
+
* write-protected table. Same recording rule as above.
|
|
57
|
+
* - `policy` — everything else: a deployment `ToolPolicy` ask or a mode-default ask. This is the
|
|
58
|
+
* hesitation family — the one an armed classifier exists to resolve. The two tighten words are
|
|
59
|
+
* classifier-eligible too (arming auto mode is the deployment's explicit choice to let the
|
|
60
|
+
* classifier be the explicit ask-resolution for those classes); they are separate words so a deny
|
|
61
|
+
* observer or an approval card can say WHICH engine layer raised the question, which one word
|
|
62
|
+
* could not.
|
|
53
63
|
*/
|
|
54
|
-
export declare const ASK_ORIGINS: readonly ["content_question", "unresolvable", "org_unavailable", "org_rule", "hook", "ask_rule", "denial_limit_fallback", "policy"];
|
|
64
|
+
export declare const ASK_ORIGINS: readonly ["content_question", "unresolvable", "org_unavailable", "org_rule", "hook", "ask_rule", "denial_limit_fallback", "shell_gate_tighten", "safety_tighten", "policy"];
|
|
55
65
|
export type AskOrigin = (typeof ASK_ORIGINS)[number];
|
|
56
66
|
/** Whether a value is a member of the closed origin set. Exported because the word crosses process
|
|
57
67
|
* boundaries: a host validating one it received must not hand-roll the set. */
|
|
@@ -77,6 +87,10 @@ export interface AskOriginFacts {
|
|
|
77
87
|
* read-back off the decision (a policy can put `decisionReason: "org_rule"` on its own ask; the
|
|
78
88
|
* gate's cell cannot be written by a policy). */
|
|
79
89
|
readonly org: "rule" | "unavailable" | undefined;
|
|
90
|
+
/** Which of the gate's OWN post-fold tightens minted the standing ask, if one did — again the gate's
|
|
91
|
+
* record of its own layer (a tighten replaces an `allow` wholesale, so at most one fires per pass),
|
|
92
|
+
* never a read-back of `decisionReason: "safety"` off a decision a policy could have composed. */
|
|
93
|
+
readonly tightened: "shell_gate" | "safety" | undefined;
|
|
80
94
|
}
|
|
81
95
|
/**
|
|
82
96
|
* The ONE derivation of an ask's origin, from the surviving decision plus the gate's own facts. The
|
|
@@ -89,6 +103,39 @@ export interface AskOriginFacts {
|
|
|
89
103
|
export declare function askOriginOf(decision: Extract<PermissionResult, {
|
|
90
104
|
action: "ask";
|
|
91
105
|
}>, facts: AskOriginFacts): AskOrigin;
|
|
106
|
+
/**
|
|
107
|
+
* Which origins IMPLY the `requiresRealApproval` bit, and WHERE that implication is verified — the
|
|
108
|
+
* consistency table {@link askOriginOf} reads. An ask of a must-carry origin exists so that no automatic
|
|
109
|
+
* lane clears it (the org's asks: "no configuration clears this, a person nods"; the classifier's
|
|
110
|
+
* fallback: no automatic lane may re-clear what the classifier already refused), so the bit must ride
|
|
111
|
+
* it. The bit is NOT derived from this table: the write sites each have their own reason, and a
|
|
112
|
+
* table-derived bit would have to either strip a safety policy's mandate or make every hook ask
|
|
113
|
+
* non-budgetable. The table only refuses the one shape that is a defect: a must-carry origin without
|
|
114
|
+
* the bit — and says where that refusal can honestly happen:
|
|
115
|
+
* - `"at_derivation"` — the origin is derived from a GATE fact only the engine writes (`facts.org`),
|
|
116
|
+
* so a missing bit is an engine defect and {@link askOriginOf} fails loud;
|
|
117
|
+
* - `"at_mint"` — the origin is derived from a decision MEMBER a policy may compose
|
|
118
|
+
* (`denialLimitFallback`: a self-declared one opts the policy's own ask out of automatic
|
|
119
|
+
* resolution, the safe direction, not a defect), so the invariant is pinned at the engine's two
|
|
120
|
+
* mint sites (the gate's fallback re-mint and the inherited station's) rather than thrown at
|
|
121
|
+
* derivation, where a policy's own object could trip it;
|
|
122
|
+
* - `false` — the origin says nothing about the bit: a `policy` ask may carry it (a safety policy's
|
|
123
|
+
* own mandate) or not, and both are legal.
|
|
124
|
+
*/
|
|
125
|
+
export declare const ORIGIN_IMPLIES_REAL_APPROVAL: {
|
|
126
|
+
readonly content_question: false;
|
|
127
|
+
readonly unresolvable: false;
|
|
128
|
+
readonly org_unavailable: "at_derivation";
|
|
129
|
+
readonly org_rule: "at_derivation";
|
|
130
|
+
readonly hook: false;
|
|
131
|
+
readonly ask_rule: false;
|
|
132
|
+
readonly denial_limit_fallback: "at_mint";
|
|
133
|
+
readonly shell_gate_tighten: false;
|
|
134
|
+
readonly safety_tighten: false;
|
|
135
|
+
readonly policy: false;
|
|
136
|
+
};
|
|
137
|
+
/** The fence over the consistency table: `never` while every origin has a row. */
|
|
138
|
+
export type RealApprovalTableCoversEveryOrigin = AssertAllKeysHandled<Exclude<AskOrigin, keyof typeof ORIGIN_IMPLIES_REAL_APPROVAL>>;
|
|
92
139
|
/**
|
|
93
140
|
* The auto-mode CLASSIFIER's table: may a live classifier verdict resolve an ask of this origin?
|
|
94
141
|
*
|
|
@@ -107,9 +154,13 @@ export declare function askOriginOf(decision: Extract<PermissionResult, {
|
|
|
107
154
|
* whether the person is asked at all, which is the rule's whole content.
|
|
108
155
|
* - `denial_limit_fallback`: the classifier already spoke (its bound is what minted this ask); an
|
|
109
156
|
* allow here would un-say the fallback, a block would count it twice.
|
|
110
|
-
* - `
|
|
111
|
-
*
|
|
112
|
-
*
|
|
157
|
+
* - `shell_gate_tighten` / `safety_tighten`: YES — the engine's own tightens are hesitation the
|
|
158
|
+
* deployment armed the classifier to resolve (the classify doctrine's whole point for the shell
|
|
159
|
+
* tier; the safety marks make the ask NON-BUDGETABLE at the durable park, a different axis from who
|
|
160
|
+
* may answer it live).
|
|
161
|
+
* - `policy`: YES — including a safety policy's `requiresRealApproval` ask (a live verdict is
|
|
162
|
+
* judgment, and arming auto mode is the deployment's explicit choice to let the classifier be the
|
|
163
|
+
* explicit ask-resolution for those classes).
|
|
113
164
|
* Exclusion is skip-not-deny: the ask flows the original chain (park / approver / headless deny) —
|
|
114
165
|
* narrower autonomy, never a lost ask.
|
|
115
166
|
*/
|
|
@@ -121,6 +172,8 @@ declare const CLASSIFIER_MAY_ANSWER: {
|
|
|
121
172
|
readonly hook: false;
|
|
122
173
|
readonly ask_rule: false;
|
|
123
174
|
readonly denial_limit_fallback: false;
|
|
175
|
+
readonly shell_gate_tighten: true;
|
|
176
|
+
readonly safety_tighten: true;
|
|
124
177
|
readonly policy: true;
|
|
125
178
|
};
|
|
126
179
|
/** The fence over the classifier table: `never` while every origin has a row (pinned by a type test). */
|
package/dist/core/ask-origin.js
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
|
-
export const ASK_ORIGINS = ["content_question", "unresolvable", "org_unavailable", "org_rule", "hook", "ask_rule", "denial_limit_fallback", "policy"];
|
|
1
|
+
export const ASK_ORIGINS = ["content_question", "unresolvable", "org_unavailable", "org_rule", "hook", "ask_rule", "denial_limit_fallback", "shell_gate_tighten", "safety_tighten", "policy"];
|
|
2
2
|
const ASK_ORIGIN_SET = new Set(ASK_ORIGINS);
|
|
3
3
|
export function isAskOrigin(v) {
|
|
4
4
|
return ASK_ORIGIN_SET.has(v);
|
|
5
5
|
}
|
|
6
6
|
export function askOriginOf(decision, facts) {
|
|
7
|
+
const origin = originLadder(decision, facts);
|
|
8
|
+
if (ORIGIN_IMPLIES_REAL_APPROVAL[origin] === "at_derivation" && decision.requiresRealApproval !== true) {
|
|
9
|
+
throw new Error(`ask origin "${origin}" requires the requiresRealApproval bit on the decision it was derived from, and the decision carries none — an engine mint site dropped the bit`);
|
|
10
|
+
}
|
|
11
|
+
return origin;
|
|
12
|
+
}
|
|
13
|
+
function originLadder(decision, facts) {
|
|
7
14
|
if (facts.contentQuestion)
|
|
8
15
|
return "content_question";
|
|
9
16
|
if (facts.markedUnresolvable)
|
|
@@ -18,8 +25,24 @@ export function askOriginOf(decision, facts) {
|
|
|
18
25
|
return "ask_rule";
|
|
19
26
|
if (decision.denialLimitFallback !== undefined)
|
|
20
27
|
return "denial_limit_fallback";
|
|
28
|
+
if (facts.tightened === "shell_gate")
|
|
29
|
+
return "shell_gate_tighten";
|
|
30
|
+
if (facts.tightened === "safety")
|
|
31
|
+
return "safety_tighten";
|
|
21
32
|
return "policy";
|
|
22
33
|
}
|
|
34
|
+
export const ORIGIN_IMPLIES_REAL_APPROVAL = {
|
|
35
|
+
content_question: false,
|
|
36
|
+
unresolvable: false,
|
|
37
|
+
org_unavailable: "at_derivation",
|
|
38
|
+
org_rule: "at_derivation",
|
|
39
|
+
hook: false,
|
|
40
|
+
ask_rule: false,
|
|
41
|
+
denial_limit_fallback: "at_mint",
|
|
42
|
+
shell_gate_tighten: false,
|
|
43
|
+
safety_tighten: false,
|
|
44
|
+
policy: false,
|
|
45
|
+
};
|
|
23
46
|
const CLASSIFIER_MAY_ANSWER = {
|
|
24
47
|
content_question: false,
|
|
25
48
|
unresolvable: false,
|
|
@@ -28,6 +51,8 @@ const CLASSIFIER_MAY_ANSWER = {
|
|
|
28
51
|
hook: false,
|
|
29
52
|
ask_rule: false,
|
|
30
53
|
denial_limit_fallback: false,
|
|
54
|
+
shell_gate_tighten: true,
|
|
55
|
+
safety_tighten: true,
|
|
31
56
|
policy: true,
|
|
32
57
|
};
|
|
33
58
|
export function classifierMayAnswer(origin) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { CheckpointPause } from "./pause-registry.js";
|
|
1
2
|
import type { SystemInjectionPriority } from "./task-notification.js";
|
|
2
3
|
import { type QuestionAnswer } from "./ask-question.js";
|
|
3
4
|
import type { ReadEntry } from "../tools/fs/safety.js";
|
|
@@ -439,10 +440,21 @@ export declare function buildRiskDescriptor(input: {
|
|
|
439
440
|
* refuses to redeem such a row (the governed boundary must not vanish with a deployment's wiring).
|
|
440
441
|
*/
|
|
441
442
|
export interface RealApprovalGateBit {
|
|
442
|
-
/** The
|
|
443
|
-
*
|
|
444
|
-
|
|
443
|
+
/** The parked ask's ORIGIN word — a projection of the same closed set the card, the observer and the
|
|
444
|
+
* `tool_end` frame carry ({@link import("./ask-origin.js").AskOrigin}), never a second vocabulary. It
|
|
445
|
+
* is the ask's ROUTING word: a marked call reads `unresolvable` even when the org governs it, because
|
|
446
|
+
* the ladder routes a marked call to the park before anything else. Rows minted before the projection
|
|
447
|
+
* carried a three-word subset of this set (`org_rule` / `org_unavailable` / `policy`), so every stored
|
|
448
|
+
* value is still a member. */
|
|
449
|
+
origin: import("./ask-origin.js").AskOrigin;
|
|
450
|
+
/** The ORG FACT the ask was minted under (`"rule"` = an org rule asked, `"unavailable"` = org adjudication
|
|
451
|
+
* unreachable), kept beside the routing word because routing can outrank the word (a marked call reads
|
|
452
|
+
* `unresolvable`) while the governance stays. The #120 governed mark and the resume belts read THIS
|
|
453
|
+
* member through {@link realApprovalOrgFact} (absent on rows minted before it: the org words imply it). */
|
|
454
|
+
org?: "rule" | "unavailable";
|
|
445
455
|
}
|
|
456
|
+
/** The ONE read of a bit's governed fact (the member; the two org origin words for rows minted before it). */
|
|
457
|
+
export declare function realApprovalOrgFact(bit: RealApprovalGateBit | undefined): "rule" | "unavailable" | undefined;
|
|
446
458
|
export type CheckpointGate =
|
|
447
459
|
/** F4: a human (or any external authority) must allow/deny a pending tool call. design/80 §D-E:
|
|
448
460
|
* carries an OPTIONAL display-only {@link RiskDescriptor} (severity/axes/summary) for the supervisor
|
|
@@ -594,66 +606,38 @@ export type ResumeOutcome = {
|
|
|
594
606
|
*/
|
|
595
607
|
answer?: QuestionAnswer;
|
|
596
608
|
/**
|
|
597
|
-
*
|
|
598
|
-
*
|
|
599
|
-
*
|
|
600
|
-
*
|
|
609
|
+
* The host's DECISION FACTS — who ended this wait: a person (`"person"`, the decide an operator made
|
|
610
|
+
* through the deployment's approval channel, with the attribution that channel reports), or the
|
|
611
|
+
* host's own SLA sweep at the row's deadline (`"sla_timeout"` — FACET A of the deadline contract; only
|
|
612
|
+
* a sweep may say it, and a sweep cannot `allow`). REQUIRED: a decide that names nobody is not a
|
|
613
|
+
* record of anything that happened, and core does not guess.
|
|
601
614
|
*
|
|
602
|
-
*
|
|
603
|
-
*
|
|
604
|
-
*
|
|
605
|
-
*
|
|
606
|
-
*
|
|
607
|
-
* `tool_end` frame of the call this resume resolves.
|
|
615
|
+
* The host supplies FACTS, never the settlement WORD: core mints the record
|
|
616
|
+
* ({@link import("./gate-outcome.js").GateOutcome} — `human_allowed` / `human_refused` / `park_sla_expired`,
|
|
617
|
+
* joined with the row's persisted `origin`) at the resume entry, writes it onto the resolved row
|
|
618
|
+
* (`resolvedOutcome.gateOutcome`) and projects it onto the resolved call's `tool_end` frame. A caller
|
|
619
|
+
* that supplies `gateOutcome` itself is refused pre-CAS.
|
|
608
620
|
*
|
|
609
|
-
*
|
|
610
|
-
*
|
|
611
|
-
*
|
|
612
|
-
*
|
|
621
|
+
* `approver` — the attribution the approval channel reports for the party that ended the wait. Core
|
|
622
|
+
* AUTHENTICATES NOTHING: the identity work is the deployment's (its card, its signature check, its
|
|
623
|
+
* console). What core owns is that the claim is well-formed: a plain string, bounded, free of control
|
|
624
|
+
* characters, refused pre-CAS (`checkpoint.invalid_outcome`) otherwise — refused rather than dropped,
|
|
625
|
+
* because a settlement that silently lost its attributed party reads as unattributed.
|
|
613
626
|
*
|
|
614
|
-
*
|
|
615
|
-
*
|
|
616
|
-
*
|
|
617
|
-
* (`checkpoint.invalid_outcome`), like every other out-of-domain field on this arm: a settlement
|
|
618
|
-
* source nobody can read is worse than none at all. So is one that contradicts its own decision:
|
|
619
|
-
* `decision:"allow"` accepts only `"human"` or omission, because "nobody answered, therefore it
|
|
620
|
-
* ran" is not a record of anything that happened (a deployment whose CONFIGURATION auto-allows an
|
|
621
|
-
* elapsed window omits the field — no one ended that wait).
|
|
622
|
-
*
|
|
623
|
-
* SCOPE — this rides the settlement, NOT the persisted winner: `winnerFromOutcome` records the
|
|
624
|
-
* decision (and its rewrite/answer/reason), which is what an `env_failed` replay must reproduce
|
|
625
|
-
* exactly, and the source is not part of that authorization. A replay therefore states its own
|
|
626
|
-
* source and is not held to the first one. Binding it into the persisted winner would also make a
|
|
627
|
-
* re-supply that merely OMITS the field fail closed on the recovery path, which is the wrong place
|
|
628
|
-
* to add a new refusal; carrying it into the durable row is a store-shape change owned jointly
|
|
629
|
-
* with the backends and belongs to its own change, not to this one.
|
|
627
|
+
* SCOPE — the facts ride the settlement, not the persisted winner: `winnerFromOutcome` records what an
|
|
628
|
+
* `env_failed` replay must reproduce exactly, and who reported a settlement is not part of that
|
|
629
|
+
* authorization; a replay states its own facts.
|
|
630
630
|
*/
|
|
631
|
-
|
|
631
|
+
hostDecision: {
|
|
632
|
+
decidedBy: "person" | "sla_timeout";
|
|
633
|
+
approver?: string;
|
|
634
|
+
};
|
|
632
635
|
/**
|
|
633
|
-
*
|
|
634
|
-
*
|
|
635
|
-
*
|
|
636
|
-
*
|
|
637
|
-
* Core AUTHENTICATES NOTHING here. On the durable lane the identity work is entirely the
|
|
638
|
-
* deployment's — its approval card, its signature check, its operator console decide who this
|
|
639
|
-
* was; a library holding no identity surface has nothing to check the claim against and does not
|
|
640
|
-
* pretend otherwise. What core owns is that the claim is well-formed: a plain string, bounded,
|
|
641
|
-
* free of control characters, refused pre-CAS (`checkpoint.invalid_outcome`) otherwise, like
|
|
642
|
-
* every other out-of-domain field on this arm. Refused rather than dropped, because a settlement
|
|
643
|
-
* that silently lost its attributed party reads as unattributed — the misreport this seat exists
|
|
644
|
-
* to end.
|
|
645
|
-
*
|
|
646
|
-
* SCOPE — like {@link settledBy}, this rides the SETTLEMENT and not the persisted winner:
|
|
647
|
-
* `winnerFromOutcome` records what an `env_failed` replay must reproduce exactly, and who
|
|
648
|
-
* reported a settlement is not part of that authorization. A replay states its own attribution
|
|
649
|
-
* and is not held to the first one; binding it into the durable row would also make a re-supply
|
|
650
|
-
* that merely omits the field fail closed on the recovery path. Carrying it into the row is a
|
|
651
|
-
* store-shape change owned jointly with the backends and belongs to its own change.
|
|
652
|
-
*
|
|
653
|
-
* OPTIONAL and additive: absent means the caller named nobody, which is not the same as "nobody
|
|
654
|
-
* approved this" and must not be read as one.
|
|
636
|
+
* CORE-MINTED, on the persisted twin only: the settlement record this decide produced. Present on
|
|
637
|
+
* the row's `resolvedOutcome` after the resolve, and on the `tool_end` frame of the resolved call.
|
|
638
|
+
* Never accepted from a caller (refused pre-CAS) — the host does not mint words.
|
|
655
639
|
*/
|
|
656
|
-
|
|
640
|
+
gateOutcome?: import("./gate-outcome.js").GateOutcome;
|
|
657
641
|
}
|
|
658
642
|
/** design/74: continue a resource-suspended run with the next slice's allowance. NO decision payload and
|
|
659
643
|
* NO budget figure — the allowance is computed from {@link Checkpoint.resourceLedger}, so money never
|
|
@@ -874,6 +858,13 @@ export type PendingAction = {
|
|
|
874
858
|
kind: "resource_limit";
|
|
875
859
|
reason: ResourceLimitReason;
|
|
876
860
|
}
|
|
861
|
+
/** design/76 §2.5: paused at a POST-PREDICTION REVIEW (`CheckpointGate.kind === "needs_review"`). There is NO
|
|
862
|
+
* pending tool to resolve — a human/judge reviews the buffered predicted state-diff (a PROFILE-held REF, never
|
|
863
|
+
* stored here) — so this arm has no fields. Its OWN member: before the pause registry a `needs_review` row
|
|
864
|
+
* borrowed another arm's seat, and the gate × pendingAction pairing lived in a comment. */
|
|
865
|
+
| {
|
|
866
|
+
kind: "review";
|
|
867
|
+
}
|
|
877
868
|
/** design/80 D-B (r3 — an EXPLICIT arm, NOT a reused `resource_limit` placeholder): paused at a PRE-ACTION
|
|
878
869
|
* PLAN REVIEW (`CheckpointGate.kind === "plan_review"`). There is NO pending tool to resolve — the human
|
|
879
870
|
* reviews the PLAN, not a tool call — so this arm has no tool fields. Every consumer that reads the
|
|
@@ -1496,6 +1487,11 @@ export interface ResolvedOutcome {
|
|
|
1496
1487
|
* the same decision" for no delivery benefit, since a replay is produced by re-presenting the same
|
|
1497
1488
|
* outcome, not by reconstructing an equivalent one. */
|
|
1498
1489
|
reason?: string;
|
|
1490
|
+
/** The settlement record core minted for this decide ({@link import("./gate-outcome.js").GateOutcome}):
|
|
1491
|
+
* the row's own copy of what the resolved call's `tool_end` frame carries, written with the winner so
|
|
1492
|
+
* an audit reading the row reads the same record the stream carried. NOT part of the replay equality
|
|
1493
|
+
* (`when` differs per replay; the record is a consequence of the decision, not its substance). */
|
|
1494
|
+
gateOutcome?: import("./gate-outcome.js").GateOutcome;
|
|
1499
1495
|
}
|
|
1500
1496
|
/**
|
|
1501
1497
|
* design/80 D-1: why a consumed checkpoint was {@link CheckpointStore.reopen | reopened} (resolved →
|
|
@@ -1579,7 +1575,9 @@ export type TerminalClaimOutcome = {
|
|
|
1579
1575
|
};
|
|
1580
1576
|
/** A persisted suspension point: enough to resume a task on any replica. `status` drives the 3-state
|
|
1581
1577
|
* machine (pending → resolved | expired) that makes resume idempotent (§5). */
|
|
1582
|
-
|
|
1578
|
+
/** A durable checkpoint: the row's fields ({@link CheckpointRow}) plus its registry-bound pause pair. */
|
|
1579
|
+
export type Checkpoint = CheckpointRow & CheckpointPause;
|
|
1580
|
+
export interface CheckpointRow {
|
|
1583
1581
|
token: CheckpointToken;
|
|
1584
1582
|
/** Non-secret stable identity ({@link mintCheckpointId}) — the display/correlation twin of
|
|
1585
1583
|
* the secret `token` (identification vs capability, separate axes). Present on every row minted
|
|
@@ -1600,8 +1598,6 @@ export interface Checkpoint {
|
|
|
1600
1598
|
sessionId: string;
|
|
1601
1599
|
/** The session leaf the suspend happened at — the resume point (CAS write base, §5). */
|
|
1602
1600
|
leafId: string;
|
|
1603
|
-
gate: CheckpointGate;
|
|
1604
|
-
pendingAction: PendingAction;
|
|
1605
1601
|
state: CheckpointState;
|
|
1606
1602
|
/** design/74 R3-B: cross-slice resource accounting. Present for a `resource_limit` gate (debited at the
|
|
1607
1603
|
* suspend's `put`; read on resume to size the next slice + aggregate the final stats). **design/80 D-E-core
|
|
@@ -2075,15 +2071,15 @@ export declare class CheckpointError extends Error {
|
|
|
2075
2071
|
* re-fetch the pending list), `"boundInputHash"` (the input you reviewed has changed under the same
|
|
2076
2072
|
* action: re-review), or `"answer"` (the content-ask answer is missing, or was attached to a
|
|
2077
2073
|
* decision that cannot carry one: re-decide with a well-formed answer — ruled 2026-08-04), or
|
|
2078
|
-
* `"
|
|
2079
|
-
*
|
|
2074
|
+
* `"hostDecision"` (the decision facts are missing, name a decider outside `person`/`sla_timeout`, pair
|
|
2075
|
+
* an SLA timeout with an allow, or try to supply the minted word), or — design/252 G-7 — `"approver"` (the attribution is not a
|
|
2080
2076
|
* well-formed identifier: re-decide with a plain, bounded, control-character-free string, or omit
|
|
2081
2077
|
* it). The throw
|
|
2082
2078
|
* sites always knew which arm fired; this carries that fact instead of dropping it (ruled
|
|
2083
2079
|
* 2026-08-03, cross-repo HITL discussion). Additive and optional — `code` remains the only REQUIRED
|
|
2084
2080
|
* discriminant. */
|
|
2085
2081
|
readonly detail?: {
|
|
2086
|
-
field?: "boundCallId" | "boundInputHash" | "answer" | "
|
|
2082
|
+
field?: "boundCallId" | "boundInputHash" | "answer" | "hostDecision" | "approver";
|
|
2087
2083
|
/** #449 G1 — on `resume.usage_window_exhausted`: how long until the binding window frees
|
|
2088
2084
|
* (the same wait a suspended row carries as `gate.resumeAfterMs`). design/380 O2 — on a
|
|
2089
2085
|
* retry-later `resume.preflight_rejected`: the deployment preflight's own wait hint, when it
|
|
@@ -2257,15 +2253,15 @@ export declare class CheckpointError extends Error {
|
|
|
2257
2253
|
* re-fetch the pending list), `"boundInputHash"` (the input you reviewed has changed under the same
|
|
2258
2254
|
* action: re-review), or `"answer"` (the content-ask answer is missing, or was attached to a
|
|
2259
2255
|
* decision that cannot carry one: re-decide with a well-formed answer — ruled 2026-08-04), or
|
|
2260
|
-
* `"
|
|
2261
|
-
*
|
|
2256
|
+
* `"hostDecision"` (the decision facts are missing, name a decider outside `person`/`sla_timeout`, pair
|
|
2257
|
+
* an SLA timeout with an allow, or try to supply the minted word), or — design/252 G-7 — `"approver"` (the attribution is not a
|
|
2262
2258
|
* well-formed identifier: re-decide with a plain, bounded, control-character-free string, or omit
|
|
2263
2259
|
* it). The throw
|
|
2264
2260
|
* sites always knew which arm fired; this carries that fact instead of dropping it (ruled
|
|
2265
2261
|
* 2026-08-03, cross-repo HITL discussion). Additive and optional — `code` remains the only REQUIRED
|
|
2266
2262
|
* discriminant. */
|
|
2267
2263
|
detail?: {
|
|
2268
|
-
field?: "boundCallId" | "boundInputHash" | "answer" | "
|
|
2264
|
+
field?: "boundCallId" | "boundInputHash" | "answer" | "hostDecision" | "approver";
|
|
2269
2265
|
/** #449 G1 — on `resume.usage_window_exhausted`: how long until the binding window frees
|
|
2270
2266
|
* (the same wait a suspended row carries as `gate.resumeAfterMs`). design/380 O2 — on a
|
|
2271
2267
|
* retry-later `resume.preflight_rejected`: the deployment preflight's own wait hint, when it
|
|
@@ -2674,22 +2670,28 @@ export interface CheckpointStore {
|
|
|
2674
2670
|
* The SINGLE resolution of "which CheckpointStore does this run use" (ruled 2026-08-04; re-homed
|
|
2675
2671
|
* from prepare-task with the design/173 wiring manifest so the static self-check half shares it
|
|
2676
2672
|
* without a runtime cycle). The spec seat is three-valued — a store wins, `undefined` falls through
|
|
2677
|
-
* to the deployment's `RunnerDeps.checkpointStore`, and
|
|
2678
|
-
* store at all (see `TaskSpec.checkpointStore`: the per-run off switch for the durable
|
|
2679
|
-
* a machine-started run cannot mint a checkpoint nobody will ever resolve).
|
|
2673
|
+
* to the deployment's `RunnerDeps.checkpointStore`, and the explicit word `"disabled"` means this run
|
|
2674
|
+
* has NO store at all (see `TaskSpec.checkpointStore`: the per-run off switch for the durable
|
|
2675
|
+
* machine, so a machine-started run cannot mint a checkpoint nobody will ever resolve).
|
|
2680
2676
|
*
|
|
2681
|
-
*
|
|
2682
|
-
*
|
|
2683
|
-
*
|
|
2684
|
-
* it
|
|
2685
|
-
*
|
|
2686
|
-
*
|
|
2677
|
+
* The off switch is a WORD, not `null`, on purpose: `??` treats `null` exactly like `undefined`, so
|
|
2678
|
+
* the old null sentinel silently fell through a bare coalesce to the very deployment store the
|
|
2679
|
+
* caller asked not to have — re-arming every suspend leg while the run looked perfectly normal right
|
|
2680
|
+
* up until it parked forever — and every read of the pair had to remember a special `=== null` test.
|
|
2681
|
+
* A string is never nullish, so the ordinary coalesce is the whole rule. The retired spelling is
|
|
2682
|
+
* REFUSED loudly here (the one read every site goes through) rather than read as either value: a
|
|
2683
|
+
* caller still writing `null` is told the word to write, at the first prepare, instead of being
|
|
2684
|
+
* handed a store it asked not to have or a silent off switch it did not spell.
|
|
2687
2685
|
*/
|
|
2688
2686
|
export declare function resolveCheckpointStore(spec: {
|
|
2689
|
-
checkpointStore?: CheckpointStore |
|
|
2687
|
+
checkpointStore?: CheckpointStore | "disabled";
|
|
2690
2688
|
}, deps: {
|
|
2691
2689
|
checkpointStore?: CheckpointStore;
|
|
2692
2690
|
}): CheckpointStore | undefined;
|
|
2691
|
+
/** The ONE refusal of the retired `checkpointStore: null` spelling (the resolver and the workflow baseline overlay both throw it). */
|
|
2692
|
+
export declare function retiredCheckpointStoreNull(): Error & {
|
|
2693
|
+
code: "config.invalid_checkpoint_store";
|
|
2694
|
+
};
|
|
2693
2695
|
/**
|
|
2694
2696
|
* design/80 D-1 (persist-winner): derive the durable {@link ResolvedOutcome} winner from a resume
|
|
2695
2697
|
* `outcome`, or `undefined` when the outcome binds to no pending action. A `policy_ask` resume names a
|
|
@@ -178,6 +178,13 @@ export function buildRiskDescriptor(input) {
|
|
|
178
178
|
return { severity: 2, axes: {}, toolName: typeof toolName === "string" ? toolName : "" };
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
|
+
export function realApprovalOrgFact(bit) {
|
|
182
|
+
if (bit === undefined)
|
|
183
|
+
return undefined;
|
|
184
|
+
if (bit.org !== undefined)
|
|
185
|
+
return bit.org;
|
|
186
|
+
return bit.origin === "org_rule" ? "rule" : bit.origin === "org_unavailable" ? "unavailable" : undefined;
|
|
187
|
+
}
|
|
181
188
|
export const CURRENT_CHECKPOINT_VERSION = 1;
|
|
182
189
|
export const RESOURCE_CHECKPOINT_VERSION = 2;
|
|
183
190
|
export const BINDING_CHECKPOINT_VERSION = 3;
|
|
@@ -356,10 +363,18 @@ function sameValueAt(a, b, depth, aToB, bToA) {
|
|
|
356
363
|
}
|
|
357
364
|
}
|
|
358
365
|
export function resolveCheckpointStore(spec, deps) {
|
|
359
|
-
if (spec.checkpointStore
|
|
366
|
+
if (Object.is(spec.checkpointStore, null))
|
|
367
|
+
throw retiredCheckpointStoreNull();
|
|
368
|
+
if (spec.checkpointStore === "disabled")
|
|
360
369
|
return undefined;
|
|
361
370
|
return spec.checkpointStore ?? deps.checkpointStore;
|
|
362
371
|
}
|
|
372
|
+
export function retiredCheckpointStoreNull() {
|
|
373
|
+
const e = new Error('TaskSpec.checkpointStore: null is not a value any more — write checkpointStore: "disabled" to run without a ' +
|
|
374
|
+
"checkpoint store (the per-run durable off switch), or leave the field unset to use the deployment's store.");
|
|
375
|
+
e.code = "config.invalid_checkpoint_store";
|
|
376
|
+
return e;
|
|
377
|
+
}
|
|
363
378
|
export function winnerFromOutcome(outcome) {
|
|
364
379
|
if (outcome.gate === "plan_review" || outcome.gate === "dry_run_review") {
|
|
365
380
|
const editedPlan = outcome.gate === "plan_review" ? outcome.editedPlan : undefined;
|
|
@@ -377,6 +392,7 @@ export function winnerFromOutcome(outcome) {
|
|
|
377
392
|
decision: outcome.decision,
|
|
378
393
|
...(outcome.updatedInput === undefined ? {} : { updatedInput: outcome.updatedInput }),
|
|
379
394
|
...(outcome.answer === undefined ? {} : { answer: outcome.answer }),
|
|
395
|
+
...(outcome.gateOutcome === undefined ? {} : { gateOutcome: outcome.gateOutcome }),
|
|
380
396
|
...(outcome.reason === undefined ? {} : { reason: outcome.reason }),
|
|
381
397
|
};
|
|
382
398
|
}
|