@tangle-network/agent-app 0.45.53 → 0.45.55

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 (480) hide show
  1. package/README.md +1 -1
  2. package/dist/alerting/index.d.ts +5 -175
  3. package/dist/alerting/slack.d.ts +176 -0
  4. package/dist/app-auth/index.d.ts +14 -17
  5. package/dist/assets/index.d.ts +2 -504
  6. package/dist/assets/schema.d.ts +264 -0
  7. package/dist/assets/types.d.ts +237 -0
  8. package/dist/assistant/AssistantDock.d.ts +56 -0
  9. package/dist/assistant/AssistantHistory.d.ts +41 -0
  10. package/dist/assistant/AssistantPanel.d.ts +84 -0
  11. package/dist/assistant/ProposalCard.d.ts +41 -0
  12. package/dist/assistant/ResizeHandle.d.ts +19 -0
  13. package/dist/assistant/client-context.d.ts +20 -0
  14. package/dist/assistant/client.d.ts +117 -0
  15. package/dist/assistant/index.d.ts +19 -848
  16. package/dist/assistant/launcher.d.ts +23 -0
  17. package/dist/assistant/persistence.d.ts +26 -0
  18. package/dist/assistant/presentation.d.ts +99 -0
  19. package/dist/assistant/provider-label.d.ts +7 -0
  20. package/dist/assistant/reducer.d.ts +130 -0
  21. package/dist/assistant/sse.d.ts +24 -0
  22. package/dist/assistant/time-ago.d.ts +6 -0
  23. package/dist/assistant/transcript.d.ts +97 -0
  24. package/dist/assistant/types.d.ts +291 -0
  25. package/dist/assistant/use-stick-to-bottom.d.ts +28 -0
  26. package/dist/assistant/useAssistantChat.d.ts +63 -0
  27. package/dist/assistant/useAssistantModels.d.ts +22 -0
  28. package/dist/assistant/useAssistantThreads.d.ts +47 -0
  29. package/dist/assistant/usePanelPrefs.d.ts +55 -0
  30. package/dist/billing/index.d.ts +19 -21
  31. package/dist/brand/index.d.ts +16 -10
  32. package/dist/brand-extraction/extract.d.ts +34 -0
  33. package/dist/brand-extraction/index.d.ts +12 -214
  34. package/dist/brand-extraction/map.d.ts +58 -0
  35. package/dist/brand-extraction/types.d.ts +119 -0
  36. package/dist/catalog/index.d.ts +11 -101
  37. package/dist/chat-react/composer-mode-controls.d.ts +14 -0
  38. package/dist/chat-react/entry-composer.d.ts +92 -0
  39. package/dist/chat-react/index.d.ts +22 -118
  40. package/dist/chat-react/types.d.ts +11 -0
  41. package/dist/chat-routes/attachment-store.d.ts +84 -0
  42. package/dist/chat-routes/attachment-upload.d.ts +84 -0
  43. package/dist/chat-routes/attachment-validation.d.ts +95 -0
  44. package/dist/chat-routes/binary-sniff.d.ts +45 -0
  45. package/dist/chat-routes/completed-sandbox-turn.d.ts +25 -0
  46. package/dist/chat-routes/detached-turn.d.ts +197 -0
  47. package/dist/chat-routes/dispatch-parts.d.ts +101 -0
  48. package/dist/chat-routes/draft-persistence.d.ts +189 -0
  49. package/dist/chat-routes/durable-projection.d.ts +18 -0
  50. package/dist/chat-routes/file-index.d.ts +115 -0
  51. package/dist/chat-routes/index.d.ts +25 -1840
  52. package/dist/chat-routes/model-failover-stream.d.ts +206 -0
  53. package/dist/chat-routes/promote-file-part.d.ts +84 -0
  54. package/dist/chat-routes/resolve-attachments.d.ts +72 -0
  55. package/dist/chat-routes/sandbox-producer.d.ts +146 -0
  56. package/dist/chat-routes/sandbox-turn-usage.d.ts +4 -0
  57. package/dist/{stale-turn-lock-DucQzvXu.d.ts → chat-routes/stale-turn-lock.d.ts} +7 -9
  58. package/dist/chat-routes/turn-routes.d.ts +540 -0
  59. package/dist/chat-routes/upload.d.ts +84 -0
  60. package/dist/{wire-DOZ-O6hD.d.ts → chat-routes/wire.d.ts} +41 -44
  61. package/dist/chat-store/core.d.ts +19 -0
  62. package/dist/chat-store/index.d.ts +22 -322
  63. package/dist/{parts-BqIHMdyu.d.ts → chat-store/parts.d.ts} +49 -47
  64. package/dist/chat-store/schema.d.ts +157 -0
  65. package/dist/chat-store/store.d.ts +177 -0
  66. package/dist/config/index.d.ts +13 -18
  67. package/dist/crypto/index.d.ts +10 -12
  68. package/dist/crypto/web-token.d.ts +22 -0
  69. package/dist/design-canvas/apply.d.ts +70 -0
  70. package/dist/design-canvas/drizzle-store.d.ts +29 -0
  71. package/dist/design-canvas/drizzle.d.ts +7 -575
  72. package/dist/{export-presets-mgVulRaV.d.ts → design-canvas/export-presets.d.ts} +18 -22
  73. package/dist/design-canvas/index.d.ts +22 -268
  74. package/dist/design-canvas/mcp-entry.d.ts +15 -0
  75. package/dist/design-canvas/mcp-handler.d.ts +34 -0
  76. package/dist/design-canvas/mcp-tools.d.ts +30 -0
  77. package/dist/{model-DnfO_PdJ.d.ts → design-canvas/model.d.ts} +33 -35
  78. package/dist/{operations-ByJ6yXOW.d.ts → design-canvas/operations.d.ts} +21 -25
  79. package/dist/design-canvas/schema.d.ts +558 -0
  80. package/dist/{store-CqfDtnPQ.d.ts → design-canvas/store.d.ts} +8 -12
  81. package/dist/design-canvas/templates.d.ts +72 -0
  82. package/dist/design-canvas/validate.d.ts +32 -0
  83. package/dist/design-canvas-react/components/BleedTrimOverlay.d.ts +22 -0
  84. package/dist/design-canvas-react/components/BrandKnot.d.ts +11 -0
  85. package/dist/design-canvas-react/components/CanvasEmptyState.d.ts +32 -0
  86. package/dist/design-canvas-react/components/CanvasInsertPanel.d.ts +53 -0
  87. package/dist/{lazy-DrkVyDpe.d.ts → design-canvas-react/components/DesignCanvas.d.ts} +27 -20
  88. package/dist/design-canvas-react/components/DesignCanvasEditor.d.ts +23 -0
  89. package/dist/design-canvas-react/components/ElementNode.d.ts +51 -0
  90. package/dist/design-canvas-react/components/ExportControl.d.ts +21 -0
  91. package/dist/design-canvas-react/components/GridLayer.d.ts +47 -0
  92. package/dist/design-canvas-react/components/InlineTextEditor.d.ts +25 -0
  93. package/dist/design-canvas-react/components/LayersPanel.d.ts +20 -0
  94. package/dist/design-canvas-react/components/PagesStrip.d.ts +34 -0
  95. package/dist/design-canvas-react/components/Rulers.d.ts +31 -0
  96. package/dist/design-canvas-react/components/SelectionLayer.d.ts +51 -0
  97. package/dist/design-canvas-react/components/SnapGuidesOverlay.d.ts +25 -0
  98. package/dist/design-canvas-react/components/Toolbar.d.ts +64 -0
  99. package/dist/design-canvas-react/components/Workspace.d.ts +92 -0
  100. package/dist/design-canvas-react/components/ZoomControls.d.ts +14 -0
  101. package/dist/design-canvas-react/components/glyphs.d.ts +46 -0
  102. package/dist/design-canvas-react/components/icon-button.d.ts +22 -0
  103. package/dist/design-canvas-react/components/index.d.ts +27 -0
  104. package/dist/design-canvas-react/components/layer-tree.d.ts +34 -0
  105. package/dist/design-canvas-react/components/ruler-math.d.ts +78 -0
  106. package/dist/design-canvas-react/components/transform-math.d.ts +168 -0
  107. package/dist/{command-stack-TNPWJCzW.d.ts → design-canvas-react/contracts.d.ts} +16 -63
  108. package/dist/design-canvas-react/engine/command-stack.d.ts +43 -0
  109. package/dist/design-canvas-react/engine/commands.d.ts +154 -0
  110. package/dist/design-canvas-react/engine/selection.d.ts +48 -0
  111. package/dist/design-canvas-react/engine/snap.d.ts +24 -0
  112. package/dist/design-canvas-react/engine/zoom-pan.d.ts +18 -0
  113. package/dist/design-canvas-react/engine.d.ts +21 -399
  114. package/dist/design-canvas-react/export-math.d.ts +104 -0
  115. package/dist/design-canvas-react/export.d.ts +89 -0
  116. package/dist/design-canvas-react/index.d.ts +16 -586
  117. package/dist/design-canvas-react/insert-builders.d.ts +61 -0
  118. package/dist/design-canvas-react/lazy.d.ts +21 -6
  119. package/dist/documents/docx.d.ts +50 -0
  120. package/dist/documents/extract.d.ts +87 -0
  121. package/dist/documents/index.d.ts +26 -232
  122. package/dist/documents/media-type.d.ts +33 -0
  123. package/dist/documents/pdf-inspector.d.ts +5 -9
  124. package/dist/{types-DrBidf3O.d.ts → documents/types.d.ts} +13 -15
  125. package/dist/documents/zip.d.ts +56 -0
  126. package/dist/eval/calibration.d.ts +106 -0
  127. package/dist/eval/index.d.ts +8 -116
  128. package/dist/eval-campaign/index.d.ts +11 -12
  129. package/dist/{trust-gate-Dcm5xSva.d.ts → eval-campaign/trust-gate.d.ts} +5 -9
  130. package/dist/forms/blank.d.ts +48 -0
  131. package/dist/forms/fill.d.ts +105 -0
  132. package/dist/forms/index.d.ts +58 -332
  133. package/dist/forms/registry.d.ts +115 -0
  134. package/dist/forms/verify.d.ts +58 -0
  135. package/dist/harness/index.d.ts +21 -19
  136. package/dist/intakes/api.d.ts +5 -11
  137. package/dist/{completion-CvcNUz07.d.ts → intakes/completion.d.ts} +7 -11
  138. package/dist/intakes/context-sufficiency.d.ts +113 -0
  139. package/dist/intakes/drizzle/schema.d.ts +338 -0
  140. package/dist/intakes/drizzle/store.d.ts +75 -0
  141. package/dist/intakes/drizzle.d.ts +12 -391
  142. package/dist/intakes/index.d.ts +23 -113
  143. package/dist/{model-C8U0HZxL.d.ts → intakes/model.d.ts} +15 -17
  144. package/dist/intakes-react/components/BrandMark.d.ts +10 -0
  145. package/dist/intakes-react/components/IntakeInterview.d.ts +15 -0
  146. package/dist/intakes-react/components/index.d.ts +1 -0
  147. package/dist/intakes-react/contracts.d.ts +42 -0
  148. package/dist/intakes-react/index.d.ts +12 -47
  149. package/dist/intakes-react/lazy.d.ts +9 -8
  150. package/dist/integrations/index.d.ts +10 -12
  151. package/dist/{contract-OxG_jVMx.d.ts → interactions/contract.d.ts} +40 -42
  152. package/dist/interactions/index.d.ts +16 -256
  153. package/dist/interactions/route.d.ts +151 -0
  154. package/dist/interactions/sidecar.d.ts +100 -0
  155. package/dist/knowledge/index.d.ts +7 -11
  156. package/dist/knowledge-loop/index.d.ts +13 -20
  157. package/dist/legibility/checks/empty-state.d.ts +59 -0
  158. package/dist/legibility/checks/reachability.d.ts +103 -0
  159. package/dist/legibility/checks/silent-failure.d.ts +57 -0
  160. package/dist/legibility/checks/success.d.ts +38 -0
  161. package/dist/legibility/checks/vocabulary.d.ts +65 -0
  162. package/dist/legibility/cli-args.d.ts +30 -0
  163. package/dist/legibility/cli.d.ts +25 -0
  164. package/dist/legibility/index.d.ts +10 -530
  165. package/dist/legibility/report.d.ts +22 -0
  166. package/dist/legibility/scan.d.ts +29 -0
  167. package/dist/legibility/source.d.ts +132 -0
  168. package/dist/legibility/suppress.d.ts +36 -0
  169. package/dist/legibility/types.d.ts +168 -0
  170. package/dist/legibility/walk-sources.d.ts +21 -0
  171. package/dist/{agent-activity-C8ZG0F0M.d.ts → missions/agent-activity.d.ts} +3 -5
  172. package/dist/missions/engine.d.ts +251 -0
  173. package/dist/missions/events.d.ts +195 -0
  174. package/dist/missions/index.d.ts +12 -765
  175. package/dist/missions/plan-parse.d.ts +50 -0
  176. package/dist/missions/service.d.ts +265 -0
  177. package/dist/{failover-D-3UXXTb.d.ts → model-resolution/failover.d.ts} +10 -12
  178. package/dist/model-resolution/index.d.ts +15 -18
  179. package/dist/object-store/index.d.ts +15 -17
  180. package/dist/openui/action.d.ts +54 -0
  181. package/dist/openui/authoring.d.ts +25 -0
  182. package/dist/openui/index.d.ts +24 -206
  183. package/dist/openui/route.d.ts +124 -0
  184. package/dist/openui/segments.d.ts +71 -0
  185. package/dist/openui/values.d.ts +82 -0
  186. package/dist/openui-react/index.d.ts +9 -13
  187. package/dist/peer-floors/check.d.ts +9 -190
  188. package/dist/peer-floors/cli.d.ts +2 -3
  189. package/dist/peer-floors/dependency-source.d.ts +178 -0
  190. package/dist/plans/index.d.ts +13 -14
  191. package/dist/{billing-BuNlaKlE.d.ts → platform/billing.d.ts} +28 -32
  192. package/dist/platform/guards.d.ts +79 -0
  193. package/dist/platform/hub.d.ts +107 -0
  194. package/dist/platform/index.d.ts +7 -112
  195. package/dist/{sso-Cm00S61u.d.ts → platform/sso.d.ts} +15 -97
  196. package/dist/preflight/cli.d.ts +3 -6
  197. package/dist/preflight/index.d.ts +16 -18
  198. package/dist/preset-cloudflare/headers.d.ts +12 -0
  199. package/dist/preset-cloudflare/index.d.ts +22 -74
  200. package/dist/preset-cloudflare/workflow-instance.d.ts +35 -0
  201. package/dist/profile/budget.d.ts +72 -0
  202. package/dist/profile/fingerprint.d.ts +65 -0
  203. package/dist/profile/index.d.ts +27 -19
  204. package/dist/prompt/index.d.ts +3 -5
  205. package/dist/record/drizzle/schema.d.ts +488 -0
  206. package/dist/record/drizzle/store.d.ts +224 -0
  207. package/dist/record/drizzle.d.ts +10 -679
  208. package/dist/record/fold.d.ts +99 -0
  209. package/dist/record/index.d.ts +13 -13
  210. package/dist/{fold-DACU7gtf.d.ts → record/model.d.ts} +22 -124
  211. package/dist/record/ulid.d.ts +12 -0
  212. package/dist/redact/index.d.ts +14 -16
  213. package/dist/run/index.d.ts +11 -16
  214. package/dist/runtime/agent.d.ts +23 -0
  215. package/dist/runtime/certified-delivery.d.ts +57 -0
  216. package/dist/runtime/index.d.ts +17 -367
  217. package/dist/runtime/loop.d.ts +58 -0
  218. package/dist/runtime/model-catalog.d.ts +103 -0
  219. package/dist/{model-CdCDfBA9.d.ts → runtime/model.d.ts} +25 -27
  220. package/dist/runtime/openai-stream.d.ts +126 -0
  221. package/dist/runtime/surface-profile.d.ts +115 -0
  222. package/dist/sandbox/binary-read.d.ts +63 -0
  223. package/dist/sandbox/diagnostics.d.ts +69 -0
  224. package/dist/sandbox/index.d.ts +103 -1085
  225. package/dist/sandbox/model.d.ts +179 -0
  226. package/dist/sandbox/outcome.d.ts +10 -0
  227. package/dist/sandbox/prewarm-claim-d1.d.ts +80 -0
  228. package/dist/sandbox/prewarm.d.ts +193 -0
  229. package/dist/sandbox/recovery.d.ts +216 -0
  230. package/dist/sandbox/terminal-connection.d.ts +145 -0
  231. package/dist/sandbox/workspace-sandbox-manager.d.ts +44 -0
  232. package/dist/sequences/apply.d.ts +54 -0
  233. package/dist/sequences/captions.d.ts +91 -0
  234. package/dist/sequences/drizzle-store.d.ts +36 -0
  235. package/dist/sequences/drizzle.d.ts +7 -1252
  236. package/dist/sequences/exports.d.ts +138 -0
  237. package/dist/sequences/index.d.ts +19 -340
  238. package/dist/sequences/mcp-entry.d.ts +15 -0
  239. package/dist/sequences/mcp-handler.d.ts +39 -0
  240. package/dist/sequences/mcp-tools.d.ts +47 -0
  241. package/dist/{store-B7LLlk9p.d.ts → sequences/model.d.ts} +26 -110
  242. package/dist/sequences/operations.d.ts +126 -0
  243. package/dist/sequences/schema.d.ts +1231 -0
  244. package/dist/sequences/store.d.ts +81 -0
  245. package/dist/sequences/validate.d.ts +98 -0
  246. package/dist/sequences-react/components/BrandMark.d.ts +10 -0
  247. package/dist/sequences-react/components/PreviewCanvas.d.ts +26 -0
  248. package/dist/sequences-react/components/SnapIndicatorLine.d.ts +11 -0
  249. package/dist/sequences-react/components/TimelineClipChip.d.ts +80 -0
  250. package/dist/sequences-react/components/TimelineEditor.d.ts +44 -0
  251. package/dist/sequences-react/components/TimelineEmptyState.d.ts +21 -0
  252. package/dist/sequences-react/components/TimelineGhostLanes.d.ts +14 -0
  253. package/dist/sequences-react/components/TimelinePlayhead.d.ts +11 -0
  254. package/dist/sequences-react/components/TimelineRuler.d.ts +16 -0
  255. package/dist/sequences-react/components/TimelineSmallScreenGate.d.ts +12 -0
  256. package/dist/sequences-react/components/TimelineTrackRow.d.ts +51 -0
  257. package/dist/sequences-react/components/ZoomControl.d.ts +15 -0
  258. package/dist/sequences-react/components/composite-command.d.ts +8 -0
  259. package/dist/sequences-react/components/glyphs.d.ts +26 -0
  260. package/dist/sequences-react/components/index.d.ts +31 -0
  261. package/dist/sequences-react/components/interaction-math.d.ts +103 -0
  262. package/dist/sequences-react/contracts.d.ts +187 -0
  263. package/dist/sequences-react/engine/command-stack.d.ts +18 -0
  264. package/dist/sequences-react/engine/commands.d.ts +146 -0
  265. package/dist/sequences-react/engine/playback.d.ts +18 -0
  266. package/dist/sequences-react/engine/snap.d.ts +29 -0
  267. package/dist/sequences-react/engine/zoom.d.ts +29 -0
  268. package/dist/sequences-react/index.d.ts +20 -880
  269. package/dist/sequences-react/lazy.d.ts +8 -0
  270. package/dist/sequences-react/media/frame-provider.d.ts +70 -0
  271. package/dist/sequences-react/media/transcription.d.ts +33 -0
  272. package/dist/sequences-react/media/waveform.d.ts +34 -0
  273. package/dist/session-shell/index.d.ts +31 -204
  274. package/dist/session-shell/nav-guard.d.ts +170 -0
  275. package/dist/session-shell/path.d.ts +19 -0
  276. package/dist/signoff/cli.d.ts +4 -7
  277. package/dist/signoff/config.d.ts +12 -0
  278. package/dist/signoff/exec.d.ts +44 -0
  279. package/dist/signoff/index.d.ts +56 -14
  280. package/dist/signoff/invoked-as-script.d.ts +25 -0
  281. package/dist/signoff/node-version.d.ts +50 -0
  282. package/dist/signoff/proof-attach.d.ts +88 -0
  283. package/dist/signoff/proof-cli.d.ts +1 -0
  284. package/dist/signoff/proof-fixture.d.ts +13 -0
  285. package/dist/signoff/proof-from-report.d.ts +22 -0
  286. package/dist/signoff/proof-git.d.ts +57 -0
  287. package/dist/signoff/proof-record.d.ts +202 -0
  288. package/dist/signoff/proof-verify.d.ts +103 -0
  289. package/dist/signoff/proof.d.ts +35 -310
  290. package/dist/signoff/report.d.ts +7 -0
  291. package/dist/{run-D80hoLKh.d.ts → signoff/run.d.ts} +3 -6
  292. package/dist/signoff/schedule.d.ts +55 -0
  293. package/dist/signoff/seeds.d.ts +50 -0
  294. package/dist/signoff/store.d.ts +22 -0
  295. package/dist/{types-Bd6uW9vQ.d.ts → signoff/types.d.ts} +15 -17
  296. package/dist/signoff/workflow-pin.d.ts +56 -0
  297. package/dist/signoff/workspace.d.ts +40 -0
  298. package/dist/skills/index.d.ts +28 -32
  299. package/dist/skills-placement/index.d.ts +7 -11
  300. package/dist/spend/budget.d.ts +123 -0
  301. package/dist/spend/ceiling.d.ts +51 -0
  302. package/dist/spend/cli-args.d.ts +20 -0
  303. package/dist/spend/cli.d.ts +1 -0
  304. package/dist/spend/fixtures/incident-parked-time.d.ts +55 -0
  305. package/dist/spend/index.d.ts +38 -1144
  306. package/dist/spend/liveness.d.ts +131 -0
  307. package/dist/spend/ownership.d.ts +87 -0
  308. package/dist/spend/reconcile.d.ts +109 -0
  309. package/dist/spend/reference.d.ts +42 -0
  310. package/dist/spend/report.d.ts +12 -0
  311. package/dist/spend/store.d.ts +123 -0
  312. package/dist/spend/types.d.ts +466 -0
  313. package/dist/store/index.d.ts +10 -12
  314. package/dist/stories/app-shell/fixtures.d.ts +43 -0
  315. package/dist/stories/app-shell/shell.d.ts +81 -0
  316. package/dist/stories/assistant/fixtures.d.ts +73 -0
  317. package/dist/stories/chat/fixtures.d.ts +70 -0
  318. package/dist/stories/chat/markdown.d.ts +11 -0
  319. package/dist/stories/chat/run-drill-in.d.ts +19 -0
  320. package/dist/stories/chat-controls/fixtures.d.ts +116 -0
  321. package/dist/stories/design-canvas/fixtures.d.ts +57 -0
  322. package/dist/stories/fixtures/canvas.d.ts +10 -0
  323. package/dist/stories/fixtures/catalog.d.ts +7 -0
  324. package/dist/stories/fixtures/chat.d.ts +39 -0
  325. package/dist/stories/fixtures/index.d.ts +4 -0
  326. package/dist/stories/fixtures/sequences.d.ts +17 -0
  327. package/dist/stories/panels/fixtures.d.ts +63 -0
  328. package/dist/stories/sequences/fixtures.d.ts +30 -0
  329. package/dist/stories/studio/fixtures.d.ts +22 -0
  330. package/dist/stream/index.d.ts +10 -56
  331. package/dist/{stream-normalizer-DKRxyVlZ.d.ts → stream/stream-normalizer.d.ts} +23 -26
  332. package/dist/{turn-buffer-COMQUwO2.d.ts → stream/turn-buffer.d.ts} +21 -23
  333. package/dist/stream/turn-identity.d.ts +49 -0
  334. package/dist/studio/generation.d.ts +179 -0
  335. package/dist/studio/index.d.ts +12 -181
  336. package/dist/studio-react/avatar-composer.d.ts +8 -0
  337. package/dist/studio-react/composer-hero.d.ts +12 -0
  338. package/dist/studio-react/composer-shell.d.ts +18 -0
  339. package/dist/studio-react/generation-card.d.ts +9 -0
  340. package/dist/studio-react/generation-detail-modal.d.ts +11 -0
  341. package/dist/studio-react/generation-detail.d.ts +6 -0
  342. package/dist/studio-react/generation-grid.d.ts +15 -0
  343. package/dist/studio-react/image-composer.d.ts +8 -0
  344. package/dist/studio-react/index.d.ts +32 -245
  345. package/dist/studio-react/library-drawer.d.ts +12 -0
  346. package/dist/studio-react/library-panel.d.ts +15 -0
  347. package/dist/studio-react/publish-package-composer.d.ts +18 -0
  348. package/dist/studio-react/result-canvas.d.ts +6 -0
  349. package/dist/studio-react/speech-composer.d.ts +4 -0
  350. package/dist/studio-react/studio-header.d.ts +5 -0
  351. package/dist/studio-react/studio-sheet.d.ts +12 -0
  352. package/dist/studio-react/studio-workspace.d.ts +23 -0
  353. package/dist/studio-react/transcription-composer.d.ts +12 -0
  354. package/dist/studio-react/type-config.d.ts +11 -0
  355. package/dist/studio-react/use-studio-generations.d.ts +20 -0
  356. package/dist/studio-react/video-composer.d.ts +10 -0
  357. package/dist/tangle/index.d.ts +7 -9
  358. package/dist/teams/drizzle/access.d.ts +82 -0
  359. package/dist/{invitations-schema-_fI7HkJP.d.ts → teams/drizzle/invitations-schema.d.ts} +39 -27
  360. package/dist/teams/drizzle/personal-organization.d.ts +41 -0
  361. package/dist/{access-490O6aOU.d.ts → teams/drizzle/schema.d.ts} +57 -122
  362. package/dist/teams/drizzle.d.ts +12 -44
  363. package/dist/teams/index.d.ts +20 -44
  364. package/dist/teams/invitations-api.d.ts +17 -21
  365. package/dist/{invitations-DoYMvXea.d.ts → teams/invitations.d.ts} +14 -18
  366. package/dist/teams/invite.d.ts +42 -0
  367. package/dist/teams/members-api.d.ts +12 -16
  368. package/dist/teams/resend.d.ts +3 -13
  369. package/dist/{roles-B4nU3jDi.d.ts → teams/roles.d.ts} +18 -20
  370. package/dist/teams-react/components/BrandMark.d.ts +10 -0
  371. package/dist/teams-react/components/InvitationsPanel.d.ts +13 -0
  372. package/dist/teams-react/components/InviteAcceptPage.d.ts +10 -0
  373. package/dist/teams-react/components/MembersPanel.d.ts +14 -0
  374. package/dist/teams-react/components/RoleSelect.d.ts +17 -0
  375. package/dist/teams-react/components/index.d.ts +3 -0
  376. package/dist/teams-react/contracts.d.ts +134 -0
  377. package/dist/teams-react/index.d.ts +12 -143
  378. package/dist/teams-react/lazy.d.ts +11 -11
  379. package/dist/test-setup.d.ts +1 -0
  380. package/dist/theme/index.d.ts +6 -104
  381. package/dist/theme/tailwind-preset.d.ts +1 -2
  382. package/dist/theme/theme.d.ts +106 -0
  383. package/dist/theme-contract/cli.d.ts +21 -0
  384. package/dist/theme-contract/index.d.ts +4 -6
  385. package/dist/{auth-BYtiazA4.d.ts → tools/auth.d.ts} +8 -11
  386. package/dist/tools/capability.d.ts +54 -0
  387. package/dist/tools/dispatch.d.ts +37 -0
  388. package/dist/tools/errors.d.ts +8 -0
  389. package/dist/tools/gating.d.ts +62 -0
  390. package/dist/tools/http.d.ts +27 -0
  391. package/dist/tools/index.d.ts +12 -217
  392. package/dist/{mcp-rpc-DYwPlYbZ.d.ts → tools/mcp-rpc.d.ts} +7 -9
  393. package/dist/{mcp-xxoZZfor.d.ts → tools/mcp.d.ts} +14 -16
  394. package/dist/tools/openai.d.ts +28 -0
  395. package/dist/tools/registry.d.ts +39 -0
  396. package/dist/tools/runtime.d.ts +22 -0
  397. package/dist/{types-B25Mib1r.d.ts → tools/types.d.ts} +15 -83
  398. package/dist/{flow-types-CJxEmaRy.d.ts → trace/flow-types.d.ts} +2 -4
  399. package/dist/trace/index.d.ts +11 -148
  400. package/dist/trace/mission-flow.d.ts +82 -0
  401. package/dist/trace/mission-trace.d.ts +52 -0
  402. package/dist/turn-health/classify.d.ts +207 -0
  403. package/dist/turn-health/index.d.ts +29 -637
  404. package/dist/turn-health/lifecycle.d.ts +75 -0
  405. package/dist/turn-health/sink.d.ts +122 -0
  406. package/dist/turn-health/sweep.d.ts +245 -0
  407. package/dist/turn-stream/adapters.d.ts +257 -0
  408. package/dist/turn-stream/core.d.ts +293 -0
  409. package/dist/turn-stream/do.d.ts +144 -0
  410. package/dist/turn-stream/index.d.ts +62 -738
  411. package/dist/turn-stream/memory.d.ts +41 -0
  412. package/dist/vault/ConfirmDialog.d.ts +22 -0
  413. package/dist/vault/VaultPane.d.ts +15 -0
  414. package/dist/{VaultPane-BDP6SB_K.d.ts → vault/contracts.d.ts} +15 -22
  415. package/dist/vault/index.d.ts +15 -22
  416. package/dist/vault/lazy.d.ts +9 -7
  417. package/dist/vault/server.d.ts +8 -10
  418. package/dist/web/free-route-limit.d.ts +187 -0
  419. package/dist/web/index.d.ts +15 -207
  420. package/dist/{rate-limit-BOVmJRXZ.d.ts → web/rate-limit.d.ts} +3 -5
  421. package/dist/web-react/agent-session-controls.d.ts +105 -0
  422. package/dist/web-react/async/async-view.d.ts +69 -0
  423. package/dist/web-react/async/index.d.ts +13 -305
  424. package/dist/web-react/async/state.d.ts +107 -0
  425. package/dist/web-react/async/use-async-resource.d.ts +41 -0
  426. package/dist/web-react/async/use-confirmed-mutation.d.ts +90 -0
  427. package/dist/web-react/brand-mark.d.ts +11 -0
  428. package/dist/web-react/chat-attachments.d.ts +21 -0
  429. package/dist/web-react/chat-composer.d.ts +174 -0
  430. package/dist/web-react/chat-interactions.d.ts +1 -0
  431. package/dist/web-react/chat-mentions.d.ts +48 -0
  432. package/dist/web-react/chat-stream.d.ts +106 -0
  433. package/dist/web-react/class-names.d.ts +10 -0
  434. package/dist/web-react/controls.d.ts +265 -0
  435. package/dist/web-react/durable-chat-cards.d.ts +40 -0
  436. package/dist/web-react/durable-interaction-submit.d.ts +22 -0
  437. package/dist/web-react/durable-plan-card.d.ts +13 -0
  438. package/dist/web-react/durable-plan-flow.d.ts +77 -0
  439. package/dist/web-react/harness-glyphs.d.ts +28 -0
  440. package/dist/web-react/index.d.ts +80 -2347
  441. package/dist/web-react/inert.d.ts +39 -0
  442. package/dist/web-react/insight-card.d.ts +203 -0
  443. package/dist/web-react/interaction-card-support.d.ts +115 -0
  444. package/dist/web-react/interaction-plan-card.d.ts +39 -0
  445. package/dist/web-react/interaction-question-card.d.ts +100 -0
  446. package/dist/web-react/message-attachments.d.ts +56 -0
  447. package/dist/web-react/mission-activity.d.ts +96 -0
  448. package/dist/web-react/motion.d.ts +43 -0
  449. package/dist/web-react/provenance-model.d.ts +195 -0
  450. package/dist/web-react/provenance.d.ts +82 -0
  451. package/dist/web-react/provider-logo.d.ts +14 -0
  452. package/dist/web-react/record-grid-model.d.ts +257 -0
  453. package/dist/web-react/record-grid.d.ts +108 -0
  454. package/dist/web-react/sandbox-terminal.d.ts +90 -0
  455. package/dist/web-react/seat-paywall.d.ts +47 -0
  456. package/dist/web-react/session-history.d.ts +196 -0
  457. package/dist/web-react/smooth-text.d.ts +29 -0
  458. package/dist/web-react/sparkline.d.ts +150 -0
  459. package/dist/web-react/terminal.d.ts +1 -92
  460. package/dist/web-react/use-chat-interactions.d.ts +78 -0
  461. package/dist/web-react/use-composer-attachments.d.ts +111 -0
  462. package/dist/web-react/use-file-mentions.d.ts +94 -0
  463. package/dist/web-react/work-product.d.ts +105 -0
  464. package/dist/work-product/claim-support.d.ts +288 -0
  465. package/dist/work-product/index.d.ts +16 -849
  466. package/dist/work-product/provenance.d.ts +61 -0
  467. package/dist/{queue-VTBA5ONX.d.ts → work-product/queue.d.ts} +10 -12
  468. package/dist/work-product/quote.d.ts +139 -0
  469. package/dist/work-product/route.d.ts +88 -0
  470. package/dist/work-product/service.d.ts +123 -0
  471. package/dist/work-product/tools.d.ts +144 -0
  472. package/dist/{types-CCeYywdS.d.ts → work-product/types.d.ts} +29 -31
  473. package/dist/work-product-react/index.d.ts +19 -8
  474. package/dist/workspace-react/index.d.ts +8 -11
  475. package/package.json +5 -4
  476. package/dist/agent-session-controls-CmlXwp_H.d.ts +0 -384
  477. package/dist/apply-6wlMOLf8.d.ts +0 -280
  478. package/dist/attachment-validation-BfJDBU1g.d.ts +0 -261
  479. package/dist/budget-BOucfcb_.d.ts +0 -142
  480. package/dist/values-C7RidJuI.d.ts +0 -156
@@ -1,1841 +1,26 @@
1
- import { c as ChatTurnRequestPayload, d as ChatTurnPartInput, e as ChatTurnFilePartInput, a as ChatAttachmentKind } from '../wire-DOZ-O6hD.js';
2
- export { b as ChatAttachmentInput, C as ChatMentionKind, f as ChatReasoningEffort, g as ChatTurnInputError, h as ChatTurnTextPartInput, D as DISPATCH_MAX_MEDIA_PARTS, i as DISPATCH_MAX_PARTS, j as DISPATCH_REQUEST_MAX_BYTES, k as DISPATCH_STRUCTURAL_RESERVE_BYTES, F as FileMention, l as FileMentionsToPartsOptions, I as INLINE_PARTS_MAX_BYTES, M as MENTION_MAX_COUNT, P as ProducerErrorEvent, m as ProducerNoticeEvent, n as ProducerPassthroughEvent, o as ProducerPassthroughEventType, p as ProducerReasoningEvent, q as ProducerTextEvent, r as ProducerToolCallEvent, s as ProducerToolResultEvent, t as ProducerUsageEvent, u as ProducerWireEvent, S as SandboxMentionPathCheck, v as assertPromptPartsWithinCap, w as base64WireLen, x as buildMentionPromptBlock, y as chatTurnRequestInit, z as fileMentionsToParts, A as formatBytes, B as mediaTypeForMentionPath, E as mentionKindForPath, G as parseChatTurnParts, H as parseFileMentions, J as promptPartsByteSize, K as validateSandboxMentionPath } from '../wire-DOZ-O6hD.js';
3
- export { A as ALLOWED_ATTACHMENT_SNIFFED_MIMES, a as ATTACHMENT_ACCEPT, b as ATTACHMENT_MAX_COUNT, c as AttachmentTypeCheckResult, C as CreateSandboxFileIndexRouteOptions, F as FileIndexAuthorization, d as FileIndexCache, e as FileIndexReadyResponse, f as FileIndexResponse, g as FileIndexWarmingResponse, M as MACRO_ENABLED_OOXML_SNIFFED_MIMES, h as MAX_ATTACHMENT_TOTAL_BYTES, i as MAX_BINARY_ATTACHMENT_BYTES, j as MAX_TEXT_ATTACHMENT_BYTES, O as OOXML_PRESENTATION_MACRO_ENABLED_MIME, k as OOXML_PRESENTATION_MIME, l as OOXML_SNIFFED_MIMES, m as OOXML_SPREADSHEET_MACRO_ENABLED_MIME, n as OOXML_SPREADSHEET_MIME, o as OOXML_WORD_MACRO_ENABLED_MIME, p as OOXML_WORD_MIME, S as SandboxFileTreeSource, q as SandboxTreeFile, r as SandboxTreeResult, s as SniffResult, t as attachmentSizeErrorMessage, u as attachmentTotalSizeErrorMessage, v as checkAttachmentType, w as createSandboxFileIndexRoute, x as sanitizeAttachmentFileName, y as sniffBinary } from '../attachment-validation-BfJDBU1g.js';
4
- import { ChatTurnIdentity, ChatTurnProducer } from '@tangle-network/agent-runtime/durable';
5
- import { C as ChatMessagePart, a as ChatAttachmentPart, e as ChatMentionPart } from '../parts-BqIHMdyu.js';
6
- import { InteractionAnswerRoute, InteractionAnswerRouteOptions } from '../interactions/index.js';
7
- import { PersistedChatMessageForTurn } from '../stream/index.js';
8
- import { T as TurnEventStore } from '../turn-buffer-COMQUwO2.js';
9
- import { M as ModelFailoverAttempt, a as ModelFailoverExhaustedError } from '../failover-D-3UXXTb.js';
10
- export { D as DEFAULT_STALE_TURN_LOCK_GRACE_MS, a as DEFAULT_TERMINAL_TURN_LOCK_GRACE_MS, R as ReconcileStaleTurnLockOptions, b as ReconcileStaleTurnLockResult, S as StaleTurnLockSandboxProbeResult, c as StaleTurnLockSessionProbeResult, r as reconcileStaleTurnLock } from '../stale-turn-lock-DucQzvXu.js';
11
- import { J as JsonRecord } from '../stream-normalizer-DKRxyVlZ.js';
12
- import { SandboxInstance } from '@tangle-network/sandbox';
13
- import { SandboxExecChannel, PromptInputPart } from '../sandbox/index.js';
14
- import '@tangle-network/agent-interface';
15
- import '../contract-OxG_jVMx.js';
16
- import '../types-CCeYywdS.js';
17
- import '../plans/index.js';
18
- import '@tangle-network/sandbox/core';
19
- import '../auth-BYtiazA4.js';
20
- import '../types-B25Mib1r.js';
21
- import '../harness/index.js';
22
- import '../model-CdCDfBA9.js';
23
- import '../budget-BOucfcb_.js';
24
-
25
1
  /**
26
- * Incremental ("draft") persistence of the assistant row WHILE a turn streams.
27
- *
28
- * Why this exists the scale argument, not a convenience:
29
- *
30
- * A live turn is readable from two places. The hot path is the session
31
- * gateway's in-memory/Redis event buffer, which exists so a viewer survives a
32
- * network blip: it is keyed one sorted set per session, refreshed on every
33
- * push, and expires on a TTL. Its memory cost is `arrival_rate x TTL x
34
- * bytes_per_session` — strictly LINEAR in the TTL. Stretching that TTL to
35
- * cover "a viewer who opens the tab later" is a category error: it buys memory
36
- * proportional to the increase and still serves nothing to a viewer who
37
- * arrives past the new horizon.
38
- *
39
- * So the hot buffer must stay SHORT (live delivery + reconnect only), and
40
- * durable storage must serve history. That only works if durable storage
41
- * actually HAS the in-flight turn — which, before this module, it did not: the
42
- * assistant row was written once, after the stream drained. A viewer arriving
43
- * mid-turn past the hot window read an empty transcript.
44
- *
45
- * This module closes that gap: the assistant row is inserted early and patched
46
- * on a coalesced cadence, so the durable transcript is at most one interval
47
- * (default 2 s) behind the live stream and the hot buffer never has to be the
48
- * history tier.
49
- *
50
- * Mechanism only. It owns no vocabulary: the caller supplies the snapshot
51
- * function, the store, and the deterministic row id.
52
- *
53
- * Four properties the cadence guarantees:
54
- * - **Time-floored.** At most one write per `intervalMs`, never one per token.
55
- * - **Dirty-gated.** Only content-bearing events arm a write; heartbeats,
56
- * status pings, and lifecycle envelopes never do.
57
- * - **Single-flight.** A write already in flight suppresses the next trigger
58
- * instead of queueing; the final write is authoritative regardless of how
59
- * many drafts landed.
60
- * - **Best-effort.** A store failure is logged and swallowed — a durability
61
- * optimization must never kill a healthy stream (the same rule
62
- * `withDurableChatProjection` already states).
63
- */
64
-
65
- /** Message row shape the writer reads back when re-entering a turn. */
66
- interface DraftStoredMessage {
67
- id: string;
68
- role: 'user' | 'assistant' | 'system' | 'tool';
69
- content: string;
70
- parts?: ChatMessagePart[] | null;
71
- model?: string | null;
72
- requestedModel?: string | null;
73
- servedModel?: string | null;
74
- servedProvider?: string | null;
75
- servedSource?: string | null;
76
- }
77
- /** Values written to the assistant row — the intersection of the append and
78
- * patch shapes, so one snapshot serves both. */
79
- interface AssistantRowValues {
80
- content: string;
81
- parts?: ChatMessagePart[];
82
- model?: string | null;
83
- requestedModel?: string | null;
84
- servedModel?: string | null;
85
- servedProvider?: string | null;
86
- servedSource?: string | null;
87
- inputTokens?: number | null;
88
- outputTokens?: number | null;
89
- reasoningTokens?: number | null;
90
- cacheReadTokens?: number | null;
91
- cacheWriteTokens?: number | null;
92
- costUsd?: number | null;
93
- }
94
- /** The store capability incremental persistence needs on top of
95
- * `appendMessage`. A store without `updateMessage` cannot patch a row, so it
96
- * cannot draft at all — the caller keeps today's single-write behavior. */
97
- interface AssistantDraftStore {
98
- listMessages(threadId: string): Promise<DraftStoredMessage[]>;
99
- appendMessage(input: AssistantRowValues & {
100
- id?: string;
101
- threadId: string;
102
- role: 'user' | 'assistant';
103
- }): Promise<unknown>;
104
- updateMessage?(id: string, patch: AssistantRowValues): Promise<unknown>;
105
- deleteMessage?(id: string): Promise<unknown>;
106
- }
107
- /** Live snapshot of the assistant body, taken from the producer's own
108
- * accumulators. `parts` is the DRAFT projection (`draftParts()`), never the
109
- * finalized one — see `draftAssistantParts`. */
110
- interface AssistantDraftSnapshot {
111
- content: string;
112
- parts?: Array<Record<string, unknown>>;
113
- usage?: ChatTurnUsage;
114
- model?: string;
115
- }
116
- /** Product-tunable cadence. Defaults are stated on each field; a product with
117
- * a chattier or heavier workload moves them without forking the writer. */
118
- interface DraftPersistenceTuning {
119
- /** Minimum wall-clock gap between draft writes, in ms. Default 2000.
120
- *
121
- * Justification for 2 s, from a measured tool-heavy production run (517
122
- * stream events over ~90 s wall = ~5.7 events/s): a 2 s floor with the
123
- * dirty gate turns 517 candidate writes into <= 45, while leaving the
124
- * durable row at most 2 s stale — an order of magnitude below the time it
125
- * takes a viewer to open a tab and render, so a late viewer never perceives
126
- * the lag. Fleet arithmetic at 10k concurrent 60 s runs: <= 30 updates per
127
- * run x 167 run-starts/s = ~334 row-updates/s spread over per-tenant
128
- * shards. Lower it and write amplification grows with no perceptible
129
- * freshness gain; raise it past ~5 s and a late viewer starts seeing a
130
- * visibly truncated answer. */
131
- intervalMs?: number;
132
- /** Serialized-parts size (bytes) past which the interval backs off, so a
133
- * turn accumulating a megabyte-scale `parts` blob does not rewrite it every
134
- * interval. Default 262144 (256 KiB) -> interval x 2.5; ten times that ->
135
- * interval x 5. The final write is never throttled. */
136
- backoffBytes?: number;
137
- /** Per-tool-part output cap applied to DRAFTS ONLY (bytes). A tool returning
138
- * a large blob would otherwise be rewritten in full on every draft. The
139
- * final write always carries the untruncated value. Default 32768 (32 KiB);
140
- * 0 disables truncation. */
141
- maxDraftToolOutputBytes?: number;
142
- }
143
- /** Define the inputs required to construct an assistant draft writer */
144
- interface AssistantDraftWriterOptions extends DraftPersistenceTuning {
145
- store: AssistantDraftStore;
146
- threadId: string;
147
- /** DETERMINISTIC row id for this turn's assistant message — the whole
148
- * idempotency mechanism. Derived from the turn's existing identity
149
- * (`deriveExecutionId` in the interactive lane, the turn id in the detached
150
- * lane), so a re-entered turn addresses the SAME row: the writer looks the
151
- * id up before its first insert and patches what it finds. */
152
- messageId: string;
153
- /** Read the producer's live accumulators. Returns null before the producer
154
- * is resolved (the assembly defers box resolution into the first pull). */
155
- snapshot(): AssistantDraftSnapshot | null;
156
- /** Pre-persist text transform (`/redact`'s `redactPII`). Applied to the
157
- * draft's scalar content AND every draft text part — parity with the final
158
- * write, or incremental persistence would re-open the at-rest PII leak that
159
- * transform closed, just seconds earlier and on every turn. */
160
- transformText?(text: string): string | Promise<string>;
161
- log?: (message: string, meta?: Record<string, unknown>) => void;
162
- }
163
- /** Coalescing writer that keeps one durable assistant row in step with a
164
- * streaming turn. Created per turn; not reusable. */
165
- interface AssistantDraftWriter {
166
- /** Arm/trigger a draft write from one engine event. Synchronous by design —
167
- * the write itself is fire-and-forget so the stream is never blocked on
168
- * store latency. */
169
- notify(event: {
170
- type?: unknown;
171
- }): void;
172
- /** Stop drafting and settle any in-flight write. Called before the final
173
- * write so a late draft can never clobber the authoritative row. */
174
- close(): Promise<void>;
175
- /** Write the AUTHORITATIVE completion values onto this turn's row —
176
- * insert-or-patch under the same deterministic id, un-throttled and
177
- * un-truncated. Errors propagate: the final write is the one that must not
178
- * fail silently. Implies {@link close}. */
179
- finalize(values: AssistantRowValues): Promise<void>;
180
- /** The durable row this turn is writing, once one exists. */
181
- rowId(): string | undefined;
182
- /** Retract the row for a turn that produced nothing (mirrors the final
183
- * write's empty-turn skip, which leaves no row at all today). Also retracts
184
- * a row a PREVIOUS attempt left behind, so a re-entered turn that ends
185
- * empty converges on "no row" rather than a stale partial. */
186
- discard(): Promise<void>;
187
- /** Diagnostics: how many draft writes actually reached the store. */
188
- writeCount(): number;
189
- }
190
- /** True when this event should arm a draft write. */
191
- declare function isDraftContentEvent(event: {
192
- type?: unknown;
193
- }): boolean;
194
- /** Build the coalescing draft writer for one turn. */
195
- declare function createAssistantDraftWriter(options: AssistantDraftWriterOptions): AssistantDraftWriter;
196
- /** True when a store can support incremental persistence at all. Without
197
- * `updateMessage` a draft row could never be patched, so the caller keeps
198
- * today's exact single-write behavior. */
199
- declare function storeSupportsDraftPersistence(store: AssistantDraftStore): boolean;
200
- /** The row id an `appendMessage` actually returned, or `null` when the store
201
- * returned nothing usable. `ChatTurnMessageStore.appendMessage` is typed
202
- * `Promise<unknown>` so a product adapter is free to resolve `void`; every
203
- * caller that wants to NAME the row it just wrote has to read defensively.
204
- *
205
- * Deliberately no fallback to a caller-assigned id — see `writeOnce`, which
206
- * adds its own. A caller that let the store mint the id has nothing to fall
207
- * back TO, and guessing one would report a row that may not exist. */
208
- declare function rowIdOf(inserted: unknown): string | null;
209
- /** The default deterministic assistant-row id for a turn. Readable on purpose
210
- * (an operator grepping a transcript row id finds the run), and stable across
211
- * re-entries because every input already is. */
212
- declare function assistantRowIdForTurn(turnKey: string): string;
213
-
214
- /**
215
- * `createChatTurnRoutes` — the assembled server chat vertical (issue #188
216
- * Phase 1). One factory composing the pieces every product re-wired by hand:
217
- *
218
- * body parse/validate → `/web` `parseJsonObjectBody` + `./wire`
219
- * turn identity → `/stream` `resolveChatTurn` + agent-runtime `/durable`
220
- * `deriveExecutionId`
221
- * producer → injected seam (sandbox lane via
222
- * `createSandboxChatProducer`; router lane is the
223
- * product's own `ChatTurnProducer`)
224
- * turn engine → agent-runtime `/durable` `handleChatTurn` (verbatim)
225
- * durability → `/stream` turn-buffer tap, wired BY DEFAULT
226
- * (tee + drain keeps the turn running after a
227
- * client drop; replay serves the buffered tail)
228
- * persistence → injected `/chat-store`-shaped store
229
- * (user row on send, assistant row on completion)
230
- * interactions answer → `/interactions` `createInteractionAnswerRoute`
231
- *
232
- * Handlers are web-standard `Request → Response` (Workers, Node 18+, Deno) —
233
- * no router import. Auth/access is one injected `authorize` seam, composable
234
- * with `/app-auth` guards but not coupled to them.
235
- *
236
- * Optional product seams let a complex turn-orchestrator compose the vertical
237
- * instead of hand-rolling a generator — each omittable to the exact behavior
238
- * above: `turnLock` (single-flight acquire/release around the turn),
239
- * `contextGate` (pre-producer domain-readiness short-circuit), `beforeTurn`
240
- * (observe + augment the producer input), `lifecycle` (deterministic
241
- * start/complete/error telemetry), `heartbeat` (keepalive during silent
242
- * producer waits), and `onRawEvent` (the raw producer events, for telemetry) —
243
- * plus the `authorize` result's `insertUserMessage` flag (suppress the user row
244
- * for a product-dispatched turn). `handleChatTurn` stays the engine — the seams
245
- * only wrap its input, its producer stream, and its settle.
246
- *
247
- * Seam stability: all of the above are STABLE and safe to depend on. They
248
- * graduated in #227 against the bar this package holds itself to — a seam is
249
- * provisional until two INDEPENDENT consumers exercise it, because one
250
- * consumer's shape is indistinguishable from that consumer's assumptions.
251
- * `turnLock` cleared it with `/turn-stream`'s shared DO adapter (#221);
252
- * `contextGate`, `beforeTurn`, `onRawEvent` and `insertUserMessage` each
253
- * cleared it with two product verticals reading them differently — and the
254
- * review found no leaked assumptions to fix, only `ChatRouteEvent` to export.
255
- *
256
- * They stay FLAT top-level options (not grouped under a `hooks` object): that
257
- * grouping would break every shipped consumer's call for no mechanism gain, and
258
- * this package's exports are additive-only. For the same reason `onRawEvent`
259
- * keeps its two-argument `(event, context)` signature rather than being
260
- * normalized to the single-args shape the other seams take.
261
- */
262
-
263
- /** Usage receipt persisted onto the assistant message (the flattened
264
- * `step-finish` shape `/chat-store`'s columns mirror). */
265
- interface ChatTurnUsage {
266
- inputTokens?: number;
267
- outputTokens?: number;
268
- reasoningTokens?: number;
269
- cacheReadTokens?: number;
270
- cacheWriteTokens?: number;
271
- costUsd?: number;
272
- }
273
- /** What the route persists — a structural subset of `/chat-store`'s
274
- * `ChatStore`, so `createChatStore(db, tables)` satisfies it directly and a
275
- * product with its own persistence adapts without importing drizzle. */
276
- interface ChatTurnMessageStore {
277
- listMessages(threadId: string): Promise<Array<{
278
- id: string;
279
- role: 'user' | 'assistant' | 'system' | 'tool';
280
- content: string;
281
- parts?: ChatMessagePart[] | null;
282
- }>>;
283
- appendMessage(input: {
284
- /** Caller-assigned row id. Honored by `/chat-store`'s `createChatStore`;
285
- * a product store free to ignore it (the writer then adopts whatever id
286
- * the insert returns). Set only by incremental persistence. */
287
- id?: string;
288
- threadId: string;
289
- role: 'user' | 'assistant';
290
- content: string;
291
- parts?: ChatMessagePart[];
292
- model?: string | null;
293
- requestedModel?: string | null;
294
- servedModel?: string | null;
295
- servedProvider?: string | null;
296
- servedSource?: string | null;
297
- inputTokens?: number | null;
298
- outputTokens?: number | null;
299
- reasoningTokens?: number | null;
300
- cacheReadTokens?: number | null;
301
- cacheWriteTokens?: number | null;
302
- costUsd?: number | null;
303
- }): Promise<unknown>;
304
- /** Patch an existing row. Its PRESENCE is what enables incremental assistant
305
- * persistence — a store without it keeps today's exact single-write-on-
306
- * completion behavior, which is what makes this additive. */
307
- updateMessage?(id: string, patch: {
308
- content?: string;
309
- parts?: ChatMessagePart[];
310
- model?: string | null;
311
- requestedModel?: string | null;
312
- servedModel?: string | null;
313
- servedProvider?: string | null;
314
- servedSource?: string | null;
315
- inputTokens?: number | null;
316
- outputTokens?: number | null;
317
- reasoningTokens?: number | null;
318
- cacheReadTokens?: number | null;
319
- cacheWriteTokens?: number | null;
320
- costUsd?: number | null;
321
- }): Promise<unknown>;
322
- /** Remove a row. Used to retract a draft assistant row for a turn that ended
323
- * producing nothing, so the empty-turn case still leaves no row at all. */
324
- deleteMessage?(id: string): Promise<unknown>;
325
- }
326
- /** `ChatTurnProducer` plus the persisted projection the assembly reads after
327
- * drain. `createSandboxChatProducer` returns this; a router-lane producer
328
- * may omit the optional members (finalText persists as a single text part). */
329
- interface ChatTurnRouteProducer extends ChatTurnProducer {
330
- assistantParts?(): Array<Record<string, unknown>>;
331
- /** MID-STREAM snapshot of the same projection, safe to call while the turn
332
- * is running: no dangling-tool terminalization, no pending-ask settlement
333
- * (see `/stream`'s `draftAssistantParts`). Read by incremental persistence.
334
- * A producer that omits it still drafts — the scalar text — but persists no
335
- * parts until the turn completes. */
336
- draftParts?(): Array<Record<string, unknown>>;
337
- usage?(): ChatTurnUsage;
338
- /** The model that SERVED the turn. With failover wired this is the model that
339
- * actually answered, which is not necessarily the one the caller preferred —
340
- * read it after the stream drains, never before. */
341
- model?: string;
342
- /** Model-failover attribution, when the producer supports it. Reported onto
343
- * the usage/billing receipt so a downgrade is never silent. */
344
- modelFailover?(): ChatTurnModelFailover;
345
- /** Requested-versus-served attribution reported by the sandbox sidecar.
346
- * `echoReceived` distinguishes a missing echo from a partial echo. */
347
- modelAttribution?(): ChatTurnModelAttribution;
348
- }
349
- /** Which model served, and what it took to get there. */
350
- interface ChatTurnModelFailover {
351
- /** The model that served the turn. */
352
- model?: string;
353
- /** Every model tried, in order, with the reason each was abandoned. */
354
- attempts: ModelFailoverAttempt[];
355
- /** True when the preferred model did not serve. */
356
- usedFallback: boolean;
357
- }
358
- /** Requested and effective model attribution for one sandbox turn. */
359
- interface ChatTurnModelAttribution {
360
- /** The model explicitly requested by the caller, before shell failover. */
361
- requestedModel?: string;
362
- /** The model the downstream sandbox reports actually served the turn. */
363
- servedModel?: string;
364
- /** The provider that served the turn, when echoed by the sandbox. */
365
- servedProvider?: string;
366
- /** How the sandbox selected the served model. */
367
- servedSource?: 'request' | 'environment' | 'profile';
368
- /** True when a structurally valid effective-backend echo was observed. */
369
- echoReceived: boolean;
370
- }
371
- /** Resolve authorization status and context for a chat turn including tenant and user identification */
372
- type ChatTurnAuthorization<TContext> = {
373
- ok: true;
374
- tenantId: string;
375
- userId: string;
376
- context: TContext;
377
- /** When `false`, skip the `role:'user'` message insert for this turn — for
378
- * a product-dispatched / synthetic turn (e.g. a follow-up the product
379
- * raised itself) that must not surface a new user row. Composes with —
380
- * never overrides — the engine's retry-dedup: `authorize` runs before
381
- * turn identity is resolved, so it cannot tell a retry from a fresh turn;
382
- * a turn already deduped stays deduped. Omit / `true` → today's behavior.
383
- *
384
- * Settled shape (#227). The validated case in both consumers is the same:
385
- * a durable plan-approval follow-up re-entering an execution the decision
386
- * route already enqueued — a decision, not a typed message, so it must
387
- * not surface a user bubble. Suppressing the insert also propagates:
388
- * `ChatTurnProduceArgs.userMessageId` is `null` for the rest of the turn
389
- * when there is no row to reuse, so a seam anchoring to the user row must
390
- * handle that arm rather than assume a string. */
391
- insertUserMessage?: boolean;
392
- } | {
393
- ok: false;
394
- response: Response;
395
- };
396
- /** Define arguments required to authorize a chat turn based on intent and request details */
397
- interface ChatTurnAuthorizeArgs {
398
- request: Request;
399
- intent: 'turn' | 'replay' | 'running';
400
- /** Parsed, validated POST body (turn intent only). */
401
- body?: ChatTurnRequestPayload;
402
- /** The buffered turn id being replayed (replay intent only). */
403
- turnId?: string;
404
- /** The thread whose running turns are being discovered (running intent only). */
405
- threadId?: string;
406
- }
407
- /** Define the arguments required to produce a chat turn with context and messaging details */
408
- interface ChatTurnProduceArgs<TContext> {
409
- request: Request;
410
- body: ChatTurnRequestPayload;
411
- identity: ChatTurnIdentity;
412
- context: TContext;
413
- /** The message to send: plain text, or parts when the client attached
414
- * files (a text part is prepended from `content` when present). */
415
- prompt: string | ChatTurnPartInput[];
416
- /** Stable id for cross-process reconnect (`deriveExecutionId`). */
417
- executionId: string;
418
- /** The turn-buffer id announced to the client for replay. */
419
- turnStreamId: string;
420
- priorMessages: PersistedChatMessageForTurn[];
421
- /** The durable `role:'user'` row this turn is anchored to — the row the
422
- * factory just inserted, or the one retry-dedup REUSED. Products anchor
423
- * optimistic-bubble swaps, retry targeting, and stop-polling to it, and it
424
- * is the only way to name a REUSED row (which `priorMessages` excludes).
425
- *
426
- * Three states, all meaningful:
427
- * - `undefined` — not resolved yet. `turnLock.acquire` is the one seam that
428
- * sees this: it runs before any side effect by contract, so the row does
429
- * not exist when it reads the args.
430
- * - `null` — resolved, no row: `authorize` returned `insertUserMessage:
431
- * false` on a turn with nothing to reuse, or the store's `appendMessage`
432
- * resolved without a usable id.
433
- * - a string — the row id, for `contextGate`, `beforeTurn`, and `produce`. */
434
- userMessageId?: string | null;
435
- }
436
- /** One event as it crosses the route: the producer's own vocabulary, or an
437
- * injected keepalive. Same shape the engine forwards verbatim.
438
- *
439
- * Public because it IS the vocabulary of two seams — `onRawEvent`'s parameter
440
- * and `heartbeat.event`'s return. Exported so a product can declare a
441
- * standalone handler (`function onRawEvent(e: ChatRouteEvent, ctx: T)`) rather
442
- * than depending on contextual typing from an inline object literal. */
443
- type ChatRouteEvent = {
444
- type: string;
445
- data?: Record<string, unknown>;
446
- };
447
- /** Keepalive emitted while the producer is quiet (long tool calls, first-token
448
- * wait) so client watchdogs stay re-armed. One is emitted each time
449
- * `intervalMs` elapses with no producer event; the window resets on every real
450
- * event, so a chatty producer never triggers one. The product owns the event
451
- * shape (`type` + `data`). Omit → no keepalives (today's behavior). */
452
- interface ChatTurnHeartbeat {
453
- intervalMs: number;
454
- event(info: {
455
- elapsedMs: number;
456
- tick: number;
457
- }): ChatRouteEvent;
458
- }
459
- /** Patch a `beforeTurn` hook returns to augment the producer's input. Omitted
460
- * fields keep the route-assembled value; the product's `produce` still owns
461
- * the system prompt. */
462
- interface ChatTurnInputPatch {
463
- prompt?: string | ChatTurnPartInput[];
464
- priorMessages?: PersistedChatMessageForTurn[];
465
- }
466
- /** Pre-turn readiness verdict — proceed, or short-circuit with the product's
467
- * own `Response` (e.g. a canned assistant reply asking for missing context).
468
- * Distinct from `authorize`: this gates domain readiness, not access. */
469
- type ChatTurnGateResult = {
470
- proceed: true;
471
- } | {
472
- proceed: false;
473
- response: Response;
474
- };
475
- /** Single-flight lock verdict — acquired (with an opaque handle passed back to
476
- * `release`), or already held (short-circuit with the product's 409-style
477
- * `Response`). */
478
- type ChatTurnLockResult = {
479
- acquired: true;
480
- handle?: unknown;
481
- } | {
482
- acquired: false;
483
- response: Response;
484
- };
485
- /** Async acquire/release wrapped around the turn. `acquire` runs before any
486
- * side effect; `release` runs once when the turn settles — including on a
487
- * short-circuit or a throw. */
488
- interface ChatTurnLock<TContext> {
489
- acquire(args: ChatTurnProduceArgs<TContext>): ChatTurnLockResult | Promise<ChatTurnLockResult>;
490
- release(handle: unknown): void | Promise<void>;
491
- }
492
- interface ChatTurnLifecycleBase<TContext> {
493
- identity: ChatTurnIdentity;
494
- executionId: string;
495
- turnStreamId: string;
496
- context: TContext;
497
- }
498
- /** Define lifecycle start event with context and timestamp for a chat turn */
499
- interface ChatTurnLifecycleStart<TContext> extends ChatTurnLifecycleBase<TContext> {
500
- startedAt: number;
501
- }
502
- /** Define the structure representing the completion state of a chat turn lifecycle with usage data */
503
- interface ChatTurnLifecycleComplete<TContext> extends ChatTurnLifecycleBase<TContext> {
504
- finalText: string;
505
- usage: ChatTurnUsage;
506
- durationMs: number;
507
- /** The model that SERVED the turn — the fallback's id when failover moved it.
508
- * `usage` is that model's, so telemetry that splits cost or quality by model
509
- * must key on this and not on the requested one. */
510
- model?: string;
511
- /** Requested-versus-served attribution. A difference is detectable from
512
- * this receipt and from the persisted assistant row independently. */
513
- requestedModel?: string;
514
- servedModel?: string;
515
- servedProvider?: string;
516
- servedSource?: 'request' | 'environment' | 'profile';
517
- /** Attribution for a downgrade: which models were tried and why each failed.
518
- * `undefined` when the producer reports no failover support. */
519
- modelFailover?: ChatTurnModelFailover;
520
- /** The durable `role:'assistant'` row this turn wrote, or `null` when it
521
- * wrote none (an empty turn leaves no row — a draft started mid-stream is
522
- * retracted). The detached lane surfaces the same id as
523
- * `DetachedTurnResult.messageId`; one contract, two lanes. */
524
- assistantMessageId: string | null;
525
- /** Set when `contextGate` answered this turn and the producer never ran.
526
- *
527
- * Present so telemetry can tell "the model produced nothing" apart from "the
528
- * model was never asked" — they look identical here (`finalText: ''`,
529
- * empty `usage`) and mean opposite things. A gated turn writes no assistant
530
- * row, so `assistantMessageId` is `null`, and the billing/persistence
531
- * `onTurnComplete` is deliberately NOT fired for it. */
532
- gated?: true;
533
- }
534
- /** Represent an error occurring during a chat turn lifecycle with context and duration information */
535
- interface ChatTurnLifecycleError<TContext> extends ChatTurnLifecycleBase<TContext> {
536
- error: unknown;
537
- durationMs: number;
538
- }
539
- /** Deterministic run telemetry: `onTurnStart` fires before the producer runs;
540
- * exactly one of `onTurnComplete` / `onTurnError` fires after the turn
541
- * settles, always after `onTurnStart`. Failure is derived from the turn's own
542
- * `error` / `session.run.failed` events (or a drain throw), not the engine's
543
- * lifecycle envelope. Hook errors are swallowed — telemetry never fails a
544
- * turn. */
545
- interface ChatTurnLifecycle<TContext> {
546
- onTurnStart?(info: ChatTurnLifecycleStart<TContext>): void | Promise<void>;
547
- onTurnComplete?(info: ChatTurnLifecycleComplete<TContext>): void | Promise<void>;
548
- onTurnError?(info: ChatTurnLifecycleError<TContext>): void | Promise<void>;
549
- }
550
- /** What a settled turn reports to `onTurnComplete` — the product's
551
- * post-processing seam (billing, titles, audit). */
552
- interface ChatTurnCompleteInput<TContext> {
553
- identity: ChatTurnIdentity;
554
- finalText: string;
555
- context: TContext;
556
- failed: boolean;
557
- failureReason?: string;
558
- /** The model that SERVED this turn. With failover wired it may differ from
559
- * the requested one, so a product that bills or scores per model MUST read
560
- * it here rather than assuming the model it asked for. */
561
- model?: string;
562
- /** Requested-versus-served attribution. A difference is detectable from
563
- * this receipt and from the persisted assistant row independently. */
564
- requestedModel?: string;
565
- servedModel?: string;
566
- servedProvider?: string;
567
- servedSource?: 'request' | 'environment' | 'profile';
568
- /** Present when the producer supports failover: the full attempt trail, and
569
- * `usedFallback` — the flag that makes a silent downgrade impossible. */
570
- modelFailover?: ChatTurnModelFailover;
571
- /** The durable `role:'assistant'` row this turn wrote, or `null` when it
572
- * wrote none (an empty turn leaves no row).
573
- *
574
- * Populated even when `failed` is true — a terminal error event still
575
- * persists whatever partial answer arrived, and that pairing is exactly
576
- * what lets a product render an error row against a REAL message instead of
577
- * hunting for the newest row in the thread. The detached lane surfaces the
578
- * same id as `DetachedTurnResult.messageId`. */
579
- assistantMessageId: string | null;
580
- }
581
- /** Define options to configure chat turn routes including authorization, storage, and event buffering */
582
- interface CreateChatTurnRoutesOptions<TContext = void> {
583
- /** Names the product in `deriveExecutionId` so retries land on the same
584
- * substrate execution. */
585
- projectId: string;
586
- /** Authenticate + authorize the caller for a turn or a replay. The only
587
- * product-supplied access step: session auth, thread/workspace access,
588
- * seat/balance gates, rate limits all live here. */
589
- authorize(args: ChatTurnAuthorizeArgs): Promise<ChatTurnAuthorization<TContext>>;
590
- /** Thread/message persistence (`/chat-store`'s store or a product adapter). */
591
- store: ChatTurnMessageStore;
592
- /** Turn-event buffer (`createD1TurnEventStore(env.DB)` or `/turn-stream`'s
593
- * `createDurableObjectTurnEventStore(env.TURN_STREAM_DO)` in production,
594
- * `createMemoryTurnEventStore()` in tests). Wired by default — every turn
595
- * is buffered and replayable. */
596
- turnStore: TurnEventStore;
597
- /** Build the turn's event stream. Sandbox lane: `streamSandboxPrompt(...)`
598
- * wrapped in `createSandboxChatProducer`. Router/openai-compat lane: the
599
- * product's own producer. May be async (box resolution). */
600
- produce(args: ChatTurnProduceArgs<TContext>): ChatTurnRouteProducer | Promise<ChatTurnRouteProducer>;
601
- /** Single-flight lock acquired before any side effect and released once when
602
- * the turn settles (including short-circuit/throw). `/turn-stream`'s
603
- * `createDurableTurnLock` is the shared DO-backed implementation. Omit →
604
- * no lock. */
605
- turnLock?: ChatTurnLock<TContext>;
606
- /** Pre-turn readiness gate that can short-circuit with a product `Response`
607
- * before the producer runs (the user row is already persisted). Runs after
608
- * `turnLock.acquire`, before `beforeTurn`. Omit → always proceed.
609
- *
610
- * Settled shape (#227). What a consumer may depend on:
611
- * - `args.userMessageId` is RESOLVED here — a string, or `null` when the
612
- * insert was suppressed with nothing to reuse. (`turnLock.acquire` is the
613
- * only seam that sees it `undefined`.)
614
- * - `{proceed:false}` releases the lock and returns the product's `Response`
615
- * verbatim: `beforeTurn` and `produce` never run, no assistant row is
616
- * written, and the user row already inserted is KEPT — a real user turn
617
- * whose assistant side is the gate's own response.
618
- * - Always returning `{proceed:true}` is supported, not a misuse: the seam
619
- * doubles as the one place that runs after the user row exists and before
620
- * the producer, which is where per-turn analytics and readiness
621
- * precomputation belong. A gate that never gates is a valid consumer. */
622
- contextGate?(args: ChatTurnProduceArgs<TContext>): ChatTurnGateResult | Promise<ChatTurnGateResult>;
623
- /** Observe the assembled producer input and optionally augment it (rewrite
624
- * the prompt / prior messages) before the producer runs. Omit → no change.
625
- *
626
- * Settled shape (#227). BOTH return arms are contract:
627
- * - a `ChatTurnInputPatch` shallow-merges over the route-assembled args, so
628
- * an omitted field keeps the route's value;
629
- * - `void` means "no patch" — and mutating `args.context` in place is the
630
- * supported way to thread request-scoped state forward to `produce` /
631
- * `lifecycle` / `onTurnComplete`, which all receive the same object.
632
- *
633
- * A throw propagates (the turn fails with the lock released). It runs BEFORE
634
- * `lifecycle.onTurnStart`, so a throw here fires no terminal lifecycle hook —
635
- * the span never opened. Telemetry for a failure in this seam belongs in the
636
- * seam, not in `lifecycle`. */
637
- beforeTurn?(args: ChatTurnProduceArgs<TContext>): ChatTurnInputPatch | void | Promise<ChatTurnInputPatch | void>;
638
- /** Deterministic run telemetry (start / complete / error) with identity and
639
- * timing. Omit → no telemetry. */
640
- lifecycle?: ChatTurnLifecycle<TContext>;
641
- /** Keepalive injected while the producer is quiet. Omit → no keepalives. */
642
- heartbeat?: ChatTurnHeartbeat;
643
- /** Observe each event the producer emits, before the engine frames it and
644
- * before any heartbeat injection (the raw sidecar-producer events, for
645
- * telemetry). Never alters the stream; errors are swallowed. Distinct from
646
- * `onEvent`, which sees the engine-framed stream incl. lifecycle envelopes.
647
- *
648
- * Settled shape (#227). What a consumer may depend on:
649
- * - it sees EXACTLY the producer's own events — no engine lifecycle
650
- * envelopes, and no injected keepalives (`heartbeat` wraps the stream
651
- * downstream of this tap, so a synthetic event never reaches a trace);
652
- * - a throw is caught and logged, never surfaced: a broken telemetry sink
653
- * cannot fail a turn or truncate the client's stream;
654
- * - it is an OBSERVER — the return value is ignored, and the event object
655
- * continues downstream. Mutating it mutates the stream; don't.
656
- *
657
- * Takes `(event, context)` rather than one args object, matching both
658
- * shipped consumers; see the module header on why it stays that way. */
659
- onRawEvent?(event: ChatRouteEvent, context: TContext): void | Promise<void>;
660
- /** Pre-persist transform of the final text (e.g. `/redact`'s `redactPII`).
661
- * Live stream is never altered. */
662
- transformFinalText?(text: string): string | Promise<string>;
663
- /** Incremental persistence of the assistant row WHILE the turn streams
664
- * (`./draft-persistence`), so a viewer arriving mid-run is served from
665
- * durable storage instead of the streaming gateway's hot event buffer.
666
- *
667
- * This is what lets the gateway keep that hot buffer SHORT at scale: its
668
- * Redis footprint is one sorted set per session refreshed on every push, so
669
- * memory grows LINEARLY with `ttl x concurrent sessions`. Stretching the
670
- * TTL to cover late viewers buys memory proportional to the increase and
671
- * still serves nothing past the new horizon. The buffer stays a reconnect
672
- * window; durable storage — kept at most one cadence interval stale by this
673
- * option — is the history tier.
674
- *
675
- * Enabled by DEFAULT whenever `store.updateMessage` exists (every
676
- * `/chat-store` consumer); a store without it keeps today's exact
677
- * single-write behavior. Pass `false` to opt out, or an object to tune the
678
- * cadence. Wiring it against a store that cannot patch rows throws at route
679
- * construction rather than silently no-op'ing. */
680
- incrementalPersistence?: false | DraftPersistenceTuning;
681
- /** Deterministic durable row id for this turn's assistant message. Default
682
- * `assistant:<executionId>` — already stable across retries because
683
- * `deriveExecutionId` is. Override only if the product's message ids have a
684
- * format constraint; it MUST stay deterministic per turn or a re-entered
685
- * turn will duplicate its row instead of converging. */
686
- draftMessageId?(args: {
687
- identity: ChatTurnIdentity;
688
- executionId: string;
689
- threadId: string;
690
- }): string;
691
- /** Post-processing after a turn settles (billing, titles, audit). Fires with
692
- * `failed:true` + `failureReason` when the turn carried a terminal error
693
- * event (model 402 / rate-limit / server error) instead of a clean
694
- * completion, so products skip the deduct and render an error row rather
695
- * than billing an empty turn and marking it done. A turn that THROWS never
696
- * reaches this hook (the engine skips it on a producer throw). Errors are
697
- * swallowed by the engine — they never fail a streamed turn. */
698
- onTurnComplete?(input: ChatTurnCompleteInput<TContext>): Promise<void>;
699
- /** Per-event side channel (product broadcast). The turn-buffer tap is
700
- * already wired; this runs in addition. */
701
- onEvent?(event: {
702
- type: string;
703
- data?: Record<string, unknown>;
704
- }, context: TContext): void | Promise<void>;
705
- /** Trace flush handed to `waitUntil` (OTLP export). */
706
- traceFlush?(context: TContext): Promise<void>;
707
- /** Compose the interaction-answer endpoints (`/interactions`). Omit when the
708
- * product has no sidecar ask channel. */
709
- interactions?: InteractionAnswerRouteOptions;
710
- /** Byte budget for inline prompt parts. Default `INLINE_PARTS_MAX_BYTES`. */
711
- maxInlinePartBytes?: number;
712
- /** Per-flush coalescer for the turn buffer. Default `coalesceDeltas` (this
713
- * assembly streams the client vocabulary's `{type:'text'|'reasoning',
714
- * text}` lines, which it merges). A producer streaming raw
715
- * `message.part.updated` events passes `coalesceChatStreamEvents`. */
716
- coalesceTurnEvents?: (events: unknown[]) => unknown[];
717
- replay?: {
718
- pollMs?: number;
719
- timeoutMs?: number;
720
- };
721
- log?: (message: string, meta?: Record<string, unknown>) => void;
722
- }
723
- /** Define routes to run, replay, and list running chat turns with streaming and reconnect support */
724
- interface ChatTurnRoutes {
725
- /** POST — run one turn, streaming NDJSON. First line is
726
- * `{type:'turn', turnId}` (the replay handle); the rest is the engine's
727
- * event protocol. Pass the platform's `waitUntil` so the turn keeps
728
- * running (and buffering) after a client disconnect. */
729
- turn(request: Request, ctx?: {
730
- waitUntil?(p: Promise<unknown>): void;
731
- }): Promise<Response>;
732
- /** GET — replay a buffered turn from `?fromSeq=` (0 = everything), then
733
- * follow it live until it completes. */
734
- replay(request: Request, params: {
735
- turnId: string;
736
- }): Promise<Response>;
737
- /** GET `?threadId=` — the reconnect-discovery endpoint: the turn ids still
738
- * running on a thread, so a client that reloaded mid-turn can re-attach to
739
- * the live stream via {@link replay} instead of losing it. Returns `[]` when
740
- * the turn store cannot enumerate running turns (`listRunning` unimplemented). */
741
- running(request: Request): Promise<Response>;
742
- /** list/answer endpoints from `/interactions`; null when not configured. */
743
- interactions: InteractionAnswerRoute | null;
744
- }
745
- /** Build chat turn routes to handle and validate incoming chat requests with optional logging */
746
- declare function createChatTurnRoutes<TContext = void>(options: CreateChatTurnRoutesOptions<TContext>): ChatTurnRoutes;
747
-
748
- /**
749
- * Model failover for a STREAMING turn.
750
- *
751
- * `/model-resolution`'s `runWithModelFailover` already owns the policy: walk a
752
- * chain, classify a resolved-or-thrown signal, re-throw a non-outage failure
753
- * immediately, and carry the attempt trail. This module does NOT re-implement
754
- * any of that — it composes it. What it adds is the one thing a whole-call
755
- * primitive cannot express, because a stream fails PARTWAY:
756
- *
757
- * **A turn may only fail over before its first client-visible byte.**
758
- *
759
- * Once a text delta, tool call, or ask has reached the browser (and the
760
- * persisted transcript), restarting on another model would duplicate the
761
- * answer. So each attempt is probed: open the stream, pull events into a small
762
- * buffer, and decide at the first meaningful event whether this model is
763
- * serving. Committing replays the buffer and hands the live iterator through;
764
- * abandoning discards the buffer (those events describe the dead model's
765
- * session — including its `step-finish` usage, which must never be billed) and
766
- * lets `runWithModelFailover` walk to the next model.
767
- *
768
- * The classification itself is `isUpstreamUnavailable` verbatim, so this path
769
- * inherits the measured facts from the 2026-07-25 outage — above all that an
770
- * outage is NOT always a thrown error: the sandbox RESOLVES a terminal `error`
771
- * event carrying `{ errorCode: 'provider_inference_unavailable' }`, which a
772
- * classifier inspecting only `catch` misses entirely. That resolved shape is
773
- * the whole reason the breakage went unnoticed, so it is classified here first.
774
- *
775
- * Conservative by construction:
776
- * - A terminal failure that is NOT an outage (400, bad schema, content filter)
777
- * COMMITS rather than failing over — it surfaces to the user exactly as it
778
- * does today. Those fail identically on every model; walking the chain would
779
- * only multiply latency and spend to reach the same error.
780
- * - A clean stream that produced nothing NEVER walks the chain. An empty answer
781
- * is not evidence of a dead upstream, and a silent re-roll on another model is
782
- * precisely the unattributable downgrade this work exists to prevent. Opt-in
783
- * `emptyTurnRetries` re-runs the SAME model instead, which leaves attribution
784
- * untouched — measured on production 2026-07-27, an empty turn is a transient
785
- * platform flake that a same-model re-run recovers (8 hard cases: 7/8
786
- * delivered on the first pass, 8/8 with one re-run, at a cost of 1 extra turn
787
- * in 9). Default `0`, so the behavior is unchanged unless a product asks.
788
- * - A responsive chain of length 1 costs no extra call or latency and remains
789
- * byte-identical to no failover. A silent chain is now deliberately bounded.
790
- */
791
-
792
- /**
793
- * True when `event` puts content in front of the user (or in the persisted
794
- * transcript), making a restart on another model unsafe.
795
- *
796
- * Deliberately an allow-list of KNOWN-INERT types rather than a deny-list: an
797
- * unrecognized event commits. Getting this wrong in the safe direction costs a
798
- * missed failover; getting it wrong the other way duplicates a user's answer.
799
- */
800
- declare function isCommittingSandboxEvent(event: unknown): boolean;
801
- /**
802
- * Condense an abandoned attempt's raw failure text into something safe to show
803
- * a customer in the transcript.
804
- *
805
- * Measured on the live router 2026-07-27 19:40 UTC: an edge 5xx arrives as
806
- * Cloudflare's full HTML error PAGE, so the verbatim reason began
807
- * `<!DOCTYPE html>\n<!--[if lt IE 7]> <html class="no-js ie6 oldie"…` and the
808
- * fallback notice pasted 200 bytes of that markup into the answer the customer
809
- * reads. The cause is worth stating; the markup is not.
810
- *
811
- * Only the DISPLAY string is condensed. The full text stays on
812
- * `modelFailover.attempts[].reason` for the operator, so nothing is lost —
813
- * this narrows what the customer sees, never what telemetry records.
814
- */
815
- declare function summarizeFailoverReason(reason: string): string;
816
- /** A terminal failure event, classified. `outage` decides failover vs surface. */
817
- interface TerminalFailure {
818
- outage: boolean;
819
- reason: string;
820
- code?: string;
821
- }
822
- /**
823
- * Classify a terminal failure event. Returns `null` for any non-terminal event.
824
- *
825
- * The RESOLVED shape is checked first and deliberately: the sandbox reports an
826
- * upstream outage by resolving `{ success: false, errorCode:
827
- * 'provider_inference_unavailable' }` inside a terminal `error` event's `data`,
828
- * never by throwing. Both `data` and the whole record are offered to
829
- * `isUpstreamUnavailable` so a payload nested either way is caught.
830
- */
831
- declare function classifyTerminalFailure(event: unknown): TerminalFailure | null;
832
- /** Open the raw turn stream for one specific model. */
833
- type OpenModelStream = (args: {
834
- model: string;
835
- /** 1 for the preferred model, 2 for the first fallback, and so on. */
836
- attempt: number;
837
- /** Aborted when this attempt times out, is abandoned, or finishes. Forward
838
- * this to `streamSandboxPrompt(..., { signal })` so a blocked transport is
839
- * cancelled immediately rather than only receiving iterator.return(). */
840
- signal: AbortSignal;
841
- }) => AsyncIterable<unknown> | Promise<AsyncIterable<unknown>>;
842
- /** Fired when a model is abandoned and the next one is about to be tried. */
843
- interface ModelFallbackInfo {
844
- from: string;
845
- to: string;
846
- reason: string;
847
- }
848
- /** Define inputs for streaming a turn across a model failover chain */
849
- interface ModelFailoverStreamOptions {
850
- /** Preferred model first, then fallbacks in descending preference. */
851
- models: readonly string[];
852
- open: OpenModelStream;
853
- /**
854
- * Maximum time to open/start one model's event source, including its first
855
- * iterator pull. Default 120 seconds, matching the sandbox provisioning
856
- * ceiling so cold infrastructure startup is bounded independently from
857
- * provider inference.
858
- */
859
- openTimeoutMs?: number;
860
- /**
861
- * Hard deadline from the source's first lifecycle event to its first
862
- * answer-bearing event. Default 60 seconds. Processing/lifecycle events do
863
- * not commit the attempt and do not extend this deadline.
864
- */
865
- firstResponseTimeoutMs?: number;
866
- /** Override the commit-point rule. Default {@link isCommittingSandboxEvent}. */
867
- isCommitting?: (event: unknown) => boolean;
868
- onFallback?: (info: ModelFallbackInfo) => void;
869
- /**
870
- * How many times to RE-RUN THE SAME MODEL when a turn completes having
871
- * produced no assistant text at all. Default `0` — byte-identical to no
872
- * retry.
873
- *
874
- * This is deliberately not a chain walk. Falling over to a different model
875
- * on an empty answer is the unattributable downgrade this module refuses to
876
- * do; re-running the SAME model changes nothing about attribution, because
877
- * the model that serves is the model that was asked for.
878
- *
879
- * Bounded by the same commit rule as failover: only a turn whose ONLY
880
- * committing event is a terminal receipt with no text is retried, so nothing
881
- * that reached the user can ever be produced twice.
882
- */
883
- emptyTurnRetries?: number;
884
- /** Fired when an empty turn is discarded and the same model re-run. */
885
- onEmptyTurnRetry?: (info: EmptyTurnRetryInfo) => void;
886
- log?: (message: string, meta?: Record<string, unknown>) => void;
887
- }
888
- /** One same-model re-run of a turn that completed with no assistant text. */
889
- interface EmptyTurnRetryInfo {
890
- model: string;
891
- /** 1 for the first re-run. */
892
- retry: number;
893
- /** How many re-runs remain after this one. */
894
- remaining: number;
895
- }
896
- /** The failover-wrapped stream plus the attribution every consumer needs. */
897
- interface ModelFailoverStreamHandle {
898
- events: AsyncGenerator<unknown, void, unknown>;
899
- /** The model that actually served. `undefined` until the first pull resolves it. */
900
- servingModel(): string | undefined;
901
- /** Every model tried, in order, with the reason each was abandoned. */
902
- attempts(): ModelFailoverAttempt[];
903
- /** True when the preferred model did not serve — the attributability signal. */
904
- usedFallback(): boolean;
905
- }
906
- /** Default ceiling for opening/starting one source through its first event. */
907
- declare const DEFAULT_MODEL_STREAM_OPEN_TIMEOUT_MS = 120000;
908
- /** Default ceiling for the first answer-bearing event after the source's first event. */
909
- declare const DEFAULT_MODEL_FIRST_RESPONSE_TIMEOUT_MS = 60000;
910
- /** Structured timeout codes surfaced by the producer on final exhaustion. */
911
- type ModelAttemptTimeoutCode = 'model_stream_open_timeout' | 'provider_first_response_timeout';
912
- /** Every configured model was exhausted and the final one timed out. */
913
- declare class ModelFailoverTimeoutError extends ModelFailoverExhaustedError {
914
- readonly code: ModelAttemptTimeoutCode;
915
- readonly model: string;
916
- readonly timeoutMs: number;
917
- constructor(input: {
918
- attempts: ModelFailoverAttempt[];
919
- code: ModelAttemptTimeoutCode;
920
- model: string;
921
- timeoutMs: number;
922
- });
923
- }
924
- /**
925
- * Hard ceiling on same-model re-runs. A turn that comes back blank three times
926
- * running is not a flake this can retry away, and each pass costs a full
927
- * sandbox turn — so the budget is capped rather than trusted.
928
- */
929
- declare const MAX_EMPTY_TURN_RETRIES = 3;
930
- /**
931
- * Coerce the caller's budget to a finite, bounded, non-negative integer.
932
- *
933
- * `Math.trunc(NaN)` is `NaN` and `retry >= NaN` is false for every `retry`, so
934
- * a naive clamp turns a bad config value into a loop that opens sandbox streams
935
- * until the worker dies. `Infinity` has the same shape. Both resolve to `0` —
936
- * an unusable budget disables the retry rather than running unbounded.
937
- */
938
- declare function resolveEmptyTurnRetries(value: number | undefined): number;
939
- /**
940
- * Wrap `open` in reactive model failover, streaming from the first model in
941
- * `models` that reaches its commit point.
942
- *
943
- * Zero added latency on the happy path: the preferred model is opened first and,
944
- * the moment it emits anything meaningful, its events flow straight through.
945
- *
946
- * @throws ModelFailoverTimeoutError when the final model times out,
947
- * ModelFailoverExhaustedError when every model's upstream is down, and
948
- * re-throws a non-outage error from the FIRST model without walking the
949
- * chain (the latter behaviors are inherited from
950
- * `runWithModelFailover`).
951
- */
952
- declare function streamWithModelFailover(options: ModelFailoverStreamOptions): ModelFailoverStreamHandle;
953
-
954
- /**
955
- * Sandbox lane: bridge a raw sandbox event stream (`streamSandboxPrompt`) into
956
- * the `ChatTurnProducer` shape agent-runtime's `handleChatTurn` consumes AND
957
- * the client vocabulary `/web-react`'s `dispatchChatStreamLine` already parses
958
- * (`text` / `reasoning` / `tool_call` / `tool_result` / `usage` /
959
- * `notice` / structured `error` / `interaction`). Legal and tax each hand-rolled
960
- * this mapping differently;
961
- * this is that middle, composed from `/stream`'s normalizers — no new loop
962
- * logic, no SDK import (the event source is an injected `AsyncIterable`).
963
- *
964
- * Alongside the live mapping it accumulates the PERSISTED projection — the
965
- * `message.parts` rows `/chat-store` stores — via `normalizePersistedPart` /
966
- * `mergePersistedPart` / `finalizeAssistantParts`, plus the usage receipt from
967
- * `step-finish` parts. `createChatTurnRoutes` reads both after drain.
968
- */
969
-
970
- /** Outcome of a `promoteFilePart` attempt. `key`, when given, becomes the
971
- * persisted part's row key (e.g. `attachment:<path>`) so repeat promotions
972
- * of the same underlying file fold into one segment instead of appending;
973
- * omitted, the default `getPartKey` keying applies.
974
- *
975
- * On failure, `part` is an OPTIONAL substitute part to persist in place of
976
- * the raw url-bearing one — this is how a product swaps in a transcript
977
- * notice (gtm persists a `warning` notice part, never the transient url) for
978
- * a failed promotion instead of baking a `data:`/sandbox-path url into the
979
- * durable row. When `part` is present it is persisted (via the same
980
- * `recordPersistedPart` path as a success, honoring the optional `key`);
981
- * when absent, the existing raw-part fallback applies unchanged — so a
982
- * caller that only returns `{ succeeded: false, reason }` keeps today's
983
- * behavior verbatim. */
984
- type FilePartPromotionOutcome = {
985
- succeeded: true;
986
- part: Record<string, unknown>;
987
- key?: string;
988
- } | {
989
- succeeded: false;
990
- reason: string;
991
- part?: Record<string, unknown>;
992
- key?: string;
993
- };
994
- /** Define options for producing sandbox chat events with rendering and interaction controls */
995
- interface SandboxChatProducerOptions {
996
- /** The raw sandbox event stream (e.g. `streamSandboxPrompt(...)`).
997
- *
998
- * An ALREADY-OPEN stream is bound to one model and cannot be reopened, so
999
- * this form can never fail over. Prefer {@link openEvents}; exactly one of
1000
- * the two is required. */
1001
- events?: AsyncIterable<unknown>;
1002
- /** Open the raw sandbox stream FOR A GIVEN MODEL — the failover-capable form
1003
- * of {@link events}. The callback receives the model to run and returns the
1004
- * same `AsyncIterable` `events` would have been (typically
1005
- * `streamSandboxPrompt(shell, box, prompt, { ...opts, model })`).
1006
- *
1007
- * Wiring this turns failover ON with no further flag: whenever the resolved
1008
- * chain (`model` + {@link fallbackModels}) holds more than one entry, a model
1009
- * whose upstream is dead is abandoned BEFORE its first client-visible byte
1010
- * and the next one is tried. A one-entry chain opens exactly once — no extra
1011
- * call, no added latency, byte-identical to today.
1012
- *
1013
- * Requires {@link model}: failover has to know which model it is running. */
1014
- openEvents?: OpenModelStream;
1015
- /** Recorded on the persisted assistant message. When failover moves the turn
1016
- * to another model, the producer's `model` reports the model that ACTUALLY
1017
- * served, not this preferred one — see {@link modelFailover}. */
1018
- model?: string;
1019
- /** Models to try, in order, when `model`'s upstream is dead. Product config,
1020
- * never a shell default: agent-app cannot know which ids are live, and a
1021
- * baked list would rot into exactly the stale-liveness bug this guards
1022
- * against. Empty/omitted → one attempt, today's behavior.
1023
- *
1024
- * Pick these deliberately. A same-family fallback is NOT automatically safe:
1025
- * `gemini-2.5-flash` produced zero persisted deliverables in 2 of 3 measured
1026
- * runs on a med-legal filing flow where `gemini-2.5-pro` succeeded. That is
1027
- * why every fallback is surfaced (persisted `model`, a transcript notice, and
1028
- * `modelFailover()`) instead of being applied silently. */
1029
- fallbackModels?: readonly string[];
1030
- /** Opt out of failover while still using {@link openEvents}. `false` collapses
1031
- * the chain to `model` alone. */
1032
- modelFailover?: false;
1033
- /** Maximum time to open/start one model event source through its first event.
1034
- * Default 120 seconds. This is separate from provider first-output latency
1035
- * so cold sandbox startup is not mislabeled as an inference failure. */
1036
- openTimeoutMs?: number;
1037
- /** Hard deadline after the source's first lifecycle event for the first
1038
- * answer-bearing event. Default 60 seconds. Processing/lifecycle events do
1039
- * not extend it. */
1040
- firstResponseTimeoutMs?: number;
1041
- /** Fired when a model is abandoned mid-chain (telemetry/alerting). The user
1042
- * already sees a transcript notice; this is for the operator. */
1043
- onModelFallback?: (info: ModelFallbackInfo) => void;
1044
- /** Re-run the SAME model this many times when a turn completes with no
1045
- * assistant text at all. Default `0` (unchanged behavior). Requires
1046
- * {@link openEvents} — there is nothing to re-open on a fixed stream.
1047
- *
1048
- * Distinct from {@link fallbackModels} on purpose: this never changes which
1049
- * model answers, so it carries none of the attribution risk a downgrade
1050
- * does. Measured on production 2026-07-27 through gtm-agent's profile, a
1051
- * completed-but-blank turn is a transient platform flake — 8 hard cases went
1052
- * 7/8 delivered to 8/8 with one re-run, costing 1 extra turn in 9. */
1053
- emptyTurnRetries?: number;
1054
- /** Fired when a blank turn is discarded and the same model re-run. */
1055
- onEmptyTurnRetry?: (info: EmptyTurnRetryInfo) => void;
1056
- /** Which ask kinds the product renders a card for. Anything else is
1057
- * auto-declined (see `declineInteraction`) so the run never hangs in the
1058
- * broker waiting on a card no client will show. Default: question/plan.
1059
- * Products with per-turn plan mode can close over it without another option:
1060
- * `(kind) => kind === 'question' || (kind === 'plan' && planEnabled)`. */
1061
- isRenderableInteraction?: (kind: string) => boolean;
1062
- /** Resolve a non-renderable ask (wire `respondToSessionInteraction` with the
1063
- * session's sidecar connection). Without it, a failure notice is emitted and
1064
- * the run stays blocked until the broker times out. */
1065
- declineInteraction?: (id: string) => Promise<void>;
1066
- /** Opt-in eager promotion of harness-emitted `file` parts. Unset, a `file`
1067
- * part persists exactly as the harness sent it — a transient `url` (a
1068
- * `data:` URI or in-sandbox path) baked into the transcript, which is
1069
- * today's behavior and stays byte-identical if this is never wired. Set,
1070
- * EVERY `file` part (never `image`, never any other kind) is routed
1071
- * through this callback instead of `recordPersistedPart`'s default
1072
- * fallback — including a part with NEITHER `id` NOR `url` (gtm always
1073
- * attempts promotion; such a part simply fails "carries no url" and
1074
- * resolves through the same failure path as any other rejection, rather
1075
- * than being persisted raw and unpromoted) — so the product can durably
1076
- * write the bytes and swap in a path-bearing part before the raw url ever
1077
- * reaches the persisted transcript. Keyed per source-prefixed `id:<id>` /
1078
- * `url:<url>` (an `id` and a `url` sharing the same text must never collide
1079
- * onto one memo entry) and memoized by PROMISE (not result), so re-emitted
1080
- * snapshot events for the same part —
1081
- * the harness resends the whole part on every update, not just deltas —
1082
- * fold onto the one in-flight or settled attempt rather than promoting
1083
- * twice or racing two concurrent writes; a raw part with neither `id` nor
1084
- * `url` cannot be keyed, so it is invoked UN-memoized (once per event) —
1085
- * each occurrence is its own attempt. A rejecting promise is caught,
1086
- * logged via `log`, and treated as `succeeded: false`. On `succeeded:
1087
- * false` the outcome's optional `part` (a substitute — e.g. a warning
1088
- * notice — see {@link FilePartPromotionOutcome}) persists in its place when
1089
- * given; otherwise the raw part persists exactly as it does today — this
1090
- * seam only decides whether to call the promoter and what to do with its
1091
- * outcomes; the promotion mechanics (vault write, key derivation, notice
1092
- * construction) live in the caller's callback, not here. */
1093
- promoteFilePart?: (raw: JsonRecord) => Promise<FilePartPromotionOutcome>;
1094
- log?: (message: string, meta?: Record<string, unknown>) => void;
1095
- }
1096
- /** Create a sandbox chat producer that manages chat turn routing with logging and interaction rendering options */
1097
- declare function createSandboxChatProducer(options: SandboxChatProducerOptions): ChatTurnRouteProducer;
1098
-
1099
- /**
1100
- * Detached (autonomous) turn → live buffer bridge.
1101
- *
1102
- * The interactive lane (`createChatTurnRoutes`) already streams a user-typed
1103
- * turn to the browser while it runs. An AUTONOMOUS turn — a mission step, a
1104
- * queue job, an inbound-email review — runs detached (`dispatchPrompt`/
1105
- * `streamPrompt` server-side so it survives no one watching) and, historically,
1106
- * only persisted its FINAL message. A browser opening the session mid-run saw a
1107
- * dead screen: the live tokens existed server-side but were never written to
1108
- * the turn-event buffer the client re-attach path (`listRunning` + `/replay`)
1109
- * reads.
1110
- *
1111
- * `runDetachedTurn` is that missing bridge, packaged. It taps the same buffer
1112
- * the interactive lane uses (`createBufferedTurnTap`) with the same producer
1113
- * mapping (`createSandboxChatProducer`), so an autonomous run is watchable
1114
- * token-by-token exactly like an interactive one — while staying durable
1115
- * (a durable driver re-invokes it after a crash; a turn that finished
1116
- * server-side short-circuits instead of re-streaming). Products supply only the
1117
- * domain seams: the raw sandbox event stream, the turn store, and the ids.
1118
- *
1119
- * This is app-shell mechanism (turn durability + live projection), not engine:
1120
- * it owns no loop logic and imports no SDK — the event source is an injected
1121
- * `AsyncIterable`.
1122
- */
1123
-
1124
- /** The normalized structured message body (tool-call / file / plan / interaction
1125
- * parts) that `/chat-store` persists as the durable assistant row — the same
1126
- * shape `createSandboxChatProducer().assistantParts()` returns. */
1127
- type DetachedTurnParts = Array<Record<string, unknown>>;
1128
- /** Authoritative final receipt for a turn that finished server-side, or whose
1129
- * live stream carried no usage (some harness paths only expose tokens via the
1130
- * completed-turn record, e.g. `box.findCompletedTurn(turnId)`). */
1131
- interface DetachedTurnFinal {
1132
- text?: string;
1133
- usage?: ChatTurnUsage;
1134
- /** The structured parts to persist when this receipt is more complete than
1135
- * the live stream (cached, finished server-side, or a fast stream that
1136
- * delivered scalar text before its message-part events). Omitted when the
1137
- * record only carries a usage receipt. */
1138
- parts?: DetachedTurnParts;
1139
- }
1140
- /** Define options for managing and projecting a detached turn event stream in a session */
1141
- interface DetachedTurnOptions {
1142
- store: TurnEventStore;
1143
- turnId: string;
1144
- /** Thread/session id — recorded as the buffer scope so a browser opening the
1145
- * session mid-run rediscovers this turn via `listRunning(scopeId)` after it
1146
- * has lost the turnId. */
1147
- scopeId: string;
1148
- /** The raw sandbox event stream for this turn (e.g. `streamSandboxPrompt`).
1149
- * Ownership of the box, prompt, tooling, and attachments stays with the
1150
- * caller — this only projects the stream.
1151
- *
1152
- * An already-open stream is bound to one model and cannot fail over. Prefer
1153
- * {@link openEvents}; exactly one of the two is required. */
1154
- events?: AsyncIterable<unknown>;
1155
- /** Open the raw sandbox stream FOR A GIVEN MODEL — the failover-capable form
1156
- * of {@link events}. Wiring it turns failover on with no further flag
1157
- * whenever {@link fallbackModels} is non-empty. Requires {@link model}.
1158
- *
1159
- * An autonomous run is the case that most needs this: nobody is watching to
1160
- * notice a dead upstream and retry by hand, so without failover the mission
1161
- * step or queue job simply fails. Forwarded to the producer. */
1162
- openEvents?: SandboxChatProducerOptions['openEvents'];
1163
- /** Models to try, in order, when `model`'s upstream is dead. Product config —
1164
- * see the producer's note on why a same-family fallback is not automatically
1165
- * safe and why every fallback is surfaced. */
1166
- fallbackModels?: SandboxChatProducerOptions['fallbackModels'];
1167
- /** Opt out of failover while still using {@link openEvents}. */
1168
- modelFailover?: false;
1169
- /** Maximum time to open/start one model event source through its first event.
1170
- * Forwarded to the producer; default 120 seconds. */
1171
- openTimeoutMs?: SandboxChatProducerOptions['openTimeoutMs'];
1172
- /** Hard deadline after the source's first lifecycle event for the first
1173
- * answer-bearing event. Forwarded to the producer; default 60 seconds. */
1174
- firstResponseTimeoutMs?: SandboxChatProducerOptions['firstResponseTimeoutMs'];
1175
- /** Fired when a model is abandoned mid-chain (telemetry/alerting). */
1176
- onModelFallback?: SandboxChatProducerOptions['onModelFallback'];
1177
- /** The PREFERRED model. Recorded on the persisted assistant message + usage
1178
- * receipt — unless failover moved the turn, in which case the model that
1179
- * actually served is recorded instead and surfaced on the result. */
1180
- model?: string;
1181
- /** Per-flush buffer coalescer. Default `coalesceDeltas`. */
1182
- coalesce?: (events: unknown[]) => unknown[];
1183
- /** Which ask kinds the product renders a card for; anything else is
1184
- * auto-declined via {@link declineInteraction}. Forwarded to the producer. */
1185
- isRenderableInteraction?: SandboxChatProducerOptions['isRenderableInteraction'];
1186
- /** Resolve a non-renderable ask so the run never hangs in the broker. An
1187
- * autonomous turn has NO human watching to answer an ask, so a caller that
1188
- * omits this risks the run blocking until the broker times out — wire it for
1189
- * any unattended run. Forwarded to the producer. */
1190
- declineInteraction?: SandboxChatProducerOptions['declineInteraction'];
1191
- /** Opt-in eager promotion of harness-emitted `file` parts. Forwarded to the
1192
- * producer (see its docs). */
1193
- promoteFilePart?: SandboxChatProducerOptions['promoteFilePart'];
1194
- /** Authoritative final receipt, consulted whenever a re-invoke finds a prior
1195
- * buffer: (a) an already-`complete` turn returns it as the cached result,
1196
- * (b) a `running` turn (crash mid-run) uses it to detect a run that finished
1197
- * server-side, and (c) a clean run whose stream carried no usage or only
1198
- * scalar text falls back to it. For Sandbox runs, use
1199
- * `readCompletedSandboxTurn` so the exact completed session message
1200
- * restores tool/file parts as well as text. */
1201
- completedResult?: () => Promise<DetachedTurnFinal | null | undefined>;
1202
- /** Clear the prior partial buffer for `turnId` before a genuine re-stream.
1203
- * A crash mid-run leaves buffered rows at seqs 1..N with status `running`;
1204
- * re-streaming restarts the tap's seq at 0 and would duplicate/interleave
1205
- * rows. Wire this (delete `turnId`'s buffered events) so a retry is clean.
1206
- * Unset, a re-stream over a `running` buffer is still attempted but logged
1207
- * as a possible-duplication hazard. */
1208
- resetBuffer?: (turnId: string) => Promise<void>;
1209
- /** Own the durable assistant row for this turn instead of returning the body
1210
- * for the caller to insert — and keep it in step with the stream.
1211
- *
1212
- * An autonomous run is exactly the case a late viewer hits: nobody is
1213
- * watching when it starts, so by the time a browser opens the session the
1214
- * streaming gateway's hot event buffer may already have expired it. Keeping
1215
- * that buffer short is what makes it affordable at scale (its Redis
1216
- * footprint is linear in `ttl x concurrent sessions`), so the durable row —
1217
- * written incrementally here — is what serves the late viewer.
1218
- *
1219
- * WIRING THIS TRANSFERS ROW OWNERSHIP: the returned {@link
1220
- * DetachedTurnResult.messageId} names the row this call wrote (draft rows
1221
- * during the stream, authoritative values at the end, retraction when the
1222
- * turn produced nothing). The caller must NOT insert its own assistant row
1223
- * for the turn. Omit the seam and nothing changes — the result is returned
1224
- * and the caller persists it exactly as today.
1225
- *
1226
- * Idempotency reuses the turn's own identity: the row id defaults to
1227
- * `assistant:<turnId>`, so a durable driver re-invoking after a crash
1228
- * patches the same row instead of duplicating parts. */
1229
- persist?: DraftPersistenceTuning & {
1230
- store: AssistantDraftStore;
1231
- threadId: string;
1232
- /** Deterministic row id. Default `assistant:<turnId>`. */
1233
- messageId?: string;
1234
- /** Pre-persist text transform (`/redact`), applied to drafts AND the final
1235
- * write — parity with the interactive lane's `transformFinalText`. */
1236
- transformText?: (text: string) => string | Promise<string>;
1237
- };
1238
- log?: (message: string, meta?: Record<string, unknown>) => void;
1239
- }
1240
- /** Describe the result of a detached turn including state, text, parts, usage, and optional error or cache flag */
1241
- interface DetachedTurnResult {
1242
- /** `completed` — clean drain: persist + bill. `failed` — a terminal error
1243
- * event, including the producer's structured `sandbox.stream_failed` event
1244
- * when the raw sandbox stream throws: skip billing, render an error row. */
1245
- state: 'completed' | 'failed';
1246
- text: string;
1247
- /** The structured assistant body to persist (tool calls, file/plan/interaction
1248
- * parts). Empty array when the run produced none. */
1249
- parts: DetachedTurnParts;
1250
- usage: ChatTurnUsage;
1251
- /** Present when `state === 'failed'`. */
1252
- error?: string;
1253
- /** True when a prior buffer meant this call returned a cached/finished result
1254
- * WITHOUT re-streaming (durable-driver retry after a crash). */
1255
- cached: boolean;
1256
- /** The durable assistant row this call wrote, when `persist` was wired.
1257
- * `null` when the turn produced nothing and the row was retracted. Absent
1258
- * when the caller owns persistence (today's behavior). */
1259
- messageId?: string | null;
1260
- /** The model that SERVED the turn — the fallback's id when failover moved it.
1261
- * A caller that bills or scores per model must read this, not the model it
1262
- * requested. */
1263
- model?: string;
1264
- /** The caller's explicit model request, before shell failover. */
1265
- requestedModel?: string;
1266
- /** The effective model echoed by the downstream sandbox. */
1267
- servedModel?: string;
1268
- /** The effective provider echoed by the downstream sandbox. */
1269
- servedProvider?: string;
1270
- /** How the downstream sandbox selected the effective model. */
1271
- servedSource?: 'request' | 'environment' | 'profile';
1272
- /** True when the preferred model did not serve. Makes an autonomous
1273
- * downgrade — which no human watched happen — attributable after the fact. */
1274
- usedModelFallback?: boolean;
1275
- /** Every model tried, in order, with the reason each was abandoned. */
1276
- modelAttempts?: ModelFailoverAttempt[];
1277
- }
1278
- /**
1279
- * Stream a detached turn into the live turn-event buffer, durably.
1280
- *
1281
- * - Idempotent: an already-`complete` turn returns the cached result without
1282
- * re-streaming (a second event sequence would collide with the buffered one).
1283
- * - Crash-safe: a `running` turn (a prior attempt crashed mid-tap) consults
1284
- * `completedResult` to detect a run that finished server-side; only a run that
1285
- * genuinely did not complete is re-streamed, and then over a `resetBuffer`-
1286
- * cleared buffer so seqs don't corrupt.
1287
- * - Marks the turn `running` under `scopeId` so a mid-run browser finds it.
1288
- * - Settles `complete`/`error` so the client stops tailing and billing/render
1289
- * can branch on `state`.
1290
- */
1291
- declare function runDetachedTurn(opts: DetachedTurnOptions): Promise<DetachedTurnResult>;
1292
-
1293
- /**
1294
- * Recover a detached Sandbox turn from its durable completed records.
1295
- *
1296
- * A fast detached run can finish before the live event subscriber receives
1297
- * every message-part event. The Sandbox still retains two exact records: a
1298
- * turn-id keyed result cache and the completed assistant message on the
1299
- * session. This adapter joins them without guessing across turns, then returns
1300
- * the same `DetachedTurnFinal` shape `runDetachedTurn` already consumes.
1301
- */
1302
-
1303
- /** The official Sandbox methods needed for completed-turn recovery. */
1304
- type CompletedSandboxTurnSource = Pick<SandboxInstance, 'findCompletedTurn' | 'session'>;
1305
- /** Options for resolving one exact detached turn. */
1306
- interface ReadCompletedSandboxTurnOptions {
1307
- turnId: string;
1308
- sessionId: string;
1309
- log?: (message: string, meta?: Record<string, unknown>) => void;
1310
- }
1311
- /**
1312
- * Read the exact completed turn. The session-level result is consulted only
1313
- * when the matching completed assistant message is still the latest message;
1314
- * otherwise that result could belong to a newer turn on the same session.
1315
- */
1316
- declare function readCompletedSandboxTurn(box: CompletedSandboxTurnSource, options: ReadCompletedSandboxTurnOptions): Promise<DetachedTurnFinal | null>;
1317
-
1318
- /** Resolve chat route events and materialize their durable state records */
1319
- interface ChatRouteDurableProjection {
1320
- observe(event: unknown): void | Promise<void>;
1321
- materialize(): Array<Record<string, unknown>> | Promise<Array<Record<string, unknown>>>;
1322
- }
1323
- /** Log chat route projection messages with optional metadata for durable processing */
1324
- type ChatRouteDurableProjectionLogger = (message: string, meta?: Record<string, unknown>) => void;
1325
- /** Adds durable lifecycle projection to any producer lane without moving its
1326
- * transport into agent-app. Purely STRUCTURAL: agent-app ships no implementer
1327
- * of {@link ChatRouteDurableProjection} and deliberately does not — the one it
1328
- * used to ship (`/durable-chat`) was removed in 0.44.0 with zero fleet imports.
1329
- * Pass any `{ observe, materialize }` object backed by your own store. The projection is observed inline and its
1330
- * materialized parts replace same-key pending snapshots after the stream
1331
- * drains. Projection persistence is best-effort for the live lane: a store
1332
- * outage must not terminate an otherwise healthy sandbox stream. Failures are
1333
- * reported through the optional logger so products can retain diagnostics. */
1334
- declare function withDurableChatProjection(producer: ChatTurnRouteProducer, projection: ChatRouteDurableProjection, log?: ChatRouteDurableProjectionLogger): ChatTurnRouteProducer;
1335
-
1336
- /**
1337
- * `createUploadRoute` — the multimodal middle. Accepts multipart file uploads
1338
- * and returns `PromptInputPart`-shaped descriptors the client echoes back on
1339
- * send (`ChatTurnRequestPayload.parts`):
1340
- *
1341
- * ≤ inlineMaxBytes (700 KiB default) → inline `data:` URI part — rides the
1342
- * turn body directly, no sandbox round trip.
1343
- * > inlineMaxBytes → written into the sandbox workspace (base64 through the
1344
- * structural `write` seam — `box.fs` satisfies it) and referenced by
1345
- * `path`. Mandatory two-step: the gateway caps request bodies at ~1 MiB,
1346
- * so a large file can never ride the prompt POST.
1347
- *
1348
- * The sink is structural (no sandbox-SDK import); products pass `box.fs`.
1349
- *
1350
- * @remarks Sole consumer today is the `--chat` scaffold (`create-agent-app
1351
- * --chat` → `template-chat/src/chat.ts`), the reference multimodal path — its
1352
- * inline-`data:`-or-ephemeral-sandbox-workspace split stays the scaffold's
1353
- * proven upload pattern, not a fleet primitive; keep that distinction in mind
1354
- * before widening its surface. Fleet apps with a durable store of their own
1355
- * (KV, or AES-GCM-encrypted R2) no longer need to hand-roll a vault upload
1356
- * route: `createAttachmentUploadRoute` (`./attachment-upload`, agent-app#234)
1357
- * is the shared hardened path for that persistence model — a content-sniffed
1358
- * type gate, two-phase atomic batch writes, and per-kind/aggregate size caps,
1359
- * all seamed through an injected `WriteAttachmentFn`. Point readers there
1360
- * instead of widening this route to cover both models.
1361
- */
1362
-
1363
- /** 700 KiB: base64 inflates ~4/3, so an inline part stays comfortably under
1364
- * the ~1 MiB gateway body cap alongside the JSON envelope. */
1365
- declare const UPLOAD_INLINE_MAX_BYTES: number;
1366
- /** 8 MiB default ceiling per file — one base64 `write` call handles it. Raise
1367
- * it only with a sink that can take the bigger single write. */
1368
- declare const UPLOAD_MAX_FILE_BYTES: number;
1369
- /** Structural match of the sandbox SDK's `box.fs` write surface (v0.10.5+:
1370
- * `encoding: 'base64'` is the worker-safe binary path). */
1371
- interface SandboxUploadSink {
1372
- write(path: string, content: string, options?: {
1373
- encoding?: 'utf8' | 'base64';
1374
- }): Promise<unknown>;
1375
- }
1376
- /** Resolve upload authorization status and provide upload destination or error response */
1377
- type UploadAuthorization = {
1378
- ok: true;
1379
- /** Where large files land. Absent/null: only inline uploads are
1380
- * accepted and an over-inline-cap file is rejected with 413. */
1381
- sink?: SandboxUploadSink | null;
1382
- /** Per-request override of the workspace directory large files go to. */
1383
- uploadDir?: string;
1384
- } | {
1385
- ok: false;
1386
- response: Response;
1387
- };
1388
- /** Define options to authorize uploads and configure file size limits and upload directory */
1389
- interface CreateUploadRouteOptions {
1390
- /** Authenticate the caller and resolve the sandbox file sink (usually
1391
- * `ensureWorkspaceSandbox(...)` → `box.fs`). */
1392
- authorize(args: {
1393
- request: Request;
1394
- }): Promise<UploadAuthorization>;
1395
- /** Inline-vs-sandbox threshold. Default {@link UPLOAD_INLINE_MAX_BYTES}. */
1396
- inlineMaxBytes?: number;
1397
- /** Hard per-file cap. Default {@link UPLOAD_MAX_FILE_BYTES}. */
1398
- maxFileBytes?: number;
1399
- /** Absolute workspace directory for path-ref files.
1400
- * Default `'/workspace/uploads'`. */
1401
- uploadDir?: string;
1402
- }
1403
- /** One uploaded file, ready for the composer chip and the turn body. */
1404
- interface UploadedChatFile {
1405
- id: string;
1406
- name: string;
1407
- size: number;
1408
- mediaType: string;
1409
- /** True when the part carries the bytes inline (`data:` URI). */
1410
- inline: boolean;
1411
- /** Echo this back verbatim in `ChatTurnRequestPayload.parts`. */
1412
- part: ChatTurnFilePartInput;
1413
- }
1414
- /** Path-safe file name: basename only, conservative charset, length-capped. */
1415
- declare function sanitizeUploadFilename(name: string): string;
1416
- /** Convert a Uint8Array of bytes into a base64-encoded string */
1417
- declare function bytesToBase64(bytes: Uint8Array): string;
1418
- /** Create an upload route handler that authorizes requests and processes file uploads with size limits */
1419
- declare function createUploadRoute(options: CreateUploadRouteOptions): (request: Request) => Promise<Response>;
1420
-
1421
- /**
1422
- * Storage seams for the chat-attachment vertical (`resolveChatAttachments`,
1423
- * `buildDispatchParts`, `promoteAgentFilePart`). Structural function ports in
1424
- * the same style as `upload.ts`'s `SandboxUploadSink`: REQUIRED injection, no
1425
- * default implementation — agent-app owns the size/budget/idempotency
1426
- * mechanism, the product owns where the bytes actually live.
1427
- *
1428
- * One reader, two callers: `resolveChatAttachments` reads only the
1429
- * authoritative `size` (never trusting the client-reported size), while
1430
- * `buildDispatchParts` reads the inline `content` (base64 or raw bytes) to
1431
- * build a `data:` URI. Both fit behind ONE `ReadAttachmentFn` so a product
1432
- * wires a single vault/object-store adapter, not two. gtm's KV vault (stores
1433
- * base64 bodies) and `/object-store`'s `ObjectStore` (hands back raw bytes)
1434
- * both satisfy this shape.
1435
- */
1436
- /**
1437
- * The result of reading one stored attachment. `ok:true` MUST carry the
1438
- * authoritative decoded byte `size` (the cap is meaningless against a
1439
- * client-controlled number) and, when the caller needs to inline the file,
1440
- * its content as `base64` and/or raw `bytes`. `mediaType` is the stored
1441
- * content type when the store knows it (used as the fallback when the wire
1442
- * part carried none). `ok:false` carries a human `reason` that SHOULD name the
1443
- * offending path — it is surfaced verbatim in the caller's typed outcome.
1444
- */
1445
- type AttachmentReadResult = {
1446
- ok: true;
1447
- /** Authoritative decoded byte length of the stored content. */
1448
- size: number;
1449
- /** Inline content as base64 — reused verbatim for a `data:` URI, so a
1450
- * store holding already-base64 bodies never decodes-and-re-encodes. */
1451
- base64?: string;
1452
- /** Inline content as raw bytes — base64-encoded by the caller when a
1453
- * `data:` URI is needed. Ignored when `base64` is present. */
1454
- bytes?: Uint8Array;
1455
- /** Stored content type, when the store tracks one. */
1456
- mediaType?: string;
1457
- } | {
1458
- ok: false;
1459
- reason: string;
1460
- };
1461
- /**
1462
- * Read one stored attachment for `scopeId` (the product's workspace/tenant
1463
- * key) at its store-relative `path`. Missing, deleted, or unreadable content
1464
- * MUST resolve to `{ ok: false, reason }`, never throw — a store failure is a
1465
- * per-attachment outcome the caller folds into its own typed result, not a
1466
- * turn-level exception.
1467
- */
1468
- type ReadAttachmentFn = (scopeId: string, path: string) => Promise<AttachmentReadResult>;
1469
- /** Outcome of persisting one attachment. Mirrors `AttachmentReadResult`'s
1470
- * `ok`/`reason` shape and `upload.ts`'s `{ ok }` convention. */
1471
- type AttachmentWriteResult = {
1472
- ok: true;
1473
- } | {
1474
- ok: false;
1475
- reason: string;
1476
- };
1477
- /**
1478
- * Persist `content` for `scopeId` at `path`. `content` is either raw `bytes`
1479
- * or a base64 `string` — a string argument is ALWAYS base64 (never utf8), so
1480
- * a store that speaks base64 (gtm's vault) writes it verbatim and one that
1481
- * speaks bytes decodes once. Like the reader, failures resolve to
1482
- * `{ ok: false, reason }` rather than throwing.
1483
- *
1484
- * `opts` mirrors the vault frontmatter gtm's `writeAttachmentVaultFile`
1485
- * persists alongside the body (promote-file-parts.ts:181-190), so a product
1486
- * reimplementing that vault writer through this seam can reproduce it
1487
- * exactly:
1488
- * - `mediaType` — the resolved MIME type; gtm's frontmatter key `mime`.
1489
- * - `name` — the sanitized (store-path-safe) display filename; gtm passes
1490
- * this only to shape its oversize message, not into frontmatter.
1491
- * - `originalName` — the filename as the harness/browser reported it, BEFORE
1492
- * sanitization (`raw.filename ?? filename` — falls back to the sanitized
1493
- * name when the source carried none); gtm's frontmatter key `originalName`.
1494
- * This is the one field with no other recovery path once sanitization has
1495
- * run, so it must ride the write, not be re-derived after the fact.
1496
- * - `size` — the authoritative decoded byte length being written; gtm's
1497
- * frontmatter key `size`.
1498
- */
1499
- type WriteAttachmentFn = (scopeId: string, path: string, content: Uint8Array | string, opts: {
1500
- mediaType?: string;
1501
- name?: string;
1502
- originalName?: string;
1503
- size?: number;
1504
- }) => Promise<AttachmentWriteResult>;
1505
-
1506
- /**
1507
- * `resolveChatAttachments` — validate a turn body's `attachments` field into
1508
- * persistable {@link ChatAttachmentPart}s. Every path is re-validated (a path
1509
- * off the wire is never trusted to stay inside the store root) and every size
1510
- * is re-derived from the STORED body via the injected {@link ReadAttachmentFn},
1511
- * never the client-reported `size` — the upload path lets a caller rewrite its
1512
- * own frontmatter, so a stored size cannot bound anything and the wire size can
1513
- * be anything. Both the aggregate cap and the size carried on the returned part
1514
- * come from the authoritative read.
1515
- *
1516
- * Storage-parameterized: the frontmatter parsing / base64 sizing that derives
1517
- * the authoritative size lives BEHIND `readAttachment` (a product's vault or
1518
- * object-store adapter), so this module is a pure validator + budget gate with
1519
- * no store knowledge. Lifted from gtm-agent's `resolve-attachments.ts`
1520
- * (workspaceId → scopeId, the vault read → the injected reader) and kept
1521
- * behavior-identical for gtm-agent#618 adoption.
1522
- */
1523
-
1524
- /** Resolve the result of chat attachment processing with success status and corresponding data or error */
1525
- type ResolveChatAttachmentsResult = {
1526
- succeeded: true;
1527
- value: ChatAttachmentPart[];
1528
- } | {
1529
- succeeded: false;
1530
- error: string;
1531
- };
1532
- /** Verdict of a path check: OK, or a rejection naming why. Mirrors
1533
- * `SandboxMentionPathCheck` in `./wire`. */
1534
- type AttachmentPathCheck = {
1535
- succeeded: true;
1536
- } | {
1537
- succeeded: false;
1538
- error: string;
1539
- };
1540
- /**
1541
- * Default path validator when a caller supplies none. Rejects the ways a path
1542
- * picked in a client can escape the store root — traversal (`..` segment),
1543
- * absolute (leading `/`), backslashes, null bytes, control characters (see
1544
- * {@link CONTROL_CHARS} — a path also feeds {@link buildAttachmentPromptBlock}'s
1545
- * `(vault: ${path})` pointer, so it is exposed to the same injection surface as
1546
- * `name`) — plus a dotfile/hidden segment (a leading `.` on any segment).
1547
- * Generalized from gtm's `validateVaultFilePath`, in the spirit of
1548
- * `validateSandboxMentionPath` (`/chat-routes`'s wire mention-path validator) —
1549
- * but the dotfile rejection here is INTENTIONALLY stricter than that sibling:
1550
- * an uploaded attachment path is sanitized store-relative storage the product
1551
- * itself assigned, whereas a mention path points at a file that already exists
1552
- * in the sandbox and may legitimately live under a dotfile segment. A caller
1553
- * that needs gtm's exact (looser) rule can supply `validatePath` to override
1554
- * this default entirely.
1555
- */
1556
- declare function defaultValidateAttachmentPath(path: string): AttachmentPathCheck;
1557
- /** Define options to resolve and validate chat attachments with size, count, and path constraints */
1558
- interface ResolveChatAttachmentsOptions {
1559
- /** The product's workspace/tenant key, passed to `readAttachment`. */
1560
- scopeId: string;
1561
- /** Authoritative size + content reader — see {@link ReadAttachmentFn}. */
1562
- readAttachment: ReadAttachmentFn;
1563
- /** Most attachments one request may carry. Default {@link ATTACHMENT_MAX_COUNT}. */
1564
- maxCount?: number;
1565
- /** Aggregate raw-byte ceiling. Default {@link MAX_ATTACHMENT_TOTAL_BYTES}. */
1566
- maxTotalBytes?: number;
1567
- /** Path validator override. Default {@link defaultValidateAttachmentPath}. */
1568
- validatePath?: (path: string) => AttachmentPathCheck;
1569
- }
1570
- /**
1571
- * Validate and resolve a turn body's `attachments` field into persistable
1572
- * parts. Every path is confirmed present (and not deleted) in the caller's own
1573
- * store by `readAttachment` before it is trusted, and size is derived from the
1574
- * authoritative read for both the aggregate cap and the returned part's size.
1575
- */
1576
- declare function resolveChatAttachments(value: unknown, options: ResolveChatAttachmentsOptions): Promise<ResolveChatAttachmentsResult>;
1577
-
1578
- /**
1579
- * `createAttachmentUploadRoute` — the fleet-primitive durable-store upload
1580
- * route: a two-phase atomic batch (every file is validated before any file is
1581
- * written — a batch never partially lands), a content-sniffed type gate
1582
- * (`checkAttachmentType` over `sniffBinary`'s magic-byte read, not the
1583
- * extension or the browser-reported MIME), per-kind + aggregate byte caps,
1584
- * and sanitized filenames. Storage is fully seamed through the injected
1585
- * `WriteAttachmentFn` (`./attachment-store`) — no default store, the product
1586
- * owns where bytes actually live (vault, object store, …) — and auth/rate
1587
- * limiting is entirely the injected `authorize` seam's job: this factory
1588
- * never invents a 401 or 429 response, it only returns `auth.response`
1589
- * verbatim on failure.
1590
- *
1591
- * Lifted from gtm-agent's `src/routes/api.vault.upload.ts` (the hardening
1592
- * lineage other lifted modules in this vertical cite: gtm#584 binary
1593
- * corruption, gtm#592 sniff gate/caps, gtm#593 batch-atomic writes) and
1594
- * generalized the way `resolve-attachments.ts` generalized gtm's read path —
1595
- * the vault-specific pieces (KV vault paths, frontmatter, per-user rate
1596
- * limiting) are all injected seams here, while the validate-then-write phase
1597
- * split and the type/size gate ordering survive byte-for-byte.
1598
- *
1599
- * @remarks Sibling to, NOT an extension of, `./upload.ts`'s
1600
- * `createUploadRoute` — a different persistence model (durable product store
1601
- * vs. inline-`data:`-or-ephemeral-sandbox-workspace). See that module's doc
1602
- * comment for the up-to-date framing between the two.
1603
- */
1604
-
1605
- /** Outcome of the injected `authorize` seam: auth + rate limiting +
1606
- * scope resolution, all in one place so a 429 rides `{ok:false, response}`
1607
- * exactly like a 401 does — this factory has no rate-limit opinion of its
1608
- * own. `writeAttachment` lets a single request override the option-level
1609
- * store (e.g. routing per-tenant), defaulting to `options.writeAttachment`
1610
- * when absent. */
1611
- type AttachmentUploadAuthorization = {
1612
- ok: true;
1613
- scopeId: string;
1614
- writeAttachment?: WriteAttachmentFn;
1615
- } | {
1616
- ok: false;
1617
- response: Response;
1618
- };
1619
- /** Define options to authorize, write, and limit attachment uploads in a route */
1620
- interface CreateAttachmentUploadRouteOptions {
1621
- /** Authenticate the caller, rate-limit, and resolve the store scope
1622
- * (workspace/tenant id) — never a query param. */
1623
- authorize(args: {
1624
- request: Request;
1625
- }): Promise<AttachmentUploadAuthorization>;
1626
- /** Default store writer. `authorize` may override it per-request. */
1627
- writeAttachment: WriteAttachmentFn;
1628
- /** Overridable caps. Defaults come from `./attachment-validation`. */
1629
- limits?: {
1630
- /** Most files one request may carry. Default {@link ATTACHMENT_MAX_COUNT}. */
1631
- maxCount?: number;
1632
- /** Ceiling on a binary file's raw size. Default {@link MAX_BINARY_ATTACHMENT_BYTES}. */
1633
- maxBinaryBytes?: number;
1634
- /** Ceiling on a text file's raw size. Default {@link MAX_TEXT_ATTACHMENT_BYTES}. */
1635
- maxTextBytes?: number;
1636
- /** Aggregate raw-byte ceiling across the batch. Default {@link MAX_ATTACHMENT_TOTAL_BYTES}. */
1637
- maxTotalBytes?: number;
1638
- };
1639
- /** Attachment kinds this route accepts. Default `['image', 'file']`. */
1640
- allowedKinds?: ChatAttachmentKind[];
1641
- /** Sniffed-mime allowlist fed to `checkAttachmentType`. Default
1642
- * {@link ALLOWED_ATTACHMENT_SNIFFED_MIMES}. Narrow it to accept less than
1643
- * the default (`new Set(['application/pdf'])`), or widen it to accept a
1644
- * format the default refuses — macro-enabled Office packages are the
1645
- * shipped case:
1646
- * `new Set([...ALLOWED_ATTACHMENT_SNIFFED_MIMES, ...MACRO_ENABLED_OOXML_SNIFFED_MIMES])`. */
1647
- allowedSniffedMimes?: ReadonlySet<string>;
1648
- /** Sanitized-name → store path. Default identity (the sanitized name IS
1649
- * the path); gtm passes `vaultFolderForFileName`, a tenant product a
1650
- * scope prefix. */
1651
- pathFor?: (name: string) => string;
1652
- /** Store-path validator. Default {@link defaultValidateAttachmentPath}. */
1653
- validatePath?: (path: string) => AttachmentPathCheck;
1654
- /** Last-resort media-type hook for text content the sniffer can't type.
1655
- * Default {@link sniffMimeFromName}. */
1656
- sniffMime?: (name: string) => string;
1657
- }
1658
- /** Resolve an attachment upload route handler with customizable limits and validation options */
1659
- declare function createAttachmentUploadRoute(options: CreateAttachmentUploadRouteOptions): (request: Request) => Promise<Response>;
1660
-
1661
- /**
1662
- * `buildDispatchParts` — assemble the `PromptInputPart[]` a turn carrying
1663
- * attachments and/or `@`-mentions dispatches to the sandbox. `parts[0]` is
1664
- * always the full prompt text (typed text plus the attachment + mention pointer
1665
- * blocks); each attachment or mention becomes one media part. An attachment
1666
- * (read from the product store via the injected reader) draws the inline byte
1667
- * budget first; a mention (read from the LIVE box) takes what is left. A file
1668
- * inlines as a `data:` URI when it fits the remaining budget, otherwise uses
1669
- * the in-box file reference supported by the current sandbox prompt contract.
1670
- * Every media part is deduped by its resolved absolute path. This module only
1671
- * produces the parts array; the caller decides when a turn dispatches parts
1672
- * instead of a plain string.
1673
- *
1674
- * Storage-parameterized port of gtm-agent's `dispatch-parts.ts`: the vault
1675
- * default reader is dropped (`readAttachment` is REQUIRED — the product supplies
1676
- * its store adapter), the `GTM_SANDBOX_VAULT_DIR` prefixing becomes the required
1677
- * `resolveAttachmentPath` seam, the `GTM_MULTIMODAL_FORCE_PATH` env fallback
1678
- * becomes an explicit `forcePath` flag, and every budget cap reads an overridable
1679
- * `./wire` constant. Kept behavior-identical for gtm-agent#618 adoption (the
1680
- * demotion math and emitted part shapes reproduce its dispatched prompt bytes).
1681
- */
1682
-
1683
- /** Resolve the outcome of dispatching parts with success status and corresponding value or error message */
1684
- type DispatchPartsOutcome = {
1685
- succeeded: true;
1686
- value: PromptInputPart[];
1687
- } | {
1688
- succeeded: false;
1689
- error: string;
1690
- };
1691
- /** One mention file's size (always) and inline bytes (only when the caller
1692
- * asked for them — a path-only mention never reads its bytes). */
1693
- type SandboxMentionReadOutcome = {
1694
- succeeded: true;
1695
- value: {
1696
- size: number;
1697
- base64?: string;
1698
- };
1699
- } | {
1700
- succeeded: false;
1701
- error: string;
1702
- };
1703
- /** Resolve sandbox mention details by reading from a specified path with optional byte reading */
1704
- type ReadSandboxMentionFn = (box: SandboxExecChannel, absolutePath: string, options: {
1705
- readBytes: boolean;
1706
- }) => Promise<SandboxMentionReadOutcome>;
1707
- /**
1708
- * Convert the browser-safe chat attachment contract into the current sandbox
1709
- * prompt contract. Images carry exactly one URL or path; generic files carry a
1710
- * filename and URL.
1711
- */
1712
- declare function normalizeChatPromptForSandbox(prompt: string | readonly ChatTurnPartInput[]): string | PromptInputPart[];
1713
- /** Build input parameters for dispatching chat message parts including text, attachments, mentions, and history */
1714
- interface BuildDispatchPartsInput {
1715
- text: string;
1716
- attachments: ChatAttachmentPart[];
1717
- mentions?: ChatMentionPart[];
1718
- history: Array<{
1719
- role: 'user' | 'assistant';
1720
- content: string;
1721
- }>;
1722
- systemPrompt: string;
1723
- /** Serialized size of the backend profile the SDK inlines into the same
1724
- * prompt request body — a large, non-negotiable rider that must come out of
1725
- * the inline budget or near-cap attachments 413 at the proxy instead of
1726
- * demoting to path parts. */
1727
- profileWireBytes: number;
1728
- /** The product's workspace/tenant key, passed to `readAttachment`. */
1729
- scopeId: string;
1730
- /** Maps an attachment's store-relative path to the in-box absolute path a
1731
- * local media reference uses (same style as `fileMentionsToParts`'s
1732
- * `resolvePath`). */
1733
- resolveAttachmentPath: (path: string) => string;
1734
- /** Maps a mention's workspace-relative path to its in-box absolute path.
1735
- * Default: {@link BuildDispatchPartsInput.resolveAttachmentPath} — in gtm the
1736
- * vault mount roots both; a product that mounts them apart overrides this. */
1737
- resolveMentionPath?: (path: string) => string;
1738
- /** The turn's already-ensured box — required when `mentions` is non-empty
1739
- * (mention bytes are read from the live box, not the store). */
1740
- box?: SandboxExecChannel;
1741
- /** Force every media part to a local file reference, skipping all inlining. */
1742
- forcePath?: boolean;
1743
- /** REQUIRED store reader for attachment content — no default (the product
1744
- * owns its store; see {@link ReadAttachmentFn}). */
1745
- readAttachment: ReadAttachmentFn;
1746
- readSandboxMention?: ReadSandboxMentionFn;
1747
- /** Whole-request proxy cap. Default {@link DISPATCH_REQUEST_MAX_BYTES}. */
1748
- requestMaxBytes?: number;
1749
- /** JSON-envelope reserve off the top of the request cap. Default
1750
- * {@link DISPATCH_STRUCTURAL_RESERVE_BYTES}. */
1751
- structuralReserveBytes?: number;
1752
- /** Sidecar per-request parts-array cap. Default {@link DISPATCH_MAX_PARTS}. */
1753
- maxParts?: number;
1754
- }
1755
- /** Build dispatch parts from input by resolving mentions, paths, and applying size constraints asynchronously */
1756
- declare function buildDispatchParts(input: BuildDispatchPartsInput): Promise<DispatchPartsOutcome>;
1757
-
1758
- /**
1759
- * `promoteAgentFilePart` — turn a harness-emitted `type:"file"` stream part
1760
- * into a store-backed {@link ChatAttachmentPart}. The harness hands back a URL
1761
- * pointing at bytes it produced (a `data:` URI, or a path inside the sandbox);
1762
- * nothing durable survives past the turn unless it is written into the
1763
- * product's store, the same way a user upload is. Typed outcomes throughout:
1764
- * every failure mode (unsupported scheme, no sandbox, oversize, store-write
1765
- * failure, malformed part) resolves to `{ succeeded: false, filename, reason }`
1766
- * rather than throwing past this boundary, so the caller folds a visible notice
1767
- * instead of losing the file silently.
1768
- *
1769
- * Storage-parameterized port of gtm-agent's `promote-file-parts.ts` with the
1770
- * refactor gtm never made: persistence goes through the injected
1771
- * {@link WriteAttachmentFn} (gtm hard-wired its vault writer), the path strategy
1772
- * is the injected `buildAttachmentPath` (neutral `uploads/agent/<date>/` default,
1773
- * no domain bucket taxonomy baked), the MIME map is an injectable hook, and the
1774
- * date segment reads an injectable clock. The idempotent `hash8(id ?? url ??
1775
- * filename)` naming is preserved so re-promoting the same source part resolves
1776
- * to the same path.
1777
- */
1778
-
1779
- /** Default ceiling on a promoted file's raw (pre-encoding) byte size. */
1780
- declare const PROMOTE_MAX_FILE_BYTES: number;
1781
- /** Define the structure for a raw file part with optional metadata and media type information */
1782
- interface RawAgentFilePart {
1783
- type: 'file';
1784
- id?: string;
1785
- filename?: string;
1786
- /** AI-SDK-shaped parts carry the MIME type here… */
1787
- mediaType?: string;
1788
- /** …but OpenCode's native FilePart calls the same field `mime`. */
1789
- mime?: string;
1790
- url?: string;
1791
- }
1792
- /** Resolve the result of promoting a file part with success status and relevant data or error details */
1793
- type PromoteFilePartResult = {
1794
- succeeded: true;
1795
- part: ChatAttachmentPart;
1796
- } | {
1797
- succeeded: false;
1798
- filename: string;
1799
- reason: string;
1800
- };
1801
- /** Arguments handed to a {@link PromoteAgentFilePartOptions.buildAttachmentPath}
1802
- * override — everything needed to place the file deterministically. */
1803
- interface AttachmentPathArgs {
1804
- /** Sanitized display filename (basename, safe charset). */
1805
- filename: string;
1806
- /** First 8 hex chars of the SHA-256 idempotency digest. */
1807
- hash8: string;
1808
- /** `YYYY-MM-DD` from the injected clock. */
1809
- date: string;
1810
- /** Resolved media type. */
1811
- mediaType: string;
1812
- /** `image`/`file` split of the media type. */
1813
- kind: ChatAttachmentKind;
1814
- }
1815
- /** Default MIME hook: extension → mime, or `text/plain` for the unknown. */
1816
- declare function sniffMimeFromName(filename: string): string;
1817
- /** Define options for promoting a part of an agent file within a specific session and scope */
1818
- interface PromoteAgentFilePartOptions {
1819
- raw: RawAgentFilePart;
1820
- /** The turn's box — required only to promote a sandbox-path part; a `data:`
1821
- * URI needs none. */
1822
- box?: SandboxExecChannel;
1823
- /** The product's workspace/tenant key, passed to `writeAttachment`. */
1824
- scopeId: string;
1825
- /** The turn's session id, used for the sandbox stat/read exec calls. */
1826
- sessionId: string;
1827
- /** REQUIRED store writer — no default (the product owns its store). */
1828
- writeAttachment: WriteAttachmentFn;
1829
- /** Store-path strategy. Default {@link defaultBuildAttachmentPath}. */
1830
- buildAttachmentPath?: (args: AttachmentPathArgs) => string;
1831
- /** Raw-byte ceiling. Default {@link PROMOTE_MAX_FILE_BYTES}. */
1832
- maxBytes?: number;
1833
- /** Last-resort media-type hook. Default {@link sniffMimeFromName}. */
1834
- sniffMime?: (filename: string) => string;
1835
- /** Clock for the date path segment. Default `() => new Date()`. */
1836
- now?: () => Date;
1837
- }
1838
- /** Promote a part of an agent file with optional byte limits and MIME type detection */
1839
- declare function promoteAgentFilePart(options: PromoteAgentFilePartOptions): Promise<PromoteFilePartResult>;
1840
-
1841
- export { type AssistantDraftSnapshot, type AssistantDraftStore, type AssistantDraftWriter, type AssistantDraftWriterOptions, type AssistantRowValues, type AttachmentPathArgs, type AttachmentPathCheck, type AttachmentReadResult, type AttachmentUploadAuthorization, type AttachmentWriteResult, type BuildDispatchPartsInput, ChatAttachmentKind, type ChatRouteDurableProjection, type ChatRouteDurableProjectionLogger, type ChatRouteEvent, type ChatTurnAuthorization, type ChatTurnAuthorizeArgs, type ChatTurnCompleteInput, ChatTurnFilePartInput, type ChatTurnGateResult, type ChatTurnHeartbeat, type ChatTurnInputPatch, type ChatTurnLifecycle, type ChatTurnLifecycleComplete, type ChatTurnLifecycleError, type ChatTurnLifecycleStart, type ChatTurnLock, type ChatTurnLockResult, type ChatTurnMessageStore, type ChatTurnModelAttribution, type ChatTurnModelFailover, ChatTurnPartInput, type ChatTurnProduceArgs, ChatTurnRequestPayload, type ChatTurnRouteProducer, type ChatTurnRoutes, type ChatTurnUsage, type CompletedSandboxTurnSource, type CreateAttachmentUploadRouteOptions, type CreateChatTurnRoutesOptions, type CreateUploadRouteOptions, DEFAULT_MODEL_FIRST_RESPONSE_TIMEOUT_MS, DEFAULT_MODEL_STREAM_OPEN_TIMEOUT_MS, type DetachedTurnFinal, type DetachedTurnOptions, type DetachedTurnParts, type DetachedTurnResult, type DispatchPartsOutcome, type DraftPersistenceTuning, type DraftStoredMessage, type EmptyTurnRetryInfo, type FilePartPromotionOutcome, MAX_EMPTY_TURN_RETRIES, type ModelAttemptTimeoutCode, type ModelFailoverStreamHandle, type ModelFailoverStreamOptions, ModelFailoverTimeoutError, type ModelFallbackInfo, type OpenModelStream, PROMOTE_MAX_FILE_BYTES, type PromoteAgentFilePartOptions, type PromoteFilePartResult, PromptInputPart, type RawAgentFilePart, type ReadAttachmentFn, type ReadCompletedSandboxTurnOptions, type ReadSandboxMentionFn, type ResolveChatAttachmentsOptions, type ResolveChatAttachmentsResult, type SandboxChatProducerOptions, type SandboxUploadSink, UPLOAD_INLINE_MAX_BYTES, UPLOAD_MAX_FILE_BYTES, type UploadAuthorization, type UploadedChatFile, type WriteAttachmentFn, assistantRowIdForTurn, buildDispatchParts, bytesToBase64, classifyTerminalFailure, createAssistantDraftWriter, createAttachmentUploadRoute, createChatTurnRoutes, createSandboxChatProducer, createUploadRoute, defaultValidateAttachmentPath, isCommittingSandboxEvent, isDraftContentEvent, normalizeChatPromptForSandbox, promoteAgentFilePart, readCompletedSandboxTurn, resolveChatAttachments, resolveEmptyTurnRetries, rowIdOf, runDetachedTurn, sanitizeUploadFilename, sniffMimeFromName, storeSupportsDraftPersistence, streamWithModelFailover, summarizeFailoverReason, withDurableChatProjection };
2
+ * `/chat-routes` the assembled server chat vertical (issue #188 Phase 1).
3
+ *
4
+ * Subpath-only (NOT re-exported from the root barrel): `turn-routes` imports
5
+ * the optional `@tangle-network/agent-runtime` peer at module top, same rule
6
+ * as `/app-auth`. The browser-safe wire contract lives in `./wire` and is
7
+ * re-exported through `/web-react`'s chat-stream glue.
8
+ */
9
+ export * from './wire';
10
+ export * from './binary-sniff';
11
+ export * from './attachment-validation';
12
+ export * from './turn-routes';
13
+ export * from './stale-turn-lock';
14
+ export * from './sandbox-producer';
15
+ export * from './model-failover-stream';
16
+ export * from './draft-persistence';
17
+ export * from './detached-turn';
18
+ export * from './completed-sandbox-turn';
19
+ export * from './durable-projection';
20
+ export * from './upload';
21
+ export * from './file-index';
22
+ export * from './attachment-store';
23
+ export * from './resolve-attachments';
24
+ export * from './attachment-upload';
25
+ export * from './dispatch-parts';
26
+ export * from './promote-file-part';