@vellumai/assistant 0.6.3 → 0.6.4

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 (667) hide show
  1. package/ARCHITECTURE.md +273 -10
  2. package/Dockerfile +2 -3
  3. package/bun.lock +5 -13
  4. package/docs/backup-troubleshooting.md +52 -0
  5. package/docs/browser-use-architecture-phase2.md +174 -0
  6. package/docs/stt-provider-onboarding.md +120 -0
  7. package/knip.json +12 -2
  8. package/node_modules/@vellumai/ces-contracts/bun.lock +8 -6
  9. package/node_modules/@vellumai/ces-contracts/package.json +3 -3
  10. package/openapi.yaml +982 -72
  11. package/package.json +4 -6
  12. package/scripts/generate-openapi.ts +0 -1
  13. package/scripts/test.sh +73 -18
  14. package/src/__tests__/agent-image-optimize.test.ts +28 -0
  15. package/src/__tests__/agent-loop.test.ts +123 -0
  16. package/src/__tests__/anthropic-provider.test.ts +263 -10
  17. package/src/__tests__/auto-analysis-end-to-end.test.ts +550 -0
  18. package/src/__tests__/auto-analysis-prompt.test.ts +50 -0
  19. package/src/__tests__/browser-fill-credential.test.ts +11 -0
  20. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +2 -2
  21. package/src/__tests__/browser-skill-endstate.test.ts +31 -7
  22. package/src/__tests__/btw-routes.test.ts +7 -0
  23. package/src/__tests__/call-controller.test.ts +581 -20
  24. package/src/__tests__/catalog-files.test.ts +138 -0
  25. package/src/__tests__/channel-invite-transport.test.ts +2 -2
  26. package/src/__tests__/channel-readiness-routes.test.ts +16 -20
  27. package/src/__tests__/channel-readiness-service.test.ts +12 -7
  28. package/src/__tests__/checker.test.ts +157 -10
  29. package/src/__tests__/clawhub-files.test.ts +347 -0
  30. package/src/__tests__/commit-message-enrichment-service.test.ts +36 -19
  31. package/src/__tests__/config-analysis.test.ts +100 -0
  32. package/src/__tests__/config-schema.test.ts +1013 -66
  33. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +339 -0
  34. package/src/__tests__/config-watcher.test.ts +43 -8
  35. package/src/__tests__/contact-store-user-file.test.ts +512 -0
  36. package/src/__tests__/contacts-write.test.ts +197 -0
  37. package/src/__tests__/context-window-manager.test.ts +88 -0
  38. package/src/__tests__/conversation-abort-tool-results.test.ts +2 -0
  39. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
  40. package/src/__tests__/conversation-agent-loop.test.ts +98 -2
  41. package/src/__tests__/conversation-confirmation-signals.test.ts +135 -0
  42. package/src/__tests__/conversation-error.test.ts +70 -0
  43. package/src/__tests__/conversation-history-web-search.test.ts +11 -4
  44. package/src/__tests__/conversation-init.benchmark.test.ts +6 -1
  45. package/src/__tests__/conversation-launcher-skill-regression.test.ts +51 -0
  46. package/src/__tests__/conversation-list-source.test.ts +145 -0
  47. package/src/__tests__/conversation-pre-run-repair.test.ts +2 -0
  48. package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -0
  49. package/src/__tests__/conversation-queue.test.ts +901 -60
  50. package/src/__tests__/conversation-routes-disk-view.test.ts +270 -0
  51. package/src/__tests__/conversation-runtime-assembly.test.ts +55 -0
  52. package/src/__tests__/conversation-skill-tools.test.ts +7 -4
  53. package/src/__tests__/conversation-slash-commands.test.ts +33 -0
  54. package/src/__tests__/conversation-slash-queue.test.ts +89 -18
  55. package/src/__tests__/conversation-slash-unknown.test.ts +2 -0
  56. package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +226 -0
  57. package/src/__tests__/conversation-workspace-injection.test.ts +2 -0
  58. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +2 -0
  59. package/src/__tests__/credential-health-service.test.ts +352 -0
  60. package/src/__tests__/credential-security-invariants.test.ts +5 -3
  61. package/src/__tests__/credential-vault-unit.test.ts +379 -3
  62. package/src/__tests__/credentials-cli.test.ts +40 -16
  63. package/src/__tests__/cross-provider-web-search.test.ts +146 -35
  64. package/src/__tests__/deterministic-verification-control-plane.test.ts +10 -1
  65. package/src/__tests__/device-id.test.ts +112 -0
  66. package/src/__tests__/docker-signing-key-bootstrap.test.ts +167 -4
  67. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +1 -3
  68. package/src/__tests__/email-html-renderer.test.ts +71 -0
  69. package/src/__tests__/email-invite-adapter.test.ts +36 -32
  70. package/src/__tests__/emit-event-signal.test.ts +71 -0
  71. package/src/__tests__/extension-id-sync-guard.test.ts +75 -8
  72. package/src/__tests__/fixtures/mock-chrome-extension.ts +11 -0
  73. package/src/__tests__/gateway-only-enforcement.test.ts +206 -1
  74. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  75. package/src/__tests__/gemini-provider.test.ts +64 -0
  76. package/src/__tests__/get-skill-detail-audit.test.ts +325 -0
  77. package/src/__tests__/gmail-archive-fallback.test.ts +193 -0
  78. package/src/__tests__/gmail-archive-gate.test.ts +246 -0
  79. package/src/__tests__/gmail-preferences.test.ts +117 -0
  80. package/src/__tests__/headless-browser-interactions.test.ts +43 -0
  81. package/src/__tests__/headless-browser-mode.test.ts +614 -0
  82. package/src/__tests__/headless-browser-navigate.test.ts +142 -5
  83. package/src/__tests__/headless-browser-read-tools.test.ts +11 -0
  84. package/src/__tests__/headless-browser-snapshot.test.ts +10 -0
  85. package/src/__tests__/heartbeat-service.test.ts +70 -17
  86. package/src/__tests__/home-state-routes.test.ts +162 -0
  87. package/src/__tests__/host-bash-proxy.test.ts +0 -5
  88. package/src/__tests__/host-browser-e2e-cloud.test.ts +138 -4
  89. package/src/__tests__/host-browser-e2e-self-hosted.test.ts +4 -4
  90. package/src/__tests__/host-browser-ws-events-e2e.test.ts +103 -0
  91. package/src/__tests__/host-cu-proxy.test.ts +0 -5
  92. package/src/__tests__/identity-intro-cache.test.ts +40 -10
  93. package/src/__tests__/init-feature-flag-overrides.test.ts +38 -112
  94. package/src/__tests__/jobs-store-upsert-debounced.test.ts +141 -0
  95. package/src/__tests__/llm-context-normalization.test.ts +488 -0
  96. package/src/__tests__/llm-context-route-provider.test.ts +86 -5
  97. package/src/__tests__/llm-usage-store.test.ts +363 -0
  98. package/src/__tests__/media-stream-output.test.ts +555 -0
  99. package/src/__tests__/media-stream-parser.test.ts +374 -0
  100. package/src/__tests__/media-stream-server-integration.test.ts +1234 -0
  101. package/src/__tests__/media-stream-stt-session.test.ts +588 -0
  102. package/src/__tests__/media-turn-detector.test.ts +440 -0
  103. package/src/__tests__/message-queue.test.ts +125 -0
  104. package/src/__tests__/migration-export-http.test.ts +6 -6
  105. package/src/__tests__/migration-import-commit-http.test.ts +8 -6
  106. package/src/__tests__/migration-import-preflight-http.test.ts +6 -5
  107. package/src/__tests__/migration-validate-http.test.ts +3 -3
  108. package/src/__tests__/mock-gateway-ipc.ts +151 -0
  109. package/src/__tests__/model-intents.test.ts +2 -2
  110. package/src/__tests__/oauth-apps-routes.test.ts +1 -0
  111. package/src/__tests__/oauth-cli.test.ts +2 -0
  112. package/src/__tests__/oauth-connect-orchestrator.test.ts +2 -0
  113. package/src/__tests__/oauth-provider-serializer.test.ts +1 -0
  114. package/src/__tests__/oauth-providers-routes.test.ts +2 -0
  115. package/src/__tests__/oauth-store.test.ts +85 -0
  116. package/src/__tests__/oauth2-gateway-transport.test.ts +249 -6
  117. package/src/__tests__/onboarding-template-contract.test.ts +6 -13
  118. package/src/__tests__/openai-provider.test.ts +176 -0
  119. package/src/__tests__/openai-responses-cutover-guard.test.ts +184 -0
  120. package/src/__tests__/openai-responses-provider.test.ts +1105 -0
  121. package/src/__tests__/openrouter-token-estimation.test.ts +100 -0
  122. package/src/__tests__/outlook-unsubscribe.test.ts +31 -2
  123. package/src/__tests__/persona-resolver.test.ts +251 -0
  124. package/src/__tests__/platform-bash-auto-approve.test.ts +4 -0
  125. package/src/__tests__/platform.test.ts +92 -1
  126. package/src/__tests__/post-turn-tool-result-truncation.test.ts +47 -0
  127. package/src/__tests__/prechat-onboarding-contract.test.ts +267 -0
  128. package/src/__tests__/pricing.test.ts +174 -0
  129. package/src/__tests__/qdrant-manager.test.ts +29 -8
  130. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +194 -0
  131. package/src/__tests__/relationship-state-contract.test.ts +175 -0
  132. package/src/__tests__/relay-server.test.ts +423 -5
  133. package/src/__tests__/search-skills-unified.test.ts +118 -0
  134. package/src/__tests__/secret-scanner-executor.test.ts +4 -0
  135. package/src/__tests__/secure-keys.test.ts +107 -0
  136. package/src/__tests__/send-endpoint-busy.test.ts +5 -1
  137. package/src/__tests__/sequence-store.test.ts +1 -1
  138. package/src/__tests__/server-history-render.test.ts +49 -0
  139. package/src/__tests__/settings-routes.test.ts +201 -0
  140. package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
  141. package/src/__tests__/skills-file-content-endpoint.test.ts +276 -145
  142. package/src/__tests__/skills-files-catalog-fallback.test.ts +381 -93
  143. package/src/__tests__/skills.test.ts +5 -2
  144. package/src/__tests__/skillssh-files.test.ts +446 -0
  145. package/src/__tests__/slack-block-formatting.test.ts +110 -0
  146. package/src/__tests__/slack-channel-config.test.ts +564 -1
  147. package/src/__tests__/stt-catalog-parity.test.ts +282 -0
  148. package/src/__tests__/stt-stream-session.test.ts +535 -0
  149. package/src/__tests__/system-prompt.test.ts +112 -26
  150. package/src/__tests__/telephony-stt-routing.test.ts +329 -0
  151. package/src/__tests__/terminal-tools.test.ts +18 -7
  152. package/src/__tests__/test-preload.ts +18 -0
  153. package/src/__tests__/test-support/browser-skill-harness.ts +4 -1
  154. package/src/__tests__/tool-executor-lifecycle-events.test.ts +9 -5
  155. package/src/__tests__/tool-executor-shell-integration.test.ts +4 -0
  156. package/src/__tests__/tool-executor.test.ts +33 -24
  157. package/src/__tests__/tool-result-truncation.test.ts +36 -0
  158. package/src/__tests__/trust-store.test.ts +7 -1
  159. package/src/__tests__/trusted-contact-approval-notifier.test.ts +1 -1
  160. package/src/__tests__/tts-catalog-parity.test.ts +345 -0
  161. package/src/__tests__/twilio-routes-twiml.test.ts +512 -114
  162. package/src/__tests__/twilio-routes.test.ts +376 -0
  163. package/src/__tests__/unicode.test.ts +293 -0
  164. package/src/__tests__/update-bulletin-format.test.ts +59 -0
  165. package/src/__tests__/update-bulletin.test.ts +206 -5
  166. package/src/__tests__/usage-routes.test.ts +25 -4
  167. package/src/__tests__/user-reference.test.ts +46 -61
  168. package/src/__tests__/verification-control-plane-policy.test.ts +4 -0
  169. package/src/__tests__/voice-config-update.test.ts +403 -0
  170. package/src/__tests__/voice-quality.test.ts +434 -19
  171. package/src/__tests__/workspace-heartbeat-service.test.ts +7 -0
  172. package/src/__tests__/workspace-migration-033-stt-service-explicit-config.test.ts +547 -0
  173. package/src/__tests__/workspace-migration-034-remove-calls-voice-transcription-provider.test.ts +596 -0
  174. package/src/__tests__/workspace-migration-drop-user-md.test.ts +368 -0
  175. package/src/__tests__/workspace-migration-meets.test.ts +244 -0
  176. package/src/__tests__/workspace-migration-seed-device-id.test.ts +14 -20
  177. package/src/__tests__/workspace-policy.test.ts +2 -0
  178. package/src/agent/image-optimize.ts +24 -12
  179. package/src/agent/loop.ts +43 -3
  180. package/src/backup/__tests__/backup-key.test.ts +152 -0
  181. package/src/backup/__tests__/backup-worker.test.ts +767 -0
  182. package/src/backup/__tests__/list-snapshots.test.ts +87 -0
  183. package/src/backup/__tests__/local-writer.test.ts +218 -0
  184. package/src/backup/__tests__/offsite-writer.test.ts +641 -0
  185. package/src/backup/__tests__/paths.test.ts +300 -0
  186. package/src/backup/__tests__/restore.test.ts +498 -0
  187. package/src/backup/__tests__/snapshot-lock.test.ts +352 -0
  188. package/src/backup/__tests__/stream-crypt.test.ts +228 -0
  189. package/src/backup/backup-key.ts +137 -0
  190. package/src/backup/backup-worker.ts +459 -0
  191. package/src/backup/list-snapshots.ts +147 -0
  192. package/src/backup/local-writer.ts +133 -0
  193. package/src/backup/offsite-writer.ts +222 -0
  194. package/src/backup/paths.ts +226 -0
  195. package/src/backup/restore.ts +322 -0
  196. package/src/backup/snapshot-lock.ts +431 -0
  197. package/src/backup/stream-crypt.ts +263 -0
  198. package/src/bundler/package-resolver.ts +4 -0
  199. package/src/calls/audio-store.ts +11 -5
  200. package/src/calls/call-controller.ts +226 -71
  201. package/src/calls/call-domain.ts +9 -0
  202. package/src/calls/call-speech-output.ts +190 -0
  203. package/src/calls/call-transport.ts +77 -0
  204. package/src/calls/media-stream-audio-transcode.ts +173 -0
  205. package/src/calls/media-stream-output.ts +660 -0
  206. package/src/calls/media-stream-parser.ts +300 -0
  207. package/src/calls/media-stream-protocol.ts +166 -0
  208. package/src/calls/media-stream-server.ts +592 -0
  209. package/src/calls/media-stream-stt-session.ts +460 -0
  210. package/src/calls/media-turn-detector.ts +230 -0
  211. package/src/calls/relay-server.ts +90 -75
  212. package/src/calls/resolve-call-tts-provider.ts +136 -0
  213. package/src/calls/telephony-stt-routing.ts +145 -0
  214. package/src/calls/tts-call-strategy.ts +161 -0
  215. package/src/calls/tts-text-sanitizer.ts +32 -16
  216. package/src/calls/twilio-routes.ts +281 -17
  217. package/src/calls/voice-quality.ts +78 -35
  218. package/src/calls/voice-session-bridge.ts +8 -1
  219. package/src/channels/types.ts +16 -0
  220. package/src/cli/__tests__/run-assistant-command.ts +11 -1
  221. package/src/cli/commands/__tests__/backup.test.ts +1165 -0
  222. package/src/cli/commands/__tests__/domain-register.test.ts +234 -0
  223. package/src/cli/commands/__tests__/domain-status.test.ts +132 -0
  224. package/src/cli/commands/__tests__/email-attachment.test.ts +422 -0
  225. package/src/cli/commands/__tests__/email-download.test.ts +16 -1
  226. package/src/cli/commands/__tests__/email-list.test.ts +22 -4
  227. package/src/cli/commands/__tests__/email-register.test.ts +4 -4
  228. package/src/cli/commands/__tests__/email-send.test.ts +37 -4
  229. package/src/cli/commands/__tests__/email-status.test.ts +5 -1
  230. package/src/cli/commands/__tests__/email-unregister.test.ts +34 -5
  231. package/src/cli/commands/backup.ts +993 -0
  232. package/src/cli/commands/conversations.ts +77 -0
  233. package/src/cli/commands/credentials.ts +0 -1
  234. package/src/cli/commands/domain.ts +210 -0
  235. package/src/cli/commands/email.ts +255 -3
  236. package/src/cli/commands/oauth/__tests__/connect.test.ts +12 -0
  237. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +1 -0
  238. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -0
  239. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -0
  240. package/src/cli/commands/oauth/mode.ts +12 -3
  241. package/src/cli/commands/oauth/providers.ts +15 -0
  242. package/src/cli/commands/oauth/shared.ts +2 -1
  243. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +4 -9
  244. package/src/cli/commands/platform/__tests__/connect.test.ts +6 -0
  245. package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -1
  246. package/src/cli/commands/platform/__tests__/status.test.ts +6 -0
  247. package/src/cli/program.ts +30 -4
  248. package/src/config/__tests__/backup-schema.test.ts +134 -0
  249. package/src/config/assistant-feature-flags.ts +61 -62
  250. package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +37 -1
  251. package/src/config/bundled-skills/browser/SKILL.md +30 -5
  252. package/src/config/bundled-skills/browser/TOOLS.json +123 -0
  253. package/src/config/bundled-skills/browser/tools/browser-attach.ts +12 -0
  254. package/src/config/bundled-skills/browser/tools/browser-detach.ts +12 -0
  255. package/src/config/bundled-skills/browser/tools/browser-status.ts +12 -0
  256. package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +17 -0
  257. package/src/config/bundled-skills/contacts/SKILL.md +2 -2
  258. package/src/config/bundled-skills/gmail/SKILL.md +53 -7
  259. package/src/config/bundled-skills/gmail/TOOLS.json +33 -3
  260. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +116 -9
  261. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +138 -11
  262. package/src/config/bundled-skills/gmail/tools/gmail-preferences-tool.ts +59 -0
  263. package/src/config/bundled-skills/gmail/tools/gmail-preferences.ts +82 -0
  264. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +113 -17
  265. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +2 -2
  266. package/src/config/bundled-skills/media-processing/SKILL.md +3 -9
  267. package/src/config/bundled-skills/media-processing/TOOLS.json +1 -6
  268. package/src/config/bundled-skills/media-processing/__tests__/audio-transcribe.test.ts +125 -0
  269. package/src/config/bundled-skills/media-processing/__tests__/extract-keyframes.test.ts +181 -0
  270. package/src/config/bundled-skills/media-processing/__tests__/preprocess-audio.test.ts +141 -0
  271. package/src/config/bundled-skills/media-processing/services/audio-transcribe.ts +32 -87
  272. package/src/config/bundled-skills/media-processing/services/preprocess.ts +8 -4
  273. package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +0 -10
  274. package/src/config/bundled-skills/messaging/SKILL.md +3 -3
  275. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +2 -2
  276. package/src/config/bundled-skills/outlook/SKILL.md +2 -2
  277. package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +2 -2
  278. package/src/config/bundled-skills/phone-calls/SKILL.md +2 -2
  279. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +27 -18
  280. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +3 -3
  281. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  282. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +26 -22
  283. package/src/config/bundled-skills/slack/SKILL.md +1 -0
  284. package/src/config/bundled-skills/transcribe/SKILL.md +9 -14
  285. package/src/config/bundled-skills/transcribe/TOOLS.json +2 -7
  286. package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +256 -0
  287. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +38 -188
  288. package/src/config/bundled-tool-registry.ts +8 -0
  289. package/src/config/env-registry.ts +24 -0
  290. package/src/config/env.ts +34 -10
  291. package/src/config/feature-flag-registry.json +46 -14
  292. package/src/config/loader.ts +26 -12
  293. package/src/config/schema.ts +35 -10
  294. package/src/config/schemas/__tests__/stt.test.ts +43 -0
  295. package/src/config/schemas/analysis.ts +51 -0
  296. package/src/config/schemas/backup.ts +72 -0
  297. package/src/config/schemas/calls.ts +1 -26
  298. package/src/config/schemas/elevenlabs.ts +0 -59
  299. package/src/config/schemas/filing.ts +47 -7
  300. package/src/config/schemas/heartbeat.ts +27 -5
  301. package/src/config/schemas/host-browser.ts +47 -1
  302. package/src/config/schemas/inference.ts +1 -1
  303. package/src/config/schemas/memory-lifecycle.ts +14 -2
  304. package/src/config/schemas/services.ts +44 -0
  305. package/src/config/schemas/stt.ts +59 -0
  306. package/src/config/schemas/tts.ts +230 -0
  307. package/src/config/schemas/updates.ts +14 -0
  308. package/src/config/skills.ts +4 -0
  309. package/src/config/types.ts +4 -0
  310. package/src/contacts/contact-store.ts +56 -11
  311. package/src/contacts/contacts-write.ts +38 -1
  312. package/src/context/post-turn-tool-result-truncation.ts +3 -2
  313. package/src/context/tool-result-truncation.ts +2 -1
  314. package/src/context/window-manager.ts +45 -12
  315. package/src/credential-execution/executable-discovery.ts +12 -2
  316. package/src/credential-execution/process-manager.ts +33 -2
  317. package/src/credential-health/credential-health-service.ts +366 -0
  318. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +324 -0
  319. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +497 -0
  320. package/src/daemon/__tests__/conversation-tool-setup.test.ts +17 -8
  321. package/src/daemon/__tests__/lifecycle-startup-ordering.test.ts +127 -0
  322. package/src/daemon/config-watcher.ts +99 -5
  323. package/src/daemon/conversation-agent-loop-handlers.ts +6 -0
  324. package/src/daemon/conversation-agent-loop.ts +101 -24
  325. package/src/daemon/conversation-error.ts +11 -0
  326. package/src/daemon/conversation-history.ts +40 -6
  327. package/src/daemon/conversation-launch.ts +220 -0
  328. package/src/daemon/conversation-lifecycle.ts +59 -9
  329. package/src/daemon/conversation-messaging.ts +37 -3
  330. package/src/daemon/conversation-notifiers.ts +5 -0
  331. package/src/daemon/conversation-process.ts +581 -19
  332. package/src/daemon/conversation-queue-manager.ts +24 -0
  333. package/src/daemon/conversation-runtime-assembly.ts +11 -1
  334. package/src/daemon/conversation-slash.ts +36 -0
  335. package/src/daemon/conversation-surfaces.ts +94 -4
  336. package/src/daemon/conversation-tool-setup.ts +25 -0
  337. package/src/daemon/conversation-usage.ts +7 -4
  338. package/src/daemon/conversation.ts +86 -28
  339. package/src/daemon/handlers/config-slack-channel.ts +269 -94
  340. package/src/daemon/handlers/conversations.ts +4 -1
  341. package/src/daemon/handlers/shared.ts +22 -0
  342. package/src/daemon/handlers/skills.ts +321 -77
  343. package/src/daemon/host-browser-proxy.ts +2 -1
  344. package/src/daemon/lifecycle.ts +122 -25
  345. package/src/daemon/message-protocol.ts +6 -0
  346. package/src/daemon/message-types/conversations.ts +34 -1
  347. package/src/daemon/message-types/home.ts +40 -0
  348. package/src/daemon/message-types/meet.ts +143 -0
  349. package/src/daemon/message-types/messages.ts +14 -0
  350. package/src/daemon/message-types/schedules.ts +34 -2
  351. package/src/daemon/message-types/skills.ts +16 -0
  352. package/src/daemon/message-types/surfaces.ts +2 -0
  353. package/src/daemon/server.ts +347 -2
  354. package/src/daemon/shutdown-handlers.ts +32 -4
  355. package/src/daemon/shutdown-registry.ts +40 -0
  356. package/src/daemon/tool-side-effects.ts +9 -0
  357. package/src/email/html-renderer.ts +76 -0
  358. package/src/heartbeat/heartbeat-service.ts +93 -7
  359. package/src/home/__tests__/assistant-feed-authoring.test.ts +156 -0
  360. package/src/home/__tests__/emit-feed-event.test.ts +169 -0
  361. package/src/home/__tests__/feed-scheduler.test.ts +194 -0
  362. package/src/home/__tests__/feed-types.test.ts +275 -0
  363. package/src/home/__tests__/feed-writer.test.ts +688 -0
  364. package/src/home/__tests__/phase5-exit-criteria.test.ts +212 -0
  365. package/src/home/__tests__/platform-gmail-digest.test.ts +222 -0
  366. package/src/home/__tests__/progress-formula.test.ts +213 -0
  367. package/src/home/__tests__/relationship-state-writer.test.ts +740 -0
  368. package/src/home/__tests__/rollup-producer.test.ts +398 -0
  369. package/src/home/assistant-feed-authoring.ts +124 -0
  370. package/src/home/emit-feed-event.ts +158 -0
  371. package/src/home/feed-scheduler.ts +247 -0
  372. package/src/home/feed-types.ts +181 -0
  373. package/src/home/feed-writer.ts +469 -0
  374. package/src/home/platform-gmail-digest.ts +163 -0
  375. package/src/home/progress-formula.ts +86 -0
  376. package/src/home/relationship-state-writer.ts +824 -0
  377. package/src/home/relationship-state.ts +143 -0
  378. package/src/home/rollup-producer.ts +384 -0
  379. package/src/hooks/runner.ts +7 -0
  380. package/src/inbound/platform-callback-registration.ts +12 -3
  381. package/src/inbound/public-ingress-urls.ts +12 -0
  382. package/src/instrument.ts +1 -1
  383. package/src/ipc/__tests__/cli-ipc.test.ts +200 -0
  384. package/src/ipc/cli-client.ts +151 -0
  385. package/src/ipc/cli-server.ts +234 -0
  386. package/src/ipc/gateway-client.ts +180 -0
  387. package/src/ipc/routes/index.ts +5 -0
  388. package/src/ipc/routes/wake-conversation.ts +19 -0
  389. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +356 -0
  390. package/src/memory/__tests__/auto-analysis-guard.test.ts +57 -0
  391. package/src/memory/__tests__/conversation-analyze-job.test.ts +232 -0
  392. package/src/memory/__tests__/find-analysis-conversation.test.ts +196 -0
  393. package/src/memory/app-store.ts +1 -1
  394. package/src/memory/attachments-store.ts +70 -0
  395. package/src/memory/auto-analysis-enqueue.ts +127 -0
  396. package/src/memory/auto-analysis-guard.ts +27 -0
  397. package/src/memory/cleanup-schedule-state.ts +37 -0
  398. package/src/memory/conversation-analyze-job.ts +73 -0
  399. package/src/memory/conversation-crud.ts +99 -0
  400. package/src/memory/conversation-disk-view.ts +7 -0
  401. package/src/memory/conversation-group-migration.ts +34 -2
  402. package/src/memory/conversation-queries.ts +6 -5
  403. package/src/memory/db-init.ts +6 -0
  404. package/src/memory/db-maintenance.ts +108 -0
  405. package/src/memory/db.ts +1 -0
  406. package/src/memory/graph/conversation-graph-memory.ts +15 -0
  407. package/src/memory/graph/extraction.test.ts +23 -0
  408. package/src/memory/graph/extraction.ts +8 -0
  409. package/src/memory/graph/retriever.ts +27 -18
  410. package/src/memory/graph/scoring.test.ts +186 -0
  411. package/src/memory/graph/scoring.ts +31 -1
  412. package/src/memory/graph/tools.ts +1 -1
  413. package/src/memory/group-crud.ts +6 -1
  414. package/src/memory/indexer.ts +95 -16
  415. package/src/memory/job-handlers/cleanup.ts +11 -8
  416. package/src/memory/job-handlers/conversation-starters.ts +16 -10
  417. package/src/memory/jobs-store.ts +64 -4
  418. package/src/memory/jobs-worker.ts +22 -9
  419. package/src/memory/llm-usage-store.ts +92 -56
  420. package/src/memory/migrations/219-oauth-providers-token-exchange-body-format.ts +15 -0
  421. package/src/memory/migrations/220-normalize-user-file-by-principal.ts +190 -0
  422. package/src/memory/migrations/221-conversations-archived-at.ts +16 -0
  423. package/src/memory/migrations/index.ts +6 -0
  424. package/src/memory/migrations/registry.ts +8 -0
  425. package/src/memory/qdrant-manager.ts +43 -16
  426. package/src/memory/schema/conversations.ts +2 -0
  427. package/src/memory/schema/oauth.ts +3 -0
  428. package/src/memory/usage-buckets.ts +396 -0
  429. package/src/messaging/providers/gmail/client.ts +57 -6
  430. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +282 -0
  431. package/src/messaging/providers/slack/adapter.ts +143 -38
  432. package/src/messaging/providers/slack/client.ts +16 -0
  433. package/src/messaging/providers/slack/types.ts +4 -0
  434. package/src/notifications/decision-engine.ts +3 -3
  435. package/src/notifications/signal.ts +5 -0
  436. package/src/oauth/__tests__/identity-verifier.test.ts +1 -0
  437. package/src/oauth/byo-connection.test.ts +18 -1
  438. package/src/oauth/byo-connection.ts +3 -1
  439. package/src/oauth/connect-orchestrator.ts +2 -0
  440. package/src/oauth/connection-resolver.ts +6 -2
  441. package/src/oauth/connection.ts +2 -0
  442. package/src/oauth/oauth-store.ts +9 -0
  443. package/src/oauth/platform-connection.test.ts +98 -0
  444. package/src/oauth/platform-connection.ts +52 -31
  445. package/src/oauth/seed-providers.ts +7 -0
  446. package/src/permissions/checker.ts +16 -6
  447. package/src/permissions/defaults.ts +49 -1
  448. package/src/permissions/trust-store.ts +3 -3
  449. package/src/permissions/workspace-policy.ts +3 -0
  450. package/src/platform/client.test.ts +10 -0
  451. package/src/platform/sync-identity.ts +129 -0
  452. package/src/prompts/persona-resolver.ts +126 -2
  453. package/src/prompts/system-prompt.ts +59 -18
  454. package/src/prompts/templates/BOOTSTRAP.md +5 -5
  455. package/src/prompts/templates/SOUL.md +3 -1
  456. package/src/prompts/templates/UPDATES.md +12 -0
  457. package/src/prompts/templates/channels/slack.md +20 -0
  458. package/src/prompts/update-bulletin-format.ts +26 -9
  459. package/src/prompts/update-bulletin.ts +34 -23
  460. package/src/prompts/user-reference.ts +20 -17
  461. package/src/providers/__tests__/provider-secret-catalog.test.ts +42 -0
  462. package/src/providers/anthropic/client.ts +157 -61
  463. package/src/providers/fireworks/client.ts +2 -2
  464. package/src/providers/gemini/client.ts +9 -1
  465. package/src/providers/model-catalog.ts +6 -0
  466. package/src/providers/model-intents.ts +4 -4
  467. package/src/providers/ollama/client.ts +2 -2
  468. package/src/providers/openai/chat-completions-provider.ts +474 -0
  469. package/src/providers/openai/client.ts +25 -440
  470. package/src/providers/openai/responses-provider.ts +502 -0
  471. package/src/providers/openrouter/client.ts +101 -4
  472. package/src/providers/provider-secret-catalog.ts +139 -0
  473. package/src/providers/registry.ts +2 -2
  474. package/src/providers/retry.ts +14 -3
  475. package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +251 -0
  476. package/src/providers/speech-to-text/__tests__/resolve.test.ts +828 -0
  477. package/src/providers/speech-to-text/deepgram-realtime.test.ts +980 -0
  478. package/src/providers/speech-to-text/deepgram-realtime.ts +767 -0
  479. package/src/providers/speech-to-text/deepgram.test.ts +332 -0
  480. package/src/providers/speech-to-text/deepgram.ts +115 -0
  481. package/src/providers/speech-to-text/google-gemini-live-stream.test.ts +743 -0
  482. package/src/providers/speech-to-text/google-gemini-live-stream.ts +625 -0
  483. package/src/providers/speech-to-text/google-gemini.test.ts +226 -0
  484. package/src/providers/speech-to-text/google-gemini.ts +101 -0
  485. package/src/providers/speech-to-text/openai-whisper-stream.test.ts +564 -0
  486. package/src/providers/speech-to-text/openai-whisper-stream.ts +381 -0
  487. package/src/providers/speech-to-text/openai-whisper.test.ts +1 -37
  488. package/src/providers/speech-to-text/openai-whisper.ts +63 -33
  489. package/src/providers/speech-to-text/provider-catalog.ts +306 -0
  490. package/src/providers/speech-to-text/resolve.ts +386 -6
  491. package/src/providers/types.ts +9 -0
  492. package/src/runtime/AGENTS.md +43 -1
  493. package/src/runtime/__tests__/agent-wake.test.ts +831 -0
  494. package/src/runtime/__tests__/runtime-mode.test.ts +62 -0
  495. package/src/runtime/__tests__/slack-block-formatting.test.ts +481 -0
  496. package/src/runtime/agent-wake.ts +512 -0
  497. package/src/runtime/auth/__tests__/route-policy.test.ts +40 -0
  498. package/src/runtime/auth/route-policy.ts +30 -5
  499. package/src/runtime/auth/token-service.ts +56 -1
  500. package/src/runtime/btw-sidechain.ts +2 -0
  501. package/src/runtime/capability-tokens.ts +10 -10
  502. package/src/runtime/channel-invite-transport.ts +1 -1
  503. package/src/runtime/channel-invite-transports/email.ts +14 -6
  504. package/src/runtime/channel-readiness-service.ts +12 -22
  505. package/src/runtime/chrome-extension-registry.ts +38 -2
  506. package/src/runtime/http-server.ts +395 -10
  507. package/src/runtime/http-types.ts +6 -2
  508. package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +36 -0
  509. package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +360 -0
  510. package/src/runtime/migrations/migration-transport.ts +1 -0
  511. package/src/runtime/migrations/migration-wizard.ts +1 -0
  512. package/src/runtime/migrations/vbundle-import-analyzer.ts +77 -1
  513. package/src/runtime/migrations/vbundle-importer.ts +34 -0
  514. package/src/runtime/pending-interactions.ts +0 -11
  515. package/src/runtime/routes/__tests__/backup-routes.test.ts +967 -0
  516. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +507 -0
  517. package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +208 -0
  518. package/src/runtime/routes/__tests__/stt-routes.test.ts +406 -0
  519. package/src/runtime/routes/__tests__/tts-routes.test.ts +474 -0
  520. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +148 -17
  521. package/src/runtime/routes/app-management-routes.ts +12 -18
  522. package/src/runtime/routes/attachment-routes.test.ts +9 -3
  523. package/src/runtime/routes/attachment-routes.ts +216 -17
  524. package/src/runtime/routes/backup-routes.ts +519 -0
  525. package/src/runtime/routes/browser-extension-pair-routes.ts +82 -23
  526. package/src/runtime/routes/btw-routes.ts +8 -6
  527. package/src/runtime/routes/contact-routes.test.ts +298 -0
  528. package/src/runtime/routes/contact-routes.ts +132 -5
  529. package/src/runtime/routes/conversation-analysis-routes.ts +22 -142
  530. package/src/runtime/routes/conversation-management-routes.ts +115 -0
  531. package/src/runtime/routes/conversation-routes.ts +367 -146
  532. package/src/runtime/routes/filing-routes.ts +93 -0
  533. package/src/runtime/routes/home-feed-routes.ts +334 -0
  534. package/src/runtime/routes/home-state-routes.ts +138 -0
  535. package/src/runtime/routes/host-browser-routes.ts +3 -14
  536. package/src/runtime/routes/identity-intro-cache.ts +7 -3
  537. package/src/runtime/routes/identity-routes.ts +3 -17
  538. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +46 -39
  539. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +15 -15
  540. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +137 -0
  541. package/src/runtime/routes/integrations/slack/__tests__/share.test.ts +179 -0
  542. package/src/runtime/routes/integrations/slack/channel.ts +11 -3
  543. package/src/runtime/routes/integrations/slack/share.ts +45 -7
  544. package/src/runtime/routes/llm-context-normalization.ts +303 -0
  545. package/src/runtime/routes/memory-item-routes.test.ts +3 -2
  546. package/src/runtime/routes/migration-routes.ts +40 -5
  547. package/src/runtime/routes/settings-routes.ts +22 -5
  548. package/src/runtime/routes/skills-routes.ts +76 -7
  549. package/src/runtime/routes/stt-routes.ts +233 -0
  550. package/src/runtime/routes/surface-action-routes.ts +41 -2
  551. package/src/runtime/routes/tts-routes.ts +108 -24
  552. package/src/runtime/routes/usage-routes.ts +30 -2
  553. package/src/runtime/routes/user-route-dispatcher.ts +50 -5
  554. package/src/runtime/routes/user-routes.ts +13 -1
  555. package/src/runtime/routes/work-items-routes.ts +8 -1
  556. package/src/runtime/runtime-mode.ts +33 -0
  557. package/src/runtime/services/__tests__/analyze-conversation.test.ts +444 -0
  558. package/src/runtime/services/__tests__/analyze-deps-singleton.test.ts +67 -0
  559. package/src/runtime/services/__tests__/auto-analysis-prompt.test.ts +53 -0
  560. package/src/runtime/services/__tests__/manual-analysis-prompt.test.ts +41 -0
  561. package/src/runtime/services/analyze-conversation.ts +344 -0
  562. package/src/runtime/services/analyze-deps-singleton.ts +32 -0
  563. package/src/runtime/services/auto-analysis-prompt.ts +55 -0
  564. package/src/runtime/skill-route-registry.ts +49 -0
  565. package/src/runtime/slack-block-formatting.ts +437 -10
  566. package/src/schedule/scheduler.ts +50 -0
  567. package/src/security/oauth2.ts +26 -4
  568. package/src/security/secure-keys.ts +25 -2
  569. package/src/security/token-manager.ts +8 -0
  570. package/src/sequence/engine.ts +23 -0
  571. package/src/sequence/types.ts +1 -1
  572. package/src/skills/catalog-files.ts +64 -2
  573. package/src/skills/category-inference.ts +122 -0
  574. package/src/skills/clawhub-files.ts +213 -0
  575. package/src/skills/clawhub.ts +84 -23
  576. package/src/skills/skill-file-provider.ts +40 -0
  577. package/src/skills/skillssh-files.ts +395 -0
  578. package/src/skills/skillssh-registry.ts +4 -4
  579. package/src/stt/__tests__/daemon-batch-transcriber.test.ts +392 -0
  580. package/src/stt/__tests__/types.test.ts +89 -0
  581. package/src/stt/daemon-batch-transcriber.ts +195 -0
  582. package/src/stt/stt-stream-session.ts +499 -0
  583. package/src/stt/types.ts +330 -0
  584. package/src/stt/wav-encoder.test.ts +373 -0
  585. package/src/stt/wav-encoder.ts +175 -0
  586. package/src/subagent/manager.ts +38 -14
  587. package/src/tools/browser/__tests__/browser-mode.test.ts +119 -0
  588. package/src/tools/browser/__tests__/browser-status.test.ts +123 -0
  589. package/src/tools/browser/browser-execution.ts +1163 -23
  590. package/src/tools/browser/browser-manager.ts +45 -0
  591. package/src/tools/browser/browser-mode-constants.ts +12 -0
  592. package/src/tools/browser/browser-mode.ts +92 -0
  593. package/src/tools/browser/browser-status-constants.ts +33 -0
  594. package/src/tools/browser/cdp-client/__tests__/cdp-inspect-client.test.ts +393 -0
  595. package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +29 -0
  596. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +1648 -32
  597. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/discovery.test.ts +264 -0
  598. package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +183 -17
  599. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +254 -21
  600. package/src/tools/browser/cdp-client/errors.ts +15 -0
  601. package/src/tools/browser/cdp-client/extension-cdp-client.ts +39 -16
  602. package/src/tools/browser/cdp-client/factory.ts +797 -87
  603. package/src/tools/browser/cdp-client/index.ts +16 -2
  604. package/src/tools/browser/cdp-client/types.ts +68 -0
  605. package/src/tools/credentials/vault.ts +35 -6
  606. package/src/tools/network/web-fetch.ts +5 -2
  607. package/src/tools/network/web-search.ts +5 -2
  608. package/src/tools/shared/shell-output.ts +3 -1
  609. package/src/tools/side-effects.ts +2 -0
  610. package/src/tools/skills/sandbox-runner.ts +3 -2
  611. package/src/tools/terminal/safe-env.ts +10 -2
  612. package/src/tools/terminal/shell.ts +15 -4
  613. package/src/tools/tool-manifest.ts +21 -0
  614. package/src/tools/types.ts +17 -0
  615. package/src/tools/ui-surface/definitions.ts +6 -1
  616. package/src/tts/__tests__/provider-adapters.test.ts +834 -0
  617. package/src/tts/__tests__/provider-catalog-consistency.test.ts +196 -0
  618. package/src/tts/__tests__/provider-catalog.test.ts +183 -0
  619. package/src/tts/__tests__/provider-registry.test.ts +90 -0
  620. package/src/tts/provider-catalog.ts +201 -0
  621. package/src/tts/provider-registry.ts +73 -0
  622. package/src/tts/providers/deepgram-provider.ts +219 -0
  623. package/src/tts/providers/elevenlabs-provider.ts +211 -0
  624. package/src/tts/providers/fish-audio-provider.ts +183 -0
  625. package/src/tts/providers/index.ts +42 -0
  626. package/src/tts/providers/register-builtins.ts +130 -0
  627. package/src/tts/synthesize-text.ts +110 -0
  628. package/src/tts/tts-config-resolver.ts +78 -0
  629. package/src/tts/types.ts +153 -0
  630. package/src/types/onboarding-context.ts +7 -0
  631. package/src/util/abort-reasons.ts +58 -0
  632. package/src/util/device-id.ts +32 -16
  633. package/src/util/errors.ts +9 -1
  634. package/src/util/platform.ts +54 -10
  635. package/src/util/pricing.ts +66 -3
  636. package/src/util/spawn.ts +1 -1
  637. package/src/util/truncate.ts +4 -2
  638. package/src/util/unicode.ts +201 -0
  639. package/src/version.ts +19 -24
  640. package/src/watcher/engine.ts +23 -0
  641. package/src/watcher/watcher-store.ts +31 -0
  642. package/src/workspace/migrations/003-seed-device-id.ts +9 -3
  643. package/src/workspace/migrations/017-seed-persona-dirs.ts +68 -4
  644. package/src/workspace/migrations/029-seed-pkb.ts +1 -1
  645. package/src/workspace/migrations/031-drop-user-md.ts +317 -0
  646. package/src/workspace/migrations/031-llm-log-retention-zero-to-null.ts +73 -0
  647. package/src/workspace/migrations/032-tts-provider-unification.ts +227 -0
  648. package/src/workspace/migrations/033-stt-service-explicit-config.ts +122 -0
  649. package/src/workspace/migrations/034-remove-calls-voice-transcription-provider.ts +215 -0
  650. package/src/workspace/migrations/035-seed-slack-channel-persona.ts +50 -0
  651. package/src/workspace/migrations/036-update-pkb-index-bar.ts +37 -0
  652. package/src/workspace/migrations/037-create-meets-dir.ts +61 -0
  653. package/src/workspace/migrations/registry.ts +16 -0
  654. package/src/workspace/top-level-renderer.ts +13 -1
  655. package/src/workspace/turn-commit.ts +31 -0
  656. package/src/__tests__/email-cli.test.ts +0 -297
  657. package/src/__tests__/email-service-config-fallback.test.ts +0 -102
  658. package/src/cli/commands/browser-relay.ts +0 -466
  659. package/src/email/guardrails.ts +0 -221
  660. package/src/email/provider.ts +0 -117
  661. package/src/email/providers/agentmail.ts +0 -361
  662. package/src/email/providers/index.ts +0 -65
  663. package/src/email/service.ts +0 -384
  664. package/src/email/types.ts +0 -126
  665. package/src/prompts/templates/USER.md +0 -13
  666. package/src/providers/speech-to-text/types.ts +0 -17
  667. package/src/runtime/routes/browser-cdp-routes.ts +0 -229
@@ -1,5 +1,10 @@
1
1
  // Skill management types.
2
2
 
3
+ import type { PartnerAudit } from "../../skills/skillssh-registry.js";
4
+
5
+ // Re-export so consumers can access the audit types from this module.
6
+ export type { PartnerAudit } from "../../skills/skillssh-registry.js";
7
+
3
8
  // === Client → Server ===
4
9
 
5
10
  export interface SkillsListRequest {
@@ -98,6 +103,7 @@ interface ClawhubSlimSkill extends SlimSkillBase {
98
103
  installs: number;
99
104
  reports: number;
100
105
  publishedAt?: string;
106
+ version: string;
101
107
  }
102
108
 
103
109
  interface SkillsshSlimSkill extends SlimSkillBase {
@@ -105,6 +111,7 @@ interface SkillsshSlimSkill extends SlimSkillBase {
105
111
  slug: string;
106
112
  sourceRepo: string;
107
113
  installs: number;
114
+ audit?: Record<string, PartnerAudit>;
108
115
  }
109
116
 
110
117
  interface CustomSlimSkill extends SlimSkillBase {
@@ -122,6 +129,13 @@ export interface SkillsListResponse {
122
129
  skills: SlimSkillResponse[];
123
130
  }
124
131
 
132
+ export interface SkillsListFilteredResponse {
133
+ type: "skills_list_response";
134
+ skills: SlimSkillResponse[];
135
+ categoryCounts: Record<string, number>;
136
+ totalCount: number;
137
+ }
138
+
125
139
  export interface SkillStateChanged {
126
140
  type: "skills_state_changed";
127
141
  name: string;
@@ -159,6 +173,7 @@ interface ClawhubSkillDetail extends SkillDetailBase {
159
173
  installs: number;
160
174
  reports: number;
161
175
  publishedAt?: string;
176
+ version: string;
162
177
  // Enrichment fields (from clawhubInspect):
163
178
  owner?: { handle: string; displayName: string; image?: string } | null;
164
179
  stats?: {
@@ -177,6 +192,7 @@ interface SkillsshSkillDetail extends SkillDetailBase {
177
192
  slug: string;
178
193
  sourceRepo: string;
179
194
  installs: number;
195
+ audit?: Record<string, PartnerAudit>;
180
196
  }
181
197
 
182
198
  interface CustomSkillDetail extends SkillDetailBase {
@@ -184,6 +184,8 @@ interface UiSurfaceShowBase {
184
184
  display?: "inline" | "panel";
185
185
  /** The message ID that this surface belongs to (for history loading). */
186
186
  messageId?: string;
187
+ /** When `true`, clicking an action does not dismiss the surface — the client keeps the card visible and only marks the clicked `actionId` as spent so siblings remain clickable. */
188
+ persistent?: boolean;
187
189
  }
188
190
 
189
191
  export interface UiSurfaceShowCard extends UiSurfaceShowBase {
@@ -7,6 +7,7 @@ import {
7
7
  setBroadcastToAllClients,
8
8
  } from "../acp/index.js";
9
9
  import { enrichMessageWithSourcePaths } from "../agent/attachments.js";
10
+ import type { AgentEvent } from "../agent/loop.js";
10
11
  import {
11
12
  createAssistantMessage,
12
13
  createUserMessage,
@@ -23,7 +24,9 @@ import { getConfig } from "../config/loader.js";
23
24
  import { onContactChange } from "../contacts/contact-events.js";
24
25
  import type { CesClient } from "../credential-execution/client.js";
25
26
  import type { CesProcessManager } from "../credential-execution/process-manager.js";
27
+ import type { FilingService } from "../filing/filing-service.js";
26
28
  import type { HeartbeatService } from "../heartbeat/heartbeat-service.js";
29
+ import { CliIpcServer } from "../ipc/cli-server.js";
27
30
  import { getApp, getAppDirPath, isMultifileApp } from "../memory/app-store.js";
28
31
  import * as attachmentsStore from "../memory/attachments-store.js";
29
32
  import {
@@ -39,11 +42,19 @@ import {
39
42
  setConversationOriginChannelIfUnset,
40
43
  setConversationOriginInterfaceIfUnset,
41
44
  } from "../memory/conversation-crud.js";
42
- import { updateMetaFile } from "../memory/conversation-disk-view.js";
45
+ import {
46
+ syncMessageToDisk,
47
+ updateMetaFile,
48
+ } from "../memory/conversation-disk-view.js";
43
49
  import { getOrCreateConversation } from "../memory/conversation-key-store.js";
50
+ import { syncIdentityNameToPlatform } from "../platform/sync-identity.js";
44
51
  import { buildSystemPrompt } from "../prompts/system-prompt.js";
45
52
  import { RateLimitProvider } from "../providers/ratelimit.js";
46
53
  import { getProvider, initializeProviders } from "../providers/registry.js";
54
+ import {
55
+ registerDefaultWakeResolver,
56
+ type WakeTarget,
57
+ } from "../runtime/agent-wake.js";
47
58
  import { buildAssistantEvent } from "../runtime/assistant-event.js";
48
59
  import { assistantEventHub } from "../runtime/assistant-event-hub.js";
49
60
  import { DAEMON_INTERNAL_ASSISTANT_ID } from "../runtime/assistant-scope.js";
@@ -59,6 +70,7 @@ import { appendEventToStream } from "../signals/event-stream.js";
59
70
  import { registerUserMessageCallback } from "../signals/user-message.js";
60
71
  import { getSubagentManager } from "../subagent/index.js";
61
72
  import { summarizeToolInput } from "../tools/tool-input-summary.js";
73
+ import { createAbortReason } from "../util/abort-reasons.js";
62
74
  import { getLogger } from "../util/logger.js";
63
75
  import {
64
76
  getAvatarImagePath,
@@ -77,6 +89,7 @@ import {
77
89
  DEFAULT_MEMORY_POLICY,
78
90
  } from "./conversation.js";
79
91
  import { ConversationEvictor } from "./conversation-evictor.js";
92
+ import { registerLaunchConversationDeps } from "./conversation-launch.js";
80
93
  import { formatCompactResult } from "./conversation-process.js";
81
94
  import { resolveChannelCapabilities } from "./conversation-runtime-assembly.js";
82
95
  import { resolveSlash, type SlashContext } from "./conversation-slash.js";
@@ -288,6 +301,7 @@ export class DaemonServer {
288
301
  // Composed subsystems
289
302
  private configWatcher = new ConfigWatcher();
290
303
  private appSourceWatcher = new AppSourceWatcher();
304
+ private cliIpc = new CliIpcServer();
291
305
 
292
306
  // CES (Credential Execution Service) — process-level singleton.
293
307
  // Lifecycle is managed by startCesProcess() in lifecycle.ts; the server
@@ -364,6 +378,17 @@ export class DaemonServer {
364
378
  return this._heartbeatService;
365
379
  }
366
380
 
381
+ /** Optional filing service reference for "Run Now" from the UI. */
382
+ private _filingService?: FilingService;
383
+
384
+ setFilingService(service: FilingService): void {
385
+ this._filingService = service;
386
+ }
387
+
388
+ getFilingService(): FilingService | undefined {
389
+ return this._filingService;
390
+ }
391
+
367
392
  private deriveMemoryPolicy(conversationId: string): ConversationMemoryPolicy {
368
393
  const conversationType = getConversationType(conversationId);
369
394
  if (conversationType === "private") {
@@ -545,11 +570,32 @@ export class DaemonServer {
545
570
  emoji: fields.emoji,
546
571
  home: fields.home,
547
572
  });
573
+
574
+ // Best-effort sync of the assistant name to the platform record.
575
+ if (fields.name) {
576
+ syncIdentityNameToPlatform(fields.name);
577
+ }
548
578
  } catch (err) {
549
579
  log.error({ err }, "Failed to broadcast identity change");
550
580
  }
551
581
  }
552
582
 
583
+ /** Best-effort sync of the IDENTITY.md name to the platform record. */
584
+ private syncIdentityToPlatform(): void {
585
+ try {
586
+ const identityPath = getWorkspacePromptPath("IDENTITY.md");
587
+ const content = existsSync(identityPath)
588
+ ? readFileSync(identityPath, "utf-8")
589
+ : "";
590
+ const fields = parseIdentityFields(content);
591
+ if (fields.name) {
592
+ syncIdentityNameToPlatform(fields.name);
593
+ }
594
+ } catch (err) {
595
+ log.error({ err }, "Failed to sync identity to platform at startup");
596
+ }
597
+ }
598
+
553
599
  private broadcastConfigChanged(): void {
554
600
  this.broadcast({ type: "config_changed" });
555
601
  }
@@ -626,7 +672,13 @@ export class DaemonServer {
626
672
  const conversation = this.conversations.get(conversationId);
627
673
  if (!conversation) return false;
628
674
  this.evictor.touch(conversationId);
629
- conversation.abort();
675
+ conversation.abort(
676
+ createAbortReason(
677
+ "signal_cancel",
678
+ "registerCancelCallback",
679
+ conversationId,
680
+ ),
681
+ );
630
682
  getSubagentManager().abortAllForParent(conversationId);
631
683
  return true;
632
684
  });
@@ -753,6 +805,56 @@ export class DaemonServer {
753
805
  return { accepted: true };
754
806
  });
755
807
 
808
+ // Install the default resolver for `wakeAgentForOpportunity()` so
809
+ // internal subsystems (e.g. the Meet chat-opportunity detector wired
810
+ // up in `MeetSessionManager`) can invoke it without having to build
811
+ // a `WakeTarget` adapter themselves. The adapter wraps a live
812
+ // `Conversation` fetched from the in-memory map / hydrated from the
813
+ // DB, exposing only the narrow surface the wake helper needs.
814
+ registerDefaultWakeResolver(async (conversationId) => {
815
+ try {
816
+ const conversation = await this.getOrCreateConversation(conversationId);
817
+ return conversationToWakeTarget(conversation);
818
+ } catch (err) {
819
+ log.warn(
820
+ { err, conversationId },
821
+ "agent-wake default resolver: failed to hydrate conversation",
822
+ );
823
+ return null;
824
+ }
825
+ });
826
+
827
+ // Start the CLI IPC server. Built-in methods (wake_conversation) are
828
+ // registered by the constructor; CLI commands connect to this socket to
829
+ // invoke daemon-side operations that require in-process state.
830
+ this.cliIpc.start();
831
+
832
+ // Wire the launchConversation helper to daemon-side state so
833
+ // handleSurfaceAction can spawn conversations through it.
834
+ registerLaunchConversationDeps({
835
+ getOrCreateConversation: (id, options) =>
836
+ this.getOrCreateConversation(id, options),
837
+ persistAndProcessMessage: (
838
+ conversationId,
839
+ content,
840
+ attachmentIds,
841
+ options,
842
+ sourceChannel,
843
+ sourceInterface,
844
+ ) =>
845
+ this.persistAndProcessMessage(
846
+ conversationId,
847
+ content,
848
+ attachmentIds,
849
+ options,
850
+ sourceChannel,
851
+ sourceInterface,
852
+ ),
853
+ publishAssistantEvent: (msg, conversationId) =>
854
+ this.publishAssistantEvent(msg, conversationId),
855
+ getAssistantId: () => this.assistantId,
856
+ });
857
+
756
858
  this.configWatcher.start(
757
859
  () => this.evictConversationsForReload(),
758
860
  () => this.broadcastIdentityChanged(),
@@ -762,6 +864,8 @@ export class DaemonServer {
762
864
  () => this.broadcastFeatureFlagsChanged(),
763
865
  );
764
866
 
867
+ this.syncIdentityToPlatform();
868
+
765
869
  this.appSourceWatcher.start((appId) => this.handleAppSourceChange(appId));
766
870
 
767
871
  // Broadcast contacts_changed to all clients when any contact mutation occurs.
@@ -778,6 +882,7 @@ export class DaemonServer {
778
882
  this.evictor.stop();
779
883
  this.configWatcher.stop();
780
884
  this.appSourceWatcher.stop();
885
+ this.cliIpc.stop();
781
886
  if (this.unsubscribeContactChange) {
782
887
  this.unsubscribeContactChange();
783
888
  this.unsubscribeContactChange = null;
@@ -959,6 +1064,9 @@ export class DaemonServer {
959
1064
  memoryPolicy,
960
1065
  sharedCesClient,
961
1066
  storedOptions?.speed,
1067
+ undefined,
1068
+ storedOptions?.modelIntent,
1069
+ storedOptions?.modelOverride,
962
1070
  );
963
1071
  newConversation.updateClient(sendToClient, true);
964
1072
  await newConversation.loadFromDb();
@@ -997,6 +1105,15 @@ export class DaemonServer {
997
1105
  // overwrite the in-flight conversation's transportHints.
998
1106
  if (!conversation.isProcessing()) {
999
1107
  this.applyTransportMetadata(conversation, options);
1108
+ // trustContext is reapplied here only when the conversation is idle,
1109
+ // so concurrent requests cannot overwrite an in-flight turn's guardian
1110
+ // scope. Direct callers (e.g. schedule-routes run-now) that invoke
1111
+ // processMessage without going through prepareConversationForMessage
1112
+ // rely on this to pick up the trustContext passed in options.
1113
+ // prepareConversationForMessage also reapplies after its own idle check.
1114
+ if (options?.trustContext !== undefined) {
1115
+ conversation.setTrustContext(options.trustContext);
1116
+ }
1000
1117
  }
1001
1118
  this.evictor.touch(conversationId);
1002
1119
  }
@@ -1584,3 +1701,231 @@ function extractConversationId(msg: ServerMessage): string | undefined {
1584
1701
  }
1585
1702
  return undefined;
1586
1703
  }
1704
+
1705
+ /**
1706
+ * Translate a raw {@link AgentEvent} from the agent loop into the
1707
+ * corresponding {@link ServerMessage} wire frame. The normal user-turn
1708
+ * path does this via the full state-aware handler in
1709
+ * `conversation-agent-loop-handlers.ts`; the wake path has no tool
1710
+ * accounting, title generation, or activity-state tracking to worry
1711
+ * about, so we only need the subset that produces client-visible
1712
+ * frames. Events that have no client-visible wire shape (usage, error,
1713
+ * preview/input-json deltas, etc.) are dropped — they produce no UI.
1714
+ *
1715
+ * Keeping this translator co-located with the wake adapter preserves
1716
+ * the runtime/daemon layering: `runtime/agent-wake.ts` never imports
1717
+ * `message-protocol.ts` or wire shapes, and the daemon owns all
1718
+ * translation from agent-loop semantics to client frames.
1719
+ */
1720
+ function translateAgentEventToServerMessage(
1721
+ event: AgentEvent,
1722
+ conversationId: string,
1723
+ ): ServerMessage | null {
1724
+ switch (event.type) {
1725
+ case "text_delta":
1726
+ return {
1727
+ type: "assistant_text_delta",
1728
+ text: event.text,
1729
+ conversationId,
1730
+ };
1731
+ case "thinking_delta":
1732
+ return {
1733
+ type: "assistant_thinking_delta",
1734
+ thinking: event.thinking,
1735
+ conversationId,
1736
+ };
1737
+ case "tool_use":
1738
+ return {
1739
+ type: "tool_use_start",
1740
+ toolName: event.name,
1741
+ input: event.input,
1742
+ conversationId,
1743
+ toolUseId: event.id,
1744
+ };
1745
+ case "tool_use_preview_start":
1746
+ return {
1747
+ type: "tool_use_preview_start",
1748
+ toolUseId: event.toolUseId,
1749
+ toolName: event.toolName,
1750
+ conversationId,
1751
+ };
1752
+ case "tool_output_chunk":
1753
+ return {
1754
+ type: "tool_output_chunk",
1755
+ chunk: event.chunk,
1756
+ conversationId,
1757
+ toolUseId: event.toolUseId,
1758
+ };
1759
+ case "tool_result": {
1760
+ const imageBlocks = event.contentBlocks?.filter(
1761
+ (b): b is Extract<typeof b, { type: "image" }> => b.type === "image",
1762
+ );
1763
+ const imageDataList = imageBlocks?.length
1764
+ ? imageBlocks.map((b) => b.source.data)
1765
+ : undefined;
1766
+ return {
1767
+ type: "tool_result",
1768
+ toolName: "",
1769
+ result: event.content,
1770
+ isError: event.isError,
1771
+ diff: event.diff,
1772
+ status: event.status,
1773
+ conversationId,
1774
+ imageData: imageDataList?.[0],
1775
+ imageDataList,
1776
+ toolUseId: event.toolUseId,
1777
+ };
1778
+ }
1779
+ case "server_tool_start":
1780
+ return {
1781
+ type: "tool_use_start",
1782
+ toolName: event.name,
1783
+ input: event.input,
1784
+ conversationId,
1785
+ toolUseId: event.toolUseId,
1786
+ };
1787
+ case "server_tool_complete": {
1788
+ let resultText = "";
1789
+ if (Array.isArray(event.content) && event.content.length > 0) {
1790
+ resultText = (event.content as unknown[])
1791
+ .filter(
1792
+ (r): r is { type: string; title: string; url: string } =>
1793
+ typeof r === "object" &&
1794
+ r != null &&
1795
+ (r as { type?: string }).type === "web_search_result",
1796
+ )
1797
+ .map((r) => `${r.title}\n${r.url}`)
1798
+ .join("\n\n");
1799
+ }
1800
+ return {
1801
+ type: "tool_result",
1802
+ toolName: "web_search",
1803
+ result: resultText,
1804
+ isError: event.isError,
1805
+ conversationId,
1806
+ toolUseId: event.toolUseId,
1807
+ };
1808
+ }
1809
+ case "message_complete":
1810
+ return {
1811
+ type: "message_complete",
1812
+ conversationId,
1813
+ };
1814
+ // No wire frame for these — usage/error/input_json_delta are either
1815
+ // server-internal (accounting/classification) or app-only debug
1816
+ // streams the client doesn't surface for wake-originated turns.
1817
+ case "input_json_delta":
1818
+ case "usage":
1819
+ case "error":
1820
+ return null;
1821
+ }
1822
+ }
1823
+
1824
+ /**
1825
+ * Adapt a live {@link Conversation} to the narrow {@link WakeTarget}
1826
+ * surface expected by `wakeAgentForOpportunity()`. Kept here so the
1827
+ * runtime-level wake helper stays decoupled from the heavyweight
1828
+ * conversation class (see `registerDefaultWakeResolver` above).
1829
+ *
1830
+ * Routing notes:
1831
+ * - `emitAgentEvent` dispatches via `broadcastToAllClients` rather
1832
+ * than `sendToClient`. Several signal-injected paths reset
1833
+ * `sendToClient` to a no-op in their `finally` blocks (see the
1834
+ * `updateClient(() => {}, true)` calls in `persistAndProcessMessage`
1835
+ * / `processMessage`), so a wake fired on such a conversation would
1836
+ * find a silent sink. `broadcastToAllClients` is wired to
1837
+ * `this.broadcast(msg)` at construction time and always reaches the
1838
+ * hub, regardless of which sender the most-recent user turn left
1839
+ * behind.
1840
+ * - `persistTailMessage` mirrors the canonical user-turn handlers
1841
+ * (`handleMessageComplete` / the tool-result block in
1842
+ * `conversation-agent-loop-handlers.ts`): builds channel/interface
1843
+ * metadata via `provenanceFromTrustContext` plus the live turn
1844
+ * channel/interface contexts, persists with metadata, and syncs the
1845
+ * resulting row to the disk view so wake-produced messages appear
1846
+ * in the on-disk transcript and carry provenance tags.
1847
+ * - `drainQueue` delegates to the conversation so any user messages
1848
+ * queued while the wake was running are processed. The wake helper
1849
+ * calls this in its finally AFTER `markProcessing(false)`; the
1850
+ * order matters because `enqueueMessage` only queues when
1851
+ * `processing === true`.
1852
+ */
1853
+ function conversationToWakeTarget(conversation: Conversation): WakeTarget {
1854
+ return {
1855
+ conversationId: conversation.conversationId,
1856
+ agentLoop: conversation.agentLoop,
1857
+ getMessages: () => conversation.getMessages(),
1858
+ pushMessage: (msg) => {
1859
+ conversation.messages.push(msg);
1860
+ },
1861
+ emitAgentEvent: (event) => {
1862
+ const frame = translateAgentEventToServerMessage(
1863
+ event,
1864
+ conversation.conversationId,
1865
+ );
1866
+ if (!frame) return;
1867
+ // Prefer `broadcastToAllClients` (wired to the hub at construction
1868
+ // time and always live) over `sendToClient` (which several
1869
+ // signal-injected paths reset to `() => {}` in their finally
1870
+ // blocks). Fall back to `sendToClient` when the broadcaster is
1871
+ // missing (e.g. in tests that construct a Conversation directly).
1872
+ if (conversation.broadcastToAllClients) {
1873
+ conversation.broadcastToAllClients(frame);
1874
+ } else {
1875
+ conversation.sendToClient(frame);
1876
+ }
1877
+ },
1878
+ isProcessing: () => conversation.isProcessing(),
1879
+ markProcessing: (on) => {
1880
+ conversation.processing = on;
1881
+ },
1882
+ persistTailMessage: async (message) => {
1883
+ // Build metadata that mirrors the canonical handlers in
1884
+ // `conversation-agent-loop-handlers.ts`. If the live turn channel
1885
+ // / interface contexts are missing (a wake can fire on a
1886
+ // conversation that has never run a user turn), fall back to the
1887
+ // conversation's origin channel/interface defaults (`"vellum"`)
1888
+ // so persisted rows still carry valid channel/interface ids.
1889
+ const turnChannelCtx = conversation.getTurnChannelContext();
1890
+ const turnInterfaceCtx = conversation.getTurnInterfaceContext();
1891
+ const metadata: Record<string, unknown> = {
1892
+ ...provenanceFromTrustContext(conversation.trustContext),
1893
+ userMessageChannel: turnChannelCtx?.userMessageChannel ?? "vellum",
1894
+ assistantMessageChannel:
1895
+ turnChannelCtx?.assistantMessageChannel ?? "vellum",
1896
+ userMessageInterface:
1897
+ turnInterfaceCtx?.userMessageInterface ?? "vellum",
1898
+ assistantMessageInterface:
1899
+ turnInterfaceCtx?.assistantMessageInterface ?? "vellum",
1900
+ };
1901
+ const persisted = await addMessage(
1902
+ conversation.conversationId,
1903
+ message.role,
1904
+ JSON.stringify(message.content),
1905
+ metadata,
1906
+ );
1907
+ // Sync the persisted row to the disk view so wake-produced
1908
+ // messages appear in the on-disk transcript and tools that read
1909
+ // from disk (e.g. `messages.jsonl`-based diagnostics) see them.
1910
+ // Mirrors the `syncMessageToDisk(...)` calls in the canonical
1911
+ // handlers — best-effort because a sync failure must not strand
1912
+ // the in-memory tail.
1913
+ try {
1914
+ const convRow = getConversation(conversation.conversationId);
1915
+ if (convRow) {
1916
+ syncMessageToDisk(
1917
+ conversation.conversationId,
1918
+ persisted.id,
1919
+ convRow.createdAt,
1920
+ );
1921
+ }
1922
+ } catch (err) {
1923
+ log.warn(
1924
+ { err, conversationId: conversation.conversationId },
1925
+ "wake adapter: syncMessageToDisk failed (non-fatal)",
1926
+ );
1927
+ }
1928
+ },
1929
+ drainQueue: () => conversation.drainQueue(),
1930
+ };
1931
+ }
@@ -1,5 +1,6 @@
1
1
  import * as Sentry from "@sentry/node";
2
2
 
3
+ import type { BackupWorkerHandle } from "../backup/backup-worker.js";
3
4
  import type { FilingService } from "../filing/filing-service.js";
4
5
  import type { HeartbeatService } from "../heartbeat/heartbeat-service.js";
5
6
  import type { HookManager } from "../hooks/manager.js";
@@ -13,6 +14,7 @@ import { getLogger } from "../util/logger.js";
13
14
  import { getEnrichmentService } from "../workspace/commit-message-enrichment-service.js";
14
15
  import type { WorkspaceHeartbeatService } from "../workspace/heartbeat-service.js";
15
16
  import type { DaemonServer } from "./server.js";
17
+ import { runShutdownHooks } from "./shutdown-registry.js";
16
18
 
17
19
  const log = getLogger("lifecycle");
18
20
 
@@ -24,7 +26,9 @@ export interface ShutdownDeps {
24
26
  hookManager: HookManager;
25
27
  runtimeHttp: RuntimeHttpServer | null;
26
28
  scheduler: { stop(): void };
29
+ feedScheduler: { stop(): void } | null;
27
30
  getMemoryWorker: () => { stop(): void } | null;
31
+ getBackupWorker: () => BackupWorkerHandle | null;
28
32
  getQdrantManager: () => QdrantManager | null;
29
33
  mcpManager: McpServerManager | null;
30
34
  telemetryReporter: { stop(): Promise<void> } | null;
@@ -45,11 +49,18 @@ export function installShutdownHandlers(deps: ShutdownDeps): void {
45
49
  // Force exit if graceful shutdown takes too long.
46
50
  // Set this BEFORE awaiting heartbeat stop and triggering daemon-stop hooks
47
51
  // so it covers all potentially-blocking async shutdown work.
52
+ //
53
+ // 20s budget: 15s reserved for Meet session teardown
54
+ // (`MeetSessionManager.shutdownAll`), plus ~5s for the remaining
55
+ // daemon work (workspace commits, server drain, enrichment, telemetry,
56
+ // mcp, qdrant, sqlite checkpoint). Without a live Meet session the
57
+ // rest of the shutdown routinely completes in under a second, so this
58
+ // bump only changes behavior for the stuck-shutdown path.
48
59
  const forceTimer = setTimeout(() => {
49
60
  log.warn("Graceful shutdown timed out, forcing exit");
50
61
  deps.cleanupPidFile();
51
62
  process.exit(1);
52
- }, 10_000);
63
+ }, 20_000);
53
64
  forceTimer.unref();
54
65
 
55
66
  await deps.workspaceHeartbeat.stop();
@@ -62,6 +73,15 @@ export function installShutdownHandlers(deps: ShutdownDeps): void {
62
73
  // Don't let hook failures block shutdown
63
74
  }
64
75
 
76
+ // Run registered skill shutdown hooks (e.g. meet-join session teardown)
77
+ // before stopping the server so any HTTP round-trips and SSE emissions
78
+ // still have live transports.
79
+ try {
80
+ await runShutdownHooks("daemon-shutdown");
81
+ } catch (err) {
82
+ log.warn({ err }, "Skill shutdown hooks failed (non-fatal)");
83
+ }
84
+
65
85
  // Commit any uncommitted workspace changes before stopping the server.
66
86
  // This ensures no workspace state is lost during graceful shutdown.
67
87
  try {
@@ -111,7 +131,9 @@ export function installShutdownHandlers(deps: ShutdownDeps): void {
111
131
  await browserManager.closeAllPages();
112
132
  cleanupShellOutputTempFiles();
113
133
  deps.scheduler.stop();
134
+ deps.feedScheduler?.stop();
114
135
  deps.getMemoryWorker()?.stop();
136
+ deps.getBackupWorker()?.stop();
115
137
 
116
138
  if (deps.mcpManager) {
117
139
  try {
@@ -123,9 +145,15 @@ export function installShutdownHandlers(deps: ShutdownDeps): void {
123
145
 
124
146
  await deps.getQdrantManager()?.stop();
125
147
 
126
- // Checkpoint WAL and close SQLite so no writes are lost on exit.
127
- // Checkpoint and close are in separate try blocks so that close()
128
- // always runs even if checkpointing throws (e.g. SQLITE_BUSY).
148
+ // Optimize query planner statistics before closing so they persist for
149
+ // the next session. Checkpoint WAL and close SQLite so no writes are
150
+ // lost on exit. Each step is in its own try block so later steps still
151
+ // run if an earlier one throws (e.g. SQLITE_BUSY).
152
+ try {
153
+ getSqlite().exec("PRAGMA optimize");
154
+ } catch (err) {
155
+ log.warn({ err }, "PRAGMA optimize at shutdown failed (non-fatal)");
156
+ }
129
157
  try {
130
158
  getSqlite().exec("PRAGMA wal_checkpoint(TRUNCATE)");
131
159
  } catch (err) {
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Registry for skill shutdown hooks.
3
+ *
4
+ * Skills register async shutdown callbacks at initialization time. The daemon
5
+ * calls {@link runShutdownHooks} during graceful shutdown so skill-owned
6
+ * resources (containers, sockets, etc.) are torn down before the process exits.
7
+ */
8
+
9
+ import { getLogger } from "../util/logger.js";
10
+
11
+ const log = getLogger("shutdown-registry");
12
+
13
+ type ShutdownHook = (reason: string) => Promise<void>;
14
+
15
+ const hooks = new Map<string, ShutdownHook>();
16
+
17
+ /**
18
+ * Register a named shutdown hook. Called by skills at initialization time.
19
+ * If a hook with the same name already exists it is silently replaced (supports
20
+ * hot-reload).
21
+ */
22
+ export function registerShutdownHook(name: string, hook: ShutdownHook): void {
23
+ hooks.set(name, hook);
24
+ log.info({ name }, "Shutdown hook registered");
25
+ }
26
+
27
+ /**
28
+ * Run all registered shutdown hooks. Each hook receives a human-readable
29
+ * `reason` string (e.g. "daemon-shutdown"). Failures are logged but do not
30
+ * prevent other hooks from running.
31
+ */
32
+ export async function runShutdownHooks(reason: string): Promise<void> {
33
+ for (const [name, hook] of hooks) {
34
+ try {
35
+ await hook(reason);
36
+ } catch (err) {
37
+ log.warn({ err, name }, `Shutdown hook "${name}" failed (non-fatal)`);
38
+ }
39
+ }
40
+ }
@@ -215,7 +215,9 @@ registerHook(
215
215
  const SETTING_TO_KEY: Record<string, string> = {
216
216
  activation_key: "pttActivationKey",
217
217
  tts_voice_id: "ttsVoiceId",
218
+ tts_provider: "ttsProvider",
218
219
  conversation_timeout: "voiceConversationTimeoutSeconds",
220
+ fish_audio_reference_id: "fishAudioReferenceId",
219
221
  };
220
222
  const key = SETTING_TO_KEY[setting];
221
223
  if (!key) return;
@@ -228,6 +230,13 @@ registerHook(
228
230
  coerced = typeof raw === "number" ? raw : Number(raw);
229
231
  } else if (setting === "tts_voice_id" && typeof raw === "string") {
230
232
  coerced = raw.trim();
233
+ } else if (
234
+ setting === "fish_audio_reference_id" &&
235
+ typeof raw === "string"
236
+ ) {
237
+ coerced = raw.trim();
238
+ } else if (setting === "tts_provider" && typeof raw === "string") {
239
+ coerced = raw.trim();
231
240
  }
232
241
  broadcastToAllClients?.({
233
242
  type: "client_settings_update",