@vellumai/assistant 0.12.5 → 0.12.6-dev.202609292019.a6f6d85

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 (264) hide show
  1. package/.env.example +2 -0
  2. package/ARCHITECTURE.md +23 -0
  3. package/docs/automatic-done.md +32 -0
  4. package/docs/desktop-browser-cli.md +3 -2
  5. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +2 -1
  6. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/mcp-oauth.test.ts +53 -0
  7. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  8. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/mcp-oauth.ts +60 -0
  9. package/node_modules/@vellumai/environments/src/shell.test.ts +13 -1
  10. package/node_modules/@vellumai/environments/src/shell.ts +22 -1
  11. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +2 -1
  12. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/mcp-oauth.test.ts +53 -0
  13. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  14. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/mcp-oauth.ts +60 -0
  15. package/node_modules/@vellumai/gateway-client/src/__tests__/status-contracts.test.ts +39 -0
  16. package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +25 -0
  17. package/node_modules/@vellumai/service-contracts/package.json +2 -1
  18. package/node_modules/@vellumai/service-contracts/src/__tests__/mcp-oauth.test.ts +53 -0
  19. package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  20. package/node_modules/@vellumai/service-contracts/src/mcp-oauth.ts +60 -0
  21. package/openapi.yaml +270 -10
  22. package/package.json +1 -1
  23. package/src/__tests__/agent-loop.test.ts +76 -0
  24. package/src/__tests__/anthropic-provider.test.ts +2 -0
  25. package/src/__tests__/byok-default-profile-ensure.test.ts +7 -0
  26. package/src/__tests__/config-get-vision-flag.test.ts +1 -1
  27. package/src/__tests__/config-loader-validation-reset-notice.test.ts +2 -2
  28. package/src/__tests__/conversation-agent-loop-overflow.test.ts +160 -154
  29. package/src/__tests__/conversation-evictor.test.ts +54 -0
  30. package/src/__tests__/conversation-pairing.test.ts +112 -0
  31. package/src/__tests__/document-append-idempotency.test.ts +13 -0
  32. package/src/__tests__/document-create-dedupe.test.ts +13 -0
  33. package/src/__tests__/document-find-replace.test.ts +13 -0
  34. package/src/__tests__/document-revision-history.test.ts +390 -0
  35. package/src/__tests__/document-revision.test.ts +425 -0
  36. package/src/__tests__/document-tool-security.test.ts +13 -0
  37. package/src/__tests__/document-update-default-surface.test.ts +13 -0
  38. package/src/__tests__/emit-signal-routing-intent.test.ts +270 -0
  39. package/src/__tests__/gateway-flag-listener.test.ts +59 -6
  40. package/src/__tests__/headless-browser-navigate.test.ts +20 -30
  41. package/src/__tests__/host-cu-proxy.test.ts +79 -0
  42. package/src/__tests__/host-cu-routes-targeted.test.ts +14 -5
  43. package/src/__tests__/host-shell-tool.test.ts +6 -2
  44. package/src/__tests__/identity-routes.test.ts +4 -3
  45. package/src/__tests__/llm-catalog-parity.test.ts +4 -4
  46. package/src/__tests__/llm-schema.test.ts +1 -1
  47. package/src/__tests__/notification-platform-adapter.test.ts +83 -1
  48. package/src/__tests__/oauth2-gateway-transport.test.ts +2 -0
  49. package/src/__tests__/openai-provider.test.ts +11 -11
  50. package/src/__tests__/pricing.test.ts +11 -0
  51. package/src/__tests__/public-ingress-urls.test.ts +26 -0
  52. package/src/__tests__/retry-thinking-tool-choice.test.ts +47 -0
  53. package/src/__tests__/usage-attribution.test.ts +31 -0
  54. package/src/__tests__/workspace-migration-133-collapse-provider-connections.test.ts +3 -3
  55. package/src/__tests__/workspace-migration-160-repair-retired-fireworks-model-ids.test.ts +262 -0
  56. package/src/__tests__/workspace-migration-161-repair-retired-fireworks-glm-5p2-model-id.test.ts +233 -0
  57. package/src/acp/agent-process.ts +6 -1
  58. package/src/acp/client-handler.ts +6 -1
  59. package/src/agent/loop.ts +29 -6
  60. package/src/api/events/document-editor-show.ts +5 -0
  61. package/src/api/events/document-editor-update.ts +10 -0
  62. package/src/api/events/tool-result.ts +1 -0
  63. package/src/calls/__tests__/voice-session-bridge.test.ts +54 -0
  64. package/src/calls/channel-admission-reader.ts +15 -9
  65. package/src/calls/twilio-provider.ts +14 -31
  66. package/src/calls/voice-session-bridge.ts +18 -1
  67. package/src/cli/commands/browser.ts +1 -2
  68. package/src/cli/lib/__tests__/plugin-artifact.test.ts +23 -0
  69. package/src/cli/lib/__tests__/plugin-catalog-visibility.test.ts +2 -0
  70. package/src/cli/lib/__tests__/plugin-details.test.ts +16 -0
  71. package/src/cli/lib/bundled-marketplace.json +41 -2
  72. package/src/cli/lib/inspect-plugin.ts +15 -15
  73. package/src/cli/lib/list-installed-plugins.ts +29 -28
  74. package/src/cli/lib/plugin-artifact.ts +74 -63
  75. package/src/cli/lib/plugin-catalog-visibility.ts +1 -0
  76. package/src/cli/lib/plugin-details.ts +27 -42
  77. package/src/cli/lib/plugin-marketplace.ts +6 -8
  78. package/src/cli/lib/plugin-surfaces.ts +8 -7
  79. package/src/cli.ts +1 -3
  80. package/src/config/__tests__/chat-settings.test.ts +121 -0
  81. package/src/config/assistant-feature-flags.ts +28 -23
  82. package/src/config/bundled-skills/computer-use/SKILL.md +20 -0
  83. package/src/config/bundled-skills/computer-use/TOOLS.json +11 -11
  84. package/src/config/chat-settings.ts +100 -0
  85. package/src/config/default-profile-catalog.ts +1 -1
  86. package/src/config/feature-flag-cache.ts +7 -25
  87. package/src/config/feature-flag-registry.json +1 -9
  88. package/src/config/loader.ts +5 -1
  89. package/src/config/profile-text-generation.ts +25 -21
  90. package/src/config/schemas/conversations.ts +14 -0
  91. package/src/config/schemas/llm.ts +1 -0
  92. package/src/config/schemas/notifications.ts +8 -1
  93. package/src/contacts/gateway-channel-read.ts +1 -1
  94. package/src/conversations/__tests__/auto-archive-activity.test.ts +136 -0
  95. package/src/conversations/__tests__/auto-archive.test.ts +304 -0
  96. package/src/conversations/auto-archive-activity.ts +61 -0
  97. package/src/conversations/auto-archive.ts +201 -0
  98. package/src/daemon/computer-use-observation.test.ts +81 -0
  99. package/src/daemon/computer-use-observation.ts +23 -0
  100. package/src/daemon/conversation-agent-loop.ts +11 -6
  101. package/src/daemon/conversation-evictor.ts +50 -16
  102. package/src/daemon/doordash-steps.ts +1 -3
  103. package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +43 -1
  104. package/src/daemon/handlers/__tests__/config-channels.test.ts +1 -1
  105. package/src/daemon/handlers/config-a2a.ts +75 -2
  106. package/src/daemon/host-cu-proxy.ts +34 -3
  107. package/src/daemon/lifecycle.ts +15 -2
  108. package/src/daemon/shutdown-handlers.ts +3 -0
  109. package/src/daemon/turn-finalization.ts +4 -0
  110. package/src/desktop/desktop-browser-endpoint.ts +11 -2
  111. package/src/desktop/desktop-chrome-policy.test.ts +1 -0
  112. package/src/desktop/desktop-chrome-policy.ts +7 -1
  113. package/src/desktop/desktop-computer-use.test.ts +14 -4
  114. package/src/desktop/desktop-session-manager.ts +5 -3
  115. package/src/documents/document-revisions-store.ts +153 -0
  116. package/src/documents/document-store.ts +471 -133
  117. package/src/followups/followup-store.ts +0 -4
  118. package/src/inbound/__tests__/oauth-callback-url.test.ts +25 -2
  119. package/src/inbound/oauth-callback-url.ts +30 -1
  120. package/src/inbound/public-ingress-urls.ts +12 -2
  121. package/src/ipc/gateway-client.test.ts +40 -0
  122. package/src/ipc/gateway-client.ts +12 -29
  123. package/src/ipc/gateway-flag-listener.ts +7 -10
  124. package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +71 -0
  125. package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +83 -0
  126. package/src/live-voice/__tests__/protocol.test.ts +77 -0
  127. package/src/live-voice/__tests__/share-targets.test.ts +163 -0
  128. package/src/live-voice/live-voice-session.ts +23 -6
  129. package/src/live-voice/protocol.ts +22 -0
  130. package/src/live-voice/share-targets.ts +225 -0
  131. package/src/mcp/__tests__/mcp-oauth-client-registration.test.ts +144 -3
  132. package/src/mcp/__tests__/mcp-oauth-provider.test.ts +2 -0
  133. package/src/mcp/client.ts +1 -19
  134. package/src/mcp/mcp-oauth-provider.ts +66 -49
  135. package/src/media/openrouter-image-service.ts +39 -22
  136. package/src/monitoring/__tests__/oom-inheritance-sweep.test.ts +86 -0
  137. package/src/monitoring/__tests__/oom-kill-reporter.test.ts +374 -0
  138. package/src/monitoring/control.ts +2 -1
  139. package/src/monitoring/oom-inheritance-sweep.ts +96 -0
  140. package/src/monitoring/oom-kill-reporter.ts +469 -0
  141. package/src/monitoring/resource-sampler.ts +30 -0
  142. package/src/notifications/README.md +53 -4
  143. package/src/notifications/__tests__/assistant-initiated-thread.test.ts +254 -0
  144. package/src/notifications/__tests__/broadcaster.test.ts +558 -6
  145. package/src/notifications/__tests__/completion-policy.test.ts +37 -0
  146. package/src/notifications/__tests__/from-me-alert-integration.test.ts +192 -0
  147. package/src/notifications/adapters/platform.ts +14 -0
  148. package/src/notifications/assistant-initiated-thread.ts +101 -0
  149. package/src/notifications/broadcaster.ts +94 -16
  150. package/src/notifications/chat-reply-policy.ts +11 -0
  151. package/src/notifications/completion-policy.ts +12 -2
  152. package/src/notifications/conversation-pairing.ts +15 -70
  153. package/src/notifications/emit-signal.ts +34 -3
  154. package/src/notifications/types.ts +2 -0
  155. package/src/oauth/connect-orchestrator.ts +1 -2
  156. package/src/persistence/__tests__/conversation-auto-archive.test.ts +330 -0
  157. package/src/persistence/conversation-auto-archive.ts +170 -0
  158. package/src/persistence/conversation-crud.ts +2 -1
  159. package/src/persistence/conversation-queries.ts +15 -8
  160. package/src/persistence/embeddings/embed.test.ts +96 -0
  161. package/src/persistence/embeddings/embed.ts +20 -2
  162. package/src/persistence/embeddings/embedding-gemini.test.ts +39 -3
  163. package/src/persistence/embeddings/embedding-gemini.ts +39 -1
  164. package/src/persistence/embeddings/embedding-local.ts +6 -0
  165. package/src/persistence/embeddings/qdrant-manager.ts +5 -0
  166. package/src/persistence/job-utils.ts +10 -0
  167. package/src/persistence/migrations/384-add-document-revision.test.ts +83 -0
  168. package/src/persistence/migrations/384-add-document-revision.ts +21 -0
  169. package/src/persistence/migrations/385-create-document-revisions.test.ts +67 -0
  170. package/src/persistence/migrations/385-create-document-revisions.ts +28 -0
  171. package/src/persistence/migrations/386-conversations-last-reopened-at.ts +10 -0
  172. package/src/persistence/migrations/__tests__/386-conversations-last-reopened-at.test.ts +32 -0
  173. package/src/persistence/schema/conversations.ts +1 -0
  174. package/src/persistence/schema/documents.ts +20 -0
  175. package/src/persistence/steps.ts +6 -0
  176. package/src/platform/managed-speech.ts +42 -46
  177. package/src/plugin-api/vision-support.test.ts +21 -21
  178. package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +11 -6
  179. package/src/providers/__tests__/dispatch-connection-routing.test.ts +1 -1
  180. package/src/providers/__tests__/search-provider-catalog.test.ts +8 -0
  181. package/src/providers/anthropic/client.ts +1 -1
  182. package/src/providers/fireworks/client.test.ts +168 -0
  183. package/src/providers/fireworks/client.ts +24 -0
  184. package/src/providers/inference/adapter-factory.ts +6 -0
  185. package/src/providers/ionet/client.test.ts +143 -0
  186. package/src/providers/ionet/client.ts +37 -0
  187. package/src/providers/model-catalog.ts +141 -84
  188. package/src/providers/model-intents.ts +4 -4
  189. package/src/providers/openai/chat-completions-provider.ts +23 -1
  190. package/src/providers/retry.ts +56 -4
  191. package/src/providers/search-provider-catalog.ts +10 -0
  192. package/src/providers/types.ts +10 -2
  193. package/src/providers/usage-tracking.ts +5 -3
  194. package/src/runtime/assistant-event-hub.ts +23 -0
  195. package/src/runtime/auth/__tests__/guard-tests.test.ts +0 -7
  196. package/src/runtime/auth/__tests__/route-policy.test.ts +1 -1
  197. package/src/runtime/channel-retry-sweep.ts +1 -2
  198. package/src/runtime/migrations/__tests__/vbundle-streaming-validator.test.ts +35 -10
  199. package/src/runtime/migrations/__tests__/vbundle-validator-v1-schema.test.ts +48 -0
  200. package/src/runtime/migrations/vbundle-builder.ts +19 -3
  201. package/src/runtime/migrations/vbundle-streaming-importer.ts +14 -19
  202. package/src/runtime/migrations/vbundle-streaming-validator.ts +14 -7
  203. package/src/runtime/migrations/vbundle-validator.ts +54 -1
  204. package/src/runtime/pending-interactions.ts +20 -2
  205. package/src/runtime/routes/__tests__/contact-routes.test.ts +5 -3
  206. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +36 -0
  207. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +375 -2
  208. package/src/runtime/routes/__tests__/inference-send-routes.test.ts +1 -1
  209. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +12 -0
  210. package/src/runtime/routes/contact-routes.ts +2 -2
  211. package/src/runtime/routes/conversation-list-routes.ts +10 -2
  212. package/src/runtime/routes/conversation-query-routes.ts +48 -10
  213. package/src/runtime/routes/documents-routes.ts +185 -47
  214. package/src/runtime/routes/events-routes.ts +1 -3
  215. package/src/runtime/routes/host-app-control-routes.ts +1 -0
  216. package/src/runtime/routes/host-bash-routes.ts +1 -0
  217. package/src/runtime/routes/host-browser-routes.ts +15 -0
  218. package/src/runtime/routes/host-cu-routes.ts +4 -1
  219. package/src/runtime/routes/host-file-routes.ts +1 -0
  220. package/src/runtime/routes/host-proxy-result-binding.test.ts +83 -0
  221. package/src/runtime/routes/host-proxy-result-binding.ts +25 -0
  222. package/src/runtime/routes/host-transfer-routes.ts +1 -0
  223. package/src/runtime/routes/identity-routes.ts +3 -3
  224. package/src/runtime/routes/integrations/a2a.ts +4 -1
  225. package/src/runtime/routes/migration-routes.ts +3 -1
  226. package/src/runtime/routes/notification-routes.ts +1 -2
  227. package/src/runtime/routes/ui-snapshot-routes.test.ts +1 -0
  228. package/src/runtime/routes/user-route-dispatcher.ts +5 -1
  229. package/src/runtime/verification-templates.ts +1 -2
  230. package/src/sequence/analytics.ts +0 -4
  231. package/src/sequence/engine.ts +1 -11
  232. package/src/sequence/guardrails.ts +1 -6
  233. package/src/sequence/importer.ts +0 -8
  234. package/src/sequence/store.ts +0 -10
  235. package/src/signals/user-message.ts +0 -6
  236. package/src/skills/catalog-install.ts +52 -23
  237. package/src/tools/browser/browser-execution.ts +20 -23
  238. package/src/tools/browser/cdp-client/cdp-inspect/ws-transport.ts +1 -3
  239. package/src/tools/document/document-tool.ts +13 -11
  240. package/src/tools/executor.ts +1 -2
  241. package/src/tools/host-terminal/host-shell.ts +4 -2
  242. package/src/tools/network/__tests__/web-search.test.ts +105 -0
  243. package/src/tools/network/web-fetch.ts +2 -4
  244. package/src/tools/network/web-search.ts +239 -12
  245. package/src/tools/shared/filesystem/format-diff.ts +1 -2
  246. package/src/tools/shared/shell-output.test.ts +42 -0
  247. package/src/tools/shared/shell-output.ts +27 -4
  248. package/src/tools/terminal/shell.ts +3 -3
  249. package/src/tools/ui-surface/visual-validation.ts +1 -2
  250. package/src/usage/attribution.ts +18 -1
  251. package/src/util/__tests__/oom-priority.test.ts +55 -0
  252. package/src/util/__tests__/worker-memory.test.ts +16 -0
  253. package/src/util/debounce.ts +0 -4
  254. package/src/util/oom-priority.ts +79 -0
  255. package/src/util/platform.ts +0 -2
  256. package/src/util/plugin-manifest.ts +5 -7
  257. package/src/util/pricing.ts +25 -0
  258. package/src/util/worker-memory.ts +9 -3
  259. package/src/util/worker-process.ts +8 -0
  260. package/src/workspace/byok-default-profile-ensure.ts +11 -0
  261. package/src/workspace/hatched-date.ts +11 -4
  262. package/src/workspace/migrations/160-repair-retired-fireworks-model-ids.ts +209 -0
  263. package/src/workspace/migrations/161-repair-retired-fireworks-glm-5p2-model-id.ts +197 -0
  264. package/src/workspace/migrations/registry.ts +4 -0
@@ -166,6 +166,7 @@ describe("identity routes — health endpoint", () => {
166
166
  memoryOptOut: true,
167
167
  retryLastTurn: true,
168
168
  appPins: true,
169
+ chatsSettings: true,
169
170
  });
170
171
 
171
172
  // Profiler should either be absent or show enabled: false
@@ -492,9 +493,9 @@ describe("identity routes — /readyz readiness gate", () => {
492
493
  expect(body.ready).toBe(false);
493
494
  expect(body.reason).toBe("db_migrations_failed");
494
495
  expect((body.dbMigrations as Record<string, unknown>).state).toBe("failed");
495
- expect((body.dbMigrations as Record<string, unknown>).failedMigrations).toEqual(
496
- [{ name: "flakyStep", error: "transient failure" }],
497
- );
496
+ expect(
497
+ (body.dbMigrations as Record<string, unknown>).failedMigrations,
498
+ ).toEqual([{ name: "flakyStep", error: "transient failure" }]);
498
499
  });
499
500
 
500
501
  test("returns 200 even if CES is down", async () => {
@@ -390,9 +390,9 @@ describe("LLM catalog parity: daemon vs client", () => {
390
390
  });
391
391
  });
392
392
 
393
- test("Fireworks catalog includes GLM 5.2", () => {
393
+ test("Fireworks catalog includes GLM 5.3", () => {
394
394
  expect(
395
- isModelInCatalog("fireworks", "accounts/fireworks/models/glm-5p2"),
395
+ isModelInCatalog("fireworks", "accounts/fireworks/models/glm-5p3"),
396
396
  ).toBe(true);
397
397
 
398
398
  const fireworks = PROVIDER_CATALOG.find(
@@ -400,10 +400,10 @@ describe("LLM catalog parity: daemon vs client", () => {
400
400
  );
401
401
  expect(
402
402
  fireworks?.models.find(
403
- (model) => model.id === "accounts/fireworks/models/glm-5p2",
403
+ (model) => model.id === "accounts/fireworks/models/glm-5p3",
404
404
  ),
405
405
  ).toMatchObject({
406
- displayName: "GLM 5.2",
406
+ displayName: "GLM 5.3",
407
407
  contextWindowTokens: 1040000,
408
408
  maxOutputTokens: 131072,
409
409
  supportsToolUse: true,
@@ -198,7 +198,7 @@ describe("LLMSchema", () => {
198
198
  profiles: {
199
199
  "glm-default": {
200
200
  provider: "vellum",
201
- model: "accounts/fireworks/models/glm-5p2",
201
+ model: "accounts/fireworks/models/glm-5p3",
202
202
  },
203
203
  },
204
204
  activeProfile: "glm-default",
@@ -1,11 +1,14 @@
1
1
  import { beforeEach, describe, expect, mock, test } from "bun:test";
2
2
 
3
+ import { saveRawConfig } from "../config/loader.js";
4
+
3
5
  // ── Mock logger ──────────────────────────────────────────────────────────────
4
6
 
5
7
  // ── Mock sleep so retry tests don't slow down the suite ──────────────────────
6
8
 
9
+ let onRetrySleep: () => Promise<void> = async () => {};
7
10
  mock.module("../util/retry.js", () => ({
8
- sleep: async (_ms: number): Promise<void> => {},
11
+ sleep: async (_ms: number): Promise<void> => onRetrySleep(),
9
12
  isRetryableStatus: (status: number): boolean =>
10
13
  status === 429 || status >= 500,
11
14
  isRetryableNetworkError: (error: unknown): boolean => {
@@ -41,10 +44,12 @@ const fetchResponses: Array<{ ok: boolean; status: number; body?: string }> =
41
44
  [];
42
45
  const fetchErrors: Error[] = [];
43
46
  let clientAvailable = true;
47
+ let onClientCreation: () => Promise<void> = async () => {};
44
48
 
45
49
  mock.module("../platform/client.js", () => ({
46
50
  VellumPlatformClient: {
47
51
  create: async () => {
52
+ await onClientCreation();
48
53
  if (!clientAvailable) {
49
54
  return null;
50
55
  }
@@ -117,12 +122,89 @@ function makeDestination(
117
122
 
118
123
  describe("PlatformPushAdapter", () => {
119
124
  beforeEach(() => {
125
+ saveRawConfig({});
120
126
  fetchCalls.length = 0;
121
127
  fetchResponses.length = 0;
122
128
  fetchErrors.length = 0;
123
129
  clientAvailable = true;
130
+ onRetrySleep = async () => {};
131
+ onClientCreation = async () => {};
132
+ });
133
+
134
+ test("skips a disabled chat reply without sending a native push", async () => {
135
+ saveRawConfig({ notifications: { newMessageEnabled: false } });
136
+ const result = await new PlatformPushAdapter().send(
137
+ makePayload({ sourceEventName: "chat.assistant_reply" }),
138
+ makeDestination(),
139
+ );
140
+ expect(result).toMatchObject({
141
+ success: false,
142
+ skipped: true,
143
+ remotePushAccepted: false,
144
+ });
145
+ expect(fetchCalls).toHaveLength(0);
146
+ });
147
+
148
+ test("rechecks the setting after platform client creation", async () => {
149
+ onClientCreation = async () => {
150
+ saveRawConfig({ notifications: { newMessageEnabled: false } });
151
+ };
152
+ const result = await new PlatformPushAdapter().send(
153
+ makePayload({ sourceEventName: "chat.assistant_reply" }),
154
+ makeDestination(),
155
+ );
156
+ expect(result).toMatchObject({
157
+ success: false,
158
+ skipped: true,
159
+ remotePushAccepted: false,
160
+ });
161
+ expect(fetchCalls).toHaveLength(0);
124
162
  });
125
163
 
164
+ test.each([false, true])(
165
+ "stops retries after disabling replies (accepted push: %s)",
166
+ async (accepted) => {
167
+ fetchResponses.push({
168
+ ok: false,
169
+ status: 503,
170
+ body: accepted ? '{"accepted_platforms":["ios"]}' : "{}",
171
+ });
172
+ onRetrySleep = async () => {
173
+ saveRawConfig({ notifications: { newMessageEnabled: false } });
174
+ };
175
+ const result = await new PlatformPushAdapter().send(
176
+ makePayload({ sourceEventName: "chat.assistant_reply" }),
177
+ makeDestination(),
178
+ );
179
+ expect(fetchCalls).toHaveLength(1);
180
+ expect(result).toMatchObject({
181
+ success: accepted,
182
+ skipped: !accepted,
183
+ remotePushAccepted: accepted,
184
+ remotePushPlatforms: accepted ? ["ios"] : undefined,
185
+ });
186
+ },
187
+ );
188
+
189
+ test.each([
190
+ "schedule.result",
191
+ "schedule.notify",
192
+ "guardian.question",
193
+ "activity.failed",
194
+ "chat.assistant_reply.extra",
195
+ ])(
196
+ "keeps %s native push enabled when ordinary reply alerts are off",
197
+ async (sourceEventName) => {
198
+ saveRawConfig({ notifications: { newMessageEnabled: false } });
199
+ const result = await new PlatformPushAdapter().send(
200
+ makePayload({ sourceEventName }),
201
+ makeDestination(),
202
+ );
203
+ expect(result.success).toBe(true);
204
+ expect(fetchCalls).toHaveLength(1);
205
+ },
206
+ );
207
+
126
208
  test("channel is 'platform'", () => {
127
209
  expect(new PlatformPushAdapter().channel).toBe("platform");
128
210
  });
@@ -48,6 +48,8 @@ let mockOAuthCallbackUrl = "";
48
48
  // getPublicBaseUrl.
49
49
  mock.module("../inbound/public-ingress-urls.js", () => ({
50
50
  getOAuthCallbackUrl: () => mockOAuthCallbackUrl,
51
+ getMcpOAuthClientMetadataUrl: () =>
52
+ "http://localhost/oauth/client-metadata.json",
51
53
  getPublicBaseUrl: (config?: { ingress?: { publicBaseUrl?: string } }) => {
52
54
  const url = config?.ingress?.publicBaseUrl ?? "";
53
55
  if (!url) {
@@ -1960,10 +1960,10 @@ describe("FireworksProvider reasoning_effort ceiling", () => {
1960
1960
  lastCreateParams = null;
1961
1961
  });
1962
1962
 
1963
- test('DeepSeek V4 Pro accepts "max" unclamped', async () => {
1963
+ test('DeepSeek V4.1 Flash accepts "max" unclamped', async () => {
1964
1964
  const fw = new FireworksProvider(
1965
1965
  "fw-key",
1966
- "accounts/fireworks/models/deepseek-v4-pro-0813",
1966
+ "accounts/fireworks/models/deepseek-v4p1-flash",
1967
1967
  );
1968
1968
  await fw.sendMessage([userMsg("hi")], {
1969
1969
  systemPrompt: "system",
@@ -1972,10 +1972,10 @@ describe("FireworksProvider reasoning_effort ceiling", () => {
1972
1972
  expect(lastCreateParams!.reasoning_effort).toBe("max");
1973
1973
  });
1974
1974
 
1975
- test('DeepSeek V4 Pro accepts "xhigh" unclamped', async () => {
1975
+ test('DeepSeek V4.1 Flash accepts "xhigh" unclamped', async () => {
1976
1976
  const fw = new FireworksProvider(
1977
1977
  "fw-key",
1978
- "accounts/fireworks/models/deepseek-v4-pro-0813",
1978
+ "accounts/fireworks/models/deepseek-v4p1-flash",
1979
1979
  );
1980
1980
  await fw.sendMessage([userMsg("hi")], {
1981
1981
  systemPrompt: "system",
@@ -1984,10 +1984,10 @@ describe("FireworksProvider reasoning_effort ceiling", () => {
1984
1984
  expect(lastCreateParams!.reasoning_effort).toBe("xhigh");
1985
1985
  });
1986
1986
 
1987
- test('Kimi K2.6 clamps "xhigh"/"max" down to "high"', async () => {
1987
+ test('MiniMax M3 clamps "xhigh"/"max" down to "high"', async () => {
1988
1988
  const fw = new FireworksProvider(
1989
1989
  "fw-key",
1990
- "accounts/fireworks/models/kimi-k2p6",
1990
+ "accounts/fireworks/models/minimax-m3",
1991
1991
  );
1992
1992
  await fw.sendMessage([userMsg("hi")], {
1993
1993
  systemPrompt: "system",
@@ -2016,7 +2016,7 @@ describe("FireworksProvider reasoning_effort ceiling", () => {
2016
2016
  test("effort below ceiling is forwarded verbatim", async () => {
2017
2017
  const fw = new FireworksProvider(
2018
2018
  "fw-key",
2019
- "accounts/fireworks/models/deepseek-v4-pro-0813",
2019
+ "accounts/fireworks/models/deepseek-v4p1-flash",
2020
2020
  );
2021
2021
  await fw.sendMessage([userMsg("hi")], {
2022
2022
  systemPrompt: "system",
@@ -2028,7 +2028,7 @@ describe("FireworksProvider reasoning_effort ceiling", () => {
2028
2028
  test('effort: "none" passes through regardless of ceiling', async () => {
2029
2029
  const fw = new FireworksProvider(
2030
2030
  "fw-key",
2031
- "accounts/fireworks/models/kimi-k2p6",
2031
+ "accounts/fireworks/models/minimax-m3",
2032
2032
  );
2033
2033
  await fw.sendMessage([userMsg("hi")], {
2034
2034
  systemPrompt: "system",
@@ -2040,13 +2040,13 @@ describe("FireworksProvider reasoning_effort ceiling", () => {
2040
2040
  test("model override picks ceiling from runtime model, not constructor model", async () => {
2041
2041
  const fw = new FireworksProvider(
2042
2042
  "fw-key",
2043
- "accounts/fireworks/models/kimi-k2p6",
2043
+ "accounts/fireworks/models/minimax-m3",
2044
2044
  );
2045
2045
  await fw.sendMessage([userMsg("hi")], {
2046
2046
  systemPrompt: "system",
2047
2047
  config: {
2048
2048
  effort: "max",
2049
- model: "accounts/fireworks/models/deepseek-v4-pro-0813",
2049
+ model: "accounts/fireworks/models/deepseek-v4p1-flash",
2050
2050
  },
2051
2051
  });
2052
2052
  expect(lastCreateParams!.reasoning_effort).toBe("max");
@@ -2111,7 +2111,7 @@ describe("FireworksProvider sparse reasoning_effort support (GLM 5.3)", () => {
2111
2111
 
2112
2112
  test("models without supportedEfforts are unaffected", async () => {
2113
2113
  expect(
2114
- await send("accounts/fireworks/models/deepseek-v4-pro-0813", "medium"),
2114
+ await send("accounts/fireworks/models/deepseek-v4p1-flash", "medium"),
2115
2115
  ).toBe("medium");
2116
2116
  });
2117
2117
  });
@@ -10,6 +10,17 @@ import {
10
10
  } from "../util/pricing.js";
11
11
 
12
12
  describe("resolvePricing", () => {
13
+ test.each([
14
+ ["accounts/fireworks/models/glm-5p2", 1.4 + 4.4],
15
+ ["accounts/fireworks/models/kimi-k2p6", 0.95 + 4.0],
16
+ ["accounts/fireworks/models/deepseek-v4-pro-0813", 1.32 + 3.96],
17
+ ["accounts/fireworks/models/deepseek-v4-flash-0731", 0.14 + 0.28],
18
+ ])("prices stored usage for retired Fireworks model %s", (model, cost) => {
19
+ const result = resolvePricing("fireworks", model, 1_000_000, 1_000_000);
20
+ expect(result.pricingStatus).toBe("priced");
21
+ expect(result.estimatedCostUsd).toBeCloseTo(cost, 10);
22
+ });
23
+
13
24
  describe("Anthropic models", () => {
14
25
  test("returns priced for claude-opus-4-6", () => {
15
26
  const result = resolvePricing(
@@ -6,6 +6,7 @@ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
6
6
  import { setIngressPublicBaseUrl } from "../config/env.js";
7
7
  import { IngressConfigSchema } from "../config/schemas/ingress.js";
8
8
  import {
9
+ getMcpOAuthClientMetadataUrl,
9
10
  getOAuthCallbackUrl,
10
11
  getPublicBaseUrl,
11
12
  getTelegramWebhookUrl,
@@ -307,6 +308,31 @@ describe("getOAuthCallbackUrl", () => {
307
308
  });
308
309
  expect(url).toBe("https://example.com/webhooks/oauth/callback");
309
310
  });
311
+
312
+ test("canonicalizes the base before building OAuth URLs", () => {
313
+ const config = {
314
+ ingress: {
315
+ publicBaseUrl: "https://EXAMPLE.com:443/a/../assistant-123///",
316
+ },
317
+ };
318
+
319
+ expect(getOAuthCallbackUrl(config)).toBe(
320
+ "https://example.com/assistant-123/webhooks/oauth/callback",
321
+ );
322
+ expect(getMcpOAuthClientMetadataUrl(config)).toBe(
323
+ "https://example.com/assistant-123/oauth/client-metadata.json",
324
+ );
325
+ });
326
+ });
327
+
328
+ describe("getMcpOAuthClientMetadataUrl", () => {
329
+ test("builds the public metadata URL", () => {
330
+ expect(
331
+ getMcpOAuthClientMetadataUrl({
332
+ ingress: { publicBaseUrl: "https://example.com/assistant-123/" },
333
+ }),
334
+ ).toBe("https://example.com/assistant-123/oauth/client-metadata.json");
335
+ });
310
336
  });
311
337
 
312
338
  // ---------------------------------------------------------------------------
@@ -144,6 +144,53 @@ describe("retry normalization: thinking + forced tool_choice", () => {
144
144
  expect(lastConfig()?.thinking).toEqual({ type: "adaptive" });
145
145
  });
146
146
 
147
+ for (const [providerName, model] of [
148
+ ["anthropic", "claude-sonnet-5-5"],
149
+ ["anthropic", "claude-opus-5-5"],
150
+ ["openrouter", "anthropic/claude-sonnet-5.5"],
151
+ ["vercel-ai-gateway", "anthropic/claude-sonnet-5.5"],
152
+ ] as const) {
153
+ test(`downgrades forced tool_choice to auto for ${providerName} ${model}`, async () => {
154
+ // These models always reason and reject forced tool use, so stripping
155
+ // `thinking` is not enough: the forced choice itself has to go.
156
+ setLlmConfig({
157
+ callSites: {
158
+ memoryExtraction: { provider: providerName, model },
159
+ },
160
+ });
161
+ const { provider, lastConfig } = makePipeline(providerName);
162
+ await provider.sendMessage([userMessage], {
163
+ config: {
164
+ callSite: "memoryExtraction",
165
+ tool_choice: { type: "tool", name: "extract_graph_diff" },
166
+ },
167
+ });
168
+ expect(lastConfig()?.tool_choice).toEqual({ type: "auto" });
169
+ });
170
+ }
171
+
172
+ test("keeps forced tool_choice for an adaptive-only model off the Anthropic wire", async () => {
173
+ setLlmConfig({
174
+ callSites: {
175
+ memoryExtraction: {
176
+ provider: "fireworks",
177
+ model: "accounts/fireworks/models/glm-5p3",
178
+ },
179
+ },
180
+ });
181
+ const { provider, lastConfig } = makePipeline("fireworks");
182
+ await provider.sendMessage([userMessage], {
183
+ config: {
184
+ callSite: "memoryExtraction",
185
+ tool_choice: { type: "tool", name: "extract_graph_diff" },
186
+ },
187
+ });
188
+ expect(lastConfig()?.tool_choice).toEqual({
189
+ type: "tool",
190
+ name: "extract_graph_diff",
191
+ });
192
+ });
193
+
147
194
  test("preserves explicit thinking: disabled with forced tool_choice", async () => {
148
195
  // Callers like the retriever explicitly set thinking: { type: "disabled" }
149
196
  // alongside forced tool_choice. This should pass through unchanged since
@@ -109,6 +109,37 @@ describe("resolveUsageAttribution", () => {
109
109
  expectResolvedProviderModelMatchesResolver("mainAgent", "pinned");
110
110
  });
111
111
 
112
+ test("reports an override the Auto router chose as the auto source", () => {
113
+ setLlmConfig({
114
+ profiles: {
115
+ active: { provider: "openai", model: "gpt-5.4" },
116
+ routed: { provider: "gemini", model: "gemini-3-pro" },
117
+ },
118
+ activeProfile: "active",
119
+ });
120
+
121
+ const snapshot = resolveUsageAttribution({
122
+ callSite: "mainAgent",
123
+ overrideProfile: "routed",
124
+ overrideProfileOrigin: "auto",
125
+ });
126
+
127
+ expect(snapshot).toMatchObject({
128
+ appliedProfile: "routed",
129
+ profileSource: "auto",
130
+ resolvedProvider: "gemini",
131
+ resolvedModel: "gemini-3-pro",
132
+ });
133
+ // The origin only relabels an override that actually won.
134
+ expect(
135
+ resolveUsageAttribution({
136
+ callSite: "mainAgent",
137
+ overrideProfile: "missing",
138
+ overrideProfileOrigin: "auto",
139
+ }).profileSource,
140
+ ).toBe("active");
141
+ });
142
+
112
143
  test("resolves call-site profile attribution", () => {
113
144
  setLlmConfig({
114
145
  profiles: {
@@ -100,7 +100,7 @@ describe("133-collapse-provider-connections migration", () => {
100
100
  source: "user",
101
101
  provider: "fireworks",
102
102
  provider_connection: "vellum",
103
- model: "accounts/fireworks/models/glm-5p2",
103
+ model: "accounts/fireworks/models/minimax-m3",
104
104
  },
105
105
  },
106
106
  },
@@ -257,7 +257,7 @@ describe("133-collapse-provider-connections migration", () => {
257
257
  encoded: {
258
258
  source: "user",
259
259
  provider: "vellum",
260
- model: "fireworks/accounts/fireworks/models/glm-5p2",
260
+ model: "fireworks/accounts/fireworks/models/minimax-m3",
261
261
  },
262
262
  },
263
263
  },
@@ -272,7 +272,7 @@ describe("133-collapse-provider-connections migration", () => {
272
272
  expect(llm.profiles.encoded).toEqual({
273
273
  source: "user",
274
274
  provider: "vellum",
275
- model: "accounts/fireworks/models/glm-5p2",
275
+ model: "accounts/fireworks/models/minimax-m3",
276
276
  });
277
277
  });
278
278