@sema-agent/core 7.48.0 → 7.49.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (421) hide show
  1. package/CHANGELOG.md +457 -0
  2. package/README.md +2 -2
  3. package/dist/agents/cascade.d.ts +2 -5
  4. package/dist/agents/child-failure.d.ts +20 -0
  5. package/dist/agents/child-failure.js +28 -0
  6. package/dist/agents/cross-session-ref.d.ts +11 -3
  7. package/dist/agents/cross-session-ref.js +6 -0
  8. package/dist/agents/cumulative-stats.d.ts +12 -15
  9. package/dist/agents/cumulative-stats.js +1 -1
  10. package/dist/agents/extra-tool-names.js +1 -1
  11. package/dist/agents/inherited-tool-pool.d.ts +17 -13
  12. package/dist/agents/inherited-tool-pool.js +4 -3
  13. package/dist/agents/peer-session-drain.d.ts +3 -2
  14. package/dist/agents/peer-session-drain.js +5 -4
  15. package/dist/agents/send-message-tool.js +2 -9
  16. package/dist/agents/subagent.d.ts +10 -29
  17. package/dist/agents/subagent.js +65 -53
  18. package/dist/agents/suspend-guard.d.ts +1 -1
  19. package/dist/agents/tool-filter.d.ts +76 -65
  20. package/dist/agents/tool-filter.js +122 -92
  21. package/dist/agents/verify.d.ts +4 -7
  22. package/dist/agents/verify.js +6 -7
  23. package/dist/bench/metrics.d.ts +1 -2
  24. package/dist/brain/anthropic.js +133 -79
  25. package/dist/brain/errors.d.ts +53 -6
  26. package/dist/brain/errors.js +22 -2
  27. package/dist/brain/input-too-long.d.ts +40 -19
  28. package/dist/brain/input-too-long.js +3 -16
  29. package/dist/brain/open-responses.js +19 -10
  30. package/dist/brain/openai.js +123 -32
  31. package/dist/brain/provider-dialect.d.ts +214 -0
  32. package/dist/brain/provider-dialect.js +186 -0
  33. package/dist/brain/request-params.d.ts +27 -8
  34. package/dist/brain/request-params.js +7 -12
  35. package/dist/brain/request-route.d.ts +84 -0
  36. package/dist/brain/request-route.js +41 -0
  37. package/dist/brain/retry.d.ts +8 -0
  38. package/dist/brain/retry.js +10 -4
  39. package/dist/brain/route-adjudicator.d.ts +7 -0
  40. package/dist/brain/route-adjudicator.js +5 -1
  41. package/dist/brain/status-sink.d.ts +20 -0
  42. package/dist/brain/status-sink.js +11 -0
  43. package/dist/brain/stream-engine.d.ts +52 -1
  44. package/dist/brain/stream-engine.js +81 -24
  45. package/dist/brain/stream-shared.d.ts +57 -11
  46. package/dist/brain/stream-shared.js +28 -2
  47. package/dist/core/a2a.d.ts +1 -0
  48. package/dist/core/agent-config-dir.js +3 -2
  49. package/dist/core/allow-entry.d.ts +21 -12
  50. package/dist/core/allow-entry.js +38 -31
  51. package/dist/core/arg-summary.d.ts +21 -28
  52. package/dist/core/arg-summary.js +235 -88
  53. package/dist/core/ask-origin.d.ts +42 -24
  54. package/dist/core/ask-origin.js +18 -13
  55. package/dist/core/auto-mode-prompt-assets.d.ts +2 -2
  56. package/dist/core/auto-mode-prompt-assets.js +1 -1
  57. package/dist/core/auto-mode-prompt.d.ts +11 -1
  58. package/dist/core/auto-mode-prompt.js +13 -10
  59. package/dist/core/auto-mode-rebuild.d.ts +7 -0
  60. package/dist/core/auto-mode-rebuild.js +4 -1
  61. package/dist/core/canonical-json.d.ts +3 -0
  62. package/dist/core/canonical-json.js +2 -1
  63. package/dist/core/checkpoint-requirements.d.ts +892 -0
  64. package/dist/core/checkpoint-requirements.js +333 -0
  65. package/dist/core/checkpoint-store.d.ts +94 -65
  66. package/dist/core/checkpoint-store.js +14 -26
  67. package/dist/core/child-clamp-seats.d.ts +228 -69
  68. package/dist/core/child-clamp-seats.js +211 -17
  69. package/dist/core/classifier-unavailable-arm.d.ts +5 -1
  70. package/dist/core/classifier-unavailable-arm.js +3 -3
  71. package/dist/core/compliance.d.ts +3 -0
  72. package/dist/core/compliance.js +9 -4
  73. package/dist/core/credential-vocabulary.d.ts +140 -0
  74. package/dist/core/credential-vocabulary.js +131 -0
  75. package/dist/core/decision-receipt.js +1 -1
  76. package/dist/core/effective-path-target.d.ts +73 -16
  77. package/dist/core/effective-path-target.js +63 -33
  78. package/dist/core/engine-notice.d.ts +43 -11
  79. package/dist/core/file-history-store.js +2 -2
  80. package/dist/core/fs-write-gate-policy.d.ts +4 -0
  81. package/dist/core/fs-write-gate-policy.js +22 -5
  82. package/dist/core/gate-bounded-await.d.ts +28 -13
  83. package/dist/core/gate-bounded-await.js +41 -4
  84. package/dist/core/gate-fold.d.ts +1 -1
  85. package/dist/core/gate-fold.js +24 -25
  86. package/dist/core/gate-lanes.js +16 -15
  87. package/dist/core/gate-outcome.d.ts +20 -5
  88. package/dist/core/gate-outcome.js +5 -0
  89. package/dist/core/governance-codes.d.ts +11 -4
  90. package/dist/core/governance-codes.js +14 -3
  91. package/dist/core/hooks.d.ts +21 -0
  92. package/dist/core/locked-config.d.ts +8 -0
  93. package/dist/core/locked-config.js +14 -2
  94. package/dist/core/lsp.js +3 -5
  95. package/dist/core/mailbox-store.d.ts +3 -4
  96. package/dist/core/mcp-connection-pool.d.ts +75 -0
  97. package/dist/core/mcp-connection-pool.js +128 -0
  98. package/dist/core/mcp-failure.d.ts +2 -1
  99. package/dist/core/mcp-failure.js +13 -2
  100. package/dist/core/mcp.d.ts +4 -0
  101. package/dist/core/mcp.js +70 -43
  102. package/dist/core/memory-engine/content-origin.js +2 -14
  103. package/dist/core/memory-engine/engine.d.ts +32 -22
  104. package/dist/core/memory-engine/engine.js +103 -55
  105. package/dist/core/memory-engine/file-backend.d.ts +13 -30
  106. package/dist/core/memory-engine/file-backend.js +37 -252
  107. package/dist/core/memory-engine/index.d.ts +1 -1
  108. package/dist/core/memory-engine/index.js +1 -1
  109. package/dist/core/memory-engine/layout.d.ts +139 -9
  110. package/dist/core/memory-engine/layout.js +350 -7
  111. package/dist/core/memory-engine/scope-contract.d.ts +10 -0
  112. package/dist/core/memory-engine/scope-contract.js +8 -0
  113. package/dist/core/memory-engine/session-incarnation.d.ts +14 -4
  114. package/dist/core/memory-engine/session-incarnation.js +5 -2
  115. package/dist/core/memory-engine/types.d.ts +10 -1
  116. package/dist/core/memory.js +5 -17
  117. package/dist/core/model-seat.d.ts +4 -4
  118. package/dist/core/model-seat.js +1 -1
  119. package/dist/core/mounted-mcp.d.ts +57 -0
  120. package/dist/core/mounted-mcp.js +19 -0
  121. package/dist/core/namespaced-rule-name.d.ts +29 -0
  122. package/dist/core/namespaced-rule-name.js +52 -0
  123. package/dist/core/park-selfcheck.d.ts +1 -1
  124. package/dist/core/park-selfcheck.js +16 -6
  125. package/dist/core/path-guard-rows.d.ts +99 -0
  126. package/dist/core/path-guard-rows.js +45 -0
  127. package/dist/core/path-identity.d.ts +22 -16
  128. package/dist/core/path-identity.js +10 -10
  129. package/dist/core/path-rule-binding.d.ts +2 -1
  130. package/dist/core/path-rule-binding.js +3 -1
  131. package/dist/core/pause-registry.d.ts +16 -2
  132. package/dist/core/pause-registry.js +6 -0
  133. package/dist/core/permission-rule-consent.js +0 -7
  134. package/dist/core/permission-rule-model.d.ts +68 -24
  135. package/dist/core/permission-rule-model.js +105 -24
  136. package/dist/core/permission-rule-store.js +2 -2
  137. package/dist/core/permission-rules.d.ts +3 -30
  138. package/dist/core/permission-rules.js +4 -54
  139. package/dist/core/read-root-candidate.d.ts +1 -3
  140. package/dist/core/read-root-candidate.js +4 -15
  141. package/dist/core/real-approval.d.ts +4 -3
  142. package/dist/core/retired-keys.d.ts +13 -0
  143. package/dist/core/retired-keys.js +14 -0
  144. package/dist/core/roles.d.ts +2 -3
  145. package/dist/core/roles.js +15 -6
  146. package/dist/core/runner/assemble-result.d.ts +0 -7
  147. package/dist/core/runner/assemble-result.js +19 -31
  148. package/dist/core/runner/caller-roster.d.ts +78 -0
  149. package/dist/core/runner/caller-roster.js +219 -0
  150. package/dist/core/runner/checkpoint-scope.d.ts +16 -16
  151. package/dist/core/runner/checkpoint-scope.js +2 -2
  152. package/dist/core/runner/child-tree.js +2 -2
  153. package/dist/core/runner/contracts.d.ts +68 -35
  154. package/dist/core/runner/decide-continuation.d.ts +51 -3
  155. package/dist/core/runner/decide-continuation.js +76 -21
  156. package/dist/core/runner/denial-limit-arms.d.ts +17 -5
  157. package/dist/core/runner/denial-limit-arms.js +11 -9
  158. package/dist/core/runner/extra-body-dropped-seat.d.ts +8 -0
  159. package/dist/core/runner/extra-body-dropped-seat.js +22 -0
  160. package/dist/core/runner/finish-reason-seat.d.ts +9 -0
  161. package/dist/core/runner/finish-reason-seat.js +40 -0
  162. package/dist/core/runner/gate-exit.d.ts +44 -32
  163. package/dist/core/runner/gate-exit.js +112 -25
  164. package/dist/core/runner/inherited-ask-grants.js +6 -4
  165. package/dist/core/runner/mcp-redial.d.ts +3 -3
  166. package/dist/core/runner/mcp-redial.js +3 -4
  167. package/dist/core/runner/permission-rule-lanes.d.ts +1 -2
  168. package/dist/core/runner/permission-rule-lanes.js +5 -4
  169. package/dist/core/runner/prepare-artifact.d.ts +4 -0
  170. package/dist/core/runner/prepare-artifact.js +2 -1
  171. package/dist/core/runner/prepare-ask-lane.d.ts +2 -0
  172. package/dist/core/runner/prepare-ask-lane.js +12 -6
  173. package/dist/core/runner/prepare-boundary-parks.d.ts +5 -8
  174. package/dist/core/runner/prepare-boundary-parks.js +4 -7
  175. package/dist/core/runner/prepare-caps-and-workflow.d.ts +11 -15
  176. package/dist/core/runner/prepare-caps-and-workflow.js +19 -27
  177. package/dist/core/runner/prepare-config-doors.d.ts +9 -9
  178. package/dist/core/runner/prepare-config-doors.js +10 -5
  179. package/dist/core/runner/prepare-defer-classify.d.ts +2 -2
  180. package/dist/core/runner/prepare-defer-classify.js +5 -3
  181. package/dist/core/runner/prepare-delegation-surface.d.ts +1 -1
  182. package/dist/core/runner/prepare-delegation-surface.js +3 -3
  183. package/dist/core/runner/prepare-gate-stations.d.ts +4 -4
  184. package/dist/core/runner/prepare-gate-stations.js +9 -7
  185. package/dist/core/runner/prepare-hands-readface.d.ts +7 -7
  186. package/dist/core/runner/prepare-hands-readface.js +7 -7
  187. package/dist/core/runner/prepare-inherited-gate.d.ts +7 -6
  188. package/dist/core/runner/prepare-inherited-gate.js +14 -15
  189. package/dist/core/runner/prepare-listings.d.ts +7 -24
  190. package/dist/core/runner/prepare-listings.js +4 -5
  191. package/dist/core/runner/prepare-memory-engine-session.d.ts +2 -0
  192. package/dist/core/runner/prepare-memory-engine-session.js +4 -3
  193. package/dist/core/runner/prepare-memory.d.ts +6 -0
  194. package/dist/core/runner/prepare-memory.js +13 -13
  195. package/dist/core/runner/prepare-offload-wrappers.d.ts +5 -0
  196. package/dist/core/runner/prepare-offload-wrappers.js +3 -3
  197. package/dist/core/runner/prepare-park-ask.d.ts +7 -8
  198. package/dist/core/runner/prepare-park-ask.js +17 -10
  199. package/dist/core/runner/prepare-policy-chain.js +24 -44
  200. package/dist/core/runner/prepare-project-context.d.ts +1 -1
  201. package/dist/core/runner/prepare-project-context.js +3 -4
  202. package/dist/core/runner/prepare-protocol-tools.d.ts +9 -3
  203. package/dist/core/runner/prepare-protocol-tools.js +3 -4
  204. package/dist/core/runner/prepare-run-refs.d.ts +3 -0
  205. package/dist/core/runner/prepare-run-refs.js +2 -1
  206. package/dist/core/runner/prepare-safety-scan.d.ts +4 -8
  207. package/dist/core/runner/prepare-safety-scan.js +1 -2
  208. package/dist/core/runner/prepare-suspend-saga.d.ts +7 -1
  209. package/dist/core/runner/prepare-suspend-saga.js +28 -12
  210. package/dist/core/runner/prepare-task.d.ts +1 -1
  211. package/dist/core/runner/prepare-task.js +67 -56
  212. package/dist/core/runner/prepare-wiring-manifest.d.ts +11 -10
  213. package/dist/core/runner/prepare-wiring-manifest.js +5 -8
  214. package/dist/core/runner/resume-apply.js +7 -14
  215. package/dist/core/runner/resume-checkpoint-screen.js +11 -4
  216. package/dist/core/runner/resume-internals-and-config.js +2 -2
  217. package/dist/core/runner/resume-policy-outcome.js +5 -32
  218. package/dist/core/runner/resume-review-outcome.js +4 -1
  219. package/dist/core/runner/retired-runner-deps.d.ts +6 -0
  220. package/dist/core/runner/retired-runner-deps.js +10 -1
  221. package/dist/core/runner/run-brain-sinks.d.ts +4 -0
  222. package/dist/core/runner/run-brain-sinks.js +5 -2
  223. package/dist/core/runner/run-harness-handlers.js +29 -36
  224. package/dist/core/runner/run-leg.d.ts +0 -2
  225. package/dist/core/runner/run-leg.js +32 -33
  226. package/dist/core/runner/run-terminal-adoption.js +4 -3
  227. package/dist/core/runner/runtask.js +8 -8
  228. package/dist/core/runner/session-rule-policy.js +5 -5
  229. package/dist/core/runner/stream-settle-backstop.js +7 -4
  230. package/dist/core/runner/sub-agent-deny-lists.d.ts +51 -12
  231. package/dist/core/runner/sub-agent-deny-lists.js +111 -80
  232. package/dist/core/runner/terminal-account.d.ts +48 -0
  233. package/dist/core/runner/terminal-account.js +186 -0
  234. package/dist/core/runner/tool-end-body.d.ts +54 -17
  235. package/dist/core/runner/tool-end-body.js +27 -2
  236. package/dist/core/runner/tool-face-overlay.d.ts +3 -1
  237. package/dist/core/runner/tool-face-overlay.js +6 -4
  238. package/dist/core/runner/tool-occurrence.d.ts +106 -0
  239. package/dist/core/runner/tool-occurrence.js +123 -0
  240. package/dist/core/runner/tool-progress-lane.d.ts +16 -8
  241. package/dist/core/runner/tool-progress-lane.js +25 -34
  242. package/dist/core/runner/tool-turn-repair-seat.d.ts +28 -4
  243. package/dist/core/runner/tool-turn-repair-seat.js +36 -13
  244. package/dist/core/runner/turn-attachments.d.ts +1 -1
  245. package/dist/core/runner/write-protection-station.d.ts +21 -9
  246. package/dist/core/runner/write-protection-station.js +20 -17
  247. package/dist/core/runner-deps.d.ts +12 -22
  248. package/dist/core/secret-env.d.ts +16 -5
  249. package/dist/core/secret-env.js +23 -63
  250. package/dist/core/sensitive-path-policy.d.ts +19 -18
  251. package/dist/core/sensitive-path-policy.js +73 -49
  252. package/dist/core/session-reconcile.d.ts +3 -0
  253. package/dist/core/session-reconcile.js +5 -4
  254. package/dist/core/session-store.d.ts +5 -0
  255. package/dist/core/session-store.js +12 -0
  256. package/dist/core/session.d.ts +13 -0
  257. package/dist/core/shared-memory/normalize.d.ts +17 -4
  258. package/dist/core/shared-memory/normalize.js +7 -7
  259. package/dist/core/shell-lexer.js +5 -4
  260. package/dist/core/shell-wrapper-projection.js +1 -1
  261. package/dist/core/skill-grant.js +1 -1
  262. package/dist/core/store-contracts/checkpoint-store-contract.js +13 -0
  263. package/dist/core/store-contracts/mailbox-store-contract.d.ts +7 -14
  264. package/dist/core/store-contracts/session-store-incarnation-contract.d.ts +27 -0
  265. package/dist/core/store-contracts/session-store-incarnation-contract.js +92 -0
  266. package/dist/core/stub-env.d.ts +2 -0
  267. package/dist/core/stub-env.js +1 -0
  268. package/dist/core/synthetic-read-verdict.js +2 -2
  269. package/dist/core/task-event.d.ts +59 -5
  270. package/dist/core/task-result.d.ts +1 -1
  271. package/dist/core/task-spec.d.ts +42 -9
  272. package/dist/core/terminal-cause.d.ts +94 -11
  273. package/dist/core/tighten-task-spec.d.ts +5 -4
  274. package/dist/core/tighten-task-spec.js +20 -25
  275. package/dist/core/tool-catalog-entries.d.ts +5 -7
  276. package/dist/core/tool-catalog-entries.js +14 -15
  277. package/dist/core/tool-face.d.ts +47 -0
  278. package/dist/core/tool-face.js +5 -0
  279. package/dist/core/tool-model-gate.d.ts +5 -5
  280. package/dist/core/tool-name-aliases.d.ts +26 -2
  281. package/dist/core/tool-name-aliases.js +13 -0
  282. package/dist/core/tool-name-list.d.ts +30 -0
  283. package/dist/core/tool-name-list.js +55 -0
  284. package/dist/core/tool-name-readings.d.ts +1 -9
  285. package/dist/core/tool-name-readings.js +1 -3
  286. package/dist/core/tool-policy.d.ts +195 -85
  287. package/dist/core/tool-policy.js +165 -380
  288. package/dist/core/tool-registry.d.ts +44 -1
  289. package/dist/core/tool-registry.js +26 -0
  290. package/dist/core/tool-roster.d.ts +19 -1
  291. package/dist/core/tool-roster.js +21 -2
  292. package/dist/core/tool-spec-members.d.ts +45 -0
  293. package/dist/core/tool-spec-members.js +169 -0
  294. package/dist/core/tool-spec.d.ts +46 -8
  295. package/dist/core/tools.d.ts +6 -2
  296. package/dist/core/tools.js +8 -4
  297. package/dist/core/untrusted-egress.js +29 -26
  298. package/dist/core/untrusted-text.d.ts +13 -10
  299. package/dist/core/untrusted-text.js +6 -44
  300. package/dist/core/workflow-journal-store.d.ts +4 -1
  301. package/dist/core/write-protect.d.ts +65 -29
  302. package/dist/core/write-protect.js +235 -177
  303. package/dist/engine/compaction/compaction.d.ts +2 -2
  304. package/dist/engine/execution-env/node-execution-env.d.ts +5 -23
  305. package/dist/engine/execution-env/node-execution-env.js +16 -38
  306. package/dist/engine/execution-env/symlink-chain.d.ts +23 -0
  307. package/dist/engine/execution-env/symlink-chain.js +32 -0
  308. package/dist/engine/harness/types.d.ts +7 -0
  309. package/dist/engine/llm/tool-turns.d.ts +3 -0
  310. package/dist/engine/llm/tool-turns.js +1 -0
  311. package/dist/engine/llm/types.d.ts +59 -23
  312. package/dist/engine/loop/agent-loop.js +16 -2
  313. package/dist/engine/session/memory-repo.js +11 -3
  314. package/dist/engine/session/repo-utils.d.ts +8 -0
  315. package/dist/engine/session/repo-utils.js +13 -0
  316. package/dist/index.d.ts +20 -23
  317. package/dist/index.js +17 -20
  318. package/dist/internal/harness.d.ts +1 -1
  319. package/dist/internal/harness.js +1 -1
  320. package/dist/internal/llm.d.ts +2 -2
  321. package/dist/internal/llm.js +1 -1
  322. package/dist/orchestration/governance-baseline-validity.js +1 -1
  323. package/dist/orchestration/run-spec.js +3 -3
  324. package/dist/orchestration/run-workflow-tool.d.ts +16 -35
  325. package/dist/orchestration/run-workflow-tool.js +38 -78
  326. package/dist/orchestration/workflow-governance.d.ts +21 -25
  327. package/dist/orchestration/workflow-governance.js +38 -78
  328. package/dist/orchestration/workflow-primitives.d.ts +7 -22
  329. package/dist/orchestration/workflow-primitives.js +5 -12
  330. package/dist/orchestration/workflow-types.d.ts +1 -1
  331. package/dist/orchestration/workflow.d.ts +61 -11
  332. package/dist/orchestration/workflow.js +189 -117
  333. package/dist/scenarios/full-body.d.ts +1 -1
  334. package/dist/shell/lex.d.ts +0 -21
  335. package/dist/shell/lex.js +250 -68
  336. package/dist/shell/shell-words.d.ts +35 -0
  337. package/dist/shell/shell-words.js +65 -0
  338. package/dist/stores/file/checkpoint-store.d.ts +5 -5
  339. package/dist/stores/file/checkpoint-store.js +5 -2
  340. package/dist/stores/file/index.d.ts +0 -8
  341. package/dist/stores/file/index.js +0 -12
  342. package/dist/stores/file/session-store.d.ts +3 -0
  343. package/dist/stores/file/session-store.js +44 -19
  344. package/dist/tools/artifact/artifact-tool.js +2 -0
  345. package/dist/tools/fs/bash-program-position.d.ts +12 -4
  346. package/dist/tools/fs/bash-program-position.js +32 -9
  347. package/dist/tools/fs/bash-readonly-classifier.d.ts +1 -1
  348. package/dist/tools/fs/bash-readonly-classifier.js +8 -7
  349. package/dist/tools/fs/containment-face.d.ts +4 -0
  350. package/dist/tools/fs/containment-face.js +3 -0
  351. package/dist/tools/fs/fs-shared.d.ts +10 -0
  352. package/dist/tools/fs/fs-shared.js +18 -0
  353. package/dist/tools/fs/fs-write.js +4 -2
  354. package/dist/tools/fs/index.d.ts +1 -1
  355. package/dist/tools/fs/program-read-model.d.ts +46 -20
  356. package/dist/tools/fs/program-read-model.js +50 -20
  357. package/dist/tools/fs/program-read-model.rows.d.ts +0 -14
  358. package/dist/tools/fs/program-read-model.rows.js +16 -15
  359. package/dist/tools/fs/read-deny-home.d.ts +33 -47
  360. package/dist/tools/fs/read-deny-home.js +35 -49
  361. package/dist/tools/fs/read-deny.d.ts +23 -22
  362. package/dist/tools/fs/read-deny.js +114 -89
  363. package/dist/tools/fs/removal-targets.d.ts +56 -23
  364. package/dist/tools/fs/removal-targets.js +65 -87
  365. package/dist/tools/fs/safety.d.ts +29 -22
  366. package/dist/tools/fs/safety.js +17 -11
  367. package/dist/tools/fs/search.js +10 -8
  368. package/dist/tools/fs/shell-read-argv.js +1 -1
  369. package/dist/tools/fs/shell-read-bindings.d.ts +169 -37
  370. package/dist/tools/fs/shell-read-bindings.js +324 -86
  371. package/dist/tools/fs/shell-read-builtins.d.ts +35 -0
  372. package/dist/tools/fs/shell-read-builtins.js +43 -0
  373. package/dist/tools/fs/shell-read-check.d.ts +15 -1
  374. package/dist/tools/fs/shell-read-check.js +67 -43
  375. package/dist/tools/fs/shell-read-establish.js +6 -6
  376. package/dist/tools/fs/shell-read-functions.d.ts +22 -0
  377. package/dist/tools/fs/shell-read-functions.js +29 -0
  378. package/dist/tools/fs/shell-read-judge.d.ts +51 -18
  379. package/dist/tools/fs/shell-read-judge.js +40 -4
  380. package/dist/tools/fs/shell-read-plan.d.ts +12 -6
  381. package/dist/tools/fs/shell-read-plan.js +118 -113
  382. package/dist/tools/fs/shell-read-start.d.ts +29 -6
  383. package/dist/tools/fs/shell-read-start.js +16 -9
  384. package/dist/tools/fs/shell-read-word.js +4 -0
  385. package/dist/tools/fs/shell-read-workdir.d.ts +156 -0
  386. package/dist/tools/fs/shell-read-workdir.js +281 -0
  387. package/dist/tools/fs/shell-read-write-targets.d.ts +5 -2
  388. package/dist/tools/fs/shell-read-write-targets.js +14 -5
  389. package/dist/tools/fs/shell-reentry-text.d.ts +4 -6
  390. package/dist/tools/fs/shell-reentry-text.js +3 -11
  391. package/dist/tools/task-list.d.ts +1 -2
  392. package/dist/tools/web.js +1 -1
  393. package/package.json +4 -1
  394. package/test/export-surface.snapshot.json +107 -143
  395. package/wire-changes/core@7.49.0.json +4765 -0
  396. package/dist/agents/teacher.d.ts +0 -159
  397. package/dist/agents/teacher.js +0 -577
  398. package/dist/agents/team.d.ts +0 -226
  399. package/dist/agents/team.js +0 -257
  400. package/dist/core/strategy-store.d.ts +0 -238
  401. package/dist/core/strategy-store.js +0 -253
  402. package/dist/orchestration/builtin-workflows.d.ts +0 -133
  403. package/dist/orchestration/builtin-workflows.js +0 -159
  404. package/dist/scenarios/env.d.ts +0 -40
  405. package/dist/scenarios/env.js +0 -39
  406. package/dist/scenarios/scenario-registry.d.ts +0 -110
  407. package/dist/scenarios/scenario-registry.js +0 -140
  408. package/dist/scenarios/teacher-quickstart.d.ts +0 -40
  409. package/dist/scenarios/teacher-quickstart.js +0 -12
  410. package/dist/stores/cc/lockfile.d.ts +0 -10
  411. package/dist/stores/cc/lockfile.js +0 -98
  412. package/dist/stores/cc/mailbox-store.d.ts +0 -20
  413. package/dist/stores/cc/mailbox-store.js +0 -255
  414. package/dist/stores/cc/roster-adapter.d.ts +0 -12
  415. package/dist/stores/cc/roster-adapter.js +0 -117
  416. package/dist/stores/cc/sidecar-transcript.d.ts +0 -97
  417. package/dist/stores/cc/sidecar-transcript.js +0 -264
  418. package/dist/stores/cc/task-list-store.d.ts +0 -2
  419. package/dist/stores/cc/task-list-store.js +0 -125
  420. package/dist/stores/file/strategy-store.d.ts +0 -97
  421. package/dist/stores/file/strategy-store.js +0 -341
@@ -3,15 +3,15 @@ import { isNamespacedCoveringRuleName, namespacedRuleNameCovers, parsePermission
3
3
  import { judgeRuleToolName, splitNamespacedName } from "./tool-name-aliases.js";
4
4
  import { catalogToolNames, isShellFaceToolName } from "./tool-registry.js";
5
5
  import { ALLOW_RULE_MEMBERS } from "./allow-rule-shadow.js";
6
- import { COMMAND_RULE_TOOL, READ_RULE_TOOL, findAdmittingRule, laneToolNamesOf, parseRuleText, programRunReachOf, ruleToolGrammarOf } from "./permission-rule-model.js";
6
+ import { COMMAND_RULE_TOOL, adjudicatePersistedDomainRules, adjudicatePersistedNameRules, domainRuleSubjectOf, findAdmittingRule, laneToolNamesOf, nameRuleSubjectOf, parseRuleText, programRunReachOf, ruleToolGrammarOf } from "./permission-rule-model.js";
7
7
  import { pathRuleLaneAnswer } from "./runner/permission-rule-lanes.js";
8
8
  import { callTargetReadingOf } from "./effective-path-target.js";
9
9
  import { splitShellCompoundSegments } from "../tools/fs/bash-readonly-classifier.js";
10
10
  import { mandateDisclosureOf } from "./ask-origin.js";
11
11
  import { inlineUntrusted } from "./untrusted-text.js";
12
- import { valueTypeLabel } from "../agents/tool-filter.js";
12
+ import { valueTypeLabel } from "./tool-name-list.js";
13
13
  import { deliverEngineNotice } from "./engine-notice.js";
14
- export const ALLOW_ENTRY_INERT_REASONS = Object.freeze(["wildcard_all", "namespace_wildcard", "path_pattern", "domain", "content_unsupported", "grammar", "tool_name", "near_miss"]);
14
+ export const ALLOW_ENTRY_INERT_REASONS = Object.freeze(["wildcard_all", "namespace_wildcard", "content_unsupported", "grammar", "tool_name", "near_miss"]);
15
15
  export function nonStringMemberLabel(value) {
16
16
  return `<not a string: ${valueTypeLabel(value)}>`;
17
17
  }
@@ -69,30 +69,21 @@ function readAllowEntry(raw, behavior, roster) {
69
69
  }
70
70
  return row(isShellFaceToolName(name) ? { kind: "shell", name } : { kind: "tool", name });
71
71
  }
72
- if (name === COMMAND_RULE_TOOL) {
73
- const allowRead = parseRuleText(raw, behavior);
74
- if ("rule" in allowRead)
75
- return row({ kind: "command", rule: ruleRowOf(allowRead.rule) });
76
- if (behavior === "allow" && allowRead.reject.code === "invalid.bare_interpreter_prefix") {
77
- const wide = parseRuleText(raw, "deny");
78
- if ("rule" in wide)
79
- return row({ kind: "command", rule: ruleRowOf({ ...wide.rule, behavior: "allow" }) });
80
- }
81
- return inert("grammar");
82
- }
83
- if (ruleToolGrammarOf(name) === "path") {
84
- const read = parseRuleText(raw, behavior);
85
- if ("rule" in read) {
86
- if (read.rule.match === "subpath" && read.rule.tool === READ_RULE_TOOL && behavior === "allow")
87
- return row({ kind: "read_dir", rule: ruleRowOf(read.rule) });
88
- if (behavior === "deny")
89
- return row({ kind: "path", rule: ruleRowOf(read.rule) });
90
- }
91
- return inert("path_pattern");
72
+ const grammar = ruleToolGrammarOf(name);
73
+ if (grammar === undefined)
74
+ return inert("content_unsupported");
75
+ const read = parseRuleText(raw, behavior);
76
+ if ("rule" in read)
77
+ return row({ kind: grammar, rule: ruleRowOf(read.rule) });
78
+ if (grammar === "command" && behavior === "allow" && read.reject.code === "invalid.bare_interpreter_prefix") {
79
+ const wide = parseRuleText(raw, "deny");
80
+ if ("rule" in wide)
81
+ return row({ kind: "command", rule: ruleRowOf({ ...wide.rule, behavior: "allow" }) });
92
82
  }
93
- if (/^domain:/.test(parsed.ruleContent))
94
- return inert("domain");
95
- return inert("content_unsupported");
83
+ return inert(inertReasonOf(read.reject.code));
84
+ }
85
+ function inertReasonOf(code) {
86
+ return code === "unsupported.tool" ? "content_unsupported" : "grammar";
96
87
  }
97
88
  export function compileAllowEntry(raw, behavior, roster) {
98
89
  const read = readAllowEntry(raw, behavior, roster);
@@ -150,8 +141,9 @@ export function autoStripped(compiled, call) {
150
141
  case "command":
151
142
  return shellContentStripped(shellRuleContent(compiled.rule));
152
143
  case "namespace":
153
- case "read_dir":
154
144
  case "path":
145
+ case "name":
146
+ case "domain":
155
147
  return false;
156
148
  default: {
157
149
  const exhaustive = compiled;
@@ -199,8 +191,7 @@ export function judgeAllowEntries(rows, req, behavior, ctx) {
199
191
  }
200
192
  }
201
193
  }
202
- const pathKind = behavior === "allow" ? "read_dir" : "path";
203
- const pathRows = live.filter((r) => r.compiled.kind === pathKind);
194
+ const pathRows = live.filter((r) => r.compiled.kind === "path");
204
195
  if (pathRows.length > 0) {
205
196
  const table = pathRows.map((r) => r.compiled.rule);
206
197
  for (const name of laneToolNamesOf(req).filter((n) => ruleToolGrammarOf(n) === "path")) {
@@ -212,6 +203,18 @@ export function judgeAllowEntries(rows, req, behavior, ctx) {
212
203
  }
213
204
  }
214
205
  }
206
+ for (const grammar of ["name", "domain"]) {
207
+ const familyRows = live.filter((r) => r.compiled.kind === grammar);
208
+ if (familyRows.length === 0)
209
+ continue;
210
+ const table = familyRows.map((r) => r.compiled.rule);
211
+ for (const name of laneToolNamesOf(req).filter((n) => ruleToolGrammarOf(n) === grammar)) {
212
+ const at = { tool: name, cwd: ctx.root, ...(ctx.sessionId !== undefined ? { sessionId: ctx.sessionId } : {}) };
213
+ const verdict = grammar === "name" ? adjudicatePersistedNameRules(table, { ...at, name: nameRuleSubjectOf(name, req.args) }) : adjudicatePersistedDomainRules(table, { ...at, host: domainRuleSubjectOf(name, req.args) });
214
+ if (verdict !== undefined && verdict.behavior === behavior)
215
+ return [familyRows.find((r) => r.compiled.rule === verdict.rules[0]) ?? familyRows[0]];
216
+ }
217
+ }
215
218
  return undefined;
216
219
  }
217
220
  export function allowingRows(rows, req, mode) {
@@ -233,6 +236,9 @@ export class AllowEntryTable {
233
236
  entries() {
234
237
  return this.held.map((r) => r.entry);
235
238
  }
239
+ holdsPathRows() {
240
+ return this.held.some((r) => r.compiled.kind === "path");
241
+ }
236
242
  lane(ctx) {
237
243
  return {
238
244
  allowing: (req, mode) => {
@@ -319,6 +325,7 @@ const SHADOW_REASON = {
319
325
  ancestor: "the task that delegated this one owns this question",
320
326
  dangerous_delete: "this removal targets a protected location (the filesystem root, a top-level directory, a home directory, or a workspace directory or one of its ancestors) — it is cleared by confirming this call, never by an allow rule",
321
327
  unverifiable_delete: "this removal could not be verified safe — it is cleared by confirming this call, never by an allow rule",
328
+ unverifiable_shell_text: "the text this shell will run cannot be read by the engine — it is cleared by confirming this call, never by an allow rule",
322
329
  transcript_write: "this write targets the session transcript — it is cleared by confirming this call, never by an allow rule",
323
330
  org_rule: "an organization policy rule requires approval for this call",
324
331
  governance_unavailable: "this deployment is org-governed and cannot currently read its organization policy",
@@ -329,7 +336,7 @@ const SHADOW_REASON = {
329
336
  requires_user_interaction: "this tool's server declares that a call must be put to a person",
330
337
  operator_forced: "this call arrived already marked as requiring confirmation (an earlier layer of this run, or the task that delegated it, judged it so) — it is cleared by confirming this call, never by a standing allow rule",
331
338
  shell_floor: mandateDisclosureOf("operator_always"),
332
- write_protection_unresolved: mandateDisclosureOf("write_protection_unresolved"),
339
+ target_unresolved: mandateDisclosureOf("target_unresolved"),
333
340
  probe: mandateDisclosureOf("probe_mandate"),
334
341
  write_protection: mandateDisclosureOf("write_protection"),
335
342
  tool_marks_mcp: mandateDisclosureOf("tool_marks"),
@@ -422,7 +429,7 @@ export function allowedToolsNotGrantedNotice(inert, sessionId) {
422
429
  return {
423
430
  code: ALLOWED_TOOL_NOT_GRANTED_CODE,
424
431
  message: `${inert.length} allowed-tools entr${inert.length === 1 ? "y grants" : "ies grant"} or refuse${inert.length === 1 ? "s" : ""} nothing — ${shown.join("; ")}${rest > 0 ? `; and ${rest} more` : ""}. ` +
425
- "A list holding `*` voids the list, `mcp__*` names no server, a write-path or domain entry has no allow reading, and a tool name must be spelled as the tool is mounted; name the tools (or `mcp__<server>`) instead." +
432
+ "A list holding `*` voids the list, `mcp__*` names no server, a rule the grammar refuses (`grammar`) or one on a tool with no such rule form (`content_unsupported`: an allow by path is an `Edit(…)` or `Read(…)` rule) grants nothing, and a tool name must be spelled as the tool is mounted; name the tools (or `mcp__<server>`) instead." +
426
433
  (refusing ? " Such an entry in a disallowed-tools list keeps its Skill from activating: a refusal that reads as nothing is not dropped." : ""),
427
434
  detail: { ...(sessionId !== undefined ? { sessionId } : {}), entries: inert.map((e) => ({ ...e })) },
428
435
  };
@@ -1,27 +1,15 @@
1
- /** Known credential SHAPES + `secretKey=value` — redacted from the FINAL abbreviated string, the single scrub
2
- * point before any store/SSE fan-out. Conservative (real credential formats). */
3
- /** every prefix is LEFT-ANCHORED with \`(?<![A-Za-z0-9])\` — without it, ordinary words whose
4
- * TAIL spells a prefix were silently redacted (NETWORK-attached → NETWO[redacted], RTMARK-100,
5
- * task_categories, monkey=abcd1234, MonkeyJumping…) while triggering unpredictably on suffix
6
- * length. \`_\` is deliberately NOT in the anchor class: \`api_key=\`, \`STRIPE=sk_live_…\`-style glued
7
- * assignments are REAL credential shapes that must keep scrubbing. The prefixed-token arm also
8
- * drops the \`i\` flag: the real vendors' prefixes are lowercase-only (sk_/pk_/rk_/ghp_…), and the
9
- * case-insensitive form is what turned every UPPERCASE technical word ending in RK/SK/PK into a
10
- * false positive. */
11
- export type RedactionConfidence = "high" | "medium" | "low";
12
- /** Which module's rule table minted a finding (ANG-11 provenance). A closed set, and there is no public
13
- * factory for a finding — a report's entries can only have been written by one of these tables running
14
- * here, so "this finding says X" is an engine statement, not something a caller can hand-assemble into
15
- * a report and have the pipeline endorse. */
16
- export type RedactionSource = "arg-summary" | "untrusted-egress" | "secret-env";
1
+ import { type RedactionConfidence, type RedactionSource, type ValueFamilyRow } from "./credential-vocabulary.js";
2
+ export type { RedactionConfidence, RedactionSource } from "./credential-vocabulary.js";
17
3
  /** One redaction hit: what was detected, where (ORIGINAL-input coordinates), how confidently, the marker
18
- * literal written in its place, and which rule table minted it. */
4
+ * literal written in its place, which rule table minted it, and — present when a vocabulary row minted it — the
5
+ * value family (the row's stable id; a host-leak hit such as a URL or a path names none). */
19
6
  export interface RedactionFinding {
20
7
  kind: string;
21
8
  span: readonly [number, number];
22
9
  confidence: RedactionConfidence;
23
10
  marker: string;
24
11
  source: RedactionSource;
12
+ family?: string;
25
13
  }
26
14
  /** The minimum a finding must declare to be summarizable. Both text findings ({@link RedactionFinding})
27
15
  * and the env arm's entry findings satisfy it, so {@link summarizeRedactions} is ONE helper rather than
@@ -56,13 +44,15 @@ export interface RedactionPass {
56
44
  kind: string;
57
45
  confidence: RedactionConfidence;
58
46
  marker: string;
59
- /** The module this pass is declared in — copied verbatim onto every finding it mints (ANG-11). It
60
- * travels WITH the pass, so a table composed into another module's pipeline (untrusted-egress chains
61
- * {@link SECRET_PASSES}) still attributes its hits to the table that owns the rule, not to whoever ran it. */
47
+ /** The rule table that owns this pass's rule — copied verbatim onto every finding it mints (ANG-11), whoever
48
+ * runs the pass. */
62
49
  source: RedactionSource;
63
50
  re: RegExp;
64
51
  /** A literal replacement (only `$1` substitution is used by these passes) or a replacer fn. */
65
52
  replace: string | ((match: string, ...groups: string[]) => string);
53
+ /** The value family a hit belongs to: one id, or — for a pass joining several rows — the id of the row that
54
+ * matches at the hit's offset in the pass input. Absent = the pass is no vocabulary row. */
55
+ family?: string | ((input: string, at: number) => string);
66
56
  }
67
57
  /**
68
58
  * Run the declared passes in order, collecting findings (original-input spans) when a report is given.
@@ -72,16 +62,19 @@ export interface RedactionPass {
72
62
  * only the first (or neither) matches. The OUTPUT keeps every format character that sits OUTSIDE a
73
63
  * replaced span — a joiner in an emoji sequence, a non-joiner in Persian text, a soft hyphen in a word
74
64
  * — and drops only those INSIDE the span the marker replaces (they were part of the secret). A
75
- * format-free input takes the exact historical path (byte-identical output). When the replacements
76
- * cannot be mapped back exactly (a later pass matched across an earlier pass's inserted text), the
77
- * output is the redacted view itself — every format character dropped: the failure direction is
78
- * over-stripping, never a leak.
65
+ * format-free input takes the exact historical path (byte-identical output). The view's replacements and
66
+ * every boundary scan's are taken as disjoint spans of their input (a later pass that matched across an
67
+ * earlier pass's inserted text is rendered from the removals instead), so the spans always map back
68
+ * exactly and no scan is skipped.
79
69
  */
80
70
  export declare function runRedactionPasses(input: string, passes: readonly RedactionPass[], report?: RedactionReport): string;
81
- /** The credential-shape passes (the pre-channel SECRET_PATTERNS, now with declared kinds/confidence).
82
- * Exported for {@link runRedactionPasses} composition (untrusted-egress chains its URL passes after
83
- * these in ONE pipeline so its findings' spans stay in original coordinates). */
84
- export declare const SECRET_PASSES: readonly RedactionPass[];
71
+ /**
72
+ * The egress view rendered as passes, in the vocabulary's order: one pass per group (a group of several rows is ONE
73
+ * leftmost alternation, its hits attributed to the row that matches at the hit's offset). A row whose wide form
74
+ * captures a label keeps it ahead of the marker; `replacers` renders a kind whose marker placement is a policy (the
75
+ * URL userinfo).
76
+ */
77
+ export declare function egressPasses(groups: ReadonlyArray<readonly ValueFamilyRow[]>, replacers?: Readonly<Record<string, (match: string, ...groups: string[]) => string>>): RedactionPass[];
85
78
  export declare function scrubSecrets(s: string, report?: RedactionReport): string;
86
79
  /** A SHORT, SECRET-SCRUBBED primary-arg summary. Abbreviation REDUCES the surface; a final {@link scrubSecrets}
87
80
  * pass is the leak boundary (the value is PERSISTED + SSE-forwarded and may be read by a platform operator in a
@@ -1,4 +1,5 @@
1
1
  import { writtenAssignmentPrefix } from "../tools/fs/bash-program-position.js";
2
+ import { CREDENTIAL_VOCABULARY, egressPassGroups } from "./credential-vocabulary.js";
2
3
  const ACTIVITY_ARG_MAX = 80;
3
4
  const SENSITIVE_KEY = /token|secret|key|password|passwd|credential|auth/i;
4
5
  export function summarizeRedactions(findings) {
@@ -12,72 +13,216 @@ export function summarizeRedactions(findings) {
12
13
  const parts = [...counts].map(([label, n]) => `${label} x${n}`);
13
14
  return `${findings.length} redaction${findings.length === 1 ? "" : "s"}: ${parts.join(", ")}`;
14
15
  }
15
- function mapBackOnePass(edits, pos) {
16
- let delta = 0;
17
- for (const e of edits) {
18
- const outStart = e.at + delta;
19
- if (pos < outStart)
20
- return pos - delta;
21
- const outEnd = outStart + e.insertedLen;
22
- if (pos < outEnd)
23
- return e.at;
24
- delta += e.insertedLen - e.removedLen;
25
- }
26
- return pos - delta;
16
+ function passZeroMapper(batches) {
17
+ const cursors = batches.map(() => ({ i: 0, delta: 0 }));
18
+ return (pos) => {
19
+ let p = pos;
20
+ for (let b = batches.length - 1; b >= 0; b--) {
21
+ const edits = batches[b];
22
+ const c = cursors[b];
23
+ let mapped;
24
+ while (mapped === undefined && c.i < edits.length) {
25
+ const e = edits[c.i];
26
+ const outStart = e.at + c.delta;
27
+ if (p < outStart)
28
+ mapped = p - c.delta;
29
+ else if (p < outStart + e.insertedLen)
30
+ mapped = e.at;
31
+ else {
32
+ c.delta += e.insertedLen - e.removedLen;
33
+ c.i++;
34
+ }
35
+ }
36
+ p = mapped ?? p - c.delta;
37
+ }
38
+ return p;
39
+ };
27
40
  }
28
41
  const PREEXISTING_MARKER_RE = /\[redacted(?:-[a-z]+)?\]/g;
29
42
  const FORMAT_CHAR_RE = /\p{Cf}/u;
30
43
  const MAX_FORMAT_SLOT_SCANS = 64;
31
44
  const REDACTED_WHOLE = "[redacted]";
32
- function applyPasses(input, passes, report, toOriginal) {
45
+ function finding(pass, span, family) {
46
+ const f = { kind: pass.kind, confidence: pass.confidence, span, marker: pass.marker, source: pass.source };
47
+ if (family !== undefined)
48
+ f.family = family;
49
+ return f;
50
+ }
51
+ function insertedFor(pass, match, groups) {
52
+ return typeof pass.replace === "string"
53
+ ? pass.replace.replace(/\$(\d)/g, (_m, d) => groups[Number(d) - 1] ?? "")
54
+ : pass.replace(match, ...groups);
55
+ }
56
+ function removedRegion(pass, match, groups, inserted) {
57
+ if (typeof pass.replace === "string") {
58
+ const parts = pass.replace.split(/(\$\d)/).filter((p) => p !== "");
59
+ const isRef = (p) => /^\$\d$/.test(p);
60
+ const refText = (p) => groups[Number(p[1]) - 1] ?? "";
61
+ let head = "";
62
+ for (const p of parts) {
63
+ if (!isRef(p))
64
+ break;
65
+ head += refText(p);
66
+ }
67
+ let tail = "";
68
+ for (let i = parts.length - 1; i >= 0 && isRef(parts[i]); i--)
69
+ tail = refText(parts[i]) + tail;
70
+ if (parts.every(isRef))
71
+ return [0, match.length];
72
+ if (!match.startsWith(head))
73
+ head = "";
74
+ if (!match.endsWith(tail) || head.length + tail.length > match.length)
75
+ tail = "";
76
+ return [head.length, match.length - tail.length];
77
+ }
78
+ let head = 0;
79
+ while (head < match.length && head < inserted.length && match[head] === inserted[head])
80
+ head++;
81
+ let tail = 0;
82
+ while (tail < match.length - head && tail < inserted.length - head && match[match.length - 1 - tail] === inserted[inserted.length - 1 - tail])
83
+ tail++;
84
+ return [head, match.length - tail];
85
+ }
86
+ function soloHits(input, passes) {
87
+ const hits = [];
88
+ passes.forEach((pass, i) => {
89
+ for (const m of input.matchAll(new RegExp(pass.re.source, pass.re.flags.includes("g") ? pass.re.flags : `${pass.re.flags}g`))) {
90
+ const inserted = insertedFor(pass, m[0], m.slice(1));
91
+ if (inserted === m[0])
92
+ continue;
93
+ const [a, b] = removedRegion(pass, m[0], m.slice(1), inserted);
94
+ if (b <= a)
95
+ continue;
96
+ const at = m.index;
97
+ const family = typeof pass.family === "function" ? pass.family(input, at) : pass.family;
98
+ hits.push({ start: at + a, end: at + b, marker: inserted.slice(a, inserted.length - (m[0].length - b)), pass: i, family, matchStart: at, matchEnd: at + m[0].length });
99
+ }
100
+ });
101
+ return hits;
102
+ }
103
+ function unremovedHits(hits, removals) {
104
+ const gone = [];
105
+ for (const r of [...removals].filter((x) => x.end > x.start).sort((x, y) => x.start - y.start || y.end - x.end)) {
106
+ const last = gone[gone.length - 1];
107
+ if (last !== undefined && r.start <= last[1])
108
+ last[1] = Math.max(last[1], r.end);
109
+ else
110
+ gone.push([r.start, r.end]);
111
+ }
112
+ return hits.filter((h) => {
113
+ let lo = 0;
114
+ let hi = gone.length - 1;
115
+ let at = -1;
116
+ while (lo <= hi) {
117
+ const mid = (lo + hi) >> 1;
118
+ if (gone[mid][0] <= h.start) {
119
+ at = mid;
120
+ lo = mid + 1;
121
+ }
122
+ else
123
+ hi = mid - 1;
124
+ }
125
+ return at === -1 || gone[at][1] < h.end;
126
+ });
127
+ }
128
+ function sequentialRemovals(batches) {
129
+ const removals = [];
130
+ for (let j = 0; j < batches.length; j++) {
131
+ const toPassZero = passZeroMapper(batches.slice(0, j));
132
+ for (const e of batches[j]) {
133
+ const start = toPassZero(e.at + e.head);
134
+ const end = toPassZero(e.at + e.removedLen - e.tail);
135
+ removals.push({ start, end: Math.max(start, end), marker: e.inserted.slice(e.head, e.insertedLen - e.tail), pass: e.pass, family: e.family });
136
+ }
137
+ }
138
+ return removals;
139
+ }
140
+ function renderRemovals(input, removals) {
141
+ const sorted = removals.filter((r) => r.end > r.start || r.marker !== "").sort((x, y) => x.start - y.start || y.end - x.end);
142
+ const edits = [];
143
+ let out = "";
144
+ let cursor = 0;
145
+ for (let i = 0; i < sorted.length;) {
146
+ const start = sorted[i].start;
147
+ let end = sorted[i].end;
148
+ let widest;
149
+ for (; i < sorted.length && (sorted[i].start < end || sorted[i].start === start || (sorted[i].marker === "" && sorted[i].start === end)); i++) {
150
+ const r = sorted[i];
151
+ if (r.end > end)
152
+ end = r.end;
153
+ if (r.marker !== "" && (widest === undefined || r.end - r.start > widest.end - widest.start))
154
+ widest = r;
155
+ }
156
+ const marker = widest?.marker ?? REDACTED_WHOLE;
157
+ out += input.slice(cursor, start) + marker;
158
+ cursor = end;
159
+ edits.push({ at: start, removedLen: end - start, insertedLen: marker.length, inserted: marker, family: widest?.family, head: 0, tail: 0, pass: widest?.pass ?? sorted[i - 1].pass });
160
+ }
161
+ return { out: out + input.slice(cursor), edits };
162
+ }
163
+ function runPasses(input, passes, findings, toOriginal) {
33
164
  const batches = [];
34
165
  let cur = input;
35
- for (const pass of passes) {
166
+ passes.forEach((pass, passIndex) => {
36
167
  const edits = [];
168
+ const passInput = cur;
169
+ const toPassZero = passZeroMapper(batches.slice());
37
170
  cur = cur.replace(pass.re, (...args) => {
38
171
  const match = args[0];
39
172
  const offset = args[args.length - 2];
40
173
  const groups = args.slice(1, -2);
41
- const inserted = typeof pass.replace === "string"
42
- ? pass.replace.replace(/\$(\d)/g, (_m, d) => groups[Number(d) - 1] ?? "")
43
- : pass.replace(match, ...groups);
44
- if (inserted !== match) {
45
- if (report !== undefined) {
46
- let s0 = offset;
47
- let e0 = offset + match.length;
48
- for (let i = batches.length - 1; i >= 0; i--) {
49
- s0 = mapBackOnePass(batches[i], s0);
50
- e0 = mapBackOnePass(batches[i], e0);
51
- }
52
- report.findings.push({
53
- kind: pass.kind,
54
- confidence: pass.confidence,
55
- span: [toOriginal(s0), toOriginal(e0)],
56
- marker: pass.marker,
57
- source: pass.source,
58
- });
59
- }
60
- edits.push({ at: offset, removedLen: match.length, insertedLen: inserted.length, inserted });
61
- }
174
+ const inserted = insertedFor(pass, match, groups);
175
+ if (inserted === match)
176
+ return inserted;
177
+ const family = typeof pass.family === "function" ? pass.family(passInput, offset) : pass.family;
178
+ if (findings !== undefined)
179
+ findings.push({ pass: passIndex, f: finding(pass, [toOriginal(toPassZero(offset)), toOriginal(toPassZero(offset + match.length))], family) });
180
+ const [a, b] = removedRegion(pass, match, groups, inserted);
181
+ edits.push({ at: offset, removedLen: match.length, insertedLen: inserted.length, inserted, family, head: a, tail: match.length - b, pass: passIndex });
62
182
  return inserted;
63
183
  });
64
184
  batches.push(edits);
65
- }
185
+ });
66
186
  return { out: cur, batches };
67
187
  }
188
+ function applyPasses(input, passes, report, toOriginal, disjoint = false) {
189
+ const found = report === undefined ? undefined : [];
190
+ const plain = runPasses(input, passes, found, toOriginal);
191
+ const solo = soloHits(input, passes);
192
+ const sequential = solo.length === 0 ? [] : sequentialRemovals(plain.batches);
193
+ const outer = unremovedHits(solo, sequential);
194
+ const mappable = !disjoint || replacedSpans(plain.batches) !== undefined;
195
+ const whole = outer.length === 0 && mappable ? undefined : renderRemovals(input, [...sequential, ...outer]);
196
+ if (whole === undefined || (whole.out === plain.out && mappable)) {
197
+ if (report !== undefined)
198
+ report.findings.push(...found.map((x) => x.f));
199
+ return plain;
200
+ }
201
+ if (report !== undefined) {
202
+ const seen = new Set(found.map((x) => `${x.pass}:${x.f.span[0]}:${x.f.span[1]}`));
203
+ for (const h of outer) {
204
+ const span = [toOriginal(h.matchStart), toOriginal(h.matchEnd)];
205
+ const key = `${h.pass}:${span[0]}:${span[1]}`;
206
+ if (seen.has(key))
207
+ continue;
208
+ seen.add(key);
209
+ found.push({ pass: h.pass, f: finding(passes[h.pass], span, h.family) });
210
+ }
211
+ found.sort((x, y) => x.pass - y.pass || x.f.span[0] - y.f.span[0]);
212
+ report.findings.push(...found.map((x) => x.f));
213
+ }
214
+ return { out: whole.out, batches: [whole.edits] };
215
+ }
68
216
  function replacedSpans(batches) {
69
217
  const spans = [];
70
218
  for (let j = 0; j < batches.length; j++) {
219
+ const toPassZero = passZeroMapper(batches.slice(0, j));
71
220
  for (const e of batches[j]) {
72
- let s0 = e.at;
73
- let e0 = e.at + e.removedLen;
74
- for (let i = j - 1; i >= 0; i--) {
75
- s0 = mapBackOnePass(batches[i], s0);
76
- e0 = mapBackOnePass(batches[i], e0);
77
- }
221
+ const s0 = toPassZero(e.at);
222
+ const e0 = toPassZero(e.at + e.removedLen);
78
223
  if (e0 - s0 !== e.removedLen)
79
224
  return undefined;
80
- spans.push({ start: s0, end: e0, inserted: e.inserted, pass: j });
225
+ spans.push({ start: s0, end: e0, inserted: e.inserted, pass: e.pass, family: e.family });
81
226
  }
82
227
  }
83
228
  spans.sort((a, b) => a.start - b.start);
@@ -125,10 +270,10 @@ export function runRedactionPasses(input, passes, report) {
125
270
  const idx = offsetToCharIndex.get(pos);
126
271
  return idx === undefined ? input.length : viewToOriginal[idx];
127
272
  };
128
- const { out, batches } = applyPasses(view, passes, report, toOriginal);
273
+ const { batches } = applyPasses(view, passes, report, toOriginal, true);
129
274
  const viewSpans = replacedSpans(batches);
130
275
  if (viewSpans === undefined)
131
- return out;
276
+ return REDACTED_WHOLE;
132
277
  const spans = viewSpans.map((sp) => {
133
278
  const startIdx = offsetToCharIndex.get(sp.start) ?? viewChars.length;
134
279
  const endIdx = offsetToCharIndex.get(sp.end) ?? viewChars.length;
@@ -150,42 +295,49 @@ export function runRedactionPasses(input, passes, report) {
150
295
  if (cutOffsets.length > MAX_FORMAT_SLOT_SCANS)
151
296
  return REDACTED_WHOLE;
152
297
  const extra = [];
153
- const rawSpans = replacedSpans(applyPasses(input, passes, undefined, (pos) => pos).batches);
298
+ const rawSpans = replacedSpans(applyPasses(input, passes, undefined, (pos) => pos, true).batches);
154
299
  if (rawSpans !== undefined)
155
300
  extra.push(...rawSpans);
156
301
  for (const cut of cutOffsets) {
157
- const cutSpans = replacedSpans(applyPasses(view.slice(cut), passes, undefined, (pos) => pos).batches);
302
+ const cutSpans = replacedSpans(applyPasses(view.slice(cut), passes, undefined, (pos) => pos, true).batches);
158
303
  if (cutSpans === undefined)
159
304
  continue;
160
305
  for (const c of cutSpans) {
161
306
  const o = viewOffsetToOriginal(c.start + cut, c.end + cut);
162
- extra.push({ start: o.start, end: o.end, inserted: c.inserted, pass: c.pass });
307
+ extra.push({ ...c, start: o.start, end: o.end });
163
308
  }
164
309
  }
165
310
  extra.sort((a, b) => a.start - b.start);
311
+ const firstEndingAfter = (pos) => {
312
+ let lo = 0;
313
+ let hi = spans.length;
314
+ while (lo < hi) {
315
+ const mid = (lo + hi) >> 1;
316
+ if (spans[mid].end > pos)
317
+ hi = mid;
318
+ else
319
+ lo = mid + 1;
320
+ }
321
+ return lo;
322
+ };
166
323
  for (const r of extra) {
167
- const overlapping = spans.find((v) => r.start < v.end && v.start < r.end);
168
- if (overlapping !== undefined) {
169
- if (r.start < overlapping.start)
170
- overlapping.start = r.start;
171
- if (r.end > overlapping.end)
172
- overlapping.end = r.end;
324
+ const at = firstEndingAfter(r.start);
325
+ const hit = spans[at];
326
+ if (hit !== undefined && hit.start < r.end) {
327
+ if (r.start < hit.start)
328
+ hit.start = r.start;
329
+ if (r.end > hit.end)
330
+ hit.end = r.end;
331
+ let next = at + 1;
332
+ while (next < spans.length && spans[next].start < hit.end)
333
+ hit.end = Math.max(hit.end, spans[next++].end);
334
+ if (next > at + 1)
335
+ spans.splice(at + 1, next - at - 1);
173
336
  continue;
174
337
  }
175
- spans.push({ start: r.start, end: r.end, inserted: r.inserted });
176
- if (report !== undefined) {
177
- const pass = passes[r.pass];
178
- report.findings.push({ kind: pass.kind, confidence: pass.confidence, span: [r.start, r.end], marker: pass.marker, source: pass.source });
179
- }
180
- spans.sort((a, b) => a.start - b.start);
181
- }
182
- for (let i = 1; i < spans.length;) {
183
- if (spans[i].start < spans[i - 1].end) {
184
- spans[i - 1].end = Math.max(spans[i - 1].end, spans[i].end);
185
- spans.splice(i, 1);
186
- }
187
- else
188
- i++;
338
+ spans.splice(at, 0, { start: r.start, end: r.end, inserted: r.inserted });
339
+ if (report !== undefined)
340
+ report.findings.push(finding(passes[r.pass], [r.start, r.end], r.family));
189
341
  }
190
342
  let result = "";
191
343
  let cursor = 0;
@@ -196,27 +348,22 @@ export function runRedactionPasses(input, passes, report) {
196
348
  result += input.slice(cursor);
197
349
  return result;
198
350
  }
199
- export const SECRET_PASSES = [
200
- { kind: "prefixed-token", confidence: "high", marker: "[redacted]", source: "arg-summary", re: /(?<![A-Za-z0-9])(?:sk|pk|rk|gh[opsur]|github_pat)[-_][A-Za-z0-9_-]{8,}/g, replace: "[redacted]" },
201
- { kind: "prefixed-token", confidence: "high", marker: "[redacted]", source: "arg-summary", re: /(?<![A-Za-z0-9])AIza[A-Za-z0-9_-]{20,}/g, replace: "[redacted]" },
202
- { kind: "prefixed-token", confidence: "high", marker: "[redacted]", source: "arg-summary", re: /(?<![A-Za-z0-9])xox[baprs]-[A-Za-z0-9-]{10,}/g, replace: "[redacted]" },
203
- { kind: "prefixed-token", confidence: "high", marker: "[redacted]", source: "arg-summary", re: /(?<![A-Za-z0-9])AKIA[0-9A-Z]{12,}/g, replace: "[redacted]" },
204
- { kind: "prefixed-token", confidence: "high", marker: "[redacted]", source: "arg-summary", re: /(?<![A-Za-z0-9])hf_[A-Za-z0-9]{20,}/g, replace: "[redacted]" },
205
- { kind: "jwt", confidence: "high", marker: "[redacted]", source: "arg-summary", re: /(?<![A-Za-z0-9])eyJ[A-Za-z0-9._-]{20,}/g, replace: "[redacted]" },
206
- { kind: "private-key-block", confidence: "high", marker: "[redacted]", source: "arg-summary", re: /-----BEGIN[ A-Z0-9]*PRIVATE KEY(?: BLOCK)?-----[\s\S]*?(?:-----END[ A-Z0-9]*PRIVATE KEY(?: BLOCK)?-----|$)/g, replace: "[redacted]" },
207
- { kind: "bearer-token", confidence: "high", marker: "[redacted]", source: "arg-summary", re: /(?<![A-Za-z0-9])Bearer\s+[A-Za-z0-9._~+/-]{8,}=*/gi, replace: "[redacted]" },
208
- { kind: "prefixed-token", confidence: "high", marker: "[redacted]", source: "arg-summary", re: /(AccountKey=)[A-Za-z0-9+/]{40,}={0,2}/g, replace: "$1[redacted]" },
209
- {
210
- kind: "keyword-value",
211
- confidence: "medium",
212
- marker: "[redacted]",
213
- source: "arg-summary",
214
- re: /(?<![A-Za-z0-9])((?:token|secret|key|password|passwd|credential|authorization)["']?\s*[=:]\s*["']?)[^\s"';|&]{4,}/gi,
215
- replace: "$1[redacted]",
216
- },
217
- ];
351
+ const BRACKET_MARKER = { "url-credentials": "[redacted-credentials]" };
352
+ const bracketMarker = (kind) => BRACKET_MARKER[kind] ?? "[redacted]";
353
+ const capturesLabel = (re) => new RegExp(`${re.source}|`).exec("").length > 1;
354
+ export function egressPasses(groups, replacers = {}) {
355
+ return groups.map((rows) => {
356
+ const head = rows[0];
357
+ const marker = bracketMarker(head.kind);
358
+ const re = rows.length === 1 ? head.match : new RegExp(rows.map((r) => `(?:${r.match.source})`).join("|"), head.match.flags);
359
+ const sticky = rows.map((r) => ({ id: r.id, re: new RegExp(r.match.source, r.match.flags.replace("g", "") + "y") }));
360
+ const family = rows.length === 1 ? head.id : (input, at) => sticky.find((s) => ((s.re.lastIndex = at), s.re.test(input)))?.id ?? head.id;
361
+ return { kind: head.kind, confidence: head.confidence, marker, source: head.source, re, replace: replacers[head.kind] ?? (capturesLabel(head.match) ? `$1${marker}` : marker), family };
362
+ });
363
+ }
364
+ const SHAPE_PASSES = egressPasses(egressPassGroups(CREDENTIAL_VOCABULARY).filter((rows) => rows[0].source === "arg-summary"));
218
365
  export function scrubSecrets(s, report) {
219
- return runRedactionPasses(s, SECRET_PASSES, report);
366
+ return runRedactionPasses(s, SHAPE_PASSES, report);
220
367
  }
221
368
  function truncCodePoints(s) {
222
369
  const cp = Array.from(s);