@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
@@ -87,6 +87,13 @@ interface WakeConversationProbe {
87
87
  * persistent `setTrustContext`) and restores the prior value in its finally.
88
88
  */
89
89
  turnTrustContextSets: Array<{ ctx: unknown; order: number }>;
90
+ /**
91
+ * `setTrustContext` writes to the persistent `conversation.trustContext`,
92
+ * in order. A trust-carrying wake's resolver leaves its trust here; the
93
+ * wake must put the prior value back so it doesn't linger for a later
94
+ * no-trust wake.
95
+ */
96
+ trustContextSets: unknown[];
90
97
  /**
91
98
  * Every assignment to `conversation.wakePersonaOverride`, in order. A
92
99
  * wake that applies an override records `[override, undefined]` — the
@@ -340,6 +347,10 @@ import type {
340
347
  AgentLoopRunResult,
341
348
  } from "../../agent/loop.js";
342
349
  import type { Conversation } from "../../daemon/conversation.js";
350
+ import {
351
+ deleteConversation,
352
+ setConversation,
353
+ } from "../../daemon/conversation-registry.js";
343
354
  import { ContextOverflowError, type Message } from "../../providers/types.js";
344
355
  import {
345
356
  __resetWakeChainForTests,
@@ -387,6 +398,8 @@ function makeWakeConversation(options: {
387
398
  * (always under the mocked 200k window).
388
399
  */
389
400
  estimatedInputTokens?: number;
401
+ /** Seed for the conversation's resting `trustContext`. */
402
+ initialTrustContext?: unknown;
390
403
  }): WakeConversation {
391
404
  const conversationId = options.conversationId ?? "conv-test";
392
405
  const probe: WakeConversationProbe = {
@@ -401,6 +414,7 @@ function makeWakeConversation(options: {
401
414
  processingDuringDrain: [],
402
415
  allowedToolSnapshots: [],
403
416
  turnTrustContextSets: [],
417
+ trustContextSets: [],
404
418
  personaOverrideSets: [],
405
419
  persistedAtEachEmit: [],
406
420
  maybeCompactOrders: [],
@@ -413,6 +427,7 @@ function makeWakeConversation(options: {
413
427
  let activeAllowedTools = options.initialAllowedTools;
414
428
  let wakePersonaOverride: unknown;
415
429
  let currentTurnTrustContext: unknown;
430
+ let persistentTrustContext: unknown = options.initialTrustContext;
416
431
  const snapshotAllowedTools = (): string[] | undefined =>
417
432
  activeAllowedTools ? [...activeAllowedTools].sort() : undefined;
418
433
 
@@ -526,7 +541,14 @@ function makeWakeConversation(options: {
526
541
  },
527
542
  getTurnChannelContext: () => null,
528
543
  getTurnInterfaceContext: () => null,
529
- trustContext: undefined,
544
+ get trustContext() {
545
+ return persistentTrustContext;
546
+ },
547
+ // Mirrors Conversation.setTrustContext (coerces null → undefined).
548
+ setTrustContext(ctx: unknown) {
549
+ persistentTrustContext = ctx ?? undefined;
550
+ probe.trustContextSets.push(ctx);
551
+ },
530
552
  buildCurrentSystemPrompt: () => "mock-system-prompt",
531
553
  modelOverride: undefined,
532
554
  ...(drainQueue ? { drainQueue } : {}),
@@ -1535,6 +1557,141 @@ describe("wakeAgentForOpportunity", () => {
1535
1557
  expect(conversation.currentTurnTrustContext).toBeUndefined();
1536
1558
  });
1537
1559
 
1560
+ // ── Persistent-trust restore ────────────────────────────────────────
1561
+ // The resolver leaves the wake's trust on the conversation. The wake must
1562
+ // put the prior resting value back, or a later no-trust wake inherits it
1563
+ // through tool setup's `currentTurnTrustContext ?? trustContext` fallback.
1564
+
1565
+ const GUARDIAN_TRUST = {
1566
+ sourceChannel: "vellum",
1567
+ trustClass: "guardian",
1568
+ } as const;
1569
+
1570
+ // The wake reads the prior trust from the live registry, so a test that
1571
+ // wants to exercise the restore registers its double there and simulates
1572
+ // the resolver's write by setting the trust in resolveTarget.
1573
+ const withRegisteredConversation = async (
1574
+ conversation: WakeConversation,
1575
+ run: () => Promise<unknown>,
1576
+ ): Promise<void> => {
1577
+ setConversation(conversation.conversationId, conversation as Conversation);
1578
+ try {
1579
+ await run();
1580
+ } finally {
1581
+ deleteConversation(conversation.conversationId);
1582
+ }
1583
+ };
1584
+
1585
+ test("restores the conversation's prior trust once the wake ends", async () => {
1586
+ const priorTrust = {
1587
+ sourceChannel: "slack",
1588
+ trustClass: "trusted_contact",
1589
+ } as const;
1590
+ const conversation = makeWakeConversation({
1591
+ conversationId: "conv-trust-restore",
1592
+ initialTrustContext: priorTrust,
1593
+ });
1594
+
1595
+ await withRegisteredConversation(conversation, () =>
1596
+ wakeAgentForOpportunity(
1597
+ {
1598
+ conversationId: "conv-trust-restore",
1599
+ hint: "x",
1600
+ source: "t",
1601
+ trustContext: GUARDIAN_TRUST,
1602
+ },
1603
+ {
1604
+ resolveTarget: async () => {
1605
+ conversation.setTrustContext(GUARDIAN_TRUST);
1606
+ return conversation;
1607
+ },
1608
+ },
1609
+ ),
1610
+ );
1611
+
1612
+ // Resolver installs guardian, wake restores the prior value.
1613
+ expect(conversation.trustContextSets).toEqual([GUARDIAN_TRUST, priorTrust]);
1614
+ expect(conversation.trustContext).toEqual(priorTrust);
1615
+ });
1616
+
1617
+ test("restores the prior trust even when the agent loop throws", async () => {
1618
+ const conversation = makeWakeConversation({
1619
+ conversationId: "conv-trust-restore-throw",
1620
+ runImpl: async () => {
1621
+ throw new Error("boom");
1622
+ },
1623
+ });
1624
+
1625
+ await withRegisteredConversation(conversation, () =>
1626
+ wakeAgentForOpportunity(
1627
+ {
1628
+ conversationId: "conv-trust-restore-throw",
1629
+ hint: "x",
1630
+ source: "t",
1631
+ trustContext: GUARDIAN_TRUST,
1632
+ },
1633
+ {
1634
+ resolveTarget: async () => {
1635
+ conversation.setTrustContext(GUARDIAN_TRUST);
1636
+ return conversation;
1637
+ },
1638
+ },
1639
+ ),
1640
+ );
1641
+
1642
+ // Nothing was resting before the wake, so it goes back to unset.
1643
+ expect(conversation.trustContext).toBeUndefined();
1644
+ });
1645
+
1646
+ test("leaves a trust that was re-set mid-run alone (identity guard)", async () => {
1647
+ const replaced = {
1648
+ sourceChannel: "vellum",
1649
+ trustClass: "unknown",
1650
+ } as const;
1651
+ const conversation = makeWakeConversation({
1652
+ conversationId: "conv-trust-guard",
1653
+ runImpl: async (input) => {
1654
+ conversation.setTrustContext(replaced);
1655
+ return runResult(input);
1656
+ },
1657
+ });
1658
+
1659
+ await withRegisteredConversation(conversation, () =>
1660
+ wakeAgentForOpportunity(
1661
+ {
1662
+ conversationId: "conv-trust-guard",
1663
+ hint: "x",
1664
+ source: "t",
1665
+ trustContext: GUARDIAN_TRUST,
1666
+ },
1667
+ {
1668
+ resolveTarget: async () => {
1669
+ conversation.setTrustContext(GUARDIAN_TRUST);
1670
+ return conversation;
1671
+ },
1672
+ },
1673
+ ),
1674
+ );
1675
+
1676
+ // The wake sees a different reference than it installed, so it doesn't
1677
+ // restore: two writes (resolver install, mid-run replacement), no third.
1678
+ expect(conversation.trustContext).toEqual(replaced);
1679
+ expect(conversation.trustContextSets).toHaveLength(2);
1680
+ });
1681
+
1682
+ test("never touches the persistent trust when the wake carries none", async () => {
1683
+ const conversation = makeWakeConversation({
1684
+ conversationId: "conv-trust-none",
1685
+ });
1686
+
1687
+ await wakeAgentForOpportunity(
1688
+ { conversationId: "conv-trust-none", hint: "x", source: "t" },
1689
+ { resolveTarget: async () => conversation },
1690
+ );
1691
+
1692
+ expect(conversation.trustContextSets).toHaveLength(0);
1693
+ });
1694
+
1538
1695
  test("two concurrent wakes on the same conversation are serialized", async () => {
1539
1696
  // Build a target whose agentLoop.run resolves only when we signal.
1540
1697
  const gate1 = Promise.withResolvers<void>();
@@ -73,7 +73,7 @@ describe("getGuardianBinding", () => {
73
73
  expect(binding?.verifiedVia).toBe("verified");
74
74
  });
75
75
 
76
- test("falls back to empty strings for absent optional delivery fields", async () => {
76
+ test("a missing principal surfaces as null (unresolved), never an empty string", async () => {
77
77
  mockGuardianList = [
78
78
  {
79
79
  channelType: "telegram",
@@ -85,7 +85,7 @@ describe("getGuardianBinding", () => {
85
85
 
86
86
  const binding = await getGuardianBinding("asst-1", "telegram");
87
87
 
88
- expect(binding?.guardianPrincipalId).toBe("");
88
+ expect(binding?.guardianPrincipalId).toBeNull();
89
89
  expect(binding?.guardianDeliveryChatId).toBe("");
90
90
  expect(binding?.verifiedAt).toBe(0);
91
91
  });
@@ -128,6 +128,43 @@ describe("trustContextFromVerdict", () => {
128
128
  expect(result.memberPolicy).toBe("escalate");
129
129
  });
130
130
 
131
+ test("carries the verdict's gateway-owned interaction count onto the context", () => {
132
+ const verdict = {
133
+ trustClass: "trusted_contact",
134
+ canonicalSenderId: "u-1",
135
+ contactId: "contact-1",
136
+ channelId: "channel-1",
137
+ status: "active",
138
+ policy: "allow",
139
+ interactionCount: 9,
140
+ } satisfies TrustVerdict;
141
+
142
+ const result = trustContextFromVerdict(verdict, {
143
+ sourceChannel: "slack",
144
+ conversationExternalId: CONV,
145
+ });
146
+
147
+ expect(result.requesterInteractionCount).toBe(9);
148
+ });
149
+
150
+ test("leaves interaction count undefined when the verdict carries none", () => {
151
+ const verdict = {
152
+ trustClass: "trusted_contact",
153
+ canonicalSenderId: "u-1",
154
+ contactId: "contact-1",
155
+ channelId: "channel-1",
156
+ status: "active",
157
+ policy: "allow",
158
+ } satisfies TrustVerdict;
159
+
160
+ const result = trustContextFromVerdict(verdict, {
161
+ sourceChannel: "slack",
162
+ conversationExternalId: CONV,
163
+ });
164
+
165
+ expect(result.requesterInteractionCount).toBeUndefined();
166
+ });
167
+
131
168
  test("memberless verdict leaves ACL member fields undefined", () => {
132
169
  const verdict = {
133
170
  trustClass: "unknown",
@@ -322,8 +359,6 @@ describe("actorTrustContextFromVerdict", () => {
322
359
  // INFO fields must be null/default placeholders.
323
360
  expect(memberRecord!.contact.notes).toBeNull();
324
361
  expect(memberRecord!.contact.userFile).toBeNull();
325
- expect(memberRecord!.contact.interactionCount).toBe(0);
326
- expect(memberRecord!.contact.lastInteraction).toBeNull();
327
362
  });
328
363
 
329
364
  test("guardian member verdict maps role guardian + principalId", () => {
@@ -436,10 +471,6 @@ describe("toTrustContext member grounding", () => {
436
471
  address: "+15550100",
437
472
  isPrimary: true,
438
473
  externalChatId: null,
439
- inviteId: null,
440
- lastSeenAt: null,
441
- interactionCount: 0,
442
- lastInteraction: null,
443
474
  updatedAt: null,
444
475
  createdAt: 0,
445
476
  };
@@ -451,8 +482,6 @@ describe("toTrustContext member grounding", () => {
451
482
  displayName: "Frank",
452
483
  notes: null,
453
484
  role: "contact",
454
- lastInteraction: null,
455
- interactionCount: 0,
456
485
  createdAt: 0,
457
486
  updatedAt: 0,
458
487
  contactType: "human",
@@ -26,6 +26,8 @@ import { emitNotificationSignal } from "../notifications/emit-signal.js";
26
26
  import type { GuardianResolutionSource } from "../notifications/signal.js";
27
27
  import { getLogger } from "../util/logger.js";
28
28
  import { resolveAnchoredGuardian } from "./anchored-guardian.js";
29
+ import { CHALLENGE_TTL_MS } from "./channel-verification-service.js";
30
+ import { serializeRequesterSignals } from "./introduction-policy.js";
29
31
  import { GUARDIAN_APPROVAL_TTL_MS } from "./routes/channel-route-shared.js";
30
32
 
31
33
  const log = getLogger("access-request-helper");
@@ -44,6 +46,8 @@ export interface AccessRequestParams {
44
46
  previousMemberStatus?: Exclude<ChannelStatus, "unverified">;
45
47
  /** Preview of the requester's original message, shown to the guardian. */
46
48
  messagePreview?: string;
49
+ /** The sender is a bot / integration account (no verification handshake possible). */
50
+ isBot?: boolean;
47
51
  /** Slack-specific: user is from an external workspace (Slack Connect). */
48
52
  isStranger?: boolean;
49
53
  /** Slack-specific: user is a guest / restricted account. */
@@ -54,7 +58,98 @@ export interface AccessRequestParams {
54
58
 
55
59
  export type AccessRequestResult =
56
60
  | { notified: true; created: boolean; requestId: string }
57
- | { notified: false; reason: "no_sender_id" };
61
+ | {
62
+ notified: false;
63
+ reason:
64
+ | "no_sender_id"
65
+ | "already_denied"
66
+ | "approval_pending_verification";
67
+ };
68
+
69
+ // ---------------------------------------------------------------------------
70
+ // Terminal-deny lookup
71
+ // ---------------------------------------------------------------------------
72
+
73
+ /**
74
+ * Assistant-scoped conversation id for an actor's access requests. Stable key
75
+ * that dedupes pending prompts and detects a prior terminal deny for the same
76
+ * (assistant, channel, actor).
77
+ */
78
+ export function accessRequestConversationId(
79
+ canonicalAssistantId: string,
80
+ sourceChannel: string,
81
+ actorExternalId: string,
82
+ ): string {
83
+ return `access-req-${canonicalAssistantId}-${sourceChannel}-${actorExternalId}`;
84
+ }
85
+
86
+ /**
87
+ * Whether the guardian has already terminally denied an access request from
88
+ * this actor on this channel. Callers use it to suppress re-engagement — both
89
+ * the guardian prompt and the self-verify challenge — for a sender the guardian
90
+ * explicitly rejected. Reads the retained `denied` canonical request scoped by
91
+ * the assistant-scoped conversation id.
92
+ */
93
+ export function isAccessRequestDenied(params: {
94
+ canonicalAssistantId: string;
95
+ sourceChannel: string;
96
+ actorExternalId: string;
97
+ }): boolean {
98
+ const conversationId = accessRequestConversationId(
99
+ params.canonicalAssistantId,
100
+ params.sourceChannel,
101
+ params.actorExternalId,
102
+ );
103
+ return (
104
+ listCanonicalGuardianRequests({
105
+ status: "denied",
106
+ requesterExternalUserId: params.actorExternalId,
107
+ sourceChannel: params.sourceChannel,
108
+ kind: "access_request",
109
+ conversationId,
110
+ }).length > 0
111
+ );
112
+ }
113
+
114
+ /**
115
+ * Whether this sender is inside the post-approval verification window: the
116
+ * guardian approved their access request recently enough that the minted
117
+ * 6-digit code could still be redeemable. In that state the handshake is in
118
+ * progress — the sender needs to enter the code, not trigger a fresh access
119
+ * request.
120
+ *
121
+ * Keyed on the approval decision time (the request row's `updatedAt`) bounded
122
+ * by the verification-code TTL, deliberately NOT on live verification-session
123
+ * rows: session lookups cannot distinguish the approval-minted session from a
124
+ * self-verify challenge session the ACL mints on the same inbound, or from an
125
+ * unrelated session bound to the same identity (guardian-initiated
126
+ * verification, invites).
127
+ *
128
+ * Voice is excluded: phone approvals activate the caller directly and never
129
+ * mint a code.
130
+ */
131
+ export function isApprovalHandshakeInProgress(params: {
132
+ canonicalAssistantId: string;
133
+ sourceChannel: string;
134
+ actorExternalId: string;
135
+ }): boolean {
136
+ if (params.sourceChannel === "phone") {
137
+ return false;
138
+ }
139
+ const conversationId = accessRequestConversationId(
140
+ params.canonicalAssistantId,
141
+ params.sourceChannel,
142
+ params.actorExternalId,
143
+ );
144
+ const windowStart = Date.now() - CHALLENGE_TTL_MS;
145
+ return listCanonicalGuardianRequests({
146
+ status: "approved",
147
+ requesterExternalUserId: params.actorExternalId,
148
+ sourceChannel: params.sourceChannel,
149
+ kind: "access_request",
150
+ conversationId,
151
+ }).some((request) => request.updatedAt >= windowStart);
152
+ }
58
153
 
59
154
  // ---------------------------------------------------------------------------
60
155
  // Helper
@@ -86,6 +181,7 @@ export async function notifyGuardianOfAccessRequest(
86
181
  actorUsername,
87
182
  previousMemberStatus,
88
183
  messagePreview,
184
+ isBot,
89
185
  isStranger,
90
186
  isRestricted,
91
187
  messageTs,
@@ -122,7 +218,11 @@ export async function notifyGuardianOfAccessRequest(
122
218
  // matches requests for the same assistant. Without this, a pending request
123
219
  // from assistant A could be returned for assistant B, allowing the caller
124
220
  // to piggyback on A's guardian approval.
125
- const conversationId = `access-req-${canonicalAssistantId}-${sourceChannel}-${actorExternalId}`;
221
+ const conversationId = accessRequestConversationId(
222
+ canonicalAssistantId,
223
+ sourceChannel,
224
+ actorExternalId,
225
+ );
126
226
 
127
227
  // Deduplicate: skip creation if there is already a pending canonical request
128
228
  // for the same requester on this channel *and* assistant. Still return
@@ -147,6 +247,47 @@ export async function notifyGuardianOfAccessRequest(
147
247
  };
148
248
  }
149
249
 
250
+ // Terminal-deny suppression: once the guardian has denied an access request
251
+ // for this sender on this channel, subsequent inbound must not re-prompt.
252
+ // The denied decision persists the sender as an unverified_contact (see the
253
+ // accessRequestResolver deny path); re-surfacing the same request the
254
+ // guardian already rejected would be noise. The guardian can still verify the
255
+ // contact manually — that path does not go through here. Checked before the
256
+ // handshake window below so a standing deny always wins over an earlier
257
+ // approval.
258
+ if (
259
+ isAccessRequestDenied({
260
+ canonicalAssistantId,
261
+ sourceChannel,
262
+ actorExternalId,
263
+ })
264
+ ) {
265
+ log.debug(
266
+ { sourceChannel, actorExternalId },
267
+ "Suppressing access request notification — guardian already denied this sender",
268
+ );
269
+ return { notified: false, reason: "already_denied" };
270
+ }
271
+
272
+ // Handshake-in-progress suppression: within the verification-code window
273
+ // after the guardian approves, the flow is waiting on the requester to enter
274
+ // their code. Inbound from the sender in that window must not create a new
275
+ // request or re-notify the guardian; once the window lapses unconsumed,
276
+ // re-prompting is allowed again.
277
+ if (
278
+ isApprovalHandshakeInProgress({
279
+ canonicalAssistantId,
280
+ sourceChannel,
281
+ actorExternalId,
282
+ })
283
+ ) {
284
+ log.debug(
285
+ { sourceChannel, actorExternalId },
286
+ "Suppressing access request notification — approval granted, verification window still open",
287
+ );
288
+ return { notified: false, reason: "approval_pending_verification" };
289
+ }
290
+
150
291
  const senderIdentifier = actorDisplayName || actorUsername || actorExternalId;
151
292
  const requestId = `access-req-${canonicalAssistantId}-${sourceChannel}-${actorExternalId}-${Date.now()}`;
152
293
 
@@ -162,6 +303,11 @@ export async function notifyGuardianOfAccessRequest(
162
303
  guardianPrincipalId: guardianPrincipalId ?? undefined,
163
304
  toolName: "ingress_access_request",
164
305
  questionText: `${senderIdentifier} is requesting access to the assistant`,
306
+ requesterSignals: serializeRequesterSignals({
307
+ isBot,
308
+ isStranger,
309
+ isRestricted,
310
+ }),
165
311
  expiresAt: Date.now() + GUARDIAN_APPROVAL_TTL_MS,
166
312
  });
167
313
 
@@ -207,14 +353,19 @@ export async function notifyGuardianOfAccessRequest(
207
353
  guardianResolutionSource,
208
354
  previousMemberStatus: previousMemberStatus ?? null,
209
355
  messagePreview: messagePreview ?? null,
356
+ ...(isBot !== undefined ? { isBot } : {}),
210
357
  ...(isStranger !== undefined ? { isStranger } : {}),
211
358
  ...(isRestricted !== undefined ? { isRestricted } : {}),
212
359
  ...(messageTs ? { messageTs } : {}),
213
360
  },
214
361
  dedupeKey: `access-request:${canonicalRequest.id}`,
215
362
  onConversationCreated: (info) => {
216
- if (info.sourceEventName !== "ingress.access_request" || vellumDeliveryId)
363
+ if (
364
+ info.sourceEventName !== "ingress.access_request" ||
365
+ vellumDeliveryId
366
+ ) {
217
367
  return;
368
+ }
218
369
  vellumDeliveryId = recordApprovalCardDelivery({
219
370
  requestId: canonicalRequest.id,
220
371
  channel: "vellum",
@@ -34,6 +34,7 @@ import type { TrustContext } from "../daemon/trust-context.js";
34
34
  import { canonicalizeInboundIdentity } from "../util/canonicalize-identity.js";
35
35
  import { getLogger } from "../util/logger.js";
36
36
  import { getCachedMemberAcl } from "./member-verdict-cache.js";
37
+ import type { TrustClass } from "./trust-class.js";
37
38
 
38
39
  const log = getLogger("actor-trust-resolver");
39
40
 
@@ -44,27 +45,11 @@ export type { TrustContext } from "../daemon/trust-context.js";
44
45
  // ---------------------------------------------------------------------------
45
46
 
46
47
  /**
47
- * Trust classification for an inbound actor.
48
- *
49
- * - `'guardian'`: The sender matches the active guardian binding for this
50
- * (assistant, channel). Guardians have full control-plane access and
51
- * self-approve tool invocations.
52
- * - `'trusted_contact'`: The sender is an active contact with a channel
53
- * (not the guardian). Trusted contacts can invoke tools but require
54
- * guardian approval for sensitive operations.
55
- * - `'unverified_contact'`: The sender matches a contact channel whose
56
- * status is `pending` or `unverified` — known to the guardian but not yet
57
- * verified. Treated identically to `trusted_contact` for every downstream
58
- * capability/tool/approval decision; the distinction is admission-only.
59
- * - `'unknown'`: The sender has no contact record, no identity could be
60
- * established, or the sender is a blocked/revoked contact. Unknown
61
- * actors are fail-closed with no escalation path.
48
+ * Trust classification for an inbound actor. Defined once in `./trust-class.ts`
49
+ * (shared with the persistence metadata schema) and re-exported here, the
50
+ * canonical import site for the resolver's consumers.
62
51
  */
63
- export type TrustClass =
64
- | "guardian"
65
- | "trusted_contact"
66
- | "unverified_contact"
67
- | "unknown";
52
+ export type { TrustClass };
68
53
 
69
54
  /**
70
55
  * Fully resolved trust context from the actor trust resolver.
@@ -211,7 +196,8 @@ export function resolveActorTrust(
211
196
  };
212
197
  guardianPrincipalId = guardianDelivery.principalId ?? undefined;
213
198
  isGuardian =
214
- guardianDelivery.address.toLowerCase() === canonicalSenderId.toLowerCase();
199
+ guardianDelivery.address.toLowerCase() ===
200
+ canonicalSenderId.toLowerCase();
215
201
  }
216
202
 
217
203
  log.debug(
@@ -108,6 +108,7 @@ import {
108
108
  type UntrustedContentSource,
109
109
  wrapUntrustedContent,
110
110
  } from "../security/untrusted-content.js";
111
+ import type { CompletedBackgroundTool } from "../tools/background-tool-registry.js";
111
112
  import { getLogger } from "../util/logger.js";
112
113
 
113
114
  const log = getLogger("agent-wake");
@@ -323,6 +324,12 @@ export interface WakeOptions {
323
324
  * framing outside the fence.
324
325
  */
325
326
  untrustedOutput?: WakeUntrustedOutput;
327
+ /**
328
+ * Structured terminal record for a backgrounded bash/host_bash run, stamped
329
+ * onto the persisted background-event wake so the web can rebuild the inline
330
+ * card from history after a daemon restart.
331
+ */
332
+ backgroundToolCompletion?: CompletedBackgroundTool;
326
333
  /**
327
334
  * Schedule-run id to stamp on the usage rows this wake records. Set when the
328
335
  * wake is triggered by a script-mode schedule (the firing's run id), so the
@@ -612,6 +619,18 @@ export async function wakeAgentForOpportunity(
612
619
  const startedAt = nowFn();
613
620
 
614
621
  return runSingleFlight(conversationId, async () => {
622
+ // Snapshot the conversation's resting trust before the resolver runs, so
623
+ // it can be restored after. The resolver leaves the wake's trust on the
624
+ // conversation, and a following no-trust wake would otherwise read it via
625
+ // tool setup's `currentTurnTrustContext ?? trustContext` fallback. Null
626
+ // when the conversation isn't resident yet (a fresh hydrate or a fork).
627
+ let priorPersistentTrust: TrustContext | null = null;
628
+ if (opts.trustContext) {
629
+ const { findConversation } =
630
+ await import("../daemon/conversation-registry.js");
631
+ priorPersistentTrust =
632
+ findConversation(conversationId)?.trustContext ?? null;
633
+ }
615
634
  const resolved = await resolveTarget(opts);
616
635
  if (resolved === "archived") {
617
636
  log.info(
@@ -633,6 +652,18 @@ export async function wakeAgentForOpportunity(
633
652
  }
634
653
  const conversation = resolved;
635
654
 
655
+ // Put the resting trust back on exit. The guard restores only our own
656
+ // elevation, so a trust re-set by another turn is left alone; it's also
657
+ // idempotent, so the several call sites below are safe.
658
+ const restorePersistentWakeTrust = (): void => {
659
+ if (
660
+ opts.trustContext &&
661
+ conversation.trustContext === opts.trustContext
662
+ ) {
663
+ conversation.setTrustContext(priorPersistentTrust);
664
+ }
665
+ };
666
+
636
667
  const { decision: diskPressureDecision, status: diskPressureStatus } =
637
668
  classifyWakeDiskPressurePolicy(opts);
638
669
  if (diskPressureDecision.action === "block") {
@@ -650,6 +681,7 @@ export async function wakeAgentForOpportunity(
650
681
  },
651
682
  "agent-wake: blocked by disk pressure cleanup mode",
652
683
  );
684
+ restorePersistentWakeTrust();
653
685
  return {
654
686
  invoked: false,
655
687
  producedToolCalls: false,
@@ -663,6 +695,7 @@ export async function wakeAgentForOpportunity(
663
695
  { conversationId, source },
664
696
  "agent-wake: conversation still processing after timeout; skipping",
665
697
  );
698
+ restorePersistentWakeTrust();
666
699
  return { invoked: false, producedToolCalls: false, reason: "timeout" };
667
700
  }
668
701
 
@@ -784,7 +817,12 @@ export async function wakeAgentForOpportunity(
784
817
  };
785
818
  conversation.messages.push(triggerMessage);
786
819
  try {
787
- await persistWakeTriggerMessage(conversation, triggerMessage, source);
820
+ await persistWakeTriggerMessage(
821
+ conversation,
822
+ triggerMessage,
823
+ source,
824
+ opts.backgroundToolCompletion,
825
+ );
788
826
  } catch (err) {
789
827
  log.warn(
790
828
  { conversationId, source, err },
@@ -1412,6 +1450,8 @@ export async function wakeAgentForOpportunity(
1412
1450
 
1413
1451
  return { invoked: true, producedToolCalls };
1414
1452
  } finally {
1453
+ // Put the conversation's resting trust back on every exit path.
1454
+ restorePersistentWakeTrust();
1415
1455
  // The success path (above) already called setProcessing(false)
1416
1456
  // + drainQueue after tail persist. This catch-all handles the
1417
1457
  // error and early-return paths where no tail was produced — those