@vellumai/assistant 0.8.0 → 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 (991) hide show
  1. package/AGENTS.md +11 -0
  2. package/ARCHITECTURE.md +2 -7
  3. package/Dockerfile +80 -5
  4. package/README.md +2 -2
  5. package/bun.lock +11 -1
  6. package/docker-entrypoint.sh +21 -0
  7. package/docker-init-apt-root.sh +94 -0
  8. package/docker-kata-apt-env.sh +39 -0
  9. package/docs/plugins.md +88 -47
  10. package/docs/skills.md +9 -7
  11. package/eslint-rules/__tests__/cli-no-daemon-internals.test.ts +420 -0
  12. package/eslint-rules/cli-no-daemon-internals.js +283 -0
  13. package/eslint.config.mjs +12 -0
  14. package/examples/plugins/echo/README.md +27 -27
  15. package/examples/plugins/echo/package.json +3 -0
  16. package/examples/plugins/echo/register.ts +31 -31
  17. package/knip.json +2 -1
  18. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +10 -1
  19. package/node_modules/@vellumai/slack-text/src/index.test.ts +114 -14
  20. package/node_modules/@vellumai/slack-text/src/index.ts +82 -18
  21. package/openapi.yaml +4462 -991
  22. package/package.json +5 -1
  23. package/scripts/generate-openapi.ts +135 -14
  24. package/scripts/sync-llm-catalog.ts +165 -0
  25. package/scripts/sync-web-search-catalog.ts +129 -0
  26. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +169 -0
  27. package/src/__tests__/agent-image-optimize.test.ts +11 -3
  28. package/src/__tests__/agent-loop-override-profile.test.ts +26 -1
  29. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +131 -0
  30. package/src/__tests__/anthropic-provider.test.ts +137 -2
  31. package/src/__tests__/app-builder-tool-scripts.test.ts +9 -3
  32. package/src/__tests__/app-control-flow.test.ts +7 -0
  33. package/src/__tests__/app-executors.test.ts +220 -4
  34. package/src/__tests__/assistant-events-sse-shed.test.ts +232 -0
  35. package/src/__tests__/auto-analysis-end-to-end.test.ts +35 -0
  36. package/src/__tests__/avatar-identity-sync.test.ts +87 -0
  37. package/src/__tests__/background-workers-disk-pressure.test.ts +11 -22
  38. package/src/__tests__/btw-routes.test.ts +1 -0
  39. package/src/__tests__/bundled-asset.test.ts +6 -6
  40. package/src/__tests__/call-site-routing-provider.test.ts +172 -45
  41. package/src/__tests__/cancel-resolves-conversation-key.test.ts +44 -3
  42. package/src/__tests__/channel-availability-routes.test.ts +206 -0
  43. package/src/__tests__/channel-delivery-store.test.ts +289 -1
  44. package/src/__tests__/channel-policy.test.ts +12 -0
  45. package/src/__tests__/checker.test.ts +89 -0
  46. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -1
  47. package/src/__tests__/clawhub.test.ts +75 -16
  48. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +35 -7
  49. package/src/__tests__/compact-event-conversation-id-guard.test.ts +33 -5
  50. package/src/__tests__/compaction-strip-metadata-clear.test.ts +26 -1
  51. package/src/__tests__/compactor-tail-resolution.test.ts +41 -0
  52. package/src/__tests__/config-loader-backfill.test.ts +526 -102
  53. package/src/__tests__/config-loader-corrupt.test.ts +68 -0
  54. package/src/__tests__/config-loader-platform-defaults.test.ts +77 -23
  55. package/src/__tests__/config-schema-cmd.test.ts +63 -29
  56. package/src/__tests__/config-schema.test.ts +35 -3
  57. package/src/__tests__/config-set-platform-guard.test.ts +75 -152
  58. package/src/__tests__/config-set-route.test.ts +278 -0
  59. package/src/__tests__/config-sounds-sync.test.ts +97 -0
  60. package/src/__tests__/config-watcher-skill-reseed.test.ts +453 -0
  61. package/src/__tests__/config-watcher.test.ts +6 -0
  62. package/src/__tests__/contacts-tools.test.ts +51 -199
  63. package/src/__tests__/context-search-agent-protocol.test.ts +21 -2
  64. package/src/__tests__/context-search-agent-runner.test.ts +22 -138
  65. package/src/__tests__/context-search-conversations-source.test.ts +159 -18
  66. package/src/__tests__/context-search-fanout.test.ts +20 -157
  67. package/src/__tests__/context-search-memory-v2-source.test.ts +3 -4
  68. package/src/__tests__/context-search-types.test.ts +7 -2
  69. package/src/__tests__/context-search-workspace-source.test.ts +7 -0
  70. package/src/__tests__/context-token-estimator.test.ts +1 -0
  71. package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
  72. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
  73. package/src/__tests__/conversation-agent-loop-overflow.test.ts +93 -92
  74. package/src/__tests__/conversation-agent-loop.test.ts +2 -0
  75. package/src/__tests__/conversation-crud-inference-profile.test.ts +100 -0
  76. package/src/__tests__/conversation-error.test.ts +80 -3
  77. package/src/__tests__/conversation-fork-crud.test.ts +323 -1
  78. package/src/__tests__/conversation-inference-profile-route.test.ts +54 -18
  79. package/src/__tests__/conversation-init.benchmark.test.ts +1 -0
  80. package/src/__tests__/conversation-lifecycle.test.ts +297 -0
  81. package/src/__tests__/conversation-message-sync-tags.test.ts +97 -0
  82. package/src/__tests__/conversation-pairing.test.ts +54 -0
  83. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +100 -1
  84. package/src/__tests__/conversation-process-callsite.test.ts +25 -2
  85. package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -1
  86. package/src/__tests__/conversation-queue.test.ts +4 -1
  87. package/src/__tests__/conversation-runtime-assembly.test.ts +80 -13
  88. package/src/__tests__/conversation-slash-commands.test.ts +194 -2
  89. package/src/__tests__/conversation-slash-queue.test.ts +59 -1
  90. package/src/__tests__/conversation-slash-unknown.test.ts +4 -1
  91. package/src/__tests__/conversation-surfaces-app-control.test.ts +323 -3
  92. package/src/__tests__/conversation-surfaces-table-action.test.ts +360 -0
  93. package/src/__tests__/conversation-sync-tags.test.ts +235 -0
  94. package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
  95. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
  96. package/src/__tests__/credential-security-invariants.test.ts +8 -8
  97. package/src/__tests__/daemon-credential-client.test.ts +56 -1
  98. package/src/__tests__/db-activation-state-fk-cascade.test.ts +132 -0
  99. package/src/__tests__/db-conversation-inference-profile-migration.test.ts +37 -0
  100. package/src/__tests__/db-memory-graph-event-date-repair.test.ts +43 -20
  101. package/src/__tests__/db-proxy-transaction.test.ts +206 -0
  102. package/src/__tests__/db-slack-external-content-normalization.test.ts +301 -0
  103. package/src/__tests__/delete-managed-skill-tool.test.ts +55 -13
  104. package/src/__tests__/disk-pressure-tools.test.ts +1 -0
  105. package/src/__tests__/dm-backfill.test.ts +121 -10
  106. package/src/__tests__/document-tool-security.test.ts +258 -0
  107. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
  108. package/src/__tests__/edit-propagation.test.ts +33 -0
  109. package/src/__tests__/empty-response-pipeline.test.ts +0 -4
  110. package/src/__tests__/external-plugin-loader.test.ts +482 -0
  111. package/src/__tests__/filing-service.test.ts +163 -3
  112. package/src/__tests__/fixtures/mock-chrome-extension.ts +5 -0
  113. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  114. package/src/__tests__/get-skill-detail-audit.test.ts +0 -4
  115. package/src/__tests__/graph-extraction-event-date.test.ts +34 -0
  116. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +42 -69
  117. package/src/__tests__/heartbeat-disk-pressure.test.ts +21 -8
  118. package/src/__tests__/heartbeat-service.test.ts +50 -233
  119. package/src/__tests__/helpers/tar-fixtures.ts +39 -0
  120. package/src/__tests__/helpers/wait-for.ts +21 -0
  121. package/src/__tests__/history-repair-pipeline.test.ts +0 -3
  122. package/src/__tests__/history-repair.test.ts +162 -0
  123. package/src/__tests__/host-app-control-proxy.test.ts +365 -1
  124. package/src/__tests__/host-app-control-routes.test.ts +247 -1
  125. package/src/__tests__/host-browser-proxy.test.ts +416 -20
  126. package/src/__tests__/host-browser-routes.test.ts +325 -33
  127. package/src/__tests__/host-proxy-preactivation.test.ts +211 -0
  128. package/src/__tests__/image-credentials.test.ts +1 -1
  129. package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
  130. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +246 -0
  131. package/src/__tests__/inference-profile-reaper.test.ts +156 -0
  132. package/src/__tests__/inference-profile-session-handler.test.ts +410 -0
  133. package/src/__tests__/inference-profile-session-ipc.test.ts +248 -0
  134. package/src/__tests__/injector-chain.test.ts +10 -8
  135. package/src/__tests__/inline-skill-load-permissions.test.ts +6 -1
  136. package/src/__tests__/install-skill-routing.test.ts +157 -39
  137. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +107 -3
  138. package/src/__tests__/list-messages-page-latest.test.ts +55 -0
  139. package/src/__tests__/llm-call-pipeline.test.ts +0 -3
  140. package/src/__tests__/llm-callsite-catalog.test.ts +20 -1
  141. package/src/__tests__/llm-catalog-parity.test.ts +190 -2
  142. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +222 -0
  143. package/src/__tests__/llm-request-log-source-factory.test.ts +100 -0
  144. package/src/__tests__/llm-resolver.test.ts +46 -0
  145. package/src/__tests__/llm-usage-store.test.ts +114 -0
  146. package/src/__tests__/managed-profile-guard.test.ts +145 -14
  147. package/src/__tests__/managed-skill-lifecycle.test.ts +109 -18
  148. package/src/__tests__/managed-store.test.ts +84 -192
  149. package/src/__tests__/mcp-auth-routes.test.ts +1 -0
  150. package/src/__tests__/mcp-cli.test.ts +182 -220
  151. package/src/__tests__/mcp-health-check.test.ts +56 -27
  152. package/src/__tests__/media-generate-image.test.ts +1 -1
  153. package/src/__tests__/memory-jobs-worker-lanes.test.ts +18 -11
  154. package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -2
  155. package/src/__tests__/message-complete-display-id.test.ts +175 -0
  156. package/src/__tests__/messages-after-tiebreaker.test.ts +122 -0
  157. package/src/__tests__/notification-platform-adapter.test.ts +229 -0
  158. package/src/__tests__/oauth-cli.test.ts +38 -2009
  159. package/src/__tests__/oauth-commands-routes.test.ts +863 -0
  160. package/src/__tests__/oauth-connect-routes.test.ts +174 -11
  161. package/src/__tests__/oauth-provider-profiles.test.ts +9 -0
  162. package/src/__tests__/oauth-providers-routes.test.ts +14 -10
  163. package/src/__tests__/openai-provider.test.ts +24 -0
  164. package/src/__tests__/openai-responses-cutover-guard.test.ts +48 -19
  165. package/src/__tests__/openai-responses-provider.test.ts +17 -0
  166. package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -2
  167. package/src/__tests__/persistence-pipeline.test.ts +0 -2
  168. package/src/__tests__/{managed-proxy-context.test.ts → platform-proxy-context.test.ts} +1 -1
  169. package/src/__tests__/platform.test.ts +2 -0
  170. package/src/__tests__/plugin-api-shim.test.ts +125 -0
  171. package/src/__tests__/plugin-bootstrap.test.ts +41 -38
  172. package/src/__tests__/plugin-external-api.test.ts +68 -0
  173. package/src/__tests__/plugin-registry.test.ts +0 -77
  174. package/src/__tests__/plugin-route-contribution.test.ts +31 -4
  175. package/src/__tests__/plugin-skill-contribution.test.ts +0 -2
  176. package/src/__tests__/plugin-tool-contribution.test.ts +47 -18
  177. package/src/__tests__/plugin-types.test.ts +15 -23
  178. package/src/__tests__/process-message-background-slack.test.ts +53 -0
  179. package/src/__tests__/process-message-display-content.test.ts +421 -0
  180. package/src/__tests__/profile-entry-status.test.ts +43 -0
  181. package/src/__tests__/provider-catalog-visibility.test.ts +142 -0
  182. package/src/__tests__/provider-error-scenarios.test.ts +111 -0
  183. package/src/__tests__/{provider-managed-proxy-integration.test.ts → provider-platform-proxy-integration.test.ts} +20 -12
  184. package/src/__tests__/provider-registry-ollama.test.ts +12 -4
  185. package/src/__tests__/provider-send-message-override-profile.test.ts +10 -4
  186. package/src/__tests__/relay-server.test.ts +118 -0
  187. package/src/__tests__/retry-thinking-tool-choice.test.ts +15 -0
  188. package/src/__tests__/scaffold-managed-skill-tool.test.ts +65 -13
  189. package/src/__tests__/schedule-retry.test.ts +56 -4
  190. package/src/__tests__/schedule-routes.test.ts +151 -0
  191. package/src/__tests__/schedule-store.test.ts +94 -0
  192. package/src/__tests__/scheduler-disk-pressure.test.ts +0 -4
  193. package/src/__tests__/scheduler-recurrence.test.ts +87 -34
  194. package/src/__tests__/scheduler-reuse-conversation.test.ts +208 -5
  195. package/src/__tests__/scheduler-wake.test.ts +0 -63
  196. package/src/__tests__/schema-transforms.test.ts +20 -0
  197. package/src/__tests__/search-skills-unified.test.ts +0 -5
  198. package/src/__tests__/secret-allowlist.test.ts +1 -0
  199. package/src/__tests__/{secret-routes-managed-proxy.test.ts → secret-routes-platform-proxy.test.ts} +12 -4
  200. package/src/__tests__/server-history-render.test.ts +43 -0
  201. package/src/__tests__/shell-credential-ref.test.ts +95 -3
  202. package/src/__tests__/shell-tool-proxy-mode.test.ts +14 -0
  203. package/src/__tests__/skill-load-feature-flag.test.ts +1 -12
  204. package/src/__tests__/skill-load-tool.test.ts +29 -93
  205. package/src/__tests__/skill-memory.test.ts +23 -3
  206. package/src/__tests__/skills-file-content-endpoint.test.ts +9 -38
  207. package/src/__tests__/skills-files-catalog-fallback.test.ts +0 -3
  208. package/src/__tests__/skills-install-extract.test.ts +49 -38
  209. package/src/__tests__/skills-install-staging.test.ts +159 -0
  210. package/src/__tests__/skills-uninstall.test.ts +9 -41
  211. package/src/__tests__/skills.test.ts +51 -58
  212. package/src/__tests__/slack-channel-config.test.ts +9 -0
  213. package/src/__tests__/subagent-call-site-routing.test.ts +78 -16
  214. package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
  215. package/src/__tests__/suggestion-routes.test.ts +3 -3
  216. package/src/__tests__/sync-message-contract.test.ts +63 -0
  217. package/src/__tests__/system-prompt.test.ts +737 -63
  218. package/src/__tests__/task-scheduler.test.ts +88 -23
  219. package/src/__tests__/terminal-tools.test.ts +28 -1
  220. package/src/__tests__/thread-backfill.test.ts +557 -27
  221. package/src/__tests__/title-generate-pipeline.test.ts +0 -13
  222. package/src/__tests__/token-estimate-pipeline.test.ts +0 -3
  223. package/src/__tests__/tool-error-pipeline.test.ts +0 -3
  224. package/src/__tests__/tool-execute-pipeline.test.ts +0 -5
  225. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
  226. package/src/__tests__/tool-executor.test.ts +16 -4
  227. package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -12
  228. package/src/__tests__/turn-events-store.test.ts +256 -0
  229. package/src/__tests__/twilio-routes.test.ts +4 -0
  230. package/src/__tests__/update-bulletin-job.test.ts +96 -193
  231. package/src/__tests__/usage-cli.test.ts +11 -73
  232. package/src/__tests__/user-plugin-loader.test.ts +143 -5
  233. package/src/__tests__/vercel-config.test.ts +168 -0
  234. package/src/__tests__/voice-session-bridge.test.ts +198 -0
  235. package/src/__tests__/web-search-catalog-parity.test.ts +108 -0
  236. package/src/__tests__/web-search.test.ts +303 -2
  237. package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +1 -21
  238. package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +170 -0
  239. package/src/__tests__/workspace-migration-069-seed-onboarding-threads.test.ts +53 -20
  240. package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +241 -0
  241. package/src/__tests__/workspace-migration-073-repair-recall-callsite-empty-profile.test.ts +153 -0
  242. package/src/__tests__/workspace-migration-076-drop-services-inference-mode.test.ts +211 -0
  243. package/src/__tests__/workspace-migration-077-seed-memory-router-callsite.test.ts +174 -0
  244. package/src/__tests__/workspace-migration-079-home-feed-notification-only.test.ts +323 -0
  245. package/src/__tests__/workspace-migration-080-restrict-vercel-api-token-metadata.test.ts +299 -0
  246. package/src/__tests__/workspace-migration-081-backfill-bash-allowed-tools.test.ts +410 -0
  247. package/src/__tests__/workspace-migration-082-backfill-managed-profile-labels.test.ts +268 -0
  248. package/src/__tests__/workspace-migration-085-memory-v2-bm25-b-reembed-disabled-v2-pages.test.ts +220 -0
  249. package/src/__tests__/workspace-migration-086-revert-stale-gemini-mis-rewrites.test.ts +269 -0
  250. package/src/__tests__/workspace-migration-remove-legacy-skills-index.test.ts +309 -0
  251. package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +3 -3
  252. package/src/__tests__/workspace-migrations-runner.test.ts +111 -3
  253. package/src/__tests__/workspace-release-notes-feature-flag-guard.test.ts +115 -0
  254. package/src/acp/__tests__/helpers/which-stub.ts +4 -2
  255. package/src/acp/resolve-agent.test.ts +25 -0
  256. package/src/acp/resolve-agent.ts +13 -2
  257. package/src/acp/session-manager.ts +14 -0
  258. package/src/agent/image-optimize.ts +13 -5
  259. package/src/approvals/guardian-request-resolvers.ts +32 -87
  260. package/src/calls/relay-server.ts +35 -0
  261. package/src/calls/relay-setup-router.ts +36 -0
  262. package/src/calls/types.ts +1 -0
  263. package/src/calls/voice-session-bridge.ts +74 -36
  264. package/src/channels/config.ts +14 -1
  265. package/src/channels/types.ts +109 -0
  266. package/src/cli/AGENTS.md +164 -4
  267. package/src/cli/__tests__/notifications.test.ts +54 -0
  268. package/src/cli/__tests__/unknown-command.test.ts +24 -0
  269. package/src/cli/commands/__tests__/avatar.test.ts +540 -0
  270. package/src/cli/commands/__tests__/backup.test.ts +236 -776
  271. package/src/cli/commands/__tests__/cache.test.ts +1 -1
  272. package/src/cli/commands/__tests__/changelog.test.ts +578 -0
  273. package/src/cli/commands/__tests__/channel-verification-sessions.test.ts +503 -0
  274. package/src/cli/commands/__tests__/conversations-import.test.ts +515 -0
  275. package/src/cli/commands/__tests__/domain-register.test.ts +140 -167
  276. package/src/cli/commands/__tests__/domain-status.test.ts +137 -76
  277. package/src/cli/commands/__tests__/email-attachment.test.ts +314 -337
  278. package/src/cli/commands/__tests__/email-core.test.ts +579 -0
  279. package/src/cli/commands/__tests__/image-generation.test.ts +87 -824
  280. package/src/cli/commands/__tests__/inference-send.test.ts +30 -266
  281. package/src/cli/commands/__tests__/inference-session.test.ts +423 -0
  282. package/src/cli/commands/__tests__/memory-v2.test.ts +81 -110
  283. package/src/cli/commands/__tests__/schedules.test.ts +491 -0
  284. package/src/cli/commands/__tests__/skills.test.ts +563 -0
  285. package/src/cli/commands/__tests__/status.test.ts +249 -0
  286. package/src/cli/commands/__tests__/stt.test.ts +320 -0
  287. package/src/cli/commands/__tests__/tts-synthesize.test.ts +4 -603
  288. package/src/cli/commands/__tests__/tts.test.ts +321 -0
  289. package/src/cli/commands/__tests__/webhooks.test.ts +86 -511
  290. package/src/cli/commands/attachment.ts +8 -3
  291. package/src/cli/commands/audit.ts +95 -64
  292. package/src/cli/commands/auth.ts +61 -58
  293. package/src/cli/commands/avatar.ts +276 -390
  294. package/src/cli/commands/backup.ts +409 -505
  295. package/src/cli/commands/bash.ts +9 -5
  296. package/src/cli/commands/browser.ts +28 -9
  297. package/src/cli/commands/cache.ts +9 -4
  298. package/src/cli/commands/changelog.ts +478 -0
  299. package/src/cli/commands/channel-verification-sessions.ts +238 -317
  300. package/src/cli/commands/clients.ts +8 -3
  301. package/src/cli/commands/completions.ts +9 -9
  302. package/src/cli/commands/config.ts +102 -72
  303. package/src/cli/commands/contacts.ts +575 -696
  304. package/src/cli/commands/conversations-defer.ts +17 -69
  305. package/src/cli/commands/conversations-import.ts +90 -253
  306. package/src/cli/commands/conversations.ts +429 -434
  307. package/src/cli/commands/credential-execution.ts +9 -6
  308. package/src/cli/commands/credentials.ts +456 -736
  309. package/src/cli/commands/default-action.ts +10 -53
  310. package/src/cli/commands/domain.ts +128 -206
  311. package/src/cli/commands/email.ts +606 -794
  312. package/src/cli/commands/gateway.ts +8 -1
  313. package/src/cli/commands/image-generation.ts +157 -205
  314. package/src/cli/commands/inference-providers.ts +352 -0
  315. package/src/cli/commands/inference-session.ts +415 -0
  316. package/src/cli/commands/inference.ts +87 -65
  317. package/src/cli/commands/keys.ts +8 -3
  318. package/src/cli/commands/mcp.ts +103 -287
  319. package/src/cli/commands/memory-v2.ts +162 -516
  320. package/src/cli/commands/notifications.ts +342 -304
  321. package/src/cli/commands/oauth/apps.ts +292 -261
  322. package/src/cli/commands/oauth/connect.ts +176 -297
  323. package/src/cli/commands/oauth/disconnect.ts +16 -215
  324. package/src/cli/commands/oauth/index.ts +49 -45
  325. package/src/cli/commands/oauth/mode.ts +43 -199
  326. package/src/cli/commands/oauth/ping.ts +17 -125
  327. package/src/cli/commands/oauth/providers.ts +732 -921
  328. package/src/cli/commands/oauth/request.ts +60 -350
  329. package/src/cli/commands/oauth/shared.ts +11 -121
  330. package/src/cli/commands/oauth/status.ts +31 -121
  331. package/src/cli/commands/oauth/token.ts +13 -55
  332. package/src/cli/commands/pending.ts +19 -10
  333. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +133 -183
  334. package/src/cli/commands/platform/__tests__/connect.test.ts +66 -181
  335. package/src/cli/commands/platform/__tests__/disconnect.test.ts +71 -227
  336. package/src/cli/commands/platform/__tests__/status.test.ts +169 -287
  337. package/src/cli/commands/platform/connect.ts +16 -80
  338. package/src/cli/commands/platform/disconnect.ts +14 -112
  339. package/src/cli/commands/platform/index.ts +177 -246
  340. package/src/cli/commands/plugins.ts +185 -0
  341. package/src/cli/commands/routes.ts +153 -336
  342. package/src/cli/commands/schedules.ts +391 -0
  343. package/src/cli/commands/sequence.ts +316 -360
  344. package/src/cli/commands/skills.ts +449 -671
  345. package/src/cli/commands/status.ts +58 -37
  346. package/src/cli/commands/stt.ts +94 -262
  347. package/src/cli/commands/task.ts +14 -40
  348. package/src/cli/commands/telemetry.ts +40 -0
  349. package/src/cli/commands/trust.ts +8 -3
  350. package/src/cli/commands/tts.ts +162 -167
  351. package/src/cli/commands/ui.ts +35 -42
  352. package/src/cli/commands/usage.ts +188 -126
  353. package/src/cli/commands/watchers.ts +8 -3
  354. package/src/cli/commands/webhooks.ts +99 -193
  355. package/src/cli/lib/__tests__/cli-colors.test.ts +48 -0
  356. package/src/cli/lib/__tests__/confirm-prompt.test.ts +159 -0
  357. package/src/cli/lib/__tests__/install-from-github.test.ts +355 -0
  358. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +154 -0
  359. package/src/cli/lib/__tests__/register-command.test.ts +85 -0
  360. package/src/cli/lib/__tests__/uninstall-plugin.test.ts +124 -0
  361. package/src/cli/lib/__tests__/unknown-command.test.ts +106 -0
  362. package/src/cli/lib/cli-colors.ts +12 -0
  363. package/src/cli/lib/confirm-prompt.ts +79 -0
  364. package/src/cli/lib/daemon-credential-client.ts +4 -5
  365. package/src/cli/lib/install-from-github.ts +304 -0
  366. package/src/cli/lib/list-installed-plugins.ts +137 -0
  367. package/src/cli/lib/nested-value.ts +44 -0
  368. package/src/cli/lib/open-browser.ts +36 -0
  369. package/src/cli/lib/register-command.ts +19 -0
  370. package/src/cli/lib/time-ago.ts +34 -0
  371. package/src/cli/lib/uninstall-plugin.ts +82 -0
  372. package/src/cli/lib/unknown-command.ts +111 -0
  373. package/src/cli/program.ts +40 -6
  374. package/src/cli/utils/__tests__/conversation-id.test.ts +66 -0
  375. package/src/cli/utils/__tests__/parse-duration.test.ts +49 -0
  376. package/src/cli/utils/conversation-id.ts +30 -0
  377. package/src/cli/utils/parse-duration.ts +41 -0
  378. package/src/config/acp-defaults.test.ts +5 -1
  379. package/src/config/acp-defaults.ts +11 -4
  380. package/src/config/bundled-skills/acp/TOOLS.json +2 -2
  381. package/src/config/bundled-skills/app-builder/SKILL.md +23 -21
  382. package/src/config/bundled-skills/app-builder/TOOLS.json +7 -0
  383. package/src/config/bundled-skills/app-control/TOOLS.json +32 -0
  384. package/src/config/bundled-skills/computer-use/TOOLS.json +15 -52
  385. package/src/config/bundled-skills/contacts/SKILL.md +12 -45
  386. package/src/config/bundled-skills/contacts/TOOLS.json +0 -57
  387. package/src/config/bundled-skills/document/SKILL.md +23 -3
  388. package/src/config/bundled-skills/document/TOOLS.json +53 -0
  389. package/src/config/bundled-skills/document/tools/document-delete.ts +12 -0
  390. package/src/config/bundled-skills/document/tools/document-list.ts +12 -0
  391. package/src/config/bundled-skills/document/tools/document-read.ts +12 -0
  392. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +0 -12
  393. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +0 -58
  394. package/src/config/bundled-skills/skill-management/SKILL.md +2 -2
  395. package/src/config/bundled-skills/skill-management/TOOLS.json +7 -7
  396. package/src/config/bundled-tool-registry.ts +6 -2
  397. package/src/config/feature-flag-registry.json +57 -1
  398. package/src/config/llm-resolver.ts +16 -1
  399. package/src/config/loader.ts +140 -52
  400. package/src/config/raw-config-utils.ts +2 -30
  401. package/src/config/schema.ts +8 -7
  402. package/src/config/schemas/__tests__/llm-request-logs.test.ts +36 -0
  403. package/src/config/schemas/__tests__/memory-v2.test.ts +49 -0
  404. package/src/config/schemas/call-site-catalog.ts +29 -7
  405. package/src/config/schemas/channels.ts +8 -0
  406. package/src/config/schemas/compaction.ts +28 -0
  407. package/src/config/schemas/heartbeat.ts +9 -0
  408. package/src/config/schemas/llm-request-logs.ts +81 -0
  409. package/src/config/schemas/llm.ts +55 -2
  410. package/src/config/schemas/memory-retrieval.ts +18 -0
  411. package/src/config/schemas/memory-retrospective.ts +48 -0
  412. package/src/config/schemas/memory-v2.ts +32 -1
  413. package/src/config/schemas/memory.ts +4 -0
  414. package/src/config/schemas/services.ts +15 -12
  415. package/src/config/schemas/tools.ts +14 -0
  416. package/src/config/seed-inference-profiles.ts +195 -134
  417. package/src/config/skills.ts +3 -96
  418. package/src/contacts/contact-store.ts +0 -61
  419. package/src/context/compactor.ts +1047 -0
  420. package/src/context/token-estimator.ts +2 -2
  421. package/src/context/window-manager.ts +197 -1334
  422. package/src/credential-execution/managed-catalog.ts +37 -0
  423. package/src/credential-health/credential-health-service.ts +280 -19
  424. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +113 -0
  425. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +138 -0
  426. package/src/daemon/__tests__/conversation-tool-setup.test.ts +183 -4
  427. package/src/daemon/__tests__/daemon-skill-host.test.ts +10 -4
  428. package/src/daemon/approval-generators.ts +26 -30
  429. package/src/daemon/config-watcher.ts +94 -29
  430. package/src/daemon/conversation-agent-loop-handlers.ts +24 -0
  431. package/src/daemon/conversation-agent-loop.ts +293 -103
  432. package/src/daemon/conversation-error.ts +188 -33
  433. package/src/daemon/conversation-lifecycle.ts +80 -26
  434. package/src/daemon/conversation-messaging.ts +25 -6
  435. package/src/daemon/conversation-process.ts +85 -31
  436. package/src/daemon/conversation-runtime-assembly.ts +30 -6
  437. package/src/daemon/conversation-slash.ts +184 -25
  438. package/src/daemon/conversation-store.ts +24 -10
  439. package/src/daemon/conversation-surfaces.ts +76 -12
  440. package/src/daemon/conversation-tool-setup.ts +63 -21
  441. package/src/daemon/conversation.ts +81 -10
  442. package/src/daemon/external-plugins-bootstrap.ts +231 -185
  443. package/src/daemon/first-greeting.ts +22 -2
  444. package/src/daemon/guardian-action-generators.ts +7 -22
  445. package/src/daemon/handlers/config-model.ts +13 -130
  446. package/src/daemon/handlers/config-slack-channel.ts +25 -10
  447. package/src/daemon/handlers/config-vercel.ts +3 -1
  448. package/src/daemon/handlers/shared.ts +14 -5
  449. package/src/daemon/handlers/skills.ts +166 -84
  450. package/src/daemon/history-repair.ts +61 -7
  451. package/src/daemon/host-app-control-proxy.ts +129 -29
  452. package/src/daemon/host-bash-proxy.ts +85 -158
  453. package/src/daemon/host-browser-proxy.ts +96 -35
  454. package/src/daemon/host-proxy-base.ts +13 -1
  455. package/src/daemon/host-proxy-preactivation.ts +25 -1
  456. package/src/daemon/identity-helpers.ts +19 -0
  457. package/src/daemon/lifecycle.ts +79 -70
  458. package/src/daemon/meet-host-supervisor.ts +20 -19
  459. package/src/daemon/memory-v2-startup.ts +58 -2
  460. package/src/daemon/message-protocol.ts +7 -0
  461. package/src/daemon/message-types/bookmarks.ts +18 -0
  462. package/src/daemon/message-types/conversations.ts +37 -9
  463. package/src/daemon/message-types/messages.ts +70 -1
  464. package/src/daemon/message-types/subagents.ts +1 -0
  465. package/src/daemon/message-types/sync.ts +61 -0
  466. package/src/daemon/pkb-reminder-builder.test.ts +54 -13
  467. package/src/daemon/pkb-reminder-builder.ts +21 -7
  468. package/src/daemon/plugin-source-watcher.ts +146 -0
  469. package/src/daemon/process-message.ts +77 -26
  470. package/src/daemon/server.ts +34 -20
  471. package/src/daemon/shutdown-handlers.ts +0 -2
  472. package/src/daemon/skill-memory-refresh.ts +29 -0
  473. package/src/daemon/tool-setup-types.ts +9 -0
  474. package/src/daemon/tool-side-effects.ts +6 -4
  475. package/src/daemon/wake-target-adapter.ts +11 -0
  476. package/src/documents/document-store.ts +221 -3
  477. package/src/embedded/plugin-api.ts +40 -0
  478. package/src/export/transcript-formatter.ts +61 -2
  479. package/src/filing/filing-service.ts +79 -53
  480. package/src/heartbeat/__tests__/heartbeat-service.test.ts +444 -0
  481. package/src/heartbeat/heartbeat-run-store.ts +3 -1
  482. package/src/heartbeat/heartbeat-service.ts +189 -127
  483. package/src/home/__tests__/feed-types.test.ts +99 -127
  484. package/src/home/__tests__/feed-writer.test.ts +77 -278
  485. package/src/home/__tests__/post-connect-feed.test.ts +9 -12
  486. package/src/home/feed-types.ts +41 -73
  487. package/src/home/feed-writer.ts +25 -156
  488. package/src/home/post-connect-feed.ts +2 -3
  489. package/src/index.ts +18 -1
  490. package/src/ipc/__tests__/cli-ipc.test.ts +2 -0
  491. package/src/ipc/__tests__/email-ipc.test.ts +506 -0
  492. package/src/ipc/__tests__/exit-helper.test.ts +104 -0
  493. package/src/ipc/__tests__/streaming-client.test.ts +237 -0
  494. package/src/ipc/__tests__/streaming-framing.test.ts +142 -0
  495. package/src/ipc/assistant-server.ts +55 -6
  496. package/src/ipc/cli-client.ts +370 -50
  497. package/src/ipc/routes/db-proxy-transaction.ts +151 -0
  498. package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +60 -0
  499. package/src/ipc/skill-routes/events.ts +30 -3
  500. package/src/live-voice/__tests__/live-voice-session-manager.test.ts +46 -0
  501. package/src/live-voice/__tests__/live-voice-stt.test.ts +57 -0
  502. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +1 -0
  503. package/src/live-voice/live-voice-session-manager.ts +11 -4
  504. package/src/live-voice/live-voice-session.ts +14 -6
  505. package/src/mcp/client.ts +20 -4
  506. package/src/media/image-credentials.ts +3 -3
  507. package/src/memory/__tests__/bookmark-crud.test.ts +264 -0
  508. package/src/memory/__tests__/bookmark-schema.test.ts +181 -0
  509. package/src/memory/__tests__/conversation-queries.test.ts +263 -0
  510. package/src/memory/__tests__/conversation-types.test.ts +36 -0
  511. package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +130 -0
  512. package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +113 -0
  513. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +177 -0
  514. package/src/memory/__tests__/memory-retrospective-job.test.ts +328 -0
  515. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +318 -0
  516. package/src/memory/__tests__/memory-retrospective-trigger-check.test.ts +90 -0
  517. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +69 -0
  518. package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +3 -0
  519. package/src/memory/__tests__/message-content.test.ts +35 -0
  520. package/src/memory/bookmark-crud.ts +211 -0
  521. package/src/memory/context-search/__tests__/agent-runner-redaction.test.ts +31 -9
  522. package/src/memory/context-search/agent-protocol.ts +5 -1
  523. package/src/memory/context-search/agent-runner.ts +60 -85
  524. package/src/memory/context-search/limits.ts +1 -4
  525. package/src/memory/context-search/search.ts +23 -113
  526. package/src/memory/context-search/sources/conversations.ts +80 -8
  527. package/src/memory/context-search/sources/memory-v2.ts +39 -14
  528. package/src/memory/context-search/sources/memory.ts +7 -0
  529. package/src/memory/context-search/sources/workspace.ts +17 -10
  530. package/src/memory/context-search/types.ts +1 -1
  531. package/src/memory/conversation-bootstrap.ts +11 -0
  532. package/src/memory/conversation-crud.ts +368 -22
  533. package/src/memory/conversation-queries.ts +116 -12
  534. package/src/memory/conversation-title-service.ts +1 -0
  535. package/src/memory/conversation-types.ts +16 -0
  536. package/src/memory/db-init.ts +20 -0
  537. package/src/memory/delivery-crud.ts +152 -5
  538. package/src/memory/embedding-backend.ts +6 -5
  539. package/src/memory/embedding-runtime-manager.ts +1 -2
  540. package/src/memory/external-conversation-store.ts +66 -5
  541. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +66 -9
  542. package/src/memory/graph/__tests__/remember-description.test.ts +55 -0
  543. package/src/memory/graph/conversation-graph-memory.ts +92 -5
  544. package/src/memory/graph/extraction.ts +4 -0
  545. package/src/memory/graph/graph-memory-state-store.ts +16 -3
  546. package/src/memory/graph/tool-handlers.ts +17 -7
  547. package/src/memory/graph/tools.ts +45 -6
  548. package/src/memory/indexer.ts +51 -29
  549. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +86 -15
  550. package/src/memory/jobs/embed-concept-page.ts +65 -20
  551. package/src/memory/jobs-store.ts +51 -1
  552. package/src/memory/jobs-worker.ts +57 -3
  553. package/src/memory/llm-request-log-source-clickhouse.ts +324 -0
  554. package/src/memory/llm-request-log-source-local.ts +26 -0
  555. package/src/memory/llm-request-log-source.ts +64 -0
  556. package/src/memory/llm-request-log-store.ts +1 -1
  557. package/src/memory/llm-usage-store.ts +125 -5
  558. package/src/memory/memory-retrospective-constants.ts +13 -0
  559. package/src/memory/memory-retrospective-enqueue.ts +114 -0
  560. package/src/memory/memory-retrospective-job.ts +351 -0
  561. package/src/memory/memory-retrospective-startup-cleanup.ts +175 -0
  562. package/src/memory/memory-retrospective-state.ts +162 -0
  563. package/src/memory/memory-retrospective-trigger-check.ts +91 -0
  564. package/src/memory/memory-v2-activation-log-store.ts +49 -5
  565. package/src/memory/memory-v2-concept-frequency.ts +4 -0
  566. package/src/memory/message-content.ts +38 -1
  567. package/src/memory/migrations/109-external-conversation-bindings.ts +15 -4
  568. package/src/memory/migrations/227-add-conversation-inference-profile.ts +6 -1
  569. package/src/memory/migrations/228-rename-inference-profile-snake-case.ts +20 -7
  570. package/src/memory/migrations/229-delete-private-conversations.test.ts +107 -1
  571. package/src/memory/migrations/229-delete-private-conversations.ts +19 -0
  572. package/src/memory/migrations/231-repair-memory-graph-event-dates.ts +16 -2
  573. package/src/memory/migrations/240-conversation-inference-profile-session.ts +25 -0
  574. package/src/memory/migrations/241-activation-state-fk-cascade.ts +50 -0
  575. package/src/memory/migrations/242-message-bookmarks.ts +38 -0
  576. package/src/memory/migrations/243-provider-connections.ts +68 -0
  577. package/src/memory/migrations/244-provider-connection-status-label.ts +23 -0
  578. package/src/memory/migrations/245-memory-retrospective-state.ts +36 -0
  579. package/src/memory/migrations/246-backfill-provider-connection-label.ts +81 -0
  580. package/src/memory/migrations/247-external-conversation-binding-thread-id.ts +78 -0
  581. package/src/memory/migrations/248-create-onboarding-events.ts +21 -0
  582. package/src/memory/migrations/249-normalize-slack-external-content.ts +240 -0
  583. package/src/memory/migrations/__tests__/244-provider-connection-status-label.test.ts +84 -0
  584. package/src/memory/migrations/__tests__/245-memory-retrospective-state.test.ts +125 -0
  585. package/src/memory/migrations/__tests__/246-backfill-provider-connection-label.test.ts +192 -0
  586. package/src/memory/migrations/index.ts +13 -0
  587. package/src/memory/migrations/registry.ts +8 -0
  588. package/src/memory/onboarding-events-store.ts +106 -0
  589. package/src/memory/published-pages-store.ts +16 -0
  590. package/src/memory/schema/bookmarks.ts +36 -0
  591. package/src/memory/schema/calls.ts +1 -0
  592. package/src/memory/schema/conversations.ts +2 -0
  593. package/src/memory/schema/index.ts +2 -0
  594. package/src/memory/schema/inference.ts +27 -0
  595. package/src/memory/schema/infrastructure.ts +12 -0
  596. package/src/memory/schema/memory-core.ts +9 -0
  597. package/src/memory/search/semantic.ts +1 -4
  598. package/src/memory/turn-events-store.ts +127 -2
  599. package/src/memory/v2/__tests__/__snapshots__/prompts-router.test.ts.snap +27 -0
  600. package/src/memory/v2/__tests__/activation-store.test.ts +5 -5
  601. package/src/memory/v2/__tests__/activation.test.ts +11 -12
  602. package/src/memory/v2/__tests__/backfill-jobs.test.ts +38 -21
  603. package/src/memory/v2/__tests__/consolidation-job.test.ts +123 -135
  604. package/src/memory/v2/__tests__/edge-index.test.ts +1 -1
  605. package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +111 -0
  606. package/src/memory/v2/__tests__/injection.test.ts +726 -18
  607. package/src/memory/v2/__tests__/migration.test.ts +94 -3
  608. package/src/memory/v2/__tests__/page-index.test.ts +360 -0
  609. package/src/memory/v2/__tests__/page-store.test.ts +14 -1
  610. package/src/memory/v2/__tests__/prompts-router.test.ts +309 -0
  611. package/src/memory/v2/__tests__/qdrant.test.ts +138 -3
  612. package/src/memory/v2/__tests__/reranker.test.ts +4 -4
  613. package/src/memory/v2/__tests__/router.test.ts +531 -0
  614. package/src/memory/v2/__tests__/sim.test.ts +45 -1
  615. package/src/memory/v2/__tests__/skill-store.test.ts +445 -11
  616. package/src/memory/v2/__tests__/static-context.test.ts +7 -22
  617. package/src/memory/v2/__tests__/sweep-job.test.ts +95 -0
  618. package/src/memory/v2/activation-store.ts +34 -5
  619. package/src/memory/v2/activation.ts +40 -27
  620. package/src/memory/v2/backfill-jobs.ts +17 -84
  621. package/src/memory/v2/consolidation-job.ts +85 -78
  622. package/src/memory/v2/frontmatter-sweep.ts +91 -0
  623. package/src/memory/v2/injection.ts +466 -109
  624. package/src/memory/v2/migration.ts +147 -20
  625. package/src/memory/v2/page-index.ts +221 -0
  626. package/src/memory/v2/page-store.ts +3 -0
  627. package/src/memory/v2/prompts/consolidation.ts +9 -7
  628. package/src/memory/v2/prompts/router.ts +195 -0
  629. package/src/memory/v2/prompts/sweep.ts +2 -2
  630. package/src/memory/v2/qdrant.ts +234 -93
  631. package/src/memory/v2/reranker.ts +14 -7
  632. package/src/memory/v2/router.ts +323 -0
  633. package/src/memory/v2/sim.ts +25 -12
  634. package/src/memory/v2/skill-store.ts +204 -30
  635. package/src/memory/v2/static-context.ts +16 -9
  636. package/src/memory/v2/sweep-job.ts +122 -96
  637. package/src/memory/v2/types.ts +10 -6
  638. package/src/memory/validation.ts +13 -0
  639. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +45 -5
  640. package/src/messaging/providers/slack/__tests__/download.test.ts +231 -0
  641. package/src/messaging/providers/slack/adapter.ts +43 -5
  642. package/src/messaging/providers/slack/client.ts +27 -0
  643. package/src/messaging/providers/slack/deep-link.ts +65 -0
  644. package/src/messaging/providers/slack/download.ts +104 -0
  645. package/src/messaging/providers/slack/message-metadata.test.ts +32 -0
  646. package/src/messaging/providers/slack/message-metadata.ts +27 -0
  647. package/src/messaging/providers/slack/render-transcript.test.ts +134 -0
  648. package/src/messaging/providers/slack/render-transcript.ts +69 -5
  649. package/src/messaging/providers/slack/types.ts +20 -1
  650. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +182 -0
  651. package/src/notifications/__tests__/home-feed-side-effect.test.ts +199 -0
  652. package/src/notifications/__tests__/signal-registry.test.ts +17 -0
  653. package/src/notifications/adapters/platform.ts +171 -0
  654. package/src/notifications/conversation-pairing.ts +4 -3
  655. package/src/notifications/copy-composer.ts +15 -0
  656. package/src/notifications/decision-engine.ts +2 -1
  657. package/src/notifications/destination-resolver.ts +21 -0
  658. package/src/notifications/emit-signal.ts +48 -2
  659. package/src/notifications/home-feed-side-effect.ts +165 -0
  660. package/src/notifications/signal.ts +8 -1
  661. package/src/oauth/connection-resolver.ts +8 -4
  662. package/src/oauth/platform-connection.ts +6 -2
  663. package/src/oauth/seed-providers.ts +10 -1
  664. package/src/permissions/checker.ts +14 -0
  665. package/src/permissions/ipc-risk-types.ts +3 -0
  666. package/src/permissions/question-prompter.test.ts +416 -0
  667. package/src/permissions/question-prompter.ts +294 -0
  668. package/src/platform/client.test.ts +1 -1
  669. package/src/platform/client.ts +1 -1
  670. package/src/plugin-api/constants.ts +26 -0
  671. package/src/plugin-api/index.ts +46 -0
  672. package/src/plugin-api/package.json +12 -0
  673. package/src/plugin-api/types.ts +144 -0
  674. package/src/plugins/defaults/circuit-breaker.ts +0 -5
  675. package/src/plugins/defaults/compaction.ts +0 -4
  676. package/src/plugins/defaults/empty-response.ts +0 -2
  677. package/src/plugins/defaults/history-repair.ts +0 -2
  678. package/src/plugins/defaults/injectors.ts +55 -6
  679. package/src/plugins/defaults/llm-call.ts +0 -2
  680. package/src/plugins/defaults/memory-retrieval.ts +0 -1
  681. package/src/plugins/defaults/overflow-reduce.ts +0 -1
  682. package/src/plugins/defaults/persistence.ts +0 -2
  683. package/src/plugins/defaults/title-generate.ts +0 -5
  684. package/src/plugins/defaults/token-estimate.ts +0 -2
  685. package/src/plugins/defaults/tool-error.ts +0 -7
  686. package/src/plugins/defaults/tool-execute.ts +0 -2
  687. package/src/plugins/defaults/tool-result-truncate.ts +0 -4
  688. package/src/plugins/ensure-plugin-api-shim.ts +96 -0
  689. package/src/plugins/external-api.ts +104 -0
  690. package/src/plugins/external-plugin-loader.ts +367 -0
  691. package/src/plugins/feature-gate.ts +22 -0
  692. package/src/plugins/pipeline.ts +37 -0
  693. package/src/plugins/registry.ts +48 -80
  694. package/src/plugins/types.ts +74 -53
  695. package/src/plugins/user-loader.ts +85 -43
  696. package/src/proactive-artifact/aux-message-injector.ts +11 -0
  697. package/src/proactive-artifact/job.test.ts +49 -9
  698. package/src/proactive-artifact/job.ts +4 -0
  699. package/src/proactive-artifact/trigger-state.test.ts +9 -0
  700. package/src/proactive-artifact/trigger-state.ts +4 -0
  701. package/src/prompts/__tests__/system-prompt.test.ts +117 -0
  702. package/src/prompts/__tests__/task-progress-hint-section.test.ts +99 -0
  703. package/src/prompts/normalize-onboarding.ts +27 -0
  704. package/src/prompts/sections.ts +302 -0
  705. package/src/prompts/system-prompt.ts +72 -154
  706. package/src/prompts/templates/BOOTSTRAP.md +17 -1
  707. package/src/prompts/templates/system-sections.ts +173 -0
  708. package/src/prompts/update-bulletin-job.ts +61 -73
  709. package/src/providers/__tests__/dispatch-connection-routing.test.ts +279 -0
  710. package/src/providers/__tests__/inference.test.ts +303 -0
  711. package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
  712. package/src/providers/__tests__/provider-secret-catalog.test.ts +6 -0
  713. package/src/providers/__tests__/retry-callsite.test.ts +14 -32
  714. package/src/providers/__tests__/satellite-connection-routing.test.ts +510 -0
  715. package/src/providers/__tests__/search-provider-catalog.test.ts +80 -0
  716. package/src/providers/anthropic/client.ts +123 -54
  717. package/src/providers/call-site-routing.ts +94 -16
  718. package/src/providers/connection-resolution.ts +170 -0
  719. package/src/providers/inference/__tests__/connections-status-label.test.ts +250 -0
  720. package/src/providers/inference/adapter-factory.ts +210 -0
  721. package/src/providers/inference/auth.ts +112 -0
  722. package/src/providers/inference/backfill.ts +196 -0
  723. package/src/providers/inference/connections.ts +401 -0
  724. package/src/providers/inference/resolve-auth.ts +73 -0
  725. package/src/providers/model-catalog.ts +386 -6
  726. package/src/providers/openai/chat-completions-provider.ts +10 -2
  727. package/src/providers/openai/responses-provider.ts +4 -2
  728. package/src/providers/openrouter/client.ts +7 -0
  729. package/src/providers/{managed-proxy → platform-proxy}/constants.ts +4 -1
  730. package/src/providers/{managed-proxy → platform-proxy}/context.ts +3 -3
  731. package/src/providers/provider-availability.ts +17 -2
  732. package/src/providers/provider-catalog-visibility.ts +36 -0
  733. package/src/providers/provider-env-vars.ts +17 -7
  734. package/src/providers/provider-secret-catalog.ts +49 -30
  735. package/src/providers/provider-send-message.ts +41 -20
  736. package/src/providers/registry.ts +151 -159
  737. package/src/providers/retry.ts +65 -11
  738. package/src/providers/search-provider-catalog.ts +121 -0
  739. package/src/runtime/AGENTS.md +18 -5
  740. package/src/runtime/__tests__/agent-wake.test.ts +152 -0
  741. package/src/runtime/__tests__/background-job-runner.test.ts +357 -0
  742. package/src/runtime/__tests__/pre-first-message-gate.test.ts +82 -0
  743. package/src/runtime/actor-trust-resolver.ts +32 -10
  744. package/src/runtime/agent-wake.ts +64 -7
  745. package/src/runtime/assistant-event-hub.ts +3 -85
  746. package/src/runtime/auth/route-policy.ts +311 -9
  747. package/src/runtime/auth/same-actor.ts +2 -0
  748. package/src/runtime/background-job-runner.ts +339 -0
  749. package/src/runtime/btw-sidechain.ts +3 -0
  750. package/src/runtime/http-router.ts +36 -1
  751. package/src/runtime/http-server.ts +31 -5
  752. package/src/runtime/http-types.ts +21 -0
  753. package/src/runtime/middleware/__tests__/request-logger.test.ts +162 -0
  754. package/src/runtime/middleware/request-logger.ts +62 -1
  755. package/src/runtime/migrations/origin-mode.ts +1 -1
  756. package/src/runtime/pending-interactions.ts +1 -0
  757. package/src/runtime/pre-first-message-gate.ts +83 -0
  758. package/src/runtime/routes/__tests__/backup-routes.test.ts +8 -1
  759. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +268 -0
  760. package/src/runtime/routes/__tests__/connection-routes-vs-cli-parity.test.ts +142 -0
  761. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +319 -0
  762. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +280 -4
  763. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +15 -136
  764. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +736 -0
  765. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +4 -4
  766. package/src/runtime/routes/__tests__/question-routes.test.ts +395 -0
  767. package/src/runtime/routes/__tests__/stt-routes.test.ts +5 -1
  768. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +384 -0
  769. package/src/runtime/routes/__tests__/tts-routes.test.ts +70 -3
  770. package/src/runtime/routes/acp-routes-list.test.ts +143 -0
  771. package/src/runtime/routes/acp-routes.ts +12 -8
  772. package/src/runtime/routes/app-management-routes.ts +228 -3
  773. package/src/runtime/routes/approval-routes.ts +0 -18
  774. package/src/runtime/routes/audit-routes.ts +43 -0
  775. package/src/runtime/routes/auth-routes.ts +72 -0
  776. package/src/runtime/routes/avatar-routes.ts +273 -20
  777. package/src/runtime/routes/backup-routes.ts +406 -2
  778. package/src/runtime/routes/bookmark-routes.ts +156 -0
  779. package/src/runtime/routes/btw-routes.ts +5 -1
  780. package/src/runtime/routes/channel-availability-routes.ts +121 -0
  781. package/src/runtime/routes/channel-verification-routes.ts +2 -1
  782. package/src/runtime/routes/contact-routes.ts +0 -160
  783. package/src/runtime/routes/conversation-cli-routes.ts +233 -0
  784. package/src/runtime/routes/conversation-list-routes.ts +3 -20
  785. package/src/runtime/routes/conversation-management-routes.ts +47 -85
  786. package/src/runtime/routes/conversation-query-routes.ts +350 -97
  787. package/src/runtime/routes/conversation-routes.ts +121 -21
  788. package/src/runtime/routes/conversations-import-routes.ts +229 -0
  789. package/src/runtime/routes/credential-routes.ts +540 -0
  790. package/src/runtime/routes/debug-routes.ts +2 -2
  791. package/src/runtime/routes/document-pdf-renderer.ts +5 -1
  792. package/src/runtime/routes/documents-routes.ts +25 -86
  793. package/src/runtime/routes/domain-routes.ts +167 -0
  794. package/src/runtime/routes/email-routes.ts +603 -0
  795. package/src/runtime/routes/errors.ts +2 -2
  796. package/src/runtime/routes/events-routes.ts +192 -0
  797. package/src/runtime/routes/group-routes.ts +5 -0
  798. package/src/runtime/routes/home-feed-routes.ts +6 -78
  799. package/src/runtime/routes/host-app-control-routes.ts +44 -2
  800. package/src/runtime/routes/host-browser-routes.ts +103 -22
  801. package/src/runtime/routes/http-adapter.ts +2 -0
  802. package/src/runtime/routes/identity-routes.ts +5 -0
  803. package/src/runtime/routes/image-generation-routes.ts +99 -0
  804. package/src/runtime/routes/inbound-conversation.ts +28 -8
  805. package/src/runtime/routes/inbound-message-handler.ts +236 -41
  806. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +248 -1
  807. package/src/runtime/routes/inbound-stages/background-dispatch.ts +118 -7
  808. package/src/runtime/routes/inbound-stages/edit-intercept.ts +17 -4
  809. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +156 -0
  810. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +22 -4
  811. package/src/runtime/routes/index.ts +42 -0
  812. package/src/runtime/routes/inference-profile-session-handler.ts +285 -0
  813. package/src/runtime/routes/inference-profile-session-reaper.ts +84 -0
  814. package/src/runtime/routes/inference-profile-session-routes.ts +146 -0
  815. package/src/runtime/routes/inference-provider-connection-routes.ts +361 -0
  816. package/src/runtime/routes/inference-send-routes.ts +115 -0
  817. package/src/runtime/routes/integrations/slack/share.ts +4 -52
  818. package/src/runtime/routes/integrations/slack/token.ts +43 -0
  819. package/src/runtime/routes/integrations/twilio.ts +7 -13
  820. package/src/runtime/routes/mcp-auth-routes.ts +283 -9
  821. package/src/runtime/routes/memory-v2-routes.ts +13 -398
  822. package/src/runtime/routes/notification-routes.ts +3 -1
  823. package/src/runtime/routes/oauth-apps.ts +112 -7
  824. package/src/runtime/routes/oauth-commands-routes.ts +1097 -0
  825. package/src/runtime/routes/oauth-connect-routes.ts +67 -5
  826. package/src/runtime/routes/oauth-lifecycle-routes.ts +43 -0
  827. package/src/runtime/routes/oauth-providers.ts +298 -8
  828. package/src/runtime/routes/platform-routes.ts +336 -0
  829. package/src/runtime/routes/playground/inject-failures.ts +2 -1
  830. package/src/runtime/routes/playground/reset-circuit.ts +2 -1
  831. package/src/runtime/routes/playground/state.ts +2 -1
  832. package/src/runtime/routes/publish-routes.ts +221 -0
  833. package/src/runtime/routes/question-routes.ts +259 -0
  834. package/src/runtime/routes/rename-conversation-routes.ts +2 -33
  835. package/src/runtime/routes/schedule-routes.ts +79 -0
  836. package/src/runtime/routes/sequence-routes.ts +291 -0
  837. package/src/runtime/routes/settings-routes.ts +2 -10
  838. package/src/runtime/routes/skills-routes.ts +31 -1
  839. package/src/runtime/routes/stt-routes.ts +240 -3
  840. package/src/runtime/routes/subagents-routes.ts +57 -18
  841. package/src/runtime/routes/surface-action-routes.ts +43 -7
  842. package/src/runtime/routes/telemetry-routes.ts +27 -0
  843. package/src/runtime/routes/tts-routes.ts +93 -1
  844. package/src/runtime/routes/types.ts +32 -0
  845. package/src/runtime/routes/user-routes-cli.ts +243 -0
  846. package/src/runtime/routes/webhook-routes.ts +165 -0
  847. package/src/runtime/routes/workspace-routes.test.ts +43 -0
  848. package/src/runtime/routes/workspace-routes.ts +28 -0
  849. package/src/runtime/services/conversation-serializer.ts +39 -7
  850. package/src/runtime/sync/resource-sync-events.ts +117 -0
  851. package/src/runtime/sync/sync-publisher.test.ts +105 -0
  852. package/src/runtime/sync/sync-publisher.ts +21 -0
  853. package/src/schedule/schedule-store.ts +27 -2
  854. package/src/schedule/scheduler.ts +208 -123
  855. package/src/security/__tests__/provider-key-env-fallback.test.ts +12 -6
  856. package/src/security/__tests__/untrusted-content.test.ts +86 -0
  857. package/src/security/secret-patterns.ts +3 -0
  858. package/src/security/untrusted-content.ts +93 -8
  859. package/src/sequence/engine.ts +38 -40
  860. package/src/skills/catalog-files.ts +1 -1
  861. package/src/skills/catalog-install.ts +233 -116
  862. package/src/skills/clawhub.ts +70 -13
  863. package/src/skills/managed-store.ts +4 -119
  864. package/src/skills/skillssh-registry.ts +27 -48
  865. package/src/subagent/manager.ts +28 -15
  866. package/src/telemetry/types.ts +113 -1
  867. package/src/telemetry/usage-telemetry-reporter.test.ts +312 -5
  868. package/src/telemetry/usage-telemetry-reporter.ts +113 -7
  869. package/src/tools/apps/executors.ts +58 -7
  870. package/src/tools/ask-question/ask-question-tool.test.ts +509 -0
  871. package/src/tools/ask-question/ask-question-tool.ts +304 -0
  872. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +206 -0
  873. package/src/tools/browser/browser-execution.ts +29 -14
  874. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +174 -0
  875. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +16 -13
  876. package/src/tools/browser/cdp-client/extension-cdp-client.ts +24 -1
  877. package/src/tools/browser/cdp-client/factory.ts +66 -5
  878. package/src/tools/browser/runtime-check.ts +77 -0
  879. package/src/tools/computer-use/definitions.ts +3 -3
  880. package/src/tools/credentials/vault.ts +1 -1
  881. package/src/tools/document/document-tool.ts +124 -1
  882. package/src/tools/filesystem/edit.ts +1 -1
  883. package/src/tools/filesystem/list.ts +1 -1
  884. package/src/tools/filesystem/read.ts +1 -1
  885. package/src/tools/filesystem/write.ts +5 -2
  886. package/src/tools/host-filesystem/transfer.ts +1 -1
  887. package/src/tools/host-terminal/host-shell.ts +1 -1
  888. package/src/tools/memory/register.test.ts +3 -3
  889. package/src/tools/memory/register.ts +9 -1
  890. package/src/tools/network/__tests__/web-search.test.ts +156 -0
  891. package/src/tools/network/web-search.ts +280 -37
  892. package/src/tools/permission-checker.ts +14 -6
  893. package/src/tools/registry.ts +17 -7
  894. package/src/tools/schedule/create.ts +2 -2
  895. package/src/tools/schema-transforms.ts +7 -2
  896. package/src/tools/side-effects.ts +1 -0
  897. package/src/tools/skills/delete-managed.ts +4 -4
  898. package/src/tools/skills/execute.ts +1 -1
  899. package/src/tools/skills/scaffold-managed.ts +3 -2
  900. package/src/tools/subagent/notify-parent.ts +1 -1
  901. package/src/tools/subagent/spawn.ts +3 -3
  902. package/src/tools/system/request-permission.ts +2 -2
  903. package/src/tools/terminal/safe-env.ts +60 -1
  904. package/src/tools/terminal/shell.ts +44 -0
  905. package/src/tools/tool-manifest.ts +2 -0
  906. package/src/tools/types.ts +72 -21
  907. package/src/tools/ui-surface/definitions.ts +6 -5
  908. package/src/tts/__tests__/provider-adapters.test.ts +76 -2
  909. package/src/tts/providers/elevenlabs-provider.ts +75 -1
  910. package/src/types/onboarding-context.ts +2 -0
  911. package/src/usage/attribution.ts +3 -2
  912. package/src/util/errors.ts +17 -0
  913. package/src/util/platform.ts +10 -0
  914. package/src/util/pricing.ts +86 -160
  915. package/src/watcher/__tests__/engine.test.ts +323 -0
  916. package/src/watcher/constants.ts +7 -0
  917. package/src/watcher/engine.ts +94 -90
  918. package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +6 -9
  919. package/src/workspace/migrations/054-seed-recall-callsite.ts +10 -1
  920. package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +94 -5
  921. package/src/workspace/migrations/069-seed-onboarding-threads.ts +8 -2
  922. package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +117 -0
  923. package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +95 -0
  924. package/src/workspace/migrations/074-drop-deprecated-secret-detection-keys.ts +117 -0
  925. package/src/workspace/migrations/075-memory-v2-bm25-b-default-reembed.ts +61 -0
  926. package/src/workspace/migrations/076-drop-services-inference-mode.ts +62 -0
  927. package/src/workspace/migrations/077-seed-memory-router-callsite.ts +89 -0
  928. package/src/workspace/migrations/078-release-notes-tavily-web-search.ts +66 -0
  929. package/src/workspace/migrations/079-home-feed-notification-only.ts +197 -0
  930. package/src/workspace/migrations/080-restrict-vercel-api-token-metadata.ts +182 -0
  931. package/src/workspace/migrations/081-backfill-bash-allowed-tools-for-injection-credentials.ts +160 -0
  932. package/src/workspace/migrations/082-backfill-managed-profile-labels.ts +154 -0
  933. package/src/workspace/migrations/083-system-prompt-prefix-to-file.ts +191 -0
  934. package/src/workspace/migrations/084-remove-legacy-skills-index.ts +276 -0
  935. package/src/workspace/migrations/085-memory-v2-bm25-b-reembed-disabled-v2-pages.ts +137 -0
  936. package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +198 -0
  937. package/src/workspace/migrations/registry.ts +30 -0
  938. package/src/workspace/migrations/runner.ts +46 -5
  939. package/src/workspace/migrations/types.ts +17 -3
  940. package/src/workspace/provider-commit-message-generator.ts +3 -2
  941. package/examples/plugins/echo/bun.lock +0 -25
  942. package/src/__tests__/context-search-pkb-source.test.ts +0 -498
  943. package/src/__tests__/context-window-manager.test.ts +0 -2093
  944. package/src/__tests__/credentials-cli.test.ts +0 -1225
  945. package/src/__tests__/memory-admin-recall.test.ts +0 -213
  946. package/src/approvals/__tests__/guardian-feed-event.test.ts +0 -303
  947. package/src/cli/commands/__tests__/email-download.test.ts +0 -260
  948. package/src/cli/commands/__tests__/email-list.test.ts +0 -216
  949. package/src/cli/commands/__tests__/email-register.test.ts +0 -186
  950. package/src/cli/commands/__tests__/email-send.test.ts +0 -416
  951. package/src/cli/commands/__tests__/email-status.test.ts +0 -185
  952. package/src/cli/commands/__tests__/email-unregister.test.ts +0 -168
  953. package/src/cli/commands/__tests__/routes.test.ts +0 -562
  954. package/src/cli/commands/__tests__/stt-transcribe.test.ts +0 -454
  955. package/src/cli/commands/autonomy.ts +0 -365
  956. package/src/cli/commands/memory.ts +0 -424
  957. package/src/cli/commands/oauth/__tests__/connect.test.ts +0 -947
  958. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +0 -686
  959. package/src/cli/commands/oauth/__tests__/mode.test.ts +0 -632
  960. package/src/cli/commands/oauth/__tests__/ping.test.ts +0 -631
  961. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +0 -573
  962. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +0 -330
  963. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +0 -521
  964. package/src/cli/commands/oauth/__tests__/status.test.ts +0 -551
  965. package/src/cli/commands/oauth/__tests__/token.test.ts +0 -420
  966. package/src/cli/lib/daemon-avatar-client.ts +0 -37
  967. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -87
  968. package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +0 -207
  969. package/src/context/__tests__/compact-prompt.test.ts +0 -63
  970. package/src/context/prompts/compact.md +0 -26
  971. package/src/daemon/__tests__/conversation-feed-event.test.ts +0 -304
  972. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +0 -233
  973. package/src/home/__tests__/assistant-feed-authoring.test.ts +0 -156
  974. package/src/home/__tests__/emit-feed-event.test.ts +0 -169
  975. package/src/home/__tests__/feed-population-integration.test.ts +0 -312
  976. package/src/home/__tests__/feed-scheduler.test.ts +0 -222
  977. package/src/home/__tests__/phase5-exit-criteria.test.ts +0 -229
  978. package/src/home/__tests__/platform-gmail-digest.test.ts +0 -222
  979. package/src/home/__tests__/rollup-producer.test.ts +0 -507
  980. package/src/home/assistant-feed-authoring.ts +0 -135
  981. package/src/home/emit-feed-event.ts +0 -169
  982. package/src/home/feed-scheduler.ts +0 -281
  983. package/src/home/platform-gmail-digest.ts +0 -163
  984. package/src/home/rewrite-command-preview.ts +0 -66
  985. package/src/home/rewrite-feed-title.ts +0 -58
  986. package/src/home/rollup-producer.ts +0 -426
  987. package/src/memory/admin.ts +0 -326
  988. package/src/memory/context-search/sources/pkb.ts +0 -476
  989. package/src/memory/graph/compaction.ts +0 -299
  990. package/src/prompts/__tests__/build-cli-reference-section.test.ts +0 -37
  991. /package/src/cli/{commands → lib}/cache-fs.ts +0 -0
@@ -0,0 +1,1047 @@
1
+ /**
2
+ * Assistant-driven context compaction.
3
+ *
4
+ * When a conversation grows long, we hand the model the entire conversation
5
+ * plus an appended instruction message and let it write its own summary,
6
+ * choose its own cut point, and decide which images to retain.
7
+ *
8
+ * The instruction message is appended as a `user`-role message at the tail
9
+ * so the full conversation prefix (system prompt + tools + messages) remains
10
+ * cacheable across compaction calls.
11
+ *
12
+ * The model responds with a `<compaction_result>` XML block. We parse it,
13
+ * resolve `tail_start` to a message index, re-attach any retained images,
14
+ * and rebuild the conversation as:
15
+ *
16
+ * [<assistant summary>, <retained-image user message?>, ...tail]
17
+ *
18
+ * On any parse or resolution failure we abort the compaction and return
19
+ * `compacted: false` — never silently lose messages.
20
+ */
21
+ import { optimizeImageForTransport } from "../agent/image-optimize.js";
22
+ import type { CompactionConfig } from "../config/schemas/compaction.js";
23
+ import type { LLMCallSite } from "../config/schemas/llm.js";
24
+ import { stripInjectionsForCompaction } from "../daemon/conversation-runtime-assembly.js";
25
+ import {
26
+ getAttachmentContent,
27
+ getAttachmentMetadataForMessage,
28
+ } from "../memory/attachments-store.js";
29
+ import { getMessages } from "../memory/conversation-crud.js";
30
+ import type {
31
+ ContentBlock,
32
+ ImageContent,
33
+ Message,
34
+ Provider,
35
+ ProviderResponse,
36
+ ToolDefinition,
37
+ } from "../providers/types.js";
38
+ import { getLogger } from "../util/logger.js";
39
+ import { estimatePromptTokens } from "./token-estimator.js";
40
+
41
+ const log = getLogger("compactor");
42
+
43
+ /**
44
+ * Stable call-site identifier used when invoking the provider for a
45
+ * compaction pass. Using `mainAgent` (rather than a dedicated
46
+ * `conversationCompaction` site) keeps the resolved provider/model/system
47
+ * prompt/tools identical to the agent's last turn, so the prefix cache hit
48
+ * rate is maximized — the compaction-instruction user message is the only
49
+ * new token sequence.
50
+ */
51
+ const COMPACTION_CALL_SITE: LLMCallSite = "mainAgent";
52
+
53
+ const RESULT_TAG_OPEN = "<compaction_result>";
54
+ const RESULT_TAG_CLOSE = "</compaction_result>";
55
+
56
+ /**
57
+ * Generic compaction instruction. Used when `compaction.prompt` is unset.
58
+ *
59
+ * `{image_manifest}` is the only interpolation point. Custom prompts in
60
+ * `config.json` go through the same interpolation, so override authors can
61
+ * place the placeholder wherever they want.
62
+ */
63
+ export const DEFAULT_COMPACTION_PROMPT = `<compaction_instructions>
64
+ This conversation is getting long. It's time to run a compaction pass.
65
+
66
+ You have the full conversation in your context right now. Your job is to
67
+ compress the older parts into a summary while preserving recent messages
68
+ exactly as they are.
69
+
70
+ Write the summary in YOUR voice — as if you're remembering this conversation,
71
+ not writing meeting notes about it. Prioritize:
72
+ - Decisions made and commitments given
73
+ - Key context that's still relevant going forward
74
+ - Emotional moments that shaped the conversation's direction
75
+ - Exact quotes when the specific wording matters
76
+ - Project/task state changes
77
+
78
+ Compress aggressively:
79
+ - Repeated debugging or troubleshooting attempts → just the outcome
80
+ - Tool call outputs → results only, not raw data
81
+ - Intermediate states superseded by later states
82
+ - Back-and-forth deliberation → just the conclusion
83
+
84
+ For picking where to cut between summary and preserved tail:
85
+ - Find the last major topic shift or energy change
86
+ - Keep the active thread of conversation fully intact
87
+ - When in doubt, preserve more rather than less
88
+ - Never cut in the middle of an ongoing discussion
89
+
90
+ IMAGE MANIFEST (images in this conversation):
91
+ {image_manifest}
92
+
93
+ If any images from the summarized portion are still relevant to the
94
+ ongoing conversation, include them in retained_images by filename.
95
+
96
+ Output your result in this exact format:
97
+
98
+ <compaction_result>
99
+ <summary>
100
+ Your summary in your voice. Aim for 2000-4000 tokens — rich enough
101
+ to preserve what matters, compact enough to free real space.
102
+ </summary>
103
+
104
+ <key_state>
105
+ Short structured list of anything PENDING from this conversation:
106
+ active decisions, open questions, commitments, project states.
107
+ </key_state>
108
+
109
+ <retained_images>
110
+ <image file="filename.ext" />
111
+ (only images from BEFORE the tail that are still contextually important)
112
+ (omit this section entirely if no images need retention)
113
+ </retained_images>
114
+
115
+ <tail_start
116
+ timestamp="[exact timestamp from the turn_context of the first message to preserve verbatim]"
117
+ preview="[first ~60 characters of that message for verification]" />
118
+ </compaction_result>
119
+ </compaction_instructions>`;
120
+
121
+ // ---------------------------------------------------------------------------
122
+ // Public types
123
+ // ---------------------------------------------------------------------------
124
+
125
+ export interface CompactionRunArgs {
126
+ conversationId: string;
127
+ messages: Message[];
128
+ provider: Provider;
129
+ systemPrompt: string;
130
+ tools?: ToolDefinition[];
131
+ compaction: CompactionConfig;
132
+ /** Effective context window for the conversation (in tokens). */
133
+ maxInputTokens: number;
134
+ /** Pre-computed estimated input tokens for the live history. */
135
+ previousEstimatedInputTokens: number;
136
+ /** Skip the autoThreshold check — fire compaction unconditionally. */
137
+ force?: boolean;
138
+ signal?: AbortSignal;
139
+ overrideProfile?: string | null;
140
+ /**
141
+ * Number of leading non-persisted messages (e.g. inherited summary from a
142
+ * parent fork). Compacted-persisted-count subtracts this so the DB
143
+ * `contextCompactedMessageCount` only advances by rows that actually have
144
+ * DB counterparts.
145
+ */
146
+ nonPersistedPrefixCount?: number;
147
+ }
148
+
149
+ export interface CompactionRunResult {
150
+ messages: Message[];
151
+ compacted: boolean;
152
+ previousEstimatedInputTokens: number;
153
+ estimatedInputTokens: number;
154
+ maxInputTokens: number;
155
+ thresholdTokens: number;
156
+ compactedMessages: number;
157
+ compactedPersistedMessages: number;
158
+ summaryCalls: number;
159
+ summaryInputTokens: number;
160
+ summaryOutputTokens: number;
161
+ summaryModel: string;
162
+ summaryCallSite?: LLMCallSite;
163
+ summaryOverrideProfile?: string | null;
164
+ summaryCacheCreationInputTokens?: number;
165
+ summaryCacheReadInputTokens?: number;
166
+ summaryRawResponses?: unknown[];
167
+ summaryText: string;
168
+ /** Inline structured pending state from the model's `<key_state>` block. */
169
+ keyState?: string;
170
+ reason?: string;
171
+ /** True when the provider call threw and no compaction was applied. */
172
+ summaryFailed?: boolean;
173
+ }
174
+
175
+ export interface ParsedCompactionResult {
176
+ summary: string;
177
+ keyState: string;
178
+ retainedImageFilenames: string[];
179
+ tailStartTimestamp: string;
180
+ tailStartPreview: string;
181
+ }
182
+
183
+ // ---------------------------------------------------------------------------
184
+ // XML parser
185
+ // ---------------------------------------------------------------------------
186
+
187
+ /**
188
+ * Extract the `<compaction_result>` block from raw assistant output and
189
+ * pull out `<summary>`, `<key_state>`, `<retained_images>` (filenames),
190
+ * and `<tail_start>` (timestamp + preview).
191
+ *
192
+ * Lenient by design — the model may wrap the block in narration, may omit
193
+ * `<retained_images>`, and may produce slightly malformed inner tags. We
194
+ * accept any of those. Returns `null` only when the required fields
195
+ * (summary + tail_start.timestamp) are missing.
196
+ */
197
+ export function parseCompactionResult(
198
+ raw: string,
199
+ ): ParsedCompactionResult | null {
200
+ const openIdx = raw.indexOf(RESULT_TAG_OPEN);
201
+ if (openIdx < 0) return null;
202
+ const closeIdx = raw.lastIndexOf(RESULT_TAG_CLOSE);
203
+ const inner =
204
+ closeIdx > openIdx
205
+ ? raw.slice(openIdx + RESULT_TAG_OPEN.length, closeIdx)
206
+ : raw.slice(openIdx + RESULT_TAG_OPEN.length);
207
+
208
+ const summary = extractTagContent(inner, "summary")?.trim() ?? "";
209
+ if (summary.length === 0) return null;
210
+
211
+ const keyState = extractTagContent(inner, "key_state")?.trim() ?? "";
212
+
213
+ const tail = extractTailStart(inner);
214
+ if (!tail || tail.timestamp.length === 0) return null;
215
+
216
+ const retainedImageFilenames = extractRetainedImages(inner);
217
+
218
+ return {
219
+ summary,
220
+ keyState,
221
+ retainedImageFilenames,
222
+ tailStartTimestamp: tail.timestamp,
223
+ tailStartPreview: tail.preview,
224
+ };
225
+ }
226
+
227
+ function extractTagContent(haystack: string, tag: string): string | null {
228
+ const open = `<${tag}>`;
229
+ const close = `</${tag}>`;
230
+ const openIdx = haystack.indexOf(open);
231
+ if (openIdx < 0) return null;
232
+ const closeIdx = haystack.indexOf(close, openIdx + open.length);
233
+ if (closeIdx < 0) return null;
234
+ return haystack.slice(openIdx + open.length, closeIdx);
235
+ }
236
+
237
+ function extractTailStart(
238
+ inner: string,
239
+ ): { timestamp: string; preview: string } | null {
240
+ // Match `<tail_start ... />` or `<tail_start ...></tail_start>` with
241
+ // attributes in any order. We require at least `timestamp="..."`.
242
+ const tagMatch = inner.match(
243
+ /<tail_start\b([\s\S]*?)(?:\/>|<\/tail_start>)/i,
244
+ );
245
+ if (!tagMatch) return null;
246
+ const attrs = tagMatch[1];
247
+ const timestamp = extractAttr(attrs, "timestamp") ?? "";
248
+ const preview = extractAttr(attrs, "preview") ?? "";
249
+ return { timestamp, preview };
250
+ }
251
+
252
+ function extractAttr(attrs: string, name: string): string | null {
253
+ const pattern = new RegExp(`${name}\\s*=\\s*"([^"]*)"`, "i");
254
+ const m = attrs.match(pattern);
255
+ return m ? m[1] : null;
256
+ }
257
+
258
+ function extractRetainedImages(inner: string): string[] {
259
+ const block = extractTagContent(inner, "retained_images");
260
+ if (block == null) return [];
261
+ const out: string[] = [];
262
+ const seen = new Set<string>();
263
+ const re = /<image\b[^>]*\bfile\s*=\s*"([^"]+)"[^>]*\/?>/gi;
264
+ let m: RegExpExecArray | null;
265
+ while ((m = re.exec(block)) !== null) {
266
+ const name = m[1].trim();
267
+ if (name.length === 0 || seen.has(name)) continue;
268
+ seen.add(name);
269
+ out.push(name);
270
+ }
271
+ return out;
272
+ }
273
+
274
+ // ---------------------------------------------------------------------------
275
+ // Image manifest
276
+ // ---------------------------------------------------------------------------
277
+
278
+ interface ManifestEntry {
279
+ filename: string;
280
+ attachmentId: string;
281
+ role: "user" | "assistant" | string;
282
+ timestamp: number;
283
+ }
284
+
285
+ /**
286
+ * Walk the DB rows for the conversation and build an entry per image
287
+ * attachment. Returns entries sorted by message createdAt ascending. The
288
+ * `filename` is the attachment's `originalFilename`; collisions across
289
+ * messages are kept as separate entries (the model can disambiguate via
290
+ * the timestamp it sees in the manifest).
291
+ */
292
+ export function collectImageManifest(conversationId: string): ManifestEntry[] {
293
+ const rows = getMessages(conversationId);
294
+ const entries: ManifestEntry[] = [];
295
+ for (const row of rows) {
296
+ const atts = getAttachmentMetadataForMessage(row.id);
297
+ for (const att of atts) {
298
+ if (att.kind !== "image") continue;
299
+ entries.push({
300
+ filename: att.originalFilename,
301
+ attachmentId: att.id,
302
+ role: row.role,
303
+ timestamp: row.createdAt,
304
+ });
305
+ }
306
+ }
307
+ return entries;
308
+ }
309
+
310
+ export function renderImageManifest(entries: ManifestEntry[]): string {
311
+ if (entries.length === 0) return "(no images in this conversation)";
312
+ return entries
313
+ .map((e) => {
314
+ const ts = new Date(e.timestamp).toISOString();
315
+ return `- ${e.filename} (${e.role} message at ${ts})`;
316
+ })
317
+ .join("\n");
318
+ }
319
+
320
+ // ---------------------------------------------------------------------------
321
+ // Timestamp extraction from live messages
322
+ // ---------------------------------------------------------------------------
323
+
324
+ /**
325
+ * Extract the `current_time:` value from a user message's `<turn_context>`
326
+ * block, if present. Returns the raw timestamp string (whatever format the
327
+ * runtime emitted — typically
328
+ * `2026-04-02 (Thursday) 01:52:33 -05:00 (America/Chicago)`).
329
+ */
330
+ function extractTurnContextTimestamp(message: Message): string | null {
331
+ if (message.role !== "user") return null;
332
+ for (const block of message.content) {
333
+ if (block.type !== "text") continue;
334
+ const text = block.text;
335
+ const idx = text.indexOf("<turn_context>");
336
+ if (idx < 0) continue;
337
+ const end = text.indexOf("</turn_context>", idx);
338
+ const slice = end > 0 ? text.slice(idx, end) : text.slice(idx);
339
+ const m = slice.match(/current_time:\s*([^\n]+)/);
340
+ if (m) return m[1].trim();
341
+ }
342
+ return null;
343
+ }
344
+
345
+ /**
346
+ * Build a parallel array of timestamp strings — one per message — used to
347
+ * resolve the model's `tail_start.timestamp` back to a message index.
348
+ *
349
+ * Assistant and tool-result-only messages get `null` (they have no
350
+ * turn_context); the resolver walks forward from the matched index to
351
+ * include the surrounding user→assistant cluster.
352
+ */
353
+ function buildTimestampIndex(messages: Message[]): (string | null)[] {
354
+ return messages.map((m) => extractTurnContextTimestamp(m));
355
+ }
356
+
357
+ function extractFirstTextPreview(message: Message, maxChars = 120): string {
358
+ for (const block of message.content) {
359
+ if (block.type !== "text") continue;
360
+ let text = block.text;
361
+ // Skip injected blocks (`<turn_context>`, `<memory>`, `<workspace>`, ...) —
362
+ // they're not what the model means by "first 60 chars of that message".
363
+ while (text.startsWith("<") && text.includes("</")) {
364
+ const closeMatch = text.match(/<\/[a-zA-Z_][\w-]*>\s*\n?/);
365
+ if (!closeMatch || closeMatch.index === undefined) break;
366
+ text = text.slice(closeMatch.index + closeMatch[0].length).trimStart();
367
+ }
368
+ if (text.length === 0) continue;
369
+ return text.slice(0, maxChars);
370
+ }
371
+ return "";
372
+ }
373
+
374
+ // ---------------------------------------------------------------------------
375
+ // Tail resolution
376
+ // ---------------------------------------------------------------------------
377
+
378
+ /**
379
+ * Reduce a timestamp string to a canonical `YYYY-MM-DDTHH:MM:SS` form by
380
+ * extracting the date and time components and discarding everything else
381
+ * (weekday names, parens, timezone offsets, timezone names, separators).
382
+ *
383
+ * The stored format is e.g. `2026-04-02 (Thursday) 01:52:33 -05:00 (America/Chicago)`.
384
+ * Models routinely paraphrase this — dropping the weekday, dropping the
385
+ * timezone, switching to ISO-8601 with a `T` separator. As long as the
386
+ * model's emission contains a date and a time, both reduce to the same
387
+ * canonical key and we can match.
388
+ *
389
+ * Returns null when no date+time pair is detected.
390
+ */
391
+ export function canonicalDateTimeKey(ts: string): string | null {
392
+ const m = ts.match(/(\d{4}-\d{2}-\d{2})\D+(\d{2}:\d{2}:\d{2})/);
393
+ if (!m) return null;
394
+ return `${m[1]}T${m[2]}`;
395
+ }
396
+
397
+ /**
398
+ * Resolve the model's `tail_start` reference to an index in the live
399
+ * messages array. Match priority:
400
+ * 1. Exact timestamp match against a `<turn_context>` `current_time:` line
401
+ * 2. Substring match (model may emit a shortened or re-formatted ts)
402
+ * 3. Canonical date+time match (tolerant of weekday/timezone paraphrasing)
403
+ * 4. Preview-text fallback — locate the message whose first non-injection
404
+ * text starts with the preview string
405
+ */
406
+ function resolveTailStartIndex(
407
+ messages: Message[],
408
+ timestamps: (string | null)[],
409
+ parsed: ParsedCompactionResult,
410
+ ): number | null {
411
+ const wantedTs = parsed.tailStartTimestamp.trim();
412
+ if (wantedTs.length > 0) {
413
+ for (let i = 0; i < timestamps.length; i++) {
414
+ if (timestamps[i] === wantedTs) return i;
415
+ }
416
+ for (let i = 0; i < timestamps.length; i++) {
417
+ const ts = timestamps[i];
418
+ if (ts && (ts.includes(wantedTs) || wantedTs.includes(ts))) return i;
419
+ }
420
+ const wantedKey = canonicalDateTimeKey(wantedTs);
421
+ if (wantedKey) {
422
+ for (let i = 0; i < timestamps.length; i++) {
423
+ const ts = timestamps[i];
424
+ if (!ts) continue;
425
+ if (canonicalDateTimeKey(ts) === wantedKey) return i;
426
+ }
427
+ }
428
+ }
429
+ const wantedPreview = parsed.tailStartPreview.trim();
430
+ if (wantedPreview.length > 0) {
431
+ const previewHead = wantedPreview.slice(0, 40);
432
+ for (let i = 0; i < messages.length; i++) {
433
+ const m = messages[i];
434
+ if (m.role !== "user") continue;
435
+ const head = extractFirstTextPreview(m);
436
+ if (head.length > 0 && head.startsWith(previewHead)) return i;
437
+ }
438
+ }
439
+ return null;
440
+ }
441
+
442
+ // ---------------------------------------------------------------------------
443
+ // Retained-image hydration
444
+ // ---------------------------------------------------------------------------
445
+
446
+ function buildRetainedImageBlocks(
447
+ filenames: string[],
448
+ manifest: ManifestEntry[],
449
+ ): { blocks: ImageContent[]; resolved: string[]; missing: string[] } {
450
+ const blocks: ImageContent[] = [];
451
+ const resolved: string[] = [];
452
+ const missing: string[] = [];
453
+ for (const name of filenames) {
454
+ const entry = manifest.find((e) => e.filename === name);
455
+ if (!entry) {
456
+ missing.push(name);
457
+ continue;
458
+ }
459
+ const content = getAttachmentContent(entry.attachmentId);
460
+ if (!content) {
461
+ missing.push(name);
462
+ continue;
463
+ }
464
+ const sourceMime = guessMimeFromFilename(name);
465
+ // Run the same downscale pass the agent uses when first sending an
466
+ // image. Without this, attachments that exceed the provider's per-image
467
+ // byte limit (Anthropic: 5 MB) crash the next turn after compaction.
468
+ const optimized = optimizeImageForTransport(
469
+ content.toString("base64"),
470
+ sourceMime,
471
+ );
472
+ blocks.push({
473
+ type: "image",
474
+ source: {
475
+ type: "base64",
476
+ media_type: optimized.mediaType,
477
+ data: optimized.data,
478
+ },
479
+ });
480
+ resolved.push(name);
481
+ }
482
+ return { blocks, resolved, missing };
483
+ }
484
+
485
+ function guessMimeFromFilename(filename: string): string {
486
+ const ext = filename.toLowerCase().split(".").pop() ?? "";
487
+ switch (ext) {
488
+ case "jpg":
489
+ case "jpeg":
490
+ return "image/jpeg";
491
+ case "png":
492
+ return "image/png";
493
+ case "gif":
494
+ return "image/gif";
495
+ case "webp":
496
+ return "image/webp";
497
+ default:
498
+ return "image/png";
499
+ }
500
+ }
501
+
502
+ // ---------------------------------------------------------------------------
503
+ // Instruction message
504
+ // ---------------------------------------------------------------------------
505
+
506
+ export function buildInstructionMessage(
507
+ customPrompt: string | null | undefined,
508
+ imageManifest: string,
509
+ ): Message {
510
+ const template =
511
+ customPrompt && customPrompt.trim().length > 0
512
+ ? customPrompt
513
+ : DEFAULT_COMPACTION_PROMPT;
514
+ const text = template.replace("{image_manifest}", imageManifest);
515
+ return {
516
+ role: "user",
517
+ content: [{ type: "text", text }],
518
+ };
519
+ }
520
+
521
+ // ---------------------------------------------------------------------------
522
+ // Summary message construction
523
+ // ---------------------------------------------------------------------------
524
+
525
+ /**
526
+ * Stitch summary + key_state into the assistant-role memory message that
527
+ * heads the compacted context. Kept as a single block so downstream
528
+ * lifecycle code can rehydrate it with the existing `contextSummary` text
529
+ * column without needing a parallel `keyState` column.
530
+ */
531
+ export function buildSummaryMemoryText(
532
+ summary: string,
533
+ keyState: string,
534
+ ): string {
535
+ const trimmedSummary = summary.trim();
536
+ const trimmedKey = keyState.trim();
537
+ if (trimmedKey.length === 0) return trimmedSummary;
538
+ return `${trimmedSummary}\n\n## Pending State\n${trimmedKey}`;
539
+ }
540
+
541
+ // ---------------------------------------------------------------------------
542
+ // Orchestrator
543
+ // ---------------------------------------------------------------------------
544
+
545
+ function emptyResult(
546
+ args: CompactionRunArgs,
547
+ thresholdTokens: number,
548
+ reason: string,
549
+ ): CompactionRunResult {
550
+ return {
551
+ messages: args.messages,
552
+ compacted: false,
553
+ previousEstimatedInputTokens: args.previousEstimatedInputTokens,
554
+ estimatedInputTokens: args.previousEstimatedInputTokens,
555
+ maxInputTokens: args.maxInputTokens,
556
+ thresholdTokens,
557
+ compactedMessages: 0,
558
+ compactedPersistedMessages: 0,
559
+ summaryCalls: 0,
560
+ summaryInputTokens: 0,
561
+ summaryOutputTokens: 0,
562
+ summaryModel: "",
563
+ summaryText: "",
564
+ reason,
565
+ };
566
+ }
567
+
568
+ function extractTextFromResponse(content: ContentBlock[]): string {
569
+ return content
570
+ .filter(
571
+ (b): b is Extract<ContentBlock, { type: "text" }> => b.type === "text",
572
+ )
573
+ .map((b) => b.text)
574
+ .join("\n");
575
+ }
576
+
577
+ export async function runAssistantDrivenCompaction(
578
+ args: CompactionRunArgs,
579
+ ): Promise<CompactionRunResult> {
580
+ const thresholdTokens = Math.floor(
581
+ args.maxInputTokens * args.compaction.autoThreshold,
582
+ );
583
+
584
+ if (!args.compaction.enabled) {
585
+ return emptyResult(args, thresholdTokens, "compaction disabled");
586
+ }
587
+
588
+ if (!args.force && args.previousEstimatedInputTokens < thresholdTokens) {
589
+ return emptyResult(args, thresholdTokens, "below auto threshold");
590
+ }
591
+
592
+ if (args.messages.length === 0) {
593
+ return emptyResult(args, thresholdTokens, "no messages to compact");
594
+ }
595
+
596
+ // Build image manifest from the DB before invoking the model so the
597
+ // instruction message carries a faithful picture of available images.
598
+ const manifest = collectImageManifest(args.conversationId);
599
+ const manifestText = renderImageManifest(manifest);
600
+ const instruction = buildInstructionMessage(
601
+ args.compaction.prompt ?? null,
602
+ manifestText,
603
+ );
604
+
605
+ // Append instruction at the tail — prefix unchanged, so prefix cache
606
+ // stays warm.
607
+ const requestMessages = [...args.messages, instruction];
608
+
609
+ let response: ProviderResponse;
610
+ try {
611
+ response = await args.provider.sendMessage(
612
+ requestMessages,
613
+ args.tools,
614
+ args.systemPrompt,
615
+ {
616
+ signal: args.signal,
617
+ config: {
618
+ callSite: COMPACTION_CALL_SITE,
619
+ usageTracking: "manual",
620
+ ...(args.overrideProfile
621
+ ? { overrideProfile: args.overrideProfile }
622
+ : {}),
623
+ },
624
+ },
625
+ );
626
+ } catch (err) {
627
+ log.warn({ err }, "Compaction provider call failed");
628
+ return {
629
+ ...emptyResult(args, thresholdTokens, "provider error"),
630
+ summaryFailed: true,
631
+ };
632
+ }
633
+
634
+ const rawText = extractTextFromResponse(response.content);
635
+ const parsed = parseCompactionResult(rawText);
636
+ if (!parsed) {
637
+ log.warn(
638
+ { rawPreview: rawText.slice(0, 200) },
639
+ "Compaction response did not contain a valid <compaction_result> block",
640
+ );
641
+ return {
642
+ ...emptyResult(args, thresholdTokens, "unparseable response"),
643
+ summaryFailed: false,
644
+ summaryInputTokens: response.usage.inputTokens,
645
+ summaryOutputTokens: response.usage.outputTokens,
646
+ summaryModel: response.model,
647
+ summaryCacheCreationInputTokens:
648
+ response.usage.cacheCreationInputTokens ?? 0,
649
+ summaryCacheReadInputTokens: response.usage.cacheReadInputTokens ?? 0,
650
+ summaryCallSite: COMPACTION_CALL_SITE,
651
+ summaryOverrideProfile: args.overrideProfile ?? null,
652
+ summaryRawResponses: response.rawResponse ? [response.rawResponse] : [],
653
+ summaryCalls: 1,
654
+ };
655
+ }
656
+
657
+ const timestamps = buildTimestampIndex(args.messages);
658
+ const tailIndex = resolveTailStartIndex(args.messages, timestamps, parsed);
659
+ if (tailIndex == null) {
660
+ log.warn(
661
+ {
662
+ timestamp: parsed.tailStartTimestamp,
663
+ preview: parsed.tailStartPreview.slice(0, 60),
664
+ },
665
+ "Compaction tail_start did not match any message — aborting compaction",
666
+ );
667
+ return {
668
+ ...emptyResult(args, thresholdTokens, "tail_start unresolved"),
669
+ summaryFailed: false,
670
+ summaryInputTokens: response.usage.inputTokens,
671
+ summaryOutputTokens: response.usage.outputTokens,
672
+ summaryModel: response.model,
673
+ summaryCacheCreationInputTokens:
674
+ response.usage.cacheCreationInputTokens ?? 0,
675
+ summaryCacheReadInputTokens: response.usage.cacheReadInputTokens ?? 0,
676
+ summaryCallSite: COMPACTION_CALL_SITE,
677
+ summaryOverrideProfile: args.overrideProfile ?? null,
678
+ summaryRawResponses: response.rawResponse ? [response.rawResponse] : [],
679
+ summaryCalls: 1,
680
+ };
681
+ }
682
+
683
+ if (tailIndex === 0) {
684
+ return {
685
+ ...emptyResult(
686
+ args,
687
+ thresholdTokens,
688
+ "tail_start at head — nothing to compact",
689
+ ),
690
+ summaryFailed: false,
691
+ summaryInputTokens: response.usage.inputTokens,
692
+ summaryOutputTokens: response.usage.outputTokens,
693
+ summaryModel: response.model,
694
+ summaryCacheCreationInputTokens:
695
+ response.usage.cacheCreationInputTokens ?? 0,
696
+ summaryCacheReadInputTokens: response.usage.cacheReadInputTokens ?? 0,
697
+ summaryCallSite: COMPACTION_CALL_SITE,
698
+ summaryOverrideProfile: args.overrideProfile ?? null,
699
+ summaryRawResponses: response.rawResponse ? [response.rawResponse] : [],
700
+ summaryCalls: 1,
701
+ };
702
+ }
703
+
704
+ const compactableMessages = args.messages.slice(0, tailIndex);
705
+ // Strip runtime injections from preserved tail messages before they land in
706
+ // the compacted history. The static blocks (NOW.md, PKB, v2 essentials/
707
+ // threads/recent/buffer, system reminders) on the tail are stale snapshots
708
+ // from the moment of capture — keeping them would (a) waste tokens on
709
+ // outdated content, (b) duplicate against the freshly re-injected blocks
710
+ // the next turn produces, and (c) leak `<system_reminder>` text the model
711
+ // is not supposed to see in history. `<turn_context>` and `<workspace>`
712
+ // are intentionally preserved by `RUNTIME_INJECTION_PREFIXES`.
713
+ const tailMessages = stripInjectionsForCompaction(
714
+ args.messages.slice(tailIndex),
715
+ );
716
+
717
+ const summaryText = buildSummaryMemoryText(parsed.summary, parsed.keyState);
718
+ const summaryMessage: Message = {
719
+ role: "assistant",
720
+ content: [{ type: "text", text: summaryText }],
721
+ };
722
+
723
+ const {
724
+ blocks: retainedImageBlocks,
725
+ resolved,
726
+ missing,
727
+ } = buildRetainedImageBlocks(parsed.retainedImageFilenames, manifest);
728
+ if (missing.length > 0) {
729
+ log.warn(
730
+ { missing },
731
+ "Compaction referenced images that could not be resolved against attachments — dropping",
732
+ );
733
+ }
734
+
735
+ const compactedMessages: Message[] = [summaryMessage];
736
+ if (retainedImageBlocks.length > 0) {
737
+ compactedMessages.push({
738
+ role: "user",
739
+ content: [
740
+ {
741
+ type: "text" as const,
742
+ text: "Images retained from the compacted portion of the conversation:",
743
+ },
744
+ ...retainedImageBlocks,
745
+ ],
746
+ });
747
+ }
748
+ compactedMessages.push(...tailMessages);
749
+
750
+ const nonPersistedCompactedAway = Math.min(
751
+ args.nonPersistedPrefixCount ?? 0,
752
+ compactableMessages.length,
753
+ );
754
+ const compactedPersistedMessages = Math.max(
755
+ 0,
756
+ compactableMessages.length - nonPersistedCompactedAway,
757
+ );
758
+
759
+ log.info(
760
+ {
761
+ conversationId: args.conversationId,
762
+ compactedMessages: compactableMessages.length,
763
+ compactedPersistedMessages,
764
+ tailIndex,
765
+ retainedImages: resolved.length,
766
+ summaryChars: summaryText.length,
767
+ },
768
+ "Applied assistant-driven compaction",
769
+ );
770
+
771
+ return {
772
+ messages: compactedMessages,
773
+ compacted: true,
774
+ previousEstimatedInputTokens: args.previousEstimatedInputTokens,
775
+ // We don't re-estimate here — the caller (window manager) will recompute
776
+ // when it returns to the agent loop. Returning the previous estimate is
777
+ // a conservative placeholder.
778
+ estimatedInputTokens: args.previousEstimatedInputTokens,
779
+ maxInputTokens: args.maxInputTokens,
780
+ thresholdTokens,
781
+ compactedMessages: compactableMessages.length,
782
+ compactedPersistedMessages,
783
+ summaryCalls: 1,
784
+ summaryInputTokens: response.usage.inputTokens,
785
+ summaryOutputTokens: response.usage.outputTokens,
786
+ summaryModel: response.model,
787
+ summaryCallSite: COMPACTION_CALL_SITE,
788
+ summaryOverrideProfile: args.overrideProfile ?? null,
789
+ summaryCacheCreationInputTokens:
790
+ response.usage.cacheCreationInputTokens ?? 0,
791
+ summaryCacheReadInputTokens: response.usage.cacheReadInputTokens ?? 0,
792
+ summaryRawResponses: response.rawResponse ? [response.rawResponse] : [],
793
+ summaryText,
794
+ keyState: parsed.keyState,
795
+ summaryFailed: false,
796
+ };
797
+ }
798
+
799
+ // ---------------------------------------------------------------------------
800
+ // Emergency mid-turn compaction
801
+ // ---------------------------------------------------------------------------
802
+
803
+ /**
804
+ * Simplified instruction for emergency compaction. No `tail_start` or
805
+ * `retained_images` — the caller already knows the split point. The model
806
+ * just needs to produce a summary + key_state.
807
+ */
808
+ const EMERGENCY_COMPACTION_PROMPT = `<emergency_compaction>
809
+ The conversation has exceeded the context window during an active task.
810
+ This is an emergency compaction — summarize EVERYTHING you see into a
811
+ fresh-start summary so the assistant can continue its work.
812
+
813
+ Write the summary in YOUR voice. Prioritize:
814
+ - What task is currently in progress and what stage it is at
815
+ - Decisions already made during this task
816
+ - Key results from tool calls that are still relevant
817
+ - Any commitments or state changes from earlier in the conversation
818
+ - What the next step should be
819
+
820
+ Be thorough on task state — this summary plus the most recent tool call
821
+ and its result are the ONLY context the assistant will have to continue.
822
+
823
+ Output your result in this exact format:
824
+
825
+ <compaction_result>
826
+ <summary>
827
+ Your complete summary of everything that happened.
828
+ </summary>
829
+
830
+ <key_state>
831
+ Structured list of:
832
+ - Current task and its status
833
+ - Important intermediate results
834
+ - What to do next
835
+ </key_state>
836
+ </compaction_result>
837
+ </emergency_compaction>`;
838
+
839
+ /**
840
+ * Find the start index of the last tool_use + tool_result cluster in the
841
+ * message array. Walks backwards to find the last assistant message
842
+ * containing a `tool_use` content block, then returns that index. The
843
+ * caller keeps everything from this index onwards as the preserved tail.
844
+ *
845
+ * Returns `null` if no tool_use message is found.
846
+ */
847
+ function findLastToolPairStart(messages: Message[]): number | null {
848
+ for (let i = messages.length - 1; i >= 0; i--) {
849
+ const msg = messages[i];
850
+ if (msg.role !== "assistant") continue;
851
+ const hasToolUse = msg.content.some((b) => b.type === "tool_use");
852
+ if (hasToolUse) return i;
853
+ }
854
+ return null;
855
+ }
856
+
857
+ /**
858
+ * Emergency compaction for the mid-turn context overflow case.
859
+ *
860
+ * When a `context_too_large` error fires during an active agent turn,
861
+ * the normal compactor may itself exceed the context window (the
862
+ * conversation that needs compacting is, by definition, too large to
863
+ * send to the model as-is).
864
+ *
865
+ * This function:
866
+ * 1. Finds the last tool_use message + its trailing tool_result(s)
867
+ * 2. Splits the history there
868
+ * 3. Truncates the prefix from the front if it exceeds the model's
869
+ * context window
870
+ * 4. Sends the (possibly truncated) prefix to the model with a
871
+ * simplified emergency instruction
872
+ * 5. Returns `[summary_message, ...last_tool_pair]` so the agent
873
+ * can continue with knowledge of what it just did
874
+ *
875
+ * If the provider call fails or no tool pair is found, returns
876
+ * `compacted: false` so the caller can fall through to other
877
+ * recovery strategies (tool-result truncation, media stubbing, etc.).
878
+ */
879
+ export async function runEmergencyCompaction(
880
+ args: CompactionRunArgs,
881
+ ): Promise<CompactionRunResult> {
882
+ const thresholdTokens = Math.floor(
883
+ args.maxInputTokens * args.compaction.autoThreshold,
884
+ );
885
+
886
+ const splitIndex = findLastToolPairStart(args.messages);
887
+ if (splitIndex == null || splitIndex === 0) {
888
+ log.info(
889
+ "Emergency compaction: no tool pair found — falling through",
890
+ );
891
+ return emptyResult(args, thresholdTokens, "no tool pair for emergency split");
892
+ }
893
+
894
+ const keptTail = stripInjectionsForCompaction(
895
+ args.messages.slice(splitIndex),
896
+ );
897
+ let prefix = args.messages.slice(0, splitIndex);
898
+
899
+ // If the prefix itself exceeds the context window, truncate messages
900
+ // from the front so the model can at least see the recent portion.
901
+ // Reserve budget for the instruction message + output.
902
+ const instructionBudget = 800; // ~tokens for the emergency prompt
903
+ const outputBudget = Math.floor(args.maxInputTokens * 0.15);
904
+ const prefixBudget = args.maxInputTokens - instructionBudget - outputBudget;
905
+
906
+ let prefixEstimate = estimatePromptTokens(prefix, args.systemPrompt, {
907
+ providerName:
908
+ args.provider.tokenEstimationProvider ?? args.provider.name,
909
+ });
910
+
911
+ if (prefixEstimate > prefixBudget && prefix.length > 1) {
912
+ log.info(
913
+ {
914
+ prefixEstimate,
915
+ prefixBudget,
916
+ prefixMessages: prefix.length,
917
+ },
918
+ "Emergency compaction: prefix exceeds context window — truncating from front",
919
+ );
920
+ // Drop messages from the front until we fit. Keep at least the first
921
+ // message (may be an existing summary) and try to preserve recent context.
922
+ let dropCount = 0;
923
+ while (
924
+ prefixEstimate > prefixBudget &&
925
+ dropCount < prefix.length - 1
926
+ ) {
927
+ dropCount++;
928
+ const truncated = prefix.slice(dropCount);
929
+ prefixEstimate = estimatePromptTokens(truncated, args.systemPrompt, {
930
+ providerName:
931
+ args.provider.tokenEstimationProvider ?? args.provider.name,
932
+ });
933
+ }
934
+ if (dropCount > 0) {
935
+ prefix = [
936
+ {
937
+ role: "user" as const,
938
+ content: [
939
+ {
940
+ type: "text" as const,
941
+ text: `[${dropCount} earlier messages truncated — summary covers only the visible portion]`,
942
+ },
943
+ ],
944
+ },
945
+ ...prefix.slice(dropCount),
946
+ ];
947
+ }
948
+ }
949
+
950
+ const instruction: Message = {
951
+ role: "user",
952
+ content: [{ type: "text", text: EMERGENCY_COMPACTION_PROMPT }],
953
+ };
954
+ const requestMessages = [...prefix, instruction];
955
+
956
+ let response: ProviderResponse;
957
+ try {
958
+ response = await args.provider.sendMessage(
959
+ requestMessages,
960
+ args.tools,
961
+ args.systemPrompt,
962
+ {
963
+ signal: args.signal,
964
+ config: {
965
+ callSite: COMPACTION_CALL_SITE,
966
+ usageTracking: "manual",
967
+ ...(args.overrideProfile
968
+ ? { overrideProfile: args.overrideProfile }
969
+ : {}),
970
+ },
971
+ },
972
+ );
973
+ } catch (err) {
974
+ log.warn({ err }, "Emergency compaction provider call failed");
975
+ return {
976
+ ...emptyResult(args, thresholdTokens, "emergency provider error"),
977
+ summaryFailed: true,
978
+ };
979
+ }
980
+
981
+ const rawText = extractTextFromResponse(response.content);
982
+ const parsed = parseCompactionResult(rawText);
983
+ if (!parsed) {
984
+ log.warn(
985
+ { rawPreview: rawText.slice(0, 200) },
986
+ "Emergency compaction response did not contain a valid <compaction_result>",
987
+ );
988
+ return {
989
+ ...emptyResult(args, thresholdTokens, "emergency unparseable response"),
990
+ summaryFailed: false,
991
+ summaryCalls: 1,
992
+ summaryInputTokens: response.usage.inputTokens,
993
+ summaryOutputTokens: response.usage.outputTokens,
994
+ summaryModel: response.model,
995
+ };
996
+ }
997
+
998
+ const summaryText = buildSummaryMemoryText(parsed.summary, parsed.keyState);
999
+ const summaryMessage: Message = {
1000
+ role: "assistant",
1001
+ content: [{ type: "text", text: summaryText }],
1002
+ };
1003
+
1004
+ const compactedMessages: Message[] = [summaryMessage, ...keptTail];
1005
+
1006
+ const compactedCount = splitIndex;
1007
+ const nonPersistedAway = Math.min(
1008
+ args.nonPersistedPrefixCount ?? 0,
1009
+ compactedCount,
1010
+ );
1011
+
1012
+ log.info(
1013
+ {
1014
+ conversationId: args.conversationId,
1015
+ compactedMessages: compactedCount,
1016
+ keptTailMessages: keptTail.length,
1017
+ summaryChars: summaryText.length,
1018
+ prefixTruncated: prefix[0]?.content?.[0]?.type === "text" &&
1019
+ (prefix[0].content[0] as { text: string }).text.includes("truncated"),
1020
+ },
1021
+ "Applied emergency mid-turn compaction",
1022
+ );
1023
+
1024
+ return {
1025
+ messages: compactedMessages,
1026
+ compacted: true,
1027
+ previousEstimatedInputTokens: args.previousEstimatedInputTokens,
1028
+ estimatedInputTokens: args.previousEstimatedInputTokens,
1029
+ maxInputTokens: args.maxInputTokens,
1030
+ thresholdTokens,
1031
+ compactedMessages: compactedCount,
1032
+ compactedPersistedMessages: Math.max(0, compactedCount - nonPersistedAway),
1033
+ summaryCalls: 1,
1034
+ summaryInputTokens: response.usage.inputTokens,
1035
+ summaryOutputTokens: response.usage.outputTokens,
1036
+ summaryModel: response.model,
1037
+ summaryCallSite: COMPACTION_CALL_SITE,
1038
+ summaryOverrideProfile: args.overrideProfile ?? null,
1039
+ summaryCacheCreationInputTokens:
1040
+ response.usage.cacheCreationInputTokens ?? 0,
1041
+ summaryCacheReadInputTokens: response.usage.cacheReadInputTokens ?? 0,
1042
+ summaryRawResponses: response.rawResponse ? [response.rawResponse] : [],
1043
+ summaryText,
1044
+ keyState: parsed.keyState,
1045
+ summaryFailed: false,
1046
+ };
1047
+ }