@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,15 +1,17 @@
1
1
  /**
2
- * Unified secure key storage — single-writer routing through CredentialBackend
2
+ * Unified secure key storage — single-backend routing through CredentialBackend
3
3
  * adapters.
4
4
  *
5
- * Backend selection (`resolveBackend`) is the single decision point:
6
- * - Containerized (IS_CONTAINERIZED + CES_CREDENTIAL_URL set): CES HTTP client.
7
- * - Production (VELLUM_DEV unset or "0"): keychain backend when available.
8
- * - Dev mode (VELLUM_DEV=1): encrypted file store always.
5
+ * Backend selection (`resolveBackendAsync`) is the single async decision point:
6
+ * 1. CES RPC (primary) injected via `setCesClient()`: delegates credential
7
+ * operations to the CES process over stdio RPC. This is the default path
8
+ * for all local modes (desktop app, dev, CLI).
9
+ * 2. CES HTTP — containerized mode (IS_CONTAINERIZED + CES_CREDENTIAL_URL):
10
+ * delegates to the CES sidecar over HTTP. Used in Docker/managed mode.
11
+ * 3. Encrypted file store (fallback) — used when CES is unavailable.
9
12
  *
10
- * Writes go to exactly one backend (no dual-writing). Reads in keychain mode
11
- * fall back to the encrypted store for keys that haven't been migrated yet.
12
- * Deletes clean up both stores regardless of mode.
13
+ * All operations (reads, writes, lists, deletes) go to exactly one backend.
14
+ * There are no cross-backend fallbacks or merges.
13
15
  */
14
16
 
15
17
  import type {
@@ -19,15 +21,21 @@ import type {
19
21
 
20
22
  import providerEnvVarsRegistry from "../../../meta/provider-env-vars.json" with { type: "json" };
21
23
  import { getIsContainerized } from "../config/env-registry.js";
24
+ import type { CesClient } from "../credential-execution/client.js";
22
25
  import { getLogger } from "../util/logger.js";
23
26
  import { createCesCredentialBackend } from "./ces-credential-client.js";
24
- import type { CredentialBackend, DeleteResult } from "./credential-backend.js";
25
- import {
26
- createEncryptedStoreBackend,
27
- createKeychainBackend,
27
+ import { CesRpcCredentialBackend } from "./ces-rpc-credential-backend.js";
28
+ import type {
29
+ CredentialBackend,
30
+ CredentialListResult,
31
+ DeleteResult,
28
32
  } from "./credential-backend.js";
33
+ import { createEncryptedStoreBackend } from "./credential-backend.js";
29
34
 
30
- export type { DeleteResult } from "./credential-backend.js";
35
+ export type {
36
+ CredentialListResult,
37
+ DeleteResult,
38
+ } from "./credential-backend.js";
31
39
 
32
40
  /**
33
41
  * Re-export shared-package secure-key abstractions so downstream consumers
@@ -36,15 +44,24 @@ export type { DeleteResult } from "./credential-backend.js";
36
44
  */
37
45
  export type { SecureKeyBackend, SecureKeyDeleteResult };
38
46
 
47
+ export interface SecureKeyResult {
48
+ value: string | undefined;
49
+ unreachable: boolean;
50
+ }
51
+
39
52
  const log = getLogger("secure-keys");
40
53
 
41
- let _keychain: CredentialBackend | undefined;
54
+ let _cesClient: CesClient | undefined;
42
55
  let _encryptedStore: CredentialBackend | undefined;
43
56
  let _resolvedBackend: CredentialBackend | undefined;
57
+ let _resolvePromise: Promise<CredentialBackend> | undefined;
44
58
 
45
- function getKeychainBackend(): CredentialBackend {
46
- if (!_keychain) _keychain = createKeychainBackend();
47
- return _keychain;
59
+ /** Inject a CES RPC client for credential routing. Resets the resolved backend. */
60
+ export function setCesClient(client: CesClient | undefined): void {
61
+ _cesClient = client;
62
+ // Reset resolved backend so next call picks up CES
63
+ _resolvedBackend = undefined;
64
+ _resolvePromise = undefined;
48
65
  }
49
66
 
50
67
  function getEncryptedStoreBackend(): CredentialBackend {
@@ -53,101 +70,98 @@ function getEncryptedStoreBackend(): CredentialBackend {
53
70
  }
54
71
 
55
72
  /**
56
- * Resolve the primary credential backend for this process.
73
+ * Resolve the primary credential backend for this process (async).
57
74
  *
58
75
  * Priority:
59
- * 1. Containerized + CES_CREDENTIAL_URLCES HTTP client (skip keychain
60
- * and encrypted store entirely the sidecar owns credential storage).
61
- * 2. Production (VELLUM_DEV unset or "0") keychain when available.
62
- * 3. Dev mode (VELLUM_DEV=1) → encrypted file store always.
76
+ * 1. CES RPC clientprimary path for all local modes.
77
+ * 2. Containerized + CES_CREDENTIAL_URL CES HTTP client (Docker/managed).
78
+ * 3. Encrypted file storefallback when CES is unavailable.
63
79
  *
64
80
  * Once resolved, the backend does not change during the process lifetime.
65
81
  * Call `_resetBackend()` in tests to clear the cached resolution.
66
82
  */
67
- function resolveBackend(): CredentialBackend {
68
- if (!_resolvedBackend) {
69
- if (getIsContainerized() && process.env.CES_CREDENTIAL_URL) {
70
- const ces = createCesCredentialBackend();
71
- if (ces.isAvailable()) {
72
- _resolvedBackend = ces;
73
- } else {
74
- log.warn(
75
- "CES_CREDENTIAL_URL is set but CES backend is not available — " +
76
- "falling back to local credential store",
77
- );
78
- }
83
+ async function resolveBackendAsync(): Promise<CredentialBackend> {
84
+ if (_resolvedBackend) return _resolvedBackend;
85
+ if (!_resolvePromise) {
86
+ _resolvePromise = doResolveBackend();
87
+ }
88
+ return _resolvePromise;
89
+ }
90
+
91
+ async function doResolveBackend(): Promise<CredentialBackend> {
92
+ // 1. CES RPC — primary credential backend for all local modes
93
+ if (_cesClient) {
94
+ const cesRpc = new CesRpcCredentialBackend(_cesClient);
95
+ if (cesRpc.isAvailable()) {
96
+ _resolvedBackend = cesRpc;
97
+ return cesRpc;
79
98
  }
99
+ log.warn(
100
+ "CES RPC client is set but not ready — falling back to local credential store",
101
+ );
102
+ }
80
103
 
81
- if (!_resolvedBackend) {
82
- if (
83
- process.env.VELLUM_DEV !== "1" &&
84
- getKeychainBackend().isAvailable()
85
- ) {
86
- _resolvedBackend = getKeychainBackend();
87
- } else {
88
- _resolvedBackend = getEncryptedStoreBackend();
89
- }
104
+ // 2. CES HTTP — containerized / Docker / managed mode
105
+ if (getIsContainerized() && process.env.CES_CREDENTIAL_URL) {
106
+ const ces = createCesCredentialBackend();
107
+ if (ces.isAvailable()) {
108
+ _resolvedBackend = ces;
109
+ return ces;
90
110
  }
111
+ log.warn(
112
+ "CES_CREDENTIAL_URL is set but CES backend is not available — " +
113
+ "falling back to local credential store",
114
+ );
91
115
  }
116
+
117
+ // 3. Encrypted file store — fallback when CES is unavailable
118
+ _resolvedBackend = getEncryptedStoreBackend();
92
119
  return _resolvedBackend;
93
120
  }
94
121
 
95
122
  /**
96
- * List all account names across both backends (async).
123
+ * List all account names from the resolved backend (async).
97
124
  *
98
- * In CES mode, only the CES backend is queried there are no local stores.
99
- *
100
- * When the primary backend is the keychain, this merges keys from the keychain
101
- * and the encrypted store (for legacy keys that haven't been migrated). The
102
- * result is deduplicated. When the primary backend is already the encrypted
103
- * store, only that store is queried.
125
+ * Queries exactly one backend — no cross-store merge.
104
126
  */
105
- export async function listSecureKeysAsync(): Promise<string[]> {
106
- const backend = resolveBackend();
107
- const primaryKeys = await backend.list();
108
-
109
- // CES mode — the sidecar is the single source of truth, no local merge.
110
- if (backend.name === "ces-http") return primaryKeys;
111
-
112
- // If primary backend is NOT the encrypted store, also check
113
- // the encrypted store for legacy keys that haven't been migrated.
114
- if (backend !== getEncryptedStoreBackend()) {
115
- const encKeys = await getEncryptedStoreBackend().list();
116
- const merged = new Set([...primaryKeys, ...encKeys]);
117
- return Array.from(merged);
118
- }
119
-
120
- return primaryKeys;
127
+ export async function listSecureKeysAsync(): Promise<CredentialListResult> {
128
+ const backend = await resolveBackendAsync();
129
+ return backend.list();
121
130
  }
122
131
 
123
132
  // ---------------------------------------------------------------------------
124
- // Async CRUD — single-writer routing
133
+ // Async CRUD — single-backend routing
125
134
  // ---------------------------------------------------------------------------
126
135
 
127
136
  /**
128
- * Retrieve a secret from secure storage. Reads from the primary backend
129
- * first. In CES mode, the sidecar is the single source of truth — no
130
- * local fallback. In local mode, if the primary backend is the keychain,
131
- * falls back to the encrypted store for legacy keys that haven't been
132
- * migrated.
137
+ * Retrieve a secret from secure storage with richer result metadata.
138
+ *
139
+ * Returns both the value (if found) and whether the backend was
140
+ * unreachable. Callers that need to distinguish "not found" from
141
+ * "backend down" should use this instead of `getSecureKeyAsync`.
142
+ *
143
+ * Reads from exactly one backend — no cross-store fallback.
133
144
  */
134
- export async function getSecureKeyAsync(
145
+ export async function getSecureKeyResultAsync(
135
146
  account: string,
136
- ): Promise<string | undefined> {
137
- const backend = resolveBackend();
147
+ ): Promise<SecureKeyResult> {
148
+ const backend = await resolveBackendAsync();
138
149
  const result = await backend.get(account);
139
- if (result != null) return result;
140
-
141
- // CES mode — no local fallback.
142
- if (backend.name === "ces-http") return undefined;
143
-
144
- // Legacy fallback: if primary backend is NOT the encrypted store,
145
- // check the encrypted store for keys that haven't been migrated.
146
- if (backend !== getEncryptedStoreBackend()) {
147
- return await getEncryptedStoreBackend().get(account);
150
+ if (result.value != null) {
151
+ return { value: result.value, unreachable: false };
148
152
  }
153
+ return { value: undefined, unreachable: result.unreachable };
154
+ }
149
155
 
150
- return undefined;
156
+ /**
157
+ * Retrieve a secret from secure storage. Convenience wrapper over
158
+ * `getSecureKeyResultAsync` that returns only the value.
159
+ */
160
+ export async function getSecureKeyAsync(
161
+ account: string,
162
+ ): Promise<string | undefined> {
163
+ const result = await getSecureKeyResultAsync(account);
164
+ return result.value;
151
165
  }
152
166
 
153
167
  /**
@@ -158,7 +172,7 @@ export async function setSecureKeyAsync(
158
172
  account: string,
159
173
  value: string,
160
174
  ): Promise<boolean> {
161
- const backend = resolveBackend();
175
+ const backend = await resolveBackendAsync();
162
176
  const ok = await backend.set(account, value);
163
177
  if (!ok) {
164
178
  log.warn(
@@ -172,38 +186,13 @@ export async function setSecureKeyAsync(
172
186
  /**
173
187
  * Delete a secret from secure storage.
174
188
  *
175
- * In containerized mode with CES, deletion is routed exclusively through the
176
- * CES backend — there are no local stores to clean up.
177
- *
178
- * In local mode, always attempts deletion on both the keychain backend (if
179
- * available) and the encrypted store backend, regardless of routing mode.
180
- * This cleans up legacy data from both stores.
189
+ * Deletes from exactly one backend no cross-store cleanup.
181
190
  */
182
191
  export async function deleteSecureKeyAsync(
183
192
  account: string,
184
193
  ): Promise<DeleteResult> {
185
- const backend = resolveBackend();
186
-
187
- // In CES mode, the sidecar is the only store — no local cleanup needed.
188
- if (backend.name === "ces-http") {
189
- return backend.delete(account);
190
- }
191
-
192
- const keychain = getKeychainBackend();
193
- const enc = getEncryptedStoreBackend();
194
-
195
- let keychainResult: DeleteResult = "not-found";
196
- if (keychain.isAvailable()) {
197
- keychainResult = await keychain.delete(account);
198
- }
199
-
200
- const encResult = await enc.delete(account);
201
-
202
- // Return "error" if either errored
203
- if (keychainResult === "error" || encResult === "error") return "error";
204
- // Return "deleted" if either deleted
205
- if (keychainResult === "deleted" || encResult === "deleted") return "deleted";
206
- return "not-found";
194
+ const backend = await resolveBackendAsync();
195
+ return backend.delete(account);
207
196
  }
208
197
 
209
198
  // ---------------------------------------------------------------------------
@@ -261,9 +250,18 @@ export async function getMaskedProviderKey(
261
250
  // Test helpers
262
251
  // ---------------------------------------------------------------------------
263
252
 
253
+ /**
254
+ * Return the name of the currently resolved credential backend.
255
+ * Useful for diagnostic messages when credential operations fail.
256
+ */
257
+ export function getActiveBackendName(): string {
258
+ return _resolvedBackend?.name ?? "none";
259
+ }
260
+
264
261
  /** @internal Test-only: reset the cached backends so they're re-created. */
265
262
  export function _resetBackend(): void {
266
- _keychain = undefined;
263
+ _cesClient = undefined;
267
264
  _encryptedStore = undefined;
268
265
  _resolvedBackend = undefined;
266
+ _resolvePromise = undefined;
269
267
  }
@@ -13,12 +13,9 @@ import { homedir } from "node:os";
13
13
  import { dirname, join, posix, resolve, sep } from "node:path";
14
14
  import { gunzipSync } from "node:zlib";
15
15
 
16
+ import { getPlatformBaseUrl } from "../config/env.js";
16
17
  import { getLogger } from "../util/logger.js";
17
- import {
18
- getWorkspaceConfigPath,
19
- getWorkspaceSkillsDir,
20
- readPlatformToken,
21
- } from "../util/platform.js";
18
+ import { getWorkspaceSkillsDir, readPlatformToken } from "../util/platform.js";
22
19
  import { deleteSkillCapabilityMemory } from "./skill-memory.js";
23
20
 
24
21
  const log = getLogger("catalog-install");
@@ -70,31 +67,6 @@ export function getRepoSkillsDir(): string | undefined {
70
67
 
71
68
  // ─── Platform API ────────────────────────────────────────────────────────────
72
69
 
73
- function getConfigPlatformUrl(): string | undefined {
74
- try {
75
- const configPath = getWorkspaceConfigPath();
76
- if (!existsSync(configPath)) return undefined;
77
- const raw = JSON.parse(readFileSync(configPath, "utf-8")) as Record<
78
- string,
79
- unknown
80
- >;
81
- const platform = raw.platform as Record<string, unknown> | undefined;
82
- const baseUrl = platform?.baseUrl;
83
- if (typeof baseUrl === "string" && baseUrl.trim()) return baseUrl.trim();
84
- } catch {
85
- // ignore
86
- }
87
- return undefined;
88
- }
89
-
90
- function getPlatformUrl(): string {
91
- return (
92
- process.env.VELLUM_PLATFORM_URL ??
93
- getConfigPlatformUrl() ??
94
- "https://platform.vellum.ai"
95
- );
96
- }
97
-
98
70
  function buildHeaders(): Record<string, string> {
99
71
  const headers: Record<string, string> = {};
100
72
  const token = readPlatformToken();
@@ -107,7 +79,8 @@ function buildHeaders(): Record<string, string> {
107
79
  // ─── Catalog operations ──────────────────────────────────────────────────────
108
80
 
109
81
  export async function fetchCatalog(): Promise<CatalogSkill[]> {
110
- const url = `${getPlatformUrl()}/v1/skills/`;
82
+ const platformUrl = getPlatformBaseUrl();
83
+ const url = `${platformUrl}/v1/skills/`;
111
84
  const response = await fetch(url, {
112
85
  headers: buildHeaders(),
113
86
  signal: AbortSignal.timeout(10000),
@@ -214,7 +187,8 @@ export async function fetchAndExtractSkill(
214
187
  skillId: string,
215
188
  destDir: string,
216
189
  ): Promise<void> {
217
- const url = `${getPlatformUrl()}/v1/skills/${encodeURIComponent(skillId)}/`;
190
+ const platformUrl = getPlatformBaseUrl();
191
+ const url = `${platformUrl}/v1/skills/${encodeURIComponent(skillId)}/`;
218
192
  const response = await fetch(url, {
219
193
  headers: buildHeaders(),
220
194
  signal: AbortSignal.timeout(15000),
@@ -123,6 +123,7 @@ export function upsertSkillCapabilityMemory(
123
123
  confidence,
124
124
  importance,
125
125
  fingerprint,
126
+ sourceType: "extraction",
126
127
  scopeId,
127
128
  firstSeenAt: now,
128
129
  lastSeenAt: now,
@@ -18,7 +18,7 @@ import {
18
18
  import type { ServerMessage } from "../daemon/message-protocol.js";
19
19
  import { bootstrapConversation } from "../memory/conversation-bootstrap.js";
20
20
  import { RateLimitProvider } from "../providers/ratelimit.js";
21
- import { getFailoverProvider } from "../providers/registry.js";
21
+ import { getProvider } from "../providers/registry.js";
22
22
  import { getLogger } from "../util/logger.js";
23
23
  import { getSandboxWorkingDir } from "../util/platform.js";
24
24
  import {
@@ -129,10 +129,7 @@ export class SubagentManager {
129
129
 
130
130
  // ── Build conversation dependencies ─────────────────────────────
131
131
  const appConfig = getConfig();
132
- let provider = getFailoverProvider(
133
- appConfig.services.inference.provider,
134
- appConfig.providerOrder,
135
- );
132
+ let provider = getProvider(appConfig.services.inference.provider);
136
133
  const { rateLimit } = appConfig;
137
134
  if (rateLimit.maxRequestsPerMinute > 0) {
138
135
  provider = new RateLimitProvider(
@@ -140,7 +140,7 @@ export class UsageTelemetryReporter {
140
140
 
141
141
  // Resolve auth context — skip flush when neither auth mode is viable
142
142
  const client = await VellumPlatformClient.create();
143
- if (!client && !getTelemetryAppToken()) {
143
+ if (!client && (!getTelemetryAppToken() || !getTelemetryPlatformUrl())) {
144
144
  return;
145
145
  }
146
146
 
@@ -203,7 +203,9 @@ export class UsageTelemetryReporter {
203
203
  if (client) {
204
204
  resp = await client.fetch(TELEMETRY_PATH, fetchInit);
205
205
  } else {
206
- const url = `${getTelemetryPlatformUrl()}${TELEMETRY_PATH}`;
206
+ const platformUrl = getTelemetryPlatformUrl();
207
+ if (!platformUrl) return;
208
+ const url = `${platformUrl}${TELEMETRY_PATH}`;
207
209
  resp = await fetch(url, {
208
210
  ...fetchInit,
209
211
  headers: {
@@ -1,7 +1,68 @@
1
+ import { execFile } from "node:child_process";
2
+ import { promisify } from "node:util";
3
+
1
4
  import { getAcpSessionManager } from "../../acp/index.js";
2
5
  import { getConfig } from "../../config/loader.js";
6
+ import { getLogger } from "../../util/logger.js";
3
7
  import type { ToolContext, ToolExecutionResult } from "../types.js";
4
8
 
9
+ const execFileAsync = promisify(execFile);
10
+ const log = getLogger("acp:spawn");
11
+
12
+ /** Cache so we only check once per process lifetime. */
13
+ let adapterVersionChecked = false;
14
+
15
+ interface AdapterVersionInfo {
16
+ outdated: true;
17
+ installed: string;
18
+ latest: string;
19
+ }
20
+
21
+ /**
22
+ * Checks if the globally-installed claude-agent-acp adapter is outdated.
23
+ * Runs at most once per process lifetime. Does NOT auto-update — returns
24
+ * version info so the caller can ask the user first.
25
+ */
26
+ async function checkAdapterVersion(
27
+ command: string,
28
+ ): Promise<AdapterVersionInfo | null> {
29
+ if (adapterVersionChecked || command !== "claude-agent-acp") {
30
+ return null;
31
+ }
32
+
33
+ try {
34
+ const { stdout: installedRaw } = await execFileAsync("npm", [
35
+ "ls",
36
+ "-g",
37
+ "--json",
38
+ "@zed-industries/claude-agent-acp",
39
+ ]);
40
+ const { stdout: latestRaw } = await execFileAsync("npm", [
41
+ "view",
42
+ "@zed-industries/claude-agent-acp",
43
+ "version",
44
+ ]);
45
+
46
+ const installed =
47
+ JSON.parse(installedRaw)?.dependencies?.[
48
+ "@zed-industries/claude-agent-acp"
49
+ ]?.version;
50
+ const latest = latestRaw.trim();
51
+
52
+ adapterVersionChecked = true;
53
+
54
+ if (!installed || !latest || installed === latest) {
55
+ return null;
56
+ }
57
+
58
+ log.info({ installed, latest }, "claude-agent-acp is outdated");
59
+ return { outdated: true, installed, latest };
60
+ } catch (err) {
61
+ log.warn({ err }, "Failed to check claude-agent-acp version");
62
+ return null;
63
+ }
64
+ }
65
+
5
66
  export async function executeAcpSpawn(
6
67
  input: Record<string, unknown>,
7
68
  context: ToolContext,
@@ -37,6 +98,17 @@ export async function executeAcpSpawn(
37
98
  };
38
99
  }
39
100
 
101
+ // Check if the ACP adapter is outdated before spawning
102
+ const versionInfo = await checkAdapterVersion(agentConfig.command);
103
+ if (versionInfo) {
104
+ return {
105
+ content:
106
+ `claude-agent-acp is outdated (installed: ${versionInfo.installed}, latest: ${versionInfo.latest}). ` +
107
+ `Ask the user if they'd like to update. If yes, run: npm install -g @zed-industries/claude-agent-acp@${versionInfo.latest} — then retry acp_spawn.`,
108
+ isError: true,
109
+ };
110
+ }
111
+
40
112
  try {
41
113
  const manager = getAcpSessionManager();
42
114
  const cwd = (input.cwd as string) || context.workingDir;
@@ -64,7 +136,12 @@ export async function executeAcpSpawn(
64
136
  isError: false,
65
137
  };
66
138
  } catch (err) {
67
- const msg = err instanceof Error ? err.message : String(err);
139
+ const msg =
140
+ err instanceof Error
141
+ ? err.message
142
+ : typeof err === "object" && err !== undefined
143
+ ? JSON.stringify(err)
144
+ : String(err);
68
145
  return { content: `Failed to spawn ACP agent: ${msg}`, isError: true };
69
146
  }
70
147
  }
@@ -46,6 +46,7 @@ export async function executeCallStart(
46
46
  | "assistant_number"
47
47
  | "user_number"
48
48
  | undefined,
49
+ skipDisclosure: input.skip_disclosure === true,
49
50
  });
50
51
 
51
52
  if (!result.ok) {
@@ -400,8 +400,9 @@ class CredentialStoreTool implements Tool {
400
400
  const credIdSuffix = metadata
401
401
  ? ` (credential_id: ${metadata.credentialId})`
402
402
  : "";
403
+ const retrieveHint = ` Retrieve with: \`assistant credentials reveal --service ${service} --field ${field}\``;
403
404
  return {
404
- content: `Stored credential for ${service}/${field}.${credIdSuffix}${
405
+ content: `Stored credential for ${service}/${field}.${credIdSuffix}${retrieveHint}${
405
406
  slackChannelResult
406
407
  ? formatSlackChannelStatus(slackChannelResult)
407
408
  : ""
@@ -428,7 +429,7 @@ class CredentialStoreTool implements Tool {
428
429
  // (e.g. keychain) and the encrypted store (legacy keys).
429
430
  let secureKeySet: Set<string> | undefined;
430
431
  try {
431
- secureKeySet = new Set(await listSecureKeysAsync());
432
+ secureKeySet = new Set((await listSecureKeysAsync()).accounts);
432
433
  } catch (err) {
433
434
  log.error(
434
435
  { err },
@@ -810,8 +811,9 @@ class CredentialStoreTool implements Tool {
810
811
  const promptCredIdSuffix = promptMeta
811
812
  ? ` (credential_id: ${promptMeta.credentialId})`
812
813
  : "";
814
+ const promptRetrieveHint = ` Retrieve with: \`assistant credentials reveal --service ${service} --field ${field}\``;
813
815
  return {
814
- content: `Credential stored for ${service}/${field}.${promptCredIdSuffix}${
816
+ content: `Credential stored for ${service}/${field}.${promptCredIdSuffix}${promptRetrieveHint}${
815
817
  slackChannelResult
816
818
  ? formatSlackChannelStatus(slackChannelResult)
817
819
  : ""
@@ -183,10 +183,6 @@ export class ToolExecutor {
183
183
  );
184
184
  // Buffer so the shell's own timeout fires first and handles cleanup
185
185
  toolTimeoutMs = (shellTimeoutSec + 5) * 1000;
186
- } else if (name === "claude_code") {
187
- // Claude Code spawns a subprocess that manages its own turn limits
188
- // (maxTurns). Give it a generous timeout so it isn't killed mid-task.
189
- toolTimeoutMs = 10 * 60 * 1000; // 10 minutes
190
186
  } else {
191
187
  const rawTimeoutSec = getConfig().timeouts.toolExecutionTimeoutSec;
192
188
  toolTimeoutMs = safeTimeoutMs(rawTimeoutSec);
@@ -46,9 +46,10 @@ const memoryManageProperties = {
46
46
  "decision",
47
47
  "constraint",
48
48
  "event",
49
+ "journal",
49
50
  ],
50
51
  description:
51
- 'Category of the memory item (required for save). Use "constraint" for mistakes, gotchas, discoveries, and working solutions - write as advice to your future self.',
52
+ 'Category of the memory item (required for save). Use "constraint" for mistakes, gotchas, discoveries, and working solutions - write as advice to your future self. Use "journal" for journal-style memories — experiential snapshots, upcoming events, things to carry forward.',
52
53
  },
53
54
  subject: {
54
55
  type: "string" as const,
@@ -59,7 +60,7 @@ const memoryManageProperties = {
59
60
  export const memoryManageDefinition: ToolDefinition = {
60
61
  name: "memory_manage",
61
62
  description:
62
- "Save, update, or delete memory items. Memory does not survive session restarts - if you want to remember something, save it now. Use 'save' for new information worth remembering (facts, preferences, mistakes, discoveries, gotchas), 'update' to correct existing items, 'delete' to remove outdated items. When a user says 'remember this', save immediately. For user profile or personality changes, update workspace files (USER.md, SOUL.md) instead.",
63
+ "Save, update, or delete memory items. If you want to remember something, save it now. Use 'save' for new information worth remembering (facts, preferences, mistakes, discoveries, gotchas), 'update' to correct existing items, 'delete' to remove outdated items. When a user says 'remember this', save immediately. Be proactive: if you learn something important that may be useful in the future, always call this tool — don't just say or hope you'll remember it. This is not a substitute for updating workspace files when relevant - do both.",
63
64
  input_schema: {
64
65
  type: "object",
65
66
  properties: memoryManageProperties,