@vellumai/assistant 0.6.4 → 0.6.6

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 (1008) hide show
  1. package/.prettierignore +5 -0
  2. package/AGENTS.md +9 -1
  3. package/ARCHITECTURE.md +43 -49
  4. package/Dockerfile +17 -3
  5. package/README.md +3 -4
  6. package/__tests__/permissions/gateway-threshold-reader.test.ts +283 -0
  7. package/bun.lock +8 -3
  8. package/docs/architecture/integrations.md +33 -59
  9. package/docs/architecture/memory.md +25 -30
  10. package/docs/architecture/security.md +19 -18
  11. package/docs/browser-use-architecture-phase2.md +63 -20
  12. package/docs/error-handling.md +111 -0
  13. package/docs/plugins.md +761 -0
  14. package/docs/skills.md +10 -10
  15. package/docs/stt-provider-onboarding.md +2 -1
  16. package/examples/plugins/echo/README.md +132 -0
  17. package/examples/plugins/echo/package.json +17 -0
  18. package/examples/plugins/echo/register.ts +187 -0
  19. package/knip.json +9 -2
  20. package/node_modules/@vellumai/ces-contracts/package.json +2 -1
  21. package/node_modules/@vellumai/ces-contracts/src/__tests__/trust-rules.test.ts +471 -0
  22. package/node_modules/@vellumai/ces-contracts/src/trust-rules.ts +398 -4
  23. package/node_modules/@vellumai/credential-storage/bun.lock +2 -2
  24. package/node_modules/@vellumai/credential-storage/package.json +2 -2
  25. package/node_modules/@vellumai/credential-storage/src/oauth-runtime.ts +20 -2
  26. package/node_modules/@vellumai/egress-proxy/bun.lock +2 -2
  27. package/node_modules/@vellumai/egress-proxy/package.json +2 -2
  28. package/node_modules/@vellumai/egress-proxy/src/types.ts +19 -0
  29. package/openapi.yaml +334 -78
  30. package/package.json +6 -3
  31. package/scripts/generate-openapi.ts +50 -11
  32. package/src/__tests__/agent-loop-callsite-precedence.test.ts +318 -0
  33. package/src/__tests__/agent-loop-sentry-hygiene.test.ts +137 -0
  34. package/src/__tests__/agent-loop.test.ts +112 -1
  35. package/src/__tests__/anthropic-error-formatting.test.ts +98 -0
  36. package/src/__tests__/anthropic-provider.test.ts +171 -2
  37. package/src/__tests__/app-compiler.test.ts +57 -0
  38. package/src/__tests__/approval-cascade.test.ts +36 -10
  39. package/src/__tests__/approval-routes-http.test.ts +134 -10
  40. package/src/__tests__/assistant-attachments.test.ts +44 -0
  41. package/src/__tests__/assistant-feature-flags-integration.test.ts +29 -0
  42. package/src/__tests__/auto-analysis-end-to-end.test.ts +1 -0
  43. package/src/__tests__/avatar-generator.test.ts +4 -2
  44. package/src/__tests__/browser-fill-credential.test.ts +1 -1
  45. package/src/__tests__/browser-identifier-parity-guard.test.ts +53 -0
  46. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +23 -33
  47. package/src/__tests__/browser-skill-endstate.test.ts +51 -182
  48. package/src/__tests__/btw-routes.test.ts +47 -1
  49. package/src/__tests__/bundled-asset.test.ts +6 -6
  50. package/src/__tests__/call-controller.test.ts +1 -2
  51. package/src/__tests__/call-site-routing-provider.test.ts +214 -0
  52. package/src/__tests__/catalog-cache.test.ts +96 -4
  53. package/src/__tests__/channel-approval-routes.test.ts +4 -4
  54. package/src/__tests__/channel-reply-delivery.test.ts +300 -2
  55. package/src/__tests__/checker.test.ts +870 -655
  56. package/src/__tests__/circuit-breaker-pipeline.test.ts +406 -0
  57. package/src/__tests__/cli-command-risk-guard.test.ts +30 -33
  58. package/src/__tests__/compaction-events.test.ts +501 -0
  59. package/src/__tests__/compaction-pipeline.test.ts +210 -0
  60. package/src/__tests__/compaction-strip-metadata-clear.test.ts +181 -0
  61. package/src/__tests__/compaction-timeout-recovery.test.ts +262 -0
  62. package/src/__tests__/compaction.benchmark.test.ts +1 -1
  63. package/src/__tests__/config-analysis.test.ts +11 -28
  64. package/src/__tests__/config-loader-backfill.test.ts +174 -0
  65. package/src/__tests__/config-loader-corrupt.test.ts +183 -0
  66. package/src/__tests__/config-loader-quarantine-bulletin.test.ts +202 -0
  67. package/src/__tests__/config-model-image-provider.test.ts +110 -0
  68. package/src/__tests__/config-schema-cmd.test.ts +11 -5
  69. package/src/__tests__/config-schema.test.ts +440 -114
  70. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +0 -4
  71. package/src/__tests__/config-watcher.test.ts +2 -2
  72. package/src/__tests__/contact-store-user-file.test.ts +72 -73
  73. package/src/__tests__/contacts-tools.test.ts +26 -0
  74. package/src/__tests__/contacts-write.test.ts +4 -4
  75. package/src/__tests__/context-overflow-policy.test.ts +7 -7
  76. package/src/__tests__/context-token-estimator.test.ts +191 -1
  77. package/src/__tests__/context-window-manager.test.ts +883 -4
  78. package/src/__tests__/conversation-abort-tool-results.test.ts +32 -15
  79. package/src/__tests__/conversation-agent-loop-overflow.test.ts +86 -46
  80. package/src/__tests__/conversation-agent-loop.test.ts +435 -216
  81. package/src/__tests__/conversation-attachments.test.ts +1 -1
  82. package/src/__tests__/conversation-confirmation-signals.test.ts +36 -10
  83. package/src/__tests__/conversation-error.test.ts +37 -6
  84. package/src/__tests__/conversation-history-web-search.test.ts +7 -0
  85. package/src/__tests__/conversation-init.benchmark.test.ts +34 -12
  86. package/src/__tests__/conversation-lifecycle.test.ts +336 -0
  87. package/src/__tests__/conversation-load-history-repair.test.ts +27 -10
  88. package/src/__tests__/conversation-pairing.test.ts +174 -10
  89. package/src/__tests__/conversation-pre-run-repair.test.ts +32 -15
  90. package/src/__tests__/conversation-process-callsite.test.ts +309 -0
  91. package/src/__tests__/conversation-provider-retry-repair.test.ts +44 -21
  92. package/src/__tests__/conversation-queue.test.ts +68 -38
  93. package/src/__tests__/conversation-routes-disk-view.test.ts +36 -7
  94. package/src/__tests__/conversation-routes-slash-commands.test.ts +31 -3
  95. package/src/__tests__/conversation-runtime-assembly.test.ts +2877 -152
  96. package/src/__tests__/conversation-runtime-workspace.test.ts +35 -50
  97. package/src/__tests__/conversation-seed-composer.test.ts +2 -2
  98. package/src/__tests__/conversation-skill-tools.test.ts +12 -146
  99. package/src/__tests__/conversation-slash-queue.test.ts +39 -19
  100. package/src/__tests__/conversation-slash-unknown.test.ts +53 -16
  101. package/src/__tests__/conversation-speed-override.test.ts +36 -12
  102. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +1035 -0
  103. package/src/__tests__/conversation-surfaces-standalone.test.ts +630 -0
  104. package/src/__tests__/conversation-title-service.test.ts +118 -2
  105. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +41 -2
  106. package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +1 -1
  107. package/src/__tests__/conversation-unread-route.test.ts +2 -2
  108. package/src/__tests__/conversation-usage.test.ts +4 -2
  109. package/src/__tests__/conversation-workspace-cache-state.test.ts +33 -9
  110. package/src/__tests__/conversation-workspace-injection.test.ts +46 -15
  111. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +46 -15
  112. package/src/__tests__/credential-broker-browser-fill.test.ts +110 -0
  113. package/src/__tests__/credential-health-service.test.ts +78 -9
  114. package/src/__tests__/credential-security-invariants.test.ts +5 -2
  115. package/src/__tests__/credential-storage-oauth-compat.test.ts +18 -0
  116. package/src/__tests__/credential-storage-static-compat.test.ts +28 -0
  117. package/src/__tests__/credential-vault-unit.test.ts +135 -19
  118. package/src/__tests__/credentials-cli.test.ts +1 -9
  119. package/src/__tests__/cross-provider-web-search.test.ts +84 -0
  120. package/src/__tests__/daemon-server-persist-and-process-callsite.test.ts +92 -0
  121. package/src/__tests__/db-schedule-syntax-migration.test.ts +1 -0
  122. package/src/__tests__/delete-propagation.test.ts +437 -0
  123. package/src/__tests__/dm-backfill.test.ts +417 -0
  124. package/src/__tests__/dm-persistence.test.ts +227 -0
  125. package/src/__tests__/edit-propagation.test.ts +280 -0
  126. package/src/__tests__/empty-response-pipeline.test.ts +305 -0
  127. package/src/__tests__/ephemeral-permissions.test.ts +93 -3
  128. package/src/__tests__/estimator-calibration-integration.test.ts +208 -0
  129. package/src/__tests__/estimator-calibration.test.ts +213 -0
  130. package/src/__tests__/extension-id-sync-guard.test.ts +29 -10
  131. package/src/__tests__/file-write-tool.test.ts +151 -1
  132. package/src/__tests__/filing-service.test.ts +255 -0
  133. package/src/__tests__/first-greeting.test.ts +247 -5
  134. package/src/__tests__/gemini-provider.test.ts +0 -3
  135. package/src/__tests__/guardian-grant-minting.test.ts +8 -0
  136. package/src/__tests__/headless-browser-interactions.test.ts +1 -1
  137. package/src/__tests__/headless-browser-mode.test.ts +57 -0
  138. package/src/__tests__/heartbeat-service.test.ts +96 -15
  139. package/src/__tests__/history-repair-pipeline.test.ts +399 -0
  140. package/src/__tests__/host-browser-e2e-cloud.test.ts +307 -0
  141. package/src/__tests__/host-browser-e2e-self-hosted.test.ts +3 -3
  142. package/src/__tests__/host-proxy-interface.test.ts +36 -2
  143. package/src/__tests__/host-shell-tool.test.ts +124 -18
  144. package/src/__tests__/http-user-message-parity.test.ts +29 -1
  145. package/src/__tests__/image-credentials.test.ts +137 -0
  146. package/src/__tests__/image-service-dispatcher.test.ts +186 -0
  147. package/src/__tests__/inbound-slack-persistence.test.ts +340 -0
  148. package/src/__tests__/injector-chain.test.ts +526 -0
  149. package/src/__tests__/intent-routing.test.ts +1 -66
  150. package/src/__tests__/llm-call-pipeline.test.ts +285 -0
  151. package/src/__tests__/llm-catalog-parity.test.ts +174 -0
  152. package/src/__tests__/llm-context-normalization.test.ts +121 -0
  153. package/src/__tests__/llm-resolver.test.ts +214 -0
  154. package/src/__tests__/llm-schema.test.ts +223 -0
  155. package/src/__tests__/managed-proxy-context.test.ts +6 -2
  156. package/src/__tests__/media-generate-image.test.ts +119 -13
  157. package/src/__tests__/memory-retrieval-pipeline.test.ts +401 -0
  158. package/src/__tests__/memory-upsert-concurrency.test.ts +1 -0
  159. package/src/__tests__/messaging-skill-split.test.ts +3 -34
  160. package/src/__tests__/migration-import-from-url.test.ts +621 -0
  161. package/src/__tests__/model-intents.test.ts +11 -83
  162. package/src/__tests__/notification-broadcaster.test.ts +3 -3
  163. package/src/__tests__/notification-decision-fallback.test.ts +0 -10
  164. package/src/__tests__/notification-decision-identity.test.ts +0 -9
  165. package/src/__tests__/notification-decision-recipient-context.test.ts +0 -9
  166. package/src/__tests__/notification-decision-strategy.test.ts +0 -11
  167. package/src/__tests__/notification-schedule-notify-dedup.test.ts +108 -0
  168. package/src/__tests__/oauth-apps-routes.test.ts +1 -1
  169. package/src/__tests__/oauth-cli.test.ts +14 -12
  170. package/src/__tests__/oauth-connect-orchestrator.test.ts +4 -13
  171. package/src/__tests__/oauth-provider-serializer.test.ts +6 -4
  172. package/src/__tests__/oauth-provider-visibility.test.ts +3 -5
  173. package/src/__tests__/oauth-providers-routes.test.ts +3 -2
  174. package/src/__tests__/oauth-store.test.ts +46 -78
  175. package/src/__tests__/oauth2-gateway-transport.test.ts +8 -3
  176. package/src/__tests__/oauth2-refresh-retry.test.ts +279 -0
  177. package/src/__tests__/onboarding-template-contract.test.ts +16 -64
  178. package/src/__tests__/openai-image-service.test.ts +368 -0
  179. package/src/__tests__/openai-provider.test.ts +7 -0
  180. package/src/__tests__/openai-responses-provider.test.ts +396 -0
  181. package/src/__tests__/openrouter-provider-only.test.ts +135 -0
  182. package/src/__tests__/outbound-slack-persistence.test.ts +293 -0
  183. package/src/__tests__/overflow-reduce-pipeline.test.ts +676 -0
  184. package/src/__tests__/permission-checker-host-gate.test.ts +1 -25
  185. package/src/__tests__/permission-mode.test.ts +16 -0
  186. package/src/__tests__/permission-types.test.ts +0 -1
  187. package/src/__tests__/persist-onboarding-artifacts.test.ts +266 -0
  188. package/src/__tests__/persistence-pipeline.test.ts +377 -0
  189. package/src/__tests__/persona-resolver.test.ts +13 -13
  190. package/src/__tests__/pipeline-runner.test.ts +565 -0
  191. package/src/__tests__/pkb-autoinject.test.ts +37 -1
  192. package/src/__tests__/platform-bash-auto-approve.test.ts +1 -1
  193. package/src/__tests__/platform.test.ts +5 -2
  194. package/src/__tests__/plugin-bootstrap.test.ts +483 -0
  195. package/src/__tests__/plugin-registry.test.ts +273 -0
  196. package/src/__tests__/plugin-route-contribution.test.ts +288 -0
  197. package/src/__tests__/plugin-skill-contribution.test.ts +367 -0
  198. package/src/__tests__/plugin-tool-contribution.test.ts +286 -0
  199. package/src/__tests__/plugin-types.test.ts +320 -0
  200. package/src/__tests__/pricing.test.ts +93 -14
  201. package/src/__tests__/profiler-routes.test.ts +1 -1
  202. package/src/__tests__/provider-commit-message-generator.test.ts +14 -84
  203. package/src/__tests__/provider-env-vars-scope.test.ts +52 -0
  204. package/src/__tests__/provider-error-scenarios.test.ts +135 -6
  205. package/src/__tests__/provider-managed-proxy-integration.test.ts +42 -11
  206. package/src/__tests__/provider-registry-ollama.test.ts +1 -2
  207. package/src/__tests__/proxy-approval-callback.test.ts +69 -9
  208. package/src/__tests__/reaction-persistence.test.ts +561 -0
  209. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
  210. package/src/__tests__/registry.test.ts +0 -2
  211. package/src/__tests__/relay-server.test.ts +1 -1
  212. package/src/__tests__/require-fresh-approval.test.ts +1 -1
  213. package/src/__tests__/retry-openrouter-only-normalization.test.ts +136 -0
  214. package/src/__tests__/retry-thinking-tool-choice.test.ts +226 -0
  215. package/src/__tests__/risk-classifier-parity.test.ts +230 -0
  216. package/src/__tests__/sanitize-config-for-transfer.test.ts +78 -1
  217. package/src/__tests__/schedule-routes.test.ts +131 -1
  218. package/src/__tests__/scheduler-recurrence.test.ts +14 -70
  219. package/src/__tests__/scheduler-reuse-conversation.test.ts +10 -50
  220. package/src/__tests__/secret-detection-handler.test.ts +0 -10
  221. package/src/__tests__/secret-ingress-http.test.ts +28 -0
  222. package/src/__tests__/secret-prompter-channel-fallback.test.ts +125 -0
  223. package/src/__tests__/secret-routes-managed-proxy.test.ts +2 -3
  224. package/src/__tests__/secret-scanner-executor.test.ts +1 -1
  225. package/src/__tests__/send-endpoint-busy.test.ts +29 -1
  226. package/src/__tests__/server-history-render.test.ts +31 -0
  227. package/src/__tests__/shell-identity.test.ts +0 -134
  228. package/src/__tests__/shell-parser-property.test.ts +13 -13
  229. package/src/__tests__/skill-cache-store.test.ts +182 -0
  230. package/src/__tests__/skills.test.ts +19 -33
  231. package/src/__tests__/slack-app-setup-skill-regression.test.ts +3 -1
  232. package/src/__tests__/slack-skill.test.ts +3 -8
  233. package/src/__tests__/starter-bundle.test.ts +35 -0
  234. package/src/__tests__/subagent-call-site-routing.test.ts +280 -0
  235. package/src/__tests__/suggestion-routes.test.ts +259 -3
  236. package/src/__tests__/system-prompt.test.ts +22 -35
  237. package/src/__tests__/task-memory-cleanup.test.ts +1 -0
  238. package/src/__tests__/task-runner.test.ts +3 -1
  239. package/src/__tests__/task-scheduler.test.ts +3 -15
  240. package/src/__tests__/tcc-sandbox-deny.test.ts +198 -0
  241. package/src/__tests__/terminal-tools.test.ts +8 -0
  242. package/src/__tests__/test-preload.ts +11 -0
  243. package/src/__tests__/test-support/browser-skill-harness.ts +2 -52
  244. package/src/__tests__/thread-backfill.test.ts +941 -0
  245. package/src/__tests__/title-generate-pipeline.test.ts +224 -0
  246. package/src/__tests__/token-estimate-pipeline.test.ts +431 -0
  247. package/src/__tests__/tool-error-pipeline.test.ts +244 -0
  248. package/src/__tests__/tool-execute-pipeline.test.ts +431 -0
  249. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -8
  250. package/src/__tests__/tool-executor-lifecycle-events.test.ts +2 -2
  251. package/src/__tests__/tool-executor-shell-integration.test.ts +7 -10
  252. package/src/__tests__/tool-executor.test.ts +201 -94
  253. package/src/__tests__/tool-result-truncate-pipeline.test.ts +356 -0
  254. package/src/__tests__/tool-result-truncation.test.ts +0 -110
  255. package/src/__tests__/trust-store.test.ts +442 -109
  256. package/src/__tests__/update-bulletin-job.test.ts +389 -0
  257. package/src/__tests__/usage-cache-backfill-migration.test.ts +3 -1
  258. package/src/__tests__/user-plugin-loader.test.ts +191 -0
  259. package/src/__tests__/verification-control-plane-policy.test.ts +1 -22
  260. package/src/__tests__/voice-session-bridge.test.ts +39 -0
  261. package/src/__tests__/volume-security-guard.test.ts +3 -2
  262. package/src/__tests__/web-search-history.test.ts +337 -0
  263. package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +343 -0
  264. package/src/__tests__/workspace-migration-043-release-notes-latex-rendering.test.ts +202 -0
  265. package/src/__tests__/workspace-migration-045-release-notes-meet-avatar.test.ts +210 -0
  266. package/src/__tests__/workspace-migration-046-seed-conversation-starters-callsite.test.ts +185 -0
  267. package/src/__tests__/workspace-migration-049-release-notes-default-sonnet.test.ts +100 -0
  268. package/src/__tests__/workspace-migration-050-seed-main-agent-opus-callsite.test.ts +171 -0
  269. package/src/__tests__/workspace-migration-051-seed-conversation-summarization-callsite.test.ts +252 -0
  270. package/src/__tests__/workspace-migration-drop-user-md.test.ts +11 -11
  271. package/src/__tests__/workspace-migration-remove-hooks.test.ts +99 -0
  272. package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +841 -0
  273. package/src/__tests__/workspace-policy.test.ts +22 -16
  274. package/src/acp/client-handler.ts +1 -2
  275. package/src/agent/loop.ts +545 -115
  276. package/src/approvals/__tests__/guardian-feed-event.test.ts +304 -0
  277. package/src/approvals/guardian-request-resolvers.ts +80 -0
  278. package/src/avatar/resvg-lazy.test.ts +136 -0
  279. package/src/avatar/resvg-lazy.ts +82 -9
  280. package/src/avatar/traits-png-sync.ts +21 -1
  281. package/src/backup/__tests__/backup-worker.test.ts +2 -13
  282. package/src/backup/backup-worker.ts +3 -15
  283. package/src/browser/__tests__/operations.test.ts +163 -0
  284. package/src/browser/identifiers.ts +51 -0
  285. package/src/browser/operations.ts +660 -0
  286. package/src/browser/types.ts +81 -0
  287. package/src/bundler/app-compiler.ts +84 -1
  288. package/src/calls/call-state.ts +2 -2
  289. package/src/calls/guardian-question-copy.ts +2 -2
  290. package/src/calls/telephony-stt-routing.ts +1 -1
  291. package/src/calls/voice-session-bridge.ts +1 -0
  292. package/src/channels/__tests__/types.test.ts +3 -3
  293. package/src/channels/types.ts +6 -4
  294. package/src/cli/AGENTS.md +1 -1
  295. package/src/cli/__tests__/notifications.test.ts +87 -211
  296. package/src/cli/commands/__tests__/attachment.test.ts +438 -0
  297. package/src/cli/commands/__tests__/backup.test.ts +1 -1
  298. package/src/cli/commands/__tests__/browser.test.ts +554 -0
  299. package/src/cli/commands/__tests__/cache.test.ts +623 -0
  300. package/src/cli/commands/__tests__/email-list.test.ts +6 -0
  301. package/src/cli/commands/__tests__/email-send.test.ts +93 -1
  302. package/src/cli/commands/__tests__/image-generation.test.ts +886 -0
  303. package/src/cli/commands/__tests__/inference-send.test.ts +463 -0
  304. package/src/cli/commands/__tests__/stt-transcribe.test.ts +454 -0
  305. package/src/cli/commands/__tests__/task.test.ts +913 -0
  306. package/src/cli/commands/__tests__/tts-synthesize.test.ts +606 -0
  307. package/src/cli/commands/__tests__/ui-confirm.test.ts +650 -0
  308. package/src/cli/commands/__tests__/ui.test.ts +1215 -0
  309. package/src/cli/commands/__tests__/watchers.test.ts +716 -0
  310. package/src/cli/commands/attachment.ts +182 -0
  311. package/src/cli/commands/backup.ts +2 -2
  312. package/src/cli/commands/browser.ts +350 -0
  313. package/src/cli/commands/cache.ts +341 -0
  314. package/src/cli/commands/clients.ts +138 -0
  315. package/src/cli/commands/completions.ts +2 -12
  316. package/src/cli/commands/config.ts +6 -6
  317. package/src/cli/commands/conversations-import.ts +347 -0
  318. package/src/cli/commands/conversations.ts +69 -8
  319. package/src/cli/commands/email.ts +234 -194
  320. package/src/cli/commands/image-generation.ts +299 -0
  321. package/src/cli/commands/inference.ts +200 -0
  322. package/src/cli/commands/memory.ts +127 -17
  323. package/src/cli/commands/notifications.ts +68 -103
  324. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -1
  325. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -1
  326. package/src/cli/commands/oauth/connect.ts +2 -2
  327. package/src/cli/commands/oauth/providers.ts +176 -8
  328. package/src/cli/commands/oauth/status.ts +46 -36
  329. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -1
  330. package/src/cli/commands/platform/__tests__/connect.test.ts +0 -1
  331. package/src/cli/commands/platform/__tests__/disconnect.test.ts +0 -1
  332. package/src/cli/commands/platform/__tests__/status.test.ts +0 -1
  333. package/src/cli/commands/skills.ts +3 -4
  334. package/src/cli/commands/stt.ts +339 -0
  335. package/src/cli/commands/task.ts +795 -0
  336. package/src/cli/commands/trust.ts +50 -19
  337. package/src/cli/commands/tts.ts +273 -0
  338. package/src/cli/commands/ui.ts +670 -0
  339. package/src/cli/commands/watchers.ts +509 -0
  340. package/src/cli/lib/daemon-credential-client.ts +0 -19
  341. package/src/cli/program.ts +39 -24
  342. package/src/cli.ts +0 -37
  343. package/src/config/__tests__/backup-schema.test.ts +7 -2
  344. package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
  345. package/src/config/bundled-skills/app-builder/references/WIDGETS.md +10 -10
  346. package/src/config/bundled-skills/contacts/tools/contact-merge.ts +66 -87
  347. package/src/config/bundled-skills/contacts/tools/contact-search.ts +28 -51
  348. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +22 -40
  349. package/src/config/bundled-skills/image-studio/SKILL.md +2 -1
  350. package/src/config/bundled-skills/image-studio/TOOLS.json +2 -1
  351. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +23 -39
  352. package/src/config/bundled-skills/media-processing/services/reduce.ts +1 -1
  353. package/src/config/bundled-skills/messaging/SKILL.md +5 -5
  354. package/src/config/bundled-skills/messaging/TOOLS.json +4 -0
  355. package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +207 -0
  356. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +20 -1
  357. package/src/config/bundled-skills/messaging/tools/messaging-read.ts +15 -1
  358. package/src/config/bundled-skills/messaging/tools/messaging-search.ts +21 -1
  359. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +69 -12
  360. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +9 -8
  361. package/src/config/bundled-skills/schedule/SKILL.md +8 -3
  362. package/src/config/bundled-skills/schedule/TOOLS.json +15 -7
  363. package/src/config/bundled-skills/schedule/references/SCRIPT_MODE_PATTERNS.md +59 -0
  364. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  365. package/src/config/bundled-tool-registry.ts +0 -190
  366. package/src/config/env.ts +7 -2
  367. package/src/config/feature-flag-registry.json +42 -10
  368. package/src/config/llm-resolver.ts +128 -0
  369. package/src/config/loader.ts +194 -10
  370. package/src/config/raw-config-utils.ts +30 -2
  371. package/src/config/sanitize-for-transfer.ts +35 -0
  372. package/src/config/schema.ts +49 -41
  373. package/src/config/schemas/analysis.ts +3 -22
  374. package/src/config/schemas/backup.ts +1 -1
  375. package/src/config/schemas/calls.ts +0 -4
  376. package/src/config/schemas/conversations.ts +16 -0
  377. package/src/config/schemas/filing.ts +2 -7
  378. package/src/config/schemas/heartbeat.ts +0 -5
  379. package/src/config/schemas/inference.ts +3 -23
  380. package/src/config/schemas/llm.ts +317 -0
  381. package/src/config/schemas/memory-processing.ts +1 -9
  382. package/src/config/schemas/notifications.ts +4 -11
  383. package/src/config/schemas/platform.ts +3 -9
  384. package/src/config/schemas/security.ts +33 -0
  385. package/src/config/schemas/services.ts +9 -4
  386. package/src/config/schemas/stt.ts +1 -0
  387. package/src/config/schemas/tts.ts +64 -0
  388. package/src/config/schemas/updates.ts +1 -1
  389. package/src/config/schemas/workspace-git.ts +3 -40
  390. package/src/config/skill-state.ts +6 -2
  391. package/src/config/skills.ts +96 -7
  392. package/src/context/__tests__/compact-prompt.test.ts +63 -0
  393. package/src/context/__tests__/microcompact.test.ts +805 -0
  394. package/src/context/estimator-calibration.ts +136 -0
  395. package/src/context/microcompact.ts +443 -0
  396. package/src/context/prompts/compact.md +26 -0
  397. package/src/context/token-estimator.ts +61 -3
  398. package/src/context/tool-result-truncation.ts +3 -63
  399. package/src/context/window-manager.ts +417 -39
  400. package/src/credential-execution/approval-bridge.ts +0 -1
  401. package/src/credential-execution/executable-discovery.ts +19 -8
  402. package/src/credential-execution/process-manager.test.ts +109 -0
  403. package/src/credential-execution/process-manager.ts +65 -2
  404. package/src/credential-health/credential-health-service.ts +19 -6
  405. package/src/daemon/__tests__/conversation-feed-event.test.ts +317 -0
  406. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +4 -12
  407. package/src/daemon/__tests__/conversation-tool-setup.test.ts +14 -15
  408. package/src/daemon/approval-generators.ts +29 -4
  409. package/src/daemon/assistant-attachments.ts +24 -13
  410. package/src/daemon/classifier.ts +2 -2
  411. package/src/daemon/config-watcher.ts +0 -3
  412. package/src/daemon/context-overflow-policy.ts +4 -13
  413. package/src/daemon/context-overflow-reducer.ts +4 -1
  414. package/src/daemon/conversation-agent-loop-handlers.ts +162 -34
  415. package/src/daemon/conversation-agent-loop.ts +1282 -599
  416. package/src/daemon/conversation-attachments.ts +2 -6
  417. package/src/daemon/conversation-error.ts +36 -1
  418. package/src/daemon/conversation-history.ts +10 -19
  419. package/src/daemon/conversation-lifecycle.ts +59 -17
  420. package/src/daemon/conversation-messaging.ts +73 -4
  421. package/src/daemon/conversation-notifiers.ts +2 -110
  422. package/src/daemon/conversation-process.ts +24 -11
  423. package/src/daemon/conversation-queue-manager.ts +3 -0
  424. package/src/daemon/conversation-runtime-assembly.ts +1063 -211
  425. package/src/daemon/conversation-slash.ts +2 -2
  426. package/src/daemon/conversation-surfaces.ts +389 -1
  427. package/src/daemon/conversation-tool-setup.ts +51 -9
  428. package/src/daemon/conversation-usage.ts +1 -1
  429. package/src/daemon/conversation.ts +197 -64
  430. package/src/daemon/external-plugins-bootstrap.ts +478 -0
  431. package/src/daemon/external-skills-bootstrap.ts +41 -0
  432. package/src/daemon/first-greeting.ts +191 -14
  433. package/src/daemon/guardian-action-generators.ts +34 -14
  434. package/src/daemon/handlers/config-model.test.ts +86 -0
  435. package/src/daemon/handlers/config-model.ts +65 -12
  436. package/src/daemon/handlers/conversations.ts +9 -2
  437. package/src/daemon/handlers/shared.ts +39 -11
  438. package/src/daemon/handlers/skills.ts +7 -3
  439. package/src/daemon/handlers/slack-channel-oauth-install.ts +197 -0
  440. package/src/daemon/lifecycle.ts +109 -82
  441. package/src/daemon/message-types/computer-use.ts +2 -34
  442. package/src/daemon/message-types/conversations.ts +63 -0
  443. package/src/daemon/message-types/messages.ts +21 -1
  444. package/src/daemon/message-types/trust.ts +0 -2
  445. package/src/daemon/parse-actual-tokens-from-error.test.ts +57 -1
  446. package/src/daemon/parse-actual-tokens-from-error.ts +66 -0
  447. package/src/daemon/pkb-context-tracker.test.ts +169 -0
  448. package/src/daemon/pkb-context-tracker.ts +125 -0
  449. package/src/daemon/pkb-reminder-builder.test.ts +70 -0
  450. package/src/daemon/pkb-reminder-builder.ts +31 -0
  451. package/src/daemon/providers-setup.ts +6 -0
  452. package/src/daemon/server.ts +122 -12
  453. package/src/daemon/shutdown-handlers.ts +2 -12
  454. package/src/daemon/tool-side-effects.ts +14 -65
  455. package/src/daemon/web-search-history.ts +126 -0
  456. package/src/events/domain-events.ts +0 -1
  457. package/src/filing/filing-service.ts +9 -10
  458. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +160 -0
  459. package/src/heartbeat/heartbeat-service.ts +99 -28
  460. package/src/home/__tests__/feed-population-integration.test.ts +312 -0
  461. package/src/home/__tests__/feed-scheduler.test.ts +39 -11
  462. package/src/home/__tests__/rollup-producer.test.ts +44 -0
  463. package/src/home/assistant-feed-authoring.ts +4 -0
  464. package/src/home/emit-feed-event.ts +11 -0
  465. package/src/home/feed-scheduler.ts +20 -4
  466. package/src/home/feed-types.ts +97 -4
  467. package/src/home/relationship-state-writer.ts +2 -2
  468. package/src/home/rewrite-command-preview.ts +66 -0
  469. package/src/home/rollup-producer.ts +34 -5
  470. package/src/home/suggested-prompts.ts +101 -0
  471. package/src/ipc/__tests__/attachment-ipc.test.ts +213 -0
  472. package/src/ipc/__tests__/browser-ipc.test.ts +339 -0
  473. package/src/ipc/__tests__/cache-ipc.test.ts +266 -0
  474. package/src/ipc/__tests__/socket-path.test.ts +34 -0
  475. package/src/ipc/__tests__/task-ipc.test.ts +577 -0
  476. package/src/ipc/__tests__/ui-request-route.test.ts +495 -0
  477. package/src/ipc/__tests__/watcher-ipc.test.ts +295 -0
  478. package/src/ipc/cli-client.ts +2 -1
  479. package/src/ipc/cli-server.ts +26 -8
  480. package/src/ipc/gateway-client.ts +6 -3
  481. package/src/ipc/routes/attachment.ts +114 -0
  482. package/src/ipc/routes/browser-context.ts +63 -0
  483. package/src/ipc/routes/browser.ts +97 -0
  484. package/src/ipc/routes/cache.ts +96 -0
  485. package/src/ipc/routes/get-contact.ts +16 -0
  486. package/src/ipc/routes/index.ts +31 -1
  487. package/src/ipc/routes/list-clients.ts +31 -0
  488. package/src/ipc/routes/merge-contacts.ts +17 -0
  489. package/src/ipc/routes/notification.ts +133 -0
  490. package/src/ipc/routes/rename-conversation.ts +59 -0
  491. package/src/ipc/routes/search-contacts.ts +19 -0
  492. package/src/ipc/routes/task-queue.ts +226 -0
  493. package/src/ipc/routes/task.ts +173 -0
  494. package/src/ipc/routes/ui-request.ts +50 -0
  495. package/src/ipc/routes/upsert-contact.ts +25 -0
  496. package/src/ipc/routes/watcher.ts +203 -0
  497. package/src/ipc/socket-path.ts +76 -0
  498. package/src/media/app-icon-generator.ts +23 -46
  499. package/src/media/avatar-router.ts +26 -41
  500. package/src/media/gemini-image-service.ts +8 -41
  501. package/src/media/image-credentials.ts +73 -0
  502. package/src/media/image-service.ts +85 -0
  503. package/src/media/openai-image-service.ts +131 -0
  504. package/src/media/types.ts +46 -0
  505. package/src/memory/__tests__/conversation-analyze-job.test.ts +9 -8
  506. package/src/memory/__tests__/conversation-group-migration.test.ts +99 -0
  507. package/src/memory/admin.ts +18 -0
  508. package/src/memory/conversation-analyze-job.ts +14 -13
  509. package/src/memory/conversation-attention-store.ts +13 -6
  510. package/src/memory/conversation-crud.ts +133 -3
  511. package/src/memory/conversation-group-migration.ts +38 -6
  512. package/src/memory/conversation-queries.ts +57 -4
  513. package/src/memory/conversation-title-service.ts +32 -4
  514. package/src/memory/db-init.ts +10 -0
  515. package/src/memory/embedding-backend.ts +1 -1
  516. package/src/memory/embedding-gemini.test.ts +41 -2
  517. package/src/memory/embedding-gemini.ts +6 -1
  518. package/src/memory/graph/bootstrap.test.ts +282 -0
  519. package/src/memory/graph/bootstrap.ts +8 -5
  520. package/src/memory/graph/compaction.ts +299 -0
  521. package/src/memory/graph/consolidation.ts +4 -4
  522. package/src/memory/graph/conversation-graph-memory.ts +89 -29
  523. package/src/memory/graph/extraction.test.ts +272 -2
  524. package/src/memory/graph/extraction.ts +183 -53
  525. package/src/memory/graph/graph-search.test.ts +93 -0
  526. package/src/memory/graph/graph-search.ts +4 -1
  527. package/src/memory/graph/inspect.ts +2 -2
  528. package/src/memory/graph/narrative.ts +2 -2
  529. package/src/memory/graph/pattern-scan.ts +2 -2
  530. package/src/memory/graph/retriever.test.ts +459 -0
  531. package/src/memory/graph/retriever.ts +237 -48
  532. package/src/memory/graph/store.ts +41 -0
  533. package/src/memory/graph/tool-handlers.ts +27 -0
  534. package/src/memory/graph/tools.ts +6 -1
  535. package/src/memory/indexer.ts +5 -5
  536. package/src/memory/job-handlers/conversation-starters.ts +23 -20
  537. package/src/memory/job-handlers/summarization.ts +2 -2
  538. package/src/memory/job-utils.ts +7 -1
  539. package/src/memory/jobs/embed-pkb-file.test.ts +168 -0
  540. package/src/memory/jobs/embed-pkb-file.ts +54 -0
  541. package/src/memory/jobs-store.ts +44 -3
  542. package/src/memory/jobs-worker.ts +4 -0
  543. package/src/memory/migrations/041-approval-prompt-ts-tracker.ts +26 -0
  544. package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +1 -1
  545. package/src/memory/migrations/149-oauth-tables.ts +1 -0
  546. package/src/memory/migrations/220-normalize-user-file-by-principal.ts +2 -2
  547. package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +82 -0
  548. package/src/memory/migrations/223-schedule-script-column.ts +11 -0
  549. package/src/memory/migrations/224-oauth-providers-managed-service-is-paid.ts +24 -0
  550. package/src/memory/migrations/225-oauth-providers-available-scopes.ts +13 -0
  551. package/src/memory/migrations/index.ts +5 -0
  552. package/src/memory/pkb/pkb-index.test.ts +369 -0
  553. package/src/memory/pkb/pkb-index.ts +255 -0
  554. package/src/memory/pkb/pkb-reconcile.test.ts +252 -0
  555. package/src/memory/pkb/pkb-reconcile.ts +148 -0
  556. package/src/memory/pkb/pkb-search.test.ts +499 -0
  557. package/src/memory/pkb/pkb-search.ts +159 -0
  558. package/src/memory/pkb/types.ts +53 -0
  559. package/src/memory/qdrant-client.test.ts +60 -0
  560. package/src/memory/qdrant-client.ts +147 -1
  561. package/src/memory/schema/infrastructure.ts +1 -0
  562. package/src/memory/schema/oauth.ts +4 -1
  563. package/src/memory/slack-thread-store.ts +37 -0
  564. package/src/messaging/providers/gmail/adapter.ts +6 -16
  565. package/src/messaging/providers/gmail/client.ts +22 -0
  566. package/src/messaging/providers/gmail/types.ts +7 -0
  567. package/src/messaging/providers/slack/adapter.ts +14 -2
  568. package/src/messaging/providers/slack/backfill.test.ts +257 -0
  569. package/src/messaging/providers/slack/backfill.ts +101 -0
  570. package/src/messaging/providers/slack/message-metadata.test.ts +316 -0
  571. package/src/messaging/providers/slack/message-metadata.ts +123 -0
  572. package/src/messaging/providers/slack/render-transcript.test.ts +1421 -0
  573. package/src/messaging/providers/slack/render-transcript.ts +501 -0
  574. package/src/messaging/style-analyzer.ts +5 -2
  575. package/src/notifications/README.md +9 -5
  576. package/src/notifications/conversation-pairing.ts +78 -19
  577. package/src/notifications/copy-composer.ts +0 -5
  578. package/src/notifications/decision-engine.ts +3 -9
  579. package/src/notifications/emit-signal.ts +1 -1
  580. package/src/notifications/preference-extractor.ts +2 -6
  581. package/src/notifications/signal.ts +1 -2
  582. package/src/oauth/AGENTS.md +1 -1
  583. package/src/oauth/__tests__/identity-verifier.test.ts +2 -1
  584. package/src/oauth/connect-orchestrator.ts +8 -34
  585. package/src/oauth/connect-types.ts +6 -10
  586. package/src/oauth/manual-token-connection.ts +23 -0
  587. package/src/oauth/oauth-store.ts +31 -14
  588. package/src/oauth/platform-connection.test.ts +47 -0
  589. package/src/oauth/platform-connection.ts +15 -5
  590. package/src/oauth/provider-serializer.ts +6 -1
  591. package/src/oauth/seed-providers.ts +56 -106
  592. package/src/outbound-proxy/http-forwarder.ts +9 -0
  593. package/src/permissions/approval-policy.test.ts +1223 -0
  594. package/src/permissions/approval-policy.ts +309 -0
  595. package/src/permissions/arg-parser.test.ts +161 -0
  596. package/src/permissions/arg-parser.ts +141 -0
  597. package/src/permissions/bash-risk-classifier.test.ts +1620 -0
  598. package/src/permissions/bash-risk-classifier.ts +950 -0
  599. package/src/permissions/checker.ts +348 -711
  600. package/src/permissions/command-registry.test.ts +774 -0
  601. package/src/permissions/command-registry.ts +1005 -0
  602. package/src/permissions/defaults.ts +28 -79
  603. package/src/permissions/file-risk-classifier.test.ts +535 -0
  604. package/src/permissions/file-risk-classifier.ts +274 -0
  605. package/src/permissions/gateway-threshold-reader.ts +196 -0
  606. package/src/permissions/prompter.ts +4 -0
  607. package/src/permissions/risk-types.ts +262 -0
  608. package/src/permissions/schedule-risk-classifier.test.ts +129 -0
  609. package/src/permissions/schedule-risk-classifier.ts +85 -0
  610. package/src/permissions/secret-prompter.ts +53 -2
  611. package/src/permissions/shell-identity.ts +2 -42
  612. package/src/permissions/skill-risk-classifier.test.ts +311 -0
  613. package/src/permissions/skill-risk-classifier.ts +214 -0
  614. package/src/permissions/trust-client.ts +52 -25
  615. package/src/permissions/trust-store-interface.ts +1 -6
  616. package/src/permissions/trust-store.ts +161 -62
  617. package/src/permissions/types.ts +25 -14
  618. package/src/permissions/web-risk-classifier.test.ts +170 -0
  619. package/src/permissions/web-risk-classifier.ts +89 -0
  620. package/src/permissions/workspace-policy.ts +9 -19
  621. package/src/platform/client.ts +19 -1
  622. package/src/plugins/defaults/circuit-breaker.ts +146 -0
  623. package/src/plugins/defaults/compaction.ts +145 -0
  624. package/src/plugins/defaults/empty-response.ts +126 -0
  625. package/src/plugins/defaults/history-repair.ts +85 -0
  626. package/src/plugins/defaults/index.ts +116 -0
  627. package/src/plugins/defaults/injectors.ts +491 -0
  628. package/src/plugins/defaults/llm-call.ts +82 -0
  629. package/src/plugins/defaults/memory-retrieval.ts +226 -0
  630. package/src/plugins/defaults/overflow-reduce.ts +181 -0
  631. package/src/plugins/defaults/persistence.ts +129 -0
  632. package/src/plugins/defaults/title-generate.ts +95 -0
  633. package/src/plugins/defaults/token-estimate.ts +104 -0
  634. package/src/plugins/defaults/tool-error.ts +126 -0
  635. package/src/plugins/defaults/tool-execute.ts +89 -0
  636. package/src/plugins/defaults/tool-result-truncate.ts +88 -0
  637. package/src/plugins/pipeline.ts +316 -0
  638. package/src/plugins/plugin-skill-contributions.ts +292 -0
  639. package/src/plugins/registry.ts +241 -0
  640. package/src/plugins/types.ts +1134 -0
  641. package/src/plugins/user-loader.ts +177 -0
  642. package/src/prompts/persona-resolver.ts +3 -3
  643. package/src/prompts/system-prompt.ts +19 -20
  644. package/src/prompts/templates/BOOTSTRAP.md +27 -77
  645. package/src/prompts/templates/SOUL.md +2 -2
  646. package/src/prompts/update-bulletin-job.ts +190 -0
  647. package/src/providers/__tests__/context-overflow-error.test.ts +328 -0
  648. package/src/providers/__tests__/provider-env-vars.test.ts +102 -0
  649. package/src/providers/__tests__/retry-callsite.test.ts +424 -0
  650. package/src/providers/anthropic/client.ts +183 -14
  651. package/src/providers/call-site-routing.ts +71 -0
  652. package/src/providers/gemini/client.ts +65 -2
  653. package/src/providers/managed-proxy/constants.ts +2 -1
  654. package/src/providers/model-catalog.ts +524 -33
  655. package/src/providers/model-intents.ts +4 -4
  656. package/src/providers/openai/chat-completions-provider.ts +57 -1
  657. package/src/providers/openai/responses-provider.ts +86 -9
  658. package/src/providers/openrouter/client.ts +80 -9
  659. package/src/providers/provider-env-vars.ts +56 -0
  660. package/src/providers/provider-send-message.ts +22 -5
  661. package/src/providers/ratelimit.ts +4 -0
  662. package/src/providers/registry.ts +19 -8
  663. package/src/providers/retry.ts +174 -39
  664. package/src/providers/speech-to-text/__tests__/resolve.test.ts +55 -0
  665. package/src/providers/speech-to-text/deepgram-realtime.test.ts +61 -0
  666. package/src/providers/speech-to-text/deepgram-realtime.ts +57 -0
  667. package/src/providers/speech-to-text/google-gemini-live-stream.ts +4 -4
  668. package/src/providers/speech-to-text/provider-catalog.ts +17 -0
  669. package/src/providers/speech-to-text/resolve.ts +7 -0
  670. package/src/providers/speech-to-text/xai-realtime.test.ts +646 -0
  671. package/src/providers/speech-to-text/xai-realtime.ts +821 -0
  672. package/src/providers/speech-to-text/xai.test.ts +155 -0
  673. package/src/providers/speech-to-text/xai.ts +97 -0
  674. package/src/providers/types.ts +93 -3
  675. package/src/runtime/AGENTS.md +27 -18
  676. package/src/runtime/__tests__/agent-wake.test.ts +43 -2
  677. package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +3 -3
  678. package/src/runtime/__tests__/client-registry.test.ts +293 -0
  679. package/src/runtime/__tests__/interactive-ui.test.ts +673 -0
  680. package/src/runtime/agent-wake.ts +63 -22
  681. package/src/runtime/auth/route-policy.ts +4 -0
  682. package/src/runtime/btw-sidechain.ts +13 -3
  683. package/src/runtime/channel-reply-delivery.ts +106 -2
  684. package/src/runtime/client-registry.ts +261 -0
  685. package/src/runtime/decision-token.ts +116 -0
  686. package/src/runtime/gateway-client.ts +2 -2
  687. package/src/runtime/http-router.ts +32 -0
  688. package/src/runtime/http-server.ts +129 -9
  689. package/src/runtime/http-types.ts +23 -3
  690. package/src/runtime/interactive-ui.ts +362 -0
  691. package/src/runtime/invite-instruction-generator.ts +2 -2
  692. package/src/runtime/migrations/__tests__/gcs-signed-url.test.ts +176 -0
  693. package/src/runtime/migrations/__tests__/vbundle-metadata-merge-integration.test.ts +390 -0
  694. package/src/runtime/migrations/__tests__/vbundle-metadata-merge.test.ts +221 -0
  695. package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +1540 -0
  696. package/src/runtime/migrations/__tests__/vbundle-streaming-validator.test.ts +453 -0
  697. package/src/runtime/migrations/__tests__/vbundle-tar-stream.test.ts +222 -0
  698. package/src/runtime/migrations/gcs-signed-url.ts +162 -0
  699. package/src/runtime/migrations/vbundle-builder.ts +1 -22
  700. package/src/runtime/migrations/vbundle-importer.ts +154 -9
  701. package/src/runtime/migrations/vbundle-metadata-merge.ts +124 -0
  702. package/src/runtime/migrations/vbundle-streaming-importer.ts +2522 -0
  703. package/src/runtime/migrations/vbundle-streaming-validator.ts +244 -0
  704. package/src/runtime/migrations/vbundle-tar-stream.ts +217 -0
  705. package/src/runtime/migrations/vbundle-validator.ts +15 -6
  706. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +111 -0
  707. package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +114 -75
  708. package/src/runtime/routes/__tests__/migration-vellum-metadata-reconcile.test.ts +246 -0
  709. package/src/runtime/routes/approval-prompt-ts-tracker.ts +78 -0
  710. package/src/runtime/routes/approval-routes.ts +29 -17
  711. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +9 -0
  712. package/src/runtime/routes/avatar-routes.ts +20 -4
  713. package/src/runtime/routes/browser-extension-pair-routes.ts +27 -8
  714. package/src/runtime/routes/btw-routes.ts +1 -4
  715. package/src/runtime/routes/conversation-management-routes.ts +20 -2
  716. package/src/runtime/routes/conversation-routes.ts +351 -138
  717. package/src/runtime/routes/debug-routes.ts +1 -1
  718. package/src/runtime/routes/diagnostics-routes.ts +6 -4
  719. package/src/runtime/routes/events-routes.ts +16 -0
  720. package/src/runtime/routes/guardian-approval-interception.ts +33 -3
  721. package/src/runtime/routes/guardian-approval-prompt.ts +13 -3
  722. package/src/runtime/routes/home-feed-routes.ts +120 -2
  723. package/src/runtime/routes/inbound-message-handler.ts +987 -2
  724. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +113 -2
  725. package/src/runtime/routes/inbound-stages/background-dispatch.ts +61 -3
  726. package/src/runtime/routes/inbound-stages/edit-intercept.ts +129 -6
  727. package/src/runtime/routes/integrations/slack/channel.ts +25 -3
  728. package/src/runtime/routes/llm-context-normalization.ts +23 -1
  729. package/src/runtime/routes/memory-item-routes.test.ts +1 -0
  730. package/src/runtime/routes/migration-routes.ts +720 -127
  731. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +284 -0
  732. package/src/runtime/routes/playground/__tests__/guard.test.ts +80 -0
  733. package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +294 -0
  734. package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +271 -0
  735. package/src/runtime/routes/playground/__tests__/seed-conversation.test.ts +202 -0
  736. package/src/runtime/routes/playground/__tests__/seeded-conversations.test.ts +309 -0
  737. package/src/runtime/routes/playground/__tests__/state.test.ts +224 -0
  738. package/src/runtime/routes/playground/conversation-not-found.ts +29 -0
  739. package/src/runtime/routes/playground/deps.ts +56 -0
  740. package/src/runtime/routes/playground/force-compact.ts +73 -0
  741. package/src/runtime/routes/playground/guard.ts +37 -0
  742. package/src/runtime/routes/playground/index.ts +28 -0
  743. package/src/runtime/routes/playground/inject-failures.ts +159 -0
  744. package/src/runtime/routes/playground/reset-circuit.ts +115 -0
  745. package/src/runtime/routes/playground/seed-conversation.ts +139 -0
  746. package/src/runtime/routes/playground/seeded-conversations.ts +78 -0
  747. package/src/runtime/routes/playground/state.ts +78 -0
  748. package/src/runtime/routes/schedule-routes.ts +89 -8
  749. package/src/runtime/routes/settings-routes.ts +4 -2
  750. package/src/runtime/routes/trust-rules-routes.ts +30 -14
  751. package/src/runtime/routes/work-items-routes.test.ts +1 -1
  752. package/src/runtime/routes/work-items-routes.ts +3 -2
  753. package/src/runtime/services/__tests__/analyze-conversation.test.ts +25 -43
  754. package/src/runtime/services/analyze-conversation.ts +12 -16
  755. package/src/runtime/skill-route-registry.ts +97 -15
  756. package/src/schedule/run-script.ts +68 -0
  757. package/src/schedule/schedule-store.ts +7 -1
  758. package/src/schedule/scheduler.ts +56 -8
  759. package/src/security/__tests__/provider-key-env-fallback.test.ts +119 -0
  760. package/src/security/__tests__/untrusted-content.test.ts +109 -0
  761. package/src/security/oauth2.ts +98 -35
  762. package/src/security/secure-keys.ts +7 -8
  763. package/src/security/token-manager.ts +27 -13
  764. package/src/security/untrusted-content.ts +102 -0
  765. package/src/skills/catalog-cache.ts +35 -9
  766. package/src/skills/catalog-install.ts +31 -3
  767. package/src/skills/skill-cache-store.ts +97 -0
  768. package/src/stt/__tests__/daemon-batch-transcriber.test.ts +76 -0
  769. package/src/stt/daemon-batch-transcriber.ts +33 -0
  770. package/src/stt/stt-stream-session.ts +8 -1
  771. package/src/stt/types.ts +5 -1
  772. package/src/subagent/manager.ts +41 -13
  773. package/src/tasks/ephemeral-permissions.ts +9 -4
  774. package/src/telemetry/usage-telemetry-reporter.ts +27 -5
  775. package/src/tools/browser/__tests__/browser-status.test.ts +234 -2
  776. package/src/tools/browser/browser-execution.ts +150 -54
  777. package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +230 -0
  778. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +146 -3
  779. package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +22 -0
  780. package/src/tools/browser/cdp-client/extension-cdp-client.ts +54 -3
  781. package/src/tools/browser/cdp-client/factory.ts +15 -4
  782. package/src/tools/credentials/tool-policy.ts +39 -5
  783. package/src/tools/credentials/vault.ts +9 -4
  784. package/src/tools/executor.ts +129 -73
  785. package/src/tools/filesystem/write.ts +52 -0
  786. package/src/tools/host-terminal/host-shell.ts +45 -5
  787. package/src/tools/memory/register.test.ts +185 -0
  788. package/src/tools/memory/register.ts +3 -1
  789. package/src/tools/network/script-proxy/session-manager.ts +37 -1
  790. package/src/tools/network/web-fetch.ts +20 -10
  791. package/src/tools/network/web-search.ts +19 -4
  792. package/src/tools/permission-checker.ts +116 -46
  793. package/src/tools/policy-context.ts +29 -8
  794. package/src/tools/registry.ts +195 -6
  795. package/src/tools/schedule/create.ts +23 -8
  796. package/src/tools/schedule/update.ts +3 -1
  797. package/src/tools/secret-detection-handler.ts +0 -51
  798. package/src/tools/side-effects.ts +0 -11
  799. package/src/tools/skills/execute.ts +2 -2
  800. package/src/tools/skills/sandbox-runner.ts +5 -2
  801. package/src/tools/system/avatar-generator.ts +6 -2
  802. package/src/tools/terminal/backends/native.ts +51 -2
  803. package/src/tools/terminal/safe-env.ts +3 -2
  804. package/src/tools/terminal/shell.ts +1 -0
  805. package/src/tools/tool-manifest.ts +6 -21
  806. package/src/tools/types.ts +40 -5
  807. package/src/tools/verification-control-plane-policy.ts +1 -1
  808. package/src/tts/__tests__/provider-adapters.test.ts +240 -13
  809. package/src/tts/provider-catalog.ts +18 -0
  810. package/src/tts/providers/index.ts +2 -0
  811. package/src/tts/providers/xai-provider.ts +224 -0
  812. package/src/tts/types.ts +46 -0
  813. package/src/types/tar-stream.d.ts +66 -0
  814. package/src/util/json.ts +17 -0
  815. package/src/util/platform.ts +9 -4
  816. package/src/util/pricing.ts +41 -8
  817. package/src/watcher/engine.ts +1 -1
  818. package/src/watcher/providers/google-calendar.ts +134 -8
  819. package/src/watcher/providers/outlook-calendar.ts +42 -2
  820. package/src/workspace/git-service.ts +23 -4
  821. package/src/workspace/migrations/006-services-config.ts +2 -4
  822. package/src/workspace/migrations/022-move-hooks-to-workspace.ts +2 -3
  823. package/src/workspace/migrations/038-unify-llm-callsite-configs.ts +516 -0
  824. package/src/workspace/migrations/039-drop-legacy-llm-keys.ts +171 -0
  825. package/src/workspace/migrations/040-seed-latency-callsite-defaults.ts +154 -0
  826. package/src/workspace/migrations/041-backfill-google-gmail-settings-scope.ts +56 -0
  827. package/src/workspace/migrations/042-fix-backfill-google-gmail-settings-scope.ts +70 -0
  828. package/src/workspace/migrations/043-release-notes-latex-rendering.ts +75 -0
  829. package/src/workspace/migrations/044-bump-stale-provider-stream-timeout.ts +51 -0
  830. package/src/workspace/migrations/045-release-notes-meet-avatar.ts +130 -0
  831. package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +108 -0
  832. package/src/workspace/migrations/047-remove-watch-callsites.ts +54 -0
  833. package/src/workspace/migrations/048-remove-workspace-hooks.ts +81 -0
  834. package/src/workspace/migrations/049-release-notes-default-sonnet.ts +80 -0
  835. package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +86 -0
  836. package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +128 -0
  837. package/src/workspace/migrations/AGENTS.md +1 -1
  838. package/src/workspace/migrations/registry.ts +28 -0
  839. package/src/workspace/provider-commit-message-generator.ts +19 -38
  840. package/tsconfig.json +1 -1
  841. package/hook-templates/debug-prompt-logger/hook.json +0 -7
  842. package/hook-templates/debug-prompt-logger/run.sh +0 -66
  843. package/src/__tests__/context-overflow-approval.test.ts +0 -156
  844. package/src/__tests__/gmail-archive-fallback.test.ts +0 -193
  845. package/src/__tests__/gmail-archive-gate.test.ts +0 -246
  846. package/src/__tests__/gmail-preferences.test.ts +0 -117
  847. package/src/__tests__/hooks-blocking.test.ts +0 -178
  848. package/src/__tests__/hooks-cli.test.ts +0 -182
  849. package/src/__tests__/hooks-config.test.ts +0 -108
  850. package/src/__tests__/hooks-discovery.test.ts +0 -211
  851. package/src/__tests__/hooks-integration.test.ts +0 -196
  852. package/src/__tests__/hooks-manager.test.ts +0 -226
  853. package/src/__tests__/hooks-runner.test.ts +0 -175
  854. package/src/__tests__/hooks-settings.test.ts +0 -160
  855. package/src/__tests__/hooks-templates.test.ts +0 -169
  856. package/src/__tests__/hooks-ts-runner.test.ts +0 -170
  857. package/src/__tests__/hooks-watch.test.ts +0 -112
  858. package/src/__tests__/notification-schedule-dedup.test.ts +0 -213
  859. package/src/__tests__/oauth-scope-policy.test.ts +0 -180
  860. package/src/__tests__/outlook-attachments.test.ts +0 -301
  861. package/src/__tests__/outlook-automation-tools.test.ts +0 -425
  862. package/src/__tests__/outlook-categories.test.ts +0 -212
  863. package/src/__tests__/outlook-compose-tools.test.ts +0 -325
  864. package/src/__tests__/outlook-declutter-tools.test.ts +0 -585
  865. package/src/__tests__/outlook-follow-up.test.ts +0 -196
  866. package/src/__tests__/outlook-trash.test.ts +0 -77
  867. package/src/__tests__/outlook-unsubscribe.test.ts +0 -279
  868. package/src/__tests__/send-notification-tool.test.ts +0 -83
  869. package/src/__tests__/update-bulletin-format.test.ts +0 -181
  870. package/src/__tests__/update-bulletin-state.test.ts +0 -135
  871. package/src/__tests__/update-bulletin.test.ts +0 -478
  872. package/src/__tests__/update-template-contract.test.ts +0 -29
  873. package/src/cli/commands/doctor.ts +0 -341
  874. package/src/cli/commands/shotgun.ts +0 -266
  875. package/src/config/bundled-skills/browser/SKILL.md +0 -88
  876. package/src/config/bundled-skills/browser/TOOLS.json +0 -516
  877. package/src/config/bundled-skills/browser/tools/browser-attach.ts +0 -12
  878. package/src/config/bundled-skills/browser/tools/browser-click.ts +0 -12
  879. package/src/config/bundled-skills/browser/tools/browser-close.ts +0 -12
  880. package/src/config/bundled-skills/browser/tools/browser-detach.ts +0 -12
  881. package/src/config/bundled-skills/browser/tools/browser-extract.ts +0 -12
  882. package/src/config/bundled-skills/browser/tools/browser-fill-credential.ts +0 -12
  883. package/src/config/bundled-skills/browser/tools/browser-hover.ts +0 -12
  884. package/src/config/bundled-skills/browser/tools/browser-navigate.ts +0 -12
  885. package/src/config/bundled-skills/browser/tools/browser-press-key.ts +0 -12
  886. package/src/config/bundled-skills/browser/tools/browser-screenshot.ts +0 -12
  887. package/src/config/bundled-skills/browser/tools/browser-scroll.ts +0 -12
  888. package/src/config/bundled-skills/browser/tools/browser-select-option.ts +0 -12
  889. package/src/config/bundled-skills/browser/tools/browser-snapshot.ts +0 -12
  890. package/src/config/bundled-skills/browser/tools/browser-status.ts +0 -12
  891. package/src/config/bundled-skills/browser/tools/browser-type.ts +0 -12
  892. package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +0 -49
  893. package/src/config/bundled-skills/browser/tools/browser-wait-for.ts +0 -12
  894. package/src/config/bundled-skills/chatgpt-import/SKILL.md +0 -27
  895. package/src/config/bundled-skills/chatgpt-import/TOOLS.json +0 -27
  896. package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +0 -378
  897. package/src/config/bundled-skills/conversations/SKILL.md +0 -20
  898. package/src/config/bundled-skills/conversations/TOOLS.json +0 -23
  899. package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +0 -66
  900. package/src/config/bundled-skills/gmail/SKILL.md +0 -221
  901. package/src/config/bundled-skills/gmail/TOOLS.json +0 -588
  902. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +0 -256
  903. package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +0 -112
  904. package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +0 -44
  905. package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +0 -81
  906. package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +0 -108
  907. package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +0 -146
  908. package/src/config/bundled-skills/gmail/tools/gmail-label.ts +0 -53
  909. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +0 -347
  910. package/src/config/bundled-skills/gmail/tools/gmail-preferences-tool.ts +0 -59
  911. package/src/config/bundled-skills/gmail/tools/gmail-preferences.ts +0 -82
  912. package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +0 -26
  913. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +0 -347
  914. package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +0 -29
  915. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +0 -122
  916. package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +0 -67
  917. package/src/config/bundled-skills/gmail/tools/scan-result-store.ts +0 -100
  918. package/src/config/bundled-skills/gmail/tools/shared.ts +0 -47
  919. package/src/config/bundled-skills/google-calendar/SKILL.md +0 -51
  920. package/src/config/bundled-skills/google-calendar/TOOLS.json +0 -226
  921. package/src/config/bundled-skills/google-calendar/calendar-client.ts +0 -223
  922. package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +0 -27
  923. package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +0 -48
  924. package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +0 -19
  925. package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +0 -36
  926. package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +0 -58
  927. package/src/config/bundled-skills/google-calendar/tools/shared.ts +0 -17
  928. package/src/config/bundled-skills/google-calendar/types.ts +0 -97
  929. package/src/config/bundled-skills/heartbeat/SKILL.md +0 -43
  930. package/src/config/bundled-skills/notifications/SKILL.md +0 -40
  931. package/src/config/bundled-skills/notifications/TOOLS.json +0 -80
  932. package/src/config/bundled-skills/notifications/tools/send-notification.ts +0 -152
  933. package/src/config/bundled-skills/notifications/tools/shared.ts +0 -13
  934. package/src/config/bundled-skills/outlook/SKILL.md +0 -196
  935. package/src/config/bundled-skills/outlook/TOOLS.json +0 -530
  936. package/src/config/bundled-skills/outlook/tools/outlook-attachments.ts +0 -85
  937. package/src/config/bundled-skills/outlook/tools/outlook-categories.ts +0 -77
  938. package/src/config/bundled-skills/outlook/tools/outlook-draft.ts +0 -84
  939. package/src/config/bundled-skills/outlook/tools/outlook-follow-up.ts +0 -94
  940. package/src/config/bundled-skills/outlook/tools/outlook-forward.ts +0 -49
  941. package/src/config/bundled-skills/outlook/tools/outlook-outreach-scan.ts +0 -237
  942. package/src/config/bundled-skills/outlook/tools/outlook-rules.ts +0 -161
  943. package/src/config/bundled-skills/outlook/tools/outlook-send-draft.ts +0 -32
  944. package/src/config/bundled-skills/outlook/tools/outlook-sender-digest.ts +0 -272
  945. package/src/config/bundled-skills/outlook/tools/outlook-trash.ts +0 -29
  946. package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +0 -129
  947. package/src/config/bundled-skills/outlook/tools/outlook-vacation.ts +0 -87
  948. package/src/config/bundled-skills/outlook/tools/shared.ts +0 -20
  949. package/src/config/bundled-skills/outlook-calendar/SKILL.md +0 -51
  950. package/src/config/bundled-skills/outlook-calendar/TOOLS.json +0 -221
  951. package/src/config/bundled-skills/outlook-calendar/calendar-client.ts +0 -252
  952. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-check-availability.ts +0 -53
  953. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-create-event.ts +0 -74
  954. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-get-event.ts +0 -18
  955. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-list-events.ts +0 -46
  956. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-rsvp.ts +0 -36
  957. package/src/config/bundled-skills/outlook-calendar/tools/shared.ts +0 -17
  958. package/src/config/bundled-skills/outlook-calendar/types.ts +0 -120
  959. package/src/config/bundled-skills/screen-watch/SKILL.md +0 -27
  960. package/src/config/bundled-skills/screen-watch/TOOLS.json +0 -35
  961. package/src/config/bundled-skills/screen-watch/tools/start-screen-watch.ts +0 -12
  962. package/src/config/bundled-skills/skills-catalog/SKILL.md +0 -84
  963. package/src/config/bundled-skills/slack/SKILL.md +0 -108
  964. package/src/config/bundled-skills/tasks/SKILL.md +0 -37
  965. package/src/config/bundled-skills/tasks/TOOLS.json +0 -353
  966. package/src/config/bundled-skills/tasks/icon.svg +0 -34
  967. package/src/config/bundled-skills/tasks/tools/task-delete.ts +0 -12
  968. package/src/config/bundled-skills/tasks/tools/task-list-add.ts +0 -12
  969. package/src/config/bundled-skills/tasks/tools/task-list-remove.ts +0 -12
  970. package/src/config/bundled-skills/tasks/tools/task-list-show.ts +0 -12
  971. package/src/config/bundled-skills/tasks/tools/task-list-update.ts +0 -12
  972. package/src/config/bundled-skills/tasks/tools/task-list.ts +0 -12
  973. package/src/config/bundled-skills/tasks/tools/task-queue-run.ts +0 -12
  974. package/src/config/bundled-skills/tasks/tools/task-run.ts +0 -12
  975. package/src/config/bundled-skills/tasks/tools/task-save.ts +0 -12
  976. package/src/config/bundled-skills/watcher/SKILL.md +0 -31
  977. package/src/config/bundled-skills/watcher/TOOLS.json +0 -167
  978. package/src/config/bundled-skills/watcher/tools/watcher-create.ts +0 -12
  979. package/src/config/bundled-skills/watcher/tools/watcher-delete.ts +0 -12
  980. package/src/config/bundled-skills/watcher/tools/watcher-digest.ts +0 -12
  981. package/src/config/bundled-skills/watcher/tools/watcher-list.ts +0 -12
  982. package/src/config/bundled-skills/watcher/tools/watcher-update.ts +0 -12
  983. package/src/daemon/context-overflow-approval.ts +0 -52
  984. package/src/daemon/watch-handler.ts +0 -399
  985. package/src/hooks/cli.ts +0 -253
  986. package/src/hooks/config.ts +0 -100
  987. package/src/hooks/discovery.ts +0 -135
  988. package/src/hooks/manager.ts +0 -179
  989. package/src/hooks/runner.ts +0 -117
  990. package/src/hooks/templates.ts +0 -77
  991. package/src/hooks/types.ts +0 -75
  992. package/src/oauth/scope-policy.ts +0 -89
  993. package/src/prompts/templates/UPDATES.md +0 -50
  994. package/src/prompts/update-bulletin-format.ts +0 -85
  995. package/src/prompts/update-bulletin-state.ts +0 -58
  996. package/src/prompts/update-bulletin-template-path.ts +0 -13
  997. package/src/prompts/update-bulletin.ts +0 -139
  998. package/src/runtime/gateway-internal-client.ts +0 -94
  999. package/src/runtime/routes/watch-routes.ts +0 -156
  1000. package/src/shared/provider-env-vars.ts +0 -19
  1001. package/src/signals/shotgun.ts +0 -203
  1002. package/src/tools/watch/screen-watch.ts +0 -144
  1003. package/src/tools/watch/watch-state.ts +0 -142
  1004. package/src/tools/watcher/create.ts +0 -86
  1005. package/src/tools/watcher/delete.ts +0 -36
  1006. package/src/tools/watcher/digest.ts +0 -54
  1007. package/src/tools/watcher/list.ts +0 -83
  1008. package/src/tools/watcher/update.ts +0 -71
@@ -3,15 +3,19 @@ import { describe, expect, test } from "bun:test";
3
3
  import type { ContextWindowConfig } from "../config/types.js";
4
4
  import { estimateTextTokens } from "../context/token-estimator.js";
5
5
  import {
6
+ clampSummaryAtSectionBoundary,
6
7
  CONTEXT_SUMMARY_MARKER,
7
8
  ContextWindowManager,
8
9
  createContextSummaryMessage,
9
10
  getSummaryFromContextMessage,
11
+ stripCompactionOnlyInjections,
10
12
  } from "../context/window-manager.js";
11
13
  import type {
14
+ ContentBlock,
12
15
  Message,
13
16
  Provider,
14
17
  ProviderResponse,
18
+ SendMessageOptions,
15
19
  } from "../providers/types.js";
16
20
 
17
21
  function makeConfig(
@@ -36,9 +40,10 @@ function makeConfig(
36
40
 
37
41
  function createProvider(
38
42
  fn: (messages: Message[]) => ProviderResponse | Promise<ProviderResponse>,
43
+ name: string = "mock",
39
44
  ): Provider {
40
45
  return {
41
- name: "mock",
46
+ name,
42
47
  async sendMessage(messages: Message[]): Promise<ProviderResponse> {
43
48
  return fn(messages);
44
49
  },
@@ -242,6 +247,63 @@ describe("ContextWindowManager", () => {
242
247
  expect(result.summaryText).toContain("## Recent Progress");
243
248
  });
244
249
 
250
+ test("marks summaryFailed when the provider throws and fallback runs", async () => {
251
+ // The agent-loop circuit breaker distinguishes "LLM call failed but
252
+ // fallback rescued us" from "compaction succeeded end-to-end". The
253
+ // fallback path must set summaryFailed:true so callers can count
254
+ // consecutive failures without losing the compacted messages.
255
+ const provider = createProvider(async () => {
256
+ throw new Error("provider unavailable");
257
+ });
258
+ const manager = new ContextWindowManager({
259
+ provider,
260
+ systemPrompt: "system prompt",
261
+ config: makeConfig({
262
+ maxInputTokens: 260,
263
+ targetBudgetRatio: 0.59,
264
+ }),
265
+ });
266
+ const long = "z".repeat(220);
267
+ const history = [
268
+ message("user", `task ${long}`),
269
+ message("assistant", `result ${long}`),
270
+ message("user", `followup ${long}`),
271
+ ];
272
+
273
+ const result = await manager.maybeCompact(history);
274
+ expect(result.compacted).toBe(true);
275
+ expect(result.summaryFailed).toBe(true);
276
+ });
277
+
278
+ test("does not mark summaryFailed on a successful provider call", async () => {
279
+ const provider = createProvider(() => ({
280
+ content: [
281
+ { type: "text", text: "## Goals\n- summary produced by provider" },
282
+ ],
283
+ model: "mock-model",
284
+ usage: { inputTokens: 60, outputTokens: 12 },
285
+ stopReason: "end_turn",
286
+ }));
287
+ const manager = new ContextWindowManager({
288
+ provider,
289
+ systemPrompt: "system prompt",
290
+ config: makeConfig({
291
+ maxInputTokens: 260,
292
+ targetBudgetRatio: 0.59,
293
+ }),
294
+ });
295
+ const long = "z".repeat(220);
296
+ const history = [
297
+ message("user", `task ${long}`),
298
+ message("assistant", `result ${long}`),
299
+ message("user", `followup ${long}`),
300
+ ];
301
+
302
+ const result = await manager.maybeCompact(history);
303
+ expect(result.compacted).toBe(true);
304
+ expect(result.summaryFailed).toBe(false);
305
+ });
306
+
245
307
  test("serializes file blocks for summary chunks", async () => {
246
308
  const prompts: string[] = [];
247
309
  const provider = createProvider((messages) => {
@@ -261,11 +323,12 @@ describe("ContextWindowManager", () => {
261
323
  provider,
262
324
  systemPrompt: "system prompt",
263
325
  config: makeConfig({
264
- maxInputTokens: 550,
265
- targetBudgetRatio: 0.59,
326
+ maxInputTokens: 2000,
327
+ targetBudgetRatio: 0.4,
328
+ compactThreshold: 0.35,
266
329
  }),
267
330
  });
268
- const long = "f".repeat(500);
331
+ const long = "f".repeat(1500);
269
332
  const history: Message[] = [
270
333
  {
271
334
  role: "user",
@@ -834,6 +897,143 @@ describe("ContextWindowManager", () => {
834
897
  );
835
898
  });
836
899
 
900
+ test("force compaction with loose target override still summarizes persisted messages", async () => {
901
+ // `pickKeepBoundary` clamps `targetInputTokensOverride` to
902
+ // `config.targetInputTokens`, so a loose override cannot
903
+ // short-circuit summarization into the truncate-only early-exit.
904
+
905
+ let summaryCalls = 0;
906
+ const provider = createProvider(() => {
907
+ summaryCalls += 1;
908
+ return {
909
+ content: [{ type: "text", text: "## Goals\n- real summary" }],
910
+ model: "mock-model",
911
+ usage: { inputTokens: 80, outputTokens: 20 },
912
+ stopReason: "end_turn",
913
+ };
914
+ });
915
+
916
+ // Scaled from prod (max 200k → 1000) preserving key ratios: the
917
+ // loose override (~0.85×max) is ~17× the post-compaction target
918
+ // (~0.05×max), so history between the two exercises the clamp.
919
+ const manager = new ContextWindowManager({
920
+ provider,
921
+ systemPrompt: "system prompt",
922
+ config: makeConfig({
923
+ maxInputTokens: 1000,
924
+ targetBudgetRatio: 0.1,
925
+ summaryBudgetRatio: 0.05,
926
+ compactThreshold: 0.3,
927
+ }),
928
+ });
929
+
930
+ // History in the "no-op zone": above threshold (300), below override (850).
931
+ const long = "x".repeat(180);
932
+ const history: Message[] = [
933
+ message("user", `u1 ${long}`),
934
+ message("assistant", `a1 ${long}`),
935
+ message("user", `u2 ${long}`),
936
+ message("assistant", `a2 ${long}`),
937
+ message("user", `u3 ${long}`),
938
+ message("assistant", `a3 ${long}`),
939
+ message("user", `u4 ${long}`),
940
+ message("assistant", `a4 ${long}`),
941
+ message("user", `u5 ${long}`),
942
+ ];
943
+
944
+ const preflightBudgetAnalog = Math.floor(1000 * 0.85);
945
+ const result = await manager.maybeCompact(history, undefined, {
946
+ force: true,
947
+ targetInputTokensOverride: preflightBudgetAnalog,
948
+ });
949
+
950
+ // Guard: we're actually above the compact threshold.
951
+ expect(result.previousEstimatedInputTokens).toBeGreaterThan(
952
+ result.thresholdTokens,
953
+ );
954
+
955
+ // A real summarization happened (not the truncate-only no-op).
956
+ expect(result.compactedPersistedMessages).toBeGreaterThan(0);
957
+ expect(summaryCalls).toBeGreaterThan(0);
958
+ });
959
+
960
+ test("force=true compacts below minFloor when a kept turn exceeds target", async () => {
961
+ // A giant paste in the last user turn means minFloor=1 alone exceeds target.
962
+ // Under force, pickKeepBoundary should walk keepTurns below minFloor (down to
963
+ // 0) so the huge block falls into the compacted region and gets summarized
964
+ // instead of being kept at full size.
965
+ const provider = createProvider(() => ({
966
+ content: [{ type: "text", text: "## Goals\n- compressed large paste" }],
967
+ model: "mock-model",
968
+ usage: { inputTokens: 120, outputTokens: 20 },
969
+ stopReason: "end_turn",
970
+ }));
971
+ const manager = new ContextWindowManager({
972
+ provider,
973
+ systemPrompt: "system prompt",
974
+ config: makeConfig({ maxInputTokens: 600, targetBudgetRatio: 0.2 }),
975
+ });
976
+ const hugePaste = "p".repeat(4000); // ~1000 tokens, well above targetInputTokens
977
+ const history: Message[] = [
978
+ message("user", "u1 small"),
979
+ message("assistant", "a1 small"),
980
+ message("user", `u2 ${hugePaste}`),
981
+ ];
982
+
983
+ const result = await manager.maybeCompact(history, undefined, {
984
+ force: true,
985
+ });
986
+
987
+ expect(result.compacted).toBe(true);
988
+ // With force=true the kept region is empty; all turns including the oversized
989
+ // paste were summarized, so the compacted result is just the summary.
990
+ expect(result.messages).toHaveLength(1);
991
+ expect(result.compactedMessages).toBe(history.length);
992
+ expect(getSummaryFromContextMessage(result.messages[0])).toContain(
993
+ "compressed large paste",
994
+ );
995
+ expect(result.estimatedInputTokens).toBeLessThan(
996
+ result.previousEstimatedInputTokens,
997
+ );
998
+ });
999
+
1000
+ test("force=false honors minFloor even when the kept turn exceeds target", async () => {
1001
+ // Same oversized paste, but without force the algorithm must preserve the
1002
+ // minFloor=1 recent turn (auto mid-loop compaction needs the in-flight turn
1003
+ // intact). Anything compactable before the floor still gets summarized.
1004
+ const provider = createProvider(() => ({
1005
+ content: [{ type: "text", text: "## Goals\n- summary" }],
1006
+ model: "mock-model",
1007
+ usage: { inputTokens: 60, outputTokens: 10 },
1008
+ stopReason: "end_turn",
1009
+ }));
1010
+ const manager = new ContextWindowManager({
1011
+ provider,
1012
+ systemPrompt: "system prompt",
1013
+ config: makeConfig({ maxInputTokens: 600, targetBudgetRatio: 0.2 }),
1014
+ });
1015
+ const hugePaste = "p".repeat(4000);
1016
+ const history: Message[] = [
1017
+ message("user", "u1 small"),
1018
+ message("assistant", "a1 small"),
1019
+ message("user", "u2 small"),
1020
+ message("assistant", "a2 small"),
1021
+ message("user", `u3 ${hugePaste}`),
1022
+ ];
1023
+
1024
+ const result = await manager.maybeCompact(history);
1025
+
1026
+ expect(result.compacted).toBe(true);
1027
+ // The oversized last user turn is retained verbatim; the kept array starts
1028
+ // with the summary followed by the messages from that turn onward.
1029
+ const lastUser = result.messages
1030
+ .filter((m) => m.role === "user")
1031
+ .map((m) => (m.content[0].type === "text" ? m.content[0].text : ""))
1032
+ .find((t) => t.startsWith("u3 "));
1033
+ expect(lastUser).toBeDefined();
1034
+ expect(lastUser!.length).toBeGreaterThan(hugePaste.length);
1035
+ });
1036
+
837
1037
  test("shouldCompact returns needed=false with estimatedTokens when below threshold", () => {
838
1038
  const provider = createProvider(() => {
839
1039
  throw new Error("should not be called");
@@ -1096,6 +1296,197 @@ describe("ContextWindowManager", () => {
1096
1296
  expect(manager.nonPersistedPrefixCount).toBe(0);
1097
1297
  });
1098
1298
 
1299
+ test("summary system prompt instructs verbatim thread-anchor preservation", async () => {
1300
+ const capturedSystemPrompts: (string | undefined)[] = [];
1301
+ const provider: Provider = {
1302
+ name: "mock",
1303
+ async sendMessage(
1304
+ _messages: Message[],
1305
+ _tools,
1306
+ systemPrompt,
1307
+ ): Promise<ProviderResponse> {
1308
+ capturedSystemPrompts.push(systemPrompt);
1309
+ return {
1310
+ content: [
1311
+ {
1312
+ type: "text",
1313
+ text: "## Goals\n- preserved thread parent verbatim",
1314
+ },
1315
+ ],
1316
+ model: "mock-model",
1317
+ usage: { inputTokens: 60, outputTokens: 12 },
1318
+ stopReason: "end_turn",
1319
+ };
1320
+ },
1321
+ };
1322
+ const manager = new ContextWindowManager({
1323
+ provider,
1324
+ systemPrompt: "system prompt",
1325
+ config: makeConfig({ maxInputTokens: 600 }),
1326
+ });
1327
+ const long = "x".repeat(240);
1328
+ // Simulate a Slack-style transcript where an old user "thread parent"
1329
+ // message is about to be compacted while a later reply survives in the
1330
+ // retained tail. The clause being asserted instructs the summarizer to
1331
+ // preserve that parent verbatim — we cannot verify the model's behavior
1332
+ // here (the provider is a stub), so we instead assert the clause itself
1333
+ // reaches the summarizer.
1334
+ const history: Message[] = [
1335
+ message("user", `parent: kickoff plan ${long}`),
1336
+ message("assistant", `a1 ${long}`),
1337
+ message("user", `u2 ${long}`),
1338
+ message("assistant", `a2 ${long}`),
1339
+ message("user", `reply-in-thread ${long}`),
1340
+ message("assistant", `a3 ${long}`),
1341
+ ];
1342
+
1343
+ const result = await manager.maybeCompact(history);
1344
+ expect(result.compacted).toBe(true);
1345
+ expect(capturedSystemPrompts.length).toBeGreaterThan(0);
1346
+ const seenPrompt = capturedSystemPrompts[0];
1347
+ expect(seenPrompt).toBeDefined();
1348
+ expect(seenPrompt).toContain("Thread anchors");
1349
+ expect(seenPrompt).toContain("verbatim");
1350
+ });
1351
+
1352
+ test("summary prompt lists retained-tail thread-reply references", async () => {
1353
+ const capturedMessages: Message[][] = [];
1354
+ const provider: Provider = {
1355
+ name: "mock",
1356
+ async sendMessage(messages: Message[]): Promise<ProviderResponse> {
1357
+ capturedMessages.push(messages);
1358
+ return {
1359
+ content: [{ type: "text", text: "## Goals\n- ok" }],
1360
+ model: "mock-model",
1361
+ usage: { inputTokens: 60, outputTokens: 12 },
1362
+ stopReason: "end_turn",
1363
+ };
1364
+ },
1365
+ };
1366
+ const manager = new ContextWindowManager({
1367
+ provider,
1368
+ systemPrompt: "system prompt",
1369
+ config: makeConfig({ maxInputTokens: 600 }),
1370
+ });
1371
+ const long = "x".repeat(240);
1372
+ // Compactable region ends before the retained tail, which contains a
1373
+ // Slack-style reply line that cites its parent via `→ M1a2b3c`. The
1374
+ // summary prompt must surface that reference so the Thread-anchors
1375
+ // instruction has something to act on.
1376
+ const history: Message[] = [
1377
+ message("user", `[11/14/23 14:25 @alice]: parent kickoff ${long}`),
1378
+ message("assistant", `a1 ${long}`),
1379
+ message("user", `u2 ${long}`),
1380
+ message("assistant", `a2 ${long}`),
1381
+ message("user", `[11/14/23 14:28 @bob → M1a2b3c]: reply ${long}`),
1382
+ message("assistant", `a3 ${long}`),
1383
+ ];
1384
+
1385
+ const result = await manager.maybeCompact(history);
1386
+ expect(result.compacted).toBe(true);
1387
+ expect(capturedMessages.length).toBeGreaterThan(0);
1388
+ const userPromptText = capturedMessages[0]
1389
+ .flatMap((m) => m.content)
1390
+ .filter(
1391
+ (b): b is Extract<ContentBlock, { type: "text" }> => b.type === "text",
1392
+ )
1393
+ .map((b) => b.text)
1394
+ .join("\n");
1395
+ expect(userPromptText).toContain("### Retained Thread References");
1396
+ expect(userPromptText).toContain("→ M1a2b3c");
1397
+ });
1398
+
1399
+ test("summary prompt lists retained-tail thread-reply references for edited replies", async () => {
1400
+ const capturedMessages: Message[][] = [];
1401
+ const provider: Provider = {
1402
+ name: "mock",
1403
+ async sendMessage(messages: Message[]): Promise<ProviderResponse> {
1404
+ capturedMessages.push(messages);
1405
+ return {
1406
+ content: [{ type: "text", text: "## Goals\n- ok" }],
1407
+ model: "mock-model",
1408
+ usage: { inputTokens: 60, outputTokens: 12 },
1409
+ stopReason: "end_turn",
1410
+ };
1411
+ },
1412
+ };
1413
+ const manager = new ContextWindowManager({
1414
+ provider,
1415
+ systemPrompt: "system prompt",
1416
+ config: makeConfig({ maxInputTokens: 600 }),
1417
+ });
1418
+ const long = "x".repeat(240);
1419
+ // An edited reply renders with `, edited …` between the parent alias and
1420
+ // the closing bracket: `→ Mxxxxxx, edited MM/DD/YY HH:MM]`. The regex
1421
+ // must still flag these lines so retention works for edited replies.
1422
+ const history: Message[] = [
1423
+ message("user", `[11/14/23 14:25 @alice]: parent kickoff ${long}`),
1424
+ message("assistant", `a1 ${long}`),
1425
+ message("user", `u2 ${long}`),
1426
+ message("assistant", `a2 ${long}`),
1427
+ message(
1428
+ "user",
1429
+ `[11/14/23 14:28 @bob → M1a2b3c, edited 11/14/23 14:32]: reply ${long}`,
1430
+ ),
1431
+ message("assistant", `a3 ${long}`),
1432
+ ];
1433
+
1434
+ const result = await manager.maybeCompact(history);
1435
+ expect(result.compacted).toBe(true);
1436
+ expect(capturedMessages.length).toBeGreaterThan(0);
1437
+ const userPromptText = capturedMessages[0]
1438
+ .flatMap((m) => m.content)
1439
+ .filter(
1440
+ (b): b is Extract<ContentBlock, { type: "text" }> => b.type === "text",
1441
+ )
1442
+ .map((b) => b.text)
1443
+ .join("\n");
1444
+ expect(userPromptText).toContain("### Retained Thread References");
1445
+ expect(userPromptText).toContain("→ M1a2b3c, edited 11/14/23 14:32");
1446
+ });
1447
+
1448
+ test("summary prompt omits retained references when retained tail has no thread markers", async () => {
1449
+ const capturedMessages: Message[][] = [];
1450
+ const provider: Provider = {
1451
+ name: "mock",
1452
+ async sendMessage(messages: Message[]): Promise<ProviderResponse> {
1453
+ capturedMessages.push(messages);
1454
+ return {
1455
+ content: [{ type: "text", text: "## Goals\n- ok" }],
1456
+ model: "mock-model",
1457
+ usage: { inputTokens: 60, outputTokens: 12 },
1458
+ stopReason: "end_turn",
1459
+ };
1460
+ },
1461
+ };
1462
+ const manager = new ContextWindowManager({
1463
+ provider,
1464
+ systemPrompt: "system prompt",
1465
+ config: makeConfig({ maxInputTokens: 600 }),
1466
+ });
1467
+ const long = "x".repeat(240);
1468
+ const history: Message[] = [
1469
+ message("user", `u1 ${long}`),
1470
+ message("assistant", `a1 ${long}`),
1471
+ message("user", `u2 ${long}`),
1472
+ message("assistant", `a2 ${long}`),
1473
+ message("user", `u3 ${long}`),
1474
+ message("assistant", `a3 ${long}`),
1475
+ ];
1476
+
1477
+ const result = await manager.maybeCompact(history);
1478
+ expect(result.compacted).toBe(true);
1479
+ const userPromptText = capturedMessages[0]
1480
+ .flatMap((m) => m.content)
1481
+ .filter(
1482
+ (b): b is Extract<ContentBlock, { type: "text" }> => b.type === "text",
1483
+ )
1484
+ .map((b) => b.text)
1485
+ .join("\n");
1486
+ expect(userPromptText).not.toContain("### Retained Thread References");
1487
+ expect(userPromptText).not.toMatch(/→ M[0-9a-f]{6}]/);
1488
+ });
1489
+
1099
1490
  test("does not subtract summaryOffset when summary at index 0 is child-owned from prior compaction", async () => {
1100
1491
  const provider = createProvider(() => ({
1101
1492
  content: [{ type: "text", text: "## Goals\n- next child summary" }],
@@ -1140,4 +1531,492 @@ describe("ContextWindowManager", () => {
1140
1531
  expect(result.compactedPersistedMessages).toBe(2);
1141
1532
  expect(manager.nonPersistedPrefixCount).toBe(0);
1142
1533
  });
1534
+
1535
+ test("Slack origin bumps default minKeepRecentUserTurns to 8", async () => {
1536
+ const provider = createProvider(() => ({
1537
+ content: [{ type: "text", text: "## Goals\n- slack thread context" }],
1538
+ model: "mock-model",
1539
+ usage: { inputTokens: 60, outputTokens: 12 },
1540
+ stopReason: "end_turn",
1541
+ }));
1542
+
1543
+ // Use targetInputTokensOverride so the binary search is forced even
1544
+ // for a small history. Both managers see the same tight budget; the
1545
+ // only knob that varies is conversationOriginChannel.
1546
+ const config = makeConfig({ maxInputTokens: 12_000 });
1547
+ const long = "s".repeat(220);
1548
+ // 9 user turns: enough headroom for Slack's bumped floor of 8 to be
1549
+ // distinguishable from the default floor of 1.
1550
+ const history: Message[] = [];
1551
+ for (let i = 1; i <= 9; i++) {
1552
+ history.push(message("user", `u${i} ${long}`));
1553
+ history.push(message("assistant", `a${i} ${long}`));
1554
+ }
1555
+
1556
+ const slackManager = new ContextWindowManager({
1557
+ provider,
1558
+ systemPrompt: "system prompt",
1559
+ config,
1560
+ });
1561
+ const slackResult = await slackManager.maybeCompact(history, undefined, {
1562
+ force: true,
1563
+ targetInputTokensOverride: 200,
1564
+ conversationOriginChannel: "slack",
1565
+ });
1566
+
1567
+ const defaultManager = new ContextWindowManager({
1568
+ provider,
1569
+ systemPrompt: "system prompt",
1570
+ config,
1571
+ });
1572
+ const defaultResult = await defaultManager.maybeCompact(
1573
+ history,
1574
+ undefined,
1575
+ { force: true, targetInputTokensOverride: 200 },
1576
+ );
1577
+
1578
+ expect(slackResult.compacted).toBe(true);
1579
+ expect(defaultResult.compacted).toBe(true);
1580
+ // Default floor (1 user turn) compacts more of the history than the
1581
+ // Slack floor (8 user turns), which preserves more recent context.
1582
+ expect(defaultResult.compactedMessages).toBeGreaterThan(
1583
+ slackResult.compactedMessages,
1584
+ );
1585
+ // Slack keeps 8 of 9 user turns: 16 kept messages, 2 compacted.
1586
+ expect(slackResult.compactedMessages).toBe(2);
1587
+ });
1588
+
1589
+ test("non-Slack origin keeps default minKeepRecentUserTurns of 1", async () => {
1590
+ const provider = createProvider(() => ({
1591
+ content: [{ type: "text", text: "## Goals\n- standard summary" }],
1592
+ model: "mock-model",
1593
+ usage: { inputTokens: 60, outputTokens: 12 },
1594
+ stopReason: "end_turn",
1595
+ }));
1596
+
1597
+ const config = makeConfig({ maxInputTokens: 12_000 });
1598
+ const long = "n".repeat(220);
1599
+ const history: Message[] = [];
1600
+ for (let i = 1; i <= 9; i++) {
1601
+ history.push(message("user", `u${i} ${long}`));
1602
+ history.push(message("assistant", `a${i} ${long}`));
1603
+ }
1604
+
1605
+ // Telegram origin must behave identically to no-channel-hint default.
1606
+ const telegramManager = new ContextWindowManager({
1607
+ provider,
1608
+ systemPrompt: "system prompt",
1609
+ config,
1610
+ });
1611
+ const telegramResult = await telegramManager.maybeCompact(
1612
+ history,
1613
+ undefined,
1614
+ {
1615
+ force: true,
1616
+ targetInputTokensOverride: 200,
1617
+ conversationOriginChannel: "telegram",
1618
+ },
1619
+ );
1620
+
1621
+ const defaultManager = new ContextWindowManager({
1622
+ provider,
1623
+ systemPrompt: "system prompt",
1624
+ config,
1625
+ });
1626
+ const defaultResult = await defaultManager.maybeCompact(
1627
+ history,
1628
+ undefined,
1629
+ { force: true, targetInputTokensOverride: 200 },
1630
+ );
1631
+
1632
+ expect(telegramResult.compacted).toBe(true);
1633
+ expect(defaultResult.compacted).toBe(true);
1634
+ expect(telegramResult.compactedMessages).toBe(
1635
+ defaultResult.compactedMessages,
1636
+ );
1637
+ });
1638
+
1639
+ test("explicit minKeepRecentUserTurns wins over Slack default", async () => {
1640
+ const provider = createProvider(() => ({
1641
+ content: [{ type: "text", text: "## Goals\n- emergency override" }],
1642
+ model: "mock-model",
1643
+ usage: { inputTokens: 60, outputTokens: 12 },
1644
+ stopReason: "end_turn",
1645
+ }));
1646
+
1647
+ const manager = new ContextWindowManager({
1648
+ provider,
1649
+ systemPrompt: "system prompt",
1650
+ config: makeConfig({
1651
+ maxInputTokens: 260,
1652
+ targetBudgetRatio: 0.28,
1653
+ }),
1654
+ });
1655
+ const long = "e".repeat(220);
1656
+ const history: Message[] = [
1657
+ message("user", `u1 ${long}`),
1658
+ message("assistant", `a1 ${long}`),
1659
+ message("user", `u2 ${long}`),
1660
+ ];
1661
+
1662
+ // Emergency override (`minKeepRecentUserTurns: 0`) must take precedence
1663
+ // over the Slack-bumped default of 8 — this guards the agent loop's
1664
+ // context-too-large recovery path which always passes 0.
1665
+ const result = await manager.maybeCompact(history, undefined, {
1666
+ force: true,
1667
+ minKeepRecentUserTurns: 0,
1668
+ conversationOriginChannel: "slack",
1669
+ });
1670
+ expect(result.compacted).toBe(true);
1671
+ expect(result.compactedMessages).toBe(3);
1672
+ expect(result.messages).toHaveLength(1);
1673
+ });
1674
+
1675
+ test("summary provider call includes callSite: conversationSummarization", async () => {
1676
+ // Regression guard for JARVIS-587: without the callSite, the summary
1677
+ // call fell through to `llm.default` (opus + effort=max + thinking
1678
+ // enabled) and exceeded the 30s plugin pipeline budget on ~150k-token
1679
+ // transcripts. The fix is to route the summary call through the
1680
+ // dedicated `conversationSummarization` call-site config.
1681
+ const capturedOptions: (SendMessageOptions | undefined)[] = [];
1682
+ const provider: Provider = {
1683
+ name: "mock",
1684
+ async sendMessage(
1685
+ _messages: Message[],
1686
+ _tools: unknown,
1687
+ _systemPrompt: unknown,
1688
+ options?: SendMessageOptions,
1689
+ ): Promise<ProviderResponse> {
1690
+ capturedOptions.push(options);
1691
+ return {
1692
+ content: [{ type: "text", text: "## Goals\n- summary" }],
1693
+ model: "mock-model",
1694
+ usage: { inputTokens: 50, outputTokens: 10 },
1695
+ stopReason: "end_turn",
1696
+ };
1697
+ },
1698
+ };
1699
+ const manager = new ContextWindowManager({
1700
+ provider,
1701
+ systemPrompt: "system prompt",
1702
+ config: makeConfig({ maxInputTokens: 600 }),
1703
+ });
1704
+ const long = "x".repeat(240);
1705
+ const history: Message[] = [
1706
+ message("user", `u1 ${long}`),
1707
+ message("assistant", `a1 ${long}`),
1708
+ message("user", `u2 ${long}`),
1709
+ message("assistant", `a2 ${long}`),
1710
+ message("user", `u3 ${long}`),
1711
+ message("assistant", `a3 ${long}`),
1712
+ ];
1713
+
1714
+ const result = await manager.maybeCompact(history);
1715
+ expect(result.compacted).toBe(true);
1716
+ expect(capturedOptions.length).toBeGreaterThan(0);
1717
+ for (const options of capturedOptions) {
1718
+ expect(options?.config?.callSite).toBe("conversationSummarization");
1719
+ }
1720
+ });
1721
+ });
1722
+
1723
+ describe("stripCompactionOnlyInjections", () => {
1724
+ test("removes memory, turn_context, and workspace text blocks from user messages", () => {
1725
+ const messages: Message[] = [
1726
+ {
1727
+ role: "user",
1728
+ content: [
1729
+ {
1730
+ type: "text",
1731
+ text: "<memory __injected>\nrecall notes\n</memory>",
1732
+ },
1733
+ {
1734
+ type: "text",
1735
+ text: "<turn_context>\nActor: Alice\n</turn_context>",
1736
+ },
1737
+ { type: "text", text: "real user content" },
1738
+ ],
1739
+ },
1740
+ {
1741
+ role: "assistant",
1742
+ content: [{ type: "text", text: "assistant reply" }],
1743
+ },
1744
+ ];
1745
+ const stripped = stripCompactionOnlyInjections(messages);
1746
+ expect(stripped).toHaveLength(2);
1747
+ const firstText = (stripped[0].content[0] as { text: string }).text;
1748
+ expect(firstText).toBe("real user content");
1749
+ expect(stripped[0].content).toHaveLength(1);
1750
+ });
1751
+
1752
+ test("drops user messages that become empty after stripping", () => {
1753
+ const messages: Message[] = [
1754
+ {
1755
+ role: "user",
1756
+ content: [
1757
+ { type: "text", text: "<memory __injected>\nonly memory\n</memory>" },
1758
+ ],
1759
+ },
1760
+ { role: "user", content: [{ type: "text", text: "real content" }] },
1761
+ ];
1762
+ const stripped = stripCompactionOnlyInjections(messages);
1763
+ expect(stripped).toHaveLength(1);
1764
+ expect((stripped[0].content[0] as { text: string }).text).toBe(
1765
+ "real content",
1766
+ );
1767
+ });
1768
+
1769
+ test("leaves assistant messages and non-text blocks untouched", () => {
1770
+ const messages: Message[] = [
1771
+ {
1772
+ role: "assistant",
1773
+ content: [
1774
+ {
1775
+ type: "text",
1776
+ text: "<turn_context>\nnot really injected\n</turn_context>",
1777
+ },
1778
+ ],
1779
+ },
1780
+ {
1781
+ role: "user",
1782
+ content: [
1783
+ {
1784
+ type: "tool_result",
1785
+ tool_use_id: "t1",
1786
+ content: "<memory>fake</memory>",
1787
+ },
1788
+ { type: "text", text: "user reply" },
1789
+ ],
1790
+ },
1791
+ ];
1792
+ const stripped = stripCompactionOnlyInjections(messages);
1793
+ expect(stripped).toHaveLength(2);
1794
+ expect((stripped[0].content[0] as { text: string }).text).toContain(
1795
+ "turn_context",
1796
+ );
1797
+ expect(stripped[1].content).toHaveLength(2);
1798
+ });
1799
+
1800
+ test("preserves user prose that merely mentions ambiguous tag names", () => {
1801
+ // Common-word bare tags embedded in legitimate user prose (discussions of
1802
+ // XML, system terminology, etc.) must survive stripping because they are
1803
+ // not shaped like a runtime injection — no leading newline after the
1804
+ // open tag, or other prose surrounds the tag.
1805
+ const messages: Message[] = [
1806
+ {
1807
+ role: "user",
1808
+ content: [
1809
+ {
1810
+ type: "text",
1811
+ text: "<memory> is a tag I'd like to add to my parser",
1812
+ },
1813
+ ],
1814
+ },
1815
+ {
1816
+ role: "user",
1817
+ content: [
1818
+ {
1819
+ type: "text",
1820
+ text: "checking <workspace> usage across the repo, any thoughts?",
1821
+ },
1822
+ ],
1823
+ },
1824
+ {
1825
+ role: "user",
1826
+ content: [
1827
+ {
1828
+ type: "text",
1829
+ text: "what is <knowledge_base> in this context?",
1830
+ },
1831
+ ],
1832
+ },
1833
+ {
1834
+ role: "user",
1835
+ content: [
1836
+ { type: "text", text: "<pkb> sounds like a short name — wrong?" },
1837
+ ],
1838
+ },
1839
+ {
1840
+ role: "user",
1841
+ content: [
1842
+ {
1843
+ type: "text",
1844
+ text: "when the model hits a <system_reminder>, what happens next?",
1845
+ },
1846
+ ],
1847
+ },
1848
+ ];
1849
+ const stripped = stripCompactionOnlyInjections(messages);
1850
+ expect(stripped).toHaveLength(messages.length);
1851
+ for (let i = 0; i < messages.length; i++) {
1852
+ expect(stripped[i].content).toHaveLength(1);
1853
+ expect((stripped[i].content[0] as { text: string }).text).toBe(
1854
+ (messages[i].content[0] as { text: string }).text,
1855
+ );
1856
+ }
1857
+ });
1858
+
1859
+ test("still strips runtime-shaped wrapped blocks for ambiguous tag names", () => {
1860
+ // Bare-tag blocks with a newline after the open tag and a matching close
1861
+ // tag (e.g. `<memory>\n...\n</memory>`) match the wrapped-strip path.
1862
+ // This covers both the current runtime emission shape and blocks
1863
+ // persisted before the `__injected` attribute existed — the prefix list
1864
+ // handles `__injected`-attributed tags, and the wrapped matcher handles
1865
+ // the bare-tag wrap shape.
1866
+ const messages: Message[] = [
1867
+ {
1868
+ role: "user",
1869
+ content: [
1870
+ { type: "text", text: "<memory>\nlegacy recall blob\n</memory>" },
1871
+ { type: "text", text: "actual user content" },
1872
+ ],
1873
+ },
1874
+ {
1875
+ role: "user",
1876
+ content: [
1877
+ {
1878
+ type: "text",
1879
+ text: "<workspace>\nRoot: /home\nFiles: a, b\n</workspace>",
1880
+ },
1881
+ { type: "text", text: "more prose" },
1882
+ ],
1883
+ },
1884
+ {
1885
+ role: "user",
1886
+ content: [
1887
+ {
1888
+ type: "text",
1889
+ text: "<system_reminder>\nread your PKB\n</system_reminder>",
1890
+ },
1891
+ { type: "text", text: "ok" },
1892
+ ],
1893
+ },
1894
+ ];
1895
+ const stripped = stripCompactionOnlyInjections(messages);
1896
+ expect(stripped).toHaveLength(3);
1897
+ for (const msg of stripped) {
1898
+ expect(msg.content).toHaveLength(1);
1899
+ }
1900
+ expect((stripped[0].content[0] as { text: string }).text).toBe(
1901
+ "actual user content",
1902
+ );
1903
+ expect((stripped[1].content[0] as { text: string }).text).toBe(
1904
+ "more prose",
1905
+ );
1906
+ expect((stripped[2].content[0] as { text: string }).text).toBe("ok");
1907
+ });
1908
+
1909
+ test("does not strip a user's inline snippet that is not shaped like an injection", () => {
1910
+ // A user quoting a `<memory>...</memory>` snippet alongside prose in the
1911
+ // SAME text block should survive — the block does not start with
1912
+ // `<memory>\n` (there's surrounding prose) so the wrapped-tag match
1913
+ // does not trigger.
1914
+ const messages: Message[] = [
1915
+ {
1916
+ role: "user",
1917
+ content: [
1918
+ {
1919
+ type: "text",
1920
+ text: "Here's the XML I'm working with: <memory>x</memory> — what do you think?",
1921
+ },
1922
+ ],
1923
+ },
1924
+ ];
1925
+ const stripped = stripCompactionOnlyInjections(messages);
1926
+ expect(stripped).toHaveLength(1);
1927
+ expect((stripped[0].content[0] as { text: string }).text).toContain(
1928
+ "<memory>x</memory>",
1929
+ );
1930
+ });
1931
+ });
1932
+
1933
+ describe("summarizer input excludes runtime injections", () => {
1934
+ test("maybeCompact does not pass memory/turn_context text to the summarizer", async () => {
1935
+ const seenPrompts: string[] = [];
1936
+ const provider = createProvider((messages) => {
1937
+ for (const msg of messages) {
1938
+ for (const block of msg.content) {
1939
+ if (block.type === "text") seenPrompts.push(block.text);
1940
+ }
1941
+ }
1942
+ return {
1943
+ content: [
1944
+ {
1945
+ type: "text",
1946
+ text: "## Facts Worth Remembering\n- summary produced",
1947
+ },
1948
+ ],
1949
+ model: "mock",
1950
+ usage: { inputTokens: 100, outputTokens: 25 },
1951
+ stopReason: "end_turn",
1952
+ };
1953
+ });
1954
+ const manager = new ContextWindowManager({
1955
+ provider,
1956
+ systemPrompt: "system prompt",
1957
+ config: makeConfig({
1958
+ maxInputTokens: 2000,
1959
+ targetBudgetRatio: 0.4,
1960
+ compactThreshold: 0.35,
1961
+ }),
1962
+ });
1963
+ const long = "x".repeat(1500);
1964
+ const memoryBlob =
1965
+ "<memory __injected>\nBOB_ATTENDED_STANDUP_YESTERDAY\n</memory>";
1966
+ const turnCtx =
1967
+ "<turn_context>\nACTOR_METADATA_THAT_SHOULD_NOT_LEAK\n</turn_context>";
1968
+ const history: Message[] = [
1969
+ {
1970
+ role: "user",
1971
+ content: [
1972
+ { type: "text", text: memoryBlob },
1973
+ { type: "text", text: turnCtx },
1974
+ { type: "text", text: `u1 ${long}` },
1975
+ ],
1976
+ },
1977
+ message("assistant", `a1 ${long}`),
1978
+ message("user", `u2 ${long}`),
1979
+ message("assistant", `a2 ${long}`),
1980
+ message("user", `u3 ${long}`),
1981
+ ];
1982
+
1983
+ const result = await manager.maybeCompact(history);
1984
+ expect(result.compacted).toBe(true);
1985
+ const joined = seenPrompts.join("\n");
1986
+ expect(joined).not.toContain("BOB_ATTENDED_STANDUP_YESTERDAY");
1987
+ expect(joined).not.toContain("ACTOR_METADATA_THAT_SHOULD_NOT_LEAK");
1988
+ expect(joined).not.toContain("<memory __injected>");
1989
+ expect(joined).not.toContain("<turn_context>");
1990
+ // Real conversation content should survive — at least one of the
1991
+ // middle turns (whose header/body is short enough to fit within the
1992
+ // capped transcript budget) should appear in the summarizer input.
1993
+ expect(joined).toMatch(/u2 |a1 /);
1994
+ });
1995
+ });
1996
+
1997
+ describe("clampSummaryAtSectionBoundary", () => {
1998
+ test("returns the input unchanged when under the limit", () => {
1999
+ const summary = "## Decisions\nWe decided to ship.";
2000
+ expect(clampSummaryAtSectionBoundary(summary, 1000)).toBe(summary);
2001
+ });
2002
+
2003
+ test("truncates at a `## ` boundary when one exists in the allowed region", () => {
2004
+ const keeper = "## Facts\n" + "a".repeat(200);
2005
+ const dropped = "## Open Threads\n" + "b".repeat(500);
2006
+ const summary = `${keeper}\n${dropped}`;
2007
+ const maxChars = keeper.length + 20;
2008
+ const clamped = clampSummaryAtSectionBoundary(summary, maxChars);
2009
+ expect(clamped.endsWith("...")).toBe(true);
2010
+ expect(clamped).not.toContain("## Open Threads");
2011
+ expect(clamped).toContain("## Facts");
2012
+ // No mid-header cut: nothing that looks like a partial heading.
2013
+ expect(/##\s*$/.test(clamped)).toBe(false);
2014
+ });
2015
+
2016
+ test("falls back to a hard cut when no section boundary is past the midpoint", () => {
2017
+ const body = "no section headers in this output " + "z".repeat(1000);
2018
+ const clamped = clampSummaryAtSectionBoundary(body, 100);
2019
+ expect(clamped.endsWith("...")).toBe(true);
2020
+ expect(clamped.length).toBeLessThanOrEqual(100);
2021
+ });
1143
2022
  });