humanish 0.106.1 → 0.107.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 (453) hide show
  1. package/AGENTS.md +4 -4
  2. package/ARCHITECTURE.md +17 -16
  3. package/CHANGELOG.md +14 -0
  4. package/CONTEXT.md +42 -39
  5. package/CONTRIBUTING.md +8 -8
  6. package/README.md +8 -3
  7. package/dist/actors/codex/app-server-client.js +2 -1
  8. package/dist/actors/codex/app-server-trace.d.ts +0 -1
  9. package/dist/actors/codex/app-server-trace.js +1 -3
  10. package/dist/actors/codex/app-server.js +2 -1
  11. package/dist/actors/codex/protocol-compat.d.ts +27 -0
  12. package/dist/actors/codex/protocol-compat.js +357 -0
  13. package/dist/actors/codex/protocol-contract.d.ts +61 -0
  14. package/dist/actors/codex/protocol-contract.js +471 -0
  15. package/dist/actors/codex/qualified-versions.d.ts +30 -3
  16. package/dist/actors/codex/qualified-versions.js +50 -5
  17. package/dist/actors/codex/restricted-executable.d.ts +24 -0
  18. package/dist/actors/codex/restricted-executable.js +97 -22
  19. package/dist/actors/codex/restricted-launch.d.ts +7 -1
  20. package/dist/actors/codex/restricted-launch.js +42 -3
  21. package/dist/actors/codex/restricted-notifications.d.ts +101 -0
  22. package/dist/actors/codex/restricted-notifications.js +277 -0
  23. package/dist/actors/codex/restricted-participant.d.ts +30 -0
  24. package/dist/actors/codex/restricted-participant.js +60 -2
  25. package/dist/actors/codex/restricted-policy.d.ts +9 -1
  26. package/dist/actors/codex/restricted-session.d.ts +14 -1
  27. package/dist/actors/codex/restricted-session.js +50 -17
  28. package/dist/actors/codex/restricted-transport.d.ts +29 -0
  29. package/dist/actors/codex/restricted-transport.js +139 -27
  30. package/dist/actors/codex/restricted-turn.d.ts +19 -4
  31. package/dist/actors/codex/restricted-turn.js +43 -30
  32. package/dist/actors/computer-use/actor.d.ts +1 -1
  33. package/dist/actors/computer-use/actor.js +4 -4
  34. package/dist/actors/computer-use/loop/backstop.js +1 -1
  35. package/dist/actors/computer-use/loop/ending.d.ts +2 -2
  36. package/dist/actors/computer-use/loop/ending.js +2 -2
  37. package/dist/actors/computer-use/loop/spend.d.ts +1 -1
  38. package/dist/actors/computer-use/loop/spend.js +3 -3
  39. package/dist/actors/computer-use/loop/trace.js +2 -2
  40. package/dist/actors/computer-use/loop/types.d.ts +14 -5
  41. package/dist/actors/computer-use/loop.js +4 -4
  42. package/dist/actors/computer-use/openai-provider.d.ts +1 -1
  43. package/dist/actors/computer-use/openai-provider.js +2 -2
  44. package/dist/actors/contract.d.ts +9 -7
  45. package/dist/actors/local-agent/cli.js +1 -1
  46. package/dist/actors/scripted-browser/actor.d.ts +2 -2
  47. package/dist/actors/scripted-browser/journey.js +15 -4
  48. package/dist/actors/scripted-browser/steps.js +18 -0
  49. package/dist/actors/scripted-browser/types.d.ts +11 -1
  50. package/dist/actors/stop-conditions.js +1 -3
  51. package/dist/analysis/automatic-completion.d.ts +13 -8
  52. package/dist/analysis/automatic-completion.js +14 -8
  53. package/dist/analysis/automatic-config.d.ts +1 -1
  54. package/dist/analysis/automatic-config.js +2 -2
  55. package/dist/analysis/automatic.d.ts +10 -4
  56. package/dist/analysis/automatic.js +17 -13
  57. package/dist/analysis/diagnostics.d.ts +23 -0
  58. package/dist/analysis/diagnostics.js +112 -0
  59. package/dist/analysis/evidence-sources.js +2 -2
  60. package/dist/analysis/evidence.js +6 -6
  61. package/dist/analysis/execute.d.ts +7 -1
  62. package/dist/analysis/execute.js +39 -12
  63. package/dist/analysis/job.d.ts +8 -5
  64. package/dist/analysis/job.js +15 -14
  65. package/dist/analysis/load.d.ts +1 -1
  66. package/dist/analysis/load.js +5 -5
  67. package/dist/analysis/provider.d.ts +9 -1
  68. package/dist/analysis/restricted-codex.d.ts +2 -0
  69. package/dist/analysis/restricted-codex.js +10 -1
  70. package/dist/analysis/service.d.ts +2 -0
  71. package/dist/analysis/service.js +31 -12
  72. package/dist/analysis/sharing.d.ts +2 -2
  73. package/dist/analysis/sharing.js +5 -5
  74. package/dist/analysis/store-executions.d.ts +5 -5
  75. package/dist/analysis/store-executions.js +12 -12
  76. package/dist/analysis/store.d.ts +4 -4
  77. package/dist/analysis/store.js +13 -13
  78. package/dist/cli/commands/analysis-signals.d.ts +2 -1
  79. package/dist/cli/commands/analysis-signals.js +4 -1
  80. package/dist/cli/commands/analyze.js +4 -2
  81. package/dist/cli/commands/comms.js +8 -1
  82. package/dist/cli/commands/lab-route-computer-use.d.ts +0 -2
  83. package/dist/cli/commands/lab-route-computer-use.js +26 -14
  84. package/dist/cli/commands/lab-route-preview.d.ts +1 -3
  85. package/dist/cli/commands/lab-route-preview.js +3 -4
  86. package/dist/cli/commands/lab-route-run.d.ts +6 -4
  87. package/dist/cli/commands/lab-route-run.js +6 -2
  88. package/dist/cli/commands/lab-route-scripted.d.ts +0 -2
  89. package/dist/cli/commands/lab-route-scripted.js +0 -1
  90. package/dist/cli/commands/lab-route-shared-world.d.ts +0 -2
  91. package/dist/cli/commands/lab-route-shared-world.js +0 -1
  92. package/dist/cli/commands/lab-route-terminal.d.ts +0 -2
  93. package/dist/cli/commands/lab-route-terminal.js +0 -1
  94. package/dist/cli/commands/lab-run.js +76 -45
  95. package/dist/cli/commands/lab-scorer.d.ts +0 -2
  96. package/dist/cli/commands/lab.js +12 -6
  97. package/dist/cli/commands/observe.js +22 -0
  98. package/dist/cli/commands/run-signals.d.ts +30 -0
  99. package/dist/cli/commands/run-signals.js +146 -0
  100. package/dist/cli/commands/runs.js +10 -8
  101. package/dist/cli/commands/watch.js +12 -10
  102. package/dist/cli/doctor.d.ts +3 -0
  103. package/dist/cli/doctor.js +10 -2
  104. package/dist/cli/io.d.ts +5 -4
  105. package/dist/cli/io.js +12 -8
  106. package/dist/cli/program.js +3 -3
  107. package/dist/cli/renamed-options.d.ts +11 -0
  108. package/dist/cli/renamed-options.js +18 -0
  109. package/dist/cli/telemetry.d.ts +2 -2
  110. package/dist/cli/telemetry.js +1 -1
  111. package/dist/comms/capture-surface.d.ts +1 -1
  112. package/dist/comms/catch-host.d.ts +1 -1
  113. package/dist/comms/connections.js +4 -4
  114. package/dist/comms/external-evidence.d.ts +27 -0
  115. package/dist/comms/external-evidence.js +93 -0
  116. package/dist/comms/lease-store.js +7 -7
  117. package/dist/comms/types.js +1 -1
  118. package/dist/evidence/browser-hygiene.d.ts +1 -1
  119. package/dist/evidence/browser-hygiene.js +1 -1
  120. package/dist/evidence/redaction.js +1 -1
  121. package/dist/evidence/secret-scrub.d.ts +9 -0
  122. package/dist/evidence/secret-scrub.js +96 -0
  123. package/dist/feedback/draft.js +7 -9
  124. package/dist/feedback/export-bundle.js +4 -4
  125. package/dist/feedback/export.js +9 -9
  126. package/dist/feedback/feedback.d.ts +1 -1
  127. package/dist/index.d.ts +3 -6
  128. package/dist/index.js +3 -6
  129. package/dist/keys/key-resolution.d.ts +3 -1
  130. package/dist/keys/key-resolution.js +18 -16
  131. package/dist/lab/adapter-extension.d.ts +28 -6
  132. package/dist/lab/adapter-extension.js +65 -126
  133. package/dist/lab/adapter-scorer-loader.d.ts +25 -5
  134. package/dist/lab/adapter-scorer-loader.js +24 -0
  135. package/dist/lab/composition-rules.js +20 -20
  136. package/dist/lab/config.js +28 -26
  137. package/dist/lab/device-presets.d.ts +2 -2
  138. package/dist/lab/discover.js +1 -3
  139. package/dist/lab/doctor.d.ts +44 -9
  140. package/dist/lab/doctor.js +149 -41
  141. package/dist/lab/init-plan.js +1 -3
  142. package/dist/lab/keys.js +1 -1
  143. package/dist/lab/lab-deps.d.ts +85 -0
  144. package/dist/lab/lab-deps.js +4 -0
  145. package/dist/lab/parse/actors.js +8 -7
  146. package/dist/lab/parse/comms.js +3 -2
  147. package/dist/lab/parse/execution.js +3 -2
  148. package/dist/lab/parse/subject-state.js +2 -1
  149. package/dist/lab/parse/subject.js +2 -1
  150. package/dist/lab/parse/values.d.ts +0 -1
  151. package/dist/lab/parse/values.js +0 -3
  152. package/dist/lab/persona-resolve.d.ts +3 -3
  153. package/dist/lab/persona-resolve.js +4 -4
  154. package/dist/lab/persona.js +1 -3
  155. package/dist/lab/plan-base.d.ts +0 -2
  156. package/dist/lab/plan-base.js +6 -4
  157. package/dist/lab/plan-participants.d.ts +5 -5
  158. package/dist/lab/plan-participants.js +2 -2
  159. package/dist/lab/plan-types.d.ts +22 -4
  160. package/dist/lab/plan.d.ts +3 -11
  161. package/dist/lab/plan.js +18 -42
  162. package/dist/lab/preflight-result.js +0 -2
  163. package/dist/lab/preflight.d.ts +0 -3
  164. package/dist/lab/preflight.js +1 -1
  165. package/dist/lab/requirements.d.ts +20 -0
  166. package/dist/lab/requirements.js +54 -0
  167. package/dist/lab/route-inputs.d.ts +23 -12
  168. package/dist/lab/route-inputs.js +48 -51
  169. package/dist/lab/routing.d.ts +19 -24
  170. package/dist/lab/routing.js +14 -47
  171. package/dist/lab/run-lab-events.d.ts +6 -11
  172. package/dist/lab/run-lab-events.js +3 -11
  173. package/dist/lab/run-lab-homes.d.ts +64 -0
  174. package/dist/lab/run-lab-homes.js +3 -0
  175. package/dist/lab/run-lab-options.d.ts +12 -66
  176. package/dist/lab/run-lab-options.js +45 -275
  177. package/dist/lab/summary.d.ts +9 -4
  178. package/dist/lab/summary.js +15 -8
  179. package/dist/lab/types.d.ts +132 -132
  180. package/dist/lab/validation.d.ts +15 -15
  181. package/dist/lab/validation.js +28 -28
  182. package/dist/lab/warnings.js +20 -19
  183. package/dist/observer/artifact.d.ts +1 -1
  184. package/dist/observer/artifact.js +3 -3
  185. package/dist/observer/data.js +2 -2
  186. package/dist/observer/render.js +2 -2
  187. package/dist/observer/run-routes.d.ts +1 -1
  188. package/dist/observer/run-routes.js +5 -5
  189. package/dist/observer/serve.d.ts +17 -0
  190. package/dist/observer/serve.js +34 -3
  191. package/dist/routes/computer-use/bundle-parts.d.ts +1 -1
  192. package/dist/routes/computer-use/bundle-parts.js +10 -3
  193. package/dist/routes/computer-use/bundle.d.ts +4 -11
  194. package/dist/routes/computer-use/bundle.js +9 -15
  195. package/dist/routes/computer-use/diagnostics.d.ts +1 -1
  196. package/dist/routes/computer-use/diagnostics.js +1 -1
  197. package/dist/routes/computer-use/e2b-desktop/comms.d.ts +10 -10
  198. package/dist/routes/computer-use/e2b-desktop/comms.js +11 -14
  199. package/dist/routes/computer-use/e2b-desktop/desktop.d.ts +2 -2
  200. package/dist/routes/computer-use/e2b-desktop/desktop.js +4 -7
  201. package/dist/routes/computer-use/e2b-desktop/fidelity.d.ts +13 -13
  202. package/dist/routes/computer-use/e2b-desktop/fidelity.js +11 -11
  203. package/dist/routes/computer-use/e2b-desktop/prepare.d.ts +3 -3
  204. package/dist/routes/computer-use/e2b-desktop/prepare.js +33 -36
  205. package/dist/routes/computer-use/e2b-desktop/start.d.ts +1 -1
  206. package/dist/routes/computer-use/e2b-desktop/start.js +24 -23
  207. package/dist/routes/computer-use/e2b-desktop/state.d.ts +5 -4
  208. package/dist/routes/computer-use/e2b-desktop/state.js +5 -3
  209. package/dist/routes/computer-use/e2b-desktop/teardown.d.ts +1 -1
  210. package/dist/routes/computer-use/e2b-desktop/teardown.js +29 -17
  211. package/dist/routes/computer-use/external-comms.d.ts +4 -4
  212. package/dist/routes/computer-use/external-comms.js +19 -43
  213. package/dist/routes/computer-use/fanout-bundle.d.ts +3 -3
  214. package/dist/routes/computer-use/fanout-bundle.js +25 -29
  215. package/dist/routes/computer-use/{fanout-lanes.js → fanout-records.js} +25 -19
  216. package/dist/routes/computer-use/in-process-desktop.d.ts +3 -10
  217. package/dist/routes/computer-use/in-process-desktop.js +3 -9
  218. package/dist/routes/computer-use/live-flush.d.ts +7 -5
  219. package/dist/routes/computer-use/live-flush.js +19 -27
  220. package/dist/routes/computer-use/{run-lanes.js → live-phase.js} +12 -11
  221. package/dist/routes/computer-use/local-tree-pack.d.ts +3 -3
  222. package/dist/routes/computer-use/local-tree-pack.js +4 -5
  223. package/dist/routes/computer-use/local-vm.d.ts +11 -8
  224. package/dist/routes/computer-use/local-vm.js +39 -66
  225. package/dist/routes/computer-use/participant-desktop.d.ts +8 -23
  226. package/dist/routes/computer-use/participant-desktop.js +5 -25
  227. package/dist/routes/computer-use/{lanes.d.ts → participant-execution.d.ts} +10 -10
  228. package/dist/routes/computer-use/{lanes.js → participant-execution.js} +35 -22
  229. package/dist/routes/computer-use/participant-facts.d.ts +7 -1
  230. package/dist/routes/computer-use/participant-facts.js +12 -1
  231. package/dist/routes/computer-use/participant-model.d.ts +19 -11
  232. package/dist/routes/computer-use/participant-model.js +49 -25
  233. package/dist/routes/computer-use/participant-prompt.d.ts +3 -3
  234. package/dist/routes/computer-use/participant-prompt.js +4 -4
  235. package/dist/routes/computer-use/participant-runs.d.ts +6 -6
  236. package/dist/routes/computer-use/participant-runs.js +8 -8
  237. package/dist/routes/computer-use/plan.d.ts +21 -29
  238. package/dist/routes/computer-use/plan.js +52 -60
  239. package/dist/routes/computer-use/preflight.d.ts +3 -5
  240. package/dist/routes/computer-use/preflight.js +20 -16
  241. package/dist/routes/computer-use/rerun-selection.js +10 -6
  242. package/dist/routes/computer-use/result.d.ts +1 -1
  243. package/dist/routes/computer-use/result.js +40 -30
  244. package/dist/routes/computer-use/route.d.ts +3 -4
  245. package/dist/routes/computer-use/route.js +21 -19
  246. package/dist/routes/computer-use/self-report.d.ts +2 -2
  247. package/dist/routes/computer-use/self-report.js +4 -4
  248. package/dist/routes/computer-use/setup.d.ts +8 -14
  249. package/dist/routes/computer-use/setup.js +96 -72
  250. package/dist/routes/computer-use/single-bundle.d.ts +8 -11
  251. package/dist/routes/computer-use/single-bundle.js +20 -25
  252. package/dist/routes/computer-use/subject-projection.d.ts +5 -7
  253. package/dist/routes/computer-use/subject-projection.js +20 -19
  254. package/dist/routes/computer-use/types.d.ts +169 -210
  255. package/dist/routes/computer-use/types.js +13 -5
  256. package/dist/routes/preview.js +0 -1
  257. package/dist/routes/scripted/bundle.d.ts +3 -6
  258. package/dist/routes/scripted/bundle.js +11 -16
  259. package/dist/routes/scripted/plan.d.ts +5 -7
  260. package/dist/routes/scripted/plan.js +6 -4
  261. package/dist/routes/scripted/result.d.ts +4 -1
  262. package/dist/routes/scripted/result.js +10 -6
  263. package/dist/routes/scripted/route.js +10 -11
  264. package/dist/routes/scripted/scenario.d.ts +1 -1
  265. package/dist/routes/scripted/scenario.js +1 -1
  266. package/dist/routes/scripted/setup.d.ts +3 -2
  267. package/dist/routes/scripted/setup.js +22 -21
  268. package/dist/routes/scripted/subject.d.ts +8 -3
  269. package/dist/routes/scripted/subject.js +9 -5
  270. package/dist/routes/scripted/surface-records.js +1 -1
  271. package/dist/routes/scripted/surface-sessions.d.ts +2 -2
  272. package/dist/routes/scripted/surface-sessions.js +4 -4
  273. package/dist/routes/scripted/types.d.ts +19 -36
  274. package/dist/routes/scripted/types.js +1 -2
  275. package/dist/routes/shared-world/{seat-records.js → bundle-records.js} +8 -8
  276. package/dist/routes/shared-world/bundle.d.ts +2 -2
  277. package/dist/routes/shared-world/bundle.js +22 -26
  278. package/dist/routes/shared-world/checkpoints.d.ts +4 -4
  279. package/dist/routes/shared-world/checkpoints.js +4 -4
  280. package/dist/routes/shared-world/comms.d.ts +5 -4
  281. package/dist/routes/shared-world/comms.js +28 -41
  282. package/dist/routes/shared-world/external-public.d.ts +5 -5
  283. package/dist/routes/shared-world/external-public.js +19 -20
  284. package/dist/routes/shared-world/handoff.d.ts +4 -4
  285. package/dist/routes/shared-world/handoff.js +50 -50
  286. package/dist/routes/shared-world/lobby-code.d.ts +5 -5
  287. package/dist/routes/shared-world/lobby-code.js +13 -13
  288. package/dist/routes/shared-world/{seats.d.ts → participant-specs.d.ts} +8 -6
  289. package/dist/routes/shared-world/{seats.js → participant-specs.js} +43 -48
  290. package/dist/routes/shared-world/plan.d.ts +2 -6
  291. package/dist/routes/shared-world/plan.js +1 -2
  292. package/dist/routes/shared-world/provenance.d.ts +4 -4
  293. package/dist/routes/shared-world/provenance.js +4 -4
  294. package/dist/routes/shared-world/provisioned.d.ts +3 -2
  295. package/dist/routes/shared-world/provisioned.js +46 -40
  296. package/dist/routes/shared-world/result.js +31 -19
  297. package/dist/routes/shared-world/route.js +37 -41
  298. package/dist/routes/shared-world/setup.d.ts +5 -4
  299. package/dist/routes/shared-world/setup.js +12 -14
  300. package/dist/routes/shared-world/types.d.ts +53 -126
  301. package/dist/routes/shared-world/types.js +2 -2
  302. package/dist/routes/terminal/adapter.d.ts +8 -8
  303. package/dist/routes/terminal/adapter.js +28 -125
  304. package/dist/routes/terminal/bundle.d.ts +7 -12
  305. package/dist/routes/terminal/bundle.js +24 -34
  306. package/dist/routes/terminal/credentials.d.ts +2 -2
  307. package/dist/routes/terminal/credentials.js +16 -16
  308. package/dist/routes/terminal/dry-run.d.ts +1 -1
  309. package/dist/routes/terminal/dry-run.js +9 -11
  310. package/dist/routes/terminal/encoding.d.ts +1 -1
  311. package/dist/routes/terminal/encoding.js +2 -2
  312. package/dist/routes/terminal/ledger.d.ts +6 -6
  313. package/dist/routes/terminal/ledger.js +9 -9
  314. package/dist/routes/terminal/live-finish.d.ts +1 -2
  315. package/dist/routes/terminal/live-finish.js +21 -20
  316. package/dist/routes/terminal/live-sandbox.d.ts +3 -2
  317. package/dist/routes/terminal/live-sandbox.js +20 -20
  318. package/dist/routes/terminal/participant-text.d.ts +23 -0
  319. package/dist/routes/terminal/participant-text.js +165 -0
  320. package/dist/routes/terminal/plan.d.ts +2 -6
  321. package/dist/routes/terminal/plan.js +13 -9
  322. package/dist/routes/terminal/recorder.d.ts +6 -1
  323. package/dist/routes/terminal/recorder.js +12 -4
  324. package/dist/routes/terminal/result.js +4 -4
  325. package/dist/routes/terminal/route.js +26 -26
  326. package/dist/routes/terminal/runtime.d.ts +3 -2
  327. package/dist/routes/terminal/runtime.js +3 -3
  328. package/dist/routes/terminal/sandbox.d.ts +3 -3
  329. package/dist/routes/terminal/sandbox.js +9 -9
  330. package/dist/routes/terminal/session.js +16 -14
  331. package/dist/routes/terminal/token-usage.js +5 -5
  332. package/dist/routes/terminal/trace.d.ts +5 -8
  333. package/dist/routes/terminal/trace.js +9 -64
  334. package/dist/routes/terminal/types.d.ts +85 -93
  335. package/dist/routes/terminal/types.js +9 -4
  336. package/dist/run/active-runs.d.ts +13 -0
  337. package/dist/run/active-runs.js +12 -0
  338. package/dist/run/bundle-shape.d.ts +2 -1
  339. package/dist/run/bundle-shape.js +4 -4
  340. package/dist/run/bundle.d.ts +63 -56
  341. package/dist/run/bundle.js +9 -8
  342. package/dist/run/cost-summary.js +3 -2
  343. package/dist/run/costs.d.ts +6 -6
  344. package/dist/run/costs.js +10 -10
  345. package/dist/run/detail.d.ts +5 -5
  346. package/dist/run/detail.js +6 -6
  347. package/dist/run/dry-run.js +5 -12
  348. package/dist/run/{study-files.d.ts → evidence-files.d.ts} +4 -4
  349. package/dist/run/{study-files.js → evidence-files.js} +5 -5
  350. package/dist/run/feedback-shape.js +1 -1
  351. package/dist/run/judge.d.ts +16 -4
  352. package/dist/run/judge.js +24 -7
  353. package/dist/run/locate.d.ts +2 -0
  354. package/dist/run/locate.js +3 -1
  355. package/dist/run/outcomes.d.ts +3 -3
  356. package/dist/run/outcomes.js +3 -3
  357. package/dist/run/participant-records.d.ts +2 -2
  358. package/dist/run/participant-records.js +2 -2
  359. package/dist/run/paths.js +1 -1
  360. package/dist/run/pricing.d.ts +19 -9
  361. package/dist/run/pricing.js +30 -18
  362. package/dist/run/projection.d.ts +13 -13
  363. package/dist/run/projection.js +8 -8
  364. package/dist/run/reclaim.d.ts +6 -0
  365. package/dist/run/reclaim.js +13 -3
  366. package/dist/run/results.d.ts +1 -1
  367. package/dist/run/run-index.d.ts +1 -1
  368. package/dist/run/run-index.js +11 -11
  369. package/dist/run/run.d.ts +4 -2
  370. package/dist/run/run.js +9 -5
  371. package/dist/run/sandbox-receipts.d.ts +4 -4
  372. package/dist/run/sandbox-receipts.js +4 -4
  373. package/dist/run/shared-world-evidence.d.ts +47 -47
  374. package/dist/run/shared-world-shape.d.ts +3 -3
  375. package/dist/run/shared-world-shape.js +11 -11
  376. package/dist/run/stats.d.ts +5 -5
  377. package/dist/run/stats.js +8 -8
  378. package/dist/run/status.d.ts +16 -6
  379. package/dist/run/status.js +31 -8
  380. package/dist/run/streams.d.ts +26 -19
  381. package/dist/run/terminal-contract.d.ts +5 -5
  382. package/dist/run/terminal-contract.js +4 -4
  383. package/dist/run/transient-comms-secrets.d.ts +5 -0
  384. package/dist/run/transient-comms-secrets.js +12 -0
  385. package/dist/run-lab.d.ts +36 -34
  386. package/dist/run-lab.js +41 -37
  387. package/dist/subject/desktop-cli.d.ts +1 -1
  388. package/dist/subject/desktop-cli.js +1 -1
  389. package/dist/subject/local-tree-archive.d.ts +1 -1
  390. package/dist/subject/local-tree-archive.js +4 -4
  391. package/dist/subject/local-tree.d.ts +2 -2
  392. package/dist/subject/local-tree.js +1 -1
  393. package/dist/subject/node-bootstrap.d.ts +1 -1
  394. package/dist/subject/node-bootstrap.js +4 -4
  395. package/dist/subject/runtime.js +5 -5
  396. package/dist/subject/serve.js +1 -1
  397. package/dist/subject/steps.d.ts +4 -4
  398. package/dist/subject/steps.js +5 -5
  399. package/dist/substrates/e2b/cdp-probe.d.ts +2 -2
  400. package/dist/substrates/e2b/cdp-probe.js +1 -1
  401. package/dist/substrates/e2b/desktop-browser.d.ts +3 -3
  402. package/dist/substrates/e2b/desktop-browser.js +4 -4
  403. package/dist/substrates/e2b/desktop-cdp.d.ts +7 -7
  404. package/dist/substrates/e2b/desktop-cdp.js +4 -4
  405. package/dist/substrates/e2b/desktop-geometry.d.ts +4 -4
  406. package/dist/substrates/e2b/desktop-geometry.js +3 -3
  407. package/dist/substrates/e2b/desktop-media.js +2 -2
  408. package/dist/substrates/e2b/sandbox.js +1 -1
  409. package/dist/substrates/e2b/sdk.js +2 -2
  410. package/dist/substrates/local/runtime-host.d.ts +2 -0
  411. package/dist/substrates/local/runtime-host.js +6 -0
  412. package/dist/tui/actions.d.ts +7 -5
  413. package/dist/tui/actions.js +18 -9
  414. package/dist/tui-app.js +42 -42
  415. package/dist/verify/actor.d.ts +10 -10
  416. package/dist/verify/actor.js +15 -15
  417. package/dist/verify/artifacts.d.ts +1 -1
  418. package/dist/verify/artifacts.js +3 -3
  419. package/dist/verify/costs.d.ts +2 -2
  420. package/dist/verify/costs.js +2 -2
  421. package/dist/verify/liveness.d.ts +1 -1
  422. package/dist/verify/liveness.js +4 -1
  423. package/dist/verify/rerun.d.ts +1 -1
  424. package/dist/verify/rerun.js +1 -1
  425. package/dist/verify/shared-world.js +2 -2
  426. package/dist/verify/verify.js +6 -6
  427. package/docs/README.md +1 -1
  428. package/docs/architecture/actor-contract.md +60 -22
  429. package/docs/architecture/desktop-sessions.md +9 -9
  430. package/docs/architecture/external-public-shared-world.md +5 -0
  431. package/docs/architecture/observer.md +4 -4
  432. package/docs/architecture/restricted-codex-analysis.md +36 -2
  433. package/docs/architecture/state-driven-executor.md +4 -5
  434. package/docs/architecture/task-protocol-support.md +3 -5
  435. package/docs/architecture/{terminal-product-lane.md → terminal-product-route.md} +24 -16
  436. package/docs/contracts/run-bundle.md +10 -8
  437. package/docs/contracts/schemas.md +109 -53
  438. package/docs/contracts/study-analysis.md +24 -0
  439. package/docs/decisions/0004-participant-desktops-stay-in-the-route.md +2 -1
  440. package/docs/product/automatic-analysis.md +1 -0
  441. package/docs/ramp/README.md +2 -1
  442. package/package.json +4 -4
  443. package/skills/humanish/SKILL.md +3 -2
  444. package/skills/humanish/agents/openai.yaml +1 -1
  445. package/dist/deprecated.d.ts +0 -90
  446. package/dist/deprecated.js +0 -106
  447. package/dist/lab/bag-overrides.d.ts +0 -18
  448. package/dist/lab/bag-overrides.js +0 -121
  449. package/dist/routes/computer-use/legacy-lane-spec.d.ts +0 -62
  450. package/dist/routes/computer-use/legacy-lane-spec.js +0 -39
  451. /package/dist/routes/computer-use/{fanout-lanes.d.ts → fanout-records.d.ts} +0 -0
  452. /package/dist/routes/computer-use/{run-lanes.d.ts → live-phase.d.ts} +0 -0
  453. /package/dist/routes/shared-world/{seat-records.d.ts → bundle-records.d.ts} +0 -0
@@ -1,9 +1,10 @@
1
1
  import { automaticAnalysisBudget, formatAutomaticAnalysisBudget, } from "../../analysis/automatic-config.js";
2
2
  import { resolve } from "node:path";
3
3
  import { Command } from "commander";
4
- import { deriveStudyFacts } from "../telemetry.js";
4
+ import { deriveRunFacts } from "../telemetry.js";
5
5
  import { resolveLabManifest } from "../../lab/discover.js";
6
- import { resolveLabDryRun } from "../../lab/plan.js";
6
+ import { planLab, resolveLabDryRun } from "../../lab/plan.js";
7
+ import { keyNamesOf } from "../../lab/requirements.js";
7
8
  import { routeOf } from "../../lab/plan.js";
8
9
  import { computerUseRouteRun } from "./lab-route-computer-use.js";
9
10
  import { runRoute } from "./lab-route-run.js";
@@ -12,8 +13,9 @@ import { sharedWorldRouteRun } from "./lab-route-shared-world.js";
12
13
  import { previewRouteRun } from "./lab-route-preview.js";
13
14
  import { terminalRouteRun } from "./lab-route-terminal.js";
14
15
  import { maybeLoadAdapterScorer } from "./lab-scorer.js";
15
- import { discoverCliKeys, formatRunHuman, noteStudyFacts, writeResult, } from "../io.js";
16
+ import { discoverCliKeys, formatRunHuman, noteRunFacts, writeResult, } from "../io.js";
16
17
  import { watchExposeRequested } from "../observer-follow.js";
18
+ import { beginRunSignalPhase } from "./run-signals.js";
17
19
  import { WATCH_SAFE_NOT_APPLICABLE_MESSAGE } from "../../observer/exposure.js";
18
20
  export async function runLabCommand(args) {
19
21
  const resolved = await resolveLabManifest(args.options.cwd, args.lab);
@@ -35,7 +37,7 @@ export async function runLabCommand(args) {
35
37
  const lab = { id: config.id, path: resolved.path, origin: resolved.origin };
36
38
  // Named here, once, for every route: a preview or terminal result carries no labId, so the
37
39
  // starter lab `first-run` went unnamed in telemetry while the computer-use ones were named.
38
- noteStudyFacts(args.command, deriveStudyFacts({ labId: config.id }));
40
+ noteRunFacts(args.command, deriveRunFacts({ labId: config.id }));
39
41
  const route = routeOf(config);
40
42
  if (route !== "computer-use" && labRerunFlagsRequested(args.options)) {
41
43
  writeUnsupportedRerunFlagsResult(args, route);
@@ -76,52 +78,81 @@ export async function runLabCommand(args) {
76
78
  return;
77
79
  }
78
80
  // Only a live run reads provider keys, so a dry run looks none up: no `gh auth token`, no e2b
79
- // login, overlay or key store. This comes after the option refusals above, which read no key,
80
- // and before the route's CLI setup and prepareLab, which do.
81
+ // login, overlay or key store. A live run fills every key it finds and prints a line only for
82
+ // the keys its plan reads. This comes after the option refusals above, which read no key, and
83
+ // before the route's CLI setup and prepareLab, which do.
81
84
  if (resolveLabDryRun(config, args.options.dryRun, true) === false) {
82
- await discoverCliKeys({ io: args.io, cwd: args.options.cwd });
85
+ const announced = announcedKeyNames(config, args.options);
86
+ await discoverCliKeys({
87
+ io: args.io,
88
+ cwd: args.options.cwd,
89
+ ...(announced === undefined ? {} : { announced }),
90
+ });
83
91
  }
84
92
  // The route's CLI setup refuses bad options, runLab's plan refuses bad labs, and the route's local
85
93
  // checks (keys, runtime auth, subject env, the local agent, caps) refuse this machine, all before
86
94
  // the scorer loads, so none imports the scorer's host code. The scripted route's checks and the
87
95
  // run-id claim still come after it; the scripted route takes no scorer.
88
- const run = routeRunFor(route, { ...args, config, labProvenance: lab });
89
- if (run === undefined)
96
+ const routeRun = routeRunFor(route, { ...args, config });
97
+ if (routeRun === undefined)
90
98
  return;
91
- // #316: resolve + load a config-declared/CLI-flagged adopter scorer FAIL-CLOSED, before any spend,
92
- // and only for a plan that will run. A declared gate that cannot load (bad ref, not found, load
93
- // failure, no hooks, unsupported route) aborts with exit 2 rather than green-passing.
94
- await runRoute(config, run, async () => {
95
- const scorerLoad = await maybeLoadAdapterScorer({
96
- cwd: args.options.cwd,
97
- config,
98
- route,
99
- flag: args.options.scorer,
100
- });
101
- if (!scorerLoad.ok) {
102
- const result = {
103
- schema: "humanish.run-result.v1",
104
- ok: false,
105
- cwd: resolve(args.options.cwd),
106
- warnings: [],
107
- error: scorerLoad.error,
108
- };
109
- writeResult(args.command, args.io, result, formatRunHuman);
110
- args.io.setExitCode(2);
111
- return undefined;
112
- }
113
- const scorer = scorerLoad.scorer;
114
- if (scorer) {
115
- // Cross-repo guardrail: `humanish lab run` now import()s host JS named in the manifest.
116
- // Surface it so the invoker (who may not be the manifest author) knows executable code ran.
117
- args.io.writeErr(`warning: review scorer ${scorer.provenance.ref} (${scorer.provenance.source}) is executable host code loaded and run in-process — review it as code, not config.\n`);
118
- }
119
- const analysisBudget = automaticAnalysisBudget(config.review?.analysis, route);
120
- if (analysisBudget && resolveLabDryRun(config, args.options.dryRun, true) === false) {
121
- args.io.writeErr(`${formatAutomaticAnalysisBudget(analysisBudget)}\n`);
122
- }
123
- return scorer === undefined ? {} : { scorer };
124
- });
99
+ // Every route's runLab options carry the lab's provenance, from here only.
100
+ const run = { ...routeRun, options: { ...routeRun.options, lab } };
101
+ // From here a signal marks the run interrupted and reclaims its sandboxes (run-signals.ts).
102
+ const signals = beginRunSignalPhase(args.io);
103
+ try {
104
+ // #316: resolve + load a config-declared/CLI-flagged adopter scorer FAIL-CLOSED, before any
105
+ // spend, and only for a plan that will run. A declared gate that cannot load (bad ref, not
106
+ // found, load failure, no hooks, unsupported route) aborts with exit 2 rather than green-passing.
107
+ await runRoute(config, run, async () => {
108
+ const scorerLoad = await maybeLoadAdapterScorer({
109
+ cwd: args.options.cwd,
110
+ config,
111
+ route,
112
+ flag: args.options.scorer,
113
+ });
114
+ if (!scorerLoad.ok) {
115
+ const result = {
116
+ schema: "humanish.run-result.v1",
117
+ ok: false,
118
+ cwd: resolve(args.options.cwd),
119
+ warnings: [],
120
+ error: scorerLoad.error,
121
+ };
122
+ writeResult(args.command, args.io, result, formatRunHuman);
123
+ args.io.setExitCode(2);
124
+ return undefined;
125
+ }
126
+ const scorer = scorerLoad.scorer;
127
+ if (scorer) {
128
+ // Cross-repo guardrail: `humanish lab run` now import()s host JS named in the manifest.
129
+ // Surface it so the invoker (who may not be the manifest author) knows executable code ran.
130
+ args.io.writeErr(`warning: review scorer ${scorer.provenance.ref} (${scorer.provenance.source}) is executable host code loaded and run in-process — review it as code, not config.\n`);
131
+ }
132
+ const analysisBudget = automaticAnalysisBudget(config.review?.analysis, route);
133
+ if (analysisBudget && resolveLabDryRun(config, args.options.dryRun, true) === false) {
134
+ args.io.writeErr(`${formatAutomaticAnalysisBudget(analysisBudget)}\n`);
135
+ }
136
+ return scorer === undefined ? {} : { scorer };
137
+ },
138
+ // The run is over before presentation, which may own shutdown itself (watch's Observer).
139
+ // A shutdown already begun keeps its handlers and finishes through its cleanups.
140
+ signals.release);
141
+ }
142
+ finally {
143
+ signals.release();
144
+ }
145
+ }
146
+ /**
147
+ * The filled keys a live run prints a line for: the ones its plan reads. Count and rerun options
148
+ * change who runs, not which keys. Undefined, so every fill is printed, when a scorer is declared,
149
+ * since its host code may read any key, and when the lab does not plan.
150
+ */
151
+ function announcedKeyNames(config, options) {
152
+ if (options.scorer !== undefined || config.review?.scorer !== undefined)
153
+ return undefined;
154
+ const planned = planLab(config, { cwd: options.cwd, dryRun: false });
155
+ return planned.ok ? keyNamesOf(planned.planned.plan) : undefined;
125
156
  }
126
157
  /** The route's CLI setup. Undefined when the setup has already written its own result. */
127
158
  function routeRunFor(route, args) {
@@ -142,7 +173,7 @@ function routeRunFor(route, args) {
142
173
  }
143
174
  }
144
175
  function labRerunFlagsRequested(options) {
145
- return options.rerunFailedFrom !== undefined || options.lanes !== undefined;
176
+ return options.rerunFailedFrom !== undefined || options.participants !== undefined;
146
177
  }
147
178
  function writeUnsupportedRerunFlagsResult(args, route) {
148
179
  const result = {
@@ -152,7 +183,7 @@ function writeUnsupportedRerunFlagsResult(args, route) {
152
183
  warnings: [],
153
184
  error: {
154
185
  code: "HUMANISH_UNSUPPORTED_RERUN_FLAGS",
155
- message: `--rerun-failed-from/--lanes apply only to computer-use fan-out labs; this lab resolved to the ${route} route.`,
186
+ message: `--rerun-failed-from/--participants apply only to computer-use fan-out labs; this lab resolved to the ${route} route.`,
156
187
  },
157
188
  };
158
189
  writeResult(args.command, args.io, result, formatRunHuman);
@@ -1,4 +1,3 @@
1
- import type { RunLabProvenance } from "../../run/status.js";
2
1
  import { type AdapterScorerModule } from "../../lab/adapter-scorer-loader.js";
3
2
  import type { LabRoute } from "../../lab/plan.js";
4
3
  import type { RunScorerProvenance } from "../../run/bundle.js";
@@ -18,7 +17,6 @@ export interface LoadedAdapterScorer {
18
17
  export declare function maybeLoadAdapterScorer(args: {
19
18
  cwd: string;
20
19
  config: LabConfig;
21
- labProvenance?: RunLabProvenance;
22
20
  route: LabRoute;
23
21
  flag: string | undefined;
24
22
  }): Promise<{
@@ -4,6 +4,7 @@ import { Command, Option } from "commander";
4
4
  import { inspectLabManifest, listLabManifests } from "../../lab/discover.js";
5
5
  import { runLabPreflight, } from "../../lab/preflight.js";
6
6
  import { runLabCommand } from "./lab-run.js";
7
+ import { countOption, participantsOption } from "../renamed-options.js";
7
8
  import { applyEnvFileOption, JSON_OPTION_DESCRIPTION, parsePositiveInteger, writeResult, } from "../io.js";
8
9
  export function registerLabCommands(parent, io) {
9
10
  const lab = parent
@@ -45,10 +46,13 @@ export function registerLabCommands(parent, io) {
45
46
  .option("--no-open", "Render without opening a browser.")
46
47
  .option("--detach", "Render/open once and exit without attached watch server.")
47
48
  .option("--port <port>", "Local observer server port when following.", "0")
48
- .option("--sims <count>", "Override the synthetic or headed desktop participant count.")
49
- .option("--count <count>", "Computer-use only: override the headed desktop participant count.")
49
+ .option("--count <count>", "Override the participant count of a preview or computer-use lab.")
50
+ // The older spelling of --count, hidden and noted on stderr (renamed-options.ts).
51
+ .addOption(new Option("--sims <count>").hideHelp())
50
52
  .option("--rerun-failed-from <run>", "CUA fan-out only: create a new run for failed participants from a prior run.")
51
- .option("--lanes <participant-ids>", "CUA rerun only: comma-separated participant ids to rerun from the source run.")
53
+ .option("--participants <ids>", "CUA rerun only: comma-separated participant ids from the source run. Ids are the lab's declared actors[0].lanes[].id, or lane-01, lane-02, … by position.")
54
+ // The older spelling of --participants, hidden and noted on stderr (renamed-options.ts).
55
+ .addOption(new Option("--lanes <participant-ids>").hideHelp())
52
56
  .option("--run-id <id>", "Explicit lab run id; refused when that run already exists.")
53
57
  .option("--cwd <path>", "Target project directory.", ".")
54
58
  .option("--scorer <path>", "Terminal/computer-use/shared-world labs only: repo-relative adopter scorer module (.mjs). Overrides review.scorer.ref. Executable code: review it as code.")
@@ -57,7 +61,7 @@ export function registerLabCommands(parent, io) {
57
61
  "",
58
62
  "Examples:",
59
63
  " humanish lab run first-run",
60
- " humanish lab run fanout-demo --rerun-failed-from latest --lanes lane-02,lane-04",
64
+ " humanish lab run fanout-demo --rerun-failed-from latest --participants lane-02,lane-04",
61
65
  " humanish lab run my-terminal-lab --scorer scorers/product.mjs",
62
66
  " humanish lab run .humanish/labs/private-dogfood.yaml --env-file .humanish/local/provider.env",
63
67
  "",
@@ -118,6 +122,8 @@ async function handleLabPreflight(io, labName, options, command) {
118
122
  io.setExitCode(result.ok ? 0 : 2);
119
123
  }
120
124
  async function handleLabRun(io, labName, options, command) {
125
+ const count = countOption(io, options);
126
+ const participants = participantsOption(io, options);
121
127
  if (!(await applyEnvFileOption({
122
128
  command,
123
129
  cwd: options.cwd,
@@ -133,7 +139,7 @@ async function handleLabRun(io, labName, options, command) {
133
139
  io,
134
140
  lab: labName,
135
141
  mode: "run",
136
- options,
142
+ options: { ...options, count, participants },
137
143
  });
138
144
  }
139
145
  function formatLabListHuman(result) {
@@ -177,7 +183,7 @@ function formatLabPreflightHuman(result) {
177
183
  return ([
178
184
  `humanish lab preflight ${result.ok ? "passed" : "failed"}`,
179
185
  `lab: ${result.labId ?? result.lab}`,
180
- ...(result.backend ? [`backend: ${result.backend}`] : []),
186
+ ...(result.route ? [`route: ${result.route}`] : []),
181
187
  `reachability: ${result.reachability}`,
182
188
  `targets: ${checkedTargets.length ? `${reachableTargets.length}/${checkedTargets.length} reachable` : `${result.targets.length} declared, not checked`}`,
183
189
  ...(blockedTargets.length ? [`blocked-targets: ${blockedTargets.length}`] : []),
@@ -292,6 +292,7 @@ async function reportServe(io, command, options, server, plan, edge) {
292
292
  : {}),
293
293
  runsListed: server.runsListed,
294
294
  ...(server.shareReadyCount !== undefined ? { shareReadyCount: server.shareReadyCount } : {}),
295
+ ...(server.hiddenRuns !== undefined ? { hiddenRuns: server.hiddenRuns } : {}),
295
296
  ...(server.entryRunId ? { entryRunId: server.entryRunId } : {}),
296
297
  opened: openResult.opened,
297
298
  ...(openResult.command ? { openCommand: openResult.command } : {}),
@@ -309,6 +310,26 @@ async function reportServe(io, command, options, server, plan, edge) {
309
310
  },
310
311
  }, { json: wantsMachine });
311
312
  }
313
+ /**
314
+ * The runs --safe left out, grouped by grade and reasons, and how to share one. A run held back
315
+ * only for raw screenshots can be exported as a copy with blurred screenshots; the read-results
316
+ * docs name that step.
317
+ */
318
+ function hiddenRunLines(groups) {
319
+ const total = groups.reduce((sum, group) => sum + group.runs, 0);
320
+ if (total === 0)
321
+ return [];
322
+ const runs = (count) => `${count} run${count === 1 ? "" : "s"}`;
323
+ const lines = [`hidden: ${runs(total)} not share_ready`];
324
+ for (const group of groups) {
325
+ const reasons = group.reasons.length > 0 ? ` (${group.reasons.join(", ")})` : "";
326
+ lines.push(` ${runs(group.runs)} ${group.status}${reasons}`);
327
+ }
328
+ if (groups.some((group) => group.reasons.join(",") === "RAW_SCREENSHOTS"))
329
+ lines.push("share: a run held back only for RAW_SCREENSHOTS can be copied with blurred screenshots: `humanish export --run <id> --format bundle --redact-screenshots --out <dir>`, then `humanish serve --safe --cwd <dir>`");
330
+ lines.push("why: `humanish verify --run <id>` explains each reason");
331
+ return lines;
332
+ }
312
333
  function formatServeHuman(result) {
313
334
  if (!result.ok) {
314
335
  return ([
@@ -323,6 +344,7 @@ function formatServeHuman(result) {
323
344
  `mode: ${result.mode}${modeSuffix}`,
324
345
  `library: ${result.url ?? ""}`,
325
346
  `runs: ${result.runsListed}`,
347
+ ...hiddenRunLines(result.hiddenRuns ?? []),
326
348
  ];
327
349
  if (result.publicUrl) {
328
350
  lines.push(`public: ${result.publicUrl}`);
@@ -0,0 +1,30 @@
1
+ import { type ReclaimHooks } from "../../run/reclaim.js";
2
+ import type { RunInterruptSignal } from "../../run/status.js";
3
+ import type { CliIo } from "../io.js";
4
+ interface RunSignalTarget {
5
+ on(event: RunInterruptSignal, listener: () => void): unknown;
6
+ removeListener(event: RunInterruptSignal, listener: () => void): unknown;
7
+ }
8
+ /** Seams for tests; the CLI passes none. */
9
+ export interface RunSignalOptions {
10
+ signalTarget?: RunSignalTarget;
11
+ exit?: (code: number) => void;
12
+ reclaim?: ReclaimHooks;
13
+ deadlineMs?: number;
14
+ }
15
+ /**
16
+ * Register work a committed shutdown finishes before it exits, such as closing watch's Observer
17
+ * server and tunnel. It runs within the same deadline as reclaim. The returned function removes it.
18
+ */
19
+ export declare function onRunShutdown(cleanup: () => Promise<void>): () => void;
20
+ /**
21
+ * Install the run's signal handling. `end` removes it. `release` removes it unless a shutdown has
22
+ * begun, which keeps its handlers so a second signal still exits at once.
23
+ */
24
+ export declare function beginRunSignalPhase(io: Pick<CliIo, "writeErr">, options?: RunSignalOptions): {
25
+ end(): void;
26
+ release(): void;
27
+ };
28
+ /** Analysis is starting, and its own cancel handlers take the signals from here. */
29
+ export declare function handOverRunSignals(): void;
30
+ export {};
@@ -0,0 +1,146 @@
1
+ // The run command's signal handling outside post-run analysis. The first interrupt, terminate or
2
+ // hangup signal marks each unfinished run in this process interrupted, kills the E2B sandboxes
3
+ // its create-time receipts name, runs the registered shutdown cleanups (watch's Observer server
4
+ // and tunnel) and exits 128+n, all within one deadline; a second signal exits at once. Routes
5
+ // take no abort signal, so the bundle is not finished, but the record ends at once and no
6
+ // journaled sandbox waits for its timeout. Analysis has its own cancel handlers
7
+ // (analysis-signals.ts) and takes over when it starts.
8
+ import path from "node:path";
9
+ import { activeRuns } from "../../run/active-runs.js";
10
+ import { LATEST_RUN_ALIAS } from "../../run/locate.js";
11
+ import { reclaimPinnedRunSandboxes, } from "../../run/reclaim.js";
12
+ import { SANDBOX_RECEIPTS_ARTIFACT } from "../../run/sandbox-receipts.js";
13
+ import { exitCodeForSignal } from "../observer-follow.js";
14
+ /** How long the handler waits for interrupt, reclaim and cleanups before it exits anyway. */
15
+ const INTERRUPT_RECLAIM_DEADLINE_MS = 10_000;
16
+ /** Each kill's own request timeout, inside the deadline. */
17
+ const KILL_REQUEST_TIMEOUT_MS = 8_000;
18
+ const SIGNALS = ["SIGINT", "SIGTERM", "SIGHUP"];
19
+ let current;
20
+ const shutdownCleanups = new Set();
21
+ /**
22
+ * Register work a committed shutdown finishes before it exits, such as closing watch's Observer
23
+ * server and tunnel. It runs within the same deadline as reclaim. The returned function removes it.
24
+ */
25
+ export function onRunShutdown(cleanup) {
26
+ shutdownCleanups.add(cleanup);
27
+ return () => {
28
+ shutdownCleanups.delete(cleanup);
29
+ };
30
+ }
31
+ /**
32
+ * Install the run's signal handling. `end` removes it. `release` removes it unless a shutdown has
33
+ * begun, which keeps its handlers so a second signal still exits at once.
34
+ */
35
+ export function beginRunSignalPhase(io, options = {}) {
36
+ const target = options.signalTarget ?? process;
37
+ const exit = options.exit ??
38
+ ((code) => {
39
+ process.exit(code);
40
+ });
41
+ const handlers = new Map();
42
+ let stopping = false;
43
+ const end = () => {
44
+ for (const [signal, handler] of handlers)
45
+ target.removeListener(signal, handler);
46
+ handlers.clear();
47
+ if (current?.end === end)
48
+ current = undefined;
49
+ };
50
+ const release = () => {
51
+ if (!stopping)
52
+ end();
53
+ };
54
+ const onSignal = (signal) => {
55
+ if (stopping) {
56
+ exit(exitCodeForSignal(signal));
57
+ return;
58
+ }
59
+ stopping = true;
60
+ void stopActiveRuns(io, signal, options)
61
+ .catch(() => undefined)
62
+ .finally(() => exit(exitCodeForSignal(signal)));
63
+ };
64
+ current?.end();
65
+ for (const signal of SIGNALS) {
66
+ const handler = () => onSignal(signal);
67
+ handlers.set(signal, handler);
68
+ target.on(signal, handler);
69
+ }
70
+ current = { end, release };
71
+ return { end, release };
72
+ }
73
+ /** Analysis is starting, and its own cancel handlers take the signals from here. */
74
+ export function handOverRunSignals() {
75
+ current?.release();
76
+ }
77
+ /**
78
+ * Interrupt, reclaim and the registered cleanups all run inside one deadline that starts before
79
+ * the first status write is awaited, so a stalled write or kill still ends in a bounded exit.
80
+ */
81
+ async function stopActiveRuns(io, signal, options) {
82
+ const deadlineMs = options.deadlineMs ?? INTERRUPT_RECLAIM_DEADLINE_MS;
83
+ const runs = activeRuns();
84
+ // Absent: still pending at the deadline. Null: the run had finished, so there is nothing to say.
85
+ const reports = new Map();
86
+ const work = Promise.all([
87
+ ...runs.map(async (run) => {
88
+ // False when the run had already finished: its route released what it held.
89
+ if (!(await run.status.interrupt(signal))) {
90
+ reports.set(run, null);
91
+ return;
92
+ }
93
+ const reclaimed = await reclaimPinnedRunSandboxes(run.cwd, run.paths, {
94
+ requestTimeoutMs: KILL_REQUEST_TIMEOUT_MS,
95
+ ...options.reclaim,
96
+ }).catch(() => undefined);
97
+ reports.set(run, reclaimSummary(run, reclaimed));
98
+ }),
99
+ ...[...shutdownCleanups].map((cleanup) => cleanup().catch(() => undefined)),
100
+ ]);
101
+ const finished = await withDeadline(work, deadlineMs);
102
+ for (const run of runs) {
103
+ const report = reports.get(run);
104
+ if (report === null)
105
+ continue;
106
+ const text = report ??
107
+ (finished === "deadline"
108
+ ? `stopping did not finish within ${deadlineMs / 1000} s; ${recovery(run)}.`
109
+ : `reclaim failed; ${recovery(run)}.`);
110
+ try {
111
+ io.writeErr(`humanish: ${signal}: run ${run.runId} marked interrupted; ${text}\n`);
112
+ }
113
+ catch {
114
+ // A hung-up terminal cannot take the line; the status record and reclaim receipt hold it.
115
+ }
116
+ }
117
+ }
118
+ function withDeadline(work, ms) {
119
+ let timer;
120
+ const deadline = new Promise((resolve) => {
121
+ timer = setTimeout(() => resolve("deadline"), ms);
122
+ });
123
+ return Promise.race([work, deadline]).finally(() => clearTimeout(timer));
124
+ }
125
+ /**
126
+ * How to finish what the handler could not. A run whose id is the alias `latest` cannot be named
127
+ * to reclaim, which would resolve the newest run instead, so it gets its receipts path.
128
+ */
129
+ function recovery(run) {
130
+ return run.runId === LATEST_RUN_ALIAS
131
+ ? `its id is \`${LATEST_RUN_ALIAS}\`, which \`humanish reclaim --run\` reads as the newest run, so kill the ids in ${path.join(run.paths.relativeRunRoot, SANDBOX_RECEIPTS_ARTIFACT)} by hand`
132
+ : `run \`humanish reclaim --run ${run.runId}\``;
133
+ }
134
+ function reclaimSummary(run, reclaimed) {
135
+ if (reclaimed === undefined)
136
+ return `reclaim failed; ${recovery(run)}.`;
137
+ if (reclaimed.error !== undefined)
138
+ return reclaimed.error.message;
139
+ if (reclaimed.outcomes.length === 0)
140
+ return "no sandbox receipts to reclaim.";
141
+ const counts = new Map();
142
+ for (const outcome of reclaimed.outcomes)
143
+ counts.set(outcome.state, (counts.get(outcome.state) ?? 0) + 1);
144
+ const tally = [...counts].map(([state, count]) => `${count} ${state}`).join(", ");
145
+ return reclaimed.ok ? `sandboxes: ${tally}.` : `sandboxes: ${tally}; ${recovery(run)} to retry.`;
146
+ }
@@ -6,6 +6,7 @@ import { runDryRun } from "../../run/dry-run.js";
6
6
  import { verifyRun } from "../../verify/verify.js";
7
7
  import { reclaimPreflightSandboxes, reclaimRunSandboxes, } from "../../run/reclaim.js";
8
8
  import { runLabCommand } from "./lab-run.js";
9
+ import { countOption } from "../renamed-options.js";
9
10
  import { applyEnvFileOption, formatRunHuman, JSON_OPTION_DESCRIPTION, parsePositiveInteger, writeResult, } from "../io.js";
10
11
  const SCRIPTED_BROWSER_DOCS_URL = "https://humanish.dev/docs/lab-manifests#scripted-browser-scenarios";
11
12
  export function registerRunCommand(parent, io) {
@@ -24,7 +25,9 @@ export function registerRunCommand(parent, io) {
24
25
  .option("--no-open", "Render without opening a browser.")
25
26
  .option("--detach", "Render/open once and exit without an attached watch server.")
26
27
  .option("--port <port>", "Local observer server port when following.", "0")
27
- .option("--sims <count>", "Simulation count. A dry run accepts any positive count.")
28
+ .option("--count <count>", "Override the participant count of a preview or computer-use lab, or of the synthetic run without a lab.")
29
+ // The older spelling of --count, hidden and noted on stderr (renamed-options.ts).
30
+ .addOption(new Option("--sims <count>").hideHelp())
28
31
  // Agents with an older installed skill still send --app-url. Accepting it hidden lets the
29
32
  // refusal name the replacement; commander's bare unknown-option error names nothing. Delete
30
33
  // after 0.106.x.
@@ -34,6 +37,7 @@ export function registerRunCommand(parent, io) {
34
37
  .option("--run-id <id>", "Explicit run id for deterministic fixture tests; refused when that run already exists.")
35
38
  .option("--json", JSON_OPTION_DESCRIPTION)
36
39
  .action(async (lab, options, command) => {
40
+ const count = countOption(io, options);
37
41
  if (options.appUrl !== undefined) {
38
42
  const result = {
39
43
  schema: "humanish.run-result.v1",
@@ -67,12 +71,12 @@ export function registerRunCommand(parent, io) {
67
71
  mode: "run",
68
72
  // Forwarded wholesale, exactly as `lab run` does. Cherry-picking a subset here is what
69
73
  // made the two commands disagree in the first place.
70
- options: options,
74
+ options: { ...options, count },
71
75
  });
72
76
  return;
73
77
  }
74
- const participantCount = options.sims === undefined ? undefined : parsePositiveInteger(options.sims);
75
- if (options.sims !== undefined && participantCount === null) {
78
+ const participantCount = count === undefined ? undefined : parsePositiveInteger(count);
79
+ if (participantCount === null) {
76
80
  const result = {
77
81
  schema: "humanish.run-result.v1",
78
82
  ok: false,
@@ -80,7 +84,7 @@ export function registerRunCommand(parent, io) {
80
84
  warnings: [],
81
85
  error: {
82
86
  code: "HUMANISH_INVALID_SIM_COUNT",
83
- message: "--sims must be a positive integer.",
87
+ message: "--count must be a positive integer.",
84
88
  },
85
89
  };
86
90
  writeResult(command, io, result, formatRunHuman);
@@ -91,9 +95,7 @@ export function registerRunCommand(parent, io) {
91
95
  cwd: options.cwd,
92
96
  ...(options.dryRun === undefined ? {} : { dryRun: options.dryRun }),
93
97
  ...(options.runId === undefined ? {} : { runId: options.runId }),
94
- ...(participantCount === undefined || participantCount === null
95
- ? {}
96
- : { simCount: participantCount }),
98
+ ...(participantCount === undefined ? {} : { simCount: participantCount }),
97
99
  // Rendered the way `watch` renders it, so a bundle is the same bundle whichever command
98
100
  // produced it (#597). A render failure is a warning on the result.
99
101
  observer: { open: false },
@@ -3,6 +3,7 @@ import { renderObserver, serveObserver } from "../../observer/render.js";
3
3
  import { WATCH_SAFE_NOT_APPLICABLE_MESSAGE } from "../../observer/exposure.js";
4
4
  import { runDryRun } from "../../run/dry-run.js";
5
5
  import { runLabCommand } from "./lab-run.js";
6
+ import { countOption } from "../renamed-options.js";
6
7
  import { applyEnvFileOption, collectRepeated, formatRunHuman, JSON_OPTION_DESCRIPTION, parseObserverPort, parsePositiveInteger, wantsJson, writeResult, } from "../io.js";
7
8
  import { followObserver, formatObserverHuman, watchExposeRequested } from "../observer-follow.js";
8
9
  export function registerWatchCommand(parent, io) {
@@ -14,8 +15,9 @@ export function registerWatchCommand(parent, io) {
14
15
  .option("--lab <id-or-path>", "Explicit lab id or .yaml path.")
15
16
  .option("--run <id>", "Watch an existing run id or latest pointer.")
16
17
  .option("--dry-run", "Lab only: render contract evidence without live provider spend.")
17
- .option("--sims <count>", "Start a fresh synthetic run with this many participants before rendering. Defaults to 4 when --run is omitted.")
18
- .option("--count <count>", "Lab only: override the headed desktop participant count.")
18
+ .option("--count <count>", "With a lab, override a preview or computer-use lab's participant count. Without one, start a fresh synthetic run with this many participants; 4 when --run is omitted.")
19
+ // The older spelling of --count, hidden and noted on stderr (renamed-options.ts).
20
+ .addOption(new Option("--sims <count>").hideHelp())
19
21
  .option("--scorer <path>", "Terminal/computer-use/shared-world labs only: repo-relative adopter scorer module (.mjs). Overrides review.scorer.ref. Executable code: review it as code.")
20
22
  .option("--run-id <id>", "Explicit run id for deterministic fixture tests; refused when that run already exists.")
21
23
  .option("--cwd <path>", "Target project directory.", ".")
@@ -54,7 +56,8 @@ export function registerWatchCommand(parent, io) {
54
56
  ].join("\n"))
55
57
  .action((labArg, options, command) => handleWatch(io, labArg, options, command));
56
58
  }
57
- async function handleWatch(io, labArg, options, command) {
59
+ async function handleWatch(io, labArg, parsed, command) {
60
+ const options = { ...parsed, count: countOption(io, parsed) };
58
61
  const lab = options.lab ?? labArg;
59
62
  if (options.lab !== undefined && labArg !== undefined) {
60
63
  refuseWatch(command, io, options.cwd, {
@@ -151,7 +154,6 @@ async function watchLab(io, command, lab, options) {
151
154
  port: options.port,
152
155
  ...(options.runId === undefined ? {} : { runId: options.runId }),
153
156
  ...(options.scorer === undefined ? {} : { scorer: options.scorer }),
154
- ...(options.sims === undefined ? {} : { sims: options.sims }),
155
157
  ...(options.expose === undefined ? {} : { expose: options.expose }),
156
158
  ...(options.tunnel === undefined ? {} : { tunnel: options.tunnel }),
157
159
  ...(options.tunnelDomain === undefined ? {} : { tunnelDomain: options.tunnelDomain }),
@@ -164,21 +166,21 @@ async function watchLab(io, command, lab, options) {
164
166
  },
165
167
  });
166
168
  }
167
- /** Without a lab, watch shows existing evidence (`--run`) or a fresh synthetic run (`--sims`). */
169
+ /** Without a lab, watch shows existing evidence (`--run`) or a fresh synthetic run (`--count`). */
168
170
  function resolveWatchTarget(options, command) {
169
171
  const runOptionSource = typeof command.getOptionValueSource === "function"
170
172
  ? command.getOptionValueSource("run")
171
173
  : undefined;
172
174
  const runWasOmitted = runOptionSource === undefined || runOptionSource === "default";
173
- const participantCount = options.sims === undefined ? undefined : parsePositiveInteger(options.sims);
175
+ const participantCount = options.count === undefined ? undefined : parsePositiveInteger(options.count);
174
176
  const port = parseObserverPort(options.port);
175
- if (options.sims !== undefined && participantCount === null) {
176
- return { code: "HUMANISH_INVALID_SIM_COUNT", message: "--sims must be a positive integer." };
177
+ if (participantCount === null) {
178
+ return { code: "HUMANISH_INVALID_SIM_COUNT", message: "--count must be a positive integer." };
177
179
  }
178
- if (!runWasOmitted && options.sims !== undefined) {
180
+ if (!runWasOmitted && participantCount !== undefined) {
179
181
  return {
180
182
  code: "HUMANISH_WATCH_OPTION_CONFLICT",
181
- message: "Use either --run to watch existing evidence or --sims to start a fresh run, not both.",
183
+ message: "Use either --run to watch existing evidence or --count to start a fresh run, not both.",
182
184
  };
183
185
  }
184
186
  if (!runWasOmitted && options.runId !== undefined) {
@@ -1,5 +1,6 @@
1
1
  import { type KeyResolutionDeps } from "../keys/key-resolution.js";
2
2
  import { type DetectLocalAgentsOptions } from "../actors/local-agent/cli.js";
3
+ import { type LabSetupCheckArgs } from "../lab/doctor.js";
3
4
  declare const DOCTOR_SCHEMA = "humanish.doctor-result.v1";
4
5
  export interface DoctorResult {
5
6
  schema: typeof DOCTOR_SCHEMA;
@@ -35,5 +36,7 @@ export declare function doctor(cwdInput: string, options?: {
35
36
  localAgents?: DetectLocalAgentsOptions;
36
37
  /** Where the key probe looks for vendor stores; tests point it at a temp home. */
37
38
  keyDeps?: KeyResolutionDeps;
39
+ /** The hosted Codex participant's operator handshake; tests replace it. */
40
+ codexParticipantReadiness?: LabSetupCheckArgs["codexParticipantReadiness"];
38
41
  }): Promise<DoctorResult>;
39
42
  export {};