@vellumai/assistant 0.6.5 → 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 (443) hide show
  1. package/AGENTS.md +9 -1
  2. package/ARCHITECTURE.md +15 -17
  3. package/Dockerfile +6 -4
  4. package/__tests__/permissions/gateway-threshold-reader.test.ts +283 -0
  5. package/docs/architecture/integrations.md +32 -39
  6. package/docs/architecture/memory.md +25 -30
  7. package/docs/architecture/security.md +7 -6
  8. package/docs/browser-use-architecture-phase2.md +63 -20
  9. package/docs/plugins.md +761 -0
  10. package/examples/plugins/echo/README.md +132 -0
  11. package/examples/plugins/echo/package.json +17 -0
  12. package/examples/plugins/echo/register.ts +187 -0
  13. package/node_modules/@vellumai/egress-proxy/src/types.ts +19 -0
  14. package/openapi.yaml +212 -68
  15. package/package.json +1 -1
  16. package/src/__tests__/app-compiler.test.ts +57 -0
  17. package/src/__tests__/approval-cascade.test.ts +7 -2
  18. package/src/__tests__/auto-analysis-end-to-end.test.ts +1 -0
  19. package/src/__tests__/avatar-generator.test.ts +4 -2
  20. package/src/__tests__/bundled-asset.test.ts +6 -6
  21. package/src/__tests__/catalog-cache.test.ts +69 -0
  22. package/src/__tests__/checker.test.ts +459 -171
  23. package/src/__tests__/circuit-breaker-pipeline.test.ts +406 -0
  24. package/src/__tests__/compaction-events.test.ts +501 -0
  25. package/src/__tests__/compaction-pipeline.test.ts +210 -0
  26. package/src/__tests__/compaction-strip-metadata-clear.test.ts +181 -0
  27. package/src/__tests__/compaction-timeout-recovery.test.ts +262 -0
  28. package/src/__tests__/config-model-image-provider.test.ts +110 -0
  29. package/src/__tests__/config-schema.test.ts +22 -9
  30. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +0 -4
  31. package/src/__tests__/contacts-tools.test.ts +26 -0
  32. package/src/__tests__/context-overflow-policy.test.ts +7 -7
  33. package/src/__tests__/context-window-manager.test.ts +355 -4
  34. package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
  35. package/src/__tests__/conversation-agent-loop-overflow.test.ts +26 -30
  36. package/src/__tests__/conversation-agent-loop.test.ts +30 -141
  37. package/src/__tests__/conversation-confirmation-signals.test.ts +6 -1
  38. package/src/__tests__/conversation-history-web-search.test.ts +1 -0
  39. package/src/__tests__/conversation-init.benchmark.test.ts +2 -16
  40. package/src/__tests__/conversation-pairing.test.ts +174 -10
  41. package/src/__tests__/conversation-pre-run-repair.test.ts +4 -1
  42. package/src/__tests__/conversation-process-callsite.test.ts +3 -0
  43. package/src/__tests__/conversation-provider-retry-repair.test.ts +16 -7
  44. package/src/__tests__/conversation-queue.test.ts +29 -14
  45. package/src/__tests__/conversation-routes-disk-view.test.ts +7 -6
  46. package/src/__tests__/conversation-runtime-assembly.test.ts +155 -110
  47. package/src/__tests__/conversation-runtime-workspace.test.ts +23 -38
  48. package/src/__tests__/conversation-seed-composer.test.ts +2 -2
  49. package/src/__tests__/conversation-slash-queue.test.ts +7 -2
  50. package/src/__tests__/conversation-slash-unknown.test.ts +25 -2
  51. package/src/__tests__/conversation-speed-override.test.ts +6 -1
  52. package/src/__tests__/conversation-title-service.test.ts +116 -0
  53. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +41 -2
  54. package/src/__tests__/conversation-usage.test.ts +1 -1
  55. package/src/__tests__/conversation-workspace-cache-state.test.ts +4 -1
  56. package/src/__tests__/conversation-workspace-injection.test.ts +3 -0
  57. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +4 -1
  58. package/src/__tests__/credential-health-service.test.ts +78 -9
  59. package/src/__tests__/credential-security-invariants.test.ts +2 -2
  60. package/src/__tests__/db-schedule-syntax-migration.test.ts +1 -0
  61. package/src/__tests__/empty-response-pipeline.test.ts +305 -0
  62. package/src/__tests__/extension-id-sync-guard.test.ts +3 -3
  63. package/src/__tests__/first-greeting.test.ts +247 -5
  64. package/src/__tests__/headless-browser-mode.test.ts +57 -0
  65. package/src/__tests__/history-repair-pipeline.test.ts +399 -0
  66. package/src/__tests__/host-browser-e2e-cloud.test.ts +307 -0
  67. package/src/__tests__/host-browser-e2e-self-hosted.test.ts +3 -3
  68. package/src/__tests__/host-proxy-interface.test.ts +36 -2
  69. package/src/__tests__/image-credentials.test.ts +137 -0
  70. package/src/__tests__/image-service-dispatcher.test.ts +186 -0
  71. package/src/__tests__/injector-chain.test.ts +526 -0
  72. package/src/__tests__/intent-routing.test.ts +0 -26
  73. package/src/__tests__/llm-call-pipeline.test.ts +285 -0
  74. package/src/__tests__/llm-schema.test.ts +1 -1
  75. package/src/__tests__/media-generate-image.test.ts +119 -13
  76. package/src/__tests__/memory-retrieval-pipeline.test.ts +401 -0
  77. package/src/__tests__/memory-upsert-concurrency.test.ts +1 -0
  78. package/src/__tests__/migration-import-from-url.test.ts +5 -68
  79. package/src/__tests__/model-intents.test.ts +4 -2
  80. package/src/__tests__/notification-broadcaster.test.ts +3 -3
  81. package/src/__tests__/notification-decision-strategy.test.ts +0 -11
  82. package/src/__tests__/notification-schedule-notify-dedup.test.ts +108 -0
  83. package/src/__tests__/oauth-apps-routes.test.ts +1 -1
  84. package/src/__tests__/oauth-cli.test.ts +14 -12
  85. package/src/__tests__/oauth-connect-orchestrator.test.ts +4 -13
  86. package/src/__tests__/oauth-provider-serializer.test.ts +6 -4
  87. package/src/__tests__/oauth-provider-visibility.test.ts +3 -5
  88. package/src/__tests__/oauth-providers-routes.test.ts +3 -2
  89. package/src/__tests__/oauth-store.test.ts +41 -76
  90. package/src/__tests__/onboarding-template-contract.test.ts +16 -64
  91. package/src/__tests__/openai-image-service.test.ts +368 -0
  92. package/src/__tests__/overflow-reduce-pipeline.test.ts +676 -0
  93. package/src/__tests__/permission-checker-host-gate.test.ts +0 -24
  94. package/src/__tests__/persist-onboarding-artifacts.test.ts +266 -0
  95. package/src/__tests__/persistence-pipeline.test.ts +377 -0
  96. package/src/__tests__/pipeline-runner.test.ts +565 -0
  97. package/src/__tests__/platform.test.ts +5 -2
  98. package/src/__tests__/plugin-bootstrap.test.ts +483 -0
  99. package/src/__tests__/plugin-registry.test.ts +273 -0
  100. package/src/__tests__/plugin-route-contribution.test.ts +288 -0
  101. package/src/__tests__/plugin-skill-contribution.test.ts +367 -0
  102. package/src/__tests__/plugin-tool-contribution.test.ts +286 -0
  103. package/src/__tests__/plugin-types.test.ts +320 -0
  104. package/src/__tests__/pricing.test.ts +44 -12
  105. package/src/__tests__/proxy-approval-callback.test.ts +69 -8
  106. package/src/__tests__/reaction-persistence.test.ts +1 -0
  107. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
  108. package/src/__tests__/registry.test.ts +0 -2
  109. package/src/__tests__/schedule-routes.test.ts +131 -1
  110. package/src/__tests__/scheduler-recurrence.test.ts +14 -70
  111. package/src/__tests__/scheduler-reuse-conversation.test.ts +10 -50
  112. package/src/__tests__/secret-detection-handler.test.ts +0 -10
  113. package/src/__tests__/shell-identity.test.ts +0 -134
  114. package/src/__tests__/suggestion-routes.test.ts +103 -4
  115. package/src/__tests__/task-memory-cleanup.test.ts +1 -0
  116. package/src/__tests__/task-scheduler.test.ts +3 -15
  117. package/src/__tests__/test-preload.ts +11 -0
  118. package/src/__tests__/title-generate-pipeline.test.ts +224 -0
  119. package/src/__tests__/token-estimate-pipeline.test.ts +431 -0
  120. package/src/__tests__/tool-error-pipeline.test.ts +244 -0
  121. package/src/__tests__/tool-execute-pipeline.test.ts +431 -0
  122. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -6
  123. package/src/__tests__/tool-executor-shell-integration.test.ts +7 -10
  124. package/src/__tests__/tool-executor.test.ts +141 -0
  125. package/src/__tests__/tool-result-truncate-pipeline.test.ts +356 -0
  126. package/src/__tests__/tool-result-truncation.test.ts +0 -110
  127. package/src/__tests__/user-plugin-loader.test.ts +191 -0
  128. package/src/__tests__/workspace-migration-046-seed-conversation-starters-callsite.test.ts +185 -0
  129. package/src/__tests__/workspace-migration-049-release-notes-default-sonnet.test.ts +100 -0
  130. package/src/__tests__/workspace-migration-050-seed-main-agent-opus-callsite.test.ts +171 -0
  131. package/src/__tests__/workspace-migration-051-seed-conversation-summarization-callsite.test.ts +252 -0
  132. package/src/__tests__/workspace-migration-remove-hooks.test.ts +99 -0
  133. package/src/__tests__/workspace-policy.test.ts +21 -3
  134. package/src/agent/loop.ts +340 -102
  135. package/src/approvals/__tests__/guardian-feed-event.test.ts +304 -0
  136. package/src/approvals/guardian-request-resolvers.ts +80 -0
  137. package/src/backup/__tests__/backup-worker.test.ts +2 -13
  138. package/src/backup/backup-worker.ts +3 -15
  139. package/src/bundler/app-compiler.ts +84 -1
  140. package/src/calls/call-state.ts +2 -2
  141. package/src/channels/__tests__/types.test.ts +3 -3
  142. package/src/channels/types.ts +6 -4
  143. package/src/cli/__tests__/notifications.test.ts +87 -211
  144. package/src/cli/commands/__tests__/backup.test.ts +1 -1
  145. package/src/cli/commands/__tests__/image-generation.test.ts +255 -35
  146. package/src/cli/commands/__tests__/inference-send.test.ts +12 -0
  147. package/src/cli/commands/__tests__/tts-synthesize.test.ts +12 -0
  148. package/src/cli/commands/backup.ts +2 -2
  149. package/src/cli/commands/clients.ts +138 -0
  150. package/src/cli/commands/completions.ts +2 -9
  151. package/src/cli/commands/conversations.ts +55 -7
  152. package/src/cli/commands/image-generation.ts +33 -34
  153. package/src/cli/commands/notifications.ts +68 -103
  154. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -1
  155. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -1
  156. package/src/cli/commands/oauth/connect.ts +2 -2
  157. package/src/cli/commands/oauth/providers.ts +176 -8
  158. package/src/cli/commands/oauth/status.ts +46 -36
  159. package/src/cli/commands/skills.ts +3 -4
  160. package/src/cli/program.ts +25 -29
  161. package/src/config/__tests__/backup-schema.test.ts +7 -2
  162. package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
  163. package/src/config/bundled-skills/app-builder/references/WIDGETS.md +10 -10
  164. package/src/config/bundled-skills/contacts/tools/contact-merge.ts +66 -87
  165. package/src/config/bundled-skills/contacts/tools/contact-search.ts +28 -51
  166. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +22 -40
  167. package/src/config/bundled-skills/image-studio/SKILL.md +2 -1
  168. package/src/config/bundled-skills/image-studio/TOOLS.json +2 -1
  169. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +23 -39
  170. package/src/config/bundled-skills/messaging/SKILL.md +3 -3
  171. package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +207 -0
  172. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +12 -0
  173. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +58 -0
  174. package/src/config/bundled-skills/schedule/SKILL.md +8 -3
  175. package/src/config/bundled-skills/schedule/TOOLS.json +15 -7
  176. package/src/config/bundled-skills/schedule/references/SCRIPT_MODE_PATTERNS.md +59 -0
  177. package/src/config/bundled-tool-registry.ts +0 -15
  178. package/src/config/feature-flag-registry.json +17 -1
  179. package/src/config/schema.ts +19 -0
  180. package/src/config/schemas/backup.ts +1 -1
  181. package/src/config/schemas/conversations.ts +16 -0
  182. package/src/config/schemas/llm.ts +2 -3
  183. package/src/config/schemas/security.ts +6 -6
  184. package/src/config/schemas/tts.ts +11 -0
  185. package/src/config/skill-state.ts +6 -2
  186. package/src/config/skills.ts +94 -5
  187. package/src/context/__tests__/compact-prompt.test.ts +27 -9
  188. package/src/context/prompts/compact.md +26 -12
  189. package/src/context/tool-result-truncation.ts +3 -63
  190. package/src/context/window-manager.ts +190 -16
  191. package/src/credential-health/credential-health-service.ts +19 -6
  192. package/src/daemon/__tests__/conversation-feed-event.test.ts +317 -0
  193. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +4 -12
  194. package/src/daemon/__tests__/conversation-tool-setup.test.ts +14 -15
  195. package/src/daemon/config-watcher.ts +0 -2
  196. package/src/daemon/context-overflow-policy.ts +4 -13
  197. package/src/daemon/conversation-agent-loop-handlers.ts +83 -22
  198. package/src/daemon/conversation-agent-loop.ts +984 -683
  199. package/src/daemon/conversation-history.ts +10 -19
  200. package/src/daemon/conversation-lifecycle.ts +37 -19
  201. package/src/daemon/conversation-notifiers.ts +2 -110
  202. package/src/daemon/conversation-process.ts +14 -7
  203. package/src/daemon/conversation-runtime-assembly.ts +532 -411
  204. package/src/daemon/conversation-tool-setup.ts +41 -4
  205. package/src/daemon/conversation.ts +80 -35
  206. package/src/daemon/external-plugins-bootstrap.ts +478 -0
  207. package/src/daemon/first-greeting.ts +191 -14
  208. package/src/daemon/handlers/config-model.ts +11 -0
  209. package/src/daemon/handlers/skills.ts +5 -1
  210. package/src/daemon/lifecycle.ts +33 -68
  211. package/src/daemon/message-types/computer-use.ts +2 -34
  212. package/src/daemon/message-types/conversations.ts +49 -0
  213. package/src/daemon/message-types/messages.ts +12 -0
  214. package/src/daemon/server.ts +5 -3
  215. package/src/daemon/shutdown-handlers.ts +2 -12
  216. package/src/daemon/tool-side-effects.ts +14 -56
  217. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +160 -0
  218. package/src/heartbeat/heartbeat-service.ts +24 -1
  219. package/src/home/__tests__/feed-population-integration.test.ts +312 -0
  220. package/src/home/emit-feed-event.ts +7 -0
  221. package/src/home/feed-types.ts +41 -2
  222. package/src/home/rewrite-command-preview.ts +66 -0
  223. package/src/ipc/__tests__/socket-path.test.ts +11 -50
  224. package/src/ipc/cli-client.ts +1 -1
  225. package/src/ipc/cli-server.ts +3 -3
  226. package/src/ipc/gateway-client.ts +4 -1
  227. package/src/ipc/routes/browser-context.ts +2 -0
  228. package/src/ipc/routes/browser.ts +1 -0
  229. package/src/ipc/routes/get-contact.ts +16 -0
  230. package/src/ipc/routes/index.ts +14 -0
  231. package/src/ipc/routes/list-clients.ts +31 -0
  232. package/src/ipc/routes/merge-contacts.ts +17 -0
  233. package/src/ipc/routes/notification.ts +133 -0
  234. package/src/ipc/routes/rename-conversation.ts +59 -0
  235. package/src/ipc/routes/search-contacts.ts +19 -0
  236. package/src/ipc/routes/upsert-contact.ts +25 -0
  237. package/src/ipc/socket-path.ts +14 -38
  238. package/src/media/app-icon-generator.ts +23 -46
  239. package/src/media/avatar-router.ts +26 -41
  240. package/src/media/gemini-image-service.ts +8 -41
  241. package/src/media/image-credentials.ts +73 -0
  242. package/src/media/image-service.ts +85 -0
  243. package/src/media/openai-image-service.ts +131 -0
  244. package/src/media/types.ts +46 -0
  245. package/src/memory/conversation-crud.ts +48 -18
  246. package/src/memory/conversation-queries.ts +57 -4
  247. package/src/memory/conversation-title-service.ts +25 -0
  248. package/src/memory/db-init.ts +8 -0
  249. package/src/memory/embedding-gemini.test.ts +41 -2
  250. package/src/memory/embedding-gemini.ts +6 -1
  251. package/src/memory/graph/bootstrap.test.ts +282 -0
  252. package/src/memory/graph/bootstrap.ts +8 -5
  253. package/src/memory/graph/extraction.ts +10 -2
  254. package/src/memory/graph/graph-search.test.ts +1 -0
  255. package/src/memory/graph/inspect.ts +2 -2
  256. package/src/memory/graph/retriever.ts +10 -3
  257. package/src/memory/migrations/041-approval-prompt-ts-tracker.ts +26 -0
  258. package/src/memory/migrations/149-oauth-tables.ts +1 -0
  259. package/src/memory/migrations/223-schedule-script-column.ts +11 -0
  260. package/src/memory/migrations/224-oauth-providers-managed-service-is-paid.ts +24 -0
  261. package/src/memory/migrations/225-oauth-providers-available-scopes.ts +13 -0
  262. package/src/memory/migrations/index.ts +4 -0
  263. package/src/memory/pkb/pkb-index.test.ts +1 -0
  264. package/src/memory/pkb/pkb-reconcile.test.ts +1 -0
  265. package/src/memory/pkb/pkb-search.test.ts +65 -4
  266. package/src/memory/pkb/pkb-search.ts +40 -18
  267. package/src/memory/qdrant-client.test.ts +60 -0
  268. package/src/memory/qdrant-client.ts +25 -0
  269. package/src/memory/schema/infrastructure.ts +1 -0
  270. package/src/memory/schema/oauth.ts +4 -1
  271. package/src/messaging/providers/slack/render-transcript.test.ts +77 -29
  272. package/src/messaging/providers/slack/render-transcript.ts +58 -0
  273. package/src/notifications/conversation-pairing.ts +78 -19
  274. package/src/notifications/copy-composer.ts +0 -5
  275. package/src/notifications/emit-signal.ts +1 -1
  276. package/src/notifications/signal.ts +1 -2
  277. package/src/oauth/AGENTS.md +1 -1
  278. package/src/oauth/__tests__/identity-verifier.test.ts +2 -1
  279. package/src/oauth/connect-orchestrator.ts +8 -34
  280. package/src/oauth/connect-types.ts +6 -10
  281. package/src/oauth/manual-token-connection.ts +23 -0
  282. package/src/oauth/oauth-store.ts +30 -14
  283. package/src/oauth/provider-serializer.ts +6 -1
  284. package/src/oauth/seed-providers.ts +56 -108
  285. package/src/outbound-proxy/http-forwarder.ts +9 -0
  286. package/src/permissions/approval-policy.test.ts +293 -18
  287. package/src/permissions/approval-policy.ts +110 -58
  288. package/src/permissions/arg-parser.test.ts +161 -0
  289. package/src/permissions/arg-parser.ts +141 -0
  290. package/src/permissions/bash-risk-classifier.test.ts +414 -2
  291. package/src/permissions/bash-risk-classifier.ts +303 -60
  292. package/src/permissions/checker.ts +157 -29
  293. package/src/permissions/command-registry.test.ts +239 -0
  294. package/src/permissions/command-registry.ts +234 -54
  295. package/src/permissions/defaults.ts +5 -4
  296. package/src/permissions/gateway-threshold-reader.ts +196 -0
  297. package/src/permissions/prompter.ts +4 -0
  298. package/src/permissions/risk-types.ts +61 -4
  299. package/src/permissions/schedule-risk-classifier.test.ts +129 -0
  300. package/src/permissions/schedule-risk-classifier.ts +85 -0
  301. package/src/permissions/shell-identity.ts +2 -42
  302. package/src/permissions/types.ts +2 -0
  303. package/src/permissions/workspace-policy.ts +8 -3
  304. package/src/plugins/defaults/circuit-breaker.ts +146 -0
  305. package/src/plugins/defaults/compaction.ts +145 -0
  306. package/src/plugins/defaults/empty-response.ts +126 -0
  307. package/src/plugins/defaults/history-repair.ts +85 -0
  308. package/src/plugins/defaults/index.ts +116 -0
  309. package/src/plugins/defaults/injectors.ts +491 -0
  310. package/src/plugins/defaults/llm-call.ts +82 -0
  311. package/src/plugins/defaults/memory-retrieval.ts +226 -0
  312. package/src/plugins/defaults/overflow-reduce.ts +181 -0
  313. package/src/plugins/defaults/persistence.ts +129 -0
  314. package/src/plugins/defaults/title-generate.ts +95 -0
  315. package/src/plugins/defaults/token-estimate.ts +104 -0
  316. package/src/plugins/defaults/tool-error.ts +126 -0
  317. package/src/plugins/defaults/tool-execute.ts +89 -0
  318. package/src/plugins/defaults/tool-result-truncate.ts +88 -0
  319. package/src/plugins/pipeline.ts +316 -0
  320. package/src/plugins/plugin-skill-contributions.ts +292 -0
  321. package/src/plugins/registry.ts +241 -0
  322. package/src/plugins/types.ts +1134 -0
  323. package/src/plugins/user-loader.ts +177 -0
  324. package/src/prompts/templates/BOOTSTRAP.md +27 -77
  325. package/src/providers/model-catalog.ts +52 -29
  326. package/src/providers/model-intents.ts +1 -1
  327. package/src/providers/openrouter/client.ts +5 -1
  328. package/src/providers/speech-to-text/deepgram-realtime.test.ts +61 -0
  329. package/src/providers/speech-to-text/deepgram-realtime.ts +57 -0
  330. package/src/providers/speech-to-text/xai-realtime.test.ts +72 -4
  331. package/src/providers/speech-to-text/xai-realtime.ts +39 -14
  332. package/src/runtime/AGENTS.md +25 -16
  333. package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +3 -3
  334. package/src/runtime/__tests__/client-registry.test.ts +293 -0
  335. package/src/runtime/client-registry.ts +261 -0
  336. package/src/runtime/http-server.ts +77 -8
  337. package/src/runtime/http-types.ts +0 -2
  338. package/src/runtime/migrations/vbundle-builder.ts +1 -22
  339. package/src/runtime/routes/approval-prompt-ts-tracker.ts +51 -31
  340. package/src/runtime/routes/approval-routes.ts +17 -0
  341. package/src/runtime/routes/browser-extension-pair-routes.ts +27 -8
  342. package/src/runtime/routes/conversation-routes.ts +223 -116
  343. package/src/runtime/routes/inbound-message-handler.ts +88 -13
  344. package/src/runtime/routes/memory-item-routes.test.ts +1 -0
  345. package/src/runtime/routes/migration-routes.ts +0 -3
  346. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +284 -0
  347. package/src/runtime/routes/playground/__tests__/guard.test.ts +80 -0
  348. package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +294 -0
  349. package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +271 -0
  350. package/src/runtime/routes/playground/__tests__/seed-conversation.test.ts +202 -0
  351. package/src/runtime/routes/playground/__tests__/seeded-conversations.test.ts +309 -0
  352. package/src/runtime/routes/playground/__tests__/state.test.ts +224 -0
  353. package/src/runtime/routes/playground/conversation-not-found.ts +29 -0
  354. package/src/runtime/routes/playground/deps.ts +56 -0
  355. package/src/runtime/routes/playground/force-compact.ts +73 -0
  356. package/src/runtime/routes/playground/guard.ts +37 -0
  357. package/src/runtime/routes/playground/index.ts +28 -0
  358. package/src/runtime/routes/playground/inject-failures.ts +159 -0
  359. package/src/runtime/routes/playground/reset-circuit.ts +115 -0
  360. package/src/runtime/routes/playground/seed-conversation.ts +139 -0
  361. package/src/runtime/routes/playground/seeded-conversations.ts +78 -0
  362. package/src/runtime/routes/playground/state.ts +78 -0
  363. package/src/runtime/routes/schedule-routes.ts +89 -8
  364. package/src/runtime/skill-route-registry.ts +75 -15
  365. package/src/schedule/run-script.ts +68 -0
  366. package/src/schedule/schedule-store.ts +7 -1
  367. package/src/schedule/scheduler.ts +48 -8
  368. package/src/skills/catalog-cache.ts +12 -5
  369. package/src/tools/browser/__tests__/browser-status.test.ts +189 -0
  370. package/src/tools/browser/browser-execution.ts +88 -19
  371. package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +230 -0
  372. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +146 -3
  373. package/src/tools/browser/cdp-client/extension-cdp-client.ts +54 -3
  374. package/src/tools/browser/cdp-client/factory.ts +15 -4
  375. package/src/tools/executor.ts +126 -74
  376. package/src/tools/network/script-proxy/session-manager.ts +37 -1
  377. package/src/tools/permission-checker.ts +98 -49
  378. package/src/tools/policy-context.ts +4 -0
  379. package/src/tools/registry.ts +140 -3
  380. package/src/tools/schedule/create.ts +23 -8
  381. package/src/tools/schedule/update.ts +3 -1
  382. package/src/tools/secret-detection-handler.ts +0 -51
  383. package/src/tools/system/avatar-generator.ts +6 -2
  384. package/src/tools/types.ts +28 -2
  385. package/src/util/platform.ts +7 -2
  386. package/src/util/pricing.ts +26 -3
  387. package/src/workspace/migrations/006-services-config.ts +2 -4
  388. package/src/workspace/migrations/022-move-hooks-to-workspace.ts +2 -3
  389. package/src/workspace/migrations/041-backfill-google-gmail-settings-scope.ts +3 -4
  390. package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +108 -0
  391. package/src/workspace/migrations/047-remove-watch-callsites.ts +54 -0
  392. package/src/workspace/migrations/048-remove-workspace-hooks.ts +81 -0
  393. package/src/workspace/migrations/049-release-notes-default-sonnet.ts +80 -0
  394. package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +86 -0
  395. package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +128 -0
  396. package/src/workspace/migrations/registry.ts +12 -0
  397. package/tsconfig.json +1 -1
  398. package/hook-templates/debug-prompt-logger/hook.json +0 -7
  399. package/hook-templates/debug-prompt-logger/run.sh +0 -66
  400. package/src/__tests__/compaction-circuit-breaker.test.ts +0 -336
  401. package/src/__tests__/context-overflow-approval.test.ts +0 -156
  402. package/src/__tests__/hooks-blocking.test.ts +0 -178
  403. package/src/__tests__/hooks-cli.test.ts +0 -182
  404. package/src/__tests__/hooks-config.test.ts +0 -108
  405. package/src/__tests__/hooks-discovery.test.ts +0 -211
  406. package/src/__tests__/hooks-integration.test.ts +0 -196
  407. package/src/__tests__/hooks-manager.test.ts +0 -226
  408. package/src/__tests__/hooks-runner.test.ts +0 -175
  409. package/src/__tests__/hooks-settings.test.ts +0 -160
  410. package/src/__tests__/hooks-templates.test.ts +0 -169
  411. package/src/__tests__/hooks-ts-runner.test.ts +0 -170
  412. package/src/__tests__/hooks-watch.test.ts +0 -112
  413. package/src/__tests__/notification-schedule-dedup.test.ts +0 -213
  414. package/src/__tests__/oauth-scope-policy.test.ts +0 -180
  415. package/src/__tests__/send-notification-tool.test.ts +0 -83
  416. package/src/cli/commands/shotgun.ts +0 -266
  417. package/src/config/bundled-skills/conversations/SKILL.md +0 -20
  418. package/src/config/bundled-skills/conversations/TOOLS.json +0 -23
  419. package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +0 -88
  420. package/src/config/bundled-skills/heartbeat/SKILL.md +0 -43
  421. package/src/config/bundled-skills/notifications/SKILL.md +0 -40
  422. package/src/config/bundled-skills/notifications/TOOLS.json +0 -80
  423. package/src/config/bundled-skills/notifications/tools/send-notification.ts +0 -152
  424. package/src/config/bundled-skills/notifications/tools/shared.ts +0 -13
  425. package/src/config/bundled-skills/screen-watch/SKILL.md +0 -27
  426. package/src/config/bundled-skills/screen-watch/TOOLS.json +0 -35
  427. package/src/config/bundled-skills/screen-watch/tools/start-screen-watch.ts +0 -12
  428. package/src/config/bundled-skills/skills-catalog/SKILL.md +0 -84
  429. package/src/daemon/context-overflow-approval.ts +0 -52
  430. package/src/daemon/watch-handler.ts +0 -399
  431. package/src/hooks/cli.ts +0 -253
  432. package/src/hooks/config.ts +0 -100
  433. package/src/hooks/discovery.ts +0 -135
  434. package/src/hooks/manager.ts +0 -179
  435. package/src/hooks/runner.ts +0 -117
  436. package/src/hooks/templates.ts +0 -77
  437. package/src/hooks/types.ts +0 -75
  438. package/src/oauth/scope-policy.ts +0 -89
  439. package/src/runtime/gateway-internal-client.ts +0 -94
  440. package/src/runtime/routes/watch-routes.ts +0 -156
  441. package/src/signals/shotgun.ts +0 -203
  442. package/src/tools/watch/screen-watch.ts +0 -144
  443. package/src/tools/watch/watch-state.ts +0 -142
@@ -31,13 +31,13 @@ describe("resolveOverflowAction", () => {
31
31
 
32
32
  // ── Interactive defaults ──
33
33
 
34
- test("interactive session with default config asks for user approval", () => {
34
+ test("interactive session with default config auto-compresses", () => {
35
35
  expect(
36
36
  resolveOverflowAction({
37
37
  overflowRecovery: DEFAULTS,
38
38
  isInteractive: true,
39
39
  }),
40
- ).toBe("request_user_approval");
40
+ ).toBe("auto_compress_latest_turn");
41
41
  });
42
42
 
43
43
  // ── Non-interactive defaults ──
@@ -53,7 +53,7 @@ describe("resolveOverflowAction", () => {
53
53
 
54
54
  // ── Interactive with explicit policies ──
55
55
 
56
- test("interactive + truncate policy asks for user approval", () => {
56
+ test("interactive + truncate policy auto-compresses", () => {
57
57
  expect(
58
58
  resolveOverflowAction({
59
59
  overflowRecovery: {
@@ -62,10 +62,10 @@ describe("resolveOverflowAction", () => {
62
62
  },
63
63
  isInteractive: true,
64
64
  }),
65
- ).toBe("request_user_approval");
65
+ ).toBe("auto_compress_latest_turn");
66
66
  });
67
67
 
68
- test("interactive + summarize policy asks for user approval", () => {
68
+ test("interactive + summarize policy auto-compresses", () => {
69
69
  expect(
70
70
  resolveOverflowAction({
71
71
  overflowRecovery: {
@@ -74,7 +74,7 @@ describe("resolveOverflowAction", () => {
74
74
  },
75
75
  isInteractive: true,
76
76
  }),
77
- ).toBe("request_user_approval");
77
+ ).toBe("auto_compress_latest_turn");
78
78
  });
79
79
 
80
80
  test("interactive + drop policy fails gracefully", () => {
@@ -139,7 +139,7 @@ describe("resolveOverflowAction", () => {
139
139
  },
140
140
  isInteractive: true,
141
141
  }),
142
- ).toBe("request_user_approval");
142
+ ).toBe("auto_compress_latest_turn");
143
143
  });
144
144
 
145
145
  test("non-interactive policy is independent of interactive setting", () => {
@@ -3,16 +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 {
12
14
  ContentBlock,
13
15
  Message,
14
16
  Provider,
15
17
  ProviderResponse,
18
+ SendMessageOptions,
16
19
  } from "../providers/types.js";
17
20
 
18
21
  function makeConfig(
@@ -320,12 +323,12 @@ describe("ContextWindowManager", () => {
320
323
  provider,
321
324
  systemPrompt: "system prompt",
322
325
  config: makeConfig({
323
- maxInputTokens: 620,
324
- targetBudgetRatio: 0.59,
325
- compactThreshold: 0.5,
326
+ maxInputTokens: 2000,
327
+ targetBudgetRatio: 0.4,
328
+ compactThreshold: 0.35,
326
329
  }),
327
330
  });
328
- const long = "f".repeat(500);
331
+ const long = "f".repeat(1500);
329
332
  const history: Message[] = [
330
333
  {
331
334
  role: "user",
@@ -1668,4 +1671,352 @@ describe("ContextWindowManager", () => {
1668
1671
  expect(result.compactedMessages).toBe(3);
1669
1672
  expect(result.messages).toHaveLength(1);
1670
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
+ });
1671
2022
  });
@@ -25,7 +25,7 @@ mock.module("../providers/registry.js", () => ({
25
25
  mock.module("../config/loader.js", () => ({
26
26
  getConfig: () => ({
27
27
  ui: {},
28
-
28
+
29
29
  llm: {
30
30
  default: {
31
31
  provider: "mock-provider",
@@ -168,6 +168,9 @@ mock.module("../agent/loop.js", () => ({
168
168
  getToolTokenBudget() {
169
169
  return 0;
170
170
  }
171
+ getResolvedTools() {
172
+ return [];
173
+ }
171
174
  getActiveModel() {
172
175
  return undefined;
173
176
  }
@@ -18,6 +18,7 @@ import type {
18
18
  CheckpointInfo,
19
19
  } from "../agent/loop.js";
20
20
  import type { ServerMessage } from "../daemon/message-protocol.js";
21
+ import { resetPluginRegistryAndRegisterDefaults } from "../plugins/defaults/index.js";
21
22
  import type { ContentBlock, Message } from "../providers/types.js";
22
23
 
23
24
  // ── Module mocks (must precede imports of the module under test) ─────
@@ -71,12 +72,23 @@ mock.module("../config/loader.js", () => ({
71
72
  // Token estimator — controllable per-test via mockEstimateTokens.
72
73
  // Can be a number (constant), a no-arg function, or a function that
73
74
  // receives the messages array for dynamic behavior based on content.
75
+ // Both the calibrated entry point (`estimatePromptTokens`, used in the
76
+ // convergence path) and the raw entry point (`estimatePromptTokensRaw`,
77
+ // used by the default `tokenEstimate` plugin pipeline for preflight/mid-
78
+ // loop) are stubbed so either call site can drive the test.
74
79
  let mockEstimateTokens: number | ((msgs?: Message[]) => number) = 1000;
75
80
  mock.module("../context/token-estimator.js", () => ({
76
81
  estimatePromptTokens: (msgs: Message[]) =>
77
82
  typeof mockEstimateTokens === "function"
78
83
  ? mockEstimateTokens(msgs)
79
84
  : mockEstimateTokens,
85
+ estimatePromptTokensRaw: (msgs: Message[]) =>
86
+ typeof mockEstimateTokens === "function"
87
+ ? mockEstimateTokens(msgs)
88
+ : mockEstimateTokens,
89
+ // Default plugin multiplies-in tool tokens via this helper; 0 keeps the
90
+ // stubbed raw value unchanged.
91
+ estimateToolsTokens: () => 0,
80
92
  // Conversation agent loop now calls this helper to canonicalize the
81
93
  // provider key shared with the calibration system. The tests here
82
94
  // don't exercise that path, so a passthrough mock is fine.
@@ -126,27 +138,6 @@ mock.module("../daemon/context-overflow-policy.js", () => ({
126
138
  resolveOverflowAction: () => mockOverflowAction,
127
139
  }));
128
140
 
129
- // Approval: default to denied
130
- let mockApprovalResult = { approved: false };
131
- mock.module("../daemon/context-overflow-approval.js", () => ({
132
- requestCompressionApproval: async () => mockApprovalResult,
133
- CONTEXT_OVERFLOW_TOOL_NAME: "context_overflow_compression",
134
- }));
135
-
136
- let hookBlocked = false;
137
- let hookBlockedBy = "";
138
-
139
- mock.module("../hooks/manager.js", () => ({
140
- getHookManager: () => ({
141
- trigger: async (hookName: string) => {
142
- if (hookName === "pre-message" && hookBlocked) {
143
- return { blocked: true, blockedBy: hookBlockedBy };
144
- }
145
- return { blocked: false };
146
- },
147
- }),
148
- }));
149
-
150
141
  mock.module("../memory/conversation-crud.js", () => ({
151
142
  getConversationType: () => "default",
152
143
  setConversationOriginChannelIfUnset: () => {},
@@ -174,7 +165,7 @@ mock.module("../memory/conversation-crud.js", () => ({
174
165
  getMessageById: () => null,
175
166
  updateMessageContent: () => {},
176
167
  updateMessageMetadata: () => {},
177
- clearPkbSystemReminderMetadataForConversation: () => {},
168
+ clearStrippedInjectionMetadataForConversation: () => {},
178
169
  }));
179
170
 
180
171
  mock.module("../memory/retriever.js", () => ({
@@ -380,7 +371,9 @@ type AgentLoopRun = (
380
371
  onEvent: (event: AgentEvent) => void,
381
372
  signal?: AbortSignal,
382
373
  requestId?: string,
383
- onCheckpoint?: (checkpoint: CheckpointInfo) => CheckpointDecision,
374
+ onCheckpoint?: (
375
+ checkpoint: CheckpointInfo,
376
+ ) => CheckpointDecision | Promise<CheckpointDecision>,
384
377
  ) => Promise<Message[]>;
385
378
 
386
379
  function makeCtx(
@@ -410,6 +403,7 @@ function makeCtx(
410
403
  agentLoop: {
411
404
  run: agentLoopRun,
412
405
  getToolTokenBudget: () => 0,
406
+ getResolvedTools: () => [],
413
407
  // Tests in this file don't exercise calibration, so returning
414
408
  // undefined is fine — the estimator falls back to the per-provider
415
409
  // aggregate key.
@@ -570,14 +564,16 @@ function buildLongConversation(messageCount: number): Message[] {
570
564
  // ── Tests ────────────────────────────────────────────────────────────
571
565
 
572
566
  beforeEach(() => {
573
- hookBlocked = false;
574
- hookBlockedBy = "";
575
567
  mockEstimateTokens = 1000;
576
568
  mockReducerStepFn = null;
577
569
  mockOverflowAction = "fail_gracefully";
578
- mockApprovalResult = { approved: false };
579
570
  mockApplyRuntimeInjections = (msgs) => msgs;
580
571
  recordUsageMock.mockClear();
572
+ // Reset the plugin registry and re-register every default so the
573
+ // orchestrator's pipelines (`overflowReduce`, `persistence`, …) dispatch to
574
+ // the default middleware, which in turn hits the mocked collaborators
575
+ // (`reduceContextOverflow`, `syncMessageToDisk`, …) these tests install.
576
+ resetPluginRegistryAndRegisterDefaults();
581
577
  });
582
578
 
583
579
  describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
@@ -1393,7 +1389,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1393
1389
  // Call onCheckpoint — this should trigger the mid-loop budget check
1394
1390
  // which sees 170_000 > 161_500 and returns "yield"
1395
1391
  if (onCheckpoint) {
1396
- const decision = onCheckpoint({
1392
+ const decision = await onCheckpoint({
1397
1393
  turnIndex: 0,
1398
1394
  toolCount: 1,
1399
1395
  hasToolUse: true,
@@ -1567,7 +1563,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1567
1563
  });
1568
1564
 
1569
1565
  if (onCheckpoint) {
1570
- const decision = onCheckpoint({
1566
+ const decision = await onCheckpoint({
1571
1567
  turnIndex: i,
1572
1568
  toolCount: 1,
1573
1569
  hasToolUse: true,
@@ -1751,7 +1747,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1751
1747
 
1752
1748
  // Always yield at checkpoint — simulates compaction not helping
1753
1749
  if (onCheckpoint) {
1754
- const decision = onCheckpoint({
1750
+ const decision = await onCheckpoint({
1755
1751
  turnIndex: 0,
1756
1752
  toolCount: 1,
1757
1753
  hasToolUse: true,
@@ -1907,7 +1903,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1907
1903
 
1908
1904
  // Always yield at checkpoint — simulates reduction not helping enough
1909
1905
  if (onCheckpoint) {
1910
- const decision = onCheckpoint({
1906
+ const decision = await onCheckpoint({
1911
1907
  turnIndex: 0,
1912
1908
  toolCount: 1,
1913
1909
  hasToolUse: true,