@vellumai/assistant 0.3.5 → 0.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (486) hide show
  1. package/README.md +51 -0
  2. package/eslint.config.mjs +31 -0
  3. package/package.json +1 -1
  4. package/scripts/ipc/check-swift-decoder-drift.ts +4 -1
  5. package/scripts/ipc/generate-swift.ts +18 -2
  6. package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +338 -1
  7. package/src/__tests__/approval-conversation-turn.test.ts +214 -0
  8. package/src/__tests__/browser-manager.test.ts +1 -0
  9. package/src/__tests__/call-conversation-messages.test.ts +130 -0
  10. package/src/__tests__/call-orchestrator.test.ts +752 -271
  11. package/src/__tests__/call-pointer-messages.test.ts +148 -0
  12. package/src/__tests__/call-recovery.test.ts +3 -0
  13. package/src/__tests__/call-routes-http.test.ts +5 -0
  14. package/src/__tests__/call-store.test.ts +3 -0
  15. package/src/__tests__/channel-approval-routes.test.ts +1260 -85
  16. package/src/__tests__/channel-approval.test.ts +37 -0
  17. package/src/__tests__/channel-approvals.test.ts +4 -65
  18. package/src/__tests__/channel-guardian.test.ts +556 -0
  19. package/src/__tests__/channel-readiness-service.test.ts +74 -7
  20. package/src/__tests__/checker.test.ts +14 -7
  21. package/src/__tests__/clarification-resolver.test.ts +44 -24
  22. package/src/__tests__/commit-message-enrichment-service.test.ts +9 -4
  23. package/src/__tests__/computer-use-session-working-dir.test.ts +8 -0
  24. package/src/__tests__/config-schema.test.ts +12 -7
  25. package/src/__tests__/context-window-manager.test.ts +30 -2
  26. package/src/__tests__/contradiction-checker.test.ts +20 -5
  27. package/src/__tests__/credential-security-invariants.test.ts +6 -2
  28. package/src/__tests__/db-migration-rollback.test.ts +752 -0
  29. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -0
  30. package/src/__tests__/fuzzy-match-property.test.ts +5 -5
  31. package/src/__tests__/guardian-action-store.test.ts +123 -0
  32. package/src/__tests__/guardian-action-sweep.test.ts +277 -0
  33. package/src/__tests__/guardian-dispatch.test.ts +389 -0
  34. package/src/__tests__/guardian-question-copy.test.ts +47 -0
  35. package/src/__tests__/handlers-telegram-config.test.ts +4 -2
  36. package/src/__tests__/handlers-twilio-config.test.ts +126 -0
  37. package/src/__tests__/intent-routing.test.ts +2 -0
  38. package/src/__tests__/ipc-snapshot.test.ts +228 -1
  39. package/src/__tests__/memory-upsert-concurrency.test.ts +828 -0
  40. package/src/__tests__/model-intents.test.ts +96 -0
  41. package/src/__tests__/no-direct-anthropic-sdk-imports.test.ts +42 -0
  42. package/src/__tests__/oauth2-gateway-transport.test.ts +130 -0
  43. package/src/__tests__/onboarding-starter-tasks.test.ts +2 -0
  44. package/src/__tests__/provider-commit-message-generator.test.ts +89 -13
  45. package/src/__tests__/provider-error-scenarios.test.ts +621 -0
  46. package/src/__tests__/provider-fail-open-selection.test.ts +119 -0
  47. package/src/__tests__/qdrant-manager.test.ts +27 -20
  48. package/src/__tests__/relay-server.test.ts +779 -40
  49. package/src/__tests__/run-orchestrator-assistant-events.test.ts +2 -0
  50. package/src/__tests__/run-orchestrator.test.ts +20 -4
  51. package/src/__tests__/runtime-runs-http.test.ts +17 -1
  52. package/src/__tests__/runtime-runs.test.ts +16 -0
  53. package/src/__tests__/schedule-store.test.ts +18 -4
  54. package/src/__tests__/scheduler-recurrence.test.ts +13 -4
  55. package/src/__tests__/session-abort-tool-results.test.ts +6 -0
  56. package/src/__tests__/session-agent-loop.test.ts +857 -0
  57. package/src/__tests__/session-conflict-gate.test.ts +6 -0
  58. package/src/__tests__/session-pre-run-repair.test.ts +6 -0
  59. package/src/__tests__/session-profile-injection.test.ts +6 -0
  60. package/src/__tests__/session-provider-retry-repair.test.ts +6 -0
  61. package/src/__tests__/session-queue.test.ts +6 -0
  62. package/src/__tests__/session-runtime-assembly.test.ts +237 -13
  63. package/src/__tests__/session-slash-known.test.ts +6 -0
  64. package/src/__tests__/session-slash-queue.test.ts +6 -0
  65. package/src/__tests__/session-slash-unknown.test.ts +6 -0
  66. package/src/__tests__/session-surfaces-task-progress.test.ts +2 -0
  67. package/src/__tests__/session-tool-setup-app-refresh.test.ts +1 -0
  68. package/src/__tests__/session-tool-setup-memory-scope.test.ts +1 -0
  69. package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +1 -0
  70. package/src/__tests__/session-workspace-injection.test.ts +6 -0
  71. package/src/__tests__/session-workspace-tool-tracking.test.ts +6 -0
  72. package/src/__tests__/skills.test.ts +2 -0
  73. package/src/__tests__/sms-messaging-provider.test.ts +2 -1
  74. package/src/__tests__/starter-task-flow.test.ts +2 -0
  75. package/src/__tests__/swarm-dag-pathological.test.ts +535 -0
  76. package/src/__tests__/system-prompt.test.ts +2 -0
  77. package/src/__tests__/task-management-tools.test.ts +2 -2
  78. package/src/__tests__/task-runner.test.ts +14 -4
  79. package/src/__tests__/terminal-tools.test.ts +25 -19
  80. package/src/__tests__/tool-execution-abort-cleanup.test.ts +545 -0
  81. package/src/__tests__/tool-executor-shell-integration.test.ts +11 -11
  82. package/src/__tests__/tool-executor.test.ts +23 -24
  83. package/src/__tests__/trust-store.test.ts +3 -3
  84. package/src/__tests__/twilio-rest.test.ts +29 -0
  85. package/src/__tests__/twilio-routes-elevenlabs.test.ts +3 -0
  86. package/src/__tests__/twilio-routes-twiml.test.ts +11 -0
  87. package/src/__tests__/twilio-routes.test.ts +141 -21
  88. package/src/__tests__/user-reference.test.ts +2 -0
  89. package/src/__tests__/voice-quality.test.ts +222 -0
  90. package/src/__tests__/web-search.test.ts +45 -29
  91. package/src/agent/loop.ts +1 -1
  92. package/src/agent-heartbeat/agent-heartbeat-service.ts +2 -10
  93. package/src/amazon/client.ts +1418 -0
  94. package/src/amazon/request-extractor.ts +135 -0
  95. package/src/amazon/session.ts +109 -0
  96. package/src/autonomy/autonomy-store.ts +5 -5
  97. package/src/browser-extension-relay/client.ts +124 -0
  98. package/src/browser-extension-relay/protocol.ts +63 -0
  99. package/src/browser-extension-relay/server.ts +177 -0
  100. package/src/bundler/app-bundler.ts +3 -3
  101. package/src/bundler/bundle-signer.ts +1 -1
  102. package/src/bundler/signature-verifier.ts +1 -1
  103. package/src/calls/call-conversation-messages.ts +33 -0
  104. package/src/calls/call-domain.ts +106 -5
  105. package/src/calls/call-orchestrator.ts +252 -54
  106. package/src/calls/call-pointer-messages.ts +53 -0
  107. package/src/calls/call-recovery.ts +3 -8
  108. package/src/calls/call-store.ts +69 -87
  109. package/src/calls/elevenlabs-config.ts +3 -2
  110. package/src/calls/guardian-action-sweep.ts +105 -0
  111. package/src/calls/guardian-dispatch.ts +203 -0
  112. package/src/calls/guardian-question-copy.ts +133 -0
  113. package/src/calls/relay-server.ts +466 -8
  114. package/src/calls/speaker-identification.ts +1 -1
  115. package/src/calls/twilio-config.ts +7 -5
  116. package/src/calls/twilio-provider.ts +6 -4
  117. package/src/calls/twilio-rest.ts +40 -15
  118. package/src/calls/twilio-routes.ts +60 -45
  119. package/src/calls/types.ts +3 -1
  120. package/src/channels/types.ts +25 -0
  121. package/src/cli/amazon.ts +815 -0
  122. package/src/cli/config-commands.ts +2 -2
  123. package/src/cli/core-commands.ts +4 -3
  124. package/src/cli/influencer.ts +244 -0
  125. package/src/cli/map.ts +89 -6
  126. package/src/cli.ts +1 -1
  127. package/src/config/agent-schema.ts +171 -0
  128. package/src/config/bundled-skills/amazon/SKILL.md +127 -0
  129. package/src/config/bundled-skills/amazon/icon.svg +13 -0
  130. package/src/config/bundled-skills/api-mapping/SKILL.md +78 -0
  131. package/src/config/bundled-skills/browser/SKILL.md +1 -0
  132. package/src/config/bundled-skills/browser/TOOLS.json +17 -0
  133. package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +25 -0
  134. package/src/config/bundled-skills/doordash/SKILL.md +51 -51
  135. package/src/config/bundled-skills/email-setup/SKILL.md +14 -5
  136. package/src/config/bundled-skills/google-oauth-setup/SKILL.md +183 -0
  137. package/src/config/bundled-skills/influencer/SKILL.md +144 -0
  138. package/src/config/bundled-skills/macos-automation/icon.svg +12 -0
  139. package/src/config/bundled-skills/media-processing/SKILL.md +72 -95
  140. package/src/config/bundled-skills/media-processing/TOOLS.json +57 -147
  141. package/src/config/bundled-skills/media-processing/__tests__/concurrency-pool.test.ts +77 -0
  142. package/src/config/bundled-skills/media-processing/__tests__/cost-tracker.test.ts +69 -0
  143. package/src/config/bundled-skills/media-processing/__tests__/preprocess.test.ts +303 -0
  144. package/src/config/bundled-skills/media-processing/services/concurrency-pool.ts +55 -0
  145. package/src/config/bundled-skills/media-processing/services/cost-tracker.ts +86 -0
  146. package/src/config/bundled-skills/media-processing/services/gemini-map.ts +339 -0
  147. package/src/config/bundled-skills/media-processing/services/preprocess.ts +551 -0
  148. package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +7 -9
  149. package/src/config/bundled-skills/media-processing/services/reduce.ts +197 -0
  150. package/src/config/bundled-skills/media-processing/tools/analyze-keyframes.ts +88 -253
  151. package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +22 -153
  152. package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -2
  153. package/src/config/bundled-skills/media-processing/tools/media-diagnostics.ts +28 -51
  154. package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +35 -270
  155. package/src/config/bundled-skills/messaging/SKILL.md +12 -2
  156. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +4 -7
  157. package/src/config/bundled-skills/messaging/tools/messaging-reply.ts +2 -1
  158. package/src/config/bundled-skills/phone-calls/SKILL.md +86 -21
  159. package/src/config/bundled-skills/twitter/icon.svg +14 -0
  160. package/src/config/bundled-tool-registry.ts +310 -0
  161. package/src/config/calls-schema.ts +181 -0
  162. package/src/config/core-schema.ts +309 -0
  163. package/src/config/defaults.ts +26 -2
  164. package/src/config/env-registry.ts +162 -0
  165. package/src/config/env.ts +175 -0
  166. package/src/config/loader.ts +6 -6
  167. package/src/config/memory-schema.ts +528 -0
  168. package/src/config/sandbox-schema.ts +55 -0
  169. package/src/config/schema.ts +156 -1137
  170. package/src/config/skill-state.ts +1 -1
  171. package/src/config/skills-schema.ts +32 -0
  172. package/src/config/skills.ts +35 -24
  173. package/src/config/system-prompt.ts +107 -56
  174. package/src/config/templates/SOUL.md +1 -1
  175. package/src/config/types.ts +1 -0
  176. package/src/config/user-reference.ts +4 -9
  177. package/src/config/vellum-skills/catalog.json +0 -7
  178. package/src/config/vellum-skills/chatgpt-import/tools/chatgpt-import.ts +5 -1
  179. package/src/config/vellum-skills/slack-oauth-setup/SKILL.md +1 -0
  180. package/src/config/vellum-skills/sms-setup/SKILL.md +112 -14
  181. package/src/context/window-manager.ts +27 -7
  182. package/src/daemon/approval-generators.ts +186 -0
  183. package/src/daemon/approved-devices-store.ts +140 -0
  184. package/src/daemon/assistant-attachments.ts +1 -1
  185. package/src/daemon/classifier.ts +35 -32
  186. package/src/daemon/config-watcher.ts +1 -1
  187. package/src/daemon/daemon-control.ts +217 -0
  188. package/src/daemon/handlers/apps.ts +2 -3
  189. package/src/daemon/handlers/config-channels.ts +158 -0
  190. package/src/daemon/handlers/config-inbox.ts +540 -0
  191. package/src/daemon/handlers/config-ingress.ts +231 -0
  192. package/src/daemon/handlers/config-integrations.ts +258 -0
  193. package/src/daemon/handlers/config-model.ts +143 -0
  194. package/src/daemon/handlers/config-parental.ts +163 -0
  195. package/src/daemon/handlers/config-scheduling.ts +172 -0
  196. package/src/daemon/handlers/config-slack.ts +92 -0
  197. package/src/daemon/handlers/config-telegram.ts +301 -0
  198. package/src/daemon/handlers/config-tools.ts +177 -0
  199. package/src/daemon/handlers/config-trust.ts +104 -0
  200. package/src/daemon/handlers/config-twilio.ts +1080 -0
  201. package/src/daemon/handlers/config.ts +53 -2463
  202. package/src/daemon/handlers/diagnostics.ts +1 -1
  203. package/src/daemon/handlers/dictation.ts +4 -6
  204. package/src/daemon/handlers/documents.ts +18 -32
  205. package/src/daemon/handlers/index.ts +9 -0
  206. package/src/daemon/handlers/misc.ts +3 -5
  207. package/src/daemon/handlers/pairing.ts +98 -0
  208. package/src/daemon/handlers/sessions.ts +54 -5
  209. package/src/daemon/handlers/shared.ts +3 -1
  210. package/src/daemon/handlers/skills.ts +1 -1
  211. package/src/daemon/handlers/twitter-auth.ts +2 -0
  212. package/src/daemon/handlers/work-items.ts +2 -2
  213. package/src/daemon/handlers/workspace-files.ts +4 -3
  214. package/src/daemon/install-cli-launchers.ts +113 -0
  215. package/src/daemon/ipc-contract/apps.ts +356 -0
  216. package/src/daemon/ipc-contract/browser.ts +74 -0
  217. package/src/daemon/ipc-contract/computer-use.ts +151 -0
  218. package/src/daemon/ipc-contract/diagnostics.ts +56 -0
  219. package/src/daemon/ipc-contract/documents.ts +74 -0
  220. package/src/daemon/ipc-contract/inbox.ts +209 -0
  221. package/src/daemon/ipc-contract/integrations.ts +284 -0
  222. package/src/daemon/ipc-contract/memory.ts +48 -0
  223. package/src/daemon/ipc-contract/messages.ts +211 -0
  224. package/src/daemon/ipc-contract/pairing.ts +45 -0
  225. package/src/daemon/ipc-contract/parental-control.ts +95 -0
  226. package/src/daemon/ipc-contract/schedules.ts +97 -0
  227. package/src/daemon/ipc-contract/sessions.ts +315 -0
  228. package/src/daemon/ipc-contract/shared.ts +42 -0
  229. package/src/daemon/ipc-contract/skills.ts +120 -0
  230. package/src/daemon/ipc-contract/subagents.ts +58 -0
  231. package/src/daemon/ipc-contract/surfaces.ts +250 -0
  232. package/src/daemon/ipc-contract/trust.ts +60 -0
  233. package/src/daemon/ipc-contract/work-items.ts +225 -0
  234. package/src/daemon/ipc-contract/workspace.ts +113 -0
  235. package/src/daemon/ipc-contract-inventory.json +60 -0
  236. package/src/daemon/ipc-contract-inventory.ts +55 -29
  237. package/src/daemon/ipc-contract.ts +226 -2527
  238. package/src/daemon/ipc-protocol.ts +1 -1
  239. package/src/daemon/ipc-validate.ts +7 -0
  240. package/src/daemon/lifecycle.ts +97 -379
  241. package/src/daemon/pairing-store.ts +177 -0
  242. package/src/daemon/providers-setup.ts +43 -0
  243. package/src/daemon/ride-shotgun-handler.ts +67 -2
  244. package/src/daemon/server.ts +60 -44
  245. package/src/daemon/session-agent-loop-handlers.ts +421 -0
  246. package/src/daemon/session-agent-loop.ts +113 -275
  247. package/src/daemon/session-dynamic-profile.ts +1 -1
  248. package/src/daemon/session-history.ts +1 -1
  249. package/src/daemon/session-media-retry.ts +1 -1
  250. package/src/daemon/session-messaging.ts +37 -2
  251. package/src/daemon/session-notifiers.ts +5 -25
  252. package/src/daemon/session-process.ts +99 -59
  253. package/src/daemon/session-queue-manager.ts +96 -4
  254. package/src/daemon/session-runtime-assembly.ts +149 -15
  255. package/src/daemon/session-surfaces.ts +19 -4
  256. package/src/daemon/session-tool-setup.ts +28 -30
  257. package/src/daemon/session-workspace.ts +1 -1
  258. package/src/daemon/session.ts +24 -1
  259. package/src/daemon/shutdown-handlers.ts +122 -0
  260. package/src/daemon/trace-emitter.ts +1 -1
  261. package/src/daemon/watch-handler.ts +36 -33
  262. package/src/doordash/cart-queries.ts +787 -0
  263. package/src/doordash/client.ts +144 -127
  264. package/src/doordash/order-queries.ts +85 -0
  265. package/src/doordash/queries.ts +10 -1308
  266. package/src/doordash/search-queries.ts +203 -0
  267. package/src/doordash/session.ts +3 -2
  268. package/src/doordash/store-queries.ts +246 -0
  269. package/src/doordash/types.ts +367 -0
  270. package/src/email/providers/agentmail.ts +2 -1
  271. package/src/email/providers/index.ts +3 -2
  272. package/src/email/service.ts +3 -2
  273. package/src/errors.ts +43 -0
  274. package/src/home-base/prebuilt/seed.ts +1 -1
  275. package/src/hooks/cli.ts +6 -5
  276. package/src/hooks/config.ts +6 -8
  277. package/src/hooks/discovery.ts +6 -5
  278. package/src/hooks/manager.ts +4 -3
  279. package/src/hooks/runner.ts +2 -2
  280. package/src/hooks/templates.ts +5 -5
  281. package/src/inbound/public-ingress-urls.ts +3 -1
  282. package/src/index.ts +4 -2
  283. package/src/influencer/client.ts +1104 -0
  284. package/src/instrument.ts +4 -3
  285. package/src/logfire.ts +4 -3
  286. package/src/memory/admin.ts +25 -35
  287. package/src/memory/attachments-store.ts +4 -7
  288. package/src/memory/channel-delivery-store.ts +30 -1
  289. package/src/memory/channel-guardian-store.ts +200 -1
  290. package/src/memory/clarification-resolver.ts +37 -33
  291. package/src/memory/conflict-store.ts +67 -61
  292. package/src/memory/contradiction-checker.ts +141 -117
  293. package/src/memory/conversation-store.ts +335 -51
  294. package/src/memory/db-connection.ts +27 -4
  295. package/src/memory/db-init.ts +121 -4
  296. package/src/memory/db.ts +14 -1
  297. package/src/memory/embedding-backend.ts +27 -5
  298. package/src/memory/embedding-ollama.ts +2 -1
  299. package/src/memory/entity-extractor.ts +38 -35
  300. package/src/memory/guardian-action-store.ts +430 -0
  301. package/src/memory/inbox-escalation-projection.ts +59 -0
  302. package/src/memory/inbox-thread-store.ts +218 -0
  303. package/src/memory/ingress-invite-store.ts +338 -0
  304. package/src/memory/ingress-member-store.ts +350 -0
  305. package/src/memory/items-extractor.ts +91 -97
  306. package/src/memory/job-handlers/index-maintenance.ts +3 -3
  307. package/src/memory/job-handlers/media-processing.ts +11 -42
  308. package/src/memory/job-handlers/summarization.ts +32 -26
  309. package/src/memory/job-utils.ts +3 -10
  310. package/src/memory/jobs-store.ts +6 -9
  311. package/src/memory/jobs-worker.ts +51 -36
  312. package/src/memory/migrations/001-job-deferrals.ts +45 -0
  313. package/src/memory/migrations/002-tool-invocations-fk.ts +43 -0
  314. package/src/memory/migrations/003-memory-fts-backfill.ts +24 -0
  315. package/src/memory/migrations/004-entity-relation-dedup.ts +87 -0
  316. package/src/memory/migrations/005-fingerprint-scope-unique.ts +80 -0
  317. package/src/memory/migrations/006-scope-salted-fingerprints.ts +62 -0
  318. package/src/memory/migrations/007-assistant-id-to-self.ts +254 -0
  319. package/src/memory/migrations/008-remove-assistant-id-columns.ts +208 -0
  320. package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +83 -0
  321. package/src/memory/migrations/010-ext-conv-bindings-channel-chat-unique.ts +56 -0
  322. package/src/memory/migrations/011-call-sessions-provider-sid-dedup.ts +63 -0
  323. package/src/memory/migrations/012-call-sessions-add-initiated-from.ts +19 -0
  324. package/src/memory/migrations/013-guardian-action-tables.ts +68 -0
  325. package/src/memory/migrations/014-backfill-inbox-thread-state.ts +76 -0
  326. package/src/memory/migrations/015-drop-active-search-index.ts +27 -0
  327. package/src/memory/migrations/016-memory-segments-indexes.ts +11 -0
  328. package/src/memory/migrations/017-memory-items-indexes.ts +10 -0
  329. package/src/memory/migrations/018-remaining-table-indexes.ts +13 -0
  330. package/src/memory/migrations/index.ts +24 -0
  331. package/src/memory/migrations/registry.ts +79 -0
  332. package/src/memory/migrations/validate-migration-state.ts +69 -0
  333. package/src/memory/qdrant-manager.ts +49 -8
  334. package/src/memory/query-builder.ts +1 -1
  335. package/src/memory/raw-query.ts +119 -0
  336. package/src/memory/recall-cache.ts +4 -1
  337. package/src/memory/retriever.ts +160 -47
  338. package/src/memory/schema-migration.ts +25 -984
  339. package/src/memory/schema.ts +130 -7
  340. package/src/memory/search/entity.ts +10 -19
  341. package/src/memory/search/lexical.ts +81 -52
  342. package/src/memory/search/ranking.ts +21 -22
  343. package/src/memory/search/semantic.ts +157 -19
  344. package/src/memory/shared-app-links-store.ts +4 -5
  345. package/src/memory/validation.ts +19 -0
  346. package/src/messaging/draft-store.ts +5 -6
  347. package/src/messaging/providers/sms/adapter.ts +3 -6
  348. package/src/messaging/providers/telegram-bot/adapter.ts +2 -5
  349. package/src/messaging/providers/whatsapp/adapter.ts +136 -0
  350. package/src/messaging/providers/whatsapp/client.ts +67 -0
  351. package/src/messaging/style-analyzer.ts +5 -4
  352. package/src/messaging/thread-summarizer.ts +61 -69
  353. package/src/messaging/triage-engine.ts +62 -71
  354. package/src/migrations/config-merge.ts +53 -0
  355. package/src/migrations/data-layout.ts +68 -0
  356. package/src/migrations/data-merge.ts +33 -0
  357. package/src/migrations/hooks-merge.ts +90 -0
  358. package/src/migrations/index.ts +6 -0
  359. package/src/migrations/log.ts +23 -0
  360. package/src/migrations/skills-merge.ts +33 -0
  361. package/src/migrations/workspace-layout.ts +79 -0
  362. package/src/permissions/checker.ts +119 -11
  363. package/src/permissions/prompter.ts +14 -0
  364. package/src/permissions/shell-identity.ts +31 -1
  365. package/src/permissions/trust-store.ts +21 -1
  366. package/src/providers/anthropic/client.ts +4 -4
  367. package/src/providers/failover.ts +2 -2
  368. package/src/providers/model-intents.ts +70 -0
  369. package/src/providers/ollama/client.ts +2 -1
  370. package/src/providers/provider-send-message.ts +176 -0
  371. package/src/providers/registry.ts +71 -30
  372. package/src/providers/retry.ts +35 -1
  373. package/src/providers/types.ts +12 -1
  374. package/src/runtime/approval-conversation-turn.ts +97 -0
  375. package/src/runtime/approval-message-composer.ts +115 -5
  376. package/src/runtime/channel-approval-parser.ts +36 -2
  377. package/src/runtime/channel-approvals.ts +0 -21
  378. package/src/runtime/channel-guardian-service.ts +48 -7
  379. package/src/runtime/channel-readiness-service.ts +160 -34
  380. package/src/runtime/channel-readiness-types.ts +10 -4
  381. package/src/runtime/channel-retry-sweep.ts +184 -0
  382. package/src/runtime/guardian-context-resolver.ts +108 -0
  383. package/src/runtime/http-server.ts +275 -743
  384. package/src/runtime/http-types.ts +56 -3
  385. package/src/runtime/middleware/auth.ts +116 -0
  386. package/src/runtime/middleware/error-handler.ts +33 -0
  387. package/src/runtime/middleware/twilio-validation.ts +127 -0
  388. package/src/runtime/routes/app-routes.ts +1 -1
  389. package/src/runtime/routes/call-routes.ts +49 -6
  390. package/src/runtime/routes/channel-delivery-routes.ts +170 -0
  391. package/src/runtime/routes/channel-guardian-routes.ts +1191 -0
  392. package/src/runtime/routes/channel-inbound-routes.ts +1152 -0
  393. package/src/runtime/routes/channel-route-shared.ts +144 -0
  394. package/src/runtime/routes/channel-routes.ts +32 -1634
  395. package/src/runtime/routes/conversation-routes.ts +50 -7
  396. package/src/runtime/routes/events-routes.ts +2 -2
  397. package/src/runtime/routes/identity-routes.ts +126 -0
  398. package/src/runtime/routes/pairing-routes.ts +143 -0
  399. package/src/runtime/routes/run-routes.ts +15 -1
  400. package/src/runtime/run-orchestrator.ts +52 -34
  401. package/src/schedule/schedule-store.ts +36 -32
  402. package/src/schedule/scheduler.ts +3 -3
  403. package/src/security/encrypted-store.ts +5 -7
  404. package/src/security/oauth2.ts +45 -15
  405. package/src/security/parental-control-store.ts +183 -0
  406. package/src/security/secret-allowlist.ts +4 -3
  407. package/src/security/secret-scanner.ts +5 -5
  408. package/src/security/secure-keys.ts +1 -1
  409. package/src/security/token-manager.ts +3 -2
  410. package/src/services/vercel-deploy.ts +6 -2
  411. package/src/skills/tool-manifest.ts +3 -3
  412. package/src/skills/vellum-catalog-remote.ts +75 -16
  413. package/src/slack/slack-webhook.ts +2 -1
  414. package/src/swarm/orchestrator.ts +92 -1
  415. package/src/swarm/router-planner.ts +6 -9
  416. package/src/swarm/worker-prompts.ts +9 -12
  417. package/src/tasks/task-compiler.ts +19 -28
  418. package/src/tasks/task-runner.ts +1 -1
  419. package/src/tools/assets/search.ts +15 -14
  420. package/src/tools/browser/__tests__/auth-detector.test.ts +1 -0
  421. package/src/tools/browser/auto-navigate.ts +1 -0
  422. package/src/tools/browser/browser-execution.ts +10 -1
  423. package/src/tools/browser/browser-manager.ts +119 -4
  424. package/src/tools/browser/network-recorder.ts +5 -0
  425. package/src/tools/credentials/broker.ts +11 -2
  426. package/src/tools/credentials/metadata-store.ts +18 -14
  427. package/src/tools/credentials/post-connect-hooks.ts +61 -0
  428. package/src/tools/credentials/vault.ts +49 -23
  429. package/src/tools/executor.ts +68 -9
  430. package/src/tools/host-terminal/cli-discover.ts +1 -1
  431. package/src/tools/network/script-proxy/http-forwarder.ts +1 -1
  432. package/src/tools/network/script-proxy/mitm-handler.ts +1 -1
  433. package/src/tools/network/script-proxy/server.ts +1 -1
  434. package/src/tools/network/script-proxy/session-manager.ts +6 -5
  435. package/src/tools/network/web-fetch.ts +18 -2
  436. package/src/tools/network/web-search.ts +7 -3
  437. package/src/tools/reminder/reminder-store.ts +14 -15
  438. package/src/tools/schedule/create.ts +1 -0
  439. package/src/tools/schedule/list.ts +2 -1
  440. package/src/tools/shared/filesystem/file-ops-service.ts +5 -7
  441. package/src/tools/skills/skill-script-runner.ts +24 -9
  442. package/src/tools/skills/skill-tool-factory.ts +1 -0
  443. package/src/tools/tasks/work-item-enqueue.ts +2 -2
  444. package/src/tools/terminal/evaluate-typescript.ts +21 -12
  445. package/src/tools/terminal/parser.ts +50 -0
  446. package/src/tools/watcher/delete.ts +6 -0
  447. package/src/tools/weather/service.ts +1 -1
  448. package/src/twitter/client.ts +190 -24
  449. package/src/twitter/session.ts +4 -3
  450. package/src/util/clipboard.ts +1 -1
  451. package/src/util/errors.ts +65 -8
  452. package/src/util/fs.ts +40 -0
  453. package/src/util/json.ts +10 -0
  454. package/src/util/log-redact.ts +189 -0
  455. package/src/util/logger.ts +19 -17
  456. package/src/util/object.ts +3 -0
  457. package/src/util/platform.ts +72 -365
  458. package/src/util/pricing.ts +1 -1
  459. package/src/util/promise-guard.ts +1 -1
  460. package/src/util/retry.ts +19 -0
  461. package/src/util/row-mapper.ts +79 -0
  462. package/src/util/silently.ts +21 -0
  463. package/src/watcher/engine.ts +5 -1
  464. package/src/watcher/provider-types.ts +20 -0
  465. package/src/watcher/providers/github.ts +156 -0
  466. package/src/watcher/providers/gmail.ts +1 -0
  467. package/src/watcher/providers/google-calendar.ts +1 -0
  468. package/src/watcher/providers/linear.ts +460 -0
  469. package/src/watcher/providers/slack.ts +1 -0
  470. package/src/work-items/work-item-runner.ts +1 -1
  471. package/src/workspace/git-service.ts +1 -1
  472. package/src/workspace/provider-commit-message-generator.ts +51 -22
  473. package/src/__tests__/call-bridge.test.ts +0 -517
  474. package/src/__tests__/session-process-bridge.test.ts +0 -244
  475. package/src/calls/call-bridge.ts +0 -168
  476. package/src/config/bundled-skills/media-processing/services/capability-registry.ts +0 -137
  477. package/src/config/bundled-skills/media-processing/services/event-detection-service.ts +0 -280
  478. package/src/config/bundled-skills/media-processing/services/feedback-aggregation.ts +0 -144
  479. package/src/config/bundled-skills/media-processing/services/feedback-store.ts +0 -136
  480. package/src/config/bundled-skills/media-processing/services/retrieval-service.ts +0 -95
  481. package/src/config/bundled-skills/media-processing/services/timeline-service.ts +0 -267
  482. package/src/config/bundled-skills/media-processing/tools/detect-events.ts +0 -110
  483. package/src/config/bundled-skills/media-processing/tools/recalibrate.ts +0 -235
  484. package/src/config/bundled-skills/media-processing/tools/select-tracking-profile.ts +0 -142
  485. package/src/config/bundled-skills/media-processing/tools/submit-feedback.ts +0 -150
  486. package/src/config/vellum-skills/google-oauth-setup/SKILL.md +0 -199
@@ -7,6 +7,7 @@
7
7
  */
8
8
 
9
9
  import { getSecureKey } from '../security/secure-keys.js';
10
+ import { ProviderError, ConfigError } from '../util/errors.js';
10
11
 
11
12
  export interface TwilioCredentials {
12
13
  accountSid: string;
@@ -18,7 +19,7 @@ export function getTwilioCredentials(): TwilioCredentials {
18
19
  const accountSid = getSecureKey('credential:twilio:account_sid');
19
20
  const authToken = getSecureKey('credential:twilio:auth_token');
20
21
  if (!accountSid || !authToken) {
21
- throw new Error(
22
+ throw new ConfigError(
22
23
  'Twilio credentials not configured. Set credential:twilio:account_sid and credential:twilio:auth_token via the credential_store tool.',
23
24
  );
24
25
  }
@@ -58,7 +59,7 @@ export async function listIncomingPhoneNumbers(
58
59
 
59
60
  if (!res.ok) {
60
61
  const text = await res.text();
61
- throw new Error(`Twilio API error ${res.status}: ${text}`);
62
+ throw new ProviderError(`Twilio API error ${res.status}: ${text}`, 'twilio', res.status);
62
63
  }
63
64
 
64
65
  const data = (await res.json()) as {
@@ -102,7 +103,7 @@ export async function searchAvailableNumbers(
102
103
 
103
104
  if (!res.ok) {
104
105
  const text = await res.text();
105
- throw new Error(`Twilio API error ${res.status}: ${text}`);
106
+ throw new ProviderError(`Twilio API error ${res.status}: ${text}`, 'twilio', res.status);
106
107
  }
107
108
 
108
109
  const data = (await res.json()) as {
@@ -139,7 +140,7 @@ export async function provisionPhoneNumber(
139
140
 
140
141
  if (!res.ok) {
141
142
  const text = await res.text();
142
- throw new Error(`Twilio API error ${res.status}: ${text}`);
143
+ throw new ProviderError(`Twilio API error ${res.status}: ${text}`, 'twilio', res.status);
143
144
  }
144
145
 
145
146
  const data = (await res.json()) as {
@@ -171,7 +172,7 @@ export async function fetchMessageStatus(
171
172
 
172
173
  if (!res.ok) {
173
174
  const text = await res.text();
174
- throw new Error(`Twilio API error ${res.status}: ${text}`);
175
+ throw new ProviderError(`Twilio API error ${res.status}: ${text}`, 'twilio', res.status);
175
176
  }
176
177
 
177
178
  const data = (await res.json()) as {
@@ -217,7 +218,7 @@ export async function updatePhoneNumberWebhooks(
217
218
 
218
219
  if (!listRes.ok) {
219
220
  const text = await listRes.text();
220
- throw new Error(`Twilio API error ${listRes.status} looking up phone number: ${text}`);
221
+ throw new ProviderError(`Twilio API error ${listRes.status} looking up phone number: ${text}`, 'twilio', listRes.status);
221
222
  }
222
223
 
223
224
  const listData = (await listRes.json()) as {
@@ -226,7 +227,7 @@ export async function updatePhoneNumberWebhooks(
226
227
 
227
228
  const match = listData.incoming_phone_numbers.find((n) => n.phone_number === phoneNumber);
228
229
  if (!match) {
229
- throw new Error(`Phone number ${phoneNumber} not found on Twilio account ${accountSid}`);
230
+ throw new ProviderError(`Phone number ${phoneNumber} not found on Twilio account ${accountSid}`, 'twilio');
230
231
  }
231
232
 
232
233
  // Update the phone number's webhook configuration
@@ -253,7 +254,7 @@ export async function updatePhoneNumberWebhooks(
253
254
 
254
255
  if (!updateRes.ok) {
255
256
  const text = await updateRes.text();
256
- throw new Error(`Twilio API error ${updateRes.status} updating webhooks: ${text}`);
257
+ throw new ProviderError(`Twilio API error ${updateRes.status} updating webhooks: ${text}`, 'twilio', updateRes.status);
257
258
  }
258
259
  }
259
260
 
@@ -310,7 +311,7 @@ export async function getTollFreeVerificationStatus(
310
311
 
311
312
  if (!res.ok) {
312
313
  const text = await res.text();
313
- throw new Error(`Twilio Toll-Free Verification API error ${res.status}: ${text}`);
314
+ throw new ProviderError(`Twilio Toll-Free Verification API error ${res.status}: ${text}`, 'twilio', res.status);
314
315
  }
315
316
 
316
317
  const data = (await res.json()) as { verifications?: Array<Record<string, unknown>> };
@@ -320,6 +321,30 @@ export async function getTollFreeVerificationStatus(
320
321
  return parseTollFreeVerification(verifications[0]);
321
322
  }
322
323
 
324
+ /** Fetch a specific toll-free verification by SID. */
325
+ export async function getTollFreeVerificationBySid(
326
+ accountSid: string,
327
+ authToken: string,
328
+ verificationSid: string,
329
+ ): Promise<TollFreeVerification | null> {
330
+ const res = await fetch(`${TOLLFREE_VERIFICATION_BASE}/${encodeURIComponent(verificationSid)}`, {
331
+ method: 'GET',
332
+ headers: { Authorization: twilioAuthHeader(accountSid, authToken) },
333
+ });
334
+
335
+ if (res.status === 404) {
336
+ return null;
337
+ }
338
+
339
+ if (!res.ok) {
340
+ const text = await res.text();
341
+ throw new ProviderError(`Twilio Toll-Free Verification fetch error ${res.status}: ${text}`, 'twilio', res.status);
342
+ }
343
+
344
+ const data = (await res.json()) as Record<string, unknown>;
345
+ return parseTollFreeVerification(data);
346
+ }
347
+
323
348
  export interface TollFreeVerificationSubmitParams {
324
349
  tollfreePhoneNumberSid: string;
325
350
  businessName: string;
@@ -373,7 +398,7 @@ export async function submitTollFreeVerification(
373
398
 
374
399
  if (!res.ok) {
375
400
  const text = await res.text();
376
- throw new Error(`Twilio Toll-Free Verification submit error ${res.status}: ${text}`);
401
+ throw new ProviderError(`Twilio Toll-Free Verification submit error ${res.status}: ${text}`, 'twilio', res.status);
377
402
  }
378
403
 
379
404
  const data = (await res.json()) as Record<string, unknown>;
@@ -419,7 +444,7 @@ export async function updateTollFreeVerification(
419
444
 
420
445
  if (!res.ok) {
421
446
  const text = await res.text();
422
- throw new Error(`Twilio Toll-Free Verification update error ${res.status}: ${text}`);
447
+ throw new ProviderError(`Twilio Toll-Free Verification update error ${res.status}: ${text}`, 'twilio', res.status);
423
448
  }
424
449
 
425
450
  const data = (await res.json()) as Record<string, unknown>;
@@ -439,7 +464,7 @@ export async function deleteTollFreeVerification(
439
464
 
440
465
  if (!res.ok) {
441
466
  const text = await res.text();
442
- throw new Error(`Twilio Toll-Free Verification delete error ${res.status}: ${text}`);
467
+ throw new ProviderError(`Twilio Toll-Free Verification delete error ${res.status}: ${text}`, 'twilio', res.status);
443
468
  }
444
469
  }
445
470
 
@@ -462,7 +487,7 @@ export async function getPhoneNumberSid(
462
487
 
463
488
  if (!res.ok) {
464
489
  const text = await res.text();
465
- throw new Error(`Twilio API error ${res.status} looking up phone number SID: ${text}`);
490
+ throw new ProviderError(`Twilio API error ${res.status} looking up phone number SID: ${text}`, 'twilio', res.status);
466
491
  }
467
492
 
468
493
  const data = (await res.json()) as {
@@ -484,7 +509,7 @@ export async function releasePhoneNumber(
484
509
  ): Promise<void> {
485
510
  const sid = await getPhoneNumberSid(accountSid, authToken, phoneNumber);
486
511
  if (!sid) {
487
- throw new Error(`Phone number ${phoneNumber} not found on Twilio account ${accountSid}`);
512
+ throw new ProviderError(`Phone number ${phoneNumber} not found on Twilio account ${accountSid}`, 'twilio');
488
513
  }
489
514
 
490
515
  const res = await fetch(
@@ -497,6 +522,6 @@ export async function releasePhoneNumber(
497
522
 
498
523
  if (!res.ok) {
499
524
  const text = await res.text();
500
- throw new Error(`Twilio API error ${res.status} releasing phone number: ${text}`);
525
+ throw new ProviderError(`Twilio API error ${res.status} releasing phone number: ${text}`, 'twilio', res.status);
501
526
  }
502
527
  }
@@ -7,6 +7,7 @@
7
7
  */
8
8
 
9
9
  import { getLogger } from '../util/logger.js';
10
+ import { getCallWelcomeGreeting } from '../config/env.js';
10
11
  import {
11
12
  getCallSession,
12
13
  getCallSessionByCallSid,
@@ -25,14 +26,12 @@ import { getTwilioConfig } from './twilio-config.js';
25
26
  import { loadConfig } from '../config/loader.js';
26
27
  import { getTwilioRelayUrl } from '../inbound/public-ingress-urls.js';
27
28
  import { fireCallCompletionNotifier } from './call-state.js';
29
+ import { persistCallCompletionMessage } from './call-conversation-messages.js';
28
30
  import { resolveVoiceQualityProfile, isVoiceProfileValid } from './voice-quality.js';
31
+ import { createInboundVoiceSession } from './call-domain.js';
29
32
 
30
33
  const log = getLogger('twilio-routes');
31
34
 
32
- const CONTEXT_BLOCK_SPLIT_REGEX = /\n\s*\nContext:\s*/i;
33
- const MAX_TASK_SUMMARY_CHARS = 120;
34
- const DEFAULT_WELCOME_GREETING = 'Hello, this is an assistant calling. Is now a good time to talk?';
35
-
36
35
  // ── Helpers ──────────────────────────────────────────────────────────
37
36
 
38
37
  function escapeXml(str: string): string {
@@ -47,15 +46,18 @@ function escapeXml(str: string): string {
47
46
  export function generateTwiML(
48
47
  callSessionId: string,
49
48
  relayUrl: string,
50
- welcomeGreeting: string,
49
+ welcomeGreeting: string | null,
51
50
  profile: { language: string; transcriptionProvider: string; ttsProvider: string; voice: string },
52
51
  ): string {
52
+ const greetingAttr = welcomeGreeting && welcomeGreeting.trim().length > 0
53
+ ? `\n welcomeGreeting="${escapeXml(welcomeGreeting.trim())}"`
54
+ : '';
53
55
  return `<?xml version="1.0" encoding="UTF-8"?>
54
56
  <Response>
55
57
  <Connect>
56
58
  <ConversationRelay
57
59
  url="${escapeXml(relayUrl)}?callSessionId=${escapeXml(callSessionId)}"
58
- welcomeGreeting="${escapeXml(welcomeGreeting)}"
60
+ ${greetingAttr}
59
61
  voice="${escapeXml(profile.voice)}"
60
62
  language="${escapeXml(profile.language)}"
61
63
  transcriptionProvider="${escapeXml(profile.transcriptionProvider)}"
@@ -67,38 +69,14 @@ export function generateTwiML(
67
69
  </Response>`;
68
70
  }
69
71
 
70
- function summarizeTaskForGreeting(task: string | null): string | null {
71
- if (!task) return null;
72
- const primaryTask = task.split(CONTEXT_BLOCK_SPLIT_REGEX)[0]?.trim() ?? '';
73
- if (!primaryTask) return null;
74
-
75
- const compact = primaryTask.replace(/\s+/g, ' ').trim().replace(/[.!?]+$/, '');
76
- if (!compact) return null;
77
- if (compact.length <= MAX_TASK_SUMMARY_CHARS) return compact;
78
- return `${compact.slice(0, MAX_TASK_SUMMARY_CHARS - 3).trimEnd()}...`;
79
- }
80
-
81
- function formatTaskAsCallPurpose(taskSummary: string): string {
82
- const lower = taskSummary.toLowerCase();
83
- if (
84
- lower.startsWith('about ') ||
85
- lower.startsWith('to ') ||
86
- lower.startsWith('for ') ||
87
- lower.startsWith('regarding ')
88
- ) {
89
- return taskSummary;
90
- }
91
- return `about ${taskSummary}`;
92
- }
93
-
94
72
  export function buildWelcomeGreeting(task: string | null, configuredGreeting?: string): string {
73
+ void task;
95
74
  const override = configuredGreeting?.trim();
96
75
  if (override) return override;
97
-
98
- const taskSummary = summarizeTaskForGreeting(task);
99
- if (!taskSummary) return DEFAULT_WELCOME_GREETING;
100
-
101
- return `Hello, I am calling ${formatTaskAsCallPurpose(taskSummary)}. Is now a good time to talk?`;
76
+ // The contextual first opener now comes from the call orchestrator's
77
+ // initial LLM turn. Keep Twilio's relay-level greeting empty by default
78
+ // so we don't speak a deterministic static line first.
79
+ return '';
102
80
  }
103
81
 
104
82
  /**
@@ -144,16 +122,43 @@ function mapTwilioStatus(twilioStatus: string): CallStatus | null {
144
122
  /**
145
123
  * Receives the initial voice webhook when Twilio connects the call.
146
124
  * Returns TwiML XML that tells Twilio to open a ConversationRelay WebSocket.
125
+ *
126
+ * Supports two modes:
127
+ * - **Outbound** (callSessionId present in query): uses the existing session
128
+ * - **Inbound** (callSessionId absent): creates or reuses a session keyed
129
+ * by the Twilio CallSid. The optional `forwardedAssistantId` is resolved
130
+ * by the gateway from the "To" phone number.
147
131
  */
148
- export async function handleVoiceWebhook(req: Request): Promise<Response> {
132
+ export async function handleVoiceWebhook(req: Request, forwardedAssistantId?: string): Promise<Response> {
149
133
  const url = new URL(req.url);
150
134
  const callSessionId = url.searchParams.get('callSessionId');
151
135
 
136
+ // Parse the Twilio POST body to capture CallSid and caller metadata.
137
+ const formBody = new URLSearchParams(await req.text());
138
+ const callSid = formBody.get('CallSid');
139
+ const callerFrom = formBody.get('From') ?? '';
140
+ const callerTo = formBody.get('To') ?? '';
141
+
142
+ // ── Inbound mode: no callSessionId in query ─────────────────────
152
143
  if (!callSessionId) {
153
- log.warn('Voice webhook called without callSessionId');
154
- return new Response('Missing callSessionId', { status: 400 });
144
+ if (!callSid) {
145
+ log.warn('Inbound voice webhook called without CallSid');
146
+ return new Response('Missing CallSid', { status: 400 });
147
+ }
148
+
149
+ log.info({ callSid, from: callerFrom, to: callerTo, assistantId: forwardedAssistantId }, 'Inbound voice webhook — creating/reusing session');
150
+
151
+ const { session } = createInboundVoiceSession({
152
+ callSid,
153
+ fromNumber: callerFrom,
154
+ toNumber: callerTo,
155
+ assistantId: forwardedAssistantId,
156
+ });
157
+
158
+ return buildVoiceWebhookTwiml(session.id, session.assistantId ?? undefined, session.task);
155
159
  }
156
160
 
161
+ // ── Outbound mode: callSessionId is present ─────────────────────
157
162
  const session = getCallSession(callSessionId);
158
163
  if (!session) {
159
164
  log.warn({ callSessionId }, 'Voice webhook: call session not found');
@@ -165,16 +170,25 @@ export async function handleVoiceWebhook(req: Request): Promise<Response> {
165
170
  return new Response('Call session is no longer active', { status: 410 });
166
171
  }
167
172
 
168
- // Parse the Twilio POST body to capture CallSid immediately, so status
169
- // callbacks (keyed by CallSid) can locate this session even if the
170
- // WebSocket relay hasn't been set up yet.
171
- const formBody = new URLSearchParams(await req.text());
172
- const callSid = formBody.get('CallSid');
173
+ // Capture CallSid immediately so status callbacks can locate this session
173
174
  if (callSid && callSid !== session.providerCallSid) {
174
175
  updateCallSession(callSessionId, { providerCallSid: callSid });
175
176
  log.info({ callSessionId, callSid }, 'Stored CallSid from voice webhook');
176
177
  }
177
178
 
179
+ return buildVoiceWebhookTwiml(callSessionId, session.assistantId ?? undefined, session.task);
180
+ }
181
+
182
+ /**
183
+ * Shared TwiML generation for both inbound and outbound voice webhooks.
184
+ * Resolves voice quality profile, relay URL, and welcome greeting,
185
+ * then returns a Response with the generated TwiML.
186
+ */
187
+ function buildVoiceWebhookTwiml(
188
+ callSessionId: string,
189
+ assistantId: string | undefined,
190
+ task: string | null,
191
+ ): Response {
178
192
  let profile = resolveVoiceQualityProfile(loadConfig());
179
193
 
180
194
  log.info({ callSessionId, mode: profile.mode, ttsProvider: profile.ttsProvider, voice: profile.voice }, 'Voice quality profile resolved');
@@ -213,7 +227,7 @@ export async function handleVoiceWebhook(req: Request): Promise<Response> {
213
227
  });
214
228
  }
215
229
 
216
- const twilioConfig = getTwilioConfig();
230
+ const twilioConfig = getTwilioConfig(assistantId);
217
231
  let relayUrl: string;
218
232
  try {
219
233
  relayUrl = getTwilioRelayUrl(loadConfig());
@@ -221,7 +235,7 @@ export async function handleVoiceWebhook(req: Request): Promise<Response> {
221
235
  // Fallback to legacy resolution when ingress is not configured
222
236
  relayUrl = resolveRelayUrl(twilioConfig.wssBaseUrl, twilioConfig.webhookBaseUrl);
223
237
  }
224
- const welcomeGreeting = buildWelcomeGreeting(session.task, process.env.CALL_WELCOME_GREETING);
238
+ const welcomeGreeting = buildWelcomeGreeting(task, getCallWelcomeGreeting());
225
239
 
226
240
  const twiml = generateTwiML(callSessionId, relayUrl, welcomeGreeting, profile);
227
241
 
@@ -308,6 +322,7 @@ export async function handleStatusCallback(req: Request): Promise<Response> {
308
322
  expirePendingQuestions(session.id);
309
323
 
310
324
  if (!wasTerminal) {
325
+ persistCallCompletionMessage(session.conversationId, session.id);
311
326
  fireCallCompletionNotifier(session.conversationId, session.id);
312
327
  }
313
328
  }
@@ -1,5 +1,5 @@
1
1
  export type CallStatus = 'initiated' | 'ringing' | 'in_progress' | 'waiting_on_user' | 'completed' | 'failed' | 'cancelled';
2
- export type CallEventType = 'call_started' | 'call_connected' | 'caller_spoke' | 'assistant_spoke' | 'user_question_asked' | 'user_answered' | 'user_instruction_relayed' | 'call_ended' | 'call_failed';
2
+ export type CallEventType = 'call_started' | 'call_connected' | 'caller_spoke' | 'assistant_spoke' | 'user_question_asked' | 'user_answered' | 'user_instruction_relayed' | 'call_ended' | 'call_failed' | 'callee_verification_started' | 'callee_verification_succeeded' | 'callee_verification_failed' | 'guardian_voice_verification_started' | 'guardian_voice_verification_succeeded' | 'guardian_voice_verification_failed';
3
3
  export type PendingQuestionStatus = 'pending' | 'answered' | 'expired' | 'cancelled';
4
4
 
5
5
  export interface CallSession {
@@ -13,6 +13,8 @@ export interface CallSession {
13
13
  status: CallStatus;
14
14
  callerIdentityMode: string | null;
15
15
  callerIdentitySource: string | null;
16
+ assistantId: string | null;
17
+ initiatedFromConversationId?: string | null;
16
18
  startedAt: number | null;
17
19
  endedAt: number | null;
18
20
  lastError: string | null;
@@ -0,0 +1,25 @@
1
+ export const CHANNEL_IDS = [
2
+ 'telegram', 'sms', 'voice', 'macos', 'ios', 'whatsapp', 'slack', 'email',
3
+ ] as const;
4
+
5
+ export type ChannelId = (typeof CHANNEL_IDS)[number];
6
+
7
+ export function isChannelId(value: unknown): value is ChannelId {
8
+ return typeof value === 'string' && (CHANNEL_IDS as readonly string[]).includes(value);
9
+ }
10
+
11
+ export function parseChannelId(value: unknown): ChannelId | null {
12
+ return isChannelId(value) ? value : null;
13
+ }
14
+
15
+ export function assertChannelId(value: unknown, field: string): ChannelId {
16
+ if (!isChannelId(value)) {
17
+ throw new Error(`Invalid channel ID for ${field}: ${String(value)}. Valid values: ${CHANNEL_IDS.join(', ')}`);
18
+ }
19
+ return value;
20
+ }
21
+
22
+ export interface TurnChannelContext {
23
+ userMessageChannel: ChannelId;
24
+ assistantMessageChannel: ChannelId;
25
+ }