@oscharko-dev/keiko 0.1.0-beta.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 (450) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +7 -0
  3. package/README.md +621 -0
  4. package/TRADEMARKS.md +41 -0
  5. package/dist/audit/aggregate.d.ts +5 -0
  6. package/dist/audit/aggregate.js +25 -0
  7. package/dist/audit/build.d.ts +2 -0
  8. package/dist/audit/build.js +224 -0
  9. package/dist/audit/errors.d.ts +25 -0
  10. package/dist/audit/errors.js +39 -0
  11. package/dist/audit/index-api.d.ts +14 -0
  12. package/dist/audit/index-api.js +131 -0
  13. package/dist/audit/index.d.ts +12 -0
  14. package/dist/audit/index.js +17 -0
  15. package/dist/audit/persist.d.ts +8 -0
  16. package/dist/audit/persist.js +40 -0
  17. package/dist/audit/redaction.d.ts +3 -0
  18. package/dist/audit/redaction.js +61 -0
  19. package/dist/audit/report.d.ts +18 -0
  20. package/dist/audit/report.js +50 -0
  21. package/dist/audit/retention.d.ts +3 -0
  22. package/dist/audit/retention.js +95 -0
  23. package/dist/audit/runid.d.ts +1 -0
  24. package/dist/audit/runid.js +29 -0
  25. package/dist/audit/side-file.d.ts +12 -0
  26. package/dist/audit/side-file.js +82 -0
  27. package/dist/audit/store.d.ts +12 -0
  28. package/dist/audit/store.js +198 -0
  29. package/dist/audit/types.d.ts +188 -0
  30. package/dist/audit/types.js +8 -0
  31. package/dist/audit/workflow-evidence.d.ts +27 -0
  32. package/dist/audit/workflow-evidence.js +145 -0
  33. package/dist/cli/context.d.ts +2 -0
  34. package/dist/cli/context.js +102 -0
  35. package/dist/cli/evaluate.d.ts +7 -0
  36. package/dist/cli/evaluate.js +207 -0
  37. package/dist/cli/evidence.d.ts +8 -0
  38. package/dist/cli/evidence.js +88 -0
  39. package/dist/cli/gateway-config.d.ts +10 -0
  40. package/dist/cli/gateway-config.js +12 -0
  41. package/dist/cli/gen-tests.d.ts +7 -0
  42. package/dist/cli/gen-tests.js +208 -0
  43. package/dist/cli/index.d.ts +2 -0
  44. package/dist/cli/index.js +14 -0
  45. package/dist/cli/investigate.d.ts +8 -0
  46. package/dist/cli/investigate.js +242 -0
  47. package/dist/cli/models.d.ts +3 -0
  48. package/dist/cli/models.js +64 -0
  49. package/dist/cli/run.d.ts +7 -0
  50. package/dist/cli/run.js +187 -0
  51. package/dist/cli/runner.d.ts +6 -0
  52. package/dist/cli/runner.js +83 -0
  53. package/dist/cli/ui.d.ts +31 -0
  54. package/dist/cli/ui.js +240 -0
  55. package/dist/cli/verify.d.ts +2 -0
  56. package/dist/cli/verify.js +103 -0
  57. package/dist/evaluations/fixtures/bug-investigation/happy-path.d.ts +2 -0
  58. package/dist/evaluations/fixtures/bug-investigation/happy-path.js +66 -0
  59. package/dist/evaluations/fixtures/bug-investigation/investigation-only.d.ts +2 -0
  60. package/dist/evaluations/fixtures/bug-investigation/investigation-only.js +39 -0
  61. package/dist/evaluations/fixtures/bug-investigation/unsafe-action.d.ts +2 -0
  62. package/dist/evaluations/fixtures/bug-investigation/unsafe-action.js +37 -0
  63. package/dist/evaluations/fixtures/index.d.ts +7 -0
  64. package/dist/evaluations/fixtures/index.js +35 -0
  65. package/dist/evaluations/fixtures/support.d.ts +5 -0
  66. package/dist/evaluations/fixtures/support.js +42 -0
  67. package/dist/evaluations/fixtures/unit-tests/happy-path.d.ts +2 -0
  68. package/dist/evaluations/fixtures/unit-tests/happy-path.js +40 -0
  69. package/dist/evaluations/fixtures/unit-tests/retry-then-accept.d.ts +2 -0
  70. package/dist/evaluations/fixtures/unit-tests/retry-then-accept.js +39 -0
  71. package/dist/evaluations/fixtures/unit-tests/unsafe-action.d.ts +2 -0
  72. package/dist/evaluations/fixtures/unit-tests/unsafe-action.js +32 -0
  73. package/dist/evaluations/index.d.ts +12 -0
  74. package/dist/evaluations/index.js +12 -0
  75. package/dist/evaluations/manifest-check.d.ts +1 -0
  76. package/dist/evaluations/manifest-check.js +48 -0
  77. package/dist/evaluations/model-provider.d.ts +12 -0
  78. package/dist/evaluations/model-provider.js +26 -0
  79. package/dist/evaluations/render.d.ts +2 -0
  80. package/dist/evaluations/render.js +59 -0
  81. package/dist/evaluations/runner-support.d.ts +27 -0
  82. package/dist/evaluations/runner-support.js +163 -0
  83. package/dist/evaluations/runner.d.ts +20 -0
  84. package/dist/evaluations/runner.js +174 -0
  85. package/dist/evaluations/scorer.d.ts +14 -0
  86. package/dist/evaluations/scorer.js +131 -0
  87. package/dist/evaluations/scripted-model.d.ts +6 -0
  88. package/dist/evaluations/scripted-model.js +26 -0
  89. package/dist/evaluations/surface-parity.d.ts +2 -0
  90. package/dist/evaluations/surface-parity.js +184 -0
  91. package/dist/evaluations/types.d.ts +74 -0
  92. package/dist/evaluations/types.js +16 -0
  93. package/dist/gateway/capabilities.d.ts +11 -0
  94. package/dist/gateway/capabilities.data.d.ts +2 -0
  95. package/dist/gateway/capabilities.data.js +203 -0
  96. package/dist/gateway/capabilities.js +41 -0
  97. package/dist/gateway/config.d.ts +15 -0
  98. package/dist/gateway/config.js +154 -0
  99. package/dist/gateway/errors.d.ts +72 -0
  100. package/dist/gateway/errors.js +82 -0
  101. package/dist/gateway/gateway.d.ts +19 -0
  102. package/dist/gateway/gateway.js +94 -0
  103. package/dist/gateway/index.d.ts +10 -0
  104. package/dist/gateway/index.js +11 -0
  105. package/dist/gateway/model-selection.d.ts +9 -0
  106. package/dist/gateway/model-selection.js +36 -0
  107. package/dist/gateway/normalize.d.ts +7 -0
  108. package/dist/gateway/normalize.js +93 -0
  109. package/dist/gateway/openai-adapter.d.ts +20 -0
  110. package/dist/gateway/openai-adapter.js +263 -0
  111. package/dist/gateway/redaction.d.ts +1 -0
  112. package/dist/gateway/redaction.js +51 -0
  113. package/dist/gateway/resilience.d.ts +24 -0
  114. package/dist/gateway/resilience.js +166 -0
  115. package/dist/gateway/types.d.ts +108 -0
  116. package/dist/gateway/types.js +2 -0
  117. package/dist/harness/adapters.d.ts +23 -0
  118. package/dist/harness/adapters.js +38 -0
  119. package/dist/harness/context.d.ts +33 -0
  120. package/dist/harness/context.js +21 -0
  121. package/dist/harness/emitter.d.ts +15 -0
  122. package/dist/harness/emitter.js +72 -0
  123. package/dist/harness/errors.d.ts +21 -0
  124. package/dist/harness/errors.js +39 -0
  125. package/dist/harness/executor.d.ts +3 -0
  126. package/dist/harness/executor.js +211 -0
  127. package/dist/harness/fingerprint.d.ts +6 -0
  128. package/dist/harness/fingerprint.js +43 -0
  129. package/dist/harness/index.d.ts +9 -0
  130. package/dist/harness/index.js +13 -0
  131. package/dist/harness/loop.d.ts +3 -0
  132. package/dist/harness/loop.js +159 -0
  133. package/dist/harness/patcher.d.ts +4 -0
  134. package/dist/harness/patcher.js +49 -0
  135. package/dist/harness/planner.d.ts +3 -0
  136. package/dist/harness/planner.js +21 -0
  137. package/dist/harness/ports.d.ts +61 -0
  138. package/dist/harness/ports.js +4 -0
  139. package/dist/harness/session.d.ts +25 -0
  140. package/dist/harness/session.js +116 -0
  141. package/dist/harness/sinks.d.ts +30 -0
  142. package/dist/harness/sinks.js +72 -0
  143. package/dist/harness/tasks/explain-plan.d.ts +3 -0
  144. package/dist/harness/tasks/explain-plan.js +29 -0
  145. package/dist/harness/tasks/generate-unit-tests.d.ts +3 -0
  146. package/dist/harness/tasks/generate-unit-tests.js +28 -0
  147. package/dist/harness/tasks/investigate-bug.d.ts +3 -0
  148. package/dist/harness/tasks/investigate-bug.js +31 -0
  149. package/dist/harness/tasks/policy.d.ts +11 -0
  150. package/dist/harness/tasks/policy.js +22 -0
  151. package/dist/harness/tasks/verify.d.ts +3 -0
  152. package/dist/harness/tasks/verify.js +16 -0
  153. package/dist/harness/types.d.ts +270 -0
  154. package/dist/harness/types.js +33 -0
  155. package/dist/index.d.ts +11 -0
  156. package/dist/index.js +36 -0
  157. package/dist/sdk/index.d.ts +9 -0
  158. package/dist/sdk/index.js +37 -0
  159. package/dist/sdk/run-agent.d.ts +16 -0
  160. package/dist/sdk/run-agent.js +56 -0
  161. package/dist/tools/browser/cdp-client.d.ts +35 -0
  162. package/dist/tools/browser/cdp-client.js +218 -0
  163. package/dist/tools/browser/errors.d.ts +25 -0
  164. package/dist/tools/browser/errors.js +55 -0
  165. package/dist/tools/browser/index.d.ts +5 -0
  166. package/dist/tools/browser/index.js +6 -0
  167. package/dist/tools/browser/session.d.ts +44 -0
  168. package/dist/tools/browser/session.js +748 -0
  169. package/dist/tools/browser/types.d.ts +48 -0
  170. package/dist/tools/browser/types.js +2 -0
  171. package/dist/tools/browser/validators.d.ts +5 -0
  172. package/dist/tools/browser/validators.js +97 -0
  173. package/dist/tools/errors.d.ts +59 -0
  174. package/dist/tools/errors.js +94 -0
  175. package/dist/tools/exec.d.ts +42 -0
  176. package/dist/tools/exec.js +327 -0
  177. package/dist/tools/index.d.ts +11 -0
  178. package/dist/tools/index.js +14 -0
  179. package/dist/tools/patch-content.d.ts +10 -0
  180. package/dist/tools/patch-content.js +126 -0
  181. package/dist/tools/patch-normalize.d.ts +1 -0
  182. package/dist/tools/patch-normalize.js +80 -0
  183. package/dist/tools/patch-parse.d.ts +8 -0
  184. package/dist/tools/patch-parse.js +201 -0
  185. package/dist/tools/patch.d.ts +18 -0
  186. package/dist/tools/patch.js +403 -0
  187. package/dist/tools/registry.d.ts +36 -0
  188. package/dist/tools/registry.js +231 -0
  189. package/dist/tools/sandbox.d.ts +8 -0
  190. package/dist/tools/sandbox.js +121 -0
  191. package/dist/tools/schemas.d.ts +2 -0
  192. package/dist/tools/schemas.js +51 -0
  193. package/dist/tools/terminal-policy.d.ts +9 -0
  194. package/dist/tools/terminal-policy.js +313 -0
  195. package/dist/tools/types.d.ts +99 -0
  196. package/dist/tools/types.js +103 -0
  197. package/dist/tools/writer.d.ts +7 -0
  198. package/dist/tools/writer.js +20 -0
  199. package/dist/ui/browser.d.ts +10 -0
  200. package/dist/ui/browser.js +231 -0
  201. package/dist/ui/chat-handlers.d.ts +4 -0
  202. package/dist/ui/chat-handlers.js +281 -0
  203. package/dist/ui/csp-hashes.json +17 -0
  204. package/dist/ui/csp.d.ts +2 -0
  205. package/dist/ui/csp.js +66 -0
  206. package/dist/ui/deps.d.ts +34 -0
  207. package/dist/ui/deps.js +137 -0
  208. package/dist/ui/evidence.d.ts +27 -0
  209. package/dist/ui/evidence.js +142 -0
  210. package/dist/ui/files-deny.d.ts +2 -0
  211. package/dist/ui/files-deny.js +12 -0
  212. package/dist/ui/files.d.ts +65 -0
  213. package/dist/ui/files.js +492 -0
  214. package/dist/ui/headers.d.ts +2 -0
  215. package/dist/ui/headers.js +21 -0
  216. package/dist/ui/host-check.d.ts +2 -0
  217. package/dist/ui/host-check.js +58 -0
  218. package/dist/ui/index.d.ts +20 -0
  219. package/dist/ui/index.js +23 -0
  220. package/dist/ui/load-csp.d.ts +1 -0
  221. package/dist/ui/load-csp.js +28 -0
  222. package/dist/ui/read-handlers.d.ts +8 -0
  223. package/dist/ui/read-handlers.js +247 -0
  224. package/dist/ui/routes.d.ts +36 -0
  225. package/dist/ui/routes.js +129 -0
  226. package/dist/ui/run-engine.d.ts +20 -0
  227. package/dist/ui/run-engine.js +345 -0
  228. package/dist/ui/run-handlers.d.ts +8 -0
  229. package/dist/ui/run-handlers.js +431 -0
  230. package/dist/ui/run-request.d.ts +13 -0
  231. package/dist/ui/run-request.js +219 -0
  232. package/dist/ui/runs.d.ts +43 -0
  233. package/dist/ui/runs.js +92 -0
  234. package/dist/ui/server.d.ts +11 -0
  235. package/dist/ui/server.js +143 -0
  236. package/dist/ui/sink.d.ts +27 -0
  237. package/dist/ui/sink.js +80 -0
  238. package/dist/ui/sse.d.ts +7 -0
  239. package/dist/ui/sse.js +27 -0
  240. package/dist/ui/static/404.html +1 -0
  241. package/dist/ui/static/_next/static/ca-A01hy9W98aRvMZKdAw/_buildManifest.js +1 -0
  242. package/dist/ui/static/_next/static/ca-A01hy9W98aRvMZKdAw/_ssgManifest.js +1 -0
  243. package/dist/ui/static/_next/static/chunks/255-d47fd57964443afe.js +1 -0
  244. package/dist/ui/static/_next/static/chunks/4-be1fef693af8e088.js +1 -0
  245. package/dist/ui/static/_next/static/chunks/4bd1b696-c023c6e3521b1417.js +1 -0
  246. package/dist/ui/static/_next/static/chunks/app/_not-found/page-75825b09bcecad97.js +1 -0
  247. package/dist/ui/static/_next/static/chunks/app/launch/page-9c86a13c29884245.js +1 -0
  248. package/dist/ui/static/_next/static/chunks/app/layout-bdea63fe87947d50.js +1 -0
  249. package/dist/ui/static/_next/static/chunks/app/page-4168c12c68b7a853.js +1 -0
  250. package/dist/ui/static/_next/static/chunks/framework-a6e0b7e30f98059a.js +1 -0
  251. package/dist/ui/static/_next/static/chunks/main-778a50aebff02192.js +1 -0
  252. package/dist/ui/static/_next/static/chunks/main-app-30679af7240d63e9.js +1 -0
  253. package/dist/ui/static/_next/static/chunks/pages/_app-7d307437aca18ad4.js +1 -0
  254. package/dist/ui/static/_next/static/chunks/pages/_error-cb2a52f75f2162e2.js +1 -0
  255. package/dist/ui/static/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
  256. package/dist/ui/static/_next/static/chunks/webpack-4a462cecab786e93.js +1 -0
  257. package/dist/ui/static/_next/static/css/be7cb54d5c5673b6.css +1 -0
  258. package/dist/ui/static/assets/editors/goland.svg +35 -0
  259. package/dist/ui/static/assets/editors/intellij.svg +39 -0
  260. package/dist/ui/static/assets/editors/pycharm.svg +58 -0
  261. package/dist/ui/static/assets/editors/rustrover.svg +19 -0
  262. package/dist/ui/static/assets/editors/vscode.svg +1 -0
  263. package/dist/ui/static/assets/editors/webstorm.svg +21 -0
  264. package/dist/ui/static/assets/icons/anthropic.svg +1 -0
  265. package/dist/ui/static/assets/icons/brave.svg +1 -0
  266. package/dist/ui/static/assets/icons/css3.svg +1 -0
  267. package/dist/ui/static/assets/icons/docker.svg +1 -0
  268. package/dist/ui/static/assets/icons/git.svg +1 -0
  269. package/dist/ui/static/assets/icons/github.svg +1 -0
  270. package/dist/ui/static/assets/icons/go.svg +1 -0
  271. package/dist/ui/static/assets/icons/gradle.svg +1 -0
  272. package/dist/ui/static/assets/icons/grafana.svg +1 -0
  273. package/dist/ui/static/assets/icons/graphql.svg +1 -0
  274. package/dist/ui/static/assets/icons/html5.svg +1 -0
  275. package/dist/ui/static/assets/icons/image.svg +1 -0
  276. package/dist/ui/static/assets/icons/java.svg +1 -0
  277. package/dist/ui/static/assets/icons/javascript.svg +1 -0
  278. package/dist/ui/static/assets/icons/json.svg +1 -0
  279. package/dist/ui/static/assets/icons/kafka.svg +1 -0
  280. package/dist/ui/static/assets/icons/kubernetes.svg +1 -0
  281. package/dist/ui/static/assets/icons/linear.svg +1 -0
  282. package/dist/ui/static/assets/icons/markdown.svg +1 -0
  283. package/dist/ui/static/assets/icons/nginx.svg +1 -0
  284. package/dist/ui/static/assets/icons/nodejs.svg +1 -0
  285. package/dist/ui/static/assets/icons/notion.svg +1 -0
  286. package/dist/ui/static/assets/icons/openai.svg +1 -0
  287. package/dist/ui/static/assets/icons/playwright.svg +1 -0
  288. package/dist/ui/static/assets/icons/postgresql.svg +1 -0
  289. package/dist/ui/static/assets/icons/prometheus.svg +1 -0
  290. package/dist/ui/static/assets/icons/properties.svg +1 -0
  291. package/dist/ui/static/assets/icons/puppeteer.svg +1 -0
  292. package/dist/ui/static/assets/icons/python.svg +1 -0
  293. package/dist/ui/static/assets/icons/react.svg +1 -0
  294. package/dist/ui/static/assets/icons/redis.svg +1 -0
  295. package/dist/ui/static/assets/icons/rust.svg +1 -0
  296. package/dist/ui/static/assets/icons/sentry.svg +1 -0
  297. package/dist/ui/static/assets/icons/slack.svg +1 -0
  298. package/dist/ui/static/assets/icons/spring.svg +1 -0
  299. package/dist/ui/static/assets/icons/typescript.svg +1 -0
  300. package/dist/ui/static/assets/icons/upstash.svg +1 -0
  301. package/dist/ui/static/assets/icons/yaml.svg +1 -0
  302. package/dist/ui/static/assets/keiko-logo.svg +10 -0
  303. package/dist/ui/static/index.html +1 -0
  304. package/dist/ui/static/index.txt +19 -0
  305. package/dist/ui/static/keiko-logo.svg +10 -0
  306. package/dist/ui/static/launch.html +1 -0
  307. package/dist/ui/static/launch.txt +19 -0
  308. package/dist/ui/static.d.ts +3 -0
  309. package/dist/ui/static.js +72 -0
  310. package/dist/ui/store/chats.d.ts +14 -0
  311. package/dist/ui/store/chats.js +110 -0
  312. package/dist/ui/store/db.d.ts +6 -0
  313. package/dist/ui/store/db.js +182 -0
  314. package/dist/ui/store/errors.d.ts +12 -0
  315. package/dist/ui/store/errors.js +30 -0
  316. package/dist/ui/store/index.d.ts +6 -0
  317. package/dist/ui/store/index.js +6 -0
  318. package/dist/ui/store/messages.d.ts +5 -0
  319. package/dist/ui/store/messages.js +137 -0
  320. package/dist/ui/store/paths.d.ts +4 -0
  321. package/dist/ui/store/paths.js +69 -0
  322. package/dist/ui/store/projects.d.ts +7 -0
  323. package/dist/ui/store/projects.js +61 -0
  324. package/dist/ui/store/schema.d.ts +3 -0
  325. package/dist/ui/store/schema.js +77 -0
  326. package/dist/ui/store/types.d.ts +80 -0
  327. package/dist/ui/store/types.js +3 -0
  328. package/dist/ui/store/validation.d.ts +4 -0
  329. package/dist/ui/store/validation.js +72 -0
  330. package/dist/ui/store-handlers.d.ts +16 -0
  331. package/dist/ui/store-handlers.js +465 -0
  332. package/dist/ui/terminal-errors.d.ts +21 -0
  333. package/dist/ui/terminal-errors.js +45 -0
  334. package/dist/ui/terminal-evidence.d.ts +20 -0
  335. package/dist/ui/terminal-evidence.js +65 -0
  336. package/dist/ui/terminal-routes.d.ts +9 -0
  337. package/dist/ui/terminal-routes.js +219 -0
  338. package/dist/ui/terminal.d.ts +67 -0
  339. package/dist/ui/terminal.js +835 -0
  340. package/dist/verification/classify.d.ts +10 -0
  341. package/dist/verification/classify.js +53 -0
  342. package/dist/verification/detect.d.ts +4 -0
  343. package/dist/verification/detect.js +81 -0
  344. package/dist/verification/errors.d.ts +11 -0
  345. package/dist/verification/errors.js +21 -0
  346. package/dist/verification/index.d.ts +17 -0
  347. package/dist/verification/index.js +13 -0
  348. package/dist/verification/limits.d.ts +3 -0
  349. package/dist/verification/limits.js +40 -0
  350. package/dist/verification/monitor.d.ts +4 -0
  351. package/dist/verification/monitor.js +58 -0
  352. package/dist/verification/orchestrator.d.ts +16 -0
  353. package/dist/verification/orchestrator.js +363 -0
  354. package/dist/verification/plan.d.ts +9 -0
  355. package/dist/verification/plan.js +125 -0
  356. package/dist/verification/summary.d.ts +40 -0
  357. package/dist/verification/summary.js +67 -0
  358. package/dist/verification/types.d.ts +63 -0
  359. package/dist/verification/types.js +13 -0
  360. package/dist/workflows/bug-investigation/context.d.ts +7 -0
  361. package/dist/workflows/bug-investigation/context.js +119 -0
  362. package/dist/workflows/bug-investigation/descriptor.d.ts +3 -0
  363. package/dist/workflows/bug-investigation/descriptor.js +46 -0
  364. package/dist/workflows/bug-investigation/emit.d.ts +12 -0
  365. package/dist/workflows/bug-investigation/emit.js +35 -0
  366. package/dist/workflows/bug-investigation/events.d.ts +81 -0
  367. package/dist/workflows/bug-investigation/events.js +9 -0
  368. package/dist/workflows/bug-investigation/failure-parse.d.ts +3 -0
  369. package/dist/workflows/bug-investigation/failure-parse.js +154 -0
  370. package/dist/workflows/bug-investigation/guard.d.ts +2 -0
  371. package/dist/workflows/bug-investigation/guard.js +69 -0
  372. package/dist/workflows/bug-investigation/index.d.ts +7 -0
  373. package/dist/workflows/bug-investigation/index.js +13 -0
  374. package/dist/workflows/bug-investigation/internal.d.ts +37 -0
  375. package/dist/workflows/bug-investigation/internal.js +64 -0
  376. package/dist/workflows/bug-investigation/model-loop.d.ts +4 -0
  377. package/dist/workflows/bug-investigation/model-loop.js +223 -0
  378. package/dist/workflows/bug-investigation/parse.d.ts +3 -0
  379. package/dist/workflows/bug-investigation/parse.js +123 -0
  380. package/dist/workflows/bug-investigation/prompt.d.ts +4 -0
  381. package/dist/workflows/bug-investigation/prompt.js +107 -0
  382. package/dist/workflows/bug-investigation/report.d.ts +23 -0
  383. package/dist/workflows/bug-investigation/report.js +151 -0
  384. package/dist/workflows/bug-investigation/stages.d.ts +13 -0
  385. package/dist/workflows/bug-investigation/stages.js +242 -0
  386. package/dist/workflows/bug-investigation/types.d.ts +91 -0
  387. package/dist/workflows/bug-investigation/types.js +14 -0
  388. package/dist/workflows/bug-investigation/verify-stage.d.ts +10 -0
  389. package/dist/workflows/bug-investigation/verify-stage.js +91 -0
  390. package/dist/workflows/bug-investigation/workflow.d.ts +2 -0
  391. package/dist/workflows/bug-investigation/workflow.js +74 -0
  392. package/dist/workflows/descriptor.d.ts +20 -0
  393. package/dist/workflows/descriptor.js +8 -0
  394. package/dist/workflows/index.d.ts +3 -0
  395. package/dist/workflows/index.js +2 -0
  396. package/dist/workflows/unit-tests/context.d.ts +7 -0
  397. package/dist/workflows/unit-tests/context.js +129 -0
  398. package/dist/workflows/unit-tests/conventions.d.ts +4 -0
  399. package/dist/workflows/unit-tests/conventions.js +87 -0
  400. package/dist/workflows/unit-tests/descriptor.d.ts +4 -0
  401. package/dist/workflows/unit-tests/descriptor.js +43 -0
  402. package/dist/workflows/unit-tests/emit.d.ts +12 -0
  403. package/dist/workflows/unit-tests/emit.js +35 -0
  404. package/dist/workflows/unit-tests/events.d.ts +78 -0
  405. package/dist/workflows/unit-tests/events.js +7 -0
  406. package/dist/workflows/unit-tests/index.d.ts +6 -0
  407. package/dist/workflows/unit-tests/index.js +10 -0
  408. package/dist/workflows/unit-tests/internal.d.ts +35 -0
  409. package/dist/workflows/unit-tests/internal.js +43 -0
  410. package/dist/workflows/unit-tests/model-loop.d.ts +4 -0
  411. package/dist/workflows/unit-tests/model-loop.js +95 -0
  412. package/dist/workflows/unit-tests/parse.d.ts +6 -0
  413. package/dist/workflows/unit-tests/parse.js +68 -0
  414. package/dist/workflows/unit-tests/prompt.d.ts +4 -0
  415. package/dist/workflows/unit-tests/prompt.js +71 -0
  416. package/dist/workflows/unit-tests/report.d.ts +21 -0
  417. package/dist/workflows/unit-tests/report.js +90 -0
  418. package/dist/workflows/unit-tests/stages.d.ts +9 -0
  419. package/dist/workflows/unit-tests/stages.js +155 -0
  420. package/dist/workflows/unit-tests/types.d.ts +70 -0
  421. package/dist/workflows/unit-tests/types.js +11 -0
  422. package/dist/workflows/unit-tests/verify-stage.d.ts +9 -0
  423. package/dist/workflows/unit-tests/verify-stage.js +56 -0
  424. package/dist/workflows/unit-tests/workflow.d.ts +2 -0
  425. package/dist/workflows/unit-tests/workflow.js +58 -0
  426. package/dist/workspace/contextPack.d.ts +9 -0
  427. package/dist/workspace/contextPack.js +94 -0
  428. package/dist/workspace/detect.d.ts +3 -0
  429. package/dist/workspace/detect.js +135 -0
  430. package/dist/workspace/discovery.d.ts +9 -0
  431. package/dist/workspace/discovery.js +167 -0
  432. package/dist/workspace/errors.d.ts +39 -0
  433. package/dist/workspace/errors.js +66 -0
  434. package/dist/workspace/fs.d.ts +21 -0
  435. package/dist/workspace/fs.js +36 -0
  436. package/dist/workspace/ignore.d.ts +14 -0
  437. package/dist/workspace/ignore.js +176 -0
  438. package/dist/workspace/index.d.ts +11 -0
  439. package/dist/workspace/index.js +13 -0
  440. package/dist/workspace/paths.d.ts +2 -0
  441. package/dist/workspace/paths.js +38 -0
  442. package/dist/workspace/realpath.d.ts +7 -0
  443. package/dist/workspace/realpath.js +72 -0
  444. package/dist/workspace/retrieval.d.ts +9 -0
  445. package/dist/workspace/retrieval.js +74 -0
  446. package/dist/workspace/summary.d.ts +3 -0
  447. package/dist/workspace/summary.js +54 -0
  448. package/dist/workspace/types.d.ts +103 -0
  449. package/dist/workspace/types.js +27 -0
  450. package/package.json +58 -0
@@ -0,0 +1,48 @@
1
+ export interface NormalizedNavigateUrl {
2
+ readonly url: string;
3
+ readonly host: string;
4
+ readonly originOnly: string;
5
+ readonly port: number;
6
+ }
7
+ export interface BrowserViewportPx {
8
+ readonly width: number;
9
+ readonly height: number;
10
+ }
11
+ export type BrowserSessionStatus = "open" | "closed";
12
+ export interface BrowserSessionMeta {
13
+ readonly sessionId: string;
14
+ readonly cdpPort: number;
15
+ readonly targetId: string;
16
+ readonly status: BrowserSessionStatus;
17
+ readonly createdAt: number;
18
+ }
19
+ export interface BrowserNavigateResult {
20
+ readonly originOnly: string;
21
+ readonly httpStatus: number | null;
22
+ }
23
+ export interface BrowserScreenshotPreview {
24
+ readonly seq: number;
25
+ readonly viewportPx: BrowserViewportPx;
26
+ readonly dataBase64: string;
27
+ readonly persisted: false;
28
+ }
29
+ export interface BrowserScreenshotPersisted {
30
+ readonly seq: number;
31
+ readonly viewportPx: BrowserViewportPx;
32
+ readonly persisted: true;
33
+ readonly path: string;
34
+ readonly sha256: string;
35
+ readonly bytes: number;
36
+ }
37
+ export type BrowserScreenshotResult = BrowserScreenshotPreview | BrowserScreenshotPersisted;
38
+ export interface BrowserContentResult {
39
+ readonly seq: number;
40
+ readonly byteLength: number;
41
+ readonly redactedHtml: string;
42
+ }
43
+ export interface CdpReachability {
44
+ readonly reachable: boolean;
45
+ readonly userAgent: string | null;
46
+ readonly browserVersion: string | null;
47
+ readonly webSocketDebuggerUrl: string | null;
48
+ }
@@ -0,0 +1,2 @@
1
+ // Data shapes for the browser tool (ADR-0017). Pure types: no runtime imports.
2
+ export {};
@@ -0,0 +1,5 @@
1
+ import type { NormalizedNavigateUrl } from "./types.js";
2
+ export declare function normalizeCdpPort(value: unknown): number;
3
+ export declare function normalizeNavigateUrl(raw: unknown): NormalizedNavigateUrl;
4
+ export declare function isLoopbackHost(host: string): boolean;
5
+ export declare function isLoopbackUrl(raw: string): boolean;
@@ -0,0 +1,97 @@
1
+ // ADR-0017 D2 — pure URL validation. NO filesystem, NO network. The localhost→127.0.0.1 rewrite
2
+ // happens here so that downstream code never hands `localhost` to the OS resolver, eliminating the
3
+ // /etc/hosts attack surface even if a downstream caller forgets the rule.
4
+ import { BrowserToolError } from "./errors.js";
5
+ const MIN_PORT = 1024;
6
+ const MAX_PORT = 65535;
7
+ const ALLOWED_SCHEMES = new Set(["http:", "https:"]);
8
+ // Strict literal-host policy: IPv4-mapped IPv6 forms like `::ffff:127.0.0.1` are
9
+ // REJECTED on purpose. ADR-0017 D2 normalises only `localhost`/`127.0.0.1`/`::1`.
10
+ const LOOPBACK_HOSTS = new Set(["127.0.0.1", "::1"]);
11
+ export function normalizeCdpPort(value) {
12
+ if (typeof value !== "number") {
13
+ throw new BrowserToolError("BAD_PORT", "CDP port must be a number.");
14
+ }
15
+ if (!Number.isInteger(value)) {
16
+ throw new BrowserToolError("BAD_PORT", "CDP port must be an integer.");
17
+ }
18
+ if (value < MIN_PORT || value > MAX_PORT) {
19
+ throw new BrowserToolError("BAD_PORT", `CDP port must be in the range ${String(MIN_PORT)}-${String(MAX_PORT)}.`);
20
+ }
21
+ return value;
22
+ }
23
+ // Parses the URL with the WHATWG parser, rejects non-http(s) schemes, rewrites `localhost` to its
24
+ // literal IP before any further use, then enforces literal-IP loopback + bounded-port. Returns the
25
+ // canonical {url, originOnly, host, port} struct the CDP layer consumes.
26
+ export function normalizeNavigateUrl(raw) {
27
+ if (typeof raw !== "string" || raw.length === 0) {
28
+ throw new BrowserToolError("BAD_URL", "Navigate URL must be a non-empty string.");
29
+ }
30
+ const parsed = parseUrlOrThrow(raw);
31
+ if (!ALLOWED_SCHEMES.has(parsed.protocol)) {
32
+ throw new BrowserToolError("SCHEME_NOT_ALLOWED", "Navigate URL must use the http or https scheme.");
33
+ }
34
+ const rewritten = rewriteLocalhost(parsed);
35
+ const host = bareHost(rewritten);
36
+ if (!LOOPBACK_HOSTS.has(host)) {
37
+ throw new BrowserToolError("ORIGIN_NOT_ALLOWED", "Navigate URL host must be a loopback literal (127.0.0.1 or ::1).");
38
+ }
39
+ if (rewritten.port === "") {
40
+ throw new BrowserToolError("BAD_PORT", "Navigate URL must include an explicit port.");
41
+ }
42
+ const port = normalizeCdpPort(Number.parseInt(rewritten.port, 10));
43
+ return {
44
+ url: rewritten.toString(),
45
+ host,
46
+ originOnly: rewritten.origin,
47
+ port,
48
+ };
49
+ }
50
+ function parseUrlOrThrow(raw) {
51
+ try {
52
+ return new URL(raw);
53
+ }
54
+ catch {
55
+ throw new BrowserToolError("BAD_URL", "Navigate URL is not a valid URL.");
56
+ }
57
+ }
58
+ // `URL.hostname` returns `localhost` unchanged; we rewrite to 127.0.0.1 and rebuild the URL so the
59
+ // canonical .url field handed to CDP never contains `localhost`.
60
+ function rewriteLocalhost(parsed) {
61
+ if (parsed.hostname !== "localhost")
62
+ return parsed;
63
+ const clone = new URL(parsed.toString());
64
+ clone.hostname = "127.0.0.1";
65
+ return clone;
66
+ }
67
+ // URL.hostname returns `[::1]` for IPv6 input; strip brackets so the LOOPBACK_HOSTS set comparison
68
+ // is performed against the bare literal `::1`.
69
+ function bareHost(parsed) {
70
+ const h = parsed.hostname;
71
+ if (h.startsWith("[") && h.endsWith("]"))
72
+ return h.slice(1, -1);
73
+ return h;
74
+ }
75
+ // Narrow host-string check: takes a bare hostname (no scheme, no port, no brackets stripped)
76
+ // and returns true if it is a loopback literal. Used by session.ts to validate the host
77
+ // component of webSocketDebuggerUrl returned by /json/version (ADR-0017 D2 H1).
78
+ export function isLoopbackHost(host) {
79
+ return LOOPBACK_HOSTS.has(host);
80
+ }
81
+ // Re-check after a navigation completes (ADR-0017 D2 layer 2). The CDP `frameNavigated` event
82
+ // reports the effective URL; if it drifted to a non-loopback origin (server-side redirect), the
83
+ // session manager must stop loading and refuse subsequent capture. Pure: takes the post-navigate
84
+ // URL string and returns the typed result.
85
+ export function isLoopbackUrl(raw) {
86
+ let parsed;
87
+ try {
88
+ parsed = new URL(raw);
89
+ }
90
+ catch {
91
+ return false;
92
+ }
93
+ if (!ALLOWED_SCHEMES.has(parsed.protocol))
94
+ return false;
95
+ const host = bareHost(parsed);
96
+ return LOOPBACK_HOSTS.has(host);
97
+ }
@@ -0,0 +1,59 @@
1
+ import type { PatchConflict, PatchRejection } from "./types.js";
2
+ export declare const TOOL_CODES: {
3
+ readonly ARGUMENT: "TOOL_ARGUMENT";
4
+ readonly UNKNOWN: "TOOL_UNKNOWN";
5
+ readonly COMMAND_DENIED: "TOOL_COMMAND_DENIED";
6
+ readonly COMMAND_TIMEOUT: "TOOL_COMMAND_TIMEOUT";
7
+ readonly COMMAND_CANCELLED: "TOOL_COMMAND_CANCELLED";
8
+ readonly OUTPUT_LIMIT: "TOOL_OUTPUT_LIMIT";
9
+ readonly PATCH_INVALID: "TOOL_PATCH_INVALID";
10
+ readonly PATCH_APPLY_DISABLED: "TOOL_PATCH_APPLY_DISABLED";
11
+ readonly PATCH_APPLY_FAILED: "TOOL_PATCH_APPLY_FAILED";
12
+ };
13
+ export type ToolCode = (typeof TOOL_CODES)[keyof typeof TOOL_CODES];
14
+ export declare abstract class ToolError extends Error {
15
+ abstract readonly code: ToolCode;
16
+ constructor(message: string, secrets?: readonly string[]);
17
+ }
18
+ export declare class ToolArgumentError extends ToolError {
19
+ readonly code: "TOOL_ARGUMENT";
20
+ readonly toolName: string;
21
+ constructor(message: string, toolName: string, secrets?: readonly string[]);
22
+ }
23
+ export declare class UnknownToolError extends ToolError {
24
+ readonly code: "TOOL_UNKNOWN";
25
+ readonly toolName: string;
26
+ constructor(message: string, toolName: string, secrets?: readonly string[]);
27
+ }
28
+ export declare class CommandDeniedError extends ToolError {
29
+ readonly code: "TOOL_COMMAND_DENIED";
30
+ readonly executable: string;
31
+ constructor(message: string, executable: string, secrets?: readonly string[]);
32
+ }
33
+ export declare class CommandTimeoutError extends ToolError {
34
+ readonly code: "TOOL_COMMAND_TIMEOUT";
35
+ readonly timeoutMs: number;
36
+ constructor(message: string, timeoutMs: number, secrets?: readonly string[]);
37
+ }
38
+ export declare class CommandCancelledError extends ToolError {
39
+ readonly code: "TOOL_COMMAND_CANCELLED";
40
+ }
41
+ export declare class OutputLimitError extends ToolError {
42
+ readonly code: "TOOL_OUTPUT_LIMIT";
43
+ readonly limitBytes: number;
44
+ constructor(message: string, limitBytes: number, secrets?: readonly string[]);
45
+ }
46
+ export declare class PatchValidationError extends ToolError {
47
+ readonly code: "TOOL_PATCH_INVALID";
48
+ readonly reasons: readonly PatchRejection[];
49
+ readonly conflicts: readonly PatchConflict[];
50
+ constructor(message: string, reasons: readonly PatchRejection[], conflicts: readonly PatchConflict[], secrets?: readonly string[]);
51
+ }
52
+ export declare class PatchApplyDisabledError extends ToolError {
53
+ readonly code: "TOOL_PATCH_APPLY_DISABLED";
54
+ }
55
+ export declare class PatchApplyError extends ToolError {
56
+ readonly code: "TOOL_PATCH_APPLY_FAILED";
57
+ readonly path: string;
58
+ constructor(message: string, path: string, secrets?: readonly string[]);
59
+ }
@@ -0,0 +1,94 @@
1
+ // Tool error taxonomy, mirroring gateway/harness/workspace (ADR-0003/0004/0005/0006).
2
+ // Errors carry a stable `code` discriminant; callers switch on `code`, never parse `message`.
3
+ // Every message is redacted at construction so errors are always safe to log or surface.
4
+ import { redact } from "../gateway/redaction.js";
5
+ export const TOOL_CODES = {
6
+ ARGUMENT: "TOOL_ARGUMENT",
7
+ UNKNOWN: "TOOL_UNKNOWN",
8
+ COMMAND_DENIED: "TOOL_COMMAND_DENIED",
9
+ COMMAND_TIMEOUT: "TOOL_COMMAND_TIMEOUT",
10
+ COMMAND_CANCELLED: "TOOL_COMMAND_CANCELLED",
11
+ OUTPUT_LIMIT: "TOOL_OUTPUT_LIMIT",
12
+ PATCH_INVALID: "TOOL_PATCH_INVALID",
13
+ PATCH_APPLY_DISABLED: "TOOL_PATCH_APPLY_DISABLED",
14
+ PATCH_APPLY_FAILED: "TOOL_PATCH_APPLY_FAILED",
15
+ };
16
+ export class ToolError extends Error {
17
+ constructor(message, secrets = []) {
18
+ super(redact(message, secrets));
19
+ this.name = new.target.name;
20
+ }
21
+ }
22
+ // Malformed tool arguments (a required field missing or of the wrong type).
23
+ export class ToolArgumentError extends ToolError {
24
+ code = TOOL_CODES.ARGUMENT;
25
+ toolName;
26
+ constructor(message, toolName, secrets = []) {
27
+ super(message, secrets);
28
+ this.toolName = toolName;
29
+ }
30
+ }
31
+ // No such tool in the host's dispatch map.
32
+ export class UnknownToolError extends ToolError {
33
+ code = TOOL_CODES.UNKNOWN;
34
+ toolName;
35
+ constructor(message, toolName, secrets = []) {
36
+ super(message, secrets);
37
+ this.toolName = toolName;
38
+ }
39
+ }
40
+ // Executable or subcommand not on the allowlist (deny-by-default). Raised BEFORE any spawn.
41
+ export class CommandDeniedError extends ToolError {
42
+ code = TOOL_CODES.COMMAND_DENIED;
43
+ executable;
44
+ constructor(message, executable, secrets = []) {
45
+ super(message, secrets);
46
+ this.executable = executable;
47
+ }
48
+ }
49
+ // The command exceeded its wall-time budget and was terminated.
50
+ export class CommandTimeoutError extends ToolError {
51
+ code = TOOL_CODES.COMMAND_TIMEOUT;
52
+ timeoutMs;
53
+ constructor(message, timeoutMs, secrets = []) {
54
+ super(message, secrets);
55
+ this.timeoutMs = timeoutMs;
56
+ }
57
+ }
58
+ // Abort-driven cancellation of a command or a patch apply.
59
+ export class CommandCancelledError extends ToolError {
60
+ code = TOOL_CODES.COMMAND_CANCELLED;
61
+ }
62
+ // A hard output-limit breach (used where truncate-and-flag is not acceptable, e.g. patch input).
63
+ export class OutputLimitError extends ToolError {
64
+ code = TOOL_CODES.OUTPUT_LIMIT;
65
+ limitBytes;
66
+ constructor(message, limitBytes, secrets = []) {
67
+ super(message, secrets);
68
+ this.limitBytes = limitBytes;
69
+ }
70
+ }
71
+ // Patch validation failed; carries the structured rejection reasons. Nothing was written.
72
+ export class PatchValidationError extends ToolError {
73
+ code = TOOL_CODES.PATCH_INVALID;
74
+ reasons;
75
+ conflicts;
76
+ constructor(message, reasons, conflicts, secrets = []) {
77
+ super(message, secrets);
78
+ this.reasons = reasons;
79
+ this.conflicts = conflicts;
80
+ }
81
+ }
82
+ // Fail-closed: apply requested while applyEnabled is false. Nothing was written.
83
+ export class PatchApplyDisabledError extends ToolError {
84
+ code = TOOL_CODES.PATCH_APPLY_DISABLED;
85
+ }
86
+ // A write (or rollback) failed during the apply phase at the filesystem boundary.
87
+ export class PatchApplyError extends ToolError {
88
+ code = TOOL_CODES.PATCH_APPLY_FAILED;
89
+ path;
90
+ constructor(message, path, secrets = []) {
91
+ super(message, secrets);
92
+ this.path = path;
93
+ }
94
+ }
@@ -0,0 +1,42 @@
1
+ import type { ChildProcess } from "node:child_process";
2
+ import { type WorkspaceFs } from "../workspace/fs.js";
3
+ import type { WorkspaceInfo } from "../workspace/types.js";
4
+ import type { CommandResult, CommandRule, SandboxPolicy } from "./types.js";
5
+ export interface SpawnOptions {
6
+ readonly cwd: string;
7
+ readonly env: Record<string, string>;
8
+ readonly shell: false;
9
+ readonly detached: boolean;
10
+ }
11
+ export type SpawnFn = (command: string, args: readonly string[], options: SpawnOptions) => ChildProcess;
12
+ export interface ExecutableResolverDeps {
13
+ readonly workspace: WorkspaceInfo;
14
+ readonly processEnv: NodeJS.ProcessEnv;
15
+ readonly fs?: WorkspaceFs | undefined;
16
+ }
17
+ export type ExecutableResolver = (command: string, deps: ExecutableResolverDeps) => string;
18
+ export declare const nodeSpawnFn: SpawnFn;
19
+ export interface HomeProvider {
20
+ readonly make: () => string;
21
+ readonly cleanup: (dir: string) => void;
22
+ }
23
+ export declare const nodeHomeProvider: HomeProvider;
24
+ export interface RunCommandDeps {
25
+ readonly workspace: WorkspaceInfo;
26
+ readonly policy: SandboxPolicy;
27
+ readonly commandRules: readonly CommandRule[];
28
+ readonly spawn: SpawnFn;
29
+ readonly resolveExecutable?: ExecutableResolver | undefined;
30
+ readonly processEnv: NodeJS.ProcessEnv;
31
+ readonly now: () => number;
32
+ readonly fs?: WorkspaceFs | undefined;
33
+ readonly home?: HomeProvider | undefined;
34
+ }
35
+ export interface RunCommandInput {
36
+ readonly command: string;
37
+ readonly args: readonly string[];
38
+ readonly cwd: string | undefined;
39
+ readonly timeoutMs: number | undefined;
40
+ readonly signal: AbortSignal;
41
+ }
42
+ export declare function runCommand(input: RunCommandInput, deps: RunCommandDeps): Promise<CommandResult>;
@@ -0,0 +1,327 @@
1
+ // Command execution — the spawn boundary. Deny-by-default allowlist is checked BEFORE any
2
+ // spawn; the child runs with a clean name-allowlisted env, no shell, and a resolved-in-workspace
3
+ // cwd. Timeout and abort both kill the process group (SIGTERM→SIGKILL after the grace period).
4
+ // stdout/stderr are byte-capped and redacted before they leave this layer (ADR-0006 D3/D5).
5
+ //
6
+ // node:child_process is imported ONLY for the default SpawnFn adapter; all decision logic lives
7
+ // in sandbox.ts (pure). Tests inject a fake SpawnFn for the allowlist/timeout/cancel paths and a
8
+ // real `node`-spawn for the env-isolation / no-shell / real-cancellation integration cases.
9
+ import { spawn as nodeSpawn } from "node:child_process";
10
+ import { accessSync, constants, mkdtempSync, realpathSync, rmSync } from "node:fs";
11
+ import { tmpdir } from "node:os";
12
+ import { delimiter, join, resolve as resolvePath } from "node:path";
13
+ import { redact } from "../gateway/redaction.js";
14
+ import { isDenied } from "../workspace/ignore.js";
15
+ import { isWithinWorkspace, resolveWithinWorkspace } from "../workspace/paths.js";
16
+ import { containedRealPathInfo } from "../workspace/realpath.js";
17
+ import { nodeWorkspaceFs } from "../workspace/fs.js";
18
+ import { PathDeniedError } from "../workspace/errors.js";
19
+ import { CommandCancelledError, CommandDeniedError, CommandTimeoutError } from "./errors.js";
20
+ import { buildSandboxEnv, collectSensitiveEnvValues, isCommandAllowed } from "./sandbox.js";
21
+ export const nodeSpawnFn = (command, args, options) => nodeSpawn(command, [...args], options);
22
+ export const nodeHomeProvider = {
23
+ make: () => mkdtempSync(join(tmpdir(), "keiko-home-")),
24
+ cleanup: (dir) => {
25
+ // Best-effort: a leftover temp dir is not worth failing or rejecting the command over.
26
+ rmSync(dir, { recursive: true, force: true });
27
+ },
28
+ };
29
+ const POSIX = process.platform !== "win32";
30
+ // Kills the whole process group on POSIX (negative pid) so orphaned grandchildren die too;
31
+ // on Windows, best-effort child.kill() (a tree-kill needs a dependency we cannot add).
32
+ function killGroup(child, sig) {
33
+ const pid = child.pid;
34
+ if (pid === undefined) {
35
+ return;
36
+ }
37
+ try {
38
+ if (POSIX) {
39
+ process.kill(-pid, sig);
40
+ }
41
+ else {
42
+ child.kill(sig);
43
+ }
44
+ }
45
+ catch {
46
+ // The child already exited; nothing to signal. Swallowing here keeps termination idempotent.
47
+ }
48
+ }
49
+ const TRUNCATED_OUTPUT_MARKER = "[TRUNCATED OUTPUT REDACTED]";
50
+ function hasPathSeparator(value) {
51
+ return value.includes("/") || value.includes("\\");
52
+ }
53
+ function hasNul(value) {
54
+ return value.includes("\u0000");
55
+ }
56
+ function realRoot(fs, root) {
57
+ try {
58
+ return fs.realPath(root);
59
+ }
60
+ catch {
61
+ return root;
62
+ }
63
+ }
64
+ function assertBareExecutable(command) {
65
+ if (command.length === 0 || hasNul(command) || hasPathSeparator(command)) {
66
+ throw new CommandDeniedError("executable must be a bare PATH-resolved name", command);
67
+ }
68
+ }
69
+ function pathEntries(processEnv) {
70
+ const pathValue = processEnv.PATH ?? "";
71
+ return pathValue.length === 0 ? [] : pathValue.split(delimiter).filter(Boolean);
72
+ }
73
+ function executableExtensions(processEnv) {
74
+ if (process.platform !== "win32") {
75
+ return [""];
76
+ }
77
+ return (processEnv.PATHEXT ?? ".EXE;.CMD;.BAT;.COM")
78
+ .split(";")
79
+ .filter((value) => value.length > 0);
80
+ }
81
+ function candidateExecutable(command, rawEntry, ext) {
82
+ const candidate = resolvePath(resolvePath(rawEntry), command + ext);
83
+ try {
84
+ accessSync(candidate, constants.X_OK);
85
+ return { path: candidate, real: realpathSync(candidate) };
86
+ }
87
+ catch {
88
+ return undefined;
89
+ }
90
+ }
91
+ function assertExecutableOutsideWorkspace(command, lexicalWorkspaceRoot, realWorkspaceRoot, candidate) {
92
+ if (isWithinWorkspace(lexicalWorkspaceRoot, candidate.path) ||
93
+ isWithinWorkspace(realWorkspaceRoot, candidate.real)) {
94
+ throw new CommandDeniedError(`executable resolves inside workspace: ${command}`, command);
95
+ }
96
+ }
97
+ function defaultResolveExecutable(command, deps) {
98
+ assertBareExecutable(command);
99
+ const fs = deps.fs ?? nodeWorkspaceFs;
100
+ const lexicalWorkspaceRoot = deps.workspace.root;
101
+ const realWorkspaceRoot = realRoot(fs, lexicalWorkspaceRoot);
102
+ for (const rawEntry of pathEntries(deps.processEnv)) {
103
+ for (const ext of executableExtensions(deps.processEnv)) {
104
+ const candidate = candidateExecutable(command, rawEntry, ext);
105
+ if (candidate === undefined) {
106
+ continue;
107
+ }
108
+ assertExecutableOutsideWorkspace(command, lexicalWorkspaceRoot, realWorkspaceRoot, candidate);
109
+ return candidate.real;
110
+ }
111
+ }
112
+ throw new CommandDeniedError(`executable not found on PATH: ${command}`, command);
113
+ }
114
+ function appendCapped(buffers, sink, chunk, max) {
115
+ if (buffers.truncated) {
116
+ return false;
117
+ }
118
+ const remaining = max - buffers.total;
119
+ if (chunk.length <= remaining) {
120
+ sink.push(chunk);
121
+ buffers.total += chunk.length;
122
+ return false;
123
+ }
124
+ if (remaining > 0) {
125
+ sink.push(chunk.subarray(0, remaining));
126
+ buffers.total = max;
127
+ }
128
+ buffers.truncated = true;
129
+ return true; // signals the caller to kill the child (flood protection)
130
+ }
131
+ // Resolves the validated cwd. Lexical containment first, then symlink containment via realpath
132
+ // (S-H1): a cwd that is a symlink escaping the root or resolving into an always-denied path must
133
+ // not become the spawn cwd. Both cases surface as workspace path errors, which the host maps to a
134
+ // tool error — the command never spawns.
135
+ function resolveCwd(deps, cwd) {
136
+ const lexical = resolveWithinWorkspace(deps.workspace.root, cwd ?? ".");
137
+ const fs = deps.fs ?? nodeWorkspaceFs;
138
+ const rel = lexical.slice(deps.workspace.root.length).replace(/^[/\\]/, "");
139
+ if (isDenied(rel === "" ? (cwd ?? ".") : rel)) {
140
+ throw new PathDeniedError("path matches an always-on deny pattern", cwd ?? ".");
141
+ }
142
+ const info = containedRealPathInfo(fs, deps.workspace.root, lexical);
143
+ if (isDenied(info.realRelative)) {
144
+ throw new PathDeniedError("path matches an always-on deny pattern", cwd ?? ".");
145
+ }
146
+ return lexical;
147
+ }
148
+ function buildResult(input, buffers, state, exitCode, termSignal, deps, startedAt) {
149
+ const secrets = collectSensitiveEnvValues(deps.processEnv, deps.policy.envAllowlist);
150
+ if (buffers.truncated) {
151
+ return {
152
+ command: input.command,
153
+ args: input.args,
154
+ exitCode,
155
+ signal: termSignal,
156
+ stdout: TRUNCATED_OUTPUT_MARKER,
157
+ stderr: TRUNCATED_OUTPUT_MARKER,
158
+ durationMs: deps.now() - startedAt,
159
+ timedOut: state.timedOut,
160
+ truncated: true,
161
+ };
162
+ }
163
+ return {
164
+ command: input.command,
165
+ args: input.args,
166
+ exitCode,
167
+ signal: termSignal,
168
+ stdout: redact(Buffer.concat(buffers.out).toString("utf8"), secrets),
169
+ stderr: redact(Buffer.concat(buffers.err).toString("utf8"), secrets),
170
+ durationMs: deps.now() - startedAt,
171
+ timedOut: state.timedOut,
172
+ truncated: buffers.truncated,
173
+ };
174
+ }
175
+ function cleanup(state, signal) {
176
+ if (state.timer !== undefined) {
177
+ clearTimeout(state.timer);
178
+ }
179
+ if (state.graceTimer !== undefined) {
180
+ clearTimeout(state.graceTimer);
181
+ }
182
+ if (state.onAbort !== undefined) {
183
+ signal.removeEventListener("abort", state.onAbort);
184
+ }
185
+ // Remove the ephemeral HOME exactly once, on whichever settle path fires first (C5).
186
+ if (!state.homeCleaned && state.home !== undefined && state.homeDir !== undefined) {
187
+ state.homeCleaned = true;
188
+ state.home.cleanup(state.homeDir);
189
+ }
190
+ }
191
+ // Escalates from SIGTERM to SIGKILL after the grace period so a child ignoring SIGTERM is still
192
+ // guaranteed to terminate within terminationGraceMs of the trigger.
193
+ function terminate(child, policy, state) {
194
+ killGroup(child, "SIGTERM");
195
+ state.graceTimer = setTimeout(() => {
196
+ killGroup(child, "SIGKILL");
197
+ }, policy.terminationGraceMs);
198
+ state.graceTimer.unref();
199
+ }
200
+ function wireStreams(child, buffers, policy, state) {
201
+ const onData = (sink) => (chunk) => {
202
+ if (appendCapped(buffers, sink, chunk, policy.maxOutputBytes)) {
203
+ terminate(child, policy, state); // output flood → kill
204
+ }
205
+ };
206
+ child.stdout?.on("data", onData(buffers.out));
207
+ child.stderr?.on("data", onData(buffers.err));
208
+ }
209
+ function settleOnClose(ctx, resolve, reject) {
210
+ ctx.child.on("close", (code, signalName) => {
211
+ if (ctx.state.settled) {
212
+ return;
213
+ }
214
+ ctx.state.settled = true;
215
+ cleanup(ctx.state, ctx.input.signal);
216
+ if (ctx.state.timedOut) {
217
+ reject(new CommandTimeoutError("command timed out", timeoutOf(ctx)));
218
+ return;
219
+ }
220
+ if (ctx.input.signal.aborted) {
221
+ reject(new CommandCancelledError("command cancelled"));
222
+ return;
223
+ }
224
+ resolve(buildResult(ctx.input, ctx.buffers, ctx.state, code, signalName, ctx.deps, ctx.startedAt));
225
+ });
226
+ ctx.child.on("error", (error) => {
227
+ if (ctx.state.settled) {
228
+ return;
229
+ }
230
+ ctx.state.settled = true;
231
+ cleanup(ctx.state, ctx.input.signal);
232
+ reject(error);
233
+ });
234
+ }
235
+ function timeoutOf(ctx) {
236
+ return ctx.input.timeoutMs ?? ctx.deps.policy.defaultTimeoutMs;
237
+ }
238
+ function armTimersAndAbort(ctx) {
239
+ const ms = timeoutOf(ctx);
240
+ ctx.state.timer = setTimeout(() => {
241
+ ctx.state.timedOut = true;
242
+ terminate(ctx.child, ctx.deps.policy, ctx.state);
243
+ }, ms);
244
+ ctx.state.timer.unref();
245
+ const onAbort = () => {
246
+ terminate(ctx.child, ctx.deps.policy, ctx.state);
247
+ };
248
+ ctx.state.onAbort = onAbort;
249
+ if (ctx.input.signal.aborted) {
250
+ onAbort();
251
+ }
252
+ else {
253
+ ctx.input.signal.addEventListener("abort", onAbort, { once: true });
254
+ }
255
+ }
256
+ function asError(error, fallback) {
257
+ return error instanceof Error ? error : new Error(fallback);
258
+ }
259
+ function validateRunCommandInput(input, deps) {
260
+ if (!Array.isArray(deps.policy.envAllowlist) || deps.policy.envAllowlist.length === 0) {
261
+ throw new CommandDeniedError("sandbox envAllowlist must be a non-empty array", input.command);
262
+ }
263
+ const decision = isCommandAllowed(deps.commandRules, input.command, input.args);
264
+ if (!decision.allowed) {
265
+ throw new CommandDeniedError(decision.reason ?? "command denied", input.command);
266
+ }
267
+ }
268
+ function resolveExecutable(input, deps) {
269
+ const resolver = deps.resolveExecutable ?? defaultResolveExecutable;
270
+ return resolver(input.command, {
271
+ workspace: deps.workspace,
272
+ processEnv: deps.processEnv,
273
+ fs: deps.fs,
274
+ });
275
+ }
276
+ function createRunState(home, homeDir) {
277
+ return {
278
+ settled: false,
279
+ timedOut: false,
280
+ timer: undefined,
281
+ graceTimer: undefined,
282
+ onAbort: undefined,
283
+ home,
284
+ homeDir,
285
+ homeCleaned: false,
286
+ };
287
+ }
288
+ function spawnChild(input, deps, executable, cwd, env, state) {
289
+ try {
290
+ return deps.spawn(executable, input.args, { cwd, env, shell: false, detached: POSIX });
291
+ }
292
+ catch (error) {
293
+ cleanup(state, input.signal);
294
+ throw asError(error, "spawn failed");
295
+ }
296
+ }
297
+ function runSpawnedChild(ctx) {
298
+ return new Promise((resolve, reject) => {
299
+ wireStreams(ctx.child, ctx.buffers, ctx.deps.policy, ctx.state);
300
+ settleOnClose(ctx, resolve, reject);
301
+ armTimersAndAbort(ctx);
302
+ });
303
+ }
304
+ // Runs an allowlisted command. Rejects with CommandDeniedError (before spawn) for a denied
305
+ // command or a workspace-escaping cwd (PathEscapeError), CommandTimeoutError on timeout, and
306
+ // CommandCancelledError on abort; otherwise resolves a redacted, byte-capped CommandResult. All
307
+ // failure paths are Promise rejections — the function never throws synchronously.
308
+ export function runCommand(input, deps) {
309
+ try {
310
+ validateRunCommandInput(input, deps);
311
+ const executable = resolveExecutable(input, deps);
312
+ const cwd = resolveCwd(deps, input.cwd);
313
+ const env = buildSandboxEnv(deps.processEnv, deps.policy.envAllowlist);
314
+ const home = deps.home ?? nodeHomeProvider;
315
+ const homeDir = home.make();
316
+ env.HOME = homeDir;
317
+ env.USERPROFILE = homeDir;
318
+ const state = createRunState(home, homeDir);
319
+ const child = spawnChild(input, deps, executable, cwd, env, state);
320
+ const buffers = { out: [], err: [], total: 0, truncated: false };
321
+ const ctx = { child, input, deps, buffers, state, startedAt: deps.now() };
322
+ return runSpawnedChild(ctx);
323
+ }
324
+ catch (error) {
325
+ return Promise.reject(asError(error, "command execution failed"));
326
+ }
327
+ }