@vellumai/assistant 0.8.1 → 0.8.2

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 (506) hide show
  1. package/ARCHITECTURE.md +2 -7
  2. package/Dockerfile +75 -1
  3. package/bun.lock +11 -1
  4. package/docker-entrypoint.sh +5 -0
  5. package/docker-init-apt-root.sh +94 -0
  6. package/docker-kata-apt-env.sh +39 -0
  7. package/docs/plugins.md +88 -47
  8. package/docs/skills.md +9 -7
  9. package/examples/plugins/echo/README.md +27 -27
  10. package/examples/plugins/echo/package.json +3 -0
  11. package/examples/plugins/echo/register.ts +31 -31
  12. package/node_modules/@vellumai/slack-text/src/index.test.ts +114 -14
  13. package/node_modules/@vellumai/slack-text/src/index.ts +82 -18
  14. package/openapi.yaml +325 -3
  15. package/package.json +3 -1
  16. package/scripts/generate-openapi.ts +83 -10
  17. package/scripts/sync-llm-catalog.ts +2 -2
  18. package/scripts/sync-web-search-catalog.ts +47 -25
  19. package/src/__tests__/agent-image-optimize.test.ts +11 -3
  20. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +131 -0
  21. package/src/__tests__/anthropic-provider.test.ts +45 -0
  22. package/src/__tests__/app-builder-tool-scripts.test.ts +9 -3
  23. package/src/__tests__/app-executors.test.ts +220 -4
  24. package/src/__tests__/auto-analysis-end-to-end.test.ts +35 -0
  25. package/src/__tests__/bundled-asset.test.ts +6 -6
  26. package/src/__tests__/channel-availability-routes.test.ts +206 -0
  27. package/src/__tests__/channel-delivery-store.test.ts +289 -1
  28. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -1
  29. package/src/__tests__/clawhub.test.ts +75 -16
  30. package/src/__tests__/compactor-tail-resolution.test.ts +41 -0
  31. package/src/__tests__/config-schema.test.ts +21 -0
  32. package/src/__tests__/config-set-route.test.ts +80 -0
  33. package/src/__tests__/config-sounds-sync.test.ts +97 -0
  34. package/src/__tests__/config-watcher-skill-reseed.test.ts +453 -0
  35. package/src/__tests__/context-search-conversations-source.test.ts +117 -2
  36. package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
  37. package/src/__tests__/context-search-workspace-source.test.ts +7 -0
  38. package/src/__tests__/context-token-estimator.test.ts +1 -0
  39. package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
  40. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
  41. package/src/__tests__/conversation-agent-loop-overflow.test.ts +92 -92
  42. package/src/__tests__/conversation-agent-loop.test.ts +2 -0
  43. package/src/__tests__/conversation-error.test.ts +42 -3
  44. package/src/__tests__/conversation-fork-crud.test.ts +82 -0
  45. package/src/__tests__/conversation-inference-profile-route.test.ts +40 -4
  46. package/src/__tests__/conversation-lifecycle.test.ts +173 -0
  47. package/src/__tests__/conversation-message-sync-tags.test.ts +97 -0
  48. package/src/__tests__/conversation-pairing.test.ts +54 -0
  49. package/src/__tests__/conversation-process-callsite.test.ts +4 -1
  50. package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -1
  51. package/src/__tests__/conversation-queue.test.ts +4 -1
  52. package/src/__tests__/conversation-runtime-assembly.test.ts +76 -9
  53. package/src/__tests__/conversation-slash-queue.test.ts +59 -1
  54. package/src/__tests__/conversation-slash-unknown.test.ts +4 -1
  55. package/src/__tests__/conversation-surfaces-table-action.test.ts +360 -0
  56. package/src/__tests__/conversation-sync-tags.test.ts +235 -0
  57. package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
  58. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
  59. package/src/__tests__/credential-security-invariants.test.ts +3 -2
  60. package/src/__tests__/db-slack-external-content-normalization.test.ts +301 -0
  61. package/src/__tests__/delete-managed-skill-tool.test.ts +55 -13
  62. package/src/__tests__/disk-pressure-tools.test.ts +1 -0
  63. package/src/__tests__/dm-backfill.test.ts +121 -10
  64. package/src/__tests__/document-tool-security.test.ts +258 -0
  65. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
  66. package/src/__tests__/edit-propagation.test.ts +33 -0
  67. package/src/__tests__/empty-response-pipeline.test.ts +0 -4
  68. package/src/__tests__/external-plugin-loader.test.ts +60 -36
  69. package/src/__tests__/filing-service.test.ts +140 -0
  70. package/src/__tests__/get-skill-detail-audit.test.ts +0 -4
  71. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +43 -62
  72. package/src/__tests__/helpers/tar-fixtures.ts +39 -0
  73. package/src/__tests__/helpers/wait-for.ts +21 -0
  74. package/src/__tests__/history-repair-pipeline.test.ts +0 -3
  75. package/src/__tests__/history-repair.test.ts +73 -0
  76. package/src/__tests__/host-app-control-proxy.test.ts +266 -10
  77. package/src/__tests__/image-credentials.test.ts +1 -1
  78. package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
  79. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +1 -1
  80. package/src/__tests__/inference-profile-reaper.test.ts +4 -2
  81. package/src/__tests__/inference-profile-session-handler.test.ts +18 -6
  82. package/src/__tests__/inference-profile-session-ipc.test.ts +17 -5
  83. package/src/__tests__/injector-chain.test.ts +10 -8
  84. package/src/__tests__/install-skill-routing.test.ts +155 -37
  85. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +92 -3
  86. package/src/__tests__/list-messages-page-latest.test.ts +55 -0
  87. package/src/__tests__/llm-call-pipeline.test.ts +0 -3
  88. package/src/__tests__/llm-catalog-parity.test.ts +55 -13
  89. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +34 -0
  90. package/src/__tests__/llm-request-log-source-factory.test.ts +29 -53
  91. package/src/__tests__/llm-usage-store.test.ts +114 -0
  92. package/src/__tests__/managed-profile-guard.test.ts +31 -29
  93. package/src/__tests__/managed-skill-lifecycle.test.ts +109 -18
  94. package/src/__tests__/managed-store.test.ts +84 -192
  95. package/src/__tests__/media-generate-image.test.ts +1 -1
  96. package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -2
  97. package/src/__tests__/messages-after-tiebreaker.test.ts +122 -0
  98. package/src/__tests__/oauth-commands-routes.test.ts +168 -16
  99. package/src/__tests__/oauth-provider-profiles.test.ts +9 -0
  100. package/src/__tests__/openai-provider.test.ts +24 -0
  101. package/src/__tests__/openai-responses-cutover-guard.test.ts +17 -9
  102. package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -2
  103. package/src/__tests__/persistence-pipeline.test.ts +0 -2
  104. package/src/__tests__/{managed-proxy-context.test.ts → platform-proxy-context.test.ts} +1 -1
  105. package/src/__tests__/platform.test.ts +2 -0
  106. package/src/__tests__/plugin-api-shim.test.ts +125 -0
  107. package/src/__tests__/plugin-bootstrap.test.ts +10 -36
  108. package/src/__tests__/plugin-external-api.test.ts +68 -0
  109. package/src/__tests__/plugin-registry.test.ts +0 -77
  110. package/src/__tests__/plugin-route-contribution.test.ts +0 -1
  111. package/src/__tests__/plugin-skill-contribution.test.ts +0 -2
  112. package/src/__tests__/plugin-tool-contribution.test.ts +16 -15
  113. package/src/__tests__/plugin-types.test.ts +3 -13
  114. package/src/__tests__/process-message-background-slack.test.ts +8 -1
  115. package/src/__tests__/process-message-display-content.test.ts +421 -0
  116. package/src/__tests__/provider-catalog-visibility.test.ts +142 -0
  117. package/src/__tests__/provider-error-scenarios.test.ts +111 -0
  118. package/src/__tests__/{provider-managed-proxy-integration.test.ts → provider-platform-proxy-integration.test.ts} +8 -8
  119. package/src/__tests__/scaffold-managed-skill-tool.test.ts +65 -13
  120. package/src/__tests__/schedule-routes.test.ts +50 -3
  121. package/src/__tests__/schedule-store.test.ts +94 -0
  122. package/src/__tests__/scheduler-reuse-conversation.test.ts +54 -7
  123. package/src/__tests__/schema-transforms.test.ts +20 -0
  124. package/src/__tests__/search-skills-unified.test.ts +0 -5
  125. package/src/__tests__/server-history-render.test.ts +43 -0
  126. package/src/__tests__/skill-load-feature-flag.test.ts +0 -12
  127. package/src/__tests__/skill-load-tool.test.ts +27 -89
  128. package/src/__tests__/skill-memory.test.ts +23 -3
  129. package/src/__tests__/skills-file-content-endpoint.test.ts +9 -38
  130. package/src/__tests__/skills-files-catalog-fallback.test.ts +0 -3
  131. package/src/__tests__/skills-install-extract.test.ts +49 -38
  132. package/src/__tests__/skills-install-staging.test.ts +159 -0
  133. package/src/__tests__/skills-uninstall.test.ts +9 -41
  134. package/src/__tests__/skills.test.ts +51 -58
  135. package/src/__tests__/slack-channel-config.test.ts +9 -0
  136. package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
  137. package/src/__tests__/system-prompt.test.ts +737 -63
  138. package/src/__tests__/terminal-tools.test.ts +28 -1
  139. package/src/__tests__/thread-backfill.test.ts +557 -27
  140. package/src/__tests__/title-generate-pipeline.test.ts +0 -13
  141. package/src/__tests__/token-estimate-pipeline.test.ts +0 -3
  142. package/src/__tests__/tool-error-pipeline.test.ts +0 -3
  143. package/src/__tests__/tool-execute-pipeline.test.ts +0 -5
  144. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
  145. package/src/__tests__/tool-executor.test.ts +16 -4
  146. package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -12
  147. package/src/__tests__/turn-events-store.test.ts +256 -0
  148. package/src/__tests__/twilio-routes.test.ts +4 -0
  149. package/src/__tests__/user-plugin-loader.test.ts +0 -7
  150. package/src/__tests__/voice-session-bridge.test.ts +198 -0
  151. package/src/__tests__/web-search-catalog-parity.test.ts +32 -10
  152. package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +115 -3
  153. package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +50 -0
  154. package/src/__tests__/workspace-migration-073-repair-recall-callsite-empty-profile.test.ts +153 -0
  155. package/src/__tests__/workspace-migration-085-memory-v2-bm25-b-reembed-disabled-v2-pages.test.ts +220 -0
  156. package/src/__tests__/workspace-migration-086-revert-stale-gemini-mis-rewrites.test.ts +269 -0
  157. package/src/__tests__/workspace-migration-remove-legacy-skills-index.test.ts +309 -0
  158. package/src/__tests__/workspace-migrations-runner.test.ts +111 -3
  159. package/src/acp/resolve-agent.ts +1 -1
  160. package/src/agent/image-optimize.ts +13 -5
  161. package/src/calls/voice-session-bridge.ts +61 -42
  162. package/src/channels/types.ts +108 -0
  163. package/src/cli/__tests__/unknown-command.test.ts +24 -0
  164. package/src/cli/commands/__tests__/changelog.test.ts +304 -319
  165. package/src/cli/commands/__tests__/schedules.test.ts +491 -0
  166. package/src/cli/commands/changelog.ts +106 -42
  167. package/src/cli/commands/conversations.ts +102 -17
  168. package/src/cli/commands/default-action.ts +10 -53
  169. package/src/cli/commands/notifications.ts +329 -317
  170. package/src/cli/commands/plugins.ts +185 -0
  171. package/src/cli/commands/schedules.ts +391 -0
  172. package/src/cli/commands/telemetry.ts +40 -0
  173. package/src/cli/lib/__tests__/cli-colors.test.ts +48 -0
  174. package/src/cli/lib/__tests__/confirm-prompt.test.ts +159 -0
  175. package/src/cli/lib/__tests__/install-from-github.test.ts +355 -0
  176. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +154 -0
  177. package/src/cli/lib/__tests__/uninstall-plugin.test.ts +124 -0
  178. package/src/cli/lib/__tests__/unknown-command.test.ts +106 -0
  179. package/src/cli/lib/cli-colors.ts +12 -0
  180. package/src/cli/lib/confirm-prompt.ts +79 -0
  181. package/src/cli/lib/install-from-github.ts +304 -0
  182. package/src/cli/lib/list-installed-plugins.ts +137 -0
  183. package/src/cli/lib/uninstall-plugin.ts +82 -0
  184. package/src/cli/lib/unknown-command.ts +111 -0
  185. package/src/cli/program.ts +38 -2
  186. package/src/config/bundled-skills/app-builder/SKILL.md +23 -21
  187. package/src/config/bundled-skills/app-builder/TOOLS.json +7 -0
  188. package/src/config/bundled-skills/computer-use/TOOLS.json +15 -52
  189. package/src/config/bundled-skills/document/SKILL.md +23 -3
  190. package/src/config/bundled-skills/document/TOOLS.json +53 -0
  191. package/src/config/bundled-skills/document/tools/document-delete.ts +12 -0
  192. package/src/config/bundled-skills/document/tools/document-list.ts +12 -0
  193. package/src/config/bundled-skills/document/tools/document-read.ts +12 -0
  194. package/src/config/bundled-skills/skill-management/SKILL.md +2 -2
  195. package/src/config/bundled-skills/skill-management/TOOLS.json +7 -7
  196. package/src/config/bundled-tool-registry.ts +6 -0
  197. package/src/config/feature-flag-registry.json +41 -1
  198. package/src/config/loader.ts +64 -38
  199. package/src/config/schema.ts +7 -10
  200. package/src/config/schemas/__tests__/llm-request-logs.test.ts +36 -0
  201. package/src/config/schemas/channels.ts +8 -0
  202. package/src/config/schemas/compaction.ts +28 -0
  203. package/src/config/schemas/heartbeat.ts +9 -0
  204. package/src/config/schemas/llm-request-logs.ts +31 -7
  205. package/src/config/schemas/llm.ts +3 -0
  206. package/src/config/schemas/memory-retrieval.ts +18 -0
  207. package/src/config/schemas/tools.ts +14 -0
  208. package/src/config/skills.ts +3 -96
  209. package/src/context/compactor.ts +1047 -0
  210. package/src/context/token-estimator.ts +2 -2
  211. package/src/context/window-manager.ts +197 -1520
  212. package/src/credential-execution/managed-catalog.ts +37 -0
  213. package/src/credential-health/credential-health-service.ts +280 -19
  214. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +34 -0
  215. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +138 -0
  216. package/src/daemon/__tests__/conversation-tool-setup.test.ts +74 -0
  217. package/src/daemon/approval-generators.ts +8 -6
  218. package/src/daemon/config-watcher.ts +94 -31
  219. package/src/daemon/conversation-agent-loop.ts +169 -9
  220. package/src/daemon/conversation-error.ts +171 -37
  221. package/src/daemon/conversation-lifecycle.ts +53 -40
  222. package/src/daemon/conversation-messaging.ts +25 -6
  223. package/src/daemon/conversation-process.ts +49 -12
  224. package/src/daemon/conversation-runtime-assembly.ts +16 -1
  225. package/src/daemon/conversation-slash.ts +12 -5
  226. package/src/daemon/conversation-store.ts +11 -4
  227. package/src/daemon/conversation-tool-setup.ts +39 -7
  228. package/src/daemon/conversation.ts +33 -1
  229. package/src/daemon/external-plugins-bootstrap.ts +217 -181
  230. package/src/daemon/first-greeting.ts +22 -2
  231. package/src/daemon/handlers/config-model.ts +6 -5
  232. package/src/daemon/handlers/config-slack-channel.ts +15 -3
  233. package/src/daemon/handlers/shared.ts +14 -5
  234. package/src/daemon/handlers/skills.ts +111 -108
  235. package/src/daemon/history-repair.ts +28 -1
  236. package/src/daemon/host-app-control-proxy.ts +98 -23
  237. package/src/daemon/lifecycle.ts +45 -35
  238. package/src/daemon/meet-host-supervisor.ts +5 -4
  239. package/src/daemon/memory-v2-startup.ts +49 -0
  240. package/src/daemon/message-protocol.ts +1 -0
  241. package/src/daemon/message-types/conversations.ts +25 -0
  242. package/src/daemon/message-types/messages.ts +61 -0
  243. package/src/daemon/message-types/subagents.ts +1 -0
  244. package/src/daemon/message-types/sync.ts +1 -0
  245. package/src/daemon/pkb-reminder-builder.test.ts +1 -1
  246. package/src/daemon/pkb-reminder-builder.ts +1 -1
  247. package/src/daemon/plugin-source-watcher.ts +146 -0
  248. package/src/daemon/process-message.ts +21 -3
  249. package/src/daemon/server.ts +11 -2
  250. package/src/daemon/skill-memory-refresh.ts +29 -0
  251. package/src/documents/document-store.ts +221 -3
  252. package/src/embedded/plugin-api.ts +40 -0
  253. package/src/filing/filing-service.ts +39 -0
  254. package/src/heartbeat/__tests__/heartbeat-service.test.ts +91 -6
  255. package/src/heartbeat/heartbeat-run-store.ts +2 -1
  256. package/src/heartbeat/heartbeat-service.ts +41 -0
  257. package/src/home/__tests__/feed-types.test.ts +40 -0
  258. package/src/home/feed-types.ts +22 -0
  259. package/src/home/post-connect-feed.ts +1 -0
  260. package/src/index.ts +18 -1
  261. package/src/live-voice/__tests__/live-voice-stt.test.ts +57 -0
  262. package/src/mcp/client.ts +20 -4
  263. package/src/media/image-credentials.ts +3 -3
  264. package/src/memory/__tests__/bookmark-crud.test.ts +33 -27
  265. package/src/memory/__tests__/conversation-queries.test.ts +263 -0
  266. package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +113 -0
  267. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +119 -14
  268. package/src/memory/__tests__/message-content.test.ts +35 -0
  269. package/src/memory/bookmark-crud.ts +42 -10
  270. package/src/memory/context-search/sources/conversations.ts +62 -2
  271. package/src/memory/context-search/sources/workspace.ts +4 -0
  272. package/src/memory/conversation-crud.ts +63 -19
  273. package/src/memory/conversation-queries.ts +110 -10
  274. package/src/memory/db-init.ts +6 -0
  275. package/src/memory/delivery-crud.ts +152 -5
  276. package/src/memory/embedding-backend.ts +4 -4
  277. package/src/memory/external-conversation-store.ts +66 -5
  278. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +66 -9
  279. package/src/memory/graph/conversation-graph-memory.ts +31 -15
  280. package/src/memory/graph/tools.ts +3 -3
  281. package/src/memory/indexer.ts +34 -29
  282. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +73 -0
  283. package/src/memory/jobs/embed-concept-page.ts +20 -11
  284. package/src/memory/jobs-worker.ts +6 -1
  285. package/src/memory/llm-request-log-source-clickhouse.ts +17 -10
  286. package/src/memory/llm-request-log-source.ts +19 -52
  287. package/src/memory/llm-usage-store.ts +125 -5
  288. package/src/memory/memory-retrospective-startup-cleanup.ts +72 -5
  289. package/src/memory/message-content.ts +1 -1
  290. package/src/memory/migrations/109-external-conversation-bindings.ts +15 -4
  291. package/src/memory/migrations/229-delete-private-conversations.test.ts +38 -1
  292. package/src/memory/migrations/229-delete-private-conversations.ts +7 -0
  293. package/src/memory/migrations/247-external-conversation-binding-thread-id.ts +78 -0
  294. package/src/memory/migrations/248-create-onboarding-events.ts +21 -0
  295. package/src/memory/migrations/249-normalize-slack-external-content.ts +240 -0
  296. package/src/memory/migrations/index.ts +6 -0
  297. package/src/memory/migrations/registry.ts +8 -0
  298. package/src/memory/onboarding-events-store.ts +106 -0
  299. package/src/memory/schema/bookmarks.ts +0 -2
  300. package/src/memory/schema/calls.ts +1 -0
  301. package/src/memory/schema/inference.ts +1 -3
  302. package/src/memory/schema/infrastructure.ts +12 -0
  303. package/src/memory/turn-events-store.ts +127 -2
  304. package/src/memory/v2/__tests__/activation.test.ts +0 -8
  305. package/src/memory/v2/__tests__/injection.test.ts +98 -8
  306. package/src/memory/v2/__tests__/migration.test.ts +87 -0
  307. package/src/memory/v2/__tests__/page-index.test.ts +83 -0
  308. package/src/memory/v2/__tests__/prompts-router.test.ts +58 -6
  309. package/src/memory/v2/__tests__/qdrant.test.ts +66 -3
  310. package/src/memory/v2/__tests__/router.test.ts +15 -0
  311. package/src/memory/v2/__tests__/skill-store.test.ts +387 -8
  312. package/src/memory/v2/injection.ts +32 -6
  313. package/src/memory/v2/migration.ts +49 -19
  314. package/src/memory/v2/page-index.ts +35 -5
  315. package/src/memory/v2/prompts/router.ts +11 -8
  316. package/src/memory/v2/prompts/sweep.ts +2 -2
  317. package/src/memory/v2/qdrant.ts +135 -7
  318. package/src/memory/v2/router.ts +9 -8
  319. package/src/memory/v2/skill-store.ts +120 -35
  320. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +45 -5
  321. package/src/messaging/providers/slack/__tests__/download.test.ts +231 -0
  322. package/src/messaging/providers/slack/adapter.ts +43 -5
  323. package/src/messaging/providers/slack/client.ts +27 -0
  324. package/src/messaging/providers/slack/deep-link.ts +65 -0
  325. package/src/messaging/providers/slack/download.ts +104 -0
  326. package/src/messaging/providers/slack/message-metadata.test.ts +32 -0
  327. package/src/messaging/providers/slack/message-metadata.ts +27 -0
  328. package/src/messaging/providers/slack/render-transcript.test.ts +134 -0
  329. package/src/messaging/providers/slack/render-transcript.ts +69 -5
  330. package/src/messaging/providers/slack/types.ts +20 -1
  331. package/src/notifications/conversation-pairing.ts +2 -1
  332. package/src/notifications/decision-engine.ts +2 -1
  333. package/src/notifications/emit-signal.ts +20 -1
  334. package/src/notifications/home-feed-side-effect.ts +54 -0
  335. package/src/notifications/signal.ts +3 -1
  336. package/src/oauth/connection-resolver.ts +8 -4
  337. package/src/oauth/platform-connection.ts +6 -2
  338. package/src/oauth/seed-providers.ts +10 -1
  339. package/src/permissions/checker.ts +2 -0
  340. package/src/permissions/ipc-risk-types.ts +1 -0
  341. package/src/permissions/question-prompter.test.ts +416 -0
  342. package/src/permissions/question-prompter.ts +294 -0
  343. package/src/platform/client.test.ts +1 -1
  344. package/src/platform/client.ts +1 -1
  345. package/src/plugin-api/constants.ts +26 -0
  346. package/src/plugin-api/index.ts +34 -1
  347. package/src/plugin-api/types.ts +104 -22
  348. package/src/plugins/defaults/circuit-breaker.ts +0 -5
  349. package/src/plugins/defaults/compaction.ts +0 -4
  350. package/src/plugins/defaults/empty-response.ts +0 -2
  351. package/src/plugins/defaults/history-repair.ts +0 -2
  352. package/src/plugins/defaults/injectors.ts +36 -3
  353. package/src/plugins/defaults/llm-call.ts +0 -2
  354. package/src/plugins/defaults/memory-retrieval.ts +0 -1
  355. package/src/plugins/defaults/overflow-reduce.ts +0 -1
  356. package/src/plugins/defaults/persistence.ts +0 -2
  357. package/src/plugins/defaults/title-generate.ts +0 -5
  358. package/src/plugins/defaults/token-estimate.ts +0 -2
  359. package/src/plugins/defaults/tool-error.ts +0 -7
  360. package/src/plugins/defaults/tool-execute.ts +0 -2
  361. package/src/plugins/defaults/tool-result-truncate.ts +0 -4
  362. package/src/plugins/ensure-plugin-api-shim.ts +96 -0
  363. package/src/plugins/external-api.ts +104 -0
  364. package/src/plugins/external-plugin-loader.ts +105 -32
  365. package/src/plugins/feature-gate.ts +22 -0
  366. package/src/plugins/pipeline.ts +37 -0
  367. package/src/plugins/registry.ts +48 -80
  368. package/src/plugins/types.ts +31 -26
  369. package/src/plugins/user-loader.ts +21 -2
  370. package/src/proactive-artifact/aux-message-injector.ts +11 -0
  371. package/src/proactive-artifact/job.test.ts +37 -5
  372. package/src/prompts/__tests__/system-prompt.test.ts +12 -0
  373. package/src/prompts/__tests__/task-progress-hint-section.test.ts +99 -0
  374. package/src/prompts/normalize-onboarding.ts +27 -0
  375. package/src/prompts/sections.ts +302 -0
  376. package/src/prompts/system-prompt.ts +63 -166
  377. package/src/prompts/templates/BOOTSTRAP.md +17 -1
  378. package/src/prompts/templates/system-sections.ts +173 -0
  379. package/src/providers/__tests__/inference.test.ts +22 -7
  380. package/src/providers/anthropic/client.ts +28 -28
  381. package/src/providers/connection-resolution.ts +7 -0
  382. package/src/providers/inference/adapter-factory.ts +41 -4
  383. package/src/providers/inference/connections.ts +74 -29
  384. package/src/providers/inference/resolve-auth.ts +12 -4
  385. package/src/providers/model-catalog.ts +294 -12
  386. package/src/providers/openai/chat-completions-provider.ts +10 -2
  387. package/src/providers/openrouter/client.ts +7 -0
  388. package/src/providers/{managed-proxy → platform-proxy}/constants.ts +4 -1
  389. package/src/providers/{managed-proxy → platform-proxy}/context.ts +3 -3
  390. package/src/providers/provider-availability.ts +17 -2
  391. package/src/providers/provider-catalog-visibility.ts +36 -0
  392. package/src/providers/registry.ts +22 -14
  393. package/src/providers/retry.ts +47 -1
  394. package/src/runtime/__tests__/agent-wake.test.ts +152 -0
  395. package/src/runtime/agent-wake.ts +42 -14
  396. package/src/runtime/auth/route-policy.ts +8 -1
  397. package/src/runtime/btw-sidechain.ts +2 -0
  398. package/src/runtime/http-types.ts +19 -0
  399. package/src/runtime/migrations/origin-mode.ts +1 -1
  400. package/src/runtime/pending-interactions.ts +1 -0
  401. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +17 -0
  402. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +5 -1
  403. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +107 -20
  404. package/src/runtime/routes/__tests__/question-routes.test.ts +395 -0
  405. package/src/runtime/routes/__tests__/tts-routes.test.ts +64 -1
  406. package/src/runtime/routes/acp-routes-list.test.ts +143 -0
  407. package/src/runtime/routes/acp-routes.ts +5 -3
  408. package/src/runtime/routes/auth-routes.ts +1 -1
  409. package/src/runtime/routes/bookmark-routes.ts +5 -3
  410. package/src/runtime/routes/btw-routes.ts +5 -1
  411. package/src/runtime/routes/channel-availability-routes.ts +121 -0
  412. package/src/runtime/routes/conversation-cli-routes.ts +44 -3
  413. package/src/runtime/routes/conversation-list-routes.ts +3 -20
  414. package/src/runtime/routes/conversation-management-routes.ts +17 -42
  415. package/src/runtime/routes/conversation-query-routes.ts +40 -35
  416. package/src/runtime/routes/conversation-routes.ts +90 -11
  417. package/src/runtime/routes/documents-routes.ts +25 -86
  418. package/src/runtime/routes/group-routes.ts +5 -0
  419. package/src/runtime/routes/inbound-conversation.ts +28 -8
  420. package/src/runtime/routes/inbound-message-handler.ts +236 -41
  421. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +111 -0
  422. package/src/runtime/routes/inbound-stages/background-dispatch.ts +32 -1
  423. package/src/runtime/routes/inbound-stages/edit-intercept.ts +17 -4
  424. package/src/runtime/routes/index.ts +6 -0
  425. package/src/runtime/routes/inference-profile-session-handler.ts +17 -44
  426. package/src/runtime/routes/inference-profile-session-reaper.ts +7 -21
  427. package/src/runtime/routes/inference-provider-connection-routes.ts +65 -21
  428. package/src/runtime/routes/integrations/slack/share.ts +4 -52
  429. package/src/runtime/routes/integrations/slack/token.ts +43 -0
  430. package/src/runtime/routes/integrations/twilio.ts +6 -13
  431. package/src/runtime/routes/notification-routes.ts +1 -1
  432. package/src/runtime/routes/oauth-commands-routes.ts +105 -15
  433. package/src/runtime/routes/oauth-lifecycle-routes.ts +43 -0
  434. package/src/runtime/routes/question-routes.ts +259 -0
  435. package/src/runtime/routes/rename-conversation-routes.ts +2 -33
  436. package/src/runtime/routes/schedule-routes.ts +4 -7
  437. package/src/runtime/routes/subagents-routes.ts +57 -18
  438. package/src/runtime/routes/telemetry-routes.ts +27 -0
  439. package/src/runtime/routes/tts-routes.ts +27 -2
  440. package/src/runtime/routes/workspace-routes.test.ts +43 -0
  441. package/src/runtime/routes/workspace-routes.ts +28 -0
  442. package/src/runtime/services/conversation-serializer.ts +39 -7
  443. package/src/runtime/sync/resource-sync-events.ts +93 -1
  444. package/src/schedule/schedule-store.ts +27 -2
  445. package/src/schedule/scheduler.ts +9 -1
  446. package/src/security/__tests__/untrusted-content.test.ts +86 -0
  447. package/src/security/untrusted-content.ts +93 -8
  448. package/src/skills/catalog-files.ts +1 -1
  449. package/src/skills/catalog-install.ts +233 -116
  450. package/src/skills/clawhub.ts +70 -13
  451. package/src/skills/managed-store.ts +4 -119
  452. package/src/skills/skillssh-registry.ts +27 -48
  453. package/src/subagent/manager.ts +15 -7
  454. package/src/telemetry/types.ts +113 -1
  455. package/src/telemetry/usage-telemetry-reporter.test.ts +312 -5
  456. package/src/telemetry/usage-telemetry-reporter.ts +113 -7
  457. package/src/tools/apps/executors.ts +58 -7
  458. package/src/tools/ask-question/ask-question-tool.test.ts +509 -0
  459. package/src/tools/ask-question/ask-question-tool.ts +304 -0
  460. package/src/tools/browser/browser-execution.ts +15 -11
  461. package/src/tools/computer-use/definitions.ts +3 -3
  462. package/src/tools/credentials/vault.ts +1 -1
  463. package/src/tools/document/document-tool.ts +124 -1
  464. package/src/tools/filesystem/edit.ts +1 -1
  465. package/src/tools/filesystem/list.ts +1 -1
  466. package/src/tools/filesystem/read.ts +1 -1
  467. package/src/tools/filesystem/write.ts +5 -2
  468. package/src/tools/host-filesystem/transfer.ts +1 -1
  469. package/src/tools/host-terminal/host-shell.ts +1 -1
  470. package/src/tools/permission-checker.ts +1 -1
  471. package/src/tools/registry.ts +17 -7
  472. package/src/tools/schedule/create.ts +2 -2
  473. package/src/tools/schema-transforms.ts +7 -2
  474. package/src/tools/side-effects.ts +1 -0
  475. package/src/tools/skills/delete-managed.ts +4 -4
  476. package/src/tools/skills/execute.ts +1 -1
  477. package/src/tools/skills/scaffold-managed.ts +3 -2
  478. package/src/tools/subagent/notify-parent.ts +1 -1
  479. package/src/tools/system/request-permission.ts +2 -2
  480. package/src/tools/terminal/safe-env.ts +60 -1
  481. package/src/tools/tool-manifest.ts +2 -0
  482. package/src/tools/types.ts +72 -21
  483. package/src/tools/ui-surface/definitions.ts +6 -5
  484. package/src/tts/__tests__/provider-adapters.test.ts +76 -2
  485. package/src/tts/providers/elevenlabs-provider.ts +75 -1
  486. package/src/types/onboarding-context.ts +2 -0
  487. package/src/util/errors.ts +17 -0
  488. package/src/util/platform.ts +10 -0
  489. package/src/watcher/__tests__/engine.test.ts +22 -0
  490. package/src/watcher/engine.ts +6 -2
  491. package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +80 -15
  492. package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +35 -22
  493. package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +3 -1
  494. package/src/workspace/migrations/083-system-prompt-prefix-to-file.ts +191 -0
  495. package/src/workspace/migrations/084-remove-legacy-skills-index.ts +276 -0
  496. package/src/workspace/migrations/085-memory-v2-bm25-b-reembed-disabled-v2-pages.ts +137 -0
  497. package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +198 -0
  498. package/src/workspace/migrations/registry.ts +8 -0
  499. package/src/workspace/migrations/runner.ts +39 -9
  500. package/src/workspace/migrations/types.ts +4 -0
  501. package/examples/plugins/echo/bun.lock +0 -25
  502. package/src/__tests__/context-window-manager.test.ts +0 -2481
  503. package/src/context/__tests__/compact-prompt.test.ts +0 -63
  504. package/src/context/prompts/compact.md +0 -26
  505. package/src/prompts/__tests__/build-cli-reference-section.test.ts +0 -37
  506. /package/src/__tests__/{secret-routes-managed-proxy.test.ts → secret-routes-platform-proxy.test.ts} +0 -0
@@ -17,9 +17,15 @@
17
17
  * targets the user's actual desktop application, which is a host-wide
18
18
  * resource. It is acquired optimistically when `app_control_start` is
19
19
  * dispatched (storing `(conversationId, app)`) so that the synchronous
20
- * guard and the asynchronous host round-trip cannot race; it is released
21
- * when the host returns a non-running state, the dispatch fails, or the
22
- * owning proxy's `dispose()` fires.
20
+ * guard and the asynchronous host round-trip cannot race. A separate
21
+ * `confirmedAppControlSession` tracks the last session the host reported
22
+ * `running`; the rollback path on a failed `start` restores from the
23
+ * current confirmed pointer (not from a per-call snapshot of a sibling
24
+ * optimistic write), so two overlapping starts that both fail cannot
25
+ * leave a phantom lock — and a late-arriving `running` for an older
26
+ * overlapping start still updates the confirmed pointer so the lock
27
+ * survives a subsequent rollback of the newer start. The lock is
28
+ * released outright when the owning proxy's `dispose()` fires.
23
29
  *
24
30
  * `app_control_start` is the only tool that can acquire the lock — the
25
31
  * user's medium-risk approval at start time is the consent boundary. All
@@ -81,34 +87,47 @@ export interface ActiveAppControlSession {
81
87
  }
82
88
 
83
89
  /**
84
- * Currently active session, or `undefined` when no session is held.
90
+ * Currently active session, or `undefined` when no session is held. This
91
+ * is the optimistic value: it is set the moment a `start` is dispatched
92
+ * and only promoted to {@link confirmedAppControlSession} when the host
93
+ * returns `running`.
85
94
  *
86
95
  * Exported for test inspection only. Production code paths must not read
87
96
  * or mutate this directly — use the proxy methods.
88
97
  */
89
98
  let activeAppControlSession: ActiveAppControlSession | undefined;
90
99
 
91
- /** Test-only helper: read current session. */
100
+ /**
101
+ * Last session whose `start` was confirmed by the host (`payload.state ===
102
+ * "running"`). Used as the rollback baseline so a failed start never
103
+ * restores a sibling in-flight optimistic write — only a session that was
104
+ * actually running can re-emerge from a rollback.
105
+ */
106
+ let confirmedAppControlSession: ActiveAppControlSession | undefined;
107
+
108
+ /** Test-only helper: read current (optimistic) session. */
92
109
  export function _getActiveAppControlSession():
93
110
  | ActiveAppControlSession
94
111
  | undefined {
95
112
  return activeAppControlSession;
96
113
  }
97
114
 
98
- /** Test-only helper: clear session between test cases. */
115
+ /** Test-only helper: clear both session pointers between test cases. */
99
116
  export function _resetActiveAppControlSession(): void {
100
117
  activeAppControlSession = undefined;
118
+ confirmedAppControlSession = undefined;
101
119
  }
102
120
 
103
121
  /**
104
- * Test-only helper: prime the active session without a full `start` round-trip.
105
- * Useful for tests that exercise non-start tool paths and don't need to
106
- * verify the start flow itself.
122
+ * Test-only helper: prime both session pointers without a full `start`
123
+ * round-trip. Useful for tests that exercise non-start tool paths and
124
+ * don't need to verify the start flow itself.
107
125
  */
108
126
  export function _setActiveAppControlSession(
109
127
  session: ActiveAppControlSession,
110
128
  ): void {
111
129
  activeAppControlSession = session;
130
+ confirmedAppControlSession = session;
112
131
  }
113
132
 
114
133
  /**
@@ -236,6 +255,7 @@ export class HostAppControlProxy extends HostProxyBase<
236
255
  // belong to the active session and target the same `app`. Without this
237
256
  // gate, prompt-injected calls would bypass the start-time approval and
238
257
  // send raw input to arbitrary apps.
258
+ let attemptedSession: ActiveAppControlSession | undefined;
239
259
  if (input.tool === "start") {
240
260
  if (
241
261
  activeAppControlSession != null &&
@@ -253,12 +273,15 @@ export class HostAppControlProxy extends HostProxyBase<
253
273
  // synchronous guard and the asynchronous `dispatchRequest` below. Two
254
274
  // concurrent starts from different conversations would otherwise both
255
275
  // see `activeAppControlSession == null` and both pass the guard. The
256
- // lock is released below if dispatch fails or the host returns a
257
- // non-running state.
258
- activeAppControlSession = {
276
+ // lock is rolled back below if dispatch fails or the host returns a
277
+ // non-running state — keyed on object identity so that a later
278
+ // overlapping start that has already replaced our write is not
279
+ // clobbered by a stale rollback.
280
+ attemptedSession = {
259
281
  conversationId: this.conversationId,
260
282
  app: input.app,
261
283
  };
284
+ activeAppControlSession = attemptedSession;
262
285
  } else {
263
286
  const sessionError = checkNonStartAuthorization(
264
287
  input,
@@ -278,10 +301,17 @@ export class HostAppControlProxy extends HostProxyBase<
278
301
  undefined,
279
302
  targetClientId,
280
303
  );
281
- return this.handleSuccess(input, payload);
304
+ if (input.tool === "start") {
305
+ if (payload.state === "running") {
306
+ this.promoteStartIfCurrent(attemptedSession);
307
+ } else {
308
+ this.rollbackStartIfCurrent(attemptedSession);
309
+ }
310
+ }
311
+ return this.handleSuccess(payload);
282
312
  } catch (err) {
283
313
  if (input.tool === "start") {
284
- this.releaseSessionIfHeld();
314
+ this.rollbackStartIfCurrent(attemptedSession);
285
315
  }
286
316
  if (err instanceof HostProxyRequestError) {
287
317
  if (err.reason === "timeout") {
@@ -301,10 +331,64 @@ export class HostAppControlProxy extends HostProxyBase<
301
331
  }
302
332
  }
303
333
 
334
+ /**
335
+ * Roll back the optimistic overwrite performed by a `start` when the
336
+ * dispatch fails or the host returns non-running. Keyed on the
337
+ * `attempted` reference, not just `conversationId`, so that an
338
+ * out-of-order failure does not clobber a newer overlapping start that
339
+ * already replaced our write — e.g. start A → start B (pending) →
340
+ * start C (success); when B later fails, the live session is C and the
341
+ * identity check makes our rollback a no-op rather than restoring A.
342
+ *
343
+ * Restores from the *current* `confirmedAppControlSession`, not a
344
+ * per-call snapshot of it. This matters when a late-arriving `running`
345
+ * for an older overlapping start has updated `confirmedAppControlSession`
346
+ * in the meantime: if A is dispatched, then C is dispatched (overwriting
347
+ * active), then A returns `running` (confirming A), then C returns
348
+ * non-running, the rollback must restore active to A — not undefined.
349
+ */
350
+ private rollbackStartIfCurrent(
351
+ attempted: ActiveAppControlSession | undefined,
352
+ ): void {
353
+ if (attempted != null && activeAppControlSession === attempted) {
354
+ activeAppControlSession = confirmedAppControlSession;
355
+ }
356
+ }
357
+
358
+ /**
359
+ * Promote this start's optimistic write to the confirmed pointer when
360
+ * the host returns `running`. Gated on conversation ownership rather
361
+ * than object identity: a newer overlapping start in the same
362
+ * conversation may have superseded our optimistic write while we were
363
+ * waiting on the host, but the host's `running` response for our
364
+ * `attempted` is still ground-truth that the lock should be held.
365
+ * The conversation-ownership check ensures we don't resurrect a session
366
+ * after `dispose()` cleared the lock or after another conversation
367
+ * acquired it.
368
+ */
369
+ private promoteStartIfCurrent(
370
+ attempted: ActiveAppControlSession | undefined,
371
+ ): void {
372
+ if (attempted == null) return;
373
+ if (activeAppControlSession?.conversationId !== attempted.conversationId) {
374
+ return;
375
+ }
376
+ confirmedAppControlSession = attempted;
377
+ }
378
+
379
+ /**
380
+ * Release both the optimistic and confirmed module-level session
381
+ * pointers if this proxy is the current holder. Used by `dispose()` —
382
+ * distinct from `rollbackStartIfCurrent` because dispose is keyed on
383
+ * ownership (conversationId) rather than on a specific in-flight start.
384
+ */
304
385
  private releaseSessionIfHeld(): void {
305
386
  if (activeAppControlSession?.conversationId === this.conversationId) {
306
387
  activeAppControlSession = undefined;
307
388
  }
389
+ if (confirmedAppControlSession?.conversationId === this.conversationId) {
390
+ confirmedAppControlSession = undefined;
391
+ }
308
392
  }
309
393
 
310
394
  // ---------------------------------------------------------------------------
@@ -312,7 +396,6 @@ export class HostAppControlProxy extends HostProxyBase<
312
396
  // ---------------------------------------------------------------------------
313
397
 
314
398
  private handleSuccess(
315
- input: HostAppControlInput,
316
399
  payload: HostAppControlResultPayload,
317
400
  ): ToolExecutionResult {
318
401
  // Update PNG-hash loop tracking only for the "running" state — other
@@ -332,14 +415,6 @@ export class HostAppControlProxy extends HostProxyBase<
332
415
  }
333
416
  }
334
417
 
335
- // The optimistic lock acquired in `request()` for `start` stays held
336
- // only when the host confirms the session is running. Non-running
337
- // outcomes (missing/minimized) release it so a retry or another
338
- // conversation can acquire.
339
- if (input.tool === "start" && payload.state !== "running") {
340
- this.releaseSessionIfHeld();
341
- }
342
-
343
418
  return this.formatResult(payload, stuck);
344
419
  }
345
420
 
@@ -20,6 +20,7 @@ import type { AssistantConfig } from "../config/schema.js";
20
20
  import { seedInferenceProfiles } from "../config/seed-inference-profiles.js";
21
21
  import type { CesClient } from "../credential-execution/client.js";
22
22
  import { createCesClient } from "../credential-execution/client.js";
23
+ import { refreshManagedConnectionCache } from "../credential-execution/managed-catalog.js";
23
24
  import {
24
25
  type CesProcessManager,
25
26
  CesUnavailableError,
@@ -54,11 +55,12 @@ import {
54
55
  } from "../notifications/emit-signal.js";
55
56
  import { backfillManualTokenConnections } from "../oauth/manual-token-connection.js";
56
57
  import { seedOAuthProviders } from "../oauth/seed-providers.js";
58
+ import { installPluginRuntime } from "../plugins/external-api.js";
57
59
  import { loadUserPlugins } from "../plugins/user-loader.js";
58
60
  import { backfillGuardIfNeeded } from "../proactive-artifact/index.js";
59
61
  import { ensurePromptFiles } from "../prompts/system-prompt.js";
60
62
  import { runProviderConnectionsBackfill } from "../providers/inference/backfill.js";
61
- import { resolveManagedProxyContext } from "../providers/managed-proxy/context.js";
63
+ import { resolveManagedProxyContext } from "../providers/platform-proxy/context.js";
62
64
  import { broadcastMessage } from "../runtime/assistant-event-hub.js";
63
65
  import {
64
66
  initAuthSigningKey,
@@ -74,7 +76,10 @@ import {
74
76
  setCesClient,
75
77
  setCesReconnect,
76
78
  } from "../security/secure-keys.js";
77
- import { UsageTelemetryReporter } from "../telemetry/usage-telemetry-reporter.js";
79
+ import {
80
+ setUsageTelemetryReporter,
81
+ UsageTelemetryReporter,
82
+ } from "../telemetry/usage-telemetry-reporter.js";
78
83
  import { registerBuiltinTtsProviders } from "../tts/providers/register-builtins.js";
79
84
  import { getDeviceId } from "../util/device-id.js";
80
85
  import { getLogger, initLogger } from "../util/logger.js";
@@ -114,7 +119,7 @@ import { backfillSlackInjectionTemplates } from "./handlers/config-slack-channel
114
119
  import { installAssistantSymlink } from "./install-symlink.js";
115
120
  import {
116
121
  maybeRebuildMemoryV2Concepts,
117
- maybeSeedMemoryV2Skills,
122
+ rebuildBm25CorpusStatsAndReseedSkills,
118
123
  } from "./memory-v2-startup.js";
119
124
  import { processMessage } from "./process-message.js";
120
125
  import { runProfilerSweep } from "./profiler-run-store.js";
@@ -126,6 +131,7 @@ import {
126
131
  import { seedInterfaceFiles } from "./seed-files.js";
127
132
  import { DaemonServer } from "./server.js";
128
133
  import { installShutdownHandlers } from "./shutdown-handlers.js";
134
+ import { refreshSkillCapabilityMemories } from "./skill-memory-refresh.js";
129
135
 
130
136
  const log = getLogger("lifecycle");
131
137
  let diskPressureStartupSampleTimer: ReturnType<typeof setTimeout> | null = null;
@@ -504,6 +510,25 @@ export async function runDaemon(): Promise<void> {
504
510
  }
505
511
  } // end if (dbReady)
506
512
 
513
+ // Populate the managed-connection cache so buildIntegrationSection()
514
+ // can include platform-managed OAuth connections (e.g. Twitter) in the
515
+ // system prompt's "Connected Services" section from the first turn.
516
+ // This is an HTTP-only call with no DB dependency, so it runs regardless
517
+ // of dbReady. A periodic refresh keeps the cache current when users
518
+ // connect/disconnect managed providers while the assistant is running.
519
+ void refreshManagedConnectionCache().catch((err) =>
520
+ log.warn(
521
+ { err },
522
+ "Managed connection cache refresh failed — continuing startup",
523
+ ),
524
+ );
525
+ const MANAGED_CONNECTION_REFRESH_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes
526
+ setInterval(() => {
527
+ void refreshManagedConnectionCache().catch((err) =>
528
+ log.warn({ err }, "Periodic managed connection cache refresh failed"),
529
+ );
530
+ }, MANAGED_CONNECTION_REFRESH_INTERVAL_MS);
531
+
507
532
  // Merge CLI-provided default config (from VELLUM_DEFAULT_WORKSPACE_CONFIG_PATH)
508
533
  // into the workspace config file before profile seeding and the first
509
534
  // loadConfig() call so onboarding/platform preferences are visible to the
@@ -565,6 +590,7 @@ export async function runDaemon(): Promise<void> {
565
590
  let telemetryReporter: UsageTelemetryReporter | null = null;
566
591
  if (collectUsageData) {
567
592
  telemetryReporter = new UsageTelemetryReporter();
593
+ setUsageTelemetryReporter(telemetryReporter);
568
594
  telemetryReporter.start();
569
595
  log.info("Usage telemetry reporter started");
570
596
  }
@@ -648,6 +674,12 @@ export async function runDaemon(): Promise<void> {
648
674
  });
649
675
  }
650
676
 
677
+ // Install the `globalThis.__vellumPluginRuntime` bridge before scanning
678
+ // for user plugins. Plugins that touch the bridge from their module body
679
+ // would throw without this — see `plugins/external-api.ts` for the
680
+ // rationale (compiled-binary module identity).
681
+ installPluginRuntime();
682
+
651
683
  // Populate the registry with user plugins from `<workspaceDir>/plugins/*`
652
684
  // AFTER first-party plugins have already registered via their static
653
685
  // side-effect imports. User plugins may fail to load individually; a
@@ -830,25 +862,13 @@ export async function runDaemon(): Promise<void> {
830
862
  }
831
863
 
832
864
  // Build the BM25 corpus stats (per-token document frequencies and
833
- // average document length) used by the v2 sparse channel. Without
834
- // this, document-side sparse embeddings fall back to legacy TF-only
835
- // weighting via the chicken-and-egg guard in
836
- // `embed-concept-page.ts`. Fire-and-forget for the same reason as
837
- // PKB reconcile — the stats are an optional optimization, never a
838
- // boot-blocking dependency.
839
- void (async () => {
840
- try {
841
- const { rebuildConceptPageCorpusStats } =
842
- await import("../memory/v2/sparse-bm25.js");
843
- await rebuildConceptPageCorpusStats(getWorkspaceDir());
844
- log.info("Memory v2 BM25 corpus stats built");
845
- } catch (err) {
846
- log.warn(
847
- { err },
848
- "BM25 corpus-stats rebuild failed — sparse channel will fall back to TF-only until next rebuild",
849
- );
850
- }
851
- })();
865
+ // average document length) used by the v2 sparse channel, then
866
+ // re-seed v2 skill entries so any skill vectors written during the
867
+ // cold-start window with the legacy TF encoder get rewritten with
868
+ // stemmed BM25 vectors. Fire-and-forget for the same reason as PKB
869
+ // reconcile — the stats and skill reseed are optional optimizations,
870
+ // never boot-blocking dependencies.
871
+ void rebuildBm25CorpusStatsAndReseedSkills(config);
852
872
 
853
873
  // Validate every concept page's frontmatter against the strict
854
874
  // schema and emit a `warn` per offender. Surfaces schema drift
@@ -876,20 +896,10 @@ export async function runDaemon(): Promise<void> {
876
896
 
877
897
  // Seed capability graph nodes (new memory graph system)
878
898
  try {
879
- const {
880
- seedSkillGraphNodes,
881
- seedCliGraphNodes,
882
- seedUninstalledCatalogSkillMemories,
883
- } = await import("../memory/graph/capability-seed.js");
884
- seedSkillGraphNodes();
885
- maybeSeedMemoryV2Skills(config);
899
+ const { seedCliGraphNodes } =
900
+ await import("../memory/graph/capability-seed.js");
901
+ refreshSkillCapabilityMemories(config);
886
902
  await seedCliGraphNodes();
887
- void seedUninstalledCatalogSkillMemories().catch((err) =>
888
- log.warn(
889
- { err },
890
- "Uninstalled catalog skill memory seeding failed — continuing",
891
- ),
892
- );
893
903
  } catch (err) {
894
904
  log.warn({ err }, "Graph capability seeding failed — continuing");
895
905
  }
@@ -254,10 +254,11 @@ export class MeetHostSupervisor {
254
254
  }
255
255
 
256
256
  /**
257
- * Ensure the meet-host child is spawned, the IPC handshake has been
258
- * received, and the manifest hash validated. Idempotent: a second
259
- * call while the child is already running is a no-op; a second call
260
- * during an in-flight spawn awaits the same promise.
257
+ * Ensure the meet-host child is spawned and the IPC handshake has
258
+ * been received. Idempotent: a second call while the child is
259
+ * already running is a no-op; a second call during an in-flight
260
+ * spawn awaits the same promise. Manifest hash validation is
261
+ * currently dormant (see {@link notifyHandshake}).
261
262
  */
262
263
  ensureRunning(): Promise<void> {
263
264
  if (this.shuttingDown) {
@@ -35,6 +35,55 @@ export function maybeSeedMemoryV2Skills(config: AssistantConfig): void {
35
35
  );
36
36
  }
37
37
 
38
+ /**
39
+ * Build the v2 BM25 corpus stats (per-token document frequencies + avg doc
40
+ * length), then re-seed the v2 skill entries so any skills written during
41
+ * cold start with the legacy TF encoder get rewritten with stemmed BM25
42
+ * vectors. The cold-start window exists because the very first
43
+ * `maybeSeedMemoryV2Skills` call can race ahead of the corpus-stats build —
44
+ * `skill-store.runSeedOnce` falls back to `generateSparseEmbedding` while
45
+ * `getConceptPageCorpusStats()` is still `null`, leaving stored skill
46
+ * sparse vectors in a different hash space than the BM25 query vectors
47
+ * callers issue (see `simBatch`, `activation.selectCandidates`). Reseeding
48
+ * here closes that gap without operator intervention.
49
+ *
50
+ * Fire-and-forget by design — startup must not block on either step. The
51
+ * reseed depends on the corpus-stats build, so a corpus-stats failure
52
+ * short-circuits and skips the reseed (the BM25 vectors it would produce
53
+ * would be wrong without fresh stats). Both steps log and swallow their own
54
+ * errors so neither blocks startup.
55
+ */
56
+ export async function rebuildBm25CorpusStatsAndReseedSkills(
57
+ config: AssistantConfig,
58
+ ): Promise<void> {
59
+ try {
60
+ const { rebuildConceptPageCorpusStats } =
61
+ await import("../memory/v2/sparse-bm25.js");
62
+ await rebuildConceptPageCorpusStats(getWorkspaceDir());
63
+ log.info("Memory v2 BM25 corpus stats built");
64
+ } catch (err) {
65
+ log.warn(
66
+ { err },
67
+ "BM25 corpus-stats rebuild failed — sparse channel will fall back to TF-only until next rebuild",
68
+ );
69
+ return;
70
+ }
71
+
72
+ if (!config.memory.v2.enabled) return;
73
+ try {
74
+ const { seedV2SkillEntries } = await import("../memory/v2/skill-store.js");
75
+ await seedV2SkillEntries({ throwOnError: true });
76
+ log.info(
77
+ "Memory v2 skill embeddings re-seeded with BM25 vectors after corpus-stats build",
78
+ );
79
+ } catch (err) {
80
+ log.warn(
81
+ { err },
82
+ "Failed to re-seed v2 skill entries after BM25 corpus-stats build — skills seeded during cold start may keep TF-only sparse vectors until next reseed",
83
+ );
84
+ }
85
+ }
86
+
38
87
  /**
39
88
  * Reconcile the v2 concept-page Qdrant collection with the expected schema
40
89
  * and enqueue `memory_v2_reembed` when the collection is missing data.
@@ -149,6 +149,7 @@ import type {
149
149
  export interface SubagentEvent {
150
150
  type: "subagent_event";
151
151
  subagentId: string;
152
+ conversationId: string;
152
153
  event: ServerMessage;
153
154
  }
154
155
 
@@ -224,9 +224,18 @@ export interface ConversationTitleUpdated {
224
224
  interface ChannelBinding {
225
225
  sourceChannel: ChannelId;
226
226
  externalChatId: string;
227
+ externalThreadId?: string | null;
227
228
  externalUserId?: string | null;
228
229
  displayName?: string | null;
229
230
  username?: string | null;
231
+ slackThread?: {
232
+ channelId: string;
233
+ threadTs: string;
234
+ link?: {
235
+ appUrl?: string;
236
+ webUrl?: string;
237
+ };
238
+ };
230
239
  }
231
240
 
232
241
  /** Attention state metadata for a conversation's latest assistant message. */
@@ -411,6 +420,7 @@ export interface HistoryResponse {
411
420
  status: "running" | "completed" | "failed" | "aborted";
412
421
  error?: string;
413
422
  conversationId?: string;
423
+ objective?: string;
414
424
  };
415
425
  /** True when text or tool result content was truncated due to maxTextChars/maxToolResultChars. */
416
426
  wasTruncated?: boolean;
@@ -535,6 +545,7 @@ export type ConversationErrorCode =
535
545
  | "PROVIDER_ORDERING"
536
546
  | "PROVIDER_WEB_SEARCH"
537
547
  | "PROVIDER_NOT_CONFIGURED"
548
+ | "PROVIDER_INVALID_KEY"
538
549
  | "MANAGED_KEY_INVALID"
539
550
  | "CONTEXT_TOO_LARGE"
540
551
  | "CONVERSATION_ABORTED"
@@ -552,6 +563,20 @@ export interface ConversationErrorMessage {
552
563
  debugDetails?: string;
553
564
  /** Machine-readable error category for log report metadata and triage. */
554
565
  errorCategory?: string;
566
+ /**
567
+ * Name of the `provider_connections` row in play when the error occurred.
568
+ * Surfaced by the macOS chat banner so users know which connection to fix
569
+ * (e.g. an invalid API key on `my-anthropic`). Optional because some
570
+ * errors fire before a connection is resolved.
571
+ */
572
+ connectionName?: string;
573
+ /**
574
+ * Name of the resolved profile (`llm.activeProfile` or per-call override)
575
+ * in play when the error occurred. Lets the macOS chat banner point
576
+ * users at the right profile even when the connection name is generic.
577
+ * Optional because some errors fire before a profile is resolved.
578
+ */
579
+ profileName?: string;
555
580
  }
556
581
 
557
582
  /** Reason the conversation list was invalidated. */
@@ -235,6 +235,66 @@ export interface SecretRequest {
235
235
  allowOneTimeSend?: boolean;
236
236
  }
237
237
 
238
+ export interface QuestionOption {
239
+ id: string;
240
+ label: string;
241
+ description?: string;
242
+ }
243
+
244
+ /**
245
+ * One entry in a batched ask-question request.
246
+ *
247
+ * `id` is daemon-assigned (e.g. `q1`, `q2`...) — the LLM neither sees nor
248
+ * supplies it. It exists so the client has a stable handle to post the
249
+ * user's answer back against. See `QuestionRequest` for the batching
250
+ * contract.
251
+ */
252
+ export interface QuestionEntry {
253
+ id: string;
254
+ question: string;
255
+ description?: string;
256
+ /** LLM-supplied options, capped at 4. The client always renders a fixed
257
+ * 5th "Type something else" slot wired to a free-text response — so this
258
+ * array never represents the full choice set the user sees. */
259
+ options: QuestionOption[];
260
+ /** Optional placeholder shown in the free-text input. */
261
+ freeTextPlaceholder?: string;
262
+ }
263
+
264
+ /**
265
+ * A single broadcast that carries either one or a small batch (≤5) of
266
+ * clarifying questions. The whole batch is one card lifecycle on the client:
267
+ * one render, one state machine, one response submission.
268
+ *
269
+ * Wire-compat plan: both shapes are populated on every broadcast.
270
+ * - `questions[]` is the canonical shape new clients should consume.
271
+ * - The flat `question` / `description` / `options` / `freeTextPlaceholder`
272
+ * fields mirror `questions[0]` for backwards compat with the existing
273
+ * web client, which keys off the flat fields. Once that client adopts
274
+ * `questions[]`, the flat fields can be dropped (separate cleanup).
275
+ *
276
+ * Daemon callers that don't supply a batch get a one-element `questions`
277
+ * array synthesized from the flat fields.
278
+ */
279
+ export interface QuestionRequest {
280
+ type: "question_request";
281
+ requestId: string;
282
+ /** Batched-question payload. Always populated (single questions are sent
283
+ * as a one-element array). Each entry's `id` is daemon-assigned. */
284
+ questions: QuestionEntry[];
285
+ /** Legacy: mirrors `questions[0].question`. Kept populated for clients
286
+ * that haven't adopted the batched `questions[]` shape yet. */
287
+ question: string;
288
+ /** Legacy: mirrors `questions[0].description`. */
289
+ description?: string;
290
+ /** Legacy: mirrors `questions[0].options`. */
291
+ options: QuestionOption[];
292
+ /** Legacy: mirrors `questions[0].freeTextPlaceholder`. */
293
+ freeTextPlaceholder?: string;
294
+ conversationId?: string;
295
+ toolUseId?: string;
296
+ }
297
+
238
298
  export interface MessageComplete {
239
299
  type: "message_complete";
240
300
  conversationId?: string;
@@ -430,6 +490,7 @@ export type _MessagesServerMessages =
430
490
  | ToolResult
431
491
  | ConfirmationRequest
432
492
  | SecretRequest
493
+ | QuestionRequest
433
494
  | MessageComplete
434
495
  | ErrorMessage
435
496
  | MessageQueued
@@ -26,6 +26,7 @@ export interface SubagentDetailResponse {
26
26
  type: "subagent_detail_response";
27
27
  subagentId: string;
28
28
  objective?: string;
29
+ usage?: UsageStats;
29
30
  events: Array<{
30
31
  type: string;
31
32
  content: string;
@@ -5,6 +5,7 @@ export const SYNC_TAGS = {
5
5
  assistantIdentity: "assistant:self:identity",
6
6
  assistantConfig: "assistant:self:config",
7
7
  assistantSounds: "assistant:self:sounds",
8
+ assistantSchedules: "assistant:self:schedules",
8
9
  conversationsList: "conversations:list",
9
10
  } as const;
10
11
 
@@ -14,7 +14,7 @@ const BASE_REMINDER_DEFAULT =
14
14
  // `memory-retrospective` feature flag is on.
15
15
  const BASE_REMINDER_RELAXED =
16
16
  "<system_reminder>" +
17
- "\nStay present in this conversation. Use `remember` when something feels worth pausing to mark — corrections (highest priority), plans, decisions, felt moments, things the user asks you to hold onto. You don't have to capture everything in the moment — a retrospective pass reviews this conversation in the background and saves what you didn't capture." +
17
+ "\nStay present in this conversation. Use `remember` when something feels worth pausing to mark — corrections (highest priority), plans, decisions, felt moments. You don't have to capture everything in the moment — a retrospective pass reviews this conversation in the background and saves what you didn't capture." +
18
18
  "\nIf you're unsure about something that may live in the workspace — past decisions, prior conversations, files — use `recall` before asking or guessing." +
19
19
  "\n</system_reminder>";
20
20
 
@@ -3,7 +3,7 @@ const BODY_DEFAULT =
3
3
  "\nIf you're unsure about something that may live in the workspace — past decisions, prior conversations, files — use `recall` before asking or guessing.";
4
4
 
5
5
  const BODY_RELAXED =
6
- "\nStay present in this conversation. Use `remember` when something feels worth pausing to mark — corrections (highest priority), plans, decisions, felt moments, things the user asks you to hold onto. You don't have to capture everything in the moment — a retrospective pass reviews this conversation in the background and saves what you didn't capture." +
6
+ "\nStay present in this conversation. Use `remember` when something feels worth pausing to mark — corrections (highest priority), plans, decisions, felt moments. You don't have to capture everything in the moment — a retrospective pass reviews this conversation in the background and saves what you didn't capture." +
7
7
  "\nIf you're unsure about something that may live in the workspace — past decisions, prior conversations, files — use `recall` before asking or guessing.";
8
8
 
9
9
  /**