@sema-agent/core 7.5.2 → 7.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/dist/agents/agent-transcript-tool.d.ts +2 -2
  3. package/dist/agents/cascade.d.ts +4 -5
  4. package/dist/agents/cascade.js +12 -10
  5. package/dist/agents/repair-loop.d.ts +7 -5
  6. package/dist/agents/repair-loop.js +13 -15
  7. package/dist/agents/retain-ledger.d.ts +2 -3
  8. package/dist/agents/send-message-tool.d.ts +2 -2
  9. package/dist/agents/session-util.d.ts +2 -2
  10. package/dist/agents/subagent.d.ts +27 -46
  11. package/dist/agents/subagent.js +119 -105
  12. package/dist/agents/suspend-guard.d.ts +31 -19
  13. package/dist/agents/suspend-guard.js +14 -8
  14. package/dist/agents/teacher.d.ts +2 -2
  15. package/dist/agents/teacher.js +9 -9
  16. package/dist/agents/team.d.ts +6 -5
  17. package/dist/agents/team.js +10 -8
  18. package/dist/agents/verify.d.ts +8 -9
  19. package/dist/agents/verify.js +17 -17
  20. package/dist/core/a2a.js +2 -1
  21. package/dist/core/agent-definition.d.ts +172 -0
  22. package/dist/core/agent-definition.js +1 -0
  23. package/dist/core/ask-origin.d.ts +60 -7
  24. package/dist/core/ask-origin.js +26 -1
  25. package/dist/core/checkpoint-store.d.ts +78 -76
  26. package/dist/core/checkpoint-store.js +17 -1
  27. package/dist/core/delegation-frames.d.ts +298 -0
  28. package/dist/core/delegation-frames.js +21 -0
  29. package/dist/core/engine-notice.d.ts +555 -0
  30. package/dist/core/engine-notice.js +55 -0
  31. package/dist/core/gate-fold.d.ts +12 -0
  32. package/dist/core/gate-fold.js +158 -0
  33. package/dist/core/gate-lanes.d.ts +93 -0
  34. package/dist/core/gate-lanes.js +626 -0
  35. package/dist/core/gate-outcome.d.ts +189 -0
  36. package/dist/core/gate-outcome.js +70 -0
  37. package/dist/core/hands-band.d.ts +134 -0
  38. package/dist/core/hands-band.js +1 -0
  39. package/dist/core/hooks.d.ts +22 -177
  40. package/dist/core/hooks.js +53 -851
  41. package/dist/core/mcp-failure.d.ts +142 -0
  42. package/dist/core/mcp-failure.js +145 -0
  43. package/dist/core/mcp-server-spec.d.ts +217 -0
  44. package/dist/core/mcp-server-spec.js +1 -0
  45. package/dist/core/mcp.d.ts +21 -77
  46. package/dist/core/mcp.js +76 -150
  47. package/dist/core/model-seat.d.ts +99 -0
  48. package/dist/core/model-seat.js +1 -0
  49. package/dist/core/pause-registry.d.ts +131 -0
  50. package/dist/core/pause-registry.js +27 -0
  51. package/dist/core/reminder-mint.d.ts +10 -0
  52. package/dist/core/reminder-mint.js +3 -0
  53. package/dist/core/runner/assemble-result.d.ts +32 -41
  54. package/dist/core/runner/assemble-result.js +55 -74
  55. package/dist/core/runner/contracts.d.ts +427 -69
  56. package/dist/core/runner/denial-limit-arms.d.ts +1 -1
  57. package/dist/core/runner/denial-limit-arms.js +3 -3
  58. package/dist/core/runner/gate-exit.d.ts +242 -0
  59. package/dist/core/runner/gate-exit.js +124 -0
  60. package/dist/core/runner/park-commit.d.ts +17 -23
  61. package/dist/core/runner/park-commit.js +14 -15
  62. package/dist/core/runner/prepare-ask-lane.d.ts +0 -3
  63. package/dist/core/runner/prepare-ask-lane.js +3 -5
  64. package/dist/core/runner/prepare-boundary-parks.d.ts +3 -6
  65. package/dist/core/runner/prepare-boundary-parks.js +3 -3
  66. package/dist/core/runner/prepare-caps-and-workflow.d.ts +2 -7
  67. package/dist/core/runner/prepare-caps-and-workflow.js +1 -1
  68. package/dist/core/runner/prepare-delegation-surface.d.ts +2 -7
  69. package/dist/core/runner/prepare-gate-stations.d.ts +4 -7
  70. package/dist/core/runner/prepare-gate-stations.js +29 -54
  71. package/dist/core/runner/prepare-inherited-gate.js +1 -1
  72. package/dist/core/runner/prepare-memory.d.ts +44 -26
  73. package/dist/core/runner/prepare-park-ask.d.ts +2 -4
  74. package/dist/core/runner/prepare-park-ask.js +5 -5
  75. package/dist/core/runner/prepare-task.d.ts +2 -2
  76. package/dist/core/runner/prepare-task.js +8 -9
  77. package/dist/core/runner/prepare-wiring-manifest.d.ts +7 -15
  78. package/dist/core/runner/prepare-wiring-manifest.js +9 -10
  79. package/dist/core/runner/runtask.d.ts +20 -102
  80. package/dist/core/runner/runtask.js +119 -121
  81. package/dist/core/runner/terminal-projection.d.ts +22 -0
  82. package/dist/core/runner/terminal-projection.js +28 -0
  83. package/dist/core/runner-deps.d.ts +1416 -0
  84. package/dist/core/runner-deps.js +1 -0
  85. package/dist/core/runtime-caps.d.ts +164 -0
  86. package/dist/core/runtime-caps.js +1 -0
  87. package/dist/core/store-contracts/checkpoint-store-contract.d.ts +4 -1
  88. package/dist/core/store-contracts/checkpoint-store-contract.js +8 -2
  89. package/dist/core/task-event.d.ts +910 -0
  90. package/dist/core/task-event.js +1 -0
  91. package/dist/core/task-limits.d.ts +110 -0
  92. package/dist/core/task-limits.js +1 -0
  93. package/dist/core/task-result.d.ts +809 -0
  94. package/dist/core/task-result.js +1 -0
  95. package/dist/core/task-spec.d.ts +1370 -0
  96. package/dist/core/task-spec.js +1 -0
  97. package/dist/core/task-stream.d.ts +382 -0
  98. package/dist/core/task-stream.js +1 -0
  99. package/dist/core/terminal-cause.d.ts +137 -0
  100. package/dist/core/terminal-cause.js +9 -0
  101. package/dist/core/tool-policy.d.ts +43 -139
  102. package/dist/core/tool-policy.js +79 -112
  103. package/dist/core/tool-spec.d.ts +1174 -0
  104. package/dist/core/tool-spec.js +1 -0
  105. package/dist/core/types.d.ts +27 -7789
  106. package/dist/core/types.js +2 -76
  107. package/dist/core/warm-resume.d.ts +2 -2
  108. package/dist/core/wiring-manifest.d.ts +6 -3
  109. package/dist/core/workflow-journal-store.js +3 -4
  110. package/dist/engine/harness/agent-harness.d.ts +1 -1
  111. package/dist/index.d.ts +12 -8
  112. package/dist/index.js +9 -6
  113. package/dist/orchestration/builtin-workflows.d.ts +2 -2
  114. package/dist/orchestration/builtin-workflows.js +1 -1
  115. package/dist/orchestration/goal.d.ts +2 -2
  116. package/dist/orchestration/goal.js +8 -7
  117. package/dist/orchestration/run-spec.d.ts +2 -2
  118. package/dist/orchestration/run-spec.js +5 -3
  119. package/dist/orchestration/run-workflow-tool.d.ts +4 -4
  120. package/dist/orchestration/run-workflow-tool.js +4 -4
  121. package/dist/orchestration/workflow-governance.d.ts +4 -4
  122. package/dist/orchestration/workflow-governance.js +4 -2
  123. package/dist/orchestration/workflow-primitives.d.ts +1 -1
  124. package/dist/orchestration/workflow-primitives.js +1 -1
  125. package/dist/orchestration/workflow.d.ts +15 -4
  126. package/dist/orchestration/workflow.js +64 -39
  127. package/dist/prompts/supervisor.d.ts +1 -1
  128. package/dist/prompts/supervisor.js +3 -3
  129. package/dist/scenarios/scenario-registry.d.ts +3 -3
  130. package/dist/scenarios/scenario-registry.js +1 -1
  131. package/dist/scenarios/teacher-quickstart.d.ts +2 -2
  132. package/dist/server/http.d.ts +2 -2
  133. package/dist/stores/file/fs-atomic.d.ts +88 -12
  134. package/dist/stores/file/fs-atomic.js +184 -55
  135. package/dist/stores/file/index.d.ts +1 -0
  136. package/dist/stores/file/index.js +1 -0
  137. package/package.json +3 -1
  138. package/test/export-surface.snapshot.json +82 -22
@@ -22,29 +22,35 @@ import type { WorkflowSizeGuideline } from "../../orchestration/workflow-size-gu
22
22
  import type { ToolManifestRow } from "../../prompt-assembly/tool-catalog.js";
23
23
  import type { CwdRef, ReadFace } from "../../tools/fs/index.js";
24
24
  import type { MaterializedA2a } from "../a2a.js";
25
- import type { CompactionForkContext } from "../auto-compaction.js";
25
+ import type { CompactionForkContext, MaybeCompactOptions, RapidRefillState } from "../auto-compaction.js";
26
26
  import type { AutoModeArmingRecipe } from "../auto-mode-arming.js";
27
27
  import type { AutoModeDecider, AutoModeDenialTracker } from "../auto-mode.js";
28
28
  import type { CacheBreakDetector, ToolFingerprintInput } from "../cache-break-detector.js";
29
- import type { Checkpoint, CheckpointGate, CheckpointState, CheckpointToken, PlatformLimitReason, ResourceLedger, ResourceLimitReason, SerializedCheckpointState } from "../checkpoint-store.js";
29
+ import type { ActorAssertion, Checkpoint, CheckpointState, CheckpointStore, CheckpointToken, PendingSteerEntry, PlatformLimitReason, ReopenReason, ResourceLedger, ResourceLimitReason, ResumeOutcome, SerializedCheckpointState } from "../checkpoint-store.js";
30
30
  import type { ClearedProjectionLedger, ContextEditMachine, OccurrenceIndex } from "../context-edit.js";
31
- import type { HookInvocationIdentity, Hooks, OrgGateVerdict, PermissionDeniedPayload, ToolGateInput } from "../hooks.js";
31
+ import type { HookInvocationIdentity, Hooks, OrgGateVerdict, PermissionDeniedPayload, PostToolBatchCall, PostToolBatchResult, ToolGateInput } from "../hooks.js";
32
32
  import type { MaterializedMcp } from "../mcp.js";
33
33
  import type { MemoryEngine } from "../memory-engine/engine.js";
34
34
  import type { HarvestReport, MemorySessionHandle } from "../memory-engine/types.js";
35
35
  import type { SessionPermissionRules } from "../session-policy-store.js";
36
36
  import type { RecoveredOrphan } from "../session-reconcile.js";
37
- import type { StoredSession } from "../session.js";
38
- import type { TaskNotificationPayload } from "../task-notification.js";
37
+ import type { SessionStore, StoredSession } from "../session.js";
38
+ import type { TaskOutcome } from "../task-outcome.js";
39
+ import type { ModelPricing } from "../pricing.js";
40
+ import type { PushQueue } from "../push-queue.js";
41
+ import type { SystemInjectionPriority, TaskNotificationPayload } from "../task-notification.js";
39
42
  import type { OnAsk, PermissionResult, ResolvedAsk, ToolCallRequest, ToolPolicy } from "../tool-policy.js";
40
43
  import type { RemoteExecutionEnv, SnapshotId, WorkspaceHandle } from "../remote-env.js";
41
44
  import type { ToolDisclosureManifest } from "../trace.js";
42
- import type { NestedUsageAccum, RunnerDeps, TaskEvent, TaskResult, TaskSpec, ToolActivity, ToolEffect } from "../types.js";
45
+ import type { AgentDefinition, Brain, CompactOutcome, NestedUsageAccum, RunnerDeps, TaskEvent, TaskResult, TaskSpec, TaskStream, ToolActivity, ToolEffect } from "../types.js";
43
46
  import type { WiringManifest } from "../wiring-manifest.js";
44
47
  import type { ActiveSkillFrame } from "./active-skill-scope.js";
48
+ import type { BudgetAxis } from "./assemble-result.js";
45
49
  import type { GitStatusLaneRef } from "./git-status-frame.js";
46
50
  import type { SessionReadFileStates } from "./prepare-hands-readface.js";
47
51
  import type { BlockedRef, OutputRef, SkillListingEntry } from "./synthetic-tools.js";
52
+ import type { createAttachmentState, WriteFamily } from "./turn-attachments.js";
53
+ import type { cacheFamilyOf } from "./usage-accounting.js";
48
54
  /**
49
55
  * The frozen task-start snapshot of the caller's tool-face control arrays: produced by the
50
56
  * config-doors phase, read by the hands/read-face phase. Named here rather than reached for as
@@ -144,6 +150,18 @@ export interface FileHistoryBoundarySeat {
144
150
  begin(entryId: string): void;
145
151
  settle(): Promise<void>;
146
152
  }
153
+ /**
154
+ * The commit-side facts of the run's ONE durable pause: the terminal cause the result assembles
155
+ * ({@link import("../types.js").PausedCause} — gate, token, checkpoint id, restore mode), the COMMITTED scope
156
+ * (read verbatim by the reap/cancel path — never re-derived from the spec, which the cross-scope re-scope
157
+ * may no longer match), and the id of the tool call the park is holding (only a `tool_approval` pending
158
+ * action parks ON a call; absent, never guessed, for the kinds that hold none).
159
+ */
160
+ export interface CommittedPause {
161
+ cause: import("../types.js").PausedCause;
162
+ scope: string;
163
+ gatedCallId?: string;
164
+ }
147
165
  export interface Prepared {
148
166
  harness: AgentHarness;
149
167
  /** The CONCRETE built-in session (engine-internal: prepare constructs/acquires `StoredSession` itself,
@@ -221,17 +239,12 @@ export interface Prepared {
221
239
  *
222
240
  * It is a sideband and not a field on the tool RESULT because a result is not a trustworthy carrier
223
241
  * for this: `details` is arbitrary tool-authored data that post-tool hooks may also replace, so a
224
- * failing tool could stamp `settledBy:"human"` on itself and tell a consumer's audit view that a
225
- * person approved something nobody was ever shown. Provenance has to travel on a channel only the
242
+ * failing tool could stamp a settlement on itself and tell a consumer's audit view that a person
243
+ * approved something nobody was ever shown. Provenance has to travel on a channel only the
226
244
  * adjudicating layer can write. Same reason the entries are keyed by CALL id: the gate adjudicated
227
245
  * that exact call, and the frame that reads it is that call's own.
228
246
  */
229
- approvalSettlement: Map<string, {
230
- settledBy?: import("../tool-policy.js").ApprovalSettledBy;
231
- approver?: string;
232
- resolution?: import("../tool-policy.js").AskDenyResolution;
233
- autoDenied?: true;
234
- }>;
247
+ gateOutcomes: Map<string, import("../gate-outcome.js").GateOutcome>;
235
248
  /**
236
249
  * The parent-thread human-rejection halt fact (see `maybeHumanRejectionHalt`): present from the
237
250
  * moment a bare human rejection halts the turn's batch until the run ends or the NEXT provider
@@ -554,24 +567,15 @@ export interface Prepared {
554
567
  * and the Runner must tear down on task end. Undefined when the env came from a (caller-owned) static
555
568
  * `deps.executionEnv` or the stub — those outlive the task and must NOT be destroyed here. */
556
569
  ownedEnv?: ExecutionEnv;
557
- /** design/45: a mutable holder the durable-suspend gate writes when a policy `ask` was checkpointed
558
- * (capture + abort). The run loop reads it to assemble `status:"suspended"`. Empty unless a suspension
559
- * fired this run.
560
- *
561
- * `gatedCallId` is the id of the tool call the committed gate is holding read straight off the
562
- * committed checkpoint's `tool_approval` pendingAction, so the id a contaminated sibling frame names
563
- * and the id the checkpoint parks on are the SAME value by construction, not by convention. ABSENT
564
- * (never guessed) for a park that binds no tool call at all — a `resource_limit` slice and a
565
- * `plan_review` pause both have pendingActions with no tool call, so there is no causal id to name.
566
- * Written unconditionally by the commit-side publisher precisely so a later park cannot inherit an
567
- * earlier one's id. */
568
- suspendRef: {
569
- token?: CheckpointToken;
570
- checkpointId?: string;
571
- gate?: CheckpointGate;
572
- scope?: string;
573
- restoreMode?: "snapshot" | "park_only";
574
- gatedCallId?: string;
570
+ /** The run's ONE committed durable pause ({@link CommittedPause}): a mutable holder the commit-side
571
+ * publisher (`publishCommittedSuspend`) writes exactly once per run when a park commits an approval
572
+ * or content ask, a resource slice, a platform pause, a plan or dry-run review — and the run loop reads
573
+ * to assemble the result's `terminal: { kind: "paused", … }`. WHICH pause it is is the cause's own
574
+ * `gate.kind` (a registry row), not the holder it landed in: the two same-shaped holders that used to
575
+ * split the approval family from the review family, and the commit-side routing between them, are
576
+ * gone. `current` is absent until a park commits. */
577
+ pausedRef: {
578
+ current?: CommittedPause;
575
579
  };
576
580
  /** Ruled 2026-08-05 (matrix ruling arm A): set true by the resume engine when this leg EXECUTES the
577
581
  * approved pending call — the restart-loop cap then counts from a fresh base (consecutive
@@ -579,22 +583,6 @@ export interface Prepared {
579
583
  suspendProgressRef: {
580
584
  executedApproved: boolean;
581
585
  };
582
- /** design/76 §2.5 (dry-run / shadow) + design/80 D-B (plan-gate): the DUAL of {@link suspendRef} for the
583
- * REVIEW-PAUSE family — a `{kind:"needs_review"}` pause (a profile's dry-run interception committed a
584
- * checkpoint whose predicted state-diff a human/judge must REVIEW) OR a `{kind:"plan_review"}` pause (a
585
- * profile's plan-gate committed a checkpoint whose proposed PLAN a human must approve/edit/reject). The
586
- * commit-side discriminant (`publishCommittedSuspend`) writes HERE for a `needs_review` OR `plan_review`
587
- * gate and into {@link suspendRef} for every other gate kind — **never both** (else assemble-result's slot
588
- * 8.6 `needs_review` branch is dead code, v4 MAJOR-A). The run loop reads it to assemble
589
- * `status:"needs_review"`. Empty unless a review pause fired this run. */
590
- reviewRef: {
591
- token?: CheckpointToken;
592
- checkpointId?: string;
593
- gate?: CheckpointGate;
594
- scope?: string;
595
- restoreMode?: "snapshot" | "park_only";
596
- gatedCallId?: string;
597
- };
598
586
  /** RB-439-a: the remote-workspace lifecycle failures this run hit, appended in call order and echoed
599
587
  * verbatim on `TaskResult.remoteEnvFailures`. A shared array (not a per-call return) because a suspend
600
588
  * refusal is reported through the deployment's `onError` side channel and the run then continues or
@@ -614,7 +602,7 @@ export interface Prepared {
614
602
  * results, and a remote — or static caller-owned, never per-task-stub — env). The run loop calls it at a
615
603
  * CLEAN turn boundary when a resource limit (turns/budget/walltime) was hit: it mints a `resource_limit`
616
604
  * checkpoint + pauses the workspace + stops the loop cleanly (NOT abort). Returns true iff it committed a
617
- * resumable checkpoint (sets `suspendRef`); false ⇒ caller falls through to normal limit handling.
605
+ * resumable checkpoint (publishes the committed pause); false ⇒ caller falls through to normal limit handling.
618
606
  * `sliceSpend` (Slice 4) is THIS slice's cost/tokens/turns, debited onto the cross-slice ledger. */
619
607
  suspendForResource?: (reason: ResourceLimitReason, sliceSpend: {
620
608
  costMicroUsd: number;
@@ -685,7 +673,7 @@ export interface Prepared {
685
673
  };
686
674
  /** design/80 D-B: present (≠ undefined) ONLY when a `checkpointStore` is wired (the deployment can pause). The
687
675
  * run loop calls it at a CLEAN turn boundary when `reviewRequestRef.pending` is set: it mints a `plan_review`
688
- * checkpoint (`status:"needs_review"`, routes to `reviewRef`) + pauses the workspace + aborts the loop, reusing
676
+ * checkpoint (`status:"needs_review"` by its registry row) + pauses the workspace + aborts the loop, reusing
689
677
  * the SAME commit saga as the human/resource suspends. Returns true iff it committed a resumable checkpoint;
690
678
  * false ⇒ the request could not be honored (caller drops it and continues). */
691
679
  suspendForReview?: (reason?: string) => Promise<boolean>;
@@ -1298,6 +1286,25 @@ export interface InheritedGate {
1298
1286
  hookEnv?: unknown;
1299
1287
  }>;
1300
1288
  }
1289
+ /** The runtime feature flags the stable prompt's harness-context block gates its sections on
1290
+ * (design/64 §6.3: claim only what the task has). Spread into the assembly's StablePromptContext
1291
+ * by the prompt-assembly phase, which re-derives the two orchestration flags against the post-exclusion roster. */
1292
+ export interface PromptFeatureFlags {
1293
+ policyEnabled: boolean;
1294
+ hooksEnabled: boolean;
1295
+ isolationEnabled: boolean;
1296
+ reminderMark: string;
1297
+ readFaceOpen: boolean;
1298
+ orchestrationEnabled: boolean;
1299
+ orchestrationDeferred: boolean;
1300
+ promptProfile: "simple" | "classic";
1301
+ fableMitigations: boolean;
1302
+ goalEnabled: boolean;
1303
+ awarenessEnabled: boolean;
1304
+ worktreeIsolated: boolean;
1305
+ withinTaskCompactionEnabled: boolean;
1306
+ isSubagent: boolean;
1307
+ }
1301
1308
  /**
1302
1309
  * design/78 Slice-1 (MAJOR-3 wiring): a TRUSTED, run-scoped internal channel into {@link prepareTask}, set
1303
1310
  * ONLY by a trusted CORE caller (`runRepairLoop` via the Runner's internal `runTaskStream` arg) — NEVER from
@@ -1322,25 +1329,6 @@ export interface InheritedGate {
1322
1329
  * them; the engine does not defend against it. The untrusted surface is {@link TaskSpec}, whose pre-first-
1323
1330
  * await latch is a separate, pinned guarantee (prepare-task-phase-pins T1).
1324
1331
  */
1325
- /** The runtime feature flags the stable prompt's harness-context block gates its sections on
1326
- * (design/64 §6.3: claim only what the task has). Spread into the assembly's StablePromptContext
1327
- * by the prompt-assembly phase, which re-derives the two orchestration flags against the post-exclusion roster. */
1328
- export interface PromptFeatureFlags {
1329
- policyEnabled: boolean;
1330
- hooksEnabled: boolean;
1331
- isolationEnabled: boolean;
1332
- reminderMark: string;
1333
- readFaceOpen: boolean;
1334
- orchestrationEnabled: boolean;
1335
- orchestrationDeferred: boolean;
1336
- promptProfile: "simple" | "classic";
1337
- fableMitigations: boolean;
1338
- goalEnabled: boolean;
1339
- awarenessEnabled: boolean;
1340
- worktreeIsolated: boolean;
1341
- withinTaskCompactionEnabled: boolean;
1342
- isSubagent: boolean;
1343
- }
1344
1332
  export interface RunInternals {
1345
1333
  /** The live repair bundle from a `runRepairLoop` attempt in flight (attemptCount>0). Serialized onto a
1346
1334
  * checkpoint minted MID-attempt so a resume re-seeds it; undefined for any non-repair run. */
@@ -1988,3 +1976,373 @@ export interface ParkAsk {
1988
1976
  * lane is not armed (the gate then keeps the synchronous onAsk path). */
1989
1977
  suspendAsk: ToolGateInput["suspendAsk"];
1990
1978
  }
1979
+ /**
1980
+ * design/157 B15 二期 — `runLocked`'s per-run mutable state, EXPLICIT. These fields started as loose
1981
+ * `let`/`const` bindings in the R2 declaration hub; the extraction kept every assignment at its
1982
+ * original site and order (binding-form-only rewrite — the event-snapshot pin held the run
1983
+ * byte-identical), and later work has since added fields directly here (see the per-field notes).
1984
+ * Grouped by lifecycle semantics per the B15 pre-recon partition. Function members start as inert
1985
+ * placeholders and are assigned at their ORIGINAL declaration sites (the original code could not
1986
+ * call them earlier either — TDZ proved it). Not on the npm face; this extraction is what enabled
1987
+ * makeTurnBoundary / makeHarnessHandlers (三期, both in runtask.ts).
1988
+ */
1989
+ export interface RunState {
1990
+ telemetry: {
1991
+ cacheFamily: ReturnType<typeof cacheFamilyOf>;
1992
+ pricing: ModelPricing;
1993
+ /** RB-368 ([2076]): whether the CURRENT serving model has a configured price table (a
1994
+ * `RunnerDeps.pricing` entry or a `Model.cost` declaration). When neither exists,
1995
+ * `modelCostToPricing(undefined)` yields an all-zero table and every computed cost is a
1996
+ * fabricated 0 — indistinguishable from "declared free". This flag keeps the two apart so
1997
+ * the DISCLOSURE faces (trace frames, public stats) can go absent instead of lying; the
1998
+ * internal budget coordinate (`stats.costMicroUsd`) stays numeric either way. */
1999
+ pricingConfigured: boolean;
2000
+ /** RB-368: sticky — some spend was ACCOUNTED while `pricingConfigured` was false (a mid-task
2001
+ * model switch can flip pricedness, so per-call state isn't enough for the task total). Once
2002
+ * true, the task-level cost total is an undercount and its faces are omitted. */
2003
+ unpricedSpend: boolean;
2004
+ tracer: TaskSpec["tracer"];
2005
+ taskId: string;
2006
+ /** #499 — the run's engine-minted identity, so the task.start/task.end pair carries it from ONE
2007
+ * seat (the same place taskId is published). Empty only before the run body assigns it. */
2008
+ runId: string;
2009
+ taskStart: number;
2010
+ taskStartMonotonic: number;
2011
+ cacheBreakReported: boolean;
2012
+ };
2013
+ degrade: {
2014
+ degradeToModel: Model | undefined;
2015
+ degraded: TaskResult["degraded"];
2016
+ outputErrorStreak: number;
2017
+ outputInvalid: boolean;
2018
+ recordDegraded: (info: NonNullable<TaskResult["degraded"]>, toModel?: Model) => void;
2019
+ };
2020
+ limits: {
2021
+ turnsExceeded: boolean;
2022
+ budgetHit: "exceeded" | "precall" | undefined;
2023
+ /** design/164 — WHICH budget axis tripped, set at the same instant as {@link budgetHit} so the
2024
+ * terminal can name it (`limits.max_tokens_exceeded` vs `limits.max_cost_exceeded`). Tokens
2025
+ * outrank cost when both are over at the same boundary (the documented attribution order). */
2026
+ budgetAxis: BudgetAxis | undefined;
2027
+ /** design/164 件四/件五 — the TYPED terminal an EXTERNAL cause (a dying execution environment, an
2028
+ * exhausted governance window) recorded when it could not suspend the run durably. Set together with
2029
+ * the abort it triggers, and adopted as the run's `threw` once the loop settles, so the caller gets
2030
+ * `env.lifetime_expired` / `usage.window_exhausted` instead of the anonymous error an abort produces.
2031
+ * Same carrier shape as `brainCallGuardrailRef.timedOut`, and for the same reason: the harness turns a
2032
+ * loop throw into an error assistant message that strips the error object. */
2033
+ platformTerminal: (Error & {
2034
+ code?: string;
2035
+ retryAfterMs?: number;
2036
+ }) | undefined;
2037
+ outputRetryCap: number;
2038
+ effectiveMaxTurns: number | undefined;
2039
+ };
2040
+ budget: {
2041
+ remainingMicroUsd: number | undefined;
2042
+ maxCostMicroUsd: number | undefined;
2043
+ /** design/164 — tokens still available on the cross-slice allocation (`resourceSuspend.totalTokens`
2044
+ * minus everything earlier slices spent), or `undefined` when no total is set. */
2045
+ remainingTokens: number | undefined;
2046
+ /** This slice's EFFECTIVE token ceiling = `min(limits.maxTokens, remainingTokens)`. The exact mirror
2047
+ * of `maxCostMicroUsd`. `undefined` = no token ceiling; `0` is a real, exhausted ceiling. */
2048
+ maxTokensWindow: number | undefined;
2049
+ /** The axis that is over its ceiling right now, or `undefined` when neither is — a predicate that
2050
+ * ALSO carries the attribution, so no caller can set `budgetHit` without an axis. */
2051
+ overBudget: () => BudgetAxis | undefined;
2052
+ streamCancel: boolean;
2053
+ callOutputChars: number;
2054
+ lastStreamBudgetCheck: number;
2055
+ projectedOverBudget: () => BudgetAxis | undefined;
2056
+ };
2057
+ turn: {
2058
+ callStartAt: number | undefined;
2059
+ firstTokenAt: number | undefined;
2060
+ turnUsage: {
2061
+ inputTokens: number;
2062
+ totalInputTokens: number;
2063
+ outputTokens: number;
2064
+ cacheReadTokens: number;
2065
+ cacheWriteTokens: number;
2066
+ costMicroUsd: number;
2067
+ } | undefined;
2068
+ turnUsageMissing: boolean;
2069
+ turnStopReason: string | undefined;
2070
+ lastTurnHadToolCalls: boolean;
2071
+ /** B15 三期使能:design/134 §3.1b postToolBatch 的本回合聚集批(每回合边界消费并重置 — 回合语义,故归 turn)。 */
2072
+ toolBatch: PostToolBatchCall[];
2073
+ };
2074
+ counters: {
2075
+ /** design/164 — how many LIMIT-APPROACH frames have been injected this run (0..2). Also the
2076
+ * one-shot latch itself: each frame fires when the count is still below its index. */
2077
+ approachNoticesSent: number;
2078
+ /** B15 三期使能:R9 引用的 R3/压缩区 latch(walltime 收尾 latch — preemptIgnoredReported 同族)。 */
2079
+ walltimeSyncBackstopFired: boolean;
2080
+ /** B15 三期终件补遗:压缩地板/背压 latch — rs.counters.compactionFloor 跨 drainManualCompact(写)/R9(读写)/收尾(读)三方共享,rs.counters.trimForceBackoff 为 R9 跨调用 latch(同款只改绑定不改时机)。 */
2081
+ compactionFloor: number;
2082
+ trimForceBackoff: boolean;
2083
+ repetitionCuts: number;
2084
+ repetitionSpared: number;
2085
+ repetitionEvents: Array<{
2086
+ turn: number;
2087
+ action: "cut" | "spared";
2088
+ rule: "char-run" | "unit-loop";
2089
+ period: number;
2090
+ reps: number;
2091
+ segment: string;
2092
+ }>;
2093
+ REPETITION_EVENTS_CAP: number;
2094
+ preemptIgnoredReported: boolean;
2095
+ wroteThisRun: boolean;
2096
+ finalVerifyInjections: number;
2097
+ groundingSignalPreR9: boolean;
2098
+ groundingSignalPostR9: boolean;
2099
+ cadenceTurns: number;
2100
+ };
2101
+ attach: {
2102
+ attachmentsCfg: TaskSpec["attachments"];
2103
+ agentListingOn: boolean;
2104
+ skillsListingOn: boolean;
2105
+ attachState: ReturnType<typeof createAttachmentState> | undefined;
2106
+ dateState: {
2107
+ announcedDate: string;
2108
+ } | undefined;
2109
+ instrProbe: RunnerDeps["probeInstructionSources"];
2110
+ instrState: {
2111
+ lastAnnouncedHash: Map<string, string | null>;
2112
+ } | undefined;
2113
+ /** #20 lane — the Workflow tool card's advisory size guideline as last ANNOUNCED to the model,
2114
+ * plus the live boundary-time read (Prepared.workflowSizeGuideline). Same deliberate posture as
2115
+ * `dateState`/`instrState`: NOT an AttachmentState member, one field, zero reducers. */
2116
+ sizeGuidelineState: {
2117
+ announcedGuideline: WorkflowSizeGuideline;
2118
+ current: () => WorkflowSizeGuideline;
2119
+ } | undefined;
2120
+ /** B15 三期使能:boundary 注入计数(→ stats.mechanisms.attachmentsInjected;attachState 注入族语义,故归 attach)。 */
2121
+ attachmentsInjected: number;
2122
+ };
2123
+ }
2124
+ /** design/171 §6.3 parity, LIVE leg — one accepted live steer's REPLAY identity, the twin of the parked
2125
+ * queue's `samePendingSteerPayload`. `payload` is the exact string handed to the harness, so the text,
2126
+ * the speaker envelope and the trust framing all fold into it; `trusted` is kept SEPARATELY from those
2127
+ * framed bytes so a text that spells its own `<system-reminder>` wrapper can never read as the trusted
2128
+ * arm's byte-equal twin. `actor` carries the attribution the ledger frame reports. */
2129
+ export interface AcceptedSteerInput {
2130
+ payload: string;
2131
+ trusted: boolean;
2132
+ /** design/373 (R2-F3) — the NORMALIZED priority the call delivered under (absent input ⇒ "next"),
2133
+ * the replay identity's sixth axis: same id + same text at a DIFFERENT tier is not a replay —
2134
+ * a `next` input re-sent as `now` answering "idempotent success" without the interrupt would be
2135
+ * a disposition lie, so it refuses `duplicate_input_id` like any other identity mismatch. */
2136
+ priority: SystemInjectionPriority;
2137
+ actor?: ActorAssertion;
2138
+ }
2139
+ /**
2140
+ * Config re-supplied to {@link Runner.resume} (design/45). A suspended task's tools / model / policy /
2141
+ * hooks cannot be reconstructed from a checkpoint token (the session stores neither tool implementations
2142
+ * nor the hand band), so the caller's trusted control plane re-supplies the same {@link TaskSpec} it ran
2143
+ * with — minus the conversation bits: `sessionId` comes from the checkpoint and `objective` is replaced by
2144
+ * an internally-generated continuation, so both are omitted.
2145
+ */
2146
+ export type ResumeTaskConfig = Omit<TaskSpec, "objective" | "sessionId">;
2147
+ /** design/45 resume plan threaded from {@link Runner.resume} into the shared run loop. */
2148
+ export interface ResumeRun {
2149
+ cp: Checkpoint;
2150
+ /**
2151
+ * RB-152 (2026-07-25, 按面收口): did the APPROVED ACTION actually begin executing?
2152
+ *
2153
+ * The reopen compensation used to key on an error-code whitelist (`resume.env_failed` /
2154
+ * `resume.session_not_found` / `resume.tool_unavailable`). That is a proxy for the thing that actually
2155
+ * matters, and every time a NEW way to exit without running the action appeared, the whitelist did not
2156
+ * know about it: a caller's abort landing anywhere between the CAS and `tool.execute` — across
2157
+ * `SessionStore.acquire` (pluggable, cross-network on a durable backend), the MCP reconnect, the env
2158
+ * rebuild, `resumeVM`, tool materialization — produced a `failed` result with NO errorCode, no reopen,
2159
+ * and a human approval consumed for work that never happened. Two rounds of fixes (RB-77, RB-109) each
2160
+ * moved that window rather than closing it, because both extended the whitelist.
2161
+ *
2162
+ * This binds the compensation to the FACT instead: set the instant before `tool.execute` for the gated
2163
+ * call (the same `onExecuteStart` signal the orphan-reconcile split already trusts). Every terminal —
2164
+ * throw OR returned result — asks one question: was the checkpoint consumed while this stayed false?
2165
+ */
2166
+ pendingActionStarted?: boolean;
2167
+ /** Validated against `cp.gate.kind` at the resume entry: human/irreversible_ask→`policy_ask`,
2168
+ * resource_limit→`resource_limit` (design/74), needs_review→`dry_run_review` (design/76 §2.5),
2169
+ * plan_review→`plan_review` (design/80 D-B). The gate-match guard in `resumeStream` enforces the
2170
+ * correlation. design/144 §3: `wake` is the NON-GATE arm — only a checkpoint awaiting NO gate
2171
+ * decision passes the resume entry with it (gate purity, `wake.gate_pending`); the run loop skips
2172
+ * `applyResumeDecision` for it (no pending action to resolve) and re-enters via the continuation +
2173
+ * pendingSteer tail alone. */
2174
+ outcome: Extract<ResumeOutcome, {
2175
+ gate: "policy_ask" | "resource_limit" | "dry_run_review" | "plan_review" | "wake";
2176
+ }>;
2177
+ /** design/144 §3 (X5) — the wake's own operator message, validated (`validatePendingSteer`) at the
2178
+ * resume entry and carried SEPARATELY from the checkpoint's parked `pendingSteer`: a message-bearing
2179
+ * wake of a checkpoint that ALSO holds a parked steer must deliver BOTH (park order: parked first,
2180
+ * wake message second), each under its own trusted framing — the old merge-into-the-slot shape
2181
+ * silently DISPLACED the parked (undelivered) supervisor steer. Wake outcomes only. */
2182
+ wakeMessage?: Omit<PendingSteerEntry, "seq">;
2183
+ /** design/373 §4.3 (D2) — the userPromptSubmit screen's `additionalContext` for {@link wakeMessage},
2184
+ * captured at the resume ENTRY (the message is screened once, pre-CAS, on the resuming process's
2185
+ * hook) and delivered by the drain as the engine's own reminder AHEAD of the wake frame — carrying
2186
+ * it forward is what keeps the hook single-run (re-screening at the drain would be the double-run
2187
+ * §4.3-3 reserves for the cross-process parked leg). Present only when a wake message passed a
2188
+ * screen that supplied context. */
2189
+ wakeMessageHookContext?: string;
2190
+ /** Compensation hook (design/45/49): called iff the resumed run fails with `resume.env_failed` (post-CAS
2191
+ * workspace `resumeVM` failed) OR `resume.tool_unavailable` (P-7: the approved tool vanished) — in both
2192
+ * the CAS already consumed the checkpoint but the pending action never ran. `resumeStream` supplies a
2193
+ * closure that reopens the checkpoint (`resolved → pending`) so a retry re-resumes the SAME suspended work
2194
+ * instead of losing it to a forced "re-initiate". design/80 D-1 (reopen-by-reason): the `reason` is
2195
+ * recorded on the reopened row so the next re-resume validates per reason — an `env_failed` reopen must
2196
+ * replay the persisted winner (a system retry of the approved action), while a `tool_unavailable` reopen
2197
+ * lets a human re-decide with the tool present (a fresh decision is allowed — preserves P-7). */
2198
+ onEnvRestoreFailed?: (reason: ReopenReason) => Promise<void>;
2199
+ /** RB-471/FR-C1 — set in the run body right after `applyResumeDecision` completes: the negative-
2200
+ * decision twin of `pendingActionStarted`. A reject/deny consumes its gate BY BEING DELIVERED, and
2201
+ * this bit is the delivery fact — every throw-arm cause (prepare failure, `session_not_found`, a
2202
+ * pre-delivery abort) and the walltime-exhausted settle fire BEFORE it is set, so an undelivered
2203
+ * negative decision still reopens as `env_failed` (the retry replays the persisted decision; it
2204
+ * never re-asks — design/80 D-1), closing the RB-152/RB-70 loss class the first RB-471 cut reopened. */
2205
+ decisionDelivered?: boolean;
2206
+ }
2207
+ /**
2208
+ * What {@link TaskStream.destroy} needs to reap a SUSPENDED task (design/51 §4). Captured (stashed) onto
2209
+ * the stream handle the moment a run assembles `status:"suspended"` — `finish()` deliberately left the
2210
+ * paused env + committed checkpoint alive for resume, so without this stash they'd be unreachable (the
2211
+ * `Prepared`, and its `ownedEnv`, are GC'd once the run promise settles). `env` is `undefined` for a
2212
+ * process-local suspend (no remote env to reap — only the checkpoint is expired).
2213
+ */
2214
+ export interface SuspendReap {
2215
+ env: Prepared["ownedEnv"];
2216
+ token: CheckpointToken;
2217
+ scope: string;
2218
+ store: CheckpointStore;
2219
+ /** The suspended task's session — for onError correlation only (the `token` is a capability and must
2220
+ * NEVER be logged, checkpoint-store.ts §6). */
2221
+ sessionId: string;
2222
+ }
2223
+ /** B15 三期终件 — R9(回合边界全域:压缩/attachments 注入/提醒节律/预算判决)工厂。
2224
+ * 正文自 runLocked 逐字搬迁(this 面经 runnerHooks 闭包打包,B13 #coreView 同款);模块级符号
2225
+ * 同文件直见不经 deps;可变运行态全在 rs(B15 二期+使能车),deps 全部为 const 稳定引用。 */
2226
+ export interface TurnBoundaryDeps {
2227
+ spec: TaskSpec;
2228
+ queue: PushQueue<TaskEvent>;
2229
+ /** The run loop's own latch — read for `userHalted` (the public `TaskStream.halt()` verb, which sets
2230
+ * the harness stop latch directly, not through the ctx primitive `Prepared.stopRequestedRef`
2231
+ * mirrors): a halted boundary has no next request either, so the bundle is not collected there. */
2232
+ loopLatch: {
2233
+ readonly userHalted: boolean;
2234
+ };
2235
+ manualCompactRef: {
2236
+ requested: boolean;
2237
+ waiters: Array<{
2238
+ resolve: (outcome: CompactOutcome) => void;
2239
+ signal?: AbortSignal;
2240
+ instructions?: string;
2241
+ }>;
2242
+ };
2243
+ todoToolMounted: boolean;
2244
+ taskToolsMounted: boolean;
2245
+ walltimeMonotonicDeadline: number | undefined;
2246
+ timeout: {
2247
+ fired: boolean;
2248
+ latenessMs?: number;
2249
+ clear: () => void;
2250
+ };
2251
+ ident: () => {
2252
+ eventId: string;
2253
+ parentToolCallId?: string;
2254
+ sourceTaskId?: string;
2255
+ };
2256
+ postToolBatchHook: ((batch: PostToolBatchCall[], meta?: {
2257
+ injectedThisTurn: "final_verification" | "finalize";
2258
+ }, ctx?: import("../hooks.js").PostToolBatchContext) => PostToolBatchResult | undefined | Promise<PostToolBatchResult | undefined>) | undefined;
2259
+ batchArgs: Map<string, unknown> | undefined;
2260
+ compactionBrain: Brain;
2261
+ withinTaskCompaction: boolean;
2262
+ compactionBreaker: {
2263
+ failures: number;
2264
+ };
2265
+ windowSafetyOptions: (mainModel: Model) => Pick<MaybeCompactOptions, "fallbackBudget" | "onWindowSafety">;
2266
+ rapidRefill: RapidRefillState;
2267
+ drainManualCompact: (outcome: CompactOutcome) => void;
2268
+ runnerHooks: {
2269
+ onError: RunnerDeps["onError"];
2270
+ seamCCompactionOptions: (prepared: Prepared) => Pick<MaybeCompactOptions, "summaryProvider" | "onCompaction" | "maxConsecutiveProviderReuse" | "consecutiveProviderReuse"> | undefined;
2271
+ compactionHookOptions: (spec: TaskSpec, sessionId: string, trigger: "auto" | "manual" | "forced") => Pick<MaybeCompactOptions, "trigger" | "preCompact" | "postCompact">;
2272
+ recordCompactionReuse: (prepared: Prepared, comp: {
2273
+ compacted: boolean;
2274
+ reused?: boolean;
2275
+ }) => void;
2276
+ };
2277
+ }
2278
+ /** design/157 B15 尾件 — R5(harness 事件处理器族)的依赖包。全部为 runLocked 内声明顺序早于
2279
+ * 工厂调用点的 const 稳定引用 + 三个 runLocked 形参;this 面(deps.onError)经 runnerHooks 打包
2280
+ * (B13 #coreView / R9 TurnBoundaryDeps 同款)。*/
2281
+ export interface HarnessHandlersDeps {
2282
+ spec: TaskSpec;
2283
+ queue: PushQueue<TaskEvent>;
2284
+ internals: RunInternals | undefined;
2285
+ ident: () => {
2286
+ eventId: string;
2287
+ parentToolCallId?: string;
2288
+ sourceTaskId?: string;
2289
+ };
2290
+ parentToolCallId: string | undefined;
2291
+ subagentName: string | undefined;
2292
+ pushContent: (e: TaskEvent) => void;
2293
+ emitCommitted: (entryId: string, role: "user" | "assistant" | "toolResult", toolCallId?: string) => void;
2294
+ startedToolCallIds: Set<string>;
2295
+ toolStartAt: Map<string, number>;
2296
+ writeFamilyOf: (name: string) => WriteFamily | undefined;
2297
+ toolLabels: Map<string, string>;
2298
+ postToolBatchHook: ((batch: PostToolBatchCall[], meta?: {
2299
+ injectedThisTurn: "final_verification" | "finalize";
2300
+ }, ctx?: import("../hooks.js").PostToolBatchContext) => PostToolBatchResult | undefined | Promise<PostToolBatchResult | undefined>) | undefined;
2301
+ batchArgs: Map<string, unknown> | undefined;
2302
+ runnerHooks: {
2303
+ onError: RunnerDeps["onError"];
2304
+ };
2305
+ }
2306
+ /**
2307
+ * The Runner as the modules it drives and the compositions over it see it — the delegation tools
2308
+ * (Agent / SendMessage / AgentTranscript), the repair, verify, cascade, teacher and team loops, the
2309
+ * workflow and goal orchestrations, the scenario registry, the HTTP server, `warmResume`, and the
2310
+ * orchestrator's own trusted `runnerSelf` seat. Every one of them used to name the `Runner` CLASS
2311
+ * through `import type`, which is a type edge from below the runner back up into it — a cycle the
2312
+ * direction gate could only carry as an exemption, and one that reached the orchestrator through the
2313
+ * delegation tools (prepare-task → send-message-tool → runtask).
2314
+ *
2315
+ * THE MEMBER SET IS MEASURED, NOT DESIGNED: it is exactly the union of what those consumers read off a
2316
+ * Runner today (a TS-Program census over `src/`, pinned in test/runner-self-seat.test.ts so a member
2317
+ * nobody reads reds as dead weight and a consumer reaching for a member outside it reds as a widening).
2318
+ * `swapModels`, `sideQuery` and the constructor are NOT here — no consumer reaches them; a host that
2319
+ * needs them holds the class. The class implements this interface, so the two cannot drift apart.
2320
+ *
2321
+ * Signatures are spelled out (not `Pick<Runner, …>`), because naming the class from here would be the
2322
+ * very edge this seat exists to remove.
2323
+ */
2324
+ export interface RunnerSelfSeat {
2325
+ /** The session store this Runner was constructed over — the delegation tools acquire / release / fork through it. */
2326
+ readonly sessions: SessionStore;
2327
+ /** The deployment agent + model catalog, READ-ONLY (a fresh copy per read; the definitions are the shared objects). */
2328
+ readonly agentCatalog: {
2329
+ agents?: AgentDefinition[];
2330
+ builtinAgents?: boolean;
2331
+ models?: Record<string, Model>;
2332
+ };
2333
+ /** The deployment-level gating baseline (deps toolPolicy / hooks) as delegate wrappers — for assemblers that compose with it. */
2334
+ readonly gateBaseline: {
2335
+ toolPolicy?: RunnerDeps["toolPolicy"];
2336
+ hooks?: RunnerDeps["hooks"];
2337
+ };
2338
+ /** Fire `RunnerDeps.onTaskOutcome` through the swallow-guarded chokepoint (thin compositions emit at their terminal state). */
2339
+ emitTaskOutcome(outcome: TaskOutcome): void;
2340
+ /** Run a task to completion. `internals` is the TRUSTED run-scoped channel (never a TaskSpec field). */
2341
+ runTask(spec: TaskSpec, internals?: RunInternals): Promise<TaskResult>;
2342
+ /** Streaming form of {@link RunnerSelfSeat.runTask}; `resume` is the run loop's resume plan, `internals` the trusted channel. */
2343
+ runTaskStream(spec: TaskSpec, resume?: ResumeRun, internals?: RunInternals): TaskStream;
2344
+ /** Resume a suspended task from its checkpoint token with the caller's decision, to completion. */
2345
+ resume(token: CheckpointToken, outcome: ResumeOutcome, taskConfig: ResumeTaskConfig, internals?: RunInternals): Promise<TaskResult>;
2346
+ /** Streaming form of {@link RunnerSelfSeat.resume}: the pre-CAS guards and the CAS run first, then the live stream is returned. */
2347
+ resumeStream(token: CheckpointToken, outcome: ResumeOutcome, taskConfig: ResumeTaskConfig, internals?: RunInternals): Promise<TaskStream>;
2348
+ }
@@ -148,7 +148,7 @@ export declare function judgeInheritedClassifier<A extends InheritedAsk>(opts: {
148
148
  subject: "this call" | "the approved edit";
149
149
  }): Promise<InheritedClassifierJudgment<A>>;
150
150
  /** The resolver's answer as the settlement reads it. */
151
- export type FallbackResolution = Pick<ResolvedAsk, "action" | "resolution" | "approverUnavailable">;
151
+ export type FallbackResolution = Pick<ResolvedAsk, "action" | "settlement" | "approverUnavailable">;
152
152
  /** A deny nobody made, at a wrapper arm: no approver wired, or a blanket `onAsk:"allow"` refused as
153
153
  * no approver. `approver_unavailable` is NOT headless here — it floats to the main gate, which owns
154
154
  * that arm (a park, or the fail-closed refusal). */
@@ -53,7 +53,7 @@ export function createDenialLimitStop(opts) {
53
53
  };
54
54
  return { gateStopRef, stopForDenialLimit };
55
55
  }
56
- const INHERITED_STATION_FACTS = { contentQuestion: false, markedUnresolvable: false, org: undefined };
56
+ const INHERITED_STATION_FACTS = { contentQuestion: false, markedUnresolvable: false, org: undefined, tightened: undefined };
57
57
  function fallbackCarry(fallback, liveApprover, tracker) {
58
58
  if (fallback === undefined)
59
59
  return {};
@@ -97,10 +97,10 @@ export async function judgeInheritedClassifier(opts) {
97
97
  return { kind: "resolve", ask: minted, fallback, mintedHere: true, origin: askOriginOf(minted, INHERITED_STATION_FACTS) };
98
98
  }
99
99
  export function headlessDenyAtFold(r) {
100
- return r.resolution === "no_approver" || r.resolution === "blanket_allow_refused";
100
+ return r.settlement?.kind === "no_approver" || r.settlement?.kind === "blanket_allow_refused";
101
101
  }
102
102
  export function headlessDenyAtRecheck(r) {
103
- return headlessDenyAtFold(r) || r.resolution === "approver_unavailable" || r.approverUnavailable === true;
103
+ return headlessDenyAtFold(r) || r.settlement?.kind === "approver_unavailable" || r.approverUnavailable === true;
104
104
  }
105
105
  export function settleDenialLimitFallback(opts) {
106
106
  const { fallback, resolved } = opts;