@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
@@ -5,12 +5,19 @@ import { stopCes } from "../credential-execution/ces-runtime.js";
5
5
  import { stopHeartbeatService } from "../heartbeat/heartbeat-service.js";
6
6
  import { stopCliIpcServer } from "../ipc/assistant-server.js";
7
7
  import { stopGatewayFlagListener } from "../ipc/gateway-flag-listener.js";
8
- import { stopSkillIpcServer } from "../ipc/skill-server.js";
9
8
  import { stopMcpServerManager } from "../mcp/manager.js";
10
- import { getSqlite, resetDb } from "../persistence/db-connection.js";
9
+ import { stopMonitoring } from "../monitoring/control.js";
10
+ import {
11
+ runAsyncSqlite,
12
+ spawnDetachedWalCheckpoint,
13
+ } from "../persistence/db-async-query.js";
14
+ import { getSqlite, isDbOpen, resetDb } from "../persistence/db-connection.js";
11
15
  import { stopQdrantManager } from "../persistence/embeddings/qdrant-manager.js";
12
16
  import { stopMemoryJobsWorker } from "../persistence/jobs-worker.js";
13
17
  import { stopMemoryWorkerProcess } from "../persistence/worker-control.js";
18
+ import { stopConsentRefresh } from "../platform/consent-cache.js";
19
+ import { HOOKS } from "../plugin-api/constants.js";
20
+ import { runHook } from "../plugins/pipeline.js";
14
21
  import { stopRuntimeHttpServer } from "../runtime/http-server.js";
15
22
  import { stopScheduler } from "../schedule/scheduler.js";
16
23
  import { getSubagentManager } from "../subagent/index.js";
@@ -18,6 +25,7 @@ import { stopUsageTelemetryReporter } from "../telemetry/usage-telemetry-reporte
18
25
  import { browserManager } from "../tools/browser/browser-manager.js";
19
26
  import { cleanupShellOutputTempFiles } from "../tools/shared/shell-output.js";
20
27
  import { getLogger } from "../util/logger.js";
28
+ import { APP_VERSION } from "../version.js";
21
29
  import { getEnrichmentService } from "../workspace/commit-message-enrichment-service.js";
22
30
  import {
23
31
  commitAllPendingWorkspaceChanges,
@@ -27,11 +35,11 @@ import { stopAppSourceWatcher } from "./app-source-watcher.js";
27
35
  import { stopConfigWatcher } from "./config-watcher.js";
28
36
  import { stopConversationEvictor } from "./conversation-evictor.js";
29
37
  import { stopConversations } from "./conversation-store.js";
30
- import { cleanupPidFile } from "./daemon-control.js";
38
+ import { cleanupPidFile, cleanupPidFileIfOwner } from "./daemon-control.js";
39
+ import { isStartupComplete } from "./daemon-readiness.js";
31
40
  import { stopEventLoopWatchdog } from "./event-loop-watchdog.js";
32
41
  import { stopDiskPressureGuardForLifecycle } from "./lifecycle.js";
33
42
  import { stopOrphanReaper } from "./orphan-reaper.js";
34
- import { runShutdownHooks } from "./shutdown-registry.js";
35
43
 
36
44
  const log = getLogger("lifecycle");
37
45
 
@@ -71,6 +79,16 @@ async function shutdown(): Promise<void> {
71
79
  // bump only changes behavior for the stuck-shutdown path.
72
80
  const forceTimer = setTimeout(() => {
73
81
  log.warn("Graceful shutdown timed out, forcing exit");
82
+ // A stuck shutdown may never reach the graceful WAL checkpoint below —
83
+ // fold the WAL from a detached subprocess that survives this exit, so
84
+ // the next boot opens a small WAL instead of paying a multi-minute
85
+ // recovery. Skipped when the DB was never opened: there is nothing to
86
+ // fold, and sqlite3 would create an empty DB file.
87
+ if (isDbOpen() && !spawnDetachedWalCheckpoint()) {
88
+ log.warn(
89
+ "No sqlite3 CLI on host — WAL left at high-water mark on force-exit",
90
+ );
91
+ }
74
92
  stopBackgroundServicesAndCleanupPidFile();
75
93
  process.exit(1);
76
94
  }, 20_000);
@@ -79,13 +97,21 @@ async function shutdown(): Promise<void> {
79
97
  await stopWorkspaceHeartbeatService();
80
98
  await stopHeartbeatService();
81
99
 
82
- // Run registered plugin/skill shutdown hooks (e.g. the memory plugin's
83
- // filing-service teardown, meet-join session teardown) before stopping the
84
- // server so any HTTP round-trips and SSE emissions still have live transports.
100
+ // Stop the periodic consent-cache refresh (a daemon-owned interval).
101
+ await stopConsentRefresh();
102
+
103
+ // Fire plugin / user / workspace / skill `shutdown` hooks through the unified
104
+ // hook pipeline — the same dispatch path every other lifecycle hook uses —
105
+ // before stopping the server so any teardown work still has live transports.
106
+ // We don't unregister tool/route surfaces here: the daemon is exiting, so that
107
+ // in-memory registry state is discarded with the process anyway.
85
108
  try {
86
- await runShutdownHooks("daemon-shutdown");
109
+ await runHook(HOOKS.SHUTDOWN, {
110
+ assistantVersion: APP_VERSION,
111
+ reason: "shutdown",
112
+ });
87
113
  } catch (err) {
88
- log.warn({ err }, "Skill shutdown hooks failed (non-fatal)");
114
+ log.warn({ err }, "Plugin shutdown hooks failed (non-fatal)");
89
115
  }
90
116
 
91
117
  // Commit any uncommitted workspace changes before stopping the server.
@@ -104,7 +130,6 @@ async function shutdown(): Promise<void> {
104
130
  stopConfigWatcher();
105
131
  stopAppSourceWatcher();
106
132
  stopCliIpcServer();
107
- stopSkillIpcServer();
108
133
  stopConversations();
109
134
  await stopCes();
110
135
 
@@ -160,6 +185,9 @@ async function shutdown(): Promise<void> {
160
185
  log.warn({ err }, "Failed to stop memory worker process (non-fatal)");
161
186
  }
162
187
 
188
+ // Stop the resource monitor process if it's actually running.
189
+ stopMonitoring();
190
+
163
191
  try {
164
192
  await stopMcpServerManager();
165
193
  } catch (err) {
@@ -171,21 +199,51 @@ async function shutdown(): Promise<void> {
171
199
  // Optimize query planner statistics before closing so they persist for
172
200
  // the next session. Checkpoint WAL and close SQLite so no writes are
173
201
  // lost on exit. Each step is in its own try block so later steps still
174
- // run if an earlier one throws (e.g. SQLITE_BUSY).
175
- try {
176
- getSqlite().exec("PRAGMA optimize");
177
- } catch (err) {
178
- log.warn({ err }, "PRAGMA optimize at shutdown failed (non-fatal)");
179
- }
180
- try {
181
- getSqlite().exec("PRAGMA wal_checkpoint(TRUNCATE)");
182
- } catch (err) {
183
- log.warn({ err }, "WAL checkpoint failed (non-fatal)");
184
- }
185
- try {
186
- resetDb();
187
- } catch (err) {
188
- log.warn({ err }, "Database close failed (non-fatal)");
202
+ // run if an earlier one throws (e.g. SQLITE_BUSY). Skipped entirely when
203
+ // the DB was never opened: there is nothing to persist, fold, or close,
204
+ // and the lazy `getSqlite()` accessor would open the database just to
205
+ // tear it down.
206
+ if (isDbOpen()) {
207
+ try {
208
+ getSqlite().exec("PRAGMA optimize");
209
+ } catch (err) {
210
+ log.warn({ err }, "PRAGMA optimize at shutdown failed (non-fatal)");
211
+ }
212
+ // Fold the WAL back into the main database so the next boot opens a
213
+ // small WAL instead of paying a multi-minute recovery (see
214
+ // `checkpointWalBeforeOpen` in db-init.ts). Runs through `runAsyncSqlite`
215
+ // (sqlite3 subprocess when available) because a synchronous checkpoint
216
+ // of a WAL at its high-water mark blocks the event loop, which keeps the
217
+ // force-exit timer above from ever firing. Off the loop, the timer stays
218
+ // armed — and if it fires mid-checkpoint, the subprocess survives
219
+ // `process.exit` and finishes the fold in the background.
220
+ try {
221
+ const checkpointResult = await runAsyncSqlite(
222
+ "PRAGMA wal_checkpoint(TRUNCATE)",
223
+ "shutdown:wal-checkpoint-truncate",
224
+ );
225
+ if (checkpointResult.ok) {
226
+ log.info(
227
+ {
228
+ backend: checkpointResult.backend,
229
+ elapsedMs: checkpointResult.elapsedMs,
230
+ },
231
+ "Shutdown WAL checkpoint complete",
232
+ );
233
+ } else {
234
+ log.warn(
235
+ { error: checkpointResult.error, backend: checkpointResult.backend },
236
+ "WAL checkpoint failed (non-fatal)",
237
+ );
238
+ }
239
+ } catch (err) {
240
+ log.warn({ err }, "WAL checkpoint failed (non-fatal)");
241
+ }
242
+ try {
243
+ resetDb();
244
+ } catch (err) {
245
+ log.warn({ err }, "Database close failed (non-fatal)");
246
+ }
189
247
  }
190
248
 
191
249
  await Sentry.flush(2000);
@@ -194,45 +252,79 @@ async function shutdown(): Promise<void> {
194
252
  process.exit(exitCode);
195
253
  }
196
254
 
255
+ /**
256
+ * Minimal exit for signals and fatal errors that land before startup
257
+ * completes. The graceful `shutdown()` path assumes started subsystems and
258
+ * would interleave its teardown with the still-running startup sequence, so
259
+ * this path does only the work that matters mid-boot: fold the WAL when the
260
+ * DB is already open (detached, so the fold survives the exit) and remove
261
+ * the PID file when this process wrote it — ownership-checked so a duplicate
262
+ * daemon exiting early never deletes the live daemon's PID file.
263
+ */
264
+ function exitDuringStartup(code: number): void {
265
+ if (isDbOpen()) {
266
+ spawnDetachedWalCheckpoint();
267
+ }
268
+ cleanupPidFileIfOwner(process.pid);
269
+ process.exit(code);
270
+ }
271
+
272
+ function handleShutdownSignal(signal: string, message: string): void {
273
+ log.warn({ signal, pid: process.pid, uptime: process.uptime() }, message);
274
+ if (!isStartupComplete()) {
275
+ log.warn(
276
+ { signal },
277
+ "Signal arrived before startup completed — exiting without graceful shutdown",
278
+ );
279
+ exitDuringStartup(0);
280
+ return;
281
+ }
282
+ void shutdown();
283
+ }
284
+
285
+ function handleFatalError(err: unknown, message: string): void {
286
+ log.error({ err }, message);
287
+ Sentry.captureException(err);
288
+ exitCode = 1;
289
+ if (!isStartupComplete()) {
290
+ exitDuringStartup(1);
291
+ return;
292
+ }
293
+ void shutdown();
294
+ }
295
+
296
+ /**
297
+ * Install signal and fatal-error handlers. Runs before any blocking startup
298
+ * work — a boot that inherits a large WAL can spend minutes in DB init, and
299
+ * without handlers a SIGTERM in that window is the default hard kill (no WAL
300
+ * fold, no PID cleanup). Handlers stay in the minimal `exitDuringStartup`
301
+ * mode until `setStartupComplete()` marks the daemon ready; from then on
302
+ * signals run the full graceful `shutdown()`.
303
+ */
197
304
  export function installShutdownHandlers(): void {
198
305
  process.on("SIGTERM", () => {
199
- log.warn(
200
- { signal: "SIGTERM", pid: process.pid, uptime: process.uptime() },
306
+ handleShutdownSignal(
307
+ "SIGTERM",
201
308
  "Received SIGTERM — process termination requested",
202
309
  );
203
- void shutdown();
204
310
  });
205
311
 
206
312
  process.on("SIGINT", () => {
207
- log.warn(
208
- { signal: "SIGINT", pid: process.pid, uptime: process.uptime() },
209
- "Received SIGINT — user interrupt",
210
- );
211
- void shutdown();
313
+ handleShutdownSignal("SIGINT", "Received SIGINT — user interrupt");
212
314
  });
213
315
 
214
316
  process.on("SIGHUP", () => {
215
- log.warn(
216
- { signal: "SIGHUP", pid: process.pid, uptime: process.uptime() },
217
- "Received SIGHUP — terminal hangup",
218
- );
219
- void shutdown();
317
+ handleShutdownSignal("SIGHUP", "Received SIGHUP — terminal hangup");
220
318
  });
221
319
 
222
320
  process.on("unhandledRejection", (reason) => {
223
- log.error(
224
- { err: reason },
321
+ handleFatalError(
322
+ reason,
225
323
  "Unhandled promise rejection — initiating shutdown",
226
324
  );
227
- Sentry.captureException(reason);
228
- exitCode = 1;
229
- void shutdown();
230
325
  });
231
326
 
232
327
  process.on("uncaughtException", (err) => {
233
- log.error({ err }, "Uncaught exception — initiating shutdown");
234
- Sentry.captureException(err);
235
- exitCode = 1;
236
- void shutdown();
328
+ handleFatalError(err, "Uncaught exception — initiating shutdown");
237
329
  });
238
330
  }
@@ -4,11 +4,11 @@ import {
4
4
  seedSkillGraphNodes,
5
5
  seedUninstalledCatalogSkillMemories,
6
6
  } from "../plugins/defaults/memory/graph/capability-seed.js";
7
- import { getLogger } from "../util/logger.js";
8
7
  import {
9
8
  maybeSeedMemoryV2CliCommands,
10
9
  maybeSeedMemoryV2Skills,
11
- } from "./memory-v2-startup.js";
10
+ } from "../plugins/defaults/memory/v2/memory-v2-startup.js";
11
+ import { getLogger } from "../util/logger.js";
12
12
 
13
13
  const log = getLogger("skill-memory-refresh");
14
14
 
@@ -0,0 +1,217 @@
1
+ /**
2
+ * Tests for the SQLite corruption watchdog.
3
+ *
4
+ * The watchdog turns a `SQLITE_CORRUPT` / `SQLITE_NOTADB` error thrown by any
5
+ * statement into a direct `sqlite_corrupted` telemetry emit (bypassing the
6
+ * SQLite watchdog buffer). It must ignore non-corruption errors, debounce per
7
+ * database, and — wired straight into the slow-query wrapper — fire the moment
8
+ * a wrapped connection hits corruption. The direct emit itself (opt-out gate,
9
+ * POST payload) is covered in `../telemetry/watchdog-direct-emit.test.ts`.
10
+ */
11
+ import { mkdtempSync, writeFileSync } from "node:fs";
12
+ import { tmpdir } from "node:os";
13
+ import { join } from "node:path";
14
+ import { Database } from "bun:sqlite";
15
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
16
+
17
+ // Silence the logger.
18
+ mock.module("../util/logger.js", () => ({
19
+ getLogger: () =>
20
+ new Proxy({} as Record<string, unknown>, { get: () => () => {} }),
21
+ }));
22
+
23
+ // Sentry is a side-effecting no-op in tests.
24
+ mock.module("@sentry/node", () => ({
25
+ withScope: (fn: (scope: unknown) => void) =>
26
+ fn({ setLevel() {}, setTag() {}, setContext() {} }),
27
+ captureMessage: () => {},
28
+ }));
29
+
30
+ // Capture direct emits instead of POSTing them.
31
+ const emitCalls: Array<{ checkName: string; detail: Record<string, unknown> }> =
32
+ [];
33
+ mock.module("../telemetry/watchdog-direct-emit.js", () => ({
34
+ emitWatchdogEventDirect: (
35
+ checkName: string,
36
+ detail: Record<string, unknown>,
37
+ ) => {
38
+ emitCalls.push({ checkName, detail });
39
+ return Promise.resolve();
40
+ },
41
+ }));
42
+
43
+ import { wrapSqliteForSlowQueryLogging } from "../persistence/slow-query-log.js";
44
+ import {
45
+ flushCorruptionEmitsForTesting,
46
+ isSqliteCorruptionError,
47
+ observeSqliteStatementError,
48
+ resetSqliteCorruptionWatchdogForTesting,
49
+ SQLITE_CORRUPTED_CHECK_NAME,
50
+ } from "./sqlite-corruption-watchdog.js";
51
+
52
+ /** A synthetic `bun:sqlite`-style error carrying a `code`. */
53
+ function sqliteError(code: string, message: string): Error {
54
+ return Object.assign(new Error(message), { code });
55
+ }
56
+
57
+ describe("isSqliteCorruptionError", () => {
58
+ test("matches the SQLITE_CORRUPT / SQLITE_NOTADB families by code", () => {
59
+ expect(
60
+ isSqliteCorruptionError(
61
+ sqliteError("SQLITE_CORRUPT", "database disk image is malformed"),
62
+ ),
63
+ ).toBe(true);
64
+ expect(
65
+ isSqliteCorruptionError(
66
+ sqliteError("SQLITE_CORRUPT_VTAB", "corrupt virtual table"),
67
+ ),
68
+ ).toBe(true);
69
+ expect(
70
+ isSqliteCorruptionError(
71
+ sqliteError("SQLITE_NOTADB", "file is not a database"),
72
+ ),
73
+ ).toBe(true);
74
+ });
75
+
76
+ test("matches the canonical corruption messages even without a code", () => {
77
+ expect(
78
+ isSqliteCorruptionError(new Error("database disk image is malformed")),
79
+ ).toBe(true);
80
+ expect(isSqliteCorruptionError(new Error("file is not a database"))).toBe(
81
+ true,
82
+ );
83
+ });
84
+
85
+ test("does not match transient contention or unrelated errors", () => {
86
+ expect(
87
+ isSqliteCorruptionError(sqliteError("SQLITE_BUSY", "database is locked")),
88
+ ).toBe(false);
89
+ expect(
90
+ isSqliteCorruptionError(sqliteError("SQLITE_IOERR", "disk I/O error")),
91
+ ).toBe(false);
92
+ expect(
93
+ isSqliteCorruptionError(
94
+ sqliteError("SQLITE_CONSTRAINT", "UNIQUE constraint failed"),
95
+ ),
96
+ ).toBe(false);
97
+ expect(isSqliteCorruptionError(new Error("no such table: foo"))).toBe(
98
+ false,
99
+ );
100
+ });
101
+ });
102
+
103
+ describe("observeSqliteStatementError", () => {
104
+ beforeEach(() => {
105
+ emitCalls.length = 0;
106
+ resetSqliteCorruptionWatchdogForTesting();
107
+ });
108
+
109
+ test("a corruption error emits sqlite_corrupted with detail", async () => {
110
+ observeSqliteStatementError(
111
+ sqliteError("SQLITE_CORRUPT", "database disk image is malformed"),
112
+ {
113
+ sql: "SELECT * FROM messages WHERE id = ?",
114
+ database: "main",
115
+ label: "load-history",
116
+ },
117
+ );
118
+ await flushCorruptionEmitsForTesting();
119
+
120
+ expect(emitCalls).toHaveLength(1);
121
+ expect(emitCalls[0].checkName).toBe(SQLITE_CORRUPTED_CHECK_NAME);
122
+ expect(emitCalls[0].checkName).toBe("sqlite_corrupted");
123
+ expect(emitCalls[0].detail).toMatchObject({
124
+ database: "main",
125
+ error: "database disk image is malformed",
126
+ sql: "SELECT * FROM messages WHERE id = ?",
127
+ label: "load-history",
128
+ });
129
+ });
130
+
131
+ test('falls back to "unknown" when the connection carried no database tag', async () => {
132
+ observeSqliteStatementError(new Error("file is not a database"), {
133
+ sql: "SELECT 1",
134
+ });
135
+ await flushCorruptionEmitsForTesting();
136
+
137
+ expect(emitCalls).toHaveLength(1);
138
+ expect(emitCalls[0].detail.database).toBe("unknown");
139
+ });
140
+
141
+ test("a transient / unrelated error emits nothing", async () => {
142
+ observeSqliteStatementError(sqliteError("SQLITE_BUSY", "locked"), {
143
+ sql: "INSERT ...",
144
+ database: "main",
145
+ });
146
+ observeSqliteStatementError(
147
+ sqliteError("SQLITE_CONSTRAINT", "UNIQUE constraint failed"),
148
+ { sql: "INSERT ...", database: "main" },
149
+ );
150
+ observeSqliteStatementError(new Error("no such table: foo"), {
151
+ sql: "SELECT ...",
152
+ database: "main",
153
+ });
154
+ await flushCorruptionEmitsForTesting();
155
+
156
+ expect(emitCalls).toHaveLength(0);
157
+ });
158
+
159
+ test("debounces repeated corruption on the same database", async () => {
160
+ const err = sqliteError(
161
+ "SQLITE_CORRUPT",
162
+ "database disk image is malformed",
163
+ );
164
+ observeSqliteStatementError(err, { sql: "SELECT 1", database: "main" });
165
+ observeSqliteStatementError(err, { sql: "SELECT 2", database: "main" });
166
+ observeSqliteStatementError(err, { sql: "SELECT 3", database: "main" });
167
+ await flushCorruptionEmitsForTesting();
168
+ // One report for `main` within the cooldown window...
169
+ expect(emitCalls).toHaveLength(1);
170
+
171
+ // ...but a distinct database is still free to report.
172
+ observeSqliteStatementError(err, { sql: "SELECT 1", database: "memory" });
173
+ await flushCorruptionEmitsForTesting();
174
+ expect(emitCalls).toHaveLength(2);
175
+ });
176
+ });
177
+
178
+ describe("wired into the slow-query wrapper", () => {
179
+ beforeEach(() => {
180
+ emitCalls.length = 0;
181
+ resetSqliteCorruptionWatchdogForTesting();
182
+ });
183
+
184
+ test("a wrapped connection hitting a corrupt-header file emits, and the error still propagates", async () => {
185
+ // A garbage file throws SQLITE_NOTADB while `.query()` compiles the SQL —
186
+ // the wrapper's create-time seam surfaces it straight to the watchdog, and
187
+ // names the file from the connection's own `filename` (no key plumbing).
188
+ const dir = mkdtempSync(join(tmpdir(), "sqlite-corruption-wd-"));
189
+ const garbagePath = join(dir, "not-a.db");
190
+ writeFileSync(
191
+ garbagePath,
192
+ Buffer.from("this is definitely not a sqlite db"),
193
+ );
194
+
195
+ const db = new Database(garbagePath);
196
+ wrapSqliteForSlowQueryLogging(db);
197
+
198
+ // The original SQLite error must still propagate unchanged.
199
+ expect(() => db.query("SELECT name FROM sqlite_master").all()).toThrow();
200
+ await flushCorruptionEmitsForTesting();
201
+
202
+ expect(emitCalls).toHaveLength(1);
203
+ expect(emitCalls[0].checkName).toBe("sqlite_corrupted");
204
+ expect(emitCalls[0].detail.database).toBe("not-a.db");
205
+ });
206
+
207
+ test("a wrapped connection running healthy queries emits nothing", async () => {
208
+ const db = new Database(":memory:");
209
+ wrapSqliteForSlowQueryLogging(db);
210
+ db.exec("CREATE TABLE t (id INTEGER PRIMARY KEY)");
211
+ db.query("INSERT INTO t (id) VALUES (1)").run();
212
+ db.query("SELECT * FROM t").all();
213
+ await flushCorruptionEmitsForTesting();
214
+
215
+ expect(emitCalls).toHaveLength(0);
216
+ });
217
+ });
@@ -0,0 +1,155 @@
1
+ /**
2
+ * SQLite corruption watchdog.
3
+ *
4
+ * The daemon keeps all of its state — conversations, schedules, memory,
5
+ * telemetry — in `bun:sqlite` files. When one of those files is damaged (a
6
+ * torn write, a truncated file, bad disk media) SQLite surfaces it as a
7
+ * `SQLITE_CORRUPT` / `SQLITE_NOTADB` error ("database disk image is malformed"
8
+ * / "file is not a database") on the next statement that touches the damaged
9
+ * page. Left unobserved, that damage is only noticed when a user hits a broken
10
+ * feature; the platform has no fleet-wide view of how often it happens.
11
+ *
12
+ * This watchdog makes corruption observable. It emits a `watchdog` telemetry
13
+ * event with `check_name = "sqlite_corrupted"` — the same stream the event-loop
14
+ * watchdog uses — but POSTs it *directly* to the platform ingest rather than
15
+ * through the SQLite-backed watchdog buffer: a check that fires because SQLite
16
+ * is unusable must not depend on writing to SQLite, and going direct keeps this
17
+ * module out of the `db-connection` import graph. The direct path still honors
18
+ * the `share_analytics` opt-out (see {@link emitWatchdogEventDirect}). The
19
+ * platform's already-merged `watchdog__sqlite_corruption_daily` admin query
20
+ * filters `check_name` to this exact string, so it is the cross-repo contract
21
+ * and must stay stable.
22
+ *
23
+ * Detection is per-statement: the shared slow-query wrapper — which already
24
+ * wraps every `bun:sqlite` connection (main, logs, memory, telemetry — Drizzle
25
+ * and raw) — hands every failed statement to {@link observeSqliteStatementError}.
26
+ * So corruption is caught on *any* failing query or write, read or write, the
27
+ * moment the workload actually hits the damaged page — no dedicated scan
28
+ * needed. Non-corruption errors (constraint violations, syntax errors,
29
+ * transient `SQLITE_BUSY`) are ignored.
30
+ *
31
+ * Reports are debounced per-database (a corrupt file throws on every
32
+ * subsequent statement) — see {@link REPORT_COOLDOWN_MS}.
33
+ */
34
+
35
+ import * as Sentry from "@sentry/node";
36
+
37
+ import type { StatementErrorContext } from "../persistence/slow-query-log.js";
38
+ import { getLogger } from "../util/logger.js";
39
+
40
+ const log = getLogger("sqlite-corruption-watchdog");
41
+
42
+ /**
43
+ * Check name emitted for SQLite corruption events. The platform's
44
+ * `watchdog__sqlite_corruption_daily` admin query filters `check_name` to
45
+ * this exact string — it is the cross-repo contract, keep it stable.
46
+ */
47
+ export const SQLITE_CORRUPTED_CHECK_NAME = "sqlite_corrupted";
48
+
49
+ /**
50
+ * Minimum spacing between reports for a single database. Corruption is sticky:
51
+ * once a file is malformed every subsequent statement against it throws the
52
+ * same way, so without a cooldown a single damaged DB hit in a tight loop
53
+ * would emit a flood of identical events. Keyed per database so damage to one
54
+ * file never suppresses the first report for another.
55
+ */
56
+ const REPORT_COOLDOWN_MS = 60_000;
57
+
58
+ const lastReportAtByDatabase = new Map<string, number>();
59
+
60
+ /** The most recent in-flight direct emit, so tests can await it. */
61
+ let pendingEmit: Promise<unknown> = Promise.resolve();
62
+
63
+ /**
64
+ * Whether an error is SQLite reporting structural corruption — the
65
+ * `SQLITE_CORRUPT` / `SQLITE_NOTADB` families, by extended result code or by
66
+ * the canonical message text ("database disk image is malformed" / "file is
67
+ * not a database"). Deliberately distinct from `isRetryableSqliteError`: a
68
+ * corrupt image is permanent, not transient contention, so retrying can never
69
+ * help. Pure, so callers (and tests) can classify a synthesized error without
70
+ * a real database.
71
+ */
72
+ export function isSqliteCorruptionError(err: unknown): boolean {
73
+ const code = (err as { code?: string } | null)?.code ?? "";
74
+ if (code.startsWith("SQLITE_CORRUPT") || code.startsWith("SQLITE_NOTADB")) {
75
+ return true;
76
+ }
77
+ const message = (
78
+ err instanceof Error ? err.message : String(err)
79
+ ).toLowerCase();
80
+ return (
81
+ message.includes("database disk image is malformed") ||
82
+ message.includes("file is not a database")
83
+ );
84
+ }
85
+
86
+ /**
87
+ * Report a corruption detection: debounce per database, capture to Sentry, and
88
+ * emit the `sqlite_corrupted` telemetry event directly (opt-out gated inside
89
+ * {@link emitWatchdogEventDirect}). The direct emit is lazy-imported so the
90
+ * platform/telemetry stack only loads on the rare corruption path — and so this
91
+ * module never pulls `db-connection` into its static import graph. Fire-and-
92
+ * forget: the observer runs on a query hot path, so nothing here throws or
93
+ * blocks on the POST.
94
+ */
95
+ function reportCorruption(
96
+ detail: Record<string, unknown> & { database: string },
97
+ ): void {
98
+ const now = Date.now();
99
+ const last =
100
+ lastReportAtByDatabase.get(detail.database) ?? Number.NEGATIVE_INFINITY;
101
+ if (now - last < REPORT_COOLDOWN_MS) return;
102
+ lastReportAtByDatabase.set(detail.database, now);
103
+
104
+ log.error(detail, "SQLite corruption detected");
105
+
106
+ pendingEmit = import("../telemetry/watchdog-direct-emit.js")
107
+ .then(({ emitWatchdogEventDirect }) =>
108
+ emitWatchdogEventDirect(SQLITE_CORRUPTED_CHECK_NAME, detail),
109
+ )
110
+ .catch(() => {
111
+ // Best-effort; never let a telemetry failure escape the query path.
112
+ });
113
+
114
+ try {
115
+ Sentry.withScope((scope) => {
116
+ scope.setLevel("error");
117
+ scope.setTag("sqlite_corruption_database", detail.database);
118
+ scope.setContext("sqlite_corruption", detail);
119
+ Sentry.captureMessage(SQLITE_CORRUPTED_CHECK_NAME);
120
+ });
121
+ } catch {
122
+ // Never let a Sentry failure escape into the calling query path.
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Called by the slow-query wrapper for every failed statement: fires the
128
+ * corruption event for `SQLITE_CORRUPT` / `SQLITE_NOTADB` errors and ignores
129
+ * everything else (constraint/syntax errors, transient `SQLITE_BUSY` /
130
+ * `SQLITE_IOERR` contention). The wrapper calls this directly, so there is no
131
+ * observer registration or start/stop lifecycle — a corrupt file reports the
132
+ * moment a statement hits it.
133
+ */
134
+ export function observeSqliteStatementError(
135
+ err: unknown,
136
+ context: StatementErrorContext,
137
+ ): void {
138
+ if (!isSqliteCorruptionError(err)) return;
139
+ reportCorruption({
140
+ database: context.database ?? "unknown",
141
+ error: err instanceof Error ? err.message : String(err),
142
+ ...(context.sql ? { sql: context.sql } : {}),
143
+ ...(context.label ? { label: context.label } : {}),
144
+ });
145
+ }
146
+
147
+ /** Test-only: clear the per-database debounce state between cases. */
148
+ export function resetSqliteCorruptionWatchdogForTesting(): void {
149
+ lastReportAtByDatabase.clear();
150
+ }
151
+
152
+ /** Test-only: await the most recent fire-and-forget direct emit. */
153
+ export function flushCorruptionEmitsForTesting(): Promise<unknown> {
154
+ return pendingEmit;
155
+ }
@@ -105,6 +105,14 @@ export interface ToolSetupContext extends SurfaceConversationContext {
105
105
  callSessionId?: string;
106
106
  /** The interface ID of the connected client driving the current turn (e.g. "macos", "chrome-extension"). Propagated into ToolContext for browser backend selection. */
107
107
  readonly transportInterface?: InterfaceId;
108
+ /**
109
+ * The conversation's per-chat plugin scope (mirrors
110
+ * {@link Conversation.enabledPlugins}). `null`/absent means no per-chat
111
+ * restriction. Read per tool call to derive the effective enabled-plugin set
112
+ * (via `getEffectiveEnabledPluginSet`) for the `ToolContext.enabledPluginSet`
113
+ * field and the `skill_execute` dispatch guard.
114
+ */
115
+ readonly enabledPlugins?: string[] | null;
108
116
 
109
117
  /** Turn-scoped flag: true when any tool call in the current turn received explicit user approval via interactive prompt. Cleared at turn end. */
110
118
  approvedViaPromptThisTurn?: boolean;