humanish 0.105.0 → 0.106.1

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 (1408) hide show
  1. package/AGENTS.md +91 -94
  2. package/ARCHITECTURE.md +146 -0
  3. package/CHANGELOG.md +512 -0
  4. package/CONTEXT.md +51 -0
  5. package/CONTRIBUTING.md +113 -9
  6. package/README.md +213 -124
  7. package/SECURITY.md +1 -1
  8. package/TELEMETRY.md +112 -0
  9. package/dist/actors/affordance.d.ts +56 -0
  10. package/dist/actors/affordance.js +155 -0
  11. package/dist/actors/agent-session.js +42 -0
  12. package/dist/actors/codex/app-server-actor-trace.d.ts +11 -0
  13. package/dist/actors/codex/app-server-actor-trace.js +211 -0
  14. package/dist/actors/codex/app-server-client.d.ts +39 -0
  15. package/dist/actors/codex/app-server-client.js +116 -0
  16. package/dist/actors/codex/app-server-trace.d.ts +154 -0
  17. package/dist/actors/codex/app-server-trace.js +424 -0
  18. package/dist/actors/codex/app-server-ui.d.ts +40 -0
  19. package/dist/actors/codex/app-server-ui.js +328 -0
  20. package/dist/actors/codex/app-server.d.ts +44 -0
  21. package/dist/actors/codex/app-server.js +268 -0
  22. package/dist/actors/codex/qualified-versions.d.ts +45 -0
  23. package/dist/actors/codex/qualified-versions.js +87 -0
  24. package/dist/actors/codex/restricted-executable.d.ts +17 -0
  25. package/dist/actors/codex/restricted-executable.js +147 -0
  26. package/dist/actors/codex/restricted-launch.d.ts +116 -0
  27. package/dist/actors/codex/restricted-launch.js +222 -0
  28. package/dist/actors/codex/restricted-participant-policy.d.ts +46 -0
  29. package/dist/actors/codex/restricted-participant-policy.js +82 -0
  30. package/dist/actors/codex/restricted-participant.d.ts +20 -0
  31. package/dist/actors/codex/restricted-participant.js +432 -0
  32. package/dist/actors/codex/restricted-policy.d.ts +70 -0
  33. package/dist/actors/codex/restricted-policy.js +326 -0
  34. package/dist/actors/codex/restricted-session.d.ts +65 -0
  35. package/dist/actors/codex/restricted-session.js +432 -0
  36. package/dist/actors/codex/restricted-transport.d.ts +73 -0
  37. package/dist/actors/codex/restricted-transport.js +335 -0
  38. package/dist/actors/codex/restricted-turn.d.ts +62 -0
  39. package/dist/actors/codex/restricted-turn.js +284 -0
  40. package/dist/actors/computer-use/actor.d.ts +77 -0
  41. package/dist/actors/computer-use/actor.js +78 -0
  42. package/dist/actors/computer-use/admission-limit.d.ts +12 -0
  43. package/dist/actors/computer-use/admission-limit.js +19 -0
  44. package/dist/actors/computer-use/executor-error.js +48 -0
  45. package/dist/actors/computer-use/loop/actions.d.ts +26 -0
  46. package/dist/actors/computer-use/loop/actions.js +245 -0
  47. package/dist/actors/computer-use/loop/backstop.d.ts +42 -0
  48. package/dist/actors/computer-use/loop/backstop.js +202 -0
  49. package/dist/actors/computer-use/loop/debrief.d.ts +19 -0
  50. package/dist/actors/computer-use/loop/debrief.js +164 -0
  51. package/dist/actors/computer-use/loop/ending.d.ts +86 -0
  52. package/dist/actors/computer-use/loop/ending.js +316 -0
  53. package/dist/actors/computer-use/loop/observation.d.ts +55 -0
  54. package/dist/actors/computer-use/loop/observation.js +246 -0
  55. package/dist/actors/computer-use/loop/output-limit.d.ts +11 -0
  56. package/dist/actors/computer-use/loop/output-limit.js +21 -0
  57. package/dist/actors/computer-use/loop/provider-call.d.ts +19 -0
  58. package/dist/actors/computer-use/loop/provider-call.js +208 -0
  59. package/dist/actors/computer-use/loop/race.d.ts +34 -0
  60. package/dist/actors/computer-use/loop/race.js +79 -0
  61. package/dist/actors/computer-use/loop/session.d.ts +77 -0
  62. package/dist/actors/computer-use/loop/session.js +135 -0
  63. package/dist/actors/computer-use/loop/spend.d.ts +30 -0
  64. package/dist/actors/computer-use/loop/spend.js +85 -0
  65. package/dist/actors/computer-use/loop/trace.d.ts +35 -0
  66. package/dist/actors/computer-use/loop/trace.js +140 -0
  67. package/dist/actors/computer-use/loop/types.d.ts +404 -0
  68. package/dist/actors/computer-use/loop/types.js +1 -0
  69. package/dist/actors/computer-use/loop/usage.d.ts +102 -0
  70. package/dist/actors/computer-use/loop/usage.js +355 -0
  71. package/dist/actors/computer-use/loop.d.ts +11 -0
  72. package/dist/actors/computer-use/loop.js +248 -0
  73. package/dist/actors/computer-use/openai-provider.d.ts +107 -0
  74. package/dist/actors/computer-use/openai-provider.js +472 -0
  75. package/dist/actors/computer-use/openai-requests.d.ts +35 -0
  76. package/dist/actors/computer-use/openai-requests.js +74 -0
  77. package/dist/actors/computer-use/openai-wire.d.ts +67 -0
  78. package/dist/actors/computer-use/openai-wire.js +365 -0
  79. package/dist/actors/computer-use/provider-error.d.ts +27 -0
  80. package/dist/actors/computer-use/provider-error.js +86 -0
  81. package/dist/actors/computer-use/speech.js +6 -0
  82. package/dist/actors/contract.d.ts +315 -0
  83. package/dist/actors/contract.js +207 -0
  84. package/dist/actors/goal-source.js +38 -0
  85. package/dist/actors/local-agent/claude-session.d.ts +43 -0
  86. package/dist/actors/local-agent/claude-session.js +370 -0
  87. package/dist/actors/local-agent/cli.d.ts +115 -0
  88. package/dist/actors/local-agent/cli.js +413 -0
  89. package/dist/actors/local-agent/readiness.d.ts +31 -0
  90. package/dist/actors/local-agent/readiness.js +45 -0
  91. package/dist/actors/openai-endpoint.d.ts +2 -0
  92. package/dist/actors/openai-endpoint.js +2 -0
  93. package/dist/actors/output-token-limit.js +4 -0
  94. package/dist/actors/reasoning-effort.js +33 -0
  95. package/dist/actors/registry.d.ts +59 -0
  96. package/dist/actors/registry.js +89 -0
  97. package/dist/actors/scripted-browser/actor.d.ts +49 -0
  98. package/dist/actors/scripted-browser/actor.js +428 -0
  99. package/dist/actors/scripted-browser/browser-command.d.ts +1 -0
  100. package/dist/actors/scripted-browser/browser-command.js +48 -0
  101. package/dist/actors/scripted-browser/journey.d.ts +9 -0
  102. package/dist/actors/scripted-browser/journey.js +171 -0
  103. package/dist/actors/scripted-browser/steps.d.ts +60 -0
  104. package/dist/actors/scripted-browser/steps.js +384 -0
  105. package/dist/actors/scripted-browser/types.d.ts +135 -0
  106. package/dist/actors/scripted-browser/types.js +26 -0
  107. package/dist/actors/stop-cause.d.ts +9 -0
  108. package/dist/actors/stop-cause.js +42 -0
  109. package/dist/actors/stop-conditions.d.ts +40 -0
  110. package/dist/actors/stop-conditions.js +65 -0
  111. package/dist/analysis/automatic-completion.d.ts +38 -0
  112. package/dist/analysis/automatic-completion.js +90 -0
  113. package/dist/analysis/automatic-config.d.ts +41 -0
  114. package/dist/analysis/automatic-config.js +135 -0
  115. package/dist/analysis/automatic.d.ts +17 -0
  116. package/dist/analysis/automatic.js +279 -0
  117. package/dist/analysis/codex-config.d.ts +8 -0
  118. package/dist/analysis/codex-config.js +69 -0
  119. package/dist/analysis/evidence-sources.d.ts +23 -0
  120. package/dist/analysis/evidence-sources.js +251 -0
  121. package/dist/analysis/evidence.d.ts +18 -0
  122. package/dist/analysis/evidence.js +482 -0
  123. package/dist/analysis/execute.d.ts +52 -0
  124. package/dist/analysis/execute.js +504 -0
  125. package/dist/analysis/image-tokens.d.ts +1 -0
  126. package/dist/analysis/image-tokens.js +41 -0
  127. package/dist/analysis/job.d.ts +97 -0
  128. package/dist/analysis/job.js +311 -0
  129. package/dist/analysis/load.d.ts +3 -0
  130. package/dist/analysis/load.js +11 -0
  131. package/dist/analysis/provider.d.ts +57 -0
  132. package/dist/analysis/provider.js +227 -0
  133. package/dist/analysis/restricted-codex.d.ts +19 -0
  134. package/dist/analysis/restricted-codex.js +23 -0
  135. package/dist/analysis/service.d.ts +72 -0
  136. package/dist/analysis/service.js +399 -0
  137. package/dist/analysis/sharing.d.ts +11 -0
  138. package/dist/analysis/sharing.js +44 -0
  139. package/dist/analysis/store-executions.d.ts +27 -0
  140. package/dist/analysis/store-executions.js +224 -0
  141. package/dist/analysis/store.d.ts +31 -0
  142. package/dist/analysis/store.js +315 -0
  143. package/dist/analysis/types.d.ts +217 -0
  144. package/dist/analysis/types.js +12 -0
  145. package/dist/analysis/validation.d.ts +225 -0
  146. package/dist/analysis/validation.js +562 -0
  147. package/dist/browser-control/client.d.ts +18 -0
  148. package/dist/browser-control/client.js +239 -0
  149. package/dist/browser-control/dispatcher.d.ts +19 -0
  150. package/dist/browser-control/dispatcher.js +221 -0
  151. package/dist/browser-control/protocol.d.ts +492 -0
  152. package/dist/browser-control/protocol.js +360 -0
  153. package/dist/browser-control/recording-transfer.d.ts +3 -0
  154. package/dist/browser-control/recording-transfer.js +48 -0
  155. package/dist/browser-control/transport.d.ts +27 -0
  156. package/dist/browser-control/transport.js +189 -0
  157. package/dist/cli/commands/analysis-signals.d.ts +7 -0
  158. package/dist/cli/commands/analysis-signals.js +25 -0
  159. package/dist/cli/commands/analyze.d.ts +3 -0
  160. package/dist/cli/commands/analyze.js +212 -0
  161. package/dist/cli/commands/codex.d.ts +3 -0
  162. package/dist/cli/commands/codex.js +145 -0
  163. package/dist/cli/commands/comms.d.ts +3 -0
  164. package/dist/cli/commands/comms.js +225 -0
  165. package/dist/cli/commands/feedback.d.ts +3 -0
  166. package/dist/cli/commands/feedback.js +138 -0
  167. package/dist/cli/commands/lab-format.d.ts +8 -0
  168. package/dist/cli/commands/lab-format.js +105 -0
  169. package/dist/cli/commands/lab-route-computer-use.d.ts +20 -0
  170. package/dist/cli/commands/lab-route-computer-use.js +251 -0
  171. package/dist/cli/commands/lab-route-open.d.ts +27 -0
  172. package/dist/cli/commands/lab-route-open.js +37 -0
  173. package/dist/cli/commands/lab-route-preview.d.ts +20 -0
  174. package/dist/cli/commands/lab-route-preview.js +74 -0
  175. package/dist/cli/commands/lab-route-run.d.ts +17 -0
  176. package/dist/cli/commands/lab-route-run.js +31 -0
  177. package/dist/cli/commands/lab-route-scripted.d.ts +19 -0
  178. package/dist/cli/commands/lab-route-scripted.js +50 -0
  179. package/dist/cli/commands/lab-route-shared-world.d.ts +20 -0
  180. package/dist/cli/commands/lab-route-shared-world.js +135 -0
  181. package/dist/cli/commands/lab-route-terminal.d.ts +19 -0
  182. package/dist/cli/commands/lab-route-terminal.js +49 -0
  183. package/dist/cli/commands/lab-run.d.ts +9 -0
  184. package/dist/cli/commands/lab-run.js +166 -0
  185. package/dist/cli/commands/lab-scorer.d.ts +30 -0
  186. package/dist/cli/commands/lab-scorer.js +17 -0
  187. package/dist/cli/commands/lab.d.ts +3 -0
  188. package/dist/cli/commands/lab.js +193 -0
  189. package/dist/cli/commands/observe.d.ts +4 -0
  190. package/dist/cli/commands/observe.js +345 -0
  191. package/dist/cli/commands/runs.d.ts +11 -0
  192. package/dist/cli/commands/runs.js +314 -0
  193. package/dist/cli/commands/setup.d.ts +11 -0
  194. package/dist/cli/commands/setup.js +325 -0
  195. package/dist/cli/commands/tui.d.ts +32 -0
  196. package/dist/cli/commands/tui.js +305 -0
  197. package/dist/cli/commands/watch.d.ts +3 -0
  198. package/dist/cli/commands/watch.js +245 -0
  199. package/dist/cli/doctor.d.ts +39 -0
  200. package/dist/cli/doctor.js +263 -0
  201. package/dist/cli/first-run-path.d.ts +67 -0
  202. package/dist/cli/first-run-path.js +150 -0
  203. package/dist/cli/io.d.ts +85 -0
  204. package/dist/cli/io.js +232 -0
  205. package/dist/cli/observer-follow.d.ts +56 -0
  206. package/dist/cli/observer-follow.js +160 -0
  207. package/dist/cli/orientation.d.ts +30 -0
  208. package/dist/cli/orientation.js +114 -0
  209. package/dist/cli/program.d.ts +15 -0
  210. package/dist/cli/program.js +338 -0
  211. package/dist/cli/secret-prompt.js +39 -0
  212. package/dist/cli/telemetry.js +351 -0
  213. package/dist/cli.js +2 -4
  214. package/dist/comms/agentmail.d.ts +29 -0
  215. package/dist/comms/agentmail.js +568 -0
  216. package/dist/comms/capture-surface.d.ts +54 -0
  217. package/dist/comms/capture-surface.js +468 -0
  218. package/dist/comms/catch-host.d.ts +52 -0
  219. package/dist/comms/catch-host.js +192 -0
  220. package/dist/comms/connections.d.ts +20 -0
  221. package/dist/comms/connections.js +116 -0
  222. package/dist/comms/email-catch.d.ts +49 -0
  223. package/dist/comms/email-catch.js +236 -0
  224. package/dist/comms/extract.d.ts +7 -0
  225. package/dist/comms/extract.js +65 -0
  226. package/dist/comms/fake-inbox.d.ts +39 -0
  227. package/dist/comms/fake-inbox.js +136 -0
  228. package/dist/comms/images.d.ts +7 -0
  229. package/dist/comms/images.js +53 -0
  230. package/dist/comms/lease-store.d.ts +91 -0
  231. package/dist/comms/lease-store.js +600 -0
  232. package/dist/comms/providers.d.ts +30 -0
  233. package/dist/comms/providers.js +24 -0
  234. package/dist/comms/receiving-common.d.ts +17 -0
  235. package/dist/comms/receiving-common.js +82 -0
  236. package/dist/comms/receiving-evidence.d.ts +4 -0
  237. package/dist/comms/receiving-evidence.js +59 -0
  238. package/dist/comms/receiving-recovery.d.ts +22 -0
  239. package/dist/comms/receiving-recovery.js +100 -0
  240. package/dist/comms/receiving-render.d.ts +16 -0
  241. package/dist/comms/receiving-render.js +409 -0
  242. package/dist/comms/receiving-runtime.d.ts +41 -0
  243. package/dist/comms/receiving-runtime.js +66 -0
  244. package/dist/comms/receiving-surface.d.ts +8 -0
  245. package/dist/comms/receiving-surface.js +156 -0
  246. package/dist/comms/receiving-types.d.ts +122 -0
  247. package/dist/comms/receiving-types.js +3 -0
  248. package/dist/comms/receiving.d.ts +33 -0
  249. package/dist/comms/receiving.js +508 -0
  250. package/dist/comms/sandbox-catch-script.d.ts +16 -0
  251. package/dist/comms/sandbox-catch-script.js +331 -0
  252. package/dist/comms/sandbox-catch.d.ts +214 -0
  253. package/dist/comms/sandbox-catch.js +451 -0
  254. package/dist/comms/setup.d.ts +65 -0
  255. package/dist/comms/setup.js +259 -0
  256. package/dist/comms/thread-evidence.d.ts +28 -0
  257. package/dist/comms/thread-evidence.js +30 -0
  258. package/dist/comms/types.d.ts +76 -0
  259. package/dist/comms/types.js +8 -0
  260. package/dist/deprecated.d.ts +90 -0
  261. package/dist/deprecated.js +106 -0
  262. package/dist/evidence/browser-hygiene.js +42 -0
  263. package/dist/evidence/desktop-recorder.d.ts +39 -0
  264. package/dist/evidence/desktop-recorder.js +340 -0
  265. package/dist/evidence/desktop-recording-artifact.d.ts +5 -0
  266. package/dist/evidence/desktop-recording-artifact.js +53 -0
  267. package/dist/evidence/desktop-recording-types.d.ts +27 -0
  268. package/dist/evidence/desktop-recording-types.js +16 -0
  269. package/dist/evidence/encoded-text.d.ts +27 -0
  270. package/dist/evidence/encoded-text.js +243 -0
  271. package/dist/evidence/frame-signature.d.ts +17 -0
  272. package/dist/evidence/frame-signature.js +111 -0
  273. package/dist/evidence/image.d.ts +24 -0
  274. package/dist/evidence/image.js +170 -0
  275. package/dist/evidence/plain-text.d.ts +8 -0
  276. package/dist/evidence/plain-text.js +16 -0
  277. package/dist/evidence/redaction.d.ts +110 -0
  278. package/dist/evidence/redaction.js +329 -0
  279. package/dist/feedback/draft.d.ts +58 -0
  280. package/dist/feedback/draft.js +309 -0
  281. package/dist/feedback/export-bundle.d.ts +6 -0
  282. package/dist/feedback/export-bundle.js +585 -0
  283. package/dist/feedback/export.d.ts +50 -0
  284. package/dist/feedback/export.js +392 -0
  285. package/dist/feedback/feedback.d.ts +37 -0
  286. package/dist/feedback/feedback.js +282 -0
  287. package/dist/feedback/proof.d.ts +8 -0
  288. package/dist/feedback/proof.js +56 -0
  289. package/dist/guest/bootstrap.d.ts +55 -0
  290. package/dist/guest/bootstrap.js +348 -0
  291. package/dist/guest/browser-tools.d.ts +8 -0
  292. package/dist/guest/browser-tools.js +72 -0
  293. package/dist/guest/chromium-text.d.ts +62 -0
  294. package/dist/guest/chromium-text.js +386 -0
  295. package/dist/guest/desktop-executor.d.ts +57 -0
  296. package/dist/guest/desktop-executor.js +256 -0
  297. package/dist/guest/desktop-keys.d.ts +8 -0
  298. package/dist/guest/desktop-keys.js +89 -0
  299. package/dist/guest/desktop-media.d.ts +70 -0
  300. package/dist/guest/desktop-media.js +385 -0
  301. package/dist/guest/desktop-native.d.ts +14 -0
  302. package/dist/guest/desktop-native.js +156 -0
  303. package/dist/guest/media-config.d.ts +15 -0
  304. package/dist/guest/media-config.js +16 -0
  305. package/dist/guest/runtime-desktop.d.ts +45 -0
  306. package/dist/guest/runtime-desktop.js +420 -0
  307. package/dist/guest/runtime-processes.d.ts +28 -0
  308. package/dist/guest/runtime-processes.js +94 -0
  309. package/dist/guest/runtime.d.ts +31 -0
  310. package/dist/guest/runtime.js +124 -0
  311. package/dist/guest-media-worker.js +184 -48
  312. package/dist/guest-runtime-main.js +23 -10
  313. package/dist/guest-runtime-revision.js +1 -1
  314. package/dist/index.d.ts +32 -83
  315. package/dist/index.js +21 -42
  316. package/dist/keys/env-file.d.ts +15 -0
  317. package/dist/keys/env-file.js +109 -0
  318. package/dist/keys/key-resolution.d.ts +58 -0
  319. package/dist/keys/key-resolution.js +389 -0
  320. package/dist/lab/adapter-extension.d.ts +75 -0
  321. package/dist/lab/adapter-extension.js +253 -0
  322. package/dist/lab/adapter-scorer-loader.d.ts +43 -0
  323. package/dist/lab/adapter-scorer-loader.js +130 -0
  324. package/dist/lab/bag-overrides.d.ts +18 -0
  325. package/dist/lab/bag-overrides.js +121 -0
  326. package/dist/lab/composition-rules.d.ts +7 -0
  327. package/dist/lab/composition-rules.js +292 -0
  328. package/dist/lab/config.d.ts +7 -0
  329. package/dist/lab/config.js +208 -0
  330. package/dist/lab/device-presets.d.ts +97 -0
  331. package/dist/lab/device-presets.js +87 -0
  332. package/dist/lab/discover.d.ts +65 -0
  333. package/dist/lab/discover.js +422 -0
  334. package/dist/lab/doctor.d.ts +39 -0
  335. package/dist/lab/doctor.js +233 -0
  336. package/dist/lab/init-paths.d.ts +5 -0
  337. package/dist/lab/init-paths.js +89 -0
  338. package/dist/lab/init-plan.d.ts +20 -0
  339. package/dist/lab/init-plan.js +163 -0
  340. package/dist/lab/init-templates.d.ts +28 -0
  341. package/dist/lab/init-templates.js +416 -0
  342. package/dist/lab/init.d.ts +39 -0
  343. package/dist/lab/init.js +287 -0
  344. package/dist/lab/keys.d.ts +2 -0
  345. package/dist/lab/keys.js +264 -0
  346. package/dist/lab/parse/actors.d.ts +14 -0
  347. package/dist/lab/parse/actors.js +453 -0
  348. package/dist/lab/parse/comms.d.ts +12 -0
  349. package/dist/lab/parse/comms.js +246 -0
  350. package/dist/lab/parse/execution.d.ts +19 -0
  351. package/dist/lab/parse/execution.js +356 -0
  352. package/dist/lab/parse/subject-state.d.ts +30 -0
  353. package/dist/lab/parse/subject-state.js +179 -0
  354. package/dist/lab/parse/subject.d.ts +9 -0
  355. package/dist/lab/parse/subject.js +420 -0
  356. package/dist/lab/parse/values.d.ts +17 -0
  357. package/dist/lab/parse/values.js +65 -0
  358. package/dist/lab/participant-assignment.d.ts +12 -0
  359. package/dist/lab/participant-assignment.js +19 -0
  360. package/dist/lab/persona-resolve.d.ts +39 -0
  361. package/dist/lab/persona-resolve.js +118 -0
  362. package/dist/lab/persona.d.ts +39 -0
  363. package/dist/lab/persona.js +192 -0
  364. package/dist/lab/plan-base.d.ts +30 -0
  365. package/dist/lab/plan-base.js +126 -0
  366. package/dist/lab/plan-participants.d.ts +81 -0
  367. package/dist/lab/plan-participants.js +92 -0
  368. package/dist/lab/plan-types.d.ts +277 -0
  369. package/dist/lab/plan-types.js +5 -0
  370. package/dist/lab/plan.d.ts +27 -0
  371. package/dist/lab/plan.js +156 -0
  372. package/dist/lab/preflight-probes.d.ts +3 -0
  373. package/dist/lab/preflight-probes.js +312 -0
  374. package/dist/lab/preflight-result.d.ts +7 -0
  375. package/dist/lab/preflight-result.js +43 -0
  376. package/dist/lab/preflight.d.ts +95 -0
  377. package/dist/lab/preflight.js +117 -0
  378. package/dist/lab/route-inputs.d.ts +17 -0
  379. package/dist/lab/route-inputs.js +95 -0
  380. package/dist/lab/routing.d.ts +81 -0
  381. package/dist/lab/routing.js +214 -0
  382. package/dist/lab/run-lab-events.d.ts +69 -0
  383. package/dist/lab/run-lab-events.js +95 -0
  384. package/dist/lab/run-lab-options.d.ts +82 -0
  385. package/dist/lab/run-lab-options.js +399 -0
  386. package/dist/lab/summary.d.ts +60 -0
  387. package/dist/lab/summary.js +168 -0
  388. package/dist/lab/tasks.d.ts +91 -0
  389. package/dist/lab/tasks.js +139 -0
  390. package/dist/lab/types.d.ts +813 -0
  391. package/dist/lab/types.js +6 -0
  392. package/dist/lab/validation.d.ts +67 -0
  393. package/dist/lab/validation.js +387 -0
  394. package/dist/lab/warnings.d.ts +2 -0
  395. package/dist/lab/warnings.js +338 -0
  396. package/dist/observer/artifact.d.ts +23 -0
  397. package/dist/observer/artifact.js +235 -0
  398. package/dist/observer/data.d.ts +115 -0
  399. package/dist/observer/data.js +258 -0
  400. package/dist/observer/exposure.d.ts +64 -0
  401. package/dist/observer/exposure.js +143 -0
  402. package/dist/observer/http.js +50 -0
  403. package/dist/observer/library.js +190 -0
  404. package/dist/observer/listen.js +85 -0
  405. package/dist/observer/live.d.ts +9 -0
  406. package/dist/observer/live.js +25 -0
  407. package/dist/observer/pinned-files.d.ts +30 -0
  408. package/dist/observer/pinned-files.js +160 -0
  409. package/dist/observer/render.d.ts +69 -0
  410. package/dist/observer/render.js +350 -0
  411. package/dist/observer/run-inventory.d.ts +35 -0
  412. package/dist/observer/run-inventory.js +95 -0
  413. package/dist/observer/run-routes.d.ts +37 -0
  414. package/dist/observer/run-routes.js +373 -0
  415. package/dist/observer/serve.d.ts +97 -0
  416. package/dist/observer/serve.js +305 -0
  417. package/dist/observer/static.js +369 -0
  418. package/dist/observer/tunnel.js +112 -0
  419. package/dist/observer-app.html +9 -20
  420. package/dist/routes/computer-use/bundle-parts.d.ts +50 -0
  421. package/dist/routes/computer-use/bundle-parts.js +177 -0
  422. package/dist/routes/computer-use/bundle.d.ts +56 -0
  423. package/dist/routes/computer-use/bundle.js +184 -0
  424. package/dist/routes/computer-use/diagnostics.d.ts +38 -0
  425. package/dist/routes/computer-use/diagnostics.js +94 -0
  426. package/dist/routes/computer-use/e2b-desktop/comms.d.ts +65 -0
  427. package/dist/routes/computer-use/e2b-desktop/comms.js +225 -0
  428. package/dist/routes/computer-use/e2b-desktop/desktop.d.ts +3 -0
  429. package/dist/routes/computer-use/e2b-desktop/desktop.js +91 -0
  430. package/dist/routes/computer-use/e2b-desktop/fidelity.d.ts +65 -0
  431. package/dist/routes/computer-use/e2b-desktop/fidelity.js +156 -0
  432. package/dist/routes/computer-use/e2b-desktop/prepare.d.ts +18 -0
  433. package/dist/routes/computer-use/e2b-desktop/prepare.js +198 -0
  434. package/dist/routes/computer-use/e2b-desktop/start.d.ts +14 -0
  435. package/dist/routes/computer-use/e2b-desktop/start.js +193 -0
  436. package/dist/routes/computer-use/e2b-desktop/state.d.ts +65 -0
  437. package/dist/routes/computer-use/e2b-desktop/state.js +69 -0
  438. package/dist/routes/computer-use/e2b-desktop/teardown.d.ts +6 -0
  439. package/dist/routes/computer-use/e2b-desktop/teardown.js +152 -0
  440. package/dist/routes/computer-use/external-comms.d.ts +20 -0
  441. package/dist/routes/computer-use/external-comms.js +55 -0
  442. package/dist/routes/computer-use/fanout-bundle.d.ts +11 -0
  443. package/dist/routes/computer-use/fanout-bundle.js +274 -0
  444. package/dist/routes/computer-use/fanout-lanes.d.ts +15 -0
  445. package/dist/routes/computer-use/fanout-lanes.js +302 -0
  446. package/dist/routes/computer-use/in-process-desktop.d.ts +17 -0
  447. package/dist/routes/computer-use/in-process-desktop.js +40 -0
  448. package/dist/routes/computer-use/lanes.d.ts +35 -0
  449. package/dist/routes/computer-use/lanes.js +321 -0
  450. package/dist/routes/computer-use/legacy-lane-spec.d.ts +62 -0
  451. package/dist/routes/computer-use/legacy-lane-spec.js +39 -0
  452. package/dist/routes/computer-use/live-flush.d.ts +42 -0
  453. package/dist/routes/computer-use/live-flush.js +154 -0
  454. package/dist/routes/computer-use/local-tree-pack.d.ts +27 -0
  455. package/dist/routes/computer-use/local-tree-pack.js +47 -0
  456. package/dist/routes/computer-use/local-vm.d.ts +20 -0
  457. package/dist/routes/computer-use/local-vm.js +218 -0
  458. package/dist/routes/computer-use/participant-desktop.d.ts +63 -0
  459. package/dist/routes/computer-use/participant-desktop.js +33 -0
  460. package/dist/routes/computer-use/participant-facts.d.ts +6 -0
  461. package/dist/routes/computer-use/participant-facts.js +22 -0
  462. package/dist/routes/computer-use/participant-model.d.ts +30 -0
  463. package/dist/routes/computer-use/participant-model.js +223 -0
  464. package/dist/routes/computer-use/participant-prompt.d.ts +53 -0
  465. package/dist/routes/computer-use/participant-prompt.js +87 -0
  466. package/dist/routes/computer-use/participant-runs.d.ts +67 -0
  467. package/dist/routes/computer-use/participant-runs.js +255 -0
  468. package/dist/routes/computer-use/plan.d.ts +56 -0
  469. package/dist/routes/computer-use/plan.js +310 -0
  470. package/dist/routes/computer-use/preflight.d.ts +23 -0
  471. package/dist/routes/computer-use/preflight.js +89 -0
  472. package/dist/routes/computer-use/rerun-selection.d.ts +19 -0
  473. package/dist/routes/computer-use/rerun-selection.js +121 -0
  474. package/dist/routes/computer-use/result.d.ts +7 -0
  475. package/dist/routes/computer-use/result.js +292 -0
  476. package/dist/routes/computer-use/route.d.ts +33 -0
  477. package/dist/routes/computer-use/route.js +145 -0
  478. package/dist/routes/computer-use/run-lanes.d.ts +59 -0
  479. package/dist/routes/computer-use/run-lanes.js +135 -0
  480. package/dist/routes/computer-use/self-report.d.ts +21 -0
  481. package/dist/routes/computer-use/self-report.js +231 -0
  482. package/dist/routes/computer-use/setup.d.ts +133 -0
  483. package/dist/routes/computer-use/setup.js +359 -0
  484. package/dist/routes/computer-use/single-bundle.d.ts +97 -0
  485. package/dist/routes/computer-use/single-bundle.js +393 -0
  486. package/dist/routes/computer-use/subject-projection.d.ts +51 -0
  487. package/dist/routes/computer-use/subject-projection.js +159 -0
  488. package/dist/routes/computer-use/types.d.ts +538 -0
  489. package/dist/routes/computer-use/types.js +36 -0
  490. package/dist/routes/preview.d.ts +13 -0
  491. package/dist/routes/preview.js +36 -0
  492. package/dist/routes/scripted/bundle.d.ts +47 -0
  493. package/dist/routes/scripted/bundle.js +195 -0
  494. package/dist/routes/scripted/plan.d.ts +30 -0
  495. package/dist/routes/scripted/plan.js +95 -0
  496. package/dist/routes/scripted/result.d.ts +48 -0
  497. package/dist/routes/scripted/result.js +176 -0
  498. package/dist/routes/scripted/route.d.ts +28 -0
  499. package/dist/routes/scripted/route.js +218 -0
  500. package/dist/routes/scripted/scenario.d.ts +21 -0
  501. package/dist/routes/scripted/scenario.js +102 -0
  502. package/dist/routes/scripted/session-result.d.ts +8 -0
  503. package/dist/routes/scripted/session-result.js +60 -0
  504. package/dist/routes/scripted/setup.d.ts +45 -0
  505. package/dist/routes/scripted/setup.js +126 -0
  506. package/dist/routes/scripted/subject.d.ts +49 -0
  507. package/dist/routes/scripted/subject.js +157 -0
  508. package/dist/routes/scripted/surface-records.d.ts +26 -0
  509. package/dist/routes/scripted/surface-records.js +88 -0
  510. package/dist/routes/scripted/surface-sessions.d.ts +13 -0
  511. package/dist/routes/scripted/surface-sessions.js +40 -0
  512. package/dist/routes/scripted/types.d.ts +88 -0
  513. package/dist/routes/scripted/types.js +3 -0
  514. package/dist/routes/shared-world/bundle.d.ts +20 -0
  515. package/dist/routes/shared-world/bundle.js +446 -0
  516. package/dist/routes/shared-world/checkpoints.d.ts +25 -0
  517. package/dist/routes/shared-world/checkpoints.js +46 -0
  518. package/dist/routes/shared-world/comms.d.ts +68 -0
  519. package/dist/routes/shared-world/comms.js +176 -0
  520. package/dist/routes/shared-world/external-public.d.ts +21 -0
  521. package/dist/routes/shared-world/external-public.js +130 -0
  522. package/dist/routes/shared-world/handoff.d.ts +62 -0
  523. package/dist/routes/shared-world/handoff.js +348 -0
  524. package/dist/routes/shared-world/lobby-code.d.ts +25 -0
  525. package/dist/routes/shared-world/lobby-code.js +152 -0
  526. package/dist/routes/shared-world/plan.d.ts +27 -0
  527. package/dist/routes/shared-world/plan.js +174 -0
  528. package/dist/routes/shared-world/provenance.d.ts +27 -0
  529. package/dist/routes/shared-world/provenance.js +65 -0
  530. package/dist/routes/shared-world/provisioned.d.ts +57 -0
  531. package/dist/routes/shared-world/provisioned.js +506 -0
  532. package/dist/routes/shared-world/result.d.ts +18 -0
  533. package/dist/routes/shared-world/result.js +327 -0
  534. package/dist/routes/shared-world/route.d.ts +21 -0
  535. package/dist/routes/shared-world/route.js +277 -0
  536. package/dist/routes/shared-world/seat-records.d.ts +20 -0
  537. package/dist/routes/shared-world/seat-records.js +213 -0
  538. package/dist/routes/shared-world/seats.d.ts +38 -0
  539. package/dist/routes/shared-world/seats.js +208 -0
  540. package/dist/routes/shared-world/setup.d.ts +48 -0
  541. package/dist/routes/shared-world/setup.js +200 -0
  542. package/dist/routes/shared-world/types.d.ts +354 -0
  543. package/dist/routes/shared-world/types.js +29 -0
  544. package/dist/routes/terminal/adapter.d.ts +37 -0
  545. package/dist/routes/terminal/adapter.js +201 -0
  546. package/dist/routes/terminal/bundle.d.ts +79 -0
  547. package/dist/routes/terminal/bundle.js +305 -0
  548. package/dist/routes/terminal/credentials.d.ts +34 -0
  549. package/dist/routes/terminal/credentials.js +102 -0
  550. package/dist/routes/terminal/dry-run.d.ts +24 -0
  551. package/dist/routes/terminal/dry-run.js +157 -0
  552. package/dist/routes/terminal/encoding.js +79 -0
  553. package/dist/routes/terminal/ledger.d.ts +49 -0
  554. package/dist/routes/terminal/ledger.js +170 -0
  555. package/dist/routes/terminal/lifetime.d.ts +10 -0
  556. package/dist/routes/terminal/lifetime.js +22 -0
  557. package/dist/routes/terminal/live-finish.d.ts +31 -0
  558. package/dist/routes/terminal/live-finish.js +215 -0
  559. package/dist/routes/terminal/live-sandbox.d.ts +70 -0
  560. package/dist/routes/terminal/live-sandbox.js +456 -0
  561. package/dist/routes/terminal/persona.d.ts +20 -0
  562. package/dist/routes/terminal/persona.js +34 -0
  563. package/dist/routes/terminal/plan.d.ts +21 -0
  564. package/dist/routes/terminal/plan.js +115 -0
  565. package/dist/routes/terminal/recorder.d.ts +23 -0
  566. package/dist/routes/terminal/recorder.js +77 -0
  567. package/dist/routes/terminal/result.d.ts +47 -0
  568. package/dist/routes/terminal/result.js +114 -0
  569. package/dist/routes/terminal/route.d.ts +23 -0
  570. package/dist/routes/terminal/route.js +170 -0
  571. package/dist/routes/terminal/runtime-auth.d.ts +12 -0
  572. package/dist/routes/terminal/runtime-auth.js +23 -0
  573. package/dist/routes/terminal/runtime.d.ts +14 -0
  574. package/dist/routes/terminal/runtime.js +47 -0
  575. package/dist/routes/terminal/sandbox.d.ts +37 -0
  576. package/dist/routes/terminal/sandbox.js +123 -0
  577. package/dist/routes/terminal/session.d.ts +25 -0
  578. package/dist/routes/terminal/session.js +199 -0
  579. package/dist/routes/terminal/token-usage.d.ts +15 -0
  580. package/dist/routes/terminal/token-usage.js +93 -0
  581. package/dist/routes/terminal/trace.d.ts +40 -0
  582. package/dist/routes/terminal/trace.js +172 -0
  583. package/dist/routes/terminal/types.d.ts +342 -0
  584. package/dist/routes/terminal/types.js +31 -0
  585. package/dist/run/bundle-shape.d.ts +7 -0
  586. package/dist/run/bundle-shape.js +369 -0
  587. package/dist/run/bundle.d.ts +547 -0
  588. package/dist/run/bundle.js +44 -0
  589. package/dist/run/concurrency.js +24 -0
  590. package/dist/run/contained-output.d.ts +50 -0
  591. package/dist/run/contained-output.js +351 -0
  592. package/dist/run/cost-summary.d.ts +28 -0
  593. package/dist/run/cost-summary.js +213 -0
  594. package/dist/run/costs.js +140 -0
  595. package/dist/run/detail.d.ts +62 -0
  596. package/dist/run/detail.js +139 -0
  597. package/dist/run/dry-run-inputs.d.ts +9 -0
  598. package/dist/run/dry-run-inputs.js +90 -0
  599. package/dist/run/dry-run.d.ts +6 -0
  600. package/dist/run/dry-run.js +362 -0
  601. package/dist/run/feedback-shape.d.ts +7 -0
  602. package/dist/run/feedback-shape.js +89 -0
  603. package/dist/run/git-state.d.ts +35 -0
  604. package/dist/run/git-state.js +316 -0
  605. package/dist/run/git-workspace.d.ts +32 -0
  606. package/dist/run/git-workspace.js +450 -0
  607. package/dist/run/judge.d.ts +210 -0
  608. package/dist/run/judge.js +240 -0
  609. package/dist/run/locate.d.ts +22 -0
  610. package/dist/run/locate.js +122 -0
  611. package/dist/run/outcomes.d.ts +31 -0
  612. package/dist/run/outcomes.js +210 -0
  613. package/dist/run/participant-records.d.ts +28 -0
  614. package/dist/run/participant-records.js +63 -0
  615. package/dist/run/participant.d.ts +29 -0
  616. package/dist/run/participant.js +12 -0
  617. package/dist/run/paths.d.ts +79 -0
  618. package/dist/run/paths.js +356 -0
  619. package/dist/run/preflight-receipts.d.ts +46 -0
  620. package/dist/run/preflight-receipts.js +173 -0
  621. package/dist/run/pricing.d.ts +124 -0
  622. package/dist/run/pricing.js +298 -0
  623. package/dist/run/project.d.ts +8 -0
  624. package/dist/run/project.js +103 -0
  625. package/dist/run/projection.d.ts +184 -0
  626. package/dist/run/projection.js +356 -0
  627. package/dist/run/reclaim.d.ts +41 -0
  628. package/dist/run/reclaim.js +193 -0
  629. package/dist/run/results.d.ts +88 -0
  630. package/dist/run/results.js +3 -0
  631. package/dist/run/run-index.d.ts +107 -0
  632. package/dist/run/run-index.js +243 -0
  633. package/dist/run/run.d.ts +103 -0
  634. package/dist/run/run.js +199 -0
  635. package/dist/run/sandbox-receipts.d.ts +31 -0
  636. package/dist/run/sandbox-receipts.js +49 -0
  637. package/dist/run/shared-world-evidence.d.ts +220 -0
  638. package/dist/run/shared-world-evidence.js +49 -0
  639. package/dist/run/shared-world-shape.d.ts +26 -0
  640. package/dist/run/shared-world-shape.js +181 -0
  641. package/dist/run/stats.d.ts +82 -0
  642. package/dist/run/stats.js +258 -0
  643. package/dist/run/status.d.ts +111 -0
  644. package/dist/run/status.js +176 -0
  645. package/dist/run/stored-runs.d.ts +38 -0
  646. package/dist/run/stored-runs.js +273 -0
  647. package/dist/run/stream-shape.d.ts +4 -0
  648. package/dist/run/stream-shape.js +160 -0
  649. package/dist/run/streams.d.ts +257 -0
  650. package/dist/run/streams.js +12 -0
  651. package/dist/run/study-files.d.ts +35 -0
  652. package/dist/run/study-files.js +164 -0
  653. package/dist/run/terminal-contract.d.ts +25 -0
  654. package/dist/run/terminal-contract.js +47 -0
  655. package/dist/run/text.d.ts +2 -0
  656. package/dist/run/text.js +4 -0
  657. package/dist/run/transient-comms-secrets.js +72 -0
  658. package/dist/run/type-guards.d.ts +6 -0
  659. package/dist/run/type-guards.js +18 -0
  660. package/dist/run-lab.d.ts +110 -0
  661. package/dist/run-lab.js +148 -0
  662. package/dist/subject/clone.d.ts +37 -0
  663. package/dist/subject/clone.js +62 -0
  664. package/dist/subject/desktop-cli.d.ts +17 -0
  665. package/dist/subject/desktop-cli.js +51 -0
  666. package/dist/subject/local-tree-archive.d.ts +82 -0
  667. package/dist/subject/local-tree-archive.js +431 -0
  668. package/dist/subject/local-tree.d.ts +28 -0
  669. package/dist/subject/local-tree.js +58 -0
  670. package/dist/subject/node-bootstrap.d.ts +12 -0
  671. package/dist/subject/node-bootstrap.js +66 -0
  672. package/dist/subject/runtime.d.ts +13 -0
  673. package/dist/subject/runtime.js +50 -0
  674. package/dist/subject/serve.d.ts +39 -0
  675. package/dist/subject/serve.js +249 -0
  676. package/dist/subject/state.d.ts +23 -0
  677. package/dist/subject/state.js +58 -0
  678. package/dist/subject/steps.d.ts +37 -0
  679. package/dist/subject/steps.js +39 -0
  680. package/dist/substrates/command-failure.d.ts +23 -0
  681. package/dist/substrates/command-failure.js +45 -0
  682. package/dist/substrates/desktop-session.d.ts +43 -0
  683. package/dist/substrates/desktop-session.js +59 -0
  684. package/dist/substrates/detached.d.ts +55 -0
  685. package/dist/substrates/detached.js +139 -0
  686. package/dist/substrates/e2b/cdp-probe.d.ts +99 -0
  687. package/dist/substrates/e2b/cdp-probe.js +556 -0
  688. package/dist/substrates/e2b/desktop-browser.d.ts +81 -0
  689. package/dist/substrates/e2b/desktop-browser.js +410 -0
  690. package/dist/substrates/e2b/desktop-cdp.d.ts +77 -0
  691. package/dist/substrates/e2b/desktop-cdp.js +211 -0
  692. package/dist/substrates/e2b/desktop-executor.d.ts +92 -0
  693. package/dist/substrates/e2b/desktop-executor.js +302 -0
  694. package/dist/substrates/e2b/desktop-geometry.d.ts +83 -0
  695. package/dist/substrates/e2b/desktop-geometry.js +331 -0
  696. package/dist/substrates/e2b/desktop-media.d.ts +29 -0
  697. package/dist/substrates/e2b/desktop-media.js +151 -0
  698. package/dist/substrates/e2b/desktop-recording.d.ts +18 -0
  699. package/dist/substrates/e2b/desktop-recording.js +238 -0
  700. package/dist/substrates/e2b/desktop-resources.d.ts +11 -0
  701. package/dist/substrates/e2b/desktop-resources.js +35 -0
  702. package/dist/substrates/e2b/desktop-screenshot-cleanup.d.ts +15 -0
  703. package/dist/substrates/e2b/desktop-screenshot-cleanup.js +66 -0
  704. package/dist/substrates/e2b/lifetime.d.ts +12 -0
  705. package/dist/substrates/e2b/lifetime.js +18 -0
  706. package/dist/substrates/e2b/sandbox.d.ts +104 -0
  707. package/dist/substrates/e2b/sandbox.js +216 -0
  708. package/dist/substrates/e2b/sdk.d.ts +205 -0
  709. package/dist/substrates/e2b/sdk.js +189 -0
  710. package/dist/substrates/e2b/shell.d.ts +10 -0
  711. package/dist/substrates/e2b/shell.js +61 -0
  712. package/dist/substrates/local/captured-inbox.d.ts +6 -0
  713. package/dist/substrates/local/captured-inbox.js +87 -0
  714. package/dist/substrates/local/firecracker-desktop.d.ts +25 -0
  715. package/dist/substrates/local/firecracker-desktop.js +315 -0
  716. package/dist/substrates/local/runtime-config.d.ts +6 -0
  717. package/dist/substrates/local/runtime-config.js +91 -0
  718. package/dist/substrates/local/runtime-host.d.ts +30 -0
  719. package/dist/substrates/local/runtime-host.js +143 -0
  720. package/dist/substrates/local/runtime-release.d.ts +5 -0
  721. package/dist/substrates/local/runtime-release.js +32 -0
  722. package/dist/substrates/local/runtime-ssh.d.ts +9 -0
  723. package/dist/substrates/local/runtime-ssh.js +129 -0
  724. package/dist/substrates/local/runtime.d.ts +25 -0
  725. package/dist/substrates/local/runtime.js +190 -0
  726. package/dist/substrates/shell.d.ts +44 -0
  727. package/dist/substrates/shell.js +22 -0
  728. package/dist/tui/actions.d.ts +39 -0
  729. package/dist/tui/actions.js +203 -0
  730. package/dist/tui/contract.d.ts +144 -0
  731. package/dist/tui/contract.js +52 -0
  732. package/dist/tui/launch.js +172 -0
  733. package/dist/tui/project.d.ts +10 -0
  734. package/dist/tui/project.js +17 -0
  735. package/dist/tui-app.js +125 -125
  736. package/dist/verify/actor.d.ts +27 -0
  737. package/dist/verify/actor.js +292 -0
  738. package/dist/verify/artifacts.d.ts +47 -0
  739. package/dist/verify/artifacts.js +373 -0
  740. package/dist/verify/costs.d.ts +30 -0
  741. package/dist/verify/costs.js +129 -0
  742. package/dist/verify/liveness.d.ts +7 -0
  743. package/dist/verify/liveness.js +72 -0
  744. package/dist/verify/rerun.d.ts +3 -0
  745. package/dist/verify/rerun.js +46 -0
  746. package/dist/verify/shared-world-concurrent.d.ts +11 -0
  747. package/dist/verify/shared-world-concurrent.js +346 -0
  748. package/dist/verify/shared-world.d.ts +8 -0
  749. package/dist/verify/shared-world.js +295 -0
  750. package/dist/verify/subject.d.ts +17 -0
  751. package/dist/verify/subject.js +123 -0
  752. package/dist/verify/verify.d.ts +34 -0
  753. package/dist/verify/verify.js +333 -0
  754. package/docs/README.md +67 -0
  755. package/docs/architecture/actor-contract.md +215 -174
  756. package/docs/architecture/browser-control.md +51 -37
  757. package/docs/architecture/comms-inbox.md +8 -8
  758. package/docs/architecture/desktop-recording.md +2 -2
  759. package/docs/architecture/desktop-sessions.md +33 -21
  760. package/docs/architecture/external-public-shared-world.md +80 -64
  761. package/docs/architecture/github-feedback-loop.md +40 -27
  762. package/docs/architecture/guest-desktop.md +22 -10
  763. package/docs/architecture/local-browser-runtime.md +30 -21
  764. package/docs/architecture/observer-review.md +9 -9
  765. package/docs/architecture/observer.md +53 -57
  766. package/docs/architecture/participant-media.md +3 -3
  767. package/docs/architecture/project-layout.md +33 -137
  768. package/docs/architecture/real-email-receiving.md +7 -6
  769. package/docs/architecture/restricted-codex-analysis.md +213 -25
  770. package/docs/architecture/serve.md +89 -87
  771. package/docs/architecture/state-driven-executor.md +79 -64
  772. package/docs/architecture/task-protocol-support.md +18 -18
  773. package/docs/architecture/terminal-product-lane.md +92 -86
  774. package/docs/contracts/adapter-admission.md +8 -4
  775. package/docs/contracts/adapter-fixtures.md +6 -6
  776. package/docs/contracts/core.md +22 -24
  777. package/docs/contracts/feedback.md +33 -23
  778. package/docs/contracts/policy.md +28 -31
  779. package/docs/contracts/run-bundle.md +168 -82
  780. package/docs/contracts/schemas.md +563 -379
  781. package/docs/contracts/study-analysis.md +2 -2
  782. package/docs/contracts/study-costs.md +12 -12
  783. package/docs/decisions/0001-run-bundle-is-the-source-of-truth.md +36 -0
  784. package/docs/decisions/0002-observer-is-one-self-contained-file.md +30 -0
  785. package/docs/decisions/0003-contained-paths-and-cleanup-authority.md +35 -0
  786. package/docs/decisions/0004-participant-desktops-stay-in-the-route.md +31 -0
  787. package/docs/decisions/README.md +16 -0
  788. package/docs/goals/current.md +22 -50
  789. package/docs/principles/actor-fidelity.md +37 -40
  790. package/docs/principles/engineering.md +81 -0
  791. package/docs/principles/invariants-and-defaults.md +48 -45
  792. package/docs/principles/self-driving-harness.md +11 -10
  793. package/docs/principles/three-roles.md +12 -13
  794. package/docs/product/automatic-analysis.md +39 -9
  795. package/docs/product/lobby-trivia-3player-external-public.md +14 -13
  796. package/docs/product/open-source-install-experience.md +34 -38
  797. package/docs/ramp/README.md +91 -218
  798. package/docs/release/open-source-readiness.md +5 -2
  799. package/docs/release/public-readiness-standard.md +1 -1
  800. package/examples/README.md +11 -0
  801. package/examples/participant/README.md +80 -0
  802. package/examples/participant/app.mjs +55 -0
  803. package/examples/participant/run.mjs +116 -0
  804. package/examples/scorer/README.md +29 -0
  805. package/examples/scorer/run.mjs +63 -0
  806. package/examples/scorer/scorer.mjs +20 -0
  807. package/package.json +61 -57
  808. package/skills/humanish/SKILL.md +72 -60
  809. package/skills/humanish/agents/openai.yaml +2 -2
  810. package/dist/actor-contract.d.ts +0 -296
  811. package/dist/actor-contract.js +0 -340
  812. package/dist/actor-contract.js.map +0 -1
  813. package/dist/actor-goal-source.js +0 -32
  814. package/dist/actor-goal-source.js.map +0 -1
  815. package/dist/actor-registry.d.ts +0 -77
  816. package/dist/actor-registry.js +0 -106
  817. package/dist/actor-registry.js.map +0 -1
  818. package/dist/actor-stop-cause.d.ts +0 -9
  819. package/dist/actor-stop-cause.js +0 -42
  820. package/dist/actor-stop-cause.js.map +0 -1
  821. package/dist/adapter-extension.d.ts +0 -76
  822. package/dist/adapter-extension.js +0 -273
  823. package/dist/adapter-extension.js.map +0 -1
  824. package/dist/adapter-scorer-loader.d.ts +0 -42
  825. package/dist/adapter-scorer-loader.js +0 -129
  826. package/dist/adapter-scorer-loader.js.map +0 -1
  827. package/dist/affordance.d.ts +0 -53
  828. package/dist/affordance.js +0 -145
  829. package/dist/affordance.js.map +0 -1
  830. package/dist/agent-session.js +0 -43
  831. package/dist/agent-session.js.map +0 -1
  832. package/dist/argv.d.ts +0 -1
  833. package/dist/argv.js +0 -8
  834. package/dist/argv.js.map +0 -1
  835. package/dist/artifact-reference.d.ts +0 -22
  836. package/dist/artifact-reference.js +0 -47
  837. package/dist/artifact-reference.js.map +0 -1
  838. package/dist/automatic-analysis-completion.d.ts +0 -25
  839. package/dist/automatic-analysis-completion.js +0 -56
  840. package/dist/automatic-analysis-completion.js.map +0 -1
  841. package/dist/automatic-analysis-config.d.ts +0 -38
  842. package/dist/automatic-analysis-config.js +0 -68
  843. package/dist/automatic-analysis-config.js.map +0 -1
  844. package/dist/automatic-study-analysis.d.ts +0 -17
  845. package/dist/automatic-study-analysis.js +0 -195
  846. package/dist/automatic-study-analysis.js.map +0 -1
  847. package/dist/browser-control-client.d.ts +0 -18
  848. package/dist/browser-control-client.js +0 -176
  849. package/dist/browser-control-client.js.map +0 -1
  850. package/dist/browser-control-dispatcher.d.ts +0 -19
  851. package/dist/browser-control-dispatcher.js +0 -147
  852. package/dist/browser-control-dispatcher.js.map +0 -1
  853. package/dist/browser-control-protocol.d.ts +0 -472
  854. package/dist/browser-control-protocol.js +0 -185
  855. package/dist/browser-control-protocol.js.map +0 -1
  856. package/dist/browser-control-recording-transfer.d.ts +0 -4
  857. package/dist/browser-control-recording-transfer.js +0 -42
  858. package/dist/browser-control-recording-transfer.js.map +0 -1
  859. package/dist/browser-control-transport.d.ts +0 -27
  860. package/dist/browser-control-transport.js +0 -177
  861. package/dist/browser-control-transport.js.map +0 -1
  862. package/dist/browser-evidence-hygiene.js +0 -43
  863. package/dist/browser-evidence-hygiene.js.map +0 -1
  864. package/dist/chrome-cdp-probe.d.ts +0 -97
  865. package/dist/chrome-cdp-probe.js +0 -536
  866. package/dist/chrome-cdp-probe.js.map +0 -1
  867. package/dist/claude-agent-sdk.d.ts +0 -94
  868. package/dist/claude-agent-sdk.js +0 -367
  869. package/dist/claude-agent-sdk.js.map +0 -1
  870. package/dist/cli.js.map +0 -1
  871. package/dist/codex-app-server-ui.d.ts +0 -39
  872. package/dist/codex-app-server-ui.js +0 -357
  873. package/dist/codex-app-server-ui.js.map +0 -1
  874. package/dist/codex-app-server.d.ts +0 -144
  875. package/dist/codex-app-server.js +0 -754
  876. package/dist/codex-app-server.js.map +0 -1
  877. package/dist/command-failure.d.ts +0 -43
  878. package/dist/command-failure.js +0 -73
  879. package/dist/command-failure.js.map +0 -1
  880. package/dist/comms-agentmail.d.ts +0 -25
  881. package/dist/comms-agentmail.js +0 -418
  882. package/dist/comms-agentmail.js.map +0 -1
  883. package/dist/comms-catch-host.d.ts +0 -52
  884. package/dist/comms-catch-host.js +0 -178
  885. package/dist/comms-catch-host.js.map +0 -1
  886. package/dist/comms-connections.d.ts +0 -52
  887. package/dist/comms-connections.js +0 -123
  888. package/dist/comms-connections.js.map +0 -1
  889. package/dist/comms-email-catch.d.ts +0 -54
  890. package/dist/comms-email-catch.js +0 -212
  891. package/dist/comms-email-catch.js.map +0 -1
  892. package/dist/comms-evidence.d.ts +0 -27
  893. package/dist/comms-evidence.js +0 -31
  894. package/dist/comms-evidence.js.map +0 -1
  895. package/dist/comms-fake-inbox.d.ts +0 -45
  896. package/dist/comms-fake-inbox.js +0 -184
  897. package/dist/comms-fake-inbox.js.map +0 -1
  898. package/dist/comms-images.d.ts +0 -7
  899. package/dist/comms-images.js +0 -39
  900. package/dist/comms-images.js.map +0 -1
  901. package/dist/comms-inbox.d.ts +0 -66
  902. package/dist/comms-inbox.js +0 -389
  903. package/dist/comms-inbox.js.map +0 -1
  904. package/dist/comms-lease-store.d.ts +0 -91
  905. package/dist/comms-lease-store.js +0 -495
  906. package/dist/comms-lease-store.js.map +0 -1
  907. package/dist/comms-receiving-evidence.d.ts +0 -46
  908. package/dist/comms-receiving-evidence.js +0 -35
  909. package/dist/comms-receiving-evidence.js.map +0 -1
  910. package/dist/comms-receiving-inbox.d.ts +0 -16
  911. package/dist/comms-receiving-inbox.js +0 -410
  912. package/dist/comms-receiving-inbox.js.map +0 -1
  913. package/dist/comms-receiving-runtime.d.ts +0 -25
  914. package/dist/comms-receiving-runtime.js +0 -45
  915. package/dist/comms-receiving-runtime.js.map +0 -1
  916. package/dist/comms-receiving-types.d.ts +0 -98
  917. package/dist/comms-receiving-types.js +0 -2
  918. package/dist/comms-receiving-types.js.map +0 -1
  919. package/dist/comms-receiving.d.ts +0 -58
  920. package/dist/comms-receiving.js +0 -554
  921. package/dist/comms-receiving.js.map +0 -1
  922. package/dist/comms-sandbox-catch.d.ts +0 -229
  923. package/dist/comms-sandbox-catch.js +0 -737
  924. package/dist/comms-sandbox-catch.js.map +0 -1
  925. package/dist/comms-setup.d.ts +0 -40
  926. package/dist/comms-setup.js +0 -121
  927. package/dist/comms-setup.js.map +0 -1
  928. package/dist/comms-types.d.ts +0 -72
  929. package/dist/comms-types.js +0 -10
  930. package/dist/comms-types.js.map +0 -1
  931. package/dist/computer-use-actor.d.ts +0 -76
  932. package/dist/computer-use-actor.js +0 -68
  933. package/dist/computer-use-actor.js.map +0 -1
  934. package/dist/computer-use.d.ts +0 -429
  935. package/dist/computer-use.js +0 -1751
  936. package/dist/computer-use.js.map +0 -1
  937. package/dist/concurrency.js +0 -27
  938. package/dist/concurrency.js.map +0 -1
  939. package/dist/concurrent-shared-world-lab.d.ts +0 -186
  940. package/dist/concurrent-shared-world-lab.js +0 -2181
  941. package/dist/concurrent-shared-world-lab.js.map +0 -1
  942. package/dist/core/git-state.d.ts +0 -33
  943. package/dist/core/git-state.js +0 -277
  944. package/dist/core/git-state.js.map +0 -1
  945. package/dist/core/git-workspace.d.ts +0 -31
  946. package/dist/core/git-workspace.js +0 -447
  947. package/dist/core/git-workspace.js.map +0 -1
  948. package/dist/core/index.d.ts +0 -4
  949. package/dist/core/index.js +0 -3
  950. package/dist/core/index.js.map +0 -1
  951. package/dist/core/run-primitives.d.ts +0 -66
  952. package/dist/core/run-primitives.js +0 -120
  953. package/dist/core/run-primitives.js.map +0 -1
  954. package/dist/cua-actor-lab.d.ts +0 -855
  955. package/dist/cua-actor-lab.js +0 -3543
  956. package/dist/cua-actor-lab.js.map +0 -1
  957. package/dist/cua-admission-limit.d.ts +0 -12
  958. package/dist/cua-admission-limit.js +0 -20
  959. package/dist/cua-admission-limit.js.map +0 -1
  960. package/dist/cua-desktop-lane.d.ts +0 -35
  961. package/dist/cua-desktop-lane.js +0 -13
  962. package/dist/cua-desktop-lane.js.map +0 -1
  963. package/dist/cua-diagnostics.d.ts +0 -38
  964. package/dist/cua-diagnostics.js +0 -68
  965. package/dist/cua-diagnostics.js.map +0 -1
  966. package/dist/cua-executor-error.js +0 -48
  967. package/dist/cua-executor-error.js.map +0 -1
  968. package/dist/cua-provider-error.d.ts +0 -16
  969. package/dist/cua-provider-error.js +0 -35
  970. package/dist/cua-provider-error.js.map +0 -1
  971. package/dist/cua-speech.js +0 -2
  972. package/dist/cua-speech.js.map +0 -1
  973. package/dist/desktop-recorder.d.ts +0 -39
  974. package/dist/desktop-recorder.js +0 -226
  975. package/dist/desktop-recorder.js.map +0 -1
  976. package/dist/desktop-recording-artifact.d.ts +0 -5
  977. package/dist/desktop-recording-artifact.js +0 -54
  978. package/dist/desktop-recording-artifact.js.map +0 -1
  979. package/dist/desktop-recording-types.d.ts +0 -24
  980. package/dist/desktop-recording-types.js +0 -14
  981. package/dist/desktop-recording-types.js.map +0 -1
  982. package/dist/desktop-session.d.ts +0 -41
  983. package/dist/desktop-session.js +0 -47
  984. package/dist/desktop-session.js.map +0 -1
  985. package/dist/device-presets.d.ts +0 -67
  986. package/dist/device-presets.js +0 -55
  987. package/dist/device-presets.js.map +0 -1
  988. package/dist/doctor-lab.d.ts +0 -38
  989. package/dist/doctor-lab.js +0 -126
  990. package/dist/doctor-lab.js.map +0 -1
  991. package/dist/e2b-cua-desktop.d.ts +0 -3
  992. package/dist/e2b-cua-desktop.js +0 -708
  993. package/dist/e2b-cua-desktop.js.map +0 -1
  994. package/dist/e2b-cua-provisioning.d.ts +0 -314
  995. package/dist/e2b-cua-provisioning.js +0 -1219
  996. package/dist/e2b-cua-provisioning.js.map +0 -1
  997. package/dist/e2b-desktop-executor.d.ts +0 -101
  998. package/dist/e2b-desktop-executor.js +0 -297
  999. package/dist/e2b-desktop-executor.js.map +0 -1
  1000. package/dist/e2b-desktop-launch.d.ts +0 -243
  1001. package/dist/e2b-desktop-launch.js +0 -234
  1002. package/dist/e2b-desktop-launch.js.map +0 -1
  1003. package/dist/e2b-desktop-media.d.ts +0 -14
  1004. package/dist/e2b-desktop-media.js +0 -58
  1005. package/dist/e2b-desktop-media.js.map +0 -1
  1006. package/dist/e2b-desktop-recording.d.ts +0 -16
  1007. package/dist/e2b-desktop-recording.js +0 -162
  1008. package/dist/e2b-desktop-recording.js.map +0 -1
  1009. package/dist/e2b-desktop-resources.d.ts +0 -11
  1010. package/dist/e2b-desktop-resources.js +0 -33
  1011. package/dist/e2b-desktop-resources.js.map +0 -1
  1012. package/dist/e2b-desktop-screenshot-cleanup.d.ts +0 -15
  1013. package/dist/e2b-desktop-screenshot-cleanup.js +0 -67
  1014. package/dist/e2b-desktop-screenshot-cleanup.js.map +0 -1
  1015. package/dist/e2b-desktop-session.d.ts +0 -7
  1016. package/dist/e2b-desktop-session.js +0 -29
  1017. package/dist/e2b-desktop-session.js.map +0 -1
  1018. package/dist/e2b-detached.d.ts +0 -53
  1019. package/dist/e2b-detached.js +0 -130
  1020. package/dist/e2b-detached.js.map +0 -1
  1021. package/dist/e2b-terminal-lab.d.ts +0 -383
  1022. package/dist/e2b-terminal-lab.js +0 -2000
  1023. package/dist/e2b-terminal-lab.js.map +0 -1
  1024. package/dist/env-file.d.ts +0 -14
  1025. package/dist/env-file.js +0 -108
  1026. package/dist/env-file.js.map +0 -1
  1027. package/dist/export-bundle.d.ts +0 -7
  1028. package/dist/export-bundle.js +0 -441
  1029. package/dist/export-bundle.js.map +0 -1
  1030. package/dist/export.d.ts +0 -49
  1031. package/dist/export.js +0 -308
  1032. package/dist/export.js.map +0 -1
  1033. package/dist/feedback-proof.d.ts +0 -8
  1034. package/dist/feedback-proof.js +0 -44
  1035. package/dist/feedback-proof.js.map +0 -1
  1036. package/dist/feedback.d.ts +0 -76
  1037. package/dist/feedback.js +0 -496
  1038. package/dist/feedback.js.map +0 -1
  1039. package/dist/first-run-path.d.ts +0 -55
  1040. package/dist/first-run-path.js +0 -136
  1041. package/dist/first-run-path.js.map +0 -1
  1042. package/dist/frame-signature.d.ts +0 -24
  1043. package/dist/frame-signature.js +0 -128
  1044. package/dist/frame-signature.js.map +0 -1
  1045. package/dist/guest-bootstrap.d.ts +0 -55
  1046. package/dist/guest-bootstrap.js +0 -285
  1047. package/dist/guest-bootstrap.js.map +0 -1
  1048. package/dist/guest-browser-tools.d.ts +0 -8
  1049. package/dist/guest-browser-tools.js +0 -66
  1050. package/dist/guest-browser-tools.js.map +0 -1
  1051. package/dist/guest-chromium-text.d.ts +0 -27
  1052. package/dist/guest-chromium-text.js +0 -281
  1053. package/dist/guest-chromium-text.js.map +0 -1
  1054. package/dist/guest-desktop-executor.d.ts +0 -22
  1055. package/dist/guest-desktop-executor.js +0 -178
  1056. package/dist/guest-desktop-executor.js.map +0 -1
  1057. package/dist/guest-desktop-media.d.ts +0 -33
  1058. package/dist/guest-desktop-media.js +0 -281
  1059. package/dist/guest-desktop-media.js.map +0 -1
  1060. package/dist/guest-desktop-native.d.ts +0 -14
  1061. package/dist/guest-desktop-native.js +0 -131
  1062. package/dist/guest-desktop-native.js.map +0 -1
  1063. package/dist/guest-media-config.d.ts +0 -15
  1064. package/dist/guest-media-config.js +0 -8
  1065. package/dist/guest-media-config.js.map +0 -1
  1066. package/dist/guest-media-worker.js.map +0 -1
  1067. package/dist/guest-runtime-desktop.d.ts +0 -42
  1068. package/dist/guest-runtime-desktop.js +0 -314
  1069. package/dist/guest-runtime-desktop.js.map +0 -1
  1070. package/dist/guest-runtime-main.js.map +0 -1
  1071. package/dist/guest-runtime-revision.js.map +0 -1
  1072. package/dist/guest-runtime.d.ts +0 -31
  1073. package/dist/guest-runtime.js +0 -99
  1074. package/dist/guest-runtime.js.map +0 -1
  1075. package/dist/image-evidence.d.ts +0 -2
  1076. package/dist/image-evidence.js +0 -47
  1077. package/dist/image-evidence.js.map +0 -1
  1078. package/dist/index.js.map +0 -1
  1079. package/dist/init-templates.d.ts +0 -25
  1080. package/dist/init-templates.js +0 -541
  1081. package/dist/init-templates.js.map +0 -1
  1082. package/dist/init.d.ts +0 -38
  1083. package/dist/init.js +0 -532
  1084. package/dist/init.js.map +0 -1
  1085. package/dist/key-resolution.d.ts +0 -67
  1086. package/dist/key-resolution.js +0 -375
  1087. package/dist/key-resolution.js.map +0 -1
  1088. package/dist/lab-app-runner.d.ts +0 -78
  1089. package/dist/lab-app-runner.js +0 -403
  1090. package/dist/lab-app-runner.js.map +0 -1
  1091. package/dist/lab-config.d.ts +0 -968
  1092. package/dist/lab-config.js +0 -2714
  1093. package/dist/lab-config.js.map +0 -1
  1094. package/dist/lab-engine.d.ts +0 -97
  1095. package/dist/lab-engine.js +0 -285
  1096. package/dist/lab-engine.js.map +0 -1
  1097. package/dist/lab-preflight.d.ts +0 -70
  1098. package/dist/lab-preflight.js +0 -388
  1099. package/dist/lab-preflight.js.map +0 -1
  1100. package/dist/lab-summary.d.ts +0 -57
  1101. package/dist/lab-summary.js +0 -126
  1102. package/dist/lab-summary.js.map +0 -1
  1103. package/dist/labs.d.ts +0 -66
  1104. package/dist/labs.js +0 -401
  1105. package/dist/labs.js.map +0 -1
  1106. package/dist/listen.js +0 -84
  1107. package/dist/listen.js.map +0 -1
  1108. package/dist/local-agent-claude-session.d.ts +0 -45
  1109. package/dist/local-agent-claude-session.js +0 -203
  1110. package/dist/local-agent-claude-session.js.map +0 -1
  1111. package/dist/local-agent-cli.d.ts +0 -121
  1112. package/dist/local-agent-cli.js +0 -427
  1113. package/dist/local-agent-cli.js.map +0 -1
  1114. package/dist/local-captured-inbox.d.ts +0 -6
  1115. package/dist/local-captured-inbox.js +0 -65
  1116. package/dist/local-captured-inbox.js.map +0 -1
  1117. package/dist/local-firecracker-desktop.d.ts +0 -23
  1118. package/dist/local-firecracker-desktop.js +0 -191
  1119. package/dist/local-firecracker-desktop.js.map +0 -1
  1120. package/dist/local-firecracker-study.d.ts +0 -9
  1121. package/dist/local-firecracker-study.js +0 -135
  1122. package/dist/local-firecracker-study.js.map +0 -1
  1123. package/dist/local-runtime-config.d.ts +0 -6
  1124. package/dist/local-runtime-config.js +0 -60
  1125. package/dist/local-runtime-config.js.map +0 -1
  1126. package/dist/local-runtime-host.d.ts +0 -31
  1127. package/dist/local-runtime-host.js +0 -128
  1128. package/dist/local-runtime-host.js.map +0 -1
  1129. package/dist/local-runtime-release.d.ts +0 -7
  1130. package/dist/local-runtime-release.js +0 -33
  1131. package/dist/local-runtime-release.js.map +0 -1
  1132. package/dist/local-runtime-ssh.d.ts +0 -27
  1133. package/dist/local-runtime-ssh.js +0 -91
  1134. package/dist/local-runtime-ssh.js.map +0 -1
  1135. package/dist/local-runtime.d.ts +0 -25
  1136. package/dist/local-runtime.js +0 -133
  1137. package/dist/local-runtime.js.map +0 -1
  1138. package/dist/observer-data.d.ts +0 -114
  1139. package/dist/observer-data.js +0 -224
  1140. package/dist/observer-data.js.map +0 -1
  1141. package/dist/observer-library.js +0 -191
  1142. package/dist/observer-library.js.map +0 -1
  1143. package/dist/observer-serve.d.ts +0 -99
  1144. package/dist/observer-serve.js +0 -284
  1145. package/dist/observer-serve.js.map +0 -1
  1146. package/dist/observer-static.js +0 -357
  1147. package/dist/observer-static.js.map +0 -1
  1148. package/dist/observer.d.ts +0 -118
  1149. package/dist/observer.js +0 -947
  1150. package/dist/observer.js.map +0 -1
  1151. package/dist/openai-responses-cu.d.ts +0 -158
  1152. package/dist/openai-responses-cu.js +0 -714
  1153. package/dist/openai-responses-cu.js.map +0 -1
  1154. package/dist/orientation.d.ts +0 -29
  1155. package/dist/orientation.js +0 -95
  1156. package/dist/orientation.js.map +0 -1
  1157. package/dist/oss-lab.d.ts +0 -55
  1158. package/dist/oss-lab.js +0 -344
  1159. package/dist/oss-lab.js.map +0 -1
  1160. package/dist/oss-meta-lab-scoring.d.ts +0 -15
  1161. package/dist/oss-meta-lab-scoring.js +0 -145
  1162. package/dist/oss-meta-lab-scoring.js.map +0 -1
  1163. package/dist/oss-meta-lab.d.ts +0 -287
  1164. package/dist/oss-meta-lab.js +0 -4551
  1165. package/dist/oss-meta-lab.js.map +0 -1
  1166. package/dist/oss-remote-telemetry.d.ts +0 -77
  1167. package/dist/oss-remote-telemetry.js +0 -403
  1168. package/dist/oss-remote-telemetry.js.map +0 -1
  1169. package/dist/output-token-limit.js +0 -5
  1170. package/dist/output-token-limit.js.map +0 -1
  1171. package/dist/participant-assignment.d.ts +0 -12
  1172. package/dist/participant-assignment.js +0 -15
  1173. package/dist/participant-assignment.js.map +0 -1
  1174. package/dist/persona-resolve.d.ts +0 -44
  1175. package/dist/persona-resolve.js +0 -99
  1176. package/dist/persona-resolve.js.map +0 -1
  1177. package/dist/persona.d.ts +0 -39
  1178. package/dist/persona.js +0 -144
  1179. package/dist/persona.js.map +0 -1
  1180. package/dist/pi-agent-core.d.ts +0 -77
  1181. package/dist/pi-agent-core.js +0 -207
  1182. package/dist/pi-agent-core.js.map +0 -1
  1183. package/dist/pricing.d.ts +0 -145
  1184. package/dist/pricing.js +0 -277
  1185. package/dist/pricing.js.map +0 -1
  1186. package/dist/program.d.ts +0 -96
  1187. package/dist/program.js +0 -4030
  1188. package/dist/program.js.map +0 -1
  1189. package/dist/reasoning-effort.js +0 -26
  1190. package/dist/reasoning-effort.js.map +0 -1
  1191. package/dist/reclaim.d.ts +0 -31
  1192. package/dist/reclaim.js +0 -90
  1193. package/dist/reclaim.js.map +0 -1
  1194. package/dist/redaction.d.ts +0 -90
  1195. package/dist/redaction.js +0 -294
  1196. package/dist/redaction.js.map +0 -1
  1197. package/dist/restricted-codex-analysis.d.ts +0 -15
  1198. package/dist/restricted-codex-analysis.js +0 -13
  1199. package/dist/restricted-codex-analysis.js.map +0 -1
  1200. package/dist/restricted-codex-participant-policy.d.ts +0 -44
  1201. package/dist/restricted-codex-participant-policy.js +0 -43
  1202. package/dist/restricted-codex-participant-policy.js.map +0 -1
  1203. package/dist/restricted-codex-participant-run.d.ts +0 -20
  1204. package/dist/restricted-codex-participant-run.js +0 -79
  1205. package/dist/restricted-codex-participant-run.js.map +0 -1
  1206. package/dist/restricted-codex-participant.d.ts +0 -20
  1207. package/dist/restricted-codex-participant.js +0 -250
  1208. package/dist/restricted-codex-participant.js.map +0 -1
  1209. package/dist/restricted-codex-policy.d.ts +0 -64
  1210. package/dist/restricted-codex-policy.js +0 -176
  1211. package/dist/restricted-codex-policy.js.map +0 -1
  1212. package/dist/restricted-codex-session.d.ts +0 -44
  1213. package/dist/restricted-codex-session.js +0 -657
  1214. package/dist/restricted-codex-session.js.map +0 -1
  1215. package/dist/restricted-codex-transport.d.ts +0 -69
  1216. package/dist/restricted-codex-transport.js +0 -296
  1217. package/dist/restricted-codex-transport.js.map +0 -1
  1218. package/dist/run-detail.d.ts +0 -61
  1219. package/dist/run-detail.js +0 -137
  1220. package/dist/run-detail.js.map +0 -1
  1221. package/dist/run-index.d.ts +0 -71
  1222. package/dist/run-index.js +0 -206
  1223. package/dist/run-index.js.map +0 -1
  1224. package/dist/run-narration-secrets.js +0 -68
  1225. package/dist/run-narration-secrets.js.map +0 -1
  1226. package/dist/run-paths.d.ts +0 -56
  1227. package/dist/run-paths.js +0 -355
  1228. package/dist/run-paths.js.map +0 -1
  1229. package/dist/run-projection.d.ts +0 -186
  1230. package/dist/run-projection.js +0 -350
  1231. package/dist/run-projection.js.map +0 -1
  1232. package/dist/run-status.d.ts +0 -139
  1233. package/dist/run-status.js +0 -218
  1234. package/dist/run-status.js.map +0 -1
  1235. package/dist/run.d.ts +0 -1255
  1236. package/dist/run.js +0 -5949
  1237. package/dist/run.js.map +0 -1
  1238. package/dist/sandbox-receipts.d.ts +0 -19
  1239. package/dist/sandbox-receipts.js +0 -51
  1240. package/dist/sandbox-receipts.js.map +0 -1
  1241. package/dist/screenshot-image.d.ts +0 -11
  1242. package/dist/screenshot-image.js +0 -26
  1243. package/dist/screenshot-image.js.map +0 -1
  1244. package/dist/scripted-browser-actor.d.ts +0 -297
  1245. package/dist/scripted-browser-actor.js +0 -1399
  1246. package/dist/scripted-browser-actor.js.map +0 -1
  1247. package/dist/scripted-browser-lab.d.ts +0 -121
  1248. package/dist/scripted-browser-lab.js +0 -852
  1249. package/dist/scripted-browser-lab.js.map +0 -1
  1250. package/dist/secret-prompt.js +0 -36
  1251. package/dist/secret-prompt.js.map +0 -1
  1252. package/dist/selected-output-paths.d.ts +0 -52
  1253. package/dist/selected-output-paths.js +0 -366
  1254. package/dist/selected-output-paths.js.map +0 -1
  1255. package/dist/serve-exposure.d.ts +0 -62
  1256. package/dist/serve-exposure.js +0 -129
  1257. package/dist/serve-exposure.js.map +0 -1
  1258. package/dist/serve-http.js +0 -45
  1259. package/dist/serve-http.js.map +0 -1
  1260. package/dist/serve-tunnel.js +0 -113
  1261. package/dist/serve-tunnel.js.map +0 -1
  1262. package/dist/shared-world-lab.d.ts +0 -269
  1263. package/dist/shared-world-lab.js +0 -1424
  1264. package/dist/shared-world-lab.js.map +0 -1
  1265. package/dist/source-archive.d.ts +0 -82
  1266. package/dist/source-archive.js +0 -408
  1267. package/dist/source-archive.js.map +0 -1
  1268. package/dist/stats.d.ts +0 -82
  1269. package/dist/stats.js +0 -219
  1270. package/dist/stats.js.map +0 -1
  1271. package/dist/stop-conditions.d.ts +0 -39
  1272. package/dist/stop-conditions.js +0 -66
  1273. package/dist/stop-conditions.js.map +0 -1
  1274. package/dist/study-analysis-codex-config.d.ts +0 -11
  1275. package/dist/study-analysis-codex-config.js +0 -34
  1276. package/dist/study-analysis-codex-config.js.map +0 -1
  1277. package/dist/study-analysis-engine.d.ts +0 -47
  1278. package/dist/study-analysis-engine.js +0 -327
  1279. package/dist/study-analysis-engine.js.map +0 -1
  1280. package/dist/study-analysis-evidence.d.ts +0 -27
  1281. package/dist/study-analysis-evidence.js +0 -660
  1282. package/dist/study-analysis-evidence.js.map +0 -1
  1283. package/dist/study-analysis-job.d.ts +0 -97
  1284. package/dist/study-analysis-job.js +0 -214
  1285. package/dist/study-analysis-job.js.map +0 -1
  1286. package/dist/study-analysis-provider.d.ts +0 -40
  1287. package/dist/study-analysis-provider.js +0 -170
  1288. package/dist/study-analysis-provider.js.map +0 -1
  1289. package/dist/study-analysis-service.d.ts +0 -59
  1290. package/dist/study-analysis-service.js +0 -257
  1291. package/dist/study-analysis-service.js.map +0 -1
  1292. package/dist/study-analysis-sharing.d.ts +0 -11
  1293. package/dist/study-analysis-sharing.js +0 -35
  1294. package/dist/study-analysis-sharing.js.map +0 -1
  1295. package/dist/study-analysis-store.d.ts +0 -47
  1296. package/dist/study-analysis-store.js +0 -493
  1297. package/dist/study-analysis-store.js.map +0 -1
  1298. package/dist/study-analysis-validation.d.ts +0 -618
  1299. package/dist/study-analysis-validation.js +0 -384
  1300. package/dist/study-analysis-validation.js.map +0 -1
  1301. package/dist/study-analysis.d.ts +0 -206
  1302. package/dist/study-analysis.js +0 -4
  1303. package/dist/study-analysis.js.map +0 -1
  1304. package/dist/study-costs.js +0 -114
  1305. package/dist/study-costs.js.map +0 -1
  1306. package/dist/subject-runtime.d.ts +0 -23
  1307. package/dist/subject-runtime.js +0 -70
  1308. package/dist/subject-runtime.js.map +0 -1
  1309. package/dist/tasks.d.ts +0 -90
  1310. package/dist/tasks.js +0 -144
  1311. package/dist/tasks.js.map +0 -1
  1312. package/dist/telemetry.js +0 -324
  1313. package/dist/telemetry.js.map +0 -1
  1314. package/dist/terminal-agent-actor.d.ts +0 -41
  1315. package/dist/terminal-agent-actor.js +0 -25
  1316. package/dist/terminal-agent-actor.js.map +0 -1
  1317. package/dist/terminal-encoding.js +0 -80
  1318. package/dist/terminal-encoding.js.map +0 -1
  1319. package/dist/terminal-node-bootstrap.d.ts +0 -4
  1320. package/dist/terminal-node-bootstrap.js +0 -58
  1321. package/dist/terminal-node-bootstrap.js.map +0 -1
  1322. package/dist/terminal-participant-activity.d.ts +0 -6
  1323. package/dist/terminal-participant-activity.js +0 -34
  1324. package/dist/terminal-participant-activity.js.map +0 -1
  1325. package/dist/terminal-runtime-auth.d.ts +0 -13
  1326. package/dist/terminal-runtime-auth.js +0 -24
  1327. package/dist/terminal-runtime-auth.js.map +0 -1
  1328. package/dist/terminal-runtime.d.ts +0 -16
  1329. package/dist/terminal-runtime.js +0 -42
  1330. package/dist/terminal-runtime.js.map +0 -1
  1331. package/dist/terminal-token-usage.d.ts +0 -15
  1332. package/dist/terminal-token-usage.js +0 -94
  1333. package/dist/terminal-token-usage.js.map +0 -1
  1334. package/dist/tui-actions.d.ts +0 -39
  1335. package/dist/tui-actions.js +0 -178
  1336. package/dist/tui-actions.js.map +0 -1
  1337. package/dist/tui-contract.d.ts +0 -141
  1338. package/dist/tui-contract.js +0 -54
  1339. package/dist/tui-contract.js.map +0 -1
  1340. package/dist/tui-launch.js +0 -167
  1341. package/dist/tui-launch.js.map +0 -1
  1342. package/dist/tui-project.d.ts +0 -9
  1343. package/dist/tui-project.js +0 -18
  1344. package/dist/tui-project.js.map +0 -1
  1345. package/docs/architecture/examples/state-driven-local-app/README.md +0 -80
  1346. package/docs/architecture/examples/state-driven-local-app/app.mjs +0 -48
  1347. package/docs/architecture/examples/state-driven-local-app/runner.mjs +0 -105
  1348. package/docs/architecture/local-codex-tui-actor.md +0 -213
  1349. package/docs/architecture/oss-lab-poc.md +0 -250
  1350. package/docs/architecture/runtime-broker-core.md +0 -30
  1351. package/docs/release/0.100.0-local-setup.md +0 -31
  1352. package/docs/release/0.100.1-participant-continuity.md +0 -20
  1353. package/docs/release/0.101.0-codex-ui-tools.md +0 -28
  1354. package/docs/release/0.102.0-participant-media.md +0 -43
  1355. package/docs/release/0.103.0-desktop-recording.md +0 -29
  1356. package/docs/release/0.103.1-analysis-capture-allocation.md +0 -18
  1357. package/docs/release/0.103.2-analysis-ending-context.md +0 -21
  1358. package/docs/release/0.104.0-local-captured-inboxes.md +0 -29
  1359. package/docs/release/0.104.1-recording-startup.md +0 -24
  1360. package/docs/release/0.104.2-recording-timestamps.md +0 -32
  1361. package/docs/release/0.104.3-local-smtp-catch.md +0 -15
  1362. package/docs/release/0.105.0-participant-backgrounds.md +0 -50
  1363. package/docs/release/0.20.0-external-public-shared-world.md +0 -43
  1364. package/docs/release/0.84.0-redacted-evidence.md +0 -42
  1365. package/docs/release/0.84.1-portable-feedback.md +0 -20
  1366. package/docs/release/0.85.0-observer-review.md +0 -56
  1367. package/docs/release/0.85.1-observer-continuity.md +0 -20
  1368. package/docs/release/0.86.0-participant-evidence.md +0 -50
  1369. package/docs/release/0.86.1-task-preflight-saved-recordings.md +0 -40
  1370. package/docs/release/0.87.0-participant-endings-and-phone-review.md +0 -64
  1371. package/docs/release/0.88.0-study-diagnostics.md +0 -43
  1372. package/docs/release/0.88.1-completion-evidence-and-local-app.md +0 -79
  1373. package/docs/release/0.88.2-sequential-study-budgets.md +0 -57
  1374. package/docs/release/0.89.0-study-findings.md +0 -79
  1375. package/docs/release/0.89.1-analysis-finished-notice.md +0 -24
  1376. package/docs/release/0.90.0-automatic-analysis.md +0 -21
  1377. package/docs/release/0.91.0-analysis-quality-and-defaults.md +0 -33
  1378. package/docs/release/0.91.1-study-review-polish.md +0 -28
  1379. package/docs/release/0.92.0-grid-playback.md +0 -22
  1380. package/docs/release/0.93.0-global-playback.md +0 -26
  1381. package/docs/release/0.93.1-observer-review-controls.md +0 -24
  1382. package/docs/release/0.94.0-reliability.md +0 -48
  1383. package/docs/release/0.95.0-connections-setup.md +0 -38
  1384. package/docs/release/0.96.0-real-email-receiving.md +0 -47
  1385. package/docs/release/0.96.1-browser-navigation.md +0 -22
  1386. package/docs/release/0.97.0-codex-account-analysis.md +0 -45
  1387. package/docs/release/0.98.0-local-browser-studies.md +0 -31
  1388. package/docs/release/0.99.0-local-browser-mac.md +0 -34
  1389. package/docs/release/0.99.1-action-recovery.md +0 -19
  1390. /package/dist/{agent-session.d.ts → actors/agent-session.d.ts} +0 -0
  1391. /package/dist/{cua-executor-error.d.ts → actors/computer-use/executor-error.d.ts} +0 -0
  1392. /package/dist/{cua-speech.d.ts → actors/computer-use/speech.d.ts} +0 -0
  1393. /package/dist/{actor-goal-source.d.ts → actors/goal-source.d.ts} +0 -0
  1394. /package/dist/{output-token-limit.d.ts → actors/output-token-limit.d.ts} +0 -0
  1395. /package/dist/{reasoning-effort.d.ts → actors/reasoning-effort.d.ts} +0 -0
  1396. /package/dist/{secret-prompt.d.ts → cli/secret-prompt.d.ts} +0 -0
  1397. /package/dist/{telemetry.d.ts → cli/telemetry.d.ts} +0 -0
  1398. /package/dist/{browser-evidence-hygiene.d.ts → evidence/browser-hygiene.d.ts} +0 -0
  1399. /package/dist/{serve-http.d.ts → observer/http.d.ts} +0 -0
  1400. /package/dist/{observer-library.d.ts → observer/library.d.ts} +0 -0
  1401. /package/dist/{listen.d.ts → observer/listen.d.ts} +0 -0
  1402. /package/dist/{observer-static.d.ts → observer/static.d.ts} +0 -0
  1403. /package/dist/{serve-tunnel.d.ts → observer/tunnel.d.ts} +0 -0
  1404. /package/dist/{terminal-encoding.d.ts → routes/terminal/encoding.d.ts} +0 -0
  1405. /package/dist/{concurrency.d.ts → run/concurrency.d.ts} +0 -0
  1406. /package/dist/{study-costs.d.ts → run/costs.d.ts} +0 -0
  1407. /package/dist/{run-narration-secrets.d.ts → run/transient-comms-secrets.d.ts} +0 -0
  1408. /package/dist/{tui-launch.d.ts → tui/launch.d.ts} +0 -0
@@ -1,2181 +0,0 @@
1
- import { scrubPersonaBrief } from "./persona.js";
2
- import { withTransientCommsSecrets } from "./run-narration-secrets.js";
3
- import { prepareReceivingRun, receivingPublication } from "./comms-receiving-runtime.js";
4
- import { receivingEmailValidationReason } from "./lab-config.js";
5
- // The CONCURRENT shared-world lab backend (#164 phase 2): N persona lanes drive ONE shared,
6
- // mutable service plane SIMULTANEOUSLY — the actual leverage of a sim. A recomposition of shipped
7
- // pieces + the getHost wrapper:
8
- //
9
- // - ONE SUBJECT sandbox: provisionCloneSubject ONCE (clone+install+build+seed) + serve on
10
- // 0.0.0.0, exposed via getHost(port) → a tokenless reachable URL (the headless service host;
11
- // no GUI seat).
12
- // - N ACTOR desktop sandboxes: fan-out's runCuaLane machinery (per-lane device/persona, by-id
13
- // teardown) bounded by execution.concurrency, each browser pointed at the getHost URL —
14
- // driving the shared service AT THE SAME TIME. INDEPENDENT (FIX-11): no pipeline gate, no
15
- // fail-fast — one actor's failure must not block the swarm or corrupt the "M of N" outcomes.
16
- // - A background prober snapshots the subject DB checkpoint digests on a cadence → a stateSeries
17
- // of the shared world evolving under load.
18
- // - ALL N+1 sandboxes torn down BY exact id in a finally — NEVER Sandbox.list.
19
- //
20
- // HONEST ATTRIBUTION (verify-enforced, doctrine-audit fixes incorporated): the bundle declares
21
- // attributionClass: shared-world + a CONCURRENT humanish.shared-world.v1 block (topologyMode
22
- // "concurrent"; laneWindows + stateSeries + outcomes; NO timeline) whose attributionLimits drop
23
- // `sequential-only`/`no-concurrent-races` and add `concurrent`,
24
- // `best-effort-causal-attribution`, `non-deterministic-shared-state`,
25
- // `window-and-snapshot-granularity`, `contention-observed-not-proven-safe`,
26
- // `state-change-not-isolated-to-actors`. laneWindows + stateSeries are INDEPENDENT series with NO
27
- // per-delta→actor field — causation under concurrency is structurally inexpressible.
28
- //
29
- // CAPABILITY vs PROOF (FIX-1): the deterministic $0 gate proves the PLUMBING + the honesty
30
- // contract — the real mapWithConcurrency produces genuinely overlapping laneWindows (a rendezvous
31
- // latch in the fake session forces two lane fns in-flight while the REAL orchestrator clock
32
- // measures the windows). Every generated bundle describes only its own observations; no one run
33
- // establishes scale, repeatability, or adopter-harness replacement.
34
- //
35
- // Synthetic-subject (FIX-3): a getHost URL is internet-reachable for the run, so this route is
36
- // synthetic-seeded-subjects ONLY. Verify fail-closes on subject.state.provenance != "seeded" and
37
- // requires the author attestation subject.exposure: synthetic. This is author-trust + a provenance
38
- // gate, NOT a no-real-data guarantee (Humanish cannot tell synthetic from real data).
39
- import { resolveAutomaticAnalysis } from "./automatic-analysis-config.js";
40
- import { completeAutomaticAnalysis, markFinalizedStudyResult } from "./automatic-analysis-completion.js";
41
- import { desktopMediaValidationReason, taskProtocolValidationReason } from "./lab-config.js";
42
- import { randomBytes } from "node:crypto";
43
- import { describeMissingKeys } from "./key-resolution.js";
44
- import { beginRunStatus, withRunStatusScope } from "./run-status.js";
45
- import path from "node:path";
46
- import { pathToFileURL } from "node:url";
47
- import { adapterScoreFailureMessage, applyBrowserAdapterHooks } from "./adapter-extension.js";
48
- import { DEFAULT_OPENAI_CU_MODEL } from "./openai-responses-cu.js";
49
- import { MODEL_RATES } from "./pricing.js";
50
- import { actorRegistry, isCuaActorDescriptor } from "./actor-registry.js";
51
- import { toErrorMessage } from "./command-failure.js";
52
- import { mapWithConcurrency } from "./concurrency.js";
53
- import { appendSandboxReceipt } from "./sandbox-receipts.js";
54
- import { labPersonaIds, resolveCommittedPersonasForCwd } from "./persona-resolve.js";
55
- import { commandDigestOf, composeLaneInstructions, defaultPackLocalTree, provisionCloneSubject, provisionLocalTreeSubject, declaredScreenForRender, inboxRecipientFor, laneHasInboxRecipient, resolveLaneDevice, resolveSubjectState, runCuaLane, makeCuaRunBudget, withInboxMission } from "./cua-actor-lab.js";
56
- import { DEFAULT_SANDBOX_CATCH_PORT, collectCommsThread, collectExternalCommsThread, deployCommsCatch, externalCatchHealthy, externalInboxUrl, refreshInboxSurface, writeInboxSurface } from "./comms-sandbox-catch.js";
57
- import { FakeInbox } from "./comms-fake-inbox.js";
58
- import { buildOriginMap } from "./comms-inbox.js";
59
- import { createDesktopSandbox, loadE2BDesktopModule } from "./e2b-desktop-launch.js";
60
- import { concurrentSharedWorldValidationReason, outputTokenLimitValidationReason, externalPublicSharedWorldValidationReason } from "./lab-config.js";
61
- import { buildObserverData } from "./observer-data.js";
62
- import { attachObserverRuntimeStreamUrls, renderObserver } from "./observer.js";
63
- import { redactText } from "./redaction.js";
64
- import { participantAssignment } from "./participant-assignment.js";
65
- import { prepareRunArtifactPaths, validatePreparedRunArtifactPaths } from "./run-paths.js";
66
- import { writeContainedOutputFile, writePreparedRunLatestPointer } from "./selected-output-paths.js";
67
- import { combineCheckpointDigest, runCheckpointSnapshot, seedRecipeDigest } from "./shared-world-lab.js";
68
- import { buildRunSource, PUBLIC_TARGET_CWD, REVIEW_SCHEMA, RUN_BUNDLE_SCHEMA, SHARED_WORLD_SCHEMA } from "./run.js";
69
- export const CONCURRENT_SHARED_WORLD_LAB_SCHEMA = "humanish.concurrent-shared-world-lab-result.v1";
70
- export const CONCURRENT_SHARED_WORLD_PROVIDER_METADATA = {
71
- mode: "concurrent-shared-world-lab",
72
- tool: "humanish"
73
- };
74
- // The verify-enforced CONCURRENT attribution ceiling (FIX-5). Mirrored in run.ts's required set.
75
- export const CONCURRENT_ATTRIBUTION_LIMITS = [
76
- "concurrent",
77
- "best-effort-causal-attribution",
78
- "non-deterministic-shared-state",
79
- "window-and-snapshot-granularity",
80
- "contention-observed-not-proven-safe",
81
- "state-change-not-isolated-to-actors"
82
- ];
83
- // The DEFAULT per-seat session budget is DERIVED, not flat. On a provisioned route the binding
84
- // constraint is the SUBJECT sandbox (it must outlive every seat: timeoutMs + provisioning +
85
- // seeding + teardown buffer, and E2B refuses a sandbox over one hour), so the derivation hands
86
- // each seat the most that cap allows — capped at 15 minutes, floored at the historical 300s so a
87
- // seed-heavy lab never gets LESS room than it always had. App-url seats have no subject sandbox
88
- // and default to 30 minutes (seat sandbox: 30m + 10m buffer stays well under the hour). An
89
- // explicit execution.timeoutMs is never adjusted. The handoff latch scales off this (40%).
90
- const MAX_SANDBOX_MS = 60 * 60_000;
91
- const MAX_DERIVED_SEAT_SESSION_MS = 15 * 60_000;
92
- const MIN_DERIVED_SEAT_SESSION_MS = 300_000;
93
- const DEFAULT_APP_URL_SEAT_SESSION_MS = 30 * 60_000;
94
- function defaultSeatSessionTimeoutMs(config) {
95
- const provisionedRoute = config.subject.source === "clone" || config.subject.source === "local-tree";
96
- if (!provisionedRoute)
97
- return DEFAULT_APP_URL_SEAT_SESSION_MS;
98
- const stateBudgetMs = (config.subject.state?.seed ?? []).reduce((sum, step) => sum + (step.timeoutMs ?? DEFAULT_STATE_STEP_TIMEOUT_MS), 0);
99
- const room = MAX_SANDBOX_MS - SUBJECT_PROVISION_BUDGET_MS - stateBudgetMs - SANDBOX_TIMEOUT_BUFFER_MS;
100
- return Math.max(MIN_DERIVED_SEAT_SESSION_MS, Math.min(MAX_DERIVED_SEAT_SESSION_MS, room));
101
- }
102
- const SANDBOX_TIMEOUT_BUFFER_MS = 10 * 60_000;
103
- const SUBJECT_PROVISION_BUDGET_MS = 30 * 60_000;
104
- const DEFAULT_STATE_STEP_TIMEOUT_MS = 5 * 60_000;
105
- const DEFAULT_PROBER_CADENCE_MS = 1000;
106
- const DEFAULT_MISSION = "You are one of MANY users hitting a shared web application at the same time. The browser is already open at the app. Accomplish your role's task, then stop.";
107
- // EXTERNAL-PUBLIC plane class: the honest-downgrade attribution ceiling. The concurrent family
108
- // (an honest ceiling) PLUS the mandatory external-public disclosures — mirrored in run.ts's required
109
- // set (CONCURRENT_ATTRIBUTION_LIMITS + EXTERNAL_PUBLIC_EXTRA_LIMITS). Verify fails closed on a missing one.
110
- export const EXTERNAL_PUBLIC_ATTRIBUTION_LIMITS = [
111
- ...CONCURRENT_ATTRIBUTION_LIMITS,
112
- "external-public-plane",
113
- "operator-attested-target-not-harness-controlled",
114
- "no-synthetic-attestation",
115
- "no-authoritative-shared-state-proof",
116
- "concurrency-by-temporal-co-occupancy-only"
117
- ];
118
- // The FLOOR for the host-first handoff barrier deadline (ms). The host seat must surface a
119
- // shared-session (/lobby/CODE) URL within the deadline or the run fails closed and no follower
120
- // opens. The effective deadline SCALES with the per-seat run budget (execution.timeoutMs): a fixed
121
- // 2 min is too tight for a real create-a-lobby flow on a mobile-layout seat once you subtract the
122
- // seat's own desktop provisioning — the host reaches /lobby/CODE, but after the followers already
123
- // gave up. So use max(FLOOR, 40% of the budget), capped at the budget. The latch resolves the
124
- // instant the host actually reaches /lobby, so a generous ceiling only affects the fail-closed case.
125
- const DEFAULT_HANDOFF_DEADLINE_MS = 120_000;
126
- const HANDOFF_DEADLINE_BUDGET_FRACTION = 0.4;
127
- // Per-seat runaway backstop for the vision-off-frame lobby-code read (used by the host to LATCH the
128
- // handoff code, and by each follower to independently OBSERVE its own code for the convergence proof):
129
- // at most this many single-frame reads before the seat is assumed to be somewhere without a code. Each
130
- // reader stops the instant it has what it needs, so in practice only a handful fire (a seat reaches its
131
- // /lobby within a few turns). NOTE: these reads are out-of-band OpenAI calls (external-public route
132
- // only) and are NOT counted against execution.caps.maxUsd — this hard cap is what bounds their spend
133
- // instead (each read is one cheap single-frame OCR call). If this route ever runs under a strict
134
- // budget, fold the estimate in.
135
- const MAX_LOBBY_CODE_VISION_READS = 30;
136
- // Idle/no-progress backstop for the HOST lane specifically (default is 6/8). The host legitimately sits
137
- // on an unchanging waiting-room screen while followers provision and join; it must not give up first.
138
- const HOST_WAIT_IDLE_STEPS = 80;
139
- const FOLLOWER_WAIT_IDLE_STEPS = 40;
140
- /**
141
- * The lobby-trivia (and general "/lobby/CODE") shared-session URL matcher. A code is exactly 6 chars of
142
- * the [A-Z2-9] class; a locale prefix (/en/lobby/…) and a query/hash suffix are tolerated. RUNTIME-ONLY
143
- * input (a live location.href); only the extracted CODE is used, and it lands only as a digest.
144
- */
145
- export const LOBBY_CODE_PATTERN = /\/lobby\/([A-Z2-9]{6})(?:$|[/?#])/;
146
- /** Extract the shared-session CODE from a (runtime-only) observed URL, or undefined. Exported for the
147
- * handoff regex table test — pure, no side effects, never persists its input. */
148
- export function extractLobbyCode(url) {
149
- if (typeof url !== "string")
150
- return undefined;
151
- const match = url.match(LOBBY_CODE_PATTERN);
152
- return match ? match[1] : undefined;
153
- }
154
- /**
155
- * Extract a lobby CODE from free-form ACTOR NARRATION (the host's reasoning/message where it states
156
- * the lobby URL it sees), where the /lobby/CODE is followed by arbitrary prose (a space, backtick,
157
- * newline) rather than end-of-string or /?# — so the strict LOBBY_CODE_PATTERN would miss it. Uses a
158
- * negative-lookahead boundary (exactly 6 code chars). This is the CDP-INDEPENDENT handoff path: the
159
- * host reads the code on screen and states it, and this reads it from the model's own text. Pure;
160
- * input is runtime-only; only the code is used (as a digest).
161
- */
162
- const LOBBY_CODE_IN_TEXT = /\/lobby\/([A-Z2-9]{6})(?![A-Z2-9])/;
163
- export function extractLobbyCodeFromNarration(text) {
164
- if (typeof text !== "string")
165
- return undefined;
166
- const inUrl = text.match(LOBBY_CODE_IN_TEXT);
167
- if (inUrl)
168
- return inUrl[1];
169
- // Fallback: an explicitly-labeled bare code (e.g. "LOBBY_CODE=ABC123"), which the host may state
170
- // if it copied the code rather than the URL. The label is matched case-insensitively, but the CODE
171
- // itself must be UPPERCASE [A-Z2-9] — a real lobby code always renders uppercase, whereas an /i match
172
- // on the code class would also grab an ordinary lowercase word after "lobby code " (e.g. "the lobby
173
- // code screen") and latch a WRONG code. Precision-first, matching parseLobbyCodeReply's rationale.
174
- const labeled = text.match(/lobby[ _-]?code[=:\s]+([A-Z2-9]{6})(?![A-Za-z2-9])/i);
175
- return labeled && labeled[1] && /^[A-Z2-9]{6}$/.test(labeled[1]) ? labeled[1] : undefined;
176
- }
177
- /** Pull the assistant's plain text out of an OpenAI Responses API body (`output_text` convenience
178
- * field, else the concatenated `output[].content[].text`). Tolerant of shape drift; pure. */
179
- export function extractResponsesOutputText(parsed) {
180
- if (typeof parsed !== "object" || parsed === null)
181
- return undefined;
182
- const obj = parsed;
183
- if (typeof obj.output_text === "string" && obj.output_text.length > 0)
184
- return obj.output_text;
185
- const out = obj.output;
186
- if (!Array.isArray(out))
187
- return undefined;
188
- const parts = [];
189
- for (const item of out) {
190
- if (typeof item !== "object" || item === null)
191
- continue;
192
- const content = item.content;
193
- if (!Array.isArray(content))
194
- continue;
195
- for (const chunk of content) {
196
- if (typeof chunk === "object" && chunk !== null && typeof chunk.text === "string") {
197
- parts.push(chunk.text);
198
- }
199
- }
200
- }
201
- return parts.length > 0 ? parts.join("\n") : undefined;
202
- }
203
- /** Parse a vision reply into a lobby CODE. PRECISION-FIRST: accept ONLY when the whole reply IS the
204
- * six-character code, or when it echoes an explicit /lobby/CODE — never a bare 6-letter token buried in
205
- * prose (e.g. "I see a home SCREEN"), because a wrong latch fails the entire run, whereas a miss just
206
- * retries on the next frame while the host keeps waiting. NONE (the instructed "no code" reply) is
207
- * rejected. Pure. */
208
- export function parseLobbyCodeReply(reply) {
209
- if (typeof reply !== "string")
210
- return undefined;
211
- const up = reply.trim().toUpperCase();
212
- if (up.length === 0 || /\bNONE\b/.test(up))
213
- return undefined;
214
- if (/^[A-Z2-9]{6}$/.test(up))
215
- return up; // the well-behaved "code only" reply
216
- const inUrl = up.match(/\/LOBBY\/([A-Z2-9]{6})(?![A-Z2-9])/); // model echoed the invite link
217
- return inUrl ? inUrl[1] : undefined;
218
- }
219
- const LOBBY_CODE_VISION_PROMPT = "This is a screenshot of a the example multiplayer app multiplayer lobby. If a waiting-room / invite screen is " +
220
- "shown, read the 6-character lobby code (characters A-Z and 2-9 only) — it appears near a 'lobby " +
221
- "code'/'room code' label or inside an invite link of the form /lobby/CODE. Your entire reply MUST be " +
222
- "exactly those 6 characters in uppercase and NOTHING else (no words, no punctuation). If no lobby " +
223
- "code is visible on this screen (e.g. it is the home screen or a game round), reply exactly NONE.";
224
- const LOBBY_CODE_VISION_ENDPOINT = "https://api.openai.com/v1/responses";
225
- // A single-frame OCR-style read. gpt-5.5 (the CU default) is used deliberately: it reliably reads the
226
- // 6-char code off a dense MOBILE-viewport waiting room — a smaller/cheaper model (gpt-4.1-mini) was
227
- // tried and could NOT read it. reasoning.effort stays "low" (minimal) and the output budget is small
228
- // but comfortably clear of the "incomplete on reasoning overflow" edge. Kept on the same account/key
229
- // as the actor; the same full-fidelity frame is already sent to this API by the CU provider, so this
230
- // adds no new data-exposure surface. See onScreenshot in runHostLane.
231
- const LOBBY_CODE_VISION_MODEL = "gpt-5.5";
232
- // Output-token budget for the read. The answer is 6 chars, but leave clear margin over any low-effort
233
- // reasoning tokens so the response never comes back status:"incomplete" with empty output.
234
- const LOBBY_CODE_VISION_MAX_OUTPUT_TOKENS = 64;
235
- // Per-read wall-clock cap. Without it a stalled fetch (Node fetch has no default timeout) would leave
236
- // visionInFlight pinned true and silently kill the relay for the rest of the host run.
237
- const LOBBY_CODE_VISION_TIMEOUT_MS = 15_000;
238
- /**
239
- * Vision-read a lobby CODE straight off a host waiting-room FRAME (the robust, CDP-independent handoff
240
- * relay). Fail-soft: any network/HTTP/parse problem returns undefined so the caller simply retries on
241
- * the next frame. The frame is runtime-only; only the extracted code is used (as a digest downstream).
242
- */
243
- export async function readLobbyCodeFromFrame(frame, apiKey, options = {}) {
244
- if (typeof apiKey !== "string" || apiKey.length === 0 || frame.length === 0)
245
- return undefined;
246
- const fetchFn = options.fetchFn ?? fetch;
247
- // Default a wall-clock timeout so a stalled request can't wedge the caller's in-flight guard. An
248
- // explicit signal (e.g. run abort) takes precedence when provided.
249
- const signal = options.signal ?? AbortSignal.timeout(LOBBY_CODE_VISION_TIMEOUT_MS);
250
- const body = {
251
- model: options.model ?? LOBBY_CODE_VISION_MODEL,
252
- reasoning: { effort: "low" },
253
- max_output_tokens: LOBBY_CODE_VISION_MAX_OUTPUT_TOKENS,
254
- input: [
255
- {
256
- role: "user",
257
- content: [
258
- { type: "input_text", text: LOBBY_CODE_VISION_PROMPT },
259
- { type: "input_image", image_url: `data:image/png;base64,${frame.toString("base64")}` }
260
- ]
261
- }
262
- ]
263
- };
264
- let res;
265
- try {
266
- res = await fetchFn(options.endpoint ?? LOBBY_CODE_VISION_ENDPOINT, {
267
- method: "POST",
268
- headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
269
- body: JSON.stringify(body),
270
- signal
271
- });
272
- }
273
- catch {
274
- return undefined; // transient network error: skip this frame, next turn retries
275
- }
276
- if (!res.ok)
277
- return undefined; // never read a non-ok body (it can echo the frame/input)
278
- let parsed;
279
- try {
280
- parsed = await res.json();
281
- }
282
- catch {
283
- return undefined;
284
- }
285
- return parseLobbyCodeReply(extractResponsesOutputText(parsed));
286
- }
287
- /** A minimal resolve-once latch for the host-first handoff barrier. */
288
- function deferred() {
289
- let resolve;
290
- let reject;
291
- let done = false;
292
- const promise = new Promise((res, rej) => {
293
- resolve = (value) => { if (!done) {
294
- done = true;
295
- res(value);
296
- } };
297
- reject = (reason) => { if (!done) {
298
- done = true;
299
- rej(reason);
300
- } };
301
- });
302
- return { promise, resolve, reject, settled: () => done };
303
- }
304
- /** Marker error the host-first barrier rejects with when the deadline elapses (fail-closed). */
305
- class HandoffTimeoutError extends Error {
306
- constructor(deadlineMs) {
307
- super(`the host never produced a /lobby/CODE URL within the ${deadlineMs}ms handoff deadline`);
308
- this.name = "HandoffTimeoutError";
309
- }
310
- }
311
- function readPositiveInt(value, fallback) {
312
- if (value === undefined)
313
- return fallback;
314
- const parsed = Number.parseInt(value, 10);
315
- return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : fallback;
316
- }
317
- function makeRunId() {
318
- const stamp = new Date().toISOString().replace(/[:.]/g, "-");
319
- return `concurrent-shared-world-${stamp}-${randomBytes(4).toString("hex")}`;
320
- }
321
- /** Extract the in-sandbox port from the (loopback) serve.url so getHost can expose it. */
322
- function servePort(serveUrl) {
323
- const url = new URL(serveUrl);
324
- if (url.port)
325
- return Number(url.port);
326
- return url.protocol === "https:" ? 443 : 80;
327
- }
328
- /** Resolve an actor's seat URL against the harness-minted getHost base (entry is a same-origin
329
- * relative path, validated at parse against serve.url). */
330
- function resolveActorSeatUrl(baseUrl, entry) {
331
- if (!entry)
332
- return baseUrl;
333
- try {
334
- return new URL(entry, baseUrl).toString();
335
- }
336
- catch {
337
- return baseUrl;
338
- }
339
- }
340
- /** A getHost URL must be TOKENLESS (no userinfo, no query — no authKey; invariant 1). */
341
- function isTokenlessHost(value) {
342
- try {
343
- const url = new URL(value);
344
- return url.username === "" && url.password === "" && url.search === "";
345
- }
346
- catch {
347
- return false;
348
- }
349
- }
350
- /** sha256-16 of a URL's ORIGIN — the publish-safe host identity persisted in the bundle (the raw
351
- * getHost URL embeds the live sandbox id + matches the e2b-URL redaction, so it never lands raw). */
352
- function hostOriginDigest(url) {
353
- try {
354
- return commandDigestOf(new URL(url).origin);
355
- }
356
- catch {
357
- return commandDigestOf(url);
358
- }
359
- }
360
- /** A public-safe, human-readable route label for the bundle (host redacted to a placeholder; the
361
- * entry path kept). Never contains the raw getHost URL. */
362
- function publicSafeRouteLabel(entry) {
363
- return `[provisioned-subject]${entry ?? "/"}`;
364
- }
365
- /**
366
- * Build the ONE subject sandbox's provenance (invariant 5): clone (repo + optional commit) or
367
- * local-tree (archiveSha256 + optional commit/dirty from the once-per-run host-packed archive -
368
- * archiveSha256 IS the pin; there is only ONE archive, so no per-lane unanimity math applies,
369
- * unlike the cua fan-out route). Used for both the in-progress and final bundle: the archive
370
- * never changes mid-run (packed before any sandbox exists).
371
- */
372
- function buildSubjectProvenance(args) {
373
- if (args.localTreeRoute) {
374
- return {
375
- source: "local-tree",
376
- ...(args.localTreeArchive === undefined ? {} : { archiveSha256: args.localTreeArchive.archiveSha256 }),
377
- ...(args.subjectCommit === undefined ? {} : { commit: args.subjectCommit }),
378
- ...(args.localTreeArchive?.git === undefined ? {} : { dirty: args.localTreeArchive.git.dirty }),
379
- envNames: args.subjectEnvNames,
380
- state: args.state
381
- };
382
- }
383
- return {
384
- source: "clone",
385
- repo: args.publicRepo,
386
- ...(args.subjectCommit === undefined ? {} : { commit: args.subjectCommit }),
387
- envNames: args.subjectEnvNames,
388
- state: args.state
389
- };
390
- }
391
- function laneTaxonomyLabel(spec) {
392
- const parts = [
393
- spec.actorType ? `type:${spec.actorType}` : undefined,
394
- spec.surface ? `surface:${spec.surface}` : undefined,
395
- spec.caseGroup ? `case:${spec.caseGroup}` : undefined
396
- ].filter((part) => part !== undefined);
397
- return parts.length > 0 ? ` (${parts.join(" / ")})` : "";
398
- }
399
- /** Build one actor lane's CuaLaneSpec from a roster role (per-actor device IS honored here — each
400
- * actor has its OWN desktop, unlike the sequential one-sandbox PoC). */
401
- function buildActorSpec(config, role, index, personas) {
402
- const mission = config.actors[0]?.mission ?? DEFAULT_MISSION;
403
- const device = resolveLaneDevice(config, role);
404
- const resolvedPersona = role.persona === undefined ? undefined : personas.get(role.persona);
405
- const composed = composeLaneInstructions({
406
- mission,
407
- ...(role.persona === undefined ? {} : { persona: role.persona }),
408
- ...(resolvedPersona === undefined ? {} : { resolvedPersona }),
409
- ...(role.instruction === undefined ? {} : { instruction: role.instruction }),
410
- device: { name: device.name, preset: device.preset }
411
- });
412
- const roleId = role.id ?? `role-${String(index + 1).padStart(2, "0")}`;
413
- const streamId = `stream-${String(index + 1).padStart(3, "0")}`;
414
- return {
415
- laneId: roleId,
416
- ...(role.actorType === undefined ? {} : { actorType: role.actorType }),
417
- ...(role.surface === undefined ? {} : { surface: role.surface }),
418
- ...(role.caseGroup === undefined ? {} : { caseGroup: role.caseGroup }),
419
- laneIndex: index,
420
- simId: `sim-${String(index + 1).padStart(3, "0")}`,
421
- streamId,
422
- persona: composed.persona,
423
- instructions: composed.instructions,
424
- assignment: { mission, ...(role.instruction === undefined ? {} : { focus: role.instruction }) },
425
- ...((role.reasoningEffort ?? config.actors[0]?.reasoningEffort) === undefined
426
- ? {} : { reasoningEffort: (role.reasoningEffort ?? config.actors[0]?.reasoningEffort) }),
427
- ...(config.actors[0]?.maxOutputTokens === undefined ? {} : { maxOutputTokens: config.actors[0].maxOutputTokens }),
428
- ...((role.stopWhen ?? config.actors[0]?.stopWhen) === undefined ? {} : { stopWhen: (role.stopWhen ?? config.actors[0]?.stopWhen) }),
429
- ...((role.dwell ?? config.actors[0]?.dwell) === undefined ? {} : { dwell: (role.dwell ?? config.actors[0]?.dwell) }),
430
- deviceName: device.name,
431
- devicePreset: device.preset,
432
- resolution: device.resolution,
433
- screenshotDir: roleId,
434
- traceArtifactPath: `actors/${streamId}.json`
435
- };
436
- }
437
- /** Thread the host-yielded lobby CODE into a follower's mission at runtime (external-public route).
438
- * The CODE flows into the follower's join instruction; it is persisted only as the composed prompt
439
- * the model reads (never a raw bundle field), and the lab scrubs the CODE from all narration. The
440
- * follower joins through the real UI (a direct /lobby/CODE visit does not auto-join a non-member). */
441
- function withLobbyCodeMission(spec, code) {
442
- return {
443
- ...spec,
444
- instructions: `${spec.instructions}\n\nThe multiplayer lobby code is ${code}. On the home screen choose Join, enter this lobby code, enter your name, and submit to join the shared game (do not open a lobby URL directly — go through the Join flow).`
445
- };
446
- }
447
- /** A follower blocked by an expired deadline or an ended host. It never opened a browser;
448
- * keep the actual reason rather than turning every upstream failure into a timeout. */
449
- function makeBlockedFollowerOutcome(spec, reason, timedOut) {
450
- return {
451
- spec,
452
- sessionError: `handoff barrier: ${reason}; this follower failed closed WITHOUT opening (no wasted turns).`,
453
- killed: false,
454
- streamUrlPresent: false,
455
- screenshots: [],
456
- stateStepRecords: [],
457
- phaseRecords: [],
458
- warnings: [],
459
- noEngagement: true,
460
- selfReportedBlocker: false,
461
- harnessError: false,
462
- skippedReason: timedOut ? "handoff-timeout" : "host-ended-before-handoff"
463
- };
464
- }
465
- async function writeConcurrentRunArtifacts(bundle, preparedRunPaths) {
466
- const runPaths = await validatePreparedRunArtifactPaths(preparedRunPaths);
467
- const publicBundle = {
468
- ...bundle,
469
- cwd: PUBLIC_TARGET_CWD
470
- };
471
- await writeContainedOutputFile(runPaths, "run.json", `${JSON.stringify(publicBundle, null, 2)}\n`, "utf8");
472
- await writeContainedOutputFile(runPaths, "review.json", `${JSON.stringify(publicBundle.review, null, 2)}\n`, "utf8");
473
- await writeContainedOutputFile(runPaths, "review.md", renderConcurrentReviewMarkdown(publicBundle), "utf8");
474
- await writeContainedOutputFile(runPaths, "events.ndjson", `${publicBundle.events.map((event) => JSON.stringify(event)).join("\n")}\n`, "utf8");
475
- await writeContainedOutputFile(runPaths, "observer/observer-data.json", `${JSON.stringify(buildObserverData(publicBundle), null, 2)}\n`, "utf8");
476
- await writePreparedRunLatestPointer(runPaths, `${JSON.stringify({
477
- schema: "humanish.latest-run.v1",
478
- runId: publicBundle.runId,
479
- path: runPaths.relativeRunRoot,
480
- updatedAt: new Date().toISOString()
481
- }, null, 2)}\n`, "utf8");
482
- }
483
- function observerResultForConcurrentArtifacts(cwd, runId, artifactRoot, warnings = []) {
484
- const observerPath = path.join(artifactRoot, "observer", "index.html");
485
- const observerDataPath = path.join(artifactRoot, "observer", "observer-data.json");
486
- const eventsPath = path.join(artifactRoot, "events.ndjson");
487
- return {
488
- schema: "humanish.observer-result.v1",
489
- ok: true,
490
- cwd,
491
- run: runId,
492
- observerPath: path.relative(cwd, observerPath),
493
- observerDataPath: path.relative(cwd, observerDataPath),
494
- eventsPath: path.relative(cwd, eventsPath),
495
- observerUrl: pathToFileURL(observerPath).href,
496
- bundlePath: path.join(artifactRoot, "run.json"),
497
- opened: false,
498
- warnings
499
- };
500
- }
501
- /**
502
- * Wrapped so a DIRECT library caller gets the same status-record lifetime the CLI does: returning
503
- * from this function finalizes any record the run opened, whichever of its fail-closed exits it
504
- * took. `runLab` establishes a scope too and nesting is harmless — the inner scope owns what it
505
- * opened. Without this a test or an adopter calling the backend directly leaves the 5s cadence
506
- * ticking into a directory something else is deleting, which surfaces as an unrelated ENOTEMPTY.
507
- */
508
- export async function runConcurrentSharedWorld(options) {
509
- return withTransientCommsSecrets(() => runConcurrentSharedWorldWithSecrets(options));
510
- }
511
- async function runConcurrentSharedWorldWithSecrets(options) {
512
- const analysis = resolveAutomaticAnalysis(options.config.review?.analysis);
513
- const result = await withRunStatusScope(() => runConcurrentSharedWorldInScope(options));
514
- return completeAutomaticAnalysis(result, analysis.ok ? analysis.config : undefined, options.automaticAnalysis, options.config.review?.analysis === undefined ? "default" : "explicit", analysis.ok && analysis.preferLargerOutput === true);
515
- }
516
- async function runConcurrentSharedWorldInScope(options) {
517
- const { config, dryRun } = options;
518
- const cwd = path.resolve(options.cwd);
519
- const hooks = options.hooks ?? {};
520
- const env = hooks.env ?? process.env;
521
- const render = hooks.renderObserverFn ?? renderObserver;
522
- const actorType = config.actors[0]?.type ?? "";
523
- const roles = config.actors[0]?.lanes ?? [];
524
- // All-parallel default (#350): the parser fills concurrency for multi-seat labs, so this
525
- // fallback serves only library callers constructing configs directly — same meaning: every
526
- // declared seat runs at once unless the author declared a cap.
527
- const concurrency = config.execution?.concurrency ?? Math.max(1, roles.length);
528
- const fail = (code, message, actorLabel) => ({
529
- schema: CONCURRENT_SHARED_WORLD_LAB_SCHEMA,
530
- ok: false,
531
- cwd,
532
- labId: config.id,
533
- actor: actorLabel ?? actorType,
534
- topology: "shared-world",
535
- topologyMode: "concurrent",
536
- roleCount: roles.length,
537
- concurrency,
538
- dryRun,
539
- runId: options.runId ?? "not-created",
540
- roles: [],
541
- warnings: [],
542
- error: { code, message }
543
- });
544
- const mediaReason = desktopMediaValidationReason(config, false);
545
- if (mediaReason)
546
- return fail("HUMANISH_CONCURRENT_SHARED_WORLD_LAB_INVALID", mediaReason);
547
- const analysis = resolveAutomaticAnalysis(config.review?.analysis);
548
- if (!analysis.ok)
549
- return fail("HUMANISH_LAB_ANALYSIS_INVALID", analysis.message);
550
- const tasksReason = taskProtocolValidationReason(config, false);
551
- if (tasksReason)
552
- return fail("HUMANISH_LAB_TASKS_UNSUPPORTED", tasksReason);
553
- const descriptor = actorRegistry[actorType];
554
- if (!descriptor || !isCuaActorDescriptor(descriptor)) {
555
- return fail("HUMANISH_CONCURRENT_SHARED_WORLD_LAB_ACTOR_UNSUPPORTED", `actors[0].type "${actorType}" is not a registered computer-use actor.`);
556
- }
557
- // The PLANE-class discriminator (#164 phase 2): an app-url subject is the EXTERNAL-PUBLIC plane (a
558
- // real operator-owned public deployment used directly as the shared plane — NO getHost, clone,
559
- // subject sandbox, or seed); everything else is the historical provisioned-getHost plane.
560
- const planeClass = config.subject.source === "app-url" ? "external-public" : "provisioned-getHost";
561
- // Re-enforce the cross-validation (library API surface). The external-public branch NEVER touches
562
- // the getHost synthetic gate — that gate exists because getHost is internet-reachable AND
563
- // harness-owned; a public site the harness neither provisioned nor exposed has neither property.
564
- const invalidReason = outputTokenLimitValidationReason(config) ?? (planeClass === "external-public"
565
- ? externalPublicSharedWorldValidationReason(config)
566
- : concurrentSharedWorldValidationReason(config));
567
- if (invalidReason) {
568
- return fail("HUMANISH_CONCURRENT_SHARED_WORLD_LAB_INVALID", invalidReason, descriptor.id);
569
- }
570
- if (config.actors[0]?.maxOutputTokens !== undefined && hooks.runSession) {
571
- return fail("HUMANISH_CONCURRENT_SHARED_WORLD_LAB_INVALID", "maxOutputTokens cannot be enforced by a custom runSession.", descriptor.id);
572
- }
573
- const caps = config.execution?.caps;
574
- if (!dryRun && (caps?.maxUsd !== undefined || caps?.maxTotalUsd !== undefined)) {
575
- const model = (config.actors[0]?.model ?? DEFAULT_OPENAI_CU_MODEL).trim().toLowerCase();
576
- if (!MODEL_RATES[model]) {
577
- return fail("HUMANISH_CONCURRENT_SHARED_WORLD_LAB_INVALID", `The declared spend cap cannot be enforced for unpriced model "${model}".`, descriptor.id);
578
- }
579
- }
580
- const runBudget = !dryRun && caps?.maxTotalUsd !== undefined ? makeCuaRunBudget(caps.maxTotalUsd) : undefined;
581
- // provisioned-getHost fields (all absent on the external-public plane — forbidden at validation).
582
- const serve = config.subject.serve;
583
- const localTreeRoute = config.subject.source === "local-tree";
584
- const subjectRepo = config.subject.repos?.[0] ?? "";
585
- const subjectEnvNames = config.subject.env ?? [];
586
- const checkpoints = config.subject.state?.checkpoint ?? [];
587
- const runSession = hooks.runSession ?? descriptor.runSession;
588
- // The per-seat vision lobby-code reader (default: the real single-frame OpenAI read). Injectable so the
589
- // barrier's handoff + convergence proof are testable without a live vision call.
590
- const readLobbyCode = hooks.readLobbyCodeFromFrame ?? readLobbyCodeFromFrame;
591
- const receivingReason = receivingEmailValidationReason(config);
592
- if (receivingReason)
593
- return fail("HUMANISH_CONCURRENT_SHARED_WORLD_LAB_INVALID", receivingReason, descriptor.id);
594
- const openaiApiKey = env.OPENAI_API_KEY?.trim() ?? "";
595
- const e2bApiKey = env.E2B_API_KEY?.trim() ?? "";
596
- const knownSecretValues = [
597
- openaiApiKey,
598
- e2bApiKey,
599
- ...subjectEnvNames.map((name) => env[name] ?? ""),
600
- ...checkpoints.flatMap((probe) => probe.redact ?? [])
601
- ].filter((value) => value.length >= 4);
602
- const scrubKnownValues = (text) => knownSecretValues.reduce((current, value) => current.split(value).join("[REDACTED_SECRET]"), text);
603
- const redactRepoLabel = config.policies?.redactRepos ?? subjectEnvNames.includes("GITHUB_TOKEN");
604
- const publicRepo = redactRepoLabel ? "repo-01" : subjectRepo;
605
- const hasGithubToken = subjectEnvNames.includes("GITHUB_TOKEN");
606
- if (!dryRun) {
607
- const missingKeys = [
608
- ...(openaiApiKey ? [] : ["OPENAI_API_KEY"]),
609
- ...(e2bApiKey ? [] : ["E2B_API_KEY"])
610
- ];
611
- if (missingKeys.length > 0) {
612
- return fail("HUMANISH_CONCURRENT_SHARED_WORLD_LAB_KEYS_MISSING", `Live concurrent shared-world labs need ${missingKeys.join(" and ")} in the environment (values are never persisted). ${describeMissingKeys(missingKeys, env)}`, descriptor.id);
613
- }
614
- const missingSubjectEnv = subjectEnvNames.filter((name) => !env[name]?.trim());
615
- if (missingSubjectEnv.length > 0) {
616
- return fail("HUMANISH_CONCURRENT_SHARED_WORLD_LAB_SUBJECT_ENV_MISSING", `subject.env declares ${missingSubjectEnv.join(", ")} but the environment does not provide ${missingSubjectEnv.length === 1 ? "it" : "them"} (pass via --env-file; values are never persisted).`, descriptor.id);
617
- }
618
- }
619
- const runId = options.runId ?? makeRunId();
620
- const runPaths = await prepareRunArtifactPaths(cwd, runId);
621
- // Identity + liveness on disk (#455): every backend writes this, so a watcher can classify any
622
- // run without parsing bundles and without depending on the interactive-observer path.
623
- const runStatus = beginRunStatus(runPaths, {
624
- runId,
625
- mode: dryRun ? "dry-run" : "live",
626
- ...(options.lab === undefined ? {} : { lab: options.lab })
627
- });
628
- const artifactRoot = runPaths.absoluteRunRoot;
629
- const physicalArtifactRoot = runPaths.physicalRunRoot;
630
- const createdAt = new Date().toISOString();
631
- const timeoutMs = config.execution?.timeoutMs ?? defaultSeatSessionTimeoutMs(config);
632
- const requestTimeoutMs = readPositiveInt(env.HUMANISH_E2B_REQUEST_TIMEOUT_MS, 60_000);
633
- const redactScreenshots = config.policies?.redactScreenshots === true;
634
- const timers = hooks.detachedTimers ?? {};
635
- const now = hooks.now ?? Date.now;
636
- const proberCadenceMs = hooks.proberCadenceMs ?? DEFAULT_PROBER_CADENCE_MS;
637
- const seedDigest = seedRecipeDigest(config);
638
- const source = await buildRunSource({ capturedAt: createdAt, cwd, humanishSource: "present", packageName: "humanish" });
639
- const warnings = [];
640
- const stateStepRecords = [];
641
- const stateSnapshots = [];
642
- // Compile committed personas so each seat's prompt carries real behavioral directives (#381).
643
- const personaResolution = await resolveCommittedPersonasForCwd(cwd, labPersonaIds(config));
644
- const actorSpecs = roles.map((role, i) => buildActorSpec(config, role, i, personaResolution.personas));
645
- for (const spec of actorSpecs) {
646
- if (spec.assignment)
647
- spec.assignment = participantAssignment(spec.assignment, scrubKnownValues);
648
- spec.evidenceInstructions = redactText(scrubKnownValues(spec.instructions));
649
- spec.persona = scrubPersonaBrief(spec.persona, scrubKnownValues);
650
- }
651
- let actorResults = [];
652
- let subjectCommit;
653
- let subjectSandboxId;
654
- let subjectKilled = false;
655
- let getHostUrl;
656
- // Persona inbox SURFACE (#297 slice B, shared-world): the getHost-exposed inbox URL a persona (in a
657
- // DIFFERENT sandbox) opens, the serve->getHost origin-rewrite map (REQUIRED here so the app's loopback
658
- // verify links resolve to a reachable host), and the dedicated surface channel + render loop.
659
- let commsInboxUrl;
660
- let commsOriginMap = [];
661
- let surfaceRenderedCount = 0;
662
- let surfaceLoop;
663
- let runError;
664
- let snapshotIndex = 0;
665
- let liveObserver;
666
- const runtimeStreamUrls = [];
667
- // Off-app comms (#297): on the provisioned-getHost plane the harness owns the ONE subject sandbox, so
668
- // it can redirect the app's email-API sends into an in-sandbox catch and evidence them. Gated ENTIRELY
669
- // on config.comms — no comms declared → zero change. The base-URL env is injected into the subject
670
- // sandbox at create (fixed port known up front); the catch is deployed before serve; the drain + digest
671
- // evidence run at subject teardown, then register run-level in the bundle. NOT available on the
672
- // external-public plane (the app is an operator-owned deployment the harness never provisions).
673
- const commsEmail = planeClass === "provisioned-getHost" && config.comms?.email?.kind === "fake" ? config.comms.email : undefined;
674
- const commsPort = commsEmail ? (commsEmail.port ?? DEFAULT_SANDBOX_CATCH_PORT) : undefined;
675
- // injectEnv is absent on an adopter-hosted plane (#328): there is no subject env to inject
676
- // because the operator points their own app at their own catch.
677
- const commsEnv = commsEmail?.injectEnv !== undefined && commsPort !== undefined
678
- ? { [commsEmail.injectEnv]: `http://127.0.0.1:${commsPort}` }
679
- : {};
680
- let commsArtifactPath;
681
- // ADOPTER-HOSTED ingress (#328): on the external-public plane the harness provisions nothing, so
682
- // it cannot host a catch — but the OPERATOR can, and then humanish still does every other part of
683
- // the funnel: it tells each persona its address and inbox URL, drains the declared catch over
684
- // HTTP at teardown, and writes the same digest-only evidence. Declaring `external` is what turns
685
- // the previously-inert block into a working one.
686
- const externalComms = planeClass === "external-public" ? config.comms?.email?.external : undefined;
687
- const externalCommsEmail = externalComms ? config.comms?.email : undefined;
688
- if (config.comms?.email?.kind === "fake" && planeClass === "external-public" && externalComms === undefined) {
689
- warnings.push("comms.email is declared but this is the external-public plane (the shared plane is an operator-owned public deployment the harness does not provision) — the in-sandbox email catch cannot be deployed and no comms evidence is collected. Declare `comms.email.external` to host the catch yourself (#328).");
690
- }
691
- if (externalComms) {
692
- commsInboxUrl = externalInboxUrl(externalComms);
693
- // Fail closed BEFORE any actor sandbox is created: a comms lab whose catch is unreachable
694
- // collects nothing while every lane still spends. The probe asserts OUR service marker in
695
- // /health, so an adopter's proxy answering 200 for everything cannot pass for a catch.
696
- if (!dryRun && !(await externalCatchHealthy(externalComms))) {
697
- return fail("HUMANISH_CONCURRENT_SHARED_WORLD_LAB_COMMS_CATCH_UNREACHABLE", `The external comms catch or inbox is unreachable or incompatible (GET /health must identify humanish-comms-catch and advertise recipient-inbox-v1). Update Humanish on the catch host and restart it with \`humanish comms catch\` on that host, or drop comms.email to run without the inbox funnel.`);
698
- }
699
- }
700
- // EXTERNAL-PUBLIC plane state (#164 phase 2). publicAppUrl is the operator-declared shared plane;
701
- // its ORIGIN is persisted digest-only (publicOriginDigest), never raw (the raw URL + the runtime
702
- // observed lobby CODE never land — TENSION 3). The latch code is scrubbed from all narration.
703
- const publicAppUrl = config.subject.appUrl ?? "";
704
- // The operator-DECLARED origin (from subject.appUrl) — recorded for evidence/reference ONLY. The
705
- // operator-OWNERSHIP claim rests on the subject.publicTarget.authorized attestation + this declared
706
- // appUrl, NOT on digest equality (blocker 2): a normal cross-origin redirect (apex->www, http->https;
707
- // lobby-trivia.example.test 307-redirects) makes the seats' OBSERVED origin differ from the declared one, which
708
- // is expected and MUST NOT fail the run. Persisted digest-only (never the raw origin).
709
- const declaredOriginDigest = planeClass === "external-public" && publicAppUrl
710
- ? hostOriginDigest(publicAppUrl)
711
- : undefined;
712
- // The OBSERVED convergence origin — computed AFTER fan-out from what the seats ACTUALLY reached (the
713
- // convergence proof is what the seats OBSERVED, not what was declared). Set iff every observing seat
714
- // agrees on ONE origin; that agreement IS the convergence proof and becomes plane.publicOriginDigest.
715
- let publicOriginDigest;
716
- // Per-lane runtime-only observed state (never persisted raw): the last observed URL and the last
717
- // observed /lobby/CODE per seat, fed by onObservedUrl. The URL is digested to ORIGIN for each seat's
718
- // routeHostDigest (no code leaks); the codes drive the cross-seat lobby-convergence digest.
719
- const observedFinalUrls = new Array(roles.length);
720
- const observedLobbyCodes = new Array(roles.length);
721
- let lobbyConvergenceDigest;
722
- let handoffTimedOut = false;
723
- let hostHandoffFailure;
724
- // A closure that scrubs the latched lobby CODE from ANY persisted narration once the host resolves
725
- // it (the 6-char code has no detectable secret shape, so shape-only redaction cannot catch it).
726
- let latchedLobbyCode;
727
- const scrubKnownValuesWithLobbyCode = (text) => {
728
- const base = scrubKnownValues(text);
729
- return latchedLobbyCode && latchedLobbyCode.length > 0
730
- ? base.split(latchedLobbyCode).join("[REDACTED_LOBBY_CODE]")
731
- : base;
732
- };
733
- // Pack the working tree ONCE per run, on the host, BEFORE the subject sandbox is created
734
- // (mirrors the sequential route + the cua route's ordering): a packing failure fails the run
735
- // closed here, never spending sandbox cost. Dry-run packs nothing.
736
- let localTreeArchive;
737
- let localTreeArchiveBuffer;
738
- if (localTreeRoute && !dryRun) {
739
- const packLocalTree = hooks.packLocalTree ?? defaultPackLocalTree;
740
- try {
741
- const packed = await packLocalTree({
742
- root: cwd,
743
- ...(config.subject.localTree?.exclude === undefined ? {} : { extraExclude: config.subject.localTree.exclude }),
744
- ...(config.subject.localTree?.maxArchiveBytes === undefined ? {} : { maxArchiveBytes: config.subject.localTree.maxArchiveBytes })
745
- });
746
- localTreeArchive = packed.archive;
747
- localTreeArchiveBuffer = packed.buffer;
748
- process.stderr.write(`humanish concurrent shared-world local-tree: packed ${packed.archive.fileCount} entries, ${packed.archive.totalBytes} bytes, archiveSha256 ${packed.archive.archiveSha256}`
749
- + `${packed.archive.git ? ` (commit ${packed.archive.git.commit.slice(0, 12)}, ${packed.archive.git.dirty ? "dirty" : "clean"} working tree)` : " (not a git work tree)"}\n`);
750
- }
751
- catch (error) {
752
- return fail("HUMANISH_CONCURRENT_SHARED_WORLD_LAB_FAILED", `local-tree packing failed: ${redactText(scrubKnownValues(toErrorMessage(error)))}`, descriptor.id);
753
- }
754
- }
755
- let receiving;
756
- if (!dryRun && config.comms?.email?.kind === "real") {
757
- try {
758
- receiving = await prepareReceivingRun({ cwd, runId, config, env, participants: actorSpecs.map(spec => spec.laneId), runPaths,
759
- registerSecrets: values => { for (const value of values)
760
- if (value.length >= 4 && !knownSecretValues.includes(value))
761
- knownSecretValues.push(value); }
762
- });
763
- commsArtifactPath = "comms/receiving.json";
764
- }
765
- catch {
766
- return fail("HUMANISH_CONCURRENT_SHARED_WORLD_LAB_INVALID", "Real email setup failed before desktop allocation. Run humanish comms check --online and humanish comms recover to inspect authentication and pending cleanup.", descriptor.id);
767
- }
768
- }
769
- try {
770
- if (!dryRun && planeClass === "provisioned-getHost") {
771
- if (!serve) {
772
- // Defense-in-depth: concurrentSharedWorldValidationReason already required serve above.
773
- return fail("HUMANISH_CONCURRENT_SHARED_WORLD_LAB_INVALID", "the provisioned-getHost concurrent shared-world route requires `subject.serve`.", descriptor.id);
774
- }
775
- let subjectModule;
776
- let subjectDesktop;
777
- // The in-sandbox email catch on the ONE subject sandbox (#297); drained at teardown. Undefined
778
- // unless a comms lab declared it. Hoisted so the finally can drain before the subject is killed.
779
- let deployedComms;
780
- // Background prober dispose signal (FIX-9: cleared in finally).
781
- let proberDisposed = false;
782
- let releaseDispose = () => { };
783
- const disposeSignal = new Promise((resolve) => { releaseDispose = resolve; });
784
- let proberLoop;
785
- const proberSnapshot = async () => {
786
- if (!subjectDesktop)
787
- return;
788
- const timestamp = now();
789
- const idx = snapshotIndex;
790
- snapshotIndex += 1;
791
- const snapshot = await runCheckpointSnapshot({
792
- desktop: subjectDesktop,
793
- snapshotIndex: idx,
794
- name: `state-${idx}`,
795
- checkpoints,
796
- prevDigest: undefined,
797
- scrub: scrubKnownValues,
798
- requestTimeoutMs,
799
- timers
800
- });
801
- stateSnapshots.push({ timestamp, digest: snapshot.digest });
802
- };
803
- try {
804
- subjectModule = await (hooks.loadDesktopModule ?? loadE2BDesktopModule)();
805
- // The ONE subject sandbox: headless service host (no GUI seat). The SUBJECT env is provisioned
806
- // HERE; the actor sandboxes get NONE of it (FIX-10). A custom desktop template (image) is
807
- // honored on BOTH the subject sandbox (here) and every actor sandbox (via runCuaLane, which
808
- // reads the same config); absent keeps the byte-stable Sandbox.create(opts) default.
809
- subjectDesktop = await createDesktopSandbox(subjectModule, {
810
- apiKey: e2bApiKey,
811
- requestTimeoutMs,
812
- timeoutMs: timeoutMs + SUBJECT_PROVISION_BUDGET_MS
813
- + (config.subject.state?.seed ?? []).reduce((sum, step) => sum + (step.timeoutMs ?? DEFAULT_STATE_STEP_TIMEOUT_MS), 0)
814
- + SANDBOX_TIMEOUT_BUFFER_MS,
815
- metadata: {
816
- ...CONCURRENT_SHARED_WORLD_PROVIDER_METADATA,
817
- labId: config.id,
818
- topology: "shared-world",
819
- topologyMode: "concurrent",
820
- role: "subject",
821
- roleCount: String(roles.length)
822
- },
823
- ...(subjectEnvNames.length > 0 || Object.keys(commsEnv).length > 0
824
- ? { envs: { ...Object.fromEntries(subjectEnvNames.map((name) => [name, env[name]])), ...commsEnv } }
825
- : {}),
826
- dpi: 96,
827
- lifecycle: { onTimeout: "kill" }
828
- }, config.execution?.desktop?.template);
829
- subjectSandboxId = subjectDesktop.sandboxId;
830
- // #358 salvage: durable id receipt the moment the subject sandbox exists.
831
- await appendSandboxReceipt(runPaths, { at: new Date().toISOString(), laneId: "subject", sandboxId: subjectSandboxId });
832
- if (hooks.prepareDesktop) {
833
- await hooks.prepareDesktop(subjectDesktop);
834
- }
835
- // Start the in-sandbox email catch BEFORE the subject serve, so the app's send-API base URL
836
- // (injected into its env at create) resolves the moment it boots. Fail closed if the catch can't
837
- // stand up rather than let a comms-declared app silently send real mail to the internet.
838
- if (commsEmail && commsPort !== undefined) {
839
- // A SECOND (0.0.0.0) read-only inbox listener on commsPort+1 so the persona — which lives in a
840
- // DIFFERENT sandbox here — can reach the inbox surface via getHost; capture stays loopback.
841
- deployedComms = await deployCommsCatch(subjectDesktop, { port: commsPort, inboxPort: commsPort + 1, requestTimeoutMs, timers });
842
- if (!deployedComms.ready) {
843
- throw new Error(`comms email catch did not become ready in the subject sandbox (loopback capture ${commsPort} / inbox ${commsPort + 1})`);
844
- }
845
- }
846
- // Provision the ONE shared plane: clone + install/build + seed + serve on 0.0.0.0 + probe
847
- // (clone route), or upload/extract the once-per-run packed archive + the SAME shared serve
848
- // pipeline (local-tree route).
849
- const onSubjectPhase = hooks.onPhase ?? ((event) => {
850
- process.stderr.write(`humanish shared-world (concurrent): ${event.message}${event.durationMs === undefined ? "" : ` (${event.durationMs}ms)`}\n`);
851
- });
852
- if (localTreeRoute) {
853
- await provisionLocalTreeSubject(subjectDesktop, {
854
- archiveBuffer: localTreeArchiveBuffer,
855
- serve,
856
- ...(config.subject.state === undefined ? {} : { state: config.subject.state }),
857
- requestTimeoutMs,
858
- scrub: scrubKnownValues,
859
- onStateStep: (record) => { stateStepRecords.push(record); },
860
- onPhase: onSubjectPhase,
861
- ...timers
862
- });
863
- }
864
- else {
865
- subjectCommit = await provisionCloneSubject(subjectDesktop, {
866
- repo: subjectRepo,
867
- depth: config.subject.clone?.depth ?? 1,
868
- serve,
869
- ...(config.subject.state === undefined ? {} : { state: config.subject.state }),
870
- hasGithubToken,
871
- requestTimeoutMs,
872
- scrub: scrubKnownValues,
873
- onCommit: (commit) => { subjectCommit = commit; },
874
- onStateStep: (record) => { stateStepRecords.push(record); },
875
- onPhase: onSubjectPhase,
876
- ...timers
877
- });
878
- }
879
- // Expose the served port via getHost (FIX-2). Fail closed if the SDK lacks it.
880
- if (typeof subjectDesktop.getHost !== "function") {
881
- throw new Error("the installed @e2b/desktop SDK does not expose getHost(port); the concurrent shared-world route requires it to reach the subject plane");
882
- }
883
- // getHost returns a BARE host (e.g. "3000-<sandboxId>.e2b.app", no scheme); e2b exposes the
884
- // port over https. Normalize to a full URL before the tokenless check + before persisting.
885
- const rawHost = subjectDesktop.getHost(servePort(serve.url));
886
- const hostUrl = /^https?:\/\//i.test(rawHost) ? rawHost : `https://${rawHost}`;
887
- if (!isTokenlessHost(hostUrl)) {
888
- throw new Error("getHost returned a non-tokenless URL; refusing to persist a host URL that may carry a credential (invariant 1)");
889
- }
890
- getHostUrl = hostUrl;
891
- // Persona inbox SURFACE (#297 slice B, shared-world): getHost-expose the read-only inbox listener so
892
- // a persona in a DIFFERENT sandbox can open it; build the serve->getHost origin map (REQUIRED here —
893
- // the app's loopback verify links must be rewritten to a reachable host); provision the surface
894
- // channel; write the EMPTY inbox up front (so /inbox never 404s); and start a render loop that drains
895
- // + re-renders on a cadence. The loop shares the prober's dispose signal (disposed together, before
896
- // the teardown evidence drain), and uses a DEDICATED FakeInbox + cursor (independent of that drain).
897
- if (commsEmail && deployedComms?.inboxPort !== undefined) {
898
- const rawInboxHost = subjectDesktop.getHost(deployedComms.inboxPort);
899
- const inboxHostUrl = /^https?:\/\//i.test(rawInboxHost) ? rawInboxHost : `https://${rawInboxHost}`;
900
- if (!isTokenlessHost(inboxHostUrl)) {
901
- throw new Error("getHost returned a non-tokenless URL for the comms inbox; refusing to advertise it (invariant 1)");
902
- }
903
- commsInboxUrl = `${inboxHostUrl}/inbox`;
904
- commsOriginMap = buildOriginMap({
905
- internalServeUrl: serve.url,
906
- reachableBaseUrl: getHostUrl,
907
- ...(commsEmail.linkOrigin === undefined ? {} : { linkOrigin: commsEmail.linkOrigin })
908
- });
909
- const surfaceRecipients = (commsEmail.recipients ?? [])
910
- .filter((recipient) => recipient.address !== undefined)
911
- .map((recipient) => ({ lane: recipient.lane, address: recipient.address }));
912
- await writeInboxSurface(subjectDesktop, deployedComms.surfaceDir, [], { originMap: commsOriginMap, requestTimeoutMs });
913
- const surfaceDeployed = deployedComms;
914
- const surfaceCadenceMs = 2500;
915
- surfaceLoop = (async () => {
916
- // Full, idempotent rebuild each tick; surfaceRenderedCount advances only on a successful render,
917
- // so a transient failure retries cleanly. Real timer (dispose-interruptible + cleared) — an
918
- // unbounded loop must not busy-spin on the injected instant clock.
919
- for (;;) {
920
- try {
921
- const refreshed = await refreshInboxSurface({
922
- desktop: subjectDesktop,
923
- deployed: surfaceDeployed,
924
- recipients: surfaceRecipients,
925
- sinceCount: surfaceRenderedCount,
926
- originMap: commsOriginMap,
927
- requestTimeoutMs
928
- });
929
- if (refreshed.rendered)
930
- surfaceRenderedCount = refreshed.count;
931
- }
932
- catch {
933
- // Never throw into the render loop; the teardown drain + by-id teardown must still run.
934
- }
935
- if (proberDisposed)
936
- break;
937
- await new Promise((resolve) => {
938
- const timer = setTimeout(resolve, surfaceCadenceMs);
939
- void disposeSignal.then(() => { clearTimeout(timer); resolve(); });
940
- });
941
- if (proberDisposed)
942
- break;
943
- }
944
- })();
945
- }
946
- // Baseline state snapshot, then start the background cadence prober.
947
- await proberSnapshot();
948
- if (options.onObserverReady) {
949
- const inProgressPlaneCommit = localTreeRoute ? localTreeArchive?.git?.commit : subjectCommit;
950
- const inProgressSubject = buildSubjectProvenance({
951
- localTreeRoute,
952
- publicRepo,
953
- subjectCommit: inProgressPlaneCommit,
954
- localTreeArchive,
955
- subjectEnvNames,
956
- state: resolveSubjectState({ declared: config.subject.state, dryRun: false, executed: stateStepRecords })
957
- });
958
- const inProgressBundle = buildConcurrentSharedWorldBundle({
959
- config,
960
- descriptor,
961
- createdAt,
962
- dryRun: false,
963
- inProgress: true,
964
- runId,
965
- source,
966
- roles,
967
- actorSpecs,
968
- actorResults: [],
969
- stateSnapshots,
970
- subject: inProgressSubject,
971
- seedDigest,
972
- ...(inProgressPlaneCommit === undefined ? {} : { subjectCommit: inProgressPlaneCommit }),
973
- hostDigest: hostOriginDigest(getHostUrl)
974
- });
975
- await writeConcurrentRunArtifacts(inProgressBundle, runPaths);
976
- liveObserver = observerResultForConcurrentArtifacts(cwd, runId, artifactRoot, [
977
- "Live concurrent shared-world Observer is attached before final verification; stream auth URLs are runtime-only and are not persisted."
978
- ]);
979
- await options.onObserverReady(liveObserver);
980
- }
981
- proberLoop = (async () => {
982
- while (!proberDisposed) {
983
- let timer;
984
- await Promise.race([
985
- new Promise((resolve) => { timer = setTimeout(resolve, proberCadenceMs); }),
986
- disposeSignal
987
- ]);
988
- if (timer)
989
- clearTimeout(timer); // FIX-9: no dangling prober timer.
990
- if (proberDisposed)
991
- break;
992
- await proberSnapshot().catch(() => undefined);
993
- }
994
- })();
995
- // Launch N actor sandboxes CONCURRENTLY, INDEPENDENT (FIX-11: runCuaLane + mapWithConcurrency,
996
- // NOT runCuaLanes — no pipeline gate / fail-fast). Each actor's window is measured on the ONE
997
- // orchestrator clock (FIX-1). cloneRoute=false + subjectEnvNames=[] keep subject creds out of
998
- // every actor sandbox (FIX-10).
999
- const cuaHooks = {
1000
- ...(hooks.loadDesktopModule ? { loadDesktopModule: hooks.loadDesktopModule } : {}),
1001
- ...(hooks.detachedTimers ? { detachedTimers: hooks.detachedTimers } : {}),
1002
- ...(hooks.env ? { env: hooks.env } : {}),
1003
- ...(hooks.prepareDesktop ? { prepareDesktop: (desktop) => hooks.prepareDesktop(desktop) } : {}),
1004
- onRuntimeStreamReady: (stream) => {
1005
- runtimeStreamUrls.push({ streamId: stream.streamId, url: stream.url });
1006
- if (liveObserver) {
1007
- attachObserverRuntimeStreamUrls(liveObserver, runtimeStreamUrls);
1008
- }
1009
- },
1010
- onRuntimeStreamEnded: (stream) => {
1011
- // Mark, never remove (#357): the tile falls back to recorded evidence and says why.
1012
- for (const entry of runtimeStreamUrls) {
1013
- if (entry.streamId === stream.streamId)
1014
- entry.ended = true;
1015
- }
1016
- if (liveObserver) {
1017
- attachObserverRuntimeStreamUrls(liveObserver, runtimeStreamUrls);
1018
- }
1019
- }
1020
- };
1021
- const baseActorDeps = {
1022
- config,
1023
- descriptor,
1024
- cloneRoute: false,
1025
- subjectEnvNames: [],
1026
- hasGithubToken: false,
1027
- env,
1028
- openaiApiKey,
1029
- e2bApiKey,
1030
- requestTimeoutMs,
1031
- perLaneSandboxMs: timeoutMs + SANDBOX_TIMEOUT_BUFFER_MS,
1032
- timeoutMs,
1033
- laneCount: roles.length,
1034
- artifactRoot: runPaths,
1035
- labCwd: cwd,
1036
- redactScreenshots,
1037
- scrubKnownValues,
1038
- runSession,
1039
- ...(receiving ? { receiving } : {}),
1040
- now,
1041
- hooks: cuaHooks,
1042
- ...(runBudget === undefined ? {} : { runBudget }),
1043
- // Concurrent lanes are independent evidence seats: a requested-vs-verified screen
1044
- // mismatch is recorded as separate facts + a warning instead of failing the lane's
1045
- // device claim closed, so one seat's window-manager drift cannot abort the whole
1046
- // live multi-actor world (the single-lane/fan-out routes keep fail-closed).
1047
- screenMismatchPolicy: "record-evidence"
1048
- };
1049
- actorResults = await mapWithConcurrency(actorSpecs, Math.max(1, concurrency), async (spec, i) => {
1050
- const route = resolveActorSeatUrl(getHostUrl, roles[i]?.entry);
1051
- // Tell this persona its (getHost-reachable) inbox URL — but only when comms is live AND this lane
1052
- // has a declared recipient it can actually receive mail into (else it would stall on an empty
1053
- // inbox). Only the in-sandbox catch exists on this plane; the adopter-hosted catch is the
1054
- // external-public plane's, wired in ITS execution block below (#387).
1055
- const laneSpec = commsEmail && commsInboxUrl && laneHasInboxRecipient(commsEmail, spec.laneId)
1056
- ? withInboxMission(spec, commsInboxUrl, inboxRecipientFor(commsEmail, spec.laneId)?.address)
1057
- : spec;
1058
- const startedAt = now();
1059
- const outcome = await runCuaLane(laneSpec, { ...baseActorDeps, appUrl: route });
1060
- const endedAt = now();
1061
- return { spec, outcome, startedAt, endedAt, route };
1062
- });
1063
- }
1064
- catch (error) {
1065
- runError = redactText(scrubKnownValues(toErrorMessage(error)));
1066
- warnings.push(`Concurrent shared-world run failed before completion: ${runError}`);
1067
- }
1068
- finally {
1069
- // FIX-9: stop the prober, take a final snapshot while the subject is still alive, then tear
1070
- // down the ONE subject sandbox BY id (the actor sandboxes are torn down inside runCuaLane).
1071
- proberDisposed = true;
1072
- releaseDispose();
1073
- if (proberLoop) {
1074
- await proberLoop.catch(() => undefined);
1075
- }
1076
- // Stop the inbox-surface render loop too (shares the prober's dispose signal), before the teardown
1077
- // evidence drain below — so the two in-sandbox reads never overlap and the surface state is final.
1078
- if (surfaceLoop) {
1079
- await surfaceLoop.catch(() => undefined);
1080
- }
1081
- if (subjectDesktop && getHostUrl) {
1082
- await proberSnapshot().catch(() => undefined);
1083
- }
1084
- // Off-app comms evidence (#297): drain everything the in-sandbox catch captured, route it into a
1085
- // host fake inbox addressed to the declared recipients, and write the run-level digest-only thread
1086
- // artifact — while the subject is STILL alive, before it is killed below. Wrapped so a drain error
1087
- // never blocks teardown (invariant: all sandboxes torn down by id in this finally).
1088
- if (commsEmail && deployedComms?.ready && subjectDesktop) {
1089
- try {
1090
- const commsChannel = new FakeInbox();
1091
- const commsInboxes = [];
1092
- for (const recipient of commsEmail.recipients ?? []) {
1093
- if (recipient.address !== undefined) {
1094
- commsInboxes.push(await commsChannel.provisionAddress(recipient.lane, recipient.address));
1095
- }
1096
- }
1097
- const collected = await collectCommsThread({
1098
- desktop: subjectDesktop,
1099
- deployed: deployedComms,
1100
- channel: commsChannel,
1101
- inboxes: commsInboxes,
1102
- requestTimeoutMs
1103
- });
1104
- if (collected.artifact) {
1105
- await writeContainedOutputFile(runPaths, "comms/thread.json", `${JSON.stringify(collected.artifact, null, 2)}\n`, "utf8");
1106
- commsArtifactPath = "comms/thread.json";
1107
- }
1108
- else if (collected.captured > 0) {
1109
- warnings.push(`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.`);
1110
- }
1111
- else {
1112
- // Zero captures is the silent-broken shape (#351): the app never posted to the catch.
1113
- warnings.push(`Comms catch captured ZERO email sends — the app never delivered mail through the catch. Verify the app reads ${commsEmail.injectEnv} for its email API base URL (an SDK that ignores it sends real mail or throws) and that the flow reached an email step.`);
1114
- }
1115
- }
1116
- catch (error) {
1117
- warnings.push(`Comms evidence collection failed (run continues; subject still torn down): ${redactText(scrubKnownValues(toErrorMessage(error)))}`);
1118
- }
1119
- }
1120
- if (subjectDesktop && subjectModule) {
1121
- if (typeof subjectModule.Sandbox.kill === "function") {
1122
- try {
1123
- await subjectModule.Sandbox.kill(subjectDesktop.sandboxId, { requestTimeoutMs: 60_000 });
1124
- subjectKilled = true;
1125
- }
1126
- catch (error) {
1127
- warnings.push(`Subject sandbox teardown failed (server-side kill-on-timeout will reclaim it): ${redactText(scrubKnownValues(toErrorMessage(error)))}`);
1128
- }
1129
- }
1130
- else {
1131
- warnings.push("Installed @e2b/desktop SDK does not expose Sandbox.kill; server-side kill-on-timeout will reclaim the subject sandbox.");
1132
- }
1133
- }
1134
- }
1135
- }
1136
- // EXTERNAL-PUBLIC plane (#164 phase 2): NO subject sandbox, NO getHost, NO prober. The shared plane
1137
- // is the operator-declared public deployment (publicAppUrl); each seat opens it directly and reaches
1138
- // the shared session through the real UI. A host-first barrier extracts the /lobby/CODE from the host
1139
- // seat's CDP-observed URL (onObservedUrl) and threads it into the follower missions; a follower fails
1140
- // closed WITHOUT opening if the host never yields a code within the handoff deadline.
1141
- if (!dryRun && planeClass === "external-public") {
1142
- const cuaHooks = {
1143
- ...(hooks.loadDesktopModule ? { loadDesktopModule: hooks.loadDesktopModule } : {}),
1144
- ...(hooks.detachedTimers ? { detachedTimers: hooks.detachedTimers } : {}),
1145
- ...(hooks.env ? { env: hooks.env } : {}),
1146
- ...(hooks.prepareDesktop ? { prepareDesktop: (desktop) => hooks.prepareDesktop(desktop) } : {}),
1147
- onRuntimeStreamReady: (stream) => {
1148
- runtimeStreamUrls.push({ streamId: stream.streamId, url: stream.url });
1149
- if (liveObserver) {
1150
- attachObserverRuntimeStreamUrls(liveObserver, runtimeStreamUrls);
1151
- }
1152
- },
1153
- onRuntimeStreamEnded: (stream) => {
1154
- // Mark, never remove (#357): the tile falls back to recorded evidence and says why.
1155
- for (const entry of runtimeStreamUrls) {
1156
- if (entry.streamId === stream.streamId)
1157
- entry.ended = true;
1158
- }
1159
- if (liveObserver) {
1160
- attachObserverRuntimeStreamUrls(liveObserver, runtimeStreamUrls);
1161
- }
1162
- }
1163
- };
1164
- const baseActorDeps = {
1165
- config,
1166
- descriptor,
1167
- cloneRoute: false,
1168
- subjectEnvNames: [],
1169
- hasGithubToken: false,
1170
- env,
1171
- openaiApiKey,
1172
- e2bApiKey,
1173
- requestTimeoutMs,
1174
- perLaneSandboxMs: timeoutMs + SANDBOX_TIMEOUT_BUFFER_MS,
1175
- timeoutMs,
1176
- laneCount: roles.length,
1177
- artifactRoot: runPaths,
1178
- labCwd: cwd,
1179
- redactScreenshots,
1180
- // Scrub the latched lobby CODE (known once the host resolves it) from ALL narration.
1181
- scrubKnownValues: scrubKnownValuesWithLobbyCode,
1182
- runSession,
1183
- ...(receiving ? { receiving } : {}),
1184
- now,
1185
- hooks: cuaHooks,
1186
- ...(runBudget === undefined ? {} : { runBudget }),
1187
- screenMismatchPolicy: "record-evidence"
1188
- };
1189
- // Publish an attached live Observer BEFORE fan-out (mirrors the provisioned path).
1190
- if (options.onObserverReady) {
1191
- const inProgressBundle = buildConcurrentSharedWorldBundle({
1192
- config,
1193
- descriptor,
1194
- createdAt,
1195
- dryRun: false,
1196
- inProgress: true,
1197
- runId,
1198
- source,
1199
- roles,
1200
- actorSpecs,
1201
- actorResults: [],
1202
- stateSnapshots: [],
1203
- subject: { source: "app-url", envNames: [], state: { provenance: "external-public" } },
1204
- seedDigest,
1205
- planeClass: "external-public",
1206
- // Pre-fan-out snapshot: no seat has observed an origin yet, so the OBSERVED publicOriginDigest
1207
- // is not available; surface the DECLARED origin for the live Observer's reference.
1208
- ...(declaredOriginDigest === undefined ? {} : { declaredOriginDigest })
1209
- });
1210
- await writeConcurrentRunArtifacts(inProgressBundle, runPaths);
1211
- liveObserver = observerResultForConcurrentArtifacts(cwd, runId, artifactRoot, [
1212
- "Live external-public concurrent shared-world Observer is attached before final verification; stream auth URLs are runtime-only and are not persisted."
1213
- ]);
1214
- await options.onObserverReady(liveObserver);
1215
- }
1216
- // The host-first handoff barrier.
1217
- //
1218
- // TEMPORARY SHIM (tracked by #296): this CDP URL-relay handoff — reading the host's /lobby/CODE off
1219
- // its own browser and threading it into the follower missions — is a temporary coordination shim.
1220
- // It is to be augmented/replaced by the actor message bus (fake SMS/email invite) in #297: the
1221
- // human-realistic version is the HOST SENDING the invite link and followers RECEIVING and tapping
1222
- // it, rather than the orchestrator relaying the code out-of-band.
1223
- const lobbyCodeLatch = deferred();
1224
- const handoffDeadlineMs = hooks.handoffDeadlineMs
1225
- ?? Math.min(timeoutMs, Math.max(DEFAULT_HANDOFF_DEADLINE_MS, Math.floor(timeoutMs * HANDOFF_DEADLINE_BUDGET_FRACTION)));
1226
- let deadlineTimer;
1227
- const deadline = new Promise((_resolve, reject) => {
1228
- deadlineTimer = setTimeout(() => reject(new HandoffTimeoutError(handoffDeadlineMs)), handoffDeadlineMs);
1229
- });
1230
- deadline.catch(() => undefined); // never an unhandled rejection
1231
- // Resolve the host->follower handoff latch from WHICHEVER path sees the code first (CDP url-read,
1232
- // host narration, or vision-off-frame). Idempotent: only the first code wins, and it is also stashed
1233
- // as latchedLobbyCode so it gets scrubbed from any later narration. The latched code and observed URLs
1234
- // are runtime-only and land in persisted METADATA only as digests (origin + convergence). (The code
1235
- // is a shareable game code, not a secret, and it still renders in the host's screenshots, which are
1236
- // full-fidelity unless redactScreenshots is set — the digesting is about narration/URL metadata.)
1237
- const latchLobbyCode = (code, laneIndex) => {
1238
- if (latchedLobbyCode !== undefined)
1239
- return;
1240
- observedLobbyCodes[laneIndex] = code;
1241
- latchedLobbyCode = code;
1242
- if (deadlineTimer) {
1243
- clearTimeout(deadlineTimer);
1244
- deadlineTimer = undefined;
1245
- }
1246
- lobbyCodeLatch.resolve(code);
1247
- };
1248
- // Build an onScreenshot handler that vision-reads the lobby code off THIS seat's own frame (the
1249
- // CDP-independent observation). `done()` short-circuits once this seat has what it needs (the host
1250
- // once latched; a follower once it has recorded its own observed code), `onCode` records/latches the
1251
- // result. One read in flight at a time, bounded by MAX_LOBBY_CODE_VISION_READS so a seat that never
1252
- // reaches a lobby can't rack up unbounded calls (fire-and-forget; the loop never awaits it).
1253
- const makeLobbyCodeVisionReader = (done, onCode) => {
1254
- let inFlight = false;
1255
- let reads = 0;
1256
- return (frame) => {
1257
- if (done() || inFlight || reads >= MAX_LOBBY_CODE_VISION_READS)
1258
- return;
1259
- inFlight = true;
1260
- reads += 1;
1261
- void readLobbyCode(frame, openaiApiKey)
1262
- .then((code) => {
1263
- if (code !== undefined && !done())
1264
- onCode(code);
1265
- })
1266
- .catch(() => undefined)
1267
- .finally(() => {
1268
- inFlight = false;
1269
- });
1270
- };
1271
- };
1272
- const makeLaneObservedUrl = (laneIndex, isHost) => (url) => {
1273
- if (typeof url !== "string" || url.length === 0)
1274
- return;
1275
- observedFinalUrls[laneIndex] = url; // runtime-only; digested to origin, never persisted raw
1276
- const code = extractLobbyCode(url);
1277
- if (code !== undefined) {
1278
- observedLobbyCodes[laneIndex] = code;
1279
- if (isHost)
1280
- latchLobbyCode(code, laneIndex);
1281
- }
1282
- };
1283
- // The HOST lane (which yields the /lobby/CODE the followers wait on) runs on its OWN dedicated
1284
- // slot, and the FOLLOWERS run through a bounded pool of size concurrency-1 (blockers 1 & 4):
1285
- // followers block on `Promise.race([lobbyCodeLatch.promise, deadline])` while holding a worker
1286
- // slot, so if the host lane were scheduled INSIDE the same bounded pool it could be starved (never
1287
- // scheduled among the first `concurrency` workers) and the run would die with a spurious
1288
- // HANDOFF_TIMEOUT (e.g. lanes [p2,p3,host] with concurrency 2). Giving the host its own slot,
1289
- // started IMMEDIATELY and OUTSIDE the follower pool, guarantees it is ALWAYS schedulable regardless
1290
- // of its roster position or of concurrency vs lane count — while total in-flight paid desktops stay
1291
- // ≤ the declared concurrency (host + up to concurrency-1 followers), preserving the spend cap.
1292
- const runHostLane = async (spec, laneIndex) => {
1293
- const onObservedUrl = makeLaneObservedUrl(laneIndex, true);
1294
- // CDP-INDEPENDENT handoff paths (the E2B-desktop CDP url-read the onObservedUrl path relies on is
1295
- // unreliable in practice). Two backups, both resolving the SAME latch; whichever sees the code first
1296
- // wins, all digest-only:
1297
- // (1) onMessage — scan the host's own narration IF it happens to state the lobby URL; and
1298
- // (2) onScreenshot — vision-read the code straight off the host's waiting-room frame. This is the
1299
- // robust one: the code is rendered on screen even when CDP fails AND when the host never
1300
- // narrates it, and — crucially — the host is NOT asked to announce anything, so it keeps
1301
- // running (create -> wait for players -> Start -> play) instead of ending on a stray message.
1302
- const onMessage = (text) => {
1303
- if (latchedLobbyCode !== undefined)
1304
- return;
1305
- const code = extractLobbyCodeFromNarration(text);
1306
- if (code !== undefined)
1307
- latchLobbyCode(code, laneIndex);
1308
- };
1309
- // Vision-read the host's waiting-room frame and LATCH the code for the followers (stops once latched).
1310
- const onScreenshot = makeLobbyCodeVisionReader(() => latchedLobbyCode !== undefined, (code) => latchLobbyCode(code, laneIndex));
1311
- // The host's job includes a long LEGITIMATE idle wait — sitting in the waiting room while the
1312
- // followers provision their own desktops and walk the Join flow (easily 15-30 turns of an
1313
- // unchanging "waiting for players" screen). At the default idle backstop (6) the host would give up
1314
- // before anyone arrives, orphaning the lobby (exactly the earlier failure). Raise the host's idle /
1315
- // no-progress tolerance so it waits patiently; the per-seat timeout still bounds a truly stuck host.
1316
- // Adopter-hosted inbox (#387): the persona is told its address and inbox URL on THIS plane —
1317
- // previously only the provisioned plane's seats ever got the instruction, so external comms
1318
- // ran on no route at all.
1319
- const hostInboxSpec = externalCommsEmail && commsInboxUrl && laneHasInboxRecipient(externalCommsEmail, spec.laneId)
1320
- ? withInboxMission(spec, commsInboxUrl, inboxRecipientFor(externalCommsEmail, spec.laneId)?.address)
1321
- : spec;
1322
- const hostSpec = {
1323
- ...hostInboxSpec,
1324
- idleSteps: spec.idleSteps ?? HOST_WAIT_IDLE_STEPS,
1325
- noProgressSteps: spec.noProgressSteps ?? HOST_WAIT_IDLE_STEPS
1326
- };
1327
- const startedAt = now();
1328
- let outcome;
1329
- try {
1330
- outcome = await runCuaLane(hostSpec, { ...baseActorDeps, appUrl: publicAppUrl, onObservedUrl, onMessage, onScreenshot });
1331
- }
1332
- finally {
1333
- // If the host finished without ever surfacing a code, release followers to fail closed
1334
- // immediately rather than wait the full deadline (a no-op if it already resolved).
1335
- if (!lobbyCodeLatch.settled()) {
1336
- const reason = outcome?.sessionError ?? outcome?.session?.reason ?? "no terminal host outcome was recorded";
1337
- hostHandoffFailure = scrubKnownValuesWithLobbyCode(`Host seat ended before producing a lobby URL: ${reason}`);
1338
- lobbyCodeLatch.reject(new Error(hostHandoffFailure));
1339
- }
1340
- }
1341
- const endedAt = now();
1342
- return { spec, outcome, startedAt, endedAt, route: observedFinalUrls[laneIndex] ?? publicAppUrl };
1343
- };
1344
- const runFollowerLane = async (spec, laneIndex) => {
1345
- const onObservedUrl = makeLaneObservedUrl(laneIndex, false);
1346
- // FOLLOWER: do NOT compose a mission or open the target until the host yields a lobby code.
1347
- let code;
1348
- try {
1349
- code = await Promise.race([lobbyCodeLatch.promise, deadline]);
1350
- }
1351
- catch (error) {
1352
- // An ended host is not a deadline expiry. Preserve its actual failure.
1353
- const timedOut = error instanceof HandoffTimeoutError;
1354
- handoffTimedOut ||= timedOut;
1355
- const reason = scrubKnownValuesWithLobbyCode(toErrorMessage(error));
1356
- const at = now();
1357
- return { spec, outcome: makeBlockedFollowerOutcome(spec, reason, timedOut), startedAt: at, endedAt: at, route: publicAppUrl };
1358
- }
1359
- // Followers also idle-wait — in the waiting room until the host starts, and between rounds. Raise
1360
- // their idle backstop too (less than the host's: they wait less), so a follower that joins ahead of
1361
- // the other does not give up before the game begins. Per-seat timeout still bounds a stuck follower.
1362
- const followerInboxSpec = externalCommsEmail && commsInboxUrl && laneHasInboxRecipient(externalCommsEmail, spec.laneId)
1363
- ? withInboxMission(spec, commsInboxUrl, inboxRecipientFor(externalCommsEmail, spec.laneId)?.address)
1364
- : spec;
1365
- const followerSpec = {
1366
- ...withLobbyCodeMission(followerInboxSpec, code),
1367
- idleSteps: spec.idleSteps ?? FOLLOWER_WAIT_IDLE_STEPS,
1368
- noProgressSteps: spec.noProgressSteps ?? FOLLOWER_WAIT_IDLE_STEPS
1369
- };
1370
- // Independently OBSERVE this follower's own lobby code by vision-reading its waiting-room frame,
1371
- // and record it for the cross-seat convergence proof. This does NOT latch anything (followers gate
1372
- // on the HOST's code, not their own) — it just fills this seat's observedLobbyCodes slot from a
1373
- // reliable signal instead of the flaky CDP url-read, so lobbyConvergenceDigest can prove all seats
1374
- // reached the SAME /lobby/CODE. If a follower somehow joined a DIFFERENT lobby, it reads a different
1375
- // code and convergence correctly fails (no false proof); if it never reads one, the seat stays a
1376
- // hole and convergence is honestly "not observed" for that seat.
1377
- const onScreenshot = makeLobbyCodeVisionReader(() => observedLobbyCodes[laneIndex] !== undefined, (observed) => { observedLobbyCodes[laneIndex] = observed; });
1378
- const startedAt = now();
1379
- const outcome = await runCuaLane(followerSpec, { ...baseActorDeps, appUrl: publicAppUrl, onObservedUrl, onScreenshot });
1380
- const endedAt = now();
1381
- return { spec, outcome, startedAt, endedAt, route: observedFinalUrls[laneIndex] ?? publicAppUrl };
1382
- };
1383
- // Split the roster into the designated host lane and the followers, preserving each follower's
1384
- // ORIGINAL lane index so results land back in lane order (validation guarantees EXACTLY ONE host).
1385
- const hostLaneIndex = roles.findIndex((role) => role.host === true);
1386
- const followerEntries = actorSpecs
1387
- .map((spec, index) => ({ spec, index }))
1388
- .filter(({ index }) => index !== hostLaneIndex);
1389
- const laneResults = new Array(actorSpecs.length);
1390
- try {
1391
- const hostPromise = hostLaneIndex >= 0 && actorSpecs[hostLaneIndex] !== undefined
1392
- ? runHostLane(actorSpecs[hostLaneIndex], hostLaneIndex)
1393
- : undefined;
1394
- const followerResultsPromise = mapWithConcurrency(followerEntries, Math.max(1, concurrency - 1), ({ spec, index }) => runFollowerLane(spec, index));
1395
- const [hostResult, followerResults] = await Promise.all([hostPromise, followerResultsPromise]);
1396
- if (hostResult !== undefined && hostLaneIndex >= 0) {
1397
- laneResults[hostLaneIndex] = hostResult;
1398
- }
1399
- followerEntries.forEach((entry, i) => { laneResults[entry.index] = followerResults[i]; });
1400
- actorResults = laneResults;
1401
- }
1402
- catch (error) {
1403
- runError = redactText(scrubKnownValuesWithLobbyCode(toErrorMessage(error)));
1404
- warnings.push(`External-public concurrent shared-world run failed before completion: ${runError}`);
1405
- }
1406
- finally {
1407
- if (deadlineTimer) {
1408
- clearTimeout(deadlineTimer);
1409
- deadlineTimer = undefined;
1410
- }
1411
- // Adopter-hosted drain (#328/#387): same routing and digest-only artifact as the in-sandbox
1412
- // catch — only the transport differs (HTTP GET /deliveries against the catch the operator
1413
- // runs). In the finally so the evidence survives a failed run; a drain error never masks
1414
- // the run's own outcome.
1415
- if (externalComms && externalCommsEmail) {
1416
- try {
1417
- const commsChannel = new FakeInbox();
1418
- const commsInboxes = [];
1419
- for (const recipient of externalCommsEmail.recipients ?? []) {
1420
- if (recipient.address !== undefined) {
1421
- commsInboxes.push(await commsChannel.provisionAddress(recipient.lane, recipient.address));
1422
- }
1423
- }
1424
- const authToken = externalComms.authTokenEnv === undefined ? undefined : env[externalComms.authTokenEnv];
1425
- const collected = await collectExternalCommsThread({
1426
- external: { ...externalComms, ...(authToken === undefined ? {} : { authToken }) },
1427
- channel: commsChannel,
1428
- inboxes: commsInboxes
1429
- });
1430
- if (collected.artifact) {
1431
- const path = "comms/thread.json";
1432
- await writeContainedOutputFile(runPaths, path, `${JSON.stringify(collected.artifact, null, 2)}\n`, "utf8");
1433
- commsArtifactPath = path;
1434
- }
1435
- else if (collected.captured > 0) {
1436
- warnings.push(`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.`);
1437
- }
1438
- else {
1439
- warnings.push(`Comms catch captured ZERO email sends — your app never delivered mail through the catch at ${externalComms.catchBaseUrl}. Verify the app's email-API base URL points at it and that the flow reached an email step.`);
1440
- }
1441
- }
1442
- catch (error) {
1443
- warnings.push(`Comms evidence collection failed against the adopter-hosted catch (run continues): ${redactText(toErrorMessage(error))}`);
1444
- }
1445
- }
1446
- }
1447
- // Observed-origin convergence proof (blocker 2): the convergence claim is about what the seats
1448
- // OBSERVED, not what was DECLARED. Digest each observing seat's origin and require they AGREE on
1449
- // ONE — that agreement IS the convergence proof and becomes plane.publicOriginDigest. A normal
1450
- // cross-origin redirect (declared apex -> observed www) is therefore tolerated: the seats still
1451
- // converge on ONE observed origin. Leave it undefined (verify fails closed) only if the seats did
1452
- // not converge on a single observed origin (or none observed one).
1453
- const observedOriginDigests = observedFinalUrls
1454
- .filter((url) => typeof url === "string" && url.length > 0)
1455
- .map((url) => hostOriginDigest(url));
1456
- const distinctObservedOrigins = new Set(observedOriginDigests);
1457
- publicOriginDigest = distinctObservedOrigins.size === 1
1458
- ? [...distinctObservedOrigins][0]
1459
- // NOTHING observed (e.g. a handoff-timeout run where no seat ever navigated): fall back to the
1460
- // DECLARED origin so a FAILED run's bundle stays structurally valid (every seat's route then
1461
- // digests to the declared origin too). The run still fails closed for its own reason (HANDOFF_
1462
- // TIMEOUT / no lobby convergence / no overlap-on-pass). GENUINE divergence (≥2 distinct observed
1463
- // origins) leaves it undefined so verify fails closed on the non-convergence.
1464
- : distinctObservedOrigins.size === 0
1465
- ? declaredOriginDigest
1466
- : undefined;
1467
- // Lobby-convergence proof: a digest of the shared /lobby/CODE path iff EVERY seat converged on the
1468
- // SAME code (a follower stuck on "/" yields no code → no false convergence). Digest-only. NOTE:
1469
- // observedLobbyCodes may be a SPARSE array (a seat that never observed a code leaves a hole), and
1470
- // Array.prototype.every SKIPS holes — so count the DEFINED codes explicitly, never rely on every().
1471
- const definedCodes = observedLobbyCodes.filter((code) => code !== undefined);
1472
- const distinctCodes = new Set(definedCodes);
1473
- if (distinctCodes.size === 1 && definedCodes.length === roles.length) {
1474
- lobbyConvergenceDigest = commandDigestOf(`/lobby/${[...distinctCodes][0]}`);
1475
- }
1476
- if (handoffTimedOut && runError === undefined) {
1477
- runError = `The host seat never produced a /lobby/CODE URL within the ${handoffDeadlineMs}ms handoff deadline; follower seats failed closed without opening.`;
1478
- }
1479
- }
1480
- }
1481
- finally {
1482
- try {
1483
- await receiving?.finish();
1484
- }
1485
- catch {
1486
- warnings.push("Email finalization could not complete. Inspect humanish comms recover; provider cleanup remains unresolved.");
1487
- }
1488
- }
1489
- // Subject provenance: external-public is the operator-declared, operator-owned public deployment
1490
- // (neither provisioned nor seeded); the provisioned path builds clone/local-tree provenance.
1491
- const subject = planeClass === "external-public"
1492
- ? { source: "app-url", envNames: [], state: { provenance: "external-public" } }
1493
- : buildSubjectProvenance({
1494
- localTreeRoute,
1495
- publicRepo,
1496
- subjectCommit: localTreeRoute ? localTreeArchive?.git?.commit : subjectCommit,
1497
- localTreeArchive,
1498
- subjectEnvNames,
1499
- state: resolveSubjectState({ declared: config.subject.state, dryRun, executed: stateStepRecords })
1500
- });
1501
- const planeCommit = localTreeRoute ? localTreeArchive?.git?.commit : subjectCommit;
1502
- // Collect per-actor warnings (each lane's own teardown/raw-screenshot notes).
1503
- for (const result of actorResults) {
1504
- warnings.push(...result.outcome.warnings);
1505
- }
1506
- const bundle = buildConcurrentSharedWorldBundle({
1507
- ...(options.lab === undefined ? {} : { lab: options.lab }),
1508
- config,
1509
- descriptor,
1510
- createdAt,
1511
- dryRun,
1512
- runId,
1513
- source,
1514
- roles,
1515
- actorSpecs,
1516
- actorResults,
1517
- stateSnapshots,
1518
- subject,
1519
- seedDigest,
1520
- planeClass,
1521
- ...(planeCommit === undefined ? {} : { subjectCommit: planeCommit }),
1522
- ...(getHostUrl === undefined ? {} : { hostDigest: hostOriginDigest(getHostUrl) }),
1523
- ...(publicOriginDigest === undefined ? {} : { publicOriginDigest }),
1524
- ...(declaredOriginDigest === undefined ? {} : { declaredOriginDigest }),
1525
- ...(lobbyConvergenceDigest === undefined ? {} : { lobbyConvergenceDigest }),
1526
- ...(commsArtifactPath === undefined ? {} : { commsArtifactPath }),
1527
- ...(runError === undefined ? {} : { runError })
1528
- });
1529
- const adapterWarnings = [];
1530
- const scorerResult = await applyBrowserAdapterHooks({
1531
- hooks,
1532
- bundle,
1533
- context: {
1534
- bundle,
1535
- runDir: physicalArtifactRoot,
1536
- labId: config.id,
1537
- runId,
1538
- actor: descriptor.id,
1539
- backend: "concurrent-shared-world",
1540
- dryRun,
1541
- laneCount: roles.length
1542
- },
1543
- sanitize: (text) => redactText(scrubKnownValues(text)),
1544
- warnings: adapterWarnings,
1545
- hookLabel: "sharedWorldHooks",
1546
- ...(options.scorerProvenance === undefined ? {} : { scorerProvenance: options.scorerProvenance })
1547
- });
1548
- if (receiving)
1549
- bundle.commsReceiving = receiving.snapshot();
1550
- await writeConcurrentRunArtifacts(bundle, runPaths);
1551
- // Finalize identity+liveness from the bundle just written. Deliberately here and not inside
1552
- // writeConcurrentRunArtifacts — that writer is shared with the mid-run in-progress flushes, and
1553
- // finalizing there would declare the run finished while it is still going (#455).
1554
- await runStatus.finish({
1555
- ...(bundle.review?.verdict === undefined ? {} : { verdict: bundle.review.verdict }),
1556
- ...(bundle.review?.participants === undefined
1557
- ? {}
1558
- : {
1559
- participants: {
1560
- total: bundle.review.participants.total,
1561
- reachedGoal: bundle.review.participants.reachedGoal,
1562
- ...(bundle.review.participants.reportedFriction === undefined
1563
- ? {}
1564
- : { reportedFriction: bundle.review.participants.reportedFriction })
1565
- }
1566
- }),
1567
- ...(bundle.cost?.estimatedTotalUsd === undefined ? {} : { estimatedCostUsd: bundle.cost.estimatedTotalUsd })
1568
- });
1569
- const observer = await render(cwd, runId, { open: options.open === true });
1570
- if (observer.ok && liveObserver) {
1571
- attachObserverRuntimeStreamUrls(observer, runtimeStreamUrls);
1572
- }
1573
- const roleOk = (result) => {
1574
- if (dryRun)
1575
- return true;
1576
- return actorLanePassed(result);
1577
- };
1578
- // Concurrent "ok": every actor must produce a terminal, engaged PASSED session. This is a
1579
- // harness/session-credibility gate, not mission-completion proof; a failed actor trace cannot
1580
- // make the route green just because the harness got a terminal.
1581
- const swarmRan = !dryRun && actorResults.length === roles.length
1582
- && actorResults.every(actorLanePassed);
1583
- const adapterFailure = adapterScoreFailureMessage(bundle);
1584
- const ok = observer.ok && runError === undefined && (dryRun || swarmRan) && adapterFailure === undefined && scorerResult.declaredVerdictFailure === undefined;
1585
- const overlapProven = !dryRun && actorWindowsOverlap(actorResults);
1586
- const roleResults = actorSpecs.map((spec, index) => {
1587
- const result = actorResults[index];
1588
- const base = { id: spec.laneId, index: index + 1, persona: spec.persona.id };
1589
- if (dryRun || !result) {
1590
- return { ...base, status: "contract_proof_only", ok: dryRun };
1591
- }
1592
- const session = result.outcome.session;
1593
- const thisOk = roleOk(result);
1594
- return {
1595
- ...base,
1596
- status: session ? session.status : "failed",
1597
- ok: thisOk,
1598
- window: { startedAt: result.startedAt, endedAt: result.endedAt },
1599
- ...(session
1600
- ? { session: { status: session.status, completionReason: session.completionReason, reason: session.reason, screenshots: result.outcome.screenshots.length } }
1601
- : {}),
1602
- ...(result.outcome.sandboxId === undefined
1603
- ? {}
1604
- : { sandbox: { sandboxId: result.outcome.sandboxId, killed: result.outcome.killed } }),
1605
- ...(thisOk
1606
- ? {}
1607
- : {
1608
- error: {
1609
- code: "HUMANISH_CONCURRENT_SHARED_WORLD_LAB_FAILED",
1610
- message: result.outcome.sessionError
1611
- ?? (result.outcome.noEngagement
1612
- ? "Actor took no actions and produced no message (likely a blank/still-loading screen); not a credible goal_satisfied."
1613
- : result.outcome.selfReportedBlocker
1614
- ? "Actor reported goal_satisfied while its final message described a blocker or asked for missing instructions; not a credible pass."
1615
- : session?.completionReason === "harness_error"
1616
- ? `Actor seat ended with a harness error: ${session.reason}`
1617
- : "Actor did not produce a terminal session.")
1618
- }
1619
- })
1620
- };
1621
- });
1622
- const errorResult = (() => {
1623
- if (ok)
1624
- return undefined;
1625
- if (handoffTimedOut) {
1626
- // Checked BEFORE the observer failure: the host never yielded a /lobby/CODE within the
1627
- // deadline (followers failed closed without opening), which is the ROOT CAUSE — and it can
1628
- // itself make the Observer unable to render a coherent run. Report the distinct, honest
1629
- // handoff-timeout code rather than a generic observer/run failure.
1630
- return { code: "HUMANISH_CONCURRENT_SHARED_WORLD_LAB_HANDOFF_TIMEOUT", message: runError ?? "The host seat never produced a /lobby/CODE URL within the handoff deadline." };
1631
- }
1632
- if (hostHandoffFailure !== undefined) {
1633
- return { code: "HUMANISH_CONCURRENT_SHARED_WORLD_LAB_FAILED", message: hostHandoffFailure };
1634
- }
1635
- if (!observer.ok) {
1636
- return { code: "HUMANISH_CONCURRENT_SHARED_WORLD_LAB_FAILED", message: observer.error?.message ?? "Observer failed for the concurrent shared-world run." };
1637
- }
1638
- if (runError) {
1639
- return { code: "HUMANISH_CONCURRENT_SHARED_WORLD_LAB_FAILED", message: runError };
1640
- }
1641
- if (adapterFailure !== undefined) {
1642
- return { code: "HUMANISH_CONCURRENT_SHARED_WORLD_LAB_FAILED", message: adapterFailure };
1643
- }
1644
- const passed = roleResults.filter((role) => role.ok).length;
1645
- return { code: "HUMANISH_CONCURRENT_SHARED_WORLD_LAB_FAILED", message: `Concurrent shared-world run did not run coherently: ${passed}/${roles.length} actor(s) reached a terminal, engaged passed session.` };
1646
- })();
1647
- return markFinalizedStudyResult({
1648
- schema: CONCURRENT_SHARED_WORLD_LAB_SCHEMA,
1649
- ok,
1650
- cwd,
1651
- labId: config.id,
1652
- actor: descriptor.id,
1653
- topology: "shared-world",
1654
- topologyMode: "concurrent",
1655
- roleCount: roles.length,
1656
- concurrency,
1657
- dryRun,
1658
- runId,
1659
- ...(getHostUrl === undefined ? {} : { host: getHostUrl }),
1660
- ...(subjectSandboxId === undefined ? {} : { subjectSandbox: { sandboxId: subjectSandboxId, killed: subjectKilled } }),
1661
- ...(dryRun ? {} : { overlapProven }),
1662
- ...(dryRun ? {} : { maxSimultaneousLanes: maxSimultaneousWindows(actorResults) }),
1663
- subject,
1664
- roles: roleResults,
1665
- observer,
1666
- warnings: [...warnings, ...adapterWarnings, ...observer.warnings],
1667
- ...(errorResult === undefined ? {} : { error: errorResult })
1668
- }, runPaths);
1669
- }
1670
- /** Max windows live at the same instant (sweep over start/end points). The honest simultaneity
1671
- * count: lane COUNT says how many seats existed; this says how many ever ran at once. */
1672
- function maxSimultaneousWindows(windows) {
1673
- const points = windows
1674
- .filter((w) => w.endedAt > w.startedAt)
1675
- .flatMap((w) => [{ at: w.startedAt, delta: 1 }, { at: w.endedAt, delta: -1 }]);
1676
- points.sort((a, b) => a.at - b.at || a.delta - b.delta); // end before start at the same instant
1677
- let live = 0;
1678
- let max = 0;
1679
- for (const point of points) {
1680
- live += point.delta;
1681
- if (live > max)
1682
- max = live;
1683
- }
1684
- return max;
1685
- }
1686
- /** True when ≥2 actor windows overlap in time (the proven-concurrency signal). */
1687
- function actorWindowsOverlap(results) {
1688
- for (let i = 0; i < results.length; i += 1) {
1689
- for (let j = i + 1; j < results.length; j += 1) {
1690
- const a = results[i];
1691
- const b = results[j];
1692
- if (a.startedAt < b.endedAt && b.startedAt < a.endedAt) {
1693
- return true;
1694
- }
1695
- }
1696
- }
1697
- return false;
1698
- }
1699
- function actorLanePassed(result) {
1700
- if (!result)
1701
- return false;
1702
- const session = result.outcome.session;
1703
- return session !== undefined
1704
- && session.status === "passed"
1705
- && session.completionReason !== "harness_error"
1706
- && result.outcome.sessionError === undefined
1707
- && !result.outcome.noEngagement
1708
- && !result.outcome.selfReportedBlocker;
1709
- }
1710
- /**
1711
- * Keep three different claims separate in the stakeholder roll-up:
1712
- *
1713
- * - `outcome.ok` says the actor session passed the harness's credibility checks;
1714
- * - `completionReason` says how the participant session ended;
1715
- * - shared-world convergence is reported by the plane-specific summary alongside this line.
1716
- *
1717
- * None of those is adopter-scored proof that the mission text was completed. In particular, a
1718
- * productive `budget_reached` session can coexist with lobby convergence without becoming a
1719
- * `goal_satisfied` result (#364).
1720
- */
1721
- function formatSharedWorldActorOutcomes(outcomes, expectedCount) {
1722
- const passedSessions = outcomes.filter((outcome) => outcome.ok).length;
1723
- const goalSatisfiedSessions = outcomes.filter((outcome) => outcome.ok && outcome.completionReason === "goal_satisfied").length;
1724
- const completionReasonCounts = new Map();
1725
- for (const outcome of outcomes) {
1726
- const reason = outcome.completionReason ?? "not_recorded";
1727
- completionReasonCounts.set(reason, (completionReasonCounts.get(reason) ?? 0) + 1);
1728
- }
1729
- for (let missing = outcomes.length; missing < expectedCount; missing += 1) {
1730
- completionReasonCounts.set("not_recorded", (completionReasonCounts.get("not_recorded") ?? 0) + 1);
1731
- }
1732
- const completionReasons = [...completionReasonCounts.entries()]
1733
- // ASCII contract tokens: compare directly so bundle text is byte-stable across host locales.
1734
- .sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0)
1735
- .map(([reason, count]) => `${reason} ${count}/${expectedCount}`)
1736
- .join(", ");
1737
- return `${passedSessions}/${expectedCount} actor session(s) passed credibility checks; mission endpoint: ${goalSatisfiedSessions}/${expectedCount} ended goal_satisfied; completion reasons: ${completionReasons}`;
1738
- }
1739
- /** Project the concurrent run into a humanish.run-bundle.v1 with the CONCURRENT shared-world block. */
1740
- export function buildConcurrentSharedWorldBundle(args) {
1741
- const { config, descriptor, createdAt, dryRun, actorSpecs, actorResults, roles } = args;
1742
- const inProgress = args.inProgress === true;
1743
- const external = (args.planeClass ?? "provisioned-getHost") === "external-public";
1744
- const simulations = [];
1745
- const streams = [];
1746
- const events = [];
1747
- // Public-safe label only — neither the raw getHost URL (provisioned) nor the raw public origin
1748
- // (external-public) lands in the bundle. The plane identity is a DIGEST (plane.hostDigest on
1749
- // getHost; plane.publicOriginDigest on external-public).
1750
- const appUrl = external ? "[external-public-plane]" : "[provisioned-subject]";
1751
- const planeCommit = external ? undefined : dryRun ? undefined : args.subjectCommit;
1752
- events.push({
1753
- id: "event-000-created",
1754
- at: createdAt,
1755
- level: "info",
1756
- type: "concurrent-shared-world.run.created",
1757
- message: `Created CONCURRENT shared-world run for ${config.id} (actor ${descriptor.id}, ${actorSpecs.length} persona(s) vs ONE shared plane, max ${config.execution?.concurrency ?? actorSpecs.length} concurrent).`
1758
- });
1759
- // Human-readable plane label, byte-stable for the clone route (see shared-world-lab.ts's
1760
- // buildSharedWorldBundle for the same pattern). local-tree has no repo slug: it labels the
1761
- // packed archive instead (archiveSha256 + dirty/clean when the packed root was a git work tree).
1762
- const dryRunPlaneLabel = args.subject.source === "local-tree"
1763
- ? "packed working tree"
1764
- : `clone of ${args.subject.repo}`;
1765
- const livePlaneLabel = args.subject.source === "local-tree"
1766
- ? (args.subject.archiveSha256
1767
- ? `packed working tree (archiveSha256 ${args.subject.archiveSha256}${args.subject.dirty === true ? ", dirty working tree" : args.subject.dirty === false ? ", clean working tree" : ""})`
1768
- : "packed working tree (archive digest unresolved; provisioning failed before resolution)")
1769
- : `clone of ${args.subject.repo}${args.subjectCommit ? `@${args.subjectCommit}` : ""}`;
1770
- // External-public plane provenance is HONESTLY different: an operator-declared, operator-OWNED
1771
- // public deployment humanish neither provisioned nor seeded — NO getHost, NO clone, NO synthetic
1772
- // attestation (claiming synthetic on a real site is a lie). The origin persists digest-only.
1773
- const externalPlaneOwner = config.subject.publicTarget?.owner ?? "(operator-declared)";
1774
- events.push({
1775
- id: "event-001-plane",
1776
- at: createdAt,
1777
- level: "info",
1778
- type: "concurrent-shared-world.plane.provenance",
1779
- message: external
1780
- ? `Shared plane: an EXTERNAL-PUBLIC deployment (operator-attested owner ${externalPlaneOwner}, authorized) used DIRECTLY as the shared plane — NO getHost, clone, subject sandbox, or seed. The harness OBSERVES that each seat reached the operator-declared origin (publicOriginDigest); it did NOT mint or control the plane. Author-trust ownership attestation, NOT a synthetic-data claim.`
1781
- : dryRun
1782
- ? `Shared plane declared: ${dryRunPlaneLabel}, served + getHost-exposed in-sandbox (dry-run contract; nothing ${args.subject.source === "local-tree" ? "packed" : "cloned"}). Seed recipe ${args.seedDigest}; SYNTHETIC subject (author-attested); env names: ${args.subject.envNames?.join(", ") || "none"} (values never persisted).`
1783
- : `Shared plane: ${livePlaneLabel}, served + exposed at the harness-minted getHost URL; seed recipe ${args.seedDigest}; SYNTHETIC subject (author-attested); env names: ${args.subject.envNames?.join(", ") || "none"} (values never persisted).`,
1784
- simId: actorSpecs[0]?.simId ?? "sim-001",
1785
- streamId: actorSpecs[0]?.streamId ?? "stream-001"
1786
- });
1787
- let eventSeq = 2;
1788
- const nextEventId = (suffix) => `event-${String(eventSeq++).padStart(3, "0")}-${suffix}`;
1789
- actorSpecs.forEach((spec, index) => {
1790
- const taxonomy = laneTaxonomyLabel(spec);
1791
- const result = actorResults[index];
1792
- const outcome = result?.outcome;
1793
- const session = outcome?.session;
1794
- const screenshots = outcome?.screenshots ?? [];
1795
- const lastScreenshot = screenshots[screenshots.length - 1];
1796
- // public-safe (origin redacted): external-public seats open the public plane; getHost seats a seat path.
1797
- const route = external ? "[external-public-plane]" : publicSafeRouteLabel(roles[index]?.entry);
1798
- const status = session
1799
- ? session.status
1800
- : outcome?.sessionError
1801
- ? "failed"
1802
- : inProgress
1803
- ? "running"
1804
- : "contract_proof_only";
1805
- const reason = session?.reason
1806
- ?? outcome?.sessionError
1807
- ?? (inProgress
1808
- ? "Actor desktop is running; the attached Observer hydrates the runtime stream URL without persisting it."
1809
- : "Contract actor only: dry-run produced the evidence shape without launching a desktop or spending provider tokens.");
1810
- const traceScreenshotMode = session?.trace.redaction.screenshots;
1811
- // Include `declared` on the no-outcome fallback too (dry-run, skipped lane): otherwise an
1812
- // ABSENT declared means either "the preset rendered faithfully" or "there was no live
1813
- // outcome", and a dry-run bundle keeps the self-confirming shape this field exists to kill.
1814
- const fallbackDeclared = declaredScreenForRender(spec.devicePreset, spec.deviceName, spec.resolution);
1815
- const desktopGeometry = outcome?.desktopGeometry ?? {
1816
- screen: {
1817
- requested: { width: spec.resolution[0], height: spec.resolution[1] },
1818
- ...(fallbackDeclared ? { declared: fallbackDeclared } : {})
1819
- }
1820
- };
1821
- const screenshotMode = traceScreenshotMode === "raw" || traceScreenshotMode === "blurred"
1822
- ? traceScreenshotMode
1823
- : config.policies?.redactScreenshots === true ? "blurred" : "raw";
1824
- simulations.push({
1825
- id: spec.simId,
1826
- index: index + 1,
1827
- personaId: spec.persona.id,
1828
- scenarioId: `concurrent-shared-world-${config.id}`,
1829
- status,
1830
- streamKind: "browser",
1831
- mode: "browser-sim",
1832
- progress: inProgress ? 35 : 100,
1833
- currentStep: reason,
1834
- summary: session
1835
- ? `Persona ${spec.laneId}${taxonomy} (${spec.persona.id}): drove the shared plane concurrently; ${session.completionReason}.`
1836
- : outcome?.sessionError
1837
- ? `Persona ${spec.laneId}${taxonomy} failed before a terminal session verdict: ${outcome.sessionError}`
1838
- : inProgress
1839
- ? `Persona ${spec.laneId}${taxonomy} (${spec.persona.id}) is running against the shared plane.`
1840
- : `Contract persona ${spec.laneId}${taxonomy} (${spec.persona.id}) for ${descriptor.id} against the shared plane at ${appUrl}.`,
1841
- streamIds: [spec.streamId],
1842
- startedAt: createdAt,
1843
- updatedAt: createdAt
1844
- });
1845
- streams.push({
1846
- id: spec.streamId,
1847
- simId: spec.simId,
1848
- ...(spec.assignment === undefined ? {} : { assignment: participantAssignment(spec.assignment) }),
1849
- kind: "browser",
1850
- label: `Concurrent persona ${spec.laneId}${taxonomy} — ${config.id}`,
1851
- status,
1852
- transport: "snapshot",
1853
- updatedAt: createdAt,
1854
- embed: lastScreenshot
1855
- ? { kind: "screenshot", url: lastScreenshot, title: `Shared plane, persona ${spec.laneId} (${screenshotMode})` }
1856
- : { kind: "placeholder", title: `Shared plane, persona ${spec.laneId}` },
1857
- ...(desktopGeometry.viewport === undefined
1858
- ? {}
1859
- : {
1860
- viewport: {
1861
- width: desktopGeometry.viewport.width,
1862
- height: desktopGeometry.viewport.height,
1863
- deviceScaleFactor: desktopGeometry.viewport.deviceScaleFactor,
1864
- isMobile: spec.devicePreset.isMobile
1865
- }
1866
- }),
1867
- desktopGeometry,
1868
- ui: {
1869
- route,
1870
- intent: `Watch persona ${spec.laneId}${taxonomy} (${spec.persona.id}) drive the SHARED plane concurrently with the other personas.`,
1871
- state: reason,
1872
- ...(session ? { actorStatus: session.status } : {}),
1873
- ...(lastScreenshot ? { screenshotUrl: lastScreenshot } : {})
1874
- },
1875
- ...(session ? { actor: session.trace } : {}),
1876
- artifacts: [
1877
- { label: "run bundle", path: "run.json", kind: "bundle" },
1878
- { label: "review", path: "review.md", kind: "review" },
1879
- { label: "events", path: "events.ndjson", kind: "events" },
1880
- ...(session
1881
- ? [{ label: `persona ${spec.laneId} actor trace`, path: spec.traceArtifactPath, kind: "trace" }]
1882
- : []),
1883
- // Run-level comms evidence belongs to the ONE shared app, not a persona — register it once, on
1884
- // the first stream, so the bundle's existence-verify + public-safety scan cover it without
1885
- // double-counting across seats.
1886
- ...(index === 0 && args.commsArtifactPath
1887
- ? [{ label: "comms thread", path: args.commsArtifactPath, kind: "log" }]
1888
- : []),
1889
- ...screenshots.map((screenshot, screenshotIndex) => ({
1890
- label: `persona ${spec.laneId} screenshot ${String(screenshotIndex + 1).padStart(2, "0")} (${screenshotMode})`,
1891
- path: screenshot,
1892
- kind: "screenshot"
1893
- }))
1894
- ]
1895
- });
1896
- for (const warning of outcome?.warnings ?? []) {
1897
- events.push({
1898
- id: nextEventId(`warning-${spec.laneId}`),
1899
- at: createdAt,
1900
- level: "warn",
1901
- type: "concurrent-shared-world.actor.warning",
1902
- message: `Persona ${spec.laneId}: ${warning}`,
1903
- simId: spec.simId,
1904
- streamId: spec.streamId
1905
- });
1906
- }
1907
- if (session) {
1908
- events.push({
1909
- id: nextEventId(`session-${spec.laneId}`),
1910
- at: createdAt,
1911
- level: session.status === "passed" ? "info" : "warn",
1912
- type: `concurrent-shared-world.session.${session.completionReason}`,
1913
- message: `Persona ${spec.laneId}: ${session.status} — ${session.reason}`,
1914
- simId: spec.simId,
1915
- streamId: spec.streamId
1916
- });
1917
- }
1918
- else if (outcome?.sessionError) {
1919
- events.push({
1920
- id: nextEventId(`session-error-${spec.laneId}`),
1921
- at: createdAt,
1922
- level: "error",
1923
- type: "concurrent-shared-world.session.error",
1924
- message: `Persona ${spec.laneId}: ${outcome.sessionError}`,
1925
- simId: spec.simId,
1926
- streamId: spec.streamId
1927
- });
1928
- }
1929
- else if (inProgress) {
1930
- events.push({
1931
- id: nextEventId(`running-${spec.laneId}`),
1932
- at: createdAt,
1933
- level: "info",
1934
- type: "actor.running",
1935
- message: `Persona ${spec.laneId}: desktop actor is running; live stream URL is runtime-only and not persisted.`,
1936
- simId: spec.simId,
1937
- streamId: spec.streamId
1938
- });
1939
- }
1940
- else {
1941
- events.push({
1942
- id: nextEventId(`contract-${spec.laneId}`),
1943
- at: createdAt,
1944
- level: "info",
1945
- type: "concurrent-shared-world.contract.ready",
1946
- message: `Persona ${spec.laneId}: dry-run contract actor ready; switch scenario.mode to live for a real concurrent session.`,
1947
- simId: spec.simId,
1948
- streamId: spec.streamId
1949
- });
1950
- }
1951
- });
1952
- // Build the concurrent shared-world evidence block. routeHostDigest is sha256-16 of the ORIGIN each
1953
- // seat reached: on getHost the seat URL the actor drove (verify confirms == plane.hostDigest); on
1954
- // external-public the seat's CDP-OBSERVED URL origin (verify confirms == plane.publicOriginDigest).
1955
- const fallbackHostDigest = external
1956
- ? (args.publicOriginDigest ?? commandDigestOf("[external-public-plane]"))
1957
- : (args.hostDigest ?? commandDigestOf("[provisioned-subject]"));
1958
- const laneWindows = actorSpecs.map((spec, index) => {
1959
- const result = actorResults[index];
1960
- const session = result?.outcome.session;
1961
- const routeHostDigest = result ? hostOriginDigest(result.route) : fallbackHostDigest;
1962
- return {
1963
- roleId: spec.laneId,
1964
- ...(spec.actorType === undefined ? {} : { actorType: spec.actorType }),
1965
- ...(spec.surface === undefined ? {} : { surface: spec.surface }),
1966
- ...(spec.caseGroup === undefined ? {} : { caseGroup: spec.caseGroup }),
1967
- simId: spec.simId,
1968
- streamId: spec.streamId,
1969
- startedAt: result?.startedAt ?? 0,
1970
- endedAt: result?.endedAt ?? 0,
1971
- verdict: session ? session.status : result?.outcome.sessionError ? "failed" : inProgress ? "running" : "contract_proof_only",
1972
- routeHostDigest,
1973
- ...(planeCommit === undefined ? {} : { commit: planeCommit }),
1974
- seedDigest: args.seedDigest
1975
- };
1976
- });
1977
- // Option A (external-public): NO authoritative shared-state proof — OMIT stateSeries entirely (there
1978
- // is no in-sandbox filesystem to digest; concurrency is proven by temporal co-occupancy + lobby
1979
- // convergence). The provisioned-getHost plane keeps its authoritative in-sandbox checkpoint series.
1980
- const stateSeries = external
1981
- ? undefined
1982
- : dryRun
1983
- ? [{ timestamp: 0, digest: declaredStateDigest(config) }]
1984
- : [...args.stateSnapshots].sort((a, b) => a.timestamp - b.timestamp);
1985
- const outcomes = actorSpecs.map((spec, index) => {
1986
- const result = actorResults[index];
1987
- const session = result?.outcome.session;
1988
- const ok = !dryRun && actorLanePassed(result);
1989
- return {
1990
- roleId: spec.laneId,
1991
- ...(spec.actorType === undefined ? {} : { actorType: spec.actorType }),
1992
- ...(spec.surface === undefined ? {} : { surface: spec.surface }),
1993
- ...(spec.caseGroup === undefined ? {} : { caseGroup: spec.caseGroup }),
1994
- simId: spec.simId,
1995
- streamId: spec.streamId,
1996
- status: session ? session.status : result?.outcome.sessionError ? "failed" : inProgress ? "running" : "contract_proof_only",
1997
- ...(session ? { completionReason: session.completionReason } : {}),
1998
- ok
1999
- };
2000
- });
2001
- // The plane block is plane-class-specific. getHost: harness-minted hostDigest + synthetic
2002
- // attestation. external-public: operator-declared publicOriginDigest, NO hostDigest, NO exposure
2003
- // (claiming synthetic on a real site would be a lie — verify asserts both ABSENT there).
2004
- const plane = external
2005
- ? {
2006
- seedDigest: args.seedDigest,
2007
- envNames: [],
2008
- // publicOriginDigest is the OBSERVED convergence origin; declaredOriginDigest records the
2009
- // operator-declared origin for reference (a redirect makes them differ — not a failure).
2010
- ...(args.publicOriginDigest === undefined ? {} : { publicOriginDigest: args.publicOriginDigest }),
2011
- ...(args.declaredOriginDigest === undefined ? {} : { declaredOriginDigest: args.declaredOriginDigest })
2012
- }
2013
- : {
2014
- ...(planeCommit === undefined ? {} : { commit: planeCommit }),
2015
- seedDigest: args.seedDigest,
2016
- envNames: args.subject.envNames ?? [],
2017
- ...(args.hostDigest === undefined ? {} : { hostDigest: args.hostDigest }),
2018
- exposure: "synthetic"
2019
- };
2020
- const sharedWorld = {
2021
- schema: SHARED_WORLD_SCHEMA,
2022
- topology: "shared-world",
2023
- topologyMode: "concurrent",
2024
- // Byte-stable: the provisioned-getHost plane omits planeClass (absent == provisioned-getHost).
2025
- ...(external ? { planeClass: "external-public" } : {}),
2026
- roleCount: actorSpecs.length,
2027
- plane,
2028
- attributionLimits: external ? [...EXTERNAL_PUBLIC_ATTRIBUTION_LIMITS] : [...CONCURRENT_ATTRIBUTION_LIMITS],
2029
- laneWindows,
2030
- // Option A: external-public carries NO stateSeries.
2031
- ...(stateSeries === undefined ? {} : { stateSeries }),
2032
- outcomes,
2033
- ...(args.lobbyConvergenceDigest === undefined ? {} : { lobbyConvergenceDigest: args.lobbyConvergenceDigest })
2034
- };
2035
- const overlaps = actorWindowsOverlap(actorResults);
2036
- const deltas = (stateSeries ?? []).filter((snapshot, i) => i > 0 && snapshot.digest !== (stateSeries ?? [])[i - 1].digest).length;
2037
- const stateSeriesLabel = external
2038
- ? "stateSeries omitted (no authoritative shared-state proof on the external-public plane)"
2039
- : `stateSeries ${(stateSeries ?? []).length} snapshot(s), ${deltas} delta(s)`;
2040
- const convergenceLabel = external
2041
- ? `; lobby convergence ${args.lobbyConvergenceDigest ? "PROVEN (all seats reached one /lobby/CODE)" : "not observed"}`
2042
- : "";
2043
- // The count that matters is how many lanes were LIVE AT ONCE, not how many lanes exist — a
2044
- // 6-lane run capped at 3 must never read as 6-wide concurrency (#350, the field failure).
2045
- const capForReport = config.execution?.concurrency ?? laneWindows.length;
2046
- const maxLive = maxSimultaneousWindows(laneWindows);
2047
- events.push({
2048
- id: nextEventId("concurrency"),
2049
- at: createdAt,
2050
- level: "info",
2051
- type: "concurrent-shared-world.concurrency",
2052
- message: `Concurrency: ${laneWindows.length} lane(s)${dryRun ? " (dry-run contract; $0)" : `, up to ${maxLive} live at once (cap ${capForReport}), overlap ${overlaps ? "PROVEN" : "not observed"}`}; ${stateSeriesLabel}${convergenceLabel}. Attribution ceiling: ${sharedWorld.attributionLimits.join(", ")}. ${dryRun ? "This contract-only run proves no live concurrency, scale, or adoption." : "This run reports only its own observed overlap and state changes; it does not prove scale, repeatability, or adopter-harness replacement."}`
2053
- });
2054
- // Concurrent verdict: dryRun → contract; else every actor produced a terminal, engaged PASSED
2055
- // session → pass; otherwise fail. Mission endpoint and completion reasons are reported
2056
- // separately below; `outcomes[].ok` is not renamed into mission success (#364).
2057
- const verdict = dryRun
2058
- ? "contract_proof_only"
2059
- : inProgress
2060
- ? "contract_proof_only"
2061
- : (actorResults.length === actorSpecs.length
2062
- && actorResults.every(actorLanePassed)
2063
- ? "pass"
2064
- : "fail");
2065
- const actorOutcomeSummary = formatSharedWorldActorOutcomes(outcomes, actorSpecs.length);
2066
- const review = {
2067
- schema: REVIEW_SCHEMA,
2068
- verdict,
2069
- // Plane-class-aware: the external-public plane has NO getHost/clone/seed and carries NO
2070
- // authoritative state series, so its summary must not claim a getHost-exposed plane (dry-run) nor
2071
- // report "state delta(s) under load" (live) — it reports lobby convergence instead.
2072
- summary: dryRun
2073
- ? external
2074
- ? `Dry-run concurrent shared-world contract: ${actorSpecs.length} persona(s) declared against ONE external-public shared plane (a real public deployment used directly; no getHost/clone/seed); no sandboxes launched, $0 spend.`
2075
- : `Dry-run concurrent shared-world contract: ${actorSpecs.length} persona(s) declared against ONE getHost-exposed plane (${descriptor.id}); no sandboxes launched, $0 spend.`
2076
- : inProgress
2077
- ? `In-progress concurrent shared-world Observer snapshot: ${actorSpecs.length} persona(s) running against ONE shared plane; final verification is pending.`
2078
- : external
2079
- ? `Concurrent shared-world (ONE external-public plane, ${actorSpecs.length} simultaneous personas): swarm ${verdict === "pass" ? "ran coherently" : "did not run coherently"}; ${actorOutcomeSummary}; overlap ${overlaps ? "proven" : "not observed"}; ${args.lobbyConvergenceDigest ? `${actorSpecs.length} seats converged on one lobby` : "lobby convergence not observed"}.`
2080
- : `Concurrent shared-world (ONE plane, ${actorSpecs.length} simultaneous personas): swarm ${verdict === "pass" ? "ran coherently" : "did not run coherently"}; ${actorOutcomeSummary}; overlap ${overlaps ? "proven" : "not observed"}; ${deltas} state delta(s) under load.`,
2081
- gaps: dryRun
2082
- ? ["This dry-run launched no concurrent shared-world session; it proves contract shape only, not live behavior, scale, or adopter-harness replacement."]
2083
- : inProgress
2084
- ? ["Final actor traces, screenshots, state deltas, and verification are pending; this Observer is for live watch only."]
2085
- : actorResults
2086
- .filter((result) => result.outcome.sessionError !== undefined
2087
- || result.outcome.noEngagement
2088
- || result.outcome.selfReportedBlocker
2089
- || result.outcome.session === undefined
2090
- || result.outcome.session.status !== "passed")
2091
- .map((result) => `${result.spec.laneId}: ${result.outcome.sessionError ?? result.outcome.session?.reason ?? "did not pass"}`)
2092
- };
2093
- const anyRaw = actorResults.some((result) => result.outcome.session?.trace.redaction.screenshots === "raw");
2094
- const ranLive = actorResults.some((result) => result.outcome.session !== undefined || result.outcome.sessionError !== undefined);
2095
- return {
2096
- schema: RUN_BUNDLE_SCHEMA,
2097
- ...receivingPublication(args.config, args.dryRun),
2098
- runId: args.runId,
2099
- mode: dryRun ? "dry-run" : "live",
2100
- simCount: actorSpecs.length,
2101
- createdAt,
2102
- cwd: PUBLIC_TARGET_CWD,
2103
- ...(args.lab === undefined ? {} : { lab: args.lab }),
2104
- artifactRoot: path.join(".humanish", "runs", args.runId),
2105
- source: args.source,
2106
- persona: {
2107
- id: actorSpecs[0]?.persona.id ?? "concurrent-persona",
2108
- name: `Concurrent shared-world swarm (${actorSpecs.length} personas)`,
2109
- source: `lab:${config.id}`,
2110
- sourceDigest: actorSpecs[0]?.persona.promptDigest ?? args.seedDigest
2111
- },
2112
- scenario: {
2113
- id: `concurrent-shared-world-${config.id}`,
2114
- title: config.title ?? `Concurrent shared-world: ${config.id}`,
2115
- goal: redactText(actorSpecs[0]?.evidenceInstructions ?? actorSpecs[0]?.instructions ?? "Concurrent shared-world interaction."),
2116
- source: `lab:${config.id}`,
2117
- sourceDigest: actorSpecs[0]?.persona.promptDigest ?? args.seedDigest
2118
- },
2119
- lifecycle: [
2120
- {
2121
- at: createdAt,
2122
- event: "concurrent-shared-world.run.created",
2123
- message: `Created concurrent shared-world run with ONE shared plane and ${actorSpecs.length} simultaneous actor seats (actor ${descriptor.id}).`
2124
- }
2125
- ],
2126
- simulations,
2127
- streams,
2128
- events,
2129
- redaction: {
2130
- status: "passed",
2131
- notes: ranLive
2132
- ? anyRaw
2133
- ? "Typed text recorded as length only and reasoning/messages pass through text redaction. Some personas captured FULL-FIDELITY (raw) screenshots, retained for local use — NOT redacted for publishing; set policies.redactScreenshots: true to blur a share-as-is bundle. stateSeries persists digest-only."
2134
- : "Typed text recorded as length only and reasoning/messages pass through text redaction. Screenshots are blurred at capture (policies.redactScreenshots: true) for a share-as-is bundle. stateSeries persists digest-only."
2135
- : inProgress
2136
- ? "In-progress live Observer snapshot: runtime stream auth URLs are process-local only and are not persisted. Final typed text, traces, and screenshots are pending. stateSeries persists digest-only."
2137
- : "Dry-run concurrent shared-world contract bundle: no sandboxes launched and no screenshots captured. Typed text is recorded as length only and reasoning/messages pass through text redaction whenever a session runs. stateSeries persists digest-only."
2138
- },
2139
- artifacts: {
2140
- run: "run.json",
2141
- reviewJson: "review.json",
2142
- reviewMarkdown: "review.md",
2143
- observerData: "observer/observer-data.json",
2144
- events: "events.ndjson"
2145
- },
2146
- review,
2147
- feedbackCandidates: [],
2148
- // Custom desktop image provenance (subject + every actor sandbox launched on it); omitted on the default.
2149
- ...(config.execution?.desktop?.template === undefined ? {} : { desktopTemplate: config.execution.desktop.template }),
2150
- subject: args.subject,
2151
- attributionClass: "shared-world",
2152
- sharedWorld
2153
- };
2154
- }
2155
- /** The declared (dry-run) state digest: the probe RECIPE (command digests), no run. */
2156
- function declaredStateDigest(config) {
2157
- const probes = config.subject.state?.checkpoint ?? [];
2158
- return combineCheckpointDigest(probes.map((probe) => `${probe.name}=${commandDigestOf(probe.command)}`));
2159
- }
2160
- function renderConcurrentReviewMarkdown(bundle) {
2161
- const plane = bundle.events.find((event) => event.type === "concurrent-shared-world.plane.provenance");
2162
- const concurrency = bundle.events.find((event) => event.type === "concurrent-shared-world.concurrency");
2163
- const sw = bundle.sharedWorld;
2164
- return [
2165
- `# ${bundle.scenario.title}`,
2166
- "",
2167
- `- run: ${bundle.runId}`,
2168
- `- mode: ${bundle.mode}`,
2169
- `- attribution class: ${bundle.attributionClass ?? "isolated"}`,
2170
- `- topology: ${sw?.topology ?? "(none)"} / ${sw?.topologyMode ?? "(none)"}`,
2171
- `- personas: ${sw?.roleCount ?? 0}`,
2172
- `- verdict: ${bundle.review.verdict}`,
2173
- `- summary: ${bundle.review.summary}`,
2174
- ...(plane ? [`- plane: ${plane.message}`] : []),
2175
- ...(concurrency ? [`- concurrency: ${concurrency.message}`] : []),
2176
- ...(sw ? [`- attribution limits: ${sw.attributionLimits.join(", ")}`] : []),
2177
- ...(bundle.review.gaps.length > 0 ? ["", "## Gaps", ...bundle.review.gaps.map((gap) => `- ${gap}`)] : []),
2178
- ""
2179
- ].join("\n");
2180
- }
2181
- //# sourceMappingURL=concurrent-shared-world-lab.js.map