@vellumai/assistant 0.8.7 → 0.8.8-dev.202606052332.17fc8ea

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 (570) hide show
  1. package/Dockerfile +20 -4
  2. package/bun.lock +2 -2
  3. package/docker-entrypoint.sh +4 -2
  4. package/docker-init-apt-root.sh +3 -1
  5. package/docker-kata-apt-env.sh +3 -1
  6. package/docker-kata-runtime-family.sh +12 -0
  7. package/docs/architecture/memory.md +1 -1
  8. package/examples/plugins/echo/README.md +61 -66
  9. package/examples/plugins/echo/hooks/post-tool-use.ts +18 -0
  10. package/examples/plugins/echo/hooks/stop.ts +16 -0
  11. package/examples/plugins/echo/hooks/user-prompt-submit.ts +18 -0
  12. package/examples/plugins/echo/package.json +1 -2
  13. package/examples/plugins/echo/src/emit.ts +19 -0
  14. package/node_modules/@vellumai/skill-host-contracts/src/server-message.ts +3 -3
  15. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +7 -6
  16. package/openapi.yaml +3378 -335
  17. package/package.json +2 -2
  18. package/scripts/generate-openapi.ts +68 -41
  19. package/src/__tests__/agent-loop-exit-reason.test.ts +35 -93
  20. package/src/__tests__/agent-loop-provider-error-recording.test.ts +1 -1
  21. package/src/__tests__/agent-loop.test.ts +37 -87
  22. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
  23. package/src/__tests__/annotate-activity-metadata.test.ts +262 -0
  24. package/src/__tests__/annotate-risk-options.test.ts +2 -3
  25. package/src/__tests__/anthropic-provider.test.ts +95 -2
  26. package/src/__tests__/app-control-flow.test.ts +1 -1
  27. package/src/__tests__/app-dir-path-guard.test.ts +1 -0
  28. package/src/__tests__/approval-routes-http.test.ts +4 -1
  29. package/src/__tests__/assistant-event-hub.test.ts +25 -0
  30. package/src/__tests__/assistant-events-sse-shed.test.ts +8 -0
  31. package/src/__tests__/{conversation-stream-state.test.ts → assistant-stream-state.test.ts} +252 -91
  32. package/src/__tests__/auth-fallback-events-store.test.ts +116 -0
  33. package/src/__tests__/background-workers-disk-pressure.test.ts +6 -0
  34. package/src/__tests__/btw-routes.test.ts +62 -3
  35. package/src/__tests__/build-persisted-content.test.ts +184 -0
  36. package/src/__tests__/catalog-files.test.ts +1 -1
  37. package/src/__tests__/channel-approval-routes.test.ts +1 -1
  38. package/src/__tests__/channel-approvals.test.ts +1 -1
  39. package/src/__tests__/clawhub-files.test.ts +1 -1
  40. package/src/__tests__/compaction-circuit.test.ts +258 -0
  41. package/src/__tests__/compaction-direct.test.ts +132 -0
  42. package/src/__tests__/compaction.benchmark.test.ts +0 -30
  43. package/src/__tests__/config-watcher.test.ts +1 -1
  44. package/src/__tests__/conversation-abort-tool-results.test.ts +57 -19
  45. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +6 -5
  46. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +10 -7
  47. package/src/__tests__/conversation-agent-loop-overflow.test.ts +316 -1143
  48. package/src/__tests__/conversation-agent-loop.test.ts +638 -1655
  49. package/src/__tests__/conversation-analysis-routes.test.ts +6 -0
  50. package/src/__tests__/conversation-clean-command.test.ts +5 -2
  51. package/src/__tests__/conversation-history-web-search.test.ts +11 -1
  52. package/src/__tests__/conversation-pairing.test.ts +4 -31
  53. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +6 -0
  54. package/src/__tests__/conversation-provider-retry-repair.test.ts +30 -10
  55. package/src/__tests__/conversation-queue.test.ts +2 -0
  56. package/src/__tests__/conversation-routes-disk-view.test.ts +3 -0
  57. package/src/__tests__/conversation-routes-slash-commands.test.ts +6 -5
  58. package/src/__tests__/conversation-runtime-assembly.test.ts +310 -300
  59. package/src/__tests__/conversation-runtime-workspace.test.ts +105 -45
  60. package/src/__tests__/conversation-slash-commands.test.ts +8 -42
  61. package/src/__tests__/conversation-slash-queue.test.ts +6 -1
  62. package/src/__tests__/conversation-starter-routes.test.ts +14 -6
  63. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +84 -0
  64. package/src/__tests__/conversation-sync-tags.test.ts +27 -15
  65. package/src/__tests__/conversation-title-service.test.ts +135 -2
  66. package/src/__tests__/conversation-workspace-cache-state.test.ts +17 -16
  67. package/src/__tests__/conversation-workspace-injection.test.ts +67 -2
  68. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +7 -6
  69. package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
  70. package/src/__tests__/cross-provider-web-search.test.ts +214 -1
  71. package/src/__tests__/db-acp-history.test.ts +101 -0
  72. package/src/__tests__/db-schedule-syntax-migration.test.ts +5 -0
  73. package/src/__tests__/dm-persistence.test.ts +5 -1
  74. package/src/__tests__/dynamic-page-surface.test.ts +31 -0
  75. package/src/__tests__/empty-response-hook.test.ts +304 -0
  76. package/src/__tests__/feature-flag-test-helpers.ts +2 -2
  77. package/src/__tests__/file-write-tool.test.ts +63 -0
  78. package/src/__tests__/gateway-only-guard.test.ts +12 -2
  79. package/src/__tests__/gemini-image-service.test.ts +13 -0
  80. package/src/__tests__/guardian-grant-minting.test.ts +1 -1
  81. package/src/__tests__/guardian-routing-invariants.test.ts +2 -4
  82. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
  83. package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
  84. package/src/__tests__/heartbeat-service.test.ts +1 -0
  85. package/src/__tests__/helpers/mock-provider.ts +110 -0
  86. package/src/__tests__/helpers/native-web-search-harness.ts +129 -0
  87. package/src/__tests__/history-repair-hook.test.ts +1 -0
  88. package/src/__tests__/host-app-control-routes.test.ts +1 -1
  89. package/src/__tests__/host-cu-routes-targeted.test.ts +3 -3
  90. package/src/__tests__/identity-intro-cache.test.ts +12 -100
  91. package/src/__tests__/identity-routes.test.ts +248 -7
  92. package/src/__tests__/inbound-slack-persistence.test.ts +5 -1
  93. package/src/__tests__/injector-background-turn.test.ts +3 -9
  94. package/src/__tests__/injector-chain.test.ts +139 -275
  95. package/src/__tests__/injector-disk-pressure.test.ts +75 -41
  96. package/src/__tests__/injector-document-comments.test.ts +3 -3
  97. package/src/__tests__/injector-pkb-v2-silenced.test.ts +30 -22
  98. package/src/__tests__/injector-v3-suppression.test.ts +31 -37
  99. package/src/__tests__/internal-telemetry-routes.test.ts +109 -0
  100. package/src/__tests__/list-messages-hidden-metadata.test.ts +38 -0
  101. package/src/__tests__/list-messages-page-latest.test.ts +60 -0
  102. package/src/__tests__/list-messages-tool-merge.test.ts +20 -0
  103. package/src/__tests__/llm-usage-store.test.ts +223 -1
  104. package/src/__tests__/memory-retrieval-hook.test.ts +297 -0
  105. package/src/__tests__/memory-v2-static-injector.test.ts +103 -35
  106. package/src/__tests__/native-web-search.test.ts +191 -0
  107. package/src/__tests__/onboarding-template-contract.test.ts +2 -0
  108. package/src/__tests__/openai-image-service.test.ts +17 -0
  109. package/src/__tests__/openai-provider.test.ts +31 -1
  110. package/src/__tests__/{overflow-reduce-pipeline.test.ts → overflow-reduction-loop.test.ts} +64 -284
  111. package/src/__tests__/persist-unsendable-image.test.ts +215 -0
  112. package/src/__tests__/persistence-secret-redaction.test.ts +1 -0
  113. package/src/__tests__/pkb-autoinject.test.ts +2 -5
  114. package/src/__tests__/plugin-api-shim.test.ts +3 -6
  115. package/src/__tests__/plugin-bootstrap.test.ts +14 -40
  116. package/src/__tests__/plugin-registry.test.ts +3 -76
  117. package/src/__tests__/plugin-types.test.ts +0 -193
  118. package/src/__tests__/process-message-display-content.test.ts +6 -2
  119. package/src/__tests__/reaction-persistence.test.ts +1 -1
  120. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +5 -1
  121. package/src/__tests__/resolve-trust-class.test.ts +4 -4
  122. package/src/__tests__/runtime-events-sse-reconnect.test.ts +60 -23
  123. package/src/__tests__/schedule-routes.test.ts +603 -2
  124. package/src/__tests__/schedule-store.test.ts +41 -0
  125. package/src/__tests__/schedule-tools.test.ts +35 -0
  126. package/src/__tests__/send-endpoint-busy.test.ts +4 -1
  127. package/src/__tests__/server-history-render.test.ts +314 -1
  128. package/src/__tests__/skill-feature-flags-integration.test.ts +33 -0
  129. package/src/__tests__/skillssh-files.test.ts +1 -1
  130. package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
  131. package/src/__tests__/subagent-fork-notifications.test.ts +1 -3
  132. package/src/__tests__/subagent-fork-spawn.test.ts +1 -1
  133. package/src/__tests__/subagent-manager-notify.test.ts +1 -3
  134. package/src/__tests__/subagent-notify-parent.test.ts +1 -3
  135. package/src/__tests__/subagent-spawn-tool-fork.test.ts +1 -1
  136. package/src/__tests__/system-prompt.test.ts +20 -0
  137. package/src/__tests__/task-scheduler.test.ts +162 -1
  138. package/src/__tests__/terminal-tools.test.ts +6 -1
  139. package/src/__tests__/title-generate-hook.test.ts +319 -0
  140. package/src/__tests__/tool-error-hook.test.ts +278 -0
  141. package/src/__tests__/tool-preview-lifecycle.test.ts +468 -5
  142. package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
  143. package/src/__tests__/tool-result-truncate-hook.test.ts +127 -0
  144. package/src/__tests__/tool-result-truncation.test.ts +0 -2
  145. package/src/__tests__/ui-choice-copy-surfaces.test.ts +254 -0
  146. package/src/__tests__/ui-work-result-surface.test.ts +159 -0
  147. package/src/__tests__/usage-routes.test.ts +285 -1
  148. package/src/__tests__/user-plugin-loader.test.ts +54 -286
  149. package/src/__tests__/voice-session-bridge.test.ts +6 -3
  150. package/src/__tests__/web-search-backend-failure.test.ts +166 -0
  151. package/src/acp/__tests__/agent-process.test.ts +161 -0
  152. package/src/acp/__tests__/client-handler.test.ts +40 -0
  153. package/src/acp/__tests__/helpers/acp-history-db.ts +82 -0
  154. package/src/acp/__tests__/helpers/exec-file-stub.ts +101 -0
  155. package/src/acp/__tests__/prepare-agent-env.test.ts +137 -0
  156. package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
  157. package/src/acp/__tests__/session-manager-resume.test.ts +736 -0
  158. package/src/acp/agent-process.ts +61 -1
  159. package/src/acp/auto-install.test.ts +196 -0
  160. package/src/acp/auto-install.ts +177 -0
  161. package/src/acp/client-handler.ts +31 -0
  162. package/src/acp/feature-gate.test.ts +48 -0
  163. package/src/acp/feature-gate.ts +34 -0
  164. package/src/acp/prepare-agent-env.ts +83 -29
  165. package/src/acp/resolve-agent.test.ts +320 -7
  166. package/src/acp/resolve-agent.ts +182 -18
  167. package/src/acp/resume-hint.ts +25 -0
  168. package/src/acp/session-manager.ts +495 -73
  169. package/src/acp/types.ts +8 -0
  170. package/src/agent/compaction-circuit.ts +60 -102
  171. package/src/agent/loop.ts +362 -485
  172. package/src/api/events/assistant-thinking-delta.ts +33 -0
  173. package/src/api/events/tool-output-chunk.ts +45 -0
  174. package/src/api/events/tool-use-preview-start.ts +32 -0
  175. package/src/api/events/trace-event.ts +69 -0
  176. package/src/api/index.ts +48 -13
  177. package/src/api/responses/conversation-message.ts +374 -0
  178. package/src/approvals/guardian-request-resolvers.ts +1 -1
  179. package/src/avatar/__tests__/avatar-store.test.ts +34 -29
  180. package/src/background-wake/next-wake.ts +1 -0
  181. package/src/cli/commands/__tests__/notifications.test.ts +58 -14
  182. package/src/cli/commands/notifications.ts +112 -60
  183. package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
  184. package/src/config/acp-defaults.test.ts +10 -0
  185. package/src/config/acp-defaults.ts +6 -0
  186. package/src/config/assistant-feature-flags.ts +22 -11
  187. package/src/config/bundled-skills/acp/SKILL.md +83 -31
  188. package/src/config/bundled-skills/acp/TOOLS.json +4 -4
  189. package/src/config/bundled-skills/app-builder/SKILL.md +224 -398
  190. package/src/config/bundled-skills/app-builder/TOOLS.json +29 -0
  191. package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
  192. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
  193. package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
  194. package/src/config/bundled-skills/app-builder/references/examples/README.md +17 -0
  195. package/src/config/bundled-skills/app-builder/references/examples/expense-tracker.md +515 -0
  196. package/src/config/bundled-skills/app-builder/references/examples/focus-timer.md +342 -0
  197. package/src/config/bundled-skills/app-builder/references/examples/habit-tracker.md +490 -0
  198. package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
  199. package/src/config/bundled-skills/document-editor/SKILL.md +28 -23
  200. package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
  201. package/src/config/bundled-skills/messaging/SKILL.md +0 -7
  202. package/src/config/bundled-tool-registry.ts +2 -0
  203. package/src/config/feature-flag-cache.ts +3 -3
  204. package/src/config/feature-flag-registry.json +48 -7
  205. package/src/config/schemas/__tests__/memory-v2.test.ts +1 -0
  206. package/src/config/schemas/__tests__/memory-v3.test.ts +25 -0
  207. package/src/config/schemas/heartbeat.ts +9 -0
  208. package/src/config/schemas/llm.ts +1 -0
  209. package/src/config/schemas/memory-v2.ts +8 -0
  210. package/src/config/schemas/memory-v3.ts +8 -0
  211. package/src/config/schemas/platform.ts +8 -0
  212. package/src/config/seed-inference-profiles.ts +2 -2
  213. package/src/config/skills.ts +13 -0
  214. package/src/context/compactor.ts +1 -1
  215. package/src/context/strip-injections.ts +128 -0
  216. package/src/context/token-estimator.ts +23 -0
  217. package/src/context/tool-result-truncation.ts +0 -23
  218. package/src/context/window-manager.ts +5 -7
  219. package/src/credential-execution/executable-discovery.ts +16 -0
  220. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +6 -0
  221. package/src/daemon/__tests__/inference-profile-notification.test.ts +153 -0
  222. package/src/daemon/__tests__/native-web-search-metadata.test.ts +10 -8
  223. package/src/daemon/assistant-attachments.ts +1 -1
  224. package/src/daemon/config-watcher.ts +2 -2
  225. package/src/daemon/context-overflow-reducer.ts +0 -1
  226. package/src/daemon/conversation-agent-loop-handlers.ts +594 -153
  227. package/src/daemon/conversation-agent-loop.ts +301 -997
  228. package/src/daemon/conversation-history.ts +5 -4
  229. package/src/daemon/conversation-lifecycle.ts +3 -4
  230. package/src/daemon/conversation-messaging.ts +7 -6
  231. package/src/daemon/conversation-process.ts +11 -16
  232. package/src/daemon/conversation-registry.ts +159 -0
  233. package/src/daemon/conversation-runtime-assembly.ts +218 -398
  234. package/src/daemon/conversation-slash.ts +6 -25
  235. package/src/daemon/conversation-store.ts +9 -90
  236. package/src/daemon/conversation-surfaces.ts +222 -4
  237. package/src/daemon/conversation-tool-setup.ts +2 -29
  238. package/src/daemon/conversation-workspace.ts +17 -0
  239. package/src/daemon/conversation.ts +32 -20
  240. package/src/daemon/external-plugins-bootstrap.ts +17 -18
  241. package/src/daemon/handlers/config-a2a.ts +51 -36
  242. package/src/daemon/handlers/config-slack-channel.ts +20 -14
  243. package/src/daemon/handlers/config-telegram.ts +16 -2
  244. package/src/daemon/handlers/conversations.ts +3 -1
  245. package/src/daemon/handlers/shared.ts +156 -84
  246. package/src/daemon/handlers/skills.ts +42 -10
  247. package/src/daemon/lifecycle.ts +25 -0
  248. package/src/daemon/message-types/apps.ts +1 -29
  249. package/src/daemon/message-types/messages.ts +9 -57
  250. package/src/daemon/message-types/skills.ts +2 -0
  251. package/src/daemon/message-types/surfaces.ts +136 -3
  252. package/src/daemon/now-scratchpad.ts +21 -0
  253. package/src/daemon/orphan-reaper.test.ts +210 -0
  254. package/src/daemon/orphan-reaper.ts +240 -0
  255. package/src/daemon/overflow-reduction-loop.ts +230 -0
  256. package/src/daemon/persist-unsendable-image.ts +117 -0
  257. package/src/daemon/process-message.ts +1 -3
  258. package/src/daemon/server.ts +2 -0
  259. package/src/daemon/trace-emitter.ts +6 -4
  260. package/src/daemon/trust-context.ts +19 -0
  261. package/src/daemon/wake-target-adapter.ts +3 -1
  262. package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -0
  263. package/src/heartbeat/heartbeat-run-store.ts +23 -1
  264. package/src/heartbeat/heartbeat-service.ts +26 -0
  265. package/src/home/home-greeting-cache.ts +24 -1
  266. package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
  267. package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
  268. package/src/ipc/gateway-client.test.ts +2 -2
  269. package/src/ipc/gateway-client.ts +3 -3
  270. package/src/ipc/skill-routes/__tests__/memory.test.ts +15 -0
  271. package/src/ipc/skill-routes/memory.ts +4 -2
  272. package/src/media/gemini-image-service.ts +15 -0
  273. package/src/media/openai-image-service.ts +14 -0
  274. package/src/media/types.ts +34 -0
  275. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +56 -0
  276. package/src/memory/auth-fallback-events-store.ts +94 -0
  277. package/src/memory/conversation-starter-checkpoints.ts +1 -0
  278. package/src/memory/conversation-title-service.ts +65 -41
  279. package/src/memory/db-init.ts +6 -0
  280. package/src/memory/graph/__tests__/conversation-graph-memory-registry.test.ts +119 -0
  281. package/src/memory/graph/conversation-graph-memory.ts +65 -0
  282. package/src/memory/job-handlers/conversation-starters.ts +13 -2
  283. package/src/memory/jobs-store.ts +33 -0
  284. package/src/memory/jobs-worker.ts +32 -5
  285. package/src/memory/llm-usage-store.ts +224 -50
  286. package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +6 -5
  287. package/src/memory/migrations/270-schedule-source-conversation.ts +13 -0
  288. package/src/memory/migrations/271-create-auth-fallback-events.ts +21 -0
  289. package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
  290. package/src/memory/migrations/index.ts +3 -0
  291. package/src/memory/pkb/autoinject.ts +61 -0
  292. package/src/memory/pkb/context.ts +50 -0
  293. package/src/memory/pkb/types.ts +14 -0
  294. package/src/memory/schedule-attribution-sql.ts +104 -0
  295. package/src/memory/schema/acp.ts +4 -0
  296. package/src/memory/schema/infrastructure.ts +16 -0
  297. package/src/memory/usage-grouped-buckets.ts +6 -1
  298. package/src/memory/v2/__tests__/consolidation-job.test.ts +4 -4
  299. package/src/memory/v2/consolidation-job.ts +14 -5
  300. package/src/notifications/conversation-pairing.ts +8 -15
  301. package/src/notifications/decision-engine.ts +6 -3
  302. package/src/notifications/home-feed-side-effect.ts +12 -1
  303. package/src/permissions/prompter.ts +4 -0
  304. package/src/plugin-api/constants.ts +4 -0
  305. package/src/plugin-api/index.ts +7 -5
  306. package/src/plugin-api/types.ts +151 -1
  307. package/src/plugins/defaults/compaction/compact.ts +59 -0
  308. package/src/plugins/defaults/compaction/package.json +1 -1
  309. package/src/plugins/defaults/compaction/register.ts +8 -19
  310. package/src/plugins/defaults/empty-response/hooks/stop.ts +126 -0
  311. package/src/plugins/defaults/empty-response/register.ts +8 -13
  312. package/src/plugins/defaults/index.ts +2 -18
  313. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +95 -0
  314. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +216 -0
  315. package/src/plugins/defaults/memory-retrieval/injector-chain.ts +35 -0
  316. package/src/plugins/defaults/{injectors/register.ts → memory-retrieval/injectors.ts} +288 -81
  317. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/assign.test.ts +4 -4
  318. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/health.test.ts +16 -0
  319. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +4 -4
  320. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/maintain-job.test.ts +5 -5
  321. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/orchestrate.test.ts +48 -12
  322. package/src/plugins/defaults/memory-v3-shadow/__tests__/provider-blocks.test.ts +13 -0
  323. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/reconcile.test.ts +2 -2
  324. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/render-injection.test.ts +1 -1
  325. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/router.test.ts +104 -32
  326. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +8 -8
  327. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selector.test.ts +96 -30
  328. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/shadow-plugin.test.ts +34 -16
  329. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/assign.ts +5 -5
  330. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/capabilities.ts +2 -2
  331. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/health.ts +0 -0
  332. package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +14 -0
  333. package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +19 -0
  334. package/src/plugins/defaults/memory-v3-shadow/injector.ts +75 -0
  335. package/src/plugins/defaults/memory-v3-shadow/llm-retry.ts +32 -0
  336. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/maintain-job.ts +8 -8
  337. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/orchestrate.ts +26 -14
  338. package/src/plugins/defaults/{llm-call → memory-v3-shadow}/package.json +2 -2
  339. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/page-content.ts +2 -2
  340. package/src/plugins/defaults/memory-v3-shadow/provider-blocks.ts +26 -0
  341. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/reconcile.ts +3 -3
  342. package/src/plugins/defaults/memory-v3-shadow/register.ts +26 -0
  343. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +1 -1
  344. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/router.ts +51 -45
  345. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selection-log-store.ts +4 -4
  346. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selector.ts +61 -46
  347. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/shadow-plugin.ts +69 -99
  348. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/tree.ts +1 -1
  349. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/types.ts +8 -0
  350. package/src/plugins/defaults/title-generate/hooks/stop.ts +75 -0
  351. package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +35 -0
  352. package/src/plugins/defaults/title-generate/package.json +1 -1
  353. package/src/plugins/defaults/title-generate/register.ts +18 -18
  354. package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +118 -0
  355. package/src/plugins/defaults/tool-error/package.json +1 -1
  356. package/src/plugins/defaults/tool-error/register.ts +9 -21
  357. package/src/plugins/defaults/tool-result-truncate/hooks/post-tool-use.ts +32 -0
  358. package/src/plugins/defaults/tool-result-truncate/register.ts +10 -21
  359. package/src/plugins/defaults/tool-result-truncate/terminal.ts +37 -18
  360. package/src/plugins/external-api.ts +2 -2
  361. package/src/plugins/pipeline.ts +6 -305
  362. package/src/plugins/registry.ts +10 -55
  363. package/src/plugins/types.ts +62 -797
  364. package/src/plugins/user-loader.ts +30 -127
  365. package/src/proactive-artifact/aux-message-injector.ts +4 -4
  366. package/src/proactive-artifact/job.test.ts +8 -13
  367. package/src/prompts/__tests__/system-prompt.test.ts +42 -0
  368. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +64 -0
  369. package/src/prompts/templates/BOOTSTRAP.md +2 -2
  370. package/src/prompts/templates/system-sections.ts +15 -0
  371. package/src/providers/anthropic/client.ts +37 -29
  372. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +112 -0
  373. package/src/providers/openai/chat-completions-provider.ts +44 -0
  374. package/src/providers/openrouter/client.ts +1 -0
  375. package/src/providers/placeholder-sentinels.ts +35 -0
  376. package/src/runtime/__tests__/agent-wake.test.ts +10 -6
  377. package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
  378. package/src/runtime/agent-wake.ts +2 -5
  379. package/src/runtime/assistant-event-hub.ts +37 -7
  380. package/src/runtime/{conversation-stream-state.ts → assistant-stream-state.ts} +132 -58
  381. package/src/runtime/channel-approvals.ts +1 -1
  382. package/src/runtime/http-router.ts +16 -21
  383. package/src/runtime/http-types.ts +16 -70
  384. package/src/runtime/interactive-ui.ts +1 -1
  385. package/src/runtime/pending-interactions.ts +1 -0
  386. package/src/runtime/routes/__tests__/acp-routes.test.ts +283 -55
  387. package/src/runtime/routes/__tests__/consolidation-routes.test.ts +265 -2
  388. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
  389. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +31 -1
  390. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +6 -2
  391. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +5 -4
  392. package/src/runtime/routes/__tests__/surface-content-routes.test.ts +4 -1
  393. package/src/runtime/routes/__tests__/tts-routes.test.ts +6 -2
  394. package/src/runtime/routes/acp-routes.test.ts +89 -25
  395. package/src/runtime/routes/acp-routes.ts +81 -29
  396. package/src/runtime/routes/app-management-routes.ts +6 -117
  397. package/src/runtime/routes/app-routes.ts +13 -15
  398. package/src/runtime/routes/approval-routes.ts +1 -1
  399. package/src/runtime/routes/attachment-routes.ts +26 -15
  400. package/src/runtime/routes/avatar-routes.ts +26 -0
  401. package/src/runtime/routes/browser-routes.ts +1 -1
  402. package/src/runtime/routes/browser-tabs-routes.ts +6 -10
  403. package/src/runtime/routes/btw-routes.ts +29 -23
  404. package/src/runtime/routes/consolidation-routes.ts +120 -20
  405. package/src/runtime/routes/conversation-cli-routes.ts +1 -1
  406. package/src/runtime/routes/conversation-list-routes.ts +1 -1
  407. package/src/runtime/routes/conversation-query-routes.ts +3 -1
  408. package/src/runtime/routes/conversation-routes.ts +372 -185
  409. package/src/runtime/routes/conversation-starter-routes.ts +13 -7
  410. package/src/runtime/routes/conversations-import-routes.ts +24 -7
  411. package/src/runtime/routes/documents-routes.ts +4 -0
  412. package/src/runtime/routes/domain-routes.ts +51 -37
  413. package/src/runtime/routes/epoch-millis-range.ts +34 -0
  414. package/src/runtime/routes/events-routes.ts +28 -34
  415. package/src/runtime/routes/gateway-log-routes.ts +26 -4
  416. package/src/runtime/routes/heartbeat-routes.ts +32 -12
  417. package/src/runtime/routes/host-app-control-routes.ts +1 -1
  418. package/src/runtime/routes/host-cu-routes.ts +1 -1
  419. package/src/runtime/routes/identity-intro-cache.ts +11 -34
  420. package/src/runtime/routes/identity-routes.ts +224 -18
  421. package/src/runtime/routes/image-generation-routes.ts +40 -2
  422. package/src/runtime/routes/inbound-message-handler.ts +1 -1
  423. package/src/runtime/routes/index.ts +2 -0
  424. package/src/runtime/routes/integrations/a2a.ts +12 -10
  425. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +16 -0
  426. package/src/runtime/routes/integrations/slack/channel.ts +4 -0
  427. package/src/runtime/routes/integrations/slack/share.ts +27 -6
  428. package/src/runtime/routes/integrations/telegram.ts +6 -0
  429. package/src/runtime/routes/integrations/twilio.ts +42 -0
  430. package/src/runtime/routes/internal-telemetry-routes.ts +88 -0
  431. package/src/runtime/routes/log-export-routes.ts +8 -0
  432. package/src/runtime/routes/memory-v2-routes.ts +15 -8
  433. package/src/runtime/routes/memory-v3-routes.ts +66 -34
  434. package/src/runtime/routes/oauth-apps.ts +66 -12
  435. package/src/runtime/routes/oauth-providers.ts +44 -5
  436. package/src/runtime/routes/platform-routes.ts +81 -5
  437. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +6 -4
  438. package/src/runtime/routes/playground/force-compact.ts +1 -1
  439. package/src/runtime/routes/playground/helpers.ts +1 -1
  440. package/src/runtime/routes/rename-conversation-routes.ts +5 -0
  441. package/src/runtime/routes/schedule-routes.ts +152 -42
  442. package/src/runtime/routes/secret-routes.ts +14 -2
  443. package/src/runtime/routes/skills-routes.ts +43 -14
  444. package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
  445. package/src/runtime/routes/tool-call-confirmation-enrichment.test.ts +161 -0
  446. package/src/runtime/routes/tool-call-confirmation-enrichment.ts +107 -0
  447. package/src/runtime/routes/trust-rules-routes.ts +26 -2
  448. package/src/runtime/routes/tts-routes.ts +35 -0
  449. package/src/runtime/routes/types.ts +66 -8
  450. package/src/runtime/routes/usage-routes.ts +47 -39
  451. package/src/runtime/routes/webhook-routes.ts +41 -2
  452. package/src/runtime/routes/work-items-routes.ts +2 -4
  453. package/src/runtime/routes/workspace-routes.ts +4 -0
  454. package/src/runtime/services/__tests__/analyze-conversation.test.ts +6 -0
  455. package/src/runtime/services/analyze-conversation.ts +2 -2
  456. package/src/runtime/services/conversation-serializer.ts +1 -1
  457. package/src/schedule/schedule-store.ts +20 -1
  458. package/src/schedule/schedule-usage-store.ts +83 -0
  459. package/src/schedule/scheduler.ts +12 -5
  460. package/src/signals/cancel.ts +2 -4
  461. package/src/skills/catalog-files.ts +2 -2
  462. package/src/skills/catalog-install.ts +3 -0
  463. package/src/skills/categories-cache.ts +118 -0
  464. package/src/skills/clawhub-files.ts +1 -2
  465. package/src/skills/skillssh-files.ts +1 -2
  466. package/src/subagent/manager.ts +17 -5
  467. package/src/telemetry/types.ts +29 -1
  468. package/src/telemetry/usage-telemetry-reporter.test.ts +112 -3
  469. package/src/telemetry/usage-telemetry-reporter.ts +57 -2
  470. package/src/tools/acp/context.ts +20 -0
  471. package/src/tools/acp/list-agents.test.ts +7 -1
  472. package/src/tools/acp/spawn.test.ts +158 -55
  473. package/src/tools/acp/spawn.ts +47 -72
  474. package/src/tools/acp/steer.test.ts +105 -8
  475. package/src/tools/acp/steer.ts +48 -17
  476. package/src/tools/apps/executors.ts +13 -8
  477. package/src/tools/executor.ts +1 -53
  478. package/src/tools/filesystem/write.ts +34 -0
  479. package/src/tools/network/__tests__/web-search-metadata.test.ts +7 -1
  480. package/src/tools/network/__tests__/web-search.test.ts +11 -3
  481. package/src/tools/network/web-search-error.test.ts +248 -0
  482. package/src/tools/network/web-search-error.ts +267 -0
  483. package/src/tools/network/web-search.ts +207 -48
  484. package/src/tools/schedule/create.ts +2 -0
  485. package/src/tools/subagent/spawn.ts +2 -4
  486. package/src/tools/terminal/safe-env.ts +10 -1
  487. package/src/tools/ui-surface/definitions.ts +34 -5
  488. package/src/tts/__tests__/provider-catalog-consistency.test.ts +85 -1
  489. package/src/tts/provider-catalog.ts +76 -1
  490. package/src/util/mutex.ts +47 -0
  491. package/src/workspace/git-service.ts +1 -42
  492. package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +4 -5
  493. package/src/workspace/migrations/095-bump-heartbeat-interval-30m-to-60m.ts +51 -0
  494. package/src/workspace/migrations/096-reduce-quality-profile-effort.ts +72 -0
  495. package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +117 -0
  496. package/src/workspace/migrations/registry.ts +6 -0
  497. package/docs/plugins.md +0 -836
  498. package/examples/plugins/echo/register.ts +0 -184
  499. package/src/__tests__/bootstrap-turn-cleanup.test.ts +0 -44
  500. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -405
  501. package/src/__tests__/compaction-pipeline.test.ts +0 -210
  502. package/src/__tests__/compaction-timeout-recovery.test.ts +0 -251
  503. package/src/__tests__/empty-response-pipeline.test.ts +0 -423
  504. package/src/__tests__/llm-call-pipeline.test.ts +0 -287
  505. package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -418
  506. package/src/__tests__/persistence-pipeline.test.ts +0 -503
  507. package/src/__tests__/pipeline-runner.test.ts +0 -564
  508. package/src/__tests__/title-generate-pipeline.test.ts +0 -211
  509. package/src/__tests__/token-estimate-pipeline.test.ts +0 -479
  510. package/src/__tests__/tool-error-pipeline.test.ts +0 -241
  511. package/src/__tests__/tool-execute-pipeline.test.ts +0 -417
  512. package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -341
  513. package/src/daemon/bootstrap-turn-cleanup.ts +0 -45
  514. package/src/gallery/default-gallery.ts +0 -1359
  515. package/src/gallery/gallery-manifest.ts +0 -28
  516. package/src/home/feature-gate.ts +0 -22
  517. package/src/memory/v3/provider-blocks.ts +0 -16
  518. package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +0 -93
  519. package/src/plugins/defaults/circuit-breaker/package.json +0 -15
  520. package/src/plugins/defaults/circuit-breaker/register.ts +0 -39
  521. package/src/plugins/defaults/compaction/middlewares/compaction.ts +0 -25
  522. package/src/plugins/defaults/compaction/terminal.ts +0 -73
  523. package/src/plugins/defaults/empty-response/middlewares/emptyResponse.ts +0 -22
  524. package/src/plugins/defaults/empty-response/terminal.ts +0 -106
  525. package/src/plugins/defaults/injectors/package.json +0 -15
  526. package/src/plugins/defaults/llm-call/middlewares/llmCall.ts +0 -17
  527. package/src/plugins/defaults/llm-call/register.ts +0 -45
  528. package/src/plugins/defaults/memory-retrieval/middlewares/memoryRetrieval.ts +0 -17
  529. package/src/plugins/defaults/memory-retrieval/package.json +0 -15
  530. package/src/plugins/defaults/memory-retrieval/register.ts +0 -181
  531. package/src/plugins/defaults/overflow-reduce/middlewares/overflowReduce.ts +0 -126
  532. package/src/plugins/defaults/overflow-reduce/package.json +0 -15
  533. package/src/plugins/defaults/overflow-reduce/register.ts +0 -42
  534. package/src/plugins/defaults/persistence/middlewares/persistence.ts +0 -19
  535. package/src/plugins/defaults/persistence/package.json +0 -15
  536. package/src/plugins/defaults/persistence/register.ts +0 -38
  537. package/src/plugins/defaults/persistence/terminal.ts +0 -83
  538. package/src/plugins/defaults/title-generate/terminal.ts +0 -31
  539. package/src/plugins/defaults/token-estimate/middlewares/tokenEstimate.ts +0 -23
  540. package/src/plugins/defaults/token-estimate/package.json +0 -15
  541. package/src/plugins/defaults/token-estimate/register.ts +0 -34
  542. package/src/plugins/defaults/token-estimate/terminal.ts +0 -40
  543. package/src/plugins/defaults/tool-error/middlewares/toolError.ts +0 -21
  544. package/src/plugins/defaults/tool-error/terminal.ts +0 -47
  545. package/src/plugins/defaults/tool-execute/middlewares/toolExecute.ts +0 -23
  546. package/src/plugins/defaults/tool-execute/package.json +0 -15
  547. package/src/plugins/defaults/tool-execute/register.ts +0 -49
  548. package/src/plugins/defaults/tool-result-truncate/middlewares/toolResultTruncate.ts +0 -23
  549. package/src/plugins/defaults/tool-result-truncate/types.ts +0 -22
  550. package/src/skills/category-inference.ts +0 -111
  551. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/capabilities.test.ts +0 -0
  552. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/core.test.ts +0 -0
  553. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/eval-turns.json +0 -0
  554. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/live-turns.json +0 -0
  555. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/needle.test.ts +0 -0
  556. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/snapshot.test.ts +0 -0
  557. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/tree.test.ts +0 -0
  558. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +0 -0
  559. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-eviction.test.ts +0 -0
  560. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-skeleton.test.ts +0 -0
  561. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/core.ts +0 -0
  562. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/README.md +0 -0
  563. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/assignments.json +0 -0
  564. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/core.json +0 -0
  565. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-x.md +0 -0
  566. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-y.md +0 -0
  567. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-b/topic-z.md +0 -0
  568. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/needle.ts +0 -0
  569. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/snapshot.ts +0 -0
  570. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +0 -0
@@ -85,6 +85,7 @@ const stubConfig: {
85
85
  activeHoursStart: number | null;
86
86
  activeHoursEnd: number | null;
87
87
  maxConsecutiveRuns: number | null;
88
+ maxDailyRuns: number | null;
88
89
  disposition: string;
89
90
  };
90
91
  } = {
@@ -94,6 +95,7 @@ const stubConfig: {
94
95
  activeHoursStart: null,
95
96
  activeHoursEnd: null,
96
97
  maxConsecutiveRuns: null,
98
+ maxDailyRuns: null,
97
99
  disposition: "Default disposition text.",
98
100
  },
99
101
  };
@@ -195,6 +197,7 @@ mock.module("../heartbeat-run-store.js", () => ({
195
197
  markStaleRunsAsMissed: () => 0,
196
198
  markStaleRunningAsError: () => 0,
197
199
  countCompletedHeartbeatRuns: () => 10,
200
+ countCompletedRunsToday: () => 0,
198
201
  countRecentConsecutiveRuns: () => 0,
199
202
  }));
200
203
 
@@ -24,7 +24,8 @@ export type HeartbeatSkipReason =
24
24
  | "outside_active_hours"
25
25
  | "overlap"
26
26
  | "pre_first_user_message"
27
- | "max_consecutive_runs";
27
+ | "max_consecutive_runs"
28
+ | "max_daily_runs";
28
29
 
29
30
  export interface HeartbeatRunRecord {
30
31
  id: string;
@@ -217,6 +218,27 @@ export function countCompletedHeartbeatRuns(): number {
217
218
  return row?.count ?? 0;
218
219
  }
219
220
 
221
+ /**
222
+ * Count heartbeat runs that completed with status `ok` today (local midnight).
223
+ */
224
+ export function countCompletedRunsToday(): number {
225
+ const db = getDb();
226
+ const now = new Date();
227
+ const startOfDay = new Date(
228
+ now.getFullYear(),
229
+ now.getMonth(),
230
+ now.getDate(),
231
+ ).getTime();
232
+ const row = db
233
+ .select({ count: sql<number>`count(*)` })
234
+ .from(heartbeatRuns)
235
+ .where(
236
+ sql`${heartbeatRuns.status} = 'ok' AND ${heartbeatRuns.scheduledFor} >= ${startOfDay}`,
237
+ )
238
+ .get();
239
+ return row?.count ?? 0;
240
+ }
241
+
220
242
  /**
221
243
  * Count the most recent consecutive heartbeat runs with status `ok`,
222
244
  * walking backwards from newest. Stops at the first non-`ok` status
@@ -25,6 +25,7 @@ import { stripCommentLines } from "../util/strip-comment-lines.js";
25
25
  import {
26
26
  completeHeartbeatRun,
27
27
  countCompletedHeartbeatRuns,
28
+ countCompletedRunsToday,
28
29
  countRecentConsecutiveRuns,
29
30
  insertPendingHeartbeatRun,
30
31
  markStaleRunningAsError,
@@ -187,6 +188,13 @@ export class HeartbeatService {
187
188
  );
188
189
  }
189
190
 
191
+ /** Whether the daily run cap has been reached. */
192
+ get isDailyCapReached(): boolean {
193
+ const config = getConfig().heartbeat;
194
+ if (config.maxDailyRuns == null) return false;
195
+ return countCompletedRunsToday() >= config.maxDailyRuns;
196
+ }
197
+
190
198
  async runManagedWakeIfDue(
191
199
  options: ManagedWakeHeartbeatRunOptions = {},
192
200
  ): Promise<ManagedWakeHeartbeatRunResult> {
@@ -551,6 +559,24 @@ export class HeartbeatService {
551
559
  return false;
552
560
  }
553
561
 
562
+ // Daily run cap — stop burning tokens when the daily budget is exhausted.
563
+ // Force runs bypass the cap.
564
+ if (
565
+ !force &&
566
+ config.maxDailyRuns != null &&
567
+ countCompletedRunsToday() >= config.maxDailyRuns
568
+ ) {
569
+ log.debug(
570
+ { maxDailyRuns: config.maxDailyRuns },
571
+ "Daily run cap reached, skipping",
572
+ );
573
+ if (runId) skipHeartbeatRun(runId, "max_daily_runs");
574
+ if (!this.cronMode) {
575
+ this.scheduleNextRun(config.intervalMs);
576
+ }
577
+ return false;
578
+ }
579
+
554
580
  // Overlap prevention
555
581
  if (this.activeRun) {
556
582
  log.debug("Previous heartbeat run still active, skipping");
@@ -10,11 +10,15 @@
10
10
  * Storage uses the existing `memory_checkpoints` table (simple key-value store).
11
11
  */
12
12
 
13
+ import { createHash } from "node:crypto";
14
+ import { existsSync, readFileSync } from "node:fs";
15
+
13
16
  import {
14
17
  getMemoryCheckpoint,
15
18
  setMemoryCheckpoint,
16
19
  } from "../memory/checkpoints.js";
17
- import { computeIdentityContentHash } from "../runtime/routes/identity-intro-cache.js";
20
+ import { resolveGuardianPersona } from "../prompts/persona-resolver.js";
21
+ import { getWorkspacePromptPath } from "../util/platform.js";
18
22
 
19
23
  // ---------------------------------------------------------------------------
20
24
  // Constants
@@ -26,6 +30,25 @@ const CHECKPOINT_KEY_TEXT = "home:greeting:text";
26
30
  const CHECKPOINT_KEY_HASH = "home:greeting:content_hash";
27
31
  const CHECKPOINT_KEY_TIMESTAMP = "home:greeting:cached_at";
28
32
 
33
+ const IDENTITY_FILES = ["IDENTITY.md", "SOUL.md"] as const;
34
+
35
+ function readWorkspaceFile(name: string): string {
36
+ try {
37
+ const path = getWorkspacePromptPath(name);
38
+ if (!existsSync(path)) return "";
39
+ return readFileSync(path, "utf-8");
40
+ } catch {
41
+ return "";
42
+ }
43
+ }
44
+
45
+ function computeIdentityContentHash(): string {
46
+ const staticFiles = IDENTITY_FILES.map(readWorkspaceFile).join("\n---\n");
47
+ const guardianPersona = resolveGuardianPersona() ?? "";
48
+ const combined = staticFiles + "\n---\n" + guardianPersona;
49
+ return createHash("sha256").update(combined).digest("hex");
50
+ }
51
+
29
52
  // ---------------------------------------------------------------------------
30
53
  // Public API
31
54
  // ---------------------------------------------------------------------------
@@ -55,7 +55,7 @@ mock.module("../../browser/operations.js", () => ({
55
55
  },
56
56
  }));
57
57
 
58
- mock.module("../../daemon/conversation-store.js", () => ({
58
+ mock.module("../../daemon/conversation-registry.js", () => ({
59
59
  findConversation: (conversationId: string) => {
60
60
  mockFindConversationCalls.push(conversationId);
61
61
  return mockConversation ?? undefined;
@@ -32,9 +32,9 @@ let mockSurfaceResult: InteractiveUiResult = {
32
32
  let mockSurfaceThrows: Error | null = null;
33
33
 
34
34
  // Re-export the real module and override only findConversation.
35
- const realStore = await import("../../daemon/conversation-store.js");
36
- mock.module("../../daemon/conversation-store.js", () => ({
37
- ...realStore,
35
+ const realRegistry = await import("../../daemon/conversation-registry.js");
36
+ mock.module("../../daemon/conversation-registry.js", () => ({
37
+ ...realRegistry,
38
38
  findConversation: (_conversationId: string) => mockConversation ?? undefined,
39
39
  }));
40
40
 
@@ -91,7 +91,7 @@ describe("ipcGetFeatureFlags", () => {
91
91
  expect(flags).toEqual({ "flag-a": true, "flag-b": false });
92
92
  });
93
93
 
94
- test("filters non-boolean values from response", async () => {
94
+ test("accepts boolean and string values, filters other types from response", async () => {
95
95
  mockGatewayIpc(null, {
96
96
  results: {
97
97
  get_feature_flags: {
@@ -104,7 +104,7 @@ describe("ipcGetFeatureFlags", () => {
104
104
  });
105
105
 
106
106
  const flags = await ipcGetFeatureFlags();
107
- expect(flags).toEqual({ valid: true });
107
+ expect(flags).toEqual({ valid: true, string: "yes" });
108
108
  });
109
109
 
110
110
  test("returns empty record when IPC returns undefined", async () => {
@@ -98,12 +98,12 @@ export function resetPersistentClient(): void {
98
98
  */
99
99
  export async function ipcGetFeatureFlags(
100
100
  timeoutMs?: number,
101
- ): Promise<Record<string, boolean>> {
101
+ ): Promise<Record<string, boolean | string>> {
102
102
  const result = await ipcCall("get_feature_flags", undefined, timeoutMs);
103
103
  if (result && typeof result === "object" && !Array.isArray(result)) {
104
- const filtered: Record<string, boolean> = {};
104
+ const filtered: Record<string, boolean | string> = {};
105
105
  for (const [k, v] of Object.entries(result as Record<string, unknown>)) {
106
- if (typeof v === "boolean") filtered[k] = v;
106
+ if (typeof v === "boolean" || typeof v === "string") filtered[k] = v;
107
107
  }
108
108
  return filtered;
109
109
  }
@@ -137,6 +137,21 @@ describe("host.memory.addMessage", () => {
137
137
  }),
138
138
  ).rejects.toThrow();
139
139
  });
140
+
141
+ test("rejects the non-renderable system role", async () => {
142
+ // GIVEN the messages store is UI-facing (ConversationMessage), so only
143
+ // renderable turns may be persisted via this facet
144
+ // WHEN a skill attempts to add a system row
145
+ // THEN the route rejects it instead of persisting agent-context scaffolding
146
+ await expect(
147
+ memoryAddMessageRoute.handler({
148
+ conversationId: "c",
149
+ role: "system",
150
+ content: "agent-context scaffolding",
151
+ }),
152
+ ).rejects.toThrow();
153
+ expect(addMessageSpy).not.toHaveBeenCalled();
154
+ });
140
155
  });
141
156
 
142
157
  describe("host.memory.wakeAgentForOpportunity", () => {
@@ -17,7 +17,9 @@ import type { SkillIpcRoute } from "../skill-ipc-types.js";
17
17
 
18
18
  /**
19
19
  * IPC params for `addMessage()`. `role` is constrained to the
20
- * `MessageRole` union. `metadata` is a free-form record (validated
20
+ * renderable conversation roles (`user`/`assistant`): the messages store
21
+ * is UI-facing (`ConversationMessage`), so agent-context `system` rows are
22
+ * not accepted here. `metadata` is a free-form record (validated
21
23
  * downstream by `messageMetadataSchema` with a warn-and-store fallback).
22
24
  * `skipIndexing` and `clientMessageId` mirror `AddMessageOptions`.
23
25
  *
@@ -28,7 +30,7 @@ import type { SkillIpcRoute } from "../skill-ipc-types.js";
28
30
  */
29
31
  const MemoryAddMessageParams = z.object({
30
32
  conversationId: z.string().min(1),
31
- role: z.enum(["user", "assistant", "system"]),
33
+ role: z.enum(["user", "assistant"]),
32
34
  content: z.string(),
33
35
  metadata: z.record(z.string(), z.unknown()).optional(),
34
36
  skipIndexing: z.boolean().optional(),
@@ -5,6 +5,7 @@ import {
5
5
  type ImageGenCredentials,
6
6
  type ImageGenerationRequest,
7
7
  type ImageGenerationResult,
8
+ isImageProviderBillingError,
8
9
  type ManagedProxyCredentials,
9
10
  MAX_VARIANTS,
10
11
  } from "./types.js";
@@ -19,9 +20,18 @@ const ALLOWED_MODELS = new Set([
19
20
 
20
21
  // --- Error mapping ---
21
22
 
23
+ const GEMINI_BILLING_MESSAGE =
24
+ "Image generation is unavailable because the Gemini account or API key is out of credits. " +
25
+ "Add funds with the provider or update the key in Settings — retrying won't help until credits are added.";
26
+
22
27
  export function mapGeminiError(error: unknown): string {
23
28
  if (error instanceof ApiError) {
24
29
  const status = error.status;
30
+ // Billing failures are non-retryable, so check them before the rate-limit
31
+ // branch to avoid telling the user to "wait and try again".
32
+ if (isImageProviderBillingError({ status, message: error.message })) {
33
+ return GEMINI_BILLING_MESSAGE;
34
+ }
25
35
  if (status === 400) {
26
36
  return "The image request was invalid. Please check your prompt and try again.";
27
37
  }
@@ -37,6 +47,11 @@ export function mapGeminiError(error: unknown): string {
37
47
  return `Gemini API error (status ${status}). Please try again.`;
38
48
  }
39
49
  if (error instanceof Error) {
50
+ // The managed proxy surfaces failures as plain Errors whose message embeds
51
+ // the upstream status (e.g. "Managed proxy request failed (402): ...").
52
+ if (isImageProviderBillingError({ message: error.message })) {
53
+ return GEMINI_BILLING_MESSAGE;
54
+ }
40
55
  return `Image generation failed: ${error.message}`;
41
56
  }
42
57
  return "An unexpected error occurred during image generation.";
@@ -6,6 +6,7 @@ import {
6
6
  type ImageGenCredentials,
7
7
  type ImageGenerationRequest,
8
8
  type ImageGenerationResult,
9
+ isImageProviderBillingError,
9
10
  MAX_VARIANTS,
10
11
  } from "./types.js";
11
12
 
@@ -16,6 +17,10 @@ const ALLOWED_MODELS = new Set(["gpt-image-2"]);
16
17
 
17
18
  // --- Error mapping ---
18
19
 
20
+ const OPENAI_BILLING_MESSAGE =
21
+ "Image generation is unavailable because the OpenAI account or API key is out of credits. " +
22
+ "Add funds with the provider or update the key in Settings — retrying won't help until credits are added.";
23
+
19
24
  /**
20
25
  * Map an error raised by the OpenAI Images API to a user-friendly string.
21
26
  * Mirrors the status-code branches of `mapGeminiError` in
@@ -24,6 +29,12 @@ const ALLOWED_MODELS = new Set(["gpt-image-2"]);
24
29
  export function mapOpenAIError(error: unknown): string {
25
30
  if (error instanceof OpenAI.APIError) {
26
31
  const status = error.status;
32
+ // Billing failures are non-retryable and can surface as a 402 or as a 429
33
+ // with an `insufficient_quota` body, so check them before the rate-limit
34
+ // branch to avoid telling the user to "wait and try again".
35
+ if (isImageProviderBillingError({ status, message: error.message })) {
36
+ return OPENAI_BILLING_MESSAGE;
37
+ }
27
38
  if (status === 400) {
28
39
  return "The image request was invalid. Please check your prompt and try again.";
29
40
  }
@@ -39,6 +50,9 @@ export function mapOpenAIError(error: unknown): string {
39
50
  return `OpenAI API error (status ${status}). Please try again.`;
40
51
  }
41
52
  if (error instanceof Error) {
53
+ if (isImageProviderBillingError({ message: error.message })) {
54
+ return OPENAI_BILLING_MESSAGE;
55
+ }
42
56
  return `Image generation failed: ${error.message}`;
43
57
  }
44
58
  return "An unexpected error occurred during image generation.";
@@ -44,3 +44,37 @@ export function providerForImageModelPrefix(model: string): ImageGenProvider {
44
44
  }
45
45
  return "gemini";
46
46
  }
47
+
48
+ /**
49
+ * Message fragments that indicate a provider billing / insufficient-credits
50
+ * failure. Used by the per-provider error mappers to detect a non-retryable
51
+ * out-of-credits condition that no number of retries will resolve.
52
+ */
53
+ const BILLING_MESSAGE_PATTERNS: readonly RegExp[] = [
54
+ /credit balance is too low/i,
55
+ /insufficient[\s_-]*credits?/i,
56
+ /insufficient_quota/i,
57
+ /exceeded your current quota/i,
58
+ /out of credits/i,
59
+ /requires more credits/i,
60
+ /billing/i,
61
+ /request failed \(402\)/i,
62
+ ];
63
+
64
+ /**
65
+ * Detect a provider billing / insufficient-credits failure from an HTTP status
66
+ * and/or error message. A 402 status is billing by definition; otherwise the
67
+ * message is matched against known billing phrasings (OpenAI's
68
+ * `insufficient_quota` is reported as a 429, so status alone is insufficient).
69
+ *
70
+ * Billing failures are non-retryable: the user must add funds or update the API
71
+ * key. Callers surface a distinct message instead of a generic "try again".
72
+ */
73
+ export function isImageProviderBillingError(args: {
74
+ status?: number;
75
+ message?: string;
76
+ }): boolean {
77
+ if (args.status === 402) return true;
78
+ const message = args.message ?? "";
79
+ return BILLING_MESSAGE_PATTERNS.some((pattern) => pattern.test(message));
80
+ }
@@ -72,6 +72,7 @@ const CONSOLIDATE_CHECKPOINT_KEY = "memory_v2_consolidate_last_run";
72
72
 
73
73
  function buildConfig(overrides: {
74
74
  v2Enabled?: boolean;
75
+ consolidationEnabled?: boolean;
75
76
  intervalHours?: number;
76
77
  maxBufferLines?: number | null;
77
78
  }) {
@@ -79,6 +80,13 @@ function buildConfig(overrides: {
79
80
  if (overrides.v2Enabled !== undefined) {
80
81
  partial.memory.v2.enabled = overrides.v2Enabled;
81
82
  }
83
+ if (overrides.consolidationEnabled !== undefined) {
84
+ (
85
+ partial.memory.v2 as typeof partial.memory.v2 & {
86
+ consolidation_enabled?: boolean;
87
+ }
88
+ ).consolidation_enabled = overrides.consolidationEnabled;
89
+ }
82
90
  if (overrides.intervalHours !== undefined) {
83
91
  partial.memory.v2.consolidation_interval_hours = overrides.intervalHours;
84
92
  }
@@ -110,6 +118,15 @@ function countPendingJobs(type: string): number {
110
118
  .all().length;
111
119
  }
112
120
 
121
+ function consolidationJobPayloads(): Record<string, unknown>[] {
122
+ return getDb()
123
+ .select({ payload: memoryJobs.payload })
124
+ .from(memoryJobs)
125
+ .where(eq(memoryJobs.type, "memory_v2_consolidate"))
126
+ .all()
127
+ .map((row) => JSON.parse(row.payload) as Record<string, unknown>);
128
+ }
129
+
113
130
  // Initialize the DB once for the file; clear per-test tables in beforeEach
114
131
  // rather than tearing down the singleton, which is slow because it re-runs
115
132
  // every migration on the next access.
@@ -138,6 +155,7 @@ describe("maybeEnqueueGraphMaintenanceJobs — memory v2 consolidation", () => {
138
155
  maybeEnqueueGraphMaintenanceJobs(config, Date.now());
139
156
 
140
157
  expect(countPendingJobs("memory_v2_consolidate")).toBe(1);
158
+ expect(consolidationJobPayloads()).toEqual([{ trigger: "automatic" }]);
141
159
  // v1 entries are suppressed when v2 is active.
142
160
  expect(countPendingJobs("graph_decay")).toBe(0);
143
161
  expect(countPendingJobs("graph_consolidate")).toBe(0);
@@ -170,6 +188,7 @@ describe("maybeEnqueueGraphMaintenanceJobs — memory v2 consolidation", () => {
170
188
  maybeEnqueueGraphMaintenanceJobs(config, now);
171
189
 
172
190
  expect(countPendingJobs("memory_v2_consolidate")).toBe(1);
191
+ expect(consolidationJobPayloads()).toEqual([{ trigger: "automatic" }]);
173
192
  });
174
193
 
175
194
  test("respects a custom consolidation_interval_hours value", () => {
@@ -231,6 +250,28 @@ describe("maybeEnqueueGraphMaintenanceJobs — memory v2 consolidation", () => {
231
250
  expect(countPendingJobs("graph_narrative_refine")).toBe(1);
232
251
  expect(countPendingJobs("memory_v2_consolidate")).toBe(0);
233
252
  });
253
+
254
+ test("automatic consolidation off suppresses the v2 schedule without re-enabling v1 maintenance", () => {
255
+ const config = buildConfig({
256
+ v2Enabled: true,
257
+ consolidationEnabled: false,
258
+ intervalHours: 1,
259
+ });
260
+
261
+ deleteMemoryCheckpoint("graph_maintenance:decay:last_run");
262
+ deleteMemoryCheckpoint("graph_maintenance:consolidate:last_run");
263
+ deleteMemoryCheckpoint("graph_maintenance:pattern_scan:last_run");
264
+ deleteMemoryCheckpoint("graph_maintenance:narrative:last_run");
265
+ deleteMemoryCheckpoint(CONSOLIDATE_CHECKPOINT_KEY);
266
+
267
+ maybeEnqueueGraphMaintenanceJobs(config, Date.now());
268
+
269
+ expect(countPendingJobs("memory_v2_consolidate")).toBe(0);
270
+ expect(countPendingJobs("graph_decay")).toBe(0);
271
+ expect(countPendingJobs("graph_consolidate")).toBe(0);
272
+ expect(countPendingJobs("graph_pattern_scan")).toBe(0);
273
+ expect(countPendingJobs("graph_narrative_refine")).toBe(0);
274
+ });
234
275
  });
235
276
 
236
277
  describe("maybeEnqueueGraphMaintenanceJobs — buffer-size trigger", () => {
@@ -369,4 +410,19 @@ describe("maybeEnqueueGraphMaintenanceJobs — buffer-size trigger", () => {
369
410
 
370
411
  expect(countPendingJobs("memory_v2_consolidate")).toBe(0);
371
412
  });
413
+
414
+ test("size trigger inert when automatic consolidation is disabled", () => {
415
+ const config = buildConfig({
416
+ v2Enabled: true,
417
+ consolidationEnabled: false,
418
+ intervalHours: 1,
419
+ maxBufferLines: 1,
420
+ });
421
+
422
+ writeBuffer(100);
423
+
424
+ maybeEnqueueGraphMaintenanceJobs(config, Date.now());
425
+
426
+ expect(countPendingJobs("memory_v2_consolidate")).toBe(0);
427
+ });
372
428
  });
@@ -0,0 +1,94 @@
1
+ import { and, asc, eq, gt, or } from "drizzle-orm";
2
+ import { v4 as uuid } from "uuid";
3
+
4
+ import { getConfig } from "../config/loader.js";
5
+ import { getDb } from "./db-connection.js";
6
+ import { authFallbackEvents } from "./schema.js";
7
+
8
+ /** A single aggregated auth-fallback count for one (guard, path, failure_kind). */
9
+ export interface AuthFallbackCount {
10
+ guard: string;
11
+ path: string;
12
+ failureKind: string;
13
+ count: number;
14
+ }
15
+
16
+ /** A persisted auth-fallback event row. */
17
+ export interface AuthFallbackEvent {
18
+ id: string;
19
+ createdAt: number;
20
+ guard: string;
21
+ path: string;
22
+ failureKind: string;
23
+ count: number;
24
+ windowStart: number;
25
+ windowEnd: number;
26
+ }
27
+
28
+ /**
29
+ * Record a batch of aggregated auth-fallback counts forwarded by the gateway —
30
+ * one row per count entry, all sharing the same flush window. Returns the
31
+ * number of rows recorded, or 0 when usage data collection is disabled (the
32
+ * counts are dropped to honor the opt-out, matching the rest of telemetry).
33
+ */
34
+ export function recordAuthFallbackCounts(
35
+ windowStart: number,
36
+ windowEnd: number,
37
+ counts: AuthFallbackCount[],
38
+ ): number {
39
+ if (!getConfig().collectUsageData) return 0;
40
+ if (counts.length === 0) return 0;
41
+ const db = getDb();
42
+ const createdAt = Date.now();
43
+ const rows = counts.map((c) => ({
44
+ id: uuid(),
45
+ createdAt,
46
+ guard: c.guard,
47
+ path: c.path,
48
+ failureKind: c.failureKind,
49
+ count: c.count,
50
+ windowStart,
51
+ windowEnd,
52
+ }));
53
+ db.insert(authFallbackEvents).values(rows).run();
54
+ return rows.length;
55
+ }
56
+
57
+ /**
58
+ * Query auth-fallback events that haven't been reported to telemetry yet.
59
+ * Uses a compound cursor (createdAt + id) for reliable watermarking.
60
+ */
61
+ export function queryUnreportedAuthFallbackEvents(
62
+ afterCreatedAt: number,
63
+ afterId: string | undefined,
64
+ limit: number,
65
+ ): AuthFallbackEvent[] {
66
+ const db = getDb();
67
+ const rows = db
68
+ .select({
69
+ id: authFallbackEvents.id,
70
+ createdAt: authFallbackEvents.createdAt,
71
+ guard: authFallbackEvents.guard,
72
+ path: authFallbackEvents.path,
73
+ failureKind: authFallbackEvents.failureKind,
74
+ count: authFallbackEvents.count,
75
+ windowStart: authFallbackEvents.windowStart,
76
+ windowEnd: authFallbackEvents.windowEnd,
77
+ })
78
+ .from(authFallbackEvents)
79
+ .where(
80
+ afterId
81
+ ? or(
82
+ gt(authFallbackEvents.createdAt, afterCreatedAt),
83
+ and(
84
+ eq(authFallbackEvents.createdAt, afterCreatedAt),
85
+ gt(authFallbackEvents.id, afterId),
86
+ ),
87
+ )
88
+ : gt(authFallbackEvents.createdAt, afterCreatedAt),
89
+ )
90
+ .orderBy(asc(authFallbackEvents.createdAt), asc(authFallbackEvents.id))
91
+ .limit(limit)
92
+ .all();
93
+ return rows;
94
+ }
@@ -15,6 +15,7 @@ import { memoryCheckpoints } from "./schema.js";
15
15
  export const CK_ITEM_COUNT = "conversation_starters:item_count_at_last_gen";
16
16
  export const CK_BATCH = "conversation_starters:generation_batch";
17
17
  export const CK_LAST_GEN_AT = "conversation_starters:last_gen_at";
18
+ export const CK_LAST_ATTEMPT_AT = "conversation_starters:last_attempt_at";
18
19
 
19
20
  export function checkpointKey(base: string, scopeId: string): string {
20
21
  return `${base}:${scopeId}`;