@prismatic-io/lux 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (636) hide show
  1. package/LICENSE +21 -0
  2. package/lib/answerers/claude-code/index.d.ts +17 -0
  3. package/lib/answerers/claude-code/index.d.ts.map +1 -0
  4. package/lib/answerers/claude-code/index.js +280 -0
  5. package/lib/answerers/claude-code/index.js.map +1 -0
  6. package/lib/answerers/persona/index.d.ts +40 -0
  7. package/lib/answerers/persona/index.d.ts.map +1 -0
  8. package/lib/answerers/persona/index.js +265 -0
  9. package/lib/answerers/persona/index.js.map +1 -0
  10. package/lib/answerers/scripted/index.d.ts +8 -0
  11. package/lib/answerers/scripted/index.d.ts.map +1 -0
  12. package/lib/answerers/scripted/index.js +163 -0
  13. package/lib/answerers/scripted/index.js.map +1 -0
  14. package/lib/answerers/shared.d.ts +13 -0
  15. package/lib/answerers/shared.d.ts.map +1 -0
  16. package/lib/answerers/shared.js +17 -0
  17. package/lib/answerers/shared.js.map +1 -0
  18. package/lib/answerers/terminal/index.d.ts +9 -0
  19. package/lib/answerers/terminal/index.d.ts.map +1 -0
  20. package/lib/answerers/terminal/index.js +97 -0
  21. package/lib/answerers/terminal/index.js.map +1 -0
  22. package/lib/assertions/core/command-exits-zero.d.ts +21 -0
  23. package/lib/assertions/core/command-exits-zero.d.ts.map +1 -0
  24. package/lib/assertions/core/command-exits-zero.js +87 -0
  25. package/lib/assertions/core/command-exits-zero.js.map +1 -0
  26. package/lib/assertions/core/event-checks.d.ts +57 -0
  27. package/lib/assertions/core/event-checks.d.ts.map +1 -0
  28. package/lib/assertions/core/event-checks.js +123 -0
  29. package/lib/assertions/core/event-checks.js.map +1 -0
  30. package/lib/assertions/core/file-checks.d.ts +103 -0
  31. package/lib/assertions/core/file-checks.d.ts.map +1 -0
  32. package/lib/assertions/core/file-checks.js +288 -0
  33. package/lib/assertions/core/file-checks.js.map +1 -0
  34. package/lib/assertions/core/helpers.d.ts +66 -0
  35. package/lib/assertions/core/helpers.d.ts.map +1 -0
  36. package/lib/assertions/core/helpers.js +170 -0
  37. package/lib/assertions/core/helpers.js.map +1 -0
  38. package/lib/assertions/core/index.d.ts +15 -0
  39. package/lib/assertions/core/index.d.ts.map +1 -0
  40. package/lib/assertions/core/index.js +56 -0
  41. package/lib/assertions/core/index.js.map +1 -0
  42. package/lib/assertions/core/json-pointer-equals.d.ts +19 -0
  43. package/lib/assertions/core/json-pointer-equals.d.ts.map +1 -0
  44. package/lib/assertions/core/json-pointer-equals.js +71 -0
  45. package/lib/assertions/core/json-pointer-equals.js.map +1 -0
  46. package/lib/assertions/core/output-checks.d.ts +46 -0
  47. package/lib/assertions/core/output-checks.d.ts.map +1 -0
  48. package/lib/assertions/core/output-checks.js +121 -0
  49. package/lib/assertions/core/output-checks.js.map +1 -0
  50. package/lib/assertions/core/predicate.d.ts +14 -0
  51. package/lib/assertions/core/predicate.d.ts.map +1 -0
  52. package/lib/assertions/core/predicate.js +55 -0
  53. package/lib/assertions/core/predicate.js.map +1 -0
  54. package/lib/assertions/core/resource-checks.d.ts +71 -0
  55. package/lib/assertions/core/resource-checks.d.ts.map +1 -0
  56. package/lib/assertions/core/resource-checks.js +159 -0
  57. package/lib/assertions/core/resource-checks.js.map +1 -0
  58. package/lib/assertions/core/safe-regex.d.ts +23 -0
  59. package/lib/assertions/core/safe-regex.d.ts.map +1 -0
  60. package/lib/assertions/core/safe-regex.js +102 -0
  61. package/lib/assertions/core/safe-regex.js.map +1 -0
  62. package/lib/assertions/core/tool-called-with.d.ts +21 -0
  63. package/lib/assertions/core/tool-called-with.d.ts.map +1 -0
  64. package/lib/assertions/core/tool-called-with.js +69 -0
  65. package/lib/assertions/core/tool-called-with.js.map +1 -0
  66. package/lib/assertions/core/tool-calls.d.ts +22 -0
  67. package/lib/assertions/core/tool-calls.d.ts.map +1 -0
  68. package/lib/assertions/core/tool-calls.js +17 -0
  69. package/lib/assertions/core/tool-calls.js.map +1 -0
  70. package/lib/assertions/core/tool-invocation.d.ts +36 -0
  71. package/lib/assertions/core/tool-invocation.d.ts.map +1 -0
  72. package/lib/assertions/core/tool-invocation.js +170 -0
  73. package/lib/assertions/core/tool-invocation.js.map +1 -0
  74. package/lib/assertions/core/tool-result-checks.d.ts +59 -0
  75. package/lib/assertions/core/tool-result-checks.d.ts.map +1 -0
  76. package/lib/assertions/core/tool-result-checks.js +193 -0
  77. package/lib/assertions/core/tool-result-checks.js.map +1 -0
  78. package/lib/assertions/mcp/discovery-checks.d.ts +36 -0
  79. package/lib/assertions/mcp/discovery-checks.d.ts.map +1 -0
  80. package/lib/assertions/mcp/discovery-checks.js +122 -0
  81. package/lib/assertions/mcp/discovery-checks.js.map +1 -0
  82. package/lib/assertions/mcp/index.d.ts +10 -0
  83. package/lib/assertions/mcp/index.d.ts.map +1 -0
  84. package/lib/assertions/mcp/index.js +25 -0
  85. package/lib/assertions/mcp/index.js.map +1 -0
  86. package/lib/assertions/rubric/index.d.ts +104 -0
  87. package/lib/assertions/rubric/index.d.ts.map +1 -0
  88. package/lib/assertions/rubric/index.js +191 -0
  89. package/lib/assertions/rubric/index.js.map +1 -0
  90. package/lib/assertions/rubric/internal.d.ts +14 -0
  91. package/lib/assertions/rubric/internal.d.ts.map +1 -0
  92. package/lib/assertions/rubric/internal.js +335 -0
  93. package/lib/assertions/rubric/internal.js.map +1 -0
  94. package/lib/authoring.d.ts +3 -0
  95. package/lib/authoring.d.ts.map +1 -0
  96. package/lib/authoring.js +3 -0
  97. package/lib/authoring.js.map +1 -0
  98. package/lib/cli/bin.d.ts +3 -0
  99. package/lib/cli/bin.d.ts.map +1 -0
  100. package/lib/cli/bin.js +15 -0
  101. package/lib/cli/bin.js.map +1 -0
  102. package/lib/cli/command-runtime.d.ts +5 -0
  103. package/lib/cli/command-runtime.d.ts.map +1 -0
  104. package/lib/cli/command-runtime.js +30 -0
  105. package/lib/cli/command-runtime.js.map +1 -0
  106. package/lib/cli/init.d.ts +21 -0
  107. package/lib/cli/init.d.ts.map +1 -0
  108. package/lib/cli/init.js +340 -0
  109. package/lib/cli/init.js.map +1 -0
  110. package/lib/cli/program.d.ts +22 -0
  111. package/lib/cli/program.d.ts.map +1 -0
  112. package/lib/cli/program.js +757 -0
  113. package/lib/cli/program.js.map +1 -0
  114. package/lib/cli/render/format.d.ts +14 -0
  115. package/lib/cli/render/format.d.ts.map +1 -0
  116. package/lib/cli/render/format.js +71 -0
  117. package/lib/cli/render/format.js.map +1 -0
  118. package/lib/cli/render/reporter.d.ts +73 -0
  119. package/lib/cli/render/reporter.d.ts.map +1 -0
  120. package/lib/cli/render/reporter.js +519 -0
  121. package/lib/cli/render/reporter.js.map +1 -0
  122. package/lib/cli/render/tty.d.ts +77 -0
  123. package/lib/cli/render/tty.d.ts.map +1 -0
  124. package/lib/cli/render/tty.js +283 -0
  125. package/lib/cli/render/tty.js.map +1 -0
  126. package/lib/cli/run-options.d.ts +27 -0
  127. package/lib/cli/run-options.d.ts.map +1 -0
  128. package/lib/cli/run-options.js +58 -0
  129. package/lib/cli/run-options.js.map +1 -0
  130. package/lib/cli/skills.d.ts +38 -0
  131. package/lib/cli/skills.d.ts.map +1 -0
  132. package/lib/cli/skills.js +84 -0
  133. package/lib/cli/skills.js.map +1 -0
  134. package/lib/core/annotation.d.ts +56 -0
  135. package/lib/core/annotation.d.ts.map +1 -0
  136. package/lib/core/annotation.js +92 -0
  137. package/lib/core/annotation.js.map +1 -0
  138. package/lib/core/answerer.d.ts +68 -0
  139. package/lib/core/answerer.d.ts.map +1 -0
  140. package/lib/core/answerer.js +17 -0
  141. package/lib/core/answerer.js.map +1 -0
  142. package/lib/core/artifact-evidence.d.ts +50 -0
  143. package/lib/core/artifact-evidence.d.ts.map +1 -0
  144. package/lib/core/artifact-evidence.js +185 -0
  145. package/lib/core/artifact-evidence.js.map +1 -0
  146. package/lib/core/artifact-snapshot.d.ts +37 -0
  147. package/lib/core/artifact-snapshot.d.ts.map +1 -0
  148. package/lib/core/artifact-snapshot.js +339 -0
  149. package/lib/core/artifact-snapshot.js.map +1 -0
  150. package/lib/core/assertion.d.ts +129 -0
  151. package/lib/core/assertion.d.ts.map +1 -0
  152. package/lib/core/assertion.js +75 -0
  153. package/lib/core/assertion.js.map +1 -0
  154. package/lib/core/case.d.ts +79 -0
  155. package/lib/core/case.d.ts.map +1 -0
  156. package/lib/core/case.js +90 -0
  157. package/lib/core/case.js.map +1 -0
  158. package/lib/core/conversation-turn.d.ts +23 -0
  159. package/lib/core/conversation-turn.d.ts.map +1 -0
  160. package/lib/core/conversation-turn.js +31 -0
  161. package/lib/core/conversation-turn.js.map +1 -0
  162. package/lib/core/driver.d.ts +64 -0
  163. package/lib/core/driver.d.ts.map +1 -0
  164. package/lib/core/driver.js +18 -0
  165. package/lib/core/driver.js.map +1 -0
  166. package/lib/core/emitter.d.ts +20 -0
  167. package/lib/core/emitter.d.ts.map +1 -0
  168. package/lib/core/emitter.js +34 -0
  169. package/lib/core/emitter.js.map +1 -0
  170. package/lib/core/events.d.ts +202 -0
  171. package/lib/core/events.d.ts.map +1 -0
  172. package/lib/core/events.js +107 -0
  173. package/lib/core/events.js.map +1 -0
  174. package/lib/core/exec.d.ts +82 -0
  175. package/lib/core/exec.d.ts.map +1 -0
  176. package/lib/core/exec.js +168 -0
  177. package/lib/core/exec.js.map +1 -0
  178. package/lib/core/experiment.d.ts +285 -0
  179. package/lib/core/experiment.d.ts.map +1 -0
  180. package/lib/core/experiment.js +389 -0
  181. package/lib/core/experiment.js.map +1 -0
  182. package/lib/core/glob.d.ts +3 -0
  183. package/lib/core/glob.d.ts.map +1 -0
  184. package/lib/core/glob.js +32 -0
  185. package/lib/core/glob.js.map +1 -0
  186. package/lib/core/hash.d.ts +8 -0
  187. package/lib/core/hash.d.ts.map +1 -0
  188. package/lib/core/hash.js +214 -0
  189. package/lib/core/hash.js.map +1 -0
  190. package/lib/core/index.d.ts +29 -0
  191. package/lib/core/index.d.ts.map +1 -0
  192. package/lib/core/index.js +29 -0
  193. package/lib/core/index.js.map +1 -0
  194. package/lib/core/lifecycle-fixtures.d.ts +20 -0
  195. package/lib/core/lifecycle-fixtures.d.ts.map +1 -0
  196. package/lib/core/lifecycle-fixtures.js +109 -0
  197. package/lib/core/lifecycle-fixtures.js.map +1 -0
  198. package/lib/core/lifecycle-hooks.d.ts +52 -0
  199. package/lib/core/lifecycle-hooks.d.ts.map +1 -0
  200. package/lib/core/lifecycle-hooks.js +42 -0
  201. package/lib/core/lifecycle-hooks.js.map +1 -0
  202. package/lib/core/mcp-events.d.ts +44 -0
  203. package/lib/core/mcp-events.d.ts.map +1 -0
  204. package/lib/core/mcp-events.js +23 -0
  205. package/lib/core/mcp-events.js.map +1 -0
  206. package/lib/core/model-cli.d.ts +57 -0
  207. package/lib/core/model-cli.d.ts.map +1 -0
  208. package/lib/core/model-cli.js +237 -0
  209. package/lib/core/model-cli.js.map +1 -0
  210. package/lib/core/observer.d.ts +31 -0
  211. package/lib/core/observer.d.ts.map +1 -0
  212. package/lib/core/observer.js +25 -0
  213. package/lib/core/observer.js.map +1 -0
  214. package/lib/core/platform-process.d.ts +33 -0
  215. package/lib/core/platform-process.d.ts.map +1 -0
  216. package/lib/core/platform-process.js +214 -0
  217. package/lib/core/platform-process.js.map +1 -0
  218. package/lib/core/registry.d.ts +22 -0
  219. package/lib/core/registry.d.ts.map +1 -0
  220. package/lib/core/registry.js +45 -0
  221. package/lib/core/registry.js.map +1 -0
  222. package/lib/core/rubric.d.ts +18 -0
  223. package/lib/core/rubric.d.ts.map +1 -0
  224. package/lib/core/rubric.js +49 -0
  225. package/lib/core/rubric.js.map +1 -0
  226. package/lib/core/run.d.ts +419 -0
  227. package/lib/core/run.d.ts.map +1 -0
  228. package/lib/core/run.js +126 -0
  229. package/lib/core/run.js.map +1 -0
  230. package/lib/core/statistics.d.ts +39 -0
  231. package/lib/core/statistics.d.ts.map +1 -0
  232. package/lib/core/statistics.js +163 -0
  233. package/lib/core/statistics.js.map +1 -0
  234. package/lib/core/subject.d.ts +4 -0
  235. package/lib/core/subject.d.ts.map +1 -0
  236. package/lib/core/subject.js +46 -0
  237. package/lib/core/subject.js.map +1 -0
  238. package/lib/core/tool-events.d.ts +69 -0
  239. package/lib/core/tool-events.d.ts.map +1 -0
  240. package/lib/core/tool-events.js +242 -0
  241. package/lib/core/tool-events.js.map +1 -0
  242. package/lib/core/usage.d.ts +78 -0
  243. package/lib/core/usage.d.ts.map +1 -0
  244. package/lib/core/usage.js +104 -0
  245. package/lib/core/usage.js.map +1 -0
  246. package/lib/core/wire.d.ts +83 -0
  247. package/lib/core/wire.d.ts.map +1 -0
  248. package/lib/core/wire.js +101 -0
  249. package/lib/core/wire.js.map +1 -0
  250. package/lib/core/zod.d.ts +10 -0
  251. package/lib/core/zod.d.ts.map +1 -0
  252. package/lib/core/zod.js +18 -0
  253. package/lib/core/zod.js.map +1 -0
  254. package/lib/drivers/claude-code/index.d.ts +55 -0
  255. package/lib/drivers/claude-code/index.d.ts.map +1 -0
  256. package/lib/drivers/claude-code/index.js +779 -0
  257. package/lib/drivers/claude-code/index.js.map +1 -0
  258. package/lib/drivers/claude-code/parse-events.d.ts +46 -0
  259. package/lib/drivers/claude-code/parse-events.d.ts.map +1 -0
  260. package/lib/drivers/claude-code/parse-events.js +387 -0
  261. package/lib/drivers/claude-code/parse-events.js.map +1 -0
  262. package/lib/drivers/codex/app-events.d.ts +16 -0
  263. package/lib/drivers/codex/app-events.d.ts.map +1 -0
  264. package/lib/drivers/codex/app-events.js +184 -0
  265. package/lib/drivers/codex/app-events.js.map +1 -0
  266. package/lib/drivers/codex/config.d.ts +55 -0
  267. package/lib/drivers/codex/config.d.ts.map +1 -0
  268. package/lib/drivers/codex/config.js +125 -0
  269. package/lib/drivers/codex/config.js.map +1 -0
  270. package/lib/drivers/codex/index.d.ts +8 -0
  271. package/lib/drivers/codex/index.d.ts.map +1 -0
  272. package/lib/drivers/codex/index.js +904 -0
  273. package/lib/drivers/codex/index.js.map +1 -0
  274. package/lib/drivers/mcp/index.d.ts +52 -0
  275. package/lib/drivers/mcp/index.d.ts.map +1 -0
  276. package/lib/drivers/mcp/index.js +282 -0
  277. package/lib/drivers/mcp/index.js.map +1 -0
  278. package/lib/drivers/mcp/transport.d.ts +50 -0
  279. package/lib/drivers/mcp/transport.d.ts.map +1 -0
  280. package/lib/drivers/mcp/transport.js +190 -0
  281. package/lib/drivers/mcp/transport.js.map +1 -0
  282. package/lib/drivers/shared/artifacts.d.ts +4 -0
  283. package/lib/drivers/shared/artifacts.d.ts.map +1 -0
  284. package/lib/drivers/shared/artifacts.js +81 -0
  285. package/lib/drivers/shared/artifacts.js.map +1 -0
  286. package/lib/drivers/shared/secure-copy.d.ts +2 -0
  287. package/lib/drivers/shared/secure-copy.d.ts.map +1 -0
  288. package/lib/drivers/shared/secure-copy.js +35 -0
  289. package/lib/drivers/shared/secure-copy.js.map +1 -0
  290. package/lib/drivers/subprocess/index.d.ts +24 -0
  291. package/lib/drivers/subprocess/index.d.ts.map +1 -0
  292. package/lib/drivers/subprocess/index.js +451 -0
  293. package/lib/drivers/subprocess/index.js.map +1 -0
  294. package/lib/index.d.ts +40 -0
  295. package/lib/index.d.ts.map +1 -0
  296. package/lib/index.js +48 -0
  297. package/lib/index.js.map +1 -0
  298. package/lib/optimization/reflective.d.ts +27 -0
  299. package/lib/optimization/reflective.d.ts.map +1 -0
  300. package/lib/optimization/reflective.js +257 -0
  301. package/lib/optimization/reflective.js.map +1 -0
  302. package/lib/orchestrator/annotation-loader.d.ts +65 -0
  303. package/lib/orchestrator/annotation-loader.d.ts.map +1 -0
  304. package/lib/orchestrator/annotation-loader.js +73 -0
  305. package/lib/orchestrator/annotation-loader.js.map +1 -0
  306. package/lib/orchestrator/annotation-store.d.ts +3 -0
  307. package/lib/orchestrator/annotation-store.d.ts.map +1 -0
  308. package/lib/orchestrator/annotation-store.js +100 -0
  309. package/lib/orchestrator/annotation-store.js.map +1 -0
  310. package/lib/orchestrator/authored-dependencies.d.ts +3 -0
  311. package/lib/orchestrator/authored-dependencies.d.ts.map +1 -0
  312. package/lib/orchestrator/authored-dependencies.js +146 -0
  313. package/lib/orchestrator/authored-dependencies.js.map +1 -0
  314. package/lib/orchestrator/campaign-lifecycle.d.ts +105 -0
  315. package/lib/orchestrator/campaign-lifecycle.d.ts.map +1 -0
  316. package/lib/orchestrator/campaign-lifecycle.js +141 -0
  317. package/lib/orchestrator/campaign-lifecycle.js.map +1 -0
  318. package/lib/orchestrator/candidate-integrity.d.ts +4 -0
  319. package/lib/orchestrator/candidate-integrity.d.ts.map +1 -0
  320. package/lib/orchestrator/candidate-integrity.js +14 -0
  321. package/lib/orchestrator/candidate-integrity.js.map +1 -0
  322. package/lib/orchestrator/candidates.d.ts +13 -0
  323. package/lib/orchestrator/candidates.d.ts.map +1 -0
  324. package/lib/orchestrator/candidates.js +577 -0
  325. package/lib/orchestrator/candidates.js.map +1 -0
  326. package/lib/orchestrator/compare.d.ts +80 -0
  327. package/lib/orchestrator/compare.d.ts.map +1 -0
  328. package/lib/orchestrator/compare.js +270 -0
  329. package/lib/orchestrator/compare.js.map +1 -0
  330. package/lib/orchestrator/comparison-identity.d.ts +10 -0
  331. package/lib/orchestrator/comparison-identity.d.ts.map +1 -0
  332. package/lib/orchestrator/comparison-identity.js +39 -0
  333. package/lib/orchestrator/comparison-identity.js.map +1 -0
  334. package/lib/orchestrator/config.d.ts +121 -0
  335. package/lib/orchestrator/config.d.ts.map +1 -0
  336. package/lib/orchestrator/config.js +176 -0
  337. package/lib/orchestrator/config.js.map +1 -0
  338. package/lib/orchestrator/content-bound-json.d.ts +5 -0
  339. package/lib/orchestrator/content-bound-json.d.ts.map +1 -0
  340. package/lib/orchestrator/content-bound-json.js +59 -0
  341. package/lib/orchestrator/content-bound-json.js.map +1 -0
  342. package/lib/orchestrator/discover.d.ts +45 -0
  343. package/lib/orchestrator/discover.d.ts.map +1 -0
  344. package/lib/orchestrator/discover.js +98 -0
  345. package/lib/orchestrator/discover.js.map +1 -0
  346. package/lib/orchestrator/doctor.d.ts +141 -0
  347. package/lib/orchestrator/doctor.d.ts.map +1 -0
  348. package/lib/orchestrator/doctor.js +525 -0
  349. package/lib/orchestrator/doctor.js.map +1 -0
  350. package/lib/orchestrator/driver-session.d.ts +177 -0
  351. package/lib/orchestrator/driver-session.d.ts.map +1 -0
  352. package/lib/orchestrator/driver-session.js +396 -0
  353. package/lib/orchestrator/driver-session.js.map +1 -0
  354. package/lib/orchestrator/evaluator-identity.d.ts +7 -0
  355. package/lib/orchestrator/evaluator-identity.d.ts.map +1 -0
  356. package/lib/orchestrator/evaluator-identity.js +42 -0
  357. package/lib/orchestrator/evaluator-identity.js.map +1 -0
  358. package/lib/orchestrator/experiment-budget.d.ts +11 -0
  359. package/lib/orchestrator/experiment-budget.d.ts.map +1 -0
  360. package/lib/orchestrator/experiment-budget.js +51 -0
  361. package/lib/orchestrator/experiment-budget.js.map +1 -0
  362. package/lib/orchestrator/experiment-context.d.ts +43 -0
  363. package/lib/orchestrator/experiment-context.d.ts.map +1 -0
  364. package/lib/orchestrator/experiment-context.js +9 -0
  365. package/lib/orchestrator/experiment-context.js.map +1 -0
  366. package/lib/orchestrator/experiment-contracts.d.ts +290 -0
  367. package/lib/orchestrator/experiment-contracts.d.ts.map +1 -0
  368. package/lib/orchestrator/experiment-contracts.js +38 -0
  369. package/lib/orchestrator/experiment-contracts.js.map +1 -0
  370. package/lib/orchestrator/experiment-corpus.d.ts +21 -0
  371. package/lib/orchestrator/experiment-corpus.d.ts.map +1 -0
  372. package/lib/orchestrator/experiment-corpus.js +276 -0
  373. package/lib/orchestrator/experiment-corpus.js.map +1 -0
  374. package/lib/orchestrator/experiment-evaluation.d.ts +44 -0
  375. package/lib/orchestrator/experiment-evaluation.d.ts.map +1 -0
  376. package/lib/orchestrator/experiment-evaluation.js +542 -0
  377. package/lib/orchestrator/experiment-evaluation.js.map +1 -0
  378. package/lib/orchestrator/experiment-finish.d.ts +6 -0
  379. package/lib/orchestrator/experiment-finish.d.ts.map +1 -0
  380. package/lib/orchestrator/experiment-finish.js +19 -0
  381. package/lib/orchestrator/experiment-finish.js.map +1 -0
  382. package/lib/orchestrator/experiment-identity.d.ts +6 -0
  383. package/lib/orchestrator/experiment-identity.d.ts.map +1 -0
  384. package/lib/orchestrator/experiment-identity.js +257 -0
  385. package/lib/orchestrator/experiment-identity.js.map +1 -0
  386. package/lib/orchestrator/experiment-loader.d.ts +175 -0
  387. package/lib/orchestrator/experiment-loader.d.ts.map +1 -0
  388. package/lib/orchestrator/experiment-loader.js +77 -0
  389. package/lib/orchestrator/experiment-loader.js.map +1 -0
  390. package/lib/orchestrator/experiment-report.d.ts +450 -0
  391. package/lib/orchestrator/experiment-report.d.ts.map +1 -0
  392. package/lib/orchestrator/experiment-report.js +570 -0
  393. package/lib/orchestrator/experiment-report.js.map +1 -0
  394. package/lib/orchestrator/experiment-runs.d.ts +379 -0
  395. package/lib/orchestrator/experiment-runs.d.ts.map +1 -0
  396. package/lib/orchestrator/experiment-runs.js +796 -0
  397. package/lib/orchestrator/experiment-runs.js.map +1 -0
  398. package/lib/orchestrator/experiment-runtime-identity.d.ts +41 -0
  399. package/lib/orchestrator/experiment-runtime-identity.d.ts.map +1 -0
  400. package/lib/orchestrator/experiment-runtime-identity.js +411 -0
  401. package/lib/orchestrator/experiment-runtime-identity.js.map +1 -0
  402. package/lib/orchestrator/experiment-selection.d.ts +25 -0
  403. package/lib/orchestrator/experiment-selection.d.ts.map +1 -0
  404. package/lib/orchestrator/experiment-selection.js +286 -0
  405. package/lib/orchestrator/experiment-selection.js.map +1 -0
  406. package/lib/orchestrator/experiment.d.ts +11 -0
  407. package/lib/orchestrator/experiment.d.ts.map +1 -0
  408. package/lib/orchestrator/experiment.js +639 -0
  409. package/lib/orchestrator/experiment.js.map +1 -0
  410. package/lib/orchestrator/fixture-identity.d.ts +7 -0
  411. package/lib/orchestrator/fixture-identity.d.ts.map +1 -0
  412. package/lib/orchestrator/fixture-identity.js +55 -0
  413. package/lib/orchestrator/fixture-identity.js.map +1 -0
  414. package/lib/orchestrator/fs-read.d.ts +19 -0
  415. package/lib/orchestrator/fs-read.d.ts.map +1 -0
  416. package/lib/orchestrator/fs-read.js +113 -0
  417. package/lib/orchestrator/fs-read.js.map +1 -0
  418. package/lib/orchestrator/grader-experiment.d.ts +190 -0
  419. package/lib/orchestrator/grader-experiment.d.ts.map +1 -0
  420. package/lib/orchestrator/grader-experiment.js +579 -0
  421. package/lib/orchestrator/grader-experiment.js.map +1 -0
  422. package/lib/orchestrator/grading.d.ts +31 -0
  423. package/lib/orchestrator/grading.d.ts.map +1 -0
  424. package/lib/orchestrator/grading.js +186 -0
  425. package/lib/orchestrator/grading.js.map +1 -0
  426. package/lib/orchestrator/index.d.ts +22 -0
  427. package/lib/orchestrator/index.d.ts.map +1 -0
  428. package/lib/orchestrator/index.js +30 -0
  429. package/lib/orchestrator/index.js.map +1 -0
  430. package/lib/orchestrator/lifecycle-hook-execution.d.ts +4 -0
  431. package/lib/orchestrator/lifecycle-hook-execution.d.ts.map +1 -0
  432. package/lib/orchestrator/lifecycle-hook-execution.js +67 -0
  433. package/lib/orchestrator/lifecycle-hook-execution.js.map +1 -0
  434. package/lib/orchestrator/list-runs.d.ts +33 -0
  435. package/lib/orchestrator/list-runs.d.ts.map +1 -0
  436. package/lib/orchestrator/list-runs.js +308 -0
  437. package/lib/orchestrator/list-runs.js.map +1 -0
  438. package/lib/orchestrator/loader.d.ts +3 -0
  439. package/lib/orchestrator/loader.d.ts.map +1 -0
  440. package/lib/orchestrator/loader.js +17 -0
  441. package/lib/orchestrator/loader.js.map +1 -0
  442. package/lib/orchestrator/optimization-lifecycle.d.ts +12 -0
  443. package/lib/orchestrator/optimization-lifecycle.d.ts.map +1 -0
  444. package/lib/orchestrator/optimization-lifecycle.js +92 -0
  445. package/lib/orchestrator/optimization-lifecycle.js.map +1 -0
  446. package/lib/orchestrator/orchestrator.d.ts +144 -0
  447. package/lib/orchestrator/orchestrator.d.ts.map +1 -0
  448. package/lib/orchestrator/orchestrator.js +632 -0
  449. package/lib/orchestrator/orchestrator.js.map +1 -0
  450. package/lib/orchestrator/owned-lock.d.ts +17 -0
  451. package/lib/orchestrator/owned-lock.d.ts.map +1 -0
  452. package/lib/orchestrator/owned-lock.js +149 -0
  453. package/lib/orchestrator/owned-lock.js.map +1 -0
  454. package/lib/orchestrator/package-version.d.ts +2 -0
  455. package/lib/orchestrator/package-version.d.ts.map +1 -0
  456. package/lib/orchestrator/package-version.js +4 -0
  457. package/lib/orchestrator/package-version.js.map +1 -0
  458. package/lib/orchestrator/provenance.d.ts +5 -0
  459. package/lib/orchestrator/provenance.d.ts.map +1 -0
  460. package/lib/orchestrator/provenance.js +137 -0
  461. package/lib/orchestrator/provenance.js.map +1 -0
  462. package/lib/orchestrator/regrade.d.ts +80 -0
  463. package/lib/orchestrator/regrade.d.ts.map +1 -0
  464. package/lib/orchestrator/regrade.js +433 -0
  465. package/lib/orchestrator/regrade.js.map +1 -0
  466. package/lib/orchestrator/rubric-loader.d.ts +7 -0
  467. package/lib/orchestrator/rubric-loader.d.ts.map +1 -0
  468. package/lib/orchestrator/rubric-loader.js +65 -0
  469. package/lib/orchestrator/rubric-loader.js.map +1 -0
  470. package/lib/orchestrator/run-dir.d.ts +40 -0
  471. package/lib/orchestrator/run-dir.d.ts.map +1 -0
  472. package/lib/orchestrator/run-dir.js +161 -0
  473. package/lib/orchestrator/run-dir.js.map +1 -0
  474. package/lib/orchestrator/run-execution.d.ts +30 -0
  475. package/lib/orchestrator/run-execution.d.ts.map +1 -0
  476. package/lib/orchestrator/run-execution.js +531 -0
  477. package/lib/orchestrator/run-execution.js.map +1 -0
  478. package/lib/orchestrator/run-lifecycle.d.ts +115 -0
  479. package/lib/orchestrator/run-lifecycle.d.ts.map +1 -0
  480. package/lib/orchestrator/run-lifecycle.js +136 -0
  481. package/lib/orchestrator/run-lifecycle.js.map +1 -0
  482. package/lib/orchestrator/suite-compare.d.ts +76 -0
  483. package/lib/orchestrator/suite-compare.d.ts.map +1 -0
  484. package/lib/orchestrator/suite-compare.js +159 -0
  485. package/lib/orchestrator/suite-compare.js.map +1 -0
  486. package/lib/orchestrator/suite-lifecycle.d.ts +157 -0
  487. package/lib/orchestrator/suite-lifecycle.d.ts.map +1 -0
  488. package/lib/orchestrator/suite-lifecycle.js +253 -0
  489. package/lib/orchestrator/suite-lifecycle.js.map +1 -0
  490. package/lib/orchestrator/suite-runs.d.ts +91 -0
  491. package/lib/orchestrator/suite-runs.d.ts.map +1 -0
  492. package/lib/orchestrator/suite-runs.js +157 -0
  493. package/lib/orchestrator/suite-runs.js.map +1 -0
  494. package/lib/orchestrator/suite-summary.d.ts +109 -0
  495. package/lib/orchestrator/suite-summary.d.ts.map +1 -0
  496. package/lib/orchestrator/suite-summary.js +213 -0
  497. package/lib/orchestrator/suite-summary.js.map +1 -0
  498. package/lib/orchestrator/trace-limits.d.ts +20 -0
  499. package/lib/orchestrator/trace-limits.d.ts.map +1 -0
  500. package/lib/orchestrator/trace-limits.js +28 -0
  501. package/lib/orchestrator/trace-limits.js.map +1 -0
  502. package/lib/orchestrator/validate-plugin-config.d.ts +10 -0
  503. package/lib/orchestrator/validate-plugin-config.d.ts.map +1 -0
  504. package/lib/orchestrator/validate-plugin-config.js +26 -0
  505. package/lib/orchestrator/validate-plugin-config.js.map +1 -0
  506. package/package.json +84 -0
  507. package/skills/lux/SKILL.md +85 -0
  508. package/skills/lux/references/cli.md +102 -0
  509. package/skills/lux/references/eval-authoring.md +105 -0
  510. package/skills/lux-answerer/SKILL.md +169 -0
  511. package/src/answerers/claude-code/index.ts +342 -0
  512. package/src/answerers/persona/index.ts +327 -0
  513. package/src/answerers/scripted/index.ts +186 -0
  514. package/src/answerers/shared.ts +23 -0
  515. package/src/answerers/terminal/index.ts +117 -0
  516. package/src/assertions/core/command-exits-zero.ts +94 -0
  517. package/src/assertions/core/event-checks.ts +138 -0
  518. package/src/assertions/core/file-checks.ts +336 -0
  519. package/src/assertions/core/helpers.ts +203 -0
  520. package/src/assertions/core/index.ts +80 -0
  521. package/src/assertions/core/json-pointer-equals.ts +84 -0
  522. package/src/assertions/core/output-checks.ts +134 -0
  523. package/src/assertions/core/predicate.ts +67 -0
  524. package/src/assertions/core/resource-checks.ts +180 -0
  525. package/src/assertions/core/safe-regex.ts +196 -0
  526. package/src/assertions/core/tool-called-with.ts +68 -0
  527. package/src/assertions/core/tool-calls.ts +28 -0
  528. package/src/assertions/core/tool-invocation.ts +198 -0
  529. package/src/assertions/core/tool-result-checks.ts +211 -0
  530. package/src/assertions/mcp/discovery-checks.ts +139 -0
  531. package/src/assertions/mcp/index.ts +37 -0
  532. package/src/assertions/rubric/index.ts +258 -0
  533. package/src/assertions/rubric/internal.ts +367 -0
  534. package/src/authoring.ts +2 -0
  535. package/src/cli/bin.ts +14 -0
  536. package/src/cli/command-runtime.ts +34 -0
  537. package/src/cli/init.ts +410 -0
  538. package/src/cli/program.ts +992 -0
  539. package/src/cli/render/format.ts +69 -0
  540. package/src/cli/render/reporter.ts +643 -0
  541. package/src/cli/render/tty.ts +314 -0
  542. package/src/cli/run-options.ts +82 -0
  543. package/src/cli/skills.ts +129 -0
  544. package/src/core/annotation.ts +104 -0
  545. package/src/core/answerer.ts +75 -0
  546. package/src/core/artifact-evidence.ts +249 -0
  547. package/src/core/artifact-snapshot.ts +429 -0
  548. package/src/core/assertion.ts +156 -0
  549. package/src/core/case.ts +103 -0
  550. package/src/core/conversation-turn.ts +47 -0
  551. package/src/core/driver.ts +75 -0
  552. package/src/core/emitter.ts +38 -0
  553. package/src/core/events.ts +135 -0
  554. package/src/core/exec.ts +263 -0
  555. package/src/core/experiment.ts +467 -0
  556. package/src/core/glob.ts +31 -0
  557. package/src/core/hash.ts +237 -0
  558. package/src/core/index.ts +28 -0
  559. package/src/core/lifecycle-fixtures.ts +145 -0
  560. package/src/core/lifecycle-hooks.ts +101 -0
  561. package/src/core/mcp-events.ts +57 -0
  562. package/src/core/model-cli.ts +271 -0
  563. package/src/core/observer.ts +50 -0
  564. package/src/core/platform-process.ts +284 -0
  565. package/src/core/registry.ts +57 -0
  566. package/src/core/rubric.ts +52 -0
  567. package/src/core/run.ts +144 -0
  568. package/src/core/statistics.ts +194 -0
  569. package/src/core/subject.ts +50 -0
  570. package/src/core/tool-events.ts +292 -0
  571. package/src/core/usage.ts +123 -0
  572. package/src/core/wire.ts +135 -0
  573. package/src/core/zod.ts +20 -0
  574. package/src/drivers/claude-code/index.ts +866 -0
  575. package/src/drivers/claude-code/parse-events.ts +464 -0
  576. package/src/drivers/codex/app-events.ts +218 -0
  577. package/src/drivers/codex/config.ts +128 -0
  578. package/src/drivers/codex/index.ts +995 -0
  579. package/src/drivers/mcp/index.ts +351 -0
  580. package/src/drivers/mcp/transport.ts +222 -0
  581. package/src/drivers/shared/artifacts.ts +86 -0
  582. package/src/drivers/shared/secure-copy.ts +40 -0
  583. package/src/drivers/subprocess/index.ts +490 -0
  584. package/src/index.ts +594 -0
  585. package/src/optimization/reflective.ts +319 -0
  586. package/src/orchestrator/annotation-loader.ts +97 -0
  587. package/src/orchestrator/annotation-store.ts +112 -0
  588. package/src/orchestrator/authored-dependencies.ts +193 -0
  589. package/src/orchestrator/campaign-lifecycle.ts +181 -0
  590. package/src/orchestrator/candidate-integrity.ts +18 -0
  591. package/src/orchestrator/candidates.ts +683 -0
  592. package/src/orchestrator/compare.ts +358 -0
  593. package/src/orchestrator/comparison-identity.ts +45 -0
  594. package/src/orchestrator/config.ts +205 -0
  595. package/src/orchestrator/content-bound-json.ts +71 -0
  596. package/src/orchestrator/discover.ts +132 -0
  597. package/src/orchestrator/doctor.ts +781 -0
  598. package/src/orchestrator/driver-session.ts +507 -0
  599. package/src/orchestrator/evaluator-identity.ts +45 -0
  600. package/src/orchestrator/experiment-budget.ts +61 -0
  601. package/src/orchestrator/experiment-context.ts +41 -0
  602. package/src/orchestrator/experiment-contracts.ts +43 -0
  603. package/src/orchestrator/experiment-corpus.ts +416 -0
  604. package/src/orchestrator/experiment-evaluation.ts +749 -0
  605. package/src/orchestrator/experiment-finish.ts +33 -0
  606. package/src/orchestrator/experiment-identity.ts +356 -0
  607. package/src/orchestrator/experiment-loader.ts +105 -0
  608. package/src/orchestrator/experiment-report.ts +788 -0
  609. package/src/orchestrator/experiment-runs.ts +960 -0
  610. package/src/orchestrator/experiment-runtime-identity.ts +465 -0
  611. package/src/orchestrator/experiment-selection.ts +467 -0
  612. package/src/orchestrator/experiment.ts +991 -0
  613. package/src/orchestrator/fixture-identity.ts +67 -0
  614. package/src/orchestrator/fs-read.ts +126 -0
  615. package/src/orchestrator/grader-experiment.ts +856 -0
  616. package/src/orchestrator/grading.ts +260 -0
  617. package/src/orchestrator/index.ts +186 -0
  618. package/src/orchestrator/lifecycle-hook-execution.ts +90 -0
  619. package/src/orchestrator/list-runs.ts +384 -0
  620. package/src/orchestrator/loader.ts +19 -0
  621. package/src/orchestrator/optimization-lifecycle.ts +126 -0
  622. package/src/orchestrator/orchestrator.ts +919 -0
  623. package/src/orchestrator/owned-lock.ts +147 -0
  624. package/src/orchestrator/package-version.ts +5 -0
  625. package/src/orchestrator/provenance.ts +140 -0
  626. package/src/orchestrator/regrade.ts +580 -0
  627. package/src/orchestrator/rubric-loader.ts +74 -0
  628. package/src/orchestrator/run-dir.ts +209 -0
  629. package/src/orchestrator/run-execution.ts +694 -0
  630. package/src/orchestrator/run-lifecycle.ts +174 -0
  631. package/src/orchestrator/suite-compare.ts +195 -0
  632. package/src/orchestrator/suite-lifecycle.ts +335 -0
  633. package/src/orchestrator/suite-runs.ts +202 -0
  634. package/src/orchestrator/suite-summary.ts +276 -0
  635. package/src/orchestrator/trace-limits.ts +65 -0
  636. package/src/orchestrator/validate-plugin-config.ts +34 -0
@@ -0,0 +1,866 @@
1
+ import { cp, mkdir, readdir, realpath, rm, writeFile } from "node:fs/promises";
2
+ import { homedir } from "node:os";
3
+ import { dirname, join, resolve, sep } from "node:path";
4
+ import { temporaryDirectory } from "tempy";
5
+ import { z } from "zod";
6
+ import {
7
+ type AgentDriver,
8
+ type Answer,
9
+ type Artifact,
10
+ DEFAULT_CLAUDE_MODEL,
11
+ type DriverEventMap,
12
+ type DriverPlugin,
13
+ discoverCliVersion,
14
+ exec,
15
+ type ParsedDriverEvent,
16
+ type ReadyState,
17
+ type StartContext,
18
+ TypedEmitter,
19
+ } from "../../core/index.js";
20
+ import { walkArtifacts } from "../shared/artifacts.js";
21
+ import { copyRegularFileNoFollow } from "../shared/secure-copy.js";
22
+ import { subprocessDriver } from "../subprocess/index.js";
23
+ import { ClaudeCodeLineSchema, makeParseEvent } from "./parse-events.js";
24
+
25
+ const LUX_BOOKKEEPING_FILES = new Set([
26
+ "events.jsonl",
27
+ "case.json",
28
+ "run.json",
29
+ "artifacts.json",
30
+ "grading.json",
31
+ ".lux-claude-hitl-hook.cjs",
32
+ ]);
33
+ const commandName = (command: string): string => command.split(/[\\/]/).at(-1) ?? command;
34
+ const ISOLATION_RESERVED_ARGS = new Set([
35
+ "--add-dir",
36
+ "--agent",
37
+ "--agents",
38
+ "--bare",
39
+ "--mcp-config",
40
+ "--no-session-persistence",
41
+ "--plugin-dir",
42
+ "--plugin-url",
43
+ "--safe-mode",
44
+ "--setting-sources",
45
+ "--settings",
46
+ "--strict-mcp-config",
47
+ ]);
48
+ const MINIMAL_ALLOWED_TOOLS = ["Read", "Write", "Edit", "Bash", "Glob", "Grep"] as const;
49
+
50
+ const IsolationSchema = z
51
+ .object({
52
+ /**
53
+ * Additional read-only trees available to Bash in an isolated run. Plugin
54
+ * and `addDirs` paths are included automatically.
55
+ */
56
+ allowRead: z.array(z.string()).default([]),
57
+ })
58
+ .strict();
59
+
60
+ export const ClaudeCodeDriverConfigSchema = z
61
+ .object({
62
+ command: z.string().default("claude"),
63
+ /** Extra args appended to the default arg list. */
64
+ extraArgs: z.array(z.string()).optional(),
65
+ /** Where Claude Code runs. "artifacts" maps to ctx.artifactsDir. */
66
+ cwdMode: z.enum(["artifacts", "runDir"]).default("artifacts"),
67
+ cwd: z.string().optional(),
68
+ env: z.record(z.string(), z.string()).optional(),
69
+ /** Low-cost by default; choose a larger model explicitly when needed. */
70
+ model: z.string().min(1).default(DEFAULT_CLAUDE_MODEL),
71
+ /** Claude effort level; low by default to control eval cost. */
72
+ effort: z.enum(["low", "medium", "high", "max"]).default("low"),
73
+ /** Permission mode passed to claude. */
74
+ permissionMode: z
75
+ .enum(["acceptEdits", "auto", "bypassPermissions", "default", "dontAsk", "manual", "plan"])
76
+ .optional(),
77
+ /** Additional tools pre-approved beyond Lux's minimal coding and ask tools. */
78
+ allowedTools: z.array(z.string().min(1)).optional(),
79
+ /** Plugin directories to load (`--plugin-dir`): skills, subagents, commands, hooks. */
80
+ pluginDirs: z.array(z.string()).optional(),
81
+ /** Route the session to a named subagent (`--agent`); bare or namespaced name. */
82
+ agent: z.string().optional(),
83
+ /** Extra readable directories (`--add-dir`). Grant the narrowest tree the case needs. */
84
+ addDirs: z.array(z.string()).optional(),
85
+ /**
86
+ * Run in a disposable workspace with local memory, persisted runs, and
87
+ * standard credential stores denied. Live network access is retained.
88
+ */
89
+ isolation: IsolationSchema.optional(),
90
+ /** Tool name that, when invoked, surfaces as an `ask` interrupt. */
91
+ askToolName: z.string().default("AskUserQuestion"),
92
+ /**
93
+ * `defer-resume` uses Claude Code's persisted tool deferral protocol.
94
+ * `stream-json` is the legacy live-stdin protocol for compatible stubs.
95
+ */
96
+ interactionMode: z.enum(["defer-resume", "stream-json"]).default("stream-json"),
97
+ /**
98
+ * Treat a terminal agent message containing a question as an interrupt
99
+ * when a weaker model asks in text instead of calling `askToolName`.
100
+ */
101
+ textQuestionFallback: z.boolean().default(false),
102
+ /** Per-event idle timeout. */
103
+ idleTimeoutMs: z.number().positive().default(600_000),
104
+ /** Cap on outstanding interrupts before the driver aborts. */
105
+ maxInterrupts: z.number().positive().default(20),
106
+ })
107
+ .strict()
108
+ .superRefine((config, ctx) => {
109
+ if (config.interactionMode === "defer-resume") {
110
+ const reserved = config.extraArgs?.find((arg) =>
111
+ ["--input-format", "--no-session-persistence", "--resume", "-r", "--settings"].some(
112
+ (name) => arg === name || arg.startsWith(`${name}=`),
113
+ ),
114
+ );
115
+ if (reserved) {
116
+ ctx.addIssue({
117
+ code: "custom",
118
+ path: ["extraArgs"],
119
+ message: `defer-resume owns ${reserved}`,
120
+ });
121
+ }
122
+ }
123
+ if (!config.isolation) return;
124
+ if (config.permissionMode && config.permissionMode !== "default") {
125
+ ctx.addIssue({
126
+ code: "custom",
127
+ path: ["permissionMode"],
128
+ message:
129
+ "isolation requires permissionMode to be omitted or 'default'; Claude Code forces default mode while subprocess environment scrubbing is enabled",
130
+ });
131
+ }
132
+ if (config.interactionMode === "defer-resume") {
133
+ ctx.addIssue({
134
+ code: "custom",
135
+ path: ["interactionMode"],
136
+ message: "defer-resume requires session persistence and is incompatible with isolation",
137
+ });
138
+ }
139
+ if (config.cwd || config.cwdMode !== "artifacts") {
140
+ ctx.addIssue({
141
+ code: "custom",
142
+ path: ["isolation"],
143
+ message: "isolation requires cwdMode 'artifacts' and does not allow an explicit cwd",
144
+ });
145
+ }
146
+ const reserved = config.extraArgs?.find((arg) =>
147
+ [...ISOLATION_RESERVED_ARGS].some((name) => arg === name || arg.startsWith(`${name}=`)),
148
+ );
149
+ if (reserved) {
150
+ ctx.addIssue({
151
+ code: "custom",
152
+ path: ["extraArgs"],
153
+ message: `isolation owns ${reserved}; configure isolation instead`,
154
+ });
155
+ }
156
+ });
157
+
158
+ export type ClaudeCodeDriverConfig = z.infer<typeof ClaudeCodeDriverConfigSchema>;
159
+
160
+ const buildArgs = (config: ClaudeCodeDriverConfig): string[] => {
161
+ const out = [
162
+ "--print",
163
+ "--output-format",
164
+ "stream-json",
165
+ "--input-format",
166
+ "stream-json",
167
+ "--verbose",
168
+ ];
169
+ out.push("--model", config.model, "--effort", config.effort);
170
+ if (config.permissionMode) out.push("--permission-mode", config.permissionMode);
171
+ out.push("--allowed-tools", ...allowedTools(config));
172
+ for (const dir of config.pluginDirs ?? []) out.push("--plugin-dir", dir);
173
+ if (config.agent) out.push("--agent", config.agent);
174
+ for (const dir of config.addDirs ?? []) out.push("--add-dir", dir);
175
+ if (config.extraArgs) out.push(...config.extraArgs);
176
+ return out;
177
+ };
178
+
179
+ const shellArgument = (value: string): string => `'${value.replaceAll("'", "'\\''")}'`;
180
+
181
+ const deferredHookSettings = (
182
+ config: ClaudeCodeDriverConfig,
183
+ hookPath: string,
184
+ ): Record<string, unknown> => ({
185
+ hooks: {
186
+ PreToolUse: [
187
+ {
188
+ matcher: config.askToolName,
189
+ hooks: [
190
+ {
191
+ type: "command",
192
+ command: `${shellArgument(process.execPath)} ${shellArgument(hookPath)}`,
193
+ },
194
+ ],
195
+ },
196
+ ],
197
+ },
198
+ });
199
+
200
+ const allowedTools = (config: ClaudeCodeDriverConfig): string[] => [
201
+ ...new Set([...MINIMAL_ALLOWED_TOOLS, config.askToolName, ...(config.allowedTools ?? [])]),
202
+ ];
203
+
204
+ const buildDeferredArgs = (
205
+ config: ClaudeCodeDriverConfig,
206
+ hookPath: string,
207
+ sessionId?: string,
208
+ ): string[] => {
209
+ const out = ["--print", "--output-format", "stream-json", "--verbose"];
210
+ out.push("--model", config.model, "--effort", config.effort);
211
+ if (config.permissionMode) out.push("--permission-mode", config.permissionMode);
212
+ out.push("--allowed-tools", ...allowedTools(config));
213
+ for (const dir of config.pluginDirs ?? []) out.push("--plugin-dir", dir);
214
+ if (config.agent) out.push("--agent", config.agent);
215
+ for (const dir of config.addDirs ?? []) out.push("--add-dir", dir);
216
+ out.push("--settings", JSON.stringify(deferredHookSettings(config, hookPath)));
217
+ if (sessionId) out.push("--resume", sessionId);
218
+ if (config.extraArgs) out.push(...config.extraArgs);
219
+ return out;
220
+ };
221
+
222
+ const DEFERRED_HOOK_SOURCE = `let input = "";
223
+ process.stdin.setEncoding("utf8");
224
+ process.stdin.on("data", (chunk) => { input += chunk; });
225
+ process.stdin.on("end", () => {
226
+ const hook = JSON.parse(input);
227
+ const raw = process.env.LUX_CLAUDE_DEFERRED_ANSWER;
228
+ const answer = raw ? JSON.parse(raw) : null;
229
+ const defer = () => process.stdout.write(JSON.stringify({
230
+ hookSpecificOutput: {
231
+ hookEventName: "PreToolUse",
232
+ permissionDecision: "defer"
233
+ }
234
+ }));
235
+ if (!answer || answer.id !== hook.tool_use_id) return defer();
236
+ const questions = Array.isArray(hook.tool_input?.questions) ? hook.tool_input.questions : [];
237
+ let answers;
238
+ if (answer.kind === "ask") {
239
+ try {
240
+ const parsed = JSON.parse(answer.text);
241
+ answers = parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
242
+ } catch {
243
+ answers = null;
244
+ }
245
+ answers ??= Object.fromEntries(questions
246
+ .map((question) => question?.question)
247
+ .filter((question) => typeof question === "string")
248
+ .map((question) => [question, answer.text]));
249
+ } else {
250
+ answers = {};
251
+ }
252
+ if (questions.length === 0) {
253
+ return process.stdout.write(JSON.stringify({
254
+ hookSpecificOutput: {
255
+ hookEventName: "PreToolUse",
256
+ permissionDecision: "allow",
257
+ updatedInput: hook.tool_input,
258
+ additionalContext: \`The user responded to this message: \${answer.text}\`
259
+ }
260
+ }));
261
+ }
262
+ process.stdout.write(JSON.stringify({
263
+ hookSpecificOutput: {
264
+ hookEventName: "PreToolUse",
265
+ permissionDecision: "allow",
266
+ updatedInput: { ...hook.tool_input, answers }
267
+ }
268
+ }));
269
+ });
270
+ `;
271
+
272
+ const isWithin = (parent: string, child: string): boolean =>
273
+ child === parent || child.startsWith(`${parent}${sep}`);
274
+
275
+ const permissionPath = (tool: "Edit" | "Read", path: string, directory: boolean): string => {
276
+ const absolute = resolve(path).replaceAll("\\", "/");
277
+ return `${tool}(//${absolute.replace(/^\/+/, "")}${directory ? "/**" : ""})`;
278
+ };
279
+
280
+ /**
281
+ * Permission rules have no "allow inside deny" override, so isolate a root by
282
+ * denying every sibling around explicitly allowed descendants.
283
+ */
284
+ const carvedPermissionDenials = async (
285
+ tool: "Edit" | "Read",
286
+ root: string,
287
+ allowed: readonly string[],
288
+ ): Promise<string[]> => {
289
+ const normalizedRoot = resolve(root);
290
+ const inside = allowed
291
+ .map((path) => resolve(path))
292
+ .filter((path) => isWithin(normalizedRoot, path));
293
+ if (inside.some((path) => path === normalizedRoot)) return [];
294
+ if (inside.length === 0) return [permissionPath(tool, normalizedRoot, true)];
295
+
296
+ const out: string[] = [];
297
+ const walk = async (dir: string): Promise<void> => {
298
+ const entries = await readdir(dir, { withFileTypes: true });
299
+ entries.sort((a, b) => a.name.localeCompare(b.name));
300
+ for (const entry of entries) {
301
+ const path = join(dir, entry.name);
302
+ if (inside.some((allowedPath) => allowedPath === path)) continue;
303
+ if (entry.isDirectory() && inside.some((allowedPath) => isWithin(path, allowedPath))) {
304
+ await walk(path);
305
+ } else {
306
+ out.push(permissionPath(tool, path, entry.isDirectory()));
307
+ }
308
+ }
309
+ };
310
+ await walk(normalizedRoot);
311
+ return out;
312
+ };
313
+
314
+ const carvedReadDenials = (root: string, allowed: readonly string[]): Promise<string[]> =>
315
+ carvedPermissionDenials("Read", root, allowed);
316
+
317
+ const existingRealpaths = async (paths: readonly string[]): Promise<string[]> => {
318
+ const found = await Promise.all(
319
+ paths.map(async (path) => {
320
+ try {
321
+ return await realpath(resolve(path));
322
+ } catch {
323
+ return null;
324
+ }
325
+ }),
326
+ );
327
+ return [...new Set(found.filter((path): path is string => path !== null))];
328
+ };
329
+
330
+ const compactRoots = (paths: readonly string[]): string[] =>
331
+ [...new Set(paths)]
332
+ .sort((a, b) => a.length - b.length || a.localeCompare(b))
333
+ .filter((path, index, roots) => !roots.slice(0, index).some((root) => isWithin(root, path)));
334
+
335
+ const isolatedLaunch = async (
336
+ config: ClaudeCodeDriverConfig,
337
+ ctx: StartContext,
338
+ workspace: string,
339
+ ): Promise<{ args: string[]; env: Record<string, string> }> => {
340
+ const homeRoots = await existingRealpaths([
341
+ homedir(),
342
+ ...(config.env?.HOME ? [config.env.HOME] : []),
343
+ ]);
344
+ const sensitiveHomePaths = homeRoots.flatMap((home) => [
345
+ join(home, ".aws"),
346
+ join(home, ".config", "gcloud"),
347
+ join(home, ".docker", "config.json"),
348
+ join(home, ".git-credentials"),
349
+ join(home, ".gnupg"),
350
+ join(home, ".kube"),
351
+ join(home, ".netrc"),
352
+ join(home, ".npmrc"),
353
+ join(home, ".ssh"),
354
+ ]);
355
+ const protectedRoots = await existingRealpaths([
356
+ ...(ctx.runsRoot ? [ctx.runsRoot] : [ctx.runDir]),
357
+ ...(process.env.CLAUDE_CONFIG_DIR ? [process.env.CLAUDE_CONFIG_DIR] : []),
358
+ ...(config.env?.CLAUDE_CONFIG_DIR ? [config.env.CLAUDE_CONFIG_DIR] : []),
359
+ ...sensitiveHomePaths,
360
+ ...homeRoots.flatMap((home) => [
361
+ join(home, ".claude", "projects"),
362
+ join(home, ".claude", "memory"),
363
+ ]),
364
+ ]);
365
+ const denyRoots = compactRoots(protectedRoots);
366
+ const allowed = compactRoots(
367
+ await existingRealpaths([
368
+ ...(config.pluginDirs ?? []),
369
+ ...(config.addDirs ?? []),
370
+ ...(config.isolation?.allowRead ?? []),
371
+ ]),
372
+ );
373
+ const overlapping = allowed.find(
374
+ (path) =>
375
+ (path !== workspace && isWithin(path, workspace)) ||
376
+ denyRoots.some((root) => isWithin(path, root)) ||
377
+ protectedRoots.some((root) => isWithin(root, path)),
378
+ );
379
+ if (overlapping) {
380
+ throw new Error(`isolation read carve-out overlaps a protected boundary: ${overlapping}`);
381
+ }
382
+ const readOnlyRoots = allowed.filter((path) => !isWithin(workspace, path));
383
+ const editDenyRoots = compactRoots([...denyRoots, ...readOnlyRoots]);
384
+ const [readDenials, writeDenials] = await Promise.all([
385
+ Promise.all(denyRoots.map((root) => carvedPermissionDenials("Read", root, allowed))),
386
+ Promise.all(editDenyRoots.map((root) => carvedPermissionDenials("Edit", root, []))),
387
+ ]);
388
+ const denyRules = [...readDenials.flat(), ...writeDenials.flat()];
389
+ const sandboxSettings = {
390
+ disableAllHooks: true,
391
+ permissions: { deny: denyRules },
392
+ sandbox: {
393
+ enabled: true,
394
+ failIfUnavailable: true,
395
+ allowUnsandboxedCommands: false,
396
+ filesystem: {
397
+ denyRead: denyRoots,
398
+ denyWrite: denyRoots,
399
+ allowRead: allowed,
400
+ },
401
+ // Filesystem isolation does not disable the live docs and package
402
+ // registry access that cases are intended to exercise.
403
+ network: { allowedDomains: ["*"] },
404
+ },
405
+ };
406
+ const stateDir = join(workspace, ".lux-claude");
407
+ const tmpDir = join(stateDir, "tmp");
408
+ const cacheDir = join(stateDir, "cache");
409
+ const pluginCacheDir = join(stateDir, "plugins");
410
+ await Promise.all([
411
+ mkdir(tmpDir, { recursive: true }),
412
+ mkdir(cacheDir, { recursive: true }),
413
+ mkdir(pluginCacheDir, { recursive: true }),
414
+ ]);
415
+ return {
416
+ args: [
417
+ // --add-dir also discovers skills and agents. Isolation treats addDirs
418
+ // as data-only read grants, so expose them through the filesystem policy
419
+ // above without passing the discovery flag to Claude.
420
+ ...buildArgs({ ...config, addDirs: undefined }),
421
+ "--no-session-persistence",
422
+ "--strict-mcp-config",
423
+ // Authentication is independent of filesystem setting sources. Keep
424
+ // these empty instead of using --safe-mode: safe mode would also disable
425
+ // the explicitly requested plugin's skills, commands, and agents.
426
+ "--setting-sources",
427
+ "",
428
+ "--settings",
429
+ JSON.stringify(sandboxSettings),
430
+ ],
431
+ env: {
432
+ ...(config.env ?? {}),
433
+ CLAUDE_CODE_DISABLE_AUTO_MEMORY: "1",
434
+ CLAUDE_CODE_DISABLE_CLAUDE_MDS: "1",
435
+ CLAUDE_CODE_SKIP_PROMPT_HISTORY: "1",
436
+ CLAUDE_CODE_DISABLE_BACKGROUND_TASKS: "1",
437
+ CLAUDE_CODE_SUBPROCESS_ENV_SCRUB: "1",
438
+ CLAUDE_CODE_AUTO_CONNECT_IDE: "false",
439
+ CLAUDE_CODE_PLUGIN_CACHE_DIR: pluginCacheDir,
440
+ CLAUDE_CODE_DISABLE_OFFICIAL_MARKETPLACE_AUTOINSTALL: "1",
441
+ DISABLE_AUTOUPDATER: "1",
442
+ TMPDIR: tmpDir,
443
+ XDG_CACHE_HOME: cacheDir,
444
+ npm_config_cache: join(cacheDir, "npm"),
445
+ BUN_INSTALL_CACHE_DIR: join(cacheDir, "bun"),
446
+ },
447
+ };
448
+ };
449
+
450
+ const formatPrompt = (text: string): string =>
451
+ `${JSON.stringify({
452
+ type: "user",
453
+ message: { role: "user", content: [{ type: "text", text }] },
454
+ })}\n`;
455
+
456
+ const formatAnswer = (answer: Answer): string => {
457
+ let content: string;
458
+ if (answer.kind === "ask") {
459
+ content = answer.text;
460
+ } else if (answer.kind === "approve") {
461
+ content = `${answer.approved ? "approved" : "denied"}${answer.reason ? `: ${answer.reason}` : ""}`;
462
+ } else {
463
+ content = typeof answer.payload === "string" ? answer.payload : JSON.stringify(answer.payload);
464
+ }
465
+ const event = {
466
+ type: "user",
467
+ message: {
468
+ role: "user",
469
+ content: [{ type: "tool_result", tool_use_id: answer.id, content }],
470
+ },
471
+ };
472
+ return `${JSON.stringify(event)}\n`;
473
+ };
474
+
475
+ class ClaudeCodeDriverImpl implements AgentDriver {
476
+ private inner: AgentDriver | null = null;
477
+ private readonly emitter = new TypedEmitter<DriverEventMap>();
478
+ // The attach closure captures its (event, handler) pair with narrow types,
479
+ // so the queue stays type-safe without per-entry casts; the identity pair
480
+ // rides along so off() can drop a queued attach symmetrically.
481
+ private pendingAttach: Array<{
482
+ event: keyof DriverEventMap;
483
+ handler: unknown;
484
+ attach: (inner: AgentDriver) => void;
485
+ }> = [];
486
+ private readonly config: ClaudeCodeDriverConfig;
487
+ private collectDir: string | null = null;
488
+ private artifactsDir: string | null = null;
489
+ private runDir: string | null = null;
490
+ private isolatedWorkspace: string | null = null;
491
+ private deferredContext: StartContext | null = null;
492
+ private deferredCwd: string | null = null;
493
+ private deferredHookPath: string | null = null;
494
+ private deferredSessionId: string | null = null;
495
+ private deferredParser: ReturnType<typeof makeParseEvent> | null = null;
496
+ private deferredReady:
497
+ | {
498
+ resolve: (ready: ReadyState) => void;
499
+ reject: (error: Error) => void;
500
+ }
501
+ | undefined;
502
+ private deferredPendingId: string | null = null;
503
+ private deferredTextQuestionPending = false;
504
+ private deferredLastAgentText: string | null = null;
505
+ private deferredTextQuestionIndex = 0;
506
+ private deferredInterrupts = 0;
507
+ private deferredTerminal = false;
508
+ private deferredTurn: Promise<void> | null = null;
509
+
510
+ constructor(config: ClaudeCodeDriverConfig) {
511
+ this.config = config;
512
+ }
513
+
514
+ on<K extends keyof DriverEventMap>(event: K, handler: (e: DriverEventMap[K]) => void): void {
515
+ if (this.config.interactionMode === "defer-resume") {
516
+ this.emitter.on(event, handler);
517
+ return;
518
+ }
519
+ if (!this.inner) {
520
+ this.pendingAttach.push({ event, handler, attach: (inner) => inner.on(event, handler) });
521
+ return;
522
+ }
523
+ this.inner.on(event, handler);
524
+ }
525
+
526
+ off<K extends keyof DriverEventMap>(event: K, handler: (e: DriverEventMap[K]) => void): void {
527
+ if (this.config.interactionMode === "defer-resume") {
528
+ this.emitter.off(event, handler);
529
+ return;
530
+ }
531
+ if (!this.inner) {
532
+ this.pendingAttach = this.pendingAttach.filter(
533
+ (queued) => queued.event !== event || queued.handler !== handler,
534
+ );
535
+ return;
536
+ }
537
+ this.inner.off(event, handler);
538
+ }
539
+
540
+ async start(ctx: StartContext): Promise<ReadyState> {
541
+ if (this.config.interactionMode === "defer-resume") {
542
+ return this.startDeferred(ctx);
543
+ }
544
+ let cwd =
545
+ this.config.cwd ?? (this.config.cwdMode === "artifacts" ? ctx.artifactsDir : ctx.runDir);
546
+ let args = buildArgs(this.config);
547
+ let env = this.config.env;
548
+ if (this.config.isolation) {
549
+ const workspace = await realpath(temporaryDirectory({ prefix: "lux-claude-workspace-" }));
550
+ await cp(ctx.artifactsDir, workspace, { recursive: true });
551
+ this.isolatedWorkspace = workspace;
552
+ cwd = workspace;
553
+ const launch = await isolatedLaunch(this.config, ctx, workspace);
554
+ args = launch.args;
555
+ env = launch.env;
556
+ }
557
+ this.collectDir = cwd;
558
+ this.artifactsDir = ctx.artifactsDir;
559
+ this.runDir = ctx.runDir;
560
+ const cliVersion =
561
+ commandName(this.config.command) === "claude"
562
+ ? await discoverCliVersion(this.config.command)
563
+ : undefined;
564
+ const inner = await subprocessDriver.create({
565
+ command: this.config.command,
566
+ args,
567
+ cwd,
568
+ env,
569
+ protocol: "custom",
570
+ parseEvent: makeParseEvent(this.config, cliVersion),
571
+ formatAnswer,
572
+ formatPrompt,
573
+ idleTimeoutMs: this.config.idleTimeoutMs,
574
+ maxInterrupts: this.config.maxInterrupts,
575
+ });
576
+ for (const queued of this.pendingAttach) queued.attach(inner);
577
+ this.pendingAttach = [];
578
+ this.inner = inner;
579
+ return inner.start(ctx);
580
+ }
581
+
582
+ async respond(answer: Answer): Promise<void> {
583
+ if (this.config.interactionMode === "defer-resume") {
584
+ if (!this.deferredContext || !this.deferredSessionId) {
585
+ throw new Error("claude-code driver: no deferred session to resume");
586
+ }
587
+ if (answer.id !== this.deferredPendingId) {
588
+ throw new Error(
589
+ `claude-code driver: answer '${answer.id}' does not match deferred tool '${this.deferredPendingId ?? "none"}'`,
590
+ );
591
+ }
592
+ const textQuestion = this.deferredTextQuestionPending;
593
+ this.deferredPendingId = null;
594
+ this.deferredTextQuestionPending = false;
595
+ this.launchDeferredTurn(answer, textQuestion);
596
+ return;
597
+ }
598
+ if (!this.inner) throw new Error("claude-code driver: respond called before start");
599
+ return this.inner.respond(answer);
600
+ }
601
+
602
+ async collect(): Promise<Artifact[]> {
603
+ if (!this.collectDir) return [];
604
+ // A terminal protocol event can arrive before the CLI and its descendants
605
+ // have exited. Quiesce the whole process tree before walking/copying files
606
+ // so it cannot swap a checked regular file into a symlink.
607
+ await this.inner?.close();
608
+ const artifacts = await walkArtifacts(this.collectDir);
609
+ if (this.isolatedWorkspace && this.artifactsDir) {
610
+ await Promise.all(
611
+ artifacts.map(async (artifact) => {
612
+ const target = join(this.artifactsDir as string, artifact.path);
613
+ await mkdir(dirname(target), { recursive: true });
614
+ await rm(target, { force: true });
615
+ await copyRegularFileNoFollow(
616
+ this.isolatedWorkspace as string,
617
+ join(this.isolatedWorkspace as string, artifact.path),
618
+ target,
619
+ );
620
+ }),
621
+ );
622
+ return artifacts;
623
+ }
624
+ // Consumers resolve artifact paths against <runDir>/artifacts by default;
625
+ // a walk of any other root must say what its paths are relative to, or
626
+ // the judge and file assertions silently miss every file.
627
+ if (this.collectDir === this.artifactsDir) return artifacts;
628
+ const root = this.collectDir;
629
+ // Walking the run dir itself would list Lux's own bookkeeping as agent
630
+ // work product; only top-level files can collide with these names.
631
+ const withRoot = artifacts.map((a) => ({ ...a, root }));
632
+ return root === this.runDir
633
+ ? withRoot.filter((a) => !LUX_BOOKKEEPING_FILES.has(a.path))
634
+ : withRoot;
635
+ }
636
+
637
+ async quiesce(): Promise<void> {
638
+ if (this.config.interactionMode === "defer-resume") {
639
+ await this.deferredTurn;
640
+ return;
641
+ }
642
+ await this.inner?.close();
643
+ }
644
+
645
+ async close(): Promise<void> {
646
+ try {
647
+ if (this.config.interactionMode === "defer-resume") {
648
+ this.emitter.removeAllListeners();
649
+ if (this.deferredHookPath) {
650
+ await rm(this.deferredHookPath, { force: true });
651
+ this.deferredHookPath = null;
652
+ }
653
+ } else {
654
+ await this.inner?.close();
655
+ }
656
+ } finally {
657
+ if (this.isolatedWorkspace) {
658
+ await rm(this.isolatedWorkspace, { recursive: true, force: true });
659
+ this.isolatedWorkspace = null;
660
+ }
661
+ }
662
+ }
663
+
664
+ private async startDeferred(ctx: StartContext): Promise<ReadyState> {
665
+ const cwd =
666
+ this.config.cwd ?? (this.config.cwdMode === "artifacts" ? ctx.artifactsDir : ctx.runDir);
667
+ this.collectDir = cwd;
668
+ this.artifactsDir = ctx.artifactsDir;
669
+ this.runDir = ctx.runDir;
670
+ this.deferredContext = ctx;
671
+ this.deferredCwd = cwd;
672
+ this.deferredHookPath = join(ctx.runDir, ".lux-claude-hitl-hook.cjs");
673
+ await writeFile(this.deferredHookPath, DEFERRED_HOOK_SOURCE);
674
+ const cliVersion =
675
+ commandName(this.config.command) === "claude"
676
+ ? await discoverCliVersion(this.config.command)
677
+ : undefined;
678
+ this.deferredParser = makeParseEvent(this.config, cliVersion);
679
+ const ready = new Promise<ReadyState>((resolveReady, reject) => {
680
+ this.deferredReady = { resolve: resolveReady, reject };
681
+ });
682
+ this.launchDeferredTurn();
683
+ return ready;
684
+ }
685
+
686
+ private launchDeferredTurn(answer?: Answer, resumeWithAnswer = false): void {
687
+ let task!: Promise<void>;
688
+ task = (async () => {
689
+ try {
690
+ await this.runDeferredTurn(answer, resumeWithAnswer);
691
+ } catch (error) {
692
+ await this.failDeferred(error);
693
+ } finally {
694
+ if (this.deferredTurn === task) this.deferredTurn = null;
695
+ }
696
+ })();
697
+ this.deferredTurn = task;
698
+ }
699
+
700
+ private async runDeferredTurn(answer?: Answer, resumeWithAnswer = false): Promise<void> {
701
+ const ctx = this.deferredContext;
702
+ const cwd = this.deferredCwd;
703
+ const hookPath = this.deferredHookPath;
704
+ const parse = this.deferredParser;
705
+ if (!ctx || !cwd || !hookPath || !parse) {
706
+ throw new Error("claude-code driver: deferred session was not initialized");
707
+ }
708
+ const resuming = this.deferredSessionId !== null;
709
+ let input: { input: string } | undefined;
710
+ if (!resuming) {
711
+ input = { input: ctx.prompt };
712
+ } else if (resumeWithAnswer && answer?.kind === "ask") {
713
+ input = { input: answer.text };
714
+ }
715
+ const result = await exec(
716
+ this.config.command,
717
+ buildDeferredArgs(this.config, hookPath, this.deferredSessionId ?? undefined),
718
+ {
719
+ cwd,
720
+ env: {
721
+ ...process.env,
722
+ LUX_RUN_DIR: ctx.runDir,
723
+ LUX_ARTIFACTS_DIR: ctx.artifactsDir,
724
+ ...this.config.env,
725
+ ...(answer ? { LUX_CLAUDE_DEFERRED_ANSWER: JSON.stringify(answer) } : {}),
726
+ },
727
+ ...input,
728
+ timeoutMs: this.config.idleTimeoutMs,
729
+ signal: ctx.abortSignal,
730
+ },
731
+ );
732
+ if (result.stderr.trim()) {
733
+ for (const line of result.stderr.trimEnd().split("\n")) {
734
+ process.stderr.write(`[claude-code] ${line}\n`);
735
+ }
736
+ }
737
+ for (const line of result.stdout.split("\n")) {
738
+ if (!line.trim()) continue;
739
+ try {
740
+ const envelope = ClaudeCodeLineSchema.safeParse(JSON.parse(line));
741
+ if (
742
+ envelope.success &&
743
+ (envelope.data.type === "system" || envelope.data.type === "result") &&
744
+ envelope.data.session_id
745
+ ) {
746
+ this.deferredSessionId = envelope.data.session_id;
747
+ }
748
+ } catch {
749
+ // The parser below deliberately ignores unmodeled output.
750
+ }
751
+ const parsed = parse(line);
752
+ if (!parsed) continue;
753
+ for (const event of Array.isArray(parsed) ? parsed : [parsed]) {
754
+ await this.dispatchDeferred(event);
755
+ }
756
+ }
757
+ if (result.timedOut) {
758
+ throw new Error(`no event for ${this.config.idleTimeoutMs}ms`);
759
+ }
760
+ if (result.signal) throw new Error(`claude exited from signal ${result.signal}`);
761
+ if (result.exitCode !== 0 && !this.deferredTerminal) {
762
+ throw new Error(`claude exited ${result.exitCode}`);
763
+ }
764
+ if (!resuming && this.deferredReady) {
765
+ throw new Error("claude exited before ready");
766
+ }
767
+ }
768
+
769
+ private async dispatchDeferred(event: ParsedDriverEvent): Promise<void> {
770
+ if (this.deferredTerminal) return;
771
+ if (event.tag === "ready") {
772
+ this.deferredReady?.resolve(event.ready);
773
+ this.deferredReady = undefined;
774
+ return;
775
+ }
776
+ if (event.tag === "progress") {
777
+ if (
778
+ event.progress.kind === "agent-message" &&
779
+ typeof (event.progress.payload as { text?: unknown })?.text === "string"
780
+ ) {
781
+ this.deferredLastAgentText = (event.progress.payload as { text: string }).text;
782
+ }
783
+ await this.emitter.emit("progress", event.progress);
784
+ return;
785
+ }
786
+ if (event.tag === "interrupt") {
787
+ this.deferredLastAgentText = null;
788
+ this.deferredInterrupts++;
789
+ if (this.deferredInterrupts > this.config.maxInterrupts) {
790
+ this.deferredTerminal = true;
791
+ await this.emitter.emit("error", {
792
+ exitReason: "max-interactions",
793
+ reason: `exceeded maxInterrupts (${this.config.maxInterrupts})`,
794
+ });
795
+ return;
796
+ }
797
+ this.deferredPendingId = event.interrupt.id;
798
+ await this.emitter.emit("interrupt", event.interrupt);
799
+ return;
800
+ }
801
+ if (
802
+ event.tag === "done" &&
803
+ this.config.textQuestionFallback &&
804
+ this.deferredLastAgentText?.includes("?")
805
+ ) {
806
+ const question = this.deferredLastAgentText;
807
+ this.deferredLastAgentText = null;
808
+ this.deferredInterrupts++;
809
+ if (this.deferredInterrupts > this.config.maxInterrupts) {
810
+ this.deferredTerminal = true;
811
+ await this.emitter.emit("error", {
812
+ exitReason: "max-interactions",
813
+ reason: `exceeded maxInterrupts (${this.config.maxInterrupts})`,
814
+ });
815
+ return;
816
+ }
817
+ this.deferredTextQuestionIndex++;
818
+ const id = `text-question-${this.deferredTextQuestionIndex}`;
819
+ this.deferredPendingId = id;
820
+ this.deferredTextQuestionPending = true;
821
+ await this.emitter.emit("interrupt", {
822
+ kind: "ask",
823
+ id,
824
+ question,
825
+ context: { fallback: "terminal-agent-message" },
826
+ });
827
+ return;
828
+ }
829
+ this.deferredTerminal = true;
830
+ if (event.tag === "done") await this.emitter.emit("done", event.done);
831
+ else await this.emitter.emit("error", event.error);
832
+ }
833
+
834
+ private async failDeferred(error: unknown): Promise<void> {
835
+ if (this.deferredTerminal) return;
836
+ this.deferredTerminal = true;
837
+ const reason = error instanceof Error ? error.message : String(error);
838
+ if (this.deferredReady) {
839
+ this.deferredReady.reject(new Error(reason));
840
+ this.deferredReady = undefined;
841
+ return;
842
+ }
843
+ await this.emitter.emit("error", {
844
+ exitReason: reason.startsWith("no event for") ? "idle-timeout" : "error",
845
+ reason,
846
+ });
847
+ }
848
+ }
849
+
850
+ export const claudeCodeDriver: DriverPlugin<ClaudeCodeDriverConfig> = {
851
+ name: "claude-code",
852
+ configSchema: ClaudeCodeDriverConfigSchema,
853
+ create: (config) => new ClaudeCodeDriverImpl(config),
854
+ };
855
+
856
+ // Internal exports for testing
857
+ export {
858
+ buildArgs,
859
+ buildDeferredArgs,
860
+ carvedReadDenials,
861
+ copyRegularFileNoFollow,
862
+ formatAnswer,
863
+ formatPrompt,
864
+ makeParseEvent,
865
+ walkArtifacts,
866
+ };