@vellumai/assistant 0.10.2-dev.202606250318.5e7cfb0 → 0.10.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 (430) hide show
  1. package/bun.lock +0 -20
  2. package/docs/workspace-tools.md +33 -42
  3. package/eslint-rules/cli-no-daemon-internals.js +0 -6
  4. package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +0 -31
  5. package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +0 -44
  6. package/node_modules/@vellumai/gateway-client/src/index.ts +0 -14
  7. package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +0 -17
  8. package/node_modules/@vellumai/service-contracts/package.json +0 -1
  9. package/node_modules/@vellumai/service-contracts/src/index.ts +0 -1
  10. package/openapi.yaml +0 -155
  11. package/package.json +1 -4
  12. package/scripts/test.sh +15 -36
  13. package/src/__tests__/actor-token-service.test.ts +14 -36
  14. package/src/__tests__/agent-loop-override-profile.test.ts +0 -1
  15. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +0 -2
  16. package/src/__tests__/agent-wake-override-profile.test.ts +0 -2
  17. package/src/__tests__/annotate-activity-metadata.test.ts +0 -2
  18. package/src/__tests__/annotate-risk-options.test.ts +0 -2
  19. package/src/__tests__/approval-cascade.test.ts +0 -2
  20. package/src/__tests__/assistant-attachments.test.ts +0 -42
  21. package/src/__tests__/background-workers-disk-pressure.test.ts +0 -2
  22. package/src/__tests__/btw-routes.test.ts +0 -2
  23. package/src/__tests__/build-persisted-content.test.ts +0 -2
  24. package/src/__tests__/call-controller.test.ts +0 -19
  25. package/src/__tests__/channel-guardian.test.ts +58 -94
  26. package/src/__tests__/channel-reply-delivery.test.ts +0 -2
  27. package/src/__tests__/compaction-events.test.ts +0 -2
  28. package/src/__tests__/compaction.benchmark.test.ts +0 -2
  29. package/src/__tests__/compactor-call-site-logging.test.ts +0 -2
  30. package/src/__tests__/compactor-low-watermark-cut.test.ts +0 -2
  31. package/src/__tests__/compactor-preserved-tail-count.test.ts +0 -2
  32. package/src/__tests__/compactor-summary-call-truncation.test.ts +0 -2
  33. package/src/__tests__/compactor-web-search-strip.test.ts +0 -2
  34. package/src/__tests__/config-loader-backfill.test.ts +10 -123
  35. package/src/__tests__/config-schema.test.ts +0 -1
  36. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +29 -31
  37. package/src/__tests__/contacts-relay-reads.test.ts +15 -13
  38. package/src/__tests__/conversation-abort-tool-results.test.ts +0 -2
  39. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +0 -2
  40. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +0 -2
  41. package/src/__tests__/conversation-agent-loop-overflow.test.ts +0 -2
  42. package/src/__tests__/conversation-agent-loop.test.ts +0 -134
  43. package/src/__tests__/conversation-analysis-routes.test.ts +0 -2
  44. package/src/__tests__/conversation-app-control-lifecycle.test.ts +0 -2
  45. package/src/__tests__/conversation-confirmation-signals.test.ts +0 -2
  46. package/src/__tests__/conversation-history-web-search.test.ts +0 -2
  47. package/src/__tests__/conversation-load-history-repair.test.ts +0 -2
  48. package/src/__tests__/conversation-load-history-stripped.test.ts +0 -2
  49. package/src/__tests__/conversation-pairing.test.ts +0 -2
  50. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +0 -2
  51. package/src/__tests__/conversation-process-callsite.test.ts +0 -2
  52. package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -2
  53. package/src/__tests__/conversation-queue.test.ts +0 -91
  54. package/src/__tests__/conversation-routes-guardian-reply.test.ts +0 -14
  55. package/src/__tests__/conversation-routes-slash-commands.test.ts +0 -14
  56. package/src/__tests__/conversation-slash-queue.test.ts +0 -2
  57. package/src/__tests__/conversation-slash-unknown.test.ts +0 -2
  58. package/src/__tests__/conversation-speed-override.test.ts +0 -2
  59. package/src/__tests__/conversation-surfaces-task-progress.test.ts +0 -29
  60. package/src/__tests__/conversation-title-service.test.ts +0 -2
  61. package/src/__tests__/conversation-tool-setup-attribution.test.ts +0 -47
  62. package/src/__tests__/conversation-usage.test.ts +0 -2
  63. package/src/__tests__/conversation-workspace-cache-state.test.ts +0 -2
  64. package/src/__tests__/conversation-workspace-injection.test.ts +0 -2
  65. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -2
  66. package/src/__tests__/credential-security-invariants.test.ts +1 -1
  67. package/src/__tests__/db-migration-rollback.test.ts +171 -205
  68. package/src/__tests__/db-test-helpers.ts +4 -5
  69. package/src/__tests__/deterministic-verification-control-plane.test.ts +2 -4
  70. package/src/__tests__/disk-pressure-guard.test.ts +0 -41
  71. package/src/__tests__/dm-persistence.test.ts +0 -2
  72. package/src/__tests__/emit-signal-routing-intent.test.ts +5 -10
  73. package/src/__tests__/events-dev-bypass-actor.test.ts +1 -7
  74. package/src/__tests__/exploration-drift-hook.test.ts +2 -3
  75. package/src/__tests__/filing-service.test.ts +0 -2
  76. package/src/__tests__/guardian-binding-drift-heal.test.ts +10 -75
  77. package/src/__tests__/guardian-dispatch.test.ts +1 -95
  78. package/src/__tests__/guardian-outbound-http.test.ts +0 -13
  79. package/src/__tests__/heartbeat-disk-pressure.test.ts +0 -2
  80. package/src/__tests__/heartbeat-service.test.ts +0 -2
  81. package/src/__tests__/helpers/channel-test-adapter.ts +7 -1
  82. package/src/__tests__/host-app-control-routes.test.ts +30 -24
  83. package/src/__tests__/host-bash-routes.test.ts +41 -31
  84. package/src/__tests__/host-browser-routes.test.ts +32 -26
  85. package/src/__tests__/host-cu-routes-targeted.test.ts +33 -25
  86. package/src/__tests__/host-file-routes-targeted.test.ts +52 -40
  87. package/src/__tests__/host-transfer-routes-targeted.test.ts +43 -31
  88. package/src/__tests__/http-user-message-parity.test.ts +8 -290
  89. package/src/__tests__/inbound-invite-redemption.test.ts +0 -28
  90. package/src/__tests__/inbound-slack-persistence.test.ts +0 -2
  91. package/src/__tests__/invite-redemption-service.test.ts +0 -198
  92. package/src/__tests__/llm-context-normalization.test.ts +0 -105
  93. package/src/__tests__/llm-request-log-error-payload.test.ts +9 -71
  94. package/src/__tests__/llm-usage-store.test.ts +0 -25
  95. package/src/__tests__/mcp-health-check.test.ts +1 -2
  96. package/src/__tests__/media-stream-server-integration.test.ts +0 -127
  97. package/src/__tests__/memory-retrieval-hook.test.ts +0 -2
  98. package/src/__tests__/messaging-send-tool.test.ts +0 -2
  99. package/src/__tests__/migration-import-from-url.test.ts +2 -2
  100. package/src/__tests__/mtime-cache.test.ts +5 -146
  101. package/src/__tests__/native-web-search.test.ts +0 -2
  102. package/src/__tests__/non-member-access-request.test.ts +17 -189
  103. package/src/__tests__/notification-broadcaster.test.ts +0 -4
  104. package/src/__tests__/notification-decision-recipient-context.test.ts +32 -33
  105. package/src/__tests__/notification-deep-link.test.ts +0 -6
  106. package/src/__tests__/notification-guardian-path.test.ts +0 -19
  107. package/src/__tests__/openai-provider.test.ts +12 -22
  108. package/src/__tests__/openai-responses-provider.test.ts +2 -12
  109. package/src/__tests__/outbound-slack-persistence.test.ts +0 -2
  110. package/src/__tests__/pending-interactions-resolved-event.test.ts +4 -7
  111. package/src/__tests__/persistence-secret-redaction.test.ts +0 -2
  112. package/src/__tests__/plugin-bootstrap.test.ts +73 -3
  113. package/src/__tests__/plugin-route-contribution.test.ts +17 -4
  114. package/src/__tests__/plugin-tool-contribution.test.ts +18 -3
  115. package/src/__tests__/plugin-types.test.ts +2 -0
  116. package/src/__tests__/process-message-background-slack.test.ts +0 -2
  117. package/src/__tests__/process-message-display-content.test.ts +0 -2
  118. package/src/__tests__/provider-error-scenarios.test.ts +4 -5
  119. package/src/__tests__/provider-usage-tracking.test.ts +0 -39
  120. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +0 -2
  121. package/src/__tests__/registry.test.ts +1 -4
  122. package/src/__tests__/relay-server.test.ts +25 -694
  123. package/src/__tests__/runtime-attachment-metadata.test.ts +1 -0
  124. package/src/__tests__/secret-ingress-http.test.ts +0 -14
  125. package/src/__tests__/send-endpoint-busy.test.ts +8 -30
  126. package/src/__tests__/skills.test.ts +0 -44
  127. package/src/__tests__/slack-inbound-verification.test.ts +2 -47
  128. package/src/__tests__/stt-hints.test.ts +13 -44
  129. package/src/__tests__/subagent-detail.test.ts +0 -27
  130. package/src/__tests__/subagent-disposal.test.ts +0 -65
  131. package/src/__tests__/subagent-notify-parent.test.ts +0 -2
  132. package/src/__tests__/subagent-role-registry.test.ts +2 -7
  133. package/src/__tests__/subagent-spawn-tool-fork.test.ts +0 -2
  134. package/src/__tests__/subagent-tools.test.ts +0 -2
  135. package/src/__tests__/suggestion-routes.test.ts +0 -2
  136. package/src/__tests__/title-generate-hook.test.ts +0 -2
  137. package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -2
  138. package/src/__tests__/tool-executor.test.ts +11 -16
  139. package/src/__tests__/tool-preview-lifecycle.test.ts +0 -2
  140. package/src/__tests__/tool-result-metadata-plumbing.test.ts +0 -2
  141. package/src/__tests__/tool-start-timestamp.test.ts +0 -2
  142. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +10 -10
  143. package/src/__tests__/twilio-routes.test.ts +0 -96
  144. package/src/__tests__/ui-file-upload-surface.test.ts +0 -86
  145. package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
  146. package/src/__tests__/voice-invite-redemption.test.ts +0 -33
  147. package/src/__tests__/web-search-backend-failure.test.ts +0 -2
  148. package/src/__tests__/workspace-migration-remove-hooks.test.ts +35 -14
  149. package/src/__tests__/workspace-tool-loader.test.ts +2 -195
  150. package/src/__tests__/workspace-tools-watcher-flag.test.ts +70 -0
  151. package/src/agent/loop.ts +0 -56
  152. package/src/api/index.ts +1 -19
  153. package/src/api/responses/llm-request-log-entry.ts +0 -29
  154. package/src/api/responses/subagent-detail.ts +0 -17
  155. package/src/api/surfaces.ts +3 -39
  156. package/src/approvals/guardian-request-resolvers.ts +11 -1
  157. package/src/calls/__tests__/relay-setup-router.test.ts +4 -262
  158. package/src/calls/call-domain.ts +3 -3
  159. package/src/calls/guardian-dispatch.ts +8 -10
  160. package/src/calls/inbound-trust-reader.ts +1 -17
  161. package/src/calls/media-stream-server.ts +0 -21
  162. package/src/calls/relay-server.ts +50 -167
  163. package/src/calls/relay-setup-router.ts +7 -37
  164. package/src/calls/relay-verification.ts +4 -4
  165. package/src/calls/stt-hints.ts +12 -9
  166. package/src/calls/twilio-routes.ts +4 -14
  167. package/src/channels/types.ts +20 -10
  168. package/src/cli/commands/__tests__/cache.test.ts +1 -8
  169. package/src/cli/commands/cache.ts +181 -194
  170. package/src/cli/commands/db/__tests__/repair.test.ts +5 -6
  171. package/src/cli/commands/db/status.ts +1 -37
  172. package/src/cli/commands/mcp.ts +218 -252
  173. package/src/cli/commands/memory/index.ts +0 -2
  174. package/src/cli/commands/plugins.ts +3 -75
  175. package/src/cli/lib/__tests__/install-from-github.test.ts +0 -102
  176. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +1 -160
  177. package/src/cli/lib/list-installed-plugins.ts +1 -179
  178. package/src/config/__tests__/sync-gated-profiles.test.ts +3 -11
  179. package/src/config/bundled-skills/contacts/tools/contact-merge.ts +17 -27
  180. package/src/config/bundled-skills/contacts/tools/contact-search.ts +3 -13
  181. package/src/config/bundled-skills/subagent/SKILL.md +1 -1
  182. package/src/config/bundled-skills/subagent/TOOLS.json +1 -1
  183. package/src/config/feature-flag-registry.json +13 -5
  184. package/src/config/loader.ts +5 -38
  185. package/src/config/schemas/__tests__/memory-v3.test.ts +0 -1
  186. package/src/config/schemas/memory-lifecycle.ts +0 -12
  187. package/src/config/schemas/memory-v3.ts +0 -7
  188. package/src/config/schemas/memory.ts +0 -4
  189. package/src/config/schemas/timeouts.ts +0 -8
  190. package/src/config/seed-inference-profiles.ts +11 -21
  191. package/src/config/skills.ts +5 -27
  192. package/src/config/sync-gated-profiles.ts +13 -12
  193. package/src/contacts/contacts-write.ts +0 -3
  194. package/src/daemon/assistant-attachments.ts +4 -27
  195. package/src/daemon/conversation-agent-loop.ts +0 -28
  196. package/src/daemon/conversation-process.ts +16 -35
  197. package/src/daemon/conversation-surfaces.ts +38 -111
  198. package/src/daemon/conversation-tool-setup.ts +16 -50
  199. package/src/daemon/conversation.ts +1 -13
  200. package/src/daemon/disk-pressure-guard.ts +2 -12
  201. package/src/daemon/event-loop-watchdog.ts +1 -28
  202. package/src/daemon/external-plugins-bootstrap.ts +34 -4
  203. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -25
  204. package/src/daemon/handlers/config-a2a.ts +14 -6
  205. package/src/daemon/handlers/config-channels.ts +22 -78
  206. package/src/daemon/handlers/conversations.ts +0 -77
  207. package/src/daemon/lifecycle.ts +0 -4
  208. package/src/daemon/mcp-reload-service.ts +0 -10
  209. package/src/daemon/memory-v2-startup.test.ts +0 -72
  210. package/src/daemon/memory-v2-startup.ts +19 -87
  211. package/src/daemon/message-types/conversations.ts +0 -2
  212. package/src/daemon/message-types/surfaces.ts +12 -12
  213. package/src/daemon/server.ts +4 -0
  214. package/src/daemon/shutdown-handlers.ts +0 -20
  215. package/src/daemon/tool-setup-types.ts +0 -9
  216. package/src/daemon/workspace-tools-watcher.ts +328 -0
  217. package/src/ipc/__tests__/clients-list-ipc.test.ts +1 -1
  218. package/src/ipc/assistant-server.ts +2 -2
  219. package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +0 -1
  220. package/src/mcp/client.ts +1 -15
  221. package/src/mcp/mcp-auth-orchestrator.ts +1 -6
  222. package/src/mcp/mcp-oauth-provider.ts +8 -19
  223. package/src/memory/__tests__/memory-retrospective-job.test.ts +0 -8
  224. package/src/memory/conversation-crud.ts +0 -38
  225. package/src/memory/db-connection.ts +3 -22
  226. package/src/memory/db-init.ts +502 -36
  227. package/src/memory/db-singleton.ts +4 -6
  228. package/src/memory/jobs-worker.ts +0 -58
  229. package/src/memory/llm-request-log-store.ts +1 -26
  230. package/src/memory/llm-usage-store.ts +20 -48
  231. package/src/memory/memory-retrospective-job.ts +8 -9
  232. package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +56 -130
  233. package/src/memory/migrations/__tests__/run-migrations.test.ts +2 -2
  234. package/src/memory/migrations/registry.ts +573 -0
  235. package/src/memory/migrations/run-migrations.ts +6 -90
  236. package/src/memory/migrations/validate-migration-state.ts +66 -101
  237. package/src/memory/schema/conversations.ts +0 -9
  238. package/src/memory/schema/infrastructure.ts +0 -20
  239. package/src/memory/v2/__tests__/cli-command-store.test.ts +0 -25
  240. package/src/memory/v2/__tests__/skill-store.test.ts +0 -80
  241. package/src/memory/v2/cli-command-store.ts +38 -75
  242. package/src/memory/v2/prompts/consolidation.ts +82 -13
  243. package/src/memory/v2/prompts/router.ts +93 -21
  244. package/src/memory/v2/skill-store.ts +31 -68
  245. package/src/notifications/__tests__/broadcaster.test.ts +8 -16
  246. package/src/notifications/__tests__/decision-engine.test.ts +9 -78
  247. package/src/notifications/broadcaster.ts +1 -8
  248. package/src/notifications/decision-engine.ts +7 -15
  249. package/src/notifications/destination-resolver.ts +24 -68
  250. package/src/notifications/emit-signal.ts +14 -39
  251. package/src/permissions/question-prompter.test.ts +1 -1
  252. package/src/permissions/question-prompter.ts +4 -7
  253. package/src/plugin-api/index.ts +6 -6
  254. package/src/plugin-api/types.ts +5 -3
  255. package/src/plugin-api/vision-support.test.ts +4 -28
  256. package/src/plugin-api/vision-support.ts +31 -66
  257. package/src/plugins/defaults/advisor/__tests__/consult.test.ts +0 -161
  258. package/src/plugins/defaults/advisor/consult.ts +6 -110
  259. package/src/plugins/defaults/advisor/steering.ts +2 -14
  260. package/src/plugins/defaults/advisor/tools/advisor.ts +5 -32
  261. package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +1 -2
  262. package/src/plugins/defaults/image-fallback/__tests__/image-fallback.test.ts +7 -47
  263. package/src/plugins/defaults/image-fallback/hooks/post-tool-use.ts +11 -10
  264. package/src/plugins/defaults/image-fallback/hooks/user-prompt-submit.ts +20 -12
  265. package/src/plugins/defaults/image-fallback/src/caption-blocks.ts +11 -42
  266. package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +3 -33
  267. package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +4 -48
  268. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +8 -4
  269. package/src/plugins/defaults/memory-v3-shadow/injector.ts +15 -43
  270. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +2 -11
  271. package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +13 -77
  272. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +11 -12
  273. package/src/plugins/mtime-cache.ts +291 -76
  274. package/src/plugins/pipeline.ts +13 -111
  275. package/src/plugins/types.ts +2 -0
  276. package/src/providers/anthropic/client.ts +0 -5
  277. package/src/providers/call-site-routing.ts +0 -4
  278. package/src/providers/model-catalog.ts +0 -16
  279. package/src/providers/openai/__tests__/api-error-detail.test.ts +120 -0
  280. package/src/providers/openai/chat-completions-provider.ts +83 -37
  281. package/src/providers/openai/responses-provider.ts +46 -50
  282. package/src/providers/openrouter/client.ts +0 -5
  283. package/src/providers/provider-send-message.ts +0 -4
  284. package/src/providers/ratelimit.ts +0 -4
  285. package/src/providers/retry.ts +0 -4
  286. package/src/providers/types.ts +0 -9
  287. package/src/providers/usage-tracking.ts +0 -4
  288. package/src/runtime/__tests__/trust-verdict-consumer.test.ts +3 -335
  289. package/src/runtime/access-request-helper.ts +39 -19
  290. package/src/runtime/actor-trust-resolver.ts +2 -2
  291. package/src/runtime/assistant-event-hub.ts +1 -1
  292. package/src/runtime/assistant-stream-state.ts +2 -9
  293. package/src/runtime/auth/require-bound-guardian.ts +11 -21
  294. package/src/runtime/channel-verification-service.ts +31 -56
  295. package/src/runtime/confirmation-request-guardian-bridge.ts +3 -3
  296. package/src/runtime/guardian-vellum-migration.ts +7 -66
  297. package/src/runtime/invite-redemption-service.ts +187 -198
  298. package/src/runtime/local-actor-identity.ts +11 -76
  299. package/src/runtime/pending-interactions.ts +1 -11
  300. package/src/runtime/routes/__tests__/channel-verification-revoke.test.ts +5 -56
  301. package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
  302. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +0 -187
  303. package/src/runtime/routes/browser-routes.ts +1 -1
  304. package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +5 -13
  305. package/src/runtime/routes/channel-verification-routes.ts +3 -3
  306. package/src/runtime/routes/contact-routes.ts +32 -8
  307. package/src/runtime/routes/conversation-cli-routes.ts +5 -4
  308. package/src/runtime/routes/conversation-list-routes.ts +7 -4
  309. package/src/runtime/routes/conversation-query-routes.ts +0 -72
  310. package/src/runtime/routes/conversation-routes.ts +85 -84
  311. package/src/runtime/routes/events-routes.ts +2 -2
  312. package/src/runtime/routes/global-search-routes.ts +1 -3
  313. package/src/runtime/routes/guardian-action-routes.ts +5 -4
  314. package/src/runtime/routes/host-app-control-routes.ts +4 -5
  315. package/src/runtime/routes/host-bash-routes.ts +4 -5
  316. package/src/runtime/routes/host-browser-routes.ts +11 -9
  317. package/src/runtime/routes/host-cu-routes.ts +4 -5
  318. package/src/runtime/routes/host-file-routes.ts +4 -5
  319. package/src/runtime/routes/host-transfer-routes.ts +6 -6
  320. package/src/runtime/routes/http-adapter.ts +1 -1
  321. package/src/runtime/routes/identity-routes.ts +2 -3
  322. package/src/runtime/routes/inbound-message-handler.ts +5 -5
  323. package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +5 -97
  324. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +49 -61
  325. package/src/runtime/routes/inbound-stages/background-dispatch.ts +4 -16
  326. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +7 -7
  327. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +8 -21
  328. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +3 -14
  329. package/src/runtime/routes/index.ts +0 -2
  330. package/src/runtime/routes/llm-context-normalization.ts +0 -83
  331. package/src/runtime/routes/mcp-auth-routes.ts +19 -171
  332. package/src/runtime/routes/migration-rollback-routes.ts +3 -4
  333. package/src/runtime/routes/migration-routes.ts +1 -4
  334. package/src/runtime/routes/subagents-routes.ts +0 -5
  335. package/src/runtime/routes/surface-action-routes.ts +56 -42
  336. package/src/runtime/services/__tests__/conversation-serializer.test.ts +0 -1
  337. package/src/runtime/services/conversation-serializer.ts +9 -7
  338. package/src/runtime/tool-grant-request-helper.ts +3 -3
  339. package/src/runtime/trust-verdict-consumer.ts +9 -85
  340. package/src/runtime/verification-outbound-actions.ts +18 -18
  341. package/src/signals/user-message.ts +0 -16
  342. package/src/subagent/manager.ts +0 -9
  343. package/src/subagent/types.ts +3 -3
  344. package/src/telemetry/types.ts +1 -34
  345. package/src/telemetry/usage-telemetry-reporter.test.ts +2 -3
  346. package/src/telemetry/usage-telemetry-reporter.ts +3 -87
  347. package/src/tools/ask-question/ask-question-tool.test.ts +0 -29
  348. package/src/tools/ask-question/ask-question-tool.ts +0 -13
  349. package/src/tools/executor.ts +4 -4
  350. package/src/tools/registry.ts +0 -18
  351. package/src/tools/shared/filesystem/path-policy.ts +5 -12
  352. package/src/tools/tool-approval-handler.ts +1 -1
  353. package/src/tools/tool-defaults.ts +2 -9
  354. package/src/tools/tool-manifest.ts +0 -3
  355. package/src/tools/types.ts +2 -17
  356. package/src/tools/workspace-tools/loader.ts +244 -348
  357. package/src/util/errors.ts +1 -26
  358. package/src/util/platform.ts +0 -5
  359. package/src/workflows/library.test.ts +0 -140
  360. package/src/workflows/library.ts +28 -82
  361. package/src/workspace/migrations/017-seed-persona-dirs.ts +34 -3
  362. package/src/workspace/migrations/019-scope-journal-to-guardian.ts +24 -3
  363. package/src/workspace/migrations/048-remove-workspace-hooks.ts +66 -14
  364. package/src/workspace/migrations/registry.ts +0 -2
  365. package/node_modules/@vellumai/gateway-client/src/__tests__/guardian-delivery-contract.test.ts +0 -91
  366. package/node_modules/@vellumai/gateway-client/src/guardian-delivery-contract.ts +0 -48
  367. package/node_modules/@vellumai/service-contracts/src/__tests__/channels.test.ts +0 -28
  368. package/node_modules/@vellumai/service-contracts/src/channels.ts +0 -41
  369. package/src/__tests__/code-search-tool.test.ts +0 -585
  370. package/src/__tests__/guardian-expiry-notifier.test.ts +0 -282
  371. package/src/__tests__/mcp-config-secret-boundary.test.ts +0 -390
  372. package/src/__tests__/plugin-pipeline.test.ts +0 -96
  373. package/src/__tests__/sse-actor-principal-guardian-source.test.ts +0 -102
  374. package/src/__tests__/steer-on-enqueue-question.test.ts +0 -181
  375. package/src/__tests__/workspace-migration-111-prune-seeded-callsite-defaults.test.ts +0 -208
  376. package/src/agent/loop-exclusive-tool.test.ts +0 -150
  377. package/src/api/constants/sse-replay.ts +0 -41
  378. package/src/api/events/conversation-notice.ts +0 -26
  379. package/src/approvals/guardian-channel-delivery.ts +0 -30
  380. package/src/approvals/guardian-expiry-notifier.ts +0 -148
  381. package/src/cli/commands/memory/__tests__/worker.test.ts +0 -302
  382. package/src/cli/commands/memory/worker.ts +0 -175
  383. package/src/config/__tests__/loader-callsite-strip-fallback.test.ts +0 -143
  384. package/src/config/prune-seeded-callsite-defaults.ts +0 -110
  385. package/src/contacts/__tests__/contacts-write-revoke-relay.test.ts +0 -129
  386. package/src/contacts/__tests__/guardian-delivery-reader.test.ts +0 -312
  387. package/src/contacts/__tests__/member-write-relay.test.ts +0 -202
  388. package/src/contacts/guardian-delivery-reader.ts +0 -223
  389. package/src/contacts/member-write-relay.ts +0 -189
  390. package/src/daemon/conversation-notices.ts +0 -60
  391. package/src/daemon/handlers/__tests__/config-channels.test.ts +0 -225
  392. package/src/hooks/hook-loader.ts +0 -341
  393. package/src/mcp/mcp-header-store.ts +0 -134
  394. package/src/memory/__tests__/301-create-watchdog-events.test.ts +0 -110
  395. package/src/memory/__tests__/prompt-override.test.ts +0 -192
  396. package/src/memory/__tests__/watchdog-events-store.test.ts +0 -161
  397. package/src/memory/migrations/300-add-processing-started-at.ts +0 -30
  398. package/src/memory/migrations/301-create-watchdog-events.ts +0 -45
  399. package/src/memory/migrations/__tests__/209-strip-thinking-from-consolidated.test.ts +0 -224
  400. package/src/memory/prompt-override.ts +0 -129
  401. package/src/memory/steps.ts +0 -573
  402. package/src/memory/watchdog-events-store.ts +0 -87
  403. package/src/memory/worker-control.ts +0 -118
  404. package/src/memory/worker-process.ts +0 -72
  405. package/src/notifications/__tests__/connected-channels.test.ts +0 -114
  406. package/src/notifications/__tests__/destination-resolver.test.ts +0 -256
  407. package/src/onboarding/checkin-event.test.ts +0 -222
  408. package/src/onboarding/checkin-event.ts +0 -321
  409. package/src/onboarding/schedule-checkin.ts +0 -190
  410. package/src/plugins/defaults/advisor/__tests__/context-pack-gating.test.ts +0 -106
  411. package/src/plugins/defaults/advisor/__tests__/context-pack.test.ts +0 -60
  412. package/src/plugins/defaults/advisor/context-pack.ts +0 -288
  413. package/src/plugins/defaults/memory-v3-shadow/pool-select.test.ts +0 -146
  414. package/src/plugins/surface-import.ts +0 -121
  415. package/src/providers/openai/__tests__/api-error-normalization.test.ts +0 -321
  416. package/src/providers/openai/api-error-normalization.ts +0 -270
  417. package/src/runtime/__tests__/channel-verification-service.test.ts +0 -133
  418. package/src/runtime/__tests__/guardian-vellum-migration.test.ts +0 -181
  419. package/src/runtime/__tests__/is-guardian-bound-for-channel.test.ts +0 -66
  420. package/src/runtime/__tests__/local-principal-trust.test.ts +0 -164
  421. package/src/runtime/anchored-guardian.test.ts +0 -156
  422. package/src/runtime/anchored-guardian.ts +0 -135
  423. package/src/runtime/auth/__tests__/require-bound-guardian.test.ts +0 -99
  424. package/src/runtime/local-principal-trust.ts +0 -52
  425. package/src/runtime/routes/__tests__/contact-routes.test.ts +0 -212
  426. package/src/runtime/routes/__tests__/global-search-routes.test.ts +0 -93
  427. package/src/runtime/routes/onboarding-checkin-routes.ts +0 -86
  428. package/src/tools/filesystem/search.ts +0 -543
  429. package/src/util/telemetry-db-path.ts +0 -24
  430. package/src/workspace/migrations/111-prune-seeded-callsite-defaults.ts +0 -134
@@ -1,146 +0,0 @@
1
- /**
2
- * Tests for `pool-select.ts` `selectPool` — focused on error SURFACING:
3
- * - a provider call that THROWS on every attempt surfaces the underlying
4
- * provider error (e.g. an upstream HTTP 4xx) in the thrown
5
- * `MemoryV3RetrievalUnavailableError` message, rather than the generic
6
- * "no usable selection" string that hid it;
7
- * - a 200 response carrying no usable `tool_use` still throws the generic
8
- * "no usable selection" message;
9
- * - happy paths preserved: explicit ids → selection, omitted ids → keepAll,
10
- * empty pool → [].
11
- *
12
- * `mock.module` is process-global and leaks into sibling files in a directory
13
- * run, so the provider-send-message stub DELEGATES to the real implementation
14
- * (keeping the real `extractToolUse`) unless this test is actively running
15
- * (`selectMockActive`) — mirrors `prune.test.ts` / `ever-injected-store.test.ts`.
16
- */
17
-
18
- import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
19
-
20
- import type {
21
- ContentBlock,
22
- Provider,
23
- ProviderResponse,
24
- } from "../../../providers/types.js";
25
- import type { MemoryRoutingTurn, Slug } from "./types.js";
26
-
27
- const realProviderSend = {
28
- ...(await import("../../../providers/provider-send-message.js")),
29
- };
30
-
31
- let selectMockActive = false;
32
- let sendMessageImpl: (() => Promise<ProviderResponse>) | null = null;
33
-
34
- const mockProvider = {
35
- name: "mock-memory-v3-selector",
36
- async sendMessage(): Promise<ProviderResponse> {
37
- if (!sendMessageImpl) throw new Error("sendMessageImpl not configured");
38
- return sendMessageImpl();
39
- },
40
- } as unknown as Provider;
41
-
42
- mock.module("../../../providers/provider-send-message.js", () => ({
43
- ...realProviderSend,
44
- getConfiguredProvider: (
45
- ...args: Parameters<typeof realProviderSend.getConfiguredProvider>
46
- ) =>
47
- selectMockActive
48
- ? Promise.resolve(mockProvider)
49
- : realProviderSend.getConfiguredProvider(...args),
50
- }));
51
-
52
- const { MemoryV3RetrievalUnavailableError, selectPool } =
53
- await import("./pool-select.js");
54
-
55
- function response(content: ContentBlock[]): ProviderResponse {
56
- return {
57
- content,
58
- model: "mock",
59
- usage: { inputTokens: 0, outputTokens: 0 },
60
- } as ProviderResponse;
61
- }
62
-
63
- const turn: MemoryRoutingTurn = {
64
- conversationId: "conv-1",
65
- turnNumber: 0,
66
- currentMessage: "echo something back",
67
- recentContext: "",
68
- };
69
-
70
- const pool = {
71
- stable: [],
72
- finder: [{ slug: "page-a" as Slug, descriptor: "a descriptor" }],
73
- };
74
-
75
- describe("selectPool", () => {
76
- beforeEach(() => {
77
- selectMockActive = true;
78
- sendMessageImpl = null;
79
- });
80
- afterAll(() => {
81
- selectMockActive = false;
82
- });
83
-
84
- test("a provider throw surfaces the underlying error in the thrown message", async () => {
85
- const upstream = "Together AI API error (400): 400 status code (no body)";
86
- sendMessageImpl = async () => {
87
- throw new Error(upstream);
88
- };
89
- let caught: unknown;
90
- try {
91
- await selectPool(pool, turn);
92
- } catch (err) {
93
- caught = err;
94
- }
95
- expect(caught).toBeInstanceOf(MemoryV3RetrievalUnavailableError);
96
- expect((caught as Error).message).toContain(
97
- "provider call failed after retries",
98
- );
99
- // The real upstream error is no longer hidden behind the generic message.
100
- expect((caught as Error).message).toContain(upstream);
101
- });
102
-
103
- test("a 200 with no usable tool_use throws the generic message", async () => {
104
- sendMessageImpl = async () =>
105
- response([{ type: "text", text: "I cannot call the tool." }]);
106
- let caught: unknown;
107
- try {
108
- await selectPool(pool, turn);
109
- } catch (err) {
110
- caught = err;
111
- }
112
- expect(caught).toBeInstanceOf(MemoryV3RetrievalUnavailableError);
113
- expect((caught as Error).message).toBe(
114
- "memory-v3 pool selector returned no usable selection after retries",
115
- );
116
- });
117
-
118
- test("explicit ids select the matching candidates", async () => {
119
- sendMessageImpl = async () =>
120
- response([
121
- {
122
- type: "tool_use",
123
- id: "call-1",
124
- name: "select_pages",
125
- input: { ids: [1] },
126
- },
127
- ]);
128
- expect(await selectPool(pool, turn)).toEqual([
129
- { slug: "page-a", pinned: false },
130
- ]);
131
- });
132
-
133
- test("omitted ids keep all candidates (recall-safe)", async () => {
134
- sendMessageImpl = async () =>
135
- response([
136
- { type: "tool_use", id: "call-1", name: "select_pages", input: {} },
137
- ]);
138
- expect(await selectPool(pool, turn)).toEqual([
139
- { slug: "page-a", pinned: false },
140
- ]);
141
- });
142
-
143
- test("an empty candidate pool returns no selections", async () => {
144
- expect(await selectPool({ stable: [], finder: [] }, turn)).toEqual([]);
145
- });
146
- });
@@ -1,121 +0,0 @@
1
- /**
2
- * Shared low-level import machinery for user-surface files (plugin tools,
3
- * plugin hooks, and standalone workspace hooks).
4
- *
5
- * Both the plugin/tool cache (`mtime-cache.ts`) and the hook cache
6
- * (`../hooks/hook-loader.ts`) re-import surface files keyed by mtime. This
7
- * module owns the pieces they share — mtime stat, the per-process import
8
- * timeout, and a timeout-guarded, concurrency-deduplicated dynamic import —
9
- * so neither surface duplicates the logic and the import-timeout knob is
10
- * configured in exactly one place.
11
- *
12
- * Kept dependency-light (only the logger and `importDefault`) so it can sit
13
- * below both caches without introducing an import cycle.
14
- */
15
-
16
- import { statSync } from "node:fs";
17
-
18
- import { getLogger } from "../util/logger.js";
19
- import { importDefault } from "./external-plugin-loader.js";
20
-
21
- const log = getLogger("surface-import");
22
-
23
- /**
24
- * Per-process upper bound on how long a single surface import may take.
25
- * Set once at boot by `populateCacheAtBoot`; read by every re-import. A
26
- * plugin/hook with a hanging top-level `await` is skipped rather than
27
- * blocking the daemon.
28
- */
29
- let importTimeoutMs = 10_000;
30
-
31
- /** Override the surface import timeout (called once at boot). */
32
- export function setSurfaceImportTimeout(ms: number): void {
33
- importTimeoutMs = ms;
34
- }
35
-
36
- /** Current surface import timeout in milliseconds. */
37
- export function getSurfaceImportTimeout(): number {
38
- return importTimeoutMs;
39
- }
40
-
41
- /**
42
- * Get the mtimeMs of a file, or 0 if the file doesn't exist or can't be
43
- * stat'd. Callers treat 0 as "absent" (cache-evict / skip).
44
- */
45
- export function getMtime(filePath: string): number {
46
- try {
47
- return statSync(filePath).mtimeMs;
48
- } catch {
49
- return 0;
50
- }
51
- }
52
-
53
- /**
54
- * In-flight import promises, keyed by file path. Prevents duplicate
55
- * `import()` calls when multiple readers request the same surface
56
- * concurrently.
57
- */
58
- const inflight = new Map<string, Promise<unknown>>();
59
-
60
- /**
61
- * Import a module's default export with a timeout. If the import doesn't
62
- * resolve within `timeoutMs`, logs a warning and returns `undefined` so a
63
- * hanging module doesn't block daemon startup indefinitely. Defaults to the
64
- * module-level {@link getSurfaceImportTimeout}.
65
- */
66
- export async function importWithTimeout<T>(
67
- filePath: string,
68
- timeoutMs: number = importTimeoutMs,
69
- ): Promise<T | undefined> {
70
- let timeoutHandle: ReturnType<typeof setTimeout> | undefined;
71
- try {
72
- const timeoutSentinel = Symbol("import-timeout");
73
- const importPromise = importWithDedup<T>(filePath);
74
- const timeoutPromise = new Promise<typeof timeoutSentinel>((resolve) => {
75
- timeoutHandle = setTimeout(() => resolve(timeoutSentinel), timeoutMs);
76
- });
77
- const result = await Promise.race([importPromise, timeoutPromise]);
78
- if (result === timeoutSentinel) {
79
- importPromise.catch(() => {
80
- /* swallow — late rejection from abandoned import */
81
- });
82
- log.warn(
83
- { filePath, timeoutMs },
84
- `Import timed out after ${timeoutMs}ms — skipping surface`,
85
- );
86
- return undefined;
87
- }
88
- return result as T;
89
- } finally {
90
- if (timeoutHandle !== undefined) clearTimeout(timeoutHandle);
91
- }
92
- }
93
-
94
- /**
95
- * Import a module's default export, deduplicating concurrent imports for
96
- * the same file path. This prevents two readers from triggering duplicate
97
- * `import()` calls when they request the same surface simultaneously.
98
- *
99
- * Note: Bun caches `import()` by resolved path within a process and does not
100
- * bust on query-string or hash changes, so the dedup is about avoiding
101
- * redundant async work, not cache-busting. A content edit to an existing
102
- * file is only picked up after a process restart (added and removed files
103
- * are picked up live, since discovery is by directory listing).
104
- */
105
- async function importWithDedup<T>(filePath: string): Promise<T> {
106
- let promise = inflight.get(filePath);
107
- if (promise === undefined) {
108
- promise = importDefault<T>(filePath);
109
- inflight.set(filePath, promise);
110
- }
111
- try {
112
- return (await promise) as T;
113
- } finally {
114
- inflight.delete(filePath);
115
- }
116
- }
117
-
118
- /** Clear in-flight import state. Test-only. */
119
- export function clearSurfaceImportInflight(): void {
120
- inflight.clear();
121
- }
@@ -1,321 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
-
3
- import type OpenAI from "openai";
4
-
5
- import {
6
- captureRawErrorBodyFetch,
7
- formatNormalizedOpenAIAPIError,
8
- normalizeOpenAIAPIError,
9
- } from "../api-error-normalization.js";
10
-
11
- /**
12
- * Stand-in for an `OpenAI.APIError`. The real SDK constructor stringifies the
13
- * body into `.message` and renders unparseable bodies as "(no body)" — exactly
14
- * the lossy behavior we work around by capturing the raw body separately — so
15
- * the tests pass the raw body explicitly the way the provider does.
16
- */
17
- function apiError(
18
- status: number,
19
- opts: { message?: string; sdkBody?: unknown; headers?: Headers } = {},
20
- ): InstanceType<typeof OpenAI.APIError> {
21
- return {
22
- status,
23
- message: opts.message ?? `${status} status code (no body)`,
24
- headers: opts.headers ?? new Headers(),
25
- error: opts.sdkBody,
26
- } as unknown as InstanceType<typeof OpenAI.APIError>;
27
- }
28
-
29
- describe("normalizeOpenAIAPIError", () => {
30
- test("surfaces Django {detail} bodies the SDK would drop", () => {
31
- const n = normalizeOpenAIAPIError(
32
- apiError(400),
33
- JSON.stringify({
34
- detail: "Model 'MiniMax-M3' is not yet supported on Vellum.",
35
- }),
36
- );
37
- expect(n.message).toBe(
38
- "Model 'MiniMax-M3' is not yet supported on Vellum.",
39
- );
40
- expect(formatNormalizedOpenAIAPIError("Together AI", 400, n)).toBe(
41
- "Together AI API error (400): Model 'MiniMax-M3' is not yet supported on Vellum.",
42
- );
43
- });
44
-
45
- test("extracts OpenAI-shaped error metadata", () => {
46
- const n = normalizeOpenAIAPIError(
47
- apiError(401),
48
- JSON.stringify({
49
- error: {
50
- message: "Invalid API key provided",
51
- code: "invalid_api_key",
52
- type: "invalid_request_error",
53
- param: "api_key",
54
- },
55
- }),
56
- );
57
- expect(n).toMatchObject({
58
- message: "Invalid API key provided",
59
- apiErrorCode: "invalid_api_key",
60
- apiErrorType: "invalid_request_error",
61
- apiErrorParam: "api_key",
62
- });
63
- expect(formatNormalizedOpenAIAPIError("OpenAI", 401, n)).toContain(
64
- "code=invalid_api_key; type=invalid_request_error; param=api_key",
65
- );
66
- });
67
-
68
- test("promotes OpenRouter metadata.raw over the generic wrapper message", () => {
69
- const n = normalizeOpenAIAPIError(
70
- apiError(400, {
71
- sdkBody: {
72
- error: {
73
- code: 400,
74
- message: "Provider returned error",
75
- metadata: {
76
- raw: "messages.4: tool_use_id must reference a prior tool_use block",
77
- provider_name: "Anthropic",
78
- },
79
- },
80
- },
81
- }),
82
- );
83
- expect(n.message).toBe(
84
- "messages.4: tool_use_id must reference a prior tool_use block",
85
- );
86
- expect(n.detail).toBe("provider=Anthropic");
87
- expect(n.apiErrorCode).toBe("400");
88
- });
89
-
90
- test("prefers the captured raw body over the SDK's parsed error", () => {
91
- // SDK collapsed the body; raw body still has the real detail.
92
- const n = normalizeOpenAIAPIError(
93
- apiError(400, { sdkBody: {} }),
94
- JSON.stringify({ detail: "managed proxy rejected model" }),
95
- );
96
- expect(n.message).toBe("managed proxy rejected model");
97
- });
98
-
99
- test("falls back to the SDK-parsed error when the captured JSON was truncated", () => {
100
- // encodeCapturedBody slices oversized bodies, which can leave an invalid
101
- // JSON prefix. Rather than render that fragment (and lose metadata), fall
102
- // back to the body the SDK already parsed from the full response.
103
- const truncated =
104
- '{"error":{"message":"too long","code":"context_length_exceeded","type":"invalid_request_error","param":"messag';
105
- const n = normalizeOpenAIAPIError(
106
- apiError(400, {
107
- sdkBody: {
108
- error: {
109
- message: "too long",
110
- code: "context_length_exceeded",
111
- type: "invalid_request_error",
112
- param: "messages",
113
- },
114
- },
115
- }),
116
- truncated,
117
- );
118
- expect(n.message).toBe("too long");
119
- expect(n.apiErrorCode).toBe("context_length_exceeded");
120
- expect(n.apiErrorType).toBe("invalid_request_error");
121
- expect(n.apiErrorParam).toBe("messages");
122
- });
123
-
124
- test("uses a plain-text raw body when JSON parsing fails", () => {
125
- const n = normalizeOpenAIAPIError(apiError(502), "upstream timeout");
126
- expect(n.message).toBe("upstream timeout");
127
- });
128
-
129
- test("reads the upstream request id from headers", () => {
130
- const n = normalizeOpenAIAPIError(
131
- apiError(400, { headers: new Headers({ "x-request-id": "req_abc123" }) }),
132
- JSON.stringify({ detail: "bad request" }),
133
- );
134
- expect(n.requestId).toBe("req_abc123");
135
- expect(formatNormalizedOpenAIAPIError("OpenAI", 400, n)).toContain(
136
- "request_id=req_abc123",
137
- );
138
- });
139
-
140
- test("falls back to x-openrouter-request-id", () => {
141
- const n = normalizeOpenAIAPIError(
142
- apiError(400, {
143
- headers: new Headers({ "x-openrouter-request-id": "gen-or-xyz" }),
144
- }),
145
- JSON.stringify({ detail: "bad request" }),
146
- );
147
- expect(n.requestId).toBe("gen-or-xyz");
148
- });
149
-
150
- test("truncates very long details with an ellipsis", () => {
151
- const n = normalizeOpenAIAPIError(
152
- apiError(400),
153
- JSON.stringify({ detail: "X".repeat(5000) }),
154
- );
155
- expect(n.message.length).toBeLessThanOrEqual(2001);
156
- expect(n.message.endsWith("…")).toBe(true);
157
- });
158
-
159
- test("degrades to the status-stripped SDK message when there is no body", () => {
160
- const n = normalizeOpenAIAPIError(
161
- apiError(500, { message: "500 Internal Server Error" }),
162
- );
163
- expect(n.message).toBe("Internal Server Error");
164
- });
165
-
166
- test("falls back to 'Request failed' for the SDK '(no body)' sentinel", () => {
167
- const n = normalizeOpenAIAPIError(
168
- apiError(500, { message: "500 status code (no body)" }),
169
- );
170
- expect(n.message).toBe("Request failed");
171
- });
172
-
173
- test("surfaces the verbatim captured body as rawBody for downstream persistence", () => {
174
- const raw = JSON.stringify({ detail: "model gone", extra: "kept" });
175
- const n = normalizeOpenAIAPIError(apiError(400), raw);
176
- // Extracted message is the clean detail...
177
- expect(n.message).toBe("model gone");
178
- // ...but the raw body is carried verbatim so the Raw tab can show it all.
179
- expect(n.rawBody).toBe(raw);
180
- });
181
-
182
- test("keeps sibling code/type/param when the body is {error: <string>}", () => {
183
- const n = normalizeOpenAIAPIError(
184
- apiError(400),
185
- JSON.stringify({
186
- error: "bad request",
187
- code: "context_length_exceeded",
188
- type: "invalid_request_error",
189
- }),
190
- );
191
- expect(n.message).toBe("bad request");
192
- expect(n.apiErrorCode).toBe("context_length_exceeded");
193
- expect(n.apiErrorType).toBe("invalid_request_error");
194
- });
195
- });
196
-
197
- describe("captureRawErrorBodyFetch", () => {
198
- // The fetch wrapper stashes the dropped body in a WeakMap keyed by the
199
- // response's headers object — which the SDK passes through to
200
- // APIError.headers — so normalize() recovers it with no explicit rawBody
201
- // argument and nothing is written to a (loggable) header.
202
- function fakeFetch(body: string, status: number): typeof globalThis.fetch {
203
- return (async () =>
204
- new Response(body, { status })) as unknown as typeof globalThis.fetch;
205
- }
206
-
207
- function errFor(res: Response, status: number) {
208
- return {
209
- status,
210
- message: `${status} status code (no body)`,
211
- headers: res.headers,
212
- error: undefined,
213
- } as unknown as InstanceType<typeof import("openai").APIError>;
214
- }
215
-
216
- test("recovers a non-2xx body into normalize() via the headers WeakMap", async () => {
217
- const original = globalThis.fetch;
218
- globalThis.fetch = fakeFetch(JSON.stringify({ detail: "model gone" }), 400);
219
- try {
220
- const res = await captureRawErrorBodyFetch("https://x/v1/chat", {});
221
- // No rawBody arg: normalize recovers the body keyed by res.headers alone.
222
- expect(normalizeOpenAIAPIError(errFor(res, 400)).message).toBe(
223
- "model gone",
224
- );
225
- } finally {
226
- globalThis.fetch = original;
227
- }
228
- });
229
-
230
- test("never writes the captured body to a header", async () => {
231
- const original = globalThis.fetch;
232
- globalThis.fetch = fakeFetch(JSON.stringify({ detail: "secret" }), 400);
233
- try {
234
- const res = await captureRawErrorBodyFetch("https://x/v1/chat", {});
235
- const headerNames = [...res.headers.keys()];
236
- expect(headerNames.some((h) => h.includes("vellum"))).toBe(false);
237
- } finally {
238
- globalThis.fetch = original;
239
- }
240
- });
241
-
242
- test("passes OK responses through untouched", async () => {
243
- const original = globalThis.fetch;
244
- globalThis.fetch = fakeFetch("ok", 200);
245
- try {
246
- const res = await captureRawErrorBodyFetch("https://x/v1/chat", {});
247
- expect(await res.text()).toBe("ok");
248
- } finally {
249
- globalThis.fetch = original;
250
- }
251
- });
252
-
253
- test("recovers (truncated) detail for oversized terminal errors", async () => {
254
- const big = "y".repeat(40_000);
255
- const original = globalThis.fetch;
256
- globalThis.fetch = fakeFetch(JSON.stringify({ detail: big }), 400);
257
- try {
258
- const res = await captureRawErrorBodyFetch("https://x/v1/chat", {});
259
- const n = normalizeOpenAIAPIError(errFor(res, 400));
260
- // The captured body is bounded (16 KB) and the message re-truncated to
261
- // MAX_DETAIL_CHARS, so an oversized error can't balloon either.
262
- expect(n.message).toContain("yyyy");
263
- expect(n.message.length).toBeLessThanOrEqual(2001);
264
- } finally {
265
- globalThis.fetch = original;
266
- }
267
- });
268
-
269
- test("does not drain SDK-retryable bodies (408/409/429/5xx)", async () => {
270
- const original = globalThis.fetch;
271
- // Mirror the full SDK retry predicate, not just 429/5xx.
272
- for (const status of [408, 409, 429, 500, 503]) {
273
- globalThis.fetch = fakeFetch(
274
- JSON.stringify({ detail: "retry me" }),
275
- status,
276
- );
277
- try {
278
- const res = await captureRawErrorBodyFetch("https://x/v1/chat", {});
279
- // Pass-through: nothing captured, and the body is still readable by the
280
- // SDK (we never consumed it).
281
- expect(normalizeOpenAIAPIError(errFor(res, status)).message).not.toBe(
282
- "retry me",
283
- );
284
- expect(res.status).toBe(status);
285
- expect(await res.text()).toContain("retry me");
286
- } finally {
287
- globalThis.fetch = original;
288
- }
289
- }
290
- });
291
-
292
- test("honors the x-should-retry header override", async () => {
293
- const original = globalThis.fetch;
294
- const withHeader = (
295
- status: number,
296
- value: string,
297
- ): typeof globalThis.fetch =>
298
- (async () =>
299
- new Response(JSON.stringify({ detail: "header says so" }), {
300
- status,
301
- headers: { "x-should-retry": value },
302
- })) as unknown as typeof globalThis.fetch;
303
- try {
304
- // x-should-retry:true on an otherwise-terminal 400 → SDK retries → skip.
305
- globalThis.fetch = withHeader(400, "true");
306
- let res = await captureRawErrorBodyFetch("https://x/v1/chat", {});
307
- expect(normalizeOpenAIAPIError(errFor(res, 400)).message).not.toBe(
308
- "header says so",
309
- );
310
-
311
- // x-should-retry:false on a 500 → SDK won't retry → capture it.
312
- globalThis.fetch = withHeader(500, "false");
313
- res = await captureRawErrorBodyFetch("https://x/v1/chat", {});
314
- expect(normalizeOpenAIAPIError(errFor(res, 500)).message).toBe(
315
- "header says so",
316
- );
317
- } finally {
318
- globalThis.fetch = original;
319
- }
320
- });
321
- });