humanish 0.107.0 → 0.109.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 (671) hide show
  1. package/ARCHITECTURE.md +73 -69
  2. package/CHANGELOG.md +27 -0
  3. package/CONTEXT.md +51 -44
  4. package/README.md +148 -175
  5. package/SECURITY.md +16 -26
  6. package/TELEMETRY.md +8 -6
  7. package/dist/actors/affordance.d.ts +4 -4
  8. package/dist/actors/affordance.js +12 -12
  9. package/dist/actors/agent-session.d.ts +2 -2
  10. package/dist/actors/agent-session.js +7 -7
  11. package/dist/actors/codex/app-server-actor-trace.d.ts +1 -1
  12. package/dist/actors/codex/app-server-actor-trace.js +1 -1
  13. package/dist/actors/codex/app-server-trace.js +2 -1
  14. package/dist/actors/codex/app-server-ui.js +1 -1
  15. package/dist/actors/codex/codex-admission.d.ts +91 -0
  16. package/dist/actors/codex/codex-admission.js +150 -0
  17. package/dist/actors/codex/restricted-executable.d.ts +4 -4
  18. package/dist/actors/codex/restricted-executable.js +8 -8
  19. package/dist/actors/codex/restricted-launch.d.ts +2 -1
  20. package/dist/actors/codex/restricted-launch.js +1 -1
  21. package/dist/actors/codex/restricted-participant.js +20 -18
  22. package/dist/actors/codex/restricted-policy.d.ts +2 -2
  23. package/dist/actors/codex/restricted-policy.js +2 -2
  24. package/dist/actors/codex/restricted-session.d.ts +3 -3
  25. package/dist/actors/codex/restricted-session.js +10 -5
  26. package/dist/actors/computer-use/actor.d.ts +18 -18
  27. package/dist/actors/computer-use/actor.js +4 -4
  28. package/dist/actors/computer-use/admission-limit.d.ts +3 -3
  29. package/dist/actors/computer-use/admission-limit.js +4 -4
  30. package/dist/actors/computer-use/executor-error.d.ts +2 -2
  31. package/dist/actors/computer-use/executor-error.js +3 -3
  32. package/dist/actors/computer-use/loop/actions.d.ts +3 -3
  33. package/dist/actors/computer-use/loop/actions.js +15 -15
  34. package/dist/actors/computer-use/loop/backstop.d.ts +3 -3
  35. package/dist/actors/computer-use/loop/backstop.js +14 -12
  36. package/dist/actors/computer-use/loop/debrief.js +2 -2
  37. package/dist/actors/computer-use/loop/ending.d.ts +6 -6
  38. package/dist/actors/computer-use/loop/ending.js +18 -17
  39. package/dist/actors/computer-use/loop/observation.d.ts +2 -2
  40. package/dist/actors/computer-use/loop/observation.js +15 -14
  41. package/dist/actors/computer-use/loop/provider-call.d.ts +1 -1
  42. package/dist/actors/computer-use/loop/provider-call.js +12 -12
  43. package/dist/actors/computer-use/loop/session.d.ts +4 -4
  44. package/dist/actors/computer-use/loop/session.js +4 -4
  45. package/dist/actors/computer-use/loop/spend.d.ts +1 -1
  46. package/dist/actors/computer-use/loop/spend.js +1 -1
  47. package/dist/actors/computer-use/loop/trace.js +11 -9
  48. package/dist/actors/computer-use/loop/types.d.ts +47 -45
  49. package/dist/actors/computer-use/loop/usage.d.ts +2 -2
  50. package/dist/actors/computer-use/loop/usage.js +2 -2
  51. package/dist/actors/computer-use/loop.d.ts +6 -1
  52. package/dist/actors/computer-use/loop.js +26 -4
  53. package/dist/actors/computer-use/openai-context.d.ts +37 -0
  54. package/dist/actors/computer-use/openai-context.js +214 -0
  55. package/dist/actors/computer-use/openai-provider.d.ts +13 -10
  56. package/dist/actors/computer-use/openai-provider.js +52 -41
  57. package/dist/actors/computer-use/openai-requests.d.ts +36 -10
  58. package/dist/actors/computer-use/openai-requests.js +82 -15
  59. package/dist/actors/computer-use/openai-wire.d.ts +19 -7
  60. package/dist/actors/computer-use/openai-wire.js +36 -34
  61. package/dist/actors/computer-use/provider-error.d.ts +4 -4
  62. package/dist/actors/computer-use/provider-error.js +6 -6
  63. package/dist/actors/contract.d.ts +82 -47
  64. package/dist/actors/contract.js +30 -24
  65. package/dist/actors/local-agent/claude-session.d.ts +1 -1
  66. package/dist/actors/local-agent/claude-session.js +13 -12
  67. package/dist/actors/local-agent/cli.d.ts +9 -7
  68. package/dist/actors/local-agent/cli.js +24 -28
  69. package/dist/actors/local-agent/readiness.d.ts +2 -2
  70. package/dist/actors/local-agent/readiness.js +4 -4
  71. package/dist/actors/reasoning-effort.js +8 -8
  72. package/dist/actors/registry.d.ts +12 -12
  73. package/dist/actors/registry.js +21 -20
  74. package/dist/actors/scripted-browser/actor.d.ts +10 -10
  75. package/dist/actors/scripted-browser/actor.js +17 -17
  76. package/dist/actors/scripted-browser/browser-command.js +1 -1
  77. package/dist/actors/scripted-browser/steps.js +1 -1
  78. package/dist/actors/scripted-browser/types.d.ts +1 -1
  79. package/dist/actors/stop-conditions.d.ts +1 -1
  80. package/dist/analysis/automatic-completion.d.ts +7 -7
  81. package/dist/analysis/automatic-completion.js +1 -1
  82. package/dist/analysis/automatic-config.d.ts +6 -6
  83. package/dist/analysis/automatic-config.js +1 -1
  84. package/dist/analysis/codex-config.d.ts +6 -2
  85. package/dist/analysis/codex-config.js +22 -14
  86. package/dist/analysis/execute.d.ts +1 -1
  87. package/dist/analysis/execute.js +1 -1
  88. package/dist/analysis/service.js +5 -5
  89. package/dist/analysis/types.d.ts +6 -0
  90. package/dist/browser-control/client.js +16 -16
  91. package/dist/browser-control/dispatcher.js +4 -4
  92. package/dist/browser-control/protocol.js +14 -14
  93. package/dist/browser-control/recording-transfer.js +4 -4
  94. package/dist/browser-control/transport.js +7 -7
  95. package/dist/cli/commands/analysis-signals.d.ts +2 -2
  96. package/dist/cli/commands/analyze.js +19 -16
  97. package/dist/cli/commands/codex.js +9 -9
  98. package/dist/cli/commands/comms.js +48 -35
  99. package/dist/cli/commands/feedback.js +20 -21
  100. package/dist/cli/commands/migrate.d.ts +4 -0
  101. package/dist/cli/commands/migrate.js +65 -0
  102. package/dist/cli/commands/observe.d.ts +0 -1
  103. package/dist/cli/commands/observe.js +136 -112
  104. package/dist/cli/commands/run-command.d.ts +10 -0
  105. package/dist/cli/commands/run-command.js +85 -0
  106. package/dist/cli/commands/runs.js +136 -139
  107. package/dist/cli/commands/setup.d.ts +2 -2
  108. package/dist/cli/commands/setup.js +27 -24
  109. package/dist/cli/commands/study-format.d.ts +9 -0
  110. package/dist/cli/commands/{lab-format.js → study-format.js} +39 -27
  111. package/dist/cli/commands/{lab-route-computer-use.d.ts → study-route-computer-use.d.ts} +6 -6
  112. package/dist/cli/commands/{lab-route-computer-use.js → study-route-computer-use.js} +17 -18
  113. package/dist/cli/commands/{lab-route-open.d.ts → study-route-open.d.ts} +4 -4
  114. package/dist/cli/commands/{lab-route-open.js → study-route-open.js} +2 -2
  115. package/dist/cli/commands/{lab-route-preview.d.ts → study-route-preview.d.ts} +7 -7
  116. package/dist/cli/commands/{lab-route-preview.js → study-route-preview.js} +7 -6
  117. package/dist/cli/commands/study-route-run.d.ts +19 -0
  118. package/dist/cli/commands/{lab-route-run.js → study-route-run.js} +6 -6
  119. package/dist/cli/commands/study-route-scripted.d.ts +17 -0
  120. package/dist/cli/commands/{lab-route-scripted.js → study-route-scripted.js} +4 -4
  121. package/dist/cli/commands/{lab-route-shared-world.d.ts → study-route-shared-world.d.ts} +5 -5
  122. package/dist/cli/commands/{lab-route-shared-world.js → study-route-shared-world.js} +10 -10
  123. package/dist/cli/commands/study-route-terminal.d.ts +17 -0
  124. package/dist/cli/commands/{lab-route-terminal.js → study-route-terminal.js} +4 -4
  125. package/dist/cli/commands/study-run.d.ts +9 -0
  126. package/dist/cli/commands/{lab-run.js → study-run.js} +44 -45
  127. package/dist/cli/commands/{lab-scorer.d.ts → study-scorer.d.ts} +7 -7
  128. package/dist/cli/commands/{lab-scorer.js → study-scorer.js} +2 -2
  129. package/dist/cli/commands/study.d.ts +3 -0
  130. package/dist/cli/commands/study.js +146 -0
  131. package/dist/cli/commands/tui.d.ts +4 -4
  132. package/dist/cli/commands/tui.js +31 -31
  133. package/dist/cli/commands/watch.js +71 -121
  134. package/dist/cli/deprecations.d.ts +6 -0
  135. package/dist/cli/deprecations.js +18 -0
  136. package/dist/cli/doctor.d.ts +18 -8
  137. package/dist/cli/doctor.js +114 -59
  138. package/dist/cli/first-run-path.d.ts +15 -13
  139. package/dist/cli/first-run-path.js +35 -33
  140. package/dist/cli/io.d.ts +44 -9
  141. package/dist/cli/io.js +98 -23
  142. package/dist/cli/observer-follow.d.ts +3 -3
  143. package/dist/cli/observer-follow.js +6 -7
  144. package/dist/cli/orientation.d.ts +10 -7
  145. package/dist/cli/orientation.js +89 -47
  146. package/dist/cli/product-sentence.d.ts +6 -0
  147. package/dist/cli/product-sentence.js +6 -0
  148. package/dist/cli/program.d.ts +6 -1
  149. package/dist/cli/program.js +111 -43
  150. package/dist/cli/telemetry.d.ts +16 -16
  151. package/dist/cli/telemetry.js +46 -44
  152. package/dist/cli.js +1 -1
  153. package/dist/comms/capture-surface.d.ts +13 -12
  154. package/dist/comms/capture-surface.js +23 -22
  155. package/dist/comms/catch-host.d.ts +7 -7
  156. package/dist/comms/catch-host.js +17 -17
  157. package/dist/comms/connections.d.ts +1 -1
  158. package/dist/comms/connections.js +2 -2
  159. package/dist/comms/email-catch.d.ts +3 -3
  160. package/dist/comms/email-catch.js +8 -8
  161. package/dist/comms/external-evidence.d.ts +5 -5
  162. package/dist/comms/external-evidence.js +5 -4
  163. package/dist/comms/extract.js +2 -2
  164. package/dist/comms/fake-inbox.d.ts +3 -3
  165. package/dist/comms/fake-inbox.js +3 -3
  166. package/dist/comms/receiving-render.d.ts +1 -1
  167. package/dist/comms/receiving-render.js +2 -2
  168. package/dist/comms/receiving-runtime.d.ts +4 -4
  169. package/dist/comms/sandbox-catch-script.d.ts +6 -6
  170. package/dist/comms/sandbox-catch-script.js +6 -6
  171. package/dist/comms/sandbox-catch.d.ts +38 -39
  172. package/dist/comms/sandbox-catch.js +36 -37
  173. package/dist/comms/setup.d.ts +2 -2
  174. package/dist/comms/setup.js +56 -25
  175. package/dist/comms/thread-evidence.d.ts +1 -1
  176. package/dist/comms/thread-evidence.js +6 -6
  177. package/dist/comms/types.d.ts +6 -6
  178. package/dist/comms/types.js +3 -3
  179. package/dist/evidence/frame-signature.d.ts +1 -1
  180. package/dist/evidence/frame-signature.js +4 -4
  181. package/dist/evidence/redaction.d.ts +3 -3
  182. package/dist/evidence/redaction.js +4 -4
  183. package/dist/feedback/draft.js +38 -80
  184. package/dist/feedback/export-bundle.js +1 -1
  185. package/dist/feedback/export.d.ts +5 -1
  186. package/dist/feedback/export.js +10 -10
  187. package/dist/feedback/feedback.d.ts +2 -2
  188. package/dist/feedback/feedback.js +21 -3
  189. package/dist/feedback/proof.d.ts +1 -1
  190. package/dist/feedback/proof.js +2 -2
  191. package/dist/guest/bootstrap.js +6 -6
  192. package/dist/guest/browser-tools.js +8 -8
  193. package/dist/guest/chromium-text.js +19 -19
  194. package/dist/guest/desktop-executor.d.ts +2 -2
  195. package/dist/guest/desktop-executor.js +20 -20
  196. package/dist/guest/desktop-keys.js +4 -4
  197. package/dist/guest/desktop-media.d.ts +2 -2
  198. package/dist/guest/desktop-media.js +16 -16
  199. package/dist/guest/desktop-native.d.ts +1 -1
  200. package/dist/guest/desktop-native.js +10 -10
  201. package/dist/guest/runtime-desktop.js +2 -2
  202. package/dist/guest/runtime-processes.d.ts +2 -2
  203. package/dist/guest/runtime-processes.js +2 -2
  204. package/dist/guest/runtime.js +6 -6
  205. package/dist/index.d.ts +14 -14
  206. package/dist/index.js +8 -8
  207. package/dist/keys/key-resolution.d.ts +5 -5
  208. package/dist/keys/key-resolution.js +23 -23
  209. package/dist/observer/artifact.d.ts +2 -2
  210. package/dist/observer/artifact.js +7 -7
  211. package/dist/observer/data.d.ts +6 -6
  212. package/dist/observer/data.js +5 -4
  213. package/dist/observer/exposure.d.ts +1 -1
  214. package/dist/observer/exposure.js +11 -11
  215. package/dist/observer/http.js +1 -1
  216. package/dist/observer/library.d.ts +3 -1
  217. package/dist/observer/library.js +8 -3
  218. package/dist/observer/listen.js +5 -4
  219. package/dist/observer/render.d.ts +1 -1
  220. package/dist/observer/render.js +17 -6
  221. package/dist/observer/run-routes.d.ts +4 -3
  222. package/dist/observer/run-routes.js +38 -13
  223. package/dist/observer/serve.d.ts +1 -1
  224. package/dist/observer/serve.js +7 -3
  225. package/dist/observer/tunnel.js +1 -1
  226. package/dist/observer-app.html +4 -4
  227. package/dist/routes/computer-use/bundle-parts.d.ts +9 -9
  228. package/dist/routes/computer-use/bundle-parts.js +18 -17
  229. package/dist/routes/computer-use/bundle.d.ts +2 -2
  230. package/dist/routes/computer-use/bundle.js +10 -9
  231. package/dist/routes/computer-use/e2b-desktop/comms.d.ts +6 -6
  232. package/dist/routes/computer-use/e2b-desktop/comms.js +18 -17
  233. package/dist/routes/computer-use/e2b-desktop/desktop.js +4 -4
  234. package/dist/routes/computer-use/e2b-desktop/fidelity.d.ts +5 -4
  235. package/dist/routes/computer-use/e2b-desktop/fidelity.js +11 -10
  236. package/dist/routes/computer-use/e2b-desktop/prepare.d.ts +1 -1
  237. package/dist/routes/computer-use/e2b-desktop/prepare.js +10 -10
  238. package/dist/routes/computer-use/e2b-desktop/start.js +7 -7
  239. package/dist/routes/computer-use/e2b-desktop/state.d.ts +2 -2
  240. package/dist/routes/computer-use/e2b-desktop/teardown.js +2 -2
  241. package/dist/routes/computer-use/external-comms.d.ts +4 -4
  242. package/dist/routes/computer-use/external-comms.js +1 -1
  243. package/dist/routes/computer-use/fanout-bundle.js +27 -26
  244. package/dist/routes/computer-use/fanout-records.js +12 -13
  245. package/dist/routes/computer-use/live-flush.d.ts +4 -4
  246. package/dist/routes/computer-use/live-flush.js +6 -6
  247. package/dist/routes/computer-use/live-phase.d.ts +5 -4
  248. package/dist/routes/computer-use/live-phase.js +8 -8
  249. package/dist/routes/computer-use/local-tree-pack.d.ts +5 -5
  250. package/dist/routes/computer-use/local-tree-pack.js +1 -1
  251. package/dist/routes/computer-use/local-vm.d.ts +8 -8
  252. package/dist/routes/computer-use/local-vm.js +3 -3
  253. package/dist/routes/computer-use/participant-desktop.d.ts +7 -7
  254. package/dist/routes/computer-use/participant-desktop.js +5 -5
  255. package/dist/routes/computer-use/participant-execution.d.ts +4 -4
  256. package/dist/routes/computer-use/participant-execution.js +10 -10
  257. package/dist/routes/computer-use/participant-model.d.ts +6 -0
  258. package/dist/routes/computer-use/participant-model.js +33 -15
  259. package/dist/routes/computer-use/participant-prompt.d.ts +15 -15
  260. package/dist/routes/computer-use/participant-prompt.js +9 -9
  261. package/dist/routes/computer-use/participant-runs.d.ts +16 -16
  262. package/dist/routes/computer-use/participant-runs.js +26 -25
  263. package/dist/routes/computer-use/plan.d.ts +12 -12
  264. package/dist/routes/computer-use/plan.js +42 -42
  265. package/dist/routes/computer-use/preflight.d.ts +5 -5
  266. package/dist/routes/computer-use/preflight.js +25 -25
  267. package/dist/routes/computer-use/rerun-selection.d.ts +1 -1
  268. package/dist/routes/computer-use/rerun-selection.js +4 -3
  269. package/dist/routes/computer-use/result.d.ts +4 -4
  270. package/dist/routes/computer-use/result.js +22 -21
  271. package/dist/routes/computer-use/route.d.ts +12 -11
  272. package/dist/routes/computer-use/route.js +31 -30
  273. package/dist/routes/computer-use/self-report.d.ts +7 -7
  274. package/dist/routes/computer-use/self-report.js +32 -32
  275. package/dist/routes/computer-use/setup.d.ts +23 -23
  276. package/dist/routes/computer-use/setup.js +28 -29
  277. package/dist/routes/computer-use/single-bundle.d.ts +11 -11
  278. package/dist/routes/computer-use/single-bundle.js +40 -41
  279. package/dist/routes/computer-use/subject-projection.d.ts +4 -4
  280. package/dist/routes/computer-use/subject-projection.js +6 -6
  281. package/dist/routes/computer-use/types.d.ts +71 -73
  282. package/dist/routes/computer-use/types.js +12 -12
  283. package/dist/routes/preview.d.ts +13 -7
  284. package/dist/routes/preview.js +11 -7
  285. package/dist/routes/scripted/bundle.d.ts +7 -7
  286. package/dist/routes/scripted/bundle.js +20 -18
  287. package/dist/routes/scripted/plan.d.ts +8 -8
  288. package/dist/routes/scripted/plan.js +13 -13
  289. package/dist/routes/scripted/result.d.ts +3 -3
  290. package/dist/routes/scripted/result.js +13 -13
  291. package/dist/routes/scripted/route.d.ts +9 -9
  292. package/dist/routes/scripted/route.js +15 -16
  293. package/dist/routes/scripted/scenario.d.ts +1 -1
  294. package/dist/routes/scripted/scenario.js +5 -5
  295. package/dist/routes/scripted/setup.d.ts +6 -6
  296. package/dist/routes/scripted/setup.js +8 -8
  297. package/dist/routes/scripted/subject.d.ts +5 -5
  298. package/dist/routes/scripted/subject.js +4 -4
  299. package/dist/routes/scripted/surface-records.d.ts +1 -1
  300. package/dist/routes/scripted/surface-records.js +4 -4
  301. package/dist/routes/scripted/surface-sessions.d.ts +3 -3
  302. package/dist/routes/scripted/types.d.ts +18 -20
  303. package/dist/routes/scripted/types.js +2 -2
  304. package/dist/routes/shared-world/bundle-records.js +11 -10
  305. package/dist/routes/shared-world/bundle.d.ts +5 -5
  306. package/dist/routes/shared-world/bundle.js +46 -43
  307. package/dist/routes/shared-world/checkpoints.d.ts +4 -4
  308. package/dist/routes/shared-world/checkpoints.js +2 -2
  309. package/dist/routes/shared-world/comms.d.ts +14 -14
  310. package/dist/routes/shared-world/comms.js +19 -18
  311. package/dist/routes/shared-world/external-public.d.ts +4 -3
  312. package/dist/routes/shared-world/external-public.js +16 -12
  313. package/dist/routes/shared-world/handoff.d.ts +7 -6
  314. package/dist/routes/shared-world/handoff.js +65 -57
  315. package/dist/routes/shared-world/lobby-code.d.ts +2 -2
  316. package/dist/routes/shared-world/lobby-code.js +9 -9
  317. package/dist/routes/shared-world/participant-specs.d.ts +10 -9
  318. package/dist/routes/shared-world/participant-specs.js +38 -31
  319. package/dist/routes/shared-world/plan.d.ts +9 -9
  320. package/dist/routes/shared-world/plan.js +16 -16
  321. package/dist/routes/shared-world/provenance.d.ts +2 -2
  322. package/dist/routes/shared-world/provenance.js +2 -2
  323. package/dist/routes/shared-world/provisioned.d.ts +8 -8
  324. package/dist/routes/shared-world/provisioned.js +18 -17
  325. package/dist/routes/shared-world/result.d.ts +6 -6
  326. package/dist/routes/shared-world/result.js +29 -29
  327. package/dist/routes/shared-world/route.d.ts +11 -11
  328. package/dist/routes/shared-world/route.js +40 -39
  329. package/dist/routes/shared-world/setup.d.ts +16 -16
  330. package/dist/routes/shared-world/setup.js +23 -23
  331. package/dist/routes/shared-world/types.d.ts +55 -53
  332. package/dist/routes/shared-world/types.js +4 -4
  333. package/dist/routes/terminal/adapter.d.ts +5 -5
  334. package/dist/routes/terminal/adapter.js +9 -9
  335. package/dist/routes/terminal/bundle.d.ts +13 -13
  336. package/dist/routes/terminal/bundle.js +28 -27
  337. package/dist/routes/terminal/credentials.d.ts +8 -8
  338. package/dist/routes/terminal/credentials.js +12 -12
  339. package/dist/routes/terminal/dry-run.d.ts +4 -4
  340. package/dist/routes/terminal/dry-run.js +12 -12
  341. package/dist/routes/terminal/encoding.js +2 -2
  342. package/dist/routes/terminal/ledger.d.ts +5 -5
  343. package/dist/routes/terminal/ledger.js +14 -14
  344. package/dist/routes/terminal/lifetime.d.ts +1 -1
  345. package/dist/routes/terminal/lifetime.js +1 -1
  346. package/dist/routes/terminal/live-finish.d.ts +2 -2
  347. package/dist/routes/terminal/live-finish.js +10 -10
  348. package/dist/routes/terminal/live-sandbox.d.ts +2 -2
  349. package/dist/routes/terminal/live-sandbox.js +15 -15
  350. package/dist/routes/terminal/participant-text.js +2 -1
  351. package/dist/routes/terminal/persona.d.ts +2 -2
  352. package/dist/routes/terminal/persona.js +3 -3
  353. package/dist/routes/terminal/plan.d.ts +7 -7
  354. package/dist/routes/terminal/plan.js +15 -15
  355. package/dist/routes/terminal/recorder.js +2 -2
  356. package/dist/routes/terminal/result.d.ts +5 -5
  357. package/dist/routes/terminal/result.js +14 -14
  358. package/dist/routes/terminal/route.d.ts +9 -9
  359. package/dist/routes/terminal/route.js +19 -20
  360. package/dist/routes/terminal/sandbox.d.ts +1 -1
  361. package/dist/routes/terminal/sandbox.js +1 -1
  362. package/dist/routes/terminal/session.d.ts +5 -5
  363. package/dist/routes/terminal/session.js +14 -13
  364. package/dist/routes/terminal/token-usage.d.ts +1 -1
  365. package/dist/routes/terminal/token-usage.js +7 -8
  366. package/dist/routes/terminal/trace.d.ts +4 -4
  367. package/dist/routes/terminal/trace.js +2 -2
  368. package/dist/routes/terminal/types.d.ts +48 -50
  369. package/dist/routes/terminal/types.js +5 -5
  370. package/dist/run/bundle-shape.js +3 -3
  371. package/dist/run/bundle.d.ts +49 -49
  372. package/dist/run/bundle.js +3 -2
  373. package/dist/run/cost-summary.js +3 -3
  374. package/dist/run/costs.d.ts +23 -0
  375. package/dist/run/costs.js +79 -44
  376. package/dist/run/detail.d.ts +6 -6
  377. package/dist/run/detail.js +4 -4
  378. package/dist/run/dry-run-inputs.d.ts +1 -1
  379. package/dist/run/dry-run-inputs.js +2 -2
  380. package/dist/run/dry-run.js +20 -20
  381. package/dist/run/judge.d.ts +19 -13
  382. package/dist/run/judge.js +18 -8
  383. package/dist/run/outcomes.d.ts +1 -1
  384. package/dist/run/participant.d.ts +1 -1
  385. package/dist/run/paths.d.ts +1 -1
  386. package/dist/run/paths.js +1 -1
  387. package/dist/run/preflight-receipts.js +1 -1
  388. package/dist/run/pricing.d.ts +11 -6
  389. package/dist/run/pricing.js +22 -15
  390. package/dist/run/projection.d.ts +46 -46
  391. package/dist/run/projection.js +57 -57
  392. package/dist/run/reclaim.d.ts +1 -1
  393. package/dist/run/reclaim.js +3 -3
  394. package/dist/run/results.d.ts +5 -5
  395. package/dist/run/run-cost.d.ts +53 -0
  396. package/dist/run/run-cost.js +105 -0
  397. package/dist/run/run-index-analysis.d.ts +15 -0
  398. package/dist/run/run-index-analysis.js +77 -0
  399. package/dist/run/run-index.d.ts +19 -7
  400. package/dist/run/run-index.js +77 -64
  401. package/dist/run/run-not-found.d.ts +5 -0
  402. package/dist/run/run-not-found.js +15 -0
  403. package/dist/run/run.d.ts +6 -5
  404. package/dist/run/run.js +3 -3
  405. package/dist/run/sandbox-receipts.d.ts +2 -2
  406. package/dist/run/sandbox-receipts.js +3 -3
  407. package/dist/run/shared-world-evidence.d.ts +37 -36
  408. package/dist/run/shared-world-evidence.js +4 -4
  409. package/dist/run/shared-world-shape.d.ts +1 -1
  410. package/dist/run/shared-world-shape.js +7 -7
  411. package/dist/run/stats.d.ts +14 -8
  412. package/dist/run/stats.js +121 -63
  413. package/dist/run/status.d.ts +16 -29
  414. package/dist/run/status.js +10 -29
  415. package/dist/run/stored-runs.js +2 -1
  416. package/dist/run/stream-shape.js +1 -1
  417. package/dist/run/streams.d.ts +7 -7
  418. package/dist/run/study-provenance.d.ts +49 -0
  419. package/dist/run/study-provenance.js +66 -0
  420. package/dist/run/study-result.d.ts +11 -0
  421. package/dist/run/study-result.js +9 -0
  422. package/dist/run/text.d.ts +2 -0
  423. package/dist/run/text.js +4 -0
  424. package/dist/run-study.d.ts +112 -0
  425. package/dist/{run-lab.js → run-study.js} +52 -47
  426. package/dist/{lab → study}/adapter-extension.d.ts +15 -27
  427. package/dist/{lab → study}/adapter-extension.js +10 -45
  428. package/dist/{lab → study}/adapter-scorer-loader.d.ts +17 -17
  429. package/dist/{lab → study}/adapter-scorer-loader.js +34 -34
  430. package/dist/study/composition-rules.d.ts +7 -0
  431. package/dist/{lab → study}/composition-rules.js +53 -53
  432. package/dist/study/config.d.ts +15 -0
  433. package/dist/{lab → study}/config.js +98 -41
  434. package/dist/study/convert.d.ts +33 -0
  435. package/dist/study/convert.js +432 -0
  436. package/dist/{lab → study}/device-presets.d.ts +10 -10
  437. package/dist/{lab → study}/device-presets.js +11 -11
  438. package/dist/study/discover.d.ts +75 -0
  439. package/dist/{lab → study}/discover.js +134 -95
  440. package/dist/{lab → study}/doctor.d.ts +19 -12
  441. package/dist/{lab → study}/doctor.js +58 -20
  442. package/dist/study/field-paths.d.ts +12 -0
  443. package/dist/study/field-paths.js +126 -0
  444. package/dist/study/files.d.ts +26 -0
  445. package/dist/study/files.js +44 -0
  446. package/dist/{lab → study}/init-templates.d.ts +2 -2
  447. package/dist/study/init-templates.js +421 -0
  448. package/dist/{lab → study}/init.d.ts +2 -2
  449. package/dist/{lab → study}/init.js +46 -11
  450. package/dist/study/keys.d.ts +4 -0
  451. package/dist/{lab → study}/keys.js +46 -12
  452. package/dist/study/migrate.d.ts +48 -0
  453. package/dist/study/migrate.js +408 -0
  454. package/dist/study/owned-files.d.ts +38 -0
  455. package/dist/study/owned-files.js +182 -0
  456. package/dist/{lab → study}/parse/actors.d.ts +5 -5
  457. package/dist/{lab → study}/parse/actors.js +14 -14
  458. package/dist/study/parse/comms.d.ts +12 -0
  459. package/dist/{lab → study}/parse/comms.js +5 -5
  460. package/dist/study/parse/execution.d.ts +19 -0
  461. package/dist/{lab → study}/parse/execution.js +13 -13
  462. package/dist/study/parse/study-v3.d.ts +21 -0
  463. package/dist/study/parse/study-v3.js +259 -0
  464. package/dist/study/parse/subject-state.d.ts +30 -0
  465. package/dist/{lab → study}/parse/subject-state.js +19 -19
  466. package/dist/{lab → study}/parse/subject.d.ts +3 -3
  467. package/dist/{lab → study}/parse/subject.js +34 -34
  468. package/dist/{lab → study}/parse/values.d.ts +3 -3
  469. package/dist/{lab → study}/parse/values.js +3 -3
  470. package/dist/{lab → study}/persona-resolve.d.ts +8 -8
  471. package/dist/{lab → study}/persona-resolve.js +6 -6
  472. package/dist/{lab → study}/plan-base.d.ts +7 -7
  473. package/dist/{lab → study}/plan-base.js +1 -1
  474. package/dist/{lab → study}/plan-participants.d.ts +12 -12
  475. package/dist/{lab → study}/plan-participants.js +4 -4
  476. package/dist/{lab → study}/plan-types.d.ts +31 -31
  477. package/dist/{lab → study}/plan-types.js +2 -2
  478. package/dist/study/plan.d.ts +13 -0
  479. package/dist/study/plan.js +101 -0
  480. package/dist/study/preflight-probes.d.ts +3 -0
  481. package/dist/{lab → study}/preflight-probes.js +20 -19
  482. package/dist/study/preflight-result.d.ts +8 -0
  483. package/dist/{lab → study}/preflight-result.js +5 -4
  484. package/dist/study/preflight.d.ts +94 -0
  485. package/dist/{lab → study}/preflight.js +12 -12
  486. package/dist/{lab → study}/requirements.d.ts +3 -3
  487. package/dist/{lab → study}/requirements.js +1 -1
  488. package/dist/study/route-inputs.d.ts +28 -0
  489. package/dist/{lab → study}/route-inputs.js +1 -1
  490. package/dist/{lab → study}/routing.d.ts +39 -33
  491. package/dist/{lab → study}/routing.js +60 -29
  492. package/dist/{lab/run-lab-events.d.ts → study/run-study-events.d.ts} +10 -13
  493. package/dist/{lab/run-lab-events.js → study/run-study-events.js} +3 -9
  494. package/dist/{lab/run-lab-homes.d.ts → study/run-study-homes.d.ts} +21 -18
  495. package/dist/{lab/run-lab-homes.js → study/run-study-homes.js} +1 -1
  496. package/dist/study/run-study-options.d.ts +28 -0
  497. package/dist/{lab/run-lab-options.js → study/run-study-options.js} +29 -25
  498. package/dist/{lab/lab-deps.d.ts → study/study-deps.d.ts} +8 -13
  499. package/dist/{lab/lab-deps.js → study/study-deps.js} +2 -2
  500. package/dist/{lab → study}/summary.d.ts +16 -15
  501. package/dist/{lab → study}/summary.js +21 -21
  502. package/dist/{lab → study}/tasks.d.ts +18 -18
  503. package/dist/{lab → study}/tasks.js +14 -14
  504. package/dist/{lab → study}/types.d.ts +202 -196
  505. package/dist/{lab → study}/types.js +3 -1
  506. package/dist/{lab → study}/validation.d.ts +27 -27
  507. package/dist/{lab → study}/validation.js +53 -51
  508. package/dist/study/warnings.d.ts +12 -0
  509. package/dist/{lab → study}/warnings.js +62 -34
  510. package/dist/subject/clone.d.ts +7 -7
  511. package/dist/subject/clone.js +2 -2
  512. package/dist/subject/desktop-cli.d.ts +1 -1
  513. package/dist/subject/desktop-cli.js +2 -2
  514. package/dist/subject/local-tree-archive.d.ts +2 -2
  515. package/dist/subject/local-tree-archive.js +4 -4
  516. package/dist/subject/local-tree.d.ts +4 -4
  517. package/dist/subject/node-bootstrap.js +1 -1
  518. package/dist/subject/runtime.js +9 -10
  519. package/dist/subject/serve.d.ts +5 -5
  520. package/dist/subject/serve.js +3 -3
  521. package/dist/subject/state.d.ts +4 -4
  522. package/dist/subject/state.js +1 -1
  523. package/dist/subject/steps.d.ts +6 -6
  524. package/dist/subject/steps.js +5 -5
  525. package/dist/substrates/command-failure.d.ts +3 -3
  526. package/dist/substrates/command-failure.js +3 -3
  527. package/dist/substrates/desktop-session.js +2 -2
  528. package/dist/substrates/detached.d.ts +6 -6
  529. package/dist/substrates/detached.js +7 -7
  530. package/dist/substrates/e2b/cdp-probe.d.ts +9 -9
  531. package/dist/substrates/e2b/cdp-probe.js +14 -17
  532. package/dist/substrates/e2b/desktop-browser.d.ts +6 -6
  533. package/dist/substrates/e2b/desktop-browser.js +9 -9
  534. package/dist/substrates/e2b/desktop-cdp.d.ts +10 -10
  535. package/dist/substrates/e2b/desktop-cdp.js +9 -9
  536. package/dist/substrates/e2b/desktop-executor.d.ts +2 -2
  537. package/dist/substrates/e2b/desktop-executor.js +13 -13
  538. package/dist/substrates/e2b/desktop-geometry.d.ts +4 -4
  539. package/dist/substrates/e2b/desktop-geometry.js +8 -8
  540. package/dist/substrates/e2b/desktop-media.d.ts +4 -4
  541. package/dist/substrates/e2b/desktop-media.js +1 -1
  542. package/dist/substrates/e2b/desktop-screenshot-cleanup.d.ts +3 -3
  543. package/dist/substrates/e2b/desktop-screenshot-cleanup.js +3 -3
  544. package/dist/substrates/e2b/lifetime.js +1 -1
  545. package/dist/substrates/e2b/sandbox.d.ts +4 -4
  546. package/dist/substrates/e2b/sandbox.js +2 -2
  547. package/dist/substrates/e2b/sdk.d.ts +12 -12
  548. package/dist/substrates/e2b/sdk.js +7 -7
  549. package/dist/substrates/local/runtime-config.d.ts +4 -4
  550. package/dist/substrates/local/runtime-config.js +4 -4
  551. package/dist/tui/actions.d.ts +1 -1
  552. package/dist/tui/actions.js +13 -13
  553. package/dist/tui/contract.d.ts +23 -23
  554. package/dist/tui/contract.js +11 -11
  555. package/dist/tui/launch.d.ts +7 -7
  556. package/dist/tui/launch.js +36 -31
  557. package/dist/tui/project.js +5 -5
  558. package/dist/tui-app.js +139 -139
  559. package/dist/verify/actor.d.ts +8 -8
  560. package/dist/verify/actor.js +36 -36
  561. package/dist/verify/artifacts.d.ts +2 -2
  562. package/dist/verify/artifacts.js +3 -3
  563. package/dist/verify/context-growth.d.ts +9 -0
  564. package/dist/verify/context-growth.js +62 -0
  565. package/dist/verify/costs.d.ts +6 -6
  566. package/dist/verify/costs.js +8 -8
  567. package/dist/verify/shared-world-concurrent.d.ts +2 -2
  568. package/dist/verify/shared-world-concurrent.js +79 -76
  569. package/dist/verify/shared-world.d.ts +2 -2
  570. package/dist/verify/shared-world.js +16 -16
  571. package/dist/verify/subject.d.ts +5 -5
  572. package/dist/verify/subject.js +19 -19
  573. package/dist/verify/verify.js +94 -44
  574. package/docs/README.md +25 -32
  575. package/docs/architecture/actor-contract.md +60 -91
  576. package/docs/architecture/browser-control.md +1 -1
  577. package/docs/architecture/desktop-sessions.md +7 -7
  578. package/docs/architecture/external-public-shared-world.md +68 -65
  579. package/docs/architecture/github-feedback-loop.md +2 -5
  580. package/docs/architecture/guest-desktop.md +2 -2
  581. package/docs/architecture/observer.md +34 -34
  582. package/docs/architecture/restricted-codex-analysis.md +34 -28
  583. package/docs/architecture/serve.md +34 -37
  584. package/docs/architecture/state-driven-executor.md +55 -58
  585. package/docs/architecture/task-protocol-support.md +6 -6
  586. package/docs/architecture/terminal-product-route.md +70 -110
  587. package/docs/contracts/README.md +24 -0
  588. package/docs/contracts/adapter-admission.md +3 -3
  589. package/docs/contracts/adapter-fixtures.md +4 -7
  590. package/docs/contracts/core.md +1 -3
  591. package/docs/contracts/errors.md +62 -0
  592. package/docs/contracts/feedback.md +8 -9
  593. package/docs/contracts/policy.md +7 -12
  594. package/docs/contracts/run-bundle.md +42 -44
  595. package/docs/contracts/schemas.md +446 -368
  596. package/docs/contracts/study-analysis.md +7 -4
  597. package/docs/contracts/study-costs.md +28 -9
  598. package/docs/decisions/0001-run-bundle-is-the-source-of-truth.md +1 -1
  599. package/docs/decisions/0002-observer-is-one-self-contained-file.md +2 -2
  600. package/docs/decisions/0003-contained-paths-and-cleanup-authority.md +3 -3
  601. package/docs/principles/actor-fidelity.md +4 -4
  602. package/docs/principles/engineering.md +3 -3
  603. package/docs/principles/invariants-and-defaults.md +38 -38
  604. package/docs/principles/self-driving-harness.md +1 -5
  605. package/docs/principles/three-roles.md +1 -1
  606. package/docs/product/automatic-analysis.md +7 -7
  607. package/examples/README.md +1 -1
  608. package/examples/participant/README.md +5 -5
  609. package/examples/participant/run.mjs +11 -10
  610. package/examples/scorer/README.md +3 -3
  611. package/examples/scorer/run.mjs +11 -12
  612. package/examples/scorer/scorer.mjs +3 -3
  613. package/package.json +16 -17
  614. package/skills/humanish/SKILL.md +64 -60
  615. package/skills/humanish/agents/openai.yaml +1 -1
  616. package/AGENTS.md +0 -99
  617. package/CONTRIBUTING.md +0 -148
  618. package/dist/actors/codex/qualified-versions.d.ts +0 -72
  619. package/dist/actors/codex/qualified-versions.js +0 -132
  620. package/dist/cli/commands/lab-format.d.ts +0 -8
  621. package/dist/cli/commands/lab-route-run.d.ts +0 -19
  622. package/dist/cli/commands/lab-route-scripted.d.ts +0 -17
  623. package/dist/cli/commands/lab-route-terminal.d.ts +0 -17
  624. package/dist/cli/commands/lab-run.d.ts +0 -9
  625. package/dist/cli/commands/lab.d.ts +0 -3
  626. package/dist/cli/commands/lab.js +0 -199
  627. package/dist/cli/renamed-options.d.ts +0 -11
  628. package/dist/cli/renamed-options.js +0 -18
  629. package/dist/lab/composition-rules.d.ts +0 -7
  630. package/dist/lab/config.d.ts +0 -7
  631. package/dist/lab/discover.d.ts +0 -65
  632. package/dist/lab/init-templates.js +0 -416
  633. package/dist/lab/keys.d.ts +0 -2
  634. package/dist/lab/parse/comms.d.ts +0 -12
  635. package/dist/lab/parse/execution.d.ts +0 -19
  636. package/dist/lab/parse/subject-state.d.ts +0 -30
  637. package/dist/lab/plan.d.ts +0 -19
  638. package/dist/lab/plan.js +0 -132
  639. package/dist/lab/preflight-probes.d.ts +0 -3
  640. package/dist/lab/preflight-result.d.ts +0 -7
  641. package/dist/lab/preflight.d.ts +0 -92
  642. package/dist/lab/route-inputs.d.ts +0 -28
  643. package/dist/lab/run-lab-options.d.ts +0 -28
  644. package/dist/lab/warnings.d.ts +0 -2
  645. package/dist/observer/static.d.ts +0 -39
  646. package/dist/observer/static.js +0 -369
  647. package/dist/run-lab.d.ts +0 -112
  648. package/docs/architecture/comms-inbox.md +0 -122
  649. package/docs/architecture/desktop-recording.md +0 -80
  650. package/docs/architecture/local-browser-runtime.md +0 -178
  651. package/docs/architecture/observer-review.md +0 -144
  652. package/docs/architecture/participant-media.md +0 -90
  653. package/docs/architecture/project-layout.md +0 -62
  654. package/docs/architecture/real-email-receiving.md +0 -136
  655. package/docs/assets/humanish-drawdb-hero.png +0 -0
  656. package/docs/assets/humanish-observer-hero.png +0 -0
  657. package/docs/goals/current.md +0 -207
  658. package/docs/product/lobby-trivia-3player-external-public.md +0 -77
  659. package/docs/product/open-source-install-experience.md +0 -240
  660. package/docs/ramp/README.md +0 -295
  661. package/docs/release/open-source-readiness.md +0 -222
  662. package/docs/release/public-readiness-standard.md +0 -210
  663. package/docs/roadmap/world-class-open-source-v0.md +0 -311
  664. /package/dist/{lab → study}/init-paths.d.ts +0 -0
  665. /package/dist/{lab → study}/init-paths.js +0 -0
  666. /package/dist/{lab → study}/init-plan.d.ts +0 -0
  667. /package/dist/{lab → study}/init-plan.js +0 -0
  668. /package/dist/{lab → study}/participant-assignment.d.ts +0 -0
  669. /package/dist/{lab → study}/participant-assignment.js +0 -0
  670. /package/dist/{lab → study}/persona.d.ts +0 -0
  671. /package/dist/{lab → study}/persona.js +0 -0
@@ -1,14 +1,14 @@
1
- import type { LabActor, LabParticipantEntry, LabParticipantFocus, LabConfigParseFailure } from "../types.js";
1
+ import type { StudyActor, StudyParticipantEntry, StudyParticipantFocus, StudyParseFailure } from "../types.js";
2
2
  export declare const PARTICIPANT_ID_PATTERN: RegExp;
3
3
  export declare const PARTICIPANT_ID_MAX_CHARS = 40;
4
4
  /** The participant entries an actor declares, after roster groups expand into them. The manifest
5
5
  * spells them `actors[].lanes`. Undefined when the actor declares none. */
6
- export declare function rosterOf<Entry = LabParticipantEntry>(actor: {
6
+ export declare function rosterOf<Entry = StudyParticipantEntry>(actor: {
7
7
  readonly lanes?: readonly Entry[];
8
8
  } | undefined): readonly Entry[] | undefined;
9
9
  /** An actor's per-participant focus on the app-url route. The manifest spells it `laneFocus`. */
10
- export declare function focusOf(actor: Pick<LabActor, "laneFocus"> | undefined): LabParticipantFocus | undefined;
10
+ export declare function focusOf(actor: Pick<StudyActor, "laneFocus"> | undefined): StudyParticipantFocus | undefined;
11
11
  export declare function parseActors(raw: unknown): {
12
12
  ok: true;
13
- value: LabActor[];
14
- } | LabConfigParseFailure;
13
+ value: StudyActor[];
14
+ } | StudyParseFailure;
@@ -6,7 +6,7 @@ import { registeredComputerUseActors, registeredScriptedBrowserActors, registere
6
6
  import { invalid, posInt, str } from "./values.js";
7
7
  import { isRecord } from "../../run/type-guards.js";
8
8
  // A participant id interpolates into its evidence paths (screenshots/<id>/, actors/<id>.json), so
9
- // it must be a public-safe path token, same shape as a lab id.
9
+ // it must be a public-safe path token, same shape as a study id.
10
10
  export const PARTICIPANT_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_.-]*$/;
11
11
  export const PARTICIPANT_ID_MAX_CHARS = 40;
12
12
  const METADATA_MAX_CHARS = 80;
@@ -19,11 +19,11 @@ export function rosterOf(actor) {
19
19
  export function focusOf(actor) {
20
20
  return actor?.laneFocus;
21
21
  }
22
- // Actor ids humanish no longer registers. Rejecting them at parse keeps a lab that names one
22
+ // Actor ids humanish no longer registers. Rejecting them at parse keeps a study that names one
23
23
  // from running on a route that ignores actors[0].type, such as a this-repo dry run.
24
24
  const REMOVED_ACTOR_TYPES = new Set(["pi-agent-core", "claude-agent-sdk"]);
25
- // Only actors on a lane a route dispatches. codex-app-server is registered but declares only the
26
- // "code" lane, which no lab route runs, so naming it would lead to the same dead end.
25
+ // Only actors of a run kind a route dispatches. codex-app-server is registered but declares only
26
+ // the "code" run kind, which no study route runs, so naming it would lead to the same dead end.
27
27
  function routableActorTypes() {
28
28
  return [
29
29
  ...registeredComputerUseActors(),
@@ -33,7 +33,7 @@ function routableActorTypes() {
33
33
  }
34
34
  export function parseActors(raw) {
35
35
  if (!Array.isArray(raw) || raw.length === 0) {
36
- return invalid("Lab `actors` must be a non-empty array.");
36
+ return invalid("`actors` must be a non-empty array.");
37
37
  }
38
38
  // Multi-actor fan-out is not wired yet (only actors[0] is consumed). Fail closed rather than
39
39
  // silently ignore actors[1..]; multi-actor support lands in a later slice.
@@ -50,17 +50,17 @@ export function parseActors(raw) {
50
50
  return invalid(`actors[${index}].type is required.`);
51
51
  }
52
52
  if (REMOVED_ACTOR_TYPES.has(type)) {
53
- return invalid(`actors[${index}].type "${type}" is no longer a humanish actor. Use an actor a lab route runs (one of: ${routableActorTypes().join(", ")}). To drive a study with a signed-in Claude Code, use type: local-agent with localAgent: claude.`);
53
+ return invalid(`actors[${index}].type "${type}" is no longer a humanish actor. Use an actor a study route runs (one of: ${routableActorTypes().join(", ")}). To drive a study with a signed-in Claude Code, use type: local-agent with localAgent: claude.`);
54
54
  }
55
55
  const actor = { type };
56
56
  const count = posInt(entry.count);
57
57
  if (count !== undefined)
58
58
  actor.count = count;
59
59
  if (entry.lanes !== undefined && entry.roster !== undefined) {
60
- return invalid(`actors[${index}].lanes and actors[${index}].roster are mutually exclusive: declare participants in \`lanes\` OR in compact \`roster\` groups, not both.`);
60
+ return invalid(`Set either actors[${index}].lanes or actors[${index}].roster, not both: list participants one by one in \`lanes\`, or in groups in \`roster\`.`);
61
61
  }
62
62
  if (entry.roster !== undefined && count !== undefined) {
63
- return invalid(`actors[${index}].roster and actors[${index}].count are mutually exclusive — use compact differentiated groups OR a homogeneous count, not both.`);
63
+ return invalid(`Set either actors[${index}].roster or actors[${index}].count, not both: \`roster\` declares groups of distinct participants, and \`count\` declares identical ones.`);
64
64
  }
65
65
  if (entry.roster !== undefined && entry.laneFocus !== undefined) {
66
66
  return invalid(`actors[${index}].roster and actors[${index}].laneFocus are mutually exclusive: a roster group's instruction is each participant's steer.`);
@@ -160,7 +160,7 @@ function parseRosterGroups(raw, actorIndex) {
160
160
  return invalid(`actors[${actorIndex}].roster[${groupIndex}].id is required and must be a public-safe token matching ${PARTICIPANT_ID_PATTERN}.`);
161
161
  }
162
162
  if (!PARTICIPANT_ID_PATTERN.test(groupId) || groupId.length > PARTICIPANT_ID_MAX_CHARS - 3) {
163
- return invalid(`actors[${actorIndex}].roster[${groupIndex}].id must be a public-safe token matching ${PARTICIPANT_ID_PATTERN} and at most ${PARTICIPANT_ID_MAX_CHARS - 3} chars (generated participant ids use <id>-NN); got "${groupId}".`);
163
+ return invalid(`actors[${actorIndex}].roster[${groupIndex}].id must match ${PARTICIPANT_ID_PATTERN} and be at most ${PARTICIPANT_ID_MAX_CHARS - 3} characters, because each generated participant id adds a suffix like "-01"; "${groupId}" is not.`);
164
164
  }
165
165
  if (seenGroupIds.has(groupId)) {
166
166
  return invalid(`actors[${actorIndex}].roster group ids must be unique (duplicate "${groupId}").`);
@@ -189,7 +189,7 @@ function parseRosterGroups(raw, actorIndex) {
189
189
  * Participant ids (when declared) must be public-safe path tokens and unique; grouping metadata
190
190
  * must be public-safe tokens; a device must be a known preset name. The
191
191
  * route-scoped cross-validation (`lanes` XOR `count`/`laneFocus`, device XOR raw resolution, cap 16)
192
- * runs in parseLabConfig where the route is known.
192
+ * runs in parseStudy where the route is known.
193
193
  */
194
194
  function parseParticipantEntries(raw, actorIndex) {
195
195
  if (raw === undefined) {
@@ -272,7 +272,7 @@ function parseParticipantEntries(raw, actorIndex) {
272
272
  const entryPath = str(entry.entry);
273
273
  if (entryPath !== undefined)
274
274
  parsedEntry.entry = entryPath;
275
- // `host` marks the designated host seat on the external-public shared-world route; the
275
+ // `host` marks the designated host on the external-public shared-world route; the
276
276
  // exactly-one-host check runs in externalPublicSharedWorldValidationReason (route context).
277
277
  if (entry.host !== undefined) {
278
278
  if (typeof entry.host !== "boolean") {
@@ -287,7 +287,7 @@ function parseParticipantEntries(raw, actorIndex) {
287
287
  }
288
288
  /**
289
289
  * The researcher's protocol. Each task carries what the participant is asked to do and, optionally,
290
- * the observation that proves it happened — reusing `stopWhen`, so a criterion is exactly as
290
+ * the observation that proves it happened, reusing `stopWhen`, so a criterion is exactly as
291
291
  * expressive as a stop condition and an author who knows one knows the other.
292
292
  *
293
293
  * A task with no `success` is allowed on purpose: some things you ask a participant to do (think
@@ -314,7 +314,7 @@ function parseTasks(raw, field) {
314
314
  seen.add(id);
315
315
  const goal = str(entry.goal);
316
316
  if (goal === undefined) {
317
- return invalid(`\`${field}[${index}].goal\` is required — what the PARTICIPANT is asked to do, in their language.`);
317
+ return invalid(`\`${field}[${index}].goal\` is required: what the participant is asked to do, in their own words.`);
318
318
  }
319
319
  const successResult = parseStopWhen(entry.success, `${field}[${index}].success`);
320
320
  if (!successResult.ok)
@@ -327,7 +327,7 @@ function parseTasks(raw, field) {
327
327
  }
328
328
  return { ok: true, value: tasks };
329
329
  }
330
- /** The bounds a dwell window (#510) must sit inside: at least one frame, at most an hour. */
330
+ /** The bounds a dwell window must sit inside: at least one frame, at most an hour. */
331
331
  const DWELL_MIN_MS = 1_000;
332
332
  const DWELL_MAX_MS = 3_600_000;
333
333
  const DWELL_DEFAULT_EVERY_MS = 10_000;
@@ -0,0 +1,12 @@
1
+ import type { StudyComms, StudyCommsEmail, StudyCommsRecipient, StudyParseFailure } from "../types.js";
2
+ export declare function parseComms(raw: unknown): {
3
+ ok: true;
4
+ value: StudyComms | undefined;
5
+ } | StudyParseFailure;
6
+ /** The participant a declared inbox recipient belongs to. The manifest spells it `lane`. */
7
+ export declare function recipientParticipantId(recipient: StudyCommsRecipient): string;
8
+ /** The declared recipients that name an address, in declaration order, by participant id. */
9
+ export declare function addressedRecipients(email: Pick<StudyCommsEmail, "recipients">): {
10
+ participantId: string;
11
+ address: string;
12
+ }[];
@@ -30,8 +30,8 @@ function parseCommsEmail(raw) {
30
30
  if (raw.kind !== undefined && raw.kind !== "fake") {
31
31
  return invalid("`comms.email.kind` must be `fake`.");
32
32
  }
33
- // external ingress (#328): the ADOPTER runs the catch, so there is no subject env for humanish to
34
- // inject and `injectEnv` becomes meaningless rather than merely unused — the operator points their
33
+ // external ingress: the adopter runs the catch, so there is no subject env for humanish to
34
+ // inject and `injectEnv` has nothing to name: the operator points their
35
35
  // own app at their own catch. Parse it first so the injectEnv requirement can key off it.
36
36
  const external = parseExternalCatch(raw.external);
37
37
  if (!external.ok)
@@ -127,7 +127,7 @@ function parseExternalCatch(raw) {
127
127
  return invalid("`comms.email.external` must be a mapping.");
128
128
  const catchBaseUrl = str(raw.catchBaseUrl);
129
129
  if (catchBaseUrl === undefined) {
130
- return invalid("`comms.email.external.catchBaseUrl` is required — the base URL of the catch YOU run (humanish reads its GET /deliveries and your app POSTs its sends to it).");
130
+ return invalid("`comms.email.external.catchBaseUrl` is required: the base URL of the catch you run. Your app sends its mail to it, and humanish reads GET /deliveries from it.");
131
131
  }
132
132
  for (const [field, value] of [
133
133
  ["catchBaseUrl", catchBaseUrl],
@@ -148,7 +148,7 @@ function parseExternalCatch(raw) {
148
148
  }
149
149
  const authTokenEnv = str(raw.authTokenEnv);
150
150
  if (authTokenEnv !== undefined && !/^[A-Za-z_][A-Za-z0-9_]*$/.test(authTokenEnv)) {
151
- return invalid(`\`comms.email.external.authTokenEnv\` must be a valid env var NAME (got "${authTokenEnv}"); the value is read at runtime and never persisted.`);
151
+ return invalid(`\`comms.email.external.authTokenEnv\` must be an environment variable name, and "${authTokenEnv}" is not one. Its value is read when the run starts and never stored.`);
152
152
  }
153
153
  const inboxBaseUrl = str(raw.inboxBaseUrl);
154
154
  return {
@@ -175,7 +175,7 @@ function parseSmtp(raw) {
175
175
  const hostEnv = envName(raw.hostEnv, "hostEnv");
176
176
  const portEnv = envName(raw.portEnv, "portEnv");
177
177
  if (hostEnv === undefined || portEnv === undefined) {
178
- return invalid("`comms.email.smtp` needs both `hostEnv` and `portEnv` — the subject-env vars your app reads for its SMTP host and port. The harness sets them to its own loopback listener.");
178
+ return invalid("`comms.email.smtp` needs both `hostEnv` and `portEnv`: the environment variables your app reads for its SMTP host and port. humanish sets them to its own loopback listener.");
179
179
  }
180
180
  if (hostEnv.startsWith("__invalid__") || portEnv.startsWith("__invalid__")) {
181
181
  return invalid("`comms.email.smtp.hostEnv` and `portEnv` must be valid env var names.");
@@ -0,0 +1,19 @@
1
+ import type { StudyParseFailure, StudyDefaults, StudyExecution, StudyPolicies, StudyReview, StudyScenario } from "../types.js";
2
+ export declare function parseExecution(raw: unknown): {
3
+ ok: true;
4
+ value: StudyExecution | undefined;
5
+ } | StudyParseFailure;
6
+ export declare function parsePersonas(raw: unknown): Record<string, unknown>[] | undefined;
7
+ export declare function parseScenario(raw: unknown): {
8
+ ok: true;
9
+ value: StudyScenario | undefined;
10
+ } | StudyParseFailure;
11
+ export declare function parsePolicies(raw: unknown): {
12
+ ok: true;
13
+ value: StudyPolicies | undefined;
14
+ } | StudyParseFailure;
15
+ export declare function parseReview(raw: unknown): {
16
+ ok: true;
17
+ value: StudyReview | undefined;
18
+ } | StudyParseFailure;
19
+ export declare function parseDefaults(raw: unknown): StudyDefaults | undefined;
@@ -33,7 +33,7 @@ export function parseExecution(raw) {
33
33
  }
34
34
  if (desktopResult.value)
35
35
  execution.desktop = desktopResult.value;
36
- // Reuse the terminal route'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
37
37
  // error, never silently dropped (a cap that silently does nothing would be a safety lie).
38
38
  const capsResult = parseCaps(raw.caps);
39
39
  if (!capsResult.ok) {
@@ -90,9 +90,9 @@ function parseTerminal(raw) {
90
90
  const transport = str(raw.transport);
91
91
  if (transport !== "exec-stream") {
92
92
  // "pty" is deliberately rejected: stdin is disabled, so the capture is a non-interactive
93
- // exec stream — an interactive-PTY label would overstate the mechanism (invariant 6 + the
94
- // goal packet's PTY ruling). True duplex PTY does not ship.
95
- return invalid("`execution.terminal.transport` must be exec-stream — captured non-interactive exec output (stdin disabled) is not an interactive PTY; true duplex PTY transport is not supported.");
93
+ // exec stream, and an interactive-PTY label would overstate the mechanism. True duplex PTY
94
+ // does not ship.
95
+ return invalid("`execution.terminal.transport` must be exec-stream: the route captures the agent's output with stdin closed, and an interactive terminal (pty) is not supported.");
96
96
  }
97
97
  terminal.transport = transport;
98
98
  }
@@ -103,9 +103,9 @@ function parseTerminal(raw) {
103
103
  }
104
104
  if (stdin === "sent") {
105
105
  // Assisted input is forbidden until the interventions ledger + comparability flag + verify
106
- // check exist (safety contract item 7) — shipping it now would let an assisted run pose as
107
- // autonomous green proof.
108
- return invalid("`execution.terminal.stdin: sent` (assisted input) is not supported — the current route cannot capture assisted input with a non-comparable marker. stdin is disabled by default.");
106
+ // check exist (the terminal route's stdin rule); shipping it now would let an assisted run
107
+ // pose as autonomous green proof.
108
+ return invalid("`execution.terminal.stdin: sent` (assisted input) is not supported: the route cannot yet mark a run that received typed help as different from an unassisted one. Remove the setting; stdin stays closed.");
109
109
  }
110
110
  terminal.stdin = stdin;
111
111
  }
@@ -153,13 +153,13 @@ function parseDesktop(raw) {
153
153
  }
154
154
  desktop.browser = browser;
155
155
  }
156
- // A custom E2B desktop template NAME or ID. Trimmed non-empty when present; deliberately NOT
157
- // allowlisted (any string is a valid template name/id — over-restricting would reject real
156
+ // A custom E2B desktop template name or ID. Trimmed non-empty when present; deliberately not
157
+ // allowlisted (any string is a valid template name/id; over-restricting would reject real
158
158
  // adopter images). An explicitly-set but blank/whitespace value is a mistake, not a template.
159
159
  if (raw.template !== undefined) {
160
160
  const template = str(raw.template);
161
161
  if (template === undefined) {
162
- return invalid("`execution.desktop.template` must be a non-empty E2B desktop template NAME or ID when set (any string is accepted; there is no allowlist).");
162
+ return invalid("`execution.desktop.template` must be a non-empty E2B desktop template name or ID when it is set. Any name is accepted.");
163
163
  }
164
164
  desktop.template = template;
165
165
  }
@@ -247,8 +247,8 @@ export function parseScenario(raw) {
247
247
  }
248
248
  /**
249
249
  * Parse `scenario.caps`. Returns a parse failure on a malformed value rather than silently
250
- * dropping a budget declaration (a cap that silently does nothing would be a safety lie —
251
- * invariant 6). Each cap must be a non-negative finite number.
250
+ * dropping a budget declaration (a cap that silently does nothing would claim protection it
251
+ * does not give). Each cap must be a non-negative finite number.
252
252
  */
253
253
  function parseCaps(raw) {
254
254
  if (raw === undefined) {
@@ -339,7 +339,7 @@ function parseReviewScorer(raw) {
339
339
  return invalid("`review.scorer` must be a mapping with a `ref` path (e.g. { ref: scorers/product.mjs }).");
340
340
  const unknownKeys = Object.keys(raw).filter((key) => key !== "ref");
341
341
  if (unknownKeys.length > 0) {
342
- return invalid(`Unknown \`review.scorer\` field(s): ${unknownKeys.join(", ")}. review.scorer accepts only \`ref\` (a repo-relative scorer-module path).`);
342
+ return invalid(`\`review.scorer\` accepts only \`ref\` (a scorer module path relative to the project), and these fields are unknown: ${unknownKeys.join(", ")}.`);
343
343
  }
344
344
  const ref = str(raw.ref);
345
345
  if (!ref)
@@ -0,0 +1,21 @@
1
+ import type { StudyRoute } from "../routing.js";
2
+ import { type StudyParseFailure } from "../types.js";
3
+ export interface StudyDocument {
4
+ readonly route: StudyRoute;
5
+ /** The same document in the humanish.lab.v2 spelling. */
6
+ readonly v2: Record<string, unknown>;
7
+ /** For each expanded participant, the index of the `participants` entry it came from. */
8
+ readonly participantSource: readonly number[];
9
+ }
10
+ type Parsed<T> = {
11
+ ok: true;
12
+ value: T;
13
+ } | StudyParseFailure;
14
+ /** Rewrite a humanish.study.v3 document into humanish.lab.v2, or refuse it. */
15
+ export declare function studyToV2(raw: Record<string, unknown>): Parsed<StudyDocument>;
16
+ /**
17
+ * Spell a v2 parser message the way the study file is written: `actors[0].lanes[3]` becomes the
18
+ * `participants` entry it came from, `execution.caps` and `scenario.caps` become `caps`, and so on.
19
+ */
20
+ export declare function studySpelling(message: string, route: StudyRoute, participantSource: readonly number[]): string;
21
+ export {};
@@ -0,0 +1,259 @@
1
+ // humanish.study.v3, the study format. A study declares its route, one `actor`, its `participants`
2
+ // and one `caps` block. studyToV2 rewrites a v3 document into the v2 spelling, so both formats run
3
+ // through one parser and parse into the same StudyConfig. parseStudy then checks the declared
4
+ // route against the one the config takes, and refuses any field that route does not read.
5
+ import { isRecord } from "../../run/type-guards.js";
6
+ import { findUnknownStudyKey } from "../keys.js";
7
+ import { V2_SCHEMA } from "../types.js";
8
+ import { PARTICIPANT_ID_MAX_CHARS, PARTICIPANT_ID_PATTERN } from "./actors.js";
9
+ import { invalid, posInt } from "./values.js";
10
+ // The values `route:` takes: every route routeOf can return.
11
+ const ROUTES = [
12
+ "preview",
13
+ "computer-use",
14
+ "shared-world",
15
+ "terminal",
16
+ "scripted",
17
+ ];
18
+ // The caps keys each route reads. Computer use and shared world stop on estimated model spend;
19
+ // the terminal route checks its spend ledger, product jobs and the command deadline.
20
+ const CAPS_KEYS = {
21
+ preview: [],
22
+ "computer-use": ["maxUsd", "maxTotalUsd"],
23
+ "shared-world": ["maxUsd", "maxTotalUsd"],
24
+ terminal: ["maxUsd", "maxJobs", "maxMinutes"],
25
+ scripted: [],
26
+ };
27
+ /** Rewrite a humanish.study.v3 document into humanish.lab.v2, or refuse it. */
28
+ export function studyToV2(raw) {
29
+ const moved = movedKeyReason(raw);
30
+ if (moved)
31
+ return invalid(moved);
32
+ const unknownKey = findUnknownStudyKey(raw);
33
+ if (unknownKey)
34
+ return invalid(unknownKey);
35
+ const route = ROUTES.find((name) => name === raw.route);
36
+ if (route === undefined) {
37
+ return invalid(`A study needs \`route:\`, one of ${ROUTES.join(", ")}.`);
38
+ }
39
+ if (raw.mode !== undefined && raw.mode !== "dry-run" && raw.mode !== "live") {
40
+ return invalid("`mode` must be `dry-run` or `live`. Leave it out for a dry run.");
41
+ }
42
+ if (!isRecord(raw.actor)) {
43
+ return invalid("A study needs `actor:`, an object with at least `type`.");
44
+ }
45
+ if (raw.scenario !== undefined && typeof raw.scenario !== "string") {
46
+ return invalid("`scenario` is a scenario id or a path to one, as a string.");
47
+ }
48
+ const participants = participantsOf(route, raw.participants);
49
+ if (!participants.ok)
50
+ return participants;
51
+ const surfaces = surfaceCount(route, raw.surfaces);
52
+ if (!surfaces.ok)
53
+ return surfaces;
54
+ const capsReason = capsKeysReason(route, raw.caps);
55
+ if (capsReason)
56
+ return invalid(capsReason);
57
+ // The v2 inert-field table does not list this one, but the terminal planner never reads it.
58
+ if (route === "terminal" && isRecord(raw.execution) && raw.execution.timeoutMs !== undefined) {
59
+ return invalid("route: terminal does not read `execution.timeoutMs`: `caps.maxMinutes` is the command deadline. Remove it.");
60
+ }
61
+ const { count, instruction, entries, source } = participants.value;
62
+ // The v2 actor keys that hold participants.
63
+ const v2Participants = [
64
+ ["count", count ?? surfaces.value],
65
+ ["laneFocus", instruction === undefined ? undefined : { instruction }],
66
+ ["lanes", entries],
67
+ ];
68
+ const actor = { ...raw.actor };
69
+ for (const [key, value] of v2Participants)
70
+ if (value !== undefined)
71
+ actor[key] = value;
72
+ const caps = raw.caps;
73
+ const scenario = {};
74
+ if (raw.scenario !== undefined)
75
+ scenario.ref = raw.scenario;
76
+ if (raw.mode !== undefined)
77
+ scenario.mode = raw.mode;
78
+ if (caps !== undefined && route === "terminal")
79
+ scenario.caps = caps;
80
+ const execution = caps !== undefined && route !== "terminal"
81
+ ? withKey(raw.execution, "caps", caps)
82
+ : raw.execution;
83
+ const subject = route === "shared-world" ? withKey(raw.subject, "topology", "shared-world") : raw.subject;
84
+ const v2 = { schema: V2_SCHEMA };
85
+ const fields = [
86
+ ["id", raw.id],
87
+ ["title", raw.title],
88
+ ["description", raw.description],
89
+ ["subject", subject],
90
+ ["actors", [actor]],
91
+ ["execution", execution],
92
+ ["scenario", Object.keys(scenario).length > 0 ? scenario : undefined],
93
+ ["policies", raw.policies],
94
+ ["review", raw.review],
95
+ ["defaults", raw.defaults],
96
+ ["comms", raw.comms],
97
+ ];
98
+ for (const [key, value] of fields)
99
+ if (value !== undefined)
100
+ v2[key] = value;
101
+ return { ok: true, value: { route, v2, participantSource: source } };
102
+ }
103
+ /**
104
+ * Spell a v2 parser message the way the study file is written: `actors[0].lanes[3]` becomes the
105
+ * `participants` entry it came from, `execution.caps` and `scenario.caps` become `caps`, and so on.
106
+ */
107
+ export function studySpelling(message, route, participantSource) {
108
+ return (message
109
+ .replaceAll(/actors\[0\]\.lanes\[(\d+)\]/g, (_match, index) => `participants[${participantSource[Number(index)] ?? index}]`)
110
+ // "an `actors[0].lanes` roster" reads "a `participants` list" in a v3 file.
111
+ .replaceAll(/\ban `actors\[0\]\.(lanes|roster)` roster\b/g, "a `participants` list")
112
+ .replaceAll(/actors\[0\]\.(lanes|roster)\b/g, "participants")
113
+ .replaceAll(/actors\[0\]\.laneFocus(\.instruction)?/g, "participants.instruction")
114
+ .replaceAll(/actors\[0\]\.count\b/g, route === "scripted" ? "surfaces" : "participants")
115
+ .replaceAll("actors[0]", "actor")
116
+ .replaceAll(/\b(execution|scenario)\.caps\b/g, "caps")
117
+ .replaceAll(/\bscenario\.mode\b/g, "mode")
118
+ .replaceAll(/\bscenario\.ref\b/g, "scenario")
119
+ .replaceAll("subject.topology: shared-world", "route: shared-world"));
120
+ }
121
+ // The v2 keys a hand-converted file is most likely to keep, each with where it went.
122
+ function movedKeyReason(raw) {
123
+ if (raw.actors !== undefined) {
124
+ return "A study has one `actor:` object, and its participants go in `participants:`. `actors:` is the humanish.lab.v2 spelling.";
125
+ }
126
+ if (isRecord(raw.actor)) {
127
+ for (const key of ["count", "lanes", "roster", "laneFocus"]) {
128
+ if (raw.actor[key] !== undefined) {
129
+ return `\`actor.${key}\` moved to \`participants:\`: a count, \`{ count, instruction }\`, or a list of participants.`;
130
+ }
131
+ }
132
+ }
133
+ if (isRecord(raw.subject) && raw.subject.topology !== undefined) {
134
+ return "`subject.topology` moved to `route:`. A shared world is `route: shared-world`.";
135
+ }
136
+ if (isRecord(raw.execution) && raw.execution.caps !== undefined) {
137
+ return "`execution.caps` moved to a top-level `caps:` block.";
138
+ }
139
+ return undefined;
140
+ }
141
+ function participantsOf(route, raw) {
142
+ if (raw === undefined) {
143
+ if (route === "shared-world")
144
+ return invalid(listOnly(route));
145
+ return { ok: true, value: { source: [] } };
146
+ }
147
+ switch (route) {
148
+ case "scripted":
149
+ return invalid("route: scripted takes no `participants`. It replays committed steps on `surfaces`: [desktop] or [desktop, mobile].");
150
+ case "terminal":
151
+ return invalid("route: terminal takes no `participants`. It runs one agent.");
152
+ case "preview":
153
+ return typeof raw === "number"
154
+ ? countOf(raw)
155
+ : invalid("route: preview takes `participants` as a count, such as `participants: 4`.");
156
+ case "shared-world":
157
+ return Array.isArray(raw) ? entriesOf(raw) : invalid(listOnly(route));
158
+ case "computer-use":
159
+ if (typeof raw === "number")
160
+ return countOf(raw);
161
+ if (Array.isArray(raw))
162
+ return entriesOf(raw);
163
+ return homogeneousOf(raw);
164
+ }
165
+ }
166
+ function listOnly(route) {
167
+ return `route: ${route} takes \`participants\` as a list of at least two entries, one per participant in the shared world.`;
168
+ }
169
+ function countOf(raw) {
170
+ const count = posInt(raw);
171
+ if (count === undefined)
172
+ return invalid("`participants` must be a positive integer.");
173
+ return { ok: true, value: { count, source: [] } };
174
+ }
175
+ // `{ count, instruction }`: participants that share one instruction. The count stays a count, so
176
+ // `--count` still overrides it.
177
+ function homogeneousOf(raw) {
178
+ if (!isRecord(raw)) {
179
+ return invalid("`participants` is a count, an object `{ count, instruction }`, or a list of participants.");
180
+ }
181
+ const extra = Object.keys(raw).filter((key) => key !== "count" && key !== "instruction");
182
+ if (extra.length > 0) {
183
+ return invalid(`\`participants\` as an object takes only \`count\` and \`instruction\`, and every participant gets them. To set ${extra.map((key) => `\`${key}\``).join(", ")}, write participants as a list.`);
184
+ }
185
+ const participants = { source: [] };
186
+ if (raw.count !== undefined) {
187
+ const count = typeof raw.count === "number" ? posInt(raw.count) : undefined;
188
+ if (count === undefined)
189
+ return invalid("`participants.count` must be a positive integer.");
190
+ participants.count = count;
191
+ }
192
+ if (raw.instruction !== undefined)
193
+ participants.instruction = raw.instruction;
194
+ return { ok: true, value: participants };
195
+ }
196
+ // A list entry with `count: n` is a group: n participants `<id>-01` to `<id>-NN`, even when n is 1.
197
+ // The v2 parser then checks every entry and the expanded ids for collisions.
198
+ function entriesOf(raw) {
199
+ const entries = [];
200
+ const source = [];
201
+ for (const [index, entry] of raw.entries()) {
202
+ if (!isRecord(entry) || entry.count === undefined) {
203
+ entries.push(entry);
204
+ source.push(index);
205
+ continue;
206
+ }
207
+ const count = typeof entry.count === "number" ? posInt(entry.count) : undefined;
208
+ if (count === undefined) {
209
+ return invalid(`participants[${index}].count must be a positive integer.`);
210
+ }
211
+ const id = entry.id;
212
+ if (typeof id !== "string" ||
213
+ !PARTICIPANT_ID_PATTERN.test(id) ||
214
+ id.length > PARTICIPANT_ID_MAX_CHARS - 3) {
215
+ return invalid(`participants[${index}] has a count, so it needs an \`id\` matching ${PARTICIPANT_ID_PATTERN} of at most ${PARTICIPANT_ID_MAX_CHARS - 3} characters. Its participants are <id>-01 to <id>-${String(count).padStart(2, "0")}.`);
216
+ }
217
+ const shared = { ...entry };
218
+ delete shared.count;
219
+ for (let n = 1; n <= count; n += 1) {
220
+ entries.push({ ...shared, id: `${id}-${String(n).padStart(2, "0")}` });
221
+ source.push(index);
222
+ }
223
+ }
224
+ return { ok: true, value: { entries, source } };
225
+ }
226
+ // Scripted surfaces become the v2 count: 1 is desktop, 2 adds mobile.
227
+ function surfaceCount(route, raw) {
228
+ if (raw === undefined)
229
+ return { ok: true, value: undefined };
230
+ if (route !== "scripted") {
231
+ return invalid(`\`surfaces\` belongs to route: scripted. This study's route is ${route}; remove it.`);
232
+ }
233
+ if (Array.isArray(raw) && raw.length === 1 && raw[0] === "desktop")
234
+ return { ok: true, value: 1 };
235
+ if (Array.isArray(raw) && raw.length === 2 && raw[0] === "desktop" && raw[1] === "mobile") {
236
+ return { ok: true, value: 2 };
237
+ }
238
+ return invalid("`surfaces` must be [desktop] or [desktop, mobile].");
239
+ }
240
+ function capsKeysReason(route, raw) {
241
+ if (raw === undefined)
242
+ return undefined;
243
+ const read = CAPS_KEYS[route];
244
+ if (read.length === 0)
245
+ return `route: ${route} reads no \`caps\`; remove the block.`;
246
+ if (!isRecord(raw))
247
+ return undefined;
248
+ const unread = Object.keys(raw).filter((key) => !read.includes(key));
249
+ if (unread.length === 0)
250
+ return undefined;
251
+ return `route: ${route} reads only ${read.map((key) => `caps.${key}`).join(", ")}. Remove ${unread.map((key) => `caps.${key}`).join(", ")}.`;
252
+ }
253
+ // Add one key to a section. A section that is not an object passes through unchanged, so the v2
254
+ // parser refuses it with its own message.
255
+ function withKey(section, key, value) {
256
+ if (section === undefined)
257
+ return { [key]: value };
258
+ return isRecord(section) ? { ...section, [key]: value } : section;
259
+ }
@@ -0,0 +1,30 @@
1
+ import type { StudyParseFailure, StudySubjectState } from "../types.js";
2
+ /**
3
+ * Literal non-secret subject env. Real apps need configuration before they will boot: a public base
4
+ * URL, a transport selector, a feature flag, and none of that is secret. Routing it through
5
+ * `subject.env` would force an adopter to carry a private env file just to reproduce a public study.
6
+ *
7
+ * These values are recorded in evidence (they are part of how the subject was configured), so a
8
+ * value that looks like a credential is refused here rather than committed to a public repo.
9
+ */
10
+ export declare function parseEnvValues(raw: unknown): {
11
+ ok: true;
12
+ value?: Record<string, string>;
13
+ } | StudyParseFailure;
14
+ /**
15
+ * Structural parse of `subject.state` into a candidate StudySubjectState. Deliberately keeps
16
+ * unrecognized `when`/`timeoutMs` values in the candidate (instead of silently dropping
17
+ * them) so subjectStateInvalidReason rejects them: a state declaration that silently does
18
+ * less than it says would claim more than its mechanism does.
19
+ */
20
+ export declare function parseState(raw: unknown): {
21
+ ok: true;
22
+ value: StudySubjectState | undefined;
23
+ } | StudyParseFailure;
24
+ /**
25
+ * Semantic validation for `subject.state`, shared by parseStudy and the engine
26
+ * (the route re-enforces it on configs that arrive through runStudyWith). Returns
27
+ * the failure message, or null when the declaration is valid. Reads the candidate
28
+ * defensively: library callers can hand the engine arbitrarily-shaped objects.
29
+ */
30
+ export declare function subjectStateInvalidReason(state: StudySubjectState, env: readonly string[] | undefined): string | null;