@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
@@ -20,6 +20,7 @@ const conversationDiskViewRealSnapshot = {
20
20
  "../memory/conversation-disk-view.js",
21
21
  ) as Record<string, unknown>),
22
22
  };
23
+ let mockUiConfig: { userTimezone?: string; detectedTimezone?: string } = {};
23
24
 
24
25
  // ── Module mocks (must precede imports of the module under test) ─────
25
26
 
@@ -60,7 +61,7 @@ mock.module("../config/loader.js", () => ({
60
61
  },
61
62
  rateLimit: { maxRequestsPerMinute: 0 },
62
63
  workspaceGit: { turnCommitMaxWaitMs: 10 },
63
- ui: {},
64
+ ui: mockUiConfig,
64
65
  }),
65
66
  loadRawConfig: () => ({}),
66
67
  saveRawConfig: () => {},
@@ -125,6 +126,34 @@ mock.module("../daemon/context-overflow-policy.js", () => ({
125
126
  resolveOverflowAction: () => mockOverflowAction,
126
127
  }));
127
128
 
129
+ const mockDiskPressureStatus = {
130
+ enabled: true,
131
+ state: "critical",
132
+ locked: true,
133
+ acknowledged: true,
134
+ overrideActive: false,
135
+ effectivelyLocked: true,
136
+ lockId: "disk-pressure-test",
137
+ usagePercent: 98,
138
+ thresholdPercent: 95,
139
+ path: "/workspace",
140
+ lastCheckedAt: "2026-05-05T00:00:00.000Z",
141
+ blockedCapabilities: ["agent-turns", "background-work", "remote-ingress"],
142
+ error: null,
143
+ };
144
+ let mockDiskPressureDecision: Record<string, unknown> = {
145
+ action: "allow-normal",
146
+ };
147
+ const classifyDiskPressureTurnPolicyMock = mock(
148
+ (_status: unknown, _metadata: unknown) => mockDiskPressureDecision,
149
+ );
150
+ mock.module("../daemon/disk-pressure-guard.js", () => ({
151
+ getDiskPressureStatus: () => mockDiskPressureStatus,
152
+ }));
153
+ mock.module("../daemon/disk-pressure-policy.js", () => ({
154
+ classifyDiskPressureTurnPolicy: classifyDiskPressureTurnPolicyMock,
155
+ }));
156
+
128
157
  const updateMessageMetadataMock = mock(
129
158
  (_id: string, _updates: Record<string, unknown>) => {},
130
159
  );
@@ -144,6 +173,7 @@ let mockConversationRow: Record<string, unknown> = {
144
173
  totalEstimatedCost: 0,
145
174
  title: null,
146
175
  };
176
+ let mockMessageById: Record<string, unknown> | null = null;
147
177
  mock.module("../memory/conversation-crud.js", () => ({
148
178
  setConversationOriginChannelIfUnset: () => {},
149
179
  updateConversationUsage: () => {},
@@ -164,7 +194,7 @@ mock.module("../memory/conversation-crud.js", () => ({
164
194
  updateConversationSlackContextWatermarkMock,
165
195
  updateConversationTitle: () => {},
166
196
  getConversationOriginChannel: () => null,
167
- getMessageById: () => null,
197
+ getMessageById: () => mockMessageById,
168
198
  getLastUserTimestampBefore: () => 0,
169
199
  }));
170
200
 
@@ -237,6 +267,10 @@ let mockInjectionBlocks: {
237
267
  pkbSystemReminder?: string;
238
268
  unifiedTurnContext?: string;
239
269
  } = {};
270
+ const buildUnifiedTurnContextBlockMock = mock(
271
+ (options: Record<string, unknown>) =>
272
+ `<turn_context>\ncurrent_time: ${String(options.timestamp)}\n</turn_context>`,
273
+ );
240
274
  const applyRuntimeInjectionsMock = mock(
241
275
  async (msgs: Message[], _options?: unknown) => ({
242
276
  messages: msgs,
@@ -278,6 +312,7 @@ const getSlackCompactionWatermarkForPrefixMock = mock(
278
312
  );
279
313
  mock.module("../daemon/conversation-runtime-assembly.js", () => ({
280
314
  applyRuntimeInjections: applyRuntimeInjectionsMock,
315
+ buildUnifiedTurnContextBlock: buildUnifiedTurnContextBlockMock,
281
316
  stripInjectionsForCompaction: (msgs: Message[]) => msgs,
282
317
  findLastInjectedNowContent: () => null,
283
318
  readNowScratchpad: () => null,
@@ -298,8 +333,40 @@ mock.module("../daemon/conversation-runtime-assembly.js", () => ({
298
333
  assembleSlackActiveThreadFocusBlock: () => null,
299
334
  }));
300
335
 
336
+ const resolveTurnTimezoneContextMock = mock(
337
+ (options: {
338
+ configuredUserTimeZone?: string | null;
339
+ clientTimezone?: string | null;
340
+ detectedTimezone?: string | null;
341
+ hostTimeZone?: string | null;
342
+ }) => ({
343
+ configuredUserTimezone: options.configuredUserTimeZone ?? null,
344
+ clientTimezone: options.clientTimezone ?? null,
345
+ detectedTimezone: options.detectedTimezone ?? null,
346
+ hostTimezone: options.hostTimeZone ?? "UTC",
347
+ effectiveTimezone:
348
+ options.configuredUserTimeZone ??
349
+ options.clientTimezone ??
350
+ options.detectedTimezone ??
351
+ options.hostTimeZone ??
352
+ "UTC",
353
+ source: options.configuredUserTimeZone
354
+ ? "configuredUserTimezone"
355
+ : options.clientTimezone
356
+ ? "clientTimezone"
357
+ : options.detectedTimezone
358
+ ? "detectedTimezone"
359
+ : options.hostTimeZone
360
+ ? "hostTimezone"
361
+ : "utcFallback",
362
+ }),
363
+ );
364
+ const formatTurnTimestampMock = mock(
365
+ (_options?: unknown) => "2026-01-01 (Thursday) 00:00:00 +00:00 (UTC)",
366
+ );
301
367
  mock.module("../daemon/date-context.js", () => ({
302
- formatTurnTimestamp: () => "2026-01-01 (Thursday) 00:00:00 +00:00 (UTC)",
368
+ formatTurnTimestamp: formatTurnTimestampMock,
369
+ resolveTurnTimezoneContext: resolveTurnTimezoneContextMock,
303
370
  }));
304
371
 
305
372
  mock.module("../daemon/history-repair.js", () => ({
@@ -416,6 +483,17 @@ mock.module("../memory/llm-request-log-store.js", () => ({
416
483
  backfillMessageIdOnLogs: () => {},
417
484
  }));
418
485
 
486
+ let mockHasProactiveArtifactCompleted = true;
487
+ let mockTryClaimProactiveArtifactTrigger = false;
488
+ const runProactiveArtifactJobMock = mock(
489
+ async (_params: Record<string, unknown>) => {},
490
+ );
491
+ mock.module("../proactive-artifact/index.js", () => ({
492
+ hasProactiveArtifactCompleted: () => mockHasProactiveArtifactCompleted,
493
+ tryClaimProactiveArtifactTrigger: () => mockTryClaimProactiveArtifactTrigger,
494
+ runProactiveArtifactJob: runProactiveArtifactJobMock,
495
+ }));
496
+
419
497
  // ── Imports (after mocks) ────────────────────────────────────────────
420
498
 
421
499
  import {
@@ -428,7 +506,7 @@ import {
428
506
 
429
507
  type AgentLoopRun = (
430
508
  messages: Message[],
431
- onEvent: (event: AgentEvent) => void,
509
+ onEvent: (event: AgentEvent) => void | Promise<void>,
432
510
  signal?: AbortSignal,
433
511
  requestId?: string,
434
512
  onCheckpoint?: (
@@ -569,9 +647,12 @@ function makeCtx(
569
647
  // ── Tests ────────────────────────────────────────────────────────────
570
648
 
571
649
  beforeEach(() => {
650
+ mockUiConfig = {};
572
651
  mockEstimateTokens = 1000;
573
652
  mockReducerStepFn = null;
574
653
  mockOverflowAction = "fail_gracefully";
654
+ mockDiskPressureDecision = { action: "allow-normal" };
655
+ classifyDiskPressureTurnPolicyMock.mockClear();
575
656
  mockInjectionBlocks = {};
576
657
  recordUsageMock.mockClear();
577
658
  recordRequestLogMock.mockClear();
@@ -591,11 +672,18 @@ beforeEach(() => {
591
672
  totalEstimatedCost: 0,
592
673
  title: null,
593
674
  };
675
+ mockMessageById = null;
676
+ mockHasProactiveArtifactCompleted = true;
677
+ mockTryClaimProactiveArtifactTrigger = false;
678
+ runProactiveArtifactJobMock.mockClear();
594
679
  clearStrippedInjectionMetadataForConversationMock.mockClear();
595
680
  clearStrippedInjectionMetadataForConversationMock.mockImplementation(
596
681
  () => {},
597
682
  );
598
683
  applyRuntimeInjectionsMock.mockClear();
684
+ buildUnifiedTurnContextBlockMock.mockClear();
685
+ resolveTurnTimezoneContextMock.mockClear();
686
+ formatTurnTimestampMock.mockClear();
599
687
  mockSlackChronologicalContext = null;
600
688
  loadSlackChronologicalContextMock.mockClear();
601
689
  getSlackCompactionWatermarkForPrefixMock.mockClear();
@@ -608,6 +696,56 @@ beforeEach(() => {
608
696
  });
609
697
 
610
698
  describe("session-agent-loop", () => {
699
+ describe("timezone turn context", () => {
700
+ test("passes ctx.clientTimezone and ui.detectedTimezone into timezone resolution", async () => {
701
+ mockUiConfig = {
702
+ userTimezone: "America/New_York",
703
+ detectedTimezone: "America/Chicago",
704
+ };
705
+ const ctx = makeCtx({ clientTimezone: "America/Los_Angeles" });
706
+
707
+ await runAgentLoopImpl(ctx, "hello", "msg-1", () => {});
708
+
709
+ expect(resolveTurnTimezoneContextMock).toHaveBeenCalled();
710
+ const timezoneOptions = resolveTurnTimezoneContextMock.mock.calls[0]?.[0];
711
+ expect(timezoneOptions).toMatchObject({
712
+ configuredUserTimeZone: "America/New_York",
713
+ clientTimezone: "America/Los_Angeles",
714
+ detectedTimezone: "America/Chicago",
715
+ });
716
+ });
717
+
718
+ test("passes resolved canonical timezones into unified turn context", async () => {
719
+ mockUiConfig = {
720
+ userTimezone: "US/Eastern",
721
+ detectedTimezone: "US/Central",
722
+ };
723
+ resolveTurnTimezoneContextMock.mockImplementationOnce(() => ({
724
+ configuredUserTimezone: "America/New_York",
725
+ clientTimezone: "America/Los_Angeles",
726
+ detectedTimezone: "America/Chicago",
727
+ hostTimezone: "America/Denver",
728
+ effectiveTimezone: "America/New_York",
729
+ source: "configuredUserTimezone",
730
+ }));
731
+ const ctx = makeCtx({ clientTimezone: "US/Pacific" });
732
+
733
+ await runAgentLoopImpl(ctx, "hello", "msg-1", () => {});
734
+
735
+ expect(formatTurnTimestampMock).toHaveBeenCalledWith({
736
+ timeZone: "America/New_York",
737
+ });
738
+ expect(buildUnifiedTurnContextBlockMock).toHaveBeenCalled();
739
+ const turnContextOptions =
740
+ buildUnifiedTurnContextBlockMock.mock.calls[0]?.[0];
741
+ expect(turnContextOptions).toMatchObject({
742
+ configuredUserTimezone: "America/New_York",
743
+ clientTimezone: "America/Los_Angeles",
744
+ detectedTimezone: "America/Chicago",
745
+ });
746
+ });
747
+ });
748
+
611
749
  describe("pre-flight checks", () => {
612
750
  test("throws if called without an abortController", async () => {
613
751
  const ctx = makeCtx();
@@ -618,6 +756,304 @@ describe("session-agent-loop", () => {
618
756
  });
619
757
  });
620
758
 
759
+ describe("proactive artifact trigger", () => {
760
+ test("suppresses proactive app build when the foreground turn used app tools", async () => {
761
+ mockConversationRow = {
762
+ ...mockConversationRow,
763
+ id: "test-conv",
764
+ conversationType: "standard",
765
+ };
766
+ mockMessageById = {
767
+ id: "user-msg-1",
768
+ conversationId: "test-conv",
769
+ createdAt: 1000,
770
+ };
771
+ mockHasProactiveArtifactCompleted = false;
772
+ mockTryClaimProactiveArtifactTrigger = true;
773
+
774
+ const agentLoopRun: AgentLoopRun = async (
775
+ messages,
776
+ onEvent,
777
+ _signal,
778
+ _requestId,
779
+ onCheckpoint,
780
+ ) => {
781
+ await onEvent({
782
+ type: "message_complete",
783
+ message: {
784
+ role: "assistant",
785
+ content: [{ type: "text", text: "I'll build that app." }],
786
+ },
787
+ });
788
+ await onEvent({
789
+ type: "tool_use",
790
+ id: "tool-1",
791
+ name: "app_create",
792
+ input: { name: "Flow" },
793
+ });
794
+ await onEvent({
795
+ type: "tool_result",
796
+ toolUseId: "tool-1",
797
+ content: "{}",
798
+ isError: false,
799
+ });
800
+ await onCheckpoint?.({
801
+ turnIndex: 0,
802
+ toolCount: 1,
803
+ hasToolUse: true,
804
+ history: messages,
805
+ });
806
+ await onEvent({
807
+ type: "message_complete",
808
+ message: {
809
+ role: "assistant",
810
+ content: [{ type: "text", text: "Done." }],
811
+ },
812
+ });
813
+ return [
814
+ ...messages,
815
+ {
816
+ role: "assistant" as const,
817
+ content: [{ type: "text" as const, text: "Done." }],
818
+ },
819
+ ];
820
+ };
821
+
822
+ const ctx = makeCtx({
823
+ conversationId: "test-conv",
824
+ agentLoopRun,
825
+ });
826
+ await runAgentLoopImpl(
827
+ ctx,
828
+ "build a kanban app",
829
+ "user-msg-1",
830
+ () => {},
831
+ {
832
+ isUserMessage: true,
833
+ },
834
+ );
835
+ await new Promise((resolve) => setTimeout(resolve, 0));
836
+
837
+ expect(runProactiveArtifactJobMock).toHaveBeenCalledTimes(1);
838
+ expect(runProactiveArtifactJobMock.mock.calls[0]?.[0]).toMatchObject({
839
+ conversationId: "test-conv",
840
+ suppressAppBuild: true,
841
+ });
842
+ });
843
+ });
844
+
845
+ describe("disk pressure injection context", () => {
846
+ test("passes cleanup context into runtime injections for cleanup-mode turns", async () => {
847
+ mockDiskPressureDecision = {
848
+ action: "allow-cleanup-mode",
849
+ reason: "guardian",
850
+ };
851
+ mockConversationRow = {
852
+ ...mockConversationRow,
853
+ conversationType: "standard",
854
+ source: "user",
855
+ };
856
+ const ctx = makeCtx({
857
+ channelCapabilities: {
858
+ channel: "telegram",
859
+ dashboardCapable: false,
860
+ supportsDynamicUi: false,
861
+ supportsVoiceInput: false,
862
+ chatType: "private",
863
+ },
864
+ trustContext: {
865
+ sourceChannel: "telegram",
866
+ trustClass: "guardian",
867
+ } as AgentLoopConversationContext["trustContext"],
868
+ });
869
+
870
+ await runAgentLoopImpl(ctx, "free up space", "msg-1", () => {});
871
+
872
+ expect(classifyDiskPressureTurnPolicyMock).toHaveBeenCalledWith(
873
+ mockDiskPressureStatus,
874
+ expect.objectContaining({
875
+ callSite: "mainAgent",
876
+ conversationSource: "user",
877
+ conversationType: "standard",
878
+ isInteractive: true,
879
+ sourceChannel: "telegram",
880
+ sourceInterface: "web",
881
+ trustContext: {
882
+ sourceChannel: "telegram",
883
+ trustClass: "guardian",
884
+ },
885
+ }),
886
+ );
887
+ const firstInjectionOptions = applyRuntimeInjectionsMock.mock
888
+ .calls[0]![1] as {
889
+ diskPressureContext?: { cleanupModeActive: boolean } | null;
890
+ };
891
+ expect(firstInjectionOptions.diskPressureContext).toEqual({
892
+ cleanupModeActive: true,
893
+ });
894
+ });
895
+
896
+ test("passes cleanup context into runtime injections for local-owner turns", async () => {
897
+ mockDiskPressureDecision = {
898
+ action: "allow-cleanup-mode",
899
+ reason: "local-owner",
900
+ };
901
+ const ctx = makeCtx();
902
+
903
+ await runAgentLoopImpl(ctx, "free up space", "msg-1", () => {});
904
+
905
+ expect(classifyDiskPressureTurnPolicyMock).toHaveBeenCalledWith(
906
+ mockDiskPressureStatus,
907
+ expect.objectContaining({
908
+ sourceChannel: "vellum",
909
+ sourceInterface: "web",
910
+ trustContext: null,
911
+ }),
912
+ );
913
+ const firstInjectionOptions = applyRuntimeInjectionsMock.mock
914
+ .calls[0]![1] as {
915
+ diskPressureContext?: { cleanupModeActive: boolean } | null;
916
+ };
917
+ expect(firstInjectionOptions.diskPressureContext).toEqual({
918
+ cleanupModeActive: true,
919
+ });
920
+ });
921
+
922
+ test("keeps cleanup context on overflow recovery reinjection", async () => {
923
+ mockDiskPressureDecision = {
924
+ action: "allow-cleanup-mode",
925
+ reason: "guardian",
926
+ };
927
+ mockEstimateTokens = 96000;
928
+ mockReducerStepFn = (msgs: Message[]) => ({
929
+ messages: msgs,
930
+ tier: "forced_compaction",
931
+ state: {
932
+ appliedTiers: ["forced_compaction"],
933
+ injectionMode: "full",
934
+ exhausted: true,
935
+ },
936
+ estimatedTokens: 50000,
937
+ });
938
+ const ctx = makeCtx({
939
+ trustContext: {
940
+ sourceChannel: "telegram",
941
+ trustClass: "guardian",
942
+ } as AgentLoopConversationContext["trustContext"],
943
+ });
944
+
945
+ await runAgentLoopImpl(ctx, "free up space", "msg-1", () => {});
946
+
947
+ expect(applyRuntimeInjectionsMock.mock.calls.length).toBeGreaterThan(1);
948
+ for (const call of applyRuntimeInjectionsMock.mock.calls) {
949
+ const options = call[1] as {
950
+ diskPressureContext?: { cleanupModeActive: boolean } | null;
951
+ };
952
+ expect(options.diskPressureContext).toEqual({
953
+ cleanupModeActive: true,
954
+ });
955
+ }
956
+ });
957
+
958
+ test("blocks policy-denied turns before runtime injection or model execution", async () => {
959
+ mockDiskPressureDecision = {
960
+ action: "block",
961
+ reason: "trusted-contact",
962
+ };
963
+ const events: ServerMessage[] = [];
964
+ const agentLoopRun = mock(async (_messages: Message[]) => {
965
+ throw new Error("agent loop should not run");
966
+ });
967
+ const activityStates: unknown[][] = [];
968
+ const traceEvents: unknown[][] = [];
969
+ const ctx = makeCtx({
970
+ emitActivityState: (...args: unknown[]) => {
971
+ activityStates.push(args);
972
+ },
973
+ traceEmitter: {
974
+ emit: (...args: unknown[]) => {
975
+ traceEvents.push(args);
976
+ },
977
+ } as unknown as AgentLoopConversationContext["traceEmitter"],
978
+ });
979
+ ctx.agentLoop.run = agentLoopRun as AgentLoopRun;
980
+
981
+ await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
982
+
983
+ expect(agentLoopRun).not.toHaveBeenCalled();
984
+ expect(applyRuntimeInjectionsMock).not.toHaveBeenCalled();
985
+ expect(activityStates).toContainEqual([
986
+ "idle",
987
+ "error_terminal",
988
+ "global",
989
+ "test-req",
990
+ ]);
991
+ expect(traceEvents[0]).toEqual([
992
+ "request_error",
993
+ expect.stringContaining("Storage is critically low"),
994
+ expect.objectContaining({
995
+ requestId: "test-req",
996
+ status: "error",
997
+ attributes: expect.objectContaining({
998
+ errorCategory: "disk_pressure",
999
+ errorCode: "DISK_SPACE_CRITICAL",
1000
+ diskPressureReason: "trusted-contact",
1001
+ }),
1002
+ }),
1003
+ ]);
1004
+ expect(events.find((event) => event.type === "error")).toMatchObject({
1005
+ type: "error",
1006
+ conversationId: "test-conv",
1007
+ requestId: "test-req",
1008
+ code: "DISK_SPACE_CRITICAL",
1009
+ category: "disk_pressure",
1010
+ message: expect.stringContaining("remote messages are ignored"),
1011
+ });
1012
+ expect(
1013
+ events.find((event) => event.type === "conversation_error"),
1014
+ ).toMatchObject({
1015
+ type: "conversation_error",
1016
+ conversationId: "test-conv",
1017
+ code: "DISK_SPACE_CRITICAL",
1018
+ retryable: true,
1019
+ errorCategory: "disk_pressure",
1020
+ userMessage: expect.stringContaining("remote messages are ignored"),
1021
+ });
1022
+ });
1023
+
1024
+ test("blocked background turns clear processing state and drain the queue", async () => {
1025
+ mockDiskPressureDecision = {
1026
+ action: "block",
1027
+ reason: "background",
1028
+ };
1029
+ const drainQueue = mock(async (_reason: unknown) => {});
1030
+ const activityStates: unknown[][] = [];
1031
+ const ctx = makeCtx({
1032
+ drainQueue,
1033
+ emitActivityState: (...args: unknown[]) => {
1034
+ activityStates.push(args);
1035
+ },
1036
+ });
1037
+
1038
+ await runAgentLoopImpl(ctx, "background task", "msg-1", () => {}, {
1039
+ callSite: "memoryConsolidation",
1040
+ isInteractive: false,
1041
+ });
1042
+
1043
+ expect(applyRuntimeInjectionsMock).not.toHaveBeenCalled();
1044
+ expect(ctx.processing).toBe(false);
1045
+ expect(ctx.abortController).toBeNull();
1046
+ expect(ctx.currentRequestId).toBeUndefined();
1047
+ expect(drainQueue).toHaveBeenCalledWith("loop_complete");
1048
+ expect(activityStates).toContainEqual([
1049
+ "idle",
1050
+ "error_terminal",
1051
+ "global",
1052
+ "test-req",
1053
+ ]);
1054
+ });
1055
+ });
1056
+
621
1057
  describe("tool execution errors via agent loop", () => {
622
1058
  test("error events from agent loop are classified and emitted", async () => {
623
1059
  const events: ServerMessage[] = [];
@@ -2095,16 +2531,29 @@ describe("session-agent-loop", () => {
2095
2531
  });
2096
2532
 
2097
2533
  test("clears state even when agent loop throws", async () => {
2534
+ const events: ServerMessage[] = [];
2098
2535
  const ctx = makeCtx({
2099
2536
  agentLoopRun: async () => {
2100
2537
  throw new Error("unexpected crash");
2101
2538
  },
2102
2539
  });
2103
2540
 
2104
- await runAgentLoopImpl(ctx, "hi", "msg-1", () => {});
2541
+ await runAgentLoopImpl(ctx, "hi", "msg-1", (msg) => events.push(msg));
2105
2542
 
2106
2543
  expect(ctx.processing).toBe(false);
2107
2544
  expect(ctx.abortController).toBeNull();
2545
+ expect(events.find((event) => event.type === "error")).toMatchObject({
2546
+ type: "error",
2547
+ code: "CONVERSATION_PROCESSING_FAILED",
2548
+ errorCategory: "processing_failed",
2549
+ });
2550
+ expect(
2551
+ events.find((event) => event.type === "conversation_error"),
2552
+ ).toMatchObject({
2553
+ type: "conversation_error",
2554
+ code: "CONVERSATION_PROCESSING_FAILED",
2555
+ errorCategory: "processing_failed",
2556
+ });
2108
2557
  });
2109
2558
 
2110
2559
  test("drains queue after completion", async () => {