@vellumai/assistant 0.11.1 → 0.11.2-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 (318) hide show
  1. package/Dockerfile +1 -3
  2. package/README.md +1 -1
  3. package/eslint.config.mjs +28 -8
  4. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
  5. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/stripe-currency.test.ts +37 -0
  6. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/stripe-currency.ts +55 -0
  7. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
  8. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/stripe-currency.test.ts +37 -0
  9. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/stripe-currency.ts +55 -0
  10. package/node_modules/@vellumai/service-contracts/package.json +1 -0
  11. package/node_modules/@vellumai/service-contracts/src/__tests__/stripe-currency.test.ts +37 -0
  12. package/node_modules/@vellumai/service-contracts/src/stripe-currency.ts +55 -0
  13. package/node_modules/@vellumai/slack-text/src/index.test.ts +48 -0
  14. package/node_modules/@vellumai/slack-text/src/index.ts +25 -6
  15. package/openapi.yaml +292 -5
  16. package/package.json +1 -1
  17. package/src/__tests__/agent-loop-resume-interrupted.test.ts +223 -0
  18. package/src/__tests__/byok-default-profile-ensure.test.ts +43 -0
  19. package/src/__tests__/cli-logger-boundary-guard.test.ts +87 -0
  20. package/src/__tests__/compaction-events.test.ts +139 -2
  21. package/src/__tests__/config-loader-backfill.test.ts +35 -3
  22. package/src/__tests__/config-schema.test.ts +1 -0
  23. package/src/__tests__/conversation-agent-loop-fatal-cleanup.test.ts +30 -0
  24. package/src/__tests__/conversation-agent-loop.test.ts +180 -7
  25. package/src/__tests__/conversation-error.test.ts +211 -4
  26. package/src/__tests__/conversation-options-turn-scoped-transport.test.ts +64 -0
  27. package/src/__tests__/conversation-queue.test.ts +126 -0
  28. package/src/__tests__/conversation-retry-route.test.ts +46 -7
  29. package/src/__tests__/conversation-slash.test.ts +4 -2
  30. package/src/__tests__/conversation-summarize-route.test.ts +7 -1
  31. package/src/__tests__/conversation-summarize-up-to.test.ts +71 -5
  32. package/src/__tests__/document-create-dedupe.test.ts +2 -1
  33. package/src/__tests__/document-find-replace.test.ts +2 -1
  34. package/src/__tests__/document-tool-security.test.ts +2 -1
  35. package/src/__tests__/document-update-default-surface.test.ts +2 -1
  36. package/src/__tests__/document-workspace-file.test.ts +467 -0
  37. package/src/__tests__/emit-signal-routing-intent.test.ts +290 -49
  38. package/src/__tests__/guardian-card-withdrawal.test.ts +91 -3
  39. package/src/__tests__/http-user-message-parity.test.ts +66 -0
  40. package/src/__tests__/list-messages-attachments.test.ts +87 -0
  41. package/src/__tests__/list-messages-provider-error.test.ts +143 -0
  42. package/src/__tests__/list-messages-system-card.test.ts +100 -0
  43. package/src/__tests__/llm-resolver.test.ts +19 -9
  44. package/src/__tests__/managed-profile-guard.test.ts +23 -0
  45. package/src/__tests__/notification-platform-adapter.test.ts +130 -2
  46. package/src/__tests__/notification-telegram-adapter.test.ts +6 -0
  47. package/src/__tests__/notification-vellum-adapter.test.ts +45 -0
  48. package/src/__tests__/plugin-api-model-profiles.test.ts +10 -1
  49. package/src/__tests__/plugin-import-boundary-guard.test.ts +3 -0
  50. package/src/__tests__/provider-error-scenarios.test.ts +140 -0
  51. package/src/__tests__/provider-send-message-override-profile.test.ts +95 -0
  52. package/src/__tests__/run-conversation-turn-persistence.test.ts +66 -3
  53. package/src/__tests__/scripted-turn-metadata-persistence.test.ts +209 -0
  54. package/src/__tests__/skills.test.ts +27 -0
  55. package/src/__tests__/slack-channels-routes.test.ts +0 -2
  56. package/src/__tests__/slack-share-routes.test.ts +0 -3
  57. package/src/__tests__/slack-users-routes.test.ts +0 -2
  58. package/src/__tests__/subagent-tools.test.ts +11 -0
  59. package/src/__tests__/tool-preview-lifecycle.test.ts +58 -0
  60. package/src/__tests__/tool-result-spool.test.ts +5 -4
  61. package/src/__tests__/turn-boundary-resolution.test.ts +53 -0
  62. package/src/__tests__/turn-events-store.test.ts +26 -0
  63. package/src/__tests__/ui-visual-surface.test.ts +695 -0
  64. package/src/__tests__/unified-turn-context-visible-app.test.ts +99 -0
  65. package/src/__tests__/visible-app-context.test.ts +189 -0
  66. package/src/__tests__/workspace-git-service.test.ts +173 -33
  67. package/src/__tests__/workspace-migration-137-repair-retired-fireworks-minimax-model-id.test.ts +157 -0
  68. package/src/__tests__/workspace-migration-138-backfill-home-feed-titles.test.ts +373 -0
  69. package/src/__tests__/workspace-migration-139-clear-renamed-cost-profile-label.test.ts +137 -0
  70. package/src/agent/loop.ts +36 -7
  71. package/src/api/events/context-window-usage.ts +31 -0
  72. package/src/api/events/notification-intent.ts +8 -0
  73. package/src/api/events/ui-surface-pending.ts +35 -0
  74. package/src/api/index.ts +14 -0
  75. package/src/api/responses/conversation-message.ts +25 -4
  76. package/src/api/surfaces.ts +90 -1
  77. package/src/approvals/guardian-card-withdrawal.ts +66 -31
  78. package/src/approvals/guardian-decision-primitive.ts +4 -0
  79. package/src/calls/__tests__/call-setup-router.test.ts +156 -26
  80. package/src/calls/__tests__/voice-session-bridge.test.ts +201 -12
  81. package/src/calls/call-setup-router.ts +80 -36
  82. package/src/calls/voice-session-bridge.ts +173 -26
  83. package/src/cli/commands/inference.help.ts +3 -3
  84. package/src/cli/commands/notifications.help.ts +3 -2
  85. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +42 -128
  86. package/src/cli/commands/platform/__tests__/credits.test.ts +9 -78
  87. package/src/cli/commands/platform/__tests__/helpers.ts +90 -0
  88. package/src/cli/commands/platform/__tests__/invoices.test.ts +238 -0
  89. package/src/cli/commands/platform/__tests__/plans.test.ts +9 -88
  90. package/src/cli/commands/platform/__tests__/status.test.ts +12 -87
  91. package/src/cli/commands/platform/__tests__/subscription.test.ts +9 -86
  92. package/src/cli/commands/platform/index.help.ts +92 -0
  93. package/src/cli/commands/platform/index.ts +7 -0
  94. package/src/cli/commands/platform/invoices.ts +132 -0
  95. package/src/cli/commands/usage.help.ts +1 -1
  96. package/src/cli/lib/list-installed-plugins.ts +2 -1
  97. package/src/config/__tests__/default-profile-catalog.test.ts +7 -10
  98. package/src/config/__tests__/deployment-context-defaults.test.ts +27 -5
  99. package/src/config/assistant-feature-flags.ts +7 -2
  100. package/src/config/bundled-skills/app-builder/SKILL.md +3 -2
  101. package/src/config/bundled-skills/subagent/SKILL.md +3 -1
  102. package/src/config/bundled-skills/subagent/TOOLS.json +3 -3
  103. package/src/config/bundled-skills/visualize/SKILL.md +163 -0
  104. package/src/config/call-site-defaults.ts +3 -2
  105. package/src/config/default-profile-catalog.ts +75 -70
  106. package/src/config/default-profile-names.ts +13 -28
  107. package/src/config/env-registry.ts +1 -0
  108. package/src/config/feature-flag-registry.json +16 -0
  109. package/src/config/llm-resolver.ts +21 -5
  110. package/src/config/loader.ts +18 -11
  111. package/src/config/schemas/llm.ts +1 -9
  112. package/src/config/schemas/memory-retrospective.ts +9 -0
  113. package/src/config/schemas/monitoring.ts +28 -2
  114. package/src/config/schemas/workspace-git.ts +15 -0
  115. package/src/config/seed-inference-profiles.ts +19 -5
  116. package/src/context/post-turn-tool-result-truncation.ts +2 -2
  117. package/src/conversations/__tests__/message-consolidation.test.ts +54 -0
  118. package/src/conversations/message-consolidation.ts +17 -15
  119. package/src/daemon/__tests__/turn-tail-assistant-reply-notify.test.ts +182 -0
  120. package/src/daemon/__tests__/turn-tail-deleted-conversation.test.ts +181 -0
  121. package/src/daemon/conversation-agent-loop-handlers.ts +90 -7
  122. package/src/daemon/conversation-agent-loop.ts +107 -22
  123. package/src/daemon/conversation-error.ts +169 -48
  124. package/src/daemon/conversation-messaging.ts +87 -4
  125. package/src/daemon/conversation-process.ts +57 -45
  126. package/src/daemon/conversation-runtime-assembly.ts +57 -0
  127. package/src/daemon/conversation-store.ts +36 -1
  128. package/src/daemon/conversation-surfaces.ts +68 -6
  129. package/src/daemon/conversation-turn-finalize.ts +76 -18
  130. package/src/daemon/conversation.ts +122 -27
  131. package/src/daemon/lifecycle.ts +9 -0
  132. package/src/daemon/message-types/conversations.ts +8 -0
  133. package/src/daemon/message-types/surfaces.ts +3 -0
  134. package/src/documents/document-store.ts +247 -10
  135. package/src/home/__tests__/feed-types.test.ts +43 -8
  136. package/src/home/__tests__/feed-writer.test.ts +59 -0
  137. package/src/home/feed-types.ts +34 -7
  138. package/src/home/feed-writer.ts +15 -6
  139. package/src/live-voice/__tests__/activity-label.test.ts +95 -0
  140. package/src/live-voice/__tests__/live-activity-reporter.test.ts +86 -5
  141. package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +310 -25
  142. package/src/live-voice/__tests__/live-voice-events.test.ts +4 -4
  143. package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +8 -4
  144. package/src/live-voice/__tests__/live-voice-vad.test.ts +9 -1
  145. package/src/live-voice/activity-label.ts +169 -0
  146. package/src/live-voice/live-activity-reporter.ts +35 -6
  147. package/src/live-voice/live-voice-session.ts +282 -41
  148. package/src/live-voice/protocol.ts +35 -0
  149. package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +84 -2
  150. package/src/messaging/providers/slack/adapter.ts +86 -19
  151. package/src/messaging/providers/slack/api.test.ts +85 -1
  152. package/src/messaging/providers/slack/api.ts +121 -288
  153. package/src/messaging/providers/slack/client.ts +20 -251
  154. package/src/messaging/providers/slack/send.test.ts +4 -9
  155. package/src/messaging/providers/slack/send.ts +1 -1
  156. package/src/messaging/providers/slack/types.ts +5 -0
  157. package/src/messaging/providers/slack/web-api-transport.test.ts +181 -0
  158. package/src/messaging/providers/slack/web-api-transport.ts +367 -0
  159. package/src/messaging/providers/slack/withdraw.ts +6 -14
  160. package/src/messaging/providers/telegram-bot/send.test.ts +31 -1
  161. package/src/messaging/providers/telegram-bot/send.ts +23 -2
  162. package/src/messaging/providers/telegram-bot/withdraw.test.ts +152 -0
  163. package/src/messaging/providers/telegram-bot/withdraw.ts +166 -0
  164. package/src/monitoring/__tests__/db-integrity-sample.test.ts +18 -4
  165. package/src/monitoring/__tests__/file-descriptors.test.ts +144 -0
  166. package/src/monitoring/file-descriptors.ts +262 -0
  167. package/src/monitoring/process-memory.ts +4 -11
  168. package/src/monitoring/resource-sampler.ts +5 -0
  169. package/src/monitoring/worker.ts +12 -0
  170. package/src/notifications/__tests__/assistant-reply-producer.test.ts +740 -0
  171. package/src/notifications/__tests__/broadcaster.test.ts +347 -7
  172. package/src/notifications/__tests__/copy-composer.test.ts +53 -3
  173. package/src/notifications/__tests__/decision-engine.test.ts +349 -29
  174. package/src/notifications/__tests__/deterministic-checks.test.ts +21 -0
  175. package/src/notifications/__tests__/edit-notification.test.ts +330 -0
  176. package/src/notifications/__tests__/guardian-delivery-recorder.test.ts +71 -0
  177. package/src/notifications/__tests__/home-feed-side-effect.test.ts +170 -12
  178. package/src/notifications/adapters/macos.ts +3 -0
  179. package/src/notifications/adapters/platform.ts +90 -14
  180. package/src/notifications/adapters/telegram.ts +6 -4
  181. package/src/notifications/assistant-reply-producer.ts +219 -0
  182. package/src/notifications/broadcaster.ts +523 -317
  183. package/src/notifications/copy-composer.ts +27 -6
  184. package/src/notifications/decision-engine.ts +154 -77
  185. package/src/notifications/deterministic-checks.ts +6 -7
  186. package/src/notifications/edit-notification.ts +8 -4
  187. package/src/notifications/emit-signal.ts +49 -16
  188. package/src/notifications/guardian-delivery-recorder.ts +8 -2
  189. package/src/notifications/home-feed-side-effect.ts +43 -6
  190. package/src/notifications/notification-utils.ts +39 -4
  191. package/src/notifications/signal.ts +10 -0
  192. package/src/notifications/types.ts +17 -0
  193. package/src/persistence/bookmark-crud.ts +18 -9
  194. package/src/persistence/conversation-attention-store.ts +26 -0
  195. package/src/persistence/conversation-crud.ts +79 -32
  196. package/src/persistence/conversation-queries.ts +30 -15
  197. package/src/persistence/conversation-title-service.ts +5 -170
  198. package/src/persistence/conversation-types.ts +192 -1
  199. package/src/persistence/db-init.ts +14 -1
  200. package/src/persistence/db-maintenance.ts +5 -4
  201. package/src/persistence/embeddings/__tests__/plugin-index-qdrant-init.test.ts +219 -0
  202. package/src/persistence/embeddings/__tests__/plugin-index.test.ts +22 -0
  203. package/src/persistence/embeddings/__tests__/worker-script-version.test.ts +76 -0
  204. package/src/persistence/embeddings/embedding-local.ts +2 -1
  205. package/src/persistence/embeddings/embedding-runtime-manager.ts +29 -5
  206. package/src/persistence/embeddings/plugin-index.ts +84 -12
  207. package/src/persistence/migrations/360-add-document-workspace-path.test.ts +110 -0
  208. package/src/persistence/migrations/360-add-document-workspace-path.ts +39 -0
  209. package/src/persistence/planner-statistics.ts +14 -0
  210. package/src/persistence/schema/documents.ts +26 -11
  211. package/src/persistence/steps.ts +2 -0
  212. package/src/platform/client.test.ts +172 -2
  213. package/src/platform/client.ts +178 -55
  214. package/src/plugin-api/conversation-turn.ts +13 -0
  215. package/src/plugin-api/model-profiles.test.ts +6 -2
  216. package/src/plugins/defaults/memory/__tests__/bookmark-crud.test.ts +34 -0
  217. package/src/plugins/defaults/memory/__tests__/conversation-queries.test.ts +22 -0
  218. package/src/plugins/defaults/memory/__tests__/jobs-store-enqueue-gate.test.ts +11 -0
  219. package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +199 -0
  220. package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +98 -1
  221. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +105 -1
  222. package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +24 -2
  223. package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +524 -0
  224. package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +1 -1
  225. package/src/plugins/defaults/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +9 -4
  226. package/src/plugins/defaults/memory/host-utils.ts +5 -0
  227. package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +105 -1
  228. package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +64 -5
  229. package/src/plugins/defaults/memory/memory-retrospective-job.ts +36 -1
  230. package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +12 -3
  231. package/src/plugins/defaults/memory/src/__tests__/memory-v2-simulate-route.test.ts +1 -0
  232. package/src/plugins/defaults/memory/substrate/__tests__/skill-store.test.ts +195 -1
  233. package/src/plugins/defaults/memory/substrate/consolidation-job.ts +1 -1
  234. package/src/plugins/defaults/memory/substrate/skill-store.ts +167 -29
  235. package/src/plugins/defaults/memory/v2/__tests__/injection.test.ts +131 -0
  236. package/src/plugins/defaults/memory/v2/__tests__/router.test.ts +1 -0
  237. package/src/plugins/defaults/memory/v2/activation-log-store.ts +4 -0
  238. package/src/plugins/defaults/memory/v2/injection.ts +48 -3
  239. package/src/plugins/defaults/memory/v2/rerank-local.ts +6 -2
  240. package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +1 -1
  241. package/src/plugins/defaults/platform-hosted/routes/reengage.ts +1 -1
  242. package/src/plugins/defaults/turn-context/injectors.ts +1 -0
  243. package/src/plugins/defaults/turn-context/unified-turn-context.ts +26 -0
  244. package/src/plugins/types.ts +17 -0
  245. package/src/prompts/templates/system-sections.ts +2 -2
  246. package/src/providers/__tests__/dispatch-connection-routing.test.ts +43 -2
  247. package/src/providers/__tests__/vellum-mismatch-routing.test.ts +8 -0
  248. package/src/providers/call-site-routing.ts +77 -14
  249. package/src/providers/connection-resolution.ts +42 -2
  250. package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +127 -1
  251. package/src/providers/inference/adapter-factory.ts +70 -12
  252. package/src/providers/model-catalog.ts +0 -11
  253. package/src/providers/model-intents.ts +11 -0
  254. package/src/providers/registry.ts +8 -0
  255. package/src/providers/retry.ts +85 -17
  256. package/src/providers/types.ts +8 -1
  257. package/src/runtime/__tests__/agent-wake.test.ts +181 -0
  258. package/src/runtime/agent-wake.ts +132 -9
  259. package/src/runtime/channel-approval-types.ts +25 -0
  260. package/src/runtime/routes/__tests__/connection-routes-vs-cli-parity.test.ts +2 -2
  261. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +11 -0
  262. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +121 -9
  263. package/src/runtime/routes/__tests__/platform-invoice-routes.test.ts +392 -0
  264. package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +7 -0
  265. package/src/runtime/routes/__tests__/workspace-commit-routes.test.ts +63 -0
  266. package/src/runtime/routes/consolidation-routes.ts +1 -1
  267. package/src/runtime/routes/conversation-management-routes.ts +18 -6
  268. package/src/runtime/routes/conversation-query-routes.ts +14 -23
  269. package/src/runtime/routes/conversation-routes.ts +113 -8
  270. package/src/runtime/routes/credential-routes.ts +1 -5
  271. package/src/runtime/routes/documents-routes.ts +207 -12
  272. package/src/runtime/routes/identity-routes.ts +3 -93
  273. package/src/runtime/routes/inbound-stages/admission-policy.test.ts +31 -1
  274. package/src/runtime/routes/inbound-stages/admission-policy.ts +18 -0
  275. package/src/runtime/routes/inference-provider-connection-routes.ts +52 -3
  276. package/src/runtime/routes/notification-routes.ts +4 -1
  277. package/src/runtime/routes/platform-routes.ts +238 -3
  278. package/src/runtime/routes/playground/guard.ts +1 -2
  279. package/src/runtime/routes/slack-channel-routes.ts +2 -4
  280. package/src/runtime/routes/workspace-commit-routes.ts +49 -2
  281. package/src/runtime/routes/workspace-routes.ts +11 -12
  282. package/src/runtime/routes/workspace-utils.ts +49 -2
  283. package/src/runtime/services/conversation-serializer.ts +2 -4
  284. package/src/subagent/__tests__/consult-context-gating.test.ts +98 -0
  285. package/src/subagent/__tests__/consult-context-skills.test.ts +82 -0
  286. package/src/subagent/__tests__/consult-context.test.ts +84 -0
  287. package/src/subagent/__tests__/consult-prompt.test.ts +36 -0
  288. package/src/subagent/consult-context.ts +410 -0
  289. package/src/subagent/consult-prompt.ts +38 -5
  290. package/src/subagent/manager.ts +9 -4
  291. package/src/subagent/types.ts +8 -0
  292. package/src/telemetry/telemetry-event-sources.ts +7 -0
  293. package/src/telemetry/telemetry-wire-source.json +1 -1
  294. package/src/telemetry/telemetry-wire.generated.ts +1 -0
  295. package/src/telemetry/turn-events-store.ts +30 -1
  296. package/src/telemetry/types.ts +24 -0
  297. package/src/telemetry/usage-telemetry-reporter.test.ts +48 -0
  298. package/src/tools/browser/pinned-tabs.ts +3 -1
  299. package/src/tools/subagent/spawn.ts +23 -1
  300. package/src/tools/terminal/safe-env.ts +1 -0
  301. package/src/tools/ui-surface/definitions.ts +39 -1
  302. package/src/tools/ui-surface/surface-shape-docs.ts +38 -4
  303. package/src/tools/ui-surface/visual-validation.ts +787 -0
  304. package/src/tools/workflows/run-workflow.test.ts +1 -0
  305. package/src/util/__tests__/short-title.test.ts +229 -0
  306. package/src/util/__tests__/worker-compute.test.ts +65 -0
  307. package/src/util/cgroup-cpu.ts +93 -0
  308. package/src/util/errors.ts +27 -0
  309. package/src/util/process-tree.ts +19 -0
  310. package/src/util/short-title.ts +189 -0
  311. package/src/util/worker-compute.ts +83 -0
  312. package/src/workspace/byok-default-profile-ensure.ts +25 -15
  313. package/src/workspace/git-service.ts +79 -30
  314. package/src/workspace/migrations/137-repair-retired-fireworks-minimax-model-id.ts +131 -0
  315. package/src/workspace/migrations/138-backfill-home-feed-titles.ts +179 -0
  316. package/src/workspace/migrations/139-clear-renamed-cost-profile-label.ts +92 -0
  317. package/src/workspace/migrations/registry.ts +6 -0
  318. package/src/plugins/defaults/memory/substrate/constants.ts +0 -8
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Tests for handleListMessages provider-error projection.
3
+ *
4
+ * When a turn dies on the provider-error path the agent loop persists the
5
+ * classified error text as a synthetic assistant row stamped with
6
+ * `metadata.messageKind: "provider_error"` (plus the classified
7
+ * code/category). The history projection must surface that marker as the
8
+ * wire `providerError` field, asserted against the FINAL response payload:
9
+ * the final projection rebuilds each message object field by field, so a
10
+ * field carried only on the intermediate object is silently dropped.
11
+ */
12
+
13
+ import { beforeEach, describe, expect, test } from "bun:test";
14
+
15
+ import { setConfig } from "./helpers/set-config.js";
16
+
17
+ // Keep the memory system off so addMessage skips indexing side effects.
18
+ setConfig("memory", { enabled: false });
19
+
20
+ import { ConversationMessageSchema } from "../api/responses/conversation-message.js";
21
+ import {
22
+ addMessage,
23
+ createConversation,
24
+ PROVIDER_ERROR_MESSAGE_KIND,
25
+ } from "../persistence/conversation-crud.js";
26
+ import { getDb } from "../persistence/db-connection.js";
27
+ import { initializeDb } from "../persistence/db-init.js";
28
+ import { handleListMessages } from "../runtime/routes/conversation-routes.js";
29
+
30
+ await initializeDb();
31
+
32
+ function resetTables() {
33
+ const db = getDb();
34
+ db.run("DELETE FROM message_attachments");
35
+ db.run("DELETE FROM attachments");
36
+ db.run("DELETE FROM messages");
37
+ db.run("DELETE FROM conversations");
38
+ }
39
+
40
+ interface ProjectedMessage {
41
+ role: string;
42
+ providerError?: { code?: string; category?: string };
43
+ }
44
+
45
+ describe("handleListMessages provider-error projection", () => {
46
+ beforeEach(resetTables);
47
+
48
+ test("surfaces providerError on tagged rows in the final payload", async () => {
49
+ const conv = createConversation();
50
+ await addMessage(
51
+ conv.id,
52
+ "user",
53
+ JSON.stringify([{ type: "text", text: "hello" }]),
54
+ );
55
+ // Mirror the agent loop's provider-error persist site: a synthetic
56
+ // assistant row carrying the classified error, tagged in metadata.
57
+ await addMessage(
58
+ conv.id,
59
+ "assistant",
60
+ JSON.stringify([
61
+ {
62
+ type: "text",
63
+ text: "I couldn't reply because you ran out of credits. Add credits in Settings → Billing and we can pick up where we left off.",
64
+ },
65
+ ]),
66
+ {
67
+ metadata: {
68
+ messageKind: PROVIDER_ERROR_MESSAGE_KIND,
69
+ providerErrorCode: "PROVIDER_BILLING",
70
+ providerErrorCategory: "credits_exhausted",
71
+ },
72
+ },
73
+ );
74
+
75
+ const response = handleListMessages({
76
+ queryParams: { conversationId: conv.id },
77
+ }) as { messages: ProjectedMessage[] };
78
+
79
+ // Every projected message validates against the wire schema.
80
+ for (const message of response.messages) {
81
+ expect(() => ConversationMessageSchema.parse(message)).not.toThrow();
82
+ }
83
+
84
+ expect(response.messages).toHaveLength(2);
85
+ const [userRow, errorRow] = response.messages;
86
+ expect(userRow?.providerError).toBeUndefined();
87
+ expect(errorRow?.role).toBe("assistant");
88
+ expect(errorRow?.providerError).toEqual({
89
+ code: "PROVIDER_BILLING",
90
+ category: "credits_exhausted",
91
+ });
92
+ });
93
+
94
+ test("coerces non-string code/category to absent fields", async () => {
95
+ const conv = createConversation();
96
+ await addMessage(
97
+ conv.id,
98
+ "assistant",
99
+ JSON.stringify([{ type: "text", text: "something went wrong" }]),
100
+ {
101
+ metadata: {
102
+ messageKind: PROVIDER_ERROR_MESSAGE_KIND,
103
+ providerErrorCode: 402,
104
+ providerErrorCategory: null,
105
+ },
106
+ },
107
+ );
108
+
109
+ const response = handleListMessages({
110
+ queryParams: { conversationId: conv.id },
111
+ }) as { messages: ProjectedMessage[] };
112
+
113
+ expect(response.messages).toHaveLength(1);
114
+ expect(response.messages[0]?.providerError).toEqual({});
115
+ expect(() =>
116
+ ConversationMessageSchema.parse(response.messages[0]),
117
+ ).not.toThrow();
118
+ });
119
+
120
+ test("omits providerError on untagged rows", async () => {
121
+ const conv = createConversation();
122
+ await addMessage(
123
+ conv.id,
124
+ "user",
125
+ JSON.stringify([{ type: "text", text: "hello" }]),
126
+ );
127
+ await addMessage(
128
+ conv.id,
129
+ "assistant",
130
+ JSON.stringify([{ type: "text", text: "hi there" }]),
131
+ );
132
+
133
+ const response = handleListMessages({
134
+ queryParams: { conversationId: conv.id },
135
+ }) as { messages: ProjectedMessage[] };
136
+
137
+ expect(response.messages).toHaveLength(2);
138
+ for (const message of response.messages) {
139
+ expect(message.providerError).toBeUndefined();
140
+ expect(() => ConversationMessageSchema.parse(message)).not.toThrow();
141
+ }
142
+ });
143
+ });
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Tests for handleListMessages system-card projection.
3
+ *
4
+ * Daemon-authored status cards (the /compact, /clean, and summarize-up-to
5
+ * result cards persisted via `persistCannedAssistantCard`) are stamped
6
+ * `messageKind: "system_card"`. The history projection must surface the
7
+ * `systemCard` flag so clients render them as standalone system notices
8
+ * instead of assistant-persona speech after a reload.
9
+ */
10
+
11
+ import { beforeEach, describe, expect, test } from "bun:test";
12
+
13
+ import { setConfig } from "./helpers/set-config.js";
14
+
15
+ // Keep the memory system off so addMessage skips indexing side effects.
16
+ setConfig("memory", { enabled: false });
17
+
18
+ import { ConversationMessageSchema } from "../api/responses/conversation-message.js";
19
+ import {
20
+ addMessage,
21
+ createConversation,
22
+ SYSTEM_CARD_MESSAGE_KIND,
23
+ } from "../persistence/conversation-crud.js";
24
+ import { getDb } from "../persistence/db-connection.js";
25
+ import { initializeDb } from "../persistence/db-init.js";
26
+ import { handleListMessages } from "../runtime/routes/conversation-routes.js";
27
+
28
+ await initializeDb();
29
+
30
+ function resetTables() {
31
+ const db = getDb();
32
+ db.run("DELETE FROM message_attachments");
33
+ db.run("DELETE FROM attachments");
34
+ db.run("DELETE FROM messages");
35
+ db.run("DELETE FROM conversations");
36
+ }
37
+
38
+ interface ProjectedMessage {
39
+ id: string;
40
+ role: string;
41
+ systemCard?: boolean;
42
+ }
43
+
44
+ describe("handleListMessages system-card projection", () => {
45
+ beforeEach(resetTables);
46
+
47
+ test("projects systemCard from the persisted card row metadata", async () => {
48
+ const conv = createConversation();
49
+ await addMessage(
50
+ conv.id,
51
+ "user",
52
+ JSON.stringify([{ type: "text", text: "/compact" }]),
53
+ );
54
+ // Mirror persistCannedAssistantCard: an assistant row stamped with the
55
+ // system-card marker.
56
+ const card = await addMessage(
57
+ conv.id,
58
+ "assistant",
59
+ JSON.stringify([{ type: "text", text: "Compacted 12 messages." }]),
60
+ { metadata: { messageKind: SYSTEM_CARD_MESSAGE_KIND } },
61
+ );
62
+
63
+ const response = handleListMessages({
64
+ queryParams: { conversationId: conv.id },
65
+ }) as { messages: ProjectedMessage[] };
66
+
67
+ // Every projected message validates against the wire schema.
68
+ for (const message of response.messages) {
69
+ expect(() => ConversationMessageSchema.parse(message)).not.toThrow();
70
+ }
71
+
72
+ const cardRow = response.messages.find((m) => m.id === card.id);
73
+ expect(cardRow).toBeDefined();
74
+ expect(cardRow?.systemCard).toBe(true);
75
+ });
76
+
77
+ test("omits systemCard on ordinary user and assistant rows", async () => {
78
+ const conv = createConversation();
79
+ await addMessage(
80
+ conv.id,
81
+ "user",
82
+ JSON.stringify([{ type: "text", text: "hello" }]),
83
+ );
84
+ await addMessage(
85
+ conv.id,
86
+ "assistant",
87
+ JSON.stringify([{ type: "text", text: "hi there" }]),
88
+ );
89
+
90
+ const response = handleListMessages({
91
+ queryParams: { conversationId: conv.id },
92
+ }) as { messages: ProjectedMessage[] };
93
+
94
+ expect(response.messages).toHaveLength(2);
95
+ for (const message of response.messages) {
96
+ expect(message.systemCard).toBeUndefined();
97
+ expect(() => ConversationMessageSchema.parse(message)).not.toThrow();
98
+ }
99
+ });
100
+ });
@@ -6,6 +6,7 @@ import { CODE_DEFAULT_PROFILE_ENTRIES } from "../config/default-profile-catalog.
6
6
  import {
7
7
  type ResolutionFallbackReason,
8
8
  resolveCallSiteConfig,
9
+ resolveCallSiteConfigWithProfile,
9
10
  resolveDefaultProfileKey,
10
11
  resolveEffectiveProfileKey,
11
12
  } from "../config/llm-resolver.js";
@@ -697,9 +698,7 @@ describe("resolveCallSiteConfig", () => {
697
698
  const resolved = resolveCallSiteConfig("memoryExtraction", llm);
698
699
  expect(resolved.provider).toBe("openai");
699
700
  expect(resolved.provider_connection).toBe("openai-personal");
700
- expect(resolved.model).toBe(
701
- resolveModelIntent("openai", "latency-optimized"),
702
- );
701
+ expect(resolved.model).toBe(resolveModelIntent("openai", "cost-optimized"));
703
702
  });
704
703
 
705
704
  test("BYOK full-workspace: every call site resolves through the default provider, never the managed connection", () => {
@@ -751,9 +750,7 @@ describe("resolveCallSiteConfig", () => {
751
750
 
752
751
  // Cost-optimized call sites resolve the intent through the BYOK provider.
753
752
  const costSite = resolveCallSiteConfig("heartbeatAgent", byokConfig);
754
- expect(costSite.model).toBe(
755
- resolveModelIntent("openai", "latency-optimized"),
756
- );
753
+ expect(costSite.model).toBe(resolveModelIntent("openai", "cost-optimized"));
757
754
 
758
755
  // mainAgent uses the user's active profile.
759
756
  const balancedSite = resolveCallSiteConfig("mainAgent", byokConfig);
@@ -770,9 +767,7 @@ describe("resolveCallSiteConfig", () => {
770
767
 
771
768
  const resolved = resolveCallSiteConfig("commitMessage", byokConfig);
772
769
  expect(resolved.provider).toBe("openai");
773
- expect(resolved.model).toBe(
774
- resolveModelIntent("openai", "latency-optimized"),
775
- );
770
+ expect(resolved.model).toBe(resolveModelIntent("openai", "cost-optimized"));
776
771
  expect(resolved.maxTokens).toBe(120);
777
772
  expect(resolved.effort).toBe("low");
778
773
  expect(resolved.thinking.enabled).toBe(false);
@@ -859,6 +854,21 @@ describe("mix profiles", () => {
859
854
  }
860
855
  });
861
856
 
857
+ test("config and profile attribution share one mix selection", () => {
858
+ const selectedArms: string[] = [];
859
+ const resolved = resolveCallSiteConfigWithProfile("mainAgent", mixLlm, {
860
+ onMixSelected: ({ chosenProfile }) => {
861
+ selectedArms.push(chosenProfile);
862
+ },
863
+ });
864
+
865
+ expect(resolved.profileName).toBe("ab");
866
+ expect(selectedArms).toHaveLength(1);
867
+ expect(resolved.config.model).toBe(
868
+ selectedArms[0] === "a" ? "model-a" : "model-b",
869
+ );
870
+ });
871
+
862
872
  test("all dereference spots in a turn agree for the same seed", () => {
863
873
  // mainAgent (mix as activeProfile) and a non-main call site resolving the
864
874
  // same mix as its call-site profile must pick the same arm when given the
@@ -263,6 +263,29 @@ describe("PUT /v1/config/llm/profiles/:name — managed profile guard", () => {
263
263
  expectNothingCommitted();
264
264
  });
265
265
 
266
+ test("PUT on the code-owned latency profile is rejected, status re-enable included", async () => {
267
+ // Speed fronts live voice, so it takes no writes at all: the re-enable
268
+ // escape hatch the other managed profiles keep would persist a stub that
269
+ // never governs what the name resolves to.
270
+ seedRawConfig({ llm: { profiles: {} } });
271
+
272
+ for (const body of [
273
+ { label: "Zippy" },
274
+ { status: "active" as const },
275
+ { status: null },
276
+ ]) {
277
+ await expect(
278
+ replaceRoute.handler({
279
+ pathParams: { name: "latency-optimized" },
280
+ body,
281
+ }),
282
+ ).rejects.toThrow(
283
+ 'Profile "latency-optimized" is code-owned and cannot be edited.',
284
+ );
285
+ }
286
+ expectNothingCommitted();
287
+ });
288
+
266
289
  test("PUT { status: null } on managed profile clears status (back to active-by-absence)", async () => {
267
290
  seedRawConfig({
268
291
  llm: {
@@ -33,11 +33,13 @@ interface FetchCall {
33
33
  path: string;
34
34
  method: string;
35
35
  body: Record<string, unknown>;
36
+ hasAbortSignal: boolean;
36
37
  }
37
38
 
38
39
  const fetchCalls: FetchCall[] = [];
39
40
  const fetchResponses: Array<{ ok: boolean; status: number; body?: string }> =
40
41
  [];
42
+ const fetchErrors: Error[] = [];
41
43
  let clientAvailable = true;
42
44
 
43
45
  mock.module("../platform/client.js", () => ({
@@ -52,7 +54,16 @@ mock.module("../platform/client.js", () => ({
52
54
  const body = init?.body
53
55
  ? (JSON.parse(init.body as string) as Record<string, unknown>)
54
56
  : {};
55
- fetchCalls.push({ path, method: init?.method ?? "GET", body });
57
+ fetchCalls.push({
58
+ path,
59
+ method: init?.method ?? "GET",
60
+ body,
61
+ hasAbortSignal: init?.signal instanceof AbortSignal,
62
+ });
63
+ const error = fetchErrors.shift();
64
+ if (error) {
65
+ throw error;
66
+ }
56
67
  const response = fetchResponses.shift() ?? {
57
68
  ok: true,
58
69
  status: 200,
@@ -62,6 +73,7 @@ mock.module("../platform/client.js", () => ({
62
73
  ok: response.ok,
63
74
  status: response.status,
64
75
  text: async () => response.body ?? "",
76
+ json: async () => JSON.parse(response.body ?? "") as unknown,
65
77
  };
66
78
  },
67
79
  };
@@ -82,6 +94,7 @@ function makePayload(
82
94
  ): ChannelDeliveryPayload {
83
95
  return {
84
96
  deliveryId: "delivery-uuid-1",
97
+ correlationId: "signal-1",
85
98
  sourceEventName: "schedule.notify",
86
99
  copy: { title: "Reminder", body: "Check the oven!" },
87
100
  deepLinkTarget: { type: "conversation", id: "conv-1" },
@@ -106,6 +119,7 @@ describe("PlatformPushAdapter", () => {
106
119
  beforeEach(() => {
107
120
  fetchCalls.length = 0;
108
121
  fetchResponses.length = 0;
122
+ fetchErrors.length = 0;
109
123
  clientAvailable = true;
110
124
  });
111
125
 
@@ -123,7 +137,7 @@ describe("PlatformPushAdapter", () => {
123
137
  const call = fetchCalls[0]!;
124
138
  expect(call.path).toBe("/v1/assistants/test-assistant-id/push/dispatch/");
125
139
  expect(call.method).toBe("POST");
126
- expect(call.body.delivery_id).toBe("delivery-uuid-1");
140
+ expect(call.body.delivery_id).toBe("signal-1");
127
141
  expect(call.body.source_event_name).toBe("schedule.notify");
128
142
  expect(call.body.title).toBe("Reminder");
129
143
  expect(call.body.body).toBe("Check the oven!");
@@ -202,6 +216,28 @@ describe("PlatformPushAdapter", () => {
202
216
  expect(fetchCalls).toHaveLength(4);
203
217
  });
204
218
 
219
+ test("bounds every attempt with an abort signal", async () => {
220
+ const adapter = new PlatformPushAdapter();
221
+ const result = await adapter.send(makePayload(), makeDestination());
222
+
223
+ expect(result.success).toBe(true);
224
+ expect(fetchCalls[0]?.hasAbortSignal).toBe(true);
225
+ });
226
+
227
+ test("retries attempts that abort on the per-attempt timeout", async () => {
228
+ fetchErrors.push(
229
+ new DOMException("The operation timed out.", "TimeoutError"),
230
+ );
231
+ fetchResponses.push({ ok: true, status: 200, body: '{"tokens_sent": 1}' });
232
+
233
+ const adapter = new PlatformPushAdapter();
234
+ const result = await adapter.send(makePayload(), makeDestination());
235
+
236
+ expect(result.success).toBe(true);
237
+ expect(result.remotePushAccepted).toBe(true);
238
+ expect(fetchCalls).toHaveLength(2);
239
+ });
240
+
205
241
  test("does not retry on 4xx responses", async () => {
206
242
  fetchResponses.push({ ok: false, status: 400, body: "bad request" });
207
243
  const adapter = new PlatformPushAdapter();
@@ -212,6 +248,98 @@ describe("PlatformPushAdapter", () => {
212
248
  expect(fetchCalls).toHaveLength(1);
213
249
  });
214
250
 
251
+ test("preserves an explicit empty platform list with legacy acceptance", async () => {
252
+ fetchResponses.push({
253
+ ok: true,
254
+ status: 200,
255
+ body: '{"tokens_sent":2,"accepted_platforms":[]}',
256
+ });
257
+ const adapter = new PlatformPushAdapter();
258
+ const result = await adapter.send(makePayload(), makeDestination());
259
+
260
+ expect(result.success).toBe(true);
261
+ expect(result.remotePushAccepted).toBe(true);
262
+ expect(result.remotePushPlatforms).toEqual([]);
263
+ });
264
+
265
+ test("preserves a successful provider from the final partial 503", async () => {
266
+ fetchResponses.push(
267
+ {
268
+ ok: false,
269
+ status: 503,
270
+ body: '{"accepted_platforms":["ios"]}',
271
+ },
272
+ { ok: false, status: 503, body: "{}" },
273
+ { ok: false, status: 503, body: "{}" },
274
+ {
275
+ ok: false,
276
+ status: 503,
277
+ body: '{"accepted_platforms":["android"]}',
278
+ },
279
+ );
280
+
281
+ const result = await new PlatformPushAdapter().send(
282
+ makePayload(),
283
+ makeDestination(),
284
+ );
285
+
286
+ expect(result.success).toBe(false);
287
+ expect(result.remotePushPlatforms).toEqual(["ios", "android"]);
288
+ });
289
+
290
+ test("reports remotePushAccepted: false on 202 skipped (flag off)", async () => {
291
+ fetchResponses.push({
292
+ ok: true,
293
+ status: 202,
294
+ body: '{"skipped": "flag_off"}',
295
+ });
296
+ const adapter = new PlatformPushAdapter();
297
+ const result = await adapter.send(makePayload(), makeDestination());
298
+
299
+ expect(result.success).toBe(true);
300
+ expect(result.remotePushAccepted).toBe(false);
301
+ });
302
+
303
+ test("reports remotePushAccepted: false on 200 with tokens_sent 0", async () => {
304
+ fetchResponses.push({
305
+ ok: true,
306
+ status: 200,
307
+ body: '{"idempotent": true, "tokens_sent": 0}',
308
+ });
309
+ const adapter = new PlatformPushAdapter();
310
+ const result = await adapter.send(makePayload(), makeDestination());
311
+
312
+ expect(result.success).toBe(true);
313
+ expect(result.remotePushAccepted).toBe(false);
314
+ });
315
+
316
+ test("reports remotePushAccepted: false when the success body is unparseable", async () => {
317
+ fetchResponses.push({ ok: true, status: 200, body: "not json" });
318
+ const adapter = new PlatformPushAdapter();
319
+ const result = await adapter.send(makePayload(), makeDestination());
320
+
321
+ expect(result.success).toBe(true);
322
+ expect(result.remotePushAccepted).toBe(false);
323
+ });
324
+
325
+ test("leaves remotePushAccepted unset on non-2xx failure", async () => {
326
+ fetchResponses.push({ ok: false, status: 400, body: "bad request" });
327
+ const adapter = new PlatformPushAdapter();
328
+ const result = await adapter.send(makePayload(), makeDestination());
329
+
330
+ expect(result.success).toBe(false);
331
+ expect(result.remotePushAccepted).toBeUndefined();
332
+ });
333
+
334
+ test("leaves remotePushAccepted unset when the platform client is unavailable", async () => {
335
+ clientAvailable = false;
336
+ const adapter = new PlatformPushAdapter();
337
+ const result = await adapter.send(makePayload(), makeDestination());
338
+
339
+ expect(result.success).toBe(false);
340
+ expect(result.remotePushAccepted).toBeUndefined();
341
+ });
342
+
215
343
  test("omits optional fields when absent from payload", async () => {
216
344
  const adapter = new PlatformPushAdapter();
217
345
  const payload: ChannelDeliveryPayload = {
@@ -27,6 +27,9 @@ mock.module("../messaging/providers/telegram-bot/send.js", () => ({
27
27
  text,
28
28
  approval: approval as (typeof sendCalls)[0]["approval"],
29
29
  });
30
+ // Mirror the real send result: the id of the sent message, which the
31
+ // adapter surfaces so the delivery row can address the card later.
32
+ return { lastMessageId: String(1000 + sendCalls.length) };
30
33
  },
31
34
  sendTelegramAttachments: async () => ({
32
35
  allFailed: false,
@@ -196,6 +199,9 @@ describe("TelegramAdapter", () => {
196
199
  const result = await adapter.send(payload, makeDestination());
197
200
 
198
201
  expect(result.success).toBe(true);
202
+ // The sent message's channel-native id is surfaced so the delivery row
203
+ // can address the card for in-place withdrawal later.
204
+ expect(result.messageId).toBe("1001");
199
205
  expect(sendCalls).toHaveLength(1);
200
206
 
201
207
  const call = sendCalls[0]!;
@@ -111,3 +111,48 @@ describe("VellumAdapter silent flag", () => {
111
111
  expect(intent.silent).toBe(false);
112
112
  });
113
113
  });
114
+
115
+ describe("VellumAdapter remotePushDispatched pass-through", () => {
116
+ test("broadcasts remote push acceptance fields verbatim", async () => {
117
+ const { adapter, sent } = captureBroadcast();
118
+ await adapter.send(
119
+ makePayload({
120
+ remotePushDispatched: true,
121
+ remotePushPlatforms: ["ios"],
122
+ }),
123
+ makeDestination(),
124
+ );
125
+
126
+ const intent = sent[0] as Extract<
127
+ AssistantEvent,
128
+ { type: "notification_intent" }
129
+ >;
130
+ expect(intent.remotePushDispatched).toBe(true);
131
+ expect(intent.remotePushPlatforms).toEqual(["ios"]);
132
+ });
133
+
134
+ test("broadcasts remotePushDispatched: false verbatim", async () => {
135
+ const { adapter, sent } = captureBroadcast();
136
+ await adapter.send(
137
+ makePayload({ remotePushDispatched: false }),
138
+ makeDestination(),
139
+ );
140
+
141
+ const intent = sent[0] as Extract<
142
+ AssistantEvent,
143
+ { type: "notification_intent" }
144
+ >;
145
+ expect(intent.remotePushDispatched).toBe(false);
146
+ });
147
+
148
+ test("leaves remotePushDispatched undefined when absent from the payload", async () => {
149
+ const { adapter, sent } = captureBroadcast();
150
+ await adapter.send(makePayload(), makeDestination());
151
+
152
+ const intent = sent[0] as Extract<
153
+ AssistantEvent,
154
+ { type: "notification_intent" }
155
+ >;
156
+ expect(intent.remotePushDispatched).toBeUndefined();
157
+ });
158
+ });
@@ -64,6 +64,7 @@ describe("getModelProfiles", () => {
64
64
  "balanced",
65
65
  "cost-optimized",
66
66
  "alpha",
67
+ "latency-optimized",
67
68
  "quality-optimized",
68
69
  "zeta",
69
70
  ]);
@@ -95,6 +96,7 @@ describe("getModelProfiles", () => {
95
96
  ["blend", true],
96
97
  ["balanced", false],
97
98
  ["cost-optimized", false],
99
+ ["latency-optimized", false],
98
100
  ["quality-optimized", false],
99
101
  ]);
100
102
  });
@@ -144,6 +146,7 @@ describe("getModelProfiles", () => {
144
146
  ["beta", true],
145
147
  ["balanced", false],
146
148
  ["cost-optimized", false],
149
+ ["latency-optimized", false],
147
150
  ["quality-optimized", false],
148
151
  ]);
149
152
  });
@@ -188,6 +191,7 @@ describe("getModelProfiles", () => {
188
191
  ["beta", true],
189
192
  ["balanced", false],
190
193
  ["cost-optimized", false],
194
+ ["latency-optimized", false],
191
195
  ["quality-optimized", false],
192
196
  ]);
193
197
  });
@@ -224,6 +228,11 @@ describe("getModelProfiles", () => {
224
228
  getModelProfiles()
225
229
  .map((p) => p.key)
226
230
  .sort(),
227
- ).toEqual(["balanced", "cost-optimized", "quality-optimized"]);
231
+ ).toEqual([
232
+ "balanced",
233
+ "cost-optimized",
234
+ "latency-optimized",
235
+ "quality-optimized",
236
+ ]);
228
237
  });
229
238
  });
@@ -125,6 +125,7 @@ const BASELINE: Record<string, readonly string[]> = {
125
125
  "../../../../daemon/turn-latency-sub-spans.js",
126
126
  "../../../../notifications/emit-signal.js",
127
127
  "../../../../persistence/checkpoints.js",
128
+ "../../../../persistence/conversation-types.js",
128
129
  "../../../../persistence/db-connection.js",
129
130
  "../../../../persistence/embeddings/embed.js",
130
131
  "../../../../persistence/embeddings/embedding-backend.js",
@@ -183,6 +184,7 @@ const BASELINE: Record<string, readonly string[]> = {
183
184
  "../../../persistence/checkpoints.js",
184
185
  "../../../persistence/cleanup-schedule-state.js",
185
186
  "../../../persistence/conversation-crud.js",
187
+ "../../../persistence/conversation-types.js",
186
188
  "../../../persistence/db-connection.js",
187
189
  "../../../persistence/db-maintenance.js",
188
190
  "../../../persistence/embeddings/embedding-backend.js",
@@ -222,6 +224,7 @@ const BASELINE: Record<string, readonly string[]> = {
222
224
  "../../../util/sqlite-retry.js",
223
225
  "../../../util/strip-comment-lines.js",
224
226
  "../../../util/truncate.js",
227
+ "../../../util/worker-compute.js",
225
228
  "../../../util/worker-memory.js",
226
229
  "../../../util/worker-process.js",
227
230
  "../../types.js",