humanish 0.106.0 → 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 (459) hide show
  1. package/AGENTS.md +4 -2
  2. package/ARCHITECTURE.md +17 -16
  3. package/CHANGELOG.md +24 -0
  4. package/CONTEXT.md +42 -39
  5. package/CONTRIBUTING.md +8 -6
  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 +31 -4
  16. package/dist/actors/codex/qualified-versions.js +51 -6
  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 +10 -8
  45. package/dist/actors/contract.js +7 -1
  46. package/dist/actors/local-agent/cli.js +1 -1
  47. package/dist/actors/scripted-browser/actor.d.ts +2 -2
  48. package/dist/actors/scripted-browser/journey.js +15 -4
  49. package/dist/actors/scripted-browser/steps.js +18 -0
  50. package/dist/actors/scripted-browser/types.d.ts +11 -1
  51. package/dist/actors/stop-conditions.js +1 -3
  52. package/dist/analysis/automatic-completion.d.ts +13 -8
  53. package/dist/analysis/automatic-completion.js +14 -8
  54. package/dist/analysis/automatic-config.d.ts +1 -1
  55. package/dist/analysis/automatic-config.js +2 -2
  56. package/dist/analysis/automatic.d.ts +10 -4
  57. package/dist/analysis/automatic.js +17 -13
  58. package/dist/analysis/diagnostics.d.ts +23 -0
  59. package/dist/analysis/diagnostics.js +112 -0
  60. package/dist/analysis/evidence-sources.js +2 -2
  61. package/dist/analysis/evidence.js +6 -6
  62. package/dist/analysis/execute.d.ts +7 -1
  63. package/dist/analysis/execute.js +39 -12
  64. package/dist/analysis/job.d.ts +8 -5
  65. package/dist/analysis/job.js +15 -14
  66. package/dist/analysis/load.d.ts +1 -1
  67. package/dist/analysis/load.js +5 -5
  68. package/dist/analysis/provider.d.ts +9 -1
  69. package/dist/analysis/restricted-codex.d.ts +2 -0
  70. package/dist/analysis/restricted-codex.js +10 -1
  71. package/dist/analysis/service.d.ts +2 -0
  72. package/dist/analysis/service.js +31 -12
  73. package/dist/analysis/sharing.d.ts +2 -2
  74. package/dist/analysis/sharing.js +5 -5
  75. package/dist/analysis/store-executions.d.ts +5 -5
  76. package/dist/analysis/store-executions.js +12 -12
  77. package/dist/analysis/store.d.ts +4 -4
  78. package/dist/analysis/store.js +13 -13
  79. package/dist/cli/commands/analysis-signals.d.ts +2 -1
  80. package/dist/cli/commands/analysis-signals.js +4 -1
  81. package/dist/cli/commands/analyze.js +4 -2
  82. package/dist/cli/commands/comms.js +8 -1
  83. package/dist/cli/commands/lab-route-computer-use.d.ts +0 -2
  84. package/dist/cli/commands/lab-route-computer-use.js +26 -14
  85. package/dist/cli/commands/lab-route-preview.d.ts +1 -3
  86. package/dist/cli/commands/lab-route-preview.js +3 -4
  87. package/dist/cli/commands/lab-route-run.d.ts +6 -4
  88. package/dist/cli/commands/lab-route-run.js +6 -2
  89. package/dist/cli/commands/lab-route-scripted.d.ts +0 -2
  90. package/dist/cli/commands/lab-route-scripted.js +0 -1
  91. package/dist/cli/commands/lab-route-shared-world.d.ts +0 -2
  92. package/dist/cli/commands/lab-route-shared-world.js +0 -1
  93. package/dist/cli/commands/lab-route-terminal.d.ts +0 -2
  94. package/dist/cli/commands/lab-route-terminal.js +0 -1
  95. package/dist/cli/commands/lab-run.js +76 -45
  96. package/dist/cli/commands/lab-scorer.d.ts +0 -2
  97. package/dist/cli/commands/lab.js +12 -6
  98. package/dist/cli/commands/observe.js +22 -0
  99. package/dist/cli/commands/run-signals.d.ts +30 -0
  100. package/dist/cli/commands/run-signals.js +146 -0
  101. package/dist/cli/commands/runs.js +10 -8
  102. package/dist/cli/commands/watch.js +12 -10
  103. package/dist/cli/doctor.d.ts +3 -0
  104. package/dist/cli/doctor.js +10 -2
  105. package/dist/cli/io.d.ts +5 -4
  106. package/dist/cli/io.js +12 -8
  107. package/dist/cli/program.js +3 -3
  108. package/dist/cli/renamed-options.d.ts +11 -0
  109. package/dist/cli/renamed-options.js +18 -0
  110. package/dist/cli/telemetry.d.ts +2 -2
  111. package/dist/cli/telemetry.js +1 -1
  112. package/dist/comms/capture-surface.d.ts +1 -1
  113. package/dist/comms/catch-host.d.ts +1 -1
  114. package/dist/comms/connections.js +4 -4
  115. package/dist/comms/external-evidence.d.ts +27 -0
  116. package/dist/comms/external-evidence.js +93 -0
  117. package/dist/comms/lease-store.js +7 -7
  118. package/dist/comms/types.js +1 -1
  119. package/dist/evidence/browser-hygiene.d.ts +1 -1
  120. package/dist/evidence/browser-hygiene.js +1 -1
  121. package/dist/evidence/redaction.js +1 -1
  122. package/dist/evidence/secret-scrub.d.ts +9 -0
  123. package/dist/evidence/secret-scrub.js +96 -0
  124. package/dist/feedback/draft.js +7 -9
  125. package/dist/feedback/export-bundle.js +4 -4
  126. package/dist/feedback/export.js +9 -9
  127. package/dist/feedback/feedback.d.ts +1 -1
  128. package/dist/index.d.ts +3 -6
  129. package/dist/index.js +3 -6
  130. package/dist/keys/key-resolution.d.ts +3 -1
  131. package/dist/keys/key-resolution.js +18 -16
  132. package/dist/lab/adapter-extension.d.ts +28 -6
  133. package/dist/lab/adapter-extension.js +65 -126
  134. package/dist/lab/adapter-scorer-loader.d.ts +25 -5
  135. package/dist/lab/adapter-scorer-loader.js +24 -0
  136. package/dist/lab/composition-rules.d.ts +2 -0
  137. package/dist/lab/composition-rules.js +35 -26
  138. package/dist/lab/config.js +28 -26
  139. package/dist/lab/device-presets.d.ts +2 -2
  140. package/dist/lab/discover.js +1 -3
  141. package/dist/lab/doctor.d.ts +44 -9
  142. package/dist/lab/doctor.js +149 -41
  143. package/dist/lab/init-plan.js +1 -3
  144. package/dist/lab/keys.js +1 -1
  145. package/dist/lab/lab-deps.d.ts +85 -0
  146. package/dist/lab/lab-deps.js +4 -0
  147. package/dist/lab/parse/actors.js +8 -7
  148. package/dist/lab/parse/comms.js +3 -2
  149. package/dist/lab/parse/execution.js +3 -2
  150. package/dist/lab/parse/subject-state.js +2 -1
  151. package/dist/lab/parse/subject.js +2 -1
  152. package/dist/lab/parse/values.d.ts +0 -1
  153. package/dist/lab/parse/values.js +0 -3
  154. package/dist/lab/persona-resolve.d.ts +3 -3
  155. package/dist/lab/persona-resolve.js +4 -4
  156. package/dist/lab/persona.js +1 -3
  157. package/dist/lab/plan-base.d.ts +6 -2
  158. package/dist/lab/plan-base.js +22 -7
  159. package/dist/lab/plan-participants.d.ts +5 -5
  160. package/dist/lab/plan-participants.js +2 -2
  161. package/dist/lab/plan-types.d.ts +33 -7
  162. package/dist/lab/plan.d.ts +3 -11
  163. package/dist/lab/plan.js +18 -42
  164. package/dist/lab/preflight-result.js +0 -2
  165. package/dist/lab/preflight.d.ts +0 -3
  166. package/dist/lab/preflight.js +1 -1
  167. package/dist/lab/requirements.d.ts +20 -0
  168. package/dist/lab/requirements.js +54 -0
  169. package/dist/lab/route-inputs.d.ts +23 -12
  170. package/dist/lab/route-inputs.js +48 -51
  171. package/dist/lab/routing.d.ts +19 -24
  172. package/dist/lab/routing.js +14 -47
  173. package/dist/lab/run-lab-events.d.ts +6 -11
  174. package/dist/lab/run-lab-events.js +3 -11
  175. package/dist/lab/run-lab-homes.d.ts +64 -0
  176. package/dist/lab/run-lab-homes.js +3 -0
  177. package/dist/lab/run-lab-options.d.ts +12 -66
  178. package/dist/lab/run-lab-options.js +45 -275
  179. package/dist/lab/summary.d.ts +9 -4
  180. package/dist/lab/summary.js +15 -8
  181. package/dist/lab/types.d.ts +132 -132
  182. package/dist/lab/validation.d.ts +15 -15
  183. package/dist/lab/validation.js +28 -28
  184. package/dist/lab/warnings.js +20 -19
  185. package/dist/observer/artifact.d.ts +1 -1
  186. package/dist/observer/artifact.js +3 -3
  187. package/dist/observer/data.js +2 -2
  188. package/dist/observer/render.js +2 -2
  189. package/dist/observer/run-routes.d.ts +1 -1
  190. package/dist/observer/run-routes.js +5 -5
  191. package/dist/observer/serve.d.ts +17 -0
  192. package/dist/observer/serve.js +34 -3
  193. package/dist/observer-app.html +1 -1
  194. package/dist/routes/computer-use/bundle-parts.d.ts +1 -1
  195. package/dist/routes/computer-use/bundle-parts.js +10 -3
  196. package/dist/routes/computer-use/bundle.d.ts +4 -11
  197. package/dist/routes/computer-use/bundle.js +9 -15
  198. package/dist/routes/computer-use/diagnostics.d.ts +1 -1
  199. package/dist/routes/computer-use/diagnostics.js +1 -1
  200. package/dist/routes/computer-use/e2b-desktop/comms.d.ts +10 -10
  201. package/dist/routes/computer-use/e2b-desktop/comms.js +11 -14
  202. package/dist/routes/computer-use/e2b-desktop/desktop.d.ts +2 -2
  203. package/dist/routes/computer-use/e2b-desktop/desktop.js +4 -7
  204. package/dist/routes/computer-use/e2b-desktop/fidelity.d.ts +13 -13
  205. package/dist/routes/computer-use/e2b-desktop/fidelity.js +11 -11
  206. package/dist/routes/computer-use/e2b-desktop/prepare.d.ts +3 -3
  207. package/dist/routes/computer-use/e2b-desktop/prepare.js +33 -36
  208. package/dist/routes/computer-use/e2b-desktop/start.d.ts +1 -1
  209. package/dist/routes/computer-use/e2b-desktop/start.js +24 -23
  210. package/dist/routes/computer-use/e2b-desktop/state.d.ts +5 -4
  211. package/dist/routes/computer-use/e2b-desktop/state.js +5 -3
  212. package/dist/routes/computer-use/e2b-desktop/teardown.d.ts +1 -1
  213. package/dist/routes/computer-use/e2b-desktop/teardown.js +29 -17
  214. package/dist/routes/computer-use/external-comms.d.ts +4 -4
  215. package/dist/routes/computer-use/external-comms.js +19 -43
  216. package/dist/routes/computer-use/fanout-bundle.d.ts +3 -3
  217. package/dist/routes/computer-use/fanout-bundle.js +25 -29
  218. package/dist/routes/computer-use/{fanout-lanes.js → fanout-records.js} +25 -19
  219. package/dist/routes/computer-use/in-process-desktop.d.ts +3 -10
  220. package/dist/routes/computer-use/in-process-desktop.js +3 -9
  221. package/dist/routes/computer-use/live-flush.d.ts +7 -5
  222. package/dist/routes/computer-use/live-flush.js +19 -27
  223. package/dist/routes/computer-use/{run-lanes.js → live-phase.js} +14 -13
  224. package/dist/routes/computer-use/local-tree-pack.d.ts +3 -3
  225. package/dist/routes/computer-use/local-tree-pack.js +4 -5
  226. package/dist/routes/computer-use/local-vm.d.ts +11 -8
  227. package/dist/routes/computer-use/local-vm.js +39 -66
  228. package/dist/routes/computer-use/participant-desktop.d.ts +8 -23
  229. package/dist/routes/computer-use/participant-desktop.js +5 -25
  230. package/dist/routes/computer-use/{lanes.d.ts → participant-execution.d.ts} +10 -10
  231. package/dist/routes/computer-use/{lanes.js → participant-execution.js} +35 -22
  232. package/dist/routes/computer-use/participant-facts.d.ts +7 -1
  233. package/dist/routes/computer-use/participant-facts.js +12 -1
  234. package/dist/routes/computer-use/participant-model.d.ts +19 -11
  235. package/dist/routes/computer-use/participant-model.js +58 -33
  236. package/dist/routes/computer-use/participant-prompt.d.ts +3 -3
  237. package/dist/routes/computer-use/participant-prompt.js +4 -4
  238. package/dist/routes/computer-use/participant-runs.d.ts +6 -6
  239. package/dist/routes/computer-use/participant-runs.js +8 -8
  240. package/dist/routes/computer-use/plan.d.ts +21 -29
  241. package/dist/routes/computer-use/plan.js +59 -65
  242. package/dist/routes/computer-use/preflight.d.ts +6 -11
  243. package/dist/routes/computer-use/preflight.js +26 -18
  244. package/dist/routes/computer-use/rerun-selection.js +10 -6
  245. package/dist/routes/computer-use/result.d.ts +1 -1
  246. package/dist/routes/computer-use/result.js +40 -30
  247. package/dist/routes/computer-use/route.d.ts +3 -4
  248. package/dist/routes/computer-use/route.js +21 -19
  249. package/dist/routes/computer-use/self-report.d.ts +2 -2
  250. package/dist/routes/computer-use/self-report.js +4 -4
  251. package/dist/routes/computer-use/setup.d.ts +8 -17
  252. package/dist/routes/computer-use/setup.js +98 -86
  253. package/dist/routes/computer-use/single-bundle.d.ts +8 -11
  254. package/dist/routes/computer-use/single-bundle.js +20 -25
  255. package/dist/routes/computer-use/subject-projection.d.ts +5 -7
  256. package/dist/routes/computer-use/subject-projection.js +20 -19
  257. package/dist/routes/computer-use/types.d.ts +169 -211
  258. package/dist/routes/computer-use/types.js +13 -6
  259. package/dist/routes/preview.js +0 -1
  260. package/dist/routes/scripted/bundle.d.ts +3 -6
  261. package/dist/routes/scripted/bundle.js +11 -16
  262. package/dist/routes/scripted/plan.d.ts +5 -7
  263. package/dist/routes/scripted/plan.js +6 -4
  264. package/dist/routes/scripted/result.d.ts +4 -1
  265. package/dist/routes/scripted/result.js +10 -6
  266. package/dist/routes/scripted/route.js +10 -11
  267. package/dist/routes/scripted/scenario.d.ts +1 -1
  268. package/dist/routes/scripted/scenario.js +1 -1
  269. package/dist/routes/scripted/setup.d.ts +3 -2
  270. package/dist/routes/scripted/setup.js +22 -21
  271. package/dist/routes/scripted/subject.d.ts +8 -3
  272. package/dist/routes/scripted/subject.js +11 -6
  273. package/dist/routes/scripted/surface-records.js +1 -1
  274. package/dist/routes/scripted/surface-sessions.d.ts +2 -2
  275. package/dist/routes/scripted/surface-sessions.js +4 -4
  276. package/dist/routes/scripted/types.d.ts +19 -36
  277. package/dist/routes/scripted/types.js +1 -2
  278. package/dist/routes/shared-world/{seat-records.js → bundle-records.js} +8 -8
  279. package/dist/routes/shared-world/bundle.d.ts +2 -2
  280. package/dist/routes/shared-world/bundle.js +22 -26
  281. package/dist/routes/shared-world/checkpoints.d.ts +4 -4
  282. package/dist/routes/shared-world/checkpoints.js +4 -4
  283. package/dist/routes/shared-world/comms.d.ts +5 -4
  284. package/dist/routes/shared-world/comms.js +28 -41
  285. package/dist/routes/shared-world/external-public.d.ts +5 -5
  286. package/dist/routes/shared-world/external-public.js +19 -20
  287. package/dist/routes/shared-world/handoff.d.ts +4 -4
  288. package/dist/routes/shared-world/handoff.js +50 -50
  289. package/dist/routes/shared-world/lobby-code.d.ts +5 -5
  290. package/dist/routes/shared-world/lobby-code.js +13 -13
  291. package/dist/routes/shared-world/{seats.d.ts → participant-specs.d.ts} +8 -6
  292. package/dist/routes/shared-world/{seats.js → participant-specs.js} +46 -51
  293. package/dist/routes/shared-world/plan.d.ts +2 -6
  294. package/dist/routes/shared-world/plan.js +1 -2
  295. package/dist/routes/shared-world/provenance.d.ts +4 -4
  296. package/dist/routes/shared-world/provenance.js +4 -4
  297. package/dist/routes/shared-world/provisioned.d.ts +3 -2
  298. package/dist/routes/shared-world/provisioned.js +48 -41
  299. package/dist/routes/shared-world/result.js +31 -19
  300. package/dist/routes/shared-world/route.js +37 -41
  301. package/dist/routes/shared-world/setup.d.ts +5 -4
  302. package/dist/routes/shared-world/setup.js +12 -14
  303. package/dist/routes/shared-world/types.d.ts +53 -126
  304. package/dist/routes/shared-world/types.js +2 -2
  305. package/dist/routes/terminal/adapter.d.ts +8 -8
  306. package/dist/routes/terminal/adapter.js +28 -125
  307. package/dist/routes/terminal/bundle.d.ts +7 -12
  308. package/dist/routes/terminal/bundle.js +24 -34
  309. package/dist/routes/terminal/credentials.d.ts +2 -2
  310. package/dist/routes/terminal/credentials.js +16 -16
  311. package/dist/routes/terminal/dry-run.d.ts +1 -1
  312. package/dist/routes/terminal/dry-run.js +9 -11
  313. package/dist/routes/terminal/encoding.d.ts +1 -1
  314. package/dist/routes/terminal/encoding.js +2 -2
  315. package/dist/routes/terminal/ledger.d.ts +6 -6
  316. package/dist/routes/terminal/ledger.js +9 -9
  317. package/dist/routes/terminal/live-finish.d.ts +1 -2
  318. package/dist/routes/terminal/live-finish.js +21 -20
  319. package/dist/routes/terminal/live-sandbox.d.ts +4 -2
  320. package/dist/routes/terminal/live-sandbox.js +25 -21
  321. package/dist/routes/terminal/participant-text.d.ts +23 -0
  322. package/dist/routes/terminal/participant-text.js +165 -0
  323. package/dist/routes/terminal/plan.d.ts +2 -6
  324. package/dist/routes/terminal/plan.js +13 -9
  325. package/dist/routes/terminal/recorder.d.ts +6 -1
  326. package/dist/routes/terminal/recorder.js +12 -4
  327. package/dist/routes/terminal/result.js +4 -4
  328. package/dist/routes/terminal/route.js +26 -26
  329. package/dist/routes/terminal/runtime.d.ts +3 -2
  330. package/dist/routes/terminal/runtime.js +3 -3
  331. package/dist/routes/terminal/sandbox.d.ts +5 -3
  332. package/dist/routes/terminal/sandbox.js +13 -16
  333. package/dist/routes/terminal/session.js +16 -14
  334. package/dist/routes/terminal/token-usage.js +5 -5
  335. package/dist/routes/terminal/trace.d.ts +5 -8
  336. package/dist/routes/terminal/trace.js +9 -64
  337. package/dist/routes/terminal/types.d.ts +85 -93
  338. package/dist/routes/terminal/types.js +9 -4
  339. package/dist/run/active-runs.d.ts +13 -0
  340. package/dist/run/active-runs.js +12 -0
  341. package/dist/run/bundle-shape.d.ts +2 -1
  342. package/dist/run/bundle-shape.js +4 -4
  343. package/dist/run/bundle.d.ts +63 -56
  344. package/dist/run/bundle.js +9 -8
  345. package/dist/run/cost-summary.js +3 -2
  346. package/dist/run/costs.d.ts +6 -6
  347. package/dist/run/costs.js +10 -10
  348. package/dist/run/detail.d.ts +5 -5
  349. package/dist/run/detail.js +6 -6
  350. package/dist/run/dry-run.js +5 -12
  351. package/dist/run/{study-files.d.ts → evidence-files.d.ts} +4 -4
  352. package/dist/run/{study-files.js → evidence-files.js} +5 -5
  353. package/dist/run/feedback-shape.js +1 -1
  354. package/dist/run/judge.d.ts +16 -4
  355. package/dist/run/judge.js +24 -7
  356. package/dist/run/locate.d.ts +2 -0
  357. package/dist/run/locate.js +3 -1
  358. package/dist/run/outcomes.d.ts +3 -3
  359. package/dist/run/outcomes.js +3 -3
  360. package/dist/run/participant-records.d.ts +2 -2
  361. package/dist/run/participant-records.js +2 -2
  362. package/dist/run/paths.js +1 -1
  363. package/dist/run/pricing.d.ts +19 -9
  364. package/dist/run/pricing.js +30 -18
  365. package/dist/run/projection.d.ts +13 -13
  366. package/dist/run/projection.js +8 -8
  367. package/dist/run/reclaim.d.ts +7 -1
  368. package/dist/run/reclaim.js +24 -4
  369. package/dist/run/results.d.ts +1 -1
  370. package/dist/run/run-index.d.ts +1 -1
  371. package/dist/run/run-index.js +11 -11
  372. package/dist/run/run.d.ts +4 -2
  373. package/dist/run/run.js +9 -5
  374. package/dist/run/sandbox-receipts.d.ts +4 -4
  375. package/dist/run/sandbox-receipts.js +4 -4
  376. package/dist/run/shared-world-evidence.d.ts +47 -47
  377. package/dist/run/shared-world-shape.d.ts +3 -3
  378. package/dist/run/shared-world-shape.js +11 -11
  379. package/dist/run/stats.d.ts +5 -5
  380. package/dist/run/stats.js +8 -8
  381. package/dist/run/status.d.ts +16 -6
  382. package/dist/run/status.js +31 -8
  383. package/dist/run/streams.d.ts +26 -19
  384. package/dist/run/terminal-contract.d.ts +5 -5
  385. package/dist/run/terminal-contract.js +4 -4
  386. package/dist/run/transient-comms-secrets.d.ts +5 -0
  387. package/dist/run/transient-comms-secrets.js +12 -0
  388. package/dist/run-lab.d.ts +36 -34
  389. package/dist/run-lab.js +41 -37
  390. package/dist/subject/desktop-cli.d.ts +1 -1
  391. package/dist/subject/desktop-cli.js +1 -1
  392. package/dist/subject/local-tree-archive.d.ts +1 -1
  393. package/dist/subject/local-tree-archive.js +4 -4
  394. package/dist/subject/local-tree.d.ts +2 -2
  395. package/dist/subject/local-tree.js +1 -1
  396. package/dist/subject/node-bootstrap.d.ts +1 -1
  397. package/dist/subject/node-bootstrap.js +4 -4
  398. package/dist/subject/runtime.js +5 -5
  399. package/dist/subject/serve.js +1 -1
  400. package/dist/subject/steps.d.ts +4 -4
  401. package/dist/subject/steps.js +5 -5
  402. package/dist/substrates/desktop-session.d.ts +2 -0
  403. package/dist/substrates/e2b/cdp-probe.d.ts +2 -2
  404. package/dist/substrates/e2b/cdp-probe.js +1 -1
  405. package/dist/substrates/e2b/desktop-browser.d.ts +3 -3
  406. package/dist/substrates/e2b/desktop-browser.js +4 -4
  407. package/dist/substrates/e2b/desktop-cdp.d.ts +7 -7
  408. package/dist/substrates/e2b/desktop-cdp.js +4 -4
  409. package/dist/substrates/e2b/desktop-geometry.d.ts +4 -4
  410. package/dist/substrates/e2b/desktop-geometry.js +3 -3
  411. package/dist/substrates/e2b/desktop-media.js +2 -2
  412. package/dist/substrates/e2b/sandbox.d.ts +2 -0
  413. package/dist/substrates/e2b/sandbox.js +22 -8
  414. package/dist/substrates/e2b/sdk.d.ts +12 -1
  415. package/dist/substrates/e2b/sdk.js +30 -9
  416. package/dist/substrates/local/runtime-host.d.ts +2 -0
  417. package/dist/substrates/local/runtime-host.js +6 -0
  418. package/dist/tui/actions.d.ts +7 -5
  419. package/dist/tui/actions.js +18 -9
  420. package/dist/tui-app.js +42 -42
  421. package/dist/verify/actor.d.ts +10 -10
  422. package/dist/verify/actor.js +15 -15
  423. package/dist/verify/artifacts.d.ts +1 -1
  424. package/dist/verify/artifacts.js +3 -3
  425. package/dist/verify/costs.d.ts +2 -2
  426. package/dist/verify/costs.js +2 -2
  427. package/dist/verify/liveness.d.ts +1 -1
  428. package/dist/verify/liveness.js +4 -1
  429. package/dist/verify/rerun.d.ts +1 -1
  430. package/dist/verify/rerun.js +1 -1
  431. package/dist/verify/shared-world.js +2 -2
  432. package/dist/verify/verify.js +6 -6
  433. package/docs/README.md +1 -1
  434. package/docs/architecture/actor-contract.md +60 -22
  435. package/docs/architecture/desktop-sessions.md +9 -9
  436. package/docs/architecture/external-public-shared-world.md +5 -0
  437. package/docs/architecture/observer.md +4 -4
  438. package/docs/architecture/restricted-codex-analysis.md +40 -4
  439. package/docs/architecture/state-driven-executor.md +4 -5
  440. package/docs/architecture/task-protocol-support.md +3 -5
  441. package/docs/architecture/{terminal-product-lane.md → terminal-product-route.md} +24 -16
  442. package/docs/contracts/run-bundle.md +10 -8
  443. package/docs/contracts/schemas.md +109 -53
  444. package/docs/contracts/study-analysis.md +24 -0
  445. package/docs/decisions/0004-participant-desktops-stay-in-the-route.md +2 -1
  446. package/docs/product/automatic-analysis.md +1 -0
  447. package/docs/ramp/README.md +2 -1
  448. package/package.json +4 -4
  449. package/skills/humanish/SKILL.md +3 -2
  450. package/skills/humanish/agents/openai.yaml +1 -1
  451. package/dist/deprecated.d.ts +0 -90
  452. package/dist/deprecated.js +0 -106
  453. package/dist/lab/bag-overrides.d.ts +0 -18
  454. package/dist/lab/bag-overrides.js +0 -121
  455. package/dist/routes/computer-use/legacy-lane-spec.d.ts +0 -62
  456. package/dist/routes/computer-use/legacy-lane-spec.js +0 -39
  457. /package/dist/routes/computer-use/{fanout-lanes.d.ts → fanout-records.d.ts} +0 -0
  458. /package/dist/routes/computer-use/{run-lanes.d.ts → live-phase.d.ts} +0 -0
  459. /package/dist/routes/shared-world/{seat-records.d.ts → bundle-records.d.ts} +0 -0
@@ -7,6 +7,7 @@ import { parse } from "yaml";
7
7
  import { parseLabConfig } from "./config.js";
8
8
  import {} from "./types.js";
9
9
  import { assertPreparedSelectedOutputDirectory, assertSafeOutputPathSegment, prepareSelectedOutputDirectory, readContainedRegularFile, } from "../run/contained-output.js";
10
+ import { isNodeError } from "../run/type-guards.js";
10
11
  const LAB_LIST_SCHEMA = "humanish.lab-list.v1";
11
12
  const LAB_INSPECT_SCHEMA = "humanish.lab-inspect.v1";
12
13
  const committedLabsDir = path.join("humanish", "labs");
@@ -417,6 +418,3 @@ function relativeToCwd(cwd, filePath) {
417
418
  const relative = path.relative(cwd, filePath);
418
419
  return relative && !relative.startsWith("..") ? relative : filePath;
419
420
  }
420
- function isNodeError(error) {
421
- return error instanceof Error && "code" in error;
422
- }
@@ -1,21 +1,48 @@
1
1
  import { type LocalRuntimeStatus } from "../substrates/local/runtime.js";
2
2
  import type { LabConfig } from "./types.js";
3
- import type { LabRoute } from "./plan.js";
3
+ import type { PlanResult } from "./plan-types.js";
4
4
  import type { DetectedLocalAgent } from "../actors/local-agent/cli.js";
5
+ import type { ReasoningEffort } from "../actors/reasoning-effort.js";
5
6
  import type { DoctorResult } from "../cli/doctor.js";
7
+ import { type CodexInstallation } from "../actors/codex/qualified-versions.js";
8
+ import type { RefusedCodexExecutable } from "../actors/codex/restricted-executable.js";
6
9
  type Check = DoctorResult["checks"][number];
7
- /** Read-only Codex account readiness. An unadmitted CLI also reports the release it found. */
10
+ /**
11
+ * Read-only Codex account readiness. An unadmitted CLI also reports the release it found, and an
12
+ * unavailable one the file humanish found and turned down.
13
+ */
8
14
  type CodexReadiness = {
9
15
  ready: boolean;
10
16
  errorCode: string | null;
11
17
  detectedCliVersion?: string;
18
+ refusedExecutable?: RefusedCodexExecutable;
19
+ /** Where the `codex` on `PATH` was installed, for the command that replaces it. */
20
+ installation?: CodexInstallation;
21
+ /** How the release's app-server schema differs from the fields humanish reads. */
22
+ protocolIncompatibilities?: readonly string[];
23
+ /** Schema values beyond the baseline, recorded by a launch that passed. */
24
+ protocolAdditions?: readonly string[];
12
25
  };
26
+ /** What doctor's hosted Codex participant check found: a readiness result and what it admitted. */
27
+ type HostedCodexReadiness = CodexReadiness & {
28
+ cliVersion?: string;
29
+ resolvedModel?: string;
30
+ authentication?: "chatgpt-account" | "api-key";
31
+ };
32
+ /**
33
+ * The hosted participant check: the operator-auth launch to an ephemeral thread, with no turn,
34
+ * using the lab's declared model and reasoning effort as a run would.
35
+ */
36
+ declare function hostedCodexReadiness(env: NodeJS.ProcessEnv, actor: {
37
+ model?: string;
38
+ reasoningEffort?: ReasoningEffort;
39
+ }): Promise<HostedCodexReadiness>;
13
40
  /** Shared read-only Codex account check for local participants in doctor and the TUI. */
14
41
  export declare function localCodexParticipantCheck(args: {
15
42
  env: NodeJS.ProcessEnv;
16
43
  readiness?: (env: NodeJS.ProcessEnv) => Promise<CodexReadiness>;
17
44
  }): Promise<Check>;
18
- interface LabSetupCheckArgs {
45
+ export interface LabSetupCheckArgs {
19
46
  cwd: string;
20
47
  lab: string;
21
48
  env: NodeJS.ProcessEnv;
@@ -24,16 +51,24 @@ interface LabSetupCheckArgs {
24
51
  /** Internal read-only qualification seam; never a participant/model request. */
25
52
  localRuntimeReadiness?: () => Promise<LocalRuntimeStatus>;
26
53
  codexAnalysisReadiness?: (env: NodeJS.ProcessEnv) => Promise<CodexReadiness>;
54
+ /** The hosted Codex participant's operator handshake; tests replace it. */
55
+ codexParticipantReadiness?: typeof hostedCodexReadiness;
27
56
  }
28
- /** Setup checks only: no model turn, browser or desktop creation. CLI startup may use the network. */
57
+ /**
58
+ * Setup checks only: no model turn, browser or desktop creation. CLI startup may use the network.
59
+ * `keys` are the keys a live run requires. `reads` are the provider keys the lab is known to read,
60
+ * required or not; it is undefined when that is unknown: the lab does not plan, or a declared
61
+ * scorer's host code may read any key.
62
+ */
29
63
  export declare function labSetupChecks(args: LabSetupCheckArgs): Promise<{
30
64
  desktop: boolean;
31
65
  keys: string[];
66
+ reads?: ReadonlySet<string>;
32
67
  checks: Check[];
33
68
  }>;
34
- /** Required participant provider keys, shared by doctor and the TUI. Optional analysis is separate. */
35
- export declare function labKeyRequirements(config: LabConfig, route: LabRoute, dryRun: boolean, keyPresent: (name: string) => boolean): {
36
- desktop: boolean;
37
- keys: string[];
38
- };
69
+ /**
70
+ * The lab planned with no run options, so its own scenario mode decides dry or live. Doctor and the
71
+ * TUI read a live run's keys and subject env from this plan's requirements.
72
+ */
73
+ export declare function planCliRun(config: LabConfig, cwd: string): Promise<PlanResult>;
39
74
  export {};
@@ -1,25 +1,107 @@
1
+ import path from "node:path";
1
2
  import { isLocalBrowserLab } from "../substrates/local/runtime-config.js";
2
3
  import { localRuntimeStatus } from "../substrates/local/runtime.js";
4
+ import { keyNamesOf, requiredKeys, requiredSubjectEnv } from "./requirements.js";
5
+ import { protocolAdditionsWarning, protocolIncompatibilityMessage, } from "../actors/codex/protocol-compat.js";
3
6
  import { automaticAnalysisBudget } from "../analysis/automatic-config.js";
4
7
  import { externalCatchHealthy } from "../comms/sandbox-catch.js";
5
8
  import { receivingRequiredKey } from "../comms/setup.js";
6
- import { codexVersionRecovery } from "../actors/codex/qualified-versions.js";
7
- /** The account check doctor and the TUI share; the version probe runs only for an unadmitted CLI. */
9
+ import { classifyCodexInstallation, codexInstallAdvice, codexVersionRecovery, } from "../actors/codex/qualified-versions.js";
10
+ /** A ready row's message, with the schema values the launch recorded. */
11
+ function readyMessage(message, readiness) {
12
+ const additions = protocolAdditionsWarning(readiness.cliVersion, readiness.protocolAdditions);
13
+ return additions === undefined ? message : `${message} ${additions}`;
14
+ }
15
+ /** npm's global prefix (`npm prefix -g`), or undefined when npm does not answer. */
16
+ async function npmGlobalPrefix(env) {
17
+ const { execFile } = await import("node:child_process");
18
+ return new Promise((resolve) => {
19
+ execFile("npm", ["prefix", "-g"], { env, timeout: 5000 }, (error, stdout) => {
20
+ const prefix = String(stdout).trim();
21
+ resolve(error === null && path.isAbsolute(prefix) ? prefix : undefined);
22
+ });
23
+ });
24
+ }
25
+ /** Where the `codex` on `PATH` was installed; npm's prefix is read only when not project-local. */
26
+ async function codexInstallation(env) {
27
+ const { foundCodexExecutable } = await import("../actors/codex/restricted-executable.js");
28
+ const found = await foundCodexExecutable(env).catch(() => undefined);
29
+ if (found === undefined)
30
+ return undefined;
31
+ const local = classifyCodexInstallation(found, undefined);
32
+ return local.kind === "project"
33
+ ? local
34
+ : classifyCodexInstallation(found, await npmGlobalPrefix(env));
35
+ }
36
+ /** The account check doctor and the TUI share: the isolated launch, with recovery details. */
8
37
  async function codexAccountReadiness(env) {
9
- const readiness = await (await import("../analysis/restricted-codex.js")).checkRestrictedCodexAnalysisReadiness({ timeoutMs: 5000 }, { env });
10
- if (readiness.errorCode !== "codex_unsupported_version")
11
- return readiness;
38
+ const checked = await (await import("../analysis/restricted-codex.js")).checkRestrictedCodexAnalysisReadiness({ timeoutMs: 5000 }, { env });
39
+ return withCodexRecoveryDetails(env, checked);
40
+ }
41
+ /**
42
+ * The hosted participant check: the operator-auth launch to an ephemeral thread, with no turn,
43
+ * using the lab's declared model and reasoning effort as a run would.
44
+ */
45
+ async function hostedCodexReadiness(env, actor) {
46
+ const { checkRestrictedCodexParticipantReadiness } = await import("../actors/codex/restricted-participant.js");
47
+ const checked = await checkRestrictedCodexParticipantReadiness({
48
+ session: { env },
49
+ ...(actor.model === undefined ? {} : { model: actor.model }),
50
+ ...(actor.reasoningEffort === undefined ? {} : { reasoningEffort: actor.reasoningEffort }),
51
+ });
52
+ return checked.ready
53
+ ? checked
54
+ : { ...checked, ...(await withCodexRecoveryDetails(env, checked)) };
55
+ }
56
+ /**
57
+ * Adds what the recovery line names to a failed check. An unadmitted CLI gets its release, an
58
+ * unavailable one the file humanish turned down, and both where the binary came from.
59
+ */
60
+ async function withCodexRecoveryDetails(env, checked) {
61
+ if (checked.errorCode !== "codex_unavailable" &&
62
+ checked.errorCode !== "codex_unsupported_version" &&
63
+ checked.errorCode !== "codex_incompatible_release")
64
+ return checked;
65
+ const installation = await codexInstallation(env);
66
+ const readiness = installation === undefined ? checked : { ...checked, installation };
67
+ if (readiness.errorCode === "codex_unavailable") {
68
+ const { refusedCodexExecutable } = await import("../actors/codex/restricted-executable.js");
69
+ const refused = await refusedCodexExecutable(env).catch(() => undefined);
70
+ return refused === undefined ? readiness : { ...readiness, refusedExecutable: refused };
71
+ }
12
72
  const { detectRestrictedCodexCliVersion } = await import("../actors/codex/restricted-session.js");
13
73
  const detected = await detectRestrictedCodexCliVersion({ timeoutMs: 5000 }, { env }).catch(() => undefined);
14
74
  // An admitted release here means app-server reported a different one; name what --version says.
15
75
  const version = detected?.cliVersion ?? detected?.detectedVersion;
16
76
  return version === undefined ? readiness : { ...readiness, detectedCliVersion: version };
17
77
  }
18
- /** What to do about a Codex readiness failure; an unadmitted CLI gets its exact install command. */
78
+ /**
79
+ * What to do about a Codex readiness failure, one line per code: an unadmitted CLI gets its exact
80
+ * install command, a missing login `codex login`, and an unavailable CLI the file humanish turned
81
+ * down and why. Other codes get `fallback`.
82
+ */
19
83
  function codexRecovery(readiness, fallback) {
20
- return readiness.errorCode === "codex_unsupported_version"
21
- ? codexVersionRecovery(readiness.detectedCliVersion)
22
- : fallback;
84
+ switch (readiness.errorCode) {
85
+ case "codex_unsupported_version":
86
+ return codexVersionRecovery(readiness.detectedCliVersion, readiness.installation);
87
+ case "codex_incompatible_release":
88
+ return `${protocolIncompatibilityMessage(readiness.detectedCliVersion, readiness.protocolIncompatibilities ?? [])} ${codexInstallAdvice(readiness.installation)} Then sign in with a ChatGPT account (\`codex login\`).`;
89
+ case "codex_login_required":
90
+ return "Codex is installed but not signed in. Run `codex login` and sign in with a ChatGPT account.";
91
+ case "codex_unsupported_auth":
92
+ return "Codex is signed in without a ChatGPT account, for example with an API key. Run `codex logout`, then `codex login` with a ChatGPT account.";
93
+ case "codex_unavailable": {
94
+ const refused = readiness.refusedExecutable;
95
+ const found = refused === undefined
96
+ ? "humanish could not start the Codex CLI."
97
+ : refused.path === undefined
98
+ ? `Codex is unavailable: ${refused.reason}.`
99
+ : `humanish found \`${refused.path}\` and cannot run it: ${refused.reason}.`;
100
+ return `${found} ${codexInstallAdvice(readiness.installation)} Then sign in with a ChatGPT account (\`codex login\`).`;
101
+ }
102
+ default:
103
+ return fallback;
104
+ }
23
105
  }
24
106
  /** Shared read-only Codex account check for local participants in doctor and the TUI. */
25
107
  export async function localCodexParticipantCheck(args) {
@@ -28,11 +110,16 @@ export async function localCodexParticipantCheck(args) {
28
110
  name: "local participant authentication",
29
111
  ok: readiness.ready,
30
112
  message: readiness.ready
31
- ? "Qualified Codex CLI and ChatGPT login are ready for restricted local browser participants. No E2B or model API key is required; inference is remote, and model access and account quota remain untested."
113
+ ? readyMessage("Qualified Codex CLI and ChatGPT login are ready for restricted local browser participants. No E2B or model API key is required; inference is remote, and model access and account quota remain untested.", readiness)
32
114
  : `Local Codex participant setup is unavailable (${readiness.errorCode}). ${codexRecovery(readiness, "Install the supported Codex CLI version and sign in with a ChatGPT account.")} No API fallback is used.`,
33
115
  };
34
116
  }
35
- /** Setup checks only: no model turn, browser or desktop creation. CLI startup may use the network. */
117
+ /**
118
+ * Setup checks only: no model turn, browser or desktop creation. CLI startup may use the network.
119
+ * `keys` are the keys a live run requires. `reads` are the provider keys the lab is known to read,
120
+ * required or not; it is undefined when that is unknown: the lab does not plan, or a declared
121
+ * scorer's host code may read any key.
122
+ */
36
123
  export async function labSetupChecks(args) {
37
124
  const { resolveLabManifest } = await import("./discover.js");
38
125
  const { resolveLabDryRun, routeOf } = await import("./plan.js");
@@ -53,7 +140,7 @@ export async function labSetupChecks(args) {
53
140
  },
54
141
  ];
55
142
  if (dryRun)
56
- return { desktop: false, keys: [], checks };
143
+ return { desktop: false, keys: [], reads: new Set(), checks };
57
144
  const unsupported = unsupportedCliRoute(config, route);
58
145
  if (unsupported)
59
146
  return {
@@ -61,7 +148,17 @@ export async function labSetupChecks(args) {
61
148
  keys: [],
62
149
  checks: [...checks, { name: "live route", ok: false, message: unsupported }],
63
150
  };
64
- const { desktop, keys } = labKeyRequirements(config, route, false, args.keyPresent);
151
+ const planned = await planCliRun(config, args.cwd);
152
+ if (!planned.ok)
153
+ return {
154
+ desktop: false,
155
+ keys: [],
156
+ checks: [...checks, { name: "live route", ok: false, message: planned.refusal.message }],
157
+ };
158
+ const requirements = planned.planned.plan.requirements;
159
+ const keys = requiredKeys(requirements, args.keyPresent);
160
+ // This flag controls the hosted desktop SDK check as well as its API key.
161
+ const desktop = keys.includes("E2B_API_KEY");
65
162
  const local = isLocalBrowserLab(config);
66
163
  // One account check, shared by the local participant row and the Codex analysis row.
67
164
  let accountReadiness;
@@ -73,12 +170,15 @@ export async function labSetupChecks(args) {
73
170
  checks.push(...(await participantChecks(config, route, keys, local, args, checkAccount)));
74
171
  if (route === "scripted")
75
172
  checks.push(await scriptedBrowserCheck());
76
- checks.push(...subjectEnvChecks(config, args));
173
+ checks.push(...subjectEnvChecks(requiredSubjectEnv(requirements), args));
77
174
  const analysis = automaticAnalysisBudget(config.review?.analysis, route);
78
175
  if (analysis)
79
176
  checks.push(await analysisCheck(analysis, args, checkAccount));
80
177
  checks.push(checkScope(analysis));
81
- return { desktop, keys, checks };
178
+ const reads = config.review?.scorer === undefined
179
+ ? new Set([...keyNamesOf(planned.planned.plan), ...keys])
180
+ : undefined;
181
+ return { desktop, keys, ...(reads === undefined ? {} : { reads }), checks };
82
182
  }
83
183
  /** A local browser study's runtime and, with an external catch, its captured inbox. */
84
184
  async function localBrowserChecks(config, args) {
@@ -137,6 +237,10 @@ async function participantChecks(config, route, keys, local, args, checkAccount)
137
237
  const agent = args.agents.find((entry) => entry.id === choice);
138
238
  if (local)
139
239
  return [await localCodexParticipantCheck({ env: args.env, readiness: checkAccount })];
240
+ // A signed-in hosted Codex gets the operator handshake; its sign-in status alone does not show
241
+ // that the operator's config, release and model admit a launch.
242
+ if (choice === "codex" && agent?.authStatus === "authenticated")
243
+ return [await hostedCodexParticipantCheck(args, config.actors[0])];
140
244
  return [
141
245
  {
142
246
  name: "local participant authentication",
@@ -149,6 +253,24 @@ async function participantChecks(config, route, keys, local, args, checkAccount)
149
253
  },
150
254
  ];
151
255
  }
256
+ /** The hosted Codex participant row: what the operator handshake admitted, or how to fix it. */
257
+ async function hostedCodexParticipantCheck(args, actor) {
258
+ const declared = {
259
+ ...(actor?.model === undefined ? {} : { model: actor.model }),
260
+ ...(actor?.reasoningEffort === undefined ? {} : { reasoningEffort: actor.reasoningEffort }),
261
+ };
262
+ const readiness = await (args.codexParticipantReadiness ?? hostedCodexReadiness)(args.env, declared).catch(() => ({ ready: false, errorCode: "codex_unavailable" }));
263
+ const account = readiness.authentication === "api-key"
264
+ ? "an API key, which bills its usage to that key"
265
+ : "a ChatGPT account";
266
+ return {
267
+ name: "local participant authentication",
268
+ ok: readiness.ready,
269
+ message: readiness.ready
270
+ ? readyMessage(`Codex CLI ${readiness.cliVersion ?? "(unknown release)"} passed the operator handshake without a turn (initialize, config/read, account/read and an ephemeral thread/start): model ${readiness.resolvedModel ?? "(not reported)"} on ${account}. E2B supplies the desktop; no OpenAI API key is required for this participant.`, readiness)
271
+ : `Hosted Codex participant setup is unavailable (${readiness.errorCode}). ${codexRecovery(readiness, "Check `codex login status` and the operator's Codex configuration, then rerun doctor.")} No API fallback is used.`,
272
+ };
273
+ }
152
274
  /** The local Chrome/Chromium the scripted-browser route drives. */
153
275
  async function scriptedBrowserCheck() {
154
276
  const { resolveBrowserCommand } = await import("../actors/scripted-browser/browser-command.js");
@@ -158,9 +280,9 @@ async function scriptedBrowserCheck() {
158
280
  message: "Scripted-browser uses local Chrome/Chromium and no participant model. Install Chrome/Chromium or set HUMANISH_BROWSER_COMMAND; a clone subject additionally needs E2B.",
159
281
  };
160
282
  }
161
- /** One row per declared subject env name: present or missing, never the value. */
162
- function subjectEnvChecks(config, args) {
163
- return (config.subject.env ?? []).map((name) => ({
283
+ /** One row per subject env name the plan requires: present or missing, never the value. */
284
+ function subjectEnvChecks(names, args) {
285
+ return names.map((name) => ({
164
286
  name: `subject env ${name}`,
165
287
  ok: !!args.env[name]?.trim() || args.keyPresent(name),
166
288
  message: args.env[name]?.trim() || args.keyPresent(name)
@@ -168,7 +290,7 @@ function subjectEnvChecks(config, args) {
168
290
  : "missing declared subject environment variable; provide it with --env-file",
169
291
  }));
170
292
  }
171
- /** The post-run analysis: the Codex account, or the OpenAI key and the admission estimate limit. */
293
+ /** The post-run analysis: the Codex account, or the OpenAI key and the analysis cost limit. */
172
294
  async function analysisCheck(analysis, args, checkAccount) {
173
295
  if (analysis.provider === "codex") {
174
296
  const readiness = await checkAccount();
@@ -181,7 +303,7 @@ async function analysisCheck(analysis, args, checkAccount) {
181
303
  name: "post-run analysis",
182
304
  ok: readiness.ready,
183
305
  message: readiness.ready
184
- ? "Qualified Codex CLI and ChatGPT account login are ready for a separate restricted analyst. Analysis sends selected evidence to remote inference; model access and account allowance remain untested. Dollar cost and output-token ceilings are unavailable."
306
+ ? readyMessage("Qualified Codex CLI and ChatGPT account login are ready for a separate restricted analyst. Analysis sends selected evidence to remote inference; model access and account allowance remain untested. Dollar cost and output-token ceilings are unavailable.", readiness)
185
307
  : `Codex account analysis is unavailable (${readiness.errorCode ?? "codex_unavailable"}). ${recovery} No API fallback is used; participant readiness is independent.`,
186
308
  };
187
309
  }
@@ -189,7 +311,7 @@ async function analysisCheck(analysis, args, checkAccount) {
189
311
  name: "post-run analysis",
190
312
  ok: true,
191
313
  message: args.keyPresent("OPENAI_API_KEY")
192
- ? `OPENAI_API_KEY is present for the separate automatic analysis request; model access and quota are not tested. Its $${analysis.maxCostUsd} admission estimate limit may decline larger studies before dispatch; it is not a provider billing cap. Participant readiness is independent.`
314
+ ? `OPENAI_API_KEY is present for the separate automatic analysis request; model access and quota are not tested. The analysis is refused before it starts if its estimate is over $${analysis.maxCostUsd}; this is not a billing cap. Participant readiness is independent.`
193
315
  : "Will be skipped: OPENAI_API_KEY is missing. The participant may run, but there will be no automatic findings report. Add an OpenAI API key or set review.analysis: false deliberately.",
194
316
  };
195
317
  }
@@ -203,26 +325,12 @@ function checkScope(analysis) {
203
325
  : "Local setup only. Provider credentials are not validated, model access/quota and target reachability are untested, and no paid resources were created.",
204
326
  };
205
327
  }
206
- /** Required participant provider keys, shared by doctor and the TUI. Optional analysis is separate. */
207
- export function labKeyRequirements(config, route, dryRun, keyPresent) {
208
- if (dryRun || unsupportedCliRoute(config, route))
209
- return { desktop: false, keys: [] };
210
- // This flag controls the hosted desktop SDK check as well as its API key.
211
- const desktop = !isLocalBrowserLab(config) &&
212
- (route === "computer-use" ||
213
- route === "terminal" ||
214
- route === "shared-world" ||
215
- (route === "scripted" && config.subject.source === "clone"));
216
- const keys = desktop ? ["E2B_API_KEY"] : [];
217
- const openaiBrain = config.actors[0]?.type !== "local-agent";
218
- if (route === "terminal")
219
- keys.push(keyPresent("CODEX_API_KEY") ? "CODEX_API_KEY" : "OPENAI_API_KEY");
220
- // Shared world's external-public plane reads the host's lobby code with the OpenAI API,
221
- // whatever brain drives the participants (routes/shared-world/plan.ts).
222
- else if ((route === "computer-use" && openaiBrain) ||
223
- (route === "shared-world" && (openaiBrain || config.subject.source === "app-url")))
224
- keys.push("OPENAI_API_KEY");
225
- return { desktop, keys };
328
+ /**
329
+ * The lab planned with no run options, so its own scenario mode decides dry or live. Doctor and the
330
+ * TUI read a live run's keys and subject env from this plan's requirements.
331
+ */
332
+ export async function planCliRun(config, cwd) {
333
+ return (await import("./plan.js")).planLab(config, { cwd });
226
334
  }
227
335
  function unsupportedCliRoute(config, route) {
228
336
  if (config.subject.source === "local-app")
@@ -3,6 +3,7 @@
3
3
  // here; runInit applies the plan.
4
4
  import path from "node:path";
5
5
  import { humanishScripts } from "./init-templates.js";
6
+ import { isRecord } from "../run/type-guards.js";
6
7
  import { readTextIfExists } from "./init-paths.js";
7
8
  export async function planGitignore(projectRoot, cwd) {
8
9
  const relativePath = ".gitignore";
@@ -158,6 +159,3 @@ export async function planPackageJson(projectRoot, cwd) {
158
159
  function trimTrailingNewlines(text) {
159
160
  return text.replace(/\n+$/, "");
160
161
  }
161
- function isRecord(value) {
162
- return typeof value === "object" && value !== null && !Array.isArray(value);
163
- }
package/dist/lab/keys.js CHANGED
@@ -92,7 +92,7 @@ const ACTOR = {
92
92
  type: true,
93
93
  count: true,
94
94
  lanes: PARTICIPANT_ENTRY,
95
- // Roster groups are lanes with a count; the parser expands them into lanes.
95
+ // Roster groups are participants with a count; the parser expands them into `lanes[]`.
96
96
  roster: { ...PARTICIPANT_ENTRY, count: true },
97
97
  persona: true,
98
98
  mission: true,
@@ -0,0 +1,85 @@
1
+ import type { ScriptedBrowserSessionOptions, ScriptedBrowserSessionResult } from "../actors/scripted-browser/actor.js";
2
+ import type { ScriptedBrowserLaunchArgs, ScriptedBrowserLike } from "../actors/scripted-browser/types.js";
3
+ import type { CuaActorSessionOptions } from "../actors/computer-use/actor.js";
4
+ import type { AutomaticAnalysisDeps, runAutomaticAnalysis } from "../analysis/automatic.js";
5
+ import type { CuaLoopResult } from "../actors/computer-use/loop.js";
6
+ import type { renderObserver } from "../observer/render.js";
7
+ import type { TerminalCostProbe } from "../routes/terminal/types.js";
8
+ import type { DetachedTimers } from "../substrates/detached.js";
9
+ import type { E2BDesktopModule } from "../substrates/e2b/sdk.js";
10
+ import type { LocalTreeArchive } from "../subject/local-tree-archive.js";
11
+ import type { SubjectPhaseEvent } from "../subject/steps.js";
12
+ /** The participant a computer-use subject phase belongs to, with the route's field names. */
13
+ export interface PhaseParticipant {
14
+ readonly laneId: string;
15
+ readonly laneIndex: number;
16
+ readonly laneCount: number;
17
+ }
18
+ export interface LabDeps {
19
+ /** Loads the E2B SDK. Defaults to loadE2BDesktopModule. */
20
+ readonly desktopModule?: () => Promise<E2BDesktopModule>;
21
+ /** Renders the Observer. Defaults to renderObserver. */
22
+ readonly renderObserver?: typeof renderObserver;
23
+ /** The clock, in epoch milliseconds. Defaults to Date.now. */
24
+ readonly now?: () => number;
25
+ /** Clock and sleep for detached provisioning steps. */
26
+ readonly detachedTimers?: DetachedTimers;
27
+ /**
28
+ * Computer use and shared world: where subject-provisioning phases go in place of stderr. A
29
+ * computer-use phase names its participant; a shared-world plane's names none. onEvent still
30
+ * receives every phase.
31
+ */
32
+ readonly subjectPhaseSink?: (event: SubjectPhaseEvent, participant?: PhaseParticipant) => void;
33
+ /**
34
+ * Shared world: runs each seat's computer-use session in place of the actor's own. The planner
35
+ * reads whether it is set: a custom runner cannot enforce actors[0].maxOutputTokens.
36
+ */
37
+ readonly runSession?: (options: CuaActorSessionOptions) => Promise<CuaLoopResult>;
38
+ /**
39
+ * Shared world: packs a local-tree subject on the host. Defaults to createLocalTreeArchive and a
40
+ * read of the archive it writes. Called once per run, before the subject sandbox exists.
41
+ */
42
+ readonly packLocalTree?: (args: {
43
+ root: string;
44
+ extraExclude?: string[];
45
+ maxArchiveBytes?: number;
46
+ }) => Promise<{
47
+ archive: LocalTreeArchive;
48
+ buffer: ArrayBuffer;
49
+ }>;
50
+ /** Shared world: the background stateSeries prober's cadence in milliseconds. Defaults to 1000. */
51
+ readonly proberCadenceMs?: number;
52
+ /**
53
+ * Shared world, external-public plane: how long the host seat has to surface a /lobby/CODE URL
54
+ * before the run fails closed. Defaults to 120000, capped by execution.timeoutMs.
55
+ */
56
+ readonly handoffDeadlineMs?: number;
57
+ /**
58
+ * Shared world, external-public plane: reads a /lobby/CODE off a seat's screenshot. Defaults to
59
+ * the single-frame OpenAI read.
60
+ */
61
+ readonly readLobbyCodeFromFrame?: (frame: Buffer, apiKey: string) => Promise<string | undefined>;
62
+ /**
63
+ * Every route: post-run analysis in place of the real one. `run` replaces
64
+ * the real analysis runner, and `deps` (provider fetch, keys, clock) reach the analysis only,
65
+ * never a participant.
66
+ */
67
+ readonly analysis?: {
68
+ readonly run?: typeof runAutomaticAnalysis;
69
+ readonly deps?: AutomaticAnalysisDeps;
70
+ };
71
+ /** Scripted: runs one surface's session in place of the scripted-browser actor. */
72
+ readonly runScriptedSession?: (options: ScriptedBrowserSessionOptions) => Promise<ScriptedBrowserSessionResult>;
73
+ /**
74
+ * Scripted: launches the browser each session drives, so no browser binary is resolved. The
75
+ * planner reads whether it, or browserCommand, is set: a live run then needs no host browser.
76
+ */
77
+ readonly launchBrowser?: (args: ScriptedBrowserLaunchArgs) => Promise<ScriptedBrowserLike>;
78
+ /** Scripted: the browser binary to launch; operators use HUMANISH_BROWSER_COMMAND. */
79
+ readonly browserCommand?: string;
80
+ /**
81
+ * Terminal: known spend lines for the cost ledger. The planner reads whether it is set, since
82
+ * only a measured line can trip a positive maxUsd.
83
+ */
84
+ readonly costProbe?: TerminalCostProbe;
85
+ }
@@ -0,0 +1,4 @@
1
+ // Test seams: the implementations a test puts in place of the real ones. The internal runLab takes
2
+ // them as its third argument and each route runner as `deps`; the package's runLab takes none.
3
+ // Each field defaults to the real implementation.
4
+ export {};
@@ -3,8 +3,9 @@ import { isReasoningEffort, reasoningEffortNames } from "../../actors/reasoning-
3
3
  import { isMaxOutputTokens } from "../../actors/output-token-limit.js";
4
4
  import { isHttpUrl } from "./subject.js";
5
5
  import { registeredComputerUseActors, registeredScriptedBrowserActors, registeredTerminalActors, } from "../routing.js";
6
- import { invalid, isRecord, posInt, str } from "./values.js";
7
- // A lane id interpolates into per-lane evidence paths (screenshots/<id>/, actors/<id>.json), so
6
+ import { invalid, posInt, str } from "./values.js";
7
+ import { isRecord } from "../../run/type-guards.js";
8
+ // A participant id interpolates into its evidence paths (screenshots/<id>/, actors/<id>.json), so
8
9
  // it must be a public-safe path token, same shape as a lab id.
9
10
  export const PARTICIPANT_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_.-]*$/;
10
11
  export const PARTICIPANT_ID_MAX_CHARS = 40;
@@ -138,7 +139,7 @@ function parseFocus(raw) {
138
139
  return Object.keys(focus).length > 0 ? focus : undefined;
139
140
  }
140
141
  /**
141
- * Parse `actors[index].roster` compact groups into concrete lanes. This is authoring sugar for
142
+ * Parse `actors[index].roster` compact groups into concrete `lanes[]` entries. This is authoring sugar for
142
143
  * "N users of M adapter-owned types across S surfaces"; the runtime receives only `lanes[]`.
143
144
  */
144
145
  function parseRosterGroups(raw, actorIndex) {
@@ -183,11 +184,11 @@ function parseRosterGroups(raw, actorIndex) {
183
184
  }
184
185
  /**
185
186
  * Parse `actors[index].lanes` into a fan-out roster (computer-use E2B route). Structural only:
186
- * each lane is `{ id?, actorType?, surface?, caseGroup?, persona?, device?, instruction?, target?,
187
+ * each entry is `{ id?, actorType?, surface?, caseGroup?, persona?, device?, instruction?, target?,
187
188
  * entry?, host?, reasoningEffort?, stopWhen?, dwell? }`.
188
- * Lane ids (when declared) must be public-safe path tokens and unique; lane grouping metadata
189
- * must be public-safe tokens; a lane device must be a known preset name. The
190
- * route-scoped cross-validation (lanes XOR count/laneFocus, device XOR raw resolution, cap 16)
189
+ * Participant ids (when declared) must be public-safe path tokens and unique; grouping metadata
190
+ * must be public-safe tokens; a device must be a known preset name. The
191
+ * route-scoped cross-validation (`lanes` XOR `count`/`laneFocus`, device XOR raw resolution, cap 16)
191
192
  * runs in parseLabConfig where the route is known.
192
193
  */
193
194
  function parseParticipantEntries(raw, actorIndex) {
@@ -1,4 +1,5 @@
1
- import { invalid, isRecord, posInt, str } from "./values.js";
1
+ import { invalid, posInt, str } from "./values.js";
2
+ import { isRecord } from "../../run/type-guards.js";
2
3
  // Fail-loud (never silently swallow a comms setting): a malformed `comms` block returns a parse
3
4
  // failure rather than being dropped.
4
5
  export function parseComms(raw) {
@@ -226,7 +227,7 @@ export function addressedRecipients(email) {
226
227
  ? []
227
228
  : [{ participantId: recipient.lane, address: recipient.address }]);
228
229
  }
229
- /** `comms.email.recipients`: which lane each captured address belongs to. */
230
+ /** `comms.email.recipients`: which participant each captured address belongs to. */
230
231
  function parseRecipients(raw) {
231
232
  if (raw === undefined)
232
233
  return { ok: true, value: undefined };
@@ -1,7 +1,8 @@
1
1
  import { resolveAutomaticAnalysis } from "../../analysis/automatic-config.js";
2
2
  import { DEVICE_PRESET_NAMES, isDevicePresetName } from "../device-presets.js";
3
3
  import { isExactRuntimeVersion } from "../../routes/terminal/runtime.js";
4
- import { invalid, isRecord, nonNegNumber, posInt, str } from "./values.js";
4
+ import { invalid, nonNegNumber, posInt, str } from "./values.js";
5
+ import { isRecord } from "../../run/type-guards.js";
5
6
  export function parseExecution(raw) {
6
7
  if (raw === undefined) {
7
8
  return { ok: true, value: undefined };
@@ -32,7 +33,7 @@ export function parseExecution(raw) {
32
33
  }
33
34
  if (desktopResult.value)
34
35
  execution.desktop = desktopResult.value;
35
- // Reuse the terminal lane's caps parser (same shape, not a fork) — a malformed budget is a hard
36
+ // Reuse the terminal route's caps parser (same shape, not a fork) — a malformed budget is a hard
36
37
  // error, never silently dropped (a cap that silently does nothing would be a safety lie).
37
38
  const capsResult = parseCaps(raw.caps);
38
39
  if (!capsResult.ok) {
@@ -2,7 +2,8 @@
2
2
  // the structural parse of `subject.state`, and subjectStateInvalidReason, the semantic check
3
3
  // parseLabConfig and the computer-use route share.
4
4
  import { containsSensitive } from "../../evidence/redaction.js";
5
- import { ENV_NAME_PATTERN, invalid, isRecord, posInt, str, strList } from "./values.js";
5
+ import { ENV_NAME_PATTERN, invalid, posInt, str, strList } from "./values.js";
6
+ import { isRecord } from "../../run/type-guards.js";
6
7
  /**
7
8
  * LITERAL non-secret subject env. Real apps need configuration before they will boot — a public base
8
9
  * URL, a transport selector, a feature flag — and none of that is secret. Routing it through
@@ -1,6 +1,7 @@
1
1
  import { normalizeExtraExcludeEntry } from "../../subject/local-tree-archive.js";
2
- import { ENV_NAME_PATTERN, invalid, isRecord, posInt, str, strList } from "./values.js";
2
+ import { ENV_NAME_PATTERN, invalid, posInt, str, strList } from "./values.js";
3
3
  import { parseEnvValues, parseState, subjectStateInvalidReason } from "./subject-state.js";
4
+ import { isRecord } from "../../run/type-guards.js";
4
5
  export function parseSubject(raw) {
5
6
  if (!isRecord(raw)) {
6
7
  return invalid("Lab `subject` is required and must be an object.");
@@ -2,7 +2,6 @@ import type { LabConfigParseFailure } from "../types.js";
2
2
  export declare const REPO_SLUG_PATTERN: RegExp;
3
3
  export declare const ENV_NAME_PATTERN: RegExp;
4
4
  export declare function invalid(message: string): LabConfigParseFailure;
5
- export declare function isRecord(value: unknown): value is Record<string, unknown>;
6
5
  export declare function str(value: unknown): string | undefined;
7
6
  export declare function optionalStr(key: string, value: unknown): Record<string, string>;
8
7
  export declare function strList(value: unknown): string[] | undefined;
@@ -4,9 +4,6 @@ export const ENV_NAME_PATTERN = /^[A-Z][A-Z0-9_]*$/;
4
4
  export function invalid(message) {
5
5
  return { ok: false, error: { code: "HUMANISH_LAB_INVALID", message } };
6
6
  }
7
- export function isRecord(value) {
8
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
9
- }
10
7
  export function str(value) {
11
8
  return typeof value === "string" && value.trim() ? value.trim() : undefined;
12
9
  }