@vellumai/assistant 0.3.5 → 0.3.6

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 (486) 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 +26 -2
  164. package/src/config/env-registry.ts +162 -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 +156 -1137
  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 +217 -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 +54 -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 +315 -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 +60 -0
  236. package/src/daemon/ipc-contract-inventory.ts +55 -29
  237. package/src/daemon/ipc-contract.ts +226 -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 +96 -4
  254. package/src/daemon/session-runtime-assembly.ts +149 -15
  255. package/src/daemon/session-surfaces.ts +19 -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 +10 -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 +160 -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 +119 -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/channel-approval-parser.ts +36 -2
  377. package/src/runtime/channel-approvals.ts +0 -21
  378. package/src/runtime/channel-guardian-service.ts +48 -7
  379. package/src/runtime/channel-readiness-service.ts +160 -34
  380. package/src/runtime/channel-readiness-types.ts +10 -4
  381. package/src/runtime/channel-retry-sweep.ts +184 -0
  382. package/src/runtime/guardian-context-resolver.ts +108 -0
  383. package/src/runtime/http-server.ts +275 -743
  384. package/src/runtime/http-types.ts +56 -3
  385. package/src/runtime/middleware/auth.ts +116 -0
  386. package/src/runtime/middleware/error-handler.ts +33 -0
  387. package/src/runtime/middleware/twilio-validation.ts +127 -0
  388. package/src/runtime/routes/app-routes.ts +1 -1
  389. package/src/runtime/routes/call-routes.ts +49 -6
  390. package/src/runtime/routes/channel-delivery-routes.ts +170 -0
  391. package/src/runtime/routes/channel-guardian-routes.ts +1191 -0
  392. package/src/runtime/routes/channel-inbound-routes.ts +1152 -0
  393. package/src/runtime/routes/channel-route-shared.ts +144 -0
  394. package/src/runtime/routes/channel-routes.ts +32 -1634
  395. package/src/runtime/routes/conversation-routes.ts +50 -7
  396. package/src/runtime/routes/events-routes.ts +2 -2
  397. package/src/runtime/routes/identity-routes.ts +126 -0
  398. package/src/runtime/routes/pairing-routes.ts +143 -0
  399. package/src/runtime/routes/run-routes.ts +15 -1
  400. package/src/runtime/run-orchestrator.ts +52 -34
  401. package/src/schedule/schedule-store.ts +36 -32
  402. package/src/schedule/scheduler.ts +3 -3
  403. package/src/security/encrypted-store.ts +5 -7
  404. package/src/security/oauth2.ts +45 -15
  405. package/src/security/parental-control-store.ts +183 -0
  406. package/src/security/secret-allowlist.ts +4 -3
  407. package/src/security/secret-scanner.ts +5 -5
  408. package/src/security/secure-keys.ts +1 -1
  409. package/src/security/token-manager.ts +3 -2
  410. package/src/services/vercel-deploy.ts +6 -2
  411. package/src/skills/tool-manifest.ts +3 -3
  412. package/src/skills/vellum-catalog-remote.ts +75 -16
  413. package/src/slack/slack-webhook.ts +2 -1
  414. package/src/swarm/orchestrator.ts +92 -1
  415. package/src/swarm/router-planner.ts +6 -9
  416. package/src/swarm/worker-prompts.ts +9 -12
  417. package/src/tasks/task-compiler.ts +19 -28
  418. package/src/tasks/task-runner.ts +1 -1
  419. package/src/tools/assets/search.ts +15 -14
  420. package/src/tools/browser/__tests__/auth-detector.test.ts +1 -0
  421. package/src/tools/browser/auto-navigate.ts +1 -0
  422. package/src/tools/browser/browser-execution.ts +10 -1
  423. package/src/tools/browser/browser-manager.ts +119 -4
  424. package/src/tools/browser/network-recorder.ts +5 -0
  425. package/src/tools/credentials/broker.ts +11 -2
  426. package/src/tools/credentials/metadata-store.ts +18 -14
  427. package/src/tools/credentials/post-connect-hooks.ts +61 -0
  428. package/src/tools/credentials/vault.ts +49 -23
  429. package/src/tools/executor.ts +68 -9
  430. package/src/tools/host-terminal/cli-discover.ts +1 -1
  431. package/src/tools/network/script-proxy/http-forwarder.ts +1 -1
  432. package/src/tools/network/script-proxy/mitm-handler.ts +1 -1
  433. package/src/tools/network/script-proxy/server.ts +1 -1
  434. package/src/tools/network/script-proxy/session-manager.ts +6 -5
  435. package/src/tools/network/web-fetch.ts +18 -2
  436. package/src/tools/network/web-search.ts +7 -3
  437. package/src/tools/reminder/reminder-store.ts +14 -15
  438. package/src/tools/schedule/create.ts +1 -0
  439. package/src/tools/schedule/list.ts +2 -1
  440. package/src/tools/shared/filesystem/file-ops-service.ts +5 -7
  441. package/src/tools/skills/skill-script-runner.ts +24 -9
  442. package/src/tools/skills/skill-tool-factory.ts +1 -0
  443. package/src/tools/tasks/work-item-enqueue.ts +2 -2
  444. package/src/tools/terminal/evaluate-typescript.ts +21 -12
  445. package/src/tools/terminal/parser.ts +50 -0
  446. package/src/tools/watcher/delete.ts +6 -0
  447. package/src/tools/weather/service.ts +1 -1
  448. package/src/twitter/client.ts +190 -24
  449. package/src/twitter/session.ts +4 -3
  450. package/src/util/clipboard.ts +1 -1
  451. package/src/util/errors.ts +65 -8
  452. package/src/util/fs.ts +40 -0
  453. package/src/util/json.ts +10 -0
  454. package/src/util/log-redact.ts +189 -0
  455. package/src/util/logger.ts +19 -17
  456. package/src/util/object.ts +3 -0
  457. package/src/util/platform.ts +72 -365
  458. package/src/util/pricing.ts +1 -1
  459. package/src/util/promise-guard.ts +1 -1
  460. package/src/util/retry.ts +19 -0
  461. package/src/util/row-mapper.ts +79 -0
  462. package/src/util/silently.ts +21 -0
  463. package/src/watcher/engine.ts +5 -1
  464. package/src/watcher/provider-types.ts +20 -0
  465. package/src/watcher/providers/github.ts +156 -0
  466. package/src/watcher/providers/gmail.ts +1 -0
  467. package/src/watcher/providers/google-calendar.ts +1 -0
  468. package/src/watcher/providers/linear.ts +460 -0
  469. package/src/watcher/providers/slack.ts +1 -0
  470. package/src/work-items/work-item-runner.ts +1 -1
  471. package/src/workspace/git-service.ts +1 -1
  472. package/src/workspace/provider-commit-message-generator.ts +51 -22
  473. package/src/__tests__/call-bridge.test.ts +0 -517
  474. package/src/__tests__/session-process-bridge.test.ts +0 -244
  475. package/src/calls/call-bridge.ts +0 -168
  476. package/src/config/bundled-skills/media-processing/services/capability-registry.ts +0 -137
  477. package/src/config/bundled-skills/media-processing/services/event-detection-service.ts +0 -280
  478. package/src/config/bundled-skills/media-processing/services/feedback-aggregation.ts +0 -144
  479. package/src/config/bundled-skills/media-processing/services/feedback-store.ts +0 -136
  480. package/src/config/bundled-skills/media-processing/services/retrieval-service.ts +0 -95
  481. package/src/config/bundled-skills/media-processing/services/timeline-service.ts +0 -267
  482. package/src/config/bundled-skills/media-processing/tools/detect-events.ts +0 -110
  483. package/src/config/bundled-skills/media-processing/tools/recalibrate.ts +0 -235
  484. package/src/config/bundled-skills/media-processing/tools/select-tracking-profile.ts +0 -142
  485. package/src/config/bundled-skills/media-processing/tools/submit-feedback.ts +0 -150
  486. package/src/config/vellum-skills/google-oauth-setup/SKILL.md +0 -199
@@ -1,6 +1,7 @@
1
- import { eq } from 'drizzle-orm';
1
+ import { inArray } from 'drizzle-orm';
2
2
  import { getDb } from '../db.js';
3
3
  import { getQdrantClient } from '../qdrant-client.js';
4
+ import type { QdrantSearchResult } from '../qdrant-client.js';
4
5
  import {
5
6
  memoryItems,
6
7
  memoryItemSources,
@@ -9,6 +10,84 @@ import {
9
10
  } from '../schema.js';
10
11
  import type { Candidate } from './types.js';
11
12
  import { computeRecencyScore } from './ranking.js';
13
+ import { getLogger } from '../../util/logger.js';
14
+
15
+ const log = getLogger('qdrant-circuit-breaker');
16
+
17
+ // ── Qdrant circuit breaker ───────────────────────────────────────────
18
+ // After FAILURE_THRESHOLD consecutive Qdrant failures, stop sending
19
+ // requests (open state). After COOLDOWN_MS, allow a single probe
20
+ // request (half-open). If the probe succeeds, close the circuit; if it
21
+ // fails, re-open and restart the cooldown.
22
+
23
+ const FAILURE_THRESHOLD = 5;
24
+ const COOLDOWN_MS = 60_000;
25
+
26
+ type BreakerState = 'closed' | 'open' | 'half-open';
27
+
28
+ let breakerState: BreakerState = 'closed';
29
+ let consecutiveFailures = 0;
30
+ let openedAt = 0;
31
+ // Ensures only one request passes through during half-open state
32
+ let halfOpenProbeInFlight = false;
33
+
34
+ function qdrantBreakerAllows(): boolean {
35
+ if (breakerState === 'closed') return true;
36
+ if (breakerState === 'open') {
37
+ if (Date.now() - openedAt >= COOLDOWN_MS) {
38
+ breakerState = 'half-open';
39
+ halfOpenProbeInFlight = true;
40
+ log.info('Qdrant circuit breaker entering half-open state — allowing probe request');
41
+ return true;
42
+ }
43
+ return false;
44
+ }
45
+ // half-open: only allow through if no probe is already in flight
46
+ if (halfOpenProbeInFlight) return false;
47
+ halfOpenProbeInFlight = true;
48
+ return true;
49
+ }
50
+
51
+ function qdrantBreakerRecordSuccess(): void {
52
+ if (breakerState !== 'closed') {
53
+ log.info({ previousFailures: consecutiveFailures }, 'Qdrant circuit breaker closed — search succeeded');
54
+ }
55
+ consecutiveFailures = 0;
56
+ breakerState = 'closed';
57
+ openedAt = 0;
58
+ halfOpenProbeInFlight = false;
59
+ }
60
+
61
+ function qdrantBreakerRecordFailure(): void {
62
+ consecutiveFailures++;
63
+ halfOpenProbeInFlight = false;
64
+ if (consecutiveFailures >= FAILURE_THRESHOLD) {
65
+ breakerState = 'open';
66
+ openedAt = Date.now();
67
+ log.warn(
68
+ { consecutiveFailures, cooldownMs: COOLDOWN_MS },
69
+ 'Qdrant circuit breaker opened — semantic search disabled until probe succeeds',
70
+ );
71
+ } else if (breakerState === 'half-open') {
72
+ // Probe failed — re-open
73
+ breakerState = 'open';
74
+ openedAt = Date.now();
75
+ log.warn('Qdrant circuit breaker re-opened — half-open probe failed');
76
+ }
77
+ }
78
+
79
+ /** @internal Test-only: reset circuit breaker state */
80
+ export function _resetQdrantBreaker(): void {
81
+ breakerState = 'closed';
82
+ consecutiveFailures = 0;
83
+ openedAt = 0;
84
+ halfOpenProbeInFlight = false;
85
+ }
86
+
87
+ /** @internal Test-only: get breaker state */
88
+ export function _getQdrantBreakerState(): { state: BreakerState; consecutiveFailures: number } {
89
+ return { state: breakerState, consecutiveFailures };
90
+ }
12
91
 
13
92
  export async function semanticSearch(
14
93
  queryVector: number[],
@@ -20,18 +99,79 @@ export async function semanticSearch(
20
99
  ): Promise<Candidate[]> {
21
100
  if (limit <= 0) return [];
22
101
 
102
+ // Circuit breaker: throw so the caller's .catch() marks the result as degraded
103
+ if (!qdrantBreakerAllows()) {
104
+ log.debug('Qdrant circuit breaker open — skipping semantic search');
105
+ throw new Error('Qdrant circuit breaker open');
106
+ }
107
+
23
108
  const qdrant = getQdrantClient();
24
109
 
25
110
  // Overfetch to account for items filtered out post-query (invalidated, excluded, etc.)
26
- const fetchLimit = limit * 2;
27
- const results = await qdrant.searchWithFilter(
28
- queryVector,
29
- fetchLimit,
30
- ['item', 'summary', 'segment'],
31
- excludedMessageIds,
32
- );
111
+ // Use 3x when exclusions are active to ensure enough results survive filtering
112
+ const overfetchMultiplier = excludedMessageIds.length > 0 ? 3 : 2;
113
+ const fetchLimit = limit * overfetchMultiplier;
114
+ let results: QdrantSearchResult[];
115
+ try {
116
+ results = await qdrant.searchWithFilter(
117
+ queryVector,
118
+ fetchLimit,
119
+ ['item', 'summary', 'segment'],
120
+ excludedMessageIds,
121
+ );
122
+ qdrantBreakerRecordSuccess();
123
+ } catch (err) {
124
+ qdrantBreakerRecordFailure();
125
+ throw err;
126
+ }
33
127
 
34
128
  const db = getDb();
129
+
130
+ // Batch-fetch all backing records upfront to avoid N+1 queries per result
131
+ const itemTargetIds: string[] = [];
132
+ const summaryTargetIds: string[] = [];
133
+ const segmentTargetIds: string[] = [];
134
+ for (const r of results) {
135
+ if (r.payload.target_type === 'item') itemTargetIds.push(r.payload.target_id);
136
+ else if (r.payload.target_type === 'summary') summaryTargetIds.push(r.payload.target_id);
137
+ else segmentTargetIds.push(r.payload.target_id);
138
+ }
139
+
140
+ const itemsMap = new Map<string, typeof memoryItems.$inferSelect>();
141
+ if (itemTargetIds.length > 0) {
142
+ const allItems = db.select().from(memoryItems).where(inArray(memoryItems.id, itemTargetIds)).all();
143
+ for (const item of allItems) itemsMap.set(item.id, item);
144
+ }
145
+
146
+ const sourcesMap = new Map<string, string[]>();
147
+ if (itemTargetIds.length > 0) {
148
+ const allSources = db.select({
149
+ memoryItemId: memoryItemSources.memoryItemId,
150
+ messageId: memoryItemSources.messageId,
151
+ }).from(memoryItemSources)
152
+ .where(inArray(memoryItemSources.memoryItemId, itemTargetIds))
153
+ .all();
154
+ for (const s of allSources) {
155
+ const existing = sourcesMap.get(s.memoryItemId);
156
+ if (existing) existing.push(s.messageId);
157
+ else sourcesMap.set(s.memoryItemId, [s.messageId]);
158
+ }
159
+ }
160
+
161
+ const summariesMap = new Map<string, typeof memorySummaries.$inferSelect>();
162
+ if (scopeIds && summaryTargetIds.length > 0) {
163
+ const allSummaries = db.select().from(memorySummaries).where(inArray(memorySummaries.id, summaryTargetIds)).all();
164
+ for (const s of allSummaries) summariesMap.set(s.id, s);
165
+ }
166
+
167
+ const segmentsMap = new Map<string, typeof memorySegments.$inferSelect>();
168
+ if (scopeIds && segmentTargetIds.length > 0) {
169
+ const allSegments = db.select().from(memorySegments).where(inArray(memorySegments.id, segmentTargetIds)).all();
170
+ for (const seg of allSegments) segmentsMap.set(seg.id, seg);
171
+ }
172
+
173
+ const excludedSet = excludedMessageIds.length > 0 ? new Set(excludedMessageIds) : null;
174
+
35
175
  const candidates: Candidate[] = [];
36
176
  for (const result of results) {
37
177
  const { payload, score } = result;
@@ -39,16 +179,14 @@ export async function semanticSearch(
39
179
  const createdAt = payload.created_at ?? Date.now();
40
180
 
41
181
  if (payload.target_type === 'item') {
42
- // Validate the backing memory item is still active and has non-excluded evidence
43
- const item = db.select().from(memoryItems).where(eq(memoryItems.id, payload.target_id)).get();
44
- if (!item || item.status !== 'active' || item.invalidAt !== null) continue;
182
+ const item = itemsMap.get(payload.target_id);
183
+ if (!item || item.status !== 'active' || item.invalidAt != null) continue;
45
184
  if (scopeIds && !scopeIds.includes(item.scopeId)) continue;
46
- const sources = db.select().from(memoryItemSources)
47
- .where(eq(memoryItemSources.memoryItemId, payload.target_id)).all();
48
- if (sources.length === 0) continue;
49
- if (excludedMessageIds.length > 0) {
50
- const nonExcluded = sources.filter((s) => !excludedMessageIds.includes(s.messageId));
51
- if (nonExcluded.length === 0) continue;
185
+ const sources = sourcesMap.get(payload.target_id);
186
+ if (!sources || sources.length === 0) continue;
187
+ if (excludedSet) {
188
+ const hasNonExcluded = sources.some((msgId) => !excludedSet.has(msgId));
189
+ if (!hasNonExcluded) continue;
52
190
  }
53
191
  candidates.push({
54
192
  key: `item:${payload.target_id}`,
@@ -67,7 +205,7 @@ export async function semanticSearch(
67
205
  });
68
206
  } else if (payload.target_type === 'summary') {
69
207
  if (scopeIds) {
70
- const summary = db.select().from(memorySummaries).where(eq(memorySummaries.id, payload.target_id)).get();
208
+ const summary = summariesMap.get(payload.target_id);
71
209
  if (!summary || !scopeIds.includes(summary.scopeId)) continue;
72
210
  }
73
211
  candidates.push({
@@ -87,7 +225,7 @@ export async function semanticSearch(
87
225
  });
88
226
  } else {
89
227
  if (scopeIds) {
90
- const segment = db.select().from(memorySegments).where(eq(memorySegments.id, payload.target_id)).get();
228
+ const segment = segmentsMap.get(payload.target_id);
91
229
  if (!segment || !scopeIds.includes(segment.scopeId)) continue;
92
230
  }
93
231
  candidates.push({
@@ -6,7 +6,7 @@
6
6
 
7
7
  import { eq, lte, or, and, isNull } from 'drizzle-orm';
8
8
  import { randomUUID, randomBytes } from 'node:crypto';
9
- import { getDb } from './db.js';
9
+ import { getDb, rawRun } from './db.js';
10
10
  import { sharedAppLinks } from './schema.js';
11
11
  import type { AppManifest } from '../bundler/manifest.js';
12
12
 
@@ -130,9 +130,8 @@ export function deleteSharedAppLinkByToken(shareToken: string): boolean {
130
130
  }
131
131
 
132
132
  export function incrementDownloadCount(shareToken: string): void {
133
- const db = getDb();
134
- const raw = (db as unknown as { $client: import('bun:sqlite').Database }).$client;
135
- raw.prepare(
133
+ rawRun(
136
134
  `UPDATE shared_app_links SET download_count = download_count + 1 WHERE share_token = ?`,
137
- ).run(shareToken);
135
+ shareToken,
136
+ );
138
137
  }
@@ -0,0 +1,19 @@
1
+ import { z } from 'zod';
2
+
3
+ /**
4
+ * Unit interval [0, 1] — used for confidence and importance fields on memory items.
5
+ * Coerces out-of-range numbers to the nearest bound rather than rejecting,
6
+ * since LLM-generated values occasionally exceed the range.
7
+ */
8
+ export const unitInterval = z.number().transform((v) => Math.min(1, Math.max(0, v)));
9
+
10
+ /** Zod schema for validating confidence/importance values on memory items. */
11
+ export const memoryItemScores = z.object({
12
+ confidence: unitInterval,
13
+ importance: unitInterval,
14
+ });
15
+
16
+ /** Clamp a numeric value to [0, 1]. */
17
+ export function clampUnitInterval(value: number): number {
18
+ return Math.min(1, Math.max(0, value));
19
+ }
@@ -5,8 +5,9 @@
5
5
  * Works across all platforms — Slack, Gmail, Discord, etc.
6
6
  */
7
7
 
8
- import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync, readdirSync } from 'node:fs';
8
+ import { readFileSync, writeFileSync, unlinkSync, readdirSync } from 'node:fs';
9
9
  import { join } from 'node:path';
10
+ import { ensureDir, pathExists } from '../util/fs.js';
10
11
  import { randomUUID } from 'node:crypto';
11
12
  import { getRootDir } from '../util/platform.js';
12
13
 
@@ -24,9 +25,7 @@ export interface Draft {
24
25
 
25
26
  function getDraftsDir(platform: string): string {
26
27
  const dir = join(getRootDir(), 'workspace', 'data', 'drafts', platform);
27
- if (!existsSync(dir)) {
28
- mkdirSync(dir, { recursive: true });
29
- }
28
+ ensureDir(dir);
30
29
  return dir;
31
30
  }
32
31
 
@@ -61,7 +60,7 @@ export function createDraft(opts: {
61
60
 
62
61
  export function getDraft(platform: string, id: string): Draft | null {
63
62
  const path = getDraftPath(platform, id);
64
- if (!existsSync(path)) return null;
63
+ if (!pathExists(path)) return null;
65
64
  return JSON.parse(readFileSync(path, 'utf-8')) as Draft;
66
65
  }
67
66
 
@@ -82,7 +81,7 @@ export function listDrafts(platform: string): Draft[] {
82
81
 
83
82
  export function deleteDraft(platform: string, id: string): boolean {
84
83
  const path = getDraftPath(platform, id);
85
- if (!existsSync(path)) return false;
84
+ if (!pathExists(path)) return false;
86
85
  unlinkSync(path);
87
86
  return true;
88
87
  }
@@ -29,17 +29,14 @@ import type {
29
29
  import { getSecureKey } from '../../../security/secure-keys.js';
30
30
  import { readHttpToken } from '../../../util/platform.js';
31
31
  import { loadConfig } from '../../../config/loader.js';
32
+ import { getGatewayInternalBaseUrl, getTwilioPhoneNumberEnv } from '../../../config/env.js';
32
33
  import { getOrCreateConversation } from '../../../memory/conversation-key-store.js';
33
34
  import * as externalConversationStore from '../../../memory/external-conversation-store.js';
34
35
  import * as sms from './client.js';
35
36
 
36
37
  /** Resolve the gateway base URL, preferring GATEWAY_INTERNAL_BASE_URL if set. */
37
38
  function getGatewayUrl(): string {
38
- if (process.env.GATEWAY_INTERNAL_BASE_URL) {
39
- return process.env.GATEWAY_INTERNAL_BASE_URL.replace(/\/+$/, '');
40
- }
41
- const port = Number(process.env.GATEWAY_PORT) || 7830;
42
- return `http://127.0.0.1:${port}`;
39
+ return getGatewayInternalBaseUrl();
43
40
  }
44
41
 
45
42
  /** Read the runtime HTTP bearer token used to authenticate with the gateway. */
@@ -75,7 +72,7 @@ function getPhoneNumber(assistantId?: string): string | undefined {
75
72
  }
76
73
  }
77
74
 
78
- const fromEnv = process.env.TWILIO_PHONE_NUMBER;
75
+ const fromEnv = getTwilioPhoneNumberEnv();
79
76
  if (fromEnv) return fromEnv;
80
77
 
81
78
  try {
@@ -25,17 +25,14 @@ import type {
25
25
  } from '../../provider-types.js';
26
26
  import { getSecureKey } from '../../../security/secure-keys.js';
27
27
  import { readHttpToken } from '../../../util/platform.js';
28
+ import { getGatewayInternalBaseUrl } from '../../../config/env.js';
28
29
  import { getOrCreateConversation } from '../../../memory/conversation-key-store.js';
29
30
  import * as externalConversationStore from '../../../memory/external-conversation-store.js';
30
31
  import * as telegram from './client.js';
31
32
 
32
33
  /** Resolve the gateway base URL, preferring GATEWAY_INTERNAL_BASE_URL if set. */
33
34
  function getGatewayUrl(): string {
34
- if (process.env.GATEWAY_INTERNAL_BASE_URL) {
35
- return process.env.GATEWAY_INTERNAL_BASE_URL.replace(/\/+$/, "");
36
- }
37
- const port = Number(process.env.GATEWAY_PORT) || 7830;
38
- return `http://127.0.0.1:${port}`;
35
+ return getGatewayInternalBaseUrl();
39
36
  }
40
37
 
41
38
  /** Read the runtime HTTP bearer token used to authenticate with the gateway. */
@@ -0,0 +1,136 @@
1
+ /**
2
+ * WhatsApp Business messaging provider adapter.
3
+ *
4
+ * Enables proactive outbound WhatsApp messaging via the gateway's /deliver/whatsapp
5
+ * endpoint. Delivery is proxied through the gateway which owns the Meta Cloud API
6
+ * credentials (phone_number_id + access_token).
7
+ *
8
+ * The `token` parameter in MessagingProvider methods is unused for WhatsApp
9
+ * because delivery is authenticated via the gateway's bearer token, not
10
+ * a per-user OAuth token.
11
+ */
12
+
13
+ import type { MessagingProvider } from '../../provider.js';
14
+ import type {
15
+ Conversation,
16
+ Message,
17
+ SearchResult,
18
+ SendResult,
19
+ ConnectionInfo,
20
+ ListOptions,
21
+ HistoryOptions,
22
+ SearchOptions,
23
+ SendOptions,
24
+ } from '../../provider-types.js';
25
+ import { getSecureKey } from '../../../security/secure-keys.js';
26
+ import { readHttpToken } from '../../../util/platform.js';
27
+ import { getGatewayInternalBaseUrl } from '../../../config/env.js';
28
+ import { getOrCreateConversation } from '../../../memory/conversation-key-store.js';
29
+ import * as externalConversationStore from '../../../memory/external-conversation-store.js';
30
+ import * as whatsapp from './client.js';
31
+
32
+ /** Resolve the gateway base URL. */
33
+ function getGatewayUrl(): string {
34
+ return getGatewayInternalBaseUrl();
35
+ }
36
+
37
+ /** Read the runtime HTTP bearer token used to authenticate with the gateway. */
38
+ function getBearerToken(): string {
39
+ const token = readHttpToken();
40
+ if (!token) {
41
+ throw new Error('No runtime HTTP bearer token available — is the daemon running?');
42
+ }
43
+ return token;
44
+ }
45
+
46
+ /** Check whether WhatsApp credentials are stored. */
47
+ function hasWhatsAppCredentials(): boolean {
48
+ return (
49
+ !!getSecureKey('credential:whatsapp:phone_number_id') &&
50
+ !!getSecureKey('credential:whatsapp:access_token')
51
+ );
52
+ }
53
+
54
+ export const whatsappMessagingProvider: MessagingProvider = {
55
+ id: 'whatsapp',
56
+ displayName: 'WhatsApp',
57
+ credentialService: 'whatsapp',
58
+ capabilities: new Set(['send']),
59
+
60
+ /**
61
+ * WhatsApp is connected when Meta Cloud API credentials are stored.
62
+ */
63
+ isConnected(): boolean {
64
+ return hasWhatsAppCredentials();
65
+ },
66
+
67
+ async testConnection(_token: string): Promise<ConnectionInfo> {
68
+ if (!hasWhatsAppCredentials()) {
69
+ return {
70
+ connected: false,
71
+ user: 'unknown',
72
+ platform: 'whatsapp',
73
+ metadata: { error: 'No WhatsApp credentials found. Configure WHATSAPP_PHONE_NUMBER_ID and WHATSAPP_ACCESS_TOKEN.' },
74
+ };
75
+ }
76
+
77
+ const phoneNumberId = getSecureKey('credential:whatsapp:phone_number_id')!;
78
+
79
+ return {
80
+ connected: true,
81
+ user: phoneNumberId,
82
+ platform: 'whatsapp',
83
+ metadata: {
84
+ phoneNumberId: phoneNumberId.slice(0, 6) + '...',
85
+ },
86
+ };
87
+ },
88
+
89
+ async sendMessage(_token: string, conversationId: string, text: string, options?: SendOptions): Promise<SendResult> {
90
+ const gatewayUrl = getGatewayUrl();
91
+ const bearerToken = getBearerToken();
92
+ const assistantId = options?.assistantId;
93
+
94
+ await whatsapp.sendMessage(gatewayUrl, bearerToken, conversationId, text, assistantId);
95
+
96
+ // Upsert external conversation binding so the conversation key mapping
97
+ // exists for the next inbound WhatsApp message from this number.
98
+ try {
99
+ const sourceChannel = 'whatsapp';
100
+ const conversationKey = assistantId && assistantId !== 'self'
101
+ ? `asst:${assistantId}:${sourceChannel}:${conversationId}`
102
+ : `${sourceChannel}:${conversationId}`;
103
+ const { conversationId: internalId } = getOrCreateConversation(conversationKey);
104
+ if (!assistantId || assistantId === 'self') {
105
+ externalConversationStore.upsertOutboundBinding({
106
+ conversationId: internalId,
107
+ sourceChannel,
108
+ externalChatId: conversationId,
109
+ });
110
+ }
111
+ } catch {
112
+ // Best-effort — don't fail the send if binding upsert fails
113
+ }
114
+
115
+ return {
116
+ id: `whatsapp-${Date.now()}`,
117
+ timestamp: Date.now(),
118
+ conversationId,
119
+ };
120
+ },
121
+
122
+ // WhatsApp does not support listing conversations via this provider.
123
+ async listConversations(_token: string, _options?: ListOptions): Promise<Conversation[]> {
124
+ return [];
125
+ },
126
+
127
+ // WhatsApp does not provide message history retrieval via the gateway.
128
+ async getHistory(_token: string, _conversationId: string, _options?: HistoryOptions): Promise<Message[]> {
129
+ return [];
130
+ },
131
+
132
+ // WhatsApp does not support message search.
133
+ async search(_token: string, _query: string, _options?: SearchOptions): Promise<SearchResult> {
134
+ return { total: 0, messages: [], hasMore: false };
135
+ },
136
+ };
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Low-level WhatsApp operations.
3
+ *
4
+ * Outbound message delivery routes through the gateway's /deliver/whatsapp
5
+ * endpoint, which handles WhatsApp credential management and the Meta Cloud API.
6
+ */
7
+
8
+ const DELIVERY_TIMEOUT_MS = 30_000;
9
+
10
+ export class WhatsAppApiError extends Error {
11
+ constructor(
12
+ public readonly status: number,
13
+ message: string,
14
+ ) {
15
+ super(message);
16
+ this.name = 'WhatsAppApiError';
17
+ }
18
+ }
19
+
20
+ /** Payload accepted by the gateway's /deliver/whatsapp endpoint. */
21
+ interface DeliverPayload {
22
+ to: string;
23
+ text: string;
24
+ assistantId?: string;
25
+ }
26
+
27
+ /** Result returned by sendMessage. */
28
+ export interface WhatsAppSendResult {
29
+ ok: boolean;
30
+ }
31
+
32
+ /**
33
+ * Send a WhatsApp message via the gateway's /deliver/whatsapp endpoint.
34
+ */
35
+ export async function sendMessage(
36
+ gatewayUrl: string,
37
+ bearerToken: string,
38
+ to: string,
39
+ text: string,
40
+ assistantId?: string,
41
+ ): Promise<WhatsAppSendResult> {
42
+ const payload: DeliverPayload = { to, text };
43
+ if (assistantId) {
44
+ payload.assistantId = assistantId;
45
+ }
46
+
47
+ const url = `${gatewayUrl}/deliver/whatsapp`;
48
+ const resp = await fetch(url, {
49
+ method: 'POST',
50
+ headers: {
51
+ 'Content-Type': 'application/json',
52
+ Authorization: `Bearer ${bearerToken}`,
53
+ },
54
+ body: JSON.stringify(payload),
55
+ signal: AbortSignal.timeout(DELIVERY_TIMEOUT_MS),
56
+ });
57
+
58
+ if (!resp.ok) {
59
+ const body = await resp.text().catch(() => '<unreadable>');
60
+ throw new WhatsAppApiError(
61
+ resp.status,
62
+ `Gateway /deliver/whatsapp failed (${resp.status}): ${body}`,
63
+ );
64
+ }
65
+
66
+ return { ok: true };
67
+ }
@@ -9,8 +9,7 @@
9
9
  import type { Message as ProviderMessage } from './provider-types.js';
10
10
  import type { Message, ToolDefinition } from '../providers/types.js';
11
11
  import { truncate } from '../util/truncate.js';
12
- import { getProvider } from '../providers/registry.js';
13
- import { getConfig } from '../config/loader.js';
12
+ import { getConfiguredProvider } from '../providers/provider-send-message.js';
14
13
 
15
14
  export interface StylePattern {
16
15
  aspect: string;
@@ -118,8 +117,10 @@ export async function extractStylePatterns(
118
117
 
119
118
  const corpus = corpusEntries.map((e, i) => `--- Message ${i + 1} ---\n${e}`).join('\n\n');
120
119
 
121
- const config = getConfig();
122
- const provider = getProvider(config.provider);
120
+ const provider = getConfiguredProvider();
121
+ if (!provider) {
122
+ return { stylePatterns: [], contactObservations: [] };
123
+ }
123
124
  const promptMessages: Message[] = [{
124
125
  role: 'user',
125
126
  content: [{ type: 'text', text: `Analyze these ${corpusEntries.length} sent messages for writing style patterns:\n\n${corpus}` }],