@sema-agent/core 7.16.0 → 7.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +209 -0
- package/dist/agents/builtin-agents.d.ts +3 -3
- package/dist/agents/cross-session-envelope.d.ts +0 -1
- package/dist/agents/cross-session-envelope.js +0 -1
- package/dist/agents/peer-notices.d.ts +0 -9
- package/dist/agents/peer-notices.js +0 -63
- package/dist/agents/subagent.js +7 -15
- package/dist/brain/request-params.d.ts +2 -2
- package/dist/config/defaults.d.ts +0 -7
- package/dist/config/defaults.js +0 -2
- package/dist/core/artifact-host.d.ts +218 -0
- package/dist/core/artifact-host.js +92 -0
- package/dist/core/ask-origin.d.ts +29 -3
- package/dist/core/ask-origin.js +17 -9
- package/dist/core/ask-question.d.ts +25 -0
- package/dist/core/ask-question.js +4 -0
- package/dist/core/ask-unresolvable-notice.d.ts +30 -21
- package/dist/core/ask-unresolvable-notice.js +16 -8
- package/dist/core/auto-compaction.d.ts +10 -10
- package/dist/core/auto-mode-arming.d.ts +2 -2
- package/dist/core/background-agent-store.d.ts +5 -5
- package/dist/core/background-shell.d.ts +1 -1
- package/dist/core/checkpoint-execution-record.d.ts +0 -3
- package/dist/core/checkpoint-execution-record.js +0 -4
- package/dist/core/checkpoint-store.d.ts +71 -99
- package/dist/core/checkpoint-store.js +9 -16
- package/dist/core/context-edit.d.ts +36 -104
- package/dist/core/context-edit.js +37 -60
- package/dist/core/effective-path-target.d.ts +68 -8
- package/dist/core/effective-path-target.js +42 -30
- package/dist/core/engine-notice.d.ts +46 -32
- package/dist/core/env-budget.d.ts +31 -0
- package/dist/core/env-budget.js +39 -0
- package/dist/core/exec-output-tail.d.ts +5 -5
- package/dist/core/file-history-store.js +3 -6
- package/dist/core/fs-write-gate-policy.d.ts +1 -1
- package/dist/core/fs-write-gate-policy.js +3 -3
- package/dist/core/gate-lanes.js +9 -15
- package/dist/core/governance-codes.d.ts +1 -1
- package/dist/core/governance-codes.js +2 -0
- package/dist/core/hands-band.d.ts +1 -1
- package/dist/core/hooks.d.ts +18 -17
- package/dist/core/lsp-session.d.ts +1 -1
- package/dist/core/lsp.d.ts +1 -1
- package/dist/core/lsp.js +5 -4
- package/dist/core/mcp.d.ts +5 -5
- package/dist/core/mcp.js +4 -48
- package/dist/core/memory-engine/engine.js +2 -1
- package/dist/core/memory-engine/layout.d.ts +18 -6
- package/dist/core/memory-engine/layout.js +40 -21
- package/dist/core/memory-recall.d.ts +0 -8
- package/dist/core/memory.d.ts +1 -1
- package/dist/core/oracle-isolation.js +2 -2
- package/dist/core/parent-spec-seat.d.ts +66 -0
- package/dist/core/parent-spec-seat.js +47 -0
- package/dist/core/permission-rule-model.d.ts +11 -6
- package/dist/core/permission-rule-model.js +53 -37
- package/dist/core/permission-rules.js +8 -3
- package/dist/core/physical-path.d.ts +37 -0
- package/dist/core/physical-path.js +30 -0
- package/dist/core/protocol-naming.d.ts +2 -2
- package/dist/core/remote-env.d.ts +1 -1
- package/dist/core/retired-keys.d.ts +19 -0
- package/dist/core/retired-keys.js +15 -0
- package/dist/core/runner/active-skill-scope.js +3 -3
- package/dist/core/runner/assemble-result.d.ts +15 -13
- package/dist/core/runner/assemble-result.js +5 -4
- package/dist/core/runner/compaction-call-options.d.ts +25 -89
- package/dist/core/runner/contracts.d.ts +11 -10
- package/dist/core/runner/gate-exit.js +15 -1
- package/dist/core/runner/halt-attribution.d.ts +35 -0
- package/dist/core/runner/halt-attribution.js +16 -0
- package/dist/core/runner/memory-consolidation.d.ts +0 -2
- package/dist/core/runner/memory-consolidation.js +0 -1
- package/dist/core/runner/prepare-artifact.d.ts +52 -0
- package/dist/core/runner/prepare-artifact.js +63 -0
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +4 -2
- package/dist/core/runner/prepare-caps-and-workflow.js +2 -3
- package/dist/core/runner/prepare-config-doors.d.ts +3 -3
- package/dist/core/runner/prepare-config-doors.js +5 -2
- package/dist/core/runner/prepare-context-lane.d.ts +2 -2
- package/dist/core/runner/prepare-context-lane.js +2 -13
- package/dist/core/runner/prepare-file-history.d.ts +1 -1
- package/dist/core/runner/prepare-file-history.js +3 -52
- package/dist/core/runner/prepare-hands-readface.d.ts +4 -7
- package/dist/core/runner/prepare-hands-readface.js +3 -2
- package/dist/core/runner/prepare-inherited-gate.d.ts +5 -2
- package/dist/core/runner/prepare-inherited-gate.js +7 -8
- package/dist/core/runner/prepare-memory.d.ts +4 -6
- package/dist/core/runner/prepare-memory.js +4 -2
- package/dist/core/runner/prepare-policy-chain.d.ts +3 -3
- package/dist/core/runner/prepare-policy-chain.js +15 -30
- package/dist/core/runner/prepare-prompt-assembly.js +1 -1
- package/dist/core/runner/prepare-question-face.js +2 -1
- package/dist/core/runner/prepare-safety-scan.d.ts +1 -1
- package/dist/core/runner/prepare-suspend-saga.js +0 -2
- package/dist/core/runner/prepare-task.js +12 -11
- package/dist/core/runner/prepare-wiring-manifest.js +9 -7
- package/dist/core/runner/resume-admission.js +2 -1
- package/dist/core/runner/resume-internals-and-config.js +4 -0
- package/dist/core/runner/retired-runner-deps.d.ts +3 -0
- package/dist/core/runner/retired-runner-deps.js +7 -0
- package/dist/core/runner/run-harness-handlers.js +5 -3
- package/dist/core/runner/run-settle-and-teardown.d.ts +1 -1
- package/dist/core/runner/run-settle-and-teardown.js +3 -1
- package/dist/core/runner/session-file-state-replay.js +3 -3
- package/dist/core/runner/session-rule-policy.js +3 -4
- package/dist/core/runner/stream-halt-verbs.js +1 -1
- package/dist/core/runner/stream-steer-verb.js +1 -4
- package/dist/core/runner/workspace-path.js +3 -0
- package/dist/core/runner-deps.d.ts +30 -49
- package/dist/core/safe-notify.d.ts +1 -1
- package/dist/core/sensitive-path-policy.d.ts +1 -1
- package/dist/core/sensitive-path-policy.js +11 -12
- package/dist/core/session.d.ts +2 -2
- package/dist/core/skill-tool-specifier.d.ts +1 -1
- package/dist/core/skills-directory.d.ts +1 -1
- package/dist/core/skills-directory.js +4 -3
- package/dist/core/spec-contract.js +5 -4
- package/dist/core/store-contracts/checkpoint-store-contract.js +1 -1
- package/dist/core/strategy-store.d.ts +8 -11
- package/dist/core/task-event.d.ts +20 -17
- package/dist/core/task-limits.d.ts +1 -1
- package/dist/core/task-notification.d.ts +5 -5
- package/dist/core/task-registry-agent.d.ts +5 -5
- package/dist/core/task-registry-monitor.d.ts +2 -2
- package/dist/core/task-registry-shared.d.ts +32 -28
- package/dist/core/task-registry-shared.js +4 -13
- package/dist/core/task-registry.d.ts +8 -8
- package/dist/core/task-result.d.ts +37 -37
- package/dist/core/task-spec.d.ts +5 -5
- package/dist/core/task-stream.d.ts +16 -11
- package/dist/core/tool-catalog-entries.d.ts +1 -0
- package/dist/core/tool-catalog-entries.js +6 -1
- package/dist/core/tool-conformance.d.ts +0 -2
- package/dist/core/tool-conformance.js +1 -3
- package/dist/core/tool-errors.d.ts +5 -0
- package/dist/core/tool-errors.js +1 -0
- package/dist/core/tool-policy.d.ts +66 -22
- package/dist/core/tool-policy.js +31 -5
- package/dist/core/tool-result-budget.d.ts +1 -1
- package/dist/core/tool-result-store.d.ts +11 -11
- package/dist/core/tool-spec.d.ts +34 -38
- package/dist/core/tools.d.ts +2 -2
- package/dist/core/trace.d.ts +12 -15
- package/dist/core/untrusted-text.d.ts +1 -1
- package/dist/core/wiring-manifest.d.ts +4 -3
- package/dist/core/workflow-journal-store.d.ts +11 -11
- package/dist/engine/execution-env/node-execution-env.d.ts +3 -2
- package/dist/engine/execution-env/node-execution-env.js +8 -5
- package/dist/engine/harness/agent-harness.d.ts +47 -4
- package/dist/engine/harness/agent-harness.js +52 -9
- package/dist/engine/loop/agent-loop.js +15 -8
- package/dist/engine/loop/types.d.ts +5 -0
- package/dist/index.d.ts +20 -14
- package/dist/index.js +16 -10
- package/dist/internal/harness.d.ts +1 -0
- package/dist/orchestration/run-spec.d.ts +3 -0
- package/dist/orchestration/run-spec.js +3 -3
- package/dist/orchestration/run-workflow-tool.d.ts +12 -6
- package/dist/orchestration/run-workflow-tool.js +23 -5
- package/dist/orchestration/workflow-primitives.d.ts +4 -2
- package/dist/orchestration/workflow-primitives.js +1 -4
- package/dist/orchestration/workflow-script-store.js +9 -25
- package/dist/orchestration/workflow-types.d.ts +34 -4
- package/dist/orchestration/workflow-types.js +2 -0
- package/dist/orchestration/workflow.d.ts +39 -7
- package/dist/orchestration/workflow.js +237 -113
- package/dist/prompt-assembly/assemble.d.ts +2 -2
- package/dist/prompt-assembly/assemble.js +9 -9
- package/dist/prompt-assembly/composer.d.ts +6 -7
- package/dist/prompt-assembly/composer.js +10 -10
- package/dist/prompt-assembly/epoch.js +1 -1
- package/dist/prompt-assembly/event-registry.d.ts +0 -21
- package/dist/prompt-assembly/packs/sema-default.js +30 -30
- package/dist/prompt-assembly/types.d.ts +14 -14
- package/dist/prompts/coordinator.d.ts +0 -13
- package/dist/prompts/coordinator.js +0 -3
- package/dist/stores/cc/task-list-store.js +2 -10
- package/dist/stores/file/checkpoint-store.js +1 -5
- package/dist/stores/file/file-history-store.js +2 -2
- package/dist/stores/file/fs-atomic.d.ts +15 -18
- package/dist/stores/file/fs-atomic.js +4 -14
- package/dist/stores/file/mailbox-store.d.ts +7 -11
- package/dist/stores/file/mailbox-store.js +4 -11
- package/dist/stores/file/permission-rule-store.d.ts +4 -35
- package/dist/stores/file/permission-rule-store.js +4 -148
- package/dist/tools/artifact/artifact-text.d.ts +62 -0
- package/dist/tools/artifact/artifact-text.js +68 -0
- package/dist/tools/artifact/artifact-tool.d.ts +52 -0
- package/dist/tools/artifact/artifact-tool.js +351 -0
- package/dist/tools/artifact/local-stub.d.ts +26 -0
- package/dist/tools/artifact/local-stub.js +297 -0
- package/dist/tools/fs/bash-readonly-classifier.d.ts +90 -8
- package/dist/tools/fs/bash-readonly-classifier.js +294 -70
- package/dist/tools/fs/fs-bash.d.ts +14 -10
- package/dist/tools/fs/fs-bash.js +50 -33
- package/dist/tools/fs/fs-search-tools.js +5 -2
- package/dist/tools/fs/fs-shared.d.ts +2 -0
- package/dist/tools/fs/fs-shared.js +3 -11
- package/dist/tools/fs/index.d.ts +4 -0
- package/dist/tools/fs/index.js +4 -1
- package/dist/tools/fs/safety.d.ts +117 -1
- package/dist/tools/fs/safety.js +131 -31
- package/dist/tools/fs/search.js +8 -6
- package/package.json +2 -1
- package/test/export-surface.snapshot.json +119 -39
|
@@ -6,7 +6,6 @@ import { type QuestionAnswer } from "./ask-question.js";
|
|
|
6
6
|
import type { ReadEntry } from "../tools/fs/safety.js";
|
|
7
7
|
import type { RepairBundle } from "../agents/repair-loop.js";
|
|
8
8
|
import type { WorkspaceHandle } from "./remote-env.js";
|
|
9
|
-
import type { ConsolidationNote } from "./runner/memory-consolidation.js";
|
|
10
9
|
import type { SessionPermissionRules } from "./session-policy-store.js";
|
|
11
10
|
import type { NestedUsageAccum, ShellGateDoctrine, TaskResult } from "./types.js";
|
|
12
11
|
import type { ActiveWorktreeSession } from "../internal/harness-types.js";
|
|
@@ -95,17 +94,18 @@ export type ResourceLimitReason = "budget_tokens" | "budget_cost" | "walltime" |
|
|
|
95
94
|
*/
|
|
96
95
|
export type PlatformLimitReason = Extract<ResourceLimitReason, "env_lifetime" | "usage_window">;
|
|
97
96
|
/**
|
|
98
|
-
* design/80 D-2: which SAFETY
|
|
99
|
-
* `irreversible_ask`. Derived from the tool's
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
* THE DURABLE RECORD (never re-derives risk from a model-controlled value — the model self-reports nothing
|
|
103
|
-
* here). Both can be true (a tool marked BOTH egress AND irreversible).
|
|
97
|
+
* design/80 D-2: which SAFETY axis of THIS call caused an `ask` to mint an {@link CheckpointGate}
|
|
98
|
+
* `irreversible_ask`. Derived by the gate (`safetyAxisOf`) from the tool's static marks plus the engine's own
|
|
99
|
+
* per-call facts — never from a decision member a policy composes (forgeable). Persisted on the gate so a
|
|
100
|
+
* network budget/escalation resolver reads WHY this is a safety ask FROM THE DURABLE RECORD. Both can be true.
|
|
104
101
|
*/
|
|
105
102
|
export interface SafetyAxis {
|
|
106
103
|
/** design/70: the tool is egress-marked (`ToolSpec.egress` — an external write: push, open PR, send). */
|
|
107
104
|
egress?: boolean;
|
|
108
|
-
/** design/77 §4: the tool's
|
|
105
|
+
/** design/77 §4: this call carries an irreversibility MANDATE — the tool's own `always`/`maybe` tier, the
|
|
106
|
+
* operator's `shellGate:"always"`, the probe's structural verdict (mandated / unanswered), or the shell
|
|
107
|
+
* gate's own tighten under its `"maybe"` tier; a policy ask the doctrine's probe neither tightened nor
|
|
108
|
+
* mandated carries no bit (its park is the budgetable kind). */
|
|
109
109
|
irreversible?: boolean;
|
|
110
110
|
}
|
|
111
111
|
/**
|
|
@@ -157,7 +157,7 @@ export interface RiskDescriptor {
|
|
|
157
157
|
* {@link summary} (a path name can carry the same reordering hazard as a command) + capped.
|
|
158
158
|
* Omitted when none derivable. */
|
|
159
159
|
touchedPaths?: string[];
|
|
160
|
-
/**
|
|
160
|
+
/** A persisted allow rule MATCHED this call but could not clear the mandated ask —
|
|
161
161
|
* the matched rule text (rendered through the same display baseline as {@link summary}, capped),
|
|
162
162
|
* threaded to the mint so the durable-park route carries the same disclosure the synchronous ask
|
|
163
163
|
* does. The inbox renders "their rule is alive, just outranked" instead of the person concluding
|
|
@@ -173,7 +173,7 @@ export interface RiskDescriptor {
|
|
|
173
173
|
* same-value contract (unlike `probeReason`/`probeCause` below), which is why the one-face move
|
|
174
174
|
* is legal — a byte-pinning consumer must read per face.) */
|
|
175
175
|
shadowedRule?: string;
|
|
176
|
-
/**
|
|
176
|
+
/** WHY the reversibility probe did not clear this call, in the probe's own
|
|
177
177
|
* words — the cause the tighten acted on, which until now existed only inside the probe and left the
|
|
178
178
|
* card showing WHAT is gated with no account of WHY. Present only for a `"maybe"`-tier tighten whose
|
|
179
179
|
* probe supplied a cause (an `"always"`-tier tighten runs no probe; a timeout/throw yields no verdict).
|
|
@@ -190,7 +190,7 @@ export interface RiskDescriptor {
|
|
|
190
190
|
* one-face re-render would break it. A consumer rendering this seat owes its own screen (or the
|
|
191
191
|
* descriptor family's display baseline). */
|
|
192
192
|
probeReason?: string;
|
|
193
|
-
/**
|
|
193
|
+
/** The STRUCTURED cause, for a probe whose verdict this engine understands —
|
|
194
194
|
* the built-in shell probe is the first. Carries a machine-readable {@link ProbeCause.code} and the
|
|
195
195
|
* operand families as ARRAYS with honest totals, so a consumer renders its own sentence in its own
|
|
196
196
|
* language instead of receiving one this engine had to write in advance.
|
|
@@ -202,7 +202,7 @@ export interface RiskDescriptor {
|
|
|
202
202
|
* whichever part sorts last. A code cannot be false and an array cannot be truncated into a lie. */
|
|
203
203
|
probeCause?: ProbeCause;
|
|
204
204
|
}
|
|
205
|
-
/**
|
|
205
|
+
/** One operand family inside a {@link ProbeCause} — the entries a card may NAME, plus how
|
|
206
206
|
* many the family really has. `shown.length` ≤ {@link total}, and a consumer that renders only `shown`
|
|
207
207
|
* is still obliged to tell the person when `total` exceeds it: the count is the part that cannot be
|
|
208
208
|
* cut, and "there is a second unreviewed operand" is exactly what a reader cannot recover from the
|
|
@@ -221,7 +221,7 @@ export interface ProbeCauseOperands {
|
|
|
221
221
|
readonly total: number;
|
|
222
222
|
}
|
|
223
223
|
/**
|
|
224
|
-
*
|
|
224
|
+
* An ENGINE-MINTED, structured account of why a reversibility probe did not clear a call.
|
|
225
225
|
*
|
|
226
226
|
* DISPLAY/TRIAGE metadata, exactly like the rest of {@link RiskDescriptor} — nothing reads it to gate,
|
|
227
227
|
* budget or suppress anything, and it deliberately never reaches the ask's `message` (that string is
|
|
@@ -248,14 +248,14 @@ export interface ProbeCause {
|
|
|
248
248
|
* account of an approval's reach is worse than none. */
|
|
249
249
|
readonly further?: ProbeCauseOperands;
|
|
250
250
|
}
|
|
251
|
-
/**
|
|
251
|
+
/** The bloat ceiling on {@link RiskDescriptor.probeReason} — one sentence of cause, sized
|
|
252
252
|
* like its {@link RiskDescriptor.shadowedRule} sibling. The neutralizer is the injection ceiling; this
|
|
253
253
|
* is only the length one. Exported (module-level, not part of the package's public surface) so the gate
|
|
254
254
|
* that sanitizes at the entry boundary and the descriptor builder that re-sanitizes at the persist
|
|
255
255
|
* boundary cannot cap the same value at two different lengths. */
|
|
256
256
|
export declare const PROBE_REASON_MAX = 200;
|
|
257
257
|
/**
|
|
258
|
-
*
|
|
258
|
+
* The ONE validator for a {@link ProbeCause} arriving from a probe.
|
|
259
259
|
*
|
|
260
260
|
* The value crosses a trust boundary twice — once at the gate (where a deployment-supplied probe hands
|
|
261
261
|
* it over) and once at the persist boundary (where {@link buildRiskDescriptor} is an exported function a
|
|
@@ -410,19 +410,19 @@ export declare function buildRiskDescriptor(input: {
|
|
|
410
410
|
shellGated?: boolean;
|
|
411
411
|
/** The resolved doctrine to persist when `shellGated` (see {@link RiskDescriptor.shellGateDoctrine}). */
|
|
412
412
|
shellGateDoctrine?: ShellGateDoctrine;
|
|
413
|
-
/**
|
|
413
|
+
/** The matched-but-outranked persisted rule to persist (see {@link RiskDescriptor.shadowedRule}). */
|
|
414
414
|
shadowedRule?: string;
|
|
415
|
-
/**
|
|
415
|
+
/** The reversibility probe's cause for not clearing this call (see
|
|
416
416
|
* {@link RiskDescriptor.probeReason}). Re-neutralized here like its shadowed-rule sibling: this
|
|
417
417
|
* function is exported, so a caller reaching it directly must get the same redaction the gate applies. */
|
|
418
418
|
probeReason?: string;
|
|
419
|
-
/**
|
|
419
|
+
/** The STRUCTURED cause (see {@link RiskDescriptor.probeCause}). Re-validated here for
|
|
420
420
|
* the same reason the prose sibling is re-neutralized — this function is exported, so a caller
|
|
421
421
|
* reaching it directly must not be able to persist a shape the gate would have rejected. */
|
|
422
422
|
probeCause?: unknown;
|
|
423
423
|
}): RiskDescriptor;
|
|
424
424
|
/**
|
|
425
|
-
*
|
|
425
|
+
* The durable record of an ask's `requiresRealApproval` bit, which used
|
|
426
426
|
* to DIE at the park: the mint keyed the gate kind on the static tool marks only, so an org-governed
|
|
427
427
|
* ask on an unmarked tool minted a plain `{kind:"human"}` — the one kind a network budget resolver may
|
|
428
428
|
* auto-approve — and the org's "only judgment clears this" demand was silently budgetable after the
|
|
@@ -567,7 +567,7 @@ export type ResumeOutcome = {
|
|
|
567
567
|
* that the human actually saw/approved (`PendingAction.tool_approval.boundInputHash`, computed by the
|
|
568
568
|
* engine at suspend-mint via {@link import("./canonical-json.js").boundInputHashOf} over the post-hook
|
|
569
569
|
* `args`). The caller echoes it VERBATIM — it is opaque; the SDK/service NEVER re-canonicalize args
|
|
570
|
-
* (
|
|
570
|
+
* (a second runtime's serialization could diverge → false mismatch → fail-closed a
|
|
571
571
|
* legitimate approval). The resume verifies it by **string equality** against the checkpoint's
|
|
572
572
|
* persisted value (`checkpoint.invalid_outcome`, fail-closed, pre-CAS) — closing the TOCTOU "same call
|
|
573
573
|
* id, different input" variant that `boundCallId` alone misses (a re-mint that swapped the input under
|
|
@@ -687,11 +687,11 @@ export type ResumeOutcome = {
|
|
|
687
687
|
*
|
|
688
688
|
* `message` (optional): operator content delivered to the resumed run — the ATOMIC equivalent of
|
|
689
689
|
* `setPendingSteer` + resume in one verb (same `validatePendingSteer` persist-time guards, same
|
|
690
|
-
* trusted/untrusted delivery framing as {@link CheckpointState.
|
|
691
|
-
* continuation's steer tail as its OWN segment, appended AFTER any already-parked
|
|
690
|
+
* trusted/untrusted delivery framing as {@link CheckpointState.pendingSteerQueue}). It rides the resume
|
|
691
|
+
* continuation's steer tail as its OWN segment, appended AFTER any already-parked queue entry
|
|
692
692
|
* (X5: both are delivered — the wake message never displaces a parked, undelivered supervisor steer;
|
|
693
693
|
* each segment is framed under its own `trusted` semantics, never one merged trust domain). WITHOUT a
|
|
694
|
-
* `message`, the checkpoint must already carry a parked
|
|
694
|
+
* `message`, the checkpoint must already carry a parked steer — a wake with nothing to
|
|
695
695
|
* deliver is rejected (`wake.nothing_to_deliver`, anti spin-wake). Idempotency rides the existing
|
|
696
696
|
* resolve CAS: a second wake of the same token loses the CAS (`checkpoint.already_resolved`). */
|
|
697
697
|
| {
|
|
@@ -798,7 +798,7 @@ export type PendingAction = {
|
|
|
798
798
|
* this"). Absent otherwise (never `false`). Display metadata: the resume belts keep reading the
|
|
799
799
|
* gate's own bit. */
|
|
800
800
|
requiresRealApproval?: true;
|
|
801
|
-
/**
|
|
801
|
+
/** The PARK twin of `AskRequest.denialLimitFallback`: present
|
|
802
802
|
* ⇔ this parked ask is the classifier DENIAL-LIMIT fallback, carrying the counts that tripped
|
|
803
803
|
* the bound. Its `autoDenyAfterMs` is ALWAYS `0` on a row: the window is a fact of the ask's
|
|
804
804
|
* route, armed only at a hand-out to a live approver, and nothing counts down on the parked lane
|
|
@@ -935,8 +935,6 @@ export interface CheckpointState {
|
|
|
935
935
|
};
|
|
936
936
|
/** design/38: nested sub-agent cumulative cost — else pre-suspend child cost evaporates (§4.bis/Q7). */
|
|
937
937
|
nestedStats: NestedUsageAccum;
|
|
938
|
-
/** design/41: consolidation notes collected pre-suspend — else the task-end pass loses them. */
|
|
939
|
-
consolidationNotes?: ConsolidationNote[];
|
|
940
938
|
/** design/44: the hand's read-file state (content hashes), serialized from the hands-toolkit closure
|
|
941
939
|
* (NOT part of Prepared — §15.2 net-add #8). Without it a resumed `edit_file` is rejected "not read". */
|
|
942
940
|
readFileState?: Array<[string, ReadEntry]>;
|
|
@@ -1002,56 +1000,31 @@ export interface CheckpointState {
|
|
|
1002
1000
|
*/
|
|
1003
1001
|
workspaceHandle?: WorkspaceHandle;
|
|
1004
1002
|
/**
|
|
1005
|
-
* design/
|
|
1006
|
-
*
|
|
1007
|
-
*
|
|
1008
|
-
*
|
|
1009
|
-
* never
|
|
1003
|
+
* design/171 §5.3 — the BOUNDED, ORDERED queue of parked operator steers for a DURABLY-SUSPENDED task,
|
|
1004
|
+
* drained in `seq` order on resume with every entry framed under its own trust semantics. Live
|
|
1005
|
+
* `TaskStream.steer` (runtask.ts) is unreachable while the harness is idle, so a supervisor's guidance is
|
|
1006
|
+
* parked HERE via {@link CheckpointStore.setPendingSteer} and injected on resume (after the
|
|
1007
|
+
* resume-continuation prompt). It is GUIDANCE ONLY — never an approval channel (§3 inv #4) and never
|
|
1008
|
+
* parsed into control state (§3 inv #5); a budget/autonomy/gate-threshold is CONFIG, not steer text.
|
|
1010
1009
|
*
|
|
1011
1010
|
* `trusted` is FROZEN at `setPendingSteer` from the SERVICE's verified-principal check (an operator-role
|
|
1012
|
-
* check, NOT a client header
|
|
1013
|
-
*
|
|
1014
|
-
*
|
|
1015
|
-
*
|
|
1016
|
-
* is REJECTED at `setPendingSteer` (typed `steering.invalid_content`) so a dirty steer never enters this
|
|
1017
|
-
* state (§3 inv #2); the untrusted-injection path ALSO sanitizes the text as untrusted data, belt-and-braces.
|
|
1018
|
-
* Absent when no steer is pending. All-string fields → JSON/`structuredClone` round-trips safely.
|
|
1011
|
+
* check, NOT a client header) and NEVER recomputed on resume (§3 inv #1). A `trusted:false` entry reaches
|
|
1012
|
+
* the model as a PLAIN user message with NO `<system-reminder>` wrapper (§3 inv #3); a `trusted:true`
|
|
1013
|
+
* entry MAY ride the reminder. `text` containing `</system-reminder>` is REJECTED at `setPendingSteer`
|
|
1014
|
+
* (`steering.invalid_content`) so a dirty steer never enters this state (§3 inv #2).
|
|
1019
1015
|
*
|
|
1020
|
-
*
|
|
1021
|
-
*
|
|
1022
|
-
*
|
|
1023
|
-
*
|
|
1024
|
-
* leg; the model re-does the work, re-seeing the steer). It is NOT carried onto a NEW re-suspend checkpoint
|
|
1025
|
-
* (serializeCheckpointState stamps `undefined`) — a supervisor steers the new checkpoint afresh.
|
|
1026
|
-
* - It is DROPPED (never delivered) on a resume that runs no turn (an exhausted-budget resume) or that is
|
|
1027
|
-
* set in the get→resolve race window of an in-flight resume — both rare; the run is ending or the steer
|
|
1028
|
-
* just missed its train. A supervisor re-issues `setPendingSteer` if a steer didn't land.
|
|
1029
|
-
* A precise exactly-once delivery would need clearing the steer from the persisted row on consume (a reopen
|
|
1030
|
-
* clear + a get→resolve interlock); deferred as not worth the cross-backend complexity for guidance text.
|
|
1031
|
-
*
|
|
1032
|
-
* **Superseded by {@link CheckpointState.pendingSteerQueue} (design/171 §5.3).** New writes go to the
|
|
1033
|
-
* queue; this seat remains ONLY so rows written before the queue existed keep delivering their parked
|
|
1034
|
-
* steer. Never read it directly — {@link readPendingSteerQueue} is the single read point that folds
|
|
1035
|
-
* this legacy seat in as queue member 0.
|
|
1036
|
-
*/
|
|
1037
|
-
pendingSteer?: {
|
|
1038
|
-
text: string;
|
|
1039
|
-
trusted: boolean;
|
|
1040
|
-
};
|
|
1041
|
-
/**
|
|
1042
|
-
* design/171 §5.3 — the BOUNDED, ORDERED queue of parked operator steers, drained in `seq` order on
|
|
1043
|
-
* resume with every entry framed under its own trust semantics.
|
|
1044
|
-
*
|
|
1045
|
-
* It replaces the single {@link CheckpointState.pendingSteer} seat, whose last-writer-wins overwrite
|
|
1046
|
-
* meant two people steering one suspended run silently destroyed the first instruction — and destroyed
|
|
1047
|
-
* it with no attribution, so nobody could tell afterwards that anything was lost.
|
|
1016
|
+
* Delivery is BEST-EFFORT, at-most-meaningfully-once: an entry rides EVERY resume of THIS checkpoint
|
|
1017
|
+
* that runs a turn (a faithful reopen→re-resume re-shows it), is NOT carried onto a re-suspend
|
|
1018
|
+
* checkpoint (serializeCheckpointState stamps `undefined`), and is DROPPED on a resume that runs no
|
|
1019
|
+
* turn. A supervisor re-issues `setPendingSteer` if a steer did not land.
|
|
1048
1020
|
*
|
|
1049
1021
|
* Bounds are fail-loud, never lossy: {@link MAX_PENDING_STEER_ENTRIES} entries and
|
|
1050
1022
|
* {@link PENDING_STEER_QUEUE_BYTE_BUDGET_BYTES} serialized bytes, whichever binds first, reject the
|
|
1051
1023
|
* APPEND with `steering.queue_full` rather than evicting an already-accepted instruction.
|
|
1052
1024
|
*
|
|
1053
|
-
* Absent when nothing is parked.
|
|
1054
|
-
*
|
|
1025
|
+
* Absent when nothing is parked. {@link readPendingSteerQueue} is the single read point; a row that
|
|
1026
|
+
* still carries the retired pre-queue single seat (`pendingSteer`) is refused there
|
|
1027
|
+
* (`checkpoint.unsupported_version`, `seat_retired`), never folded in.
|
|
1055
1028
|
*/
|
|
1056
1029
|
pendingSteerQueue?: PendingSteerEntry[];
|
|
1057
1030
|
/**
|
|
@@ -1258,7 +1231,7 @@ export interface CheckpointState {
|
|
|
1258
1231
|
* EXPLICIT live `internals.placementRoot` wins when re-supplied; otherwise THIS key restores into
|
|
1259
1232
|
* `internals.placementRoot` at prepare so descendants and the NEXT re-suspend mint inherit it too
|
|
1260
1233
|
* — outranking a supplied `rootSessionId`, which is DELEGATION IDENTITY (the peer/registry axis a
|
|
1261
|
-
* revive leg re-supplies from its row), never a placement claim (
|
|
1234
|
+
* revive leg re-supplies from its row), never a placement claim (reading it as one
|
|
1262
1235
|
* permanently stuck every parked agent spawned under an explicit placement root). The resume entry
|
|
1263
1236
|
* ALSO reads this key as an identity record: a resume whose supplied `internals.placementRoot`
|
|
1264
1237
|
* CONTRADICTS it is refused pre-CAS (`resume.placement_mismatch`); either side absent skips the
|
|
@@ -1474,7 +1447,7 @@ export declare function remainingTokens(ledger: ResourceLedger | undefined): num
|
|
|
1474
1447
|
* later D-1 slice (1a.2) and is deliberately NOT part of this winner.
|
|
1475
1448
|
*/
|
|
1476
1449
|
export interface ResolvedOutcome {
|
|
1477
|
-
/** The pending tool call this decision was bound to (design/80 D-1 slice 1a).
|
|
1450
|
+
/** The pending tool call this decision was bound to (design/80 D-1 slice 1a). A REVIEW gate binds
|
|
1478
1451
|
* no tool call, so it records the synthetic `gate:<kind>` marker instead — the replay guard compares the
|
|
1479
1452
|
* DECISION for those, never a call id. */
|
|
1480
1453
|
boundCallId: string;
|
|
@@ -1878,7 +1851,7 @@ export interface CheckpointSummary {
|
|
|
1878
1851
|
*/
|
|
1879
1852
|
toolInput?: string;
|
|
1880
1853
|
/**
|
|
1881
|
-
*
|
|
1854
|
+
* HOW this pause's remote workspace comes back, projected from
|
|
1882
1855
|
* `state.workspaceHandle.restoreMode` so a supervisor inbox can tell the two apart in ONE `listByScope`
|
|
1883
1856
|
* call. `"snapshot"` = the VM was paused into a snapshot (billing typically stops, memory state
|
|
1884
1857
|
* captured); `"park_only"` = a non-suspendable target (SSH host / ADB device) was never paused — the
|
|
@@ -1906,12 +1879,12 @@ export interface CheckpointSummary {
|
|
|
1906
1879
|
* projection's own materials (a pre-#457 row therefore reads absent, which is honest: unreported).
|
|
1907
1880
|
* ECHO-ONLY display metadata; nothing gates on it. */
|
|
1908
1881
|
previewWithheld?: "oversize" | "unavailable";
|
|
1909
|
-
/**
|
|
1882
|
+
/** The row's `PendingAction.tool_approval.requiresRealApproval`, projected so the ONE-CALL
|
|
1910
1883
|
* inbox can label "only a person can clear this" without an N+1 `get`. `true` or OMITTED (the
|
|
1911
1884
|
* {@link toolCallId} absence-shape contract); pure echo of the persisted bit — a pre-#557 row reads
|
|
1912
1885
|
* absent, which is honest (unreported), never a claim that budget may clear it. */
|
|
1913
1886
|
requiresRealApproval?: true;
|
|
1914
|
-
/**
|
|
1887
|
+
/** The row's `PendingAction.tool_approval.denialLimitFallback`, projected BESIDE the preview
|
|
1915
1888
|
* for the same one-call reason: a card that shows the fallback's counts and limit must not re-open
|
|
1916
1889
|
* the row to learn them (`autoDenyAfterMs` is `0` on every row — see the row member). Pure echo;
|
|
1917
1890
|
* OMITTED when the row carries none. */
|
|
@@ -1925,7 +1898,7 @@ export interface CheckpointSummary {
|
|
|
1925
1898
|
* member of the closed set ({@link import("./ask-origin.js").isRuleStoreUnreadable}); OMITTED otherwise. */
|
|
1926
1899
|
ruleStoreUnreadable?: import("./ask-origin.js").RuleStoreUnreadable;
|
|
1927
1900
|
/**
|
|
1928
|
-
*
|
|
1901
|
+
* PRESENCE ONLY — this pending `tool_approval` row's payload carries at least one DIRECTIONAL
|
|
1929
1902
|
* format control (the {@link import("./tool-policy.js").AskRequest.hasBidiControls} character class;
|
|
1930
1903
|
* that doc owns the hazard). Only ever for a `tool_approval` pendingAction; OMITTED for every other
|
|
1931
1904
|
* kind (the {@link toolCallId} ABSENCE-SHAPE contract applies: never `null`, never present-but-
|
|
@@ -1984,12 +1957,14 @@ export declare function summarizeCheckpoint(cp: Checkpoint): CheckpointSummary;
|
|
|
1984
1957
|
* decides the orphaned card itself, no worker version redeems it), `"ledger_event_unknown"` (a durable
|
|
1985
1958
|
* LEDGER carries an event kind this binary does not know, so the replay is refused rather than served
|
|
1986
1959
|
* incomplete — the one arm that is not row-scoped: NO row in that data directory is readable here, so
|
|
1987
|
-
* the host rolls the binary forward instead of routing one token elsewhere)
|
|
1960
|
+
* the host rolls the binary forward instead of routing one token elsewhere), `"seat_retired"` (the row
|
|
1961
|
+
* parks a steer in the retired pre-queue single seat — terminal for the row's bytes; no worker version
|
|
1962
|
+
* reads that seat, the host decides the parked instruction itself).
|
|
1988
1963
|
* · `invalid_outcome` pre-CAS row-integrity arms: `"real_approval_damaged"`, `"real_approval_forged"`,
|
|
1989
1964
|
* `"read_face_damaged"`, `"read_face_forged"`, `"constraint_chain_missing"` (all terminal for the row's
|
|
1990
1965
|
* current bytes — no worker version redeems a damaged row).
|
|
1991
1966
|
*/
|
|
1992
|
-
export type CheckpointErrorReason = "version_newer" | "env_factory_missing" | "governed_unwired" | "origin_missing" | "ledger_event_unknown" | "real_approval_damaged" | "real_approval_forged" | "read_face_damaged" | "read_face_forged" | "constraint_chain_missing";
|
|
1967
|
+
export type CheckpointErrorReason = "version_newer" | "env_factory_missing" | "governed_unwired" | "origin_missing" | "ledger_event_unknown" | "seat_retired" | "real_approval_damaged" | "real_approval_forged" | "read_face_damaged" | "read_face_forged" | "constraint_chain_missing";
|
|
1993
1968
|
/** A typed checkpoint-store error so callers branch on `code` (mirrors `SessionError`). */
|
|
1994
1969
|
export declare class CheckpointError extends Error {
|
|
1995
1970
|
readonly code: "checkpoint.already_exists" | "checkpoint.already_resolved" | "checkpoint.not_found"
|
|
@@ -1997,7 +1972,7 @@ export declare class CheckpointError extends Error {
|
|
|
1997
1972
|
* persisted {@link CheckpointGate} `kind` (council #3 — type confusion guard), or a gate v1
|
|
1998
1973
|
* resume does not serve (`task_done`). */
|
|
1999
1974
|
| "checkpoint.gate_mismatch"
|
|
2000
|
-
/**
|
|
1975
|
+
/** `runner.resume` was handed an ALREADY-ABORTED `TaskSpec.signal`. Refused BEFORE the CAS, so
|
|
2001
1976
|
* the checkpoint stays `pending` — this worker demonstrably cannot execute the approved action, and
|
|
2002
1977
|
* consuming the human's decision on a leg that will abort immediately loses BOTH the decision and the
|
|
2003
1978
|
* suspended work with nothing to reopen. Re-resume with a live signal. */
|
|
@@ -2011,7 +1986,7 @@ export declare class CheckpointError extends Error {
|
|
|
2011
1986
|
* Rejected pre-CAS so the checkpoint stays `pending` and a capable worker can still resume it
|
|
2012
1987
|
* (design/49 §2/§3, code-ready council round-2). */
|
|
2013
1988
|
| "checkpoint.unsupported_version"
|
|
2014
|
-
/**
|
|
1989
|
+
/** `resume`/`resumeStream` was handed `internals.requestedCwd` for a checkpoint that
|
|
2015
1990
|
* carries a remote `workspaceHandle`. The restored workspace's mountPath is authoritative for the
|
|
2016
1991
|
* task root, so the cwd would be ignored at best — and verifying it would touch the env BEFORE the
|
|
2017
1992
|
* VM restore (a lazy-connect adapter then connects a fresh empty sandbox, the design/49 disease).
|
|
@@ -2074,7 +2049,7 @@ export declare class CheckpointError extends Error {
|
|
|
2074
2049
|
* the checkpoint through its own decide entry instead (the error message names it). */
|
|
2075
2050
|
| "wake.gate_pending"
|
|
2076
2051
|
/** design/144 §3 (anti spin-wake): a `wake` outcome carried no `message` and the checkpoint has no
|
|
2077
|
-
* parked
|
|
2052
|
+
* parked steer either — there is nothing to deliver, so the wake is rejected pre-CAS
|
|
2078
2053
|
* (the checkpoint stays pending) instead of burning the token on an empty continuation. */
|
|
2079
2054
|
| "wake.nothing_to_deliver"
|
|
2080
2055
|
/** Parent effective-policy inheritance: the checkpoint records `state.inheritedGate.requiresParentConstraint`
|
|
@@ -2114,7 +2089,7 @@ export declare class CheckpointError extends Error {
|
|
|
2114
2089
|
* the suspend mint. A target-bound env factory keys its placement lookup on that fixed point,
|
|
2115
2090
|
* so silently resuming under a different one would re-place the leg — and every descendant
|
|
2116
2091
|
* the child-internals chain hands it to — on another target than the parked workspace lives
|
|
2117
|
-
* on. A supplied `rootSessionId` deliberately does NOT take this refusal
|
|
2092
|
+
* on. A supplied `rootSessionId` deliberately does NOT take this refusal: it is
|
|
2118
2093
|
* delegation identity, re-supplied by every revive leg, and legitimately differs from the
|
|
2119
2094
|
* recorded root whenever the spawner carried an explicit placement — the recorded root simply
|
|
2120
2095
|
* outranks it in prepare's restore fold. Rejected PRE-CAS (the checkpoint stays `pending`);
|
|
@@ -2176,7 +2151,7 @@ export declare class CheckpointError extends Error {
|
|
|
2176
2151
|
* persisted {@link CheckpointGate} `kind` (council #3 — type confusion guard), or a gate v1
|
|
2177
2152
|
* resume does not serve (`task_done`). */
|
|
2178
2153
|
| "checkpoint.gate_mismatch"
|
|
2179
|
-
/**
|
|
2154
|
+
/** `runner.resume` was handed an ALREADY-ABORTED `TaskSpec.signal`. Refused BEFORE the CAS, so
|
|
2180
2155
|
* the checkpoint stays `pending` — this worker demonstrably cannot execute the approved action, and
|
|
2181
2156
|
* consuming the human's decision on a leg that will abort immediately loses BOTH the decision and the
|
|
2182
2157
|
* suspended work with nothing to reopen. Re-resume with a live signal. */
|
|
@@ -2190,7 +2165,7 @@ export declare class CheckpointError extends Error {
|
|
|
2190
2165
|
* Rejected pre-CAS so the checkpoint stays `pending` and a capable worker can still resume it
|
|
2191
2166
|
* (design/49 §2/§3, code-ready council round-2). */
|
|
2192
2167
|
| "checkpoint.unsupported_version"
|
|
2193
|
-
/**
|
|
2168
|
+
/** `resume`/`resumeStream` was handed `internals.requestedCwd` for a checkpoint that
|
|
2194
2169
|
* carries a remote `workspaceHandle`. The restored workspace's mountPath is authoritative for the
|
|
2195
2170
|
* task root, so the cwd would be ignored at best — and verifying it would touch the env BEFORE the
|
|
2196
2171
|
* VM restore (a lazy-connect adapter then connects a fresh empty sandbox, the design/49 disease).
|
|
@@ -2253,7 +2228,7 @@ export declare class CheckpointError extends Error {
|
|
|
2253
2228
|
* the checkpoint through its own decide entry instead (the error message names it). */
|
|
2254
2229
|
| "wake.gate_pending"
|
|
2255
2230
|
/** design/144 §3 (anti spin-wake): a `wake` outcome carried no `message` and the checkpoint has no
|
|
2256
|
-
* parked
|
|
2231
|
+
* parked steer either — there is nothing to deliver, so the wake is rejected pre-CAS
|
|
2257
2232
|
* (the checkpoint stays pending) instead of burning the token on an empty continuation. */
|
|
2258
2233
|
| "wake.nothing_to_deliver"
|
|
2259
2234
|
/** Parent effective-policy inheritance: the checkpoint records `state.inheritedGate.requiresParentConstraint`
|
|
@@ -2293,7 +2268,7 @@ export declare class CheckpointError extends Error {
|
|
|
2293
2268
|
* the suspend mint. A target-bound env factory keys its placement lookup on that fixed point,
|
|
2294
2269
|
* so silently resuming under a different one would re-place the leg — and every descendant
|
|
2295
2270
|
* the child-internals chain hands it to — on another target than the parked workspace lives
|
|
2296
|
-
* on. A supplied `rootSessionId` deliberately does NOT take this refusal
|
|
2271
|
+
* on. A supplied `rootSessionId` deliberately does NOT take this refusal: it is
|
|
2297
2272
|
* delegation identity, re-supplied by every revive leg, and legitimately differs from the
|
|
2298
2273
|
* recorded root whenever the spawner carried an explicit placement — the recorded root simply
|
|
2299
2274
|
* outranks it in prepare's restore fold. Rejected PRE-CAS (the checkpoint stays `pending`);
|
|
@@ -2627,11 +2602,9 @@ export interface CheckpointStore {
|
|
|
2627
2602
|
* {@link PENDING_STEER_QUEUE_BYTE_BUDGET_BYTES} throws `steering.queue_full` — bounded fail-loud, never
|
|
2628
2603
|
* by evicting an accepted instruction.
|
|
2629
2604
|
*
|
|
2630
|
-
*
|
|
2631
|
-
* {@link
|
|
2632
|
-
*
|
|
2633
|
-
* fields, and must build its new queue with {@link appendPendingSteer} so the bounds and the `seq` mint
|
|
2634
|
-
* are one implementation rather than one per store.
|
|
2605
|
+
* A backend must round-trip the queue field verbatim and must build its new queue with
|
|
2606
|
+
* {@link appendPendingSteer} so the bounds and the `seq` mint are one implementation rather than one
|
|
2607
|
+
* per store.
|
|
2635
2608
|
*
|
|
2636
2609
|
* **Trust is FROZEN here** — `steer.trusted` is whatever the SERVICE computed from the verified principal
|
|
2637
2610
|
* at this call (an operator-role check, NOT a client header — the service's job); the core stores it
|
|
@@ -2841,17 +2814,16 @@ export declare const MAX_STEER_INPUT_ID_CHARS = 128;
|
|
|
2841
2814
|
export declare const ACTOR_ASSERTION_FROZEN_FIELDS: readonly ["id", "hostAsserted", "issuer"];
|
|
2842
2815
|
/**
|
|
2843
2816
|
* design/171 §5.3 — THE single read point for a checkpoint's parked steers, in delivery (`seq`) order.
|
|
2817
|
+
* Every consumer — the resume drain, the wake's nothing-to-deliver guard, a serving layer — goes through
|
|
2818
|
+
* here, so no site can grow its own idea of what "the parked steer" is.
|
|
2844
2819
|
*
|
|
2845
|
-
*
|
|
2846
|
-
*
|
|
2847
|
-
*
|
|
2848
|
-
*
|
|
2849
|
-
*
|
|
2820
|
+
* A row that still carries the RETIRED pre-queue single seat (`state.pendingSteer`, written before the
|
|
2821
|
+
* queue existed) is refused here rather than read: the seat has no `inputId`, `seq` or actor, so folding
|
|
2822
|
+
* it in would mint a synthetic identity nothing else can name. The refusal is `checkpoint.unsupported_version`
|
|
2823
|
+
* with `reason: "seat_retired"` — terminal for the row's bytes; the host decides the parked instruction
|
|
2824
|
+
* on its own surface and parks it again through `setPendingSteer` if it still applies.
|
|
2850
2825
|
*/
|
|
2851
|
-
export declare function readPendingSteerQueue(state: Pick<CheckpointState, "
|
|
2852
|
-
/** design/171 §5.3 — the synthetic `inputId` a pre-queue single-seat steer reads back as. Stable (never
|
|
2853
|
-
* minted per read) so dedupe and re-reads behave, and reserved so a caller cannot collide with it. */
|
|
2854
|
-
export declare const LEGACY_PENDING_STEER_INPUT_ID = "legacy-single-seat";
|
|
2826
|
+
export declare function readPendingSteerQueue(state: Pick<CheckpointState, "pendingSteerQueue">): PendingSteerEntry[];
|
|
2855
2827
|
/**
|
|
2856
2828
|
* design/171 §5.3/§6.3 — THE append, shared by every backend so the bounds and the ordering are one
|
|
2857
2829
|
* implementation rather than one per store.
|
|
@@ -2860,7 +2832,7 @@ export declare const LEGACY_PENDING_STEER_INPUT_ID = "legacy-single-seat";
|
|
|
2860
2832
|
* be crossed. Re-appending an entry whose `inputId` is already queued is a NO-OP returning the queue
|
|
2861
2833
|
* unchanged — that is what keeps `setPendingSteer` retry-safe now that it appends instead of overwriting.
|
|
2862
2834
|
*/
|
|
2863
|
-
export declare function appendPendingSteer(state: Pick<CheckpointState, "
|
|
2835
|
+
export declare function appendPendingSteer(state: Pick<CheckpointState, "pendingSteerQueue">, entry: Omit<PendingSteerEntry, "seq">): PendingSteerEntry[];
|
|
2864
2836
|
/**
|
|
2865
2837
|
* REF-A4 — THE row half of every guarded checkpoint transition's CAS predicate, shared by every backend:
|
|
2866
2838
|
* the row must EXIST, its `scope` must match (multi-tenant isolation, §2.1 — a wrong-scope caller never
|
|
@@ -453,9 +453,6 @@ export function validatePendingSteer(steer) {
|
|
|
453
453
|
if (inputId !== undefined && (inputId === "" || inputId.length > MAX_STEER_INPUT_ID_CHARS)) {
|
|
454
454
|
throw new CheckpointError("steering.invalid_content", `steering inputId must be a non-empty string of at most ${MAX_STEER_INPUT_ID_CHARS} characters`);
|
|
455
455
|
}
|
|
456
|
-
if (inputId === LEGACY_PENDING_STEER_INPUT_ID) {
|
|
457
|
-
throw new CheckpointError("steering.invalid_content", `steering inputId "${LEGACY_PENDING_STEER_INPUT_ID}" is reserved for a pre-queue parked steer and cannot be supplied by a caller`);
|
|
458
|
-
}
|
|
459
456
|
if (sanitizeUntrustedText(text) !== text) {
|
|
460
457
|
throw new CheckpointError("steering.invalid_content", "steering text must not contain a system-reminder break-out tag");
|
|
461
458
|
}
|
|
@@ -512,14 +509,11 @@ function captureActorAssertion(actor) {
|
|
|
512
509
|
return { id, hostAsserted, ...(issuer !== undefined ? { issuer } : {}) };
|
|
513
510
|
}
|
|
514
511
|
export function readPendingSteerQueue(state) {
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
return [...legacy, ...queued];
|
|
521
|
-
}
|
|
522
|
-
export const LEGACY_PENDING_STEER_INPUT_ID = "legacy-single-seat";
|
|
512
|
+
if (state.pendingSteer !== undefined) {
|
|
513
|
+
throw new CheckpointError("checkpoint.unsupported_version", "this checkpoint carries a parked steer in the retired pre-queue single seat (`state.pendingSteer`), which this engine does not read — decide the parked instruction on the host's surface and, if it still applies, park it again with setPendingSteer", { reason: "seat_retired" });
|
|
514
|
+
}
|
|
515
|
+
return [...(state.pendingSteerQueue ?? [])].sort((a, b) => a.seq - b.seq);
|
|
516
|
+
}
|
|
523
517
|
function samePendingSteerPayload(a, b) {
|
|
524
518
|
return (a.text === b.text &&
|
|
525
519
|
a.trusted === b.trusted &&
|
|
@@ -530,23 +524,22 @@ function samePendingSteerPayload(a, b) {
|
|
|
530
524
|
}
|
|
531
525
|
export function appendPendingSteer(state, entry) {
|
|
532
526
|
const current = readPendingSteerQueue(state);
|
|
533
|
-
const existingQueue = [...(state.pendingSteerQueue ?? [])].sort((a, b) => a.seq - b.seq);
|
|
534
527
|
const collision = current.find((e) => e.inputId === entry.inputId);
|
|
535
528
|
if (collision !== undefined) {
|
|
536
529
|
if (!samePendingSteerPayload(collision, entry)) {
|
|
537
530
|
throw new CheckpointError("steering.duplicate_input_id", `a different steering instruction is already parked under inputId "${entry.inputId}" — ` +
|
|
538
531
|
`re-issue this one with a fresh inputId (an identical payload would have been an idempotent retry)`);
|
|
539
532
|
}
|
|
540
|
-
return
|
|
533
|
+
return current;
|
|
541
534
|
}
|
|
542
535
|
const nextSeq = current.reduce((max, e) => Math.max(max, e.seq), 0) + 1;
|
|
543
|
-
const next = [...
|
|
544
|
-
const totalEntries = next.length
|
|
536
|
+
const next = [...current, { ...entry, seq: nextSeq }];
|
|
537
|
+
const totalEntries = next.length;
|
|
545
538
|
if (totalEntries > MAX_PENDING_STEER_ENTRIES) {
|
|
546
539
|
throw new CheckpointError("steering.queue_full", `the parked steering queue already holds ${totalEntries - 1} entries (max ${MAX_PENDING_STEER_ENTRIES}) — ` +
|
|
547
540
|
`deliver or remove a parked steer before adding another`);
|
|
548
541
|
}
|
|
549
|
-
const bytes = Buffer.byteLength(JSON.stringify({
|
|
542
|
+
const bytes = Buffer.byteLength(JSON.stringify({ pendingSteerQueue: next }), "utf8");
|
|
550
543
|
if (bytes > PENDING_STEER_QUEUE_BYTE_BUDGET_BYTES) {
|
|
551
544
|
throw new CheckpointError("steering.queue_full", `the parked steering queue would serialize to ${bytes} bytes (max ${PENDING_STEER_QUEUE_BYTE_BUDGET_BYTES}) — ` +
|
|
552
545
|
`deliver or remove a parked steer before adding another`);
|