@vellumai/assistant 0.10.4 → 0.10.5-dev.202607022329.d89bf2f

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 (866) hide show
  1. package/ARCHITECTURE.md +94 -91
  2. package/Dockerfile +5 -16
  3. package/README.md +7 -10
  4. package/bun.lock +0 -4
  5. package/docs/architecture/security.md +20 -20
  6. package/docs/stt-provider-onboarding.md +4 -5
  7. package/docs/trusted-contact-access.md +7 -7
  8. package/eslint-rules/cli-no-daemon-internals.js +4 -0
  9. package/eslint.config.mjs +7 -1
  10. package/knip.json +0 -1
  11. package/node_modules/@vellumai/gateway-client/src/__tests__/contact-read-contracts.test.ts +36 -0
  12. package/node_modules/@vellumai/gateway-client/src/__tests__/invite-contract.test.ts +233 -0
  13. package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +40 -0
  14. package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +20 -8
  15. package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +10 -0
  16. package/node_modules/@vellumai/gateway-client/src/index.ts +33 -0
  17. package/node_modules/@vellumai/gateway-client/src/invite-contract.ts +216 -0
  18. package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +91 -0
  19. package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +39 -3
  20. package/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +0 -8
  21. package/node_modules/@vellumai/service-contracts/src/__tests__/package-boundary.test.ts +1 -5
  22. package/node_modules/@vellumai/service-contracts/src/twilio-ingress.ts +3 -14
  23. package/openapi.yaml +1073 -225
  24. package/package.json +1 -4
  25. package/scripts/test.sh +35 -13
  26. package/src/__tests__/access-request-seed-content-blocks.test.ts +60 -4
  27. package/src/__tests__/acp-session.test.ts +4 -1
  28. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +0 -6
  29. package/src/__tests__/add-message-preassigned-id.test.ts +56 -0
  30. package/src/__tests__/agent-loop-set-system-prompt.test.ts +139 -0
  31. package/src/__tests__/app-conversation-ids-backfill.test.ts +3 -0
  32. package/src/__tests__/app-source-watcher.test.ts +11 -0
  33. package/src/__tests__/approval-interception-trust-gates.test.ts +148 -3
  34. package/src/__tests__/assistant-attachments.test.ts +62 -4
  35. package/src/__tests__/assistant-id-boundary-guard.test.ts +0 -1
  36. package/src/__tests__/attachments-store-heic-normalize.test.ts +230 -0
  37. package/src/__tests__/attachments-store.test.ts +3 -0
  38. package/src/__tests__/background-shell-bash.test.ts +34 -0
  39. package/src/__tests__/background-shell-host-bash.test.ts +12 -0
  40. package/src/__tests__/background-tool-registry.test.ts +97 -0
  41. package/src/__tests__/background-tool-routes.test.ts +72 -1
  42. package/src/__tests__/background-workers-disk-pressure.test.ts +2 -1
  43. package/src/__tests__/btw-routes.test.ts +57 -3
  44. package/src/__tests__/call-controller.test.ts +474 -84
  45. package/src/__tests__/call-domain.test.ts +48 -1
  46. package/src/__tests__/call-pointer-messages.test.ts +57 -19
  47. package/src/__tests__/call-pointer-no-hardcoded-copy.guard.test.ts +2 -6
  48. package/src/__tests__/call-routes-http.test.ts +15 -14
  49. package/src/__tests__/call-setup-flow-invite.test.ts +568 -0
  50. package/src/__tests__/call-setup-flow-midcall-trust.test.ts +231 -0
  51. package/src/__tests__/call-setup-flow-name-capture.test.ts +754 -0
  52. package/src/__tests__/call-setup-flow-verification.test.ts +925 -0
  53. package/src/__tests__/call-setup-flow.test.ts +311 -0
  54. package/src/__tests__/cancel-resolves-conversation-key.test.ts +0 -1
  55. package/src/__tests__/channel-guardian.test.ts +4 -7
  56. package/src/__tests__/channel-readiness-service.test.ts +0 -6
  57. package/src/__tests__/channel-readiness-slack-remote.test.ts +0 -4
  58. package/src/__tests__/channel-retry-sweep.test.ts +76 -64
  59. package/src/__tests__/channel-setup-panel-ack.test.ts +184 -0
  60. package/src/__tests__/clear-all-lexical.test.ts +90 -0
  61. package/src/__tests__/compactor-media-strip.test.ts +177 -0
  62. package/src/__tests__/config-loader-backfill.test.ts +5 -5
  63. package/src/__tests__/config-schema.test.ts +17 -0
  64. package/src/__tests__/contact-store-interaction-info.test.ts +27 -70
  65. package/src/__tests__/contacts-relay-reads.test.ts +188 -23
  66. package/src/__tests__/contacts-tools.test.ts +42 -1
  67. package/src/__tests__/contacts-write.test.ts +181 -0
  68. package/src/__tests__/context-search-conversations-source.test.ts +220 -81
  69. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -0
  70. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
  71. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
  72. package/src/__tests__/conversation-agent-loop.test.ts +39 -0
  73. package/src/__tests__/conversation-analysis-routes.test.ts +1 -1
  74. package/src/__tests__/conversation-app-control-instantiation.test.ts +2 -0
  75. package/src/__tests__/conversation-clear-safety.test.ts +0 -1
  76. package/src/__tests__/conversation-crud-enabled-plugins.test.ts +60 -0
  77. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -1
  78. package/src/__tests__/conversation-disk-view.test.ts +1 -0
  79. package/src/__tests__/conversation-enabledplugins-route.test.ts +184 -0
  80. package/src/__tests__/conversation-error.test.ts +0 -18
  81. package/src/__tests__/conversation-fork-crud.test.ts +67 -0
  82. package/src/__tests__/conversation-history-web-search.test.ts +7 -137
  83. package/src/__tests__/conversation-initial-prompt.test.ts +110 -0
  84. package/src/__tests__/conversation-loop-db-lock-resilience.test.ts +191 -0
  85. package/src/__tests__/conversation-process-callsite.test.ts +37 -0
  86. package/src/__tests__/conversation-queue.test.ts +149 -0
  87. package/src/__tests__/conversation-routes-enabled-plugins.test.ts +392 -0
  88. package/src/__tests__/conversation-routes-guardian-reply.test.ts +84 -0
  89. package/src/__tests__/conversation-routes-hidden-queue.test.ts +407 -0
  90. package/src/__tests__/conversation-runtime-assembly.test.ts +36 -16
  91. package/src/__tests__/conversation-skill-tools.test.ts +73 -0
  92. package/src/__tests__/conversation-wipe.test.ts +20 -5
  93. package/src/__tests__/credential-prompt-route.test.ts +69 -0
  94. package/src/__tests__/credential-security-invariants.test.ts +1 -3
  95. package/src/__tests__/db-drop-messages-fts.test.ts +115 -0
  96. package/src/__tests__/deterministic-verification-control-plane.test.ts +1 -62
  97. package/src/__tests__/edit-propagation.test.ts +56 -2
  98. package/src/__tests__/empty-state-greeting-cache.test.ts +9 -0
  99. package/src/__tests__/gateway-only-enforcement.test.ts +15 -110
  100. package/src/__tests__/guardian-expiry-notifier.test.ts +1 -0
  101. package/src/__tests__/guardian-question-mode.test.ts +57 -0
  102. package/src/__tests__/guardian-verification-voice-binding.test.ts +1 -1
  103. package/src/__tests__/guardian-wait-controller.test.ts +619 -0
  104. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +33 -2
  105. package/src/__tests__/heic-fixture.ts +55 -0
  106. package/src/__tests__/helpers/seed-contact-channel.ts +0 -2
  107. package/src/__tests__/http-conversation-lineage.test.ts +6 -1
  108. package/src/__tests__/image-conversion.test.ts +170 -0
  109. package/src/__tests__/inbound-invite-redemption.test.ts +72 -341
  110. package/src/__tests__/introduction-card-resolver.test.ts +354 -0
  111. package/src/__tests__/invite-redeemed-ipc.test.ts +97 -0
  112. package/src/__tests__/invite-routes-http.test.ts +246 -420
  113. package/src/__tests__/job-handler-registration-guard.test.ts +104 -0
  114. package/src/__tests__/lexical-index-dual-write.test.ts +426 -0
  115. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +2 -2
  116. package/src/__tests__/list-messages-attachments.test.ts +159 -0
  117. package/src/__tests__/list-messages-background-tool-completion.test.ts +141 -0
  118. package/src/__tests__/list-messages-queued.test.ts +25 -0
  119. package/src/__tests__/managed-store.test.ts +129 -1
  120. package/src/__tests__/media-stream-audio-transcode.test.ts +113 -0
  121. package/src/__tests__/media-stream-output.test.ts +350 -19
  122. package/src/__tests__/media-stream-server-integration.test.ts +943 -169
  123. package/src/__tests__/media-stream-stt-session.test.ts +498 -1
  124. package/src/__tests__/memory-jobs-worker-lanes.test.ts +2 -2
  125. package/src/__tests__/model-intents.test.ts +1 -1
  126. package/src/__tests__/mtime-cache.test.ts +19 -0
  127. package/src/__tests__/non-member-access-request.test.ts +349 -3
  128. package/src/__tests__/notification-decision-fallback.test.ts +104 -8
  129. package/src/__tests__/notification-decision-strategy.test.ts +57 -8
  130. package/src/__tests__/onboarding-template-contract.test.ts +27 -43
  131. package/src/__tests__/openai-provider.test.ts +33 -0
  132. package/src/__tests__/openai-responses-provider.test.ts +40 -0
  133. package/src/__tests__/persistence-layering-guard.test.ts +29 -20
  134. package/src/__tests__/persona-resolver.test.ts +56 -2
  135. package/src/__tests__/personal-memory-auth-bypass.test.ts +62 -0
  136. package/src/__tests__/phone.test.ts +1 -15
  137. package/src/__tests__/plugin-bootstrap.test.ts +25 -196
  138. package/src/__tests__/plugin-disabled-state.test.ts +33 -0
  139. package/src/__tests__/plugin-effective-enabled-set.test.ts +227 -0
  140. package/src/__tests__/plugin-event-hub-facade.test.ts +323 -0
  141. package/src/__tests__/plugin-import-boundary-guard.test.ts +156 -42
  142. package/src/__tests__/plugin-injector-contribution.test.ts +4 -20
  143. package/src/__tests__/plugin-pipeline.test.ts +63 -1
  144. package/src/__tests__/plugin-route-contribution.test.ts +62 -115
  145. package/src/__tests__/plugin-tool-contribution.test.ts +0 -22
  146. package/src/__tests__/plugin-types.test.ts +0 -1
  147. package/src/__tests__/pointer-conversation-trust.test.ts +38 -6
  148. package/src/__tests__/pre-model-call-sanitize.test.ts +1 -1
  149. package/src/__tests__/prune-old-tool-invocations-job.test.ts +97 -0
  150. package/src/__tests__/public-ingress-urls.test.ts +0 -48
  151. package/src/__tests__/qdrant-collection-migration.test.ts +145 -2
  152. package/src/__tests__/reaction-intercept-member-verdict-warm.test.ts +0 -1
  153. package/src/__tests__/resolve-trust-class.test.ts +28 -10
  154. package/src/__tests__/scaffold-managed-skill-tool.test.ts +137 -0
  155. package/src/__tests__/schedule-retry.test.ts +38 -14
  156. package/src/__tests__/schedule-routes-workflow-validation.test.ts +3 -3
  157. package/src/__tests__/schedule-routes.test.ts +14 -6
  158. package/src/__tests__/schedule-store.test.ts +3 -1
  159. package/src/__tests__/scheduler-disk-pressure.test.ts +17 -6
  160. package/src/__tests__/scheduler-recurrence.test.ts +57 -107
  161. package/src/__tests__/scheduler-reuse-conversation.test.ts +51 -46
  162. package/src/__tests__/scheduler-wake.test.ts +13 -23
  163. package/src/__tests__/scheduler-worker-standdown.test.ts +220 -0
  164. package/src/__tests__/secret-routes-platform-proxy.test.ts +1 -1
  165. package/src/__tests__/shell-observability.test.ts +1 -0
  166. package/src/__tests__/skill-boundary-guard.test.ts +3 -4
  167. package/src/__tests__/skill-load-plugin-scope.test.ts +238 -0
  168. package/src/__tests__/skill-projection-feature-flag.test.ts +2 -0
  169. package/src/__tests__/skill-script-runner-host.test.ts +53 -0
  170. package/src/__tests__/skill-script-runner-sandbox.test.ts +5 -2
  171. package/src/__tests__/skill-tool-factory.test.ts +73 -1
  172. package/src/__tests__/slack-app-setup-skill-regression.test.ts +37 -14
  173. package/src/__tests__/slack-inbound-verification.test.ts +36 -0
  174. package/src/__tests__/slack-notification-approval-card.test.ts +89 -2
  175. package/src/__tests__/slack-reaction-canonical-approval.test.ts +51 -1
  176. package/src/__tests__/stt-hints.test.ts +2 -256
  177. package/src/__tests__/subagent-allowlist-validation.test.ts +47 -0
  178. package/src/__tests__/subagent-call-site-routing.test.ts +72 -0
  179. package/src/__tests__/subagent-fork-notifications.test.ts +14 -2
  180. package/src/__tests__/subagent-persistence.test.ts +122 -0
  181. package/src/__tests__/subagent-spawn-and-await.test.ts +1 -0
  182. package/src/__tests__/subagent-terminal-message.test.ts +99 -0
  183. package/src/__tests__/subagent-tool-gate-mode.test.ts +78 -1
  184. package/src/__tests__/system-prompt.test.ts +17 -118
  185. package/src/__tests__/task-scheduler.test.ts +68 -89
  186. package/src/__tests__/telephony-credential-preflight.test.ts +312 -0
  187. package/src/__tests__/telephony-tts-guard.test.ts +538 -0
  188. package/src/__tests__/title-generate-hook.test.ts +16 -0
  189. package/src/__tests__/tool-approval-handler.test.ts +87 -1
  190. package/src/__tests__/tool-approval-seed-content-blocks.test.ts +45 -20
  191. package/src/__tests__/tool-grant-request-escalation.test.ts +39 -0
  192. package/src/__tests__/trust-context-guards.test.ts +21 -10
  193. package/src/__tests__/trusted-contact-multichannel.test.ts +4 -0
  194. package/src/__tests__/twilio-provider.test.ts +17 -17
  195. package/src/__tests__/twilio-routes-twiml.test.ts +6 -543
  196. package/src/__tests__/twilio-routes.test.ts +257 -387
  197. package/src/__tests__/twilio-validation.test.ts +1 -1
  198. package/src/__tests__/unified-turn-context-client-os.test.ts +45 -0
  199. package/src/__tests__/usage-cache-backfill-migration.test.ts +5 -0
  200. package/src/__tests__/voice-approval-resolver-relay.test.ts +150 -0
  201. package/src/__tests__/voice-ingress-preflight.test.ts +61 -0
  202. package/src/__tests__/voice-quality.test.ts +2 -2
  203. package/src/__tests__/voice-scoped-grant-consumer.test.ts +1 -7
  204. package/src/__tests__/voice-session-bridge-processing-wait.test.ts +27 -0
  205. package/src/__tests__/voice-session-bridge.test.ts +1 -10
  206. package/src/__tests__/workspace-git-service.test.ts +1 -1
  207. package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +1 -0
  208. package/src/__tests__/workspace-migration-118-seed-now-md.test.ts +120 -0
  209. package/src/__tests__/workspace-migration-120-revise-onboarding-threads.test.ts +175 -0
  210. package/src/__tests__/workspace-migration-121-seed-default-user-guardrails.test.ts +118 -0
  211. package/src/__tests__/workspace-migration-122-relocate-default-user-boundary.test.ts +119 -0
  212. package/src/__tests__/workspace-migration-123-swap-quality-profile-to-fable.test.ts +191 -0
  213. package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +235 -241
  214. package/src/acp/__tests__/prepare-agent-env.test.ts +0 -97
  215. package/src/acp/__tests__/session-manager-persistence.test.ts +2 -0
  216. package/src/acp/__tests__/session-manager-resume.test.ts +8 -0
  217. package/src/acp/__tests__/session-manager-usage.test.ts +6 -0
  218. package/src/acp/__tests__/session-manager.test.ts +6 -0
  219. package/src/acp/agent-process.test.ts +116 -0
  220. package/src/acp/agent-process.ts +71 -0
  221. package/src/acp/auto-install.ts +12 -8
  222. package/src/acp/failure-error.test.ts +109 -0
  223. package/src/acp/failure-error.ts +111 -0
  224. package/src/acp/prepare-agent-env.ts +0 -12
  225. package/src/acp/resolve-agent.test.ts +6 -45
  226. package/src/acp/resolve-agent.ts +2 -4
  227. package/src/acp/session-manager.test.ts +312 -0
  228. package/src/acp/session-manager.ts +102 -46
  229. package/src/agent/ax-tree-compaction.test.ts +4 -1
  230. package/src/agent/image-optimize.ts +17 -127
  231. package/src/agent/loop.ts +45 -192
  232. package/src/api/events/background-tool-completed.test.ts +51 -0
  233. package/src/api/events/background-tool-completed.ts +29 -0
  234. package/src/api/events/background-tool-started.test.ts +36 -0
  235. package/src/api/events/background-tool-started.ts +28 -0
  236. package/src/api/events/conversation-error.ts +0 -1
  237. package/src/api/events/open-panel.ts +33 -0
  238. package/src/api/events/subagent-status-changed.ts +4 -1
  239. package/src/api/index.ts +19 -0
  240. package/src/api/responses/conversation-message.ts +34 -0
  241. package/src/approvals/guardian-decision-primitive.ts +74 -24
  242. package/src/approvals/guardian-request-resolvers.ts +450 -122
  243. package/src/apps/app-store.ts +1 -0
  244. package/src/apps/shared-app-links-store.ts +1 -0
  245. package/src/avatar/identity-avatar.ts +3 -7
  246. package/src/background-wake/wake-intent-hooks.test.ts +1 -1
  247. package/src/calls/__tests__/fish-audio-client.test.ts +73 -0
  248. package/src/calls/__tests__/gateway-invite-reader.test.ts +170 -0
  249. package/src/calls/__tests__/relay-setup-router.test.ts +126 -102
  250. package/src/calls/call-controller.ts +134 -48
  251. package/src/calls/call-domain.ts +18 -42
  252. package/src/calls/call-pointer-messages.ts +51 -67
  253. package/src/calls/call-setup-flow-types.ts +76 -0
  254. package/src/calls/call-setup-flow.ts +1743 -0
  255. package/src/calls/call-speech-output.ts +87 -23
  256. package/src/calls/call-store.ts +4 -0
  257. package/src/calls/call-transport.ts +22 -36
  258. package/src/calls/fish-audio-client.ts +3 -0
  259. package/src/calls/gateway-invite-reader.ts +98 -0
  260. package/src/calls/guardian-wait-controller.ts +451 -0
  261. package/src/calls/media-stream-audio-transcode.ts +82 -2
  262. package/src/calls/media-stream-output.ts +142 -36
  263. package/src/calls/media-stream-server.ts +297 -178
  264. package/src/calls/media-stream-stt-session.ts +393 -50
  265. package/src/calls/media-turn-detector.ts +4 -6
  266. package/src/calls/relay-access-wait.ts +3 -4
  267. package/src/calls/relay-setup-router.ts +22 -43
  268. package/src/calls/relay-verification.ts +17 -24
  269. package/src/calls/resolve-call-tts-provider.ts +96 -29
  270. package/src/calls/stt-hints.ts +24 -87
  271. package/src/calls/telephony-credential-preflight.ts +159 -0
  272. package/src/calls/telephony-tts-capability.ts +183 -0
  273. package/src/calls/tts-call-strategy.ts +18 -20
  274. package/src/calls/twilio-provider.ts +3 -2
  275. package/src/calls/twilio-routes.ts +47 -351
  276. package/src/calls/types.ts +5 -3
  277. package/src/calls/voice-ingress-preflight.ts +9 -0
  278. package/src/calls/voice-quality.ts +13 -18
  279. package/src/calls/voice-session-bridge.ts +25 -35
  280. package/src/channels/__tests__/types.test.ts +28 -0
  281. package/src/channels/channel-verification-sessions.ts +7 -2
  282. package/src/channels/config.ts +0 -42
  283. package/src/channels/slack-thread-store.ts +12 -0
  284. package/src/channels/types.ts +23 -0
  285. package/src/cli/commands/__tests__/conversations-slack.test.ts +1 -1
  286. package/src/cli/commands/__tests__/schedules.test.ts +83 -34
  287. package/src/cli/commands/__tests__/status.test.ts +42 -2
  288. package/src/cli/commands/contacts.ts +2 -2
  289. package/src/cli/commands/credentials.ts +17 -0
  290. package/src/cli/commands/monitoring.ts +191 -0
  291. package/src/cli/commands/schedules-worker.ts +175 -0
  292. package/src/cli/commands/schedules.ts +169 -43
  293. package/src/cli/commands/status.ts +12 -1
  294. package/src/cli/commands/tools.ts +5 -1
  295. package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +1 -0
  296. package/src/cli/lib/__tests__/search-plugins.test.ts +29 -1
  297. package/src/cli/lib/__tests__/toggle-plugin.test.ts +7 -0
  298. package/src/cli/lib/search-plugins.ts +6 -0
  299. package/src/cli/lib/toggle-plugin.ts +21 -2
  300. package/src/cli/program.ts +3 -1
  301. package/src/config/__tests__/deployment-context-defaults.test.ts +23 -0
  302. package/src/config/acp-defaults.test.ts +0 -10
  303. package/src/config/acp-defaults.ts +0 -6
  304. package/src/config/bundled-skills/acp/SKILL.md +13 -45
  305. package/src/config/bundled-skills/acp/TOOLS.json +3 -3
  306. package/src/config/bundled-skills/app-builder/SKILL.md +3 -3
  307. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +9 -9
  308. package/src/config/bundled-skills/contacts/tools/contact-merge.ts +1 -1
  309. package/src/config/bundled-skills/contacts/tools/contact-search.ts +1 -1
  310. package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
  311. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +9 -9
  312. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +10 -7
  313. package/src/config/bundled-skills/skill-management/TOOLS.json +10 -0
  314. package/src/config/call-site-defaults.ts +0 -2
  315. package/src/config/feature-flag-registry.json +16 -8
  316. package/src/config/loader.ts +11 -0
  317. package/src/config/schema.ts +9 -1
  318. package/src/config/schemas/__tests__/memory-v2.test.ts +2 -2
  319. package/src/config/schemas/__tests__/memory-v3.test.ts +64 -0
  320. package/src/config/schemas/call-site-catalog.ts +0 -12
  321. package/src/config/schemas/calls.ts +8 -0
  322. package/src/config/schemas/elevenlabs.ts +1 -1
  323. package/src/config/schemas/llm.ts +0 -2
  324. package/src/config/schemas/memory-v2.ts +1 -1
  325. package/src/config/schemas/memory-v3.ts +82 -0
  326. package/src/config/schemas/monitoring.ts +61 -0
  327. package/src/config/schemas/schedules.ts +35 -0
  328. package/src/config/seed-inference-profiles.ts +2 -2
  329. package/src/config/skills-plugin-filter.test.ts +96 -0
  330. package/src/config/skills.ts +26 -0
  331. package/src/contacts/__tests__/member-write-relay.test.ts +156 -4
  332. package/src/contacts/canonical-guardian-store.ts +5 -0
  333. package/src/contacts/contact-store.ts +81 -35
  334. package/src/contacts/contacts-write.ts +50 -25
  335. package/src/contacts/member-write-relay.ts +115 -31
  336. package/src/contacts/types.ts +0 -8
  337. package/src/context/compactor.ts +29 -15
  338. package/src/context/outbound-sanitize.ts +202 -0
  339. package/src/daemon/__tests__/abort-null-controller.test.ts +167 -0
  340. package/src/daemon/__tests__/conversation-tool-setup-plugin-scope.test.ts +122 -0
  341. package/src/daemon/__tests__/embedding-reconcile.test.ts +99 -16
  342. package/src/daemon/__tests__/wake-conversation-ops-completion.test.ts +107 -0
  343. package/src/daemon/abort-watchdog.ts +7 -0
  344. package/src/daemon/app-source-watcher.ts +9 -0
  345. package/src/daemon/assistant-attachments.ts +42 -0
  346. package/src/daemon/conversation-agent-loop-handlers.ts +256 -147
  347. package/src/daemon/conversation-agent-loop.ts +61 -68
  348. package/src/daemon/conversation-error.ts +1 -15
  349. package/src/daemon/conversation-history.ts +32 -236
  350. package/src/daemon/conversation-initial-prompt.ts +77 -0
  351. package/src/daemon/conversation-lifecycle.ts +25 -1
  352. package/src/daemon/conversation-messaging.ts +2 -2
  353. package/src/daemon/conversation-plugin-scope.ts +38 -0
  354. package/src/daemon/conversation-process.ts +50 -14
  355. package/src/daemon/conversation-runtime-assembly.ts +36 -6
  356. package/src/daemon/conversation-skill-tools.ts +19 -3
  357. package/src/daemon/conversation-store.ts +3 -3
  358. package/src/daemon/conversation-surfaces.ts +171 -0
  359. package/src/daemon/conversation-tool-setup.test.ts +95 -0
  360. package/src/daemon/conversation-tool-setup.ts +116 -1
  361. package/src/daemon/conversation-turn-finalize.ts +211 -0
  362. package/src/daemon/conversation.ts +92 -1
  363. package/src/daemon/disk-pressure-policy.ts +2 -1
  364. package/src/daemon/event-loop-watchdog.ts +22 -6
  365. package/src/daemon/external-plugins-bootstrap.ts +47 -134
  366. package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +1 -1
  367. package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +26 -14
  368. package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +27 -14
  369. package/src/daemon/handlers/__tests__/config-channels.test.ts +0 -5
  370. package/src/daemon/handlers/config-a2a.ts +7 -14
  371. package/src/daemon/handlers/config-channels.ts +2 -10
  372. package/src/daemon/handlers/conversation-history.ts +3 -1
  373. package/src/daemon/handlers/conversations.ts +0 -52
  374. package/src/daemon/handlers/shared.ts +4 -24
  375. package/src/daemon/handlers/skills.ts +4 -1
  376. package/src/daemon/host-cu-proxy.ts +1 -1
  377. package/src/daemon/lifecycle.ts +59 -472
  378. package/src/daemon/message-protocol.ts +3 -3
  379. package/src/daemon/message-provenance.ts +12 -14
  380. package/src/daemon/message-types/background-tools.ts +28 -0
  381. package/src/daemon/message-types/contacts.ts +2 -10
  382. package/src/daemon/message-types/conversations.ts +10 -0
  383. package/src/daemon/message-types/integrations.ts +2 -0
  384. package/src/daemon/message-types/surfaces.ts +1 -0
  385. package/src/daemon/message-types/sync.ts +1 -0
  386. package/src/daemon/pointer-conversation-trust.ts +23 -9
  387. package/src/daemon/pointer-turn-runner.ts +174 -0
  388. package/src/daemon/providers-setup.ts +16 -0
  389. package/src/daemon/shutdown-handlers.ts +139 -47
  390. package/src/daemon/skill-memory-refresh.ts +2 -2
  391. package/src/daemon/sqlite-corruption-watchdog.test.ts +217 -0
  392. package/src/daemon/sqlite-corruption-watchdog.ts +155 -0
  393. package/src/daemon/tool-setup-types.ts +8 -0
  394. package/src/daemon/trust-context.ts +21 -6
  395. package/src/daemon/turn-latency-tracker.test.ts +93 -0
  396. package/src/daemon/turn-latency-tracker.ts +38 -0
  397. package/src/daemon/wake-conversation-ops.ts +10 -8
  398. package/src/documents/document-comments-store.test.ts +7 -1
  399. package/src/documents/document-comments-store.ts +8 -0
  400. package/src/documents/document-store.ts +15 -0
  401. package/src/export/__tests__/transcript-formatter.test.ts +11 -0
  402. package/src/export/transcript-formatter.ts +49 -67
  403. package/src/home/__tests__/feed-writer.test.ts +247 -1
  404. package/src/home/conversation-starter-checkpoints.ts +1 -0
  405. package/src/home/feed-writer.ts +61 -0
  406. package/src/home/job-handlers/conversation-starters.ts +1 -0
  407. package/src/hooks/hook-loader.ts +12 -0
  408. package/src/hooks/registry.ts +32 -4
  409. package/src/inbound/public-ingress-urls.ts +0 -17
  410. package/src/ipc/__tests__/contacts-info-ipc.test.ts +285 -0
  411. package/src/ipc/assistant-server.ts +28 -7
  412. package/src/ipc/routes/__tests__/contacts-mirror-apply-atomicity.test.ts +62 -0
  413. package/src/ipc/routes/__tests__/contacts-mirror-apply.test.ts +272 -0
  414. package/src/ipc/routes/__tests__/contacts-mirror-ipc-routes.test.ts +322 -0
  415. package/src/ipc/routes/__tests__/invite-ipc-routes.test.ts +60 -4
  416. package/src/ipc/routes/contacts-info-ipc-routes.ts +239 -0
  417. package/src/ipc/routes/contacts-mirror-ipc-routes.ts +202 -0
  418. package/src/ipc/routes/db-proxy.ts +5 -2
  419. package/src/ipc/routes/invite-ipc-routes.ts +53 -39
  420. package/src/jobs/worker.ts +9 -2
  421. package/src/live-voice/__tests__/live-voice-archive.test.ts +10 -1
  422. package/src/live-voice/live-voice-archive.ts +5 -0
  423. package/src/messaging/providers/__tests__/transport-dispatch.test.ts +22 -6
  424. package/src/messaging/providers/channel-transport.ts +6 -0
  425. package/src/messaging/providers/index.ts +3 -0
  426. package/src/messaging/providers/slack/api.test.ts +49 -1
  427. package/src/messaging/providers/slack/api.ts +110 -0
  428. package/src/messaging/providers/slack/send.test.ts +85 -0
  429. package/src/messaging/providers/slack/send.ts +100 -28
  430. package/src/messaging/providers/slack/transport.ts +7 -13
  431. package/src/messaging/providers/whatsapp/send.ts +29 -9
  432. package/src/monitoring/__tests__/control.test.ts +235 -0
  433. package/src/monitoring/__tests__/sample-ring-buffer.test.ts +96 -0
  434. package/src/monitoring/control.ts +105 -0
  435. package/src/monitoring/resource-sampler.ts +258 -0
  436. package/src/monitoring/sample-ring-buffer.ts +108 -0
  437. package/src/monitoring/worker.ts +94 -0
  438. package/src/notifications/README.md +1 -1
  439. package/src/notifications/access-request-copy.ts +94 -23
  440. package/src/notifications/adapters/slack.ts +89 -21
  441. package/src/notifications/approval-card-builder.ts +33 -18
  442. package/src/notifications/approval-card-data.ts +48 -15
  443. package/src/notifications/broadcaster.ts +25 -16
  444. package/src/notifications/decision-engine.ts +131 -32
  445. package/src/notifications/guardian-question-mode.ts +58 -10
  446. package/src/permissions/approval-policy.ts +8 -1
  447. package/src/permissions/checker.ts +2 -0
  448. package/src/permissions/gateway-threshold-reader.ts +7 -6
  449. package/src/permissions/ipc-risk-types.ts +1 -0
  450. package/src/permissions/secret-prompter.ts +11 -3
  451. package/src/permissions/types.ts +2 -2
  452. package/src/persistence/__tests__/conversation-queries-search.test.ts +397 -0
  453. package/src/persistence/__tests__/conversation-search-lexical.test.ts +103 -0
  454. package/src/persistence/__tests__/jobs-worker-memory-disabled.test.ts +95 -0
  455. package/src/persistence/__tests__/slow-query-log.test.ts +266 -0
  456. package/src/persistence/__tests__/slow-sync-log.test.ts +155 -0
  457. package/src/persistence/a2a-invite-store.ts +205 -0
  458. package/src/persistence/attachments-store.ts +94 -10
  459. package/src/persistence/checkpoints.ts +45 -0
  460. package/src/persistence/conversation-crud.ts +453 -260
  461. package/src/persistence/conversation-display-order-migration.ts +8 -2
  462. package/src/persistence/conversation-group-migration.test.ts +8 -0
  463. package/src/persistence/conversation-group-migration.ts +9 -0
  464. package/src/persistence/conversation-queries.ts +244 -125
  465. package/src/persistence/conversation-row-batch-delete.ts +9 -5
  466. package/src/persistence/conversation-search-lexical.ts +49 -0
  467. package/src/persistence/db-async-query.ts +42 -0
  468. package/src/persistence/db-connection.ts +13 -0
  469. package/src/persistence/delivery-channels.ts +0 -82
  470. package/src/persistence/delivery-crud.ts +27 -6
  471. package/src/persistence/embeddings/__tests__/embedding-identity.test.ts +18 -5
  472. package/src/persistence/embeddings/__tests__/messages-lexical-index.test.ts +334 -0
  473. package/src/persistence/embeddings/embedding-identity.ts +17 -5
  474. package/src/persistence/embeddings/embedding-runtime-manager.ts +31 -0
  475. package/src/persistence/embeddings/messages-lexical-index.ts +396 -0
  476. package/src/persistence/embeddings/qdrant-client.ts +81 -5
  477. package/src/persistence/group-crud.ts +13 -1
  478. package/src/persistence/job-handlers/__tests__/message-lexical-backfill.test.ts +506 -0
  479. package/src/persistence/job-handlers/__tests__/message-lexical-enqueue.test.ts +164 -0
  480. package/src/persistence/job-handlers/__tests__/message-lexical.test.ts +259 -0
  481. package/src/persistence/job-handlers/cleanup.ts +57 -9
  482. package/src/persistence/job-handlers/message-lexical-backfill.ts +187 -0
  483. package/src/persistence/job-handlers/message-lexical.ts +251 -0
  484. package/src/persistence/jobs-store.ts +102 -8
  485. package/src/persistence/jobs-worker.ts +68 -28
  486. package/src/persistence/llm-request-log-store.ts +35 -19
  487. package/src/persistence/llm-usage-store.ts +11 -0
  488. package/src/persistence/memory-lifecycle-hooks.test.ts +65 -0
  489. package/src/persistence/memory-lifecycle-hooks.ts +55 -1
  490. package/src/persistence/migrations/028-call-session-mode.ts +1 -1
  491. package/src/persistence/migrations/116-messages-fts.ts +8 -1
  492. package/src/persistence/migrations/311-create-subagents-table.ts +50 -0
  493. package/src/persistence/migrations/312-drop-inbox-conversation-state-table.ts +15 -0
  494. package/src/persistence/migrations/313-drop-messages-fts.ts +17 -0
  495. package/src/persistence/migrations/314-add-conversation-enabled-plugins.ts +25 -0
  496. package/src/persistence/migrations/315-create-a2a-invites.ts +58 -0
  497. package/src/persistence/migrations/316-drop-contact-channels-invite-id.ts +28 -0
  498. package/src/persistence/migrations/317-canonical-guardian-requester-signals.ts +24 -0
  499. package/src/persistence/migrations/318-drop-contact-channel-telemetry.ts +32 -0
  500. package/src/persistence/migrations/__tests__/315-create-a2a-invites.test.ts +161 -0
  501. package/src/persistence/migrations/__tests__/316-drop-contact-channels-invite-id.test.ts +156 -0
  502. package/src/persistence/migrations/__tests__/318-drop-contact-channel-telemetry.test.ts +134 -0
  503. package/src/persistence/migrations/__tests__/migration-prefix-guard.test.ts +99 -0
  504. package/src/persistence/raw-query.ts +50 -11
  505. package/src/persistence/schema/a2a.ts +14 -0
  506. package/src/persistence/schema/contacts.ts +0 -62
  507. package/src/persistence/schema/conversation-groups.ts +12 -0
  508. package/src/persistence/schema/conversations.ts +2 -0
  509. package/src/persistence/schema/documents.ts +65 -0
  510. package/src/persistence/schema/guardian.ts +4 -0
  511. package/src/persistence/schema/index.ts +4 -0
  512. package/src/persistence/schema/memory-injection.ts +62 -0
  513. package/src/persistence/schema/workflows.ts +55 -0
  514. package/src/persistence/slow-query-log.ts +437 -0
  515. package/src/persistence/slow-sync-log.ts +204 -0
  516. package/src/persistence/steps.ts +16 -0
  517. package/src/persistence/subagent-store.ts +125 -0
  518. package/src/persistence/worker-control.ts +45 -238
  519. package/src/plugin-api/event-hub-facade.ts +172 -0
  520. package/src/plugin-api/index.ts +9 -1
  521. package/src/plugin-api/types.ts +40 -9
  522. package/src/plugins/defaults/default-plugin-names.ts +60 -0
  523. package/src/plugins/defaults/index.ts +19 -29
  524. package/src/plugins/defaults/memory/__tests__/conversation-queries.test.ts +46 -67
  525. package/src/plugins/defaults/memory/__tests__/db-async-query.test.ts +36 -1
  526. package/src/plugins/defaults/memory/__tests__/find-most-recent-retrospective-for.test.ts +2 -4
  527. package/src/plugins/defaults/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +2 -2
  528. package/src/plugins/defaults/memory/__tests__/jobs-worker-v2-schedule.test.ts +9 -0
  529. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +7 -4
  530. package/src/plugins/defaults/memory/config.ts +11 -0
  531. package/src/plugins/defaults/memory/context-search/agent-runner.ts +1 -1
  532. package/src/plugins/defaults/memory/context-search/sources/conversations.ts +124 -113
  533. package/src/plugins/defaults/memory/embeddings.ts +77 -0
  534. package/src/plugins/defaults/memory/find-most-recent-retrospective-for.ts +66 -0
  535. package/src/plugins/defaults/memory/frontmatter.ts +50 -0
  536. package/src/plugins/defaults/memory/graph/bootstrap.test.ts +36 -10
  537. package/src/plugins/defaults/memory/graph/bootstrap.ts +6 -5
  538. package/src/plugins/defaults/memory/graph/consolidation.ts +3 -5
  539. package/src/plugins/defaults/memory/graph/extraction.ts +3 -6
  540. package/src/plugins/defaults/memory/graph/graph-search.ts +12 -23
  541. package/src/plugins/defaults/memory/graph/narrative.ts +3 -5
  542. package/src/plugins/defaults/memory/graph/pattern-scan.ts +3 -5
  543. package/src/plugins/defaults/memory/graph/retriever.test.ts +1 -6
  544. package/src/plugins/defaults/memory/graph/retriever.ts +5 -10
  545. package/src/plugins/defaults/memory/graph/tools.ts +1 -1
  546. package/src/plugins/defaults/memory/hooks/init.ts +31 -5
  547. package/src/plugins/defaults/memory/indexer.ts +2 -3
  548. package/src/plugins/defaults/memory/injectors.ts +2 -2
  549. package/src/plugins/defaults/memory/job-handler-registration.ts +96 -0
  550. package/src/plugins/defaults/memory/job-handlers/backfill.ts +4 -26
  551. package/src/plugins/defaults/memory/job-handlers/embedding.test.ts +6 -19
  552. package/src/plugins/defaults/memory/job-handlers/embedding.ts +9 -22
  553. package/src/plugins/defaults/memory/job-handlers/index-maintenance.ts +2 -3
  554. package/src/plugins/defaults/memory/job-handlers.ts +13 -12
  555. package/src/plugins/defaults/memory/jobs/embed-concept-page.ts +1 -1
  556. package/src/plugins/defaults/memory/llm-helpers.ts +76 -0
  557. package/src/plugins/defaults/memory/memory-retrospective-job.ts +8 -5
  558. package/src/plugins/defaults/memory/memory-retrospective-startup-cleanup.ts +2 -2
  559. package/src/plugins/defaults/memory/memory-v2-concept-frequency.ts +2 -0
  560. package/src/plugins/defaults/memory/persistence-hooks.ts +46 -1
  561. package/src/plugins/defaults/memory/pkb/pkb-index.ts +1 -5
  562. package/src/plugins/defaults/memory/pkb/pkb-search.ts +2 -2
  563. package/src/plugins/defaults/memory/routes/__tests__/memory-v2-simulate-route.test.ts +4 -6
  564. package/src/plugins/defaults/memory/routes/memory-item-routes.ts +1 -1
  565. package/src/plugins/defaults/memory/search/semantic.ts +2 -2
  566. package/src/plugins/defaults/memory/startup.ts +244 -0
  567. package/src/plugins/defaults/memory/task-memory-cleanup.ts +8 -0
  568. package/src/{daemon → plugins/defaults/memory/v2}/__tests__/memory-v2-startup.test.ts +6 -6
  569. package/src/plugins/defaults/memory/v2/__tests__/migration.test.ts +2 -13
  570. package/src/plugins/defaults/memory/v2/__tests__/router.test.ts +4 -5
  571. package/src/plugins/defaults/memory/v2/__tests__/sweep-job.test.ts +1 -8
  572. package/src/plugins/defaults/memory/v2/activation.ts +2 -4
  573. package/src/plugins/defaults/memory/v2/cli-command-store.ts +2 -4
  574. package/src/plugins/defaults/memory/v2/frontmatter-sweep.ts +1 -1
  575. package/src/{daemon → plugins/defaults/memory/v2}/memory-v2-startup.test.ts +8 -8
  576. package/src/{daemon → plugins/defaults/memory/v2}/memory-v2-startup.ts +20 -22
  577. package/src/plugins/defaults/memory/v2/migration.ts +2 -5
  578. package/src/plugins/defaults/memory/v2/page-store.ts +1 -1
  579. package/src/plugins/defaults/memory/v2/qdrant.ts +2 -3
  580. package/src/plugins/defaults/memory/v2/router.ts +5 -5
  581. package/src/plugins/defaults/memory/v2/sim.ts +2 -4
  582. package/src/plugins/defaults/memory/v2/skill-store.ts +2 -4
  583. package/src/plugins/defaults/memory/v2/sweep-job.ts +6 -6
  584. package/src/plugins/defaults/memory/v3/__tests__/carry-integration.test.ts +11 -9
  585. package/src/plugins/defaults/memory/v3/__tests__/dense.test.ts +107 -2
  586. package/src/plugins/defaults/memory/v3/__tests__/gate-flag.test.ts +69 -0
  587. package/src/plugins/defaults/memory/v3/__tests__/gate.test.ts +181 -0
  588. package/src/plugins/defaults/memory/v3/__tests__/live-integration.test.ts +7 -3
  589. package/src/plugins/defaults/memory/v3/__tests__/orchestrate.test.ts +345 -8
  590. package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +1 -4
  591. package/src/plugins/defaults/memory/v3/__tests__/section-needle.test.ts +64 -0
  592. package/src/plugins/defaults/memory/v3/__tests__/shadow-integration.test.ts +18 -13
  593. package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +62 -1
  594. package/src/plugins/defaults/memory/v3/card.ts +1 -4
  595. package/src/plugins/defaults/memory/v3/dense.ts +60 -25
  596. package/src/plugins/defaults/memory/v3/edge.ts +1 -1
  597. package/src/plugins/defaults/memory/v3/gate-flag.ts +20 -0
  598. package/src/plugins/defaults/memory/v3/gate.ts +158 -0
  599. package/src/plugins/defaults/memory/v3/maintain-job.ts +2 -4
  600. package/src/plugins/defaults/memory/v3/orchestrate.ts +186 -25
  601. package/src/plugins/defaults/memory/v3/pool-select.test.ts +7 -9
  602. package/src/plugins/defaults/memory/v3/pool-select.ts +6 -6
  603. package/src/plugins/defaults/memory/v3/prune.ts +2 -2
  604. package/src/plugins/defaults/memory/v3/section-dense-store.ts +8 -11
  605. package/src/plugins/defaults/memory/v3/section-needle.ts +27 -7
  606. package/src/plugins/defaults/memory/v3/shadow-plugin.ts +10 -0
  607. package/src/plugins/defaults/memory/v3-eval/eval-packets.ts +1 -4
  608. package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +5 -0
  609. package/src/plugins/defaults/turn-context/injectors.ts +1 -0
  610. package/src/plugins/defaults/turn-context/unified-turn-context.ts +12 -0
  611. package/src/plugins/injector-registry.ts +13 -2
  612. package/src/plugins/mtime-cache.ts +36 -47
  613. package/src/plugins/pipeline.ts +15 -1
  614. package/src/plugins/types.ts +14 -23
  615. package/src/prompts/__tests__/system-prompt.test.ts +170 -32
  616. package/src/prompts/persona-resolver.ts +13 -9
  617. package/src/prompts/system-prompt.ts +30 -9
  618. package/src/prompts/templates/BOOTSTRAP.md +16 -119
  619. package/src/prompts/templates/SOUL.md +43 -13
  620. package/src/prompts/templates/system-sections.ts +32 -27
  621. package/src/prompts/templates/users/default.md +19 -0
  622. package/src/providers/__tests__/placeholder-sentinels.test.ts +56 -0
  623. package/src/providers/inference/__tests__/base-url-route-validation.test.ts +96 -1
  624. package/src/providers/model-intents.ts +2 -2
  625. package/src/providers/openai/chat-completions-provider.ts +14 -1
  626. package/src/providers/openai/responses-provider.ts +18 -0
  627. package/src/providers/placeholder-sentinels.ts +18 -8
  628. package/src/providers/retry.ts +1 -0
  629. package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +0 -64
  630. package/src/providers/speech-to-text/__tests__/resolve.test.ts +92 -79
  631. package/src/providers/speech-to-text/deepgram-realtime.test.ts +98 -0
  632. package/src/providers/speech-to-text/deepgram-realtime.ts +62 -3
  633. package/src/providers/speech-to-text/provider-catalog.ts +0 -89
  634. package/src/providers/speech-to-text/resolve.ts +55 -0
  635. package/src/providers/tool-progress-events.test.ts +50 -0
  636. package/src/providers/tool-progress-events.ts +99 -0
  637. package/src/providers/types.ts +1 -1
  638. package/src/providers/vellum-model-routing.test.ts +51 -0
  639. package/src/providers/vellum-model-routing.ts +84 -0
  640. package/src/runtime/AGENTS.md +5 -4
  641. package/src/runtime/__tests__/agent-wake.test.ts +158 -1
  642. package/src/runtime/__tests__/channel-verification-service.test.ts +2 -2
  643. package/src/runtime/__tests__/trust-verdict-consumer.test.ts +37 -8
  644. package/src/runtime/access-request-helper.ts +154 -3
  645. package/src/runtime/actor-trust-resolver.ts +7 -21
  646. package/src/runtime/agent-wake.ts +41 -1
  647. package/src/runtime/assistant-event.ts +86 -12
  648. package/src/runtime/auth/__tests__/route-policy.test.ts +31 -0
  649. package/src/runtime/channel-approval-types.ts +52 -2
  650. package/src/runtime/channel-approvals.ts +23 -8
  651. package/src/runtime/channel-invite-transport.ts +2 -2
  652. package/src/runtime/channel-invite-transports/voice.ts +1 -1
  653. package/src/runtime/channel-readiness-service.ts +3 -5
  654. package/src/runtime/channel-retry-sweep.ts +18 -24
  655. package/src/runtime/channel-verification-service.ts +9 -11
  656. package/src/runtime/finalize-event-delivery.ts +30 -16
  657. package/src/runtime/guardian-action-service.ts +10 -11
  658. package/src/runtime/guardian-reply-router.ts +122 -21
  659. package/src/runtime/http-server.ts +44 -101
  660. package/src/runtime/introduction-policy.test.ts +214 -0
  661. package/src/runtime/introduction-policy.ts +284 -0
  662. package/src/runtime/invite-service.ts +104 -361
  663. package/src/runtime/local-actor-identity.test.ts +38 -0
  664. package/src/runtime/local-actor-identity.ts +14 -0
  665. package/src/runtime/middleware/__tests__/rate-limiter.test.ts +22 -0
  666. package/src/runtime/middleware/rate-limiter.ts +30 -0
  667. package/src/runtime/middleware/twilio-validation.ts +5 -7
  668. package/src/runtime/migrations/__tests__/gcs-signed-url.test.ts +28 -0
  669. package/src/runtime/migrations/origin-mode.ts +6 -6
  670. package/src/runtime/pre-first-message-gate.ts +1 -0
  671. package/src/runtime/routes/__tests__/channel-verification-revoke.test.ts +6 -35
  672. package/src/runtime/routes/__tests__/consolidation-routes.test.ts +24 -3
  673. package/src/runtime/routes/__tests__/contact-routes.test.ts +80 -15
  674. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +7 -1
  675. package/src/runtime/routes/__tests__/global-search-routes.test.ts +0 -3
  676. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +142 -1
  677. package/src/runtime/routes/__tests__/invite-relay-routes.test.ts +119 -30
  678. package/src/runtime/routes/__tests__/migration-import-allowed-hosts-env.test.ts +70 -0
  679. package/src/runtime/routes/__tests__/monitoring-routes.test.ts +194 -0
  680. package/src/runtime/routes/__tests__/plugins-routes.test.ts +755 -29
  681. package/src/runtime/routes/__tests__/retrospective-routes.test.ts +4 -0
  682. package/src/runtime/routes/__tests__/schedule-routes-create.test.ts +70 -0
  683. package/src/runtime/routes/__tests__/schedule-worker-routes.test.ts +161 -0
  684. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +1 -1
  685. package/src/runtime/routes/__tests__/surface-content-routes.test.ts +1 -1
  686. package/src/runtime/routes/acp-routes.ts +15 -3
  687. package/src/runtime/routes/app-management-routes.ts +4 -7
  688. package/src/runtime/routes/approval-routes.ts +4 -0
  689. package/src/runtime/routes/attachment-routes.ts +54 -3
  690. package/src/runtime/routes/avatar-routes.ts +0 -4
  691. package/src/runtime/routes/background-tool-routes.ts +34 -5
  692. package/src/runtime/routes/btw-routes.ts +32 -4
  693. package/src/runtime/routes/channel-route-shared.ts +18 -14
  694. package/src/runtime/routes/channel-verification-routes.ts +0 -3
  695. package/src/runtime/routes/contact-routes.ts +298 -117
  696. package/src/runtime/routes/conversation-list-routes.ts +6 -0
  697. package/src/runtime/routes/conversation-management-routes.ts +74 -37
  698. package/src/runtime/routes/conversation-query-routes.ts +5 -2
  699. package/src/runtime/routes/conversation-routes.ts +222 -82
  700. package/src/runtime/routes/conversations-import-routes.ts +8 -1
  701. package/src/runtime/routes/credential-prompt-routes.ts +20 -5
  702. package/src/runtime/routes/debug-routes.ts +1 -0
  703. package/src/runtime/routes/documents-routes.ts +5 -2
  704. package/src/runtime/routes/empty-state-greeting-cache.ts +37 -10
  705. package/src/runtime/routes/global-search-routes.ts +2 -1
  706. package/src/runtime/routes/guardian-approval-interception.ts +51 -0
  707. package/src/runtime/routes/home-feed-routes.ts +60 -1
  708. package/src/runtime/routes/identity-routes.ts +4 -87
  709. package/src/runtime/routes/inbound-message-handler.ts +82 -41
  710. package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +430 -57
  711. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +302 -572
  712. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +76 -270
  713. package/src/runtime/routes/inbound-stages/background-dispatch.ts +21 -116
  714. package/src/runtime/routes/inbound-stages/edit-intercept.ts +9 -1
  715. package/src/runtime/routes/inbound-stages/escalation-intercept.test.ts +158 -0
  716. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +22 -1
  717. package/src/runtime/routes/index.ts +6 -0
  718. package/src/runtime/routes/inference-provider-connection-routes.ts +22 -16
  719. package/src/runtime/routes/internal-twilio-routes.ts +0 -15
  720. package/src/runtime/routes/messages-lexical-routes.ts +75 -0
  721. package/src/runtime/routes/migration-routes.ts +69 -6
  722. package/src/runtime/routes/monitoring-routes.ts +237 -0
  723. package/src/runtime/routes/plugins-routes.ts +329 -8
  724. package/src/runtime/routes/schedule-routes.ts +49 -8
  725. package/src/runtime/routes/schedule-worker-routes.ts +210 -0
  726. package/src/runtime/routes/secret-routes.ts +1 -1
  727. package/src/runtime/routes/surface-conversation-resolver.ts +1 -0
  728. package/src/runtime/services/__tests__/analyze-conversation.test.ts +54 -9
  729. package/src/runtime/services/__tests__/auto-analysis-enqueue.test.ts +30 -6
  730. package/src/runtime/services/__tests__/conversation-analyze-job.test.ts +43 -8
  731. package/src/runtime/services/__tests__/conversation-serializer.test.ts +20 -0
  732. package/src/runtime/services/analyze-conversation.ts +14 -4
  733. package/src/runtime/services/conversation-serializer.ts +5 -0
  734. package/src/runtime/slack-no-response.ts +47 -0
  735. package/src/runtime/slack-reply-session.test.ts +633 -0
  736. package/src/runtime/slack-reply-session.ts +382 -0
  737. package/src/runtime/slack-task-progress.ts +95 -0
  738. package/src/runtime/sync/resource-sync-events.ts +25 -0
  739. package/src/runtime/tool-grant-request-helper.ts +17 -1
  740. package/src/runtime/trust-class.test.ts +141 -0
  741. package/src/runtime/trust-class.ts +121 -0
  742. package/src/runtime/trust-verdict-consumer.ts +7 -6
  743. package/src/schedule/__tests__/run-script.test.ts +21 -0
  744. package/src/schedule/__tests__/worker-control.test.ts +241 -0
  745. package/src/schedule/run-script.ts +13 -6
  746. package/src/schedule/schedule-usage-store.ts +1 -0
  747. package/src/schedule/scheduler.ts +178 -127
  748. package/src/schedule/worker-control.ts +118 -0
  749. package/src/schedule/worker.ts +131 -0
  750. package/src/skills/__tests__/categories-cache.test.ts +38 -0
  751. package/src/skills/categories-cache.ts +40 -5
  752. package/src/skills/managed-store.ts +41 -6
  753. package/src/skills/validate-input.ts +47 -0
  754. package/src/stt/types.ts +5 -9
  755. package/src/subagent/manager.ts +267 -39
  756. package/src/subagent/types.ts +6 -2
  757. package/src/subagent/validate-allowlists.ts +65 -0
  758. package/src/telemetry/watchdog-direct-emit.test.ts +109 -0
  759. package/src/telemetry/watchdog-direct-emit.ts +89 -0
  760. package/src/tools/acp/list-agents.test.ts +0 -6
  761. package/src/tools/background-tool-registry.ts +61 -0
  762. package/src/tools/document/document-comment-tool.test.ts +4 -0
  763. package/src/tools/host-terminal/host-shell.test.ts +397 -0
  764. package/src/tools/host-terminal/host-shell.ts +203 -14
  765. package/src/tools/skills/find-similar-skills.test.ts +152 -1
  766. package/src/tools/skills/find-similar-skills.ts +30 -4
  767. package/src/tools/skills/load.ts +36 -0
  768. package/src/tools/skills/scaffold-managed.ts +66 -34
  769. package/src/tools/skills/skill-script-runner.ts +17 -0
  770. package/src/tools/skills/skill-tool-factory.ts +5 -1
  771. package/src/tools/terminal/safe-env.ts +3 -0
  772. package/src/tools/terminal/shell.test.ts +177 -0
  773. package/src/tools/terminal/shell.ts +98 -4
  774. package/src/tools/tool-approval-handler.ts +106 -29
  775. package/{node_modules/@vellumai/skill-host-contracts/src → src/tools}/tool-types.ts +16 -17
  776. package/src/tools/types.ts +29 -17
  777. package/src/tools/ui-surface/definitions.ts +3 -1
  778. package/src/tts/__tests__/provider-adapters.test.ts +37 -0
  779. package/src/tts/__tests__/provider-catalog.test.ts +21 -0
  780. package/src/tts/provider-catalog.ts +40 -9
  781. package/src/tts/providers/fish-audio-provider.ts +19 -10
  782. package/src/tts/types.ts +3 -4
  783. package/src/util/__tests__/process-tree.test.ts +10 -2
  784. package/src/util/__tests__/text-spacing.test.ts +53 -0
  785. package/src/util/cgroup-memory.ts +142 -0
  786. package/src/util/image-conversion.ts +281 -0
  787. package/src/util/phone.ts +1 -8
  788. package/src/util/platform.ts +33 -0
  789. package/src/util/process-tree.ts +10 -2
  790. package/src/util/text-spacing.ts +53 -0
  791. package/src/util/worker-process.ts +271 -0
  792. package/src/workflows/journal-store.ts +16 -1
  793. package/src/workflows/library.test.ts +35 -20
  794. package/src/workflows/library.ts +105 -66
  795. package/src/workspace/git-service.ts +1 -0
  796. package/src/workspace/migrations/011-backfill-installation-id.ts +83 -31
  797. package/src/workspace/migrations/041-backfill-google-gmail-settings-scope.ts +41 -22
  798. package/src/workspace/migrations/070-memory-v2-summary-schema-rebuild.ts +1 -1
  799. package/src/workspace/migrations/118-seed-now-md.ts +64 -0
  800. package/src/workspace/migrations/119-strip-persisted-memory-v3-tuning-defaults.ts +169 -0
  801. package/src/workspace/migrations/120-revise-onboarding-threads.ts +47 -0
  802. package/src/workspace/migrations/121-seed-default-user-guardrails.ts +82 -0
  803. package/src/workspace/migrations/122-relocate-default-user-boundary.ts +111 -0
  804. package/src/workspace/migrations/123-swap-quality-profile-to-fable.ts +85 -0
  805. package/src/workspace/migrations/__tests__/119-strip-persisted-memory-v3-tuning-defaults.test.ts +181 -0
  806. package/src/workspace/migrations/registry.ts +12 -0
  807. package/drizzle/0000_dizzy_maggott.sql +0 -301
  808. package/drizzle/meta/0000_snapshot.json +0 -1933
  809. package/drizzle/meta/_journal.json +0 -13
  810. package/node_modules/@vellumai/skill-host-contracts/__tests__/client.test.ts +0 -887
  811. package/node_modules/@vellumai/skill-host-contracts/bun.lock +0 -24
  812. package/node_modules/@vellumai/skill-host-contracts/package.json +0 -18
  813. package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +0 -97
  814. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +0 -1349
  815. package/node_modules/@vellumai/skill-host-contracts/src/index.ts +0 -6
  816. package/node_modules/@vellumai/skill-host-contracts/src/runtime-mode.ts +0 -11
  817. package/node_modules/@vellumai/skill-host-contracts/src/server-message.ts +0 -32
  818. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +0 -333
  819. package/node_modules/@vellumai/skill-host-contracts/tsconfig.json +0 -20
  820. package/node_modules/@vellumai/skill-host-contracts/tsconfig.test.json +0 -12
  821. package/src/__tests__/invite-redemption-service.test.ts +0 -1180
  822. package/src/__tests__/invite-service-ipc.test.ts +0 -350
  823. package/src/__tests__/job-handler-registry-guard.test.ts +0 -125
  824. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +0 -203
  825. package/src/__tests__/relay-server.test.ts +0 -6153
  826. package/src/__tests__/telephony-stt-routing.test.ts +0 -329
  827. package/src/__tests__/voice-invite-redemption.test.ts +0 -702
  828. package/src/calls/relay-server.ts +0 -2463
  829. package/src/calls/telephony-stt-routing.ts +0 -145
  830. package/src/contacts/__tests__/contacts-write-revoke-relay.test.ts +0 -128
  831. package/src/daemon/__tests__/daemon-skill-host.test.ts +0 -273
  832. package/src/daemon/daemon-skill-host.ts +0 -262
  833. package/src/daemon/message-types/meet.ts +0 -143
  834. package/src/daemon/shutdown-registry.ts +0 -40
  835. package/src/ipc/__tests__/skill-server-bidirectional.test.ts +0 -253
  836. package/src/ipc/__tests__/skill-server.test.ts +0 -182
  837. package/src/ipc/skill-ipc-types.ts +0 -54
  838. package/src/ipc/skill-routes/__tests__/config.test.ts +0 -146
  839. package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +0 -448
  840. package/src/ipc/skill-routes/__tests__/identity.test.ts +0 -62
  841. package/src/ipc/skill-routes/__tests__/log.test.ts +0 -133
  842. package/src/ipc/skill-routes/__tests__/memory.test.ts +0 -203
  843. package/src/ipc/skill-routes/__tests__/platform.test.ts +0 -111
  844. package/src/ipc/skill-routes/__tests__/providers.test.ts +0 -265
  845. package/src/ipc/skill-routes/__tests__/registries.test.ts +0 -400
  846. package/src/ipc/skill-routes/config.ts +0 -47
  847. package/src/ipc/skill-routes/events.ts +0 -147
  848. package/src/ipc/skill-routes/identity.ts +0 -21
  849. package/src/ipc/skill-routes/index.ts +0 -37
  850. package/src/ipc/skill-routes/log.ts +0 -40
  851. package/src/ipc/skill-routes/memory.ts +0 -92
  852. package/src/ipc/skill-routes/platform.ts +0 -39
  853. package/src/ipc/skill-routes/providers.ts +0 -162
  854. package/src/ipc/skill-routes/registries.ts +0 -338
  855. package/src/ipc/skill-server.ts +0 -831
  856. package/src/ipc/skill-socket-path.ts +0 -20
  857. package/src/jobs/register-job-handlers.ts +0 -74
  858. package/src/persistence/invite-store.ts +0 -481
  859. package/src/plugins/job-handler-registry.ts +0 -94
  860. package/src/providers/cache-control.ts +0 -26
  861. package/src/runtime/__tests__/runtime-mode.test.ts +0 -62
  862. package/src/runtime/invite-redemption-service.ts +0 -828
  863. package/src/runtime/invite-redemption-templates.ts +0 -46
  864. package/src/runtime/runtime-mode.ts +0 -40
  865. package/src/runtime/slack-dm-text-delivery.ts +0 -177
  866. package/src/util/voice-code.ts +0 -31
@@ -31,7 +31,10 @@ import {
31
31
  getSilenceTimeoutMs,
32
32
  getUserConsultationTimeoutMs,
33
33
  } from "./call-constants.js";
34
- import { addPointerMessage, formatDuration } from "./call-pointer-messages.js";
34
+ import {
35
+ formatDuration,
36
+ postPointerMessageSafe,
37
+ } from "./call-pointer-messages.js";
35
38
  import {
36
39
  fireCallQuestionNotifier,
37
40
  fireCallTranscriptNotifier,
@@ -239,7 +242,7 @@ export class CallController {
239
242
  }
240
243
 
241
244
  /**
242
- * Handle a final caller utterance from the ConversationRelay.
245
+ * Handle a final caller utterance from the call transport.
243
246
  * Caller utterances always trigger normal turns, even when a guardian
244
247
  * consultation is pending — the consultation is tracked separately.
245
248
  */
@@ -361,10 +364,15 @@ export class CallController {
361
364
  * interruption on initial inbound media frames that arrive before
362
365
  * the assistant has had a chance to produce its first response.
363
366
  *
367
+ * @param onAccepted Invoked synchronously after the speaking gate
368
+ * passes but before {@link handleInterrupt} runs. Transports use this
369
+ * to flush queued outbound audio without wiping the end-of-turn mark
370
+ * that handleInterrupt enqueues — and without flushing at all when
371
+ * the barge-in is ignored.
364
372
  * @returns `true` if the barge-in was accepted (assistant was speaking),
365
373
  * `false` if it was ignored (assistant idle or processing).
366
374
  */
367
- handleBargeIn(): boolean {
375
+ handleBargeIn(onAccepted?: () => void): boolean {
368
376
  if (this.state !== "speaking") {
369
377
  log.debug(
370
378
  {
@@ -380,6 +388,7 @@ export class CallController {
380
388
  { callSessionId: this.callSessionId },
381
389
  "Barge-in accepted — interrupting assistant speech",
382
390
  );
391
+ onAccepted?.();
383
392
  this.handleInterrupt();
384
393
  return true;
385
394
  }
@@ -395,11 +404,6 @@ export class CallController {
395
404
  const wasSpeaking = this.state === "speaking";
396
405
  this.abortCurrentTurn();
397
406
  this.llmRunVersion++;
398
- // Cancel in-flight synthesized TTS on barge-in
399
- if (this.activeSynthesisAbort) {
400
- this.activeSynthesisAbort.abort();
401
- this.activeSynthesisAbort = null;
402
- }
403
407
  // Explicitly terminate the in-progress TTS turn so the relay can
404
408
  // immediately hand control back to the caller after barge-in.
405
409
  if (wasSpeaking) {
@@ -483,6 +487,16 @@ export class CallController {
483
487
  }
484
488
  this.abortController.abort();
485
489
  this.abortController = new AbortController();
490
+ // Abort any in-flight synthesized-TTS playback too, so a superseded or
491
+ // torn-down turn's audio isn't streamed to the caller after they move on.
492
+ if (this.activeSynthesisAbort) {
493
+ this.activeSynthesisAbort.abort();
494
+ this.activeSynthesisAbort = null;
495
+ }
496
+ // Drop the aborted turn's unsent buffered text on transports that
497
+ // accumulate tokens (media-stream), so it cannot leak into the next
498
+ // turn's synthesis.
499
+ this.transport.discardPendingText?.();
486
500
  }
487
501
 
488
502
  private formatCallerUtterance(
@@ -523,7 +537,10 @@ export class CallController {
523
537
  }
524
538
 
525
539
  try {
526
- this.state = "speaking";
540
+ // Stay in `processing` through the lock-wait and LLM generation; flip to
541
+ // `speaking` only when real outbound audio/tokens start (see
542
+ // beginSpeaking). This keeps barge-in from aborting a silent turn.
543
+ this.state = "processing";
527
544
 
528
545
  const fullResponseText = await this.streamTtsTokens(
529
546
  content,
@@ -561,6 +578,21 @@ export class CallController {
561
578
  );
562
579
  return;
563
580
  }
581
+ if (this.isLockContentionError(err) && this.isCurrentRun(runVersion)) {
582
+ log.debug(
583
+ { callSessionId: this.callSessionId },
584
+ "Prior voice turn wedged past lock-hold budget; re-prompting caller",
585
+ );
586
+ // Reaching here means the prior turn is genuinely wedged past the full
587
+ // lock-hold wait budget, so surface a brief natural re-prompt (never a
588
+ // technical-error message) and re-arm listening. last=true doubles as
589
+ // the end-of-turn marker.
590
+ this.transport.sendTextToken("Sorry, could you say that again?", true);
591
+ this.state = "idle";
592
+ this.resetSilenceTimer();
593
+ this.flushPendingInstructions();
594
+ return;
595
+ }
564
596
  log.error({ err, callSessionId: this.callSessionId }, "Voice turn error");
565
597
  this.transport.sendTextToken(
566
598
  "I'm sorry, I encountered a technical issue. Could you repeat that?",
@@ -586,13 +618,14 @@ export class CallController {
586
618
  // The catalog's callMode determines the call path: synthesized-play
587
619
  // providers buffer text, synthesize via provider API, and stream
588
620
  // audio chunks to Twilio via play-URL. Native-twilio providers
589
- // stream text tokens to the relay for Twilio's built-in TTS.
621
+ // stream text tokens through the transport, which re-synthesizes
622
+ // them via daemon TTS on media-stream.
590
623
  //
591
624
  // When the transport requires WAV (media-stream), request WAV so
592
625
  // the audio store entry and any downstream fetch/transcode receives
593
626
  // PCM that audioBufferToFrames can convert to mu-law.
594
627
  const { provider, useSynthesizedPath, audioFormat } =
595
- resolveCallTtsProvider({
628
+ await resolveCallTtsProvider({
596
629
  preferWav: this.transport.requiresWavAudio,
597
630
  });
598
631
 
@@ -613,6 +646,7 @@ export class CallController {
613
646
  if (useSynthesizedPath) {
614
647
  synthesizedTextBuffer += cleaned;
615
648
  } else {
649
+ this.beginSpeakingOnAudioStart(runVersion);
616
650
  this.transport.sendTextToken(cleaned, false);
617
651
  }
618
652
  };
@@ -732,6 +766,10 @@ export class CallController {
732
766
  const sanitizedSynthText = sanitizeForTts(synthesizedTextBuffer.trim());
733
767
  if (useSynthesizedPath && provider && sanitizedSynthText.length > 0) {
734
768
  if (!this.isCurrentRun(runVersion)) return fullResponseText;
769
+ // Do NOT flip to `speaking` here — provider synthesis latency (or the
770
+ // no-audio fallback window) would still be silent. The transition happens
771
+ // inside synthesizeAndStreamAudio when the play URL / first audio chunk
772
+ // (or native fallback token) is actually emitted.
735
773
  await this.synthesizeAndStreamAudio(
736
774
  provider,
737
775
  sanitizedSynthText,
@@ -740,12 +778,18 @@ export class CallController {
740
778
  );
741
779
  }
742
780
 
781
+ // Synthesized playback (and its native fallback) can await provider
782
+ // latency; re-check the run wasn't superseded meanwhile so a stale turn
783
+ // doesn't inject its end-of-turn marker (or fallback text) into the next
784
+ // turn's output stream.
785
+ if (!this.isCurrentRun(runVersion)) return fullResponseText;
786
+
743
787
  // Signal end of this turn's speech. An empty token with `last: true`
744
- // tells ConversationRelay to start listening — it does NOT trigger TTS
788
+ // tells the transport to start listening — it does NOT trigger TTS
745
789
  // synthesis. This is required even when a synthesized provider handled
746
- // all audio playback, because ConversationRelay still needs the
747
- // end-of-turn signal to transition from "assistant speaking" to
748
- // "caller speaking" state.
790
+ // all audio playback, because the transport still needs the end-of-turn
791
+ // signal to transition from "assistant speaking" to "caller speaking"
792
+ // state.
749
793
  this.transport.sendTextToken("", true);
750
794
 
751
795
  // Mark the greeting's first response as awaiting ack
@@ -764,7 +808,7 @@ export class CallController {
764
808
  private async synthesizeAndStreamAudio(
765
809
  provider: TtsProvider,
766
810
  text: string,
767
- _runVersion: number,
811
+ runVersion: number,
768
812
  format: "mp3" | "wav" | "opus" = "mp3",
769
813
  ): Promise<void> {
770
814
  let handle: ReturnType<typeof createStreamingEntry> | null = null;
@@ -788,6 +832,14 @@ export class CallController {
788
832
  const url = `${baseUrl}/v1/audio/${handle.audioId}`;
789
833
  const sendPlayUrlOnce = (): void => {
790
834
  if (playUrlSent) return;
835
+ // Superseded/aborted while synthesis was pending — don't start playing
836
+ // a stale response after the caller has already moved on.
837
+ if (!this.isCurrentRun(runVersion)) return;
838
+ // Audio is now reaching the caller (or, on transports with an
839
+ // audio-start signal, will be the moment the first fetched frame
840
+ // goes out) — flip to `speaking` so barge-in can interrupt (it
841
+ // stays `processing` until this point).
842
+ this.beginSpeakingOnAudioStart(runVersion);
791
843
  this.transport.sendPlayUrl(url);
792
844
  playUrlSent = true;
793
845
  };
@@ -870,11 +922,18 @@ export class CallController {
870
922
  { err, provider: provider.id, errName, errCode },
871
923
  "TTS synthesis failed — falling back to native token TTS",
872
924
  );
873
- // If synthesis fails before any audio has started, degrade to
874
- // token-based speech on ConversationRelay so the caller still
925
+ // If synthesis fails before any audio has started on a non-WAV
926
+ // transport, degrade to token-based speech so the caller still
875
927
  // hears a response instead of silence. This fallback is only
876
928
  // used for providers whose catalog entry allows native fallback.
877
- if (!playUrlSent && !this.transport.requiresWavAudio) {
929
+ // Skip it entirely for a superseded run so a stale response can't
930
+ // leak into the next caller turn.
931
+ if (
932
+ !playUrlSent &&
933
+ !this.transport.requiresWavAudio &&
934
+ this.isCurrentRun(runVersion)
935
+ ) {
936
+ this.beginSpeakingOnAudioStart(runVersion);
878
937
  this.transport.sendTextToken(text, false);
879
938
  }
880
939
  }
@@ -1196,22 +1255,14 @@ export class CallController {
1196
1255
  const durationMs = currentSession.startedAt
1197
1256
  ? Date.now() - currentSession.startedAt
1198
1257
  : 0;
1199
- addPointerMessage(
1258
+ postPointerMessageSafe(
1200
1259
  currentSession.initiatedFromConversationId,
1201
1260
  "completed",
1202
1261
  currentSession.toNumber,
1203
1262
  {
1204
1263
  duration: durationMs > 0 ? formatDuration(durationMs) : undefined,
1205
1264
  },
1206
- ).catch((err) => {
1207
- log.warn(
1208
- {
1209
- conversationId: currentSession.initiatedFromConversationId,
1210
- err,
1211
- },
1212
- "Skipping pointer write — origin conversation may no longer exist",
1213
- );
1214
- });
1265
+ );
1215
1266
  }
1216
1267
  this.state = "idle";
1217
1268
  }
@@ -1221,6 +1272,54 @@ export class CallController {
1221
1272
  return err.name === "AbortError" || err.name === "APIUserAbortError";
1222
1273
  }
1223
1274
 
1275
+ /**
1276
+ * Transient teardown race: a new voice turn reached the session bridge
1277
+ * before the previous turn released the conversation processing lock.
1278
+ * This is not a real error and must never be spoken to the caller.
1279
+ */
1280
+ private isLockContentionError(err: unknown): boolean {
1281
+ return (
1282
+ err instanceof Error &&
1283
+ err.message.includes("already processing a message")
1284
+ );
1285
+ }
1286
+
1287
+ /**
1288
+ * Flip from the pre-speech `processing` phase to `speaking` at the moment the
1289
+ * first real outbound audio/token is emitted. Guarded so a superseded or
1290
+ * aborted (idle) turn never (re)enters `speaking`, and so barge-in
1291
+ * (handleBargeIn, gated on `speaking`) can't abort a turn that is still
1292
+ * waiting for the processing lock or generating with no audio yet.
1293
+ */
1294
+ private beginSpeaking(runVersion: number): void {
1295
+ if (!this.isCurrentRun(runVersion)) return;
1296
+ if (this.state === "processing") {
1297
+ this.state = "speaking";
1298
+ }
1299
+ }
1300
+
1301
+ /**
1302
+ * Flip to `speaking` when outbound audio genuinely starts.
1303
+ *
1304
+ * Transports that buffer text and synthesize asynchronously (e.g.
1305
+ * media-stream) expose an audio-start signal; on those, the flip is
1306
+ * deferred until the transport reports the first audio frame actually
1307
+ * went out — otherwise a turn whose tokens are merely buffered (no
1308
+ * audible output yet) would be barge-in-abortable, leaving the caller
1309
+ * with silence. Transports without the signal emit audio immediately,
1310
+ * so the flip happens inline. Both paths stay gated by isCurrentRun
1311
+ * via {@link beginSpeaking}.
1312
+ */
1313
+ private beginSpeakingOnAudioStart(runVersion: number): void {
1314
+ if (this.transport.setAudioStartCallback) {
1315
+ this.transport.setAudioStartCallback(() =>
1316
+ this.beginSpeaking(runVersion),
1317
+ );
1318
+ } else {
1319
+ this.beginSpeaking(runVersion);
1320
+ }
1321
+ }
1322
+
1224
1323
  private isCurrentRun(runVersion: number): boolean {
1225
1324
  return runVersion === this.llmRunVersion;
1226
1325
  }
@@ -1456,22 +1555,14 @@ export class CallController {
1456
1555
  const durationMs = currentSession.startedAt
1457
1556
  ? Date.now() - currentSession.startedAt
1458
1557
  : 0;
1459
- addPointerMessage(
1558
+ postPointerMessageSafe(
1460
1559
  currentSession.initiatedFromConversationId,
1461
1560
  "completed",
1462
1561
  currentSession.toNumber,
1463
1562
  {
1464
1563
  duration: durationMs > 0 ? formatDuration(durationMs) : undefined,
1465
1564
  },
1466
- ).catch((err) => {
1467
- log.warn(
1468
- {
1469
- conversationId: currentSession.initiatedFromConversationId,
1470
- err,
1471
- },
1472
- "Skipping pointer write — origin conversation may no longer exist",
1473
- );
1474
- });
1565
+ );
1475
1566
  }
1476
1567
  }, 3000);
1477
1568
  }, maxDurationMs);
@@ -1481,15 +1572,10 @@ export class CallController {
1481
1572
  if (this.silenceTimer) clearTimeout(this.silenceTimer);
1482
1573
  if (this.destroyed) return;
1483
1574
  this.silenceTimer = setTimeout(() => {
1484
- // During guardian wait states, the relay heartbeat timer handles
1485
- // periodic updatessuppress the generic "Are you still there?"
1486
- // which is confusing when the caller is waiting on a decision.
1487
- // Two paths: in-call consultation (pendingGuardianInput) and
1488
- // inbound access-request wait (relay state).
1489
- if (
1490
- this.pendingGuardianInput ||
1491
- this.transport.getConnectionState() === "awaiting_guardian_decision"
1492
- ) {
1575
+ // During an in-call guardian consultation, suppress the generic
1576
+ // "Are you still there?" it is confusing when the caller is
1577
+ // waiting on a decision.
1578
+ if (this.pendingGuardianInput) {
1493
1579
  log.debug(
1494
1580
  { callSessionId: this.callSessionId },
1495
1581
  "Silence timeout suppressed during guardian wait",
@@ -25,7 +25,7 @@ import { getSecureKeyAsync } from "../security/secure-keys.js";
25
25
  import { getLogger } from "../util/logger.js";
26
26
  import { upsertActiveCallLease } from "./active-call-lease.js";
27
27
  import { isDeniedNumber } from "./call-constants.js";
28
- import { addPointerMessage } from "./call-pointer-messages.js";
28
+ import { postPointerMessageSafe } from "./call-pointer-messages.js";
29
29
  import { getCallController, unregisterCallController } from "./call-state.js";
30
30
  import { isTerminalState } from "./call-state-machine.js";
31
31
  import {
@@ -39,9 +39,8 @@ import {
39
39
  updateCallSession,
40
40
  } from "./call-store.js";
41
41
  import { activeMediaStreamSessions } from "./media-stream-server.js";
42
- import { activeRelayConnections } from "./relay-server.js";
43
42
  import { getTwilioConfig } from "./twilio-config.js";
44
- import { TwilioConversationRelayProvider } from "./twilio-provider.js";
43
+ import { TwilioVoiceProvider } from "./twilio-provider.js";
45
44
  import type { CallSession } from "./types.js";
46
45
  import { preflightVoiceIngress } from "./voice-ingress-preflight.js";
47
46
 
@@ -49,21 +48,6 @@ const log = getLogger("call-domain");
49
48
 
50
49
  const E164_REGEX = /^\+\d+$/;
51
50
 
52
- function postFailedCallPointer(
53
- conversationId: string,
54
- phoneNumber: string,
55
- reason: string,
56
- ): void {
57
- addPointerMessage(conversationId, "failed", phoneNumber, {
58
- reason,
59
- }).catch((pointerErr) => {
60
- log.warn(
61
- { conversationId, err: pointerErr },
62
- "Failed to post call-failed pointer message",
63
- );
64
- });
65
- }
66
-
67
51
  // ── Result types ─────────────────────────────────────────────────────
68
52
 
69
53
  interface StartCallResult {
@@ -228,7 +212,7 @@ export async function resolveCallerIdentity(
228
212
  }
229
213
 
230
214
  // Verify the user number is eligible as a caller ID with Twilio
231
- const provider = new TwilioConversationRelayProvider();
215
+ const provider = new TwilioVoiceProvider();
232
216
  const eligibility = await provider.checkCallerIdEligibility(userNumber);
233
217
  if (!eligibility.eligible) {
234
218
  log.warn(
@@ -428,12 +412,14 @@ export async function startCall(
428
412
 
429
413
  const preflightResult = await preflightVoiceIngress();
430
414
  if (!preflightResult.ok) {
431
- postFailedCallPointer(conversationId, phoneNumber, preflightResult.error);
415
+ postPointerMessageSafe(conversationId, "failed", phoneNumber, {
416
+ reason: preflightResult.error,
417
+ });
432
418
  return preflightResult;
433
419
  }
434
420
 
435
421
  const ingressConfig = preflightResult.ingressConfig;
436
- const provider = new TwilioConversationRelayProvider();
422
+ const provider = new TwilioVoiceProvider();
437
423
 
438
424
  const session = createCallSession({
439
425
  conversationId,
@@ -531,12 +517,7 @@ export async function startCall(
531
517
  );
532
518
 
533
519
  // Post a concise pointer message in the initiating conversation
534
- addPointerMessage(conversationId, "started", phoneNumber).catch((err) => {
535
- log.warn(
536
- { conversationId, err },
537
- "Failed to post call-started pointer message",
538
- );
539
- });
520
+ postPointerMessageSafe(conversationId, "started", phoneNumber);
540
521
 
541
522
  return {
542
523
  ok: true,
@@ -569,7 +550,9 @@ export async function startCall(
569
550
  });
570
551
  }
571
552
 
572
- postFailedCallPointer(conversationId, phoneNumber, msg);
553
+ postPointerMessageSafe(conversationId, "failed", phoneNumber, {
554
+ reason: msg,
555
+ });
573
556
 
574
557
  return { ok: false, error: `Error initiating call: ${msg}`, status: 500 };
575
558
  }
@@ -662,7 +645,7 @@ export async function cancelCall(
662
645
  // Terminate the call via the provider API
663
646
  if (session.providerCallSid) {
664
647
  try {
665
- const provider = new TwilioConversationRelayProvider();
648
+ const provider = new TwilioVoiceProvider();
666
649
  await provider.endCall(session.providerCallSid);
667
650
  } catch (endErr) {
668
651
  log.warn(
@@ -672,14 +655,6 @@ export async function cancelCall(
672
655
  }
673
656
  }
674
657
 
675
- // End the relay connection if active
676
- const relayConnection = activeRelayConnections.get(callSessionId);
677
- if (relayConnection) {
678
- relayConnection.endSession(reason);
679
- relayConnection.destroy();
680
- activeRelayConnections.delete(callSessionId);
681
- }
682
-
683
658
  // End the media-stream session if active
684
659
  const mediaStreamSession = activeMediaStreamSessions.get(callSessionId);
685
660
  if (mediaStreamSession) {
@@ -914,7 +889,8 @@ type StartVerificationCallResult =
914
889
  *
915
890
  * Creates a minimal call session with a voice channel binding and
916
891
  * passes `verificationSessionId` as a custom parameter so the
917
- * relay server can detect this is a guardian verification call.
892
+ * media-stream server's `routeSetup` can detect this is a guardian
893
+ * verification call.
918
894
  */
919
895
  export async function startVerificationCall(
920
896
  input: StartVerificationCallInput,
@@ -933,7 +909,7 @@ export async function startVerificationCall(
933
909
 
934
910
  try {
935
911
  const config = loadConfig();
936
- const provider = new TwilioConversationRelayProvider();
912
+ const provider = new TwilioVoiceProvider();
937
913
 
938
914
  // Resolve the assistant's Twilio number as the caller ID
939
915
  const identityResult = await resolveCallerIdentity(config);
@@ -1045,8 +1021,8 @@ type StartInviteCallResult = { ok: true; callSid: string } | CallError;
1045
1021
  * Initiate an outbound call to deliver a voice invite to a contact.
1046
1022
  *
1047
1023
  * Creates a minimal call session with a voice channel binding and
1048
- * passes invite-specific custom parameters so the relay server can
1049
- * detect this is an invite redemption call.
1024
+ * passes invite-specific custom parameters so the media-stream
1025
+ * server's `routeSetup` can detect this is an invite redemption call.
1050
1026
  */
1051
1027
  export async function startInviteCall(
1052
1028
  input: StartInviteCallInput,
@@ -1065,7 +1041,7 @@ export async function startInviteCall(
1065
1041
 
1066
1042
  try {
1067
1043
  const config = loadConfig();
1068
- const provider = new TwilioConversationRelayProvider();
1044
+ const provider = new TwilioVoiceProvider();
1069
1045
 
1070
1046
  // Resolve the assistant's Twilio number as the caller ID
1071
1047
  const identityResult = await resolveCallerIdentity(config);
@@ -3,17 +3,21 @@
3
3
  * so the user sees call lifecycle events without the full transcript
4
4
  * (which lives in the dedicated voice conversation).
5
5
  *
6
- * Trust-aware: trusted audiences get pointer messages routed through the
7
- * daemon conversation as a conversation turn (the assistant generates the text).
8
- * Untrusted/unknown audiences always receive deterministic fallback text
9
- * written directly to the conversation store.
6
+ * Trust-aware: the owner's own conversations get pointer messages routed through
7
+ * the daemon conversation as an owner self-maintenance turn (the assistant
8
+ * generates the text). Contact and unknown audiences always receive
9
+ * deterministic fallback text written directly to the conversation store, so a
10
+ * guardian-elevated turn never rehydrates private history into a non-owner
11
+ * conversation.
10
12
  */
11
13
 
14
+ import { runPointerMessageTurn } from "../daemon/pointer-turn-runner.js";
12
15
  import {
13
16
  addMessage,
14
17
  getConversationOriginChannel,
15
18
  getConversationRecentProvenanceTrustClass,
16
19
  } from "../persistence/conversation-crud.js";
20
+ import { isContactTrustClass } from "../runtime/trust-class.js";
17
21
  import { getLogger } from "../util/logger.js";
18
22
  import {
19
23
  buildPointerInstruction,
@@ -32,77 +36,39 @@ type PointerEvent =
32
36
 
33
37
  type PointerAudienceMode = "auto" | "trusted" | "untrusted";
34
38
 
35
- /**
36
- * Daemon-injected function that sends a message through the daemon conversation
37
- * pipeline (persistAndProcessMessage), letting the assistant generate the
38
- * pointer text as a natural conversation turn.
39
- *
40
- * @param requiredFacts - facts that must appear verbatim in the generated
41
- * text (phone number, duration, outcome keyword, etc.). The processor
42
- * should validate the output and throw if any are missing so the
43
- * deterministic fallback fires.
44
- */
45
- type PointerMessageProcessor = (
46
- conversationId: string,
47
- instruction: string,
48
- requiredFacts?: string[],
49
- ) => Promise<void>;
50
-
51
- // ---------------------------------------------------------------------------
52
- // Module-level processor injection (set by daemon lifecycle at startup)
53
- // ---------------------------------------------------------------------------
54
-
55
- let pointerMessageProcessor: PointerMessageProcessor | undefined;
56
-
57
- /**
58
- * Inject the daemon-provided pointer message processor.
59
- * Called from daemon/lifecycle.ts at startup, following the same pattern
60
- * as setRelayBroadcast.
61
- */
62
- export function setPointerMessageProcessor(
63
- processor: PointerMessageProcessor,
64
- ): void {
65
- pointerMessageProcessor = processor;
66
- }
67
-
68
- /** @internal Reset for tests. */
69
- export function resetPointerMessageProcessor(): void {
70
- pointerMessageProcessor = undefined;
71
- }
72
-
73
39
  // ---------------------------------------------------------------------------
74
40
  // Trust resolution
75
41
  // ---------------------------------------------------------------------------
76
42
 
77
43
  /**
78
- * Resolve whether the audience for a pointer message is trusted.
44
+ * Resolve whether the pointer audience is the assistant's owner (guardian)
45
+ * rather than a contact or unknown caller.
79
46
  *
80
- * Trusted when:
81
- * - recent message provenance trust class is 'guardian', 'trusted_contact',
82
- * or 'unverified_contact'
83
- * - conversation origin channel is 'vellum' (desktop app)
47
+ * Owner when:
48
+ * - recent message provenance trust class is 'guardian', or
49
+ * - conversation origin channel is 'vellum' (desktop app).
84
50
  *
85
- * Untrusted by default when insufficient evidence.
51
+ * Known non-guardian contacts ('trusted_contact' / 'unverified_contact') and
52
+ * unknown callers are NOT the owner: routing their pointer status through the
53
+ * daemon turn would run it under the internal guardian context, rehydrating
54
+ * guardian-only history that then leaks into the contact's own conversation.
55
+ * They take the deterministic fallback instead. Defaults to non-owner when
56
+ * evidence is insufficient.
86
57
  */
87
- function resolvePointerAudienceTrust(conversationId: string): boolean {
58
+ function resolvePointerAudienceIsOwner(conversationId: string): boolean {
88
59
  try {
89
- // Check provenance trust class on recent messages first this catches
90
- // identity-known non-guardian contacts (trusted and unverified) who
91
- // initiate calls from gateway channels (e.g. WhatsApp) where the
92
- // conversation itself isn't desktop-origin.
60
+ // Provenance is read from persisted message metadata, so it survives
61
+ // conversation eviction — a known contact is diverted to the deterministic
62
+ // fallback even on a cold load where the in-memory trust context is absent.
93
63
  const provenance =
94
64
  getConversationRecentProvenanceTrustClass(conversationId);
95
- if (
96
- provenance === "guardian" ||
97
- provenance === "trusted_contact" ||
98
- provenance === "unverified_contact"
99
- )
100
- return true;
65
+ if (isContactTrustClass(provenance)) return false;
66
+ if (provenance === "guardian") return true;
101
67
 
102
68
  const originChannel = getConversationOriginChannel(conversationId);
103
69
  if (originChannel === "vellum") return true;
104
70
  } catch {
105
- // Conversation may not exist or DB may be unavailable — default untrusted.
71
+ // Conversation may not exist or DB may be unavailable — default to non-owner.
106
72
  }
107
73
 
108
74
  return false;
@@ -153,17 +119,17 @@ export async function addPointerMessage(
153
119
  const outcomeKeyword = eventOutcomeKeywords[event];
154
120
  if (outcomeKeyword) requiredFacts.push(outcomeKeyword);
155
121
 
156
- const trustedAudience =
122
+ const ownerAudience =
157
123
  audienceMode === "trusted" ||
158
- (audienceMode === "auto" && resolvePointerAudienceTrust(conversationId));
124
+ (audienceMode === "auto" && resolvePointerAudienceIsOwner(conversationId));
159
125
 
160
- if (trustedAudience && pointerMessageProcessor) {
126
+ if (ownerAudience) {
161
127
  // Route through the daemon conversation — the assistant generates the
162
- // pointer text as a natural conversation turn, shaped by context,
128
+ // pointer text as a natural owner self-maintenance turn, shaped by context,
163
129
  // identity, and preferences.
164
130
  const instruction = buildPointerInstruction(context);
165
131
  try {
166
- await pointerMessageProcessor(conversationId, instruction, requiredFacts);
132
+ await runPointerMessageTurn(conversationId, instruction, requiredFacts);
167
133
  return;
168
134
  } catch (err) {
169
135
  log.warn(
@@ -171,10 +137,10 @@ export async function addPointerMessage(
171
137
  "Daemon pointer processing failed, falling back to deterministic",
172
138
  );
173
139
  }
174
- } else if (!trustedAudience && pointerMessageProcessor) {
140
+ } else {
175
141
  log.debug(
176
142
  { event, conversationId },
177
- "Untrusted audience — using deterministic pointer copy",
143
+ "Non-owner audience — using deterministic pointer copy",
178
144
  );
179
145
  }
180
146
 
@@ -194,6 +160,24 @@ export async function addPointerMessage(
194
160
  );
195
161
  }
196
162
 
163
+ /**
164
+ * Fire-and-forget pointer write that tolerates an evicted origin
165
+ * conversation: failures are logged at warn level and swallowed.
166
+ */
167
+ export function postPointerMessageSafe(
168
+ conversationId: string,
169
+ event: PointerEvent,
170
+ phoneNumber: string,
171
+ extra?: Parameters<typeof addPointerMessage>[3],
172
+ ): void {
173
+ addPointerMessage(conversationId, event, phoneNumber, extra).catch((err) => {
174
+ log.warn(
175
+ { conversationId, err },
176
+ "Skipping pointer write — origin conversation may no longer exist",
177
+ );
178
+ });
179
+ }
180
+
197
181
  /**
198
182
  * Format a duration in milliseconds into a human-friendly string.
199
183
  */