@sema-agent/core 7.37.1 → 7.38.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 (155) hide show
  1. package/CHANGELOG.md +168 -0
  2. package/dist/agents/observer.js +1 -1
  3. package/dist/agents/peer-notices.js +4 -4
  4. package/dist/agents/subagent.js +1 -1
  5. package/dist/agents/team-role.js +2 -2
  6. package/dist/brain/anthropic.js +10 -9
  7. package/dist/brain/errors.d.ts +26 -0
  8. package/dist/brain/errors.js +34 -0
  9. package/dist/brain/input-too-long.d.ts +0 -31
  10. package/dist/brain/input-too-long.js +1 -18
  11. package/dist/brain/open-responses.js +10 -9
  12. package/dist/brain/openai.js +10 -9
  13. package/dist/brain/status-sink.d.ts +1 -1
  14. package/dist/brain/stream-engine.js +10 -2
  15. package/dist/brain/stream-shared.d.ts +11 -3
  16. package/dist/brain/stream-shared.js +4 -2
  17. package/dist/brain/tool-call-id.d.ts +29 -8
  18. package/dist/brain/tool-call-id.js +36 -6
  19. package/dist/brain/tool-call-repair.js +1 -1
  20. package/dist/core/a2a-task-state.js +2 -2
  21. package/dist/core/artifact-host.js +6 -6
  22. package/dist/core/ask-origin.js +6 -6
  23. package/dist/core/ask-unresolvable-notice.js +1 -1
  24. package/dist/core/auto-mode.js +4 -4
  25. package/dist/core/background-agent-store.js +2 -2
  26. package/dist/core/checkpoint-execution-record.js +1 -1
  27. package/dist/core/checkpoint-store.d.ts +2 -2
  28. package/dist/core/checkpoint-store.js +2 -2
  29. package/dist/core/child-clamp-seats.js +3 -3
  30. package/dist/core/compliance.js +1 -1
  31. package/dist/core/context-edit.js +1 -1
  32. package/dist/core/context-guard.d.ts +0 -19
  33. package/dist/core/context-guard.js +0 -38
  34. package/dist/core/decision-receipt.d.ts +27 -1
  35. package/dist/core/decision-receipt.js +176 -1
  36. package/dist/core/engine-notice.d.ts +60 -8
  37. package/dist/core/engine-notice.js +2 -1
  38. package/dist/core/gate-fold.js +3 -2
  39. package/dist/core/gate-outcome.js +4 -4
  40. package/dist/core/governance-codes.d.ts +221 -13
  41. package/dist/core/governance-codes.js +104 -4
  42. package/dist/core/hook-seats.js +2 -2
  43. package/dist/core/hooks.d.ts +21 -2
  44. package/dist/core/hooks.js +4 -1
  45. package/dist/core/mailbox-store.js +3 -3
  46. package/dist/core/mcp-failure.js +4 -4
  47. package/dist/core/mcp-injection-drop.js +1 -1
  48. package/dist/core/memory-engine/consolidation.js +1 -1
  49. package/dist/core/memory-engine/delegation-settlement.d.ts +8 -0
  50. package/dist/core/memory-engine/delegation-settlement.js +7 -0
  51. package/dist/core/memory-engine/engine.d.ts +12 -2
  52. package/dist/core/memory-engine/engine.js +10 -6
  53. package/dist/core/memory-engine/export-bundle.js +1 -1
  54. package/dist/core/memory-engine/layout.d.ts +6 -6
  55. package/dist/core/memory-engine/layout.js +6 -8
  56. package/dist/core/memory-engine/session-write-home.d.ts +30 -0
  57. package/dist/core/memory-engine/session-write-home.js +34 -0
  58. package/dist/core/memory-engine/tools.js +1 -1
  59. package/dist/core/memory-engine/types.js +1 -1
  60. package/dist/core/memory.js +1 -1
  61. package/dist/core/mode-policy.d.ts +10 -1
  62. package/dist/core/mode-policy.js +15 -7
  63. package/dist/core/model-seat.js +1 -1
  64. package/dist/core/permission-rule-model.js +3 -3
  65. package/dist/core/permission-rule-store.js +2 -2
  66. package/dist/core/read-only-shell-table.js +8 -8
  67. package/dist/core/retention.js +2 -2
  68. package/dist/core/runner/assemble-result.js +15 -5
  69. package/dist/core/runner/classifier-ladder.js +1 -1
  70. package/dist/core/runner/denial-limit-arms.d.ts +2 -0
  71. package/dist/core/runner/denial-limit-arms.js +6 -2
  72. package/dist/core/runner/inherited-ask-grants.d.ts +6 -5
  73. package/dist/core/runner/inherited-ask-grants.js +3 -2
  74. package/dist/core/runner/prepare-artifact.d.ts +3 -2
  75. package/dist/core/runner/prepare-context-lane.d.ts +19 -15
  76. package/dist/core/runner/prepare-context-lane.js +11 -15
  77. package/dist/core/runner/prepare-gate-stations.js +5 -5
  78. package/dist/core/runner/prepare-hands-readface.d.ts +4 -3
  79. package/dist/core/runner/prepare-hands-readface.js +1 -0
  80. package/dist/core/runner/prepare-inherited-gate.js +1 -1
  81. package/dist/core/runner/prepare-policy-chain.js +10 -10
  82. package/dist/core/runner/prepare-turn-wiring.d.ts +2 -2
  83. package/dist/core/runner/run-harness-handlers.js +3 -0
  84. package/dist/core/runner/run-identity-wiring.d.ts +1 -1
  85. package/dist/core/runner/run-identity-wiring.js +0 -2
  86. package/dist/core/runner/run-leg.js +2 -2
  87. package/dist/core/runner/run-stop-and-final-verify.js +1 -0
  88. package/dist/core/runner/run-turn-boundary.js +1 -1
  89. package/dist/core/runner/session-mode-seats.d.ts +11 -3
  90. package/dist/core/runner/session-mode-seats.js +6 -0
  91. package/dist/core/runner/session-rule-policy.js +2 -3
  92. package/dist/core/runner/stream-steer-verb.js +1 -1
  93. package/dist/core/runner/tool-call-id-fold-seat.js +16 -9
  94. package/dist/core/runner/tool-turn-repair-seat.d.ts +11 -1
  95. package/dist/core/runner/tool-turn-repair-seat.js +35 -0
  96. package/dist/core/runner/turn-attachments.js +1 -1
  97. package/dist/core/runtime-caps.js +1 -1
  98. package/dist/core/safety-axis-vocab.d.ts +5 -0
  99. package/dist/core/safety-axis-vocab.js +5 -0
  100. package/dist/core/session-reconcile.d.ts +9 -7
  101. package/dist/core/session-reconcile.js +21 -1
  102. package/dist/core/shared-memory/types.js +1 -1
  103. package/dist/core/side-query.d.ts +6 -1
  104. package/dist/core/store-contracts/read-back-shape-contract.js +1 -1
  105. package/dist/core/swappable-deps.js +1 -1
  106. package/dist/core/task-notification.js +4 -4
  107. package/dist/core/task-registry-shared.js +1 -1
  108. package/dist/core/terminal-cause.d.ts +19 -0
  109. package/dist/core/tool-face.js +6 -6
  110. package/dist/core/tool-policy.d.ts +17 -11
  111. package/dist/core/tool-policy.js +38 -72
  112. package/dist/core/tool-roster.js +1 -1
  113. package/dist/core/tool-search-setting.js +1 -1
  114. package/dist/core/trace.d.ts +5 -4
  115. package/dist/core/types.d.ts +2 -2
  116. package/dist/core/types.js +1 -1
  117. package/dist/core/wiring-manifest.js +2 -2
  118. package/dist/core/write-protect.js +1 -1
  119. package/dist/engine/harness/agent-harness.js +10 -6
  120. package/dist/engine/llm/tool-turns.d.ts +93 -4
  121. package/dist/engine/llm/tool-turns.js +63 -9
  122. package/dist/engine/llm/types.d.ts +11 -0
  123. package/dist/index.d.ts +5 -3
  124. package/dist/index.js +4 -2
  125. package/dist/internal/llm.d.ts +2 -2
  126. package/dist/internal/llm.js +1 -1
  127. package/dist/orchestration/workflow-governance.js +2 -2
  128. package/dist/orchestration/workflow-types.js +2 -2
  129. package/dist/shell/lex.js +7 -7
  130. package/dist/tools/fs/bash-program-position.js +2 -2
  131. package/dist/tools/fs/fs-shared.d.ts +2 -1
  132. package/dist/tools/fs/fs-shared.js +1 -1
  133. package/dist/tools/fs/index.d.ts +13 -6
  134. package/dist/tools/fs/index.js +12 -4
  135. package/dist/tools/fs/read-deny-home.d.ts +98 -45
  136. package/dist/tools/fs/read-deny-home.js +294 -117
  137. package/dist/tools/fs/read-deny.d.ts +2 -2
  138. package/dist/tools/fs/read-deny.js +2 -2
  139. package/dist/tools/fs/safety.d.ts +2 -0
  140. package/dist/tools/fs/safety.js +2 -2
  141. package/dist/tools/fs/search.d.ts +4 -3
  142. package/dist/tools/fs/search.js +3 -3
  143. package/dist/tools/fs/shell-read-check.d.ts +9 -0
  144. package/dist/tools/fs/shell-read-check.js +3 -2
  145. package/dist/tools/fs/shell-read-establish.d.ts +3 -0
  146. package/dist/tools/fs/shell-read-establish.js +1 -1
  147. package/dist/tools/fs/shell-read-identity.d.ts +4 -3
  148. package/dist/tools/fs/shell-read-identity.js +2 -2
  149. package/dist/tools/fs/shell-read-judge.js +2 -2
  150. package/dist/tools/fs/shell-read-plan.js +2 -2
  151. package/dist/tools/fs/shell-read-write-targets.js +1 -1
  152. package/package.json +1 -1
  153. package/test/export-surface.snapshot.json +17 -1
  154. package/wire-changes/core@7.38.0.json +664 -0
  155. package/wire-changes/core@7.38.1.json +56 -0
@@ -1,3 +1,4 @@
1
+ import type { AssertAllKeysHandled } from "./ask-origin.js";
1
2
  /** Retry class of a governance code: `transient` = a later retry can self-heal (control-plane
2
3
  * absence/outage); `terminal` = policy adjudicated, re-issuing changes nothing. */
3
4
  export type GovernanceRetryClass = "transient" | "terminal";
@@ -118,28 +119,131 @@ export type RuleQuarantineReason = Exclude<RuleSyncDropReason, "own_actor_forged
118
119
  */
119
120
  export type NoticeAudience = "user" | "operator";
120
121
  /**
121
- * The CLOSED catalog of EngineNotice codes this engine mints — the enumeration half of the
122
- * presentation registry below, and the thing a consumer's own audience table can be diffed against
123
- * (that is the point of exporting it: a downstream table with a row missing, or a row for a code
124
- * this engine no longer mints, is a mechanically detectable disagreement rather than an argument).
122
+ * The CLOSED catalog of EngineNotice codes — every code this engine mints, plus the few a server mints through the
123
+ * same sink ({@link NOTICE_MINTER}) — the enumeration half of the presentation registry below, and the thing a
124
+ * consumer's own audience table can be diffed against (that is the point of exporting it: a downstream table with a
125
+ * row missing, or a row for a code no longer minted, is a mechanically detectable disagreement rather than an
126
+ * argument).
125
127
  *
126
- * Kept in LOCKSTEP with {@link NOTICE_AUDIENCE} by the compiler (`satisfies Record<EngineNoticeCode,
127
- * …>` below: a catalog entry with no audience row, or an audience row for a non-member, is a tsc
128
- * error — a mirrored enumeration that is only checked at runtime is a mirror that drifts). The
129
- * other half — "is the catalog itself still complete?" — cannot be a type: `EngineNotice.code` is
130
- * declared `string` (a host may forward its own), so the gate test (governance-codes.test.ts) scans
131
- * src/ for notice mint shapes and names any code that is minted but unregistered, or registered but
132
- * no longer minted.
128
+ * Kept in LOCKSTEP with {@link NOTICE_AUDIENCE} and {@link NOTICE_MINTER} by the compiler (`satisfies
129
+ * Record<EngineNoticeCode, …>` below: a catalog entry with no row, or a row for a non-member, is a tsc error — a
130
+ * mirrored enumeration that is only checked at runtime is a mirror that drifts). The other half — "is the catalog
131
+ * itself still complete?" — cannot be a type: `EngineNotice.code` is declared `string` (a host may forward its own),
132
+ * so the gate test (governance-codes.test.ts) scans src/ for notice mint shapes and names any code that is minted but
133
+ * unregistered, an engine-minted code that is no longer minted, or a server-minted code minted here.
133
134
  *
134
135
  * Each row's own story — what fires it, its `detail` shape, its dedup unit and its audience — lives at
135
136
  * the union's catalog ({@link EngineNotice.code}). The groups below say only which family a row joins,
136
137
  * so the catalog is not written a second time here and the two cannot drift.
137
138
  */
138
- export declare const ENGINE_NOTICE_CODES: readonly ["config.autocompact_window_clamped", "config.env_timeout_discarded", "config.materialize_env_discarded", "config.models_swapped", "config.context_settings_swapped", "config.read_face_swapped", "config.read_face_deployment_clamped", "config.write_face_swapped", "config.write_face_deployment_clamped", "config.tool_model_gate_removed", "config.tool_model_gate_unknown_class", "config.tool_model_gate_env_invalid", "config.artifact_host_invalid", "config.task_root_not_canonical", "config.write_protection_target_view_absent", "config.write_protection_unresolved", "config.execution_env_capability_invalid", "config.tool_card_undeclared", "config.tool_face_undeclared", "config.tool_face_invalid", "config.forward_sink_failed", "config.durable_gate_unavailable", "config.peer_admission_out_of_range", "config.peer_lane_unmounted", "config.secret_env_scrubbed", "config.permission_mode_unavailable", "permission.ask_released_by_mode", "permission.read_deny_unapplied", "permission.rule_store_unavailable", "resume.legacy_mode_seed_migrated", "peer.inbound_disposition", "peer.held_settled", "peer.idle_subscription", "classifier.denial_limit", "classifier.fallback", "classifier.role_defaulted", "classifier.contract_broken", "classifier.unavailable", "checkpoint.execution_outcome_unrecorded", "delegation.transcript_integrity", "delegation.ask_unresolvable", "mcp.injection_dropped", "mcp.revocation_probe_failed", "mcp.server_redialed", "workflow.governance_key_stripped", "workflow.agent_option_ignored", "memory.session_polluted", "memory.harvest_quarantined", "memory.delegation_static_mark_waived", "memory.content_class_declared", "memory.hold_opened", "memory.hold_released", "memory.hold_disposed", "memory.consolidation_recommended", "memory.consolidation_committed", "memory.consolidation_conflict", "memory.consolidation_incomplete", "memory.consolidation_refused", "memory.consolidation_withheld", "memory.restore_withheld", "memory.project_scope_absent", "route.fallback_to_primary", "route.classifier_unroutable", "route.base_url_changed_key_unchanged", "route.response_model_mismatch", "route.tool_call_id_folded", "context.tool_turn_repaired", "task.user_steer_undrained", "task.user_followup_undrained", "steering.parked_input_blocked", "task.turn_interrupted", "task.halt_unconsumed", "task.late_approval", "task.interrupt_unconsumed", "approval.read_root_granted", "approval.read_root_grant_rejected", "memory.capture_opted_out", "memory.capture_optout_unpersisted", "skills.listing_truncated", "agents.listing_truncated", "tool_result.offload_put_failed"];
139
- /** A code this engine mints (see {@link ENGINE_NOTICE_CODES}). NOT the type of
139
+ export declare const ENGINE_NOTICE_CODES: readonly ["config.autocompact_window_clamped", "config.env_timeout_discarded", "config.materialize_env_discarded", "config.models_swapped", "config.context_settings_swapped", "config.read_face_swapped", "config.read_face_deployment_clamped", "config.write_face_swapped", "config.write_face_deployment_clamped", "config.tool_model_gate_removed", "config.tool_model_gate_unknown_class", "config.tool_model_gate_env_invalid", "config.artifact_host_invalid", "config.task_root_not_canonical", "config.write_protection_target_view_absent", "config.write_protection_unresolved", "config.execution_env_capability_invalid", "config.tool_card_undeclared", "config.tool_face_undeclared", "config.tool_face_invalid", "config.forward_sink_failed", "config.durable_gate_unavailable", "config.peer_admission_out_of_range", "config.peer_lane_unmounted", "config.secret_env_scrubbed", "config.permission_mode_unavailable", "permission.ask_released_by_mode", "permission.read_deny_unapplied", "permission.rule_store_unavailable", "resume.legacy_mode_seed_migrated", "peer.inbound_disposition", "peer.held_settled", "peer.idle_subscription", "classifier.denial_limit", "classifier.fallback", "classifier.role_defaulted", "classifier.contract_broken", "classifier.unavailable", "checkpoint.execution_outcome_unrecorded", "delegation.transcript_integrity", "delegation.ask_unresolvable", "mcp.injection_dropped", "mcp.revocation_probe_failed", "mcp.server_redialed", "workflow.governance_key_stripped", "workflow.agent_option_ignored", "memory.session_polluted", "memory.harvest_quarantined", "memory.delegation_static_mark_waived", "memory.content_class_declared", "memory.hold_opened", "memory.hold_released", "memory.hold_disposed", "memory.consolidation_recommended", "memory.consolidation_committed", "memory.consolidation_conflict", "memory.consolidation_incomplete", "memory.consolidation_refused", "memory.consolidation_withheld", "memory.restore_withheld", "memory.project_scope_absent", "route.fallback_to_primary", "route.classifier_unroutable", "route.base_url_changed_key_unchanged", "route.response_model_mismatch", "route.tool_call_id_folded", "context.tool_turn_repaired", "task.user_steer_undrained", "task.user_followup_undrained", "steering.parked_input_blocked", "task.turn_interrupted", "task.halt_unconsumed", "task.late_approval", "task.interrupt_unconsumed", "task.tool_turn_incomplete", "approval.read_root_granted", "approval.read_root_grant_rejected", "memory.capture_opted_out", "memory.capture_optout_unpersisted", "skills.listing_truncated", "agents.listing_truncated", "tool_result.offload_put_failed", "instructions.source_changed", "session.records_skipped", "memory.layer_locked_legacy", "server.model_unreachable"];
140
+ /** A catalogued code (see {@link ENGINE_NOTICE_CODES}); {@link NOTICE_MINTER} says who mints it. NOT the type of
140
141
  * `EngineNotice.code`, which stays `string` — a host forwarding its own notices through the same
141
142
  * sink is a supported shape, and narrowing that field would break it. */
142
143
  export type EngineNoticeCode = (typeof ENGINE_NOTICE_CODES)[number];
144
+ /**
145
+ * The presentation-tier registry (the server's wire whitelist retires into this table once
146
+ * shipped): one row per {@link ENGINE_NOTICE_CODES} member. Presentation tier is a property of the
147
+ * CODE (closed set, one code one tier) — never of an individual emission, which is why this is a
148
+ * registry and not an EngineNotice field. Codes absent from the table (a host's own code forwarded
149
+ * through the same sink) read as `"operator"`: the conservative default, never push an unclassified
150
+ * code at an end user. Orthogonal to {@link NON_GOVERNANCE_MEMORY_CODES} (a retry-semantics table,
151
+ * not presentation).
152
+ *
153
+ * The judgment applied per row: WHO IS ENTITLED to the fact, not who could be shown it. A notice
154
+ * that a session can be attributed to is still an operator's fact when the person who would act on
155
+ * it runs the deployment — three rows below are exactly that case and say so.
156
+ */
157
+ declare const NOTICE_AUDIENCE_TABLE: {
158
+ readonly "memory.session_polluted": "user";
159
+ readonly "memory.harvest_quarantined": "user";
160
+ readonly "memory.delegation_static_mark_waived": "user";
161
+ readonly "memory.hold_opened": "user";
162
+ readonly "memory.hold_released": "user";
163
+ readonly "memory.hold_disposed": "user";
164
+ readonly "task.user_steer_undrained": "user";
165
+ readonly "task.user_followup_undrained": "user";
166
+ readonly "classifier.denial_limit": "user";
167
+ readonly "classifier.unavailable": "user";
168
+ readonly "classifier.fallback": "operator";
169
+ readonly "classifier.role_defaulted": "operator";
170
+ readonly "classifier.contract_broken": "operator";
171
+ readonly "task.turn_interrupted": "user";
172
+ readonly "steering.parked_input_blocked": "user";
173
+ readonly "task.halt_unconsumed": "user";
174
+ readonly "task.late_approval": "user";
175
+ readonly "task.interrupt_unconsumed": "user";
176
+ readonly "task.tool_turn_incomplete": "user";
177
+ readonly "approval.read_root_granted": "user";
178
+ readonly "approval.read_root_grant_rejected": "user";
179
+ readonly "config.durable_gate_unavailable": "user";
180
+ readonly "config.permission_mode_unavailable": "user";
181
+ readonly "permission.ask_released_by_mode": "user";
182
+ readonly "permission.read_deny_unapplied": "user";
183
+ readonly "permission.rule_store_unavailable": "user";
184
+ readonly "memory.capture_opted_out": "user";
185
+ readonly "memory.capture_optout_unpersisted": "user";
186
+ readonly "memory.consolidation_withheld": "operator";
187
+ readonly "delegation.ask_unresolvable": "user";
188
+ readonly "config.autocompact_window_clamped": "operator";
189
+ readonly "resume.legacy_mode_seed_migrated": "operator";
190
+ readonly "config.env_timeout_discarded": "operator";
191
+ readonly "config.materialize_env_discarded": "operator";
192
+ readonly "config.models_swapped": "operator";
193
+ readonly "config.context_settings_swapped": "operator";
194
+ readonly "config.read_face_swapped": "operator";
195
+ readonly "config.read_face_deployment_clamped": "operator";
196
+ readonly "config.write_face_swapped": "operator";
197
+ readonly "config.write_face_deployment_clamped": "operator";
198
+ readonly "config.tool_model_gate_removed": "operator";
199
+ readonly "config.tool_model_gate_unknown_class": "operator";
200
+ readonly "config.tool_model_gate_env_invalid": "operator";
201
+ readonly "config.artifact_host_invalid": "operator";
202
+ readonly "config.task_root_not_canonical": "operator";
203
+ readonly "config.write_protection_target_view_absent": "operator";
204
+ readonly "config.write_protection_unresolved": "operator";
205
+ readonly "config.execution_env_capability_invalid": "operator";
206
+ readonly "config.tool_card_undeclared": "operator";
207
+ readonly "config.tool_face_undeclared": "operator";
208
+ readonly "config.tool_face_invalid": "operator";
209
+ readonly "config.peer_lane_unmounted": "operator";
210
+ readonly "config.peer_admission_out_of_range": "operator";
211
+ readonly "config.secret_env_scrubbed": "operator";
212
+ readonly "peer.inbound_disposition": "user";
213
+ readonly "peer.held_settled": "user";
214
+ readonly "peer.idle_subscription": "user";
215
+ readonly "delegation.transcript_integrity": "operator";
216
+ readonly "checkpoint.execution_outcome_unrecorded": "operator";
217
+ readonly "mcp.injection_dropped": "user";
218
+ readonly "mcp.revocation_probe_failed": "operator";
219
+ readonly "mcp.server_redialed": "operator";
220
+ readonly "workflow.governance_key_stripped": "operator";
221
+ readonly "workflow.agent_option_ignored": "operator";
222
+ readonly "memory.content_class_declared": "operator";
223
+ readonly "memory.consolidation_recommended": "operator";
224
+ readonly "memory.consolidation_committed": "operator";
225
+ readonly "memory.consolidation_conflict": "operator";
226
+ readonly "memory.consolidation_incomplete": "operator";
227
+ readonly "memory.consolidation_refused": "operator";
228
+ readonly "memory.restore_withheld": "operator";
229
+ readonly "memory.project_scope_absent": "operator";
230
+ readonly "route.fallback_to_primary": "operator";
231
+ readonly "route.classifier_unroutable": "operator";
232
+ readonly "route.base_url_changed_key_unchanged": "operator";
233
+ readonly "route.response_model_mismatch": "operator";
234
+ readonly "route.tool_call_id_folded": "operator";
235
+ readonly "context.tool_turn_repaired": "operator";
236
+ readonly "skills.listing_truncated": "operator";
237
+ readonly "agents.listing_truncated": "operator";
238
+ readonly "tool_result.offload_put_failed": "operator";
239
+ readonly "config.forward_sink_failed": "operator";
240
+ readonly "instructions.source_changed": "user";
241
+ readonly "session.records_skipped": "user";
242
+ readonly "server.model_unreachable": "user";
243
+ readonly "memory.layer_locked_legacy": "operator";
244
+ };
245
+ /** Compile-time fence: every catalogued code has an audience row (and, via `satisfies`, only those). */
246
+ export type AudienceTableCoversEveryNoticeCode = AssertAllKeysHandled<Exclude<EngineNoticeCode, keyof typeof NOTICE_AUDIENCE_TABLE>>;
143
247
  /**
144
248
  * The presentation-tier registry — see {@link NOTICE_AUDIENCE_TABLE} for the per-row judgment.
145
249
  * The exported TYPE stays the open `Record<string, …>` on purpose: a consumer indexing it with a
@@ -149,3 +253,107 @@ export type EngineNoticeCode = (typeof ENGINE_NOTICE_CODES)[number];
149
253
  export declare const NOTICE_AUDIENCE: Readonly<Record<string, NoticeAudience>>;
150
254
  /** The audience for `code` — table lookup with the conservative `"operator"` default. */
151
255
  export declare function noticeAudienceOf(code: string): NoticeAudience;
256
+ /**
257
+ * WHO MINTS a catalogued code: `"engine"` = this package (the catalog gate finds its mint site in src/); `"server"` = a
258
+ * server process that forwards its own notices through the same sink — registered here so its audience comes from
259
+ * {@link NOTICE_AUDIENCE} like every other row, and NEVER minted by this package (the catalog gate reds a mint site for
260
+ * one in src/). One code, one minter: a code two processes mint would be two facts under one spelling.
261
+ *
262
+ * Member-level exception, stated where a consumer reads the minter: `task.user_steer_undrained` is an engine row, and
263
+ * its `detail.cause: "crashed"` arm is minted by the server — the engine's process is gone when that loss is counted
264
+ * (see `USER_STEER_UNDRAINED_CAUSES`). The code keeps one row here and one spelling for both arms.
265
+ */
266
+ export type NoticeMinter = "engine" | "server";
267
+ declare const NOTICE_MINTER_TABLE: {
268
+ readonly "config.autocompact_window_clamped": "engine";
269
+ readonly "config.env_timeout_discarded": "engine";
270
+ readonly "config.materialize_env_discarded": "engine";
271
+ readonly "config.models_swapped": "engine";
272
+ readonly "config.context_settings_swapped": "engine";
273
+ readonly "config.read_face_swapped": "engine";
274
+ readonly "config.read_face_deployment_clamped": "engine";
275
+ readonly "config.write_face_swapped": "engine";
276
+ readonly "config.write_face_deployment_clamped": "engine";
277
+ readonly "config.tool_model_gate_removed": "engine";
278
+ readonly "config.tool_model_gate_unknown_class": "engine";
279
+ readonly "config.tool_model_gate_env_invalid": "engine";
280
+ readonly "config.artifact_host_invalid": "engine";
281
+ readonly "config.task_root_not_canonical": "engine";
282
+ readonly "config.write_protection_target_view_absent": "engine";
283
+ readonly "config.write_protection_unresolved": "engine";
284
+ readonly "config.execution_env_capability_invalid": "engine";
285
+ readonly "config.tool_card_undeclared": "engine";
286
+ readonly "config.tool_face_undeclared": "engine";
287
+ readonly "config.tool_face_invalid": "engine";
288
+ readonly "config.forward_sink_failed": "engine";
289
+ readonly "config.durable_gate_unavailable": "engine";
290
+ readonly "config.peer_admission_out_of_range": "engine";
291
+ readonly "config.peer_lane_unmounted": "engine";
292
+ readonly "config.secret_env_scrubbed": "engine";
293
+ readonly "config.permission_mode_unavailable": "engine";
294
+ readonly "permission.ask_released_by_mode": "engine";
295
+ readonly "permission.read_deny_unapplied": "engine";
296
+ readonly "permission.rule_store_unavailable": "engine";
297
+ readonly "resume.legacy_mode_seed_migrated": "engine";
298
+ readonly "peer.inbound_disposition": "engine";
299
+ readonly "peer.held_settled": "engine";
300
+ readonly "peer.idle_subscription": "engine";
301
+ readonly "classifier.denial_limit": "engine";
302
+ readonly "classifier.fallback": "engine";
303
+ readonly "classifier.role_defaulted": "engine";
304
+ readonly "classifier.contract_broken": "engine";
305
+ readonly "classifier.unavailable": "engine";
306
+ readonly "checkpoint.execution_outcome_unrecorded": "engine";
307
+ readonly "delegation.transcript_integrity": "engine";
308
+ readonly "delegation.ask_unresolvable": "engine";
309
+ readonly "mcp.injection_dropped": "engine";
310
+ readonly "mcp.revocation_probe_failed": "engine";
311
+ readonly "mcp.server_redialed": "engine";
312
+ readonly "workflow.governance_key_stripped": "engine";
313
+ readonly "workflow.agent_option_ignored": "engine";
314
+ readonly "memory.session_polluted": "engine";
315
+ readonly "memory.harvest_quarantined": "engine";
316
+ readonly "memory.delegation_static_mark_waived": "engine";
317
+ readonly "memory.content_class_declared": "engine";
318
+ readonly "memory.hold_opened": "engine";
319
+ readonly "memory.hold_released": "engine";
320
+ readonly "memory.hold_disposed": "engine";
321
+ readonly "memory.consolidation_recommended": "engine";
322
+ readonly "memory.consolidation_committed": "engine";
323
+ readonly "memory.consolidation_conflict": "engine";
324
+ readonly "memory.consolidation_incomplete": "engine";
325
+ readonly "memory.consolidation_refused": "engine";
326
+ readonly "memory.consolidation_withheld": "engine";
327
+ readonly "memory.restore_withheld": "engine";
328
+ readonly "memory.project_scope_absent": "engine";
329
+ readonly "route.fallback_to_primary": "engine";
330
+ readonly "route.classifier_unroutable": "engine";
331
+ readonly "route.base_url_changed_key_unchanged": "engine";
332
+ readonly "route.response_model_mismatch": "engine";
333
+ readonly "route.tool_call_id_folded": "engine";
334
+ readonly "context.tool_turn_repaired": "engine";
335
+ readonly "task.user_steer_undrained": "engine";
336
+ readonly "task.user_followup_undrained": "engine";
337
+ readonly "steering.parked_input_blocked": "engine";
338
+ readonly "task.turn_interrupted": "engine";
339
+ readonly "task.halt_unconsumed": "engine";
340
+ readonly "task.late_approval": "engine";
341
+ readonly "task.interrupt_unconsumed": "engine";
342
+ readonly "task.tool_turn_incomplete": "engine";
343
+ readonly "approval.read_root_granted": "engine";
344
+ readonly "approval.read_root_grant_rejected": "engine";
345
+ readonly "memory.capture_opted_out": "engine";
346
+ readonly "memory.capture_optout_unpersisted": "engine";
347
+ readonly "skills.listing_truncated": "engine";
348
+ readonly "agents.listing_truncated": "engine";
349
+ readonly "tool_result.offload_put_failed": "engine";
350
+ readonly "instructions.source_changed": "server";
351
+ readonly "session.records_skipped": "server";
352
+ readonly "memory.layer_locked_legacy": "server";
353
+ readonly "server.model_unreachable": "server";
354
+ };
355
+ /** Compile-time fence: every catalogued code has a minter row (and, via `satisfies`, only those). */
356
+ export type MinterTableCoversEveryNoticeCode = AssertAllKeysHandled<Exclude<EngineNoticeCode, keyof typeof NOTICE_MINTER_TABLE>>;
357
+ /** The minter registry — see {@link NoticeMinter}. Frozen: a caller writing a row would move the catalog gate's verdict. */
358
+ export declare const NOTICE_MINTER: Readonly<Record<EngineNoticeCode, NoticeMinter>>;
359
+ export {};
@@ -14,6 +14,7 @@ export const NON_GOVERNANCE_MEMORY_CODES = new Set([
14
14
  "memory.partition_adopt_failed",
15
15
  "memory.partition_split",
16
16
  "memory.layout_migrated",
17
+ "memory.layer_locked_legacy",
17
18
  "memory.layout_unowned_root_content",
18
19
  "memory.tail",
19
20
  "memory.pollution_mark_failed",
@@ -92,7 +93,7 @@ export function governanceRetryClass(code) {
92
93
  return "terminal";
93
94
  return undefined;
94
95
  }
95
- export const TOOL_DECLARATION_CODES = [
96
+ export const TOOL_DECLARATION_CODES = Object.freeze([
96
97
  "config.tool_mount_denied",
97
98
  "config.tool_roster_bound",
98
99
  "config.tool_name_too_long",
@@ -102,7 +103,7 @@ export const TOOL_DECLARATION_CODES = [
102
103
  "config.tool_name_invalid",
103
104
  "config.egress_requires_write_effect",
104
105
  "config.reserved_tool_name",
105
- ];
106
+ ]);
106
107
  export const RULE_SYNC_DROP_CODES = {
107
108
  invalid_rule_text: "inbound-refused-or-local-quarantined",
108
109
  metadata_mismatch: "inbound-refused-or-local-quarantined",
@@ -112,7 +113,7 @@ export const RULE_SYNC_DROP_CODES = {
112
113
  server_rejected: "local-quarantined",
113
114
  session_scope_not_durable: "inbound-refused",
114
115
  };
115
- export const ENGINE_NOTICE_CODES = [
116
+ export const ENGINE_NOTICE_CODES = Object.freeze([
116
117
  "config.autocompact_window_clamped",
117
118
  "config.env_timeout_discarded",
118
119
  "config.materialize_env_discarded",
@@ -187,6 +188,7 @@ export const ENGINE_NOTICE_CODES = [
187
188
  "task.halt_unconsumed",
188
189
  "task.late_approval",
189
190
  "task.interrupt_unconsumed",
191
+ "task.tool_turn_incomplete",
190
192
  "approval.read_root_granted",
191
193
  "approval.read_root_grant_rejected",
192
194
  "memory.capture_opted_out",
@@ -194,7 +196,11 @@ export const ENGINE_NOTICE_CODES = [
194
196
  "skills.listing_truncated",
195
197
  "agents.listing_truncated",
196
198
  "tool_result.offload_put_failed",
197
- ];
199
+ "instructions.source_changed",
200
+ "session.records_skipped",
201
+ "memory.layer_locked_legacy",
202
+ "server.model_unreachable",
203
+ ]);
198
204
  const NOTICE_AUDIENCE_TABLE = {
199
205
  "memory.session_polluted": "user",
200
206
  "memory.harvest_quarantined": "user",
@@ -214,6 +220,7 @@ const NOTICE_AUDIENCE_TABLE = {
214
220
  "task.halt_unconsumed": "user",
215
221
  "task.late_approval": "user",
216
222
  "task.interrupt_unconsumed": "user",
223
+ "task.tool_turn_incomplete": "user",
217
224
  "approval.read_root_granted": "user",
218
225
  "approval.read_root_grant_rejected": "user",
219
226
  "config.durable_gate_unavailable": "user",
@@ -277,8 +284,101 @@ const NOTICE_AUDIENCE_TABLE = {
277
284
  "agents.listing_truncated": "operator",
278
285
  "tool_result.offload_put_failed": "operator",
279
286
  "config.forward_sink_failed": "operator",
287
+ "instructions.source_changed": "user",
288
+ "session.records_skipped": "user",
289
+ "server.model_unreachable": "user",
290
+ "memory.layer_locked_legacy": "operator",
280
291
  };
281
292
  export const NOTICE_AUDIENCE = NOTICE_AUDIENCE_TABLE;
282
293
  export function noticeAudienceOf(code) {
283
294
  return NOTICE_AUDIENCE[code] ?? "operator";
284
295
  }
296
+ const NOTICE_MINTER_TABLE = {
297
+ "config.autocompact_window_clamped": "engine",
298
+ "config.env_timeout_discarded": "engine",
299
+ "config.materialize_env_discarded": "engine",
300
+ "config.models_swapped": "engine",
301
+ "config.context_settings_swapped": "engine",
302
+ "config.read_face_swapped": "engine",
303
+ "config.read_face_deployment_clamped": "engine",
304
+ "config.write_face_swapped": "engine",
305
+ "config.write_face_deployment_clamped": "engine",
306
+ "config.tool_model_gate_removed": "engine",
307
+ "config.tool_model_gate_unknown_class": "engine",
308
+ "config.tool_model_gate_env_invalid": "engine",
309
+ "config.artifact_host_invalid": "engine",
310
+ "config.task_root_not_canonical": "engine",
311
+ "config.write_protection_target_view_absent": "engine",
312
+ "config.write_protection_unresolved": "engine",
313
+ "config.execution_env_capability_invalid": "engine",
314
+ "config.tool_card_undeclared": "engine",
315
+ "config.tool_face_undeclared": "engine",
316
+ "config.tool_face_invalid": "engine",
317
+ "config.forward_sink_failed": "engine",
318
+ "config.durable_gate_unavailable": "engine",
319
+ "config.peer_admission_out_of_range": "engine",
320
+ "config.peer_lane_unmounted": "engine",
321
+ "config.secret_env_scrubbed": "engine",
322
+ "config.permission_mode_unavailable": "engine",
323
+ "permission.ask_released_by_mode": "engine",
324
+ "permission.read_deny_unapplied": "engine",
325
+ "permission.rule_store_unavailable": "engine",
326
+ "resume.legacy_mode_seed_migrated": "engine",
327
+ "peer.inbound_disposition": "engine",
328
+ "peer.held_settled": "engine",
329
+ "peer.idle_subscription": "engine",
330
+ "classifier.denial_limit": "engine",
331
+ "classifier.fallback": "engine",
332
+ "classifier.role_defaulted": "engine",
333
+ "classifier.contract_broken": "engine",
334
+ "classifier.unavailable": "engine",
335
+ "checkpoint.execution_outcome_unrecorded": "engine",
336
+ "delegation.transcript_integrity": "engine",
337
+ "delegation.ask_unresolvable": "engine",
338
+ "mcp.injection_dropped": "engine",
339
+ "mcp.revocation_probe_failed": "engine",
340
+ "mcp.server_redialed": "engine",
341
+ "workflow.governance_key_stripped": "engine",
342
+ "workflow.agent_option_ignored": "engine",
343
+ "memory.session_polluted": "engine",
344
+ "memory.harvest_quarantined": "engine",
345
+ "memory.delegation_static_mark_waived": "engine",
346
+ "memory.content_class_declared": "engine",
347
+ "memory.hold_opened": "engine",
348
+ "memory.hold_released": "engine",
349
+ "memory.hold_disposed": "engine",
350
+ "memory.consolidation_recommended": "engine",
351
+ "memory.consolidation_committed": "engine",
352
+ "memory.consolidation_conflict": "engine",
353
+ "memory.consolidation_incomplete": "engine",
354
+ "memory.consolidation_refused": "engine",
355
+ "memory.consolidation_withheld": "engine",
356
+ "memory.restore_withheld": "engine",
357
+ "memory.project_scope_absent": "engine",
358
+ "route.fallback_to_primary": "engine",
359
+ "route.classifier_unroutable": "engine",
360
+ "route.base_url_changed_key_unchanged": "engine",
361
+ "route.response_model_mismatch": "engine",
362
+ "route.tool_call_id_folded": "engine",
363
+ "context.tool_turn_repaired": "engine",
364
+ "task.user_steer_undrained": "engine",
365
+ "task.user_followup_undrained": "engine",
366
+ "steering.parked_input_blocked": "engine",
367
+ "task.turn_interrupted": "engine",
368
+ "task.halt_unconsumed": "engine",
369
+ "task.late_approval": "engine",
370
+ "task.interrupt_unconsumed": "engine",
371
+ "task.tool_turn_incomplete": "engine",
372
+ "approval.read_root_granted": "engine",
373
+ "approval.read_root_grant_rejected": "engine",
374
+ "memory.capture_opted_out": "engine",
375
+ "memory.capture_optout_unpersisted": "engine",
376
+ "skills.listing_truncated": "engine",
377
+ "agents.listing_truncated": "engine",
378
+ "tool_result.offload_put_failed": "engine",
379
+ "instructions.source_changed": "server",
380
+ "session.records_skipped": "server",
381
+ "memory.layer_locked_legacy": "server",
382
+ "server.model_unreachable": "server",
383
+ };
384
+ export const NOTICE_MINTER = Object.freeze(NOTICE_MINTER_TABLE);
@@ -1,4 +1,4 @@
1
- export const HOOK_FAMILIES = [
1
+ export const HOOK_FAMILIES = Object.freeze([
2
2
  "preToolUse",
3
3
  "postToolUse",
4
4
  "userPromptSubmit",
@@ -9,7 +9,7 @@ export const HOOK_FAMILIES = [
9
9
  "postCompact",
10
10
  "stopFailure",
11
11
  "permissionDenied",
12
- ];
12
+ ]);
13
13
  export const HOOKS_INVALID_CODE = "config.hooks_invalid";
14
14
  export function assertHookSeatsCallable(hooks, provenance) {
15
15
  if (hooks === undefined)
@@ -5,6 +5,7 @@ import type { GateOutcome } from "./gate-outcome.js";
5
5
  export { normalizeOrgGateVerdict, normalizePersistedRuleHit } from "./gate-lanes.js";
6
6
  export { cloneObserverInput } from "./runner/gate-exit.js";
7
7
  import { type AskClass } from "./ask-class.js";
8
+ import { type PermissionMode } from "./ask-origin.js";
8
9
  import type { AutoModeDenialTracker } from "./auto-mode.js";
9
10
  export { formatHookFeedback } from "./reminder-mint.js";
10
11
  import type { WiringLegKind } from "./wiring-manifest.js";
@@ -202,8 +203,9 @@ export interface Hooks {
202
203
  * - a **PreToolUse hook deny** (CC-exact: "PreToolUse hook denies circumvention canUseTool and are not
203
204
  * covered here").
204
205
  * DOES fire for a **PreToolUse hook CRASH** (RB-442, `source:"hook"`): a throwing hook is intercepted by
205
- * the ENGINE, which then makes its OWN fail-closed deny decision. The R5 exclusion above covers the
206
- * hook's own DENY DECISION — not an engine decision that merely happened near a hook.
206
+ * the ENGINE, which then makes its OWN fail-closed deny decision — likewise a hook result the decision
207
+ * receipt REFUSES (out of contract, undetachable). The R5 exclusion above covers the hook's own DENY
208
+ * DECISION — not an engine decision that merely happened near a hook.
207
209
  * Observe-only v1 (CC-exact payload `{toolName, input, toolCallId, reason}` + our `source` enum standing
208
210
  * in for CC `decision_reason_type`): no `retry` capability (recorded — retry semantics vs our adjudicate
209
211
  * chain need their own design), no additionalContext. A throwing callback is swallowed + traced via
@@ -411,6 +413,8 @@ export interface PermissionDeniedPayload {
411
413
  * settled none.
412
414
  */
413
415
  gate: GateOutcome;
416
+ /** {@link HookToolContext.permissionMode}, read when the deny is observed. */
417
+ permissionMode?: PermissionMode;
414
418
  /** {@link HookSeatSignal} — this invocation's own abort signal. On an OBSERVATION seat the deny has
415
419
  * already happened and nothing this callback does can change it, so the signal says exactly one
416
420
  * thing: stop reading, nobody is waiting for your answer any more. */
@@ -562,6 +566,8 @@ export interface StopHookContext {
562
566
  description?: string;
563
567
  status: string;
564
568
  }>;
569
+ /** {@link HookToolContext.permissionMode}. */
570
+ permissionMode?: PermissionMode;
565
571
  /** {@link HookSeatSignal} — this invocation's own abort signal. */
566
572
  signal?: AbortSignal;
567
573
  }
@@ -799,6 +805,15 @@ export interface HookToolContext {
799
805
  * machine's answer). Absence means "this engine cannot tell you", never "the root".
800
806
  */
801
807
  cwd?: string;
808
+ /** The leg's EFFECTIVE permission mode when the callback is invoked — the mode seat's `observed` read (it arms
809
+ * no classifier on the leg or an ancestor, so it moves no decision); a steer, a plan-mode entry or exit and a
810
+ * resumed leg's restored word show on the next callback. Stamped by the Runner on its own legs' contexts (this
811
+ * one, {@link PermissionDeniedPayload}, {@link UserPromptSubmitContext}, {@link PostToolBatchContext},
812
+ * {@link StopHookContext}); ABSENT ⇔ no leg seat: a host-driven {@link runToolGate} (its
813
+ * `ToolGateInput.permissionMode` is the arbitration's reader, never called for this), the delegation-fold twin,
814
+ * the `"resume_message"` screen (before the resumed leg exists). Not a version signal. The compaction seats and
815
+ * {@link StopFailureContext} carry none (the reference's hook input omits it there). */
816
+ permissionMode?: PermissionMode;
802
817
  /** {@link HookSeatSignal} — this invocation's own abort signal. A FRESH one per screening pass, like
803
818
  * the context object it rides: phase 1 and the approval-edit re-screen are two bounded waits, and a
804
819
  * shared signal would let the first pass's expiry cancel the second. */
@@ -852,6 +867,8 @@ export interface PostToolUseResult {
852
867
  export interface UserPromptSubmitContext {
853
868
  /** The run/leg identity envelope — see {@link HookInvocationIdentity} for the presence law. */
854
869
  identity?: HookInvocationIdentity;
870
+ /** {@link HookToolContext.permissionMode} (absent on `"resume_message"`, screened before the leg exists). */
871
+ permissionMode?: PermissionMode;
855
872
  /** {@link HookSeatSignal} — this invocation's own abort signal. */
856
873
  signal?: AbortSignal;
857
874
  /**
@@ -905,6 +922,8 @@ export interface UserPromptSubmitContext {
905
922
  export interface PostToolBatchContext {
906
923
  /** The run/leg identity envelope — see {@link HookInvocationIdentity} for the presence law. */
907
924
  identity?: HookInvocationIdentity;
925
+ /** {@link HookToolContext.permissionMode}. */
926
+ permissionMode?: PermissionMode;
908
927
  /** {@link HookSeatSignal} — this invocation's own abort signal. */
909
928
  signal?: AbortSignal;
910
929
  }
@@ -1,4 +1,4 @@
1
- import { decisionText, describeThrown, refuseOutOfContractDecision } from "./tool-policy.js";
1
+ import { decisionText, describeThrown, refuseOutOfContractDecision, isEngineRefusal } from "./tool-policy.js";
2
2
  import { asThrownError } from "./thrown-value.js";
3
3
  import { createSettlementLedger, engineFailClosedExit, hookDenyExit } from "./runner/gate-exit.js";
4
4
  import { traceHookCrash } from "./gate-crash-trace.js";
@@ -339,6 +339,9 @@ export async function runToolGate(input) {
339
339
  return await engineFailClosedExit(pass, reason);
340
340
  }
341
341
  if (r) {
342
+ if (isEngineRefusal(r)) {
343
+ return await engineFailClosedExit(pass, String(r.message ?? `the PreToolUse hook's result for "${toolName}" was refused by the engine`));
344
+ }
342
345
  if (r.additionalContext) {
343
346
  pass.preToolContext.push(r.additionalContext);
344
347
  }
@@ -1,7 +1,7 @@
1
1
  import { assertRetentionPolicy } from "./retention-policy.js";
2
- export const MAILBOX_PEER_FROM_MODES = ["bypass", "prompting"];
3
- export const MAILBOX_PEER_RECORD_KINDS = ["peer_message", "idle_notice", "delivery_notice", "idle_subscription", "engine_frame"];
4
- export const MAILBOX_PEER_NOTICE_STATES = ["held", "denied", "expired", "delivered", "refused", "dropped", "idle", "exited", "unavailable"];
2
+ export const MAILBOX_PEER_FROM_MODES = Object.freeze(["bypass", "prompting"]);
3
+ export const MAILBOX_PEER_RECORD_KINDS = Object.freeze(["peer_message", "idle_notice", "delivery_notice", "idle_subscription", "engine_frame"]);
4
+ export const MAILBOX_PEER_NOTICE_STATES = Object.freeze(["held", "denied", "expired", "delivered", "refused", "dropped", "idle", "exited", "unavailable"]);
5
5
  export const MAILBOX_INVALID_PEER_META_CODE = "mailbox.invalid_peer_meta";
6
6
  export const MAILBOX_CROSS_PROCESS_UNSAFE_CODE = "mailbox.cross_process_unsafe";
7
7
  const PEER_META_STRING_KEYS = ["fromSession", "fromRoot", "senderKey", "fromScope", "gateReceiptId"];
@@ -1,6 +1,6 @@
1
1
  import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
2
2
  import { StreamableHTTPError } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
3
- export const MCP_FAILURE_KINDS = [
3
+ export const MCP_FAILURE_KINDS = Object.freeze([
4
4
  "connect_refused",
5
5
  "connection_failed",
6
6
  "connection_closed",
@@ -11,8 +11,8 @@ export const MCP_FAILURE_KINDS = [
11
11
  "protocol",
12
12
  "invalid_config",
13
13
  "unknown",
14
- ];
15
- export const MCP_DELIVERY_VERDICTS = ["yes", "no", "unknown"];
14
+ ]);
15
+ export const MCP_DELIVERY_VERDICTS = Object.freeze(["yes", "no", "unknown"]);
16
16
  export const DELIVERED_BY_KIND = {
17
17
  connect_refused: "no",
18
18
  connection_failed: "unknown",
@@ -25,7 +25,7 @@ export const DELIVERED_BY_KIND = {
25
25
  invalid_config: "no",
26
26
  unknown: "unknown",
27
27
  };
28
- export const MCP_LIVENESS_STATES = ["reachable", "unreachable", "unknown"];
28
+ export const MCP_LIVENESS_STATES = Object.freeze(["reachable", "unreachable", "unknown"]);
29
29
  export const LIVENESS_BY_FAILURE_KIND = {
30
30
  connect_refused: "unreachable",
31
31
  connection_failed: "unreachable",
@@ -1,4 +1,4 @@
1
- export const MCP_INJECTION_DROP_REASONS = ["malformed_entry", "name_reserved_by_deployment", "gate_closed", "over_cap"];
1
+ export const MCP_INJECTION_DROP_REASONS = Object.freeze(["malformed_entry", "name_reserved_by_deployment", "gate_closed", "over_cap"]);
2
2
  export const MCP_INJECTION_DROP_TEXT = {
3
3
  malformed_entry: "its declaration could not be read — fix the entry and start a new session",
4
4
  name_reserved_by_deployment: "the deployment mounts a server of that name and its own wins — rename the entry",
@@ -373,4 +373,4 @@ export class ConsolidationRefusedError extends Error {
373
373
  export function productAddPatch(product) {
374
374
  return { op: "add", id: product.id, entry: product, guard: "absent" };
375
375
  }
376
- export const CONSOLIDATION_RUN_STOP_REASONS = ["converged", "fuse_residue", "max_cycles", "parked", "refused", "driver_failed", "mint_invalidated", "purity_violation"];
376
+ export const CONSOLIDATION_RUN_STOP_REASONS = Object.freeze(["converged", "fuse_residue", "max_cycles", "parked", "refused", "driver_failed", "mint_invalidated", "purity_violation"]);
@@ -202,6 +202,14 @@ export declare function openLayerWriters(controlDir: string, selfLeg: string | u
202
202
  };
203
203
  /** The leg that holds `sessionId`'s account row (undefined ⇔ no row, or a row without one). */
204
204
  export declare function sessionAccountLeg(controlDir: string, sessionId: string): string | undefined;
205
+ /** The write scope `sessionId`'s account row records — the scope its newest account-opening mount wrote
206
+ * under (the value that mount's handle carries as `writeScope`), whether that leg is still live or has
207
+ * ended. Undefined ⇔ no row, a row written before the field, or a v1 ledger (whose rows predate it —
208
+ * answered without the conversion, so this read writes nothing). Throws on a corrupt ledger. */
209
+ export declare function sessionAccountWriteHome(controlDir: string, sessionId: string): {
210
+ writeScope: string;
211
+ openedAt: number;
212
+ } | undefined;
205
213
  /** The membership question the harvest asks per file: is `(scope, rel)` in the session's sticky
206
214
  * set? A session carrying members the conversion could not attribute answers yes for every seat. */
207
215
  export interface SessionUnattributedSet {
@@ -331,6 +331,13 @@ export function openLayerWriters(controlDir, selfLeg) {
331
331
  export function sessionAccountLeg(controlDir, sessionId) {
332
332
  return readSessionAccountsConverted(controlDir).rows.find((r) => r.sessionId === sessionId)?.leg;
333
333
  }
334
+ export function sessionAccountWriteHome(controlDir, sessionId) {
335
+ const read = coerceSessionAccounts(readStrictSidecar(controlDir, SESSION_ACCOUNTS_FILE, "memory session-account ledger"));
336
+ if (read.version !== 2)
337
+ return undefined;
338
+ const row = read.rows.find((r) => r.sessionId === sessionId);
339
+ return row?.writeScope === undefined ? undefined : { writeScope: row.writeScope, openedAt: row.openedAt };
340
+ }
334
341
  export function emptyUnattributedSet() {
335
342
  return { has: () => false, empty: true };
336
343
  }