@provos/ironcurtain 0.10.0 → 0.12.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 (660) hide show
  1. package/README.md +68 -14
  2. package/dist/auth/oauth-provider.js +1 -4
  3. package/dist/auth/oauth-provider.js.map +1 -1
  4. package/dist/cli-help.d.ts +12 -0
  5. package/dist/cli-help.js +21 -0
  6. package/dist/cli-help.js.map +1 -1
  7. package/dist/cli.js +46 -6
  8. package/dist/cli.js.map +1 -1
  9. package/dist/config/agent-model-guard.d.ts +14 -0
  10. package/dist/config/agent-model-guard.js +24 -0
  11. package/dist/config/agent-model-guard.js.map +1 -0
  12. package/dist/config/config-command.js +277 -7
  13. package/dist/config/config-command.js.map +1 -1
  14. package/dist/config/first-start.js +62 -7
  15. package/dist/config/first-start.js.map +1 -1
  16. package/dist/config/generated/compiled-policy.json +528 -161
  17. package/dist/config/generated/test-scenarios.json +4810 -170
  18. package/dist/config/generated/tool-annotations.json +34 -47
  19. package/dist/config/index.d.ts +58 -0
  20. package/dist/config/index.js +97 -15
  21. package/dist/config/index.js.map +1 -1
  22. package/dist/config/mcp-servers.json +2 -2
  23. package/dist/config/model-provider.d.ts +9 -0
  24. package/dist/config/model-provider.js +13 -0
  25. package/dist/config/model-provider.js.map +1 -1
  26. package/dist/config/paths.d.ts +217 -0
  27. package/dist/config/paths.js +363 -14
  28. package/dist/config/paths.js.map +1 -1
  29. package/dist/config/types.d.ts +7 -1
  30. package/dist/config/user-config.d.ts +101 -6
  31. package/dist/config/user-config.js +145 -31
  32. package/dist/config/user-config.js.map +1 -1
  33. package/dist/config/validate-policy-dir.d.ts +10 -0
  34. package/dist/config/validate-policy-dir.js +59 -0
  35. package/dist/config/validate-policy-dir.js.map +1 -0
  36. package/dist/cron/job-commands.js +22 -2
  37. package/dist/cron/job-commands.js.map +1 -1
  38. package/dist/cron/types.d.ts +16 -2
  39. package/dist/cron/types.js +1 -3
  40. package/dist/cron/types.js.map +1 -1
  41. package/dist/daemon/daemon-command.js +30 -7
  42. package/dist/daemon/daemon-command.js.map +1 -1
  43. package/dist/daemon/ironcurtain-daemon.d.ts +62 -1
  44. package/dist/daemon/ironcurtain-daemon.js +231 -8
  45. package/dist/daemon/ironcurtain-daemon.js.map +1 -1
  46. package/dist/daemon-client/daemon-client.d.ts +123 -0
  47. package/dist/daemon-client/daemon-client.js +324 -0
  48. package/dist/daemon-client/daemon-client.js.map +1 -0
  49. package/dist/docker/adapters/claude-code.js +194 -20
  50. package/dist/docker/adapters/claude-code.js.map +1 -1
  51. package/dist/docker/adapters/codex.d.ts +9 -0
  52. package/dist/docker/adapters/codex.js +297 -0
  53. package/dist/docker/adapters/codex.js.map +1 -0
  54. package/dist/docker/adapters/goose.d.ts +12 -11
  55. package/dist/docker/adapters/goose.js +80 -27
  56. package/dist/docker/adapters/goose.js.map +1 -1
  57. package/dist/docker/agent-adapter.d.ts +183 -5
  58. package/dist/docker/agent-adapter.js +16 -0
  59. package/dist/docker/agent-adapter.js.map +1 -1
  60. package/dist/docker/agent-registry.js +5 -0
  61. package/dist/docker/agent-registry.js.map +1 -1
  62. package/dist/docker/apple-container-manager.d.ts +49 -0
  63. package/dist/docker/apple-container-manager.js +464 -0
  64. package/dist/docker/apple-container-manager.js.map +1 -0
  65. package/dist/docker/code-mode-proxy.d.ts +38 -0
  66. package/dist/docker/code-mode-proxy.js +6 -1
  67. package/dist/docker/code-mode-proxy.js.map +1 -1
  68. package/dist/docker/container-lifecycle.d.ts +2 -2
  69. package/dist/docker/container-lifecycle.js.map +1 -1
  70. package/dist/docker/container-runtime.d.ts +29 -0
  71. package/dist/docker/container-runtime.js +76 -0
  72. package/dist/docker/container-runtime.js.map +1 -0
  73. package/dist/docker/docker-agent-session.d.ts +113 -62
  74. package/dist/docker/docker-agent-session.js +271 -294
  75. package/dist/docker/docker-agent-session.js.map +1 -1
  76. package/dist/docker/docker-infrastructure.d.ts +428 -26
  77. package/dist/docker/docker-infrastructure.js +1005 -70
  78. package/dist/docker/docker-infrastructure.js.map +1 -1
  79. package/dist/docker/docker-manager.d.ts +58 -3
  80. package/dist/docker/docker-manager.js +378 -33
  81. package/dist/docker/docker-manager.js.map +1 -1
  82. package/dist/docker/docker-probe.d.ts +45 -0
  83. package/dist/docker/docker-probe.js +86 -0
  84. package/dist/docker/docker-probe.js.map +1 -0
  85. package/dist/docker/docker-progress-sink.d.ts +43 -0
  86. package/dist/docker/docker-progress-sink.js +299 -0
  87. package/dist/docker/docker-progress-sink.js.map +1 -0
  88. package/dist/docker/mitm-proxy.d.ts +135 -1
  89. package/dist/docker/mitm-proxy.js +611 -25
  90. package/dist/docker/mitm-proxy.js.map +1 -1
  91. package/dist/docker/network-topology.d.ts +81 -0
  92. package/dist/docker/network-topology.js +160 -0
  93. package/dist/docker/network-topology.js.map +1 -0
  94. package/dist/docker/oauth-credentials.d.ts +59 -3
  95. package/dist/docker/oauth-credentials.js +181 -14
  96. package/dist/docker/oauth-credentials.js.map +1 -1
  97. package/dist/docker/oauth-token-manager.js +2 -2
  98. package/dist/docker/oauth-token-manager.js.map +1 -1
  99. package/dist/docker/orientation.d.ts +1 -1
  100. package/dist/docker/package-types.d.ts +1 -1
  101. package/dist/docker/provider-config.d.ts +78 -3
  102. package/dist/docker/provider-config.js +295 -31
  103. package/dist/docker/provider-config.js.map +1 -1
  104. package/dist/docker/provision-lock.d.ts +46 -0
  105. package/dist/docker/provision-lock.js +152 -0
  106. package/dist/docker/provision-lock.js.map +1 -0
  107. package/dist/docker/proxy-tools.d.ts +0 -2
  108. package/dist/docker/proxy-tools.js +2 -7
  109. package/dist/docker/proxy-tools.js.map +1 -1
  110. package/dist/docker/pty-session.d.ts +64 -1
  111. package/dist/docker/pty-session.js +306 -104
  112. package/dist/docker/pty-session.js.map +1 -1
  113. package/dist/docker/pty-types.d.ts +0 -2
  114. package/dist/docker/pty-types.js +0 -2
  115. package/dist/docker/pty-types.js.map +1 -1
  116. package/dist/docker/registry-proxy.d.ts +46 -0
  117. package/dist/docker/registry-proxy.js +255 -1
  118. package/dist/docker/registry-proxy.js.map +1 -1
  119. package/dist/docker/resource-limits.d.ts +134 -0
  120. package/dist/docker/resource-limits.js +203 -0
  121. package/dist/docker/resource-limits.js.map +1 -0
  122. package/dist/docker/spawn-with-idle-timeout.d.ts +39 -0
  123. package/dist/docker/spawn-with-idle-timeout.js +152 -0
  124. package/dist/docker/spawn-with-idle-timeout.js.map +1 -0
  125. package/dist/docker/sse-extractor.d.ts +51 -0
  126. package/dist/docker/sse-extractor.js +303 -0
  127. package/dist/docker/sse-extractor.js.map +1 -0
  128. package/dist/docker/token-stream-bus.d.ts +83 -0
  129. package/dist/docker/token-stream-bus.js +98 -0
  130. package/dist/docker/token-stream-bus.js.map +1 -0
  131. package/dist/docker/token-stream-types.d.ts +71 -0
  132. package/dist/docker/token-stream-types.js +8 -0
  133. package/dist/docker/token-stream-types.js.map +1 -0
  134. package/dist/docker/trajectory-capture.d.ts +79 -0
  135. package/dist/docker/trajectory-capture.js +521 -0
  136. package/dist/docker/trajectory-capture.js.map +1 -0
  137. package/dist/docker/trajectory-reassembler.d.ts +286 -0
  138. package/dist/docker/trajectory-reassembler.js +990 -0
  139. package/dist/docker/trajectory-reassembler.js.map +1 -0
  140. package/dist/docker/trajectory-tap.d.ts +100 -0
  141. package/dist/docker/trajectory-tap.js +410 -0
  142. package/dist/docker/trajectory-tap.js.map +1 -0
  143. package/dist/docker/trajectory-types.d.ts +187 -0
  144. package/dist/docker/trajectory-types.js +43 -0
  145. package/dist/docker/trajectory-types.js.map +1 -0
  146. package/dist/docker/types.d.ts +105 -12
  147. package/dist/doctor/checks.d.ts +171 -0
  148. package/dist/doctor/checks.js +598 -0
  149. package/dist/doctor/checks.js.map +1 -0
  150. package/dist/doctor/doctor-command.d.ts +29 -0
  151. package/dist/doctor/doctor-command.js +190 -0
  152. package/dist/doctor/doctor-command.js.map +1 -0
  153. package/dist/doctor/mcp-liveness.d.ts +28 -0
  154. package/dist/doctor/mcp-liveness.js +92 -0
  155. package/dist/doctor/mcp-liveness.js.map +1 -0
  156. package/dist/doctor/oauth-checks.d.ts +23 -0
  157. package/dist/doctor/oauth-checks.js +157 -0
  158. package/dist/doctor/oauth-checks.js.map +1 -0
  159. package/dist/doctor/output.d.ts +26 -0
  160. package/dist/doctor/output.js +82 -0
  161. package/dist/doctor/output.js.map +1 -0
  162. package/dist/escalation/listener-command.js +0 -3
  163. package/dist/escalation/listener-command.js.map +1 -1
  164. package/dist/event-bus/typed-event-bus.d.ts +26 -0
  165. package/dist/event-bus/typed-event-bus.js +35 -0
  166. package/dist/event-bus/typed-event-bus.js.map +1 -0
  167. package/dist/index.js +54 -21
  168. package/dist/index.js.map +1 -1
  169. package/dist/logger.js +31 -2
  170. package/dist/logger.js.map +1 -1
  171. package/dist/memory/auto-save.d.ts +18 -4
  172. package/dist/memory/auto-save.js +46 -6
  173. package/dist/memory/auto-save.js.map +1 -1
  174. package/dist/memory/memory-policy.d.ts +41 -0
  175. package/dist/memory/memory-policy.js +36 -0
  176. package/dist/memory/memory-policy.js.map +1 -0
  177. package/dist/mux/mux-app.d.ts +2 -0
  178. package/dist/mux/mux-app.js +1 -0
  179. package/dist/mux/mux-app.js.map +1 -1
  180. package/dist/mux/mux-command.d.ts +10 -1
  181. package/dist/mux/mux-command.js +74 -40
  182. package/dist/mux/mux-command.js.map +1 -1
  183. package/dist/mux/mux-renderer.js +2 -12
  184. package/dist/mux/mux-renderer.js.map +1 -1
  185. package/dist/mux/pty-bridge.d.ts +2 -0
  186. package/dist/mux/pty-bridge.js +3 -0
  187. package/dist/mux/pty-bridge.js.map +1 -1
  188. package/dist/mux/session-scanner.js +1 -2
  189. package/dist/mux/session-scanner.js.map +1 -1
  190. package/dist/{pipeline → observability}/llm-logger.d.ts +8 -3
  191. package/dist/{pipeline → observability}/llm-logger.js +8 -3
  192. package/dist/observability/llm-logger.js.map +1 -0
  193. package/dist/observe/observe-command.d.ts +12 -0
  194. package/dist/observe/observe-command.js +285 -0
  195. package/dist/observe/observe-command.js.map +1 -0
  196. package/dist/observe/observe-renderer.d.ts +24 -0
  197. package/dist/observe/observe-renderer.js +87 -0
  198. package/dist/observe/observe-renderer.js.map +1 -0
  199. package/dist/observe/observe-tui-rain.d.ts +42 -0
  200. package/dist/observe/observe-tui-rain.js +531 -0
  201. package/dist/observe/observe-tui-rain.js.map +1 -0
  202. package/dist/observe/observe-tui-text-panel.d.ts +46 -0
  203. package/dist/observe/observe-tui-text-panel.js +525 -0
  204. package/dist/observe/observe-tui-text-panel.js.map +1 -0
  205. package/dist/observe/observe-tui-types.d.ts +270 -0
  206. package/dist/observe/observe-tui-types.js +200 -0
  207. package/dist/observe/observe-tui-types.js.map +1 -0
  208. package/dist/observe/observe-tui-word-scorer.d.ts +143 -0
  209. package/dist/observe/observe-tui-word-scorer.js +572 -0
  210. package/dist/observe/observe-tui-word-scorer.js.map +1 -0
  211. package/dist/observe/observe-tui.d.ts +59 -0
  212. package/dist/observe/observe-tui.js +592 -0
  213. package/dist/observe/observe-tui.js.map +1 -0
  214. package/dist/persona/broad-policy-validator.d.ts +95 -0
  215. package/dist/persona/broad-policy-validator.js +171 -0
  216. package/dist/persona/broad-policy-validator.js.map +1 -0
  217. package/dist/persona/compile-persona-policy.d.ts +54 -2
  218. package/dist/persona/compile-persona-policy.js +29 -4
  219. package/dist/persona/compile-persona-policy.js.map +1 -1
  220. package/dist/persona/event-bus-progress-reporter.d.ts +57 -0
  221. package/dist/persona/event-bus-progress-reporter.js +66 -0
  222. package/dist/persona/event-bus-progress-reporter.js.map +1 -0
  223. package/dist/persona/memory-gate.d.ts +19 -0
  224. package/dist/persona/memory-gate.js +33 -0
  225. package/dist/persona/memory-gate.js.map +1 -0
  226. package/dist/persona/persona-command.js +77 -72
  227. package/dist/persona/persona-command.js.map +1 -1
  228. package/dist/persona/persona-compile-orchestrator.d.ts +101 -0
  229. package/dist/persona/persona-compile-orchestrator.js +504 -0
  230. package/dist/persona/persona-compile-orchestrator.js.map +1 -0
  231. package/dist/persona/persona-service.d.ts +137 -0
  232. package/dist/persona/persona-service.js +263 -0
  233. package/dist/persona/persona-service.js.map +1 -0
  234. package/dist/persona/policy-mutation-audit.d.ts +109 -0
  235. package/dist/persona/policy-mutation-audit.js +179 -0
  236. package/dist/persona/policy-mutation-audit.js.map +1 -0
  237. package/dist/persona/resolve.d.ts +22 -0
  238. package/dist/persona/resolve.js +45 -2
  239. package/dist/persona/resolve.js.map +1 -1
  240. package/dist/persona/rule-delta.d.ts +30 -0
  241. package/dist/persona/rule-delta.js +82 -0
  242. package/dist/persona/rule-delta.js.map +1 -0
  243. package/dist/persona/types.d.ts +28 -0
  244. package/dist/pipeline/annotate.js +10 -2
  245. package/dist/pipeline/annotate.js.map +1 -1
  246. package/dist/pipeline/compile.js +14 -2
  247. package/dist/pipeline/compile.js.map +1 -1
  248. package/dist/pipeline/constitution-compiler.d.ts +25 -14
  249. package/dist/pipeline/constitution-compiler.js +107 -38
  250. package/dist/pipeline/constitution-compiler.js.map +1 -1
  251. package/dist/pipeline/dynamic-list-types.d.ts +3 -6
  252. package/dist/pipeline/dynamic-list-types.js +3 -16
  253. package/dist/pipeline/dynamic-list-types.js.map +1 -1
  254. package/dist/pipeline/pipeline-runner.d.ts +63 -4
  255. package/dist/pipeline/pipeline-runner.js +242 -94
  256. package/dist/pipeline/pipeline-runner.js.map +1 -1
  257. package/dist/pipeline/pipeline-shared.d.ts +33 -4
  258. package/dist/pipeline/pipeline-shared.js +79 -19
  259. package/dist/pipeline/pipeline-shared.js.map +1 -1
  260. package/dist/pipeline/policy-verifier.js +16 -3
  261. package/dist/pipeline/policy-verifier.js.map +1 -1
  262. package/dist/pipeline/proxy-mcp-connections.js +17 -2
  263. package/dist/pipeline/proxy-mcp-connections.js.map +1 -1
  264. package/dist/pipeline/refresh-lists.js +3 -5
  265. package/dist/pipeline/refresh-lists.js.map +1 -1
  266. package/dist/pipeline/scenario-generator.d.ts +4 -8
  267. package/dist/pipeline/scenario-generator.js +13 -46
  268. package/dist/pipeline/scenario-generator.js.map +1 -1
  269. package/dist/sandbox/index.d.ts +29 -0
  270. package/dist/sandbox/index.js +351 -119
  271. package/dist/sandbox/index.js.map +1 -1
  272. package/dist/sandbox/ironcurtain-protocol.d.ts +52 -0
  273. package/dist/sandbox/ironcurtain-protocol.js +185 -0
  274. package/dist/sandbox/ironcurtain-protocol.js.map +1 -0
  275. package/dist/session/agent-session.d.ts +6 -0
  276. package/dist/session/agent-session.js +16 -5
  277. package/dist/session/agent-session.js.map +1 -1
  278. package/dist/session/cli-transport.js +0 -6
  279. package/dist/session/cli-transport.js.map +1 -1
  280. package/dist/session/index.d.ts +35 -14
  281. package/dist/session/index.js +333 -130
  282. package/dist/session/index.js.map +1 -1
  283. package/dist/session/preflight.d.ts +21 -13
  284. package/dist/session/preflight.js +227 -88
  285. package/dist/session/preflight.js.map +1 -1
  286. package/dist/session/prompts.d.ts +2 -5
  287. package/dist/session/prompts.js.map +1 -1
  288. package/dist/session/session-manager.d.ts +61 -6
  289. package/dist/session/session-manager.js +72 -10
  290. package/dist/session/session-manager.js.map +1 -1
  291. package/dist/session/session-metadata.d.ts +21 -5
  292. package/dist/session/session-metadata.js +34 -12
  293. package/dist/session/session-metadata.js.map +1 -1
  294. package/dist/session/types.d.ts +320 -5
  295. package/dist/session/types.js +57 -1
  296. package/dist/session/types.js.map +1 -1
  297. package/dist/signal/bot-command.js +6 -1
  298. package/dist/signal/bot-command.js.map +1 -1
  299. package/dist/signal/setup-signal.js +13 -21
  300. package/dist/signal/setup-signal.js.map +1 -1
  301. package/dist/signal/signal-bot-daemon.d.ts +16 -1
  302. package/dist/signal/signal-bot-daemon.js +39 -27
  303. package/dist/signal/signal-bot-daemon.js.map +1 -1
  304. package/dist/signal/signal-container.d.ts +3 -3
  305. package/dist/signal/signal-container.js +9 -3
  306. package/dist/signal/signal-container.js.map +1 -1
  307. package/dist/skills/discovery.d.ts +77 -0
  308. package/dist/skills/discovery.js +206 -0
  309. package/dist/skills/discovery.js.map +1 -0
  310. package/dist/skills/index.d.ts +4 -0
  311. package/dist/skills/index.js +3 -0
  312. package/dist/skills/index.js.map +1 -0
  313. package/dist/skills/staging.d.ts +28 -0
  314. package/dist/skills/staging.js +68 -0
  315. package/dist/skills/staging.js.map +1 -0
  316. package/dist/skills/types.d.ts +46 -0
  317. package/dist/skills/types.js +2 -0
  318. package/dist/skills/types.js.map +1 -0
  319. package/dist/trusted-process/approval-whitelist.d.ts +0 -2
  320. package/dist/trusted-process/approval-whitelist.js +17 -17
  321. package/dist/trusted-process/approval-whitelist.js.map +1 -1
  322. package/dist/trusted-process/async-mutex.d.ts +32 -0
  323. package/dist/trusted-process/async-mutex.js +50 -0
  324. package/dist/trusted-process/async-mutex.js.map +1 -0
  325. package/dist/trusted-process/audit-log.d.ts +38 -1
  326. package/dist/trusted-process/audit-log.js +82 -13
  327. package/dist/trusted-process/audit-log.js.map +1 -1
  328. package/dist/trusted-process/call-circuit-breaker.d.ts +6 -0
  329. package/dist/trusted-process/call-circuit-breaker.js +23 -3
  330. package/dist/trusted-process/call-circuit-breaker.js.map +1 -1
  331. package/dist/trusted-process/container-command.d.ts +35 -0
  332. package/dist/trusted-process/container-command.js +49 -0
  333. package/dist/trusted-process/container-command.js.map +1 -0
  334. package/dist/trusted-process/control-server.d.ts +101 -0
  335. package/dist/trusted-process/control-server.js +282 -0
  336. package/dist/trusted-process/control-server.js.map +1 -0
  337. package/dist/trusted-process/domain-utils.d.ts +6 -6
  338. package/dist/trusted-process/domain-utils.js +7 -23
  339. package/dist/trusted-process/domain-utils.js.map +1 -1
  340. package/dist/trusted-process/error-prefixes.d.ts +16 -0
  341. package/dist/trusted-process/error-prefixes.js +17 -0
  342. package/dist/trusted-process/error-prefixes.js.map +1 -0
  343. package/dist/trusted-process/index.d.ts +40 -23
  344. package/dist/trusted-process/index.js +131 -225
  345. package/dist/trusted-process/index.js.map +1 -1
  346. package/dist/trusted-process/list-matcher.d.ts +17 -0
  347. package/dist/trusted-process/list-matcher.js +31 -0
  348. package/dist/trusted-process/list-matcher.js.map +1 -0
  349. package/dist/trusted-process/mcp-client-manager.d.ts +35 -6
  350. package/dist/trusted-process/mcp-client-manager.js +40 -45
  351. package/dist/trusted-process/mcp-client-manager.js.map +1 -1
  352. package/dist/trusted-process/mcp-proxy-server.d.ts +31 -120
  353. package/dist/trusted-process/mcp-proxy-server.js +201 -660
  354. package/dist/trusted-process/mcp-proxy-server.js.map +1 -1
  355. package/dist/trusted-process/policy-engine.d.ts +16 -4
  356. package/dist/trusted-process/policy-engine.js +103 -47
  357. package/dist/trusted-process/policy-engine.js.map +1 -1
  358. package/dist/trusted-process/policy-roots.d.ts +6 -0
  359. package/dist/trusted-process/policy-roots.js +20 -0
  360. package/dist/trusted-process/policy-roots.js.map +1 -1
  361. package/dist/trusted-process/sandbox-integration.d.ts +12 -0
  362. package/dist/trusted-process/sandbox-integration.js +18 -0
  363. package/dist/trusted-process/sandbox-integration.js.map +1 -1
  364. package/dist/trusted-process/tcp-server-transport.d.ts +12 -1
  365. package/dist/trusted-process/tcp-server-transport.js +9 -1
  366. package/dist/trusted-process/tcp-server-transport.js.map +1 -1
  367. package/dist/trusted-process/tool-call-coordinator.d.ts +283 -0
  368. package/dist/trusted-process/tool-call-coordinator.js +592 -0
  369. package/dist/trusted-process/tool-call-coordinator.js.map +1 -0
  370. package/dist/trusted-process/tool-call-pipeline.d.ts +213 -0
  371. package/dist/trusted-process/tool-call-pipeline.js +832 -0
  372. package/dist/trusted-process/tool-call-pipeline.js.map +1 -0
  373. package/dist/trusted-process/tool-description-hints.d.ts +1 -1
  374. package/dist/types/argument-roles.d.ts +1 -1
  375. package/dist/types/argument-roles.js +14 -1
  376. package/dist/types/argument-roles.js.map +1 -1
  377. package/dist/types/audit.d.ts +9 -0
  378. package/dist/{session → types}/errors.d.ts +8 -2
  379. package/dist/{session → types}/errors.js +8 -2
  380. package/dist/types/errors.js.map +1 -0
  381. package/dist/types/reserved-servers.d.ts +11 -0
  382. package/dist/types/reserved-servers.js +12 -0
  383. package/dist/types/reserved-servers.js.map +1 -0
  384. package/dist/types/server-listing.d.ts +14 -0
  385. package/dist/types/server-listing.js +2 -0
  386. package/dist/types/server-listing.js.map +1 -0
  387. package/dist/types/slug.d.ts +2 -0
  388. package/dist/types/slug.js +2 -0
  389. package/dist/types/slug.js.map +1 -1
  390. package/dist/types/url-normalize.d.ts +16 -0
  391. package/dist/types/url-normalize.js +33 -0
  392. package/dist/types/url-normalize.js.map +1 -0
  393. package/dist/util/atomic-write.d.ts +31 -0
  394. package/dist/util/atomic-write.js +43 -0
  395. package/dist/util/atomic-write.js.map +1 -0
  396. package/dist/utils/error-message.d.ts +8 -0
  397. package/dist/utils/error-message.js +11 -0
  398. package/dist/utils/error-message.js.map +1 -0
  399. package/dist/utils/exec-error.d.ts +19 -0
  400. package/dist/utils/exec-error.js +16 -0
  401. package/dist/utils/exec-error.js.map +1 -0
  402. package/dist/utils/is-plain-object.d.ts +7 -0
  403. package/dist/utils/is-plain-object.js +12 -0
  404. package/dist/utils/is-plain-object.js.map +1 -0
  405. package/dist/utils/preflight-checks.d.ts +16 -0
  406. package/dist/utils/preflight-checks.js +178 -0
  407. package/dist/utils/preflight-checks.js.map +1 -0
  408. package/dist/web-ui/__tests__/json-rpc-dispatch.test.d.ts +7 -0
  409. package/dist/web-ui/__tests__/json-rpc-dispatch.test.js +725 -0
  410. package/dist/web-ui/__tests__/json-rpc-dispatch.test.js.map +1 -0
  411. package/dist/web-ui/conn-registry.d.ts +14 -0
  412. package/dist/web-ui/conn-registry.js +25 -0
  413. package/dist/web-ui/conn-registry.js.map +1 -0
  414. package/dist/web-ui/dispatch/escalation-dispatch.d.ts +7 -0
  415. package/dist/web-ui/dispatch/escalation-dispatch.js +52 -0
  416. package/dist/web-ui/dispatch/escalation-dispatch.js.map +1 -0
  417. package/dist/web-ui/dispatch/job-dispatch.d.ts +8 -0
  418. package/dist/web-ui/dispatch/job-dispatch.js +83 -0
  419. package/dist/web-ui/dispatch/job-dispatch.js.map +1 -0
  420. package/dist/web-ui/dispatch/persona-dispatch.d.ts +50 -0
  421. package/dist/web-ui/dispatch/persona-dispatch.js +269 -0
  422. package/dist/web-ui/dispatch/persona-dispatch.js.map +1 -0
  423. package/dist/web-ui/dispatch/session-dispatch.d.ts +9 -0
  424. package/dist/web-ui/dispatch/session-dispatch.js +219 -0
  425. package/dist/web-ui/dispatch/session-dispatch.js.map +1 -0
  426. package/dist/web-ui/dispatch/token-stream-dispatch.d.ts +15 -0
  427. package/dist/web-ui/dispatch/token-stream-dispatch.js +55 -0
  428. package/dist/web-ui/dispatch/token-stream-dispatch.js.map +1 -0
  429. package/dist/web-ui/dispatch/types.d.ts +54 -0
  430. package/dist/web-ui/dispatch/types.js +72 -0
  431. package/dist/web-ui/dispatch/types.js.map +1 -0
  432. package/dist/web-ui/dispatch/workflow-dispatch.d.ts +103 -0
  433. package/dist/web-ui/dispatch/workflow-dispatch.js +1036 -0
  434. package/dist/web-ui/dispatch/workflow-dispatch.js.map +1 -0
  435. package/dist/web-ui/json-rpc-dispatch.d.ts +12 -0
  436. package/dist/web-ui/json-rpc-dispatch.js +34 -0
  437. package/dist/web-ui/json-rpc-dispatch.js.map +1 -0
  438. package/dist/web-ui/state-graph.d.ts +13 -0
  439. package/dist/web-ui/state-graph.js +82 -0
  440. package/dist/web-ui/state-graph.js.map +1 -0
  441. package/dist/web-ui/token-stream-bridge.d.ts +76 -0
  442. package/dist/web-ui/token-stream-bridge.js +265 -0
  443. package/dist/web-ui/token-stream-bridge.js.map +1 -0
  444. package/dist/web-ui/web-event-bus.d.ts +176 -0
  445. package/dist/web-ui/web-event-bus.js +19 -0
  446. package/dist/web-ui/web-event-bus.js.map +1 -0
  447. package/dist/web-ui/web-session-transport.d.ts +31 -0
  448. package/dist/web-ui/web-session-transport.js +109 -0
  449. package/dist/web-ui/web-session-transport.js.map +1 -0
  450. package/dist/web-ui/web-ui-server.d.ts +90 -0
  451. package/dist/web-ui/web-ui-server.js +477 -0
  452. package/dist/web-ui/web-ui-server.js.map +1 -0
  453. package/dist/web-ui/web-ui-types.d.ts +418 -0
  454. package/dist/web-ui/web-ui-types.js +32 -0
  455. package/dist/web-ui/web-ui-types.js.map +1 -0
  456. package/dist/web-ui/ws-utils.d.ts +5 -0
  457. package/dist/web-ui/ws-utils.js +12 -0
  458. package/dist/web-ui/ws-utils.js.map +1 -0
  459. package/dist/web-ui-static/assets/index-BD5U6ILh.css +1 -0
  460. package/dist/web-ui-static/assets/index-FMsHWEfK.js +79 -0
  461. package/dist/web-ui-static/assets/orbitron-latin-700-normal-4jsRXGGJ.woff2 +0 -0
  462. package/dist/web-ui-static/assets/orbitron-latin-700-normal-DmYOh-W9.woff +0 -0
  463. package/dist/web-ui-static/index.html +14 -0
  464. package/dist/workflow/artifacts.d.ts +59 -0
  465. package/dist/workflow/artifacts.js +188 -0
  466. package/dist/workflow/artifacts.js.map +1 -0
  467. package/dist/workflow/checkpoint-selection.d.ts +34 -0
  468. package/dist/workflow/checkpoint-selection.js +48 -0
  469. package/dist/workflow/checkpoint-selection.js.map +1 -0
  470. package/dist/workflow/checkpoint.d.ts +35 -0
  471. package/dist/workflow/checkpoint.js +84 -0
  472. package/dist/workflow/checkpoint.js.map +1 -0
  473. package/dist/workflow/cli-shared.d.ts +62 -0
  474. package/dist/workflow/cli-shared.js +117 -0
  475. package/dist/workflow/cli-shared.js.map +1 -0
  476. package/dist/workflow/cli-support.d.ts +92 -0
  477. package/dist/workflow/cli-support.js +421 -0
  478. package/dist/workflow/cli-support.js.map +1 -0
  479. package/dist/workflow/container-snapshots.d.ts +29 -0
  480. package/dist/workflow/container-snapshots.js +137 -0
  481. package/dist/workflow/container-snapshots.js.map +1 -0
  482. package/dist/workflow/daemon-gate-commands.d.ts +21 -0
  483. package/dist/workflow/daemon-gate-commands.js +599 -0
  484. package/dist/workflow/daemon-gate-commands.js.map +1 -0
  485. package/dist/workflow/definition-loader.d.ts +32 -0
  486. package/dist/workflow/definition-loader.js +56 -0
  487. package/dist/workflow/definition-loader.js.map +1 -0
  488. package/dist/workflow/discovery.d.ts +107 -0
  489. package/dist/workflow/discovery.js +263 -0
  490. package/dist/workflow/discovery.js.map +1 -0
  491. package/dist/workflow/errors.d.ts +66 -0
  492. package/dist/workflow/errors.js +49 -0
  493. package/dist/workflow/errors.js.map +1 -0
  494. package/dist/workflow/guards.d.ts +8 -0
  495. package/dist/workflow/guards.js +38 -0
  496. package/dist/workflow/guards.js.map +1 -0
  497. package/dist/workflow/lane-template.d.ts +89 -0
  498. package/dist/workflow/lane-template.js +141 -0
  499. package/dist/workflow/lane-template.js.map +1 -0
  500. package/dist/workflow/lint-integration.d.ts +64 -0
  501. package/dist/workflow/lint-integration.js +56 -0
  502. package/dist/workflow/lint-integration.js.map +1 -0
  503. package/dist/workflow/lint.d.ts +72 -0
  504. package/dist/workflow/lint.js +593 -0
  505. package/dist/workflow/lint.js.map +1 -0
  506. package/dist/workflow/machine-builder.d.ts +178 -0
  507. package/dist/workflow/machine-builder.js +678 -0
  508. package/dist/workflow/machine-builder.js.map +1 -0
  509. package/dist/workflow/message-log.d.ts +99 -0
  510. package/dist/workflow/message-log.js +40 -0
  511. package/dist/workflow/message-log.js.map +1 -0
  512. package/dist/workflow/orchestrator.d.ts +954 -0
  513. package/dist/workflow/orchestrator.js +3594 -0
  514. package/dist/workflow/orchestrator.js.map +1 -0
  515. package/dist/workflow/prompt-builder.d.ts +39 -0
  516. package/dist/workflow/prompt-builder.js +243 -0
  517. package/dist/workflow/prompt-builder.js.map +1 -0
  518. package/dist/workflow/run-state-command.d.ts +9 -0
  519. package/dist/workflow/run-state-command.js +414 -0
  520. package/dist/workflow/run-state-command.js.map +1 -0
  521. package/dist/workflow/run-state-debug-capture.d.ts +40 -0
  522. package/dist/workflow/run-state-debug-capture.js +101 -0
  523. package/dist/workflow/run-state-debug-capture.js.map +1 -0
  524. package/dist/workflow/status-parser.d.ts +68 -0
  525. package/dist/workflow/status-parser.js +274 -0
  526. package/dist/workflow/status-parser.js.map +1 -0
  527. package/dist/workflow/types.d.ts +598 -0
  528. package/dist/workflow/types.js +85 -0
  529. package/dist/workflow/types.js.map +1 -0
  530. package/dist/workflow/validate.d.ts +80 -0
  531. package/dist/workflow/validate.js +788 -0
  532. package/dist/workflow/validate.js.map +1 -0
  533. package/dist/workflow/workflow-command.d.ts +9 -0
  534. package/dist/workflow/workflow-command.js +670 -0
  535. package/dist/workflow/workflow-command.js.map +1 -0
  536. package/dist/workflow/workflow-discovery.d.ts +27 -0
  537. package/dist/workflow/workflow-discovery.js +121 -0
  538. package/dist/workflow/workflow-discovery.js.map +1 -0
  539. package/dist/workflow/workflow-manager.d.ts +156 -0
  540. package/dist/workflow/workflow-manager.js +359 -0
  541. package/dist/workflow/workflow-manager.js.map +1 -0
  542. package/dist/workflow/workflows/design-and-code/README.md +50 -0
  543. package/dist/workflow/workflows/design-and-code/workflow.yaml +169 -0
  544. package/dist/workflow/workflows/deterministic-eval-smoke/scripts/format_report.js +29 -0
  545. package/dist/workflow/workflows/deterministic-eval-smoke/scripts/package.json +5 -0
  546. package/dist/workflow/workflows/deterministic-eval-smoke/scripts/requirements.txt +1 -0
  547. package/dist/workflow/workflows/deterministic-eval-smoke/scripts/run_eval.py +41 -0
  548. package/dist/workflow/workflows/deterministic-eval-smoke/workflow.yaml +57 -0
  549. package/dist/workflow/workflows/deterministic-verdict-smoke/scripts/classify.py +18 -0
  550. package/dist/workflow/workflows/deterministic-verdict-smoke/workflow.yaml +51 -0
  551. package/dist/workflow/workflows/evolve/scripts/LICENSE +201 -0
  552. package/dist/workflow/workflows/evolve/scripts/README.md +314 -0
  553. package/dist/workflow/workflows/evolve/scripts/evolve-brief +16 -0
  554. package/dist/workflow/workflows/evolve/scripts/evolve-cognition +16 -0
  555. package/dist/workflow/workflows/evolve/scripts/evolve-db +16 -0
  556. package/dist/workflow/workflows/evolve/scripts/evolve-eval +16 -0
  557. package/dist/workflow/workflows/evolve/scripts/evolve-files +16 -0
  558. package/dist/workflow/workflows/evolve/scripts/evolve-summary +16 -0
  559. package/dist/workflow/workflows/evolve/scripts/evolve_core/__init__.py +14 -0
  560. package/dist/workflow/workflows/evolve/scripts/evolve_core/algorithms/__init__.py +17 -0
  561. package/dist/workflow/workflows/evolve/scripts/evolve_core/algorithms/base.py +21 -0
  562. package/dist/workflow/workflows/evolve/scripts/evolve_core/algorithms/factory.py +90 -0
  563. package/dist/workflow/workflows/evolve/scripts/evolve_core/algorithms/greedy.py +17 -0
  564. package/dist/workflow/workflows/evolve/scripts/evolve_core/algorithms/island.py +316 -0
  565. package/dist/workflow/workflows/evolve/scripts/evolve_core/algorithms/random.py +18 -0
  566. package/dist/workflow/workflows/evolve/scripts/evolve_core/algorithms/ucb1.py +55 -0
  567. package/dist/workflow/workflows/evolve/scripts/evolve_core/best_snapshot.py +62 -0
  568. package/dist/workflow/workflows/evolve/scripts/evolve_core/cli.py +752 -0
  569. package/dist/workflow/workflows/evolve/scripts/evolve_core/cognition.py +127 -0
  570. package/dist/workflow/workflows/evolve/scripts/evolve_core/database.py +218 -0
  571. package/dist/workflow/workflows/evolve/scripts/evolve_core/diff.py +57 -0
  572. package/dist/workflow/workflows/evolve/scripts/evolve_core/embedding.py +84 -0
  573. package/dist/workflow/workflows/evolve/scripts/evolve_core/file_lock.py +45 -0
  574. package/dist/workflow/workflows/evolve/scripts/evolve_core/run_state.py +343 -0
  575. package/dist/workflow/workflows/evolve/scripts/evolve_core/sampling_config.py +173 -0
  576. package/dist/workflow/workflows/evolve/scripts/evolve_core/structures.py +93 -0
  577. package/dist/workflow/workflows/evolve/scripts/evolve_core/vector_index.py +191 -0
  578. package/dist/workflow/workflows/evolve/scripts/evolve_result.py +1503 -0
  579. package/dist/workflow/workflows/evolve/scripts/requirements.txt +2 -0
  580. package/dist/workflow/workflows/evolve/workflow.yaml +646 -0
  581. package/dist/workflow/workflows/test-email-summary/skills/email-formatting/SKILL.md +55 -0
  582. package/dist/workflow/workflows/test-email-summary/skills/gmail-query-syntax/SKILL.md +63 -0
  583. package/dist/workflow/workflows/test-email-summary/skills/json-schema-emails/SKILL.md +41 -0
  584. package/dist/workflow/workflows/test-email-summary/skills/json-schema-emails/schema.json +34 -0
  585. package/dist/workflow/workflows/test-email-summary/skills/summary-style-guide/SKILL.md +51 -0
  586. package/dist/workflow/workflows/test-email-summary/workflow.yaml +90 -0
  587. package/dist/workflow/workflows/vuln-discovery/skills/harness-design-fuzzing/SKILL.md +206 -0
  588. package/dist/workflow/workflows/vuln-discovery/skills/memory-safety-c-cpp/SKILL.md +167 -0
  589. package/dist/workflow/workflows/vuln-discovery/skills/vulnerability-triage/SKILL.md +189 -0
  590. package/dist/workflow/workflows/vuln-discovery/workflow.yaml +925 -0
  591. package/dist/workflow/worktree.d.ts +43 -0
  592. package/dist/workflow/worktree.js +106 -0
  593. package/dist/workflow/worktree.js.map +1 -0
  594. package/docker/Dockerfile.base +31 -0
  595. package/docker/Dockerfile.base.arm64 +27 -0
  596. package/docker/Dockerfile.claude-code +3 -1
  597. package/docker/Dockerfile.codex +17 -0
  598. package/docker/Dockerfile.goose +3 -1
  599. package/docker/entrypoint-claude-code.sh +7 -0
  600. package/docker/entrypoint-codex.sh +42 -0
  601. package/docker/entrypoint-goose.sh +6 -0
  602. package/docker/entrypoint-uid-remap.sh +93 -0
  603. package/package.json +27 -11
  604. package/packages/web-ui/README.md +113 -0
  605. package/packages/web-ui/docs/workflow-state-machine.png +0 -0
  606. package/src/config/generated/compiled-policy.json +528 -161
  607. package/src/config/generated/test-scenarios.json +4810 -170
  608. package/src/config/generated/tool-annotations.json +34 -47
  609. package/src/config/mcp-servers.json +2 -2
  610. package/src/workflow/workflows/design-and-code/README.md +50 -0
  611. package/src/workflow/workflows/design-and-code/workflow.yaml +169 -0
  612. package/src/workflow/workflows/deterministic-eval-smoke/scripts/format_report.js +29 -0
  613. package/src/workflow/workflows/deterministic-eval-smoke/scripts/package.json +5 -0
  614. package/src/workflow/workflows/deterministic-eval-smoke/scripts/requirements.txt +1 -0
  615. package/src/workflow/workflows/deterministic-eval-smoke/scripts/run_eval.py +41 -0
  616. package/src/workflow/workflows/deterministic-eval-smoke/workflow.yaml +57 -0
  617. package/src/workflow/workflows/deterministic-verdict-smoke/scripts/classify.py +18 -0
  618. package/src/workflow/workflows/deterministic-verdict-smoke/workflow.yaml +51 -0
  619. package/src/workflow/workflows/evolve/scripts/LICENSE +201 -0
  620. package/src/workflow/workflows/evolve/scripts/README.md +314 -0
  621. package/src/workflow/workflows/evolve/scripts/evolve-brief +16 -0
  622. package/src/workflow/workflows/evolve/scripts/evolve-cognition +16 -0
  623. package/src/workflow/workflows/evolve/scripts/evolve-db +16 -0
  624. package/src/workflow/workflows/evolve/scripts/evolve-eval +16 -0
  625. package/src/workflow/workflows/evolve/scripts/evolve-files +16 -0
  626. package/src/workflow/workflows/evolve/scripts/evolve-summary +16 -0
  627. package/src/workflow/workflows/evolve/scripts/evolve_core/__init__.py +14 -0
  628. package/src/workflow/workflows/evolve/scripts/evolve_core/algorithms/__init__.py +17 -0
  629. package/src/workflow/workflows/evolve/scripts/evolve_core/algorithms/base.py +21 -0
  630. package/src/workflow/workflows/evolve/scripts/evolve_core/algorithms/factory.py +90 -0
  631. package/src/workflow/workflows/evolve/scripts/evolve_core/algorithms/greedy.py +17 -0
  632. package/src/workflow/workflows/evolve/scripts/evolve_core/algorithms/island.py +316 -0
  633. package/src/workflow/workflows/evolve/scripts/evolve_core/algorithms/random.py +18 -0
  634. package/src/workflow/workflows/evolve/scripts/evolve_core/algorithms/ucb1.py +55 -0
  635. package/src/workflow/workflows/evolve/scripts/evolve_core/best_snapshot.py +62 -0
  636. package/src/workflow/workflows/evolve/scripts/evolve_core/cli.py +752 -0
  637. package/src/workflow/workflows/evolve/scripts/evolve_core/cognition.py +127 -0
  638. package/src/workflow/workflows/evolve/scripts/evolve_core/database.py +218 -0
  639. package/src/workflow/workflows/evolve/scripts/evolve_core/diff.py +57 -0
  640. package/src/workflow/workflows/evolve/scripts/evolve_core/embedding.py +84 -0
  641. package/src/workflow/workflows/evolve/scripts/evolve_core/file_lock.py +45 -0
  642. package/src/workflow/workflows/evolve/scripts/evolve_core/run_state.py +343 -0
  643. package/src/workflow/workflows/evolve/scripts/evolve_core/sampling_config.py +173 -0
  644. package/src/workflow/workflows/evolve/scripts/evolve_core/structures.py +93 -0
  645. package/src/workflow/workflows/evolve/scripts/evolve_core/vector_index.py +191 -0
  646. package/src/workflow/workflows/evolve/scripts/evolve_result.py +1503 -0
  647. package/src/workflow/workflows/evolve/scripts/requirements.txt +2 -0
  648. package/src/workflow/workflows/evolve/workflow.yaml +646 -0
  649. package/src/workflow/workflows/test-email-summary/skills/email-formatting/SKILL.md +55 -0
  650. package/src/workflow/workflows/test-email-summary/skills/gmail-query-syntax/SKILL.md +63 -0
  651. package/src/workflow/workflows/test-email-summary/skills/json-schema-emails/SKILL.md +41 -0
  652. package/src/workflow/workflows/test-email-summary/skills/json-schema-emails/schema.json +34 -0
  653. package/src/workflow/workflows/test-email-summary/skills/summary-style-guide/SKILL.md +51 -0
  654. package/src/workflow/workflows/test-email-summary/workflow.yaml +90 -0
  655. package/src/workflow/workflows/vuln-discovery/skills/harness-design-fuzzing/SKILL.md +206 -0
  656. package/src/workflow/workflows/vuln-discovery/skills/memory-safety-c-cpp/SKILL.md +167 -0
  657. package/src/workflow/workflows/vuln-discovery/skills/vulnerability-triage/SKILL.md +189 -0
  658. package/src/workflow/workflows/vuln-discovery/workflow.yaml +925 -0
  659. package/dist/pipeline/llm-logger.js.map +0 -1
  660. package/dist/session/errors.js.map +0 -1
@@ -0,0 +1,990 @@
1
+ /**
2
+ * SSE → final-message reassembly for streaming LLM API responses.
3
+ *
4
+ * The reassembler reads SSE wire bytes and produces a body that is
5
+ * byte-identical to the equivalent non-streaming response. The contract
6
+ * is byte fidelity, not structural equivalence: see §6 invariants in
7
+ * docs/designs/mitm-token-trajectory-capture.md.
8
+ *
9
+ * Critical invariant (§6 #1): the values concatenated into the assembled
10
+ * body must be raw wire substrings. JSON.parse is allowed only to read
11
+ * structural fields (`type`, `index`, `delta.type`); the values themselves
12
+ * are spliced from the wire. There is exactly one JSON.stringify-style
13
+ * serialization in this file — and even that is a substring extraction
14
+ * helper for header/usage fields, not a JSON round-trip on captured
15
+ * content.
16
+ */
17
+ import { StringDecoder } from 'node:string_decoder';
18
+ /** Errors thrown by the reassemblers when the stream is unrecoverable. */
19
+ export class ReassemblyError extends Error {
20
+ constructor(message) {
21
+ super(message);
22
+ this.name = 'ReassemblyError';
23
+ }
24
+ }
25
+ /**
26
+ * Thrown by `finalize()` when the stream ended before the provider's
27
+ * terminal event was parsed. This is a transport truncation / upstream
28
+ * abort, NOT a reassembly bug — the caller should classify it as
29
+ * `mid-stream-abort` rather than `reassembly-failure` so disconnects don't
30
+ * pollute reassembly-failure metrics.
31
+ */
32
+ export class TruncatedStreamError extends ReassemblyError {
33
+ constructor(message) {
34
+ super(message);
35
+ this.name = 'TruncatedStreamError';
36
+ }
37
+ }
38
+ /**
39
+ * Provider-agnostic SSE line splitter. Mirrors the line discipline in
40
+ * `sse-extractor.ts` (CRLF or LF treated as a single break) but emits
41
+ * `(eventType, dataPayload)` tuples instead of parsed events. The data
42
+ * payload is the exact bytes after `data: ` — no trim, no parse.
43
+ *
44
+ * Bytes are decoded through a `StringDecoder` so a multibyte UTF-8
45
+ * sequence split across two `feed()` chunks (zlib emits chunks on
46
+ * arbitrary boundaries) is held back until its continuation arrives,
47
+ * rather than each chunk decoding independently to a `U+FFFD`
48
+ * replacement and corrupting the captured body.
49
+ */
50
+ export class SseLineSplitter {
51
+ decoder = new StringDecoder('utf8');
52
+ buffer = '';
53
+ currentEventType = '';
54
+ /** Pending `data:` payload for the in-flight event (multi-line dataspec). */
55
+ currentData = null;
56
+ feed(chunk, sink) {
57
+ this.buffer += this.decoder.write(chunk);
58
+ let pos = 0;
59
+ while (pos < this.buffer.length) {
60
+ const nextLf = this.buffer.indexOf('\n', pos);
61
+ const nextCr = this.buffer.indexOf('\r', pos);
62
+ let lineEnd;
63
+ if (nextLf === -1 && nextCr === -1)
64
+ break;
65
+ if (nextCr === -1)
66
+ lineEnd = nextLf;
67
+ else if (nextLf === -1)
68
+ lineEnd = nextCr;
69
+ else
70
+ lineEnd = Math.min(nextLf, nextCr);
71
+ const line = this.buffer.slice(pos, lineEnd);
72
+ this.processLine(line, sink);
73
+ pos = lineEnd + 1;
74
+ if (this.buffer[lineEnd] === '\r' && pos < this.buffer.length && this.buffer[pos] === '\n') {
75
+ pos++;
76
+ }
77
+ }
78
+ this.buffer = this.buffer.slice(pos);
79
+ }
80
+ /** Force-flush a trailing line buffer (no terminator). */
81
+ flush(sink) {
82
+ // Drain any bytes the decoder is still holding. For a complete stream
83
+ // this is empty; for one truncated mid-multibyte it yields the U+FFFD
84
+ // replacement — acceptable, since a truncated stream fails reassembly
85
+ // anyway (the binary-session model discards it).
86
+ this.buffer += this.decoder.end();
87
+ if (this.buffer.length > 0) {
88
+ this.processLine(this.buffer, sink);
89
+ this.buffer = '';
90
+ }
91
+ // Emit any pending event on EOF.
92
+ if (this.currentData !== null) {
93
+ sink(this.currentEventType, this.currentData);
94
+ this.currentData = null;
95
+ this.currentEventType = '';
96
+ }
97
+ }
98
+ processLine(line, sink) {
99
+ if (line === '') {
100
+ // Empty line = SSE event terminator
101
+ if (this.currentData !== null) {
102
+ sink(this.currentEventType, this.currentData);
103
+ }
104
+ this.currentEventType = '';
105
+ this.currentData = null;
106
+ return;
107
+ }
108
+ if (line.startsWith(':')) {
109
+ // Comment line, ignore
110
+ return;
111
+ }
112
+ if (line.startsWith('event:')) {
113
+ // Per SSE spec, single leading space after `:` is optional and ignored
114
+ let value = line.slice(6);
115
+ if (value.startsWith(' '))
116
+ value = value.slice(1);
117
+ this.currentEventType = value;
118
+ return;
119
+ }
120
+ if (line.startsWith('data:')) {
121
+ let value = line.slice(5);
122
+ if (value.startsWith(' '))
123
+ value = value.slice(1);
124
+ if (this.currentData === null) {
125
+ this.currentData = value;
126
+ }
127
+ else {
128
+ // Concatenate multi-line data with newline (per SSE spec, but
129
+ // both Anthropic and OpenAI emit single-line data).
130
+ this.currentData += '\n' + value;
131
+ }
132
+ return;
133
+ }
134
+ // Ignore id:, retry:, and other unknown fields.
135
+ }
136
+ }
137
+ // =====================================================================
138
+ // Helpers for parsing JSON without losing byte fidelity
139
+ // =====================================================================
140
+ /**
141
+ * Reads a JSON object at `start` of `data`, returning the substring that
142
+ * makes up the value. Tracks brace depth while respecting string escapes
143
+ * — we never need the parsed value, only the raw substring.
144
+ */
145
+ export function readJsonValueSubstring(data, start) {
146
+ // Skip leading whitespace
147
+ let i = start;
148
+ while (i < data.length && /\s/.test(data[i] ?? ''))
149
+ i++;
150
+ if (i >= data.length)
151
+ return undefined;
152
+ const first = data[i];
153
+ if (first === '"') {
154
+ // String value
155
+ const end = findJsonStringEnd(data, i);
156
+ if (end < 0)
157
+ return undefined;
158
+ return data.slice(i, end + 1);
159
+ }
160
+ if (first === '{' || first === '[') {
161
+ const close = first === '{' ? '}' : ']';
162
+ let depth = 0;
163
+ let j = i;
164
+ while (j < data.length) {
165
+ const ch = data[j];
166
+ if (ch === '"') {
167
+ const sEnd = findJsonStringEnd(data, j);
168
+ if (sEnd < 0)
169
+ return undefined;
170
+ j = sEnd + 1;
171
+ continue;
172
+ }
173
+ if (ch === first)
174
+ depth++;
175
+ else if (ch === close) {
176
+ depth--;
177
+ if (depth === 0)
178
+ return data.slice(i, j + 1);
179
+ }
180
+ j++;
181
+ }
182
+ return undefined;
183
+ }
184
+ // Primitive (number, true/false/null)
185
+ let j = i;
186
+ while (j < data.length && !/[,}\]\s]/.test(data[j] ?? ''))
187
+ j++;
188
+ return data.slice(i, j);
189
+ }
190
+ /** Returns the index of the closing `"` of a JSON string starting at `start`. */
191
+ export function findJsonStringEnd(data, start) {
192
+ if (data[start] !== '"')
193
+ return -1;
194
+ let i = start + 1;
195
+ while (i < data.length) {
196
+ const ch = data[i];
197
+ if (ch === '\\') {
198
+ i += 2;
199
+ continue;
200
+ }
201
+ if (ch === '"')
202
+ return i;
203
+ i++;
204
+ }
205
+ return -1;
206
+ }
207
+ /**
208
+ * Finds a top-level field by name in a JSON object and returns the raw
209
+ * substring of its value. Returns `undefined` if not found.
210
+ *
211
+ * Naïve scan that respects string escapes; sufficient for the
212
+ * well-formed Anthropic/OpenAI envelopes we capture.
213
+ */
214
+ export function findFieldValueSubstring(data, fieldName) {
215
+ const needle = `"${fieldName}"`;
216
+ let i = 0;
217
+ while (i < data.length) {
218
+ // Skip strings entirely so a quoted occurrence of fieldName doesn't fool us
219
+ const ch = data[i];
220
+ if (ch === '"') {
221
+ const end = findJsonStringEnd(data, i);
222
+ if (end < 0)
223
+ return undefined;
224
+ // Check whether this string is our key (followed by `:`)
225
+ if (data.slice(i, end + 1) === needle) {
226
+ let j = end + 1;
227
+ while (j < data.length && /\s/.test(data[j] ?? ''))
228
+ j++;
229
+ if (data[j] !== ':') {
230
+ // Not our key (some other string), keep scanning
231
+ i = end + 1;
232
+ continue;
233
+ }
234
+ j++;
235
+ return readJsonValueSubstring(data, j);
236
+ }
237
+ i = end + 1;
238
+ continue;
239
+ }
240
+ i++;
241
+ }
242
+ return undefined;
243
+ }
244
+ /**
245
+ * Replaces the value of a top-level field by substring. Locates
246
+ * `"fieldName"` followed by `:`, finds the existing value's substring,
247
+ * and splices in the new value. The new value MUST already be a valid
248
+ * JSON expression (string-with-quotes, object, array, primitive).
249
+ *
250
+ * A generic top-level-field splice — not provider-specific. Shared by
251
+ * the Anthropic envelope-splice (`content`/`stop_reason`/`usage`) and the
252
+ * Responses envelope-splice (`output`). Returns `body` unchanged if the
253
+ * field is absent.
254
+ */
255
+ export function replaceTopLevelField(body, fieldName, newValue) {
256
+ const needle = `"${fieldName}"`;
257
+ let i = 0;
258
+ while (i < body.length) {
259
+ const ch = body[i];
260
+ if (ch === '"') {
261
+ const end = findJsonStringEnd(body, i);
262
+ if (end < 0)
263
+ return body;
264
+ if (body.slice(i, end + 1) === needle) {
265
+ let j = end + 1;
266
+ while (j < body.length && /\s/.test(body[j] ?? ''))
267
+ j++;
268
+ if (body[j] === ':') {
269
+ j++;
270
+ while (j < body.length && /\s/.test(body[j] ?? ''))
271
+ j++;
272
+ const existing = readJsonValueSubstring(body, j);
273
+ if (existing !== undefined) {
274
+ return body.slice(0, j) + newValue + body.slice(j + existing.length);
275
+ }
276
+ }
277
+ }
278
+ i = end + 1;
279
+ continue;
280
+ }
281
+ i++;
282
+ }
283
+ return body;
284
+ }
285
+ /**
286
+ * Decodes a JSON string literal (the substring including surrounding
287
+ * quotes) into the raw string it represents. Used for `text` /
288
+ * `thinking` / `signature` payloads where the wire bytes carry escapes
289
+ * we must decode before appending to our concatenation buffer.
290
+ */
291
+ export function decodeJsonString(literal) {
292
+ try {
293
+ return JSON.parse(literal);
294
+ }
295
+ catch {
296
+ return '';
297
+ }
298
+ }
299
+ /**
300
+ * Re-encodes a string as a JSON string literal (including quotes).
301
+ * Used when assembling the final body — we accept JSON.stringify of a
302
+ * single string value because the captured `text` payload was decoded
303
+ * from the same wire encoding; re-encoding restores byte fidelity to
304
+ * the equivalent non-streaming response (which carries the standard
305
+ * JSON encoding).
306
+ *
307
+ * This is the ONE place strings are JSON-encoded in this file.
308
+ * `tool_use.input` and `tool_calls[].function.arguments` are NEVER
309
+ * encoded this way — they're spliced as raw wire substrings.
310
+ */
311
+ export function encodeJsonString(value) {
312
+ return JSON.stringify(value);
313
+ }
314
+ // =====================================================================
315
+ // Scalar-peek helpers (provider-neutral, pure substring readers)
316
+ // =====================================================================
317
+ /**
318
+ * Reads the `type` discriminator of an SSE data payload. The `type`
319
+ * field in `data` is the authoritative event identifier across both the
320
+ * Anthropic typed-event and OpenAI named-event streams; the SSE
321
+ * `event:` line is advisory.
322
+ */
323
+ export function peekTypeField(data) {
324
+ return peekStringField(data, 'type');
325
+ }
326
+ /** Reads a top-level JSON string field, decoded. Returns `undefined` if absent or not a string. */
327
+ export function peekStringField(data, name) {
328
+ const raw = findFieldValueSubstring(data, name);
329
+ if (raw === undefined || !raw.startsWith('"'))
330
+ return undefined;
331
+ return decodeJsonString(raw);
332
+ }
333
+ /** Reads a top-level JSON number field. Returns `undefined` if absent or non-finite. */
334
+ export function peekNumberField(data, name) {
335
+ const raw = findFieldValueSubstring(data, name);
336
+ if (raw === undefined)
337
+ return undefined;
338
+ const n = Number(raw);
339
+ return Number.isFinite(n) ? n : undefined;
340
+ }
341
+ // =====================================================================
342
+ // Abstract SSE reassembler base
343
+ // =====================================================================
344
+ /**
345
+ * Shared SSE reassembler skeleton. Owns the boilerplate every provider's
346
+ * state machine needs — splitter feeding, event log, started-at clock,
347
+ * fail latching, and the finalize gating — and exposes three seams the
348
+ * subclasses implement:
349
+ *
350
+ * - `dispatch(eventType, data)`: route a single SSE event into provider
351
+ * state. May throw to latch a fatal reassembly failure.
352
+ * - `terminalSeen()`: true once the provider's terminal event was
353
+ * parsed (`message_stop`, `[DONE]`, or `response.completed`). Gates
354
+ * both finalize success and the `canFinalize()` lifecycle signal.
355
+ * - `assembleResult()`: build the final body + structured fields. May
356
+ * throw `ReassemblyError`; the base does NOT catch it so the tap can
357
+ * distinguish a reassembly failure from success.
358
+ *
359
+ * The base intentionally holds NO envelope-splice machinery — the
360
+ * providers assemble differently (Anthropic splices a message envelope,
361
+ * the OpenAI Responses reassembler assembles from item-done payloads), so
362
+ * that logic stays subclass-private.
363
+ */
364
+ export class AbstractSseReassembler {
365
+ splitter = new SseLineSplitter();
366
+ events = [];
367
+ startedAt = Date.now();
368
+ failed = false;
369
+ failureReason;
370
+ push(chunk) {
371
+ if (this.failed)
372
+ return;
373
+ this.splitter.feed(chunk, (eventType, data) => this.onEvent(eventType, data));
374
+ }
375
+ finalize() {
376
+ this.splitter.flush((eventType, data) => this.onEvent(eventType, data));
377
+ if (this.failed) {
378
+ throw new ReassemblyError(this.failureReason ?? 'reassembly failed');
379
+ }
380
+ if (!this.terminalSeen()) {
381
+ throw new TruncatedStreamError('stream ended without terminal event');
382
+ }
383
+ return this.assembleResult();
384
+ }
385
+ /**
386
+ * True once the terminal event has been parsed. Lets the tap finalize a
387
+ * complete-but-socket-aborted stream instead of poisoning it.
388
+ */
389
+ canFinalize() {
390
+ return this.terminalSeen();
391
+ }
392
+ onEvent(eventType, data) {
393
+ this.events.push({ eventType, dataUtf8: data, offsetMs: Date.now() - this.startedAt });
394
+ if (this.failed)
395
+ return;
396
+ try {
397
+ this.dispatch(eventType, data);
398
+ }
399
+ catch (err) {
400
+ this.failed = true;
401
+ this.failureReason = err instanceof Error ? err.message : String(err);
402
+ }
403
+ }
404
+ }
405
+ export class AnthropicReassembler extends AbstractSseReassembler {
406
+ /**
407
+ * Envelope captured from `message_start.message` — used to extract
408
+ * top-level fields (id, type, role, model, etc.). We splice the
409
+ * assembled content array between the message-envelope prefix and
410
+ * suffix so the assembled body is byte-identical to a non-streaming
411
+ * response.
412
+ */
413
+ messageEnvelope;
414
+ blocks = new Map();
415
+ stopReason;
416
+ stopSequence;
417
+ usageRaw;
418
+ providerRequestId;
419
+ modelFingerprint;
420
+ receivedMessageStop = false;
421
+ terminalSeen() {
422
+ return this.receivedMessageStop;
423
+ }
424
+ assembleResult() {
425
+ if (!this.messageEnvelope) {
426
+ throw new ReassemblyError('anthropic stream missing message_start envelope');
427
+ }
428
+ const bodyUtf8 = this.assembleBody();
429
+ const usage = this.parseUsage();
430
+ return {
431
+ bodyUtf8,
432
+ providerRequestId: this.providerRequestId,
433
+ stopReason: this.stopReason,
434
+ modelFingerprint: this.modelFingerprint,
435
+ usage,
436
+ events: this.events,
437
+ };
438
+ }
439
+ dispatch(eventType, data) {
440
+ // The `type` field in `data` is the authoritative event identifier;
441
+ // SSE `event:` lines are advisory.
442
+ const type = peekTypeField(data) ?? eventType;
443
+ switch (type) {
444
+ case 'message_start':
445
+ this.onMessageStart(data);
446
+ return;
447
+ case 'content_block_start':
448
+ this.onContentBlockStart(data);
449
+ return;
450
+ case 'content_block_delta':
451
+ this.onContentBlockDelta(data);
452
+ return;
453
+ case 'content_block_stop':
454
+ this.onContentBlockStop(data);
455
+ return;
456
+ case 'message_delta':
457
+ this.onMessageDelta(data);
458
+ return;
459
+ case 'message_stop':
460
+ this.receivedMessageStop = true;
461
+ return;
462
+ case 'ping':
463
+ case 'error':
464
+ default:
465
+ // Other events (ping, error, unknown) are recorded in this.events
466
+ // but don't affect reassembly. An error event leaves the stream
467
+ // incomplete — message_stop is what gates success.
468
+ return;
469
+ }
470
+ }
471
+ onMessageStart(data) {
472
+ const messageRaw = findFieldValueSubstring(data, 'message');
473
+ if (!messageRaw) {
474
+ throw new ReassemblyError('message_start missing `message` field');
475
+ }
476
+ this.messageEnvelope = messageRaw;
477
+ const id = peekStringField(messageRaw, 'id');
478
+ if (id)
479
+ this.providerRequestId = id;
480
+ }
481
+ onContentBlockStart(data) {
482
+ const index = peekNumberField(data, 'index');
483
+ const blockRaw = findFieldValueSubstring(data, 'content_block');
484
+ if (index === undefined || blockRaw === undefined) {
485
+ throw new ReassemblyError('content_block_start missing index or content_block');
486
+ }
487
+ const kind = peekStringField(blockRaw, 'type');
488
+ let state;
489
+ if (kind === 'text') {
490
+ state = { kind: 'text', openRaw: blockRaw, textChunks: [] };
491
+ }
492
+ else if (kind === 'tool_use') {
493
+ const idRaw = findFieldValueSubstring(blockRaw, 'id');
494
+ const nameRaw = findFieldValueSubstring(blockRaw, 'name');
495
+ state = { kind: 'tool_use', openRaw: blockRaw, partialJsonChunks: [], idRaw, nameRaw };
496
+ }
497
+ else if (kind === 'thinking') {
498
+ state = { kind: 'thinking', openRaw: blockRaw, thinkingChunks: [], signatureChunks: [] };
499
+ }
500
+ else if (kind === 'redacted_thinking') {
501
+ state = { kind: 'redacted_thinking', openRaw: blockRaw };
502
+ }
503
+ else {
504
+ state = { kind: 'other', openRaw: blockRaw, rawDeltas: [] };
505
+ }
506
+ this.blocks.set(index, state);
507
+ }
508
+ onContentBlockDelta(data) {
509
+ const index = peekNumberField(data, 'index');
510
+ const deltaRaw = findFieldValueSubstring(data, 'delta');
511
+ if (index === undefined || deltaRaw === undefined) {
512
+ throw new ReassemblyError('content_block_delta missing index or delta');
513
+ }
514
+ const block = this.blocks.get(index);
515
+ if (!block) {
516
+ throw new ReassemblyError(`content_block_delta for unknown index ${index}`);
517
+ }
518
+ const deltaType = peekStringField(deltaRaw, 'type');
519
+ if (block.kind === 'text' && deltaType === 'text_delta') {
520
+ const literal = findFieldValueSubstring(deltaRaw, 'text');
521
+ if (literal)
522
+ block.textChunks.push(decodeJsonString(literal));
523
+ return;
524
+ }
525
+ if (block.kind === 'tool_use' && deltaType === 'input_json_delta') {
526
+ const literal = findFieldValueSubstring(deltaRaw, 'partial_json');
527
+ if (literal) {
528
+ // partial_json is a JSON string literal on the wire whose decoded
529
+ // value is a fragment of the final tool_use.input JSON object.
530
+ // We decode to obtain the raw fragment and accumulate it verbatim
531
+ // — concatenation reconstructs the original input bytes (§5).
532
+ block.partialJsonChunks.push(decodeJsonString(literal));
533
+ }
534
+ return;
535
+ }
536
+ if (block.kind === 'thinking') {
537
+ if (deltaType === 'thinking_delta') {
538
+ const literal = findFieldValueSubstring(deltaRaw, 'thinking');
539
+ if (literal)
540
+ block.thinkingChunks.push(decodeJsonString(literal));
541
+ return;
542
+ }
543
+ if (deltaType === 'signature_delta') {
544
+ const literal = findFieldValueSubstring(deltaRaw, 'signature');
545
+ if (literal)
546
+ block.signatureChunks.push(decodeJsonString(literal));
547
+ return;
548
+ }
549
+ }
550
+ if (block.kind === 'other') {
551
+ block.rawDeltas.push(deltaRaw);
552
+ return;
553
+ }
554
+ // Unknown delta type for known block: record but don't fail (the
555
+ // wire format may add new delta types over time).
556
+ }
557
+ onContentBlockStop(data) {
558
+ const index = peekNumberField(data, 'index');
559
+ if (index === undefined) {
560
+ throw new ReassemblyError('content_block_stop missing index');
561
+ }
562
+ if (!this.blocks.has(index)) {
563
+ throw new ReassemblyError(`content_block_stop for unknown index ${index}`);
564
+ }
565
+ }
566
+ onMessageDelta(data) {
567
+ const deltaRaw = findFieldValueSubstring(data, 'delta');
568
+ if (deltaRaw) {
569
+ const stopReasonRaw = findFieldValueSubstring(deltaRaw, 'stop_reason');
570
+ if (stopReasonRaw !== undefined) {
571
+ if (stopReasonRaw.startsWith('"')) {
572
+ this.stopReason = decodeJsonString(stopReasonRaw);
573
+ }
574
+ }
575
+ const stopSequenceRaw = findFieldValueSubstring(deltaRaw, 'stop_sequence');
576
+ if (stopSequenceRaw !== undefined) {
577
+ this.stopSequence = stopSequenceRaw;
578
+ }
579
+ }
580
+ const usageRaw = findFieldValueSubstring(data, 'usage');
581
+ if (usageRaw)
582
+ this.usageRaw = usageRaw;
583
+ }
584
+ /**
585
+ * Assembles the final non-streaming message body from the captured
586
+ * state. Constructed via raw substring concatenation — see §6 invariant
587
+ * #1.
588
+ *
589
+ * Strategy: the `message` envelope from `message_start` already has
590
+ * the shape of a non-streaming response, but with empty content,
591
+ * stop_reason: null, stop_sequence: null, and a partial usage. We
592
+ * mutate three fields by substring replacement:
593
+ * - "content":[] → "content":[<blocks>]
594
+ * - "stop_reason":null → "stop_reason":"<reason>"
595
+ * - "stop_sequence":null → "stop_sequence":<value>
596
+ * - "usage":{<initial>} → "usage":<final-from-message_delta>
597
+ */
598
+ assembleBody() {
599
+ if (!this.messageEnvelope) {
600
+ throw new ReassemblyError('cannot assemble: missing envelope');
601
+ }
602
+ let body = this.messageEnvelope;
603
+ body = replaceTopLevelField(body, 'content', this.assembleContentArray());
604
+ if (this.stopReason !== undefined) {
605
+ body = replaceTopLevelField(body, 'stop_reason', encodeJsonString(this.stopReason));
606
+ }
607
+ if (this.stopSequence !== undefined) {
608
+ body = replaceTopLevelField(body, 'stop_sequence', this.stopSequence);
609
+ }
610
+ if (this.usageRaw !== undefined) {
611
+ body = replaceTopLevelField(body, 'usage', this.usageRaw);
612
+ }
613
+ return body;
614
+ }
615
+ assembleContentArray() {
616
+ const sortedIndices = [...this.blocks.keys()].sort((a, b) => a - b);
617
+ const parts = [];
618
+ for (const idx of sortedIndices) {
619
+ const block = this.blocks.get(idx);
620
+ if (!block)
621
+ continue;
622
+ parts.push(this.assembleBlock(block));
623
+ }
624
+ return `[${parts.join(',')}]`;
625
+ }
626
+ assembleBlock(block) {
627
+ if (block.kind === 'text') {
628
+ const text = block.textChunks.join('');
629
+ return `{"type":"text","text":${encodeJsonString(text)}}`;
630
+ }
631
+ if (block.kind === 'tool_use') {
632
+ const inputJson = block.partialJsonChunks.join('') || '{}';
633
+ // The block's openRaw carries id and name; reuse them as raw
634
+ // substrings to preserve byte fidelity (e.g., id ordering).
635
+ const idPart = block.idRaw ?? '""';
636
+ const namePart = block.nameRaw ?? '""';
637
+ return `{"type":"tool_use","id":${idPart},"name":${namePart},"input":${inputJson}}`;
638
+ }
639
+ if (block.kind === 'thinking') {
640
+ const thinking = block.thinkingChunks.join('');
641
+ const signature = block.signatureChunks.join('');
642
+ return `{"type":"thinking","thinking":${encodeJsonString(thinking)},"signature":${encodeJsonString(signature)}}`;
643
+ }
644
+ if (block.kind === 'redacted_thinking') {
645
+ return block.openRaw;
646
+ }
647
+ return block.openRaw;
648
+ }
649
+ parseUsage() {
650
+ if (!this.usageRaw)
651
+ return undefined;
652
+ try {
653
+ const parsed = JSON.parse(this.usageRaw);
654
+ return parsed;
655
+ }
656
+ catch {
657
+ return undefined;
658
+ }
659
+ }
660
+ }
661
+ /**
662
+ * Reassembler for the OpenAI Responses API stream that Codex emits on
663
+ * chatgpt.com. Named `response.*` events; an item tree addressed by
664
+ * `output_index` (and content parts by `content_index`).
665
+ *
666
+ * The terminal `response.completed` carries the FULL response envelope
667
+ * (~35 fields: status, usage, instructions, tools, ...) but with its
668
+ * `output` array EMPTY. So the final body is the VERBATIM terminal
669
+ * envelope (zero re-encode, all fields preserved) with its empty
670
+ * `"output":[]` spliced to the assembled `response.output_item.done`
671
+ * payloads (each a COMPLETE item JSON object on the wire, spliced
672
+ * VERBATIM, in output_index order). providerRequestId/stopReason/usage
673
+ * derive from that same envelope. The accumulated `output_text` deltas
674
+ * are cross-checked against the item-done text (delta-join must equal
675
+ * the item text) — a mismatch throws so the binary-session model poisons
676
+ * `reassembly-failure` rather than silently trusting item.done.
677
+ */
678
+ export class ResponsesReassembler extends AbstractSseReassembler {
679
+ completedSeen = false;
680
+ status;
681
+ usageRaw;
682
+ providerRequestId;
683
+ modelFingerprint;
684
+ /** Verbatim `response` envelope from the terminal event (output[] empty). */
685
+ responseEnvelopeRaw;
686
+ /**
687
+ * Stop reason for the record. Equals `status` for a completed response;
688
+ * for `incomplete`/`failed` prefers the more specific
689
+ * `incomplete_details.reason` / `error` reason when present (FIX 4).
690
+ */
691
+ stopReasonValue;
692
+ items = new Map();
693
+ terminalSeen() {
694
+ return this.completedSeen;
695
+ }
696
+ dispatch(eventType, data) {
697
+ const type = peekTypeField(data) ?? eventType;
698
+ switch (type) {
699
+ case 'response.created':
700
+ this.onResponseCreated(data);
701
+ return;
702
+ case 'response.output_item.added':
703
+ this.onOutputItemAdded(data);
704
+ return;
705
+ case 'response.output_text.delta':
706
+ this.onOutputTextDelta(data);
707
+ return;
708
+ case 'response.output_text.done':
709
+ this.onOutputTextDone(data);
710
+ return;
711
+ case 'response.output_item.done':
712
+ this.onOutputItemDone(data);
713
+ return;
714
+ case 'response.completed':
715
+ case 'response.failed':
716
+ case 'response.incomplete':
717
+ this.onTerminal(data);
718
+ return;
719
+ default:
720
+ // response.in_progress, response.content_part.added/.done, and any
721
+ // future events are recorded in this.events but don't change
722
+ // assembly state.
723
+ return;
724
+ }
725
+ }
726
+ onResponseCreated(data) {
727
+ const responseRaw = findFieldValueSubstring(data, 'response');
728
+ if (!responseRaw)
729
+ return;
730
+ const id = peekStringField(responseRaw, 'id');
731
+ if (id)
732
+ this.providerRequestId = id;
733
+ const model = peekStringField(responseRaw, 'model');
734
+ if (model)
735
+ this.modelFingerprint = model;
736
+ }
737
+ getItem(outputIndex) {
738
+ let item = this.items.get(outputIndex);
739
+ if (!item) {
740
+ item = {
741
+ outputIndex,
742
+ textChunksByContentIndex: new Map(),
743
+ doneTextByContentIndex: new Map(),
744
+ };
745
+ this.items.set(outputIndex, item);
746
+ }
747
+ return item;
748
+ }
749
+ onOutputItemAdded(data) {
750
+ const outputIndex = peekNumberField(data, 'output_index');
751
+ if (outputIndex === undefined) {
752
+ throw new ReassemblyError('response.output_item.added missing output_index');
753
+ }
754
+ this.getItem(outputIndex);
755
+ }
756
+ onOutputTextDelta(data) {
757
+ const outputIndex = peekNumberField(data, 'output_index');
758
+ const contentIndex = peekNumberField(data, 'content_index') ?? 0;
759
+ if (outputIndex === undefined)
760
+ return;
761
+ const item = this.getItem(outputIndex);
762
+ const literal = findFieldValueSubstring(data, 'delta');
763
+ if (literal === undefined || !literal.startsWith('"'))
764
+ return;
765
+ let chunks = item.textChunksByContentIndex.get(contentIndex);
766
+ if (!chunks) {
767
+ chunks = [];
768
+ item.textChunksByContentIndex.set(contentIndex, chunks);
769
+ }
770
+ chunks.push(decodeJsonString(literal));
771
+ }
772
+ onOutputTextDone(data) {
773
+ const outputIndex = peekNumberField(data, 'output_index');
774
+ const contentIndex = peekNumberField(data, 'content_index') ?? 0;
775
+ if (outputIndex === undefined)
776
+ return;
777
+ const text = peekStringField(data, 'text');
778
+ if (text === undefined)
779
+ return;
780
+ this.getItem(outputIndex).doneTextByContentIndex.set(contentIndex, text);
781
+ }
782
+ onOutputItemDone(data) {
783
+ const outputIndex = peekNumberField(data, 'output_index');
784
+ const itemRaw = findFieldValueSubstring(data, 'item');
785
+ if (outputIndex === undefined || itemRaw === undefined) {
786
+ throw new ReassemblyError('response.output_item.done missing output_index or item');
787
+ }
788
+ this.getItem(outputIndex).doneRaw = itemRaw;
789
+ }
790
+ onTerminal(data) {
791
+ const responseRaw = findFieldValueSubstring(data, 'response');
792
+ if (responseRaw) {
793
+ // Capture the FULL envelope verbatim — assembleBody splices the
794
+ // assembled item array into its empty output[] (zero re-encode).
795
+ this.responseEnvelopeRaw = responseRaw;
796
+ const status = peekStringField(responseRaw, 'status');
797
+ if (status)
798
+ this.status = status;
799
+ this.stopReasonValue = deriveResponsesStopReason(responseRaw, status);
800
+ const usageRaw = findFieldValueSubstring(responseRaw, 'usage');
801
+ if (usageRaw !== undefined && usageRaw !== 'null')
802
+ this.usageRaw = usageRaw;
803
+ }
804
+ this.completedSeen = true;
805
+ }
806
+ assembleResult() {
807
+ this.crossCheckDeltasAgainstItems();
808
+ const bodyUtf8 = this.assembleBody();
809
+ return {
810
+ bodyUtf8,
811
+ providerRequestId: this.providerRequestId,
812
+ stopReason: this.stopReasonValue,
813
+ modelFingerprint: this.modelFingerprint,
814
+ usage: this.parseUsage(),
815
+ events: this.events,
816
+ };
817
+ }
818
+ /**
819
+ * Cross-check (FIX 3): the per-content-index delta-join must equal the
820
+ * text the provider reported in `response.output_text.done` and/or the
821
+ * `output_item.done` message content. The verbatim-item-splice strategy
822
+ * trusts `output_item.done` as self-contained; this guard fails LOUD
823
+ * (throws → the binary-session model poisons `reassembly-failure`)
824
+ * rather than silently emitting a body whose final text disagrees with
825
+ * the streamed deltas.
826
+ */
827
+ crossCheckDeltasAgainstItems() {
828
+ for (const item of this.items.values()) {
829
+ for (const [contentIndex, chunks] of item.textChunksByContentIndex) {
830
+ const deltaJoin = chunks.join('');
831
+ const doneText = item.doneTextByContentIndex.get(contentIndex);
832
+ if (doneText !== undefined && doneText !== deltaJoin) {
833
+ throw new ReassemblyError(`responses delta/done mismatch at output_index ${item.outputIndex} ` +
834
+ `content_index ${contentIndex} (output_text.done)`);
835
+ }
836
+ const itemText = item.doneRaw !== undefined ? extractItemContentText(item.doneRaw, contentIndex) : undefined;
837
+ if (itemText !== undefined && itemText !== deltaJoin) {
838
+ throw new ReassemblyError(`responses delta/item mismatch at output_index ${item.outputIndex} ` +
839
+ `content_index ${contentIndex} (output_item.done)`);
840
+ }
841
+ }
842
+ }
843
+ }
844
+ /**
845
+ * Byte-faithful body (FIX 2): the verbatim terminal `response` envelope
846
+ * with its empty `"output":[]` spliced to the assembled item array.
847
+ * Falls back to a minimal synthesized envelope only if the terminal
848
+ * event carried no `response` (defensive; the stream-without-terminal
849
+ * case is already gated by `terminalSeen()` before assembly).
850
+ */
851
+ assembleBody() {
852
+ const sorted = [...this.items.keys()].sort((a, b) => a - b);
853
+ const itemParts = [];
854
+ for (const idx of sorted) {
855
+ const item = this.items.get(idx);
856
+ if (!item?.doneRaw)
857
+ continue;
858
+ itemParts.push(item.doneRaw);
859
+ }
860
+ const outputArray = `[${itemParts.join(',')}]`;
861
+ if (this.responseEnvelopeRaw !== undefined) {
862
+ return replaceTopLevelField(this.responseEnvelopeRaw, 'output', outputArray);
863
+ }
864
+ // Defensive fallback: terminal lacked a `response` envelope. Synthesize
865
+ // a minimal one from the captured leaves.
866
+ const parts = [];
867
+ if (this.providerRequestId !== undefined) {
868
+ parts.push(`"id":${encodeJsonString(this.providerRequestId)}`);
869
+ }
870
+ parts.push('"object":"response"');
871
+ parts.push(`"status":${this.status !== undefined ? encodeJsonString(this.status) : 'null'}`);
872
+ parts.push(`"output":${outputArray}`);
873
+ parts.push(`"usage":${this.usageRaw ?? 'null'}`);
874
+ return `{${parts.join(',')}}`;
875
+ }
876
+ parseUsage() {
877
+ if (!this.usageRaw)
878
+ return undefined;
879
+ try {
880
+ return JSON.parse(this.usageRaw);
881
+ }
882
+ catch {
883
+ return undefined;
884
+ }
885
+ }
886
+ }
887
+ /**
888
+ * Derive the Responses stop reason (FIX 4). For a `completed` response
889
+ * the status IS the stop reason. For `incomplete` / `failed` the status
890
+ * alone is uninformative, so prefer the more specific
891
+ * `incomplete_details.reason` (incomplete) or the `error` reason/code
892
+ * (failed) when the envelope carries one. Falls back to status.
893
+ */
894
+ function deriveResponsesStopReason(responseRaw, status) {
895
+ if (status === 'incomplete') {
896
+ const details = findFieldValueSubstring(responseRaw, 'incomplete_details');
897
+ if (details !== undefined && details !== 'null') {
898
+ const reason = peekStringField(details, 'reason');
899
+ if (reason !== undefined)
900
+ return reason;
901
+ }
902
+ }
903
+ if (status === 'failed') {
904
+ const error = findFieldValueSubstring(responseRaw, 'error');
905
+ if (error !== undefined && error !== 'null') {
906
+ const reason = peekStringField(error, 'code') ?? peekStringField(error, 'type');
907
+ if (reason !== undefined)
908
+ return reason;
909
+ }
910
+ }
911
+ return status;
912
+ }
913
+ /**
914
+ * Extract the `text` of the `output_text` content part at `contentIndex`
915
+ * from a verbatim `output_item.done` item substring, decoded. Returns
916
+ * `undefined` if the item has no `content` array or no matching part —
917
+ * the cross-check skips items it cannot read (e.g. non-message items).
918
+ */
919
+ function extractItemContentText(itemRaw, contentIndex) {
920
+ const contentRaw = findFieldValueSubstring(itemRaw, 'content');
921
+ if (contentRaw === undefined || !contentRaw.startsWith('['))
922
+ return undefined;
923
+ const parts = splitJsonArrayElements(contentRaw);
924
+ if (contentIndex < 0 || contentIndex >= parts.length)
925
+ return undefined;
926
+ return peekStringField(parts[contentIndex], 'text');
927
+ }
928
+ /**
929
+ * Split a raw JSON array substring (including the surrounding brackets)
930
+ * into its top-level element substrings, verbatim. Respects nested
931
+ * objects/arrays and string escapes. Returns `[]` for an empty array.
932
+ */
933
+ function splitJsonArrayElements(arrayRaw) {
934
+ const elements = [];
935
+ let i = 1; // skip leading '['
936
+ while (i < arrayRaw.length) {
937
+ while (i < arrayRaw.length && /[\s,]/.test(arrayRaw[i] ?? ''))
938
+ i++;
939
+ if (i >= arrayRaw.length || arrayRaw[i] === ']')
940
+ break;
941
+ const element = readJsonValueSubstring(arrayRaw, i);
942
+ if (element === undefined)
943
+ break;
944
+ elements.push(element);
945
+ i += element.length;
946
+ }
947
+ return elements;
948
+ }
949
+ /**
950
+ * Classify an upstream host into a capture provider. Single source of
951
+ * truth for host → provider mapping across the capture pipeline (the
952
+ * `ExchangeRecord.provider` field and reassembler selection).
953
+ *
954
+ * Note: `provider:'openai'` spans two wire formats — `api.openai.com`
955
+ * Chat Completions and `chatgpt.com` Responses — disambiguated by the
956
+ * record's `host` field. `auth.openai.com` intentionally stays
957
+ * `'unknown'`: it is an OAuth/identity host, not a completion host, so
958
+ * do NOT broaden this to map all `*.openai.com` to `'openai'` (that would
959
+ * route a non-completion host to a reassembler).
960
+ */
961
+ export function providerForHost(host) {
962
+ const normalized = host.toLowerCase();
963
+ if (normalized === 'api.anthropic.com')
964
+ return 'anthropic';
965
+ if (normalized === 'api.openai.com')
966
+ return 'openai';
967
+ if (normalized === 'chatgpt.com')
968
+ return 'openai';
969
+ return 'unknown';
970
+ }
971
+ /**
972
+ * Pick a reassembler by upstream host. Routing is BY HOST so a specific
973
+ * wire format maps to its state machine. Both OpenAI Responses surfaces —
974
+ * `chatgpt.com/backend-api/codex/responses` (Codex via ChatGPT OAuth) and
975
+ * `api.openai.com/v1/responses` (the platform Responses API, e.g. Codex
976
+ * authenticated with an API key) — share the same wire format, so both use
977
+ * `ResponsesReassembler`. Note: the classic Chat Completions API
978
+ * (`/v1/chat/completions`) is NOT a path any IronCurtain harness uses, so
979
+ * `openaiProvider` does not capture it. Unclassifiable hosts yield
980
+ * `undefined`; the caller falls back to capturing raw bytes verbatim.
981
+ */
982
+ export function createReassembler(host) {
983
+ const h = host.toLowerCase();
984
+ if (h === 'api.anthropic.com')
985
+ return new AnthropicReassembler();
986
+ if (h === 'chatgpt.com' || h === 'api.openai.com')
987
+ return new ResponsesReassembler();
988
+ return undefined;
989
+ }
990
+ //# sourceMappingURL=trajectory-reassembler.js.map