@vellumai/assistant 0.3.4 → 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 (506) hide show
  1. package/Dockerfile +2 -0
  2. package/README.md +88 -2
  3. package/eslint.config.mjs +31 -0
  4. package/package.json +1 -1
  5. package/scripts/ipc/check-swift-decoder-drift.ts +4 -1
  6. package/scripts/ipc/generate-swift.ts +31 -2
  7. package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +438 -1
  8. package/src/__tests__/approval-conversation-turn.test.ts +214 -0
  9. package/src/__tests__/approval-hardcoded-copy-guard.test.ts +41 -0
  10. package/src/__tests__/approval-message-composer.test.ts +253 -0
  11. package/src/__tests__/browser-manager.test.ts +1 -0
  12. package/src/__tests__/call-conversation-messages.test.ts +130 -0
  13. package/src/__tests__/call-domain.test.ts +12 -2
  14. package/src/__tests__/call-orchestrator.test.ts +799 -249
  15. package/src/__tests__/call-pointer-messages.test.ts +148 -0
  16. package/src/__tests__/call-recovery.test.ts +3 -0
  17. package/src/__tests__/call-routes-http.test.ts +32 -2
  18. package/src/__tests__/call-store.test.ts +3 -0
  19. package/src/__tests__/channel-approval-routes.test.ts +1277 -98
  20. package/src/__tests__/channel-approval.test.ts +37 -0
  21. package/src/__tests__/channel-approvals.test.ts +36 -50
  22. package/src/__tests__/channel-guardian.test.ts +630 -22
  23. package/src/__tests__/channel-readiness-service.test.ts +324 -0
  24. package/src/__tests__/checker.test.ts +14 -7
  25. package/src/__tests__/clarification-resolver.test.ts +44 -24
  26. package/src/__tests__/commit-message-enrichment-service.test.ts +9 -4
  27. package/src/__tests__/computer-use-session-working-dir.test.ts +8 -0
  28. package/src/__tests__/config-schema.test.ts +14 -8
  29. package/src/__tests__/context-window-manager.test.ts +30 -2
  30. package/src/__tests__/contradiction-checker.test.ts +20 -5
  31. package/src/__tests__/credential-security-invariants.test.ts +7 -2
  32. package/src/__tests__/daemon-lifecycle.test.ts +13 -12
  33. package/src/__tests__/db-migration-rollback.test.ts +752 -0
  34. package/src/__tests__/dictation-mode-detection.test.ts +63 -0
  35. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -0
  36. package/src/__tests__/entity-search.test.ts +615 -0
  37. package/src/__tests__/fuzzy-match-property.test.ts +5 -5
  38. package/src/__tests__/guardian-action-store.test.ts +123 -0
  39. package/src/__tests__/guardian-action-sweep.test.ts +277 -0
  40. package/src/__tests__/guardian-dispatch.test.ts +389 -0
  41. package/src/__tests__/guardian-question-copy.test.ts +47 -0
  42. package/src/__tests__/handlers-telegram-config.test.ts +4 -2
  43. package/src/__tests__/handlers-twilio-config.test.ts +533 -0
  44. package/src/__tests__/intent-routing.test.ts +2 -0
  45. package/src/__tests__/ipc-snapshot.test.ts +291 -1
  46. package/src/__tests__/memory-upsert-concurrency.test.ts +828 -0
  47. package/src/__tests__/messaging-send-tool.test.ts +65 -0
  48. package/src/__tests__/model-intents.test.ts +96 -0
  49. package/src/__tests__/no-direct-anthropic-sdk-imports.test.ts +42 -0
  50. package/src/__tests__/oauth2-gateway-transport.test.ts +130 -0
  51. package/src/__tests__/onboarding-starter-tasks.test.ts +2 -0
  52. package/src/__tests__/provider-commit-message-generator.test.ts +89 -13
  53. package/src/__tests__/provider-error-scenarios.test.ts +621 -0
  54. package/src/__tests__/provider-fail-open-selection.test.ts +119 -0
  55. package/src/__tests__/qdrant-manager.test.ts +27 -20
  56. package/src/__tests__/relay-server.test.ts +779 -40
  57. package/src/__tests__/run-orchestrator-assistant-events.test.ts +6 -0
  58. package/src/__tests__/run-orchestrator.test.ts +42 -4
  59. package/src/__tests__/runtime-runs-http.test.ts +17 -1
  60. package/src/__tests__/runtime-runs.test.ts +16 -0
  61. package/src/__tests__/schedule-store.test.ts +18 -4
  62. package/src/__tests__/scheduler-recurrence.test.ts +13 -4
  63. package/src/__tests__/session-abort-tool-results.test.ts +6 -0
  64. package/src/__tests__/session-agent-loop.test.ts +857 -0
  65. package/src/__tests__/session-conflict-gate.test.ts +6 -0
  66. package/src/__tests__/session-pre-run-repair.test.ts +6 -0
  67. package/src/__tests__/session-profile-injection.test.ts +6 -0
  68. package/src/__tests__/session-provider-retry-repair.test.ts +6 -0
  69. package/src/__tests__/session-queue.test.ts +6 -0
  70. package/src/__tests__/session-runtime-assembly.test.ts +321 -13
  71. package/src/__tests__/session-slash-known.test.ts +6 -0
  72. package/src/__tests__/session-slash-queue.test.ts +6 -0
  73. package/src/__tests__/session-slash-unknown.test.ts +6 -0
  74. package/src/__tests__/session-surfaces-task-progress.test.ts +2 -0
  75. package/src/__tests__/session-tool-setup-app-refresh.test.ts +1 -0
  76. package/src/__tests__/session-tool-setup-memory-scope.test.ts +1 -0
  77. package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +1 -0
  78. package/src/__tests__/session-workspace-injection.test.ts +6 -0
  79. package/src/__tests__/session-workspace-tool-tracking.test.ts +6 -0
  80. package/src/__tests__/skills.test.ts +2 -0
  81. package/src/__tests__/sms-messaging-provider.test.ts +126 -0
  82. package/src/__tests__/starter-task-flow.test.ts +2 -0
  83. package/src/__tests__/swarm-dag-pathological.test.ts +535 -0
  84. package/src/__tests__/system-prompt.test.ts +2 -0
  85. package/src/__tests__/task-management-tools.test.ts +2 -2
  86. package/src/__tests__/task-runner.test.ts +14 -4
  87. package/src/__tests__/terminal-tools.test.ts +25 -19
  88. package/src/__tests__/tool-execution-abort-cleanup.test.ts +545 -0
  89. package/src/__tests__/tool-executor-shell-integration.test.ts +11 -11
  90. package/src/__tests__/tool-executor.test.ts +23 -24
  91. package/src/__tests__/trust-store.test.ts +3 -3
  92. package/src/__tests__/twilio-rest.test.ts +29 -0
  93. package/src/__tests__/twilio-routes-elevenlabs.test.ts +3 -0
  94. package/src/__tests__/twilio-routes-twiml.test.ts +11 -0
  95. package/src/__tests__/twilio-routes.test.ts +167 -11
  96. package/src/__tests__/twitter-cli-error-shaping.test.ts +2 -2
  97. package/src/__tests__/user-reference.test.ts +2 -0
  98. package/src/__tests__/voice-quality.test.ts +222 -0
  99. package/src/__tests__/web-search.test.ts +46 -30
  100. package/src/__tests__/work-item-output.test.ts +110 -0
  101. package/src/agent/loop.ts +1 -1
  102. package/src/agent-heartbeat/agent-heartbeat-service.ts +2 -10
  103. package/src/amazon/client.ts +1418 -0
  104. package/src/amazon/request-extractor.ts +135 -0
  105. package/src/amazon/session.ts +109 -0
  106. package/src/autonomy/autonomy-store.ts +5 -5
  107. package/src/browser-extension-relay/client.ts +124 -0
  108. package/src/browser-extension-relay/protocol.ts +63 -0
  109. package/src/browser-extension-relay/server.ts +177 -0
  110. package/src/bundler/app-bundler.ts +3 -3
  111. package/src/bundler/bundle-signer.ts +1 -1
  112. package/src/bundler/signature-verifier.ts +1 -1
  113. package/src/calls/call-conversation-messages.ts +33 -0
  114. package/src/calls/call-domain.ts +114 -10
  115. package/src/calls/call-orchestrator.ts +268 -59
  116. package/src/calls/call-pointer-messages.ts +53 -0
  117. package/src/calls/call-recovery.ts +3 -8
  118. package/src/calls/call-store.ts +69 -87
  119. package/src/calls/elevenlabs-config.ts +3 -2
  120. package/src/calls/guardian-action-sweep.ts +105 -0
  121. package/src/calls/guardian-dispatch.ts +203 -0
  122. package/src/calls/guardian-question-copy.ts +133 -0
  123. package/src/calls/relay-server.ts +466 -8
  124. package/src/calls/speaker-identification.ts +1 -1
  125. package/src/calls/twilio-config.ts +22 -14
  126. package/src/calls/twilio-provider.ts +6 -4
  127. package/src/calls/twilio-rest.ts +308 -7
  128. package/src/calls/twilio-routes.ts +65 -12
  129. package/src/calls/types.ts +3 -1
  130. package/src/channels/types.ts +25 -0
  131. package/src/cli/amazon.ts +815 -0
  132. package/src/cli/config-commands.ts +2 -2
  133. package/src/cli/core-commands.ts +4 -3
  134. package/src/cli/influencer.ts +244 -0
  135. package/src/cli/map.ts +89 -6
  136. package/src/cli.ts +1 -1
  137. package/src/config/agent-schema.ts +171 -0
  138. package/src/config/bundled-skills/amazon/SKILL.md +127 -0
  139. package/src/config/bundled-skills/amazon/icon.svg +13 -0
  140. package/src/config/bundled-skills/api-mapping/SKILL.md +78 -0
  141. package/src/config/bundled-skills/browser/SKILL.md +1 -0
  142. package/src/config/bundled-skills/browser/TOOLS.json +17 -0
  143. package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +25 -0
  144. package/src/config/bundled-skills/doordash/SKILL.md +51 -51
  145. package/src/config/bundled-skills/email-setup/SKILL.md +14 -5
  146. package/src/config/bundled-skills/google-oauth-setup/SKILL.md +183 -0
  147. package/src/config/bundled-skills/influencer/SKILL.md +144 -0
  148. package/src/config/bundled-skills/knowledge-graph/SKILL.md +15 -0
  149. package/src/config/bundled-skills/knowledge-graph/TOOLS.json +56 -0
  150. package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +185 -0
  151. package/src/config/bundled-skills/macos-automation/icon.svg +12 -0
  152. package/src/config/bundled-skills/media-processing/SKILL.md +176 -0
  153. package/src/config/bundled-skills/media-processing/TOOLS.json +230 -0
  154. package/src/config/bundled-skills/media-processing/__tests__/concurrency-pool.test.ts +77 -0
  155. package/src/config/bundled-skills/media-processing/__tests__/cost-tracker.test.ts +69 -0
  156. package/src/config/bundled-skills/media-processing/__tests__/preprocess.test.ts +303 -0
  157. package/src/config/bundled-skills/media-processing/services/concurrency-pool.ts +55 -0
  158. package/src/config/bundled-skills/media-processing/services/cost-tracker.ts +86 -0
  159. package/src/config/bundled-skills/media-processing/services/gemini-map.ts +339 -0
  160. package/src/config/bundled-skills/media-processing/services/preprocess.ts +551 -0
  161. package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +259 -0
  162. package/src/config/bundled-skills/media-processing/services/reduce.ts +197 -0
  163. package/src/config/bundled-skills/media-processing/tools/analyze-keyframes.ts +136 -0
  164. package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +59 -0
  165. package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +195 -0
  166. package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +197 -0
  167. package/src/config/bundled-skills/media-processing/tools/media-diagnostics.ts +143 -0
  168. package/src/config/bundled-skills/media-processing/tools/media-status.ts +75 -0
  169. package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +65 -0
  170. package/src/config/bundled-skills/messaging/SKILL.md +33 -8
  171. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +4 -7
  172. package/src/config/bundled-skills/messaging/tools/messaging-reply.ts +2 -1
  173. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +5 -1
  174. package/src/config/bundled-skills/phone-calls/SKILL.md +88 -23
  175. package/src/config/bundled-skills/twitter/SKILL.md +19 -3
  176. package/src/config/bundled-skills/twitter/icon.svg +14 -0
  177. package/src/config/bundled-tool-registry.ts +310 -0
  178. package/src/config/calls-schema.ts +181 -0
  179. package/src/config/core-schema.ts +309 -0
  180. package/src/config/defaults.ts +28 -3
  181. package/src/config/env-registry.ts +162 -0
  182. package/src/config/env.ts +175 -0
  183. package/src/config/loader.ts +6 -6
  184. package/src/config/memory-schema.ts +528 -0
  185. package/src/config/sandbox-schema.ts +55 -0
  186. package/src/config/schema.ts +158 -1133
  187. package/src/config/skill-state.ts +1 -1
  188. package/src/config/skills-schema.ts +32 -0
  189. package/src/config/skills.ts +35 -24
  190. package/src/config/system-prompt.ts +131 -56
  191. package/src/config/templates/IDENTITY.md +2 -2
  192. package/src/config/templates/SOUL.md +1 -1
  193. package/src/config/types.ts +1 -0
  194. package/src/config/user-reference.ts +4 -9
  195. package/src/config/vellum-skills/catalog.json +6 -7
  196. package/src/config/vellum-skills/chatgpt-import/tools/chatgpt-import.ts +5 -1
  197. package/src/config/vellum-skills/slack-oauth-setup/SKILL.md +4 -3
  198. package/src/config/vellum-skills/sms-setup/SKILL.md +216 -0
  199. package/src/config/vellum-skills/twilio-setup/SKILL.md +40 -8
  200. package/src/context/window-manager.ts +27 -7
  201. package/src/daemon/approval-generators.ts +186 -0
  202. package/src/daemon/approved-devices-store.ts +140 -0
  203. package/src/daemon/assistant-attachments.ts +1 -1
  204. package/src/daemon/classifier.ts +35 -32
  205. package/src/daemon/config-watcher.ts +1 -1
  206. package/src/daemon/daemon-control.ts +217 -0
  207. package/src/daemon/handlers/apps.ts +2 -3
  208. package/src/daemon/handlers/config-channels.ts +158 -0
  209. package/src/daemon/handlers/config-inbox.ts +540 -0
  210. package/src/daemon/handlers/config-ingress.ts +231 -0
  211. package/src/daemon/handlers/config-integrations.ts +258 -0
  212. package/src/daemon/handlers/config-model.ts +143 -0
  213. package/src/daemon/handlers/config-parental.ts +163 -0
  214. package/src/daemon/handlers/config-scheduling.ts +172 -0
  215. package/src/daemon/handlers/config-slack.ts +92 -0
  216. package/src/daemon/handlers/config-telegram.ts +301 -0
  217. package/src/daemon/handlers/config-tools.ts +177 -0
  218. package/src/daemon/handlers/config-trust.ts +104 -0
  219. package/src/daemon/handlers/config-twilio.ts +1080 -0
  220. package/src/daemon/handlers/config.ts +53 -1689
  221. package/src/daemon/handlers/diagnostics.ts +1 -1
  222. package/src/daemon/handlers/dictation.ts +180 -0
  223. package/src/daemon/handlers/documents.ts +18 -32
  224. package/src/daemon/handlers/identity.ts +14 -23
  225. package/src/daemon/handlers/index.ts +11 -0
  226. package/src/daemon/handlers/misc.ts +3 -5
  227. package/src/daemon/handlers/pairing.ts +98 -0
  228. package/src/daemon/handlers/sessions.ts +56 -5
  229. package/src/daemon/handlers/shared.ts +6 -1
  230. package/src/daemon/handlers/skills.ts +1 -1
  231. package/src/daemon/handlers/twitter-auth.ts +2 -0
  232. package/src/daemon/handlers/work-items.ts +17 -9
  233. package/src/daemon/handlers/workspace-files.ts +4 -3
  234. package/src/daemon/install-cli-launchers.ts +113 -0
  235. package/src/daemon/ipc-contract/apps.ts +356 -0
  236. package/src/daemon/ipc-contract/browser.ts +74 -0
  237. package/src/daemon/ipc-contract/computer-use.ts +151 -0
  238. package/src/daemon/ipc-contract/diagnostics.ts +56 -0
  239. package/src/daemon/ipc-contract/documents.ts +74 -0
  240. package/src/daemon/ipc-contract/inbox.ts +209 -0
  241. package/src/daemon/ipc-contract/integrations.ts +284 -0
  242. package/src/daemon/ipc-contract/memory.ts +48 -0
  243. package/src/daemon/ipc-contract/messages.ts +211 -0
  244. package/src/daemon/ipc-contract/pairing.ts +45 -0
  245. package/src/daemon/ipc-contract/parental-control.ts +95 -0
  246. package/src/daemon/ipc-contract/schedules.ts +97 -0
  247. package/src/daemon/ipc-contract/sessions.ts +315 -0
  248. package/src/daemon/ipc-contract/shared.ts +42 -0
  249. package/src/daemon/ipc-contract/skills.ts +120 -0
  250. package/src/daemon/ipc-contract/subagents.ts +58 -0
  251. package/src/daemon/ipc-contract/surfaces.ts +250 -0
  252. package/src/daemon/ipc-contract/trust.ts +60 -0
  253. package/src/daemon/ipc-contract/work-items.ts +225 -0
  254. package/src/daemon/ipc-contract/workspace.ts +113 -0
  255. package/src/daemon/ipc-contract-inventory.json +70 -0
  256. package/src/daemon/ipc-contract-inventory.ts +55 -29
  257. package/src/daemon/ipc-contract.ts +229 -2426
  258. package/src/daemon/ipc-protocol.ts +1 -1
  259. package/src/daemon/ipc-validate.ts +7 -0
  260. package/src/daemon/lifecycle.ts +97 -377
  261. package/src/daemon/pairing-store.ts +177 -0
  262. package/src/daemon/providers-setup.ts +43 -0
  263. package/src/daemon/ride-shotgun-handler.ts +68 -3
  264. package/src/daemon/server.ts +66 -46
  265. package/src/daemon/session-agent-loop-handlers.ts +421 -0
  266. package/src/daemon/session-agent-loop.ts +117 -275
  267. package/src/daemon/session-dynamic-profile.ts +1 -1
  268. package/src/daemon/session-history.ts +1 -1
  269. package/src/daemon/session-media-retry.ts +1 -1
  270. package/src/daemon/session-messaging.ts +37 -2
  271. package/src/daemon/session-notifiers.ts +5 -25
  272. package/src/daemon/session-process.ts +99 -59
  273. package/src/daemon/session-queue-manager.ts +96 -4
  274. package/src/daemon/session-runtime-assembly.ts +199 -10
  275. package/src/daemon/session-surfaces.ts +19 -4
  276. package/src/daemon/session-tool-setup.ts +30 -30
  277. package/src/daemon/session-workspace.ts +1 -1
  278. package/src/daemon/session.ts +35 -2
  279. package/src/daemon/shutdown-handlers.ts +122 -0
  280. package/src/daemon/trace-emitter.ts +1 -1
  281. package/src/daemon/watch-handler.ts +36 -33
  282. package/src/doordash/cart-queries.ts +787 -0
  283. package/src/doordash/client.ts +144 -127
  284. package/src/doordash/order-queries.ts +85 -0
  285. package/src/doordash/queries.ts +10 -1308
  286. package/src/doordash/search-queries.ts +203 -0
  287. package/src/doordash/session.ts +3 -2
  288. package/src/doordash/store-queries.ts +246 -0
  289. package/src/doordash/types.ts +367 -0
  290. package/src/email/providers/agentmail.ts +2 -1
  291. package/src/email/providers/index.ts +3 -2
  292. package/src/email/service.ts +3 -2
  293. package/src/errors.ts +43 -0
  294. package/src/home-base/prebuilt/seed.ts +1 -1
  295. package/src/hooks/cli.ts +6 -5
  296. package/src/hooks/config.ts +6 -8
  297. package/src/hooks/discovery.ts +6 -5
  298. package/src/hooks/manager.ts +4 -3
  299. package/src/hooks/runner.ts +2 -2
  300. package/src/hooks/templates.ts +5 -5
  301. package/src/inbound/public-ingress-urls.ts +6 -4
  302. package/src/index.ts +4 -2
  303. package/src/influencer/client.ts +1104 -0
  304. package/src/instrument.ts +4 -3
  305. package/src/logfire.ts +4 -3
  306. package/src/memory/admin.ts +25 -35
  307. package/src/memory/attachments-store.ts +4 -7
  308. package/src/memory/channel-delivery-store.ts +30 -1
  309. package/src/memory/channel-guardian-store.ts +202 -2
  310. package/src/memory/clarification-resolver.ts +37 -33
  311. package/src/memory/conflict-store.ts +67 -61
  312. package/src/memory/contradiction-checker.ts +141 -117
  313. package/src/memory/conversation-store.ts +335 -51
  314. package/src/memory/db-connection.ts +27 -4
  315. package/src/memory/db-init.ts +265 -4
  316. package/src/memory/db.ts +14 -1
  317. package/src/memory/embedding-backend.ts +27 -5
  318. package/src/memory/embedding-ollama.ts +2 -1
  319. package/src/memory/entity-extractor.ts +38 -35
  320. package/src/memory/guardian-action-store.ts +430 -0
  321. package/src/memory/inbox-escalation-projection.ts +59 -0
  322. package/src/memory/inbox-thread-store.ts +218 -0
  323. package/src/memory/ingress-invite-store.ts +338 -0
  324. package/src/memory/ingress-member-store.ts +350 -0
  325. package/src/memory/items-extractor.ts +91 -97
  326. package/src/memory/job-handlers/index-maintenance.ts +3 -3
  327. package/src/memory/job-handlers/media-processing.ts +69 -0
  328. package/src/memory/job-handlers/summarization.ts +32 -26
  329. package/src/memory/job-utils.ts +3 -10
  330. package/src/memory/jobs-store.ts +8 -10
  331. package/src/memory/jobs-worker.ts +55 -36
  332. package/src/memory/media-store.ts +759 -0
  333. package/src/memory/migrations/001-job-deferrals.ts +45 -0
  334. package/src/memory/migrations/002-tool-invocations-fk.ts +43 -0
  335. package/src/memory/migrations/003-memory-fts-backfill.ts +24 -0
  336. package/src/memory/migrations/004-entity-relation-dedup.ts +87 -0
  337. package/src/memory/migrations/005-fingerprint-scope-unique.ts +80 -0
  338. package/src/memory/migrations/006-scope-salted-fingerprints.ts +62 -0
  339. package/src/memory/migrations/007-assistant-id-to-self.ts +254 -0
  340. package/src/memory/migrations/008-remove-assistant-id-columns.ts +208 -0
  341. package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +83 -0
  342. package/src/memory/migrations/010-ext-conv-bindings-channel-chat-unique.ts +56 -0
  343. package/src/memory/migrations/011-call-sessions-provider-sid-dedup.ts +63 -0
  344. package/src/memory/migrations/012-call-sessions-add-initiated-from.ts +19 -0
  345. package/src/memory/migrations/013-guardian-action-tables.ts +68 -0
  346. package/src/memory/migrations/014-backfill-inbox-thread-state.ts +76 -0
  347. package/src/memory/migrations/015-drop-active-search-index.ts +27 -0
  348. package/src/memory/migrations/016-memory-segments-indexes.ts +11 -0
  349. package/src/memory/migrations/017-memory-items-indexes.ts +10 -0
  350. package/src/memory/migrations/018-remaining-table-indexes.ts +13 -0
  351. package/src/memory/migrations/index.ts +24 -0
  352. package/src/memory/migrations/registry.ts +79 -0
  353. package/src/memory/migrations/validate-migration-state.ts +69 -0
  354. package/src/memory/qdrant-manager.ts +49 -8
  355. package/src/memory/query-builder.ts +1 -1
  356. package/src/memory/raw-query.ts +119 -0
  357. package/src/memory/recall-cache.ts +4 -1
  358. package/src/memory/retriever.ts +165 -47
  359. package/src/memory/schema-migration.ts +25 -984
  360. package/src/memory/schema.ts +228 -7
  361. package/src/memory/search/entity.ts +205 -31
  362. package/src/memory/search/lexical.ts +81 -52
  363. package/src/memory/search/ranking.ts +27 -23
  364. package/src/memory/search/semantic.ts +157 -19
  365. package/src/memory/search/types.ts +24 -0
  366. package/src/memory/shared-app-links-store.ts +4 -5
  367. package/src/memory/validation.ts +19 -0
  368. package/src/messaging/draft-store.ts +5 -6
  369. package/src/messaging/provider-types.ts +2 -0
  370. package/src/messaging/providers/sms/adapter.ts +201 -0
  371. package/src/messaging/providers/sms/client.ts +93 -0
  372. package/src/messaging/providers/sms/types.ts +7 -0
  373. package/src/messaging/providers/telegram-bot/adapter.ts +2 -5
  374. package/src/messaging/providers/whatsapp/adapter.ts +136 -0
  375. package/src/messaging/providers/whatsapp/client.ts +67 -0
  376. package/src/messaging/style-analyzer.ts +5 -4
  377. package/src/messaging/thread-summarizer.ts +61 -69
  378. package/src/messaging/triage-engine.ts +62 -71
  379. package/src/migrations/config-merge.ts +53 -0
  380. package/src/migrations/data-layout.ts +68 -0
  381. package/src/migrations/data-merge.ts +33 -0
  382. package/src/migrations/hooks-merge.ts +90 -0
  383. package/src/migrations/index.ts +6 -0
  384. package/src/migrations/log.ts +23 -0
  385. package/src/migrations/skills-merge.ts +33 -0
  386. package/src/migrations/workspace-layout.ts +79 -0
  387. package/src/permissions/checker.ts +133 -11
  388. package/src/permissions/prompter.ts +14 -0
  389. package/src/permissions/shell-identity.ts +31 -1
  390. package/src/permissions/trust-store.ts +21 -1
  391. package/src/providers/anthropic/client.ts +4 -4
  392. package/src/providers/failover.ts +2 -2
  393. package/src/providers/model-intents.ts +70 -0
  394. package/src/providers/ollama/client.ts +2 -1
  395. package/src/providers/provider-send-message.ts +176 -0
  396. package/src/providers/registry.ts +71 -30
  397. package/src/providers/retry.ts +35 -1
  398. package/src/providers/types.ts +12 -1
  399. package/src/runtime/approval-conversation-turn.ts +97 -0
  400. package/src/runtime/approval-message-composer.ts +253 -0
  401. package/src/runtime/channel-approval-parser.ts +36 -2
  402. package/src/runtime/channel-approvals.ts +11 -24
  403. package/src/runtime/channel-guardian-service.ts +88 -21
  404. package/src/runtime/channel-readiness-service.ts +418 -0
  405. package/src/runtime/channel-readiness-types.ts +35 -0
  406. package/src/runtime/channel-retry-sweep.ts +184 -0
  407. package/src/runtime/guardian-context-resolver.ts +108 -0
  408. package/src/runtime/http-server.ts +275 -717
  409. package/src/runtime/http-types.ts +59 -3
  410. package/src/runtime/middleware/auth.ts +116 -0
  411. package/src/runtime/middleware/error-handler.ts +33 -0
  412. package/src/runtime/middleware/twilio-validation.ts +127 -0
  413. package/src/runtime/routes/app-routes.ts +1 -1
  414. package/src/runtime/routes/call-routes.ts +51 -7
  415. package/src/runtime/routes/channel-delivery-routes.ts +170 -0
  416. package/src/runtime/routes/channel-guardian-routes.ts +1191 -0
  417. package/src/runtime/routes/channel-inbound-routes.ts +1152 -0
  418. package/src/runtime/routes/channel-route-shared.ts +144 -0
  419. package/src/runtime/routes/channel-routes.ts +32 -1588
  420. package/src/runtime/routes/conversation-routes.ts +50 -7
  421. package/src/runtime/routes/events-routes.ts +2 -2
  422. package/src/runtime/routes/identity-routes.ts +126 -0
  423. package/src/runtime/routes/pairing-routes.ts +143 -0
  424. package/src/runtime/routes/run-routes.ts +15 -1
  425. package/src/runtime/run-orchestrator.ts +86 -35
  426. package/src/schedule/schedule-store.ts +36 -32
  427. package/src/schedule/scheduler.ts +3 -3
  428. package/src/security/encrypted-store.ts +5 -7
  429. package/src/security/oauth2.ts +45 -15
  430. package/src/security/parental-control-store.ts +183 -0
  431. package/src/security/secret-allowlist.ts +4 -3
  432. package/src/security/secret-scanner.ts +5 -5
  433. package/src/security/secure-keys.ts +1 -1
  434. package/src/security/token-manager.ts +3 -2
  435. package/src/services/vercel-deploy.ts +6 -2
  436. package/src/skills/tool-manifest.ts +3 -3
  437. package/src/skills/vellum-catalog-remote.ts +75 -16
  438. package/src/slack/slack-webhook.ts +2 -1
  439. package/src/swarm/orchestrator.ts +92 -1
  440. package/src/swarm/router-planner.ts +6 -9
  441. package/src/swarm/worker-prompts.ts +9 -12
  442. package/src/tasks/task-compiler.ts +19 -28
  443. package/src/tasks/task-runner.ts +1 -1
  444. package/src/tools/assets/materialize.ts +2 -2
  445. package/src/tools/assets/search.ts +15 -14
  446. package/src/tools/browser/__tests__/auth-detector.test.ts +1 -0
  447. package/src/tools/browser/auto-navigate.ts +1 -0
  448. package/src/tools/browser/browser-execution.ts +10 -1
  449. package/src/tools/browser/browser-manager.ts +119 -4
  450. package/src/tools/browser/network-recorder.ts +5 -0
  451. package/src/tools/calls/call-start.ts +1 -0
  452. package/src/tools/credentials/broker.ts +11 -2
  453. package/src/tools/credentials/metadata-store.ts +18 -14
  454. package/src/tools/credentials/post-connect-hooks.ts +61 -0
  455. package/src/tools/credentials/vault.ts +49 -23
  456. package/src/tools/execution-target.ts +11 -1
  457. package/src/tools/executor.ts +68 -9
  458. package/src/tools/host-terminal/cli-discover.ts +1 -1
  459. package/src/tools/network/script-proxy/http-forwarder.ts +1 -1
  460. package/src/tools/network/script-proxy/mitm-handler.ts +1 -1
  461. package/src/tools/network/script-proxy/server.ts +1 -1
  462. package/src/tools/network/script-proxy/session-manager.ts +6 -5
  463. package/src/tools/network/web-fetch.ts +18 -2
  464. package/src/tools/network/web-search.ts +8 -4
  465. package/src/tools/reminder/reminder-store.ts +14 -15
  466. package/src/tools/schedule/create.ts +1 -0
  467. package/src/tools/schedule/list.ts +2 -1
  468. package/src/tools/shared/filesystem/file-ops-service.ts +5 -7
  469. package/src/tools/skills/skill-script-runner.ts +24 -9
  470. package/src/tools/skills/skill-tool-factory.ts +1 -0
  471. package/src/tools/tasks/work-item-enqueue.ts +2 -2
  472. package/src/tools/terminal/evaluate-typescript.ts +21 -12
  473. package/src/tools/terminal/parser.ts +50 -0
  474. package/src/tools/types.ts +2 -0
  475. package/src/tools/watcher/delete.ts +6 -0
  476. package/src/tools/weather/service.ts +1 -1
  477. package/src/twitter/client.ts +190 -24
  478. package/src/twitter/router.ts +1 -1
  479. package/src/twitter/session.ts +4 -3
  480. package/src/util/clipboard.ts +1 -1
  481. package/src/util/errors.ts +65 -8
  482. package/src/util/fs.ts +40 -0
  483. package/src/util/json.ts +10 -0
  484. package/src/util/log-redact.ts +189 -0
  485. package/src/util/logger.ts +19 -17
  486. package/src/util/object.ts +3 -0
  487. package/src/util/platform.ts +105 -363
  488. package/src/util/pricing.ts +1 -1
  489. package/src/util/promise-guard.ts +1 -1
  490. package/src/util/retry.ts +19 -0
  491. package/src/util/row-mapper.ts +79 -0
  492. package/src/util/silently.ts +21 -0
  493. package/src/watcher/engine.ts +5 -1
  494. package/src/watcher/provider-types.ts +20 -0
  495. package/src/watcher/providers/github.ts +156 -0
  496. package/src/watcher/providers/gmail.ts +1 -0
  497. package/src/watcher/providers/google-calendar.ts +1 -0
  498. package/src/watcher/providers/linear.ts +460 -0
  499. package/src/watcher/providers/slack.ts +1 -0
  500. package/src/work-items/work-item-runner.ts +1 -1
  501. package/src/workspace/git-service.ts +1 -1
  502. package/src/workspace/provider-commit-message-generator.ts +51 -22
  503. package/src/__tests__/call-bridge.test.ts +0 -517
  504. package/src/__tests__/session-process-bridge.test.ts +0 -244
  505. package/src/calls/call-bridge.ts +0 -168
  506. package/src/config/vellum-skills/google-oauth-setup/SKILL.md +0 -199
@@ -1,4 +1,3 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
1
  /**
3
2
  * Integration tests: ToolExecutor → real checker.js → real shell-identity → real tree-sitter parser.
4
3
  *
@@ -10,6 +9,7 @@
10
9
  */
11
10
  import { describe, test, expect, beforeAll, mock } from 'bun:test';
12
11
  import type { ToolContext } from '../tools/types.js';
12
+ import type { AllowlistOption, ScopeOption } from '../permissions/types.js';
13
13
  import { PermissionPrompter } from '../permissions/prompter.js';
14
14
 
15
15
  // ── Config mock ──────────────────────────────────────────────────────
@@ -133,16 +133,16 @@ function makeContext(overrides?: Partial<ToolContext>): ToolContext {
133
133
  * passed to the prompter by the executor, then allows the tool.
134
134
  */
135
135
  function makeCapturingPrompter() {
136
- let capturedAllowlist: any[] | undefined;
137
- let capturedScopes: any[] | undefined;
136
+ let capturedAllowlist: AllowlistOption[] | undefined;
137
+ let capturedScopes: ScopeOption[] | undefined;
138
138
 
139
139
  const prompter = {
140
140
  prompt: async (
141
141
  _toolName: string,
142
142
  _input: Record<string, unknown>,
143
143
  _riskLevel: string,
144
- allowlistOptions: any[],
145
- scopeOptions: any[],
144
+ allowlistOptions: AllowlistOption[],
145
+ scopeOptions: ScopeOption[],
146
146
  ) => {
147
147
  capturedAllowlist = allowlistOptions;
148
148
  capturedScopes = scopeOptions;
@@ -177,7 +177,7 @@ describe('ToolExecutor → real shell allowlist integration', () => {
177
177
  expect(allowlist).toBeDefined();
178
178
  expect(allowlist!.length).toBeGreaterThan(1);
179
179
 
180
- const patterns = allowlist!.map((o: any) => o.pattern);
180
+ const patterns = allowlist!.map((o: AllowlistOption) => o.pattern);
181
181
 
182
182
  // Should contain the exact command
183
183
  expect(patterns).toContain('npm install express');
@@ -197,8 +197,8 @@ describe('ToolExecutor → real shell allowlist integration', () => {
197
197
  const scopes = getScopes();
198
198
  expect(scopes).toBeDefined();
199
199
  expect(scopes!.length).toBeGreaterThanOrEqual(2);
200
- expect(scopes!.some((s: any) => s.scope === '/tmp/project')).toBe(true);
201
- expect(scopes!.some((s: any) => s.scope === 'everywhere')).toBe(true);
200
+ expect(scopes!.some((s: ScopeOption) => s.scope === '/tmp/project')).toBe(true);
201
+ expect(scopes!.some((s: ScopeOption) => s.scope === 'everywhere')).toBe(true);
202
202
  });
203
203
 
204
204
  test('compound command produces only exact compound option (no action keys)', async () => {
@@ -226,7 +226,7 @@ describe('ToolExecutor → real shell allowlist integration', () => {
226
226
  expect(allowlist).toBeDefined();
227
227
  expect(allowlist!.length).toBeGreaterThan(1);
228
228
 
229
- const patterns = allowlist!.map((o: any) => o.pattern);
229
+ const patterns = allowlist!.map((o: AllowlistOption) => o.pattern);
230
230
 
231
231
  // Should contain the full original command as the exact option
232
232
  expect(patterns).toContain('cd /repo && gh pr view 123');
@@ -250,7 +250,7 @@ describe('ToolExecutor → real shell allowlist integration', () => {
250
250
  expect(scopes).toBeDefined();
251
251
  expect(scopes!.length).toBeGreaterThanOrEqual(2);
252
252
 
253
- const scopeValues = scopes!.map((s: any) => s.scope);
253
+ const scopeValues = scopes!.map((s: ScopeOption) => s.scope);
254
254
 
255
255
  // Project-scoped option
256
256
  expect(scopeValues).toContain('/Users/test/my-project');
@@ -276,7 +276,7 @@ describe('ToolExecutor → real shell allowlist integration', () => {
276
276
  expect(allowlist).toBeDefined();
277
277
  expect(allowlist!.length).toBeGreaterThan(1);
278
278
 
279
- const patterns = allowlist!.map((o: any) => o.pattern);
279
+ const patterns = allowlist!.map((o: AllowlistOption) => o.pattern);
280
280
 
281
281
  // Should contain exact command and action keys
282
282
  expect(patterns).toContain('git status');
@@ -1,8 +1,7 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
1
  import { describe, test, expect, beforeEach, afterEach, afterAll, mock, spyOn } from 'bun:test';
3
- import type { ToolExecutionResult, Tool } from '../tools/types.js';
2
+ import type { ToolExecutionResult, Tool, ToolLifecycleEvent, ToolPermissionPromptEvent } from '../tools/types.js';
4
3
  import { RiskLevel } from '../permissions/types.js';
5
- import type { PolicyContext } from '../permissions/types.js';
4
+ import type { AllowlistOption, ScopeOption, PolicyContext, TrustRule } from '../permissions/types.js';
6
5
 
7
6
  const mockConfig = {
8
7
  provider: 'anthropic',
@@ -322,8 +321,8 @@ describe('ToolExecutor contextual rule creation', () => {
322
321
 
323
322
  function setupAddRuleSpy() {
324
323
  addRuleSpy = spyOn(trustStore, 'addRule').mockImplementation(
325
- (tool: string, pattern: string, scope: string, decision = 'allow', priority = 100, options?: any) => {
326
- return { id: 'spy-rule-id', tool, pattern, scope, decision, priority, createdAt: Date.now(), ...options } as any;
324
+ (tool: string, pattern: string, scope: string, decision = 'allow', priority = 100, options?: { allowHighRisk?: boolean; executionTarget?: string }) => {
325
+ return { id: 'spy-rule-id', tool, pattern, scope, decision, priority, createdAt: Date.now(), ...options } as TrustRule;
327
326
  },
328
327
  );
329
328
  return addRuleSpy;
@@ -508,8 +507,8 @@ describe('ToolExecutor strict mode + high-risk integration (PR 25)', () => {
508
507
 
509
508
  function setupAddRuleSpy() {
510
509
  addRuleSpy = spyOn(trustStore, 'addRule').mockImplementation(
511
- (tool: string, pattern: string, scope: string, decision = 'allow', priority = 100, options?: any) => {
512
- return { id: 'spy-rule-id', tool, pattern, scope, decision, priority, createdAt: Date.now(), ...options } as any;
510
+ (tool: string, pattern: string, scope: string, decision = 'allow', priority = 100, options?: { allowHighRisk?: boolean; executionTarget?: string }) => {
511
+ return { id: 'spy-rule-id', tool, pattern, scope, decision, priority, createdAt: Date.now(), ...options } as TrustRule;
513
512
  },
514
513
  );
515
514
  return addRuleSpy;
@@ -1481,8 +1480,8 @@ describe('ToolExecutor persistentDecisionsAllowed contract', () => {
1481
1480
 
1482
1481
  function setupAddRuleSpy() {
1483
1482
  addRuleSpy = spyOn(trustStore, 'addRule').mockImplementation(
1484
- (tool: string, pattern: string, scope: string, decision = 'allow', priority = 100, options?: any) => {
1485
- return { id: 'spy-rule-id', tool, pattern, scope, decision, priority, createdAt: Date.now(), ...options } as any;
1483
+ (tool: string, pattern: string, scope: string, decision = 'allow', priority = 100, options?: { allowHighRisk?: boolean; executionTarget?: string }) => {
1484
+ return { id: 'spy-rule-id', tool, pattern, scope, decision, priority, createdAt: Date.now(), ...options } as TrustRule;
1486
1485
  },
1487
1486
  );
1488
1487
  return addRuleSpy;
@@ -1534,14 +1533,14 @@ describe('ToolExecutor persistentDecisionsAllowed contract', () => {
1534
1533
  });
1535
1534
 
1536
1535
  test('persistentDecisionsAllowed: false is emitted in lifecycle event for proxied bash', async () => {
1537
- let capturedEvent: any;
1536
+ let capturedEvent: ToolPermissionPromptEvent | undefined;
1538
1537
  const prompter = makePrompterWithDecision('allow');
1539
1538
  const executor = new ToolExecutor(prompter);
1540
1539
  const result = await executor.execute(
1541
1540
  'bash',
1542
1541
  { command: 'curl https://example.com', network_mode: 'proxied' },
1543
1542
  makeContext({
1544
- onToolLifecycleEvent: (event: any) => {
1543
+ onToolLifecycleEvent: (event: ToolLifecycleEvent) => {
1545
1544
  if (event.type === 'permission_prompt') {
1546
1545
  capturedEvent = event;
1547
1546
  }
@@ -1551,18 +1550,18 @@ describe('ToolExecutor persistentDecisionsAllowed contract', () => {
1551
1550
 
1552
1551
  expect(result.isError).toBe(false);
1553
1552
  expect(capturedEvent).toBeDefined();
1554
- expect(capturedEvent.persistentDecisionsAllowed).toBe(false);
1553
+ expect(capturedEvent!.persistentDecisionsAllowed).toBe(false);
1555
1554
  });
1556
1555
 
1557
1556
  test('persistentDecisionsAllowed: true is emitted in lifecycle event for non-proxied bash', async () => {
1558
- let capturedEvent: any;
1557
+ let capturedEvent: ToolPermissionPromptEvent | undefined;
1559
1558
  const prompter = makePrompterWithDecision('allow');
1560
1559
  const executor = new ToolExecutor(prompter);
1561
1560
  const result = await executor.execute(
1562
1561
  'bash',
1563
1562
  { command: 'echo hello' },
1564
1563
  makeContext({
1565
- onToolLifecycleEvent: (event: any) => {
1564
+ onToolLifecycleEvent: (event: ToolLifecycleEvent) => {
1566
1565
  if (event.type === 'permission_prompt') {
1567
1566
  capturedEvent = event;
1568
1567
  }
@@ -1572,7 +1571,7 @@ describe('ToolExecutor persistentDecisionsAllowed contract', () => {
1572
1571
 
1573
1572
  expect(result.isError).toBe(false);
1574
1573
  expect(capturedEvent).toBeDefined();
1575
- expect(capturedEvent.persistentDecisionsAllowed).toBe(true);
1574
+ expect(capturedEvent!.persistentDecisionsAllowed).toBe(true);
1576
1575
  });
1577
1576
 
1578
1577
  test('persistentDecisionsAllowed is passed to prompter confirmation_request for proxied bash', async () => {
@@ -1580,8 +1579,8 @@ describe('ToolExecutor persistentDecisionsAllowed contract', () => {
1580
1579
  const prompter = {
1581
1580
  prompt: async (
1582
1581
  _toolName: string, _input: Record<string, unknown>, _riskLevel: string,
1583
- _allowlistOptions: any[], _scopeOptions: any[], _diff: any, _sandboxed: any,
1584
- _sessionId: any, _executionTarget: any, persistentDecisionsAllowed: any,
1582
+ _allowlistOptions: AllowlistOption[], _scopeOptions: ScopeOption[], _diff: unknown, _sandboxed: unknown,
1583
+ _sessionId: unknown, _executionTarget: unknown, persistentDecisionsAllowed: boolean | undefined,
1585
1584
  ) => {
1586
1585
  capturedPersistent = persistentDecisionsAllowed;
1587
1586
  return { decision: 'allow' as const };
@@ -1643,8 +1642,8 @@ describe('E2E: proxied bash activation vs proxy approval persistence', () => {
1643
1642
 
1644
1643
  function setupAddRuleSpy() {
1645
1644
  addRuleSpy = spyOn(trustStore, 'addRule').mockImplementation(
1646
- (tool: string, pattern: string, scope: string, decision = 'allow', priority = 100, options?: any) => {
1647
- return { id: 'spy-rule-id', tool, pattern, scope, decision, priority, createdAt: Date.now(), ...options } as any;
1645
+ (tool: string, pattern: string, scope: string, decision = 'allow', priority = 100, options?: { allowHighRisk?: boolean; executionTarget?: string }) => {
1646
+ return { id: 'spy-rule-id', tool, pattern, scope, decision, priority, createdAt: Date.now(), ...options } as TrustRule;
1648
1647
  },
1649
1648
  );
1650
1649
  return addRuleSpy;
@@ -1871,8 +1870,8 @@ describe('ToolExecutor persistent-allow lifecycle', () => {
1871
1870
 
1872
1871
  function setupAddRuleSpy() {
1873
1872
  addRuleSpy = spyOn(trustStore, 'addRule').mockImplementation(
1874
- (tool: string, pattern: string, scope: string, decision = 'allow', priority = 100, options?: any) => {
1875
- return { id: 'spy-rule-id', tool, pattern, scope, decision, priority, createdAt: Date.now(), ...options } as any;
1873
+ (tool: string, pattern: string, scope: string, decision = 'allow', priority = 100, options?: { allowHighRisk?: boolean; executionTarget?: string }) => {
1874
+ return { id: 'spy-rule-id', tool, pattern, scope, decision, priority, createdAt: Date.now(), ...options } as TrustRule;
1876
1875
  },
1877
1876
  );
1878
1877
  return addRuleSpy;
@@ -1954,12 +1953,12 @@ describe('integration regressions — prompt payload (PR 11)', () => {
1954
1953
  test('shell command prompt payload includes allowlist and scope options', async () => {
1955
1954
  checkResultOverride = { decision: 'prompt', reason: 'Medium risk: requires approval' };
1956
1955
 
1957
- let capturedAllowlist: any[] | undefined;
1958
- let capturedScopes: any[] | undefined;
1956
+ let capturedAllowlist: AllowlistOption[] | undefined;
1957
+ let capturedScopes: ScopeOption[] | undefined;
1959
1958
  const prompter = {
1960
1959
  prompt: async (
1961
1960
  _toolName: string, _input: Record<string, unknown>, _riskLevel: string,
1962
- allowlistOptions: any[], scopeOptions: any[],
1961
+ allowlistOptions: AllowlistOption[], scopeOptions: ScopeOption[],
1963
1962
  ) => {
1964
1963
  capturedAllowlist = allowlistOptions;
1965
1964
  capturedScopes = scopeOptions;
@@ -845,7 +845,7 @@ describe('Trust Store', () => {
845
845
  const safePath = join(testDir, 'data', 'assistant.db');
846
846
  const match = findHighestPriorityRule('file_read', [`file_read:${safePath}`], '/tmp');
847
847
  // Should not match a default deny rule
848
- expect(match === null || !match.id.startsWith('default:')).toBe(true);
848
+ expect(match == null || !match.id.startsWith('default:')).toBe(true);
849
849
  });
850
850
 
851
851
  test('default rules are backfilled after malformed JSON in trust file', () => {
@@ -1396,7 +1396,7 @@ describe('Trust Store', () => {
1396
1396
  const match = findHighestPriorityRule('bash', ['run script.js'], '/tmp', {
1397
1397
  executionTarget: '/usr/local/bin/bun',
1398
1398
  });
1399
- expect(match === null || match.id !== 'et-diff').toBe(true);
1399
+ expect(match == null || match.id !== 'et-diff').toBe(true);
1400
1400
  });
1401
1401
 
1402
1402
  test('rule with executionTarget does NOT match when no target in context', () => {
@@ -1411,7 +1411,7 @@ describe('Trust Store', () => {
1411
1411
  executionTarget: '/usr/local/bin/node',
1412
1412
  }]);
1413
1413
  const match = findHighestPriorityRule('bash', ['run script.js'], '/tmp', {});
1414
- expect(match === null || match.id !== 'et-no-ctx').toBe(true);
1414
+ expect(match == null || match.id !== 'et-no-ctx').toBe(true);
1415
1415
  });
1416
1416
 
1417
1417
  test('rule WITHOUT executionTarget matches any target (wildcard)', () => {
@@ -0,0 +1,29 @@
1
+ import { describe, test, expect } from 'bun:test';
2
+ import { twilioAuthHeader, twilioBaseUrl } from '../calls/twilio-rest.js';
3
+
4
+ describe('twilioAuthHeader', () => {
5
+ test('returns a valid Basic auth header', () => {
6
+ const header = twilioAuthHeader('AC_test_sid', 'test_token');
7
+ const expected = 'Basic ' + Buffer.from('AC_test_sid:test_token').toString('base64');
8
+ expect(header).toBe(expected);
9
+ });
10
+
11
+ test('encodes special characters correctly', () => {
12
+ const header = twilioAuthHeader('AC_special!@#', 'tok$%^&');
13
+ const decoded = Buffer.from(header.replace('Basic ', ''), 'base64').toString();
14
+ expect(decoded).toBe('AC_special!@#:tok$%^&');
15
+ });
16
+ });
17
+
18
+ describe('twilioBaseUrl', () => {
19
+ test('constructs correct base URL for a given account SID', () => {
20
+ const url = twilioBaseUrl('AC_abc123');
21
+ expect(url).toBe('https://api.twilio.com/2010-04-01/Accounts/AC_abc123');
22
+ });
23
+
24
+ test('handles different account SIDs', () => {
25
+ const url = twilioBaseUrl('AC_xyz789');
26
+ expect(url).toContain('AC_xyz789');
27
+ expect(url).toStartWith('https://api.twilio.com/2010-04-01/Accounts/');
28
+ });
29
+ });
@@ -169,10 +169,13 @@ function ensureConversation(id: string): void {
169
169
 
170
170
  function resetTables() {
171
171
  const db = getDb();
172
+ db.run('DELETE FROM guardian_action_deliveries');
173
+ db.run('DELETE FROM guardian_action_requests');
172
174
  db.run('DELETE FROM processed_callbacks');
173
175
  db.run('DELETE FROM call_pending_questions');
174
176
  db.run('DELETE FROM call_events');
175
177
  db.run('DELETE FROM call_sessions');
178
+ db.run('DELETE FROM messages');
176
179
  db.run('DELETE FROM conversations');
177
180
  ensuredConvIds = new Set();
178
181
  }
@@ -124,4 +124,15 @@ describe('generateTwiML with voice quality profile', () => {
124
124
  expect(twiml).toContain('interruptible="true"');
125
125
  expect(twiml).toContain('dtmfDetection="true"');
126
126
  });
127
+
128
+ test('TwiML omits welcomeGreeting attribute when not provided', () => {
129
+ const twiml = generateTwiML(callSessionId, relayUrl, null, {
130
+ language: 'en-US',
131
+ transcriptionProvider: 'Deepgram',
132
+ ttsProvider: 'Google',
133
+ voice: 'Google.en-US-Journey-O',
134
+ });
135
+
136
+ expect(twiml).not.toContain('welcomeGreeting=');
137
+ });
127
138
  });
@@ -31,6 +31,7 @@ mock.module('../util/platform.js', () => ({
31
31
  getDbPath: () => join(testDir, 'test.db'),
32
32
  getLogPath: () => join(testDir, 'test.log'),
33
33
  ensureDataDir: () => {},
34
+ readHttpToken: () => null,
34
35
  }));
35
36
 
36
37
  mock.module('../util/logger.js', () => ({
@@ -39,15 +40,27 @@ mock.module('../util/logger.js', () => ({
39
40
  }),
40
41
  }));
41
42
 
43
+ const mockConfigObj = {
44
+ model: 'test',
45
+ provider: 'test',
46
+ apiKeys: {},
47
+ memory: { enabled: false },
48
+ rateLimit: { maxRequestsPerMinute: 0, maxTokensPerSession: 0 },
49
+ secretDetection: { enabled: false },
50
+ calls: {
51
+ voice: {
52
+ mode: 'twilio_standard',
53
+ language: 'en-US',
54
+ transcriptionProvider: 'Deepgram',
55
+ fallbackToStandardOnError: true,
56
+ elevenlabs: { voiceId: '' },
57
+ },
58
+ },
59
+ };
60
+
42
61
  mock.module('../config/loader.js', () => ({
43
- getConfig: () => ({
44
- model: 'test',
45
- provider: 'test',
46
- apiKeys: {},
47
- memory: { enabled: false },
48
- rateLimit: { maxRequestsPerMinute: 0, maxTokensPerSession: 0 },
49
- secretDetection: { enabled: false },
50
- }),
62
+ getConfig: () => mockConfigObj,
63
+ loadConfig: () => mockConfigObj,
51
64
  }));
52
65
 
53
66
  mock.module('../security/secure-keys.js', () => ({
@@ -84,10 +97,11 @@ import * as callStore from '../calls/call-store.js';
84
97
  import {
85
98
  createCallSession,
86
99
  getCallSession,
100
+ getCallSessionByCallSid,
87
101
  updateCallSession,
88
102
  getCallEvents,
89
103
  } from '../calls/call-store.js';
90
- import { resolveRelayUrl, handleStatusCallback, handleVoiceWebhook } from '../calls/twilio-routes.js';
104
+ import { resolveRelayUrl, buildWelcomeGreeting, handleStatusCallback, handleVoiceWebhook } from '../calls/twilio-routes.js';
91
105
  import { registerCallCompletionNotifier, unregisterCallCompletionNotifier } from '../calls/call-state.js';
92
106
 
93
107
  initializeDb();
@@ -111,22 +125,28 @@ function ensureConversation(id: string): void {
111
125
 
112
126
  function resetTables() {
113
127
  const db = getDb();
128
+ db.run('DELETE FROM guardian_action_deliveries');
129
+ db.run('DELETE FROM guardian_action_requests');
114
130
  db.run('DELETE FROM processed_callbacks');
115
131
  db.run('DELETE FROM call_pending_questions');
116
132
  db.run('DELETE FROM call_events');
117
133
  db.run('DELETE FROM call_sessions');
134
+ db.run('DELETE FROM external_conversation_bindings');
135
+ db.run('DELETE FROM conversation_keys');
136
+ db.run('DELETE FROM tool_invocations');
137
+ db.run('DELETE FROM messages');
118
138
  db.run('DELETE FROM conversations');
119
139
  ensuredConvIds = new Set();
120
140
  }
121
141
 
122
- function createTestSession(convId: string, callSid: string) {
142
+ function createTestSession(convId: string, callSid: string, task = 'test task') {
123
143
  ensureConversation(convId);
124
144
  const session = createCallSession({
125
145
  conversationId: convId,
126
146
  provider: 'twilio',
127
147
  fromNumber: '+15550001111',
128
148
  toNumber: '+15559998888',
129
- task: 'test task',
149
+ task,
130
150
  });
131
151
  updateCallSession(session.id, { providerCallSid: callSid });
132
152
  return session;
@@ -148,6 +168,14 @@ function makeVoiceRequest(sessionId: string, params: Record<string, string>): Re
148
168
  });
149
169
  }
150
170
 
171
+ function makeInboundVoiceRequest(params: Record<string, string>): Request {
172
+ return new Request('http://127.0.0.1/v1/calls/twilio/voice-webhook', {
173
+ method: 'POST',
174
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
175
+ body: new URLSearchParams(params).toString(),
176
+ });
177
+ }
178
+
151
179
  // ── Tests ──────────────────────────────────────────────────────────────
152
180
 
153
181
  describe('twilio webhook routes', () => {
@@ -155,6 +183,7 @@ describe('twilio webhook routes', () => {
155
183
  resetTables();
156
184
  mockWssBaseUrl = 'wss://test.example.com';
157
185
  mockWebhookBaseUrl = 'https://test.example.com';
186
+ delete process.env.CALL_WELCOME_GREETING;
158
187
  });
159
188
 
160
189
  afterAll(() => {
@@ -416,6 +445,18 @@ describe('twilio webhook routes', () => {
416
445
  });
417
446
  });
418
447
 
448
+ describe('buildWelcomeGreeting', () => {
449
+ test('returns empty by default so orchestrator drives first opener', () => {
450
+ const greeting = buildWelcomeGreeting('check store hours for tomorrow');
451
+ expect(greeting).toBe('');
452
+ });
453
+
454
+ test('uses configured greeting override when provided', () => {
455
+ const greeting = buildWelcomeGreeting('check store hours', 'Custom hello');
456
+ expect(greeting).toBe('Custom hello');
457
+ });
458
+ });
459
+
419
460
  // ── TwiML relay URL generation ──────────────────────────────────────
420
461
  // Call handleVoiceWebhook directly since direct routes are blocked.
421
462
 
@@ -446,6 +487,33 @@ describe('twilio webhook routes', () => {
446
487
  const twiml = await res.text();
447
488
  expect(twiml).toContain('wss://gateway.example.com/v1/calls/relay');
448
489
  });
490
+
491
+ test('TwiML omits welcome greeting by default so call opener is model-driven', async () => {
492
+ const session = createTestSession(
493
+ 'conv-twiml-3',
494
+ 'CA_twiml_3',
495
+ 'confirm appointment time\n\nContext: Prior email thread',
496
+ );
497
+ const req = makeVoiceRequest(session.id, { CallSid: 'CA_twiml_3' });
498
+
499
+ const res = await handleVoiceWebhook(req);
500
+
501
+ expect(res.status).toBe(200);
502
+ const twiml = await res.text();
503
+ expect(twiml).not.toContain('welcomeGreeting=');
504
+ });
505
+
506
+ test('TwiML includes explicit welcome greeting override when configured', async () => {
507
+ process.env.CALL_WELCOME_GREETING = 'Custom transport greeting';
508
+ const session = createTestSession('conv-twiml-4', 'CA_twiml_4');
509
+ const req = makeVoiceRequest(session.id, { CallSid: 'CA_twiml_4' });
510
+
511
+ const res = await handleVoiceWebhook(req);
512
+
513
+ expect(res.status).toBe(200);
514
+ const twiml = await res.text();
515
+ expect(twiml).toContain('welcomeGreeting="Custom transport greeting"');
516
+ });
449
517
  });
450
518
 
451
519
  // ── Handler-level idempotency concurrency tests ─────────────────
@@ -574,4 +642,92 @@ describe('twilio webhook routes', () => {
574
642
  expect(events2.filter(e => e.eventType === 'call_ended').length).toBe(1);
575
643
  });
576
644
  });
645
+
646
+ // ── Inbound voice webhook tests ─────────────────────────────────────
647
+ // Tests the inbound mode where callSessionId is absent and a session
648
+ // is created/reused from the Twilio CallSid.
649
+
650
+ describe('inbound voice webhook', () => {
651
+ test('creates a new session from CallSid when callSessionId is absent', async () => {
652
+ const req = makeInboundVoiceRequest({
653
+ CallSid: 'CA_inbound_new_1',
654
+ From: '+14155551234',
655
+ To: '+15550001111',
656
+ });
657
+
658
+ const res = await handleVoiceWebhook(req);
659
+
660
+ expect(res.status).toBe(200);
661
+ const twiml = await res.text();
662
+ expect(twiml).toContain('<ConversationRelay');
663
+ expect(twiml).toContain('callSessionId=');
664
+
665
+ // Verify session was created with the CallSid
666
+ const session = getCallSessionByCallSid('CA_inbound_new_1');
667
+ expect(session).not.toBeNull();
668
+ expect(session!.fromNumber).toBe('+14155551234');
669
+ expect(session!.toNumber).toBe('+15550001111');
670
+ expect(session!.providerCallSid).toBe('CA_inbound_new_1');
671
+ });
672
+
673
+ test('replayed inbound webhook for same CallSid does not create duplicate sessions', async () => {
674
+ const params = {
675
+ CallSid: 'CA_inbound_replay_1',
676
+ From: '+14155551234',
677
+ To: '+15550001111',
678
+ };
679
+
680
+ // First call — creates the session
681
+ const res1 = await handleVoiceWebhook(makeInboundVoiceRequest(params));
682
+ expect(res1.status).toBe(200);
683
+
684
+ const session1 = getCallSessionByCallSid('CA_inbound_replay_1');
685
+ expect(session1).not.toBeNull();
686
+
687
+ // Second call (replay) — reuses the same session
688
+ const res2 = await handleVoiceWebhook(makeInboundVoiceRequest(params));
689
+ expect(res2.status).toBe(200);
690
+
691
+ const session2 = getCallSessionByCallSid('CA_inbound_replay_1');
692
+ expect(session2).not.toBeNull();
693
+ expect(session2!.id).toBe(session1!.id);
694
+ });
695
+
696
+ test('inbound webhook without CallSid returns 400', async () => {
697
+ const req = makeInboundVoiceRequest({
698
+ From: '+14155551234',
699
+ To: '+15550001111',
700
+ });
701
+
702
+ const res = await handleVoiceWebhook(req);
703
+ expect(res.status).toBe(400);
704
+ });
705
+
706
+ test('inbound webhook with forwarded assistantId creates session with correct assistantId', async () => {
707
+ const req = makeInboundVoiceRequest({
708
+ CallSid: 'CA_inbound_assist_1',
709
+ From: '+14155551234',
710
+ To: '+15550001111',
711
+ });
712
+
713
+ const res = await handleVoiceWebhook(req, 'my-assistant-id');
714
+
715
+ expect(res.status).toBe(200);
716
+ const session = getCallSessionByCallSid('CA_inbound_assist_1');
717
+ expect(session).not.toBeNull();
718
+ expect(session!.assistantId).toBe('my-assistant-id');
719
+ });
720
+
721
+ test('outbound call flow remains non-regressed with callSessionId present', async () => {
722
+ const session = createTestSession('conv-outbound-compat-1', 'CA_outbound_compat_1');
723
+ const req = makeVoiceRequest(session.id, { CallSid: 'CA_outbound_compat_1' });
724
+
725
+ const res = await handleVoiceWebhook(req);
726
+
727
+ expect(res.status).toBe(200);
728
+ const twiml = await res.text();
729
+ expect(twiml).toContain('<ConversationRelay');
730
+ expect(twiml).toContain(`callSessionId=${session.id}`);
731
+ });
732
+ });
577
733
  });
@@ -100,7 +100,7 @@ describe('CLI error shaping', () => {
100
100
 
101
101
  test('routed non-session error with suggestAlternative emits structured JSON', () => {
102
102
  const err = Object.assign(
103
- new Error('OAuth is not configured. Set up OAuth credentials in Settings, or switch to browser strategy.'),
103
+ new Error('OAuth is not configured. Provide your X developer credentials here in the chat to set up OAuth, or switch to browser strategy.'),
104
104
  {
105
105
  pathUsed: 'oauth' as const,
106
106
  suggestAlternative: 'browser' as const,
@@ -110,7 +110,7 @@ describe('CLI error shaping', () => {
110
110
 
111
111
  expect(payload).toEqual({
112
112
  ok: false,
113
- error: 'OAuth is not configured. Set up OAuth credentials in Settings, or switch to browser strategy.',
113
+ error: 'OAuth is not configured. Provide your X developer credentials here in the chat to set up OAuth, or switch to browser strategy.',
114
114
  pathUsed: 'oauth',
115
115
  suggestAlternative: 'browser',
116
116
  });
@@ -1,5 +1,6 @@
1
1
  import { describe, test, expect, mock, beforeEach } from 'bun:test';
2
2
  import { join } from 'node:path';
3
+ import * as realFs from 'node:fs';
3
4
 
4
5
  const TEST_DIR = '/tmp/vellum-user-ref-test';
5
6
 
@@ -12,6 +13,7 @@ let mockFileExists = false;
12
13
  let mockFileContent = '';
13
14
 
14
15
  mock.module('node:fs', () => ({
16
+ ...realFs,
15
17
  existsSync: (path: string) => {
16
18
  if (path === join(TEST_DIR, 'USER.md')) return mockFileExists;
17
19
  return false;