@vellumai/assistant 0.7.1 → 0.7.2

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 (535) hide show
  1. package/ARCHITECTURE.md +32 -49
  2. package/Dockerfile +1 -0
  3. package/README.md +1 -2
  4. package/__tests__/permissions/gateway-threshold-reader.test.ts +9 -3
  5. package/bun.lock +26 -26
  6. package/docs/architecture/security.md +20 -0
  7. package/docs/plugins.md +7 -9
  8. package/knip.json +1 -0
  9. package/node_modules/@vellumai/gateway-client/src/index.ts +1 -0
  10. package/node_modules/@vellumai/gateway-client/src/ipc-client.ts +39 -1
  11. package/node_modules/@vellumai/gateway-client/src/types.ts +11 -0
  12. package/node_modules/@vellumai/service-contracts/package.json +2 -0
  13. package/node_modules/@vellumai/service-contracts/src/__tests__/contracts.test.ts +4 -0
  14. package/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +107 -0
  15. package/node_modules/@vellumai/service-contracts/src/index.ts +5 -1
  16. package/node_modules/@vellumai/service-contracts/src/ingress.ts +24 -0
  17. package/node_modules/@vellumai/service-contracts/src/twilio-ingress.ts +84 -0
  18. package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +9 -0
  19. package/node_modules/@vellumai/twilio-client/bun.lock +24 -0
  20. package/node_modules/@vellumai/twilio-client/package.json +18 -0
  21. package/node_modules/@vellumai/twilio-client/src/__tests__/twilio-client.test.ts +128 -0
  22. package/node_modules/@vellumai/twilio-client/src/index.ts +179 -0
  23. package/node_modules/@vellumai/twilio-client/tsconfig.json +20 -0
  24. package/openapi.yaml +565 -12
  25. package/package.json +6 -3
  26. package/src/__tests__/app-builder-tool-scripts.test.ts +3 -3
  27. package/src/__tests__/app-bundler.test.ts +170 -1
  28. package/src/__tests__/app-control-flow.test.ts +374 -0
  29. package/src/__tests__/app-control-no-global-cgevent.test.ts +98 -0
  30. package/src/__tests__/app-control-tool-schemas.test.ts +621 -0
  31. package/src/__tests__/app-executors.test.ts +30 -43
  32. package/src/__tests__/approval-routes-http.test.ts +23 -6
  33. package/src/__tests__/assistant-event-hub-machine-name.test.ts +146 -0
  34. package/src/__tests__/assistant-event-hub-targeted.test.ts +257 -0
  35. package/src/__tests__/assistant-event-hub.test.ts +109 -2
  36. package/src/__tests__/assistant-event.test.ts +10 -0
  37. package/src/__tests__/assistant-events-sse-hardening.test.ts +7 -2
  38. package/src/__tests__/assistant-feature-flags-integration.test.ts +11 -7
  39. package/src/__tests__/background-shell-host-bash.test.ts +14 -15
  40. package/src/__tests__/bootstrap-turn-cleanup.test.ts +44 -0
  41. package/src/__tests__/btw-routes.test.ts +13 -4
  42. package/src/__tests__/call-controller.test.ts +49 -1
  43. package/src/__tests__/call-domain.test.ts +0 -2
  44. package/src/__tests__/call-routes-http.test.ts +0 -2
  45. package/src/__tests__/channel-readiness-service.test.ts +59 -1
  46. package/src/__tests__/checker.test.ts +3 -4
  47. package/src/__tests__/config-loader-backfill.test.ts +90 -155
  48. package/src/__tests__/config-loader-platform-defaults.test.ts +196 -0
  49. package/src/__tests__/config-schema-cmd.test.ts +0 -1
  50. package/src/__tests__/config-set-platform-guard.test.ts +48 -4
  51. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +2 -2
  52. package/src/__tests__/config-watcher.test.ts +2 -2
  53. package/src/__tests__/conversation-app-control-instantiation.test.ts +392 -0
  54. package/src/__tests__/conversation-app-control-lifecycle.test.ts +237 -0
  55. package/src/__tests__/conversation-init.benchmark.test.ts +0 -2
  56. package/src/__tests__/conversation-lifecycle.test.ts +36 -0
  57. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +283 -0
  58. package/src/__tests__/conversation-routes-disk-view.test.ts +6 -0
  59. package/src/__tests__/conversation-routes-guardian-reply.test.ts +120 -72
  60. package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
  61. package/src/__tests__/conversation-slash-commands.test.ts +0 -4
  62. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +202 -0
  63. package/src/__tests__/conversation-surfaces-app-control.test.ts +317 -0
  64. package/src/__tests__/credential-execution-feature-gates.test.ts +5 -12
  65. package/src/__tests__/credential-execution-managed-contract.test.ts +3 -131
  66. package/src/__tests__/credentials-cli.test.ts +5 -12
  67. package/src/__tests__/cu-unified-flow.test.ts +185 -23
  68. package/src/__tests__/daemon-credential-client.test.ts +101 -19
  69. package/src/__tests__/db-schedule-syntax-migration.test.ts +2 -0
  70. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
  71. package/src/__tests__/gateway-only-enforcement.test.ts +0 -1
  72. package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -2
  73. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +0 -2
  74. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +0 -1
  75. package/src/__tests__/heartbeat-service.test.ts +718 -1
  76. package/src/__tests__/helpers/call-route-handler.ts +7 -1
  77. package/src/__tests__/host-app-control-proxy.test.ts +602 -0
  78. package/src/__tests__/host-app-control-routes.test.ts +263 -0
  79. package/src/__tests__/host-bash-proxy.test.ts +246 -47
  80. package/src/__tests__/host-bash-routes.test.ts +294 -0
  81. package/src/__tests__/host-browser-proxy.test.ts +24 -22
  82. package/src/__tests__/host-browser-routes.test.ts +39 -13
  83. package/src/__tests__/host-cu-proxy.test.ts +41 -52
  84. package/src/__tests__/host-cu-routes-targeted.test.ts +300 -0
  85. package/src/__tests__/host-file-edit-tool.test.ts +47 -1
  86. package/src/__tests__/host-file-proxy-targeted.test.ts +339 -0
  87. package/src/__tests__/host-file-proxy.test.ts +37 -43
  88. package/src/__tests__/host-file-read-tool.test.ts +17 -0
  89. package/src/__tests__/host-file-routes-targeted.test.ts +262 -0
  90. package/src/__tests__/host-file-write-tool.test.ts +42 -1
  91. package/src/__tests__/host-proxy-base.test.ts +312 -0
  92. package/src/__tests__/host-shell-tool.test.ts +22 -4
  93. package/src/__tests__/host-transfer-proxy-targeted.test.ts +583 -0
  94. package/src/__tests__/host-transfer-proxy.test.ts +121 -22
  95. package/src/__tests__/host-transfer-routes-targeted.test.ts +447 -0
  96. package/src/__tests__/http-user-message-parity.test.ts +1 -0
  97. package/src/__tests__/identity-intro-cache.test.ts +29 -0
  98. package/src/__tests__/identity-routes.test.ts +103 -1
  99. package/src/__tests__/init-feature-flag-overrides.test.ts +26 -3
  100. package/src/__tests__/inline-command-runner.test.ts +0 -1
  101. package/src/__tests__/inline-skill-load-permissions.test.ts +5 -11
  102. package/src/__tests__/integration-status.test.ts +85 -5
  103. package/src/__tests__/intent-routing.test.ts +0 -1
  104. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +95 -5
  105. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +17 -0
  106. package/src/__tests__/managed-skill-lifecycle.test.ts +0 -1
  107. package/src/__tests__/mcp-auth-routes.test.ts +197 -0
  108. package/src/__tests__/mcp-cli.test.ts +338 -2
  109. package/src/__tests__/memory-jobs-worker-lanes.test.ts +188 -0
  110. package/src/__tests__/migration-import-commit-http.test.ts +108 -2
  111. package/src/__tests__/mock-gateway-ipc.ts +1 -0
  112. package/src/__tests__/oauth-cli.test.ts +0 -2
  113. package/src/__tests__/oauth2-gateway-transport.test.ts +0 -1
  114. package/src/__tests__/persistence-secret-redaction.test.ts +299 -0
  115. package/src/__tests__/platform-bash-auto-approve.test.ts +5 -9
  116. package/src/__tests__/prechat-onboarding-contract.test.ts +3 -1
  117. package/src/__tests__/process-message-background-slack.test.ts +2 -0
  118. package/src/__tests__/provider-commit-message-generator.test.ts +0 -1
  119. package/src/__tests__/public-ingress-urls.test.ts +97 -0
  120. package/src/__tests__/require-fresh-approval.test.ts +0 -1
  121. package/src/__tests__/retry-backoff.test.ts +87 -0
  122. package/src/__tests__/runtime-events-sse.test.ts +10 -6
  123. package/src/__tests__/sanitize-config-for-transfer.test.ts +24 -2
  124. package/src/__tests__/schedule-retry.test.ts +715 -0
  125. package/src/__tests__/script-proxy-mitm-handler.test.ts +1 -1
  126. package/src/__tests__/secret-ingress-http.test.ts +1 -0
  127. package/src/__tests__/send-endpoint-busy.test.ts +3 -0
  128. package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -1
  129. package/src/__tests__/skill-feature-flags.test.ts +43 -41
  130. package/src/__tests__/skill-load-feature-flag.test.ts +13 -14
  131. package/src/__tests__/skill-load-inline-command.test.ts +0 -51
  132. package/src/__tests__/skill-load-inline-includes.test.ts +0 -43
  133. package/src/__tests__/skill-projection.benchmark.test.ts +0 -1
  134. package/src/__tests__/skill-script-runner-sandbox.test.ts +0 -1
  135. package/src/__tests__/slack-channel-config.test.ts +9 -14
  136. package/src/__tests__/system-prompt-ask-mode.test.ts +0 -1
  137. package/src/__tests__/system-prompt.test.ts +0 -1
  138. package/src/__tests__/telegram-config.test.ts +0 -1
  139. package/src/__tests__/test-preload.ts +8 -0
  140. package/src/__tests__/tool-approval-handler.test.ts +3 -4
  141. package/src/__tests__/tool-audit-listener.test.ts +48 -0
  142. package/src/__tests__/tool-execute-pipeline.test.ts +0 -1
  143. package/src/__tests__/tool-execution-abort-cleanup.test.ts +0 -1
  144. package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -1
  145. package/src/__tests__/tool-executor.test.ts +0 -1
  146. package/src/__tests__/twilio-config.test.ts +3 -16
  147. package/src/__tests__/twilio-routes.test.ts +3 -5
  148. package/src/__tests__/twilio-validation.test.ts +93 -0
  149. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +1 -4
  150. package/src/__tests__/verification-control-plane-policy.test.ts +2 -4
  151. package/src/__tests__/voice-ingress-preflight.test.ts +19 -0
  152. package/src/__tests__/workspace-migration-006-services-config.test.ts +3 -2
  153. package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +1 -5
  154. package/src/__tests__/workspace-migration-down-functions.test.ts +8 -8
  155. package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +10 -6
  156. package/src/backup/__tests__/paths.test.ts +0 -22
  157. package/src/backup/__tests__/restore.test.ts +51 -151
  158. package/src/backup/paths.ts +2 -18
  159. package/src/backup/restore.ts +107 -231
  160. package/src/bundler/app-bundler.ts +51 -3
  161. package/src/calls/relay-server.ts +4 -44
  162. package/src/calls/twilio-config.ts +2 -17
  163. package/src/calls/twilio-rest.ts +33 -105
  164. package/src/calls/twilio-routes.ts +11 -12
  165. package/src/channels/types.ts +8 -7
  166. package/src/cli/commands/__tests__/backup.test.ts +6 -277
  167. package/src/cli/commands/__tests__/gateway.test.ts +288 -0
  168. package/src/cli/commands/__tests__/memory-v2.test.ts +4 -0
  169. package/src/cli/commands/__tests__/webhooks.test.ts +0 -1
  170. package/src/cli/commands/backup.ts +6 -331
  171. package/src/cli/commands/clients.ts +36 -37
  172. package/src/cli/commands/contacts.ts +73 -0
  173. package/src/cli/commands/conversations.ts +2 -5
  174. package/src/cli/commands/credentials.ts +15 -7
  175. package/src/cli/commands/domain.ts +66 -15
  176. package/src/cli/commands/gateway.ts +183 -0
  177. package/src/cli/commands/keys.ts +9 -6
  178. package/src/cli/commands/mcp.ts +116 -156
  179. package/src/cli/commands/memory-v2.ts +296 -1
  180. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -1
  181. package/src/cli/commands/platform/__tests__/connect.test.ts +0 -2
  182. package/src/cli/commands/platform/__tests__/disconnect.test.ts +0 -2
  183. package/src/cli/commands/platform/__tests__/status.test.ts +13 -15
  184. package/src/cli/commands/platform/disconnect.ts +5 -4
  185. package/src/cli/commands/platform/index.ts +0 -18
  186. package/src/cli/lib/daemon-credential-client.ts +110 -28
  187. package/src/cli/program.ts +2 -0
  188. package/src/config/assistant-feature-flags.ts +67 -10
  189. package/src/config/bundled-skills/acp/SKILL.md +6 -0
  190. package/src/config/bundled-skills/acp/TOOLS.json +1 -22
  191. package/src/config/bundled-skills/app-builder/SKILL.md +14 -109
  192. package/src/config/bundled-skills/app-builder/TOOLS.json +1 -28
  193. package/src/config/bundled-skills/app-builder/tools/app-create.ts +1 -10
  194. package/src/config/bundled-skills/app-control/SKILL.md +75 -0
  195. package/src/config/bundled-skills/app-control/TOOLS.json +299 -0
  196. package/src/config/bundled-skills/app-control/tools/app-control-click.ts +12 -0
  197. package/src/config/bundled-skills/app-control/tools/app-control-combo.ts +12 -0
  198. package/src/config/bundled-skills/app-control/tools/app-control-drag.ts +12 -0
  199. package/src/config/bundled-skills/app-control/tools/app-control-observe.ts +12 -0
  200. package/src/config/bundled-skills/app-control/tools/app-control-press.ts +12 -0
  201. package/src/config/bundled-skills/app-control/tools/app-control-sequence.ts +12 -0
  202. package/src/config/bundled-skills/app-control/tools/app-control-start.ts +12 -0
  203. package/src/config/bundled-skills/app-control/tools/app-control-stop.ts +12 -0
  204. package/src/config/bundled-skills/app-control/tools/app-control-type.ts +12 -0
  205. package/src/config/bundled-skills/computer-use/SKILL.md +6 -0
  206. package/src/config/bundled-skills/computer-use/TOOLS.json +67 -43
  207. package/src/config/bundled-skills/contacts/TOOLS.json +0 -16
  208. package/src/config/bundled-skills/document/TOOLS.json +0 -8
  209. package/src/config/bundled-skills/followups/TOOLS.json +0 -12
  210. package/src/config/bundled-skills/image-studio/SKILL.md +4 -0
  211. package/src/config/bundled-skills/image-studio/TOOLS.json +0 -4
  212. package/src/config/bundled-skills/media-processing/TOOLS.json +0 -24
  213. package/src/config/bundled-skills/messaging/TOOLS.json +0 -40
  214. package/src/config/bundled-skills/phone-calls/TOOLS.json +0 -12
  215. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +19 -4
  216. package/src/config/bundled-skills/playbooks/TOOLS.json +0 -16
  217. package/src/config/bundled-skills/schedule/TOOLS.json +14 -14
  218. package/src/config/bundled-skills/sequences/TOOLS.json +0 -36
  219. package/src/config/bundled-skills/settings/SKILL.md +4 -0
  220. package/src/config/bundled-skills/settings/TOOLS.json +0 -12
  221. package/src/config/bundled-skills/skill-management/SKILL.md +6 -0
  222. package/src/config/bundled-skills/skill-management/TOOLS.json +0 -8
  223. package/src/config/bundled-skills/subagent/SKILL.md +6 -2
  224. package/src/config/bundled-skills/subagent/TOOLS.json +0 -20
  225. package/src/config/bundled-skills/transcribe/SKILL.md +4 -0
  226. package/src/config/bundled-skills/transcribe/TOOLS.json +0 -4
  227. package/src/config/bundled-tool-registry.ts +21 -0
  228. package/src/config/env-registry.ts +0 -2
  229. package/src/config/env.ts +19 -12
  230. package/src/config/feature-flag-registry.json +21 -133
  231. package/src/config/loader.ts +73 -99
  232. package/src/config/sanitize-for-transfer.ts +2 -0
  233. package/src/config/schemas/__tests__/memory-lifecycle.test.ts +80 -0
  234. package/src/config/schemas/__tests__/memory-v2.test.ts +7 -4
  235. package/src/config/schemas/calls.ts +0 -9
  236. package/src/config/schemas/heartbeat.ts +63 -0
  237. package/src/config/schemas/ingress.ts +10 -6
  238. package/src/config/schemas/llm.ts +5 -10
  239. package/src/config/schemas/memory-lifecycle.ts +77 -24
  240. package/src/config/schemas/memory-v2.ts +48 -4
  241. package/src/config/schemas/platform.ts +6 -0
  242. package/src/config/schemas/services.ts +1 -15
  243. package/src/config/schemas/skills.ts +0 -6
  244. package/src/config/seed-inference-profiles.ts +1 -1
  245. package/src/contacts/contact-store.ts +0 -30
  246. package/src/contacts/contacts-write.ts +0 -27
  247. package/src/context/window-manager.ts +1 -2
  248. package/src/credential-execution/feature-gates.ts +10 -10
  249. package/src/credential-execution/process-manager.ts +12 -41
  250. package/src/daemon/__tests__/conversation-tool-setup.test.ts +126 -5
  251. package/src/daemon/bootstrap-turn-cleanup.ts +45 -0
  252. package/src/daemon/config-watcher.ts +4 -3
  253. package/src/daemon/conversation-agent-loop-handlers.ts +21 -3
  254. package/src/daemon/conversation-agent-loop.ts +32 -28
  255. package/src/daemon/conversation-lifecycle.ts +8 -1
  256. package/src/daemon/conversation-process.ts +16 -11
  257. package/src/daemon/conversation-runtime-assembly.ts +2 -2
  258. package/src/daemon/conversation-surfaces.ts +125 -4
  259. package/src/daemon/conversation-tool-setup.ts +16 -55
  260. package/src/daemon/conversation.ts +21 -2
  261. package/src/daemon/doordash-steps.ts +1 -1
  262. package/src/daemon/handlers/shared.ts +4 -1
  263. package/src/daemon/host-app-control-proxy.ts +293 -0
  264. package/src/daemon/host-bash-proxy.ts +84 -74
  265. package/src/daemon/host-browser-proxy.ts +67 -82
  266. package/src/daemon/host-cu-proxy.ts +81 -86
  267. package/src/daemon/host-file-proxy.ts +93 -69
  268. package/src/daemon/host-proxy-base.ts +294 -0
  269. package/src/daemon/host-proxy-preactivation.ts +82 -0
  270. package/src/daemon/host-transfer-proxy.ts +247 -129
  271. package/src/daemon/lifecycle.ts +115 -117
  272. package/src/daemon/message-protocol.ts +3 -8
  273. package/src/daemon/message-types/contacts.ts +23 -1
  274. package/src/daemon/message-types/conversations.ts +11 -8
  275. package/src/daemon/message-types/host-app-control.ts +150 -0
  276. package/src/daemon/message-types/host-bash.ts +4 -0
  277. package/src/daemon/message-types/host-cu.ts +2 -0
  278. package/src/daemon/message-types/host-file.ts +4 -0
  279. package/src/daemon/message-types/host-transfer.ts +3 -0
  280. package/src/daemon/message-types/schedules.ts +8 -3
  281. package/src/daemon/message-types/skills.ts +2 -2
  282. package/src/daemon/process-message.ts +18 -1
  283. package/src/daemon/shutdown-handlers.ts +0 -3
  284. package/src/daemon/tool-setup-types.ts +51 -0
  285. package/src/daemon/tool-side-effects.ts +1 -1
  286. package/src/events/tool-audit-listener.ts +2 -1
  287. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +15 -7
  288. package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +216 -0
  289. package/src/heartbeat/heartbeat-run-store.ts +236 -0
  290. package/src/heartbeat/heartbeat-service.ts +280 -49
  291. package/src/home/__tests__/post-connect-feed.test.ts +99 -0
  292. package/src/home/__tests__/relationship-state-writer.test.ts +11 -9
  293. package/src/home/__tests__/suggested-prompts.test.ts +89 -0
  294. package/src/home/post-connect-feed.ts +68 -0
  295. package/src/home/relationship-state-writer.ts +17 -92
  296. package/src/home/suggested-prompts.ts +46 -10
  297. package/src/inbound/public-ingress-urls.ts +32 -34
  298. package/src/ipc/__tests__/route-error-envelope.test.ts +80 -0
  299. package/src/ipc/assistant-server.ts +14 -1
  300. package/src/ipc/cli-client.ts +32 -1
  301. package/src/live-voice/live-voice-metrics.ts +10 -10
  302. package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +304 -0
  303. package/src/mcp/mcp-auth-orchestrator.ts +213 -0
  304. package/src/mcp/mcp-auth-state.ts +133 -0
  305. package/src/mcp/mcp-oauth-provider.ts +19 -0
  306. package/src/memory/__tests__/jobs-store-job-classes.test.ts +24 -0
  307. package/src/memory/__tests__/qdrant-client-sentinel.test.ts +49 -0
  308. package/src/memory/__tests__/sparse-tokenize.test.ts +66 -0
  309. package/src/memory/anisotropy.test.ts +247 -0
  310. package/src/memory/anisotropy.ts +443 -0
  311. package/src/memory/auto-analysis-constants.ts +17 -0
  312. package/src/memory/auto-analysis-guard.ts +5 -15
  313. package/src/memory/canonical-guardian-store.ts +7 -7
  314. package/src/memory/context-search/__tests__/agent-runner-redaction.test.ts +122 -0
  315. package/src/memory/context-search/agent-protocol.ts +6 -6
  316. package/src/memory/context-search/agent-runner.ts +32 -7
  317. package/src/memory/context-search/sources/memory-v2.ts +17 -5
  318. package/src/memory/conversation-crud.ts +1 -1
  319. package/src/memory/conversation-key-store.ts +2 -15
  320. package/src/memory/db-init.ts +4 -0
  321. package/src/memory/embedding-backend.ts +9 -21
  322. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +49 -4
  323. package/src/memory/graph/conversation-graph-memory.ts +1 -24
  324. package/src/memory/graph/graph-search.ts +8 -0
  325. package/src/memory/graph/retriever.ts +28 -0
  326. package/src/memory/graph/tools.ts +1 -1
  327. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +8 -2
  328. package/src/memory/jobs/embed-concept-page.ts +28 -2
  329. package/src/memory/jobs/embed-pkb-file.test.ts +2 -2
  330. package/src/memory/jobs-store.ts +66 -22
  331. package/src/memory/jobs-worker.ts +112 -63
  332. package/src/memory/memory-v2-activation-log-store.ts +1 -1
  333. package/src/memory/migrations/237-heartbeat-runs.ts +45 -0
  334. package/src/memory/migrations/238-schedule-retry-policy.ts +20 -0
  335. package/src/memory/migrations/index.ts +5 -0
  336. package/src/memory/migrations/registry.ts +8 -0
  337. package/src/memory/pkb/pkb-search.ts +7 -0
  338. package/src/memory/qdrant-client.ts +50 -20
  339. package/src/memory/schema/infrastructure.ts +15 -0
  340. package/src/memory/search/semantic.ts +7 -0
  341. package/src/memory/sparse-tokenize.ts +49 -0
  342. package/src/memory/v2/__tests__/activation.test.ts +77 -95
  343. package/src/memory/v2/__tests__/injection.test.ts +43 -21
  344. package/src/memory/v2/__tests__/sim.test.ts +166 -6
  345. package/src/memory/v2/__tests__/sparse-bm25.test.ts +292 -0
  346. package/src/memory/v2/__tests__/static-context.test.ts +0 -1
  347. package/src/memory/v2/activation.ts +69 -88
  348. package/src/memory/v2/consolidation-job.ts +3 -5
  349. package/src/memory/v2/constants.ts +7 -0
  350. package/src/memory/v2/injection.ts +86 -53
  351. package/src/memory/v2/prompts/consolidation.ts +312 -91
  352. package/src/memory/v2/qdrant.ts +99 -1
  353. package/src/memory/v2/sim.ts +126 -16
  354. package/src/memory/v2/skill-qdrant.ts +12 -3
  355. package/src/memory/v2/skill-store.ts +16 -1
  356. package/src/memory/v2/sparse-bm25.ts +245 -0
  357. package/src/memory/v2/static-context.ts +6 -5
  358. package/src/messaging/providers/gmail/types.ts +0 -49
  359. package/src/messaging/providers/slack/adapter.ts +1 -31
  360. package/src/messaging/providers/slack/types.ts +0 -32
  361. package/src/notifications/README.md +10 -10
  362. package/src/notifications/broadcaster.ts +1 -1
  363. package/src/notifications/guardian-question-mode.ts +5 -5
  364. package/src/oauth/connect-orchestrator.ts +4 -0
  365. package/src/oauth/credential-token-resolver.ts +1 -3
  366. package/src/oauth/manual-token-connection.ts +0 -4
  367. package/src/outbound-proxy/index.ts +1 -37
  368. package/src/outbound-proxy/logging.ts +1 -1
  369. package/src/outbound-proxy/policy.ts +6 -5
  370. package/src/outbound-proxy/router.ts +2 -1
  371. package/src/permissions/approval-policy.test.ts +6 -275
  372. package/src/permissions/approval-policy.ts +0 -51
  373. package/src/permissions/checker.test.ts +0 -1
  374. package/src/permissions/checker.ts +3 -17
  375. package/src/permissions/gateway-threshold-reader.ts +2 -0
  376. package/src/permissions/prompter.ts +34 -1
  377. package/src/permissions/secret-prompter.ts +6 -2
  378. package/src/prompts/bootstrap-cleanup.ts +27 -0
  379. package/src/prompts/system-prompt.ts +3 -18
  380. package/src/prompts/templates/SOUL.md +13 -1
  381. package/src/providers/speech-to-text/provider-catalog.ts +7 -8
  382. package/src/runtime/assistant-event-hub.ts +118 -96
  383. package/src/runtime/assistant-event.ts +1 -0
  384. package/src/runtime/auth/__tests__/middleware.test.ts +11 -56
  385. package/src/runtime/auth/middleware.ts +0 -96
  386. package/src/runtime/auth/route-policy.ts +19 -0
  387. package/src/runtime/btw-sidechain.ts +2 -3
  388. package/src/runtime/channel-invite-transport.ts +2 -48
  389. package/src/runtime/channel-invite-transports/email.ts +1 -1
  390. package/src/runtime/channel-invite-transports/slack.ts +1 -1
  391. package/src/runtime/channel-invite-transports/telegram.ts +1 -1
  392. package/src/runtime/channel-invite-transports/voice.ts +1 -1
  393. package/src/runtime/channel-invite-transports/whatsapp.ts +1 -1
  394. package/src/runtime/channel-invite-types.ts +54 -0
  395. package/src/runtime/channel-readiness-service.ts +32 -13
  396. package/src/runtime/http-server.ts +3 -329
  397. package/src/runtime/http-types.ts +0 -5
  398. package/src/runtime/migrations/__tests__/vbundle-import-parity.test.ts +413 -0
  399. package/src/runtime/migrations/__tests__/vbundle-import-policy.test.ts +260 -0
  400. package/src/runtime/migrations/__tests__/vbundle-import-version-compat.test.ts +189 -0
  401. package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +153 -1
  402. package/src/runtime/migrations/__tests__/vbundle-symlink-importer.test.ts +451 -0
  403. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -0
  404. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming.test.ts +515 -0
  405. package/src/runtime/migrations/__tests__/vbundle-symlink-tar.test.ts +437 -0
  406. package/src/runtime/migrations/__tests__/vbundle-symlink-walker.test.ts +319 -0
  407. package/src/runtime/migrations/__tests__/vbundle-validator-v1-schema.test.ts +51 -1
  408. package/src/runtime/migrations/migration-transport.ts +7 -7
  409. package/src/runtime/migrations/vbundle-builder.ts +327 -60
  410. package/src/runtime/migrations/vbundle-import-analyzer.ts +4 -4
  411. package/src/runtime/migrations/vbundle-import-policy.ts +172 -0
  412. package/src/runtime/migrations/vbundle-importer.ts +245 -68
  413. package/src/runtime/migrations/vbundle-streaming-importer.ts +326 -35
  414. package/src/runtime/migrations/vbundle-streaming-validator.ts +157 -4
  415. package/src/runtime/migrations/vbundle-tar-stream.ts +15 -6
  416. package/src/runtime/migrations/vbundle-validator.ts +114 -0
  417. package/src/runtime/pending-interactions.ts +35 -9
  418. package/src/runtime/routes/__tests__/backup-routes.test.ts +22 -150
  419. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +98 -0
  420. package/src/runtime/routes/__tests__/gateway-log-routes.test.ts +242 -0
  421. package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +112 -0
  422. package/src/runtime/routes/approval-interception-types.ts +13 -0
  423. package/src/runtime/routes/approval-strategies/guardian-text-engine-strategy.ts +1 -1
  424. package/src/runtime/routes/backup-routes.ts +15 -38
  425. package/src/runtime/routes/btw-routes.ts +14 -37
  426. package/src/runtime/routes/client-routes.ts +1 -0
  427. package/src/runtime/routes/contact-prompt-routes.ts +183 -0
  428. package/src/runtime/routes/conversation-query-routes.ts +36 -1
  429. package/src/runtime/routes/conversation-routes.ts +30 -13
  430. package/src/runtime/routes/document-pdf-renderer.ts +165 -0
  431. package/src/runtime/routes/documents-routes.ts +30 -0
  432. package/src/runtime/routes/errors.ts +19 -4
  433. package/src/runtime/routes/events-routes.ts +12 -6
  434. package/src/runtime/routes/gateway-log-routes.ts +79 -0
  435. package/src/runtime/routes/guardian-approval-interception.ts +2 -8
  436. package/src/runtime/routes/heartbeat-routes.ts +103 -38
  437. package/src/runtime/routes/host-app-control-routes.ts +134 -0
  438. package/src/runtime/routes/host-bash-routes.ts +36 -6
  439. package/src/runtime/routes/host-browser-routes.ts +108 -13
  440. package/src/runtime/routes/host-cu-routes.ts +44 -14
  441. package/src/runtime/routes/host-file-routes.ts +33 -10
  442. package/src/runtime/routes/host-transfer-routes.ts +64 -24
  443. package/src/runtime/routes/http-adapter.ts +1 -0
  444. package/src/runtime/routes/identity-intro-cache.ts +30 -0
  445. package/src/runtime/routes/identity-routes.ts +15 -43
  446. package/src/runtime/routes/inbound-message-handler.ts +1 -9
  447. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +0 -7
  448. package/src/runtime/routes/inbound-stages/edit-intercept.ts +0 -8
  449. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +0 -20
  450. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +5 -13
  451. package/src/runtime/routes/index.ts +8 -0
  452. package/src/runtime/routes/mcp-auth-routes.ts +132 -0
  453. package/src/runtime/routes/memory-item-routes.ts +10 -12
  454. package/src/runtime/routes/memory-v2-routes.ts +441 -1
  455. package/src/runtime/routes/migration-routes.ts +96 -0
  456. package/src/runtime/routes/schedule-routes.ts +7 -0
  457. package/src/runtime/verification-templates.ts +4 -7
  458. package/src/schedule/integration-status.ts +66 -2
  459. package/src/schedule/recurrence-engine.ts +4 -1
  460. package/src/schedule/retry-backoff.ts +18 -0
  461. package/src/schedule/retry-policy.ts +82 -0
  462. package/src/schedule/schedule-recovery.ts +64 -0
  463. package/src/schedule/schedule-store.ts +106 -2
  464. package/src/schedule/scheduler-types.ts +25 -0
  465. package/src/schedule/scheduler.ts +63 -38
  466. package/src/security/oauth-callback-registry.ts +8 -0
  467. package/src/sequence/analytics.ts +5 -5
  468. package/src/sequence/engine.ts +1 -1
  469. package/src/skills/catalog-files.ts +2 -8
  470. package/src/skills/include-graph.ts +5 -5
  471. package/src/skills/remote-skill-policy.ts +5 -5
  472. package/src/skills/skill-file-provider.ts +1 -1
  473. package/src/skills/skill-file-types.ts +13 -0
  474. package/src/skills/skillssh-audit-types.ts +28 -0
  475. package/src/skills/skillssh-registry.ts +8 -21
  476. package/src/telemetry/types.ts +2 -0
  477. package/src/telemetry/usage-telemetry-reporter.test.ts +21 -0
  478. package/src/telemetry/usage-telemetry-reporter.ts +1 -0
  479. package/src/tools/app-control/skill-proxy-bridge.ts +28 -0
  480. package/src/tools/apps/executors.ts +56 -69
  481. package/src/tools/browser/__tests__/browser-status.test.ts +21 -18
  482. package/src/tools/browser/browser-execution.ts +2 -2
  483. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +55 -4
  484. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +12 -6
  485. package/src/tools/browser/cdp-client/factory.ts +23 -24
  486. package/src/tools/browser/cdp-client/index.ts +1 -14
  487. package/src/tools/computer-use/definitions.ts +42 -20
  488. package/src/tools/executor.ts +2 -0
  489. package/src/tools/host-filesystem/edit.ts +26 -0
  490. package/src/tools/host-filesystem/read.ts +26 -0
  491. package/src/tools/host-filesystem/transfer.ts +31 -1
  492. package/src/tools/host-filesystem/write.ts +26 -0
  493. package/src/tools/host-terminal/host-shell.ts +58 -0
  494. package/src/tools/schedule/create.ts +6 -0
  495. package/src/tools/schedule/list.ts +2 -0
  496. package/src/tools/schedule/update.ts +10 -0
  497. package/src/tools/shared/filesystem/file-ops-service.ts +2 -0
  498. package/src/tools/shared/filesystem/path-policy.ts +25 -1
  499. package/src/tools/skills/load.ts +0 -32
  500. package/src/tools/tool-approval-handler.ts +1 -5
  501. package/src/tools/types.ts +4 -0
  502. package/src/usage/pricing.ts +1 -1
  503. package/src/workspace/hatched-date.ts +86 -0
  504. package/src/workspace/migrations/003-seed-device-id.ts +1 -1
  505. package/src/workspace/migrations/006-services-config.ts +8 -5
  506. package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +3 -9
  507. package/src/workspace/migrations/021-move-signals-to-workspace.ts +4 -10
  508. package/src/workspace/migrations/022-move-hooks-to-workspace.ts +4 -10
  509. package/src/workspace/migrations/023-move-config-files-to-workspace.ts +4 -11
  510. package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +3 -10
  511. package/src/workspace/migrations/040-seed-latency-callsite-defaults.ts +3 -2
  512. package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +2 -1
  513. package/src/workspace/migrations/059-move-pid-to-workspace.ts +3 -8
  514. package/src/workspace/migrations/061-move-backup-key-to-workspace.ts +3 -8
  515. package/src/workspace/migrations/AGENTS.md +1 -1
  516. package/src/workspace/migrations/migrate-to-workspace-volume.ts +4 -10
  517. package/src/workspace/migrations/utils.ts +21 -0
  518. package/src/__tests__/host-browser-e2e-cloud.test.ts +0 -443
  519. package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +0 -226
  520. package/src/__tests__/host-browser-ws-events-e2e.test.ts +0 -427
  521. package/src/__tests__/twilio-rest.test.ts +0 -34
  522. package/src/backup/__tests__/backup-key.test.ts +0 -152
  523. package/src/backup/__tests__/backup-worker.test.ts +0 -782
  524. package/src/backup/__tests__/offsite-writer.test.ts +0 -641
  525. package/src/backup/__tests__/stream-crypt.test.ts +0 -228
  526. package/src/backup/backup-key.ts +0 -137
  527. package/src/backup/backup-worker.ts +0 -472
  528. package/src/backup/offsite-writer.ts +0 -222
  529. package/src/backup/stream-crypt.ts +0 -263
  530. package/src/daemon/message-types/pairing.ts +0 -58
  531. package/src/outbound-proxy/config.ts +0 -20
  532. package/src/outbound-proxy/health.ts +0 -18
  533. package/src/outbound-proxy/types.ts +0 -150
  534. package/src/runtime/capability-tokens.ts +0 -190
  535. package/src/signals/mcp-reload.ts +0 -18
@@ -60,6 +60,7 @@ mock.module("../prompts/persona-resolver.js", () => ({
60
60
  import {
61
61
  computeIdentityContentHash,
62
62
  getCachedIntro,
63
+ readWorkspaceIdentityIntro,
63
64
  setCachedIntro,
64
65
  } from "../runtime/routes/identity-intro-cache.js";
65
66
 
@@ -80,6 +81,34 @@ afterEach(() => {
80
81
  // ---------------------------------------------------------------------------
81
82
 
82
83
  describe("identity intro cache", () => {
84
+ test("reads explicit intro from IDENTITY.md before SOUL.md", () => {
85
+ workspaceFiles["IDENTITY.md"] = [
86
+ "# Identity",
87
+ "",
88
+ "## Identity Intro",
89
+ "Nova here.",
90
+ ].join("\n");
91
+ workspaceFiles["SOUL.md"] = [
92
+ "# Soul",
93
+ "",
94
+ "## Identity Intro",
95
+ "Soul fallback.",
96
+ ].join("\n");
97
+
98
+ expect(readWorkspaceIdentityIntro()).toBe("Nova here.");
99
+ });
100
+
101
+ test("falls back to SOUL.md identity intro for legacy workspaces", () => {
102
+ workspaceFiles["SOUL.md"] = [
103
+ "# Soul",
104
+ "",
105
+ "## Identity Intro",
106
+ "Soul fallback.",
107
+ ].join("\n");
108
+
109
+ expect(readWorkspaceIdentityIntro()).toBe("Soul fallback.");
110
+ });
111
+
83
112
  test("returns null when cache is empty", () => {
84
113
  expect(getCachedIntro()).toBeNull();
85
114
  });
@@ -22,8 +22,16 @@ mock.module("../util/logger.js", () => ({
22
22
  }),
23
23
  }));
24
24
 
25
- import { handleDetailedHealth } from "../runtime/routes/identity-routes.js";
25
+ import {
26
+ handleDetailedHealth,
27
+ ROUTES,
28
+ } from "../runtime/routes/identity-routes.js";
26
29
  import { getWorkspaceDir } from "../util/platform.js";
30
+ import {
31
+ getHatchedSidecarPath,
32
+ resolveHatchedAtReadOnly,
33
+ selectHatchedAtFromStats,
34
+ } from "../workspace/hatched-date.js";
27
35
 
28
36
  // ── Env helpers ─────────────────────────────────────────────────────────
29
37
 
@@ -118,6 +126,9 @@ beforeEach(() => {
118
126
  if (existsSync(profilerRunsDir)) {
119
127
  rmSync(profilerRunsDir, { recursive: true, force: true });
120
128
  }
129
+
130
+ rmSync(getHatchedSidecarPath(), { force: true });
131
+ rmSync(join(getWorkspaceDir(), "IDENTITY.md"), { force: true });
121
132
  });
122
133
 
123
134
  afterEach(() => {
@@ -326,3 +337,94 @@ describe("identity routes — health endpoint", () => {
326
337
  });
327
338
  });
328
339
  });
340
+
341
+ describe("identity routes — createdAt selection", () => {
342
+ test("falls back to mtime when birthtime is the Unix epoch", () => {
343
+ const mtime = new Date("2026-05-01T14:49:47.519Z");
344
+
345
+ expect(
346
+ selectHatchedAtFromStats({
347
+ birthtime: new Date(0),
348
+ mtime,
349
+ })?.toISOString(),
350
+ ).toBe(mtime.toISOString());
351
+ });
352
+
353
+ test("prefers birthtime when it is valid", () => {
354
+ const birthtime = new Date("2026-04-30T12:00:00.000Z");
355
+ const mtime = new Date("2026-05-01T14:49:47.519Z");
356
+
357
+ expect(
358
+ selectHatchedAtFromStats({
359
+ birthtime,
360
+ mtime,
361
+ })?.toISOString(),
362
+ ).toBe(birthtime.toISOString());
363
+ });
364
+
365
+ test("returns undefined when both birthtime and mtime are the Unix epoch", () => {
366
+ expect(
367
+ selectHatchedAtFromStats({
368
+ birthtime: new Date(0),
369
+ mtime: new Date(0),
370
+ }),
371
+ ).toBeUndefined();
372
+ });
373
+
374
+ test("read-only resolver falls back to current time without writing sidecar", () => {
375
+ const now = new Date("2026-05-04T12:00:00.000Z");
376
+
377
+ expect(
378
+ resolveHatchedAtReadOnly(
379
+ join(getWorkspaceDir(), "missing-identity.md"),
380
+ now,
381
+ ),
382
+ ).toBe(now.toISOString());
383
+ expect(existsSync(getHatchedSidecarPath())).toBe(false);
384
+ });
385
+
386
+ test("/identity uses persisted hatched sidecar instead of live file metadata", () => {
387
+ const workspaceDir = getWorkspaceDir();
388
+ const dataDir = join(workspaceDir, "data");
389
+ const identityPath = join(workspaceDir, "IDENTITY.md");
390
+ const persistedHatchedAt = "2026-05-01T14:49:47.519Z";
391
+
392
+ mkdirSync(dataDir, { recursive: true });
393
+ writeFileSync(
394
+ identityPath,
395
+ "# Identity\n\n- **Name:** Example Assistant\n",
396
+ "utf-8",
397
+ );
398
+ writeFileSync(
399
+ getHatchedSidecarPath(),
400
+ JSON.stringify({ hatchedAt: persistedHatchedAt }),
401
+ "utf-8",
402
+ );
403
+
404
+ const route = ROUTES.find(
405
+ (candidate) => candidate.operationId === "identity",
406
+ );
407
+ expect(route).toBeDefined();
408
+
409
+ const body = route!.handler({}) as { createdAt?: string };
410
+ expect(body.createdAt).toBe(persistedHatchedAt);
411
+ });
412
+
413
+ test("/identity does not write hatched sidecar on read", () => {
414
+ const identityPath = join(getWorkspaceDir(), "IDENTITY.md");
415
+ writeFileSync(
416
+ identityPath,
417
+ "# Identity\n\n- **Name:** Example Assistant\n",
418
+ "utf-8",
419
+ );
420
+
421
+ const route = ROUTES.find(
422
+ (candidate) => candidate.operationId === "identity",
423
+ );
424
+ expect(route).toBeDefined();
425
+
426
+ const body = route!.handler({}) as { createdAt?: string };
427
+ expect(Date.parse(body.createdAt ?? "")).toBeGreaterThan(0);
428
+ expect(existsSync(getHatchedSidecarPath())).toBe(false);
429
+ });
430
+ });
@@ -41,8 +41,11 @@ describe("initFeatureFlagOverrides", () => {
41
41
  it("falls back gracefully when gateway socket is unavailable", async () => {
42
42
  mockGatewayIpc(null, { error: true });
43
43
 
44
- // Should not throw
45
- await initFeatureFlagOverrides();
44
+ // Disable retries — production retries the IPC fetch on failure to
45
+ // dodge the daemon-vs-gateway startup race, but here we're explicitly
46
+ // testing the no-gateway fallback and don't want the test to wait
47
+ // through the backoff schedule.
48
+ await initFeatureFlagOverrides({ retryBackoffsMs: [] });
46
49
 
47
50
  // Without gateway data or file, undeclared flags default to true
48
51
  const config = {} as any;
@@ -64,7 +67,10 @@ describe("initFeatureFlagOverrides", () => {
64
67
  it("does not cache empty gateway response", async () => {
65
68
  mockGatewayIpc({});
66
69
 
67
- await initFeatureFlagOverrides();
70
+ // Disable retries — this test explicitly simulates a sustained empty
71
+ // response (gateway up but reporting zero flags) and should not wait
72
+ // through the production backoff schedule.
73
+ await initFeatureFlagOverrides({ retryBackoffsMs: [] });
68
74
 
69
75
  // Undeclared flags without overrides default to true (not false from
70
76
  // a cached empty map)
@@ -72,6 +78,23 @@ describe("initFeatureFlagOverrides", () => {
72
78
  expect(isAssistantFeatureFlagEnabled("foo-enabled", config)).toBe(true);
73
79
  });
74
80
 
81
+ it("retries empty gateway responses and picks up flags once they become available", async () => {
82
+ // Simulate the daemon-vs-gateway startup race: the first IPC call
83
+ // returns empty (gateway not yet ready), but a later attempt sees
84
+ // the populated flag map. The retry loop in init should bridge this
85
+ // gap without losing the flag.
86
+ mockGatewayIpc({});
87
+ setTimeout(() => {
88
+ resetMockGatewayIpc();
89
+ mockGatewayIpc({ "retry-test-flag": true });
90
+ }, 50);
91
+
92
+ await initFeatureFlagOverrides({ retryBackoffsMs: [200] });
93
+
94
+ const config = {} as any;
95
+ expect(isAssistantFeatureFlagEnabled("retry-test-flag", config)).toBe(true);
96
+ });
97
+
75
98
  it("does not re-fetch when cache is already populated", async () => {
76
99
  mockGatewayIpc({ "first-call": true });
77
100
 
@@ -27,7 +27,6 @@ mock.module("../config/loader.js", () => ({
27
27
  getConfig: () => mockConfig,
28
28
  loadConfig: () => mockConfig,
29
29
  invalidateConfigCache: () => {},
30
- saveConfig: () => {},
31
30
  loadRawConfig: () => ({}),
32
31
  saveRawConfig: () => {},
33
32
  getNestedValue: () => undefined,
@@ -1,8 +1,7 @@
1
1
  /**
2
2
  * Tests for inline-command skill load permission handling.
3
3
  *
4
- * When a skill contains inline command expansions (!\`...\`) and the
5
- * inline-skill-commands flag is on, the permission
4
+ * When a skill contains inline command expansions (!\`...\`), the permission
6
5
  * system must:
7
6
  *
8
7
  * 1. Emit skill_load_dynamic:<id>@<hash> / skill_load_dynamic:<id> candidates
@@ -17,8 +16,6 @@ import { mkdirSync, rmSync, writeFileSync } from "node:fs";
17
16
  import { join } from "node:path";
18
17
  import { beforeEach, describe, expect, mock, test } from "bun:test";
19
18
 
20
- import { _setOverridesForTesting } from "../config/assistant-feature-flags.js";
21
-
22
19
  // ── Mock setup (must be before any imports from the project) ──────────────
23
20
 
24
21
  const testDir = process.env.VELLUM_WORKSPACE_DIR!;
@@ -48,7 +45,6 @@ mock.module("../config/loader.js", () => ({
48
45
  getConfig: () => testConfig,
49
46
  loadConfig: () => testConfig,
50
47
  invalidateConfigCache: () => {},
51
- saveConfig: () => {},
52
48
  loadRawConfig: () => ({}),
53
49
  saveRawConfig: () => {},
54
50
  getNestedValue: () => undefined,
@@ -67,12 +63,13 @@ mockIpcResponse("classify_risk", {
67
63
  });
68
64
  mockIpcResponse("get_global_thresholds", {
69
65
  interactive: "low",
70
- background: "medium",
66
+ autonomous: "medium",
71
67
  headless: "none",
72
68
  });
73
69
 
74
70
  // ── Imports (after mocks) ─────────────────────────────────────────────────
75
71
 
72
+ import { _setOverridesForTesting } from "../config/assistant-feature-flags.js";
76
73
  import { check, generateAllowlistOptions } from "../permissions/checker.js";
77
74
  import { clearRiskCache } from "../permissions/checker.js";
78
75
  import { _clearGlobalCacheForTesting } from "../permissions/gateway-threshold-reader.js";
@@ -120,13 +117,10 @@ describe("inline-command skill_load permissions", () => {
120
117
  _clearGlobalCacheForTesting();
121
118
  mockIpcResponse("get_global_thresholds", {
122
119
  interactive: "low",
123
- background: "medium",
120
+ autonomous: "medium",
124
121
  headless: "none",
125
122
  });
126
123
  testConfig.skills = { load: { extraDirs: [] } };
127
- _setOverridesForTesting({
128
- "inline-skill-commands": true,
129
- });
130
124
  try {
131
125
  rmSync(join(testDir, "protected", "trust.json"));
132
126
  } catch {
@@ -159,7 +153,7 @@ describe("inline-command skill_load permissions", () => {
159
153
  writeDynamicSkill("dynamic-strict", "Dynamic Strict Skill");
160
154
  mockIpcResponse("get_global_thresholds", {
161
155
  interactive: "none",
162
- background: "none",
156
+ autonomous: "none",
163
157
  headless: "none",
164
158
  });
165
159
  _clearGlobalCacheForTesting();
@@ -5,8 +5,9 @@ import { credentialKey } from "../security/credential-key.js";
5
5
  const secureKeyValues = new Map<string, string>();
6
6
  let mockTwilioAccountSid: string | undefined;
7
7
 
8
- /** Set of providers that should report as connected via isProviderConnected(). */
9
8
  const connectedProviders = new Set<string>();
9
+ const managedProviders = new Set<string>();
10
+ const platformConnectedProviders = new Set<string>();
10
11
 
11
12
  mock.module("../security/secure-keys.js", () => ({
12
13
  getSecureKeyAsync: async (account: string) => secureKeyValues.get(account),
@@ -18,21 +19,60 @@ mock.module("../config/loader.js", () => ({
18
19
  ? { accountSid: mockTwilioAccountSid }
19
20
  : undefined,
20
21
  }),
22
+ getConfig: () => ({ services: {} }),
23
+ }));
24
+
25
+ mock.module("../config/schemas/services.js", () => ({
26
+ getServiceMode: (_services: unknown, key: string) =>
27
+ managedProviders.has(key) ? "managed" : "your-own",
28
+ ServicesSchema: { shape: { google: true, slack: true } },
21
29
  }));
22
30
 
23
31
  mock.module("../oauth/oauth-store.js", () => ({
24
- isProviderConnected: (provider: string) => connectedProviders.has(provider),
32
+ isProviderConnected: (provider: string) =>
33
+ Promise.resolve(connectedProviders.has(provider)),
25
34
  getConnectionByProvider: (provider: string) =>
26
35
  connectedProviders.has(provider)
27
36
  ? { id: `conn-${provider}`, status: "active" }
28
37
  : undefined,
38
+ getProvider: (provider: string) => {
39
+ const managedKeys: Record<string, string> = {
40
+ google: "google",
41
+ slack: "slack",
42
+ };
43
+ return managedKeys[provider]
44
+ ? { managedServiceConfigKey: managedKeys[provider] }
45
+ : undefined;
46
+ },
47
+ }));
48
+
49
+ mock.module("../platform/client.js", () => ({
50
+ VellumPlatformClient: {
51
+ create: async () => ({
52
+ platformAssistantId: "test-assistant",
53
+ fetch: async (path: string) => {
54
+ const url = new URL(`http://localhost${path}`);
55
+ const provider = url.searchParams.get("provider");
56
+ const hasConnections =
57
+ provider && platformConnectedProviders.has(provider);
58
+ return {
59
+ ok: true,
60
+ json: async () => (hasConnections ? [{ id: "conn-1" }] : []),
61
+ };
62
+ },
63
+ }),
64
+ },
29
65
  }));
30
66
 
31
- /** Mark a provider as fully connected (active row + access token). */
32
67
  function setOAuthConnected(provider: string): void {
33
68
  connectedProviders.add(provider);
34
69
  }
35
70
 
71
+ function setPlatformConnected(provider: string, configKey: string): void {
72
+ managedProviders.add(configKey);
73
+ platformConnectedProviders.add(provider);
74
+ }
75
+
36
76
  const { getIntegrationSummary, formatIntegrationSummary, hasCapability } =
37
77
  await import("../schedule/integration-status.js");
38
78
 
@@ -40,6 +80,8 @@ describe("integration-status", () => {
40
80
  beforeEach(() => {
41
81
  secureKeyValues.clear();
42
82
  connectedProviders.clear();
83
+ managedProviders.clear();
84
+ platformConnectedProviders.clear();
43
85
  mockTwilioAccountSid = undefined;
44
86
  });
45
87
 
@@ -99,7 +141,6 @@ describe("integration-status", () => {
99
141
  });
100
142
 
101
143
  test("Telegram disconnected when no connection record exists", async () => {
102
- // No oauth_connection record for telegram — should be disconnected
103
144
  const summary = await getIntegrationSummary();
104
145
  const telegram = summary.find(
105
146
  (s: { name: string }) => s.name === "Telegram",
@@ -153,7 +194,6 @@ describe("integration-status", () => {
153
194
  });
154
195
 
155
196
  test("returns false when no connection record exists for category integrations", async () => {
156
- // No oauth_connection record for telegram — should not count as connected
157
197
  expect(await hasCapability("messaging")).toBe(false);
158
198
  });
159
199
 
@@ -166,4 +206,44 @@ describe("integration-status", () => {
166
206
  expect(await hasCapability("email")).toBe(true);
167
207
  });
168
208
  });
209
+
210
+ describe("managed mode", () => {
211
+ test("Gmail shows connected when platform has active connection", async () => {
212
+ setPlatformConnected("google", "google");
213
+
214
+ const summary = await getIntegrationSummary();
215
+ const gmail = summary.find((s: { name: string }) => s.name === "Gmail");
216
+ expect(gmail?.connected).toBe(true);
217
+ });
218
+
219
+ test("Gmail shows disconnected when managed but no platform connection", async () => {
220
+ managedProviders.add("google");
221
+
222
+ const summary = await getIntegrationSummary();
223
+ const gmail = summary.find((s: { name: string }) => s.name === "Gmail");
224
+ expect(gmail?.connected).toBe(false);
225
+ });
226
+
227
+ test("Slack shows connected when platform has active connection", async () => {
228
+ setPlatformConnected("slack", "slack");
229
+
230
+ const summary = await getIntegrationSummary();
231
+ const slack = summary.find((s: { name: string }) => s.name === "Slack");
232
+ expect(slack?.connected).toBe(true);
233
+ });
234
+
235
+ test("formatIntegrationSummary reflects managed connections", async () => {
236
+ setPlatformConnected("google", "google");
237
+ setPlatformConnected("slack", "slack");
238
+
239
+ const result = await formatIntegrationSummary();
240
+ expect(result).toContain("Gmail \u2713");
241
+ expect(result).toContain("Slack \u2713");
242
+ });
243
+
244
+ test("hasCapability returns true for managed email connection", async () => {
245
+ setPlatformConnected("google", "google");
246
+ expect(await hasCapability("email")).toBe(true);
247
+ });
248
+ });
169
249
  });
@@ -36,7 +36,6 @@ mock.module("../config/loader.js", () => ({
36
36
  }),
37
37
  loadConfig: () => ({}),
38
38
  loadRawConfig: () => ({}),
39
- saveConfig: () => {},
40
39
  saveRawConfig: () => {},
41
40
  invalidateConfigCache: () => {},
42
41
  getNestedValue: () => undefined,
@@ -13,6 +13,8 @@ mock.module("../config/loader.js", () => ({
13
13
  invalidateConfigCache: () => {},
14
14
  }));
15
15
 
16
+ import { eq } from "drizzle-orm";
17
+
16
18
  import { getDb } from "../memory/db-connection.js";
17
19
  import { initializeDb } from "../memory/db-init.js";
18
20
  import {
@@ -24,6 +26,7 @@ import {
24
26
  _resetQdrantBreaker,
25
27
  withQdrantBreaker,
26
28
  } from "../memory/qdrant-circuit-breaker.js";
29
+ import { memoryJobs } from "../memory/schema.js";
27
30
 
28
31
  describe("claimMemoryJobs with Qdrant circuit breaker", () => {
29
32
  beforeAll(() => {
@@ -41,7 +44,7 @@ describe("claimMemoryJobs with Qdrant circuit breaker", () => {
41
44
  enqueueMemoryJob("embed_graph_node", { nodeId: "node-1" });
42
45
  enqueueMemoryJob("graph_extract", { conversationId: "conv-1" });
43
46
 
44
- const claimed = claimMemoryJobs(10);
47
+ const claimed = claimMemoryJobs({ slowLlm: 10, fast: 10, embed: 10 });
45
48
  const types = claimed.map((j) => j.type);
46
49
 
47
50
  expect(types).toContain("embed_segment");
@@ -70,7 +73,7 @@ describe("claimMemoryJobs with Qdrant circuit breaker", () => {
70
73
  conversationId: "conv-1",
71
74
  });
72
75
 
73
- const claimed = claimMemoryJobs(10);
76
+ const claimed = claimMemoryJobs({ slowLlm: 10, fast: 10, embed: 10 });
74
77
  const types = claimed.map((j) => j.type);
75
78
 
76
79
  // Only non-embed jobs should be claimed
@@ -98,7 +101,11 @@ describe("claimMemoryJobs with Qdrant circuit breaker", () => {
98
101
  enqueueMemoryJob("embed_segment", { segmentId: "seg-1" });
99
102
  enqueueMemoryJob("graph_extract", { conversationId: "conv-1" });
100
103
 
101
- const claimedWhileOpen = claimMemoryJobs(10);
104
+ const claimedWhileOpen = claimMemoryJobs({
105
+ slowLlm: 10,
106
+ fast: 10,
107
+ embed: 10,
108
+ });
102
109
  expect(claimedWhileOpen.map((j) => j.type)).not.toContain("embed_segment");
103
110
 
104
111
  // Reset breaker (simulates successful probe closing the circuit)
@@ -107,12 +114,95 @@ describe("claimMemoryJobs with Qdrant circuit breaker", () => {
107
114
  // Re-enqueue an embed job (the previous one is now "running")
108
115
  enqueueMemoryJob("embed_graph_node", { nodeId: "node-2" });
109
116
 
110
- const claimedAfterClose = claimMemoryJobs(10);
117
+ const claimedAfterClose = claimMemoryJobs({
118
+ slowLlm: 10,
119
+ fast: 10,
120
+ embed: 10,
121
+ });
111
122
  const types = claimedAfterClose.map((j) => j.type);
112
123
 
113
124
  expect(types).toContain("embed_graph_node");
114
125
  });
115
126
 
127
+ test("lane budgets are honored when called with explicit budgets", () => {
128
+ // 5 slow-lane jobs
129
+ for (let i = 0; i < 5; i++) {
130
+ enqueueMemoryJob("graph_extract", { conversationId: `slow-${i}` });
131
+ }
132
+ // 5 fast-lane jobs (rebuild_index is neither slow-LLM nor embed)
133
+ for (let i = 0; i < 5; i++) {
134
+ enqueueMemoryJob("rebuild_index", { id: `fast-${i}` });
135
+ }
136
+ // 5 embed-lane jobs
137
+ for (let i = 0; i < 5; i++) {
138
+ enqueueMemoryJob("embed_segment", { segmentId: `embed-${i}` });
139
+ }
140
+
141
+ const claimed = claimMemoryJobs({ slowLlm: 1, fast: 2, embed: 2 });
142
+ const slowClaimed = claimed.filter((j) => j.type === "graph_extract");
143
+ const fastClaimed = claimed.filter((j) => j.type === "rebuild_index");
144
+ const embedClaimed = claimed.filter((j) => j.type === "embed_segment");
145
+
146
+ expect(claimed).toHaveLength(5);
147
+ expect(slowClaimed).toHaveLength(1);
148
+ expect(fastClaimed).toHaveLength(2);
149
+ expect(embedClaimed).toHaveLength(2);
150
+
151
+ // Remaining 10 jobs should still be pending.
152
+ const db = getDb();
153
+ const pendingRows = db
154
+ .select()
155
+ .from(memoryJobs)
156
+ .where(eq(memoryJobs.status, "pending"))
157
+ .all();
158
+ expect(pendingRows).toHaveLength(10);
159
+ });
160
+
161
+ test("Qdrant breaker gates only the embed lane in lane-aware mode", async () => {
162
+ // 5 slow + 5 fast + 5 embed pending jobs
163
+ for (let i = 0; i < 5; i++) {
164
+ enqueueMemoryJob("graph_extract", { conversationId: `slow-${i}` });
165
+ }
166
+ for (let i = 0; i < 5; i++) {
167
+ enqueueMemoryJob("rebuild_index", { id: `fast-${i}` });
168
+ }
169
+ for (let i = 0; i < 5; i++) {
170
+ enqueueMemoryJob("embed_segment", { segmentId: `embed-${i}` });
171
+ }
172
+
173
+ // Trip the breaker
174
+ for (let i = 0; i < 5; i++) {
175
+ try {
176
+ await withQdrantBreaker(async () => {
177
+ throw new Error("simulated qdrant failure");
178
+ });
179
+ } catch {
180
+ // expected
181
+ }
182
+ }
183
+
184
+ const claimed = claimMemoryJobs({ slowLlm: 1, fast: 2, embed: 2 });
185
+ const slowClaimed = claimed.filter((j) => j.type === "graph_extract");
186
+ const fastClaimed = claimed.filter((j) => j.type === "rebuild_index");
187
+ const embedClaimed = claimed.filter((j) => j.type === "embed_segment");
188
+
189
+ expect(slowClaimed).toHaveLength(1);
190
+ expect(fastClaimed).toHaveLength(2);
191
+ // Breaker is open and probe window has not elapsed → no embed jobs claimed.
192
+ expect(embedClaimed).toHaveLength(0);
193
+ });
194
+
195
+ test("FIFO order within a lane is preserved by runAfter ascending", () => {
196
+ const t0 = Date.now() - 30_000;
197
+ enqueueMemoryJob("graph_extract", { conversationId: "third" }, t0 + 200);
198
+ enqueueMemoryJob("graph_extract", { conversationId: "first" }, t0);
199
+ enqueueMemoryJob("graph_extract", { conversationId: "second" }, t0 + 100);
200
+
201
+ const claimed = claimMemoryJobs({ slowLlm: 3, fast: 0, embed: 0 });
202
+ const order = claimed.map((j) => j.payload.conversationId);
203
+ expect(order).toEqual(["first", "second", "third"]);
204
+ });
205
+
116
206
  test("all embed job types are skipped when breaker is open", async () => {
117
207
  const embedTypes: MemoryJobType[] = [
118
208
  "embed_segment",
@@ -141,7 +231,7 @@ describe("claimMemoryJobs with Qdrant circuit breaker", () => {
141
231
  // Also enqueue a non-embed job
142
232
  enqueueMemoryJob("graph_consolidate", { conversationId: "conv-1" });
143
233
 
144
- const claimed = claimMemoryJobs(20);
234
+ const claimed = claimMemoryJobs({ slowLlm: 20, fast: 20, embed: 20 });
145
235
  const types = claimed.map((j) => j.type);
146
236
 
147
237
  // Only the non-embed job should be claimed
@@ -139,6 +139,23 @@ describe("maybeSeedMemoryV2Skills (daemon startup gate)", () => {
139
139
  expect(state.warnCalls).toHaveLength(0);
140
140
  });
141
141
 
142
+ test("re-invocation seeds after flag flips on (deferred-init race recovery)", async () => {
143
+ // Models the lifecycle-startup race: the synchronous seed call evaluates
144
+ // the flag while the gateway IPC override fetch is still in flight, falls
145
+ // through to the registry default (`false`), and skips. Once
146
+ // `initFeatureFlagOverrides()` resolves, the chained `.then` re-invokes
147
+ // the seed with the now-populated cache and the flag flips to `true`.
148
+ state.flagOverrides = { "memory-v2-enabled": false };
149
+ maybeSeedMemoryV2Skills(makeConfig(true));
150
+ await flushMicrotasks();
151
+ expect(state.seedCallCount).toBe(0);
152
+
153
+ state.flagOverrides = { "memory-v2-enabled": true };
154
+ maybeSeedMemoryV2Skills(makeConfig(true));
155
+ await flushMicrotasks();
156
+ expect(state.seedCallCount).toBe(1);
157
+ });
158
+
142
159
  test("swallows seedV2SkillEntries rejections and logs a warning", async () => {
143
160
  state.flagOverrides = { "memory-v2-enabled": true };
144
161
  state.seedShouldReject = new Error("seed failed");
@@ -45,7 +45,6 @@ mock.module("../config/loader.js", () => ({
45
45
  getConfig: () => mockConfig,
46
46
  loadConfig: () => mockConfig,
47
47
  invalidateConfigCache: () => {},
48
- saveConfig: () => {},
49
48
  loadRawConfig: () => ({}),
50
49
  saveRawConfig: () => {},
51
50
  getNestedValue: () => undefined,