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
@@ -52,6 +52,24 @@ export async function executeBrowserPersonaStep(args) {
52
52
  await target.click({ timeout: stepTimeoutMs });
53
53
  await args.page.waitForTimeout(300);
54
54
  }
55
+ else if (args.step.action === "press") {
56
+ if (!args.step.key) {
57
+ throw new Error(`${args.step.id} press step is missing key`);
58
+ }
59
+ // With a selector, Playwright focuses that element first; without one, the key goes to
60
+ // whatever has focus, such as the input a fill step just typed into.
61
+ if (args.step.selector) {
62
+ const target = args.page.locator(args.step.selector).first();
63
+ if ((await target.count()) === 0) {
64
+ throw new Error(`${args.step.id} press step found no target`);
65
+ }
66
+ await target.press(args.step.key, { timeout: stepTimeoutMs });
67
+ }
68
+ else {
69
+ await args.page.keyboard.press(args.step.key);
70
+ }
71
+ await args.page.waitForTimeout(300);
72
+ }
55
73
  else if (args.step.action === "assertText" || args.step.action === "waitForText") {
56
74
  const expectedText = args.step.expectation?.text ?? args.step.value;
57
75
  if (!expectedText) {
@@ -6,6 +6,10 @@ export interface ScriptedLocatorLike {
6
6
  click(options?: {
7
7
  timeout?: number;
8
8
  }): Promise<void>;
9
+ /** Focuses the element and presses the key, as Playwright's Locator.press does. */
10
+ press(key: string, options?: {
11
+ timeout?: number;
12
+ }): Promise<void>;
9
13
  count(): Promise<number>;
10
14
  waitFor(options?: {
11
15
  state?: "visible";
@@ -21,6 +25,10 @@ export interface ScriptedPageLike {
21
25
  timeout?: number;
22
26
  }): Promise<unknown>;
23
27
  locator(selector: string): ScriptedLocatorLike;
28
+ /** Presses a key on whatever element has focus. */
29
+ keyboard: {
30
+ press(key: string): Promise<void>;
31
+ };
24
32
  waitForTimeout(ms: number): Promise<void>;
25
33
  waitForFunction(fn: string, arg: unknown, options?: {
26
34
  timeout?: number;
@@ -83,7 +91,7 @@ export interface BrowserSurfaceCapture {
83
91
  surface: BrowserSurface;
84
92
  tracePath: string;
85
93
  }
86
- export type BrowserPersonaAction = "goto" | "click" | "fill" | "assertText" | "waitForText" | "waitForSelector";
94
+ export type BrowserPersonaAction = "goto" | "click" | "press" | "fill" | "assertText" | "waitForText" | "waitForSelector";
87
95
  export interface BrowserPersonaAssertionCapture {
88
96
  id: string;
89
97
  reason: string;
@@ -122,6 +130,8 @@ export interface BrowserPersonaStepManifest {
122
130
  path?: string;
123
131
  selector?: string;
124
132
  value?: string;
133
+ /** A press step's key, in Playwright's key syntax ("Enter", "Tab", "Control+A"). */
134
+ key?: string;
125
135
  }
126
136
  export interface BrowserPersonaJourney {
127
137
  goal: string;
@@ -1,3 +1,4 @@
1
+ import { isRecord } from "../run/type-guards.js";
1
2
  export function evaluateStopWhen(stopWhen, observation) {
2
3
  if (!stopWhen)
3
4
  return undefined;
@@ -60,6 +61,3 @@ function valueAtPath(source, path) {
60
61
  }
61
62
  return current;
62
63
  }
63
- function isRecord(value) {
64
- return typeof value === "object" && value !== null && !Array.isArray(value);
65
- }
@@ -1,13 +1,16 @@
1
1
  import { FinishedRun } from "../run/run.js";
2
2
  import type { AnalysisConfig } from "./types.js";
3
- import { runAutomaticStudyAnalysis, type AutomaticAnalysisDeps } from "./automatic.js";
3
+ import type { LabDeps } from "../lab/lab-deps.js";
4
+ import type { LabEvent } from "../lab/run-lab-events.js";
5
+ import { type AutomaticAnalysisDeps } from "./automatic.js";
4
6
  import type { AutomaticAnalysisOutcome } from "./job.js";
5
- export interface AutomaticAnalysisHooks {
6
- /** Provider/test dependencies apply only to analysis, never to the participant. */
7
- deps?: AutomaticAnalysisDeps;
8
- /** Called after the source is finalized; the returned cleanup always runs. */
9
- onStart?: () => void | (() => void);
10
- run?: typeof runAutomaticStudyAnalysis;
7
+ /** What a route's input gives its analysis: the test's runner and deps, the signal and onEvent. */
8
+ export interface AnalysisInput {
9
+ readonly deps?: Pick<LabDeps, "analysis">;
10
+ /** Cancels the analysis only. */
11
+ readonly analysisSignal?: AbortSignal;
12
+ /** Receives analysis-started and analysis-finished. */
13
+ readonly emit?: (event: LabEvent) => void;
11
14
  }
12
15
  export interface AutomaticAnalysisResult {
13
16
  automaticAnalysis?: AutomaticAnalysisOutcome;
@@ -23,11 +26,13 @@ export declare function completeAutomaticAnalysis<T extends {
23
26
  cwd: string;
24
27
  runId: string;
25
28
  dryRun: boolean;
26
- }>(result: T, finished: FinishedRun | undefined, config: AnalysisConfig | undefined, hooks?: AutomaticAnalysisHooks, { trigger, preferLargerOutput, }?: {
29
+ }>(result: T, finished: FinishedRun | undefined, config: AnalysisConfig | undefined, input: AnalysisInput | undefined, { trigger, preferLargerOutput, refusal, }?: {
27
30
  /** "default" when the lab declared no analysis; its missing-key skip is not a failure. */
28
31
  trigger?: "default" | "explicit";
29
32
  /** The lab omitted an output limit, so a larger one may be used within the admission budget. */
30
33
  preferLargerOutput?: boolean;
34
+ /** The route's reason not to analyze this run; see AutomaticAnalysisDeps.refusal. */
35
+ refusal?: AutomaticAnalysisDeps["refusal"];
31
36
  }): Promise<T & AutomaticAnalysisResult>;
32
37
  /**
33
38
  * True when an analysis the lab never declared was refused because its conservative estimate is
@@ -1,12 +1,12 @@
1
1
  import { physicalCwdOf, validatePreparedRunRootIdentity } from "../run/paths.js";
2
2
  import { FinishedRun } from "../run/run.js";
3
- import { runAutomaticStudyAnalysis } from "./automatic.js";
3
+ import { runAutomaticAnalysis } from "./automatic.js";
4
4
  /**
5
5
  * One post-completion boundary shared by all live recording producers. `finished` is the token
6
6
  * the producer's run scope issued when the run published its final bundle; without it, or when it
7
7
  * names another run than the result, there is no source to analyze.
8
8
  */
9
- export async function completeAutomaticAnalysis(result, finished, config, hooks, { trigger = "explicit", preferLargerOutput = false, } = {}) {
9
+ export async function completeAutomaticAnalysis(result, finished, config, input, { trigger = "explicit", preferLargerOutput = false, refusal, } = {}) {
10
10
  if (config === undefined)
11
11
  return result;
12
12
  const origin = trigger === "default" ? { automaticAnalysisTrigger: trigger } : {};
@@ -26,9 +26,12 @@ export async function completeAutomaticAnalysis(result, finished, config, hooks,
26
26
  };
27
27
  }
28
28
  const prepared = finished.paths;
29
- let cleanup = undefined;
29
+ const seams = input?.deps?.analysis;
30
+ const signal = input?.analysisSignal;
31
+ let started = false;
30
32
  try {
31
- cleanup = hooks?.onStart?.();
33
+ input?.emit?.({ type: "analysis-started" });
34
+ started = true;
32
35
  try {
33
36
  await validatePreparedRunRootIdentity(prepared);
34
37
  }
@@ -40,8 +43,10 @@ export async function completeAutomaticAnalysis(result, finished, config, hooks,
40
43
  };
41
44
  }
42
45
  const sourceCwd = physicalCwdOf(prepared);
43
- const automaticAnalysis = await (hooks?.run ?? runAutomaticStudyAnalysis)(sourceCwd, finished.runId, config, {
44
- ...hooks?.deps,
46
+ const automaticAnalysis = await (seams?.run ?? runAutomaticAnalysis)(sourceCwd, finished.runId, config, {
47
+ ...seams?.deps,
48
+ ...(signal === undefined ? {} : { signal }),
49
+ ...(refusal === undefined ? {} : { refusal }),
45
50
  preferLargerOutput,
46
51
  ...(trigger === "default" ? { defaultRequest: true } : {}),
47
52
  expectedRun: prepared,
@@ -58,10 +63,11 @@ export async function completeAutomaticAnalysis(result, finished, config, hooks,
58
63
  }
59
64
  finally {
60
65
  try {
61
- cleanup?.();
66
+ if (started)
67
+ input?.emit?.({ type: "analysis-finished" });
62
68
  }
63
69
  catch {
64
- /* Cleanup cannot erase accounting or the completed recording. */
70
+ /* A report cannot erase accounting or the completed recording. */
65
71
  }
66
72
  }
67
73
  }
@@ -19,7 +19,7 @@ export type LabAnalysis = LabAnalysisSettings & ({
19
19
  });
20
20
  /** The spend cap on an analysis the lab did not declare. */
21
21
  export declare const DEFAULT_ANALYSIS_MAX_COST_USD = 3;
22
- /** Called for parsed manifests AND direct library configs, before participant execution. */
22
+ /** Called for parsed manifests and direct library configs, before participant execution. */
23
23
  export declare function resolveAutomaticAnalysis(raw: unknown): {
24
24
  ok: true;
25
25
  config: AnalysisConfig | undefined;
@@ -24,7 +24,7 @@ const SENSITIVE_QUESTION = {
24
24
  };
25
25
  /** The spend cap on an analysis the lab did not declare. */
26
26
  export const DEFAULT_ANALYSIS_MAX_COST_USD = 3;
27
- /** Called for parsed manifests AND direct library configs, before participant execution. */
27
+ /** Called for parsed manifests and direct library configs, before participant execution. */
28
28
  export function resolveAutomaticAnalysis(raw) {
29
29
  if (raw === false)
30
30
  return { ok: true, config: undefined };
@@ -131,5 +131,5 @@ export function automaticAnalysisBudget(raw, route) {
131
131
  export function formatAutomaticAnalysisBudget(budget) {
132
132
  if (budget.provider === "codex")
133
133
  return `After live runs: Codex account analysis · ${budget.model} · separate restricted analyst with remote inference. Account limits apply; dollar cost and output-token ceiling are unknown. Set review.analysis: false to disable.`;
134
- return `After live runs: ${budget.trigger} analysis · ${budget.model} · separate $${budget.maxCostUsd} admission estimate limit (not a provider billing cap). Set review.analysis: false to disable.`;
134
+ return `After live runs: ${budget.trigger} analysis · ${budget.model} · refused before it starts if its estimate is over $${budget.maxCostUsd}; this is not a billing cap. Set review.analysis: false to disable.`;
135
135
  }
@@ -1,17 +1,23 @@
1
1
  import { type AnalyzeDeps } from "./service.js";
2
- import { type AutomaticAnalysisView, type AutomaticAnalysisOutcome, type AutomaticAnalysisCancellation } from "./job.js";
2
+ import { type AutomaticAnalysisView, type AutomaticAnalysisOutcome, type AutomaticAnalysisRefusal, type AutomaticAnalysisCancellation } from "./job.js";
3
3
  import { type AnalysisConfig } from "./types.js";
4
4
  export type { AutomaticAnalysisView, AutomaticAnalysisOutcome, AutomaticAnalysisCancellation, } from "./job.js";
5
5
  export type AutomaticAnalysisDeps = Omit<AnalyzeDeps, "analysisId" | "beforeDispatch"> & {
6
6
  /** A missing default key records a skip before admission, preserving a successful recording. */
7
7
  defaultRequest?: boolean;
8
+ /**
9
+ * A route's reason not to analyze this run, such as a local study whose cleanup is unconfirmed.
10
+ * It is checked after the job is claimed, so the skip is recorded where the CLI, the TUI and the
11
+ * Observer read it.
12
+ */
13
+ refusal?: () => AutomaticAnalysisRefusal | undefined;
8
14
  /** Expand an omitted output limit only within the existing admission budget. */
9
15
  preferLargerOutput?: boolean;
10
16
  };
11
17
  /** Small read-only TUI/CLI projection. Neither this nor Observer can resume a job. */
12
- export declare function readAutomaticStudyAnalysis(cwd: string, runId: string): Promise<AutomaticAnalysisView | undefined>;
13
- export declare function requestAutomaticStudyAnalysisCancellation(cwd: string, runId: string): Promise<AutomaticAnalysisCancellation>;
18
+ export declare function readAutomaticAnalysis(cwd: string, runId: string): Promise<AutomaticAnalysisView | undefined>;
19
+ export declare function requestAutomaticAnalysisCancellation(cwd: string, runId: string): Promise<AutomaticAnalysisCancellation>;
14
20
  /** One permanent claim per completed run, consumed even when preparation/cancellation fails.
15
21
  * Only the original producer calls this after all recording writes return.
16
22
  * No file reader, restart recovery, Observer poll or export calls this function. */
17
- export declare function runAutomaticStudyAnalysis(cwdInput: string, runId: string, configInput: AnalysisConfig, deps?: AutomaticAnalysisDeps): Promise<AutomaticAnalysisOutcome>;
23
+ export declare function runAutomaticAnalysis(cwdInput: string, runId: string, configInput: AnalysisConfig, deps?: AutomaticAnalysisDeps): Promise<AutomaticAnalysisOutcome>;
@@ -5,10 +5,10 @@ import { refreshObserver, analyzeRun, readCompletedAnalysisSource, resolveAnalys
5
5
  import { preferLargerAnalysisOutput, ANALYSIS_PROMPT_VERSION } from "./execute.js";
6
6
  import { captureEvidence } from "./evidence.js";
7
7
  import { hashAnalysisValue } from "./validation.js";
8
- import { claimAutomaticStudyAnalysis, readAutomaticStudyAnalysisPrepared, requestAutomaticStudyAnalysisCancellationPrepared, } from "./job.js";
8
+ import { claimAutomaticAnalysis, readAutomaticAnalysisPrepared, requestAutomaticAnalysisCancellationPrepared, } from "./job.js";
9
9
  import { ANALYSIS_ID_PATTERN } from "./types.js";
10
- import { readStudyAnalysisVersion } from "./store.js";
11
- import { readStudyAnalysisExecution } from "./store-executions.js";
10
+ import { readAnalysisVersion } from "./store.js";
11
+ import { readAnalysisExecution } from "./store-executions.js";
12
12
  import { physicalCwdOf, resolvePhysicalCwd } from "../run/paths.js";
13
13
  const CANCELLATION_POLL_MS = 250;
14
14
  // Well inside AUTOMATIC_ANALYSIS_STALE_MS, so a live owner never reads as stale.
@@ -26,18 +26,18 @@ function hasParticipantEvidence(bundle) {
26
26
  });
27
27
  }
28
28
  /** Small read-only TUI/CLI projection. Neither this nor Observer can resume a job. */
29
- export async function readAutomaticStudyAnalysis(cwd, runId) {
29
+ export async function readAutomaticAnalysis(cwd, runId) {
30
30
  if (!exactId(runId))
31
31
  return undefined;
32
32
  const prepared = await resolveRunPath(await resolvePhysicalCwd(cwd), runId).catch(() => null);
33
- return prepared ? readAutomaticStudyAnalysisPrepared(prepared) : undefined;
33
+ return prepared ? readAutomaticAnalysisPrepared(prepared) : undefined;
34
34
  }
35
- export async function requestAutomaticStudyAnalysisCancellation(cwd, runId) {
35
+ export async function requestAutomaticAnalysisCancellation(cwd, runId) {
36
36
  if (!exactId(runId))
37
37
  return { requested: false, reason: "AUTOMATIC_ANALYSIS_SOURCE_UNAVAILABLE" };
38
38
  const prepared = await resolveRunPath(await resolvePhysicalCwd(cwd), runId).catch(() => null);
39
39
  return prepared
40
- ? requestAutomaticStudyAnalysisCancellationPrepared(prepared)
40
+ ? requestAutomaticAnalysisCancellationPrepared(prepared)
41
41
  : { requested: false, reason: "AUTOMATIC_ANALYSIS_SOURCE_UNAVAILABLE" };
42
42
  }
43
43
  /** The job reason for an analysis that reached a status. */
@@ -114,8 +114,8 @@ async function persistOutcome(job, prepared, outcome) {
114
114
  const analysisId = outcome.result?.analysisId;
115
115
  const [entry, receipt] = analysisId
116
116
  ? await Promise.all([
117
- readStudyAnalysisVersion(prepared, analysisId),
118
- readStudyAnalysisExecution(prepared, analysisId),
117
+ readAnalysisVersion(prepared, analysisId),
118
+ readAnalysisExecution(prepared, analysisId),
119
119
  ])
120
120
  : [null, null];
121
121
  await job.update({
@@ -131,7 +131,7 @@ async function persistOutcome(job, prepared, outcome) {
131
131
  }),
132
132
  ...(entry?.analysis ? { analysisSha256: hashAnalysisValue(entry.analysis) } : {}),
133
133
  });
134
- const persisted = await readAutomaticStudyAnalysisPrepared(prepared);
134
+ const persisted = await readAutomaticAnalysisPrepared(prepared);
135
135
  return persisted?.state === "unknown"
136
136
  ? { ...outcome, state: "unknown", reason: persisted.reason }
137
137
  : outcome;
@@ -143,7 +143,7 @@ async function persistOutcome(job, prepared, outcome) {
143
143
  /** One permanent claim per completed run, consumed even when preparation/cancellation fails.
144
144
  * Only the original producer calls this after all recording writes return.
145
145
  * No file reader, restart recovery, Observer poll or export calls this function. */
146
- export async function runAutomaticStudyAnalysis(cwdInput, runId, configInput, deps = {}) {
146
+ export async function runAutomaticAnalysis(cwdInput, runId, configInput, deps = {}) {
147
147
  if (!exactId(runId))
148
148
  return skipped("AUTOMATIC_ANALYSIS_SOURCE_UNAVAILABLE");
149
149
  let cwd = path.resolve(cwdInput);
@@ -162,7 +162,7 @@ export async function runAutomaticStudyAnalysis(cwdInput, runId, configInput, de
162
162
  config = await (await import("./restricted-codex.js")).bindCodexAnalysisCliVersion(config, deps.detectCodexCliVersion);
163
163
  let job;
164
164
  try {
165
- const claimed = await claimAutomaticStudyAnalysis(prepared, {
165
+ const claimed = await claimAutomaticAnalysis(prepared, {
166
166
  configDigest: hashAnalysisValue(config),
167
167
  promptVersion: ANALYSIS_PROMPT_VERSION,
168
168
  });
@@ -211,7 +211,11 @@ export async function runAutomaticStudyAnalysis(cwdInput, runId, configInput, de
211
211
  try {
212
212
  await poll();
213
213
  const missingKey = config.provider !== "codex" && !hasKey;
214
- if (deps.defaultRequest === true && (missingKey || !participantEvidence)) {
214
+ const refused = deps.refusal?.();
215
+ if (refused !== undefined) {
216
+ outcome = skipped(refused);
217
+ }
218
+ else if (deps.defaultRequest === true && (missingKey || !participantEvidence)) {
215
219
  outcome = signal.aborted
216
220
  ? { state: "cancelled", reason: "AUTOMATIC_ANALYSIS_CANCELLED" }
217
221
  : skipped(missingKey
@@ -0,0 +1,23 @@
1
+ /** How many rejected outputs are kept across all runs; older ones are removed on each write. */
2
+ export declare const MAX_ANALYSIS_DIAGNOSTICS = 20;
3
+ export interface RejectedAnalysisOutput {
4
+ /** The allowlisted code the analysis artifact records. */
5
+ error: string;
6
+ /** Every failed validation rule, when validation ran. */
7
+ errors: string[];
8
+ /** The response after the narrative scrub when validation ran, else the parsed response. */
9
+ output: unknown;
10
+ }
11
+ export interface RejectedAnalysisRecord {
12
+ runId: string;
13
+ analysisId: string;
14
+ model: string;
15
+ promptVersion: string;
16
+ }
17
+ /** Internal fault-injection seam for tests. */
18
+ export interface AnalysisDiagnosticsHooks {
19
+ /** Runs after pruning has listed the records and before it removes any. */
20
+ beforeRemove?: () => Promise<void>;
21
+ }
22
+ /** Write one rejected output and prune to the newest MAX_ANALYSIS_DIAGNOSTICS. Returns its path relative to cwd. */
23
+ export declare function keepRejectedAnalysisOutput(cwd: string, record: RejectedAnalysisRecord, rejected: RejectedAnalysisOutput, hooks?: AnalysisDiagnosticsHooks): Promise<string>;
@@ -0,0 +1,112 @@
1
+ import { lstat, readdir, rmdir, unlink } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { decodeEscapes } from "../evidence/encoded-text.js";
4
+ import { redactText } from "../evidence/redaction.js";
5
+ import { assertPreparedSelectedOutputDirectory, assertSafeOutputPathSegment, bindExistingManagedHumanishOutputDirectory, prepareManagedHumanishOutputDirectory, writeContainedOutputFile, } from "../run/contained-output.js";
6
+ import { scrubTransientCommsText, transientCommsEncodedScrub, } from "../run/transient-comms-secrets.js";
7
+ import { ANALYSIS_ID_PATTERN } from "./types.js";
8
+ // A rejected analyst response is kept for diagnosis under .humanish/analysis-diagnostics/<run>/,
9
+ // outside every run directory. Export, verify and the Observer read run directories only, so the
10
+ // file never reaches a bundle, an export or a served page. The analysis artifact itself still
11
+ // records only the allowlisted code.
12
+ const ANALYSIS_DIAGNOSTICS_DIRECTORY = "analysis-diagnostics";
13
+ const ANALYSIS_DIAGNOSTIC_SCHEMA = "humanish.analysis-rejected-output.v1";
14
+ /** How many rejected outputs are kept across all runs; older ones are removed on each write. */
15
+ export const MAX_ANALYSIS_DIAGNOSTICS = 20;
16
+ const REDACTED = "[REDACTED_SECRET]";
17
+ const markers = (text) => text.split(REDACTED).length - 1;
18
+ /**
19
+ * Scrub every string, key and scalar in the output. The encoded scrub finds a known value written
20
+ * percent-encoded, escaped or base64-encoded and returns decoded text; a string where it finds
21
+ * nothing keeps its original spelling, so an escape in a rejected quote stays visible, and still
22
+ * gets the literal scrub. A number or boolean equal to a known value becomes the marker.
23
+ */
24
+ function scrubber() {
25
+ const encoded = transientCommsEncodedScrub();
26
+ const text = (value) => {
27
+ const found = encoded(value);
28
+ return redactText(markers(found) > markers(decodeEscapes(value)) ? found : scrubTransientCommsText(value));
29
+ };
30
+ const scrub = (value) => {
31
+ if (typeof value === "string")
32
+ return text(value);
33
+ if (typeof value === "number" || typeof value === "boolean")
34
+ return text(String(value)) === String(value) ? value : REDACTED;
35
+ if (Array.isArray(value))
36
+ return value.map(scrub);
37
+ if (value !== null && typeof value === "object")
38
+ return Object.fromEntries(Object.entries(value).map(([key, entry]) => [text(key), scrub(entry)]));
39
+ return value;
40
+ };
41
+ return scrub;
42
+ }
43
+ /** Write one rejected output and prune to the newest MAX_ANALYSIS_DIAGNOSTICS. Returns its path relative to cwd. */
44
+ export async function keepRejectedAnalysisOutput(cwd, record, rejected, hooks = {}) {
45
+ assertSafeOutputPathSegment(record.runId, "Run id");
46
+ if (!ANALYSIS_ID_PATTERN.test(record.analysisId))
47
+ throw new Error("Unsafe analysis id.");
48
+ const root = await prepareManagedHumanishOutputDirectory(cwd, ANALYSIS_DIAGNOSTICS_DIRECTORY, record.runId);
49
+ const file = `${record.analysisId}.json`;
50
+ const document = {
51
+ schema: ANALYSIS_DIAGNOSTIC_SCHEMA,
52
+ ...record,
53
+ recordedAt: new Date().toISOString(),
54
+ error: rejected.error,
55
+ errors: rejected.errors,
56
+ output: scrubber()(rejected.output),
57
+ };
58
+ await writeContainedOutputFile(root, file, `${JSON.stringify(document, null, 2)}\n`, "utf8");
59
+ // A failed prune leaves extra files that the next write removes.
60
+ await pruneAnalysisDiagnostics(cwd, hooks).catch(() => undefined);
61
+ return path.join(".humanish", ANALYSIS_DIAGNOSTICS_DIRECTORY, record.runId, file);
62
+ }
63
+ /**
64
+ * Remove one record, or with no name one emptied run directory, only while the directory is still
65
+ * the one listed: its path resolves to the same physical directory with the same identity, so a
66
+ * directory swapped for a symlink after listing is refused. The record must be a regular file.
67
+ */
68
+ async function removeListed(root, name) {
69
+ try {
70
+ await assertPreparedSelectedOutputDirectory(root);
71
+ if (name === undefined) {
72
+ await rmdir(root.physicalPath);
73
+ return;
74
+ }
75
+ const file = path.join(root.physicalPath, name);
76
+ if (!(await lstat(file)).isFile())
77
+ return;
78
+ await unlink(file);
79
+ }
80
+ catch {
81
+ // A changed directory, a vanished file or a directory that still holds a file is left alone.
82
+ }
83
+ }
84
+ async function pruneAnalysisDiagnostics(cwd, hooks) {
85
+ const parent = await bindExistingManagedHumanishOutputDirectory(cwd, ANALYSIS_DIAGNOSTICS_DIRECTORY);
86
+ if (!parent)
87
+ return;
88
+ const files = [];
89
+ for (const run of await readdir(parent.physicalPath, { withFileTypes: true })) {
90
+ if (!run.isDirectory())
91
+ continue;
92
+ const root = await bindExistingManagedHumanishOutputDirectory(cwd, ANALYSIS_DIAGNOSTICS_DIRECTORY, run.name);
93
+ if (!root)
94
+ continue;
95
+ for (const entry of await readdir(root.physicalPath, { withFileTypes: true })) {
96
+ if (!entry.isFile() || !entry.name.endsWith(".json"))
97
+ continue;
98
+ const modified = (await lstat(path.join(root.physicalPath, entry.name))).mtimeMs;
99
+ files.push({ root, name: entry.name, modified });
100
+ }
101
+ }
102
+ files.sort((a, b) => b.modified - a.modified ||
103
+ path.join(b.root.physicalPath, b.name).localeCompare(path.join(a.root.physicalPath, a.name)));
104
+ const removed = files.slice(MAX_ANALYSIS_DIAGNOSTICS);
105
+ if (removed.length === 0)
106
+ return;
107
+ await hooks.beforeRemove?.();
108
+ for (const { root, name } of removed)
109
+ await removeListed(root, name);
110
+ for (const root of new Set(removed.map((entry) => entry.root)))
111
+ await removeListed(root);
112
+ }
@@ -3,7 +3,7 @@ import { eventRecordIdOf, recordIdOf, streamParticipantIdOf } from "../run/parti
3
3
  import { cuaGoalSource } from "../actors/goal-source.js";
4
4
  import { isRecord } from "../run/type-guards.js";
5
5
  import { ACTION_CAPTURE_VERSION, } from "./types.js";
6
- import { decodesToPlainRelativePath, isStudyEvidencePath } from "../run/study-files.js";
6
+ import { decodesToPlainRelativePath, isEvidencePath } from "../run/evidence-files.js";
7
7
  // How a run bundle becomes analysis sources: each participant's recorded provenance and assignment,
8
8
  // the evidence entries its trace offers, and the order in which entries are admitted under the
9
9
  // count and byte budgets. captureEvidence selects from these, and validateAnalysisEvidence
@@ -127,7 +127,7 @@ export function sourceEntries(bundle, stream, captureVersion) {
127
127
  const capturePath = isCaptureItem(item, captureVersion) &&
128
128
  isRecord(item.screenshotRef) &&
129
129
  typeof item.screenshotRef.path === "string" &&
130
- isStudyEvidencePath(item.screenshotRef.path)
130
+ isEvidencePath(item.screenshotRef.path)
131
131
  ? item.screenshotRef.path
132
132
  : null;
133
133
  if (frameIds.has(item.id))
@@ -7,7 +7,7 @@ import { RUN_BUNDLE_FILE } from "../run/bundle.js";
7
7
  import { ACTION_CAPTURE_VERSION, } from "./types.js";
8
8
  import { digestAnalysisInput, hashAnalysisValue, validateAnalysisInputMetadata, } from "./validation.js";
9
9
  import { boundedText, fairShares, hasUnmappedCaptures, participantAssignment, participantSource, sourceEntries, sourceOrder, } from "./evidence-sources.js";
10
- import { readBoundedStudyFile, readBoundedStudyFileResult } from "../run/study-files.js";
10
+ import { readBoundedFile, readBoundedFileResult } from "../run/evidence-files.js";
11
11
  export const EVIDENCE_LIMITS = Object.freeze({
12
12
  participants: 16,
13
13
  evidence: 800,
@@ -96,7 +96,7 @@ export async function captureEvidence(prepared, bundleBytes, requested = {}) {
96
96
  }
97
97
  if (bundleBytes.length > limits.sourceBytes)
98
98
  throw new Error("ANALYSIS_SOURCE_TOO_LARGE");
99
- const current = await readBoundedStudyFile(prepared, RUN_BUNDLE_FILE, limits.sourceBytes);
99
+ const current = await readBoundedFile(prepared, RUN_BUNDLE_FILE, limits.sourceBytes);
100
100
  if (!current || !current.equals(bundleBytes))
101
101
  throw new Error("ANALYSIS_SOURCE_CHANGED");
102
102
  const bundle = parseSource(prepared, bundleBytes);
@@ -164,7 +164,7 @@ export async function captureEvidence(prepared, bundleBytes, requested = {}) {
164
164
  };
165
165
  result.inputDigest = digestAnalysisInput(result);
166
166
  validateAnalysisInputMetadata(result);
167
- const after = await readBoundedStudyFile(prepared, RUN_BUNDLE_FILE, limits.sourceBytes);
167
+ const after = await readBoundedFile(prepared, RUN_BUNDLE_FILE, limits.sourceBytes);
168
168
  if (!after || !after.equals(bundleBytes))
169
169
  throw new Error("ANALYSIS_SOURCE_CHANGED");
170
170
  return result;
@@ -229,7 +229,7 @@ async function selectCaptures(prepared, packings, sel, omissions) {
229
229
  packing.attempts++;
230
230
  sel.attemptedReads++;
231
231
  const source = sel.captureOrders[index][packing.captureCursor];
232
- const result = await readBoundedStudyFileResult(prepared, source.capturePath, allowance);
232
+ const result = await readBoundedFileResult(prepared, source.capturePath, allowance);
233
233
  if (result.state === "limit" &&
234
234
  result.size <= BigInt(limits.imageBytes) &&
235
235
  result.size <= BigInt(limits.totalImageBytes - sel.imageBytes) &&
@@ -458,7 +458,7 @@ export async function validateAnalysisEvidence(prepared, artifact, bundleBytes)
458
458
  throw new Error("ANALYSIS_CAPTURE_REFERENCE_INVALID");
459
459
  let hash = checkedCaptures.get(source.capturePath);
460
460
  if (hash === undefined) {
461
- const bytes = await readBoundedStudyFile(prepared, source.capturePath, EVIDENCE_LIMITS.imageBytes);
461
+ const bytes = await readBoundedFile(prepared, source.capturePath, EVIDENCE_LIMITS.imageBytes);
462
462
  if (!bytes || screenshotEvidenceError(source.capturePath, bytes) !== null)
463
463
  throw new Error("ANALYSIS_CAPTURE_UNAVAILABLE");
464
464
  checkedImageBytes += bytes.length;
@@ -476,7 +476,7 @@ export async function validateAnalysisEvidence(prepared, artifact, bundleBytes)
476
476
  artifact.evidence.length) {
477
477
  throw new Error("ANALYSIS_COVERAGE_INCOMPLETE");
478
478
  }
479
- const current = await readBoundedStudyFile(prepared, RUN_BUNDLE_FILE, EVIDENCE_LIMITS.sourceBytes);
479
+ const current = await readBoundedFile(prepared, RUN_BUNDLE_FILE, EVIDENCE_LIMITS.sourceBytes);
480
480
  if (!current?.equals(bundleBytes))
481
481
  throw new Error("ANALYSIS_SOURCE_CHANGED");
482
482
  }
@@ -1,5 +1,6 @@
1
1
  import { type AnalysisArtifact, type AnalysisConfig, type AnalysisInput } from "./types.js";
2
2
  import { type AnalysisFetch, type AnalysisProvider } from "./provider.js";
3
+ import type { RejectedAnalysisOutput } from "./diagnostics.js";
3
4
  export declare const ANALYSIS_PROMPT_VERSION = "study-evidence-6";
4
5
  export declare const isSupportedAnalysisModel: (model: string) => boolean;
5
6
  /** The OpenAI output limit when a lab omits maxOutputTokens, and the most it may ask for. */
@@ -21,7 +22,7 @@ export interface AnalysisProgress {
21
22
  status?: AnalysisArtifact["status"];
22
23
  }
23
24
  /**
24
- * Local admission ESTIMATE, not a provider-enforced billed-spend guarantee. No token-count API
25
+ * Local admission estimate, not a provider-enforced billed-spend guarantee. No token-count API
25
26
  * call, credential, or network access. One UTF-8 byte/token for all text/schema plus framing is
26
27
  * intentionally conservative. Each high-detail image is priced from its PNG size by the vision
27
28
  * guide's patch formula (see image-tokens.ts), or at the 3,000-token ceiling when its size or the
@@ -46,6 +47,11 @@ interface RunAnalysisOptions {
46
47
  /** Set by automatic analysis to its job attempt id, claimed before any provider call. */
47
48
  analysisId?: string;
48
49
  beforeDispatch?: (context: AnalysisDispatchContext) => Promise<void>;
50
+ /** Receives the provider's run warnings, such as Codex notification methods humanish does not know. */
51
+ warnings?: string[];
52
+ /** Receives a completed response that failed validation, for local diagnosis only. The
53
+ * artifact still records only the allowlisted code. */
54
+ onRejectedOutput?: (rejected: RejectedAnalysisOutput) => void;
49
55
  }
50
56
  /** Explicit invocation or an opted-in post-run owner; Observer readers never call this. */
51
57
  export declare function runAnalysis(input: AnalysisInput, config: AnalysisConfig, options: RunAnalysisOptions): Promise<AnalysisArtifact>;