@vellumai/assistant 0.5.6 → 0.5.8

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 (442) hide show
  1. package/.env.example +16 -2
  2. package/ARCHITECTURE.md +6 -75
  3. package/Dockerfile +3 -2
  4. package/README.md +0 -2
  5. package/bun.lock +0 -414
  6. package/docker-entrypoint.sh +9 -0
  7. package/docs/architecture/keychain-broker.md +45 -240
  8. package/docs/architecture/memory.md +13 -11
  9. package/docs/architecture/security.md +0 -17
  10. package/docs/credential-execution-service.md +2 -2
  11. package/node_modules/@vellumai/ces-contracts/package.json +1 -0
  12. package/node_modules/@vellumai/ces-contracts/src/error.ts +1 -1
  13. package/node_modules/@vellumai/ces-contracts/src/grants.ts +1 -1
  14. package/node_modules/@vellumai/ces-contracts/src/handles.ts +1 -1
  15. package/node_modules/@vellumai/ces-contracts/src/index.ts +1 -1
  16. package/node_modules/@vellumai/ces-contracts/src/rpc.ts +120 -1
  17. package/node_modules/@vellumai/credential-storage/package.json +1 -0
  18. package/node_modules/@vellumai/egress-proxy/package.json +1 -0
  19. package/package.json +2 -3
  20. package/src/__tests__/actor-token-service.test.ts +0 -114
  21. package/src/__tests__/approval-cascade.test.ts +0 -1
  22. package/src/__tests__/assistant-feature-flags-integration.test.ts +30 -29
  23. package/src/__tests__/browser-fill-credential.test.ts +1 -1
  24. package/src/__tests__/browser-skill-endstate.test.ts +6 -5
  25. package/src/__tests__/btw-routes.test.ts +0 -39
  26. package/src/__tests__/call-controller.test.ts +0 -1
  27. package/src/__tests__/call-domain.test.ts +0 -128
  28. package/src/__tests__/ces-rpc-credential-backend.test.ts +199 -0
  29. package/src/__tests__/ces-startup-timeout.test.ts +40 -0
  30. package/src/__tests__/channel-approval-routes.test.ts +0 -5
  31. package/src/__tests__/channel-readiness-service.test.ts +1 -60
  32. package/src/__tests__/checker.test.ts +4 -2
  33. package/src/__tests__/cli-command-risk-guard.test.ts +112 -0
  34. package/src/__tests__/config-schema-cmd.test.ts +0 -2
  35. package/src/__tests__/config-schema.test.ts +3 -1
  36. package/src/__tests__/conversation-abort-tool-results.test.ts +0 -1
  37. package/src/__tests__/conversation-agent-loop-overflow.test.ts +0 -2
  38. package/src/__tests__/conversation-agent-loop.test.ts +2 -4
  39. package/src/__tests__/conversation-attention-telegram.test.ts +0 -5
  40. package/src/__tests__/conversation-confirmation-signals.test.ts +0 -1
  41. package/src/__tests__/conversation-error.test.ts +15 -1
  42. package/src/__tests__/conversation-init.benchmark.test.ts +0 -2
  43. package/src/__tests__/conversation-messaging-secret-redirect.test.ts +1 -1
  44. package/src/__tests__/conversation-pre-run-repair.test.ts +0 -1
  45. package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -1
  46. package/src/__tests__/conversation-queue.test.ts +0 -1
  47. package/src/__tests__/conversation-skill-tools.test.ts +0 -54
  48. package/src/__tests__/conversation-slash-queue.test.ts +0 -1
  49. package/src/__tests__/conversation-slash-unknown.test.ts +0 -1
  50. package/src/__tests__/conversation-title-service.test.ts +87 -0
  51. package/src/__tests__/conversation-workspace-injection.test.ts +0 -1
  52. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -1
  53. package/src/__tests__/credential-execution-client.test.ts +5 -2
  54. package/src/__tests__/credential-execution-feature-gates.test.ts +59 -30
  55. package/src/__tests__/credential-execution-managed-contract.test.ts +35 -20
  56. package/src/__tests__/credential-security-e2e.test.ts +1 -67
  57. package/src/__tests__/credential-security-invariants.test.ts +6 -50
  58. package/src/__tests__/credentials-cli.test.ts +82 -3
  59. package/src/__tests__/daemon-credential-client.test.ts +123 -0
  60. package/src/__tests__/db-migration-rollback.test.ts +2015 -1
  61. package/src/__tests__/deterministic-verification-control-plane.test.ts +1 -0
  62. package/src/__tests__/docker-signing-key-bootstrap.test.ts +34 -143
  63. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +6 -4
  64. package/src/__tests__/gateway-client-managed-outbound.test.ts +79 -1
  65. package/src/__tests__/guardian-routing-state.test.ts +0 -5
  66. package/src/__tests__/host-shell-tool.test.ts +6 -7
  67. package/src/__tests__/http-user-message-parity.test.ts +3 -103
  68. package/src/__tests__/inbound-invite-redemption.test.ts +0 -4
  69. package/src/__tests__/inline-skill-load-permissions.test.ts +6 -8
  70. package/src/__tests__/intent-routing.test.ts +0 -13
  71. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +178 -0
  72. package/src/__tests__/journal-context.test.ts +335 -0
  73. package/src/__tests__/keychain-broker-client.test.ts +161 -22
  74. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -3
  75. package/src/__tests__/memory-jobs-worker-backoff.test.ts +150 -0
  76. package/src/__tests__/memory-lifecycle-e2e.test.ts +70 -25
  77. package/src/__tests__/memory-recall-quality.test.ts +48 -17
  78. package/src/__tests__/memory-regressions.test.ts +408 -363
  79. package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -3
  80. package/src/__tests__/migration-export-http.test.ts +2 -2
  81. package/src/__tests__/migration-import-commit-http.test.ts +2 -2
  82. package/src/__tests__/migration-import-preflight-http.test.ts +2 -2
  83. package/src/__tests__/migration-validate-http.test.ts +2 -2
  84. package/src/__tests__/non-member-access-request.test.ts +2 -7
  85. package/src/__tests__/notification-decision-fallback.test.ts +4 -0
  86. package/src/__tests__/notification-decision-identity.test.ts +4 -0
  87. package/src/__tests__/notification-decision-strategy.test.ts +71 -0
  88. package/src/__tests__/oauth-cli.test.ts +5 -1
  89. package/src/__tests__/permission-types.test.ts +1 -0
  90. package/src/__tests__/provider-commit-message-generator.test.ts +0 -37
  91. package/src/__tests__/provider-error-scenarios.test.ts +0 -267
  92. package/src/__tests__/provider-managed-proxy-integration.test.ts +5 -6
  93. package/src/__tests__/provider-streaming.benchmark.test.ts +2 -81
  94. package/src/__tests__/qdrant-manager.test.ts +28 -2
  95. package/src/__tests__/registry.test.ts +0 -6
  96. package/src/__tests__/relay-server.test.ts +1 -2
  97. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -4
  98. package/src/__tests__/script-proxy-injection-runtime.test.ts +1 -1
  99. package/src/__tests__/secret-onetime-send.test.ts +1 -1
  100. package/src/__tests__/secret-routes-managed-proxy.test.ts +0 -4
  101. package/src/__tests__/secure-keys.test.ts +95 -272
  102. package/src/__tests__/shell-identity.test.ts +96 -6
  103. package/src/__tests__/skill-feature-flags-integration.test.ts +22 -14
  104. package/src/__tests__/skill-feature-flags.test.ts +46 -45
  105. package/src/__tests__/skill-load-feature-flag.test.ts +7 -10
  106. package/src/__tests__/skill-load-inline-command.test.ts +8 -12
  107. package/src/__tests__/skill-load-inline-includes.test.ts +6 -10
  108. package/src/__tests__/skill-load-tool.test.ts +0 -2
  109. package/src/__tests__/skill-memory.test.ts +17 -3
  110. package/src/__tests__/skill-projection-feature-flag.test.ts +33 -29
  111. package/src/__tests__/skills.test.ts +0 -2
  112. package/src/__tests__/slack-inbound-verification.test.ts +0 -4
  113. package/src/__tests__/stale-approval-dedup.test.ts +171 -0
  114. package/src/__tests__/stt-hints.test.ts +437 -0
  115. package/src/__tests__/suggestion-routes.test.ts +1 -32
  116. package/src/__tests__/system-prompt.test.ts +0 -1
  117. package/src/__tests__/task-memory-cleanup.test.ts +14 -0
  118. package/src/__tests__/tool-executor-shell-integration.test.ts +5 -3
  119. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +0 -5
  120. package/src/__tests__/trusted-contact-multichannel.test.ts +0 -4
  121. package/src/__tests__/twilio-routes-twiml.test.ts +139 -1
  122. package/src/__tests__/update-bulletin.test.ts +0 -2
  123. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +6 -9
  124. package/src/__tests__/voice-quality.test.ts +58 -0
  125. package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -7
  126. package/src/__tests__/workspace-migration-015-migrate-credentials-to-keychain.test.ts +252 -0
  127. package/src/__tests__/workspace-migration-016-migrate-credentials-from-keychain.test.ts +220 -0
  128. package/src/__tests__/workspace-migration-down-functions.test.ts +1009 -0
  129. package/src/__tests__/workspace-migrations-runner.test.ts +114 -0
  130. package/src/acp/agent-process.ts +9 -1
  131. package/src/agent/loop.ts +1 -1
  132. package/src/approvals/guardian-request-resolvers.ts +164 -38
  133. package/src/calls/__tests__/tts-text-sanitizer.test.ts +254 -0
  134. package/src/calls/audio-store.test.ts +97 -0
  135. package/src/calls/audio-store.ts +205 -0
  136. package/src/calls/call-controller.ts +90 -8
  137. package/src/calls/call-domain.ts +3 -0
  138. package/src/calls/call-store.ts +10 -3
  139. package/src/calls/fish-audio-client.ts +129 -0
  140. package/src/calls/relay-server.ts +27 -0
  141. package/src/calls/stt-hints.ts +189 -0
  142. package/src/calls/tts-text-sanitizer.ts +61 -0
  143. package/src/calls/twilio-routes.ts +34 -5
  144. package/src/calls/types.ts +1 -0
  145. package/src/calls/voice-ingress-preflight.ts +0 -42
  146. package/src/calls/voice-quality.ts +38 -5
  147. package/src/calls/voice-session-bridge.ts +7 -12
  148. package/src/cli/commands/avatar.ts +2 -2
  149. package/src/cli/commands/config.ts +1 -4
  150. package/src/cli/commands/credentials.ts +128 -82
  151. package/src/cli/commands/doctor.ts +2 -2
  152. package/src/cli/commands/keys.ts +7 -7
  153. package/src/cli/commands/memory.ts +1 -1
  154. package/src/cli/commands/oauth/connections.ts +11 -29
  155. package/src/cli/commands/oauth/index.ts +7 -0
  156. package/src/cli/commands/oauth/platform.ts +525 -0
  157. package/src/cli/commands/platform.ts +3 -3
  158. package/src/cli/lib/daemon-credential-client.ts +284 -0
  159. package/src/cli.ts +1 -1
  160. package/src/config/assistant-feature-flags.ts +186 -5
  161. package/src/config/bundled-skills/AGENTS.md +34 -0
  162. package/src/config/bundled-skills/acp/SKILL.md +10 -0
  163. package/src/config/bundled-skills/app-builder/SKILL.md +0 -4
  164. package/src/config/bundled-skills/messaging/SKILL.md +5 -5
  165. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +2 -2
  166. package/src/config/bundled-skills/phone-calls/TOOLS.json +4 -0
  167. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +1 -0
  168. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +1 -0
  169. package/src/config/bundled-skills/settings/SKILL.md +15 -2
  170. package/src/config/bundled-skills/settings/TOOLS.json +47 -2
  171. package/src/config/bundled-skills/settings/tools/avatar-remove.ts +59 -0
  172. package/src/config/bundled-skills/settings/tools/avatar-update.ts +80 -0
  173. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +42 -0
  174. package/src/config/bundled-skills/slack/SKILL.md +1 -1
  175. package/src/config/bundled-tool-registry.ts +5 -11
  176. package/src/config/defaults.ts +0 -2
  177. package/src/config/env-registry.ts +5 -5
  178. package/src/config/env.ts +21 -14
  179. package/src/config/feature-flag-registry.json +49 -9
  180. package/src/config/loader.ts +106 -42
  181. package/src/config/schema.ts +9 -29
  182. package/src/config/schemas/calls.ts +30 -0
  183. package/src/config/schemas/fish-audio.ts +39 -0
  184. package/src/config/schemas/inference.ts +2 -2
  185. package/src/config/schemas/journal.ts +16 -0
  186. package/src/config/schemas/memory-processing.ts +2 -2
  187. package/src/config/schemas/security.ts +0 -4
  188. package/src/config/types.ts +1 -1
  189. package/src/contacts/contact-store.ts +39 -0
  190. package/src/contacts/types.ts +2 -0
  191. package/src/credential-execution/approval-bridge.ts +1 -0
  192. package/src/credential-execution/executable-discovery.ts +28 -4
  193. package/src/credential-execution/feature-gates.ts +16 -0
  194. package/src/credential-execution/process-manager.ts +38 -0
  195. package/src/credential-execution/startup-timeout.ts +36 -0
  196. package/src/daemon/approval-generators.ts +3 -9
  197. package/src/daemon/assistant-attachments.ts +9 -0
  198. package/src/daemon/config-watcher.ts +5 -0
  199. package/src/daemon/conversation-error.ts +13 -1
  200. package/src/daemon/conversation-memory.ts +1 -2
  201. package/src/daemon/conversation-process.ts +18 -1
  202. package/src/daemon/conversation-surfaces.ts +30 -1
  203. package/src/daemon/conversation-tool-setup.ts +0 -105
  204. package/src/daemon/conversation.ts +21 -1
  205. package/src/daemon/guardian-action-generators.ts +3 -9
  206. package/src/daemon/handlers/config-vercel.ts +92 -0
  207. package/src/daemon/handlers/skills.ts +2 -15
  208. package/src/daemon/install-symlink.ts +195 -0
  209. package/src/daemon/lifecycle.ts +234 -51
  210. package/src/daemon/message-types/conversations.ts +4 -4
  211. package/src/daemon/message-types/diagnostics.ts +3 -22
  212. package/src/daemon/message-types/messages.ts +0 -2
  213. package/src/daemon/message-types/upgrades.ts +8 -0
  214. package/src/daemon/server.ts +32 -95
  215. package/src/events/domain-events.ts +2 -1
  216. package/src/inbound/platform-callback-registration.ts +3 -3
  217. package/src/instrument.ts +8 -5
  218. package/src/memory/app-store.ts +31 -0
  219. package/src/memory/conversation-title-service.ts +50 -1
  220. package/src/memory/db-init.ts +16 -0
  221. package/src/memory/indexer.ts +19 -10
  222. package/src/memory/items-extractor.ts +328 -321
  223. package/src/memory/job-handlers/conversation-starters.ts +4 -1
  224. package/src/memory/job-handlers/summarization.ts +26 -16
  225. package/src/memory/jobs-store.ts +63 -6
  226. package/src/memory/jobs-worker.ts +31 -7
  227. package/src/memory/journal-memory.ts +214 -0
  228. package/src/memory/migrations/001-job-deferrals.ts +19 -0
  229. package/src/memory/migrations/004-entity-relation-dedup.ts +10 -0
  230. package/src/memory/migrations/005-fingerprint-scope-unique.ts +76 -0
  231. package/src/memory/migrations/006-scope-salted-fingerprints.ts +50 -0
  232. package/src/memory/migrations/007-assistant-id-to-self.ts +10 -0
  233. package/src/memory/migrations/008-remove-assistant-id-columns.ts +34 -0
  234. package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +26 -0
  235. package/src/memory/migrations/014-backfill-inbox-thread-state.ts +10 -0
  236. package/src/memory/migrations/015-drop-active-search-index.ts +17 -0
  237. package/src/memory/migrations/019-notification-tables-schema-migration.ts +12 -0
  238. package/src/memory/migrations/020-rename-macos-ios-channel-to-vellum.ts +121 -0
  239. package/src/memory/migrations/024-embedding-vector-blob.ts +74 -0
  240. package/src/memory/migrations/026a-embeddings-nullable-vector-json.ts +82 -0
  241. package/src/memory/migrations/036-normalize-phone-identities.ts +11 -0
  242. package/src/memory/migrations/116-messages-fts.ts +106 -1
  243. package/src/memory/migrations/126-backfill-guardian-principal-id.ts +52 -0
  244. package/src/memory/migrations/127-guardian-principal-id-not-null.ts +77 -0
  245. package/src/memory/migrations/134-contacts-notes-column.ts +13 -0
  246. package/src/memory/migrations/135-backfill-contact-interaction-stats.ts +20 -0
  247. package/src/memory/migrations/136-drop-assistant-id-columns.ts +52 -0
  248. package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +13 -0
  249. package/src/memory/migrations/141-rename-verification-table.ts +54 -0
  250. package/src/memory/migrations/142-rename-verification-session-id-column.ts +25 -0
  251. package/src/memory/migrations/143-rename-guardian-verification-values.ts +35 -0
  252. package/src/memory/migrations/144-rename-voice-to-phone.ts +136 -0
  253. package/src/memory/migrations/145-drop-accounts-table.ts +32 -0
  254. package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +14 -1
  255. package/src/memory/migrations/148-drop-reminders-table.ts +35 -1
  256. package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +69 -1
  257. package/src/memory/migrations/162-guardian-timestamps-epoch-ms.ts +290 -0
  258. package/src/memory/migrations/169-rename-gmail-provider-key-to-google.ts +51 -1
  259. package/src/memory/migrations/174-rename-thread-starters-table.ts +47 -1
  260. package/src/memory/migrations/176-drop-capability-card-state.ts +13 -0
  261. package/src/memory/migrations/180-backfill-inline-attachments-to-disk.ts +16 -0
  262. package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +28 -1
  263. package/src/memory/migrations/190-call-session-skip-disclosure.ts +15 -0
  264. package/src/memory/migrations/191-backfill-audio-attachment-mime-types.ts +64 -0
  265. package/src/memory/migrations/192-contacts-user-file-column.ts +15 -0
  266. package/src/memory/migrations/193-add-source-type-columns.ts +81 -0
  267. package/src/memory/migrations/index.ts +5 -0
  268. package/src/memory/migrations/registry.ts +98 -0
  269. package/src/memory/migrations/validate-migration-state.ts +137 -11
  270. package/src/memory/qdrant-circuit-breaker.ts +9 -0
  271. package/src/memory/qdrant-manager.ts +64 -7
  272. package/src/memory/retriever.test.ts +37 -25
  273. package/src/memory/retriever.ts +24 -49
  274. package/src/memory/schema/calls.ts +1 -0
  275. package/src/memory/schema/contacts.ts +1 -0
  276. package/src/memory/schema/memory-core.ts +2 -0
  277. package/src/memory/search/formatting.ts +7 -44
  278. package/src/memory/search/staleness.ts +4 -0
  279. package/src/memory/search/tier-classifier.ts +10 -2
  280. package/src/memory/search/types.ts +2 -5
  281. package/src/memory/task-memory-cleanup.ts +4 -3
  282. package/src/notifications/adapters/slack.ts +168 -6
  283. package/src/notifications/broadcaster.ts +1 -0
  284. package/src/notifications/copy-composer.ts +59 -2
  285. package/src/notifications/decision-engine.ts +4 -1
  286. package/src/notifications/signal.ts +2 -0
  287. package/src/notifications/types.ts +2 -0
  288. package/src/oauth/connection-resolver.ts +6 -4
  289. package/src/permissions/checker.ts +0 -38
  290. package/src/permissions/shell-identity.ts +76 -22
  291. package/src/permissions/types.ts +4 -2
  292. package/src/platform/client.ts +35 -7
  293. package/src/prompts/journal-context.ts +133 -0
  294. package/src/prompts/persona-resolver.ts +194 -0
  295. package/src/prompts/system-prompt.ts +44 -4
  296. package/src/prompts/templates/SOUL.md +10 -0
  297. package/src/prompts/templates/users/default.md +1 -0
  298. package/src/providers/provider-send-message.ts +3 -32
  299. package/src/providers/registry.ts +29 -179
  300. package/src/providers/types.ts +1 -1
  301. package/src/runtime/access-request-helper.ts +4 -0
  302. package/src/runtime/auth/__tests__/credential-service.test.ts +0 -1
  303. package/src/runtime/auth/__tests__/external-assistant-id.test.ts +13 -68
  304. package/src/runtime/auth/__tests__/guard-tests.test.ts +9 -50
  305. package/src/runtime/auth/external-assistant-id.ts +13 -59
  306. package/src/runtime/auth/route-policy.ts +17 -1
  307. package/src/runtime/auth/token-service.ts +43 -138
  308. package/src/runtime/channel-readiness-service.ts +1 -16
  309. package/src/runtime/gateway-client.ts +47 -4
  310. package/src/runtime/guardian-decision-types.ts +45 -4
  311. package/src/runtime/http-server.ts +31 -3
  312. package/src/runtime/middleware/error-handler.ts +1 -9
  313. package/src/runtime/routes/access-request-decision.ts +2 -2
  314. package/src/runtime/routes/app-management-routes.ts +2 -1
  315. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +219 -30
  316. package/src/runtime/routes/approval-strategies/guardian-text-engine-strategy.ts +37 -14
  317. package/src/runtime/routes/audio-routes.ts +40 -0
  318. package/src/runtime/routes/btw-routes.ts +0 -17
  319. package/src/runtime/routes/channel-readiness-routes.ts +9 -4
  320. package/src/runtime/routes/conversation-query-routes.ts +63 -1
  321. package/src/runtime/routes/conversation-routes.ts +4 -44
  322. package/src/runtime/routes/debug-routes.ts +12 -9
  323. package/src/runtime/routes/diagnostics-routes.ts +1 -477
  324. package/src/runtime/routes/guardian-approval-interception.ts +168 -11
  325. package/src/runtime/routes/guardian-approval-prompt.ts +6 -1
  326. package/src/runtime/routes/guardian-approval-reply-helpers.ts +103 -21
  327. package/src/runtime/routes/identity-routes.ts +19 -30
  328. package/src/runtime/routes/inbound-message-handler.ts +31 -1
  329. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +64 -5
  330. package/src/runtime/routes/inbound-stages/background-dispatch.ts +52 -40
  331. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +4 -33
  332. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +1 -1
  333. package/src/runtime/routes/integrations/twilio.ts +52 -10
  334. package/src/runtime/routes/integrations/vercel.ts +89 -0
  335. package/src/runtime/routes/log-export-routes.ts +5 -0
  336. package/src/runtime/routes/memory-item-routes.test.ts +3 -3
  337. package/src/runtime/routes/memory-item-routes.ts +46 -14
  338. package/src/runtime/routes/migration-rollback-routes.ts +209 -0
  339. package/src/runtime/routes/migration-routes.ts +17 -1
  340. package/src/runtime/routes/notification-routes.ts +58 -0
  341. package/src/runtime/routes/schedule-routes.ts +65 -0
  342. package/src/runtime/routes/secret-routes.ts +141 -10
  343. package/src/runtime/routes/settings-routes.ts +41 -1
  344. package/src/runtime/routes/tts-routes.ts +96 -0
  345. package/src/runtime/routes/upgrade-broadcast-routes.ts +26 -2
  346. package/src/runtime/routes/workspace-commit-routes.ts +62 -0
  347. package/src/runtime/routes/workspace-routes.test.ts +22 -1
  348. package/src/runtime/routes/workspace-routes.ts +1 -1
  349. package/src/runtime/routes/workspace-utils.ts +86 -2
  350. package/src/security/ces-credential-client.ts +75 -29
  351. package/src/security/ces-rpc-credential-backend.ts +86 -0
  352. package/src/security/credential-backend.ts +22 -92
  353. package/src/security/keychain-broker-client.ts +10 -2
  354. package/src/security/secure-keys.ts +113 -115
  355. package/src/skills/catalog-install.ts +6 -32
  356. package/src/skills/skill-memory.ts +1 -0
  357. package/src/subagent/manager.ts +2 -5
  358. package/src/telemetry/usage-telemetry-reporter.ts +4 -2
  359. package/src/tools/acp/spawn.ts +78 -1
  360. package/src/tools/calls/call-start.ts +1 -0
  361. package/src/tools/credentials/vault.ts +5 -3
  362. package/src/tools/executor.ts +0 -4
  363. package/src/tools/memory/definitions.ts +3 -2
  364. package/src/tools/memory/handlers.ts +10 -7
  365. package/src/tools/network/script-proxy/session-manager.ts +19 -4
  366. package/src/tools/network/web-fetch.ts +3 -1
  367. package/src/tools/skills/execute.ts +1 -1
  368. package/src/tools/terminal/safe-env.ts +1 -0
  369. package/src/tools/types.ts +0 -8
  370. package/src/util/browser.ts +15 -0
  371. package/src/util/errors.ts +0 -12
  372. package/src/util/platform.ts +4 -51
  373. package/src/workspace/git-service.ts +5 -2
  374. package/src/workspace/migrations/001-avatar-rename.ts +15 -0
  375. package/src/workspace/migrations/003-seed-device-id.ts +17 -1
  376. package/src/workspace/migrations/004-extract-collect-usage-data.ts +33 -0
  377. package/src/workspace/migrations/005-add-send-diagnostics.ts +3 -0
  378. package/src/workspace/migrations/006-services-config.ts +49 -0
  379. package/src/workspace/migrations/007-web-search-provider-rename.ts +27 -0
  380. package/src/workspace/migrations/008-voice-timeout-and-max-steps.ts +3 -0
  381. package/src/workspace/migrations/009-backfill-conversation-disk-view.ts +4 -0
  382. package/src/workspace/migrations/010-app-dir-rename.ts +78 -0
  383. package/src/workspace/migrations/011-backfill-installation-id.ts +11 -0
  384. package/src/workspace/migrations/012-rename-conversation-disk-view-dirs.ts +44 -0
  385. package/src/workspace/migrations/013-repair-conversation-disk-view.ts +5 -0
  386. package/src/workspace/migrations/015-migrate-credentials-to-keychain.ts +153 -0
  387. package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +156 -0
  388. package/src/workspace/migrations/016-migrate-credentials-from-keychain.ts +150 -0
  389. package/src/workspace/migrations/017-seed-persona-dirs.ts +96 -0
  390. package/src/workspace/migrations/018-rekey-compound-credential-keys.ts +184 -0
  391. package/src/workspace/migrations/019-scope-journal-to-guardian.ts +103 -0
  392. package/src/workspace/migrations/migrate-to-workspace-volume.ts +27 -5
  393. package/src/workspace/migrations/registry.ts +12 -0
  394. package/src/workspace/migrations/runner.ts +106 -2
  395. package/src/workspace/migrations/types.ts +4 -0
  396. package/src/workspace/provider-commit-message-generator.ts +12 -21
  397. package/src/__tests__/claude-code-skill-regression.test.ts +0 -206
  398. package/src/__tests__/claude-code-tool-profiles.test.ts +0 -99
  399. package/src/__tests__/diagnostics-export.test.ts +0 -288
  400. package/src/__tests__/local-gateway-health.test.ts +0 -209
  401. package/src/__tests__/provider-fail-open-selection.test.ts +0 -271
  402. package/src/__tests__/provider-failover-actual-provider.test.ts +0 -66
  403. package/src/__tests__/secret-ingress-handler.test.ts +0 -120
  404. package/src/__tests__/swarm-conversation-integration.test.ts +0 -358
  405. package/src/__tests__/swarm-dag-pathological.test.ts +0 -547
  406. package/src/__tests__/swarm-orchestrator.test.ts +0 -463
  407. package/src/__tests__/swarm-plan-validator.test.ts +0 -384
  408. package/src/__tests__/swarm-recursion.test.ts +0 -197
  409. package/src/__tests__/swarm-router-planner.test.ts +0 -234
  410. package/src/__tests__/swarm-tool.test.ts +0 -185
  411. package/src/__tests__/swarm-worker-backend.test.ts +0 -144
  412. package/src/__tests__/swarm-worker-runner.test.ts +0 -288
  413. package/src/commands/__tests__/cc-command-registry.test.ts +0 -396
  414. package/src/commands/cc-command-registry.ts +0 -248
  415. package/src/config/bundled-skills/claude-code/SKILL.md +0 -53
  416. package/src/config/bundled-skills/claude-code/TOOLS.json +0 -47
  417. package/src/config/bundled-skills/claude-code/tools/claude-code.ts +0 -12
  418. package/src/config/bundled-skills/orchestration/SKILL.md +0 -33
  419. package/src/config/bundled-skills/orchestration/TOOLS.json +0 -35
  420. package/src/config/bundled-skills/orchestration/tools/swarm-delegate.ts +0 -12
  421. package/src/config/schemas/swarm.ts +0 -82
  422. package/src/logfire.ts +0 -135
  423. package/src/memory/search/lexical.ts +0 -48
  424. package/src/providers/failover.ts +0 -186
  425. package/src/runtime/local-gateway-health.ts +0 -275
  426. package/src/security/secret-ingress.ts +0 -68
  427. package/src/swarm/backend-claude-code.ts +0 -225
  428. package/src/swarm/checkpoint.ts +0 -137
  429. package/src/swarm/graph-utils.ts +0 -53
  430. package/src/swarm/index.ts +0 -55
  431. package/src/swarm/limits.ts +0 -66
  432. package/src/swarm/orchestrator.ts +0 -424
  433. package/src/swarm/plan-validator.ts +0 -117
  434. package/src/swarm/router-planner.ts +0 -162
  435. package/src/swarm/router-prompts.ts +0 -39
  436. package/src/swarm/synthesizer.ts +0 -81
  437. package/src/swarm/types.ts +0 -72
  438. package/src/swarm/worker-backend.ts +0 -131
  439. package/src/swarm/worker-prompts.ts +0 -80
  440. package/src/swarm/worker-runner.ts +0 -170
  441. package/src/tools/claude-code/claude-code.ts +0 -610
  442. package/src/tools/swarm/delegate.ts +0 -205
@@ -128,6 +128,7 @@ describe("TwiML parameter propagation", () => {
128
128
  transcriptionProvider: "deepgram",
129
129
  ttsProvider: "google",
130
130
  voice: "en-US-Standard-A",
131
+ interruptSensitivity: "low",
131
132
  };
132
133
 
133
134
  test("includes verificationSessionId as Parameter when provided", () => {
@@ -1,31 +1,20 @@
1
1
  /**
2
- * Integration tests for resolveSigningKey() covering the Docker bootstrap
3
- * lifecycle: fresh fetch from gateway, daemon restart (load from disk),
4
- * and local mode (file-based load/create).
2
+ * Tests for resolveSigningKey() covering env var injection (Docker)
3
+ * and file-based load/create (local mode).
5
4
  */
6
5
 
7
- import { mkdtempSync, readFileSync, realpathSync, rmSync } from "node:fs";
6
+ import { mkdirSync, mkdtempSync, realpathSync, rmSync } from "node:fs";
8
7
  import { tmpdir } from "node:os";
9
8
  import { join } from "node:path";
10
9
  import { afterAll, afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
11
10
 
12
- // ---------------------------------------------------------------------------
13
- // Temp directory for signing key persistence
14
- // ---------------------------------------------------------------------------
15
-
16
- const testDir = realpathSync(mkdtempSync(join(tmpdir(), "docker-signing-key-test-")));
17
-
18
- // ---------------------------------------------------------------------------
19
- // Mock platform to redirect signing key file to our temp directory
20
- // ---------------------------------------------------------------------------
11
+ const testDir = realpathSync(mkdtempSync(join(tmpdir(), "signing-key-test-")));
21
12
 
22
13
  mock.module("../util/platform.js", () => ({
23
14
  getRootDir: () => testDir,
24
15
  getDataDir: () => testDir,
25
16
  getDbPath: () => join(testDir, "test.db"),
26
17
  normalizeAssistantId: (id: string) => (id === "self" ? "self" : id),
27
- readLockfile: () => null,
28
- writeLockfile: () => {},
29
18
  isMacOS: () => process.platform === "darwin",
30
19
  isLinux: () => process.platform === "linux",
31
20
  isWindows: () => process.platform === "win32",
@@ -41,53 +30,23 @@ mock.module("../util/logger.js", () => ({
41
30
  }),
42
31
  }));
43
32
 
44
- // ---------------------------------------------------------------------------
45
- // Import the functions under test (after mocks are installed)
46
- // ---------------------------------------------------------------------------
47
-
48
- const {
49
- resolveSigningKey,
50
- loadOrCreateSigningKey: _loadOrCreateSigningKey,
51
- BootstrapAlreadyCompleted: _BootstrapAlreadyCompleted,
52
- } = await import("../runtime/auth/token-service.js");
53
-
54
- // ---------------------------------------------------------------------------
55
- // Test constants
56
- // ---------------------------------------------------------------------------
33
+ const { resolveSigningKey } = await import("../runtime/auth/token-service.js");
57
34
 
58
- const VALID_32_BYTE_KEY = "ab".repeat(32); // 64 hex chars = 32 bytes
59
- const SIGNING_KEY_PATH = join(testDir, "protected", "actor-token-signing-key");
35
+ const VALID_HEX_KEY = "ab".repeat(32); // 64 hex chars = 32 bytes
60
36
 
61
- // ---------------------------------------------------------------------------
62
- // Environment & fetch state management
63
- // ---------------------------------------------------------------------------
64
-
65
- const originalFetch = globalThis.fetch;
66
37
  const savedEnv: Record<string, string | undefined> = {};
67
38
 
68
- function saveEnv(...keys: string[]) {
69
- for (const key of keys) {
70
- savedEnv[key] = process.env[key];
71
- }
72
- }
73
-
74
- function restoreEnv() {
75
- for (const [key, val] of Object.entries(savedEnv)) {
76
- if (val === undefined) {
77
- delete process.env[key];
78
- } else {
79
- process.env[key] = val;
80
- }
81
- }
82
- }
83
-
84
39
  beforeEach(() => {
85
- saveEnv("IS_CONTAINERIZED", "GATEWAY_INTERNAL_URL");
40
+ savedEnv.ACTOR_TOKEN_SIGNING_KEY = process.env.ACTOR_TOKEN_SIGNING_KEY;
41
+ mkdirSync(join(testDir, "protected"), { recursive: true });
86
42
  });
87
43
 
88
44
  afterEach(() => {
89
- globalThis.fetch = originalFetch;
90
- restoreEnv();
45
+ if (savedEnv.ACTOR_TOKEN_SIGNING_KEY === undefined) {
46
+ delete process.env.ACTOR_TOKEN_SIGNING_KEY;
47
+ } else {
48
+ process.env.ACTOR_TOKEN_SIGNING_KEY = savedEnv.ACTOR_TOKEN_SIGNING_KEY;
49
+ }
91
50
  });
92
51
 
93
52
  afterAll(() => {
@@ -96,112 +55,44 @@ afterAll(() => {
96
55
  } catch {}
97
56
  });
98
57
 
99
- // ---------------------------------------------------------------------------
100
- // Docker mode tests resolveSigningKey() bootstrap lifecycle
101
- // ---------------------------------------------------------------------------
102
-
103
- describe("resolveSigningKey — Docker bootstrap lifecycle", () => {
104
- test("fresh bootstrap: fetches key from gateway and persists to disk", async () => {
105
- process.env.IS_CONTAINERIZED = "true";
106
- process.env.GATEWAY_INTERNAL_URL = "http://localhost:19876";
58
+ describe("resolveSigningKey", () => {
59
+ test("reads key from ACTOR_TOKEN_SIGNING_KEY env var", () => {
60
+ process.env.ACTOR_TOKEN_SIGNING_KEY = VALID_HEX_KEY;
107
61
 
108
- // Mock fetch to return a known 32-byte key on first call.
109
- globalThis.fetch = (async () =>
110
- new Response(JSON.stringify({ key: VALID_32_BYTE_KEY }), {
111
- status: 200,
112
- headers: { "Content-Type": "application/json" },
113
- })) as unknown as typeof fetch;
62
+ const key = resolveSigningKey();
114
63
 
115
- const key = await resolveSigningKey();
116
-
117
- // Verify the returned key is a 32-byte buffer with the expected content.
118
64
  expect(key).toBeInstanceOf(Buffer);
119
65
  expect(key.length).toBe(32);
120
- expect(key.toString("hex")).toBe(VALID_32_BYTE_KEY);
121
-
122
- // Verify the key was persisted to disk.
123
- const persisted = readFileSync(SIGNING_KEY_PATH);
124
- expect(persisted.length).toBe(32);
125
- expect(Buffer.from(persisted).equals(key)).toBe(true);
66
+ expect(key.toString("hex")).toBe(VALID_HEX_KEY);
126
67
  });
127
68
 
128
- test("daemon restart: gateway returns 403, loads persisted key from disk", async () => {
129
- process.env.IS_CONTAINERIZED = "true";
130
- process.env.GATEWAY_INTERNAL_URL = "http://localhost:19876";
69
+ test("rejects invalid ACTOR_TOKEN_SIGNING_KEY", () => {
70
+ process.env.ACTOR_TOKEN_SIGNING_KEY = "tooshort";
131
71
 
132
- // The previous test persisted the key. Simulate a daemon restart where
133
- // the gateway returns 403 (bootstrap already completed).
134
- globalThis.fetch = (async () =>
135
- new Response(JSON.stringify({ error: "Bootstrap already completed" }), {
136
- status: 403,
137
- })) as unknown as typeof fetch;
138
-
139
- const key = await resolveSigningKey();
140
-
141
- // Should have loaded the previously persisted key from disk.
142
- expect(key).toBeInstanceOf(Buffer);
143
- expect(key.length).toBe(32);
144
- expect(key.toString("hex")).toBe(VALID_32_BYTE_KEY);
145
- });
146
- });
147
-
148
- // ---------------------------------------------------------------------------
149
- // Local mode tests — resolveSigningKey() file-based path
150
- // ---------------------------------------------------------------------------
151
-
152
- describe("resolveSigningKey — local mode", () => {
153
- test("uses file-based loadOrCreateSigningKey without calling fetch", async () => {
154
- // Ensure Docker env vars are unset.
155
- delete process.env.IS_CONTAINERIZED;
156
- delete process.env.GATEWAY_INTERNAL_URL;
157
-
158
- let fetchCalled = false;
159
- globalThis.fetch = (async () => {
160
- fetchCalled = true;
161
- return new Response("should not be called", { status: 500 });
162
- }) as unknown as typeof fetch;
163
-
164
- const key = await resolveSigningKey();
165
-
166
- // Should return a valid 32-byte key (loaded from disk or newly created).
167
- expect(key).toBeInstanceOf(Buffer);
168
- expect(key.length).toBe(32);
169
-
170
- // Crucially, fetch should NOT have been called.
171
- expect(fetchCalled).toBe(false);
72
+ expect(() => resolveSigningKey()).toThrow("Invalid ACTOR_TOKEN_SIGNING_KEY");
172
73
  });
173
74
 
174
- test("IS_CONTAINERIZED=false does not trigger gateway fetch", async () => {
175
- process.env.IS_CONTAINERIZED = "false";
176
- process.env.GATEWAY_INTERNAL_URL = "http://localhost:19876";
75
+ test("falls back to file-based load/create when env var is not set", () => {
76
+ delete process.env.ACTOR_TOKEN_SIGNING_KEY;
177
77
 
178
- let fetchCalled = false;
179
- globalThis.fetch = (async () => {
180
- fetchCalled = true;
181
- return new Response("should not be called", { status: 500 });
182
- }) as unknown as typeof fetch;
183
-
184
- const key = await resolveSigningKey();
78
+ const key = resolveSigningKey();
185
79
 
186
80
  expect(key).toBeInstanceOf(Buffer);
187
81
  expect(key.length).toBe(32);
188
- expect(fetchCalled).toBe(false);
189
82
  });
190
83
 
191
- test("IS_CONTAINERIZED=true without GATEWAY_INTERNAL_URL uses local path", async () => {
192
- process.env.IS_CONTAINERIZED = "true";
193
- delete process.env.GATEWAY_INTERNAL_URL;
84
+ test("env var takes priority over file on disk", () => {
85
+ process.env.ACTOR_TOKEN_SIGNING_KEY = VALID_HEX_KEY;
194
86
 
195
- let fetchCalled = false;
196
- globalThis.fetch = (async () => {
197
- fetchCalled = true;
198
- return new Response("should not be called", { status: 500 });
199
- }) as unknown as typeof fetch;
87
+ // First call creates a file-based key
88
+ delete process.env.ACTOR_TOKEN_SIGNING_KEY;
89
+ const fileKey = resolveSigningKey();
200
90
 
201
- const key = await resolveSigningKey();
91
+ // Second call with env var should use the env var, not the file
92
+ process.env.ACTOR_TOKEN_SIGNING_KEY = "cd".repeat(32);
93
+ const envKey = resolveSigningKey();
202
94
 
203
- expect(key).toBeInstanceOf(Buffer);
204
- expect(key.length).toBe(32);
205
- expect(fetchCalled).toBe(false);
95
+ expect(envKey.toString("hex")).toBe("cd".repeat(32));
96
+ expect(envKey.toString("hex")).not.toBe(fileKey.toString("hex"));
206
97
  });
207
98
  });
@@ -53,9 +53,6 @@ mock.module("../util/logger.js", () => ({
53
53
 
54
54
  mock.module("../config/loader.js", () => ({
55
55
  getConfig: () => ({
56
- assistantFeatureFlagValues: {
57
- "feature_flags.browser.enabled": true,
58
- },
59
56
  services: {
60
57
  inference: {
61
58
  mode: "your-own",
@@ -77,17 +74,22 @@ mock.module("../config/loader.js", () => ({
77
74
  invalidateConfigCache: () => {},
78
75
  getNestedValue: () => undefined,
79
76
  setNestedValue: () => {},
80
- syncConfigToLockfile: () => {},
81
77
  }));
82
78
 
83
79
  const { buildSystemPrompt } = await import("../prompts/system-prompt.js");
80
+ const { _setOverridesForTesting } =
81
+ await import("../config/assistant-feature-flags.js");
84
82
 
85
83
  describe("Dynamic Skill Authoring Workflow moved to tool descriptions", () => {
86
84
  beforeEach(() => {
87
85
  mkdirSync(TEST_DIR, { recursive: true });
86
+ _setOverridesForTesting({
87
+ "feature_flags.browser.enabled": true,
88
+ });
88
89
  });
89
90
 
90
91
  afterEach(() => {
92
+ _setOverridesForTesting({});
91
93
  if (existsSync(TEST_DIR)) {
92
94
  rmSync(TEST_DIR, { recursive: true, force: true });
93
95
  }
@@ -1,6 +1,9 @@
1
1
  import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
2
2
 
3
- import { deliverChannelReply } from "../runtime/gateway-client.js";
3
+ import {
4
+ ChannelDeliveryError,
5
+ deliverChannelReply,
6
+ } from "../runtime/gateway-client.js";
4
7
 
5
8
  type FetchCall = {
6
9
  url: string;
@@ -163,4 +166,79 @@ describe("gateway-client managed outbound lane", () => {
163
166
  text: "standard gateway callback",
164
167
  });
165
168
  });
169
+
170
+ test("throws ChannelDeliveryError with userMessage when gateway returns JSON error with userMessage", async () => {
171
+ globalThis.fetch = mock(async () => {
172
+ return new Response(
173
+ JSON.stringify({
174
+ error: "Permission denied",
175
+ userMessage:
176
+ "The bot is not a member of this channel. Please invite it first.",
177
+ }),
178
+ { status: 403 },
179
+ );
180
+ }) as unknown as typeof globalThis.fetch;
181
+
182
+ let caught: unknown;
183
+ try {
184
+ await deliverChannelReply("https://gateway.test/deliver/slack", {
185
+ chatId: "C123",
186
+ text: "hello",
187
+ });
188
+ } catch (err) {
189
+ caught = err;
190
+ }
191
+
192
+ expect(caught).toBeInstanceOf(ChannelDeliveryError);
193
+ const deliveryError = caught as ChannelDeliveryError;
194
+ expect(deliveryError.statusCode).toBe(403);
195
+ expect(deliveryError.userMessage).toBe(
196
+ "The bot is not a member of this channel. Please invite it first.",
197
+ );
198
+ expect(deliveryError.message).toContain("403");
199
+ });
200
+
201
+ test("throws ChannelDeliveryError without userMessage when gateway returns JSON error without userMessage", async () => {
202
+ globalThis.fetch = mock(async () => {
203
+ return new Response(JSON.stringify({ error: "Delivery failed" }), {
204
+ status: 502,
205
+ });
206
+ }) as unknown as typeof globalThis.fetch;
207
+
208
+ let caught: unknown;
209
+ try {
210
+ await deliverChannelReply("https://gateway.test/deliver/slack", {
211
+ chatId: "C123",
212
+ text: "hello",
213
+ });
214
+ } catch (err) {
215
+ caught = err;
216
+ }
217
+
218
+ expect(caught).toBeInstanceOf(ChannelDeliveryError);
219
+ const deliveryError = caught as ChannelDeliveryError;
220
+ expect(deliveryError.statusCode).toBe(502);
221
+ expect(deliveryError.userMessage).toBeUndefined();
222
+ });
223
+
224
+ test("throws ChannelDeliveryError without userMessage when gateway returns non-JSON error", async () => {
225
+ globalThis.fetch = mock(async () => {
226
+ return new Response("Internal Server Error", { status: 500 });
227
+ }) as unknown as typeof globalThis.fetch;
228
+
229
+ let caught: unknown;
230
+ try {
231
+ await deliverChannelReply("https://gateway.test/deliver/slack", {
232
+ chatId: "C123",
233
+ text: "hello",
234
+ });
235
+ } catch (err) {
236
+ caught = err;
237
+ }
238
+
239
+ expect(caught).toBeInstanceOf(ChannelDeliveryError);
240
+ const deliveryError = caught as ChannelDeliveryError;
241
+ expect(deliveryError.statusCode).toBe(500);
242
+ expect(deliveryError.userMessage).toBeUndefined();
243
+ });
166
244
  });
@@ -31,11 +31,6 @@ mock.module("../util/logger.js", () => ({
31
31
  }),
32
32
  }));
33
33
 
34
- // Mock security check to always pass
35
- mock.module("../security/secret-ingress.js", () => ({
36
- checkIngressForSecrets: () => ({ blocked: false }),
37
- }));
38
-
39
34
  import { upsertContact } from "../contacts/contact-store.js";
40
35
  import { createGuardianBinding } from "../contacts/contacts-write.js";
41
36
  import type { TrustContext } from "../daemon/conversation-runtime-assembly.js";
@@ -846,12 +846,12 @@ describe("host_bash — proxy delegation", () => {
846
846
  });
847
847
 
848
848
  test("propagates VELLUM_UNTRUSTED_SHELL env to proxy under CES lockdown", async () => {
849
- // Enable CES shell lockdown
850
- const origFlags = (mockConfig as Record<string, unknown>)
851
- .assistantFeatureFlagValues;
852
- (mockConfig as Record<string, unknown>).assistantFeatureFlagValues = {
849
+ // Enable CES shell lockdown via the override cache
850
+ const { _setOverridesForTesting } =
851
+ await import("../config/assistant-feature-flags.js");
852
+ _setOverridesForTesting({
853
853
  "feature_flags.ces-shell-lockdown.enabled": true,
854
- };
854
+ });
855
855
 
856
856
  try {
857
857
  const proxyResult: ToolExecutionResult = {
@@ -875,8 +875,7 @@ describe("host_bash — proxy delegation", () => {
875
875
  expect(calls.length).toBe(1);
876
876
  expect(calls[0].input.env).toEqual({ VELLUM_UNTRUSTED_SHELL: "1" });
877
877
  } finally {
878
- (mockConfig as Record<string, unknown>).assistantFeatureFlagValues =
879
- origFlags;
878
+ _setOverridesForTesting({});
880
879
  }
881
880
  });
882
881
 
@@ -2,10 +2,9 @@
2
2
  * Tests for HTTP POST /v1/messages behavior after the legacy handleUserMessage
3
3
  * legacy entry point was retired.
4
4
  *
5
- * Secret ingress blocking has been ported to the HTTP path. Recording intent
6
- * interception has been deliberately retired the HTTP path has dedicated
7
- * /v1/recording/* endpoints and the model handles recording-related messages
8
- * through the agent loop.
5
+ * Recording intent interception has been deliberately retired the HTTP path
6
+ * has dedicated /v1/recording/* endpoints and the model handles
7
+ * recording-related messages through the agent loop.
9
8
  *
10
9
  * Approval reply interception has parity and is covered by
11
10
  * conversation-routes-guardian-reply.test.ts and send-endpoint-busy.test.ts.
@@ -121,12 +120,10 @@ mock.module("../runtime/trust-context-resolver.js", () => ({
121
120
  }),
122
121
  }));
123
122
 
124
- // Mock config to enable secret detection + ingress blocking
125
123
  mock.module("../config/loader.js", () => ({
126
124
  getConfig: () => ({
127
125
  secretDetection: {
128
126
  enabled: true,
129
- blockIngress: true,
130
127
  customPatterns: [],
131
128
  entropyThreshold: 3.5,
132
129
  },
@@ -238,103 +235,6 @@ async function sendMessage(
238
235
  );
239
236
  }
240
237
 
241
- // ============================================================================
242
- // SECRET INGRESS BLOCKING — now ported to HTTP path
243
- // ============================================================================
244
- describe("HTTP POST /v1/messages blocks secret ingress", () => {
245
- beforeEach(() => {
246
- routeGuardianReplyMock.mockClear();
247
- listPendingByDestinationMock.mockClear();
248
- listCanonicalMock.mockClear();
249
- addMessageMock.mockClear();
250
- });
251
-
252
- test("handleSendMessage rejects messages containing Telegram bot token patterns", async () => {
253
- const secretContent =
254
- "Set up Telegram with my bot token 123456789:ABCDefGHIJklmnopQRSTuvwxyz012345678";
255
- const persistUserMessage = mock(async () => "persisted-msg-id");
256
- const runAgentLoop = mock(async () => undefined);
257
- const conversation = makeConversation({ persistUserMessage, runAgentLoop });
258
-
259
- const res = await sendMessage(secretContent, conversation);
260
-
261
- expect(res.status).toBe(422);
262
- const body = (await res.json()) as {
263
- accepted: boolean;
264
- error: string;
265
- message: string;
266
- detectedTypes: string[];
267
- };
268
- expect(body.accepted).toBe(false);
269
- expect(body.error).toBe("secret_blocked");
270
- expect(body.detectedTypes.length).toBeGreaterThan(0);
271
-
272
- // The message should NOT reach the agent loop
273
- expect(persistUserMessage).toHaveBeenCalledTimes(0);
274
- expect(runAgentLoop).toHaveBeenCalledTimes(0);
275
- });
276
-
277
- test("handleSendMessage rejects messages containing AWS credentials", async () => {
278
- const secretContent =
279
- "Here is my AWS key AKIAQRSTUVWXYZ123456 and secret wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY";
280
- const persistUserMessage = mock(async () => "persisted-msg-id");
281
- const runAgentLoop = mock(async () => undefined);
282
- const conversation = makeConversation({ persistUserMessage, runAgentLoop });
283
-
284
- const res = await sendMessage(secretContent, conversation);
285
-
286
- expect(res.status).toBe(422);
287
- const body = (await res.json()) as {
288
- accepted: boolean;
289
- error: string;
290
- };
291
- expect(body.accepted).toBe(false);
292
- expect(body.error).toBe("secret_blocked");
293
-
294
- // The message should NOT reach the agent loop
295
- expect(persistUserMessage).toHaveBeenCalledTimes(0);
296
- expect(runAgentLoop).toHaveBeenCalledTimes(0);
297
- });
298
-
299
- test("handleSendMessage rejects messages containing Stripe live API keys", async () => {
300
- const secretContent = "My Stripe key is sk_live_4eC39HqLyjWDarjtT1zdp7dc";
301
- const persistUserMessage = mock(async () => "persisted-msg-id");
302
- const runAgentLoop = mock(async () => undefined);
303
- const conversation = makeConversation({ persistUserMessage, runAgentLoop });
304
-
305
- const res = await sendMessage(secretContent, conversation);
306
-
307
- expect(res.status).toBe(422);
308
- const body = (await res.json()) as {
309
- accepted: boolean;
310
- error: string;
311
- };
312
- expect(body.accepted).toBe(false);
313
- expect(body.error).toBe("secret_blocked");
314
-
315
- // The message should NOT reach the agent loop
316
- expect(persistUserMessage).toHaveBeenCalledTimes(0);
317
- expect(runAgentLoop).toHaveBeenCalledTimes(0);
318
- });
319
-
320
- test("handleSendMessage allows normal messages without secrets", async () => {
321
- const normalContent = "What is the weather today?";
322
- const persistUserMessage = mock(async () => "persisted-msg-id");
323
- const runAgentLoop = mock(async () => undefined);
324
- const conversation = makeConversation({ persistUserMessage, runAgentLoop });
325
-
326
- const res = await sendMessage(normalContent, conversation);
327
-
328
- expect(res.status).toBe(202);
329
- const body = (await res.json()) as { accepted: boolean };
330
- expect(body.accepted).toBe(true);
331
-
332
- // Normal messages proceed to the agent loop
333
- expect(persistUserMessage).toHaveBeenCalledTimes(1);
334
- expect(runAgentLoop).toHaveBeenCalledTimes(1);
335
- });
336
- });
337
-
338
238
  // ============================================================================
339
239
  // RECORDING INTENT — deliberately NOT intercepted on HTTP path
340
240
  // ============================================================================
@@ -35,10 +35,6 @@ mock.module("../util/logger.js", () => ({
35
35
  }),
36
36
  }));
37
37
 
38
- mock.module("../security/secret-ingress.js", () => ({
39
- checkIngressForSecrets: () => ({ blocked: false }),
40
- }));
41
-
42
38
  mock.module("../config/env.js", () => ({
43
39
  isHttpAuthDisabled: () => true,
44
40
  getGatewayInternalBaseUrl: () => "http://127.0.0.1:7830",
@@ -18,6 +18,8 @@ import { tmpdir } from "node:os";
18
18
  import { join } from "node:path";
19
19
  import { beforeEach, describe, expect, mock, test } from "bun:test";
20
20
 
21
+ import { _setOverridesForTesting } from "../config/assistant-feature-flags.js";
22
+
21
23
  // ── Mock setup (must be before any imports from the project) ──────────────
22
24
 
23
25
  const testDir = mkdtempSync(join(tmpdir(), "inline-skill-perm-test-"));
@@ -46,7 +48,6 @@ interface TestConfig {
46
48
  permissions: { mode: "strict" | "workspace" };
47
49
  skills: { load: { extraDirs: string[] } };
48
50
  sandbox: { enabled: boolean };
49
- assistantFeatureFlagValues?: Record<string, boolean>;
50
51
  [key: string]: unknown;
51
52
  }
52
53
 
@@ -54,9 +55,6 @@ const testConfig: TestConfig = {
54
55
  permissions: { mode: "workspace" },
55
56
  skills: { load: { extraDirs: [] } },
56
57
  sandbox: { enabled: true },
57
- assistantFeatureFlagValues: {
58
- "feature_flags.inline-skill-commands.enabled": true,
59
- },
60
58
  };
61
59
 
62
60
  mock.module("../config/loader.js", () => ({
@@ -118,9 +116,9 @@ describe("inline-command skill_load permissions", () => {
118
116
  clearCache();
119
117
  testConfig.permissions = { mode: "workspace" };
120
118
  testConfig.skills = { load: { extraDirs: [] } };
121
- testConfig.assistantFeatureFlagValues = {
119
+ _setOverridesForTesting({
122
120
  "feature_flags.inline-skill-commands.enabled": true,
123
- };
121
+ });
124
122
  try {
125
123
  rmSync(join(testDir, "protected", "trust.json"));
126
124
  } catch {
@@ -352,9 +350,9 @@ describe("inline-command skill_load permissions", () => {
352
350
  writeDynamicSkill("dynamic-flag-off", "Dynamic Flag Off Skill");
353
351
 
354
352
  // Disable the feature flag
355
- testConfig.assistantFeatureFlagValues = {
353
+ _setOverridesForTesting({
356
354
  "feature_flags.inline-skill-commands.enabled": false,
357
- };
355
+ });
358
356
 
359
357
  const result = await check(
360
358
  "skill_load",
@@ -53,8 +53,6 @@ mock.module("../util/logger.js", () => ({
53
53
  mock.module("../config/loader.js", () => ({
54
54
  getConfig: () => ({
55
55
  ui: {},
56
-
57
- assistantFeatureFlagValues: {},
58
56
  services: {
59
57
  inference: {
60
58
  mode: "your-own",
@@ -76,7 +74,6 @@ mock.module("../config/loader.js", () => ({
76
74
  invalidateConfigCache: () => {},
77
75
  getNestedValue: () => undefined,
78
76
  setNestedValue: () => {},
79
- syncConfigToLockfile: () => {},
80
77
  }));
81
78
 
82
79
  // ── Import after mocks ───────────────────────────────────────────────
@@ -250,16 +247,6 @@ describe("Activation hints in skills catalog", () => {
250
247
  expect(line).toContain("voice-setup");
251
248
  });
252
249
 
253
- test("orchestration skill includes hints and avoid-when in catalog line", () => {
254
- const prompt = buildSystemPrompt();
255
- const line = prompt
256
- .split("\n")
257
- .find((l) => l.includes("**orchestration**"));
258
- expect(line).toBeDefined();
259
- expect(line).toContain("parallel");
260
- expect(line).toContain("Single-focus");
261
- });
262
-
263
250
  test("browser skill includes hints in catalog line", () => {
264
251
  const prompt = buildSystemPrompt();
265
252
  const line = prompt.split("\n").find((l) => l.includes("**browser**"));