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
@@ -1,14 +1,51 @@
1
1
  import path from "node:path";
2
- import { isFeedbackIdempotencyKey } from "../run/feedback-shape.js";
2
+ import { isRunAdapterScore } from "../run/bundle-shape.js";
3
+ import { isRunFeedbackCandidate } from "../run/feedback-shape.js";
4
+ import { isRecord } from "../run/type-guards.js";
5
+ const OLDER_BACKEND = {
6
+ "computer-use": "cua",
7
+ "shared-world": "concurrent-shared-world",
8
+ };
9
+ const warnedFields = new Set();
10
+ function warnOlderField(name, replacement) {
11
+ if (warnedFields.has(name))
12
+ return;
13
+ warnedFields.add(name);
14
+ process.emitWarning(`BrowserLabScoringContext.${name} is deprecated and is removed in the next minor. Use ${replacement}.`, { type: "DeprecationWarning", code: "HUMANISH_SCORING_CONTEXT_FIELD_DEPRECATED" });
15
+ }
16
+ /**
17
+ * The scorer's context: the facts, plus the older `backend` and `laneCount` as getters that warn
18
+ * once when a scorer reads them. They are not enumerable, so a spread, `Object.assign`,
19
+ * `JSON.stringify` or `structuredClone` of the context skips them and warns about no field the
20
+ * code never named. `"backend" in ctx` still finds them. Core never reads them.
21
+ */
22
+ function scorerContext(facts) {
23
+ return Object.defineProperties({ ...facts }, {
24
+ backend: {
25
+ enumerable: false,
26
+ get: () => {
27
+ warnOlderField("backend", "route");
28
+ return OLDER_BACKEND[facts.route];
29
+ },
30
+ },
31
+ laneCount: {
32
+ enumerable: false,
33
+ get: () => {
34
+ warnOlderField("laneCount", "participantCount");
35
+ return facts.participantCount;
36
+ },
37
+ },
38
+ });
39
+ }
3
40
  /** A DECLARED scorer that returned a malformed value never rendered a verdict. */
4
41
  export const DECLARED_SCORER_MALFORMED = "Declared product scorer returned a malformed value instead of a verdict; a declared gate that cannot render a pass is recorded as a fail, never a silent pass.";
5
42
  /** A DECLARED scorer that threw never rendered a verdict. `detail` is already sanitized. */
6
43
  export function declaredScorerThrew(detail) {
7
44
  return `Declared product scorer threw before returning a verdict (${detail}); a crashed declared gate is recorded as a fail, never a silent pass.`;
8
45
  }
9
- export async function applyBrowserAdapterHooks(args) {
10
- const { hooks, context, bundle, sanitize, warnings, hookLabel, scorerProvenance } = args;
11
- if (!hooks?.score && !hooks?.deriveFeedback && !hooks?.deriveArtifacts)
46
+ export async function applyBrowserScorer(args) {
47
+ const { scorer, context, bundle, sanitize, warnings, scorerProvenance } = args;
48
+ if (!scorer?.score && !scorer?.deriveFeedback && !scorer?.deriveArtifacts)
12
49
  return { failures: [] };
13
50
  const declared = scorerProvenance !== undefined;
14
51
  // Record the loaded scorer's identity regardless of hook outcome (a throwing/invalid scorer was
@@ -19,78 +56,86 @@ export async function applyBrowserAdapterHooks(args) {
19
56
  // The scorer sees a READ-ONLY view of the bundle: it cannot mutate noSpend/cost/review in place to
20
57
  // launder a verdict (a tamper attempt throws in the scorer's strict-mode ESM and is caught below as
21
58
  // a hook failure). The seam still stamps the REAL bundle.
22
- const scoringContext = {
59
+ const scoringContext = scorerContext({
23
60
  ...context,
24
61
  bundle: frozenBundleView(context.bundle),
25
- };
62
+ });
26
63
  const scrubValue = (value) => {
27
64
  const encoded = JSON.stringify(value);
28
65
  return encoded === undefined ? value : JSON.parse(sanitize(encoded));
29
66
  };
30
67
  const failures = [];
31
- if (hooks.score) {
68
+ if (scorer?.score) {
32
69
  try {
33
- const score = await hooks.score(scoringContext);
70
+ const score = await scorer.score(scoringContext);
34
71
  const cleaned = scrubValue(score);
35
- if (isAdapterScoreShape(cleaned)) {
72
+ if (isRunAdapterScore(cleaned)) {
36
73
  bundle.adapterScore = cleaned;
37
74
  const message = adapterScoreFailureMessage(bundle);
38
75
  if (message !== undefined)
39
76
  failures.push(message);
40
77
  }
41
78
  else {
42
- warnings.push(`${hookLabel}.score returned a value that is not a well-formed humanish.adapter-score.v1 (non-empty namespace + status + numeric score + summary); dropped so the bundle stays verifiable.`);
79
+ warnings.push(`scorer.score returned a value that is not a well-formed humanish.adapter-score.v1 (non-empty namespace + status + numeric score + summary); dropped so the bundle stays verifiable.`);
43
80
  if (declared)
44
81
  failures.push(DECLARED_SCORER_MALFORMED);
45
82
  }
46
83
  }
47
84
  catch (error) {
48
85
  const detail = sanitize(error instanceof Error ? error.message : String(error));
49
- warnings.push(`${hookLabel}.score threw (${detail}); dropped so the bundle stays verifiable.`);
86
+ warnings.push(`scorer.score threw (${detail}); dropped so the bundle stays verifiable.`);
50
87
  if (declared)
51
88
  failures.push(declaredScorerThrew(detail));
52
89
  }
53
90
  }
54
- if (hooks.deriveFeedback) {
91
+ if (scorer?.deriveFeedback) {
55
92
  try {
56
- const candidates = await hooks.deriveFeedback(scoringContext);
93
+ const candidates = await scorer.deriveFeedback(scoringContext);
57
94
  const accepted = [];
58
95
  for (const candidate of Array.isArray(candidates) ? candidates : []) {
59
96
  const cleaned = scrubValue(candidate);
60
- if (isAdapterFeedbackCandidateShape(cleaned))
97
+ if (isAdapterFeedbackCandidate(cleaned))
61
98
  accepted.push(cleaned);
62
99
  else
63
- warnings.push(`${hookLabel}.deriveFeedback returned a candidate that is not a well-formed humanish.feedback-candidate.v1 (or its adapter block lacked a non-empty namespace + data record); dropped so the bundle stays verifiable.`);
100
+ warnings.push(`scorer.deriveFeedback returned a candidate that is not a well-formed humanish.feedback-candidate.v1 (or its adapter block lacked a non-empty namespace + data record); dropped so the bundle stays verifiable.`);
64
101
  }
65
102
  if (accepted.length > 0) {
66
103
  bundle.feedbackCandidates = [...bundle.feedbackCandidates, ...accepted];
67
104
  }
68
105
  }
69
106
  catch (error) {
70
- warnings.push(`${hookLabel}.deriveFeedback threw (${sanitize(error instanceof Error ? error.message : String(error))}); dropped so the bundle stays verifiable.`);
107
+ warnings.push(`scorer.deriveFeedback threw (${sanitize(error instanceof Error ? error.message : String(error))}); dropped so the bundle stays verifiable.`);
71
108
  }
72
109
  }
73
- if (hooks.deriveArtifacts) {
110
+ if (scorer?.deriveArtifacts) {
74
111
  try {
75
- const artifacts = await hooks.deriveArtifacts(scoringContext);
112
+ const artifacts = await scorer.deriveArtifacts(scoringContext);
76
113
  const accepted = [];
77
114
  for (const artifact of Array.isArray(artifacts) ? artifacts : []) {
78
115
  const cleaned = scrubValue(artifact);
79
116
  if (isAdapterArtifactShape(cleaned))
80
117
  accepted.push(cleaned);
81
118
  else
82
- warnings.push(`${hookLabel}.deriveArtifacts returned an artifact that is not a well-formed humanish.adapter-artifact.v1 (non-empty namespace + label + local path + supported kind); dropped so the bundle stays verifiable.`);
119
+ warnings.push(`scorer.deriveArtifacts returned an artifact that is not a well-formed humanish.adapter-artifact.v1 (non-empty namespace + label + local path + supported kind); dropped so the bundle stays verifiable.`);
83
120
  }
84
121
  if (accepted.length > 0) {
85
122
  bundle.adapterArtifacts = [...(bundle.adapterArtifacts ?? []), ...accepted];
86
123
  }
87
124
  }
88
125
  catch (error) {
89
- warnings.push(`${hookLabel}.deriveArtifacts threw (${sanitize(error instanceof Error ? error.message : String(error))}); dropped so the bundle stays verifiable.`);
126
+ warnings.push(`scorer.deriveArtifacts threw (${sanitize(error instanceof Error ? error.message : String(error))}); dropped so the bundle stays verifiable.`);
90
127
  }
91
128
  }
92
129
  return { failures };
93
130
  }
131
+ /**
132
+ * A feedback candidate a scorer returned, accepted at the seam: the bundle's candidate shape with a
133
+ * non-empty summary. The browser and terminal seams both use it, so a candidate either seam drops
134
+ * never reaches a bundle.
135
+ */
136
+ export function isAdapterFeedbackCandidate(value) {
137
+ return isRunFeedbackCandidate(value) && value.summary.trim().length > 0;
138
+ }
94
139
  export function adapterScoreFailureMessage(bundle) {
95
140
  return bundle.adapterScore?.status === "fail"
96
141
  ? `Adapter scorer failed the run: ${bundle.adapterScore.summary}`
@@ -115,62 +160,6 @@ export function frozenBundleView(bundle) {
115
160
  freeze(clone);
116
161
  return clone;
117
162
  }
118
- function isAdapterScoreShape(value) {
119
- return (typeof value === "object" &&
120
- value !== null &&
121
- !Array.isArray(value) &&
122
- value.schema === "humanish.adapter-score.v1" &&
123
- typeof value.namespace === "string" &&
124
- value.namespace.trim().length > 0 &&
125
- ["pass", "partial", "fail"].includes(value.status) &&
126
- typeof value.score === "number" &&
127
- Number.isFinite(value.score) &&
128
- typeof value.summary === "string" &&
129
- (value.data === undefined ||
130
- (typeof value.data === "object" &&
131
- value.data !== null &&
132
- !Array.isArray(value.data))));
133
- }
134
- function isAdapterFeedbackCandidateShape(value) {
135
- if (!isRecord(value))
136
- return false;
137
- const candidate = value;
138
- if (candidate.schema !== "humanish.feedback-candidate.v1" ||
139
- typeof candidate.id !== "string" ||
140
- typeof candidate.run_id !== "string" ||
141
- (candidate.stream_id !== undefined && typeof candidate.stream_id !== "string") ||
142
- typeof candidate.adapter_id !== "string" ||
143
- typeof candidate.scenario_id !== "string" ||
144
- typeof candidate.persona_id !== "string" ||
145
- !isFeedbackActor(candidate.actor) ||
146
- !isFeedbackSubstrate(candidate.substrate) ||
147
- !isFeedbackFailureOwner(candidate.failure_owner) ||
148
- typeof candidate.summary !== "string" ||
149
- candidate.summary.trim().length === 0 ||
150
- typeof candidate.expected !== "string" ||
151
- typeof candidate.actual !== "string" ||
152
- !Array.isArray(candidate.evidence) ||
153
- !candidate.evidence.every(isFeedbackEvidence) ||
154
- !isRecord(candidate.redaction) ||
155
- candidate.redaction.status !== "passed" ||
156
- typeof candidate.redaction.notes !== "string" ||
157
- !isFeedbackIdempotencyKey(candidate.idempotency_key) ||
158
- !isFeedbackNextState(candidate.proposed_next_state) ||
159
- !Array.isArray(candidate.acceptance_proof) ||
160
- !candidate.acceptance_proof.every((item) => typeof item === "string")) {
161
- return false;
162
- }
163
- if (candidate.adapter !== undefined) {
164
- const adapter = candidate.adapter;
165
- if (!isRecord(adapter) ||
166
- typeof adapter.namespace !== "string" ||
167
- adapter.namespace.trim().length === 0 ||
168
- !isRecord(adapter.data)) {
169
- return false;
170
- }
171
- }
172
- return true;
173
- }
174
163
  function isAdapterArtifactShape(value) {
175
164
  if (!isRecord(value))
176
165
  return false;
@@ -186,9 +175,6 @@ function isAdapterArtifactShape(value) {
186
175
  typeof artifact.note === "string" &&
187
176
  artifact.note.trim().length > 0);
188
177
  }
189
- function isRecord(value) {
190
- return typeof value === "object" && value !== null && !Array.isArray(value);
191
- }
192
178
  function isSafeRelativeArtifactPath(value) {
193
179
  return (value.trim().length > 0 &&
194
180
  !path.isAbsolute(value) &&
@@ -204,50 +190,3 @@ function isAdapterArtifactKind(value) {
204
190
  value === "filesystem" ||
205
191
  value === "summary");
206
192
  }
207
- function isFeedbackActor(value) {
208
- return (value === "codex-tui" ||
209
- value === "codex-exec" ||
210
- value === "codex-app-server" ||
211
- value === "computer-use" ||
212
- value === "synthetic-dry-run" ||
213
- value === "unknown");
214
- }
215
- function isFeedbackSubstrate(value) {
216
- return (value === "e2b-desktop" ||
217
- value === "local-desktop" ||
218
- value === "e2b-terminal" ||
219
- value === "local-filesystem" ||
220
- value === "codex-app-server" ||
221
- value === "unknown");
222
- }
223
- function isFeedbackFailureOwner(value) {
224
- return (value === "harness" ||
225
- value === "target-app" ||
226
- value === "actor" ||
227
- value === "environment" ||
228
- value === "unknown");
229
- }
230
- function isFeedbackNextState(value) {
231
- return (value === "watch" ||
232
- value === "adapter-hardening" ||
233
- value === "target-app-setup" ||
234
- value === "actor-auth" ||
235
- value === "setup-quality-review" ||
236
- value === "study-quality-review");
237
- }
238
- function isFeedbackEvidence(value) {
239
- if (!isRecord(value))
240
- return false;
241
- return (typeof value.path === "string" &&
242
- value.path.length > 0 &&
243
- !path.isAbsolute(value.path) &&
244
- !value.path.includes("://") &&
245
- !value.path.includes("..") &&
246
- (value.kind === "review" ||
247
- value.kind === "state" ||
248
- value.kind === "log" ||
249
- value.kind === "trace" ||
250
- value.kind === "screenshot" ||
251
- value.kind === "filesystem") &&
252
- typeof value.note === "string");
253
- }
@@ -3,20 +3,40 @@ import type { TerminalProductScoringContext } from "../routes/terminal/types.js"
3
3
  import type { LabRoute } from "./plan.js";
4
4
  import type { RunAdapterArtifact, RunAdapterScore, RunFeedbackCandidate, RunScorerProvenance } from "../run/bundle.js";
5
5
  /** The read-model context a loaded scorer sees — the terminal or browser scoring context. The module
6
- * narrows it at runtime (`"product" in ctx` ⇒ terminal; `"backend" in ctx` ⇒ browser). */
6
+ * narrows it at runtime (`"product" in ctx` ⇒ terminal; `"route" in ctx` ⇒ browser). */
7
7
  export type AdapterScoringContext = TerminalProductScoringContext | BrowserLabScoringContext;
8
8
  /**
9
9
  * The adopter-facing scorer module contract (#316). Export any subset of these from a `.mjs` (or a
10
10
  * `.js`/`.cjs` whose package.json type matches) — named exports OR a single default object. The
11
11
  * loader wires ONLY these three; an exported `executor`/`env`/`provisionSubject`/`costProbe` is never
12
12
  * picked up (the whitelist is the scope guard). A module exporting NONE of them is a hard load error.
13
+ *
14
+ * `C` is the context `score` and `deriveFeedback` read: `BrowserLabScoringContext` for a scorer
15
+ * written for computer use and shared world, `TerminalProductScoringContext` for one written for
16
+ * terminal runs, or the default union for one that narrows at runtime.
13
17
  */
14
- export interface AdapterScorerModule {
15
- score?: (ctx: AdapterScoringContext) => RunAdapterScore | Promise<RunAdapterScore>;
16
- deriveFeedback?: (ctx: AdapterScoringContext) => RunFeedbackCandidate[] | Promise<RunFeedbackCandidate[]>;
17
- /** Browser-route only; inert on the terminal route (TerminalProductLabHooks carries no artifacts seam). */
18
+ export interface AdapterScorerModule<C extends AdapterScoringContext = AdapterScoringContext> {
19
+ score?: (ctx: C) => RunAdapterScore | Promise<RunAdapterScore>;
20
+ deriveFeedback?: (ctx: C) => RunFeedbackCandidate[] | Promise<RunFeedbackCandidate[]>;
21
+ /** Browser-route only; inert on the terminal route, whose TerminalScorer has no artifacts seam. */
18
22
  deriveArtifacts?: (ctx: BrowserLabScoringContext) => RunAdapterArtifact[] | Promise<RunAdapterArtifact[]>;
19
23
  }
24
+ /**
25
+ * A scorer written for computer use and shared world, as `RunLabOptions.scorer` takes it. Those
26
+ * routes pass the browser context; another route that calls it passes a context it was not written
27
+ * for. The call states the kind, so the module itself needs no cast.
28
+ */
29
+ export declare function browserScorer(scorer: AdapterScorerModule<BrowserLabScoringContext>): AdapterScorerModule;
30
+ /** A scorer written for terminal runs, as `RunLabOptions.scorer` takes it (see browserScorer). */
31
+ export declare function terminalScorer(scorer: AdapterScorerModule<TerminalProductScoringContext>): AdapterScorerModule;
32
+ /**
33
+ * The scorer as a computer-use or shared-world run calls it, with the browser context. The caller
34
+ * chose the context it wrote the scorer for; only the route knows which context it passes, so the
35
+ * narrowing is here. A terminal-typed scorer on a browser route reads fields the context lacks.
36
+ */
37
+ export declare function browserRouteScorer(scorer: AdapterScorerModule): AdapterScorerModule<BrowserLabScoringContext>;
38
+ /** The scorer as a terminal run calls it, with the terminal context (see browserRouteScorer). */
39
+ export declare function terminalRouteScorer(scorer: AdapterScorerModule): AdapterScorerModule<TerminalProductScoringContext>;
20
40
  type AdapterScorerLoadErrorCode = "HUMANISH_LAB_SCORER_BAD_REF" | "HUMANISH_LAB_SCORER_NOT_FOUND" | "HUMANISH_LAB_SCORER_LOAD_FAILED" | "HUMANISH_LAB_SCORER_NO_HOOKS" | "HUMANISH_LAB_SCORER_UNSUPPORTED_BACKEND";
21
41
  export type AdapterScorerLoadResult = {
22
42
  ok: true;
@@ -19,6 +19,30 @@ import path from "node:path";
19
19
  import { pathToFileURL } from "node:url";
20
20
  import { digestText, redactText } from "../evidence/redaction.js";
21
21
  import { prepareSelectedOutputDirectory, readContainedRegularFile, } from "../run/contained-output.js";
22
+ /**
23
+ * A scorer written for computer use and shared world, as `RunLabOptions.scorer` takes it. Those
24
+ * routes pass the browser context; another route that calls it passes a context it was not written
25
+ * for. The call states the kind, so the module itself needs no cast.
26
+ */
27
+ export function browserScorer(scorer) {
28
+ return scorer;
29
+ }
30
+ /** A scorer written for terminal runs, as `RunLabOptions.scorer` takes it (see browserScorer). */
31
+ export function terminalScorer(scorer) {
32
+ return scorer;
33
+ }
34
+ /**
35
+ * The scorer as a computer-use or shared-world run calls it, with the browser context. The caller
36
+ * chose the context it wrote the scorer for; only the route knows which context it passes, so the
37
+ * narrowing is here. A terminal-typed scorer on a browser route reads fields the context lacks.
38
+ */
39
+ export function browserRouteScorer(scorer) {
40
+ return scorer;
41
+ }
42
+ /** The scorer as a terminal run calls it, with the terminal context (see browserRouteScorer). */
43
+ export function terminalRouteScorer(scorer) {
44
+ return scorer;
45
+ }
22
46
  /** The routes whose hooks bag can carry the loaded scorer. A declared scorer on ANY other route
23
47
  * (scripted or preview) aborts at load — a gate that cannot run must never green-pass. */
24
48
  const SCORER_CAPABLE_ROUTES = new Set([
@@ -3,3 +3,5 @@ import type { LabConfig } from "./types.js";
3
3
  export declare function compositionReason(config: LabConfig): string | null;
4
4
  /** Why a this-repo lab cannot run live. The parser and the preview planner both refuse with it. */
5
5
  export declare const THIS_REPO_DRY_RUN_ONLY = "this-repo labs are dry-run only; use a clone or app-url subject for a live run.";
6
+ /** A desktop-cli subject with no product to study. The computer-use planner checks this too. */
7
+ export declare function desktopCliProductReason(config: LabConfig): string | null;
@@ -1,7 +1,10 @@
1
1
  // Which subject, target and actor compositions a lab may declare. Each rule returns the refusal
2
2
  // message for the first thing its composition gets wrong, or null. compositionReason runs them in
3
- // the parser's order; parseLabConfig calls it after the structural parse, and the planLab design
4
- // has library callers go through the same function.
3
+ // the parser's order, and only parseLabConfig calls it. planLab does not: each route planner checks
4
+ // a library caller's config itself, under its route's error codes. The planners call the shared
5
+ // checks in lab/validation.ts and desktopCliProductReason below. The other subject rules here have
6
+ // planner counterparts whose conditions and wording differ (clone serve and repo, local-tree
7
+ // target, in-process fan-out, loopback targets, scripted scenario.ref).
5
8
  import { isLoopbackUrl } from "./parse/subject.js";
6
9
  import { REPO_SLUG_PATTERN } from "./parse/values.js";
7
10
  import { declaredTargets } from "./plan-participants.js";
@@ -18,10 +21,10 @@ export function compositionReason(config) {
18
21
  cloneTargetValidationReason(config) ??
19
22
  cloneComputerUseValidationReason(config) ??
20
23
  localTreeValidationReason(config) ??
21
- // Independent computer-use lanes: the roster contract on every route that resolves to cua.
24
+ // Independent computer-use participants: the roster contract on every route that resolves to cua.
22
25
  (isComputerUseComposition(config) ? cuaLaneValidationReason(config) : null) ??
23
26
  // Whenever shared world is declared, not only when it routes, so a half-declared shared world
24
- // fails with a precise reason instead of running as independent lanes.
27
+ // fails with a precise reason instead of running as independent participants.
25
28
  (config.subject.topology === "shared-world" ? sharedWorldValidationReason(config) : null) ??
26
29
  desktopCliValidationReason(config) ??
27
30
  terminalValidationReason(config) ??
@@ -42,12 +45,12 @@ function thisRepoValidationReason(config) {
42
45
  }
43
46
  return null;
44
47
  }
45
- // local-app route: an already-running LOCAL dev server driven IN-PROCESS via a custom
48
+ // local-app route: an already-running local dev server driven in-process via a custom
46
49
  // CuaExecutor (no clone, no E2B desktop). Parse-validated fail-closed: a computer-use actor
47
- // only, execution.target local or absent (NEVER e2b-desktop — the whole point is to skip the
50
+ // only, execution.target local or absent (never e2b-desktop, because the route exists to skip the
48
51
  // desktop), and no public-target policy (it is always loopback; the loopback shape was already
49
- // enforced in parseSubject). The actual "no buildExecutor hook supplied" case is inherently an
50
- // engine-time decision (the parser cannot know whether a library caller will pass hooks), so
52
+ // enforced in parseSubject). The actual "no inProcess executor supplied" case is inherently an
53
+ // engine-time decision (the parser cannot know whether a library caller will pass one), so
51
54
  // it fails closed in runCuaActorLab with HUMANISH_CUA_LAB_LOCAL_APP_NO_EXECUTOR.
52
55
  function localAppValidationReason(config) {
53
56
  if (config.subject.source === "local-app") {
@@ -70,7 +73,7 @@ function localAppValidationReason(config) {
70
73
  }
71
74
  return null;
72
75
  }
73
- // app-url routes: the actor type is a REAL dispatch key (registry-resolved). The actor LANE
76
+ // app-url routes: the actor type is a real dispatch key (registry-resolved). The actor lane
74
77
  // picks the substrate: a scripted-browser actor runs locally against the declared loopback
75
78
  // app; a computer-use actor drives a hosted desktop browser. Fail closed on mis-configs.
76
79
  function appUrlValidationReason(config) {
@@ -105,9 +108,9 @@ function appUrlValidationReason(config) {
105
108
  if (!actorResolvesToComputerUse(type)) {
106
109
  return `actors[0].type must be a registered computer-use actor for app-url × e2b-desktop labs (one of: ${registeredComputerUseActors().join(", ")}); for local scripted execution use a registered scripted-browser actor (${registeredScriptedBrowserActors().join(", ")}). Got "${type}".`;
107
110
  }
108
- // Multi-lane fan-out is CONSUMED on this route (per-lane worlds; the shared cua-lane
109
- // cross-validation below enforces lanes/count XOR rules, the 16 cap, and the
110
- // lane-level target gates, and the allowPublicTargets+N>1 rejection for ambiguous one-target
111
+ // Multi-participant fan-out is consumed on this route (`per-lane-worlds`; the shared cua
112
+ // cross-validation below enforces `lanes`/`count` XOR rules, the 16 cap, and the
113
+ // per-participant target gates, and the allowPublicTargets+N>1 rejection for ambiguous one-target
111
114
  // fan-out).
112
115
  // Loopback by default; an owner may declare a public/preview target via policies.
113
116
  const targets = [config.subject.appUrl ?? "", ...declaredTargets(config)];
@@ -175,7 +178,7 @@ function scriptedBrowserValidationReason(config) {
175
178
  return null;
176
179
  }
177
180
  // clone × e2b-desktop disambiguates on the actor lane: a computer-use actor means the lab
178
- // clones AND serves the subject in-sandbox, then drives it. Scripted-browser actors were checked
181
+ // clones and serves the subject in-sandbox, then drives it. Scripted-browser actors were checked
179
182
  // above.
180
183
  function cloneComputerUseValidationReason(config) {
181
184
  if (config.subject.source === "clone" &&
@@ -191,15 +194,15 @@ function cloneComputerUseValidationReason(config) {
191
194
  if (!REPO_SLUG_PATTERN.test(repo)) {
192
195
  return `subject.repos[0] must be an owner/repo slug (got "${repo}").`;
193
196
  }
194
- // Fan-out is CONSUMED here: N lanes each clone the SAME single repo into their own E2B
195
- // desktop (per-lane worlds). The shared cua-lane cross-validation below enforces the
196
- // lanes/count rules and the 16 cap; the single-repo rule above is unchanged.
197
+ // Fan-out is consumed here: N participants each clone the same single repo into their own
198
+ // E2B desktop (`per-lane-worlds`). The shared cua cross-validation below enforces the
199
+ // `lanes`/`count` rules and the 16 cap; the single-repo rule above is unchanged.
197
200
  }
198
201
  return null;
199
202
  }
200
203
  // local-tree route: packs and uploads the operator's own working tree, then serves it exactly
201
204
  // like a computer-use clone subject. The scripted-browser route has no packed-working-tree
202
- // mode, so e2b-desktop + a computer-use actor are the ONLY combination this source supports.
205
+ // mode, so e2b-desktop + a computer-use actor are the only combination this source supports.
203
206
  // `subject.serve` is already required at parse time (parseSubject); the repos/clone rejection
204
207
  // also already happened there (local-tree never carries git slugs).
205
208
  function localTreeValidationReason(config) {
@@ -215,17 +218,23 @@ function localTreeValidationReason(config) {
215
218
  }
216
219
  // desktop-cli route: a computer-use participant studies a CLI/TUI the way a person does — at a
217
220
  // desktop, in a terminal window, by looking at it. The sibling of terminal-product, and the
218
- // distinction is the POPULATION, not the product: terminal-product sends an autonomous agent
221
+ // distinction is the population: terminal-product sends an autonomous agent
219
222
  // through a pipe with stdin disabled, which is the honest way to study what an agent meets and
220
223
  // structurally cannot study an interactive surface. This route sends someone who can see it.
221
224
  //
222
225
  // Fail-closed on the pairing (invariant 6): a hosted desktop and a computer-use actor, because
223
226
  // "watch a person use a terminal" is not something the other substrates can do.
227
+ /** A desktop-cli subject with no product to study. The computer-use planner checks this too. */
228
+ export function desktopCliProductReason(config) {
229
+ return config.subject.source === "desktop-cli" && config.subject.product?.name === undefined
230
+ ? "desktop-cli subjects need `subject.product.name` — the CLI the participant is being asked to use."
231
+ : null;
232
+ }
224
233
  function desktopCliValidationReason(config) {
225
- if (config.subject.source === "desktop-cli") {
226
- if (config.subject.product?.name === undefined) {
227
- return "desktop-cli subjects need `subject.product.name` — the CLI the participant is being asked to use.";
228
- }
234
+ const productReason = desktopCliProductReason(config);
235
+ if (productReason)
236
+ return productReason;
237
+ if (config.subject.source === "desktop-cli" && config.subject.product !== undefined) {
229
238
  if (config.execution?.target !== undefined && config.execution.target !== "e2b-desktop") {
230
239
  return "desktop-cli subjects are studied at a hosted desktop — set `execution.target: e2b-desktop` or omit it.";
231
240
  }
@@ -239,11 +248,11 @@ function desktopCliValidationReason(config) {
239
248
  }
240
249
  return null;
241
250
  }
242
- // terminal-product route: a real autonomous agent studies a CLI/product from PUBLIC surfaces
251
+ // terminal-product route: a real autonomous agent studies a CLI/product from public surfaces
243
252
  // inside an E2B shell. Fail-closed (invariant 6 — a field that cannot act on this route is an
244
253
  // honest parse error): a registered terminal actor only, execution.target e2b-terminal or absent
245
- // (absent defaults to e2b-terminal — the only honest target for an in-sandbox agent), single
246
- // lane until fan-out lands.
254
+ // (absent defaults to e2b-terminal — the only honest target for an in-sandbox agent), one
255
+ // participant until fan-out lands.
247
256
  function terminalValidationReason(config) {
248
257
  if (config.subject.source === "terminal-product") {
249
258
  const type = config.actors[0]?.type ?? "";
@@ -258,7 +267,7 @@ function terminalValidationReason(config) {
258
267
  }
259
268
  }
260
269
  else if (config.execution?.target === "e2b-terminal") {
261
- // e2b-terminal is the terminal-product substrate ONLY. Any other source declaring it is a
270
+ // e2b-terminal is the terminal-product substrate only. Any other source declaring it is a
262
271
  // mis-config — reject, never silently mishandle (mirrors app-url's e2b-desktop pairing rule).
263
272
  return "`execution.target: e2b-terminal` requires `subject.source: terminal-product` with a registered terminal actor.";
264
273
  }
@@ -1,59 +1,61 @@
1
- // humanish.lab.v2 — a lab is a COMPOSITION over code primitives, not a hardcoded kind.
1
+ // humanish.lab.v2: a lab is a composition over code primitives. There is no hardcoded lab kind.
2
2
  //
3
- // HONEST SCOPE (read before trusting field names): the engine routes by
3
+ // Scope (read before trusting field names): the engine routes by
4
4
  // subject.source × execution.target (disambiguated by the actor lane where both axes
5
5
  // collide) and consumes a deliberately small set of fields:
6
6
  // subject.source/repos/appUrl/serve/env/state/clone.{depth,fanout,keep}, actors[0].count,
7
7
  // execution.target + execution.desktop.codexAppServer, scenario.mode,
8
8
  // policies.redactRepos, defaults.open.
9
9
  // On the computer-use routes (app-url × e2b-desktop, and clone × e2b-desktop with a
10
- // computer-use actor), `actors[0].type` IS load-bearing: it must resolve to a registered
10
+ // computer-use actor), `actors[0].type` is load-bearing: it must resolve to a registered
11
11
  // computer-use actor, and that descriptor runs the session. Those routes also consume
12
12
  // actors[0].{mission,persona,laneFocus.instruction,model,reasoningEffort}, execution.timeoutMs,
13
13
  // execution.desktop.{browser,resolution,sandboxTimeoutMs}, and (clone)
14
14
  // subject.{serve,env,state,clone.depth}.
15
15
  // On the scripted-browser route (app-url × local-or-absent, or clone × e2b-desktop, with a
16
16
  // registered scripted-browser actor), `actors[0].type` is equally load-bearing, and the route
17
- // consumes scenario.ref (REQUIRED there — the committed scenario's browser steps ARE what the
17
+ // consumes scenario.ref (required there, because the committed scenario's browser steps are what the
18
18
  // actor executes), actors[0].{persona,count}, and execution.timeoutMs. On the provisioned
19
19
  // clone slice it also consumes subject.{repos,serve,env,state,exposure,clone.depth} and
20
20
  // execution.desktop.template. actors[0].{mission,laneFocus,model} are inert on that route
21
21
  // because no model runs, and most execution.desktop.* fields remain forward-declared (device
22
22
  // presets belong to the cua route — scripted surfaces are the driver's own desktop/mobile
23
- // viewports where isMobile/DSF genuinely RENDER via playwright emulation).
24
- // On the other routes those fields remain FORWARD-DECLARED and NOT yet consumed —
23
+ // viewports where isMobile/DSF genuinely render via playwright emulation).
24
+ // On the other routes those fields remain forward-declared and are not yet consumed:
25
25
  // parseLabConfig emits a warning listing any such field that is set, so `lab inspect` shows
26
26
  // the truth.
27
27
  //
28
28
  // NOTE on actors[0].count: it now carries ROUTE-SPECIFIC meanings — synthetic route: simCount;
29
29
  // scripted-browser route: surface roster {1 = desktop, 2 = desktop + mobile}, default 1 (the
30
- // defaults-table single-lane row governs; count: 2 is the declared override); computer-use
31
- // E2B route: the HOMOGENEOUS fan-out lane count (N identical lanes, each its own E2B desktop),
32
- // capped at 16; the in-process/local-app cua route stays single lane (no E2B to fan out).
30
+ // defaults-table single-participant row governs; count: 2 is the declared override); computer-use
31
+ // E2B route: the homogeneous fan-out participant count (N identical participants, each its own E2B
32
+ // desktop), capped at 16; the in-process/local-app cua route stays single-participant (no E2B to
33
+ // fan out).
33
34
  //
34
35
  // NOTE on actors[0].lanes / actors[0].roster (computer-use E2B route, this slice): a
35
- // DIFFERENTIATED fan-out roster — each `{ id?, persona?, device?, instruction?, target? }` becomes one
36
- // independent E2B desktop (per-lane worlds, the default topology). `roster[]` is parser sugar for
36
+ // differentiated fan-out roster: each `{ id?, persona?, device?, instruction?, target? }` becomes one
37
+ // independent E2B desktop (`per-lane-worlds`, the default topology). `roster[]` is parser sugar for
37
38
  // repeated groups and is normalized into `lanes[]` before the engine sees it. `lanes|roster` XOR
38
- // `count` (declare a differentiated roster OR a homogeneous count, never both); `lanes|roster`
39
- // XOR `actors[0].laneFocus` (per-lane `instruction` is the roster's steer); `lanes[].device` XOR
40
- // raw `execution.desktop.resolution`. `execution.concurrency` bounds in-flight lanes (default: the
41
- // lane count, every lane at once; env HUMANISH_CUA_MAX_CONCURRENCY may only LOWER it — invariant
39
+ // `count` (declare a differentiated roster or a homogeneous count, never both); `lanes|roster`
40
+ // XOR `actors[0].laneFocus` (each entry's `instruction` is the roster's steer); `lanes[].device` XOR
41
+ // raw `execution.desktop.resolution`. `execution.concurrency` bounds in-flight participants (default:
42
+ // the participant count, all at once; env HUMANISH_CUA_MAX_CONCURRENCY may only lower it, per invariant
42
43
  // 3). On every non-cua route normalized `lanes` are inert (warned). subject.clone.fanout is
43
- // REJECTED on the cua route. `lanes[].target` is app-url × computer-use ONLY: an absolute browser
44
- // URL this lane opens instead of `subject.appUrl`; it is the generic setup-produced-target
44
+ // rejected on the cua route. `lanes[].target` is app-url × computer-use only: an absolute browser
45
+ // URL this participant opens instead of `subject.appUrl`; it is the generic setup-produced-target
45
46
  // handoff, not a service topology primitive.
46
47
  //
47
- // There is deliberately NO v1 compatibility: v1 had zero real users. Breaking schema changes
48
+ // There is deliberately no v1 compatibility: v1 had zero real users. Breaking schema changes
48
49
  // bump the version honestly.
49
50
  import { isLocalBrowserLab, localBrowserDefaults, localBrowserUnsupportedReason, } from "../substrates/local/runtime-config.js";
51
+ import { isRecord } from "../run/type-guards.js";
50
52
  import { compositionReason } from "./composition-rules.js";
51
53
  import { findUnknownLabKey } from "./keys.js";
52
54
  import { parseActors, rosterOf } from "./parse/actors.js";
53
55
  import { parseComms, recipientParticipantId } from "./parse/comms.js";
54
56
  import { parseDefaults, parseExecution, parsePersonas, parsePolicies, parseReview, parseScenario, } from "./parse/execution.js";
55
57
  import { parseSubject } from "./parse/subject.js";
56
- import { invalid, isRecord, optionalStr, str } from "./parse/values.js";
58
+ import { invalid, optionalStr, str } from "./parse/values.js";
57
59
  import { isComputerUseComposition, isSharedWorldComposition } from "./routing.js";
58
60
  import { declaredParticipantIds } from "./plan-participants.js";
59
61
  import { ID_PATTERN, LAB_CONFIG_SCHEMA, } from "./types.js";
@@ -150,7 +152,7 @@ export function parseLabConfig(raw) {
150
152
  return invalid(scenarioCapsReason);
151
153
  // All-parallel default (#350): a multi-seat lab that does not declare execution.concurrency runs
152
154
  // every seat at once; the declared field is a cap the author chose, never a mode. Independent
153
- // computer-use lanes resolve that default from the final seat count when they plan, after any
155
+ // computer-use participants resolve that default from the final participant count when they plan, after any
154
156
  // --count override, so the parser leaves it unset for them. A shared world's roster is fixed, so
155
157
  // its default is filled here for the envelopes and warnings that read the parsed config.
156
158
  {
@@ -161,12 +163,12 @@ export function parseLabConfig(raw) {
161
163
  config.execution = { ...config.execution, concurrency: participantCount };
162
164
  }
163
165
  }
164
- // Email that just works (#351): the funnel's ONLY handoff to an actor is the per-lane inbox
165
- // instruction, gated on recipients[]. Guessed lane names broke a field run — recipients copied
166
- // from a single-lane example matched nothing, so every actor was left inbox-blind with zero
167
- // signal. Omitted recipients are therefore FILLED (one deterministic address per lane); a
168
- // recipient naming an unknown lane is a hard error listing the real lane ids; declared
169
- // recipients covering zero lanes are a hard error (a guaranteed-dead funnel).
166
+ // Email that just works (#351): the funnel's only handoff to an actor is the per-participant
167
+ // inbox instruction, gated on recipients[]. Guessed participant ids broke a field run —
168
+ // recipients copied from a single-participant example matched nothing, so every actor was left inbox-blind with zero
169
+ // signal. Omitted recipients are therefore filled (one deterministic address per participant);
170
+ // a recipient naming an unknown participant is a hard error listing the real ids; declared
171
+ // recipients covering zero participants are a hard error (a guaranteed-dead funnel).
170
172
  const receivingReason = receivingEmailValidationReason(config);
171
173
  if (receivingReason)
172
174
  return invalid(receivingReason);
@@ -4,9 +4,9 @@ export interface DevicePreset {
4
4
  width: number;
5
5
  /** Requested device height, used for the hosted physical screen. */
6
6
  height: number;
7
- /** Mobile identity; enables emulation only when the hosted lane explicitly opts in. */
7
+ /** Mobile identity; enables emulation only when the hosted participant explicitly opts in. */
8
8
  isMobile: boolean;
9
- /** Requested DPR; applied on hosted mobile lanes only with opt-in CDP emulation. */
9
+ /** Requested DPR; applied on hosted mobile participants only with opt-in CDP emulation. */
10
10
  deviceScaleFactor: number;
11
11
  }
12
12
  /**