@namzu/sdk 46.0.0 → 48.0.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 (732) hide show
  1. package/CHANGELOG.md +325 -8
  2. package/README.md +7 -8
  3. package/dist/advisory/history.d.ts.map +1 -1
  4. package/dist/advisory/history.js +21 -1
  5. package/dist/advisory/history.js.map +1 -1
  6. package/dist/advisory/index.d.ts +1 -1
  7. package/dist/advisory/index.d.ts.map +1 -1
  8. package/dist/advisory/index.js +1 -1
  9. package/dist/advisory/index.js.map +1 -1
  10. package/dist/advisory/registry.d.ts +17 -0
  11. package/dist/advisory/registry.d.ts.map +1 -1
  12. package/dist/advisory/registry.js +26 -0
  13. package/dist/advisory/registry.js.map +1 -1
  14. package/dist/agents/PipelineAgent.d.ts +2 -46
  15. package/dist/agents/PipelineAgent.d.ts.map +1 -1
  16. package/dist/agents/PipelineAgent.js +2 -242
  17. package/dist/agents/PipelineAgent.js.map +1 -1
  18. package/dist/agents/QueryAgent.d.ts +21 -0
  19. package/dist/agents/QueryAgent.d.ts.map +1 -0
  20. package/dist/agents/QueryAgent.js +214 -0
  21. package/dist/agents/QueryAgent.js.map +1 -0
  22. package/dist/agents/ReactiveAgent.d.ts +4 -16
  23. package/dist/agents/ReactiveAgent.d.ts.map +1 -1
  24. package/dist/agents/ReactiveAgent.js +4 -183
  25. package/dist/agents/ReactiveAgent.js.map +1 -1
  26. package/dist/agents/RouterAgent.d.ts +2 -20
  27. package/dist/agents/RouterAgent.d.ts.map +1 -1
  28. package/dist/agents/RouterAgent.js +2 -266
  29. package/dist/agents/RouterAgent.js.map +1 -1
  30. package/dist/agents/SupervisorAgent.d.ts +3 -43
  31. package/dist/agents/SupervisorAgent.d.ts.map +1 -1
  32. package/dist/agents/SupervisorAgent.js +3 -418
  33. package/dist/agents/SupervisorAgent.js.map +1 -1
  34. package/dist/agents/defineAgent.d.ts +2 -1
  35. package/dist/agents/defineAgent.d.ts.map +1 -1
  36. package/dist/agents/defineAgent.js +6 -1
  37. package/dist/agents/defineAgent.js.map +1 -1
  38. package/dist/agents/examples/PipelineAgent.d.ts +46 -0
  39. package/dist/agents/examples/PipelineAgent.d.ts.map +1 -0
  40. package/dist/agents/examples/PipelineAgent.js +242 -0
  41. package/dist/agents/examples/PipelineAgent.js.map +1 -0
  42. package/dist/agents/examples/RouterAgent.d.ts +22 -0
  43. package/dist/agents/examples/RouterAgent.d.ts.map +1 -0
  44. package/dist/agents/examples/RouterAgent.js +268 -0
  45. package/dist/agents/examples/RouterAgent.js.map +1 -0
  46. package/dist/agents/examples/SupervisorAgent.d.ts +45 -0
  47. package/dist/agents/examples/SupervisorAgent.d.ts.map +1 -0
  48. package/dist/agents/examples/SupervisorAgent.js +406 -0
  49. package/dist/agents/examples/SupervisorAgent.js.map +1 -0
  50. package/dist/agents/explore.d.ts +6 -3
  51. package/dist/agents/explore.d.ts.map +1 -1
  52. package/dist/agents/explore.js +6 -3
  53. package/dist/agents/explore.js.map +1 -1
  54. package/dist/agents/forward-options.d.ts +11 -0
  55. package/dist/agents/forward-options.d.ts.map +1 -0
  56. package/dist/agents/forward-options.js +15 -0
  57. package/dist/agents/forward-options.js.map +1 -0
  58. package/dist/agents/index.d.ts +1 -0
  59. package/dist/agents/index.d.ts.map +1 -1
  60. package/dist/agents/index.js +2 -0
  61. package/dist/agents/index.js.map +1 -1
  62. package/dist/agents/runAgent.d.ts +19 -8
  63. package/dist/agents/runAgent.d.ts.map +1 -1
  64. package/dist/agents/runAgent.js +99 -29
  65. package/dist/agents/runAgent.js.map +1 -1
  66. package/dist/authorization/command-line.d.ts +2 -0
  67. package/dist/authorization/command-line.d.ts.map +1 -1
  68. package/dist/authorization/command-line.js +2 -2
  69. package/dist/authorization/command-line.js.map +1 -1
  70. package/dist/authorization/gate.d.ts +8 -0
  71. package/dist/authorization/gate.d.ts.map +1 -1
  72. package/dist/authorization/gate.js +12 -1
  73. package/dist/authorization/gate.js.map +1 -1
  74. package/dist/authorization/program.d.ts +151 -0
  75. package/dist/authorization/program.d.ts.map +1 -0
  76. package/dist/authorization/program.js +713 -0
  77. package/dist/authorization/program.js.map +1 -0
  78. package/dist/authorization/reexec-wrapper.d.ts +106 -0
  79. package/dist/authorization/reexec-wrapper.d.ts.map +1 -0
  80. package/dist/authorization/reexec-wrapper.js +532 -0
  81. package/dist/authorization/reexec-wrapper.js.map +1 -0
  82. package/dist/authorization/rules.d.ts +9 -0
  83. package/dist/authorization/rules.d.ts.map +1 -1
  84. package/dist/authorization/rules.js +8 -1
  85. package/dist/authorization/rules.js.map +1 -1
  86. package/dist/authorization/shell-lexer.d.ts +19 -4
  87. package/dist/authorization/shell-lexer.d.ts.map +1 -1
  88. package/dist/authorization/shell-lexer.js +91 -65
  89. package/dist/authorization/shell-lexer.js.map +1 -1
  90. package/dist/bridge/sse/mapper.d.ts.map +1 -1
  91. package/dist/bridge/sse/mapper.js +6 -0
  92. package/dist/bridge/sse/mapper.js.map +1 -1
  93. package/dist/capabilities/index.d.ts +54 -0
  94. package/dist/capabilities/index.d.ts.map +1 -0
  95. package/dist/capabilities/index.js +92 -0
  96. package/dist/capabilities/index.js.map +1 -0
  97. package/dist/config/registry.d.ts +2 -1
  98. package/dist/config/registry.d.ts.map +1 -1
  99. package/dist/config/registry.js +3 -2
  100. package/dist/config/registry.js.map +1 -1
  101. package/dist/connector/index.d.ts +4 -4
  102. package/dist/connector/index.d.ts.map +1 -1
  103. package/dist/connector/index.js +3 -3
  104. package/dist/connector/index.js.map +1 -1
  105. package/dist/connector/mcp/adapter.d.ts +16 -1
  106. package/dist/connector/mcp/adapter.d.ts.map +1 -1
  107. package/dist/connector/mcp/adapter.js +60 -4
  108. package/dist/connector/mcp/adapter.js.map +1 -1
  109. package/dist/connector/mcp/client.d.ts +13 -4
  110. package/dist/connector/mcp/client.d.ts.map +1 -1
  111. package/dist/connector/mcp/client.js +20 -3
  112. package/dist/connector/mcp/client.js.map +1 -1
  113. package/dist/connector/mcp/discovery.d.ts +24 -1
  114. package/dist/connector/mcp/discovery.d.ts.map +1 -1
  115. package/dist/connector/mcp/discovery.js +44 -0
  116. package/dist/connector/mcp/discovery.js.map +1 -1
  117. package/dist/connector/mcp/index.d.ts +2 -0
  118. package/dist/connector/mcp/index.d.ts.map +1 -1
  119. package/dist/connector/mcp/index.js +11 -0
  120. package/dist/connector/mcp/index.js.map +1 -1
  121. package/dist/connector/mcp/mcp-toolset.d.ts +133 -0
  122. package/dist/connector/mcp/mcp-toolset.d.ts.map +1 -0
  123. package/dist/connector/mcp/mcp-toolset.js +399 -0
  124. package/dist/connector/mcp/mcp-toolset.js.map +1 -0
  125. package/dist/connector/tools/index.d.ts +2 -2
  126. package/dist/connector/tools/index.d.ts.map +1 -1
  127. package/dist/connector/tools/index.js +1 -1
  128. package/dist/connector/tools/index.js.map +1 -1
  129. package/dist/connector/tools/router.d.ts +15 -13
  130. package/dist/connector/tools/router.d.ts.map +1 -1
  131. package/dist/connector/tools/router.js +30 -51
  132. package/dist/connector/tools/router.js.map +1 -1
  133. package/dist/contracts/a2a.d.ts +2 -2
  134. package/dist/directory/derive-supervisor.d.ts +2 -2
  135. package/dist/directory/derive-supervisor.d.ts.map +1 -1
  136. package/dist/directory/derive-supervisor.js +6 -7
  137. package/dist/directory/derive-supervisor.js.map +1 -1
  138. package/dist/directory/derive.d.ts.map +1 -1
  139. package/dist/directory/derive.js +5 -5
  140. package/dist/directory/derive.js.map +1 -1
  141. package/dist/execution/code-runtime/types.d.ts +2 -2
  142. package/dist/execution/code-runtime/types.js +1 -1
  143. package/dist/invariants/index.d.ts +2 -1
  144. package/dist/invariants/index.d.ts.map +1 -1
  145. package/dist/invariants/index.js +3 -2
  146. package/dist/invariants/index.js.map +1 -1
  147. package/dist/manager/agent/lifecycle.d.ts.map +1 -1
  148. package/dist/manager/agent/lifecycle.js +9 -0
  149. package/dist/manager/agent/lifecycle.js.map +1 -1
  150. package/dist/manager/connector/environment.d.ts +6 -0
  151. package/dist/manager/connector/environment.d.ts.map +1 -1
  152. package/dist/manager/connector/environment.js +11 -4
  153. package/dist/manager/connector/environment.js.map +1 -1
  154. package/dist/manager/connector/index.d.ts +2 -2
  155. package/dist/manager/connector/index.d.ts.map +1 -1
  156. package/dist/manager/connector/index.js +2 -2
  157. package/dist/manager/connector/index.js.map +1 -1
  158. package/dist/manager/connector/tenant.d.ts +6 -0
  159. package/dist/manager/connector/tenant.d.ts.map +1 -1
  160. package/dist/manager/connector/tenant.js +11 -4
  161. package/dist/manager/connector/tenant.js.map +1 -1
  162. package/dist/manager/index.d.ts +2 -2
  163. package/dist/manager/index.d.ts.map +1 -1
  164. package/dist/manager/index.js +2 -2
  165. package/dist/manager/index.js.map +1 -1
  166. package/dist/manager/resident/learning-observation.d.ts +4 -4
  167. package/dist/manager/resident/learning.d.ts +36 -36
  168. package/dist/manager/session/attribution.d.ts +22 -0
  169. package/dist/manager/session/attribution.d.ts.map +1 -0
  170. package/dist/manager/session/attribution.js +64 -0
  171. package/dist/manager/session/attribution.js.map +1 -0
  172. package/dist/manager/session/turn-recorder.d.ts +14 -0
  173. package/dist/manager/session/turn-recorder.d.ts.map +1 -1
  174. package/dist/manager/session/turn-recorder.js +208 -55
  175. package/dist/manager/session/turn-recorder.js.map +1 -1
  176. package/dist/peers/address.d.ts +36 -0
  177. package/dist/peers/address.d.ts.map +1 -0
  178. package/dist/peers/address.js +52 -0
  179. package/dist/peers/address.js.map +1 -0
  180. package/dist/peers/client.d.ts +47 -0
  181. package/dist/peers/client.d.ts.map +1 -0
  182. package/dist/peers/client.js +132 -0
  183. package/dist/peers/client.js.map +1 -0
  184. package/dist/peers/dir.d.ts +88 -0
  185. package/dist/peers/dir.d.ts.map +1 -0
  186. package/dist/peers/dir.js +139 -0
  187. package/dist/peers/dir.js.map +1 -0
  188. package/dist/peers/endpoint.d.ts +144 -0
  189. package/dist/peers/endpoint.d.ts.map +1 -0
  190. package/dist/peers/endpoint.js +420 -0
  191. package/dist/peers/endpoint.js.map +1 -0
  192. package/dist/peers/envelope.d.ts +42 -0
  193. package/dist/peers/envelope.d.ts.map +1 -0
  194. package/dist/peers/envelope.js +110 -0
  195. package/dist/peers/envelope.js.map +1 -0
  196. package/dist/peers/index.d.ts +30 -0
  197. package/dist/peers/index.d.ts.map +1 -0
  198. package/dist/peers/index.js +22 -0
  199. package/dist/peers/index.js.map +1 -0
  200. package/dist/peers/protocol.d.ts +645 -0
  201. package/dist/peers/protocol.d.ts.map +1 -0
  202. package/dist/peers/protocol.js +151 -0
  203. package/dist/peers/protocol.js.map +1 -0
  204. package/dist/peers/record.d.ts +71 -0
  205. package/dist/peers/record.d.ts.map +1 -0
  206. package/dist/peers/record.js +45 -0
  207. package/dist/peers/record.js.map +1 -0
  208. package/dist/peers/registry.d.ts +54 -0
  209. package/dist/peers/registry.d.ts.map +1 -0
  210. package/dist/peers/registry.js +213 -0
  211. package/dist/peers/registry.js.map +1 -0
  212. package/dist/plugin/define.d.ts +24 -0
  213. package/dist/plugin/define.d.ts.map +1 -0
  214. package/dist/plugin/define.js +42 -0
  215. package/dist/plugin/define.js.map +1 -0
  216. package/dist/plugin/index.d.ts +2 -0
  217. package/dist/plugin/index.d.ts.map +1 -1
  218. package/dist/plugin/index.js +1 -0
  219. package/dist/plugin/index.js.map +1 -1
  220. package/dist/plugin/lifecycle.d.ts +27 -3
  221. package/dist/plugin/lifecycle.d.ts.map +1 -1
  222. package/dist/plugin/lifecycle.js +242 -92
  223. package/dist/plugin/lifecycle.js.map +1 -1
  224. package/dist/plugin/resolver.d.ts +2 -2
  225. package/dist/plugin/resolver.d.ts.map +1 -1
  226. package/dist/plugin/resolver.js.map +1 -1
  227. package/dist/plugin/shell-hook.d.ts.map +1 -1
  228. package/dist/plugin/shell-hook.js +17 -3
  229. package/dist/plugin/shell-hook.js.map +1 -1
  230. package/dist/probe/errors.d.ts +2 -1
  231. package/dist/probe/errors.d.ts.map +1 -1
  232. package/dist/probe/errors.js +3 -2
  233. package/dist/probe/errors.js.map +1 -1
  234. package/dist/prompt/coding-agent-doctrine.d.ts +18 -7
  235. package/dist/prompt/coding-agent-doctrine.d.ts.map +1 -1
  236. package/dist/prompt/coding-agent-doctrine.js +11 -6
  237. package/dist/prompt/coding-agent-doctrine.js.map +1 -1
  238. package/dist/prompt/contributions.d.ts +4 -1
  239. package/dist/prompt/contributions.d.ts.map +1 -1
  240. package/dist/prompt/contributions.js +7 -2
  241. package/dist/prompt/contributions.js.map +1 -1
  242. package/dist/prompt/index.d.ts +1 -1
  243. package/dist/prompt/index.d.ts.map +1 -1
  244. package/dist/prompt/index.js +1 -1
  245. package/dist/prompt/index.js.map +1 -1
  246. package/dist/provider/collect-chat-completion.d.ts +6 -1
  247. package/dist/provider/collect-chat-completion.d.ts.map +1 -1
  248. package/dist/provider/collect-chat-completion.js +88 -10
  249. package/dist/provider/collect-chat-completion.js.map +1 -1
  250. package/dist/provider/mock.d.ts.map +1 -1
  251. package/dist/provider/mock.js +23 -6
  252. package/dist/provider/mock.js.map +1 -1
  253. package/dist/provider/registry.d.ts +2 -1
  254. package/dist/provider/registry.d.ts.map +1 -1
  255. package/dist/provider/registry.js +3 -2
  256. package/dist/provider/registry.js.map +1 -1
  257. package/dist/provider/tool-call-framing.d.ts +164 -0
  258. package/dist/provider/tool-call-framing.d.ts.map +1 -0
  259. package/dist/provider/tool-call-framing.js +163 -0
  260. package/dist/provider/tool-call-framing.js.map +1 -0
  261. package/dist/public-runtime.d.ts +24 -11
  262. package/dist/public-runtime.d.ts.map +1 -1
  263. package/dist/public-runtime.js +45 -9
  264. package/dist/public-runtime.js.map +1 -1
  265. package/dist/public-tools.d.ts +6 -4
  266. package/dist/public-tools.d.ts.map +1 -1
  267. package/dist/public-tools.js +8 -3
  268. package/dist/public-tools.js.map +1 -1
  269. package/dist/public-types.d.ts +13 -2
  270. package/dist/public-types.d.ts.map +1 -1
  271. package/dist/read-model/registry.d.ts +2 -1
  272. package/dist/read-model/registry.d.ts.map +1 -1
  273. package/dist/read-model/registry.js +3 -2
  274. package/dist/read-model/registry.js.map +1 -1
  275. package/dist/registry/ManagedRegistry.d.ts +30 -0
  276. package/dist/registry/ManagedRegistry.d.ts.map +1 -1
  277. package/dist/registry/ManagedRegistry.js +45 -6
  278. package/dist/registry/ManagedRegistry.js.map +1 -1
  279. package/dist/registry/collision.d.ts +57 -0
  280. package/dist/registry/collision.d.ts.map +1 -0
  281. package/dist/registry/collision.js +41 -0
  282. package/dist/registry/collision.js.map +1 -0
  283. package/dist/registry/command/index.d.ts +5 -3
  284. package/dist/registry/command/index.d.ts.map +1 -1
  285. package/dist/registry/command/index.js +6 -4
  286. package/dist/registry/command/index.js.map +1 -1
  287. package/dist/registry/index.d.ts +3 -4
  288. package/dist/registry/index.d.ts.map +1 -1
  289. package/dist/registry/index.js +2 -2
  290. package/dist/registry/index.js.map +1 -1
  291. package/dist/registry/plugin/index.d.ts.map +1 -1
  292. package/dist/registry/plugin/index.js +14 -1
  293. package/dist/registry/plugin/index.js.map +1 -1
  294. package/dist/registry/tool/callable.d.ts +7 -7
  295. package/dist/registry/tool/callable.d.ts.map +1 -1
  296. package/dist/registry/tool/callable.js +6 -7
  297. package/dist/registry/tool/callable.js.map +1 -1
  298. package/dist/registry/tool/execute.d.ts +14 -77
  299. package/dist/registry/tool/execute.d.ts.map +1 -1
  300. package/dist/registry/tool/execute.js +15 -727
  301. package/dist/registry/tool/execute.js.map +1 -1
  302. package/dist/registry/tool/portable.d.ts +25 -0
  303. package/dist/registry/tool/portable.d.ts.map +1 -1
  304. package/dist/registry/tool/portable.js +50 -0
  305. package/dist/registry/tool/portable.js.map +1 -1
  306. package/dist/registry/tool/presentation.d.ts +6 -5
  307. package/dist/registry/tool/presentation.d.ts.map +1 -1
  308. package/dist/registry/tool/presentation.js +3 -3
  309. package/dist/registry/tool/presentation.js.map +1 -1
  310. package/dist/runtime/bidi/session.d.ts +5 -4
  311. package/dist/runtime/bidi/session.d.ts.map +1 -1
  312. package/dist/runtime/bidi/session.js +18 -1
  313. package/dist/runtime/bidi/session.js.map +1 -1
  314. package/dist/runtime/query/executor/tool-call-admission.d.ts +45 -2
  315. package/dist/runtime/query/executor/tool-call-admission.d.ts.map +1 -1
  316. package/dist/runtime/query/executor/tool-call-admission.js +189 -5
  317. package/dist/runtime/query/executor/tool-call-admission.js.map +1 -1
  318. package/dist/runtime/query/executor.d.ts +33 -3
  319. package/dist/runtime/query/executor.d.ts.map +1 -1
  320. package/dist/runtime/query/executor.js +86 -10
  321. package/dist/runtime/query/executor.js.map +1 -1
  322. package/dist/runtime/query/file-evidence-replay.d.ts +1 -1
  323. package/dist/runtime/query/file-evidence-replay.d.ts.map +1 -1
  324. package/dist/runtime/query/file-evidence-replay.js +8 -7
  325. package/dist/runtime/query/file-evidence-replay.js.map +1 -1
  326. package/dist/runtime/query/fork/prepare.d.ts +4 -1
  327. package/dist/runtime/query/fork/prepare.d.ts.map +1 -1
  328. package/dist/runtime/query/fork/prepare.js +4 -0
  329. package/dist/runtime/query/fork/prepare.js.map +1 -1
  330. package/dist/runtime/query/guardrail-presets.d.ts +4 -3
  331. package/dist/runtime/query/guardrail-presets.d.ts.map +1 -1
  332. package/dist/runtime/query/guardrail-presets.js +4 -3
  333. package/dist/runtime/query/guardrail-presets.js.map +1 -1
  334. package/dist/runtime/query/index.d.ts +23 -10
  335. package/dist/runtime/query/index.d.ts.map +1 -1
  336. package/dist/runtime/query/index.js +149 -70
  337. package/dist/runtime/query/index.js.map +1 -1
  338. package/dist/runtime/query/iteration/index.d.ts.map +1 -1
  339. package/dist/runtime/query/iteration/index.js +8 -1
  340. package/dist/runtime/query/iteration/index.js.map +1 -1
  341. package/dist/runtime/query/iteration/phases/compaction.js +1 -1
  342. package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
  343. package/dist/runtime/query/iteration/phases/context.d.ts +2 -2
  344. package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
  345. package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
  346. package/dist/runtime/query/iteration/phases/tool-review.js +55 -2
  347. package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
  348. package/dist/runtime/query/iteration/stream-turn.d.ts.map +1 -1
  349. package/dist/runtime/query/iteration/stream-turn.js +342 -116
  350. package/dist/runtime/query/iteration/stream-turn.js.map +1 -1
  351. package/dist/runtime/query/iteration/tool-input.d.ts +79 -0
  352. package/dist/runtime/query/iteration/tool-input.d.ts.map +1 -0
  353. package/dist/runtime/query/iteration/tool-input.js +291 -0
  354. package/dist/runtime/query/iteration/tool-input.js.map +1 -0
  355. package/dist/runtime/query/observation-context.d.ts +2 -2
  356. package/dist/runtime/query/observation-context.d.ts.map +1 -1
  357. package/dist/runtime/query/observation-context.js.map +1 -1
  358. package/dist/runtime/query/prelude-lease.d.ts +18 -0
  359. package/dist/runtime/query/prelude-lease.d.ts.map +1 -0
  360. package/dist/runtime/query/prelude-lease.js +121 -0
  361. package/dist/runtime/query/prelude-lease.js.map +1 -0
  362. package/dist/runtime/query/prepare-turn.d.ts.map +1 -1
  363. package/dist/runtime/query/prepare-turn.js +386 -187
  364. package/dist/runtime/query/prepare-turn.js.map +1 -1
  365. package/dist/runtime/query/prompt-cache.d.ts +2 -2
  366. package/dist/runtime/query/prompt-cache.d.ts.map +1 -1
  367. package/dist/runtime/query/prompt.d.ts +2 -2
  368. package/dist/runtime/query/prompt.d.ts.map +1 -1
  369. package/dist/runtime/query/prompt.js +1 -1
  370. package/dist/runtime/query/prompt.js.map +1 -1
  371. package/dist/runtime/query/resume-session.d.ts.map +1 -1
  372. package/dist/runtime/query/resume-session.js +37 -2
  373. package/dist/runtime/query/resume-session.js.map +1 -1
  374. package/dist/runtime/query/review-policy.d.ts +22 -3
  375. package/dist/runtime/query/review-policy.d.ts.map +1 -1
  376. package/dist/runtime/query/review-policy.js +84 -6
  377. package/dist/runtime/query/review-policy.js.map +1 -1
  378. package/dist/runtime/query/tooling.d.ts +3 -2
  379. package/dist/runtime/query/tooling.d.ts.map +1 -1
  380. package/dist/runtime/query/tooling.js.map +1 -1
  381. package/dist/runtime/query/turn-state.d.ts.map +1 -1
  382. package/dist/runtime/query/turn-state.js +5 -0
  383. package/dist/runtime/query/turn-state.js.map +1 -1
  384. package/dist/runtime/system-events.d.ts +112 -0
  385. package/dist/runtime/system-events.d.ts.map +1 -0
  386. package/dist/runtime/system-events.js +183 -0
  387. package/dist/runtime/system-events.js.map +1 -0
  388. package/dist/scheduler/delegating.d.ts +2 -1
  389. package/dist/scheduler/delegating.d.ts.map +1 -1
  390. package/dist/scheduler/delegating.js +3 -2
  391. package/dist/scheduler/delegating.js.map +1 -1
  392. package/dist/skills/index.d.ts +1 -1
  393. package/dist/skills/index.d.ts.map +1 -1
  394. package/dist/skills/index.js +1 -1
  395. package/dist/skills/index.js.map +1 -1
  396. package/dist/skills/registry.d.ts +12 -0
  397. package/dist/skills/registry.d.ts.map +1 -1
  398. package/dist/skills/registry.js +19 -0
  399. package/dist/skills/registry.js.map +1 -1
  400. package/dist/store/evidence/index-page.d.ts +2 -2
  401. package/dist/store/session-log/conformance.d.ts +1 -1
  402. package/dist/store/session-log/conformance.d.ts.map +1 -1
  403. package/dist/store/session-log/conformance.js +49 -8
  404. package/dist/store/session-log/conformance.js.map +1 -1
  405. package/dist/store/session-log/core.d.ts +8 -3
  406. package/dist/store/session-log/core.d.ts.map +1 -1
  407. package/dist/store/session-log/core.js +2 -1
  408. package/dist/store/session-log/core.js.map +1 -1
  409. package/dist/store/session-log/fold.d.ts +12 -0
  410. package/dist/store/session-log/fold.d.ts.map +1 -1
  411. package/dist/store/session-log/fold.js +57 -7
  412. package/dist/store/session-log/fold.js.map +1 -1
  413. package/dist/store/session-log/index.d.ts +2 -2
  414. package/dist/store/session-log/index.d.ts.map +1 -1
  415. package/dist/store/session-log/index.js +1 -1
  416. package/dist/store/session-log/index.js.map +1 -1
  417. package/dist/test-support/toolset.d.ts +19 -0
  418. package/dist/test-support/toolset.d.ts.map +1 -0
  419. package/dist/test-support/toolset.js +20 -0
  420. package/dist/test-support/toolset.js.map +1 -0
  421. package/dist/tools/builtins/bash.d.ts +29 -0
  422. package/dist/tools/builtins/bash.d.ts.map +1 -1
  423. package/dist/tools/builtins/bash.js +19 -9
  424. package/dist/tools/builtins/bash.js.map +1 -1
  425. package/dist/tools/builtins/browser-url.d.ts +1 -1
  426. package/dist/tools/builtins/browser-url.js +1 -1
  427. package/dist/tools/builtins/browser.d.ts +6 -6
  428. package/dist/tools/builtins/computer-use.d.ts.map +1 -1
  429. package/dist/tools/builtins/computer-use.js +19 -11
  430. package/dist/tools/builtins/computer-use.js.map +1 -1
  431. package/dist/tools/builtins/edit.d.ts.map +1 -1
  432. package/dist/tools/builtins/edit.js +9 -1
  433. package/dist/tools/builtins/edit.js.map +1 -1
  434. package/dist/tools/builtins/json-string-hint.d.ts +15 -0
  435. package/dist/tools/builtins/json-string-hint.d.ts.map +1 -0
  436. package/dist/tools/builtins/json-string-hint.js +17 -0
  437. package/dist/tools/builtins/json-string-hint.js.map +1 -0
  438. package/dist/tools/builtins/search-tools.d.ts.map +1 -1
  439. package/dist/tools/builtins/search-tools.js +7 -15
  440. package/dist/tools/builtins/search-tools.js.map +1 -1
  441. package/dist/tools/builtins/write-file.d.ts.map +1 -1
  442. package/dist/tools/builtins/write-file.js +11 -0
  443. package/dist/tools/builtins/write-file.js.map +1 -1
  444. package/dist/tools/command-shell.d.ts +10 -0
  445. package/dist/tools/command-shell.d.ts.map +1 -1
  446. package/dist/tools/command-shell.js +45 -1
  447. package/dist/tools/command-shell.js.map +1 -1
  448. package/dist/tools/coordinator/agent.d.ts.map +1 -1
  449. package/dist/tools/coordinator/agent.js +5 -0
  450. package/dist/tools/coordinator/agent.js.map +1 -1
  451. package/dist/tools/coordinator/ask-user-question.d.ts.map +1 -1
  452. package/dist/tools/coordinator/ask-user-question.js +26 -13
  453. package/dist/tools/coordinator/ask-user-question.js.map +1 -1
  454. package/dist/tools/coordinator/index.d.ts.map +1 -1
  455. package/dist/tools/coordinator/index.js +5 -0
  456. package/dist/tools/coordinator/index.js.map +1 -1
  457. package/dist/tools/coordinator/question-options.d.ts +78 -0
  458. package/dist/tools/coordinator/question-options.d.ts.map +1 -0
  459. package/dist/tools/coordinator/question-options.js +153 -0
  460. package/dist/tools/coordinator/question-options.js.map +1 -0
  461. package/dist/tools/defineTool.d.ts +10 -0
  462. package/dist/tools/defineTool.d.ts.map +1 -1
  463. package/dist/tools/defineTool.js +17 -0
  464. package/dist/tools/defineTool.js.map +1 -1
  465. package/dist/tools/memory/search.d.ts.map +1 -1
  466. package/dist/tools/memory/search.js +4 -1
  467. package/dist/tools/memory/search.js.map +1 -1
  468. package/dist/tools/memory/update.d.ts.map +1 -1
  469. package/dist/tools/memory/update.js +8 -5
  470. package/dist/tools/memory/update.js.map +1 -1
  471. package/dist/tools/render-nonce.d.ts +47 -0
  472. package/dist/tools/render-nonce.d.ts.map +1 -0
  473. package/dist/tools/render-nonce.js +63 -0
  474. package/dist/tools/render-nonce.js.map +1 -0
  475. package/dist/tools/roster.d.ts +46 -19
  476. package/dist/tools/roster.d.ts.map +1 -1
  477. package/dist/tools/roster.js +63 -33
  478. package/dist/tools/roster.js.map +1 -1
  479. package/dist/tools/schedules/schedule-tool.d.ts.map +1 -1
  480. package/dist/tools/schedules/schedule-tool.js +94 -14
  481. package/dist/tools/schedules/schedule-tool.js.map +1 -1
  482. package/dist/tools/schedules/types.d.ts +52 -6
  483. package/dist/tools/schedules/types.d.ts.map +1 -1
  484. package/dist/tools/trusted-read-only.d.ts +9 -5
  485. package/dist/tools/trusted-read-only.d.ts.map +1 -1
  486. package/dist/tools/trusted-read-only.js +14 -10
  487. package/dist/tools/trusted-read-only.js.map +1 -1
  488. package/dist/tools/untrusted-envelope.d.ts +67 -28
  489. package/dist/tools/untrusted-envelope.d.ts.map +1 -1
  490. package/dist/tools/untrusted-envelope.js +108 -59
  491. package/dist/tools/untrusted-envelope.js.map +1 -1
  492. package/dist/toolsets/combine.d.ts +44 -0
  493. package/dist/toolsets/combine.d.ts.map +1 -0
  494. package/dist/toolsets/combine.js +153 -0
  495. package/dist/toolsets/combine.js.map +1 -0
  496. package/dist/toolsets/manager.d.ts +201 -0
  497. package/dist/toolsets/manager.d.ts.map +1 -0
  498. package/dist/toolsets/manager.js +837 -0
  499. package/dist/toolsets/manager.js.map +1 -0
  500. package/dist/toolsets/source-glob.d.ts +15 -0
  501. package/dist/toolsets/source-glob.d.ts.map +1 -0
  502. package/dist/toolsets/source-glob.js +23 -0
  503. package/dist/toolsets/source-glob.js.map +1 -0
  504. package/dist/toolsets/toolset.d.ts +20 -0
  505. package/dist/toolsets/toolset.d.ts.map +1 -0
  506. package/dist/toolsets/toolset.js +28 -0
  507. package/dist/toolsets/toolset.js.map +1 -0
  508. package/dist/toolsets/types.d.ts +128 -0
  509. package/dist/toolsets/types.d.ts.map +1 -0
  510. package/dist/toolsets/types.js +33 -0
  511. package/dist/toolsets/types.js.map +1 -0
  512. package/dist/toolsets/wrappers.d.ts +59 -0
  513. package/dist/toolsets/wrappers.d.ts.map +1 -0
  514. package/dist/toolsets/wrappers.js +165 -0
  515. package/dist/toolsets/wrappers.js.map +1 -0
  516. package/dist/types/agent/base.d.ts +27 -16
  517. package/dist/types/agent/base.d.ts.map +1 -1
  518. package/dist/types/agent/index.d.ts +1 -0
  519. package/dist/types/agent/index.d.ts.map +1 -1
  520. package/dist/types/agent/index.js +1 -0
  521. package/dist/types/agent/index.js.map +1 -1
  522. package/dist/types/agent/pipeline.d.ts +2 -0
  523. package/dist/types/agent/pipeline.d.ts.map +1 -1
  524. package/dist/types/agent/query.d.ts +117 -0
  525. package/dist/types/agent/query.d.ts.map +1 -0
  526. package/dist/types/agent/query.js +2 -0
  527. package/dist/types/agent/query.js.map +1 -0
  528. package/dist/types/agent/reactive.d.ts +2 -129
  529. package/dist/types/agent/reactive.d.ts.map +1 -1
  530. package/dist/types/agent/router.d.ts +2 -0
  531. package/dist/types/agent/router.d.ts.map +1 -1
  532. package/dist/types/agent/supervisor.d.ts +5 -2
  533. package/dist/types/agent/supervisor.d.ts.map +1 -1
  534. package/dist/types/authorization/index.d.ts +49 -12
  535. package/dist/types/authorization/index.d.ts.map +1 -1
  536. package/dist/types/authorization/index.js +6 -0
  537. package/dist/types/authorization/index.js.map +1 -1
  538. package/dist/types/connector/mcp.d.ts +32 -0
  539. package/dist/types/connector/mcp.d.ts.map +1 -1
  540. package/dist/types/errors/index.d.ts +15 -0
  541. package/dist/types/errors/index.d.ts.map +1 -1
  542. package/dist/types/errors/index.js.map +1 -1
  543. package/dist/types/hitl/index.d.ts +57 -0
  544. package/dist/types/hitl/index.d.ts.map +1 -1
  545. package/dist/types/hitl/index.js.map +1 -1
  546. package/dist/types/message/index.d.ts +130 -9
  547. package/dist/types/message/index.d.ts.map +1 -1
  548. package/dist/types/message/index.js +4 -1
  549. package/dist/types/message/index.js.map +1 -1
  550. package/dist/types/plugin/index.d.ts +22 -4
  551. package/dist/types/plugin/index.d.ts.map +1 -1
  552. package/dist/types/plugin/index.js +1 -0
  553. package/dist/types/plugin/index.js.map +1 -1
  554. package/dist/types/provider/chat.d.ts +5 -0
  555. package/dist/types/provider/chat.d.ts.map +1 -1
  556. package/dist/types/provider/config.d.ts +8 -3
  557. package/dist/types/provider/config.d.ts.map +1 -1
  558. package/dist/types/provider/stream.d.ts +39 -0
  559. package/dist/types/provider/stream.d.ts.map +1 -1
  560. package/dist/types/sandbox/index.d.ts +1 -1
  561. package/dist/types/session/events.d.ts +14 -3
  562. package/dist/types/session/events.d.ts.map +1 -1
  563. package/dist/types/session/events.js.map +1 -1
  564. package/dist/types/tool/index.d.ts +152 -91
  565. package/dist/types/tool/index.d.ts.map +1 -1
  566. package/dist/types/tool/index.js.map +1 -1
  567. package/dist/types/toolset/index.d.ts +10 -40
  568. package/dist/types/toolset/index.d.ts.map +1 -1
  569. package/package.json +1 -1
  570. package/src/advisory/history.ts +27 -2
  571. package/src/advisory/index.ts +1 -1
  572. package/src/advisory/registry.ts +33 -0
  573. package/src/agents/AGENTS.md +14 -7
  574. package/src/agents/PipelineAgent.ts +2 -303
  575. package/src/agents/QueryAgent.ts +269 -0
  576. package/src/agents/ReactiveAgent.ts +6 -216
  577. package/src/agents/RouterAgent.ts +2 -337
  578. package/src/agents/SupervisorAgent.ts +3 -476
  579. package/src/agents/defineAgent.ts +13 -2
  580. package/src/agents/examples/PipelineAgent.ts +302 -0
  581. package/src/agents/examples/RouterAgent.ts +338 -0
  582. package/src/agents/examples/SupervisorAgent.ts +472 -0
  583. package/src/agents/explore.ts +6 -3
  584. package/src/agents/forward-options.ts +37 -0
  585. package/src/agents/index.ts +2 -0
  586. package/src/agents/runAgent.ts +144 -37
  587. package/src/authorization/command-line.ts +2 -2
  588. package/src/authorization/gate.ts +21 -1
  589. package/src/authorization/program.ts +773 -0
  590. package/src/authorization/reexec-wrapper.ts +589 -0
  591. package/src/authorization/rules.ts +18 -1
  592. package/src/authorization/shell-lexer.ts +104 -59
  593. package/src/bridge/sse/mapper.ts +6 -0
  594. package/src/capabilities/index.ts +157 -0
  595. package/src/config/registry.ts +4 -1
  596. package/src/connector/index.ts +9 -2
  597. package/src/connector/mcp/adapter.ts +58 -3
  598. package/src/connector/mcp/client.ts +20 -4
  599. package/src/connector/mcp/discovery.ts +60 -0
  600. package/src/connector/mcp/index.ts +13 -0
  601. package/src/connector/mcp/mcp-toolset.ts +515 -0
  602. package/src/connector/tools/index.ts +2 -2
  603. package/src/connector/tools/router.ts +35 -58
  604. package/src/directory/derive-supervisor.ts +12 -8
  605. package/src/directory/derive.ts +8 -5
  606. package/src/execution/code-runtime/types.ts +2 -2
  607. package/src/invariants/index.ts +4 -1
  608. package/src/manager/agent/lifecycle.ts +9 -0
  609. package/src/manager/connector/environment.ts +17 -4
  610. package/src/manager/connector/index.ts +2 -2
  611. package/src/manager/connector/tenant.ts +17 -4
  612. package/src/manager/index.ts +2 -2
  613. package/src/manager/session/attribution.ts +79 -0
  614. package/src/manager/session/turn-recorder.ts +211 -55
  615. package/src/peers/address.ts +58 -0
  616. package/src/peers/client.ts +202 -0
  617. package/src/peers/dir.ts +176 -0
  618. package/src/peers/endpoint.ts +564 -0
  619. package/src/peers/envelope.ts +121 -0
  620. package/src/peers/index.ts +96 -0
  621. package/src/peers/protocol.ts +196 -0
  622. package/src/peers/record.ts +56 -0
  623. package/src/peers/registry.ts +241 -0
  624. package/src/plugin/define.ts +68 -0
  625. package/src/plugin/index.ts +2 -0
  626. package/src/plugin/lifecycle.ts +286 -106
  627. package/src/plugin/resolver.ts +6 -3
  628. package/src/plugin/shell-hook.ts +21 -5
  629. package/src/probe/errors.ts +4 -1
  630. package/src/prompt/coding-agent-doctrine.ts +22 -10
  631. package/src/prompt/contributions.ts +12 -2
  632. package/src/prompt/index.ts +1 -0
  633. package/src/provider/collect-chat-completion.ts +96 -11
  634. package/src/provider/mock.ts +24 -6
  635. package/src/provider/registry.ts +4 -1
  636. package/src/provider/tool-call-framing.ts +226 -0
  637. package/src/public-runtime.ts +136 -7
  638. package/src/public-tools.ts +24 -4
  639. package/src/public-types.ts +60 -6
  640. package/src/read-model/registry.ts +7 -2
  641. package/src/registry/ManagedRegistry.ts +57 -6
  642. package/src/registry/collision.ts +64 -0
  643. package/src/registry/command/index.ts +7 -3
  644. package/src/registry/index.ts +3 -9
  645. package/src/registry/plugin/index.ts +14 -1
  646. package/src/registry/tool/callable.ts +8 -9
  647. package/src/registry/tool/execute.ts +16 -846
  648. package/src/registry/tool/portable.ts +59 -0
  649. package/src/registry/tool/presentation.ts +6 -5
  650. package/src/runtime/bidi/session.ts +24 -5
  651. package/src/runtime/query/executor/tool-call-admission.ts +228 -6
  652. package/src/runtime/query/executor.ts +110 -12
  653. package/src/runtime/query/file-evidence-replay.ts +8 -7
  654. package/src/runtime/query/fork/prepare.ts +6 -1
  655. package/src/runtime/query/guardrail-presets.ts +4 -3
  656. package/src/runtime/query/index.ts +199 -101
  657. package/src/runtime/query/iteration/index.ts +13 -3
  658. package/src/runtime/query/iteration/phases/compaction.ts +1 -1
  659. package/src/runtime/query/iteration/phases/context.ts +2 -2
  660. package/src/runtime/query/iteration/phases/tool-review.ts +59 -2
  661. package/src/runtime/query/iteration/stream-turn.ts +390 -136
  662. package/src/runtime/query/iteration/tool-input.ts +284 -0
  663. package/src/runtime/query/observation-context.ts +2 -2
  664. package/src/runtime/query/prelude-lease.ts +132 -0
  665. package/src/runtime/query/prepare-turn.ts +477 -198
  666. package/src/runtime/query/prompt-cache.ts +2 -2
  667. package/src/runtime/query/prompt.ts +4 -4
  668. package/src/runtime/query/resume-session.ts +42 -3
  669. package/src/runtime/query/review-policy.ts +87 -9
  670. package/src/runtime/query/tooling.ts +3 -6
  671. package/src/runtime/query/turn-state.ts +4 -0
  672. package/src/runtime/system-events.ts +246 -0
  673. package/src/scheduler/delegating.ts +7 -2
  674. package/src/skills/index.ts +1 -1
  675. package/src/skills/registry.ts +25 -0
  676. package/src/store/session-log/conformance.ts +51 -8
  677. package/src/store/session-log/core.ts +10 -4
  678. package/src/store/session-log/fold.ts +33 -7
  679. package/src/store/session-log/index.ts +2 -0
  680. package/src/test-support/toolset.ts +23 -0
  681. package/src/tools/builtins/bash.ts +27 -10
  682. package/src/tools/builtins/browser-url.ts +1 -1
  683. package/src/tools/builtins/computer-use.ts +21 -11
  684. package/src/tools/builtins/edit.ts +12 -2
  685. package/src/tools/builtins/json-string-hint.ts +16 -0
  686. package/src/tools/builtins/search-tools.ts +7 -20
  687. package/src/tools/builtins/write-file.ts +12 -0
  688. package/src/tools/command-shell.ts +46 -1
  689. package/src/tools/coordinator/agent.ts +6 -0
  690. package/src/tools/coordinator/ask-user-question.ts +29 -15
  691. package/src/tools/coordinator/index.ts +6 -0
  692. package/src/tools/coordinator/question-options.ts +195 -0
  693. package/src/tools/defineTool.ts +28 -0
  694. package/src/tools/memory/search.ts +4 -1
  695. package/src/tools/memory/update.ts +8 -5
  696. package/src/tools/render-nonce.ts +69 -0
  697. package/src/tools/roster.ts +84 -33
  698. package/src/tools/schedules/schedule-tool.ts +107 -14
  699. package/src/tools/schedules/types.ts +52 -6
  700. package/src/tools/trusted-read-only.ts +19 -10
  701. package/src/tools/untrusted-envelope.ts +121 -57
  702. package/src/toolsets/combine.ts +181 -0
  703. package/src/toolsets/manager.ts +996 -0
  704. package/src/toolsets/source-glob.ts +22 -0
  705. package/src/toolsets/toolset.ts +31 -0
  706. package/src/toolsets/types.ts +154 -0
  707. package/src/toolsets/wrappers.ts +191 -0
  708. package/src/types/agent/base.ts +29 -16
  709. package/src/types/agent/index.ts +1 -0
  710. package/src/types/agent/pipeline.ts +2 -0
  711. package/src/types/agent/query.ts +129 -0
  712. package/src/types/agent/reactive.ts +5 -142
  713. package/src/types/agent/router.ts +2 -0
  714. package/src/types/agent/supervisor.ts +5 -2
  715. package/src/types/authorization/index.ts +12 -0
  716. package/src/types/connector/mcp.ts +32 -0
  717. package/src/types/errors/index.ts +15 -0
  718. package/src/types/hitl/index.ts +57 -0
  719. package/src/types/message/index.ts +135 -7
  720. package/src/types/plugin/index.ts +44 -21
  721. package/src/types/provider/chat.ts +5 -0
  722. package/src/types/provider/config.ts +8 -3
  723. package/src/types/provider/stream.ts +39 -0
  724. package/src/types/sandbox/index.ts +1 -1
  725. package/src/types/session/events.ts +14 -3
  726. package/src/types/tool/index.ts +154 -105
  727. package/src/types/toolset/index.ts +10 -47
  728. package/dist/registry/toolset/catalog.d.ts +0 -42
  729. package/dist/registry/toolset/catalog.d.ts.map +0 -1
  730. package/dist/registry/toolset/catalog.js +0 -234
  731. package/dist/registry/toolset/catalog.js.map +0 -1
  732. package/src/registry/toolset/catalog.ts +0 -308
@@ -0,0 +1,773 @@
1
+ /**
2
+ * Where a command line actually execs a program.
3
+ *
4
+ * Three consumers each answered "what program does this command run" their
5
+ * own way — the live `bash` tool's escalation, the scheduled-run floor's
6
+ * `pkill`/`systemctl`/… detection, and a scheduled script's static check —
7
+ * and each guessed differently about a re-exec wrapper (`sudo`, `env`,
8
+ * `nice`, `timeout`, …) standing in front of the real program. A security
9
+ * review found that every one of them only ever looked at
10
+ * `command.words[command.assignments]`, so `env $(echo git) push`,
11
+ * `timeout 5 $(echo systemctl) stop …` and the rest ran unverified: the
12
+ * wrapper's own name is what each check saw, never the word it was actually
13
+ * about to run.
14
+ *
15
+ * `programPositions` is the one place that answer now lives. It unwraps a
16
+ * chain of re-exec wrappers — each with its OWN real option grammar, so
17
+ * `stdbuf -oL prog` and `timeout 5 prog` are read correctly rather than
18
+ * assumed to put the program right after the wrapper's name — and reports
19
+ * every position in the command where a program is actually named: the
20
+ * (possibly wrapped) head, and, for `find`, each `-exec`/`-execdir`/`-ok`/
21
+ * `-okdir` clause's own head.
22
+ *
23
+ * A position is `known` (the literal, non-expanding word that names the
24
+ * program) or `unknown` (why it could not be resolved: the word itself
25
+ * expands, an option this does not recognise stood in the way, or an
26
+ * `xargs` program that is a shell or a `{}` placeholder). Fail closed: an
27
+ * option this does not model makes the position unknown, never "assumed to
28
+ * be the program" and never "assumed harmless".
29
+ *
30
+ * `source`/`.` and `eval` are read the same way a nested `bash -c` payload
31
+ * already is, not as an automatic unknown: `source path`/`. path` with a
32
+ * LITERAL path is known — the position is the `source`/`.` word itself,
33
+ * exactly as `bash path` is known without anyone reading what `path`
34
+ * contains — and unknown only when the path itself expands (`source "$X"`).
35
+ * `eval`'s literal argument words are joined and lexed as a command line of
36
+ * their own, and every position in the commands that come out of that is
37
+ * folded into the result — unknown when that reading is opaque, when any
38
+ * argument word expands, or, transitively, when a position inside the
39
+ * lexed payload is itself unknown (`eval 'env $(echo git) push'`).
40
+ * Consistency review (2026-09-24): treating `eval`/`source`/`.` as
41
+ * automatically unknown "wherever they stand, even literal" made
42
+ * `source .venv/bin/activate` ask alongside `source "$X"`, which is exactly
43
+ * the asymmetry `bash script.sh` (known) versus `bash -c "$X"` (unknown)
44
+ * does not have — the same module read a literal argument two different
45
+ * ways depending only on which command carried it. An `unknown` position
46
+ * still carries `word` when a single word is the reason (the word itself
47
+ * expands, so its value is not known before the line runs) — a caller that
48
+ * only needs "is there a word here that could name any of several tools"
49
+ * (the scheduled-run floor's `pkill`/`systemctl`/… detection) reads that
50
+ * word whether the position is known or not; `unknown` with no `word` is a
51
+ * structural failure with no single culprit (too many wrappers, an
52
+ * unrecognised option, an opaque `eval` payload, a poisoned environment).
53
+ *
54
+ * `hasPoisoningPrefix`, `poisonsLaterCommands` and `poisonedProgramPosition`
55
+ * are the other half a caller iterating a whole script needs: a command
56
+ * that sets `PATH`, `LD_PRELOAD`, `LD_LIBRARY_PATH`, `BASH_ENV`, `ENV` or
57
+ * `IFS` — as a leading assignment (which affects that same command's own
58
+ * program position) or as an `export`/bare assignment (which affects every
59
+ * command after it in the same script) — makes a LATER literal program name
60
+ * unverifiable too: which binary a literal `ls` resolves to depends on
61
+ * `PATH` at the moment it runs, not on the text of the word `ls`.
62
+ * `resolveScriptPrograms` threads that state through a whole script's
63
+ * commands in one pass, for a caller that does not want to write the loop
64
+ * itself.
65
+ */
66
+
67
+ import { RUNTIME_EVALUATORS } from './command-line.js'
68
+ import { skipOneReexecWrapper } from './reexec-wrapper.js'
69
+ import {
70
+ NESTED_SHELLS,
71
+ type ShellCommand,
72
+ type ShellDialect,
73
+ type ShellWord,
74
+ basename,
75
+ lexShellCommandLine,
76
+ nestedShellCommand,
77
+ } from './shell-lexer.js'
78
+
79
+ /**
80
+ * Where a program is named, or why it could not be resolved. `word` is set
81
+ * whenever the position resolves to a specific word — including an unknown
82
+ * one whose OWN text is what is unresolvable (it expands, so its value is
83
+ * not known before the line runs); a caller that only cares "is there a
84
+ * word here that might be any program name" (the scheduled-run floor's
85
+ * scheduler-tool detection) reads `word` regardless of `unknown`. `unknown`
86
+ * with no `word` is a structural failure with no single culprit word — an
87
+ * option on a wrapper this does not recognise, too many wrappers, an opaque
88
+ * `eval` payload, or a poisoned resolution environment.
89
+ */
90
+ export type ProgramPosition =
91
+ | { readonly word: ShellWord; readonly unknown?: undefined }
92
+ | { readonly word?: ShellWord; readonly unknown: string }
93
+
94
+ /** How many re-exec wrappers a chain unwraps before giving up (`sudo env nice … prog`). */
95
+ const MAX_WRAPPER_DEPTH = 8
96
+
97
+ const LITERAL = (w: ShellWord): boolean => !w.expands
98
+
99
+ /** `NAME=value` read off an assignment word's own text, or null. */
100
+ function assignmentName(word: ShellWord): string | null {
101
+ const text = word.text.replace(/\\\n/g, '')
102
+ const match = /^([A-Za-z_][A-Za-z0-9_]*)\+?=/.exec(text)
103
+ return match && word.value.startsWith(match[0]) ? (match[1] as string) : null
104
+ }
105
+
106
+ type Consumed =
107
+ | { readonly rest: readonly ShellWord[] }
108
+ | { readonly unknown: string }
109
+ | { readonly none: true }
110
+
111
+ /** A `-c`/`--command` argument found among a wrapper's options — never produced by {@link skipOptions}, only by {@link findDashC}. */
112
+ type WithDashC = { readonly payload: ShellWord }
113
+
114
+ /**
115
+ * `wrapper NAME=value…` sets `NAME` for the wrapped command only (`env
116
+ * NAME=value prog`, `sudo NAME=value prog`) — the same taint a leading
117
+ * command-prefix assignment gives its own command, just spelled through the
118
+ * wrapper instead of in front of it.
119
+ */
120
+ function poisonedByWrapperAssignment(wrapper: string, name: string): string {
121
+ return `${wrapper} ${name}=… sets ${name} for the command that follows, so a literal name after it cannot be trusted to resolve to what its text says`
122
+ }
123
+
124
+ /**
125
+ * Scan `words` for a bare `-c`/`--command COMMAND` pair among options this
126
+ * wrapper recognises (everything else validated against `flags`/`withArg`,
127
+ * the same as {@link skipOptions}). `--command=X` is not modelled — a
128
+ * documented, deliberately conservative gap: it falls through to the
129
+ * unrecognised-option path, `unknown` never a silent pass. Returns the `-c`
130
+ * payload when found, the words left after options when it is not (a valid
131
+ * outcome some callers use — `flock`'s positional form, `script`'s bare
132
+ * form), or `unknown` on a bad option or a `-c` given no argument.
133
+ */
134
+ function findDashC(
135
+ words: readonly ShellWord[],
136
+ flags: ReadonlySet<string>,
137
+ withArg: ReadonlySet<string>,
138
+ optionalAttached: readonly string[] = [],
139
+ ): WithDashC | { readonly rest: readonly ShellWord[] } | { readonly unknown: string } {
140
+ let i = 0
141
+ while (i < words.length) {
142
+ const w = words[i] as ShellWord
143
+ if (!LITERAL(w)) return { rest: words.slice(i) }
144
+ if (w.value === '-c' || w.value === '--command') {
145
+ const payload = words[i + 1]
146
+ if (payload === undefined) return { unknown: `${w.value} was given no command` }
147
+ return { payload }
148
+ }
149
+ if (w.value === '--') return { rest: words.slice(i + 1) }
150
+ if (flags.has(w.value)) {
151
+ i += 1
152
+ continue
153
+ }
154
+ if (
155
+ optionalAttached.some((option) =>
156
+ option.startsWith('--')
157
+ ? w.value.startsWith(`${option}=`)
158
+ : w.value.startsWith(option) && w.value.length > option.length,
159
+ )
160
+ ) {
161
+ i += 1
162
+ continue
163
+ }
164
+ if (withArg.has(w.value)) {
165
+ i += 2
166
+ continue
167
+ }
168
+ const eq = w.value.indexOf('=')
169
+ if (w.value.startsWith('--') && eq > 0 && withArg.has(w.value.slice(0, eq))) {
170
+ i += 1
171
+ continue
172
+ }
173
+ if (/^--?[A-Za-z]/.test(w.value)) {
174
+ return { unknown: `an option this does not read: ${w.value}` }
175
+ }
176
+ return { rest: words.slice(i) }
177
+ }
178
+ return { rest: words.slice(i) }
179
+ }
180
+
181
+ /**
182
+ * Skip a wrapper's own recognised, argument-taking or bare options over
183
+ * `words`, stopping at the first word that is not one of them (the
184
+ * boundary between the wrapper's own invocation and whatever runs next).
185
+ * An option-shaped word (`-x`, `--long`) this does not recognise is
186
+ * `unknown`, never treated as the program; a word this cannot classify
187
+ * because it expands is left for the caller, which already knows to call
188
+ * an expanding program word unknown. `explicit`, when given, names options
189
+ * that ARE recognised but never lead to a named program at all (`sudo -i`
190
+ * runs a runtime-chosen login shell) — checked before `flags`/`withArg`, so
191
+ * the reason is explicit rather than an accident of `flags` swallowing them.
192
+ */
193
+ function skipOptions(
194
+ words: readonly ShellWord[],
195
+ flags: ReadonlySet<string>,
196
+ withArg: ReadonlySet<string>,
197
+ explicit?: ReadonlyMap<string, string>,
198
+ ): Consumed {
199
+ let i = 0
200
+ while (i < words.length) {
201
+ const w = words[i] as ShellWord
202
+ if (!LITERAL(w)) return { rest: words.slice(i) }
203
+ if (w.value === '--') return { rest: words.slice(i + 1) }
204
+ const reason = explicit?.get(w.value)
205
+ if (reason !== undefined) return { unknown: reason }
206
+ if (flags.has(w.value)) {
207
+ i += 1
208
+ continue
209
+ }
210
+ if (withArg.has(w.value)) {
211
+ i += 2
212
+ continue
213
+ }
214
+ const eq = w.value.indexOf('=')
215
+ if (w.value.startsWith('--') && eq > 0 && withArg.has(w.value.slice(0, eq))) {
216
+ i += 1
217
+ continue
218
+ }
219
+ // A short option's value attached to its flag (`-c2`, `-n10`, `-oL`),
220
+ // the conventional form for `ionice`, `nice`, `stdbuf` and `taskset`'s
221
+ // `-c` list — one token, no separate argument word.
222
+ if (!w.value.startsWith('--') && w.value.length > 2 && withArg.has(w.value.slice(0, 2))) {
223
+ i += 1
224
+ continue
225
+ }
226
+ if (/^--?[A-Za-z]/.test(w.value)) {
227
+ return { unknown: `an option this does not read: ${w.value}` }
228
+ }
229
+ return { rest: words.slice(i) }
230
+ }
231
+ return { none: true }
232
+ }
233
+
234
+ /** `xargs`' own options, then its program (the first non-option word, `echo` when there is none). */
235
+ function unwrapXargs(words: readonly ShellWord[]): Consumed {
236
+ const flags = new Set(['-0', '-o', '-p', '-r', '-t', '-x', '--null', '--open-tty', '--verbose'])
237
+ const withArg = new Set(['-a', '-d', '-E', '-e', '-I', '-i', '-L', '-l', '-n', '-P', '-s'])
238
+ const skipped = skipOptions(words, flags, withArg)
239
+ if ('unknown' in skipped) return skipped
240
+ const rest = 'none' in skipped ? [] : skipped.rest
241
+ const program = rest[0]
242
+ if (program === undefined) {
243
+ // No program named: xargs' own default is `echo`, always safe.
244
+ return {
245
+ rest: [{ text: 'echo', value: 'echo', expands: false, quoted: false, substitutes: false }],
246
+ }
247
+ }
248
+ if (!LITERAL(program)) return { rest }
249
+ if (program.value === '{}' || NESTED_SHELLS.has(basename(program.value))) {
250
+ return {
251
+ unknown: `xargs' program is built from its input, not named in the line: ${program.text}`,
252
+ }
253
+ }
254
+ return { rest }
255
+ }
256
+
257
+ /**
258
+ * Special command runners. Plain re-exec wrappers are all read by the
259
+ * shared skipOneReexecWrapper, also used by the shell lexer to find -c.
260
+ */
261
+ function unwrapOnce(words: readonly ShellWord[]): Consumed | WithDashC | null {
262
+ const head = words[0] as ShellWord
263
+ const name = basename(head.value)
264
+ const rest = words.slice(1)
265
+ switch (name) {
266
+ case 'xargs':
267
+ return unwrapXargs(rest)
268
+ case 'su':
269
+ case 'runuser': {
270
+ const found = findDashC(
271
+ rest,
272
+ new Set(['-p', '--preserve-environment', '-P', '--pty', '-l', '--login', '-']),
273
+ new Set([
274
+ '-s',
275
+ '--shell',
276
+ '-g',
277
+ '--group',
278
+ '-G',
279
+ '--supp-group',
280
+ '-w',
281
+ '--whitelist-environment',
282
+ ]),
283
+ )
284
+ if ('unknown' in found) return found
285
+ if ('payload' in found) return found
286
+ // No `-c`/`--command` at all: both start the target user's login
287
+ // or default shell — an interactive session, not a fixed next
288
+ // command this can name.
289
+ return {
290
+ unknown: `${name} without -c/--command starts an interactive login shell, not a named program`,
291
+ }
292
+ }
293
+ case 'script': {
294
+ // Per util-linux `script --help`: `-t[<file>], --timing[=<file>]`
295
+ // is a DEPRECATED ALIAS for `-T`/`--log-timing` with an OPTIONAL
296
+ // value (default file is stderr when bare) — it never consumes a
297
+ // separate following word, unlike the real `-T <file>`. Modelling
298
+ // `-t` as taking one used to swallow a following `-c` as `-t`'s own
299
+ // value, reading `script` as known and never looking at `-c`'s
300
+ // argument at all.
301
+ const flags = new Set([
302
+ '-a',
303
+ '--append',
304
+ '-e',
305
+ '--return',
306
+ '-f',
307
+ '--flush',
308
+ '--force',
309
+ '-q',
310
+ '--quiet',
311
+ '-t',
312
+ '--timing',
313
+ ])
314
+ const withArg = new Set([
315
+ '-I',
316
+ '--log-in',
317
+ '-O',
318
+ '--log-out',
319
+ '-B',
320
+ '--log-io',
321
+ '-T',
322
+ '--log-timing',
323
+ '-m',
324
+ '--logging-format',
325
+ '-E',
326
+ '--echo',
327
+ '-o',
328
+ '--output-limit',
329
+ ])
330
+ // `script [options] [<file>] [-- <command> [<argument>...]]`: `--`
331
+ // is an alternative to `-c`, running the command directly as an
332
+ // argv (not a joined string re-lexed through a shell) rather than
333
+ // recording an interactive session.
334
+ const dashDash = rest.findIndex((w) => LITERAL(w) && w.value === '--')
335
+ if (dashDash >= 0) {
336
+ const before = skipOptions(rest.slice(0, dashDash), flags, withArg)
337
+ if ('unknown' in before) return before
338
+ const after = rest.slice(dashDash + 1)
339
+ return after.length === 0 ? { none: true } : { rest: after }
340
+ }
341
+ const found = findDashC(rest, flags, withArg, ['-t', '--timing'])
342
+ if ('unknown' in found) return found
343
+ if ('payload' in found) return found
344
+ // No `-c` and no `--`: `script` itself is the program — it
345
+ // records a terminal session (an interactive shell as its own
346
+ // child), and its only other argument is a log file name, not a
347
+ // command to name.
348
+ return { none: true }
349
+ }
350
+ case 'flock': {
351
+ const found = findDashC(
352
+ rest,
353
+ new Set([
354
+ '-s',
355
+ '--shared',
356
+ '-x',
357
+ '-e',
358
+ '--exclusive',
359
+ '-n',
360
+ '--nb',
361
+ '--nonblock',
362
+ '-o',
363
+ '--close',
364
+ '-F',
365
+ '--no-fork',
366
+ '--verbose',
367
+ ]),
368
+ new Set(['-w', '--timeout', '-E', '--conflict-exit-code']),
369
+ )
370
+ if ('unknown' in found) return found
371
+ if ('payload' in found) return found
372
+ // Positional form (`flock file|directory command [args…]`): what
373
+ // is left after options starts with the locked file/directory
374
+ // (or a bare fd, with no command — nothing execs), then the real
375
+ // argv, run directly — no shell, no joining.
376
+ const target = found.rest[0]
377
+ if (target === undefined) return { none: true }
378
+ const remaining = found.rest.slice(1)
379
+ return remaining.length === 0 ? { none: true } : { rest: remaining }
380
+ }
381
+ default:
382
+ return null
383
+ }
384
+ }
385
+
386
+ /**
387
+ * `source path [args…]` / `. path [args…]`: known at `head` (the
388
+ * `source`/`.` word itself) when `path` is a literal word, exactly as
389
+ * `bash path` is known without reading what `path` contains — trailing
390
+ * words are the sourced script's own positional parameters, not inspected
391
+ * either, the same as `bash path`'s do not name a second program. Unknown,
392
+ * naming `path` itself, when it expands or is absent.
393
+ */
394
+ function resolveSourceOrDot(head: ShellWord, args: readonly ShellWord[]): ProgramPosition {
395
+ const path = args[0]
396
+ if (path === undefined) {
397
+ return { word: head, unknown: `${basename(head.value)} was given nothing to run` }
398
+ }
399
+ if (!LITERAL(path)) {
400
+ return { word: path, unknown: `the file this runs is decided at runtime: ${path.text}` }
401
+ }
402
+ return { word: head }
403
+ }
404
+
405
+ /**
406
+ * `eval word…`: bash joins `eval`'s arguments with a space and reads the
407
+ * result as a command line. When every argument word is literal, that joined
408
+ * text is lexed the same way a `bash -c` payload already is, and every
409
+ * position in the commands it contains (poisoning threaded across them,
410
+ * same as any script) is folded into the result — unknown when the reading
411
+ * is opaque or incomplete, and transitively unknown when a position inside
412
+ * it is. An expanding argument word is unknown outright: its value, and so
413
+ * what `eval` will even read, is not known before the line runs. `eval`
414
+ * with no arguments does nothing in bash and names no position at all.
415
+ */
416
+ function resolveEvalPayload(
417
+ args: readonly ShellWord[],
418
+ dialect: ShellDialect,
419
+ ): readonly ProgramPosition[] {
420
+ if (args.length === 0) return []
421
+ for (const arg of args) {
422
+ if (!LITERAL(arg)) {
423
+ return [{ word: arg, unknown: `eval's payload is decided at runtime: ${arg.text}` }]
424
+ }
425
+ }
426
+ const payload = args.map((arg) => arg.value).join(' ')
427
+ const reading = lexShellCommandLine(payload, { dialect })
428
+ if (reading.opaque || !reading.complete) {
429
+ return [
430
+ {
431
+ unknown: `eval's payload cannot be read (${reading.reasons[0] ?? 'a construct the lexer does not model'}): ${payload}`,
432
+ },
433
+ ]
434
+ }
435
+ return resolveScriptPrograms(reading.commands, dialect).flatMap((entry) => entry.positions)
436
+ }
437
+
438
+ /** The final position a chain of re-exec wrappers resolves to, from `words` (already literal, non-empty). */
439
+ function resolveChain(
440
+ words: readonly ShellWord[],
441
+ dialect: ShellDialect,
442
+ ): readonly ProgramPosition[] {
443
+ let current = words
444
+ for (let depth = 0; depth < MAX_WRAPPER_DEPTH; depth += 1) {
445
+ const head = current[0]
446
+ if (head === undefined) return [{ unknown: 'no program follows this wrapper' }]
447
+ if (!LITERAL(head)) {
448
+ return [{ word: head, unknown: `the program is decided at runtime: ${head.text}` }]
449
+ }
450
+ const name = basename(head.value)
451
+ if (name === 'eval') return resolveEvalPayload(current.slice(1), dialect)
452
+ if (name === 'watch') return resolveWatchArgs(current.slice(1), dialect)
453
+ if (RUNTIME_EVALUATORS.has(name)) {
454
+ // `source`/`.`: `eval` is handled above, on its own, since a
455
+ // literal `eval` reads its payload rather than being the position
456
+ // itself.
457
+ return [resolveSourceOrDot(head, current.slice(1))]
458
+ }
459
+ const wrapper = skipOneReexecWrapper(current)
460
+ if (wrapper !== null) {
461
+ if ('unknown' in wrapper) return [{ unknown: wrapper.unknown }]
462
+ if ('none' in wrapper) return [{ word: head }]
463
+ for (const assignment of wrapper.assignments) {
464
+ if (DYNAMIC_RESOLUTION_VARIABLES.has(assignment)) {
465
+ return [{ unknown: poisonedByWrapperAssignment(name, assignment) }]
466
+ }
467
+ }
468
+ if (wrapper.rest.length === 0) return [{ word: head }]
469
+ current = wrapper.rest
470
+ continue
471
+ }
472
+ const unwrapped = unwrapOnce(current)
473
+ if (unwrapped === null) return [{ word: head }]
474
+ if ('unknown' in unwrapped) return [{ unknown: unwrapped.unknown }]
475
+ if ('none' in unwrapped) return [{ word: head }]
476
+ if ('payload' in unwrapped) return resolveDashCPayload(unwrapped.payload, dialect)
477
+ if (unwrapped.rest.length === 0) return [{ word: head }]
478
+ current = unwrapped.rest
479
+ }
480
+ return [{ unknown: 'too many re-exec wrappers to follow' }]
481
+ }
482
+
483
+ /**
484
+ * A `-c COMMAND` payload (`su -c`, `runuser -c`, `script -c`, `flock -c`):
485
+ * known — recursed into, the same as a nested `bash -c '<literal>'` payload
486
+ * — when literal, unknown when it expands.
487
+ */
488
+ function resolveDashCPayload(
489
+ payload: ShellWord,
490
+ dialect: ShellDialect,
491
+ ): readonly ProgramPosition[] {
492
+ if (!LITERAL(payload)) {
493
+ return [
494
+ { word: payload, unknown: `the command this runs is decided at runtime: ${payload.text}` },
495
+ ]
496
+ }
497
+ const reading = lexShellCommandLine(payload.value, { dialect })
498
+ if (reading.opaque || !reading.complete) {
499
+ return [
500
+ {
501
+ unknown: `this -c payload cannot be read (${reading.reasons[0] ?? 'a construct the lexer does not model'}): ${payload.value}`,
502
+ },
503
+ ]
504
+ }
505
+ return resolveScriptPrograms(reading.commands, dialect).flatMap((entry) => entry.positions)
506
+ }
507
+
508
+ /**
509
+ * `watch [options] command…` joins its trailing words with a space and runs
510
+ * them via a shell, exactly the way `eval` does — except with `-x`/`--exec`,
511
+ * which execs the argv directly, no shell, no joining, read the ordinary way.
512
+ */
513
+ function resolveWatchArgs(
514
+ rest: readonly ShellWord[],
515
+ dialect: ShellDialect,
516
+ ): readonly ProgramPosition[] {
517
+ const usesExec = rest.some((w) => LITERAL(w) && (w.value === '-x' || w.value === '--exec'))
518
+ const skipped = skipOptions(
519
+ rest,
520
+ new Set([
521
+ '-d',
522
+ '--differences',
523
+ '-p',
524
+ '--precise',
525
+ '-t',
526
+ '--no-title',
527
+ '-b',
528
+ '--beep',
529
+ '-e',
530
+ '--errexit',
531
+ '-g',
532
+ '--chgexit',
533
+ '-c',
534
+ '--color',
535
+ '-x',
536
+ '--exec',
537
+ ]),
538
+ new Set(['-n', '--interval']),
539
+ )
540
+ if ('unknown' in skipped) return [{ unknown: skipped.unknown }]
541
+ const afterOptions = 'rest' in skipped ? skipped.rest : []
542
+ if (afterOptions.length === 0) return []
543
+ return usesExec ? resolveChain(afterOptions, dialect) : resolveEvalPayload(afterOptions, dialect)
544
+ }
545
+
546
+ /** `find`'s own `-exec`/`-execdir`/`-ok`/`-okdir` clauses, each an independent program position. */
547
+ function findExecPositions(words: readonly ShellWord[], dialect: ShellDialect): ProgramPosition[] {
548
+ const positions: ProgramPosition[] = []
549
+ for (let i = 0; i < words.length; i += 1) {
550
+ const w = words[i] as ShellWord
551
+ if (!LITERAL(w) || !/^-(?:exec|execdir|ok|okdir)$/.test(w.value)) continue
552
+ const clauseStart = i + 1
553
+ let k = clauseStart
554
+ while (k < words.length) {
555
+ const term = words[k] as ShellWord
556
+ if (LITERAL(term) && (term.value === ';' || term.value === '+')) break
557
+ k += 1
558
+ }
559
+ const clause = words.slice(clauseStart, k)
560
+ if (clause.length === 0) {
561
+ positions.push({ unknown: `find's ${w.value} clause has no program` })
562
+ } else {
563
+ // find execs the entire argv after -exec, including any re-exec
564
+ // wrappers. Inspecting only its first word would mistake `env` for
565
+ // the program and miss a dynamic name further into the clause.
566
+ for (const position of resolveChain(clause, dialect)) {
567
+ if (position.word && LITERAL(position.word) && position.word.value.includes('{}')) {
568
+ positions.push({
569
+ unknown: `find's own \`{}\` placeholder stands for the program, not a name in the line`,
570
+ })
571
+ } else {
572
+ positions.push(position)
573
+ }
574
+ }
575
+ // The outer lexer only sees `find`, so it cannot read a shell -c
576
+ // payload inside this clause. Read it here with the same wrapper
577
+ // and shell-option grammar the lexer uses for a top-level shell.
578
+ const nested = nestedShellCommand(clause)
579
+ if (nested !== null) {
580
+ if ('opaque' in nested) {
581
+ positions.push({ unknown: nested.opaque })
582
+ } else if (nested.shell === 'zsh' || nested.shell === 'ksh' || nested.shell === 'mksh') {
583
+ positions.push({ unknown: `nested ${nested.shell} is not modeled` })
584
+ } else if (LITERAL(nested.payload) && nested.payload.value.includes('{}')) {
585
+ positions.push({
586
+ unknown: 'find substitutes `{}` into this shell payload before it runs',
587
+ })
588
+ } else {
589
+ positions.push(
590
+ ...resolveDashCPayload(nested.payload, nested.shell === 'bash' ? 'bash' : 'sh'),
591
+ )
592
+ }
593
+ }
594
+ }
595
+ i = k
596
+ }
597
+ return positions
598
+ }
599
+
600
+ /**
601
+ * Every position in `command` where a program is actually exec'd: the
602
+ * (possibly re-exec-wrapped) head, and, for `find`, each `-exec`-family
603
+ * clause's own head. Empty for a command with no words at all. `dialect` is
604
+ * the shell that will run the line — needed only to read a literal `eval`
605
+ * payload the same way its own commands would be.
606
+ */
607
+ export function programPositions(
608
+ command: ShellCommand,
609
+ dialect: ShellDialect,
610
+ ): readonly ProgramPosition[] {
611
+ const head = command.words[command.assignments]
612
+ if (head === undefined) return []
613
+ const positions: ProgramPosition[] = [
614
+ ...resolveChain(command.words.slice(command.assignments), dialect),
615
+ ]
616
+ for (const position of positions.slice()) {
617
+ if (!position.word || !LITERAL(position.word) || basename(position.word.value) !== 'find')
618
+ continue
619
+ const index = command.words.indexOf(position.word)
620
+ if (index >= 0) positions.push(...findExecPositions(command.words.slice(index + 1), dialect))
621
+ }
622
+ return positions
623
+ }
624
+
625
+ /** Variables whose value governs how a LATER literal program name resolves to a binary or to code. */
626
+ export const DYNAMIC_RESOLUTION_VARIABLES: ReadonlySet<string> = new Set([
627
+ 'PATH',
628
+ 'LD_PRELOAD',
629
+ 'LD_LIBRARY_PATH',
630
+ 'BASH_ENV',
631
+ 'ENV',
632
+ 'IFS',
633
+ ])
634
+
635
+ function assignsPoisoningVariable(words: readonly ShellWord[]): boolean {
636
+ for (const word of words) {
637
+ const name = assignmentName(word)
638
+ if (name !== null && DYNAMIC_RESOLUTION_VARIABLES.has(name)) return true
639
+ }
640
+ return false
641
+ }
642
+
643
+ /**
644
+ * Whether `command`'s OWN leading assignment (`PATH=x prog`) sets one of
645
+ * {@link DYNAMIC_RESOLUTION_VARIABLES} — which affects only `prog`'s own
646
+ * position, the way a real command-prefix assignment is scoped to that one
647
+ * invocation and does not leak to later commands. A caller iterating a
648
+ * whole script applies this to THIS command's own {@link programPositions}.
649
+ */
650
+ export function hasPoisoningPrefix(command: ShellCommand): boolean {
651
+ return assignsPoisoningVariable(command.words.slice(0, command.assignments))
652
+ }
653
+
654
+ /**
655
+ * Whether `command` sets one of {@link DYNAMIC_RESOLUTION_VARIABLES} for
656
+ * every command AFTER it in the same script: an `export NAME=value` (or a
657
+ * bare re-export, `export NAME`, of an already-set one), or a standalone
658
+ * assignment with no trailing command (`PATH=x` alone), both of which are
659
+ * real, persisting shell-variable assignments rather than a one-call
660
+ * prefix. A caller iterating a whole script applies this to every command
661
+ * that follows, not to `command` itself: a literal `ls` after either one
662
+ * cannot be trusted to resolve to what its text says, since which binary
663
+ * it runs — or what code a shell reads on startup (`BASH_ENV`, `ENV`) —
664
+ * depends on these variables' runtime value.
665
+ */
666
+ export function poisonsLaterCommands(command: ShellCommand): boolean {
667
+ const head = command.words[command.assignments]
668
+ if (head === undefined) return hasPoisoningPrefix(command)
669
+ if (!LITERAL(head)) return false
670
+ const args = command.words.slice(command.assignments + 1)
671
+ if (head.value === 'export') {
672
+ // `export -n NAME` REMOVES the export attribute — the opposite of
673
+ // poisoning — for every name in the same invocation; bash reads it
674
+ // once for the whole command, not per name.
675
+ if (args.some((w) => LITERAL(w) && w.value === '-n')) return false
676
+ return args.some((arg) => namesPoisoningVariable(arg))
677
+ }
678
+ if (head.value === 'declare' || head.value === 'typeset') {
679
+ // `declare -x`/`typeset -x` (and a cluster with it, `-gx`, `-xg`, …,
680
+ // but never `+x`, which removes the attribute) marks a name exported,
681
+ // the same as `export`. `declare` without `-x` only sets a local
682
+ // attribute (type, read-only, …) and does not export anything.
683
+ const exports = args.some((w) => LITERAL(w) && /^-[a-zA-Z]*x[a-zA-Z]*$/.test(w.value))
684
+ if (!exports) return false
685
+ return args.some((arg) => {
686
+ if (LITERAL(arg) && /^[-+]/.test(arg.value)) return false // an option, not a name
687
+ return namesPoisoningVariable(arg)
688
+ })
689
+ }
690
+ return false
691
+ }
692
+
693
+ /**
694
+ * `assignmentName`'s prefix, or the word's own literal value for a bare
695
+ * name (`export PATH`, `declare -x PATH`) — read regardless of whether the
696
+ * word's VALUE expands, since only the `NAME` portion (always literal in an
697
+ * assignment word) or the bare word's exact text decides which variable is
698
+ * named.
699
+ */
700
+ function namesPoisoningVariable(arg: ShellWord): boolean {
701
+ const name = assignmentName(arg) ?? (LITERAL(arg) ? arg.value : null)
702
+ return name !== null && DYNAMIC_RESOLUTION_VARIABLES.has(name)
703
+ }
704
+
705
+ /**
706
+ * The `unknown` position a command's own program name becomes once
707
+ * {@link hasPoisoningPrefix} or an earlier {@link poisonsLaterCommands} is
708
+ * true for it, in place of whatever {@link programPositions} would
709
+ * otherwise resolve — a literal name is no longer trustworthy once the
710
+ * environment it resolves against is unknown.
711
+ */
712
+ export function poisonedProgramPosition(): ProgramPosition {
713
+ return {
714
+ unknown:
715
+ 'PATH, LD_PRELOAD, LD_LIBRARY_PATH, BASH_ENV, ENV or IFS was set earlier in this line, so a literal program name here cannot be trusted to resolve to what its text says',
716
+ }
717
+ }
718
+
719
+ /** One command's {@link programPositions}, paired with the command itself. */
720
+ export interface CommandProgramPositions {
721
+ readonly command: ShellCommand
722
+ readonly positions: readonly ProgramPosition[]
723
+ }
724
+
725
+ /**
726
+ * {@link programPositions} for every command a script reads, in order,
727
+ * threading {@link hasPoisoningPrefix} and {@link poisonsLaterCommands}
728
+ * through so a literal program name after an earlier `PATH=`, `export
729
+ * PATH=`, … comes back {@link poisonedProgramPosition} rather than trusted
730
+ * at face value. This is the one loop the live `bash` tool's escalation, the
731
+ * scheduled-run floor and `verifyScheduledScript` would otherwise each write
732
+ * for themselves — exactly the duplication that let `env $(echo git) push`
733
+ * pass one consumer's check and not another's.
734
+ */
735
+ export function resolveScriptPrograms(
736
+ commands: readonly ShellCommand[],
737
+ dialect: ShellDialect,
738
+ ): readonly CommandProgramPositions[] {
739
+ let poisoned = false
740
+ const out: CommandProgramPositions[] = []
741
+ for (const command of commands) {
742
+ const selfPoisoned = poisoned || hasPoisoningPrefix(command)
743
+ const positions = selfPoisoned
744
+ ? [poisonedProgramPosition()]
745
+ : programPositions(command, dialect)
746
+ out.push({ command, positions })
747
+ if (poisonsLaterCommands(command)) poisoned = true
748
+ }
749
+ return out
750
+ }
751
+
752
+ /**
753
+ * Why a command line's own program name is not knowable ahead of running
754
+ * it, in one call: the line itself, unread whole (`bash -c "$X"`'s outer
755
+ * line is opaque — the lexer could not follow the payload at all, which is
756
+ * a stronger statement than any one position being unresolvable), or a
757
+ * specific command's unresolvable position via {@link resolveScriptPrograms}.
758
+ * The live `bash` tool's escalation (`executor.ts`) calls this directly
759
+ * rather than re-implementing it, so its own tests import the same function
760
+ * they are testing instead of a hand-written mirror that can drift from it.
761
+ */
762
+ export function unknownProgramInLine(value: string, dialect: ShellDialect): string | undefined {
763
+ const reading = lexShellCommandLine(value, { dialect })
764
+ if (reading.opaque || !reading.complete) {
765
+ return `${value}: the line cannot be read (${reading.reasons[0] ?? 'a construct the lexer does not model'}), so its program cannot be verified`
766
+ }
767
+ for (const { command, positions } of resolveScriptPrograms(reading.commands, dialect)) {
768
+ for (const position of positions) {
769
+ if (position.unknown !== undefined) return `${command.text}: ${position.unknown}`
770
+ }
771
+ }
772
+ return undefined
773
+ }