@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
@@ -70,21 +70,38 @@ export type ApprovalCardBlock = z.infer<typeof ApprovalCardBlockSchema>;
70
70
 
71
71
  // ── Public types ────────────────────────────────────────────────────────────
72
72
 
73
+ /** A card action option resolved from the request's domain (id + label + style). */
74
+ export interface ApprovalCardActionOption {
75
+ /** Canonical action id — becomes the `apr:<requestId>:<id>` callback. */
76
+ id: string;
77
+ label: string;
78
+ style?: SurfaceAction["style"];
79
+ }
80
+
73
81
  export interface ApprovalCardParams {
74
82
  /** Prefix for `surfaceId` — combined with `requestId` to form e.g. "access-request-abc123". */
75
83
  surfaceIdPrefix: string;
76
84
  /** Top-level card title shown in the surface header (e.g. "Access Request", "Tool approval"). */
77
85
  cardTitle: string;
78
- /** Primary line inside the card (typically the requester's display name). */
79
- requesterName: string;
80
- /** Secondary line below the requester name (e.g. "Requesting access to the assistant"). */
86
+ /**
87
+ * Primary line inside the card — the subject of the decision: the
88
+ * requester's display name for access requests, the tool name for tool
89
+ * approvals.
90
+ */
91
+ primaryLine: string;
92
+ /** Secondary line below the primary line (e.g. "Requesting access to the assistant"). */
81
93
  subtitle: string;
82
94
  /** Markdown body — blockquotes, warnings, links, etc. */
83
95
  body: string;
84
96
  /** Key-value metadata grid rendered below the body. */
85
97
  metadata: Array<{ label: string; value: string }>;
86
- /** When present, Approve/Reject buttons are wired with `apr:<requestId>:*` action IDs. */
98
+ /** When present, action buttons are wired with `apr:<requestId>:*` action IDs. */
87
99
  requestId?: string;
100
+ /**
101
+ * Domain-specific action set (e.g. the signal-driven introduction-card
102
+ * actions). When absent, the generic Approve/Reject pair is rendered.
103
+ */
104
+ actions?: ApprovalCardActionOption[];
88
105
  /** Plain-text content for the fallback `text` block. */
89
106
  fallbackText: string;
90
107
  }
@@ -105,7 +122,7 @@ export function buildApprovalCardBlocks(
105
122
  const {
106
123
  surfaceIdPrefix,
107
124
  cardTitle,
108
- requesterName,
125
+ primaryLine,
109
126
  subtitle,
110
127
  body,
111
128
  metadata,
@@ -113,23 +130,21 @@ export function buildApprovalCardBlocks(
113
130
  fallbackText,
114
131
  } = params;
115
132
 
133
+ const actionOptions: ApprovalCardActionOption[] = params.actions ?? [
134
+ { id: "approve_once", label: "Approve", style: "primary" },
135
+ { id: "reject", label: "Reject", style: "destructive" },
136
+ ];
137
+
116
138
  const actions: SurfaceAction[] | undefined = requestId
117
- ? [
118
- {
119
- id: `apr:${requestId}:approve_once`,
120
- label: "Approve",
121
- style: "primary",
122
- },
123
- {
124
- id: `apr:${requestId}:reject`,
125
- label: "Reject",
126
- style: "destructive",
127
- },
128
- ]
139
+ ? actionOptions.map((option) => ({
140
+ id: `apr:${requestId}:${option.id}`,
141
+ label: option.label,
142
+ ...(option.style ? { style: option.style } : {}),
143
+ }))
129
144
  : undefined;
130
145
 
131
146
  const data: CardSurfaceData = {
132
- title: requesterName,
147
+ title: primaryLine,
133
148
  subtitle,
134
149
  body,
135
150
  metadata,
@@ -16,9 +16,11 @@
16
16
  import {
17
17
  buildAccessRequestCardView,
18
18
  buildAccessRequestContractText,
19
+ buildIntroductionActionsForPayload,
19
20
  parseAccessRequestPayload,
20
21
  } from "./access-request-copy.js";
21
22
  import {
23
+ type ApprovalCardActionOption,
22
24
  type ApprovalCardBlock,
23
25
  type ApprovalCardParams,
24
26
  buildApprovalCardBlocks,
@@ -97,7 +99,18 @@ export type ApprovalCardData = AccessRequestCardData | ToolApprovalCardData;
97
99
  function resolveAccessRequestCard(
98
100
  payload: Record<string, unknown>,
99
101
  ): ApprovalCardParams {
100
- const view = buildAccessRequestCardView(parseAccessRequestPayload(payload));
102
+ const parsed = parseAccessRequestPayload(payload);
103
+ const view = buildAccessRequestCardView(parsed);
104
+
105
+ // Signal-driven introduction actions; the emphasis (primary lead,
106
+ // destructive Block) is resolved by introduction-policy and maps 1:1 onto
107
+ // the Surface style vocabulary.
108
+ const actions: ApprovalCardActionOption[] =
109
+ buildIntroductionActionsForPayload(parsed).map((action) => ({
110
+ id: action.id,
111
+ label: action.label,
112
+ style: action.emphasis,
113
+ }));
101
114
 
102
115
  const metadata: Array<{ label: string; value: string }> = [];
103
116
 
@@ -139,11 +152,12 @@ function resolveAccessRequestCard(
139
152
  return {
140
153
  surfaceIdPrefix: ACCESS_REQUEST_SURFACE_PREFIX,
141
154
  cardTitle: "Access Request",
142
- requesterName: view.displayName,
155
+ primaryLine: view.displayName,
143
156
  subtitle: "Requesting access to the assistant",
144
157
  body,
145
158
  metadata,
146
159
  requestId: view.requestId,
160
+ actions,
147
161
  fallbackText: buildAccessRequestContractText(payload),
148
162
  };
149
163
  }
@@ -170,14 +184,22 @@ function isLenientToolApproval(payload: LenientToolApprovalPayload): boolean {
170
184
  payload.requestKind,
171
185
  payload.toolName,
172
186
  );
173
- if (!modeResolution) return false;
187
+ if (!modeResolution) {
188
+ return false;
189
+ }
174
190
  return (
175
191
  modeResolution.mode === "approval" &&
176
192
  payload.requestKind !== "access_request"
177
193
  );
178
194
  }
179
195
 
180
- /** Shape a tool-approval/grant payload (strict or lenient) into card params. */
196
+ /**
197
+ * Shape a tool-approval/grant payload (strict or lenient) into card params.
198
+ *
199
+ * The card is about the tool: the primary line names the tool awaiting
200
+ * approval. The requester appears only as metadata context, never as the
201
+ * subject of the decision.
202
+ */
181
203
  function extractToolApprovalCard(
182
204
  p: GuardianQuestionPayload | LenientToolApprovalPayload,
183
205
  ): ApprovalCardParams {
@@ -186,12 +208,16 @@ function extractToolApprovalCard(
186
208
  const rawRequester = nonEmpty(p.requesterIdentifier);
187
209
  const requester = rawRequester
188
210
  ? sanitizeIdentityField(rawRequester)
189
- : "Someone";
211
+ : undefined;
190
212
 
191
213
  const isGrant = p.requestKind === "tool_grant_request";
192
214
 
215
+ // Who is asking is a fact the guardian weighs, so the row always renders:
216
+ // an unresolvable requester surfaces as "Unknown" rather than a silently
217
+ // missing row. In practice the producers always carry at least the raw
218
+ // channel user ID — this placeholder covers defensive/lenient parses.
193
219
  const metadata: Array<{ label: string; value: string }> = [];
194
- metadata.push({ label: "Tool", value: toolName });
220
+ metadata.push({ label: "Requested by", value: requester ?? "Unknown" });
195
221
  const sourceChannel = nonEmpty(p.sourceChannel);
196
222
  if (sourceChannel) {
197
223
  metadata.push({ label: "Source", value: sourceChannel });
@@ -203,7 +229,8 @@ function extractToolApprovalCard(
203
229
 
204
230
  // Fallback text with request-code instructions for older clients.
205
231
  const baseFallback =
206
- p.questionText ?? `${requester} is requesting approval to use ${toolName}`;
232
+ p.questionText ??
233
+ `Approve tool: ${toolName} (requested by ${requester ?? "Unknown"})`;
207
234
  let fallbackText = baseFallback;
208
235
  const requestCode = nonEmpty(p.requestCode);
209
236
  if (requestCode) {
@@ -222,10 +249,8 @@ function extractToolApprovalCard(
222
249
  return {
223
250
  surfaceIdPrefix: TOOL_APPROVAL_SURFACE_PREFIX,
224
251
  cardTitle: isGrant ? "Tool Grant Request" : "Tool Approval",
225
- requesterName: requester,
226
- subtitle: isGrant
227
- ? "Requesting permission to use this tool"
228
- : "Requesting approval to run this tool",
252
+ primaryLine: toolName,
253
+ subtitle: "Requires your approval to run",
229
254
  body,
230
255
  metadata,
231
256
  requestId: nonEmpty(p.requestId),
@@ -246,13 +271,19 @@ function resolveToolApprovalCard(
246
271
  ): ApprovalCardParams | null {
247
272
  const strict = parseGuardianQuestionPayload(payload);
248
273
  if (strict) {
249
- if (!isToolApprovalPayload(strict)) return null;
274
+ if (!isToolApprovalPayload(strict)) {
275
+ return null;
276
+ }
250
277
  return extractToolApprovalCard(strict);
251
278
  }
252
279
 
253
280
  const lenient = LenientToolApprovalPayloadSchema.safeParse(payload);
254
- if (!lenient.success) return null;
255
- if (!isLenientToolApproval(lenient.data)) return null;
281
+ if (!lenient.success) {
282
+ return null;
283
+ }
284
+ if (!isLenientToolApproval(lenient.data)) {
285
+ return null;
286
+ }
256
287
  return extractToolApprovalCard(lenient.data);
257
288
  }
258
289
 
@@ -268,7 +299,9 @@ export function resolveApprovalCardData(
268
299
  sourceEventName: string,
269
300
  contextPayload: Record<string, unknown> | undefined,
270
301
  ): ApprovalCardData | null {
271
- if (!contextPayload) return null;
302
+ if (!contextPayload) {
303
+ return null;
304
+ }
272
305
 
273
306
  if (sourceEventName === "ingress.access_request") {
274
307
  return {
@@ -17,6 +17,7 @@ import type { ApprovalUIMetadata } from "../runtime/channel-approval-types.js";
17
17
  import { getLogger } from "../util/logger.js";
18
18
  import {
19
19
  buildAccessRequestContractText,
20
+ buildIntroductionActionsForPayload,
20
21
  parseAccessRequestPayload,
21
22
  } from "./access-request-copy.js";
22
23
  import { isGuardianSensitiveEvent } from "./adapters/macos.js";
@@ -28,10 +29,7 @@ import {
28
29
  updateDeliveryStatus,
29
30
  } from "./deliveries-store.js";
30
31
  import { resolveDestinations } from "./destination-resolver.js";
31
- import {
32
- parseGuardianQuestionPayload,
33
- resolveGuardianInstructionModeFromPayload,
34
- } from "./guardian-question-mode.js";
32
+ import { parseInteractiveApprovalPayload } from "./guardian-question-mode.js";
35
33
  import { nonEmpty } from "./notification-utils.js";
36
34
  import type { NotificationSignal } from "./signal.js";
37
35
  import type {
@@ -59,27 +57,32 @@ function resolveApprovalContext(
59
57
  signal: NotificationSignal,
60
58
  ): ApprovalUIMetadata | undefined {
61
59
  const payload = signal.contextPayload;
62
- if (!payload) return undefined;
60
+ if (!payload) {
61
+ return undefined;
62
+ }
63
63
 
64
64
  if (signal.sourceEventName === "ingress.access_request") {
65
65
  const requestId = nonEmpty(
66
66
  typeof payload.requestId === "string" ? payload.requestId : undefined,
67
67
  );
68
- if (!requestId) return undefined;
68
+ if (!requestId) {
69
+ return undefined;
70
+ }
69
71
  return {
70
72
  requestId,
71
- actions: APPROVAL_ACTIONS,
73
+ actions: buildIntroductionActionsForPayload(
74
+ parseAccessRequestPayload(payload),
75
+ ),
72
76
  plainTextFallback: buildAccessRequestContractText(payload),
73
77
  };
74
78
  }
75
79
 
76
80
  if (signal.sourceEventName === "guardian.question") {
77
- const parsed = parseGuardianQuestionPayload(payload);
78
- if (!parsed) return undefined;
79
- const { mode } = resolveGuardianInstructionModeFromPayload(parsed);
80
- if (mode !== "approval") return undefined;
81
- const requestId = nonEmpty(parsed.requestId);
82
- if (!requestId) return undefined;
81
+ const parsed = parseInteractiveApprovalPayload(payload);
82
+ if (!parsed) {
83
+ return undefined;
84
+ }
85
+ const requestId = parsed.requestId;
83
86
 
84
87
  // Extract tool context so channel adapters can render structured
85
88
  // approval cards without re-parsing contextPayload.
@@ -184,8 +187,12 @@ export class NotificationBroadcaster {
184
187
  // event fires immediately, before slower channel sends (e.g. Telegram 30s
185
188
  // timeout) can delay it past the macOS deep-link retry window.
186
189
  const orderedChannels = [...decision.selectedChannels].sort((a, b) => {
187
- if (a === "vellum") return -1;
188
- if (b === "vellum") return 1;
190
+ if (a === "vellum") {
191
+ return -1;
192
+ }
193
+ if (b === "vellum") {
194
+ return 1;
195
+ }
189
196
  return 0;
190
197
  });
191
198
 
@@ -552,7 +559,9 @@ export class NotificationBroadcaster {
552
559
  function resolveSourceConversationId(
553
560
  sourceContextId: string | undefined,
554
561
  ): string | undefined {
555
- if (!sourceContextId) return undefined;
562
+ if (!sourceContextId) {
563
+ return undefined;
564
+ }
556
565
  try {
557
566
  return getConversation(sourceContextId) ? sourceContextId : undefined;
558
567
  } catch {
@@ -32,6 +32,8 @@ import {
32
32
  buildAccessRequestInviteDirective,
33
33
  hasAccessRequestInstructions,
34
34
  hasInviteFlowDirective,
35
+ isHandshakeOfferedForPayload,
36
+ parseAccessRequestPayload,
35
37
  } from "./access-request-copy.js";
36
38
  import {
37
39
  buildAccessRequestSeedContentBlocks,
@@ -47,8 +49,10 @@ import { createDecision } from "./decisions-store.js";
47
49
  import {
48
50
  buildGuardianRequestCodeInstruction,
49
51
  hasGuardianRequestCodeInstruction,
52
+ parseInteractiveApprovalPayload,
50
53
  resolveGuardianQuestionInstructionMode,
51
54
  stripConflictingGuardianRequestInstructions,
55
+ stripGuardianRequestCodeInstructions,
52
56
  } from "./guardian-question-mode.js";
53
57
  import { nonEmpty, readPayloadString } from "./notification-utils.js";
54
58
  import { getPreferenceSummary } from "./preference-summary.js";
@@ -143,6 +147,7 @@ function buildSystemPrompt(
143
147
  ` - Avoid meta-send phrasing (e.g. "I'd like to send a notification", "May I go ahead with that?"). Write the recipient-facing message directly.`,
144
148
  ` - Avoid intermediary-instruction phrasing like "consider telling the guardian", "ask the recipient to", or "the assistant should remind them". Rewrite it as final copy the recipient can act on directly.`,
145
149
  ` - For telegram: 1-2 concise sentences.`,
150
+ ` - For slack approval requests: the message renders inside an interactive card with Approve/Reject buttons. Describe only what needs approval — never include approval/reference codes, code-reply instructions, or directions to respond in another app.`,
146
151
  `- \`conversationSeedMessage\` is the opening message in the internal notification conversation and also the expanded detail shown in the home feed. It should be richer and more contextual than the popup body.`,
147
152
  ` - For vellum (desktop): use structured markdown for readability. Break content into bullet points, numbered lists, or short sections with **bold** labels. Avoid long unbroken paragraphs — scan-friendly formatting is preferred.`,
148
153
  ` - Never dump raw JSON. Include only human-readable context.`,
@@ -382,11 +387,19 @@ function validateDecisionOutput(
382
387
  availableChannels: NotificationChannel[],
383
388
  candidateSet?: ConversationCandidateSet,
384
389
  ): NotificationDecision | null {
385
- if (typeof input.shouldNotify !== "boolean") return null;
386
- if (typeof input.reasoningSummary !== "string") return null;
387
- if (typeof input.dedupeKey !== "string") return null;
390
+ if (typeof input.shouldNotify !== "boolean") {
391
+ return null;
392
+ }
393
+ if (typeof input.reasoningSummary !== "string") {
394
+ return null;
395
+ }
396
+ if (typeof input.dedupeKey !== "string") {
397
+ return null;
398
+ }
388
399
 
389
- if (!Array.isArray(input.selectedChannels)) return null;
400
+ if (!Array.isArray(input.selectedChannels)) {
401
+ return null;
402
+ }
390
403
  const validatedChannels = (input.selectedChannels as unknown[]).filter(
391
404
  (ch): ch is NotificationChannel =>
392
405
  typeof ch === "string" &&
@@ -482,7 +495,9 @@ export function validateConversationActions(
482
495
  ): Partial<Record<NotificationChannel, ConversationAction>> {
483
496
  const result: Partial<Record<NotificationChannel, ConversationAction>> = {};
484
497
 
485
- if (!raw || typeof raw !== "object") return result;
498
+ if (!raw || typeof raw !== "object") {
499
+ return result;
500
+ }
486
501
 
487
502
  const actionsObj = raw as Record<string, unknown>;
488
503
  const channelSet = new Set(validChannels);
@@ -502,8 +517,12 @@ export function validateConversationActions(
502
517
  }
503
518
 
504
519
  for (const [ch, actionRaw] of Object.entries(actionsObj)) {
505
- if (!channelSet.has(ch as NotificationChannel)) continue;
506
- if (!actionRaw || typeof actionRaw !== "object") continue;
520
+ if (!channelSet.has(ch as NotificationChannel)) {
521
+ continue;
522
+ }
523
+ if (!actionRaw || typeof actionRaw !== "object") {
524
+ continue;
525
+ }
507
526
 
508
527
  const channel = ch as NotificationChannel;
509
528
  const action = actionRaw as Record<string, unknown>;
@@ -558,8 +577,9 @@ function ensureGuardianRequestCodeInCopy(
558
577
  requestCode,
559
578
  mode,
560
579
  );
561
- if (hasGuardianRequestCodeInstruction(sanitized, requestCode, mode))
580
+ if (hasGuardianRequestCodeInstruction(sanitized, requestCode, mode)) {
562
581
  return sanitized;
582
+ }
563
583
  return sanitized.length > 0
564
584
  ? `${sanitized}\n\n${instruction}`
565
585
  : instruction;
@@ -577,36 +597,79 @@ function ensureGuardianRequestCodeInCopy(
577
597
  };
578
598
  }
579
599
 
600
+ /**
601
+ * Remove request-code reply instructions from copy for a channel whose
602
+ * approval UI makes them redundant. Instruction-only fields keep their
603
+ * original text rather than becoming empty — downstream treats an empty
604
+ * body as missing copy.
605
+ */
606
+ function stripGuardianRequestCodeInCopy(
607
+ copy: RenderedChannelCopy,
608
+ requestCode: string,
609
+ ): RenderedChannelCopy {
610
+ const strip = (text: string): string => {
611
+ const stripped = stripGuardianRequestCodeInstructions(text, requestCode);
612
+ return stripped.length > 0 ? stripped : text;
613
+ };
614
+
615
+ return {
616
+ ...copy,
617
+ body: strip(copy.body),
618
+ deliveryText: copy.deliveryText
619
+ ? strip(copy.deliveryText)
620
+ : copy.deliveryText,
621
+ conversationSeedMessage: copy.conversationSeedMessage
622
+ ? strip(copy.conversationSeedMessage)
623
+ : copy.conversationSeedMessage,
624
+ };
625
+ }
626
+
580
627
  /**
581
628
  * Guardian questions that share a conversation require explicit request-code
582
629
  * targeting. Enforce request-code instructions in rendered copy so guardians
583
630
  * can always disambiguate replies even when model copy omits them.
631
+ *
632
+ * Slack is the exception for approval-mode questions: the Slack adapter
633
+ * renders those as an interactive card with Approve/Reject buttons (see
634
+ * `resolveApprovalContext` in broadcaster.ts, gated on the same
635
+ * `parseInteractiveApprovalPayload`), so code-reply instructions are
636
+ * redundant noise there and are stripped instead of enforced in.
584
637
  */
585
638
  function enforceGuardianRequestCode(
586
639
  decision: NotificationDecision,
587
640
  signal: NotificationSignal,
588
641
  ): NotificationDecision {
589
- if (signal.sourceEventName !== "guardian.question") return decision;
642
+ if (signal.sourceEventName !== "guardian.question") {
643
+ return decision;
644
+ }
590
645
  const rawCode = signal.contextPayload.requestCode;
591
- if (typeof rawCode !== "string" || rawCode.trim().length === 0)
646
+ if (typeof rawCode !== "string" || rawCode.trim().length === 0) {
592
647
  return decision;
648
+ }
593
649
 
594
650
  const requestCode = rawCode.trim().toUpperCase();
595
651
  const modeResolution = resolveGuardianQuestionInstructionMode(
596
652
  signal.contextPayload,
597
653
  );
654
+ const slackRendersApprovalButtons =
655
+ parseInteractiveApprovalPayload(signal.contextPayload) != null;
598
656
  const nextCopy: Partial<Record<NotificationChannel, RenderedChannelCopy>> = {
599
657
  ...decision.renderedCopy,
600
658
  };
601
659
 
602
660
  for (const channel of Object.keys(nextCopy) as NotificationChannel[]) {
603
661
  const copy = nextCopy[channel];
604
- if (!copy) continue;
605
- nextCopy[channel] = ensureGuardianRequestCodeInCopy(
606
- copy,
607
- requestCode,
608
- modeResolution.mode,
609
- );
662
+ if (!copy) {
663
+ continue;
664
+ }
665
+ nextCopy[channel] =
666
+ channel === "slack" && slackRendersApprovalButtons
667
+ ? stripGuardianRequestCodeInCopy(copy, requestCode)
668
+ : ensureGuardianRequestCodeInCopy(
669
+ copy,
670
+ requestCode,
671
+ modeResolution.mode,
672
+ );
610
673
  }
611
674
 
612
675
  return {
@@ -624,14 +687,18 @@ function ensureSeedContentBlocks(
624
687
  decision: NotificationDecision,
625
688
  blocks: unknown[] | null | undefined,
626
689
  ): NotificationDecision {
627
- if (!blocks) return decision;
690
+ if (!blocks) {
691
+ return decision;
692
+ }
628
693
 
629
694
  const nextCopy: Partial<Record<NotificationChannel, RenderedChannelCopy>> = {
630
695
  ...decision.renderedCopy,
631
696
  };
632
697
  for (const channel of Object.keys(nextCopy) as NotificationChannel[]) {
633
698
  const copy = nextCopy[channel];
634
- if (!copy) continue;
699
+ if (!copy) {
700
+ continue;
701
+ }
635
702
  if (!copy.seedContentBlocks) {
636
703
  nextCopy[channel] = { ...copy, seedContentBlocks: blocks };
637
704
  }
@@ -651,7 +718,9 @@ function enforceToolApprovalSeedBlocks(
651
718
  decision: NotificationDecision,
652
719
  signal: NotificationSignal,
653
720
  ): NotificationDecision {
654
- if (signal.sourceEventName !== "guardian.question") return decision;
721
+ if (signal.sourceEventName !== "guardian.question") {
722
+ return decision;
723
+ }
655
724
  return ensureSeedContentBlocks(
656
725
  decision,
657
726
  buildToolApprovalSeedContentBlocks(signal.contextPayload),
@@ -675,7 +744,9 @@ function enforceAccessRequestInstructions(
675
744
  decision: NotificationDecision,
676
745
  signal: NotificationSignal,
677
746
  ): NotificationDecision {
678
- if (signal.sourceEventName !== "ingress.access_request") return decision;
747
+ if (signal.sourceEventName !== "ingress.access_request") {
748
+ return decision;
749
+ }
679
750
 
680
751
  const rawCode = signal.contextPayload.requestCode;
681
752
  const hasRequestCode =
@@ -688,14 +759,20 @@ function enforceAccessRequestInstructions(
688
759
  if (hasRequestCode) {
689
760
  const requestCode = rawCode.trim().toUpperCase();
690
761
  const contractText = buildAccessRequestContractText(signal.contextPayload);
762
+ const handshakeOffered = isHandshakeOfferedForPayload(
763
+ parseAccessRequestPayload(signal.contextPayload),
764
+ );
691
765
 
692
766
  for (const channel of Object.keys(nextCopy) as NotificationChannel[]) {
693
767
  const copy = nextCopy[channel];
694
- if (!copy) continue;
768
+ if (!copy) {
769
+ continue;
770
+ }
695
771
  nextCopy[channel] = ensureAccessRequestInstructionsInCopy(
696
772
  copy,
697
773
  requestCode,
698
774
  contractText,
775
+ handshakeOffered,
699
776
  );
700
777
  }
701
778
  } else {
@@ -704,7 +781,9 @@ function enforceAccessRequestInstructions(
704
781
 
705
782
  for (const channel of Object.keys(nextCopy) as NotificationChannel[]) {
706
783
  const copy = nextCopy[channel];
707
- if (!copy) continue;
784
+ if (!copy) {
785
+ continue;
786
+ }
708
787
  nextCopy[channel] = ensureInviteFlowDirectiveInCopy(
709
788
  copy,
710
789
  inviteDirective,
@@ -730,10 +809,13 @@ function ensureAccessRequestInstructionsInCopy(
730
809
  copy: RenderedChannelCopy,
731
810
  requestCode: string,
732
811
  contractText: string,
812
+ handshakeOffered: boolean,
733
813
  ): RenderedChannelCopy {
734
814
  const ensureText = (text: string | undefined): string => {
735
815
  const base = typeof text === "string" ? text.trim() : "";
736
- if (hasAccessRequestInstructions(base, requestCode)) return base;
816
+ if (hasAccessRequestInstructions(base, requestCode, { handshakeOffered })) {
817
+ return base;
818
+ }
737
819
  return base.length > 0 ? `${base}\n\n${contractText}` : contractText;
738
820
  };
739
821
 
@@ -755,7 +837,9 @@ function ensureInviteFlowDirectiveInCopy(
755
837
  ): RenderedChannelCopy {
756
838
  const ensureText = (text: string | undefined): string => {
757
839
  const base = typeof text === "string" ? text.trim() : "";
758
- if (hasInviteFlowDirective(base)) return base;
840
+ if (hasInviteFlowDirective(base)) {
841
+ return base;
842
+ }
759
843
  return base.length > 0 ? `${base}\n\n${inviteDirective}` : inviteDirective;
760
844
  };
761
845
 
@@ -1138,15 +1222,21 @@ export function enforceRoutingIntent(
1138
1222
  // Preserve the decision's selected channels first, then add connected
1139
1223
  // channels until we reach two channels total.
1140
1224
  for (const ch of selectedConnected) {
1141
- if (seen.has(ch)) continue;
1225
+ if (seen.has(ch)) {
1226
+ continue;
1227
+ }
1142
1228
  expanded.push(ch);
1143
1229
  seen.add(ch);
1144
1230
  }
1145
1231
  for (const ch of connectedChannels) {
1146
- if (seen.has(ch)) continue;
1232
+ if (seen.has(ch)) {
1233
+ continue;
1234
+ }
1147
1235
  expanded.push(ch);
1148
1236
  seen.add(ch);
1149
- if (expanded.length >= 2) break;
1237
+ if (expanded.length >= 2) {
1238
+ break;
1239
+ }
1150
1240
  }
1151
1241
 
1152
1242
  const enforced = { ...decision };
@@ -1184,15 +1274,20 @@ export function enforceGuardianCallConversationAffinity(
1184
1274
  decision: NotificationDecision,
1185
1275
  signal: NotificationSignal,
1186
1276
  ): NotificationDecision {
1187
- if (signal.sourceEventName !== "guardian.question") return decision;
1277
+ if (signal.sourceEventName !== "guardian.question") {
1278
+ return decision;
1279
+ }
1188
1280
 
1189
1281
  const callSessionId = signal.contextPayload?.callSessionId;
1190
- if (typeof callSessionId !== "string" || callSessionId.trim().length === 0)
1282
+ if (typeof callSessionId !== "string" || callSessionId.trim().length === 0) {
1191
1283
  return decision;
1284
+ }
1192
1285
 
1193
1286
  // If an affinity hint already exists for vellum, the second+ dispatch
1194
1287
  // will be handled by enforceConversationAffinity — nothing to do here.
1195
- if (signal.conversationAffinityHint?.vellum) return decision;
1288
+ if (signal.conversationAffinityHint?.vellum) {
1289
+ return decision;
1290
+ }
1196
1291
 
1197
1292
  const enforced = { ...decision };
1198
1293
  const conversationActions: Partial<
@@ -1226,13 +1321,17 @@ function enforceConversationAffinity(
1226
1321
  decision: NotificationDecision,
1227
1322
  affinityHint: Partial<Record<string, string>> | undefined,
1228
1323
  ): NotificationDecision {
1229
- if (!affinityHint) return decision;
1324
+ if (!affinityHint) {
1325
+ return decision;
1326
+ }
1230
1327
 
1231
1328
  const entries = Object.entries(affinityHint).filter(
1232
1329
  ([, conversationId]) =>
1233
1330
  typeof conversationId === "string" && conversationId.length > 0,
1234
1331
  );
1235
- if (entries.length === 0) return decision;
1332
+ if (entries.length === 0) {
1333
+ return decision;
1334
+ }
1236
1335
 
1237
1336
  const enforced = { ...decision };
1238
1337
  const conversationActions: Partial<