@namzu/sdk 51.1.0 → 52.1.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 (395) hide show
  1. package/CHANGELOG.md +119 -0
  2. package/dist/agents/QueryAgent.d.ts.map +1 -1
  3. package/dist/agents/QueryAgent.js +2 -0
  4. package/dist/agents/QueryAgent.js.map +1 -1
  5. package/dist/agents/SupervisorAgent.d.ts +1 -1
  6. package/dist/agents/SupervisorAgent.d.ts.map +1 -1
  7. package/dist/agents/SupervisorAgent.js.map +1 -1
  8. package/dist/agents/examples/SupervisorAgent.d.ts +42 -0
  9. package/dist/agents/examples/SupervisorAgent.d.ts.map +1 -1
  10. package/dist/agents/examples/SupervisorAgent.js +80 -4
  11. package/dist/agents/examples/SupervisorAgent.js.map +1 -1
  12. package/dist/agents/index.d.ts +1 -1
  13. package/dist/agents/index.d.ts.map +1 -1
  14. package/dist/agents/index.js.map +1 -1
  15. package/dist/authorization/protected-paths.d.ts +36 -0
  16. package/dist/authorization/protected-paths.d.ts.map +1 -0
  17. package/dist/authorization/protected-paths.js +107 -0
  18. package/dist/authorization/protected-paths.js.map +1 -0
  19. package/dist/authorization/readonly-commands.d.ts +39 -0
  20. package/dist/authorization/readonly-commands.d.ts.map +1 -0
  21. package/dist/authorization/readonly-commands.js +797 -0
  22. package/dist/authorization/readonly-commands.js.map +1 -0
  23. package/dist/authorization/readonly-flags.d.ts +19 -0
  24. package/dist/authorization/readonly-flags.d.ts.map +1 -0
  25. package/dist/authorization/readonly-flags.js +178 -0
  26. package/dist/authorization/readonly-flags.js.map +1 -0
  27. package/dist/authorization/removal.d.ts +52 -0
  28. package/dist/authorization/removal.d.ts.map +1 -0
  29. package/dist/authorization/removal.js +283 -0
  30. package/dist/authorization/removal.js.map +1 -0
  31. package/dist/authorization/shell-paths.d.ts +66 -0
  32. package/dist/authorization/shell-paths.d.ts.map +1 -0
  33. package/dist/authorization/shell-paths.js +697 -0
  34. package/dist/authorization/shell-paths.js.map +1 -0
  35. package/dist/bridge/acp/update.d.ts.map +1 -1
  36. package/dist/bridge/acp/update.js +1 -0
  37. package/dist/bridge/acp/update.js.map +1 -1
  38. package/dist/compaction/context-window.d.ts +24 -0
  39. package/dist/compaction/context-window.d.ts.map +1 -1
  40. package/dist/compaction/context-window.js +34 -0
  41. package/dist/compaction/context-window.js.map +1 -1
  42. package/dist/compaction/manual.d.ts.map +1 -1
  43. package/dist/compaction/manual.js +10 -2
  44. package/dist/compaction/manual.js.map +1 -1
  45. package/dist/compaction/plan.d.ts +4 -0
  46. package/dist/compaction/plan.d.ts.map +1 -1
  47. package/dist/compaction/plan.js +6 -2
  48. package/dist/compaction/plan.js.map +1 -1
  49. package/dist/compaction/verifier.d.ts +11 -1
  50. package/dist/compaction/verifier.d.ts.map +1 -1
  51. package/dist/compaction/verifier.js +75 -23
  52. package/dist/compaction/verifier.js.map +1 -1
  53. package/dist/config/runtime.d.ts +55 -7
  54. package/dist/config/runtime.d.ts.map +1 -1
  55. package/dist/config/runtime.js +23 -2
  56. package/dist/config/runtime.js.map +1 -1
  57. package/dist/connector/web/guarded-fetch.d.ts.map +1 -1
  58. package/dist/connector/web/guarded-fetch.js +24 -0
  59. package/dist/connector/web/guarded-fetch.js.map +1 -1
  60. package/dist/connector/web/index.d.ts +1 -1
  61. package/dist/connector/web/index.d.ts.map +1 -1
  62. package/dist/connector/web/page.d.ts +59 -0
  63. package/dist/connector/web/page.d.ts.map +1 -0
  64. package/dist/connector/web/page.js +112 -0
  65. package/dist/connector/web/page.js.map +1 -0
  66. package/dist/connector/web/types.d.ts +47 -0
  67. package/dist/connector/web/types.d.ts.map +1 -1
  68. package/dist/connector/web/types.js.map +1 -1
  69. package/dist/constants/continuation.d.ts +8 -0
  70. package/dist/constants/continuation.d.ts.map +1 -1
  71. package/dist/constants/continuation.js +8 -0
  72. package/dist/constants/continuation.js.map +1 -1
  73. package/dist/constants/plugin/index.d.ts +1 -1
  74. package/dist/constants/plugin/index.d.ts.map +1 -1
  75. package/dist/constants/plugin/index.js +1 -1
  76. package/dist/constants/plugin/index.js.map +1 -1
  77. package/dist/contracts/a2a.d.ts +2 -2
  78. package/dist/manager/agent/lifecycle.d.ts +7 -0
  79. package/dist/manager/agent/lifecycle.d.ts.map +1 -1
  80. package/dist/manager/agent/lifecycle.js +11 -2
  81. package/dist/manager/agent/lifecycle.js.map +1 -1
  82. package/dist/manager/session/turn-recorder.d.ts.map +1 -1
  83. package/dist/manager/session/turn-recorder.js +4 -2
  84. package/dist/manager/session/turn-recorder.js.map +1 -1
  85. package/dist/plugin/index.d.ts +1 -1
  86. package/dist/plugin/index.d.ts.map +1 -1
  87. package/dist/plugin/index.js +1 -1
  88. package/dist/plugin/index.js.map +1 -1
  89. package/dist/plugin/lifecycle.d.ts +6 -0
  90. package/dist/plugin/lifecycle.d.ts.map +1 -1
  91. package/dist/plugin/lifecycle.js +98 -114
  92. package/dist/plugin/lifecycle.js.map +1 -1
  93. package/dist/plugin/shell-hook.d.ts +90 -22
  94. package/dist/plugin/shell-hook.d.ts.map +1 -1
  95. package/dist/plugin/shell-hook.js +434 -41
  96. package/dist/plugin/shell-hook.js.map +1 -1
  97. package/dist/provider/capabilities.d.ts +9 -0
  98. package/dist/provider/capabilities.d.ts.map +1 -1
  99. package/dist/provider/capabilities.js +15 -0
  100. package/dist/provider/capabilities.js.map +1 -1
  101. package/dist/provider/errors.d.ts.map +1 -1
  102. package/dist/provider/errors.js +13 -0
  103. package/dist/provider/errors.js.map +1 -1
  104. package/dist/provider/fallback.d.ts.map +1 -1
  105. package/dist/provider/fallback.js +10 -0
  106. package/dist/provider/fallback.js.map +1 -1
  107. package/dist/provider/idle-timeout.d.ts.map +1 -1
  108. package/dist/provider/idle-timeout.js +5 -0
  109. package/dist/provider/idle-timeout.js.map +1 -1
  110. package/dist/provider/image-input-refusal.d.ts +9 -0
  111. package/dist/provider/image-input-refusal.d.ts.map +1 -0
  112. package/dist/provider/image-input-refusal.js +54 -0
  113. package/dist/provider/image-input-refusal.js.map +1 -0
  114. package/dist/provider/index.d.ts +2 -1
  115. package/dist/provider/index.d.ts.map +1 -1
  116. package/dist/provider/index.js +2 -1
  117. package/dist/provider/index.js.map +1 -1
  118. package/dist/provider/retry.d.ts +7 -2
  119. package/dist/provider/retry.d.ts.map +1 -1
  120. package/dist/provider/retry.js +38 -4
  121. package/dist/provider/retry.js.map +1 -1
  122. package/dist/public-runtime.d.ts +5 -2
  123. package/dist/public-runtime.d.ts.map +1 -1
  124. package/dist/public-runtime.js +5 -2
  125. package/dist/public-runtime.js.map +1 -1
  126. package/dist/public-types.d.ts +2 -2
  127. package/dist/public-types.d.ts.map +1 -1
  128. package/dist/runtime/jobs/registry.d.ts +21 -0
  129. package/dist/runtime/jobs/registry.d.ts.map +1 -1
  130. package/dist/runtime/jobs/registry.js +15 -2
  131. package/dist/runtime/jobs/registry.js.map +1 -1
  132. package/dist/runtime/query/deferred-sandbox.d.ts +54 -0
  133. package/dist/runtime/query/deferred-sandbox.d.ts.map +1 -0
  134. package/dist/runtime/query/deferred-sandbox.js +75 -0
  135. package/dist/runtime/query/deferred-sandbox.js.map +1 -0
  136. package/dist/runtime/query/executor/tool-call-admission.d.ts +10 -0
  137. package/dist/runtime/query/executor/tool-call-admission.d.ts.map +1 -1
  138. package/dist/runtime/query/executor/tool-call-admission.js +74 -13
  139. package/dist/runtime/query/executor/tool-call-admission.js.map +1 -1
  140. package/dist/runtime/query/executor.d.ts +51 -2
  141. package/dist/runtime/query/executor.d.ts.map +1 -1
  142. package/dist/runtime/query/executor.js +214 -9
  143. package/dist/runtime/query/executor.js.map +1 -1
  144. package/dist/runtime/query/finalize-turn.d.ts +6 -0
  145. package/dist/runtime/query/finalize-turn.d.ts.map +1 -1
  146. package/dist/runtime/query/finalize-turn.js +1 -1
  147. package/dist/runtime/query/finalize-turn.js.map +1 -1
  148. package/dist/runtime/query/index.d.ts +29 -0
  149. package/dist/runtime/query/index.d.ts.map +1 -1
  150. package/dist/runtime/query/index.js +108 -28
  151. package/dist/runtime/query/index.js.map +1 -1
  152. package/dist/runtime/query/iteration/index.d.ts +57 -0
  153. package/dist/runtime/query/iteration/index.d.ts.map +1 -1
  154. package/dist/runtime/query/iteration/index.js +380 -28
  155. package/dist/runtime/query/iteration/index.js.map +1 -1
  156. package/dist/runtime/query/iteration/phases/compaction-breaker.d.ts +61 -0
  157. package/dist/runtime/query/iteration/phases/compaction-breaker.d.ts.map +1 -0
  158. package/dist/runtime/query/iteration/phases/compaction-breaker.js +94 -0
  159. package/dist/runtime/query/iteration/phases/compaction-breaker.js.map +1 -0
  160. package/dist/runtime/query/iteration/phases/compaction.d.ts +42 -1
  161. package/dist/runtime/query/iteration/phases/compaction.d.ts.map +1 -1
  162. package/dist/runtime/query/iteration/phases/compaction.js +203 -23
  163. package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
  164. package/dist/runtime/query/iteration/phases/context.d.ts +10 -0
  165. package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
  166. package/dist/runtime/query/iteration/phases/context.js.map +1 -1
  167. package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
  168. package/dist/runtime/query/iteration/phases/tool-review.js +165 -2
  169. package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
  170. package/dist/runtime/query/iteration/provider-rejected-image.d.ts +21 -1
  171. package/dist/runtime/query/iteration/provider-rejected-image.d.ts.map +1 -1
  172. package/dist/runtime/query/iteration/provider-rejected-image.js +53 -7
  173. package/dist/runtime/query/iteration/provider-rejected-image.js.map +1 -1
  174. package/dist/runtime/query/iteration/request-repair.d.ts +108 -0
  175. package/dist/runtime/query/iteration/request-repair.d.ts.map +1 -0
  176. package/dist/runtime/query/iteration/request-repair.js +593 -0
  177. package/dist/runtime/query/iteration/request-repair.js.map +1 -0
  178. package/dist/runtime/query/iteration/stream-turn.d.ts +6 -0
  179. package/dist/runtime/query/iteration/stream-turn.d.ts.map +1 -1
  180. package/dist/runtime/query/iteration/stream-turn.js +2 -2
  181. package/dist/runtime/query/iteration/stream-turn.js.map +1 -1
  182. package/dist/runtime/query/path-escalation.d.ts +36 -0
  183. package/dist/runtime/query/path-escalation.d.ts.map +1 -0
  184. package/dist/runtime/query/path-escalation.js +184 -0
  185. package/dist/runtime/query/path-escalation.js.map +1 -0
  186. package/dist/runtime/query/plugin-hooks.d.ts.map +1 -1
  187. package/dist/runtime/query/plugin-hooks.js +8 -0
  188. package/dist/runtime/query/plugin-hooks.js.map +1 -1
  189. package/dist/runtime/query/reminders.d.ts +35 -0
  190. package/dist/runtime/query/reminders.d.ts.map +1 -0
  191. package/dist/runtime/query/reminders.js +7 -0
  192. package/dist/runtime/query/reminders.js.map +1 -0
  193. package/dist/runtime/query/request-rich-content.d.ts +5 -1
  194. package/dist/runtime/query/request-rich-content.d.ts.map +1 -1
  195. package/dist/runtime/query/request-rich-content.js +67 -25
  196. package/dist/runtime/query/request-rich-content.js.map +1 -1
  197. package/dist/runtime/query/resume-pending.d.ts.map +1 -1
  198. package/dist/runtime/query/resume-pending.js +26 -0
  199. package/dist/runtime/query/resume-pending.js.map +1 -1
  200. package/dist/runtime/query/review-policy.d.ts +26 -0
  201. package/dist/runtime/query/review-policy.d.ts.map +1 -1
  202. package/dist/runtime/query/review-policy.js +79 -5
  203. package/dist/runtime/query/review-policy.js.map +1 -1
  204. package/dist/runtime/query/tool-output-budget.d.ts +46 -0
  205. package/dist/runtime/query/tool-output-budget.d.ts.map +1 -1
  206. package/dist/runtime/query/tool-output-budget.js +85 -1
  207. package/dist/runtime/query/tool-output-budget.js.map +1 -1
  208. package/dist/runtime/query/tooling.d.ts +1 -0
  209. package/dist/runtime/query/tooling.d.ts.map +1 -1
  210. package/dist/runtime/query/tooling.js +5 -0
  211. package/dist/runtime/query/tooling.js.map +1 -1
  212. package/dist/session/paths.d.ts +5 -1
  213. package/dist/session/paths.d.ts.map +1 -1
  214. package/dist/session/paths.js +8 -0
  215. package/dist/session/paths.js.map +1 -1
  216. package/dist/store/memory/disk.d.ts.map +1 -1
  217. package/dist/store/memory/disk.js +2 -2
  218. package/dist/store/memory/disk.js.map +1 -1
  219. package/dist/store/memory/markdown.d.ts.map +1 -1
  220. package/dist/store/memory/markdown.js +2 -2
  221. package/dist/store/memory/markdown.js.map +1 -1
  222. package/dist/store/memory/naming.d.ts +9 -0
  223. package/dist/store/memory/naming.d.ts.map +1 -1
  224. package/dist/store/memory/naming.js +12 -0
  225. package/dist/store/memory/naming.js.map +1 -1
  226. package/dist/tools/builtins/bash.d.ts +30 -1
  227. package/dist/tools/builtins/bash.d.ts.map +1 -1
  228. package/dist/tools/builtins/bash.js +170 -26
  229. package/dist/tools/builtins/bash.js.map +1 -1
  230. package/dist/tools/builtins/computer-use.d.ts +1 -1
  231. package/dist/tools/builtins/computer-use.d.ts.map +1 -1
  232. package/dist/tools/builtins/computer-use.js +12 -5
  233. package/dist/tools/builtins/computer-use.js.map +1 -1
  234. package/dist/tools/builtins/content-fingerprint.d.ts +2 -0
  235. package/dist/tools/builtins/content-fingerprint.d.ts.map +1 -1
  236. package/dist/tools/builtins/content-fingerprint.js +4 -0
  237. package/dist/tools/builtins/content-fingerprint.js.map +1 -1
  238. package/dist/tools/builtins/edit.d.ts.map +1 -1
  239. package/dist/tools/builtins/edit.js +60 -18
  240. package/dist/tools/builtins/edit.js.map +1 -1
  241. package/dist/tools/builtins/grep.d.ts +8 -1
  242. package/dist/tools/builtins/grep.d.ts.map +1 -1
  243. package/dist/tools/builtins/grep.js +152 -33
  244. package/dist/tools/builtins/grep.js.map +1 -1
  245. package/dist/tools/builtins/read-file.d.ts +1 -0
  246. package/dist/tools/builtins/read-file.d.ts.map +1 -1
  247. package/dist/tools/builtins/read-file.js +44 -5
  248. package/dist/tools/builtins/read-file.js.map +1 -1
  249. package/dist/tools/builtins/read-render.d.ts +52 -0
  250. package/dist/tools/builtins/read-render.d.ts.map +1 -1
  251. package/dist/tools/builtins/read-render.js +143 -0
  252. package/dist/tools/builtins/read-render.js.map +1 -1
  253. package/dist/tools/builtins/web.d.ts +13 -2
  254. package/dist/tools/builtins/web.d.ts.map +1 -1
  255. package/dist/tools/builtins/web.js +225 -32
  256. package/dist/tools/builtins/web.js.map +1 -1
  257. package/dist/tools/coordinator/ask-user-question.d.ts +2 -0
  258. package/dist/tools/coordinator/ask-user-question.d.ts.map +1 -1
  259. package/dist/tools/coordinator/ask-user-question.js +256 -92
  260. package/dist/tools/coordinator/ask-user-question.js.map +1 -1
  261. package/dist/tools/coordinator/question-options.d.ts +1 -0
  262. package/dist/tools/coordinator/question-options.d.ts.map +1 -1
  263. package/dist/tools/coordinator/question-options.js +6 -1
  264. package/dist/tools/coordinator/question-options.js.map +1 -1
  265. package/dist/tools/defineTool.d.ts +4 -1
  266. package/dist/tools/defineTool.d.ts.map +1 -1
  267. package/dist/tools/defineTool.js +6 -1
  268. package/dist/tools/defineTool.js.map +1 -1
  269. package/dist/tools/paths.d.ts +6 -0
  270. package/dist/tools/paths.d.ts.map +1 -1
  271. package/dist/tools/paths.js +1 -1
  272. package/dist/tools/paths.js.map +1 -1
  273. package/dist/types/agent/query.d.ts +3 -0
  274. package/dist/types/agent/query.d.ts.map +1 -1
  275. package/dist/types/errors/catalog.js +1 -1
  276. package/dist/types/errors/catalog.js.map +1 -1
  277. package/dist/types/hitl/index.d.ts +104 -0
  278. package/dist/types/hitl/index.d.ts.map +1 -1
  279. package/dist/types/hitl/index.js.map +1 -1
  280. package/dist/types/message/index.d.ts +1 -1
  281. package/dist/types/message/index.d.ts.map +1 -1
  282. package/dist/types/message/index.js +2 -0
  283. package/dist/types/message/index.js.map +1 -1
  284. package/dist/types/plugin/index.d.ts +93 -19
  285. package/dist/types/plugin/index.d.ts.map +1 -1
  286. package/dist/types/plugin/index.js +3 -0
  287. package/dist/types/plugin/index.js.map +1 -1
  288. package/dist/types/provider/errors.d.ts +19 -0
  289. package/dist/types/provider/errors.d.ts.map +1 -1
  290. package/dist/types/provider/errors.js +54 -5
  291. package/dist/types/provider/errors.js.map +1 -1
  292. package/dist/types/provider/interface.d.ts +12 -0
  293. package/dist/types/provider/interface.d.ts.map +1 -1
  294. package/dist/types/provider/stream.d.ts +1 -1
  295. package/dist/types/session/config.d.ts +14 -0
  296. package/dist/types/session/config.d.ts.map +1 -1
  297. package/dist/types/session/events.d.ts +27 -4
  298. package/dist/types/session/events.d.ts.map +1 -1
  299. package/dist/types/session/events.js.map +1 -1
  300. package/dist/types/session/records.d.ts +9 -9
  301. package/dist/types/session/records.d.ts.map +1 -1
  302. package/dist/types/session/records.js +1 -0
  303. package/dist/types/session/records.js.map +1 -1
  304. package/dist/types/session/stop-reason.d.ts +5 -0
  305. package/dist/types/session/stop-reason.d.ts.map +1 -1
  306. package/dist/types/tool/index.d.ts +52 -0
  307. package/dist/types/tool/index.d.ts.map +1 -1
  308. package/dist/types/tool/index.js.map +1 -1
  309. package/package.json +4 -2
  310. package/src/agents/QueryAgent.ts +5 -1
  311. package/src/agents/SupervisorAgent.ts +1 -1
  312. package/src/agents/examples/SupervisorAgent.ts +217 -106
  313. package/src/agents/index.ts +1 -1
  314. package/src/authorization/protected-paths.ts +110 -0
  315. package/src/authorization/readonly-commands.ts +771 -0
  316. package/src/authorization/readonly-flags.ts +196 -0
  317. package/src/authorization/removal.ts +326 -0
  318. package/src/authorization/shell-paths.ts +927 -0
  319. package/src/bridge/acp/update.ts +1 -0
  320. package/src/compaction/context-window.ts +41 -0
  321. package/src/compaction/manual.ts +10 -2
  322. package/src/compaction/plan.ts +11 -2
  323. package/src/compaction/verifier.ts +84 -22
  324. package/src/config/runtime.ts +23 -2
  325. package/src/connector/web/guarded-fetch.ts +26 -0
  326. package/src/connector/web/index.ts +3 -0
  327. package/src/connector/web/page.ts +130 -0
  328. package/src/connector/web/types.ts +47 -0
  329. package/src/constants/continuation.ts +10 -0
  330. package/src/constants/plugin/index.ts +1 -1
  331. package/src/manager/agent/lifecycle.ts +12 -4
  332. package/src/manager/session/turn-recorder.ts +10 -2
  333. package/src/plugin/index.ts +1 -0
  334. package/src/plugin/lifecycle.ts +107 -119
  335. package/src/plugin/shell-hook.ts +477 -43
  336. package/src/provider/capabilities.ts +17 -0
  337. package/src/provider/errors.ts +13 -0
  338. package/src/provider/fallback.ts +10 -0
  339. package/src/provider/idle-timeout.ts +5 -0
  340. package/src/provider/image-input-refusal.ts +54 -0
  341. package/src/provider/index.ts +2 -0
  342. package/src/provider/retry.ts +42 -4
  343. package/src/public-runtime.ts +11 -0
  344. package/src/public-types.ts +4 -0
  345. package/src/runtime/jobs/registry.ts +39 -2
  346. package/src/runtime/query/deferred-sandbox.ts +110 -0
  347. package/src/runtime/query/executor/tool-call-admission.ts +79 -13
  348. package/src/runtime/query/executor.ts +267 -11
  349. package/src/runtime/query/finalize-turn.ts +7 -1
  350. package/src/runtime/query/index.ts +169 -52
  351. package/src/runtime/query/iteration/index.ts +439 -35
  352. package/src/runtime/query/iteration/phases/compaction-breaker.ts +108 -0
  353. package/src/runtime/query/iteration/phases/compaction.ts +241 -38
  354. package/src/runtime/query/iteration/phases/context.ts +11 -0
  355. package/src/runtime/query/iteration/phases/tool-review.ts +177 -2
  356. package/src/runtime/query/iteration/provider-rejected-image.ts +71 -5
  357. package/src/runtime/query/iteration/request-repair.ts +743 -0
  358. package/src/runtime/query/iteration/stream-turn.ts +11 -1
  359. package/src/runtime/query/path-escalation.ts +204 -0
  360. package/src/runtime/query/plugin-hooks.ts +8 -0
  361. package/src/runtime/query/reminders.ts +45 -0
  362. package/src/runtime/query/request-rich-content.ts +77 -27
  363. package/src/runtime/query/resume-pending.ts +27 -0
  364. package/src/runtime/query/review-policy.ts +106 -5
  365. package/src/runtime/query/tool-output-budget.ts +115 -1
  366. package/src/runtime/query/tooling.ts +6 -0
  367. package/src/session/paths.ts +11 -1
  368. package/src/store/memory/disk.ts +2 -1
  369. package/src/store/memory/markdown.ts +2 -1
  370. package/src/store/memory/naming.ts +13 -0
  371. package/src/tools/builtins/bash.ts +203 -34
  372. package/src/tools/builtins/computer-use.ts +13 -4
  373. package/src/tools/builtins/content-fingerprint.ts +5 -0
  374. package/src/tools/builtins/edit.ts +69 -19
  375. package/src/tools/builtins/grep.ts +159 -38
  376. package/src/tools/builtins/read-file.ts +48 -5
  377. package/src/tools/builtins/read-render.ts +193 -0
  378. package/src/tools/builtins/web.ts +258 -33
  379. package/src/tools/coordinator/ask-user-question.ts +309 -103
  380. package/src/tools/coordinator/question-options.ts +8 -1
  381. package/src/tools/defineTool.ts +11 -2
  382. package/src/tools/paths.ts +1 -1
  383. package/src/types/agent/query.ts +3 -0
  384. package/src/types/errors/catalog.ts +1 -1
  385. package/src/types/hitl/index.ts +104 -0
  386. package/src/types/message/index.ts +2 -0
  387. package/src/types/plugin/index.ts +101 -18
  388. package/src/types/provider/errors.ts +66 -5
  389. package/src/types/provider/interface.ts +13 -0
  390. package/src/types/provider/stream.ts +1 -1
  391. package/src/types/session/config.ts +14 -0
  392. package/src/types/session/events.ts +32 -4
  393. package/src/types/session/records.ts +1 -0
  394. package/src/types/session/stop-reason.ts +5 -0
  395. package/src/types/tool/index.ts +53 -2
@@ -0,0 +1,927 @@
1
+ /**
2
+ * The files a shell command line names, read before it runs.
3
+ *
4
+ * The file tools declare a `pathArgument`, so the kernel can see which path a
5
+ * call reaches. A shell command line has no such argument: `cat /etc/passwd`
6
+ * and `echo x > ~/.profile` carry their paths inside one string, and a
7
+ * boundary that only reads declared arguments let both through.
8
+ *
9
+ * This is the reference terminal's answer, ported: a table of the commands
10
+ * whose positional arguments are paths (its path-command table and
11
+ * per-command extractors, version 2.1.302), the kind of access each makes (read,
12
+ * write or create), and the output redirections of the line. It is a
13
+ * reading of the text, not a model of the shell:
14
+ *
15
+ * - a word that expands at runtime (`$VAR`, `$(...)`) has no path to read;
16
+ * it is reported with `unresolved` so the caller decides what an unreadable
17
+ * write target is worth (the reference asks);
18
+ * - a `cd` earlier in the line moves every relative path after it, which the
19
+ * reading does not follow, so the caller is told one happened;
20
+ * - a command that is not in the table contributes nothing.
21
+ *
22
+ * Nothing here touches the file system. The caller resolves the paths
23
+ * against the turn's roots and decides what is outside them or protected.
24
+ */
25
+
26
+ import { unwrapReexecChain } from './reexec-wrapper.js'
27
+ import {
28
+ type ShellDialect,
29
+ type ShellRedirection,
30
+ type ShellWord,
31
+ basename,
32
+ lexShellCommandLine,
33
+ } from './shell-lexer.js'
34
+
35
+ /** One program invocation found in a line, wrappers unwrapped. */
36
+ export interface ShellInvocation {
37
+ /** The program's base name (`/bin/rm` reads as `rm`). */
38
+ readonly program: string
39
+ /** Its arguments, as the shell passes them. */
40
+ readonly args: readonly ShellWord[]
41
+ }
42
+
43
+ /** What the lexer found, in the shape this module and the removal check share. */
44
+ export interface ShellReading {
45
+ readonly invocations: readonly ShellInvocation[]
46
+ readonly redirections: readonly ShellRedirection[]
47
+ /** False when the line does not parse to the end. */
48
+ readonly complete: boolean
49
+ }
50
+
51
+ const EXEC_FLAGS = new Set(['-exec', '-execdir', '-ok', '-okdir'])
52
+
53
+ function invocationOf(words: readonly ShellWord[]): ShellInvocation | undefined {
54
+ if (words.length === 0) return undefined
55
+ const step = unwrapReexecChain(words)
56
+ if (!('rest' in step)) return undefined
57
+ const [head, ...args] = step.rest
58
+ if (head === undefined || head.expands) return undefined
59
+ return { program: basename(head.value), args }
60
+ }
61
+
62
+ /** Every program a line runs, and every redirection it carries. */
63
+ export function readShellLine(line: string, dialect: ShellDialect): ShellReading {
64
+ const lexed = lexShellCommandLine(line, { dialect })
65
+ const invocations: ShellInvocation[] = []
66
+ for (const command of lexed.commands) {
67
+ const own = invocationOf(command.words.slice(command.assignments))
68
+ if (own === undefined) continue
69
+ invocations.push(own)
70
+ if (own.program !== 'find') continue
71
+ // `find … -exec CMD … ;` runs CMD on each match; its arguments up to the
72
+ // terminator are CMD's.
73
+ for (let i = 0; i < own.args.length; i += 1) {
74
+ const flag = own.args[i] as ShellWord
75
+ if (flag.expands || !EXEC_FLAGS.has(flag.value)) continue
76
+ const clause: ShellWord[] = []
77
+ for (let j = i + 1; j < own.args.length; j += 1) {
78
+ const word = own.args[j] as ShellWord
79
+ if (!word.expands && (word.value === ';' || word.value === '+')) break
80
+ clause.push(word)
81
+ }
82
+ const nested = invocationOf(clause)
83
+ if (nested !== undefined) invocations.push(nested)
84
+ }
85
+ }
86
+ return {
87
+ invocations,
88
+ redirections: lexed.redirections,
89
+ complete: lexed.complete,
90
+ }
91
+ }
92
+
93
+ /** The kind of access a command makes to a path argument. */
94
+ export type PathAccess = 'read' | 'write'
95
+
96
+ /** A path a line names, as written. */
97
+ export interface ShellPathUse {
98
+ /** The path with quotes removed; `~` and `$HOME` are left for the caller. */
99
+ readonly path: string
100
+ readonly access: PathAccess
101
+ /** Which program or redirection named it. */
102
+ readonly via: string
103
+ /** True when the word is decided at runtime and `path` is not what it will be. */
104
+ readonly unresolved: boolean
105
+ }
106
+
107
+ export interface ShellPathAccess {
108
+ readonly uses: readonly ShellPathUse[]
109
+ /** A `cd`, `pushd`, `popd` or `chdir` runs somewhere in the line. */
110
+ readonly changesDirectory: boolean
111
+ }
112
+
113
+ /** Devices a command may name without it being a file. */
114
+ const HARMLESS_TARGETS = new Set([
115
+ '/dev/null',
116
+ '/dev/stdin',
117
+ '/dev/stdout',
118
+ '/dev/stderr',
119
+ '/dev/tty',
120
+ ])
121
+
122
+ function isHarmlessTarget(path: string): boolean {
123
+ return (
124
+ HARMLESS_TARGETS.has(path) ||
125
+ /^\/dev\/fd\/\d+$/.test(path) ||
126
+ /^\/proc\/self\/fd\/[0-2]$/.test(path)
127
+ )
128
+ }
129
+
130
+ /** Whether a word's value still holds an expansion the shell will perform. */
131
+ export function expandsAtRuntime(word: ShellWord): boolean {
132
+ return word.expands && (/[$`]/.test(word.value) || word.substitutes)
133
+ }
134
+
135
+ type Args = readonly ShellWord[]
136
+
137
+ /** Arguments that are not flags: everything after `--`, and everything from the first operand on. */
138
+ function operands(args: Args): ShellWord[] {
139
+ const out: ShellWord[] = []
140
+ let afterDashes = false
141
+ let started = false
142
+ for (const arg of args) {
143
+ if (afterDashes || started) out.push(arg)
144
+ else if (arg.value === '--') afterDashes = true
145
+ else if (arg.value === '-' || !arg.value.startsWith('-')) {
146
+ out.push(arg)
147
+ started = true
148
+ }
149
+ }
150
+ return out
151
+ }
152
+
153
+ interface FlagTable {
154
+ readonly booleanShort: string
155
+ readonly valuedShort: string
156
+ readonly attachedShort?: string
157
+ readonly booleanLong: ReadonlySet<string>
158
+ readonly attachedLong?: ReadonlySet<string>
159
+ readonly valuedLong: ReadonlySet<string>
160
+ }
161
+
162
+ /** Operands of a command whose flags are known: an unknown flag stops flag parsing, as in the reference. */
163
+ function operandsByTable(table: FlagTable): (args: Args) => ShellWord[] {
164
+ const kind = (flag: string): 'boolean' | 'valued' | 'attached' | 'unknown' => {
165
+ if (flag.startsWith('--')) {
166
+ if (flag.includes('=')) return 'attached'
167
+ if (table.booleanLong.has(flag)) return 'boolean'
168
+ if (table.attachedLong?.has(flag)) return 'attached'
169
+ return table.valuedLong.has(flag) ? 'valued' : 'unknown'
170
+ }
171
+ if (/^-\d+$/.test(flag)) return 'attached'
172
+ for (let i = 1; i < flag.length; i += 1) {
173
+ const letter = flag[i] as string
174
+ if (table.booleanShort.includes(letter)) continue
175
+ if (table.attachedShort?.includes(letter)) return 'attached'
176
+ if (!table.valuedShort.includes(letter)) return 'unknown'
177
+ return i === flag.length - 1 ? 'valued' : 'attached'
178
+ }
179
+ return 'boolean'
180
+ }
181
+ return (args) => {
182
+ const out: ShellWord[] = []
183
+ let afterDashes = false
184
+ let started = false
185
+ let parsing = true
186
+ for (let i = 0; i < args.length; i += 1) {
187
+ const arg = args[i] as ShellWord
188
+ if (afterDashes || started) out.push(arg)
189
+ else if (arg.value === '--') afterDashes = true
190
+ else if (arg.value !== '-' && arg.value.startsWith('-')) {
191
+ if (!parsing) continue
192
+ const k = kind(arg.value)
193
+ if (k === 'valued') i += 1
194
+ else if (k === 'unknown') parsing = false
195
+ } else {
196
+ out.push(arg)
197
+ started = true
198
+ }
199
+ }
200
+ return out
201
+ }
202
+ }
203
+
204
+ const set = (...names: string[]): ReadonlySet<string> => new Set(names)
205
+
206
+ /** Operands of `grep`-family and `rg`: the first operand is the pattern unless `-e`/`-f` gave one. */
207
+ function patternThenFiles(
208
+ valued: ReadonlySet<string>,
209
+ fallback: string[] = [],
210
+ ): (args: Args) => ShellWord[] {
211
+ const patternFlags = ['-e', '--regexp', '-f', '--file']
212
+ const fileFlags = ['-f', '--file']
213
+ const attachedValue = (flag: string, names: readonly string[]): string | undefined => {
214
+ if (!flag.startsWith('-')) return undefined
215
+ const eq = flag.indexOf('=')
216
+ if (eq >= 0) return names.includes(flag.slice(0, eq)) ? flag.slice(eq + 1) : undefined
217
+ for (const name of names) {
218
+ if (name.length === 2 && name[0] === '-' && flag.startsWith(name) && flag !== name) {
219
+ return flag.slice(2)
220
+ }
221
+ }
222
+ return undefined
223
+ }
224
+ return (args) => {
225
+ const out: ShellWord[] = []
226
+ let patternGiven = false
227
+ let afterDashes = false
228
+ let started = false
229
+ const take = (value: string | undefined): void => {
230
+ if (value) out.push(word(value))
231
+ }
232
+ for (let i = 0; i < args.length; i += 1) {
233
+ const arg = args[i] as ShellWord
234
+ const text = arg.value
235
+ if (!afterDashes && !started && text === '--') {
236
+ afterDashes = true
237
+ continue
238
+ }
239
+ if (!afterDashes && !started && text !== '-' && text.startsWith('-')) {
240
+ const eq = text.indexOf('=')
241
+ const name = eq >= 0 ? text.slice(0, eq) : text
242
+ if (patternFlags.includes(name)) {
243
+ patternGiven = true
244
+ if (name === '-f' || name === '--file') {
245
+ take(eq >= 0 ? text.slice(eq + 1) : args[i + 1]?.value)
246
+ }
247
+ }
248
+ if (/^-[a-zA-Z]*f$/.test(name) && name !== '-f' && eq < 0 && args[i + 1] !== undefined) {
249
+ patternGiven = true
250
+ take(args[i + 1]?.value)
251
+ i += 1
252
+ continue
253
+ }
254
+ if (['--exclude-from', '--include-from', '--ignore-file'].includes(name)) {
255
+ take(eq >= 0 ? text.slice(eq + 1) : args[i + 1]?.value)
256
+ if (eq < 0) i += 1
257
+ continue
258
+ }
259
+ if (eq < 0) {
260
+ const attachedFile = attachedValue(text, fileFlags)
261
+ if (attachedFile !== undefined) {
262
+ patternGiven = true
263
+ take(attachedFile)
264
+ continue
265
+ }
266
+ if (text.length > 2 && text.startsWith('-e')) {
267
+ patternGiven = true
268
+ continue
269
+ }
270
+ }
271
+ if (valued.has(name) && eq < 0) i += 1
272
+ continue
273
+ }
274
+ if (started && !afterDashes) {
275
+ const attachedFile = attachedValue(text, fileFlags)
276
+ if (attachedFile !== undefined) take(attachedFile)
277
+ }
278
+ started = true
279
+ if (!patternGiven) {
280
+ patternGiven = true
281
+ continue
282
+ }
283
+ out.push(arg)
284
+ }
285
+ return out.length > 0 ? out : fallback.map(word)
286
+ }
287
+ }
288
+
289
+ /** A synthetic word for a path the extractor derived rather than read. */
290
+ function word(value: string): ShellWord {
291
+ return {
292
+ text: value,
293
+ value,
294
+ expands: false,
295
+ quoted: false,
296
+ substitutes: false,
297
+ }
298
+ }
299
+
300
+ const grepOperands = (args: Args): ShellWord[] => {
301
+ const found = patternThenFiles(
302
+ set(
303
+ '-e',
304
+ '--regexp',
305
+ '-f',
306
+ '--file',
307
+ '--exclude',
308
+ '--include',
309
+ '--exclude-dir',
310
+ '--include-dir',
311
+ '-m',
312
+ '--max-count',
313
+ '-A',
314
+ '--after-context',
315
+ '-B',
316
+ '--before-context',
317
+ '-C',
318
+ '--context',
319
+ ),
320
+ )(args)
321
+ if (found.length === 0 && args.some((a) => ['-r', '-R', '--recursive'].includes(a.value))) {
322
+ return [word('.')]
323
+ }
324
+ return found
325
+ }
326
+
327
+ const rgOperands = patternThenFiles(
328
+ set(
329
+ '-e',
330
+ '--regexp',
331
+ '-f',
332
+ '--file',
333
+ '-t',
334
+ '--type',
335
+ '-T',
336
+ '--type-not',
337
+ '-g',
338
+ '--glob',
339
+ '-m',
340
+ '--max-count',
341
+ '--max-depth',
342
+ '-r',
343
+ '--replace',
344
+ '-A',
345
+ '--after-context',
346
+ '-B',
347
+ '--before-context',
348
+ '-C',
349
+ '--context',
350
+ ),
351
+ ['.'],
352
+ )
353
+
354
+ function awkOperands(args: Args): ShellWord[] {
355
+ const valued = set('-F', '--field-separator', '-v', '--assign', '-e', '--source')
356
+ const files = ['-f', '--file', '-E', '--exec']
357
+ const out: ShellWord[] = []
358
+ let afterDashes = false
359
+ let started = false
360
+ let programGiven = false
361
+ for (let i = 0; i < args.length; i += 1) {
362
+ const arg = args[i] as ShellWord
363
+ const text = arg.value
364
+ if (!afterDashes && !started && text === '--') {
365
+ afterDashes = true
366
+ continue
367
+ }
368
+ if (!afterDashes && !started && text !== '-' && text.startsWith('-')) {
369
+ const eq = text.indexOf('=')
370
+ const name = eq >= 0 ? text.slice(0, eq) : text
371
+ if (valued.has(name)) {
372
+ if (name === '-e' || name === '--source') programGiven = true
373
+ if (eq < 0) i += 1
374
+ continue
375
+ }
376
+ if (files.includes(name)) {
377
+ programGiven = true
378
+ if (eq >= 0) out.push(word(text.slice(eq + 1)))
379
+ else if (args[i + 1] !== undefined) {
380
+ out.push(args[i + 1] as ShellWord)
381
+ i += 1
382
+ }
383
+ continue
384
+ }
385
+ continue
386
+ }
387
+ started = true
388
+ if (!programGiven) {
389
+ programGiven = true
390
+ continue
391
+ }
392
+ out.push(arg)
393
+ }
394
+ return out
395
+ }
396
+
397
+ function sedOperands(args: Args): ShellWord[] {
398
+ const out: ShellWord[] = []
399
+ let skipNext = false
400
+ let scriptGiven = false
401
+ let afterDashes = false
402
+ let started = false
403
+ for (let i = 0; i < args.length; i += 1) {
404
+ if (skipNext) {
405
+ skipNext = false
406
+ continue
407
+ }
408
+ const arg = args[i] as ShellWord
409
+ const text = arg.value
410
+ if (!afterDashes && !started && text === '--') {
411
+ afterDashes = true
412
+ continue
413
+ }
414
+ if (!afterDashes && !started && text !== '-' && text.startsWith('-')) {
415
+ if (text === '-f' || text === '--file') {
416
+ const next = args[i + 1]
417
+ if (next !== undefined) out.push(next)
418
+ skipNext = true
419
+ scriptGiven = true
420
+ } else if (text === '-e' || text === '--expression') {
421
+ skipNext = true
422
+ scriptGiven = true
423
+ } else if (!text.startsWith('--') && (text.includes('e') || text.includes('f'))) {
424
+ scriptGiven = true
425
+ }
426
+ continue
427
+ }
428
+ started = true
429
+ if (!scriptGiven) {
430
+ scriptGiven = true
431
+ continue
432
+ }
433
+ out.push(arg)
434
+ }
435
+ return out
436
+ }
437
+
438
+ function findOperands(args: Args): ShellWord[] {
439
+ const out: ShellWord[] = []
440
+ const takesPath = new Set([
441
+ '-newer',
442
+ '-anewer',
443
+ '-cnewer',
444
+ '-mnewer',
445
+ '-samefile',
446
+ '-path',
447
+ '-wholename',
448
+ '-ilname',
449
+ '-lname',
450
+ '-ipath',
451
+ '-iwholename',
452
+ ])
453
+ let expressionStarted = false
454
+ let afterDashes = false
455
+ for (let i = 0; i < args.length; i += 1) {
456
+ const arg = args[i] as ShellWord
457
+ if (!arg.value) continue
458
+ if (afterDashes) {
459
+ out.push(arg)
460
+ continue
461
+ }
462
+ if (arg.value === '--') {
463
+ afterDashes = true
464
+ continue
465
+ }
466
+ if (arg.value.startsWith('-')) {
467
+ if (['-H', '-L', '-P'].includes(arg.value)) continue
468
+ expressionStarted = true
469
+ if (takesPath.has(arg.value) || /^-newer[acmBt][acmtB]$/.test(arg.value)) {
470
+ const next = args[i + 1]
471
+ if (next?.value) {
472
+ out.push(next)
473
+ i += 1
474
+ }
475
+ }
476
+ continue
477
+ }
478
+ if (!expressionStarted) out.push(arg)
479
+ }
480
+ return out.length > 0 ? out : [word('.')]
481
+ }
482
+
483
+ function gitOperands(args: Args): ShellWord[] {
484
+ const valuedGlobals = new Set([
485
+ '-C',
486
+ '-c',
487
+ '--git-dir',
488
+ '--work-tree',
489
+ '--namespace',
490
+ '--super-prefix',
491
+ '--config-env',
492
+ '--attr-source',
493
+ '--shallow-file',
494
+ ])
495
+ let index = 0
496
+ let base = ''
497
+ const join = (value: string): string =>
498
+ base === '' ? value : `${base.replace(/\/+$/, '')}/${value}`
499
+ const absolute = (value: string): boolean => value.startsWith('/') || value.startsWith('~')
500
+ const setBase = (value: string): void => {
501
+ base = absolute(value) ? value : join(value)
502
+ }
503
+ while (index < args.length && (args[index] as ShellWord).value.startsWith('-')) {
504
+ const flag = (args[index] as ShellWord).value
505
+ index += 1
506
+ if (valuedGlobals.has(flag) && index < args.length) {
507
+ const value = (args[index] as ShellWord).value
508
+ if (flag === '-C' || flag === '--work-tree') setBase(value)
509
+ index += 1
510
+ } else if (flag.startsWith('--work-tree=')) {
511
+ setBase(flag.slice('--work-tree='.length))
512
+ }
513
+ }
514
+ const sub = args[index]?.value
515
+ const rest = args.slice(index + 1)
516
+ const place = (value: string): string => (base === '' || absolute(value) ? value : join(value))
517
+ if (sub === 'grep') {
518
+ const files = patternThenFiles(
519
+ set(
520
+ '-e',
521
+ '--regexp',
522
+ '-f',
523
+ '--file',
524
+ '-A',
525
+ '--after-context',
526
+ '-B',
527
+ '--before-context',
528
+ '-C',
529
+ '--context',
530
+ '-m',
531
+ '--max-count',
532
+ '--max-depth',
533
+ '--threads',
534
+ ),
535
+ )(rest)
536
+ return files.length > 0
537
+ ? files.map((f) => word(place(f.value)))
538
+ : [word(base === '' ? '.' : base)]
539
+ }
540
+ if (sub !== 'diff') return []
541
+ const out: ShellWord[] = []
542
+ let afterDashes = false
543
+ for (const arg of rest) {
544
+ if (!afterDashes && arg.value === '--') afterDashes = true
545
+ else if (afterDashes || arg.value === '-' || !arg.value.startsWith('-')) {
546
+ const path = place(arg.value)
547
+ if (!isHarmlessTarget(path)) out.push(word(path))
548
+ }
549
+ }
550
+ return out
551
+ }
552
+
553
+ function manOperands(args: Args): ShellWord[] {
554
+ const local = args.some(
555
+ (a) => a.value === '--local-file' || (/^-[^-]/.test(a.value) && a.value.includes('l')),
556
+ )
557
+ const out: ShellWord[] = []
558
+ args.forEach((arg, i) => {
559
+ const previous = args[i - 1]?.value
560
+ if (i > 0 && (previous === '-C' || previous === '--config-file')) {
561
+ out.push(arg)
562
+ return
563
+ }
564
+ const text = arg.value
565
+ const found: string[] = []
566
+ if (!text.startsWith('-')) found.push(text)
567
+ else {
568
+ if (text.includes('=')) found.push(text.slice(text.indexOf('=') + 1))
569
+ if (text.length > 2 && !text.startsWith('--')) found.push(text.slice(2))
570
+ const cut = text.search(/[\\/~]/)
571
+ if (cut > 0) found.push(text.slice(cut))
572
+ }
573
+ for (const value of new Set(found)) {
574
+ if (
575
+ value !== '' &&
576
+ (local || /[\\/]/.test(value) || value.startsWith('~') || value.startsWith('.'))
577
+ ) {
578
+ out.push(word(value))
579
+ }
580
+ }
581
+ })
582
+ return out
583
+ }
584
+
585
+ function jqOperands(args: Args): ShellWord[] {
586
+ const valued = set(
587
+ '-e',
588
+ '--expression',
589
+ '--arg',
590
+ '--argjson',
591
+ '--args',
592
+ '--jsonargs',
593
+ '-L',
594
+ '--library-path',
595
+ '--indent',
596
+ '--tab',
597
+ )
598
+ const out: ShellWord[] = []
599
+ let afterDashes = false
600
+ let filterGiven = false
601
+ for (let i = 0; i < args.length; i += 1) {
602
+ const arg = args[i] as ShellWord
603
+ const text = arg.value
604
+ if (!afterDashes && text === '--') {
605
+ afterDashes = true
606
+ continue
607
+ }
608
+ if (!afterDashes && text.startsWith('-')) {
609
+ const eq = text.indexOf('=')
610
+ const name = eq >= 0 ? text.slice(0, eq) : text
611
+ if (name === '-e' || name === '--expression') filterGiven = true
612
+ if (name === '-f' || name === '--from-file') {
613
+ filterGiven = true
614
+ if (eq >= 0) out.push(word(text.slice(eq + 1)))
615
+ else if (args[i + 1] !== undefined) {
616
+ out.push(args[i + 1] as ShellWord)
617
+ i += 1
618
+ }
619
+ continue
620
+ }
621
+ if (name === '--slurpfile' || name === '--rawfile') {
622
+ const file = args[i + 2]
623
+ if (file !== undefined) out.push(file)
624
+ i += 2
625
+ continue
626
+ }
627
+ if (valued.has(name) && eq < 0) i += 1
628
+ continue
629
+ }
630
+ if (!filterGiven) {
631
+ filterGiven = true
632
+ continue
633
+ }
634
+ out.push(arg)
635
+ }
636
+ return out
637
+ }
638
+
639
+ const trOperands = (args: Args): ShellWord[] => {
640
+ const deleting = args.some(
641
+ (a) =>
642
+ a.value === '-d' ||
643
+ a.value === '--delete' ||
644
+ (a.value.startsWith('-') && a.value.includes('d')),
645
+ )
646
+ return operands(args).slice(deleting ? 1 : 2)
647
+ }
648
+
649
+ const teeOperands = (args: Args): ShellWord[] =>
650
+ operands(args).filter((a) => !isHarmlessTarget(a.value))
651
+
652
+ const cdOperands = (args: Args): ShellWord[] => {
653
+ const found = operands(args)
654
+ if (found.length === 0) return args[args.length - 1]?.value === '-' ? [word('-')] : [word('~')]
655
+ return [found[0] as ShellWord]
656
+ }
657
+
658
+ const lsOperands = (args: Args): ShellWord[] => {
659
+ const found = operands(args)
660
+ return found.length > 0 ? found : [word('.')]
661
+ }
662
+
663
+ const table = (
664
+ booleanShort: string,
665
+ valuedShort: string,
666
+ booleanLong: ReadonlySet<string>,
667
+ valuedLong: ReadonlySet<string>,
668
+ attachedShort?: string,
669
+ attachedLong?: ReadonlySet<string>,
670
+ ): FlagTable => ({
671
+ booleanShort,
672
+ valuedShort,
673
+ booleanLong,
674
+ valuedLong,
675
+ attachedShort,
676
+ attachedLong,
677
+ })
678
+
679
+ const EXTRACTORS: Readonly<Record<string, (args: Args) => ShellWord[]>> = {
680
+ cd: cdOperands,
681
+ ls: lsOperands,
682
+ find: findOperands,
683
+ mkdir: operands,
684
+ touch: operands,
685
+ rm: operands,
686
+ rmdir: operands,
687
+ mv: operands,
688
+ cp: operands,
689
+ cat: operands,
690
+ head: operands,
691
+ tail: operands,
692
+ sort: operands,
693
+ uniq: operands,
694
+ wc: operands,
695
+ cut: operandsByTable(
696
+ table(
697
+ 'nswz',
698
+ 'bcdf',
699
+ set('--complement', '--only-delimited', '--zero-terminated'),
700
+ set('--bytes', '--characters', '--delimiter', '--fields', '--output-delimiter'),
701
+ ),
702
+ ),
703
+ paste: operandsByTable(
704
+ table('sz', 'd', set('--serial', '--zero-terminated'), set('--delimiters')),
705
+ ),
706
+ column: operandsByTable(
707
+ table(
708
+ 'txdemLJhV',
709
+ 'cosNOClEHRTWrip',
710
+ set(
711
+ '--table',
712
+ '--fillrows',
713
+ '--table-noheadings',
714
+ '--table-header-repeat',
715
+ '--table-maxout',
716
+ '--keep-empty-lines',
717
+ '--json',
718
+ ),
719
+ set(
720
+ '--separator',
721
+ '--output-separator',
722
+ '--output-width',
723
+ '--table-name',
724
+ '--table-order',
725
+ '--table-column',
726
+ '--table-columns',
727
+ '--table-columns-limit',
728
+ '--table-noextreme',
729
+ '--table-hide',
730
+ '--table-right',
731
+ '--table-truncate',
732
+ '--table-wrap',
733
+ '--tree',
734
+ '--tree-id',
735
+ '--tree-parent',
736
+ ),
737
+ ),
738
+ ),
739
+ file: operands,
740
+ stat: operands,
741
+ diff: operands,
742
+ awk: awkOperands,
743
+ gawk: awkOperands,
744
+ mawk: awkOperands,
745
+ nawk: awkOperands,
746
+ strings: operands,
747
+ hexdump: operands,
748
+ od: operands,
749
+ base64: operands,
750
+ nl: operands,
751
+ sha256sum: operands,
752
+ sha1sum: operands,
753
+ md5sum: operands,
754
+ tee: teeOperands,
755
+ tr: trOperands,
756
+ grep: grepOperands,
757
+ egrep: grepOperands,
758
+ fgrep: grepOperands,
759
+ tac: operandsByTable(table('br', 's', set('--before', '--regex'), set('--separator'))),
760
+ rev: operands,
761
+ fold: operandsByTable(table('bs', 'w', set('--bytes', '--spaces'), set('--width'))),
762
+ expand: operandsByTable(table('i', 't', set('--initial'), set('--tabs'))),
763
+ unexpand: operandsByTable(table('af', 't', set('--all', '--first-only'), set('--tabs'))),
764
+ fmt: operandsByTable(
765
+ table(
766
+ 'csumn',
767
+ 'wgdl',
768
+ set('--crown-margin', '--split-only', '--tagged-paragraph', '--uniform-spacing'),
769
+ set('--width', '--goal', '--prefix'),
770
+ ),
771
+ ),
772
+ comm: operandsByTable(
773
+ table(
774
+ '123iz',
775
+ '',
776
+ set('--check-order', '--nocheck-order', '--total', '--zero-terminated'),
777
+ set('--output-delimiter'),
778
+ ),
779
+ ),
780
+ cmp: operandsByTable(
781
+ table(
782
+ 'blsvxzh',
783
+ '',
784
+ set('--print-bytes', '--verbose', '--quiet', '--silent'),
785
+ set('--ignore-initial', '--bytes'),
786
+ ),
787
+ ),
788
+ pr: operandsByTable(
789
+ table(
790
+ 'acdFfJmprtTv',
791
+ 'hlwWoND',
792
+ set(
793
+ '--across',
794
+ '--show-control-chars',
795
+ '--double-space',
796
+ '--form-feed',
797
+ '--join-lines',
798
+ '--merge',
799
+ '--no-file-warnings',
800
+ '--omit-header',
801
+ '--omit-pagination',
802
+ '--show-nonprinting',
803
+ ),
804
+ set(
805
+ '--header',
806
+ '--length',
807
+ '--width',
808
+ '--page-width',
809
+ '--indent',
810
+ '--first-line-number',
811
+ '--date-format',
812
+ '--columns',
813
+ ),
814
+ 'einsS',
815
+ set('--expand-tabs', '--output-tabs', '--number-lines', '--separator', '--sep-string'),
816
+ ),
817
+ ),
818
+ numfmt: operandsByTable(
819
+ table(
820
+ 'z',
821
+ 'd',
822
+ set('--grouping', '--zero-terminated', '--debug'),
823
+ set(
824
+ '--from',
825
+ '--to',
826
+ '--from-unit',
827
+ '--to-unit',
828
+ '--format',
829
+ '--padding',
830
+ '--delimiter',
831
+ '--field',
832
+ '--round',
833
+ '--suffix',
834
+ '--invalid',
835
+ ),
836
+ undefined,
837
+ set('--header'),
838
+ ),
839
+ ),
840
+ tsort: operands,
841
+ man: manOperands,
842
+ rg: rgOperands,
843
+ sed: sedOperands,
844
+ jq: jqOperands,
845
+ git: gitOperands,
846
+ }
847
+
848
+ /** Commands that create or change what their operands name. `sed` is added per call when it edits in place. */
849
+ const WRITERS = new Set(['mkdir', 'touch', 'rm', 'rmdir', 'mv', 'cp', 'tee'])
850
+
851
+ const CD_PROGRAMS = new Set(['cd', 'pushd', 'popd', 'chdir'])
852
+
853
+ /** `sed -i`, `-i.bak`, `-ni`, `--in-place[=SUFFIX]`: the script's output replaces the file. */
854
+ function editsInPlace(args: Args): boolean {
855
+ return args.some((a) => {
856
+ const text = a.value
857
+ if (text === '--') return false
858
+ if (text.startsWith('--')) return text === '--in-place' || text.startsWith('--in-place=')
859
+ return /^-[A-Za-z]*i/.test(text)
860
+ })
861
+ }
862
+
863
+ /**
864
+ * The paths `reading` names: positional arguments of the commands in the
865
+ * table, and the targets of its redirections.
866
+ */
867
+ export function shellPathAccess(reading: ShellReading): ShellPathAccess {
868
+ const uses: ShellPathUse[] = []
869
+ let changesDirectory = false
870
+ for (const { program, args } of reading.invocations) {
871
+ if (CD_PROGRAMS.has(program)) changesDirectory = true
872
+ const extract = EXTRACTORS[program]
873
+ if (extract === undefined) continue
874
+ const access: PathAccess =
875
+ WRITERS.has(program) || (program === 'sed' && editsInPlace(args)) ? 'write' : 'read'
876
+ for (const arg of extract(args)) {
877
+ if (arg.value === '' || isHarmlessTarget(arg.value)) continue
878
+ uses.push({
879
+ path: arg.value,
880
+ access,
881
+ via: program,
882
+ unresolved: expandsAtRuntime(arg),
883
+ })
884
+ }
885
+ }
886
+ for (const redirection of reading.redirections) {
887
+ const target = redirection.target
888
+ if (target.value === '' || isHarmlessTarget(target.value)) continue
889
+ switch (redirection.operator) {
890
+ case '>':
891
+ case '>>':
892
+ case '>|':
893
+ case '&>':
894
+ case '&>>':
895
+ case '<>':
896
+ uses.push({
897
+ path: target.value,
898
+ access: 'write',
899
+ via: 'redirect',
900
+ unresolved: expandsAtRuntime(target),
901
+ })
902
+ break
903
+ case '>&':
904
+ // `>&2` and `>&-` duplicate or close a descriptor; any other word is a file.
905
+ if (!/^(?:\d+-?|-)$/.test(target.value)) {
906
+ uses.push({
907
+ path: target.value,
908
+ access: 'write',
909
+ via: 'redirect',
910
+ unresolved: expandsAtRuntime(target),
911
+ })
912
+ }
913
+ break
914
+ case '<':
915
+ uses.push({
916
+ path: target.value,
917
+ access: 'read',
918
+ via: 'redirect',
919
+ unresolved: expandsAtRuntime(target),
920
+ })
921
+ break
922
+ default:
923
+ break
924
+ }
925
+ }
926
+ return { uses, changesDirectory }
927
+ }