@sema-agent/core 7.15.0 → 7.17.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 (287) hide show
  1. package/CHANGELOG.md +202 -0
  2. package/dist/agents/builtin-agents.d.ts +3 -3
  3. package/dist/agents/cross-session-envelope.d.ts +0 -1
  4. package/dist/agents/cross-session-envelope.js +0 -1
  5. package/dist/agents/peer-notices.d.ts +0 -9
  6. package/dist/agents/peer-notices.js +0 -63
  7. package/dist/agents/subagent.js +7 -15
  8. package/dist/brain/request-params.d.ts +2 -2
  9. package/dist/config/defaults.d.ts +0 -7
  10. package/dist/config/defaults.js +0 -2
  11. package/dist/core/artifact-host.d.ts +218 -0
  12. package/dist/core/artifact-host.js +92 -0
  13. package/dist/core/ask-origin.d.ts +29 -3
  14. package/dist/core/ask-origin.js +17 -9
  15. package/dist/core/ask-question.d.ts +25 -0
  16. package/dist/core/ask-question.js +4 -0
  17. package/dist/core/ask-unresolvable-notice.d.ts +30 -21
  18. package/dist/core/ask-unresolvable-notice.js +16 -8
  19. package/dist/core/auto-compaction.d.ts +10 -10
  20. package/dist/core/auto-mode-arming.d.ts +2 -2
  21. package/dist/core/background-agent-store.d.ts +5 -5
  22. package/dist/core/background-shell.d.ts +1 -1
  23. package/dist/core/checkpoint-execution-record.d.ts +0 -3
  24. package/dist/core/checkpoint-execution-record.js +0 -4
  25. package/dist/core/checkpoint-store.d.ts +71 -99
  26. package/dist/core/checkpoint-store.js +9 -16
  27. package/dist/core/context-edit.d.ts +36 -104
  28. package/dist/core/context-edit.js +37 -60
  29. package/dist/core/effective-path-target.d.ts +68 -8
  30. package/dist/core/effective-path-target.js +42 -30
  31. package/dist/core/engine-notice.d.ts +46 -32
  32. package/dist/core/env-budget.d.ts +31 -0
  33. package/dist/core/env-budget.js +39 -0
  34. package/dist/core/exec-output-tail.d.ts +5 -5
  35. package/dist/core/file-history-store.js +3 -6
  36. package/dist/core/fs-write-gate-policy.d.ts +1 -1
  37. package/dist/core/fs-write-gate-policy.js +3 -3
  38. package/dist/core/gate-lanes.js +9 -15
  39. package/dist/core/governance-codes.d.ts +1 -1
  40. package/dist/core/governance-codes.js +4 -0
  41. package/dist/core/hands-band.d.ts +1 -1
  42. package/dist/core/hooks.d.ts +18 -17
  43. package/dist/core/lsp-session.d.ts +1 -1
  44. package/dist/core/lsp.d.ts +1 -1
  45. package/dist/core/lsp.js +5 -4
  46. package/dist/core/mcp.d.ts +5 -5
  47. package/dist/core/mcp.js +4 -48
  48. package/dist/core/memory-engine/delegation-settlement.d.ts +37 -12
  49. package/dist/core/memory-engine/delegation-settlement.js +128 -39
  50. package/dist/core/memory-engine/engine.d.ts +9 -8
  51. package/dist/core/memory-engine/engine.js +115 -120
  52. package/dist/core/memory-engine/file-backend.d.ts +17 -5
  53. package/dist/core/memory-engine/file-backend.js +78 -34
  54. package/dist/core/memory-engine/index.d.ts +2 -2
  55. package/dist/core/memory-engine/index.js +2 -2
  56. package/dist/core/memory-engine/layout.d.ts +152 -54
  57. package/dist/core/memory-engine/layout.js +441 -104
  58. package/dist/core/memory-engine/types.d.ts +8 -3
  59. package/dist/core/memory-engine/types.js +5 -0
  60. package/dist/core/memory-recall.d.ts +0 -8
  61. package/dist/core/memory.d.ts +1 -1
  62. package/dist/core/oracle-isolation.js +2 -2
  63. package/dist/core/parent-spec-seat.d.ts +66 -0
  64. package/dist/core/parent-spec-seat.js +47 -0
  65. package/dist/core/permission-rule-model.d.ts +11 -6
  66. package/dist/core/permission-rule-model.js +53 -37
  67. package/dist/core/permission-rules.js +8 -3
  68. package/dist/core/protocol-naming.d.ts +2 -2
  69. package/dist/core/remote-env.d.ts +1 -1
  70. package/dist/core/retired-keys.d.ts +19 -0
  71. package/dist/core/retired-keys.js +15 -0
  72. package/dist/core/runner/abort-race.d.ts +3 -3
  73. package/dist/core/runner/active-skill-scope.d.ts +2 -2
  74. package/dist/core/runner/active-skill-scope.js +3 -3
  75. package/dist/core/runner/assemble-result.d.ts +42 -40
  76. package/dist/core/runner/assemble-result.js +5 -4
  77. package/dist/core/runner/checkpoint-scope.d.ts +4 -4
  78. package/dist/core/runner/clock-and-limits.d.ts +3 -3
  79. package/dist/core/runner/compaction-call-options.d.ts +35 -99
  80. package/dist/core/runner/compaction-knobs.d.ts +4 -4
  81. package/dist/core/runner/compaction-seams.d.ts +5 -5
  82. package/dist/core/runner/contracts.d.ts +232 -233
  83. package/dist/core/runner/decide-continuation.d.ts +6 -6
  84. package/dist/core/runner/denial-limit-arms.d.ts +8 -8
  85. package/dist/core/runner/derived-route-fallback.d.ts +2 -2
  86. package/dist/core/runner/gate-exit.d.ts +6 -6
  87. package/dist/core/runner/gate-exit.js +15 -1
  88. package/dist/core/runner/git-status-frame.d.ts +7 -7
  89. package/dist/core/runner/halt-attribution.d.ts +35 -0
  90. package/dist/core/runner/halt-attribution.js +16 -0
  91. package/dist/core/runner/inherited-ask-grants.d.ts +6 -6
  92. package/dist/core/runner/memory-capture-optout.d.ts +1 -1
  93. package/dist/core/runner/memory-consolidation-driver.d.ts +4 -4
  94. package/dist/core/runner/memory-consolidation.d.ts +6 -8
  95. package/dist/core/runner/memory-consolidation.js +0 -1
  96. package/dist/core/runner/park-commit.d.ts +6 -6
  97. package/dist/core/runner/permission-rule-lanes.d.ts +19 -19
  98. package/dist/core/runner/prepare-acquire-reconcile.d.ts +4 -4
  99. package/dist/core/runner/prepare-artifact.d.ts +53 -0
  100. package/dist/core/runner/prepare-artifact.js +77 -0
  101. package/dist/core/runner/prepare-ask-lane.d.ts +3 -3
  102. package/dist/core/runner/prepare-boundary-parks.d.ts +3 -3
  103. package/dist/core/runner/prepare-caps-and-workflow.d.ts +9 -7
  104. package/dist/core/runner/prepare-caps-and-workflow.js +2 -3
  105. package/dist/core/runner/prepare-config-doors.d.ts +24 -24
  106. package/dist/core/runner/prepare-config-doors.js +5 -2
  107. package/dist/core/runner/prepare-context-lane.d.ts +8 -8
  108. package/dist/core/runner/prepare-context-lane.js +2 -13
  109. package/dist/core/runner/prepare-defer-classify.d.ts +2 -2
  110. package/dist/core/runner/prepare-delegation-surface.d.ts +1 -1
  111. package/dist/core/runner/prepare-file-history.d.ts +3 -3
  112. package/dist/core/runner/prepare-file-history.js +3 -52
  113. package/dist/core/runner/prepare-gate-stations.d.ts +5 -5
  114. package/dist/core/runner/prepare-hands-readface.d.ts +35 -38
  115. package/dist/core/runner/prepare-hands-readface.js +3 -2
  116. package/dist/core/runner/prepare-inherited-gate.d.ts +9 -6
  117. package/dist/core/runner/prepare-inherited-gate.js +7 -8
  118. package/dist/core/runner/prepare-listings.d.ts +3 -3
  119. package/dist/core/runner/prepare-lsp.d.ts +3 -3
  120. package/dist/core/runner/prepare-memory-engine-session.d.ts +5 -5
  121. package/dist/core/runner/prepare-memory.d.ts +4 -6
  122. package/dist/core/runner/prepare-memory.js +10 -9
  123. package/dist/core/runner/prepare-offload-wrappers.d.ts +2 -2
  124. package/dist/core/runner/prepare-park-ask.d.ts +6 -6
  125. package/dist/core/runner/prepare-policy-chain.d.ts +7 -7
  126. package/dist/core/runner/prepare-policy-chain.js +15 -30
  127. package/dist/core/runner/prepare-prompt-assembly.js +1 -1
  128. package/dist/core/runner/prepare-prompt-inputs.d.ts +5 -5
  129. package/dist/core/runner/prepare-protocol-tools.d.ts +1 -1
  130. package/dist/core/runner/prepare-question-face.d.ts +4 -4
  131. package/dist/core/runner/prepare-run-refs.d.ts +11 -11
  132. package/dist/core/runner/prepare-safety-scan.d.ts +2 -2
  133. package/dist/core/runner/prepare-suspend-saga.d.ts +4 -4
  134. package/dist/core/runner/prepare-suspend-saga.js +0 -2
  135. package/dist/core/runner/prepare-task.d.ts +12 -12
  136. package/dist/core/runner/prepare-task.js +10 -9
  137. package/dist/core/runner/prepare-tool-disclosure-mount.d.ts +2 -3
  138. package/dist/core/runner/prepare-wiring-manifest.d.ts +4 -4
  139. package/dist/core/runner/prepare-wiring-manifest.js +9 -7
  140. package/dist/core/runner/prepare-workspace-restore.d.ts +1 -1
  141. package/dist/core/runner/remote-env-retry.d.ts +3 -3
  142. package/dist/core/runner/resume-admission.d.ts +1 -1
  143. package/dist/core/runner/resume-admission.js +2 -1
  144. package/dist/core/runner/resume-claim.d.ts +1 -1
  145. package/dist/core/runner/resume-internals-and-config.d.ts +1 -1
  146. package/dist/core/runner/resume-internals-and-config.js +4 -0
  147. package/dist/core/runner/resume-policy-outcome.d.ts +1 -1
  148. package/dist/core/runner/resume-preflight.d.ts +1 -1
  149. package/dist/core/runner/resume-review-outcome.d.ts +1 -1
  150. package/dist/core/runner/retired-runner-deps.d.ts +3 -0
  151. package/dist/core/runner/retired-runner-deps.js +7 -0
  152. package/dist/core/runner/run-brain-sinks.d.ts +1 -1
  153. package/dist/core/runner/run-clock-and-content.d.ts +4 -4
  154. package/dist/core/runner/run-compaction-machinery.d.ts +3 -3
  155. package/dist/core/runner/run-git-lane.d.ts +1 -1
  156. package/dist/core/runner/run-harness-handlers.d.ts +2 -2
  157. package/dist/core/runner/run-harness-handlers.js +2 -3
  158. package/dist/core/runner/run-identity-wiring.d.ts +4 -4
  159. package/dist/core/runner/run-leg.d.ts +3 -3
  160. package/dist/core/runner/run-notification-lane.d.ts +2 -2
  161. package/dist/core/runner/run-reasoning-seat.d.ts +2 -2
  162. package/dist/core/runner/run-settle-and-teardown.d.ts +5 -5
  163. package/dist/core/runner/run-settle-and-teardown.js +3 -1
  164. package/dist/core/runner/run-telemetry-and-budget-seats.d.ts +2 -2
  165. package/dist/core/runner/run-terminal-adoption.d.ts +5 -5
  166. package/dist/core/runner/runtask.d.ts +14 -14
  167. package/dist/core/runner/session-file-state-replay.d.ts +3 -3
  168. package/dist/core/runner/session-file-state-replay.js +3 -3
  169. package/dist/core/runner/session-rule-policy.d.ts +2 -2
  170. package/dist/core/runner/session-rule-policy.js +3 -4
  171. package/dist/core/runner/steer-admission.d.ts +2 -2
  172. package/dist/core/runner/stream-halt-verbs.d.ts +1 -1
  173. package/dist/core/runner/stream-halt-verbs.js +1 -1
  174. package/dist/core/runner/stream-reap.d.ts +1 -1
  175. package/dist/core/runner/stream-steer-verb.js +1 -4
  176. package/dist/core/runner/synthetic-tools.d.ts +8 -8
  177. package/dist/core/runner/teardown-bounded.d.ts +2 -2
  178. package/dist/core/runner/tool-defer-gate.d.ts +1 -1
  179. package/dist/core/runner/tool-disclosure.d.ts +33 -33
  180. package/dist/core/runner/tool-end-body.d.ts +3 -3
  181. package/dist/core/runner/tool-face-overlay.d.ts +2 -2
  182. package/dist/core/runner/tool-output-projection.d.ts +2 -2
  183. package/dist/core/runner/turn-attachments.d.ts +53 -53
  184. package/dist/core/runner/usage-accounting.d.ts +4 -4
  185. package/dist/core/runner/workspace-path.d.ts +1 -1
  186. package/dist/core/runner/workspace-path.js +3 -0
  187. package/dist/core/runner-deps.d.ts +30 -49
  188. package/dist/core/safe-notify.d.ts +1 -1
  189. package/dist/core/sensitive-path-policy.d.ts +1 -1
  190. package/dist/core/sensitive-path-policy.js +4 -4
  191. package/dist/core/session.d.ts +2 -2
  192. package/dist/core/skill-tool-specifier.d.ts +1 -1
  193. package/dist/core/skills-directory.d.ts +1 -1
  194. package/dist/core/store-contracts/checkpoint-store-contract.js +1 -1
  195. package/dist/core/strategy-store.d.ts +8 -11
  196. package/dist/core/task-event.d.ts +20 -17
  197. package/dist/core/task-limits.d.ts +1 -1
  198. package/dist/core/task-notification.d.ts +5 -5
  199. package/dist/core/task-registry-agent.d.ts +5 -5
  200. package/dist/core/task-registry-monitor.d.ts +2 -2
  201. package/dist/core/task-registry-shared.d.ts +27 -27
  202. package/dist/core/task-registry-shared.js +3 -13
  203. package/dist/core/task-registry.d.ts +8 -8
  204. package/dist/core/task-result.d.ts +37 -37
  205. package/dist/core/task-spec.d.ts +5 -5
  206. package/dist/core/task-stream.d.ts +16 -11
  207. package/dist/core/tool-catalog-entries.d.ts +1 -0
  208. package/dist/core/tool-catalog-entries.js +5 -0
  209. package/dist/core/tool-conformance.d.ts +0 -2
  210. package/dist/core/tool-conformance.js +1 -3
  211. package/dist/core/tool-errors.d.ts +5 -0
  212. package/dist/core/tool-errors.js +1 -0
  213. package/dist/core/tool-policy.d.ts +51 -22
  214. package/dist/core/tool-policy.js +28 -5
  215. package/dist/core/tool-result-budget.d.ts +1 -1
  216. package/dist/core/tool-result-store.d.ts +11 -11
  217. package/dist/core/tool-spec.d.ts +34 -38
  218. package/dist/core/tools.d.ts +2 -2
  219. package/dist/core/trace.d.ts +12 -15
  220. package/dist/core/untrusted-text.d.ts +1 -1
  221. package/dist/core/wiring-manifest.d.ts +4 -3
  222. package/dist/core/workflow-journal-store.d.ts +11 -11
  223. package/dist/engine/execution-env/node-execution-env.d.ts +3 -2
  224. package/dist/engine/execution-env/node-execution-env.js +4 -2
  225. package/dist/engine/harness/agent-harness.d.ts +47 -4
  226. package/dist/engine/harness/agent-harness.js +52 -9
  227. package/dist/engine/llm/types.d.ts +1 -1
  228. package/dist/engine/loop/agent-loop.js +15 -8
  229. package/dist/engine/loop/types.d.ts +5 -0
  230. package/dist/index.d.ts +21 -16
  231. package/dist/index.js +17 -12
  232. package/dist/internal/harness.d.ts +1 -0
  233. package/dist/orchestration/run-spec.d.ts +3 -0
  234. package/dist/orchestration/run-spec.js +3 -3
  235. package/dist/orchestration/run-workflow-tool.d.ts +12 -6
  236. package/dist/orchestration/run-workflow-tool.js +23 -5
  237. package/dist/orchestration/workflow-primitives.d.ts +4 -2
  238. package/dist/orchestration/workflow-primitives.js +1 -4
  239. package/dist/orchestration/workflow-types.d.ts +34 -4
  240. package/dist/orchestration/workflow-types.js +2 -0
  241. package/dist/orchestration/workflow.d.ts +39 -7
  242. package/dist/orchestration/workflow.js +234 -113
  243. package/dist/prompt-assembly/assemble.d.ts +2 -2
  244. package/dist/prompt-assembly/assemble.js +9 -9
  245. package/dist/prompt-assembly/composer.d.ts +6 -7
  246. package/dist/prompt-assembly/composer.js +10 -10
  247. package/dist/prompt-assembly/epoch.js +1 -1
  248. package/dist/prompt-assembly/event-registry.d.ts +0 -21
  249. package/dist/prompt-assembly/packs/sema-default.js +30 -30
  250. package/dist/prompt-assembly/types.d.ts +14 -14
  251. package/dist/prompts/coordinator.d.ts +0 -13
  252. package/dist/prompts/coordinator.js +0 -3
  253. package/dist/stores/file/checkpoint-store.js +1 -5
  254. package/dist/stores/file/file-history-store.js +2 -2
  255. package/dist/stores/file/permission-rule-store.d.ts +4 -35
  256. package/dist/stores/file/permission-rule-store.js +4 -148
  257. package/dist/tools/artifact/artifact-text.d.ts +62 -0
  258. package/dist/tools/artifact/artifact-text.js +68 -0
  259. package/dist/tools/artifact/artifact-tool.d.ts +52 -0
  260. package/dist/tools/artifact/artifact-tool.js +351 -0
  261. package/dist/tools/artifact/local-stub.d.ts +26 -0
  262. package/dist/tools/artifact/local-stub.js +296 -0
  263. package/dist/tools/fs/bash-readonly-classifier.d.ts +114 -48
  264. package/dist/tools/fs/bash-readonly-classifier.js +171 -60
  265. package/dist/tools/fs/encoding.d.ts +31 -40
  266. package/dist/tools/fs/fs-bash.d.ts +49 -45
  267. package/dist/tools/fs/fs-bash.js +19 -27
  268. package/dist/tools/fs/fs-pdf.d.ts +8 -8
  269. package/dist/tools/fs/fs-search-tools.d.ts +4 -4
  270. package/dist/tools/fs/fs-search-tools.js +5 -2
  271. package/dist/tools/fs/fs-shared.d.ts +146 -158
  272. package/dist/tools/fs/fs-shared.js +3 -11
  273. package/dist/tools/fs/fs-write.d.ts +8 -8
  274. package/dist/tools/fs/gh-rate-limit.d.ts +13 -14
  275. package/dist/tools/fs/index.d.ts +78 -78
  276. package/dist/tools/fs/index.js +4 -1
  277. package/dist/tools/fs/notebook.d.ts +24 -25
  278. package/dist/tools/fs/pdf.d.ts +3 -3
  279. package/dist/tools/fs/read-deny.d.ts +22 -22
  280. package/dist/tools/fs/read-face.d.ts +21 -24
  281. package/dist/tools/fs/repo-map.d.ts +8 -8
  282. package/dist/tools/fs/safety.d.ts +233 -130
  283. package/dist/tools/fs/safety.js +131 -31
  284. package/dist/tools/fs/search.d.ts +63 -71
  285. package/dist/tools/fs/search.js +8 -6
  286. package/package.json +6 -2
  287. package/test/export-surface.snapshot.json +119 -39
@@ -1,13 +1,13 @@
1
1
  import type { ExecutionEnv } from "../../internal/harness-types.js";
2
2
  import { protectivePathTargetOf, type ToolCallLike } from "../../core/tool-registry.js";
3
3
  /**
4
- * batch-B (CC 2.1.187 parity): Read/Edit/Write take the file path as `file_path`. Read the target the same way
4
+ * CC parity: Read/Edit/Write take the file path as `file_path`. Read the target the same way
5
5
  * EVERYWHERE the path is consumed from a tool-call's args — not just the tool body but ALSO every gate that
6
6
  * extracts the write target to confine it (sensitive-path, skill allowPaths, session allowDirs). A gate that only
7
7
  * read `args.path` would see `undefined` for a `file_path` call and either fail-OPEN (circumvent the guard — a real
8
8
  * hole) or fail-closed (deny a legitimate write). Single source.
9
9
  *
10
- * The `path` arm is deliberately BROADER than what Read/Edit/Write accept (ruled 2026-08-03): their schema requires
10
+ * The `path` arm is deliberately BROADER than what Read/Edit/Write accept: their schema requires
11
11
  * `file_path` and rejects the retired `path` alias outright (fs-shared.ts FILE_PATH_PARAMS), but this reader is
12
12
  * shared with gates that judge OTHER tool vocabularies — Glob's real `path` parameter, deployment-authored tools —
13
13
  * and a gate must never see LESS than the tool it is confining might act on. So the asymmetry is the design: the
@@ -15,8 +15,8 @@ import { protectivePathTargetOf, type ToolCallLike } from "../../core/tool-regis
15
15
  */
16
16
  export declare function fileArgPath(args: unknown): string | undefined;
17
17
  /**
18
- * The WRITE TARGET of a path-confinable write call, tool-aware (b — single source for every
19
- * gate that confines a write target, same discipline as {@link fileArgPath} above):
18
+ * The WRITE TARGET of a path-confinable write call, tool-aware — single source for every gate that
19
+ * confines a write target, same discipline as {@link fileArgPath} above:
20
20
  *
21
21
  * · canonical `NotebookEdit` → `notebook_path` FIRST, `file_path`/`path` only as fallback. The tool
22
22
  * itself writes `notebook_path`; its schema tolerates extra keys, so a stray/decoy `file_path` on a
@@ -25,30 +25,30 @@ export declare function fileArgPath(args: unknown): string | undefined;
25
25
  * · every other tool → `file_path`/`path` first ({@link fileArgPath}), `notebook_path` as the
26
26
  * historical fallback (unchanged behavior for the pre-existing gate callers).
27
27
  *
28
- * `canonicalName` is the tool name as mounted (RB-476-A: raw the legacy normalizer is retired;
29
- * this module stays dependency-free of the alias table — "NotebookEdit" is the canonical CC name and
30
- * old→new normalization is identity for it).
28
+ * `canonicalName` is the tool name as mounted, raw: there is no legacy normalizer, and this module
29
+ * stays dependency-free of the alias table — "NotebookEdit" is the canonical CC name and old→new
30
+ * normalization is identity for it.
31
31
  */
32
32
  export declare function writeTargetPath(call: ToolCallLike, pt?: ReturnType<typeof protectivePathTargetOf>): string | undefined;
33
33
  /**
34
- * The "hand" file tools (design/44) — safety invariants ported from CC's `FileEditTool` (design/43 Rule
35
- * 7): without these a weak model blind-edits / overwrites files (P0 safety, not polish). This module is
36
- * the pure/testable core (path resolution + the three edit invariants + content hashing); the tools in
37
- * this dir compose it. Per-task state and the execution env are passed in (factory-closure wiring,
38
- * design/44 §11 ruling A), never read from a shared/global.
34
+ * The "hand" file tools' safety invariants, ported from CC's `FileEditTool`: without these a weak
35
+ * model blind-edits / overwrites files (a safety property, not polish). This module is the
36
+ * pure/testable core (path resolution + the three edit invariants + content hashing); the tools in
37
+ * this dir compose it. Per-task state and the execution env are passed in (factory-closure wiring),
38
+ * never read from a shared/global.
39
39
  */
40
- /** What we remember about a file the agent has read (design/44 §4). */
40
+ /** What we remember about a file the agent has read. */
41
41
  export interface ReadEntry {
42
42
  /** SHA-256 of the content at read time. Staleness is detected by content hash, NOT mtime — mtime is
43
- * unreliable across containers/volumes/NFS and across a design/45 suspend→resume on another replica. */
43
+ * unreliable across containers/volumes/NFS and across a suspend→resume on another replica. */
44
44
  hash: string;
45
- /** Total line count at read time, and whether the read was truncated (for the edit truncation hint, S2). */
45
+ /** Total line count at read time, and whether the read was truncated (for the edit truncation hint). */
46
46
  totalLines: number;
47
47
  truncated: boolean;
48
- /** RB-200 F2 — CC `isPartialView` parity (220 @515151 sets it only when the auto-pagination catch branch
49
- * fires, i.e. the model asked for the file by DEFAULT no explicit offset/limit and the token cap
50
- * truncated it anyway; an EXPLICIT offset/limit slice that fits under the cap is NEVER marked this way,
51
- * even though it's equally "not the whole file"). Deliberately narrower than {@link truncated} above,
48
+ /** CC `isPartialView` parity: set ONLY when the auto-pagination branch fires — the model asked for
49
+ * the file by DEFAULT (no explicit offset/limit) and the token cap truncated it anyway. An EXPLICIT
50
+ * offset/limit slice that fits under the cap is NEVER marked this way, even though it is equally
51
+ * "not the whole file". Deliberately narrower than {@link truncated} above,
52
52
  * which also fires for a fully-intentional, successful slice read (`start>1 || end<total`) — folding
53
53
  * THIS flag into that broader one would make {@link OVERSIZE_READ_ESCAPE_HINT}'s documented promise
54
54
  * ("read it in slices with explicit offset/limit to satisfy the read-first rule") false: a model paging
@@ -56,51 +56,50 @@ export interface ReadEntry {
56
56
  * additive: undefined on every non-Read write-back (Write/Edit re-record the file they just fully wrote)
57
57
  * and on every seeded/attachment entry — those are never partial by construction. */
58
58
  isPartialView?: boolean;
59
- /** The line window actually served to the model on the last read (design/64 §7.2(6) dedup stub). Lets
59
+ /** The line window actually served to the model on the last read (the dedup stub's key). Lets
60
60
  * read_file return a `file_unchanged` stub when the SAME window is re-requested and the content hash is
61
61
  * unchanged — saving the re-transmitted body. Optional/additive: undefined (e.g. an entry seeded from an
62
- * older design/45 checkpoint) just means "no dedup", never a false hit. */
62
+ * older checkpoint) just means "no dedup", never a false hit. */
63
63
  view?: {
64
64
  start: number;
65
65
  end: number;
66
66
  };
67
- /** Millisecond timestamp of the last read/write-back that recorded this entry (
68
- * 2026-07-03, compact re-read parity: the post-compaction attachment picks the most RECENTLY
69
- * read files CC sorts readFileState by timestamp). Optional/additive: undefined (an entry
70
- * seeded from an older checkpoint) sorts last, never breaks. */
67
+ /** Millisecond timestamp of the last read/write-back that recorded this entry: the post-compaction
68
+ * attachment picks the most RECENTLY read files, the way CC sorts readFileState by timestamp.
69
+ * Optional/additive: undefined (an entry seeded from an older checkpoint) sorts last, never breaks. */
71
70
  lastReadAt?: number;
72
- /** CC 2.1.204 parity (`seededFromContext`, cc204-bundle @17889409 / seeding @17917159; 198 zero
73
- * hits): TRUE when the Runner pre-seeded this entry because the file's FULL disk-verbatim text
74
- * was injected into the model's context at startup (ProjectMemoryLoad.seededFiles CC's
75
- * CLAUDE.md/nested-memory seeding). A DEFAULT whole-file Read of an UNCHANGED seeded file
71
+ /** CC 2.1.204 parity: TRUE when the Runner pre-seeded this entry because the file's FULL
72
+ * disk-verbatim text was injected into the model's context at startup
73
+ * (ProjectMemoryLoad.seededFiles CC's own memory preload). A DEFAULT whole-file Read of an
74
+ * UNCHANGED seeded file
76
75
  * answers with the already-in-context reminder instead of re-transmitting the body. Any real
77
76
  * Read / edit write-back records a FRESH entry without this flag, so a file that changed on
78
77
  * disk (hash mismatch) always serves real content and the dedup disarms permanently.
79
78
  * Optional/additive: absent = normal entry. */
80
79
  seededFromContext?: boolean;
81
80
  }
82
- /** Per-task record of which files have been read, keyed by canonical path (design/44 §4, §11 ruling A:
83
- * owned by prepare-task, closure-captured by the tools; serialized into a design/45 Checkpoint later). */
81
+ /** Per-task record of which files have been read, keyed by canonical path: owned by prepare-task,
82
+ * closure-captured by the tools, and serialized into the durable checkpoint. */
84
83
  export type ReadFileState = Map<string, ReadEntry>;
85
- /** SHA-256 hex of UTF-8 content — the staleness/identity key (design/44 §4 inv 2, jury M1). */
84
+ /** SHA-256 hex of UTF-8 content — the staleness/identity key. */
86
85
  export declare function sha256(content: string): string;
87
86
  /** A failed path resolution / invariant check, surfaced to the model as a self-correctable tool error. */
88
87
  export interface FsViolation {
89
88
  code: "path_not_in_root" | "not_read" | "stale" | "ambiguous_edit" | "invalid" | "read_path_denied";
90
- /** RB-264 N1: set on the `not_read` violation raised by an AUTO-TRUNCATED read
89
+ /** Set on the `not_read` violation raised by an AUTO-TRUNCATED read
91
90
  * ({@link ReadEntry.isPartialView}) rather than by a never-read file. Both carry the same CC-verbatim
92
91
  * message; only this one needs {@link PARTIAL_VIEW_READ_ESCAPE_HINT} appended, because for it a plain
93
92
  * re-read is a fixpoint. */
94
93
  partialView?: true;
95
- /** RB-371 ④: set on `path_not_in_root` (and design/199 `read_path_denied`) — the CANONICAL key the
94
+ /** Set on `path_not_in_root` and `read_path_denied` — the CANONICAL key the
96
95
  * refused path resolved to (symlinks followed, tracked cwd applied), i.e. the value the fence
97
96
  * actually judged. Rides into the tool result's `details` (see {@link violationDetails}) so a
98
97
  * consumer can count/aggregate boundary refusals from fields instead of regexing the prose. */
99
98
  target?: string;
100
- /** RB-371 ④: set ONLY on `path_not_in_root` — the containment roots in effect (primary first,
99
+ /** Set ONLY on `path_not_in_root` — the containment roots in effect (primary first,
101
100
  * then additionalDirectories), canonical form. */
102
101
  roots?: readonly string[];
103
- /** design/199 件B: set ONLY on `read_path_denied` — the deny-list pattern (verbatim entry text)
102
+ /** Set ONLY on `read_path_denied` — the deny-list pattern (verbatim entry text)
104
103
  * the judged target matched. */
105
104
  pattern?: string;
106
105
  message: string;
@@ -108,21 +107,17 @@ export interface FsViolation {
108
107
  /**
109
108
  * Absolute in EITHER family: POSIX `/…` or drive-letter `C:\…`/`C:/…`.
110
109
  *
111
- * REF-C1 — this is the ONE definition of "already absolute, therefore do not prepend a base". It was
112
- * independently re-typed at nine coordinates (search.ts ×4, bash-readonly-classifier.ts,
113
- * core/lsp.ts, core/runner/prepare-task.ts, orchestration/run-spec.ts and here), three of which
114
- * carried a comment claiming the same philosophy as this file while keeping their own copy. The
115
- * failure mode is not the duplication itself but the drift it invites: a family added at one
116
- * coordinate leaves the other eight judging the same string differently, and this predicate gates a
110
+ * This is the ONE definition of "already absolute, therefore do not prepend a base". The failure mode
111
+ * of a private re-typing is not the duplication but the drift it invites: a family added at one
112
+ * coordinate leaves every other copy judging the same string differently, and this predicate gates a
117
113
  * security fence (frozen-path deny, readonly-boundary containment) as well as URI key minting.
118
114
  * Callers that need a family the predicate does not cover must widen it HERE, not locally.
119
115
  *
120
- * The UNC arm (`\\host\share\…`) was exactly that drift, found already in flight: core/lsp.ts carried it
121
- * privately while the other eight coordinates did not, so the LSP tool leg resolved a UNC path as
122
- * absolute while the edit-nudge leg prefixed it with the task root two URI keys for one file. Folding
123
- * it in here settles the disagreement in the direction the rest of this file already takes: a literal
124
- * `\\host\share\…` is win-form (see {@link isWinFormPath}) and `within()` lands it outside every
125
- * POSIX/drive root, i.e. it is a location the working directory does not contain.
116
+ * The UNC arm (`\\host\share\…`) is the shape that drift produced: held privately in one consumer, it
117
+ * made that consumer resolve a UNC path as absolute while a sibling prefixed it with the task root —
118
+ * two URI keys for one file. Folding it in here settles the disagreement in the direction the rest of
119
+ * this file takes: a literal `\\host\share\…` is win-form (see {@link isWinFormPath}) and `within()`
120
+ * lands it outside every POSIX/drive root, i.e. a location the working directory does not contain.
126
121
  */
127
122
  export declare function isAbsolutePathForm(p: string): boolean;
128
123
  /**
@@ -133,10 +128,10 @@ export declare function isAbsolutePathForm(p: string): boolean;
133
128
  * contains a literal backslash (legal on POSIX), and `//host/share` is therefore POSIX-form too — which is
134
129
  * the whole point: on POSIX that spelling is an ordinary absolute path, not a network share.
135
130
  *
136
- * REF-C2 — this is the ONE definition of the win-family spelling. core/file-snapshot-store.ts held a
137
- * byte-identical private twin (its manifest key/restore-target folds are conditioned on it) and now
138
- * imports this one; core/ already depends on this module at seven other coordinates, and this file's own
139
- * imports are `node:crypto` plus a type, so there is no layering wall between them.
131
+ * This is the ONE definition of the win-family spelling; core/file-snapshot-store.ts conditions its
132
+ * manifest key / restore-target folds on this import rather than a private twin. core/ already depends
133
+ * on this module elsewhere, and this file's own imports are `node:crypto` plus a type, so there is no
134
+ * layering wall between them.
140
135
  */
141
136
  export declare function isWinFormPath(p: string): boolean;
142
137
  /**
@@ -153,7 +148,7 @@ export declare function isWinFormPath(p: string): boolean;
153
148
  export declare function expandHomeTilde(p: string, home: string): string;
154
149
  export declare function isBlockedDevicePath(key: string): boolean;
155
150
  /**
156
- * RB-153 — purely LEXICAL path normalization: collapse repeated separators, drop `.` segments, and
151
+ * Purely LEXICAL path normalization: collapse repeated separators, drop `.` segments, and
157
152
  * resolve `..` against the preceding segment. No filesystem access, no cwd: this is a spelling
158
153
  * normalizer, not a resolver. (Deliberately NOT `canonicalizeNewPath`'s job — that one walks the real
159
154
  * filesystem precisely because folding `..` lexically across a SYMLINKED parent is unsound. Use this one
@@ -164,9 +159,8 @@ export declare function isBlockedDevicePath(key: string): boolean;
164
159
  * three different ways. Callers that match a device list against a raw argument must normalize first or
165
160
  * the list is a spelling filter rather than a device filter.
166
161
  *
167
- * REF-C3coverage, after folding in the two private re-implementations that had grown around this one
168
- * (the bash classifier's drive-aware wrapper and prepare-task's `normalizeSegments`). The union they
169
- * covered between them is now the contract here:
162
+ * Coveragethe union of every fold the callers need, held once here rather than re-implemented per
163
+ * consumer:
170
164
  * · POSIX absolute — `/a/./b` → `/a/b`; `/..` is `/`, matching the kernel.
171
165
  * · drive-letter (`C:\a\..\b`) and UNC (`\\host\share\..\x`) — prefix preserved, folding applies
172
166
  * to the tail only, and the REJOIN uses the path's own native separator so one file cannot acquire
@@ -175,14 +169,14 @@ export declare function isBlockedDevicePath(key: string): boolean;
175
169
  * `dev/zero` under some other directory is an ordinary file), and a leading `..` is KEPT rather than
176
170
  * popped into nothing — dropping it would move the path.
177
171
  *
178
- * FAMILY-awareness is the one thing none of the three had right. A backslash is a separator only in a
179
- * win-form path; in a POSIX path it is an ordinary filename character, and the classifier's old
180
- * unconditional `\`→`/` fold rewrote a POSIX file literally named `a\..\b` into a different location
181
- * before the boundary check ever saw it (both directions: false refusal and false allow).
172
+ * FAMILY-awareness is the part a private copy tends to get wrong. A backslash is a separator only in a
173
+ * win-form path; in a POSIX path it is an ordinary filename character, and an unconditional `\`→`/`
174
+ * fold rewrites a POSIX file literally named `a\..\b` into a different location before the boundary
175
+ * check ever sees it — in both directions: false refusal and false allow.
182
176
  */
183
177
  export declare function normalizeAbsPathLexically(p: string): string;
184
178
  /**
185
- * The READ-leg fold (REF-C3 receipt, R1 verify ISSUE-1): removes `.` segments and duplicate
179
+ * The READ-leg fold: removes `.` segments and duplicate
186
180
  * separators ONLY — transformations that name the SAME file on every real filesystem, symlinks
187
181
  * included. `..` is deliberately KEPT: `a/../x` and `x` are different files when `a` is a symlink,
188
182
  * and this function's callers go on to OPEN the result (fileInfo/readText), not just to mint a
@@ -197,7 +191,7 @@ export declare function hasBinaryExtension(path: string): boolean;
197
191
  export declare function imageMimeForRead(path: string): string | undefined;
198
192
  /** Verify the raw bytes actually ARE the image format the extension claims (magic-number sniff). Extension alone
199
193
  * is forgeable: a `.png` holding text / an empty file / a truncated blob would otherwise become a malformed
200
- * `ImageContent` that makes the NEXT provider request fail (400). Pure — no I/O. (batch-C.) */
194
+ * `ImageContent` that makes the NEXT provider request fail (400). Pure — no I/O. */
201
195
  export declare function imageMagicMatches(bytes: Uint8Array, mimeType: string): boolean;
202
196
  /** One contiguous byte pattern that must appear at a fixed offset for its signature to match. */
203
197
  export interface BinaryMagicSegment {
@@ -216,11 +210,11 @@ export interface BinaryMagicSignature {
216
210
  readonly segments: readonly BinaryMagicSegment[];
217
211
  }
218
212
  /**
219
- * ANG-5 (external black-box re-review, ruled 2026-08-03) magic-byte criteria for the formats
220
- * {@link hasBinaryExtension} names. That list carries 103 extensions; before this table only PDF
221
- * (`pdfMagicMatches`, in the PDF lane) and the four API image formats ({@link imageMagicMatches}) had a
222
- * content criterion, so for every other listed format the NAME was the only evidence available. Where the
223
- * content IS reachable that left identification to the statistical fallback ({@link isBinaryContent}),
213
+ * Magic-byte criteria for the formats {@link hasBinaryExtension} names. That list carries 103
214
+ * extensions; without this table only PDF (`pdfMagicMatches`, in the PDF lane) and the four API image
215
+ * formats ({@link imageMagicMatches}) have a content criterion, so for every other listed format the
216
+ * NAME is the only evidence available. Where the content IS reachable that leaves identification to
217
+ * the statistical fallback ({@link isBinaryContent}),
224
218
  * which samples the first 8192 decoded characters and passes anything whose control-character ratio
225
219
  * stays under 10% — a short archive/executable header followed by printable payload clears that bar
226
220
  * and gets delivered as text.
@@ -249,36 +243,147 @@ export declare const BINARY_MAGIC_SIGNATURES: readonly BinaryMagicSignature[];
249
243
  */
250
244
  export declare function binaryMagicFormat(bytes: Uint8Array): string | undefined;
251
245
  /**
252
- * Content sniff for binary data (design/64 §17.1, CC `constants/files.ts` `isBinaryContent`): a NUL byte,
246
+ * Content sniff for binary data (CC `isBinaryContent` parity): a NUL byte,
253
247
  * or more than 10% non-printable characters in the sample, means binary. Second layer behind
254
248
  * {@link hasBinaryExtension} — catches extension-less or mis-named binaries (a `Dockerfile` that is really
255
249
  * a compiled blob) that would otherwise be read as mojibake. Plain text (incl. UTF-8, tabs/newlines) passes.
256
250
  */
257
251
  export declare function isBinaryContent(sample: string): boolean;
258
252
  /**
259
- * CC's own UNC/network-path predicate, verbatim (220 @515523:
260
- * `n.startsWith("\\\\") || n.startsWith("//")`). Pure.
253
+ * CC's own UNC/network-path predicate, verbatim (`n.startsWith("\\\\") || n.startsWith("//")`). Pure.
261
254
  *
262
- * RB-279: the predicate is CC's, but CC's POLARITY is the opposite of what sema used it for. In CC this
263
- * test is an EARLY ALLOW `return { result: !0 }`, skipping the binary-extension and device checks that
264
- * follow it and CC applies it to the NORMALIZED path (`Li` `path.normalize`), not the raw argument.
265
- * See {@link canonicalizeTarget} for how sema now mirrors that (and for the two deliberate differences).
266
- * This function stays a pure spelling test with no verdict of its own.
255
+ * In CC this test is an EARLY ALLOW it skips the binary-extension and device checks that follow it
256
+ * and CC applies it to the NORMALIZED path, not the raw argument. See {@link canonicalizeTarget} for
257
+ * how sema mirrors that and for the two deliberate differences. This function stays a pure spelling
258
+ * test with no verdict of its own.
267
259
  */
268
260
  export declare function isUncPath(path: string): boolean;
269
- /** design/199 件B the family-aware LEXICAL view of a possibly-relative spelling against a
270
- * resolution base (no I/O, `.`/`..` folded via {@link normalizeAbsPathLexically}): the §3.3 second
271
- * deny-judgment view. It asks "does the NAME land on a guarded path", complementing the canonical
261
+ /** The two spelling families a tree can be in. Read from the ROOT's spelling (the cwd's when a deployment
262
+ * supplies no root) never from `process.platform`: the exec env may be remote and of the other family. */
263
+ export type PathFamily = "win" | "posix";
264
+ /** The family of a call's tree; `undefined` when the call declares neither root nor cwd (a treeless call
265
+ * gets no family invented for it — see {@link pathIdentityOf} for what it can still read). */
266
+ export declare function pathFamilyOf(bases: {
267
+ readonly root: string;
268
+ readonly cwd?: string | undefined;
269
+ }): PathFamily;
270
+ export declare function pathFamilyOf(bases: {
271
+ readonly root?: string | undefined;
272
+ readonly cwd: string;
273
+ }): PathFamily;
274
+ export declare function pathFamilyOf(bases: {
275
+ readonly root?: string | undefined;
276
+ readonly cwd?: string | undefined;
277
+ }): PathFamily | undefined;
278
+ /**
279
+ * FULLY QUALIFIED under the family. win: drive-rooted (`C:\`/`C:/`) or UNC — a `/x` / `\x` is rooted-not-
280
+ * qualified and joins the base's drive or share root ({@link joinForFamily}), a `C:rel` is drive-relative.
281
+ * posix: `/`-rooted. `undefined` (a treeless call): only the anchor-free `/`-rooted form, the one spelling
282
+ * that names a file without a tree.
283
+ */
284
+ export declare function isAbsoluteForFamily(family: PathFamily | undefined, p: string): boolean;
285
+ /** The refusal words of {@link win32NamespaceScreen} — a closed set (docs/CLOSED-SETS.md). */
286
+ export declare const WIN32_NAMESPACE_REFUSALS: readonly ["device_namespace", "unmappable_namespace", "unc_on_posix_tree", "drive_relative"];
287
+ export type Win32NamespaceRefusal = (typeof WIN32_NAMESPACE_REFUSALS)[number];
288
+ /** What each refusal word says about the spelling (the message clause after `path "…" `). */
289
+ export declare const WIN32_NAMESPACE_REFUSAL_TEXT: {
290
+ readonly device_namespace: "is a Win32 device-namespace path (\\\\.\\ names a raw device object, not a file)";
291
+ readonly unmappable_namespace: "is a Win32 extended-length namespace path with no DOS-path equivalent (only \\\\?\\<drive>: and \\\\?\\UNC\\ forms name files)";
292
+ readonly unc_on_posix_tree: "is a UNC (\\\\host\\share) spelling, which names no file on a POSIX tree";
293
+ readonly drive_relative: "is drive-relative (a drive letter with no separator after the colon), a location no tree can locate";
294
+ };
295
+ /** The fence over the text table: `never` while every refusal word has a row. */
296
+ export type NamespaceRefusalTextCoversEveryWord = AssertAllKeysHandled<Exclude<Win32NamespaceRefusal, keyof typeof WIN32_NAMESPACE_REFUSAL_TEXT>>;
297
+ type AssertAllKeysHandled<T extends never> = T;
298
+ /**
299
+ * The Win32 namespace screen — run on every model-spelled target BEFORE the family join, at every site
300
+ * (the fs throat, the effective-target reader, the rule side, the glob splitter), so one spelling is
301
+ * refused with one word everywhere. `\\.\` ⇒ `device_namespace` (every family); `\\?\` mappable ⇒ the
302
+ * stripped spelling, which is screened ON (a `\\?\UNC\…` on a POSIX tree still refuses), unmappable ⇒
303
+ * `unmappable_namespace` (every family); a backslash UNC ⇒ passes under `win`, `unc_on_posix_tree` under
304
+ * `posix` (the canonicalizer's refusal boundary on POSIX trees is preserved: the family rule governs what
305
+ * is admitted, never what was already refused); `C:rel` under `win` ⇒ `drive_relative`. A treeless call
306
+ * passes a UNC spelling through — the identity then reads it as relative, as before.
307
+ */
308
+ export declare function win32NamespaceScreen(spelled: string, family: PathFamily | undefined): {
309
+ readonly ok: true;
310
+ readonly spelled: string;
311
+ } | {
312
+ readonly ok: false;
313
+ readonly refuse: Win32NamespaceRefusal;
314
+ };
315
+ declare const PATH_IDENTITY: unique symbol;
316
+ /**
317
+ * The ONE comparison form of an absolute path (upstream's normal form): posix = the lexical-normal
318
+ * `/`-rooted path; win = `/c/rest` for a drive (letter lower-cased), `//host/share/rest` for UNC, `\`
319
+ * folded to `/`, `.`/`..` folded with the anchor preserved. A COMPARISON form — never a path to open, never
320
+ * compared against a native `C:\…` string. Branded: a spelling is not an identity until {@link pathIdentityOf}
321
+ * has read it.
322
+ */
323
+ export type PathIdentity = string & {
324
+ readonly [PATH_IDENTITY]: true;
325
+ };
326
+ /** Why a spelling has no identity: a namespace refusal, `relative` (join it to a base first), or
327
+ * `unanchored` (a win `/x` with no tree root to take a drive from). */
328
+ export type PathIdentityRefusal = Win32NamespaceRefusal | "relative" | "unanchored";
329
+ export type PathIdentityReading = {
330
+ readonly ok: true;
331
+ readonly identity: PathIdentity;
332
+ } | {
333
+ readonly ok: false;
334
+ readonly reason: PathIdentityRefusal;
335
+ };
336
+ /**
337
+ * The identity of an ABSOLUTE spelling under the family ({@link PathIdentity}). `anchor` is the tree's root
338
+ * (or cwd) spelling: under `win` a `/`-rooted spelling whose first segment is not a drive letter
339
+ * (`/proj/x`) anchors on the root's drive or share, as `path.win32.resolve` anchors it on the current
340
+ * drive; `/c/x` and `/C:/x` are drive spellings (the cygdrive form and the rule grammar's `/`+body), and
341
+ * `/\\host\share\x` is the grammar's prefix before a UNC. A relative spelling is `relative` — callers join
342
+ * it to a base FIRST. A treeless call (`family` undefined) reads only the anchor-free `/`-rooted form, as
343
+ * the POSIX identity; every other spelling names nothing, which is what a missing base answered before.
344
+ */
345
+ export declare function pathIdentityOf(spelled: string, family: PathFamily | undefined, anchor?: string): PathIdentityReading;
346
+ /** Is the identity a UNC one (`//host/share/…`)? Identities compare by anchor kind first: a UNC identity is
347
+ * never under a rooted one and vice versa (upstream's shape parity). */
348
+ export declare function isUncIdentity(identity: PathIdentity): boolean;
349
+ /** Is `s` already an identity under the family — the identity function is the identity on it? The
350
+ * runtime twin of the brand for values that arrive from outside the type system. */
351
+ export declare function isPathIdentity(s: string, family: PathFamily | undefined): s is PathIdentity;
352
+ /** Is the identity a DIRECTORY a rule may speak for — anything but the tree's bare root (`/`, a drive root
353
+ * `/c`, a share root `//host/share`), which admits nothing? */
354
+ export declare function isIdentityDir(identity: PathIdentity, family: PathFamily | undefined): boolean;
355
+ /** Does the spelling carry a `.` or `..` segment under the family's separators (a backslash is a name byte on
356
+ * POSIX)? A rule body with one admits nothing: folding it could make it name a second directory. */
357
+ export declare function carriesDotSegment(spelled: string, family: PathFamily | undefined): boolean;
358
+ /** A model-spelled path bounded for error text (the spelling is unbounded input). */
359
+ export declare function clipSpelling(spelled: string): string;
360
+ /** A ROOTED (not qualified) spelling handed to the SHELL on a win tree (`/d/x`, `/etc`, `\\x`) is the shell's to
361
+ * map — the win32 adapter runs Git Bash, where `/d/x` is drive D and `/etc` the MSYS root, neither the cwd's
362
+ * drive root — so no lexical reader may resolve it (fail-closed: the operand is unresolvable, the cd mints
363
+ * nothing). The ONE statement of that rule, for every shell-facing resolver. */
364
+ export declare function isShellRootedSpellingUnmapped(family: PathFamily | undefined, spelled: string): boolean;
365
+ /** The win family's native spelling of a UNC written with any leading-separator mix (`//host/share/x`, `\\/host/…`
366
+ * ⇒ `\\\\host\\share\\x`), for a lexical normalizer that recognises the backslash form only; every other spelling is
367
+ * returned as is. */
368
+ export declare function nativeUncSpellingOf(family: PathFamily | undefined, spelled: string): string;
369
+ /**
370
+ * Join a RELATIVE-OR-ROOTED spelling to a base under the family, without folding (`..` across a symlinked
371
+ * parent is the env's to resolve). win = `path.win32` semantics: `/x`/`\x` lands on the base's drive or
372
+ * share root, anything else under the base with the base's own separator. posix = `/`-join.
373
+ */
374
+ export declare function joinForFamily(family: PathFamily, base: string, rel: string): string;
375
+ /** The family-aware LEXICAL view of a possibly-relative spelling against a resolution base (no I/O,
376
+ * `.`/`..` folded via {@link normalizeAbsPathLexically}): the SECOND deny-judgment view. It asks
377
+ * "does the NAME land on a guarded path", complementing the canonical
272
378
  * view's "does the TARGET" — a symlink AT a guarded spelling resolves away from it canonically,
273
379
  * and only this view still names it. */
274
380
  export declare function lexicalViewOf(spelled: string, base: string): string;
275
381
  /**
276
- * Resolve a model-supplied path to a canonical key AND enforce rootPath containment (design/44 §4
277
- * inv 5/6/7). Existing paths use `canonicalPath` (resolves symlinks, so the same file can't get two
278
- * keys). New paths canonicalize the **deepest existing ancestor** then rejoin the missing tail — a
279
- * symlinked parent therefore cannot land the target outside root. Returns the canonical key, or a
280
- * `path_not_in_root` violation. (Defense-in-depth at the tool layer; NOT a substitute for a sandboxed
281
- * `executionEnv` — see design/44 §5.)
382
+ * Resolve a model-supplied path to a canonical key AND enforce rootPath containment. Existing paths
383
+ * use `canonicalPath` (resolves symlinks, so the same file cannot get two keys). New paths
384
+ * canonicalize the **deepest existing ancestor** then rejoin the missing tail — a symlinked parent
385
+ * therefore cannot land the target outside root. Returns the canonical key, or a `path_not_in_root`
386
+ * violation. Defense-in-depth at the tool layer; NOT a substitute for a sandboxed `executionEnv`.
282
387
  */
283
388
  export declare function resolveKey(env: ExecutionEnv, rootCanonical: string, path: string, signal?: AbortSignal, baseCwd?: string, additionalRootsCanonical?: readonly string[], exactFileReadExemption?: (canonicalKey: string) => boolean, readDeny?: {
284
389
  matchTarget(canonicalKey: string, lexicalView?: string, opts?: {
@@ -299,11 +404,11 @@ export declare function resolveKey(env: ExecutionEnv, rootCanonical: string, pat
299
404
  * doesn't exist yet, the deepest EXISTING ancestor is canonicalized then the missing tail rejoined —
300
405
  * so a symlinked parent cannot land the target elsewhere). NO containment check — this is the pure
301
406
  * canonicalization shared by `resolveKey` (which adds rootPath containment) and
302
- * `createSensitivePathPolicy` (design/72 §2.1, which matches the real target against a guarded list).
407
+ * `createSensitivePathPolicy` (which matches the real target against a guarded list).
303
408
  * A single source of truth for symlink resolution: a sensitive-path policy that matched the raw path
304
- * instead of the real target would compare against the spelling, not the destination (§2.1).
409
+ * instead of the real target would compare against the spelling, not the destination.
305
410
  */
306
- export declare function canonicalizeTarget(env: ExecutionEnv, path: string, signal?: AbortSignal, baseCwd?: string): Promise<{
411
+ export declare function canonicalizeTarget(env: ExecutionEnv, path: string, signal: AbortSignal | undefined, family: PathFamily, baseCwd?: string): Promise<{
307
412
  ok: true;
308
413
  key: string;
309
414
  aliasResolved?: true;
@@ -311,20 +416,21 @@ export declare function canonicalizeTarget(env: ExecutionEnv, path: string, sign
311
416
  ok: false;
312
417
  message: string;
313
418
  unresolvedSymlink?: true;
419
+ refused?: Win32NamespaceRefusal;
314
420
  }>;
315
421
  /** Render a `FsViolation` as the model-facing tool error text (returned, never thrown — model retries). */
316
422
  export declare function violationText(toolName: string, v: FsViolation): string;
317
423
  /**
318
- * RB-371 ④ — structured `details` for a rendered violation: non-`undefined` ONLY for
319
- * `path_not_in_root`, the one code with per-refusal coordinates worth counting (which canonical
320
- * target, against which roots). Designed to be passed straight as `errorResult`'s second argument by
321
- * every renderer of a {@link resolveKey} violation — `undefined` keeps the pre-existing detail-less
322
- * shape for all other violation codes, so nothing else changes byte-wise.
424
+ * Structured `details` for a rendered violation: non-`undefined` ONLY for the two codes with
425
+ * per-refusal coordinates worth counting (which canonical target, against which roots or which deny
426
+ * pattern). Designed to be passed straight as `errorResult`'s second argument by every renderer of a
427
+ * {@link resolveKey} violation — `undefined` keeps the detail-less shape for all other violation
428
+ * codes.
323
429
  *
324
- * RB-397-c (回溯复查批): `type` is the WIRE discriminator the leg's card projector (`createStructuredProjector`, design/388 L4)
325
- * (tool-output-projection.ts) keys on `details.type`, so without it the card never reached
326
- * `tool_end.structured` and the "count from fields" promise above held for in-process readers only.
327
- * `code` stays as-is for the existing RB-371 consumers the new key is strictly additive.
430
+ * `type` is the WIRE discriminator: the card projector (`createStructuredProjector`,
431
+ * tool-output-projection.ts) keys on `details.type`, so without it the card never reaches
432
+ * `tool_end.structured` and the "count from fields" promise above holds for in-process readers only.
433
+ * `code` carries the same value for consumers that read it.
328
434
  */
329
435
  export declare function violationDetails(v: FsViolation): {
330
436
  type: "path_not_in_root";
@@ -338,7 +444,7 @@ export declare function violationDetails(v: FsViolation): {
338
444
  pattern: string;
339
445
  } | undefined;
340
446
  /**
341
- * RB-371 ② — does `p` sit inside ANY of the canonical containment roots? Exposed for the Bash
447
+ * Does `p` sit inside ANY of the canonical containment roots? Exposed for the Bash
342
448
  * tool's post-`cd` disclosure note (fs-bash.ts): the note reuses the SAME segment-aware `within`
343
449
  * the fence itself uses, so the disclosure and the fence can never disagree about what counts as
344
450
  * "inside". Pure/lexical — callers own any canonicalization of `p` (the tracked cwd stays raw by
@@ -346,37 +452,34 @@ export declare function violationDetails(v: FsViolation): {
346
452
  */
347
453
  export declare function withinAnyRoot(rootsCanonical: readonly string[], p: string): boolean;
348
454
  /**
349
- * RB-371 ① — the escape-hatch tail for the `path_not_in_root` refusal: its three sibling hints
350
- * below each name a sanctioned next step; this refusal named none, leaving the model to oscillate
351
- * between "the boundary is hard" and hunting for a way around it.
455
+ * The escape-hatch tail for the `path_not_in_root` refusal: like its three sibling hints below, it
456
+ * names a sanctioned next step, so the model does not oscillate between "the boundary is hard" and
457
+ * hunting for a way around it.
352
458
  *
353
459
  * The card names the SANCTIONED way out and nothing else: widening the declaration
354
- * (`additionalDirectories` / read-only `additionalReadDirectories`, design/119), or the
355
- * deployment-level `readFace: "open"` for reads.
460
+ * (`additionalDirectories` / read-only `additionalReadDirectories`), or the deployment-level
461
+ * `readFace: "open"` for reads.
356
462
  *
357
- * A trailing sentence naming the shell as unconfined by this fence was REMOVED (it had been kept on
358
- * the argument that a statement of fact is not a recommendation, and that omitting it would only let
359
- * the model discover the same thing by trial). A live retest settled that argument the other way: a
360
- * refused agent read the sentence as the next step, went to the shell, and rewrote through it the
361
- * very file the fence had just protected. A refusal card is a PROMPT surface naming, at the moment
362
- * of denial, a second tool that does not enforce the boundary is a recipe whatever its grammar, and
363
- * the model cannot be relied on to hear "fact, not advice". The underlying fact is unchanged and
364
- * still documented where humans read it (design/44 §5: the shell is deliberately unconfined by this
365
- * fence, and every shell call still passes the deployment's approval policy); it is simply no longer
366
- * handed to the model inside a denial. What the card says now: this is denied, and here is the
367
- * sanctioned way to be allowed.
463
+ * It deliberately does NOT name the shell as unconfined by this fence, even though that is a true
464
+ * statement rather than a recommendation. A live retest settled the question: a refused agent read
465
+ * such a sentence as the next step, went to the shell, and rewrote through it the very file the fence
466
+ * had just protected. A refusal card is a PROMPT surface — naming, at the moment of denial, a second
467
+ * tool that does not enforce the boundary is a recipe whatever its grammar, and the model cannot be
468
+ * relied on to hear "fact, not advice". The underlying fact is documented where humans read it (the
469
+ * shell is deliberately unconfined by this fence, and every shell call still passes the deployment's
470
+ * approval policy); it is simply not handed to the model inside a denial.
368
471
  */
369
472
  export declare const PATH_NOT_IN_ROOT_ESCAPE_HINT = "(This boundary applies to the structured file tools. If you genuinely need content outside the allowed root(s), ask for the directory to be added to the deployment's additionalDirectories \u2014 or additionalReadDirectories for read-only access; a deployment that wants reads open everywhere can declare readFace: \"open\" instead of listing directories.)";
370
473
  /** inv 1 (read-before-edit): a file must have been read this task before it can be edited/overwritten.
371
474
  * Message is CC 2.1.198 live-verbatim (all-tools-live-probe 2026-07-08 §2.1/§3.1/§5.1 — one message for
372
475
  * Edit/Write/NotebookEdit: "before writing to it", not the old sema "before editing").
373
476
  *
374
- * RB-200 F2 (CC 220 corpus-confirmed, @367198/@367671 — Edit and Write spell the read gate identically,
375
- * `!p || p.isPartialView`): an AUTO-TRUNCATED read (the model asked for the
376
- * file by default, no explicit offset/limit, and the token cap cut it anyway — {@link ReadEntry.isPartialView})
377
- * used to satisfy this gate exactly like a full read (`state.has(key)` alone was the whole check) the
378
- * model could Write (full overwrite) or Edit a file having genuinely seen only its first page, silently
379
- * destroying or blind-editing the unseen remainder. CC's own gate treats "never read" and "auto-truncated
477
+ * CC parity (Edit and Write spell the read gate identically, `!p || p.isPartialView`): an
478
+ * AUTO-TRUNCATED read (the model asked for the file by default, no explicit offset/limit, and the token
479
+ * cap cut it anyway — {@link ReadEntry.isPartialView}) must NOT satisfy this gate. With `state.has(key)`
480
+ * alone as the whole check, the model could Write (full overwrite) or Edit a file having genuinely seen
481
+ * only its first page, silently destroying or blind-editing the unseen remainder. CC's own gate treats
482
+ * "never read" and "auto-truncated
380
483
  * partial view" identically for BOTH tools — no softer rule for Edit (whose old_string match only touches
381
484
  * a small snippet) versus Write (which replaces everything), so this doesn't carve one out either.
382
485
  *
@@ -394,8 +497,7 @@ export declare function requireRead(state: ReadFileState, key: string): FsViolat
394
497
  */
395
498
  export declare const OVERSIZE_READ_ESCAPE_HINT = "(This file is over the Read tool's whole-file byte cap, so a default Read is refused \u2014 read it in slices with explicit offset/limit to satisfy the read-first rule, or inspect/transform it with bash (e.g. `sed -n`, `grep`) instead.)";
396
499
  /**
397
- * RB-264 N1 (repro `notebookedit-tool-read-gate-deadlock` assertion A.c2)
398
- * the escape-hatch parenthetical for the OTHER shape of the read-before-edit refusal: the file was read,
500
+ * The escape-hatch parenthetical for the OTHER shape of the read-before-edit refusal: the file was read,
399
501
  * but the read was AUTO-PAGINATED by the output token cap ({@link ReadEntry.isPartialView}), so only the
400
502
  * first page was ever seen. The oversize branch above has named its escape route since the fidelity
401
503
  * sweep; this branch named none — and it is the worse of the two, because the refusal's own advice
@@ -406,29 +508,29 @@ export declare const OVERSIZE_READ_ESCAPE_HINT = "(This file is over the Read to
406
508
  */
407
509
  export declare const PARTIAL_VIEW_READ_ESCAPE_HINT = "(Your last Read of this file returned only a PARTIAL view \u2014 the output token cap paginated it, so a default Read will keep returning the same page. Re-read it with explicit offset/limit (start from the page marker's next-page hint) until you have seen the part you are about to change; an explicit slice that fits satisfies the read-first rule. Or inspect/transform it with bash (e.g. `sed -n`, `grep`) instead.)";
408
510
  /**
409
- * RB-264 ⑥W1 — the escape-hatch parenthetical for the ONE shape of the
410
- * read-first deadlock that nothing observable diagnoses: a file the Read tool REFUSES (binary /
511
+ * The escape-hatch parenthetical for the ONE shape of the read-first deadlock that nothing observable
512
+ * diagnoses: a file the Read tool REFUSES (binary /
411
513
  * BOM-less UTF-16 / unknown encoding) can never satisfy read-before-write, and — unlike the oversize
412
514
  * and partial-view shapes — leaves no trace the refusal renderer can stat for. It is therefore the
413
515
  * renderer's FALLBACK hint (least specific, appended only when no sharper branch fired); appending it
414
516
  * beside a sharper one would assert something false, since both of those shapes are files Read handled.
415
517
  * Kept byte-identical to the sentence Write rendered inline before it moved here.
416
518
  *
417
- * ANG-6 (external black-box re-review, ruled 2026-08-03): the deadlock is a property of the READ GATE,
519
+ * The deadlock is a property of the READ GATE,
418
520
  * not of Write — Edit and NotebookEdit hit the identical dead end and used to render the bare sentence
419
521
  * with no way out named. All three now pass this one constant, which is why it is no longer named for
420
522
  * the tool that happened to have it first: one sentence, three call sites, nothing to drift apart.
421
523
  */
422
524
  export declare const READ_REFUSED_ESCAPE_HINT = "(If the Read tool refuses this file (binary/unknown encoding), overwrite or convert it with bash instead \u2014 e.g. `rm` + rewrite, or `iconv`.)";
423
525
  /**
424
- * Edit "no-op" guard (design/64 §7.2(4), CC `FileEditTool:148-153`): refuse an edit whose `old_string`
526
+ * Edit "no-op" guard (CC FileEditTool parity): refuse an edit whose `old_string`
425
527
  * equals `new_string` — without it the edit passes the match check and writes the file back unchanged (a
426
528
  * silent no-op that wastes a turn and dirties mtime). Verbatim CC message so a CC-trained model recognizes it.
427
529
  */
428
530
  export declare function checkNoChange(oldString: string, newString: string): FsViolation | undefined;
429
531
  /** inv 2 (staleness): the file's current content hash must match what was recorded at read time.
430
532
  *
431
- * RB-436-g: takes `ReadEntry | undefined` and treats an ABSENT entry as stale (fail-closed). The three
533
+ * Takes `ReadEntry | undefined` and treats an ABSENT entry as stale (fail-closed). The three
432
534
  * write-side callers prove the entry exists with `requireRead`, then re-read the file — an `await` — and
433
535
  * only then reach here, so each was asserting a pre-await invariant across a suspension point with `!`.
434
536
  * Nothing deletes read state mid-turn today (the compaction sweep runs at a turn boundary), so behaviour
@@ -441,7 +543,7 @@ export declare function countOccurrences(haystack: string, needle: string): numb
441
543
  * Suggest a sibling filename for a missing path (CC `File does not exist… Did you mean X?` self-heal
442
544
  * path, bundle :335883/:478419). Honest scope (1.253 双轨终审): this is the FALLBACK tier — an
443
545
  * approximation of CC's Cxe same-directory scan plus a sema-added case-insensitive exact-name match
444
- * (the case-typo class CC misses). RB-285 (归因更正): CC's FIRST-tier suggester is `G_e` — a GATED
546
+ * (the case-typo class CC misses). Registered delta: CC's FIRST-tier suggester is a GATED
445
547
  * sibling-tree REBASE (`join(cwd, relative(dirname(cwd), missing))`, only for a path under the cwd's
446
548
  * parent but not under the cwd), NOT the "re-resolve the filename against cwd" this comment used to
447
549
  * claim; the full geometry and the sema delta are recorded at the call site (fs-shared.ts
@@ -460,14 +562,14 @@ export declare function normalizeQuotes(s: string): string;
460
562
  */
461
563
  export declare function resolveQuoteMatch(content: string, oldString: string): string | undefined;
462
564
  /**
463
- * RB-285 M9 (CC `Pdt` layers 3+4, @366594-366600): resolve `oldString` against `content` across the
565
+ * CC's forgiving-match layers 3+4: resolve `oldString` against `content` across the
464
566
  * escape/literal spelling boundary. Returns the ACTUAL span present in `content` (which is what must be
465
567
  * replaced), or `undefined` when neither spelling is present. Pure — call only after an exact match and
466
568
  * the curly-quote layer have both missed.
467
569
  */
468
570
  export declare function resolveEscapeMatch(content: string, oldString: string): string | undefined;
469
571
  /**
470
- * RB-285 M9 (CC `YZu` verbatim, @366561-366587): rewrite `newString` into the SAME spelling the matched
572
+ * CC `YZu` verbatim: rewrite `newString` into the SAME spelling the matched
471
573
  * span uses. Without this, a match found through {@link resolveEscapeMatch} would replace an escaped
472
574
  * span with a literal-character replacement (or vice versa) and silently change the file's encoding
473
575
  * convention at that spot. Hex case follows what the file itself used, per-character where known and by
@@ -477,7 +579,7 @@ export declare function adaptNewStringEscapes(oldString: string, matchedOld: str
477
579
  /** CC `KZu` (@366558): the old_string shapes for which the escape layers above were even attempted —
478
580
  * gates the explanatory note appended to a no-match refusal so it is never a false claim. */
479
581
  export declare function escapeMatchWasAttempted(oldString: string): boolean;
480
- /** RB-285 M9 — CC `KZu`'s note (@367245-367248) verbatim. */
582
+ /** CC `KZu`'s note, verbatim. */
481
583
  export declare const ESCAPE_MATCH_MISS_NOTE = "\n(note: Edit also tried swapping \\uXXXX escapes and their characters; neither form matched, so the mismatch is likely elsewhere in old_string. Re-read the file and copy the exact surrounding text.)";
482
584
  /**
483
585
  * When the quote-forgiving match resolved to a curly-quoted file span, convert the straight quotes in
@@ -508,3 +610,4 @@ export declare function deletionOldString(content: string, oldString: string, ne
508
610
  * matters most (and these refusals return as tool results, which do not pass the loop-level budget). */
509
611
  export declare const EDIT_ECHO_MAX_CHARS = 200;
510
612
  export declare function checkEditMatch(content: string, oldString: string, replaceAll: boolean, truncated: boolean): FsViolation | undefined;
613
+ export {};