@vellumai/assistant 0.5.6 → 0.5.8

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 (442) hide show
  1. package/.env.example +16 -2
  2. package/ARCHITECTURE.md +6 -75
  3. package/Dockerfile +3 -2
  4. package/README.md +0 -2
  5. package/bun.lock +0 -414
  6. package/docker-entrypoint.sh +9 -0
  7. package/docs/architecture/keychain-broker.md +45 -240
  8. package/docs/architecture/memory.md +13 -11
  9. package/docs/architecture/security.md +0 -17
  10. package/docs/credential-execution-service.md +2 -2
  11. package/node_modules/@vellumai/ces-contracts/package.json +1 -0
  12. package/node_modules/@vellumai/ces-contracts/src/error.ts +1 -1
  13. package/node_modules/@vellumai/ces-contracts/src/grants.ts +1 -1
  14. package/node_modules/@vellumai/ces-contracts/src/handles.ts +1 -1
  15. package/node_modules/@vellumai/ces-contracts/src/index.ts +1 -1
  16. package/node_modules/@vellumai/ces-contracts/src/rpc.ts +120 -1
  17. package/node_modules/@vellumai/credential-storage/package.json +1 -0
  18. package/node_modules/@vellumai/egress-proxy/package.json +1 -0
  19. package/package.json +2 -3
  20. package/src/__tests__/actor-token-service.test.ts +0 -114
  21. package/src/__tests__/approval-cascade.test.ts +0 -1
  22. package/src/__tests__/assistant-feature-flags-integration.test.ts +30 -29
  23. package/src/__tests__/browser-fill-credential.test.ts +1 -1
  24. package/src/__tests__/browser-skill-endstate.test.ts +6 -5
  25. package/src/__tests__/btw-routes.test.ts +0 -39
  26. package/src/__tests__/call-controller.test.ts +0 -1
  27. package/src/__tests__/call-domain.test.ts +0 -128
  28. package/src/__tests__/ces-rpc-credential-backend.test.ts +199 -0
  29. package/src/__tests__/ces-startup-timeout.test.ts +40 -0
  30. package/src/__tests__/channel-approval-routes.test.ts +0 -5
  31. package/src/__tests__/channel-readiness-service.test.ts +1 -60
  32. package/src/__tests__/checker.test.ts +4 -2
  33. package/src/__tests__/cli-command-risk-guard.test.ts +112 -0
  34. package/src/__tests__/config-schema-cmd.test.ts +0 -2
  35. package/src/__tests__/config-schema.test.ts +3 -1
  36. package/src/__tests__/conversation-abort-tool-results.test.ts +0 -1
  37. package/src/__tests__/conversation-agent-loop-overflow.test.ts +0 -2
  38. package/src/__tests__/conversation-agent-loop.test.ts +2 -4
  39. package/src/__tests__/conversation-attention-telegram.test.ts +0 -5
  40. package/src/__tests__/conversation-confirmation-signals.test.ts +0 -1
  41. package/src/__tests__/conversation-error.test.ts +15 -1
  42. package/src/__tests__/conversation-init.benchmark.test.ts +0 -2
  43. package/src/__tests__/conversation-messaging-secret-redirect.test.ts +1 -1
  44. package/src/__tests__/conversation-pre-run-repair.test.ts +0 -1
  45. package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -1
  46. package/src/__tests__/conversation-queue.test.ts +0 -1
  47. package/src/__tests__/conversation-skill-tools.test.ts +0 -54
  48. package/src/__tests__/conversation-slash-queue.test.ts +0 -1
  49. package/src/__tests__/conversation-slash-unknown.test.ts +0 -1
  50. package/src/__tests__/conversation-title-service.test.ts +87 -0
  51. package/src/__tests__/conversation-workspace-injection.test.ts +0 -1
  52. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -1
  53. package/src/__tests__/credential-execution-client.test.ts +5 -2
  54. package/src/__tests__/credential-execution-feature-gates.test.ts +59 -30
  55. package/src/__tests__/credential-execution-managed-contract.test.ts +35 -20
  56. package/src/__tests__/credential-security-e2e.test.ts +1 -67
  57. package/src/__tests__/credential-security-invariants.test.ts +6 -50
  58. package/src/__tests__/credentials-cli.test.ts +82 -3
  59. package/src/__tests__/daemon-credential-client.test.ts +123 -0
  60. package/src/__tests__/db-migration-rollback.test.ts +2015 -1
  61. package/src/__tests__/deterministic-verification-control-plane.test.ts +1 -0
  62. package/src/__tests__/docker-signing-key-bootstrap.test.ts +34 -143
  63. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +6 -4
  64. package/src/__tests__/gateway-client-managed-outbound.test.ts +79 -1
  65. package/src/__tests__/guardian-routing-state.test.ts +0 -5
  66. package/src/__tests__/host-shell-tool.test.ts +6 -7
  67. package/src/__tests__/http-user-message-parity.test.ts +3 -103
  68. package/src/__tests__/inbound-invite-redemption.test.ts +0 -4
  69. package/src/__tests__/inline-skill-load-permissions.test.ts +6 -8
  70. package/src/__tests__/intent-routing.test.ts +0 -13
  71. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +178 -0
  72. package/src/__tests__/journal-context.test.ts +335 -0
  73. package/src/__tests__/keychain-broker-client.test.ts +161 -22
  74. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -3
  75. package/src/__tests__/memory-jobs-worker-backoff.test.ts +150 -0
  76. package/src/__tests__/memory-lifecycle-e2e.test.ts +70 -25
  77. package/src/__tests__/memory-recall-quality.test.ts +48 -17
  78. package/src/__tests__/memory-regressions.test.ts +408 -363
  79. package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -3
  80. package/src/__tests__/migration-export-http.test.ts +2 -2
  81. package/src/__tests__/migration-import-commit-http.test.ts +2 -2
  82. package/src/__tests__/migration-import-preflight-http.test.ts +2 -2
  83. package/src/__tests__/migration-validate-http.test.ts +2 -2
  84. package/src/__tests__/non-member-access-request.test.ts +2 -7
  85. package/src/__tests__/notification-decision-fallback.test.ts +4 -0
  86. package/src/__tests__/notification-decision-identity.test.ts +4 -0
  87. package/src/__tests__/notification-decision-strategy.test.ts +71 -0
  88. package/src/__tests__/oauth-cli.test.ts +5 -1
  89. package/src/__tests__/permission-types.test.ts +1 -0
  90. package/src/__tests__/provider-commit-message-generator.test.ts +0 -37
  91. package/src/__tests__/provider-error-scenarios.test.ts +0 -267
  92. package/src/__tests__/provider-managed-proxy-integration.test.ts +5 -6
  93. package/src/__tests__/provider-streaming.benchmark.test.ts +2 -81
  94. package/src/__tests__/qdrant-manager.test.ts +28 -2
  95. package/src/__tests__/registry.test.ts +0 -6
  96. package/src/__tests__/relay-server.test.ts +1 -2
  97. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -4
  98. package/src/__tests__/script-proxy-injection-runtime.test.ts +1 -1
  99. package/src/__tests__/secret-onetime-send.test.ts +1 -1
  100. package/src/__tests__/secret-routes-managed-proxy.test.ts +0 -4
  101. package/src/__tests__/secure-keys.test.ts +95 -272
  102. package/src/__tests__/shell-identity.test.ts +96 -6
  103. package/src/__tests__/skill-feature-flags-integration.test.ts +22 -14
  104. package/src/__tests__/skill-feature-flags.test.ts +46 -45
  105. package/src/__tests__/skill-load-feature-flag.test.ts +7 -10
  106. package/src/__tests__/skill-load-inline-command.test.ts +8 -12
  107. package/src/__tests__/skill-load-inline-includes.test.ts +6 -10
  108. package/src/__tests__/skill-load-tool.test.ts +0 -2
  109. package/src/__tests__/skill-memory.test.ts +17 -3
  110. package/src/__tests__/skill-projection-feature-flag.test.ts +33 -29
  111. package/src/__tests__/skills.test.ts +0 -2
  112. package/src/__tests__/slack-inbound-verification.test.ts +0 -4
  113. package/src/__tests__/stale-approval-dedup.test.ts +171 -0
  114. package/src/__tests__/stt-hints.test.ts +437 -0
  115. package/src/__tests__/suggestion-routes.test.ts +1 -32
  116. package/src/__tests__/system-prompt.test.ts +0 -1
  117. package/src/__tests__/task-memory-cleanup.test.ts +14 -0
  118. package/src/__tests__/tool-executor-shell-integration.test.ts +5 -3
  119. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +0 -5
  120. package/src/__tests__/trusted-contact-multichannel.test.ts +0 -4
  121. package/src/__tests__/twilio-routes-twiml.test.ts +139 -1
  122. package/src/__tests__/update-bulletin.test.ts +0 -2
  123. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +6 -9
  124. package/src/__tests__/voice-quality.test.ts +58 -0
  125. package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -7
  126. package/src/__tests__/workspace-migration-015-migrate-credentials-to-keychain.test.ts +252 -0
  127. package/src/__tests__/workspace-migration-016-migrate-credentials-from-keychain.test.ts +220 -0
  128. package/src/__tests__/workspace-migration-down-functions.test.ts +1009 -0
  129. package/src/__tests__/workspace-migrations-runner.test.ts +114 -0
  130. package/src/acp/agent-process.ts +9 -1
  131. package/src/agent/loop.ts +1 -1
  132. package/src/approvals/guardian-request-resolvers.ts +164 -38
  133. package/src/calls/__tests__/tts-text-sanitizer.test.ts +254 -0
  134. package/src/calls/audio-store.test.ts +97 -0
  135. package/src/calls/audio-store.ts +205 -0
  136. package/src/calls/call-controller.ts +90 -8
  137. package/src/calls/call-domain.ts +3 -0
  138. package/src/calls/call-store.ts +10 -3
  139. package/src/calls/fish-audio-client.ts +129 -0
  140. package/src/calls/relay-server.ts +27 -0
  141. package/src/calls/stt-hints.ts +189 -0
  142. package/src/calls/tts-text-sanitizer.ts +61 -0
  143. package/src/calls/twilio-routes.ts +34 -5
  144. package/src/calls/types.ts +1 -0
  145. package/src/calls/voice-ingress-preflight.ts +0 -42
  146. package/src/calls/voice-quality.ts +38 -5
  147. package/src/calls/voice-session-bridge.ts +7 -12
  148. package/src/cli/commands/avatar.ts +2 -2
  149. package/src/cli/commands/config.ts +1 -4
  150. package/src/cli/commands/credentials.ts +128 -82
  151. package/src/cli/commands/doctor.ts +2 -2
  152. package/src/cli/commands/keys.ts +7 -7
  153. package/src/cli/commands/memory.ts +1 -1
  154. package/src/cli/commands/oauth/connections.ts +11 -29
  155. package/src/cli/commands/oauth/index.ts +7 -0
  156. package/src/cli/commands/oauth/platform.ts +525 -0
  157. package/src/cli/commands/platform.ts +3 -3
  158. package/src/cli/lib/daemon-credential-client.ts +284 -0
  159. package/src/cli.ts +1 -1
  160. package/src/config/assistant-feature-flags.ts +186 -5
  161. package/src/config/bundled-skills/AGENTS.md +34 -0
  162. package/src/config/bundled-skills/acp/SKILL.md +10 -0
  163. package/src/config/bundled-skills/app-builder/SKILL.md +0 -4
  164. package/src/config/bundled-skills/messaging/SKILL.md +5 -5
  165. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +2 -2
  166. package/src/config/bundled-skills/phone-calls/TOOLS.json +4 -0
  167. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +1 -0
  168. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +1 -0
  169. package/src/config/bundled-skills/settings/SKILL.md +15 -2
  170. package/src/config/bundled-skills/settings/TOOLS.json +47 -2
  171. package/src/config/bundled-skills/settings/tools/avatar-remove.ts +59 -0
  172. package/src/config/bundled-skills/settings/tools/avatar-update.ts +80 -0
  173. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +42 -0
  174. package/src/config/bundled-skills/slack/SKILL.md +1 -1
  175. package/src/config/bundled-tool-registry.ts +5 -11
  176. package/src/config/defaults.ts +0 -2
  177. package/src/config/env-registry.ts +5 -5
  178. package/src/config/env.ts +21 -14
  179. package/src/config/feature-flag-registry.json +49 -9
  180. package/src/config/loader.ts +106 -42
  181. package/src/config/schema.ts +9 -29
  182. package/src/config/schemas/calls.ts +30 -0
  183. package/src/config/schemas/fish-audio.ts +39 -0
  184. package/src/config/schemas/inference.ts +2 -2
  185. package/src/config/schemas/journal.ts +16 -0
  186. package/src/config/schemas/memory-processing.ts +2 -2
  187. package/src/config/schemas/security.ts +0 -4
  188. package/src/config/types.ts +1 -1
  189. package/src/contacts/contact-store.ts +39 -0
  190. package/src/contacts/types.ts +2 -0
  191. package/src/credential-execution/approval-bridge.ts +1 -0
  192. package/src/credential-execution/executable-discovery.ts +28 -4
  193. package/src/credential-execution/feature-gates.ts +16 -0
  194. package/src/credential-execution/process-manager.ts +38 -0
  195. package/src/credential-execution/startup-timeout.ts +36 -0
  196. package/src/daemon/approval-generators.ts +3 -9
  197. package/src/daemon/assistant-attachments.ts +9 -0
  198. package/src/daemon/config-watcher.ts +5 -0
  199. package/src/daemon/conversation-error.ts +13 -1
  200. package/src/daemon/conversation-memory.ts +1 -2
  201. package/src/daemon/conversation-process.ts +18 -1
  202. package/src/daemon/conversation-surfaces.ts +30 -1
  203. package/src/daemon/conversation-tool-setup.ts +0 -105
  204. package/src/daemon/conversation.ts +21 -1
  205. package/src/daemon/guardian-action-generators.ts +3 -9
  206. package/src/daemon/handlers/config-vercel.ts +92 -0
  207. package/src/daemon/handlers/skills.ts +2 -15
  208. package/src/daemon/install-symlink.ts +195 -0
  209. package/src/daemon/lifecycle.ts +234 -51
  210. package/src/daemon/message-types/conversations.ts +4 -4
  211. package/src/daemon/message-types/diagnostics.ts +3 -22
  212. package/src/daemon/message-types/messages.ts +0 -2
  213. package/src/daemon/message-types/upgrades.ts +8 -0
  214. package/src/daemon/server.ts +32 -95
  215. package/src/events/domain-events.ts +2 -1
  216. package/src/inbound/platform-callback-registration.ts +3 -3
  217. package/src/instrument.ts +8 -5
  218. package/src/memory/app-store.ts +31 -0
  219. package/src/memory/conversation-title-service.ts +50 -1
  220. package/src/memory/db-init.ts +16 -0
  221. package/src/memory/indexer.ts +19 -10
  222. package/src/memory/items-extractor.ts +328 -321
  223. package/src/memory/job-handlers/conversation-starters.ts +4 -1
  224. package/src/memory/job-handlers/summarization.ts +26 -16
  225. package/src/memory/jobs-store.ts +63 -6
  226. package/src/memory/jobs-worker.ts +31 -7
  227. package/src/memory/journal-memory.ts +214 -0
  228. package/src/memory/migrations/001-job-deferrals.ts +19 -0
  229. package/src/memory/migrations/004-entity-relation-dedup.ts +10 -0
  230. package/src/memory/migrations/005-fingerprint-scope-unique.ts +76 -0
  231. package/src/memory/migrations/006-scope-salted-fingerprints.ts +50 -0
  232. package/src/memory/migrations/007-assistant-id-to-self.ts +10 -0
  233. package/src/memory/migrations/008-remove-assistant-id-columns.ts +34 -0
  234. package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +26 -0
  235. package/src/memory/migrations/014-backfill-inbox-thread-state.ts +10 -0
  236. package/src/memory/migrations/015-drop-active-search-index.ts +17 -0
  237. package/src/memory/migrations/019-notification-tables-schema-migration.ts +12 -0
  238. package/src/memory/migrations/020-rename-macos-ios-channel-to-vellum.ts +121 -0
  239. package/src/memory/migrations/024-embedding-vector-blob.ts +74 -0
  240. package/src/memory/migrations/026a-embeddings-nullable-vector-json.ts +82 -0
  241. package/src/memory/migrations/036-normalize-phone-identities.ts +11 -0
  242. package/src/memory/migrations/116-messages-fts.ts +106 -1
  243. package/src/memory/migrations/126-backfill-guardian-principal-id.ts +52 -0
  244. package/src/memory/migrations/127-guardian-principal-id-not-null.ts +77 -0
  245. package/src/memory/migrations/134-contacts-notes-column.ts +13 -0
  246. package/src/memory/migrations/135-backfill-contact-interaction-stats.ts +20 -0
  247. package/src/memory/migrations/136-drop-assistant-id-columns.ts +52 -0
  248. package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +13 -0
  249. package/src/memory/migrations/141-rename-verification-table.ts +54 -0
  250. package/src/memory/migrations/142-rename-verification-session-id-column.ts +25 -0
  251. package/src/memory/migrations/143-rename-guardian-verification-values.ts +35 -0
  252. package/src/memory/migrations/144-rename-voice-to-phone.ts +136 -0
  253. package/src/memory/migrations/145-drop-accounts-table.ts +32 -0
  254. package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +14 -1
  255. package/src/memory/migrations/148-drop-reminders-table.ts +35 -1
  256. package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +69 -1
  257. package/src/memory/migrations/162-guardian-timestamps-epoch-ms.ts +290 -0
  258. package/src/memory/migrations/169-rename-gmail-provider-key-to-google.ts +51 -1
  259. package/src/memory/migrations/174-rename-thread-starters-table.ts +47 -1
  260. package/src/memory/migrations/176-drop-capability-card-state.ts +13 -0
  261. package/src/memory/migrations/180-backfill-inline-attachments-to-disk.ts +16 -0
  262. package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +28 -1
  263. package/src/memory/migrations/190-call-session-skip-disclosure.ts +15 -0
  264. package/src/memory/migrations/191-backfill-audio-attachment-mime-types.ts +64 -0
  265. package/src/memory/migrations/192-contacts-user-file-column.ts +15 -0
  266. package/src/memory/migrations/193-add-source-type-columns.ts +81 -0
  267. package/src/memory/migrations/index.ts +5 -0
  268. package/src/memory/migrations/registry.ts +98 -0
  269. package/src/memory/migrations/validate-migration-state.ts +137 -11
  270. package/src/memory/qdrant-circuit-breaker.ts +9 -0
  271. package/src/memory/qdrant-manager.ts +64 -7
  272. package/src/memory/retriever.test.ts +37 -25
  273. package/src/memory/retriever.ts +24 -49
  274. package/src/memory/schema/calls.ts +1 -0
  275. package/src/memory/schema/contacts.ts +1 -0
  276. package/src/memory/schema/memory-core.ts +2 -0
  277. package/src/memory/search/formatting.ts +7 -44
  278. package/src/memory/search/staleness.ts +4 -0
  279. package/src/memory/search/tier-classifier.ts +10 -2
  280. package/src/memory/search/types.ts +2 -5
  281. package/src/memory/task-memory-cleanup.ts +4 -3
  282. package/src/notifications/adapters/slack.ts +168 -6
  283. package/src/notifications/broadcaster.ts +1 -0
  284. package/src/notifications/copy-composer.ts +59 -2
  285. package/src/notifications/decision-engine.ts +4 -1
  286. package/src/notifications/signal.ts +2 -0
  287. package/src/notifications/types.ts +2 -0
  288. package/src/oauth/connection-resolver.ts +6 -4
  289. package/src/permissions/checker.ts +0 -38
  290. package/src/permissions/shell-identity.ts +76 -22
  291. package/src/permissions/types.ts +4 -2
  292. package/src/platform/client.ts +35 -7
  293. package/src/prompts/journal-context.ts +133 -0
  294. package/src/prompts/persona-resolver.ts +194 -0
  295. package/src/prompts/system-prompt.ts +44 -4
  296. package/src/prompts/templates/SOUL.md +10 -0
  297. package/src/prompts/templates/users/default.md +1 -0
  298. package/src/providers/provider-send-message.ts +3 -32
  299. package/src/providers/registry.ts +29 -179
  300. package/src/providers/types.ts +1 -1
  301. package/src/runtime/access-request-helper.ts +4 -0
  302. package/src/runtime/auth/__tests__/credential-service.test.ts +0 -1
  303. package/src/runtime/auth/__tests__/external-assistant-id.test.ts +13 -68
  304. package/src/runtime/auth/__tests__/guard-tests.test.ts +9 -50
  305. package/src/runtime/auth/external-assistant-id.ts +13 -59
  306. package/src/runtime/auth/route-policy.ts +17 -1
  307. package/src/runtime/auth/token-service.ts +43 -138
  308. package/src/runtime/channel-readiness-service.ts +1 -16
  309. package/src/runtime/gateway-client.ts +47 -4
  310. package/src/runtime/guardian-decision-types.ts +45 -4
  311. package/src/runtime/http-server.ts +31 -3
  312. package/src/runtime/middleware/error-handler.ts +1 -9
  313. package/src/runtime/routes/access-request-decision.ts +2 -2
  314. package/src/runtime/routes/app-management-routes.ts +2 -1
  315. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +219 -30
  316. package/src/runtime/routes/approval-strategies/guardian-text-engine-strategy.ts +37 -14
  317. package/src/runtime/routes/audio-routes.ts +40 -0
  318. package/src/runtime/routes/btw-routes.ts +0 -17
  319. package/src/runtime/routes/channel-readiness-routes.ts +9 -4
  320. package/src/runtime/routes/conversation-query-routes.ts +63 -1
  321. package/src/runtime/routes/conversation-routes.ts +4 -44
  322. package/src/runtime/routes/debug-routes.ts +12 -9
  323. package/src/runtime/routes/diagnostics-routes.ts +1 -477
  324. package/src/runtime/routes/guardian-approval-interception.ts +168 -11
  325. package/src/runtime/routes/guardian-approval-prompt.ts +6 -1
  326. package/src/runtime/routes/guardian-approval-reply-helpers.ts +103 -21
  327. package/src/runtime/routes/identity-routes.ts +19 -30
  328. package/src/runtime/routes/inbound-message-handler.ts +31 -1
  329. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +64 -5
  330. package/src/runtime/routes/inbound-stages/background-dispatch.ts +52 -40
  331. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +4 -33
  332. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +1 -1
  333. package/src/runtime/routes/integrations/twilio.ts +52 -10
  334. package/src/runtime/routes/integrations/vercel.ts +89 -0
  335. package/src/runtime/routes/log-export-routes.ts +5 -0
  336. package/src/runtime/routes/memory-item-routes.test.ts +3 -3
  337. package/src/runtime/routes/memory-item-routes.ts +46 -14
  338. package/src/runtime/routes/migration-rollback-routes.ts +209 -0
  339. package/src/runtime/routes/migration-routes.ts +17 -1
  340. package/src/runtime/routes/notification-routes.ts +58 -0
  341. package/src/runtime/routes/schedule-routes.ts +65 -0
  342. package/src/runtime/routes/secret-routes.ts +141 -10
  343. package/src/runtime/routes/settings-routes.ts +41 -1
  344. package/src/runtime/routes/tts-routes.ts +96 -0
  345. package/src/runtime/routes/upgrade-broadcast-routes.ts +26 -2
  346. package/src/runtime/routes/workspace-commit-routes.ts +62 -0
  347. package/src/runtime/routes/workspace-routes.test.ts +22 -1
  348. package/src/runtime/routes/workspace-routes.ts +1 -1
  349. package/src/runtime/routes/workspace-utils.ts +86 -2
  350. package/src/security/ces-credential-client.ts +75 -29
  351. package/src/security/ces-rpc-credential-backend.ts +86 -0
  352. package/src/security/credential-backend.ts +22 -92
  353. package/src/security/keychain-broker-client.ts +10 -2
  354. package/src/security/secure-keys.ts +113 -115
  355. package/src/skills/catalog-install.ts +6 -32
  356. package/src/skills/skill-memory.ts +1 -0
  357. package/src/subagent/manager.ts +2 -5
  358. package/src/telemetry/usage-telemetry-reporter.ts +4 -2
  359. package/src/tools/acp/spawn.ts +78 -1
  360. package/src/tools/calls/call-start.ts +1 -0
  361. package/src/tools/credentials/vault.ts +5 -3
  362. package/src/tools/executor.ts +0 -4
  363. package/src/tools/memory/definitions.ts +3 -2
  364. package/src/tools/memory/handlers.ts +10 -7
  365. package/src/tools/network/script-proxy/session-manager.ts +19 -4
  366. package/src/tools/network/web-fetch.ts +3 -1
  367. package/src/tools/skills/execute.ts +1 -1
  368. package/src/tools/terminal/safe-env.ts +1 -0
  369. package/src/tools/types.ts +0 -8
  370. package/src/util/browser.ts +15 -0
  371. package/src/util/errors.ts +0 -12
  372. package/src/util/platform.ts +4 -51
  373. package/src/workspace/git-service.ts +5 -2
  374. package/src/workspace/migrations/001-avatar-rename.ts +15 -0
  375. package/src/workspace/migrations/003-seed-device-id.ts +17 -1
  376. package/src/workspace/migrations/004-extract-collect-usage-data.ts +33 -0
  377. package/src/workspace/migrations/005-add-send-diagnostics.ts +3 -0
  378. package/src/workspace/migrations/006-services-config.ts +49 -0
  379. package/src/workspace/migrations/007-web-search-provider-rename.ts +27 -0
  380. package/src/workspace/migrations/008-voice-timeout-and-max-steps.ts +3 -0
  381. package/src/workspace/migrations/009-backfill-conversation-disk-view.ts +4 -0
  382. package/src/workspace/migrations/010-app-dir-rename.ts +78 -0
  383. package/src/workspace/migrations/011-backfill-installation-id.ts +11 -0
  384. package/src/workspace/migrations/012-rename-conversation-disk-view-dirs.ts +44 -0
  385. package/src/workspace/migrations/013-repair-conversation-disk-view.ts +5 -0
  386. package/src/workspace/migrations/015-migrate-credentials-to-keychain.ts +153 -0
  387. package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +156 -0
  388. package/src/workspace/migrations/016-migrate-credentials-from-keychain.ts +150 -0
  389. package/src/workspace/migrations/017-seed-persona-dirs.ts +96 -0
  390. package/src/workspace/migrations/018-rekey-compound-credential-keys.ts +184 -0
  391. package/src/workspace/migrations/019-scope-journal-to-guardian.ts +103 -0
  392. package/src/workspace/migrations/migrate-to-workspace-volume.ts +27 -5
  393. package/src/workspace/migrations/registry.ts +12 -0
  394. package/src/workspace/migrations/runner.ts +106 -2
  395. package/src/workspace/migrations/types.ts +4 -0
  396. package/src/workspace/provider-commit-message-generator.ts +12 -21
  397. package/src/__tests__/claude-code-skill-regression.test.ts +0 -206
  398. package/src/__tests__/claude-code-tool-profiles.test.ts +0 -99
  399. package/src/__tests__/diagnostics-export.test.ts +0 -288
  400. package/src/__tests__/local-gateway-health.test.ts +0 -209
  401. package/src/__tests__/provider-fail-open-selection.test.ts +0 -271
  402. package/src/__tests__/provider-failover-actual-provider.test.ts +0 -66
  403. package/src/__tests__/secret-ingress-handler.test.ts +0 -120
  404. package/src/__tests__/swarm-conversation-integration.test.ts +0 -358
  405. package/src/__tests__/swarm-dag-pathological.test.ts +0 -547
  406. package/src/__tests__/swarm-orchestrator.test.ts +0 -463
  407. package/src/__tests__/swarm-plan-validator.test.ts +0 -384
  408. package/src/__tests__/swarm-recursion.test.ts +0 -197
  409. package/src/__tests__/swarm-router-planner.test.ts +0 -234
  410. package/src/__tests__/swarm-tool.test.ts +0 -185
  411. package/src/__tests__/swarm-worker-backend.test.ts +0 -144
  412. package/src/__tests__/swarm-worker-runner.test.ts +0 -288
  413. package/src/commands/__tests__/cc-command-registry.test.ts +0 -396
  414. package/src/commands/cc-command-registry.ts +0 -248
  415. package/src/config/bundled-skills/claude-code/SKILL.md +0 -53
  416. package/src/config/bundled-skills/claude-code/TOOLS.json +0 -47
  417. package/src/config/bundled-skills/claude-code/tools/claude-code.ts +0 -12
  418. package/src/config/bundled-skills/orchestration/SKILL.md +0 -33
  419. package/src/config/bundled-skills/orchestration/TOOLS.json +0 -35
  420. package/src/config/bundled-skills/orchestration/tools/swarm-delegate.ts +0 -12
  421. package/src/config/schemas/swarm.ts +0 -82
  422. package/src/logfire.ts +0 -135
  423. package/src/memory/search/lexical.ts +0 -48
  424. package/src/providers/failover.ts +0 -186
  425. package/src/runtime/local-gateway-health.ts +0 -275
  426. package/src/security/secret-ingress.ts +0 -68
  427. package/src/swarm/backend-claude-code.ts +0 -225
  428. package/src/swarm/checkpoint.ts +0 -137
  429. package/src/swarm/graph-utils.ts +0 -53
  430. package/src/swarm/index.ts +0 -55
  431. package/src/swarm/limits.ts +0 -66
  432. package/src/swarm/orchestrator.ts +0 -424
  433. package/src/swarm/plan-validator.ts +0 -117
  434. package/src/swarm/router-planner.ts +0 -162
  435. package/src/swarm/router-prompts.ts +0 -39
  436. package/src/swarm/synthesizer.ts +0 -81
  437. package/src/swarm/types.ts +0 -72
  438. package/src/swarm/worker-backend.ts +0 -131
  439. package/src/swarm/worker-prompts.ts +0 -80
  440. package/src/swarm/worker-runner.ts +0 -170
  441. package/src/tools/claude-code/claude-code.ts +0 -610
  442. package/src/tools/swarm/delegate.ts +0 -205
@@ -1,8 +1,6 @@
1
- import { wrapWithLogfire } from "../logfire.js";
2
1
  import { getProviderKeyAsync } from "../security/secure-keys.js";
3
- import { ConfigError, ProviderNotConfiguredError } from "../util/errors.js";
2
+ import { ProviderNotConfiguredError } from "../util/errors.js";
4
3
  import { AnthropicProvider } from "./anthropic/client.js";
5
- import { FailoverProvider, type ProviderHealthStatus } from "./failover.js";
6
4
  import { FireworksProvider } from "./fireworks/client.js";
7
5
  import { GeminiProvider } from "./gemini/client.js";
8
6
  import {
@@ -18,8 +16,6 @@ import type { Provider } from "./types.js";
18
16
 
19
17
  const providers = new Map<string, Provider>();
20
18
  const routingSources = new Map<string, "user-key" | "managed-proxy">();
21
- let cachedFailoverProvider: FailoverProvider | null = null;
22
- let cachedFailoverKey: string | null = null;
23
19
 
24
20
  export function registerProvider(name: string, provider: Provider): void {
25
21
  providers.set(name, provider);
@@ -28,95 +24,11 @@ export function registerProvider(name: string, provider: Provider): void {
28
24
  export function getProvider(name: string): Provider {
29
25
  const provider = providers.get(name);
30
26
  if (!provider) {
31
- throw new ConfigError(
32
- `Provider "${name}" not found. Available: ${listProviders().join(", ")}`,
33
- );
27
+ throw new ProviderNotConfiguredError(name, listProviders());
34
28
  }
35
29
  return provider;
36
30
  }
37
31
 
38
- export interface ProviderSelection {
39
- /** Ordered list of available provider names */
40
- availableProviders: string[];
41
- /** The selected (effective) primary provider name, or null if none available */
42
- selectedPrimary: string | null;
43
- /** Whether the effective primary differs from the requested primary */
44
- usedFallbackPrimary: boolean;
45
- }
46
-
47
- /**
48
- * Resolve provider selection from requested primary and provider order.
49
- * Dedupes [requestedPrimary, ...providerOrder], filtered to initialized providers.
50
- * Returns null selectedPrimary when no providers are available.
51
- */
52
- export function resolveProviderSelection(
53
- requestedPrimary: string,
54
- providerOrder: string[],
55
- ): ProviderSelection {
56
- const ordered: string[] = [];
57
- const seen = new Set<string>();
58
-
59
- for (const name of [requestedPrimary, ...providerOrder]) {
60
- if (seen.has(name)) continue;
61
- seen.add(name);
62
- if (providers.has(name)) {
63
- ordered.push(name);
64
- }
65
- }
66
-
67
- if (ordered.length === 0) {
68
- return {
69
- availableProviders: [],
70
- selectedPrimary: null,
71
- usedFallbackPrimary: false,
72
- };
73
- }
74
-
75
- return {
76
- availableProviders: ordered,
77
- selectedPrimary: ordered[0],
78
- usedFallbackPrimary: ordered[0] !== requestedPrimary,
79
- };
80
- }
81
-
82
- /**
83
- * Build a provider that tries the effective primary provider first, then falls
84
- * back to others in the configured order. If the requested primary is not
85
- * available, automatically selects the first available provider from the
86
- * deduped [primaryName, ...providerOrder] list (fail-open).
87
- *
88
- * Throws ConfigError only when NO providers are available at all.
89
- * Caches the FailoverProvider instance so health state persists across calls.
90
- */
91
- export function getFailoverProvider(
92
- primaryName: string,
93
- providerOrder: string[],
94
- ): Provider {
95
- const selection = resolveProviderSelection(primaryName, providerOrder);
96
-
97
- if (!selection.selectedPrimary) {
98
- throw new ProviderNotConfiguredError(primaryName, listProviders());
99
- }
100
-
101
- const orderedProviders: Provider[] = selection.availableProviders.map(
102
- (name) => providers.get(name)!,
103
- );
104
-
105
- if (orderedProviders.length === 1) {
106
- return orderedProviders[0];
107
- }
108
-
109
- // Cache key from effective ordered providers (not raw input strings)
110
- const cacheKey = selection.availableProviders.join(",");
111
- if (cachedFailoverProvider && cachedFailoverKey === cacheKey) {
112
- return cachedFailoverProvider;
113
- }
114
-
115
- cachedFailoverProvider = new FailoverProvider(orderedProviders);
116
- cachedFailoverKey = cacheKey;
117
- return cachedFailoverProvider;
118
- }
119
-
120
32
  export function listProviders(): string[] {
121
33
  return Array.from(providers.keys());
122
34
  }
@@ -152,7 +64,6 @@ export interface ProvidersConfig {
152
64
  provider: string;
153
65
  };
154
66
  };
155
- providerOrder?: string[];
156
67
  timeouts?: { providerStreamTimeoutSec?: number };
157
68
  }
158
69
 
@@ -173,53 +84,6 @@ function resolveModel(config: ProvidersConfig, providerName: string): string {
173
84
  return getProviderDefaultModel(providerName);
174
85
  }
175
86
 
176
- export interface ProviderDebugStatus {
177
- configuredPrimary: string;
178
- activePrimary: string | null;
179
- usedFallback: boolean;
180
- registeredProviders: string[];
181
- failoverHealth: ProviderHealthStatus[] | null;
182
- overallHealth: "healthy" | "degraded" | "down";
183
- routingSources: Record<string, "user-key" | "managed-proxy">;
184
- }
185
-
186
- export function getProviderDebugStatus(
187
- configuredProvider: string,
188
- providerOrder: string[],
189
- ): ProviderDebugStatus {
190
- const registered = listProviders();
191
- const selection = resolveProviderSelection(configuredProvider, providerOrder);
192
-
193
- let failoverHealth: ProviderHealthStatus[] | null = null;
194
- if (cachedFailoverProvider) {
195
- failoverHealth = cachedFailoverProvider.getHealthStatus();
196
- }
197
-
198
- let overallHealth: "healthy" | "degraded" | "down" = "down";
199
- if (registered.length > 0 && selection.selectedPrimary) {
200
- if (!failoverHealth) {
201
- overallHealth = "healthy";
202
- } else {
203
- const healthyCount = failoverHealth.filter((h) => h.healthy).length;
204
- if (healthyCount === failoverHealth.length) {
205
- overallHealth = "healthy";
206
- } else if (healthyCount > 0) {
207
- overallHealth = "degraded";
208
- }
209
- }
210
- }
211
-
212
- return {
213
- configuredPrimary: configuredProvider,
214
- activePrimary: selection.selectedPrimary,
215
- usedFallback: selection.usedFallbackPrimary,
216
- registeredProviders: registered,
217
- failoverHealth,
218
- overallHealth,
219
- routingSources: Object.fromEntries(routingSources),
220
- };
221
- }
222
-
223
87
  /**
224
88
  * Resolve provider credentials using mode-aware logic.
225
89
  * In "managed" mode, routes through the platform proxy.
@@ -274,8 +138,6 @@ export async function initializeProviders(
274
138
  ): Promise<void> {
275
139
  providers.clear();
276
140
  routingSources.clear();
277
- cachedFailoverProvider = null;
278
- cachedFailoverKey = null;
279
141
 
280
142
  const streamTimeoutMs =
281
143
  (config.timeouts?.providerStreamTimeoutSec ?? 300) * 1000;
@@ -293,15 +155,13 @@ export async function initializeProviders(
293
155
  registerProvider(
294
156
  "anthropic",
295
157
  new RetryProvider(
296
- wrapWithLogfire(
297
- new AnthropicProvider(anthropicCreds.apiKey, model, {
298
- useNativeWebSearch,
299
- streamTimeoutMs,
300
- ...(anthropicCreds.baseURL
301
- ? { baseURL: anthropicCreds.baseURL }
302
- : {}),
303
- }),
304
- ),
158
+ new AnthropicProvider(anthropicCreds.apiKey, model, {
159
+ useNativeWebSearch,
160
+ streamTimeoutMs,
161
+ ...(anthropicCreds.baseURL
162
+ ? { baseURL: anthropicCreds.baseURL }
163
+ : {}),
164
+ }),
305
165
  ),
306
166
  );
307
167
  routingSources.set("anthropic", anthropicCreds.source);
@@ -314,12 +174,10 @@ export async function initializeProviders(
314
174
  registerProvider(
315
175
  "openai",
316
176
  new RetryProvider(
317
- wrapWithLogfire(
318
- new OpenAIProvider(openaiCreds.apiKey, model, {
319
- streamTimeoutMs,
320
- ...(openaiCreds.baseURL ? { baseURL: openaiCreds.baseURL } : {}),
321
- }),
322
- ),
177
+ new OpenAIProvider(openaiCreds.apiKey, model, {
178
+ streamTimeoutMs,
179
+ ...(openaiCreds.baseURL ? { baseURL: openaiCreds.baseURL } : {}),
180
+ }),
323
181
  ),
324
182
  );
325
183
  routingSources.set("openai", openaiCreds.source);
@@ -332,14 +190,12 @@ export async function initializeProviders(
332
190
  registerProvider(
333
191
  "gemini",
334
192
  new RetryProvider(
335
- wrapWithLogfire(
336
- new GeminiProvider(geminiCreds.apiKey, model, {
337
- streamTimeoutMs,
338
- ...(geminiCreds.baseURL
339
- ? { managedBaseUrl: geminiCreds.baseURL }
340
- : {}),
341
- }),
342
- ),
193
+ new GeminiProvider(geminiCreds.apiKey, model, {
194
+ streamTimeoutMs,
195
+ ...(geminiCreds.baseURL
196
+ ? { managedBaseUrl: geminiCreds.baseURL }
197
+ : {}),
198
+ }),
343
199
  ),
344
200
  );
345
201
  routingSources.set("gemini", geminiCreds.source);
@@ -352,12 +208,10 @@ export async function initializeProviders(
352
208
  registerProvider(
353
209
  "ollama",
354
210
  new RetryProvider(
355
- wrapWithLogfire(
356
- new OllamaProvider(model, {
357
- apiKey: ollamaKey ?? undefined,
358
- streamTimeoutMs,
359
- }),
360
- ),
211
+ new OllamaProvider(model, {
212
+ apiKey: ollamaKey ?? undefined,
213
+ streamTimeoutMs,
214
+ }),
361
215
  ),
362
216
  );
363
217
  routingSources.set("ollama", "user-key");
@@ -370,11 +224,9 @@ export async function initializeProviders(
370
224
  registerProvider(
371
225
  "fireworks",
372
226
  new RetryProvider(
373
- wrapWithLogfire(
374
- new FireworksProvider(fireworksKey, model, {
375
- streamTimeoutMs,
376
- }),
377
- ),
227
+ new FireworksProvider(fireworksKey, model, {
228
+ streamTimeoutMs,
229
+ }),
378
230
  ),
379
231
  );
380
232
  routingSources.set("fireworks", "user-key");
@@ -387,11 +239,9 @@ export async function initializeProviders(
387
239
  registerProvider(
388
240
  "openrouter",
389
241
  new RetryProvider(
390
- wrapWithLogfire(
391
- new OpenRouterProvider(openrouterKey, model, {
392
- streamTimeoutMs,
393
- }),
394
- ),
242
+ new OpenRouterProvider(openrouterKey, model, {
243
+ streamTimeoutMs,
244
+ }),
395
245
  ),
396
246
  );
397
247
  routingSources.set("openrouter", "user-key");
@@ -130,7 +130,7 @@ export type ProviderEvent =
130
130
  export interface SendMessageConfig {
131
131
  model?: string;
132
132
  modelIntent?: ModelIntent;
133
- effort?: "low" | "medium" | "high";
133
+ effort?: "low" | "medium" | "high" | "max";
134
134
  [key: string]: unknown;
135
135
  }
136
136
 
@@ -55,6 +55,8 @@ export interface AccessRequestParams {
55
55
  actorDisplayName?: string;
56
56
  actorUsername?: string;
57
57
  previousMemberStatus?: Exclude<ChannelStatus, "unverified">;
58
+ /** Preview of the requester's original message, shown to the guardian. */
59
+ messagePreview?: string;
58
60
  }
59
61
 
60
62
  export type AccessRequestResult =
@@ -90,6 +92,7 @@ export function notifyGuardianOfAccessRequest(
90
92
  actorDisplayName,
91
93
  actorUsername,
92
94
  previousMemberStatus,
95
+ messagePreview,
93
96
  } = params;
94
97
 
95
98
  if (!actorExternalId) {
@@ -244,6 +247,7 @@ export function notifyGuardianOfAccessRequest(
244
247
  guardianBindingChannel,
245
248
  guardianResolutionSource,
246
249
  previousMemberStatus: previousMemberStatus ?? null,
250
+ messagePreview: messagePreview ?? null,
247
251
  },
248
252
  dedupeKey: `access-request:${canonicalRequest.id}`,
249
253
  onConversationCreated: (info) => {
@@ -17,7 +17,6 @@ mock.module("../../../util/platform.js", () => ({
17
17
  getDataDir: () => testDir,
18
18
  getDbPath: () => join(testDir, "test.db"),
19
19
  normalizeAssistantId: (id: string) => (id === "self" ? "self" : id),
20
- readLockfile: () => ({ assistants: [{ assistantId: "vellum-test-eel" }] }),
21
20
  isMacOS: () => process.platform === "darwin",
22
21
  isLinux: () => process.platform === "linux",
23
22
  isWindows: () => process.platform === "win32",
@@ -1,21 +1,7 @@
1
1
  /**
2
- * Tests for getExternalAssistantId resolution order.
2
+ * Tests for getExternalAssistantId.
3
3
  */
4
- import { afterEach, describe, expect, mock, test } from "bun:test";
5
-
6
- mock.module("../../../util/logger.js", () => ({
7
- getLogger: () =>
8
- new Proxy({} as Record<string, unknown>, {
9
- get: () => () => {},
10
- }),
11
- }));
12
-
13
- // Controllable mock for readLockfile — defaults to null (no lockfile data)
14
- const mockReadLockfile = mock(() => null as Record<string, unknown> | null);
15
-
16
- mock.module("../../../util/platform.js", () => ({
17
- readLockfile: mockReadLockfile,
18
- }));
4
+ import { afterEach, describe, expect, test } from "bun:test";
19
5
 
20
6
  import {
21
7
  getExternalAssistantId,
@@ -24,65 +10,24 @@ import {
24
10
 
25
11
  afterEach(() => {
26
12
  resetExternalAssistantIdCache();
27
- mockReadLockfile.mockReset();
28
- mockReadLockfile.mockImplementation(() => null);
29
- delete process.env.BASE_DATA_DIR;
13
+ delete process.env.VELLUM_ASSISTANT_NAME;
30
14
  });
31
15
 
32
16
  describe("getExternalAssistantId", () => {
33
- test("resolves from lockfile assistants array (most recently hatched)", () => {
34
- mockReadLockfile.mockImplementation(() => ({
35
- assistants: [
36
- { assistantId: "vellum-old-fox", hatchedAt: "2025-01-01T00:00:00Z" },
37
- { assistantId: "vellum-new-eel", hatchedAt: "2025-06-15T12:00:00Z" },
38
- ],
39
- }));
40
- expect(getExternalAssistantId()).toBe("vellum-new-eel");
41
- });
42
-
43
- test("resolves from lockfile with single assistant entry", () => {
44
- mockReadLockfile.mockImplementation(() => ({
45
- assistants: [
46
- { assistantId: "vellum-solo-cat", hatchedAt: "2025-03-01T00:00:00Z" },
47
- ],
48
- }));
49
- expect(getExternalAssistantId()).toBe("vellum-solo-cat");
50
- });
51
-
52
- test("resolves from BASE_DATA_DIR when lockfile has no data", () => {
53
- process.env.BASE_DATA_DIR = "/tmp/vellum/assistants/vellum-true-eel";
54
- expect(getExternalAssistantId()).toBe("vellum-true-eel");
55
- });
56
-
57
- test("resolves from BASE_DATA_DIR with trailing slash", () => {
58
- process.env.BASE_DATA_DIR = "/tmp/vellum/assistants/vellum-cool-heron/";
59
- expect(getExternalAssistantId()).toBe("vellum-cool-heron");
60
- });
61
-
62
- test("resolves from BASE_DATA_DIR with Windows-style backslashes", () => {
63
- process.env.BASE_DATA_DIR =
64
- "C:\\Users\\user\\.local\\share\\vellum\\assistants\\vellum-nice-fox";
65
- expect(getExternalAssistantId()).toBe("vellum-nice-fox");
66
- });
67
-
68
- test("resolves from BASE_DATA_DIR with /instances/<name> path", () => {
69
- process.env.BASE_DATA_DIR = "/home/user/.vellum/instances/vellum-swift-owl";
70
- expect(getExternalAssistantId()).toBe("vellum-swift-owl");
71
- });
72
-
73
- test("resolves from BASE_DATA_DIR with /instances/<name> trailing slash", () => {
74
- process.env.BASE_DATA_DIR =
75
- "/home/user/.vellum/instances/vellum-swift-owl/";
76
- expect(getExternalAssistantId()).toBe("vellum-swift-owl");
17
+ test("resolves from VELLUM_ASSISTANT_NAME env var", () => {
18
+ process.env.VELLUM_ASSISTANT_NAME = "vellum-cool-eel";
19
+ expect(getExternalAssistantId()).toBe("vellum-cool-eel");
77
20
  });
78
21
 
79
- test("falls back to undefined when BASE_DATA_DIR does not match known patterns", () => {
80
- process.env.BASE_DATA_DIR = "/tmp/some-other-path";
81
- expect(getExternalAssistantId()).toBe(undefined);
22
+ test("caches the resolved value", () => {
23
+ process.env.VELLUM_ASSISTANT_NAME = "vellum-cool-eel";
24
+ expect(getExternalAssistantId()).toBe("vellum-cool-eel");
25
+ // Change env var — cached value should still be returned
26
+ process.env.VELLUM_ASSISTANT_NAME = "vellum-other-fox";
27
+ expect(getExternalAssistantId()).toBe("vellum-cool-eel");
82
28
  });
83
29
 
84
- test("falls back to undefined when BASE_DATA_DIR is not set", () => {
85
- delete process.env.BASE_DATA_DIR;
30
+ test("returns undefined when env var is not set", () => {
86
31
  expect(getExternalAssistantId()).toBe(undefined);
87
32
  });
88
33
  });
@@ -16,6 +16,10 @@ import { readFileSync } from "node:fs";
16
16
  import { basename, resolve } from "node:path";
17
17
  import { describe, expect, test } from "bun:test";
18
18
 
19
+ // Cross-package import: gateway duplicates the epoch constant and both must
20
+ // stay in sync. Importing directly is more reliable than regex-extracting.
21
+ import { CURRENT_POLICY_EPOCH as GATEWAY_POLICY_EPOCH } from "../../../../../gateway/src/auth/policy.js";
22
+ import { CURRENT_POLICY_EPOCH } from "../policy.js";
19
23
  import { resolveScopeProfile } from "../scopes.js";
20
24
  import type { Scope, ScopeProfile } from "../types.js";
21
25
 
@@ -337,56 +341,11 @@ describe("scope profile contract", () => {
337
341
  // ---------------------------------------------------------------------------
338
342
 
339
343
  describe("CURRENT_POLICY_EPOCH sync", () => {
340
- /**
341
- * The policy epoch constant is duplicated in assistant, gateway, and cli
342
- * packages. This test reads the exported value from each source file and
343
- * asserts they are all equal.
344
- */
345
-
346
- const EPOCH_FILES = [
347
- {
348
- label: "assistant",
349
- path: resolve(PROJECT_ROOT, "assistant/src/runtime/auth/policy.ts"),
350
- },
351
- {
352
- label: "gateway",
353
- path: resolve(PROJECT_ROOT, "gateway/src/auth/policy.ts"),
354
- },
355
- ];
356
-
357
- function extractEpoch(filePath: string): number {
358
- const src = readFileSync(filePath, "utf-8");
359
- const match = src.match(
360
- /export\s+const\s+CURRENT_POLICY_EPOCH\s*=\s*(\d+)/,
361
- );
362
- if (!match) {
363
- throw new Error(`Could not find CURRENT_POLICY_EPOCH in ${filePath}`);
364
- }
365
- return parseInt(match[1], 10);
366
- }
367
-
368
- test("all non-skill packages export the same CURRENT_POLICY_EPOCH value", () => {
369
- const values = EPOCH_FILES.map((f) => ({
370
- label: f.label,
371
- epoch: extractEpoch(f.path),
372
- }));
373
-
374
- const canonical = values[0];
375
- const mismatches = values.filter((v) => v.epoch !== canonical.epoch);
376
-
377
- if (mismatches.length > 0) {
378
- const summary = values
379
- .map((v) => ` - ${v.label}: ${v.epoch}`)
380
- .join("\n");
381
- const message = [
382
- "CURRENT_POLICY_EPOCH is out of sync across packages:",
383
- "",
384
- summary,
385
- "",
386
- "All locations must have the same value.",
344
+ test("assistant and gateway export the same CURRENT_POLICY_EPOCH value", () => {
345
+ expect(
346
+ CURRENT_POLICY_EPOCH,
347
+ `CURRENT_POLICY_EPOCH mismatch: assistant=${CURRENT_POLICY_EPOCH}, gateway=${GATEWAY_POLICY_EPOCH}. ` +
387
348
  "The canonical source is assistant/src/runtime/auth/policy.ts.",
388
- ].join("\n");
389
- expect(mismatches, message).toEqual([]);
390
- }
349
+ ).toBe(GATEWAY_POLICY_EPOCH);
391
350
  });
392
351
  });
@@ -6,81 +6,35 @@
6
6
  * must identify which assistant the token belongs to, while the daemon
7
7
  * internally uses 'self'.
8
8
  *
9
- * Resolution order:
10
- * 1. Cached in-memory value (populated on first call)
11
- * 2. Most recently hatched entry in lockfile assistants array
12
- * (sorted by `hatchedAt` descending) → assistantId
13
- * 3. BASE_DATA_DIR path matching `/assistants/<name>` or `/instances/<name>` suffix
14
- * 4. `undefined` — callers must handle the missing value
9
+ * Reads from the VELLUM_ASSISTANT_NAME env var, which is set by CLI
10
+ * hatch and Docker setup. Returns `undefined` if the env var is not set.
15
11
  *
16
- * The value is cached in memory after the first successful read.
12
+ * The value is cached in memory after the first read.
17
13
  */
18
14
 
19
- import { getBaseDataDir } from "../../config/env-registry.js";
20
15
  import { getLogger } from "../../util/logger.js";
21
- import { readLockfile } from "../../util/platform.js";
22
16
 
23
17
  const log = getLogger("external-assistant-id");
24
18
 
25
19
  let cached: string | null | undefined;
26
20
 
27
21
  /**
28
- * Get the external assistant ID.
29
- *
30
- * Resolution order:
31
- * 1. Cached in-memory value (populated on first call)
32
- * 2. Most recently hatched entry in lockfile assistants array
33
- * (sorted by `hatchedAt` descending) → assistantId
34
- * 3. BASE_DATA_DIR path matching `/assistants/<name>` or `/instances/<name>` suffix
35
- * 4. `undefined` when resolution fails entirely
22
+ * Get the external assistant ID from the VELLUM_ASSISTANT_NAME env var.
23
+ * Returns `undefined` when the env var is not set.
36
24
  */
37
25
  export function getExternalAssistantId(): string | undefined {
38
26
  if (cached !== undefined) {
39
27
  return cached ?? undefined;
40
28
  }
41
29
 
42
- try {
43
- const lockData = readLockfile();
44
- if (lockData) {
45
- const assistants = lockData.assistants as
46
- | Array<Record<string, unknown>>
47
- | undefined;
48
- if (assistants && assistants.length > 0) {
49
- // Sort by hatchedAt descending to use the most recent entry,
50
- // matching the pattern used elsewhere in the codebase.
51
- const sorted = [...assistants].sort((a, b) => {
52
- const dateA = new Date((a.hatchedAt as string) || 0).getTime();
53
- const dateB = new Date((b.hatchedAt as string) || 0).getTime();
54
- return dateB - dateA;
55
- });
56
- const latest = sorted[0];
57
- if (typeof latest.assistantId === "string") {
58
- cached = latest.assistantId;
59
- log.info(
60
- { externalAssistantId: cached },
61
- "Resolved external assistant ID from lockfile",
62
- );
63
- return cached;
64
- }
65
- }
66
- }
67
- } catch (err) {
68
- log.warn({ err }, "Failed to read lockfile for external assistant ID");
69
- }
70
-
71
- // Fallback: derive from BASE_DATA_DIR path
72
- const base = getBaseDataDir();
73
- if (base && typeof base === "string") {
74
- const normalized = base.replace(/\\/g, "/").replace(/\/+$/, "");
75
- const match = normalized.match(/\/(?:assistants|instances)\/([^/]+)$/);
76
- if (match) {
77
- cached = match[1];
78
- log.info(
79
- { externalAssistantId: cached },
80
- "Resolved external assistant ID from BASE_DATA_DIR",
81
- );
82
- return cached;
83
- }
30
+ const envName = process.env.VELLUM_ASSISTANT_NAME;
31
+ if (envName) {
32
+ cached = envName;
33
+ log.info(
34
+ { externalAssistantId: cached },
35
+ "Resolved external assistant ID from VELLUM_ASSISTANT_NAME",
36
+ );
37
+ return cached;
84
38
  }
85
39
 
86
40
  cached = null;
@@ -259,6 +259,8 @@ const ACTOR_ENDPOINTS: Array<{ endpoint: string; scopes: Scope[] }> = [
259
259
 
260
260
  // Secrets
261
261
  { endpoint: "secrets", scopes: ["settings.write"] },
262
+ { endpoint: "secrets:GET", scopes: ["settings.read"] },
263
+ { endpoint: "secrets/read", scopes: ["settings.write"] },
262
264
 
263
265
  // Pairing (authenticated)
264
266
  { endpoint: "pairing/register", scopes: ["settings.write"] },
@@ -352,6 +354,10 @@ const ACTOR_ENDPOINTS: Array<{ endpoint: string; scopes: Scope[] }> = [
352
354
  { endpoint: "config/permissions/skip:GET", scopes: ["settings.read"] },
353
355
  { endpoint: "config/permissions/skip:PUT", scopes: ["settings.write"] },
354
356
 
357
+ // Generic config read/patch
358
+ { endpoint: "config:GET", scopes: ["settings.read"] },
359
+ { endpoint: "config:PATCH", scopes: ["settings.write"] },
360
+
355
361
  // Conversation management
356
362
  { endpoint: "conversations:DELETE", scopes: ["chat.write"] },
357
363
  { endpoint: "conversations/wipe", scopes: ["chat.write"] },
@@ -366,6 +372,7 @@ const ACTOR_ENDPOINTS: Array<{ endpoint: string; scopes: Scope[] }> = [
366
372
  // Message content
367
373
  { endpoint: "messages/content", scopes: ["chat.read"] },
368
374
  { endpoint: "messages/llm-context", scopes: ["chat.read"] },
375
+ { endpoint: "messages/tts", scopes: ["chat.read"] },
369
376
 
370
377
  // Queued message deletion
371
378
  { endpoint: "messages/queued", scopes: ["chat.write"] },
@@ -443,7 +450,6 @@ const ACTOR_ENDPOINTS: Array<{ endpoint: string; scopes: Scope[] }> = [
443
450
 
444
451
  // Diagnostics
445
452
  { endpoint: "export", scopes: ["settings.read"] },
446
- { endpoint: "diagnostics/export", scopes: ["settings.read"] },
447
453
  { endpoint: "diagnostics/env-vars", scopes: ["settings.read"] },
448
454
 
449
455
  // Dictation
@@ -512,3 +518,13 @@ registerPolicy("admin/upgrade-broadcast", {
512
518
  requiredScopes: ["internal.write"],
513
519
  allowedPrincipalTypes: ["svc_gateway"],
514
520
  });
521
+
522
+ registerPolicy("admin/workspace-commit", {
523
+ requiredScopes: ["internal.write"],
524
+ allowedPrincipalTypes: ["svc_gateway"],
525
+ });
526
+
527
+ registerPolicy("admin/rollback-migrations", {
528
+ requiredScopes: ["internal.write"],
529
+ allowedPrincipalTypes: ["svc_gateway"],
530
+ });