@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
@@ -3,10 +3,11 @@
3
3
  */
4
4
  import type { ChannelId } from "../../channels/types.js";
5
5
  import { DAEMON_INTERNAL_ASSISTANT_ID } from "../assistant-scope.js";
6
- import type {
7
- ApprovalAction,
8
- ApprovalDecisionResult,
9
- ApprovalUIMetadata,
6
+ import {
7
+ type ApprovalAction,
8
+ type ApprovalDecisionResult,
9
+ type ApprovalUIMetadata,
10
+ isApprovalAction,
10
11
  } from "../channel-approval-types.js";
11
12
 
12
13
  /** Canonicalize assistantId for channel ingress paths. */
@@ -35,11 +36,6 @@ export function requiredDecisionKeywords(
35
36
  // Callback data parser — format: "apr:<requestId>:<action>"
36
37
  // ---------------------------------------------------------------------------
37
38
 
38
- const VALID_ACTIONS: ReadonlySet<string> = new Set<string>([
39
- "approve_once",
40
- "reject",
41
- ]);
42
-
43
39
  /** Map legacy callback actions to canonical ones for in-flight buttons. */
44
40
  const LEGACY_CALLBACK_MAP: Record<string, string> = {
45
41
  approve_10m: "approve_once",
@@ -52,11 +48,15 @@ export function parseCallbackData(
52
48
  sourceChannel?: string,
53
49
  ): ApprovalDecisionResult | null {
54
50
  const parts = data.split(":");
55
- if (parts.length < 3 || parts[0] !== "apr") return null;
51
+ if (parts.length < 3 || parts[0] !== "apr") {
52
+ return null;
53
+ }
56
54
  const requestId = parts[1];
57
55
  const rawAction = parts.slice(2).join(":");
58
56
  const action = LEGACY_CALLBACK_MAP[rawAction] ?? rawAction;
59
- if (!requestId || !VALID_ACTIONS.has(action)) return null;
57
+ if (!requestId || !isApprovalAction(action)) {
58
+ return null;
59
+ }
60
60
  const source =
61
61
  sourceChannel === "whatsapp"
62
62
  ? ("whatsapp_button" as const)
@@ -65,7 +65,7 @@ export function parseCallbackData(
65
65
  : sourceChannel === "vellum"
66
66
  ? ("vellum_surface" as const)
67
67
  : ("telegram_button" as const);
68
- return { action: action as ApprovalAction, source, requestId };
68
+ return { action, source, requestId };
69
69
  }
70
70
 
71
71
  // ---------------------------------------------------------------------------
@@ -93,10 +93,14 @@ const REACTION_EMOJI_MAP: ReadonlyMap<string, ApprovalAction> = new Map([
93
93
  export function parseReactionCallbackData(
94
94
  data: string,
95
95
  ): ApprovalDecisionResult | null {
96
- if (!data.startsWith("reaction:")) return null;
96
+ if (!data.startsWith("reaction:")) {
97
+ return null;
98
+ }
97
99
  const emoji = data.slice("reaction:".length);
98
100
  const action = REACTION_EMOJI_MAP.get(emoji);
99
- if (!action) return null;
101
+ if (!action) {
102
+ return null;
103
+ }
100
104
  return { action, source: "slack_reaction" };
101
105
  }
102
106
 
@@ -14,9 +14,6 @@
14
14
  * inbound code-match path (`gateway/src/verification/text-verification.ts`).
15
15
  * - The revoke/downgrade OUTCOME is relayed from the daemon via
16
16
  * `ipcCallPersistent("mark_channel_revoked", …)` to `ContactStore.markChannelRevoked`.
17
- * - The `mark_channel_verified` IPC method exists as the daemon/CLI relay surface (symmetric with
18
- * `mark_channel_revoked`) but has no caller: the trusted-contact CLI path sends codes only, and the
19
- * outcome arrives via the inbound code-match path.
20
17
  */
21
18
 
22
19
  import { z } from "zod";
@@ -8,6 +8,11 @@
8
8
  * they don't shadow more-specific sub-paths like contacts/invites.
9
9
  */
10
10
 
11
+ import {
12
+ RedeemInviteByTokenRequestSchema,
13
+ RedeemVoiceInviteRequestSchema,
14
+ } from "@vellumai/gateway-client";
15
+ import type { ContactRead } from "@vellumai/gateway-client/gateway-ipc-contracts";
11
16
  import {
12
17
  GetContactIpcResponseSchema,
13
18
  ListContactsIpcResponseSchema,
@@ -22,14 +27,19 @@ import {
22
27
  searchContacts,
23
28
  } from "../../contacts/contact-store.js";
24
29
  import { getGuardianContactIds } from "../../contacts/guardian-contact-reader.js";
25
- import type { ContactRole, ContactType } from "../../contacts/types.js";
30
+ import type {
31
+ ContactChannel,
32
+ ContactRole,
33
+ ContactType,
34
+ ContactWithChannels,
35
+ } from "../../contacts/types.js";
26
36
  import { ipcCallPersistent } from "../../ipc/gateway-client.js";
27
37
  import { resolveGuardianName } from "../../prompts/user-reference.js";
28
38
  import { getLogger } from "../../util/logger.js";
29
- import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
39
+ import { ACTOR_PRINCIPALS, GATEWAY_PRINCIPALS } from "../auth/route-policy.js";
30
40
  import {
31
- redeemIngressInvite,
32
- redeemVoiceInviteCode,
41
+ composeInvitePresentation,
42
+ resolveInviteGuardianName,
33
43
  triggerInviteCall,
34
44
  } from "../invite-service.js";
35
45
  import { BadRequestError, NotFoundError, RouteError } from "./errors.js";
@@ -147,9 +157,14 @@ function isContactType(value: string): value is ContactType {
147
157
  // (search / contactType) omit them, so they are `.optional()`. Contact-level
148
158
  // `role` is gateway-sourced: relayed reads trust the role on the `ContactRead`,
149
159
  // while daemon-native reads derive it from the gateway guardian id set at the
150
- // serve layer (see prepareContactResponse). INFO telemetry
151
- // (lastSeenAt/interactionCount/lastInteraction) is locally hydrated on every
152
- // read path and stays required.
160
+ // serve layer (see prepareContactResponse). Interaction telemetry
161
+ // (lastSeenAt/interactionCount/lastInteraction) is gateway-owned: relayed reads
162
+ // carry it directly, and daemon-native reads batch-hydrate it from the gateway
163
+ // (see hydrateTelemetryFromGateway). On a gateway fail-soft the count
164
+ // `interactionCount` defaults to 0 (never served as null, so callers render a
165
+ // real number); the `lastSeenAt`/`lastInteraction` timestamps degrade to null.
166
+ // The timestamp fields stay `.nullable()`; `interactionCount` is kept nullable
167
+ // defensively for the relay path, but is never emitted null.
153
168
  const contactChannelSchema = z.object({
154
169
  id: z.string(),
155
170
  contactId: z.string(),
@@ -163,7 +178,7 @@ const contactChannelSchema = z.object({
163
178
  verifiedAt: z.number().nullable().optional(),
164
179
  verifiedVia: z.string().nullable().optional(),
165
180
  lastSeenAt: z.number().nullable(),
166
- interactionCount: z.number(),
181
+ interactionCount: z.number().nullable(),
167
182
  lastInteraction: z.number().nullable(),
168
183
  revokedReason: z.string().nullable().optional(),
169
184
  blockedReason: z.string().nullable().optional(),
@@ -176,7 +191,7 @@ const contactSchema = z.object({
176
191
  notes: z.string().nullable().optional(),
177
192
  contactType: z.enum(["human", "assistant"]),
178
193
  lastInteraction: z.number().nullable().optional(),
179
- interactionCount: z.number(),
194
+ interactionCount: z.number().nullable(),
180
195
  createdAt: z.number(),
181
196
  updatedAt: z.number(),
182
197
  channels: z.array(contactChannelSchema),
@@ -211,6 +226,97 @@ async function relayListContacts(limit: number, role: ContactRole | undefined) {
211
226
  }
212
227
  }
213
228
 
229
+ /**
230
+ * A daemon-native contact read whose gateway-owned interaction telemetry has
231
+ * been overlaid. `interactionCount` is a count, so it defaults to 0 on the
232
+ * gateway fail-soft/id-miss path (matching the gateway's NOT NULL DEFAULT 0
233
+ * column) — consumers never see `null` and render a real number. The
234
+ * `lastSeenAt`/`lastInteraction` timestamps stay nullable: a "never" timestamp
235
+ * is legitimately absent.
236
+ */
237
+ type ContactWithGatewayTelemetry = Omit<ContactWithChannels, "channels"> & {
238
+ interactionCount: number;
239
+ lastInteraction: number | null;
240
+ channels: Array<
241
+ ContactChannel & {
242
+ lastSeenAt: number | null;
243
+ interactionCount: number;
244
+ lastInteraction: number | null;
245
+ }
246
+ >;
247
+ };
248
+
249
+ /**
250
+ * Key channels by (type, lower(address)) for the id-miss telemetry fallback.
251
+ * Matches the gateway's UNIQUE(type, address) NOCASE collation; the NUL
252
+ * delimiter cannot appear in either field, so keys never collide.
253
+ */
254
+ function channelKey(type: string, address: string): string {
255
+ return `${type}\u0000${address.toLowerCase()}`;
256
+ }
257
+
258
+ /**
259
+ * Overlay gateway-owned interaction telemetry onto daemon-native contact reads
260
+ * (search / contactType-filtered), which bypass the gateway list relay. The
261
+ * daemon still owns the FILTERING (gateway-native search/contactType is
262
+ * design-blocked), but telemetry (contact + channel
263
+ * interactionCount/lastInteraction, channel lastSeenAt) is gateway-owned — so
264
+ * batch-fetch it via `contacts_list_rich` keyed by the filtered id set and
265
+ * overlay it, keeping the local assistant-DB aggregation out of the served
266
+ * payload. Fail-soft: if the gateway read fails or omits a contact, its
267
+ * interaction counts degrade to 0 and its timestamps to null rather than
268
+ * falling back to the local assistant-DB aggregation.
269
+ */
270
+ async function hydrateTelemetryFromGateway(
271
+ contacts: ContactWithChannels[],
272
+ ): Promise<ContactWithGatewayTelemetry[]> {
273
+ if (contacts.length === 0) return [];
274
+
275
+ const gatewayById = new Map<string, ContactRead>();
276
+ try {
277
+ const result = await ipcCallPersistent("contacts_list_rich", {
278
+ ids: contacts.map((c) => c.id),
279
+ });
280
+ const { contacts: rich } = ListContactsIpcResponseSchema.parse(result);
281
+ for (const c of rich) gatewayById.set(c.id, c);
282
+ } catch (err) {
283
+ log.warn(
284
+ { err },
285
+ "hydrateTelemetryFromGateway: gateway telemetry read failed; serving 0 counts / null timestamps",
286
+ );
287
+ }
288
+
289
+ return contacts.map((c) => {
290
+ const gw = gatewayById.get(c.id);
291
+ const gwChannelById = new Map(
292
+ (gw?.channels ?? []).map((ch) => [ch.id, ch]),
293
+ );
294
+ // Local channel UUIDs can diverge from the gateway's for the same
295
+ // (type, address) (legacy pre-alignment channels), so an id-miss falls back
296
+ // to a (type, lower(address)) match — mirroring the gateway's
297
+ // overlayAclOntoContacts. UNIQUE(type, address) collates NOCASE gateway-side.
298
+ const gwChannelByTypeAddress = new Map(
299
+ (gw?.channels ?? []).map((ch) => [channelKey(ch.type, ch.address), ch]),
300
+ );
301
+ return {
302
+ ...c,
303
+ interactionCount: gw?.interactionCount ?? 0,
304
+ lastInteraction: gw?.lastInteraction ?? null,
305
+ channels: c.channels.map((ch) => {
306
+ const gwCh =
307
+ gwChannelById.get(ch.id) ??
308
+ gwChannelByTypeAddress.get(channelKey(ch.type, ch.address));
309
+ return {
310
+ ...ch,
311
+ lastSeenAt: gwCh?.lastSeenAt ?? null,
312
+ interactionCount: gwCh?.interactionCount ?? 0,
313
+ lastInteraction: gwCh?.lastInteraction ?? null,
314
+ };
315
+ }),
316
+ };
317
+ });
318
+ }
319
+
214
320
  export async function handleListContacts(queryParams: Record<string, string>) {
215
321
  const limit = Number(queryParams.limit ?? 50);
216
322
  const role = (queryParams.role as ContactRole) || undefined;
@@ -234,15 +340,20 @@ export async function handleListContacts(queryParams: Record<string, string>) {
234
340
  log.debug(
235
341
  "handleListContacts: search served daemon-native (gateway-native search is design-blocked)",
236
342
  );
237
- const contacts = searchContacts({
238
- query,
239
- channelAddress,
240
- channelType,
241
- contactType,
242
- limit,
243
- });
244
- // Daemon-native read: role is the neutral default, so derive it.
245
- const guardianIds = await getGuardianContactIds();
343
+ // Telemetry hydration and the guardian-id read (for role derivation) both
344
+ // hit the gateway and are independent — run them concurrently.
345
+ const [contacts, guardianIds] = await Promise.all([
346
+ hydrateTelemetryFromGateway(
347
+ searchContacts({
348
+ query,
349
+ channelAddress,
350
+ channelType,
351
+ contactType,
352
+ limit,
353
+ }),
354
+ ),
355
+ getGuardianContactIds(),
356
+ ]);
246
357
  return {
247
358
  ok: true,
248
359
  contacts: contacts.map((c) => prepareContactResponse(c, guardianIds)),
@@ -257,9 +368,12 @@ export async function handleListContacts(queryParams: Record<string, string>) {
257
368
  log.debug(
258
369
  "handleListContacts: contactType-filtered read served daemon-native (gateway-native contactType filtering is design-blocked, pending ACL classification)",
259
370
  );
260
- const contacts = listContacts(limit, contactType);
261
- // Daemon-native read: role is the neutral default, so derive it.
262
- const guardianIds = await getGuardianContactIds();
371
+ // Telemetry hydration and the guardian-id read (for role derivation) both
372
+ // hit the gateway and are independent run them concurrently.
373
+ const [contacts, guardianIds] = await Promise.all([
374
+ hydrateTelemetryFromGateway(listContacts(limit, contactType)),
375
+ getGuardianContactIds(),
376
+ ]);
263
377
  return {
264
378
  ok: true,
265
379
  contacts: contacts.map((c) => prepareContactResponse(c, guardianIds)),
@@ -296,17 +410,10 @@ export async function handleGetContact(contactId: string) {
296
410
  // Invite handlers (transport-agnostic)
297
411
  // ---------------------------------------------------------------------------
298
412
 
299
- // The gateway owns the canonical invite write path. These CLI handlers relay
300
- // to the gateway IPC methods via `ipcCallPersistent`; the assistant DB is
301
- // written only by the gateway. (Redemption stays daemon-local see the redeem route.)
302
-
303
- // The invite-CREATE relay needs a generous timeout: the gateway's `invites_mint`
304
- // handler calls `createIngressInvite` → `generateInviteInstruction`, which can spend
305
- // up to GENERATION_TIMEOUT_MS (~5s) waiting on an LLM before falling back. The default
306
- // 5s persistent-IPC timeout can fire mid-mint, so the caller sees a spurious failure
307
- // even though the gateway is still writing the invite. 30s safely exceeds the inner
308
- // ~5s fallback plus IPC overhead on both nested hops. (List/revoke keep the default.)
309
- const INVITE_CREATE_RELAY_TIMEOUT_MS = 30_000;
413
+ // The gateway owns the canonical invite lifecycle: mint, list, revoke, and
414
+ // redemption. These CLI handlers relay via `ipcCallPersistent`; the daemon
415
+ // then layers the presentation fields (share link, LLM guardian instruction,
416
+ // channel handle) onto the gateway's one-time create payload.
310
417
 
311
418
  export async function handleListInvites({
312
419
  queryParams = {},
@@ -325,29 +432,41 @@ export async function handleListInvites({
325
432
  }
326
433
 
327
434
  export async function handleCreateInvite({ body = {} }: RouteHandlerArgs) {
435
+ const contactId = body.contactId as string;
436
+ const sourceChannel = body.sourceChannel as string | undefined;
437
+ // The guardian display label on voice invites is daemon-resolved and passed
438
+ // through to the gateway, which stores it and never interprets it.
439
+ const guardianName =
440
+ sourceChannel === "phone" ? resolveInviteGuardianName() : undefined;
441
+ let result: { invite: Record<string, unknown>; rawToken?: string };
328
442
  try {
329
- const result = (await ipcCallPersistent(
330
- "invites_create",
331
- {
332
- contactId: body.contactId as string,
333
- sourceChannel: body.sourceChannel as string | undefined,
334
- note: body.note as string | undefined,
335
- maxUses: body.maxUses as number | undefined,
336
- expiresInMs: body.expiresInMs as number | undefined,
337
- expectedExternalUserId: body.expectedExternalUserId as
338
- | string
339
- | undefined,
340
- },
341
- INVITE_CREATE_RELAY_TIMEOUT_MS,
342
- )) as { invite: Record<string, unknown>; rawToken?: string };
343
- return {
344
- ok: true,
345
- invite: result.invite,
346
- ...(result.rawToken ? { rawToken: result.rawToken } : {}),
347
- };
443
+ result = (await ipcCallPersistent("invites_create", {
444
+ contactId,
445
+ sourceChannel,
446
+ note: body.note as string | undefined,
447
+ maxUses: body.maxUses as number | undefined,
448
+ expiresInMs: body.expiresInMs as number | undefined,
449
+ expectedExternalUserId: body.expectedExternalUserId as
450
+ | string
451
+ | undefined,
452
+ ...(guardianName ? { guardianName } : {}),
453
+ ...(typeof body.sourceConversationId === "string"
454
+ ? { sourceConversationId: body.sourceConversationId }
455
+ : {}),
456
+ })) as { invite: Record<string, unknown>; rawToken?: string };
348
457
  } catch (err) {
349
458
  rethrowGatewayError(err);
350
459
  }
460
+ const invite = await composeInvitePresentation({
461
+ contactId,
462
+ invite: result.invite,
463
+ rawToken: result.rawToken,
464
+ });
465
+ return {
466
+ ok: true,
467
+ invite,
468
+ ...(result.rawToken ? { rawToken: result.rawToken } : {}),
469
+ };
351
470
  }
352
471
 
353
472
  export async function handleRevokeInvite({
@@ -366,58 +485,69 @@ export async function handleRevokeInvite({
366
485
  /**
367
486
  * Redeem a voice invite code.
368
487
  *
369
- * Backs the HTTP `invites_redeem` route (voice path) and the IPC-only
370
- * `invites_redeem_voice` method. Wraps the identity-bound
371
- * `redeemVoiceInviteCode` path.
488
+ * Backs the HTTP `invites_redeem` route (voice path). Parses the body with
489
+ * the shared `RedeemVoiceInviteRequestSchema` wire contract (plus the
490
+ * daemon-specific `assistantId` passthrough) and relays to the gateway's
491
+ * `invites_redeem` IPC — the gateway redemption engine owns validation, the
492
+ * atomic claim, and the ACL write. Fail-closed: a gateway relay failure
493
+ * surfaces as an error; there is no local redemption fallback.
372
494
  */
373
- export async function handleRedeemVoiceInvite({ body = {} }: RouteHandlerArgs) {
374
- const callerExternalUserId = body.callerExternalUserId as string | undefined;
375
- const code = body.code as string | undefined;
376
-
377
- if (!callerExternalUserId || !code) {
495
+ async function handleRedeemVoiceInvite({ body = {} }: RouteHandlerArgs) {
496
+ const parsed = RedeemVoiceInviteRequestSchema.safeParse(body);
497
+ if (!parsed.success) {
378
498
  throw new BadRequestError("callerExternalUserId and code are required");
379
499
  }
380
500
 
381
- const result = await redeemVoiceInviteCode({
382
- assistantId: body.assistantId as string | undefined,
383
- callerExternalUserId,
384
- sourceChannel: "phone",
385
- code,
386
- });
387
-
388
- if (!result.ok) {
389
- throw new BadRequestError(result.reason);
501
+ try {
502
+ return (await ipcCallPersistent("invites_redeem", {
503
+ ...parsed.data,
504
+ ...(typeof body.assistantId === "string"
505
+ ? { assistantId: body.assistantId }
506
+ : {}),
507
+ })) as { ok: true; type: string; memberId: string; inviteId?: string };
508
+ } catch (err) {
509
+ rethrowGatewayError(err);
390
510
  }
511
+ }
391
512
 
392
- return {
393
- ok: true,
394
- type: result.type,
395
- memberId: result.memberId,
396
- ...(result.type === "redeemed" ? { inviteId: result.inviteId } : {}),
397
- };
513
+ /** Map a token-branch schema failure to the stable redeem error messages. */
514
+ function redeemTokenIssueMessage(error: z.ZodError): string {
515
+ for (const field of ["token", "sourceChannel"] as const) {
516
+ if (error.issues.some((issue) => issue.path[0] === field)) {
517
+ return `${field} is required`;
518
+ }
519
+ }
520
+ return error.issues[0]?.message ?? "Invalid redemption request";
398
521
  }
399
522
 
400
523
  /**
401
524
  * Redeem a token invite.
402
525
  *
403
- * Backs the HTTP `invites_redeem` route (token path) and the IPC-only
404
- * `invites_redeem_token` method. Wraps the generic `redeemIngressInvite`
405
- * token path.
526
+ * Backs the HTTP `invites_redeem` route (token path). Parses the body with
527
+ * the shared `RedeemInviteByTokenRequestSchema` wire contract and relays the
528
+ * parsed request verbatim — including the sender identity fields
529
+ * (`displayName` / `username`) the gateway engine stamps onto the new member —
530
+ * to the gateway's `invites_redeem` IPC. The gateway redemption engine owns
531
+ * validation, the atomic claim, and the ACL write. Fail-closed: a gateway
532
+ * relay failure surfaces as an error; there is no local redemption fallback.
406
533
  */
407
- export async function handleRedeemTokenInvite({ body = {} }: RouteHandlerArgs) {
408
- const result = await redeemIngressInvite({
409
- token: body.token as string | undefined,
410
- externalUserId: body.externalUserId as string | undefined,
411
- externalChatId: body.externalChatId as string | undefined,
412
- sourceChannel: body.sourceChannel as string | undefined,
413
- });
534
+ async function handleRedeemTokenInvite({ body = {} }: RouteHandlerArgs) {
535
+ const parsed = RedeemInviteByTokenRequestSchema.safeParse(body);
536
+ if (!parsed.success) {
537
+ throw new BadRequestError(redeemTokenIssueMessage(parsed.error));
538
+ }
414
539
 
415
- if (!result.ok) {
416
- throw new BadRequestError(result.error);
540
+ try {
541
+ // The `type` is surfaced so callers can tell a real redeem apart from an
542
+ // `already_member` no-op (which consumes no invite use).
543
+ return (await ipcCallPersistent("invites_redeem", parsed.data)) as {
544
+ ok: true;
545
+ invite: Record<string, unknown>;
546
+ type: string;
547
+ };
548
+ } catch (err) {
549
+ rethrowGatewayError(err);
417
550
  }
418
- // Surface the redemption `type` so the gateway can skip mirroring an
419
- // `already_member` redeem (which consumes no invite use).
420
- return { ok: true, invite: result.data.invite, type: result.data.type };
421
551
  }
422
552
 
423
553
  export async function handleRedeemInvite(args: RouteHandlerArgs) {
@@ -428,14 +558,17 @@ export async function handleRedeemInvite(args: RouteHandlerArgs) {
428
558
  return handleRedeemTokenInvite(args);
429
559
  }
430
560
 
431
- // Stays daemon-local by design (like invites_redeem): the gateway's call path
432
- // validates its row then delegates the actual provider call to THIS handler via
433
- // ipcCallAssistant("invites_trigger_call"). Relaying back would loop
434
- // gateway→assistant→gateway. The provider call is a daemon capability.
435
- export async function handleTriggerInviteCall({
436
- pathParams = {},
437
- }: RouteHandlerArgs) {
438
- const result = await triggerInviteCall(pathParams.id);
561
+ // Stays daemon-local by design (like invites_redeem): the gateway validates
562
+ // its canonical invite row, then delegates the actual provider call to THIS
563
+ // handler via ipcCallAssistant("invites_trigger_call") with the resolved call
564
+ // fields in the body. Relaying back would loop gateway→assistant→gateway.
565
+ // The provider call is a daemon capability.
566
+ export async function handleTriggerInviteCall({ body = {} }: RouteHandlerArgs) {
567
+ const result = await triggerInviteCall({
568
+ phoneNumber: body.phoneNumber as string | undefined,
569
+ friendName: body.friendName as string | null | undefined,
570
+ guardianName: body.guardianName as string | null | undefined,
571
+ });
439
572
  if (!result.ok) {
440
573
  throw new BadRequestError(result.error);
441
574
  }
@@ -561,6 +694,10 @@ export const ROUTES: RouteDefinition[] = [
561
694
  .string()
562
695
  .describe("Expected user ID (E.164 for phone)")
563
696
  .optional(),
697
+ sourceConversationId: z
698
+ .string()
699
+ .describe("Conversation the invite was created from (opaque)")
700
+ .optional(),
564
701
  }),
565
702
  responseBody: z.object({
566
703
  ok: z.boolean(),
@@ -577,9 +714,9 @@ export const ROUTES: RouteDefinition[] = [
577
714
  },
578
715
  },
579
716
  {
580
- // Stays daemon-local by design: redemption is an inbound-channel path (not
581
- // a CLI ACL surface), and the assistant redemption service already claims
582
- // the gateway row by id via record_invite_redemption — relaying would loop.
717
+ // Relays to the gateway `invites_redeem` IPC: the gateway redemption
718
+ // engine is the single lifecycle authority (validation, atomic claim,
719
+ // ACL upsert). Fail-closed when the gateway is unreachable.
583
720
  operationId: "invites_redeem",
584
721
  endpoint: "contacts/invites/redeem",
585
722
  method: "POST",
@@ -592,15 +729,33 @@ export const ROUTES: RouteDefinition[] = [
592
729
  description: "Redeem an invite by token or voice code.",
593
730
  tags: ["contacts"],
594
731
  requestBody: z.object({
595
- token: z.string().describe("Invite token (token-based redemption)"),
596
- code: z.string().describe("Voice code (voice-code redemption)"),
732
+ token: z
733
+ .string()
734
+ .optional()
735
+ .describe("Invite token (token-based redemption)"),
736
+ code: z.string().optional().describe("Voice code (voice-code redemption)"),
597
737
  callerExternalUserId: z
598
738
  .string()
739
+ .optional()
599
740
  .describe("Caller E.164 phone (voice-code)"),
600
- externalUserId: z.string().describe("External user ID (token-based)"),
601
- externalChatId: z.string().describe("External chat ID (token-based)"),
602
- sourceChannel: z.string().describe("Source channel (token-based)"),
603
- assistantId: z.string().describe("Assistant ID (voice-code)"),
741
+ externalUserId: z
742
+ .string()
743
+ .optional()
744
+ .describe("External user ID (token-based)"),
745
+ externalChatId: z
746
+ .string()
747
+ .optional()
748
+ .describe("External chat ID (token-based)"),
749
+ sourceChannel: z
750
+ .string()
751
+ .optional()
752
+ .describe("Source channel (token-based)"),
753
+ displayName: z
754
+ .string()
755
+ .optional()
756
+ .describe("Sender display name (token-based)"),
757
+ username: z.string().optional().describe("Sender username (token-based)"),
758
+ assistantId: z.string().optional().describe("Assistant ID (voice-code)"),
604
759
  }),
605
760
  responseBody: z.object({
606
761
  ok: z.boolean(),
@@ -639,14 +794,33 @@ export const ROUTES: RouteDefinition[] = [
639
794
  operationId: "invites_trigger_call",
640
795
  endpoint: "contacts/invites/:id/call",
641
796
  method: "POST",
797
+ // Gateway-only: the handler dials whatever number is in the body — the
798
+ // invite validation in the gateway's triggerInviteCallNative is the sole
799
+ // gate, so an actor-reachable policy would be an arbitrary-dial primitive.
642
800
  policy: {
643
- requiredScopes: ["settings.write"],
644
- allowedPrincipalTypes: ACTOR_PRINCIPALS,
801
+ requiredScopes: ["internal.write"],
802
+ allowedPrincipalTypes: GATEWAY_PRINCIPALS,
645
803
  },
646
804
  handler: handleTriggerInviteCall,
647
805
  summary: "Trigger invite call",
648
- description: "Trigger an outbound call for a phone invite.",
806
+ description:
807
+ "Trigger an outbound call for a phone invite. Gateway-only: the gateway validates its canonical invite row and supplies the resolved call fields in the body.",
649
808
  tags: ["contacts"],
809
+ requestBody: z.object({
810
+ phoneNumber: z
811
+ .string()
812
+ .describe("E.164 number the invite call dials (invite's bound caller)"),
813
+ friendName: z
814
+ .string()
815
+ .nullable()
816
+ .optional()
817
+ .describe("Invitee display name for the call greeting"),
818
+ guardianName: z
819
+ .string()
820
+ .nullable()
821
+ .optional()
822
+ .describe("Guardian display label recorded on the invite"),
823
+ }),
650
824
  responseBody: z.object({
651
825
  ok: z.boolean(),
652
826
  callSid: z.string().describe("Call SID from the provider"),
@@ -706,11 +880,13 @@ export const ROUTES: RouteDefinition[] = [
706
880
  log.debug(
707
881
  "search_contacts: search served daemon-native (gateway-native search is design-blocked)",
708
882
  );
709
- // Daemon-native read: role is the neutral default, so derive it.
710
- const guardianIds = await getGuardianContactIds();
711
- return searchContacts(parsed).map((c) =>
712
- prepareContactResponse(c, guardianIds),
713
- );
883
+ // Telemetry hydration and the guardian-id read (for role derivation) both
884
+ // hit the gateway and are independent — run them concurrently.
885
+ const [contacts, guardianIds] = await Promise.all([
886
+ hydrateTelemetryFromGateway(searchContacts(parsed)),
887
+ getGuardianContactIds(),
888
+ ]);
889
+ return contacts.map((c) => prepareContactResponse(c, guardianIds));
714
890
  },
715
891
  },
716
892
 
@@ -806,11 +982,16 @@ async function handleMergeContactsRoute(args: RouteHandlerArgs) {
806
982
 
807
983
  try {
808
984
  const contact = mergeContacts(keepId, mergeId);
809
- // Daemon-native read (assistant DB): role is the neutral default, derive it.
810
- const guardianIds = await getGuardianContactIds();
985
+ // Daemon-native read (assistant DB): telemetry is gateway-owned, so overlay
986
+ // it (fail-soft to null) and derive role from the guardian-id set. Both hit
987
+ // the gateway and are independent — run them concurrently.
988
+ const [[hydrated], guardianIds] = await Promise.all([
989
+ hydrateTelemetryFromGateway([contact]),
990
+ getGuardianContactIds(),
991
+ ]);
811
992
  return {
812
993
  ok: true,
813
- contact: prepareContactResponse(contact, guardianIds),
994
+ contact: prepareContactResponse(hydrated, guardianIds),
814
995
  };
815
996
  } catch (err) {
816
997
  const message = err instanceof Error ? err.message : String(err);
@@ -117,6 +117,12 @@ export const conversationSummarySchema = z.object({
117
117
  */
118
118
  surfacedAt: z.number().optional(),
119
119
  inferenceProfile: z.string().optional(),
120
+ /**
121
+ * Plugin-id list scoping this chat to a subset of installed plugins.
122
+ * Absent when there is no per-chat restriction (default: all enabled
123
+ * plugins); an explicit `[]` means the user cleared all optional plugins.
124
+ */
125
+ enabledPlugins: z.array(z.string()).nullable().optional(),
120
126
  /**
121
127
  * True when the agent loop is currently mid-turn for this conversation.
122
128
  * Mirrors the in-memory `Conversation.isProcessing()` flag on the daemon