@vellumai/assistant 0.4.56 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (457) hide show
  1. package/ARCHITECTURE.md +10 -10
  2. package/Dockerfile +3 -0
  3. package/README.md +11 -11
  4. package/docs/architecture/integrations.md +2 -2
  5. package/docs/architecture/memory.md +3 -4
  6. package/docs/credential-execution-service.md +13 -20
  7. package/node_modules/@vellumai/ces-contracts/src/error.ts +5 -4
  8. package/package.json +1 -1
  9. package/src/__tests__/actor-token-service.test.ts +7 -7
  10. package/src/__tests__/anthropic-provider.test.ts +172 -0
  11. package/src/__tests__/app-builder-tool-scripts.test.ts +15 -1
  12. package/src/__tests__/approval-cascade.test.ts +2 -2
  13. package/src/__tests__/approval-routes-http.test.ts +3 -4
  14. package/src/__tests__/asset-materialize-tool.test.ts +5 -5
  15. package/src/__tests__/asset-search-tool.test.ts +1 -1
  16. package/src/__tests__/assistant-attachments.test.ts +5 -5
  17. package/src/__tests__/assistant-events-sse-hardening.test.ts +1 -1
  18. package/src/__tests__/assistant-feature-flags-integration.test.ts +50 -38
  19. package/src/__tests__/attachments-store.test.ts +2 -2
  20. package/src/__tests__/avatar-e2e.test.ts +5 -3
  21. package/src/__tests__/browser-skill-endstate.test.ts +0 -1
  22. package/src/__tests__/call-routes-http.test.ts +2 -2
  23. package/src/__tests__/callback-handoff-copy.test.ts +1 -1
  24. package/src/__tests__/cancel-resolves-conversation-key.test.ts +158 -0
  25. package/src/__tests__/channel-readiness-routes.test.ts +0 -1
  26. package/src/__tests__/channel-readiness-service.test.ts +0 -1
  27. package/src/__tests__/checker.test.ts +31 -32
  28. package/src/__tests__/chrome-cdp.test.ts +47 -18
  29. package/src/__tests__/claude-code-skill-regression.test.ts +2 -2
  30. package/src/__tests__/config-schema-cmd.test.ts +2 -2
  31. package/src/__tests__/config-schema.test.ts +9 -18
  32. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +1 -1
  33. package/src/__tests__/conversation-abort-tool-results.test.ts +4 -4
  34. package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -2
  35. package/src/__tests__/conversation-agent-loop.test.ts +11 -4
  36. package/src/__tests__/conversation-attachments.test.ts +1 -1
  37. package/src/__tests__/conversation-confirmation-signals.test.ts +2 -2
  38. package/src/__tests__/conversation-error.test.ts +33 -0
  39. package/src/__tests__/conversation-init.benchmark.test.ts +0 -1
  40. package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
  41. package/src/__tests__/conversation-pairing.test.ts +1 -1
  42. package/src/__tests__/conversation-pre-run-repair.test.ts +4 -4
  43. package/src/__tests__/conversation-provider-retry-repair.test.ts +4 -4
  44. package/src/__tests__/conversation-queue.test.ts +23 -14
  45. package/src/__tests__/conversation-routes-slash-commands.test.ts +3 -3
  46. package/src/__tests__/conversation-runtime-assembly.test.ts +204 -185
  47. package/src/__tests__/conversation-seed-composer.test.ts +1 -1
  48. package/src/__tests__/conversation-slash-queue.test.ts +4 -4
  49. package/src/__tests__/conversation-slash-unknown.test.ts +4 -4
  50. package/src/__tests__/conversation-starter-routes.test.ts +291 -0
  51. package/src/__tests__/conversation-wipe.test.ts +438 -0
  52. package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -3
  53. package/src/__tests__/conversation-workspace-injection.test.ts +4 -5
  54. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +4 -5
  55. package/src/__tests__/credential-security-e2e.test.ts +20 -0
  56. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  57. package/src/__tests__/credential-vault-unit.test.ts +227 -0
  58. package/src/__tests__/credentials-cli.test.ts +3 -0
  59. package/src/__tests__/date-context.test.ts +59 -377
  60. package/src/__tests__/drop-capability-card-state-migration.test.ts +169 -0
  61. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +11 -45
  62. package/src/__tests__/emit-signal-routing-intent.test.ts +3 -3
  63. package/src/__tests__/encrypted-store.test.ts +249 -15
  64. package/src/__tests__/ephemeral-permissions.test.ts +4 -5
  65. package/src/__tests__/event-bus.test.ts +3 -3
  66. package/src/__tests__/file-read-tool.test.ts +40 -0
  67. package/src/__tests__/gateway-only-enforcement.test.ts +2 -2
  68. package/src/__tests__/gateway-only-guard.test.ts +1 -0
  69. package/src/__tests__/gemini-image-service.test.ts +4 -4
  70. package/src/__tests__/gemini-provider.test.ts +6 -9
  71. package/src/__tests__/guardian-binding-drift-heal.test.ts +128 -0
  72. package/src/__tests__/guardian-dispatch.test.ts +0 -1
  73. package/src/__tests__/host-file-read-tool.test.ts +87 -0
  74. package/src/__tests__/host-shell-tool.test.ts +6 -6
  75. package/src/__tests__/http-user-message-parity.test.ts +2 -2
  76. package/src/__tests__/identity-intro-cache.test.ts +209 -0
  77. package/src/__tests__/intent-routing.test.ts +51 -99
  78. package/src/__tests__/invite-routes-http.test.ts +5 -0
  79. package/src/__tests__/list-messages-attachments.test.ts +1 -1
  80. package/src/__tests__/managed-proxy-context.test.ts +2 -5
  81. package/src/__tests__/managed-skill-lifecycle.test.ts +8 -8
  82. package/src/__tests__/media-generate-image.test.ts +32 -15
  83. package/src/__tests__/media-reuse-story.e2e.test.ts +1 -1
  84. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +1 -1
  85. package/src/__tests__/memory-lifecycle-e2e.test.ts +24 -18
  86. package/src/__tests__/memory-recall-quality.test.ts +4 -3
  87. package/src/__tests__/memory-regressions.test.ts +86 -90
  88. package/src/__tests__/migration-cross-version-compatibility.test.ts +32 -32
  89. package/src/__tests__/migration-export-http.test.ts +26 -27
  90. package/src/__tests__/migration-import-commit-http.test.ts +165 -37
  91. package/src/__tests__/migration-import-preflight-http.test.ts +81 -20
  92. package/src/__tests__/migration-validate-http.test.ts +16 -16
  93. package/src/__tests__/model-intents.test.ts +2 -2
  94. package/src/__tests__/no-domain-routing-in-prompt-guard.test.ts +1 -1
  95. package/src/__tests__/non-member-access-request.test.ts +3 -3
  96. package/src/__tests__/notification-broadcaster.test.ts +1 -1
  97. package/src/__tests__/notification-decision-fallback.test.ts +2 -2
  98. package/src/__tests__/notification-decision-identity.test.ts +8 -9
  99. package/src/__tests__/notification-decision-strategy.test.ts +1 -1
  100. package/src/__tests__/notification-deep-link.test.ts +1 -1
  101. package/src/__tests__/notification-guardian-path.test.ts +0 -1
  102. package/src/__tests__/notification-schedule-dedup.test.ts +7 -7
  103. package/src/__tests__/oauth-store.test.ts +1 -3
  104. package/src/__tests__/oauth2-gateway-transport.test.ts +6 -1
  105. package/src/__tests__/onboarding-template-contract.test.ts +23 -59
  106. package/src/__tests__/provider-error-scenarios.test.ts +154 -0
  107. package/src/__tests__/provider-fail-open-selection.test.ts +2 -2
  108. package/src/__tests__/provider-managed-proxy-integration.test.ts +8 -9
  109. package/src/__tests__/provider-registry-ollama.test.ts +5 -2
  110. package/src/__tests__/qdrant-manager.test.ts +7 -7
  111. package/src/__tests__/ratelimit.test.ts +0 -74
  112. package/src/__tests__/recording-handler.test.ts +0 -1
  113. package/src/__tests__/require-fresh-approval.test.ts +1 -1
  114. package/src/__tests__/runtime-attachment-metadata.test.ts +1 -1
  115. package/src/__tests__/runtime-events-sse-parity.test.ts +1 -1
  116. package/src/__tests__/runtime-events-sse.test.ts +1 -1
  117. package/src/__tests__/scheduler-recurrence.test.ts +46 -2
  118. package/src/__tests__/schema-transforms.test.ts +114 -54
  119. package/src/__tests__/secret-onetime-send.test.ts +20 -0
  120. package/src/__tests__/secret-routes-managed-proxy.test.ts +5 -2
  121. package/src/__tests__/secret-scanner-executor.test.ts +1 -2
  122. package/src/__tests__/send-endpoint-busy.test.ts +63 -4
  123. package/src/__tests__/send-notification-tool.test.ts +2 -2
  124. package/src/__tests__/shell-credential-ref.test.ts +0 -1
  125. package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -2
  126. package/src/__tests__/skill-memory.test.ts +549 -0
  127. package/src/__tests__/skill-script-runner-sandbox.test.ts +1 -2
  128. package/src/__tests__/slack-app-setup-skill-regression.test.ts +37 -0
  129. package/src/__tests__/slack-channel-config.test.ts +109 -94
  130. package/src/__tests__/swarm-conversation-integration.test.ts +2 -2
  131. package/src/__tests__/swarm-recursion.test.ts +2 -2
  132. package/src/__tests__/swarm-tool.test.ts +2 -2
  133. package/src/__tests__/system-prompt.test.ts +19 -66
  134. package/src/__tests__/telegram-config.test.ts +121 -0
  135. package/src/__tests__/terminal-tools.test.ts +1 -1
  136. package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -2
  137. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
  138. package/src/__tests__/tool-executor-shell-integration.test.ts +1 -1
  139. package/src/__tests__/tool-executor.test.ts +1 -1
  140. package/src/__tests__/trace-emitter.test.ts +8 -1
  141. package/src/__tests__/trust-store.test.ts +7 -8
  142. package/src/__tests__/twilio-routes.test.ts +1 -18
  143. package/src/__tests__/user-reference.test.ts +82 -2
  144. package/src/__tests__/vbundle-pax-and-symlink.test.ts +196 -0
  145. package/src/__tests__/verification-control-plane-policy.test.ts +1 -1
  146. package/src/approvals/guardian-request-resolvers.ts +3 -3
  147. package/src/avatar/ascii-renderer.ts +2 -2
  148. package/src/avatar/png-renderer.ts +2 -2
  149. package/src/avatar/resvg-lazy.ts +21 -0
  150. package/src/calls/guardian-dispatch.ts +1 -1
  151. package/src/calls/relay-access-wait.ts +2 -2
  152. package/src/calls/twilio-rest.ts +0 -248
  153. package/src/cli/AGENTS.md +5 -8
  154. package/src/cli/__tests__/notifications.test.ts +5 -5
  155. package/src/cli/commands/avatar.ts +64 -2
  156. package/src/cli/commands/conversations.ts +131 -1
  157. package/src/cli/commands/credentials.ts +2 -0
  158. package/src/cli/commands/notifications.ts +3 -3
  159. package/src/cli.ts +10 -0
  160. package/src/config/bundled-skills/acp/SKILL.md +5 -5
  161. package/src/config/bundled-skills/acp/TOOLS.json +6 -6
  162. package/src/config/bundled-skills/app-builder/SKILL.md +42 -42
  163. package/src/config/bundled-skills/app-builder/TOOLS.json +10 -10
  164. package/src/config/bundled-skills/browser/SKILL.md +15 -15
  165. package/src/config/bundled-skills/browser/TOOLS.json +14 -14
  166. package/src/config/bundled-skills/chatgpt-import/SKILL.md +2 -2
  167. package/src/config/bundled-skills/chatgpt-import/TOOLS.json +1 -1
  168. package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +1 -1
  169. package/src/config/bundled-skills/claude-code/SKILL.md +5 -5
  170. package/src/config/bundled-skills/computer-use/SKILL.md +2 -2
  171. package/src/config/bundled-skills/computer-use/TOOLS.json +15 -15
  172. package/src/config/bundled-skills/contacts/SKILL.md +3 -3
  173. package/src/config/bundled-skills/contacts/TOOLS.json +4 -4
  174. package/src/config/bundled-skills/document/SKILL.md +4 -4
  175. package/src/config/bundled-skills/document/TOOLS.json +2 -2
  176. package/src/config/bundled-skills/followups/TOOLS.json +3 -3
  177. package/src/config/bundled-skills/gmail/SKILL.md +32 -32
  178. package/src/config/bundled-skills/gmail/TOOLS.json +16 -16
  179. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +1 -1
  180. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +1 -1
  181. package/src/config/bundled-skills/google-calendar/SKILL.md +1 -1
  182. package/src/config/bundled-skills/google-calendar/TOOLS.json +5 -5
  183. package/src/config/bundled-skills/google-calendar/types.ts +1 -1
  184. package/src/config/bundled-skills/heartbeat/SKILL.md +43 -0
  185. package/src/config/bundled-skills/image-studio/SKILL.md +3 -3
  186. package/src/config/bundled-skills/image-studio/TOOLS.json +2 -3
  187. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +16 -12
  188. package/src/config/bundled-skills/media-processing/SKILL.md +40 -40
  189. package/src/config/bundled-skills/media-processing/TOOLS.json +8 -8
  190. package/src/config/bundled-skills/media-processing/__tests__/concurrency-pool.test.ts +2 -2
  191. package/src/config/bundled-skills/media-processing/__tests__/preprocess.test.ts +1 -1
  192. package/src/config/bundled-skills/media-processing/services/gemini-map.ts +5 -5
  193. package/src/config/bundled-skills/media-processing/services/gemini-video.ts +2 -2
  194. package/src/config/bundled-skills/media-processing/services/preprocess.ts +2 -2
  195. package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +2 -2
  196. package/src/config/bundled-skills/media-processing/services/reduce.ts +3 -3
  197. package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -2
  198. package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +1 -1
  199. package/src/config/bundled-skills/messaging/SKILL.md +29 -25
  200. package/src/config/bundled-skills/messaging/TOOLS.json +11 -11
  201. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +1 -1
  202. package/src/config/bundled-skills/messaging/tools/shared.ts +1 -1
  203. package/src/config/bundled-skills/notifications/SKILL.md +3 -3
  204. package/src/config/bundled-skills/notifications/TOOLS.json +2 -2
  205. package/src/config/bundled-skills/notifications/tools/send-notification.ts +3 -3
  206. package/src/config/bundled-skills/orchestration/SKILL.md +1 -1
  207. package/src/config/bundled-skills/orchestration/TOOLS.json +1 -1
  208. package/src/config/bundled-skills/phone-calls/SKILL.md +18 -14
  209. package/src/config/bundled-skills/phone-calls/TOOLS.json +3 -3
  210. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +2 -2
  211. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +2 -2
  212. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +1 -1
  213. package/src/config/bundled-skills/playbooks/TOOLS.json +4 -4
  214. package/src/config/bundled-skills/schedule/SKILL.md +26 -26
  215. package/src/config/bundled-skills/schedule/TOOLS.json +5 -5
  216. package/src/config/bundled-skills/screen-watch/SKILL.md +3 -3
  217. package/src/config/bundled-skills/screen-watch/TOOLS.json +1 -1
  218. package/src/config/bundled-skills/sequences/SKILL.md +2 -2
  219. package/src/config/bundled-skills/sequences/TOOLS.json +10 -10
  220. package/src/config/bundled-skills/sequences/tools/sequence-analytics.ts +2 -2
  221. package/src/config/bundled-skills/sequences/tools/sequence-enroll.ts +2 -2
  222. package/src/config/bundled-skills/sequences/tools/sequence-enrollment-list.ts +1 -1
  223. package/src/config/bundled-skills/sequences/tools/sequence-get.ts +1 -1
  224. package/src/config/bundled-skills/sequences/tools/sequence-import.ts +3 -3
  225. package/src/config/bundled-skills/sequences/tools/sequence-list.ts +1 -1
  226. package/src/config/bundled-skills/sequences/tools/sequence-update.ts +1 -1
  227. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  228. package/src/config/bundled-skills/settings/tools/open-system-settings.ts +1 -1
  229. package/src/config/bundled-skills/skill-management/TOOLS.json +5 -5
  230. package/src/config/bundled-skills/skills-catalog/SKILL.md +84 -0
  231. package/src/config/bundled-skills/slack/SKILL.md +2 -2
  232. package/src/config/bundled-skills/slack/TOOLS.json +8 -8
  233. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +3 -3
  234. package/src/config/bundled-skills/subagent/TOOLS.json +5 -5
  235. package/src/config/bundled-skills/tasks/SKILL.md +1 -1
  236. package/src/config/bundled-skills/tasks/TOOLS.json +9 -9
  237. package/src/config/bundled-skills/transcribe/SKILL.md +5 -5
  238. package/src/config/bundled-skills/transcribe/TOOLS.json +1 -1
  239. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +10 -10
  240. package/src/config/bundled-skills/watcher/SKILL.md +4 -4
  241. package/src/config/bundled-skills/watcher/TOOLS.json +5 -5
  242. package/src/config/feature-flag-registry.json +33 -17
  243. package/src/config/schemas/sandbox.ts +1 -1
  244. package/src/config/schemas/services.ts +13 -3
  245. package/src/config/schemas/timeouts.ts +0 -10
  246. package/src/contacts/contact-store.ts +63 -0
  247. package/src/contacts/contacts-write.ts +1 -1
  248. package/src/daemon/assistant-attachments.ts +2 -2
  249. package/src/daemon/conversation-agent-loop-handlers.ts +2 -2
  250. package/src/daemon/conversation-agent-loop.ts +7 -30
  251. package/src/daemon/conversation-error.ts +24 -0
  252. package/src/daemon/conversation-memory.ts +8 -7
  253. package/src/daemon/conversation-runtime-assembly.ts +141 -275
  254. package/src/daemon/conversation-slash.ts +7 -26
  255. package/src/daemon/conversation-surfaces.ts +14 -0
  256. package/src/daemon/conversation-tool-setup.ts +9 -8
  257. package/src/daemon/conversation.ts +2 -0
  258. package/src/daemon/daemon-control.ts +1 -1
  259. package/src/daemon/date-context.ts +10 -83
  260. package/src/daemon/handlers/config-channels.ts +12 -2
  261. package/src/daemon/handlers/config-slack-channel.ts +7 -1
  262. package/src/daemon/handlers/config-telegram.ts +6 -1
  263. package/src/daemon/handlers/conversations.ts +2 -2
  264. package/src/daemon/handlers/skills.ts +4 -0
  265. package/src/daemon/lifecycle.ts +28 -4
  266. package/src/daemon/providers-setup.ts +1 -1
  267. package/src/daemon/server.ts +1 -5
  268. package/src/daemon/shutdown-handlers.ts +9 -3
  269. package/src/daemon/tool-side-effects.ts +40 -0
  270. package/src/daemon/trace-emitter.ts +26 -2
  271. package/src/events/domain-events.ts +1 -1
  272. package/src/events/tool-permission-telemetry-listener.ts +46 -0
  273. package/src/inbound/platform-callback-registration.ts +0 -18
  274. package/src/media/app-icon-generator.ts +15 -8
  275. package/src/media/avatar-router.ts +15 -8
  276. package/src/media/gemini-image-service.ts +125 -21
  277. package/src/memory/attachments-store.ts +3 -3
  278. package/src/memory/channel-verification-sessions.ts +6 -6
  279. package/src/memory/conversation-crud.ts +196 -1
  280. package/src/memory/{thread-starters-cadence.ts → conversation-starters-cadence.ts} +9 -42
  281. package/src/memory/conversation-title-service.ts +2 -3
  282. package/src/memory/db-init.ts +25 -1
  283. package/src/memory/invite-store.ts +4 -4
  284. package/src/memory/items-extractor.ts +4 -4
  285. package/src/memory/job-handlers/{thread-starters.ts → conversation-starters.ts} +123 -38
  286. package/src/memory/jobs-store.ts +3 -2
  287. package/src/memory/jobs-worker.ts +7 -5
  288. package/src/memory/lifecycle-events-store.ts +63 -0
  289. package/src/memory/migrations/172-rename-created-by-session-id.ts +27 -0
  290. package/src/memory/migrations/173-rename-source-session-id.ts +16 -0
  291. package/src/memory/migrations/174-rename-thread-starters-table.ts +52 -0
  292. package/src/memory/migrations/175-create-lifecycle-events.ts +15 -0
  293. package/src/memory/migrations/176-drop-capability-card-state.ts +36 -0
  294. package/src/memory/migrations/177-create-trace-events-table.ts +40 -0
  295. package/src/memory/migrations/index.ts +6 -0
  296. package/src/memory/migrations/registry.ts +13 -0
  297. package/src/memory/retriever.test.ts +223 -96
  298. package/src/memory/retriever.ts +115 -138
  299. package/src/memory/schema/calls.ts +1 -1
  300. package/src/memory/schema/contacts.ts +1 -1
  301. package/src/memory/schema/infrastructure.ts +29 -0
  302. package/src/memory/schema/memory-core.ts +7 -17
  303. package/src/memory/schema/notifications.ts +1 -1
  304. package/src/memory/search/formatting.ts +23 -6
  305. package/src/memory/search/lexical.ts +2 -0
  306. package/src/memory/search/semantic.ts +2 -0
  307. package/src/memory/search/staleness.ts +5 -1
  308. package/src/memory/search/types.ts +4 -0
  309. package/src/memory/task-memory-cleanup.ts +96 -6
  310. package/src/memory/trace-event-store.ts +148 -0
  311. package/src/notifications/README.md +1 -1
  312. package/src/notifications/decision-engine.ts +45 -4
  313. package/src/notifications/emit-signal.ts +5 -4
  314. package/src/notifications/events-store.ts +4 -4
  315. package/src/notifications/signal.ts +1 -1
  316. package/src/oauth/manual-token-connection.ts +49 -25
  317. package/src/permissions/checker.ts +6 -5
  318. package/src/permissions/defaults.ts +4 -4
  319. package/src/prompts/__tests__/build-cli-reference-section.test.ts +9 -90
  320. package/src/prompts/cache-boundary.ts +8 -0
  321. package/src/prompts/system-prompt.ts +105 -634
  322. package/src/prompts/templates/BOOTSTRAP.md +172 -33
  323. package/src/prompts/templates/IDENTITY.md +8 -24
  324. package/src/prompts/templates/SOUL.md +20 -41
  325. package/src/prompts/templates/USER.md +3 -19
  326. package/src/prompts/user-reference.ts +14 -16
  327. package/src/providers/anthropic/client.ts +51 -19
  328. package/src/providers/gemini/client.ts +6 -9
  329. package/src/providers/managed-proxy/constants.ts +1 -7
  330. package/src/providers/managed-proxy/context.ts +0 -1
  331. package/src/providers/model-intents.ts +5 -5
  332. package/src/providers/openai/client.ts +10 -1
  333. package/src/providers/openrouter/client.ts +1 -0
  334. package/src/providers/ratelimit.ts +0 -35
  335. package/src/providers/registry.ts +3 -5
  336. package/src/providers/retry.ts +18 -1
  337. package/src/runtime/access-request-helper.ts +16 -2
  338. package/src/runtime/auth/route-policy.ts +7 -0
  339. package/src/runtime/channel-verification-service.ts +1 -1
  340. package/src/runtime/confirmation-request-guardian-bridge.ts +1 -1
  341. package/src/runtime/guardian-vellum-migration.ts +61 -1
  342. package/src/runtime/http-server.ts +8 -4
  343. package/src/runtime/migrations/vbundle-builder.ts +212 -32
  344. package/src/runtime/migrations/vbundle-import-analyzer.ts +74 -8
  345. package/src/runtime/migrations/vbundle-importer.ts +66 -1
  346. package/src/runtime/migrations/vbundle-validator.ts +17 -3
  347. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +4 -4
  348. package/src/runtime/routes/attachment-routes.ts +2 -2
  349. package/src/runtime/routes/btw-routes.ts +93 -0
  350. package/src/runtime/routes/channel-verification-routes.ts +19 -2
  351. package/src/runtime/routes/conversation-management-routes.ts +55 -1
  352. package/src/runtime/routes/conversation-query-routes.ts +1 -1
  353. package/src/runtime/routes/conversation-routes.ts +49 -5
  354. package/src/runtime/routes/conversation-starter-routes.ts +207 -0
  355. package/src/runtime/routes/guardian-bootstrap-routes.ts +13 -9
  356. package/src/runtime/routes/identity-intro-cache.ts +105 -0
  357. package/src/runtime/routes/identity-routes.ts +51 -0
  358. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -1
  359. package/src/runtime/routes/inbound-stages/verification-intercept.ts +1 -1
  360. package/src/runtime/routes/migration-routes.ts +25 -13
  361. package/src/runtime/routes/secret-routes.ts +18 -0
  362. package/src/runtime/routes/settings-routes.ts +9 -9
  363. package/src/runtime/routes/telemetry-routes.ts +53 -0
  364. package/src/runtime/routes/trace-event-routes.ts +62 -0
  365. package/src/runtime/tool-grant-request-helper.ts +1 -1
  366. package/src/runtime/verification-outbound-actions.ts +47 -31
  367. package/src/security/encrypted-store.ts +262 -78
  368. package/src/skills/catalog-install.ts +10 -0
  369. package/src/skills/managed-store.ts +2 -0
  370. package/src/skills/skill-memory.ts +222 -0
  371. package/src/subagent/manager.ts +1 -4
  372. package/src/telemetry/types.ts +10 -1
  373. package/src/telemetry/usage-telemetry-reporter.test.ts +7 -2
  374. package/src/telemetry/usage-telemetry-reporter.ts +53 -4
  375. package/src/tools/AGENTS.md +11 -11
  376. package/src/tools/acp/spawn.ts +1 -1
  377. package/src/tools/apps/executors.ts +8 -8
  378. package/src/tools/apps/registry.ts +1 -1
  379. package/src/tools/assets/materialize.ts +6 -6
  380. package/src/tools/assets/search.ts +10 -10
  381. package/src/tools/browser/__tests__/auth-cache.test.ts +2 -2
  382. package/src/tools/browser/__tests__/auth-detector.test.ts +4 -4
  383. package/src/tools/browser/auth-detector.ts +6 -6
  384. package/src/tools/browser/browser-execution.ts +13 -13
  385. package/src/tools/browser/browser-manager.ts +3 -3
  386. package/src/tools/browser/chrome-cdp.ts +5 -5
  387. package/src/tools/browser/jit-auth.ts +2 -2
  388. package/src/tools/browser/network-recorder.test.ts +2 -2
  389. package/src/tools/browser/network-recorder.ts +3 -3
  390. package/src/tools/browser/runtime-check.ts +3 -3
  391. package/src/tools/claude-code/claude-code.ts +2 -2
  392. package/src/tools/computer-use/definitions.ts +18 -18
  393. package/src/tools/credential-execution/make-authenticated-request.ts +4 -4
  394. package/src/tools/credential-execution/manage-secure-command-tool.ts +3 -3
  395. package/src/tools/credential-execution/run-authenticated-command.ts +4 -4
  396. package/src/tools/credentials/broker-types.ts +5 -5
  397. package/src/tools/credentials/broker.ts +15 -15
  398. package/src/tools/credentials/metadata-store.ts +2 -2
  399. package/src/tools/credentials/resolve.ts +1 -1
  400. package/src/tools/credentials/selection.ts +1 -1
  401. package/src/tools/credentials/tool-policy.ts +1 -1
  402. package/src/tools/credentials/vault.ts +115 -25
  403. package/src/tools/execution-target.ts +2 -2
  404. package/src/tools/executor.ts +7 -7
  405. package/src/tools/filesystem/edit.ts +2 -2
  406. package/src/tools/filesystem/read.ts +15 -4
  407. package/src/tools/filesystem/write.ts +1 -1
  408. package/src/tools/host-filesystem/edit.ts +2 -1
  409. package/src/tools/host-filesystem/read.ts +18 -1
  410. package/src/tools/host-filesystem/write.ts +1 -1
  411. package/src/tools/host-terminal/host-shell.ts +9 -8
  412. package/src/tools/mcp/mcp-tool-factory.ts +7 -6
  413. package/src/tools/memory/definitions.ts +6 -5
  414. package/src/tools/memory/handlers.test.ts +1 -1
  415. package/src/tools/network/__tests__/web-search.test.ts +3 -3
  416. package/src/tools/network/domain-normalize.ts +2 -2
  417. package/src/tools/network/script-proxy/session-manager.ts +10 -10
  418. package/src/tools/network/web-fetch.ts +1 -1
  419. package/src/tools/network/web-search.ts +3 -3
  420. package/src/tools/permission-checker.ts +8 -8
  421. package/src/tools/registry.ts +7 -7
  422. package/src/tools/schedule/list.ts +2 -2
  423. package/src/tools/schema-transforms.ts +31 -21
  424. package/src/tools/secret-detection-handler.ts +1 -1
  425. package/src/tools/sensitive-output-placeholders.ts +1 -1
  426. package/src/tools/shared/filesystem/edit-engine.ts +1 -1
  427. package/src/tools/shared/filesystem/file-ops-service.ts +3 -3
  428. package/src/tools/shared/filesystem/image-read.ts +25 -5
  429. package/src/tools/shared/filesystem/path-policy.ts +2 -2
  430. package/src/tools/shared/shell-output.ts +1 -1
  431. package/src/tools/side-effects.ts +1 -1
  432. package/src/tools/skills/execute.ts +1 -1
  433. package/src/tools/skills/load.ts +3 -3
  434. package/src/tools/skills/sandbox-runner.ts +3 -3
  435. package/src/tools/subagent/read.ts +1 -1
  436. package/src/tools/subagent/spawn.ts +2 -2
  437. package/src/tools/swarm/delegate.ts +3 -3
  438. package/src/tools/system/request-permission.ts +5 -4
  439. package/src/tools/terminal/backends/native.ts +4 -4
  440. package/src/tools/terminal/parser.ts +6 -6
  441. package/src/tools/terminal/sandbox-diagnostics.ts +1 -1
  442. package/src/tools/terminal/shell.ts +16 -16
  443. package/src/tools/tool-approval-handler.ts +21 -12
  444. package/src/tools/tool-manifest.ts +4 -4
  445. package/src/tools/types.ts +3 -3
  446. package/src/tools/ui-surface/definitions.ts +9 -37
  447. package/src/tools/watcher/list.ts +1 -1
  448. package/src/util/logger.ts +7 -2
  449. package/src/util/pricing.ts +4 -0
  450. package/src/util/retry.ts +29 -1
  451. package/src/workspace/migrations/007-web-search-provider-rename.ts +37 -0
  452. package/src/workspace/migrations/registry.ts +2 -0
  453. package/src/__tests__/cli-help-reference-sync.test.ts +0 -26
  454. package/src/__tests__/onboarding-starter-tasks.test.ts +0 -190
  455. package/src/cli/reference.ts +0 -38
  456. package/src/memory/job-handlers/capability-cards.ts +0 -420
  457. package/src/runtime/routes/thread-starter-routes.ts +0 -294
@@ -1,12 +1,12 @@
1
1
  /**
2
- * Encrypted-at-rest key storage fallback for systems without OS keychain.
2
+ * Encrypted-at-rest key storage -- fallback for systems without OS keychain.
3
3
  *
4
- * Uses AES-256-GCM with a key derived from machine-specific entropy:
5
- * - hostname, username, platform, arch, homedir
6
- * - PBKDF2 with 100k iterations + a persisted random salt
4
+ * v2 stores use a cryptographically random 32-byte `store.key` file as the
5
+ * AES-256-GCM key directly (no key derivation). The key file lives alongside
6
+ * `keys.enc` in `~/.vellum/protected/`.
7
7
  *
8
- * Secrets are stored in `~/.vellum/keys.enc` as a JSON blob encrypted
9
- * with the derived key. Each entry has its own IV for authenticated encryption.
8
+ * v1 stores (legacy) derived the AES key from machine-specific entropy via
9
+ * PBKDF2. Existing v1 stores are automatically migrated to v2 on first access.
10
10
  *
11
11
  * Provides the same get/set/delete interface as `keychain.ts`.
12
12
  */
@@ -17,7 +17,13 @@ import {
17
17
  pbkdf2Sync,
18
18
  randomBytes,
19
19
  } from "node:crypto";
20
- import { chmodSync, readFileSync, renameSync, writeFileSync } from "node:fs";
20
+ import {
21
+ chmodSync,
22
+ readFileSync,
23
+ renameSync,
24
+ unlinkSync,
25
+ writeFileSync,
26
+ } from "node:fs";
21
27
  import { hostname, userInfo } from "node:os";
22
28
  import { dirname, join } from "node:path";
23
29
 
@@ -36,18 +42,27 @@ const PBKDF2_ITERATIONS =
36
42
  // In tests, PBKDF2 key derivation dominates runtime (~1-2s per file).
37
43
  // 1 iteration is sufficient for correctness; 100k is for brute-force resistance.
38
44
  process.env.BUN_TEST === "1" ? 1 : 100_000;
39
- const SALT_LENGTH = 32; // bytes
40
45
 
41
- /** On-disk format for the encrypted store. */
42
- interface StoreFile {
43
- /** Version for future format changes. */
46
+ // ---------------------------------------------------------------------------
47
+ // On-disk formats
48
+ // ---------------------------------------------------------------------------
49
+
50
+ /** v1 on-disk format (legacy): PBKDF2-derived key from machine entropy. */
51
+ interface StoreFileV1 {
44
52
  version: 1;
45
53
  /** Hex-encoded salt for PBKDF2 key derivation. */
46
54
  salt: string;
47
- /** Individual encrypted entries keyed by account name. */
48
55
  entries: Record<string, EncryptedEntry>;
49
56
  }
50
57
 
58
+ /** v2 on-disk format: random store.key used directly as AES key. */
59
+ interface StoreFileV2 {
60
+ version: 2;
61
+ entries: Record<string, EncryptedEntry>;
62
+ }
63
+
64
+ type StoreFile = StoreFileV1 | StoreFileV2;
65
+
51
66
  /** A single encrypted value. */
52
67
  interface EncryptedEntry {
53
68
  /** Hex-encoded IV. */
@@ -74,10 +89,74 @@ export function _setStorePath(path: string | null): void {
74
89
  }
75
90
 
76
91
  // ---------------------------------------------------------------------------
77
- // Machine entropy for key derivation
92
+ // Store key file (v2)
78
93
  // ---------------------------------------------------------------------------
79
94
 
80
- export function getMachineEntropy(): string {
95
+ const STORE_KEY_FILENAME = "store.key";
96
+ const STORE_KEY_LENGTH = 32; // bytes
97
+
98
+ let storeKeyPathOverride: string | null = null;
99
+
100
+ /** @internal Test-only: override the store key file path. Pass `null` to reset. */
101
+ export function _setStoreKeyPath(path: string | null): void {
102
+ storeKeyPathOverride = path;
103
+ }
104
+
105
+ function getStoreKeyPath(): string {
106
+ return (
107
+ storeKeyPathOverride ?? join(dirname(getStorePath()), STORE_KEY_FILENAME)
108
+ );
109
+ }
110
+
111
+ /**
112
+ * Read the store.key file. Returns the raw 32-byte key buffer, or null
113
+ * if the file is missing, wrong size, or unreadable.
114
+ */
115
+ function readStoreKey(): Buffer | null {
116
+ const keyPath = getStoreKeyPath();
117
+ if (!pathExists(keyPath)) return null;
118
+ try {
119
+ const buf = readFileSync(keyPath);
120
+ if (buf.length !== STORE_KEY_LENGTH) return null;
121
+ return buf;
122
+ } catch {
123
+ return null;
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Generate a cryptographically random store key and write it atomically
129
+ * to `<dir>/store.key` with 0o600 permissions. Returns the key buffer.
130
+ */
131
+ function generateAndWriteStoreKey(dir: string): Buffer {
132
+ ensureDir(dir);
133
+ const key = randomBytes(STORE_KEY_LENGTH);
134
+ const keyPath = join(dir, STORE_KEY_FILENAME);
135
+ const tmpPath = keyPath + `.tmp.${process.pid}`;
136
+ writeFileSync(tmpPath, key, { mode: 0o600 });
137
+ chmodSync(tmpPath, 0o600);
138
+ renameSync(tmpPath, keyPath);
139
+ return key;
140
+ }
141
+
142
+ /**
143
+ * Read the existing store key, or generate and write a new one if missing.
144
+ */
145
+ function getOrReadStoreKey(dir: string): Buffer {
146
+ const existing = readStoreKey();
147
+ if (existing) return existing;
148
+ return generateAndWriteStoreKey(dir);
149
+ }
150
+
151
+ // ---------------------------------------------------------------------------
152
+ // Machine entropy for key derivation (legacy v1 only)
153
+ // ---------------------------------------------------------------------------
154
+
155
+ /**
156
+ * @deprecated @internal Kept only for v1->v2 migration path.
157
+ * Derives entropy from publicly-knowable machine properties.
158
+ */
159
+ function getMachineEntropy(): string {
81
160
  const parts: string[] = [];
82
161
  try {
83
162
  parts.push(hostname());
@@ -99,11 +178,75 @@ export function getMachineEntropy(): string {
99
178
  return parts.join(":");
100
179
  }
101
180
 
181
+ /**
182
+ * @deprecated @internal Kept only for v1->v2 migration path.
183
+ * Derives an AES key from machine entropy via PBKDF2.
184
+ */
102
185
  function deriveKey(salt: Buffer): Buffer {
103
186
  const entropy = getMachineEntropy();
104
187
  return pbkdf2Sync(entropy, salt, PBKDF2_ITERATIONS, KEY_LENGTH, "sha512");
105
188
  }
106
189
 
190
+ // ---------------------------------------------------------------------------
191
+ // Key resolution
192
+ // ---------------------------------------------------------------------------
193
+
194
+ /**
195
+ * Resolve the AES key for a given store format.
196
+ * - v2: reads store.key file (returns null if missing)
197
+ * - v1: derives key from machine entropy via PBKDF2
198
+ */
199
+ function getKeyForStore(store: StoreFile): Buffer | null {
200
+ if (store.version === 2) {
201
+ return readStoreKey();
202
+ }
203
+ return deriveKey(Buffer.from(store.salt, "hex"));
204
+ }
205
+
206
+ // ---------------------------------------------------------------------------
207
+ // v1 -> v2 migration
208
+ // ---------------------------------------------------------------------------
209
+
210
+ /**
211
+ * Migrate a v1 store to v2 format:
212
+ * 1. Get or generate a random store.key
213
+ * 2. Decrypt each entry with the legacy PBKDF2-derived key
214
+ * 3. Re-encrypt each entry with the random store key
215
+ *
216
+ * Entries that fail to decrypt (corrupt/tampered) are logged and skipped.
217
+ * Returns null if a fatal error occurs (e.g. can't write store.key).
218
+ */
219
+ function migrateV1ToV2(store: StoreFileV1): StoreFileV2 | null {
220
+ const protectedDir = dirname(getStorePath());
221
+
222
+ let storeKey: Buffer;
223
+ try {
224
+ storeKey = getOrReadStoreKey(protectedDir);
225
+ } catch (err) {
226
+ log.error({ err }, "Failed to create store.key during v1->v2 migration");
227
+ return null;
228
+ }
229
+
230
+ // Derive the legacy key for decryption
231
+ const legacyKey = deriveKey(Buffer.from(store.salt, "hex"));
232
+
233
+ const newEntries: Record<string, EncryptedEntry> = Object.create(null);
234
+
235
+ for (const [account, entry] of Object.entries(store.entries)) {
236
+ try {
237
+ const plaintext = decrypt(entry, legacyKey);
238
+ newEntries[account] = encrypt(plaintext, storeKey);
239
+ } catch (err) {
240
+ log.warn(
241
+ { err, account },
242
+ "Skipping corrupt entry during v1->v2 migration",
243
+ );
244
+ }
245
+ }
246
+
247
+ return { version: 2, entries: newEntries };
248
+ }
249
+
107
250
  // ---------------------------------------------------------------------------
108
251
  // Store I/O
109
252
  // ---------------------------------------------------------------------------
@@ -124,25 +267,34 @@ function readStore(): StoreFile | null {
124
267
 
125
268
  try {
126
269
  const parsed = JSON.parse(raw);
127
- if (
128
- parsed.version !== 1 ||
129
- typeof parsed.salt !== "string" ||
130
- typeof parsed.entries !== "object"
131
- ) {
270
+
271
+ if (typeof parsed.entries !== "object") {
132
272
  throw new Error("Encrypted store has invalid format");
133
273
  }
134
- // Use null-prototype object for entries to prevent prototype pollution
135
- const safeEntries: Record<string, EncryptedEntry> = Object.create(null);
136
- Object.assign(safeEntries, parsed.entries);
137
- parsed.entries = safeEntries;
138
- return parsed as StoreFile;
274
+
275
+ // Accept v2 (no salt required) or v1 (salt required)
276
+ if (parsed.version === 2) {
277
+ const safeEntries: Record<string, EncryptedEntry> = Object.create(null);
278
+ Object.assign(safeEntries, parsed.entries);
279
+ parsed.entries = safeEntries;
280
+ return parsed as StoreFileV2;
281
+ }
282
+
283
+ if (parsed.version === 1 && typeof parsed.salt === "string") {
284
+ const safeEntries: Record<string, EncryptedEntry> = Object.create(null);
285
+ Object.assign(safeEntries, parsed.entries);
286
+ parsed.entries = safeEntries;
287
+ return parsed as StoreFileV1;
288
+ }
289
+
290
+ throw new Error("Encrypted store has invalid format");
139
291
  } catch (err) {
140
- // Corrupted or invalid store file back it up and start fresh so the
292
+ // Corrupted or invalid store file -- back it up and start fresh so the
141
293
  // daemon doesn't crash on every credential access.
142
294
  const backupPath = `${path}.corrupt.${Date.now()}`;
143
295
  log.error(
144
296
  { err, backupPath },
145
- "Encrypted store is corrupt backing up and resetting",
297
+ "Encrypted store is corrupt -- backing up and resetting",
146
298
  );
147
299
  try {
148
300
  renameSync(path, backupPath);
@@ -154,48 +306,37 @@ function readStore(): StoreFile | null {
154
306
  }
155
307
 
156
308
  /**
157
- * Well-known filename for the persisted machine entropy.
158
- * Written alongside `keys.enc` so the CES sidecar (which mounts the
159
- * assistant data volume read-only) can derive the same AES key.
309
+ * Well-known filename for the persisted machine entropy (legacy).
310
+ * Written alongside `keys.enc` so the CES sidecar could derive the same AES key.
311
+ * Superseded by `store.key` in v2 format.
160
312
  */
161
313
  const ENTROPY_FILENAME = "entropy.key";
162
314
 
163
315
  /**
164
- * Persist the current machine entropy next to the key store so the managed
165
- * CES sidecar can read it and derive the same decryption key.
166
- *
167
- * Only writes in containerized (managed) mode — in local mode, CES runs on
168
- * the same machine and derives the same entropy natively, so the file is
169
- * unnecessary and would weaken offline security by persisting entropy to disk.
316
+ * Ensure the `store.key` file exists and is accessible on the shared mount
317
+ * (containerized/managed mode). Best-effort delete the old `entropy.key` file
318
+ * since v2 stores no longer need it.
170
319
  */
171
- function persistEntropy(protectedDir: string): void {
320
+ function persistStoreKey(protectedDir: string): void {
172
321
  if (!getIsContainerized()) return;
173
322
  try {
174
- const entropyPath = join(protectedDir, ENTROPY_FILENAME);
175
- const tmpPath = entropyPath + `.tmp.${process.pid}`;
176
- writeFileSync(tmpPath, getMachineEntropy(), { mode: 0o600 });
177
- chmodSync(tmpPath, 0o600);
178
- renameSync(tmpPath, entropyPath);
323
+ const storeKeyPath = join(protectedDir, STORE_KEY_FILENAME);
324
+ if (!pathExists(storeKeyPath)) {
325
+ // store.key should already exist from normal creation, but ensure it
326
+ getOrReadStoreKey(protectedDir);
327
+ }
179
328
  } catch {
180
- // Best-effort — managed mode will log a clear error if it's missing.
329
+ // Best-effort
181
330
  }
182
- }
183
331
 
184
- /**
185
- * Backfill `entropy.key` for existing encrypted stores that predate the
186
- * entropy persistence feature. If the store file exists but `entropy.key`
187
- * does not, write it now so the managed CES sidecar can derive the key.
188
- */
189
- function backfillEntropyIfMissing(): void {
190
- if (!getIsContainerized()) return;
332
+ // Best-effort cleanup of legacy entropy.key
191
333
  try {
192
- const protectedDir = dirname(getStorePath());
193
334
  const entropyPath = join(protectedDir, ENTROPY_FILENAME);
194
- if (!pathExists(entropyPath)) {
195
- persistEntropy(protectedDir);
335
+ if (pathExists(entropyPath)) {
336
+ unlinkSync(entropyPath);
196
337
  }
197
338
  } catch {
198
- // Best-effort don't break reads if backfill fails.
339
+ // Best-effort -- don't fail if cleanup of old file doesn't work.
199
340
  }
200
341
  }
201
342
 
@@ -211,23 +352,36 @@ function writeStore(store: StoreFile): void {
211
352
  chmodSync(tmpPath, 0o600);
212
353
  renameSync(tmpPath, path);
213
354
 
214
- // Keep entropy.key in sync so the managed CES sidecar can decrypt.
215
- persistEntropy(protectedDir);
355
+ // Keep store.key in sync so the managed CES sidecar can decrypt.
356
+ persistStoreKey(protectedDir);
216
357
  }
217
358
 
218
- function getOrCreateStore(): StoreFile {
359
+ function getOrCreateStore(): StoreFileV2 {
219
360
  const existing = readStore();
220
- if (existing) return existing;
221
361
 
222
- const salt = randomBytes(SALT_LENGTH);
223
- const entries: Record<string, EncryptedEntry> = Object.create(null);
224
- const store: StoreFile = {
225
- version: 1,
226
- salt: salt.toString("hex"),
227
- entries,
228
- };
229
- writeStore(store);
230
- return store;
362
+ if (!existing) {
363
+ // Fresh store: generate store.key and create v2 format
364
+ const protectedDir = dirname(getStorePath());
365
+ getOrReadStoreKey(protectedDir);
366
+ const entries: Record<string, EncryptedEntry> = Object.create(null);
367
+ const store: StoreFileV2 = { version: 2, entries };
368
+ writeStore(store);
369
+ return store;
370
+ }
371
+
372
+ if (existing.version === 1) {
373
+ // Migrate v1 -> v2
374
+ const migrated = migrateV1ToV2(existing);
375
+ if (migrated) {
376
+ writeStore(migrated);
377
+ return migrated;
378
+ }
379
+ // Migration failed fatally -- fall through and use v1 as-is won't work
380
+ // because we can't get the key. Throw so callers handle the error.
381
+ throw new Error("Failed to migrate encrypted store from v1 to v2");
382
+ }
383
+
384
+ return existing;
231
385
  }
232
386
 
233
387
  // ---------------------------------------------------------------------------
@@ -264,7 +418,7 @@ function decrypt(entry: EncryptedEntry, key: Buffer): string {
264
418
  }
265
419
 
266
420
  // ---------------------------------------------------------------------------
267
- // Public API matches keychain.ts interface
421
+ // Public API -- matches keychain.ts interface
268
422
  // ---------------------------------------------------------------------------
269
423
 
270
424
  /**
@@ -276,15 +430,30 @@ export function getKey(account: string): string | undefined {
276
430
  const store = readStore();
277
431
  if (!store) return undefined;
278
432
 
279
- // Backfill entropy.key for existing stores that were created before
280
- // entropy persistence was added. Only needed in managed mode.
281
- backfillEntropyIfMissing();
433
+ // If v1, trigger migration
434
+ if (store.version === 1) {
435
+ const migrated = migrateV1ToV2(store);
436
+ if (migrated) {
437
+ writeStore(migrated);
438
+ const entry = migrated.entries[account];
439
+ if (!entry) return undefined;
440
+ const key = getKeyForStore(migrated);
441
+ if (!key) return undefined;
442
+ return decrypt(entry, key);
443
+ }
444
+ // Migration failed -- try reading with legacy key
445
+ const entry = store.entries[account];
446
+ if (!entry) return undefined;
447
+ const key = getKeyForStore(store);
448
+ if (!key) return undefined;
449
+ return decrypt(entry, key);
450
+ }
282
451
 
283
452
  const entry = store.entries[account];
284
453
  if (!entry) return undefined;
285
454
 
286
- const salt = Buffer.from(store.salt, "hex");
287
- const key = deriveKey(salt);
455
+ const key = getKeyForStore(store);
456
+ if (!key) return undefined;
288
457
  return decrypt(entry, key);
289
458
  } catch (err) {
290
459
  log.debug({ err, account }, "Failed to read from encrypted store");
@@ -299,8 +468,8 @@ export function getKey(account: string): string | undefined {
299
468
  export function setKey(account: string, value: string): boolean {
300
469
  try {
301
470
  const store = getOrCreateStore();
302
- const salt = Buffer.from(store.salt, "hex");
303
- const key = deriveKey(salt);
471
+ const key = getKeyForStore(store);
472
+ if (!key) return false;
304
473
  store.entries[account] = encrypt(value, key);
305
474
  writeStore(store);
306
475
  return true;
@@ -310,7 +479,7 @@ export function setKey(account: string, value: string): boolean {
310
479
  }
311
480
  }
312
481
 
313
- /** Result of a delete operation distinguishes success, not-found, and error. */
482
+ /** Result of a delete operation -- distinguishes success, not-found, and error. */
314
483
  export type DeleteKeyResult = "deleted" | "not-found" | "error";
315
484
 
316
485
  /**
@@ -320,8 +489,23 @@ export type DeleteKeyResult = "deleted" | "not-found" | "error";
320
489
  */
321
490
  export function deleteKey(account: string): DeleteKeyResult {
322
491
  try {
323
- const store = readStore();
324
- if (!store || !Object.prototype.hasOwnProperty.call(store.entries, account))
492
+ const existing = readStore();
493
+ if (!existing) return "not-found";
494
+
495
+ // Ensure v1→v2 migration happens when a store exists
496
+ let store: StoreFileV2;
497
+ if (existing.version === 1) {
498
+ const migrated = migrateV1ToV2(existing);
499
+ if (!migrated) {
500
+ throw new Error("Failed to migrate encrypted store from v1 to v2");
501
+ }
502
+ writeStore(migrated);
503
+ store = migrated;
504
+ } else {
505
+ store = existing;
506
+ }
507
+
508
+ if (!Object.prototype.hasOwnProperty.call(store.entries, account))
325
509
  return "not-found";
326
510
 
327
511
  delete store.entries[account];
@@ -19,6 +19,7 @@ import {
19
19
  getWorkspaceSkillsDir,
20
20
  readPlatformToken,
21
21
  } from "../util/platform.js";
22
+ import { deleteSkillCapabilityMemory } from "./skill-memory.js";
22
23
 
23
24
  const log = getLogger("catalog-install");
24
25
 
@@ -31,6 +32,14 @@ export interface CatalogSkill {
31
32
  emoji?: string;
32
33
  includes?: string[];
33
34
  version?: string;
35
+ metadata?: {
36
+ vellum?: {
37
+ "display-name"?: string;
38
+ "activation-hints"?: string[];
39
+ "avoid-when"?: string[];
40
+ "feature-flag"?: string;
41
+ };
42
+ };
34
43
  }
35
44
 
36
45
  export interface CatalogManifest {
@@ -280,6 +289,7 @@ export function uninstallSkillLocally(skillId: string): void {
280
289
 
281
290
  rmSync(skillDir, { recursive: true, force: true });
282
291
  removeSkillsIndexEntry(skillId);
292
+ deleteSkillCapabilityMemory(skillId);
283
293
  }
284
294
 
285
295
  export async function installSkillLocally(
@@ -13,6 +13,7 @@ import { stringify as stringifyYaml } from "yaml";
13
13
 
14
14
  import { getLogger } from "../util/logger.js";
15
15
  import { getWorkspaceSkillsDir } from "../util/platform.js";
16
+ import { deleteSkillCapabilityMemory } from "./skill-memory.js";
16
17
 
17
18
  const log = getLogger("managed-store");
18
19
 
@@ -307,6 +308,7 @@ export function deleteManagedSkill(
307
308
  }
308
309
 
309
310
  rmSync(skillDir, { recursive: true });
311
+ deleteSkillCapabilityMemory(id);
310
312
  log.info({ id, path: skillDir }, "Deleted managed skill");
311
313
 
312
314
  let indexUpdated = false;