@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
@@ -12,8 +12,6 @@ import { canonicalizeInboundIdentity } from "../util/canonicalize-identity.js";
12
12
  import { notifyContactsChanged } from "./notify-contacts-changed.js";
13
13
  import type {
14
14
  AssistantContactMetadata,
15
- ChannelPolicy,
16
- ChannelStatus,
17
15
  Contact,
18
16
  ContactChannel,
19
17
  ContactRole,
@@ -99,8 +97,6 @@ function parseContact(row: typeof contacts.$inferSelect): Contact {
99
97
  // gateway-owned; the serve layer stamps the real role from the gateway
100
98
  // guardian id set.
101
99
  role: "contact",
102
- lastInteraction: null,
103
- interactionCount: 0,
104
100
  createdAt: row.createdAt,
105
101
  updatedAt: row.updatedAt,
106
102
  contactType: row.contactType,
@@ -118,10 +114,6 @@ function parseChannel(
118
114
  address: row.address,
119
115
  isPrimary: row.isPrimary,
120
116
  externalChatId: row.externalChatId,
121
- inviteId: row.inviteId,
122
- lastSeenAt: row.lastSeenAt,
123
- interactionCount: row.interactionCount,
124
- lastInteraction: row.lastInteraction,
125
117
  updatedAt: row.updatedAt,
126
118
  createdAt: row.createdAt,
127
119
  };
@@ -140,32 +132,21 @@ function getChannelsForContact(contactId: string): ContactChannel[] {
140
132
 
141
133
  function withChannels(contact: Contact): ContactWithChannels {
142
134
  const channels = getChannelsForContact(contact.id);
143
- // INFO telemetry aggregated from channel rows (not ACL): sum interaction
144
- // counts, take the most recent interaction across channels.
145
- const interactionCount = channels.reduce(
146
- (sum, ch) => sum + ch.interactionCount,
147
- 0,
148
- );
149
- const lastInteraction =
150
- channels.reduce((max, ch) => Math.max(max, ch.lastInteraction ?? 0), 0) ||
151
- null;
152
- return { ...contact, interactionCount, lastInteraction, channels };
135
+ return { ...contact, channels };
153
136
  }
154
137
 
155
138
  // ── Channel data type for syncChannels ───────────────────────────────
156
139
 
157
140
  interface SyncChannelData {
141
+ /** Explicit gateway-minted channel id. Lets the identity mirror key the
142
+ * channel identically in both stores; omit to mint one. When a row with this
143
+ * id already exists it is updated in place (address/owner rebind), so a
144
+ * gateway re-auth is mirrored rather than colliding on the id. */
145
+ id?: string;
158
146
  type: string;
159
147
  address: string;
160
148
  isPrimary?: boolean;
161
149
  externalChatId?: string | null;
162
- status?: ChannelStatus;
163
- policy?: ChannelPolicy;
164
- verifiedAt?: number | null;
165
- verifiedVia?: string | null;
166
- inviteId?: string | null;
167
- revokedReason?: string | null;
168
- blockedReason?: string | null;
169
150
  }
170
151
 
171
152
  // ── CRUD ─────────────────────────────────────────────────────────────
@@ -184,21 +165,20 @@ export const getContactInternal = getContact;
184
165
  /** INFO-only contact fields, joined locally by contact ID. */
185
166
  export interface ContactInfo {
186
167
  notes: string | null;
187
- interactionCount: number;
188
168
  }
189
169
 
190
170
  /**
191
- * Look up a contact's INFO fields (notes, interaction count) by ID.
171
+ * Look up a contact's INFO `notes` field by ID.
192
172
  *
193
- * Carries no ACL state (status/policy/verification) those are owned by the
194
- * gateway-stamped trust verdict. Returns null when the contact does not exist.
173
+ * Carries no ACL state (status/policy/verification) or interaction telemetry
174
+ * those are owned by the gateway (ACL via the stamped trust verdict, telemetry
175
+ * via the verdict/rich reads). Returns null when the contact does not exist.
195
176
  */
196
177
  export function findContactInfoById(contactId: string): ContactInfo | null {
197
178
  const contact = getContact(contactId);
198
179
  if (!contact) return null;
199
180
  return {
200
181
  notes: contact.notes,
201
- interactionCount: contact.interactionCount,
202
182
  };
203
183
  }
204
184
 
@@ -223,6 +203,11 @@ export function upsertContact(params: {
223
203
  role?: ContactRole;
224
204
  contactType?: ContactType;
225
205
  userFile?: string | null;
206
+ /** userFile to seed ONLY when inserting a new contact; ignored on update so
207
+ * an existing persona-file pointer is never clobbered. Used by the identity
208
+ * mirror to create faithful null-user_file stubs. `userFile` takes
209
+ * precedence when both are supplied. */
210
+ userFileOnCreate?: string | null;
226
211
  channels?: SyncChannelData[];
227
212
  /** When true, conflicting channels on other contacts are reassigned to this
228
213
  * contact instead of being skipped. Used by invite redemption to bind a
@@ -312,7 +297,9 @@ export function upsertContact(params: {
312
297
  const resolvedUserFile =
313
298
  params.userFile !== undefined
314
299
  ? params.userFile
315
- : generateUserFileSlug(params.displayName);
300
+ : params.userFileOnCreate !== undefined
301
+ ? params.userFileOnCreate
302
+ : generateUserFileSlug(params.displayName);
316
303
  db.insert(contacts)
317
304
  .values({
318
305
  id: contactId,
@@ -338,6 +325,16 @@ export function upsertContact(params: {
338
325
  return { ...getContactInternal(contactId)!, created: true };
339
326
  }
340
327
 
328
+ /**
329
+ * Delete a contact row (channels cascade via FK). Info-only: the gateway DB is
330
+ * the ACL source of truth, so this only removes the local identity mirror. A
331
+ * missing row is a harmless no-op.
332
+ */
333
+ export function deleteContact(id: string): void {
334
+ getDb().delete(contacts).where(eq(contacts.id, id)).run();
335
+ notifyContactsChanged();
336
+ }
337
+
341
338
  /**
342
339
  * Add new channels to a contact without removing existing ones.
343
340
  * When a channel already exists (same type+address), updates access/verification
@@ -352,6 +349,57 @@ function syncChannels(
352
349
  const db = getDb();
353
350
 
354
351
  for (const ch of channels) {
352
+ // Identity-mirror update-by-id: when the caller supplies the gateway's
353
+ // authoritative channel id and that row already exists, update it in place.
354
+ // The gateway can rebind the row's address (guardian re-auth) or owner
355
+ // (claimed channel) under a stable id, so a match by (contactId,type,address)
356
+ // would miss it and a fresh insert would collide on the primary key.
357
+ if (ch.id) {
358
+ const byId = db
359
+ .select()
360
+ .from(contactChannels)
361
+ .where(eq(contactChannels.id, ch.id))
362
+ .get();
363
+ if (byId) {
364
+ const crossContact = byId.contactId !== contactId;
365
+ // Never steal a channel the gateway left under another contact unless
366
+ // the caller opts into reassignment (mirrors the address-conflict path).
367
+ if (crossContact && !reassignConflicting) continue;
368
+
369
+ const updateSet: Record<string, unknown> = { updatedAt: now };
370
+ if (byId.address !== ch.address) {
371
+ // Rebinding to a new address: a DIFFERENT row may already hold
372
+ // (type, new-address), and idx_contact_channels_type_address would
373
+ // reject the move. Resolve it the same way the (contactId,type,address)
374
+ // path does — findConflictingChannel + the reassign gate. When
375
+ // reassigning, adopt the (type,address) identity onto this
376
+ // gateway-keyed row by removing the stale duplicate; otherwise leave
377
+ // the address so the existing mapping stands (onConflictDoNothing).
378
+ const conflicting = findConflictingChannel(db, ch.type, ch.address);
379
+ if (conflicting && conflicting.id !== ch.id) {
380
+ if (reassignConflicting) {
381
+ db.delete(contactChannels)
382
+ .where(eq(contactChannels.id, conflicting.id))
383
+ .run();
384
+ updateSet.address = ch.address;
385
+ }
386
+ } else {
387
+ updateSet.address = ch.address;
388
+ }
389
+ }
390
+ if (crossContact) updateSet.contactId = contactId;
391
+ if (ch.isPrimary !== undefined) updateSet.isPrimary = ch.isPrimary;
392
+ if (ch.externalChatId !== undefined)
393
+ updateSet.externalChatId = ch.externalChatId;
394
+
395
+ db.update(contactChannels)
396
+ .set(updateSet)
397
+ .where(eq(contactChannels.id, ch.id))
398
+ .run();
399
+ continue;
400
+ }
401
+ }
402
+
355
403
  // Match by (type, address) — the canonical identity for all channel types.
356
404
  // COLLATE NOCASE catches legacy rows that were lowercased by old write paths.
357
405
  const existing = db
@@ -373,7 +421,6 @@ function syncChannels(
373
421
  if (ch.isPrimary !== undefined) updateSet.isPrimary = ch.isPrimary;
374
422
  if (ch.externalChatId !== undefined)
375
423
  updateSet.externalChatId = ch.externalChatId;
376
- if (ch.inviteId !== undefined) updateSet.inviteId = ch.inviteId;
377
424
 
378
425
  if (Object.keys(updateSet).length > 0) {
379
426
  updateSet.updatedAt = now;
@@ -398,7 +445,7 @@ function syncChannels(
398
445
  };
399
446
  if (ch.externalChatId !== undefined)
400
447
  reassignSet.externalChatId = ch.externalChatId;
401
- if (ch.inviteId !== undefined) reassignSet.inviteId = ch.inviteId;
448
+ if (ch.isPrimary !== undefined) reassignSet.isPrimary = ch.isPrimary;
402
449
 
403
450
  db.update(contactChannels)
404
451
  .set(reassignSet)
@@ -412,13 +459,12 @@ function syncChannels(
412
459
 
413
460
  db.insert(contactChannels)
414
461
  .values({
415
- id: uuid(),
462
+ id: ch.id ?? uuid(),
416
463
  contactId,
417
464
  type: ch.type,
418
465
  address: ch.address,
419
466
  isPrimary: ch.isPrimary ?? false,
420
467
  externalChatId: ch.externalChatId ?? null,
421
- inviteId: ch.inviteId ?? null,
422
468
  createdAt: now,
423
469
  updatedAt: now,
424
470
  })
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * Contacts write module.
3
3
  *
4
- * All mutations (member upserts, guardian bindings, revocations) write
5
- * directly to the contacts table, the single authoritative source for
6
- * identity and access-control state.
4
+ * Best-effort local mirror of contact identity/INFO fields. The gateway owns
5
+ * the ACL verdict (status, policy, verification); these writes never carry or
6
+ * mutate ACL state.
7
7
  */
8
8
 
9
9
  import {
@@ -13,18 +13,7 @@ import {
13
13
  getContactInternal,
14
14
  upsertContact,
15
15
  } from "./contact-store.js";
16
- import type { ContactWriteResult } from "./types.js";
17
-
18
- // ── Guardian operations ──────────────────────────────────────────────
19
-
20
- /**
21
- * No-op shim: the guardian channel ACL revoke is gateway-owned (relayed via
22
- * mark_channel_revoked). Retained while callers still invoke it; the return is
23
- * discarded.
24
- */
25
- export function revokeGuardianBinding(_channel: string): boolean {
26
- return false;
27
- }
16
+ import type { ContactType, ContactWriteResult } from "./types.js";
28
17
 
29
18
  // ── Member operations ────────────────────────────────────────────────
30
19
 
@@ -40,8 +29,33 @@ export function upsertContactChannel(params: {
40
29
  externalChatId?: string;
41
30
  displayName?: string;
42
31
  username?: string;
43
- inviteId?: string;
44
32
  contactId?: string;
33
+ /** Explicit channel id for a NEW channel (identity-mirror path): reuse the
34
+ * gateway-minted id so both stores key the channel identically. */
35
+ channelId?: string;
36
+ /** Classification for a newly created contact (e.g. 'assistant' for bots). */
37
+ contactType?: ContactType;
38
+ /** Notes seeded onto a newly created contact (e.g. bot/app provenance). */
39
+ notes?: string | null;
40
+ /** userFile to seed on contact CREATE only (identity-mirror stubs pass null
41
+ * for a faithful null-user_file replica); ignored on update. */
42
+ userFileOnCreate?: string | null;
43
+ /** Refresh the contact display name from the supplied identity even when a
44
+ * contactId is given. Used by the inbound identity-seed mirror, whose intent
45
+ * is to keep the mirror name in sync with the platform profile. Defaults to
46
+ * false: the invite-binding path preserves the guardian-curated name. */
47
+ refreshDisplayName?: boolean;
48
+ /** Reparent a conflicting existing channel (same (type,address) owned by a
49
+ * DIFFERENT contact) to this contact. Invite-binding only. The inbound
50
+ * identity-seed mirror must pass false so it matches the gateway insert's
51
+ * onConflictDoNothing and never steals a channel from its existing contact.
52
+ * Defaults to `!!contactId` to preserve the invite-binding callers that
53
+ * don't set it explicitly. */
54
+ reassignConflictingChannels?: boolean;
55
+ /** Mark the channel primary. Omit to leave an existing channel's flag intact
56
+ * and default a new channel to non-primary; the guardian-bootstrap mirror
57
+ * passes true so the guardian's sole channel stays primary. */
58
+ isPrimary?: boolean;
45
59
  }): ContactWriteResult | null {
46
60
  let address: string;
47
61
 
@@ -57,9 +71,10 @@ export function upsertContactChannel(params: {
57
71
  let displayName = params.displayName ?? address;
58
72
 
59
73
  // When binding a channel to a specific contact (invite redemption), preserve
60
- // the target contact's curated displayName instead of overwriting it
61
- // with the raw platform identity.
62
- if (params.contactId) {
74
+ // the target contact's curated displayName instead of overwriting it with the
75
+ // raw platform identity. The inbound identity-seed mirror opts out
76
+ // (refreshDisplayName) so a changed platform profile name refreshes the row.
77
+ if (params.contactId && !params.refreshDisplayName) {
63
78
  const targetContact = getContact(params.contactId);
64
79
  if (targetContact?.displayName?.trim().length) {
65
80
  displayName = targetContact.displayName;
@@ -69,18 +84,28 @@ export function upsertContactChannel(params: {
69
84
  upsertContact({
70
85
  id: params.contactId,
71
86
  displayName,
87
+ contactType: params.contactType,
88
+ notes: params.notes,
89
+ userFileOnCreate: params.userFileOnCreate,
72
90
  channels: [
73
91
  {
92
+ id: params.channelId,
74
93
  type: params.sourceChannel,
75
94
  address,
76
- externalChatId: params.externalChatId ?? null,
77
- inviteId: params.inviteId ?? null,
95
+ // Pass through undefined so syncChannels preserves an existing
96
+ // external_chat_id (COALESCE semantics); a new channel still defaults
97
+ // to null. An explicit value overwrites.
98
+ externalChatId: params.externalChatId,
99
+ isPrimary: params.isPrimary,
78
100
  },
79
101
  ],
80
- // When a specific contactId is provided, reassign conflicting channels from
81
- // other contacts. This enables invite redemption to bind a redeemer's
82
- // existing channel identity to the invite's target contact.
83
- reassignConflictingChannels: !!params.contactId,
102
+ // Reassign a conflicting channel from another contact only when the caller
103
+ // explicitly asks (invite redemption binding a redeemer's existing channel
104
+ // to the invite's target). Defaults to `!!contactId` for legacy callers;
105
+ // the inbound-seed mirror passes false so a first-seen race does not steal
106
+ // the channel from the contact the gateway insert kept.
107
+ reassignConflictingChannels:
108
+ params.reassignConflictingChannels ?? !!params.contactId,
84
109
  });
85
110
 
86
111
  // NOTE: We intentionally do NOT seed `users/<slug>.md` here. This is the
@@ -7,12 +7,14 @@
7
7
  */
8
8
 
9
9
  import {
10
+ CreateContactIpcResponseSchema,
10
11
  MarkChannelRevokedIpcResponseSchema,
11
12
  UpsertVerifiedChannelIpcResponseSchema,
12
13
  } from "@vellumai/gateway-client/gateway-ipc-contracts";
13
14
 
14
15
  import { log } from "../daemon/handlers/shared.js";
15
16
  import { ipcCallPersistent } from "../ipc/gateway-client.js";
17
+ import { findContactChannel } from "./contact-store.js";
16
18
  import { revokeMember, upsertContactChannel } from "./contacts-write.js";
17
19
  import type { ContactWriteResult } from "./types.js";
18
20
 
@@ -25,8 +27,6 @@ export interface ActivateMemberChannelParams {
25
27
  contactId?: string;
26
28
  displayName?: string;
27
29
  username?: string;
28
- inviteId?: string;
29
- verifiedAt?: number;
30
30
  verifiedVia?: string;
31
31
  policy?: string;
32
32
  }
@@ -130,7 +130,6 @@ function mirrorLocalActivation(
130
130
  externalChatId: params.externalChatId,
131
131
  displayName: params.displayName,
132
132
  username: params.username,
133
- inviteId: params.inviteId,
134
133
  contactId: params.contactId,
135
134
  });
136
135
  } catch (err) {
@@ -142,42 +141,127 @@ function mirrorLocalActivation(
142
141
  }
143
142
  }
144
143
 
145
- // ── Revoke ───────────────────────────────────────────────────────────
144
+ // ── Block ────────────────────────────────────────────────────────────
145
+
146
+ export interface BlockSenderChannelParams {
147
+ sourceChannel: string;
148
+ externalUserId: string;
149
+ displayName?: string;
150
+ /** Audit reason written to the gateway channel's revokedReason. */
151
+ reason?: string;
152
+ }
146
153
 
147
154
  /**
148
- * Revoke a member channel gateway-first. The gateway owns the ACL outcome; the
149
- * memberId may be a plain channel ID or the composite contactId:channelId form
150
- * revokeMember accepts.
155
+ * Block a sender's channel gateway-first: ensure a contact/channel row exists
156
+ * for the (channel, address) pair, then mark it revoked so future inbound
157
+ * resolves as `unknown` and is hard-denied. Used by the introduction card's
158
+ * **Block** action for senders that may have no contact record yet.
151
159
  *
152
- * Returns the locally-resolved native contact/channel for the revoked id, or
153
- * null when no local row exists. The local read is best-effort and never gates
154
- * the gateway-owned downgrade.
160
+ * The gateway owns the verdict: `create_contact` preserves an existing row's
161
+ * status, and `mark_channel_revoked` is idempotent (already-revoked
162
+ * didWrite:false) and refuses to downgrade a guardian channel. Fails closed —
163
+ * a relay failure surfaces as `revoked: false` so callers never report a block
164
+ * the gateway did not persist. The assistant-DB status update is a best-effort
165
+ * mirror.
155
166
  */
156
- export async function revokeMemberChannel(
157
- memberId: string,
158
- reason?: string,
159
- ): Promise<ContactWriteResult | null> {
160
- const channelId = memberId.includes(":") ? memberId.split(":")[1] : memberId;
161
-
162
- // Always relay; the gateway owns the ACL outcome and mark_channel_revoked is
163
- // idempotent (already-revoked → didWrite:false). Skipping on the local row
164
- // status would suppress a needed revoke when the local read lags the gateway.
165
- const result = await ipcCallPersistent("mark_channel_revoked", {
166
- contactChannelId: channelId,
167
- reason,
168
- });
169
- const parsed = MarkChannelRevokedIpcResponseSchema.parse(result);
170
- if (!parsed.ok) {
171
- throw new Error("mark_channel_revoked relay returned ok: false");
167
+ export async function blockSenderChannel(
168
+ params: BlockSenderChannelParams,
169
+ ): Promise<{ revoked: boolean }> {
170
+ let channelId: string;
171
+ try {
172
+ const created = await ipcCallPersistent("create_contact", {
173
+ channelType: params.sourceChannel,
174
+ address: params.externalUserId,
175
+ ...(params.displayName ? { displayName: params.displayName } : {}),
176
+ });
177
+ channelId = CreateContactIpcResponseSchema.parse(created).channelId;
178
+ } catch (err) {
179
+ log.warn(
180
+ { err, sourceChannel: params.sourceChannel },
181
+ "create_contact relay failed — refusing block (no gateway channel row)",
182
+ );
183
+ return { revoked: false };
184
+ }
185
+ if (!channelId) {
186
+ log.error(
187
+ { sourceChannel: params.sourceChannel },
188
+ "create_contact returned no channel id — refusing block",
189
+ );
190
+ return { revoked: false };
172
191
  }
173
192
 
174
193
  try {
175
- return revokeMember(memberId);
194
+ const result = await ipcCallPersistent("mark_channel_revoked", {
195
+ contactChannelId: channelId,
196
+ reason: params.reason,
197
+ });
198
+ const parsed = MarkChannelRevokedIpcResponseSchema.parse(result);
199
+ if (!parsed.ok) {
200
+ return { revoked: false };
201
+ }
176
202
  } catch (err) {
177
- log.error(
178
- { err, memberId },
179
- "Local revoke read failed after gateway revoke; gateway downgrade stands",
203
+ log.warn(
204
+ { err, sourceChannel: params.sourceChannel },
205
+ "mark_channel_revoked relay failed block did not land on the gateway",
206
+ );
207
+ return { revoked: false };
208
+ }
209
+
210
+ // Best-effort local mirror so the Contacts page reflects the downgrade.
211
+ // Resolved by logical (type, address) key — the local mirror row's id is
212
+ // not guaranteed to match the gateway channel id.
213
+ try {
214
+ const local = findContactChannel({
215
+ channelType: params.sourceChannel,
216
+ address: params.externalUserId,
217
+ });
218
+ if (local) {
219
+ revokeMember(local.channel.id);
220
+ }
221
+ } catch {
222
+ // The local row may not exist for a brand-new sender; the gateway
223
+ // outcome stands.
224
+ }
225
+ return { revoked: true };
226
+ }
227
+
228
+ // ── Seed unverified ──────────────────────────────────────────────────
229
+
230
+ export interface SeedUnverifiedMemberChannelParams {
231
+ sourceChannel: string;
232
+ externalUserId: string;
233
+ displayName?: string;
234
+ }
235
+
236
+ /**
237
+ * Seed a contact channel for a sender at the `unverified` admission tier,
238
+ * gateway-first. Used when the guardian denies an access request: the sender
239
+ * becomes a known `unverified_contact` — no longer an unknown stranger — so
240
+ * subsequent inbound resolves as `unverified_contact` instead of re-running
241
+ * discovery.
242
+ *
243
+ * Delegates to the gateway `create_contact` IPC, which upserts via
244
+ * `ContactStore.upsertContact` (gateway DB is the ACL source of truth, with a
245
+ * best-effort assistant-DB mirror). A brand-new channel lands at status
246
+ * `unverified`; an existing channel's status is preserved, so a blocked,
247
+ * revoked, or already-active row is never reactivated or downgraded.
248
+ *
249
+ * Best-effort: the gateway owns the ACL verdict, so a failed relay is logged
250
+ * and swallowed — it must never fail the guardian's deny decision.
251
+ */
252
+ export async function seedUnverifiedMemberChannel(
253
+ params: SeedUnverifiedMemberChannelParams,
254
+ ): Promise<void> {
255
+ try {
256
+ await ipcCallPersistent("create_contact", {
257
+ channelType: params.sourceChannel,
258
+ address: params.externalUserId,
259
+ ...(params.displayName ? { displayName: params.displayName } : {}),
260
+ });
261
+ } catch (err) {
262
+ log.warn(
263
+ { err, sourceChannel: params.sourceChannel },
264
+ "seed_unverified_channel relay failed (best-effort); sender not persisted as unverified_contact",
180
265
  );
181
- return null;
182
266
  }
183
267
  }
@@ -31,8 +31,6 @@ export interface Contact {
31
31
  /** Free-text notes about this contact (e.g. relationship, communication preferences). */
32
32
  notes: string | null;
33
33
  role: ContactRole;
34
- lastInteraction: number | null;
35
- interactionCount: number;
36
34
  createdAt: number;
37
35
  updatedAt: number;
38
36
  contactType: ContactType;
@@ -55,12 +53,6 @@ export interface ContactChannel {
55
53
  address: string;
56
54
  isPrimary: boolean;
57
55
  externalChatId: string | null;
58
- inviteId: string | null;
59
- // INFO telemetry (not ACL): interaction stats written locally by the gateway's
60
- // handle-inbound mirror. Model-facing turn context reads these.
61
- lastSeenAt: number | null;
62
- interactionCount: number;
63
- lastInteraction: number | null;
64
56
  updatedAt: number | null;
65
57
  createdAt: number;
66
58
  }
@@ -22,7 +22,6 @@ import { optimizeImageForTransport } from "../agent/image-optimize.js";
22
22
  import type { CompactionConfig } from "../config/schemas/compaction.js";
23
23
  import type { LLMCallSite } from "../config/schemas/llm.js";
24
24
  import { filterMessagesForUntrustedActor } from "../daemon/message-provenance.js";
25
- import { stripHistoricalWebSearchResults } from "../daemon/web-search-history.js";
26
25
  import {
27
26
  getAttachmentContent,
28
27
  getAttachmentMetadataForMessage,
@@ -40,6 +39,7 @@ import type {
40
39
  import { type TrustClass } from "../runtime/actor-trust-resolver.js";
41
40
  import { resolveCapabilities } from "../runtime/capabilities.js";
42
41
  import { getLogger } from "../util/logger.js";
42
+ import { preModelCallSanitize } from "./outbound-sanitize.js";
43
43
  import { stripInjectionsForCompaction } from "./strip-injections.js";
44
44
  import {
45
45
  estimatePromptTokens,
@@ -826,14 +826,20 @@ function extractTextFromResponse(content: ContentBlock[]): string {
826
826
  .join("\n");
827
827
  }
828
828
 
829
- // Build the outbound message list for a compaction provider call: convert
830
- // historical web_search_tool_result blocks to text, then append the
831
- // summarization instruction at the tail.
829
+ // Build the outbound message list for a compaction provider call: apply the
830
+ // same pre-send sanitization bundle as the agent loop's model calls
831
+ // (`preModelCallSanitize` old tool-result media stripped, AX trees
832
+ // collapsed, historical web-search results converted to text), then append
833
+ // the summarization instruction at the tail.
832
834
  //
833
- // Anthropic's opaque `encrypted_content` tokens are route-scoped and expire, so
834
- // replaying a stale one is rejected with `Invalid encrypted_content in
835
- // search_result block`. Sanitizing here the single seam every compaction
836
- // provider call funnels through keeps that error away from both the
835
+ // Matching the loop's projection matters for two reasons. First, the summary
836
+ // call's prefix stays byte-aligned with the agent's warm prompt cache — an
837
+ // unsanitized history diverges from what the loop actually sent at the first
838
+ // stripped block. Second, an unsanitized history carries every screenshot in
839
+ // the conversation; enough images cross Anthropic's many-image threshold,
840
+ // where a stricter per-image dimension cap applies and a single large
841
+ // screenshot rejects the whole summary call. Sanitizing here — the single
842
+ // seam every compaction provider call funnels through — covers both the
837
843
  // assistant-driven and emergency summarization calls. Only this outbound copy
838
844
  // is sanitized; tail resolution and the persisted compaction result read the
839
845
  // caller's original messages, so durable history keeps the rich blocks. The
@@ -842,7 +848,7 @@ function buildCompactionRequest(
842
848
  history: Message[],
843
849
  instruction: Message,
844
850
  ): Message[] {
845
- return [...stripHistoricalWebSearchResults(history).messages, instruction];
851
+ return [...preModelCallSanitize(history), instruction];
846
852
  }
847
853
 
848
854
  // Token headroom a compaction summary call reserves on top of its history: room
@@ -945,11 +951,14 @@ export async function runAssistantDrivenCompaction(
945
951
  // Bound the summary call's own input to the context window. With no tool
946
952
  // pair to anchor an emergency split, an overflow recovery routes the full
947
953
  // history straight here, so the summary call must front-truncate itself or
948
- // it overflows in turn. `args.messages` stays intact for tail resolution
949
- // below — only the outbound request is truncated. A below-budget history is
950
- // returned untouched, keeping the prefix aligned with the agent's warm cache.
954
+ // it overflows in turn. Truncation operates on the sanitized projection
955
+ // (what the request actually carries) so the budget estimate is honest —
956
+ // estimating on raw history would count media bytes the request strips.
957
+ // `args.messages` stays intact for tail resolution below — only the
958
+ // outbound request is truncated. A below-budget history is returned
959
+ // untouched, keeping the prefix aligned with the agent's warm cache.
951
960
  const summaryHistory = truncateHistoryToBudget({
952
- messages: args.messages,
961
+ messages: preModelCallSanitize(args.messages),
953
962
  systemPrompt: args.systemPrompt,
954
963
  budgetTokens: compactionPrefixBudget(args.maxInputTokens),
955
964
  providerName: args.provider.tokenEstimationProvider ?? args.provider.name,
@@ -1371,9 +1380,14 @@ export async function runEmergencyCompaction(
1371
1380
  );
1372
1381
  // Bound the prefix to the context window so the summary call fits, reserving
1373
1382
  // budget for the instruction message and the emitted summary. Truncates from
1374
- // the front, keeping the recent portion the summary most needs.
1383
+ // the front, keeping the recent portion the summary most needs. The prefix
1384
+ // is sliced from the sanitized projection (sanitize-then-slice, matching the
1385
+ // agent's own sends byte-for-byte for cache alignment) so the budget
1386
+ // estimate counts what the request actually carries. All sanitize
1387
+ // transforms are 1:1 per message, so `splitIndex` maps onto the sanitized
1388
+ // array unchanged.
1375
1389
  const prefix = truncateHistoryToBudget({
1376
- messages: args.messages.slice(0, splitIndex),
1390
+ messages: preModelCallSanitize(args.messages).slice(0, splitIndex),
1377
1391
  systemPrompt: args.systemPrompt,
1378
1392
  budgetTokens: compactionPrefixBudget(args.maxInputTokens),
1379
1393
  providerName: args.provider.tokenEstimationProvider ?? args.provider.name,