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
@@ -73,6 +73,9 @@ export async function doctor(cwdInput, options = {}) {
73
73
  env,
74
74
  agents,
75
75
  keyPresent: (name) => probes.some((probe) => probe.name === name && probe.source !== null),
76
+ ...(options.codexParticipantReadiness === undefined
77
+ ? {}
78
+ : { codexParticipantReadiness: options.codexParticipantReadiness }),
76
79
  })
77
80
  : undefined;
78
81
  const checks = [
@@ -158,7 +161,7 @@ async function projectChecks(projectRoot) {
158
161
  ];
159
162
  }
160
163
  /**
161
- * The optional peer dep every live browser and terminal lane needs (#346). `npx -y humanish` does
164
+ * The optional peer dep every live browser and terminal route needs (#346). `npx -y humanish` does
162
165
  * not pull optional peers, so an adopter's FIRST live run used to fail on it — safely and at $0,
163
166
  * but as a burned first impression on the flagship path. Answering it here means the readiness
164
167
  * command actually answers readiness.
@@ -250,11 +253,16 @@ function keyChecks(probes, receivingKey, setup, agents) {
250
253
  : probe.name === "E2B_API_KEY" ||
251
254
  (probe.name === "OPENAI_API_KEY" &&
252
255
  !agents.some((agent) => agent.authStatus === "authenticated"));
256
+ // A key the lab is known not to read says so, so a present but unused key does not read as
257
+ // one the run will use.
258
+ const unused = setup?.reads !== undefined && !setup.reads.has(probe.name);
253
259
  return {
254
260
  name: `key ${probe.name}`,
255
261
  ok: present || !required,
256
262
  message: present
257
- ? `supplied by ${probe.source}; presence only, validity not tested`
263
+ ? unused
264
+ ? `present (${probe.source}), not used by this lab`
265
+ : `supplied by ${probe.source}; presence only, validity not tested`
258
266
  : required
259
267
  ? `missing from every source — ${hint}`
260
268
  : `not required for ${setup ? "the selected participant route" : "every route"}; ${hint}`,
package/dist/cli/io.d.ts CHANGED
@@ -18,14 +18,13 @@ export interface LabCommandOptions {
18
18
  dryRun?: boolean | undefined;
19
19
  envFile?: string | undefined;
20
20
  json?: boolean | undefined;
21
- lanes?: string | undefined;
22
21
  open?: boolean | undefined;
22
+ participants?: string | undefined;
23
23
  port?: string | undefined;
24
24
  rerunFailedFrom?: string | undefined;
25
25
  runId?: string | undefined;
26
26
  /** #316: repo-relative path to an adopter scorer module; overrides review.scorer.ref when set. */
27
27
  scorer?: string | undefined;
28
- sims?: string | undefined;
29
28
  expose?: boolean | undefined;
30
29
  tunnel?: "ngrok" | undefined;
31
30
  tunnelDomain?: string | undefined;
@@ -59,13 +58,15 @@ export declare function discoverCliKeys(args: {
59
58
  cwd: string;
60
59
  env?: NodeJS.ProcessEnv;
61
60
  onDiscovered?: (names: string[]) => void;
61
+ /** Announce only the fills of these names; every key is still filled. Undefined announces all. */
62
+ announced?: ReadonlySet<string>;
62
63
  }): Promise<void>;
63
64
  export declare function parseLabCount(value: string | undefined, fallback: number): number | null;
64
65
  export declare function parseParticipantIds(value: string | undefined): string[];
65
66
  export declare function writeResult<T>(command: Command, io: CliIo, result: T, formatHuman: (result: T) => string): void;
66
- export declare function noteStudyFacts(command: Command, facts: TelemetryProperties): void;
67
+ export declare function noteRunFacts(command: Command, facts: TelemetryProperties): void;
67
68
  /** Exported for tests: the facts writeResult read off a command's result document. */
68
- export declare function studyFactsFor(command: Command): TelemetryProperties;
69
+ export declare function runFactsFor(command: Command): TelemetryProperties;
69
70
  export declare function formatRunHuman(result: RunResult): string;
70
71
  export declare function parsePositiveInteger(value: string): number | null;
71
72
  export declare function parseTimeoutMs(value: string): number | null;
package/dist/cli/io.js CHANGED
@@ -6,7 +6,7 @@ import { fileURLToPath } from "node:url";
6
6
  import { Command } from "commander";
7
7
  import { loadEnvFile } from "../keys/env-file.js";
8
8
  import { discoverProviderKeys } from "../keys/key-resolution.js";
9
- import { deriveStudyFacts } from "./telemetry.js";
9
+ import { deriveRunFacts } from "./telemetry.js";
10
10
  import { forTerminal } from "../routes/terminal/encoding.js";
11
11
  export const CLI_RESPONSE_SCHEMA = "humanish.cli-response.v1";
12
12
  function readCliVersion() {
@@ -89,6 +89,7 @@ export async function discoverCliKeys(args) {
89
89
  cwd: args.cwd,
90
90
  env: args.env ?? process.env,
91
91
  announce: (line) => args.io.writeErr(`${line}\n`),
92
+ ...(args.announced === undefined ? {} : { announced: args.announced }),
92
93
  });
93
94
  args.onDiscovered?.(discovered.map((fill) => fill.name));
94
95
  }
@@ -129,6 +130,9 @@ export function writeResult(command, io, result, formatHuman) {
129
130
  const analysis = output.automaticAnalysis;
130
131
  if (analysis)
131
132
  io.writeOut(`analysis: ${analysis.state}${analysis.reason ? ` (${analysis.reason})` : ""}\n`);
133
+ const rejected = analysis?.result?.rejectedOutputPath;
134
+ if (rejected)
135
+ io.writeOut(`analysis: ${analysis.result?.error?.code ?? "rejected"}; the rejected output is kept locally at ${rejected}\n`);
132
136
  if (defaultAnalysisOverBudget(output))
133
137
  io.writeOut(overBudgetAnalysisHint(output));
134
138
  }
@@ -136,17 +140,17 @@ export function writeResult(command, io, result, formatHuman) {
136
140
  markInvocationEnvelopeWritten(command);
137
141
  // Every backend's result passes through here, so this is where a study's facts get read for
138
142
  // telemetry — not in each backend, which is how they went unreported for two releases.
139
- noteStudyFacts(command, deriveStudyFacts(result));
143
+ noteRunFacts(command, deriveRunFacts(result));
140
144
  }
141
- const studyFactsByCommand = new WeakMap();
142
- export function noteStudyFacts(command, facts) {
145
+ const runFactsByCommand = new WeakMap();
146
+ export function noteRunFacts(command, facts) {
143
147
  if (Object.keys(facts).length === 0)
144
148
  return;
145
- studyFactsByCommand.set(command, { ...studyFactsByCommand.get(command), ...facts });
149
+ runFactsByCommand.set(command, { ...runFactsByCommand.get(command), ...facts });
146
150
  }
147
151
  /** Exported for tests: the facts writeResult read off a command's result document. */
148
- export function studyFactsFor(command) {
149
- return { ...studyFactsByCommand.get(command) };
152
+ export function runFactsFor(command) {
153
+ return { ...runFactsByCommand.get(command) };
150
154
  }
151
155
  export function formatRunHuman(result) {
152
156
  if (!result.ok) {
@@ -155,7 +159,7 @@ export function formatRunHuman(result) {
155
159
  return ([
156
160
  `humanish run ${result.mode}`,
157
161
  `run: ${result.runId}`,
158
- ...(result.simCount === undefined ? [] : [`sims: ${result.simCount}`]),
162
+ ...(result.simCount === undefined ? [] : [`participants: ${result.simCount}`]),
159
163
  `bundle: ${result.bundlePath}`,
160
164
  `review: ${result.reviewPath}`,
161
165
  ...result.warnings.map((warning) => `warning: ${warning}`),
@@ -16,7 +16,7 @@ import { registerCleanupCommand, registerExportCommand, registerReclaimCommand,
16
16
  import { registerDoctorCommand, registerInitCommand, registerKeysCommand, registerTelemetryCommand, } from "./commands/setup.js";
17
17
  import { defaultTuiRuntime, registerTuiCommand } from "./commands/tui.js";
18
18
  import { registerWatchCommand } from "./commands/watch.js";
19
- import { CLI_RESPONSE_SCHEMA, CLI_VERSION, defaultIo, invocationEnvelopeAlreadyWritten, studyFactsFor, wantsJson, } from "./io.js";
19
+ import { CLI_RESPONSE_SCHEMA, CLI_VERSION, defaultIo, invocationEnvelopeAlreadyWritten, runFactsFor, wantsJson, } from "./io.js";
20
20
  // Command boundary catch-all (fix set point 1): every leaf command is created
21
21
  // through HumanishCommand.createCommand, and HumanishCommand.action wraps the caller's
22
22
  // handler so an uncaught throw or promise rejection can never escape as a raw
@@ -75,7 +75,7 @@ async function recordCommandTelemetry(command, ok, durationMs, exitCode) {
75
75
  // result document when it was written; see writeResult. Without them every `run` event
76
76
  // looked the same whether it was a dry run or the first live study that ever worked.
77
77
  properties: {
78
- ...studyFactsFor(command),
78
+ ...runFactsFor(command),
79
79
  command: name,
80
80
  ok: ok && exitCode === 0,
81
81
  exitCode,
@@ -97,7 +97,7 @@ async function recordCommandTelemetry(command, ok, durationMs, exitCode) {
97
97
  * HUMANISH_DEBUG_HANDLES=1: after a command's handler settles, name what is still keeping the
98
98
  * process alive (#581). A live terminal run wrote its result 64 s in and the CLI stayed up for
99
99
  * sixteen more minutes; nothing in the bundle could say what held it, and an in-process probe of
100
- * the lane found nothing of ours. This is the one line that answers it next time: the resource
100
+ * the run found nothing of ours. This is the one line that answers it next time: the resource
101
101
  * types Node reports, once, to stderr, after one macrotask so settled work has cleared.
102
102
  */
103
103
  function reportActiveHandles(command, io) {
@@ -0,0 +1,11 @@
1
+ import type { CliIo } from "./io.js";
2
+ /** `--participants`, or `--lanes` when only the older spelling is set. */
3
+ export declare function participantsOption(io: CliIo, options: {
4
+ participants?: string | undefined;
5
+ lanes?: string | undefined;
6
+ }): string | undefined;
7
+ /** `--count`, or `--sims` when only the older spelling is set. */
8
+ export declare function countOption(io: CliIo, options: {
9
+ count?: string | undefined;
10
+ sims?: string | undefined;
11
+ }): string | undefined;
@@ -0,0 +1,18 @@
1
+ // Options renamed in this minor keep their older name for one more minor, so a skill installed
2
+ // before the rename still runs. The older name is hidden from --help, sets the same value and
3
+ // prints one stderr line naming the new name. Each command reads its options through these
4
+ // functions once, at the top of its action, so one invocation prints the line once.
5
+ function renamedOption(io, name, value, olderName, olderValue) {
6
+ if (olderValue !== undefined) {
7
+ io.writeErr(`warning: --${olderName} is deprecated and is removed in the next minor. Use --${name}.\n`);
8
+ }
9
+ return value ?? olderValue;
10
+ }
11
+ /** `--participants`, or `--lanes` when only the older spelling is set. */
12
+ export function participantsOption(io, options) {
13
+ return renamedOption(io, "participants", options.participants, "lanes", options.lanes);
14
+ }
15
+ /** `--count`, or `--sims` when only the older spelling is set. */
16
+ export function countOption(io, options) {
17
+ return renamedOption(io, "count", options.count, "sims", options.sims);
18
+ }
@@ -15,7 +15,7 @@ export interface TelemetryProperties {
15
15
  /** One of humanish's OWN error codes (`HUMANISH_*`), never a message. Which failure ends a
16
16
  * first run is the question the funnel exists to answer. */
17
17
  errorCode?: string;
18
- /** Finite result categories and recorded stop causes; never raw errors or lane details. */
18
+ /** Finite result categories and recorded stop causes; never raw errors or participant details. */
19
19
  diagnosticCategory?: string;
20
20
  stopCause?: string;
21
21
  }
@@ -87,7 +87,7 @@ export declare function buildPayload(args: {
87
87
  * or an outcome — the vocabulary existed, nothing populated it — so the one question telemetry
88
88
  * was added to answer ("does anyone get to a working LIVE first run") had no answer in the data.
89
89
  */
90
- export declare function deriveStudyFacts(result: unknown): TelemetryProperties;
90
+ export declare function deriveRunFacts(result: unknown): TelemetryProperties;
91
91
  /** The notice, shown once, before anything is sent. */
92
92
  export declare const TELEMETRY_NOTICE: string;
93
93
  export interface SendDeps {
@@ -241,7 +241,7 @@ function asRecord(value) {
241
241
  * or an outcome — the vocabulary existed, nothing populated it — so the one question telemetry
242
242
  * was added to answer ("does anyone get to a working LIVE first run") had no answer in the data.
243
243
  */
244
- export function deriveStudyFacts(result) {
244
+ export function deriveRunFacts(result) {
245
245
  const r = asRecord(result);
246
246
  if (!r)
247
247
  return {};
@@ -1,7 +1,7 @@
1
1
  import type { ReceivingSurfaceFile } from "./receiving-types.js";
2
2
  import type { CommsMessage } from "./types.js";
3
3
  /** [fromOrigin, toOrigin] pairs: rewrite a link/href whose origin is the app's in-sandbox origin into a
4
- * lane-reachable origin, so the persona's browser can actually follow the verify link when it clicks. */
4
+ * participant-reachable origin, so the persona's browser can actually follow the verify link when it clicks. */
5
5
  export type OriginMap = Array<[string, string]>;
6
6
  export interface InboxRenderOptions {
7
7
  originMap?: OriginMap;
@@ -10,7 +10,7 @@ export interface CommsCatchHostOptions {
10
10
  /** Bearer token required on GET /deliveries. Strongly recommended when the host is reachable. */
11
11
  token?: string;
12
12
  /** SECOND port for the READ-ONLY inbox listener bound to 0.0.0.0, so a persona on another machine
13
- * (or in a per-lane desktop) can reach the inbox. Omit to stay loopback-only. */
13
+ * (or on a participant's desktop) can reach the inbox. Omit to stay loopback-only. */
14
14
  inboxPort?: number;
15
15
  /** Optional loopback SMTP listener sharing the HTTP capture and inbox pipeline. */
16
16
  smtpPort?: number;
@@ -4,10 +4,10 @@ import path from "node:path";
4
4
  import { parse, stringify } from "yaml";
5
5
  import { COMMS_PROVIDERS, DEFAULT_COMMS_PROVIDER, isReceivingProviderId, } from "./providers.js";
6
6
  import { assertPreparedSelectedOutputDirectory, bindExistingManagedHumanishOutputDirectory, prepareManagedHumanishOutputDirectory, readContainedRegularFile, writeContainedOutputFile, } from "../run/contained-output.js";
7
+ import { isRecord } from "../run/type-guards.js";
7
8
  const COMMS_CONNECTIONS_SCHEMA = "humanish.comms-connections.v1";
8
9
  export const COMMS_CONFIG_PATH = ".humanish/local/comms.yaml";
9
10
  export { COMMS_PROVIDERS };
10
- const record = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
11
11
  const validName = (name) => /^[a-z][a-z0-9-]{0,47}$/.test(name) && name !== "constructor" && name !== "prototype";
12
12
  const empty = () => ({ schema: COMMS_CONNECTIONS_SCHEMA, connections: {} });
13
13
  async function exists(file) {
@@ -22,16 +22,16 @@ async function exists(file) {
22
22
  }
23
23
  }
24
24
  function parseConnections(raw) {
25
- if (!record(raw) ||
25
+ if (!isRecord(raw) ||
26
26
  raw.schema !== COMMS_CONNECTIONS_SCHEMA ||
27
- !record(raw.connections) ||
27
+ !isRecord(raw.connections) ||
28
28
  Object.keys(raw).some((key) => key !== "schema" && key !== "connections") ||
29
29
  Object.keys(raw.connections).length > 32)
30
30
  throw new Error("Invalid connection configuration.");
31
31
  const connections = {};
32
32
  for (const [name, value] of Object.entries(raw.connections)) {
33
33
  if (!validName(name) ||
34
- !record(value) ||
34
+ !isRecord(value) ||
35
35
  !isReceivingProviderId(value.provider) ||
36
36
  typeof value.apiKeyEnv !== "string" ||
37
37
  !/^[A-Z][A-Z0-9_]{0,99}$/.test(value.apiKeyEnv) ||
@@ -0,0 +1,27 @@
1
+ import type { LabCommsEmail, LabCommsExternal } from "../lab/types.js";
2
+ import type { PreparedRunArtifactPaths } from "../run/paths.js";
3
+ /**
4
+ * Why a catch token cannot be used, or undefined when it can. An unset or empty token is none. A
5
+ * token that is not well-formed Unicode cannot be sent as a header or encoded as a URL, so it is
6
+ * refused with the short ones.
7
+ */
8
+ export declare function catchTokenRefusal(token: string | undefined): string | undefined;
9
+ /** The catch token the run sends: the value of `authTokenEnv`, when the lab names one. */
10
+ export declare function catchTokenOf(external: LabCommsExternal, env: Record<string, string | undefined>): string | undefined;
11
+ /**
12
+ * Drains the catch, routes its sends to the declared recipients' inboxes and writes the thread to
13
+ * `comms/thread.json`. Returns that path when a message matched, and the warnings to add to the
14
+ * run, each scrubbed of the run's known secret values and the catch token, then redacted. It never
15
+ * throws: the run continues without comms evidence.
16
+ */
17
+ export declare function collectExternalCommsEvidence(args: {
18
+ external: LabCommsExternal;
19
+ email: LabCommsEmail;
20
+ env: Record<string, string | undefined>;
21
+ runPaths: PreparedRunArtifactPaths;
22
+ /** The run's known secret values. The route may add to it until the drain runs. */
23
+ knownSecretValues: readonly string[];
24
+ }): Promise<{
25
+ path?: string;
26
+ warnings: string[];
27
+ }>;
@@ -0,0 +1,93 @@
1
+ // An adopter-hosted catch's thread, collected into a run. The computer-use and shared-world routes
2
+ // both call this, so every warning it returns is scrubbed the same way on both: an error or the
3
+ // catch URL can carry the catch's bearer token or a provisioned value, and neither has a secret's
4
+ // shape for pattern redaction to find.
5
+ import { redactText, toErrorMessage } from "../evidence/redaction.js";
6
+ import { scrubSecretValues } from "../evidence/secret-scrub.js";
7
+ import { addressedRecipients } from "../lab/parse/comms.js";
8
+ import { writeContainedOutputFile } from "../run/contained-output.js";
9
+ import { FakeInbox } from "./fake-inbox.js";
10
+ import { collectExternalCommsThread } from "./sandbox-catch.js";
11
+ /**
12
+ * The shortest catch bearer token humanish uses. The token guards GET /deliveries on a host the run
13
+ * reaches over the network, so it must not be guessable: 16 random base64 characters are 96 bits.
14
+ * The run also scrubs the token from every warning as a literal, and a short token would scrub
15
+ * ordinary words.
16
+ */
17
+ const MIN_CATCH_TOKEN_LENGTH = 16;
18
+ /** String.prototype.isWellFormed (ES2024, Node 20+); the ES2023 lib types do not declare it. */
19
+ function isWellFormed(value) {
20
+ return value.isWellFormed();
21
+ }
22
+ /**
23
+ * Why a catch token cannot be used, or undefined when it can. An unset or empty token is none. A
24
+ * token that is not well-formed Unicode cannot be sent as a header or encoded as a URL, so it is
25
+ * refused with the short ones.
26
+ */
27
+ export function catchTokenRefusal(token) {
28
+ if (token === undefined || token.length === 0)
29
+ return undefined;
30
+ if (!isWellFormed(token))
31
+ return "The comms catch token is not well-formed Unicode; use printable ASCII, such as the output of `openssl rand -hex 16`, on the catch and in the run.";
32
+ if (token.length >= MIN_CATCH_TOKEN_LENGTH)
33
+ return undefined;
34
+ return `The comms catch token is ${token.length} characters; it must be at least ${MIN_CATCH_TOKEN_LENGTH}, such as the output of \`openssl rand -hex 16\`, on the catch and in the run.`;
35
+ }
36
+ /** The catch token the run sends: the value of `authTokenEnv`, when the lab names one. */
37
+ export function catchTokenOf(external, env) {
38
+ return external.authTokenEnv === undefined ? undefined : env[external.authTokenEnv];
39
+ }
40
+ /**
41
+ * Drains the catch, routes its sends to the declared recipients' inboxes and writes the thread to
42
+ * `comms/thread.json`. Returns that path when a message matched, and the warnings to add to the
43
+ * run, each scrubbed of the run's known secret values and the catch token, then redacted. It never
44
+ * throws: the run continues without comms evidence.
45
+ */
46
+ export async function collectExternalCommsEvidence(args) {
47
+ const { external, email, env, runPaths, knownSecretValues } = args;
48
+ const authToken = catchTokenOf(external, env);
49
+ // Pattern redaction alone until the literal scrub exists, so a warning built in the catch below
50
+ // is never unredacted. The scrub is built inside the try, where a failure becomes that warning.
51
+ let scrubbed = (warnings) => warnings.map(redactText);
52
+ try {
53
+ const scrub = scrubSecretValues([
54
+ ...knownSecretValues,
55
+ ...(authToken === undefined ? [] : [authToken]),
56
+ ]);
57
+ scrubbed = (warnings) => warnings.map((warning) => redactText(scrub(warning)));
58
+ const channel = new FakeInbox();
59
+ const inboxes = [];
60
+ for (const recipient of addressedRecipients(email)) {
61
+ inboxes.push(await channel.provisionAddress(recipient.participantId, recipient.address));
62
+ }
63
+ const collected = await collectExternalCommsThread({
64
+ external: { ...external, ...(authToken === undefined ? {} : { authToken }) },
65
+ channel,
66
+ inboxes,
67
+ });
68
+ if (collected.artifact) {
69
+ const path = "comms/thread.json";
70
+ await writeContainedOutputFile(runPaths, path, `${JSON.stringify(collected.artifact, null, 2)}\n`, "utf8");
71
+ return { path, warnings: [] };
72
+ }
73
+ if (collected.captured > 0) {
74
+ return {
75
+ warnings: scrubbed([
76
+ `Comms catch captured ${collected.captured} email send(s) but none matched a declared recipient inbox — no comms evidence written. Declare comms.email.recipients[].address to match the address the app sends to.`,
77
+ ]),
78
+ };
79
+ }
80
+ return {
81
+ warnings: scrubbed([
82
+ `Comms catch captured ZERO email sends — your app never delivered mail through the catch at ${external.catchBaseUrl}. Verify the app's email-API base URL points at it and that the flow reached an email step.`,
83
+ ]),
84
+ };
85
+ }
86
+ catch (error) {
87
+ return {
88
+ warnings: scrubbed([
89
+ `Comms evidence collection failed against the adopter-hosted catch (run continues): ${String(toErrorMessage(error))}`,
90
+ ]),
91
+ };
92
+ }
93
+ }
@@ -4,6 +4,7 @@ import { constants } from "node:fs";
4
4
  import { lstat, mkdir, open, readdir, realpath, rename, unlink } from "node:fs/promises";
5
5
  import { homedir, hostname } from "node:os";
6
6
  import path from "node:path";
7
+ import { isRecord } from "../run/type-guards.js";
7
8
  import { isReceivingProviderId } from "./providers.js";
8
9
  const SCHEMA = "humanish.comms-lease-journal.v1";
9
10
  const MAX_BYTES = 1_048_576;
@@ -30,7 +31,6 @@ function fail(code = "authority_unavailable") {
30
31
  }
31
32
  const digest = (value) => createHash("sha256").update(value).digest("hex");
32
33
  const clone = (value) => structuredClone(value);
33
- const plain = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
34
34
  const safeString = (value, max = 512) => typeof value === "string" &&
35
35
  value.length > 0 &&
36
36
  value.length <= max &&
@@ -45,14 +45,14 @@ export function sameReceivingIdentity(a, b) {
45
45
  a.scopeId === b.scopeId);
46
46
  }
47
47
  export function validReceivingIdentity(value) {
48
- return (plain(value) &&
48
+ return (isRecord(value) &&
49
49
  isReceivingProviderId(value.provider) &&
50
50
  safeString(value.accountId) &&
51
51
  ["organization", "pod", "inbox"].includes(String(value.scopeType)) &&
52
52
  safeString(value.scopeId));
53
53
  }
54
54
  export function validReceivingLease(value, clientId) {
55
- return (plain(value) &&
55
+ return (isRecord(value) &&
56
56
  value.clientId === clientId &&
57
57
  safeString(value.resourceId) &&
58
58
  safeString(value.address, 320) &&
@@ -79,7 +79,7 @@ function ownerLiveness(owner) {
79
79
  }
80
80
  }
81
81
  function validateOwner(value) {
82
- return (plain(value) &&
82
+ return (isRecord(value) &&
83
83
  Number.isSafeInteger(value.pid) &&
84
84
  value.pid > 0 &&
85
85
  safeString(value.host) &&
@@ -95,9 +95,9 @@ function parseJournal(text) {
95
95
  catch {
96
96
  return fail();
97
97
  }
98
- if (!plain(value) ||
98
+ if (!isRecord(value) ||
99
99
  value.schema !== SCHEMA ||
100
- !plain(value.project) ||
100
+ !isRecord(value.project) ||
101
101
  !safeString(value.project.path, 4096) ||
102
102
  !path.isAbsolute(value.project.path) ||
103
103
  !Number.isSafeInteger(value.project.dev) ||
@@ -123,7 +123,7 @@ function parseJournal(text) {
123
123
  const leaseIds = new Set();
124
124
  const resources = new Set();
125
125
  for (const item of value.leases) {
126
- if (!plain(item) ||
126
+ if (!isRecord(item) ||
127
127
  typeof item.participantId !== "string" ||
128
128
  !LOCAL_ID.test(item.participantId) ||
129
129
  participants.has(item.participantId) ||
@@ -1,5 +1,5 @@
1
1
  // The addressed message bus for captured mail (#297): the email or SMS a persona receives off the
2
- // app, as a testable surface. One port, addressed by lane, so the inbox surface and the evidence
2
+ // app, as a testable surface. One port, addressed by participant, so the inbox surface and the evidence
3
3
  // writer read messages without knowing how they were captured.
4
4
  //
5
5
  // PUBLIC-SAFETY: raw address values, message bodies, links, and codes are RUNTIME-ONLY. Only the
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Browser chrome that belongs to the host environment (update prompts, password
3
3
  * save bubbles, first-run UI) pollutes product evidence. Keep these knobs in one
4
- * place so browser-backed lanes can prefer product pixels over browser UI.
4
+ * place so browser-backed routes can prefer product pixels over browser UI.
5
5
  */
6
6
  export declare const CHROMIUM_EVIDENCE_HYGIENE_FLAGS: readonly ["--disable-background-networking", "--disable-component-update", "--disable-default-apps", "--disable-domain-reliability", "--disable-extensions", "--disable-sync", "--no-default-browser-check", "--no-first-run", "--password-store=basic", "--use-mock-keychain"];
7
7
  export declare function chromiumEvidenceProfilePreferencesJson(): string;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Browser chrome that belongs to the host environment (update prompts, password
3
3
  * save bubbles, first-run UI) pollutes product evidence. Keep these knobs in one
4
- * place so browser-backed lanes can prefer product pixels over browser UI.
4
+ * place so browser-backed routes can prefer product pixels over browser UI.
5
5
  */
6
6
  export const CHROMIUM_EVIDENCE_HYGIENE_FLAGS = [
7
7
  "--disable-background-networking",
@@ -101,7 +101,7 @@ export function publicPathForTrace(value, rootCwd) {
101
101
  // ---------------------------------------------------------------------------
102
102
  // Screenshot redaction.
103
103
  //
104
- // Computer-use lanes capture raw desktop frames that can contain secrets, PII,
104
+ // Computer-use participants capture raw desktop frames that can contain secrets, PII,
105
105
  // or a logged-in third-party UI. A raw frame must never reach a public artifact.
106
106
  // redactScreenshot is the fail-closed primitive that gates that surface. It
107
107
  // always returns a freshly re-encoded, downscaled, box-blurred thumbnail (or a
@@ -0,0 +1,9 @@
1
+ /**
2
+ * A scrub that replaces each non-empty value and its encoded forms with `[REDACTED_SECRET]`. Every
3
+ * occurrence of every form is found on its own, and overlapping finds are merged into one span, so
4
+ * a value that overlaps another is removed whole wherever each starts. The scrub reads the text
5
+ * through decodeEscapes, the decoder verify uses, and returns the decoded text: a warning with
6
+ * percent-encoding, JSON escapes or HTML references shows them decoded, with the values removed.
7
+ * That finds a value written partly encoded, which no single encoded form matches.
8
+ */
9
+ export declare function scrubSecretValues(values: readonly string[]): (text: string) => string;
@@ -0,0 +1,96 @@
1
+ // Literal scrubbing of secret values from text that leaves the host, such as an error quoted in a
2
+ // run's warnings. A value is found as written and in the encoded forms an HTTP client, a log line or
3
+ // a JSON body gives it, so a token that reaches the text percent-encoded or base64-encoded is still
4
+ // removed. Pattern redaction (redactText) runs after this and finds secrets by shape.
5
+ import { decodeEscapes } from "./encoded-text.js";
6
+ const REDACTED = "[REDACTED_SECRET]";
7
+ // A marker already in the text, from this scrub or from redactText. No value is matched inside one.
8
+ const MARKER = /\[REDACTED_[A-Z0-9_]+\]/g;
9
+ // The shortest encoded form searched for. Shorter base64 or hex runs are ordinary text often enough
10
+ // that matching them would redact words. A value itself is searched for at any length.
11
+ const MIN_ENCODED_FORM = 8;
12
+ /**
13
+ * The characters of a value's base64 encoding that do not depend on its neighbours, with 0, 1 or
14
+ * 2 bytes before it. A prefix byte shares the next character with the value, and so does the byte
15
+ * after a partial last group, so those characters are dropped.
16
+ */
17
+ function base64Middles(bytes, encoding) {
18
+ return [0, 1, 2].map((offset) => {
19
+ const encoded = Buffer.concat([Buffer.alloc(offset), bytes])
20
+ .toString(encoding)
21
+ .replace(/=+$/, "");
22
+ const total = offset + bytes.length;
23
+ const start = offset === 0 ? 0 : offset + 1;
24
+ const end = Math.floor(total / 3) * 4 + (total % 3);
25
+ return encoded.slice(start, end);
26
+ });
27
+ }
28
+ /** A value as written, and percent-encoded, JSON-escaped, base64 at each byte offset, base64url and hex. */
29
+ function encodedForms(value) {
30
+ const bytes = Buffer.from(value, "utf8");
31
+ const encoded = [
32
+ JSON.stringify(value).slice(1, -1),
33
+ bytes.toString("base64"),
34
+ bytes.toString("base64url"),
35
+ bytes.toString("hex"),
36
+ ...base64Middles(bytes, "base64"),
37
+ ...base64Middles(bytes, "base64url"),
38
+ ];
39
+ // encodeURIComponent throws on a lone surrogate. Such a value is still found as written.
40
+ try {
41
+ encoded.push(encodeURIComponent(value));
42
+ }
43
+ catch {
44
+ // no percent-encoded form
45
+ }
46
+ return [value, ...encoded.filter((form) => form.length >= MIN_ENCODED_FORM)];
47
+ }
48
+ /** Every occurrence of every form, outside the markers, merged where they overlap or touch. */
49
+ function secretSpans(text, forms) {
50
+ const markers = [];
51
+ for (const match of text.matchAll(MARKER)) {
52
+ const start = match.index ?? 0;
53
+ markers.push([start, start + match[0].length]);
54
+ }
55
+ const spans = [];
56
+ for (const form of forms) {
57
+ for (let at = text.indexOf(form); at !== -1; at = text.indexOf(form, at + 1)) {
58
+ const end = at + form.length;
59
+ if (!markers.some(([start, stop]) => at < stop && end > start))
60
+ spans.push([at, end]);
61
+ }
62
+ }
63
+ spans.sort((left, right) => left[0] - right[0]);
64
+ const merged = [];
65
+ for (const span of spans) {
66
+ const last = merged.at(-1);
67
+ if (last !== undefined && span[0] <= last[1])
68
+ last[1] = Math.max(last[1], span[1]);
69
+ else
70
+ merged.push([span[0], span[1]]);
71
+ }
72
+ return merged;
73
+ }
74
+ /**
75
+ * A scrub that replaces each non-empty value and its encoded forms with `[REDACTED_SECRET]`. Every
76
+ * occurrence of every form is found on its own, and overlapping finds are merged into one span, so
77
+ * a value that overlaps another is removed whole wherever each starts. The scrub reads the text
78
+ * through decodeEscapes, the decoder verify uses, and returns the decoded text: a warning with
79
+ * percent-encoding, JSON escapes or HTML references shows them decoded, with the values removed.
80
+ * That finds a value written partly encoded, which no single encoded form matches.
81
+ */
82
+ export function scrubSecretValues(values) {
83
+ const forms = [...new Set(values.filter((value) => value.length > 0).flatMap(encodedForms))];
84
+ if (forms.length === 0)
85
+ return (text) => text;
86
+ return (text) => {
87
+ const decoded = decodeEscapes(text);
88
+ let result = "";
89
+ let cursor = 0;
90
+ for (const [start, end] of secretSpans(decoded, forms)) {
91
+ result += decoded.slice(cursor, start) + REDACTED;
92
+ cursor = end;
93
+ }
94
+ return result + decoded.slice(cursor);
95
+ };
96
+ }