@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
package/src/instrument.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import * as Sentry from "@sentry/node";
2
2
  import { APP_VERSION } from "./version.js";
3
+ import { getSentryDsn } from "./config/env.js";
3
4
 
4
5
  /** Patterns that match sensitive data in Sentry event values. */
5
6
  const PII_PATTERNS = [
@@ -20,7 +21,7 @@ function redactString(value: string): string {
20
21
  function redactObject(obj: unknown): unknown {
21
22
  if (typeof obj === "string") return redactString(obj);
22
23
  if (Array.isArray(obj)) return obj.map(redactObject);
23
- if (obj !== null && typeof obj === "object") {
24
+ if (obj != null && typeof obj === "object") {
24
25
  const out: Record<string, unknown> = {};
25
26
  for (const [key, val] of Object.entries(obj)) {
26
27
  out[key] = redactObject(val);
@@ -30,10 +31,10 @@ function redactObject(obj: unknown): unknown {
30
31
  return obj;
31
32
  }
32
33
 
33
- /** Call after dotenv has loaded so process.env.SENTRY_DSN is available. */
34
+ /** Call after dotenv has loaded so SENTRY_DSN is available. */
34
35
  export function initSentry(): void {
35
36
  Sentry.init({
36
- dsn: process.env.SENTRY_DSN,
37
+ dsn: getSentryDsn(),
37
38
  release: `vellum-assistant@${APP_VERSION}`,
38
39
  environment: APP_VERSION === "0.0.0-dev" ? "development" : "production",
39
40
  sendDefaultPii: false,
package/src/logfire.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { Provider, ProviderResponse, SendMessageOptions, Message, ToolDefinition } from './providers/types.js';
2
2
  import { APP_VERSION } from './version.js';
3
3
  import { getLogger } from './util/logger.js';
4
+ import { getLogfireToken, isMonitoringEnabled } from './config/env.js';
4
5
 
5
6
  const log = getLogger('logfire');
6
7
 
@@ -8,8 +9,8 @@ type LogfireModule = typeof import('@pydantic/logfire-node');
8
9
 
9
10
  const LOGFIRE_ENABLED: boolean =
10
11
  APP_VERSION === '0.0.0-dev' &&
11
- !!process.env.LOGFIRE_TOKEN &&
12
- process.env.VELLUM_ENABLE_MONITORING === '1';
12
+ !!getLogfireToken() &&
13
+ isMonitoringEnabled();
13
14
 
14
15
  let logfireInstance: LogfireModule | null = null;
15
16
 
@@ -24,7 +25,7 @@ export async function initLogfire(): Promise<void> {
24
25
  try {
25
26
  const logfire = await import('@pydantic/logfire-node');
26
27
  logfire.configure({
27
- token: process.env.LOGFIRE_TOKEN,
28
+ token: getLogfireToken(),
28
29
  serviceName: 'vellum-assistant',
29
30
  serviceVersion: APP_VERSION,
30
31
  });
@@ -1,7 +1,7 @@
1
1
  import { getConfig } from '../config/loader.js';
2
2
  import { getLogger } from '../util/logger.js';
3
3
  import { getMemoryBackendStatus } from './embedding-backend.js';
4
- import { getDb } from './db.js';
4
+ import { rawGet } from './db.js';
5
5
  import { enqueueBackfillJob, enqueueRebuildIndexJob } from './indexer.js';
6
6
  import {
7
7
  enqueueCleanupResolvedConflictsJob,
@@ -43,28 +43,35 @@ export interface MemoryConflictAndCleanupStats {
43
43
  cleanup: MemorySystemStatus['cleanup'];
44
44
  }
45
45
 
46
+ interface ConflictStatsRow {
47
+ pending_count: number | null;
48
+ resolved_count: number | null;
49
+ oldest_pending_created_at: number | null;
50
+ }
51
+
52
+ interface CleanupStatsRow {
53
+ resolved_backlog: number | null;
54
+ superseded_backlog: number | null;
55
+ resolved_completed_24h: number | null;
56
+ superseded_completed_24h: number | null;
57
+ }
58
+
46
59
  /** Lightweight query for conflict/cleanup metrics only — no table counts or job totals. */
47
60
  export function getMemoryConflictAndCleanupStats(): MemoryConflictAndCleanupStats {
48
- const db = getDb();
49
- const raw = (db as unknown as { $client: { query: (q: string) => { get: (...args: unknown[]) => unknown } } }).$client;
50
- const conflictStats = raw.query(`
61
+ const conflictStats = rawGet<ConflictStatsRow>(`
51
62
  SELECT
52
63
  SUM(CASE WHEN status = 'pending_clarification' THEN 1 ELSE 0 END) AS pending_count,
53
64
  SUM(CASE WHEN status != 'pending_clarification' THEN 1 ELSE 0 END) AS resolved_count,
54
65
  MIN(CASE WHEN status = 'pending_clarification' THEN created_at END) AS oldest_pending_created_at
55
66
  FROM memory_item_conflicts
56
- `).get() as {
57
- pending_count: number | null;
58
- resolved_count: number | null;
59
- oldest_pending_created_at: number | null;
60
- } | null;
67
+ `);
61
68
  const pending = conflictStats?.pending_count ?? 0;
62
69
  const oldestPendingCreatedAt = conflictStats?.oldest_pending_created_at ?? null;
63
- const oldestPendingAgeMs = oldestPendingCreatedAt === null
70
+ const oldestPendingAgeMs = oldestPendingCreatedAt == null
64
71
  ? null
65
72
  : Math.max(0, Date.now() - oldestPendingCreatedAt);
66
73
  const throughputWindowStartMs = Date.now() - (24 * 60 * 60 * 1000);
67
- const cleanupStats = raw.query(`
74
+ const cleanupStats = rawGet<CleanupStatsRow>(`
68
75
  SELECT
69
76
  SUM(CASE
70
77
  WHEN type = 'cleanup_resolved_conflicts' AND status IN ('pending', 'running')
@@ -83,12 +90,7 @@ export function getMemoryConflictAndCleanupStats(): MemoryConflictAndCleanupStat
83
90
  THEN 1 ELSE 0 END
84
91
  ) AS superseded_completed_24h
85
92
  FROM memory_jobs
86
- `).get(throughputWindowStartMs, throughputWindowStartMs) as {
87
- resolved_backlog: number | null;
88
- superseded_backlog: number | null;
89
- resolved_completed_24h: number | null;
90
- superseded_completed_24h: number | null;
91
- } | null;
93
+ `, throughputWindowStartMs, throughputWindowStartMs);
92
94
  return {
93
95
  conflicts: {
94
96
  pending,
@@ -107,32 +109,26 @@ export function getMemoryConflictAndCleanupStats(): MemoryConflictAndCleanupStat
107
109
  export function getMemorySystemStatus(): MemorySystemStatus {
108
110
  const config = getConfig();
109
111
  const backend = getMemoryBackendStatus(config);
110
- const db = getDb();
111
- const raw = (db as unknown as { $client: { query: (q: string) => { get: (...args: unknown[]) => unknown } } }).$client;
112
112
  const counts = {
113
113
  segments: countTable('memory_segments'),
114
114
  items: countTable('memory_items'),
115
115
  summaries: countTable('memory_summaries'),
116
116
  embeddings: countTable('memory_embeddings'),
117
117
  };
118
- const conflictStats = raw.query(`
118
+ const conflictStats = rawGet<ConflictStatsRow>(`
119
119
  SELECT
120
120
  SUM(CASE WHEN status = 'pending_clarification' THEN 1 ELSE 0 END) AS pending_count,
121
121
  SUM(CASE WHEN status != 'pending_clarification' THEN 1 ELSE 0 END) AS resolved_count,
122
122
  MIN(CASE WHEN status = 'pending_clarification' THEN created_at END) AS oldest_pending_created_at
123
123
  FROM memory_item_conflicts
124
- `).get() as {
125
- pending_count: number | null;
126
- resolved_count: number | null;
127
- oldest_pending_created_at: number | null;
128
- } | null;
124
+ `);
129
125
  const pending = conflictStats?.pending_count ?? 0;
130
126
  const oldestPendingCreatedAt = conflictStats?.oldest_pending_created_at ?? null;
131
- const oldestPendingAgeMs = oldestPendingCreatedAt === null
127
+ const oldestPendingAgeMs = oldestPendingCreatedAt == null
132
128
  ? null
133
129
  : Math.max(0, Date.now() - oldestPendingCreatedAt);
134
130
  const throughputWindowStartMs = Date.now() - (24 * 60 * 60 * 1000);
135
- const cleanupStats = raw.query(`
131
+ const cleanupStats = rawGet<CleanupStatsRow>(`
136
132
  SELECT
137
133
  SUM(CASE
138
134
  WHEN type = 'cleanup_resolved_conflicts' AND status IN ('pending', 'running')
@@ -151,12 +147,7 @@ export function getMemorySystemStatus(): MemorySystemStatus {
151
147
  THEN 1 ELSE 0 END
152
148
  ) AS superseded_completed_24h
153
149
  FROM memory_jobs
154
- `).get(throughputWindowStartMs, throughputWindowStartMs) as {
155
- resolved_backlog: number | null;
156
- superseded_backlog: number | null;
157
- resolved_completed_24h: number | null;
158
- superseded_completed_24h: number | null;
159
- } | null;
150
+ `, throughputWindowStartMs, throughputWindowStartMs);
160
151
  return {
161
152
  enabled: backend.enabled,
162
153
  degraded: backend.degraded,
@@ -179,8 +170,7 @@ export function getMemorySystemStatus(): MemorySystemStatus {
179
170
  };
180
171
 
181
172
  function countTable(table: string): number {
182
- const row = raw.query(`SELECT COUNT(*) AS c FROM ${table}`).get() as { c: number } | null;
183
- return row?.c ?? 0;
173
+ return rawGet<{ c: number }>(`SELECT COUNT(*) AS c FROM ${table}`)?.c ?? 0;
184
174
  }
185
175
  }
186
176
 
@@ -7,7 +7,7 @@
7
7
 
8
8
  import { eq } from 'drizzle-orm';
9
9
  import { v4 as uuid } from 'uuid';
10
- import { getDb } from './db.js';
10
+ import { getDb, rawRun } from './db.js';
11
11
  import { attachments, messageAttachments } from './schema.js';
12
12
 
13
13
  export interface StoredAttachment {
@@ -322,7 +322,7 @@ export function getAttachmentsForMessage(
322
322
 
323
323
  if (links.length === 0) return [];
324
324
 
325
- const ids = links.map((l) => l.attachmentId).filter((id): id is string => id !== null);
325
+ const ids = links.map((l) => l.attachmentId).filter((id): id is string => id != null);
326
326
  return getAttachmentsByIds(ids);
327
327
  }
328
328
 
@@ -386,12 +386,9 @@ export function getAttachmentById(
386
386
  */
387
387
  export function deleteOrphanAttachments(candidateIds: string[]): number {
388
388
  if (candidateIds.length === 0) return 0;
389
- const db = getDb();
390
- const raw = (db as unknown as { $client: import('bun:sqlite').Database }).$client;
391
389
  const placeholders = candidateIds.map(() => '?').join(', ');
392
- const stmt = raw.prepare(
390
+ return rawRun(
393
391
  `DELETE FROM attachments WHERE id IN (${placeholders}) AND id NOT IN (SELECT attachment_id FROM message_attachments)`,
392
+ ...candidateIds,
394
393
  );
395
- const result = stmt.run(...candidateIds);
396
- return result.changes;
397
394
  }
@@ -11,7 +11,7 @@
11
11
  * and a replay endpoint allows manual recovery of dead-lettered ones.
12
12
  */
13
13
 
14
- import { eq, and, lte, isNotNull } from 'drizzle-orm';
14
+ import { eq, and, desc, lte, isNotNull } from 'drizzle-orm';
15
15
  import { v4 as uuid } from 'uuid';
16
16
  import { getDb } from './db.js';
17
17
  import { channelInboundEvents, conversations } from './schema.js';
@@ -229,6 +229,35 @@ export function clearPayload(eventId: string): void {
229
229
  .run();
230
230
  }
231
231
 
232
+ /**
233
+ * Retrieve the stored raw payload for a given conversation's most recent
234
+ * inbound event. Used by the escalation decide flow to recover the
235
+ * original message content after an approve/deny decision.
236
+ */
237
+ export function getLatestStoredPayload(conversationId: string): Record<string, unknown> | null {
238
+ const db = getDb();
239
+ const row = db
240
+ .select({
241
+ rawPayload: channelInboundEvents.rawPayload,
242
+ })
243
+ .from(channelInboundEvents)
244
+ .where(
245
+ and(
246
+ eq(channelInboundEvents.conversationId, conversationId),
247
+ isNotNull(channelInboundEvents.rawPayload),
248
+ ),
249
+ )
250
+ .orderBy(desc(channelInboundEvents.createdAt))
251
+ .get();
252
+
253
+ if (!row?.rawPayload) return null;
254
+ try {
255
+ return JSON.parse(row.rawPayload) as Record<string, unknown>;
256
+ } catch {
257
+ return null;
258
+ }
259
+ }
260
+
232
261
  /** Mark an event as successfully processed. */
233
262
  export function markProcessed(eventId: string): void {
234
263
  const db = getDb();
@@ -8,7 +8,7 @@
8
8
  * requests track per-run guardian approval decisions.
9
9
  */
10
10
 
11
- import { and, desc, eq, gt, lte } from 'drizzle-orm';
11
+ import { and, count, desc, eq, gt, lte } from 'drizzle-orm';
12
12
  import { v4 as uuid } from 'uuid';
13
13
  import { getDb } from './db.js';
14
14
  import {
@@ -257,6 +257,20 @@ export function createChallenge(params: {
257
257
  return rowToChallenge(row);
258
258
  }
259
259
 
260
+ export function revokePendingChallenges(assistantId: string, channel: string): void {
261
+ const db = getDb();
262
+ db.update(channelGuardianVerificationChallenges)
263
+ .set({ status: 'revoked', updatedAt: Date.now() })
264
+ .where(
265
+ and(
266
+ eq(channelGuardianVerificationChallenges.assistantId, assistantId),
267
+ eq(channelGuardianVerificationChallenges.channel, channel),
268
+ eq(channelGuardianVerificationChallenges.status, 'pending'),
269
+ ),
270
+ )
271
+ .run();
272
+ }
273
+
260
274
  export function findPendingChallengeByHash(
261
275
  assistantId: string,
262
276
  channel: string,
@@ -282,6 +296,33 @@ export function findPendingChallengeByHash(
282
296
  return row ? rowToChallenge(row) : null;
283
297
  }
284
298
 
299
+ /**
300
+ * Find any pending (non-expired) challenge for a given (assistantId, channel).
301
+ * Used by relay setup to detect whether a voice verification session is active.
302
+ */
303
+ export function findPendingChallengeForChannel(
304
+ assistantId: string,
305
+ channel: string,
306
+ ): VerificationChallenge | null {
307
+ const db = getDb();
308
+ const now = Date.now();
309
+
310
+ const row = db
311
+ .select()
312
+ .from(channelGuardianVerificationChallenges)
313
+ .where(
314
+ and(
315
+ eq(channelGuardianVerificationChallenges.assistantId, assistantId),
316
+ eq(channelGuardianVerificationChallenges.channel, channel),
317
+ eq(channelGuardianVerificationChallenges.status, 'pending'),
318
+ gt(channelGuardianVerificationChallenges.expiresAt, now),
319
+ ),
320
+ )
321
+ .get();
322
+
323
+ return row ? rowToChallenge(row) : null;
324
+ }
325
+
285
326
  export function consumeChallenge(
286
327
  id: string,
287
328
  consumedByExternalUserId: string,
@@ -539,6 +580,164 @@ export function updateApprovalDecision(
539
580
  .run();
540
581
  }
541
582
 
583
+ // ---------------------------------------------------------------------------
584
+ // Inbox / Escalation Query Helpers
585
+ // ---------------------------------------------------------------------------
586
+
587
+ /**
588
+ * List approval requests filtered by assistant, and optionally by channel,
589
+ * conversation, and status. Designed for the inbox UI to show a paginated
590
+ * list of escalations.
591
+ */
592
+ export function listPendingApprovalRequests(params: {
593
+ assistantId?: string;
594
+ channel?: string;
595
+ conversationId?: string;
596
+ status?: ApprovalRequestStatus;
597
+ limit?: number;
598
+ offset?: number;
599
+ }): GuardianApprovalRequest[] {
600
+ const db = getDb();
601
+
602
+ const conditions = [
603
+ eq(channelGuardianApprovalRequests.assistantId, params.assistantId ?? 'self'),
604
+ ];
605
+ if (params.channel) {
606
+ conditions.push(eq(channelGuardianApprovalRequests.channel, params.channel));
607
+ }
608
+ if (params.conversationId) {
609
+ conditions.push(eq(channelGuardianApprovalRequests.conversationId, params.conversationId));
610
+ }
611
+ conditions.push(
612
+ eq(channelGuardianApprovalRequests.status, params.status ?? 'pending'),
613
+ );
614
+
615
+ let query = db
616
+ .select()
617
+ .from(channelGuardianApprovalRequests)
618
+ .where(and(...conditions))
619
+ .orderBy(desc(channelGuardianApprovalRequests.createdAt));
620
+
621
+ if (params.limit !== undefined) {
622
+ query = query.limit(params.limit) as typeof query;
623
+ }
624
+ if (params.offset !== undefined) {
625
+ query = query.offset(params.offset) as typeof query;
626
+ }
627
+
628
+ return query.all().map(rowToApprovalRequest);
629
+ }
630
+
631
+ /**
632
+ * Fetch a single approval request by its primary key.
633
+ */
634
+ export function getApprovalRequestById(id: string): GuardianApprovalRequest | null {
635
+ const db = getDb();
636
+
637
+ const row = db
638
+ .select()
639
+ .from(channelGuardianApprovalRequests)
640
+ .where(eq(channelGuardianApprovalRequests.id, id))
641
+ .get();
642
+
643
+ return row ? rowToApprovalRequest(row) : null;
644
+ }
645
+
646
+ /**
647
+ * Fetch a single approval request by run ID (any status).
648
+ * Useful for checking whether a run has an associated approval request.
649
+ */
650
+ export function getApprovalRequestByRunId(runId: string): GuardianApprovalRequest | null {
651
+ const db = getDb();
652
+
653
+ const row = db
654
+ .select()
655
+ .from(channelGuardianApprovalRequests)
656
+ .where(eq(channelGuardianApprovalRequests.runId, runId))
657
+ .orderBy(desc(channelGuardianApprovalRequests.createdAt))
658
+ .get();
659
+
660
+ return row ? rowToApprovalRequest(row) : null;
661
+ }
662
+
663
+ /**
664
+ * Resolve a pending approval request with a decision.
665
+ *
666
+ * Idempotent: if the request is already resolved with the same decision,
667
+ * the existing record is returned unchanged. Returns null if the request
668
+ * does not exist or was resolved with a *different* decision.
669
+ */
670
+ export function resolveApprovalRequest(
671
+ id: string,
672
+ decision: 'approved' | 'denied',
673
+ decidedByExternalUserId?: string,
674
+ ): GuardianApprovalRequest | null {
675
+ const db = getDb();
676
+
677
+ const existing = db
678
+ .select()
679
+ .from(channelGuardianApprovalRequests)
680
+ .where(eq(channelGuardianApprovalRequests.id, id))
681
+ .get();
682
+
683
+ if (!existing) return null;
684
+
685
+ // Idempotent: already resolved with the same decision
686
+ if (existing.status === decision) {
687
+ return rowToApprovalRequest(existing);
688
+ }
689
+
690
+ // Only resolve if currently pending
691
+ if (existing.status !== 'pending') {
692
+ return null;
693
+ }
694
+
695
+ const now = Date.now();
696
+
697
+ db.update(channelGuardianApprovalRequests)
698
+ .set({
699
+ status: decision,
700
+ decidedByExternalUserId: decidedByExternalUserId ?? null,
701
+ updatedAt: now,
702
+ })
703
+ .where(eq(channelGuardianApprovalRequests.id, id))
704
+ .run();
705
+
706
+ return rowToApprovalRequest({
707
+ ...existing,
708
+ status: decision,
709
+ decidedByExternalUserId: decidedByExternalUserId ?? null,
710
+ updatedAt: now,
711
+ });
712
+ }
713
+
714
+ /**
715
+ * Count pending approval requests for a given conversation.
716
+ * Used by thread state projection to compute `pending_escalation_count`.
717
+ */
718
+ export function countPendingByConversation(
719
+ conversationId: string,
720
+ assistantId?: string,
721
+ ): number {
722
+ const db = getDb();
723
+
724
+ const conditions = [
725
+ eq(channelGuardianApprovalRequests.conversationId, conversationId),
726
+ eq(channelGuardianApprovalRequests.status, 'pending'),
727
+ ];
728
+ if (assistantId) {
729
+ conditions.push(eq(channelGuardianApprovalRequests.assistantId, assistantId));
730
+ }
731
+
732
+ const result = db
733
+ .select({ count: count() })
734
+ .from(channelGuardianApprovalRequests)
735
+ .where(and(...conditions))
736
+ .get();
737
+
738
+ return result?.count ?? 0;
739
+ }
740
+
542
741
  // ---------------------------------------------------------------------------
543
742
  // Verification Rate Limits
544
743
  // ---------------------------------------------------------------------------
@@ -1,8 +1,8 @@
1
- import Anthropic from '@anthropic-ai/sdk';
2
- import { getConfig } from '../config/loader.js';
1
+ import { getConfiguredProvider, createTimeout, extractToolUse, userMessage } from '../providers/provider-send-message.js';
2
+ import type { ModelIntent } from '../providers/types.js';
3
3
  import { truncate } from '../util/truncate.js';
4
4
 
5
- const DEFAULT_RESOLVER_MODEL = 'claude-haiku-4-5-20251001';
5
+ const DEFAULT_RESOLVER_MODEL_INTENT: ModelIntent = 'latency-optimized';
6
6
  const DEFAULT_RESOLVER_TIMEOUT_MS = 12_000;
7
7
 
8
8
  const DIRECTIONAL_EXISTING_CUES = ['existing', 'old', 'previous', 'first', 'earlier', 'original'];
@@ -38,6 +38,7 @@ export interface ClarificationResolverInput {
38
38
  export interface ClarificationResolverOptions {
39
39
  apiKey?: string;
40
40
  model?: string;
41
+ modelIntent?: ModelIntent;
41
42
  timeoutMs?: number;
42
43
  }
43
44
 
@@ -55,21 +56,20 @@ export async function resolveConflictClarification(
55
56
  const heuristicResult = resolveWithHeuristics(input);
56
57
  if (heuristicResult) return heuristicResult;
57
58
 
58
- const config = getConfig();
59
- const apiKey = options?.apiKey ?? config.apiKeys.anthropic ?? process.env.ANTHROPIC_API_KEY;
60
- if (!apiKey) {
59
+ const provider = getConfiguredProvider();
60
+ if (!provider) {
61
61
  return {
62
62
  resolution: 'still_unclear',
63
63
  strategy: 'no_llm_key',
64
64
  resolvedStatement: null,
65
- explanation: 'No Anthropic API key available for clarification fallback.',
65
+ explanation: 'Configured provider unavailable for clarification fallback.',
66
66
  };
67
67
  }
68
68
 
69
69
  try {
70
70
  return await resolveWithLlm(input, {
71
- apiKey,
72
- model: options?.model ?? DEFAULT_RESOLVER_MODEL,
71
+ model: options?.model,
72
+ modelIntent: options?.modelIntent ?? DEFAULT_RESOLVER_MODEL_INTENT,
73
73
  timeoutMs: options?.timeoutMs ?? DEFAULT_RESOLVER_TIMEOUT_MS,
74
74
  });
75
75
  } catch (err) {
@@ -168,9 +168,9 @@ function resolveWithHeuristics(input: ClarificationResolverInput): Clarification
168
168
 
169
169
  async function resolveWithLlm(
170
170
  input: ClarificationResolverInput,
171
- options: { apiKey: string; model: string; timeoutMs: number },
171
+ options: { model?: string; modelIntent: ModelIntent; timeoutMs: number },
172
172
  ): Promise<ClarificationResolverResult> {
173
- const client = new Anthropic({ apiKey: options.apiKey });
173
+ const provider = getConfiguredProvider()!;
174
174
  const userPrompt = [
175
175
  'You are resolving a memory clarification response.',
176
176
  '',
@@ -179,22 +179,12 @@ async function resolveWithLlm(
179
179
  `User clarification: ${input.userMessage}`,
180
180
  ].join('\n');
181
181
 
182
- const abortController = new AbortController();
183
- const timer = setTimeout(() => abortController.abort(), options.timeoutMs);
182
+ const { signal, cleanup } = createTimeout(options.timeoutMs);
184
183
 
185
184
  try {
186
- const response = await client.messages.create({
187
- model: options.model,
188
- max_tokens: 256,
189
- system: [
190
- 'Classify the user clarification for conflicting memory statements.',
191
- 'Return exactly one resolution:',
192
- '- keep_existing',
193
- '- keep_candidate',
194
- '- merge',
195
- '- still_unclear',
196
- ].join('\n'),
197
- tools: [{
185
+ const response = await provider.sendMessage(
186
+ [userMessage(userPrompt)],
187
+ [{
198
188
  name: 'resolve_conflict_clarification',
199
189
  description: 'Resolve a pending memory contradiction using user clarification.',
200
190
  input_schema: {
@@ -216,13 +206,27 @@ async function resolveWithLlm(
216
206
  required: ['resolution', 'explanation'],
217
207
  },
218
208
  }],
219
- tool_choice: { type: 'tool' as const, name: 'resolve_conflict_clarification' },
220
- messages: [{ role: 'user' as const, content: userPrompt }],
221
- }, { signal: abortController.signal });
222
- clearTimeout(timer);
209
+ [
210
+ 'Classify the user clarification for conflicting memory statements.',
211
+ 'Return exactly one resolution:',
212
+ '- keep_existing',
213
+ '- keep_candidate',
214
+ '- merge',
215
+ '- still_unclear',
216
+ ].join('\n'),
217
+ {
218
+ config: {
219
+ ...(options.model ? { model: options.model } : { modelIntent: options.modelIntent }),
220
+ max_tokens: 256,
221
+ tool_choice: { type: 'tool' as const, name: 'resolve_conflict_clarification' },
222
+ },
223
+ signal,
224
+ },
225
+ );
226
+ cleanup();
223
227
 
224
- const toolBlock = response.content.find((block) => block.type === 'tool_use');
225
- if (!toolBlock || toolBlock.type !== 'tool_use') {
228
+ const toolBlock = extractToolUse(response);
229
+ if (!toolBlock) {
226
230
  throw new Error('No tool_use block in clarification resolver response.');
227
231
  }
228
232
 
@@ -247,8 +251,8 @@ async function resolveWithLlm(
247
251
  explanation: truncate(normalize(parsed.explanation ?? 'Resolved via LLM fallback.'), 500, ''),
248
252
  };
249
253
  } catch (err) {
250
- clearTimeout(timer);
251
- if (abortController.signal.aborted) {
254
+ cleanup();
255
+ if (signal.aborted) {
252
256
  throw new Error('clarification_resolver_timeout');
253
257
  }
254
258
  throw err;