@vellumai/assistant 0.8.1 → 0.8.3

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 (630) hide show
  1. package/ARCHITECTURE.md +13 -19
  2. package/Dockerfile +75 -1
  3. package/bun.lock +11 -1
  4. package/docker-entrypoint.sh +17 -0
  5. package/docker-init-apt-root.sh +167 -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 +642 -5
  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-loop-exit-reason.test.ts +272 -0
  21. package/src/__tests__/agent-loop-provider-error-recording.test.ts +195 -0
  22. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +131 -0
  23. package/src/__tests__/anthropic-provider.test.ts +45 -0
  24. package/src/__tests__/app-builder-tool-scripts.test.ts +9 -3
  25. package/src/__tests__/app-executors.test.ts +220 -4
  26. package/src/__tests__/auto-analysis-end-to-end.test.ts +35 -0
  27. package/src/__tests__/bundled-asset.test.ts +6 -6
  28. package/src/__tests__/channel-availability-routes.test.ts +206 -0
  29. package/src/__tests__/channel-delivery-store.test.ts +289 -1
  30. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -1
  31. package/src/__tests__/clawhub.test.ts +75 -16
  32. package/src/__tests__/compactor-tail-resolution.test.ts +147 -0
  33. package/src/__tests__/config-get-vision-flag.test.ts +136 -0
  34. package/src/__tests__/config-loader-backfill.test.ts +115 -18
  35. package/src/__tests__/config-schema.test.ts +21 -0
  36. package/src/__tests__/config-set-route.test.ts +80 -0
  37. package/src/__tests__/config-sounds-sync.test.ts +97 -0
  38. package/src/__tests__/config-watcher-skill-reseed.test.ts +453 -0
  39. package/src/__tests__/context-search-conversations-source.test.ts +117 -2
  40. package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
  41. package/src/__tests__/context-search-workspace-source.test.ts +7 -0
  42. package/src/__tests__/context-token-estimator.test.ts +31 -65
  43. package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
  44. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
  45. package/src/__tests__/conversation-agent-loop-overflow.test.ts +92 -92
  46. package/src/__tests__/conversation-agent-loop.test.ts +59 -1
  47. package/src/__tests__/conversation-error.test.ts +42 -3
  48. package/src/__tests__/conversation-fork-crud.test.ts +82 -0
  49. package/src/__tests__/conversation-inference-profile-route.test.ts +40 -4
  50. package/src/__tests__/conversation-lifecycle.test.ts +173 -0
  51. package/src/__tests__/conversation-media-retry.test.ts +19 -8
  52. package/src/__tests__/conversation-message-sync-tags.test.ts +97 -0
  53. package/src/__tests__/conversation-pairing.test.ts +54 -0
  54. package/src/__tests__/conversation-process-callsite.test.ts +4 -1
  55. package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -1
  56. package/src/__tests__/conversation-queue.test.ts +4 -1
  57. package/src/__tests__/conversation-runtime-assembly.test.ts +102 -13
  58. package/src/__tests__/conversation-slash-queue.test.ts +59 -1
  59. package/src/__tests__/conversation-slash-unknown.test.ts +4 -1
  60. package/src/__tests__/conversation-surfaces-table-action.test.ts +360 -0
  61. package/src/__tests__/conversation-sync-tags.test.ts +235 -0
  62. package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
  63. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
  64. package/src/__tests__/credential-security-invariants.test.ts +3 -2
  65. package/src/__tests__/date-context.test.ts +45 -0
  66. package/src/__tests__/db-slack-external-content-normalization.test.ts +301 -0
  67. package/src/__tests__/delete-managed-skill-tool.test.ts +55 -13
  68. package/src/__tests__/disk-pressure-tools.test.ts +1 -0
  69. package/src/__tests__/dm-backfill.test.ts +121 -10
  70. package/src/__tests__/document-tool-security.test.ts +258 -0
  71. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
  72. package/src/__tests__/edit-propagation.test.ts +33 -0
  73. package/src/__tests__/empty-response-pipeline.test.ts +0 -4
  74. package/src/__tests__/external-plugin-loader.test.ts +151 -55
  75. package/src/__tests__/filing-service.test.ts +140 -0
  76. package/src/__tests__/get-skill-detail-audit.test.ts +0 -4
  77. package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +0 -1
  78. package/src/__tests__/guardian-dispatch.test.ts +1 -0
  79. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +43 -62
  80. package/src/__tests__/heartbeat-service.test.ts +24 -164
  81. package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
  82. package/src/__tests__/helpers/tar-fixtures.ts +39 -0
  83. package/src/__tests__/helpers/wait-for.ts +21 -0
  84. package/src/__tests__/history-repair-pipeline.test.ts +0 -3
  85. package/src/__tests__/history-repair.test.ts +73 -0
  86. package/src/__tests__/host-app-control-proxy.test.ts +507 -10
  87. package/src/__tests__/host-proxy-preactivation.test.ts +200 -13
  88. package/src/__tests__/image-credentials.test.ts +1 -1
  89. package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
  90. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +1 -1
  91. package/src/__tests__/inference-profile-reaper.test.ts +4 -2
  92. package/src/__tests__/inference-profile-session-handler.test.ts +18 -6
  93. package/src/__tests__/inference-profile-session-ipc.test.ts +17 -5
  94. package/src/__tests__/injector-background-turn.test.ts +153 -0
  95. package/src/__tests__/injector-chain.test.ts +15 -8
  96. package/src/__tests__/install-skill-routing.test.ts +155 -37
  97. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +99 -3
  98. package/src/__tests__/list-messages-page-latest.test.ts +55 -0
  99. package/src/__tests__/llm-call-pipeline.test.ts +0 -3
  100. package/src/__tests__/llm-callsite-catalog.test.ts +25 -0
  101. package/src/__tests__/llm-catalog-parity.test.ts +58 -13
  102. package/src/__tests__/llm-request-log-agent-loop-exit-reason.test.ts +116 -0
  103. package/src/__tests__/llm-request-log-error-payload.test.ts +138 -0
  104. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +36 -0
  105. package/src/__tests__/llm-request-log-source-factory.test.ts +29 -53
  106. package/src/__tests__/llm-resolver.test.ts +255 -2
  107. package/src/__tests__/llm-usage-store.test.ts +114 -0
  108. package/src/__tests__/managed-profile-guard.test.ts +41 -29
  109. package/src/__tests__/managed-skill-lifecycle.test.ts +109 -18
  110. package/src/__tests__/managed-store.test.ts +84 -192
  111. package/src/__tests__/media-generate-image.test.ts +1 -1
  112. package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -2
  113. package/src/__tests__/messages-after-tiebreaker.test.ts +122 -0
  114. package/src/__tests__/notification-decision-fallback.test.ts +0 -91
  115. package/src/__tests__/notification-decision-strategy.test.ts +14 -31
  116. package/src/__tests__/notification-deep-link.test.ts +15 -0
  117. package/src/__tests__/notification-guardian-path.test.ts +1 -2
  118. package/src/__tests__/notification-platform-adapter.test.ts +5 -4
  119. package/src/__tests__/notification-telegram-adapter.test.ts +1 -0
  120. package/src/__tests__/notification-vellum-adapter.test.ts +113 -0
  121. package/src/__tests__/oauth-commands-routes.test.ts +168 -16
  122. package/src/__tests__/oauth-provider-profiles.test.ts +9 -0
  123. package/src/__tests__/openai-provider.test.ts +242 -3
  124. package/src/__tests__/openai-responses-cutover-guard.test.ts +17 -9
  125. package/src/__tests__/openrouter-provider-only.test.ts +51 -3
  126. package/src/__tests__/openrouter-token-estimation.test.ts +34 -25
  127. package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -2
  128. package/src/__tests__/persistence-pipeline.test.ts +0 -2
  129. package/src/__tests__/{managed-proxy-context.test.ts → platform-proxy-context.test.ts} +7 -2
  130. package/src/__tests__/platform.test.ts +2 -0
  131. package/src/__tests__/plugin-api-shim.test.ts +125 -0
  132. package/src/__tests__/plugin-bootstrap.test.ts +10 -36
  133. package/src/__tests__/plugin-external-api.test.ts +68 -0
  134. package/src/__tests__/plugin-registry.test.ts +0 -77
  135. package/src/__tests__/plugin-route-contribution.test.ts +0 -1
  136. package/src/__tests__/plugin-skill-contribution.test.ts +0 -2
  137. package/src/__tests__/plugin-tool-contribution.test.ts +16 -15
  138. package/src/__tests__/plugin-types.test.ts +3 -13
  139. package/src/__tests__/process-message-background-slack.test.ts +8 -1
  140. package/src/__tests__/process-message-display-content.test.ts +421 -0
  141. package/src/__tests__/provider-catalog-visibility.test.ts +158 -0
  142. package/src/__tests__/provider-error-scenarios.test.ts +111 -0
  143. package/src/__tests__/{provider-managed-proxy-integration.test.ts → provider-platform-proxy-integration.test.ts} +33 -31
  144. package/src/__tests__/scaffold-managed-skill-tool.test.ts +65 -13
  145. package/src/__tests__/schedule-routes.test.ts +50 -3
  146. package/src/__tests__/schedule-store.test.ts +94 -0
  147. package/src/__tests__/scheduler-reuse-conversation.test.ts +54 -7
  148. package/src/__tests__/schema-transforms.test.ts +20 -0
  149. package/src/__tests__/search-skills-unified.test.ts +0 -5
  150. package/src/__tests__/{secret-routes-managed-proxy.test.ts → secret-routes-platform-proxy.test.ts} +1 -1
  151. package/src/__tests__/server-history-render.test.ts +43 -0
  152. package/src/__tests__/skill-load-feature-flag.test.ts +0 -12
  153. package/src/__tests__/skill-load-tool.test.ts +27 -89
  154. package/src/__tests__/skill-memory.test.ts +23 -3
  155. package/src/__tests__/skills-file-content-endpoint.test.ts +9 -38
  156. package/src/__tests__/skills-files-catalog-fallback.test.ts +0 -3
  157. package/src/__tests__/skills-install-extract.test.ts +49 -38
  158. package/src/__tests__/skills-install-staging.test.ts +159 -0
  159. package/src/__tests__/skills-uninstall.test.ts +9 -41
  160. package/src/__tests__/skills.test.ts +51 -58
  161. package/src/__tests__/slack-channel-config.test.ts +9 -0
  162. package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
  163. package/src/__tests__/system-prompt.test.ts +670 -63
  164. package/src/__tests__/terminal-tools.test.ts +28 -1
  165. package/src/__tests__/thread-backfill.test.ts +557 -27
  166. package/src/__tests__/title-generate-pipeline.test.ts +0 -13
  167. package/src/__tests__/token-estimate-pipeline.test.ts +0 -3
  168. package/src/__tests__/tool-error-pipeline.test.ts +0 -3
  169. package/src/__tests__/tool-execute-pipeline.test.ts +0 -5
  170. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
  171. package/src/__tests__/tool-executor.test.ts +16 -4
  172. package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -12
  173. package/src/__tests__/turn-events-store.test.ts +256 -0
  174. package/src/__tests__/twilio-routes.test.ts +4 -0
  175. package/src/__tests__/user-plugin-loader.test.ts +0 -7
  176. package/src/__tests__/voice-session-bridge.test.ts +198 -0
  177. package/src/__tests__/web-search-catalog-parity.test.ts +32 -10
  178. package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +115 -3
  179. package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +50 -0
  180. package/src/__tests__/workspace-migration-073-repair-recall-callsite-empty-profile.test.ts +153 -0
  181. package/src/__tests__/workspace-migration-085-memory-v2-bm25-b-reembed-disabled-v2-pages.test.ts +220 -0
  182. package/src/__tests__/workspace-migration-086-revert-stale-gemini-mis-rewrites.test.ts +269 -0
  183. package/src/__tests__/workspace-migration-087-memory-router-balanced-profile.test.ts +228 -0
  184. package/src/__tests__/workspace-migration-remove-legacy-skills-index.test.ts +309 -0
  185. package/src/__tests__/workspace-migrations-runner.test.ts +111 -3
  186. package/src/a2a/__tests__/agent-card.test.ts +98 -0
  187. package/src/a2a/__tests__/e2e-a2a-channel.test.ts +597 -0
  188. package/src/a2a/__tests__/protocol-helpers.test.ts +113 -0
  189. package/src/a2a/__tests__/task-store.test.ts +246 -0
  190. package/src/a2a/agent-card.ts +58 -0
  191. package/src/a2a/feature-gate.ts +8 -0
  192. package/src/a2a/protocol-constants.ts +21 -0
  193. package/src/a2a/protocol-errors.ts +50 -0
  194. package/src/a2a/protocol-types.ts +162 -0
  195. package/src/a2a/task-store.ts +168 -0
  196. package/src/acp/resolve-agent.ts +1 -1
  197. package/src/agent/image-optimize.ts +13 -5
  198. package/src/agent/loop.ts +167 -18
  199. package/src/calls/voice-session-bridge.ts +61 -42
  200. package/src/channels/config.ts +9 -0
  201. package/src/channels/types.ts +122 -0
  202. package/src/cli/__tests__/unknown-command.test.ts +24 -0
  203. package/src/cli/commands/__tests__/changelog.test.ts +304 -319
  204. package/src/cli/{__tests__ → commands/__tests__}/notifications.test.ts +201 -28
  205. package/src/cli/commands/__tests__/schedules.test.ts +960 -0
  206. package/src/cli/commands/changelog.ts +106 -42
  207. package/src/cli/commands/conversations.ts +102 -17
  208. package/src/cli/commands/default-action.ts +10 -53
  209. package/src/cli/commands/notifications.ts +388 -346
  210. package/src/cli/commands/plugins.ts +252 -0
  211. package/src/cli/commands/schedules.ts +683 -0
  212. package/src/cli/commands/telemetry.ts +40 -0
  213. package/src/cli/lib/__tests__/cli-colors.test.ts +48 -0
  214. package/src/cli/lib/__tests__/confirm-prompt.test.ts +159 -0
  215. package/src/cli/lib/__tests__/install-from-github.test.ts +355 -0
  216. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +154 -0
  217. package/src/cli/lib/__tests__/search-plugins.test.ts +261 -0
  218. package/src/cli/lib/__tests__/uninstall-plugin.test.ts +124 -0
  219. package/src/cli/lib/__tests__/unknown-command.test.ts +106 -0
  220. package/src/cli/lib/cli-colors.ts +12 -0
  221. package/src/cli/lib/confirm-prompt.ts +79 -0
  222. package/src/cli/lib/install-from-github.ts +303 -0
  223. package/src/cli/lib/list-installed-plugins.ts +137 -0
  224. package/src/cli/lib/search-plugins.ts +163 -0
  225. package/src/cli/lib/uninstall-plugin.ts +82 -0
  226. package/src/cli/lib/unknown-command.ts +111 -0
  227. package/src/cli/program.ts +52 -2
  228. package/src/config/assistant-feature-flags.ts +24 -54
  229. package/src/config/bundled-skills/app-builder/SKILL.md +140 -22
  230. package/src/config/bundled-skills/app-builder/TOOLS.json +7 -0
  231. package/src/config/bundled-skills/computer-use/TOOLS.json +15 -52
  232. package/src/config/bundled-skills/document/SKILL.md +23 -3
  233. package/src/config/bundled-skills/document/TOOLS.json +53 -0
  234. package/src/config/bundled-skills/document/tools/document-delete.ts +12 -0
  235. package/src/config/bundled-skills/document/tools/document-list.ts +12 -0
  236. package/src/config/bundled-skills/document/tools/document-read.ts +12 -0
  237. package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
  238. package/src/config/bundled-skills/skill-management/SKILL.md +2 -2
  239. package/src/config/bundled-skills/skill-management/TOOLS.json +7 -7
  240. package/src/config/bundled-tool-registry.ts +6 -0
  241. package/src/config/call-site-defaults.ts +105 -0
  242. package/src/config/feature-flag-registry.json +41 -9
  243. package/src/config/llm-resolver.ts +52 -1
  244. package/src/config/loader.ts +64 -38
  245. package/src/config/schema.ts +9 -10
  246. package/src/config/schemas/__tests__/llm-request-logs.test.ts +36 -0
  247. package/src/config/schemas/__tests__/memory-v2.test.ts +3 -3
  248. package/src/config/schemas/channels.ts +17 -0
  249. package/src/config/schemas/compaction.ts +28 -0
  250. package/src/config/schemas/conversations.ts +10 -0
  251. package/src/config/schemas/heartbeat.ts +23 -0
  252. package/src/config/schemas/llm-request-logs.ts +31 -7
  253. package/src/config/schemas/llm.ts +1 -0
  254. package/src/config/schemas/memory-retrieval.ts +18 -0
  255. package/src/config/schemas/memory-retrospective.ts +1 -1
  256. package/src/config/schemas/memory-v2.ts +4 -4
  257. package/src/config/schemas/memory.ts +3 -1
  258. package/src/config/schemas/tools.ts +14 -0
  259. package/src/config/seed-inference-profiles.ts +99 -29
  260. package/src/config/skills.ts +3 -96
  261. package/src/context/compactor.ts +1107 -0
  262. package/src/context/token-estimator.ts +34 -36
  263. package/src/context/window-manager.ts +197 -1520
  264. package/src/credential-execution/managed-catalog.ts +37 -0
  265. package/src/credential-health/credential-health-service.ts +280 -19
  266. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +33 -18
  267. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +138 -0
  268. package/src/daemon/__tests__/conversation-tool-setup.test.ts +74 -0
  269. package/src/daemon/approval-generators.ts +8 -6
  270. package/src/daemon/config-watcher.ts +94 -31
  271. package/src/daemon/conversation-agent-loop-handlers.ts +78 -0
  272. package/src/daemon/conversation-agent-loop.ts +198 -11
  273. package/src/daemon/conversation-error.ts +171 -37
  274. package/src/daemon/conversation-lifecycle.ts +53 -40
  275. package/src/daemon/conversation-messaging.ts +25 -6
  276. package/src/daemon/conversation-process.ts +49 -12
  277. package/src/daemon/conversation-runtime-assembly.ts +25 -1
  278. package/src/daemon/conversation-slash.ts +12 -5
  279. package/src/daemon/conversation-store.ts +11 -4
  280. package/src/daemon/conversation-tool-setup.ts +39 -7
  281. package/src/daemon/conversation.ts +33 -8
  282. package/src/daemon/date-context.ts +40 -0
  283. package/src/daemon/external-plugins-bootstrap.ts +217 -181
  284. package/src/daemon/first-greeting.ts +22 -2
  285. package/src/daemon/guardian-action-generators.ts +1 -125
  286. package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +248 -0
  287. package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +154 -0
  288. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +133 -0
  289. package/src/daemon/handlers/__tests__/config-a2a.test.ts +95 -0
  290. package/src/daemon/handlers/config-a2a.ts +289 -0
  291. package/src/daemon/handlers/config-model.ts +6 -5
  292. package/src/daemon/handlers/config-slack-channel.ts +15 -3
  293. package/src/daemon/handlers/conversations.ts +1 -0
  294. package/src/daemon/handlers/shared.ts +14 -5
  295. package/src/daemon/handlers/skills.ts +111 -108
  296. package/src/daemon/history-repair.ts +28 -1
  297. package/src/daemon/host-app-control-proxy.ts +153 -27
  298. package/src/daemon/host-proxy-preactivation.ts +85 -18
  299. package/src/daemon/lifecycle.ts +89 -91
  300. package/src/daemon/meet-host-supervisor.ts +5 -4
  301. package/src/daemon/memory-v2-startup.ts +85 -0
  302. package/src/daemon/message-protocol.ts +1 -0
  303. package/src/daemon/message-types/conversations.ts +25 -0
  304. package/src/daemon/message-types/messages.ts +61 -0
  305. package/src/daemon/message-types/notifications.ts +21 -0
  306. package/src/daemon/message-types/subagents.ts +1 -0
  307. package/src/daemon/message-types/sync.ts +1 -0
  308. package/src/daemon/pkb-reminder-builder.test.ts +11 -54
  309. package/src/daemon/pkb-reminder-builder.ts +5 -20
  310. package/src/daemon/plugin-source-watcher.ts +146 -0
  311. package/src/daemon/process-message.ts +24 -3
  312. package/src/daemon/server.ts +11 -2
  313. package/src/daemon/skill-memory-refresh.ts +33 -0
  314. package/src/daemon/wake-target-adapter.ts +2 -0
  315. package/src/documents/document-store.ts +221 -3
  316. package/src/embedded/plugin-api.ts +40 -0
  317. package/src/export/__tests__/transcript-formatter.test.ts +121 -0
  318. package/src/export/transcript-formatter.ts +54 -20
  319. package/src/filing/filing-service.ts +39 -0
  320. package/src/heartbeat/__tests__/heartbeat-service.test.ts +135 -6
  321. package/src/heartbeat/heartbeat-run-store.ts +2 -1
  322. package/src/heartbeat/heartbeat-service.ts +73 -189
  323. package/src/home/__tests__/feed-types.test.ts +80 -0
  324. package/src/home/feed-types.ts +36 -2
  325. package/src/home/post-connect-feed.ts +1 -0
  326. package/src/index.ts +18 -1
  327. package/src/ipc/cli-client.ts +147 -45
  328. package/src/live-voice/__tests__/live-voice-stt.test.ts +57 -0
  329. package/src/mcp/client.ts +20 -4
  330. package/src/media/image-credentials.ts +3 -3
  331. package/src/memory/__tests__/bookmark-crud.test.ts +33 -27
  332. package/src/memory/__tests__/conversation-queries.test.ts +483 -0
  333. package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +113 -0
  334. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +2 -50
  335. package/src/memory/__tests__/memory-retrospective-job.test.ts +87 -4
  336. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +119 -14
  337. package/src/memory/__tests__/message-content.test.ts +35 -0
  338. package/src/memory/bookmark-crud.ts +42 -10
  339. package/src/memory/context-search/sources/conversations.ts +62 -2
  340. package/src/memory/context-search/sources/workspace.ts +4 -0
  341. package/src/memory/conversation-crud.ts +63 -19
  342. package/src/memory/conversation-queries.ts +197 -11
  343. package/src/memory/conversation-title-service.ts +26 -4
  344. package/src/memory/db-init.ts +12 -0
  345. package/src/memory/delivery-crud.ts +152 -5
  346. package/src/memory/embedding-backend.ts +4 -4
  347. package/src/memory/external-conversation-store.ts +66 -5
  348. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +150 -12
  349. package/src/memory/graph/conversation-graph-memory.ts +49 -21
  350. package/src/memory/graph/tools.ts +9 -40
  351. package/src/memory/indexer.ts +34 -29
  352. package/src/memory/invite-store.ts +53 -0
  353. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +73 -0
  354. package/src/memory/jobs/embed-concept-page.ts +20 -11
  355. package/src/memory/jobs-worker.ts +6 -1
  356. package/src/memory/llm-request-log-source-clickhouse.ts +24 -12
  357. package/src/memory/llm-request-log-source.ts +19 -52
  358. package/src/memory/llm-request-log-store.ts +92 -1
  359. package/src/memory/llm-usage-store.ts +125 -5
  360. package/src/memory/memory-retrospective-enqueue.ts +1 -20
  361. package/src/memory/memory-retrospective-job.ts +33 -6
  362. package/src/memory/memory-retrospective-startup-cleanup.ts +72 -5
  363. package/src/memory/message-content.ts +1 -1
  364. package/src/memory/migrations/109-external-conversation-bindings.ts +15 -4
  365. package/src/memory/migrations/229-delete-private-conversations.test.ts +38 -1
  366. package/src/memory/migrations/229-delete-private-conversations.ts +7 -0
  367. package/src/memory/migrations/247-external-conversation-binding-thread-id.ts +78 -0
  368. package/src/memory/migrations/248-create-onboarding-events.ts +21 -0
  369. package/src/memory/migrations/249-normalize-slack-external-content.ts +240 -0
  370. package/src/memory/migrations/250-provider-connection-base-url-and-models.ts +28 -0
  371. package/src/memory/migrations/251-a2a-tasks.ts +49 -0
  372. package/src/memory/migrations/252-llm-request-log-agent-loop-exit-reason.ts +32 -0
  373. package/src/memory/migrations/index.ts +9 -0
  374. package/src/memory/migrations/registry.ts +16 -0
  375. package/src/memory/onboarding-events-store.ts +106 -0
  376. package/src/memory/schema/a2a.ts +15 -0
  377. package/src/memory/schema/bookmarks.ts +0 -2
  378. package/src/memory/schema/calls.ts +1 -0
  379. package/src/memory/schema/index.ts +1 -0
  380. package/src/memory/schema/inference.ts +3 -3
  381. package/src/memory/schema/infrastructure.ts +13 -0
  382. package/src/memory/turn-events-store.ts +127 -2
  383. package/src/memory/v2/__tests__/activation-store.test.ts +25 -23
  384. package/src/memory/v2/__tests__/activation.test.ts +0 -8
  385. package/src/memory/v2/__tests__/cli-command-store.test.ts +404 -0
  386. package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +25 -4
  387. package/src/memory/v2/__tests__/injection.test.ts +288 -11
  388. package/src/memory/v2/__tests__/migration.test.ts +87 -0
  389. package/src/memory/v2/__tests__/page-index.test.ts +83 -0
  390. package/src/memory/v2/__tests__/prompts-router.test.ts +58 -6
  391. package/src/memory/v2/__tests__/qdrant.test.ts +66 -3
  392. package/src/memory/v2/__tests__/router.test.ts +15 -0
  393. package/src/memory/v2/__tests__/skill-store.test.ts +387 -8
  394. package/src/memory/v2/__tests__/static-context.test.ts +12 -1
  395. package/src/memory/v2/activation-store.ts +14 -16
  396. package/src/memory/v2/cli-command-content.ts +19 -0
  397. package/src/memory/v2/cli-command-store.ts +304 -0
  398. package/src/memory/v2/frontmatter-sweep.ts +7 -1
  399. package/src/memory/v2/injection.ts +81 -26
  400. package/src/memory/v2/migration.ts +49 -19
  401. package/src/memory/v2/page-index.ts +63 -8
  402. package/src/memory/v2/prompts/router.ts +11 -8
  403. package/src/memory/v2/prompts/sweep.ts +2 -2
  404. package/src/memory/v2/qdrant.ts +135 -7
  405. package/src/memory/v2/router.ts +9 -8
  406. package/src/memory/v2/skill-store.ts +120 -35
  407. package/src/memory/v2/static-context.ts +4 -4
  408. package/src/memory/v2/types.ts +23 -0
  409. package/src/messaging/providers/a2a/__tests__/deliver.test.ts +274 -0
  410. package/src/messaging/providers/a2a/deliver.ts +156 -0
  411. package/src/messaging/providers/gmail/client.ts +9 -2
  412. package/src/messaging/providers/index.ts +11 -2
  413. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +45 -5
  414. package/src/messaging/providers/slack/__tests__/download.test.ts +231 -0
  415. package/src/messaging/providers/slack/adapter.ts +43 -5
  416. package/src/messaging/providers/slack/client.ts +27 -0
  417. package/src/messaging/providers/slack/deep-link.ts +65 -0
  418. package/src/messaging/providers/slack/download.ts +104 -0
  419. package/src/messaging/providers/slack/message-metadata.test.ts +32 -0
  420. package/src/messaging/providers/slack/message-metadata.ts +27 -0
  421. package/src/messaging/providers/slack/render-transcript.test.ts +134 -0
  422. package/src/messaging/providers/slack/render-transcript.ts +69 -5
  423. package/src/messaging/providers/slack/types.ts +20 -1
  424. package/src/notifications/__tests__/broadcaster.test.ts +203 -0
  425. package/src/notifications/__tests__/decision-engine.test.ts +283 -0
  426. package/src/notifications/__tests__/deterministic-checks.test.ts +286 -0
  427. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +1 -0
  428. package/src/notifications/__tests__/home-feed-side-effect.test.ts +430 -7
  429. package/src/notifications/adapters/macos.ts +12 -2
  430. package/src/notifications/broadcaster.ts +29 -4
  431. package/src/notifications/conversation-pairing.ts +2 -1
  432. package/src/notifications/copy-composer.ts +17 -64
  433. package/src/notifications/decision-engine.ts +113 -45
  434. package/src/notifications/deterministic-checks.ts +96 -0
  435. package/src/notifications/emit-signal.ts +21 -1
  436. package/src/notifications/home-feed-side-effect.ts +138 -5
  437. package/src/notifications/signal.ts +3 -5
  438. package/src/notifications/types.ts +8 -0
  439. package/src/oauth/connection-resolver.ts +8 -4
  440. package/src/oauth/platform-connection.test.ts +43 -3
  441. package/src/oauth/platform-connection.ts +19 -6
  442. package/src/oauth/seed-providers.ts +10 -1
  443. package/src/permissions/checker.ts +2 -0
  444. package/src/permissions/ipc-risk-types.ts +1 -0
  445. package/src/permissions/question-prompter.test.ts +416 -0
  446. package/src/permissions/question-prompter.ts +294 -0
  447. package/src/platform/client.test.ts +1 -1
  448. package/src/platform/client.ts +1 -1
  449. package/src/plugin-api/constants.ts +26 -0
  450. package/src/plugin-api/index.ts +34 -1
  451. package/src/plugin-api/types.ts +104 -22
  452. package/src/plugins/defaults/circuit-breaker.ts +0 -5
  453. package/src/plugins/defaults/compaction.ts +0 -4
  454. package/src/plugins/defaults/empty-response.ts +0 -2
  455. package/src/plugins/defaults/history-repair.ts +0 -2
  456. package/src/plugins/defaults/injectors.ts +74 -22
  457. package/src/plugins/defaults/llm-call.ts +0 -2
  458. package/src/plugins/defaults/memory-retrieval.ts +0 -1
  459. package/src/plugins/defaults/overflow-reduce.ts +0 -1
  460. package/src/plugins/defaults/persistence.ts +0 -2
  461. package/src/plugins/defaults/title-generate.ts +0 -5
  462. package/src/plugins/defaults/token-estimate.ts +0 -2
  463. package/src/plugins/defaults/tool-error.ts +0 -7
  464. package/src/plugins/defaults/tool-execute.ts +0 -2
  465. package/src/plugins/defaults/tool-result-truncate.ts +0 -4
  466. package/src/plugins/ensure-plugin-api-shim.ts +96 -0
  467. package/src/plugins/external-api.ts +104 -0
  468. package/src/plugins/external-plugin-loader.ts +187 -42
  469. package/src/plugins/feature-gate.ts +22 -0
  470. package/src/plugins/pipeline.ts +37 -0
  471. package/src/plugins/registry.ts +48 -80
  472. package/src/plugins/types.ts +40 -26
  473. package/src/plugins/user-loader.ts +21 -2
  474. package/src/proactive-artifact/aux-message-injector.ts +11 -0
  475. package/src/proactive-artifact/job.test.ts +37 -5
  476. package/src/prompts/__tests__/system-prompt.test.ts +10 -43
  477. package/src/prompts/__tests__/task-progress-hint-section.test.ts +95 -0
  478. package/src/prompts/normalize-onboarding.ts +27 -0
  479. package/src/prompts/sections.ts +302 -0
  480. package/src/prompts/system-prompt.ts +63 -174
  481. package/src/prompts/templates/BOOTSTRAP.md +17 -1
  482. package/src/prompts/templates/system-sections.ts +164 -0
  483. package/src/providers/__tests__/inference.test.ts +24 -7
  484. package/src/providers/anthropic/client.ts +28 -28
  485. package/src/providers/call-site-routing.ts +24 -6
  486. package/src/providers/connection-resolution.ts +68 -11
  487. package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +74 -0
  488. package/src/providers/inference/__tests__/connections-openai-compatible.test.ts +175 -0
  489. package/src/providers/inference/__tests__/connections-status-label.test.ts +15 -0
  490. package/src/providers/inference/adapter-factory.ts +32 -6
  491. package/src/providers/inference/auth.ts +12 -0
  492. package/src/providers/inference/backfill.ts +14 -1
  493. package/src/providers/inference/connections.ts +159 -34
  494. package/src/providers/inference/resolve-auth.ts +14 -4
  495. package/src/providers/model-catalog.ts +249 -12
  496. package/src/providers/model-intents.ts +3 -3
  497. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +235 -0
  498. package/src/providers/openai/chat-completions-provider.ts +169 -8
  499. package/src/providers/openrouter/client.ts +49 -4
  500. package/src/providers/{managed-proxy → platform-proxy}/constants.ts +4 -2
  501. package/src/providers/{managed-proxy → platform-proxy}/context.ts +3 -3
  502. package/src/providers/provider-availability.ts +17 -2
  503. package/src/providers/provider-catalog-visibility.ts +38 -0
  504. package/src/providers/provider-send-message.ts +27 -12
  505. package/src/providers/registry.ts +52 -15
  506. package/src/providers/retry.ts +47 -1
  507. package/src/runtime/__tests__/agent-wake.test.ts +152 -0
  508. package/src/runtime/agent-wake.ts +103 -15
  509. package/src/runtime/auth/route-policy.ts +21 -1
  510. package/src/runtime/btw-sidechain.ts +2 -0
  511. package/src/runtime/http-server.ts +7 -16
  512. package/src/runtime/http-types.ts +19 -47
  513. package/src/runtime/migrations/origin-mode.ts +1 -1
  514. package/src/runtime/pending-interactions.ts +1 -0
  515. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +17 -0
  516. package/src/runtime/routes/__tests__/consolidation-routes.test.ts +258 -0
  517. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +5 -1
  518. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +172 -23
  519. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +275 -44
  520. package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +12 -0
  521. package/src/runtime/routes/__tests__/question-routes.test.ts +395 -0
  522. package/src/runtime/routes/__tests__/tts-routes.test.ts +64 -1
  523. package/src/runtime/routes/acp-routes-list.test.ts +143 -0
  524. package/src/runtime/routes/acp-routes.ts +5 -3
  525. package/src/runtime/routes/auth-routes.ts +1 -1
  526. package/src/runtime/routes/bookmark-routes.ts +5 -3
  527. package/src/runtime/routes/btw-routes.ts +5 -1
  528. package/src/runtime/routes/channel-availability-routes.ts +126 -0
  529. package/src/runtime/routes/consolidation-routes.ts +100 -0
  530. package/src/runtime/routes/conversation-cli-routes.ts +44 -3
  531. package/src/runtime/routes/conversation-list-routes.ts +3 -20
  532. package/src/runtime/routes/conversation-management-routes.ts +17 -42
  533. package/src/runtime/routes/conversation-query-routes.ts +99 -35
  534. package/src/runtime/routes/conversation-routes.ts +97 -11
  535. package/src/runtime/routes/documents-routes.ts +25 -86
  536. package/src/runtime/routes/group-routes.ts +5 -0
  537. package/src/runtime/routes/inbound-conversation.ts +28 -8
  538. package/src/runtime/routes/inbound-message-handler.ts +236 -41
  539. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +111 -0
  540. package/src/runtime/routes/inbound-stages/background-dispatch.ts +32 -1
  541. package/src/runtime/routes/inbound-stages/edit-intercept.ts +17 -4
  542. package/src/runtime/routes/index.ts +8 -0
  543. package/src/runtime/routes/inference-profile-session-handler.ts +17 -44
  544. package/src/runtime/routes/inference-profile-session-reaper.ts +7 -21
  545. package/src/runtime/routes/inference-provider-connection-routes.ts +199 -22
  546. package/src/runtime/routes/integrations/a2a.ts +235 -0
  547. package/src/runtime/routes/integrations/slack/share.ts +4 -52
  548. package/src/runtime/routes/integrations/slack/token.ts +43 -0
  549. package/src/runtime/routes/integrations/twilio.ts +6 -13
  550. package/src/runtime/routes/llm-call-sites-routes.ts +11 -1
  551. package/src/runtime/routes/notification-routes.ts +1 -1
  552. package/src/runtime/routes/oauth-commands-routes.ts +105 -15
  553. package/src/runtime/routes/oauth-lifecycle-routes.ts +43 -0
  554. package/src/runtime/routes/question-routes.ts +259 -0
  555. package/src/runtime/routes/rename-conversation-routes.ts +2 -33
  556. package/src/runtime/routes/schedule-routes.ts +4 -7
  557. package/src/runtime/routes/subagents-routes.ts +98 -18
  558. package/src/runtime/routes/telemetry-routes.ts +27 -0
  559. package/src/runtime/routes/tts-routes.ts +27 -2
  560. package/src/runtime/routes/workspace-routes.test.ts +43 -0
  561. package/src/runtime/routes/workspace-routes.ts +28 -0
  562. package/src/runtime/services/conversation-serializer.ts +39 -7
  563. package/src/runtime/sync/resource-sync-events.ts +93 -1
  564. package/src/schedule/schedule-store.ts +27 -2
  565. package/src/schedule/scheduler.ts +9 -1
  566. package/src/security/__tests__/untrusted-content.test.ts +86 -0
  567. package/src/security/untrusted-content.ts +93 -8
  568. package/src/skills/catalog-files.ts +1 -1
  569. package/src/skills/catalog-install.ts +233 -116
  570. package/src/skills/clawhub.ts +70 -13
  571. package/src/skills/managed-store.ts +4 -119
  572. package/src/skills/skillssh-registry.ts +27 -48
  573. package/src/subagent/manager.ts +17 -7
  574. package/src/telemetry/types.ts +113 -1
  575. package/src/telemetry/usage-telemetry-reporter.test.ts +312 -5
  576. package/src/telemetry/usage-telemetry-reporter.ts +113 -7
  577. package/src/tools/apps/executors.ts +58 -7
  578. package/src/tools/ask-question/ask-question-tool.test.ts +509 -0
  579. package/src/tools/ask-question/ask-question-tool.ts +304 -0
  580. package/src/tools/browser/browser-execution.ts +15 -11
  581. package/src/tools/computer-use/definitions.ts +3 -3
  582. package/src/tools/credentials/vault.ts +1 -1
  583. package/src/tools/document/document-tool.ts +124 -1
  584. package/src/tools/filesystem/edit.ts +1 -1
  585. package/src/tools/filesystem/list.ts +1 -1
  586. package/src/tools/filesystem/read.ts +1 -1
  587. package/src/tools/filesystem/write.ts +5 -2
  588. package/src/tools/host-filesystem/transfer.ts +1 -1
  589. package/src/tools/host-terminal/host-shell.ts +1 -1
  590. package/src/tools/memory/register.ts +1 -9
  591. package/src/tools/permission-checker.ts +1 -1
  592. package/src/tools/registry.ts +17 -7
  593. package/src/tools/schedule/create.ts +2 -2
  594. package/src/tools/schema-transforms.ts +7 -2
  595. package/src/tools/side-effects.ts +1 -0
  596. package/src/tools/skills/delete-managed.ts +4 -4
  597. package/src/tools/skills/execute.ts +1 -1
  598. package/src/tools/skills/scaffold-managed.ts +3 -2
  599. package/src/tools/subagent/notify-parent.ts +1 -1
  600. package/src/tools/system/request-permission.ts +2 -2
  601. package/src/tools/terminal/safe-env.ts +60 -1
  602. package/src/tools/tool-manifest.ts +2 -0
  603. package/src/tools/types.ts +107 -21
  604. package/src/tools/ui-surface/definitions.ts +6 -5
  605. package/src/tts/__tests__/provider-adapters.test.ts +76 -2
  606. package/src/tts/providers/elevenlabs-provider.ts +75 -1
  607. package/src/types/onboarding-context.ts +2 -0
  608. package/src/util/errors.ts +17 -0
  609. package/src/util/platform.ts +10 -0
  610. package/src/watcher/__tests__/engine.test.ts +22 -0
  611. package/src/watcher/engine.ts +6 -2
  612. package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +80 -15
  613. package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +35 -22
  614. package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +3 -1
  615. package/src/workspace/migrations/083-system-prompt-prefix-to-file.ts +191 -0
  616. package/src/workspace/migrations/084-remove-legacy-skills-index.ts +276 -0
  617. package/src/workspace/migrations/085-memory-v2-bm25-b-reembed-disabled-v2-pages.ts +137 -0
  618. package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +198 -0
  619. package/src/workspace/migrations/087-memory-router-balanced-profile.ts +91 -0
  620. package/src/workspace/migrations/registry.ts +10 -0
  621. package/src/workspace/migrations/runner.ts +39 -9
  622. package/src/workspace/migrations/types.ts +4 -0
  623. package/examples/plugins/echo/bun.lock +0 -25
  624. package/src/__tests__/context-window-manager.test.ts +0 -2481
  625. package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -441
  626. package/src/context/__tests__/compact-prompt.test.ts +0 -63
  627. package/src/context/prompts/compact.md +0 -26
  628. package/src/memory/graph/__tests__/remember-description.test.ts +0 -55
  629. package/src/prompts/__tests__/build-cli-reference-section.test.ts +0 -37
  630. package/src/runtime/guardian-action-conversation-turn.ts +0 -99
@@ -16,6 +16,35 @@ export interface ClassifiedConversationError {
16
16
  debugDetails?: string;
17
17
  /** Machine-readable error category for log report metadata and triage. */
18
18
  errorCategory: string;
19
+ /**
20
+ * Name of the `provider_connections` row in play when the error
21
+ * occurred. Forwarded to the wire `ConversationErrorMessage` so chat
22
+ * banners can point users at the specific slot to fix. Only set by
23
+ * classifiers / callers that have the resolved connection in scope —
24
+ * generic regex fallbacks leave it undefined.
25
+ */
26
+ connectionName?: string;
27
+ /**
28
+ * Name of the resolved profile (`llm.activeProfile` / per-call override)
29
+ * in play when the error occurred. Forwarded to the wire message so
30
+ * banners can name the profile when the connection identifier is
31
+ * generic (e.g. the canonical managed connection). Optional for the
32
+ * same reason as `connectionName`.
33
+ */
34
+ profileName?: string;
35
+ }
36
+
37
+ /**
38
+ * Optional resolved-config context that callers can attach to error
39
+ * classification so the resulting `ConversationErrorMessage` can name the
40
+ * exact connection / profile in play. Used in particular by the chat
41
+ * dispatch sites to make `PROVIDER_INVALID_KEY` and `PROVIDER_NOT_CONFIGURED`
42
+ * actionable (the macOS banner reads these to render "Invalid API key for
43
+ * profile X" instead of a generic "API key required").
44
+ */
45
+ export interface ConversationErrorAttribution {
46
+ connectionName?: string;
47
+ profileName?: string;
19
48
  }
20
49
 
21
50
  // Network-level error patterns (connection refused, timeout, DNS, reset)
@@ -137,6 +166,20 @@ export interface ErrorContext {
137
166
  phase: "agent_loop" | "regenerate" | "handler" | "persist";
138
167
  /** Whether the abort signal was active when the error occurred. */
139
168
  aborted?: boolean;
169
+ /**
170
+ * Optional name of the `provider_connections` row in play. Plumbed by
171
+ * dispatch sites that know the resolved connection (chat agent loop)
172
+ * so credential-related classifications (`PROVIDER_INVALID_KEY`,
173
+ * `PROVIDER_NOT_CONFIGURED`) can name the exact slot to fix.
174
+ */
175
+ connectionName?: string;
176
+ /**
177
+ * Optional name of the resolved profile in play. Plumbed alongside
178
+ * `connectionName` by dispatch sites; surfaces in the wire message so
179
+ * the macOS chat banner can reference the profile even when the
180
+ * underlying connection name is generic.
181
+ */
182
+ profileName?: string;
140
183
  }
141
184
 
142
185
  /**
@@ -195,32 +238,43 @@ export function classifyConversationError(
195
238
  (error instanceof Error ? error.stack : undefined) ?? message;
196
239
  const debugDetails = truncateDebugDetails(rawDetails);
197
240
 
241
+ // Extract optional attribution (connection / profile names) so
242
+ // credential-related classifications can name the exact slot to fix.
243
+ // A `ProviderNotConfiguredError` instance carries its own attribution
244
+ // (from the throw site) which takes priority over context when present.
245
+ const attribution: ConversationErrorAttribution = {
246
+ ...(ctx.connectionName ? { connectionName: ctx.connectionName } : {}),
247
+ ...(ctx.profileName ? { profileName: ctx.profileName } : {}),
248
+ };
249
+
198
250
  // Dedicated classification for missing provider API key
199
251
  if (error instanceof ProviderNotConfiguredError) {
200
252
  return {
201
- code: "PROVIDER_NOT_CONFIGURED",
202
- userMessage:
203
- "No API key configured for inference. Add one in Settings to start chatting.",
204
- retryable: true,
205
- errorCategory: "provider_not_configured",
253
+ ...providerNotConfiguredClassification({
254
+ connectionName:
255
+ error.connectionName ?? attribution.connectionName,
256
+ profileName: error.profileName ?? attribution.profileName,
257
+ }),
206
258
  debugDetails,
207
259
  };
208
260
  }
209
261
 
210
262
  // Phase-specific overrides
211
263
  if (ctx.phase === "regenerate") {
212
- const base = classifyCore(error, message);
264
+ const base = classifyCore(error, message, attribution);
213
265
  return {
214
266
  code: "REGENERATE_FAILED",
215
267
  userMessage: `Could not regenerate the response. ${base.userMessage}`,
216
268
  retryable: true,
217
269
  debugDetails,
218
270
  errorCategory: `regenerate:${base.errorCategory}`,
271
+ ...(base.connectionName ? { connectionName: base.connectionName } : {}),
272
+ ...(base.profileName ? { profileName: base.profileName } : {}),
219
273
  };
220
274
  }
221
275
 
222
276
  // Classify using statusCode (if ProviderError) then regex fallback
223
- const classified = classifyCore(error, message);
277
+ const classified = classifyCore(error, message, attribution);
224
278
  return {
225
279
  ...classified,
226
280
  debugDetails,
@@ -230,10 +284,16 @@ export function classifyConversationError(
230
284
  /**
231
285
  * Core classification: check ProviderError.statusCode first for
232
286
  * deterministic classification, then fall back to regex patterns.
287
+ *
288
+ * `attribution` carries the resolved connection / profile names (when the
289
+ * caller knows them) so credential-related results can name the exact
290
+ * slot for the user to fix. Pass an empty object when no attribution is
291
+ * available — the classifier falls back to a generic message.
233
292
  */
234
293
  function classifyCore(
235
294
  error: unknown,
236
295
  message: string,
296
+ attribution: ConversationErrorAttribution = {},
237
297
  ): Omit<ClassifiedConversationError, "debugDetails"> {
238
298
  // ProviderError with statusCode — deterministic classification
239
299
  if (error instanceof ProviderError && error.statusCode !== undefined) {
@@ -247,39 +307,33 @@ function classifyCore(
247
307
  };
248
308
  }
249
309
  if (error.statusCode === 401 || error.statusCode === 403) {
250
- if (
251
- /invalid.*api.?key|invalid.*x-api-key|authentication.?error/i.test(
252
- message,
253
- )
254
- ) {
255
- // Check if this provider is routed through the managed proxy.
256
- // If so, the assistant API key is stalethe client should reprovision.
257
- const providerName = error.provider;
258
- if (getProviderRoutingSource(providerName) === "managed-proxy") {
259
- return {
260
- code: "MANAGED_KEY_INVALID",
261
- userMessage:
262
- "The assistant API key is invalid. Attempting to re-provision…",
263
- retryable: true,
264
- errorCategory: "managed_key_invalid",
265
- };
266
- }
310
+ // Both managed-proxy and user-key 401/403s reach this branch.
311
+ // Managed-proxy routes through the assistant API key (stale → re-
312
+ // provision) and emits `MANAGED_KEY_INVALID`; everything else is a
313
+ // user-set credential that the upstream provider rejected → emit
314
+ // `PROVIDER_INVALID_KEY` so the macOS chat banner renders an
315
+ // "Invalid API key" surface (distinct from "API key required"
316
+ // which only fires when the key is genuinely missing see
317
+ // `providerNotConfiguredClassification`).
318
+ const providerName = error.provider;
319
+ if (getProviderRoutingSource(providerName) === "managed-proxy") {
267
320
  return {
268
- code: "PROVIDER_NOT_CONFIGURED",
321
+ code: "MANAGED_KEY_INVALID",
269
322
  userMessage:
270
- "Your API key is invalid or expired. Update it in Settings or switch to managed mode.",
271
- retryable: false,
272
- errorCategory: "provider_not_configured",
323
+ "The assistant API key is invalid. Attempting to re-provision…",
324
+ retryable: true,
325
+ errorCategory: "managed_key_invalid",
273
326
  };
274
327
  }
328
+ return invalidApiKeyClassification(attribution);
275
329
  }
276
330
  if (error.statusCode === 401) {
277
331
  return {
278
- code: "PROVIDER_NOT_CONFIGURED",
332
+ code: "PROVIDER_INVALID_KEY",
279
333
  userMessage:
280
334
  "Your API key is invalid or expired. Update it in Settings or switch to managed mode.",
281
335
  retryable: false,
282
- errorCategory: "provider_not_configured",
336
+ errorCategory: "provider_invalid_key",
283
337
  };
284
338
  }
285
339
  if (error.statusCode === 402) {
@@ -369,13 +423,10 @@ function classifyCore(
369
423
  message,
370
424
  )
371
425
  ) {
372
- return {
373
- code: "PROVIDER_NOT_CONFIGURED",
374
- userMessage:
375
- "Your API key is invalid. Update it in Settings or switch to managed mode.",
376
- retryable: false,
377
- errorCategory: "provider_not_configured",
378
- };
426
+ // Mirror the 401/403 branch: a credential-shaped 4xx is an
427
+ // "invalid key" surface (banner: "Invalid API key"), distinct
428
+ // from "no key configured" (banner: "API key required").
429
+ return invalidApiKeyClassification(attribution);
379
430
  }
380
431
  if (isImageDimensionsTooLarge(message)) {
381
432
  return {
@@ -477,6 +528,80 @@ function providerBillingClassification(): Omit<
477
528
  };
478
529
  }
479
530
 
531
+ /**
532
+ * Build a user-facing message that names the exact profile / connection
533
+ * to fix when one is known, falling back to a generic phrase otherwise.
534
+ * Profile is preferred because that's the entity the user picks in the
535
+ * chat picker; connection is shown when no profile is in play (e.g.
536
+ * `llm.default` direct dispatch) or as a parenthetical when both differ.
537
+ */
538
+ function describeAttribution(
539
+ attribution: ConversationErrorAttribution | undefined,
540
+ ): string {
541
+ if (!attribution) return "";
542
+ const { profileName, connectionName } = attribution;
543
+ if (profileName && connectionName && profileName !== connectionName) {
544
+ return ` for profile "${profileName}" (connection "${connectionName}")`;
545
+ }
546
+ if (profileName) return ` for profile "${profileName}"`;
547
+ if (connectionName) return ` for connection "${connectionName}"`;
548
+ return "";
549
+ }
550
+
551
+ /**
552
+ * Classification for an invalid (rejected by the upstream provider, e.g.
553
+ * Anthropic 401/403) API key. Distinct from `PROVIDER_NOT_CONFIGURED`
554
+ * (which is for a key that was never set) so the macOS chat banner can
555
+ * render "Invalid API key" rather than "API key required" — they have
556
+ * different recovery actions (update vs. add).
557
+ */
558
+ function invalidApiKeyClassification(
559
+ attribution?: ConversationErrorAttribution,
560
+ ): Omit<ClassifiedConversationError, "debugDetails"> {
561
+ const target = describeAttribution(attribution);
562
+ return {
563
+ code: "PROVIDER_INVALID_KEY",
564
+ userMessage: target
565
+ ? `The API key${target} was rejected by the provider. Update it in Settings.`
566
+ : "Your API key was rejected by the provider. Update it in Settings.",
567
+ retryable: false,
568
+ errorCategory: "provider_invalid_key",
569
+ ...(attribution?.connectionName
570
+ ? { connectionName: attribution.connectionName }
571
+ : {}),
572
+ ...(attribution?.profileName
573
+ ? { profileName: attribution.profileName }
574
+ : {}),
575
+ };
576
+ }
577
+
578
+ /**
579
+ * Classification for a genuinely-missing provider credential — vault has
580
+ * no entry for the resolved connection's `auth.credential`, or no
581
+ * provider is registered at all. The macOS chat banner renders this as
582
+ * "API key required" with an "Open Settings" CTA. Distinct from
583
+ * `PROVIDER_INVALID_KEY` (where a key exists but was rejected).
584
+ */
585
+ function providerNotConfiguredClassification(
586
+ attribution?: ConversationErrorAttribution,
587
+ ): Omit<ClassifiedConversationError, "debugDetails"> {
588
+ const target = describeAttribution(attribution);
589
+ return {
590
+ code: "PROVIDER_NOT_CONFIGURED",
591
+ userMessage: target
592
+ ? `No API key configured${target}. Add one in Settings to start chatting.`
593
+ : "No API key configured for inference. Add one in Settings to start chatting.",
594
+ retryable: true,
595
+ errorCategory: "provider_not_configured",
596
+ ...(attribution?.connectionName
597
+ ? { connectionName: attribution.connectionName }
598
+ : {}),
599
+ ...(attribution?.profileName
600
+ ? { profileName: attribution.profileName }
601
+ : {}),
602
+ };
603
+ }
604
+
480
605
  function classifyByMessage(
481
606
  error: unknown,
482
607
  message: string,
@@ -650,5 +775,14 @@ export function buildConversationErrorMessage(
650
775
  retryable: classified.retryable,
651
776
  debugDetails: classified.debugDetails,
652
777
  errorCategory: classified.errorCategory,
778
+ // Optional attribution — only forwarded when the classifier was able
779
+ // to pin the failure to a specific provider connection / profile.
780
+ // The macOS chat banner reads these to render targeted CTAs.
781
+ ...(classified.connectionName
782
+ ? { connectionName: classified.connectionName }
783
+ : {}),
784
+ ...(classified.profileName
785
+ ? { profileName: classified.profileName }
786
+ : {}),
653
787
  };
654
788
  }
@@ -18,6 +18,7 @@ import {
18
18
  } from "../memory/conversation-crud.js";
19
19
  import { enqueueMemoryJob } from "../memory/jobs-store.js";
20
20
  import { enqueueMemoryRetrospectiveIfEnabled } from "../memory/memory-retrospective-enqueue.js";
21
+ import { shouldExposePersonalMemory } from "../memory/v2/static-context.js";
21
22
  import type { PermissionPrompter } from "../permissions/prompter.js";
22
23
  import type { SecretPrompter } from "../permissions/secret-prompter.js";
23
24
  import type { ContentBlock, Message } from "../providers/types.js";
@@ -31,12 +32,14 @@ import { getLogger } from "../util/logger.js";
31
32
  import { unregisterCallNotifiers } from "./conversation-notifiers.js";
32
33
  import type { MessageQueue } from "./conversation-queue-manager.js";
33
34
  import { resetSkillToolProjection } from "./conversation-skill-tools.js";
35
+ import { resolveTrustClass } from "./conversation-tool-setup.js";
34
36
  import { repairHistory } from "./history-repair.js";
35
37
  import type {
36
38
  SurfaceData,
37
39
  SurfaceType,
38
40
  UsageStats,
39
41
  } from "./message-protocol.js";
42
+ import type { TrustContext } from "./trust-context.js";
40
43
 
41
44
  const log = getLogger("conversation-lifecycle");
42
45
 
@@ -113,8 +116,9 @@ export interface LoadFromDbContext {
113
116
  usageStats: UsageStats;
114
117
  contextCompactedMessageCount: number;
115
118
  contextCompactedAt: number | null;
116
- trustContext?: { trustClass: TrustClass };
119
+ trustContext?: TrustContext;
117
120
  loadedHistoryTrustClass?: TrustClass;
121
+ loadedHistoryPersonalMemoryAllowed?: boolean;
118
122
  }
119
123
 
120
124
  export interface AbortContext {
@@ -184,7 +188,16 @@ export async function loadFromDb(ctx: LoadFromDbContext): Promise<void> {
184
188
  ctx.contextCompactedAt = conv?.contextCompactedAt ?? null;
185
189
  }
186
190
 
187
- const personalMemoryAllowed = !isUntrustedTrustClass(trustClass);
191
+ // Mirror the injection-time gate (`shouldExposePersonalMemory` in
192
+ // `conversation-agent-loop.ts`) so background/local conversations
193
+ // (sourceChannel `undefined` or `"vellum"`) can rehydrate the persisted
194
+ // v2 static memory block. Use `resolveTrustClass` for parity with the
195
+ // agent loop — it folds in the HTTP-auth-disabled dev bypass so
196
+ // rehydration and injection agree on the effective trust class.
197
+ const personalMemoryAllowed = shouldExposePersonalMemory({
198
+ sourceChannel: ctx.trustContext?.sourceChannel,
199
+ isTrustedActor: resolveTrustClass(ctx.trustContext) === "guardian",
200
+ });
188
201
  const parsedMessages: Message[] = dbMessages
189
202
  .slice(ctx.contextCompactedMessageCount)
190
203
  .map((m, index, arr) => {
@@ -212,21 +225,19 @@ export async function loadFromDb(ctx: LoadFromDbContext): Promise<void> {
212
225
  const meta = JSON.parse(m.metadata);
213
226
  const isTail = index === arr.length - 1;
214
227
 
215
- // All rehydration steps are prepends the ordering below
216
- // (innermost block first, outermost last) builds the documented
217
- // shape right-to-left, since each prepend shifts previously-
218
- // prepended blocks one slot right:
219
- // [<workspace>, <turn_context>, <NOW.md>, <memory __injected>,
220
- // <memory>\n…</memory>, <system_reminder>, <knowledge_base>,
221
- // ...original]
222
- //
223
- // Persisted non-tail rows rehydrate the full set so Anthropic's
224
- // prefix cache keeps matching msg[0] across daemon restarts and
225
- // conversation eviction. The tail row gets fresh blocks via
226
- // applyRuntimeInjections on the next turn, so rehydration for the
227
- // tail is limited to blocks that the next turn's injection pipeline
228
- // cannot reconstruct (currently just `memoryInjectedBlock`, which
229
- // is not always re-injected on the next turn).
228
+ // Rehydrate in reverse injection order (innermost block first)
229
+ // so the resulting layout matches `applyRuntimeInjections`'s
230
+ // after-memory-prefix splices in ascending injector order
231
+ // (pkb-context 30, pkb-reminder 35, memory-v2-static 38,
232
+ // now-md 40 — the v2 static block lands inside the memory
233
+ // prefix, so now-md splices *after* it):
234
+ // [<workspace>, <turn_context>, <memory __injected>,
235
+ // <memory>\n…</memory>, <NOW.md>, <system_reminder>,
236
+ // <knowledge_base>, ...original]
237
+ // Required so Anthropic's prefix cache keeps matching msg[0]
238
+ // across daemon restart and conversation eviction. The tail
239
+ // row only rehydrates `memoryInjectedBlock` — the next turn
240
+ // re-injects the rest fresh.
230
241
  if (!isTail && typeof meta.pkbContextBlock === "string") {
231
242
  content = [
232
243
  { type: "text" as const, text: meta.pkbContextBlock },
@@ -241,6 +252,13 @@ export async function loadFromDb(ctx: LoadFromDbContext): Promise<void> {
241
252
  ];
242
253
  }
243
254
 
255
+ if (!isTail && typeof meta.nowScratchpadBlock === "string") {
256
+ content = [
257
+ { type: "text" as const, text: meta.nowScratchpadBlock },
258
+ ...content,
259
+ ];
260
+ }
261
+
244
262
  // The v2 static memory block (essentials/threads/recent/buffer
245
263
  // wrapped in `<memory>…</memory>`) carries personal user memory.
246
264
  // Trust-gated to mirror `shouldExposePersonalMemory` at injection
@@ -281,13 +299,6 @@ export async function loadFromDb(ctx: LoadFromDbContext): Promise<void> {
281
299
  ];
282
300
  }
283
301
 
284
- if (!isTail && typeof meta.nowScratchpadBlock === "string") {
285
- content = [
286
- { type: "text" as const, text: meta.nowScratchpadBlock },
287
- ...content,
288
- ];
289
- }
290
-
291
302
  if (!isTail && typeof meta.turnContextBlock === "string") {
292
303
  content = [
293
304
  { type: "text" as const, text: meta.turnContextBlock },
@@ -336,6 +347,7 @@ export async function loadFromDb(ctx: LoadFromDbContext): Promise<void> {
336
347
  }
337
348
 
338
349
  ctx.loadedHistoryTrustClass = trustClass;
350
+ ctx.loadedHistoryPersonalMemoryAllowed = personalMemoryAllowed;
339
351
 
340
352
  log.info(
341
353
  { conversationId: ctx.conversationId, count: ctx.messages.length },
@@ -423,6 +435,23 @@ export function disposeConversation(ctx: DisposeContext): void {
423
435
  // Best-effort — don't block conversation disposal
424
436
  }
425
437
  }
438
+
439
+ try {
440
+ // Memory-retrospective lifecycle safety-net. The periodic triggers
441
+ // (interval / message_count / pre-compaction) handle the common
442
+ // path; lifecycle catches the gap between the last interval fire
443
+ // and conversation eviction. The job's `no_new_messages` early
444
+ // return makes this a cheap no-op when the periodic path already
445
+ // covered things. Lives inside the `!isAutoAnalysis` guard so
446
+ // auto-analysis conversations don't trigger retrospective enqueues
447
+ // on disposal — mirrors the indexer-time gate in `indexer.ts`.
448
+ enqueueMemoryRetrospectiveIfEnabled({
449
+ conversationId: ctx.conversationId,
450
+ trigger: "lifecycle",
451
+ });
452
+ } catch {
453
+ // Best-effort — don't block conversation disposal
454
+ }
426
455
  }
427
456
 
428
457
  try {
@@ -436,22 +465,6 @@ export function disposeConversation(ctx: DisposeContext): void {
436
465
  } catch {
437
466
  // Best-effort — don't block conversation disposal
438
467
  }
439
-
440
- try {
441
- // Memory-retrospective lifecycle safety-net. The periodic triggers
442
- // (interval / message_count / pre-compaction) handle the common
443
- // path; lifecycle catches the gap between the last interval fire
444
- // and conversation eviction. The job's `no_new_messages` early
445
- // return makes this a cheap no-op when the periodic path already
446
- // covered things. `enqueueMemoryRetrospectiveIfEnabled` has its
447
- // own internal recursion guard.
448
- enqueueMemoryRetrospectiveIfEnabled({
449
- conversationId: ctx.conversationId,
450
- trigger: "lifecycle",
451
- });
452
- } catch {
453
- // Best-effort — don't block conversation disposal
454
- }
455
468
  }
456
469
 
457
470
  abortConversation(
@@ -7,7 +7,10 @@
7
7
 
8
8
  import { v4 as uuid } from "uuid";
9
9
 
10
- import { enrichMessageWithSourcePaths } from "../agent/attachments.js";
10
+ import {
11
+ enrichMessageWithSourcePaths,
12
+ type MessageAttachmentInput,
13
+ } from "../agent/attachments.js";
11
14
  import { createUserMessage } from "../agent/message-types.js";
12
15
  import type {
13
16
  TurnChannelContext,
@@ -191,6 +194,19 @@ export interface MessagingConversationContext {
191
194
  getTurnInterfaceContext(): TurnInterfaceContext | null;
192
195
  }
193
196
 
197
+ export function serializePersistedUserMessageContent(
198
+ content: string,
199
+ attachments: MessageAttachmentInput[],
200
+ displayContent: string | undefined,
201
+ ): string {
202
+ return JSON.stringify(
203
+ createUserMessage(
204
+ displayContent !== undefined ? displayContent : content,
205
+ attachments,
206
+ ).content,
207
+ );
208
+ }
209
+
194
210
  function extractTurnChannelContext(
195
211
  metadata?: Record<string, unknown>,
196
212
  ): TurnChannelContext | null {
@@ -258,6 +274,9 @@ export function buildSlackMetaForPersistence(params: {
258
274
  eventKind: "message",
259
275
  ...(candidate.threadTs ? { threadTs: candidate.threadTs } : {}),
260
276
  ...(candidate.displayName ? { displayName: candidate.displayName } : {}),
277
+ ...(candidate.actorExternalUserId
278
+ ? { actorExternalUserId: candidate.actorExternalUserId }
279
+ : {}),
261
280
  };
262
281
  return writeSlackMetadata(slackMeta);
263
282
  }
@@ -454,11 +473,11 @@ export async function persistQueuedMessageBody(
454
473
  // intent stripping), persist that to DB so users see the full message
455
474
  // after restart. The in-memory userMessage (sent to the LLM) still uses
456
475
  // the stripped content.
457
- const contentToPersist = displayContent
458
- ? JSON.stringify(
459
- createUserMessage(displayContent, attachmentInputs).content,
460
- )
461
- : JSON.stringify(cleanMessage.content);
476
+ const contentToPersist = serializePersistedUserMessageContent(
477
+ content,
478
+ attachmentInputs,
479
+ displayContent,
480
+ );
462
481
  const persistedUserMessage = await addMessage(
463
482
  ctx.conversationId,
464
483
  "user",