@vellumai/assistant 0.7.2 → 0.7.3

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 (347) hide show
  1. package/ARCHITECTURE.md +16 -1
  2. package/docs/architecture/memory.md +5 -2
  3. package/node_modules/@vellumai/gateway-client/src/ipc-client.ts +13 -4
  4. package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +0 -9
  5. package/node_modules/@vellumai/slack-text/src/index.test.ts +18 -35
  6. package/node_modules/@vellumai/slack-text/src/index.ts +2 -48
  7. package/openapi.yaml +449 -22
  8. package/package.json +1 -1
  9. package/src/__tests__/app-control-flow.test.ts +21 -11
  10. package/src/__tests__/assistant-event-hub.test.ts +48 -0
  11. package/src/__tests__/assistant-event.test.ts +0 -10
  12. package/src/__tests__/assistant-events-sse-hardening.test.ts +2 -7
  13. package/src/__tests__/assistant-feature-flags-integration.test.ts +18 -0
  14. package/src/__tests__/auto-analysis-end-to-end.test.ts +62 -1
  15. package/src/__tests__/background-workers-disk-pressure.test.ts +268 -0
  16. package/src/__tests__/call-conversation-messages.test.ts +8 -2
  17. package/src/__tests__/channel-inbound-disk-pressure.test.ts +537 -0
  18. package/src/__tests__/channel-readiness-service.test.ts +4 -2
  19. package/src/__tests__/config-loader-backfill.test.ts +379 -0
  20. package/src/__tests__/config-schema.test.ts +1 -0
  21. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +18 -9
  22. package/src/__tests__/config-watcher.test.ts +140 -69
  23. package/src/__tests__/context-search-agent-runner.test.ts +61 -3
  24. package/src/__tests__/context-search-conversations-source.test.ts +0 -24
  25. package/src/__tests__/context-search-fanout.test.ts +0 -1
  26. package/src/__tests__/context-search-memory-source.test.ts +3 -7
  27. package/src/__tests__/context-search-memory-v2-source.test.ts +0 -2
  28. package/src/__tests__/context-search-pkb-source.test.ts +0 -1
  29. package/src/__tests__/context-search-workspace-source.test.ts +0 -1
  30. package/src/__tests__/conversation-abort-tool-results.test.ts +6 -0
  31. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +223 -0
  32. package/src/__tests__/conversation-agent-loop.test.ts +454 -5
  33. package/src/__tests__/conversation-error.test.ts +150 -3
  34. package/src/__tests__/conversation-process-callsite.test.ts +43 -0
  35. package/src/__tests__/conversation-provider-retry-repair.test.ts +6 -0
  36. package/src/__tests__/conversation-runtime-assembly.test.ts +65 -0
  37. package/src/__tests__/conversation-slash-unknown.test.ts +6 -0
  38. package/src/__tests__/conversation-speed-override.test.ts +0 -3
  39. package/src/__tests__/conversation-store.test.ts +0 -18
  40. package/src/__tests__/conversation-surfaces-app-control.test.ts +15 -4
  41. package/src/__tests__/conversation-surfaces-data-persist.test.ts +404 -0
  42. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +2 -5
  43. package/src/__tests__/conversation-workspace-injection.test.ts +6 -0
  44. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +6 -0
  45. package/src/__tests__/credentials-cli.test.ts +7 -0
  46. package/src/__tests__/cu-unified-flow.test.ts +176 -10
  47. package/src/__tests__/date-context.test.ts +164 -2
  48. package/src/__tests__/disk-pressure-guard.test.ts +262 -0
  49. package/src/__tests__/disk-pressure-lifecycle.test.ts +168 -0
  50. package/src/__tests__/disk-pressure-policy.test.ts +241 -0
  51. package/src/__tests__/disk-pressure-routes.test.ts +379 -0
  52. package/src/__tests__/disk-pressure-tools.test.ts +277 -0
  53. package/src/__tests__/disk-usage.test.ts +150 -0
  54. package/src/__tests__/events-client-registration.test.ts +52 -0
  55. package/src/__tests__/events-dev-bypass-actor.test.ts +162 -0
  56. package/src/__tests__/file-write-tool.test.ts +4 -10
  57. package/src/__tests__/filing-service.test.ts +3 -4
  58. package/src/__tests__/heartbeat-disk-pressure.test.ts +183 -0
  59. package/src/__tests__/heartbeat-service.test.ts +260 -11
  60. package/src/__tests__/host-app-control-proxy.test.ts +195 -25
  61. package/src/__tests__/host-bash-proxy.test.ts +227 -34
  62. package/src/__tests__/host-bash-routes.test.ts +178 -13
  63. package/src/__tests__/host-cu-proxy.test.ts +210 -3
  64. package/src/__tests__/host-cu-routes-targeted.test.ts +141 -12
  65. package/src/__tests__/host-file-proxy-targeted.test.ts +48 -9
  66. package/src/__tests__/host-file-proxy.test.ts +268 -6
  67. package/src/__tests__/host-file-routes-targeted.test.ts +175 -17
  68. package/src/__tests__/host-transfer-proxy-targeted.test.ts +408 -59
  69. package/src/__tests__/host-transfer-routes-targeted.test.ts +232 -17
  70. package/src/__tests__/http-user-message-parity.test.ts +107 -1
  71. package/src/__tests__/injector-chain.test.ts +18 -6
  72. package/src/__tests__/injector-disk-pressure.test.ts +224 -0
  73. package/src/__tests__/managed-profile-guard.test.ts +18 -0
  74. package/src/__tests__/mcp-abort-signal.test.ts +130 -0
  75. package/src/__tests__/memory-admin-recall.test.ts +3 -11
  76. package/src/__tests__/memory-retrieval-pipeline.test.ts +22 -1
  77. package/src/__tests__/normalize-onboarding.test.ts +180 -0
  78. package/src/__tests__/oauth-connect-routes.test.ts +316 -0
  79. package/src/__tests__/oauth-provider-seed-logos.test.ts +24 -2
  80. package/src/__tests__/onboarding-persona-write.test.ts +308 -0
  81. package/src/__tests__/openai-provider.test.ts +45 -8
  82. package/src/__tests__/persist-onboarding-artifacts.test.ts +44 -64
  83. package/src/__tests__/platform-callback-registration.test.ts +21 -4
  84. package/src/__tests__/platform.test.ts +2 -1
  85. package/src/__tests__/playbook-execution.test.ts +0 -43
  86. package/src/__tests__/plugin-tool-contribution.test.ts +47 -0
  87. package/src/__tests__/prechat-onboarding-contract.test.ts +214 -27
  88. package/src/__tests__/provider-tool-name.test.ts +23 -0
  89. package/src/__tests__/relay-server.test.ts +15 -4
  90. package/src/__tests__/runtime-events-sse.test.ts +4 -8
  91. package/src/__tests__/scheduler-disk-pressure.test.ts +148 -0
  92. package/src/__tests__/secret-ingress-http.test.ts +0 -1
  93. package/src/__tests__/suggestion-routes.test.ts +46 -0
  94. package/src/__tests__/twilio-validation.test.ts +2 -2
  95. package/src/__tests__/workspace-migration-065-bump-stale-heartbeat-interval.test.ts +122 -0
  96. package/src/__tests__/workspace-migration-066-seed-heartbeat-callsite-cost-default.test.ts +285 -0
  97. package/src/__tests__/workspace-migration-068-release-notes-local-timezone.test.ts +90 -0
  98. package/src/__tests__/workspace-migration-safe-storage-limits-release.test.ts +90 -0
  99. package/src/approvals/guardian-decision-primitive.ts +13 -0
  100. package/src/approvals/guardian-request-resolvers.ts +16 -17
  101. package/src/backup/snapshot-lock.ts +2 -27
  102. package/src/bundler/compiler-tools.ts +3 -2
  103. package/src/calls/call-conversation-messages.ts +46 -10
  104. package/src/cli/commands/__tests__/webhooks.test.ts +0 -4
  105. package/src/cli/commands/bash.ts +35 -108
  106. package/src/cli/commands/contacts.ts +64 -25
  107. package/src/cli/commands/credentials.ts +56 -0
  108. package/src/cli/commands/memory-v2.ts +7 -6
  109. package/src/cli/commands/oauth/__tests__/connect.test.ts +437 -1
  110. package/src/cli/commands/oauth/connect.ts +127 -1
  111. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -3
  112. package/src/cli/commands/platform/__tests__/connect.test.ts +7 -1
  113. package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -1
  114. package/src/cli/commands/platform/__tests__/status.test.ts +103 -6
  115. package/src/cli/commands/platform/index.ts +16 -7
  116. package/src/cli/commands/status.ts +57 -0
  117. package/src/cli/program.ts +4 -2
  118. package/src/config/assistant-feature-flags.ts +13 -3
  119. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +4 -3
  120. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +13 -7
  121. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +2 -2
  122. package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +2 -2
  123. package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +2 -2
  124. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +2 -2
  125. package/src/config/env.ts +0 -8
  126. package/src/config/feature-flag-registry.json +27 -3
  127. package/src/config/loader.ts +127 -8
  128. package/src/config/schemas/__tests__/memory-v2.test.ts +10 -5
  129. package/src/config/schemas/call-site-catalog.ts +14 -0
  130. package/src/config/schemas/channels.ts +0 -5
  131. package/src/config/schemas/heartbeat.ts +1 -1
  132. package/src/config/schemas/llm.ts +2 -0
  133. package/src/config/schemas/memory-lifecycle.ts +13 -0
  134. package/src/config/schemas/memory-v2.ts +75 -11
  135. package/src/config/schemas/platform.ts +43 -3
  136. package/src/config/schemas/services.ts +28 -0
  137. package/src/config/seed-inference-profiles.ts +230 -33
  138. package/src/contacts/contact-store.ts +0 -25
  139. package/src/daemon/__tests__/conversation-tool-setup.test.ts +86 -25
  140. package/src/daemon/assistant-attachments.ts +4 -4
  141. package/src/daemon/config-watcher.ts +85 -57
  142. package/src/daemon/conversation-agent-loop-handlers.ts +6 -0
  143. package/src/daemon/conversation-agent-loop.ts +170 -33
  144. package/src/daemon/conversation-error.ts +87 -15
  145. package/src/daemon/conversation-lifecycle.ts +1 -3
  146. package/src/daemon/conversation-process.ts +8 -0
  147. package/src/daemon/conversation-runtime-assembly.ts +26 -0
  148. package/src/daemon/conversation-store.ts +2 -2
  149. package/src/daemon/conversation-surfaces.ts +195 -15
  150. package/src/daemon/conversation-tool-setup.ts +57 -14
  151. package/src/daemon/conversation.ts +17 -22
  152. package/src/daemon/date-context.ts +71 -22
  153. package/src/daemon/disk-pressure-background-gate.ts +73 -0
  154. package/src/daemon/disk-pressure-guard.ts +343 -0
  155. package/src/daemon/disk-pressure-policy.ts +163 -0
  156. package/src/daemon/handlers/shared.ts +0 -1
  157. package/src/daemon/handlers/skills.ts +3 -4
  158. package/src/daemon/host-app-control-proxy.ts +137 -41
  159. package/src/daemon/host-bash-proxy.ts +46 -21
  160. package/src/daemon/host-cu-proxy.ts +49 -3
  161. package/src/daemon/host-file-proxy.ts +43 -7
  162. package/src/daemon/host-transfer-proxy.ts +95 -4
  163. package/src/daemon/lifecycle.ts +79 -28
  164. package/src/daemon/meet-host-supervisor.ts +4 -4
  165. package/src/daemon/meet-manifest-loader.ts +0 -1
  166. package/src/daemon/memory-v2-startup.ts +14 -4
  167. package/src/daemon/message-protocol.ts +3 -0
  168. package/src/daemon/message-types/conversations.ts +4 -0
  169. package/src/daemon/message-types/disk-pressure.ts +9 -0
  170. package/src/daemon/message-types/messages.ts +3 -0
  171. package/src/daemon/profiler-run-store.ts +5 -5
  172. package/src/daemon/tool-setup-types.ts +2 -2
  173. package/src/documents/document-store.ts +85 -0
  174. package/src/filing/filing-service.ts +30 -5
  175. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +9 -16
  176. package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +36 -0
  177. package/src/heartbeat/heartbeat-run-store.ts +13 -0
  178. package/src/heartbeat/heartbeat-service.ts +205 -31
  179. package/src/home/feed-scheduler.ts +18 -0
  180. package/src/inbound/platform-callback-registration.ts +8 -15
  181. package/src/ipc/__tests__/clients-list-ipc.test.ts +169 -0
  182. package/src/ipc/assistant-server.ts +56 -2
  183. package/src/ipc/gateway-client.ts +37 -3
  184. package/src/live-voice/live-voice-archive.ts +4 -4
  185. package/src/live-voice/protocol.ts +5 -7
  186. package/src/media/image-service.ts +1 -7
  187. package/src/memory/__tests__/fixtures/memory-v2-activation-fixtures.ts +21 -13
  188. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +52 -22
  189. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +0 -6
  190. package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +272 -0
  191. package/src/memory/admin.ts +5 -9
  192. package/src/memory/context-search/agent-runner.ts +19 -2
  193. package/src/memory/context-search/sources/conversations.ts +2 -11
  194. package/src/memory/context-search/sources/memory-v2.ts +5 -4
  195. package/src/memory/context-search/sources/memory.ts +0 -1
  196. package/src/memory/context-search/types.ts +0 -1
  197. package/src/memory/conversation-crud.ts +4 -12
  198. package/src/memory/db-init.ts +2 -0
  199. package/src/memory/embedding-runtime-manager.ts +119 -5
  200. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +32 -21
  201. package/src/memory/graph/conversation-graph-memory.ts +42 -54
  202. package/src/memory/graph/extraction.ts +1 -3
  203. package/src/memory/graph/graph-search.test.ts +10 -67
  204. package/src/memory/graph/graph-search.ts +1 -20
  205. package/src/memory/graph/retriever.test.ts +6 -0
  206. package/src/memory/graph/retriever.ts +6 -10
  207. package/src/memory/indexer.ts +54 -45
  208. package/src/memory/job-handlers/backfill.ts +2 -11
  209. package/src/memory/job-handlers/cleanup.ts +43 -0
  210. package/src/memory/job-handlers/embedding.ts +6 -8
  211. package/src/memory/job-handlers/summarization.ts +2 -7
  212. package/src/memory/jobs-store.ts +48 -0
  213. package/src/memory/jobs-worker.ts +81 -43
  214. package/src/memory/memory-v2-activation-log-store.ts +32 -14
  215. package/src/memory/memory-v2-concept-frequency.ts +169 -0
  216. package/src/memory/migrations/239-trace-events-created-at-index.ts +18 -0
  217. package/src/memory/migrations/index.ts +1 -0
  218. package/src/memory/pkb/pkb-search.test.ts +6 -0
  219. package/src/memory/qdrant-client.ts +0 -13
  220. package/src/memory/rerank-local.ts +374 -0
  221. package/src/memory/search/semantic.ts +6 -67
  222. package/src/memory/trace-event-store.ts +1 -17
  223. package/src/memory/v2/__tests__/activation.test.ts +311 -250
  224. package/src/memory/v2/__tests__/consolidation-job.test.ts +40 -8
  225. package/src/memory/v2/__tests__/injection.test.ts +157 -167
  226. package/src/memory/v2/__tests__/prompts-consolidation.test.ts +61 -2
  227. package/src/memory/v2/__tests__/qdrant.test.ts +16 -0
  228. package/src/memory/v2/__tests__/reranker.test.ts +338 -0
  229. package/src/memory/v2/__tests__/sim.test.ts +5 -199
  230. package/src/memory/v2/__tests__/skill-store.test.ts +71 -65
  231. package/src/memory/v2/__tests__/static-context.test.ts +76 -1
  232. package/src/memory/v2/activation.ts +149 -156
  233. package/src/memory/v2/consolidation-job.ts +62 -12
  234. package/src/memory/v2/injection.ts +47 -60
  235. package/src/memory/v2/prompts/consolidation.ts +36 -1
  236. package/src/memory/v2/qdrant.ts +99 -0
  237. package/src/memory/v2/reranker.ts +177 -0
  238. package/src/memory/v2/sim.ts +10 -84
  239. package/src/memory/v2/skill-content.ts +4 -3
  240. package/src/memory/v2/skill-store.ts +82 -59
  241. package/src/memory/v2/static-context.ts +22 -0
  242. package/src/memory/v2/types.ts +10 -10
  243. package/src/notifications/copy-composer.ts +13 -0
  244. package/src/notifications/signal.ts +4 -0
  245. package/src/oauth/AGENTS.md +3 -1
  246. package/src/oauth/__tests__/oauth-connect-state.test.ts +137 -0
  247. package/src/oauth/connect-orchestrator.ts +2 -0
  248. package/src/oauth/connection-resolver.test.ts +66 -1
  249. package/src/oauth/connection-resolver.ts +55 -1
  250. package/src/oauth/oauth-connect-state.ts +77 -0
  251. package/src/oauth/seed-providers.ts +58 -1
  252. package/src/plugins/defaults/injectors.ts +35 -2
  253. package/src/plugins/defaults/memory-retrieval.ts +5 -6
  254. package/src/plugins/types.ts +7 -0
  255. package/src/proactive-artifact/aux-message-injector.ts +74 -0
  256. package/src/proactive-artifact/decision.test.ts +226 -0
  257. package/src/proactive-artifact/decision.ts +165 -0
  258. package/src/proactive-artifact/index.ts +7 -0
  259. package/src/proactive-artifact/job.test.ts +867 -0
  260. package/src/proactive-artifact/job.ts +352 -0
  261. package/src/proactive-artifact/message-copy.ts +41 -0
  262. package/src/proactive-artifact/trigger-state.test.ts +277 -0
  263. package/src/proactive-artifact/trigger-state.ts +119 -0
  264. package/src/prompts/normalize-onboarding.ts +80 -0
  265. package/src/prompts/persona-resolver.ts +101 -9
  266. package/src/prompts/system-prompt.ts +21 -7
  267. package/src/prompts/templates/BOOTSTRAP.md +13 -5
  268. package/src/providers/__tests__/retry-callsite.test.ts +222 -1
  269. package/src/providers/model-intents.ts +7 -0
  270. package/src/providers/openrouter/client.ts +8 -0
  271. package/src/providers/retry.ts +50 -0
  272. package/src/providers/types.ts +1 -0
  273. package/src/runtime/__tests__/agent-wake.test.ts +456 -3
  274. package/src/runtime/agent-wake.ts +238 -100
  275. package/src/runtime/assistant-event-hub.ts +36 -6
  276. package/src/runtime/assistant-event.ts +0 -1
  277. package/src/runtime/auth/__tests__/route-policy.test.ts +64 -0
  278. package/src/runtime/auth/route-policy.ts +14 -1
  279. package/src/runtime/auth/same-actor.ts +216 -0
  280. package/src/runtime/channel-retry-sweep.ts +65 -1
  281. package/src/runtime/guardian-reply-router.ts +10 -0
  282. package/src/runtime/local-actor-identity.ts +52 -11
  283. package/src/runtime/pending-interactions.ts +8 -0
  284. package/src/runtime/routes/__tests__/client-routes.test.ts +155 -0
  285. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +0 -5
  286. package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +1 -1
  287. package/src/runtime/routes/client-routes.ts +20 -2
  288. package/src/runtime/routes/contact-routes.ts +0 -25
  289. package/src/runtime/routes/conversation-routes.ts +35 -26
  290. package/src/runtime/routes/debug-bash-routes.ts +163 -0
  291. package/src/runtime/routes/disk-pressure-routes.ts +121 -0
  292. package/src/runtime/routes/document-pdf-renderer.ts +6 -2
  293. package/src/runtime/routes/documents-routes.ts +2 -75
  294. package/src/runtime/routes/events-routes.ts +41 -9
  295. package/src/runtime/routes/host-bash-routes.ts +23 -3
  296. package/src/runtime/routes/host-cu-routes.ts +33 -6
  297. package/src/runtime/routes/host-file-routes.ts +32 -6
  298. package/src/runtime/routes/host-transfer-routes.ts +79 -16
  299. package/src/runtime/routes/identity-routes.ts +7 -138
  300. package/src/runtime/routes/inbound-message-handler.ts +77 -12
  301. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +3 -0
  302. package/src/runtime/routes/index.ts +6 -0
  303. package/src/runtime/routes/memory-item-routes.test.ts +41 -15
  304. package/src/runtime/routes/memory-v2-routes.ts +33 -0
  305. package/src/runtime/routes/oauth-connect-routes.ts +153 -0
  306. package/src/runtime/verification-outbound-actions.ts +4 -4
  307. package/src/schedule/run-script.ts +37 -5
  308. package/src/schedule/scheduler.ts +20 -1
  309. package/src/security/encrypted-store.ts +2 -0
  310. package/src/security/secure-keys.ts +55 -0
  311. package/src/skills/remote-skill-policy.ts +4 -10
  312. package/src/subagent/index.ts +1 -7
  313. package/src/subagent/manager.ts +1 -15
  314. package/src/tasks/task-runner.ts +0 -1
  315. package/src/tasks/task-store.ts +0 -3
  316. package/src/tools/background-tool-registry.ts +17 -3
  317. package/src/tools/host-filesystem/edit.test.ts +151 -0
  318. package/src/tools/host-filesystem/edit.ts +43 -1
  319. package/src/tools/host-filesystem/read.test.ts +129 -0
  320. package/src/tools/host-filesystem/read.ts +43 -1
  321. package/src/tools/host-filesystem/transfer.test.ts +127 -2
  322. package/src/tools/host-filesystem/transfer.ts +56 -11
  323. package/src/tools/host-filesystem/write.test.ts +134 -0
  324. package/src/tools/host-filesystem/write.ts +43 -1
  325. package/src/tools/host-terminal/host-shell.ts +13 -6
  326. package/src/tools/mcp/mcp-tool-factory.ts +2 -1
  327. package/src/tools/memory/register.test.ts +12 -9
  328. package/src/tools/memory/register.ts +1 -2
  329. package/src/tools/provider-tool-name.ts +28 -0
  330. package/src/tools/registry.ts +30 -9
  331. package/src/tools/terminal/shell.ts +9 -1
  332. package/src/tools/tool-approval-handler.ts +31 -6
  333. package/src/tools/types.ts +24 -2
  334. package/src/tts/provider-catalog.ts +3 -5
  335. package/src/util/disk-usage.ts +138 -0
  336. package/src/util/platform.ts +21 -11
  337. package/src/util/process-liveness.ts +26 -0
  338. package/src/workspace/heartbeat-service.ts +19 -0
  339. package/src/workspace/migrations/065-bump-stale-heartbeat-interval.ts +60 -0
  340. package/src/workspace/migrations/066-seed-heartbeat-callsite-cost-default.ts +146 -0
  341. package/src/workspace/migrations/067-release-notes-safe-storage-limits.ts +72 -0
  342. package/src/workspace/migrations/068-release-notes-local-timezone.ts +65 -0
  343. package/src/workspace/migrations/registry.ts +8 -0
  344. package/src/__tests__/conversation-tool-setup-memory-scope.test.ts +0 -167
  345. package/src/memory/v2/__tests__/skill-qdrant.test.ts +0 -657
  346. package/src/memory/v2/skill-qdrant.ts +0 -404
  347. package/src/signals/bash.ts +0 -198
@@ -8,7 +8,9 @@ import {
8
8
  type FSWatcher,
9
9
  mkdirSync,
10
10
  readdirSync,
11
+ unwatchFile,
11
12
  watch,
13
+ watchFile,
12
14
  } from "node:fs";
13
15
  import { join } from "node:path";
14
16
 
@@ -17,7 +19,6 @@ import type { MemoryCleanupConfig } from "../config/schemas/memory-lifecycle.js"
17
19
  import { resetCleanupScheduleThrottle } from "../memory/cleanup-schedule-state.js";
18
20
  import { clearEmbeddingBackendCache } from "../memory/embedding-backend.js";
19
21
  import { initializeProviders } from "../providers/registry.js";
20
- import { handleBashSignal } from "../signals/bash.js";
21
22
  import { handleCancelSignal } from "../signals/cancel.js";
22
23
  import { handleConversationUndoSignal } from "../signals/conversation-undo.js";
23
24
  import { handleEmitEventSignal } from "../signals/emit-event.js";
@@ -47,19 +48,44 @@ function attachWatcherErrorHandler(watcher: FSWatcher, dir: string): void {
47
48
  });
48
49
  }
49
50
 
51
+ /**
52
+ * Poll interval for `fs.watchFile()`. Use the stat-polling watcher
53
+ * because Bun's per-file `fs.watch()` doesn't detect renames on Linux
54
+ * (seemingly works on macOS). See https://github.com/oven-sh/bun/issues/15010.
55
+ */
56
+ const WATCH_FILE_POLL_MS = 2_000;
57
+
50
58
  export class ConfigWatcher {
51
59
  private watchers: FSWatcher[] = [];
52
- private debounceTimers = new DebouncerMap({
53
- defaultDelayMs: 200,
54
- maxEntries: 1000,
55
- protectedKeyPrefix: "__",
56
- });
60
+ private watchedFiles: Set<string> = new Set();
61
+ private stopped = false;
62
+ private debounceTimers: DebouncerMap;
57
63
  private suppressReload = false;
58
64
  lastFingerprint = "";
65
+ private lastConfig: ReturnType<typeof getConfig> | null = null;
59
66
  private lastRefreshTime = 0;
60
67
 
61
68
  static readonly REFRESH_INTERVAL_MS = 30_000;
62
69
 
70
+ /**
71
+ * @param pollIntervalMs Per-file stat poll interval (passed to
72
+ * `fs.watchFile`). Default `WATCH_FILE_POLL_MS` (2s); tests pass a
73
+ * smaller value for fast turnaround.
74
+ * @param debounceMs Debounce window applied to any detected file
75
+ * change before invoking its handler. Default 200ms; tests pass a
76
+ * smaller value to avoid sleeping unnecessarily.
77
+ */
78
+ constructor(
79
+ private readonly pollIntervalMs: number = WATCH_FILE_POLL_MS,
80
+ debounceMs = 200,
81
+ ) {
82
+ this.debounceTimers = new DebouncerMap({
83
+ defaultDelayMs: debounceMs,
84
+ maxEntries: 1000,
85
+ protectedKeyPrefix: "__",
86
+ });
87
+ }
88
+
63
89
  /** Expose the debounce timers so handlers can schedule debounced work. */
64
90
  get timers(): DebouncerMap {
65
91
  return this.debounceTimers;
@@ -88,12 +114,15 @@ export class ConfigWatcher {
88
114
 
89
115
  /** Initialize the config fingerprint (call after first config load). */
90
116
  initFingerprint(config: ReturnType<typeof getConfig>): void {
117
+ this.lastConfig = config;
91
118
  this.lastFingerprint = this.configFingerprint(config);
92
119
  }
93
120
 
94
121
  /** Update the fingerprint to match the current config. */
95
122
  updateFingerprint(): void {
96
- this.lastFingerprint = this.configFingerprint(getConfig());
123
+ const config = getConfig();
124
+ this.lastConfig = config;
125
+ this.lastFingerprint = this.configFingerprint(config);
97
126
  this.lastRefreshTime = Date.now();
98
127
  }
99
128
 
@@ -103,7 +132,7 @@ export class ConfigWatcher {
103
132
  * Returns true if config actually changed.
104
133
  */
105
134
  async refreshConfigFromSources(): Promise<boolean> {
106
- const prevCleanup = safeGetCleanupConfig();
135
+ const prevCleanup = this.lastConfig?.memory?.cleanup;
107
136
  invalidateConfigCache();
108
137
  const config = getConfig();
109
138
  const fingerprint = this.configFingerprint(config);
@@ -120,6 +149,7 @@ export class ConfigWatcher {
120
149
  }
121
150
  const isFirstInit = this.lastFingerprint === "";
122
151
  await initializeProviders(config);
152
+ this.lastConfig = config;
123
153
  this.lastFingerprint = fingerprint;
124
154
  return !isFirstInit;
125
155
  }
@@ -136,19 +166,24 @@ export class ConfigWatcher {
136
166
  onAvatarChanged?: () => void,
137
167
  onConfigChanged?: () => void,
138
168
  ): void {
169
+ // Reset the stopped flag so a stop()→start() cycle on the same
170
+ // instance resumes hot-reload instead of silently bailing in every
171
+ // watchFile callback. This matters because getConfigWatcher() is a
172
+ // module-level singleton — a daemon restart path that reuses it
173
+ // would otherwise be permanently mute.
174
+ this.stopped = false;
139
175
  const workspaceDir = getWorkspaceDir();
140
176
 
141
177
  const workspaceHandlers: Record<string, () => void> = {
142
178
  "config.json": async () => {
143
179
  if (this.suppressReload) return;
144
180
  try {
145
- const prevConfig = getConfig();
146
- const prevMcpFingerprint = JSON.stringify(prevConfig.mcp ?? {});
181
+ const prevMcpFingerprint = JSON.stringify(this.lastConfig?.mcp ?? {});
147
182
  const changed = await this.refreshConfigFromSources();
148
183
  if (changed) {
149
184
  onConversationEvict();
150
185
  onConfigChanged?.();
151
- const newConfig = getConfig();
186
+ const newConfig = this.lastConfig ?? getConfig();
152
187
  const newMcpFingerprint = JSON.stringify(newConfig.mcp ?? {});
153
188
  if (newMcpFingerprint !== prevMcpFingerprint) {
154
189
  reloadMcpServers().catch((err: unknown) => {
@@ -170,37 +205,11 @@ export class ConfigWatcher {
170
205
  },
171
206
  };
172
207
 
173
- const watchDir = (
174
- dir: string,
175
- handlers: Record<string, () => void>,
176
- label: string,
177
- ): void => {
178
- try {
179
- const watcher = watch(dir, (_eventType, filename) => {
180
- if (!filename) return;
181
- const file = String(filename);
182
- if (!handlers[file]) return;
183
- this.debounceTimers.schedule(`file:${file}`, () => {
184
- log.info({ file }, "File changed, reloading");
185
- handlers[file]();
186
- });
187
- });
188
- attachWatcherErrorHandler(watcher, dir);
189
- this.watchers.push(watcher);
190
- log.info({ dir }, `Watching ${label}`);
191
- } catch (err) {
192
- log.warn(
193
- { err, dir },
194
- `Failed to watch ${label}. Hot-reload will be unavailable.`,
195
- );
196
- }
197
- };
198
-
199
- watchDir(
200
- workspaceDir,
201
- workspaceHandlers,
202
- "workspace directory for config/prompt changes",
203
- );
208
+ // Per-file watches; don't watch the workspace directory itself because
209
+ // it contains socket files.
210
+ for (const [filename, handler] of Object.entries(workspaceHandlers)) {
211
+ this.watchFile(join(workspaceDir, filename), handler, filename);
212
+ }
204
213
 
205
214
  if (onSoundsConfigChanged) {
206
215
  this.startSoundsWatcher(onSoundsConfigChanged);
@@ -215,13 +224,46 @@ export class ConfigWatcher {
215
224
  }
216
225
 
217
226
  stop(): void {
227
+ this.stopped = true;
218
228
  this.debounceTimers.cancelAll();
229
+ for (const filePath of this.watchedFiles) {
230
+ unwatchFile(filePath);
231
+ }
232
+ this.watchedFiles.clear();
219
233
  for (const watcher of this.watchers) {
220
234
  watcher.close();
221
235
  }
222
236
  this.watchers = [];
223
237
  }
224
238
 
239
+ private watchFile(
240
+ filePath: string,
241
+ handler: () => void,
242
+ label: string,
243
+ ): void {
244
+ // Match the defensive pattern used by every other startXWatcher in
245
+ // this file: log the failure and continue. Per AGENTS.md, the daemon
246
+ // must never block startup — a watchFile() throw on some platform
247
+ // edge case must not propagate up to DaemonServer.start().
248
+ try {
249
+ watchFile(filePath, { interval: this.pollIntervalMs }, (curr, prev) => {
250
+ if (this.stopped) return;
251
+ if (curr.ino === prev.ino && curr.mtimeMs === prev.mtimeMs) return;
252
+ this.debounceTimers.schedule(`file:${filePath}`, () => {
253
+ log.info({ file: filePath }, "File changed, reloading");
254
+ handler();
255
+ });
256
+ });
257
+ this.watchedFiles.add(filePath);
258
+ log.info({ file: filePath }, `Watching ${label}`);
259
+ } catch (err) {
260
+ log.warn(
261
+ { err, file: filePath },
262
+ `Failed to watch ${label}. Hot-reload will be unavailable until restart.`,
263
+ );
264
+ }
265
+ }
266
+
225
267
  private startSoundsWatcher(onSoundsConfigChanged: () => void): void {
226
268
  const soundsDir = getSoundsDir();
227
269
  try {
@@ -341,7 +383,6 @@ export class ConfigWatcher {
341
383
  string,
342
384
  (filename: string) => void | Promise<void>
343
385
  > = {
344
- "bash.": handleBashSignal,
345
386
  "user-message.": handleUserMessageSignal,
346
387
  };
347
388
 
@@ -486,20 +527,6 @@ export class ConfigWatcher {
486
527
  }
487
528
  }
488
529
 
489
- /**
490
- * Snapshot the current cleanup config so we can compare it against the
491
- * post-reload value. Tolerant of config-load failures — if the config can't
492
- * be read (e.g. first-load), returns undefined so the comparison below
493
- * treats it as "no previous value".
494
- */
495
- function safeGetCleanupConfig(): MemoryCleanupConfig | undefined {
496
- try {
497
- return getConfig().memory?.cleanup;
498
- } catch {
499
- return undefined;
500
- }
501
- }
502
-
503
530
  /**
504
531
  * Return true if any cleanup field the user can change via the UI differs
505
532
  * between the previous and next config snapshots. Used to decide whether to
@@ -530,6 +557,7 @@ export function cleanupSettingsChanged(
530
557
  return (
531
558
  prev.llmRequestLogRetentionMs !== next.llmRequestLogRetentionMs ||
532
559
  prev.conversationRetentionDays !== next.conversationRetentionDays ||
560
+ prev.traceEventRetentionDays !== next.traceEventRetentionDays ||
533
561
  prev.enabled !== next.enabled
534
562
  );
535
563
  }
@@ -137,6 +137,8 @@ export interface EventHandlerState {
137
137
  readonly directiveWarnings: string[];
138
138
  readonly toolUseIdToName: Map<string, string>;
139
139
  currentTurnToolNames: string[];
140
+ /** Sticky for the whole run: this turn created/refreshed an app. */
141
+ appBuildToolUsedThisRun: boolean;
140
142
  /** Tracks whether the first text delta has been emitted this turn for activity state transitions. */
141
143
  firstTextDeltaEmitted: boolean;
142
144
  /** Tracks whether a thinking delta has been emitted this turn for activity state transitions. */
@@ -219,6 +221,7 @@ export function createEventHandlerState(): EventHandlerState {
219
221
  directiveWarnings: [],
220
222
  toolUseIdToName: new Map(),
221
223
  currentTurnToolNames: [],
224
+ appBuildToolUsedThisRun: false,
222
225
  firstTextDeltaEmitted: false,
223
226
  firstThinkingDeltaEmitted: false,
224
227
  lastCompletedToolName: undefined,
@@ -365,6 +368,9 @@ export function handleToolUse(
365
368
  ): void {
366
369
  state.toolUseIdToName.set(event.id, event.name);
367
370
  state.currentTurnToolNames.push(event.name);
371
+ if (event.name === "app_create" || event.name === "app_refresh") {
372
+ state.appBuildToolUsedThisRun = true;
373
+ }
368
374
  state.toolCallTimestamps.set(event.id, { startedAt: Date.now() });
369
375
  state.currentToolUseId = event.id;
370
376
  state.currentTurnToolUseIds.push(event.id);
@@ -73,7 +73,10 @@ import type { ConversationGraphMemory } from "../memory/graph/conversation-graph
73
73
  import { recordMemoryRecallLog } from "../memory/memory-recall-log-store.js";
74
74
  import { PKB_WORKSPACE_SCOPE } from "../memory/pkb/types.js";
75
75
  import type { QdrantSparseVector } from "../memory/qdrant-client.js";
76
- import { readMemoryV2StaticContent } from "../memory/v2/static-context.js";
76
+ import {
77
+ readMemoryV2StaticContent,
78
+ shouldLoadMemoryV2Static,
79
+ } from "../memory/v2/static-context.js";
77
80
  import type { PermissionPrompter } from "../permissions/prompter.js";
78
81
  import { defaultCompactionTerminal } from "../plugins/defaults/compaction.js";
79
82
  import { defaultHistoryRepairTerminal } from "../plugins/defaults/history-repair.js";
@@ -106,6 +109,11 @@ import type {
106
109
  TurnContext as PluginTurnContext,
107
110
  } from "../plugins/types.js";
108
111
  import { PluginExecutionError, PluginTimeoutError } from "../plugins/types.js";
112
+ import {
113
+ hasProactiveArtifactCompleted,
114
+ runProactiveArtifactJob,
115
+ tryClaimProactiveArtifactTrigger,
116
+ } from "../proactive-artifact/index.js";
109
117
  import type {
110
118
  ContentBlock,
111
119
  Message,
@@ -113,6 +121,7 @@ import type {
113
121
  } from "../providers/types.js";
114
122
  import type { Provider } from "../providers/types.js";
115
123
  import { resolveActorTrust } from "../runtime/actor-trust-resolver.js";
124
+ import { broadcastMessage } from "../runtime/assistant-event-hub.js";
116
125
  import { DAEMON_INTERNAL_ASSISTANT_ID } from "../runtime/assistant-scope.js";
117
126
  import { redactSecrets } from "../security/secret-scanner.js";
118
127
  import { getSubagentManager } from "../subagent/index.js";
@@ -175,7 +184,12 @@ import type { SkillProjectionCache } from "./conversation-skill-tools.js";
175
184
  import { markSurfaceCompleted } from "./conversation-surfaces.js";
176
185
  import { resolveTrustClass } from "./conversation-tool-setup.js";
177
186
  import { recordUsage } from "./conversation-usage.js";
178
- import { formatTurnTimestamp } from "./date-context.js";
187
+ import {
188
+ formatTurnTimestamp,
189
+ resolveTurnTimezoneContext,
190
+ } from "./date-context.js";
191
+ import { getDiskPressureStatus } from "./disk-pressure-guard.js";
192
+ import { classifyDiskPressureTurnPolicy } from "./disk-pressure-policy.js";
179
193
  import { deepRepairHistory } from "./history-repair.js";
180
194
  import type {
181
195
  DynamicPageSurfaceData,
@@ -192,6 +206,8 @@ import type { TrustContext } from "./trust-context.js";
192
206
  import { stripHistoricalWebSearchResults } from "./web-search-history.js";
193
207
 
194
208
  const log = getLogger("conversation-agent-loop");
209
+ const DISK_PRESSURE_ERROR_CODE = "DISK_SPACE_CRITICAL" as const;
210
+ const DISK_PRESSURE_ERROR_CATEGORY = "disk_pressure";
195
211
 
196
212
  /** Title-cased friendly labels for tool names, used in confirmation chips. */
197
213
  const TOOL_FRIENDLY_LABEL: Record<string, string> = {
@@ -211,6 +227,10 @@ type GitServiceInitializer = {
211
227
  ensureInitialized(): Promise<void>;
212
228
  };
213
229
 
230
+ function formatDiskPressureBlockedMessage(): string {
231
+ return "Storage is critically low, so background processes are paused and remote messages are ignored until the guardian frees enough space. Remote senders should try again later.";
232
+ }
233
+
214
234
  // ── Compaction circuit-breaker pipeline helpers ─────────────────────
215
235
  //
216
236
  // The circuit-breaker behavior (3 consecutive summary-LLM failures trips a
@@ -438,7 +458,6 @@ export interface AgentLoopConversationContext {
438
458
  /** Timestamp (ms since epoch) until which the circuit breaker is open. */
439
459
  compactionCircuitOpenUntil: number | null;
440
460
 
441
- readonly memoryPolicy: { scopeId: string; includeDefaultFallback: boolean };
442
461
  readonly graphMemory: ConversationGraphMemory;
443
462
 
444
463
  currentActiveSurfaceId?: string;
@@ -495,9 +514,11 @@ export interface AgentLoopConversationContext {
495
514
  voiceCallControlPrompt?: string;
496
515
  transportHints?: string[];
497
516
  slackRuntimeContextNotice?: string;
517
+ clientTimezone?: string;
498
518
 
499
519
  readonly coreToolNames: Set<string>;
500
520
  allowedToolNames?: Set<string>;
521
+ diskPressureCleanupModeActive?: boolean;
501
522
  toolsDisabledDepth: number;
502
523
  preactivatedSkillIds?: string[];
503
524
  readonly skillProjectionState: Map<string, string>;
@@ -707,19 +728,39 @@ export async function runAgentLoopImpl(
707
728
  };
708
729
  })();
709
730
 
731
+ const isInteractiveResolved =
732
+ options?.isInteractive ?? (!ctx.hasNoClient && !ctx.headlessLock);
733
+ const diskPressureDecision = classifyDiskPressureTurnPolicy(
734
+ getDiskPressureStatus(),
735
+ {
736
+ conversationType: turnStartConversation?.conversationType ?? null,
737
+ conversationSource: turnStartConversation?.source ?? null,
738
+ callSite: turnCallSite,
739
+ isInteractive: isInteractiveResolved,
740
+ sourceChannel:
741
+ ctx.trustContext?.sourceChannel ??
742
+ capturedTurnChannelContext.userMessageChannel,
743
+ sourceInterface:
744
+ ctx.channelCapabilities?.clientOS ??
745
+ capturedTurnInterfaceContext.userMessageInterface,
746
+ trustContext: ctx.trustContext
747
+ ? {
748
+ sourceChannel: ctx.trustContext.sourceChannel,
749
+ trustClass: ctx.trustContext.trustClass,
750
+ }
751
+ : null,
752
+ },
753
+ );
754
+ const diskPressureContext =
755
+ diskPressureDecision.action === "allow-cleanup-mode"
756
+ ? { cleanupModeActive: true }
757
+ : null;
758
+ ctx.diskPressureCleanupModeActive =
759
+ diskPressureDecision.action === "allow-cleanup-mode";
760
+
710
761
  ctx.lastAssistantAttachments = [];
711
762
  ctx.lastAttachmentWarnings = [];
712
763
 
713
- // Ensure workspace git repo is initialized before any tools run.
714
- try {
715
- const getWorkspaceGitServiceFn =
716
- ctx.getWorkspaceGitService ?? getWorkspaceGitService;
717
- const gitService = getWorkspaceGitServiceFn(ctx.workingDir);
718
- await gitService.ensureInitialized();
719
- } catch (err) {
720
- rlog.warn({ err }, "Failed to initialize workspace git repo (non-fatal)");
721
- }
722
-
723
764
  ctx.profiler.startRequest();
724
765
  let turnStarted = false;
725
766
 
@@ -736,6 +777,52 @@ export async function runAgentLoopImpl(
736
777
  });
737
778
 
738
779
  try {
780
+ if (diskPressureDecision.action === "block") {
781
+ const message = formatDiskPressureBlockedMessage();
782
+ rlog.warn(
783
+ { reason: diskPressureDecision.reason },
784
+ "Blocked turn during disk pressure cleanup mode",
785
+ );
786
+ ctx.emitActivityState("idle", "error_terminal", "global", reqId);
787
+ ctx.traceEmitter.emit("request_error", message, {
788
+ requestId: reqId,
789
+ status: "error",
790
+ attributes: {
791
+ errorCategory: DISK_PRESSURE_ERROR_CATEGORY,
792
+ errorCode: DISK_PRESSURE_ERROR_CODE,
793
+ diskPressureReason: diskPressureDecision.reason,
794
+ },
795
+ });
796
+ onEvent({
797
+ type: "error",
798
+ conversationId: ctx.conversationId,
799
+ requestId: reqId,
800
+ code: DISK_PRESSURE_ERROR_CODE,
801
+ message,
802
+ category: DISK_PRESSURE_ERROR_CATEGORY,
803
+ errorCategory: DISK_PRESSURE_ERROR_CATEGORY,
804
+ });
805
+ onEvent({
806
+ type: "conversation_error",
807
+ conversationId: ctx.conversationId,
808
+ code: DISK_PRESSURE_ERROR_CODE,
809
+ userMessage: message,
810
+ retryable: true,
811
+ errorCategory: DISK_PRESSURE_ERROR_CATEGORY,
812
+ });
813
+ return;
814
+ }
815
+
816
+ // Ensure workspace git repo is initialized before any tools run.
817
+ try {
818
+ const getWorkspaceGitServiceFn =
819
+ ctx.getWorkspaceGitService ?? getWorkspaceGitService;
820
+ const gitService = getWorkspaceGitServiceFn(ctx.workingDir);
821
+ await gitService.ensureInitialized();
822
+ } catch (err) {
823
+ rlog.warn({ err }, "Failed to initialize workspace git repo (non-fatal)");
824
+ }
825
+
739
826
  // Auto-complete stale interactive surfaces from previous turns.
740
827
  // Only dismiss when the user sends a new message (not a surface action
741
828
  // response), so internal turns (subagent notifications, lifecycle
@@ -1238,14 +1325,16 @@ export async function runAgentLoopImpl(
1238
1325
 
1239
1326
  // Compute fresh turn timestamp for date grounding.
1240
1327
  // Absolute "now" is always anchored to assistant host clock, while local
1241
- // date semantics prefer configured user timezone, then recalled memory.
1328
+ // date semantics prefer configured user timezone, then device timezones.
1242
1329
  const hostTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
1243
- const configuredUserTimeZone = getConfig().ui.userTimezone ?? null;
1244
- const recalledUserTimeZone = null;
1245
- const timestamp = formatTurnTimestamp({
1330
+ const timezoneContext = resolveTurnTimezoneContext({
1331
+ configuredUserTimeZone: config.ui.userTimezone ?? null,
1332
+ clientTimezone: ctx.clientTimezone ?? null,
1333
+ detectedTimezone: config.ui.detectedTimezone ?? null,
1246
1334
  hostTimeZone,
1247
- configuredUserTimeZone,
1248
- userTimeZone: recalledUserTimeZone,
1335
+ });
1336
+ const timestamp = formatTurnTimestamp({
1337
+ timeZone: timezoneContext.effectiveTimezone,
1249
1338
  });
1250
1339
 
1251
1340
  // Resolve the inbound actor context for the unified <turn_context> block.
@@ -1299,23 +1388,26 @@ export async function runAgentLoopImpl(
1299
1388
  }
1300
1389
  }
1301
1390
 
1391
+ const baseTurnContext = {
1392
+ timestamp,
1393
+ interfaceName,
1394
+ channelName,
1395
+ configuredUserTimezone: timezoneContext.configuredUserTimezone,
1396
+ clientTimezone: timezoneContext.clientTimezone,
1397
+ detectedTimezone: timezoneContext.detectedTimezone,
1398
+ timeSinceLastMessage,
1399
+ };
1302
1400
  const unifiedTurnContextStr = buildUnifiedTurnContextBlock(
1303
1401
  isGuardian
1304
- ? { timestamp, interfaceName, channelName, timeSinceLastMessage }
1402
+ ? baseTurnContext
1305
1403
  : {
1306
- timestamp,
1307
- interfaceName,
1308
- channelName,
1404
+ ...baseTurnContext,
1309
1405
  actorContext: resolvedInboundActorContext,
1310
- timeSinceLastMessage,
1311
1406
  },
1312
1407
  );
1313
1408
 
1314
1409
  // The `remember` tool handles scratchpad-style memory writes directly to the graph.
1315
1410
 
1316
- const isInteractiveResolved =
1317
- options?.isInteractive ?? (!ctx.hasNoClient && !ctx.headlessLock);
1318
-
1319
1411
  // Inject NOW.md and PKB content only on the first turn (or after
1320
1412
  // compaction re-strips them). Old injections persist in history and
1321
1413
  // are never stripped on normal turns — this preserves the cached prefix.
@@ -1330,10 +1422,16 @@ export async function runAgentLoopImpl(
1330
1422
  const pkbActive = currentPkbContent !== null;
1331
1423
 
1332
1424
  // V2 static memory block (essentials/threads/recent/buffer). Same
1333
- // first-turn / post-compaction cadence as PKB `readMemoryV2StaticContent`
1334
- // self-gates on the v2 flag + config, returning null when v2 is off.
1335
- // Skip the file reads entirely on non-injection turns.
1336
- const currentMemoryV2Static = shouldInjectNowAndPkb
1425
+ // first-turn / post-compaction cadence as PKB. `shouldLoadMemoryV2Static`
1426
+ // also blocks remote-channel non-guardian actors from inducing the
1427
+ // model to recite private memory; `readMemoryV2StaticContent` self-gates
1428
+ // on the v2 flag + config and returns null when v2 is off, so the file
1429
+ // reads are skipped on non-injection turns.
1430
+ const currentMemoryV2Static = shouldLoadMemoryV2Static({
1431
+ shouldInjectNowAndPkb,
1432
+ sourceChannel: ctx.trustContext?.sourceChannel,
1433
+ isTrustedActor,
1434
+ })
1337
1435
  ? readMemoryV2StaticContent()
1338
1436
  : null;
1339
1437
  const memoryV2Static = currentMemoryV2Static;
@@ -1349,8 +1447,8 @@ export async function runAgentLoopImpl(
1349
1447
  // `getInContextPkbPaths` re-reads `conversation.messages` on each call,
1350
1448
  // so post-compaction re-injects see the updated history.
1351
1449
  const pkbConversation = pkbActive ? ctx : undefined;
1352
- // PKB points live under a single workspace sentinel scope, not the
1353
- // conversation's memoryPolicy.scopeId. See `PKB_WORKSPACE_SCOPE` for why.
1450
+ // PKB points live under a single workspace sentinel scope.
1451
+ // See `PKB_WORKSPACE_SCOPE` for why.
1354
1452
  const pkbScopeId = pkbActive ? PKB_WORKSPACE_SCOPE : undefined;
1355
1453
 
1356
1454
  // Subagent status injection — gives the parent LLM visibility into active/completed children.
@@ -1417,6 +1515,7 @@ export async function runAgentLoopImpl(
1417
1515
 
1418
1516
  // Shared injection options — reused whenever we need to re-inject after reduction.
1419
1517
  const injectionOpts = {
1518
+ diskPressureContext,
1420
1519
  activeSurface,
1421
1520
  workspaceTopLevelContext: shouldInjectWorkspace
1422
1521
  ? ctx.workspaceTopLevelContext
@@ -2874,6 +2973,42 @@ export async function runAgentLoopImpl(
2874
2973
  "Failed to build home-feed event for background conversation",
2875
2974
  );
2876
2975
  }
2976
+
2977
+ // Proactive artifact: fire once when the processed turn was the 4th user message.
2978
+ // Only trigger for real user-authored turns (not subagent/system messages).
2979
+ {
2980
+ const paConv = getConversation(ctx.conversationId);
2981
+ if (
2982
+ paConv &&
2983
+ paConv.conversationType === "standard" &&
2984
+ options?.isUserMessage
2985
+ ) {
2986
+ void (async () => {
2987
+ try {
2988
+ if (hasProactiveArtifactCompleted()) return;
2989
+ const userMsg = getMessageById(
2990
+ userMessageId,
2991
+ ctx.conversationId,
2992
+ );
2993
+ if (!userMsg) return;
2994
+ if (!tryClaimProactiveArtifactTrigger(userMsg.createdAt))
2995
+ return;
2996
+ await runProactiveArtifactJob({
2997
+ conversationId: ctx.conversationId,
2998
+ userMessageCutoff: userMsg.createdAt,
2999
+ assistantMessageId: state.lastAssistantMessageId,
3000
+ suppressAppBuild: state.appBuildToolUsedThisRun,
3001
+ broadcastMessage,
3002
+ });
3003
+ } catch (err) {
3004
+ log.warn(
3005
+ { err, conversationId: ctx.conversationId },
3006
+ "Proactive artifact trigger failed",
3007
+ );
3008
+ }
3009
+ })();
3010
+ }
3011
+ }
2877
3012
  }
2878
3013
  }
2879
3014
 
@@ -2937,6 +3072,7 @@ export async function runAgentLoopImpl(
2937
3072
  conversationId: ctx.conversationId,
2938
3073
  code: classified.code,
2939
3074
  message: classified.userMessage,
3075
+ errorCategory: classified.errorCategory,
2940
3076
  });
2941
3077
  onEvent(buildConversationErrorMessage(ctx.conversationId, classified));
2942
3078
  }
@@ -2988,6 +3124,7 @@ export async function runAgentLoopImpl(
2988
3124
  ctx.currentRequestId = undefined;
2989
3125
  ctx.currentActiveSurfaceId = undefined;
2990
3126
  ctx.allowedToolNames = undefined;
3127
+ ctx.diskPressureCleanupModeActive = false;
2991
3128
  ctx.preactivatedSkillIds = undefined;
2992
3129
  ctx.currentTurnOverrideProfile = undefined;
2993
3130
  ctx.slackRuntimeContextNotice = undefined;