humanish 0.107.0 → 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 (521) hide show
  1. package/ARCHITECTURE.md +22 -17
  2. package/CHANGELOG.md +14 -0
  3. package/CONTEXT.md +51 -44
  4. package/README.md +145 -172
  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-ui.js +1 -1
  14. package/dist/actors/codex/codex-admission.d.ts +91 -0
  15. package/dist/actors/codex/codex-admission.js +150 -0
  16. package/dist/actors/codex/restricted-executable.d.ts +4 -4
  17. package/dist/actors/codex/restricted-executable.js +8 -8
  18. package/dist/actors/codex/restricted-launch.d.ts +2 -1
  19. package/dist/actors/codex/restricted-launch.js +1 -1
  20. package/dist/actors/codex/restricted-participant.js +8 -6
  21. package/dist/actors/codex/restricted-policy.d.ts +2 -2
  22. package/dist/actors/codex/restricted-policy.js +2 -2
  23. package/dist/actors/codex/restricted-session.d.ts +2 -2
  24. package/dist/actors/codex/restricted-session.js +10 -5
  25. package/dist/actors/computer-use/actor.d.ts +16 -16
  26. package/dist/actors/computer-use/actor.js +2 -2
  27. package/dist/actors/computer-use/admission-limit.d.ts +1 -1
  28. package/dist/actors/computer-use/admission-limit.js +1 -1
  29. package/dist/actors/computer-use/loop/actions.d.ts +3 -3
  30. package/dist/actors/computer-use/loop/actions.js +10 -10
  31. package/dist/actors/computer-use/loop/backstop.d.ts +3 -3
  32. package/dist/actors/computer-use/loop/backstop.js +14 -12
  33. package/dist/actors/computer-use/loop/ending.d.ts +5 -5
  34. package/dist/actors/computer-use/loop/ending.js +7 -7
  35. package/dist/actors/computer-use/loop/observation.d.ts +2 -2
  36. package/dist/actors/computer-use/loop/observation.js +9 -9
  37. package/dist/actors/computer-use/loop/provider-call.d.ts +1 -1
  38. package/dist/actors/computer-use/loop/provider-call.js +1 -1
  39. package/dist/actors/computer-use/loop/session.js +3 -3
  40. package/dist/actors/computer-use/loop/spend.d.ts +1 -1
  41. package/dist/actors/computer-use/loop/spend.js +1 -1
  42. package/dist/actors/computer-use/loop/trace.js +9 -8
  43. package/dist/actors/computer-use/loop/types.d.ts +25 -23
  44. package/dist/actors/computer-use/loop/usage.js +2 -2
  45. package/dist/actors/computer-use/openai-context.d.ts +37 -0
  46. package/dist/actors/computer-use/openai-context.js +214 -0
  47. package/dist/actors/computer-use/openai-provider.d.ts +11 -8
  48. package/dist/actors/computer-use/openai-provider.js +45 -34
  49. package/dist/actors/computer-use/openai-requests.d.ts +36 -10
  50. package/dist/actors/computer-use/openai-requests.js +82 -15
  51. package/dist/actors/computer-use/openai-wire.d.ts +19 -7
  52. package/dist/actors/computer-use/openai-wire.js +36 -34
  53. package/dist/actors/contract.d.ts +78 -43
  54. package/dist/actors/contract.js +29 -23
  55. package/dist/actors/local-agent/claude-session.d.ts +1 -1
  56. package/dist/actors/local-agent/claude-session.js +10 -9
  57. package/dist/actors/local-agent/cli.d.ts +8 -6
  58. package/dist/actors/local-agent/cli.js +24 -28
  59. package/dist/actors/local-agent/readiness.d.ts +2 -2
  60. package/dist/actors/local-agent/readiness.js +4 -4
  61. package/dist/actors/reasoning-effort.js +6 -6
  62. package/dist/actors/registry.d.ts +10 -10
  63. package/dist/actors/registry.js +18 -17
  64. package/dist/actors/scripted-browser/actor.d.ts +8 -8
  65. package/dist/actors/scripted-browser/actor.js +16 -16
  66. package/dist/actors/scripted-browser/browser-command.js +1 -1
  67. package/dist/actors/scripted-browser/steps.js +1 -1
  68. package/dist/actors/scripted-browser/types.d.ts +1 -1
  69. package/dist/actors/stop-conditions.d.ts +1 -1
  70. package/dist/analysis/codex-config.d.ts +6 -2
  71. package/dist/analysis/codex-config.js +22 -14
  72. package/dist/analysis/service.js +5 -5
  73. package/dist/cli/commands/analyze.js +19 -16
  74. package/dist/cli/commands/codex.js +9 -9
  75. package/dist/cli/commands/comms.js +54 -33
  76. package/dist/cli/commands/feedback.js +20 -21
  77. package/dist/cli/commands/lab-format.d.ts +5 -4
  78. package/dist/cli/commands/lab-format.js +35 -23
  79. package/dist/cli/commands/lab-route-computer-use.js +6 -7
  80. package/dist/cli/commands/lab-route-preview.js +4 -3
  81. package/dist/cli/commands/lab-route-shared-world.js +3 -3
  82. package/dist/cli/commands/lab-run.js +10 -11
  83. package/dist/cli/commands/lab-scorer.d.ts +2 -2
  84. package/dist/cli/commands/lab-scorer.js +1 -1
  85. package/dist/cli/commands/migrate.d.ts +4 -0
  86. package/dist/cli/commands/migrate.js +65 -0
  87. package/dist/cli/commands/observe.js +147 -108
  88. package/dist/cli/commands/run-command.d.ts +10 -0
  89. package/dist/cli/commands/run-command.js +85 -0
  90. package/dist/cli/commands/runs.js +137 -138
  91. package/dist/cli/commands/setup.d.ts +2 -2
  92. package/dist/cli/commands/setup.js +26 -21
  93. package/dist/cli/commands/study.d.ts +3 -0
  94. package/dist/cli/commands/study.js +182 -0
  95. package/dist/cli/commands/tui.d.ts +4 -4
  96. package/dist/cli/commands/tui.js +21 -21
  97. package/dist/cli/commands/watch.js +60 -75
  98. package/dist/cli/deprecations.d.ts +18 -0
  99. package/dist/cli/deprecations.js +48 -0
  100. package/dist/cli/doctor.d.ts +15 -5
  101. package/dist/cli/doctor.js +103 -48
  102. package/dist/cli/first-run-path.d.ts +12 -10
  103. package/dist/cli/first-run-path.js +30 -28
  104. package/dist/cli/io.d.ts +41 -6
  105. package/dist/cli/io.js +97 -22
  106. package/dist/cli/observer-follow.d.ts +2 -2
  107. package/dist/cli/observer-follow.js +5 -6
  108. package/dist/cli/orientation.d.ts +6 -3
  109. package/dist/cli/orientation.js +43 -39
  110. package/dist/cli/product-sentence.d.ts +6 -0
  111. package/dist/cli/product-sentence.js +6 -0
  112. package/dist/cli/program.d.ts +6 -1
  113. package/dist/cli/program.js +109 -40
  114. package/dist/cli/telemetry.d.ts +15 -15
  115. package/dist/cli/telemetry.js +48 -40
  116. package/dist/cli.js +1 -1
  117. package/dist/comms/capture-surface.d.ts +13 -12
  118. package/dist/comms/capture-surface.js +23 -22
  119. package/dist/comms/catch-host.d.ts +7 -7
  120. package/dist/comms/catch-host.js +14 -14
  121. package/dist/comms/email-catch.d.ts +3 -3
  122. package/dist/comms/email-catch.js +8 -8
  123. package/dist/comms/external-evidence.js +2 -2
  124. package/dist/comms/extract.js +2 -2
  125. package/dist/comms/fake-inbox.d.ts +2 -2
  126. package/dist/comms/fake-inbox.js +2 -2
  127. package/dist/comms/receiving-render.d.ts +1 -1
  128. package/dist/comms/receiving-render.js +2 -2
  129. package/dist/comms/sandbox-catch-script.d.ts +6 -6
  130. package/dist/comms/sandbox-catch-script.js +6 -6
  131. package/dist/comms/sandbox-catch.d.ts +36 -37
  132. package/dist/comms/sandbox-catch.js +34 -35
  133. package/dist/comms/setup.js +42 -8
  134. package/dist/comms/thread-evidence.d.ts +1 -1
  135. package/dist/comms/thread-evidence.js +6 -6
  136. package/dist/comms/types.d.ts +6 -6
  137. package/dist/comms/types.js +3 -3
  138. package/dist/evidence/frame-signature.d.ts +1 -1
  139. package/dist/evidence/frame-signature.js +4 -4
  140. package/dist/evidence/redaction.d.ts +3 -3
  141. package/dist/evidence/redaction.js +4 -4
  142. package/dist/feedback/draft.js +36 -78
  143. package/dist/feedback/export-bundle.js +1 -1
  144. package/dist/feedback/export.d.ts +5 -1
  145. package/dist/feedback/export.js +7 -8
  146. package/dist/feedback/feedback.d.ts +2 -2
  147. package/dist/feedback/feedback.js +21 -3
  148. package/dist/guest/chromium-text.js +2 -2
  149. package/dist/guest/desktop-native.d.ts +1 -1
  150. package/dist/guest/runtime.js +1 -1
  151. package/dist/index.d.ts +12 -10
  152. package/dist/index.js +8 -6
  153. package/dist/keys/key-resolution.d.ts +5 -5
  154. package/dist/keys/key-resolution.js +23 -23
  155. package/dist/lab/adapter-extension.d.ts +7 -7
  156. package/dist/lab/adapter-extension.js +13 -36
  157. package/dist/lab/adapter-scorer-loader.d.ts +7 -7
  158. package/dist/lab/adapter-scorer-loader.js +32 -32
  159. package/dist/lab/composition-rules.js +42 -42
  160. package/dist/lab/config.js +65 -25
  161. package/dist/lab/deprecated-fields.d.ts +10 -0
  162. package/dist/lab/deprecated-fields.js +24 -0
  163. package/dist/lab/device-presets.d.ts +8 -8
  164. package/dist/lab/device-presets.js +11 -11
  165. package/dist/lab/discover.d.ts +13 -9
  166. package/dist/lab/discover.js +117 -57
  167. package/dist/lab/doctor.d.ts +10 -3
  168. package/dist/lab/doctor.js +47 -9
  169. package/dist/lab/init-templates.js +147 -147
  170. package/dist/lab/init.d.ts +1 -1
  171. package/dist/lab/init.js +41 -9
  172. package/dist/lab/keys.d.ts +2 -0
  173. package/dist/lab/keys.js +42 -8
  174. package/dist/lab/lab-deps.d.ts +7 -12
  175. package/dist/lab/parse/actors.js +9 -9
  176. package/dist/lab/parse/comms.js +5 -5
  177. package/dist/lab/parse/execution.js +13 -13
  178. package/dist/lab/parse/study-v3.d.ts +23 -0
  179. package/dist/lab/parse/study-v3.js +259 -0
  180. package/dist/lab/parse/subject-state.d.ts +7 -7
  181. package/dist/lab/parse/subject-state.js +16 -16
  182. package/dist/lab/parse/subject.js +28 -28
  183. package/dist/lab/parse/values.d.ts +1 -1
  184. package/dist/lab/parse/values.js +3 -3
  185. package/dist/lab/persona-resolve.d.ts +4 -4
  186. package/dist/lab/persona-resolve.js +4 -4
  187. package/dist/lab/plan-participants.d.ts +2 -2
  188. package/dist/lab/plan-types.d.ts +4 -4
  189. package/dist/lab/plan.d.ts +1 -7
  190. package/dist/lab/plan.js +10 -41
  191. package/dist/lab/preflight-probes.js +11 -11
  192. package/dist/lab/preflight-result.d.ts +2 -1
  193. package/dist/lab/preflight-result.js +5 -4
  194. package/dist/lab/preflight.d.ts +7 -5
  195. package/dist/lab/preflight.js +5 -5
  196. package/dist/lab/routing.d.ts +31 -25
  197. package/dist/lab/routing.js +60 -29
  198. package/dist/lab/run-lab-events.d.ts +0 -3
  199. package/dist/lab/run-lab-events.js +0 -6
  200. package/dist/lab/run-lab-homes.d.ts +22 -10
  201. package/dist/lab/run-lab-homes.js +5 -1
  202. package/dist/lab/run-lab-options.d.ts +1 -1
  203. package/dist/lab/run-lab-options.js +16 -13
  204. package/dist/lab/summary.d.ts +7 -6
  205. package/dist/lab/summary.js +10 -10
  206. package/dist/lab/tasks.d.ts +15 -15
  207. package/dist/lab/tasks.js +14 -14
  208. package/dist/lab/types.d.ts +101 -95
  209. package/dist/lab/types.js +2 -0
  210. package/dist/lab/validation.d.ts +13 -13
  211. package/dist/lab/validation.js +44 -42
  212. package/dist/lab/warnings.d.ts +10 -0
  213. package/dist/lab/warnings.js +61 -33
  214. package/dist/library-aliases.d.ts +56 -0
  215. package/dist/library-aliases.js +22 -0
  216. package/dist/observer/artifact.d.ts +1 -1
  217. package/dist/observer/artifact.js +6 -6
  218. package/dist/observer/data.d.ts +6 -6
  219. package/dist/observer/data.js +5 -4
  220. package/dist/observer/exposure.d.ts +1 -1
  221. package/dist/observer/exposure.js +11 -11
  222. package/dist/observer/http.js +1 -1
  223. package/dist/observer/library.d.ts +1 -1
  224. package/dist/observer/library.js +4 -2
  225. package/dist/observer/listen.js +5 -4
  226. package/dist/observer/render.d.ts +1 -1
  227. package/dist/observer/render.js +10 -5
  228. package/dist/observer/run-routes.d.ts +3 -3
  229. package/dist/observer/run-routes.js +1 -1
  230. package/dist/observer/serve.js +7 -3
  231. package/dist/observer/tunnel.js +1 -1
  232. package/dist/observer-app.html +4 -4
  233. package/dist/routes/computer-use/bundle-parts.d.ts +9 -9
  234. package/dist/routes/computer-use/bundle-parts.js +15 -15
  235. package/dist/routes/computer-use/bundle.js +3 -3
  236. package/dist/routes/computer-use/e2b-desktop/comms.d.ts +1 -1
  237. package/dist/routes/computer-use/e2b-desktop/comms.js +14 -14
  238. package/dist/routes/computer-use/e2b-desktop/desktop.js +4 -4
  239. package/dist/routes/computer-use/e2b-desktop/fidelity.d.ts +4 -3
  240. package/dist/routes/computer-use/e2b-desktop/fidelity.js +11 -10
  241. package/dist/routes/computer-use/e2b-desktop/prepare.js +7 -7
  242. package/dist/routes/computer-use/e2b-desktop/start.js +9 -8
  243. package/dist/routes/computer-use/e2b-desktop/teardown.js +5 -4
  244. package/dist/routes/computer-use/external-comms.d.ts +1 -1
  245. package/dist/routes/computer-use/external-comms.js +1 -1
  246. package/dist/routes/computer-use/fanout-bundle.js +18 -18
  247. package/dist/routes/computer-use/fanout-records.js +10 -11
  248. package/dist/routes/computer-use/live-flush.d.ts +1 -1
  249. package/dist/routes/computer-use/live-flush.js +6 -6
  250. package/dist/routes/computer-use/live-phase.d.ts +4 -2
  251. package/dist/routes/computer-use/live-phase.js +6 -6
  252. package/dist/routes/computer-use/local-vm.js +1 -1
  253. package/dist/routes/computer-use/participant-desktop.d.ts +4 -4
  254. package/dist/routes/computer-use/participant-desktop.js +4 -4
  255. package/dist/routes/computer-use/participant-execution.d.ts +4 -4
  256. package/dist/routes/computer-use/participant-execution.js +9 -9
  257. package/dist/routes/computer-use/participant-model.d.ts +6 -0
  258. package/dist/routes/computer-use/participant-model.js +31 -13
  259. package/dist/routes/computer-use/participant-prompt.d.ts +11 -11
  260. package/dist/routes/computer-use/participant-prompt.js +6 -6
  261. package/dist/routes/computer-use/participant-runs.d.ts +7 -7
  262. package/dist/routes/computer-use/participant-runs.js +13 -13
  263. package/dist/routes/computer-use/plan.js +22 -22
  264. package/dist/routes/computer-use/preflight.js +21 -21
  265. package/dist/routes/computer-use/rerun-selection.js +1 -1
  266. package/dist/routes/computer-use/result.js +11 -11
  267. package/dist/routes/computer-use/route.d.ts +2 -1
  268. package/dist/routes/computer-use/route.js +15 -14
  269. package/dist/routes/computer-use/self-report.d.ts +7 -7
  270. package/dist/routes/computer-use/self-report.js +31 -31
  271. package/dist/routes/computer-use/setup.d.ts +1 -1
  272. package/dist/routes/computer-use/setup.js +17 -18
  273. package/dist/routes/computer-use/single-bundle.d.ts +7 -7
  274. package/dist/routes/computer-use/single-bundle.js +28 -30
  275. package/dist/routes/computer-use/subject-projection.d.ts +1 -1
  276. package/dist/routes/computer-use/subject-projection.js +6 -6
  277. package/dist/routes/computer-use/types.d.ts +40 -42
  278. package/dist/routes/computer-use/types.js +10 -10
  279. package/dist/routes/preview.d.ts +8 -2
  280. package/dist/routes/preview.js +8 -4
  281. package/dist/routes/scripted/bundle.d.ts +2 -2
  282. package/dist/routes/scripted/bundle.js +13 -12
  283. package/dist/routes/scripted/plan.d.ts +1 -1
  284. package/dist/routes/scripted/plan.js +9 -9
  285. package/dist/routes/scripted/result.js +5 -5
  286. package/dist/routes/scripted/route.js +5 -6
  287. package/dist/routes/scripted/scenario.js +2 -2
  288. package/dist/routes/scripted/setup.js +7 -7
  289. package/dist/routes/scripted/subject.js +1 -1
  290. package/dist/routes/scripted/surface-records.js +3 -3
  291. package/dist/routes/scripted/types.d.ts +3 -5
  292. package/dist/routes/scripted/types.js +1 -1
  293. package/dist/routes/shared-world/bundle-records.js +9 -8
  294. package/dist/routes/shared-world/bundle.d.ts +5 -5
  295. package/dist/routes/shared-world/bundle.js +40 -38
  296. package/dist/routes/shared-world/checkpoints.d.ts +1 -1
  297. package/dist/routes/shared-world/checkpoints.js +1 -1
  298. package/dist/routes/shared-world/comms.d.ts +5 -5
  299. package/dist/routes/shared-world/comms.js +14 -14
  300. package/dist/routes/shared-world/external-public.d.ts +4 -3
  301. package/dist/routes/shared-world/external-public.js +16 -12
  302. package/dist/routes/shared-world/handoff.d.ts +7 -6
  303. package/dist/routes/shared-world/handoff.js +65 -57
  304. package/dist/routes/shared-world/lobby-code.d.ts +2 -2
  305. package/dist/routes/shared-world/lobby-code.js +9 -9
  306. package/dist/routes/shared-world/participant-specs.d.ts +7 -6
  307. package/dist/routes/shared-world/participant-specs.js +31 -24
  308. package/dist/routes/shared-world/plan.js +7 -7
  309. package/dist/routes/shared-world/provenance.d.ts +2 -2
  310. package/dist/routes/shared-world/provenance.js +2 -2
  311. package/dist/routes/shared-world/provisioned.js +14 -13
  312. package/dist/routes/shared-world/result.js +20 -21
  313. package/dist/routes/shared-world/route.js +23 -22
  314. package/dist/routes/shared-world/setup.d.ts +3 -3
  315. package/dist/routes/shared-world/setup.js +4 -4
  316. package/dist/routes/shared-world/types.d.ts +30 -28
  317. package/dist/routes/shared-world/types.js +4 -4
  318. package/dist/routes/terminal/adapter.d.ts +3 -3
  319. package/dist/routes/terminal/adapter.js +5 -5
  320. package/dist/routes/terminal/bundle.d.ts +3 -3
  321. package/dist/routes/terminal/bundle.js +22 -21
  322. package/dist/routes/terminal/credentials.d.ts +4 -4
  323. package/dist/routes/terminal/credentials.js +7 -7
  324. package/dist/routes/terminal/dry-run.js +4 -4
  325. package/dist/routes/terminal/encoding.js +2 -2
  326. package/dist/routes/terminal/ledger.d.ts +3 -3
  327. package/dist/routes/terminal/ledger.js +14 -14
  328. package/dist/routes/terminal/live-finish.js +3 -3
  329. package/dist/routes/terminal/live-sandbox.js +12 -13
  330. package/dist/routes/terminal/persona.js +1 -1
  331. package/dist/routes/terminal/plan.js +11 -11
  332. package/dist/routes/terminal/recorder.js +2 -2
  333. package/dist/routes/terminal/result.js +8 -8
  334. package/dist/routes/terminal/route.js +6 -7
  335. package/dist/routes/terminal/sandbox.d.ts +1 -1
  336. package/dist/routes/terminal/sandbox.js +1 -1
  337. package/dist/routes/terminal/session.js +8 -7
  338. package/dist/routes/terminal/token-usage.d.ts +1 -1
  339. package/dist/routes/terminal/token-usage.js +7 -8
  340. package/dist/routes/terminal/trace.d.ts +2 -2
  341. package/dist/routes/terminal/trace.js +2 -2
  342. package/dist/routes/terminal/types.d.ts +26 -28
  343. package/dist/routes/terminal/types.js +3 -3
  344. package/dist/run/bundle-shape.js +3 -3
  345. package/dist/run/bundle.d.ts +46 -46
  346. package/dist/run/bundle.js +2 -1
  347. package/dist/run/cost-summary.js +3 -3
  348. package/dist/run/costs.js +7 -1
  349. package/dist/run/detail.d.ts +6 -6
  350. package/dist/run/detail.js +4 -4
  351. package/dist/run/dry-run-inputs.js +1 -1
  352. package/dist/run/dry-run.js +18 -18
  353. package/dist/run/judge.d.ts +19 -13
  354. package/dist/run/judge.js +18 -8
  355. package/dist/run/paths.d.ts +1 -1
  356. package/dist/run/paths.js +1 -1
  357. package/dist/run/pricing.d.ts +11 -6
  358. package/dist/run/pricing.js +20 -13
  359. package/dist/run/projection.d.ts +14 -14
  360. package/dist/run/projection.js +15 -15
  361. package/dist/run/reclaim.js +2 -2
  362. package/dist/run/results.d.ts +5 -5
  363. package/dist/run/run-index.d.ts +8 -6
  364. package/dist/run/run-index.js +15 -16
  365. package/dist/run/run-not-found.d.ts +5 -0
  366. package/dist/run/run-not-found.js +15 -0
  367. package/dist/run/run.d.ts +5 -4
  368. package/dist/run/run.js +1 -1
  369. package/dist/run/sandbox-receipts.d.ts +2 -2
  370. package/dist/run/sandbox-receipts.js +3 -3
  371. package/dist/run/shared-world-evidence.d.ts +36 -35
  372. package/dist/run/shared-world-evidence.js +4 -4
  373. package/dist/run/shared-world-shape.d.ts +1 -1
  374. package/dist/run/shared-world-shape.js +6 -6
  375. package/dist/run/stats.d.ts +12 -6
  376. package/dist/run/stats.js +116 -58
  377. package/dist/run/status.d.ts +8 -29
  378. package/dist/run/status.js +5 -27
  379. package/dist/run/stored-runs.js +2 -1
  380. package/dist/run/stream-shape.js +1 -1
  381. package/dist/run/streams.d.ts +6 -6
  382. package/dist/run/study-provenance.d.ts +47 -0
  383. package/dist/run/study-provenance.js +63 -0
  384. package/dist/run/study-result.d.ts +16 -0
  385. package/dist/run/study-result.js +9 -0
  386. package/dist/run/text.d.ts +2 -0
  387. package/dist/run/text.js +4 -0
  388. package/dist/run-lab.d.ts +7 -7
  389. package/dist/run-lab.js +1 -3
  390. package/dist/study/convert.d.ts +33 -0
  391. package/dist/study/convert.js +432 -0
  392. package/dist/study/field-paths.d.ts +12 -0
  393. package/dist/study/field-paths.js +126 -0
  394. package/dist/study/files.d.ts +26 -0
  395. package/dist/study/files.js +43 -0
  396. package/dist/study/migrate.d.ts +48 -0
  397. package/dist/study/migrate.js +408 -0
  398. package/dist/study/owned-files.d.ts +38 -0
  399. package/dist/study/owned-files.js +182 -0
  400. package/dist/subject/clone.d.ts +1 -1
  401. package/dist/subject/desktop-cli.d.ts +1 -1
  402. package/dist/subject/desktop-cli.js +2 -2
  403. package/dist/subject/local-tree-archive.js +1 -1
  404. package/dist/subject/local-tree.d.ts +1 -1
  405. package/dist/subject/node-bootstrap.js +1 -1
  406. package/dist/subject/runtime.js +8 -9
  407. package/dist/subject/serve.d.ts +1 -1
  408. package/dist/subject/serve.js +3 -3
  409. package/dist/subject/state.d.ts +1 -1
  410. package/dist/subject/steps.d.ts +5 -5
  411. package/dist/subject/steps.js +4 -4
  412. package/dist/substrates/command-failure.d.ts +3 -3
  413. package/dist/substrates/command-failure.js +3 -3
  414. package/dist/substrates/detached.d.ts +6 -6
  415. package/dist/substrates/detached.js +7 -7
  416. package/dist/substrates/e2b/cdp-probe.d.ts +9 -9
  417. package/dist/substrates/e2b/cdp-probe.js +14 -17
  418. package/dist/substrates/e2b/desktop-browser.d.ts +2 -2
  419. package/dist/substrates/e2b/desktop-browser.js +8 -8
  420. package/dist/substrates/e2b/desktop-cdp.d.ts +9 -9
  421. package/dist/substrates/e2b/desktop-cdp.js +8 -8
  422. package/dist/substrates/e2b/desktop-executor.d.ts +1 -1
  423. package/dist/substrates/e2b/desktop-executor.js +1 -1
  424. package/dist/substrates/e2b/desktop-geometry.d.ts +3 -3
  425. package/dist/substrates/e2b/desktop-geometry.js +7 -7
  426. package/dist/substrates/e2b/desktop-media.d.ts +1 -1
  427. package/dist/substrates/e2b/desktop-media.js +1 -1
  428. package/dist/substrates/e2b/desktop-screenshot-cleanup.d.ts +3 -3
  429. package/dist/substrates/e2b/desktop-screenshot-cleanup.js +3 -3
  430. package/dist/substrates/e2b/sandbox.d.ts +1 -1
  431. package/dist/substrates/e2b/sandbox.js +1 -1
  432. package/dist/substrates/e2b/sdk.d.ts +11 -11
  433. package/dist/substrates/e2b/sdk.js +6 -6
  434. package/dist/tui/actions.d.ts +1 -1
  435. package/dist/tui/actions.js +13 -13
  436. package/dist/tui/contract.d.ts +16 -16
  437. package/dist/tui/contract.js +10 -10
  438. package/dist/tui/launch.d.ts +4 -4
  439. package/dist/tui/launch.js +18 -19
  440. package/dist/tui/project.js +5 -5
  441. package/dist/tui-app.js +82 -82
  442. package/dist/verify/actor.d.ts +8 -8
  443. package/dist/verify/actor.js +36 -36
  444. package/dist/verify/artifacts.d.ts +2 -2
  445. package/dist/verify/artifacts.js +3 -3
  446. package/dist/verify/context-growth.d.ts +9 -0
  447. package/dist/verify/context-growth.js +62 -0
  448. package/dist/verify/costs.d.ts +6 -6
  449. package/dist/verify/costs.js +8 -8
  450. package/dist/verify/shared-world-concurrent.d.ts +2 -2
  451. package/dist/verify/shared-world-concurrent.js +79 -76
  452. package/dist/verify/shared-world.d.ts +2 -2
  453. package/dist/verify/shared-world.js +16 -16
  454. package/dist/verify/subject.d.ts +4 -4
  455. package/dist/verify/subject.js +18 -18
  456. package/dist/verify/verify.js +92 -43
  457. package/docs/README.md +25 -32
  458. package/docs/architecture/actor-contract.md +56 -87
  459. package/docs/architecture/desktop-sessions.md +7 -7
  460. package/docs/architecture/external-public-shared-world.md +61 -59
  461. package/docs/architecture/github-feedback-loop.md +1 -4
  462. package/docs/architecture/guest-desktop.md +2 -2
  463. package/docs/architecture/observer.md +34 -34
  464. package/docs/architecture/restricted-codex-analysis.md +34 -28
  465. package/docs/architecture/serve.md +34 -37
  466. package/docs/architecture/state-driven-executor.md +55 -58
  467. package/docs/architecture/task-protocol-support.md +5 -5
  468. package/docs/architecture/terminal-product-route.md +67 -107
  469. package/docs/contracts/README.md +24 -0
  470. package/docs/contracts/adapter-admission.md +3 -3
  471. package/docs/contracts/adapter-fixtures.md +2 -5
  472. package/docs/contracts/core.md +1 -3
  473. package/docs/contracts/errors.md +62 -0
  474. package/docs/contracts/feedback.md +8 -9
  475. package/docs/contracts/policy.md +7 -12
  476. package/docs/contracts/run-bundle.md +45 -44
  477. package/docs/contracts/schemas.md +421 -362
  478. package/docs/contracts/study-analysis.md +3 -3
  479. package/docs/contracts/study-costs.md +11 -7
  480. package/docs/decisions/0001-run-bundle-is-the-source-of-truth.md +1 -1
  481. package/docs/decisions/0002-observer-is-one-self-contained-file.md +2 -2
  482. package/docs/decisions/0003-contained-paths-and-cleanup-authority.md +3 -3
  483. package/docs/principles/actor-fidelity.md +4 -4
  484. package/docs/principles/engineering.md +3 -3
  485. package/docs/principles/invariants-and-defaults.md +38 -38
  486. package/docs/principles/self-driving-harness.md +1 -5
  487. package/docs/principles/three-roles.md +1 -1
  488. package/docs/product/automatic-analysis.md +7 -7
  489. package/examples/participant/README.md +5 -5
  490. package/examples/participant/run.mjs +11 -10
  491. package/examples/scorer/README.md +3 -3
  492. package/examples/scorer/run.mjs +11 -12
  493. package/package.json +16 -17
  494. package/skills/humanish/SKILL.md +60 -58
  495. package/skills/humanish/agents/openai.yaml +1 -1
  496. package/AGENTS.md +0 -99
  497. package/CONTRIBUTING.md +0 -148
  498. package/dist/actors/codex/qualified-versions.d.ts +0 -72
  499. package/dist/actors/codex/qualified-versions.js +0 -132
  500. package/dist/cli/commands/lab.d.ts +0 -3
  501. package/dist/cli/commands/lab.js +0 -199
  502. package/dist/cli/renamed-options.d.ts +0 -11
  503. package/dist/cli/renamed-options.js +0 -18
  504. package/dist/observer/static.d.ts +0 -39
  505. package/dist/observer/static.js +0 -369
  506. package/docs/architecture/comms-inbox.md +0 -122
  507. package/docs/architecture/desktop-recording.md +0 -80
  508. package/docs/architecture/local-browser-runtime.md +0 -178
  509. package/docs/architecture/observer-review.md +0 -144
  510. package/docs/architecture/participant-media.md +0 -90
  511. package/docs/architecture/project-layout.md +0 -62
  512. package/docs/architecture/real-email-receiving.md +0 -136
  513. package/docs/assets/humanish-drawdb-hero.png +0 -0
  514. package/docs/assets/humanish-observer-hero.png +0 -0
  515. package/docs/goals/current.md +0 -207
  516. package/docs/product/lobby-trivia-3player-external-public.md +0 -77
  517. package/docs/product/open-source-install-experience.md +0 -240
  518. package/docs/ramp/README.md +0 -295
  519. package/docs/release/open-source-readiness.md +0 -222
  520. package/docs/release/public-readiness-standard.md +0 -210
  521. package/docs/roadmap/world-class-open-source-v0.md +0 -311
package/dist/run/stats.js CHANGED
@@ -5,12 +5,10 @@ import path from "node:path";
5
5
  import { readRunIndex } from "./run-index.js";
6
6
  import { addCostTotals, emptyCostTotals, readCostTotals, } from "./costs.js";
7
7
  import { round6 } from "./pricing.js";
8
- const STATS_SCHEMA = "humanish.stats.v1";
9
- const STATS_NOTE = "Every dollar figure is a retained rate-table estimate, never a provider charge. " +
10
- "Known spend includes participant/desktop estimates and all distinct recorded analysis attempts; reuse is counted once. " +
11
- "Unknown amounts are excluded, not $0. Missing historical attempts cannot be reconstructed. " +
12
- "Analysis is attributed to its run's start date, including later reruns. " +
13
- "JSON estimatedSpendUsd and medianCostUsd retain their participant/desktop-only meaning; costs includes analysis.";
8
+ import { plural } from "./text.js";
9
+ const STATS_SCHEMA = "humanish.stats.v2";
10
+ // docs/contracts/study-costs.md holds the accounting rules this sentence summarizes.
11
+ const STATS_NOTE = "Dollar figures are estimates from humanish's rate table, never provider charges, and a cost humanish does not know is left out of every total.";
14
12
  function median(values) {
15
13
  if (values.length === 0)
16
14
  return undefined;
@@ -28,15 +26,26 @@ function addParticipants(into, entry) {
28
26
  into.reachedGoal += entry.participants.reachedGoal;
29
27
  into.reportedFriction += entry.participants.reportedFriction ?? 0;
30
28
  }
29
+ /**
30
+ * A run's participant and desktop estimate: its recorded figure, or $0 for a dry run that records
31
+ * none, since a dry run makes no model request and creates no desktop. Undefined when the cost is
32
+ * unknown, including a dry run that records an unknown (null) figure.
33
+ */
34
+ function runEstimateUsd(entry) {
35
+ const usd = entry.estimatedCostUsd;
36
+ if (typeof usd === "number" && Number.isFinite(usd) && usd >= 0)
37
+ return usd;
38
+ return usd === undefined && entry.mode === "dry-run" ? 0 : undefined;
39
+ }
31
40
  function entryTime(entry) {
32
41
  return entry.startedAt ?? entry.completedAt ?? entry.updatedAt;
33
42
  }
34
- function addToLabRow(labs, entry, priced, costs) {
35
- const labId = entry.lab?.id ?? "(no lab)";
43
+ function addToLabRow(labs, entry, runUsd, costs) {
44
+ const labId = entry.study?.id ?? "(no study)";
36
45
  let row = labs.get(labId);
37
46
  if (row === undefined) {
38
47
  row = {
39
- lab: labId,
48
+ study: labId,
40
49
  runs: 0,
41
50
  live: 0,
42
51
  dryRun: 0,
@@ -64,14 +73,14 @@ function addToLabRow(labs, entry, priced, costs) {
64
73
  }
65
74
  if (entry.mode === "live" && entry.durationMs !== undefined)
66
75
  row.durations.push(entry.durationMs);
67
- if (priced)
68
- row.runCosts.push(entry.estimatedCostUsd);
69
- else
76
+ if (runUsd === undefined)
70
77
  row.unpricedRuns += 1;
78
+ else
79
+ row.runCosts.push(runUsd);
71
80
  addParticipants(row.participants, entry);
72
81
  addCostTotals(row.costs, costs);
73
82
  }
74
- function addToDayRow(days, entry, priced, costs) {
83
+ function addToDayRow(days, entry, runUsd, costs) {
75
84
  const at = entryTime(entry);
76
85
  const day = at === undefined ? "(undated)" : at.slice(0, 10);
77
86
  let dayRow = days.get(day);
@@ -89,10 +98,10 @@ function addToDayRow(days, entry, priced, costs) {
89
98
  dayRow.runs += 1;
90
99
  if (entry.mode === "live")
91
100
  dayRow.live += 1;
92
- if (priced)
93
- dayRow.estimatedSpendUsd += entry.estimatedCostUsd;
94
- else
101
+ if (runUsd === undefined)
95
102
  dayRow.unpricedRuns += 1;
103
+ else
104
+ dayRow.estimatedSpendUsd += runUsd;
96
105
  addCostTotals(dayRow.costs, costs);
97
106
  }
98
107
  export async function computeStats(cwdInput, options = {}) {
@@ -127,7 +136,7 @@ export async function computeStats(cwdInput, options = {}) {
127
136
  })),
128
137
  ];
129
138
  const selected = entries.filter((entry) => {
130
- if (options.lab !== undefined && entry.lab?.id !== options.lab)
139
+ if (options.lab !== undefined && entry.study?.id !== options.lab)
131
140
  return false;
132
141
  if (sinceMs !== undefined) {
133
142
  const at = entryTime(entry);
@@ -164,18 +173,16 @@ export async function computeStats(cwdInput, options = {}) {
164
173
  totals.dryRun += 1;
165
174
  if (entry.liveness === "running")
166
175
  totals.running += 1;
167
- const priced = typeof entry.estimatedCostUsd === "number" &&
168
- Number.isFinite(entry.estimatedCostUsd) &&
169
- entry.estimatedCostUsd >= 0;
170
- if (priced)
171
- totals.estimatedSpendUsd += entry.estimatedCostUsd;
172
- else
176
+ const runUsd = runEstimateUsd(entry);
177
+ if (runUsd === undefined)
173
178
  totals.unpricedRuns += 1;
179
+ else
180
+ totals.estimatedSpendUsd += runUsd;
174
181
  addParticipants(totals.participants, entry);
175
182
  if (entry.verdict !== undefined)
176
183
  totals.verdicts[entry.verdict] = (totals.verdicts[entry.verdict] ?? 0) + 1;
177
- addToLabRow(labs, entry, priced, accounting.costs);
178
- addToDayRow(days, entry, priced, accounting.costs);
184
+ addToLabRow(labs, entry, runUsd, accounting.costs);
185
+ addToDayRow(days, entry, runUsd, accounting.costs);
179
186
  }
180
187
  const labRows = [...labs.values()]
181
188
  .map(({ durations, runCosts, ...row }) => ({
@@ -186,15 +193,15 @@ export async function computeStats(cwdInput, options = {}) {
186
193
  ...(runCosts.length === 0 ? {} : { medianCostUsd: round6(median(runCosts)) }),
187
194
  costSamples: runCosts.length,
188
195
  }))
189
- .sort((a, b) => b.runs - a.runs || a.lab.localeCompare(b.lab));
196
+ .sort((a, b) => b.runs - a.runs || a.study.localeCompare(b.study));
190
197
  return {
191
198
  schema: STATS_SCHEMA,
192
199
  ok: true,
193
200
  cwd,
194
201
  ...(options.since === undefined ? {} : { since: options.since }),
195
- ...(options.lab === undefined ? {} : { lab: options.lab }),
202
+ ...(options.lab === undefined ? {} : { study: options.lab }),
196
203
  totals: { ...totals, estimatedSpendUsd: round6(totals.estimatedSpendUsd) },
197
- labs: labRows,
204
+ studies: labRows,
198
205
  days: [...days.values()]
199
206
  .map((row) => ({ ...row, estimatedSpendUsd: round6(row.estimatedSpendUsd) }))
200
207
  .sort((a, b) => a.day.localeCompare(b.day)),
@@ -212,47 +219,98 @@ function knownMoney(value) {
212
219
  function minutes(ms) {
213
220
  return `${(ms / 60_000).toFixed(1)}m`;
214
221
  }
222
+ /**
223
+ * Whether every selected run is a dry run whose accounting is a complete $0: no recorded figure,
224
+ * no incomplete estimate and no analysis attempt. Only then does the one-line report hold.
225
+ */
226
+ function onlyFreeDryRuns(t) {
227
+ return (t.runs > 0 &&
228
+ t.runs === t.dryRun &&
229
+ t.costs.estimatedTotalUsd === 0 &&
230
+ t.costs.incompleteRunEstimates === 0 &&
231
+ t.costs.analysisAttempts === 0);
232
+ }
233
+ /** The run counts, with dry runs on their own line. */
234
+ function runCountLines(t) {
235
+ if (t.runs === 0)
236
+ return ["no runs yet; start one with humanish run first-run"];
237
+ if (onlyFreeDryRuns(t))
238
+ return [`${plural(t.dryRun, "dry run")} ($0); no live runs yet`];
239
+ const running = t.running > 0 ? ` (${t.running} still running)` : "";
240
+ return [`live runs: ${t.live}${running}`, ...(t.dryRun > 0 ? [`dry runs: ${t.dryRun}`] : [])];
241
+ }
242
+ /** Spend, analysis, participants and verdicts across the selected live runs. */
243
+ function totalsLines(t) {
244
+ const c = t.costs;
245
+ const incomplete = c.incompleteRunEstimates > 0
246
+ ? `; ${plural(c.incompleteRunEstimates, "run")} with incomplete accounting`
247
+ : "";
248
+ const unpriced = c.analysisUnpricedAttempts > 0 || c.analysisUnresolvedAttempts > 0
249
+ ? `; ${c.analysisUnpricedAttempts} unpriced, ${c.analysisUnresolvedAttempts} unresolved`
250
+ : "";
251
+ const verdicts = Object.entries(t.verdicts).map(([verdict, count]) => `${verdict} ${count}`);
252
+ return [
253
+ `known estimated spend: ${knownMoney(c.estimatedTotalUsd)}`,
254
+ `participants and desktops: ${knownMoney(c.runEstimatedUsd)}${incomplete}`,
255
+ c.analysisAttempts === 0
256
+ ? "analysis: none recorded"
257
+ : `analysis: ${knownMoney(c.analysisEstimatedUsd)} over ${plural(c.analysisAttempts, "recorded attempt")}${unpriced}`,
258
+ ...(c.analysisHistoryUncertainRuns > 0
259
+ ? [
260
+ `analysis history: ${plural(c.analysisHistoryUncertainRuns, "run")} missing or uncertain; --json lists each run`,
261
+ ]
262
+ : []),
263
+ ...(t.participants.total > 0
264
+ ? [
265
+ `participants: ${t.participants.reachedGoal} of ${t.participants.total} reached the goal, ${t.participants.reportedFriction} reported friction`,
266
+ ]
267
+ : []),
268
+ ...(verdicts.length > 0 ? [`verdicts: ${verdicts.join(", ")}`] : []),
269
+ ];
270
+ }
271
+ function dayLine(row) {
272
+ return `- ${row.day}: ${plural(row.runs, "run")}, ${row.live} live, known study spend ${knownMoney(row.costs.estimatedTotalUsd)}${unpricedTail(row.unpricedRuns, row.costs.analysisUnpricedAttempts)}`;
273
+ }
274
+ /** "; 2 unpriced runs, 1 unpriced analysis", or nothing when both are zero. */
275
+ function unpricedTail(runs, analyses) {
276
+ if (runs === 0 && analyses === 0)
277
+ return "";
278
+ return `; ${plural(runs, "unpriced run")}, ${plural(analyses, "unpriced analysis", "unpriced analyses")}`;
279
+ }
215
280
  export function formatStatsHuman(result) {
216
281
  if (!result.ok)
217
- return `${result.error.code}: ${result.error.message}\n`;
282
+ return { error: result.error };
218
283
  const t = result.totals;
219
284
  const scope = [
220
- result.lab === undefined ? undefined : `lab ${result.lab}`,
285
+ result.study === undefined ? undefined : `lab ${result.study}`,
221
286
  result.since === undefined ? undefined : `since ${result.since}`,
222
287
  ].filter((part) => part !== undefined);
223
288
  const lines = [
224
289
  `humanish stats${scope.length === 0 ? "" : ` (${scope.join(", ")})`}`,
225
- `runs: ${t.runs} (${t.live} live, ${t.dryRun} dry-run${t.running > 0 ? `, ${t.running} running` : ""})`,
226
- `known estimated spend: ${knownMoney(t.costs.estimatedTotalUsd)}`,
227
- `participants + desktops: ${knownMoney(t.costs.runEstimatedUsd)}; ${t.costs.incompleteRunEstimates} run(s) with incomplete accounting`,
228
- `analysis: ${knownMoney(t.costs.analysisEstimatedUsd)} over ${t.costs.analysisAttempts} recorded attempt(s); ${t.costs.analysisUnpricedAttempts} unpriced, ${t.costs.analysisUnresolvedAttempts} unresolved`,
229
- `analysis history: ${t.costs.analysisHistoryUncertainRuns} run(s) missing or uncertain; use --json for per-run accounting`,
230
- `participants: ${t.participants.reachedGoal}/${t.participants.total} recorded goal completions, ${t.participants.reportedFriction} reported friction`,
231
- `verdicts: ${Object.entries(t.verdicts)
232
- .map(([verdict, count]) => `${verdict} ${count}`)
233
- .join(", ") || "none recorded"}`,
290
+ ...runCountLines(t),
234
291
  ];
235
- if (result.labs.length > 0) {
236
- lines.push("", "per lab:");
237
- for (const row of result.labs) {
238
- const rate = row.passRate === undefined ? "no verdicts" : `${row.passed}/${row.judged} pass`;
239
- const duration = row.medianDurationMs === undefined
240
- ? "no timed live runs"
241
- : `median ${minutes(row.medianDurationMs)} over ${row.durationSamples}`;
242
- const cost = row.medianCostUsd === undefined
243
- ? "no priced participant/desktop runs"
244
- : `participant/desktop median ${money(row.medianCostUsd)} over ${row.costSamples}`;
245
- lines.push(`- ${row.lab}: ${row.runs} run(s), ${row.live} live; ${rate}; ${duration}; known study spend ${knownMoney(row.costs.estimatedTotalUsd)}; ${cost}; ${row.unpricedRuns} unpriced runs, ${row.costs.analysisUnpricedAttempts} unpriced analyses`);
246
- }
247
- }
248
- if (result.days.length > 0) {
249
- lines.push("", "by day:");
250
- for (const row of result.days) {
251
- lines.push(`- ${row.day}: ${row.runs} run(s), ${row.live} live, known study spend ${knownMoney(row.costs.estimatedTotalUsd)}, ${row.unpricedRuns} unpriced runs, ${row.costs.analysisUnpricedAttempts} unpriced analyses`);
252
- }
292
+ // Free dry runs alone have no spend, outcome or duration to report. Anything else, an unreadable
293
+ // directory or a dry run that records a cost included, gets the full report.
294
+ const reportable = t.runs > 0 && !onlyFreeDryRuns(t);
295
+ if (reportable) {
296
+ lines.push(...totalsLines(t));
297
+ if (result.studies.length > 0)
298
+ lines.push("", "per lab:", ...result.studies.map((row) => {
299
+ const rate = row.passRate === undefined ? "no verdicts" : `${row.passed} of ${row.judged} passed`;
300
+ const duration = row.medianDurationMs === undefined
301
+ ? "no timed live runs"
302
+ : `median ${minutes(row.medianDurationMs)} over ${row.durationSamples}`;
303
+ const cost = row.medianCostUsd === undefined
304
+ ? "no priced participant/desktop runs"
305
+ : `participant/desktop median ${money(row.medianCostUsd)} over ${row.costSamples}`;
306
+ return `- ${row.study}: ${plural(row.runs, "run")}, ${row.live} live; ${rate}; ${duration}; known study spend ${knownMoney(row.costs.estimatedTotalUsd)}; ${cost}${unpricedTail(row.unpricedRuns, row.costs.analysisUnpricedAttempts)}`;
307
+ }));
308
+ if (result.days.length > 0)
309
+ lines.push("", "by day:", ...result.days.map(dayLine));
253
310
  }
254
311
  if (result.unreadable.length > 0)
255
312
  lines.push("", `unreadable run directories: ${result.unreadable.join(", ")}`);
256
- lines.push("", result.note);
313
+ if (reportable)
314
+ lines.push("", result.note);
257
315
  return `${lines.join("\n")}\n`;
258
316
  }
@@ -1,4 +1,5 @@
1
1
  import type { RunBundle } from "./bundle.js";
2
+ import { type RunStudyProvenance } from "./study-provenance.js";
2
3
  import { type PreparedOutputRoot } from "./contained-output.js";
3
4
  import type { ExecutionOutcome } from "./judge.js";
4
5
  export declare const RUN_STATUS_SCHEMA = "humanish.run-status.v1";
@@ -12,15 +13,6 @@ export declare const RUN_STATUS_TOUCH_MS = 5000;
12
13
  * slack so an ordinary scheduling hiccup or a slow disk never mislabels a healthy run.
13
14
  */
14
15
  export declare const RUN_STATUS_STALE_MS: number;
15
- /** Which manifest a run came from, when it came from one. */
16
- export interface RunLabProvenance {
17
- /** The lab id as declared in its manifest (`config.id`). */
18
- id: string;
19
- /** Repo-relative manifest path, when the run came from a file on disk. */
20
- path?: string;
21
- /** `committed` = humanish/labs, `ignored` = a local overlay, `explicit` = a path the operator passed. */
22
- origin?: "committed" | "ignored" | "explicit";
23
- }
24
16
  type RunStatusState = "running" | "finished" | "interrupted";
25
17
  /** The signals the CLI's run handler records when it stops a run. */
26
18
  export type RunInterruptSignal = "SIGINT" | "SIGTERM" | "SIGHUP";
@@ -49,8 +41,10 @@ export interface RunStatusRecord {
49
41
  runId: string;
50
42
  state: RunStatusState;
51
43
  mode: "dry-run" | "live";
52
- /** Absent when the run did not come from a lab manifest (a library caller, a bare `run`). */
53
- lab?: RunLabProvenance;
44
+ /** Absent when the run did not come from a study file (a library caller, a bare `run`). */
45
+ study?: RunStudyProvenance;
46
+ /** `study`'s value, written beside it until 0.109. Read either through studyProvenanceOf. */
47
+ lab?: RunStudyProvenance;
54
48
  /** The pid that owns the run, for local liveness checks. */
55
49
  pid: number;
56
50
  startedAt: string;
@@ -68,7 +62,7 @@ export interface RunStatusHandle {
68
62
  * still has a record to classify. */
69
63
  readonly started: Promise<void>;
70
64
  /** Finalize: state `finished`, `completedAt`, and the derived outcome. Stops the cadence.
71
- * Idempotent — a second call is a no-op, so a backend with several exit paths is safe. */
65
+ * Idempotent: a second call is a no-op, so a route with several exit paths is safe. */
72
66
  finish(outcome?: RunStatusOutcome): Promise<void>;
73
67
  /** After finish, add the result's ok and execution outcome to the finished record, so status.json
74
68
  * and the result agree. Before finish it does nothing. */
@@ -85,12 +79,12 @@ export interface RunStatusHandle {
85
79
  export interface BeginRunStatusOptions {
86
80
  runId: string;
87
81
  mode: "dry-run" | "live";
88
- lab?: RunLabProvenance | undefined;
82
+ lab?: RunStudyProvenance | undefined;
89
83
  }
90
84
  /**
91
85
  * Start a run's status record and keep it fresh. Fire-and-forget by design: a status write that
92
86
  * fails must never fail the run it describes, so every write swallows its error. The interval is
93
- * `unref`'d — this file can never be the reason a process stays alive.
87
+ * `unref`'d, so this file can never be the reason a process stays alive.
94
88
  */
95
89
  export declare function beginRunStatus(runPaths: PreparedOutputRoot, options: BeginRunStatusOptions): RunStatusHandle;
96
90
  /** The three ways a run reads from disk. `interrupted` is a record its stopped process wrote, or a
@@ -103,19 +97,4 @@ export type RunLiveness = "running" | "interrupted" | "finished";
103
97
  export declare function classifyRunStatus(record: Pick<RunStatusRecord, "state" | "updatedAt">, nowMs: number, staleMs?: number): RunLiveness;
104
98
  /** Shape guard for a record read off disk. Unknown extra fields are tolerated (additive contract). */
105
99
  export declare function isRunStatusRecord(value: unknown): value is RunStatusRecord;
106
- /**
107
- * The legacy bridge: infer a lab id for a bundle written before this contract, where the only
108
- * attribution was the `lab:<id>` convention on persona/scenario source strings. Deliberately
109
- * conservative — it reads the convention and nothing else, and a `lab:` prefix with an empty
110
- * remainder is not an id. Ids may contain colons (the removed meta-lab wrote `oss:meta`), so
111
- * only the first segment is stripped. Returns undefined when the bundle carries no such marker.
112
- */
113
- export declare function inferLegacyLabId(bundle: {
114
- persona?: {
115
- source?: string;
116
- };
117
- scenario?: {
118
- source?: string;
119
- };
120
- }): string | undefined;
121
100
  export {};
@@ -9,6 +9,7 @@
9
9
  // Public safety: it holds only the run id, the lab id/path/origin (the strings `humanish lab list`
10
10
  // prints), the mode, a local pid and timestamps. It holds no hostname or user/path identity: an
11
11
  // operator may share the run directory, so a share-safety gate must have nothing to strip here.
12
+ import { isProvenanceField, studyFields } from "./study-provenance.js";
12
13
  import { writeContainedOutputFile } from "./contained-output.js";
13
14
  import { redactText } from "../evidence/redaction.js";
14
15
  export const RUN_STATUS_SCHEMA = "humanish.run-status.v1";
@@ -46,7 +47,7 @@ export function runStatusOutcome(bundle) {
46
47
  /**
47
48
  * Start a run's status record and keep it fresh. Fire-and-forget by design: a status write that
48
49
  * fails must never fail the run it describes, so every write swallows its error. The interval is
49
- * `unref`'d — this file can never be the reason a process stays alive.
50
+ * `unref`'d, so this file can never be the reason a process stays alive.
50
51
  */
51
52
  export function beginRunStatus(runPaths, options) {
52
53
  const iso = () => new Date().toISOString();
@@ -56,7 +57,7 @@ export function beginRunStatus(runPaths, options) {
56
57
  runId: options.runId,
57
58
  state: "running",
58
59
  mode: options.mode,
59
- ...(options.lab === undefined ? {} : { lab: options.lab }),
60
+ ...studyFields(options.lab),
60
61
  pid: process.pid,
61
62
  startedAt,
62
63
  updatedAt: startedAt,
@@ -170,30 +171,7 @@ export function isRunStatusRecord(value) {
170
171
  return false;
171
172
  if (typeof record.startedAt !== "string" || typeof record.updatedAt !== "string")
172
173
  return false;
173
- if (record.lab !== undefined) {
174
- if (record.lab === null || typeof record.lab !== "object")
175
- return false;
176
- if (typeof record.lab.id !== "string")
177
- return false;
178
- }
174
+ if (!isProvenanceField(record.study) || !isProvenanceField(record.lab))
175
+ return false;
179
176
  return true;
180
177
  }
181
- /**
182
- * The legacy bridge: infer a lab id for a bundle written before this contract, where the only
183
- * attribution was the `lab:<id>` convention on persona/scenario source strings. Deliberately
184
- * conservative — it reads the convention and nothing else, and a `lab:` prefix with an empty
185
- * remainder is not an id. Ids may contain colons (the removed meta-lab wrote `oss:meta`), so
186
- * only the first segment is stripped. Returns undefined when the bundle carries no such marker.
187
- */
188
- export function inferLegacyLabId(bundle) {
189
- for (const source of [bundle.persona?.source, bundle.scenario?.source]) {
190
- if (typeof source !== "string")
191
- continue;
192
- if (!source.startsWith("lab:"))
193
- continue;
194
- const id = source.slice("lab:".length).trim();
195
- if (id !== "")
196
- return id;
197
- }
198
- return undefined;
199
- }
@@ -1,4 +1,5 @@
1
1
  import { lstat, readdir } from "node:fs/promises";
2
+ import { runNotFoundMessage } from "./run-not-found.js";
2
3
  import path from "node:path";
3
4
  import { redactText } from "../evidence/redaction.js";
4
5
  import { bindExistingRunArtifactPaths, RUNS_RELATIVE_ROOT, isSafeRunIdSegment, resolveRunsRoot, validatePreparedRunArtifactPaths, } from "./paths.js";
@@ -66,7 +67,7 @@ export async function cleanupRun(cwdInput, runInput, hooks = {}) {
66
67
  if (!resolved) {
67
68
  return cleanupRefusal(cwd, runInput, checkedAt, {
68
69
  code: "HUMANISH_RUN_NOT_FOUND",
69
- message: `Run not found: ${runInput}`,
70
+ message: await runNotFoundMessage(cwd, runInput),
70
71
  });
71
72
  }
72
73
  const runPaths = resolved;
@@ -140,7 +140,7 @@ export function isRunSimulationStatus(value) {
140
140
  value === "running" ||
141
141
  value === "passed" ||
142
142
  // Participant outcomes (docs/principles/three-roles.md). This runtime allowlist is the actual
143
- // gate — the TS union alone does not validate a bundle read back from disk.
143
+ // gate; the TS union alone does not validate a bundle read back from disk.
144
144
  value === "abandoned" ||
145
145
  value === "incomplete" ||
146
146
  value === "complete" ||
@@ -70,8 +70,8 @@ export interface RunDesktopGeometry {
70
70
  * Without this, a floored run is indistinguishable from a faithful one: `verified` compares
71
71
  * the floored number with itself and reports a match, so a reader of the bundle sees
72
72
  * requested 500 / verified 500 and reasonably concludes a 500-wide preset was asked for. A
73
- * `mobile` (414) and a `small-mobile` (360) seat both render at 500 and look identical here.
74
- * When this field is set, the preset width did not render; see #221.
73
+ * `mobile` (414) and a `small-mobile` (360) participant both render at 500 and look identical
74
+ * here. When this field is set, the preset width did not render.
75
75
  */
76
76
  declared?: {
77
77
  width: number;
@@ -96,7 +96,7 @@ export interface RunDesktopGeometry {
96
96
  source: "cdp";
97
97
  };
98
98
  /**
99
- * Mobile fidelity beyond viewport size (#221), present only when the lab asked for it. `requested`
99
+ * Mobile fidelity beyond viewport size, present only when the lab asked for it. `requested`
100
100
  * is what was applied through CDP; `resolved` is what the page reported about itself afterwards
101
101
  * and is the proof, never copied from the request. A run without this block is a
102
102
  * responsive-viewport study, whatever its preset is named.
@@ -122,7 +122,7 @@ export interface RunDesktopGeometry {
122
122
  };
123
123
  /**
124
124
  * Page targets the participant drove after the launch page (a link that opened in a new tab)
125
- * whose own read-back reported the requested viewport width (#623). Absent when the
125
+ * whose own read-back reported the requested viewport width. Absent when the
126
126
  * participant never left the launch tab; a later tab that did not report the width is a
127
127
  * participant warning instead.
128
128
  */
@@ -234,9 +234,9 @@ export interface RunStream {
234
234
  };
235
235
  actor?: ActorTrace;
236
236
  /**
237
- * Mid-run partial actor evidence (#441): the redacted trace items recorded so far,
237
+ * Mid-run partial actor evidence: the redacted trace items recorded so far,
238
238
  * flushed while a live participant is still running so the attached Observer's timeline can
239
- * grow. It is deliberately not an ActorTrace: a running participant has no honest status,
239
+ * grow. It is deliberately not an ActorTrace: a running participant has no final status,
240
240
  * completionReason, or completedAt, and this shape cannot claim them. Present only on
241
241
  * `inProgress` bundles; the final write replaces it with the real `actor` and never
242
242
  * carries it.
@@ -0,0 +1,47 @@
1
+ /** Which study a run came from, when it came from one. */
2
+ export interface RunStudyProvenance {
3
+ /** The study id its file declares (`config.id`). */
4
+ id: string;
5
+ /** Project-relative path of the study file, when the run came from a file on disk. */
6
+ path?: string;
7
+ /**
8
+ * `committed` = humanish/studies or humanish/labs, `ignored` = a local overlay, `explicit` = a
9
+ * path the operator passed.
10
+ */
11
+ origin?: "committed" | "ignored" | "explicit";
12
+ }
13
+ /** Whether a saved record's `study` or `lab` field is absent or shaped like a provenance. */
14
+ export declare function isProvenanceField(value: unknown): boolean;
15
+ /**
16
+ * A saved record's study: a valid `study`, else a valid `lab`, else the id its persona or scenario
17
+ * source names by convention.
18
+ */
19
+ export declare function studyProvenanceOf(record: {
20
+ study?: unknown;
21
+ lab?: unknown;
22
+ persona?: {
23
+ source?: unknown;
24
+ };
25
+ scenario?: {
26
+ source?: unknown;
27
+ };
28
+ }): RunStudyProvenance | undefined;
29
+ /** `study` and `lab` with the same value, as run.json and status.json write them until 0.109. */
30
+ export declare function studyFields(study: RunStudyProvenance | undefined): {
31
+ study?: RunStudyProvenance;
32
+ lab?: RunStudyProvenance;
33
+ };
34
+ /**
35
+ * The study id a run names by the `study:<id>` or `lab:<id>` convention on its persona or scenario
36
+ * source, the only attribution a run written before `lab` and `study` has. A prefix with an empty
37
+ * remainder is not an id. Ids may contain colons (`lab:oss:meta`), so only the prefix is stripped.
38
+ * Readers keep both prefixes for good: saved runs say `lab:`.
39
+ */
40
+ export declare function inferLegacyStudyId(bundle: {
41
+ persona?: {
42
+ source?: unknown;
43
+ };
44
+ scenario?: {
45
+ source?: unknown;
46
+ };
47
+ }): string | undefined;
@@ -0,0 +1,63 @@
1
+ // Which study a saved run came from. run.json and status.json write it as `study`, and as `lab`
2
+ // with the same value until 0.109 drops `lab`. Every reader goes through
3
+ // studyProvenanceOf, which also reads a run older than both fields by the `study:<id>` or
4
+ // `lab:<id>` convention on its persona and scenario sources.
5
+ const ORIGINS = ["committed", "ignored", "explicit"];
6
+ /** `value` as a provenance when it has an id, keeping only its fields that are valid. */
7
+ function provenance(value) {
8
+ if (value === null || typeof value !== "object")
9
+ return undefined;
10
+ const record = value;
11
+ if (typeof record.id !== "string" || record.id === "")
12
+ return undefined;
13
+ const origin = record.origin;
14
+ return {
15
+ id: record.id,
16
+ ...(typeof record.path === "string" ? { path: record.path } : {}),
17
+ ...(typeof origin === "string" && ORIGINS.includes(origin)
18
+ ? { origin: origin }
19
+ : {}),
20
+ };
21
+ }
22
+ /** Whether a saved record's `study` or `lab` field is absent or shaped like a provenance. */
23
+ export function isProvenanceField(value) {
24
+ if (value === undefined)
25
+ return true;
26
+ if (value === null || typeof value !== "object")
27
+ return false;
28
+ return typeof value.id === "string";
29
+ }
30
+ /**
31
+ * A saved record's study: a valid `study`, else a valid `lab`, else the id its persona or scenario
32
+ * source names by convention.
33
+ */
34
+ export function studyProvenanceOf(record) {
35
+ const declared = provenance(record.study) ?? provenance(record.lab);
36
+ if (declared !== undefined)
37
+ return declared;
38
+ const id = inferLegacyStudyId(record);
39
+ return id === undefined ? undefined : { id };
40
+ }
41
+ /** `study` and `lab` with the same value, as run.json and status.json write them until 0.109. */
42
+ export function studyFields(study) {
43
+ return study === undefined ? {} : { study, lab: study };
44
+ }
45
+ /**
46
+ * The study id a run names by the `study:<id>` or `lab:<id>` convention on its persona or scenario
47
+ * source, the only attribution a run written before `lab` and `study` has. A prefix with an empty
48
+ * remainder is not an id. Ids may contain colons (`lab:oss:meta`), so only the prefix is stripped.
49
+ * Readers keep both prefixes for good: saved runs say `lab:`.
50
+ */
51
+ export function inferLegacyStudyId(bundle) {
52
+ for (const source of [bundle.persona?.source, bundle.scenario?.source]) {
53
+ if (typeof source !== "string")
54
+ continue;
55
+ const prefix = ["study:", "lab:"].find((candidate) => source.startsWith(candidate));
56
+ if (prefix === undefined)
57
+ continue;
58
+ const id = source.slice(prefix.length).trim();
59
+ if (id !== "")
60
+ return id;
61
+ }
62
+ return undefined;
63
+ }
@@ -0,0 +1,16 @@
1
+ /** The schema of a study's result on every route. */
2
+ export declare const STUDY_RESULT_SCHEMA = "humanish.study-result.v1";
3
+ /** The fields every route's result carries to name its route and its study. */
4
+ export interface StudyResultIdentity<R extends string> {
5
+ schema: typeof STUDY_RESULT_SCHEMA;
6
+ route: R;
7
+ /** The study's `id`. */
8
+ studyId: string;
9
+ /**
10
+ * The study's `id`, the same value as `studyId`.
11
+ * @deprecated Read `studyId`. The next minor removes it.
12
+ */
13
+ labId: string;
14
+ }
15
+ /** The identity fields for a result of route `route` from the study `studyId`. */
16
+ export declare function studyResultIdentity<R extends string>(route: R, studyId: string): StudyResultIdentity<R>;
@@ -0,0 +1,9 @@
1
+ // The schema every route's result carries when a study runs: preview, computer use, scripted,
2
+ // terminal and shared world. `route` says which, and route-specific fields stay on their route's
3
+ // result type.
4
+ /** The schema of a study's result on every route. */
5
+ export const STUDY_RESULT_SCHEMA = "humanish.study-result.v1";
6
+ /** The identity fields for a result of route `route` from the study `studyId`. */
7
+ export function studyResultIdentity(route, studyId) {
8
+ return { schema: STUDY_RESULT_SCHEMA, route, studyId, labId: studyId };
9
+ }
@@ -1,2 +1,4 @@
1
1
  /** Escape a literal string for use inside a RegExp source. */
2
2
  export declare function escapeRegExp(value: string): string;
3
+ /** "1 run", "2 runs"; `plural` names the irregular form, as in plural(n, "analysis", "analyses"). */
4
+ export declare function plural(count: number, singular: string, pluralForm?: string): string;
package/dist/run/text.js CHANGED
@@ -2,3 +2,7 @@
2
2
  export function escapeRegExp(value) {
3
3
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4
4
  }
5
+ /** "1 run", "2 runs"; `plural` names the irregular form, as in plural(n, "analysis", "analyses"). */
6
+ export function plural(count, singular, pluralForm = `${singular}s`) {
7
+ return `${count} ${count === 1 ? singular : pluralForm}`;
8
+ }