@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
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Tests for the GET /v1/acp/sessions list handler — specifically the SQL
3
+ * pad amount applied to acp_session_history when in-memory sessions are
4
+ * also present. We mock the DB layer so the limit value passed to the
5
+ * query builder can be asserted directly.
6
+ *
7
+ * Regression for codex P2 feedback on PR #29993: padding by the global
8
+ * inMemory.length over-fetched when many unrelated sessions were live
9
+ * but only one conversation was being queried.
10
+ */
11
+
12
+ import { afterAll, describe, expect, mock, test } from "bun:test";
13
+
14
+ import type { AcpSessionState } from "../../acp/index.js";
15
+
16
+ const inMemoryStates = new Map<string, AcpSessionState>();
17
+
18
+ mock.module("../../acp/index.js", () => ({
19
+ getAcpSessionManager: () => ({
20
+ getStatus: (id?: string) => {
21
+ if (id === undefined) return Array.from(inMemoryStates.values());
22
+ const state = inMemoryStates.get(id);
23
+ if (!state) throw new Error(`ACP session "${id}" not found`);
24
+ return state;
25
+ },
26
+ }),
27
+ }));
28
+
29
+ // Spy on the drizzle limit() call to assert the exact value the handler
30
+ // passes to SQL. The stub mirrors only the chained methods used by
31
+ // listMergedSessions; calls fall through to a final `.all()` returning [].
32
+ const capturedLimits: number[] = [];
33
+
34
+ mock.module("../../memory/db-connection.js", () => {
35
+ const builder: any = {};
36
+ builder.select = () => builder;
37
+ builder.from = () => builder;
38
+ builder.where = () => builder;
39
+ builder.orderBy = () => builder;
40
+ builder.limit = (n: number) => {
41
+ capturedLimits.push(n);
42
+ return builder;
43
+ };
44
+ builder.all = () => [];
45
+ return {
46
+ getDb: () => builder,
47
+ };
48
+ });
49
+
50
+ const { ROUTES } = await import("./acp-routes.js");
51
+
52
+ function getListHandler() {
53
+ const route = ROUTES.find(
54
+ (r: { endpoint: string; method: string }) =>
55
+ r.endpoint === "acp/sessions" && r.method === "GET",
56
+ );
57
+ if (!route) throw new Error("GET acp/sessions route not registered");
58
+ return route.handler;
59
+ }
60
+
61
+ function makeInMemoryState(
62
+ id: string,
63
+ parentConversationId: string,
64
+ ): AcpSessionState {
65
+ return {
66
+ id,
67
+ agentId: "claude",
68
+ acpSessionId: `proto-${id}`,
69
+ parentConversationId,
70
+ status: "running",
71
+ startedAt: 1_700_000_000_000,
72
+ };
73
+ }
74
+
75
+ afterAll(() => {
76
+ inMemoryStates.clear();
77
+ capturedLimits.length = 0;
78
+ });
79
+
80
+ describe("GET /v1/acp/sessions — SQL pad amount", () => {
81
+ test("pads SQL limit by the conversation-filtered in-memory count, not the global one", async () => {
82
+ inMemoryStates.clear();
83
+ capturedLimits.length = 0;
84
+
85
+ // Many in-memory sessions for an unrelated conversation, plus one
86
+ // that matches the target conversation.
87
+ for (let i = 0; i < 25; i++) {
88
+ inMemoryStates.set(
89
+ `other-${i}`,
90
+ makeInMemoryState(`other-${i}`, "conv-other"),
91
+ );
92
+ }
93
+ inMemoryStates.set(
94
+ "target-mem",
95
+ makeInMemoryState("target-mem", "conv-target"),
96
+ );
97
+
98
+ const handler = getListHandler();
99
+ await handler({
100
+ queryParams: { conversationId: "conv-target", limit: "10" },
101
+ });
102
+
103
+ expect(capturedLimits).toHaveLength(1);
104
+ // pad = filtered in-memory count (1), not global (26).
105
+ expect(capturedLimits[0]).toBe(11);
106
+ });
107
+
108
+ test("pads by zero when no in-memory sessions match the target conversation", async () => {
109
+ inMemoryStates.clear();
110
+ capturedLimits.length = 0;
111
+
112
+ for (let i = 0; i < 10; i++) {
113
+ inMemoryStates.set(
114
+ `other-${i}`,
115
+ makeInMemoryState(`other-${i}`, "conv-other"),
116
+ );
117
+ }
118
+
119
+ const handler = getListHandler();
120
+ await handler({
121
+ queryParams: { conversationId: "conv-target", limit: "5" },
122
+ });
123
+
124
+ expect(capturedLimits).toEqual([5]);
125
+ });
126
+
127
+ test("pads by the full in-memory count when no conversation filter is supplied", async () => {
128
+ inMemoryStates.clear();
129
+ capturedLimits.length = 0;
130
+
131
+ for (let i = 0; i < 4; i++) {
132
+ inMemoryStates.set(
133
+ `sess-${i}`,
134
+ makeInMemoryState(`sess-${i}`, `conv-${i}`),
135
+ );
136
+ }
137
+
138
+ const handler = getListHandler();
139
+ await handler({ queryParams: { limit: "20" } });
140
+
141
+ expect(capturedLimits).toEqual([24]);
142
+ });
143
+ });
@@ -373,11 +373,13 @@ function listMergedSessions(opts: {
373
373
  : baseQuery;
374
374
  // Fetch only enough rows to fill the requested page after merging with
375
375
  // in-memory sessions. In-memory entries take precedence on id collision,
376
- // so we pad by inMemory.length to guarantee we still surface `limit`
377
- // distinct rows even when every in-memory session shadows a DB row.
376
+ // so we pad by the count that survived the conversation filter to
377
+ // guarantee we still surface `limit` distinct rows even when every
378
+ // in-memory session shadows a DB row — without over-fetching when many
379
+ // unrelated sessions are in memory.
378
380
  const historyRows = filtered
379
381
  .orderBy(desc(acpSessionHistory.startedAt))
380
- .limit(opts.limit + inMemory.length)
382
+ .limit(opts.limit + merged.size)
381
383
  .all();
382
384
 
383
385
  for (const row of historyRows) {
@@ -12,7 +12,7 @@ import {
12
12
  getPlatformOrganizationId,
13
13
  getPlatformUserId,
14
14
  } from "../../config/env.js";
15
- import { resolveManagedProxyContext } from "../../providers/managed-proxy/context.js";
15
+ import { resolveManagedProxyContext } from "../../providers/platform-proxy/context.js";
16
16
  import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
17
17
 
18
18
  interface AuthInfoResult {
@@ -79,9 +79,11 @@ function handleCreateBookmark({
79
79
  );
80
80
  }
81
81
 
82
- const summary = createBookmark(getDb(), { messageId, conversationId });
83
- publishBookmarkCreated(summary);
84
- return summary;
82
+ const result = createBookmark(getDb(), { messageId });
83
+ if (result.inserted) {
84
+ publishBookmarkCreated(result.bookmark);
85
+ }
86
+ return result.bookmark;
85
87
  }
86
88
 
87
89
  function handleDeleteBookmarkByMessage({ pathParams = {} }: RouteHandlerArgs): {
@@ -16,6 +16,7 @@ import { existsSync, readFileSync } from "node:fs";
16
16
 
17
17
  import { z } from "zod";
18
18
 
19
+ import { getConfig } from "../../config/loader.js";
19
20
  import { readNowScratchpad } from "../../daemon/conversation-runtime-assembly.js";
20
21
  import { getOrCreateConversation } from "../../daemon/conversation-store.js";
21
22
  import { buildToolDefinitions } from "../../daemon/conversation-tool-setup.js";
@@ -92,7 +93,10 @@ async function handleBtw({
92
93
 
93
94
  // ----- Greeting context enrichment -----
94
95
  let effectiveContent = trimmedContent;
95
- if (conversationKey === GREETING_KEY) {
96
+ if (
97
+ conversationKey === GREETING_KEY &&
98
+ getConfig().memory.retrieval.scratchpadInjection.enabled
99
+ ) {
96
100
  const now = readNowScratchpad();
97
101
  if (now) {
98
102
  effectiveContent = `${trimmedContent}\n\n<context>\n${now}\n</context>`;
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Route handler for channel availability.
3
+ *
4
+ * GET /v1/channels/available — return the channels this assistant can
5
+ * surface to clients (Contacts / GuardianChannels views, etc.) along with
6
+ * their display metadata (label, subtitle, icon, verification capability,
7
+ * setup-message copy). Today this is a fixed base list plus `email` when
8
+ * an inbox is registered; eventually the list will be driven by
9
+ * plugins/skills the assistant has loaded. Clients should treat the
10
+ * response as authoritative and stop carrying their own per-channel
11
+ * switches.
12
+ *
13
+ * Distinct from `/v1/channels/readiness` (which answers "is this channel
14
+ * configured and working?"). Availability answers "could this channel be
15
+ * surfaced for setup/verification at all?".
16
+ */
17
+
18
+ import { z } from "zod";
19
+
20
+ import {
21
+ CHANNEL_IDS,
22
+ CHANNEL_METADATA,
23
+ type ChannelId,
24
+ type ChannelInfo,
25
+ } from "../../channels/types.js";
26
+ import { VellumPlatformClient } from "../../platform/client.js";
27
+ import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
28
+
29
+ // Base list every assistant currently surfaces. Order is the display
30
+ // order clients should render. Keep stable — clients sort by index.
31
+ const BASE_AVAILABLE_CHANNELS: readonly ChannelId[] = [
32
+ "slack",
33
+ "telegram",
34
+ "phone",
35
+ ] as const;
36
+
37
+ interface EmailAddressListResponse {
38
+ count?: number;
39
+ results?: Array<{ id: string; address: string }>;
40
+ }
41
+
42
+ /**
43
+ * Best-effort check that an inbox address is registered for this
44
+ * assistant. A platform fetch failure is treated as "no inbox" — we
45
+ * prefer to under-report than block the entire Contacts page when the
46
+ * platform is briefly unreachable.
47
+ */
48
+ async function hasRegisteredInbox(): Promise<boolean> {
49
+ const client = await VellumPlatformClient.create();
50
+ if (!client?.platformAssistantId) {
51
+ return false;
52
+ }
53
+
54
+ try {
55
+ const response = await client.fetch(
56
+ `/v1/assistants/${client.platformAssistantId}/email-addresses/`,
57
+ );
58
+ if (!response.ok) {
59
+ return false;
60
+ }
61
+ const data = (await response.json()) as EmailAddressListResponse;
62
+ if (typeof data.count === "number") {
63
+ return data.count > 0;
64
+ }
65
+ return Array.isArray(data.results) && data.results.length > 0;
66
+ } catch {
67
+ return false;
68
+ }
69
+ }
70
+
71
+ async function handleGetChannelAvailability(
72
+ _args: RouteHandlerArgs,
73
+ ): Promise<{ channels: ChannelInfo[] }> {
74
+ const ids: ChannelId[] = [...BASE_AVAILABLE_CHANNELS];
75
+ if (await hasRegisteredInbox()) {
76
+ ids.push("email");
77
+ }
78
+ // CHANNEL_METADATA is `Partial<Record<ChannelId, ChannelInfo>>` because
79
+ // unsurfaced channels deliberately have no metadata. `ids` only ever
80
+ // contains channels that BASE_AVAILABLE_CHANNELS / the email branch
81
+ // explicitly chose, so the lookup is always defined — filter to satisfy
82
+ // the type system without a non-null assertion.
83
+ const channels = ids
84
+ .map((id) => CHANNEL_METADATA[id])
85
+ .filter((info): info is ChannelInfo => info !== undefined);
86
+ return { channels };
87
+ }
88
+
89
+ const channelInfoSchema = z.object({
90
+ id: z.enum(CHANNEL_IDS),
91
+ label: z.string(),
92
+ subtitle: z.string(),
93
+ icon: z.string(),
94
+ supportsVerification: z.boolean(),
95
+ setupMessages: z.object({
96
+ guardian: z.string(),
97
+ contact: z.string(),
98
+ }),
99
+ });
100
+
101
+ export const ROUTES: RouteDefinition[] = [
102
+ {
103
+ operationId: "channels_available_get",
104
+ endpoint: "channels/available",
105
+ method: "GET",
106
+ summary: "Get available channels",
107
+ description:
108
+ "Return the channels this assistant can surface to clients, with " +
109
+ "display metadata (label, icon, verification capability, setup " +
110
+ "copy). Today this is a fixed base list plus `email` when an inbox " +
111
+ "is registered; will become plugin/skill-driven in future.",
112
+ tags: ["channels"],
113
+ requirePolicyEnforcement: true,
114
+ handler: handleGetChannelAvailability,
115
+ responseBody: z.object({
116
+ channels: z
117
+ .array(channelInfoSchema)
118
+ .describe("Available channels in display order"),
119
+ }),
120
+ },
121
+ ];
@@ -6,15 +6,18 @@
6
6
  * used by the macOS / web clients.
7
7
  */
8
8
 
9
+ import { v4 as uuid } from "uuid";
9
10
  import { z } from "zod";
10
11
 
11
12
  import { clearAllConversations as clearAllActive } from "../../daemon/handlers/conversations.js";
12
13
  import { formatJson, formatMarkdown } from "../../export/formatter.js";
13
14
  import {
15
+ addMessage,
14
16
  createConversation,
15
17
  getConversation,
16
18
  getMessages,
17
19
  } from "../../memory/conversation-crud.js";
20
+ import { setConversationKey } from "../../memory/conversation-key-store.js";
18
21
  import { listConversations } from "../../memory/conversation-queries.js";
19
22
  import { getLogger } from "../../util/logger.js";
20
23
  import { BadRequestError, NotFoundError } from "./errors.js";
@@ -45,12 +48,43 @@ function handleListCli({ body = {} }: RouteHandlerArgs) {
45
48
  // create (CLI)
46
49
  // ---------------------------------------------------------------------------
47
50
 
48
- function handleCreateCli({ body = {} }: RouteHandlerArgs) {
51
+ const seededConversationMessageSchema = z.object({
52
+ role: z.enum(["user", "assistant"]),
53
+ content: z.string(),
54
+ });
55
+
56
+ type SeededConversationMessage = z.infer<
57
+ typeof seededConversationMessageSchema
58
+ >;
59
+
60
+ function textContentJson(text: string): string {
61
+ return JSON.stringify([{ type: "text", text }]);
62
+ }
63
+
64
+ async function handleCreateCli({ body = {} }: RouteHandlerArgs) {
49
65
  const title = body.title as string | undefined;
66
+ const messages =
67
+ (body.messages as SeededConversationMessage[] | undefined) ?? [];
68
+
50
69
  const conversation = createConversation(title);
70
+ const conversationKey = uuid();
71
+ setConversationKey(conversationKey, conversation.id);
72
+
73
+ for (const message of messages) {
74
+ await addMessage(
75
+ conversation.id,
76
+ message.role,
77
+ textContentJson(message.content),
78
+ undefined,
79
+ { skipIndexing: true },
80
+ );
81
+ }
82
+
51
83
  return {
52
84
  id: conversation.id,
53
85
  title: conversation.title ?? "New Conversation",
86
+ conversationKey,
87
+ messagesInserted: messages.length,
54
88
  };
55
89
  }
56
90
 
@@ -109,7 +143,10 @@ function handleExportCli({ body = {} }: RouteHandlerArgs) {
109
143
  async function handleClearCli(_args: RouteHandlerArgs) {
110
144
  // Tear down in-memory conversation state before DB clear.
111
145
  const cleared = clearAllActive();
112
- log.info({ cleared }, "CLI conversations clear: active conversations torn down");
146
+ log.info(
147
+ { cleared },
148
+ "CLI conversations clear: active conversations torn down",
149
+ );
113
150
  return { cleared };
114
151
  }
115
152
 
@@ -146,14 +183,18 @@ export const ROUTES: RouteDefinition[] = [
146
183
  endpoint: "conversations/cli/create",
147
184
  method: "POST",
148
185
  summary: "Create a conversation (CLI)",
149
- description: "Create a new conversation with an optional title.",
186
+ description:
187
+ "Create a new conversation with an optional title and seeded messages.",
150
188
  tags: ["conversations"],
151
189
  requestBody: z.object({
152
190
  title: z.string().optional(),
191
+ messages: z.array(seededConversationMessageSchema).optional(),
153
192
  }),
154
193
  responseBody: z.object({
155
194
  id: z.string(),
156
195
  title: z.string(),
196
+ conversationKey: z.string(),
197
+ messagesInserted: z.number().int().nonnegative(),
157
198
  }),
158
199
  handler: handleCreateCli,
159
200
  },
@@ -28,12 +28,11 @@ import { getBindingsForConversations } from "../../memory/external-conversation-
28
28
  import { listGroups } from "../../memory/group-crud.js";
29
29
  import { UserError } from "../../util/errors.js";
30
30
  import { getLogger } from "../../util/logger.js";
31
- import { buildAssistantEvent } from "../assistant-event.js";
32
- import { assistantEventHub } from "../assistant-event-hub.js";
33
31
  import {
34
32
  buildConversationDetailResponse,
35
33
  serializeConversationSummary,
36
34
  } from "../services/conversation-serializer.js";
35
+ import { publishConversationListAndMetadataChanged } from "../sync/resource-sync-events.js";
37
36
  import {
38
37
  BadRequestError,
39
38
  InternalError,
@@ -54,22 +53,6 @@ function resolveOrThrow(rawId: string): string {
54
53
  return id;
55
54
  }
56
55
 
57
- function publishListInvalidated(): void {
58
- assistantEventHub
59
- .publish(
60
- buildAssistantEvent({
61
- type: "conversation_list_invalidated",
62
- reason: "seen_changed",
63
- }),
64
- )
65
- .catch((err) => {
66
- log.warn(
67
- { err },
68
- "Failed to publish conversation_list_invalidated (seen_changed)",
69
- );
70
- });
71
- }
72
-
73
56
  // ---------------------------------------------------------------------------
74
57
  // Handlers
75
58
  // ---------------------------------------------------------------------------
@@ -159,7 +142,7 @@ function handleRecordSeen({ body = {} }: RouteHandlerArgs) {
159
142
  });
160
143
 
161
144
  if (wasUnseen) {
162
- publishListInvalidated();
145
+ publishConversationListAndMetadataChanged("seen_changed", conversationId);
163
146
  }
164
147
 
165
148
  return { ok: true };
@@ -179,7 +162,7 @@ function handleMarkUnread({ body = {} }: RouteHandlerArgs) {
179
162
  try {
180
163
  const changed = markConversationUnread(conversationId);
181
164
  if (changed) {
182
- publishListInvalidated();
165
+ publishConversationListAndMetadataChanged("seen_changed", conversationId);
183
166
  }
184
167
  return { ok: true };
185
168
  } catch (err) {
@@ -27,7 +27,6 @@ import {
27
27
  switchConversation,
28
28
  undoLastMessage,
29
29
  } from "../../daemon/handlers/conversations.js";
30
- import type { ConversationListInvalidatedReason } from "../../daemon/message-types/conversations.js";
31
30
  import { normalizeConversationType } from "../../daemon/message-types/shared.js";
32
31
  import {
33
32
  archiveConversation,
@@ -49,9 +48,12 @@ import { enqueueMemoryJob } from "../../memory/jobs-store.js";
49
48
  import { deleteSchedule } from "../../schedule/schedule-store.js";
50
49
  import { UserError } from "../../util/errors.js";
51
50
  import { getLogger } from "../../util/logger.js";
52
- import { buildAssistantEvent } from "../assistant-event.js";
53
- import { assistantEventHub } from "../assistant-event-hub.js";
54
51
  import { buildConversationDetailResponse } from "../services/conversation-serializer.js";
52
+ import {
53
+ publishConversationListAndMetadataChanged,
54
+ publishConversationListChanged,
55
+ publishConversationTitleChanged,
56
+ } from "../sync/resource-sync-events.js";
55
57
  import { BadRequestError, InternalError, NotFoundError } from "./errors.js";
56
58
  import { setInferenceProfileSession } from "./inference-profile-session-handler.js";
57
59
  import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
@@ -68,24 +70,6 @@ function resolveOrThrow(rawId: string): string {
68
70
  return id;
69
71
  }
70
72
 
71
- function publishListInvalidated(
72
- reason: ConversationListInvalidatedReason,
73
- ): void {
74
- assistantEventHub
75
- .publish(
76
- buildAssistantEvent({
77
- type: "conversation_list_invalidated",
78
- reason,
79
- }),
80
- )
81
- .catch((err) => {
82
- log.warn(
83
- { err },
84
- `Failed to publish conversation_list_invalidated (${reason})`,
85
- );
86
- });
87
- }
88
-
89
73
  function cancelScheduleIfLast(conversationId: string): void {
90
74
  const conv = getConversation(conversationId);
91
75
  if (
@@ -108,6 +92,7 @@ function handleCreateConversation({ body = {} }: RouteHandlerArgs) {
108
92
  });
109
93
  if (result.created) {
110
94
  updateConversationTitle(result.conversationId, "New Conversation");
95
+ publishConversationListAndMetadataChanged("created", result.conversationId);
111
96
  }
112
97
  log.info(
113
98
  {
@@ -151,6 +136,7 @@ async function handleForkConversation({ body = {} }: RouteHandlerArgs) {
151
136
  `Forked conversation ${forkedConversation.id} could not be loaded`,
152
137
  );
153
138
  }
139
+ publishConversationListAndMetadataChanged("created", forkedConversation.id);
154
140
  return { conversation: detail.conversation };
155
141
  } catch (err) {
156
142
  if (err instanceof UserError) {
@@ -217,25 +203,7 @@ function handleRenameConversation({
217
203
  }
218
204
  updateConversationTitle(pathParams.id!, name, 0);
219
205
 
220
- assistantEventHub
221
- .publish(
222
- buildAssistantEvent(
223
- {
224
- type: "conversation_title_updated",
225
- conversationId: pathParams.id!,
226
- title: name,
227
- },
228
- pathParams.id!,
229
- ),
230
- )
231
- .catch((err) => {
232
- log.warn(
233
- { err, conversationId: pathParams.id },
234
- "Failed to publish conversation_title_updated",
235
- );
236
- });
237
-
238
- publishListInvalidated("renamed");
206
+ publishConversationTitleChanged(pathParams.id!, name);
239
207
 
240
208
  return { ok: true };
241
209
  }
@@ -249,6 +217,7 @@ function handleClearAllConversations({ headers = {} }: RouteHandlerArgs) {
249
217
  );
250
218
  }
251
219
  clearAllConversations();
220
+ publishConversationListChanged("deleted");
252
221
  return undefined;
253
222
  }
254
223
 
@@ -279,6 +248,7 @@ function handleWipeConversation({ pathParams = {} }: RouteHandlerArgs) {
279
248
  },
280
249
  "Wiped conversation and reverted memory changes",
281
250
  );
251
+ publishConversationListAndMetadataChanged("deleted", resolvedId);
282
252
  return {
283
253
  wiped: true,
284
254
  unsupersededItems: 0,
@@ -308,7 +278,7 @@ function handleDeleteConversation({ pathParams = {} }: RouteHandlerArgs) {
308
278
  }
309
279
  log.info({ conversationId: resolvedId }, "Deleted conversation");
310
280
 
311
- publishListInvalidated("deleted");
281
+ publishConversationListAndMetadataChanged("deleted", resolvedId);
312
282
 
313
283
  return undefined;
314
284
  }
@@ -319,6 +289,7 @@ function handleArchiveConversation({ pathParams = {} }: RouteHandlerArgs) {
319
289
  if (!archived) {
320
290
  throw new NotFoundError(`Conversation ${pathParams.id} not found`);
321
291
  }
292
+ publishConversationListAndMetadataChanged("reordered", resolvedId);
322
293
  return { ok: true, conversationId: resolvedId };
323
294
  }
324
295
 
@@ -328,6 +299,7 @@ function handleUnarchiveConversation({ pathParams = {} }: RouteHandlerArgs) {
328
299
  if (!unarchived) {
329
300
  throw new NotFoundError(`Conversation ${pathParams.id} not found`);
330
301
  }
302
+ publishConversationListAndMetadataChanged("reordered", resolvedId);
331
303
  return { ok: true, conversationId: resolvedId };
332
304
  }
333
305
 
@@ -387,7 +359,10 @@ function handleReorderConversations({ body = {} }: RouteHandlerArgs) {
387
359
  groupId: u.groupId,
388
360
  })),
389
361
  );
390
- publishListInvalidated("reordered");
362
+ publishConversationListAndMetadataChanged(
363
+ "reordered",
364
+ updates.map((u) => u.conversationId),
365
+ );
391
366
  return { ok: true };
392
367
  }
393
368