@vellumai/assistant 0.3.5 → 0.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (487) hide show
  1. package/README.md +51 -0
  2. package/eslint.config.mjs +31 -0
  3. package/package.json +1 -1
  4. package/scripts/ipc/check-swift-decoder-drift.ts +4 -1
  5. package/scripts/ipc/generate-swift.ts +18 -2
  6. package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +338 -1
  7. package/src/__tests__/approval-conversation-turn.test.ts +214 -0
  8. package/src/__tests__/browser-manager.test.ts +1 -0
  9. package/src/__tests__/call-conversation-messages.test.ts +130 -0
  10. package/src/__tests__/call-orchestrator.test.ts +752 -271
  11. package/src/__tests__/call-pointer-messages.test.ts +148 -0
  12. package/src/__tests__/call-recovery.test.ts +3 -0
  13. package/src/__tests__/call-routes-http.test.ts +5 -0
  14. package/src/__tests__/call-store.test.ts +3 -0
  15. package/src/__tests__/channel-approval-routes.test.ts +1260 -85
  16. package/src/__tests__/channel-approval.test.ts +37 -0
  17. package/src/__tests__/channel-approvals.test.ts +4 -65
  18. package/src/__tests__/channel-guardian.test.ts +556 -0
  19. package/src/__tests__/channel-readiness-service.test.ts +74 -7
  20. package/src/__tests__/checker.test.ts +14 -7
  21. package/src/__tests__/clarification-resolver.test.ts +44 -24
  22. package/src/__tests__/commit-message-enrichment-service.test.ts +9 -4
  23. package/src/__tests__/computer-use-session-working-dir.test.ts +8 -0
  24. package/src/__tests__/config-schema.test.ts +12 -7
  25. package/src/__tests__/context-window-manager.test.ts +30 -2
  26. package/src/__tests__/contradiction-checker.test.ts +20 -5
  27. package/src/__tests__/credential-security-invariants.test.ts +6 -2
  28. package/src/__tests__/db-migration-rollback.test.ts +752 -0
  29. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -0
  30. package/src/__tests__/fuzzy-match-property.test.ts +5 -5
  31. package/src/__tests__/guardian-action-store.test.ts +123 -0
  32. package/src/__tests__/guardian-action-sweep.test.ts +277 -0
  33. package/src/__tests__/guardian-dispatch.test.ts +389 -0
  34. package/src/__tests__/guardian-question-copy.test.ts +47 -0
  35. package/src/__tests__/handlers-telegram-config.test.ts +4 -2
  36. package/src/__tests__/handlers-twilio-config.test.ts +126 -0
  37. package/src/__tests__/intent-routing.test.ts +2 -0
  38. package/src/__tests__/ipc-snapshot.test.ts +228 -1
  39. package/src/__tests__/memory-upsert-concurrency.test.ts +828 -0
  40. package/src/__tests__/model-intents.test.ts +96 -0
  41. package/src/__tests__/no-direct-anthropic-sdk-imports.test.ts +42 -0
  42. package/src/__tests__/oauth2-gateway-transport.test.ts +130 -0
  43. package/src/__tests__/onboarding-starter-tasks.test.ts +2 -0
  44. package/src/__tests__/provider-commit-message-generator.test.ts +89 -13
  45. package/src/__tests__/provider-error-scenarios.test.ts +621 -0
  46. package/src/__tests__/provider-fail-open-selection.test.ts +119 -0
  47. package/src/__tests__/qdrant-manager.test.ts +27 -20
  48. package/src/__tests__/relay-server.test.ts +779 -40
  49. package/src/__tests__/run-orchestrator-assistant-events.test.ts +2 -0
  50. package/src/__tests__/run-orchestrator.test.ts +20 -4
  51. package/src/__tests__/runtime-runs-http.test.ts +17 -1
  52. package/src/__tests__/runtime-runs.test.ts +16 -0
  53. package/src/__tests__/schedule-store.test.ts +18 -4
  54. package/src/__tests__/scheduler-recurrence.test.ts +13 -4
  55. package/src/__tests__/session-abort-tool-results.test.ts +6 -0
  56. package/src/__tests__/session-agent-loop.test.ts +857 -0
  57. package/src/__tests__/session-conflict-gate.test.ts +6 -0
  58. package/src/__tests__/session-pre-run-repair.test.ts +6 -0
  59. package/src/__tests__/session-profile-injection.test.ts +6 -0
  60. package/src/__tests__/session-provider-retry-repair.test.ts +6 -0
  61. package/src/__tests__/session-queue.test.ts +6 -0
  62. package/src/__tests__/session-runtime-assembly.test.ts +237 -13
  63. package/src/__tests__/session-slash-known.test.ts +6 -0
  64. package/src/__tests__/session-slash-queue.test.ts +6 -0
  65. package/src/__tests__/session-slash-unknown.test.ts +6 -0
  66. package/src/__tests__/session-surfaces-task-progress.test.ts +2 -0
  67. package/src/__tests__/session-tool-setup-app-refresh.test.ts +1 -0
  68. package/src/__tests__/session-tool-setup-memory-scope.test.ts +1 -0
  69. package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +1 -0
  70. package/src/__tests__/session-workspace-injection.test.ts +6 -0
  71. package/src/__tests__/session-workspace-tool-tracking.test.ts +6 -0
  72. package/src/__tests__/skills.test.ts +2 -0
  73. package/src/__tests__/sms-messaging-provider.test.ts +2 -1
  74. package/src/__tests__/starter-task-flow.test.ts +2 -0
  75. package/src/__tests__/swarm-dag-pathological.test.ts +535 -0
  76. package/src/__tests__/system-prompt.test.ts +2 -0
  77. package/src/__tests__/task-management-tools.test.ts +2 -2
  78. package/src/__tests__/task-runner.test.ts +14 -4
  79. package/src/__tests__/terminal-tools.test.ts +25 -19
  80. package/src/__tests__/tool-execution-abort-cleanup.test.ts +545 -0
  81. package/src/__tests__/tool-executor-shell-integration.test.ts +11 -11
  82. package/src/__tests__/tool-executor.test.ts +23 -24
  83. package/src/__tests__/trust-store.test.ts +3 -3
  84. package/src/__tests__/twilio-rest.test.ts +29 -0
  85. package/src/__tests__/twilio-routes-elevenlabs.test.ts +3 -0
  86. package/src/__tests__/twilio-routes-twiml.test.ts +11 -0
  87. package/src/__tests__/twilio-routes.test.ts +141 -21
  88. package/src/__tests__/user-reference.test.ts +2 -0
  89. package/src/__tests__/voice-quality.test.ts +222 -0
  90. package/src/__tests__/web-search.test.ts +45 -29
  91. package/src/agent/loop.ts +1 -1
  92. package/src/agent-heartbeat/agent-heartbeat-service.ts +2 -10
  93. package/src/amazon/client.ts +1418 -0
  94. package/src/amazon/request-extractor.ts +135 -0
  95. package/src/amazon/session.ts +109 -0
  96. package/src/autonomy/autonomy-store.ts +5 -5
  97. package/src/browser-extension-relay/client.ts +124 -0
  98. package/src/browser-extension-relay/protocol.ts +63 -0
  99. package/src/browser-extension-relay/server.ts +177 -0
  100. package/src/bundler/app-bundler.ts +3 -3
  101. package/src/bundler/bundle-signer.ts +1 -1
  102. package/src/bundler/signature-verifier.ts +1 -1
  103. package/src/calls/call-conversation-messages.ts +33 -0
  104. package/src/calls/call-domain.ts +106 -5
  105. package/src/calls/call-orchestrator.ts +252 -54
  106. package/src/calls/call-pointer-messages.ts +53 -0
  107. package/src/calls/call-recovery.ts +3 -8
  108. package/src/calls/call-store.ts +69 -87
  109. package/src/calls/elevenlabs-config.ts +3 -2
  110. package/src/calls/guardian-action-sweep.ts +105 -0
  111. package/src/calls/guardian-dispatch.ts +203 -0
  112. package/src/calls/guardian-question-copy.ts +133 -0
  113. package/src/calls/relay-server.ts +466 -8
  114. package/src/calls/speaker-identification.ts +1 -1
  115. package/src/calls/twilio-config.ts +7 -5
  116. package/src/calls/twilio-provider.ts +6 -4
  117. package/src/calls/twilio-rest.ts +40 -15
  118. package/src/calls/twilio-routes.ts +60 -45
  119. package/src/calls/types.ts +3 -1
  120. package/src/channels/types.ts +25 -0
  121. package/src/cli/amazon.ts +815 -0
  122. package/src/cli/config-commands.ts +2 -2
  123. package/src/cli/core-commands.ts +4 -3
  124. package/src/cli/influencer.ts +244 -0
  125. package/src/cli/map.ts +89 -6
  126. package/src/cli.ts +1 -1
  127. package/src/config/agent-schema.ts +171 -0
  128. package/src/config/bundled-skills/amazon/SKILL.md +127 -0
  129. package/src/config/bundled-skills/amazon/icon.svg +13 -0
  130. package/src/config/bundled-skills/api-mapping/SKILL.md +78 -0
  131. package/src/config/bundled-skills/browser/SKILL.md +1 -0
  132. package/src/config/bundled-skills/browser/TOOLS.json +17 -0
  133. package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +25 -0
  134. package/src/config/bundled-skills/doordash/SKILL.md +51 -51
  135. package/src/config/bundled-skills/email-setup/SKILL.md +14 -5
  136. package/src/config/bundled-skills/google-oauth-setup/SKILL.md +183 -0
  137. package/src/config/bundled-skills/influencer/SKILL.md +144 -0
  138. package/src/config/bundled-skills/macos-automation/icon.svg +12 -0
  139. package/src/config/bundled-skills/media-processing/SKILL.md +72 -95
  140. package/src/config/bundled-skills/media-processing/TOOLS.json +57 -147
  141. package/src/config/bundled-skills/media-processing/__tests__/concurrency-pool.test.ts +77 -0
  142. package/src/config/bundled-skills/media-processing/__tests__/cost-tracker.test.ts +69 -0
  143. package/src/config/bundled-skills/media-processing/__tests__/preprocess.test.ts +303 -0
  144. package/src/config/bundled-skills/media-processing/services/concurrency-pool.ts +55 -0
  145. package/src/config/bundled-skills/media-processing/services/cost-tracker.ts +86 -0
  146. package/src/config/bundled-skills/media-processing/services/gemini-map.ts +339 -0
  147. package/src/config/bundled-skills/media-processing/services/preprocess.ts +551 -0
  148. package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +7 -9
  149. package/src/config/bundled-skills/media-processing/services/reduce.ts +197 -0
  150. package/src/config/bundled-skills/media-processing/tools/analyze-keyframes.ts +88 -253
  151. package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +22 -153
  152. package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -2
  153. package/src/config/bundled-skills/media-processing/tools/media-diagnostics.ts +28 -51
  154. package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +35 -270
  155. package/src/config/bundled-skills/messaging/SKILL.md +12 -2
  156. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +4 -7
  157. package/src/config/bundled-skills/messaging/tools/messaging-reply.ts +2 -1
  158. package/src/config/bundled-skills/phone-calls/SKILL.md +86 -21
  159. package/src/config/bundled-skills/twitter/icon.svg +14 -0
  160. package/src/config/bundled-tool-registry.ts +310 -0
  161. package/src/config/calls-schema.ts +181 -0
  162. package/src/config/core-schema.ts +309 -0
  163. package/src/config/defaults.ts +27 -3
  164. package/src/config/env-registry.ts +169 -0
  165. package/src/config/env.ts +175 -0
  166. package/src/config/loader.ts +6 -6
  167. package/src/config/memory-schema.ts +528 -0
  168. package/src/config/sandbox-schema.ts +55 -0
  169. package/src/config/schema.ts +157 -1138
  170. package/src/config/skill-state.ts +1 -1
  171. package/src/config/skills-schema.ts +32 -0
  172. package/src/config/skills.ts +35 -24
  173. package/src/config/system-prompt.ts +107 -56
  174. package/src/config/templates/SOUL.md +1 -1
  175. package/src/config/types.ts +1 -0
  176. package/src/config/user-reference.ts +4 -9
  177. package/src/config/vellum-skills/catalog.json +0 -7
  178. package/src/config/vellum-skills/chatgpt-import/tools/chatgpt-import.ts +5 -1
  179. package/src/config/vellum-skills/slack-oauth-setup/SKILL.md +1 -0
  180. package/src/config/vellum-skills/sms-setup/SKILL.md +112 -14
  181. package/src/context/window-manager.ts +27 -7
  182. package/src/daemon/approval-generators.ts +186 -0
  183. package/src/daemon/approved-devices-store.ts +140 -0
  184. package/src/daemon/assistant-attachments.ts +1 -1
  185. package/src/daemon/classifier.ts +35 -32
  186. package/src/daemon/config-watcher.ts +1 -1
  187. package/src/daemon/daemon-control.ts +254 -0
  188. package/src/daemon/handlers/apps.ts +2 -3
  189. package/src/daemon/handlers/config-channels.ts +158 -0
  190. package/src/daemon/handlers/config-inbox.ts +540 -0
  191. package/src/daemon/handlers/config-ingress.ts +231 -0
  192. package/src/daemon/handlers/config-integrations.ts +258 -0
  193. package/src/daemon/handlers/config-model.ts +143 -0
  194. package/src/daemon/handlers/config-parental.ts +163 -0
  195. package/src/daemon/handlers/config-scheduling.ts +172 -0
  196. package/src/daemon/handlers/config-slack.ts +92 -0
  197. package/src/daemon/handlers/config-telegram.ts +301 -0
  198. package/src/daemon/handlers/config-tools.ts +177 -0
  199. package/src/daemon/handlers/config-trust.ts +104 -0
  200. package/src/daemon/handlers/config-twilio.ts +1080 -0
  201. package/src/daemon/handlers/config.ts +53 -2463
  202. package/src/daemon/handlers/diagnostics.ts +1 -1
  203. package/src/daemon/handlers/dictation.ts +4 -6
  204. package/src/daemon/handlers/documents.ts +18 -32
  205. package/src/daemon/handlers/index.ts +9 -0
  206. package/src/daemon/handlers/misc.ts +3 -5
  207. package/src/daemon/handlers/pairing.ts +98 -0
  208. package/src/daemon/handlers/sessions.ts +74 -5
  209. package/src/daemon/handlers/shared.ts +3 -1
  210. package/src/daemon/handlers/skills.ts +1 -1
  211. package/src/daemon/handlers/twitter-auth.ts +2 -0
  212. package/src/daemon/handlers/work-items.ts +2 -2
  213. package/src/daemon/handlers/workspace-files.ts +4 -3
  214. package/src/daemon/install-cli-launchers.ts +113 -0
  215. package/src/daemon/ipc-contract/apps.ts +356 -0
  216. package/src/daemon/ipc-contract/browser.ts +74 -0
  217. package/src/daemon/ipc-contract/computer-use.ts +151 -0
  218. package/src/daemon/ipc-contract/diagnostics.ts +56 -0
  219. package/src/daemon/ipc-contract/documents.ts +74 -0
  220. package/src/daemon/ipc-contract/inbox.ts +209 -0
  221. package/src/daemon/ipc-contract/integrations.ts +284 -0
  222. package/src/daemon/ipc-contract/memory.ts +48 -0
  223. package/src/daemon/ipc-contract/messages.ts +211 -0
  224. package/src/daemon/ipc-contract/pairing.ts +45 -0
  225. package/src/daemon/ipc-contract/parental-control.ts +95 -0
  226. package/src/daemon/ipc-contract/schedules.ts +97 -0
  227. package/src/daemon/ipc-contract/sessions.ts +321 -0
  228. package/src/daemon/ipc-contract/shared.ts +42 -0
  229. package/src/daemon/ipc-contract/skills.ts +120 -0
  230. package/src/daemon/ipc-contract/subagents.ts +58 -0
  231. package/src/daemon/ipc-contract/surfaces.ts +250 -0
  232. package/src/daemon/ipc-contract/trust.ts +60 -0
  233. package/src/daemon/ipc-contract/work-items.ts +225 -0
  234. package/src/daemon/ipc-contract/workspace.ts +113 -0
  235. package/src/daemon/ipc-contract-inventory.json +62 -0
  236. package/src/daemon/ipc-contract-inventory.ts +55 -29
  237. package/src/daemon/ipc-contract.ts +227 -2527
  238. package/src/daemon/ipc-protocol.ts +1 -1
  239. package/src/daemon/ipc-validate.ts +7 -0
  240. package/src/daemon/lifecycle.ts +97 -379
  241. package/src/daemon/pairing-store.ts +177 -0
  242. package/src/daemon/providers-setup.ts +43 -0
  243. package/src/daemon/ride-shotgun-handler.ts +67 -2
  244. package/src/daemon/server.ts +60 -44
  245. package/src/daemon/session-agent-loop-handlers.ts +421 -0
  246. package/src/daemon/session-agent-loop.ts +113 -275
  247. package/src/daemon/session-dynamic-profile.ts +1 -1
  248. package/src/daemon/session-history.ts +1 -1
  249. package/src/daemon/session-media-retry.ts +1 -1
  250. package/src/daemon/session-messaging.ts +37 -2
  251. package/src/daemon/session-notifiers.ts +5 -25
  252. package/src/daemon/session-process.ts +99 -59
  253. package/src/daemon/session-queue-manager.ts +98 -4
  254. package/src/daemon/session-runtime-assembly.ts +149 -15
  255. package/src/daemon/session-surfaces.ts +26 -4
  256. package/src/daemon/session-tool-setup.ts +28 -30
  257. package/src/daemon/session-workspace.ts +1 -1
  258. package/src/daemon/session.ts +24 -1
  259. package/src/daemon/shutdown-handlers.ts +122 -0
  260. package/src/daemon/trace-emitter.ts +1 -1
  261. package/src/daemon/watch-handler.ts +36 -33
  262. package/src/doordash/cart-queries.ts +787 -0
  263. package/src/doordash/client.ts +144 -127
  264. package/src/doordash/order-queries.ts +85 -0
  265. package/src/doordash/queries.ts +10 -1308
  266. package/src/doordash/search-queries.ts +203 -0
  267. package/src/doordash/session.ts +3 -2
  268. package/src/doordash/store-queries.ts +246 -0
  269. package/src/doordash/types.ts +367 -0
  270. package/src/email/providers/agentmail.ts +2 -1
  271. package/src/email/providers/index.ts +3 -2
  272. package/src/email/service.ts +3 -2
  273. package/src/errors.ts +43 -0
  274. package/src/home-base/prebuilt/seed.ts +1 -1
  275. package/src/hooks/cli.ts +6 -5
  276. package/src/hooks/config.ts +6 -8
  277. package/src/hooks/discovery.ts +6 -5
  278. package/src/hooks/manager.ts +4 -3
  279. package/src/hooks/runner.ts +2 -2
  280. package/src/hooks/templates.ts +5 -5
  281. package/src/inbound/public-ingress-urls.ts +3 -1
  282. package/src/index.ts +4 -2
  283. package/src/influencer/client.ts +1104 -0
  284. package/src/instrument.ts +4 -3
  285. package/src/logfire.ts +4 -3
  286. package/src/memory/admin.ts +25 -35
  287. package/src/memory/attachments-store.ts +4 -7
  288. package/src/memory/channel-delivery-store.ts +30 -1
  289. package/src/memory/channel-guardian-store.ts +200 -1
  290. package/src/memory/clarification-resolver.ts +37 -33
  291. package/src/memory/conflict-store.ts +67 -61
  292. package/src/memory/contradiction-checker.ts +141 -117
  293. package/src/memory/conversation-store.ts +335 -51
  294. package/src/memory/db-connection.ts +27 -4
  295. package/src/memory/db-init.ts +121 -4
  296. package/src/memory/db.ts +14 -1
  297. package/src/memory/embedding-backend.ts +27 -5
  298. package/src/memory/embedding-ollama.ts +2 -1
  299. package/src/memory/entity-extractor.ts +38 -35
  300. package/src/memory/guardian-action-store.ts +430 -0
  301. package/src/memory/inbox-escalation-projection.ts +59 -0
  302. package/src/memory/inbox-thread-store.ts +218 -0
  303. package/src/memory/ingress-invite-store.ts +338 -0
  304. package/src/memory/ingress-member-store.ts +350 -0
  305. package/src/memory/items-extractor.ts +91 -97
  306. package/src/memory/job-handlers/index-maintenance.ts +3 -3
  307. package/src/memory/job-handlers/media-processing.ts +11 -42
  308. package/src/memory/job-handlers/summarization.ts +32 -26
  309. package/src/memory/job-utils.ts +3 -10
  310. package/src/memory/jobs-store.ts +6 -9
  311. package/src/memory/jobs-worker.ts +51 -36
  312. package/src/memory/migrations/001-job-deferrals.ts +45 -0
  313. package/src/memory/migrations/002-tool-invocations-fk.ts +43 -0
  314. package/src/memory/migrations/003-memory-fts-backfill.ts +24 -0
  315. package/src/memory/migrations/004-entity-relation-dedup.ts +87 -0
  316. package/src/memory/migrations/005-fingerprint-scope-unique.ts +80 -0
  317. package/src/memory/migrations/006-scope-salted-fingerprints.ts +62 -0
  318. package/src/memory/migrations/007-assistant-id-to-self.ts +254 -0
  319. package/src/memory/migrations/008-remove-assistant-id-columns.ts +208 -0
  320. package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +83 -0
  321. package/src/memory/migrations/010-ext-conv-bindings-channel-chat-unique.ts +56 -0
  322. package/src/memory/migrations/011-call-sessions-provider-sid-dedup.ts +63 -0
  323. package/src/memory/migrations/012-call-sessions-add-initiated-from.ts +19 -0
  324. package/src/memory/migrations/013-guardian-action-tables.ts +68 -0
  325. package/src/memory/migrations/014-backfill-inbox-thread-state.ts +76 -0
  326. package/src/memory/migrations/015-drop-active-search-index.ts +27 -0
  327. package/src/memory/migrations/016-memory-segments-indexes.ts +11 -0
  328. package/src/memory/migrations/017-memory-items-indexes.ts +12 -0
  329. package/src/memory/migrations/018-remaining-table-indexes.ts +13 -0
  330. package/src/memory/migrations/index.ts +24 -0
  331. package/src/memory/migrations/registry.ts +79 -0
  332. package/src/memory/migrations/validate-migration-state.ts +69 -0
  333. package/src/memory/qdrant-manager.ts +49 -8
  334. package/src/memory/query-builder.ts +1 -1
  335. package/src/memory/raw-query.ts +119 -0
  336. package/src/memory/recall-cache.ts +4 -1
  337. package/src/memory/retriever.ts +163 -47
  338. package/src/memory/schema-migration.ts +25 -984
  339. package/src/memory/schema.ts +130 -7
  340. package/src/memory/search/entity.ts +10 -19
  341. package/src/memory/search/lexical.ts +81 -52
  342. package/src/memory/search/ranking.ts +21 -22
  343. package/src/memory/search/semantic.ts +157 -19
  344. package/src/memory/shared-app-links-store.ts +4 -5
  345. package/src/memory/validation.ts +19 -0
  346. package/src/messaging/draft-store.ts +5 -6
  347. package/src/messaging/providers/sms/adapter.ts +3 -6
  348. package/src/messaging/providers/telegram-bot/adapter.ts +2 -5
  349. package/src/messaging/providers/whatsapp/adapter.ts +136 -0
  350. package/src/messaging/providers/whatsapp/client.ts +67 -0
  351. package/src/messaging/style-analyzer.ts +5 -4
  352. package/src/messaging/thread-summarizer.ts +61 -69
  353. package/src/messaging/triage-engine.ts +62 -71
  354. package/src/migrations/config-merge.ts +53 -0
  355. package/src/migrations/data-layout.ts +68 -0
  356. package/src/migrations/data-merge.ts +33 -0
  357. package/src/migrations/hooks-merge.ts +90 -0
  358. package/src/migrations/index.ts +6 -0
  359. package/src/migrations/log.ts +23 -0
  360. package/src/migrations/skills-merge.ts +33 -0
  361. package/src/migrations/workspace-layout.ts +79 -0
  362. package/src/permissions/checker.ts +126 -11
  363. package/src/permissions/prompter.ts +14 -0
  364. package/src/permissions/shell-identity.ts +31 -1
  365. package/src/permissions/trust-store.ts +21 -1
  366. package/src/providers/anthropic/client.ts +4 -4
  367. package/src/providers/failover.ts +2 -2
  368. package/src/providers/model-intents.ts +70 -0
  369. package/src/providers/ollama/client.ts +2 -1
  370. package/src/providers/provider-send-message.ts +176 -0
  371. package/src/providers/registry.ts +71 -30
  372. package/src/providers/retry.ts +35 -1
  373. package/src/providers/types.ts +12 -1
  374. package/src/runtime/approval-conversation-turn.ts +97 -0
  375. package/src/runtime/approval-message-composer.ts +115 -5
  376. package/src/runtime/assistant-event-hub.ts +3 -1
  377. package/src/runtime/channel-approval-parser.ts +36 -2
  378. package/src/runtime/channel-approvals.ts +0 -21
  379. package/src/runtime/channel-guardian-service.ts +48 -7
  380. package/src/runtime/channel-readiness-service.ts +160 -34
  381. package/src/runtime/channel-readiness-types.ts +10 -4
  382. package/src/runtime/channel-retry-sweep.ts +184 -0
  383. package/src/runtime/guardian-context-resolver.ts +108 -0
  384. package/src/runtime/http-server.ts +289 -745
  385. package/src/runtime/http-types.ts +56 -3
  386. package/src/runtime/middleware/auth.ts +116 -0
  387. package/src/runtime/middleware/error-handler.ts +33 -0
  388. package/src/runtime/middleware/twilio-validation.ts +127 -0
  389. package/src/runtime/routes/app-routes.ts +1 -1
  390. package/src/runtime/routes/call-routes.ts +49 -6
  391. package/src/runtime/routes/channel-delivery-routes.ts +170 -0
  392. package/src/runtime/routes/channel-guardian-routes.ts +1191 -0
  393. package/src/runtime/routes/channel-inbound-routes.ts +1152 -0
  394. package/src/runtime/routes/channel-route-shared.ts +144 -0
  395. package/src/runtime/routes/channel-routes.ts +32 -1634
  396. package/src/runtime/routes/conversation-routes.ts +50 -7
  397. package/src/runtime/routes/events-routes.ts +2 -2
  398. package/src/runtime/routes/identity-routes.ts +126 -0
  399. package/src/runtime/routes/pairing-routes.ts +144 -0
  400. package/src/runtime/routes/run-routes.ts +15 -1
  401. package/src/runtime/run-orchestrator.ts +52 -34
  402. package/src/schedule/schedule-store.ts +36 -32
  403. package/src/schedule/scheduler.ts +3 -3
  404. package/src/security/encrypted-store.ts +5 -7
  405. package/src/security/oauth2.ts +45 -15
  406. package/src/security/parental-control-store.ts +183 -0
  407. package/src/security/secret-allowlist.ts +4 -3
  408. package/src/security/secret-scanner.ts +5 -5
  409. package/src/security/secure-keys.ts +1 -1
  410. package/src/security/token-manager.ts +3 -2
  411. package/src/services/vercel-deploy.ts +6 -2
  412. package/src/skills/tool-manifest.ts +3 -3
  413. package/src/skills/vellum-catalog-remote.ts +75 -16
  414. package/src/slack/slack-webhook.ts +2 -1
  415. package/src/swarm/orchestrator.ts +92 -1
  416. package/src/swarm/router-planner.ts +6 -9
  417. package/src/swarm/worker-prompts.ts +9 -12
  418. package/src/tasks/task-compiler.ts +19 -28
  419. package/src/tasks/task-runner.ts +1 -1
  420. package/src/tools/assets/search.ts +15 -14
  421. package/src/tools/browser/__tests__/auth-detector.test.ts +1 -0
  422. package/src/tools/browser/auto-navigate.ts +1 -0
  423. package/src/tools/browser/browser-execution.ts +13 -1
  424. package/src/tools/browser/browser-manager.ts +119 -4
  425. package/src/tools/browser/network-recorder.ts +5 -0
  426. package/src/tools/credentials/broker.ts +11 -2
  427. package/src/tools/credentials/metadata-store.ts +18 -14
  428. package/src/tools/credentials/post-connect-hooks.ts +61 -0
  429. package/src/tools/credentials/vault.ts +49 -23
  430. package/src/tools/executor.ts +80 -18
  431. package/src/tools/host-terminal/cli-discover.ts +1 -1
  432. package/src/tools/network/script-proxy/http-forwarder.ts +1 -1
  433. package/src/tools/network/script-proxy/mitm-handler.ts +1 -1
  434. package/src/tools/network/script-proxy/server.ts +1 -1
  435. package/src/tools/network/script-proxy/session-manager.ts +6 -5
  436. package/src/tools/network/web-fetch.ts +18 -2
  437. package/src/tools/network/web-search.ts +7 -3
  438. package/src/tools/reminder/reminder-store.ts +14 -15
  439. package/src/tools/schedule/create.ts +1 -0
  440. package/src/tools/schedule/list.ts +2 -1
  441. package/src/tools/shared/filesystem/file-ops-service.ts +5 -7
  442. package/src/tools/skills/skill-script-runner.ts +24 -9
  443. package/src/tools/skills/skill-tool-factory.ts +1 -0
  444. package/src/tools/tasks/work-item-enqueue.ts +2 -2
  445. package/src/tools/terminal/evaluate-typescript.ts +21 -12
  446. package/src/tools/terminal/parser.ts +50 -0
  447. package/src/tools/watcher/delete.ts +6 -0
  448. package/src/tools/weather/service.ts +1 -1
  449. package/src/twitter/client.ts +190 -24
  450. package/src/twitter/session.ts +4 -3
  451. package/src/util/clipboard.ts +1 -1
  452. package/src/util/errors.ts +65 -8
  453. package/src/util/fs.ts +40 -0
  454. package/src/util/json.ts +10 -0
  455. package/src/util/log-redact.ts +189 -0
  456. package/src/util/logger.ts +25 -18
  457. package/src/util/object.ts +3 -0
  458. package/src/util/platform.ts +72 -365
  459. package/src/util/pricing.ts +1 -1
  460. package/src/util/promise-guard.ts +1 -1
  461. package/src/util/retry.ts +19 -0
  462. package/src/util/row-mapper.ts +79 -0
  463. package/src/util/silently.ts +21 -0
  464. package/src/watcher/engine.ts +5 -1
  465. package/src/watcher/provider-types.ts +20 -0
  466. package/src/watcher/providers/github.ts +156 -0
  467. package/src/watcher/providers/gmail.ts +1 -0
  468. package/src/watcher/providers/google-calendar.ts +1 -0
  469. package/src/watcher/providers/linear.ts +460 -0
  470. package/src/watcher/providers/slack.ts +1 -0
  471. package/src/work-items/work-item-runner.ts +1 -1
  472. package/src/workspace/git-service.ts +1 -1
  473. package/src/workspace/provider-commit-message-generator.ts +51 -22
  474. package/src/__tests__/call-bridge.test.ts +0 -517
  475. package/src/__tests__/session-process-bridge.test.ts +0 -244
  476. package/src/calls/call-bridge.ts +0 -168
  477. package/src/config/bundled-skills/media-processing/services/capability-registry.ts +0 -137
  478. package/src/config/bundled-skills/media-processing/services/event-detection-service.ts +0 -280
  479. package/src/config/bundled-skills/media-processing/services/feedback-aggregation.ts +0 -144
  480. package/src/config/bundled-skills/media-processing/services/feedback-store.ts +0 -136
  481. package/src/config/bundled-skills/media-processing/services/retrieval-service.ts +0 -95
  482. package/src/config/bundled-skills/media-processing/services/timeline-service.ts +0 -267
  483. package/src/config/bundled-skills/media-processing/tools/detect-events.ts +0 -110
  484. package/src/config/bundled-skills/media-processing/tools/recalibrate.ts +0 -235
  485. package/src/config/bundled-skills/media-processing/tools/select-tracking-profile.ts +0 -142
  486. package/src/config/bundled-skills/media-processing/tools/submit-feedback.ts +0 -150
  487. package/src/config/vellum-skills/google-oauth-setup/SKILL.md +0 -199
@@ -1,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,6 +97,7 @@ 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';
@@ -111,10 +125,16 @@ 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
  }
@@ -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(() => {
@@ -417,15 +446,9 @@ describe('twilio webhook routes', () => {
417
446
  });
418
447
 
419
448
  describe('buildWelcomeGreeting', () => {
420
- test('builds a contextual opener from task text', () => {
449
+ test('returns empty by default so orchestrator drives first opener', () => {
421
450
  const greeting = buildWelcomeGreeting('check store hours for tomorrow');
422
- expect(greeting).toBe('Hello, I am calling about check store hours for tomorrow. Is now a good time to talk?');
423
- });
424
-
425
- test('ignores appended Context block when building opener', () => {
426
- const greeting = buildWelcomeGreeting('check store hours\n\nContext: Caller asked by email');
427
- expect(greeting).toBe('Hello, I am calling about check store hours. Is now a good time to talk?');
428
- expect(greeting).not.toContain('Context:');
451
+ expect(greeting).toBe('');
429
452
  });
430
453
 
431
454
  test('uses configured greeting override when provided', () => {
@@ -465,7 +488,7 @@ describe('twilio webhook routes', () => {
465
488
  expect(twiml).toContain('wss://gateway.example.com/v1/calls/relay');
466
489
  });
467
490
 
468
- test('TwiML welcome greeting is task-aware by default', async () => {
491
+ test('TwiML omits welcome greeting by default so call opener is model-driven', async () => {
469
492
  const session = createTestSession(
470
493
  'conv-twiml-3',
471
494
  'CA_twiml_3',
@@ -477,10 +500,19 @@ describe('twilio webhook routes', () => {
477
500
 
478
501
  expect(res.status).toBe(200);
479
502
  const twiml = await res.text();
480
- expect(twiml).toContain(
481
- 'welcomeGreeting="Hello, I am calling about confirm appointment time. Is now a good time to talk?"',
482
- );
483
- expect(twiml).not.toContain('Hello, how can I help you today?');
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"');
484
516
  });
485
517
  });
486
518
 
@@ -610,4 +642,92 @@ describe('twilio webhook routes', () => {
610
642
  expect(events2.filter(e => e.eventType === 'call_ended').length).toBe(1);
611
643
  });
612
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
+ });
613
733
  });
@@ -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;