humanish 0.106.0 → 0.107.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (459) hide show
  1. package/AGENTS.md +4 -2
  2. package/ARCHITECTURE.md +17 -16
  3. package/CHANGELOG.md +24 -0
  4. package/CONTEXT.md +42 -39
  5. package/CONTRIBUTING.md +8 -6
  6. package/README.md +8 -3
  7. package/dist/actors/codex/app-server-client.js +2 -1
  8. package/dist/actors/codex/app-server-trace.d.ts +0 -1
  9. package/dist/actors/codex/app-server-trace.js +1 -3
  10. package/dist/actors/codex/app-server.js +2 -1
  11. package/dist/actors/codex/protocol-compat.d.ts +27 -0
  12. package/dist/actors/codex/protocol-compat.js +357 -0
  13. package/dist/actors/codex/protocol-contract.d.ts +61 -0
  14. package/dist/actors/codex/protocol-contract.js +471 -0
  15. package/dist/actors/codex/qualified-versions.d.ts +31 -4
  16. package/dist/actors/codex/qualified-versions.js +51 -6
  17. package/dist/actors/codex/restricted-executable.d.ts +24 -0
  18. package/dist/actors/codex/restricted-executable.js +97 -22
  19. package/dist/actors/codex/restricted-launch.d.ts +7 -1
  20. package/dist/actors/codex/restricted-launch.js +42 -3
  21. package/dist/actors/codex/restricted-notifications.d.ts +101 -0
  22. package/dist/actors/codex/restricted-notifications.js +277 -0
  23. package/dist/actors/codex/restricted-participant.d.ts +30 -0
  24. package/dist/actors/codex/restricted-participant.js +60 -2
  25. package/dist/actors/codex/restricted-policy.d.ts +9 -1
  26. package/dist/actors/codex/restricted-session.d.ts +14 -1
  27. package/dist/actors/codex/restricted-session.js +50 -17
  28. package/dist/actors/codex/restricted-transport.d.ts +29 -0
  29. package/dist/actors/codex/restricted-transport.js +139 -27
  30. package/dist/actors/codex/restricted-turn.d.ts +19 -4
  31. package/dist/actors/codex/restricted-turn.js +43 -30
  32. package/dist/actors/computer-use/actor.d.ts +1 -1
  33. package/dist/actors/computer-use/actor.js +4 -4
  34. package/dist/actors/computer-use/loop/backstop.js +1 -1
  35. package/dist/actors/computer-use/loop/ending.d.ts +2 -2
  36. package/dist/actors/computer-use/loop/ending.js +2 -2
  37. package/dist/actors/computer-use/loop/spend.d.ts +1 -1
  38. package/dist/actors/computer-use/loop/spend.js +3 -3
  39. package/dist/actors/computer-use/loop/trace.js +2 -2
  40. package/dist/actors/computer-use/loop/types.d.ts +14 -5
  41. package/dist/actors/computer-use/loop.js +4 -4
  42. package/dist/actors/computer-use/openai-provider.d.ts +1 -1
  43. package/dist/actors/computer-use/openai-provider.js +2 -2
  44. package/dist/actors/contract.d.ts +10 -8
  45. package/dist/actors/contract.js +7 -1
  46. package/dist/actors/local-agent/cli.js +1 -1
  47. package/dist/actors/scripted-browser/actor.d.ts +2 -2
  48. package/dist/actors/scripted-browser/journey.js +15 -4
  49. package/dist/actors/scripted-browser/steps.js +18 -0
  50. package/dist/actors/scripted-browser/types.d.ts +11 -1
  51. package/dist/actors/stop-conditions.js +1 -3
  52. package/dist/analysis/automatic-completion.d.ts +13 -8
  53. package/dist/analysis/automatic-completion.js +14 -8
  54. package/dist/analysis/automatic-config.d.ts +1 -1
  55. package/dist/analysis/automatic-config.js +2 -2
  56. package/dist/analysis/automatic.d.ts +10 -4
  57. package/dist/analysis/automatic.js +17 -13
  58. package/dist/analysis/diagnostics.d.ts +23 -0
  59. package/dist/analysis/diagnostics.js +112 -0
  60. package/dist/analysis/evidence-sources.js +2 -2
  61. package/dist/analysis/evidence.js +6 -6
  62. package/dist/analysis/execute.d.ts +7 -1
  63. package/dist/analysis/execute.js +39 -12
  64. package/dist/analysis/job.d.ts +8 -5
  65. package/dist/analysis/job.js +15 -14
  66. package/dist/analysis/load.d.ts +1 -1
  67. package/dist/analysis/load.js +5 -5
  68. package/dist/analysis/provider.d.ts +9 -1
  69. package/dist/analysis/restricted-codex.d.ts +2 -0
  70. package/dist/analysis/restricted-codex.js +10 -1
  71. package/dist/analysis/service.d.ts +2 -0
  72. package/dist/analysis/service.js +31 -12
  73. package/dist/analysis/sharing.d.ts +2 -2
  74. package/dist/analysis/sharing.js +5 -5
  75. package/dist/analysis/store-executions.d.ts +5 -5
  76. package/dist/analysis/store-executions.js +12 -12
  77. package/dist/analysis/store.d.ts +4 -4
  78. package/dist/analysis/store.js +13 -13
  79. package/dist/cli/commands/analysis-signals.d.ts +2 -1
  80. package/dist/cli/commands/analysis-signals.js +4 -1
  81. package/dist/cli/commands/analyze.js +4 -2
  82. package/dist/cli/commands/comms.js +8 -1
  83. package/dist/cli/commands/lab-route-computer-use.d.ts +0 -2
  84. package/dist/cli/commands/lab-route-computer-use.js +26 -14
  85. package/dist/cli/commands/lab-route-preview.d.ts +1 -3
  86. package/dist/cli/commands/lab-route-preview.js +3 -4
  87. package/dist/cli/commands/lab-route-run.d.ts +6 -4
  88. package/dist/cli/commands/lab-route-run.js +6 -2
  89. package/dist/cli/commands/lab-route-scripted.d.ts +0 -2
  90. package/dist/cli/commands/lab-route-scripted.js +0 -1
  91. package/dist/cli/commands/lab-route-shared-world.d.ts +0 -2
  92. package/dist/cli/commands/lab-route-shared-world.js +0 -1
  93. package/dist/cli/commands/lab-route-terminal.d.ts +0 -2
  94. package/dist/cli/commands/lab-route-terminal.js +0 -1
  95. package/dist/cli/commands/lab-run.js +76 -45
  96. package/dist/cli/commands/lab-scorer.d.ts +0 -2
  97. package/dist/cli/commands/lab.js +12 -6
  98. package/dist/cli/commands/observe.js +22 -0
  99. package/dist/cli/commands/run-signals.d.ts +30 -0
  100. package/dist/cli/commands/run-signals.js +146 -0
  101. package/dist/cli/commands/runs.js +10 -8
  102. package/dist/cli/commands/watch.js +12 -10
  103. package/dist/cli/doctor.d.ts +3 -0
  104. package/dist/cli/doctor.js +10 -2
  105. package/dist/cli/io.d.ts +5 -4
  106. package/dist/cli/io.js +12 -8
  107. package/dist/cli/program.js +3 -3
  108. package/dist/cli/renamed-options.d.ts +11 -0
  109. package/dist/cli/renamed-options.js +18 -0
  110. package/dist/cli/telemetry.d.ts +2 -2
  111. package/dist/cli/telemetry.js +1 -1
  112. package/dist/comms/capture-surface.d.ts +1 -1
  113. package/dist/comms/catch-host.d.ts +1 -1
  114. package/dist/comms/connections.js +4 -4
  115. package/dist/comms/external-evidence.d.ts +27 -0
  116. package/dist/comms/external-evidence.js +93 -0
  117. package/dist/comms/lease-store.js +7 -7
  118. package/dist/comms/types.js +1 -1
  119. package/dist/evidence/browser-hygiene.d.ts +1 -1
  120. package/dist/evidence/browser-hygiene.js +1 -1
  121. package/dist/evidence/redaction.js +1 -1
  122. package/dist/evidence/secret-scrub.d.ts +9 -0
  123. package/dist/evidence/secret-scrub.js +96 -0
  124. package/dist/feedback/draft.js +7 -9
  125. package/dist/feedback/export-bundle.js +4 -4
  126. package/dist/feedback/export.js +9 -9
  127. package/dist/feedback/feedback.d.ts +1 -1
  128. package/dist/index.d.ts +3 -6
  129. package/dist/index.js +3 -6
  130. package/dist/keys/key-resolution.d.ts +3 -1
  131. package/dist/keys/key-resolution.js +18 -16
  132. package/dist/lab/adapter-extension.d.ts +28 -6
  133. package/dist/lab/adapter-extension.js +65 -126
  134. package/dist/lab/adapter-scorer-loader.d.ts +25 -5
  135. package/dist/lab/adapter-scorer-loader.js +24 -0
  136. package/dist/lab/composition-rules.d.ts +2 -0
  137. package/dist/lab/composition-rules.js +35 -26
  138. package/dist/lab/config.js +28 -26
  139. package/dist/lab/device-presets.d.ts +2 -2
  140. package/dist/lab/discover.js +1 -3
  141. package/dist/lab/doctor.d.ts +44 -9
  142. package/dist/lab/doctor.js +149 -41
  143. package/dist/lab/init-plan.js +1 -3
  144. package/dist/lab/keys.js +1 -1
  145. package/dist/lab/lab-deps.d.ts +85 -0
  146. package/dist/lab/lab-deps.js +4 -0
  147. package/dist/lab/parse/actors.js +8 -7
  148. package/dist/lab/parse/comms.js +3 -2
  149. package/dist/lab/parse/execution.js +3 -2
  150. package/dist/lab/parse/subject-state.js +2 -1
  151. package/dist/lab/parse/subject.js +2 -1
  152. package/dist/lab/parse/values.d.ts +0 -1
  153. package/dist/lab/parse/values.js +0 -3
  154. package/dist/lab/persona-resolve.d.ts +3 -3
  155. package/dist/lab/persona-resolve.js +4 -4
  156. package/dist/lab/persona.js +1 -3
  157. package/dist/lab/plan-base.d.ts +6 -2
  158. package/dist/lab/plan-base.js +22 -7
  159. package/dist/lab/plan-participants.d.ts +5 -5
  160. package/dist/lab/plan-participants.js +2 -2
  161. package/dist/lab/plan-types.d.ts +33 -7
  162. package/dist/lab/plan.d.ts +3 -11
  163. package/dist/lab/plan.js +18 -42
  164. package/dist/lab/preflight-result.js +0 -2
  165. package/dist/lab/preflight.d.ts +0 -3
  166. package/dist/lab/preflight.js +1 -1
  167. package/dist/lab/requirements.d.ts +20 -0
  168. package/dist/lab/requirements.js +54 -0
  169. package/dist/lab/route-inputs.d.ts +23 -12
  170. package/dist/lab/route-inputs.js +48 -51
  171. package/dist/lab/routing.d.ts +19 -24
  172. package/dist/lab/routing.js +14 -47
  173. package/dist/lab/run-lab-events.d.ts +6 -11
  174. package/dist/lab/run-lab-events.js +3 -11
  175. package/dist/lab/run-lab-homes.d.ts +64 -0
  176. package/dist/lab/run-lab-homes.js +3 -0
  177. package/dist/lab/run-lab-options.d.ts +12 -66
  178. package/dist/lab/run-lab-options.js +45 -275
  179. package/dist/lab/summary.d.ts +9 -4
  180. package/dist/lab/summary.js +15 -8
  181. package/dist/lab/types.d.ts +132 -132
  182. package/dist/lab/validation.d.ts +15 -15
  183. package/dist/lab/validation.js +28 -28
  184. package/dist/lab/warnings.js +20 -19
  185. package/dist/observer/artifact.d.ts +1 -1
  186. package/dist/observer/artifact.js +3 -3
  187. package/dist/observer/data.js +2 -2
  188. package/dist/observer/render.js +2 -2
  189. package/dist/observer/run-routes.d.ts +1 -1
  190. package/dist/observer/run-routes.js +5 -5
  191. package/dist/observer/serve.d.ts +17 -0
  192. package/dist/observer/serve.js +34 -3
  193. package/dist/observer-app.html +1 -1
  194. package/dist/routes/computer-use/bundle-parts.d.ts +1 -1
  195. package/dist/routes/computer-use/bundle-parts.js +10 -3
  196. package/dist/routes/computer-use/bundle.d.ts +4 -11
  197. package/dist/routes/computer-use/bundle.js +9 -15
  198. package/dist/routes/computer-use/diagnostics.d.ts +1 -1
  199. package/dist/routes/computer-use/diagnostics.js +1 -1
  200. package/dist/routes/computer-use/e2b-desktop/comms.d.ts +10 -10
  201. package/dist/routes/computer-use/e2b-desktop/comms.js +11 -14
  202. package/dist/routes/computer-use/e2b-desktop/desktop.d.ts +2 -2
  203. package/dist/routes/computer-use/e2b-desktop/desktop.js +4 -7
  204. package/dist/routes/computer-use/e2b-desktop/fidelity.d.ts +13 -13
  205. package/dist/routes/computer-use/e2b-desktop/fidelity.js +11 -11
  206. package/dist/routes/computer-use/e2b-desktop/prepare.d.ts +3 -3
  207. package/dist/routes/computer-use/e2b-desktop/prepare.js +33 -36
  208. package/dist/routes/computer-use/e2b-desktop/start.d.ts +1 -1
  209. package/dist/routes/computer-use/e2b-desktop/start.js +24 -23
  210. package/dist/routes/computer-use/e2b-desktop/state.d.ts +5 -4
  211. package/dist/routes/computer-use/e2b-desktop/state.js +5 -3
  212. package/dist/routes/computer-use/e2b-desktop/teardown.d.ts +1 -1
  213. package/dist/routes/computer-use/e2b-desktop/teardown.js +29 -17
  214. package/dist/routes/computer-use/external-comms.d.ts +4 -4
  215. package/dist/routes/computer-use/external-comms.js +19 -43
  216. package/dist/routes/computer-use/fanout-bundle.d.ts +3 -3
  217. package/dist/routes/computer-use/fanout-bundle.js +25 -29
  218. package/dist/routes/computer-use/{fanout-lanes.js → fanout-records.js} +25 -19
  219. package/dist/routes/computer-use/in-process-desktop.d.ts +3 -10
  220. package/dist/routes/computer-use/in-process-desktop.js +3 -9
  221. package/dist/routes/computer-use/live-flush.d.ts +7 -5
  222. package/dist/routes/computer-use/live-flush.js +19 -27
  223. package/dist/routes/computer-use/{run-lanes.js → live-phase.js} +14 -13
  224. package/dist/routes/computer-use/local-tree-pack.d.ts +3 -3
  225. package/dist/routes/computer-use/local-tree-pack.js +4 -5
  226. package/dist/routes/computer-use/local-vm.d.ts +11 -8
  227. package/dist/routes/computer-use/local-vm.js +39 -66
  228. package/dist/routes/computer-use/participant-desktop.d.ts +8 -23
  229. package/dist/routes/computer-use/participant-desktop.js +5 -25
  230. package/dist/routes/computer-use/{lanes.d.ts → participant-execution.d.ts} +10 -10
  231. package/dist/routes/computer-use/{lanes.js → participant-execution.js} +35 -22
  232. package/dist/routes/computer-use/participant-facts.d.ts +7 -1
  233. package/dist/routes/computer-use/participant-facts.js +12 -1
  234. package/dist/routes/computer-use/participant-model.d.ts +19 -11
  235. package/dist/routes/computer-use/participant-model.js +58 -33
  236. package/dist/routes/computer-use/participant-prompt.d.ts +3 -3
  237. package/dist/routes/computer-use/participant-prompt.js +4 -4
  238. package/dist/routes/computer-use/participant-runs.d.ts +6 -6
  239. package/dist/routes/computer-use/participant-runs.js +8 -8
  240. package/dist/routes/computer-use/plan.d.ts +21 -29
  241. package/dist/routes/computer-use/plan.js +59 -65
  242. package/dist/routes/computer-use/preflight.d.ts +6 -11
  243. package/dist/routes/computer-use/preflight.js +26 -18
  244. package/dist/routes/computer-use/rerun-selection.js +10 -6
  245. package/dist/routes/computer-use/result.d.ts +1 -1
  246. package/dist/routes/computer-use/result.js +40 -30
  247. package/dist/routes/computer-use/route.d.ts +3 -4
  248. package/dist/routes/computer-use/route.js +21 -19
  249. package/dist/routes/computer-use/self-report.d.ts +2 -2
  250. package/dist/routes/computer-use/self-report.js +4 -4
  251. package/dist/routes/computer-use/setup.d.ts +8 -17
  252. package/dist/routes/computer-use/setup.js +98 -86
  253. package/dist/routes/computer-use/single-bundle.d.ts +8 -11
  254. package/dist/routes/computer-use/single-bundle.js +20 -25
  255. package/dist/routes/computer-use/subject-projection.d.ts +5 -7
  256. package/dist/routes/computer-use/subject-projection.js +20 -19
  257. package/dist/routes/computer-use/types.d.ts +169 -211
  258. package/dist/routes/computer-use/types.js +13 -6
  259. package/dist/routes/preview.js +0 -1
  260. package/dist/routes/scripted/bundle.d.ts +3 -6
  261. package/dist/routes/scripted/bundle.js +11 -16
  262. package/dist/routes/scripted/plan.d.ts +5 -7
  263. package/dist/routes/scripted/plan.js +6 -4
  264. package/dist/routes/scripted/result.d.ts +4 -1
  265. package/dist/routes/scripted/result.js +10 -6
  266. package/dist/routes/scripted/route.js +10 -11
  267. package/dist/routes/scripted/scenario.d.ts +1 -1
  268. package/dist/routes/scripted/scenario.js +1 -1
  269. package/dist/routes/scripted/setup.d.ts +3 -2
  270. package/dist/routes/scripted/setup.js +22 -21
  271. package/dist/routes/scripted/subject.d.ts +8 -3
  272. package/dist/routes/scripted/subject.js +11 -6
  273. package/dist/routes/scripted/surface-records.js +1 -1
  274. package/dist/routes/scripted/surface-sessions.d.ts +2 -2
  275. package/dist/routes/scripted/surface-sessions.js +4 -4
  276. package/dist/routes/scripted/types.d.ts +19 -36
  277. package/dist/routes/scripted/types.js +1 -2
  278. package/dist/routes/shared-world/{seat-records.js → bundle-records.js} +8 -8
  279. package/dist/routes/shared-world/bundle.d.ts +2 -2
  280. package/dist/routes/shared-world/bundle.js +22 -26
  281. package/dist/routes/shared-world/checkpoints.d.ts +4 -4
  282. package/dist/routes/shared-world/checkpoints.js +4 -4
  283. package/dist/routes/shared-world/comms.d.ts +5 -4
  284. package/dist/routes/shared-world/comms.js +28 -41
  285. package/dist/routes/shared-world/external-public.d.ts +5 -5
  286. package/dist/routes/shared-world/external-public.js +19 -20
  287. package/dist/routes/shared-world/handoff.d.ts +4 -4
  288. package/dist/routes/shared-world/handoff.js +50 -50
  289. package/dist/routes/shared-world/lobby-code.d.ts +5 -5
  290. package/dist/routes/shared-world/lobby-code.js +13 -13
  291. package/dist/routes/shared-world/{seats.d.ts → participant-specs.d.ts} +8 -6
  292. package/dist/routes/shared-world/{seats.js → participant-specs.js} +46 -51
  293. package/dist/routes/shared-world/plan.d.ts +2 -6
  294. package/dist/routes/shared-world/plan.js +1 -2
  295. package/dist/routes/shared-world/provenance.d.ts +4 -4
  296. package/dist/routes/shared-world/provenance.js +4 -4
  297. package/dist/routes/shared-world/provisioned.d.ts +3 -2
  298. package/dist/routes/shared-world/provisioned.js +48 -41
  299. package/dist/routes/shared-world/result.js +31 -19
  300. package/dist/routes/shared-world/route.js +37 -41
  301. package/dist/routes/shared-world/setup.d.ts +5 -4
  302. package/dist/routes/shared-world/setup.js +12 -14
  303. package/dist/routes/shared-world/types.d.ts +53 -126
  304. package/dist/routes/shared-world/types.js +2 -2
  305. package/dist/routes/terminal/adapter.d.ts +8 -8
  306. package/dist/routes/terminal/adapter.js +28 -125
  307. package/dist/routes/terminal/bundle.d.ts +7 -12
  308. package/dist/routes/terminal/bundle.js +24 -34
  309. package/dist/routes/terminal/credentials.d.ts +2 -2
  310. package/dist/routes/terminal/credentials.js +16 -16
  311. package/dist/routes/terminal/dry-run.d.ts +1 -1
  312. package/dist/routes/terminal/dry-run.js +9 -11
  313. package/dist/routes/terminal/encoding.d.ts +1 -1
  314. package/dist/routes/terminal/encoding.js +2 -2
  315. package/dist/routes/terminal/ledger.d.ts +6 -6
  316. package/dist/routes/terminal/ledger.js +9 -9
  317. package/dist/routes/terminal/live-finish.d.ts +1 -2
  318. package/dist/routes/terminal/live-finish.js +21 -20
  319. package/dist/routes/terminal/live-sandbox.d.ts +4 -2
  320. package/dist/routes/terminal/live-sandbox.js +25 -21
  321. package/dist/routes/terminal/participant-text.d.ts +23 -0
  322. package/dist/routes/terminal/participant-text.js +165 -0
  323. package/dist/routes/terminal/plan.d.ts +2 -6
  324. package/dist/routes/terminal/plan.js +13 -9
  325. package/dist/routes/terminal/recorder.d.ts +6 -1
  326. package/dist/routes/terminal/recorder.js +12 -4
  327. package/dist/routes/terminal/result.js +4 -4
  328. package/dist/routes/terminal/route.js +26 -26
  329. package/dist/routes/terminal/runtime.d.ts +3 -2
  330. package/dist/routes/terminal/runtime.js +3 -3
  331. package/dist/routes/terminal/sandbox.d.ts +5 -3
  332. package/dist/routes/terminal/sandbox.js +13 -16
  333. package/dist/routes/terminal/session.js +16 -14
  334. package/dist/routes/terminal/token-usage.js +5 -5
  335. package/dist/routes/terminal/trace.d.ts +5 -8
  336. package/dist/routes/terminal/trace.js +9 -64
  337. package/dist/routes/terminal/types.d.ts +85 -93
  338. package/dist/routes/terminal/types.js +9 -4
  339. package/dist/run/active-runs.d.ts +13 -0
  340. package/dist/run/active-runs.js +12 -0
  341. package/dist/run/bundle-shape.d.ts +2 -1
  342. package/dist/run/bundle-shape.js +4 -4
  343. package/dist/run/bundle.d.ts +63 -56
  344. package/dist/run/bundle.js +9 -8
  345. package/dist/run/cost-summary.js +3 -2
  346. package/dist/run/costs.d.ts +6 -6
  347. package/dist/run/costs.js +10 -10
  348. package/dist/run/detail.d.ts +5 -5
  349. package/dist/run/detail.js +6 -6
  350. package/dist/run/dry-run.js +5 -12
  351. package/dist/run/{study-files.d.ts → evidence-files.d.ts} +4 -4
  352. package/dist/run/{study-files.js → evidence-files.js} +5 -5
  353. package/dist/run/feedback-shape.js +1 -1
  354. package/dist/run/judge.d.ts +16 -4
  355. package/dist/run/judge.js +24 -7
  356. package/dist/run/locate.d.ts +2 -0
  357. package/dist/run/locate.js +3 -1
  358. package/dist/run/outcomes.d.ts +3 -3
  359. package/dist/run/outcomes.js +3 -3
  360. package/dist/run/participant-records.d.ts +2 -2
  361. package/dist/run/participant-records.js +2 -2
  362. package/dist/run/paths.js +1 -1
  363. package/dist/run/pricing.d.ts +19 -9
  364. package/dist/run/pricing.js +30 -18
  365. package/dist/run/projection.d.ts +13 -13
  366. package/dist/run/projection.js +8 -8
  367. package/dist/run/reclaim.d.ts +7 -1
  368. package/dist/run/reclaim.js +24 -4
  369. package/dist/run/results.d.ts +1 -1
  370. package/dist/run/run-index.d.ts +1 -1
  371. package/dist/run/run-index.js +11 -11
  372. package/dist/run/run.d.ts +4 -2
  373. package/dist/run/run.js +9 -5
  374. package/dist/run/sandbox-receipts.d.ts +4 -4
  375. package/dist/run/sandbox-receipts.js +4 -4
  376. package/dist/run/shared-world-evidence.d.ts +47 -47
  377. package/dist/run/shared-world-shape.d.ts +3 -3
  378. package/dist/run/shared-world-shape.js +11 -11
  379. package/dist/run/stats.d.ts +5 -5
  380. package/dist/run/stats.js +8 -8
  381. package/dist/run/status.d.ts +16 -6
  382. package/dist/run/status.js +31 -8
  383. package/dist/run/streams.d.ts +26 -19
  384. package/dist/run/terminal-contract.d.ts +5 -5
  385. package/dist/run/terminal-contract.js +4 -4
  386. package/dist/run/transient-comms-secrets.d.ts +5 -0
  387. package/dist/run/transient-comms-secrets.js +12 -0
  388. package/dist/run-lab.d.ts +36 -34
  389. package/dist/run-lab.js +41 -37
  390. package/dist/subject/desktop-cli.d.ts +1 -1
  391. package/dist/subject/desktop-cli.js +1 -1
  392. package/dist/subject/local-tree-archive.d.ts +1 -1
  393. package/dist/subject/local-tree-archive.js +4 -4
  394. package/dist/subject/local-tree.d.ts +2 -2
  395. package/dist/subject/local-tree.js +1 -1
  396. package/dist/subject/node-bootstrap.d.ts +1 -1
  397. package/dist/subject/node-bootstrap.js +4 -4
  398. package/dist/subject/runtime.js +5 -5
  399. package/dist/subject/serve.js +1 -1
  400. package/dist/subject/steps.d.ts +4 -4
  401. package/dist/subject/steps.js +5 -5
  402. package/dist/substrates/desktop-session.d.ts +2 -0
  403. package/dist/substrates/e2b/cdp-probe.d.ts +2 -2
  404. package/dist/substrates/e2b/cdp-probe.js +1 -1
  405. package/dist/substrates/e2b/desktop-browser.d.ts +3 -3
  406. package/dist/substrates/e2b/desktop-browser.js +4 -4
  407. package/dist/substrates/e2b/desktop-cdp.d.ts +7 -7
  408. package/dist/substrates/e2b/desktop-cdp.js +4 -4
  409. package/dist/substrates/e2b/desktop-geometry.d.ts +4 -4
  410. package/dist/substrates/e2b/desktop-geometry.js +3 -3
  411. package/dist/substrates/e2b/desktop-media.js +2 -2
  412. package/dist/substrates/e2b/sandbox.d.ts +2 -0
  413. package/dist/substrates/e2b/sandbox.js +22 -8
  414. package/dist/substrates/e2b/sdk.d.ts +12 -1
  415. package/dist/substrates/e2b/sdk.js +30 -9
  416. package/dist/substrates/local/runtime-host.d.ts +2 -0
  417. package/dist/substrates/local/runtime-host.js +6 -0
  418. package/dist/tui/actions.d.ts +7 -5
  419. package/dist/tui/actions.js +18 -9
  420. package/dist/tui-app.js +42 -42
  421. package/dist/verify/actor.d.ts +10 -10
  422. package/dist/verify/actor.js +15 -15
  423. package/dist/verify/artifacts.d.ts +1 -1
  424. package/dist/verify/artifacts.js +3 -3
  425. package/dist/verify/costs.d.ts +2 -2
  426. package/dist/verify/costs.js +2 -2
  427. package/dist/verify/liveness.d.ts +1 -1
  428. package/dist/verify/liveness.js +4 -1
  429. package/dist/verify/rerun.d.ts +1 -1
  430. package/dist/verify/rerun.js +1 -1
  431. package/dist/verify/shared-world.js +2 -2
  432. package/dist/verify/verify.js +6 -6
  433. package/docs/README.md +1 -1
  434. package/docs/architecture/actor-contract.md +60 -22
  435. package/docs/architecture/desktop-sessions.md +9 -9
  436. package/docs/architecture/external-public-shared-world.md +5 -0
  437. package/docs/architecture/observer.md +4 -4
  438. package/docs/architecture/restricted-codex-analysis.md +40 -4
  439. package/docs/architecture/state-driven-executor.md +4 -5
  440. package/docs/architecture/task-protocol-support.md +3 -5
  441. package/docs/architecture/{terminal-product-lane.md → terminal-product-route.md} +24 -16
  442. package/docs/contracts/run-bundle.md +10 -8
  443. package/docs/contracts/schemas.md +109 -53
  444. package/docs/contracts/study-analysis.md +24 -0
  445. package/docs/decisions/0004-participant-desktops-stay-in-the-route.md +2 -1
  446. package/docs/product/automatic-analysis.md +1 -0
  447. package/docs/ramp/README.md +2 -1
  448. package/package.json +4 -4
  449. package/skills/humanish/SKILL.md +3 -2
  450. package/skills/humanish/agents/openai.yaml +1 -1
  451. package/dist/deprecated.d.ts +0 -90
  452. package/dist/deprecated.js +0 -106
  453. package/dist/lab/bag-overrides.d.ts +0 -18
  454. package/dist/lab/bag-overrides.js +0 -121
  455. package/dist/routes/computer-use/legacy-lane-spec.d.ts +0 -62
  456. package/dist/routes/computer-use/legacy-lane-spec.js +0 -39
  457. /package/dist/routes/computer-use/{fanout-lanes.d.ts → fanout-records.d.ts} +0 -0
  458. /package/dist/routes/computer-use/{run-lanes.d.ts → live-phase.d.ts} +0 -0
  459. /package/dist/routes/shared-world/{seat-records.d.ts → bundle-records.d.ts} +0 -0
@@ -12,7 +12,7 @@ import { isNodeError } from "./type-guards.js";
12
12
  * percent-decoded up to five times, and each round must still be a plain relative path, so an
13
13
  * encoded `..` or separator cannot survive into a later decode.
14
14
  */
15
- export function isStudyEvidencePath(value) {
15
+ export function isEvidencePath(value) {
16
16
  return decodesToPlainRelativePath(value, 1024, /[\\:\x00-\x1f\x7f]|^\//);
17
17
  }
18
18
  /**
@@ -54,8 +54,8 @@ export function decodesToPlainRelativePath(value, maxLength, unsafe) {
54
54
  * not turn an analysis budget into an unbounded read. No returned bytes have
55
55
  * authority to select another file or initiate a network request.
56
56
  */
57
- export async function readBoundedStudyFile(root, relativePath, maxBytes) {
58
- const result = await readBoundedStudyFileResult(root, relativePath, maxBytes);
57
+ export async function readBoundedFile(root, relativePath, maxBytes) {
58
+ const result = await readBoundedFileResult(root, relativePath, maxBytes);
59
59
  return result.state === "read" ? result.bytes : null;
60
60
  }
61
61
  /**
@@ -89,8 +89,8 @@ export async function pathMissing(filePath) {
89
89
  }
90
90
  const unavailable = { state: "unavailable" };
91
91
  /** Size refusals are distinguished only after the same contained regular-file checks. */
92
- export async function readBoundedStudyFileResult(root, relativePath, maxBytes) {
93
- if (!isStudyEvidencePath(relativePath) || !Number.isSafeInteger(maxBytes) || maxBytes < 1)
92
+ export async function readBoundedFileResult(root, relativePath, maxBytes) {
93
+ if (!isEvidencePath(relativePath) || !Number.isSafeInteger(maxBytes) || maxBytes < 1)
94
94
  return unavailable;
95
95
  const validateRoot = async () => {
96
96
  if ("physicalRunRoot" in root) {
@@ -31,7 +31,7 @@ export function isRunFeedbackCandidate(value) {
31
31
  isFeedbackNextState(value.proposed_next_state) &&
32
32
  Array.isArray(value.acceptance_proof) &&
33
33
  value.acceptance_proof.every((item) => typeof item === "string") &&
34
- // Optional, adapter-namespaced product-noun block: when present, validate only its SHAPE
34
+ // Optional, adapter-namespaced product-noun block: when present, validate only its shape
35
35
  // (a non-empty namespace + a data record). Core never inspects the keys inside `data`.
36
36
  (value.adapter === undefined || isFeedbackAdapterBlock(value.adapter)));
37
37
  }
@@ -60,7 +60,7 @@ export declare function participantHarnessFailed(participant: ParticipantFacts):
60
60
  * goal_satisfied claim with zero engagement is a session that ran out before anything happened;
61
61
  * one whose final message describes a blocker is a participant who could not proceed and said so.
62
62
  * Both keep their trace status (the claim is evidence); neither is a participant who reached the
63
- * goal. One rule for the single lane and the fan-out roll-up (#476).
63
+ * goal. One rule for the single participant and the fan-out roll-up (#476).
64
64
  */
65
65
  export declare function participantStatus(status: ActorStatus, judgments: {
66
66
  noEngagement: boolean;
@@ -86,7 +86,7 @@ export interface Judgment {
86
86
  /** The judgment for a verdict: a run passes when its verdict is pass, and a dry run passes. */
87
87
  export declare function judgmentOf(verdict: Verdict, dryRun: boolean): Judgment;
88
88
  /** What kind of execution failure a run recorded. */
89
- type ExecutionFailureKind = "harness" | "provider-cleanup" | "sandbox-cleanup" | "evidence" | "cap" | "run";
89
+ type ExecutionFailureKind = "harness" | "provider-cleanup" | "provider-policy" | "sandbox-cleanup" | "evidence" | "cap" | "run";
90
90
  /** One way the run failed as an execution. The message is scrubbed; status.json shows it. */
91
91
  export interface ExecutionFailure {
92
92
  kind: ExecutionFailureKind;
@@ -98,7 +98,19 @@ export interface ExecutionOutcome {
98
98
  succeeded: boolean;
99
99
  /** The failures the policy counts, in the order the route recorded them. */
100
100
  failures: ExecutionFailure[];
101
+ /**
102
+ * The failures the policy lets warn, such as a sandbox whose release is unconfirmed on a route
103
+ * whose evidence stands. They leave `succeeded` and the result's ok alone but stay visible in
104
+ * status.json. Omitted when there are none.
105
+ */
106
+ warnings?: ExecutionFailure[];
101
107
  }
108
+ /**
109
+ * The execution failure for one sandbox whose release is unconfirmed. `warning` is the route's
110
+ * release warning, already scrubbed. `recovery` says how to release it by hand; without one,
111
+ * reclaim kills by the id the run recorded at create time.
112
+ */
113
+ export declare function sandboxCleanupFailure(owner: string, warning: string | undefined, runId: string, recovery?: string): ExecutionFailure;
102
114
  /**
103
115
  * How a route turns the facts into its result's ok. A gate route needs every participant to
104
116
  * pass. On an evidence route a participant that failed, was blocked or timed out is captured
@@ -131,7 +143,7 @@ export interface SharedWorldJudgment extends Judgment {
131
143
  * A run with one participant: its tallied status is the verdict, so a hollow pass fails and a
132
144
  * self-reported blocker reads as blocked. Without a session, a harness failure fails the run and
133
145
  * a dry run is a contract. A run still in progress is a contract until it finishes. Computer-use
134
- * calls it for one lane with no rerun, and terminal calls it through judgeTerminal.
146
+ * calls it for one participant with no rerun, and terminal calls it through judgeTerminal.
135
147
  */
136
148
  export declare function judgeOneParticipant(args: {
137
149
  dryRun: boolean;
@@ -195,7 +207,7 @@ export declare function judgePreview(): Judgment;
195
207
  * stays as it was, so scoring can never improve a verdict. No failures returns the review as is.
196
208
  */
197
209
  export declare function foldScorerFailures(review: ReviewSummary, failures: readonly string[]): ReviewSummary;
198
- /** The run's execution outcome under its route's policy. A failure of a kind the policy lets warn is dropped. */
210
+ /** The run's execution outcome under its route's policy. A failure of a kind the policy lets warn is a warning. */
199
211
  export declare function judgeExecution(failures: readonly ExecutionFailure[], policy: OutcomePolicy): ExecutionOutcome;
200
212
  /**
201
213
  * The result's ok: the run worked as an execution, no scorer failed it, and on a gate route every
package/dist/run/judge.js CHANGED
@@ -6,7 +6,7 @@
6
6
  // participantPassed, participantHarnessFailed);
7
7
  // 2. the judgment shapes (Judgment and judgmentOf, SharedWorldJudgment, ExecutionOutcome,
8
8
  // OutcomePolicy);
9
- // 3. one judge per route: computer-use calls judgeOneParticipant (one lane, no rerun) or
9
+ // 3. one judge per route: computer-use calls judgeOneParticipant (one participant, no rerun) or
10
10
  // judgeParticipants, shared-world calls judgeSharedWorld, terminal calls judgeTerminal,
11
11
  // scripted calls judgeScripted and preview calls judgePreview;
12
12
  // 4. foldScorerFailures, which every route with a scorer applies before the run finishes;
@@ -65,7 +65,7 @@ export function participantHarnessFailed(participant) {
65
65
  * goal_satisfied claim with zero engagement is a session that ran out before anything happened;
66
66
  * one whose final message describes a blocker is a participant who could not proceed and said so.
67
67
  * Both keep their trace status (the claim is evidence); neither is a participant who reached the
68
- * goal. One rule for the single lane and the fan-out roll-up (#476).
68
+ * goal. One rule for the single participant and the fan-out roll-up (#476).
69
69
  */
70
70
  export function participantStatus(status, judgments) {
71
71
  if (status !== "passed" || judgments === undefined)
@@ -98,6 +98,17 @@ export function verdictForStatus(status) {
98
98
  export function judgmentOf(verdict, dryRun) {
99
99
  return { verdict, passed: dryRun || verdict === "pass" };
100
100
  }
101
+ /**
102
+ * The execution failure for one sandbox whose release is unconfirmed. `warning` is the route's
103
+ * release warning, already scrubbed. `recovery` says how to release it by hand; without one,
104
+ * reclaim kills by the id the run recorded at create time.
105
+ */
106
+ export function sandboxCleanupFailure(owner, warning, runId, recovery) {
107
+ return {
108
+ kind: "sandbox-cleanup",
109
+ message: `${owner}: ${warning ?? "Sandbox release is unconfirmed."} ${recovery ?? `Reclaim it by recorded id with \`humanish reclaim --run ${runId}\`.`}`,
110
+ };
111
+ }
101
112
  /** Each route's outcome policy, in one place. */
102
113
  export const OUTCOME_POLICIES = {
103
114
  "computer-use": { participants: "gate", sandboxCleanup: "warns", evidence: "fails" },
@@ -111,7 +122,7 @@ export const OUTCOME_POLICIES = {
111
122
  * A run with one participant: its tallied status is the verdict, so a hollow pass fails and a
112
123
  * self-reported blocker reads as blocked. Without a session, a harness failure fails the run and
113
124
  * a dry run is a contract. A run still in progress is a contract until it finishes. Computer-use
114
- * calls it for one lane with no rerun, and terminal calls it through judgeTerminal.
125
+ * calls it for one participant with no rerun, and terminal calls it through judgeTerminal.
115
126
  */
116
127
  export function judgeOneParticipant(args) {
117
128
  const status = args.participant === undefined ? undefined : judgedStatus(args.participant);
@@ -220,14 +231,20 @@ export function foldScorerFailures(review, failures) {
220
231
  }
221
232
  return folded;
222
233
  }
223
- /** The run's execution outcome under its route's policy. A failure of a kind the policy lets warn is dropped. */
234
+ /** The run's execution outcome under its route's policy. A failure of a kind the policy lets warn is a warning. */
224
235
  export function judgeExecution(failures, policy) {
225
- const counted = failures.filter((failure) => failure.kind === "sandbox-cleanup"
236
+ const counts = (failure) => failure.kind === "sandbox-cleanup"
226
237
  ? policy.sandboxCleanup === "fails"
227
238
  : failure.kind === "evidence"
228
239
  ? policy.evidence === "fails"
229
- : true);
230
- return { succeeded: counted.length === 0, failures: counted };
240
+ : true;
241
+ const counted = failures.filter(counts);
242
+ const warnings = failures.filter((failure) => !counts(failure));
243
+ return {
244
+ succeeded: counted.length === 0,
245
+ failures: counted,
246
+ ...(warnings.length === 0 ? {} : { warnings }),
247
+ };
231
248
  }
232
249
  /**
233
250
  * The result's ok: the run worked as an execution, no scorer failed it, and on a gate route every
@@ -2,6 +2,8 @@ import { type PreparedRunArtifactPaths } from "./paths.js";
2
2
  import { type PreparedSelectedOutputDirectory } from "./contained-output.js";
3
3
  import type { RunPointer } from "./results.js";
4
4
  import { type RunBundle } from "./bundle.js";
5
+ /** The run input that resolves through latest.json rather than naming a run directory. */
6
+ export declare const LATEST_RUN_ALIAS = "latest";
5
7
  /** Resolve "latest" or an explicit run id to its prepared artifact paths. */
6
8
  export declare function resolveRunPath(cwd: string, runInput: string): Promise<PreparedRunArtifactPaths | null>;
7
9
  export declare function readLatest(runsRoot: PreparedSelectedOutputDirectory): Promise<RunPointer | null>;
@@ -5,9 +5,11 @@ import { assertPreparedSelectedOutputDirectory, bindExistingManagedHumanishOutpu
5
5
  import { RUN_BUNDLE_FILE } from "./bundle.js";
6
6
  import { isRunBundle, isRunPointer } from "./bundle-shape.js";
7
7
  import { isNodeError } from "./type-guards.js";
8
+ /** The run input that resolves through latest.json rather than naming a run directory. */
9
+ export const LATEST_RUN_ALIAS = "latest";
8
10
  /** Resolve "latest" or an explicit run id to its prepared artifact paths. */
9
11
  export async function resolveRunPath(cwd, runInput) {
10
- if (runInput === "latest") {
12
+ if (runInput === LATEST_RUN_ALIAS) {
11
13
  const runsRoot = await bindExistingManagedHumanishOutputDirectory(cwd, "runs");
12
14
  if (!runsRoot) {
13
15
  return null;
@@ -1,16 +1,16 @@
1
1
  import { type ActorStatus } from "../actors/contract.js";
2
2
  import { type CuaGoalSource } from "../actors/goal-source.js";
3
3
  import type { TaskFunnel } from "../lab/tasks.js";
4
- import type { ParticipantOutcomes, ReviewSummary, StudyTaskFunnel } from "./bundle.js";
4
+ import type { ParticipantOutcomes, ReviewSummary, RunTaskFunnel } from "./bundle.js";
5
5
  /** Tally participant outcomes from actor statuses. Statuses this does not recognise are counted in
6
6
  * `total` but nowhere else, so the parts can never exceed the whole. */
7
7
  export declare function tallyParticipantOutcomes(statuses: readonly ActorStatus[],
8
8
  /** Per-participant: did this one report friction or a defect? Same order as `statuses`. */
9
9
  reportedFriction?: readonly boolean[]): ParticipantOutcomes;
10
10
  /** Roll per-participant funnels up into the study funnel. Undefined when nothing measured one. */
11
- export declare function aggregateTaskFunnels(funnels: readonly TaskFunnel[]): StudyTaskFunnel | undefined;
11
+ export declare function aggregateTaskFunnels(funnels: readonly TaskFunnel[]): RunTaskFunnel | undefined;
12
12
  /** The funnel as one line, denominator on every number: `signup 2/2 · verify-email 1/2`. */
13
- export declare function formatStudyTaskFunnel(funnel: StudyTaskFunnel): string;
13
+ export declare function formatRunTaskFunnel(funnel: RunTaskFunnel): string;
14
14
  type ParticipantOutcomeDetail = {
15
15
  status: ActorStatus;
16
16
  label?: string;
@@ -68,7 +68,7 @@ export function aggregateTaskFunnels(funnels) {
68
68
  };
69
69
  }
70
70
  /** The funnel as one line, denominator on every number: `signup 2/2 · verify-email 1/2`. */
71
- export function formatStudyTaskFunnel(funnel) {
71
+ export function formatRunTaskFunnel(funnel) {
72
72
  if (funnel.tasks.length === 0)
73
73
  return "no tasks declared";
74
74
  return funnel.tasks
@@ -138,7 +138,7 @@ export function formatParticipantOutcomes(outcomes, terminalCauses = []) {
138
138
  };
139
139
  append(outcomes.abandoned, ["abandoned"], "gave up");
140
140
  append(outcomes.ranOut, ["incomplete", "timed_out"], "interrupted (stop details unavailable)");
141
- // "blocked" covers an approval the run could not give AND a blocker the participant reported in
141
+ // "blocked" covers an approval the run could not give and a blocker the participant reported in
142
142
  // its own words, so the label names no cause.
143
143
  if (outcomes.blocked > 0)
144
144
  parts.push(`${outcomes.blocked} blocked`);
@@ -191,7 +191,7 @@ export function withCuaReviewProvenance(review, streams) {
191
191
  const outcomes = formatParticipantOutcomes(review.participants, details);
192
192
  // Preserve rerun context, participant narration and adapter-specific findings. Refreshing a
193
193
  // historical summary qualifies its old tally instead of silently discarding that context.
194
- const header = `Run gate: ${review.verdict}. Participants: ${outcomes}.${review.tasks ? ` Tasks: ${formatStudyTaskFunnel(review.tasks)}.` : ""}`;
194
+ const header = `Run gate: ${review.verdict}. Participants: ${outcomes}.${review.tasks ? ` Tasks: ${formatRunTaskFunnel(review.tasks)}.` : ""}`;
195
195
  const prefix = `${header} Recorded summary: `;
196
196
  const recorded = review.summary.startsWith(prefix)
197
197
  ? review.summary.slice(prefix.length)
@@ -12,7 +12,7 @@ export declare function participantIds(position: number): ParticipantIds;
12
12
  /** A participant's record, at its 1-based place in the bundle, with its one stream. */
13
13
  export declare function participantRecord(ids: ParticipantIds, place: number, fields: Omit<RunSimulation, "id" | "index" | "streamIds">): RunSimulation;
14
14
  /**
15
- * A participant's stream. `participantId` is the route's own participant id (a fan-out lane's id),
15
+ * A participant's stream. `participantId` is the route's own participant id (a fan-out entry's `lane-NN` id),
16
16
  * saved right after the record id when the route has one.
17
17
  */
18
18
  export declare function participantStream(ids: ParticipantIds, fields: Omit<RunStream, "id" | "simId" | "laneId">, participantId?: string): RunStream;
@@ -24,5 +24,5 @@ export declare function participantResourceIds(ids: ParticipantIds, participantI
24
24
  export declare function recordIdOf(stream: RunStream): string;
25
25
  /** The record id a saved event names, when it names one. */
26
26
  export declare function eventRecordIdOf(event: RunEvent): string | undefined;
27
- /** The participant id an adapter recorded on a saved stream (a fan-out lane id), when it has one. */
27
+ /** The participant id an adapter recorded on a saved stream (a fan-out `lane-NN` id), when it has one. */
28
28
  export declare function streamParticipantIdOf(stream: RunStream): string | undefined;
@@ -30,7 +30,7 @@ export function participantRecord(ids, place, fields) {
30
30
  };
31
31
  }
32
32
  /**
33
- * A participant's stream. `participantId` is the route's own participant id (a fan-out lane's id),
33
+ * A participant's stream. `participantId` is the route's own participant id (a fan-out entry's `lane-NN` id),
34
34
  * saved right after the record id when the route has one.
35
35
  */
36
36
  export function participantStream(ids, fields, participantId) {
@@ -57,7 +57,7 @@ export function recordIdOf(stream) {
57
57
  export function eventRecordIdOf(event) {
58
58
  return event.simId;
59
59
  }
60
- /** The participant id an adapter recorded on a saved stream (a fan-out lane id), when it has one. */
60
+ /** The participant id an adapter recorded on a saved stream (a fan-out `lane-NN` id), when it has one. */
61
61
  export function streamParticipantIdOf(stream) {
62
62
  return stream.laneId;
63
63
  }
package/dist/run/paths.js CHANGED
@@ -262,7 +262,7 @@ async function assertNoSymlinkDescendants(directory) {
262
262
  const entries = await readdir(directory, { withFileTypes: true });
263
263
  for (const entry of entries) {
264
264
  const child = path.join(directory, entry.name);
265
- // A run directory can be written CONCURRENTLY with this walk — the atomic writer creates a
265
+ // A run directory can be written concurrently with this walk: the atomic writer creates a
266
266
  // `.humanish-write-*.tmp` sibling and renames it, and a run now also refreshes its own status
267
267
  // record on a cadence. An entry that vanished between readdir and lstat therefore proves
268
268
  // nothing except that it is gone, and a path that no longer exists cannot be a symlink escape:
@@ -10,12 +10,12 @@ export interface ModelRate {
10
10
  * a reduced rate. Optional: absent means we do not model a discount and every input token is
11
11
  * billed at `inputUsdPerToken`. */
12
12
  cachedInputUsdPerToken?: number;
13
- /** USD per input token newly WRITTEN to the provider's prompt cache, when the provider bills
13
+ /** USD per input token newly written to the provider's prompt cache, when the provider bills
14
14
  * writes (OpenAI: GPT-5.6+ bills `cache_write_tokens` at 1.25x the uncached input rate, as the
15
- * TOTAL rate for those tokens — not an extra charge on top). Absent = writes are free (every
15
+ * total rate for those tokens, with no extra charge on top). Absent = writes are free (every
16
16
  * pre-5.6 model) and any reported write tokens price at the plain input rate. */
17
17
  cacheWriteUsdPerToken?: number;
18
- /** Long-context tier, when the provider re-prices the WHOLE request past an input-size
18
+ /** Long-context tier, when the provider re-prices the whole request past an input-size
19
19
  * threshold (OpenAI GPT-5.6: >272K input tokens => 2x input-side, 1.5x output, full request).
20
20
  * Priced exactly only when the usage carries per-request `turns` records; totals alone cannot
21
21
  * say which requests crossed, so without turns the estimate stays on the short-context rate,
@@ -29,7 +29,7 @@ export interface ModelRate {
29
29
  asOf: string;
30
30
  /** Public pricing page the number came from (a comment/URL, never a secret). */
31
31
  source: string;
32
- /** true = a stand-in NOT copied from a live sheet; the estimate carries this flag so a
32
+ /** true = a stand-in that was not copied from a live sheet; the estimate carries this flag so a
33
33
  * placeholder rate is never mistaken for a confirmed one. */
34
34
  placeholder?: boolean;
35
35
  }
@@ -52,8 +52,8 @@ export interface DesktopResourceRate {
52
52
  source: string;
53
53
  }
54
54
  /**
55
- * The token-derived cost ESTIMATE for one actor lane. `estimatedCostUsd: null` = DECLARED ABSENT
56
- * (unknown rate or no token usage) — never coerced to 0. A non-null figure ALWAYS carries its
55
+ * The token-derived cost estimate for one actor trace. `estimatedCostUsd: null` = declared absent
56
+ * (unknown rate or no token usage) and is never coerced to 0. A non-null figure always carries its
57
57
  * pricing provenance (`ratesAsOf` + `source`) so the mechanism (a rate-table multiply) matches
58
58
  * the claim (an estimate, not a charge). Defined here so the rate table and the field that
59
59
  * consumes it live together; `ActorTrace` imports it type-only.
@@ -71,13 +71,17 @@ export interface ActorEstimatedCost {
71
71
  modelId?: string;
72
72
  /** true when the rate is a stand-in, not a live sheet. */
73
73
  placeholder?: boolean;
74
+ /** Set when usage arrived as runtime-turn totals that each sum several requests. No request's
75
+ * own size is known, so no per-request long-context tier was applied: tokens are priced at the
76
+ * base tier, cached input at the cached rate. */
77
+ basis?: "aggregated_turns_base_rate";
74
78
  breakdown?: {
75
79
  inputUsd: number;
76
80
  outputUsd: number;
77
81
  inputTokens: number;
78
82
  outputTokens: number;
79
83
  /** Of `inputTokens`, how many were billed at the reduced cached rate. Present only when the
80
- * provider reported cache hits AND the rate sheet models a cached rate. */
84
+ * provider reported cache hits and the rate sheet models a cached rate. */
81
85
  cachedInputTokens?: number;
82
86
  /** Of `inputTokens`, how many were billed at the cache-WRITE rate (OpenAI 5.6+). */
83
87
  cacheWriteInputTokens?: number;
@@ -86,7 +90,7 @@ export interface ActorEstimatedCost {
86
90
  longContextTurns?: number;
87
91
  };
88
92
  }
89
- /** The desktop-minute cost ESTIMATE (host-side create->teardown span * a per-minute rate). Same
93
+ /** The desktop-minute cost estimate (host-side create->teardown span * a per-minute rate). Same
90
94
  * null-discipline as ActorEstimatedCost: `estimatedCostUsd: null` = not measured (no duration). */
91
95
  export interface DesktopCostEstimate {
92
96
  estimatedCostUsd: number | null;
@@ -108,13 +112,19 @@ export declare function estimateAllocatedDesktopCost(minutes: number | undefined
108
112
  * precision. */
109
113
  export declare function round6(n: number): number;
110
114
  /**
111
- * Estimate one actor lane's model-token cost from its trace tokenUsage + model id. Deterministic;
115
+ * Estimate one actor trace's model-token cost from its trace tokenUsage + model id. Deterministic;
112
116
  * the rate table is injectable (tests pass a fake sheet). Returns a DECLARED-ABSENT estimate
113
117
  * (estimatedCostUsd: null + a reason) for a missing rate or missing usage — never a guessed cost.
114
118
  */
115
119
  export declare function estimateActorCostForExecution(usage: ActorTokenUsage | undefined, modelId: string | undefined, profile?: {
116
120
  billing?: unknown;
117
121
  }): ActorEstimatedCost;
122
+ /**
123
+ * Price usage reported as runtime-turn totals, such as Codex `turn.completed`, which sums every
124
+ * request in the turn. Pricing a turn as one request would apply the long-context tier to the
125
+ * whole turn, so the per-turn records are dropped and the totals price on the base tier.
126
+ */
127
+ export declare function estimateAggregatedTurnCost(tokenUsage: ActorTokenUsage | undefined, modelId: string | undefined): ActorEstimatedCost;
118
128
  export declare function estimateActorCost(tokenUsage: ActorTokenUsage | undefined, modelId: string | undefined, rates?: Record<string, ModelRate>): ActorEstimatedCost;
119
129
  /**
120
130
  * Estimate the E2B desktop-minute cost from a host-side create->teardown span (minutes). The rate
@@ -1,18 +1,18 @@
1
- // OPERATOR-EDITABLE ESTIMATES — NOT authoritative prices. Providers change pricing without
2
- // notice. When they do, update BOTH the number AND the `asOf` date on the affected entry.
3
- // Every dollar figure humanish derives from this table is surfaced/persisted as an ESTIMATE,
4
- // labeled "estimated (rates as of <asOf>)", and is NEVER presented as an exact charge.
1
+ // Operator-editable estimates, which are not authoritative prices. Providers change pricing without
2
+ // notice. When they do, update both the number and the `asOf` date on the affected entry.
3
+ // Every dollar figure humanish derives from this table is surfaced/persisted as an estimate,
4
+ // labeled "estimated (rates as of <asOf>)", and is never presented as an exact charge.
5
5
  //
6
- // This module is PURE (node builtins only, no deps) and deterministic: the estimator functions take
7
- // an OPTIONAL injected rate table/rate so tests drive assertions with a fake sheet and never depend
8
- // on the live numbers below. An UNKNOWN model/desktop rate yields a DECLARED-ABSENT estimate
9
- // (estimatedCostUsd: null + a reason), NEVER a guessed or silent-zero cost (invariant 5). A bare
6
+ // This module is pure (node builtins only, no deps) and deterministic: the estimator functions take
7
+ // an optional injected rate table/rate so tests drive assertions with a fake sheet and never depend
8
+ // on the live numbers below. An unknown model/desktop rate yields a declared-absent estimate
9
+ // (estimatedCostUsd: null + a reason) and never a guessed or silent-zero cost (invariant 5). A bare
10
10
  // `costUsd` elsewhere in the contract means a provider actually billed that amount; the
11
11
  // token-derived estimate here always lives under `estimatedCostUsd` so a reader can never confuse
12
12
  // an estimate for an authoritative charge (invariant 6).
13
13
  export const PRICING_SCHEMA = "humanish.pricing.v1";
14
14
  export const ACTOR_ESTIMATED_COST_SCHEMA = "humanish.actor-estimated-cost.v1";
15
- // The gpt-5.6 long-context tier: >272K input tokens re-prices the FULL request at 2x
15
+ // The gpt-5.6 long-context tier: >272K input tokens re-prices the full request at 2x
16
16
  // input-side / 1.5x output (developers.openai.com/api/docs/models/gpt-5.6-sol, 2026-08-18).
17
17
  const LONG_CONTEXT_TIER_272K = {
18
18
  thresholdInputTokens: 272_000,
@@ -41,7 +41,7 @@ function rateWithLongContextTier(inPer1M, cachedPer1M, writePer1M, outPer1M, asO
41
41
  const GPT56_SOL_PROMO_AS_OF = "2026-09-03";
42
42
  const GPT6_ASTRA_AS_OF = "2026-09-03";
43
43
  // Per-model rates, keyed on the model id that lands in trace.ids.model (lookup is
44
- // case-insensitive on a trimmed id). An id NOT present here is DECLARED ABSENT, never guessed.
44
+ // case-insensitive on a trimmed id). An id missing here is declared absent, never guessed.
45
45
  export const MODEL_RATES = {
46
46
  // OpenAI computer-use-preview (the classic CUA model). ~$3 / 1M input, ~$12 / 1M output.
47
47
  // source: openai.com/api/pricing (verify — providers change without notice).
@@ -74,7 +74,7 @@ export const MODEL_RATES = {
74
74
  "gpt-5.6-cyber": rateWithLongContextTier(12.5, 1.25, 15.625, 75),
75
75
  // Daybreak program aliases (blue -> sol, red -> cyber today). OpenAI repoints these as new
76
76
  // frontier models ship, so prefer the explicit tier id in labs; the entries exist so a
77
- // configured alias still prices at what the alias bills TODAY.
77
+ // configured alias still prices at what the alias bills today.
78
78
  "daybreak-blue-latest": rateWithLongContextTier(4, 0.4, 5, 20, GPT56_SOL_PROMO_AS_OF),
79
79
  "daybreak-red-latest": rateWithLongContextTier(12.5, 1.25, 15.625, 75),
80
80
  // gpt-6-astra (shipped 2026-09-03; API access announced as rolling out). Same two mechanics
@@ -157,7 +157,7 @@ export function round6(n) {
157
157
  return Math.round(n * 1e6) / 1e6;
158
158
  }
159
159
  /**
160
- * Estimate one actor lane's model-token cost from its trace tokenUsage + model id. Deterministic;
160
+ * Estimate one actor trace's model-token cost from its trace tokenUsage + model id. Deterministic;
161
161
  * the rate table is injectable (tests pass a fake sheet). Returns a DECLARED-ABSENT estimate
162
162
  * (estimatedCostUsd: null + a reason) for a missing rate or missing usage — never a guessed cost.
163
163
  */
@@ -172,6 +172,18 @@ export function estimateActorCostForExecution(usage, modelId, profile) {
172
172
  }
173
173
  : estimateActorCost(usage, modelId);
174
174
  }
175
+ /**
176
+ * Price usage reported as runtime-turn totals, such as Codex `turn.completed`, which sums every
177
+ * request in the turn. Pricing a turn as one request would apply the long-context tier to the
178
+ * whole turn, so the per-turn records are dropped and the totals price on the base tier.
179
+ */
180
+ export function estimateAggregatedTurnCost(tokenUsage, modelId) {
181
+ const { turns: _turns, ...totals } = tokenUsage ?? {};
182
+ const estimate = estimateActorCost(tokenUsage === undefined ? undefined : totals, modelId);
183
+ return estimate.estimatedCostUsd === null
184
+ ? estimate
185
+ : { ...estimate, basis: "aggregated_turns_base_rate" };
186
+ }
175
187
  export function estimateActorCost(tokenUsage, modelId, rates = MODEL_RATES) {
176
188
  if (!tokenUsage || (tokenUsage.input === undefined && tokenUsage.output === undefined)) {
177
189
  return {
@@ -193,15 +205,15 @@ export function estimateActorCost(tokenUsage, modelId, rates = MODEL_RATES) {
193
205
  }
194
206
  const inTok = tokenUsage.input ?? 0;
195
207
  const outTok = tokenUsage.output ?? 0;
196
- // Long-context tiering needs to know each REQUEST's input size (the provider re-prices whole
197
- // requests past the threshold), so it engages only when per-turn usage records exist AND their
208
+ // Long-context tiering needs to know each request's input size (the provider re-prices whole
209
+ // requests past the threshold), so it engages only when per-turn usage records exist and their
198
210
  // input sums to the reported total — a partial turn ledger must not silently price the missing
199
211
  // remainder at the wrong tier. Otherwise totals price on the base (short-context) rate, which is
200
212
  // the under-estimate direction and never trips a cap early.
201
213
  const turns = tokenUsage.turns ?? [];
202
214
  const sumOf = (field) => turns.reduce((sum, turn) => sum + (turn[field] ?? 0), 0);
203
- // The ledger is trusted only when it decomposes the totals EXACTLY — all four sums, not just
204
- // input/output. A ledger that carries request sizes but not the cache splits would otherwise
215
+ // The ledger is trusted only when it decomposes the totals exactly, in all four sums including the
216
+ // cache splits. A ledger that carries request sizes but not the cache splits would otherwise
205
217
  // price every token at the full rate while the session totals sit ignored, overstating spend
206
218
  // 3.5-5x and tripping caps early. Inconsistent evidence falls back to the totals path, which
207
219
  // honors the declared splits on the base tier.
@@ -217,8 +229,8 @@ export function estimateActorCost(tokenUsage, modelId, rates = MODEL_RATES) {
217
229
  let writeTotal = 0;
218
230
  let longContextTurns = 0;
219
231
  // Price one request's usage at one tier. Cached input is billed at a fraction of the full rate,
220
- // and on a session that threads provider state it is the MAJORITY of input — pricing it at the
221
- // full rate overstates real spend by up to ~10x. Cache WRITES bill at their own rate (1.25x on
232
+ // and on a session that threads provider state it is the majority of input, and pricing it at the
233
+ // full rate overstates real spend by up to ~10x. Cache writes bill at their own rate (1.25x on
222
234
  // OpenAI 5.6+) as the total rate for those tokens; a sheet without a write rate prices writes as
223
235
  // plain input (pre-5.6: writes are free-of-extra-fee, i.e. plain input). Every piece is honestly
224
236
  // absent: no reported split means no discount and no surcharge assumed.
@@ -40,11 +40,11 @@ export interface LabExpectation {
40
40
  costUnknown: number;
41
41
  }
42
42
  /**
43
- * Derive what to expect from a lab's own history. Only FINISHED runs count: an interrupted run's
43
+ * Derive what to expect from a lab's own history. Only finished runs count: an interrupted run's
44
44
  * duration is the length of an accident, not of a study, and including it would quietly bias the
45
45
  * estimate the operator uses to decide whether to press Start.
46
46
  *
47
- * MODE MATTERS, and mixing modes is a lie rather than an imprecision. A dry run spends nothing and
47
+ * The mode matters: mixing modes produces a false figure. A dry run spends nothing and
48
48
  * takes no time, so a median over nine dry runs and one live one reports that a live run is free —
49
49
  * next to a control that spends money. Pass the mode the figure is about; omit it only for a
50
50
  * summary that is not attached to an action.
@@ -59,7 +59,7 @@ export declare function expectationLine(expectation: LabExpectation): string;
59
59
  /**
60
60
  * The one line a lab may say about itself, wherever it is shown.
61
61
  *
62
- * Prefers LIVE figures: cost and duration are what someone reads before spending, and a median
62
+ * Prefers live figures: cost and duration are what someone reads before spending, and a median
63
63
  * diluted by dry runs reports a live study as cheaper and faster than it has ever been. With no
64
64
  * live history it reports the count and claims nothing about time or money — which is why this is
65
65
  * shared rather than reimplemented per surface, since the two disagreeing is the whole failure.
@@ -104,15 +104,15 @@ export declare function livenessLabel(entry: Pick<RunIndexEntry, "liveness" | "v
104
104
  /** A lab as the labs list shows it: what is declared, joined to what actually happened. */
105
105
  export interface LabRow {
106
106
  /**
107
- * Stable identity for THIS ROW. A manifest's path when it has one, else the lab id — because two
107
+ * Stable identity for this row. A manifest's path when it has one, else the lab id, because two
108
108
  * manifests can declare the same id, and keying rows by id makes them collapse into a pair of
109
109
  * indistinguishable duplicates.
110
110
  */
111
111
  key: string;
112
- /** The declared id. Run history attributes to this, so it is NOT unique across manifests. */
112
+ /** The declared id. Run history attributes to this, so it is not unique across manifests. */
113
113
  labId: string;
114
114
  /**
115
- * The handle an operator would actually type. Lab resolution is by FILENAME, so the manifest
115
+ * The handle an operator would actually type. Lab resolution is by filename, so the manifest
116
116
  * `\`.humanish/labs/persona-contrast-live.yaml\`` is reached as `persona-contrast-live` even when
117
117
  * the id inside it says something else.
118
118
  */
@@ -120,25 +120,25 @@ export interface LabRow {
120
120
  /** The manifest's own title, when it has one. */
121
121
  title?: string;
122
122
  /**
123
- * The shortest label that is UNIQUE among the rows it is listed with: the title when no other
123
+ * The shortest label that is unique among the rows it is listed with: the title when no other
124
124
  * lab shares it, else the filename, else the full path. A list is only navigable if every row can
125
125
  * be told from every other one, and a title is not guaranteed to be distinct — two manifests in
126
- * this repo carry the same title AND the same declared id, differing only by filename.
126
+ * this repo carry the same title and the same declared id, differing only by filename.
127
127
  */
128
128
  label: string;
129
- /** The manifest's own words, for the list to say what this study IS. */
129
+ /** The manifest's own words, for the list to say what this study is. */
130
130
  description?: string;
131
131
  /** Repo-relative manifest path, absent for a lab known only from run history. */
132
132
  path?: string;
133
133
  origin?: "committed" | "ignored" | "explicit";
134
134
  /**
135
- * False for a lab that has runs but NO manifest here — renamed, deleted, or run from a path that
135
+ * False for a lab that has runs but no manifest here: renamed, deleted, or run from a path that
136
136
  * is gone. Its runs are still evidence and must stay reachable, so it is listed and marked rather
137
137
  * than dropped.
138
138
  */
139
139
  declared: boolean;
140
140
  /**
141
- * How many OTHER manifests declare this same lab id. Above zero, the run history below is shared
141
+ * How many other manifests declare this same lab id. Above zero, the run history below is shared
142
142
  * between them and cannot be attributed to one file — worth saying, because it is a
143
143
  * misconfiguration the operator almost certainly does not know about.
144
144
  */
@@ -169,13 +169,13 @@ export interface DeclaredLab {
169
169
  * deleted — that evidence still exists on disk, so dropping those rows would make real runs
170
170
  * unreachable from the surface that is supposed to list them.
171
171
  *
172
- * ONE ROW PER MANIFEST, not per id. The two are not the same thing: a manifest is addressed by its
172
+ * One row per manifest, not per id. The two are not the same thing: a manifest is addressed by its
173
173
  * filename while its runs attribute to the id declared inside it, so several files can legitimately
174
174
  * share an id. Collapsing them hides a real file; keying by id duplicates a row with no way to tell
175
175
  * the copies apart. Both happen in practice — this repo has exactly that pair.
176
176
  *
177
177
  * Order puts a lab someone is working in first, then labs by how recently they ran, then declared
178
- * labs that have never run (alphabetically BY WHAT IS DISPLAYED, so the order on screen is the
178
+ * labs that have never run (alphabetically by the displayed label, so the order on screen is the
179
179
  * order a reader can predict), then labs known only from history.
180
180
  */
181
181
  export declare function labRows(declared: readonly DeclaredLab[], entries: readonly RunIndexEntry[]): {