@sema-agent/core 7.33.8 → 7.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. package/CHANGELOG.md +339 -0
  2. package/dist/agents/cross-session-judge.d.ts +16 -7
  3. package/dist/agents/cross-session-judge.js +18 -15
  4. package/dist/agents/peer-session-drain.d.ts +4 -0
  5. package/dist/agents/peer-session-drain.js +5 -2
  6. package/dist/agents/repair-loop.d.ts +1 -1
  7. package/dist/agents/subagent.d.ts +3 -0
  8. package/dist/agents/subagent.js +12 -3
  9. package/dist/brain/anthropic.js +1 -0
  10. package/dist/brain/open-responses.js +1 -0
  11. package/dist/brain/openai.js +1 -0
  12. package/dist/brain/stream-engine.d.ts +3 -0
  13. package/dist/brain/stream-engine.js +2 -1
  14. package/dist/core/a2a.d.ts +3 -1
  15. package/dist/core/a2a.js +1 -1
  16. package/dist/core/ask-origin.d.ts +351 -154
  17. package/dist/core/ask-origin.js +223 -45
  18. package/dist/core/ask-unresolvable-notice.d.ts +5 -2
  19. package/dist/core/ask-unresolvable-notice.js +4 -3
  20. package/dist/core/auto-mode-arming.d.ts +17 -4
  21. package/dist/core/auto-mode-arming.js +31 -6
  22. package/dist/core/auto-mode-defaults.d.ts +16 -7
  23. package/dist/core/auto-mode-defaults.js +2 -1
  24. package/dist/core/auto-mode-directive.d.ts +18 -30
  25. package/dist/core/auto-mode-directive.js +9 -18
  26. package/dist/core/auto-mode-prompt.d.ts +62 -1
  27. package/dist/core/auto-mode-prompt.js +42 -4
  28. package/dist/core/auto-mode-rebuild.d.ts +2 -1
  29. package/dist/core/auto-mode-rebuild.js +1 -0
  30. package/dist/core/auto-mode.d.ts +32 -3
  31. package/dist/core/auto-mode.js +80 -41
  32. package/dist/core/checkpoint-store.d.ts +50 -35
  33. package/dist/core/checkpoint-store.js +58 -3
  34. package/dist/core/child-clamp-seats.js +1 -0
  35. package/dist/core/classifier-unavailable-arm.d.ts +57 -0
  36. package/dist/core/classifier-unavailable-arm.js +17 -0
  37. package/dist/core/effective-path-target.d.ts +12 -0
  38. package/dist/core/effective-path-target.js +18 -7
  39. package/dist/core/engine-notice.d.ts +53 -2
  40. package/dist/core/fs-write-gate-policy.d.ts +5 -1
  41. package/dist/core/fs-write-gate-policy.js +4 -1
  42. package/dist/core/gate-fold.js +17 -1
  43. package/dist/core/gate-lanes.js +111 -46
  44. package/dist/core/gate-outcome.d.ts +40 -7
  45. package/dist/core/gate-outcome.js +26 -4
  46. package/dist/core/governance-codes.d.ts +1 -1
  47. package/dist/core/governance-codes.js +12 -0
  48. package/dist/core/hooks.d.ts +50 -29
  49. package/dist/core/hooks.js +1 -3
  50. package/dist/core/mcp.js +1 -1
  51. package/dist/core/mode-policy.d.ts +182 -0
  52. package/dist/core/mode-policy.js +179 -0
  53. package/dist/core/oracle-isolation.d.ts +3 -3
  54. package/dist/core/permission-rule-model.d.ts +16 -3
  55. package/dist/core/permission-rule-model.js +36 -3
  56. package/dist/core/permission-rule-provider.d.ts +22 -11
  57. package/dist/core/permission-rule-provider.js +17 -7
  58. package/dist/core/permission-rules.d.ts +23 -1
  59. package/dist/core/permission-rules.js +35 -8
  60. package/dist/core/persisted-rule-arms.d.ts +10 -9
  61. package/dist/core/persisted-rule-arms.js +9 -6
  62. package/dist/core/present-plan-tool.d.ts +1 -1
  63. package/dist/core/present-plan-tool.js +3 -3
  64. package/dist/core/read-only-shell.d.ts +19 -19
  65. package/dist/core/read-only-shell.js +38 -12
  66. package/dist/core/retired-keys.d.ts +9 -3
  67. package/dist/core/retired-keys.js +5 -1
  68. package/dist/core/runner/classifier-ladder.d.ts +43 -1
  69. package/dist/core/runner/classifier-ladder.js +29 -0
  70. package/dist/core/runner/contracts.d.ts +37 -35
  71. package/dist/core/runner/denial-limit-arms.d.ts +30 -7
  72. package/dist/core/runner/denial-limit-arms.js +15 -9
  73. package/dist/core/runner/env-capability-census.js +1 -1
  74. package/dist/core/runner/gate-exit.d.ts +30 -4
  75. package/dist/core/runner/gate-exit.js +28 -7
  76. package/dist/core/runner/permission-rule-lanes.d.ts +9 -5
  77. package/dist/core/runner/permission-rule-lanes.js +44 -40
  78. package/dist/core/runner/persisted-path-rows.d.ts +64 -0
  79. package/dist/core/runner/persisted-path-rows.js +89 -0
  80. package/dist/core/runner/prepare-artifact.d.ts +5 -1
  81. package/dist/core/runner/prepare-artifact.js +9 -3
  82. package/dist/core/runner/prepare-ask-lane.d.ts +5 -3
  83. package/dist/core/runner/prepare-ask-lane.js +1 -1
  84. package/dist/core/runner/prepare-caps-and-workflow.d.ts +39 -13
  85. package/dist/core/runner/prepare-caps-and-workflow.js +58 -29
  86. package/dist/core/runner/prepare-gate-stations.d.ts +33 -6
  87. package/dist/core/runner/prepare-gate-stations.js +32 -19
  88. package/dist/core/runner/prepare-hands-readface.d.ts +40 -17
  89. package/dist/core/runner/prepare-hands-readface.js +64 -53
  90. package/dist/core/runner/prepare-inherited-gate.d.ts +22 -22
  91. package/dist/core/runner/prepare-inherited-gate.js +37 -23
  92. package/dist/core/runner/prepare-park-ask.d.ts +2 -2
  93. package/dist/core/runner/prepare-park-ask.js +2 -1
  94. package/dist/core/runner/prepare-policy-chain.d.ts +22 -2
  95. package/dist/core/runner/prepare-policy-chain.js +39 -12
  96. package/dist/core/runner/prepare-protocol-tools.d.ts +28 -0
  97. package/dist/core/runner/prepare-protocol-tools.js +13 -3
  98. package/dist/core/runner/prepare-run-refs.d.ts +6 -6
  99. package/dist/core/runner/prepare-run-refs.js +9 -4
  100. package/dist/core/runner/prepare-safety-scan.d.ts +5 -0
  101. package/dist/core/runner/prepare-safety-scan.js +19 -3
  102. package/dist/core/runner/prepare-suspend-saga.d.ts +5 -5
  103. package/dist/core/runner/prepare-suspend-saga.js +6 -6
  104. package/dist/core/runner/prepare-task.js +51 -22
  105. package/dist/core/runner/prepare-wiring-manifest.d.ts +10 -6
  106. package/dist/core/runner/prepare-wiring-manifest.js +5 -3
  107. package/dist/core/runner/read-root-grant.d.ts +9 -0
  108. package/dist/core/runner/read-root-grant.js +12 -0
  109. package/dist/core/runner/retired-runner-deps.js +1 -0
  110. package/dist/core/runner/run-identity-wiring.js +1 -1
  111. package/dist/core/runner/run-leg.js +24 -15
  112. package/dist/core/runner/run-notification-lane.js +1 -0
  113. package/dist/core/runner/run-telemetry-and-budget-seats.d.ts +2 -2
  114. package/dist/core/runner/run-telemetry-and-budget-seats.js +1 -1
  115. package/dist/core/runner/run-turn-boundary.js +1 -1
  116. package/dist/core/runner/runtask.js +3 -0
  117. package/dist/core/runner/stream-mode-verb.d.ts +27 -0
  118. package/dist/core/runner/stream-mode-verb.js +19 -0
  119. package/dist/core/runner/turn-attachments.d.ts +3 -3
  120. package/dist/core/runner/turn-attachments.js +1 -1
  121. package/dist/core/runner/write-protection-station.d.ts +7 -1
  122. package/dist/core/runner/write-protection-station.js +6 -3
  123. package/dist/core/runner-deps.d.ts +31 -34
  124. package/dist/core/runtime-caps.d.ts +12 -3
  125. package/dist/core/safety-axis-vocab.d.ts +2 -2
  126. package/dist/core/safety-axis-vocab.js +1 -1
  127. package/dist/core/sensitive-path-policy.d.ts +4 -2
  128. package/dist/core/sensitive-path-policy.js +39 -30
  129. package/dist/core/task-event.d.ts +1 -1
  130. package/dist/core/task-result.d.ts +1 -1
  131. package/dist/core/task-spec.d.ts +41 -89
  132. package/dist/core/task-stream.d.ts +16 -0
  133. package/dist/core/terminal-cause.js +1 -0
  134. package/dist/core/tighten-task-spec.d.ts +2 -2
  135. package/dist/core/tighten-task-spec.js +12 -14
  136. package/dist/core/tool-catalog-entries.js +4 -3
  137. package/dist/core/tool-policy.d.ts +54 -25
  138. package/dist/core/tool-policy.js +43 -2
  139. package/dist/core/tool-registry.d.ts +2 -0
  140. package/dist/core/tool-registry.js +3 -0
  141. package/dist/core/tool-spec.d.ts +8 -9
  142. package/dist/core/trace.d.ts +7 -6
  143. package/dist/core/types.d.ts +2 -1
  144. package/dist/core/wiring-manifest.d.ts +31 -5
  145. package/dist/core/wiring-manifest.js +28 -1
  146. package/dist/core/write-protect.d.ts +11 -1
  147. package/dist/core/write-protect.js +26 -15
  148. package/dist/engine/execution-env/node-execution-env.d.ts +3 -0
  149. package/dist/engine/execution-env/node-execution-env.js +11 -1
  150. package/dist/engine/harness/types.d.ts +6 -7
  151. package/dist/engine/llm/types.d.ts +2 -4
  152. package/dist/engine/loop/types.d.ts +7 -0
  153. package/dist/index.d.ts +15 -5
  154. package/dist/index.js +12 -3
  155. package/dist/orchestration/workflow-governance.js +1 -1
  156. package/dist/orchestration/workflow-primitives.d.ts +1 -1
  157. package/dist/orchestration/workflow-size-guideline.js +2 -2
  158. package/dist/tools/artifact/artifact-tool.d.ts +17 -0
  159. package/dist/tools/artifact/artifact-tool.js +16 -1
  160. package/dist/tools/fs/bash-readonly-classifier.d.ts +28 -7
  161. package/dist/tools/fs/bash-readonly-classifier.js +88 -0
  162. package/dist/tools/fs/containment-face.d.ts +9 -3
  163. package/dist/tools/fs/containment-face.js +2 -0
  164. package/dist/tools/fs/file-read-check.d.ts +49 -0
  165. package/dist/tools/fs/file-read-check.js +71 -0
  166. package/dist/tools/fs/fs-bash.d.ts +3 -3
  167. package/dist/tools/fs/fs-read.d.ts +1 -0
  168. package/dist/tools/fs/fs-search-tools.d.ts +2 -2
  169. package/dist/tools/fs/fs-search-tools.js +4 -4
  170. package/dist/tools/fs/fs-shared.js +20 -6
  171. package/dist/tools/fs/fs-write.d.ts +4 -3
  172. package/dist/tools/fs/fs-write.js +9 -9
  173. package/dist/tools/fs/index.d.ts +39 -3
  174. package/dist/tools/fs/index.js +38 -15
  175. package/dist/tools/fs/read-deny-home.d.ts +93 -0
  176. package/dist/tools/fs/read-deny-home.js +225 -0
  177. package/dist/tools/fs/read-deny.d.ts +176 -34
  178. package/dist/tools/fs/read-deny.js +268 -61
  179. package/dist/tools/fs/safety.d.ts +6 -1
  180. package/dist/tools/fs/safety.js +18 -5
  181. package/dist/tools/fs/search.d.ts +3 -0
  182. package/dist/tools/fs/search.js +47 -9
  183. package/dist/tools/fs/shell-read-check.d.ts +42 -11
  184. package/dist/tools/fs/shell-read-check.js +188 -13
  185. package/dist/tools/fs/shell-read-establish.d.ts +7 -3
  186. package/dist/tools/fs/shell-read-establish.js +134 -21
  187. package/dist/tools/fs/shell-read-identity.d.ts +7 -0
  188. package/dist/tools/fs/shell-read-identity.js +21 -0
  189. package/dist/tools/fs/shell-read-judge.d.ts +92 -32
  190. package/dist/tools/fs/shell-read-judge.js +43 -18
  191. package/dist/tools/fs/shell-read-plan.d.ts +4 -0
  192. package/dist/tools/fs/shell-read-plan.js +26 -4
  193. package/dist/tools/fs/shell-read-write-targets.d.ts +73 -0
  194. package/dist/tools/fs/shell-read-write-targets.js +173 -0
  195. package/package.json +1 -1
  196. package/test/export-surface.snapshot.json +103 -11
  197. package/wire-changes/core@7.34.0.json +2621 -0
@@ -1113,7 +1113,7 @@ export interface ToolExecuteContext {
1113
1113
  /**
1114
1114
  * RB-201 FO-3 (form-one audit, CC 220 `Ipd`/`ein` parity) — the auto-mode classifier decider ARMED
1115
1115
  * for THIS task (auto-mode intent ∧ `RunnerDeps.autoMode` present ∧ `RuntimeCaps.autoMode !== false`
1116
- * — see {@link TaskSpec.autoModeRequested}; the same
1116
+ * — see {@link TaskSpec.permissionMode}; the same
1117
1117
  * instance `runToolGate`'s per-call ask review already consults, carrying its own live breaker
1118
1118
  * state — not a fresh one built from raw config). Runner-filled, trusted, undefined when auto-mode
1119
1119
  * is not armed or the tool runs outside a Runner task.
@@ -1286,13 +1286,12 @@ export interface ToolExecuteContext {
1286
1286
  */
1287
1287
  requestStopAfterTurn?: () => void;
1288
1288
  /**
1289
- * design/108 — enter mid-run READ-ONLY plan mode: the rest of THIS run rejects write-effect tool calls
1290
- * (`effect !== "read"`), while read/research tools and `present_plan` still run. A run-local one-way flag
1291
- * (NOT durable — a later approve-resume re-prepares with it clear, so the approved plan can write). The
1292
- * first-party `enter_plan_mode` tool (CC `EnterPlanMode` parity) is the only caller; the engine enforces the
1293
- * read-only restriction in a dedicated plan-mode tool-call check (separate from the safety gate — plan mode
1294
- * is a FIDELITY mode, not a security boundary; the real safety gates apply independently). Idempotent; a
1295
- * no-op for tools that don't call it. The Runner fills it; not a tool arg. Absent outside a Runner.
1289
+ * Enter plan mode mid-run: a steer of the leg's ONE permission-mode seat into `plan` — the same mode the spec's
1290
+ * `permissionMode` and `TaskStream.setPermissionMode` set. For the rest of THIS run the mode gate asks before every
1291
+ * covered write and every call to a tool the engine does not define that is not read-only (the plan sentence; refused
1292
+ * where nobody can answer), while read tools and `ExitPlanMode` run. The word it left is recorded: an approve-resume
1293
+ * returns to it; a rejected plan steers the resumed leg back into `plan`. The first-party `EnterPlanMode`
1294
+ * tool is the only caller. Idempotent. The Runner fills it; not a tool arg. Absent outside a Runner.
1296
1295
  */
1297
- enterPlanMode?: () => void;
1296
+ enterPlanMode?: () => Promise<void>;
1298
1297
  }
@@ -253,11 +253,12 @@ export type TraceEvent = {
253
253
  taskId: string;
254
254
  /** Which spec field the skipped entry came from. Absent = `additionalDirectories` (the
255
255
  * original face, kept implicit for existing readers); `"additionalReadDirectories"` = the
256
- * read-only whitelist arm (ruled 2026-08-05). */
257
- field?: "additionalReadDirectories";
256
+ * read-only whitelist arm (ruled 2026-08-05); `"persistedReadRule"` = a person's persisted `Read(//dir/**)` allow row. */
257
+ field?: "additionalReadDirectories" | "persistedReadRule";
258
258
  /** The spec entry as written (raw, not canonical — canonicalization is what failed). */
259
259
  entry: string;
260
- /** The canonicalization failure, `code: message` form. */
260
+ /** Why the entry was skipped, `code: message` form: the canonicalization failure, or (a persisted row only)
261
+ * `admits_artifact_store` — the in-flight grant's store rule. */
261
262
  reason: string;
262
263
  ts: number;
263
264
  } | {
@@ -442,9 +443,9 @@ export type TraceEvent = {
442
443
  ts: number;
443
444
  } | {
444
445
  /**
445
- * design/179 — the persisted allow-rule store could not be read, so this call was adjudicated with
446
- * ZERO rules (it asks, rather than silently behaving like an empty store). A loosening face that
447
- * fails must say so.
446
+ * design/179 — the person's persisted rule store could not be read (at prepare, or for one call), so
447
+ * that read's rows took no part: the decision it served was made with ZERO persisted rows. The operator's
448
+ * per-read record; the person hears it once per run (`permission.rule_store_unavailable`).
448
449
  */
449
450
  kind: "permission.rule_store_unreadable";
450
451
  version: 1;
@@ -13,7 +13,8 @@ import type { AgentTool, ThinkingLevel, ToolInputValidationContext, ToolInputVer
13
13
  import type { Model, StreamFn } from "../internal/llm.js";
14
14
  export type { ModelRef, ModelRole, RoleSpec, ModelRoles, Brain } from "./model-seat.js";
15
15
  export { MODEL_ROLES } from "./model-seat.js";
16
- export type { StaleToolResultOffloadOptions, ImageInput, TaskSpec, ShellGateDoctrine, ApproverPosture } from "./task-spec.js";
16
+ export type { StaleToolResultOffloadOptions, ImageInput, TaskSpec, ShellGateDoctrine } from "./task-spec.js";
17
+ export type { PermissionMode } from "./ask-origin.js";
17
18
  export type { ToolEffect, ToolContentOrigin, ReversibilityVerdict, ReadRootGrantCandidate, ToolSpec, ToolReturn, NestedUsage, NestedUsageAccum, ToolExecuteContext } from "./tool-spec.js";
18
19
  export type { BeforeWriteRequest, BeforeWriteResult, BeforeWriteHook, TrackEditRequest, TrackEditResult, TrackFileEditHook, FileEditedNotice, FileEditedHook, HandsBandOptions } from "./hands-band.js";
19
20
  export type { AgentDefinition, SkillManifest, SkillSpec } from "./agent-definition.js";
@@ -64,8 +64,8 @@ export type ParkLaneReason = "no_checkpoint_store" | "no_durable_approval_opt_in
64
64
  * The closed set of auto-mode ARMING outcomes a leg's manifest reports
65
65
  * ({@link WiringManifest.autoMode}). Minted from the arming predicate's own three-arm read, as the
66
66
  * FIRST failing arm in predicate order:
67
- * - `no_intent` — no `TaskSpec.autoModeRequested` seat and no intent inherited on the chain /
68
- * recorded on the resumed checkpoint (a non-auto run; byte-identical arming).
67
+ * - `no_intent` — the declared permission mode is not `auto` (no `permissionMode: "auto"` seat, none
68
+ * inherited on the chain / recorded on the resumed checkpoint; a non-auto run).
69
69
  * - `no_face` — intent, but no `RunnerDeps.autoMode` deployment face (the trust gate).
70
70
  * - `denied` — intent + face, but the organization's deny bit (`RuntimeCaps.autoMode:false`)
71
71
  * as the resolver STATED it.
@@ -306,7 +306,8 @@ export interface WiringManifest {
306
306
  * classifier decider was minted for this leg), and `reason` names the first arm that failed
307
307
  * otherwise — see {@link AUTO_MODE_ARM_REASONS}. A serving layer that used to infer the mode from
308
308
  * spec shape reads it here instead. The classifier's availability is a per-CALL fact (an unavailable
309
- * round is a deny that says so, and the next round is classified afresh), so there is no session-level
309
+ * round goes on to the approval seat or is refused with a sentence that says so, and the next round is
310
+ * classified afresh), so there is no session-level
310
311
  * breaker face here and nothing about availability enters {@link configFingerprint}.
311
312
  */
312
313
  autoMode?: AutoModeArmFact;
@@ -369,6 +370,17 @@ export interface WiringManifest {
369
370
  mounted: boolean;
370
371
  reason?: HandsUnmountedReason;
371
372
  };
373
+ /**
374
+ * EFFECTIVE half only, ALWAYS present on an engine-minted effective manifest: the leg's PERMISSION MODE as the engine
375
+ * resolved it — `requested` (the caller's word; absent = declared nothing), `effective` (the mode the leg runs under:
376
+ * the request clamped under a parent's ceiling, then folded to `default` where the deployment denies it or the
377
+ * classifier face is absent), `foldedBy` (present iff the two differ: `operator_deny` / `face_absent` /
378
+ * `inherited_ceiling`). The same three members answer every steer of the mode (`TaskStream.setPermissionMode`) and
379
+ * ride the fold notice's detail. A shell renders its mode indicator from `effective`, never from what it sent.
380
+ * ABSENT = an older mint, never a state. Leg-local (excluded from {@link configFingerprint}: a child clamped under its
381
+ * parent legitimately differs from it).
382
+ */
383
+ permissionMode?: import("./mode-policy.js").PermissionModeReceipt;
372
384
  /**
373
385
  * EFFECTIVE half only, PRESENT IFF this leg compiled a write-protection judge — a deployment that
374
386
  * replaced the table with `[]` opted the layer out entirely, and reports nothing here rather than a
@@ -407,8 +419,8 @@ export interface WiringManifest {
407
419
  * of one assembly agree.
408
420
  *
409
421
  * @contract read_deny.builtin_activation_disclosed — WHETHER the built-in sensitive-path read deny
410
- * table judges a leg is DISCLOSED, never inferred: the table is a deployment opt-in whose default
411
- * selection is empty, the leg that compiles the judge relays the tiers its own resolution materially
422
+ * table judges a leg is DISCLOSED, never inferred: the tier selection is the deployment's (absent = the default
423
+ * tiers, an explicit `[]` = none), the leg that compiles the judge relays the tiers its own resolution materially
412
424
  * activated, and the manifest carries that section iff at least one built-in row is active (absence
413
425
  * = no built-in row judges; an empty array is never emitted — "off" and "on to nothing" are one
414
426
  * fact). The disclosure changes no verdict.
@@ -524,6 +536,9 @@ export interface WiringFacts {
524
536
  mounted: boolean;
525
537
  reason?: HandsUnmountedReason;
526
538
  };
539
+ /** Effective half only — the leg's permission-mode receipt ({@link WiringManifest.permissionMode}); the engine always passes it.
540
+ * Screened: `effective` a mode word, `requested` a mode word or absent, `foldedBy` a fold reason or absent. */
541
+ permissionMode?: import("./mode-policy.js").PermissionModeReceipt;
527
542
  /** Effective half only — the compiled write-protection judge's OWN target-view word
528
543
  * ({@link WiringManifest.writeProtection}); absent ⇒ this leg compiled no judge (an emptied table). */
529
544
  writeProtectionTargetView?: WriteProtectionTargetView;
@@ -596,6 +611,17 @@ export declare function resolveSubagentTranscriptTier(agentStoreWired: boolean,
596
611
  * else the headless degrade `auto_deny`.
597
612
  */
598
613
  export declare function deriveAskEffective(form: AskSeamForm, parkEffective: boolean | "unresolved"): AskEffective;
614
+ /**
615
+ * Does an ask this leg hands on carrying `requiresRealApproval` reach a PERSON — never an automatic yes? The leg's
616
+ * APPROVAL SEAT, read off the ONE ask derivation above, never re-derived: `human_reachable` (a live approver) and
617
+ * `park_only` (the durable park; where the park cannot take the ask, a blanket seat behind it refuses the bit) are a
618
+ * person; `auto_allow`, `auto_deny` and `unresolved` are nobody (the last the fail-closed reading). The auto-mode
619
+ * classifier stations read it to decide where a classification that could not rule goes: to that person (the ask
620
+ * carrying the bit), or refused.
621
+ */
622
+ export declare function askReachesPerson(ask: {
623
+ readonly effective?: AskEffective;
624
+ }): boolean;
599
625
  export declare function deriveWiringManifest(facts: WiringFacts): WiringManifest;
600
626
  /** The one ask-seat resolution (`spec` wins over `deps`), shared with the effective half so the
601
627
  * form/provenance read cannot fork. Junk values (a JS caller passing something outside the OnAsk
@@ -1,3 +1,5 @@
1
+ import { isPermissionMode } from "./ask-origin.js";
2
+ import { PERMISSION_MODE_FOLD_REASONS } from "./mode-policy.js";
1
3
  import { createHash } from "node:crypto";
2
4
  import { canonicalize } from "./canonical-json.js";
3
5
  import { resolveCheckpointStore } from "./checkpoint-store.js";
@@ -57,6 +59,23 @@ export function deriveAskEffective(form, parkEffective) {
57
59
  }
58
60
  }
59
61
  }
62
+ export function askReachesPerson(ask) {
63
+ const effective = ask.effective ?? "unresolved";
64
+ switch (effective) {
65
+ case "human_reachable":
66
+ case "park_only":
67
+ return true;
68
+ case "auto_allow":
69
+ case "auto_deny":
70
+ case "unresolved":
71
+ return false;
72
+ default: {
73
+ const _exhaustive = effective;
74
+ void _exhaustive;
75
+ throw new Error(`unreachable ask effective ${String(effective)}`);
76
+ }
77
+ }
78
+ }
60
79
  function deriveParkLane(facts) {
61
80
  if (!facts.parkCapable) {
62
81
  return { capable: false, effective: false, reasons: ["no_checkpoint_store"] };
@@ -87,6 +106,13 @@ function readAutoModeFact(fact) {
87
106
  }
88
107
  return { armed, reason, ...(deniedSource !== undefined ? { deniedSource } : {}) };
89
108
  }
109
+ function readPermissionModeFact(fact) {
110
+ const { requested, effective, foldedBy } = typeof fact === "object" && fact !== null ? fact : { requested: undefined, effective: undefined, foldedBy: undefined };
111
+ if (!isPermissionMode(effective) || (requested !== undefined && !isPermissionMode(requested)) || (foldedBy !== undefined && !PERMISSION_MODE_FOLD_REASONS.includes(foldedBy))) {
112
+ throw new Error(`the permission-mode wiring fact is inconsistent (${JSON.stringify(fact)}) — effective must be a permission mode, requested one or absent, foldedBy one of ${PERMISSION_MODE_FOLD_REASONS.join("|")} or absent`);
113
+ }
114
+ return { ...(requested !== undefined ? { requested } : {}), effective, ...(foldedBy !== undefined ? { foldedBy } : {}) };
115
+ }
90
116
  function readHandsFact(fact) {
91
117
  const { mounted, reason } = typeof fact === "object" && fact !== null ? fact : { mounted: undefined, reason: undefined };
92
118
  if (typeof mounted !== "boolean" || (mounted ? reason !== undefined : reason !== "no_execution_env")) {
@@ -157,6 +183,7 @@ export function deriveWiringManifest(facts) {
157
183
  ...(facts.half === "effective" && facts.hooks !== undefined && facts.hooks.length > 0 ? { hooks: facts.hooks.map((h) => ({ ...h })) } : {}),
158
184
  ...(facts.half === "effective" && facts.lsp !== undefined ? { lsp: { ...facts.lsp } } : {}),
159
185
  ...(facts.half === "effective" && facts.hands !== undefined ? { hands: readHandsFact(facts.hands) } : {}),
186
+ ...(facts.half === "effective" && facts.permissionMode !== undefined ? { permissionMode: readPermissionModeFact(facts.permissionMode) } : {}),
160
187
  ...(facts.half === "effective" && facts.writeProtectionTargetView !== undefined ? { writeProtection: { targetView: facts.writeProtectionTargetView } } : {}),
161
188
  ...(facts.half === "effective" && facts.readDenyBuiltinTiersActive !== undefined && facts.readDenyBuiltinTiersActive.length > 0
162
189
  ? { readDeny: { builtinTiers: [...facts.readDenyBuiltinTiersActive] } }
@@ -169,7 +196,7 @@ export function deriveWiringManifest(facts) {
169
196
  : {}),
170
197
  };
171
198
  if (facts.half === "effective") {
172
- const { leg: _leg, mcp: _mcp, tools: _tools, lsp: _lsp, hooks: _hooks, teamRole: _teamRole, peerPresence: _peerPresence, fileHistory: _fileHistory, ...assembly } = manifest;
199
+ const { leg: _leg, mcp: _mcp, tools: _tools, lsp: _lsp, hooks: _hooks, teamRole: _teamRole, peerPresence: _peerPresence, fileHistory: _fileHistory, permissionMode: _permissionMode, ...assembly } = manifest;
173
200
  const { provenance: _askSeat, ...askForHash } = assembly.ask;
174
201
  const { provenance: _questionSeat, ...questionForHash } = assembly.question;
175
202
  manifest.configFingerprint = createHash("sha256")
@@ -20,7 +20,7 @@ export interface WriteProtectedRow {
20
20
  * String shorthand: a bare name ≡ `{ name, kind: "segment" }` (the WIDER single-segment kind —
21
21
  * over-matching is the fail-safe direction for a tighten); a name containing `/` ≡
22
22
  * `{ name, kind: "segment-run" }`. Spell `kind: "basename"` explicitly when last-segment-only
23
- * matching is the intent.
23
+ * matching is the intent. A name opening with the home word (`~/…`) refuses: the table places no row at a home.
24
24
  */
25
25
  export type WriteProtectedEntry = string | WriteProtectedRow;
26
26
  /** A table hit: which row matched (the row's canonical name + kind). */
@@ -108,6 +108,13 @@ export interface WriteProtectionJudgeContext {
108
108
  readonly base?: string;
109
109
  /** Bounds the filesystem probes of the target view. */
110
110
  readonly signal?: AbortSignal;
111
+ /** The path is a CANONICAL key the caller already resolved at the seat its program writes it (the shell read station's
112
+ * established write target: where the path lands, or — for a removal or a rename — the entry itself, its canonical
113
+ * parent joined with the last name, a link there not followed). It is judged where a key is judged — the readings and
114
+ * the canonical data-root exemption — never as a spelling (the lexical passthrough reads a spelling, and a key reached
115
+ * through a linked data root does not spell itself inside it) and never followed: no link chain is walked from it, as
116
+ * the program walks none from where it writes. Without an environment the key is judged as a spelling (the absence law). */
117
+ readonly canonical?: true;
111
118
  }
112
119
  /**
113
120
  * WHICH reading of a write's TARGET a compiled judge has — its own word, and the vocabulary the wiring
@@ -258,3 +265,6 @@ export interface WriteProtectionMount {
258
265
  * `deny > ask > allow` fold, which is what makes the missing seat cost nothing.
259
266
  */
260
267
  export declare function createWriteProtectionCheck(entries?: readonly WriteProtectedEntry[], opts?: WriteProtectionCompileOptions): WriteProtectionMount | undefined;
268
+ /** {@link createWriteProtectionCheck} over a judge already compiled — the engine's leg compiles once and hands the same
269
+ * judge to the call check and to the shell station (engine-internal; not re-exported from the package root). */
270
+ export declare function writeProtectionMountOver(matcher: WriteProtectionMatcher, rootPath: string | undefined): WriteProtectionMount;
@@ -3,6 +3,7 @@ import { protectivePathTargetOf } from "./tool-registry.js";
3
3
  import { pathTargetBaseOf } from "./effective-path-target.js";
4
4
  import { canonicalDataRootKeys, dataRootSpellings, relativeUnderRoot } from "./sensitive-path-policy.js";
5
5
  import { symlinkChainHops } from "./path-identity.js";
6
+ import { readDenyPatternRun } from "../tools/fs/read-deny.js";
6
7
  import { thrownText } from "./thrown-value.js";
7
8
  const freezeTable = (rows) => Object.freeze(rows.map((r) => Object.freeze(r)));
8
9
  export const WRITE_PROTECTED_DEFAULT_TABLE = freezeTable([
@@ -57,6 +58,7 @@ export const WRITE_PROTECTED_DEFAULT_TABLE = freezeTable([
57
58
  { name: ".gnupg", kind: "segment" },
58
59
  { name: ".sema.json", kind: "basename" },
59
60
  { name: ".sema", kind: "segment" },
61
+ { name: "credentials.env", kind: "basename" },
60
62
  ]);
61
63
  export function foldWriteProtectSegment(s) {
62
64
  const cased = s.toLowerCase().replace(/ı/g, "i").replace(/ſ/g, "s");
@@ -91,6 +93,11 @@ function normalizeWriteProtectedEntry(entry) {
91
93
  throw new Error(`writeProtectedPaths: entry ${describeEntryValue(entry)} has kind ${describeEntryValue(shape.kind)} — known kinds: ${KINDS.join(", ")}.`);
92
94
  }
93
95
  const kind = shape.kind;
96
+ const { homeAnchored, run } = readDenyPatternRun(name);
97
+ if (homeAnchored) {
98
+ throw new Error(`writeProtectedPaths: entry ${describeEntryValue(name)} opens with the home word "~", which this table does not place — its names are matched wherever their segments appear. ` +
99
+ (run.length > 0 ? `Spell the name without it (${describeEntryValue(run)}), which covers the home's file and every same-named one.` : "The home directory itself is not a row; spell the name of what it holds."));
100
+ }
94
101
  if (name.includes("*") || name.includes("?")) {
95
102
  throw new Error(`writeProtectedPaths: entry ${describeEntryValue(name)} contains a wildcard metacharacter — this table speaks LITERAL names only (a silently-literal "*" would guard less than it reads); pattern semantics live in createSensitivePathPolicy.`);
96
103
  }
@@ -254,7 +261,8 @@ export function compileWriteProtection(entries, opts) {
254
261
  return judgeSpelling(path, cwd);
255
262
  },
256
263
  async judge(path, ctx = {}) {
257
- const bySpelling = judgeSpelling(path, ctx.cwd);
264
+ const givenKey = ctx.canonical === true && env !== undefined;
265
+ const bySpelling = givenKey ? null : judgeSpelling(path, ctx.cwd);
258
266
  if (bySpelling !== null)
259
267
  return { kind: "hit", hit: bySpelling, via: "spelling" };
260
268
  if (env === undefined)
@@ -268,16 +276,18 @@ export function compileWriteProtection(entries, opts) {
268
276
  noted = true;
269
277
  opts?.onViewAbsent?.({ cause, unresolvedSymlink });
270
278
  };
271
- let canonKey;
272
- try {
273
- const canon = await canonicalizeTarget(env, path, ctx.signal, family, base);
274
- if (canon.ok)
275
- canonKey = canon.key;
276
- else
277
- noteAbsence(canon.message, canon.unresolvedSymlink === true);
278
- }
279
- catch (err) {
280
- noteAbsence(thrownText(err), false);
279
+ let canonKey = givenKey ? path : undefined;
280
+ if (!givenKey) {
281
+ try {
282
+ const canon = await canonicalizeTarget(env, path, ctx.signal, family, base);
283
+ if (canon.ok)
284
+ canonKey = canon.key;
285
+ else
286
+ noteAbsence(canon.message, canon.unresolvedSymlink === true);
287
+ }
288
+ catch (err) {
289
+ noteAbsence(thrownText(err), false);
290
+ }
281
291
  }
282
292
  try {
283
293
  if (canonKey !== undefined) {
@@ -295,7 +305,7 @@ export function compileWriteProtection(entries, opts) {
295
305
  return { kind: "hit", hit: byTarget, via: "target" };
296
306
  }
297
307
  }
298
- for (const hop of await symlinkChainHops(env, path, ctx.signal, family, base, (cause) => noteAbsence(cause, false))) {
308
+ for (const hop of givenKey ? [] : await symlinkChainHops(env, path, ctx.signal, family, base, (cause) => noteAbsence(cause, false))) {
299
309
  const byHop = judgeSpelled(hop);
300
310
  if (byHop === null)
301
311
  continue;
@@ -346,8 +356,9 @@ export function writeProtectionUnresolved(verdict) {
346
356
  }
347
357
  export function createWriteProtectionCheck(entries, opts) {
348
358
  const matcher = compileWriteProtection(entries, opts);
349
- if (matcher === undefined)
350
- return undefined;
359
+ return matcher === undefined ? undefined : writeProtectionMountOver(matcher, opts?.rootPath);
360
+ }
361
+ export function writeProtectionMountOver(matcher, rootPath) {
351
362
  const check = (toolName, args, face, cwd, signal) => {
352
363
  const call = { toolName, args, ...(face !== undefined ? { face } : {}) };
353
364
  const protective = protectivePathTargetOf(call);
@@ -356,7 +367,7 @@ export function createWriteProtectionCheck(entries, opts) {
356
367
  const path = writeTargetPath(call, protective);
357
368
  if (typeof path !== "string" || path.length === 0)
358
369
  return null;
359
- return matcher.judge(path, { ...(cwd !== undefined ? { cwd } : {}), ...(signal !== undefined ? { signal } : {}), ...(() => { const b = pathTargetBaseOf(protective, { root: opts?.rootPath, cwd }); return b !== undefined ? { base: b } : {}; })() });
370
+ return matcher.judge(path, { ...(cwd !== undefined ? { cwd } : {}), ...(signal !== undefined ? { signal } : {}), ...(() => { const b = pathTargetBaseOf(protective, { root: rootPath, cwd }); return b !== undefined ? { base: b } : {}; })() });
360
371
  };
361
372
  return { check, targetView: matcher.targetView };
362
373
  }
@@ -187,6 +187,9 @@ export declare class NodeExecutionEnv implements ExecutionEnv, BackgroundShellCa
187
187
  setFileMode(path: string, mode: number, abortSignal?: AbortSignal): Promise<Result<void, FileError>>;
188
188
  listDir(path: string, abortSignal?: AbortSignal): Promise<Result<FileInfo[], FileError>>;
189
189
  canonicalPath(path: string): Promise<Result<string, FileError>>;
190
+ /** {@link FileSystem.fileIdentity}: `stat` (links followed) read as BIGINT — an inode past 2^53 (an APFS volume root)
191
+ * is not exact as a number, and two inodes that collapse to one number would name two directories as one. */
192
+ fileIdentity(path: string): Promise<Result<string, FileError>>;
190
193
  readLink(path: string): Promise<Result<string, FileError>>;
191
194
  /**
192
195
  * The LEAF symlink chain ({@link FileSystem.canonicalChain}): `readlink` on the whole spelling, then on each
@@ -1,7 +1,7 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { closeSync, constants, createReadStream, mkdtempSync, openSync, readSync, statSync, truncateSync, unlinkSync } from "node:fs";
4
- import { access, appendFile, lstat, mkdir, mkdtemp, open, readdir, readFile, readlink, rename, rm, unlink, writeFile, } from "node:fs/promises";
4
+ import { access, appendFile, lstat, mkdir, mkdtemp, open, readdir, readFile, readlink, rename, rm, stat, unlink, writeFile, } from "node:fs/promises";
5
5
  import { tmpdir } from "node:os";
6
6
  import { dirname, isAbsolute, join, resolve, sep } from "node:path";
7
7
  import { createInterface } from "node:readline";
@@ -1010,6 +1010,16 @@ export class NodeExecutionEnv {
1010
1010
  return err(toFileError(error, resolved));
1011
1011
  }
1012
1012
  }
1013
+ async fileIdentity(path) {
1014
+ const resolved = resolvePath(this.cwd, path);
1015
+ try {
1016
+ const s = await stat(resolved, { bigint: true });
1017
+ return ok(`${s.dev}:${s.ino}`);
1018
+ }
1019
+ catch (error) {
1020
+ return err(toFileError(error, resolved));
1021
+ }
1022
+ }
1013
1023
  async readLink(path) {
1014
1024
  const resolved = resolvePath(this.cwd, path);
1015
1025
  try {
@@ -397,16 +397,15 @@ export interface FileSystem {
397
397
  listDir(path: string, abortSignal?: AbortSignal): Promise<Result<FileInfo[], FileError>>;
398
398
  /** Return the canonical path for an existing path, resolving symlinks where supported. */
399
399
  canonicalPath(path: string, abortSignal?: AbortSignal): Promise<Result<string, FileError>>;
400
- /**
401
- * VENDORED EDIT (design/72 §2.1, additive/backward-compatible): read a symlink's raw target (one
402
- * hop, NOT followed). Optional — an env that omits it falls back to best-effort canonicalization
403
- * for a DANGLING leaf symlink (a symlink whose target doesn't exist yet, which `canonicalPath`'s
404
- * `realpath` can't resolve). Needed so a sensitive-path guard / containment check sees the real
405
- * target of `write_file innocent.txt` where `innocent.txt -> .git/hooks/payload`.
406
- */
400
+ /** OPTIONAL: a symlink's raw target, one hop, not followed — so a path guard sees where `innocent.txt -> .git/hooks/payload`
401
+ * writes although the target does not exist yet (`canonicalPath`'s `realpath` cannot resolve a DANGLING leaf link).
402
+ * Omitted ⇒ such a leaf is canonicalized best-effort. */
407
403
  readLink?(path: string, abortSignal?: AbortSignal): Promise<Result<string, FileError>>;
408
404
  /** OPTIONAL: the LEAF symlink chain of `path` ({@link SymlinkChain}). Omitted, or `ok: false` ⇒ the path judges read the spelling and the canonical terminal only (the reach they had before this seat); a remote adapter answers in one `readlink` round trip and a caller never emulates it through {@link readLink}; bounded (64 hops, a cycle ends it), a network hop kept unprobed. */
409
405
  canonicalChain?(path: string, abortSignal?: AbortSignal): Promise<Result<SymlinkChain, FileError>>;
406
+ /** OPTIONAL: the identity of the object `path` names, links followed (`<device>:<inode>` on POSIX, exact): equal answers name one object, where
407
+ * a firmlink or bind mount keeps its own {@link canonicalPath}. Home-anchored deny rows are placed by it; omitted ⇒ by spelling only. */
408
+ fileIdentity?(path: string, abortSignal?: AbortSignal): Promise<Result<string, FileError>>;
410
409
  /** Return false for missing paths. Other errors, such as permission failures, return a {@link FileError}. */
411
410
  exists(path: string, abortSignal?: AbortSignal): Promise<Result<boolean, FileError>>;
412
411
  /** Create a directory. Defaults: `recursive: true`, no abort signal. */
@@ -144,10 +144,8 @@ export interface StreamOptions {
144
144
  * (see BrainTimeoutConfig.firstTokenTimeoutMs, design/17 O5).
145
145
  */
146
146
  stallTimeouts?: StallTimeouts;
147
- /**
148
- * Maximum retry attempts for providers/SDKs that support client-side retries.
149
- * For example, OpenAI and Anthropic SDK clients default to 2.
150
- */
147
+ /** Per-call cap on the brain's transient retry budget: this call retries at most this many times, never more than
148
+ * the brain is configured for. */
151
149
  maxRetries?: number;
152
150
  /**
153
151
  * Maximum delay in milliseconds to wait for a retry when the server requests a long wait.
@@ -765,6 +765,13 @@ export interface AgentTool<TParameters extends TSchema = TSchema, TDetails = unk
765
765
  * keyed by the folded-away spelling must stay inert rather than land on the other tool. Absent on non-MCP tools.
766
766
  */
767
767
  mcpRemoteName?: string;
768
+ /**
769
+ * The MCP server's own read-only declaration (`annotations.readOnlyHint === true` on its tools/list entry), set by
770
+ * the MCP materializer. It answers ONE question: whether the `plan` mode leaves the tool to run (the reference's
771
+ * `isReadOnly` for a server's tool). It is never the tool's effect — a server's self-claim does not lower the
772
+ * repeat-safety effect below its fail-closed `write` default (only the caller's `toolAxes` does). Absent on non-MCP tools.
773
+ */
774
+ mcpReadOnlyHint?: true;
768
775
  /**
769
776
  * design/120 P1.5 (CC `isConcurrencySafe(parsedInput)` parity): INPUT-level concurrency refinement
770
777
  * for a tool whose safety depends on its arguments — CC's only real consumer is
package/dist/index.d.ts CHANGED
@@ -62,6 +62,12 @@ export { probeMcpServers, type McpProbeResult } from "./core/mcp-probe.js";
62
62
  export { MCP_FAILURE_KINDS, MCP_DELIVERY_VERDICTS, MCP_LIVENESS_STATES, classifyMcpFailure, type McpFailure, type McpFailureKind, type McpDelivered, type McpLivenessState, type McpServerLiveness } from "./core/mcp-failure.js";
63
63
  export { MCP_INJECTION_DROP_REASONS, MCP_INJECTION_DROP_TEXT, mcpInjectionDroppedNotice, type McpInjectionDropReason, type McpInjectionDropFacts } from "./core/mcp-injection-drop.js";
64
64
  export { askUnresolvableNotice, type AskUnresolvableFacts } from "./core/ask-unresolvable-notice.js";
65
+ export { ASK_UNRESOLVABLE_CAUSES } from "./core/ask-unresolvable-notice.js";
66
+ export { DURABLE_MANDATE_SOURCES } from "./core/tool-policy.js";
67
+ export { TERMINAL_CAUSE_IS_REPLAYABLE } from "./core/terminal-cause.js";
68
+ export { RESERVED_AGENT_NAMES } from "./agents/subagent.js";
69
+ export { WORKFLOW_SIZE_GUIDELINE_AGENT_CAPS } from "./orchestration/workflow-size-guideline.js";
70
+ export { ENGINE_AUTHORITY_ENVELOPE_TAGS } from "./core/untrusted-text.js";
65
71
  export { materializeA2aTools, A2aRpcError, type MaterializedA2a, type A2aPeerStatus, type A2aRefreshResult, type A2aToolAxis } from "./core/a2a.js";
66
72
  export { PROTOCOL_TABLE, MCP_NAMESPACE, A2A_NAMESPACE, protocolOf, type ProtocolNamespace, type ProtocolId } from "./core/protocol-table.js";
67
73
  export { InMemorySessionPolicyStore, SessionPolicyError, loosenReasons, normalizeRules, stripRev, type SessionPolicyStore, type SessionPermissionRules, type StoredSessionRules, type SessionRulesRecord, type PutRulesOptions, } from "./core/session-policy-store.js";
@@ -104,7 +110,7 @@ export type { InvariantKind, FunctionContract, Invariant, InvariantViolation, Ch
104
110
  export { bashReversibilityProbe, BASH_READONLY_DEFAULT_ALLOW, parseLeadingCommandName, classifyCompoundReadonly, MAX_EDIT_BYTES } from "./tools/fs/index.js";
105
111
  export { classifyCompoundReadonlyDetailed, formatOutOfRootReadApprovalOption, type BashReadonlyRootBoundary, type CompoundReadonlyVerdict, } from "./tools/fs/index.js";
106
112
  export { resolveBashTimeoutCaps } from "./tools/fs/index.js";
107
- export { READ_FACE_DEFAULT_DENY_ENTRIES, READ_FACE_BUILTIN_DENY_TABLE, READ_DENY_BUILTIN_TIERS, READ_DENY_DEFAULT_TIERS, resolveReadDenyBuiltins, compileReadDeny, type ReadDenyEntry, type ReadDenyMatcher, type NormalizedReadDenyEntry, type ReadDenyBuiltinTier, type ReadDenyBuiltinRow, type ReadDenyBuiltinConfig, } from "./tools/fs/index.js";
113
+ export { READ_FACE_DEFAULT_DENY_ENTRIES, READ_FACE_BUILTIN_DENY_TABLE, READ_DENY_BUILTIN_TIERS, READ_DENY_DEFAULT_TIERS, resolveReadDenyBuiltins, compileReadDeny, READ_DENY_HOME_WORD, readDenyPatternRun, readDenyHomesOf, compileWriteDeny, DENY_FACES, type DenyFace, type PathRuleDenyRow, type WriteDenyMatcher, type ReadDenyEntry, type ReadDenyMatcher, type NormalizedReadDenyEntry, type ReadDenyBuiltinTier, type ReadDenyBuiltinRow, type ReadDenyBuiltinConfig, } from "./tools/fs/index.js";
108
114
  export { deploymentFaceClampNotice, resolveContainmentFace, type ContainmentFace, type ContainmentFaceInputs, type ContainmentFaceKind, type ContainmentFaceLayer, } from "./tools/fs/index.js";
109
115
  export { WRITE_PROTECTED_DEFAULT_TABLE, resolveWriteProtectedTable, compileWriteProtection, type WriteProtectedEntry, type WriteProtectedRow, type WriteProtectedKind, type WriteProtectedHit, type WriteProtectionMatcher, type WriteProtectionVerdict, type WriteProtectionViewAbsence, type WriteProtectionJudgeContext, type WriteProtectionCompileOptions, type WriteProtectionCheck, WRITE_PROTECTION_TARGET_VIEWS, type WriteProtectionTargetView, type WriteProtectionMount, } from "./core/write-protect.js";
110
116
  export { InMemoryToolResultStore, OFFLOAD_TOOL_NAME, DEFAULT_TOOL_RESULT_THRESHOLD_CHARS, assertSafeToolResultRef, buildToolResultRef, toolResultContentSegment, MAX_MINTED_TOOL_RESULT_REF_CHARS, type ToolResultProvenance, assertToolResultProvenanceMatch, normalizeToolResultProvenance, toolResultProvenanceOf, ToolResultRefConflictError, TOOL_RESULT_REF_CONFLICT_CODE, type ToolResultStore, type ToolResultSlice, type ToolResultDeletionReport, } from "./core/tool-result-store.js";
@@ -183,14 +189,18 @@ export { createAllowDenyPolicy, createApprovalPolicy,
183
189
  * members that seat has no lane for (`"unavailable"`, a routing answer; `updatedInput`, an operator edit a
184
190
  * policy verdict cannot get re-adjudicated). Exported so a host can NAME it instead of deriving it through
185
191
  * `Parameters<typeof createApprovalPolicy>[0]["approve"]`. */
186
- type ApprovalSeatOutcome, COARSE_SHELL_TOOLS, createCoarseCommandNamePolicy, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, findUnverifiableRecursiveDelete, combinePolicies, decisionText, resolveAsk, toolPolicyNameSets, type ToolPolicyNameSets, type NamedToolPolicy, type ToolPolicyProjection, type ToolPolicyProjectionComponent, type ConstraintChainEntry, checkToolPolicyProjection, constraintChainEntryOf, constraintChainDigest, type ToolPolicy, type ToolCallRequest, type PermissionResult, type DecisionReason, screenApproverAttribution, APPROVER_ATTRIBUTION_MAX_CHARS, type OnAsk, type AskOutcome, type ResolvedAsk, type AskRequest, type AskDelegationProvenance, type AskRuleEvidence, type AskEvidenceAbsence, ASK_EVIDENCE_ABSENCE_VALUES, } from "./core/tool-policy.js";
192
+ type ApprovalSeatOutcome, COARSE_SHELL_TOOLS, createCoarseCommandNamePolicy, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, findUnverifiableRecursiveDelete, combinePolicies, decisionText, resolveAsk, toolPolicyNameSets, toolPolicyPathDenyRules, type ToolPolicyNameSets, type NamedToolPolicy, type ToolPolicyProjection, type ToolPolicyProjectionComponent, type ConstraintChainEntry, checkToolPolicyProjection, constraintChainEntryOf, constraintChainDigest, type ToolPolicy, type ToolCallRequest, type PermissionResult, type DecisionReason, screenApproverAttribution, APPROVER_ATTRIBUTION_MAX_CHARS, type OnAsk, type AskOutcome, type ResolvedAsk, type AskRequest, type AskDelegationProvenance, type AskRuleEvidence, type AskEvidenceAbsence, ASK_EVIDENCE_ABSENCE_VALUES, } from "./core/tool-policy.js";
187
193
  export { type ReadRootGrantScreen } from "./core/read-root-grant-screen.js";
188
- export { type AskOrigin, ASK_ORIGINS, isAskOrigin, classifierMayAnswer, ORIGIN_IMPLIES_REAL_APPROVAL, RULE_STORE_UNREADABLE_KINDS, type RuleStoreUnreadable, isRuleStoreUnreadable, PERSISTED_RULE_MANDATES, type PersistedRuleMandate, isPersistedRuleMandate } from "./core/ask-origin.js";
194
+ export { type AskOrigin, ASK_ORIGINS, isAskOrigin, ORIGIN_IMPLIES_REAL_APPROVAL, PERSISTED_RULE_MANDATES, type PersistedRuleMandate, isPersistedRuleMandate } from "./core/ask-origin.js";
195
+ export { PERMISSION_MODES, type PermissionMode, isPermissionMode, PERMISSION_MODE_RANK, clampPermissionMode, ASK_CLASSES, type AskClass, isAskClass, MODE_ARBITRATION, type ModeArbitrationRow, RELEASABLE_ASK_CLASSES, RRA_EXPLAINERS, classifyAsk, foldArbitrationRow, type AskClassification, type AskClassFacts, type ToolMarkSource, arbitrateByMode, type Arbitration, fileCommandOptionsSentence } from "./core/ask-origin.js";
196
+ export { modePolicyOf, type ModePolicy, PERMISSION_MODE_FOLD_REASONS, type PermissionModeFoldReason, type PermissionModeReceipt, ACCEPT_EDITS_FILE_COMMANDS, modeAcceptsFileCommand } from "./core/mode-policy.js";
197
+ export { readDenyClassifierRules, renderClassifierDenyRules, type DerivedDenyRule } from "./core/auto-mode-prompt.js";
189
198
  export { AUTO_MODE_DENY_SOURCES, isAutoModeDenySource, type AutoModeDenySource } from "./core/runtime-caps.js";
190
- export { SETTLEMENT_KINDS, type SettlementKind, isSettlementKind, type Settlement, SETTLEMENT_IS_REFUSAL, DENIED_BY_VALUES, type DeniedBy, isDeniedBy, DENIED_BY_MAY_VETO, CLASSIFIER_DENY_CAUSES, type ClassifierDenyCause, isClassifierDenyCause, DENIED_BY_MAY_CARRY_CAUSE, type GateDisposition, type GateOutcome, screenGateOutcome } from "./core/gate-outcome.js";
199
+ export { SETTLEMENT_KINDS, type SettlementKind, isSettlementKind, type Settlement, SETTLEMENT_IS_REFUSAL, DENIED_BY_VALUES, type DeniedBy, isDeniedBy, DENIED_BY_MAY_VETO, CLASSIFIER_DENY_CAUSES, type ClassifierDenyCause, isClassifierDenyCause, DENIED_BY_MAY_CARRY_CAUSE, type GateDisposition, type GateOutcome, type ModeRelease, type ModeRefusal, screenGateOutcome } from "./core/gate-outcome.js";
191
200
  export { type AskCarry } from "./core/hooks.js";
192
201
  export { SWAPPABLE_DEP_SEATS, SWAP_SEAT_NOTICE, isSwappableDepSeat, screenSwappableDeps, type SwappableDepSeat, type SwappableDeps } from "./core/swappable-deps.js";
193
202
  export { parseAutoModeResponse, createAutoModeDecider, AUTO_MODE_UNAVAILABLE_CAUSES, isAutoModeUnavailableCause, classifierUnavailableDenyMessage, classifierParseFailureDenyMessage, AUTO_MODE_PARSE_FAILURES, isAutoModeParseFailure, type AutoModeParseFailure, type AutoModeUnavailableCause, AUTO_MODE_CLASSIFIER_FALLBACKS, isAutoModeClassifierFallback, AUTO_MODE_FALLBACK_CAUSES, isAutoModeFallbackCause, type AutoModeClassifierFallback, type AutoModeFallbackCause, type AutoModeClassifierRound, type AutoModeLadderRung, type AutoModeVerdict, type AutoModeDecider, type AutoModeDeciderOptions, type AutoModeClassified, type AutoModeClassifyFn, type AutoModeClassifyInput, createAutoModeDenialTracker, denialLimitFallbackMessage, denialLimitSentence, unarmedWindow, type AutoModeDenialTracker, type AutoModeDenialLimitOptions, type DenialLimitCounts, type DenialLimitFallbackFace, type UnarmedDenialLimitFallback, type DenialLimitFallback, type DenialLimitVerdict, } from "./core/auto-mode.js";
203
+ export type { ClassifierUnavailableSeat, ClassifierUnavailableInfo, ClassifierUnavailableDisposition, ClassifierUnavailableObserver } from "./core/classifier-unavailable-arm.js";
194
204
  export { AUTO_MODE_DENIAL_LIMIT_DEFAULTS, AUTO_MODE_DENIAL_AUTO_DENY_DEFAULT_MS, AUTO_MODE_CLASSIFIER_MAX_TOKENS } from "./core/auto-mode-defaults.js";
195
205
  export { classifierSeatModel, type ClassifierSeat } from "./core/runner/classifier-ladder.js";
196
206
  export { buildAutoModePrompt, renderAutoModeWindow, renderAutoModeAction, AUTO_MODE_DEFAULTS_SENTINEL, type AutoModeRules, type BuildAutoModePromptOptions, type AutoModeWindowOptions, } from "./core/auto-mode-prompt.js";
@@ -352,7 +362,7 @@ export { ROUTE_ADJUDICATION_CONFORMANCE_CORPUS, type RouteAdjudicationVector } f
352
362
  export { type BrainTimeoutConfig } from "./brain/timeout.js";
353
363
  export { createAssistantMessageEventStream } from "./internal/llm.js";
354
364
  export type { AssistantMessage, AssistantMessageEvent, CompleteSimpleFn, Context, DocumentContent, ImageContent, Message, StopReason, StreamFn, TextContent, ThinkingContent, ToolCall, ToolResultMessage, Usage, UserMessage, } from "./internal/llm.js";
355
- export type { AgentDefinition, BeforeWriteHook, BeforeWriteRequest, BeforeWriteResult, TrackFileEditHook, TrackEditRequest, TrackEditResult, FileEditedHook, FileEditedNotice, HandsBandOptions, Brain, BrainStatus, BrainStatusPhase, BrainRetryErrClass, ImageInput, McpElicitRequest, McpElicitResponse, McpServerSpec, A2aServerSpec, OnElicit, Model, ModelRef, ProjectMemoryLoad, RunnerDeps, ResumePreflightInfo, ResumePreflightVerdict, EngineNotice, RuntimeCaps, BackgroundChildEvent, DelegationLifecycleEvent, SkillManifest, SkillSpec, TaskEvent, TaskEventIdentity, ToolActivity, TaskLimits, StaleToolResultOffloadOptions, TaskResult, TerminalCause, PausedCause, EffectiveMemoryScopes, RemoteEnvFailureNote, TaskSpec, ShellGateDoctrine, ApproverPosture, TaskStatus, TaskStream, CompactOutcome, ThinkingLevel, ToolExecuteContext, ToolReturn, ToolSpec, ToolInputVerdict, ToolInputValidationContext, ReversibilityVerdict, ReadRootGrantCandidate, ToolEffect, ToolContentOrigin, WorkflowGovernanceBaseline, DelegationTaskType, } from "./core/types.js";
365
+ export type { AgentDefinition, BeforeWriteHook, BeforeWriteRequest, BeforeWriteResult, TrackFileEditHook, TrackEditRequest, TrackEditResult, FileEditedHook, FileEditedNotice, HandsBandOptions, Brain, BrainStatus, BrainStatusPhase, BrainRetryErrClass, ImageInput, McpElicitRequest, McpElicitResponse, McpServerSpec, A2aServerSpec, OnElicit, Model, ModelRef, ProjectMemoryLoad, RunnerDeps, ResumePreflightInfo, ResumePreflightVerdict, EngineNotice, RuntimeCaps, BackgroundChildEvent, DelegationLifecycleEvent, SkillManifest, SkillSpec, TaskEvent, TaskEventIdentity, ToolActivity, TaskLimits, StaleToolResultOffloadOptions, TaskResult, TerminalCause, PausedCause, EffectiveMemoryScopes, RemoteEnvFailureNote, TaskSpec, ShellGateDoctrine, TaskStatus, TaskStream, CompactOutcome, ThinkingLevel, ToolExecuteContext, ToolReturn, ToolSpec, ToolInputVerdict, ToolInputValidationContext, ReversibilityVerdict, ReadRootGrantCandidate, ToolEffect, ToolContentOrigin, WorkflowGovernanceBaseline, DelegationTaskType, } from "./core/types.js";
356
366
  export { Type } from "typebox";
357
367
  export type { TSchema, Static } from "typebox";
358
368
  export { explainPromptAssembly, describeDefaultPack, type DefaultPackDescription, type ExplainInput } from "./prompt-assembly/explain.js";
package/dist/index.js CHANGED
@@ -48,6 +48,12 @@ export { probeMcpServers } from "./core/mcp-probe.js";
48
48
  export { MCP_FAILURE_KINDS, MCP_DELIVERY_VERDICTS, MCP_LIVENESS_STATES, classifyMcpFailure } from "./core/mcp-failure.js";
49
49
  export { MCP_INJECTION_DROP_REASONS, MCP_INJECTION_DROP_TEXT, mcpInjectionDroppedNotice } from "./core/mcp-injection-drop.js";
50
50
  export { askUnresolvableNotice } from "./core/ask-unresolvable-notice.js";
51
+ export { ASK_UNRESOLVABLE_CAUSES } from "./core/ask-unresolvable-notice.js";
52
+ export { DURABLE_MANDATE_SOURCES } from "./core/tool-policy.js";
53
+ export { TERMINAL_CAUSE_IS_REPLAYABLE } from "./core/terminal-cause.js";
54
+ export { RESERVED_AGENT_NAMES } from "./agents/subagent.js";
55
+ export { WORKFLOW_SIZE_GUIDELINE_AGENT_CAPS } from "./orchestration/workflow-size-guideline.js";
56
+ export { ENGINE_AUTHORITY_ENVELOPE_TAGS } from "./core/untrusted-text.js";
51
57
  export { materializeA2aTools, A2aRpcError } from "./core/a2a.js";
52
58
  export { PROTOCOL_TABLE, MCP_NAMESPACE, A2A_NAMESPACE, protocolOf } from "./core/protocol-table.js";
53
59
  export { InMemorySessionPolicyStore, SessionPolicyError, loosenReasons, normalizeRules, stripRev, } from "./core/session-policy-store.js";
@@ -82,7 +88,7 @@ export { deriveInvariants, checkInvariants } from "./core/property-harness.js";
82
88
  export { bashReversibilityProbe, BASH_READONLY_DEFAULT_ALLOW, parseLeadingCommandName, classifyCompoundReadonly, MAX_EDIT_BYTES } from "./tools/fs/index.js";
83
89
  export { classifyCompoundReadonlyDetailed, formatOutOfRootReadApprovalOption, } from "./tools/fs/index.js";
84
90
  export { resolveBashTimeoutCaps } from "./tools/fs/index.js";
85
- export { READ_FACE_DEFAULT_DENY_ENTRIES, READ_FACE_BUILTIN_DENY_TABLE, READ_DENY_BUILTIN_TIERS, READ_DENY_DEFAULT_TIERS, resolveReadDenyBuiltins, compileReadDeny, } from "./tools/fs/index.js";
91
+ export { READ_FACE_DEFAULT_DENY_ENTRIES, READ_FACE_BUILTIN_DENY_TABLE, READ_DENY_BUILTIN_TIERS, READ_DENY_DEFAULT_TIERS, resolveReadDenyBuiltins, compileReadDeny, READ_DENY_HOME_WORD, readDenyPatternRun, readDenyHomesOf, compileWriteDeny, DENY_FACES, } from "./tools/fs/index.js";
86
92
  export { deploymentFaceClampNotice, resolveContainmentFace, } from "./tools/fs/index.js";
87
93
  export { WRITE_PROTECTED_DEFAULT_TABLE, resolveWriteProtectedTable, compileWriteProtection, WRITE_PROTECTION_TARGET_VIEWS, } from "./core/write-protect.js";
88
94
  export { InMemoryToolResultStore, OFFLOAD_TOOL_NAME, DEFAULT_TOOL_RESULT_THRESHOLD_CHARS, assertSafeToolResultRef, buildToolResultRef, toolResultContentSegment, MAX_MINTED_TOOL_RESULT_REF_CHARS, assertToolResultProvenanceMatch, normalizeToolResultProvenance, toolResultProvenanceOf, ToolResultRefConflictError, TOOL_RESULT_REF_CONFLICT_CODE, } from "./core/tool-result-store.js";
@@ -148,9 +154,12 @@ export { hasScheduler, isValidCronExpr, SchedulerError } from "./core/scheduler.
148
154
  export { createSchedulerTools, SCHEDULE_WAKEUP_TOOL_NAME, AUTONOMOUS_LOOP_SENTINEL, AUTONOMOUS_LOOP_DYNAMIC_SENTINEL, } from "./tools/scheduler-tools.js";
149
155
  export { resolveAutonomousLoopPrompt, AUTONOMOUS_LOOP_PREAMBLE, AUTONOMOUS_LOOP_PREAMBLE_PERSISTENT, } from "./tools/loop-tick.js";
150
156
  export { tightenTaskSpec, TaskSpecTightenError } from "./core/tighten-task-spec.js";
151
- export { createAllowDenyPolicy, createApprovalPolicy, COARSE_SHELL_TOOLS, createCoarseCommandNamePolicy, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, findUnverifiableRecursiveDelete, combinePolicies, decisionText, resolveAsk, toolPolicyNameSets, checkToolPolicyProjection, constraintChainEntryOf, constraintChainDigest, screenApproverAttribution, APPROVER_ATTRIBUTION_MAX_CHARS, ASK_EVIDENCE_ABSENCE_VALUES, } from "./core/tool-policy.js";
157
+ export { createAllowDenyPolicy, createApprovalPolicy, COARSE_SHELL_TOOLS, createCoarseCommandNamePolicy, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, findUnverifiableRecursiveDelete, combinePolicies, decisionText, resolveAsk, toolPolicyNameSets, toolPolicyPathDenyRules, checkToolPolicyProjection, constraintChainEntryOf, constraintChainDigest, screenApproverAttribution, APPROVER_ATTRIBUTION_MAX_CHARS, ASK_EVIDENCE_ABSENCE_VALUES, } from "./core/tool-policy.js";
152
158
  export {} from "./core/read-root-grant-screen.js";
153
- export { ASK_ORIGINS, isAskOrigin, classifierMayAnswer, ORIGIN_IMPLIES_REAL_APPROVAL, RULE_STORE_UNREADABLE_KINDS, isRuleStoreUnreadable, PERSISTED_RULE_MANDATES, isPersistedRuleMandate } from "./core/ask-origin.js";
159
+ export { ASK_ORIGINS, isAskOrigin, ORIGIN_IMPLIES_REAL_APPROVAL, PERSISTED_RULE_MANDATES, isPersistedRuleMandate } from "./core/ask-origin.js";
160
+ export { PERMISSION_MODES, isPermissionMode, PERMISSION_MODE_RANK, clampPermissionMode, ASK_CLASSES, isAskClass, MODE_ARBITRATION, RELEASABLE_ASK_CLASSES, RRA_EXPLAINERS, classifyAsk, foldArbitrationRow, arbitrateByMode, fileCommandOptionsSentence } from "./core/ask-origin.js";
161
+ export { modePolicyOf, PERMISSION_MODE_FOLD_REASONS, ACCEPT_EDITS_FILE_COMMANDS, modeAcceptsFileCommand } from "./core/mode-policy.js";
162
+ export { readDenyClassifierRules, renderClassifierDenyRules } from "./core/auto-mode-prompt.js";
154
163
  export { AUTO_MODE_DENY_SOURCES, isAutoModeDenySource } from "./core/runtime-caps.js";
155
164
  export { SETTLEMENT_KINDS, isSettlementKind, SETTLEMENT_IS_REFUSAL, DENIED_BY_VALUES, isDeniedBy, DENIED_BY_MAY_VETO, CLASSIFIER_DENY_CAUSES, isClassifierDenyCause, DENIED_BY_MAY_CARRY_CAUSE, screenGateOutcome } from "./core/gate-outcome.js";
156
165
  export {} from "./core/hooks.js";
@@ -298,7 +298,7 @@ function pickWhitelist(scriptSpec) {
298
298
  if (!Array.isArray(readDenyPatternsRaw)) {
299
299
  throw new WorkflowScriptError('agent(spec): `readDenyPatterns` must be an array of deny entries (a "/"-separated segment run such as ".ssh", or { pattern, caseSensitive? })');
300
300
  }
301
- const entries = mapUntrustedArray(readDenyPatternsRaw, "agent(spec): `readDenyPatterns`", MAX_SCRIPT_DENY_ENTRIES, "Every entry is judged against every path the child reads, for the child's whole run; the deployment's own entries, and whichever built-in tiers the deployment switched on (none by default), are in force on top of these.", pickReadDenyEntry);
301
+ const entries = mapUntrustedArray(readDenyPatternsRaw, "agent(spec): `readDenyPatterns`", MAX_SCRIPT_DENY_ENTRIES, "Every entry is judged against every path the child reads, for the child's whole run; the deployment's own entries, and whichever built-in tiers the deployment has on (by default the credentials and home agent-config tiers), are in force on top of these.", pickReadDenyEntry);
302
302
  try {
303
303
  compileReadDeny(entries, "agent(spec).readDenyPatterns");
304
304
  }
@@ -52,7 +52,7 @@ parentReadFace?: () => TaskSpec["readFace"],
52
52
  * stricter-wins into every spawned child on its own seat (the two seats never read each other). */
53
53
  parentWriteFace?: () => TaskSpec["writeFace"],
54
54
  /** Twin of the above for the deny-set additions (the extra entries the host's own deployment/task
55
- * layers stacked on; the built-in rows the DEPLOYMENT switched on — none by default — apply beside them). */
55
+ * layers stacked on; the built-in rows the DEPLOYMENT's tier selection activates apply beside them). */
56
56
  parentReadDenyPatterns?: () => TaskSpec["readDenyPatterns"],
57
57
  /** #345 ③ — the HOST task's write-hands clamp is ON (`spec.handsReadOnly === true`). Injected
58
58
  * tighten-only into every spawned child, like the read-face clamp above; see the injection
@@ -1,10 +1,10 @@
1
1
  export const WORKFLOW_SIZE_GUIDELINES = ["unrestricted", "small", "medium", "large"];
2
2
  export const WORKFLOW_SIZE_GUIDELINE_DEFAULT = "medium";
3
- export const WORKFLOW_SIZE_GUIDELINE_AGENT_CAPS = {
3
+ export const WORKFLOW_SIZE_GUIDELINE_AGENT_CAPS = Object.freeze({
4
4
  small: 5,
5
5
  medium: 15,
6
6
  large: 50,
7
- };
7
+ });
8
8
  export function normalizeWorkflowSizeGuideline(value) {
9
9
  return WORKFLOW_SIZE_GUIDELINES.includes(value) ? value : undefined;
10
10
  }
@@ -1,3 +1,12 @@
1
+ /**
2
+ * The `Artifact` tool: one tool, `action`-multiplexed, assembled from the host's capability declaration.
3
+ * The action enum, the parameter set and the description are all functions of `capabilities` — the
4
+ * implementation behind the seam is never inspected. Session state (the version tracked per URL and the
5
+ * local-path → URL binding that makes "same file path ⇒ same page" hold) lives in this factory's closure,
6
+ * fresh per run, never checkpointed.
7
+ */
8
+ import { type PermissionMode } from "../../core/ask-origin.js";
9
+ import { type EngineNotice } from "../../core/engine-notice.js";
1
10
  import { type ArtifactHost, type ArtifactHostCapabilities } from "../../core/artifact-host.js";
2
11
  import { type OnAsk } from "../../core/tool-policy.js";
3
12
  import type { ToolSpec } from "../../core/types.js";
@@ -42,6 +51,14 @@ export interface ArtifactToolOptions {
42
51
  * = the run has no ask face, and a shared read is then REFUSED fail-closed (`artifact.refused`, content withheld) —
43
52
  * never silently delivered. */
44
53
  onAsk?: OnAsk;
54
+ /** The run's effective permission mode, read at each shared-read consent (a steer moves it). The consent ask is
55
+ * arbitrated as the gate's asks are (class `artifact_shared_read`): `bypassPermissions` RELEASES the read — delivered,
56
+ * announced once per run through `onNotice`, and NOT remembered as an approval (a mode release is not a person's
57
+ * nod; a later `default` leg asks) — `dontAsk` refuses it, every other mode asks the approver. Absent ⇒ no arbitration
58
+ * (the approver decides, or the read is withheld with none). */
59
+ permissionMode?: () => PermissionMode;
60
+ /** The notice sink the release account rides (`permission.ask_released_by_mode`, once per run for this class). */
61
+ onNotice?: (notice: EngineNotice) => void;
45
62
  }
46
63
  /** Supporting files per publish (upstream cap); enforced in the schema AND in the body, for callers that bypass the schema. */
47
64
  export declare const MAX_FILES_PER_PUBLISH = 255;