@sema-agent/core 5.64.0 → 6.0.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 +70 -0
- package/dist/agents/subagent.d.ts +2 -2
- package/dist/agents/subagent.js +11 -0
- package/dist/agents/verify.d.ts +1 -1
- package/dist/brain/anthropic.js +1 -1
- package/dist/brain/errors.d.ts +29 -0
- package/dist/brain/errors.js +20 -0
- package/dist/brain/open-responses.js +2 -2
- package/dist/brain/route-adjudicator.d.ts +8 -1
- package/dist/brain/route-adjudicator.js +1 -0
- package/dist/brain/status-sink.js +12 -1
- package/dist/brain/stream-engine.js +17 -6
- package/dist/core/auto-compaction.d.ts +26 -0
- package/dist/core/auto-compaction.js +7 -2
- package/dist/core/auto-mode-arming.d.ts +138 -0
- package/dist/core/auto-mode-arming.js +181 -0
- package/dist/core/auto-mode-defaults.d.ts +13 -0
- package/dist/core/auto-mode-defaults.js +5 -0
- package/dist/core/auto-mode-prompt.d.ts +14 -3
- package/dist/core/auto-mode-prompt.js +10 -7
- package/dist/core/auto-mode-rebuild.d.ts +75 -0
- package/dist/core/auto-mode-rebuild.js +41 -0
- package/dist/core/auto-mode.d.ts +15 -0
- package/dist/core/auto-mode.js +4 -2
- package/dist/core/checkpoint-store.d.ts +113 -4
- package/dist/core/context-edit.d.ts +47 -5
- package/dist/core/context-guard.d.ts +1 -1
- package/dist/core/file-history-retention.d.ts +106 -0
- package/dist/core/file-history-retention.js +36 -0
- package/dist/core/file-history-store.d.ts +768 -0
- package/dist/core/file-history-store.js +880 -0
- package/dist/core/governance-codes.d.ts +2 -1
- package/dist/core/governance-codes.js +14 -0
- package/dist/core/hooks.d.ts +48 -8
- package/dist/core/hooks.js +39 -22
- package/dist/core/lsp.d.ts +2 -2
- package/dist/core/mcp.d.ts +29 -7
- package/dist/core/memory-engine/consolidation-driver.d.ts +11 -0
- package/dist/core/memory-engine/consolidation-driver.js +71 -4
- package/dist/core/memory-engine/consolidation.d.ts +25 -2
- package/dist/core/memory-engine/consolidation.js +4 -1
- package/dist/core/memory-engine/distiller.d.ts +84 -1
- package/dist/core/memory-engine/distiller.js +68 -0
- package/dist/core/memory-engine/dual-root.js +6 -0
- package/dist/core/memory-engine/engine.d.ts +329 -15
- package/dist/core/memory-engine/engine.js +364 -34
- package/dist/core/memory-engine/file-backend.d.ts +30 -0
- package/dist/core/memory-engine/file-backend.js +14 -13
- package/dist/core/memory-engine/frontmatter.d.ts +22 -1
- package/dist/core/memory-engine/frontmatter.js +3 -0
- package/dist/core/memory-engine/header-hints.d.ts +5 -0
- package/dist/core/memory-engine/index.d.ts +5 -4
- package/dist/core/memory-engine/index.js +5 -4
- package/dist/core/memory-engine/layout.d.ts +88 -2
- package/dist/core/memory-engine/layout.js +112 -3
- package/dist/core/memory-engine/provenance-wording.d.ts +7 -0
- package/dist/core/memory-engine/provenance-wording.js +3 -0
- package/dist/core/memory-engine/tools.d.ts +89 -8
- package/dist/core/memory-engine/tools.js +263 -22
- package/dist/core/memory-engine/types.d.ts +80 -1
- package/dist/core/memory-recall.d.ts +6 -0
- package/dist/core/memory.d.ts +27 -1
- package/dist/core/memory.js +16 -2
- package/dist/core/permission-rule-consent.d.ts +20 -0
- package/dist/core/permission-rule-consent.js +12 -3
- package/dist/core/permission-rule-model.d.ts +67 -7
- package/dist/core/permission-rule-model.js +53 -7
- package/dist/core/permission-rule-store.js +15 -10
- package/dist/core/permission-rule-sync.js +15 -11
- package/dist/core/remote-env.d.ts +3 -3
- package/dist/core/retention-policy.d.ts +9 -0
- package/dist/core/retention-policy.js +5 -2
- package/dist/core/retention.d.ts +13 -2
- package/dist/core/runner/assemble-result.d.ts +19 -1
- package/dist/core/runner/assemble-result.js +17 -2
- package/dist/core/runner/compaction-call-options.d.ts +93 -0
- package/dist/core/runner/compaction-call-options.js +3 -0
- package/dist/core/runner/memory-capture-optout.d.ts +80 -0
- package/dist/core/runner/memory-capture-optout.js +53 -0
- package/dist/core/runner/prepare-config-doors.d.ts +5 -0
- package/dist/core/runner/prepare-config-doors.js +16 -0
- package/dist/core/runner/prepare-hands-readface.d.ts +119 -5
- package/dist/core/runner/prepare-hands-readface.js +103 -8
- package/dist/core/runner/prepare-memory.d.ts +88 -0
- package/dist/core/runner/prepare-memory.js +306 -25
- package/dist/core/runner/prepare-task.d.ts +156 -5
- package/dist/core/runner/prepare-task.js +488 -98
- package/dist/core/runner/runtask.d.ts +27 -20
- package/dist/core/runner/runtask.js +283 -99
- package/dist/core/runner/session-file-state-replay.d.ts +18 -10
- package/dist/core/runner/session-file-state-replay.js +52 -1
- package/dist/core/runner/tool-disclosure.js +2 -1
- package/dist/core/runner/turn-attachments.d.ts +22 -12
- package/dist/core/session-store.d.ts +1 -1
- package/dist/core/session-store.js +6 -1
- package/dist/core/session.d.ts +34 -1
- package/dist/core/store-contracts/file-history-store-contract.d.ts +3 -0
- package/dist/core/store-contracts/file-history-store-contract.js +720 -0
- package/dist/core/stub-env.d.ts +4 -0
- package/dist/core/stub-env.js +1 -0
- package/dist/core/task-registry-shared.js +30 -2
- package/dist/core/tool-errors.js +1 -0
- package/dist/core/tool-policy.d.ts +172 -1
- package/dist/core/tool-policy.js +32 -1
- package/dist/core/tool-result-store.js +2 -1
- package/dist/core/trace.d.ts +24 -0
- package/dist/core/types.d.ts +875 -97
- package/dist/core/types.js +4 -3
- package/dist/core/untrusted-text.d.ts +1 -1
- package/dist/core/untrusted-text.js +8 -0
- package/dist/core/workflow-run-store-contract.js +17 -0
- package/dist/core/workflow-run-store.d.ts +20 -0
- package/dist/core/workflow-run-store.js +1 -0
- package/dist/engine/compaction/compaction.d.ts +88 -10
- package/dist/engine/compaction/compaction.js +109 -30
- package/dist/engine/execution-env/node-execution-env.d.ts +9 -1
- package/dist/engine/execution-env/node-execution-env.js +28 -0
- package/dist/engine/harness/agent-harness.d.ts +52 -1
- package/dist/engine/harness/agent-harness.js +36 -1
- package/dist/engine/harness/types.d.ts +44 -1
- package/dist/engine/llm/types.d.ts +50 -4
- package/dist/engine/loop/agent-loop.d.ts +5 -1
- package/dist/engine/loop/agent-loop.js +25 -0
- package/dist/engine/loop/types.d.ts +19 -0
- package/dist/engine/lsp/node-lsp-manager.d.ts +1 -1
- package/dist/engine/session/session.js +1 -1
- package/dist/index.d.ts +18 -8
- package/dist/index.js +14 -6
- package/dist/orchestration/run-workflow-tool.d.ts +20 -2
- package/dist/orchestration/run-workflow-tool.js +22 -3
- package/dist/orchestration/workflow-governance.d.ts +59 -1
- package/dist/orchestration/workflow-governance.js +61 -8
- package/dist/orchestration/workflow-meta.d.ts +4 -2
- package/dist/orchestration/workflow-primitives.js +56 -13
- package/dist/orchestration/workflow-types.d.ts +112 -1
- package/dist/orchestration/workflow-types.js +2 -2
- package/dist/orchestration/workflow.d.ts +20 -0
- package/dist/orchestration/workflow.js +182 -14
- package/dist/prompt-assembly/event-registry.js +1 -1
- package/dist/prompts/default.d.ts +15 -7
- package/dist/prompts/default.js +3 -0
- package/dist/stores/file/file-history-store.d.ts +368 -0
- package/dist/stores/file/file-history-store.js +1248 -0
- package/dist/stores/file/index.d.ts +22 -13
- package/dist/stores/file/index.js +4 -4
- package/dist/stores/file/permission-rule-store.js +1 -0
- package/dist/stores/file/strategy-store.d.ts +3 -3
- package/dist/tools/fs/bash-readonly-classifier.d.ts +87 -3
- package/dist/tools/fs/bash-readonly-classifier.js +106 -4
- package/dist/tools/fs/fs-bash.js +9 -5
- package/dist/tools/fs/fs-shared.d.ts +52 -1
- package/dist/tools/fs/fs-shared.js +14 -0
- package/dist/tools/fs/fs-write.d.ts +5 -5
- package/dist/tools/fs/fs-write.js +71 -14
- package/dist/tools/fs/index.d.ts +6 -1
- package/dist/tools/fs/index.js +1 -1
- package/dist/tools/web.js +2 -1
- package/package.json +5 -1
- package/test/export-surface.snapshot.json +159 -23
- package/dist/core/file-snapshot-store.d.ts +0 -165
- package/dist/core/file-snapshot-store.js +0 -259
- package/dist/core/store-contracts/file-snapshot-store-contract.d.ts +0 -13
- package/dist/core/store-contracts/file-snapshot-store-contract.js +0 -134
- package/dist/stores/file/file-snapshot-store.d.ts +0 -58
- package/dist/stores/file/file-snapshot-store.js +0 -353
package/dist/core/types.d.ts
CHANGED
|
@@ -119,11 +119,11 @@ export type ToolContentOrigin = "external" | "execution" | "local";
|
|
|
119
119
|
* design/77 §4 — what a {@link ToolSpec.reversibilityProbe} returns for ONE call.
|
|
120
120
|
*
|
|
121
121
|
* `reversible` alone decides: the gate tightens a surviving `allow` to `ask` unless it is exactly `true`,
|
|
122
|
-
* and
|
|
122
|
+
* and no optional member can widen that. They exist so the resulting approval card can say WHY, a
|
|
123
123
|
* thing the probe knows at the moment it decides and nothing downstream can re-derive.
|
|
124
124
|
*
|
|
125
125
|
* Supply `cause` when the reason has parts a consumer should render itself; `reason` when it is prose
|
|
126
|
-
* this engine cannot interpret.
|
|
126
|
+
* this engine cannot interpret. All three are optional and independent, so a probe written against the
|
|
127
127
|
* original bare `{ reversible }` shape remains valid.
|
|
128
128
|
*/
|
|
129
129
|
export interface ReversibilityVerdict {
|
|
@@ -133,6 +133,27 @@ export interface ReversibilityVerdict {
|
|
|
133
133
|
/** Structured account — see {@link import("./checkpoint-store.js").ProbeCause}. Validated by the gate;
|
|
134
134
|
* a malformed value costs the DISCLOSURE only, never the ask. */
|
|
135
135
|
cause?: import("./checkpoint-store.js").ProbeCause;
|
|
136
|
+
/**
|
|
137
|
+
* #502 (additive) — this demotion is STRUCTURAL, not the probe hesitating: the call crosses a
|
|
138
|
+
* boundary the deployment declared, so a persisted allow rule must not retire the resulting ask.
|
|
139
|
+
* The gate folds it into the #144 mandate family ("allow rules silence the CLASSIFIER's questions,
|
|
140
|
+
* never a MANDATED one") beside an operator `shellGate:"always"` and the tool's own
|
|
141
|
+
* egress/irreversibility marks: a matching rule is DISCLOSED as shadowed instead of clearing the
|
|
142
|
+
* ask, and the card's "stop asking me this" offer is withheld rather than offered and then refused.
|
|
143
|
+
*
|
|
144
|
+
* Read on the demotion arm only — a `reversible: true` verdict ends the gate with no ask to mandate,
|
|
145
|
+
* and this member cannot make one. It is the one member a DEPLOYMENT-supplied probe may set that the
|
|
146
|
+
* engine acts on, and it is safe for the same reason the probe itself is: the effect is strictly
|
|
147
|
+
* TIGHTENING (one more question survives one more configuration), so an over-claiming probe costs
|
|
148
|
+
* its own deployment a re-confirmation and can never turn an ask into an allow.
|
|
149
|
+
*
|
|
150
|
+
* The built-in shell probe sets it for exactly one family: a listed reader naming a path outside the
|
|
151
|
+
* session's allowed directories. The engine's own out-of-root demotion has no clearing channel in
|
|
152
|
+
* this version other than confirming the call each time — a directory-scoped grant that WOULD clear
|
|
153
|
+
* it is a separate, later mechanism, and until it exists a surface must not word the refusal as
|
|
154
|
+
* though one already answers it.
|
|
155
|
+
*/
|
|
156
|
+
mandated?: boolean;
|
|
136
157
|
}
|
|
137
158
|
export interface ToolSpec<TParams extends TSchema = TSchema> {
|
|
138
159
|
/**
|
|
@@ -280,7 +301,9 @@ export interface ToolSpec<TParams extends TSchema = TSchema> {
|
|
|
280
301
|
* - `"maybe"` — reversibility depends on the args; the gate calls {@link reversibilityProbe} (when one is
|
|
281
302
|
* declared) on a surviving `allow` and tightens to `ask` UNLESS the probe reports reversible. A tool
|
|
282
303
|
* that declares a `reversibilityProbe` but omits `irreversibility` defaults to `"maybe"` (so it always
|
|
283
|
-
* enters the activation set and is gated).
|
|
304
|
+
* enters the activation set and is gated). Since #502 the probe ALSO runs over a surviving `ask`
|
|
305
|
+
* — see {@link reversibilityProbe} for what that arm reads (only {@link ReversibilityVerdict.mandated})
|
|
306
|
+
* and what it costs; it tightens nothing there, because an ask cannot be tightened into an ask.
|
|
284
307
|
* - `"always"` — irreversible; the gate ALWAYS tightens a surviving `allow` to `ask` (like {@link egress}).
|
|
285
308
|
*
|
|
286
309
|
* The tighten mirrors the egress tighten (it only ever tightens allow→ask; a `deny`/`ask` is untouched),
|
|
@@ -296,13 +319,28 @@ export interface ToolSpec<TParams extends TSchema = TSchema> {
|
|
|
296
319
|
irreversibility?: "never" | "maybe" | "always";
|
|
297
320
|
/**
|
|
298
321
|
* design/77 §4: optional deployment-injected probe for an `irreversibility:"maybe"` tool. Given the call's
|
|
299
|
-
* (post-hook) args, it reports whether THIS specific call is reversible.
|
|
300
|
-
* surviving decision is `allow` and `irreversibility` resolves to `"maybe"`; it is time-bounded (the
|
|
322
|
+
* (post-hook) args, it reports whether THIS specific call is reversible. It is time-bounded (the
|
|
301
323
|
* approval timeout when configured, a 30s default when absent — the probe wait is always finite) and
|
|
302
324
|
* **fail-closed**: a non-`reversible` verdict, a timeout, or a throw all tighten to
|
|
303
325
|
* `ask`. Declaring this probe defaults `irreversibility` to `"maybe"`. It is read from the spec at
|
|
304
326
|
* prepare-time and captured in a closure — NOT a tool argument — so the model cannot monkey-patch it.
|
|
305
327
|
*
|
|
328
|
+
* **WHEN THE GATE CALLS IT — two arms, and the second is newer than this paragraph's first draft**
|
|
329
|
+
* (#502; `irreversibility` must resolve to `"maybe"` for either):
|
|
330
|
+
* 1. the surviving decision is `allow` — the TIGHTEN arm: the verdict decides allow→ask, and a
|
|
331
|
+
* tightening verdict's `reason`/`cause` ride the minted ask;
|
|
332
|
+
* 2. the surviving decision is already `ask` — the MANDATE arm: the probe is the only source of
|
|
333
|
+
* {@link ReversibilityVerdict.mandated}, and an ask another layer raised needed it too. (Before
|
|
334
|
+
* #502 a policy that asked FIRST skipped this block entirely, so an out-of-root read never
|
|
335
|
+
* acquired its mandate and a covering allow rule retired the question — measured, not theorised.)
|
|
336
|
+
* Here the `reversible` verdict is DISCARDED (a probe may never un-ask another layer's question),
|
|
337
|
+
* `reason`/`cause` are not merged onto somebody else's ask, and only `mandated` is read.
|
|
338
|
+
* The TIGHTEN is still `allow`→`ask` only — a `deny`/`ask` is never re-minted — but the INVOCATION
|
|
339
|
+
* set is wider than the tighten's, and that difference is the deployment's to budget for: on an
|
|
340
|
+
* ask-first policy the probe is called (and awaited, to the bound above) on every `"maybe"`-tier
|
|
341
|
+
* call, and a probe that throws or times out reports through `onError` with `phase: "hook"` on
|
|
342
|
+
* those calls too. A probe should therefore be a cheap, side-effect-free inspection of the args.
|
|
343
|
+
*
|
|
306
344
|
* **`reason` (optional, additive both ways)** — WHY this call was not proven reversible, in the probe's
|
|
307
345
|
* own words, minted in the same pass that reached the verdict (so nothing downstream re-derives it).
|
|
308
346
|
* Read ONLY on a tightening verdict: a `reversible: true` return ends the gate, and a timeout/throw
|
|
@@ -888,6 +926,43 @@ export interface ToolExecuteContext {
|
|
|
888
926
|
* inference decides, exactly as before the seat existed.
|
|
889
927
|
*/
|
|
890
928
|
memoryPersistenceCapable?: boolean;
|
|
929
|
+
/**
|
|
930
|
+
* design/383 §2.5 — TRUE ⇔ the spawning session is under a memory-capture OPT-OUT at the moment
|
|
931
|
+
* a delegation tool reads this seat (declared at its prepare, a standing record, or a mid-run
|
|
932
|
+
* flip — the seat is live, not a prepare-time snapshot). The delegation lanes forward it into
|
|
933
|
+
* every child's trusted internals as the capture FLOOR (`memoryCaptureFloor`): a child of an
|
|
934
|
+
* opted-out session captures nothing, whatever its chosen AgentDefinition says — the opt-out is
|
|
935
|
+
* a floor no selection loosens (the `memoryPersistenceCapable:false` floor's exact law, on the
|
|
936
|
+
* privacy axis). Trusted Runner-filled seat, never a model/tool argument.
|
|
937
|
+
*/
|
|
938
|
+
memoryCaptureOptedOut?: boolean;
|
|
939
|
+
/**
|
|
940
|
+
* design/383 §2.5 (rescan post-6.0.0-RC) — the floor seat's THIRD state: TRUE ⇔ the spawning
|
|
941
|
+
* session's capture opt-out state is INDETERMINATE at the moment a delegation tool reads this
|
|
942
|
+
* seat (its capture record store is faulting, and no genuine record is known in-process). The
|
|
943
|
+
* delegation lanes forward it into the child's trusted internals as the floor's indeterminate
|
|
944
|
+
* twin (`memoryCaptureFloorIndeterminate`): the child's prepare re-resolves it against the live
|
|
945
|
+
* record query — an ancestor record found takes the ordinary floor arm; a still-faulting store
|
|
946
|
+
* runs the child under the same fail-closed suppression an own-store outage imposes (no
|
|
947
|
+
* irreversible record is ever minted off an unreadable state); a readable-and-clean answer
|
|
948
|
+
* proceeds clean. Never TRUE beside {@link memoryCaptureOptedOut} — a known opt-out is
|
|
949
|
+
* determinate. Trusted Runner-filled seat, never a model/tool argument.
|
|
950
|
+
*/
|
|
951
|
+
memoryCaptureIndeterminate?: boolean;
|
|
952
|
+
/** design/383 §2.5 — the spawning session's write-plane control dir (the coordinate its capture
|
|
953
|
+
* record is keyed under), forwarded beside the floor bit so a cross-plane child's record-query
|
|
954
|
+
* leg reads the PARENT's carrier, not its own plane's. Trusted Runner-filled seat. */
|
|
955
|
+
memoryCaptureControlDir?: string;
|
|
956
|
+
/** design/383 §2.5 (codex round 3) — the WHOLE ancestor chain's capture-record coordinates
|
|
957
|
+
* ({sessionId, controlDir?} per generation, root first), appended one row per spawn. The
|
|
958
|
+
* record-query leg walks ALL of it: a root that flips after spawning an already-running child
|
|
959
|
+
* AND grandchild is invisible to the grandchild's one-hop parent query (the clean middle
|
|
960
|
+
* session has no record of its own to find), while the chain reaches the root directly.
|
|
961
|
+
* Trusted Runner-filled seat, never a model argument. */
|
|
962
|
+
memoryCaptureAncestors?: ReadonlyArray<{
|
|
963
|
+
sessionId: string;
|
|
964
|
+
controlDir?: string;
|
|
965
|
+
}>;
|
|
891
966
|
/**
|
|
892
967
|
* the parent task's per-model auth hook ({@link TaskSpec.getApiKeyAndHeaders}), inherited
|
|
893
968
|
* verbatim down the delegation tree like `principal`/`clientContext` (Runner-filled, read-only, NEVER
|
|
@@ -1155,6 +1230,48 @@ export type BeforeWriteResult = {
|
|
|
1155
1230
|
reason: string;
|
|
1156
1231
|
};
|
|
1157
1232
|
export type BeforeWriteHook = (req: BeforeWriteRequest) => BeforeWriteResult | undefined | Promise<BeforeWriteResult | undefined>;
|
|
1233
|
+
/**
|
|
1234
|
+
* design/381 — the hands band's FIRST-TOUCH HISTORY hook, called before EVERY file mutation in
|
|
1235
|
+
* Write/Edit/NotebookEdit (after the `beforeWrite` content gate passed, immediately before the
|
|
1236
|
+
* final env write) with the resolved canonical containment key. The Runner wires it to
|
|
1237
|
+
* `FileHistoryStore.trackEdit` (backup-before-first-edit, the CC FileHistory trigger-point set
|
|
1238
|
+
* minus the bash simulation arm sema does not have — DV-7); later calls for an already-tracked
|
|
1239
|
+
* path are cheap store no-ops. `{ok:false}` carries the REFUSAL TEXT the tool must answer instead
|
|
1240
|
+
* of writing (DV-14 default: no durable first-touch state ⇒ the edit is refused, typed and loud —
|
|
1241
|
+
* any proceed-unprotected policy is resolved INSIDE the hook by the Runner, never by the band).
|
|
1242
|
+
* A THROWING hook fails CLOSED (same posture as the write gate). Absent hook ⇒ byte-identical
|
|
1243
|
+
* behavior (no history store wired).
|
|
1244
|
+
*/
|
|
1245
|
+
export interface TrackEditRequest {
|
|
1246
|
+
tool: "Write" | "Edit" | "NotebookEdit";
|
|
1247
|
+
/** The model-supplied path argument (for the error-message coordinate the model knows). */
|
|
1248
|
+
path: string;
|
|
1249
|
+
/** The resolved canonical containment key — the identity the history record is keyed on. */
|
|
1250
|
+
key: string;
|
|
1251
|
+
/** The tool call's abort signal, threaded into the store's env read. */
|
|
1252
|
+
signal?: AbortSignal;
|
|
1253
|
+
}
|
|
1254
|
+
export type TrackEditResult = {
|
|
1255
|
+
ok: true;
|
|
1256
|
+
/**
|
|
1257
|
+
* #491 — the compensating handle for the ordering the topology cannot avoid: the pre-image
|
|
1258
|
+
* capture runs BEFORE the write, so a write that then FAILS (lost create race, ENOSPC,
|
|
1259
|
+
* EACCES, read-only mount) would leave a first-touch record for an edit that never happened —
|
|
1260
|
+
* and an `existed-not` record makes an untouched path a rewind DELETE target, removing bytes
|
|
1261
|
+
* whoever writes them next (the user, a peer process) owns. Present ONLY when THIS call minted
|
|
1262
|
+
* the record (an already-tracked path belongs to the edit that first touched it and must never
|
|
1263
|
+
* be discarded); the band calls it on the write-failure arm of every mutation lane. `proof`
|
|
1264
|
+
* says which retraction rule applies: `"proven"` = the write's own error code guarantees
|
|
1265
|
+
* nothing was written, `"verify"` = ambiguous, so the history seat must re-read the path and
|
|
1266
|
+
* retract only if it still matches what the first touch recorded. It never throws and never
|
|
1267
|
+
* blocks the failure it accompanies — the original write error is the answer.
|
|
1268
|
+
*/
|
|
1269
|
+
annul?: (proof: "proven" | "verify") => Promise<void>;
|
|
1270
|
+
} | {
|
|
1271
|
+
ok: false;
|
|
1272
|
+
refusal: string;
|
|
1273
|
+
};
|
|
1274
|
+
export type TrackFileEditHook = (req: TrackEditRequest) => Promise<TrackEditResult>;
|
|
1158
1275
|
/**
|
|
1159
1276
|
* design/141 件2 — the SAFE deployment-configurable subset of the hands toolkit ({@link RunnerDeps.hands}).
|
|
1160
1277
|
* Only fields whose injection is purely additive for a deployment are here; Runner-internal orchestration
|
|
@@ -1845,11 +1962,13 @@ export interface TaskSpec {
|
|
|
1845
1962
|
* rewinding to before the very first message = start a NEW session. (A first-class branch-reset entry
|
|
1846
1963
|
* that could support this without a second root is a recorded design candidate, not built.)
|
|
1847
1964
|
*
|
|
1848
|
-
*
|
|
1849
|
-
*
|
|
1850
|
-
*
|
|
1851
|
-
*
|
|
1852
|
-
*
|
|
1965
|
+
* File-history interaction (design/381): with {@link restoreFiles}, the `"before"` file anchor is the
|
|
1966
|
+
* target's OWN entry id first — history boundaries are keyed by a turn's initial entry and mean "file
|
|
1967
|
+
* state when that turn began", so B(target) IS the before-state. When the target carries no boundary
|
|
1968
|
+
* (a mixed-epoch session, or a non-turn-initial target), the anchor falls back to the nearest
|
|
1969
|
+
* boundary-carrying ANCESTOR, so files and context land on the same side of the cut. If no such
|
|
1970
|
+
* boundary exists, prepare fails loud (`rewind_snapshot.unresolvable`) instead of silently leaving
|
|
1971
|
+
* the files at their pre-rewind state while the conversation moved back.
|
|
1853
1972
|
* Fail-loud: setting this without {@link resumeAt}, or an unknown mode value, rejects at prepare.
|
|
1854
1973
|
*/
|
|
1855
1974
|
resumeAtMode?: "at" | "before";
|
|
@@ -1943,22 +2062,47 @@ export interface TaskSpec {
|
|
|
1943
2062
|
role?: ModelRole;
|
|
1944
2063
|
};
|
|
1945
2064
|
/**
|
|
1946
|
-
* design/
|
|
1947
|
-
*
|
|
1948
|
-
*
|
|
1949
|
-
*
|
|
1950
|
-
*
|
|
1951
|
-
*
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
*
|
|
1956
|
-
*
|
|
1957
|
-
*
|
|
1958
|
-
*
|
|
1959
|
-
*
|
|
1960
|
-
*
|
|
1961
|
-
*
|
|
2065
|
+
* design/381 (DV-9) — the RESTORE half of file rewind: when this task uses {@link resumeAt} to branch at
|
|
2066
|
+
* a prior entry, ALSO converge the working tree's **agent-edited files** (the tracked set — files this
|
|
2067
|
+
* session touched through Edit/Write/NotebookEdit; nothing else is ever visible to the history, so a
|
|
2068
|
+
* user's parallel work is structurally untouchable) to the history boundary of that entry (pairs with
|
|
2069
|
+
* E18: branch the session + restore the tracked files). SCOPE = THIS session: a delegated
|
|
2070
|
+
* subagent runs under its own session and its edits land in its own history scope, so a parent
|
|
2071
|
+
* rewind does not converge files only a subagent touched (the retired whole-tree restore did —
|
|
2072
|
+
* a design/381 narrowing, disclosed here). Requires `RunnerDeps.fileHistoryStore`.
|
|
2073
|
+
* Fail-loud: setting this without {@link resumeAt} rejects at prepare (`rewind.invalid_spec`) — a
|
|
2074
|
+
* restore with no anchor is a contradiction, not a default.
|
|
2075
|
+
*
|
|
2076
|
+
* There is NO capture flag anymore: first-touch tracking is ALWAYS ON whenever a
|
|
2077
|
+
* `RunnerDeps.fileHistoryStore` is wired (per-edited-file backups are near-zero cost — no tree walk,
|
|
2078
|
+
* no size gate), which is what retired the old `rewindFiles` capture request. The retired
|
|
2079
|
+
* `rewindFiles` spelling is handled at prepare: its capture sense (true, no `resumeAt`) is a
|
|
2080
|
+
* tolerated no-op with a deprecation disclosure (the intent is structurally satisfied); its restore
|
|
2081
|
+
* sense (true, with `resumeAt`) is a typed retirement rejection naming THIS field — the restore
|
|
2082
|
+
* semantics genuinely changed (whole-tree ⇒ tracked set), so that migration must be explicit.
|
|
2083
|
+
*/
|
|
2084
|
+
restoreFiles?: boolean;
|
|
2085
|
+
/**
|
|
2086
|
+
* design/381 (DV-15) — opt-in tolerance for a PARTIAL file restore: when the {@link restoreFiles} /
|
|
2087
|
+
* {@link rewindFilesTo} convergence ends with ≥1 per-file refusal or failure (the others applied),
|
|
2088
|
+
* the default is a loud terminal failure carrying the per-file ledger (`rewind.restore_failed`) —
|
|
2089
|
+
* a silent continue would run the agent on a mixed-epoch tree without anyone knowing. Setting this
|
|
2090
|
+
* `true` keeps the run going instead and discloses the same ledger as a `restore_partial` note on
|
|
2091
|
+
* {@link TaskResult.rewindNotes} (the reference UI's "skipped files" seat in library form). The
|
|
2092
|
+
* documented recovery either way: re-run the same restore — it is per-file idempotent and converges.
|
|
2093
|
+
*/
|
|
2094
|
+
acceptPartialRestore?: boolean;
|
|
2095
|
+
/**
|
|
2096
|
+
* design/101 §E19 R8 + design/381 (CC Rewind `code`-only mode) — converge the tracked set (see
|
|
2097
|
+
* {@link restoreFiles} for what that is and is not) to the history boundary of this prior entryId
|
|
2098
|
+
* WITHOUT forking the conversation (no {@link resumeAt} leaf-branch). This is CC's "Restore code"
|
|
2099
|
+
* mode (vs "Restore code and conversation" = {@link resumeAt}+{@link restoreFiles}, and "Restore
|
|
2100
|
+
* conversation" = {@link resumeAt} alone). Legal ONLY when {@link resumeAt} is ABSENT — handing
|
|
2101
|
+
* BOTH anchors is refused loud (`rewind.conflicting_targets`): two file targets is a
|
|
2102
|
+
* contradiction, and silently preferring one is the silent-swallow family this seam refuses; a
|
|
2103
|
+
* target with no history boundary fails loud (`rewind_snapshot.unresolvable`). Requires
|
|
2104
|
+
* `RunnerDeps.fileHistoryStore`. The session leaf is untouched — the next turn continues the
|
|
2105
|
+
* CURRENT conversation, only the files moved.
|
|
1962
2106
|
*/
|
|
1963
2107
|
rewindFilesTo?: string;
|
|
1964
2108
|
/**
|
|
@@ -2851,11 +2995,24 @@ export interface TaskSpec {
|
|
|
2851
2995
|
* {@link budgetUsd}, and for the same reason): the lane only rides boundaries whose turn resolved
|
|
2852
2996
|
* ≥1 tool call, so a boundary steer never EXTENDS a run that reached its natural end.
|
|
2853
2997
|
*
|
|
2854
|
-
*
|
|
2855
|
-
*
|
|
2856
|
-
*
|
|
2998
|
+
* DEFAULT ON since design/374 slice 4 (ruled 2026-08-28) — boolean, not `true`: explicit
|
|
2999
|
+
* `false` is the opt-out, the {@link backgroundTasks} / listing-family contract rather than the
|
|
3000
|
+
* `true`-only 133 form. BREAKING for a deployment that declares a token ceiling: pre-flip it saw
|
|
3001
|
+
* no `<total_tokens>` frame without opting in, and a probe pinning that absence reds.
|
|
3002
|
+
*
|
|
3003
|
+
* WHY THIS LANE AND NOT {@link budgetUsd}. CC 2.1.245+ ships the readout on by default (its own
|
|
3004
|
+
* settings schema reads "Defaults to padded-countdown"), which is the fact that reopened the
|
|
3005
|
+
* 2026-07-30 registration; the design/74 "no budget language reaches the model" default is
|
|
3006
|
+
* relaxed here for the same reason CC relaxed it. The BYOM honesty objection does not survive
|
|
3007
|
+
* either: core estimates nothing, publishes only the ceiling it already ENFORCES, and a task
|
|
3008
|
+
* that declares no token ceiling never fires the countdown arms at all — so the flip is
|
|
3009
|
+
* observable exactly in the deployments that asked for a ceiling. That property is EXACT, not
|
|
3010
|
+
* approximate: CC's two CONSTANT arms (`fixed`/`infinite`) publish a number with no ceiling
|
|
3011
|
+
* behind it, so the DEFAULT deliberately does not reach them — they stay behind an explicit
|
|
3012
|
+
* `true` here (see the runner's lane gate). The $ twin keeps its opt-in
|
|
3013
|
+
* default: CC has no equivalent default-on posture for it.
|
|
2857
3014
|
*/
|
|
2858
|
-
totalTokensReminder?:
|
|
3015
|
+
totalTokensReminder?: boolean;
|
|
2859
3016
|
/**
|
|
2860
3017
|
* RB-318 — which arm of the readout, CC 2.1.245's closed set (`off` / `infinite` / `fixed` /
|
|
2861
3018
|
* `countdown` / `padded-countdown`); absent ⇒ CC's own default `"padded-countdown"`.
|
|
@@ -3038,6 +3195,30 @@ export type EffectiveMemoryScopes = {
|
|
|
3038
3195
|
};
|
|
3039
3196
|
export interface TaskResult {
|
|
3040
3197
|
taskId: string;
|
|
3198
|
+
/**
|
|
3199
|
+
* #499 — the ENGINE-minted identity of THIS run (uuidv7), minted once at the top of prepare and
|
|
3200
|
+
* never rewritten. The third id of the trio, and the only one the engine owns:
|
|
3201
|
+
* - {@link taskId} — the HOST's task identity, or (when the host named none) the session id: it is
|
|
3202
|
+
* `spec.taskId ?? sessionId` and therefore answers the SAME value for every run of one session;
|
|
3203
|
+
* - {@link sessionId} — the conversation, shared by every run that continues it;
|
|
3204
|
+
* - `runId` — this call. Two runs of one session ALWAYS differ here; a host-supplied `taskId` does
|
|
3205
|
+
* not change it (the host's handle and the engine's run are different questions).
|
|
3206
|
+
*
|
|
3207
|
+
* Use it to join a run's per-run disclosures (`task.user_steer_undrained` /
|
|
3208
|
+
* `task.user_followup_undrained` / `route.fallback_to_primary` / `steering.parked_input_blocked` /
|
|
3209
|
+
* `task.turn_interrupted` all carry `detail.runId`) and its `task.start`/`task.end` trace pair to
|
|
3210
|
+
* the result they belong to. Do NOT use it as an aggregation key across runs — that is what
|
|
3211
|
+
* `taskId`/`taskSignature` are for.
|
|
3212
|
+
*
|
|
3213
|
+
* **In-presence condition**: present on every terminal of a run that ENTERED prepare — a prepare-time
|
|
3214
|
+
* refusal included. The id is minted and published at prepare's FIRST statement precisely so this
|
|
3215
|
+
* holds: prepare itself emits run-scoped disclosures (`route.fallback_to_primary`), and a disclosure
|
|
3216
|
+
* naming a run that no terminal ever names would be unjoinable on exactly the failure path it is
|
|
3217
|
+
* about. It is absent only where no run was entered — a throw above the prepare call — and on
|
|
3218
|
+
* stub/synthesized results a host assembles itself. Optional for that reason, never fabricated, and
|
|
3219
|
+
* never the `"unknown"` sentinel `taskId`/`sessionId` fall back to on that path.
|
|
3220
|
+
*/
|
|
3221
|
+
runId?: string;
|
|
3041
3222
|
/** Use this to continue the same conversation on the next call. */
|
|
3042
3223
|
sessionId: string;
|
|
3043
3224
|
status: TaskStatus;
|
|
@@ -3204,6 +3385,36 @@ export interface TaskResult {
|
|
|
3204
3385
|
* the ceiling was never evaluated (typically a store or decorator that predates the cost arm).
|
|
3205
3386
|
*/
|
|
3206
3387
|
errorCode?: string;
|
|
3388
|
+
/**
|
|
3389
|
+
* Present when this run's terminal failure came from the PROVIDER BOUNDARY — the transport or the
|
|
3390
|
+
* provider failed the model call, as opposed to this deployment refusing to send one, a limit being
|
|
3391
|
+
* reached, or the model's own output being unusable. Its PRESENCE is the assertion; the members are
|
|
3392
|
+
* whatever the failing attempt actually stated about itself:
|
|
3393
|
+
* - `status` — the failing HTTP status, when the provider answered with one. Absent for a
|
|
3394
|
+
* connect failure, a stall, a mid-stream tear, and an in-band error frame delivered inside a
|
|
3395
|
+
* 200 — never zeroed, since 0 reads as a number a consumer may format.
|
|
3396
|
+
* - `requestId` — the provider's own request identifier for that attempt, when it stated one in a
|
|
3397
|
+
* shape worth carrying. This is the handle a provider's support channel asks for.
|
|
3398
|
+
* Both may be absent together: that is a provider failure nobody could label further, which is a
|
|
3399
|
+
* different statement from "not a provider failure" (the field itself absent).
|
|
3400
|
+
*
|
|
3401
|
+
* Nested rather than three loose keys so the assertion and its evidence cannot drift apart — there
|
|
3402
|
+
* is no state in which a consumer sees the claim beside a status belonging to some other attempt.
|
|
3403
|
+
* The same family rides the assistant message itself as `isApiErrorMessage` / `apiErrorStatus` /
|
|
3404
|
+
* `requestId`; this seat is its RUN-LEVEL projection, lifted from that message at the one assembly
|
|
3405
|
+
* point rather than re-derived, and the two spellings are named here so the mapping is written down
|
|
3406
|
+
* instead of inferred.
|
|
3407
|
+
*
|
|
3408
|
+
* PRESENCE, deliberately narrow: filled only when the provider failure is the terminal that NAMED
|
|
3409
|
+
* this result. A run that hit a provider failure and then a higher-ranked terminal (a budget
|
|
3410
|
+
* ceiling, a walltime cut) reports that other cause and leaves this absent — the same rule
|
|
3411
|
+
* {@link usageWindowRetryAfterMs} states for itself, and for the same reason: a diagnostic seat
|
|
3412
|
+
* beside a cause it does not belong to is worse than a missing one.
|
|
3413
|
+
*/
|
|
3414
|
+
apiFailure?: {
|
|
3415
|
+
status?: number;
|
|
3416
|
+
requestId?: string;
|
|
3417
|
+
};
|
|
3207
3418
|
/**
|
|
3208
3419
|
* How long (milliseconds) until the deployment usage window that stopped this task frees up — the wait
|
|
3209
3420
|
* hint a scheduler needs to decide WHEN to re-submit, rather than polling.
|
|
@@ -3259,24 +3470,32 @@ export interface TaskResult {
|
|
|
3259
3470
|
* `errorCode="output.invalid"`, and this stays undefined.) */
|
|
3260
3471
|
structuredOutput?: unknown;
|
|
3261
3472
|
/**
|
|
3262
|
-
* design/101 §E19 — NON-FATAL rewind disclosures for this run: the legs where the rewind
|
|
3263
|
-
* something other than what the request's plain reading implies, reported as data instead
|
|
3264
|
-
* Present only when at least one applies (absent = nothing to disclose), so a consumer that
|
|
3265
|
-
* field is unchanged. A rewind that could NOT be delivered as asked is NOT a note — it is a
|
|
3266
|
-
* failure (`rewind_snapshot.unresolvable` / `rewind.store_unconfigured` /
|
|
3473
|
+
* design/101 §E19 + design/381 — NON-FATAL rewind disclosures for this run: the legs where the rewind
|
|
3474
|
+
* machinery did something other than what the request's plain reading implies, reported as data instead
|
|
3475
|
+
* of silence. Present only when at least one applies (absent = nothing to disclose), so a consumer that
|
|
3476
|
+
* ignores the field is unchanged. A rewind that could NOT be delivered as asked is NOT a note — it is a
|
|
3477
|
+
* terminal failure (`rewind_snapshot.unresolvable` / `rewind.store_unconfigured` /
|
|
3478
|
+
* `rewind.restore_failed` / `rewind.invalid_spec` / `rewind.rewind_files_retired` /
|
|
3479
|
+
* `rewind.conflicting_targets`).
|
|
3267
3480
|
*
|
|
3268
3481
|
* - `conversation_only` — {@link TaskSpec.resumeAt} branched the transcript WITHOUT
|
|
3269
|
-
* {@link TaskSpec.
|
|
3270
|
-
* conversation" mode). Legal and useful; the note is what makes the two axes' divergence
|
|
3271
|
-
*
|
|
3482
|
+
* {@link TaskSpec.restoreFiles}, so the working tree was deliberately left where it was (CC's
|
|
3483
|
+
* "Restore conversation" mode). Legal and useful; the note is what makes the two axes' divergence
|
|
3484
|
+
* visible.
|
|
3485
|
+
* - `files_env_unsupported` — a file restore was requested but this deployment mounts no
|
|
3272
3486
|
* filesystem-capable `ExecutionEnv` (stub env), so the file axis was inert. Not an error: such a
|
|
3273
3487
|
* deployment has no working tree to rewind.
|
|
3274
|
-
* - `
|
|
3275
|
-
*
|
|
3276
|
-
*
|
|
3488
|
+
* - `restore_partial` — {@link TaskSpec.acceptPartialRestore} tolerated a partial restore: ≥1 tracked
|
|
3489
|
+
* file was refused or failed while the others converged; the message carries the per-file ledger
|
|
3490
|
+
* summary (applied / identical / refused / failed). Present ONLY under that explicit opt-in — the
|
|
3491
|
+
* default posture is the loud terminal `rewind.restore_failed` (DV-15).
|
|
3492
|
+
*
|
|
3493
|
+
* (design/381 closed-set surgery: `snapshot_store_unconfigured` is RETIRED — its only producer was
|
|
3494
|
+
* the per-task capture request, and capture is now always-on when a store is wired; a restore request
|
|
3495
|
+
* with no store keeps the fail-loud terminal instead.)
|
|
3277
3496
|
*/
|
|
3278
3497
|
rewindNotes?: Array<{
|
|
3279
|
-
code: "conversation_only" | "files_env_unsupported" | "
|
|
3498
|
+
code: "conversation_only" | "files_env_unsupported" | "restore_partial";
|
|
3280
3499
|
/** Human-readable statement of what did NOT happen and why — safe to show a user verbatim. */
|
|
3281
3500
|
message: string;
|
|
3282
3501
|
}>;
|
|
@@ -3293,6 +3512,27 @@ export interface TaskResult {
|
|
|
3293
3512
|
* (a steer/follow-up) continued the run past the rejection and it later ended naturally.
|
|
3294
3513
|
*/
|
|
3295
3514
|
haltedOnUserRejection?: true;
|
|
3515
|
+
/**
|
|
3516
|
+
* design/373 (#504) — present (`true`) exactly when a {@link TaskStream.halt} was ACCEPTED while
|
|
3517
|
+
* this run was live: the person issued the bare user interrupt (the CC Esc form — cut the
|
|
3518
|
+
* in-flight turn, stop at that boundary, wait for their next input). The ruled terminal form is
|
|
3519
|
+
* completed-with-marker, this seat being the marker: `status` is `"completed"` on the ordinary
|
|
3520
|
+
* path (a clean, resumable ending — nothing failed, nothing is suspended, the session continues
|
|
3521
|
+
* via the ordinary next-run front door), and this field is what tells a person-stopped ending
|
|
3522
|
+
* apart from a natural finish — same reading discipline as {@link haltedOnUserRejection}, its
|
|
3523
|
+
* sibling seat ("stopped by the user, awaiting their direction", never "the task finished its
|
|
3524
|
+
* work"). `result` is whatever the model had produced before the halt (possibly empty — a halt
|
|
3525
|
+
* can land before any output; the run then completes empty rather than failing). Pass-through on
|
|
3526
|
+
* EVERY terminal the run still reaches (a halt ACCEPTED first that then raced a real
|
|
3527
|
+
* failure/limit truthfully says a person also stopped it), and — the stated honest window — on
|
|
3528
|
+
* a natural completion the halt arrived too late to prevent (past the loop's final commit
|
|
3529
|
+
* point): the fact reported is the ACCEPTED HALT, and the transcript says how far the model
|
|
3530
|
+
* got. First-writer-wins against the run's abort (the `interrupt()` attribution law, the same
|
|
3531
|
+
* gate): a halt landing AFTER the abort signal already fired neither cut nor stopped anything —
|
|
3532
|
+
* that ending belongs to the abort, and this seat stays ABSENT rather than signing someone
|
|
3533
|
+
* else's stop with the halt caller's name. Absent everywhere else; never `false`.
|
|
3534
|
+
*/
|
|
3535
|
+
haltedByUser?: true;
|
|
3296
3536
|
/**
|
|
3297
3537
|
* The deliveries of AskUserQuestion calls a person ANSWERED but whose call never executed to collect
|
|
3298
3538
|
* the answer (the leg ended first — abort, batch teardown, or a loop failure). Rides EVERY terminal
|
|
@@ -3307,6 +3547,52 @@ export interface TaskResult {
|
|
|
3307
3547
|
deliveryId: string;
|
|
3308
3548
|
toolCallId: string;
|
|
3309
3549
|
}>;
|
|
3550
|
+
/**
|
|
3551
|
+
* #481 — HOST-SUPPLIED: the approval plane's report that this leg ended with human approvals still
|
|
3552
|
+
* OUTSTANDING (the ask identities it is holding, and when the OLDEST of them was created).
|
|
3553
|
+
*
|
|
3554
|
+
* **Who writes it, and why not the engine.** Core mints no ask identity — an ask id belongs to the
|
|
3555
|
+
* deployment's approval store, and `resolveAsk` only awaits the host's `onAsk` — and core starts no
|
|
3556
|
+
* approval clock. So this seat is filled by the `Runner` a deployment hands to
|
|
3557
|
+
* `runWorkflow`/`startWorkflow`: that call both RAN the leg and owns the pending-approval map, which
|
|
3558
|
+
* is the only place the two facts meet without inventing an attribution. Core's own Runner never
|
|
3559
|
+
* writes it. ABSENT is therefore the ordinary world — an older deployment, or one whose approvals are
|
|
3560
|
+
* always answered inline — and every such leg behaves exactly as it did before this seat existed.
|
|
3561
|
+
*
|
|
3562
|
+
* **Three-state, deliberately.** Absent = "this deployment is not reporting outstanding approvals, or
|
|
3563
|
+
* there are none"; present = "these asks are outstanding". An EMPTY `askIds` is neither and is
|
|
3564
|
+
* refused: it would let a half-written record read as "waiting on nothing", which is precisely the
|
|
3565
|
+
* misdiagnosis the seat exists to prevent. A present-but-malformed value is announced and then
|
|
3566
|
+
* treated as no report — never silently folded into absence, and never granted the exemption below.
|
|
3567
|
+
*
|
|
3568
|
+
* **What core does with it** (`src/orchestration/workflow.ts`): a workflow agent leg whose progress
|
|
3569
|
+
* watchdog fired while an approval was outstanding was NOT stalled — it was waiting on a person, by
|
|
3570
|
+
* design — so that attempt is not charged against the stall-retry budget. The run's own
|
|
3571
|
+
* `totalTimeoutMs` remains the backstop for a wait nobody ever answers.
|
|
3572
|
+
*/
|
|
3573
|
+
pendingApproval?: {
|
|
3574
|
+
askIds: readonly string[];
|
|
3575
|
+
oldestCreatedAtMs: number;
|
|
3576
|
+
};
|
|
3577
|
+
/**
|
|
3578
|
+
* #485 — HOST-SUPPLIED: how long this leg spent BLOCKED on human approvals, in ms, as the deployment's
|
|
3579
|
+
* approval plane measured it. Same writer and same absence contract as {@link pendingApproval}: the
|
|
3580
|
+
* approval clock belongs to whoever holds the ask, so the figure is SINGLE-SOURCED there and core neither
|
|
3581
|
+
* estimates it nor accumulates it (two sides publishing two numbers for one wait is the drift this seat
|
|
3582
|
+
* exists to avoid). ABSENT is the ordinary world.
|
|
3583
|
+
*
|
|
3584
|
+
* Already CUMULATIVE for the leg — a receipt states the leg's total wait so far, not that attempt's
|
|
3585
|
+
* increment — so a later receipt SUPERSEDES an earlier one. Adding successive receipts double-counts.
|
|
3586
|
+
*
|
|
3587
|
+
* **Absence is not zero.** `0` is "nobody was waited on"; absent is "this deployment does not supply the
|
|
3588
|
+
* fact". A consumer that defaults absence to `0` reports a deployment's silence as a measurement.
|
|
3589
|
+
*
|
|
3590
|
+
* What it is FOR: a workflow that hit its total timeout cannot otherwise say whether it was SLOW or
|
|
3591
|
+
* WAITING. `WorkflowRun.timeoutInterruption.approvalWaitedMs` carries the largest leg's figure onto the
|
|
3592
|
+
* terminal disclosure for exactly that reading — near `timeoutMs` ⇒ the window went on a person, not on
|
|
3593
|
+
* work.
|
|
3594
|
+
*/
|
|
3595
|
+
approvalWaitedMs?: number;
|
|
3310
3596
|
/**
|
|
3311
3597
|
* #240 (design/199 v1.1) — the READ-face this leg's read surfaces actually judged under, as an
|
|
3312
3598
|
* engine-filled OBSERVATION (never a knob: writing it on a spec does nothing). It is the run's ONE
|
|
@@ -3512,7 +3798,7 @@ export interface TaskResult {
|
|
|
3512
3798
|
* byte change, always paired with a `marked` disclosure),
|
|
3513
3799
|
* · `envelope` (text shaped like one of the engine's OTHER authority envelopes — the DISCLOSED
|
|
3514
3800
|
* subset is `task-notification` / `new-diagnostics` / `user_memory` / `skills` /
|
|
3515
|
-
* `total_tokens`; `scope` is
|
|
3801
|
+
* `total_tokens` / `instruction-files`; `scope` is
|
|
3516
3802
|
* fenced but not disclosed, since `<scope>…</scope>` is also an ordinary build-file element.
|
|
3517
3803
|
* That family carries no mark, so its sentence is positional rather than byte-testable. It
|
|
3518
3804
|
* rides ON the reminder copy when both families hit, so `envelope` can be bumped alongside
|
|
@@ -3691,6 +3977,25 @@ export interface BrainStatus {
|
|
|
3691
3977
|
* A wait longer than the re-announce slice is re-emitted with the REMAINING time, so a consumer
|
|
3692
3978
|
* that simply overwrites its row always shows a live number instead of a stale one. */
|
|
3693
3979
|
retryInMs?: number;
|
|
3980
|
+
/**
|
|
3981
|
+
* The WALL-CLOCK instant the announced wait is expected to end — `Date.now() + retryInMs`, read at
|
|
3982
|
+
* the moment the frame is emitted, in the EMITTING core process's clock. Stamped centrally at the
|
|
3983
|
+
* one emit point, so the invariant is total: present exactly when {@link retryInMs} is present, and
|
|
3984
|
+
* absent on every frame that announces no wait (`recovered` / `gave_up`, and the immediate
|
|
3985
|
+
* output-cap re-send, which takes no backoff).
|
|
3986
|
+
*
|
|
3987
|
+
* WHY IT IS PRODUCED HERE rather than derived by the consumer: `Date.now() + retryInMs` is only
|
|
3988
|
+
* correct for a reader that received the frame instantly. A consumer several process hops away
|
|
3989
|
+
* (serving layer → client) recomputes a deadline already shifted by the transit, so a countdown
|
|
3990
|
+
* built that way restarts on every re-announced slice instead of converging. The producer is the
|
|
3991
|
+
* only party that can state the instant, so it does.
|
|
3992
|
+
*
|
|
3993
|
+
* CLOCK DOMAIN, stated to remove the ambiguity: WALL CLOCK (`Date.now()`, epoch ms), NOT a
|
|
3994
|
+
* monotonic reading. A consumer must not compare it against its own monotonic timers, and must
|
|
3995
|
+
* treat it as approximate across a clock adjustment or a machine boundary — {@link retryInMs} stays
|
|
3996
|
+
* the authoritative RELATIVE quantity, and this seat is the absolute convenience derived from it.
|
|
3997
|
+
*/
|
|
3998
|
+
retryAtMs?: number;
|
|
3694
3999
|
/** RB-420-c — 1-based index of the attempt that just failed (the wait precedes attempt `attempt + 1`);
|
|
3695
4000
|
* same numbering as the `brain.retry` telemetry frame. Absent on frames that are not a retry wait. */
|
|
3696
4001
|
attempt?: number;
|
|
@@ -4200,7 +4505,7 @@ export type TaskEvent = ({
|
|
|
4200
4505
|
* message-identity Phase 1 (the CC `SDKCompactBoundary` parity enabler): a persisted session-tree entry was
|
|
4201
4506
|
* just COMMITTED for a RENDERABLE message the consumer has already streamed. `entryId` = `SessionTreeEntry.id`
|
|
4202
4507
|
* (uuidv7, persisted, replica-stable) — the SAME id space as `compacted.preserved_segment.firstKeptEntryId`,
|
|
4203
|
-
* E18 `resumeAt`, and
|
|
4508
|
+
* E18 `resumeAt`, and the design/381 `rewindFilesTo`. `role` + (for a tool result) `toolCallId` let a consumer bind it to
|
|
4204
4509
|
* the message it rendered: a tool result by its `toolCallId`; an assistant/user message by stream order (this
|
|
4205
4510
|
* is the terminal boundary for the content just streamed, after its deltas and before its tool results). A
|
|
4206
4511
|
* consumer builds its own `entryId → message` map from these to resolve `preserved_segment` WITHOUT the
|
|
@@ -4639,12 +4944,125 @@ export interface TaskStream extends AsyncIterable<TaskEvent> {
|
|
|
4639
4944
|
* landing after the tool finished is a no-op. Unknown toolCallIds are a no-op (nothing to detach).
|
|
4640
4945
|
*/
|
|
4641
4946
|
detach(toolCallId: string): void;
|
|
4947
|
+
/**
|
|
4948
|
+
* design/383 §2.1 — the MID-SESSION memory-capture opt-out verb ("聊着聊着发现不该记" — the
|
|
4949
|
+
* design's primary scenario): from this call on, the running session commits NOTHING to the
|
|
4950
|
+
* long-term memory store, its already-committed contributions leave the consolidation candidate
|
|
4951
|
+
* set (the retroactive A2 arm), and the session window's residue on the writable memory root is
|
|
4952
|
+
* boundary-swept into control-plane quarantine (per-path named; a sweep failure REFUSES the call
|
|
4953
|
+
* loudly — the one-way record stands either way, so the failure mode is "opt-out on, residue
|
|
4954
|
+
* named", never a silent partial).
|
|
4955
|
+
*
|
|
4956
|
+
* ONE-WAY: no reverse verb exists at any layer; a repeat call is an idempotent `"existed"`. The
|
|
4957
|
+
* way back to capture is a NEW session (a deliberate product cost — "which turns count" has no
|
|
4958
|
+
* honest answer for a retroactive re-enable).
|
|
4959
|
+
*
|
|
4960
|
+
* AUTHORITY (steer-lane law): core does not authenticate — holding this stream IS the capability
|
|
4961
|
+
* handle, exactly as with {@link steer}; a service exposing this verb over the wire owns the
|
|
4962
|
+
* verified-principal / session-ownership check at its own face (a bare sessionId relay would let
|
|
4963
|
+
* anyone switch off anyone's memory: an availability attack). The call re-adjudicates the
|
|
4964
|
+
* per-principal entitlement AT FLIP TIME (`RuntimeCaps.allowMemoryOptOut` under
|
|
4965
|
+
* {@link RunnerDeps.memoryCapturePolicy}, fresh resolve — a denied flip refuses typed
|
|
4966
|
+
* `memory.capture_optout_denied` and leaves ZERO record residue). MODEL-UNREACHABLE by
|
|
4967
|
+
* construction (§2.6): no tool face, no MCP face, and governed workflow specs cannot spell it —
|
|
4968
|
+
* a model that could declare an opt-out could silently disable a user's memory; one that could
|
|
4969
|
+
* revoke it would pierce the privacy control.
|
|
4970
|
+
*
|
|
4971
|
+
* Typed refusals (the closed set, one per arm — rescan post-6.0.0-RC completed the roster):
|
|
4972
|
+
* `memory.capture_optout_denied` (entitlement), `memory.capture_optout_unpersisted` (the record
|
|
4973
|
+
* could not be durably written — a store-mark failure OR a failing §2.7 consolidation-epoch
|
|
4974
|
+
* bump, both refused rather than held in-process, which the first resume would silently break),
|
|
4975
|
+
* `memory.capture_optout_unavailable` (this run mounted no memory session — nothing to opt out
|
|
4976
|
+
* of), `memory.capture_optout_sweep_failed` (the record STANDS but the §2.3 boundary sweep could
|
|
4977
|
+
* not contain named residue paths — a repeat call is the retry lane),
|
|
4978
|
+
* `config.memory_capture_unsupported` (remote/per-run deployment shape with no
|
|
4979
|
+
* {@link RunnerDeps.memoryCaptureRecordStore} — the record would not survive to a resume
|
|
4980
|
+
* replica), `steering.invalid_content` (a non-string `options.reason`), plus the steer-family
|
|
4981
|
+
* `steering.not_running` once the task has finished. Resolves `{ outcome }`: `"created"` = this
|
|
4982
|
+
* call made the crossing (the `memory.capture_opted_out` notice minted); `"existed"` = already
|
|
4983
|
+
* recorded (idempotent; no second notice).
|
|
4984
|
+
*/
|
|
4985
|
+
optOutMemoryCapture(options?: {
|
|
4986
|
+
reason?: string;
|
|
4987
|
+
}): Promise<{
|
|
4988
|
+
outcome: "created" | "existed";
|
|
4989
|
+
}>;
|
|
4642
4990
|
/**
|
|
4643
4991
|
* Hard-**interrupt** the running task — a real abort (not a best-effort hint), equivalent to firing the
|
|
4644
4992
|
* task's `signal` (design/47). No-op if the task already finished. For a SOFT redirect that lets the
|
|
4645
|
-
* task keep running and adjust, use {@link steer} instead.
|
|
4993
|
+
* task keep running and adjust, use {@link steer} instead. For the CC-Esc "stop and wait for my
|
|
4994
|
+
* input" form — cut the turn, keep the ending clean and resumable — use {@link halt}.
|
|
4646
4995
|
*/
|
|
4647
4996
|
interrupt(): Promise<void>;
|
|
4997
|
+
/**
|
|
4998
|
+
* design/373 (#504) — the BARE user interrupt, the CC Esc true form ("cut + stop"): CUT the
|
|
4999
|
+
* in-flight turn and STOP the run at that manufactured boundary, collecting to a clean,
|
|
5000
|
+
* resumable ending. The run-model equivalent of CC's session staying alive waiting for the
|
|
5001
|
+
* user's next input. Three verbs, three powers: {@link interrupt} hard-aborts the run (the
|
|
5002
|
+
* orphan-reconcile ending), {@link steer} with `priority:"now"` cuts the turn AND CONTINUES the
|
|
5003
|
+
* run with the steer text, `halt` cuts the turn and lets the run END awaiting the user.
|
|
5004
|
+
*
|
|
5005
|
+
* The CUT half is the design/373 S1 turn-scoped settle, verbatim: finished tool calls keep their
|
|
5006
|
+
* REAL results; never-started ones settle as paired interrupted results
|
|
5007
|
+
* (`errorKind:"interrupted_never_started"`); the CC-verbatim interruption marker lands in the
|
|
5008
|
+
* session as an engine-note user frame. Every REAL cut is announced (`task.turn_interrupted`
|
|
5009
|
+
* notice, `detail.cause:"user_halt"` — the steer-now landing keeps its own message and no
|
|
5010
|
+
* `cause` key). The STOP half: from acceptance on, NO new model turn starts — the loop ends at
|
|
5011
|
+
* whichever boundary comes first (both boundary faces consult the latch, the pre-turn one
|
|
5012
|
+
* included, so a halt landing between turns cannot buy one more turn). With nothing in flight
|
|
5013
|
+
* the cut half is a no-op (`turnCut:false`) and the stop half alone answers — no false marker.
|
|
5014
|
+
*
|
|
5015
|
+
* **Terminal form (#504 detail ①, ruled completed-with-marker over a new status/stop-reason
|
|
5016
|
+
* member):** the run ends `status:"completed"` with the additive result seat
|
|
5017
|
+
* {@link TaskResult.haltedByUser} `: true` as the discriminator. CC anchor: 250 has NO typed
|
|
5018
|
+
* "interrupted" terminal — Esc leaves a clean continuable session with the transcript marker as
|
|
5019
|
+
* the record, and the SDK result taxonomy (`error_during_execution`/`error_max_turns`/…) gains
|
|
5020
|
+
* no interrupt member; a new closed-set status/stop-reason row would therefore be a non-anchored
|
|
5021
|
+
* invention, while completed-plus-marker is also this repo's own precedent seat
|
|
5022
|
+
* ({@link TaskResult.haltedOnUserRejection} — "the model did not finish; the person stopped it
|
|
5023
|
+
* and the run awaits their direction"). Consumers discriminating "user stopped it" vs "ran to
|
|
5024
|
+
* completion" (billing, continuation copy) read the one boolean.
|
|
5025
|
+
*
|
|
5026
|
+
* **Queued-but-undelivered frames (#504 detail ②, ruled keep-don't-clear):** a halt clears
|
|
5027
|
+
* NOTHING. CC anchor: 250's `control_request:"interrupt"` handler keeps the command queue by
|
|
5028
|
+
* default and answers `still_queued`; clearing is the explicit `cancel_queued:true` opt-in
|
|
5029
|
+
* (`cleared_on_cancel`) — destruction is never the interrupt's default meaning. The run-model
|
|
5030
|
+
* translation: queued steer/follow-up frames stay queued through the cut, are NOT drained into a
|
|
5031
|
+
* turn that will never start, and settle by the run's EXISTING terminal contract for
|
|
5032
|
+
* accepted-but-undelivered input — durable park when a suspend seat exists, else the loud
|
|
5033
|
+
* per-run accounts (`task.user_steer_undrained` / `task.user_followup_undrained`). The receipt
|
|
5034
|
+
* semantics were always "queued = accepted, never consumed": the caller holds the bytes, the
|
|
5035
|
+
* account tells it what to re-send, and `inputId` idempotency makes host-side re-delivery on the
|
|
5036
|
+
* next run replay-safe. (A per-session durable park for non-suspend endings would be new storage
|
|
5037
|
+
* machinery — out of this slice, recorded.)
|
|
5038
|
+
*
|
|
5039
|
+
* **Resume hand-off (#504 detail ③, ruled the ordinary user-lane front door):** the next input
|
|
5040
|
+
* enters as the NEXT RUN's objective on the same session (`runTask`/`runTaskStream` with this
|
|
5041
|
+
* result's `sessionId`) — no special lane, no checkpoint. The halted transcript ends with the
|
|
5042
|
+
* interruption marker exactly where CC's session shows it (marker, then the user's next
|
|
5043
|
+
* message), so the resumed model reads the same shape a CC session shows after Esc. A
|
|
5044
|
+
* checkpoint-resume is neither minted nor needed: `"completed"` + `sessionId` IS the
|
|
5045
|
+
* continuation contract, and a marker-less halted transcript (nothing was in flight to cut) is
|
|
5046
|
+
* an ordinary clean boundary.
|
|
5047
|
+
*
|
|
5048
|
+
* Receipt: resolves `{ turnCut }` — `true` iff THIS call cut a live turn (the completion-race
|
|
5049
|
+
* arm is stated, not hidden: a turn whose work finished as the abort landed ends normally and
|
|
5050
|
+
* the run still stops at its boundary). Repeat calls are accepted and answer honestly
|
|
5051
|
+
* (`turnCut:false` once the stop is already latched); the notice is per-cut, the result seat
|
|
5052
|
+
* per-run. AUTHORITY (steer-lane law): holding this stream IS the capability — a deployment
|
|
5053
|
+
* relaying this verb owns its own gate (relay IS authorization; it cancels in-flight tool work
|
|
5054
|
+
* and ends the run's forward progress). No text ⇒ the `userPromptSubmit` screen has no domain
|
|
5055
|
+
* here (nothing enters the model). Honest window, stated: a halt accepted in the run's last
|
|
5056
|
+
* moments (past the loop's final commit point) finds the run completing naturally — the result
|
|
5057
|
+
* then reports the natural completion WITH the `haltedByUser` seat (the person did press stop
|
|
5058
|
+
* while it finished; the transcript says how far the model got). Typed refusals: the
|
|
5059
|
+
* steer-family `steering.not_running` once the task has finished (teardown included); a halt
|
|
5060
|
+
* issued BEFORE the run's first prompt polls the same bounded birth window as {@link steer} and
|
|
5061
|
+
* then stops the run before its first model turn (an empty, cleanly-halted completed run).
|
|
5062
|
+
*/
|
|
5063
|
+
halt(): Promise<{
|
|
5064
|
+
turnCut: boolean;
|
|
5065
|
+
}>;
|
|
4648
5066
|
/**
|
|
4649
5067
|
* Reap this task's resources — **callable after it finished**, idempotent, never throws (design/50 §3,
|
|
4650
5068
|
* design/51). The fan-out (v2) cancel primitive: an orchestrator's `cancelAll` is the shared
|
|
@@ -4771,6 +5189,31 @@ export interface RuntimeCaps {
|
|
|
4771
5189
|
* deployment resolves to a no-op (asks flow the original chain; no warn — the gate, not a mistake).
|
|
4772
5190
|
*/
|
|
4773
5191
|
autoMode?: boolean;
|
|
5192
|
+
/**
|
|
5193
|
+
* design/383 §3.1 — may this principal declare a session memory-capture OPT-OUT
|
|
5194
|
+
* (`TaskSpec.memory.capture: "off"` / the Runner flip verb)? `false` = the deployment REQUIRES
|
|
5195
|
+
* capture for this principal (compliance / knowledge retention) — the declaration is refused
|
|
5196
|
+
* terminal (`memory.capture_optout_denied`). `undefined` / `true` = no per-principal restriction.
|
|
5197
|
+
*
|
|
5198
|
+
* ⚠️ POLARITY REVERSAL — this member is the family's one deliberate exception to the
|
|
5199
|
+
* fail-closed resolver degrade (`{allowWorkflows:false, allowFork:false}`): on the PRIVACY axis
|
|
5200
|
+
* "deny" means "capture the session of a user who explicitly said no", which is the IRREVERSIBLE
|
|
5201
|
+
* side (a wrongly-captured session has been read, may be folded into products, may have synced —
|
|
5202
|
+
* deleting later does not unhappen it), while a wrongly-honored opt-out costs one uncaptured
|
|
5203
|
+
* session (recoverable). Under the default/`"open"` posture a resolver FAULT therefore reads as
|
|
5204
|
+
* ALLOW (+ a named `onError` disclosure), and only an explicit `false` denies; a deployment for
|
|
5205
|
+
* which the compliance side is the heavier irreversibility declares `memoryCapturePolicy:
|
|
5206
|
+
* "governed"`, which flips the fault arm to refuse-to-run (see {@link RunnerDeps.memoryCapturePolicy}).
|
|
5207
|
+
*
|
|
5208
|
+
* 🔒 FAULT-TRANSIT CONTRACT (server F2, written here because the resolver's habits run the other
|
|
5209
|
+
* way): a resolver failure for THIS member must surface as the member being ABSENT (`undefined`)
|
|
5210
|
+
* or as a THROW — both read as "fault". The resolver MUST NOT coin a stand-in explicit value on
|
|
5211
|
+
* failure (the `{allowWorkflows:false}` degrade habit): an explicit `false` IS the denied
|
|
5212
|
+
* verdict, and encoding an outage as one silently swaps the fault arm for the verdict arm — under
|
|
5213
|
+
* `"open"` that is the exact polarity inversion this member exists to prevent. A non-boolean
|
|
5214
|
+
* value here is treated as a fault (never truthiness-read) and disclosed.
|
|
5215
|
+
*/
|
|
5216
|
+
allowMemoryOptOut?: boolean;
|
|
4774
5217
|
}
|
|
4775
5218
|
/**
|
|
4776
5219
|
* design/129-B — one lifecycle event of a BACKGROUND delegation child, delivered to the
|
|
@@ -5071,7 +5514,9 @@ export interface EngineNotice {
|
|
|
5071
5514
|
* `autoCompactTokens` ABOVE its physical window; the trigger-side geometry was clamped to the
|
|
5072
5515
|
* physical window (an autocompact window only ever lowers the trigger) and the bad value is
|
|
5073
5516
|
* announced once per prepared task; `detail: { modelId, declaredAutoCompactTokens,
|
|
5074
|
-
* physicalWindow, sessionId }
|
|
5517
|
+
* physicalWindow, sessionId, runId }` — `runId` (#499) is what makes the "once per prepared
|
|
5518
|
+
* task" claim checkable: the session id beside it is shared by every run of the session, so two
|
|
5519
|
+
* runs clamping the same model used to mint byte-identical lines.
|
|
5075
5520
|
* - `"config.env_timeout_discarded"` — a Bash timeout knob (option or env) held a value that is not
|
|
5076
5521
|
* the value in force; `detail: { knob, raw, usedMs }`.
|
|
5077
5522
|
* - `"config.materialize_env_discarded"` — `SEMA_TOOL_MATERIALIZE_STRATEGY` held a value outside the
|
|
@@ -5079,35 +5524,61 @@ export interface EngineNotice {
|
|
|
5079
5524
|
* - `"tool_result.offload_put_failed"` (#167) — a clear-with-offload persist's fire-and-forget put
|
|
5080
5525
|
* failed; THIS attempt stored nothing (the failure arm reports, it never re-inserts under the
|
|
5081
5526
|
* ref) — an earlier attempt of the same idempotent re-put may already have stored the row, so
|
|
5082
|
-
* the notice claims a failed write, not an empty ref;
|
|
5083
|
-
*
|
|
5527
|
+
* the notice claims a failed write, not an empty ref;
|
|
5528
|
+
* `detail: { ref, sessionId, cause, occurrenceId }`.
|
|
5529
|
+
* Per-occurrence, not per-process-deduplicated: each failed write is a distinct fact — and
|
|
5530
|
+
* `occurrenceId` (#495, uuidv7 minted at the failure) is what lets a consumer act on that:
|
|
5531
|
+
* `ref` names the ROW, and the same ref is attempted again on every later re-clear, so two
|
|
5532
|
+
* genuine losses of one ref were byte-identical notices. Dedup key: (code, occurrenceId).
|
|
5084
5533
|
* - `"workflow.governance_key_stripped"` (#235) — the fields of an LLM-authored workflow `agent(spec)`
|
|
5085
5534
|
* that did NOT cross the governed default-deny whitelist: an unrecognized/control-plane field, or a
|
|
5086
5535
|
* `readFace` value that is not the containment-tightening one. The spawn PROCEEDS on the deployment
|
|
5087
5536
|
* baseline (the strip is not a refusal in this window), which is why the drop is announced;
|
|
5088
|
-
* `detail: { total, stripped: [{ key, reason }], omitted
|
|
5089
|
-
* and length because the names come from the untrusted script. One aggregated
|
|
5090
|
-
* child build, not de-duplicated across builds: each spec is a distinct fact
|
|
5537
|
+
* `detail: { total, stripped: [{ key, reason }], omitted?, occurrenceId }`, the rendered key list
|
|
5538
|
+
* bounded in count and length because the names come from the untrusted script. One aggregated
|
|
5539
|
+
* notice per governed child build, not de-duplicated across builds: each spec is a distinct fact —
|
|
5540
|
+
* `occurrenceId` (#495, uuidv7 minted at the announcement) carries that claim to the consumer,
|
|
5541
|
+
* which otherwise saw two builds of the same spec as one repeated notice. Dedup key:
|
|
5542
|
+
* (code, occurrenceId).
|
|
5543
|
+
* - `"workflow.agent_option_ignored"` (#505 ①) — the OPTIONS-object twin of the row above: keys of
|
|
5544
|
+
* `agent(spec, opts)`'s SECOND argument that were not applied (unrecognized, or a known option
|
|
5545
|
+
* whose value its type check rejected — `label: 42`). The spawn PROCEEDS without them, which is
|
|
5546
|
+
* why the drop is announced. A separate code because the two membranes accept different key
|
|
5547
|
+
* sets and the message names its own. `detail: { total, ignored: [key], omitted?, occurrenceId }`,
|
|
5548
|
+
* same bounding and same per-call (never de-duplicated) semantics as the spec-side row; dedup
|
|
5549
|
+
* key: (code, occurrenceId). NOT the whole family: a SPEC field written on the options object
|
|
5550
|
+
* (`model`, `modelName`, `thinking`, …) is REFUSED at the membrane instead — a silently ignored
|
|
5551
|
+
* `model` runs a different agent than the author asked for.
|
|
5091
5552
|
* - `"mcp.revocation_probe_failed"` (design/338) — the deployment's `mcpRevocations.isRevoked`
|
|
5092
5553
|
* probe threw; MCP dispatch FAILS OPEN (revocation is a tightening face) and this announces
|
|
5093
|
-
* once per materialization (a resume re-materializes and may announce again).
|
|
5094
|
-
* — no sessionId (a deployment wiring fact, not
|
|
5554
|
+
* once per materialization (a resume re-materializes and may announce again).
|
|
5555
|
+
* `detail: { message, runId }` — still no sessionId (a deployment wiring fact, not
|
|
5556
|
+
* session-attributed); `runId` (#499) makes the once-per-materialization claim CHECKABLE without
|
|
5557
|
+
* attributing the line to a conversation, an opaque invocation id saying which materialization
|
|
5558
|
+
* rather than whose. `"operator"` audience by
|
|
5095
5559
|
* its explicit {@link NOTICE_AUDIENCE} row (#433 made the registry total over the catalog —
|
|
5096
5560
|
* no engine-minted code is audience-defaulted any more). The refusal itself
|
|
5097
5561
|
* (`mcp.server_revoked`) is a tool RESULT code, not a notice.
|
|
5098
5562
|
* - `"config.models_swapped"` — `Runner.swapModels` replaced the model catalog generation
|
|
5099
|
-
* (zero-restart model switching). `detail: { models, tiers }` — key COUNTS only,
|
|
5563
|
+
* (zero-restart model switching). `detail: { models, tiers, occurrenceId }` — key COUNTS only,
|
|
5564
|
+
* never the
|
|
5100
5565
|
* catalog itself. In-flight tasks finish on the models they resolved at prepare (natural
|
|
5101
5566
|
* snapshot); every later prepare resolves against the new generation. A failed swap (illegal
|
|
5102
5567
|
* tier binding) throws atomically and mints nothing.
|
|
5568
|
+
* `occurrenceId` (#495, uuidv7 minted at the announcement): the verb carries no run and no
|
|
5569
|
+
* session, and the payload is a pair of counts, so two swaps of the same shape were byte-
|
|
5570
|
+
* identical lines. Dedup key: (code, occurrenceId).
|
|
5103
5571
|
* - `"route.fallback_to_primary"` (key↔URL pairing, `src/brain/route-adjudicator.ts`) — a
|
|
5104
5572
|
* DERIVED-leg model (role/tier/system-default resolution, never a caller-explicit one) failed
|
|
5105
5573
|
* the pairing pre-flight and the seat fell back to the primary model instead of sinking the
|
|
5106
5574
|
* task; the notice is the loud half of that swap.
|
|
5107
|
-
* `detail: { seat, from, to, cause, fixHint, sessionId? }` — `cause` is the refusal code
|
|
5575
|
+
* `detail: { seat, from, to, cause, fixHint, sessionId?, runId? }` — `cause` is the refusal code
|
|
5108
5576
|
* (`route.credential_mismatch` / `route.credential_missing`); `sessionId` (#433, additive)
|
|
5109
|
-
* rides when the adjudicating seat knows its session
|
|
5110
|
-
*
|
|
5577
|
+
* rides when the adjudicating seat knows its session, and `runId` (#499, additive) names the
|
|
5578
|
+
* INVOCATION whose seats were being resolved — both correlation keys only, the audience
|
|
5579
|
+
* stays `"operator"` (see the registry's entitlement-not-routability note). Two runs of one
|
|
5580
|
+
* session falling back on the same seat used to mint byte-identical lines; `runId` is what
|
|
5581
|
+
* tells one task falling back twice apart from two tasks falling back once each.
|
|
5111
5582
|
* Explicitly-named models never mint this: they refuse at the brain's request gate instead.
|
|
5112
5583
|
* - `"route.base_url_changed_key_unchanged"` (key↔URL pairing) — `Runner.swapModels` moved a
|
|
5113
5584
|
* same-name entry's `baseUrl` while its Model-visible credential half (auth-bearing headers)
|
|
@@ -5115,8 +5586,11 @@ export interface EngineNotice {
|
|
|
5115
5586
|
* PROVIDER changed, the credential reference needs the same update. Advisory only, never a
|
|
5116
5587
|
* refusal; per-model-hook credentials are invisible to the catalog swap, so the notice is
|
|
5117
5588
|
* deliberately conservative (it may fire when a hook-side credential DID change in lockstep).
|
|
5118
|
-
* One aggregated notice per swap; `detail: { entries: [{ modelId, from, to }], total
|
|
5119
|
-
* (rendered list bounded, total always exact).
|
|
5589
|
+
* One aggregated notice per swap; `detail: { entries: [{ modelId, from, to }], total,
|
|
5590
|
+
* occurrenceId }` (rendered list bounded, total always exact). `occurrenceId` (#495, uuidv7
|
|
5591
|
+
* minted at the announcement, independent of the `config.models_swapped` line's own): the
|
|
5592
|
+
* bounded render makes two swaps moving the same first entries byte-identical.
|
|
5593
|
+
* Dedup key: (code, occurrenceId).
|
|
5120
5594
|
* - `"config.read_face_deployment_clamped"` (#237) — a deployment-wide `readFace: "open"` is not
|
|
5121
5595
|
* in force beside a read-only (verifier) mount: it clamps to "roots" without throwing
|
|
5122
5596
|
* (stricter-wins; the clamp verdict stands, only its occurrence was undisclosed). Announced
|
|
@@ -5152,10 +5626,13 @@ export interface EngineNotice {
|
|
|
5152
5626
|
* frame's two count keys). They are NOT redelivered (a steer aimed at a finished run must not
|
|
5153
5627
|
* fire at the next one — unlike ENGINE notes, which pend per session); the loud half of the
|
|
5154
5628
|
* #257 contract's "accepted = enqueued, not consumed" sentence;
|
|
5155
|
-
* `detail: { steer, taskId?, sessionId? }` / `{ followUp, taskId?, sessionId? }`
|
|
5156
|
-
* = #433's routing half for these two `"user"`-audience rows
|
|
5157
|
-
*
|
|
5158
|
-
*
|
|
5629
|
+
* `detail: { steer, taskId?, sessionId?, runId? }` / `{ followUp, taskId?, sessionId?, runId? }`
|
|
5630
|
+
* (`sessionId` = #433's routing half for these two `"user"`-audience rows; `runId` = #499's
|
|
5631
|
+
* per-run identity, the key this account is actually about — see
|
|
5632
|
+
* {@link undrainedUserInputNotices}; all omitted when the caller has none, never fabricated).
|
|
5633
|
+
* Per-run, at most once per family (the terminal sweep is a single site) — which is exactly why
|
|
5634
|
+
* `taskId`/`sessionId` cannot key it: both answer the same value for two runs of one session,
|
|
5635
|
+
* so two runs each stranding one steer folded into one indistinguishable row.
|
|
5159
5636
|
* **#389 (two corrections).** ① The family now fires on the INTERRUPT path too: `abort()` used
|
|
5160
5637
|
* to empty both queues before agent_end could count them, so the one loss path an operator most
|
|
5161
5638
|
* needs to hear about was the one path that stayed silent. ② On a DURABLE PARK the verdict is
|
|
@@ -5164,17 +5641,29 @@ export interface EngineNotice {
|
|
|
5164
5641
|
* — what is announced is the remainder that could NOT be carried, so a fully-migrated park says
|
|
5165
5642
|
* nothing at all and a queue-full / no-longer-pending row still says exactly what was lost.
|
|
5166
5643
|
*
|
|
5167
|
-
* - `"task.turn_interrupted"` (design/373) — a
|
|
5168
|
-
*
|
|
5169
|
-
*
|
|
5170
|
-
*
|
|
5171
|
-
* with
|
|
5172
|
-
*
|
|
5173
|
-
*
|
|
5174
|
-
*
|
|
5175
|
-
*
|
|
5176
|
-
*
|
|
5177
|
-
*
|
|
5644
|
+
* - `"task.turn_interrupted"` (design/373) — a running turn was ACTUALLY CUT at a manufactured
|
|
5645
|
+
* boundary: the in-flight provider stream / tool batch was aborted (finished tool calls keep
|
|
5646
|
+
* their real results, never-started ones settle as paired interrupted results, the interruption
|
|
5647
|
+
* marker lands). **TWO mint lanes, one code** — the CUT is lane-independent, so it is one fact
|
|
5648
|
+
* with one spelling, and `detail.cause` is the arm discriminator (a consumer that must tell
|
|
5649
|
+
* them apart reads that key; one that only cares "a turn was cut" reads neither):
|
|
5650
|
+
* · the STEER-NOW lane (design/373) — a caller-provenance steer with `priority: "now"` cut
|
|
5651
|
+
* the turn and the run CONTINUES with the steer at the queue head.
|
|
5652
|
+
* `detail: { inputId, sessionId, runId, actorId?, taskId? }`, NO `cause` key — `inputId` is
|
|
5653
|
+
* the steer's own correlation key (the `human_input` frame's id) and `actorId` the caller's
|
|
5654
|
+
* asserted identity when one rode the steer.
|
|
5655
|
+
* · the BARE-HALT lane (#504, {@link TaskStream.halt}) — the person's Esc-form stop cut the
|
|
5656
|
+
* turn and the run does NOT continue: it collects to a clean, resumable
|
|
5657
|
+
* `status:"completed"` ending carrying {@link TaskResult.haltedByUser}.
|
|
5658
|
+
* `detail: { cause: "user_halt", sessionId, runId, taskId? }` — no `inputId` and no
|
|
5659
|
+
* `actorId`, because no text entered the model and the verb carries no caller identity. A
|
|
5660
|
+
* consumer keying `detail.inputId` off this row must treat it as ABSENT on this lane.
|
|
5661
|
+
* In both lanes: one notice per REAL cut (a `now` that found nothing in flight or whose frame
|
|
5662
|
+
* already rode the imminent boundary, and a halt with nothing in flight, announce nothing — no
|
|
5663
|
+
* false interrupt claims); `runId` (#499) is the INVOCATION that was cut (the other two ids are
|
|
5664
|
+
* shared by every run of the session); every cut turn counts toward `maxTurns`, so an interrupt
|
|
5665
|
+
* storm's cost is bounded and each of its cuts is on the record. Audience `"user"`: the person
|
|
5666
|
+
* who forced the boundary is the one entitled to see that it landed.
|
|
5178
5667
|
* RETIRED here (BREAKING, named): `"task.injection_priority_unimplemented"` — the ladder is
|
|
5179
5668
|
* implemented, so the unhonored-knob disclosure it carried has no referent; consumers must
|
|
5180
5669
|
* judge ladder support by VERSION, never by that code's absence.
|
|
@@ -5187,8 +5676,10 @@ export interface EngineNotice {
|
|
|
5187
5676
|
* same fact is the entry's own `human_input` account with `delivery: "blocked"` — the two share
|
|
5188
5677
|
* the inputId, so "accepted at park, screened at redelivery" is auditable end to end. Audience
|
|
5189
5678
|
* `"user"` (the person whose instruction was withheld is the one entitled to re-issue it);
|
|
5190
|
-
* `detail: { inputId?, sessionId, taskId? }` — `inputId` is the parked entry's stored
|
|
5191
|
-
* correlation key
|
|
5679
|
+
* `detail: { inputId?, sessionId, runId, taskId? }` — `inputId` is the parked entry's stored
|
|
5680
|
+
* correlation key (OPTIONAL: a row parked without one exists), and `runId` (#499) names the
|
|
5681
|
+
* resume leg that withheld it, so a session resumed twice does not report two withholdings as
|
|
5682
|
+
* one.
|
|
5192
5683
|
*
|
|
5193
5684
|
* - `"memory.session_polluted"` (design/178 §3, #324a; message mode-aware since design/336) —
|
|
5194
5685
|
* this session's memory crossed into the one-way externally-exposed state (a tool classified
|
|
@@ -5216,8 +5707,13 @@ export interface EngineNotice {
|
|
|
5216
5707
|
* two numbers are read off the report's rows, never subtracted from each other). One notice PER
|
|
5217
5708
|
* HARVEST that withheld at least one entry file (a checkpoint harvest and the terminal harvest
|
|
5218
5709
|
* are distinct facts), never minted for a clean session; `detail: { count, moved, escalated,
|
|
5219
|
-
* reason?, sessionId? }` (`reason` absent ⇔ the pollution marker could not be re-read
|
|
5220
|
-
* time — the withheld count stays true either way).
|
|
5710
|
+
* reportId?, reason?, sessionId? }` (`reason` absent ⇔ the pollution marker could not be re-read
|
|
5711
|
+
* at report time — the withheld count stays true either way). `reportId` (#479) is the minting
|
|
5712
|
+
* harvest's own occurrence identity ({@link import("./memory-engine/types.js").HarvestReport}
|
|
5713
|
+
* `.reportId`, uuidv7): two harvests whose counts and paths coincide carry DISTINCT ids, a
|
|
5714
|
+
* durable replay of the same notice carries the SAME id, and the hold family below shares this
|
|
5715
|
+
* harvest's id — dedup on `(code, detail.reportId)`, never on `reportId` alone (absent only for
|
|
5716
|
+
* a hand-built report; every engine-minted report carries it). The formerly registered index-only gap is
|
|
5221
5717
|
* CLOSED under `memoryProvenance: "carry"` (design/336 §6.3, #331): the mint condition reads
|
|
5222
5718
|
* `HarvestReport.containment`, so a containment whose only act was the derived-index rollback
|
|
5223
5719
|
* announces with `count: 0` and `detail.indexRolledBack: true`; under `"off"` the pre-336
|
|
@@ -5226,15 +5722,18 @@ export interface EngineNotice {
|
|
|
5226
5722
|
* §4/§6.3) — the instruction-hold lifecycle, derived from the same structured
|
|
5227
5723
|
* `HarvestReport.containment` signal (hold seats are only ever filled under
|
|
5228
5724
|
* `memoryProvenance: "carry"`): a PENDING session's instruction-form entry files were captured
|
|
5229
|
-
* off the model-visible plane (`hold_opened`, `detail: { count, paths, sessionId? }`
|
|
5230
|
-
* neutralized/length-bounded); previously held entries re-walked the full gate set and
|
|
5725
|
+
* off the model-visible plane (`hold_opened`, `detail: { count, paths, reportId?, sessionId? }`
|
|
5726
|
+
* — paths neutralized/length-bounded); previously held entries re-walked the full gate set and
|
|
5231
5727
|
* committed after their writer session settled clean or a host valve released them
|
|
5232
|
-
* (`hold_released
|
|
5233
|
-
* `detail.disposed: [{ path, terminal }]` with the closed terminal set
|
|
5728
|
+
* (`hold_released`, same detail shape); held entries moved to control-plane quarantine
|
|
5729
|
+
* (`hold_disposed`, `detail.disposed: [{ path, terminal }]` with the closed terminal set
|
|
5234
5730
|
* dirty/expired/conflict/capture_lost/discarded — an `expired` terminal is explicitly a
|
|
5235
5731
|
* TIMEOUT, not a conviction, and the message names `resolveHold` as the recovery valve). At
|
|
5236
5732
|
* most one notice per family per harvest report; wording is factual, never threat-flavored
|
|
5237
|
-
* (the design/336 §13-2 model-psyche guardrail).
|
|
5733
|
+
* (the design/336 §13-2 model-psyche guardrail). `detail.reportId` (#479) is shared by all
|
|
5734
|
+
* three families AND `"memory.harvest_quarantined"` when they derive from the same harvest
|
|
5735
|
+
* report — that sharing is the contract (one report, several distinct facts): dedup on
|
|
5736
|
+
* `(code, detail.reportId)`; a repeat with the same pair is a replay, a new pair is a new fact.
|
|
5238
5737
|
* - `"memory.delegation_static_mark_waived"` (design/324, #324 ruling ①) — the deployment set
|
|
5239
5738
|
* {@link RunnerDeps.memoryDelegationEvidence} to `"attested-only"` and a delegation call whose
|
|
5240
5739
|
* STATIC tool-face verdict would have marked this session's memory polluted (attestation
|
|
@@ -5248,7 +5747,9 @@ export interface EngineNotice {
|
|
|
5248
5747
|
* TASK LEG (a resume leg may announce again — a new leg's audit stream is a new fact;
|
|
5249
5748
|
* deliberately no durable once-per-session state), never on the `"static-face"` default, and
|
|
5250
5749
|
* never for a delivered `"external"` attestation (that mark still lands);
|
|
5251
|
-
* `detail: { reason, subagentType?, sessionId
|
|
5750
|
+
* `detail: { reason, subagentType?, sessionId?, runId }` — `runId` (#499) is what makes the
|
|
5751
|
+
* ONCE-PER-LEG guarantee above checkable, since a resume leg may legitimately announce again and
|
|
5752
|
+
* the session id cannot tell the two legs apart; `reason` is the same sentence the waived
|
|
5252
5753
|
* mark would have carried, neutralized/length-bounded (tool and agent-type names are
|
|
5253
5754
|
* host/model-controlled inputs).
|
|
5254
5755
|
* - `"memory.content_class_declared"` (design/378) — an `McpServerSpec` entry carries an explicit
|
|
@@ -5263,7 +5764,10 @@ export interface EngineNotice {
|
|
|
5263
5764
|
* condition as the classification it talks about — a leg with no engine-memory session and no
|
|
5264
5765
|
* provenance recorder classifies nothing, so there is no posture to report and no line is
|
|
5265
5766
|
* minted. `detail: { server,
|
|
5266
|
-
* contentOrigin, toolCount, execIsExternalContent?, sessionId
|
|
5767
|
+
* contentOrigin, toolCount, execIsExternalContent?, sessionId?, runId }` — `runId` (#499) names
|
|
5768
|
+
* the LEG, which is what "one line per declared entry per prepared task leg" is a claim about:
|
|
5769
|
+
* the session id beside it is shared by every leg of the session, so two legs reading one
|
|
5770
|
+
* declaration used to mint byte-identical rows. `server` is the
|
|
5267
5771
|
* host-authored entry name, neutralized/length-bounded; `toolCount` is what this entry actually
|
|
5268
5772
|
* mounted (0 for a server that failed to connect — the declaration still stands and is still
|
|
5269
5773
|
* disclosed); `execIsExternalContent` rides the `"execution"` value only and reports the strict
|
|
@@ -5273,11 +5777,60 @@ export interface EngineNotice {
|
|
|
5273
5777
|
* long after the dial, and D-10 already rules topology is not evidence of the lineage this line
|
|
5274
5778
|
* audits — connection facts live on `MaterializedMcp.statuses` instead.
|
|
5275
5779
|
*
|
|
5780
|
+
* - `"memory.capture_opted_out"` (design/383 §2.8) — memory capture is OFF for this session and
|
|
5781
|
+
* the switch provably TOOK: nothing the session produces will be committed to the long-term
|
|
5782
|
+
* store and its contributions no longer enter consolidation. The user must be able to SEE the
|
|
5783
|
+
* switch land rather than infer it from silence, which is why it is announced rather than left
|
|
5784
|
+
* implicit. States three facts and an exit, and deliberately overclaims none of them: entries
|
|
5785
|
+
* committed BEFORE the opt-out remain in the library and stay retrievable, entries already
|
|
5786
|
+
* folded into consolidation products leave those products unaffected, and deleting committed
|
|
5787
|
+
* content is a separate host action (the erase valve), never a side effect of this switch.
|
|
5788
|
+
* `detail: { sessionId, origin }` — `origin` is the closed set
|
|
5789
|
+
* `"declaration" | "flip-verb" | "standing-record" | "delegation-floor" | "fork-origin"`, and
|
|
5790
|
+
* the message is written per value (a delegated child's inherited floor and a fork's inherited
|
|
5791
|
+
* one-way record say so, rather than claiming the session declared it). Audience `"user"`.
|
|
5792
|
+
* Dedup unit: once per SESSION — key `(code, detail.sessionId)`, so the standing-record resume
|
|
5793
|
+
* arm folds into the standing entry instead of double-announcing.
|
|
5794
|
+
* - `"memory.capture_optout_unpersisted"` (design/383 §2.8) — the OPPOSITE receipt, and a
|
|
5795
|
+
* separate code on purpose ("it is off for you" and "it could not be turned off" must never
|
|
5796
|
+
* share a spelling): the opt-out could NOT be durably recorded (the control-plane write
|
|
5797
|
+
* failed), so it is REFUSED rather than honored in-process under a switch that would silently
|
|
5798
|
+
* stop holding at the next resume. Minted only beside that refusal — the throw carrying the
|
|
5799
|
+
* same code — never beside a success, and no success notice is minted on this arm.
|
|
5800
|
+
* `detail: { sessionId }`. Audience `"user"` (the operator repairs the store, but the
|
|
5801
|
+
* entitlement to the fact belongs to whoever asked for the privacy). **TWO ingresses, and they
|
|
5802
|
+
* settle differently — a consumer must not read one as the other:**
|
|
5803
|
+
* · PREPARE-TIME (a declared/inherited opt-out, before the run starts) — the refusal is
|
|
5804
|
+
* TERMINAL: the task never runs and nothing is captured. Dedup unit: once per task, since
|
|
5805
|
+
* the refusal it rides settles the task.
|
|
5806
|
+
* · the LIVE FLIP VERB ({@link TaskStream.optOutMemoryCapture} mid-run) — the refusal is
|
|
5807
|
+
* SCOPED TO THAT CALL: it rejects the verb's promise and nothing else. The run keeps going
|
|
5808
|
+
* and capture is still ENABLED (the switch did not take), which is exactly why the notice
|
|
5809
|
+
* is owed. Because no record was retained, a retry can fail the same way, so this ingress
|
|
5810
|
+
* can announce ONCE PER ATTEMPT — a consumer de-duplicating per task would hide later
|
|
5811
|
+
* failures of a switch the person is still trying to throw.
|
|
5812
|
+
* Read the run's own outcome (or the verb's rejection) to tell the two apart; this code alone
|
|
5813
|
+
* does not say whether anything stopped.
|
|
5276
5814
|
* - `"memory.consolidation_incomplete"` (design/376, LLM consolidation driver) — a driver run
|
|
5277
5815
|
* settled without reaching the fixpoint: `detail` names the stop reason (the closed
|
|
5278
5816
|
* `ConsolidationRunStopReason` set), cycles done, and the residue (write-failure or
|
|
5279
5817
|
* fuse-refused groups by name). Advisory: committed cycles stand (add-only, never rolled
|
|
5280
5818
|
* back); the recovery verb is re-running the host driver, which resumes the same pending run.
|
|
5819
|
+
* - `"memory.consolidation_withheld"` (design/383 §1.4) — the exposure partition engaged on a
|
|
5820
|
+
* consolidation run, so the CLEAN arm's authoring prompts were composed WITHOUT the marked
|
|
5821
|
+
* rows: anything this run's clean products say was written without them, and their silence on a
|
|
5822
|
+
* topic therefore does not mean the library is silent on it (the withheld rows still stand and
|
|
5823
|
+
* are still readable). TWO counts, deliberately separate sentences and separate keys —
|
|
5824
|
+
* `withheld` rows were kept out of the clean arm's prompts, and `neverSent` of those were not
|
|
5825
|
+
* sent to the consolidation model AT ALL (a lone marked row has no group to fold, so it is
|
|
5826
|
+
* withheld from every call). "Withheld from the clean arm" and "never left this machine" are
|
|
5827
|
+
* different facts about a person's own data and must not be merged into one comforting number,
|
|
5828
|
+
* which is why `sentToProvider` is carried as its own member rather than left to subtraction.
|
|
5829
|
+
* `detail: { scope, runId, withheld, neverSent, sentToProvider }` — `scope` is
|
|
5830
|
+
* neutralized/length-bounded. Audience `"user"`: the ONE consolidation row that is a reader's
|
|
5831
|
+
* fact rather than an operator's (the other five report what a host-driven maintenance run DID;
|
|
5832
|
+
* this one reports what the products a person will READ were written without). Minted once per
|
|
5833
|
+
* run, beside the receipt — the dedup unit.
|
|
5281
5834
|
* - `"memory.consolidation_driver_superseded"` is an ERROR code, not a notice (named here only
|
|
5282
5835
|
* to keep the family's spellings in one place): a concurrent driver invocation took over this
|
|
5283
5836
|
* scope's run row (attempt fencing) and the losing worker's call THROWS with this `code` —
|
|
@@ -5298,7 +5851,9 @@ export interface EngineNotice {
|
|
|
5298
5851
|
* session count crossed the consolidation thresholds (time gate open ∧ enough distinct
|
|
5299
5852
|
* sessions); minted at most once per crossing (a completed run re-arms the edge), NEVER when
|
|
5300
5853
|
* the deployment leaves consolidation off. ADVISORY: the host owns the verbs, nothing runs
|
|
5301
|
-
* automatically; `detail: { scope, sessionsSince, sessionId? }
|
|
5854
|
+
* automatically; `detail: { scope, sessionsSince, reportId?, sessionId? }` — `reportId` (#479)
|
|
5855
|
+
* is the minting harvest report's occurrence identity, shared with the harvest/hold family's
|
|
5856
|
+
* notices of the same report (dedup on `(code, detail.reportId)`).
|
|
5302
5857
|
* - `"memory.consolidation_committed"` (design/339 §6.2) — a consolidation plan reached
|
|
5303
5858
|
* `completed`: products landed, superseded targets left the default read face (retained as
|
|
5304
5859
|
* evidence), intents settled; `detail: { planId, scope, products, superseded, intents }` —
|
|
@@ -5313,7 +5868,10 @@ export interface EngineNotice {
|
|
|
5313
5868
|
* - `"memory.consolidation_refused"` (design/339 §6.2) — the notice dialect of a consolidation
|
|
5314
5869
|
* verb's coded structured refusal (the verb itself throws
|
|
5315
5870
|
* {@link import("../core/memory-engine/consolidation.js").ConsolidationRefusedError});
|
|
5316
|
-
* `detail: { refusalCode, scope? }`.
|
|
5871
|
+
* `detail: { refusalCode, scope?, occurrenceId? }`. `occurrenceId` (#495) is the THROWN ERROR's
|
|
5872
|
+
* own id, minted once where the refusal happens and read off it by the notice factory — one
|
|
5873
|
+
* throw rendered twice keeps one id, two refusals sharing a code/scope/message get two. Omitted
|
|
5874
|
+
* (never fabricated) when the caller composes the line with no error in hand.
|
|
5317
5875
|
*
|
|
5318
5876
|
* Deliberately NOT a notice family: brain retry/reconnect liveness (a rate limit, a 5xx, a
|
|
5319
5877
|
* transient network failure being retried). Those are per-attempt liveness frames with their own
|
|
@@ -5371,11 +5929,67 @@ export declare function deliverEngineNotice(onNotice: ((notice: EngineNotice) =>
|
|
|
5371
5929
|
* — the one throat — lifts it to the typed top-level key and the two spellings cannot disagree.
|
|
5372
5930
|
* Omitted when the caller has none (never fabricated: the message is what the run lost, and a made-up
|
|
5373
5931
|
* routing key would deliver it to the wrong stream).
|
|
5932
|
+
*
|
|
5933
|
+
* `runId` (#499, additive and optional): THE key for this account. The two ids above name the task and
|
|
5934
|
+
* the conversation, and both answer the same value for two runs of one session whenever the host
|
|
5935
|
+
* supplies no task id — so two runs each stranding one steer produced two byte-identical rows, read as
|
|
5936
|
+
* one repeated disclosure about one run. The account is per-RUN by its own contract sentence ("at most
|
|
5937
|
+
* once per family, per run"), so it must carry the run's own id. Same omit-never-fabricate rule.
|
|
5374
5938
|
*/
|
|
5375
5939
|
export declare function undrainedUserInputNotices(counts: {
|
|
5376
5940
|
steer: number;
|
|
5377
5941
|
followUp: number;
|
|
5378
|
-
}, taskId?: string, sessionId?: string): EngineNotice[];
|
|
5942
|
+
}, taskId?: string, sessionId?: string, runId?: string): EngineNotice[];
|
|
5943
|
+
/**
|
|
5944
|
+
* design/380 O2 — the facts handed to a deployment's {@link RunnerDeps.resumePreflight}: the row's
|
|
5945
|
+
* own recorded identity + placement record, read off the persisted checkpoint (never off the resume
|
|
5946
|
+
* caller's bag), so the hook judges the SAME row the CAS is about to consume.
|
|
5947
|
+
*/
|
|
5948
|
+
export interface ResumePreflightInfo {
|
|
5949
|
+
token: import("./checkpoint-store.js").CheckpointToken;
|
|
5950
|
+
sessionId: string;
|
|
5951
|
+
/** design/380 O1③ — the row's recorded placement root, when stamped. */
|
|
5952
|
+
placementRootSessionId?: string;
|
|
5953
|
+
/** The row's recorded identity ({@link import("./checkpoint-store.js").Checkpoint.principal}), when stamped —
|
|
5954
|
+
* the resume entry's identity-continuity rung already refused a contradicting supplied principal
|
|
5955
|
+
* before this hook runs, so a present value IS the resumed leg's identity. */
|
|
5956
|
+
principal?: string;
|
|
5957
|
+
/** provider / deviceId / mountPath — the placement facts. */
|
|
5958
|
+
workspaceHandle?: import("./remote-env.js").WorkspaceHandle;
|
|
5959
|
+
gateKind: import("./checkpoint-store.js").CheckpointGate["kind"];
|
|
5960
|
+
}
|
|
5961
|
+
/** design/380 O2 — a {@link RunnerDeps.resumePreflight} answer. */
|
|
5962
|
+
export type ResumePreflightVerdict = {
|
|
5963
|
+
ok: true;
|
|
5964
|
+
}
|
|
5965
|
+
/** Transient obstacle (device offline, dependency briefly down): the checkpoint stays `pending`,
|
|
5966
|
+
* the SAME token is redeemable later; retryAfterMs threads the wait hint (the #449 G1 carrier). */
|
|
5967
|
+
| {
|
|
5968
|
+
ok: false;
|
|
5969
|
+
disposition?: "retry_later";
|
|
5970
|
+
message: string;
|
|
5971
|
+
retryAfterMs?: number;
|
|
5972
|
+
}
|
|
5973
|
+
/** PERMANENT obstacle (binding revoked, placement identity gone): the row is settled TERMINALLY —
|
|
5974
|
+
* core CASes it out of `pending` before answering, so a dead binding cannot
|
|
5975
|
+
* be redialed forever (prose-terminal + mechanically-retriable = unbounded redeem). The `expire`
|
|
5976
|
+
* CAS is a pure STATUS flip — the row records no reason (the store seam has no seat for one);
|
|
5977
|
+
* the REASON travels on the typed `resume.preflight_rejected` refusal (the hook's `message`
|
|
5978
|
+
* inlined), the `onError` disclosure sink, and the deployment hook's own audit plane — it
|
|
5979
|
+
* authored the verdict and owns the durable record of why. The exact
|
|
5980
|
+
* terminal CAS form is the single-shot `expire` CAS (reaper parity — exactly one settler wins; a
|
|
5981
|
+
* LOST race is re-read and reported as concurrent movement, never claimed as this refusal's
|
|
5982
|
+
* settle); the CONTRACT is: terminal verdict ⇒ atomic single-shot settle, never a silent
|
|
5983
|
+
* pending-forever. PHYSICAL reclamation of a suspended workspace stays with the DEPLOYMENT that
|
|
5984
|
+
* ruled the binding dead — core holds no env pre-restore and a factory cannot reconnect to a
|
|
5985
|
+
* revoked target; the session pin follows the store contract's documented reap posture. Trust
|
|
5986
|
+
* grant is acceptable: the hook lives on RunnerDeps, the same deployment plane that owns the
|
|
5987
|
+
* checkpoint store itself. */
|
|
5988
|
+
| {
|
|
5989
|
+
ok: false;
|
|
5990
|
+
disposition: "terminal";
|
|
5991
|
+
message: string;
|
|
5992
|
+
};
|
|
5379
5993
|
/** Runtime dependencies shared across tasks. */
|
|
5380
5994
|
export interface RunnerDeps {
|
|
5381
5995
|
brain: Brain;
|
|
@@ -5550,6 +6164,48 @@ export interface RunnerDeps {
|
|
|
5550
6164
|
* never truthiness).
|
|
5551
6165
|
*/
|
|
5552
6166
|
memoryProvenance?: "off" | "carry";
|
|
6167
|
+
/**
|
|
6168
|
+
* design/383 §3.1 — the deployment's memory-capture POSTURE (the frame the per-principal
|
|
6169
|
+
* {@link RuntimeCaps.allowMemoryOptOut} verdict is read inside). Closed spellings, screened at
|
|
6170
|
+
* the config doors (`config.memory_capture_policy` — a garbage value refuses the prepare, never
|
|
6171
|
+
* folds to any posture):
|
|
6172
|
+
* - absent / `"open"` — an opt-out declaration takes effect at face value; only a resolver's
|
|
6173
|
+
* EXPLICIT `false` denies it (`memory.capture_optout_denied`, terminal). Resolver fault
|
|
6174
|
+
* (throw / non-boolean / absent-on-fault) ⇒ the opt-out STILL TAKES EFFECT, with a named
|
|
6175
|
+
* `onError({phase:"config"})` disclosure — the polarity-reversal arm: on this posture the
|
|
6176
|
+
* irreversible failure is capturing a session whose user said no, so the fail-safe faces the
|
|
6177
|
+
* other way from the caps family's deny-on-outage degrade. Under the ABSENT posture the first
|
|
6178
|
+
* opt-out actually honored additionally announces one operator line (once per process): the
|
|
6179
|
+
* switch is in use and no policy was ever configured.
|
|
6180
|
+
* - `"governed"` — the per-principal verdict is MANDATORY (the mixed-fleet compliance
|
|
6181
|
+
* deployment: some principals are retention-required). A verdict of `true` allows, `false`
|
|
6182
|
+
* denies; resolver fault / absent verdict ⇒ the task is REFUSED (fail-closed): this posture
|
|
6183
|
+
* is the deployment explicitly declaring the compliance side's irreversibility (a
|
|
6184
|
+
* retention-required user acquiring an opt-out during an outage window) the heavier one.
|
|
6185
|
+
* - `"capture-required"` — every opt-out declaration is refused (deployment-wide forced
|
|
6186
|
+
* retention, one line, resolver-independent).
|
|
6187
|
+
* The two fault polarities are BOTH deliberate and BOTH pinned; the cost table lives beside the
|
|
6188
|
+
* adjudicator. Authority vs record separation (§3.3): this posture governs whether a NEW
|
|
6189
|
+
* declaration (or a resumed run) may proceed — it never retroactively re-enables capture for a
|
|
6190
|
+
* session whose one-way opt-out record already stands (record wins for capture; under
|
|
6191
|
+
* `"governed"`/`"capture-required"` a standing record whose entitlement has been revoked refuses
|
|
6192
|
+
* to CONTINUE instead — zero capture and zero out-of-policy running, availability paid).
|
|
6193
|
+
*/
|
|
6194
|
+
memoryCapturePolicy?: "open" | "governed" | "capture-required";
|
|
6195
|
+
/**
|
|
6196
|
+
* design/383 §2.1b — an optional host-supplied carrier for the session capture opt-out record
|
|
6197
|
+
* (per memory plane, keyed by that plane's control dir). Absent = the control-plane file trio
|
|
6198
|
+
* (single-process/single-host deployments: byte-identical behavior). A deployment whose control
|
|
6199
|
+
* plane does not survive into resume replicas (the stateless multi-replica server form) supplies
|
|
6200
|
+
* a durable store here; see {@link import("./memory-engine/layout.js").SessionCaptureRecordStore}
|
|
6201
|
+
* for the per-leg fail-closed contract. Also the §2.1b capability signal: on a REMOTE-execution
|
|
6202
|
+
* deployment with no store supplied here, a `capture:"off"` declaration is refused
|
|
6203
|
+
* (`config.memory_capture_unsupported`) rather than accepted onto a carrier that the next resume
|
|
6204
|
+
* cannot see — a false privacy promise is worse than a loud one.
|
|
6205
|
+
*/
|
|
6206
|
+
memoryCaptureRecordStore?: (plane: {
|
|
6207
|
+
controlDir: string;
|
|
6208
|
+
}) => import("./memory-engine/layout.js").SessionCaptureRecordStore;
|
|
5553
6209
|
/**
|
|
5554
6210
|
* design/339 §6.1 — the v3 memory-consolidation switch, threaded verbatim to the engine seat
|
|
5555
6211
|
* ({@link import("../core/memory-engine/engine.js").MemoryEngineOptions.consolidation} — same
|
|
@@ -5585,6 +6241,23 @@ export interface RunnerDeps {
|
|
|
5585
6241
|
* author gets no channel to point the library's consolidation at a model of its choosing.
|
|
5586
6242
|
* Absent alongside absent {@link memoryConsolidation} ⇒ nothing runs and nothing changes
|
|
5587
6243
|
* (the v3 default posture).
|
|
6244
|
+
*
|
|
6245
|
+
* ⚠️ EGRESS — read this before configuring the seat (design/383 §1.5). Turning consolidation on
|
|
6246
|
+
* SENDS MEMORY CONTENT TO A MODEL PROVIDER. Specifically:
|
|
6247
|
+
* - WHAT: that cycle's ELIGIBLE CANDIDATES — not the whole library (entries already superseded,
|
|
6248
|
+
* of an ineligible type, or held out by a governance gate are not in the set) — each rendered
|
|
6249
|
+
* as its `name`, `description` and `body`. The bodies are the user's own notes, verbatim.
|
|
6250
|
+
* - WHERE: the provider this seat resolves to. When `chat` is absent that is whatever the
|
|
6251
|
+
* `consolidate → summarize` role chain lands on, which MAY BE THE SAME PROVIDER AS THE MAIN
|
|
6252
|
+
* MODEL and may equally be a different vendor — the resolution is the deployment's, and the
|
|
6253
|
+
* driver announces the model id and endpoint it actually used at the moment of the first call.
|
|
6254
|
+
* - MARKED CONTENT: entries carrying an external-origin (or repo-ingest `trust`) marker are
|
|
6255
|
+
* withheld from the CLEAN products' prompts and minted through a separate marked-only call.
|
|
6256
|
+
* "Withheld from the clean arm" is NOT "not sent" — such rows still go to the provider in
|
|
6257
|
+
* their own call, except a lone marked row, which is sent nowhere. The run receipt reports the
|
|
6258
|
+
* two counts separately (`withheldFromCleanArm` / `withheldNeverSent`) and so should any UI.
|
|
6259
|
+
* - IRREVERSIBILITY: bytes that have already crossed to a provider cannot be recalled by any
|
|
6260
|
+
* later configuration change. Disabling the seat stops FUTURE egress and nothing else.
|
|
5588
6261
|
*/
|
|
5589
6262
|
memoryConsolidationDriver?: {
|
|
5590
6263
|
chat?: {
|
|
@@ -5640,13 +6313,84 @@ export interface RunnerDeps {
|
|
|
5640
6313
|
*/
|
|
5641
6314
|
checkpointStore?: import("./checkpoint-store.js").CheckpointStore;
|
|
5642
6315
|
/**
|
|
5643
|
-
* design/
|
|
5644
|
-
*
|
|
5645
|
-
*
|
|
5646
|
-
* (
|
|
5647
|
-
*
|
|
5648
|
-
|
|
5649
|
-
|
|
6316
|
+
* design/380 O2 — deployment-supplied resume preflight, called INSIDE the pre-CAS ladder (after the
|
|
6317
|
+
* row-integrity rungs, immediately before the CAS) with a bounded deadline
|
|
6318
|
+
* ({@link resumePreflightTimeoutMs}). Refusal / throw / timeout ⇒ typed `resume.preflight_rejected`
|
|
6319
|
+
* (CheckpointError closed-set addition); the DEFAULT/absent disposition is `retry_later` (the safe
|
|
6320
|
+
* arm — fail-closed hooks that just throw can never accidentally terminalize a row) — the
|
|
6321
|
+
* entrance-screen posture of the wake-message hook (design/373 D2), generalized from "screen the
|
|
6322
|
+
* wake message" to "screen the resume". A deployment binding runs to targets (a device lane) checks
|
|
6323
|
+
* its placement/binding tables here: a transient obstacle answers `retry_later` (+`retryAfterMs`,
|
|
6324
|
+
* the #449 G1 wait-hint carrier — the row stays `pending`, the SAME token redeems later); a
|
|
6325
|
+
* PERMANENT one answers `terminal` (the row is settled by the single-shot `expire` CAS — never a
|
|
6326
|
+
* silent pending-forever; bounded waiting for a target to come back also belongs HERE, inside the
|
|
6327
|
+
* deadline, never in the env factory). Absent ⇒ no preflight, byte-identical resume behavior.
|
|
6328
|
+
*/
|
|
6329
|
+
resumePreflight?: (info: ResumePreflightInfo, signal: AbortSignal) => Promise<ResumePreflightVerdict>;
|
|
6330
|
+
/**
|
|
6331
|
+
* design/380 O2 — the {@link resumePreflight} deadline in ms. Default 10s (its own bound,
|
|
6332
|
+
* deliberately NOT the hooks-record default: a resume preflight sits on every redeem attempt of a
|
|
6333
|
+
* parked row and must answer promptly or get out of the way). Bad values (non-finite, ≤ 0,
|
|
6334
|
+
* non-number) are DISCLOSED loudly through `onError` and fall back to the default (the bad-value
|
|
6335
|
+
* loudness default: announce, never silently absorb).
|
|
6336
|
+
*/
|
|
6337
|
+
resumePreflightTimeoutMs?: number;
|
|
6338
|
+
/**
|
|
6339
|
+
* design/381 — the per-edited-file rewind history backend (CC FileHistory topology; replaces the
|
|
6340
|
+
* E19 whole-tree `fileSnapshotStore`). WIRING IS THE OPT-IN (DV-9, the reference's own SDK
|
|
6341
|
+
* posture): with a store present, every Edit/Write/NotebookEdit first-touch is backed up before
|
|
6342
|
+
* the byte changes, every turn mints a history boundary keyed by its own initial session entry
|
|
6343
|
+
* ("file state when this turn began"), and a {@link TaskSpec.resumeAt}+{@link TaskSpec.restoreFiles}
|
|
6344
|
+
* (or {@link TaskSpec.rewindFilesTo}) run converges the tracked set back to a boundary. Omitted ⇒
|
|
6345
|
+
* rewind is OFF: zero track calls, zero boundary work, zero disclosure noise; a restore request
|
|
6346
|
+
* then fails loud (`rewind.store_unconfigured`). The reference impl is
|
|
6347
|
+
* {@link InMemoryFileHistoryStore}; a deployment supplies a durable backend behind the same seam
|
|
6348
|
+
* (backends MUST reuse the exported env-half helpers — see the contract JSDoc). Works against ANY
|
|
6349
|
+
* ExecutionEnv (local or remote): bytes are read/written through the env's FileSystem face and
|
|
6350
|
+
* stored host-side, so an env rebuild does not lose history.
|
|
6351
|
+
*/
|
|
6352
|
+
fileHistoryStore?: import("./file-history-store.js").FileHistoryStore;
|
|
6353
|
+
/**
|
|
6354
|
+
* design/381 DV-14 (终审落锤) — what an Edit/Write/NotebookEdit does when its FIRST-TOUCH history
|
|
6355
|
+
* record cannot be persisted (`FileHistoryStore.trackEdit` answered `{ok:false}` = NO durable
|
|
6356
|
+
* first-touch state exists). Default **"refuse"**: the edit is refused with a typed tool error
|
|
6357
|
+
* naming the history-store failure — proceeding would leave the path indistinguishable from
|
|
6358
|
+
* never-touched, and the store's post-recovery retry would then mint already-modified bytes as
|
|
6359
|
+
* pristine v1 (the fake-pristine hole this default welds shut).
|
|
6360
|
+
*
|
|
6361
|
+
* `"proceed-unprotected"` is the availability concession arm: the edit proceeds and the failure is
|
|
6362
|
+
* disclosed via `onError` (phase "rewind"). ITS RESIDUAL, stated so the choice is informed: during
|
|
6363
|
+
* a store outage NO durable trace of the touch can be written, so "the pristine promise is void"
|
|
6364
|
+
* lives only in this process's memory — after a process restart the risk is exactly the default
|
|
6365
|
+
* arm's nightmare again (the store recovers, the next edit mints already-modified bytes as v1).
|
|
6366
|
+
* A deployment choosing this arm owns that window and is expected to record the disclosed events
|
|
6367
|
+
* in its own audit surface. Values outside the two-member set refuse prepare loudly.
|
|
6368
|
+
*/
|
|
6369
|
+
onTrackFailure?: "refuse" | "proceed-unprotected";
|
|
6370
|
+
/**
|
|
6371
|
+
* design/381 §5.3① / §10 S-1 — the ADDITIVE discriminant for the rewind refusal that this engine
|
|
6372
|
+
* cannot decide on its own. When a restore finds no boundary for the requested entry, there are
|
|
6373
|
+
* two very different worlds: the boundary NEVER existed (those turns ran with no history store, or
|
|
6374
|
+
* it was reaped), or it exists in the RETIRED whole-tree snapshot epoch, which the per-edited-file
|
|
6375
|
+
* seat deliberately does not restore. The engine holds no whole-tree state, so absent this probe
|
|
6376
|
+
* the refusal honestly names BOTH possibilities rather than picking one — the default, and the
|
|
6377
|
+
* only honest wording for a deployment that never had a whole-tree epoch at all.
|
|
6378
|
+
*
|
|
6379
|
+
* A deployment that DOES hold the retired epoch (its assembly layer can ask the old table whether
|
|
6380
|
+
* that entry has a legacy row) supplies the bit here, and the refusal splits into the two precise
|
|
6381
|
+
* messages. `true` = a whole-tree-era boundary exists for that entry; `false` = it definitively
|
|
6382
|
+
* does not; `undefined` = the deployment cannot tell, which reads exactly like an absent probe.
|
|
6383
|
+
*
|
|
6384
|
+
* Consulted ONLY on the refusal path (never on a successful restore), so a slow probe costs
|
|
6385
|
+
* nothing in the ordinary case. It is a DIAGNOSTIC input, never a control input: a probe that
|
|
6386
|
+
* throws, hangs past the caller's own patience, or answers a non-boolean cannot change the outcome
|
|
6387
|
+
* — the refusal still happens, the wording falls back to the honest both-possibilities form, and
|
|
6388
|
+
* the bad value is disclosed through `onError` (phase "rewind") rather than silently absorbed.
|
|
6389
|
+
*/
|
|
6390
|
+
legacyRewindBoundaryProbe?: (query: {
|
|
6391
|
+
sessionId: string;
|
|
6392
|
+
entryId: string;
|
|
6393
|
+
}) => boolean | undefined | Promise<boolean | undefined>;
|
|
5650
6394
|
/**
|
|
5651
6395
|
* design/99 §E6 — DURABLE per-session permission rules (a deny-narrowing folded into the task-time tool
|
|
5652
6396
|
* policy). Omitted ⇒ the feature is OFF (no rules read, zero behavior change). A deployment supplies a
|
|
@@ -5826,6 +6570,37 @@ export interface RunnerDeps {
|
|
|
5826
6570
|
consecutiveFailures: number;
|
|
5827
6571
|
lastCause: string;
|
|
5828
6572
|
}) => void;
|
|
6573
|
+
/**
|
|
6574
|
+
* #503 — OPT IN to recording this arming's serializable criteria (an
|
|
6575
|
+
* {@link import("./auto-mode-arming.js").AutoModeArmingRecipe}) on the constraint-chain entries a
|
|
6576
|
+
* durable park freezes, so a CROSS-PROCESS redemption can rebuild an equivalent classifier over its
|
|
6577
|
+
* own model leg ({@link import("./auto-mode-rebuild.js").rebuildAutoModeDecider}) instead of
|
|
6578
|
+
* answering `unavailable` forever and parking the same approval for a human on every redemption.
|
|
6579
|
+
*
|
|
6580
|
+
* DEFAULT OFF, and the default is the safety story: a row minted without it is byte-identical to
|
|
6581
|
+
* the pre-#503 shape, so a mixed-version fleet (a worker that predates this field resuming a row a
|
|
6582
|
+
* newer worker minted) keeps the digest it can compute. Turn it on fleet-wide, not per-worker.
|
|
6583
|
+
*
|
|
6584
|
+
* What gets written down: the rule overrides, the settings-deny rules, the session-context facts,
|
|
6585
|
+
* the window bounds and the two knobs — i.e. deployment-authored configuration, on a durable row.
|
|
6586
|
+
* `sessionContext` is the one field that routinely carries per-session facts (a user-identity
|
|
6587
|
+
* line); a deployment that treats those as sensitive at rest should keep them out of the arming
|
|
6588
|
+
* face rather than out of this flag.
|
|
6589
|
+
*
|
|
6590
|
+
* Recording is CONDITIONAL on the session breaker still being closed at park time: a session that
|
|
6591
|
+
* already fell back to non-auto hands nothing forward (the redemption must never run wider than the
|
|
6592
|
+
* ancestor it inherits). A `true` here with a non-serializable face announces once and records
|
|
6593
|
+
* nothing (`phase:"config"`).
|
|
6594
|
+
*/
|
|
6595
|
+
persistArming?: boolean;
|
|
6596
|
+
/**
|
|
6597
|
+
* #503 — the deployment's auto-mode SETTINGS GENERATION (a config revision, a settings hash, a
|
|
6598
|
+
* publish id — the string is opaque to the engine). Recorded in the arming recipe and compared at
|
|
6599
|
+
* rebuild time against the REDEEMING deployment's own: it is a declaration axis that can only make
|
|
6600
|
+
* the rebuild stricter (see {@link import("./auto-mode-arming.js").foldAutoModeArming}). Absent =
|
|
6601
|
+
* undeclared, which the fold treats as "cannot assume unchanged".
|
|
6602
|
+
*/
|
|
6603
|
+
settingsEpoch?: string;
|
|
5829
6604
|
};
|
|
5830
6605
|
/**
|
|
5831
6606
|
* design/129-B — PROCESS-level observer for background delegation children (Agent/Fork
|
|
@@ -6257,8 +7032,11 @@ export interface RunnerDeps {
|
|
|
6257
7032
|
* unrepaired transcript.
|
|
6258
7033
|
* - `"suggestions"` — the best-effort follow-up-suggestions pass failed or timed out; the result
|
|
6259
7034
|
* simply carries no suggestions.
|
|
6260
|
-
* - `"rewind"` — the
|
|
6261
|
-
*
|
|
7035
|
+
* - `"rewind"` — the file-history machinery backing rewind reported a best-effort failure: a
|
|
7036
|
+
* turn-start boundary capture failed or timed out (that turn cannot be rewound to; the turn
|
|
7037
|
+
* itself proceeds), or a first-touch failure was tolerated under
|
|
7038
|
+
* `onTrackFailure:"proceed-unprotected"`. (design/381: the whole-tree size gate and its
|
|
7039
|
+
* `classification:"too_large"` payload are retired — per-edited-file history has no size axis.)
|
|
6262
7040
|
* - `"hook"` — a deployment hook misbehaved: a callback threw, or returned a verdict it was not
|
|
6263
7041
|
* allowed to (e.g. a declared-observational hook). The engine applies the hook's own documented
|
|
6264
7042
|
* fallback (swallow, or fail-closed deny, per its contract) and reports the fact here.
|