@vellumai/assistant 0.3.5 → 0.3.7

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 (487) hide show
  1. package/README.md +51 -0
  2. package/eslint.config.mjs +31 -0
  3. package/package.json +1 -1
  4. package/scripts/ipc/check-swift-decoder-drift.ts +4 -1
  5. package/scripts/ipc/generate-swift.ts +18 -2
  6. package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +338 -1
  7. package/src/__tests__/approval-conversation-turn.test.ts +214 -0
  8. package/src/__tests__/browser-manager.test.ts +1 -0
  9. package/src/__tests__/call-conversation-messages.test.ts +130 -0
  10. package/src/__tests__/call-orchestrator.test.ts +752 -271
  11. package/src/__tests__/call-pointer-messages.test.ts +148 -0
  12. package/src/__tests__/call-recovery.test.ts +3 -0
  13. package/src/__tests__/call-routes-http.test.ts +5 -0
  14. package/src/__tests__/call-store.test.ts +3 -0
  15. package/src/__tests__/channel-approval-routes.test.ts +1260 -85
  16. package/src/__tests__/channel-approval.test.ts +37 -0
  17. package/src/__tests__/channel-approvals.test.ts +4 -65
  18. package/src/__tests__/channel-guardian.test.ts +556 -0
  19. package/src/__tests__/channel-readiness-service.test.ts +74 -7
  20. package/src/__tests__/checker.test.ts +14 -7
  21. package/src/__tests__/clarification-resolver.test.ts +44 -24
  22. package/src/__tests__/commit-message-enrichment-service.test.ts +9 -4
  23. package/src/__tests__/computer-use-session-working-dir.test.ts +8 -0
  24. package/src/__tests__/config-schema.test.ts +12 -7
  25. package/src/__tests__/context-window-manager.test.ts +30 -2
  26. package/src/__tests__/contradiction-checker.test.ts +20 -5
  27. package/src/__tests__/credential-security-invariants.test.ts +6 -2
  28. package/src/__tests__/db-migration-rollback.test.ts +752 -0
  29. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -0
  30. package/src/__tests__/fuzzy-match-property.test.ts +5 -5
  31. package/src/__tests__/guardian-action-store.test.ts +123 -0
  32. package/src/__tests__/guardian-action-sweep.test.ts +277 -0
  33. package/src/__tests__/guardian-dispatch.test.ts +389 -0
  34. package/src/__tests__/guardian-question-copy.test.ts +47 -0
  35. package/src/__tests__/handlers-telegram-config.test.ts +4 -2
  36. package/src/__tests__/handlers-twilio-config.test.ts +126 -0
  37. package/src/__tests__/intent-routing.test.ts +2 -0
  38. package/src/__tests__/ipc-snapshot.test.ts +228 -1
  39. package/src/__tests__/memory-upsert-concurrency.test.ts +828 -0
  40. package/src/__tests__/model-intents.test.ts +96 -0
  41. package/src/__tests__/no-direct-anthropic-sdk-imports.test.ts +42 -0
  42. package/src/__tests__/oauth2-gateway-transport.test.ts +130 -0
  43. package/src/__tests__/onboarding-starter-tasks.test.ts +2 -0
  44. package/src/__tests__/provider-commit-message-generator.test.ts +89 -13
  45. package/src/__tests__/provider-error-scenarios.test.ts +621 -0
  46. package/src/__tests__/provider-fail-open-selection.test.ts +119 -0
  47. package/src/__tests__/qdrant-manager.test.ts +27 -20
  48. package/src/__tests__/relay-server.test.ts +779 -40
  49. package/src/__tests__/run-orchestrator-assistant-events.test.ts +2 -0
  50. package/src/__tests__/run-orchestrator.test.ts +20 -4
  51. package/src/__tests__/runtime-runs-http.test.ts +17 -1
  52. package/src/__tests__/runtime-runs.test.ts +16 -0
  53. package/src/__tests__/schedule-store.test.ts +18 -4
  54. package/src/__tests__/scheduler-recurrence.test.ts +13 -4
  55. package/src/__tests__/session-abort-tool-results.test.ts +6 -0
  56. package/src/__tests__/session-agent-loop.test.ts +857 -0
  57. package/src/__tests__/session-conflict-gate.test.ts +6 -0
  58. package/src/__tests__/session-pre-run-repair.test.ts +6 -0
  59. package/src/__tests__/session-profile-injection.test.ts +6 -0
  60. package/src/__tests__/session-provider-retry-repair.test.ts +6 -0
  61. package/src/__tests__/session-queue.test.ts +6 -0
  62. package/src/__tests__/session-runtime-assembly.test.ts +237 -13
  63. package/src/__tests__/session-slash-known.test.ts +6 -0
  64. package/src/__tests__/session-slash-queue.test.ts +6 -0
  65. package/src/__tests__/session-slash-unknown.test.ts +6 -0
  66. package/src/__tests__/session-surfaces-task-progress.test.ts +2 -0
  67. package/src/__tests__/session-tool-setup-app-refresh.test.ts +1 -0
  68. package/src/__tests__/session-tool-setup-memory-scope.test.ts +1 -0
  69. package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +1 -0
  70. package/src/__tests__/session-workspace-injection.test.ts +6 -0
  71. package/src/__tests__/session-workspace-tool-tracking.test.ts +6 -0
  72. package/src/__tests__/skills.test.ts +2 -0
  73. package/src/__tests__/sms-messaging-provider.test.ts +2 -1
  74. package/src/__tests__/starter-task-flow.test.ts +2 -0
  75. package/src/__tests__/swarm-dag-pathological.test.ts +535 -0
  76. package/src/__tests__/system-prompt.test.ts +2 -0
  77. package/src/__tests__/task-management-tools.test.ts +2 -2
  78. package/src/__tests__/task-runner.test.ts +14 -4
  79. package/src/__tests__/terminal-tools.test.ts +25 -19
  80. package/src/__tests__/tool-execution-abort-cleanup.test.ts +545 -0
  81. package/src/__tests__/tool-executor-shell-integration.test.ts +11 -11
  82. package/src/__tests__/tool-executor.test.ts +23 -24
  83. package/src/__tests__/trust-store.test.ts +3 -3
  84. package/src/__tests__/twilio-rest.test.ts +29 -0
  85. package/src/__tests__/twilio-routes-elevenlabs.test.ts +3 -0
  86. package/src/__tests__/twilio-routes-twiml.test.ts +11 -0
  87. package/src/__tests__/twilio-routes.test.ts +141 -21
  88. package/src/__tests__/user-reference.test.ts +2 -0
  89. package/src/__tests__/voice-quality.test.ts +222 -0
  90. package/src/__tests__/web-search.test.ts +45 -29
  91. package/src/agent/loop.ts +1 -1
  92. package/src/agent-heartbeat/agent-heartbeat-service.ts +2 -10
  93. package/src/amazon/client.ts +1418 -0
  94. package/src/amazon/request-extractor.ts +135 -0
  95. package/src/amazon/session.ts +109 -0
  96. package/src/autonomy/autonomy-store.ts +5 -5
  97. package/src/browser-extension-relay/client.ts +124 -0
  98. package/src/browser-extension-relay/protocol.ts +63 -0
  99. package/src/browser-extension-relay/server.ts +177 -0
  100. package/src/bundler/app-bundler.ts +3 -3
  101. package/src/bundler/bundle-signer.ts +1 -1
  102. package/src/bundler/signature-verifier.ts +1 -1
  103. package/src/calls/call-conversation-messages.ts +33 -0
  104. package/src/calls/call-domain.ts +106 -5
  105. package/src/calls/call-orchestrator.ts +252 -54
  106. package/src/calls/call-pointer-messages.ts +53 -0
  107. package/src/calls/call-recovery.ts +3 -8
  108. package/src/calls/call-store.ts +69 -87
  109. package/src/calls/elevenlabs-config.ts +3 -2
  110. package/src/calls/guardian-action-sweep.ts +105 -0
  111. package/src/calls/guardian-dispatch.ts +203 -0
  112. package/src/calls/guardian-question-copy.ts +133 -0
  113. package/src/calls/relay-server.ts +466 -8
  114. package/src/calls/speaker-identification.ts +1 -1
  115. package/src/calls/twilio-config.ts +7 -5
  116. package/src/calls/twilio-provider.ts +6 -4
  117. package/src/calls/twilio-rest.ts +40 -15
  118. package/src/calls/twilio-routes.ts +60 -45
  119. package/src/calls/types.ts +3 -1
  120. package/src/channels/types.ts +25 -0
  121. package/src/cli/amazon.ts +815 -0
  122. package/src/cli/config-commands.ts +2 -2
  123. package/src/cli/core-commands.ts +4 -3
  124. package/src/cli/influencer.ts +244 -0
  125. package/src/cli/map.ts +89 -6
  126. package/src/cli.ts +1 -1
  127. package/src/config/agent-schema.ts +171 -0
  128. package/src/config/bundled-skills/amazon/SKILL.md +127 -0
  129. package/src/config/bundled-skills/amazon/icon.svg +13 -0
  130. package/src/config/bundled-skills/api-mapping/SKILL.md +78 -0
  131. package/src/config/bundled-skills/browser/SKILL.md +1 -0
  132. package/src/config/bundled-skills/browser/TOOLS.json +17 -0
  133. package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +25 -0
  134. package/src/config/bundled-skills/doordash/SKILL.md +51 -51
  135. package/src/config/bundled-skills/email-setup/SKILL.md +14 -5
  136. package/src/config/bundled-skills/google-oauth-setup/SKILL.md +183 -0
  137. package/src/config/bundled-skills/influencer/SKILL.md +144 -0
  138. package/src/config/bundled-skills/macos-automation/icon.svg +12 -0
  139. package/src/config/bundled-skills/media-processing/SKILL.md +72 -95
  140. package/src/config/bundled-skills/media-processing/TOOLS.json +57 -147
  141. package/src/config/bundled-skills/media-processing/__tests__/concurrency-pool.test.ts +77 -0
  142. package/src/config/bundled-skills/media-processing/__tests__/cost-tracker.test.ts +69 -0
  143. package/src/config/bundled-skills/media-processing/__tests__/preprocess.test.ts +303 -0
  144. package/src/config/bundled-skills/media-processing/services/concurrency-pool.ts +55 -0
  145. package/src/config/bundled-skills/media-processing/services/cost-tracker.ts +86 -0
  146. package/src/config/bundled-skills/media-processing/services/gemini-map.ts +339 -0
  147. package/src/config/bundled-skills/media-processing/services/preprocess.ts +551 -0
  148. package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +7 -9
  149. package/src/config/bundled-skills/media-processing/services/reduce.ts +197 -0
  150. package/src/config/bundled-skills/media-processing/tools/analyze-keyframes.ts +88 -253
  151. package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +22 -153
  152. package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -2
  153. package/src/config/bundled-skills/media-processing/tools/media-diagnostics.ts +28 -51
  154. package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +35 -270
  155. package/src/config/bundled-skills/messaging/SKILL.md +12 -2
  156. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +4 -7
  157. package/src/config/bundled-skills/messaging/tools/messaging-reply.ts +2 -1
  158. package/src/config/bundled-skills/phone-calls/SKILL.md +86 -21
  159. package/src/config/bundled-skills/twitter/icon.svg +14 -0
  160. package/src/config/bundled-tool-registry.ts +310 -0
  161. package/src/config/calls-schema.ts +181 -0
  162. package/src/config/core-schema.ts +309 -0
  163. package/src/config/defaults.ts +27 -3
  164. package/src/config/env-registry.ts +169 -0
  165. package/src/config/env.ts +175 -0
  166. package/src/config/loader.ts +6 -6
  167. package/src/config/memory-schema.ts +528 -0
  168. package/src/config/sandbox-schema.ts +55 -0
  169. package/src/config/schema.ts +157 -1138
  170. package/src/config/skill-state.ts +1 -1
  171. package/src/config/skills-schema.ts +32 -0
  172. package/src/config/skills.ts +35 -24
  173. package/src/config/system-prompt.ts +107 -56
  174. package/src/config/templates/SOUL.md +1 -1
  175. package/src/config/types.ts +1 -0
  176. package/src/config/user-reference.ts +4 -9
  177. package/src/config/vellum-skills/catalog.json +0 -7
  178. package/src/config/vellum-skills/chatgpt-import/tools/chatgpt-import.ts +5 -1
  179. package/src/config/vellum-skills/slack-oauth-setup/SKILL.md +1 -0
  180. package/src/config/vellum-skills/sms-setup/SKILL.md +112 -14
  181. package/src/context/window-manager.ts +27 -7
  182. package/src/daemon/approval-generators.ts +186 -0
  183. package/src/daemon/approved-devices-store.ts +140 -0
  184. package/src/daemon/assistant-attachments.ts +1 -1
  185. package/src/daemon/classifier.ts +35 -32
  186. package/src/daemon/config-watcher.ts +1 -1
  187. package/src/daemon/daemon-control.ts +254 -0
  188. package/src/daemon/handlers/apps.ts +2 -3
  189. package/src/daemon/handlers/config-channels.ts +158 -0
  190. package/src/daemon/handlers/config-inbox.ts +540 -0
  191. package/src/daemon/handlers/config-ingress.ts +231 -0
  192. package/src/daemon/handlers/config-integrations.ts +258 -0
  193. package/src/daemon/handlers/config-model.ts +143 -0
  194. package/src/daemon/handlers/config-parental.ts +163 -0
  195. package/src/daemon/handlers/config-scheduling.ts +172 -0
  196. package/src/daemon/handlers/config-slack.ts +92 -0
  197. package/src/daemon/handlers/config-telegram.ts +301 -0
  198. package/src/daemon/handlers/config-tools.ts +177 -0
  199. package/src/daemon/handlers/config-trust.ts +104 -0
  200. package/src/daemon/handlers/config-twilio.ts +1080 -0
  201. package/src/daemon/handlers/config.ts +53 -2463
  202. package/src/daemon/handlers/diagnostics.ts +1 -1
  203. package/src/daemon/handlers/dictation.ts +4 -6
  204. package/src/daemon/handlers/documents.ts +18 -32
  205. package/src/daemon/handlers/index.ts +9 -0
  206. package/src/daemon/handlers/misc.ts +3 -5
  207. package/src/daemon/handlers/pairing.ts +98 -0
  208. package/src/daemon/handlers/sessions.ts +74 -5
  209. package/src/daemon/handlers/shared.ts +3 -1
  210. package/src/daemon/handlers/skills.ts +1 -1
  211. package/src/daemon/handlers/twitter-auth.ts +2 -0
  212. package/src/daemon/handlers/work-items.ts +2 -2
  213. package/src/daemon/handlers/workspace-files.ts +4 -3
  214. package/src/daemon/install-cli-launchers.ts +113 -0
  215. package/src/daemon/ipc-contract/apps.ts +356 -0
  216. package/src/daemon/ipc-contract/browser.ts +74 -0
  217. package/src/daemon/ipc-contract/computer-use.ts +151 -0
  218. package/src/daemon/ipc-contract/diagnostics.ts +56 -0
  219. package/src/daemon/ipc-contract/documents.ts +74 -0
  220. package/src/daemon/ipc-contract/inbox.ts +209 -0
  221. package/src/daemon/ipc-contract/integrations.ts +284 -0
  222. package/src/daemon/ipc-contract/memory.ts +48 -0
  223. package/src/daemon/ipc-contract/messages.ts +211 -0
  224. package/src/daemon/ipc-contract/pairing.ts +45 -0
  225. package/src/daemon/ipc-contract/parental-control.ts +95 -0
  226. package/src/daemon/ipc-contract/schedules.ts +97 -0
  227. package/src/daemon/ipc-contract/sessions.ts +321 -0
  228. package/src/daemon/ipc-contract/shared.ts +42 -0
  229. package/src/daemon/ipc-contract/skills.ts +120 -0
  230. package/src/daemon/ipc-contract/subagents.ts +58 -0
  231. package/src/daemon/ipc-contract/surfaces.ts +250 -0
  232. package/src/daemon/ipc-contract/trust.ts +60 -0
  233. package/src/daemon/ipc-contract/work-items.ts +225 -0
  234. package/src/daemon/ipc-contract/workspace.ts +113 -0
  235. package/src/daemon/ipc-contract-inventory.json +62 -0
  236. package/src/daemon/ipc-contract-inventory.ts +55 -29
  237. package/src/daemon/ipc-contract.ts +227 -2527
  238. package/src/daemon/ipc-protocol.ts +1 -1
  239. package/src/daemon/ipc-validate.ts +7 -0
  240. package/src/daemon/lifecycle.ts +97 -379
  241. package/src/daemon/pairing-store.ts +177 -0
  242. package/src/daemon/providers-setup.ts +43 -0
  243. package/src/daemon/ride-shotgun-handler.ts +67 -2
  244. package/src/daemon/server.ts +60 -44
  245. package/src/daemon/session-agent-loop-handlers.ts +421 -0
  246. package/src/daemon/session-agent-loop.ts +113 -275
  247. package/src/daemon/session-dynamic-profile.ts +1 -1
  248. package/src/daemon/session-history.ts +1 -1
  249. package/src/daemon/session-media-retry.ts +1 -1
  250. package/src/daemon/session-messaging.ts +37 -2
  251. package/src/daemon/session-notifiers.ts +5 -25
  252. package/src/daemon/session-process.ts +99 -59
  253. package/src/daemon/session-queue-manager.ts +98 -4
  254. package/src/daemon/session-runtime-assembly.ts +149 -15
  255. package/src/daemon/session-surfaces.ts +26 -4
  256. package/src/daemon/session-tool-setup.ts +28 -30
  257. package/src/daemon/session-workspace.ts +1 -1
  258. package/src/daemon/session.ts +24 -1
  259. package/src/daemon/shutdown-handlers.ts +122 -0
  260. package/src/daemon/trace-emitter.ts +1 -1
  261. package/src/daemon/watch-handler.ts +36 -33
  262. package/src/doordash/cart-queries.ts +787 -0
  263. package/src/doordash/client.ts +144 -127
  264. package/src/doordash/order-queries.ts +85 -0
  265. package/src/doordash/queries.ts +10 -1308
  266. package/src/doordash/search-queries.ts +203 -0
  267. package/src/doordash/session.ts +3 -2
  268. package/src/doordash/store-queries.ts +246 -0
  269. package/src/doordash/types.ts +367 -0
  270. package/src/email/providers/agentmail.ts +2 -1
  271. package/src/email/providers/index.ts +3 -2
  272. package/src/email/service.ts +3 -2
  273. package/src/errors.ts +43 -0
  274. package/src/home-base/prebuilt/seed.ts +1 -1
  275. package/src/hooks/cli.ts +6 -5
  276. package/src/hooks/config.ts +6 -8
  277. package/src/hooks/discovery.ts +6 -5
  278. package/src/hooks/manager.ts +4 -3
  279. package/src/hooks/runner.ts +2 -2
  280. package/src/hooks/templates.ts +5 -5
  281. package/src/inbound/public-ingress-urls.ts +3 -1
  282. package/src/index.ts +4 -2
  283. package/src/influencer/client.ts +1104 -0
  284. package/src/instrument.ts +4 -3
  285. package/src/logfire.ts +4 -3
  286. package/src/memory/admin.ts +25 -35
  287. package/src/memory/attachments-store.ts +4 -7
  288. package/src/memory/channel-delivery-store.ts +30 -1
  289. package/src/memory/channel-guardian-store.ts +200 -1
  290. package/src/memory/clarification-resolver.ts +37 -33
  291. package/src/memory/conflict-store.ts +67 -61
  292. package/src/memory/contradiction-checker.ts +141 -117
  293. package/src/memory/conversation-store.ts +335 -51
  294. package/src/memory/db-connection.ts +27 -4
  295. package/src/memory/db-init.ts +121 -4
  296. package/src/memory/db.ts +14 -1
  297. package/src/memory/embedding-backend.ts +27 -5
  298. package/src/memory/embedding-ollama.ts +2 -1
  299. package/src/memory/entity-extractor.ts +38 -35
  300. package/src/memory/guardian-action-store.ts +430 -0
  301. package/src/memory/inbox-escalation-projection.ts +59 -0
  302. package/src/memory/inbox-thread-store.ts +218 -0
  303. package/src/memory/ingress-invite-store.ts +338 -0
  304. package/src/memory/ingress-member-store.ts +350 -0
  305. package/src/memory/items-extractor.ts +91 -97
  306. package/src/memory/job-handlers/index-maintenance.ts +3 -3
  307. package/src/memory/job-handlers/media-processing.ts +11 -42
  308. package/src/memory/job-handlers/summarization.ts +32 -26
  309. package/src/memory/job-utils.ts +3 -10
  310. package/src/memory/jobs-store.ts +6 -9
  311. package/src/memory/jobs-worker.ts +51 -36
  312. package/src/memory/migrations/001-job-deferrals.ts +45 -0
  313. package/src/memory/migrations/002-tool-invocations-fk.ts +43 -0
  314. package/src/memory/migrations/003-memory-fts-backfill.ts +24 -0
  315. package/src/memory/migrations/004-entity-relation-dedup.ts +87 -0
  316. package/src/memory/migrations/005-fingerprint-scope-unique.ts +80 -0
  317. package/src/memory/migrations/006-scope-salted-fingerprints.ts +62 -0
  318. package/src/memory/migrations/007-assistant-id-to-self.ts +254 -0
  319. package/src/memory/migrations/008-remove-assistant-id-columns.ts +208 -0
  320. package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +83 -0
  321. package/src/memory/migrations/010-ext-conv-bindings-channel-chat-unique.ts +56 -0
  322. package/src/memory/migrations/011-call-sessions-provider-sid-dedup.ts +63 -0
  323. package/src/memory/migrations/012-call-sessions-add-initiated-from.ts +19 -0
  324. package/src/memory/migrations/013-guardian-action-tables.ts +68 -0
  325. package/src/memory/migrations/014-backfill-inbox-thread-state.ts +76 -0
  326. package/src/memory/migrations/015-drop-active-search-index.ts +27 -0
  327. package/src/memory/migrations/016-memory-segments-indexes.ts +11 -0
  328. package/src/memory/migrations/017-memory-items-indexes.ts +12 -0
  329. package/src/memory/migrations/018-remaining-table-indexes.ts +13 -0
  330. package/src/memory/migrations/index.ts +24 -0
  331. package/src/memory/migrations/registry.ts +79 -0
  332. package/src/memory/migrations/validate-migration-state.ts +69 -0
  333. package/src/memory/qdrant-manager.ts +49 -8
  334. package/src/memory/query-builder.ts +1 -1
  335. package/src/memory/raw-query.ts +119 -0
  336. package/src/memory/recall-cache.ts +4 -1
  337. package/src/memory/retriever.ts +163 -47
  338. package/src/memory/schema-migration.ts +25 -984
  339. package/src/memory/schema.ts +130 -7
  340. package/src/memory/search/entity.ts +10 -19
  341. package/src/memory/search/lexical.ts +81 -52
  342. package/src/memory/search/ranking.ts +21 -22
  343. package/src/memory/search/semantic.ts +157 -19
  344. package/src/memory/shared-app-links-store.ts +4 -5
  345. package/src/memory/validation.ts +19 -0
  346. package/src/messaging/draft-store.ts +5 -6
  347. package/src/messaging/providers/sms/adapter.ts +3 -6
  348. package/src/messaging/providers/telegram-bot/adapter.ts +2 -5
  349. package/src/messaging/providers/whatsapp/adapter.ts +136 -0
  350. package/src/messaging/providers/whatsapp/client.ts +67 -0
  351. package/src/messaging/style-analyzer.ts +5 -4
  352. package/src/messaging/thread-summarizer.ts +61 -69
  353. package/src/messaging/triage-engine.ts +62 -71
  354. package/src/migrations/config-merge.ts +53 -0
  355. package/src/migrations/data-layout.ts +68 -0
  356. package/src/migrations/data-merge.ts +33 -0
  357. package/src/migrations/hooks-merge.ts +90 -0
  358. package/src/migrations/index.ts +6 -0
  359. package/src/migrations/log.ts +23 -0
  360. package/src/migrations/skills-merge.ts +33 -0
  361. package/src/migrations/workspace-layout.ts +79 -0
  362. package/src/permissions/checker.ts +126 -11
  363. package/src/permissions/prompter.ts +14 -0
  364. package/src/permissions/shell-identity.ts +31 -1
  365. package/src/permissions/trust-store.ts +21 -1
  366. package/src/providers/anthropic/client.ts +4 -4
  367. package/src/providers/failover.ts +2 -2
  368. package/src/providers/model-intents.ts +70 -0
  369. package/src/providers/ollama/client.ts +2 -1
  370. package/src/providers/provider-send-message.ts +176 -0
  371. package/src/providers/registry.ts +71 -30
  372. package/src/providers/retry.ts +35 -1
  373. package/src/providers/types.ts +12 -1
  374. package/src/runtime/approval-conversation-turn.ts +97 -0
  375. package/src/runtime/approval-message-composer.ts +115 -5
  376. package/src/runtime/assistant-event-hub.ts +3 -1
  377. package/src/runtime/channel-approval-parser.ts +36 -2
  378. package/src/runtime/channel-approvals.ts +0 -21
  379. package/src/runtime/channel-guardian-service.ts +48 -7
  380. package/src/runtime/channel-readiness-service.ts +160 -34
  381. package/src/runtime/channel-readiness-types.ts +10 -4
  382. package/src/runtime/channel-retry-sweep.ts +184 -0
  383. package/src/runtime/guardian-context-resolver.ts +108 -0
  384. package/src/runtime/http-server.ts +289 -745
  385. package/src/runtime/http-types.ts +56 -3
  386. package/src/runtime/middleware/auth.ts +116 -0
  387. package/src/runtime/middleware/error-handler.ts +33 -0
  388. package/src/runtime/middleware/twilio-validation.ts +127 -0
  389. package/src/runtime/routes/app-routes.ts +1 -1
  390. package/src/runtime/routes/call-routes.ts +49 -6
  391. package/src/runtime/routes/channel-delivery-routes.ts +170 -0
  392. package/src/runtime/routes/channel-guardian-routes.ts +1191 -0
  393. package/src/runtime/routes/channel-inbound-routes.ts +1152 -0
  394. package/src/runtime/routes/channel-route-shared.ts +144 -0
  395. package/src/runtime/routes/channel-routes.ts +32 -1634
  396. package/src/runtime/routes/conversation-routes.ts +50 -7
  397. package/src/runtime/routes/events-routes.ts +2 -2
  398. package/src/runtime/routes/identity-routes.ts +126 -0
  399. package/src/runtime/routes/pairing-routes.ts +144 -0
  400. package/src/runtime/routes/run-routes.ts +15 -1
  401. package/src/runtime/run-orchestrator.ts +52 -34
  402. package/src/schedule/schedule-store.ts +36 -32
  403. package/src/schedule/scheduler.ts +3 -3
  404. package/src/security/encrypted-store.ts +5 -7
  405. package/src/security/oauth2.ts +45 -15
  406. package/src/security/parental-control-store.ts +183 -0
  407. package/src/security/secret-allowlist.ts +4 -3
  408. package/src/security/secret-scanner.ts +5 -5
  409. package/src/security/secure-keys.ts +1 -1
  410. package/src/security/token-manager.ts +3 -2
  411. package/src/services/vercel-deploy.ts +6 -2
  412. package/src/skills/tool-manifest.ts +3 -3
  413. package/src/skills/vellum-catalog-remote.ts +75 -16
  414. package/src/slack/slack-webhook.ts +2 -1
  415. package/src/swarm/orchestrator.ts +92 -1
  416. package/src/swarm/router-planner.ts +6 -9
  417. package/src/swarm/worker-prompts.ts +9 -12
  418. package/src/tasks/task-compiler.ts +19 -28
  419. package/src/tasks/task-runner.ts +1 -1
  420. package/src/tools/assets/search.ts +15 -14
  421. package/src/tools/browser/__tests__/auth-detector.test.ts +1 -0
  422. package/src/tools/browser/auto-navigate.ts +1 -0
  423. package/src/tools/browser/browser-execution.ts +13 -1
  424. package/src/tools/browser/browser-manager.ts +119 -4
  425. package/src/tools/browser/network-recorder.ts +5 -0
  426. package/src/tools/credentials/broker.ts +11 -2
  427. package/src/tools/credentials/metadata-store.ts +18 -14
  428. package/src/tools/credentials/post-connect-hooks.ts +61 -0
  429. package/src/tools/credentials/vault.ts +49 -23
  430. package/src/tools/executor.ts +80 -18
  431. package/src/tools/host-terminal/cli-discover.ts +1 -1
  432. package/src/tools/network/script-proxy/http-forwarder.ts +1 -1
  433. package/src/tools/network/script-proxy/mitm-handler.ts +1 -1
  434. package/src/tools/network/script-proxy/server.ts +1 -1
  435. package/src/tools/network/script-proxy/session-manager.ts +6 -5
  436. package/src/tools/network/web-fetch.ts +18 -2
  437. package/src/tools/network/web-search.ts +7 -3
  438. package/src/tools/reminder/reminder-store.ts +14 -15
  439. package/src/tools/schedule/create.ts +1 -0
  440. package/src/tools/schedule/list.ts +2 -1
  441. package/src/tools/shared/filesystem/file-ops-service.ts +5 -7
  442. package/src/tools/skills/skill-script-runner.ts +24 -9
  443. package/src/tools/skills/skill-tool-factory.ts +1 -0
  444. package/src/tools/tasks/work-item-enqueue.ts +2 -2
  445. package/src/tools/terminal/evaluate-typescript.ts +21 -12
  446. package/src/tools/terminal/parser.ts +50 -0
  447. package/src/tools/watcher/delete.ts +6 -0
  448. package/src/tools/weather/service.ts +1 -1
  449. package/src/twitter/client.ts +190 -24
  450. package/src/twitter/session.ts +4 -3
  451. package/src/util/clipboard.ts +1 -1
  452. package/src/util/errors.ts +65 -8
  453. package/src/util/fs.ts +40 -0
  454. package/src/util/json.ts +10 -0
  455. package/src/util/log-redact.ts +189 -0
  456. package/src/util/logger.ts +25 -18
  457. package/src/util/object.ts +3 -0
  458. package/src/util/platform.ts +72 -365
  459. package/src/util/pricing.ts +1 -1
  460. package/src/util/promise-guard.ts +1 -1
  461. package/src/util/retry.ts +19 -0
  462. package/src/util/row-mapper.ts +79 -0
  463. package/src/util/silently.ts +21 -0
  464. package/src/watcher/engine.ts +5 -1
  465. package/src/watcher/provider-types.ts +20 -0
  466. package/src/watcher/providers/github.ts +156 -0
  467. package/src/watcher/providers/gmail.ts +1 -0
  468. package/src/watcher/providers/google-calendar.ts +1 -0
  469. package/src/watcher/providers/linear.ts +460 -0
  470. package/src/watcher/providers/slack.ts +1 -0
  471. package/src/work-items/work-item-runner.ts +1 -1
  472. package/src/workspace/git-service.ts +1 -1
  473. package/src/workspace/provider-commit-message-generator.ts +51 -22
  474. package/src/__tests__/call-bridge.test.ts +0 -517
  475. package/src/__tests__/session-process-bridge.test.ts +0 -244
  476. package/src/calls/call-bridge.ts +0 -168
  477. package/src/config/bundled-skills/media-processing/services/capability-registry.ts +0 -137
  478. package/src/config/bundled-skills/media-processing/services/event-detection-service.ts +0 -280
  479. package/src/config/bundled-skills/media-processing/services/feedback-aggregation.ts +0 -144
  480. package/src/config/bundled-skills/media-processing/services/feedback-store.ts +0 -136
  481. package/src/config/bundled-skills/media-processing/services/retrieval-service.ts +0 -95
  482. package/src/config/bundled-skills/media-processing/services/timeline-service.ts +0 -267
  483. package/src/config/bundled-skills/media-processing/tools/detect-events.ts +0 -110
  484. package/src/config/bundled-skills/media-processing/tools/recalibrate.ts +0 -235
  485. package/src/config/bundled-skills/media-processing/tools/select-tracking-profile.ts +0 -142
  486. package/src/config/bundled-skills/media-processing/tools/submit-feedback.ts +0 -150
  487. package/src/config/vellum-skills/google-oauth-setup/SKILL.md +0 -199
@@ -1,18 +1,14 @@
1
- import { mkdirSync, existsSync, statSync, unlinkSync, renameSync, readFileSync, writeFileSync, readdirSync, chmodSync } from 'node:fs';
2
- import { join, dirname } from 'node:path';
1
+ import { mkdirSync, existsSync, statSync, unlinkSync, readFileSync, writeFileSync, chmodSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
3
  import { homedir } from 'node:os';
4
- /**
5
- * Stderr-only logger for migration code. Using the pino logger during
6
- * migration is unsafe because pino initialization calls ensureDataDir(),
7
- * which pre-creates workspace destination directories and causes migration
8
- * moves to no-op.
9
- */
10
- function migrationLog(level: 'info' | 'warn' | 'debug', msg: string, data?: Record<string, unknown>): void {
11
- if (level === 'debug') return; // suppress debug-level migration noise
12
- const prefix = level === 'warn' ? 'WARN' : 'INFO';
13
- const extra = data ? ' ' + JSON.stringify(data) : '';
14
- process.stderr.write(`[migration] ${prefix}: ${msg}${extra}\n`);
15
- }
4
+ import {
5
+ getBaseDataDir,
6
+ getDaemonSocket,
7
+ getDaemonTcpPort,
8
+ getDaemonTcpEnabled,
9
+ getDaemonTcpHost,
10
+ getDaemonIosPairing,
11
+ } from '../config/env-registry.js';
16
12
 
17
13
  export function isMacOS(): boolean {
18
14
  return process.platform === 'darwin';
@@ -52,7 +48,7 @@ export function getClipboardCommand(): string | null {
52
48
  * Returns null if neither file exists or both are malformed.
53
49
  */
54
50
  export function readLockfile(): Record<string, unknown> | null {
55
- const base = process.env.BASE_DATA_DIR?.trim() || homedir();
51
+ const base = getBaseDataDir() || homedir();
56
52
  const candidates = [
57
53
  join(base, '.vellum.lock.json'),
58
54
  join(base, '.vellum.lockfile.json'),
@@ -71,12 +67,40 @@ export function readLockfile(): Record<string, unknown> | null {
71
67
  return null;
72
68
  }
73
69
 
70
+ /**
71
+ * Normalize an assistant ID to its canonical form for DB operations.
72
+ *
73
+ * The system uses "self" as the canonical single-tenant identifier
74
+ * (see migration 007-assistant-id-to-self). However, the desktop UI
75
+ * sends the real assistant ID (e.g., "vellum-true-eel") while the
76
+ * inbound call path resolves phone numbers to config keys (typically
77
+ * "self"). This function maps any known lockfile assistant ID to "self"
78
+ * so both sides use a consistent DB key.
79
+ */
80
+ export function normalizeAssistantId(assistantId: string): string {
81
+ if (assistantId === 'self') return 'self';
82
+
83
+ try {
84
+ const lockData = readLockfile();
85
+ const assistants = lockData?.assistants as Array<Record<string, unknown>> | undefined;
86
+ if (assistants) {
87
+ for (const entry of assistants) {
88
+ if (entry.assistantId === assistantId) return 'self';
89
+ }
90
+ }
91
+ } catch {
92
+ // lockfile unreadable — return as-is
93
+ }
94
+
95
+ return assistantId;
96
+ }
97
+
74
98
  /**
75
99
  * Write data to the primary lockfile (~/.vellum.lock.json).
76
100
  * Respects BASE_DATA_DIR for non-standard home directories.
77
101
  */
78
102
  export function writeLockfile(data: Record<string, unknown>): void {
79
- const base = process.env.BASE_DATA_DIR?.trim() || homedir();
103
+ const base = getBaseDataDir() || homedir();
80
104
  writeFileSync(join(base, '.vellum.lock.json'), JSON.stringify(data, null, 2) + '\n');
81
105
  }
82
106
 
@@ -85,7 +109,7 @@ export function writeLockfile(data: Record<string, unknown>): void {
85
109
  * files, skills) and runtime files (socket, PID) live here.
86
110
  */
87
111
  export function getRootDir(): string {
88
- return join(process.env.BASE_DATA_DIR?.trim() || homedir(), '.vellum');
112
+ return join(getBaseDataDir() || homedir(), '.vellum');
89
113
  }
90
114
 
91
115
  /**
@@ -126,7 +150,7 @@ export function getInterfacesDir(): string {
126
150
  }
127
151
 
128
152
  export function getSocketPath(): string {
129
- const override = process.env.VELLUM_DAEMON_SOCKET?.trim();
153
+ const override = getDaemonSocket();
130
154
  if (override) {
131
155
  return expandHomePath(override);
132
156
  }
@@ -142,12 +166,7 @@ export function getSessionTokenPath(): string {
142
166
  * Reads VELLUM_DAEMON_TCP_PORT env var; defaults to 8765.
143
167
  */
144
168
  export function getTCPPort(): number {
145
- const override = process.env.VELLUM_DAEMON_TCP_PORT?.trim();
146
- if (override) {
147
- const port = parseInt(override, 10);
148
- if (!isNaN(port) && port > 0 && port <= 65535) return port;
149
- }
150
- return 8765;
169
+ return getDaemonTcpPort();
151
170
  }
152
171
 
153
172
  /**
@@ -162,9 +181,8 @@ export function getTCPPort(): number {
162
181
  * The macOS CLI (AssistantCli) also sets the env var for bundled-binary deployments.
163
182
  */
164
183
  export function isTCPEnabled(): boolean {
165
- const override = process.env.VELLUM_DAEMON_TCP_ENABLED?.trim();
166
- if (override === 'true' || override === '1') return true;
167
- if (override === 'false' || override === '0') return false;
184
+ const envValue = getDaemonTcpEnabled();
185
+ if (envValue !== undefined) return envValue;
168
186
  return existsSync(join(getRootDir(), 'tcp-enabled'));
169
187
  }
170
188
 
@@ -176,7 +194,7 @@ export function isTCPEnabled(): boolean {
176
194
  * 3. Default: '127.0.0.1' (localhost only)
177
195
  */
178
196
  export function getTCPHost(): string {
179
- const override = process.env.VELLUM_DAEMON_TCP_HOST?.trim();
197
+ const override = getDaemonTcpHost();
180
198
  if (override) return override;
181
199
  if (isIOSPairingEnabled()) return '0.0.0.0';
182
200
  return '127.0.0.1';
@@ -197,9 +215,8 @@ export function getTCPHost(): string {
197
215
  * access without exposing the daemon to the LAN.
198
216
  */
199
217
  export function isIOSPairingEnabled(): boolean {
200
- const override = process.env.VELLUM_DAEMON_IOS_PAIRING?.trim();
201
- if (override === 'true' || override === '1') return true;
202
- if (override === 'false' || override === '0') return false;
218
+ const envValue = getDaemonIosPairing();
219
+ if (envValue !== undefined) return envValue;
203
220
  return existsSync(join(getRootDir(), 'ios-pairing-enabled'));
204
221
  }
205
222
 
@@ -207,6 +224,27 @@ export function getHttpTokenPath(): string {
207
224
  return join(getRootDir(), 'http-token');
208
225
  }
209
226
 
227
+ /**
228
+ * Returns the path to the platform API token file (~/.vellum/platform-token).
229
+ * This token is the X-Session-Token used to authenticate with the Vellum
230
+ * Platform API (e.g. assistant.vellum.ai).
231
+ */
232
+ export function getPlatformTokenPath(): string {
233
+ return join(getRootDir(), 'platform-token');
234
+ }
235
+
236
+ /**
237
+ * Read the platform API token from disk. Returns null if the file
238
+ * doesn't exist or can't be read.
239
+ */
240
+ export function readPlatformToken(): string | null {
241
+ try {
242
+ return readFileSync(getPlatformTokenPath(), 'utf-8').trim();
243
+ } catch {
244
+ return null;
245
+ }
246
+ }
247
+
210
248
  /**
211
249
  * Read the daemon session token from disk. Returns null if the file
212
250
  * doesn't exist or can't be read (daemon not running).
@@ -302,276 +340,9 @@ export function getWorkspacePromptPath(file: string): string {
302
340
  return join(getWorkspaceDir(), file);
303
341
  }
304
342
 
305
- /**
306
- * Idempotent move: relocates source to destination for migration.
307
- * - No-op if source is missing (already migrated or never existed).
308
- * - No-op if destination already exists (avoids clobbering).
309
- * - Creates destination parent directories as needed.
310
- * - Logs warning on failure instead of throwing.
311
- *
312
- * Exported for testing; not intended for general use outside migrations.
313
- */
314
- export function migratePath(source: string, destination: string): void {
315
- if (!existsSync(source)) return;
316
- if (existsSync(destination)) {
317
- migrationLog('debug', 'Migration skipped: destination already exists', { source, destination });
318
- return;
319
- }
320
- try {
321
- const destDir = dirname(destination);
322
- if (!existsSync(destDir)) {
323
- mkdirSync(destDir, { recursive: true });
324
- }
325
- renameSync(source, destination);
326
- migrationLog('info', 'Migrated path', { from: source, to: destination });
327
- } catch (err) {
328
- migrationLog('warn', 'Failed to migrate path', { err: String(err), from: source, to: destination });
329
- }
330
- }
331
-
332
- /**
333
- * When migratePath skips config.json because the workspace copy already
334
- * exists, the legacy root config may still contain keys (e.g. slackWebhookUrl)
335
- * that were never written to the workspace config. This merges any missing
336
- * top-level keys from the legacy file into the workspace file so they are
337
- * not silently lost during upgrade.
338
- */
339
- function isPlainObject(v: unknown): v is Record<string, unknown> {
340
- return v !== null && typeof v === 'object' && !Array.isArray(v);
341
- }
342
-
343
- function mergeSkippedConfigKeys(legacyPath: string, workspacePath: string): void {
344
- if (!existsSync(legacyPath) || !existsSync(workspacePath)) return;
345
-
346
- let legacy: Record<string, unknown>;
347
- let workspace: Record<string, unknown>;
348
- try {
349
- const legacyRaw = JSON.parse(readFileSync(legacyPath, 'utf-8'));
350
- const workspaceRaw = JSON.parse(readFileSync(workspacePath, 'utf-8'));
351
- if (!isPlainObject(legacyRaw) || !isPlainObject(workspaceRaw)) return;
352
- legacy = legacyRaw;
353
- workspace = workspaceRaw;
354
- } catch {
355
- return; // malformed JSON — skip silently
356
- }
357
-
358
- const merged: string[] = [];
359
- for (const key of Object.keys(legacy)) {
360
- if (!(key in workspace)) {
361
- workspace[key] = legacy[key];
362
- merged.push(key);
363
- }
364
- }
365
-
366
- if (merged.length > 0) {
367
- try {
368
- writeFileSync(workspacePath, JSON.stringify(workspace, null, 2) + '\n');
369
- // Remove merged keys from legacy config so they are not resurrected
370
- // if a user later deletes them from the workspace config.
371
- for (const key of merged) {
372
- delete legacy[key];
373
- }
374
- if (Object.keys(legacy).length === 0) {
375
- unlinkSync(legacyPath);
376
- } else {
377
- writeFileSync(legacyPath, JSON.stringify(legacy, null, 2) + '\n');
378
- }
379
- migrationLog('info', 'Merged legacy config keys into workspace config', { keys: merged });
380
- } catch (err) {
381
- migrationLog('warn', 'Failed to merge legacy config keys', { err: String(err), keys: merged });
382
- }
383
- }
384
- }
385
-
386
- /**
387
- * When migratePath skips the hooks directory because the workspace copy
388
- * already exists (e.g. pre-created by ensureDataDir), the legacy hooks
389
- * directory may still contain individual hook files/subdirectories that
390
- * were never moved. This merges any missing entries from the legacy
391
- * path into the workspace hooks path so they are not silently lost.
392
- */
393
- function mergeLegacyHooks(legacyDir: string, workspaceDir: string): void {
394
- if (!existsSync(legacyDir) || !existsSync(workspaceDir)) return;
395
-
396
- let entries: import('node:fs').Dirent[];
397
- try {
398
- entries = readdirSync(legacyDir, { withFileTypes: true });
399
- } catch {
400
- return;
401
- }
402
-
403
- for (const entry of entries) {
404
- const src = join(legacyDir, entry.name);
405
- const dest = join(workspaceDir, entry.name);
406
- if (existsSync(dest)) {
407
- // config.json needs a merge rather than a skip — the legacy file may
408
- // contain hook enabled/settings entries that the workspace copy lacks.
409
- if (entry.name === 'config.json') {
410
- mergeHooksConfig(src, dest);
411
- }
412
- continue;
413
- }
414
- try {
415
- renameSync(src, dest);
416
- migrationLog('info', 'Merged legacy hook into workspace', { from: src, to: dest });
417
- } catch (err) {
418
- migrationLog('warn', 'Failed to merge legacy hook', { err: String(err), from: src, to: dest });
419
- }
420
- }
421
- }
422
-
423
- /**
424
- * Merge missing hook entries from a legacy hooks/config.json into the
425
- * workspace hooks/config.json. Only adds hooks that don't already exist
426
- * in the workspace config so user changes are never overwritten.
427
- */
428
- function mergeHooksConfig(legacyPath: string, workspacePath: string): void {
429
- let legacy: Record<string, unknown>;
430
- let workspace: Record<string, unknown>;
431
- try {
432
- const legacyRaw = JSON.parse(readFileSync(legacyPath, 'utf-8'));
433
- const workspaceRaw = JSON.parse(readFileSync(workspacePath, 'utf-8'));
434
- if (!isPlainObject(legacyRaw) || !isPlainObject(workspaceRaw)) return;
435
- legacy = legacyRaw;
436
- workspace = workspaceRaw;
437
- } catch {
438
- return;
439
- }
440
-
441
- const legacyHooks = legacy.hooks;
442
- const wsHooks = workspace.hooks;
443
- if (!isPlainObject(legacyHooks) || !isPlainObject(wsHooks)) return;
444
-
445
- const merged: string[] = [];
446
- for (const hookName of Object.keys(legacyHooks)) {
447
- if (!(hookName in wsHooks)) {
448
- wsHooks[hookName] = legacyHooks[hookName];
449
- merged.push(hookName);
450
- }
451
- }
452
-
453
- if (merged.length > 0) {
454
- try {
455
- writeFileSync(workspacePath, JSON.stringify(workspace, null, 2) + '\n');
456
- // Remove merged hooks from legacy config to prevent resurrection
457
- for (const hookName of merged) {
458
- delete legacyHooks[hookName];
459
- }
460
- if (Object.keys(legacyHooks).length === 0) {
461
- unlinkSync(legacyPath);
462
- } else {
463
- writeFileSync(legacyPath, JSON.stringify(legacy, null, 2) + '\n');
464
- }
465
- migrationLog('info', 'Merged legacy hooks config entries into workspace', { hooks: merged });
466
- } catch (err) {
467
- migrationLog('warn', 'Failed to merge legacy hooks config', { err: String(err), hooks: merged });
468
- }
469
- }
470
- }
471
-
472
- /**
473
- * When migratePath skips the skills directory because the workspace copy
474
- * already exists (e.g. pre-created by ensureDataDir), the legacy skills
475
- * directory may still contain individual skill subdirectories that were
476
- * never moved. This merges any missing skill subdirectories from the
477
- * legacy path into the workspace skills path so they are not stranded.
478
- */
479
- function mergeLegacySkills(legacyDir: string, workspaceDir: string): void {
480
- if (!existsSync(legacyDir) || !existsSync(workspaceDir)) return;
481
-
482
- let entries: import('node:fs').Dirent[];
483
- try {
484
- entries = readdirSync(legacyDir, { withFileTypes: true });
485
- } catch {
486
- return;
487
- }
488
-
489
- for (const entry of entries) {
490
- const src = join(legacyDir, entry.name);
491
- const dest = join(workspaceDir, entry.name);
492
- if (existsSync(dest)) continue; // already present in workspace
493
- try {
494
- renameSync(src, dest);
495
- migrationLog('info', 'Merged legacy skill into workspace', { from: src, to: dest });
496
- } catch (err) {
497
- migrationLog('warn', 'Failed to merge legacy skill', { err: String(err), from: src, to: dest });
498
- }
499
- }
500
- }
501
-
502
- /**
503
- * When migratePath skips the data directory because workspace/data already
504
- * exists (e.g. the user's project had a data/ folder that was extracted from
505
- * sandbox/fs), the legacy data directory may still contain internal state
506
- * subdirectories (db/, logs/, sandbox/, etc.) that need to be preserved.
507
- * This merges any missing entries from the legacy data path into workspace/data.
508
- */
509
- function mergeLegacyDataEntries(legacyDir: string, workspaceDir: string): void {
510
- if (!existsSync(legacyDir) || !existsSync(workspaceDir)) return;
511
-
512
- let entries: import('node:fs').Dirent[];
513
- try {
514
- entries = readdirSync(legacyDir, { withFileTypes: true });
515
- } catch {
516
- return;
517
- }
518
-
519
- for (const entry of entries) {
520
- const src = join(legacyDir, entry.name);
521
- const dest = join(workspaceDir, entry.name);
522
- if (existsSync(dest)) continue; // already present in workspace
523
- try {
524
- renameSync(src, dest);
525
- migrationLog('info', 'Merged legacy data entry into workspace', { from: src, to: dest });
526
- } catch (err) {
527
- migrationLog('warn', 'Failed to merge legacy data entry', { err: String(err), from: src, to: dest });
528
- }
529
- }
530
- }
531
-
532
- /**
533
- * Migrate from the flat ~/.vellum layout to the workspace-based layout.
534
- *
535
- * Step (a) is special: if the workspace dir doesn't exist yet but the old
536
- * sandbox working dir (data/sandbox/fs) does, its contents are "extracted"
537
- * to become the new workspace root via rename. All subsequent moves then
538
- * land inside that workspace directory.
539
- *
540
- * Idempotent: safe to call on every startup — already-migrated items are
541
- * skipped, and a second run is a no-op.
542
- */
543
- export function migrateToWorkspaceLayout(): void {
544
- const root = getRootDir();
545
- if (!existsSync(root)) return;
546
-
547
- const ws = getWorkspaceDir();
548
-
549
- // (a) Extract data/sandbox/fs -> workspace (only when workspace doesn't exist yet)
550
- if (!existsSync(ws)) {
551
- const sandboxFs = join(root, 'data', 'sandbox', 'fs');
552
- if (existsSync(sandboxFs)) {
553
- try {
554
- renameSync(sandboxFs, ws);
555
- migrationLog('info', 'Extracted sandbox/fs as workspace root', { from: sandboxFs, to: ws });
556
- } catch (err) {
557
- migrationLog('warn', 'Failed to extract sandbox/fs', { err: String(err), from: sandboxFs, to: ws });
558
- }
559
- }
560
- }
561
-
562
- // (b)-(h) Move legacy root-level items into workspace
563
- migratePath(join(root, 'config.json'), join(ws, 'config.json'));
564
- mergeSkippedConfigKeys(join(root, 'config.json'), join(ws, 'config.json'));
565
- migratePath(join(root, 'data'), join(ws, 'data'));
566
- mergeLegacyDataEntries(join(root, 'data'), join(ws, 'data'));
567
- migratePath(join(root, 'hooks'), join(ws, 'hooks'));
568
- mergeLegacyHooks(join(root, 'hooks'), join(ws, 'hooks'));
569
- migratePath(join(root, 'IDENTITY.md'), join(ws, 'IDENTITY.md'));
570
- migratePath(join(root, 'skills'), join(ws, 'skills'));
571
- mergeLegacySkills(join(root, 'skills'), join(ws, 'skills'));
572
- migratePath(join(root, 'SOUL.md'), join(ws, 'SOUL.md'));
573
- migratePath(join(root, 'USER.md'), join(ws, 'USER.md'));
574
- }
343
+ // Re-export migration functions so existing consumers don't break.
344
+ export { migratePath, migrateToWorkspaceLayout } from '../migrations/workspace-layout.js';
345
+ export { migrateToDataLayout } from '../migrations/data-layout.js';
575
346
 
576
347
  export function ensureDataDir(): void {
577
348
  const root = getRootDir();
@@ -608,67 +379,3 @@ export function ensureDataDir(): void {
608
379
  // Non-fatal: some filesystems don't support Unix permissions
609
380
  }
610
381
  }
611
-
612
- /**
613
- * Migrate files from the old flat ~/.vellum layout to the new structured
614
- * layout with data/ and protected/ subdirectories.
615
- *
616
- * Idempotent: skips items that have already been migrated.
617
- * Uses renameSync for atomic moves (same filesystem).
618
- */
619
- export function migrateToDataLayout(): void {
620
- const root = getRootDir();
621
- const data = join(root, 'data');
622
-
623
- if (!existsSync(root)) return;
624
-
625
- function migrateItem(oldPath: string, newPath: string): void {
626
- if (!existsSync(oldPath)) return;
627
- if (existsSync(newPath)) return;
628
- try {
629
- const newDir = dirname(newPath);
630
- if (!existsSync(newDir)) {
631
- mkdirSync(newDir, { recursive: true });
632
- }
633
- renameSync(oldPath, newPath);
634
- migrationLog('info', 'Migrated path', { from: oldPath, to: newPath });
635
- } catch (err) {
636
- migrationLog('warn', 'Failed to migrate path', { err: String(err), from: oldPath, to: newPath });
637
- }
638
- }
639
-
640
- // DB: ~/.vellum/data/assistant.db → ~/.vellum/data/db/assistant.db
641
- migrateItem(join(data, 'assistant.db'), join(data, 'db', 'assistant.db'));
642
- migrateItem(join(data, 'assistant.db-wal'), join(data, 'db', 'assistant.db-wal'));
643
- migrateItem(join(data, 'assistant.db-shm'), join(data, 'db', 'assistant.db-shm'));
644
-
645
- // Qdrant PID: ~/.vellum/qdrant.pid → ~/.vellum/data/qdrant/qdrant.pid
646
- migrateItem(join(root, 'qdrant.pid'), join(data, 'qdrant', 'qdrant.pid'));
647
-
648
- // Qdrant binary: ~/.vellum/bin/ → ~/.vellum/data/qdrant/bin/
649
- migrateItem(join(root, 'bin'), join(data, 'qdrant', 'bin'));
650
-
651
- // Logs: ~/.vellum/logs/ → ~/.vellum/data/logs/
652
- migrateItem(join(root, 'logs'), join(data, 'logs'));
653
-
654
- // Memory: ~/.vellum/memory/ → ~/.vellum/data/memory/
655
- migrateItem(join(root, 'memory'), join(data, 'memory'));
656
-
657
- // Apps: ~/.vellum/apps/ → ~/.vellum/data/apps/
658
- migrateItem(join(root, 'apps'), join(data, 'apps'));
659
-
660
- // Browser auth: ~/.vellum/browser-auth/ → ~/.vellum/data/browser-auth/
661
- migrateItem(join(root, 'browser-auth'), join(data, 'browser-auth'));
662
-
663
- // Browser profile: ~/.vellum/browser-profile/ → ~/.vellum/data/browser-profile/
664
- migrateItem(join(root, 'browser-profile'), join(data, 'browser-profile'));
665
-
666
- // History: ~/.vellum/history → ~/.vellum/data/history
667
- migrateItem(join(root, 'history'), join(data, 'history'));
668
-
669
- // Protected files: ~/.vellum/X → ~/.vellum/protected/X
670
- const protectedDir = join(root, 'protected');
671
- migrateItem(join(root, 'trust.json'), join(protectedDir, 'trust.json'));
672
- migrateItem(join(root, 'keys.enc'), join(protectedDir, 'keys.enc'));
673
- migrateItem(join(root, 'secret-allowlist.json'), join(protectedDir, 'secret-allowlist.json'));
674
- }
@@ -143,7 +143,7 @@ export function estimateCost(
143
143
  provider: string,
144
144
  ): number {
145
145
  const result = resolvePricing(provider, model, inputTokens, outputTokens);
146
- if (result.pricingStatus === 'priced' && result.estimatedCostUsd !== null) {
146
+ if (result.pricingStatus === 'priced' && result.estimatedCostUsd != null) {
147
147
  return result.estimatedCostUsd;
148
148
  }
149
149
  return 0;
@@ -8,7 +8,7 @@ export class PromiseGuard<T> {
8
8
 
9
9
  /** Whether a promise is currently in-flight. */
10
10
  get active(): boolean {
11
- return this.promise !== null;
11
+ return this.promise != null;
12
12
  }
13
13
 
14
14
  /**
package/src/util/retry.ts CHANGED
@@ -95,4 +95,23 @@ export function sleep(ms: number): Promise<void> {
95
95
  return new Promise((resolve) => setTimeout(resolve, ms));
96
96
  }
97
97
 
98
+ /**
99
+ * Like `sleep` but resolves early when an AbortSignal fires.
100
+ * Resolves (not rejects) on abort so callers can check the signal
101
+ * themselves and decide what to do.
102
+ */
103
+ export function abortableSleep(ms: number, signal?: AbortSignal): Promise<void> {
104
+ if (!signal) return sleep(ms);
105
+ if (signal.aborted) return Promise.resolve();
106
+ return new Promise((resolve) => {
107
+ const timer = setTimeout(onDone, ms);
108
+ signal.addEventListener('abort', onDone, { once: true });
109
+ function onDone() {
110
+ clearTimeout(timer);
111
+ signal!.removeEventListener('abort', onDone);
112
+ resolve();
113
+ }
114
+ });
115
+ }
116
+
98
117
  export { DEFAULT_MAX_RETRIES, DEFAULT_BASE_DELAY_MS };
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Generic DB row mapper — replaces repetitive parse* functions across store files.
3
+ *
4
+ * Each field in the schema is described by either a source column name (passthrough)
5
+ * or a transform descriptor. The mapper produces a function that converts a raw
6
+ * Drizzle row into a typed domain object.
7
+ */
8
+
9
+ // A field descriptor is either a key of the source row (passthrough) or a transform.
10
+ type FieldDescriptor<TRow, TOut> =
11
+ | (keyof TRow & string)
12
+ | { from: keyof TRow & string; transform: (value: TRow[keyof TRow]) => TOut };
13
+
14
+ // The schema maps each output field to a field descriptor.
15
+ type MapperSchema<TRow, TDomain> = {
16
+ [K in keyof TDomain]: FieldDescriptor<TRow, TDomain[K]>;
17
+ };
18
+
19
+ /**
20
+ * Create a row-to-domain mapper from a declarative schema.
21
+ *
22
+ * Usage:
23
+ * ```ts
24
+ * const parseReminder = createRowMapper<typeof reminders.$inferSelect, ReminderRow>({
25
+ * id: 'id',
26
+ * label: 'label',
27
+ * mode: { from: 'mode', transform: (v) => v as ReminderRow['mode'] },
28
+ * });
29
+ * ```
30
+ */
31
+ export function createRowMapper<TRow, TDomain>(
32
+ schema: MapperSchema<TRow, TDomain>,
33
+ ): (row: TRow) => TDomain {
34
+ const entries = Object.entries(schema) as Array<
35
+ [string, FieldDescriptor<TRow, unknown>]
36
+ >;
37
+
38
+ return (row: TRow): TDomain => {
39
+ const result = {} as Record<string, unknown>;
40
+ for (const [key, descriptor] of entries) {
41
+ if (typeof descriptor === 'string') {
42
+ result[key] = row[descriptor as keyof TRow];
43
+ } else {
44
+ const d = descriptor as { from: keyof TRow & string; transform: (value: TRow[keyof TRow]) => unknown };
45
+ result[key] = d.transform(row[d.from]);
46
+ }
47
+ }
48
+ return result as TDomain;
49
+ };
50
+ }
51
+
52
+ /** Convenience: cast a value to a narrower type (for string union columns). */
53
+ export function cast<T>() {
54
+ return (value: unknown) => value as T;
55
+ }
56
+
57
+ /** Convenience: parse a JSON string column with a fallback value on parse failure. */
58
+ export function parseJson<T>(fallback: T): (value: unknown) => T {
59
+ return (value: unknown): T => {
60
+ if (typeof value !== 'string' || !value) return fallback;
61
+ try {
62
+ return JSON.parse(value) as T;
63
+ } catch {
64
+ return fallback;
65
+ }
66
+ };
67
+ }
68
+
69
+ /** Convenience: parse a JSON string column, returning null on parse failure. */
70
+ export function parseJsonNullable<T>(): (value: unknown) => T | null {
71
+ return (value: unknown): T | null => {
72
+ if (typeof value !== 'string' || !value) return null;
73
+ try {
74
+ return JSON.parse(value) as T;
75
+ } catch {
76
+ return null;
77
+ }
78
+ };
79
+ }
@@ -0,0 +1,21 @@
1
+ import { getLogger } from './logger.js';
2
+
3
+ const log = getLogger('silently');
4
+
5
+ /**
6
+ * Attaches a `.catch()` to `promise` that emits a debug-level log instead of
7
+ * swallowing the rejection completely. Use this in place of bare
8
+ * `.catch(() => {})` when you need fire-and-forget semantics but still want
9
+ * visibility into unexpected errors during debugging.
10
+ *
11
+ * The original promise is returned unchanged so callers can still chain it.
12
+ *
13
+ * @example
14
+ * silentlyWithLog(stopSession(id), 'idle session cleanup');
15
+ */
16
+ export function silentlyWithLog<T>(promise: Promise<T>, context: string): Promise<T> {
17
+ promise.catch((err: unknown) => {
18
+ log.debug({ err, context }, 'Suppressed async error');
19
+ });
20
+ return promise;
21
+ }
@@ -85,7 +85,7 @@ export async function runWatchersOnce(
85
85
  log.info({ watcherId: watcher.id, watermark }, 'Initialized watermark');
86
86
  }
87
87
 
88
- const result = await provider.fetchNew(watcher.credentialService, watermark, config);
88
+ const result = await provider.fetchNew(watcher.credentialService, watermark, config, watcher.id);
89
89
 
90
90
  // Store new events with dedup
91
91
  let newEvents = 0;
@@ -118,6 +118,10 @@ export async function runWatchersOnce(
118
118
  if ((watcher.consecutiveErrors + 1) >= MAX_CONSECUTIVE_ERRORS) {
119
119
  const reason = `Disabled after ${MAX_CONSECUTIVE_ERRORS} consecutive errors. Last: ${message}`;
120
120
  disableWatcher(watcher.id, reason);
121
+ // Do NOT call provider.cleanup() here — auto-disable is reversible.
122
+ // If the watcher is re-enabled later, it must diff against the same
123
+ // baseline to avoid missing events that occurred while disabled.
124
+ // Cleanup is only correct on true deletion (see tools/watcher/delete.ts).
121
125
  log.warn({ watcherId: watcher.id, name: watcher.name }, 'Watcher disabled by circuit breaker');
122
126
  notify({
123
127
  title: `Watcher disabled: ${watcher.name}`,