humanish 0.106.1 → 0.108.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 (668) hide show
  1. package/ARCHITECTURE.md +38 -32
  2. package/CHANGELOG.md +28 -0
  3. package/CONTEXT.md +51 -41
  4. package/README.md +152 -174
  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-client.js +2 -1
  14. package/dist/actors/codex/app-server-trace.d.ts +0 -1
  15. package/dist/actors/codex/app-server-trace.js +1 -3
  16. package/dist/actors/codex/app-server-ui.js +1 -1
  17. package/dist/actors/codex/app-server.js +2 -1
  18. package/dist/actors/codex/codex-admission.d.ts +91 -0
  19. package/dist/actors/codex/codex-admission.js +150 -0
  20. package/dist/actors/codex/protocol-compat.d.ts +27 -0
  21. package/dist/actors/codex/protocol-compat.js +357 -0
  22. package/dist/actors/codex/protocol-contract.d.ts +61 -0
  23. package/dist/actors/codex/protocol-contract.js +471 -0
  24. package/dist/actors/codex/restricted-executable.d.ts +28 -4
  25. package/dist/actors/codex/restricted-executable.js +104 -29
  26. package/dist/actors/codex/restricted-launch.d.ts +9 -2
  27. package/dist/actors/codex/restricted-launch.js +43 -4
  28. package/dist/actors/codex/restricted-notifications.d.ts +101 -0
  29. package/dist/actors/codex/restricted-notifications.js +277 -0
  30. package/dist/actors/codex/restricted-participant.d.ts +30 -0
  31. package/dist/actors/codex/restricted-participant.js +66 -6
  32. package/dist/actors/codex/restricted-policy.d.ts +11 -3
  33. package/dist/actors/codex/restricted-policy.js +2 -2
  34. package/dist/actors/codex/restricted-session.d.ts +16 -3
  35. package/dist/actors/codex/restricted-session.js +60 -22
  36. package/dist/actors/codex/restricted-transport.d.ts +29 -0
  37. package/dist/actors/codex/restricted-transport.js +139 -27
  38. package/dist/actors/codex/restricted-turn.d.ts +19 -4
  39. package/dist/actors/codex/restricted-turn.js +43 -30
  40. package/dist/actors/computer-use/actor.d.ts +17 -17
  41. package/dist/actors/computer-use/actor.js +6 -6
  42. package/dist/actors/computer-use/admission-limit.d.ts +1 -1
  43. package/dist/actors/computer-use/admission-limit.js +1 -1
  44. package/dist/actors/computer-use/loop/actions.d.ts +3 -3
  45. package/dist/actors/computer-use/loop/actions.js +10 -10
  46. package/dist/actors/computer-use/loop/backstop.d.ts +3 -3
  47. package/dist/actors/computer-use/loop/backstop.js +15 -13
  48. package/dist/actors/computer-use/loop/ending.d.ts +7 -7
  49. package/dist/actors/computer-use/loop/ending.js +9 -9
  50. package/dist/actors/computer-use/loop/observation.d.ts +2 -2
  51. package/dist/actors/computer-use/loop/observation.js +9 -9
  52. package/dist/actors/computer-use/loop/provider-call.d.ts +1 -1
  53. package/dist/actors/computer-use/loop/provider-call.js +1 -1
  54. package/dist/actors/computer-use/loop/session.js +3 -3
  55. package/dist/actors/computer-use/loop/spend.d.ts +2 -2
  56. package/dist/actors/computer-use/loop/spend.js +4 -4
  57. package/dist/actors/computer-use/loop/trace.js +11 -10
  58. package/dist/actors/computer-use/loop/types.d.ts +37 -26
  59. package/dist/actors/computer-use/loop/usage.js +2 -2
  60. package/dist/actors/computer-use/loop.js +4 -4
  61. package/dist/actors/computer-use/openai-context.d.ts +37 -0
  62. package/dist/actors/computer-use/openai-context.js +214 -0
  63. package/dist/actors/computer-use/openai-provider.d.ts +12 -9
  64. package/dist/actors/computer-use/openai-provider.js +47 -36
  65. package/dist/actors/computer-use/openai-requests.d.ts +36 -10
  66. package/dist/actors/computer-use/openai-requests.js +82 -15
  67. package/dist/actors/computer-use/openai-wire.d.ts +19 -7
  68. package/dist/actors/computer-use/openai-wire.js +36 -34
  69. package/dist/actors/contract.d.ts +86 -49
  70. package/dist/actors/contract.js +29 -23
  71. package/dist/actors/local-agent/claude-session.d.ts +1 -1
  72. package/dist/actors/local-agent/claude-session.js +10 -9
  73. package/dist/actors/local-agent/cli.d.ts +8 -6
  74. package/dist/actors/local-agent/cli.js +24 -28
  75. package/dist/actors/local-agent/readiness.d.ts +2 -2
  76. package/dist/actors/local-agent/readiness.js +4 -4
  77. package/dist/actors/reasoning-effort.js +6 -6
  78. package/dist/actors/registry.d.ts +10 -10
  79. package/dist/actors/registry.js +18 -17
  80. package/dist/actors/scripted-browser/actor.d.ts +8 -8
  81. package/dist/actors/scripted-browser/actor.js +16 -16
  82. package/dist/actors/scripted-browser/browser-command.js +1 -1
  83. package/dist/actors/scripted-browser/journey.js +15 -4
  84. package/dist/actors/scripted-browser/steps.js +19 -1
  85. package/dist/actors/scripted-browser/types.d.ts +12 -2
  86. package/dist/actors/stop-conditions.d.ts +1 -1
  87. package/dist/actors/stop-conditions.js +1 -3
  88. package/dist/analysis/automatic-completion.d.ts +13 -8
  89. package/dist/analysis/automatic-completion.js +14 -8
  90. package/dist/analysis/automatic-config.d.ts +1 -1
  91. package/dist/analysis/automatic-config.js +2 -2
  92. package/dist/analysis/automatic.d.ts +10 -4
  93. package/dist/analysis/automatic.js +17 -13
  94. package/dist/analysis/codex-config.d.ts +6 -2
  95. package/dist/analysis/codex-config.js +22 -14
  96. package/dist/analysis/diagnostics.d.ts +23 -0
  97. package/dist/analysis/diagnostics.js +112 -0
  98. package/dist/analysis/evidence-sources.js +2 -2
  99. package/dist/analysis/evidence.js +6 -6
  100. package/dist/analysis/execute.d.ts +7 -1
  101. package/dist/analysis/execute.js +39 -12
  102. package/dist/analysis/job.d.ts +8 -5
  103. package/dist/analysis/job.js +15 -14
  104. package/dist/analysis/load.d.ts +1 -1
  105. package/dist/analysis/load.js +5 -5
  106. package/dist/analysis/provider.d.ts +9 -1
  107. package/dist/analysis/restricted-codex.d.ts +2 -0
  108. package/dist/analysis/restricted-codex.js +10 -1
  109. package/dist/analysis/service.d.ts +2 -0
  110. package/dist/analysis/service.js +35 -16
  111. package/dist/analysis/sharing.d.ts +2 -2
  112. package/dist/analysis/sharing.js +5 -5
  113. package/dist/analysis/store-executions.d.ts +5 -5
  114. package/dist/analysis/store-executions.js +12 -12
  115. package/dist/analysis/store.d.ts +4 -4
  116. package/dist/analysis/store.js +13 -13
  117. package/dist/cli/commands/analysis-signals.d.ts +2 -1
  118. package/dist/cli/commands/analysis-signals.js +4 -1
  119. package/dist/cli/commands/analyze.js +23 -18
  120. package/dist/cli/commands/codex.js +9 -9
  121. package/dist/cli/commands/comms.js +62 -34
  122. package/dist/cli/commands/feedback.js +20 -21
  123. package/dist/cli/commands/lab-format.d.ts +5 -4
  124. package/dist/cli/commands/lab-format.js +35 -23
  125. package/dist/cli/commands/lab-route-computer-use.d.ts +0 -2
  126. package/dist/cli/commands/lab-route-computer-use.js +32 -21
  127. package/dist/cli/commands/lab-route-preview.d.ts +1 -3
  128. package/dist/cli/commands/lab-route-preview.js +7 -7
  129. package/dist/cli/commands/lab-route-run.d.ts +6 -4
  130. package/dist/cli/commands/lab-route-run.js +6 -2
  131. package/dist/cli/commands/lab-route-scripted.d.ts +0 -2
  132. package/dist/cli/commands/lab-route-scripted.js +0 -1
  133. package/dist/cli/commands/lab-route-shared-world.d.ts +0 -2
  134. package/dist/cli/commands/lab-route-shared-world.js +3 -4
  135. package/dist/cli/commands/lab-route-terminal.d.ts +0 -2
  136. package/dist/cli/commands/lab-route-terminal.js +0 -1
  137. package/dist/cli/commands/lab-run.js +84 -54
  138. package/dist/cli/commands/lab-scorer.d.ts +2 -4
  139. package/dist/cli/commands/lab-scorer.js +1 -1
  140. package/dist/cli/commands/migrate.d.ts +4 -0
  141. package/dist/cli/commands/migrate.js +65 -0
  142. package/dist/cli/commands/observe.js +168 -107
  143. package/dist/cli/commands/run-command.d.ts +10 -0
  144. package/dist/cli/commands/run-command.js +85 -0
  145. package/dist/cli/commands/run-signals.d.ts +30 -0
  146. package/dist/cli/commands/run-signals.js +146 -0
  147. package/dist/cli/commands/runs.js +137 -136
  148. package/dist/cli/commands/setup.d.ts +2 -2
  149. package/dist/cli/commands/setup.js +26 -21
  150. package/dist/cli/commands/study.d.ts +3 -0
  151. package/dist/cli/commands/study.js +182 -0
  152. package/dist/cli/commands/tui.d.ts +4 -4
  153. package/dist/cli/commands/tui.js +21 -21
  154. package/dist/cli/commands/watch.js +64 -77
  155. package/dist/cli/deprecations.d.ts +18 -0
  156. package/dist/cli/deprecations.js +48 -0
  157. package/dist/cli/doctor.d.ts +18 -5
  158. package/dist/cli/doctor.js +111 -48
  159. package/dist/cli/first-run-path.d.ts +12 -10
  160. package/dist/cli/first-run-path.js +30 -28
  161. package/dist/cli/io.d.ts +46 -10
  162. package/dist/cli/io.js +109 -30
  163. package/dist/cli/observer-follow.d.ts +2 -2
  164. package/dist/cli/observer-follow.js +5 -6
  165. package/dist/cli/orientation.d.ts +6 -3
  166. package/dist/cli/orientation.js +43 -39
  167. package/dist/cli/product-sentence.d.ts +6 -0
  168. package/dist/cli/product-sentence.js +6 -0
  169. package/dist/cli/program.d.ts +6 -1
  170. package/dist/cli/program.js +112 -43
  171. package/dist/cli/telemetry.d.ts +17 -17
  172. package/dist/cli/telemetry.js +49 -41
  173. package/dist/cli.js +1 -1
  174. package/dist/comms/capture-surface.d.ts +14 -13
  175. package/dist/comms/capture-surface.js +23 -22
  176. package/dist/comms/catch-host.d.ts +8 -8
  177. package/dist/comms/catch-host.js +14 -14
  178. package/dist/comms/connections.js +4 -4
  179. package/dist/comms/email-catch.d.ts +3 -3
  180. package/dist/comms/email-catch.js +8 -8
  181. package/dist/comms/external-evidence.d.ts +27 -0
  182. package/dist/comms/external-evidence.js +93 -0
  183. package/dist/comms/extract.js +2 -2
  184. package/dist/comms/fake-inbox.d.ts +2 -2
  185. package/dist/comms/fake-inbox.js +2 -2
  186. package/dist/comms/lease-store.js +7 -7
  187. package/dist/comms/receiving-render.d.ts +1 -1
  188. package/dist/comms/receiving-render.js +2 -2
  189. package/dist/comms/sandbox-catch-script.d.ts +6 -6
  190. package/dist/comms/sandbox-catch-script.js +6 -6
  191. package/dist/comms/sandbox-catch.d.ts +36 -37
  192. package/dist/comms/sandbox-catch.js +34 -35
  193. package/dist/comms/setup.js +42 -8
  194. package/dist/comms/thread-evidence.d.ts +1 -1
  195. package/dist/comms/thread-evidence.js +6 -6
  196. package/dist/comms/types.d.ts +6 -6
  197. package/dist/comms/types.js +4 -4
  198. package/dist/evidence/browser-hygiene.d.ts +1 -1
  199. package/dist/evidence/browser-hygiene.js +1 -1
  200. package/dist/evidence/frame-signature.d.ts +1 -1
  201. package/dist/evidence/frame-signature.js +4 -4
  202. package/dist/evidence/redaction.d.ts +3 -3
  203. package/dist/evidence/redaction.js +5 -5
  204. package/dist/evidence/secret-scrub.d.ts +9 -0
  205. package/dist/evidence/secret-scrub.js +96 -0
  206. package/dist/feedback/draft.js +41 -85
  207. package/dist/feedback/export-bundle.js +5 -5
  208. package/dist/feedback/export.d.ts +5 -1
  209. package/dist/feedback/export.js +16 -17
  210. package/dist/feedback/feedback.d.ts +3 -3
  211. package/dist/feedback/feedback.js +21 -3
  212. package/dist/guest/chromium-text.js +2 -2
  213. package/dist/guest/desktop-native.d.ts +1 -1
  214. package/dist/guest/runtime.js +1 -1
  215. package/dist/index.d.ts +14 -15
  216. package/dist/index.js +10 -11
  217. package/dist/keys/key-resolution.d.ts +7 -5
  218. package/dist/keys/key-resolution.js +40 -38
  219. package/dist/lab/adapter-extension.d.ts +35 -13
  220. package/dist/lab/adapter-extension.js +50 -134
  221. package/dist/lab/adapter-scorer-loader.d.ts +32 -12
  222. package/dist/lab/adapter-scorer-loader.js +56 -32
  223. package/dist/lab/composition-rules.js +58 -58
  224. package/dist/lab/config.js +85 -43
  225. package/dist/lab/deprecated-fields.d.ts +10 -0
  226. package/dist/lab/deprecated-fields.js +24 -0
  227. package/dist/lab/device-presets.d.ts +10 -10
  228. package/dist/lab/device-presets.js +11 -11
  229. package/dist/lab/discover.d.ts +13 -9
  230. package/dist/lab/discover.js +118 -60
  231. package/dist/lab/doctor.d.ts +53 -11
  232. package/dist/lab/doctor.js +193 -47
  233. package/dist/lab/init-plan.js +1 -3
  234. package/dist/lab/init-templates.js +147 -147
  235. package/dist/lab/init.d.ts +1 -1
  236. package/dist/lab/init.js +41 -9
  237. package/dist/lab/keys.d.ts +2 -0
  238. package/dist/lab/keys.js +43 -9
  239. package/dist/lab/lab-deps.d.ts +80 -0
  240. package/dist/lab/lab-deps.js +4 -0
  241. package/dist/lab/parse/actors.js +17 -16
  242. package/dist/lab/parse/comms.js +8 -7
  243. package/dist/lab/parse/execution.js +15 -14
  244. package/dist/lab/parse/study-v3.d.ts +23 -0
  245. package/dist/lab/parse/study-v3.js +259 -0
  246. package/dist/lab/parse/subject-state.d.ts +7 -7
  247. package/dist/lab/parse/subject-state.js +18 -17
  248. package/dist/lab/parse/subject.js +30 -29
  249. package/dist/lab/parse/values.d.ts +1 -2
  250. package/dist/lab/parse/values.js +3 -6
  251. package/dist/lab/persona-resolve.d.ts +7 -7
  252. package/dist/lab/persona-resolve.js +8 -8
  253. package/dist/lab/persona.js +1 -3
  254. package/dist/lab/plan-base.d.ts +0 -2
  255. package/dist/lab/plan-base.js +6 -4
  256. package/dist/lab/plan-participants.d.ts +7 -7
  257. package/dist/lab/plan-participants.js +2 -2
  258. package/dist/lab/plan-types.d.ts +26 -8
  259. package/dist/lab/plan.d.ts +4 -18
  260. package/dist/lab/plan.js +24 -79
  261. package/dist/lab/preflight-probes.js +11 -11
  262. package/dist/lab/preflight-result.d.ts +2 -1
  263. package/dist/lab/preflight-result.js +5 -6
  264. package/dist/lab/preflight.d.ts +7 -8
  265. package/dist/lab/preflight.js +6 -6
  266. package/dist/lab/requirements.d.ts +20 -0
  267. package/dist/lab/requirements.js +54 -0
  268. package/dist/lab/route-inputs.d.ts +23 -12
  269. package/dist/lab/route-inputs.js +48 -51
  270. package/dist/lab/routing.d.ts +42 -41
  271. package/dist/lab/routing.js +66 -68
  272. package/dist/lab/run-lab-events.d.ts +3 -11
  273. package/dist/lab/run-lab-events.js +2 -16
  274. package/dist/lab/run-lab-homes.d.ts +76 -0
  275. package/dist/lab/run-lab-homes.js +7 -0
  276. package/dist/lab/run-lab-options.d.ts +12 -66
  277. package/dist/lab/run-lab-options.js +58 -285
  278. package/dist/lab/summary.d.ts +15 -9
  279. package/dist/lab/summary.js +24 -17
  280. package/dist/lab/tasks.d.ts +15 -15
  281. package/dist/lab/tasks.js +14 -14
  282. package/dist/lab/types.d.ts +201 -195
  283. package/dist/lab/types.js +2 -0
  284. package/dist/lab/validation.d.ts +21 -21
  285. package/dist/lab/validation.js +61 -59
  286. package/dist/lab/warnings.d.ts +10 -0
  287. package/dist/lab/warnings.js +70 -41
  288. package/dist/library-aliases.d.ts +56 -0
  289. package/dist/library-aliases.js +22 -0
  290. package/dist/observer/artifact.d.ts +2 -2
  291. package/dist/observer/artifact.js +9 -9
  292. package/dist/observer/data.d.ts +6 -6
  293. package/dist/observer/data.js +7 -6
  294. package/dist/observer/exposure.d.ts +1 -1
  295. package/dist/observer/exposure.js +11 -11
  296. package/dist/observer/http.js +1 -1
  297. package/dist/observer/library.d.ts +1 -1
  298. package/dist/observer/library.js +4 -2
  299. package/dist/observer/listen.js +5 -4
  300. package/dist/observer/render.d.ts +1 -1
  301. package/dist/observer/render.js +12 -7
  302. package/dist/observer/run-routes.d.ts +4 -4
  303. package/dist/observer/run-routes.js +6 -6
  304. package/dist/observer/serve.d.ts +17 -0
  305. package/dist/observer/serve.js +41 -6
  306. package/dist/observer/tunnel.js +1 -1
  307. package/dist/observer-app.html +4 -4
  308. package/dist/routes/computer-use/bundle-parts.d.ts +9 -9
  309. package/dist/routes/computer-use/bundle-parts.js +24 -17
  310. package/dist/routes/computer-use/bundle.d.ts +4 -11
  311. package/dist/routes/computer-use/bundle.js +12 -18
  312. package/dist/routes/computer-use/diagnostics.d.ts +1 -1
  313. package/dist/routes/computer-use/diagnostics.js +1 -1
  314. package/dist/routes/computer-use/e2b-desktop/comms.d.ts +11 -11
  315. package/dist/routes/computer-use/e2b-desktop/comms.js +24 -27
  316. package/dist/routes/computer-use/e2b-desktop/desktop.d.ts +2 -2
  317. package/dist/routes/computer-use/e2b-desktop/desktop.js +8 -11
  318. package/dist/routes/computer-use/e2b-desktop/fidelity.d.ts +16 -15
  319. package/dist/routes/computer-use/e2b-desktop/fidelity.js +20 -19
  320. package/dist/routes/computer-use/e2b-desktop/prepare.d.ts +3 -3
  321. package/dist/routes/computer-use/e2b-desktop/prepare.js +40 -43
  322. package/dist/routes/computer-use/e2b-desktop/start.d.ts +1 -1
  323. package/dist/routes/computer-use/e2b-desktop/start.js +32 -30
  324. package/dist/routes/computer-use/e2b-desktop/state.d.ts +5 -4
  325. package/dist/routes/computer-use/e2b-desktop/state.js +5 -3
  326. package/dist/routes/computer-use/e2b-desktop/teardown.d.ts +1 -1
  327. package/dist/routes/computer-use/e2b-desktop/teardown.js +33 -20
  328. package/dist/routes/computer-use/external-comms.d.ts +4 -4
  329. package/dist/routes/computer-use/external-comms.js +19 -43
  330. package/dist/routes/computer-use/fanout-bundle.d.ts +3 -3
  331. package/dist/routes/computer-use/fanout-bundle.js +38 -42
  332. package/dist/routes/computer-use/{fanout-lanes.js → fanout-records.js} +33 -28
  333. package/dist/routes/computer-use/in-process-desktop.d.ts +3 -10
  334. package/dist/routes/computer-use/in-process-desktop.js +3 -9
  335. package/dist/routes/computer-use/live-flush.d.ts +7 -5
  336. package/dist/routes/computer-use/live-flush.js +20 -28
  337. package/dist/routes/computer-use/{run-lanes.d.ts → live-phase.d.ts} +4 -2
  338. package/dist/routes/computer-use/{run-lanes.js → live-phase.js} +17 -16
  339. package/dist/routes/computer-use/local-tree-pack.d.ts +3 -3
  340. package/dist/routes/computer-use/local-tree-pack.js +4 -5
  341. package/dist/routes/computer-use/local-vm.d.ts +11 -8
  342. package/dist/routes/computer-use/local-vm.js +40 -67
  343. package/dist/routes/computer-use/participant-desktop.d.ts +8 -23
  344. package/dist/routes/computer-use/participant-desktop.js +5 -25
  345. package/dist/routes/computer-use/{lanes.d.ts → participant-execution.d.ts} +11 -11
  346. package/dist/routes/computer-use/{lanes.js → participant-execution.js} +39 -26
  347. package/dist/routes/computer-use/participant-facts.d.ts +7 -1
  348. package/dist/routes/computer-use/participant-facts.js +12 -1
  349. package/dist/routes/computer-use/participant-model.d.ts +25 -11
  350. package/dist/routes/computer-use/participant-model.js +76 -34
  351. package/dist/routes/computer-use/participant-prompt.d.ts +13 -13
  352. package/dist/routes/computer-use/participant-prompt.js +8 -8
  353. package/dist/routes/computer-use/participant-runs.d.ts +11 -11
  354. package/dist/routes/computer-use/participant-runs.js +18 -18
  355. package/dist/routes/computer-use/plan.d.ts +21 -29
  356. package/dist/routes/computer-use/plan.js +70 -78
  357. package/dist/routes/computer-use/preflight.d.ts +3 -5
  358. package/dist/routes/computer-use/preflight.js +40 -36
  359. package/dist/routes/computer-use/rerun-selection.js +11 -7
  360. package/dist/routes/computer-use/result.d.ts +1 -1
  361. package/dist/routes/computer-use/result.js +48 -38
  362. package/dist/routes/computer-use/route.d.ts +5 -5
  363. package/dist/routes/computer-use/route.js +36 -33
  364. package/dist/routes/computer-use/self-report.d.ts +8 -8
  365. package/dist/routes/computer-use/self-report.js +33 -33
  366. package/dist/routes/computer-use/setup.d.ts +9 -15
  367. package/dist/routes/computer-use/setup.js +106 -83
  368. package/dist/routes/computer-use/single-bundle.d.ts +13 -16
  369. package/dist/routes/computer-use/single-bundle.js +46 -53
  370. package/dist/routes/computer-use/subject-projection.d.ts +6 -8
  371. package/dist/routes/computer-use/subject-projection.js +24 -23
  372. package/dist/routes/computer-use/types.d.ts +195 -238
  373. package/dist/routes/computer-use/types.js +21 -13
  374. package/dist/routes/preview.d.ts +8 -2
  375. package/dist/routes/preview.js +8 -5
  376. package/dist/routes/scripted/bundle.d.ts +5 -8
  377. package/dist/routes/scripted/bundle.js +23 -27
  378. package/dist/routes/scripted/plan.d.ts +6 -8
  379. package/dist/routes/scripted/plan.js +15 -13
  380. package/dist/routes/scripted/result.d.ts +4 -1
  381. package/dist/routes/scripted/result.js +15 -11
  382. package/dist/routes/scripted/route.js +15 -17
  383. package/dist/routes/scripted/scenario.d.ts +1 -1
  384. package/dist/routes/scripted/scenario.js +3 -3
  385. package/dist/routes/scripted/setup.d.ts +3 -2
  386. package/dist/routes/scripted/setup.js +27 -26
  387. package/dist/routes/scripted/subject.d.ts +8 -3
  388. package/dist/routes/scripted/subject.js +10 -6
  389. package/dist/routes/scripted/surface-records.js +4 -4
  390. package/dist/routes/scripted/surface-sessions.d.ts +2 -2
  391. package/dist/routes/scripted/surface-sessions.js +4 -4
  392. package/dist/routes/scripted/types.d.ts +21 -40
  393. package/dist/routes/scripted/types.js +2 -3
  394. package/dist/routes/shared-world/{seat-records.js → bundle-records.js} +17 -16
  395. package/dist/routes/shared-world/bundle.d.ts +7 -7
  396. package/dist/routes/shared-world/bundle.js +60 -62
  397. package/dist/routes/shared-world/checkpoints.d.ts +5 -5
  398. package/dist/routes/shared-world/checkpoints.js +5 -5
  399. package/dist/routes/shared-world/comms.d.ts +8 -7
  400. package/dist/routes/shared-world/comms.js +38 -51
  401. package/dist/routes/shared-world/external-public.d.ts +6 -5
  402. package/dist/routes/shared-world/external-public.js +28 -25
  403. package/dist/routes/shared-world/handoff.d.ts +10 -9
  404. package/dist/routes/shared-world/handoff.js +89 -81
  405. package/dist/routes/shared-world/lobby-code.d.ts +7 -7
  406. package/dist/routes/shared-world/lobby-code.js +22 -22
  407. package/dist/routes/shared-world/{seats.d.ts → participant-specs.d.ts} +15 -12
  408. package/dist/routes/shared-world/{seats.js → participant-specs.js} +65 -63
  409. package/dist/routes/shared-world/plan.d.ts +2 -6
  410. package/dist/routes/shared-world/plan.js +8 -9
  411. package/dist/routes/shared-world/provenance.d.ts +4 -4
  412. package/dist/routes/shared-world/provenance.js +4 -4
  413. package/dist/routes/shared-world/provisioned.d.ts +3 -2
  414. package/dist/routes/shared-world/provisioned.js +55 -48
  415. package/dist/routes/shared-world/result.js +47 -36
  416. package/dist/routes/shared-world/route.js +54 -57
  417. package/dist/routes/shared-world/setup.d.ts +6 -5
  418. package/dist/routes/shared-world/setup.js +16 -18
  419. package/dist/routes/shared-world/types.d.ts +70 -141
  420. package/dist/routes/shared-world/types.js +5 -5
  421. package/dist/routes/terminal/adapter.d.ts +10 -10
  422. package/dist/routes/terminal/adapter.js +32 -129
  423. package/dist/routes/terminal/bundle.d.ts +10 -15
  424. package/dist/routes/terminal/bundle.js +43 -52
  425. package/dist/routes/terminal/credentials.d.ts +4 -4
  426. package/dist/routes/terminal/credentials.js +20 -20
  427. package/dist/routes/terminal/dry-run.d.ts +1 -1
  428. package/dist/routes/terminal/dry-run.js +13 -15
  429. package/dist/routes/terminal/encoding.d.ts +1 -1
  430. package/dist/routes/terminal/encoding.js +4 -4
  431. package/dist/routes/terminal/ledger.d.ts +8 -8
  432. package/dist/routes/terminal/ledger.js +22 -22
  433. package/dist/routes/terminal/live-finish.d.ts +1 -2
  434. package/dist/routes/terminal/live-finish.js +24 -23
  435. package/dist/routes/terminal/live-sandbox.d.ts +3 -2
  436. package/dist/routes/terminal/live-sandbox.js +31 -32
  437. package/dist/routes/terminal/participant-text.d.ts +23 -0
  438. package/dist/routes/terminal/participant-text.js +165 -0
  439. package/dist/routes/terminal/persona.js +1 -1
  440. package/dist/routes/terminal/plan.d.ts +2 -6
  441. package/dist/routes/terminal/plan.js +23 -19
  442. package/dist/routes/terminal/recorder.d.ts +6 -1
  443. package/dist/routes/terminal/recorder.js +12 -4
  444. package/dist/routes/terminal/result.js +12 -12
  445. package/dist/routes/terminal/route.js +30 -31
  446. package/dist/routes/terminal/runtime.d.ts +3 -2
  447. package/dist/routes/terminal/runtime.js +3 -3
  448. package/dist/routes/terminal/sandbox.d.ts +4 -4
  449. package/dist/routes/terminal/sandbox.js +10 -10
  450. package/dist/routes/terminal/session.js +22 -19
  451. package/dist/routes/terminal/token-usage.d.ts +1 -1
  452. package/dist/routes/terminal/token-usage.js +10 -11
  453. package/dist/routes/terminal/trace.d.ts +7 -10
  454. package/dist/routes/terminal/trace.js +11 -66
  455. package/dist/routes/terminal/types.d.ts +104 -114
  456. package/dist/routes/terminal/types.js +12 -7
  457. package/dist/run/active-runs.d.ts +13 -0
  458. package/dist/run/active-runs.js +12 -0
  459. package/dist/run/bundle-shape.d.ts +2 -1
  460. package/dist/run/bundle-shape.js +7 -7
  461. package/dist/run/bundle.d.ts +99 -92
  462. package/dist/run/bundle.js +10 -8
  463. package/dist/run/cost-summary.js +6 -5
  464. package/dist/run/costs.d.ts +6 -6
  465. package/dist/run/costs.js +17 -11
  466. package/dist/run/detail.d.ts +9 -9
  467. package/dist/run/detail.js +10 -10
  468. package/dist/run/dry-run-inputs.js +1 -1
  469. package/dist/run/dry-run.js +23 -30
  470. package/dist/run/{study-files.d.ts → evidence-files.d.ts} +4 -4
  471. package/dist/run/{study-files.js → evidence-files.js} +5 -5
  472. package/dist/run/feedback-shape.js +1 -1
  473. package/dist/run/judge.d.ts +34 -16
  474. package/dist/run/judge.js +41 -14
  475. package/dist/run/locate.d.ts +2 -0
  476. package/dist/run/locate.js +3 -1
  477. package/dist/run/outcomes.d.ts +3 -3
  478. package/dist/run/outcomes.js +3 -3
  479. package/dist/run/participant-records.d.ts +2 -2
  480. package/dist/run/participant-records.js +2 -2
  481. package/dist/run/paths.d.ts +1 -1
  482. package/dist/run/paths.js +2 -2
  483. package/dist/run/pricing.d.ts +30 -15
  484. package/dist/run/pricing.js +49 -30
  485. package/dist/run/projection.d.ts +27 -27
  486. package/dist/run/projection.js +23 -23
  487. package/dist/run/reclaim.d.ts +6 -0
  488. package/dist/run/reclaim.js +15 -5
  489. package/dist/run/results.d.ts +5 -5
  490. package/dist/run/run-index.d.ts +9 -7
  491. package/dist/run/run-index.js +24 -25
  492. package/dist/run/run-not-found.d.ts +5 -0
  493. package/dist/run/run-not-found.js +15 -0
  494. package/dist/run/run.d.ts +7 -4
  495. package/dist/run/run.js +10 -6
  496. package/dist/run/sandbox-receipts.d.ts +5 -5
  497. package/dist/run/sandbox-receipts.js +6 -6
  498. package/dist/run/shared-world-evidence.d.ts +67 -66
  499. package/dist/run/shared-world-evidence.js +4 -4
  500. package/dist/run/shared-world-shape.d.ts +3 -3
  501. package/dist/run/shared-world-shape.js +16 -16
  502. package/dist/run/stats.d.ts +17 -11
  503. package/dist/run/stats.js +124 -66
  504. package/dist/run/status.d.ts +21 -32
  505. package/dist/run/status.js +34 -33
  506. package/dist/run/stored-runs.js +2 -1
  507. package/dist/run/stream-shape.js +1 -1
  508. package/dist/run/streams.d.ts +29 -22
  509. package/dist/run/study-provenance.d.ts +47 -0
  510. package/dist/run/study-provenance.js +63 -0
  511. package/dist/run/study-result.d.ts +16 -0
  512. package/dist/run/study-result.js +9 -0
  513. package/dist/run/terminal-contract.d.ts +5 -5
  514. package/dist/run/terminal-contract.js +4 -4
  515. package/dist/run/text.d.ts +2 -0
  516. package/dist/run/text.js +4 -0
  517. package/dist/run/transient-comms-secrets.d.ts +5 -0
  518. package/dist/run/transient-comms-secrets.js +12 -0
  519. package/dist/run-lab.d.ts +40 -38
  520. package/dist/run-lab.js +42 -40
  521. package/dist/study/convert.d.ts +33 -0
  522. package/dist/study/convert.js +432 -0
  523. package/dist/study/field-paths.d.ts +12 -0
  524. package/dist/study/field-paths.js +126 -0
  525. package/dist/study/files.d.ts +26 -0
  526. package/dist/study/files.js +43 -0
  527. package/dist/study/migrate.d.ts +48 -0
  528. package/dist/study/migrate.js +408 -0
  529. package/dist/study/owned-files.d.ts +38 -0
  530. package/dist/study/owned-files.js +182 -0
  531. package/dist/subject/clone.d.ts +1 -1
  532. package/dist/subject/desktop-cli.d.ts +2 -2
  533. package/dist/subject/desktop-cli.js +3 -3
  534. package/dist/subject/local-tree-archive.d.ts +1 -1
  535. package/dist/subject/local-tree-archive.js +5 -5
  536. package/dist/subject/local-tree.d.ts +3 -3
  537. package/dist/subject/local-tree.js +1 -1
  538. package/dist/subject/node-bootstrap.d.ts +1 -1
  539. package/dist/subject/node-bootstrap.js +4 -4
  540. package/dist/subject/runtime.js +10 -11
  541. package/dist/subject/serve.d.ts +1 -1
  542. package/dist/subject/serve.js +4 -4
  543. package/dist/subject/state.d.ts +1 -1
  544. package/dist/subject/steps.d.ts +8 -8
  545. package/dist/subject/steps.js +7 -7
  546. package/dist/substrates/command-failure.d.ts +3 -3
  547. package/dist/substrates/command-failure.js +3 -3
  548. package/dist/substrates/detached.d.ts +6 -6
  549. package/dist/substrates/detached.js +7 -7
  550. package/dist/substrates/e2b/cdp-probe.d.ts +11 -11
  551. package/dist/substrates/e2b/cdp-probe.js +15 -18
  552. package/dist/substrates/e2b/desktop-browser.d.ts +5 -5
  553. package/dist/substrates/e2b/desktop-browser.js +12 -12
  554. package/dist/substrates/e2b/desktop-cdp.d.ts +15 -15
  555. package/dist/substrates/e2b/desktop-cdp.js +11 -11
  556. package/dist/substrates/e2b/desktop-executor.d.ts +1 -1
  557. package/dist/substrates/e2b/desktop-executor.js +1 -1
  558. package/dist/substrates/e2b/desktop-geometry.d.ts +6 -6
  559. package/dist/substrates/e2b/desktop-geometry.js +9 -9
  560. package/dist/substrates/e2b/desktop-media.d.ts +1 -1
  561. package/dist/substrates/e2b/desktop-media.js +3 -3
  562. package/dist/substrates/e2b/desktop-screenshot-cleanup.d.ts +3 -3
  563. package/dist/substrates/e2b/desktop-screenshot-cleanup.js +3 -3
  564. package/dist/substrates/e2b/sandbox.d.ts +1 -1
  565. package/dist/substrates/e2b/sandbox.js +2 -2
  566. package/dist/substrates/e2b/sdk.d.ts +11 -11
  567. package/dist/substrates/e2b/sdk.js +8 -8
  568. package/dist/substrates/local/runtime-host.d.ts +2 -0
  569. package/dist/substrates/local/runtime-host.js +6 -0
  570. package/dist/tui/actions.d.ts +8 -6
  571. package/dist/tui/actions.js +31 -22
  572. package/dist/tui/contract.d.ts +16 -16
  573. package/dist/tui/contract.js +10 -10
  574. package/dist/tui/launch.d.ts +4 -4
  575. package/dist/tui/launch.js +18 -19
  576. package/dist/tui/project.js +5 -5
  577. package/dist/tui-app.js +83 -83
  578. package/dist/verify/actor.d.ts +14 -14
  579. package/dist/verify/actor.js +45 -45
  580. package/dist/verify/artifacts.d.ts +3 -3
  581. package/dist/verify/artifacts.js +6 -6
  582. package/dist/verify/context-growth.d.ts +9 -0
  583. package/dist/verify/context-growth.js +62 -0
  584. package/dist/verify/costs.d.ts +7 -7
  585. package/dist/verify/costs.js +9 -9
  586. package/dist/verify/liveness.d.ts +1 -1
  587. package/dist/verify/liveness.js +4 -1
  588. package/dist/verify/rerun.d.ts +1 -1
  589. package/dist/verify/rerun.js +1 -1
  590. package/dist/verify/shared-world-concurrent.d.ts +2 -2
  591. package/dist/verify/shared-world-concurrent.js +79 -76
  592. package/dist/verify/shared-world.d.ts +2 -2
  593. package/dist/verify/shared-world.js +18 -18
  594. package/dist/verify/subject.d.ts +4 -4
  595. package/dist/verify/subject.js +18 -18
  596. package/dist/verify/verify.js +98 -49
  597. package/docs/README.md +26 -33
  598. package/docs/architecture/actor-contract.md +105 -98
  599. package/docs/architecture/desktop-sessions.md +14 -14
  600. package/docs/architecture/external-public-shared-world.md +66 -59
  601. package/docs/architecture/github-feedback-loop.md +1 -4
  602. package/docs/architecture/guest-desktop.md +2 -2
  603. package/docs/architecture/observer.md +37 -37
  604. package/docs/architecture/restricted-codex-analysis.md +70 -30
  605. package/docs/architecture/serve.md +34 -37
  606. package/docs/architecture/state-driven-executor.md +55 -59
  607. package/docs/architecture/task-protocol-support.md +7 -9
  608. package/docs/architecture/{terminal-product-lane.md → terminal-product-route.md} +83 -115
  609. package/docs/contracts/README.md +24 -0
  610. package/docs/contracts/adapter-admission.md +3 -3
  611. package/docs/contracts/adapter-fixtures.md +2 -5
  612. package/docs/contracts/core.md +1 -3
  613. package/docs/contracts/errors.md +62 -0
  614. package/docs/contracts/feedback.md +8 -9
  615. package/docs/contracts/policy.md +7 -12
  616. package/docs/contracts/run-bundle.md +54 -51
  617. package/docs/contracts/schemas.md +505 -390
  618. package/docs/contracts/study-analysis.md +26 -2
  619. package/docs/contracts/study-costs.md +11 -7
  620. package/docs/decisions/0001-run-bundle-is-the-source-of-truth.md +1 -1
  621. package/docs/decisions/0002-observer-is-one-self-contained-file.md +2 -2
  622. package/docs/decisions/0003-contained-paths-and-cleanup-authority.md +3 -3
  623. package/docs/decisions/0004-participant-desktops-stay-in-the-route.md +2 -1
  624. package/docs/principles/actor-fidelity.md +4 -4
  625. package/docs/principles/engineering.md +3 -3
  626. package/docs/principles/invariants-and-defaults.md +38 -38
  627. package/docs/principles/self-driving-harness.md +1 -5
  628. package/docs/principles/three-roles.md +1 -1
  629. package/docs/product/automatic-analysis.md +8 -7
  630. package/examples/participant/README.md +5 -5
  631. package/examples/participant/run.mjs +11 -10
  632. package/examples/scorer/README.md +3 -3
  633. package/examples/scorer/run.mjs +11 -12
  634. package/package.json +16 -17
  635. package/skills/humanish/SKILL.md +63 -60
  636. package/skills/humanish/agents/openai.yaml +1 -1
  637. package/AGENTS.md +0 -99
  638. package/CONTRIBUTING.md +0 -148
  639. package/dist/actors/codex/qualified-versions.d.ts +0 -45
  640. package/dist/actors/codex/qualified-versions.js +0 -87
  641. package/dist/cli/commands/lab.d.ts +0 -3
  642. package/dist/cli/commands/lab.js +0 -193
  643. package/dist/deprecated.d.ts +0 -90
  644. package/dist/deprecated.js +0 -106
  645. package/dist/lab/bag-overrides.d.ts +0 -18
  646. package/dist/lab/bag-overrides.js +0 -121
  647. package/dist/observer/static.d.ts +0 -39
  648. package/dist/observer/static.js +0 -369
  649. package/dist/routes/computer-use/legacy-lane-spec.d.ts +0 -62
  650. package/dist/routes/computer-use/legacy-lane-spec.js +0 -39
  651. package/docs/architecture/comms-inbox.md +0 -122
  652. package/docs/architecture/desktop-recording.md +0 -80
  653. package/docs/architecture/local-browser-runtime.md +0 -178
  654. package/docs/architecture/observer-review.md +0 -144
  655. package/docs/architecture/participant-media.md +0 -90
  656. package/docs/architecture/project-layout.md +0 -62
  657. package/docs/architecture/real-email-receiving.md +0 -136
  658. package/docs/assets/humanish-drawdb-hero.png +0 -0
  659. package/docs/assets/humanish-observer-hero.png +0 -0
  660. package/docs/goals/current.md +0 -207
  661. package/docs/product/lobby-trivia-3player-external-public.md +0 -77
  662. package/docs/product/open-source-install-experience.md +0 -240
  663. package/docs/ramp/README.md +0 -294
  664. package/docs/release/open-source-readiness.md +0 -222
  665. package/docs/release/public-readiness-standard.md +0 -210
  666. package/docs/roadmap/world-class-open-source-v0.md +0 -311
  667. /package/dist/routes/computer-use/{fanout-lanes.d.ts → fanout-records.d.ts} +0 -0
  668. /package/dist/routes/shared-world/{seat-records.d.ts → bundle-records.d.ts} +0 -0
@@ -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 LabConfig. parseLabConfig 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 { LAB_CONFIG_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: LAB_CONFIG_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
+ }
@@ -1,10 +1,10 @@
1
1
  import type { LabConfigParseFailure, LabSubjectState } from "../types.js";
2
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
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
5
  * `subject.env` would force an adopter to carry a private env file just to reproduce a public study.
6
6
  *
7
- * These values ARE recorded in evidence (they are part of how the subject was configured), so a
7
+ * These values are recorded in evidence (they are part of how the subject was configured), so a
8
8
  * value that looks like a credential is refused here rather than committed to a public repo.
9
9
  */
10
10
  export declare function parseEnvValues(raw: unknown): {
@@ -14,8 +14,8 @@ export declare function parseEnvValues(raw: unknown): {
14
14
  /**
15
15
  * Structural parse of `subject.state` into a candidate LabSubjectState. Deliberately keeps
16
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 violate invariant 6.
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
19
  */
20
20
  export declare function parseState(raw: unknown): {
21
21
  ok: true;
@@ -23,8 +23,8 @@ export declare function parseState(raw: unknown): {
23
23
  } | LabConfigParseFailure;
24
24
  /**
25
25
  * Semantic validation for `subject.state`, shared by parseLabConfig and the engine
26
- * (runCuaActorLab re-enforces it on configs that arrive through the library API). Returns
26
+ * (the route re-enforces it on configs that arrive through runLab). Returns
27
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.
28
+ * defensively: library callers can hand the engine arbitrarily-shaped objects.
29
29
  */
30
30
  export declare function subjectStateInvalidReason(state: LabSubjectState, env: readonly string[] | undefined): string | null;
@@ -2,25 +2,26 @@
2
2
  // the structural parse of `subject.state`, and subjectStateInvalidReason, the semantic check
3
3
  // parseLabConfig and the computer-use route share.
4
4
  import { containsSensitive } from "../../evidence/redaction.js";
5
- import { ENV_NAME_PATTERN, invalid, isRecord, posInt, str, strList } from "./values.js";
5
+ import { ENV_NAME_PATTERN, invalid, posInt, str, strList } from "./values.js";
6
+ import { isRecord } from "../../run/type-guards.js";
6
7
  /**
7
- * LITERAL non-secret subject env. Real apps need configuration before they will boot — a public base
8
- * URL, a transport selector, a feature flag — and none of that is secret. Routing it through
8
+ * Literal non-secret subject env. Real apps need configuration before they will boot: a public base
9
+ * URL, a transport selector, a feature flag, and none of that is secret. Routing it through
9
10
  * `subject.env` would force an adopter to carry a private env file just to reproduce a public study.
10
11
  *
11
- * These values ARE recorded in evidence (they are part of how the subject was configured), so a
12
+ * These values are recorded in evidence (they are part of how the subject was configured), so a
12
13
  * value that looks like a credential is refused here rather than committed to a public repo.
13
14
  */
14
15
  export function parseEnvValues(raw) {
15
16
  if (raw === undefined)
16
17
  return { ok: true };
17
18
  if (!isRecord(raw)) {
18
- return invalid("`subject.envValues` must be a mapping of env var NAME to a literal non-secret value.");
19
+ return invalid("`subject.envValues` must map environment variable names to literal values that are not secret.");
19
20
  }
20
21
  const envValues = {};
21
22
  for (const [name, rawValue] of Object.entries(raw)) {
22
23
  if (!ENV_NAME_PATTERN.test(name)) {
23
- return invalid(`subject.envValues keys must be env var NAMES like NEXT_PUBLIC_APP_URL (got "${name}").`);
24
+ return invalid(`subject.envValues keys must be environment variable names like NEXT_PUBLIC_APP_URL, and "${name}" is not one.`);
24
25
  }
25
26
  const value = typeof rawValue === "number" || typeof rawValue === "boolean"
26
27
  ? String(rawValue)
@@ -29,9 +30,9 @@ export function parseEnvValues(raw) {
29
30
  return invalid(`\`subject.envValues.${name}\` must be a string, number, or boolean.`);
30
31
  }
31
32
  // Reuse the redaction module's own detector rather than inventing a second opinion about what
32
- // a secret looks like — the two must never disagree about the same string.
33
+ // a secret looks like; the two must never disagree about the same string.
33
34
  if (containsSensitive(value)) {
34
- return invalid(`\`subject.envValues.${name}\` looks like a secret or a local path, and these values are committed with the lab and recorded in evidence. Declare the NAME in \`subject.env\` instead — those values come from the caller's environment and never persist.`);
35
+ return invalid(`\`subject.envValues.${name}\` looks like a secret or a local path, and envValues are committed with the study and recorded in evidence. List the name in \`subject.env\` instead: its value then comes from your environment and is never stored.`);
35
36
  }
36
37
  envValues[name] = value;
37
38
  }
@@ -40,8 +41,8 @@ export function parseEnvValues(raw) {
40
41
  /**
41
42
  * Structural parse of `subject.state` into a candidate LabSubjectState. Deliberately keeps
42
43
  * unrecognized `when`/`timeoutMs` values in the candidate (instead of silently dropping
43
- * them) so subjectStateInvalidReason rejects them — a state declaration that silently does
44
- * less than it says would violate invariant 6.
44
+ * them) so subjectStateInvalidReason rejects them: a state declaration that silently does
45
+ * less than it says would claim more than its mechanism does.
45
46
  */
46
47
  export function parseState(raw) {
47
48
  if (raw === undefined) {
@@ -67,7 +68,7 @@ export function parseState(raw) {
67
68
  if (raw.external !== undefined) {
68
69
  const external = strList(raw.external);
69
70
  if (!external) {
70
- return invalid("`subject.state.external` must be a non-empty list of env var NAMES when set.");
71
+ return invalid("`subject.state.external` must be a non-empty list of environment variable names when it is set.");
71
72
  }
72
73
  state.external = external;
73
74
  }
@@ -78,7 +79,7 @@ export function parseState(raw) {
78
79
  state.checkpoint = raw.checkpoint.map((probe) => ({
79
80
  name: typeof probe.name === "string" ? probe.name.trim() : "",
80
81
  command: typeof probe.command === "string" ? probe.command.trim() : "",
81
- // Preserve the redact list verbatim (literal secret values may contain commas, so do NOT
82
+ // Preserve the redact list verbatim (literal secret values may contain commas, so do not
82
83
  // run it through the comma-splitting strList); subjectStateInvalidReason validates the shape.
83
84
  ...(probe.redact === undefined ? {} : { redact: probe.redact }),
84
85
  }));
@@ -86,7 +87,7 @@ export function parseState(raw) {
86
87
  return { ok: true, value: state };
87
88
  }
88
89
  // The step name interpolates into in-sandbox script/status/log paths (`subject-state-<name>`);
89
- // the strict shape is load-bearing, exactly like the repo slug.
90
+ // so the shape is strict, exactly like the repo slug.
90
91
  const STATE_STEP_NAME_PATTERN = /^[a-z0-9][a-z0-9-]*$/;
91
92
  const STATE_STEP_NAME_MAX_CHARS = 40;
92
93
  const STATE_STEP_WHENS = [
@@ -96,9 +97,9 @@ const STATE_STEP_WHENS = [
96
97
  ];
97
98
  /**
98
99
  * Semantic validation for `subject.state`, shared by parseLabConfig and the engine
99
- * (runCuaActorLab re-enforces it on configs that arrive through the library API). Returns
100
+ * (the route re-enforces it on configs that arrive through runLab). Returns
100
101
  * the failure message, or null when the declaration is valid. Reads the candidate
101
- * defensively — library callers can hand the engine arbitrarily-shaped objects.
102
+ * defensively: library callers can hand the engine arbitrarily-shaped objects.
102
103
  */
103
104
  export function subjectStateInvalidReason(state, env) {
104
105
  const seed = state.seed;
@@ -139,11 +140,11 @@ export function subjectStateInvalidReason(state, env) {
139
140
  }
140
141
  if (external !== undefined) {
141
142
  if (!Array.isArray(external) || external.length === 0) {
142
- return "`subject.state.external` must be a non-empty list of env var NAMES when set.";
143
+ return "`subject.state.external` must be a non-empty list of environment variable names when it is set.";
143
144
  }
144
145
  for (const name of external) {
145
146
  if (typeof name !== "string" || !ENV_NAME_PATTERN.test(name)) {
146
- return "subject.state.external entries must be env var NAMES like DATABASE_URL; values come from the caller's environment and are never persisted.";
147
+ return "subject.state.external entries must be environment variable names like DATABASE_URL. Their values come from your environment and are never stored.";
147
148
  }
148
149
  if (!env?.includes(name)) {
149
150
  return "subject.state.external names must also be declared in subject.env (the declaration must name a provisioned channel).";
@@ -1,6 +1,7 @@
1
1
  import { normalizeExtraExcludeEntry } from "../../subject/local-tree-archive.js";
2
- import { ENV_NAME_PATTERN, invalid, isRecord, posInt, str, strList } from "./values.js";
2
+ import { ENV_NAME_PATTERN, invalid, posInt, str, strList } from "./values.js";
3
3
  import { parseEnvValues, parseState, subjectStateInvalidReason } from "./subject-state.js";
4
+ import { isRecord } from "../../run/type-guards.js";
4
5
  export function parseSubject(raw) {
5
6
  if (!isRecord(raw)) {
6
7
  return invalid("Lab `subject` is required and must be an object.");
@@ -16,9 +17,9 @@ export function parseSubject(raw) {
16
17
  return invalid("`subject.source` must be one of: this-repo, clone, app-url, local-app, terminal-product, desktop-cli, local-tree.");
17
18
  }
18
19
  const subject = { source };
19
- // topology is enum-validated everywhere; its SEMANTICS (shared-world requires clone × e2b-desktop
20
+ // topology is enum-validated everywhere; its semantics (shared-world requires clone × e2b-desktop
20
21
  // × a ≥2 roster) are enforced in the shared-world cross-validation below, and a set-but-unconsumed
21
- // topology warns as inert off the shared-world route (invariant 6).
22
+ // topology warns as inert off the shared-world route.
22
23
  if (raw.topology !== undefined) {
23
24
  const topology = str(raw.topology);
24
25
  if (topology !== "per-lane-worlds" && topology !== "shared-world") {
@@ -26,12 +27,12 @@ export function parseSubject(raw) {
26
27
  }
27
28
  subject.topology = topology;
28
29
  }
29
- // exposure is enum-validated everywhere; it is REQUIRED on the concurrent shared-world route (the
30
+ // exposure is enum-validated everywhere; it is required on the concurrent shared-world route (the
30
31
  // getHost synthetic-subject attestation) and warns inert elsewhere.
31
32
  if (raw.exposure !== undefined) {
32
33
  const exposure = str(raw.exposure);
33
34
  if (exposure !== "synthetic") {
34
- return invalid("`subject.exposure` must be `synthetic` (the author attestation that the getHost-exposed subject is synthetic seeded data).");
35
+ return invalid("`subject.exposure` must be `synthetic`: your statement that the subject served on a public sandbox URL holds only synthetic seeded data.");
35
36
  }
36
37
  subject.exposure = exposure;
37
38
  }
@@ -56,15 +57,15 @@ export function parseSubject(raw) {
56
57
  }
57
58
  /** The first field set on a source it cannot act on, in the order parseSubject has always checked. */
58
59
  function misplacedFieldFailure(source, raw) {
59
- // `product` is terminal-product-only; reject it elsewhere (invariant 6: a field that cannot act
60
- // on this route is an honest parse error, not silently dropped).
60
+ // `product` is terminal-product-only; reject it elsewhere. A field that cannot act on this
61
+ // route is a parse error and is never silently dropped.
61
62
  if (source !== "terminal-product" && source !== "desktop-cli" && raw.product !== undefined) {
62
63
  return invalid("`subject.product` applies only to terminal-product and desktop-cli subjects (the CLI a participant studies from public surfaces).");
63
64
  }
64
- // appUrl is app-url/local-app-only; a terminal-product subject drives PUBLIC surfaces, not a
65
- // single loopback app — reject appUrl on it.
65
+ // appUrl is app-url/local-app-only; a terminal-product subject drives public surfaces instead of a
66
+ // single loopback app, so reject appUrl on it.
66
67
  if (source === "terminal-product" && raw.appUrl !== undefined) {
67
- return invalid("`subject.appUrl` does not apply to terminal-product subjects — declare `subject.product.publicSurfaces` (the agent works from public surfaces, not one loopback app).");
68
+ return invalid("`subject.appUrl` does not apply to a terminal-product subject: its agent works from the product's public pages. List them in `subject.product.publicSurfaces` instead.");
68
69
  }
69
70
  // serve/env/state are shared between clone (cloned app) and local-tree (packed working
70
71
  // tree): both routes serve a subject in-sandbox with the same install/build/start/url +
@@ -78,7 +79,7 @@ function misplacedFieldFailure(source, raw) {
78
79
  if (source !== "clone" && source !== "local-tree" && raw.state !== undefined) {
79
80
  return invalid("`subject.state` applies only to clone subjects or local-tree subjects (the lab seeds the state it serves).");
80
81
  }
81
- // repos/clone are clone-ONLY (a fresh-clone subject's git inputs). local-tree packs the
82
+ // repos/clone are clone-only (a fresh-clone subject's git inputs). local-tree packs the
82
83
  // resolution cwd itself, so it has no repo slug to clone and gets its own precise reasons
83
84
  // rather than falling through to the generic clone-only message below.
84
85
  if (source === "local-tree" && raw.repos !== undefined) {
@@ -88,19 +89,19 @@ function misplacedFieldFailure(source, raw) {
88
89
  return invalid("`subject.clone` does not apply to local-tree subjects. Declare `subject.localTree` instead (keep/exclude/maxArchiveBytes).");
89
90
  }
90
91
  // Rejected, never silently dropped, on app-url/local-app/this-repo/terminal-product subjects
91
- // too (invariant 6: a field that cannot act on this route is an honest parse error).
92
+ // too: a field that cannot act on this route is a parse error.
92
93
  if (source !== "clone" && raw.repos !== undefined) {
93
94
  return invalid("`subject.repos` applies only to clone subjects (the owner/repo slugs to clone).");
94
95
  }
95
96
  if (source !== "clone" && raw.clone !== undefined) {
96
97
  return invalid("`subject.clone` applies only to clone subjects (clone depth/fanout/keep).");
97
98
  }
98
- // localTree is local-tree-ONLY (pack/upload knobs for the packed working tree).
99
+ // localTree is local-tree-only (pack/upload knobs for the packed working tree).
99
100
  if (source !== "local-tree" && raw.localTree !== undefined) {
100
101
  return invalid("`subject.localTree` applies only to local-tree subjects (keep/exclude/maxArchiveBytes for packing the working tree).");
101
102
  }
102
- // publicTarget is app-url-ONLY (the external-public shared-world ownership attestation). It is
103
- // meaningless without a real public deployment as the plane — reject it elsewhere (invariant 6).
103
+ // publicTarget is app-url-only (the external-public shared-world ownership attestation). It is
104
+ // meaningless without a real public deployment as the plane, so reject it elsewhere.
104
105
  if (source !== "app-url" && raw.publicTarget !== undefined) {
105
106
  return invalid("`subject.publicTarget` applies only to app-url subjects on the external-public shared-world route (the operator's ownership attestation for a real public deployment used directly as the shared plane).");
106
107
  }
@@ -124,7 +125,7 @@ function parseCloneSubject(raw, subject) {
124
125
  }
125
126
  function parseLocalTreeSubject(raw, subject) {
126
127
  // A local-tree subject exists to be packed and served; there is no other way to boot it, so
127
- // serve is REQUIRED here. Clone subjects get the same requirement from each route's checks in
128
+ // serve is required here. Clone subjects get the same requirement from each route's checks in
128
129
  // parseLabConfig.
129
130
  if (raw.serve === undefined) {
130
131
  return invalid("`subject.serve` is required when source is local-tree: a local-tree subject exists to be packed and served, so declare install/build/start/url exactly like the clone route.");
@@ -156,11 +157,11 @@ function parseServedApp(raw, subject) {
156
157
  if (raw.env !== undefined) {
157
158
  const env = strList(raw.env);
158
159
  if (!env || env.length === 0) {
159
- return invalid("`subject.env` must be a non-empty list of env var NAMES when set.");
160
+ return invalid("`subject.env` must be a non-empty list of environment variable names when it is set.");
160
161
  }
161
162
  const badName = env.find((name) => !ENV_NAME_PATTERN.test(name));
162
163
  if (badName) {
163
- return invalid(`subject.env entries must be env var NAMES like DATABASE_URL (got "${badName}"); values come from the caller's environment and are never persisted.`);
164
+ return invalid(`subject.env entries must be environment variable names like DATABASE_URL, and "${badName}" is not one. Their values come from your environment and are never stored.`);
164
165
  }
165
166
  subject.env = env;
166
167
  }
@@ -191,11 +192,11 @@ function parseAppSubject(raw, source, subject) {
191
192
  }
192
193
  // app-url: shape-only here; the loopback-vs-public-target gate is applied in the
193
194
  // cross-validation block below, where policies.allowPublicTargets is available.
194
- // local-app: an in-process local dev server — ALWAYS loopback (no public-target option),
195
+ // local-app: an in-process local dev server, always loopback (no public-target option),
195
196
  // so the loopback wall is enforced right here at parse.
196
197
  if (source === "local-app") {
197
198
  if (!isLoopbackUrl(appUrl)) {
198
- return invalid("`subject.appUrl` must be a loopback URL (127.0.0.1/localhost) on a local-app subject — it drives an already-running LOCAL dev server in-process; public targets are not supported on this route.");
199
+ return invalid("`subject.appUrl` must be a loopback URL (127.0.0.1 or localhost) on a local-app subject: humanish drives your running dev server from this process, so it cannot reach a public URL.");
199
200
  }
200
201
  }
201
202
  else if (!isHttpUrl(appUrl)) {
@@ -203,7 +204,7 @@ function parseAppSubject(raw, source, subject) {
203
204
  }
204
205
  subject.appUrl = appUrl;
205
206
  // publicTarget (external-public shared-world ownership attestation) is app-url-only. Shape it
206
- // here; its REQUIRED-on-that-route semantics live in externalPublicSharedWorldValidationReason.
207
+ // here; its required-on-that-route semantics live in externalPublicSharedWorldValidationReason.
207
208
  if (source === "app-url" && raw.publicTarget !== undefined) {
208
209
  const publicTargetResult = parsePublicTarget(raw.publicTarget);
209
210
  if (!publicTargetResult.ok) {
@@ -225,22 +226,22 @@ function parseProductSubject(raw, subject) {
225
226
  // "example-operator/lobby-trivia" or a bare org name). Slash allowed for the owner/repo convention.
226
227
  export const PUBLIC_TARGET_OWNER_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_./-]*$/;
227
228
  /** Parse the external-public shared-world ownership attestation ({ owner, authorized: true }). The
228
- * harness cannot verify ownership — this is author-trust, surfaced honestly in the evidence class. */
229
+ * harness cannot verify ownership: this is author-trust, and the evidence class says so. */
229
230
  function parsePublicTarget(raw) {
230
231
  if (!isRecord(raw)) {
231
- return invalid("`subject.publicTarget` must be an object ({ owner, authorized: true }) — the operator's ownership attestation for the external-public shared plane.");
232
+ return invalid("`subject.publicTarget` must be an object, `{ owner, authorized: true }`, declaring that you own or operate the public deployment.");
232
233
  }
233
234
  const owner = str(raw.owner);
234
235
  if (!owner || !PUBLIC_TARGET_OWNER_PATTERN.test(owner)) {
235
236
  return invalid("`subject.publicTarget.owner` must be a public-safe operator/repo label (e.g. owner/repo); it is recorded in evidence, so it must carry no secret.");
236
237
  }
237
238
  if (raw.authorized !== true) {
238
- return invalid("`subject.publicTarget.authorized` must be true — you must attest you own/operate the public deployment used as the shared plane (author-trust; the harness cannot verify ownership).");
239
+ return invalid("`subject.publicTarget.authorized` must be true to declare that you own or operate the public deployment. humanish cannot check ownership, so the study states it.");
239
240
  }
240
241
  return { ok: true, value: { owner, authorized: true } };
241
242
  }
242
243
  // The product name interpolates into evidence labels and the composed prompt; the public-safe
243
- // token shape is the same load-bearing constraint as a lab id.
244
+ // token shape is the same strict constraint as a lab id.
244
245
  const PRODUCT_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_.-]*$/;
245
246
  function parseProduct(raw) {
246
247
  if (!isRecord(raw)) {
@@ -262,7 +263,7 @@ function parseProduct(raw) {
262
263
  if (upload.startsWith("/") ||
263
264
  /^[A-Za-z]:/.test(upload) ||
264
265
  upload.split(/[\\/]/).includes("..")) {
265
- return invalid("`subject.product.upload` must stay inside the project — no absolute paths and no `..` segments.");
266
+ return invalid("`subject.product.upload` must stay inside the project: use a relative path with no `..` segments.");
266
267
  }
267
268
  }
268
269
  const install = str(raw.install);
@@ -288,7 +289,7 @@ function parseProduct(raw) {
288
289
  },
289
290
  };
290
291
  }
291
- // Public-safe stance: a computer-use actor's ENTRY URL is always an app the lab owner runs on
292
+ // Public-safe stance: a computer-use actor's entry URL is always an app the lab owner runs on
292
293
  // loopback (inside the sandbox), never an arbitrary public site. (The constraint binds the
293
294
  // entry point; a navigation watchdog for mid-session escapes is a later slice.) Exported so
294
295
  // the engine re-enforces the same boundary on configs that arrive through the library API.
@@ -329,7 +330,7 @@ function parseServe(raw) {
329
330
  }
330
331
  const url = str(raw.url);
331
332
  if (!url || !isLoopbackUrl(url)) {
332
- return invalid("`subject.serve.url` must be a loopback http(s) URL (127.0.0.1 or localhost) — the app is served INSIDE the sandbox.");
333
+ return invalid("`subject.serve.url` must be a loopback http(s) URL (127.0.0.1 or localhost), because the app runs inside the sandbox.");
333
334
  }
334
335
  const serve = { start, url };
335
336
  const install = str(raw.install);
@@ -367,7 +368,7 @@ function parseClone(raw) {
367
368
  /**
368
369
  * Structural parse of `subject.localTree`, mirroring parseClone. Unlike parseClone (which
369
370
  * silently drops an out-of-range depth/fanout), an invalid exclude/maxArchiveBytes value is
370
- * REJECTED, never silently dropped: a caller who typed an empty exclude entry or a non-positive
371
+ * rejected, never silently dropped: a caller who typed an empty exclude entry or a non-positive
371
372
  * maxArchiveBytes almost certainly meant something, and the archive-size cap is a safety knob,
372
373
  * not a cosmetic default.
373
374
  */
@@ -2,7 +2,6 @@ import type { LabConfigParseFailure } from "../types.js";
2
2
  export declare const REPO_SLUG_PATTERN: RegExp;
3
3
  export declare const ENV_NAME_PATTERN: RegExp;
4
4
  export declare function invalid(message: string): LabConfigParseFailure;
5
- export declare function isRecord(value: unknown): value is Record<string, unknown>;
6
5
  export declare function str(value: unknown): string | undefined;
7
6
  export declare function optionalStr(key: string, value: unknown): Record<string, string>;
8
7
  export declare function strList(value: unknown): string[] | undefined;
@@ -12,6 +11,6 @@ export declare function strList(value: unknown): string[] | undefined;
12
11
  */
13
12
  export declare function readPositiveInt(value: string | undefined, fallback: number): number;
14
13
  export declare function posInt(value: unknown): number | undefined;
15
- /** A non-negative finite number (0 allowed — caps default to 0 = no-spend). Accepts a numeric
14
+ /** A non-negative finite number (0 allowed; caps default to 0 = no-spend). Accepts a numeric
16
15
  * string too, since YAML scalars can arrive as strings. */
17
16
  export declare function nonNegNumber(value: unknown): number | undefined;
@@ -1,11 +1,8 @@
1
- // The slug interpolates into an in-sandbox shell command; the strict shape is load-bearing.
1
+ // The slug interpolates into an in-sandbox shell command, so its shape is strict.
2
2
  export const REPO_SLUG_PATTERN = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
3
3
  export const ENV_NAME_PATTERN = /^[A-Z][A-Z0-9_]*$/;
4
4
  export function invalid(message) {
5
- return { ok: false, error: { code: "HUMANISH_LAB_INVALID", message } };
6
- }
7
- export function isRecord(value) {
8
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
5
+ return { ok: false, error: { code: "HUMANISH_STUDY_INVALID", message } };
9
6
  }
10
7
  export function str(value) {
11
8
  return typeof value === "string" && value.trim() ? value.trim() : undefined;
@@ -51,7 +48,7 @@ export function posInt(value) {
51
48
  }
52
49
  return undefined;
53
50
  }
54
- /** A non-negative finite number (0 allowed — caps default to 0 = no-spend). Accepts a numeric
51
+ /** A non-negative finite number (0 allowed; caps default to 0 = no-spend). Accepts a numeric
55
52
  * string too, since YAML scalars can arrive as strings. */
56
53
  export function nonNegNumber(value) {
57
54
  if (typeof value === "number" && Number.isFinite(value) && value >= 0) {