@vellumai/assistant 0.7.2 → 0.8.0

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 (424) hide show
  1. package/ARCHITECTURE.md +45 -29
  2. package/Dockerfile +1 -0
  3. package/__tests__/permissions/gateway-threshold-reader.test.ts +236 -9
  4. package/bun.lock +3 -0
  5. package/docs/architecture/memory.md +5 -2
  6. package/knip.json +1 -0
  7. package/node_modules/@vellumai/gateway-client/src/ipc-client.ts +13 -4
  8. package/node_modules/@vellumai/ipc-server-utils/bun.lock +24 -0
  9. package/node_modules/@vellumai/ipc-server-utils/package.json +18 -0
  10. package/node_modules/@vellumai/ipc-server-utils/src/index.ts +6 -0
  11. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +430 -0
  12. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +221 -0
  13. package/node_modules/@vellumai/ipc-server-utils/tsconfig.json +20 -0
  14. package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +0 -9
  15. package/node_modules/@vellumai/slack-text/src/index.test.ts +18 -35
  16. package/node_modules/@vellumai/slack-text/src/index.ts +2 -48
  17. package/openapi.yaml +470 -25
  18. package/package.json +3 -1
  19. package/src/__tests__/annotate-risk-options.test.ts +291 -0
  20. package/src/__tests__/app-control-flow.test.ts +21 -11
  21. package/src/__tests__/approval-cascade.test.ts +8 -16
  22. package/src/__tests__/approval-routes-http.test.ts +6 -0
  23. package/src/__tests__/assistant-event-hub.test.ts +48 -0
  24. package/src/__tests__/assistant-event.test.ts +0 -10
  25. package/src/__tests__/assistant-events-sse-hardening.test.ts +2 -7
  26. package/src/__tests__/assistant-feature-flags-integration.test.ts +18 -0
  27. package/src/__tests__/auto-analysis-end-to-end.test.ts +48 -0
  28. package/src/__tests__/background-workers-disk-pressure.test.ts +268 -0
  29. package/src/__tests__/call-constants.test.ts +10 -1
  30. package/src/__tests__/call-controller.test.ts +127 -0
  31. package/src/__tests__/call-conversation-messages.test.ts +8 -2
  32. package/src/__tests__/channel-inbound-disk-pressure.test.ts +537 -0
  33. package/src/__tests__/channel-readiness-service.test.ts +4 -2
  34. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +58 -28
  35. package/src/__tests__/config-loader-backfill.test.ts +379 -0
  36. package/src/__tests__/config-loader-platform-defaults.test.ts +284 -1
  37. package/src/__tests__/config-schema.test.ts +1 -0
  38. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +18 -9
  39. package/src/__tests__/config-watcher.test.ts +140 -69
  40. package/src/__tests__/context-search-agent-runner.test.ts +61 -3
  41. package/src/__tests__/context-search-conversations-source.test.ts +0 -24
  42. package/src/__tests__/context-search-fanout.test.ts +0 -1
  43. package/src/__tests__/context-search-memory-source.test.ts +6 -33
  44. package/src/__tests__/context-search-memory-v2-source.test.ts +0 -2
  45. package/src/__tests__/context-search-pkb-source.test.ts +12 -7
  46. package/src/__tests__/context-search-workspace-source.test.ts +0 -1
  47. package/src/__tests__/conversation-abort-tool-results.test.ts +1 -0
  48. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +223 -0
  49. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -1
  50. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -1
  51. package/src/__tests__/conversation-agent-loop.test.ts +457 -8
  52. package/src/__tests__/conversation-confirmation-signals.test.ts +5 -13
  53. package/src/__tests__/conversation-error.test.ts +150 -3
  54. package/src/__tests__/conversation-init.benchmark.test.ts +1 -1
  55. package/src/__tests__/conversation-process-callsite.test.ts +38 -0
  56. package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -0
  57. package/src/__tests__/conversation-runtime-assembly.test.ts +74 -0
  58. package/src/__tests__/conversation-slash-unknown.test.ts +1 -0
  59. package/src/__tests__/conversation-speed-override.test.ts +0 -3
  60. package/src/__tests__/conversation-store.test.ts +0 -18
  61. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +170 -9
  62. package/src/__tests__/conversation-surfaces-app-control.test.ts +15 -4
  63. package/src/__tests__/conversation-surfaces-data-persist.test.ts +476 -0
  64. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +61 -5
  65. package/src/__tests__/conversation-workspace-injection.test.ts +1 -1
  66. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
  67. package/src/__tests__/credentials-cli.test.ts +7 -0
  68. package/src/__tests__/cu-unified-flow.test.ts +176 -10
  69. package/src/__tests__/date-context.test.ts +164 -2
  70. package/src/__tests__/disk-pressure-guard.test.ts +262 -0
  71. package/src/__tests__/disk-pressure-lifecycle.test.ts +168 -0
  72. package/src/__tests__/disk-pressure-policy.test.ts +241 -0
  73. package/src/__tests__/disk-pressure-routes.test.ts +379 -0
  74. package/src/__tests__/disk-pressure-tools.test.ts +277 -0
  75. package/src/__tests__/disk-usage.test.ts +150 -0
  76. package/src/__tests__/events-client-registration.test.ts +52 -0
  77. package/src/__tests__/events-dev-bypass-actor.test.ts +162 -0
  78. package/src/__tests__/file-write-tool.test.ts +4 -10
  79. package/src/__tests__/filing-service.test.ts +2 -20
  80. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +10 -26
  81. package/src/__tests__/heartbeat-disk-pressure.test.ts +183 -0
  82. package/src/__tests__/heartbeat-service.test.ts +260 -11
  83. package/src/__tests__/host-app-control-proxy.test.ts +195 -25
  84. package/src/__tests__/host-bash-proxy.test.ts +227 -34
  85. package/src/__tests__/host-bash-routes.test.ts +178 -13
  86. package/src/__tests__/host-cu-proxy.test.ts +210 -3
  87. package/src/__tests__/host-cu-routes-targeted.test.ts +141 -12
  88. package/src/__tests__/host-file-proxy-targeted.test.ts +48 -9
  89. package/src/__tests__/host-file-proxy.test.ts +268 -6
  90. package/src/__tests__/host-file-routes-targeted.test.ts +175 -17
  91. package/src/__tests__/host-transfer-proxy-targeted.test.ts +408 -59
  92. package/src/__tests__/host-transfer-routes-targeted.test.ts +232 -17
  93. package/src/__tests__/http-user-message-parity.test.ts +107 -1
  94. package/src/__tests__/injector-chain.test.ts +36 -16
  95. package/src/__tests__/injector-disk-pressure.test.ts +224 -0
  96. package/src/__tests__/injector-pkb-v2-silenced.test.ts +10 -7
  97. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +154 -67
  98. package/src/__tests__/managed-profile-guard.test.ts +18 -0
  99. package/src/__tests__/mcp-abort-signal.test.ts +130 -0
  100. package/src/__tests__/memory-admin-recall.test.ts +3 -11
  101. package/src/__tests__/memory-retrieval-pipeline.test.ts +22 -1
  102. package/src/__tests__/normalize-onboarding.test.ts +180 -0
  103. package/src/__tests__/notification-decision-fallback.test.ts +91 -0
  104. package/src/__tests__/notification-decision-strategy.test.ts +22 -0
  105. package/src/__tests__/oauth-cli.test.ts +121 -0
  106. package/src/__tests__/oauth-connect-routes.test.ts +316 -0
  107. package/src/__tests__/oauth-provider-seed-logos.test.ts +24 -2
  108. package/src/__tests__/onboarding-persona-write.test.ts +308 -0
  109. package/src/__tests__/openai-provider.test.ts +45 -8
  110. package/src/__tests__/persist-onboarding-artifacts.test.ts +44 -64
  111. package/src/__tests__/platform-callback-registration.test.ts +21 -4
  112. package/src/__tests__/platform.test.ts +2 -1
  113. package/src/__tests__/playbook-execution.test.ts +0 -43
  114. package/src/__tests__/plugin-tool-contribution.test.ts +47 -0
  115. package/src/__tests__/prechat-onboarding-contract.test.ts +214 -27
  116. package/src/__tests__/provider-tool-name.test.ts +23 -0
  117. package/src/__tests__/relay-server.test.ts +60 -5
  118. package/src/__tests__/runtime-events-sse.test.ts +4 -8
  119. package/src/__tests__/scheduler-disk-pressure.test.ts +148 -0
  120. package/src/__tests__/secret-ingress-http.test.ts +0 -1
  121. package/src/__tests__/secret-prompt-log-hygiene.test.ts +7 -5
  122. package/src/__tests__/secret-prompter-channel-fallback.test.ts +7 -5
  123. package/src/__tests__/secret-response-routing.test.ts +7 -5
  124. package/src/__tests__/server-history-render.test.ts +82 -0
  125. package/src/__tests__/skill-include-graph.test.ts +31 -0
  126. package/src/__tests__/skill-load-tool.test.ts +44 -16
  127. package/src/__tests__/skills.test.ts +39 -0
  128. package/src/__tests__/suggestion-routes.test.ts +46 -0
  129. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -42
  130. package/src/__tests__/tool-executor.test.ts +155 -0
  131. package/src/__tests__/twilio-validation.test.ts +2 -2
  132. package/src/__tests__/voice-session-bridge.test.ts +3 -0
  133. package/src/__tests__/workspace-migration-065-bump-stale-heartbeat-interval.test.ts +122 -0
  134. package/src/__tests__/workspace-migration-066-seed-heartbeat-callsite-cost-default.test.ts +285 -0
  135. package/src/__tests__/workspace-migration-068-release-notes-local-timezone.test.ts +90 -0
  136. package/src/__tests__/workspace-migration-069-seed-onboarding-threads.test.ts +120 -0
  137. package/src/__tests__/workspace-migration-071-remove-safe-storage-release-note.test.ts +206 -0
  138. package/src/__tests__/workspace-migration-safe-storage-limits-release.test.ts +78 -0
  139. package/src/agent/loop.ts +11 -0
  140. package/src/approvals/guardian-request-resolvers.ts +3 -32
  141. package/src/backup/snapshot-lock.ts +2 -27
  142. package/src/bundler/compiler-tools.ts +3 -2
  143. package/src/calls/call-constants.ts +5 -8
  144. package/src/calls/call-controller.ts +130 -67
  145. package/src/calls/call-conversation-messages.ts +46 -10
  146. package/src/calls/relay-server.ts +7 -1
  147. package/src/calls/voice-session-bridge.ts +1 -1
  148. package/src/cli/commands/__tests__/webhooks.test.ts +0 -4
  149. package/src/cli/commands/bash.ts +35 -108
  150. package/src/cli/commands/contacts.ts +64 -25
  151. package/src/cli/commands/credentials.ts +56 -0
  152. package/src/cli/commands/memory-v2.ts +11 -10
  153. package/src/cli/commands/oauth/__tests__/connect.test.ts +401 -219
  154. package/src/cli/commands/oauth/connect.ts +124 -40
  155. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -3
  156. package/src/cli/commands/platform/__tests__/connect.test.ts +7 -1
  157. package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -1
  158. package/src/cli/commands/platform/__tests__/status.test.ts +103 -6
  159. package/src/cli/commands/platform/index.ts +16 -7
  160. package/src/cli/commands/status.ts +57 -0
  161. package/src/cli/program.ts +4 -2
  162. package/src/config/assistant-feature-flags.ts +13 -3
  163. package/src/config/bundled-skills/app-builder/SKILL.md +1 -3
  164. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +4 -3
  165. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +13 -7
  166. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +2 -2
  167. package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +2 -2
  168. package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +2 -2
  169. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +2 -2
  170. package/src/config/env.ts +0 -8
  171. package/src/config/feature-flag-registry.json +13 -5
  172. package/src/config/loader.ts +199 -27
  173. package/src/config/schemas/__tests__/memory-v2.test.ts +10 -5
  174. package/src/config/schemas/call-site-catalog.ts +14 -0
  175. package/src/config/schemas/channels.ts +0 -5
  176. package/src/config/schemas/heartbeat.ts +1 -1
  177. package/src/config/schemas/llm.ts +2 -0
  178. package/src/config/schemas/memory-lifecycle.ts +13 -0
  179. package/src/config/schemas/memory-v2.ts +76 -12
  180. package/src/config/schemas/platform.ts +43 -3
  181. package/src/config/schemas/services.ts +28 -0
  182. package/src/config/seed-inference-profiles.ts +230 -33
  183. package/src/contacts/contact-store.ts +0 -25
  184. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +32 -0
  185. package/src/daemon/__tests__/conversation-tool-setup.test.ts +86 -25
  186. package/src/daemon/assistant-attachments.ts +4 -4
  187. package/src/daemon/config-watcher.ts +85 -57
  188. package/src/daemon/conversation-agent-loop-handlers.ts +38 -0
  189. package/src/daemon/conversation-agent-loop.ts +183 -43
  190. package/src/daemon/conversation-error.ts +87 -15
  191. package/src/daemon/conversation-lifecycle.ts +22 -10
  192. package/src/daemon/conversation-process.ts +8 -0
  193. package/src/daemon/conversation-runtime-assembly.ts +26 -0
  194. package/src/daemon/conversation-store.ts +2 -2
  195. package/src/daemon/conversation-surfaces.ts +211 -29
  196. package/src/daemon/conversation-tool-setup.ts +66 -19
  197. package/src/daemon/conversation.ts +18 -23
  198. package/src/daemon/date-context.ts +71 -22
  199. package/src/daemon/disk-pressure-background-gate.ts +73 -0
  200. package/src/daemon/disk-pressure-guard.ts +343 -0
  201. package/src/daemon/disk-pressure-policy.ts +163 -0
  202. package/src/daemon/handlers/shared.ts +26 -1
  203. package/src/daemon/handlers/skills.ts +3 -4
  204. package/src/daemon/host-app-control-proxy.ts +137 -41
  205. package/src/daemon/host-bash-proxy.ts +47 -22
  206. package/src/daemon/host-browser-proxy.ts +1 -1
  207. package/src/daemon/host-cu-proxy.ts +50 -4
  208. package/src/daemon/host-file-proxy.ts +44 -8
  209. package/src/daemon/host-transfer-proxy.ts +97 -6
  210. package/src/daemon/lifecycle.ts +167 -101
  211. package/src/daemon/meet-host-supervisor.ts +4 -4
  212. package/src/daemon/meet-manifest-loader.ts +0 -1
  213. package/src/daemon/memory-v2-startup.ts +66 -15
  214. package/src/daemon/message-protocol.ts +3 -0
  215. package/src/daemon/message-types/conversations.ts +4 -0
  216. package/src/daemon/message-types/disk-pressure.ts +9 -0
  217. package/src/daemon/message-types/messages.ts +22 -1
  218. package/src/daemon/profiler-run-store.ts +5 -5
  219. package/src/daemon/tool-setup-types.ts +2 -2
  220. package/src/documents/document-store.ts +119 -0
  221. package/src/filing/filing-service.ts +29 -5
  222. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +9 -16
  223. package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +36 -0
  224. package/src/heartbeat/heartbeat-run-store.ts +13 -0
  225. package/src/heartbeat/heartbeat-service.ts +205 -31
  226. package/src/home/feed-scheduler.ts +18 -0
  227. package/src/inbound/platform-callback-registration.ts +8 -15
  228. package/src/ipc/__tests__/clients-list-ipc.test.ts +169 -0
  229. package/src/ipc/assistant-server.ts +149 -38
  230. package/src/ipc/gateway-client.ts +37 -3
  231. package/src/ipc/skill-server.ts +99 -42
  232. package/src/live-voice/live-voice-archive.ts +4 -4
  233. package/src/live-voice/protocol.ts +5 -7
  234. package/src/media/image-service.ts +1 -7
  235. package/src/memory/__tests__/fixtures/memory-v2-activation-fixtures.ts +21 -13
  236. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +34 -51
  237. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +0 -6
  238. package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +272 -0
  239. package/src/memory/admin.ts +5 -9
  240. package/src/memory/context-search/agent-runner.ts +19 -2
  241. package/src/memory/context-search/sources/conversations.ts +2 -11
  242. package/src/memory/context-search/sources/memory-v2.ts +1 -16
  243. package/src/memory/context-search/sources/memory.ts +2 -3
  244. package/src/memory/context-search/sources/pkb.ts +2 -3
  245. package/src/memory/context-search/types.ts +0 -1
  246. package/src/memory/conversation-crud.ts +4 -12
  247. package/src/memory/db-init.ts +2 -0
  248. package/src/memory/embedding-runtime-manager.ts +119 -5
  249. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +136 -82
  250. package/src/memory/graph/__tests__/handle-remember-v2.test.ts +11 -26
  251. package/src/memory/graph/conversation-graph-memory.ts +72 -61
  252. package/src/memory/graph/extraction.ts +1 -3
  253. package/src/memory/graph/graph-search.test.ts +11 -67
  254. package/src/memory/graph/graph-search.ts +4 -24
  255. package/src/memory/graph/retriever.test.ts +12 -1
  256. package/src/memory/graph/retriever.ts +10 -15
  257. package/src/memory/graph/tool-handlers.ts +3 -4
  258. package/src/memory/graph/tools.ts +4 -4
  259. package/src/memory/indexer.ts +53 -45
  260. package/src/memory/job-handlers/backfill.ts +2 -11
  261. package/src/memory/job-handlers/cleanup.ts +43 -0
  262. package/src/memory/job-handlers/embedding.ts +6 -8
  263. package/src/memory/job-handlers/summarization.ts +2 -7
  264. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +116 -0
  265. package/src/memory/jobs/embed-concept-page.ts +223 -87
  266. package/src/memory/jobs-store.ts +48 -0
  267. package/src/memory/jobs-worker.ts +85 -43
  268. package/src/memory/memory-v2-activation-log-store.ts +32 -14
  269. package/src/memory/memory-v2-concept-frequency.ts +169 -0
  270. package/src/memory/migrations/239-trace-events-created-at-index.ts +18 -0
  271. package/src/memory/migrations/index.ts +1 -0
  272. package/src/memory/pkb/pkb-search.test.ts +7 -0
  273. package/src/memory/pkb/pkb-search.ts +4 -5
  274. package/src/memory/qdrant-client.ts +3 -13
  275. package/src/memory/rerank-local.ts +374 -0
  276. package/src/memory/search/semantic.ts +10 -72
  277. package/src/memory/trace-event-store.ts +1 -17
  278. package/src/memory/v2/__tests__/activation.test.ts +346 -255
  279. package/src/memory/v2/__tests__/consolidation-job.test.ts +61 -40
  280. package/src/memory/v2/__tests__/injection.test.ts +297 -190
  281. package/src/memory/v2/__tests__/prompts-consolidation.test.ts +61 -2
  282. package/src/memory/v2/__tests__/qdrant.test.ts +326 -9
  283. package/src/memory/v2/__tests__/reranker.test.ts +338 -0
  284. package/src/memory/v2/__tests__/sim.test.ts +113 -196
  285. package/src/memory/v2/__tests__/skill-store.test.ts +71 -65
  286. package/src/memory/v2/__tests__/static-context.test.ts +77 -14
  287. package/src/memory/v2/__tests__/sweep-job.test.ts +19 -33
  288. package/src/memory/v2/activation.ts +149 -156
  289. package/src/memory/v2/consolidation-job.ts +69 -20
  290. package/src/memory/v2/injection.ts +75 -68
  291. package/src/memory/v2/page-store.ts +39 -0
  292. package/src/memory/v2/prompts/consolidation.ts +41 -1
  293. package/src/memory/v2/qdrant.ts +306 -46
  294. package/src/memory/v2/reranker.ts +177 -0
  295. package/src/memory/v2/sim.ts +77 -110
  296. package/src/memory/v2/skill-content.ts +4 -3
  297. package/src/memory/v2/skill-store.ts +82 -59
  298. package/src/memory/v2/static-context.ts +26 -8
  299. package/src/memory/v2/sweep-job.ts +5 -6
  300. package/src/memory/v2/types.ts +17 -10
  301. package/src/notifications/copy-composer.ts +47 -0
  302. package/src/notifications/decision-engine.ts +46 -0
  303. package/src/notifications/signal.ts +4 -0
  304. package/src/oauth/AGENTS.md +3 -1
  305. package/src/oauth/__tests__/oauth-connect-state.test.ts +137 -0
  306. package/src/oauth/connect-orchestrator.ts +2 -0
  307. package/src/oauth/connection-resolver.test.ts +66 -1
  308. package/src/oauth/connection-resolver.ts +55 -1
  309. package/src/oauth/oauth-connect-state.ts +77 -0
  310. package/src/oauth/seed-providers.ts +58 -1
  311. package/src/permissions/gateway-threshold-reader.ts +116 -8
  312. package/src/permissions/prompter.ts +86 -96
  313. package/src/permissions/secret-prompter.ts +31 -31
  314. package/src/plugins/defaults/injectors.ts +36 -4
  315. package/src/plugins/defaults/memory-retrieval.ts +5 -6
  316. package/src/plugins/types.ts +7 -0
  317. package/src/proactive-artifact/aux-message-injector.ts +74 -0
  318. package/src/proactive-artifact/decision.test.ts +226 -0
  319. package/src/proactive-artifact/decision.ts +165 -0
  320. package/src/proactive-artifact/index.ts +7 -0
  321. package/src/proactive-artifact/job.test.ts +914 -0
  322. package/src/proactive-artifact/job.ts +366 -0
  323. package/src/proactive-artifact/message-copy.ts +58 -0
  324. package/src/proactive-artifact/trigger-state.test.ts +277 -0
  325. package/src/proactive-artifact/trigger-state.ts +119 -0
  326. package/src/prompts/normalize-onboarding.ts +80 -0
  327. package/src/prompts/persona-resolver.ts +101 -9
  328. package/src/prompts/system-prompt.ts +21 -7
  329. package/src/prompts/templates/BOOTSTRAP.md +13 -5
  330. package/src/prompts/templates/SOUL.md +13 -28
  331. package/src/providers/__tests__/retry-callsite.test.ts +222 -1
  332. package/src/providers/model-intents.ts +7 -0
  333. package/src/providers/openrouter/client.ts +8 -0
  334. package/src/providers/retry.ts +50 -0
  335. package/src/providers/types.ts +1 -0
  336. package/src/runtime/__tests__/agent-wake.test.ts +456 -3
  337. package/src/runtime/agent-wake.ts +238 -100
  338. package/src/runtime/assistant-event-hub.ts +36 -6
  339. package/src/runtime/assistant-event.ts +0 -1
  340. package/src/runtime/auth/__tests__/route-policy.test.ts +64 -0
  341. package/src/runtime/auth/route-policy.ts +15 -1
  342. package/src/runtime/auth/same-actor.ts +216 -0
  343. package/src/runtime/channel-approvals.ts +3 -2
  344. package/src/runtime/channel-retry-sweep.ts +65 -1
  345. package/src/runtime/local-actor-identity.ts +52 -11
  346. package/src/runtime/pending-interactions.ts +27 -15
  347. package/src/runtime/routes/__tests__/client-routes.test.ts +155 -0
  348. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +0 -5
  349. package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +1 -1
  350. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +147 -0
  351. package/src/runtime/routes/approval-routes.ts +7 -3
  352. package/src/runtime/routes/client-routes.ts +20 -2
  353. package/src/runtime/routes/consolidation-routes.ts +8 -9
  354. package/src/runtime/routes/contact-routes.ts +0 -25
  355. package/src/runtime/routes/conversation-query-routes.ts +44 -1
  356. package/src/runtime/routes/conversation-routes.ts +35 -26
  357. package/src/runtime/routes/debug-bash-routes.ts +165 -0
  358. package/src/runtime/routes/disk-pressure-routes.ts +121 -0
  359. package/src/runtime/routes/document-pdf-renderer.ts +6 -2
  360. package/src/runtime/routes/documents-routes.ts +2 -75
  361. package/src/runtime/routes/events-routes.ts +41 -9
  362. package/src/runtime/routes/filing-routes.ts +2 -3
  363. package/src/runtime/routes/host-bash-routes.ts +23 -3
  364. package/src/runtime/routes/host-cu-routes.ts +33 -6
  365. package/src/runtime/routes/host-file-routes.ts +32 -6
  366. package/src/runtime/routes/host-transfer-routes.ts +79 -16
  367. package/src/runtime/routes/identity-routes.ts +7 -138
  368. package/src/runtime/routes/inbound-message-handler.ts +77 -12
  369. package/src/runtime/routes/index.ts +6 -0
  370. package/src/runtime/routes/memory-item-routes.test.ts +37 -17
  371. package/src/runtime/routes/memory-item-routes.ts +5 -6
  372. package/src/runtime/routes/memory-v2-routes.ts +136 -17
  373. package/src/runtime/routes/oauth-connect-routes.ts +153 -0
  374. package/src/runtime/verification-outbound-actions.ts +4 -4
  375. package/src/schedule/run-script.ts +37 -5
  376. package/src/schedule/scheduler.ts +20 -1
  377. package/src/security/encrypted-store.ts +2 -0
  378. package/src/security/secure-keys.ts +55 -0
  379. package/src/skills/include-graph.ts +35 -13
  380. package/src/skills/remote-skill-policy.ts +4 -10
  381. package/src/subagent/index.ts +1 -7
  382. package/src/subagent/manager.ts +1 -15
  383. package/src/tasks/task-runner.ts +0 -1
  384. package/src/tasks/task-store.ts +0 -3
  385. package/src/tools/background-tool-registry.ts +17 -3
  386. package/src/tools/document/document-tool.ts +20 -0
  387. package/src/tools/executor.ts +18 -2
  388. package/src/tools/host-filesystem/edit.test.ts +151 -0
  389. package/src/tools/host-filesystem/edit.ts +43 -1
  390. package/src/tools/host-filesystem/read.test.ts +129 -0
  391. package/src/tools/host-filesystem/read.ts +43 -1
  392. package/src/tools/host-filesystem/transfer.test.ts +127 -2
  393. package/src/tools/host-filesystem/transfer.ts +56 -11
  394. package/src/tools/host-filesystem/write.test.ts +134 -0
  395. package/src/tools/host-filesystem/write.ts +43 -1
  396. package/src/tools/host-terminal/host-shell.ts +13 -6
  397. package/src/tools/mcp/mcp-tool-factory.ts +2 -1
  398. package/src/tools/memory/register.test.ts +14 -9
  399. package/src/tools/memory/register.ts +1 -2
  400. package/src/tools/permission-checker.ts +15 -0
  401. package/src/tools/provider-tool-name.ts +28 -0
  402. package/src/tools/registry.ts +30 -9
  403. package/src/tools/skills/load.ts +24 -20
  404. package/src/tools/terminal/shell.ts +9 -1
  405. package/src/tools/tool-approval-handler.ts +31 -6
  406. package/src/tools/tool-name-aliases.ts +19 -0
  407. package/src/tools/types.ts +43 -3
  408. package/src/tts/provider-catalog.ts +3 -5
  409. package/src/util/disk-usage.ts +138 -0
  410. package/src/util/platform.ts +21 -11
  411. package/src/util/process-liveness.ts +26 -0
  412. package/src/workspace/heartbeat-service.ts +19 -0
  413. package/src/workspace/migrations/065-bump-stale-heartbeat-interval.ts +60 -0
  414. package/src/workspace/migrations/066-seed-heartbeat-callsite-cost-default.ts +146 -0
  415. package/src/workspace/migrations/067-release-notes-safe-storage-limits.ts +14 -0
  416. package/src/workspace/migrations/068-release-notes-local-timezone.ts +65 -0
  417. package/src/workspace/migrations/069-seed-onboarding-threads.ts +28 -0
  418. package/src/workspace/migrations/070-memory-v2-summary-schema-rebuild.ts +31 -0
  419. package/src/workspace/migrations/071-remove-safe-storage-release-note.ts +111 -0
  420. package/src/workspace/migrations/registry.ts +14 -0
  421. package/src/__tests__/conversation-tool-setup-memory-scope.test.ts +0 -167
  422. package/src/memory/v2/__tests__/skill-qdrant.test.ts +0 -657
  423. package/src/memory/v2/skill-qdrant.ts +0 -404
  424. package/src/signals/bash.ts +0 -198
@@ -0,0 +1,138 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { existsSync, statfsSync } from "node:fs";
3
+
4
+ import { getMinikubeStorageSize } from "../config/env-registry.js";
5
+ import { getWorkspaceDir } from "./platform.js";
6
+
7
+ export interface DiskUsageInfo {
8
+ path: string;
9
+ totalMb: number;
10
+ usedMb: number;
11
+ freeMb: number;
12
+ }
13
+
14
+ /**
15
+ * Measure the on-disk usage of one or more directory paths using `du -sb`.
16
+ * Returns the sum of all paths in bytes, or null on failure.
17
+ */
18
+ function getDirectorySizeBytes(paths: string[]): number | null {
19
+ try {
20
+ const existing = paths.filter((p) => existsSync(p));
21
+ if (existing.length === 0) return null;
22
+ const result = spawnSync("du", ["-sb", ...existing], {
23
+ encoding: "utf-8",
24
+ timeout: 30_000,
25
+ });
26
+ if (result.status !== 0) return null;
27
+ let total = 0;
28
+ for (const line of result.stdout.trim().split("\n")) {
29
+ const size = parseInt(line.split("\t")[0], 10);
30
+ if (!isNaN(size) && size > 0) total += size;
31
+ }
32
+ return total > 0 ? total : null;
33
+ } catch {
34
+ return null;
35
+ }
36
+ }
37
+
38
+ const DU_CACHE_TTL_MS = 60_000;
39
+ let duCacheValue: number | null = null;
40
+ let duCacheTime = 0;
41
+ let duCachePaths: string | null = null;
42
+
43
+ function getCachedDirectorySizeBytes(paths: string[]): number | null {
44
+ const key = paths.join("\0");
45
+ const now = Date.now();
46
+ if (duCachePaths === key && now - duCacheTime < DU_CACHE_TTL_MS) {
47
+ return duCacheValue;
48
+ }
49
+ duCacheValue = getDirectorySizeBytes(paths);
50
+ duCacheTime = now;
51
+ duCachePaths = key;
52
+ return duCacheValue;
53
+ }
54
+
55
+ export function __resetDiskUsageCacheForTests(): void {
56
+ duCacheValue = null;
57
+ duCacheTime = 0;
58
+ duCachePaths = null;
59
+ }
60
+
61
+ export function getDiskUsageInfo(): DiskUsageInfo | null {
62
+ try {
63
+ const wsDir = getWorkspaceDir();
64
+ const diskPath = existsSync(wsDir) ? wsDir : "/";
65
+ const stats = statfsSync(diskPath);
66
+ const fsTotalBytes = stats.bsize * stats.blocks;
67
+ const fsFreeBytes = stats.bsize * stats.bavail;
68
+ const bytesToMb = (b: number) =>
69
+ Math.round((b / (1024 * 1024)) * 100) / 100;
70
+
71
+ // Minikube mode: the platform passes the PVC storage size so we can
72
+ // report accurate capacity. On hostPath-backed PVCs statfsSync reports
73
+ // the host's entire filesystem rather than the PVC. Detect this by
74
+ // comparing filesystem size against PVC size — if the filesystem is
75
+ // larger, measure actual directory usage with `du` instead.
76
+ const storageSizeRaw = getMinikubeStorageSize();
77
+ if (storageSizeRaw) {
78
+ const pvcTotalBytes = parseK8sMemoryBytes(storageSizeRaw);
79
+ if (pvcTotalBytes !== null && fsTotalBytes > pvcTotalBytes * 1.1) {
80
+ const volumePaths = [diskPath];
81
+ if (diskPath !== "/data" && existsSync("/data")) {
82
+ volumePaths.push("/data");
83
+ }
84
+ const usedBytes = getCachedDirectorySizeBytes(volumePaths);
85
+ if (usedBytes !== null) {
86
+ return {
87
+ path: diskPath,
88
+ totalMb: bytesToMb(pvcTotalBytes),
89
+ usedMb: bytesToMb(usedBytes),
90
+ freeMb: bytesToMb(Math.max(0, pvcTotalBytes - usedBytes)),
91
+ };
92
+ }
93
+ }
94
+ }
95
+
96
+ return {
97
+ path: diskPath,
98
+ totalMb: bytesToMb(fsTotalBytes),
99
+ usedMb: bytesToMb(fsTotalBytes - fsFreeBytes),
100
+ freeMb: bytesToMb(fsFreeBytes),
101
+ };
102
+ } catch {
103
+ return null;
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Parse a Kubernetes-style memory string (e.g. "3Gi", "512Mi", "1G") into bytes.
109
+ * Returns null if the value is not a recognized format.
110
+ */
111
+ export function parseK8sMemoryBytes(value: string): number | null {
112
+ const match = value
113
+ .trim()
114
+ .match(/^(\d+(?:\.\d+)?)\s*(Ki|Mi|Gi|Ti|Pi|Ei|k|M|G|T|P|E|m)?$/);
115
+ if (!match) return null;
116
+ const num = parseFloat(match[1]);
117
+ const unit = match[2] ?? "";
118
+ const multipliers: Record<string, number> = {
119
+ "": 1,
120
+ m: 1e-3,
121
+ k: 1e3,
122
+ M: 1e6,
123
+ G: 1e9,
124
+ T: 1e12,
125
+ P: 1e15,
126
+ E: 1e18,
127
+ Ki: 1024,
128
+ Mi: 1024 ** 2,
129
+ Gi: 1024 ** 3,
130
+ Ti: 1024 ** 4,
131
+ Pi: 1024 ** 5,
132
+ Ei: 1024 ** 6,
133
+ };
134
+ const mult = multipliers[unit];
135
+ if (mult === undefined) return null;
136
+ const bytes = Math.round(num * mult);
137
+ return bytes > 0 ? bytes : null;
138
+ }
@@ -7,14 +7,25 @@ import { getWorkspaceDirOverride } from "../config/env-registry.js";
7
7
  /**
8
8
  * The daemon's root data directory (`~/.vellum`).
9
9
  *
10
- * Multi-instance path relocation is handled by the CLI when spawning the
11
- * daemon (via `VELLUM_WORKSPACE_DIR` and `GATEWAY_SECURITY_DIR`); the
12
- * assistant itself never reads an env var for this.
10
+ * Used as a fallback when `VELLUM_WORKSPACE_DIR` is not set, and as a
11
+ * stable constant for paths (like `.env`) that intentionally live at the
12
+ * host home directory regardless of workspace relocation.
13
13
  */
14
14
  const VELLUM_ROOT = join(homedir(), ".vellum");
15
15
 
16
- /** Returns the daemon's root data directory (`~/.vellum`). */
16
+ /**
17
+ * Returns the Vellum root directory.
18
+ *
19
+ * Resolution order (mirrors workspace/migrations/utils.ts):
20
+ * 1. Parent of VELLUM_WORKSPACE_DIR — e.g. /data/.vellum/workspace → /data/.vellum
21
+ * 2. If that parent is "/" (workspace at top level), fall back to ~/.vellum
22
+ */
17
23
  export function vellumRoot(): string {
24
+ const override = getWorkspaceDirOverride();
25
+ if (override) {
26
+ const parent = dirname(override);
27
+ if (parent !== "/") return parent;
28
+ }
18
29
  return VELLUM_ROOT;
19
30
  }
20
31
 
@@ -160,17 +171,16 @@ export function getHistoryPath(): string {
160
171
  }
161
172
 
162
173
  /**
163
- * Returns the protected directory (~/.vellum/protected). Security-sensitive
164
- * files — trust rules, encrypted credential store, signing keys, feature-flag
165
- * overrides, device approval lists — live here.
174
+ * Returns the protected directory. Security-sensitive files — trust rules,
175
+ * encrypted credential store, signing keys, feature-flag overrides, device
176
+ * approval lists — live here.
166
177
  *
167
178
  * This directory is:
168
- * - Outside the workspace
169
179
  * - Outside the sandbox write boundary (tools cannot modify it)
170
180
  * - Skipped in containerized mode (credentials via CES, trust via gateway)
171
181
  */
172
182
  export function getProtectedDir(): string {
173
- return join(VELLUM_ROOT, "protected");
183
+ return join(vellumRoot(), "protected");
174
184
  }
175
185
 
176
186
  /** Returns ~/.vellum/workspace/signals — the directory for IPC signal files. */
@@ -204,7 +214,7 @@ export function getBinDir(): string {
204
214
 
205
215
  /** Returns the path to the dot-env file (~/.vellum/.env). Stays at root because it contains secrets. */
206
216
  export function getDotEnvPath(): string {
207
- return join(VELLUM_ROOT, ".env");
217
+ return join(vellumRoot(), ".env");
208
218
  }
209
219
 
210
220
  /** Returns the path to the embed-worker PID file (~/.vellum/workspace/embed-worker.pid). */
@@ -368,7 +378,7 @@ export function getBundledBunPath(): string | undefined {
368
378
  }
369
379
 
370
380
  export function ensureDataDir(): void {
371
- const root = VELLUM_ROOT;
381
+ const root = vellumRoot();
372
382
  const workspace = getWorkspaceDir();
373
383
  const wsData = join(workspace, "data");
374
384
  const dirs = [
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Cross-process liveness probe shared by file-locking helpers.
3
+ *
4
+ * Uses `kill(pid, 0)`, which sends no signal but probes the OS for whether a
5
+ * process exists and whether the caller has permission to signal it. Returns
6
+ * `false` for obviously invalid PIDs and for any error indicating the process
7
+ * is gone (most commonly ESRCH). Returns `true` for ESRCH-negative results
8
+ * (process exists) and for EPERM (process exists but is owned by another user
9
+ * — still alive, still must not be taken over).
10
+ */
11
+
12
+ import { kill } from "node:process";
13
+
14
+ export function isProcessAlive(pid: number): boolean {
15
+ if (!Number.isInteger(pid) || pid <= 0) return false;
16
+ try {
17
+ kill(pid, 0);
18
+ return true;
19
+ } catch (err) {
20
+ const code = (err as NodeJS.ErrnoException).code;
21
+ // EPERM means the PID exists but we cannot signal it — treat as alive so
22
+ // we don't accidentally take over another user's lock.
23
+ if (code === "EPERM") return true;
24
+ return false;
25
+ }
26
+ }
@@ -1,3 +1,8 @@
1
+ import {
2
+ checkDiskPressureBackgroundGate,
3
+ diskPressureBackgroundSkipLogFields,
4
+ shouldLogDiskPressureBackgroundSkip,
5
+ } from "../daemon/disk-pressure-background-gate.js";
1
6
  import { getLogger } from "../util/logger.js";
2
7
  import { getEnrichmentService } from "./commit-message-enrichment-service.js";
3
8
  import {
@@ -137,6 +142,20 @@ export class WorkspaceHeartbeatService {
137
142
  return { checked: 0, committed: 0, skipped: 0, failed: 0 };
138
143
  }
139
144
 
145
+ const diskPressureGate = checkDiskPressureBackgroundGate("background-work");
146
+ if (diskPressureGate.action === "skip") {
147
+ if (shouldLogDiskPressureBackgroundSkip("workspace-heartbeat")) {
148
+ log.warn(
149
+ {
150
+ source: "workspace-heartbeat",
151
+ ...diskPressureBackgroundSkipLogFields(diskPressureGate),
152
+ },
153
+ "Workspace heartbeat skipped during disk pressure cleanup mode",
154
+ );
155
+ }
156
+ return { checked: 0, committed: 0, skipped: 0, failed: 0 };
157
+ }
158
+
140
159
  const doCheck = async (): Promise<HeartbeatCheckResult> => {
141
160
  const result: HeartbeatCheckResult = {
142
161
  checked: 0,
@@ -0,0 +1,60 @@
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+
4
+ import type { WorkspaceMigration } from "./types.js";
5
+
6
+ const THIRTY_MINUTES_MS = 30 * 60 * 1000;
7
+ const LEGACY_DEFAULT_INTERVALS_MS = new Set([
8
+ 3 * 60 * 60 * 1000,
9
+ 6 * 60 * 60 * 1000,
10
+ ]);
11
+
12
+ /**
13
+ * Bump stale baked heartbeat defaults to 30 minutes.
14
+ *
15
+ * Older first-launch config files materialized the then-current heartbeat
16
+ * default into `config.json`, so changing the schema default alone would not
17
+ * affect existing default users. A workspace could have intentionally selected
18
+ * one of these exact intervals; product intent is still to move legacy 3h/6h
19
+ * heartbeat schedules to the 30-minute default, and those users can reset the
20
+ * interval after upgrade.
21
+ */
22
+ export const bumpStaleHeartbeatIntervalMigration: WorkspaceMigration = {
23
+ id: "065-bump-stale-heartbeat-interval",
24
+ description:
25
+ "Bump legacy heartbeat.intervalMs defaults of 3h/6h to the current 30-minute default",
26
+ run(workspaceDir: string): void {
27
+ const configPath = join(workspaceDir, "config.json");
28
+ if (!existsSync(configPath)) return;
29
+
30
+ let config: Record<string, unknown>;
31
+ try {
32
+ const raw = JSON.parse(readFileSync(configPath, "utf-8"));
33
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return;
34
+ config = raw as Record<string, unknown>;
35
+ } catch {
36
+ return;
37
+ }
38
+
39
+ const heartbeat = config.heartbeat;
40
+ if (!heartbeat || typeof heartbeat !== "object" || Array.isArray(heartbeat))
41
+ return;
42
+
43
+ const heartbeatConfig = heartbeat as Record<string, unknown>;
44
+ const intervalMs = heartbeatConfig.intervalMs;
45
+ if (
46
+ typeof intervalMs !== "number" ||
47
+ !LEGACY_DEFAULT_INTERVALS_MS.has(intervalMs)
48
+ ) {
49
+ return;
50
+ }
51
+
52
+ heartbeatConfig.intervalMs = THIRTY_MINUTES_MS;
53
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
54
+ },
55
+ down(_workspaceDir: string): void {
56
+ // Forward-only: the stale value may have been a schema-default artifact,
57
+ // while 30 minutes may also have been explicitly configured later. Without
58
+ // per-workspace state we cannot safely distinguish those cases.
59
+ },
60
+ };
@@ -0,0 +1,146 @@
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
+ * Seed a cheap, bounded default for the `heartbeatAgent` LLM call site.
8
+ *
9
+ * Heartbeats are default-on and now run frequently, so they should not inherit
10
+ * the workspace's active chat profile. The default managed profile is Sonnet
11
+ * with high effort and thinking enabled, which is appropriate for interactive
12
+ * chat but too expensive for a periodic background triage pass.
13
+ *
14
+ * Preserve user-owned model selection. If `heartbeatAgent` already has a
15
+ * `profile`, `provider`, or `model`, this migration leaves the entry unchanged
16
+ * so call-site leaves do not silently override the selected profile/model.
17
+ * Speed-only legacy entries from migration 038 are treated as defaultable.
18
+ */
19
+ export const seedHeartbeatCallsiteCostDefaultMigration: WorkspaceMigration = {
20
+ id: "066-seed-heartbeat-callsite-cost-default",
21
+ description:
22
+ "Seed cost-optimized defaults for the heartbeatAgent LLM call site",
23
+ run(workspaceDir: string): void {
24
+ if (process.env.VELLUM_DEFAULT_WORKSPACE_CONFIG_PATH) return;
25
+
26
+ const configPath = join(workspaceDir, "config.json");
27
+ const configExisted = existsSync(configPath);
28
+
29
+ let config: Record<string, unknown> = {};
30
+ if (configExisted) {
31
+ try {
32
+ const raw = JSON.parse(readFileSync(configPath, "utf-8"));
33
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return;
34
+ config = raw as Record<string, unknown>;
35
+ } catch {
36
+ return;
37
+ }
38
+ }
39
+
40
+ const llm = readObject(config.llm) ?? {};
41
+ const defaultBlock = readObject(llm.default);
42
+ const provider = readString(defaultBlock?.provider) ?? "anthropic";
43
+ const cheapModel = resolveCheapModel(provider);
44
+ if (cheapModel === undefined) return;
45
+
46
+ const callSites = readObject(llm.callSites) ?? {};
47
+ const existing = readObject(callSites.heartbeatAgent) ?? {};
48
+ if (hasExplicitModelSelection(existing)) return;
49
+
50
+ const seeded: Record<string, unknown> = { ...existing };
51
+ let changed = false;
52
+
53
+ const profiles = readObject(llm.profiles) ?? {};
54
+ const costProfile = readObject(profiles["cost-optimized"]);
55
+ if (readString(costProfile?.provider) === provider) {
56
+ seeded.profile = "cost-optimized";
57
+ } else {
58
+ seeded.provider = provider;
59
+ seeded.model = cheapModel;
60
+ }
61
+ changed = true;
62
+
63
+ changed = seedMissingLeaf(seeded, "maxTokens", 2048) || changed;
64
+ changed = seedMissingLeaf(seeded, "effort", "low") || changed;
65
+ changed = seedMissingLeaf(seeded, "temperature", 0) || changed;
66
+
67
+ const thinking = readObject(seeded.thinking) ?? {};
68
+ const seededThinking = { ...thinking };
69
+ const enabledChanged = seedMissingLeaf(seededThinking, "enabled", false);
70
+ const streamThinkingChanged = seedMissingLeaf(
71
+ seededThinking,
72
+ "streamThinking",
73
+ false,
74
+ );
75
+ const thinkingChanged = enabledChanged || streamThinkingChanged;
76
+ if (thinkingChanged || readObject(seeded.thinking) === null) {
77
+ seeded.thinking = seededThinking;
78
+ changed = true;
79
+ }
80
+
81
+ const contextWindow = readObject(seeded.contextWindow) ?? {};
82
+ const seededContextWindow = { ...contextWindow };
83
+ const contextChanged = seedMissingLeaf(
84
+ seededContextWindow,
85
+ "maxInputTokens",
86
+ 16_000,
87
+ );
88
+ if (contextChanged || readObject(seeded.contextWindow) === null) {
89
+ seeded.contextWindow = seededContextWindow;
90
+ changed = true;
91
+ }
92
+
93
+ if (!changed) return;
94
+
95
+ callSites.heartbeatAgent = seeded;
96
+ llm.callSites = callSites;
97
+ config.llm = llm;
98
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
99
+ },
100
+ down(_workspaceDir: string): void {
101
+ // Forward-only: removing the seeded default would make frequent
102
+ // heartbeats inherit the user's potentially expensive chat profile again.
103
+ },
104
+ };
105
+
106
+ // ---------------------------------------------------------------------------
107
+ // Helpers — self-contained per workspace migrations AGENTS.md
108
+ // ---------------------------------------------------------------------------
109
+
110
+ const CHEAP_MODELS_BY_PROVIDER: Record<string, string> = {
111
+ anthropic: "claude-haiku-4-5-20251001",
112
+ openai: "gpt-5.4-nano",
113
+ gemini: "gemini-3-flash",
114
+ ollama: "llama3.2",
115
+ fireworks: "accounts/fireworks/models/kimi-k2p5",
116
+ openrouter: "anthropic/claude-haiku-4.5",
117
+ };
118
+
119
+ function resolveCheapModel(provider: string): string | undefined {
120
+ return CHEAP_MODELS_BY_PROVIDER[provider];
121
+ }
122
+
123
+ function hasExplicitModelSelection(value: Record<string, unknown>): boolean {
124
+ return "profile" in value || "provider" in value || "model" in value;
125
+ }
126
+
127
+ function seedMissingLeaf(
128
+ target: Record<string, unknown>,
129
+ key: string,
130
+ value: unknown,
131
+ ): boolean {
132
+ if (key in target) return false;
133
+ target[key] = value;
134
+ return true;
135
+ }
136
+
137
+ function readObject(value: unknown): Record<string, unknown> | null {
138
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
139
+ return null;
140
+ }
141
+ return value as Record<string, unknown>;
142
+ }
143
+
144
+ function readString(value: unknown): string | undefined {
145
+ return typeof value === "string" && value.length > 0 ? value : undefined;
146
+ }
@@ -0,0 +1,14 @@
1
+ import type { WorkspaceMigration } from "./types.js";
2
+
3
+ const MIGRATION_ID = "067-release-notes-safe-storage-limits";
4
+
5
+ export const releaseNotesSafeStorageLimitsMigration: WorkspaceMigration = {
6
+ id: MIGRATION_ID,
7
+ description: "Reserved migration slot for safe storage limits release notes",
8
+
9
+ run(_workspaceDir: string): void {
10
+ // Registered no-op slot retained for workspace migration checkpoint compatibility.
11
+ },
12
+
13
+ down(_workspaceDir: string): void {},
14
+ };
@@ -0,0 +1,65 @@
1
+ import {
2
+ appendFileSync,
3
+ existsSync,
4
+ readFileSync,
5
+ writeFileSync,
6
+ } from "node:fs";
7
+ import { join } from "node:path";
8
+
9
+ import { getLogger } from "../../util/logger.js";
10
+ import type { WorkspaceMigration } from "./types.js";
11
+
12
+ const log = getLogger("workspace-migration-068-release-notes-local-timezone");
13
+
14
+ const MIGRATION_ID = "068-release-notes-local-timezone";
15
+ const MARKER = `<!-- release-note-id:${MIGRATION_ID} -->`;
16
+
17
+ const RELEASE_NOTE = `${MARKER}
18
+ ## Local timezone grounding
19
+
20
+ The assistant now grounds \`current_time\` in your local timezone across clients,
21
+ instead of falling back to UTC when the client can report the device timezone.
22
+
23
+ Manual timezone overrides still win when configured, and the assistant can help
24
+ update a stale override after you confirm that your device timezone should be
25
+ used going forward.
26
+ `;
27
+
28
+ export const releaseNotesLocalTimezoneMigration: WorkspaceMigration = {
29
+ id: MIGRATION_ID,
30
+ description:
31
+ "Append release notes for local timezone grounding to UPDATES.md",
32
+
33
+ run(workspaceDir: string): void {
34
+ const updatesPath = join(workspaceDir, "UPDATES.md");
35
+
36
+ try {
37
+ if (existsSync(updatesPath)) {
38
+ const existing = readFileSync(updatesPath, "utf-8");
39
+ if (existing.includes(MARKER)) {
40
+ return;
41
+ }
42
+ const needsLeadingNewline = !existing.endsWith("\n\n");
43
+ const prefix = existing.endsWith("\n") ? "\n" : "\n\n";
44
+ appendFileSync(
45
+ updatesPath,
46
+ needsLeadingNewline ? `${prefix}${RELEASE_NOTE}` : RELEASE_NOTE,
47
+ "utf-8",
48
+ );
49
+ } else {
50
+ writeFileSync(updatesPath, RELEASE_NOTE, "utf-8");
51
+ }
52
+ log.info({ path: updatesPath }, "Appended local timezone release note");
53
+ } catch (err) {
54
+ log.warn(
55
+ { err, path: updatesPath },
56
+ "Failed to append local timezone release note to UPDATES.md",
57
+ );
58
+ }
59
+ },
60
+
61
+ down(_workspaceDir: string): void {
62
+ // Forward-only: UPDATES.md is a user-facing bulletin the assistant
63
+ // processes and deletes on its own.
64
+ },
65
+ };
@@ -0,0 +1,28 @@
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+
4
+ import type { WorkspaceMigration } from "./types.js";
5
+
6
+ const ONBOARDING_THREADS = `- Figure out what kind of personality would best mesh with your user. Figure out who you are and what your voice should be. Your choice should be DISTINCT and have CHARACTER. Once you've figured this out, rewrite SOUL.md and IDENTITY.md in your own voice to define who you are.
7
+ - Work with your user to set a custom avatar for yourself. This task is done once data/avatar/avatar-image.png exists.
8
+ - Ask your user if they use ChatGPT, Claude or another AI tool and offer to help them import memories from there.
9
+ - Suggest connecting to available messaging services like Slack or Telegram
10
+ `;
11
+
12
+ export const seedOnboardingThreadsMigration: WorkspaceMigration = {
13
+ id: "069-seed-onboarding-threads",
14
+ description:
15
+ "Seed memory/threads.md with onboarding tasks for brand new assistants",
16
+
17
+ run(workspaceDir: string): void {
18
+ const filePath = join(workspaceDir, "memory", "threads.md");
19
+ if (!existsSync(filePath)) return;
20
+ const current = readFileSync(filePath, "utf-8");
21
+ if (current.trim().length > 0) return;
22
+ writeFileSync(filePath, ONBOARDING_THREADS, "utf-8");
23
+ },
24
+
25
+ down(_workspaceDir: string): void {
26
+ // Forward-only: never delete user-visible memory content on rollback.
27
+ },
28
+ };
@@ -0,0 +1,31 @@
1
+ import type { WorkspaceMigration } from "./types.js";
2
+
3
+ /**
4
+ * Audit-only entry for the v2 concept-page schema upgrade introduced in PR
5
+ * #29823 (summary_dense / summary_sparse named vectors). The destructive
6
+ * collection rebuild and reembed enqueue both run inside the daemon at
7
+ * Qdrant init time — see `maybeRebuildMemoryV2Concepts` in
8
+ * `assistant/src/daemon/memory-v2-startup.ts`. The "exactly-once" fence is
9
+ * per-collection schema introspection, not per-workspace checkpoint, so
10
+ * users who wipe Qdrant separately still get re-rebuilt without resetting
11
+ * any workspace flag.
12
+ *
13
+ * This migration exists so the registry chronology records the schema
14
+ * upgrade alongside the release that introduced it.
15
+ */
16
+ export const memoryV2SummarySchemaRebuildMigration: WorkspaceMigration = {
17
+ id: "070-memory-v2-summary-schema-rebuild",
18
+ description:
19
+ "Audit entry: v2 concept-page Qdrant collection now carries summary_dense / summary_sparse named vectors. Rebuild + reembed handled by the daemon's startup hook.",
20
+
21
+ run(_workspaceDir: string): void {
22
+ // No-op: the actual rebuild lives in the Qdrant client layer where the
23
+ // collection schema is owned. Workspace migrations cannot touch Qdrant
24
+ // (they run before it starts and must be self-contained per
25
+ // assistant/src/workspace/migrations/AGENTS.md).
26
+ },
27
+
28
+ down(_workspaceDir: string): void {
29
+ // Forward-only: schema rollbacks happen outside the migration system.
30
+ },
31
+ };