@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
@@ -0,0 +1,476 @@
1
+ import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
2
+
3
+ import type { ServerMessage } from "../daemon/message-protocol.js";
4
+
5
+ const realEventHub = await import("../runtime/assistant-event-hub.js");
6
+
7
+ mock.module("../runtime/assistant-event-hub.js", () => ({
8
+ ...realEventHub,
9
+ broadcastMessage: (_msg: ServerMessage) => {},
10
+ }));
11
+
12
+ // Mock the persistence layer the surface helpers reach into so we can
13
+ // observe writes without touching SQLite. We swap this out per test by
14
+ // re-assigning the spies recorded on the closure below.
15
+ let getMessagesImpl: (conversationId: string) => Array<{
16
+ id: string;
17
+ conversationId: string;
18
+ role: string;
19
+ content: string;
20
+ createdAt: number;
21
+ metadata: string | null;
22
+ }> = () => [];
23
+ let updateMessageContentSpy: (id: string, content: string) => void = () => {};
24
+
25
+ const realCrud = await import("../memory/conversation-crud.js");
26
+
27
+ mock.module("../memory/conversation-crud.js", () => ({
28
+ ...realCrud,
29
+ getMessages: (conversationId: string) => getMessagesImpl(conversationId),
30
+ updateMessageContent: (id: string, content: string) =>
31
+ updateMessageContentSpy(id, content),
32
+ }));
33
+
34
+ // Imports must come AFTER mock.module so the surface module picks up
35
+ // the mocked persistence functions.
36
+ const {
37
+ cancelPendingSurfaceDataPersists,
38
+ flushPendingSurfaceDataPersists,
39
+ createSurfaceMutex,
40
+ flushSurfaceDataPersist,
41
+ handleSurfaceAction,
42
+ markSurfaceCompleted,
43
+ scheduleSurfaceDataPersist,
44
+ showStandaloneSurface,
45
+ surfaceProxyResolver,
46
+ } = await import("../daemon/conversation-surfaces.js");
47
+
48
+ import type { SurfaceConversationContext } from "../daemon/conversation-surfaces.js";
49
+ import type {
50
+ CardSurfaceData,
51
+ SurfaceData,
52
+ SurfaceType,
53
+ } from "../daemon/message-protocol.js";
54
+
55
+ function makeContext(sent: ServerMessage[] = []): SurfaceConversationContext {
56
+ return {
57
+ conversationId: "conv-persist-1",
58
+ traceEmitter: { emit: () => {} },
59
+ sendToClient: (msg) => sent.push(msg),
60
+ pendingSurfaceActions: new Map<string, { surfaceType: SurfaceType }>(),
61
+ lastSurfaceAction: new Map<
62
+ string,
63
+ { actionId: string; data?: Record<string, unknown> }
64
+ >(),
65
+ surfaceState: new Map<
66
+ string,
67
+ { surfaceType: SurfaceType; data: SurfaceData; title?: string }
68
+ >(),
69
+ surfaceUndoStacks: new Map<string, string[]>(),
70
+ accumulatedSurfaceState: new Map<string, Record<string, unknown>>(),
71
+ surfaceActionRequestIds: new Set<string>(),
72
+ currentTurnSurfaces: [],
73
+ pendingStandaloneSurfaces: new Map(),
74
+ recentlyCompletedStandaloneSurfaces: new Map(),
75
+ isProcessing: () => false,
76
+ enqueueMessage: () => ({ queued: false, requestId: "req-1" }),
77
+ getQueueDepth: () => 0,
78
+ processMessage: async () => "ok",
79
+ withSurface: createSurfaceMutex(),
80
+ };
81
+ }
82
+
83
+ function seedRows(rows: Array<{ id: string; content: unknown }>): void {
84
+ getMessagesImpl = () =>
85
+ rows.map((r) => ({
86
+ id: r.id,
87
+ conversationId: "conv-persist-1",
88
+ role: "assistant",
89
+ content:
90
+ typeof r.content === "string" ? r.content : JSON.stringify(r.content),
91
+ createdAt: 0,
92
+ metadata: null,
93
+ }));
94
+ }
95
+
96
+ describe("ui_surface_update persistence", () => {
97
+ let writes: Array<{ id: string; content: unknown }> = [];
98
+
99
+ beforeEach(() => {
100
+ writes = [];
101
+ updateMessageContentSpy = (id: string, content: string) => {
102
+ writes.push({ id, content: JSON.parse(content) });
103
+ };
104
+ getMessagesImpl = () => [];
105
+ // Make sure module-level pending timers from a previous test don't
106
+ // leak into this one.
107
+ cancelPendingSurfaceDataPersists();
108
+ });
109
+
110
+ afterEach(() => {
111
+ cancelPendingSurfaceDataPersists();
112
+ });
113
+
114
+ test("ui_update schedules a debounced DB write that lands within ~600ms", async () => {
115
+ const sent: ServerMessage[] = [];
116
+ const ctx = makeContext(sent);
117
+
118
+ // Seed an existing in-memory surface and a persisted message that
119
+ // contains the matching ui_surface block.
120
+ const surfaceId = "surface-debounced-1";
121
+ const initial: CardSurfaceData = {
122
+ title: "Health check",
123
+ body: "",
124
+ template: "task_progress",
125
+ templateData: { status: "in_progress", steps: [] },
126
+ };
127
+ ctx.surfaceState.set(surfaceId, { surfaceType: "card", data: initial });
128
+ seedRows([
129
+ {
130
+ id: "msg-1",
131
+ content: [
132
+ { type: "text", text: "running" },
133
+ {
134
+ type: "ui_surface",
135
+ surfaceId,
136
+ surfaceType: "card",
137
+ data: initial,
138
+ },
139
+ ],
140
+ },
141
+ ]);
142
+
143
+ const result = await surfaceProxyResolver(ctx, "ui_update", {
144
+ surface_id: surfaceId,
145
+ data: { templateData: { status: "completed" } },
146
+ });
147
+ expect(result.isError).toBe(false);
148
+
149
+ // Write must not have happened synchronously.
150
+ expect(writes).toHaveLength(0);
151
+
152
+ // After the debounce window the write lands.
153
+ await new Promise((r) => setTimeout(r, 600));
154
+ expect(writes).toHaveLength(1);
155
+ expect(writes[0].id).toBe("msg-1");
156
+ const persistedBlocks = writes[0].content as Array<Record<string, unknown>>;
157
+ const persistedSurface = persistedBlocks.find(
158
+ (b) => b.type === "ui_surface",
159
+ );
160
+ expect(persistedSurface).toBeDefined();
161
+ const persistedData = persistedSurface!.data as CardSurfaceData;
162
+ expect((persistedData.templateData as Record<string, unknown>).status).toBe(
163
+ "completed",
164
+ );
165
+ });
166
+
167
+ test("multiple rapid updates collapse to a single DB write", async () => {
168
+ const sent: ServerMessage[] = [];
169
+ const ctx = makeContext(sent);
170
+
171
+ const surfaceId = "surface-debounced-2";
172
+ const initial: CardSurfaceData = {
173
+ title: "Health check",
174
+ body: "",
175
+ template: "task_progress",
176
+ templateData: { status: "in_progress", steps: [] },
177
+ };
178
+ ctx.surfaceState.set(surfaceId, { surfaceType: "card", data: initial });
179
+ seedRows([
180
+ {
181
+ id: "msg-2",
182
+ content: [
183
+ {
184
+ type: "ui_surface",
185
+ surfaceId,
186
+ surfaceType: "card",
187
+ data: initial,
188
+ },
189
+ ],
190
+ },
191
+ ]);
192
+
193
+ for (const status of ["a", "b", "c", "d"]) {
194
+ // Patches arrive faster than the debounce window.
195
+ await surfaceProxyResolver(ctx, "ui_update", {
196
+ surface_id: surfaceId,
197
+ data: { templateData: { status } },
198
+ });
199
+ await new Promise((r) => setTimeout(r, 50));
200
+ }
201
+
202
+ // Wait past the debounce window after the LAST update.
203
+ await new Promise((r) => setTimeout(r, 600));
204
+
205
+ expect(writes).toHaveLength(1);
206
+ const persistedSurface = (
207
+ writes[0].content as Array<Record<string, unknown>>
208
+ ).find((b) => b.type === "ui_surface");
209
+ const persistedData = persistedSurface!.data as CardSurfaceData;
210
+ expect((persistedData.templateData as Record<string, unknown>).status).toBe(
211
+ "d",
212
+ );
213
+ });
214
+
215
+ test("markSurfaceCompleted force-flushes any pending debounced write", async () => {
216
+ const surfaceId = "surface-flush-1";
217
+ const initial: CardSurfaceData = {
218
+ title: "x",
219
+ body: "",
220
+ template: "task_progress",
221
+ templateData: { status: "in_progress" },
222
+ };
223
+ seedRows([
224
+ {
225
+ id: "msg-flush",
226
+ content: [
227
+ {
228
+ type: "ui_surface",
229
+ surfaceId,
230
+ surfaceType: "card",
231
+ data: initial,
232
+ },
233
+ ],
234
+ },
235
+ ]);
236
+
237
+ // Schedule a debounced persist directly.
238
+ scheduleSurfaceDataPersist("conv-persist-1", surfaceId, {
239
+ ...initial,
240
+ templateData: { status: "completed" },
241
+ } as SurfaceData);
242
+
243
+ expect(writes).toHaveLength(0);
244
+
245
+ // Calling markSurfaceCompleted should immediately flush the pending
246
+ // data persist AND apply its own completion patch — two writes
247
+ // against the same row, with the completion landing last.
248
+ markSurfaceCompleted({ conversationId: "conv-persist-1" }, surfaceId, "ok");
249
+
250
+ expect(writes.length).toBeGreaterThanOrEqual(2);
251
+ const finalBlocks = writes[writes.length - 1].content as Array<
252
+ Record<string, unknown>
253
+ >;
254
+ const finalSurface = finalBlocks.find((b) => b.type === "ui_surface")!;
255
+ expect(finalSurface.completed).toBe(true);
256
+ expect(finalSurface.completionSummary).toBe("ok");
257
+ });
258
+
259
+ test("flushSurfaceDataPersist fires the latest data immediately", () => {
260
+ const surfaceId = "surface-flush-2";
261
+ seedRows([
262
+ {
263
+ id: "msg-flush-2",
264
+ content: [
265
+ {
266
+ type: "ui_surface",
267
+ surfaceId,
268
+ surfaceType: "card",
269
+ data: { title: "x", body: "" },
270
+ },
271
+ ],
272
+ },
273
+ ]);
274
+ scheduleSurfaceDataPersist("conv-persist-1", surfaceId, {
275
+ title: "x",
276
+ body: "later",
277
+ } as SurfaceData);
278
+
279
+ expect(writes).toHaveLength(0);
280
+ flushSurfaceDataPersist(surfaceId);
281
+ expect(writes).toHaveLength(1);
282
+ const block = (writes[0].content as Array<Record<string, unknown>>).find(
283
+ (b) => b.type === "ui_surface",
284
+ );
285
+ expect((block!.data as Record<string, unknown>).body).toBe("later");
286
+ });
287
+
288
+ test("update arriving before the message is persisted is safely skipped", async () => {
289
+ const surfaceId = "surface-orphan-1";
290
+ // No rows seeded — simulates mid-stream before message_complete persists.
291
+ seedRows([]);
292
+
293
+ scheduleSurfaceDataPersist("conv-persist-1", surfaceId, {
294
+ title: "x",
295
+ body: "",
296
+ } as SurfaceData);
297
+
298
+ await new Promise((r) => setTimeout(r, 600));
299
+
300
+ // No write — and no crash.
301
+ expect(writes).toHaveLength(0);
302
+ });
303
+
304
+ test("cancelPendingSurfaceDataPersists clears scoped timers without firing", async () => {
305
+ const surfaceId = "surface-cancel-1";
306
+ seedRows([
307
+ {
308
+ id: "msg-cancel",
309
+ content: [
310
+ {
311
+ type: "ui_surface",
312
+ surfaceId,
313
+ surfaceType: "card",
314
+ data: { title: "x", body: "" },
315
+ },
316
+ ],
317
+ },
318
+ ]);
319
+
320
+ scheduleSurfaceDataPersist("conv-persist-1", surfaceId, {
321
+ title: "x",
322
+ body: "queued",
323
+ } as SurfaceData);
324
+
325
+ cancelPendingSurfaceDataPersists("conv-persist-1");
326
+ await new Promise((r) => setTimeout(r, 600));
327
+
328
+ expect(writes).toHaveLength(0);
329
+ });
330
+
331
+ test("flushPendingSurfaceDataPersists writes pending updates synchronously and clears timers", async () => {
332
+ const surfaceId = "surface-flush-pending-1";
333
+ seedRows([
334
+ {
335
+ id: "msg-flush-pending",
336
+ content: [
337
+ {
338
+ type: "ui_surface",
339
+ surfaceId,
340
+ surfaceType: "card",
341
+ data: { title: "x", body: "" },
342
+ },
343
+ ],
344
+ },
345
+ ]);
346
+
347
+ scheduleSurfaceDataPersist("conv-persist-1", surfaceId, {
348
+ title: "x",
349
+ body: "shutdown-flush",
350
+ } as SurfaceData);
351
+
352
+ // Write should not have fired yet (debounce hasn't elapsed).
353
+ expect(writes).toHaveLength(0);
354
+
355
+ flushPendingSurfaceDataPersists("conv-persist-1");
356
+
357
+ // Synchronous flush — write lands immediately with the latest data.
358
+ expect(writes).toHaveLength(1);
359
+ expect(writes[0].id).toBe("msg-flush-pending");
360
+ const block = (writes[0].content as Array<Record<string, unknown>>).find(
361
+ (b) => b.type === "ui_surface",
362
+ )!;
363
+ expect((block.data as Record<string, unknown>).body).toBe("shutdown-flush");
364
+
365
+ // Timer is cleared — waiting past the debounce window doesn't fire again.
366
+ await new Promise((r) => setTimeout(r, 600));
367
+ expect(writes).toHaveLength(1);
368
+ });
369
+
370
+ test("flushPendingSurfaceDataPersists scoped to one conversation leaves other conversations' timers alone", async () => {
371
+ const surfaceA = "surface-flush-scoped-a";
372
+ const surfaceB = "surface-flush-scoped-b";
373
+ seedRows([
374
+ {
375
+ id: "msg-scoped-a",
376
+ content: [
377
+ {
378
+ type: "ui_surface",
379
+ surfaceId: surfaceA,
380
+ surfaceType: "card",
381
+ data: { title: "x", body: "" },
382
+ },
383
+ ],
384
+ },
385
+ {
386
+ id: "msg-scoped-b",
387
+ content: [
388
+ {
389
+ type: "ui_surface",
390
+ surfaceId: surfaceB,
391
+ surfaceType: "card",
392
+ data: { title: "x", body: "" },
393
+ },
394
+ ],
395
+ },
396
+ ]);
397
+
398
+ scheduleSurfaceDataPersist("conv-persist-1", surfaceA, {
399
+ title: "x",
400
+ body: "a",
401
+ } as SurfaceData);
402
+ scheduleSurfaceDataPersist("conv-other", surfaceB, {
403
+ title: "x",
404
+ body: "b",
405
+ } as SurfaceData);
406
+
407
+ flushPendingSurfaceDataPersists("conv-persist-1");
408
+
409
+ // Only conv-persist-1's surface flushed; conv-other's still pending.
410
+ expect(writes).toHaveLength(1);
411
+ expect(writes[0].id).toBe("msg-scoped-a");
412
+
413
+ // Cleanup the other conversation's timer.
414
+ cancelPendingSurfaceDataPersists("conv-other");
415
+ });
416
+ });
417
+
418
+ describe("standalone surface DB persistence", () => {
419
+ let writes: Array<{ id: string; content: unknown }> = [];
420
+
421
+ beforeEach(() => {
422
+ writes = [];
423
+ updateMessageContentSpy = (id: string, content: string) => {
424
+ writes.push({ id, content: JSON.parse(content) });
425
+ };
426
+ getMessagesImpl = () => [];
427
+ cancelPendingSurfaceDataPersists();
428
+ });
429
+
430
+ afterEach(() => {
431
+ cancelPendingSurfaceDataPersists();
432
+ });
433
+
434
+ test("standalone surface action persists completed state to DB", async () => {
435
+ const ctx = makeContext();
436
+ const surfaceId = "standalone-persist-1";
437
+
438
+ seedRows([
439
+ {
440
+ id: "msg-standalone",
441
+ content: [
442
+ { type: "text", text: "confirm this" },
443
+ {
444
+ type: "ui_surface",
445
+ surfaceId,
446
+ surfaceType: "confirmation",
447
+ data: { message: "Proceed?" },
448
+ },
449
+ ],
450
+ },
451
+ ]);
452
+
453
+ const resultPromise = showStandaloneSurface(
454
+ ctx,
455
+ {
456
+ conversationId: "conv-persist-1",
457
+ surfaceType: "confirmation",
458
+ data: { message: "Proceed?" },
459
+ timeoutMs: 60_000,
460
+ },
461
+ surfaceId,
462
+ );
463
+
464
+ await handleSurfaceAction(ctx, surfaceId, "confirm", {});
465
+ const result = await resultPromise;
466
+ expect(result.status).toBe("submitted");
467
+
468
+ expect(writes.length).toBeGreaterThanOrEqual(1);
469
+ const finalBlocks = writes[writes.length - 1].content as Array<
470
+ Record<string, unknown>
471
+ >;
472
+ const surfaceBlock = finalBlocks.find((b) => b.type === "ui_surface")!;
473
+ expect(surfaceBlock.completed).toBe(true);
474
+ expect(surfaceBlock.completionSummary).toBe("Confirmed");
475
+ });
476
+ });
@@ -95,7 +95,6 @@ function makeCtx(overrides: Partial<ToolSetupContext> = {}): ToolSetupContext {
95
95
  getQueueDepth: () => 0,
96
96
  processMessage: async () => "",
97
97
  withSurface: async <T>(_id: string, fn: () => T | Promise<T>) => fn(),
98
- memoryPolicy: { scopeId: "default" },
99
98
  ...overrides,
100
99
  };
101
100
  }
@@ -265,6 +264,65 @@ describe("session-tool-setup app refresh side effects", () => {
265
264
  });
266
265
  });
267
266
 
267
+ test("canonicalizes create_app skill_execute alias before hooks run", async () => {
268
+ const ctx = makeCtx({ allowedToolNames: new Set(["app_create"]) });
269
+ const executor = makeFakeExecutor({
270
+ content: JSON.stringify({ id: "alias-app-1", name: "Alias App" }),
271
+ isError: false,
272
+ });
273
+
274
+ const toolFn = createToolExecutor(
275
+ executor as unknown as ToolExecutor,
276
+ noopPrompter,
277
+ noopSecretPrompter,
278
+ ctx,
279
+ noopLifecycleHandler,
280
+ );
281
+
282
+ await toolFn("skill_execute", {
283
+ tool: "create_app",
284
+ input: { name: "Alias App" },
285
+ activity: "Building app",
286
+ });
287
+
288
+ const calls = executor.execute.mock.calls as unknown[][];
289
+ expect(calls[0][0]).toBe("app_create");
290
+ expect(calls[0][1]).toEqual({ name: "Alias App" });
291
+ expect(broadcastSpy.mock.calls.length).toBeGreaterThanOrEqual(1);
292
+ expect((broadcastSpy.mock.calls as unknown[][])[0][0]).toEqual({
293
+ type: "app_files_changed",
294
+ appId: "alias-app-1",
295
+ });
296
+ });
297
+
298
+ test("preserves exact active create_app skill tool when app_create is also active", async () => {
299
+ const ctx = makeCtx({
300
+ allowedToolNames: new Set(["create_app", "app_create"]),
301
+ });
302
+ const executor = makeFakeExecutor({
303
+ content: JSON.stringify({ id: "custom-app-1", name: "Custom App" }),
304
+ isError: false,
305
+ });
306
+
307
+ const toolFn = createToolExecutor(
308
+ executor as unknown as ToolExecutor,
309
+ noopPrompter,
310
+ noopSecretPrompter,
311
+ ctx,
312
+ noopLifecycleHandler,
313
+ );
314
+
315
+ await toolFn("skill_execute", {
316
+ tool: "create_app",
317
+ input: { name: "Custom App" },
318
+ activity: "Running custom app tool",
319
+ });
320
+
321
+ const calls = executor.execute.mock.calls as unknown[][];
322
+ expect(calls[0][0]).toBe("create_app");
323
+ expect(broadcastSpy).not.toHaveBeenCalled();
324
+ });
325
+
268
326
  test("skips side effects when app_create result is an error", async () => {
269
327
  const ctx = makeCtx();
270
328
  const executor = makeFakeExecutor({ content: "Error", isError: true });
@@ -383,7 +441,7 @@ describe("session-tool-setup app refresh side effects", () => {
383
441
  // Simulate calling app_refresh by name (as the agent loop does)
384
442
  for (const toolName of ["app_refresh"]) {
385
443
  refreshSpy.mockClear();
386
- broadcastSpy.mockClear();
444
+ broadcastSpy.mockClear();
387
445
  broadcastSpy.mockClear();
388
446
  updatePublishedSpy.mockClear();
389
447
 
@@ -423,7 +481,7 @@ describe("session-tool-setup app refresh side effects", () => {
423
481
  "app_file_write",
424
482
  ]) {
425
483
  refreshSpy.mockClear();
426
- broadcastSpy.mockClear();
484
+ broadcastSpy.mockClear();
427
485
  broadcastSpy.mockClear();
428
486
  updatePublishedSpy.mockClear();
429
487
 
@@ -435,6 +493,4 @@ describe("session-tool-setup app refresh side effects", () => {
435
493
  }
436
494
  });
437
495
  });
438
-
439
-
440
496
  });
@@ -63,7 +63,7 @@ mock.module("../config/loader.js", () => ({
63
63
  pricingOverrides: [],
64
64
  },
65
65
  rateLimit: { maxRequestsPerMinute: 0 },
66
- memory: { enabled: false },
66
+ memory: { enabled: false, v2: { enabled: false } },
67
67
  daemon: {
68
68
  startupSocketWaitMs: 5000,
69
69
  stopTimeoutMs: 5000,
@@ -61,7 +61,7 @@ mock.module("../config/loader.js", () => ({
61
61
  pricingOverrides: [],
62
62
  },
63
63
  rateLimit: { maxRequestsPerMinute: 0 },
64
- memory: { enabled: false },
64
+ memory: { enabled: false, v2: { enabled: false } },
65
65
  daemon: {
66
66
  startupSocketWaitMs: 5000,
67
67
  stopTimeoutMs: 5000,
@@ -53,6 +53,13 @@ mock.module("../security/secure-keys.js", () => ({
53
53
  onCesClientChanged: () => ({ unsubscribe: () => {} }),
54
54
  setCesReconnect: () => {},
55
55
  getActiveBackendName: () => "file",
56
+ getActiveBackendInfoAsync: async () => ({
57
+ backend: "encrypted-store",
58
+ storePath: "/tmp/keys.enc",
59
+ storeKeyPath: "/tmp/store.key",
60
+ storeExists: false,
61
+ storeKeyExists: false,
62
+ }),
56
63
  _resetBackend: () => {},
57
64
  }));
58
65