@vellumai/assistant 0.7.2 → 0.8.0

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 (424) hide show
  1. package/ARCHITECTURE.md +45 -29
  2. package/Dockerfile +1 -0
  3. package/__tests__/permissions/gateway-threshold-reader.test.ts +236 -9
  4. package/bun.lock +3 -0
  5. package/docs/architecture/memory.md +5 -2
  6. package/knip.json +1 -0
  7. package/node_modules/@vellumai/gateway-client/src/ipc-client.ts +13 -4
  8. package/node_modules/@vellumai/ipc-server-utils/bun.lock +24 -0
  9. package/node_modules/@vellumai/ipc-server-utils/package.json +18 -0
  10. package/node_modules/@vellumai/ipc-server-utils/src/index.ts +6 -0
  11. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +430 -0
  12. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +221 -0
  13. package/node_modules/@vellumai/ipc-server-utils/tsconfig.json +20 -0
  14. package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +0 -9
  15. package/node_modules/@vellumai/slack-text/src/index.test.ts +18 -35
  16. package/node_modules/@vellumai/slack-text/src/index.ts +2 -48
  17. package/openapi.yaml +470 -25
  18. package/package.json +3 -1
  19. package/src/__tests__/annotate-risk-options.test.ts +291 -0
  20. package/src/__tests__/app-control-flow.test.ts +21 -11
  21. package/src/__tests__/approval-cascade.test.ts +8 -16
  22. package/src/__tests__/approval-routes-http.test.ts +6 -0
  23. package/src/__tests__/assistant-event-hub.test.ts +48 -0
  24. package/src/__tests__/assistant-event.test.ts +0 -10
  25. package/src/__tests__/assistant-events-sse-hardening.test.ts +2 -7
  26. package/src/__tests__/assistant-feature-flags-integration.test.ts +18 -0
  27. package/src/__tests__/auto-analysis-end-to-end.test.ts +48 -0
  28. package/src/__tests__/background-workers-disk-pressure.test.ts +268 -0
  29. package/src/__tests__/call-constants.test.ts +10 -1
  30. package/src/__tests__/call-controller.test.ts +127 -0
  31. package/src/__tests__/call-conversation-messages.test.ts +8 -2
  32. package/src/__tests__/channel-inbound-disk-pressure.test.ts +537 -0
  33. package/src/__tests__/channel-readiness-service.test.ts +4 -2
  34. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +58 -28
  35. package/src/__tests__/config-loader-backfill.test.ts +379 -0
  36. package/src/__tests__/config-loader-platform-defaults.test.ts +284 -1
  37. package/src/__tests__/config-schema.test.ts +1 -0
  38. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +18 -9
  39. package/src/__tests__/config-watcher.test.ts +140 -69
  40. package/src/__tests__/context-search-agent-runner.test.ts +61 -3
  41. package/src/__tests__/context-search-conversations-source.test.ts +0 -24
  42. package/src/__tests__/context-search-fanout.test.ts +0 -1
  43. package/src/__tests__/context-search-memory-source.test.ts +6 -33
  44. package/src/__tests__/context-search-memory-v2-source.test.ts +0 -2
  45. package/src/__tests__/context-search-pkb-source.test.ts +12 -7
  46. package/src/__tests__/context-search-workspace-source.test.ts +0 -1
  47. package/src/__tests__/conversation-abort-tool-results.test.ts +1 -0
  48. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +223 -0
  49. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -1
  50. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -1
  51. package/src/__tests__/conversation-agent-loop.test.ts +457 -8
  52. package/src/__tests__/conversation-confirmation-signals.test.ts +5 -13
  53. package/src/__tests__/conversation-error.test.ts +150 -3
  54. package/src/__tests__/conversation-init.benchmark.test.ts +1 -1
  55. package/src/__tests__/conversation-process-callsite.test.ts +38 -0
  56. package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -0
  57. package/src/__tests__/conversation-runtime-assembly.test.ts +74 -0
  58. package/src/__tests__/conversation-slash-unknown.test.ts +1 -0
  59. package/src/__tests__/conversation-speed-override.test.ts +0 -3
  60. package/src/__tests__/conversation-store.test.ts +0 -18
  61. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +170 -9
  62. package/src/__tests__/conversation-surfaces-app-control.test.ts +15 -4
  63. package/src/__tests__/conversation-surfaces-data-persist.test.ts +476 -0
  64. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +61 -5
  65. package/src/__tests__/conversation-workspace-injection.test.ts +1 -1
  66. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
  67. package/src/__tests__/credentials-cli.test.ts +7 -0
  68. package/src/__tests__/cu-unified-flow.test.ts +176 -10
  69. package/src/__tests__/date-context.test.ts +164 -2
  70. package/src/__tests__/disk-pressure-guard.test.ts +262 -0
  71. package/src/__tests__/disk-pressure-lifecycle.test.ts +168 -0
  72. package/src/__tests__/disk-pressure-policy.test.ts +241 -0
  73. package/src/__tests__/disk-pressure-routes.test.ts +379 -0
  74. package/src/__tests__/disk-pressure-tools.test.ts +277 -0
  75. package/src/__tests__/disk-usage.test.ts +150 -0
  76. package/src/__tests__/events-client-registration.test.ts +52 -0
  77. package/src/__tests__/events-dev-bypass-actor.test.ts +162 -0
  78. package/src/__tests__/file-write-tool.test.ts +4 -10
  79. package/src/__tests__/filing-service.test.ts +2 -20
  80. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +10 -26
  81. package/src/__tests__/heartbeat-disk-pressure.test.ts +183 -0
  82. package/src/__tests__/heartbeat-service.test.ts +260 -11
  83. package/src/__tests__/host-app-control-proxy.test.ts +195 -25
  84. package/src/__tests__/host-bash-proxy.test.ts +227 -34
  85. package/src/__tests__/host-bash-routes.test.ts +178 -13
  86. package/src/__tests__/host-cu-proxy.test.ts +210 -3
  87. package/src/__tests__/host-cu-routes-targeted.test.ts +141 -12
  88. package/src/__tests__/host-file-proxy-targeted.test.ts +48 -9
  89. package/src/__tests__/host-file-proxy.test.ts +268 -6
  90. package/src/__tests__/host-file-routes-targeted.test.ts +175 -17
  91. package/src/__tests__/host-transfer-proxy-targeted.test.ts +408 -59
  92. package/src/__tests__/host-transfer-routes-targeted.test.ts +232 -17
  93. package/src/__tests__/http-user-message-parity.test.ts +107 -1
  94. package/src/__tests__/injector-chain.test.ts +36 -16
  95. package/src/__tests__/injector-disk-pressure.test.ts +224 -0
  96. package/src/__tests__/injector-pkb-v2-silenced.test.ts +10 -7
  97. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +154 -67
  98. package/src/__tests__/managed-profile-guard.test.ts +18 -0
  99. package/src/__tests__/mcp-abort-signal.test.ts +130 -0
  100. package/src/__tests__/memory-admin-recall.test.ts +3 -11
  101. package/src/__tests__/memory-retrieval-pipeline.test.ts +22 -1
  102. package/src/__tests__/normalize-onboarding.test.ts +180 -0
  103. package/src/__tests__/notification-decision-fallback.test.ts +91 -0
  104. package/src/__tests__/notification-decision-strategy.test.ts +22 -0
  105. package/src/__tests__/oauth-cli.test.ts +121 -0
  106. package/src/__tests__/oauth-connect-routes.test.ts +316 -0
  107. package/src/__tests__/oauth-provider-seed-logos.test.ts +24 -2
  108. package/src/__tests__/onboarding-persona-write.test.ts +308 -0
  109. package/src/__tests__/openai-provider.test.ts +45 -8
  110. package/src/__tests__/persist-onboarding-artifacts.test.ts +44 -64
  111. package/src/__tests__/platform-callback-registration.test.ts +21 -4
  112. package/src/__tests__/platform.test.ts +2 -1
  113. package/src/__tests__/playbook-execution.test.ts +0 -43
  114. package/src/__tests__/plugin-tool-contribution.test.ts +47 -0
  115. package/src/__tests__/prechat-onboarding-contract.test.ts +214 -27
  116. package/src/__tests__/provider-tool-name.test.ts +23 -0
  117. package/src/__tests__/relay-server.test.ts +60 -5
  118. package/src/__tests__/runtime-events-sse.test.ts +4 -8
  119. package/src/__tests__/scheduler-disk-pressure.test.ts +148 -0
  120. package/src/__tests__/secret-ingress-http.test.ts +0 -1
  121. package/src/__tests__/secret-prompt-log-hygiene.test.ts +7 -5
  122. package/src/__tests__/secret-prompter-channel-fallback.test.ts +7 -5
  123. package/src/__tests__/secret-response-routing.test.ts +7 -5
  124. package/src/__tests__/server-history-render.test.ts +82 -0
  125. package/src/__tests__/skill-include-graph.test.ts +31 -0
  126. package/src/__tests__/skill-load-tool.test.ts +44 -16
  127. package/src/__tests__/skills.test.ts +39 -0
  128. package/src/__tests__/suggestion-routes.test.ts +46 -0
  129. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -42
  130. package/src/__tests__/tool-executor.test.ts +155 -0
  131. package/src/__tests__/twilio-validation.test.ts +2 -2
  132. package/src/__tests__/voice-session-bridge.test.ts +3 -0
  133. package/src/__tests__/workspace-migration-065-bump-stale-heartbeat-interval.test.ts +122 -0
  134. package/src/__tests__/workspace-migration-066-seed-heartbeat-callsite-cost-default.test.ts +285 -0
  135. package/src/__tests__/workspace-migration-068-release-notes-local-timezone.test.ts +90 -0
  136. package/src/__tests__/workspace-migration-069-seed-onboarding-threads.test.ts +120 -0
  137. package/src/__tests__/workspace-migration-071-remove-safe-storage-release-note.test.ts +206 -0
  138. package/src/__tests__/workspace-migration-safe-storage-limits-release.test.ts +78 -0
  139. package/src/agent/loop.ts +11 -0
  140. package/src/approvals/guardian-request-resolvers.ts +3 -32
  141. package/src/backup/snapshot-lock.ts +2 -27
  142. package/src/bundler/compiler-tools.ts +3 -2
  143. package/src/calls/call-constants.ts +5 -8
  144. package/src/calls/call-controller.ts +130 -67
  145. package/src/calls/call-conversation-messages.ts +46 -10
  146. package/src/calls/relay-server.ts +7 -1
  147. package/src/calls/voice-session-bridge.ts +1 -1
  148. package/src/cli/commands/__tests__/webhooks.test.ts +0 -4
  149. package/src/cli/commands/bash.ts +35 -108
  150. package/src/cli/commands/contacts.ts +64 -25
  151. package/src/cli/commands/credentials.ts +56 -0
  152. package/src/cli/commands/memory-v2.ts +11 -10
  153. package/src/cli/commands/oauth/__tests__/connect.test.ts +401 -219
  154. package/src/cli/commands/oauth/connect.ts +124 -40
  155. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -3
  156. package/src/cli/commands/platform/__tests__/connect.test.ts +7 -1
  157. package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -1
  158. package/src/cli/commands/platform/__tests__/status.test.ts +103 -6
  159. package/src/cli/commands/platform/index.ts +16 -7
  160. package/src/cli/commands/status.ts +57 -0
  161. package/src/cli/program.ts +4 -2
  162. package/src/config/assistant-feature-flags.ts +13 -3
  163. package/src/config/bundled-skills/app-builder/SKILL.md +1 -3
  164. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +4 -3
  165. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +13 -7
  166. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +2 -2
  167. package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +2 -2
  168. package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +2 -2
  169. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +2 -2
  170. package/src/config/env.ts +0 -8
  171. package/src/config/feature-flag-registry.json +13 -5
  172. package/src/config/loader.ts +199 -27
  173. package/src/config/schemas/__tests__/memory-v2.test.ts +10 -5
  174. package/src/config/schemas/call-site-catalog.ts +14 -0
  175. package/src/config/schemas/channels.ts +0 -5
  176. package/src/config/schemas/heartbeat.ts +1 -1
  177. package/src/config/schemas/llm.ts +2 -0
  178. package/src/config/schemas/memory-lifecycle.ts +13 -0
  179. package/src/config/schemas/memory-v2.ts +76 -12
  180. package/src/config/schemas/platform.ts +43 -3
  181. package/src/config/schemas/services.ts +28 -0
  182. package/src/config/seed-inference-profiles.ts +230 -33
  183. package/src/contacts/contact-store.ts +0 -25
  184. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +32 -0
  185. package/src/daemon/__tests__/conversation-tool-setup.test.ts +86 -25
  186. package/src/daemon/assistant-attachments.ts +4 -4
  187. package/src/daemon/config-watcher.ts +85 -57
  188. package/src/daemon/conversation-agent-loop-handlers.ts +38 -0
  189. package/src/daemon/conversation-agent-loop.ts +183 -43
  190. package/src/daemon/conversation-error.ts +87 -15
  191. package/src/daemon/conversation-lifecycle.ts +22 -10
  192. package/src/daemon/conversation-process.ts +8 -0
  193. package/src/daemon/conversation-runtime-assembly.ts +26 -0
  194. package/src/daemon/conversation-store.ts +2 -2
  195. package/src/daemon/conversation-surfaces.ts +211 -29
  196. package/src/daemon/conversation-tool-setup.ts +66 -19
  197. package/src/daemon/conversation.ts +18 -23
  198. package/src/daemon/date-context.ts +71 -22
  199. package/src/daemon/disk-pressure-background-gate.ts +73 -0
  200. package/src/daemon/disk-pressure-guard.ts +343 -0
  201. package/src/daemon/disk-pressure-policy.ts +163 -0
  202. package/src/daemon/handlers/shared.ts +26 -1
  203. package/src/daemon/handlers/skills.ts +3 -4
  204. package/src/daemon/host-app-control-proxy.ts +137 -41
  205. package/src/daemon/host-bash-proxy.ts +47 -22
  206. package/src/daemon/host-browser-proxy.ts +1 -1
  207. package/src/daemon/host-cu-proxy.ts +50 -4
  208. package/src/daemon/host-file-proxy.ts +44 -8
  209. package/src/daemon/host-transfer-proxy.ts +97 -6
  210. package/src/daemon/lifecycle.ts +167 -101
  211. package/src/daemon/meet-host-supervisor.ts +4 -4
  212. package/src/daemon/meet-manifest-loader.ts +0 -1
  213. package/src/daemon/memory-v2-startup.ts +66 -15
  214. package/src/daemon/message-protocol.ts +3 -0
  215. package/src/daemon/message-types/conversations.ts +4 -0
  216. package/src/daemon/message-types/disk-pressure.ts +9 -0
  217. package/src/daemon/message-types/messages.ts +22 -1
  218. package/src/daemon/profiler-run-store.ts +5 -5
  219. package/src/daemon/tool-setup-types.ts +2 -2
  220. package/src/documents/document-store.ts +119 -0
  221. package/src/filing/filing-service.ts +29 -5
  222. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +9 -16
  223. package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +36 -0
  224. package/src/heartbeat/heartbeat-run-store.ts +13 -0
  225. package/src/heartbeat/heartbeat-service.ts +205 -31
  226. package/src/home/feed-scheduler.ts +18 -0
  227. package/src/inbound/platform-callback-registration.ts +8 -15
  228. package/src/ipc/__tests__/clients-list-ipc.test.ts +169 -0
  229. package/src/ipc/assistant-server.ts +149 -38
  230. package/src/ipc/gateway-client.ts +37 -3
  231. package/src/ipc/skill-server.ts +99 -42
  232. package/src/live-voice/live-voice-archive.ts +4 -4
  233. package/src/live-voice/protocol.ts +5 -7
  234. package/src/media/image-service.ts +1 -7
  235. package/src/memory/__tests__/fixtures/memory-v2-activation-fixtures.ts +21 -13
  236. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +34 -51
  237. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +0 -6
  238. package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +272 -0
  239. package/src/memory/admin.ts +5 -9
  240. package/src/memory/context-search/agent-runner.ts +19 -2
  241. package/src/memory/context-search/sources/conversations.ts +2 -11
  242. package/src/memory/context-search/sources/memory-v2.ts +1 -16
  243. package/src/memory/context-search/sources/memory.ts +2 -3
  244. package/src/memory/context-search/sources/pkb.ts +2 -3
  245. package/src/memory/context-search/types.ts +0 -1
  246. package/src/memory/conversation-crud.ts +4 -12
  247. package/src/memory/db-init.ts +2 -0
  248. package/src/memory/embedding-runtime-manager.ts +119 -5
  249. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +136 -82
  250. package/src/memory/graph/__tests__/handle-remember-v2.test.ts +11 -26
  251. package/src/memory/graph/conversation-graph-memory.ts +72 -61
  252. package/src/memory/graph/extraction.ts +1 -3
  253. package/src/memory/graph/graph-search.test.ts +11 -67
  254. package/src/memory/graph/graph-search.ts +4 -24
  255. package/src/memory/graph/retriever.test.ts +12 -1
  256. package/src/memory/graph/retriever.ts +10 -15
  257. package/src/memory/graph/tool-handlers.ts +3 -4
  258. package/src/memory/graph/tools.ts +4 -4
  259. package/src/memory/indexer.ts +53 -45
  260. package/src/memory/job-handlers/backfill.ts +2 -11
  261. package/src/memory/job-handlers/cleanup.ts +43 -0
  262. package/src/memory/job-handlers/embedding.ts +6 -8
  263. package/src/memory/job-handlers/summarization.ts +2 -7
  264. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +116 -0
  265. package/src/memory/jobs/embed-concept-page.ts +223 -87
  266. package/src/memory/jobs-store.ts +48 -0
  267. package/src/memory/jobs-worker.ts +85 -43
  268. package/src/memory/memory-v2-activation-log-store.ts +32 -14
  269. package/src/memory/memory-v2-concept-frequency.ts +169 -0
  270. package/src/memory/migrations/239-trace-events-created-at-index.ts +18 -0
  271. package/src/memory/migrations/index.ts +1 -0
  272. package/src/memory/pkb/pkb-search.test.ts +7 -0
  273. package/src/memory/pkb/pkb-search.ts +4 -5
  274. package/src/memory/qdrant-client.ts +3 -13
  275. package/src/memory/rerank-local.ts +374 -0
  276. package/src/memory/search/semantic.ts +10 -72
  277. package/src/memory/trace-event-store.ts +1 -17
  278. package/src/memory/v2/__tests__/activation.test.ts +346 -255
  279. package/src/memory/v2/__tests__/consolidation-job.test.ts +61 -40
  280. package/src/memory/v2/__tests__/injection.test.ts +297 -190
  281. package/src/memory/v2/__tests__/prompts-consolidation.test.ts +61 -2
  282. package/src/memory/v2/__tests__/qdrant.test.ts +326 -9
  283. package/src/memory/v2/__tests__/reranker.test.ts +338 -0
  284. package/src/memory/v2/__tests__/sim.test.ts +113 -196
  285. package/src/memory/v2/__tests__/skill-store.test.ts +71 -65
  286. package/src/memory/v2/__tests__/static-context.test.ts +77 -14
  287. package/src/memory/v2/__tests__/sweep-job.test.ts +19 -33
  288. package/src/memory/v2/activation.ts +149 -156
  289. package/src/memory/v2/consolidation-job.ts +69 -20
  290. package/src/memory/v2/injection.ts +75 -68
  291. package/src/memory/v2/page-store.ts +39 -0
  292. package/src/memory/v2/prompts/consolidation.ts +41 -1
  293. package/src/memory/v2/qdrant.ts +306 -46
  294. package/src/memory/v2/reranker.ts +177 -0
  295. package/src/memory/v2/sim.ts +77 -110
  296. package/src/memory/v2/skill-content.ts +4 -3
  297. package/src/memory/v2/skill-store.ts +82 -59
  298. package/src/memory/v2/static-context.ts +26 -8
  299. package/src/memory/v2/sweep-job.ts +5 -6
  300. package/src/memory/v2/types.ts +17 -10
  301. package/src/notifications/copy-composer.ts +47 -0
  302. package/src/notifications/decision-engine.ts +46 -0
  303. package/src/notifications/signal.ts +4 -0
  304. package/src/oauth/AGENTS.md +3 -1
  305. package/src/oauth/__tests__/oauth-connect-state.test.ts +137 -0
  306. package/src/oauth/connect-orchestrator.ts +2 -0
  307. package/src/oauth/connection-resolver.test.ts +66 -1
  308. package/src/oauth/connection-resolver.ts +55 -1
  309. package/src/oauth/oauth-connect-state.ts +77 -0
  310. package/src/oauth/seed-providers.ts +58 -1
  311. package/src/permissions/gateway-threshold-reader.ts +116 -8
  312. package/src/permissions/prompter.ts +86 -96
  313. package/src/permissions/secret-prompter.ts +31 -31
  314. package/src/plugins/defaults/injectors.ts +36 -4
  315. package/src/plugins/defaults/memory-retrieval.ts +5 -6
  316. package/src/plugins/types.ts +7 -0
  317. package/src/proactive-artifact/aux-message-injector.ts +74 -0
  318. package/src/proactive-artifact/decision.test.ts +226 -0
  319. package/src/proactive-artifact/decision.ts +165 -0
  320. package/src/proactive-artifact/index.ts +7 -0
  321. package/src/proactive-artifact/job.test.ts +914 -0
  322. package/src/proactive-artifact/job.ts +366 -0
  323. package/src/proactive-artifact/message-copy.ts +58 -0
  324. package/src/proactive-artifact/trigger-state.test.ts +277 -0
  325. package/src/proactive-artifact/trigger-state.ts +119 -0
  326. package/src/prompts/normalize-onboarding.ts +80 -0
  327. package/src/prompts/persona-resolver.ts +101 -9
  328. package/src/prompts/system-prompt.ts +21 -7
  329. package/src/prompts/templates/BOOTSTRAP.md +13 -5
  330. package/src/prompts/templates/SOUL.md +13 -28
  331. package/src/providers/__tests__/retry-callsite.test.ts +222 -1
  332. package/src/providers/model-intents.ts +7 -0
  333. package/src/providers/openrouter/client.ts +8 -0
  334. package/src/providers/retry.ts +50 -0
  335. package/src/providers/types.ts +1 -0
  336. package/src/runtime/__tests__/agent-wake.test.ts +456 -3
  337. package/src/runtime/agent-wake.ts +238 -100
  338. package/src/runtime/assistant-event-hub.ts +36 -6
  339. package/src/runtime/assistant-event.ts +0 -1
  340. package/src/runtime/auth/__tests__/route-policy.test.ts +64 -0
  341. package/src/runtime/auth/route-policy.ts +15 -1
  342. package/src/runtime/auth/same-actor.ts +216 -0
  343. package/src/runtime/channel-approvals.ts +3 -2
  344. package/src/runtime/channel-retry-sweep.ts +65 -1
  345. package/src/runtime/local-actor-identity.ts +52 -11
  346. package/src/runtime/pending-interactions.ts +27 -15
  347. package/src/runtime/routes/__tests__/client-routes.test.ts +155 -0
  348. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +0 -5
  349. package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +1 -1
  350. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +147 -0
  351. package/src/runtime/routes/approval-routes.ts +7 -3
  352. package/src/runtime/routes/client-routes.ts +20 -2
  353. package/src/runtime/routes/consolidation-routes.ts +8 -9
  354. package/src/runtime/routes/contact-routes.ts +0 -25
  355. package/src/runtime/routes/conversation-query-routes.ts +44 -1
  356. package/src/runtime/routes/conversation-routes.ts +35 -26
  357. package/src/runtime/routes/debug-bash-routes.ts +165 -0
  358. package/src/runtime/routes/disk-pressure-routes.ts +121 -0
  359. package/src/runtime/routes/document-pdf-renderer.ts +6 -2
  360. package/src/runtime/routes/documents-routes.ts +2 -75
  361. package/src/runtime/routes/events-routes.ts +41 -9
  362. package/src/runtime/routes/filing-routes.ts +2 -3
  363. package/src/runtime/routes/host-bash-routes.ts +23 -3
  364. package/src/runtime/routes/host-cu-routes.ts +33 -6
  365. package/src/runtime/routes/host-file-routes.ts +32 -6
  366. package/src/runtime/routes/host-transfer-routes.ts +79 -16
  367. package/src/runtime/routes/identity-routes.ts +7 -138
  368. package/src/runtime/routes/inbound-message-handler.ts +77 -12
  369. package/src/runtime/routes/index.ts +6 -0
  370. package/src/runtime/routes/memory-item-routes.test.ts +37 -17
  371. package/src/runtime/routes/memory-item-routes.ts +5 -6
  372. package/src/runtime/routes/memory-v2-routes.ts +136 -17
  373. package/src/runtime/routes/oauth-connect-routes.ts +153 -0
  374. package/src/runtime/verification-outbound-actions.ts +4 -4
  375. package/src/schedule/run-script.ts +37 -5
  376. package/src/schedule/scheduler.ts +20 -1
  377. package/src/security/encrypted-store.ts +2 -0
  378. package/src/security/secure-keys.ts +55 -0
  379. package/src/skills/include-graph.ts +35 -13
  380. package/src/skills/remote-skill-policy.ts +4 -10
  381. package/src/subagent/index.ts +1 -7
  382. package/src/subagent/manager.ts +1 -15
  383. package/src/tasks/task-runner.ts +0 -1
  384. package/src/tasks/task-store.ts +0 -3
  385. package/src/tools/background-tool-registry.ts +17 -3
  386. package/src/tools/document/document-tool.ts +20 -0
  387. package/src/tools/executor.ts +18 -2
  388. package/src/tools/host-filesystem/edit.test.ts +151 -0
  389. package/src/tools/host-filesystem/edit.ts +43 -1
  390. package/src/tools/host-filesystem/read.test.ts +129 -0
  391. package/src/tools/host-filesystem/read.ts +43 -1
  392. package/src/tools/host-filesystem/transfer.test.ts +127 -2
  393. package/src/tools/host-filesystem/transfer.ts +56 -11
  394. package/src/tools/host-filesystem/write.test.ts +134 -0
  395. package/src/tools/host-filesystem/write.ts +43 -1
  396. package/src/tools/host-terminal/host-shell.ts +13 -6
  397. package/src/tools/mcp/mcp-tool-factory.ts +2 -1
  398. package/src/tools/memory/register.test.ts +14 -9
  399. package/src/tools/memory/register.ts +1 -2
  400. package/src/tools/permission-checker.ts +15 -0
  401. package/src/tools/provider-tool-name.ts +28 -0
  402. package/src/tools/registry.ts +30 -9
  403. package/src/tools/skills/load.ts +24 -20
  404. package/src/tools/terminal/shell.ts +9 -1
  405. package/src/tools/tool-approval-handler.ts +31 -6
  406. package/src/tools/tool-name-aliases.ts +19 -0
  407. package/src/tools/types.ts +43 -3
  408. package/src/tts/provider-catalog.ts +3 -5
  409. package/src/util/disk-usage.ts +138 -0
  410. package/src/util/platform.ts +21 -11
  411. package/src/util/process-liveness.ts +26 -0
  412. package/src/workspace/heartbeat-service.ts +19 -0
  413. package/src/workspace/migrations/065-bump-stale-heartbeat-interval.ts +60 -0
  414. package/src/workspace/migrations/066-seed-heartbeat-callsite-cost-default.ts +146 -0
  415. package/src/workspace/migrations/067-release-notes-safe-storage-limits.ts +14 -0
  416. package/src/workspace/migrations/068-release-notes-local-timezone.ts +65 -0
  417. package/src/workspace/migrations/069-seed-onboarding-threads.ts +28 -0
  418. package/src/workspace/migrations/070-memory-v2-summary-schema-rebuild.ts +31 -0
  419. package/src/workspace/migrations/071-remove-safe-storage-release-note.ts +111 -0
  420. package/src/workspace/migrations/registry.ts +14 -0
  421. package/src/__tests__/conversation-tool-setup-memory-scope.test.ts +0 -167
  422. package/src/memory/v2/__tests__/skill-qdrant.test.ts +0 -657
  423. package/src/memory/v2/skill-qdrant.ts +0 -404
  424. package/src/signals/bash.ts +0 -198
@@ -7,8 +7,18 @@
7
7
  import { z } from "zod";
8
8
 
9
9
  import { HostFileProxy } from "../../daemon/host-file-proxy.js";
10
+ import {
11
+ enforceSameActorOrThrow,
12
+ SAME_ACTOR_FORBIDDEN_DESCRIPTION,
13
+ } from "../auth/same-actor.js";
14
+ import { resolveActorPrincipalIdForLocalGuardian } from "../local-actor-identity.js";
10
15
  import * as pendingInteractions from "../pending-interactions.js";
11
- import { BadRequestError, ConflictError, ForbiddenError, NotFoundError } from "./errors.js";
16
+ import {
17
+ BadRequestError,
18
+ ConflictError,
19
+ ForbiddenError,
20
+ NotFoundError,
21
+ } from "./errors.js";
12
22
  import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
13
23
 
14
24
  // ---------------------------------------------------------------------------
@@ -44,16 +54,33 @@ function handleHostFileResult({ body, headers }: RouteHandlerArgs) {
44
54
 
45
55
  // Validate submitting client matches the targeted client (if any).
46
56
  if (peeked.targetClientId != null) {
47
- const rawClientId = (headers as Record<string, string | undefined>)?.["x-vellum-client-id"];
48
- const submittingClientId = rawClientId?.trim() || undefined;
57
+ const headerMap = (headers as Record<string, string | undefined>) ?? {};
58
+ const submittingClientId =
59
+ headerMap["x-vellum-client-id"]?.trim() || undefined;
49
60
  if (!submittingClientId) {
50
- throw new BadRequestError("x-vellum-client-id header is missing for a targeted host file request.");
61
+ throw new BadRequestError(
62
+ "x-vellum-client-id header is missing for a targeted host file request.",
63
+ );
51
64
  }
52
65
  if (submittingClientId !== peeked.targetClientId) {
53
66
  throw new ForbiddenError(
54
67
  `Client "${submittingClientId}" is not the target for this request (expected "${peeked.targetClientId}"). The targeted client must submit the result.`,
55
68
  );
56
69
  }
70
+
71
+ // Defense-in-depth: also require the submitting actor's principal id to
72
+ // match the actor that opened the target client's SSE stream. This blocks
73
+ // cross-user submissions even if a different user somehow obtains the
74
+ // target client id.
75
+ const submittingActorPrincipalId = resolveActorPrincipalIdForLocalGuardian(
76
+ headerMap["x-vellum-actor-principal-id"]?.trim() || undefined,
77
+ );
78
+ enforceSameActorOrThrow({
79
+ sourceActorPrincipalId: submittingActorPrincipalId,
80
+ targetActorPrincipalId: peeked.targetActorPrincipalId,
81
+ targetClientId: peeked.targetClientId,
82
+ op: "host_file",
83
+ });
57
84
  }
58
85
 
59
86
  HostFileProxy.instance.resolve(requestId, {
@@ -102,8 +129,7 @@ export const ROUTES: RouteDefinition[] = [
102
129
  "x-vellum-client-id header is missing for a targeted host file request.",
103
130
  },
104
131
  "403": {
105
- description:
106
- "Submitting client does not match the targeted client for this request.",
132
+ description: SAME_ACTOR_FORBIDDEN_DESCRIPTION,
107
133
  },
108
134
  "404": {
109
135
  description: "No pending interaction found for the given requestId.",
@@ -8,8 +8,18 @@
8
8
  import { z } from "zod";
9
9
 
10
10
  import { HostTransferProxy } from "../../daemon/host-transfer-proxy.js";
11
+ import {
12
+ enforceSameActorOrThrow,
13
+ SAME_ACTOR_FORBIDDEN_DESCRIPTION,
14
+ } from "../auth/same-actor.js";
15
+ import { resolveActorPrincipalIdForLocalGuardian } from "../local-actor-identity.js";
11
16
  import * as pendingInteractions from "../pending-interactions.js";
12
- import { BadRequestError, ConflictError, ForbiddenError, NotFoundError } from "./errors.js";
17
+ import {
18
+ BadRequestError,
19
+ ConflictError,
20
+ ForbiddenError,
21
+ NotFoundError,
22
+ } from "./errors.js";
13
23
  import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
14
24
 
15
25
  /**
@@ -44,9 +54,31 @@ function handleTransferContentGet({
44
54
 
45
55
  const targetClientId = match.proxy.getTargetClientIdForTransfer(transferId);
46
56
  if (targetClientId != null) {
47
- const submittingClientId = (headers as Record<string, string>)["x-vellum-client-id"]?.trim() || undefined;
48
- if (!submittingClientId) throw new BadRequestError("x-vellum-client-id header required for targeted transfer");
49
- if (submittingClientId !== targetClientId) throw new ForbiddenError(`Client "${submittingClientId}" is not the owner of this transfer`);
57
+ const headerMap = headers as Record<string, string | undefined>;
58
+ const submittingClientId =
59
+ headerMap["x-vellum-client-id"]?.trim() || undefined;
60
+ if (!submittingClientId)
61
+ throw new BadRequestError(
62
+ "x-vellum-client-id header required for targeted transfer",
63
+ );
64
+ if (submittingClientId !== targetClientId)
65
+ throw new ForbiddenError(
66
+ `Client "${submittingClientId}" is not the owner of this transfer`,
67
+ );
68
+
69
+ // Defense-in-depth: the submitting actor's principal must match the
70
+ // actor that opened the target client's SSE stream. Compare against
71
+ // the value persisted at registration time so a brief reconnect does
72
+ // not 403 a legitimate fetch.
73
+ enforceSameActorOrThrow({
74
+ sourceActorPrincipalId: resolveActorPrincipalIdForLocalGuardian(
75
+ headerMap["x-vellum-actor-principal-id"]?.trim() || undefined,
76
+ ),
77
+ targetActorPrincipalId:
78
+ match.proxy.getTargetActorPrincipalIdForTransfer(transferId),
79
+ targetClientId,
80
+ op: "host_transfer",
81
+ });
50
82
  }
51
83
 
52
84
  const content = match.proxy.getTransferContent(transferId);
@@ -105,9 +137,27 @@ async function handleTransferContentPut({
105
137
 
106
138
  const targetClientId = match.proxy.getTargetClientIdForTransfer(transferId);
107
139
  if (targetClientId != null) {
108
- const submittingClientId = (headers as Record<string, string>)["x-vellum-client-id"]?.trim() || undefined;
109
- if (!submittingClientId) throw new BadRequestError("x-vellum-client-id header required for targeted transfer");
110
- if (submittingClientId !== targetClientId) throw new ForbiddenError(`Client "${submittingClientId}" is not the owner of this transfer`);
140
+ const headerMap = headers as Record<string, string | undefined>;
141
+ const submittingClientId =
142
+ headerMap["x-vellum-client-id"]?.trim() || undefined;
143
+ if (!submittingClientId)
144
+ throw new BadRequestError(
145
+ "x-vellum-client-id header required for targeted transfer",
146
+ );
147
+ if (submittingClientId !== targetClientId)
148
+ throw new ForbiddenError(
149
+ `Client "${submittingClientId}" is not the owner of this transfer`,
150
+ );
151
+
152
+ enforceSameActorOrThrow({
153
+ sourceActorPrincipalId: resolveActorPrincipalIdForLocalGuardian(
154
+ headerMap["x-vellum-actor-principal-id"]?.trim() || undefined,
155
+ ),
156
+ targetActorPrincipalId:
157
+ match.proxy.getTargetActorPrincipalIdForTransfer(transferId),
158
+ targetClientId,
159
+ op: "host_transfer",
160
+ });
111
161
  }
112
162
 
113
163
  const data = rawBody ? Buffer.from(rawBody) : Buffer.alloc(0);
@@ -158,10 +208,26 @@ function handleTransferResult({ body, headers }: RouteHandlerArgs) {
158
208
  }
159
209
 
160
210
  if (peeked.targetClientId != null) {
161
- const rawClientId = (headers as Record<string, string | undefined>)?.["x-vellum-client-id"];
211
+ const headerMap = (headers as Record<string, string | undefined>) ?? {};
212
+ const rawClientId = headerMap["x-vellum-client-id"];
162
213
  const submittingClientId = rawClientId?.trim() || undefined;
163
- if (!submittingClientId) throw new BadRequestError("x-vellum-client-id header is missing for a targeted host transfer request.");
164
- if (submittingClientId !== peeked.targetClientId) throw new ForbiddenError(`Client "${submittingClientId}" is not the target for this request (expected "${peeked.targetClientId}").`);
214
+ if (!submittingClientId)
215
+ throw new BadRequestError(
216
+ "x-vellum-client-id header is missing for a targeted host transfer request.",
217
+ );
218
+ if (submittingClientId !== peeked.targetClientId)
219
+ throw new ForbiddenError(
220
+ `Client "${submittingClientId}" is not the target for this request (expected "${peeked.targetClientId}").`,
221
+ );
222
+
223
+ enforceSameActorOrThrow({
224
+ sourceActorPrincipalId: resolveActorPrincipalIdForLocalGuardian(
225
+ headerMap["x-vellum-actor-principal-id"]?.trim() || undefined,
226
+ ),
227
+ targetActorPrincipalId: peeked.targetActorPrincipalId,
228
+ targetClientId: peeked.targetClientId,
229
+ op: "host_transfer",
230
+ });
165
231
  }
166
232
 
167
233
  HostTransferProxy.instance.resolveTransferResult(requestId, {
@@ -195,8 +261,7 @@ export const ROUTES: RouteDefinition[] = [
195
261
  "x-vellum-client-id header is missing for a targeted transfer.",
196
262
  },
197
263
  "403": {
198
- description:
199
- "Submitting client does not match the targeted client for this transfer.",
264
+ description: SAME_ACTOR_FORBIDDEN_DESCRIPTION,
200
265
  },
201
266
  },
202
267
  handler: handleTransferContentGet,
@@ -217,8 +282,7 @@ export const ROUTES: RouteDefinition[] = [
217
282
  "x-vellum-client-id header is missing for a targeted transfer.",
218
283
  },
219
284
  "403": {
220
- description:
221
- "Submitting client does not match the targeted client for this transfer.",
285
+ description: SAME_ACTOR_FORBIDDEN_DESCRIPTION,
222
286
  },
223
287
  },
224
288
  handler: handleTransferContentPut,
@@ -247,8 +311,7 @@ export const ROUTES: RouteDefinition[] = [
247
311
  "x-vellum-client-id header is missing for a targeted host transfer request.",
248
312
  },
249
313
  "403": {
250
- description:
251
- "Submitting client does not match the targeted client for this transfer.",
314
+ description: SAME_ACTOR_FORBIDDEN_DESCRIPTION,
252
315
  },
253
316
  },
254
317
  handler: handleTransferResult,
@@ -2,24 +2,20 @@
2
2
  * Identity and health endpoint handlers.
3
3
  */
4
4
 
5
- import { spawnSync } from "node:child_process";
6
- import { existsSync, readFileSync, statfsSync } from "node:fs";
5
+ import { existsSync, readFileSync } from "node:fs";
7
6
  import { availableParallelism, cpus, totalmem } from "node:os";
8
7
 
9
8
  import { z } from "zod";
10
9
 
11
- import {
12
- getCpuLimit,
13
- getIsPlatform,
14
- getMinikubeStorageSize,
15
- } from "../../config/env-registry.js";
10
+ import { getCpuLimit, getIsPlatform } from "../../config/env-registry.js";
16
11
  import { parseIdentityFields } from "../../daemon/handlers/identity.js";
17
12
  import { getProfilerRuntimeStatus } from "../../daemon/profiler-run-store.js";
18
13
  import { getMaxMigrationVersion } from "../../memory/migrations/registry.js";
19
14
  import {
20
- getWorkspaceDir,
21
- getWorkspacePromptPath,
22
- } from "../../util/platform.js";
15
+ getDiskUsageInfo,
16
+ parseK8sMemoryBytes,
17
+ } from "../../util/disk-usage.js";
18
+ import { getWorkspacePromptPath } from "../../util/platform.js";
23
19
  import { APP_VERSION } from "../../version.js";
24
20
  import { resolveHatchedAtReadOnly } from "../../workspace/hatched-date.js";
25
21
  import { WORKSPACE_MIGRATIONS } from "../../workspace/migrations/registry.js";
@@ -28,138 +24,11 @@ import { NotFoundError } from "./errors.js";
28
24
  import { getCachedIntro } from "./identity-intro-cache.js";
29
25
  import type { RouteDefinition } from "./types.js";
30
26
 
31
- interface DiskSpaceInfo {
32
- path: string;
33
- totalMb: number;
34
- usedMb: number;
35
- freeMb: number;
36
- }
37
-
38
- /**
39
- * Measure the on-disk usage of one or more directory paths using `du -sb`.
40
- * Returns the sum of all paths in bytes, or null on failure.
41
- */
42
- function getDirectorySizeBytes(paths: string[]): number | null {
43
- try {
44
- const existing = paths.filter((p) => existsSync(p));
45
- if (existing.length === 0) return null;
46
- const result = spawnSync("du", ["-sb", ...existing], {
47
- encoding: "utf-8",
48
- timeout: 30_000,
49
- });
50
- if (result.status !== 0) return null;
51
- let total = 0;
52
- for (const line of result.stdout.trim().split("\n")) {
53
- const size = parseInt(line.split("\t")[0], 10);
54
- if (!isNaN(size) && size > 0) total += size;
55
- }
56
- return total > 0 ? total : null;
57
- } catch {
58
- return null;
59
- }
60
- }
61
-
62
- const DU_CACHE_TTL_MS = 60_000;
63
- let duCacheValue: number | null = null;
64
- let duCacheTime = 0;
65
- let duCachePaths: string | null = null;
66
-
67
- function getCachedDirectorySizeBytes(paths: string[]): number | null {
68
- const key = paths.join("\0");
69
- const now = Date.now();
70
- if (duCachePaths === key && now - duCacheTime < DU_CACHE_TTL_MS) {
71
- return duCacheValue;
72
- }
73
- duCacheValue = getDirectorySizeBytes(paths);
74
- duCacheTime = now;
75
- duCachePaths = key;
76
- return duCacheValue;
77
- }
78
-
79
- function getDiskSpaceInfo(): DiskSpaceInfo | null {
80
- try {
81
- const wsDir = getWorkspaceDir();
82
- const diskPath = existsSync(wsDir) ? wsDir : "/";
83
- const stats = statfsSync(diskPath);
84
- const fsTotalBytes = stats.bsize * stats.blocks;
85
- const fsFreeBytes = stats.bsize * stats.bavail;
86
- const bytesToMb = (b: number) =>
87
- Math.round((b / (1024 * 1024)) * 100) / 100;
88
-
89
- // Minikube mode: the platform passes the PVC storage size so we can
90
- // report accurate capacity. On hostPath-backed PVCs statfsSync reports
91
- // the host's entire filesystem rather than the PVC. Detect this by
92
- // comparing filesystem size against PVC size — if the filesystem is
93
- // larger, measure actual directory usage with `du` instead.
94
- const storageSizeRaw = getMinikubeStorageSize();
95
- if (storageSizeRaw) {
96
- const pvcTotalBytes = parseK8sMemoryBytes(storageSizeRaw);
97
- if (pvcTotalBytes !== null && fsTotalBytes > pvcTotalBytes * 1.1) {
98
- const volumePaths = [diskPath];
99
- if (diskPath !== "/data" && existsSync("/data")) {
100
- volumePaths.push("/data");
101
- }
102
- const usedBytes = getCachedDirectorySizeBytes(volumePaths);
103
- if (usedBytes !== null) {
104
- return {
105
- path: diskPath,
106
- totalMb: bytesToMb(pvcTotalBytes),
107
- usedMb: bytesToMb(usedBytes),
108
- freeMb: bytesToMb(Math.max(0, pvcTotalBytes - usedBytes)),
109
- };
110
- }
111
- }
112
- }
113
-
114
- return {
115
- path: diskPath,
116
- totalMb: bytesToMb(fsTotalBytes),
117
- usedMb: bytesToMb(fsTotalBytes - fsFreeBytes),
118
- freeMb: bytesToMb(fsFreeBytes),
119
- };
120
- } catch {
121
- return null;
122
- }
123
- }
124
-
125
27
  interface MemoryInfo {
126
28
  currentMb: number;
127
29
  maxMb: number;
128
30
  }
129
31
 
130
- /**
131
- * Parse a Kubernetes-style memory string (e.g. "3Gi", "512Mi", "1G") into bytes.
132
- * Returns null if the value is not a recognized format.
133
- */
134
- function parseK8sMemoryBytes(value: string): number | null {
135
- const match = value
136
- .trim()
137
- .match(/^(\d+(?:\.\d+)?)\s*(Ki|Mi|Gi|Ti|Pi|Ei|k|M|G|T|P|E|m)?$/);
138
- if (!match) return null;
139
- const num = parseFloat(match[1]);
140
- const unit = match[2] ?? "";
141
- const multipliers: Record<string, number> = {
142
- "": 1,
143
- m: 1e-3,
144
- k: 1e3,
145
- M: 1e6,
146
- G: 1e9,
147
- T: 1e12,
148
- P: 1e15,
149
- E: 1e18,
150
- Ki: 1024,
151
- Mi: 1024 ** 2,
152
- Gi: 1024 ** 3,
153
- Ti: 1024 ** 4,
154
- Pi: 1024 ** 5,
155
- Ei: 1024 ** 6,
156
- };
157
- const mult = multipliers[unit];
158
- if (mult === undefined) return null;
159
- const bytes = Math.round(num * mult);
160
- return bytes > 0 ? bytes : null;
161
- }
162
-
163
32
  /**
164
33
  * Read the memory limit from the VELLUM_MEMORY_LIMIT env var (K8s resource format),
165
34
  * then fall back to cgroups, then to os.totalmem().
@@ -458,7 +327,7 @@ function getDetailedHealth() {
458
327
  status: "healthy",
459
328
  timestamp: new Date().toISOString(),
460
329
  version: APP_VERSION,
461
- disk: getDiskSpaceInfo(),
330
+ disk: getDiskUsageInfo(),
462
331
  memory: getMemoryInfo(),
463
332
  cpu: getCpuInfo(),
464
333
  migrations: {
@@ -15,6 +15,8 @@ import {
15
15
  createApprovalConversationGenerator,
16
16
  createApprovalCopyGenerator,
17
17
  } from "../../daemon/approval-generators.js";
18
+ import { getDiskPressureStatus } from "../../daemon/disk-pressure-guard.js";
19
+ import { classifyDiskPressureTurnPolicy } from "../../daemon/disk-pressure-policy.js";
18
20
  import { processMessage } from "../../daemon/process-message.js";
19
21
  import type { TrustContext } from "../../daemon/trust-context.js";
20
22
  import { HeartbeatService } from "../../heartbeat/heartbeat-service.js";
@@ -35,6 +37,7 @@ import {
35
37
  getPendingVerificationReply,
36
38
  } from "../../memory/delivery-channels.js";
37
39
  import {
40
+ clearPayload,
38
41
  findMessageBySourceId,
39
42
  linkMessage,
40
43
  recordInbound,
@@ -75,6 +78,8 @@ import { tryTranscribeAudioAttachments } from "./inbound-stages/transcribe-audio
75
78
  import type { RouteHandlerArgs } from "./types.js";
76
79
 
77
80
  const log = getLogger("runtime-http");
81
+ const DISK_PRESSURE_REMOTE_BLOCK_REPLY =
82
+ "Storage is critically low, so remote messages are ignored until the guardian frees enough space. Please try again later.";
78
83
 
79
84
  // Delete-lookup retry configuration. Delete webhooks can race ahead of
80
85
  // the inbound handler's `linkMessage` call when the original message's
@@ -524,6 +529,78 @@ export async function handleChannelInbound({
524
529
  });
525
530
  }
526
531
 
532
+ // ── Actor role resolution ──
533
+ // Uses shared channel-agnostic resolution so all ingress paths classify
534
+ // guardian vs non-guardian actors the same way.
535
+ const trustCtx: TrustContext = resolveTrustContext({
536
+ assistantId: canonicalAssistantId,
537
+ sourceChannel,
538
+ conversationExternalId,
539
+ actorExternalId: rawSenderId,
540
+ actorUsername: body.actorUsername,
541
+ actorDisplayName: body.actorDisplayName,
542
+ });
543
+
544
+ const diskPressureDecision = classifyDiskPressureTurnPolicy(
545
+ getDiskPressureStatus(),
546
+ {
547
+ sourceChannel,
548
+ sourceInterface,
549
+ trustContext: {
550
+ sourceChannel: trustCtx.sourceChannel,
551
+ trustClass: trustCtx.trustClass,
552
+ },
553
+ },
554
+ );
555
+ if (diskPressureDecision.action === "block") {
556
+ if (!result.duplicate) {
557
+ clearPayload(result.eventId);
558
+ markProcessed(result.eventId);
559
+ }
560
+ log.info(
561
+ {
562
+ conversationId: result.conversationId,
563
+ eventId: result.eventId,
564
+ duplicate: result.duplicate,
565
+ reason: diskPressureDecision.reason,
566
+ trustClass: trustCtx.trustClass,
567
+ },
568
+ "Channel inbound blocked during disk pressure cleanup mode",
569
+ );
570
+
571
+ if (replyCallbackUrl && !result.duplicate) {
572
+ const replyPayload: Parameters<typeof deliverChannelReply>[1] = {
573
+ chatId: conversationExternalId,
574
+ text: DISK_PRESSURE_REMOTE_BLOCK_REPLY,
575
+ assistantId: canonicalAssistantId,
576
+ };
577
+ if (sourceChannel === "slack" && (canonicalSenderId ?? rawSenderId)) {
578
+ replyPayload.ephemeral = true;
579
+ replyPayload.user = (canonicalSenderId ?? rawSenderId)!;
580
+ }
581
+ try {
582
+ await deliverChannelReply(replyCallbackUrl, replyPayload);
583
+ } catch (err) {
584
+ log.warn(
585
+ {
586
+ err,
587
+ conversationId: result.conversationId,
588
+ eventId: result.eventId,
589
+ },
590
+ "Failed to deliver disk pressure block reply",
591
+ );
592
+ }
593
+ }
594
+
595
+ return {
596
+ accepted: true,
597
+ duplicate: result.duplicate,
598
+ eventId: result.eventId,
599
+ diskPressure: "blocked",
600
+ reason: diskPressureDecision.reason,
601
+ };
602
+ }
603
+
527
604
  // ── Slack reaction handling ──
528
605
  // Reactions arrive as regular `SlackInboundEvent`s with `callbackData`
529
606
  // prefixed `reaction:` (added) or `reaction_removed:` (removed).
@@ -735,18 +812,6 @@ export async function handleChannelInbound({
735
812
  // which handles request code matching, callback parsing, and NL classification
736
813
  // against canonical_guardian_requests.
737
814
 
738
- // ── Actor role resolution ──
739
- // Uses shared channel-agnostic resolution so all ingress paths classify
740
- // guardian vs non-guardian actors the same way.
741
- const trustCtx: TrustContext = resolveTrustContext({
742
- assistantId: canonicalAssistantId,
743
- sourceChannel,
744
- conversationExternalId,
745
- actorExternalId: rawSenderId,
746
- actorUsername: body.actorUsername,
747
- actorDisplayName: body.actorDisplayName,
748
- });
749
-
750
815
  // ── Canonical guardian reply router ──
751
816
  const guardianReplyResult = await handleGuardianReplyIntercept({
752
817
  isDuplicate: result.duplicate,
@@ -37,9 +37,11 @@ import { ROUTES as CONVERSATION_QUERY_ROUTES } from "./conversation-query-routes
37
37
  import { ROUTES as CONVERSATION_MESSAGE_ROUTES } from "./conversation-routes.js";
38
38
  import { ROUTES as CONVERSATION_STARTER_ROUTES } from "./conversation-starter-routes.js";
39
39
  import { ROUTES as CREDENTIAL_PROMPT_ROUTES } from "./credential-prompt-routes.js";
40
+ import { ROUTES as DEBUG_BASH_ROUTES } from "./debug-bash-routes.js";
40
41
  import { ROUTES as DEBUG_ROUTES } from "./debug-routes.js";
41
42
  import { ROUTES as DEFER_ROUTES } from "./defer-routes.js";
42
43
  import { ROUTES as DIAGNOSTICS_ROUTES } from "./diagnostics-routes.js";
44
+ import { ROUTES as DISK_PRESSURE_ROUTES } from "./disk-pressure-routes.js";
43
45
  import { ROUTES as DOCUMENT_ROUTES } from "./documents-routes.js";
44
46
  import { ROUTES as EVENTS_ROUTES } from "./events-routes.js";
45
47
  import { ROUTES as FILING_ROUTES } from "./filing-routes.js";
@@ -74,6 +76,7 @@ import { ROUTES as MIGRATION_ROLLBACK_ROUTES } from "./migration-rollback-routes
74
76
  import { ROUTES as MIGRATION_ROUTES } from "./migration-routes.js";
75
77
  import { ROUTES as NOTIFICATION_ROUTES } from "./notification-routes.js";
76
78
  import { ROUTES as OAUTH_APPS_ROUTES } from "./oauth-apps.js";
79
+ import { ROUTES as OAUTH_CONNECT_ROUTES } from "./oauth-connect-routes.js";
77
80
  import { ROUTES as OAUTH_PROVIDERS_ROUTES } from "./oauth-providers.js";
78
81
  import { ROUTES as PLAYGROUND_ROUTES } from "./playground/index.js";
79
82
  import { ROUTES as PROFILER_ROUTES } from "./profiler-routes.js";
@@ -137,8 +140,10 @@ export const ROUTES: RouteDefinition[] = [
137
140
  ...DEFER_ROUTES,
138
141
  ...CONVERSATION_QUERY_ROUTES,
139
142
  ...CONVERSATION_STARTER_ROUTES,
143
+ ...DEBUG_BASH_ROUTES,
140
144
  ...DEBUG_ROUTES,
141
145
  ...DIAGNOSTICS_ROUTES,
146
+ ...DISK_PRESSURE_ROUTES,
142
147
  ...DOCUMENT_ROUTES,
143
148
  ...EVENTS_ROUTES,
144
149
  ...FILING_ROUTES,
@@ -159,6 +164,7 @@ export const ROUTES: RouteDefinition[] = [
159
164
  ...INTERFACE_ROUTES,
160
165
  ...INTERNAL_OAUTH_ROUTES,
161
166
  ...MCP_AUTH_ROUTES,
167
+ ...OAUTH_CONNECT_ROUTES,
162
168
  ...INTERNAL_TWILIO_ROUTES,
163
169
  ...LOG_EXPORT_ROUTES,
164
170
  ...LLM_CALL_SITES_ROUTES,