humanish 0.104.3 → 0.106.0

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