@vellumai/assistant 0.8.11 → 0.8.12-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 (244) hide show
  1. package/ARCHITECTURE.md +15 -17
  2. package/README.md +0 -6
  3. package/bun.lock +6 -122
  4. package/node_modules/@vellumai/gateway-client/bun.lock +1 -0
  5. package/node_modules/@vellumai/gateway-client/package.json +3 -1
  6. package/node_modules/@vellumai/gateway-client/src/__tests__/gateway-client.test.ts +1 -1
  7. package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +87 -0
  8. package/node_modules/@vellumai/gateway-client/src/index.ts +3 -5
  9. package/openapi.yaml +633 -4
  10. package/package.json +1 -3
  11. package/src/__tests__/adaptive-thinking-repair.test.ts +185 -0
  12. package/src/__tests__/agent-loop-compaction-events.test.ts +7 -6
  13. package/src/__tests__/anthropic-provider.test.ts +129 -0
  14. package/src/__tests__/background-workers-disk-pressure.test.ts +4 -1
  15. package/src/__tests__/btw-routes.test.ts +7 -34
  16. package/src/__tests__/checker.test.ts +6 -12
  17. package/src/__tests__/config-loader-backfill.test.ts +4 -2
  18. package/src/__tests__/config-loader-quarantine-notice.test.ts +167 -0
  19. package/src/__tests__/config-watcher.test.ts +2 -2
  20. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -1
  21. package/src/__tests__/conversation-error.test.ts +2 -6
  22. package/src/__tests__/conversation-history-web-search.test.ts +8 -0
  23. package/src/__tests__/conversation-title-service.test.ts +2 -1
  24. package/src/__tests__/credential-security-invariants.test.ts +1 -1
  25. package/src/__tests__/disk-pressure-tools.test.ts +1 -1
  26. package/src/__tests__/exploration-drift-hook.test.ts +692 -0
  27. package/src/__tests__/filing-service.test.ts +8 -3
  28. package/src/__tests__/guardian-action-store.test.ts +0 -167
  29. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +1 -1
  30. package/src/__tests__/heartbeat-disk-pressure.test.ts +4 -1
  31. package/src/__tests__/heartbeat-service.test.ts +5 -2
  32. package/src/__tests__/identity-intro-cache.test.ts +12 -5
  33. package/src/__tests__/identity-routes.test.ts +16 -57
  34. package/src/__tests__/injector-chain.test.ts +8 -3
  35. package/src/__tests__/injector-config-quarantine-notice.test.ts +115 -0
  36. package/src/__tests__/llm-catalog-parity.test.ts +16 -0
  37. package/src/__tests__/llm-usage-store.test.ts +11 -0
  38. package/src/__tests__/log-export-workspace.test.ts +468 -3
  39. package/src/__tests__/memory-v2-static-injector.test.ts +22 -0
  40. package/src/__tests__/model-intents.test.ts +1 -1
  41. package/src/__tests__/oauth-cli.test.ts +19 -8
  42. package/src/__tests__/openai-provider.test.ts +34 -0
  43. package/src/__tests__/prechat-onboarding-contract.test.ts +0 -1
  44. package/src/__tests__/recurrence-engine.test.ts +45 -0
  45. package/src/__tests__/schedule-routes.test.ts +34 -0
  46. package/src/__tests__/scheduler-disk-pressure.test.ts +1 -1
  47. package/src/__tests__/script-proxy-conversation-manager.test.ts +10 -5
  48. package/src/__tests__/secret-fixtures.ts +20 -0
  49. package/src/__tests__/skill-tool-factory.test.ts +49 -0
  50. package/src/__tests__/subagent-role-registry.test.ts +24 -1
  51. package/src/__tests__/subagent-tools.test.ts +1 -0
  52. package/src/__tests__/system-prompt.test.ts +109 -11
  53. package/src/__tests__/tool-approval-handler.test.ts +85 -0
  54. package/src/__tests__/tool-audit-listener.test.ts +86 -0
  55. package/src/__tests__/tool-error-hook.test.ts +1 -0
  56. package/src/__tests__/tool-result-spool.test.ts +337 -0
  57. package/src/__tests__/tool-result-truncate-hook.test.ts +1 -0
  58. package/src/__tests__/validate-input.test.ts +95 -1
  59. package/src/__tests__/workspace-migration-098-remove-stale-updates-bulletin-file.test.ts +65 -0
  60. package/src/__tests__/workspace-migration-099-disable-cache-one-shot-callsites.test.ts +139 -0
  61. package/src/__tests__/workspace-migration-100-upgrade-quality-profile-to-fable-5.test.ts +174 -0
  62. package/src/__tests__/workspace-migration-101-upgrade-balanced-economy-to-minimax-m3.test.ts +162 -0
  63. package/src/__tests__/workspace-release-notes-feature-flag-guard.test.ts +45 -95
  64. package/src/acp/__tests__/agent-process.test.ts +315 -2
  65. package/src/acp/__tests__/prepare-agent-env.test.ts +79 -5
  66. package/src/acp/agent-process.ts +163 -34
  67. package/src/acp/prepare-agent-env.ts +55 -15
  68. package/src/agent/loop.ts +81 -24
  69. package/src/api/events/usage-progress.ts +28 -0
  70. package/src/api/index.ts +6 -0
  71. package/src/background-wake/wake-intent-hooks.test.ts +2 -0
  72. package/src/bundler/app-bundler.ts +25 -42
  73. package/src/bundler/app-compiler.ts +8 -0
  74. package/src/calls/call-controller.ts +1 -1
  75. package/src/cli/commands/plugins.ts +248 -15
  76. package/src/cli/lib/__tests__/inspect-plugin.test.ts +318 -0
  77. package/src/cli/lib/__tests__/install-from-github.test.ts +16 -9
  78. package/src/cli/lib/__tests__/plugin-artifact.test.ts +183 -0
  79. package/src/cli/lib/__tests__/plugin-details.test.ts +158 -0
  80. package/src/cli/lib/__tests__/plugin-fingerprint.test.ts +245 -0
  81. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +307 -0
  82. package/src/cli/lib/inspect-plugin.ts +252 -0
  83. package/src/cli/lib/install-from-github.ts +214 -21
  84. package/src/cli/lib/list-installed-plugins.ts +17 -6
  85. package/src/cli/lib/plugin-artifact.ts +103 -0
  86. package/src/cli/lib/plugin-details.ts +18 -1
  87. package/src/cli/lib/plugin-fingerprint.ts +197 -0
  88. package/src/cli/lib/upgrade-plugin.ts +225 -0
  89. package/src/config/bundled-skills/subagent/SKILL.md +2 -0
  90. package/src/config/bundled-skills/subagent/TOOLS.json +8 -2
  91. package/src/config/call-site-defaults.ts +13 -2
  92. package/src/config/feature-flag-registry.json +8 -16
  93. package/src/config/loader.ts +52 -59
  94. package/src/config/schema.ts +0 -2
  95. package/src/config/schemas/__tests__/memory-v2.test.ts +1 -0
  96. package/src/config/schemas/__tests__/memory-v3.test.ts +10 -0
  97. package/src/config/schemas/llm.ts +10 -0
  98. package/src/config/schemas/memory-v2.ts +13 -0
  99. package/src/config/schemas/memory-v3.ts +92 -0
  100. package/src/config/seed-inference-profiles.ts +4 -8
  101. package/src/context/post-turn-tool-result-truncation.ts +32 -18
  102. package/src/context/tool-result-spool.ts +104 -0
  103. package/src/credential-execution/feature-gates.ts +0 -1
  104. package/src/daemon/conversation-agent-loop-handlers.ts +41 -16
  105. package/src/daemon/conversation-error.ts +6 -15
  106. package/src/daemon/conversation.ts +9 -0
  107. package/src/daemon/disk-pressure-policy.ts +0 -1
  108. package/src/daemon/lifecycle.ts +1 -20
  109. package/src/daemon/message-types/conversations.ts +2 -15
  110. package/src/daemon/trust-context.ts +1 -1
  111. package/src/events/tool-audit-listener.ts +40 -9
  112. package/src/heartbeat/__tests__/heartbeat-service.test.ts +1 -1
  113. package/src/home/__tests__/home-content-refresh.test.ts +114 -0
  114. package/src/home/__tests__/suggested-prompts.test.ts +86 -5
  115. package/src/home/home-content-refresh.ts +43 -31
  116. package/src/home/home-greeting-cache.ts +8 -1
  117. package/src/home/home-greeting.ts +13 -9
  118. package/src/home/suggested-prompts.ts +77 -24
  119. package/src/ipc/routes/trust-rules.test.ts +66 -72
  120. package/src/media/image-credentials.ts +2 -2
  121. package/src/memory/__tests__/compaction-log-store-clickhouse.test.ts +432 -0
  122. package/src/memory/{compaction-log-writer-clickhouse.ts → compaction-log-store-clickhouse.ts} +264 -55
  123. package/src/memory/conversation-attention-store.ts +1 -0
  124. package/src/memory/conversation-bootstrap.ts +18 -9
  125. package/src/memory/conversation-crud.ts +12 -2
  126. package/src/memory/conversation-title-service.ts +53 -9
  127. package/src/memory/delivery-channels.ts +0 -69
  128. package/src/memory/graph/extraction-job.ts +0 -15
  129. package/src/memory/guardian-action-store.ts +1 -376
  130. package/src/memory/llm-usage-store.ts +5 -1
  131. package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +2 -2
  132. package/src/memory/v2/__tests__/consolidation-job.test.ts +183 -2
  133. package/src/memory/v2/__tests__/injection.test.ts +70 -0
  134. package/src/memory/v2/__tests__/static-context.test.ts +12 -0
  135. package/src/memory/v2/consolidation-job.ts +93 -9
  136. package/src/memory/v2/injection.ts +53 -0
  137. package/src/memory/v2/prompts/consolidation.ts +1 -0
  138. package/src/memory/v2/static-context.ts +13 -1
  139. package/src/memory/v2/sweep-job.ts +1 -1
  140. package/src/memory/v2/types.ts +5 -0
  141. package/src/plugin-api/types.ts +7 -0
  142. package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +300 -0
  143. package/src/plugins/defaults/exploration-drift/package.json +15 -0
  144. package/src/plugins/defaults/index.ts +25 -0
  145. package/src/plugins/defaults/memory-retrieval/injectors.ts +132 -4
  146. package/src/plugins/defaults/memory-v3-shadow/__tests__/card.test.ts +92 -0
  147. package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +2 -1
  148. package/src/plugins/defaults/memory-v3-shadow/__tests__/fresh-set.test.ts +52 -0
  149. package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +1 -0
  150. package/src/plugins/defaults/memory-v3-shadow/__tests__/live-integration.test.ts +2 -1
  151. package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +136 -5
  152. package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +17 -0
  153. package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +6 -0
  154. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +5 -1
  155. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +68 -4
  156. package/src/plugins/defaults/memory-v3-shadow/card.ts +49 -5
  157. package/src/plugins/defaults/memory-v3-shadow/fresh-set.ts +59 -0
  158. package/src/plugins/defaults/memory-v3-shadow/injector.ts +4 -2
  159. package/src/plugins/defaults/memory-v3-shadow/learned-edges.test.ts +169 -0
  160. package/src/plugins/defaults/memory-v3-shadow/learned-edges.ts +178 -0
  161. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +115 -26
  162. package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +13 -9
  163. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +144 -22
  164. package/src/plugins/defaults/memory-v3-shadow/types.ts +24 -6
  165. package/src/plugins/defaults/title-generate/hooks/stop.ts +13 -0
  166. package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +16 -0
  167. package/src/prompts/cache-boundary.ts +17 -0
  168. package/src/prompts/sections.ts +50 -17
  169. package/src/prompts/system-prompt.ts +12 -4
  170. package/src/prompts/templates/system-sections.ts +22 -0
  171. package/src/providers/__tests__/unparseable-tool-args.test.ts +53 -0
  172. package/src/providers/anthropic/client.ts +74 -28
  173. package/src/providers/gemini/client.ts +5 -1
  174. package/src/providers/minimax/client.ts +9 -0
  175. package/src/providers/model-catalog.ts +28 -0
  176. package/src/providers/model-intents.ts +3 -3
  177. package/src/providers/openai/chat-completions-provider.ts +4 -2
  178. package/src/providers/openai/responses-provider.ts +7 -2
  179. package/src/providers/retry.ts +8 -0
  180. package/src/providers/types.ts +11 -0
  181. package/src/providers/unparseable-tool-args.ts +56 -0
  182. package/src/runtime/AGENTS.md +6 -0
  183. package/src/runtime/__tests__/agent-wake.test.ts +2 -2
  184. package/src/runtime/agent-wake.ts +5 -5
  185. package/src/runtime/background-job-runner.ts +2 -2
  186. package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +150 -3
  187. package/src/runtime/migrations/vbundle-import-analyzer.ts +29 -6
  188. package/src/runtime/migrations/vbundle-import-policy.ts +23 -0
  189. package/src/runtime/migrations/vbundle-importer.ts +9 -4
  190. package/src/runtime/migrations/vbundle-streaming-importer.ts +8 -3
  191. package/src/runtime/pre-first-message-gate.ts +1 -1
  192. package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +241 -0
  193. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +132 -0
  194. package/src/runtime/routes/__tests__/gateway-log-routes.test.ts +97 -185
  195. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +17 -0
  196. package/src/runtime/routes/__tests__/plugins-routes.test.ts +348 -0
  197. package/src/runtime/routes/__tests__/task-routes.test.ts +3 -3
  198. package/src/runtime/routes/btw-routes.ts +0 -14
  199. package/src/runtime/routes/conversation-compaction-routes.ts +86 -19
  200. package/src/runtime/routes/conversation-list-routes.ts +77 -5
  201. package/src/runtime/routes/conversation-management-routes.ts +54 -0
  202. package/src/runtime/routes/conversation-query-routes.ts +79 -4
  203. package/src/runtime/routes/gateway-log-routes.ts +14 -64
  204. package/src/runtime/routes/home-feed-routes.ts +10 -0
  205. package/src/runtime/routes/identity-intro-cache.ts +1 -1
  206. package/src/runtime/routes/identity-routes.ts +76 -20
  207. package/src/runtime/routes/inbound-message-handler.ts +0 -36
  208. package/src/runtime/routes/log-export-routes.ts +143 -96
  209. package/src/runtime/routes/plugins-routes.ts +380 -0
  210. package/src/runtime/routes/redact-staged-export.ts +259 -0
  211. package/src/runtime/routes/schedule-routes.ts +19 -2
  212. package/src/runtime/routes/trust-rules-routes.ts +14 -67
  213. package/src/schedule/recurrence-engine.ts +34 -0
  214. package/src/schedule/scheduler.ts +1 -0
  215. package/src/security/redact-json.ts +61 -0
  216. package/src/skills/validate-input.ts +41 -1
  217. package/src/subagent/types.ts +26 -1
  218. package/src/telemetry/types.ts +15 -1
  219. package/src/telemetry/usage-telemetry-reporter.test.ts +6 -1
  220. package/src/telemetry/usage-telemetry-reporter.ts +1 -0
  221. package/src/tools/apps/executors.ts +1 -1
  222. package/src/tools/skills/skill-tool-factory.ts +19 -8
  223. package/src/tools/tool-approval-handler.ts +31 -0
  224. package/src/usage/types.ts +8 -1
  225. package/src/util/platform.ts +16 -0
  226. package/src/watcher/engine.ts +1 -0
  227. package/src/workspace/adaptive-thinking-repair.ts +113 -0
  228. package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +70 -67
  229. package/src/workspace/migrations/098-remove-stale-updates-bulletin-file.ts +31 -0
  230. package/src/workspace/migrations/099-disable-cache-one-shot-callsites.ts +81 -0
  231. package/src/workspace/migrations/100-upgrade-quality-profile-to-fable-5.ts +86 -0
  232. package/src/workspace/migrations/101-upgrade-balanced-economy-to-minimax-m3.ts +70 -0
  233. package/src/workspace/migrations/registry.ts +8 -0
  234. package/src/__tests__/config-loader-quarantine-bulletin.test.ts +0 -202
  235. package/src/__tests__/conversation-starters-cadence.test.ts +0 -161
  236. package/src/__tests__/guardian-action-followup-executor.test.ts +0 -322
  237. package/src/__tests__/guardian-action-followup-store.test.ts +0 -373
  238. package/src/__tests__/guardian-action-late-reply.test.ts +0 -1083
  239. package/src/__tests__/update-bulletin-job.test.ts +0 -292
  240. package/src/config/schemas/updates.ts +0 -14
  241. package/src/memory/__tests__/compaction-log-writer-clickhouse.test.ts +0 -227
  242. package/src/memory/conversation-starters-cadence.ts +0 -78
  243. package/src/prompts/update-bulletin-job.ts +0 -180
  244. package/src/runtime/guardian-action-followup-executor.ts +0 -306
@@ -0,0 +1,113 @@
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+
4
+ // Enable adaptive thinking on the managed "balanced" and "quality-optimized"
5
+ // profiles.
6
+ //
7
+ // The assistant-side seed defaults (MANAGED_PROFILE_TEMPLATES in
8
+ // seed-inference-profiles.ts) already ship thinking: { enabled: true,
9
+ // streamThinking: true } for both profiles, which normalizes to
10
+ // { type: "adaptive" } on the wire. Off-platform (BYOK) instances pick this
11
+ // up on every boot because the seeder overwrites managed profiles. On-platform
12
+ // instances preserve existing profiles (the platform overlay is authoritative),
13
+ // so instances that were hatched before thinking was enabled in the templates
14
+ // are stuck with thinking disabled or absent.
15
+ //
16
+ // Workspace migration 097 patches the on-disk config once, but it runs before
17
+ // mergeDefaultWorkspaceConfig() which can overwrite the fix with overlay
18
+ // profiles that have thinking disabled or absent. lifecycle.ts calls this
19
+ // repair again after the overlay merge + profile seeding so the fix sticks.
20
+ // The migration keeps its own frozen copy of this logic (migration files are
21
+ // self-contained snapshots and must not be imported from).
22
+ //
23
+ // The repair patches both profiles, adding thinking: { enabled: true,
24
+ // streamThinking: true } where it's missing or explicitly disabled. It skips
25
+ // profiles that:
26
+ // - Don't exist (no profile to patch)
27
+ // - Already have thinking enabled (idempotent)
28
+ // - Are source: "user" (user-created profiles are untouched)
29
+ // - Have a non-managed, non-absent source (unknown origin)
30
+ // - Resolve to a non-Anthropic provider (adaptive thinking is
31
+ // Anthropic-specific). A profile with no explicit provider inherits
32
+ // llm.default.provider, so the check falls back to that — with a
33
+ // completely absent llm.default.provider treated as Anthropic, matching
34
+ // migration 052's own `?? "anthropic"` default. This keeps the legacy
35
+ // non-Anthropic empty `{}` shells seeded by migration 052 off the repair.
36
+
37
+ const ADAPTIVE_THINKING = { enabled: true, streamThinking: true } as const;
38
+ const TARGET_PROFILES = ["balanced", "quality-optimized"] as const;
39
+
40
+ /**
41
+ * Patch managed Anthropic profiles that are missing adaptive thinking.
42
+ *
43
+ * Idempotent: profiles that already have thinking enabled are skipped.
44
+ */
45
+ export function repairAdaptiveThinkingOnManagedProfiles(
46
+ workspaceDir: string,
47
+ ): void {
48
+ const configPath = join(workspaceDir, "config.json");
49
+ if (!existsSync(configPath)) return;
50
+
51
+ let config: Record<string, unknown>;
52
+ try {
53
+ const raw = JSON.parse(readFileSync(configPath, "utf-8"));
54
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return;
55
+ config = raw as Record<string, unknown>;
56
+ } catch {
57
+ return;
58
+ }
59
+
60
+ const llm = readObject(config.llm);
61
+ if (llm === null) return;
62
+
63
+ const profiles = readObject(llm.profiles);
64
+ if (profiles === null) return;
65
+
66
+ // Profiles without an explicit provider inherit llm.default.provider at
67
+ // resolution time; an absent llm.default.provider resolves to Anthropic.
68
+ const defaultBlock = readObject(llm.default);
69
+ const defaultProvider =
70
+ typeof defaultBlock?.provider === "string"
71
+ ? defaultBlock.provider
72
+ : "anthropic";
73
+
74
+ let changed = false;
75
+
76
+ for (const name of TARGET_PROFILES) {
77
+ const profile = readObject(profiles[name]);
78
+ if (profile === null) continue;
79
+
80
+ // Only patch managed Anthropic profiles.
81
+ // Legacy profiles created before the `source` metadata field was introduced
82
+ // have source=undefined. Treat these as managed when the profile name is one
83
+ // of the canonical managed names (which TARGET_PROFILES already guarantees)
84
+ // and the effective provider — explicit, or inherited from llm.default — is
85
+ // Anthropic. Explicit `source: "user"` profiles are always skipped.
86
+ if (profile.source === "user") continue;
87
+ if (profile.source !== undefined && profile.source !== "managed") continue;
88
+ const effectiveProvider =
89
+ typeof profile.provider === "string" ? profile.provider : defaultProvider;
90
+ if (effectiveProvider !== "anthropic") continue;
91
+
92
+ // Skip if thinking is already enabled.
93
+ const thinking = readObject(profile.thinking);
94
+ if (thinking !== null && thinking.enabled === true) continue;
95
+
96
+ profile.thinking = { ...ADAPTIVE_THINKING };
97
+ profiles[name] = profile;
98
+ changed = true;
99
+ }
100
+
101
+ if (changed) {
102
+ llm.profiles = profiles;
103
+ config.llm = llm;
104
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
105
+ }
106
+ }
107
+
108
+ function readObject(value: unknown): Record<string, unknown> | null {
109
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
110
+ return null;
111
+ }
112
+ return value as Record<string, unknown>;
113
+ }
@@ -22,87 +22,90 @@ import type { WorkspaceMigration } from "./types.js";
22
22
  // - Already have thinking enabled (idempotent)
23
23
  // - Are source: "user" (user-created profiles are untouched)
24
24
  // - Have a non-managed, non-absent source (unknown origin)
25
- // - Use a non-Anthropic provider (adaptive thinking is Anthropic-specific)
25
+ // - Resolve to a non-Anthropic provider (adaptive thinking is
26
+ // Anthropic-specific). A profile with no explicit provider inherits
27
+ // llm.default.provider, so the check falls back to that — with a
28
+ // completely absent llm.default.provider treated as Anthropic, matching
29
+ // migration 052's own `?? "anthropic"` default. This keeps the legacy
30
+ // non-Anthropic empty `{}` shells seeded by migration 052 off the repair.
31
+ //
32
+ // Note: the live startup path re-runs the same repair after the platform
33
+ // overlay merge — see assistant/src/workspace/adaptive-thinking-repair.ts.
34
+ // This file keeps its own frozen copy of the logic because migration modules
35
+ // are self-contained snapshots and must never be imported by other code.
26
36
 
27
37
  const ADAPTIVE_THINKING = { enabled: true, streamThinking: true } as const;
28
38
  const TARGET_PROFILES = ["balanced", "quality-optimized"] as const;
29
39
 
30
- /**
31
- * Patch managed Anthropic profiles that are missing adaptive thinking.
32
- *
33
- * Exported so lifecycle.ts can re-run the repair after mergeDefaultWorkspaceConfig()
34
- * and seedInferenceProfiles(). On-platform instances with a config overlay have their
35
- * profiles overwritten by the overlay merge (which runs after workspace migrations),
36
- * so the migration alone is insufficient — the post-overlay call ensures the repair
37
- * sticks even when the overlay supplies profiles without thinking enabled.
38
- *
39
- * The function is idempotent: profiles that already have thinking enabled are skipped.
40
- */
41
- export function repairAdaptiveThinkingOnManagedProfiles(
42
- workspaceDir: string,
43
- ): void {
44
- const configPath = join(workspaceDir, "config.json");
45
- if (!existsSync(configPath)) return;
40
+ export const enableAdaptiveThinkingManagedProfilesMigration: WorkspaceMigration =
41
+ {
42
+ id: "097-enable-adaptive-thinking-managed-profiles",
43
+ description:
44
+ "Enable adaptive thinking on managed balanced and quality-optimized profiles",
45
+ run(workspaceDir: string): void {
46
+ const configPath = join(workspaceDir, "config.json");
47
+ if (!existsSync(configPath)) return;
46
48
 
47
- let config: Record<string, unknown>;
48
- try {
49
- const raw = JSON.parse(readFileSync(configPath, "utf-8"));
50
- if (!raw || typeof raw !== "object" || Array.isArray(raw)) return;
51
- config = raw as Record<string, unknown>;
52
- } catch {
53
- return;
54
- }
49
+ let config: Record<string, unknown>;
50
+ try {
51
+ const raw = JSON.parse(readFileSync(configPath, "utf-8"));
52
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return;
53
+ config = raw as Record<string, unknown>;
54
+ } catch {
55
+ return;
56
+ }
55
57
 
56
- const llm = readObject(config.llm);
57
- if (llm === null) return;
58
+ const llm = readObject(config.llm);
59
+ if (llm === null) return;
58
60
 
59
- const profiles = readObject(llm.profiles);
60
- if (profiles === null) return;
61
+ const profiles = readObject(llm.profiles);
62
+ if (profiles === null) return;
61
63
 
62
- let changed = false;
64
+ // Profiles without an explicit provider inherit llm.default.provider at
65
+ // resolution time; an absent llm.default.provider resolves to Anthropic.
66
+ const defaultBlock = readObject(llm.default);
67
+ const defaultProvider =
68
+ typeof defaultBlock?.provider === "string"
69
+ ? defaultBlock.provider
70
+ : "anthropic";
63
71
 
64
- for (const name of TARGET_PROFILES) {
65
- const profile = readObject(profiles[name]);
66
- if (profile === null) continue;
72
+ let changed = false;
67
73
 
68
- // Only patch managed Anthropic profiles.
69
- // Legacy profiles created before the `source` metadata field was introduced
70
- // have source=undefined. Treat these as managed when the profile name is one
71
- // of the canonical managed names (which TARGET_PROFILES already guarantees)
72
- // and the provider is Anthropic (or absent). Explicit `source: "user"`
73
- // profiles are always skipped.
74
- if (profile.source === "user") continue;
75
- if (profile.source !== undefined && profile.source !== "managed") continue;
76
- if (
77
- typeof profile.provider === "string" &&
78
- profile.provider !== "anthropic"
79
- ) {
80
- continue;
81
- }
74
+ for (const name of TARGET_PROFILES) {
75
+ const profile = readObject(profiles[name]);
76
+ if (profile === null) continue;
82
77
 
83
- // Skip if thinking is already enabled.
84
- const thinking = readObject(profile.thinking);
85
- if (thinking !== null && thinking.enabled === true) continue;
78
+ // Only patch managed Anthropic profiles.
79
+ // Legacy profiles created before the `source` metadata field was
80
+ // introduced have source=undefined. Treat these as managed when the
81
+ // profile name is one of the canonical managed names (which
82
+ // TARGET_PROFILES already guarantees) and the effective provider —
83
+ // explicit, or inherited from llm.default — is Anthropic. Explicit
84
+ // `source: "user"` profiles are always skipped.
85
+ if (profile.source === "user") continue;
86
+ if (profile.source !== undefined && profile.source !== "managed") {
87
+ continue;
88
+ }
89
+ const effectiveProvider =
90
+ typeof profile.provider === "string"
91
+ ? profile.provider
92
+ : defaultProvider;
93
+ if (effectiveProvider !== "anthropic") continue;
86
94
 
87
- profile.thinking = { ...ADAPTIVE_THINKING };
88
- profiles[name] = profile;
89
- changed = true;
90
- }
95
+ // Skip if thinking is already enabled.
96
+ const thinking = readObject(profile.thinking);
97
+ if (thinking !== null && thinking.enabled === true) continue;
91
98
 
92
- if (changed) {
93
- llm.profiles = profiles;
94
- config.llm = llm;
95
- writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
96
- }
97
- }
99
+ profile.thinking = { ...ADAPTIVE_THINKING };
100
+ profiles[name] = profile;
101
+ changed = true;
102
+ }
98
103
 
99
- export const enableAdaptiveThinkingManagedProfilesMigration: WorkspaceMigration =
100
- {
101
- id: "097-enable-adaptive-thinking-managed-profiles",
102
- description:
103
- "Enable adaptive thinking on managed balanced and quality-optimized profiles",
104
- run(workspaceDir: string): void {
105
- repairAdaptiveThinkingOnManagedProfiles(workspaceDir);
104
+ if (changed) {
105
+ llm.profiles = profiles;
106
+ config.llm = llm;
107
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
108
+ }
106
109
  },
107
110
  down(_workspaceDir: string): void {
108
111
  // Forward-only.
@@ -0,0 +1,31 @@
1
+ import { rmSync } from "node:fs";
2
+ import { join } from "node:path";
3
+
4
+ import type { WorkspaceMigration } from "./types.js";
5
+
6
+ const MIGRATION_ID = "098-remove-stale-updates-bulletin-file";
7
+
8
+ /**
9
+ * Delete `<workspace>/UPDATES.md`.
10
+ *
11
+ * Release notes used to be appended to the file by workspace migrations and
12
+ * processed by a background conversation dispatched at daemon startup. That
13
+ * feature has been removed — nothing consumes the file's contents anymore,
14
+ * and it was system-written bulletin material, so it is deleted outright.
15
+ *
16
+ * Idempotent: `rmSync` with `force` is a no-op when the file is absent.
17
+ */
18
+ export const removeStaleUpdatesBulletinFileMigration: WorkspaceMigration = {
19
+ id: MIGRATION_ID,
20
+ description:
21
+ "Delete leftover UPDATES.md release-note bulletins (processing job removed)",
22
+
23
+ run(workspaceDir: string): void {
24
+ rmSync(join(workspaceDir, "UPDATES.md"), { force: true });
25
+ },
26
+
27
+ down(_workspaceDir: string): void {
28
+ // Forward-only: the removed content was pending release-note bulletins
29
+ // for a feature that no longer exists.
30
+ },
31
+ };
@@ -0,0 +1,81 @@
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+
4
+ import type { WorkspaceMigration } from "./types.js";
5
+
6
+ /**
7
+ * Add `disableCache: true` to existing on-disk entries for the one-shot LLM
8
+ * call sites whose shipped defaults now opt out of prompt caching.
9
+ *
10
+ * The resolver prefers an explicit `llm.callSites.<id>` entry over
11
+ * CALL_SITE_DEFAULTS, and earlier migrations (054 recall, 072 replySuggestion,
12
+ * and platform overlays for the others) seeded such entries. Without this
13
+ * migration, upgraded workspaces would keep paying prompt-cache writes on
14
+ * call sites whose prompts never produce a cache read — only fresh installs
15
+ * would pick up the new shipped default.
16
+ *
17
+ * Idempotent and customization-preserving: an entry that already carries a
18
+ * `disableCache` key (either value — the user may have opted back in) is
19
+ * left untouched, and only the one key is added to entries we patch. Entries
20
+ * that don't exist on disk are skipped — those workspaces already resolve
21
+ * the shipped default.
22
+ */
23
+ const ONE_SHOT_CALL_SITES = [
24
+ "recall",
25
+ "replySuggestion",
26
+ "homeGreeting",
27
+ "homeSuggestedPrompts",
28
+ "conversationTitle",
29
+ "memoryConsolidation",
30
+ ] as const;
31
+
32
+ export const disableCacheOneShotCallsitesMigration: WorkspaceMigration = {
33
+ id: "099-disable-cache-one-shot-callsites",
34
+ description:
35
+ "Add disableCache: true to existing one-shot call-site entries so upgraded workspaces match the new shipped defaults",
36
+ run(workspaceDir: string): void {
37
+ if (process.env.VELLUM_DEFAULT_WORKSPACE_CONFIG_PATH) return;
38
+
39
+ const configPath = join(workspaceDir, "config.json");
40
+ if (!existsSync(configPath)) return;
41
+
42
+ let config: Record<string, unknown> = {};
43
+ try {
44
+ const raw = JSON.parse(readFileSync(configPath, "utf-8"));
45
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return;
46
+ config = raw as Record<string, unknown>;
47
+ } catch {
48
+ return;
49
+ }
50
+
51
+ const llm = readObject(config.llm);
52
+ if (llm === null) return;
53
+ const callSites = readObject(llm.callSites);
54
+ if (callSites === null) return;
55
+
56
+ let changed = false;
57
+ for (const callSite of ONE_SHOT_CALL_SITES) {
58
+ const entry = readObject(callSites[callSite]);
59
+ if (entry === null) continue;
60
+ if ("disableCache" in entry) continue;
61
+ entry.disableCache = true;
62
+ callSites[callSite] = entry;
63
+ changed = true;
64
+ }
65
+ if (!changed) return;
66
+
67
+ llm.callSites = callSites;
68
+ config.llm = llm;
69
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
70
+ },
71
+ down(_workspaceDir: string): void {
72
+ // Forward-only.
73
+ },
74
+ };
75
+
76
+ function readObject(value: unknown): Record<string, unknown> | null {
77
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
78
+ return null;
79
+ }
80
+ return value as Record<string, unknown>;
81
+ }
@@ -0,0 +1,86 @@
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+
4
+ import type { WorkspaceMigration } from "./types.js";
5
+
6
+ // Upgrade quality-optimized profiles to Claude Fable 5.
7
+ //
8
+ // The quality-optimized model intent moved to claude-fable-5 (#34498), but
9
+ // the intent map is only consulted when a profile is seeded: off-platform
10
+ // installs reseed managed profiles on every boot, while on-platform
11
+ // workspaces keep the profile they were hatched with — the seeder skips
12
+ // existing profiles there, and the hatch overlay is consumed exactly once.
13
+ // Existing platform assistants are therefore stuck on whichever model the
14
+ // quality intent pointed at when they hatched (claude-opus-4-7 or
15
+ // claude-opus-4-8) until a migration rewrites it.
16
+ //
17
+ // Only the managed quality-optimized profile is touched — the user-owned
18
+ // custom-quality-optimized copy is theirs to manage. Within it, only a model
19
+ // still on a previous quality-intent default is upgraded — matching by model
20
+ // value also covers the OpenRouter-prefixed ids. A profile whose model the
21
+ // user changed to anything else is left untouched.
22
+
23
+ const TARGET_PROFILES = ["quality-optimized"];
24
+
25
+ const MODEL_UPGRADES: Record<string, string> = {
26
+ "claude-opus-4-7": "claude-fable-5",
27
+ "claude-opus-4-8": "claude-fable-5",
28
+ "anthropic/claude-opus-4.7": "anthropic/claude-fable-5",
29
+ "anthropic/claude-opus-4.8": "anthropic/claude-fable-5",
30
+ };
31
+
32
+ export const upgradeQualityProfileToFable5Migration: WorkspaceMigration = {
33
+ id: "100-upgrade-quality-profile-to-fable-5",
34
+ description:
35
+ "Upgrade the managed quality-optimized profile from Opus 4.7/4.8 to Claude Fable 5",
36
+ run(workspaceDir: string): void {
37
+ const configPath = join(workspaceDir, "config.json");
38
+ if (!existsSync(configPath)) return;
39
+
40
+ let config: Record<string, unknown>;
41
+ try {
42
+ const raw = JSON.parse(readFileSync(configPath, "utf-8"));
43
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return;
44
+ config = raw as Record<string, unknown>;
45
+ } catch {
46
+ return;
47
+ }
48
+
49
+ const llm = readObject(config.llm);
50
+ if (llm === null) return;
51
+
52
+ const profiles = readObject(llm.profiles);
53
+ if (profiles === null) return;
54
+
55
+ let changed = false;
56
+
57
+ for (const name of TARGET_PROFILES) {
58
+ const profile = readObject(profiles[name]);
59
+ if (profile === null) continue;
60
+ if (typeof profile.model !== "string") continue;
61
+
62
+ const upgraded = MODEL_UPGRADES[profile.model];
63
+ if (upgraded === undefined) continue;
64
+
65
+ profile.model = upgraded;
66
+ profiles[name] = profile;
67
+ changed = true;
68
+ }
69
+
70
+ if (changed) {
71
+ llm.profiles = profiles;
72
+ config.llm = llm;
73
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
74
+ }
75
+ },
76
+ down(_workspaceDir: string): void {
77
+ // Forward-only.
78
+ },
79
+ };
80
+
81
+ function readObject(value: unknown): Record<string, unknown> | null {
82
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
83
+ return null;
84
+ }
85
+ return value as Record<string, unknown>;
86
+ }
@@ -0,0 +1,70 @@
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+
4
+ import type { WorkspaceMigration } from "./types.js";
5
+
6
+ // Switch the Balanced Economy profile from Kimi K2.6 to MiniMax M3.
7
+ //
8
+ // The Fireworks balanced model intent moved to MiniMax M3 (#34726), but the
9
+ // intent map is only consulted when a profile is seeded: off-platform
10
+ // installs reseed managed profiles on every boot, while on-platform
11
+ // workspaces keep the profile they were hatched with — the seeder skips
12
+ // existing profiles there. Existing platform assistants would otherwise stay
13
+ // on Kimi K2.6 (including the now-dropped `suppress-cjk` logit-bias preset)
14
+ // until a migration rewrites the profile.
15
+ //
16
+ // Only the managed balanced-economy profile still pointing at the old
17
+ // default model is touched — a profile whose model the user changed is left
18
+ // alone. The rewrite mirrors the new seed template: MiniMax M3, 32K max
19
+ // output tokens, no logit-bias preset.
20
+
21
+ const OLD_MODEL = "accounts/fireworks/models/kimi-k2p6";
22
+ const NEW_MODEL = "accounts/fireworks/models/minimax-m3";
23
+
24
+ export const upgradeBalancedEconomyToMinimaxM3Migration: WorkspaceMigration = {
25
+ id: "101-upgrade-balanced-economy-to-minimax-m3",
26
+ description:
27
+ "Switch the managed Balanced Economy profile from Kimi K2.6 to MiniMax M3",
28
+ run(workspaceDir: string): void {
29
+ const configPath = join(workspaceDir, "config.json");
30
+ if (!existsSync(configPath)) return;
31
+
32
+ let config: Record<string, unknown>;
33
+ try {
34
+ const raw = JSON.parse(readFileSync(configPath, "utf-8"));
35
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return;
36
+ config = raw as Record<string, unknown>;
37
+ } catch {
38
+ return;
39
+ }
40
+
41
+ const llm = readObject(config.llm);
42
+ if (llm === null) return;
43
+
44
+ const profiles = readObject(llm.profiles);
45
+ if (profiles === null) return;
46
+
47
+ const profile = readObject(profiles["balanced-economy"]);
48
+ if (profile === null || profile.model !== OLD_MODEL) return;
49
+
50
+ profile.model = NEW_MODEL;
51
+ profile.description =
52
+ "Strong open model (MiniMax M3) at a lower price point";
53
+ profile.maxTokens = 32000;
54
+ delete profile.logitBias;
55
+ profiles["balanced-economy"] = profile;
56
+ llm.profiles = profiles;
57
+ config.llm = llm;
58
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
59
+ },
60
+ down(_workspaceDir: string): void {
61
+ // Forward-only.
62
+ },
63
+ };
64
+
65
+ function readObject(value: unknown): Record<string, unknown> | null {
66
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
67
+ return null;
68
+ }
69
+ return value as Record<string, unknown>;
70
+ }
@@ -95,6 +95,10 @@ import { seedAvatarManifestMigration } from "./094-seed-avatar-manifest.js";
95
95
  import { bumpHeartbeatInterval30mTo60mMigration } from "./095-bump-heartbeat-interval-30m-to-60m.js";
96
96
  import { reduceQualityProfileEffortMigration } from "./096-reduce-quality-profile-effort.js";
97
97
  import { enableAdaptiveThinkingManagedProfilesMigration } from "./097-enable-adaptive-thinking-managed-profiles.js";
98
+ import { removeStaleUpdatesBulletinFileMigration } from "./098-remove-stale-updates-bulletin-file.js";
99
+ import { disableCacheOneShotCallsitesMigration } from "./099-disable-cache-one-shot-callsites.js";
100
+ import { upgradeQualityProfileToFable5Migration } from "./100-upgrade-quality-profile-to-fable-5.js";
101
+ import { upgradeBalancedEconomyToMinimaxM3Migration } from "./101-upgrade-balanced-economy-to-minimax-m3.js";
98
102
  import { migrateToWorkspaceVolumeMigration } from "./migrate-to-workspace-volume.js";
99
103
  import type { WorkspaceMigration } from "./types.js";
100
104
 
@@ -201,4 +205,8 @@ export const WORKSPACE_MIGRATIONS: WorkspaceMigration[] = [
201
205
  bumpHeartbeatInterval30mTo60mMigration,
202
206
  reduceQualityProfileEffortMigration,
203
207
  enableAdaptiveThinkingManagedProfilesMigration,
208
+ removeStaleUpdatesBulletinFileMigration,
209
+ disableCacheOneShotCallsitesMigration,
210
+ upgradeQualityProfileToFable5Migration,
211
+ upgradeBalancedEconomyToMinimaxM3Migration,
204
212
  ];