humanish 0.107.0 → 0.109.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 (671) hide show
  1. package/ARCHITECTURE.md +73 -69
  2. package/CHANGELOG.md +27 -0
  3. package/CONTEXT.md +51 -44
  4. package/README.md +148 -175
  5. package/SECURITY.md +16 -26
  6. package/TELEMETRY.md +8 -6
  7. package/dist/actors/affordance.d.ts +4 -4
  8. package/dist/actors/affordance.js +12 -12
  9. package/dist/actors/agent-session.d.ts +2 -2
  10. package/dist/actors/agent-session.js +7 -7
  11. package/dist/actors/codex/app-server-actor-trace.d.ts +1 -1
  12. package/dist/actors/codex/app-server-actor-trace.js +1 -1
  13. package/dist/actors/codex/app-server-trace.js +2 -1
  14. package/dist/actors/codex/app-server-ui.js +1 -1
  15. package/dist/actors/codex/codex-admission.d.ts +91 -0
  16. package/dist/actors/codex/codex-admission.js +150 -0
  17. package/dist/actors/codex/restricted-executable.d.ts +4 -4
  18. package/dist/actors/codex/restricted-executable.js +8 -8
  19. package/dist/actors/codex/restricted-launch.d.ts +2 -1
  20. package/dist/actors/codex/restricted-launch.js +1 -1
  21. package/dist/actors/codex/restricted-participant.js +20 -18
  22. package/dist/actors/codex/restricted-policy.d.ts +2 -2
  23. package/dist/actors/codex/restricted-policy.js +2 -2
  24. package/dist/actors/codex/restricted-session.d.ts +3 -3
  25. package/dist/actors/codex/restricted-session.js +10 -5
  26. package/dist/actors/computer-use/actor.d.ts +18 -18
  27. package/dist/actors/computer-use/actor.js +4 -4
  28. package/dist/actors/computer-use/admission-limit.d.ts +3 -3
  29. package/dist/actors/computer-use/admission-limit.js +4 -4
  30. package/dist/actors/computer-use/executor-error.d.ts +2 -2
  31. package/dist/actors/computer-use/executor-error.js +3 -3
  32. package/dist/actors/computer-use/loop/actions.d.ts +3 -3
  33. package/dist/actors/computer-use/loop/actions.js +15 -15
  34. package/dist/actors/computer-use/loop/backstop.d.ts +3 -3
  35. package/dist/actors/computer-use/loop/backstop.js +14 -12
  36. package/dist/actors/computer-use/loop/debrief.js +2 -2
  37. package/dist/actors/computer-use/loop/ending.d.ts +6 -6
  38. package/dist/actors/computer-use/loop/ending.js +18 -17
  39. package/dist/actors/computer-use/loop/observation.d.ts +2 -2
  40. package/dist/actors/computer-use/loop/observation.js +15 -14
  41. package/dist/actors/computer-use/loop/provider-call.d.ts +1 -1
  42. package/dist/actors/computer-use/loop/provider-call.js +12 -12
  43. package/dist/actors/computer-use/loop/session.d.ts +4 -4
  44. package/dist/actors/computer-use/loop/session.js +4 -4
  45. package/dist/actors/computer-use/loop/spend.d.ts +1 -1
  46. package/dist/actors/computer-use/loop/spend.js +1 -1
  47. package/dist/actors/computer-use/loop/trace.js +11 -9
  48. package/dist/actors/computer-use/loop/types.d.ts +47 -45
  49. package/dist/actors/computer-use/loop/usage.d.ts +2 -2
  50. package/dist/actors/computer-use/loop/usage.js +2 -2
  51. package/dist/actors/computer-use/loop.d.ts +6 -1
  52. package/dist/actors/computer-use/loop.js +26 -4
  53. package/dist/actors/computer-use/openai-context.d.ts +37 -0
  54. package/dist/actors/computer-use/openai-context.js +214 -0
  55. package/dist/actors/computer-use/openai-provider.d.ts +13 -10
  56. package/dist/actors/computer-use/openai-provider.js +52 -41
  57. package/dist/actors/computer-use/openai-requests.d.ts +36 -10
  58. package/dist/actors/computer-use/openai-requests.js +82 -15
  59. package/dist/actors/computer-use/openai-wire.d.ts +19 -7
  60. package/dist/actors/computer-use/openai-wire.js +36 -34
  61. package/dist/actors/computer-use/provider-error.d.ts +4 -4
  62. package/dist/actors/computer-use/provider-error.js +6 -6
  63. package/dist/actors/contract.d.ts +82 -47
  64. package/dist/actors/contract.js +30 -24
  65. package/dist/actors/local-agent/claude-session.d.ts +1 -1
  66. package/dist/actors/local-agent/claude-session.js +13 -12
  67. package/dist/actors/local-agent/cli.d.ts +9 -7
  68. package/dist/actors/local-agent/cli.js +24 -28
  69. package/dist/actors/local-agent/readiness.d.ts +2 -2
  70. package/dist/actors/local-agent/readiness.js +4 -4
  71. package/dist/actors/reasoning-effort.js +8 -8
  72. package/dist/actors/registry.d.ts +12 -12
  73. package/dist/actors/registry.js +21 -20
  74. package/dist/actors/scripted-browser/actor.d.ts +10 -10
  75. package/dist/actors/scripted-browser/actor.js +17 -17
  76. package/dist/actors/scripted-browser/browser-command.js +1 -1
  77. package/dist/actors/scripted-browser/steps.js +1 -1
  78. package/dist/actors/scripted-browser/types.d.ts +1 -1
  79. package/dist/actors/stop-conditions.d.ts +1 -1
  80. package/dist/analysis/automatic-completion.d.ts +7 -7
  81. package/dist/analysis/automatic-completion.js +1 -1
  82. package/dist/analysis/automatic-config.d.ts +6 -6
  83. package/dist/analysis/automatic-config.js +1 -1
  84. package/dist/analysis/codex-config.d.ts +6 -2
  85. package/dist/analysis/codex-config.js +22 -14
  86. package/dist/analysis/execute.d.ts +1 -1
  87. package/dist/analysis/execute.js +1 -1
  88. package/dist/analysis/service.js +5 -5
  89. package/dist/analysis/types.d.ts +6 -0
  90. package/dist/browser-control/client.js +16 -16
  91. package/dist/browser-control/dispatcher.js +4 -4
  92. package/dist/browser-control/protocol.js +14 -14
  93. package/dist/browser-control/recording-transfer.js +4 -4
  94. package/dist/browser-control/transport.js +7 -7
  95. package/dist/cli/commands/analysis-signals.d.ts +2 -2
  96. package/dist/cli/commands/analyze.js +19 -16
  97. package/dist/cli/commands/codex.js +9 -9
  98. package/dist/cli/commands/comms.js +48 -35
  99. package/dist/cli/commands/feedback.js +20 -21
  100. package/dist/cli/commands/migrate.d.ts +4 -0
  101. package/dist/cli/commands/migrate.js +65 -0
  102. package/dist/cli/commands/observe.d.ts +0 -1
  103. package/dist/cli/commands/observe.js +136 -112
  104. package/dist/cli/commands/run-command.d.ts +10 -0
  105. package/dist/cli/commands/run-command.js +85 -0
  106. package/dist/cli/commands/runs.js +136 -139
  107. package/dist/cli/commands/setup.d.ts +2 -2
  108. package/dist/cli/commands/setup.js +27 -24
  109. package/dist/cli/commands/study-format.d.ts +9 -0
  110. package/dist/cli/commands/{lab-format.js → study-format.js} +39 -27
  111. package/dist/cli/commands/{lab-route-computer-use.d.ts → study-route-computer-use.d.ts} +6 -6
  112. package/dist/cli/commands/{lab-route-computer-use.js → study-route-computer-use.js} +17 -18
  113. package/dist/cli/commands/{lab-route-open.d.ts → study-route-open.d.ts} +4 -4
  114. package/dist/cli/commands/{lab-route-open.js → study-route-open.js} +2 -2
  115. package/dist/cli/commands/{lab-route-preview.d.ts → study-route-preview.d.ts} +7 -7
  116. package/dist/cli/commands/{lab-route-preview.js → study-route-preview.js} +7 -6
  117. package/dist/cli/commands/study-route-run.d.ts +19 -0
  118. package/dist/cli/commands/{lab-route-run.js → study-route-run.js} +6 -6
  119. package/dist/cli/commands/study-route-scripted.d.ts +17 -0
  120. package/dist/cli/commands/{lab-route-scripted.js → study-route-scripted.js} +4 -4
  121. package/dist/cli/commands/{lab-route-shared-world.d.ts → study-route-shared-world.d.ts} +5 -5
  122. package/dist/cli/commands/{lab-route-shared-world.js → study-route-shared-world.js} +10 -10
  123. package/dist/cli/commands/study-route-terminal.d.ts +17 -0
  124. package/dist/cli/commands/{lab-route-terminal.js → study-route-terminal.js} +4 -4
  125. package/dist/cli/commands/study-run.d.ts +9 -0
  126. package/dist/cli/commands/{lab-run.js → study-run.js} +44 -45
  127. package/dist/cli/commands/{lab-scorer.d.ts → study-scorer.d.ts} +7 -7
  128. package/dist/cli/commands/{lab-scorer.js → study-scorer.js} +2 -2
  129. package/dist/cli/commands/study.d.ts +3 -0
  130. package/dist/cli/commands/study.js +146 -0
  131. package/dist/cli/commands/tui.d.ts +4 -4
  132. package/dist/cli/commands/tui.js +31 -31
  133. package/dist/cli/commands/watch.js +71 -121
  134. package/dist/cli/deprecations.d.ts +6 -0
  135. package/dist/cli/deprecations.js +18 -0
  136. package/dist/cli/doctor.d.ts +18 -8
  137. package/dist/cli/doctor.js +114 -59
  138. package/dist/cli/first-run-path.d.ts +15 -13
  139. package/dist/cli/first-run-path.js +35 -33
  140. package/dist/cli/io.d.ts +44 -9
  141. package/dist/cli/io.js +98 -23
  142. package/dist/cli/observer-follow.d.ts +3 -3
  143. package/dist/cli/observer-follow.js +6 -7
  144. package/dist/cli/orientation.d.ts +10 -7
  145. package/dist/cli/orientation.js +89 -47
  146. package/dist/cli/product-sentence.d.ts +6 -0
  147. package/dist/cli/product-sentence.js +6 -0
  148. package/dist/cli/program.d.ts +6 -1
  149. package/dist/cli/program.js +111 -43
  150. package/dist/cli/telemetry.d.ts +16 -16
  151. package/dist/cli/telemetry.js +46 -44
  152. package/dist/cli.js +1 -1
  153. package/dist/comms/capture-surface.d.ts +13 -12
  154. package/dist/comms/capture-surface.js +23 -22
  155. package/dist/comms/catch-host.d.ts +7 -7
  156. package/dist/comms/catch-host.js +17 -17
  157. package/dist/comms/connections.d.ts +1 -1
  158. package/dist/comms/connections.js +2 -2
  159. package/dist/comms/email-catch.d.ts +3 -3
  160. package/dist/comms/email-catch.js +8 -8
  161. package/dist/comms/external-evidence.d.ts +5 -5
  162. package/dist/comms/external-evidence.js +5 -4
  163. package/dist/comms/extract.js +2 -2
  164. package/dist/comms/fake-inbox.d.ts +3 -3
  165. package/dist/comms/fake-inbox.js +3 -3
  166. package/dist/comms/receiving-render.d.ts +1 -1
  167. package/dist/comms/receiving-render.js +2 -2
  168. package/dist/comms/receiving-runtime.d.ts +4 -4
  169. package/dist/comms/sandbox-catch-script.d.ts +6 -6
  170. package/dist/comms/sandbox-catch-script.js +6 -6
  171. package/dist/comms/sandbox-catch.d.ts +38 -39
  172. package/dist/comms/sandbox-catch.js +36 -37
  173. package/dist/comms/setup.d.ts +2 -2
  174. package/dist/comms/setup.js +56 -25
  175. package/dist/comms/thread-evidence.d.ts +1 -1
  176. package/dist/comms/thread-evidence.js +6 -6
  177. package/dist/comms/types.d.ts +6 -6
  178. package/dist/comms/types.js +3 -3
  179. package/dist/evidence/frame-signature.d.ts +1 -1
  180. package/dist/evidence/frame-signature.js +4 -4
  181. package/dist/evidence/redaction.d.ts +3 -3
  182. package/dist/evidence/redaction.js +4 -4
  183. package/dist/feedback/draft.js +38 -80
  184. package/dist/feedback/export-bundle.js +1 -1
  185. package/dist/feedback/export.d.ts +5 -1
  186. package/dist/feedback/export.js +10 -10
  187. package/dist/feedback/feedback.d.ts +2 -2
  188. package/dist/feedback/feedback.js +21 -3
  189. package/dist/feedback/proof.d.ts +1 -1
  190. package/dist/feedback/proof.js +2 -2
  191. package/dist/guest/bootstrap.js +6 -6
  192. package/dist/guest/browser-tools.js +8 -8
  193. package/dist/guest/chromium-text.js +19 -19
  194. package/dist/guest/desktop-executor.d.ts +2 -2
  195. package/dist/guest/desktop-executor.js +20 -20
  196. package/dist/guest/desktop-keys.js +4 -4
  197. package/dist/guest/desktop-media.d.ts +2 -2
  198. package/dist/guest/desktop-media.js +16 -16
  199. package/dist/guest/desktop-native.d.ts +1 -1
  200. package/dist/guest/desktop-native.js +10 -10
  201. package/dist/guest/runtime-desktop.js +2 -2
  202. package/dist/guest/runtime-processes.d.ts +2 -2
  203. package/dist/guest/runtime-processes.js +2 -2
  204. package/dist/guest/runtime.js +6 -6
  205. package/dist/index.d.ts +14 -14
  206. package/dist/index.js +8 -8
  207. package/dist/keys/key-resolution.d.ts +5 -5
  208. package/dist/keys/key-resolution.js +23 -23
  209. package/dist/observer/artifact.d.ts +2 -2
  210. package/dist/observer/artifact.js +7 -7
  211. package/dist/observer/data.d.ts +6 -6
  212. package/dist/observer/data.js +5 -4
  213. package/dist/observer/exposure.d.ts +1 -1
  214. package/dist/observer/exposure.js +11 -11
  215. package/dist/observer/http.js +1 -1
  216. package/dist/observer/library.d.ts +3 -1
  217. package/dist/observer/library.js +8 -3
  218. package/dist/observer/listen.js +5 -4
  219. package/dist/observer/render.d.ts +1 -1
  220. package/dist/observer/render.js +17 -6
  221. package/dist/observer/run-routes.d.ts +4 -3
  222. package/dist/observer/run-routes.js +38 -13
  223. package/dist/observer/serve.d.ts +1 -1
  224. package/dist/observer/serve.js +7 -3
  225. package/dist/observer/tunnel.js +1 -1
  226. package/dist/observer-app.html +4 -4
  227. package/dist/routes/computer-use/bundle-parts.d.ts +9 -9
  228. package/dist/routes/computer-use/bundle-parts.js +18 -17
  229. package/dist/routes/computer-use/bundle.d.ts +2 -2
  230. package/dist/routes/computer-use/bundle.js +10 -9
  231. package/dist/routes/computer-use/e2b-desktop/comms.d.ts +6 -6
  232. package/dist/routes/computer-use/e2b-desktop/comms.js +18 -17
  233. package/dist/routes/computer-use/e2b-desktop/desktop.js +4 -4
  234. package/dist/routes/computer-use/e2b-desktop/fidelity.d.ts +5 -4
  235. package/dist/routes/computer-use/e2b-desktop/fidelity.js +11 -10
  236. package/dist/routes/computer-use/e2b-desktop/prepare.d.ts +1 -1
  237. package/dist/routes/computer-use/e2b-desktop/prepare.js +10 -10
  238. package/dist/routes/computer-use/e2b-desktop/start.js +7 -7
  239. package/dist/routes/computer-use/e2b-desktop/state.d.ts +2 -2
  240. package/dist/routes/computer-use/e2b-desktop/teardown.js +2 -2
  241. package/dist/routes/computer-use/external-comms.d.ts +4 -4
  242. package/dist/routes/computer-use/external-comms.js +1 -1
  243. package/dist/routes/computer-use/fanout-bundle.js +27 -26
  244. package/dist/routes/computer-use/fanout-records.js +12 -13
  245. package/dist/routes/computer-use/live-flush.d.ts +4 -4
  246. package/dist/routes/computer-use/live-flush.js +6 -6
  247. package/dist/routes/computer-use/live-phase.d.ts +5 -4
  248. package/dist/routes/computer-use/live-phase.js +8 -8
  249. package/dist/routes/computer-use/local-tree-pack.d.ts +5 -5
  250. package/dist/routes/computer-use/local-tree-pack.js +1 -1
  251. package/dist/routes/computer-use/local-vm.d.ts +8 -8
  252. package/dist/routes/computer-use/local-vm.js +3 -3
  253. package/dist/routes/computer-use/participant-desktop.d.ts +7 -7
  254. package/dist/routes/computer-use/participant-desktop.js +5 -5
  255. package/dist/routes/computer-use/participant-execution.d.ts +4 -4
  256. package/dist/routes/computer-use/participant-execution.js +10 -10
  257. package/dist/routes/computer-use/participant-model.d.ts +6 -0
  258. package/dist/routes/computer-use/participant-model.js +33 -15
  259. package/dist/routes/computer-use/participant-prompt.d.ts +15 -15
  260. package/dist/routes/computer-use/participant-prompt.js +9 -9
  261. package/dist/routes/computer-use/participant-runs.d.ts +16 -16
  262. package/dist/routes/computer-use/participant-runs.js +26 -25
  263. package/dist/routes/computer-use/plan.d.ts +12 -12
  264. package/dist/routes/computer-use/plan.js +42 -42
  265. package/dist/routes/computer-use/preflight.d.ts +5 -5
  266. package/dist/routes/computer-use/preflight.js +25 -25
  267. package/dist/routes/computer-use/rerun-selection.d.ts +1 -1
  268. package/dist/routes/computer-use/rerun-selection.js +4 -3
  269. package/dist/routes/computer-use/result.d.ts +4 -4
  270. package/dist/routes/computer-use/result.js +22 -21
  271. package/dist/routes/computer-use/route.d.ts +12 -11
  272. package/dist/routes/computer-use/route.js +31 -30
  273. package/dist/routes/computer-use/self-report.d.ts +7 -7
  274. package/dist/routes/computer-use/self-report.js +32 -32
  275. package/dist/routes/computer-use/setup.d.ts +23 -23
  276. package/dist/routes/computer-use/setup.js +28 -29
  277. package/dist/routes/computer-use/single-bundle.d.ts +11 -11
  278. package/dist/routes/computer-use/single-bundle.js +40 -41
  279. package/dist/routes/computer-use/subject-projection.d.ts +4 -4
  280. package/dist/routes/computer-use/subject-projection.js +6 -6
  281. package/dist/routes/computer-use/types.d.ts +71 -73
  282. package/dist/routes/computer-use/types.js +12 -12
  283. package/dist/routes/preview.d.ts +13 -7
  284. package/dist/routes/preview.js +11 -7
  285. package/dist/routes/scripted/bundle.d.ts +7 -7
  286. package/dist/routes/scripted/bundle.js +20 -18
  287. package/dist/routes/scripted/plan.d.ts +8 -8
  288. package/dist/routes/scripted/plan.js +13 -13
  289. package/dist/routes/scripted/result.d.ts +3 -3
  290. package/dist/routes/scripted/result.js +13 -13
  291. package/dist/routes/scripted/route.d.ts +9 -9
  292. package/dist/routes/scripted/route.js +15 -16
  293. package/dist/routes/scripted/scenario.d.ts +1 -1
  294. package/dist/routes/scripted/scenario.js +5 -5
  295. package/dist/routes/scripted/setup.d.ts +6 -6
  296. package/dist/routes/scripted/setup.js +8 -8
  297. package/dist/routes/scripted/subject.d.ts +5 -5
  298. package/dist/routes/scripted/subject.js +4 -4
  299. package/dist/routes/scripted/surface-records.d.ts +1 -1
  300. package/dist/routes/scripted/surface-records.js +4 -4
  301. package/dist/routes/scripted/surface-sessions.d.ts +3 -3
  302. package/dist/routes/scripted/types.d.ts +18 -20
  303. package/dist/routes/scripted/types.js +2 -2
  304. package/dist/routes/shared-world/bundle-records.js +11 -10
  305. package/dist/routes/shared-world/bundle.d.ts +5 -5
  306. package/dist/routes/shared-world/bundle.js +46 -43
  307. package/dist/routes/shared-world/checkpoints.d.ts +4 -4
  308. package/dist/routes/shared-world/checkpoints.js +2 -2
  309. package/dist/routes/shared-world/comms.d.ts +14 -14
  310. package/dist/routes/shared-world/comms.js +19 -18
  311. package/dist/routes/shared-world/external-public.d.ts +4 -3
  312. package/dist/routes/shared-world/external-public.js +16 -12
  313. package/dist/routes/shared-world/handoff.d.ts +7 -6
  314. package/dist/routes/shared-world/handoff.js +65 -57
  315. package/dist/routes/shared-world/lobby-code.d.ts +2 -2
  316. package/dist/routes/shared-world/lobby-code.js +9 -9
  317. package/dist/routes/shared-world/participant-specs.d.ts +10 -9
  318. package/dist/routes/shared-world/participant-specs.js +38 -31
  319. package/dist/routes/shared-world/plan.d.ts +9 -9
  320. package/dist/routes/shared-world/plan.js +16 -16
  321. package/dist/routes/shared-world/provenance.d.ts +2 -2
  322. package/dist/routes/shared-world/provenance.js +2 -2
  323. package/dist/routes/shared-world/provisioned.d.ts +8 -8
  324. package/dist/routes/shared-world/provisioned.js +18 -17
  325. package/dist/routes/shared-world/result.d.ts +6 -6
  326. package/dist/routes/shared-world/result.js +29 -29
  327. package/dist/routes/shared-world/route.d.ts +11 -11
  328. package/dist/routes/shared-world/route.js +40 -39
  329. package/dist/routes/shared-world/setup.d.ts +16 -16
  330. package/dist/routes/shared-world/setup.js +23 -23
  331. package/dist/routes/shared-world/types.d.ts +55 -53
  332. package/dist/routes/shared-world/types.js +4 -4
  333. package/dist/routes/terminal/adapter.d.ts +5 -5
  334. package/dist/routes/terminal/adapter.js +9 -9
  335. package/dist/routes/terminal/bundle.d.ts +13 -13
  336. package/dist/routes/terminal/bundle.js +28 -27
  337. package/dist/routes/terminal/credentials.d.ts +8 -8
  338. package/dist/routes/terminal/credentials.js +12 -12
  339. package/dist/routes/terminal/dry-run.d.ts +4 -4
  340. package/dist/routes/terminal/dry-run.js +12 -12
  341. package/dist/routes/terminal/encoding.js +2 -2
  342. package/dist/routes/terminal/ledger.d.ts +5 -5
  343. package/dist/routes/terminal/ledger.js +14 -14
  344. package/dist/routes/terminal/lifetime.d.ts +1 -1
  345. package/dist/routes/terminal/lifetime.js +1 -1
  346. package/dist/routes/terminal/live-finish.d.ts +2 -2
  347. package/dist/routes/terminal/live-finish.js +10 -10
  348. package/dist/routes/terminal/live-sandbox.d.ts +2 -2
  349. package/dist/routes/terminal/live-sandbox.js +15 -15
  350. package/dist/routes/terminal/participant-text.js +2 -1
  351. package/dist/routes/terminal/persona.d.ts +2 -2
  352. package/dist/routes/terminal/persona.js +3 -3
  353. package/dist/routes/terminal/plan.d.ts +7 -7
  354. package/dist/routes/terminal/plan.js +15 -15
  355. package/dist/routes/terminal/recorder.js +2 -2
  356. package/dist/routes/terminal/result.d.ts +5 -5
  357. package/dist/routes/terminal/result.js +14 -14
  358. package/dist/routes/terminal/route.d.ts +9 -9
  359. package/dist/routes/terminal/route.js +19 -20
  360. package/dist/routes/terminal/sandbox.d.ts +1 -1
  361. package/dist/routes/terminal/sandbox.js +1 -1
  362. package/dist/routes/terminal/session.d.ts +5 -5
  363. package/dist/routes/terminal/session.js +14 -13
  364. package/dist/routes/terminal/token-usage.d.ts +1 -1
  365. package/dist/routes/terminal/token-usage.js +7 -8
  366. package/dist/routes/terminal/trace.d.ts +4 -4
  367. package/dist/routes/terminal/trace.js +2 -2
  368. package/dist/routes/terminal/types.d.ts +48 -50
  369. package/dist/routes/terminal/types.js +5 -5
  370. package/dist/run/bundle-shape.js +3 -3
  371. package/dist/run/bundle.d.ts +49 -49
  372. package/dist/run/bundle.js +3 -2
  373. package/dist/run/cost-summary.js +3 -3
  374. package/dist/run/costs.d.ts +23 -0
  375. package/dist/run/costs.js +79 -44
  376. package/dist/run/detail.d.ts +6 -6
  377. package/dist/run/detail.js +4 -4
  378. package/dist/run/dry-run-inputs.d.ts +1 -1
  379. package/dist/run/dry-run-inputs.js +2 -2
  380. package/dist/run/dry-run.js +20 -20
  381. package/dist/run/judge.d.ts +19 -13
  382. package/dist/run/judge.js +18 -8
  383. package/dist/run/outcomes.d.ts +1 -1
  384. package/dist/run/participant.d.ts +1 -1
  385. package/dist/run/paths.d.ts +1 -1
  386. package/dist/run/paths.js +1 -1
  387. package/dist/run/preflight-receipts.js +1 -1
  388. package/dist/run/pricing.d.ts +11 -6
  389. package/dist/run/pricing.js +22 -15
  390. package/dist/run/projection.d.ts +46 -46
  391. package/dist/run/projection.js +57 -57
  392. package/dist/run/reclaim.d.ts +1 -1
  393. package/dist/run/reclaim.js +3 -3
  394. package/dist/run/results.d.ts +5 -5
  395. package/dist/run/run-cost.d.ts +53 -0
  396. package/dist/run/run-cost.js +105 -0
  397. package/dist/run/run-index-analysis.d.ts +15 -0
  398. package/dist/run/run-index-analysis.js +77 -0
  399. package/dist/run/run-index.d.ts +19 -7
  400. package/dist/run/run-index.js +77 -64
  401. package/dist/run/run-not-found.d.ts +5 -0
  402. package/dist/run/run-not-found.js +15 -0
  403. package/dist/run/run.d.ts +6 -5
  404. package/dist/run/run.js +3 -3
  405. package/dist/run/sandbox-receipts.d.ts +2 -2
  406. package/dist/run/sandbox-receipts.js +3 -3
  407. package/dist/run/shared-world-evidence.d.ts +37 -36
  408. package/dist/run/shared-world-evidence.js +4 -4
  409. package/dist/run/shared-world-shape.d.ts +1 -1
  410. package/dist/run/shared-world-shape.js +7 -7
  411. package/dist/run/stats.d.ts +14 -8
  412. package/dist/run/stats.js +121 -63
  413. package/dist/run/status.d.ts +16 -29
  414. package/dist/run/status.js +10 -29
  415. package/dist/run/stored-runs.js +2 -1
  416. package/dist/run/stream-shape.js +1 -1
  417. package/dist/run/streams.d.ts +7 -7
  418. package/dist/run/study-provenance.d.ts +49 -0
  419. package/dist/run/study-provenance.js +66 -0
  420. package/dist/run/study-result.d.ts +11 -0
  421. package/dist/run/study-result.js +9 -0
  422. package/dist/run/text.d.ts +2 -0
  423. package/dist/run/text.js +4 -0
  424. package/dist/run-study.d.ts +112 -0
  425. package/dist/{run-lab.js → run-study.js} +52 -47
  426. package/dist/{lab → study}/adapter-extension.d.ts +15 -27
  427. package/dist/{lab → study}/adapter-extension.js +10 -45
  428. package/dist/{lab → study}/adapter-scorer-loader.d.ts +17 -17
  429. package/dist/{lab → study}/adapter-scorer-loader.js +34 -34
  430. package/dist/study/composition-rules.d.ts +7 -0
  431. package/dist/{lab → study}/composition-rules.js +53 -53
  432. package/dist/study/config.d.ts +15 -0
  433. package/dist/{lab → study}/config.js +98 -41
  434. package/dist/study/convert.d.ts +33 -0
  435. package/dist/study/convert.js +432 -0
  436. package/dist/{lab → study}/device-presets.d.ts +10 -10
  437. package/dist/{lab → study}/device-presets.js +11 -11
  438. package/dist/study/discover.d.ts +75 -0
  439. package/dist/{lab → study}/discover.js +134 -95
  440. package/dist/{lab → study}/doctor.d.ts +19 -12
  441. package/dist/{lab → study}/doctor.js +58 -20
  442. package/dist/study/field-paths.d.ts +12 -0
  443. package/dist/study/field-paths.js +126 -0
  444. package/dist/study/files.d.ts +26 -0
  445. package/dist/study/files.js +44 -0
  446. package/dist/{lab → study}/init-templates.d.ts +2 -2
  447. package/dist/study/init-templates.js +421 -0
  448. package/dist/{lab → study}/init.d.ts +2 -2
  449. package/dist/{lab → study}/init.js +46 -11
  450. package/dist/study/keys.d.ts +4 -0
  451. package/dist/{lab → study}/keys.js +46 -12
  452. package/dist/study/migrate.d.ts +48 -0
  453. package/dist/study/migrate.js +408 -0
  454. package/dist/study/owned-files.d.ts +38 -0
  455. package/dist/study/owned-files.js +182 -0
  456. package/dist/{lab → study}/parse/actors.d.ts +5 -5
  457. package/dist/{lab → study}/parse/actors.js +14 -14
  458. package/dist/study/parse/comms.d.ts +12 -0
  459. package/dist/{lab → study}/parse/comms.js +5 -5
  460. package/dist/study/parse/execution.d.ts +19 -0
  461. package/dist/{lab → study}/parse/execution.js +13 -13
  462. package/dist/study/parse/study-v3.d.ts +21 -0
  463. package/dist/study/parse/study-v3.js +259 -0
  464. package/dist/study/parse/subject-state.d.ts +30 -0
  465. package/dist/{lab → study}/parse/subject-state.js +19 -19
  466. package/dist/{lab → study}/parse/subject.d.ts +3 -3
  467. package/dist/{lab → study}/parse/subject.js +34 -34
  468. package/dist/{lab → study}/parse/values.d.ts +3 -3
  469. package/dist/{lab → study}/parse/values.js +3 -3
  470. package/dist/{lab → study}/persona-resolve.d.ts +8 -8
  471. package/dist/{lab → study}/persona-resolve.js +6 -6
  472. package/dist/{lab → study}/plan-base.d.ts +7 -7
  473. package/dist/{lab → study}/plan-base.js +1 -1
  474. package/dist/{lab → study}/plan-participants.d.ts +12 -12
  475. package/dist/{lab → study}/plan-participants.js +4 -4
  476. package/dist/{lab → study}/plan-types.d.ts +31 -31
  477. package/dist/{lab → study}/plan-types.js +2 -2
  478. package/dist/study/plan.d.ts +13 -0
  479. package/dist/study/plan.js +101 -0
  480. package/dist/study/preflight-probes.d.ts +3 -0
  481. package/dist/{lab → study}/preflight-probes.js +20 -19
  482. package/dist/study/preflight-result.d.ts +8 -0
  483. package/dist/{lab → study}/preflight-result.js +5 -4
  484. package/dist/study/preflight.d.ts +94 -0
  485. package/dist/{lab → study}/preflight.js +12 -12
  486. package/dist/{lab → study}/requirements.d.ts +3 -3
  487. package/dist/{lab → study}/requirements.js +1 -1
  488. package/dist/study/route-inputs.d.ts +28 -0
  489. package/dist/{lab → study}/route-inputs.js +1 -1
  490. package/dist/{lab → study}/routing.d.ts +39 -33
  491. package/dist/{lab → study}/routing.js +60 -29
  492. package/dist/{lab/run-lab-events.d.ts → study/run-study-events.d.ts} +10 -13
  493. package/dist/{lab/run-lab-events.js → study/run-study-events.js} +3 -9
  494. package/dist/{lab/run-lab-homes.d.ts → study/run-study-homes.d.ts} +21 -18
  495. package/dist/{lab/run-lab-homes.js → study/run-study-homes.js} +1 -1
  496. package/dist/study/run-study-options.d.ts +28 -0
  497. package/dist/{lab/run-lab-options.js → study/run-study-options.js} +29 -25
  498. package/dist/{lab/lab-deps.d.ts → study/study-deps.d.ts} +8 -13
  499. package/dist/{lab/lab-deps.js → study/study-deps.js} +2 -2
  500. package/dist/{lab → study}/summary.d.ts +16 -15
  501. package/dist/{lab → study}/summary.js +21 -21
  502. package/dist/{lab → study}/tasks.d.ts +18 -18
  503. package/dist/{lab → study}/tasks.js +14 -14
  504. package/dist/{lab → study}/types.d.ts +202 -196
  505. package/dist/{lab → study}/types.js +3 -1
  506. package/dist/{lab → study}/validation.d.ts +27 -27
  507. package/dist/{lab → study}/validation.js +53 -51
  508. package/dist/study/warnings.d.ts +12 -0
  509. package/dist/{lab → study}/warnings.js +62 -34
  510. package/dist/subject/clone.d.ts +7 -7
  511. package/dist/subject/clone.js +2 -2
  512. package/dist/subject/desktop-cli.d.ts +1 -1
  513. package/dist/subject/desktop-cli.js +2 -2
  514. package/dist/subject/local-tree-archive.d.ts +2 -2
  515. package/dist/subject/local-tree-archive.js +4 -4
  516. package/dist/subject/local-tree.d.ts +4 -4
  517. package/dist/subject/node-bootstrap.js +1 -1
  518. package/dist/subject/runtime.js +9 -10
  519. package/dist/subject/serve.d.ts +5 -5
  520. package/dist/subject/serve.js +3 -3
  521. package/dist/subject/state.d.ts +4 -4
  522. package/dist/subject/state.js +1 -1
  523. package/dist/subject/steps.d.ts +6 -6
  524. package/dist/subject/steps.js +5 -5
  525. package/dist/substrates/command-failure.d.ts +3 -3
  526. package/dist/substrates/command-failure.js +3 -3
  527. package/dist/substrates/desktop-session.js +2 -2
  528. package/dist/substrates/detached.d.ts +6 -6
  529. package/dist/substrates/detached.js +7 -7
  530. package/dist/substrates/e2b/cdp-probe.d.ts +9 -9
  531. package/dist/substrates/e2b/cdp-probe.js +14 -17
  532. package/dist/substrates/e2b/desktop-browser.d.ts +6 -6
  533. package/dist/substrates/e2b/desktop-browser.js +9 -9
  534. package/dist/substrates/e2b/desktop-cdp.d.ts +10 -10
  535. package/dist/substrates/e2b/desktop-cdp.js +9 -9
  536. package/dist/substrates/e2b/desktop-executor.d.ts +2 -2
  537. package/dist/substrates/e2b/desktop-executor.js +13 -13
  538. package/dist/substrates/e2b/desktop-geometry.d.ts +4 -4
  539. package/dist/substrates/e2b/desktop-geometry.js +8 -8
  540. package/dist/substrates/e2b/desktop-media.d.ts +4 -4
  541. package/dist/substrates/e2b/desktop-media.js +1 -1
  542. package/dist/substrates/e2b/desktop-screenshot-cleanup.d.ts +3 -3
  543. package/dist/substrates/e2b/desktop-screenshot-cleanup.js +3 -3
  544. package/dist/substrates/e2b/lifetime.js +1 -1
  545. package/dist/substrates/e2b/sandbox.d.ts +4 -4
  546. package/dist/substrates/e2b/sandbox.js +2 -2
  547. package/dist/substrates/e2b/sdk.d.ts +12 -12
  548. package/dist/substrates/e2b/sdk.js +7 -7
  549. package/dist/substrates/local/runtime-config.d.ts +4 -4
  550. package/dist/substrates/local/runtime-config.js +4 -4
  551. package/dist/tui/actions.d.ts +1 -1
  552. package/dist/tui/actions.js +13 -13
  553. package/dist/tui/contract.d.ts +23 -23
  554. package/dist/tui/contract.js +11 -11
  555. package/dist/tui/launch.d.ts +7 -7
  556. package/dist/tui/launch.js +36 -31
  557. package/dist/tui/project.js +5 -5
  558. package/dist/tui-app.js +139 -139
  559. package/dist/verify/actor.d.ts +8 -8
  560. package/dist/verify/actor.js +36 -36
  561. package/dist/verify/artifacts.d.ts +2 -2
  562. package/dist/verify/artifacts.js +3 -3
  563. package/dist/verify/context-growth.d.ts +9 -0
  564. package/dist/verify/context-growth.js +62 -0
  565. package/dist/verify/costs.d.ts +6 -6
  566. package/dist/verify/costs.js +8 -8
  567. package/dist/verify/shared-world-concurrent.d.ts +2 -2
  568. package/dist/verify/shared-world-concurrent.js +79 -76
  569. package/dist/verify/shared-world.d.ts +2 -2
  570. package/dist/verify/shared-world.js +16 -16
  571. package/dist/verify/subject.d.ts +5 -5
  572. package/dist/verify/subject.js +19 -19
  573. package/dist/verify/verify.js +94 -44
  574. package/docs/README.md +25 -32
  575. package/docs/architecture/actor-contract.md +60 -91
  576. package/docs/architecture/browser-control.md +1 -1
  577. package/docs/architecture/desktop-sessions.md +7 -7
  578. package/docs/architecture/external-public-shared-world.md +68 -65
  579. package/docs/architecture/github-feedback-loop.md +2 -5
  580. package/docs/architecture/guest-desktop.md +2 -2
  581. package/docs/architecture/observer.md +34 -34
  582. package/docs/architecture/restricted-codex-analysis.md +34 -28
  583. package/docs/architecture/serve.md +34 -37
  584. package/docs/architecture/state-driven-executor.md +55 -58
  585. package/docs/architecture/task-protocol-support.md +6 -6
  586. package/docs/architecture/terminal-product-route.md +70 -110
  587. package/docs/contracts/README.md +24 -0
  588. package/docs/contracts/adapter-admission.md +3 -3
  589. package/docs/contracts/adapter-fixtures.md +4 -7
  590. package/docs/contracts/core.md +1 -3
  591. package/docs/contracts/errors.md +62 -0
  592. package/docs/contracts/feedback.md +8 -9
  593. package/docs/contracts/policy.md +7 -12
  594. package/docs/contracts/run-bundle.md +42 -44
  595. package/docs/contracts/schemas.md +446 -368
  596. package/docs/contracts/study-analysis.md +7 -4
  597. package/docs/contracts/study-costs.md +28 -9
  598. package/docs/decisions/0001-run-bundle-is-the-source-of-truth.md +1 -1
  599. package/docs/decisions/0002-observer-is-one-self-contained-file.md +2 -2
  600. package/docs/decisions/0003-contained-paths-and-cleanup-authority.md +3 -3
  601. package/docs/principles/actor-fidelity.md +4 -4
  602. package/docs/principles/engineering.md +3 -3
  603. package/docs/principles/invariants-and-defaults.md +38 -38
  604. package/docs/principles/self-driving-harness.md +1 -5
  605. package/docs/principles/three-roles.md +1 -1
  606. package/docs/product/automatic-analysis.md +7 -7
  607. package/examples/README.md +1 -1
  608. package/examples/participant/README.md +5 -5
  609. package/examples/participant/run.mjs +11 -10
  610. package/examples/scorer/README.md +3 -3
  611. package/examples/scorer/run.mjs +11 -12
  612. package/examples/scorer/scorer.mjs +3 -3
  613. package/package.json +16 -17
  614. package/skills/humanish/SKILL.md +64 -60
  615. package/skills/humanish/agents/openai.yaml +1 -1
  616. package/AGENTS.md +0 -99
  617. package/CONTRIBUTING.md +0 -148
  618. package/dist/actors/codex/qualified-versions.d.ts +0 -72
  619. package/dist/actors/codex/qualified-versions.js +0 -132
  620. package/dist/cli/commands/lab-format.d.ts +0 -8
  621. package/dist/cli/commands/lab-route-run.d.ts +0 -19
  622. package/dist/cli/commands/lab-route-scripted.d.ts +0 -17
  623. package/dist/cli/commands/lab-route-terminal.d.ts +0 -17
  624. package/dist/cli/commands/lab-run.d.ts +0 -9
  625. package/dist/cli/commands/lab.d.ts +0 -3
  626. package/dist/cli/commands/lab.js +0 -199
  627. package/dist/cli/renamed-options.d.ts +0 -11
  628. package/dist/cli/renamed-options.js +0 -18
  629. package/dist/lab/composition-rules.d.ts +0 -7
  630. package/dist/lab/config.d.ts +0 -7
  631. package/dist/lab/discover.d.ts +0 -65
  632. package/dist/lab/init-templates.js +0 -416
  633. package/dist/lab/keys.d.ts +0 -2
  634. package/dist/lab/parse/comms.d.ts +0 -12
  635. package/dist/lab/parse/execution.d.ts +0 -19
  636. package/dist/lab/parse/subject-state.d.ts +0 -30
  637. package/dist/lab/plan.d.ts +0 -19
  638. package/dist/lab/plan.js +0 -132
  639. package/dist/lab/preflight-probes.d.ts +0 -3
  640. package/dist/lab/preflight-result.d.ts +0 -7
  641. package/dist/lab/preflight.d.ts +0 -92
  642. package/dist/lab/route-inputs.d.ts +0 -28
  643. package/dist/lab/run-lab-options.d.ts +0 -28
  644. package/dist/lab/warnings.d.ts +0 -2
  645. package/dist/observer/static.d.ts +0 -39
  646. package/dist/observer/static.js +0 -369
  647. package/dist/run-lab.d.ts +0 -112
  648. package/docs/architecture/comms-inbox.md +0 -122
  649. package/docs/architecture/desktop-recording.md +0 -80
  650. package/docs/architecture/local-browser-runtime.md +0 -178
  651. package/docs/architecture/observer-review.md +0 -144
  652. package/docs/architecture/participant-media.md +0 -90
  653. package/docs/architecture/project-layout.md +0 -62
  654. package/docs/architecture/real-email-receiving.md +0 -136
  655. package/docs/assets/humanish-drawdb-hero.png +0 -0
  656. package/docs/assets/humanish-observer-hero.png +0 -0
  657. package/docs/goals/current.md +0 -207
  658. package/docs/product/lobby-trivia-3player-external-public.md +0 -77
  659. package/docs/product/open-source-install-experience.md +0 -240
  660. package/docs/ramp/README.md +0 -295
  661. package/docs/release/open-source-readiness.md +0 -222
  662. package/docs/release/public-readiness-standard.md +0 -210
  663. package/docs/roadmap/world-class-open-source-v0.md +0 -311
  664. /package/dist/{lab → study}/init-paths.d.ts +0 -0
  665. /package/dist/{lab → study}/init-paths.js +0 -0
  666. /package/dist/{lab → study}/init-plan.d.ts +0 -0
  667. /package/dist/{lab → study}/init-plan.js +0 -0
  668. /package/dist/{lab → study}/participant-assignment.d.ts +0 -0
  669. /package/dist/{lab → study}/participant-assignment.js +0 -0
  670. /package/dist/{lab → study}/persona.d.ts +0 -0
  671. /package/dist/{lab → study}/persona.js +0 -0
@@ -13,17 +13,18 @@ export interface InboxRenderOptions {
13
13
  export declare function inboxRecipientScope(address: string): string;
14
14
  export declare function recipientInboxUrl(inboxUrl: string, address: string): string;
15
15
  /** Rewrite a URL's origin per the map (origins are exact strings the harness minted). Leaves
16
- * non-matching URLs — including sibling origins sharing a prefix — untouched. */
16
+ * non-matching URLs (including sibling origins sharing a prefix) untouched. */
17
17
  export declare function rewriteOrigin(url: string, originMap?: OriginMap): string;
18
18
  /**
19
- * Build the [internalOrigin → reachableOrigin] rewrite rows for a run. The app-under-test bakes ITS OWN
19
+ * Build the [internalOrigin → reachableOrigin] rewrite rows for a run. The app-under-test bakes its own
20
20
  * origin into the verify links it emails (usually its loopback serve origin); the persona reaches the
21
- * app at a possibly-different origin — the same loopback on the CUA route (identity, a no-op), the
22
- * harness-minted getHost URL on the shared-world route (where the rewrite is REQUIRED). Emits the serve
23
- * origin plus its loopback aliases at the serve port (127.0.0.1 / localhost / 0.0.0.0) so an app that
24
- * stamps `localhost` still rewrites, and — because the harness cannot infer an absolute PUBLIC base URL
25
- * an app was configured with — an operator-declared `linkOrigin` escape hatch, matched first. Origins
26
- * only (no trailing slash, no path), so replaceOriginBoundary matches on a URL boundary.
21
+ * app at a possibly-different origin: the same loopback on the computer-use route (identity, a
22
+ * no-op), the harness-minted getHost URL on the shared-world route (where the rewrite is required).
23
+ * Emits the serve origin plus its loopback aliases at the serve port (127.0.0.1 / localhost /
24
+ * 0.0.0.0) so an app that stamps `localhost` still rewrites, and (because the harness cannot infer
25
+ * an absolute public base URL an app was configured with) an operator-declared `linkOrigin` escape
26
+ * hatch, matched first. Origins only (no trailing slash, no path), so replaceOriginBoundary matches
27
+ * on a URL boundary.
27
28
  */
28
29
  export declare function buildOriginMap(args: {
29
30
  internalServeUrl?: string | undefined;
@@ -32,11 +33,11 @@ export declare function buildOriginMap(args: {
32
33
  }): OriginMap;
33
34
  /** Best-guess primary call-to-action: the first verify/confirm-looking link, else the first link. */
34
35
  export declare function pickVerifyUrl(links: string[]): string | undefined;
35
- /** The inbox LIST page (semantic table; each row links to its message). */
36
+ /** The inbox list page (semantic table; each row links to its message). */
36
37
  export declare function renderInboxList(messages: CommsMessage[], options?: InboxRenderOptions): string;
37
- /** One message, DEFAULT view: the app's real captured email in a minimal high-contrast shell. */
38
+ /** One message, default view: the app's real captured email in a minimal high-contrast shell. */
38
39
  export declare function renderInboxMessage(message: CommsMessage, options?: InboxRenderOptions): string;
39
- /** One message, SYNTHESIZED view: a guaranteed-legible reading pane (big verify button + big OTP). The
40
+ /** One message, synthesized view: a guaranteed-legible reading pane (big verify button + big OTP). The
40
41
  * reliability fallback when the app's real email is a vision minefield; opt-in, never the default. */
41
42
  export declare function renderInboxMessageSynth(message: CommsMessage, options?: InboxRenderOptions): string;
42
43
  /** JSON projection of one message (the programmatic/CLI actor surface + the reliability backstop). Raw
@@ -46,7 +47,7 @@ export declare function inboxMessageJson(message: CommsMessage, options?: InboxR
46
47
  * Build every served file for the inbox surface from the normalized messages. The host writes each to
47
48
  * `<servedDir>/<path>`; the in-sandbox catch serves `<servedDir>/<pathname>`, falling back to
48
49
  * `<pathname>/index` (so a URL like `/inbox` maps to the `inbox/index` file while `/inbox/{id}` stays a
49
- * directory) — standard web-server index semantics that avoid a file-vs-directory path collision. The
50
+ * directory), standard web-server index semantics that avoid a file-vs-directory path collision. The
50
51
  * URLs the persona actually sees stay clean (`/inbox`, `/inbox/{id}`, `/inbox/{id}/synth`). Content type
51
52
  * is inferred from the `/api/` prefix. Message ids are `comms-NNNN` (never `index`/`latest`), so leaf
52
53
  * filenames never collide with an id.
@@ -1,4 +1,4 @@
1
- // The inbox surface for captured mail (#297): the pages a computer-use participant opens to read a
1
+ // The inbox surface for captured mail: the pages a computer-use participant opens to read a
2
2
  // captured verification email and follow its link. The pages are minimal semantic HTML (native
3
3
  // links and tables, black on white, large targets), because vision participants read a simple
4
4
  // inbox more reliably than a webmail client. The default view is the app's real captured email,
@@ -27,7 +27,7 @@ function inboxPath(options) {
27
27
  : `/inbox/for/${inboxRecipientScope(options.recipient)}`;
28
28
  }
29
29
  const VERIFY_HINT = /verify|confirm|activate|validate|magic|token|account|sign[\s-]?up/i;
30
- /** Replace `from` with `to` everywhere it ends on an ORIGIN BOUNDARY — end-of-string or one of the URL
30
+ /** Replace `from` with `to` everywhere it ends on an origin boundary: end-of-string or one of the URL
31
31
  * delimiters `/ ? # " ' <space> > \`. A bare prefix replace would mangle a sibling origin that shares
32
32
  * `from` as a numeric prefix (`:30000` vs `:3000`) or a suffix-domain (`:3000.evil`); the boundary
33
33
  * check prevents that. Works both for a single URL and inside a blob of email HTML. */
@@ -47,7 +47,7 @@ function replaceOriginBoundary(text, from, to) {
47
47
  }
48
48
  }
49
49
  /** Rewrite a URL's origin per the map (origins are exact strings the harness minted). Leaves
50
- * non-matching URLs — including sibling origins sharing a prefix — untouched. */
50
+ * non-matching URLs (including sibling origins sharing a prefix) untouched. */
51
51
  export function rewriteOrigin(url, originMap = []) {
52
52
  let out = url;
53
53
  for (const [from, to] of originMap)
@@ -75,14 +75,15 @@ function safePort(url) {
75
75
  }
76
76
  }
77
77
  /**
78
- * Build the [internalOrigin → reachableOrigin] rewrite rows for a run. The app-under-test bakes ITS OWN
78
+ * Build the [internalOrigin → reachableOrigin] rewrite rows for a run. The app-under-test bakes its own
79
79
  * origin into the verify links it emails (usually its loopback serve origin); the persona reaches the
80
- * app at a possibly-different origin — the same loopback on the CUA route (identity, a no-op), the
81
- * harness-minted getHost URL on the shared-world route (where the rewrite is REQUIRED). Emits the serve
82
- * origin plus its loopback aliases at the serve port (127.0.0.1 / localhost / 0.0.0.0) so an app that
83
- * stamps `localhost` still rewrites, and — because the harness cannot infer an absolute PUBLIC base URL
84
- * an app was configured with — an operator-declared `linkOrigin` escape hatch, matched first. Origins
85
- * only (no trailing slash, no path), so replaceOriginBoundary matches on a URL boundary.
80
+ * app at a possibly-different origin: the same loopback on the computer-use route (identity, a
81
+ * no-op), the harness-minted getHost URL on the shared-world route (where the rewrite is required).
82
+ * Emits the serve origin plus its loopback aliases at the serve port (127.0.0.1 / localhost /
83
+ * 0.0.0.0) so an app that stamps `localhost` still rewrites, and (because the harness cannot infer
84
+ * an absolute public base URL an app was configured with) an operator-declared `linkOrigin` escape
85
+ * hatch, matched first. Origins only (no trailing slash, no path), so replaceOriginBoundary matches
86
+ * on a URL boundary.
86
87
  */
87
88
  export function buildOriginMap(args) {
88
89
  const to = safeOrigin(args.reachableBaseUrl);
@@ -121,7 +122,7 @@ function esc(value) {
121
122
  .replace(/>/g, "&gt;")
122
123
  .replace(/"/g, "&quot;");
123
124
  }
124
- /** Defense-in-depth stripping before rendering the app's real email HTML. The LOAD-BEARING protection is
125
+ /** Defense-in-depth stripping before rendering the app's real email HTML. The main protection is
125
126
  * the page CSP (`script-src 'none'`, set both as a page() meta and as a catch response header), which is
126
127
  * browser-enforced and neuters inline handlers, `javascript:` URLs, and injected <script> regardless of
127
128
  * what this misses. This strip additionally removes elements CSP does not cover (a redirecting
@@ -133,9 +134,9 @@ function neutralizeEmailHtml(html, originMap) {
133
134
  .replace(/<(script|style|iframe|object|embed|svg|math|template)\b[\s\S]*?<\/\1\s*>/gi, "")
134
135
  // stray or void dangerous tags, incl. unclosed openers and redirect/reroot tags
135
136
  .replace(/<\/?(script|style|iframe|object|embed|svg|math|base|meta|link|form)\b[^>]*>/gi, "")
136
- // inline event handlers, whether preceded by whitespace OR a "/" attribute separator
137
+ // inline event handlers, whether preceded by whitespace or a "/" attribute separator
137
138
  .replace(/[\s/]on[a-z]+\s*=\s*("[^"]*"|'[^']*'|[^\s>]+)/gi, " ")
138
- // javascript:/vbscript: in nav/resource attrs — quoted OR unquoted (data: is left for inline images)
139
+ // javascript:/vbscript: in nav/resource attrs, quoted or unquoted (data: is left for inline images)
139
140
  .replace(/(href|src|xlink:href|formaction|action)\s*=\s*(?:"(?:javascript|vbscript):[^"]*"|'(?:javascript|vbscript):[^']*'|(?:javascript|vbscript):[^\s>]*)/gi, '$1="#"');
140
141
  // Rewrite the app's origin inside the (now-neutralized) HTML so its own links resolve to a reachable host.
141
142
  for (const [from, to] of originMap)
@@ -213,7 +214,7 @@ function renderEmailImages(html, message, originMap) {
213
214
  }
214
215
  }
215
216
  if (!src)
216
- return `<span class="email-image-unavailable" role="img" aria-label="${esc(alt || "Email image")}">${esc(alt ? `${alt} — ${unavailable}` : unavailable)}</span>`;
217
+ return `<span class="email-image-unavailable" role="img" aria-label="${esc(alt || "Email image")}">${esc(alt ? `${alt}: ${unavailable}` : unavailable)}</span>`;
217
218
  const sizing = ["width", "height"]
218
219
  .map((name) => {
219
220
  const value = attributes.get(name);
@@ -241,11 +242,11 @@ const PAGE_CSS = "body{font:16px/1.55 system-ui,-apple-system,Segoe UI,Roboto,sa
241
242
  "@media(max-width:560px){.inbox-list thead{display:none}.inbox-list,.inbox-list tbody,.inbox-list tr,.inbox-list td{display:block}" +
242
243
  ".inbox-list tr{padding:12px 0;border-bottom:1px solid #ddd}.inbox-list td{padding:3px 0;border:0}" +
243
244
  ".inbox-list td:first-child a{display:block;min-height:32px;font-weight:600}.inbox-list td[data-label]::before{content:attr(data-label) ': ';font-size:13px;color:#666}}";
244
- /** The surface CSP — the browser-enforced, load-bearing protection against the app-authored email running
245
+ /** The surface CSP: the browser-enforced main protection against the app-authored email running
245
246
  * script or hijacking navigation on the surface page. `script-src 'none'` blocks inline handlers,
246
247
  * `javascript:` URLs, and any injected <script>; `object-src`/`frame-src 'none'` block plugins/frames;
247
248
  * `base-uri 'none'` blocks <base> reroot. Images/styles stay permissive so the real email still renders.
248
- * Set BOTH as a page() meta (covers direct render use) and as a catch response header (covers serving). */
249
+ * Set both as a page() meta (covers direct render use) and as a catch response header (covers serving). */
249
250
  const INBOX_SURFACE_CSP = "default-src 'self'; script-src 'none'; object-src 'none'; base-uri 'none'; frame-src 'none'; img-src * data:; style-src 'unsafe-inline'; font-src * data:";
250
251
  function page(title, inner) {
251
252
  return ('<!doctype html><html lang="en"><head><meta charset="utf-8">' +
@@ -273,7 +274,7 @@ function fmtTime(ms) {
273
274
  function toValues(message) {
274
275
  return message.to.map((address) => address.value);
275
276
  }
276
- /** The inbox LIST page (semantic table; each row links to its message). */
277
+ /** The inbox list page (semantic table; each row links to its message). */
277
278
  export function renderInboxList(messages, options = {}) {
278
279
  const base = inboxPath(options);
279
280
  const rows = messages
@@ -295,8 +296,8 @@ export function renderInboxList(messages, options = {}) {
295
296
  "</td></tr>")
296
297
  .join("");
297
298
  const body = (options.recipient
298
- ? '<div class="bar">Inbox for ' + esc(options.recipient) + " — "
299
- : '<div class="bar">Shared operator inbox — all captured recipients — ') +
299
+ ? '<div class="bar">Inbox for ' + esc(options.recipient) + ": "
300
+ : '<div class="bar">Shared operator inbox (all captured recipients): ') +
300
301
  messages.length +
301
302
  " message" +
302
303
  (messages.length === 1 ? "" : "s") +
@@ -307,7 +308,7 @@ export function renderInboxList(messages, options = {}) {
307
308
  "</tbody></table></main>";
308
309
  return page("Inbox", body);
309
310
  }
310
- /** One message, DEFAULT view: the app's real captured email in a minimal high-contrast shell. */
311
+ /** One message, default view: the app's real captured email in a minimal high-contrast shell. */
311
312
  export function renderInboxMessage(message, options = {}) {
312
313
  const originMap = options.originMap ?? [];
313
314
  const base = inboxPath(options);
@@ -336,7 +337,7 @@ export function renderInboxMessage(message, options = {}) {
336
337
  "</main>";
337
338
  return page(message.subject ?? "Message", body);
338
339
  }
339
- /** One message, SYNTHESIZED view: a guaranteed-legible reading pane (big verify button + big OTP). The
340
+ /** One message, synthesized view: a guaranteed-legible reading pane (big verify button + big OTP). The
340
341
  * reliability fallback when the app's real email is a vision minefield; opt-in, never the default. */
341
342
  export function renderInboxMessageSynth(message, options = {}) {
342
343
  const originMap = options.originMap ?? [];
@@ -418,7 +419,7 @@ function inboxListJson(messages, options = {}) {
418
419
  * Build every served file for the inbox surface from the normalized messages. The host writes each to
419
420
  * `<servedDir>/<path>`; the in-sandbox catch serves `<servedDir>/<pathname>`, falling back to
420
421
  * `<pathname>/index` (so a URL like `/inbox` maps to the `inbox/index` file while `/inbox/{id}` stays a
421
- * directory) — standard web-server index semantics that avoid a file-vs-directory path collision. The
422
+ * directory), standard web-server index semantics that avoid a file-vs-directory path collision. The
422
423
  * URLs the persona actually sees stay clean (`/inbox`, `/inbox/{id}`, `/inbox/{id}/synth`). Content type
423
424
  * is inferred from the `/api/` prefix. Message ids are `comms-NNNN` (never `index`/`latest`), so leaf
424
425
  * filenames never collide with an id.
@@ -9,14 +9,14 @@ export interface CommsCatchHostOptions {
9
9
  dir: string;
10
10
  /** Bearer token required on GET /deliveries. Strongly recommended when the host is reachable. */
11
11
  token?: string;
12
- /** SECOND port for the READ-ONLY inbox listener bound to 0.0.0.0, so a persona on another machine
12
+ /** Second port for the read-only inbox listener bound to 0.0.0.0, so a persona on another machine
13
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;
17
- /** Restrict the rendered inbox to these addresses. Omit to render whatever the app actually mailed
18
- * — a standalone catch has no lab roster to read recipients from, and an operator who forgets to
19
- * name one should not get a healthy catch that renders an empty inbox forever (#380). */
17
+ /** Restrict the rendered inbox to these addresses. Omit to render whatever the app actually mailed;
18
+ * a standalone catch has no study roster to read recipients from, and an operator who forgets to
19
+ * name one should not get a healthy catch that renders an empty inbox forever. */
20
20
  recipients?: string[];
21
21
  /** Inbox re-render cadence in ms (test seam). */
22
22
  renderIntervalMs?: number;
@@ -24,10 +24,10 @@ export interface CommsCatchHostOptions {
24
24
  /**
25
25
  * Render the persona-facing inbox surface from the catch's own deliveries file into `surfaceDir`.
26
26
  *
27
- * This is the half that was missing on an adopter-hosted plane (#380). The catch serves /inbox and
28
- * /api/inbox as STATIC FILES; in-sandbox, humanish-as-host renders those files on a cadence. On a
27
+ * This is the half that was missing on an adopter-hosted plane. The catch serves /inbox and
28
+ * /api/inbox as static files; in-sandbox, humanish-as-host renders those files on a cadence. On a
29
29
  * plane humanish does not provision there is no such host, so nothing wrote them and every persona
30
- * that reached /inbox got `message not found` against a catch whose /health was green — the funnel
30
+ * that reached /inbox got `message not found` against a catch whose /health was green, so the funnel
31
31
  * dead-ended at a technically-healthy service.
32
32
  *
33
33
  * Rebuilding from scratch each pass is deliberate and matches refreshInboxSurface: a fresh channel per
@@ -1,19 +1,19 @@
1
- // `humanish comms catch` (#328): run the email catch on the OPERATOR's own host.
1
+ // `humanish comms catch`: run the email catch on the operator's own host.
2
2
  //
3
- // Why this exists. The in-sandbox catch only works when humanish provisions the subject itself —
3
+ // Why this exists. The in-sandbox catch only works when humanish provisions the subject itself:
4
4
  // it clones the app into a sandbox, injects the email-API base URL at boot, and hosts the catch
5
- // alongside it. An adopter whose study runs against their OWN deployed environment (an app-url or
5
+ // alongside it. An adopter whose study runs against their own deployed environment (an app-url or
6
6
  // operator-provisioned plane) hands humanish a URL instead, so humanish never boots the app and
7
7
  // has nowhere to put a catch. Before this, a `comms:` block on that route was warned inert and the
8
8
  // personas simply stalled at the verification screen.
9
9
  //
10
10
  // The fix is to let the adopter host the same catch and declare it (`comms.email.external`).
11
- // Shipping it as a COMMAND rather than a spec matters: the adopter runs the identical
11
+ // Shipping it as a command rather than a spec matters: the adopter runs the identical
12
12
  // implementation humanish deploys in-sandbox, so the capture shape, the inbox surface, and the
13
13
  // drain contract cannot drift between the two planes. Point the app's email-API base URL at this
14
- // server and point the lab's `catchBaseUrl` at it too.
14
+ // server and point the study's `catchBaseUrl` at it too.
15
15
  //
16
- // Runtime: the catch is a python3 stdlib server with no dependencies (that was the 0.29.0 lesson —
16
+ // Runtime: the catch is a python3 stdlib server with no dependencies (that was the 0.29.0 lesson:
17
17
  // a co-located catcher must use a runtime the environment guarantees). This command writes the
18
18
  // same script and runs it in the foreground until interrupted.
19
19
  import { spawn } from "node:child_process";
@@ -27,10 +27,10 @@ const DEFAULT_RENDER_INTERVAL_MS = 3_000;
27
27
  /**
28
28
  * Render the persona-facing inbox surface from the catch's own deliveries file into `surfaceDir`.
29
29
  *
30
- * This is the half that was missing on an adopter-hosted plane (#380). The catch serves /inbox and
31
- * /api/inbox as STATIC FILES; in-sandbox, humanish-as-host renders those files on a cadence. On a
30
+ * This is the half that was missing on an adopter-hosted plane. The catch serves /inbox and
31
+ * /api/inbox as static files; in-sandbox, humanish-as-host renders those files on a cadence. On a
32
32
  * plane humanish does not provision there is no such host, so nothing wrote them and every persona
33
- * that reached /inbox got `message not found` against a catch whose /health was green — the funnel
33
+ * that reached /inbox got `message not found` against a catch whose /health was green, so the funnel
34
34
  * dead-ended at a technically-healthy service.
35
35
  *
36
36
  * Rebuilding from scratch each pass is deliberate and matches refreshInboxSurface: a fresh channel per
@@ -43,7 +43,7 @@ export async function renderInboxSurfaceLocally(args) {
43
43
  text = await readFile(args.deliveriesPath, "utf8");
44
44
  }
45
45
  catch {
46
- text = ""; // no mail captured yet — still render, so /inbox answers "No messages yet."
46
+ text = ""; // no mail captured yet; still render, so /inbox answers "No messages yet."
47
47
  }
48
48
  const sends = parseDeliveriesNdjson(text);
49
49
  const addresses = args.recipients && args.recipients.length > 0
@@ -55,7 +55,7 @@ export async function renderInboxSurfaceLocally(args) {
55
55
  }));
56
56
  const messages = await inboxMessagesFrom(sends, recipients);
57
57
  const files = buildInboxSurface(messages, { recipients: addresses });
58
- // A reused catch directory can change recipients or truncate its delivery log. Remove ONLY files
58
+ // A reused catch directory can change recipients or truncate its delivery log. Remove only files
59
59
  // recorded as generated routes, so old recipient/message URLs cannot survive that change.
60
60
  const manifestPath = path.join(args.surfaceDir, ".inbox-files.json");
61
61
  let previous = [];
@@ -105,8 +105,8 @@ export async function runCommsCatchHost(options, io) {
105
105
  const surfaceDir = path.join(dir, "surface");
106
106
  await mkdir(surfaceDir, { recursive: true });
107
107
  await writeFile(scriptPath, SANDBOX_CATCH_SCRIPT, "utf8");
108
- // Render the EMPTY inbox before the server is announced, so /inbox resolves to the "No messages
109
- // yet." page from the first request instead of a bare `message not found` (#380) — a persona reads
108
+ // Render the empty inbox before the server is announced, so /inbox resolves to the "No messages
109
+ // yet." page from the first request instead of a bare `message not found`, because a persona reads
110
110
  // that 404 as a broken product and reports a blocker that is really just an empty mailbox.
111
111
  await renderInboxSurfaceLocally({
112
112
  deliveriesPath,
@@ -131,15 +131,15 @@ export async function runCommsCatchHost(options, io) {
131
131
  ...(inboxPort === undefined
132
132
  ? []
133
133
  : [
134
- ` read-only inbox listener on http://0.0.0.0:${inboxPort} (GET only; expose THIS to personas)`,
134
+ ` read-only inbox listener on http://0.0.0.0:${inboxPort} (GET only; this is the address to give personas)`,
135
135
  ]),
136
136
  ` POST /emails <- point your app's email-API base URL here`,
137
137
  ` GET /inbox <- shared operator inbox${inboxPort === undefined ? " (loopback only without --inbox-port)" : ""}`,
138
- ` GET /inbox/for/... <- assigned participant inbox (linked by the lab)`,
138
+ ` GET /inbox/for/... <- assigned participant inbox (linked by the study)`,
139
139
  ` GET /deliveries <- humanish drains this${options.token ? " (bearer token required)" : ""}`,
140
140
  ` GET /health <- readiness marker humanish probes before a run`,
141
141
  ``,
142
- `Declare it in the lab:`,
142
+ `Declare it in the study:`,
143
143
  ` comms:`,
144
144
  ` email:`,
145
145
  ` external:`,
@@ -149,7 +149,7 @@ export async function runCommsCatchHost(options, io) {
149
149
  ? [` authTokenEnv: HUMANISH_COMMS_TOKEN # value read at runtime, never persisted`]
150
150
  : []),
151
151
  ``,
152
- `Captured mail is written to ${deliveriesPath}. Raw bodies stay on THIS host: the run bundle`,
152
+ `Captured mail is written to ${deliveriesPath}. Raw bodies stay on this host: the run bundle`,
153
153
  `only ever receives digests (from/to/subject/link) and an OTP count.`,
154
154
  ``,
155
155
  ].join("\n") + "\n");
@@ -16,5 +16,5 @@ export interface CommsSetupResult {
16
16
  }
17
17
  /** Offline and non-mutating. Unsafe storage is an error, never an empty configuration. */
18
18
  export declare function readCommsConnections(cwd: string): Promise<CommsConnections>;
19
- /** Add one exact profile. No lab mutation, network, provider resources or credential values. */
19
+ /** Add one exact profile. No study mutation, network, provider resources or credential values. */
20
20
  export declare function saveCommsConnection(cwd: string, name?: string, apiKeyEnv?: string): Promise<CommsSetupResult>;
@@ -65,7 +65,7 @@ export async function readCommsConnections(cwd) {
65
65
  throw new Error("Invalid connection configuration.");
66
66
  }
67
67
  }
68
- /** Add one exact profile. No lab mutation, network, provider resources or credential values. */
68
+ /** Add one exact profile. No study mutation, network, provider resources or credential values. */
69
69
  export async function saveCommsConnection(cwd, name = DEFAULT_COMMS_PROVIDER.id, apiKeyEnv = DEFAULT_COMMS_PROVIDER.keyEnv) {
70
70
  const provider = DEFAULT_COMMS_PROVIDER.id;
71
71
  try {
@@ -94,7 +94,7 @@ export async function saveCommsConnection(cwd, name = DEFAULT_COMMS_PROVIDER.id,
94
94
  await writeContainedOutputFile(root, "comms.yaml", stringify(config), "utf8");
95
95
  return {
96
96
  ok: true,
97
- message: "Connection saved for this project. Select it in a supported lab to use fresh real inboxes.",
97
+ message: "Connection saved for this project. Select it in a supported study to use fresh real inboxes.",
98
98
  };
99
99
  }
100
100
  finally {
@@ -10,10 +10,10 @@ interface NormalizedSend {
10
10
  }
11
11
  /**
12
12
  * A provider wire-shape adapter. Vendor-neutral seam: the server owns the HTTP + routing + bus
13
- * delivery; a profile only knows how ONE provider (or a custom app) shapes its send request.
13
+ * delivery; a profile only knows how one provider (or a custom app) shapes its send request.
14
14
  */
15
15
  export interface EmailSendProfile {
16
- /** Descriptive name (e.g. "generic", "sendgrid"). Not a dependency — just a label. */
16
+ /** Descriptive name (e.g. "generic", "sendgrid"). Not a dependency, just a label. */
17
17
  name: string;
18
18
  /** Request paths (method POST) this profile accepts a send on. */
19
19
  sendPaths: string[];
@@ -31,7 +31,7 @@ export interface EmailCatchServer {
31
31
  close(): Promise<void>;
32
32
  }
33
33
  export interface EmailCatchOptions {
34
- /** Bind host. Default 127.0.0.1 (loopback ONLY). */
34
+ /** Bind host. Default 127.0.0.1 (loopback only). */
35
35
  host?: string;
36
36
  /** Port. Default 0 (ephemeral). */
37
37
  port?: number;
@@ -1,9 +1,9 @@
1
- // A VENDOR-NEUTRAL loopback catch server for email-send APIs (#297). An app hardwired to a hosted
2
- // email provider is redirected into the fake bus with ONE env var and no code change: point the app's
3
- // API base URL at this server. The catch does not depend on, or name itself after, any one vendor —
1
+ // A vendor-neutral loopback catch server for email-send APIs. An app hardwired to a hosted
2
+ // email provider is redirected into the fake bus with one env var and no code change: point the app's
3
+ // API base URL at this server. The catch does not depend on, or name itself after, any one vendor:
4
4
  // it normalizes each provider's distinct wire shape (Resend's flat body, SendGrid's nested
5
5
  // personalizations, Postmark's TitleCase, a custom app's own JSON) to one shape via pluggable
6
- // PROFILES, and routes the result into a CommsChannel. Resend-compatible and SendGrid-compatible out
6
+ // profiles, and routes the result into a CommsChannel. Resend-compatible and SendGrid-compatible out
7
7
  // of the box; extend with a custom profile for anything else. (There is no standardized email-send
8
8
  // request shape across vendors, so normalization is by design, not a shortcut.)
9
9
  //
@@ -34,8 +34,8 @@ function toAddresses(value) {
34
34
  return [];
35
35
  }
36
36
  // ---------------------------------------------------------------- built-in profiles
37
- /** The flat-JSON shape: `{ from, to, subject, html, text }`. Matches Resend (POST /emails) AND a
38
- * custom app that sends the common shape AND Postmark's TitleCase keys (From/To/HtmlBody/…). */
37
+ /** The flat-JSON shape: `{ from, to, subject, html, text }`. Matches Resend (POST /emails) and a
38
+ * custom app that sends the common shape and Postmark's TitleCase keys (From/To/HtmlBody/…). */
39
39
  const genericEmailProfile = {
40
40
  name: "generic",
41
41
  sendPaths: ["/emails", "/emails/batch", "/email", "/email/batch", "/send"],
@@ -125,7 +125,7 @@ function readBody(req, limit) {
125
125
  req.on("data", (chunk) => {
126
126
  size += chunk.length;
127
127
  if (size > limit) {
128
- // Reject WITHOUT destroying the socket here: the handler responds 413 cleanly first (a
128
+ // Reject without destroying the socket here: the handler responds 413 cleanly first (a
129
129
  // write-after-destroy would otherwise reset the connection), then tears the request down.
130
130
  reject(new BodyTooLargeError("request body too large"));
131
131
  return;
@@ -193,7 +193,7 @@ export async function startEmailCatchServer(channel, options = {}) {
193
193
  const sends = profile.parse(path, parsed);
194
194
  if (sends.length === 0) {
195
195
  // A well-formed request that names no deliverable recipient (empty body, or a shape the profile
196
- // could not resolve to a send) — return a bad-request rather than a fabricated success id.
196
+ // could not resolve to a send): return a bad-request rather than a fabricated success id.
197
197
  respondJson(res, 422, { error: "no deliverable message in request" });
198
198
  return;
199
199
  }
@@ -1,4 +1,4 @@
1
- import type { LabCommsEmail, LabCommsExternal } from "../lab/types.js";
1
+ import type { StudyCommsEmail, StudyCommsExternal } from "../study/types.js";
2
2
  import type { PreparedRunArtifactPaths } from "../run/paths.js";
3
3
  /**
4
4
  * Why a catch token cannot be used, or undefined when it can. An unset or empty token is none. A
@@ -6,8 +6,8 @@ import type { PreparedRunArtifactPaths } from "../run/paths.js";
6
6
  * refused with the short ones.
7
7
  */
8
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;
9
+ /** The catch token the run sends: the value of `authTokenEnv`, when the study names one. */
10
+ export declare function catchTokenOf(external: StudyCommsExternal, env: Record<string, string | undefined>): string | undefined;
11
11
  /**
12
12
  * Drains the catch, routes its sends to the declared recipients' inboxes and writes the thread to
13
13
  * `comms/thread.json`. Returns that path when a message matched, and the warnings to add to the
@@ -15,8 +15,8 @@ export declare function catchTokenOf(external: LabCommsExternal, env: Record<str
15
15
  * throws: the run continues without comms evidence.
16
16
  */
17
17
  export declare function collectExternalCommsEvidence(args: {
18
- external: LabCommsExternal;
19
- email: LabCommsEmail;
18
+ external: StudyCommsExternal;
19
+ email: StudyCommsEmail;
20
20
  env: Record<string, string | undefined>;
21
21
  runPaths: PreparedRunArtifactPaths;
22
22
  /** The run's known secret values. The route may add to it until the drain runs. */
@@ -4,10 +4,11 @@
4
4
  // shape for pattern redaction to find.
5
5
  import { redactText, toErrorMessage } from "../evidence/redaction.js";
6
6
  import { scrubSecretValues } from "../evidence/secret-scrub.js";
7
- import { addressedRecipients } from "../lab/parse/comms.js";
7
+ import { addressedRecipients } from "../study/parse/comms.js";
8
8
  import { writeContainedOutputFile } from "../run/contained-output.js";
9
9
  import { FakeInbox } from "./fake-inbox.js";
10
10
  import { collectExternalCommsThread } from "./sandbox-catch.js";
11
+ import { plural } from "../run/text.js";
11
12
  /**
12
13
  * The shortest catch bearer token humanish uses. The token guards GET /deliveries on a host the run
13
14
  * reaches over the network, so it must not be guessable: 16 random base64 characters are 96 bits.
@@ -33,7 +34,7 @@ export function catchTokenRefusal(token) {
33
34
  return undefined;
34
35
  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
  }
36
- /** The catch token the run sends: the value of `authTokenEnv`, when the lab names one. */
37
+ /** The catch token the run sends: the value of `authTokenEnv`, when the study names one. */
37
38
  export function catchTokenOf(external, env) {
38
39
  return external.authTokenEnv === undefined ? undefined : env[external.authTokenEnv];
39
40
  }
@@ -73,13 +74,13 @@ export async function collectExternalCommsEvidence(args) {
73
74
  if (collected.captured > 0) {
74
75
  return {
75
76
  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
+ `The email catch captured ${plural(collected.captured, "email send")}, but none matched a declared recipient inbox, so no email evidence was written. Set comms.email.recipients[].address to the address the app sends to.`,
77
78
  ]),
78
79
  };
79
80
  }
80
81
  return {
81
82
  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
+ `The email catch captured no email sends: the 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
  ]),
84
85
  };
85
86
  }
@@ -48,8 +48,8 @@ export function extractOtpCodes(body) {
48
48
  list.push(c);
49
49
  }
50
50
  };
51
- // The alphanumeric alternative requires at least one DIGIT (lookahead) so a labeled prose word like
52
- // "your code is INVALID" isn't captured as a code; pure-digit codes (4–8) match directly.
51
+ // The alphanumeric alternative requires at least one digit (lookahead) so a labeled prose word like
52
+ // `your code is INVALID` isn't captured as a code; pure-digit codes (4–8) match directly.
53
53
  const labeledRe = /(?:one[-\s]?time\s+(?:pass)?code|verification\s+code|security\s+code|access\s+code|login\s+code|confirmation\s+code|passcode|\bOTP\b|\bPIN\b|\bcode\b)\D{0,15}\b([0-9]{4,8}|(?=[A-Za-z0-9]*[0-9])[A-Z0-9]{6,8})\b/gi;
54
54
  let m;
55
55
  while ((m = labeledRe.exec(text)) !== null)
@@ -1,6 +1,6 @@
1
1
  import type { CommsAddress, CommsChannel, CommsChannelKind, CommsMessage, InboundRaw, OutboundMessage } from "./types.js";
2
2
  export interface FakeInboxOptions {
3
- /** "email" (default) or "sms" — the address shape + surface differ; machinery is identical. */
3
+ /** "email" (default) or "sms": the address shape + surface differ; machinery is identical. */
4
4
  channel?: CommsChannelKind;
5
5
  /** Email domain for minted addresses. Default example.test, an RFC 6761 reserved, unroutable
6
6
  * test domain. */
@@ -21,11 +21,11 @@ export declare class FakeInbox implements CommsChannel {
21
21
  constructor(options?: FakeInboxOptions);
22
22
  provision(participantId: string): Promise<CommsAddress>;
23
23
  /**
24
- * Provision an inbox for `participantId` at an EXPLICIT address (a lab-declared recipient), so
24
+ * Provision an inbox for `participantId` at an explicit address (a study-declared recipient), so
25
25
  * the app-under-test's send to that literal address resolves in `deliverRaw` (which drops
26
26
  * recipients with no provisioned inbox). Declaring the same address intentionally shares its
27
27
  * inbox; if `participantId` already held a different auto-generated address, the declared
28
- * address supersedes it (the lab's declaration wins). Idempotent: re-declaring the same address
28
+ * address supersedes it (the study's declaration wins). Idempotent: re-declaring the same address
29
29
  * returns the existing inbox without clearing it.
30
30
  */
31
31
  provisionAddress(participantId: string, value: string): Promise<CommsAddress>;
@@ -1,4 +1,4 @@
1
- // The in-process email/SMS bus (#297): a working, in-memory implementation of the CommsChannel
1
+ // The in-process email/SMS bus: a working, in-memory implementation of the CommsChannel
2
2
  // port. Deterministic, offline and free: a message the app under test sends (through an ingress
3
3
  // such as the email catch) is routed to the addressed inbox and read back through the same port.
4
4
  // Nothing leaves the process. See types.ts for the port and its public-safety notes.
@@ -60,11 +60,11 @@ export class FakeInbox {
60
60
  return address;
61
61
  }
62
62
  /**
63
- * Provision an inbox for `participantId` at an EXPLICIT address (a lab-declared recipient), so
63
+ * Provision an inbox for `participantId` at an explicit address (a study-declared recipient), so
64
64
  * the app-under-test's send to that literal address resolves in `deliverRaw` (which drops
65
65
  * recipients with no provisioned inbox). Declaring the same address intentionally shares its
66
66
  * inbox; if `participantId` already held a different auto-generated address, the declared
67
- * address supersedes it (the lab's declaration wins). Idempotent: re-declaring the same address
67
+ * address supersedes it (the study's declaration wins). Idempotent: re-declaring the same address
68
68
  * returns the existing inbox without clearing it.
69
69
  */
70
70
  async provisionAddress(participantId, value) {
@@ -7,7 +7,7 @@ export declare const MAX_RECEIVING_MESSAGES = 100;
7
7
  export declare const MAX_SURFACE_FILES: number;
8
8
  /** A local message or lease id: safe as one path segment. */
9
9
  export declare const LOCAL_ID: RegExp;
10
- /** Totals describe the whole snapshot (not increments). Register secrets BEFORE publishing files. */
10
+ /** Totals describe the whole snapshot (not increments). Register secrets before publishing files. */
11
11
  export declare function renderReceivingInbox(options: {
12
12
  address: string;
13
13
  messages: ParticipantEmail[];
@@ -91,7 +91,7 @@ function safeStyle(style) {
91
91
  }
92
92
  return declarations.join(";");
93
93
  }
94
- /** Totals describe the whole snapshot (not increments). Register secrets BEFORE publishing files. */
94
+ /** Totals describe the whole snapshot (not increments). Register secrets before publishing files. */
95
95
  export function renderReceivingInbox(options) {
96
96
  if (!options.address ||
97
97
  options.address.length > 512 ||
@@ -358,7 +358,7 @@ function renderImage(m, attrs) {
358
358
  const alt = attrs.get("alt")?.slice(0, 1024) || "Email image";
359
359
  if (!data) {
360
360
  m.assets++;
361
- return `<span class="blocked-image" role="img" aria-label="${esc(alt)}">${esc(alt)} — Image unavailable. Remote images are blocked; embedded images must be supported and within the size limit.</span>`;
361
+ return `<span class="blocked-image" role="img" aria-label="${esc(alt)}">${esc(alt)}: image unavailable. Remote images are blocked, and an embedded image must use a supported format and fit the size limit.</span>`;
362
362
  }
363
363
  // srcset, background and all network-bearing image attributes are deliberately absent.
364
364
  const width = /^\d{1,3}$/.test(attrs.get("width") ?? "") ? ` width="${attrs.get("width")}"` : "";
@@ -1,4 +1,4 @@
1
- import type { LabConfig } from "../lab/types.js";
1
+ import type { StudyConfig } from "../study/types.js";
2
2
  import { type CommsConnection } from "./connections.js";
3
3
  import { type KeyResolutionDeps } from "../keys/key-resolution.js";
4
4
  import type { ReceivingAdapter } from "./receiving-types.js";
@@ -14,9 +14,9 @@ export declare function resolveReceivingConnection(cwd: string, name: string, en
14
14
  apiKey: string;
15
15
  adapter: ReceivingAdapter;
16
16
  }>;
17
- /** What receiving reads from a lab: its email declaration and the subject env it guards. */
17
+ /** What receiving reads from a study: its email declaration and the subject env it guards. */
18
18
  export interface ReceivingSource {
19
- readonly comms?: LabConfig["comms"];
19
+ readonly comms?: StudyConfig["comms"];
20
20
  readonly subject: {
21
21
  readonly env?: readonly string[];
22
22
  readonly envValues?: Readonly<Record<string, string>>;
@@ -32,7 +32,7 @@ export declare function prepareReceivingRun(args: {
32
32
  registerSecrets(values: string[]): void;
33
33
  }): Promise<import("./receiving.js").CommsReceivingRun | undefined>;
34
34
  /** The restriction survives restarts and screenshot redaction; it is not a claim of local processing. */
35
- export declare function receivingPublication(config: Pick<LabConfig, "comms">, dryRun: boolean): {
35
+ export declare function receivingPublication(config: Pick<StudyConfig, "comms">, dryRun: boolean): {
36
36
  publication: {
37
37
  restrictions: ["real-communications"];
38
38
  };
@@ -1,15 +1,15 @@
1
1
  /**
2
- * The self-contained in-sandbox capture server — a plain **python3** script (stdlib only), because the
3
- * stock E2B desktop template ships python3 but NOT node, and the co-located catcher must run in a
2
+ * The self-contained in-sandbox capture server: a plain **python3** script (stdlib only), because the
3
+ * stock E2B desktop template ships python3 but not node, and the co-located catcher must run in a
4
4
  * runtime the sandbox guarantees (the precedented choice: LocalStack is a python catcher the app points
5
5
  * at; you pick the runtime the environment has). It runs on the sandbox's own python3, imports nothing
6
- * from humanish. DELIBERATELY dumb: it records each POST verbatim as an NDJSON line `{t, path, body}`
7
- * and returns a plausible provider success — all normalization/profile parsing happens host-side on the
6
+ * from humanish. Deliberately dumb: it records each POST verbatim as an NDJSON line `{t, path, body}`
7
+ * and returns a plausible provider success; all normalization/profile parsing happens host-side on the
8
8
  * drained lines, so the typed, tested profiles stay in one place. It also serves the host-rendered inbox
9
9
  * surface statically at /inbox + /api/inbox (with a script-forbidding CSP). argv: <port> <deliveriesFile>
10
- * <servedDir> [inboxPort]. The capture listener binds 127.0.0.1 (loopback) — the app under test reaches
10
+ * <servedDir> [inboxPort]. The capture listener binds 127.0.0.1 (loopback): the app under test reaches
11
11
  * it in-sandbox, nothing on the internet can inject a fake send. When an [inboxPort] is given (the
12
- * shared-world route, where the persona lives in a DIFFERENT sandbox), it ALSO starts a READ-ONLY inbox
12
+ * shared-world route, where the persona lives in a different sandbox), it also starts a read-only inbox
13
13
  * listener on 0.0.0.0:<inboxPort> so getHost can proxy the persona's inbox reads to it; that listener
14
14
  * serves GET only (POST → 405). The CUA same-sandbox route omits it and stays loopback-only.
15
15
  */