@vellumai/assistant 0.8.0 → 0.8.1

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 (692) hide show
  1. package/AGENTS.md +11 -0
  2. package/Dockerfile +5 -4
  3. package/README.md +2 -2
  4. package/docker-entrypoint.sh +16 -0
  5. package/eslint-rules/__tests__/cli-no-daemon-internals.test.ts +420 -0
  6. package/eslint-rules/cli-no-daemon-internals.js +283 -0
  7. package/eslint.config.mjs +12 -0
  8. package/knip.json +2 -1
  9. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +10 -1
  10. package/openapi.yaml +4847 -1698
  11. package/package.json +3 -1
  12. package/scripts/generate-openapi.ts +52 -4
  13. package/scripts/sync-llm-catalog.ts +165 -0
  14. package/scripts/sync-web-search-catalog.ts +107 -0
  15. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +169 -0
  16. package/src/__tests__/agent-loop-override-profile.test.ts +26 -1
  17. package/src/__tests__/anthropic-provider.test.ts +92 -2
  18. package/src/__tests__/app-control-flow.test.ts +7 -0
  19. package/src/__tests__/assistant-events-sse-shed.test.ts +232 -0
  20. package/src/__tests__/avatar-identity-sync.test.ts +87 -0
  21. package/src/__tests__/background-workers-disk-pressure.test.ts +11 -22
  22. package/src/__tests__/btw-routes.test.ts +1 -0
  23. package/src/__tests__/call-site-routing-provider.test.ts +172 -45
  24. package/src/__tests__/cancel-resolves-conversation-key.test.ts +44 -3
  25. package/src/__tests__/channel-policy.test.ts +12 -0
  26. package/src/__tests__/checker.test.ts +89 -0
  27. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +35 -7
  28. package/src/__tests__/compact-event-conversation-id-guard.test.ts +33 -5
  29. package/src/__tests__/compaction-strip-metadata-clear.test.ts +26 -1
  30. package/src/__tests__/config-loader-backfill.test.ts +526 -102
  31. package/src/__tests__/config-loader-corrupt.test.ts +68 -0
  32. package/src/__tests__/config-loader-platform-defaults.test.ts +77 -23
  33. package/src/__tests__/config-schema-cmd.test.ts +63 -29
  34. package/src/__tests__/config-schema.test.ts +14 -3
  35. package/src/__tests__/config-set-platform-guard.test.ts +75 -152
  36. package/src/__tests__/config-set-route.test.ts +198 -0
  37. package/src/__tests__/config-watcher.test.ts +6 -0
  38. package/src/__tests__/contacts-tools.test.ts +51 -199
  39. package/src/__tests__/context-search-agent-protocol.test.ts +21 -2
  40. package/src/__tests__/context-search-agent-runner.test.ts +22 -138
  41. package/src/__tests__/context-search-conversations-source.test.ts +42 -16
  42. package/src/__tests__/context-search-fanout.test.ts +20 -157
  43. package/src/__tests__/context-search-memory-v2-source.test.ts +3 -3
  44. package/src/__tests__/context-search-types.test.ts +7 -2
  45. package/src/__tests__/context-window-manager.test.ts +389 -1
  46. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
  47. package/src/__tests__/conversation-crud-inference-profile.test.ts +100 -0
  48. package/src/__tests__/conversation-error.test.ts +38 -0
  49. package/src/__tests__/conversation-fork-crud.test.ts +241 -1
  50. package/src/__tests__/conversation-inference-profile-route.test.ts +14 -14
  51. package/src/__tests__/conversation-init.benchmark.test.ts +1 -0
  52. package/src/__tests__/conversation-lifecycle.test.ts +124 -0
  53. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +100 -1
  54. package/src/__tests__/conversation-process-callsite.test.ts +21 -1
  55. package/src/__tests__/conversation-runtime-assembly.test.ts +4 -4
  56. package/src/__tests__/conversation-slash-commands.test.ts +194 -2
  57. package/src/__tests__/conversation-surfaces-app-control.test.ts +323 -3
  58. package/src/__tests__/credential-security-invariants.test.ts +5 -6
  59. package/src/__tests__/daemon-credential-client.test.ts +56 -1
  60. package/src/__tests__/db-activation-state-fk-cascade.test.ts +132 -0
  61. package/src/__tests__/db-conversation-inference-profile-migration.test.ts +37 -0
  62. package/src/__tests__/db-memory-graph-event-date-repair.test.ts +43 -20
  63. package/src/__tests__/db-proxy-transaction.test.ts +206 -0
  64. package/src/__tests__/external-plugin-loader.test.ts +458 -0
  65. package/src/__tests__/filing-service.test.ts +23 -3
  66. package/src/__tests__/fixtures/mock-chrome-extension.ts +5 -0
  67. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  68. package/src/__tests__/graph-extraction-event-date.test.ts +34 -0
  69. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +0 -8
  70. package/src/__tests__/heartbeat-disk-pressure.test.ts +21 -8
  71. package/src/__tests__/heartbeat-service.test.ts +50 -233
  72. package/src/__tests__/history-repair.test.ts +89 -0
  73. package/src/__tests__/host-app-control-proxy.test.ts +109 -1
  74. package/src/__tests__/host-app-control-routes.test.ts +247 -1
  75. package/src/__tests__/host-browser-proxy.test.ts +416 -20
  76. package/src/__tests__/host-browser-routes.test.ts +325 -33
  77. package/src/__tests__/host-proxy-preactivation.test.ts +211 -0
  78. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +246 -0
  79. package/src/__tests__/inference-profile-reaper.test.ts +154 -0
  80. package/src/__tests__/inference-profile-session-handler.test.ts +398 -0
  81. package/src/__tests__/inference-profile-session-ipc.test.ts +236 -0
  82. package/src/__tests__/inline-skill-load-permissions.test.ts +6 -1
  83. package/src/__tests__/install-skill-routing.test.ts +2 -2
  84. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +15 -0
  85. package/src/__tests__/llm-callsite-catalog.test.ts +20 -1
  86. package/src/__tests__/llm-catalog-parity.test.ts +146 -0
  87. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +188 -0
  88. package/src/__tests__/llm-request-log-source-factory.test.ts +124 -0
  89. package/src/__tests__/llm-resolver.test.ts +46 -0
  90. package/src/__tests__/managed-profile-guard.test.ts +131 -2
  91. package/src/__tests__/mcp-auth-routes.test.ts +1 -0
  92. package/src/__tests__/mcp-cli.test.ts +182 -220
  93. package/src/__tests__/mcp-health-check.test.ts +56 -27
  94. package/src/__tests__/memory-jobs-worker-lanes.test.ts +18 -11
  95. package/src/__tests__/message-complete-display-id.test.ts +175 -0
  96. package/src/__tests__/notification-platform-adapter.test.ts +229 -0
  97. package/src/__tests__/oauth-cli.test.ts +38 -2009
  98. package/src/__tests__/oauth-commands-routes.test.ts +711 -0
  99. package/src/__tests__/oauth-connect-routes.test.ts +174 -11
  100. package/src/__tests__/oauth-providers-routes.test.ts +14 -10
  101. package/src/__tests__/openai-responses-cutover-guard.test.ts +33 -12
  102. package/src/__tests__/openai-responses-provider.test.ts +17 -0
  103. package/src/__tests__/plugin-bootstrap.test.ts +31 -2
  104. package/src/__tests__/plugin-route-contribution.test.ts +31 -3
  105. package/src/__tests__/plugin-tool-contribution.test.ts +31 -3
  106. package/src/__tests__/plugin-types.test.ts +13 -11
  107. package/src/__tests__/process-message-background-slack.test.ts +46 -0
  108. package/src/__tests__/profile-entry-status.test.ts +43 -0
  109. package/src/__tests__/provider-managed-proxy-integration.test.ts +12 -4
  110. package/src/__tests__/provider-registry-ollama.test.ts +12 -4
  111. package/src/__tests__/provider-send-message-override-profile.test.ts +10 -4
  112. package/src/__tests__/relay-server.test.ts +118 -0
  113. package/src/__tests__/retry-thinking-tool-choice.test.ts +15 -0
  114. package/src/__tests__/schedule-retry.test.ts +56 -4
  115. package/src/__tests__/schedule-routes.test.ts +104 -0
  116. package/src/__tests__/scheduler-disk-pressure.test.ts +0 -4
  117. package/src/__tests__/scheduler-recurrence.test.ts +87 -34
  118. package/src/__tests__/scheduler-reuse-conversation.test.ts +161 -5
  119. package/src/__tests__/scheduler-wake.test.ts +0 -63
  120. package/src/__tests__/secret-allowlist.test.ts +1 -0
  121. package/src/__tests__/secret-routes-managed-proxy.test.ts +12 -4
  122. package/src/__tests__/shell-credential-ref.test.ts +95 -3
  123. package/src/__tests__/shell-tool-proxy-mode.test.ts +14 -0
  124. package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
  125. package/src/__tests__/skill-load-tool.test.ts +2 -4
  126. package/src/__tests__/subagent-call-site-routing.test.ts +78 -16
  127. package/src/__tests__/suggestion-routes.test.ts +3 -3
  128. package/src/__tests__/sync-message-contract.test.ts +63 -0
  129. package/src/__tests__/task-scheduler.test.ts +88 -23
  130. package/src/__tests__/update-bulletin-job.test.ts +96 -193
  131. package/src/__tests__/usage-cli.test.ts +11 -73
  132. package/src/__tests__/user-plugin-loader.test.ts +145 -0
  133. package/src/__tests__/vercel-config.test.ts +168 -0
  134. package/src/__tests__/web-search-catalog-parity.test.ts +86 -0
  135. package/src/__tests__/web-search.test.ts +303 -2
  136. package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +1 -21
  137. package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +58 -0
  138. package/src/__tests__/workspace-migration-069-seed-onboarding-threads.test.ts +53 -20
  139. package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +191 -0
  140. package/src/__tests__/workspace-migration-076-drop-services-inference-mode.test.ts +211 -0
  141. package/src/__tests__/workspace-migration-077-seed-memory-router-callsite.test.ts +174 -0
  142. package/src/__tests__/workspace-migration-079-home-feed-notification-only.test.ts +323 -0
  143. package/src/__tests__/workspace-migration-080-restrict-vercel-api-token-metadata.test.ts +299 -0
  144. package/src/__tests__/workspace-migration-081-backfill-bash-allowed-tools.test.ts +410 -0
  145. package/src/__tests__/workspace-migration-082-backfill-managed-profile-labels.test.ts +268 -0
  146. package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +3 -3
  147. package/src/__tests__/workspace-release-notes-feature-flag-guard.test.ts +115 -0
  148. package/src/acp/__tests__/helpers/which-stub.ts +4 -2
  149. package/src/acp/resolve-agent.test.ts +25 -0
  150. package/src/acp/resolve-agent.ts +13 -2
  151. package/src/acp/session-manager.ts +14 -0
  152. package/src/approvals/guardian-request-resolvers.ts +32 -87
  153. package/src/calls/relay-server.ts +35 -0
  154. package/src/calls/relay-setup-router.ts +36 -0
  155. package/src/calls/types.ts +1 -0
  156. package/src/calls/voice-session-bridge.ts +23 -4
  157. package/src/channels/config.ts +14 -1
  158. package/src/channels/types.ts +1 -0
  159. package/src/cli/AGENTS.md +164 -4
  160. package/src/cli/__tests__/notifications.test.ts +54 -0
  161. package/src/cli/commands/__tests__/avatar.test.ts +540 -0
  162. package/src/cli/commands/__tests__/backup.test.ts +236 -776
  163. package/src/cli/commands/__tests__/cache.test.ts +1 -1
  164. package/src/cli/commands/__tests__/changelog.test.ts +593 -0
  165. package/src/cli/commands/__tests__/channel-verification-sessions.test.ts +503 -0
  166. package/src/cli/commands/__tests__/conversations-import.test.ts +515 -0
  167. package/src/cli/commands/__tests__/domain-register.test.ts +140 -167
  168. package/src/cli/commands/__tests__/domain-status.test.ts +137 -76
  169. package/src/cli/commands/__tests__/email-attachment.test.ts +314 -337
  170. package/src/cli/commands/__tests__/email-core.test.ts +579 -0
  171. package/src/cli/commands/__tests__/image-generation.test.ts +87 -824
  172. package/src/cli/commands/__tests__/inference-send.test.ts +30 -266
  173. package/src/cli/commands/__tests__/inference-session.test.ts +423 -0
  174. package/src/cli/commands/__tests__/memory-v2.test.ts +81 -110
  175. package/src/cli/commands/__tests__/skills.test.ts +563 -0
  176. package/src/cli/commands/__tests__/status.test.ts +249 -0
  177. package/src/cli/commands/__tests__/stt.test.ts +320 -0
  178. package/src/cli/commands/__tests__/tts-synthesize.test.ts +4 -603
  179. package/src/cli/commands/__tests__/tts.test.ts +321 -0
  180. package/src/cli/commands/__tests__/webhooks.test.ts +86 -511
  181. package/src/cli/commands/attachment.ts +8 -3
  182. package/src/cli/commands/audit.ts +95 -64
  183. package/src/cli/commands/auth.ts +61 -58
  184. package/src/cli/commands/avatar.ts +276 -390
  185. package/src/cli/commands/backup.ts +409 -505
  186. package/src/cli/commands/bash.ts +9 -5
  187. package/src/cli/commands/browser.ts +28 -9
  188. package/src/cli/commands/cache.ts +9 -4
  189. package/src/cli/commands/changelog.ts +414 -0
  190. package/src/cli/commands/channel-verification-sessions.ts +238 -317
  191. package/src/cli/commands/clients.ts +8 -3
  192. package/src/cli/commands/completions.ts +9 -9
  193. package/src/cli/commands/config.ts +102 -72
  194. package/src/cli/commands/contacts.ts +575 -696
  195. package/src/cli/commands/conversations-defer.ts +17 -69
  196. package/src/cli/commands/conversations-import.ts +90 -253
  197. package/src/cli/commands/conversations.ts +346 -436
  198. package/src/cli/commands/credential-execution.ts +9 -6
  199. package/src/cli/commands/credentials.ts +456 -736
  200. package/src/cli/commands/domain.ts +128 -206
  201. package/src/cli/commands/email.ts +606 -794
  202. package/src/cli/commands/gateway.ts +8 -1
  203. package/src/cli/commands/image-generation.ts +157 -205
  204. package/src/cli/commands/inference-providers.ts +352 -0
  205. package/src/cli/commands/inference-session.ts +415 -0
  206. package/src/cli/commands/inference.ts +87 -65
  207. package/src/cli/commands/keys.ts +8 -3
  208. package/src/cli/commands/mcp.ts +103 -287
  209. package/src/cli/commands/memory-v2.ts +162 -516
  210. package/src/cli/commands/notifications.ts +33 -7
  211. package/src/cli/commands/oauth/apps.ts +292 -261
  212. package/src/cli/commands/oauth/connect.ts +176 -297
  213. package/src/cli/commands/oauth/disconnect.ts +16 -215
  214. package/src/cli/commands/oauth/index.ts +49 -45
  215. package/src/cli/commands/oauth/mode.ts +43 -199
  216. package/src/cli/commands/oauth/ping.ts +17 -125
  217. package/src/cli/commands/oauth/providers.ts +732 -921
  218. package/src/cli/commands/oauth/request.ts +60 -350
  219. package/src/cli/commands/oauth/shared.ts +11 -121
  220. package/src/cli/commands/oauth/status.ts +31 -121
  221. package/src/cli/commands/oauth/token.ts +13 -55
  222. package/src/cli/commands/pending.ts +19 -10
  223. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +133 -183
  224. package/src/cli/commands/platform/__tests__/connect.test.ts +66 -181
  225. package/src/cli/commands/platform/__tests__/disconnect.test.ts +71 -227
  226. package/src/cli/commands/platform/__tests__/status.test.ts +169 -287
  227. package/src/cli/commands/platform/connect.ts +16 -80
  228. package/src/cli/commands/platform/disconnect.ts +14 -112
  229. package/src/cli/commands/platform/index.ts +177 -246
  230. package/src/cli/commands/routes.ts +153 -336
  231. package/src/cli/commands/sequence.ts +316 -360
  232. package/src/cli/commands/skills.ts +449 -671
  233. package/src/cli/commands/status.ts +58 -37
  234. package/src/cli/commands/stt.ts +94 -262
  235. package/src/cli/commands/task.ts +14 -40
  236. package/src/cli/commands/trust.ts +8 -3
  237. package/src/cli/commands/tts.ts +162 -167
  238. package/src/cli/commands/ui.ts +35 -42
  239. package/src/cli/commands/usage.ts +188 -126
  240. package/src/cli/commands/watchers.ts +8 -3
  241. package/src/cli/commands/webhooks.ts +99 -193
  242. package/src/cli/lib/__tests__/register-command.test.ts +85 -0
  243. package/src/cli/lib/daemon-credential-client.ts +4 -5
  244. package/src/cli/lib/nested-value.ts +44 -0
  245. package/src/cli/lib/open-browser.ts +36 -0
  246. package/src/cli/lib/register-command.ts +19 -0
  247. package/src/cli/lib/time-ago.ts +34 -0
  248. package/src/cli/program.ts +2 -4
  249. package/src/cli/utils/__tests__/conversation-id.test.ts +66 -0
  250. package/src/cli/utils/__tests__/parse-duration.test.ts +49 -0
  251. package/src/cli/utils/conversation-id.ts +30 -0
  252. package/src/cli/utils/parse-duration.ts +41 -0
  253. package/src/config/acp-defaults.test.ts +5 -1
  254. package/src/config/acp-defaults.ts +11 -4
  255. package/src/config/bundled-skills/acp/TOOLS.json +2 -2
  256. package/src/config/bundled-skills/app-control/TOOLS.json +32 -0
  257. package/src/config/bundled-skills/contacts/SKILL.md +12 -45
  258. package/src/config/bundled-skills/contacts/TOOLS.json +0 -57
  259. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +0 -12
  260. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +0 -58
  261. package/src/config/bundled-tool-registry.ts +0 -2
  262. package/src/config/feature-flag-registry.json +16 -0
  263. package/src/config/llm-resolver.ts +16 -1
  264. package/src/config/loader.ts +76 -14
  265. package/src/config/raw-config-utils.ts +2 -30
  266. package/src/config/schema.ts +4 -0
  267. package/src/config/schemas/__tests__/memory-v2.test.ts +49 -0
  268. package/src/config/schemas/call-site-catalog.ts +29 -7
  269. package/src/config/schemas/llm-request-logs.ts +57 -0
  270. package/src/config/schemas/llm.ts +52 -2
  271. package/src/config/schemas/memory-retrospective.ts +48 -0
  272. package/src/config/schemas/memory-v2.ts +32 -1
  273. package/src/config/schemas/memory.ts +4 -0
  274. package/src/config/schemas/services.ts +15 -12
  275. package/src/config/seed-inference-profiles.ts +195 -134
  276. package/src/contacts/contact-store.ts +0 -61
  277. package/src/context/window-manager.ts +191 -5
  278. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +79 -0
  279. package/src/daemon/__tests__/conversation-tool-setup.test.ts +109 -4
  280. package/src/daemon/__tests__/daemon-skill-host.test.ts +10 -4
  281. package/src/daemon/approval-generators.ts +23 -29
  282. package/src/daemon/config-watcher.ts +2 -0
  283. package/src/daemon/conversation-agent-loop-handlers.ts +24 -0
  284. package/src/daemon/conversation-agent-loop.ts +127 -97
  285. package/src/daemon/conversation-error.ts +21 -0
  286. package/src/daemon/conversation-lifecycle.ts +46 -5
  287. package/src/daemon/conversation-process.ts +36 -19
  288. package/src/daemon/conversation-runtime-assembly.ts +14 -5
  289. package/src/daemon/conversation-slash.ts +175 -23
  290. package/src/daemon/conversation-store.ts +17 -10
  291. package/src/daemon/conversation-surfaces.ts +76 -12
  292. package/src/daemon/conversation-tool-setup.ts +24 -14
  293. package/src/daemon/conversation.ts +48 -9
  294. package/src/daemon/external-plugins-bootstrap.ts +18 -8
  295. package/src/daemon/guardian-action-generators.ts +7 -22
  296. package/src/daemon/handlers/config-model.ts +8 -126
  297. package/src/daemon/handlers/config-slack-channel.ts +10 -7
  298. package/src/daemon/handlers/config-vercel.ts +3 -1
  299. package/src/daemon/handlers/skills.ts +84 -5
  300. package/src/daemon/history-repair.ts +33 -6
  301. package/src/daemon/host-app-control-proxy.ts +44 -19
  302. package/src/daemon/host-bash-proxy.ts +85 -158
  303. package/src/daemon/host-browser-proxy.ts +96 -35
  304. package/src/daemon/host-proxy-base.ts +13 -1
  305. package/src/daemon/host-proxy-preactivation.ts +25 -1
  306. package/src/daemon/identity-helpers.ts +19 -0
  307. package/src/daemon/lifecycle.ts +42 -43
  308. package/src/daemon/meet-host-supervisor.ts +15 -15
  309. package/src/daemon/memory-v2-startup.ts +9 -2
  310. package/src/daemon/message-protocol.ts +6 -0
  311. package/src/daemon/message-types/bookmarks.ts +18 -0
  312. package/src/daemon/message-types/conversations.ts +12 -9
  313. package/src/daemon/message-types/messages.ts +9 -1
  314. package/src/daemon/message-types/sync.ts +60 -0
  315. package/src/daemon/pkb-reminder-builder.test.ts +54 -13
  316. package/src/daemon/pkb-reminder-builder.ts +21 -7
  317. package/src/daemon/process-message.ts +56 -23
  318. package/src/daemon/server.ts +23 -18
  319. package/src/daemon/shutdown-handlers.ts +0 -2
  320. package/src/daemon/tool-setup-types.ts +9 -0
  321. package/src/daemon/tool-side-effects.ts +6 -4
  322. package/src/daemon/wake-target-adapter.ts +11 -0
  323. package/src/export/transcript-formatter.ts +61 -2
  324. package/src/filing/filing-service.ts +40 -53
  325. package/src/heartbeat/__tests__/heartbeat-service.test.ts +359 -0
  326. package/src/heartbeat/heartbeat-run-store.ts +2 -1
  327. package/src/heartbeat/heartbeat-service.ts +148 -127
  328. package/src/home/__tests__/feed-types.test.ts +63 -131
  329. package/src/home/__tests__/feed-writer.test.ts +77 -278
  330. package/src/home/__tests__/post-connect-feed.test.ts +9 -12
  331. package/src/home/feed-types.ts +19 -73
  332. package/src/home/feed-writer.ts +25 -156
  333. package/src/home/post-connect-feed.ts +1 -3
  334. package/src/ipc/__tests__/cli-ipc.test.ts +2 -0
  335. package/src/ipc/__tests__/email-ipc.test.ts +506 -0
  336. package/src/ipc/__tests__/exit-helper.test.ts +104 -0
  337. package/src/ipc/__tests__/streaming-client.test.ts +237 -0
  338. package/src/ipc/__tests__/streaming-framing.test.ts +142 -0
  339. package/src/ipc/assistant-server.ts +55 -6
  340. package/src/ipc/cli-client.ts +370 -50
  341. package/src/ipc/routes/db-proxy-transaction.ts +151 -0
  342. package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +60 -0
  343. package/src/ipc/skill-routes/events.ts +30 -3
  344. package/src/live-voice/__tests__/live-voice-session-manager.test.ts +46 -0
  345. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +1 -0
  346. package/src/live-voice/live-voice-session-manager.ts +11 -4
  347. package/src/live-voice/live-voice-session.ts +14 -6
  348. package/src/memory/__tests__/bookmark-crud.test.ts +258 -0
  349. package/src/memory/__tests__/bookmark-schema.test.ts +181 -0
  350. package/src/memory/__tests__/conversation-types.test.ts +36 -0
  351. package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +130 -0
  352. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +177 -0
  353. package/src/memory/__tests__/memory-retrospective-job.test.ts +328 -0
  354. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +213 -0
  355. package/src/memory/__tests__/memory-retrospective-trigger-check.test.ts +90 -0
  356. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +69 -0
  357. package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +3 -0
  358. package/src/memory/bookmark-crud.ts +179 -0
  359. package/src/memory/context-search/__tests__/agent-runner-redaction.test.ts +31 -9
  360. package/src/memory/context-search/agent-protocol.ts +5 -1
  361. package/src/memory/context-search/agent-runner.ts +60 -85
  362. package/src/memory/context-search/limits.ts +1 -4
  363. package/src/memory/context-search/search.ts +23 -113
  364. package/src/memory/context-search/sources/conversations.ts +18 -6
  365. package/src/memory/context-search/sources/memory-v2.ts +39 -14
  366. package/src/memory/context-search/sources/memory.ts +7 -0
  367. package/src/memory/context-search/sources/workspace.ts +13 -10
  368. package/src/memory/context-search/types.ts +1 -1
  369. package/src/memory/conversation-bootstrap.ts +11 -0
  370. package/src/memory/conversation-crud.ts +312 -10
  371. package/src/memory/conversation-queries.ts +9 -5
  372. package/src/memory/conversation-title-service.ts +1 -0
  373. package/src/memory/conversation-types.ts +16 -0
  374. package/src/memory/db-init.ts +14 -0
  375. package/src/memory/embedding-backend.ts +2 -1
  376. package/src/memory/embedding-runtime-manager.ts +1 -2
  377. package/src/memory/graph/__tests__/remember-description.test.ts +55 -0
  378. package/src/memory/graph/conversation-graph-memory.ts +76 -5
  379. package/src/memory/graph/extraction.ts +4 -0
  380. package/src/memory/graph/graph-memory-state-store.ts +16 -3
  381. package/src/memory/graph/tool-handlers.ts +17 -7
  382. package/src/memory/graph/tools.ts +44 -5
  383. package/src/memory/indexer.ts +17 -0
  384. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +13 -15
  385. package/src/memory/jobs/embed-concept-page.ts +45 -9
  386. package/src/memory/jobs-store.ts +51 -1
  387. package/src/memory/jobs-worker.ts +52 -3
  388. package/src/memory/llm-request-log-source-clickhouse.ts +317 -0
  389. package/src/memory/llm-request-log-source-local.ts +26 -0
  390. package/src/memory/llm-request-log-source.ts +97 -0
  391. package/src/memory/llm-request-log-store.ts +1 -1
  392. package/src/memory/memory-retrospective-constants.ts +13 -0
  393. package/src/memory/memory-retrospective-enqueue.ts +114 -0
  394. package/src/memory/memory-retrospective-job.ts +351 -0
  395. package/src/memory/memory-retrospective-startup-cleanup.ts +108 -0
  396. package/src/memory/memory-retrospective-state.ts +162 -0
  397. package/src/memory/memory-retrospective-trigger-check.ts +91 -0
  398. package/src/memory/memory-v2-activation-log-store.ts +49 -5
  399. package/src/memory/memory-v2-concept-frequency.ts +4 -0
  400. package/src/memory/message-content.ts +38 -1
  401. package/src/memory/migrations/227-add-conversation-inference-profile.ts +6 -1
  402. package/src/memory/migrations/228-rename-inference-profile-snake-case.ts +20 -7
  403. package/src/memory/migrations/229-delete-private-conversations.test.ts +70 -1
  404. package/src/memory/migrations/229-delete-private-conversations.ts +12 -0
  405. package/src/memory/migrations/231-repair-memory-graph-event-dates.ts +16 -2
  406. package/src/memory/migrations/240-conversation-inference-profile-session.ts +25 -0
  407. package/src/memory/migrations/241-activation-state-fk-cascade.ts +50 -0
  408. package/src/memory/migrations/242-message-bookmarks.ts +38 -0
  409. package/src/memory/migrations/243-provider-connections.ts +68 -0
  410. package/src/memory/migrations/244-provider-connection-status-label.ts +23 -0
  411. package/src/memory/migrations/245-memory-retrospective-state.ts +36 -0
  412. package/src/memory/migrations/246-backfill-provider-connection-label.ts +81 -0
  413. package/src/memory/migrations/__tests__/244-provider-connection-status-label.test.ts +84 -0
  414. package/src/memory/migrations/__tests__/245-memory-retrospective-state.test.ts +125 -0
  415. package/src/memory/migrations/__tests__/246-backfill-provider-connection-label.test.ts +192 -0
  416. package/src/memory/migrations/index.ts +7 -0
  417. package/src/memory/published-pages-store.ts +16 -0
  418. package/src/memory/schema/bookmarks.ts +38 -0
  419. package/src/memory/schema/conversations.ts +2 -0
  420. package/src/memory/schema/index.ts +2 -0
  421. package/src/memory/schema/inference.ts +29 -0
  422. package/src/memory/schema/memory-core.ts +9 -0
  423. package/src/memory/search/semantic.ts +1 -4
  424. package/src/memory/v2/__tests__/__snapshots__/prompts-router.test.ts.snap +27 -0
  425. package/src/memory/v2/__tests__/activation-store.test.ts +5 -5
  426. package/src/memory/v2/__tests__/activation.test.ts +11 -4
  427. package/src/memory/v2/__tests__/backfill-jobs.test.ts +38 -21
  428. package/src/memory/v2/__tests__/consolidation-job.test.ts +123 -135
  429. package/src/memory/v2/__tests__/edge-index.test.ts +1 -1
  430. package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +111 -0
  431. package/src/memory/v2/__tests__/injection.test.ts +628 -10
  432. package/src/memory/v2/__tests__/migration.test.ts +7 -3
  433. package/src/memory/v2/__tests__/page-index.test.ts +277 -0
  434. package/src/memory/v2/__tests__/page-store.test.ts +14 -1
  435. package/src/memory/v2/__tests__/prompts-router.test.ts +257 -0
  436. package/src/memory/v2/__tests__/qdrant.test.ts +72 -0
  437. package/src/memory/v2/__tests__/reranker.test.ts +4 -4
  438. package/src/memory/v2/__tests__/router.test.ts +516 -0
  439. package/src/memory/v2/__tests__/sim.test.ts +45 -1
  440. package/src/memory/v2/__tests__/skill-store.test.ts +58 -3
  441. package/src/memory/v2/__tests__/static-context.test.ts +7 -22
  442. package/src/memory/v2/__tests__/sweep-job.test.ts +95 -0
  443. package/src/memory/v2/activation-store.ts +34 -5
  444. package/src/memory/v2/activation.ts +40 -27
  445. package/src/memory/v2/backfill-jobs.ts +17 -84
  446. package/src/memory/v2/consolidation-job.ts +85 -78
  447. package/src/memory/v2/frontmatter-sweep.ts +91 -0
  448. package/src/memory/v2/injection.ts +440 -109
  449. package/src/memory/v2/migration.ts +117 -20
  450. package/src/memory/v2/page-index.ts +191 -0
  451. package/src/memory/v2/page-store.ts +3 -0
  452. package/src/memory/v2/prompts/consolidation.ts +9 -7
  453. package/src/memory/v2/prompts/router.ts +192 -0
  454. package/src/memory/v2/qdrant.ts +100 -87
  455. package/src/memory/v2/reranker.ts +14 -7
  456. package/src/memory/v2/router.ts +322 -0
  457. package/src/memory/v2/sim.ts +25 -12
  458. package/src/memory/v2/skill-store.ts +118 -29
  459. package/src/memory/v2/static-context.ts +16 -9
  460. package/src/memory/v2/sweep-job.ts +122 -96
  461. package/src/memory/v2/types.ts +10 -6
  462. package/src/memory/validation.ts +13 -0
  463. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +182 -0
  464. package/src/notifications/__tests__/home-feed-side-effect.test.ts +199 -0
  465. package/src/notifications/__tests__/signal-registry.test.ts +17 -0
  466. package/src/notifications/adapters/platform.ts +171 -0
  467. package/src/notifications/conversation-pairing.ts +2 -2
  468. package/src/notifications/copy-composer.ts +15 -0
  469. package/src/notifications/destination-resolver.ts +21 -0
  470. package/src/notifications/emit-signal.ts +28 -1
  471. package/src/notifications/home-feed-side-effect.ts +111 -0
  472. package/src/notifications/signal.ts +5 -0
  473. package/src/permissions/checker.ts +12 -0
  474. package/src/permissions/ipc-risk-types.ts +2 -0
  475. package/src/plugin-api/index.ts +13 -0
  476. package/src/plugin-api/package.json +12 -0
  477. package/src/plugin-api/types.ts +62 -0
  478. package/src/plugins/defaults/injectors.ts +19 -3
  479. package/src/plugins/external-plugin-loader.ts +294 -0
  480. package/src/plugins/types.ts +46 -30
  481. package/src/plugins/user-loader.ts +64 -41
  482. package/src/proactive-artifact/job.test.ts +12 -4
  483. package/src/proactive-artifact/job.ts +4 -0
  484. package/src/proactive-artifact/trigger-state.test.ts +9 -0
  485. package/src/proactive-artifact/trigger-state.ts +4 -0
  486. package/src/prompts/__tests__/system-prompt.test.ts +105 -0
  487. package/src/prompts/system-prompt.ts +22 -1
  488. package/src/prompts/update-bulletin-job.ts +61 -73
  489. package/src/providers/__tests__/dispatch-connection-routing.test.ts +279 -0
  490. package/src/providers/__tests__/inference.test.ts +288 -0
  491. package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
  492. package/src/providers/__tests__/provider-secret-catalog.test.ts +6 -0
  493. package/src/providers/__tests__/retry-callsite.test.ts +14 -32
  494. package/src/providers/__tests__/satellite-connection-routing.test.ts +510 -0
  495. package/src/providers/__tests__/search-provider-catalog.test.ts +80 -0
  496. package/src/providers/anthropic/client.ts +95 -26
  497. package/src/providers/call-site-routing.ts +94 -16
  498. package/src/providers/connection-resolution.ts +163 -0
  499. package/src/providers/inference/__tests__/connections-status-label.test.ts +250 -0
  500. package/src/providers/inference/adapter-factory.ts +173 -0
  501. package/src/providers/inference/auth.ts +112 -0
  502. package/src/providers/inference/backfill.ts +196 -0
  503. package/src/providers/inference/connections.ts +356 -0
  504. package/src/providers/inference/resolve-auth.ts +65 -0
  505. package/src/providers/model-catalog.ts +104 -6
  506. package/src/providers/openai/responses-provider.ts +4 -2
  507. package/src/providers/provider-env-vars.ts +17 -7
  508. package/src/providers/provider-secret-catalog.ts +49 -30
  509. package/src/providers/provider-send-message.ts +41 -20
  510. package/src/providers/registry.ts +143 -159
  511. package/src/providers/retry.ts +18 -10
  512. package/src/providers/search-provider-catalog.ts +121 -0
  513. package/src/runtime/AGENTS.md +18 -5
  514. package/src/runtime/__tests__/background-job-runner.test.ts +357 -0
  515. package/src/runtime/__tests__/pre-first-message-gate.test.ts +82 -0
  516. package/src/runtime/actor-trust-resolver.ts +32 -10
  517. package/src/runtime/agent-wake.ts +35 -6
  518. package/src/runtime/assistant-event-hub.ts +3 -85
  519. package/src/runtime/auth/route-policy.ts +303 -8
  520. package/src/runtime/auth/same-actor.ts +2 -0
  521. package/src/runtime/background-job-runner.ts +339 -0
  522. package/src/runtime/btw-sidechain.ts +1 -0
  523. package/src/runtime/http-router.ts +36 -1
  524. package/src/runtime/http-server.ts +31 -5
  525. package/src/runtime/http-types.ts +2 -0
  526. package/src/runtime/middleware/__tests__/request-logger.test.ts +162 -0
  527. package/src/runtime/middleware/request-logger.ts +62 -1
  528. package/src/runtime/pre-first-message-gate.ts +83 -0
  529. package/src/runtime/routes/__tests__/backup-routes.test.ts +8 -1
  530. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +251 -0
  531. package/src/runtime/routes/__tests__/connection-routes-vs-cli-parity.test.ts +142 -0
  532. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +315 -0
  533. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +189 -0
  534. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +15 -136
  535. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +736 -0
  536. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +4 -4
  537. package/src/runtime/routes/__tests__/stt-routes.test.ts +5 -1
  538. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +384 -0
  539. package/src/runtime/routes/__tests__/tts-routes.test.ts +6 -2
  540. package/src/runtime/routes/acp-routes.ts +10 -8
  541. package/src/runtime/routes/app-management-routes.ts +228 -3
  542. package/src/runtime/routes/approval-routes.ts +0 -18
  543. package/src/runtime/routes/audit-routes.ts +43 -0
  544. package/src/runtime/routes/auth-routes.ts +72 -0
  545. package/src/runtime/routes/avatar-routes.ts +273 -20
  546. package/src/runtime/routes/backup-routes.ts +406 -2
  547. package/src/runtime/routes/bookmark-routes.ts +154 -0
  548. package/src/runtime/routes/channel-verification-routes.ts +2 -1
  549. package/src/runtime/routes/contact-routes.ts +0 -160
  550. package/src/runtime/routes/conversation-cli-routes.ts +192 -0
  551. package/src/runtime/routes/conversation-management-routes.ts +30 -43
  552. package/src/runtime/routes/conversation-query-routes.ts +334 -86
  553. package/src/runtime/routes/conversation-routes.ts +31 -10
  554. package/src/runtime/routes/conversations-import-routes.ts +229 -0
  555. package/src/runtime/routes/credential-routes.ts +540 -0
  556. package/src/runtime/routes/debug-routes.ts +2 -2
  557. package/src/runtime/routes/document-pdf-renderer.ts +5 -1
  558. package/src/runtime/routes/domain-routes.ts +167 -0
  559. package/src/runtime/routes/email-routes.ts +603 -0
  560. package/src/runtime/routes/errors.ts +2 -2
  561. package/src/runtime/routes/events-routes.ts +192 -0
  562. package/src/runtime/routes/home-feed-routes.ts +6 -78
  563. package/src/runtime/routes/host-app-control-routes.ts +44 -2
  564. package/src/runtime/routes/host-browser-routes.ts +103 -22
  565. package/src/runtime/routes/http-adapter.ts +2 -0
  566. package/src/runtime/routes/identity-routes.ts +5 -0
  567. package/src/runtime/routes/image-generation-routes.ts +99 -0
  568. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +137 -1
  569. package/src/runtime/routes/inbound-stages/background-dispatch.ts +87 -7
  570. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +156 -0
  571. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +22 -4
  572. package/src/runtime/routes/index.ts +36 -0
  573. package/src/runtime/routes/inference-profile-session-handler.ts +312 -0
  574. package/src/runtime/routes/inference-profile-session-reaper.ts +98 -0
  575. package/src/runtime/routes/inference-profile-session-routes.ts +146 -0
  576. package/src/runtime/routes/inference-provider-connection-routes.ts +317 -0
  577. package/src/runtime/routes/inference-send-routes.ts +115 -0
  578. package/src/runtime/routes/integrations/twilio.ts +1 -0
  579. package/src/runtime/routes/mcp-auth-routes.ts +283 -9
  580. package/src/runtime/routes/memory-v2-routes.ts +13 -398
  581. package/src/runtime/routes/notification-routes.ts +2 -0
  582. package/src/runtime/routes/oauth-apps.ts +112 -7
  583. package/src/runtime/routes/oauth-commands-routes.ts +1007 -0
  584. package/src/runtime/routes/oauth-connect-routes.ts +67 -5
  585. package/src/runtime/routes/oauth-providers.ts +298 -8
  586. package/src/runtime/routes/platform-routes.ts +336 -0
  587. package/src/runtime/routes/playground/inject-failures.ts +2 -1
  588. package/src/runtime/routes/playground/reset-circuit.ts +2 -1
  589. package/src/runtime/routes/playground/state.ts +2 -1
  590. package/src/runtime/routes/publish-routes.ts +221 -0
  591. package/src/runtime/routes/schedule-routes.ts +82 -0
  592. package/src/runtime/routes/sequence-routes.ts +291 -0
  593. package/src/runtime/routes/settings-routes.ts +2 -10
  594. package/src/runtime/routes/skills-routes.ts +31 -1
  595. package/src/runtime/routes/stt-routes.ts +240 -3
  596. package/src/runtime/routes/surface-action-routes.ts +43 -7
  597. package/src/runtime/routes/tts-routes.ts +67 -0
  598. package/src/runtime/routes/types.ts +32 -0
  599. package/src/runtime/routes/user-routes-cli.ts +243 -0
  600. package/src/runtime/routes/webhook-routes.ts +165 -0
  601. package/src/runtime/sync/resource-sync-events.ts +25 -0
  602. package/src/runtime/sync/sync-publisher.test.ts +105 -0
  603. package/src/runtime/sync/sync-publisher.ts +21 -0
  604. package/src/schedule/scheduler.ts +200 -123
  605. package/src/security/__tests__/provider-key-env-fallback.test.ts +12 -6
  606. package/src/security/secret-patterns.ts +3 -0
  607. package/src/sequence/engine.ts +38 -40
  608. package/src/subagent/manager.ts +20 -15
  609. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +206 -0
  610. package/src/tools/browser/browser-execution.ts +15 -4
  611. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +174 -0
  612. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +16 -13
  613. package/src/tools/browser/cdp-client/extension-cdp-client.ts +24 -1
  614. package/src/tools/browser/cdp-client/factory.ts +66 -5
  615. package/src/tools/browser/runtime-check.ts +77 -0
  616. package/src/tools/memory/register.test.ts +3 -3
  617. package/src/tools/memory/register.ts +9 -1
  618. package/src/tools/network/__tests__/web-search.test.ts +156 -0
  619. package/src/tools/network/web-search.ts +280 -37
  620. package/src/tools/permission-checker.ts +13 -5
  621. package/src/tools/subagent/spawn.ts +3 -3
  622. package/src/tools/terminal/shell.ts +44 -0
  623. package/src/usage/attribution.ts +3 -2
  624. package/src/util/pricing.ts +86 -160
  625. package/src/watcher/__tests__/engine.test.ts +301 -0
  626. package/src/watcher/constants.ts +7 -0
  627. package/src/watcher/engine.ts +90 -90
  628. package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +6 -9
  629. package/src/workspace/migrations/054-seed-recall-callsite.ts +10 -1
  630. package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +28 -4
  631. package/src/workspace/migrations/069-seed-onboarding-threads.ts +8 -2
  632. package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +104 -0
  633. package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +93 -0
  634. package/src/workspace/migrations/074-drop-deprecated-secret-detection-keys.ts +117 -0
  635. package/src/workspace/migrations/075-memory-v2-bm25-b-default-reembed.ts +61 -0
  636. package/src/workspace/migrations/076-drop-services-inference-mode.ts +62 -0
  637. package/src/workspace/migrations/077-seed-memory-router-callsite.ts +89 -0
  638. package/src/workspace/migrations/078-release-notes-tavily-web-search.ts +66 -0
  639. package/src/workspace/migrations/079-home-feed-notification-only.ts +197 -0
  640. package/src/workspace/migrations/080-restrict-vercel-api-token-metadata.ts +182 -0
  641. package/src/workspace/migrations/081-backfill-bash-allowed-tools-for-injection-credentials.ts +160 -0
  642. package/src/workspace/migrations/082-backfill-managed-profile-labels.ts +154 -0
  643. package/src/workspace/migrations/registry.ts +22 -0
  644. package/src/workspace/migrations/runner.ts +13 -2
  645. package/src/workspace/migrations/types.ts +13 -3
  646. package/src/workspace/provider-commit-message-generator.ts +3 -2
  647. package/src/__tests__/context-search-pkb-source.test.ts +0 -498
  648. package/src/__tests__/credentials-cli.test.ts +0 -1225
  649. package/src/__tests__/memory-admin-recall.test.ts +0 -213
  650. package/src/approvals/__tests__/guardian-feed-event.test.ts +0 -303
  651. package/src/cli/commands/__tests__/email-download.test.ts +0 -260
  652. package/src/cli/commands/__tests__/email-list.test.ts +0 -216
  653. package/src/cli/commands/__tests__/email-register.test.ts +0 -186
  654. package/src/cli/commands/__tests__/email-send.test.ts +0 -416
  655. package/src/cli/commands/__tests__/email-status.test.ts +0 -185
  656. package/src/cli/commands/__tests__/email-unregister.test.ts +0 -168
  657. package/src/cli/commands/__tests__/routes.test.ts +0 -562
  658. package/src/cli/commands/__tests__/stt-transcribe.test.ts +0 -454
  659. package/src/cli/commands/autonomy.ts +0 -365
  660. package/src/cli/commands/memory.ts +0 -424
  661. package/src/cli/commands/oauth/__tests__/connect.test.ts +0 -947
  662. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +0 -686
  663. package/src/cli/commands/oauth/__tests__/mode.test.ts +0 -632
  664. package/src/cli/commands/oauth/__tests__/ping.test.ts +0 -631
  665. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +0 -573
  666. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +0 -330
  667. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +0 -521
  668. package/src/cli/commands/oauth/__tests__/status.test.ts +0 -551
  669. package/src/cli/commands/oauth/__tests__/token.test.ts +0 -420
  670. package/src/cli/lib/daemon-avatar-client.ts +0 -37
  671. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -87
  672. package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +0 -207
  673. package/src/daemon/__tests__/conversation-feed-event.test.ts +0 -304
  674. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +0 -233
  675. package/src/home/__tests__/assistant-feed-authoring.test.ts +0 -156
  676. package/src/home/__tests__/emit-feed-event.test.ts +0 -169
  677. package/src/home/__tests__/feed-population-integration.test.ts +0 -312
  678. package/src/home/__tests__/feed-scheduler.test.ts +0 -222
  679. package/src/home/__tests__/phase5-exit-criteria.test.ts +0 -229
  680. package/src/home/__tests__/platform-gmail-digest.test.ts +0 -222
  681. package/src/home/__tests__/rollup-producer.test.ts +0 -507
  682. package/src/home/assistant-feed-authoring.ts +0 -135
  683. package/src/home/emit-feed-event.ts +0 -169
  684. package/src/home/feed-scheduler.ts +0 -281
  685. package/src/home/platform-gmail-digest.ts +0 -163
  686. package/src/home/rewrite-command-preview.ts +0 -66
  687. package/src/home/rewrite-feed-title.ts +0 -58
  688. package/src/home/rollup-producer.ts +0 -426
  689. package/src/memory/admin.ts +0 -326
  690. package/src/memory/context-search/sources/pkb.ts +0 -476
  691. package/src/memory/graph/compaction.ts +0 -299
  692. /package/src/cli/{commands → lib}/cache-fs.ts +0 -0
@@ -8,6 +8,11 @@
8
8
  * without this wrapper a memoryRetrieval call configured to run on OpenAI
9
9
  * but originating from an Anthropic-default conversation would still hit
10
10
  * the Anthropic transport.
11
+ *
12
+ * Alternate-provider routing requires a `provider_connection`, and the
13
+ * wrapper takes a single async hook
14
+ * `(connectionName, expectedProvider) => Promise<Provider | null>`.
15
+ * Call-site profiles without a connection throw `ConnectionResolutionError`.
11
16
  */
12
17
 
13
18
  import { beforeEach, describe, expect, mock, test } from "bun:test";
@@ -26,6 +31,7 @@ mock.module("../config/loader.js", () => ({
26
31
 
27
32
  import { LLMSchema } from "../config/schemas/llm.js";
28
33
  import { CallSiteRoutingProvider } from "../providers/call-site-routing.js";
34
+ import { ConnectionResolutionError } from "../providers/connection-resolution.js";
29
35
  import type {
30
36
  Message,
31
37
  Provider,
@@ -63,6 +69,21 @@ function setLlmConfig(raw: unknown): void {
63
69
  mockLlmConfig = LLMSchema.parse(raw) as Record<string, unknown>;
64
70
  }
65
71
 
72
+ /**
73
+ * Build a connection-resolution hook keyed by connection name. Returns the
74
+ * matching Provider (or null if not registered). Async to satisfy the new
75
+ * `resolveByConnection` signature.
76
+ */
77
+ function makeConnectionHook(
78
+ byConnection: Record<string, Provider>,
79
+ ): (
80
+ connectionName: string,
81
+ expectedProvider: string,
82
+ ) => Promise<Provider | null> {
83
+ return async (connectionName, _expectedProvider) =>
84
+ byConnection[connectionName] ?? null;
85
+ }
86
+
66
87
  beforeEach(() => {
67
88
  mockLlmConfig = LLMSchema.parse({}) as Record<string, unknown>;
68
89
  });
@@ -71,8 +92,15 @@ describe("CallSiteRoutingProvider", () => {
71
92
  test("routes to default provider when callSite is absent", async () => {
72
93
  setLlmConfig({
73
94
  default: { provider: "anthropic", model: "claude-opus-4-7" },
95
+ profiles: {
96
+ altOpenai: {
97
+ provider: "openai",
98
+ provider_connection: "openai-conn",
99
+ model: "gpt-5.4",
100
+ },
101
+ },
74
102
  callSites: {
75
- memoryRetrieval: { provider: "openai", model: "gpt-5.4" },
103
+ memoryRetrieval: { profile: "altOpenai" },
76
104
  },
77
105
  });
78
106
 
@@ -84,8 +112,9 @@ describe("CallSiteRoutingProvider", () => {
84
112
  calls.alt++;
85
113
  });
86
114
 
87
- const wrapped = new CallSiteRoutingProvider(defaultProvider, (name) =>
88
- name === "openai" ? altProvider : undefined,
115
+ const wrapped = new CallSiteRoutingProvider(
116
+ defaultProvider,
117
+ makeConnectionHook({ "openai-conn": altProvider }),
89
118
  );
90
119
 
91
120
  const response = await wrapped.sendMessage(
@@ -102,11 +131,12 @@ describe("CallSiteRoutingProvider", () => {
102
131
  expect(response.model).toBe("anthropic");
103
132
  });
104
133
 
105
- test("routes to default provider when callSite resolves to same provider name", async () => {
134
+ test("routes to default provider when callSite resolves to same provider name (no connection)", async () => {
106
135
  setLlmConfig({
107
136
  default: { provider: "anthropic", model: "claude-opus-4-7" },
108
137
  callSites: {
109
- // Same provider as default — no transport swap needed.
138
+ // Same provider as default, no connection should reuse default
139
+ // without doing any connection resolution work.
110
140
  memoryRetrieval: { model: "claude-haiku-4-5-20251001" },
111
141
  },
112
142
  });
@@ -119,8 +149,9 @@ describe("CallSiteRoutingProvider", () => {
119
149
  calls.alt++;
120
150
  });
121
151
 
122
- const wrapped = new CallSiteRoutingProvider(defaultProvider, (name) =>
123
- name === "openai" ? altProvider : undefined,
152
+ const wrapped = new CallSiteRoutingProvider(
153
+ defaultProvider,
154
+ makeConnectionHook({ "openai-conn": altProvider }),
124
155
  );
125
156
 
126
157
  await wrapped.sendMessage(DUMMY_MESSAGES, undefined, undefined, {
@@ -131,11 +162,18 @@ describe("CallSiteRoutingProvider", () => {
131
162
  expect(calls.alt).toBe(0);
132
163
  });
133
164
 
134
- test("routes to alternative provider when callSite resolves to a different provider name", async () => {
165
+ test("routes to alternative provider when callSite resolves to a profile with provider_connection", async () => {
135
166
  setLlmConfig({
136
167
  default: { provider: "anthropic", model: "claude-opus-4-7" },
168
+ profiles: {
169
+ altOpenai: {
170
+ provider: "openai",
171
+ provider_connection: "openai-conn",
172
+ model: "gpt-5.4",
173
+ },
174
+ },
137
175
  callSites: {
138
- memoryRetrieval: { provider: "openai", model: "gpt-5.4" },
176
+ memoryRetrieval: { profile: "altOpenai" },
139
177
  },
140
178
  });
141
179
 
@@ -147,8 +185,9 @@ describe("CallSiteRoutingProvider", () => {
147
185
  calls.alt++;
148
186
  });
149
187
 
150
- const wrapped = new CallSiteRoutingProvider(defaultProvider, (name) =>
151
- name === "openai" ? altProvider : undefined,
188
+ const wrapped = new CallSiteRoutingProvider(
189
+ defaultProvider,
190
+ makeConnectionHook({ "openai-conn": altProvider }),
152
191
  );
153
192
 
154
193
  const response = await wrapped.sendMessage(
@@ -163,11 +202,18 @@ describe("CallSiteRoutingProvider", () => {
163
202
  expect(response.model).toBe("openai");
164
203
  });
165
204
 
166
- test("falls back to default when alternative provider is not registered", async () => {
205
+ test("falls back to default when connection resolves to null (soft credential failure)", async () => {
167
206
  setLlmConfig({
168
207
  default: { provider: "anthropic", model: "claude-opus-4-7" },
208
+ profiles: {
209
+ altOpenai: {
210
+ provider: "openai",
211
+ provider_connection: "openai-conn",
212
+ model: "gpt-5.4",
213
+ },
214
+ },
169
215
  callSites: {
170
- memoryRetrieval: { provider: "openai", model: "gpt-5.4" },
216
+ memoryRetrieval: { profile: "altOpenai" },
171
217
  },
172
218
  });
173
219
 
@@ -176,11 +222,11 @@ describe("CallSiteRoutingProvider", () => {
176
222
  calls.default++;
177
223
  });
178
224
 
179
- // Lookup always returns undefinedsimulating a missing/uninitialized
180
- // provider in the registry.
225
+ // Hook always returns nullsimulates a credential miss / transient
226
+ // auth failure inside the resolver.
181
227
  const wrapped = new CallSiteRoutingProvider(
182
228
  defaultProvider,
183
- () => undefined,
229
+ async () => null,
184
230
  );
185
231
 
186
232
  const response = await wrapped.sendMessage(
@@ -194,19 +240,65 @@ describe("CallSiteRoutingProvider", () => {
194
240
  expect(response.model).toBe("anthropic");
195
241
  });
196
242
 
243
+ test("alternate-provider profile WITHOUT a connection throws ConnectionResolutionError", async () => {
244
+ setLlmConfig({
245
+ default: { provider: "anthropic", model: "claude-opus-4-7" },
246
+ profiles: {
247
+ legacyOpenai: {
248
+ provider: "openai",
249
+ // No provider_connection — alternate-provider routing requires
250
+ // one, so this profile is expected to throw
251
+ // `ConnectionResolutionError(missing_connection)` below.
252
+ },
253
+ },
254
+ callSites: {
255
+ memoryRetrieval: { profile: "legacyOpenai" },
256
+ },
257
+ });
258
+
259
+ const defaultProvider = makeProvider("anthropic", () => {});
260
+
261
+ const wrapped = new CallSiteRoutingProvider(
262
+ defaultProvider,
263
+ makeConnectionHook({}),
264
+ );
265
+
266
+ let caught: unknown;
267
+ try {
268
+ await wrapped.sendMessage(DUMMY_MESSAGES, undefined, undefined, {
269
+ config: { callSite: "memoryRetrieval" },
270
+ });
271
+ } catch (err) {
272
+ caught = err;
273
+ }
274
+
275
+ expect(caught).toBeInstanceOf(ConnectionResolutionError);
276
+ expect((caught as ConnectionResolutionError).reason).toBe(
277
+ "missing_connection",
278
+ );
279
+ });
280
+
197
281
  test("stamps actualProvider when routing to an alternative provider", async () => {
198
282
  setLlmConfig({
199
283
  default: { provider: "anthropic", model: "claude-opus-4-7" },
284
+ profiles: {
285
+ altOpenai: {
286
+ provider: "openai",
287
+ provider_connection: "openai-conn",
288
+ model: "gpt-5.5",
289
+ },
290
+ },
200
291
  callSites: {
201
- memoryRetrieval: { provider: "openai", model: "gpt-5.5" },
292
+ memoryRetrieval: { profile: "altOpenai" },
202
293
  },
203
294
  });
204
295
 
205
296
  const defaultProvider = makeProvider("anthropic", () => {});
206
297
  const altProvider = makeProvider("openai", () => {});
207
298
 
208
- const wrapped = new CallSiteRoutingProvider(defaultProvider, (name) =>
209
- name === "openai" ? altProvider : undefined,
299
+ const wrapped = new CallSiteRoutingProvider(
300
+ defaultProvider,
301
+ makeConnectionHook({ "openai-conn": altProvider }),
210
302
  );
211
303
 
212
304
  const response = await wrapped.sendMessage(
@@ -225,8 +317,15 @@ describe("CallSiteRoutingProvider", () => {
225
317
  test("does not overwrite actualProvider already set by the alternative provider", async () => {
226
318
  setLlmConfig({
227
319
  default: { provider: "anthropic", model: "claude-opus-4-7" },
320
+ profiles: {
321
+ altOpenai: {
322
+ provider: "openai",
323
+ provider_connection: "openai-conn",
324
+ model: "gpt-5.5",
325
+ },
326
+ },
228
327
  callSites: {
229
- memoryRetrieval: { provider: "openai", model: "gpt-5.5" },
328
+ memoryRetrieval: { profile: "altOpenai" },
230
329
  },
231
330
  });
232
331
 
@@ -242,8 +341,9 @@ describe("CallSiteRoutingProvider", () => {
242
341
  },
243
342
  };
244
343
 
245
- const wrapped = new CallSiteRoutingProvider(defaultProvider, (name) =>
246
- name === "openai" ? altProvider : undefined,
344
+ const wrapped = new CallSiteRoutingProvider(
345
+ defaultProvider,
346
+ makeConnectionHook({ "openai-conn": altProvider }),
247
347
  );
248
348
 
249
349
  const response = await wrapped.sendMessage(
@@ -265,7 +365,7 @@ describe("CallSiteRoutingProvider", () => {
265
365
 
266
366
  const wrapped = new CallSiteRoutingProvider(
267
367
  defaultProvider,
268
- () => undefined,
368
+ makeConnectionHook({}),
269
369
  );
270
370
 
271
371
  const response = await wrapped.sendMessage(
@@ -290,7 +390,7 @@ describe("CallSiteRoutingProvider", () => {
290
390
 
291
391
  const wrapped = new CallSiteRoutingProvider(
292
392
  defaultProvider,
293
- () => undefined,
393
+ makeConnectionHook({}),
294
394
  );
295
395
 
296
396
  expect(wrapped.name).toBe("anthropic");
@@ -304,8 +404,15 @@ describe("CallSiteRoutingProvider", () => {
304
404
  // event says "LLM call to anthropic" even when the call went to openai.
305
405
  setLlmConfig({
306
406
  default: { provider: "anthropic", model: "claude-opus-4-7" },
407
+ profiles: {
408
+ altOpenai: {
409
+ provider: "openai",
410
+ provider_connection: "openai-conn",
411
+ model: "gpt-5.5",
412
+ },
413
+ },
307
414
  callSites: {
308
- memoryRetrieval: { provider: "openai", model: "gpt-5.5" },
415
+ memoryRetrieval: { profile: "altOpenai" },
309
416
  },
310
417
  });
311
418
 
@@ -321,8 +428,9 @@ describe("CallSiteRoutingProvider", () => {
321
428
  },
322
429
  };
323
430
 
324
- const wrapped = new CallSiteRoutingProvider(defaultProvider, (name) =>
325
- name === "openai" ? altProvider : undefined,
431
+ const wrapped = new CallSiteRoutingProvider(
432
+ defaultProvider,
433
+ makeConnectionHook({ "openai-conn": altProvider }),
326
434
  );
327
435
 
328
436
  expect(wrapped.name).toBe("anthropic"); // idle → default
@@ -339,9 +447,21 @@ describe("CallSiteRoutingProvider", () => {
339
447
  // other. AsyncLocalStorage gives each call its own async-context slot.
340
448
  setLlmConfig({
341
449
  default: { provider: "anthropic", model: "claude-opus-4-7" },
450
+ profiles: {
451
+ altOpenai: {
452
+ provider: "openai",
453
+ provider_connection: "openai-conn",
454
+ model: "gpt-5.5",
455
+ },
456
+ altFireworks: {
457
+ provider: "fireworks",
458
+ provider_connection: "fireworks-conn",
459
+ model: "qwen3-235b",
460
+ },
461
+ },
342
462
  callSites: {
343
- memoryRetrieval: { provider: "openai", model: "gpt-5.5" },
344
- conversationTitle: { provider: "fireworks", model: "qwen3-235b" },
463
+ memoryRetrieval: { profile: "altOpenai" },
464
+ conversationTitle: { profile: "altFireworks" },
345
465
  },
346
466
  });
347
467
 
@@ -349,18 +469,25 @@ describe("CallSiteRoutingProvider", () => {
349
469
  const nameSeenByOpenAI: string[] = [];
350
470
  const nameSeenByFireworks: string[] = [];
351
471
 
352
- // Shared resolve handles so we can interleave the two calls:
353
- // openAI starts fireworks starts openAI resolves → fireworks resolves
472
+ // Shared resolve handles, pre-bound via Deferred-style promises so they
473
+ // exist before either provider's sendMessage executes. The connection-
474
+ // aware dispatch path awaits an extra tick to resolve the connection
475
+ // before invoking sendMessage, so any late-binding via
476
+ // `new Promise((r) => { resolveOpenAI = r })` would race with the
477
+ // microtask queue — these handles must be assigned synchronously.
354
478
  let resolveOpenAI!: () => void;
355
479
  let resolveFireworks!: () => void;
480
+ const openAIBlocked = new Promise<void>((r) => {
481
+ resolveOpenAI = r;
482
+ });
483
+ const fireworksBlocked = new Promise<void>((r) => {
484
+ resolveFireworks = r;
485
+ });
356
486
 
357
487
  const openAIProvider: Provider = {
358
488
  name: "openai",
359
489
  async sendMessage() {
360
- // Yield so fireworks call can start before we complete.
361
- await new Promise<void>((r) => {
362
- resolveOpenAI = r;
363
- });
490
+ await openAIBlocked;
364
491
  nameSeenByOpenAI.push(wrapped.name);
365
492
  return makeResponse("openai");
366
493
  },
@@ -369,19 +496,19 @@ describe("CallSiteRoutingProvider", () => {
369
496
  const fireworksProvider: Provider = {
370
497
  name: "fireworks",
371
498
  async sendMessage() {
372
- await new Promise<void>((r) => {
373
- resolveFireworks = r;
374
- });
499
+ await fireworksBlocked;
375
500
  nameSeenByFireworks.push(wrapped.name);
376
501
  return makeResponse("fireworks");
377
502
  },
378
503
  };
379
504
 
380
- const wrapped = new CallSiteRoutingProvider(defaultProvider, (name) => {
381
- if (name === "openai") return openAIProvider;
382
- if (name === "fireworks") return fireworksProvider;
383
- return undefined;
384
- });
505
+ const wrapped = new CallSiteRoutingProvider(
506
+ defaultProvider,
507
+ makeConnectionHook({
508
+ "openai-conn": openAIProvider,
509
+ "fireworks-conn": fireworksProvider,
510
+ }),
511
+ );
385
512
 
386
513
  // Start both calls concurrently (do not await yet).
387
514
  const callA = wrapped.sendMessage(DUMMY_MESSAGES, undefined, undefined, {
@@ -391,8 +518,8 @@ describe("CallSiteRoutingProvider", () => {
391
518
  config: { callSite: "conversationTitle" }, // → fireworks
392
519
  });
393
520
 
394
- // Let both reach their suspension point, then resolve in order.
395
- await Promise.resolve(); // flush microtasks so both calls are in-flight
521
+ // Resolve both the awaits above guarantee these handles fire even if
522
+ // the providers' sendMessage hasn't executed yet.
396
523
  resolveOpenAI();
397
524
  resolveFireworks();
398
525
 
@@ -57,6 +57,7 @@ mock.module("../daemon/handlers/conversations.js", () => ({
57
57
  import { getOrCreateConversation } from "../memory/conversation-key-store.js";
58
58
  import { initializeDb } from "../memory/db-init.js";
59
59
  import { ROUTES } from "../runtime/routes/conversation-management-routes.js";
60
+ import { routeDefinitionsToHTTPRoutes } from "../runtime/routes/http-adapter.js";
60
61
 
61
62
  initializeDb();
62
63
 
@@ -73,27 +74,67 @@ describe("POST /v1/conversations/:id/cancel", () => {
73
74
 
74
75
  expect(internalId).not.toBe(conversationKey);
75
76
 
76
- cancelRoute.handler({
77
+ const result = cancelRoute.handler({
77
78
  pathParams: { id: conversationKey },
78
79
  body: {},
79
80
  headers: {},
80
-
81
81
  });
82
82
 
83
83
  expect(cancelledId!).toBe(internalId);
84
+ expect(result).toEqual({
85
+ ok: true,
86
+ cancelled: true,
87
+ conversationId: internalId,
88
+ });
84
89
  });
85
90
 
86
91
  test("falls back to raw ID when key is not in the mapping", () => {
87
92
  cancelledId = undefined;
88
93
  const directId = "direct-conversation-id";
89
94
 
90
- cancelRoute.handler({
95
+ const result = cancelRoute.handler({
91
96
  pathParams: { id: directId },
92
97
  body: {},
93
98
  headers: {},
99
+ });
94
100
 
101
+ expect(cancelledId!).toBe(directId);
102
+ expect(result).toEqual({
103
+ ok: true,
104
+ cancelled: true,
105
+ conversationId: directId,
95
106
  });
107
+ });
96
108
 
109
+ test("HTTP adapter returns a serializable 202 response", async () => {
110
+ cancelledId = undefined;
111
+ const directId = "direct-http-conversation-id";
112
+ const [httpRoute] = routeDefinitionsToHTTPRoutes([cancelRoute]);
113
+ const url = new URL(`http://localhost/v1/conversations/${directId}/cancel`);
114
+
115
+ const response = await httpRoute.handler({
116
+ req: new Request(url, {
117
+ method: "POST",
118
+ headers: { "content-type": "application/json" },
119
+ body: "{}",
120
+ }),
121
+ url,
122
+ params: { id: directId },
123
+ authContext: {} as never,
124
+ server: {} as never,
125
+ });
126
+
127
+ expect(response.status).toBe(202);
128
+ expect(await response.json()).toEqual({
129
+ ok: true,
130
+ cancelled: true,
131
+ conversationId: directId,
132
+ });
97
133
  expect(cancelledId!).toBe(directId);
98
134
  });
135
+
136
+ test("route definition advertises the cancellation response body", () => {
137
+ expect(cancelRoute.responseStatus).toBe("202");
138
+ expect(cancelRoute.responseBody).toBeDefined();
139
+ });
99
140
  });
@@ -71,6 +71,7 @@ describe("channel policy registry", () => {
71
71
  "start_new_conversation",
72
72
  "continue_existing_conversation",
73
73
  "not_deliverable",
74
+ "push_only",
74
75
  ]);
75
76
 
76
77
  for (const channelId of CHANNEL_IDS) {
@@ -101,6 +102,7 @@ describe("channel policy registry", () => {
101
102
  ["telegram", "continue_existing_conversation"],
102
103
  ["slack", "continue_existing_conversation"],
103
104
  ["phone", "not_deliverable"],
105
+ ["platform", "push_only"],
104
106
  ];
105
107
 
106
108
  for (const [channelId, expected] of expectedStrategies) {
@@ -113,6 +115,7 @@ describe("channel policy registry", () => {
113
115
  "start_new_conversation",
114
116
  "continue_existing_conversation",
115
117
  "not_deliverable",
118
+ "push_only",
116
119
  ]);
117
120
 
118
121
  for (const channelId of CHANNEL_IDS) {
@@ -148,4 +151,13 @@ describe("channel policy registry", () => {
148
151
  }
149
152
  }
150
153
  });
154
+
155
+ test("channels with push_only strategy have deliveryEnabled: true", () => {
156
+ for (const channelId of CHANNEL_IDS) {
157
+ const policy = getChannelPolicy(channelId);
158
+ if (policy.notification.conversationStrategy === "push_only") {
159
+ expect(policy.notification.deliveryEnabled).toBe(true);
160
+ }
161
+ }
162
+ });
151
163
  });
@@ -1597,6 +1597,95 @@ describe("bash network_mode=proxied — risk capped at medium", () => {
1597
1597
  });
1598
1598
  });
1599
1599
 
1600
+ describe("credentialed proxied bash — high risk escalation", () => {
1601
+ beforeEach(() => {
1602
+ mockRisk("low");
1603
+ mockIpcResponse("get_global_thresholds", DEFAULT_GATEWAY_THRESHOLDS);
1604
+ _clearGlobalCacheForTesting();
1605
+ clearRiskCache();
1606
+ testConfig.skills = { load: { extraDirs: [] } };
1607
+ });
1608
+
1609
+ test("proxied bash with credential_ids sends credentialRefCount in IPC params", async () => {
1610
+ mockRisk("high", {
1611
+ reason:
1612
+ "Proxied credential session — shell has access to injected credentials",
1613
+ });
1614
+ const result = await check(
1615
+ "bash",
1616
+ {
1617
+ command: "curl https://api.example.com",
1618
+ network_mode: "proxied",
1619
+ credential_ids: ["cred-abc-123"],
1620
+ },
1621
+ "/tmp",
1622
+ );
1623
+ expect(result.decision).toBe("prompt");
1624
+ expect(result.reason).toContain("credential");
1625
+ });
1626
+
1627
+ test("proxied bash with multiple credential_ids prompts with high risk", async () => {
1628
+ mockRisk("high", {
1629
+ reason:
1630
+ "Proxied credential session — shell has access to injected credentials",
1631
+ });
1632
+ const result = await check(
1633
+ "bash",
1634
+ {
1635
+ command: "ls",
1636
+ network_mode: "proxied",
1637
+ credential_ids: ["cred-1", "cred-2"],
1638
+ },
1639
+ "/tmp",
1640
+ );
1641
+ expect(result.decision).toBe("prompt");
1642
+ });
1643
+
1644
+ test("proxied bash with empty credential_ids array does not escalate risk", async () => {
1645
+ mockRisk("low");
1646
+ const result = await check(
1647
+ "bash",
1648
+ {
1649
+ command: "ls",
1650
+ network_mode: "proxied",
1651
+ credential_ids: [],
1652
+ },
1653
+ "/tmp",
1654
+ );
1655
+ // Empty array means no credential refs — follows normal proxied behavior
1656
+ expect(result.decision).toBe("allow");
1657
+ });
1658
+
1659
+ test("proxied bash with credential_ids containing empty strings does not escalate", async () => {
1660
+ mockRisk("low");
1661
+ const result = await check(
1662
+ "bash",
1663
+ {
1664
+ command: "ls",
1665
+ network_mode: "proxied",
1666
+ credential_ids: ["", ""],
1667
+ },
1668
+ "/tmp",
1669
+ );
1670
+ // Empty strings are filtered out, so no credential refs
1671
+ expect(result.decision).toBe("allow");
1672
+ });
1673
+
1674
+ test("non-proxied bash with credential_ids follows normal flow", async () => {
1675
+ mockRisk("low");
1676
+ const result = await check(
1677
+ "bash",
1678
+ {
1679
+ command: "ls",
1680
+ credential_ids: ["cred-abc-123"],
1681
+ },
1682
+ "/tmp",
1683
+ );
1684
+ // Without proxied mode, credential refs don't affect IPC classification
1685
+ expect(result.decision).toBe("allow");
1686
+ });
1687
+ });
1688
+
1600
1689
  describe("workspace mode — auto-allow workspace-scoped operations", () => {
1601
1690
  const workspaceDir = "/home/user/my-project";
1602
1691
 
@@ -61,14 +61,15 @@ const { RouteError } = await import("../runtime/routes/errors.js");
61
61
  // Helpers
62
62
  // ---------------------------------------------------------------------------
63
63
 
64
- function buildProgramWithStubParent(): Command {
64
+ function buildProgram(): Command {
65
65
  const program = new Command();
66
66
  program.exitOverride();
67
67
  program.configureOutput({
68
68
  writeErr: () => {},
69
69
  writeOut: () => {},
70
70
  });
71
- program.command("memory").description("Stub parent for tests");
71
+ // The registrar creates the `memory` parent itself, so callers don't
72
+ // need to stub one.
72
73
  registerMemoryV2Command(program);
73
74
  return program;
74
75
  }
@@ -82,7 +83,7 @@ async function runCommand(args: string[]): Promise<{ exitCode: number }> {
82
83
 
83
84
  process.exitCode = 0;
84
85
  try {
85
- const program = buildProgramWithStubParent();
86
+ const program = buildProgram();
86
87
  await program.parseAsync(["node", "assistant", ...args]);
87
88
  } catch {
88
89
  if (process.exitCode === 0) process.exitCode = 1;
@@ -198,15 +199,15 @@ describe("all memory v2 routes — MEMORY_V2_DISABLED gate", () => {
198
199
  // schema validation so any body would surface the gate error, but using
199
200
  // valid shapes keeps the assertion precise: we're confirming the gate
200
201
  // (not zod) is what blocks the call.
202
+ //
203
+ // NOTE: `memory_v2_validate` is intentionally absent — validate is a
204
+ // read-only diagnostic walk that must be runnable before the flag is
205
+ // flipped (see the dedicated coverage further down).
201
206
  const MINIMAL_BODIES: Record<string, Record<string, unknown>> = {
202
207
  memory_v2_backfill: { op: "migrate" },
203
- memory_v2_validate: {},
204
208
  memory_v2_get_concept_page: { slug: "any" },
205
209
  memory_v2_list_concept_pages: {},
206
- memory_v2_rebuild_corpus_stats: {},
207
- memory_v2_explain_similarity: { userText: "hello" },
208
210
  memory_v2_concept_frequency: {},
209
- memory_v2_fit_anisotropy: {},
210
211
  };
211
212
 
212
213
  const GATE_OFF_CASES = [
@@ -235,4 +236,31 @@ describe("all memory v2 routes — MEMORY_V2_DISABLED gate", () => {
235
236
  });
236
237
  }
237
238
  }
239
+
240
+ // memory_v2_validate is the one read-only diagnostic that must work
241
+ // before the operator flips memory.v2.enabled — the
242
+ // vellum-memory-v2-migration skill's pre-flip dry-run depends on it.
243
+ test("memory_v2_validate runs when memory.v2.enabled is false", async () => {
244
+ writeWorkspaceConfig({ memory: { v2: { enabled: false } } });
245
+ const route = memoryV2Routes.find(
246
+ (r) => r.operationId === "memory_v2_validate",
247
+ );
248
+ expect(route).toBeDefined();
249
+
250
+ const result = (await route!.handler({ body: {} })) as {
251
+ pageCount: number;
252
+ edgeCount: number;
253
+ missingEdgeEndpoints: unknown[];
254
+ oversizedPages: unknown[];
255
+ parseFailures: unknown[];
256
+ };
257
+
258
+ // Empty workspace → zero-violation report. The point is that the call
259
+ // returned at all (no MEMORY_V2_DISABLED throw).
260
+ expect(result.pageCount).toBe(0);
261
+ expect(result.edgeCount).toBe(0);
262
+ expect(result.missingEdgeEndpoints).toEqual([]);
263
+ expect(result.oversizedPages).toEqual([]);
264
+ expect(result.parseFailures).toEqual([]);
265
+ });
238
266
  });