@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
@@ -1,6 +1,8 @@
1
+ import { resolveCallSiteConfig } from "../config/llm-resolver.js";
1
2
  import { loadConfig } from "../config/loader.js";
2
3
  import { wrapWithCallSiteRouting } from "../providers/call-site-routing.js";
3
4
  import { resolveDefaultProvider } from "../providers/connection-resolution.js";
5
+ import { listProviders } from "../providers/registry.js";
4
6
  import type { Provider } from "../providers/types.js";
5
7
  import {
6
8
  APPROVAL_COPY_MAX_TOKENS,
@@ -16,6 +18,7 @@ import type {
16
18
  ApprovalConversationResult,
17
19
  ApprovalCopyGenerator,
18
20
  } from "../runtime/http-types.js";
21
+ import { ProviderNotConfiguredError } from "../util/errors.js";
19
22
 
20
23
  // ---------------------------------------------------------------------------
21
24
  // Approval conversation generator constants
@@ -142,14 +145,13 @@ export function createApprovalConversationGenerator(): ApprovalConversationGener
142
145
  // Connection-aware default + per-call routing. `resolveDefaultProvider`
143
146
  // throws `ConnectionResolutionError` on hard config errors (missing /
144
147
  // unknown / mismatched connection) and returns null on soft credential
145
- // failures (vault miss, transient auth) — we treat null as "no
146
- // provider available" and throw a domain-specific error below. We do
147
- // not pre-gate on `listProviders()` because the default provider lives
148
- // behind a `provider_connection` and never appears in the registry's
149
- // initialization-time provider list.
148
+ // failures (missing credential, platform auth unavailable).
150
149
  const baseProvider = await resolveDefaultProvider(config);
151
150
  if (!baseProvider) {
152
- throw new Error("No provider available for approval conversation");
151
+ const resolved = resolveCallSiteConfig("mainAgent", config.llm);
152
+ throw new ProviderNotConfiguredError(resolved.provider, listProviders(), {
153
+ connectionName: resolved.provider_connection,
154
+ });
153
155
  }
154
156
  const provider = wrapWithCallSiteRouting(baseProvider, config);
155
157
 
@@ -4,6 +4,7 @@
4
4
  * for changes.
5
5
  */
6
6
  import {
7
+ type Dirent,
7
8
  existsSync,
8
9
  type FSWatcher,
9
10
  mkdirSync,
@@ -12,13 +13,12 @@ import {
12
13
  watch,
13
14
  watchFile,
14
15
  } from "node:fs";
15
- import { join } from "node:path";
16
+ import { join, relative } from "node:path";
16
17
 
17
18
  import { getConfig, invalidateConfigCache } from "../config/loader.js";
18
19
  import type { MemoryCleanupConfig } from "../config/schemas/memory-lifecycle.js";
19
20
  import { resetCleanupScheduleThrottle } from "../memory/cleanup-schedule-state.js";
20
21
  import { clearEmbeddingBackendCache } from "../memory/embedding-backend.js";
21
- import { invalidateLlmRequestLogSourceCache } from "../memory/llm-request-log-source.js";
22
22
  import { initializeProviders } from "../providers/registry.js";
23
23
  import { handleCancelSignal } from "../signals/cancel.js";
24
24
  import { handleConversationUndoSignal } from "../signals/conversation-undo.js";
@@ -38,6 +38,25 @@ import { reloadMcpServers } from "./mcp-reload-service.js";
38
38
 
39
39
  const log = getLogger("config-watcher");
40
40
 
41
+ const SKILL_WATCH_SKIPPED_DIRS = new Set([
42
+ "node_modules",
43
+ ".git",
44
+ "__pycache__",
45
+ ".install-staging",
46
+ ".cache",
47
+ ".next",
48
+ ".turbo",
49
+ ".venv",
50
+ "coverage",
51
+ ]);
52
+
53
+ function isSkippedSkillWatchPath(relativePath: string): boolean {
54
+ if (relativePath === "(unknown)") return false;
55
+
56
+ const segments = relativePath.split(/[\\/]+/).filter(Boolean);
57
+ return segments.some((segment) => SKILL_WATCH_SKIPPED_DIRS.has(segment));
58
+ }
59
+
41
60
  /**
42
61
  * Attach a resilient error handler to an FSWatcher so that async errors
43
62
  * (e.g. ENXIO when a Unix socket file like `gateway.sock` appears in a
@@ -141,7 +160,6 @@ export class ConfigWatcher {
141
160
  return false;
142
161
  }
143
162
  clearEmbeddingBackendCache();
144
- invalidateLlmRequestLogSourceCache();
145
163
  // If cleanup retention settings changed, reset the cleanup scheduler
146
164
  // throttle so the next worker tick re-enqueues jobs with the new values
147
165
  // instead of waiting out the remaining enqueueIntervalMs (default 6h).
@@ -160,6 +178,8 @@ export class ConfigWatcher {
160
178
  * Start all file watchers. `onConversationEvict` is called when watched
161
179
  * files change and conversations need to be evicted for reload.
162
180
  * `onIdentityChanged` is called when IDENTITY.md changes on disk.
181
+ * `onSkillsChanged` is called after skill directory changes evict
182
+ * conversations.
163
183
  */
164
184
  start(
165
185
  onConversationEvict: () => void,
@@ -167,6 +187,7 @@ export class ConfigWatcher {
167
187
  onSoundsConfigChanged?: () => void,
168
188
  onAvatarChanged?: () => void,
169
189
  onConfigChanged?: () => void,
190
+ onSkillsChanged?: () => void,
170
191
  ): void {
171
192
  // Reset the stopped flag so a stop()→start() cycle on the same
172
193
  // instance resumes hot-reload instead of silently bailing in every
@@ -222,7 +243,7 @@ export class ConfigWatcher {
222
243
 
223
244
  this.startSignalsWatcher();
224
245
  this.startUsersWatcher(onConversationEvict);
225
- this.startSkillsWatchers(onConversationEvict);
246
+ this.startSkillsWatchers(onConversationEvict, onSkillsChanged);
226
247
  }
227
248
 
228
249
  stop(): void {
@@ -422,14 +443,20 @@ export class ConfigWatcher {
422
443
  }
423
444
  }
424
445
 
425
- private startSkillsWatchers(onConversationEvict: () => void): void {
446
+ private startSkillsWatchers(
447
+ onConversationEvict: () => void,
448
+ onSkillsChanged?: () => void,
449
+ ): void {
426
450
  const skillsDir = getWorkspaceSkillsDir();
427
451
  if (!existsSync(skillsDir)) return;
428
452
 
429
453
  const scheduleSkillsReload = (file: string): void => {
430
- this.debounceTimers.schedule(`skills:${file}`, () => {
454
+ if (isSkippedSkillWatchPath(file)) return;
455
+
456
+ this.debounceTimers.schedule("skills:catalog", () => {
431
457
  log.info({ file }, "Skill file changed, reloading");
432
458
  onConversationEvict();
459
+ onSkillsChanged?.();
433
460
  });
434
461
  };
435
462
 
@@ -478,43 +505,79 @@ export class ConfigWatcher {
478
505
  }
479
506
  };
480
507
 
481
- const refreshChildWatchers = (): void => {
482
- const nextChildDirs = new Set<string>();
508
+ const formatSkillChangeLabel = (
509
+ dirPath: string,
510
+ filename: string,
511
+ ): string => {
512
+ if (filename === "(unknown)") {
513
+ const relativeDir = relative(skillsDir, dirPath);
514
+ return relativeDir || "(unknown)";
515
+ }
516
+ const relativeFile = relative(skillsDir, join(dirPath, filename));
517
+ return relativeFile || filename;
518
+ };
483
519
 
520
+ const enumerateSkillSubdirectories = (
521
+ dirPath: string,
522
+ acc: Set<string>,
523
+ ): boolean => {
524
+ let entries: Dirent[];
484
525
  try {
485
- const entries = readdirSync(skillsDir, { withFileTypes: true });
486
- for (const entry of entries) {
487
- if (!entry.isDirectory()) continue;
488
- const childDir = join(skillsDir, entry.name);
489
- nextChildDirs.add(childDir);
490
-
491
- if (childWatchers.has(childDir)) continue;
492
-
493
- const watcher = watchDir(childDir, (filename) => {
494
- const label =
495
- filename === "(unknown)"
496
- ? entry.name
497
- : `${entry.name}/${filename}`;
498
- scheduleSkillsReload(label);
499
- });
500
- if (watcher) {
501
- childWatchers.set(childDir, watcher);
502
- }
503
- }
526
+ entries = readdirSync(dirPath, { withFileTypes: true });
504
527
  } catch (err) {
505
- log.warn({ err, skillsDir }, "Failed to enumerate skill directories");
528
+ log.warn({ err, dirPath }, "Failed to enumerate skill directories");
529
+ return dirPath !== skillsDir;
530
+ }
531
+
532
+ for (const entry of entries) {
533
+ if (!entry.isDirectory()) continue;
534
+ if (SKILL_WATCH_SKIPPED_DIRS.has(entry.name)) continue;
535
+ const childDir = join(dirPath, entry.name);
536
+ acc.add(childDir);
537
+ enumerateSkillSubdirectories(childDir, acc);
538
+ }
539
+ return true;
540
+ };
541
+
542
+ const closeChildWatcher = (dirPath: string, watcher: FSWatcher): void => {
543
+ watcher.close();
544
+ childWatchers.delete(dirPath);
545
+ removeWatcher(watcher);
546
+ };
547
+
548
+ const refreshChildWatchers = (): void => {
549
+ const nextChildDirs = new Set<string>();
550
+ if (!enumerateSkillSubdirectories(skillsDir, nextChildDirs)) {
551
+ for (const [childDir, watcher] of childWatchers.entries()) {
552
+ closeChildWatcher(childDir, watcher);
553
+ }
506
554
  return;
507
555
  }
508
556
 
509
557
  for (const [childDir, watcher] of childWatchers.entries()) {
510
558
  if (nextChildDirs.has(childDir)) continue;
511
- watcher.close();
512
- childWatchers.delete(childDir);
513
- removeWatcher(watcher);
559
+ closeChildWatcher(childDir, watcher);
560
+ }
561
+
562
+ for (const childDir of nextChildDirs) {
563
+ if (childWatchers.has(childDir)) continue;
564
+
565
+ const watcher = watchDir(childDir, (filename) => {
566
+ const file = formatSkillChangeLabel(childDir, filename);
567
+ if (isSkippedSkillWatchPath(file)) return;
568
+
569
+ scheduleSkillsReload(file);
570
+ refreshChildWatchers();
571
+ });
572
+ if (watcher) {
573
+ childWatchers.set(childDir, watcher);
574
+ }
514
575
  }
515
576
  };
516
577
 
517
578
  const rootWatcher = watchDir(skillsDir, (filename) => {
579
+ if (isSkippedSkillWatchPath(filename)) return;
580
+
518
581
  scheduleSkillsReload(filename);
519
582
  refreshChildWatchers();
520
583
  });
@@ -33,6 +33,7 @@ import { resolveCallSiteConfig } from "../config/llm-resolver.js";
33
33
  import { getConfig } from "../config/loader.js";
34
34
  import type { LLMCallSite } from "../config/schemas/llm.js";
35
35
  import type { ContextWindowConfig } from "../config/types.js";
36
+ import { runEmergencyCompaction } from "../context/compactor.js";
36
37
  import {
37
38
  derefToolResultReReads,
38
39
  postTurnTruncateToolResults,
@@ -42,6 +43,7 @@ import {
42
43
  getCalibrationProviderKey,
43
44
  } from "../context/token-estimator.js";
44
45
  import type { ContextWindowManager } from "../context/window-manager.js";
46
+ import { getDocumentsForConversation } from "../documents/document-store.js";
45
47
  import type { ToolProfiler } from "../events/tool-profiling-listener.js";
46
48
  import { writeRelationshipState } from "../home/relationship-state-writer.js";
47
49
  import {
@@ -79,6 +81,8 @@ import {
79
81
  shouldExposePersonalMemory,
80
82
  } from "../memory/v2/static-context.js";
81
83
  import type { PermissionPrompter } from "../permissions/prompter.js";
84
+ import { HOOKS } from "../plugin-api/constants.js";
85
+ import type { UserPromptSubmitContext } from "../plugin-api/types.js";
82
86
  import { defaultCompactionTerminal } from "../plugins/defaults/compaction.js";
83
87
  import { defaultHistoryRepairTerminal } from "../plugins/defaults/history-repair.js";
84
88
  import {
@@ -90,7 +94,7 @@ import {
90
94
  import { defaultPersistenceTerminal } from "../plugins/defaults/persistence.js";
91
95
  import { defaultTitleGenerateTerminal } from "../plugins/defaults/title-generate.js";
92
96
  import { defaultTokenEstimateTerminal } from "../plugins/defaults/token-estimate.js";
93
- import { DEFAULT_TIMEOUTS, runPipeline } from "../plugins/pipeline.js";
97
+ import { DEFAULT_TIMEOUTS, runHook, runPipeline } from "../plugins/pipeline.js";
94
98
  import { getMiddlewaresFor } from "../plugins/registry.js";
95
99
  import type {
96
100
  CircuitBreakerArgs,
@@ -124,6 +128,7 @@ import type { Provider } from "../providers/types.js";
124
128
  import { resolveActorTrust } from "../runtime/actor-trust-resolver.js";
125
129
  import { broadcastMessage } from "../runtime/assistant-event-hub.js";
126
130
  import { DAEMON_INTERNAL_ASSISTANT_ID } from "../runtime/assistant-scope.js";
131
+ import { publishConversationMessagesChanged } from "../runtime/sync/resource-sync-events.js";
127
132
  import { redactSecrets } from "../security/secret-scanner.js";
128
133
  import { getSubagentManager } from "../subagent/index.js";
129
134
  import type { UsageActor } from "../usage/actors.js";
@@ -202,6 +207,10 @@ import type {
202
207
  } from "./message-protocol.js";
203
208
  import type { MemoryRecalled } from "./message-types/memory.js";
204
209
  import type { ConfirmationStateChanged } from "./message-types/messages.js";
210
+ import {
211
+ conversationMetadataSyncTag,
212
+ SYNC_TAGS,
213
+ } from "./message-types/sync.js";
205
214
  import { parseActualTokensFromError } from "./parse-actual-tokens-from-error.js";
206
215
  import type { TraceEmitter } from "./trace-emitter.js";
207
216
  import type { TrustContext } from "./trust-context.js";
@@ -455,6 +464,15 @@ export interface AgentLoopConversationContext {
455
464
  readonly contextWindowManager: ContextWindowManager;
456
465
  contextCompactedMessageCount: number;
457
466
  contextCompactedAt: number | null;
467
+ /**
468
+ * Set by `applyCompactionResult` when compaction strips runtime injections
469
+ * from the preserved tail. The next agent loop turn promotes this into a
470
+ * `compactedThisTurn` signal so NOW.md, PKB, and the v2 static block are
471
+ * re-injected on the first turn following `/compact` (which runs outside
472
+ * the agent loop and so has no other way to surface that compaction
473
+ * happened just before this turn).
474
+ */
475
+ pendingPostCompactReinject: boolean;
458
476
  /** Tracks consecutive compaction failures (summary LLM call threw). */
459
477
  consecutiveCompactionFailures: number;
460
478
  /** Timestamp (ms since epoch) until which the circuit breaker is open. */
@@ -767,6 +785,18 @@ export async function runAgentLoopImpl(
767
785
 
768
786
  ctx.profiler.startRequest();
769
787
  let turnStarted = false;
788
+ const state = createEventHandlerState();
789
+ let persistedErrorAssistantMessage = false;
790
+
791
+ const publishLoopMessagesChanged = (): void => {
792
+ if (
793
+ state.lastAssistantMessageId ||
794
+ state.persistedToolUseIds.size > 0 ||
795
+ persistedErrorAssistantMessage
796
+ ) {
797
+ publishConversationMessagesChanged(ctx.conversationId);
798
+ }
799
+ };
770
800
 
771
801
  // Populate Sentry scope with conversation-specific tags so any exception
772
802
  // captured during this turn (e.g. inside agent/loop.ts) can be
@@ -871,6 +901,13 @@ export async function runAgentLoopImpl(
871
901
  conversationId: ctx.conversationId,
872
902
  title,
873
903
  });
904
+ onEvent({
905
+ type: "sync_changed",
906
+ tags: [
907
+ SYNC_TAGS.conversationsList,
908
+ conversationMetadataSyncTag(ctx.conversationId),
909
+ ],
910
+ });
874
911
  },
875
912
  };
876
913
  setTimeout(() => {
@@ -892,8 +929,14 @@ export async function runAgentLoopImpl(
892
929
  }
893
930
 
894
931
  const isFirstMessage = ctx.messages.length === 1;
895
- let shouldInjectWorkspace = isFirstMessage;
896
- let compactedThisTurn = false;
932
+ // Promote a pending post-compaction re-inject signal (e.g. from `/compact`)
933
+ // into `compactedThisTurn` so NOW.md / PKB / v2 static blocks land on this
934
+ // turn even when no mid-turn compaction fires. Clear the flag immediately
935
+ // so this fires exactly once per `/compact` event.
936
+ const consumedPostCompactReinject = ctx.pendingPostCompactReinject;
937
+ ctx.pendingPostCompactReinject = false;
938
+ let shouldInjectWorkspace = isFirstMessage || consumedPostCompactReinject;
939
+ let compactedThisTurn = consumedPostCompactReinject;
897
940
  let slackCompactedThisTurn = false;
898
941
  const isSlackConversation = ctx.channelCapabilities?.channel === "slack";
899
942
  let currentSlackContextSummary =
@@ -1101,8 +1144,6 @@ export async function runAgentLoopImpl(
1101
1144
  }
1102
1145
  }
1103
1146
 
1104
- const state = createEventHandlerState();
1105
-
1106
1147
  // Register confirmation outcome tracker so the agent loop can link
1107
1148
  // confirmation decisions to tool_use_ids for persistence.
1108
1149
  ctx.onConfirmationOutcome = (
@@ -1328,6 +1369,20 @@ export async function runAgentLoopImpl(
1328
1369
  }
1329
1370
  }
1330
1371
 
1372
+ // Query active documents for this conversation so the injector chain
1373
+ // can surface them to the assistant (prevents duplicate document_create
1374
+ // calls when existing documents should be targeted with document_update).
1375
+ const conversationDocs = getDocumentsForConversation(ctx.conversationId);
1376
+ const activeDocuments =
1377
+ conversationDocs.length > 0
1378
+ ? conversationDocs.map((d) => ({
1379
+ surfaceId: d.surfaceId,
1380
+ title: d.title,
1381
+ wordCount: d.wordCount,
1382
+ updatedAt: d.updatedAt,
1383
+ }))
1384
+ : null;
1385
+
1331
1386
  ctx.refreshWorkspaceTopLevelContextIfNeeded();
1332
1387
 
1333
1388
  // Compute fresh turn timestamp for date grounding.
@@ -1429,9 +1484,13 @@ export async function runAgentLoopImpl(
1429
1484
  // are never stripped on normal turns — this preserves the cached prefix.
1430
1485
  // PKB/NOW content is sourced from the `memoryRetrieval` pipeline above
1431
1486
  // so plugins can override either source without touching the agent loop.
1432
- const currentNowContent = personalMemoryAllowed
1433
- ? memoryResult.nowContent
1434
- : null;
1487
+ // NOW.md injection can be disabled via `memory.retrieval.scratchpadInjection.enabled`.
1488
+ const scratchpadInjectionEnabled =
1489
+ getConfig().memory.retrieval.scratchpadInjection.enabled;
1490
+ const currentNowContent =
1491
+ personalMemoryAllowed && scratchpadInjectionEnabled
1492
+ ? memoryResult.nowContent
1493
+ : null;
1435
1494
  const shouldInjectNowAndPkb = isFirstMessage || compactedThisTurn;
1436
1495
  const nowScratchpad = shouldInjectNowAndPkb ? currentNowContent : null;
1437
1496
 
@@ -1535,6 +1594,7 @@ export async function runAgentLoopImpl(
1535
1594
  const injectionOpts = {
1536
1595
  diskPressureContext,
1537
1596
  activeSurface,
1597
+ activeDocuments,
1538
1598
  workspaceTopLevelContext: shouldInjectWorkspace
1539
1599
  ? ctx.workspaceTopLevelContext
1540
1600
  : null,
@@ -1968,6 +2028,30 @@ export async function runAgentLoopImpl(
1968
2028
  runMessages = webSearchStrip.messages;
1969
2029
  }
1970
2030
 
2031
+ // user-prompt-submit hook: plugins may transform `runMessages` right
2032
+ // before the agent loop receives them. Fires once per user turn at
2033
+ // the primary `agentLoop.run` only — the re-entry / retry calls
2034
+ // further down in this function do not refire it (they're not new
2035
+ // user submissions). Plugins may mutate `ctx.latestMessages` in place
2036
+ // OR return a new context with a fresh array; `runHook` forwards
2037
+ // whichever the chain settles on. Order is plugin registration order.
2038
+ //
2039
+ // Fires BEFORE `preRunHistoryLength` is captured so the boundary
2040
+ // between pre-existing and hook-emitted messages — consumed by the
2041
+ // ordering-error retry gate, the post-run reconcile loop, and the
2042
+ // new-message extraction for persistence — reflects exactly what
2043
+ // `agentLoop.run` receives.
2044
+ const userPromptCtx: UserPromptSubmitContext = {
2045
+ conversationId: ctx.conversationId,
2046
+ originalMessages: ctx.messages,
2047
+ latestMessages: runMessages,
2048
+ };
2049
+ const finalUserPromptCtx = await runHook(
2050
+ HOOKS.USER_PROMPT_SUBMIT,
2051
+ userPromptCtx,
2052
+ );
2053
+ runMessages = finalUserPromptCtx.latestMessages;
2054
+
1971
2055
  let preRunHistoryLength = runMessages.length;
1972
2056
 
1973
2057
  const shouldGenerateTitle = isReplaceableTitle(
@@ -2408,6 +2492,64 @@ export async function runAgentLoopImpl(
2408
2492
  }
2409
2493
  }
2410
2494
 
2495
+ // ── Emergency mid-turn compaction ────────────────────────────
2496
+ // Before entering the reducer tier loop, attempt a targeted
2497
+ // emergency compaction: summarize everything before the last
2498
+ // tool_use + tool_result pair and let the agent continue with
2499
+ // [summary, last_tool_call, last_tool_result]. This preserves
2500
+ // the agent's most recent action context while aggressively
2501
+ // compressing history. Falls through to reducer tiers on failure.
2502
+ {
2503
+ try {
2504
+ const emergencyConfig = getConfig().compaction;
2505
+ const emergencyResult = await runEmergencyCompaction({
2506
+ conversationId: ctx.conversationId,
2507
+ messages: ctx.messages,
2508
+ provider: ctx.provider,
2509
+ systemPrompt: ctx.systemPrompt,
2510
+ tools: undefined,
2511
+ compaction: emergencyConfig,
2512
+ maxInputTokens: effectiveContextWindow.maxInputTokens,
2513
+ previousEstimatedInputTokens: estimatedTokensAtOverflow,
2514
+ force: true,
2515
+ signal: abortController.signal,
2516
+ overrideProfile: turnOverrideProfile ?? null,
2517
+ nonPersistedPrefixCount:
2518
+ ctx.contextWindowManager.nonPersistedPrefixCount,
2519
+ });
2520
+ if (emergencyResult.compacted) {
2521
+ rlog.info(
2522
+ {
2523
+ phase: "convergence",
2524
+ compactedMessages: emergencyResult.compactedMessages,
2525
+ summaryChars: emergencyResult.summaryText.length,
2526
+ },
2527
+ "Emergency mid-turn compaction succeeded — bypassing reducer tiers",
2528
+ );
2529
+ if (emergencyResult.summaryFailed !== undefined) {
2530
+ await trackCompactionOutcome(
2531
+ ctx,
2532
+ emergencyResult.summaryFailed,
2533
+ onEvent,
2534
+ );
2535
+ }
2536
+ if (emergencyResult.compacted) {
2537
+ await applySuccessfulCompaction(emergencyResult, ctx.messages);
2538
+ shouldInjectWorkspace = true;
2539
+ }
2540
+ // Clear the overflow flag and re-run the agent loop with
2541
+ // the compacted context.
2542
+ state.contextTooLargeDetected = false;
2543
+ }
2544
+ } catch (err) {
2545
+ rlog.warn(
2546
+ { phase: "convergence", err },
2547
+ "Emergency mid-turn compaction failed; continuing to reducer tiers",
2548
+ );
2549
+ }
2550
+ // If emergency compaction failed, fall through to reducer tiers.
2551
+ }
2552
+
2411
2553
  let convergenceAttempts = 0;
2412
2554
  const maxAttempts = overflowRecovery.maxAttempts;
2413
2555
 
@@ -2814,6 +2956,7 @@ export async function runAgentLoopImpl(
2814
2956
  buildPluginTurnContext(ctx, reqId),
2815
2957
  DEFAULT_TIMEOUTS.persistence,
2816
2958
  );
2959
+ persistedErrorAssistantMessage = true;
2817
2960
  newMessages.push(errorAssistantMessage);
2818
2961
  // Do NOT send assistant_text_delta here — handleProviderError already
2819
2962
  // emitted a conversation_error event for this same error text, and the
@@ -2889,7 +3032,6 @@ export async function runAgentLoopImpl(
2889
3032
  convForDisk.createdAt,
2890
3033
  );
2891
3034
  };
2892
-
2893
3035
  // Fast-path: when the user cancelled, skip expensive post-loop work
2894
3036
  // (attachment resolution) and emit the cancellation event immediately
2895
3037
  // so the client can re-enable the UI without delay.
@@ -2908,6 +3050,7 @@ export async function runAgentLoopImpl(
2908
3050
  type: "generation_cancelled",
2909
3051
  conversationId: ctx.conversationId,
2910
3052
  });
3053
+ publishLoopMessagesChanged();
2911
3054
  } else {
2912
3055
  // Resolve attachments (only when not cancelled — this is expensive async I/O)
2913
3056
  const attachmentResult = await resolveAssistantAttachments(
@@ -2948,6 +3091,7 @@ export async function runAgentLoopImpl(
2948
3091
  type: "generation_cancelled",
2949
3092
  conversationId: ctx.conversationId,
2950
3093
  });
3094
+ publishLoopMessagesChanged();
2951
3095
  } else if (yieldedForHandoff) {
2952
3096
  ctx.traceEmitter.emit(
2953
3097
  "generation_handoff",
@@ -2976,6 +3120,7 @@ export async function runAgentLoopImpl(
2976
3120
  ? { displayMessageId: clientDisplayMessageId }
2977
3121
  : {}),
2978
3122
  });
3123
+ publishLoopMessagesChanged();
2979
3124
  } else {
2980
3125
  ctx.emitActivityState("idle", "message_complete", "global", reqId);
2981
3126
  ctx.traceEmitter.emit(
@@ -3002,6 +3147,7 @@ export async function runAgentLoopImpl(
3002
3147
  ? { displayMessageId: clientDisplayMessageId }
3003
3148
  : {}),
3004
3149
  });
3150
+ publishLoopMessagesChanged();
3005
3151
 
3006
3152
  // Proactive artifact: fire once when the processed turn was the 4th user message.
3007
3153
  // Only trigger for real user-authored turns (not subagent/system messages).
@@ -3056,6 +3202,13 @@ export async function runAgentLoopImpl(
3056
3202
  conversationId: ctx.conversationId,
3057
3203
  title,
3058
3204
  });
3205
+ onEvent({
3206
+ type: "sync_changed",
3207
+ tags: [
3208
+ SYNC_TAGS.conversationsList,
3209
+ conversationMetadataSyncTag(ctx.conversationId),
3210
+ ],
3211
+ });
3059
3212
  },
3060
3213
  signal: abortController.signal,
3061
3214
  });
@@ -3080,6 +3233,7 @@ export async function runAgentLoopImpl(
3080
3233
  type: "generation_cancelled",
3081
3234
  conversationId: ctx.conversationId,
3082
3235
  });
3236
+ publishLoopMessagesChanged();
3083
3237
  } else {
3084
3238
  ctx.emitActivityState("idle", "error_terminal", "global", reqId);
3085
3239
  const message = err instanceof Error ? err.message : String(err);
@@ -3104,6 +3258,7 @@ export async function runAgentLoopImpl(
3104
3258
  errorCategory: classified.errorCategory,
3105
3259
  });
3106
3260
  onEvent(buildConversationErrorMessage(ctx.conversationId, classified));
3261
+ publishLoopMessagesChanged();
3107
3262
  }
3108
3263
  } finally {
3109
3264
  if (turnStarted) {
@@ -3234,6 +3389,7 @@ export interface CompactionApplyContext {
3234
3389
  messages: Message[];
3235
3390
  contextCompactedMessageCount: number;
3236
3391
  contextCompactedAt: number | null;
3392
+ pendingPostCompactReinject: boolean;
3237
3393
  readonly graphMemory: ConversationGraphMemory;
3238
3394
  readonly provider: Provider;
3239
3395
  usageStats: UsageStats;
@@ -3283,6 +3439,10 @@ export async function applyCompactionResult(
3283
3439
  ctx.contextCompactedMessageCount += result.compactedPersistedMessages;
3284
3440
  const compactedAt = Date.now();
3285
3441
  ctx.contextCompactedAt = compactedAt;
3442
+ // Signal to the next agent loop turn that NOW.md / PKB / v2 static blocks
3443
+ // were stripped from the tail and need fresh re-injection. Consumed and
3444
+ // cleared at the top of the next `runAgentLoopImpl` run.
3445
+ ctx.pendingPostCompactReinject = true;
3286
3446
  await ctx.graphMemory.onCompacted(result.compactedPersistedMessages);
3287
3447
  updateConversationContextWindow(
3288
3448
  ctx.conversationId,