@vellumai/assistant 0.12.3-staging.1 → 0.12.3-staging.2

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 (560) hide show
  1. package/ARCHITECTURE.md +43 -29
  2. package/Dockerfile +63 -0
  3. package/docs/architecture/security.md +10 -5
  4. package/docs/architecture/turn-actor.md +2 -0
  5. package/docs/desktop-browser-cli.md +45 -4
  6. package/docs/live-voice-task-outcomes.md +100 -0
  7. package/docs/mode-sessions.md +183 -0
  8. package/docs/voice-input-diagnostics.md +58 -0
  9. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +2 -1
  10. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  11. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
  12. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +2 -1
  13. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  14. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
  15. package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +20 -0
  16. package/node_modules/@vellumai/gateway-client/src/index.ts +4 -1
  17. package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +38 -7
  18. package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +5 -6
  19. package/node_modules/@vellumai/service-contracts/package.json +2 -1
  20. package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  21. package/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
  22. package/node_modules/@vellumai/slack-text/src/index.ts +4 -8
  23. package/openapi.yaml +665 -14
  24. package/package.json +1 -1
  25. package/scripts/smoke-desktop-browser-cli.ts +10 -26
  26. package/scripts/smoke-desktop-window-theme.ts +2 -3
  27. package/src/__tests__/agent-loop-output-hooks.test.ts +150 -1
  28. package/src/__tests__/agent-loop-provider-error-recording.test.ts +87 -19
  29. package/src/__tests__/annotate-activity-metadata.test.ts +3 -1
  30. package/src/__tests__/annotate-risk-options.test.ts +2 -0
  31. package/src/__tests__/answered-question-persistence.test.ts +2 -0
  32. package/src/__tests__/anthropic-gateway-shared.test.ts +22 -0
  33. package/src/__tests__/approval-message-composer.test.ts +1 -118
  34. package/src/__tests__/approval-routes-http.test.ts +3 -0
  35. package/src/__tests__/assistant-event-hub-connection-history.test.ts +111 -0
  36. package/src/__tests__/assistant-event-hub-targeted.test.ts +50 -0
  37. package/src/__tests__/assistant-event-targeting.test.ts +186 -0
  38. package/src/__tests__/assistant-stream-state.test.ts +29 -9
  39. package/src/__tests__/browser-fill-credential.test.ts +11 -22
  40. package/src/__tests__/btw-routes.test.ts +4 -1
  41. package/src/__tests__/build-persisted-content.test.ts +1 -1
  42. package/src/__tests__/call-conversation-messages.test.ts +5 -0
  43. package/src/__tests__/call-recovery.test.ts +7 -2
  44. package/src/__tests__/call-routes-http.test.ts +12 -0
  45. package/src/__tests__/call-store.test.ts +7 -2
  46. package/src/__tests__/channel-retry-sweep.test.ts +9 -0
  47. package/src/__tests__/chat-reveal-guard-priming.test.ts +2 -0
  48. package/src/__tests__/checker.test.ts +11 -3
  49. package/src/__tests__/client-os-metadata-persistence.test.ts +35 -1
  50. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +0 -25
  51. package/src/__tests__/computer-use-tools.test.ts +1 -233
  52. package/src/__tests__/config-model-image-provider.test.ts +39 -0
  53. package/src/__tests__/config-schema.test.ts +16 -0
  54. package/src/__tests__/config-watcher.test.ts +24 -0
  55. package/src/__tests__/context-search-agent-runner.test.ts +260 -1
  56. package/src/__tests__/context-search-fanout.test.ts +1 -1
  57. package/src/__tests__/context-search-types.test.ts +7 -7
  58. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +8 -2
  59. package/src/__tests__/conversation-agent-loop-fatal-cleanup.test.ts +2 -0
  60. package/src/__tests__/conversation-agent-loop.test.ts +546 -16
  61. package/src/__tests__/conversation-app-control-lifecycle.test.ts +53 -1
  62. package/src/__tests__/conversation-evictor.test.ts +62 -37
  63. package/src/__tests__/conversation-history-boundary-repair-failure.test.ts +174 -0
  64. package/src/__tests__/conversation-loop-db-lock-resilience.test.ts +2 -0
  65. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +2 -0
  66. package/src/__tests__/conversation-retry-route.test.ts +22 -0
  67. package/src/__tests__/conversation-routes-disk-view.test.ts +2 -0
  68. package/src/__tests__/conversation-routes-enabled-plugins.test.ts +2 -0
  69. package/src/__tests__/conversation-routes-guardian-reply.test.ts +10 -0
  70. package/src/__tests__/conversation-routes-hidden-queue.test.ts +2 -0
  71. package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -0
  72. package/src/__tests__/conversation-runtime-assembly.test.ts +29 -0
  73. package/src/__tests__/conversation-surface-snapshots.test.ts +543 -0
  74. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +151 -0
  75. package/src/__tests__/conversation-surfaces-point-at-budget.test.ts +63 -1
  76. package/src/__tests__/conversation-tool-setup-attribution.test.ts +23 -0
  77. package/src/__tests__/conversation-undo.test.ts +61 -0
  78. package/src/__tests__/credential-execution-client.test.ts +6 -6
  79. package/src/__tests__/credential-prompt-route.test.ts +35 -3
  80. package/src/__tests__/cu-unified-flow.test.ts +82 -1
  81. package/src/__tests__/db-mode-sessions-migration.test.ts +175 -0
  82. package/src/__tests__/deterministic-verification-control-plane.test.ts +2 -0
  83. package/src/__tests__/dm-backfill.test.ts +91 -1
  84. package/src/__tests__/document-create-route.test.ts +88 -0
  85. package/src/__tests__/document-sync-tags.test.ts +27 -0
  86. package/src/__tests__/emit-signal-routing-intent.test.ts +142 -2
  87. package/src/__tests__/events-tail-route.test.ts +25 -0
  88. package/src/__tests__/evict-conversations-for-reload.test.ts +57 -8
  89. package/src/__tests__/guardian-action-sweep.test.ts +16 -0
  90. package/src/__tests__/guardian-dispatch.test.ts +13 -0
  91. package/src/__tests__/headless-browser-interactions.test.ts +64 -20
  92. package/src/__tests__/heartbeat-service.test.ts +2 -59
  93. package/src/__tests__/helpers/mock-conversation.ts +21 -0
  94. package/src/__tests__/helpers/native-web-search-harness.ts +2 -0
  95. package/src/__tests__/helpers/verification-sessions-ipc-sim.ts +5 -0
  96. package/src/__tests__/host-cu-proxy.test.ts +46 -2
  97. package/src/__tests__/http-user-message-parity.test.ts +2 -0
  98. package/src/__tests__/image-credentials.test.ts +49 -0
  99. package/src/__tests__/image-service-dispatcher.test.ts +45 -0
  100. package/src/__tests__/inference-profile-session-handler.test.ts +1 -1
  101. package/src/__tests__/interrupt-on-send.test.ts +57 -43
  102. package/src/__tests__/interrupt-turn-note.test.ts +5 -6
  103. package/src/__tests__/llm-catalog-parity.test.ts +2 -2
  104. package/src/__tests__/media-generate-image.test.ts +49 -1
  105. package/src/__tests__/message-provenance-contact-id.test.ts +69 -0
  106. package/src/__tests__/migration-export-to-gcs.test.ts +142 -0
  107. package/src/__tests__/mode-session-history.test.ts +245 -0
  108. package/src/__tests__/mode-session-recovery.test.ts +222 -0
  109. package/src/__tests__/mode-session-wire.test.ts +141 -0
  110. package/src/__tests__/notification-guardian-path.test.ts +7 -0
  111. package/src/__tests__/notification-telegram-adapter.test.ts +2 -2
  112. package/src/__tests__/notification-vellum-adapter.test.ts +34 -0
  113. package/src/__tests__/oauth-provider-serializer.test.ts +12 -0
  114. package/src/__tests__/oauth-providers-routes.test.ts +1 -0
  115. package/src/__tests__/outbound-slack-persistence.test.ts +2 -0
  116. package/src/__tests__/persistence-secret-redaction.test.ts +2 -0
  117. package/src/__tests__/plugin-api-provider.test.ts +5 -0
  118. package/src/__tests__/plugin-api-resolve-credential.test.ts +97 -0
  119. package/src/__tests__/plugin-import-boundary-guard.test.ts +9 -0
  120. package/src/__tests__/process-message-display-content.test.ts +56 -0
  121. package/src/__tests__/reaction-persistence.test.ts +5 -0
  122. package/src/__tests__/request-secret-standalone-channel-gate.test.ts +25 -9
  123. package/src/__tests__/runtime-events-sse-reconnect.test.ts +1 -1
  124. package/src/__tests__/scripted-turn-metadata-persistence.test.ts +73 -0
  125. package/src/__tests__/secret-ingress-http.test.ts +2 -0
  126. package/src/__tests__/secure-keys.test.ts +43 -1
  127. package/src/__tests__/send-endpoint-busy.test.ts +163 -1
  128. package/src/__tests__/server-history-render.test.ts +91 -1
  129. package/src/__tests__/settings-routes.test.ts +50 -1
  130. package/src/__tests__/skill-script-runner-sandbox.test.ts +32 -0
  131. package/src/__tests__/subagent-fork-notifications.test.ts +1 -2
  132. package/src/__tests__/subagent-manager-notify.test.ts +1 -2
  133. package/src/__tests__/subagent-notify-parent.test.ts +146 -39
  134. package/src/__tests__/subagent-notify-stale-rebuild.test.ts +2 -4
  135. package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +2 -0
  136. package/src/__tests__/thread-backfill.test.ts +53 -0
  137. package/src/__tests__/tool-preview-lifecycle.test.ts +2 -0
  138. package/src/__tests__/tool-result-metadata-plumbing.test.ts +3 -1
  139. package/src/__tests__/tool-start-timestamp.test.ts +2 -0
  140. package/src/__tests__/twilio-routes.test.ts +1 -0
  141. package/src/__tests__/web-search-backend-failure.test.ts +1 -1
  142. package/src/activation/progress-store.test.ts +12 -7
  143. package/src/agent/loop.ts +99 -41
  144. package/src/api/events/assistant-turn-start.ts +3 -0
  145. package/src/api/events/generation-handoff.ts +2 -0
  146. package/src/api/events/message-complete.ts +2 -0
  147. package/src/api/events/notification-conversation-created.ts +14 -16
  148. package/src/api/events/notification-intent.ts +4 -2
  149. package/src/api/events/tool-result.ts +85 -0
  150. package/src/api/events/tool-use-start.ts +3 -0
  151. package/src/api/events/user-message-echo.ts +3 -0
  152. package/src/api/index.ts +31 -6
  153. package/src/api/mode-session.test.ts +131 -0
  154. package/src/api/mode-session.ts +148 -0
  155. package/src/api/remember-tool.ts +15 -0
  156. package/src/api/responses/conversation-message.ts +6 -0
  157. package/src/approvals/AGENTS.md +4 -2
  158. package/src/browser/__tests__/operations.test.ts +29 -0
  159. package/src/browser/operation-meta.ts +2 -1
  160. package/src/browser/operations.ts +29 -0
  161. package/src/browser/virtual-desktop-target.ts +2 -8
  162. package/src/bundler/bundle-signer.ts +1 -3
  163. package/src/calls/__tests__/barge-in-guard.test.ts +3 -0
  164. package/src/calls/__tests__/call-controller.test.ts +94 -0
  165. package/src/calls/__tests__/call-funnel-telemetry.test.ts +204 -0
  166. package/src/calls/__tests__/voice-control-protocol.test.ts +15 -0
  167. package/src/calls/__tests__/voice-escalation-judge.test.ts +209 -0
  168. package/src/calls/__tests__/voice-leg-coordinator.test.ts +168 -0
  169. package/src/{live-voice/__tests__/live-voice-metrics.test.ts → calls/__tests__/voice-metrics.test.ts} +32 -38
  170. package/src/calls/__tests__/voice-session-bridge.test.ts +576 -122
  171. package/src/calls/barge-in-guard.ts +5 -0
  172. package/src/calls/call-controller.ts +196 -1
  173. package/src/calls/call-domain.ts +4 -0
  174. package/src/calls/call-store.ts +87 -2
  175. package/src/calls/guardian-action-sweep.ts +22 -8
  176. package/src/calls/media-stream-audio-transcode.ts +1 -3
  177. package/src/calls/typesafe-noul.ts +156 -0
  178. package/src/calls/voice-control-protocol.ts +6 -0
  179. package/src/calls/voice-escalation-judge.ts +145 -0
  180. package/src/calls/voice-escalation-target.ts +10 -0
  181. package/src/calls/voice-leg-coordinator.ts +121 -3
  182. package/src/{live-voice/live-voice-metrics.ts → calls/voice-metrics.ts} +94 -87
  183. package/src/calls/voice-session-bridge.ts +256 -112
  184. package/src/calls/voice-triage-escalate.ts +7 -0
  185. package/src/cli/__tests__/roadmap-cli.test.ts +4 -41
  186. package/src/cli/commands/__tests__/notifications.test.ts +35 -10
  187. package/src/cli/commands/browser.help.ts +4 -5
  188. package/src/cli/commands/browser.ts +0 -1
  189. package/src/cli/commands/clients.help.ts +46 -0
  190. package/src/cli/commands/clients.ts +116 -0
  191. package/src/cli/commands/credentials.help.ts +6 -1
  192. package/src/cli/commands/notifications.help.ts +16 -2
  193. package/src/cli/commands/notifications.ts +30 -8
  194. package/src/cli/commands/roadmap.help.ts +18 -21
  195. package/src/cli/commands/roadmap.ts +4 -21
  196. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +5 -2
  197. package/src/cli/lib/bundled-marketplace.json +84 -34
  198. package/src/cli/lib/plugin-marketplace.ts +8 -0
  199. package/src/cli/lib/publish-plugin.ts +1 -2
  200. package/src/config/__tests__/default-provider.test.ts +3 -1
  201. package/src/config/__tests__/memory-retrospective-schema.test.ts +14 -1
  202. package/src/config/bundled-skills/computer-use/SKILL.md +1 -1
  203. package/src/config/bundled-skills/computer-use/TOOLS.json +89 -1
  204. package/src/config/bundled-skills/image-studio/SKILL.md +17 -2
  205. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +7 -11
  206. package/src/config/bundled-skills/sequences/TOOLS.json +1 -5
  207. package/src/config/call-site-defaults.ts +9 -0
  208. package/src/config/feature-flag-registry.json +16 -0
  209. package/src/config/profile-text-generation.test.ts +2 -2
  210. package/src/config/schemas/__tests__/tools.test.ts +31 -0
  211. package/src/config/schemas/call-site-catalog.ts +14 -0
  212. package/src/config/schemas/channels.ts +0 -18
  213. package/src/config/schemas/llm.ts +3 -1
  214. package/src/config/schemas/mcp.ts +2 -1
  215. package/src/config/schemas/memory-retrospective.ts +11 -2
  216. package/src/config/schemas/services.ts +15 -6
  217. package/src/config/schemas/tools.ts +10 -0
  218. package/src/config/session-groups-gate.ts +21 -0
  219. package/src/config/skills.ts +2 -6
  220. package/src/conversations/__tests__/message-consolidation.test.ts +149 -0
  221. package/src/conversations/message-consolidation.ts +102 -5
  222. package/src/credential-execution/ces-connect.test.ts +69 -0
  223. package/src/credential-execution/ces-connect.ts +115 -0
  224. package/src/credential-execution/ces-runtime.ts +55 -93
  225. package/src/credential-execution/executable-discovery.ts +1 -11
  226. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +10 -1
  227. package/src/daemon/__tests__/inference-profile-notification.test.ts +2 -0
  228. package/src/daemon/browser-mode-session.test.ts +402 -0
  229. package/src/daemon/browser-mode-session.ts +186 -0
  230. package/src/daemon/computer-use-mode-session.test.ts +210 -0
  231. package/src/daemon/computer-use-mode-session.ts +105 -0
  232. package/src/daemon/config-watcher.ts +10 -0
  233. package/src/daemon/conversation-agent-loop-handlers.ts +58 -10
  234. package/src/daemon/conversation-agent-loop.ts +219 -25
  235. package/src/daemon/conversation-evictor.ts +5 -24
  236. package/src/daemon/conversation-history.ts +10 -0
  237. package/src/daemon/conversation-interrupt-repair.ts +4 -43
  238. package/src/daemon/conversation-interrupt.ts +7 -11
  239. package/src/daemon/conversation-messaging.ts +58 -5
  240. package/src/daemon/conversation-mode-session.test.ts +1667 -0
  241. package/src/daemon/conversation-mode-session.ts +1008 -0
  242. package/src/daemon/conversation-process.ts +5 -0
  243. package/src/daemon/conversation-runtime-assembly.ts +10 -20
  244. package/src/daemon/conversation-skill-tools.ts +1 -0
  245. package/src/daemon/conversation-store.ts +4 -4
  246. package/src/daemon/conversation-surface-snapshots.ts +135 -0
  247. package/src/daemon/conversation-surfaces.ts +113 -5
  248. package/src/daemon/conversation-tool-setup.ts +8 -3
  249. package/src/daemon/conversation.ts +143 -41
  250. package/src/daemon/handlers/config-model.ts +7 -5
  251. package/src/daemon/handlers/shared.ts +55 -16
  252. package/src/daemon/host-cu-proxy.ts +51 -22
  253. package/src/daemon/lifecycle.ts +30 -13
  254. package/src/daemon/message-protocol.ts +0 -1
  255. package/src/daemon/message-provenance.ts +28 -4
  256. package/src/daemon/mode-session-startup-recovery.ts +23 -0
  257. package/src/daemon/mode-session-tracking.ts +54 -0
  258. package/src/daemon/process-message.ts +13 -4
  259. package/src/daemon/virtual-desktop-context.ts +21 -0
  260. package/src/desktop/__tests__/fake-desktop.ts +1 -0
  261. package/src/desktop/desktop-accessibility-script.ts +88 -0
  262. package/src/desktop/desktop-accessibility.test.ts +160 -0
  263. package/src/desktop/desktop-accessibility.ts +169 -0
  264. package/src/desktop/desktop-automation-lease.test.ts +76 -68
  265. package/src/desktop/desktop-automation-lease.ts +41 -50
  266. package/src/desktop/desktop-browser-endpoint.ts +4 -0
  267. package/src/desktop/desktop-computer-use-routing.test.ts +330 -0
  268. package/src/desktop/desktop-computer-use.test.ts +449 -0
  269. package/src/desktop/desktop-computer-use.ts +502 -0
  270. package/src/desktop/desktop-dependencies.ts +47 -293
  271. package/src/desktop/desktop-display.ts +2 -2
  272. package/src/desktop/desktop-panel-config.test.ts +13 -3
  273. package/src/desktop/desktop-panel-config.ts +109 -15
  274. package/src/desktop/desktop-session-manager.test.ts +69 -21
  275. package/src/desktop/desktop-session-manager.ts +76 -22
  276. package/src/desktop/desktop-stream-bridge.test.ts +11 -122
  277. package/src/desktop/desktop-stream-bridge.ts +6 -8
  278. package/src/desktop/desktop-window-theme.ts +5 -25
  279. package/src/desktop/virtual-desktop-feature.ts +52 -2
  280. package/src/desktop/virtual-desktop-platform.test.ts +5 -9
  281. package/src/documents/document-store.ts +23 -0
  282. package/src/heartbeat/__tests__/heartbeat-service.test.ts +22 -26
  283. package/src/heartbeat/heartbeat-service.ts +5 -33
  284. package/src/ipc/__tests__/browser-ipc.test.ts +215 -6
  285. package/src/ipc/assistant-server.ts +2 -0
  286. package/src/ipc/routes/__tests__/channel-reply-ipc-routes.test.ts +111 -0
  287. package/src/ipc/routes/channel-reply-ipc-routes.ts +73 -0
  288. package/src/live-voice/__tests__/continuation-judge.test.ts +151 -0
  289. package/src/live-voice/__tests__/live-activity-reporter.test.ts +154 -15
  290. package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +743 -31
  291. package/src/live-voice/__tests__/live-voice-look-follow-up.test.ts +191 -10
  292. package/src/live-voice/__tests__/live-voice-session-groups-gate.test.ts +87 -0
  293. package/src/live-voice/__tests__/live-voice-session-manager.test.ts +5 -1
  294. package/src/live-voice/__tests__/live-voice-sight-frame-echo.test.ts +41 -0
  295. package/src/live-voice/__tests__/live-voice-sight-frame.test.ts +1017 -9
  296. package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +224 -1
  297. package/src/live-voice/__tests__/live-voice-tts-session.test.ts +101 -0
  298. package/src/live-voice/__tests__/live-voice-tts.test.ts +44 -0
  299. package/src/live-voice/__tests__/live-voice-vad.test.ts +939 -75
  300. package/src/live-voice/__tests__/microphone-idle-gate.test.ts +87 -0
  301. package/src/live-voice/__tests__/session-controls.test.ts +23 -0
  302. package/src/live-voice/__tests__/task-outcomes.test.ts +117 -0
  303. package/src/live-voice/__tests__/voice-input-diagnostics.test.ts +117 -0
  304. package/src/live-voice/camera-mode-session.test.ts +265 -0
  305. package/src/live-voice/camera-mode-session.ts +277 -0
  306. package/src/live-voice/continuation-judge.ts +123 -0
  307. package/src/live-voice/live-activity-reporter.ts +115 -6
  308. package/src/live-voice/live-voice-manager.ts +36 -1
  309. package/src/live-voice/live-voice-photo.ts +88 -7
  310. package/src/live-voice/live-voice-session-manager.ts +15 -0
  311. package/src/live-voice/live-voice-session.ts +1443 -747
  312. package/src/live-voice/live-voice-tts.ts +22 -4
  313. package/src/live-voice/microphone-idle-gate.ts +56 -0
  314. package/src/live-voice/protocol.ts +171 -4
  315. package/src/live-voice/session-controls.ts +11 -3
  316. package/src/live-voice/task-outcomes.ts +131 -0
  317. package/src/live-voice/voice-input-diagnostics.ts +117 -0
  318. package/src/mcp/__tests__/manager-tool-caps.test.ts +63 -18
  319. package/src/mcp/__tests__/tool-caps.test.ts +28 -1
  320. package/src/mcp/manager.ts +3 -0
  321. package/src/mcp/tool-caps.ts +12 -4
  322. package/src/media/__tests__/image-models.test.ts +58 -0
  323. package/src/media/image-credentials.ts +5 -0
  324. package/src/media/image-models.ts +52 -0
  325. package/src/media/image-service.ts +8 -0
  326. package/src/media/openrouter-image-service.test.ts +133 -0
  327. package/src/media/openrouter-image-service.ts +153 -0
  328. package/src/media/types.ts +1 -1
  329. package/src/messaging/providers/__tests__/transport-dispatch.test.ts +65 -11
  330. package/src/messaging/providers/discord/send.ts +4 -17
  331. package/src/messaging/providers/discord/transport.ts +2 -2
  332. package/src/messaging/providers/outlook/client.ts +16 -22
  333. package/src/messaging/providers/telegram-bot/send.test.ts +236 -1
  334. package/src/messaging/providers/telegram-bot/send.ts +10 -7
  335. package/src/messaging/providers/telegram-bot/transport.ts +3 -10
  336. package/src/messaging/providers/whatsapp/send.test.ts +169 -4
  337. package/src/monitoring/__tests__/control.test.ts +17 -5
  338. package/src/monitoring/control.ts +15 -5
  339. package/src/notifications/AGENTS.md +1 -1
  340. package/src/notifications/README.md +83 -99
  341. package/src/notifications/__tests__/broadcaster.test.ts +1 -1
  342. package/src/notifications/__tests__/channel-allowlist.test.ts +39 -0
  343. package/src/notifications/__tests__/copy-composer.test.ts +1 -1
  344. package/src/notifications/__tests__/decision-engine.test.ts +79 -2
  345. package/src/notifications/__tests__/deterministic-checks.test.ts +10 -10
  346. package/src/notifications/__tests__/home-feed-side-effect.test.ts +93 -4
  347. package/src/notifications/__tests__/notification-utils.test.ts +18 -0
  348. package/src/notifications/adapters/macos.ts +37 -46
  349. package/src/notifications/broadcaster.ts +5 -6
  350. package/src/notifications/channel-allowlist.ts +44 -0
  351. package/src/notifications/copy-composer.ts +0 -23
  352. package/src/notifications/decision-engine.ts +24 -14
  353. package/src/notifications/deterministic-checks.ts +1 -1
  354. package/src/notifications/emit-signal.ts +123 -37
  355. package/src/notifications/home-feed-side-effect.ts +9 -7
  356. package/src/notifications/notification-utils.ts +19 -0
  357. package/src/notifications/signal.ts +4 -19
  358. package/src/notifications/types.ts +15 -0
  359. package/src/oauth/connection-resolver.ts +1 -2
  360. package/src/oauth/oauth-store.ts +1 -3
  361. package/src/oauth/provider-serializer.ts +7 -0
  362. package/src/oauth/seed-providers.ts +30 -0
  363. package/src/onboarding/onboarding-events-store.ts +99 -12
  364. package/src/persistence/__tests__/client-connection-events-store.test.ts +322 -0
  365. package/src/persistence/client-connection-events-store.ts +377 -0
  366. package/src/persistence/conversation-crud.ts +55 -6
  367. package/src/persistence/conversation-mode-sessions.test.ts +424 -0
  368. package/src/persistence/conversation-mode-sessions.ts +460 -0
  369. package/src/persistence/conversation-queries.ts +6 -19
  370. package/src/persistence/llm-usage-store.ts +3 -9
  371. package/src/persistence/message-metadata.ts +25 -0
  372. package/src/persistence/migrations/381-create-conversation-mode-sessions.ts +41 -0
  373. package/src/persistence/migrations/382-create-client-connection-events.ts +40 -0
  374. package/src/persistence/migrations/__tests__/382-create-client-connection-events.test.ts +56 -0
  375. package/src/persistence/schema/conversations.ts +67 -0
  376. package/src/persistence/schema/infrastructure.ts +29 -0
  377. package/src/persistence/steps.ts +17 -0
  378. package/src/plugin-api/__tests__/plugin-name-env.test.ts +249 -0
  379. package/src/plugin-api/effective-context-window.ts +33 -0
  380. package/src/plugin-api/index.ts +24 -5
  381. package/src/plugin-api/plugin-name-env.ts +125 -0
  382. package/src/plugin-api/resolve-credential.ts +79 -20
  383. package/src/plugin-api/vision-support.test.ts +22 -0
  384. package/src/plugin-api/vision-support.ts +17 -5
  385. package/src/plugins/__tests__/active-plugin-env.test.ts +136 -0
  386. package/src/plugins/active-plugin-env.ts +51 -0
  387. package/src/plugins/defaults/index.ts +24 -0
  388. package/src/plugins/defaults/memory/__tests__/archive-removal-framing.test.ts +64 -0
  389. package/src/plugins/defaults/memory/__tests__/worker-control.test.ts +19 -6
  390. package/src/plugins/defaults/memory/context-search/agent-protocol.ts +9 -12
  391. package/src/plugins/defaults/memory/context-search/agent-runner.ts +78 -22
  392. package/src/plugins/defaults/memory/context-search/format.ts +51 -7
  393. package/src/plugins/defaults/memory/context-search/limits.ts +10 -12
  394. package/src/plugins/defaults/memory/context-search/recall-input.ts +38 -0
  395. package/src/plugins/defaults/memory/context-search/search.ts +8 -14
  396. package/src/plugins/defaults/memory/context-search/sources/conversations.ts +1 -0
  397. package/src/plugins/defaults/memory/context-search/types.ts +1 -11
  398. package/src/plugins/defaults/memory/graph/__tests__/handle-remember-v2.test.ts +11 -4
  399. package/src/plugins/defaults/memory/graph/tool-handlers.ts +12 -21
  400. package/src/plugins/defaults/memory/graph/tools.ts +35 -64
  401. package/src/plugins/defaults/memory/src/memory-eval-routes.ts +2 -3
  402. package/src/plugins/defaults/memory/src/memory-item-routes.test.ts +5 -0
  403. package/src/plugins/defaults/memory/src/memory-item-routes.ts +2 -1
  404. package/src/plugins/defaults/memory/substrate/prompts/consolidation.ts +2 -2
  405. package/src/plugins/defaults/memory/tools.test.ts +124 -9
  406. package/src/plugins/defaults/memory/tools.ts +38 -11
  407. package/src/plugins/defaults/memory/v3/__tests__/pool-log-store.test.ts +165 -27
  408. package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +626 -27
  409. package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +46 -27
  410. package/src/plugins/defaults/memory/v3/orchestrate.ts +78 -38
  411. package/src/plugins/defaults/memory/v3/pool-log-store.ts +65 -79
  412. package/src/plugins/defaults/memory/v3/pool-select.test.ts +14 -5
  413. package/src/plugins/defaults/memory/v3/pool-select.ts +683 -44
  414. package/src/plugins/defaults/memory/worker-control.ts +15 -5
  415. package/src/plugins/defaults/task-progress/__tests__/registration.test.ts +70 -0
  416. package/src/plugins/defaults/task-progress/__tests__/task-progress-context.test.ts +380 -0
  417. package/src/plugins/defaults/task-progress/hooks/post-compact.ts +19 -0
  418. package/src/plugins/defaults/task-progress/hooks/user-prompt-submit.ts +24 -0
  419. package/src/plugins/defaults/task-progress/package.json +14 -0
  420. package/src/plugins/defaults/task-progress/src/apply-task-progress-context.ts +93 -0
  421. package/src/plugins/defaults/task-progress/src/format-task-progress-context.ts +67 -0
  422. package/src/providers/__tests__/context-overflow-error.test.ts +10 -0
  423. package/src/providers/__tests__/search-provider-catalog.test.ts +18 -0
  424. package/src/providers/anthropic/client.ts +10 -1
  425. package/src/providers/anthropic-gateway-shared.ts +6 -0
  426. package/src/providers/fetch-provider-catalog.ts +11 -0
  427. package/src/providers/gemini/client.ts +25 -8
  428. package/src/providers/inference/adapter-factory.ts +1 -1
  429. package/src/providers/jev/client.test.ts +10 -0
  430. package/src/providers/jev/client.ts +20 -4
  431. package/src/providers/model-catalog.ts +4 -3
  432. package/src/providers/openai/__tests__/chat-completions-extra-body.test.ts +94 -1
  433. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +106 -1
  434. package/src/providers/openai/__tests__/chat-template-flatten.test.ts +24 -0
  435. package/src/providers/openai/__tests__/google-thought-signature.test.ts +1 -2
  436. package/src/providers/openai/chat-completions-provider.ts +100 -29
  437. package/src/providers/openai/google-thought-signature.ts +0 -6
  438. package/src/providers/openai/responses-provider.ts +23 -1
  439. package/src/providers/ratelimit.ts +1 -3
  440. package/src/providers/search-provider-catalog.ts +24 -0
  441. package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +24 -0
  442. package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +7 -1
  443. package/src/providers/speech-to-text/__tests__/vellum-managed-realtime.test.ts +50 -1
  444. package/src/providers/speech-to-text/deepgram-flux-frames.ts +11 -0
  445. package/src/providers/speech-to-text/vellum-managed-flux-realtime.ts +13 -0
  446. package/src/providers/types.ts +5 -0
  447. package/src/routes/control.ts +7 -5
  448. package/src/runtime/AGENTS.md +4 -2
  449. package/src/runtime/actor-trust-resolver.ts +3 -3
  450. package/src/runtime/agent-wake.ts +1 -1
  451. package/src/runtime/approval-message-composer.ts +3 -69
  452. package/src/runtime/assistant-event-hub.ts +73 -75
  453. package/src/runtime/assistant-event-publish-options.ts +7 -0
  454. package/src/runtime/assistant-event-targeting.ts +79 -0
  455. package/src/runtime/assistant-stream-state.ts +13 -100
  456. package/src/runtime/background-job-runner.ts +5 -5
  457. package/src/runtime/channel-retry-sweep.ts +1 -0
  458. package/src/runtime/guardian-action-message-composer.ts +8 -161
  459. package/src/runtime/guardian-reply-router.ts +28 -32
  460. package/src/runtime/http-types.ts +6 -0
  461. package/src/runtime/message-composer-types.ts +3 -61
  462. package/src/runtime/migrations/__tests__/vbundle-import-parity.test.ts +67 -1
  463. package/src/runtime/migrations/vbundle-builder.ts +24 -1
  464. package/src/runtime/migrations/vbundle-import-analyzer.ts +13 -4
  465. package/src/runtime/migrations/vbundle-import-policy.ts +10 -0
  466. package/src/runtime/migrations/vbundle-importer.ts +4 -1
  467. package/src/runtime/migrations/vbundle-streaming-importer.ts +4 -1
  468. package/src/runtime/migrations/vbundle-validator.ts +1 -0
  469. package/src/runtime/pending-interactions-mode-session.test.ts +62 -0
  470. package/src/runtime/pending-interactions.ts +29 -0
  471. package/src/runtime/routes/__tests__/client-history-routes.test.ts +104 -0
  472. package/src/runtime/routes/__tests__/image-generation-routes.test.ts +97 -0
  473. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +1 -1
  474. package/src/runtime/routes/__tests__/roadmap-routes.test.ts +29 -6
  475. package/src/runtime/routes/browser-context.ts +31 -11
  476. package/src/runtime/routes/browser-routes.ts +43 -4
  477. package/src/runtime/routes/btw-routes.ts +9 -4
  478. package/src/runtime/routes/client-routes.ts +108 -0
  479. package/src/runtime/routes/conversation-management-routes.ts +4 -1
  480. package/src/runtime/routes/conversation-routes.ts +113 -27
  481. package/src/runtime/routes/credential-prompt-routes.ts +4 -3
  482. package/src/runtime/routes/credential-routes.ts +1 -2
  483. package/src/runtime/routes/desktop-setup-routes.test.ts +9 -18
  484. package/src/runtime/routes/desktop-setup-routes.ts +4 -6
  485. package/src/runtime/routes/documents-routes.ts +51 -0
  486. package/src/runtime/routes/email-routes.ts +3 -7
  487. package/src/runtime/routes/events-routes.ts +75 -54
  488. package/src/runtime/routes/identity-routes.ts +1 -5
  489. package/src/runtime/routes/image-generation-routes.ts +8 -14
  490. package/src/runtime/routes/inbound-message-handler.ts +35 -38
  491. package/src/runtime/routes/inbound-stages/backfill-sender-provenance.test.ts +168 -0
  492. package/src/runtime/routes/inbound-stages/backfill-sender-provenance.ts +96 -0
  493. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +38 -9
  494. package/src/runtime/routes/inbound-stages/background-dispatch.ts +2 -5
  495. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +2 -0
  496. package/src/runtime/routes/llm-context-normalization.ts +1 -7
  497. package/src/runtime/routes/migration-routes.ts +73 -2
  498. package/src/runtime/routes/notification-routes.ts +10 -0
  499. package/src/runtime/routes/oauth-providers.ts +7 -0
  500. package/src/runtime/routes/plugins-routes.ts +6 -0
  501. package/src/runtime/routes/roadmap-routes.ts +47 -8
  502. package/src/runtime/routes/secret-routes.ts +1 -1
  503. package/src/runtime/routes/settings-routes.ts +10 -4
  504. package/src/runtime/routes/watcher-routes.ts +1 -3
  505. package/src/runtime/trust-class.test.ts +7 -6
  506. package/src/runtime/trust-class.ts +9 -17
  507. package/src/schedule/__tests__/worker-control.test.ts +36 -5
  508. package/src/schedule/worker-control.ts +15 -5
  509. package/src/security/secure-keys.ts +53 -103
  510. package/src/skills/clawhub.ts +3 -8
  511. package/src/skills/managed-store.ts +1 -5
  512. package/src/stt/types.ts +4 -0
  513. package/src/subagent/notify.ts +54 -34
  514. package/src/subagent/parent-notification.ts +6 -0
  515. package/src/telemetry/__tests__/phone-call-funnel.test.ts +72 -0
  516. package/src/telemetry/phone-call-funnel.ts +144 -0
  517. package/src/tools/__tests__/tool-schema-root-combinator-guard.test.ts +81 -0
  518. package/src/tools/ask-question/ask-question-tool.ts +6 -4
  519. package/src/tools/browser/browser-execution.ts +22 -59
  520. package/src/tools/browser/cdp-client/__tests__/cdp-dom-helpers.test.ts +561 -39
  521. package/src/tools/browser/cdp-client/cdp-dom-helpers.ts +435 -39
  522. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +1 -8
  523. package/src/tools/client-os.ts +15 -0
  524. package/src/tools/computer-use/skill-proxy-bridge.ts +1 -1
  525. package/src/tools/computer-use/target.ts +42 -0
  526. package/src/tools/document/document-tool.ts +13 -18
  527. package/src/tools/execution-target.ts +14 -16
  528. package/src/tools/host-terminal/host-shell.ts +4 -1
  529. package/src/tools/network/__tests__/web-fetch-tinyfish.test.ts +250 -0
  530. package/src/tools/network/__tests__/web-search.test.ts +203 -0
  531. package/src/tools/network/web-fetch.ts +274 -7
  532. package/src/tools/network/web-search.ts +468 -6
  533. package/src/tools/permission-checker.ts +3 -3
  534. package/src/tools/policy-context.ts +3 -1
  535. package/src/tools/skills/load.ts +2 -1
  536. package/src/tools/skills/sandbox-runner.ts +11 -2
  537. package/src/tools/skills/skill-script-runner.ts +7 -0
  538. package/src/tools/skills/skill-tool-factory.ts +15 -1
  539. package/src/tools/terminal/__tests__/safe-env.test.ts +11 -0
  540. package/src/tools/terminal/safe-env.ts +4 -0
  541. package/src/tools/terminal/shell.ts +2 -0
  542. package/src/tools/tool-approval-handler.ts +5 -1
  543. package/src/tools/tool-types.ts +3 -3
  544. package/src/tools/types.ts +8 -2
  545. package/src/util/__tests__/worker-process-command.test.ts +5 -4
  546. package/src/util/abort-reasons.ts +18 -26
  547. package/src/util/canonicalize-identity.test.ts +21 -0
  548. package/src/util/canonicalize-identity.ts +16 -0
  549. package/src/util/errors.ts +13 -3
  550. package/src/util/provider-error-patterns.ts +3 -0
  551. package/src/util/worker-process.ts +77 -35
  552. package/src/workspace/migrations/157-add-desktop-files-launcher.ts +94 -0
  553. package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +4 -10
  554. package/src/workspace/migrations/__tests__/157-add-desktop-files-launcher.test.ts +113 -0
  555. package/src/workspace/migrations/registry.ts +2 -0
  556. package/src/__tests__/computer-use-window-schema.test.ts +0 -24
  557. package/src/api/events/heartbeat-alert.ts +0 -20
  558. package/src/daemon/message-types/web-activity.ts +0 -65
  559. package/src/desktop/desktop-dependencies.test.ts +0 -131
  560. package/src/tools/computer-use/definitions.ts +0 -607
@@ -0,0 +1,67 @@
1
+ import type { ConversationSurfaceSnapshot } from "../../../../daemon/conversation-surface-snapshots.js";
2
+ import { getTaskProgressDataFromSurfaceData } from "../../../../runtime/task-progress.js";
3
+
4
+ export const ACTIVE_TASK_PROGRESS_OPEN = "<active_task_progress>";
5
+ export const ACTIVE_TASK_PROGRESS_CLOSE = "</active_task_progress>";
6
+
7
+ export const ACTIVE_TASK_PROGRESS_BLOCK =
8
+ /<active_task_progress\b[\s\S]*?<\/active_task_progress>/gi;
9
+
10
+ const STEP_INSTRUCTION =
11
+ "Update this card with ui_update as the steps change.";
12
+
13
+ function normalizeField(value: string): string {
14
+ return value
15
+ .replace(/[<>]/g, "")
16
+ .replace(/[\r\n\u2028\u2029]+/g, " ")
17
+ .replace(/\s+/g, " ")
18
+ .trim();
19
+ }
20
+
21
+ export function isActiveTaskProgressSurface(
22
+ snapshot: ConversationSurfaceSnapshot,
23
+ ): boolean {
24
+ if (snapshot.surfaceType !== "card" || snapshot.completed) {
25
+ return false;
26
+ }
27
+ const plan = getTaskProgressDataFromSurfaceData(snapshot.data);
28
+ if (!plan) {
29
+ return false;
30
+ }
31
+ return plan.steps.some(
32
+ (step) => step.status === "pending" || step.status === "in_progress",
33
+ );
34
+ }
35
+
36
+ export function formatActiveTaskProgressSnapshot(
37
+ snapshots: ConversationSurfaceSnapshot[],
38
+ ): string {
39
+ const cards = snapshots.filter(isActiveTaskProgressSurface);
40
+ if (cards.length === 0) {
41
+ return "";
42
+ }
43
+ return cards
44
+ .map((snapshot) => {
45
+ const plan = getTaskProgressDataFromSurfaceData(snapshot.data);
46
+ if (!plan) {
47
+ return "";
48
+ }
49
+ const lines = [
50
+ ACTIVE_TASK_PROGRESS_OPEN,
51
+ `surface_id: ${normalizeField(snapshot.surfaceId)}`,
52
+ ];
53
+ const title = plan.title ? normalizeField(plan.title) : "";
54
+ if (title.length > 0) {
55
+ lines.push(`title: ${title}`);
56
+ }
57
+ lines.push("steps:");
58
+ for (const step of plan.steps) {
59
+ lines.push(`- [${step.status}] ${normalizeField(step.label)}`);
60
+ }
61
+ lines.push(STEP_INSTRUCTION);
62
+ lines.push(ACTIVE_TASK_PROGRESS_CLOSE);
63
+ return lines.join("\n");
64
+ })
65
+ .filter((block) => block.length > 0)
66
+ .join("\n");
67
+ }
@@ -47,6 +47,16 @@ describe("ContextOverflowError", () => {
47
47
  expect((err as Error & { cause?: unknown }).cause).toBe(cause);
48
48
  });
49
49
 
50
+ test("forwards rawRequest onto the ProviderError", () => {
51
+ const rawRequest = { model: "claude-opus-4.6", messages: [] };
52
+ const err = new ContextOverflowError("m", "anthropic", {
53
+ actualTokens: 10,
54
+ maxTokens: 5,
55
+ rawRequest,
56
+ });
57
+ expect(err.rawRequest).toEqual(rawRequest);
58
+ });
59
+
50
60
  test("omits optional fields when unset", () => {
51
61
  const err = new ContextOverflowError("m", "gemini");
52
62
  expect(err.actualTokens).toBeUndefined();
@@ -86,4 +86,22 @@ describe("search-provider-catalog", () => {
86
86
  expect(getSearchProvider("brave")?.displayName).toBe("Brave");
87
87
  expect(getSearchProvider("unknown-xyz")).toBeUndefined();
88
88
  });
89
+
90
+ test("SearXNG is a keyless BYOK provider that requires an instance URL", () => {
91
+ const searxng = getSearchProvider("searxng");
92
+ expect(searxng?.kind).toBe("byok");
93
+ expect(searxng?.keyless).toBe(true);
94
+ expect(searxng?.supportsApiBase).toBe(true);
95
+ expect(searxng?.defaultApiBase).toBeUndefined();
96
+ expect(searxng?.envVar).toBe("SEARXNG_API_KEY");
97
+ });
98
+
99
+ test("TinyFish is a keyed BYOK provider with a cloud API base", () => {
100
+ const tinyfish = getSearchProvider("tinyfish");
101
+ expect(tinyfish?.kind).toBe("byok");
102
+ expect(tinyfish?.keyless).toBeUndefined();
103
+ expect(tinyfish?.supportsApiBase).toBe(true);
104
+ expect(tinyfish?.defaultApiBase).toBe("https://api.search.tinyfish.ai");
105
+ expect(tinyfish?.envVar).toBe("TINYFISH_API_KEY");
106
+ });
89
107
  });
@@ -958,6 +958,7 @@ export class AnthropicProvider implements Provider {
958
958
  // (30 min default) from an external transport abort (bun fetch deadline,
959
959
  // edge LB, NAT idle) — only the latter should be retried.
960
960
  let innerTimeoutSignal: AbortSignal | undefined;
961
+ let inspectableRequest: unknown | undefined;
961
962
  try {
962
963
  sentMessages = await this.buildSentMessages(messages);
963
964
  const {
@@ -1344,6 +1345,7 @@ export class AnthropicProvider implements Provider {
1344
1345
  const streamOnce = async (
1345
1346
  streamParams: Anthropic.MessageStreamParams,
1346
1347
  ): Promise<Anthropic.Message> => {
1348
+ inspectableRequest = streamParams;
1347
1349
  const stream: UnifiedStream = useFastMode
1348
1350
  ? (this.client.beta.messages.stream(
1349
1351
  {
@@ -1768,6 +1770,7 @@ export class AnthropicProvider implements Provider {
1768
1770
  statusCode: error.status,
1769
1771
  reason: "context_overflow",
1770
1772
  cause: error,
1773
+ rawRequest: inspectableRequest,
1771
1774
  },
1772
1775
  );
1773
1776
  }
@@ -1779,7 +1782,11 @@ export class AnthropicProvider implements Provider {
1779
1782
  reason?: ProviderErrorReason;
1780
1783
  apiErrorType?: string;
1781
1784
  apiErrorCode?: string;
1785
+ rawRequest?: unknown;
1782
1786
  } = {};
1787
+ if (inspectableRequest !== undefined) {
1788
+ errorOptions.rawRequest = inspectableRequest;
1789
+ }
1783
1790
  if (retryAfterMs !== undefined) {
1784
1791
  errorOptions.retryAfterMs = retryAfterMs;
1785
1792
  }
@@ -1832,7 +1839,9 @@ export class AnthropicProvider implements Provider {
1832
1839
  }`,
1833
1840
  "anthropic",
1834
1841
  undefined,
1835
- abortReason ? { cause: error, abortReason } : { cause: error },
1842
+ abortReason
1843
+ ? { cause: error, abortReason, rawRequest: inspectableRequest }
1844
+ : { cause: error, rawRequest: inspectableRequest },
1836
1845
  );
1837
1846
  }
1838
1847
  }
@@ -45,6 +45,9 @@ export function retagDelegateError(
45
45
  maxTokens: error.maxTokens,
46
46
  statusCode: error.statusCode,
47
47
  cause: error,
48
+ ...(error.rawRequest !== undefined
49
+ ? { rawRequest: error.rawRequest }
50
+ : {}),
48
51
  });
49
52
  }
50
53
  if (error instanceof ProviderError && error.provider !== providerName) {
@@ -57,6 +60,9 @@ export function retagDelegateError(
57
60
  apiErrorCode: error.apiErrorCode,
58
61
  apiErrorParam: error.apiErrorParam,
59
62
  rawBody: error.rawBody,
63
+ ...(error.rawRequest !== undefined
64
+ ? { rawRequest: error.rawRequest }
65
+ : {}),
60
66
  });
61
67
  }
62
68
  throw error;
@@ -84,6 +84,17 @@ export const FETCH_PROVIDER_CATALOG: readonly FetchProviderCatalogEntry[] = [
84
84
  supportsApiBase: true,
85
85
  defaultApiBase: "https://api.fastcrw.com",
86
86
  },
87
+ {
88
+ id: "tinyfish",
89
+ displayName: "TinyFish",
90
+ kind: "byok",
91
+ apiKeyPrefix: "TinyFish API key...",
92
+ envVar: "TINYFISH_API_KEY",
93
+ secretKey: "tinyfish",
94
+ privacyPolicyUrl: "https://www.tinyfish.ai/privacy-policy",
95
+ supportsApiBase: true,
96
+ defaultApiBase: "https://api.fetch.tinyfish.ai",
97
+ },
87
98
  ];
88
99
 
89
100
  /** Provider ids accepted by the web-fetch config schema. */
@@ -439,6 +439,14 @@ export class GeminiProvider implements Provider {
439
439
  )
440
440
  : undefined;
441
441
 
442
+ let inspectableRequest:
443
+ | {
444
+ model: string;
445
+ contents: unknown;
446
+ config: genai.GenerateContentConfig;
447
+ }
448
+ | undefined;
449
+
442
450
  try {
443
451
  recordProviderRequestDiagnostics({ model_id: activeModel });
444
452
  const geminiContents = await this.toGeminiContents(messages, activeModel);
@@ -476,6 +484,12 @@ export class GeminiProvider implements Provider {
476
484
  geminiConfig.httpOptions = { headers: usageAttributionHeaders };
477
485
  }
478
486
 
487
+ inspectableRequest = {
488
+ model: activeModel,
489
+ contents: geminiContents,
490
+ config: stripGeminiHttpOptions(geminiConfig),
491
+ };
492
+
479
493
  // Accumulate from streaming chunks
480
494
  let fullText = "";
481
495
  const functionCalls: Array<{
@@ -583,11 +597,6 @@ export class GeminiProvider implements Provider {
583
597
  content.push(block);
584
598
  }
585
599
 
586
- const rawRequest = {
587
- model: activeModel,
588
- contents: geminiContents,
589
- config: stripGeminiHttpOptions(geminiConfig),
590
- };
591
600
  const rawResponse = {
592
601
  model: responseModel,
593
602
  text: fullText || null,
@@ -609,7 +618,7 @@ export class GeminiProvider implements Provider {
609
618
  ...(cachedTokens > 0 ? { cacheReadInputTokens: cachedTokens } : {}),
610
619
  },
611
620
  stopReason: finishReason,
612
- rawRequest,
621
+ rawRequest: inspectableRequest,
613
622
  rawResponse,
614
623
  };
615
624
  } catch (error) {
@@ -630,6 +639,7 @@ export class GeminiProvider implements Provider {
630
639
  maxTokens: overflow.maxTokens,
631
640
  statusCode: error.status,
632
641
  cause: error,
642
+ rawRequest: inspectableRequest,
633
643
  },
634
644
  );
635
645
  }
@@ -639,7 +649,12 @@ export class GeminiProvider implements Provider {
639
649
  error.status,
640
650
  // Skip reason on caller-abort: abortReason already carries the intent
641
651
  // and short-circuits classification/retry (mirrors the Anthropic client).
642
- abortReason ? { abortReason } : { reason: deriveGeminiReason(error) },
652
+ abortReason
653
+ ? { abortReason, rawRequest: inspectableRequest }
654
+ : {
655
+ reason: deriveGeminiReason(error),
656
+ rawRequest: inspectableRequest,
657
+ },
643
658
  );
644
659
  }
645
660
  throw new ProviderError(
@@ -648,7 +663,9 @@ export class GeminiProvider implements Provider {
648
663
  }`,
649
664
  "gemini",
650
665
  undefined,
651
- abortReason ? { cause: error, abortReason } : { cause: error },
666
+ abortReason
667
+ ? { cause: error, abortReason, rawRequest: inspectableRequest }
668
+ : { cause: error, rawRequest: inspectableRequest },
652
669
  );
653
670
  }
654
671
  }
@@ -206,7 +206,7 @@ const ADAPTER_FACTORIES: Record<string, AdapterFactory> = {
206
206
  streamTimeoutMs,
207
207
  ...(baseURL ? { baseURL } : {}),
208
208
  }),
209
- jev: ({ apiKey, model, streamTimeoutMs, baseURL }) =>
209
+ typesafe: ({ apiKey, model, streamTimeoutMs, baseURL }) =>
210
210
  new JevProvider(apiKey, model, {
211
211
  streamTimeoutMs,
212
212
  ...(baseURL ? { baseURL } : {}),
@@ -5,6 +5,7 @@ import {
5
5
  conversationToState,
6
6
  DEFAULT_JEV_MODEL,
7
7
  JevProvider,
8
+ noulFromAnswer,
8
9
  parseSystemOneOverride,
9
10
  validateJevApiKey,
10
11
  } from "./client.js";
@@ -66,6 +67,15 @@ describe("parseSystemOneOverride", () => {
66
67
  });
67
68
  });
68
69
 
70
+ describe("noulFromAnswer", () => {
71
+ test("reads a typed noul object or a bare number", () => {
72
+ expect(noulFromAnswer({ type: "noul", noul: 0.91 })).toBe(0.91);
73
+ expect(noulFromAnswer(0.4)).toBe(0.4);
74
+ expect(noulFromAnswer({ type: "choice", choice: "keep" })).toBeUndefined();
75
+ expect(noulFromAnswer(null)).toBeUndefined();
76
+ });
77
+ });
78
+
69
79
  describe("conversationToState", () => {
70
80
  test("flattens system prompt and turns into a string", () => {
71
81
  const state = conversationToState(
@@ -15,7 +15,7 @@ import type {
15
15
 
16
16
  const log = getLogger("jev-client");
17
17
 
18
- export const JEV_PROVIDER_ID = "jev";
18
+ export const JEV_PROVIDER_ID = "typesafe";
19
19
  export const DEFAULT_JEV_BASE_URL = "https://api.typesafe.ai";
20
20
  export const DEFAULT_JEV_MODEL = "jev-latest";
21
21
 
@@ -115,6 +115,21 @@ function isJevQuestions(value: unknown): value is JevQuestions {
115
115
  return entries.every(([, question]) => isJevQuestion(question));
116
116
  }
117
117
 
118
+ /** Calibrated P(yes) from a System One noul answer, or undefined when absent. */
119
+ export function noulFromAnswer(answer: unknown): number | undefined {
120
+ if (typeof answer === "number" && Number.isFinite(answer)) {
121
+ return answer;
122
+ }
123
+ if (
124
+ isRecord(answer) &&
125
+ typeof answer.noul === "number" &&
126
+ Number.isFinite(answer.noul)
127
+ ) {
128
+ return answer.noul;
129
+ }
130
+ return undefined;
131
+ }
132
+
118
133
  /**
119
134
  * If the last user message is a TypeSafe System One payload, use it as the
120
135
  * evaluation request. `state` is optional; callers fall back to the
@@ -411,7 +426,7 @@ export class JevProvider implements Provider {
411
426
  extractErrorMessage(bodyText, response.status),
412
427
  JEV_PROVIDER_ID,
413
428
  response.status,
414
- { reason, rawBody: bodyText },
429
+ { reason, rawBody: bodyText, rawRequest: request },
415
430
  );
416
431
  }
417
432
  let parsed: unknown;
@@ -422,7 +437,7 @@ export class JevProvider implements Provider {
422
437
  "TypeSafe returned a non-JSON response.",
423
438
  JEV_PROVIDER_ID,
424
439
  response.status,
425
- { reason: "bad_request", rawBody: bodyText },
440
+ { reason: "bad_request", rawBody: bodyText, rawRequest: request },
426
441
  );
427
442
  }
428
443
  if (!isRecord(parsed) || !isRecord(parsed.answers)) {
@@ -430,7 +445,7 @@ export class JevProvider implements Provider {
430
445
  "TypeSafe returned a response without answers.",
431
446
  JEV_PROVIDER_ID,
432
447
  response.status,
433
- { reason: "bad_request", rawBody: bodyText },
448
+ { reason: "bad_request", rawBody: bodyText, rawRequest: request },
434
449
  );
435
450
  }
436
451
  const usage = isRecord(parsed.usage) ? parsed.usage : undefined;
@@ -471,6 +486,7 @@ export class JevProvider implements Provider {
471
486
  cause: error,
472
487
  abortReason,
473
488
  reason: abortReason ? undefined : "network_error",
489
+ rawRequest: request,
474
490
  },
475
491
  );
476
492
  } finally {
@@ -77,7 +77,8 @@ export interface CatalogModel {
77
77
  * Whether the model produces free-form chat text. Omit (or true) for
78
78
  * ordinary chat models. False for structured-decision models that return
79
79
  * answers rather than generated text; those stay out of conversation
80
- * pickers and cannot be the conversation model.
80
+ * pickers and cannot be the conversation model. They can still back a
81
+ * saved profile and a call-site pin.
81
82
  */
82
83
  supportsText?: boolean;
83
84
  supportsEffort?: boolean;
@@ -2532,8 +2533,8 @@ const RAW_PROVIDER_CATALOG: ProviderCatalogEntry[] = [
2532
2533
  apiKeyPlaceholder: "Your Poolside API key",
2533
2534
  },
2534
2535
  {
2535
- id: "jev",
2536
- displayName: "Jev",
2536
+ id: "typesafe",
2537
+ displayName: "TypeSafe",
2537
2538
  subtitle:
2538
2539
  "TypeSafe System One decision model. Returns structured answers, not generated text. Requires a TypeSafe API key.",
2539
2540
  setupMode: "api-key",
@@ -1,6 +1,9 @@
1
1
  import { describe, expect, test } from "bun:test";
2
2
 
3
- import type { SendMessageOptions } from "../../types.js";
3
+ import OpenAI from "openai";
4
+
5
+ import { ProviderError } from "../../../util/errors.js";
6
+ import { ContextOverflowError, type SendMessageOptions } from "../../types.js";
4
7
  import { OpenAIChatCompletionsProvider } from "../chat-completions-provider.js";
5
8
 
6
9
  class ExtraBodyProvider extends OpenAIChatCompletionsProvider {
@@ -48,4 +51,94 @@ describe("chat-completions extraBody", () => {
48
51
  expect(seenParams?.directions).toEqual({ companion: 0.4 });
49
52
  expect(seenOptions?.extraBody).toBeUndefined();
50
53
  });
54
+
55
+ test("attaches the SDK create params as ProviderError.rawRequest on 4xx", async () => {
56
+ const provider = new ExtraBodyProvider("test-key", "qwen/qwen3-8b", {
57
+ providerName: "vellum",
58
+ providerLabel: "Vellum",
59
+ });
60
+ (
61
+ provider as unknown as {
62
+ client: {
63
+ chat: {
64
+ completions: {
65
+ create: () => Promise<AsyncIterable<unknown>>;
66
+ };
67
+ };
68
+ };
69
+ }
70
+ ).client.chat.completions.create = async () => {
71
+ throw new OpenAI.APIError(
72
+ 400,
73
+ { detail: "directions are not loaded" },
74
+ undefined,
75
+ new Headers(),
76
+ );
77
+ };
78
+
79
+ try {
80
+ await provider.sendMessage([
81
+ { role: "user", content: [{ type: "text", text: "hi" }] },
82
+ ]);
83
+ throw new Error("expected ProviderError");
84
+ } catch (error) {
85
+ expect(error).toBeInstanceOf(ProviderError);
86
+ const rejected = error as ProviderError;
87
+ expect(rejected.provider).toBe("vellum");
88
+ expect(rejected.statusCode).toBe(400);
89
+ expect(rejected.rawRequest).toEqual(
90
+ expect.objectContaining({
91
+ model: "qwen/qwen3-8b",
92
+ directions: { companion: 0.4 },
93
+ }),
94
+ );
95
+ }
96
+ });
97
+
98
+ test("attaches the SDK create params as ContextOverflowError.rawRequest", async () => {
99
+ const provider = new ExtraBodyProvider("test-key", "qwen/qwen3-8b", {
100
+ providerName: "vellum",
101
+ providerLabel: "Vellum",
102
+ });
103
+ (
104
+ provider as unknown as {
105
+ client: {
106
+ chat: {
107
+ completions: {
108
+ create: () => Promise<AsyncIterable<unknown>>;
109
+ };
110
+ };
111
+ };
112
+ }
113
+ ).client.chat.completions.create = async () => {
114
+ throw new OpenAI.APIError(
115
+ 400,
116
+ {
117
+ message:
118
+ "This model's maximum context length is 128000 tokens. However, your messages resulted in 150000 tokens.",
119
+ type: "invalid_request_error",
120
+ code: "context_length_exceeded",
121
+ },
122
+ undefined,
123
+ new Headers(),
124
+ );
125
+ };
126
+
127
+ try {
128
+ await provider.sendMessage([
129
+ { role: "user", content: [{ type: "text", text: "hi" }] },
130
+ ]);
131
+ throw new Error("expected ContextOverflowError");
132
+ } catch (error) {
133
+ expect(error).toBeInstanceOf(ContextOverflowError);
134
+ const rejected = error as ContextOverflowError;
135
+ expect(rejected.provider).toBe("vellum");
136
+ expect(rejected.rawRequest).toEqual(
137
+ expect.objectContaining({
138
+ model: "qwen/qwen3-8b",
139
+ directions: { companion: 0.4 },
140
+ }),
141
+ );
142
+ }
143
+ });
51
144
  });
@@ -1,12 +1,14 @@
1
- import { describe, expect, test } from "bun:test";
1
+ import { beforeEach, describe, expect, test } from "bun:test";
2
2
 
3
3
  import OpenAI from "openai";
4
4
 
5
+ import { ProviderError } from "../../../util/errors.js";
5
6
  import { isPlaceholderSentinelText } from "../../placeholder-sentinels.js";
6
7
  import {
7
8
  EMPTY_ASSISTANT_TURN_PLACEHOLDER,
8
9
  OpenAIChatCompletionsProvider,
9
10
  type OpenAIChatCompletionsProviderOptions,
11
+ resetReasoningOptOutRejectersForTests,
10
12
  } from "../chat-completions-provider.js";
11
13
 
12
14
  type ReasoningDetail = {
@@ -777,6 +779,65 @@ function rejection(message: string, status = 400): Error {
777
779
  }
778
780
 
779
781
  describe("reasoning opt-out rejection fallback", () => {
782
+ beforeEach(() => {
783
+ resetReasoningOptOutRejectersForTests();
784
+ });
785
+
786
+ test("remembers a rejecting model and skips the opt-out on later requests", async () => {
787
+ const { provider, requests } = stubProviderWithErrors(
788
+ [rejection("reasoning_effort 'none' is not supported for this model")],
789
+ OK_CHUNKS,
790
+ );
791
+ const send = () =>
792
+ provider.sendMessage(
793
+ [{ role: "user", content: [{ type: "text", text: "hi" }] }],
794
+ { config: { effort: "none" } },
795
+ );
796
+
797
+ await send();
798
+ await send();
799
+
800
+ expect(requests).toHaveLength(3);
801
+ expect(
802
+ (requests[2] as { reasoning_effort?: string }).reasoning_effort,
803
+ ).toBeUndefined();
804
+ });
805
+
806
+ test("does not remember when success needed a further compat retry", async () => {
807
+ // The broad /reasoning/ match misreads the reasoning_content round-trip
808
+ // error as an opt-out rejection; the request only succeeds after the
809
+ // backfill retry, so the opt-out itself was never proven unsupported.
810
+ const { provider, requests } = stubProviderWithErrors(
811
+ [
812
+ rejection("reasoning_content must be passed back to the API"),
813
+ rejection("reasoning_content must be passed back to the API"),
814
+ ],
815
+ OK_CHUNKS,
816
+ );
817
+ const messages = [
818
+ {
819
+ role: "user" as const,
820
+ content: [{ type: "text" as const, text: "hi" }],
821
+ },
822
+ {
823
+ role: "assistant" as const,
824
+ content: [{ type: "text" as const, text: "hello" }],
825
+ },
826
+ {
827
+ role: "user" as const,
828
+ content: [{ type: "text" as const, text: "again" }],
829
+ },
830
+ ];
831
+
832
+ await provider.sendMessage(messages, { config: { effort: "none" } });
833
+ await provider.sendMessage(messages, { config: { effort: "none" } });
834
+
835
+ expect(requests).toHaveLength(4);
836
+ expect(
837
+ (requests[3] as { reasoning_effort?: string }).reasoning_effort,
838
+ ).toBe("none");
839
+ });
840
+
780
841
  test("retries once without reasoning params when a model rejects the explicit opt-out", async () => {
781
842
  const { provider, requests } = stubProviderWithErrors(
782
843
  [rejection("reasoning_effort 'none' is not supported for this model")],
@@ -928,12 +989,56 @@ describe("thinking-mode tool_choice rejection fallback", () => {
928
989
  expect(first.reasoning_effort).toBe("high");
929
990
  expect(second.tool_choice).toBeUndefined();
930
991
  expect(second.reasoning_effort).toBe("high");
992
+ expect(
993
+ (response.rawRequest as { tool_choice?: unknown }).tool_choice,
994
+ ).toBeUndefined();
931
995
  const text = response.content.find((b) => b.type === "text") as
932
996
  | { type: "text"; text: string }
933
997
  | undefined;
934
998
  expect(text?.text).toBe("ok");
935
999
  });
936
1000
 
1001
+ test("ProviderError.rawRequest is the post-retry params when the retried call fails", async () => {
1002
+ const { provider } = stubProviderWithErrors(
1003
+ [
1004
+ rejection("Thinking mode does not support this tool_choice"),
1005
+ Object.assign(
1006
+ new Error("Thinking mode does not support this tool_choice"),
1007
+ { status: 400 },
1008
+ ),
1009
+ ],
1010
+ OK_CHUNKS,
1011
+ );
1012
+
1013
+ try {
1014
+ await provider.sendMessage(
1015
+ [{ role: "user", content: [{ type: "text", text: "hi" }] }],
1016
+ {
1017
+ tools: [
1018
+ {
1019
+ name: "bash",
1020
+ description: "Run a shell command",
1021
+ input_schema: { type: "object", properties: {} },
1022
+ },
1023
+ ],
1024
+ config: { tool_choice: { type: "none" }, effort: "high" },
1025
+ },
1026
+ );
1027
+ throw new Error("expected ProviderError");
1028
+ } catch (error) {
1029
+ expect(error).toBeInstanceOf(ProviderError);
1030
+ const rejected = error as ProviderError;
1031
+ expect(
1032
+ (rejected.rawRequest as { tool_choice?: unknown } | undefined)
1033
+ ?.tool_choice,
1034
+ ).toBeUndefined();
1035
+ expect(
1036
+ (rejected.rawRequest as { reasoning_effort?: string } | undefined)
1037
+ ?.reasoning_effort,
1038
+ ).toBe("high");
1039
+ }
1040
+ });
1041
+
937
1042
  test("retries once when Kimi rejects a specified tool_choice in thinking mode", async () => {
938
1043
  const { provider, requests } = stubProviderWithErrors(
939
1044
  [
@@ -57,6 +57,10 @@ function stubProviderWithErrors(errors: unknown[]): {
57
57
  const CHAT_TEMPLATE_400 =
58
58
  "Failed to apply chat template: invalid operation: object is not callable (in chat:22)";
59
59
 
60
+ // Fireworks' renderer error for DeepSeek, verbatim.
61
+ const FIREWORKS_STR_JOIN_400 =
62
+ "sequence item 0: expected str instance, list found";
63
+
60
64
  function rejection(message: string, status = 400): Error {
61
65
  return Object.assign(new Error(message), { status });
62
66
  }
@@ -103,6 +107,26 @@ describe("chat-template rejection flatten fallback", () => {
103
107
  expect(text?.text).toBe("ok");
104
108
  });
105
109
 
110
+ test("retries with flattened content on Fireworks' str-join template error", async () => {
111
+ const { provider, requests } = stubProviderWithErrors([
112
+ rejection(FIREWORKS_STR_JOIN_400),
113
+ ]);
114
+
115
+ await provider.sendMessage([
116
+ {
117
+ role: "user",
118
+ content: [
119
+ { type: "text", text: "[Image: a cat]" },
120
+ { type: "text", text: "what is this?" },
121
+ ],
122
+ },
123
+ ]);
124
+
125
+ expect(requests).toHaveLength(2);
126
+ const retry = requests[1] as { messages: Array<{ content: unknown }> };
127
+ expect(retry.messages[0].content).toBe("[Image: a cat]\n\nwhat is this?");
128
+ });
129
+
106
130
  test("does not retry when a content-parts array carries media (never silently drop an image)", async () => {
107
131
  // GIVEN an endpoint whose chat template 400s on structured message content
108
132
  const { provider, requests } = stubProviderWithErrors([