@vellumai/assistant 0.5.6 → 0.5.7

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 (305) hide show
  1. package/.env.example +16 -2
  2. package/ARCHITECTURE.md +6 -75
  3. package/Dockerfile +1 -1
  4. package/README.md +0 -2
  5. package/bun.lock +0 -414
  6. package/docs/architecture/keychain-broker.md +45 -240
  7. package/docs/architecture/security.md +0 -17
  8. package/docs/credential-execution-service.md +2 -2
  9. package/node_modules/@vellumai/ces-contracts/package.json +1 -0
  10. package/node_modules/@vellumai/ces-contracts/src/rpc.ts +119 -0
  11. package/node_modules/@vellumai/credential-storage/package.json +1 -0
  12. package/node_modules/@vellumai/egress-proxy/package.json +1 -0
  13. package/package.json +2 -3
  14. package/src/__tests__/actor-token-service.test.ts +0 -114
  15. package/src/__tests__/assistant-feature-flags-integration.test.ts +30 -29
  16. package/src/__tests__/browser-skill-endstate.test.ts +6 -5
  17. package/src/__tests__/btw-routes.test.ts +0 -39
  18. package/src/__tests__/call-domain.test.ts +0 -128
  19. package/src/__tests__/ces-rpc-credential-backend.test.ts +199 -0
  20. package/src/__tests__/channel-approval-routes.test.ts +0 -5
  21. package/src/__tests__/channel-readiness-service.test.ts +1 -60
  22. package/src/__tests__/checker.test.ts +4 -2
  23. package/src/__tests__/cli-command-risk-guard.test.ts +112 -0
  24. package/src/__tests__/config-schema-cmd.test.ts +0 -1
  25. package/src/__tests__/config-schema.test.ts +1 -1
  26. package/src/__tests__/conversation-attention-telegram.test.ts +0 -5
  27. package/src/__tests__/conversation-init.benchmark.test.ts +0 -2
  28. package/src/__tests__/conversation-skill-tools.test.ts +0 -54
  29. package/src/__tests__/conversation-title-service.test.ts +87 -0
  30. package/src/__tests__/credential-execution-feature-gates.test.ts +28 -14
  31. package/src/__tests__/credential-execution-managed-contract.test.ts +33 -18
  32. package/src/__tests__/credential-security-e2e.test.ts +0 -66
  33. package/src/__tests__/credential-security-invariants.test.ts +4 -45
  34. package/src/__tests__/credentials-cli.test.ts +78 -0
  35. package/src/__tests__/db-migration-rollback.test.ts +2015 -1
  36. package/src/__tests__/docker-signing-key-bootstrap.test.ts +34 -143
  37. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +6 -4
  38. package/src/__tests__/guardian-routing-state.test.ts +0 -5
  39. package/src/__tests__/host-shell-tool.test.ts +6 -7
  40. package/src/__tests__/http-user-message-parity.test.ts +3 -103
  41. package/src/__tests__/inbound-invite-redemption.test.ts +0 -4
  42. package/src/__tests__/inline-skill-load-permissions.test.ts +6 -8
  43. package/src/__tests__/intent-routing.test.ts +0 -13
  44. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +178 -0
  45. package/src/__tests__/keychain-broker-client.test.ts +161 -22
  46. package/src/__tests__/memory-jobs-worker-backoff.test.ts +150 -0
  47. package/src/__tests__/migration-export-http.test.ts +2 -2
  48. package/src/__tests__/migration-import-commit-http.test.ts +2 -2
  49. package/src/__tests__/migration-import-preflight-http.test.ts +2 -2
  50. package/src/__tests__/migration-validate-http.test.ts +2 -2
  51. package/src/__tests__/non-member-access-request.test.ts +0 -5
  52. package/src/__tests__/notification-decision-fallback.test.ts +4 -0
  53. package/src/__tests__/notification-decision-identity.test.ts +4 -0
  54. package/src/__tests__/permission-types.test.ts +1 -0
  55. package/src/__tests__/provider-managed-proxy-integration.test.ts +5 -6
  56. package/src/__tests__/qdrant-manager.test.ts +28 -2
  57. package/src/__tests__/registry.test.ts +0 -6
  58. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -4
  59. package/src/__tests__/secret-routes-managed-proxy.test.ts +0 -4
  60. package/src/__tests__/secure-keys.test.ts +83 -263
  61. package/src/__tests__/shell-identity.test.ts +96 -6
  62. package/src/__tests__/skill-feature-flags-integration.test.ts +22 -14
  63. package/src/__tests__/skill-feature-flags.test.ts +46 -45
  64. package/src/__tests__/skill-load-feature-flag.test.ts +7 -10
  65. package/src/__tests__/skill-load-inline-command.test.ts +8 -12
  66. package/src/__tests__/skill-load-inline-includes.test.ts +6 -10
  67. package/src/__tests__/skill-load-tool.test.ts +0 -2
  68. package/src/__tests__/skill-projection-feature-flag.test.ts +33 -29
  69. package/src/__tests__/skills.test.ts +0 -2
  70. package/src/__tests__/slack-inbound-verification.test.ts +0 -4
  71. package/src/__tests__/suggestion-routes.test.ts +1 -32
  72. package/src/__tests__/system-prompt.test.ts +0 -1
  73. package/src/__tests__/tool-executor-shell-integration.test.ts +5 -3
  74. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +0 -5
  75. package/src/__tests__/trusted-contact-multichannel.test.ts +0 -4
  76. package/src/__tests__/update-bulletin.test.ts +0 -2
  77. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +6 -9
  78. package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -6
  79. package/src/__tests__/workspace-migration-015-migrate-credentials-to-keychain.test.ts +252 -0
  80. package/src/__tests__/workspace-migration-016-migrate-credentials-from-keychain.test.ts +218 -0
  81. package/src/__tests__/workspace-migration-down-functions.test.ts +1009 -0
  82. package/src/__tests__/workspace-migrations-runner.test.ts +114 -0
  83. package/src/calls/audio-store.test.ts +97 -0
  84. package/src/calls/audio-store.ts +205 -0
  85. package/src/calls/call-controller.ts +85 -7
  86. package/src/calls/call-domain.ts +3 -0
  87. package/src/calls/call-store.ts +10 -3
  88. package/src/calls/fish-audio-client.ts +117 -0
  89. package/src/calls/relay-server.ts +27 -0
  90. package/src/calls/twilio-routes.ts +2 -1
  91. package/src/calls/types.ts +1 -0
  92. package/src/calls/voice-ingress-preflight.ts +0 -42
  93. package/src/calls/voice-quality.ts +26 -5
  94. package/src/calls/voice-session-bridge.ts +6 -12
  95. package/src/cli/commands/config.ts +1 -4
  96. package/src/cli/commands/credentials.ts +34 -4
  97. package/src/cli/commands/oauth/index.ts +7 -0
  98. package/src/cli/commands/oauth/platform.ts +179 -0
  99. package/src/cli/commands/platform.ts +3 -3
  100. package/src/config/assistant-feature-flags.ts +186 -5
  101. package/src/config/bundled-skills/messaging/SKILL.md +5 -5
  102. package/src/config/bundled-skills/phone-calls/TOOLS.json +4 -0
  103. package/src/config/bundled-skills/settings/TOOLS.json +2 -2
  104. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +42 -0
  105. package/src/config/bundled-tool-registry.ts +1 -11
  106. package/src/config/env-registry.ts +1 -1
  107. package/src/config/env.ts +8 -14
  108. package/src/config/feature-flag-registry.json +48 -8
  109. package/src/config/loader.ts +98 -31
  110. package/src/config/schema.ts +4 -13
  111. package/src/config/schemas/calls.ts +13 -0
  112. package/src/config/schemas/fish-audio.ts +39 -0
  113. package/src/config/schemas/security.ts +0 -4
  114. package/src/config/types.ts +0 -1
  115. package/src/contacts/contact-store.ts +39 -0
  116. package/src/contacts/types.ts +2 -0
  117. package/src/credential-execution/approval-bridge.ts +1 -0
  118. package/src/credential-execution/executable-discovery.ts +28 -4
  119. package/src/credential-execution/feature-gates.ts +16 -0
  120. package/src/credential-execution/process-manager.ts +38 -0
  121. package/src/daemon/assistant-attachments.ts +9 -0
  122. package/src/daemon/config-watcher.ts +5 -0
  123. package/src/daemon/conversation-tool-setup.ts +0 -105
  124. package/src/daemon/conversation.ts +10 -1
  125. package/src/daemon/handlers/config-vercel.ts +92 -0
  126. package/src/daemon/handlers/skills.ts +2 -15
  127. package/src/daemon/install-symlink.ts +195 -0
  128. package/src/daemon/lifecycle.ts +227 -51
  129. package/src/daemon/message-types/conversations.ts +3 -4
  130. package/src/daemon/message-types/diagnostics.ts +3 -22
  131. package/src/daemon/message-types/messages.ts +0 -2
  132. package/src/daemon/message-types/upgrades.ts +8 -0
  133. package/src/daemon/server.ts +30 -92
  134. package/src/events/domain-events.ts +2 -1
  135. package/src/inbound/platform-callback-registration.ts +3 -3
  136. package/src/instrument.ts +8 -5
  137. package/src/memory/conversation-title-service.ts +50 -1
  138. package/src/memory/db-init.ts +12 -0
  139. package/src/memory/items-extractor.ts +15 -1
  140. package/src/memory/job-handlers/conversation-starters.ts +4 -1
  141. package/src/memory/jobs-store.ts +30 -5
  142. package/src/memory/jobs-worker.ts +31 -7
  143. package/src/memory/migrations/001-job-deferrals.ts +19 -0
  144. package/src/memory/migrations/004-entity-relation-dedup.ts +10 -0
  145. package/src/memory/migrations/005-fingerprint-scope-unique.ts +76 -0
  146. package/src/memory/migrations/006-scope-salted-fingerprints.ts +50 -0
  147. package/src/memory/migrations/007-assistant-id-to-self.ts +10 -0
  148. package/src/memory/migrations/008-remove-assistant-id-columns.ts +34 -0
  149. package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +26 -0
  150. package/src/memory/migrations/014-backfill-inbox-thread-state.ts +10 -0
  151. package/src/memory/migrations/015-drop-active-search-index.ts +17 -0
  152. package/src/memory/migrations/019-notification-tables-schema-migration.ts +12 -0
  153. package/src/memory/migrations/020-rename-macos-ios-channel-to-vellum.ts +121 -0
  154. package/src/memory/migrations/024-embedding-vector-blob.ts +74 -0
  155. package/src/memory/migrations/026a-embeddings-nullable-vector-json.ts +82 -0
  156. package/src/memory/migrations/036-normalize-phone-identities.ts +11 -0
  157. package/src/memory/migrations/116-messages-fts.ts +106 -1
  158. package/src/memory/migrations/126-backfill-guardian-principal-id.ts +52 -0
  159. package/src/memory/migrations/127-guardian-principal-id-not-null.ts +77 -0
  160. package/src/memory/migrations/134-contacts-notes-column.ts +13 -0
  161. package/src/memory/migrations/135-backfill-contact-interaction-stats.ts +20 -0
  162. package/src/memory/migrations/136-drop-assistant-id-columns.ts +52 -0
  163. package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +13 -0
  164. package/src/memory/migrations/141-rename-verification-table.ts +54 -0
  165. package/src/memory/migrations/142-rename-verification-session-id-column.ts +25 -0
  166. package/src/memory/migrations/143-rename-guardian-verification-values.ts +35 -0
  167. package/src/memory/migrations/144-rename-voice-to-phone.ts +136 -0
  168. package/src/memory/migrations/145-drop-accounts-table.ts +32 -0
  169. package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +14 -1
  170. package/src/memory/migrations/148-drop-reminders-table.ts +35 -1
  171. package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +69 -1
  172. package/src/memory/migrations/162-guardian-timestamps-epoch-ms.ts +290 -0
  173. package/src/memory/migrations/169-rename-gmail-provider-key-to-google.ts +51 -1
  174. package/src/memory/migrations/174-rename-thread-starters-table.ts +47 -1
  175. package/src/memory/migrations/176-drop-capability-card-state.ts +13 -0
  176. package/src/memory/migrations/180-backfill-inline-attachments-to-disk.ts +16 -0
  177. package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +28 -1
  178. package/src/memory/migrations/190-call-session-skip-disclosure.ts +15 -0
  179. package/src/memory/migrations/191-backfill-audio-attachment-mime-types.ts +64 -0
  180. package/src/memory/migrations/192-contacts-user-file-column.ts +15 -0
  181. package/src/memory/migrations/index.ts +4 -0
  182. package/src/memory/migrations/registry.ts +90 -0
  183. package/src/memory/migrations/validate-migration-state.ts +137 -11
  184. package/src/memory/qdrant-circuit-breaker.ts +9 -0
  185. package/src/memory/qdrant-manager.ts +64 -7
  186. package/src/memory/schema/calls.ts +1 -0
  187. package/src/memory/schema/contacts.ts +1 -0
  188. package/src/notifications/decision-engine.ts +4 -1
  189. package/src/oauth/connection-resolver.ts +6 -4
  190. package/src/permissions/checker.ts +0 -38
  191. package/src/permissions/shell-identity.ts +76 -22
  192. package/src/permissions/types.ts +4 -2
  193. package/src/platform/client.ts +35 -7
  194. package/src/prompts/persona-resolver.ts +138 -0
  195. package/src/prompts/system-prompt.ts +36 -4
  196. package/src/prompts/templates/users/default.md +1 -0
  197. package/src/providers/registry.ts +27 -40
  198. package/src/runtime/auth/__tests__/credential-service.test.ts +0 -1
  199. package/src/runtime/auth/__tests__/external-assistant-id.test.ts +13 -68
  200. package/src/runtime/auth/external-assistant-id.ts +13 -59
  201. package/src/runtime/auth/route-policy.ts +15 -1
  202. package/src/runtime/auth/token-service.ts +43 -138
  203. package/src/runtime/channel-readiness-service.ts +1 -16
  204. package/src/runtime/http-server.ts +27 -2
  205. package/src/runtime/middleware/error-handler.ts +1 -9
  206. package/src/runtime/routes/audio-routes.ts +40 -0
  207. package/src/runtime/routes/btw-routes.ts +0 -17
  208. package/src/runtime/routes/conversation-query-routes.ts +63 -1
  209. package/src/runtime/routes/conversation-routes.ts +4 -44
  210. package/src/runtime/routes/diagnostics-routes.ts +1 -477
  211. package/src/runtime/routes/identity-routes.ts +18 -29
  212. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +4 -33
  213. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +1 -1
  214. package/src/runtime/routes/integrations/vercel.ts +89 -0
  215. package/src/runtime/routes/log-export-routes.ts +5 -0
  216. package/src/runtime/routes/memory-item-routes.ts +24 -6
  217. package/src/runtime/routes/migration-rollback-routes.ts +209 -0
  218. package/src/runtime/routes/migration-routes.ts +17 -1
  219. package/src/runtime/routes/notification-routes.ts +58 -0
  220. package/src/runtime/routes/schedule-routes.ts +65 -0
  221. package/src/runtime/routes/settings-routes.ts +41 -1
  222. package/src/runtime/routes/tts-routes.ts +86 -0
  223. package/src/runtime/routes/upgrade-broadcast-routes.ts +26 -2
  224. package/src/runtime/routes/workspace-commit-routes.ts +62 -0
  225. package/src/runtime/routes/workspace-routes.test.ts +22 -1
  226. package/src/runtime/routes/workspace-routes.ts +1 -1
  227. package/src/runtime/routes/workspace-utils.ts +86 -2
  228. package/src/security/ces-credential-client.ts +59 -22
  229. package/src/security/ces-rpc-credential-backend.ts +85 -0
  230. package/src/security/credential-backend.ts +12 -88
  231. package/src/security/keychain-broker-client.ts +10 -2
  232. package/src/security/secure-keys.ts +94 -113
  233. package/src/skills/catalog-install.ts +13 -7
  234. package/src/telemetry/usage-telemetry-reporter.ts +4 -2
  235. package/src/tools/calls/call-start.ts +1 -0
  236. package/src/tools/executor.ts +0 -4
  237. package/src/tools/network/script-proxy/session-manager.ts +19 -4
  238. package/src/tools/network/web-fetch.ts +3 -1
  239. package/src/tools/skills/execute.ts +1 -1
  240. package/src/tools/types.ts +0 -8
  241. package/src/util/errors.ts +0 -12
  242. package/src/util/platform.ts +3 -50
  243. package/src/workspace/git-service.ts +5 -2
  244. package/src/workspace/migrations/001-avatar-rename.ts +15 -0
  245. package/src/workspace/migrations/003-seed-device-id.ts +17 -1
  246. package/src/workspace/migrations/004-extract-collect-usage-data.ts +33 -0
  247. package/src/workspace/migrations/005-add-send-diagnostics.ts +3 -0
  248. package/src/workspace/migrations/006-services-config.ts +49 -0
  249. package/src/workspace/migrations/007-web-search-provider-rename.ts +27 -0
  250. package/src/workspace/migrations/008-voice-timeout-and-max-steps.ts +3 -0
  251. package/src/workspace/migrations/009-backfill-conversation-disk-view.ts +4 -0
  252. package/src/workspace/migrations/010-app-dir-rename.ts +78 -0
  253. package/src/workspace/migrations/011-backfill-installation-id.ts +11 -0
  254. package/src/workspace/migrations/012-rename-conversation-disk-view-dirs.ts +44 -0
  255. package/src/workspace/migrations/013-repair-conversation-disk-view.ts +5 -0
  256. package/src/workspace/migrations/015-migrate-credentials-to-keychain.ts +153 -0
  257. package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +156 -0
  258. package/src/workspace/migrations/016-migrate-credentials-from-keychain.ts +150 -0
  259. package/src/workspace/migrations/017-seed-persona-dirs.ts +95 -0
  260. package/src/workspace/migrations/migrate-to-workspace-volume.ts +23 -1
  261. package/src/workspace/migrations/registry.ts +8 -0
  262. package/src/workspace/migrations/runner.ts +106 -2
  263. package/src/workspace/migrations/types.ts +4 -0
  264. package/src/__tests__/claude-code-skill-regression.test.ts +0 -206
  265. package/src/__tests__/claude-code-tool-profiles.test.ts +0 -99
  266. package/src/__tests__/diagnostics-export.test.ts +0 -288
  267. package/src/__tests__/local-gateway-health.test.ts +0 -209
  268. package/src/__tests__/secret-ingress-handler.test.ts +0 -120
  269. package/src/__tests__/swarm-conversation-integration.test.ts +0 -358
  270. package/src/__tests__/swarm-dag-pathological.test.ts +0 -547
  271. package/src/__tests__/swarm-orchestrator.test.ts +0 -463
  272. package/src/__tests__/swarm-plan-validator.test.ts +0 -384
  273. package/src/__tests__/swarm-recursion.test.ts +0 -197
  274. package/src/__tests__/swarm-router-planner.test.ts +0 -234
  275. package/src/__tests__/swarm-tool.test.ts +0 -185
  276. package/src/__tests__/swarm-worker-backend.test.ts +0 -144
  277. package/src/__tests__/swarm-worker-runner.test.ts +0 -288
  278. package/src/commands/__tests__/cc-command-registry.test.ts +0 -396
  279. package/src/commands/cc-command-registry.ts +0 -248
  280. package/src/config/bundled-skills/claude-code/SKILL.md +0 -53
  281. package/src/config/bundled-skills/claude-code/TOOLS.json +0 -47
  282. package/src/config/bundled-skills/claude-code/tools/claude-code.ts +0 -12
  283. package/src/config/bundled-skills/orchestration/SKILL.md +0 -33
  284. package/src/config/bundled-skills/orchestration/TOOLS.json +0 -35
  285. package/src/config/bundled-skills/orchestration/tools/swarm-delegate.ts +0 -12
  286. package/src/config/schemas/swarm.ts +0 -82
  287. package/src/logfire.ts +0 -135
  288. package/src/runtime/local-gateway-health.ts +0 -275
  289. package/src/security/secret-ingress.ts +0 -68
  290. package/src/swarm/backend-claude-code.ts +0 -225
  291. package/src/swarm/checkpoint.ts +0 -137
  292. package/src/swarm/graph-utils.ts +0 -53
  293. package/src/swarm/index.ts +0 -55
  294. package/src/swarm/limits.ts +0 -66
  295. package/src/swarm/orchestrator.ts +0 -424
  296. package/src/swarm/plan-validator.ts +0 -117
  297. package/src/swarm/router-planner.ts +0 -162
  298. package/src/swarm/router-prompts.ts +0 -39
  299. package/src/swarm/synthesizer.ts +0 -81
  300. package/src/swarm/types.ts +0 -72
  301. package/src/swarm/worker-backend.ts +0 -131
  302. package/src/swarm/worker-prompts.ts +0 -80
  303. package/src/swarm/worker-runner.ts +0 -170
  304. package/src/tools/claude-code/claude-code.ts +0 -610
  305. package/src/tools/swarm/delegate.ts +0 -205
@@ -8,9 +8,12 @@
8
8
  * behavior or existing feature flags.
9
9
  */
10
10
 
11
- import { describe, expect, test } from "bun:test";
11
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
12
12
 
13
- import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
13
+ import {
14
+ _setOverridesForTesting,
15
+ isAssistantFeatureFlagEnabled,
16
+ } from "../config/assistant-feature-flags.js";
14
17
  import type { AssistantConfig } from "../config/schema.js";
15
18
  import {
16
19
  CES_GRANT_AUDIT_FLAG_KEY,
@@ -25,15 +28,21 @@ import {
25
28
  isCesToolsEnabled,
26
29
  } from "../credential-execution/feature-gates.js";
27
30
 
31
+ beforeEach(() => {
32
+ _setOverridesForTesting({});
33
+ });
34
+
35
+ afterEach(() => {
36
+ _setOverridesForTesting({});
37
+ });
38
+
28
39
  // ---------------------------------------------------------------------------
29
40
  // Helpers
30
41
  // ---------------------------------------------------------------------------
31
42
 
32
- /** Create a minimal AssistantConfig with optional feature flag values. */
33
- function makeConfig(flagOverrides?: Record<string, boolean>): AssistantConfig {
34
- return {
35
- ...(flagOverrides ? { assistantFeatureFlagValues: flagOverrides } : {}),
36
- } as AssistantConfig;
43
+ /** Create a minimal AssistantConfig (flag overrides are now set via _setOverridesForTesting). */
44
+ function makeConfig(): AssistantConfig {
45
+ return {} as AssistantConfig;
37
46
  }
38
47
 
39
48
  /** All CES flag keys for iteration. */
@@ -87,16 +96,16 @@ describe("CES flag key format", () => {
87
96
  // ---------------------------------------------------------------------------
88
97
 
89
98
  describe("CES flags default safely (all disabled)", () => {
90
- const config = makeConfig();
91
-
92
99
  for (const { name, fn } of ALL_CES_PREDICATES) {
93
100
  test(`${name} returns false with no config overrides`, () => {
101
+ const config = makeConfig();
94
102
  expect(fn(config)).toBe(false);
95
103
  });
96
104
  }
97
105
 
98
106
  for (const key of ALL_CES_FLAG_KEYS) {
99
107
  test(`isAssistantFeatureFlagEnabled('${key}') returns false with no overrides`, () => {
108
+ const config = makeConfig();
100
109
  expect(isAssistantFeatureFlagEnabled(key, config)).toBe(false);
101
110
  });
102
111
  }
@@ -109,12 +118,14 @@ describe("CES flags default safely (all disabled)", () => {
109
118
  describe("CES flags can be enabled independently", () => {
110
119
  for (const { name, fn, key } of ALL_CES_PREDICATES) {
111
120
  test(`enabling ${key} makes ${name} return true`, () => {
112
- const config = makeConfig({ [key]: true });
121
+ _setOverridesForTesting({ [key]: true });
122
+ const config = makeConfig();
113
123
  expect(fn(config)).toBe(true);
114
124
  });
115
125
 
116
126
  test(`enabling ${key} does not enable other CES flags`, () => {
117
- const config = makeConfig({ [key]: true });
127
+ _setOverridesForTesting({ [key]: true });
128
+ const config = makeConfig();
118
129
  for (const { fn: otherFn, key: otherKey } of ALL_CES_PREDICATES) {
119
130
  if (otherKey === key) continue;
120
131
  expect(otherFn(config)).toBe(false);
@@ -130,7 +141,8 @@ describe("CES flags can be enabled independently", () => {
130
141
  describe("CES flags respect explicit false overrides", () => {
131
142
  for (const { name, fn, key } of ALL_CES_PREDICATES) {
132
143
  test(`${name} returns false when explicitly set to false`, () => {
133
- const config = makeConfig({ [key]: false });
144
+ _setOverridesForTesting({ [key]: false });
145
+ const config = makeConfig();
134
146
  expect(fn(config)).toBe(false);
135
147
  });
136
148
  }
@@ -146,7 +158,8 @@ describe("CES flags do not affect unrelated flags", () => {
146
158
  for (const key of ALL_CES_FLAG_KEYS) {
147
159
  overrides[key] = true;
148
160
  }
149
- const config = makeConfig(overrides);
161
+ _setOverridesForTesting(overrides);
162
+ const config = makeConfig();
150
163
 
151
164
  // browser defaults to true in the registry and should stay true
152
165
  expect(
@@ -159,7 +172,8 @@ describe("CES flags do not affect unrelated flags", () => {
159
172
  for (const key of ALL_CES_FLAG_KEYS) {
160
173
  overrides[key] = true;
161
174
  }
162
- const config = makeConfig(overrides);
175
+ _setOverridesForTesting(overrides);
176
+ const config = makeConfig();
163
177
 
164
178
  // contacts defaults to true in the registry and should stay true
165
179
  expect(
@@ -26,7 +26,17 @@
26
26
  * contracts — no real CES process or socket dependencies are needed.
27
27
  */
28
28
 
29
- import { describe, expect, test } from "bun:test";
29
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
30
+
31
+ import { _setOverridesForTesting } from "../config/assistant-feature-flags.js";
32
+
33
+ beforeEach(() => {
34
+ _setOverridesForTesting({});
35
+ });
36
+
37
+ afterEach(() => {
38
+ _setOverridesForTesting({});
39
+ });
30
40
 
31
41
  import {
32
42
  CES_PROTOCOL_VERSION,
@@ -58,11 +68,9 @@ import {
58
68
  // Helpers
59
69
  // ---------------------------------------------------------------------------
60
70
 
61
- /** Create a minimal AssistantConfig with optional feature flag values. */
62
- function makeConfig(flagOverrides?: Record<string, boolean>): AssistantConfig {
63
- return {
64
- ...(flagOverrides ? { assistantFeatureFlagValues: flagOverrides } : {}),
65
- } as AssistantConfig;
71
+ /** Create a minimal AssistantConfig (flag overrides are now set via _setOverridesForTesting). */
72
+ function makeConfig(): AssistantConfig {
73
+ return {} as AssistantConfig;
66
74
  }
67
75
 
68
76
  // ---------------------------------------------------------------------------
@@ -444,32 +452,36 @@ describe("feature-flag rollback safety", () => {
444
452
  });
445
453
 
446
454
  test("managed sidecar flag can be explicitly enabled", () => {
447
- const config = makeConfig({
455
+ _setOverridesForTesting({
448
456
  "feature_flags.ces-managed-sidecar.enabled": true,
449
457
  });
458
+ const config = makeConfig();
450
459
  expect(isCesManagedSidecarEnabled(config)).toBe(true);
451
460
  });
452
461
 
453
462
  test("managed sidecar flag can be explicitly disabled", () => {
454
- const config = makeConfig({
463
+ _setOverridesForTesting({
455
464
  "feature_flags.ces-managed-sidecar.enabled": false,
456
465
  });
466
+ const config = makeConfig();
457
467
  expect(isCesManagedSidecarEnabled(config)).toBe(false);
458
468
  });
459
469
 
460
470
  test("enabling managed sidecar does not enable CES tools", () => {
461
- const config = makeConfig({
471
+ _setOverridesForTesting({
462
472
  "feature_flags.ces-managed-sidecar.enabled": true,
463
473
  });
474
+ const config = makeConfig();
464
475
  // CES tools flag should remain independently controlled
465
476
  expect(isCesToolsEnabled(config)).toBe(false);
466
477
  });
467
478
 
468
479
  test("disabling managed sidecar does not affect other CES flags", () => {
469
- const config = makeConfig({
480
+ _setOverridesForTesting({
470
481
  "feature_flags.ces-managed-sidecar.enabled": false,
471
482
  "feature_flags.ces-tools.enabled": true,
472
483
  });
484
+ const config = makeConfig();
473
485
  expect(isCesToolsEnabled(config)).toBe(true);
474
486
  });
475
487
  });
@@ -480,10 +492,11 @@ describe("feature-flag rollback safety", () => {
480
492
 
481
493
  describe("process manager config wiring", () => {
482
494
  test("CesProcessManagerConfig accepts assistantConfig for flag gating", () => {
495
+ _setOverridesForTesting({
496
+ "feature_flags.ces-managed-sidecar.enabled": true,
497
+ });
483
498
  const config: CesProcessManagerConfig = {
484
- assistantConfig: makeConfig({
485
- "feature_flags.ces-managed-sidecar.enabled": true,
486
- }),
499
+ assistantConfig: makeConfig(),
487
500
  };
488
501
  expect(config.assistantConfig).toBeDefined();
489
502
  expect(isCesManagedSidecarEnabled(config.assistantConfig!)).toBe(true);
@@ -491,16 +504,17 @@ describe("process manager config wiring", () => {
491
504
 
492
505
  test("CesProcessManagerConfig requires assistantConfig for feature-flag gate", () => {
493
506
  const config: CesProcessManagerConfig = {
494
- assistantConfig: makeConfig({}),
507
+ assistantConfig: makeConfig(),
495
508
  };
496
509
  expect(config.assistantConfig).toBeDefined();
497
510
  });
498
511
 
499
512
  test("when flag is off, managed discovery is skipped", () => {
513
+ _setOverridesForTesting({
514
+ "feature_flags.ces-managed-sidecar.enabled": false,
515
+ });
500
516
  const config: CesProcessManagerConfig = {
501
- assistantConfig: makeConfig({
502
- "feature_flags.ces-managed-sidecar.enabled": false,
503
- }),
517
+ assistantConfig: makeConfig(),
504
518
  };
505
519
  // The managed path should be gated
506
520
  expect(isCesManagedSidecarEnabled(config.assistantConfig!)).toBe(false);
@@ -513,9 +527,10 @@ describe("process manager config wiring", () => {
513
527
 
514
528
  describe("non-CES internal consumers intact when flag is off", () => {
515
529
  test("existing non-agent flows are unaffected by managed sidecar flag", () => {
516
- const config = makeConfig({
530
+ _setOverridesForTesting({
517
531
  "feature_flags.ces-managed-sidecar.enabled": false,
518
532
  });
533
+ const config = makeConfig();
519
534
  expect(isCesManagedSidecarEnabled(config)).toBe(false);
520
535
  });
521
536
 
@@ -10,7 +10,6 @@ const mockConfig = {
10
10
  action: "block" as "redact" | "warn" | "block",
11
11
  entropyThreshold: 4.0,
12
12
  allowOneTimeSend: false,
13
- blockIngress: true,
14
13
  },
15
14
  timeouts: { permissionTimeoutSec: 300 },
16
15
  };
@@ -118,8 +117,6 @@ mock.module("../tools/credentials/policy-validate.js", () => ({
118
117
 
119
118
  // Import modules under test
120
119
  const { credentialStoreTool } = await import("../tools/credentials/vault.js");
121
- const { checkIngressForSecrets } =
122
- await import("../security/secret-ingress.js");
123
120
  const { isToolAllowed } = await import("../tools/credentials/tool-policy.js");
124
121
  const { isDomainAllowed } =
125
122
  await import("../tools/credentials/domain-policy.js");
@@ -203,61 +200,6 @@ describe("E2E: secure store and list lifecycle", () => {
203
200
  });
204
201
  });
205
202
 
206
- // ---------------------------------------------------------------------------
207
- // E2E Scenario 2 — Secret-in-chat blocked and redirected
208
- // ---------------------------------------------------------------------------
209
-
210
- describe("E2E: secret ingress blocking", () => {
211
- beforeEach(() => {
212
- mockConfig.secretDetection.enabled = true;
213
- mockConfig.secretDetection.action = "block";
214
- });
215
-
216
- test("blocks message containing AWS access key", () => {
217
- const awsKey = ["AKIA", "IOSFODNN7", "REALKEY"].join("");
218
- const check = checkIngressForSecrets(`Here is my key: ${awsKey}`);
219
- expect(check.blocked).toBe(true);
220
- expect(check.detectedTypes.length).toBeGreaterThan(0);
221
- // Notice must never contain the actual secret
222
- expect(check.userNotice).toBeDefined();
223
- expect(check.userNotice).not.toContain(awsKey);
224
- });
225
-
226
- test("blocks message containing GitHub token", () => {
227
- const ghToken = ["ghp_", "ABCDEFghijklMN01234567", "89abcdef"].join("");
228
- const check = checkIngressForSecrets(`Use this token: ${ghToken}`);
229
- expect(check.blocked).toBe(true);
230
- });
231
-
232
- test("allows normal text through", () => {
233
- const check = checkIngressForSecrets("Please help me configure my project");
234
- expect(check.blocked).toBe(false);
235
- expect(check.detectedTypes).toEqual([]);
236
- });
237
-
238
- test("bypasses when detection is disabled", () => {
239
- mockConfig.secretDetection.enabled = false;
240
- const awsKey = ["AKIA", "IOSFODNN7", "REALKEY"].join("");
241
- const check = checkIngressForSecrets(awsKey);
242
- expect(check.blocked).toBe(false);
243
- });
244
-
245
- test("bypasses when blockIngress is false", () => {
246
- mockConfig.secretDetection.blockIngress = false;
247
- const awsKey = ["AKIA", "IOSFODNN7", "REALKEY"].join("");
248
- const check = checkIngressForSecrets(awsKey);
249
- expect(check.blocked).toBe(false);
250
- });
251
-
252
- test("still blocks when action is warn but blockIngress is true", () => {
253
- mockConfig.secretDetection.action = "warn";
254
- mockConfig.secretDetection.blockIngress = true;
255
- const awsKey = ["AKIA", "IOSFODNN7", "REALKEY"].join("");
256
- const check = checkIngressForSecrets(awsKey);
257
- expect(check.blocked).toBe(true);
258
- });
259
- });
260
-
261
203
  // ---------------------------------------------------------------------------
262
204
  // E2E Scenario 3 — One-time send override path
263
205
  // ---------------------------------------------------------------------------
@@ -411,12 +353,4 @@ describe("E2E: cross-cutting secret leak prevention", () => {
411
353
  );
412
354
  expect(result.content).not.toContain(secret);
413
355
  });
414
-
415
- test("ingress notice never contains the detected secret", () => {
416
- const awsKey = ["AKIA", "IOSFODNN7", "REALKEY"].join("");
417
- const check = checkIngressForSecrets(awsKey);
418
- expect(check.blocked).toBe(true);
419
- expect(check.userNotice).toBeDefined();
420
- expect(check.userNotice).not.toContain(awsKey);
421
- });
422
356
  });
@@ -138,45 +138,6 @@ describe("Invariant 1: secrets never enter LLM context", () => {
138
138
  });
139
139
  }
140
140
  }
141
-
142
- // PR 27 — secret ingress block scans inbound messages
143
- test("user message containing secret is blocked from entering history", () => {
144
- // Mock config to enable block mode
145
- mock.module("../config/loader.js", () => ({
146
- applyNestedDefaults: (config: unknown) => config,
147
- getConfig: () => ({
148
- ui: {},
149
- secretDetection: {
150
- enabled: true,
151
- action: "block",
152
- blockIngress: true,
153
- },
154
- }),
155
- invalidateConfigCache: () => {},
156
- loadConfig: () => ({
157
- ui: {},
158
- secretDetection: {
159
- enabled: true,
160
- action: "block",
161
- blockIngress: true,
162
- },
163
- }),
164
- }));
165
-
166
- // Re-import to pick up the mock
167
- // eslint-disable-next-line @typescript-eslint/no-require-imports
168
- const { checkIngressForSecrets } = require("../security/secret-ingress.js");
169
-
170
- // Build a fake AWS key at runtime to avoid pre-commit hook
171
- const fakeKey = ["AKIA", "IOSFODNN7", "REALKEY"].join("");
172
- const result = checkIngressForSecrets(`My key is ${fakeKey}`);
173
-
174
- expect(result.blocked).toBe(true);
175
- expect(result.detectedTypes.length).toBeGreaterThan(0);
176
- // User notice must not echo the secret
177
- expect(result.userNotice).toBeDefined();
178
- expect(result.userNotice).not.toContain(fakeKey);
179
- });
180
141
  });
181
142
 
182
143
  // ---------------------------------------------------------------------------
@@ -208,6 +169,7 @@ describe("Invariant 2: no generic plaintext secret read API", () => {
208
169
  "tools/credentials/broker.ts", // brokered credential access
209
170
  "tools/network/web-search.ts", // web search API key lookup
210
171
  "daemon/handlers/config-telegram.ts", // Telegram bot token management
172
+ "daemon/handlers/config-vercel.ts", // Vercel API token management
211
173
  "runtime/routes/integrations/twilio.ts", // Twilio credential management (HTTP control-plane)
212
174
  "security/token-manager.ts", // OAuth token refresh flow
213
175
  "email/providers/index.ts", // email provider API key lookup
@@ -216,6 +178,7 @@ describe("Invariant 2: no generic plaintext secret read API", () => {
216
178
  "calls/twilio-config.ts", // call infrastructure credential lookup
217
179
  "calls/twilio-provider.ts", // call infrastructure credential lookup
218
180
  "calls/twilio-rest.ts", // Twilio REST API credential lookup
181
+ "calls/fish-audio-client.ts", // Fish Audio TTS API key lookup
219
182
  "runtime/channel-invite-transports/telegram.ts", // Telegram invite transport bot token lookup
220
183
  "cli/commands/keys.ts", // CLI credential management commands
221
184
  "cli/commands/credentials.ts", // CLI credential management commands
@@ -225,6 +188,7 @@ describe("Invariant 2: no generic plaintext secret read API", () => {
225
188
  "messaging/providers/slack/adapter.ts", // Slack bot token lookup for Socket Mode connectivity check
226
189
  "daemon/handlers/config-slack-channel.ts", // Slack channel config credential management
227
190
  "providers/managed-proxy/context.ts", // managed proxy API key lookup for provider initialization
191
+ "platform/client.ts", // platform client keychain fallback for standalone CLI auth
228
192
  "mcp/mcp-oauth-provider.ts", // MCP OAuth token/client/discovery persistence
229
193
  "runtime/routes/integrations/slack/share.ts", // Slack share routes credential lookup
230
194
  "mcp/client.ts", // MCP client cached-token lookup
@@ -237,7 +201,6 @@ describe("Invariant 2: no generic plaintext secret read API", () => {
237
201
  "cli/commands/oauth/connections.ts", // CLI OAuth connection delete (legacy credential cleanup)
238
202
  "oauth/manual-token-connection.ts", // manual-token provider backfill (keychain credential existence check)
239
203
  "cli/commands/doctor.ts", // CLI diagnostic API key verification via secure storage
240
- "swarm/backend-claude-code.ts", // Claude Code swarm backend API key lookup
241
204
  "workspace/provider-commit-message-generator.ts", // commit message generation provider key lookup
242
205
  "config/bundled-skills/transcribe/tools/transcribe-media.ts", // transcription tool API key lookup
243
206
  "config/bundled-skills/image-studio/tools/media-generate-image.ts", // image generation tool API key lookup
@@ -249,13 +212,13 @@ describe("Invariant 2: no generic plaintext secret read API", () => {
249
212
  "media/avatar-router.ts", // avatar generation API key lookup
250
213
  "memory/embedding-backend.ts", // embedding backend API key lookup
251
214
  "daemon/providers-setup.ts", // provider initialization API key lookup
252
- "tools/claude-code/claude-code.ts", // Claude Code tool API key lookup
253
215
  "workspace/migrations/006-services-config.ts", // services config migration reads provider API keys
254
216
  "cli/commands/avatar.ts", // CLI avatar generation API key lookup
255
217
  "config/bundled-skills/slack/tools/shared.ts", // Slack skill bot token lookup
256
218
  "daemon/conversation-process.ts", // masked provider key display
257
219
  "daemon/handlers/config-model.ts", // masked provider key display
258
220
  "providers/speech-to-text/resolve.ts", // STT provider API key lookup
221
+ "daemon/lifecycle.ts", // CES client injection into secure-keys at startup
259
222
  ]);
260
223
 
261
224
  const thisDir = dirname(fileURLToPath(import.meta.url));
@@ -613,10 +576,6 @@ describe("One-time send override", () => {
613
576
  test("default secretDetection.action is redact", () => {
614
577
  expect(DEFAULT_CONFIG.secretDetection.action).toBe("redact");
615
578
  });
616
-
617
- test("default secretDetection.blockIngress is true", () => {
618
- expect(DEFAULT_CONFIG.secretDetection.blockIngress).toBe(true);
619
- });
620
579
  });
621
580
 
622
581
  // ---------------------------------------------------------------------------
@@ -12,6 +12,7 @@ import type { CredentialMetadata } from "../tools/credentials/metadata-store.js"
12
12
  let secureKeyStore = new Map<string, string>();
13
13
  let metadataStore: CredentialMetadata[] = [];
14
14
  let idCounter = 0;
15
+ let mockBrokerUnreachable = false;
15
16
 
16
17
  function nextUUID(): string {
17
18
  idCounter += 1;
@@ -45,6 +46,12 @@ mock.module("../security/secure-keys.js", () => ({
45
46
  getSecureKeyAsync: async (account: string): Promise<string | undefined> => {
46
47
  return secureKeyStore.get(account);
47
48
  },
49
+ getSecureKeyResultAsync: async (
50
+ account: string,
51
+ ): Promise<{ value: string | undefined; unreachable: boolean }> => ({
52
+ value: secureKeyStore.get(account),
53
+ unreachable: mockBrokerUnreachable,
54
+ }),
48
55
  _resetBackend: (): void => {},
49
56
  }));
50
57
 
@@ -264,6 +271,7 @@ describe("assistant credentials CLI", () => {
264
271
  secureKeyStore = new Map();
265
272
  metadataStore = [];
266
273
  idCounter = 0;
274
+ mockBrokerUnreachable = false;
267
275
  disconnectOAuthProviderCalls = [];
268
276
  disconnectOAuthProviderResult = "not-found";
269
277
  process.exitCode = 0;
@@ -870,6 +878,42 @@ describe("assistant credentials CLI", () => {
870
878
  expect(parsed.hasSecret).toBe(false);
871
879
  expect(parsed.scrubbedValue).toBe("(not set)");
872
880
  });
881
+
882
+ test("shows broker unreachable when metadata exists but broker is down", async () => {
883
+ seedMetadataOnly("twilio", "account_sid");
884
+ mockBrokerUnreachable = true;
885
+
886
+ const result = await runCli([
887
+ "inspect",
888
+ "--service",
889
+ "twilio",
890
+ "--field",
891
+ "account_sid",
892
+ "--json",
893
+ ]);
894
+ expect(result.exitCode).toBe(0);
895
+ const parsed = JSON.parse(result.stdout);
896
+ expect(parsed.ok).toBe(true);
897
+ expect(parsed.scrubbedValue).toBe("(broker unreachable)");
898
+ expect(parsed.brokerUnreachable).toBe(true);
899
+ });
900
+
901
+ test("shows unreachable error when no metadata and broker is down", async () => {
902
+ mockBrokerUnreachable = true;
903
+
904
+ const result = await runCli([
905
+ "inspect",
906
+ "--service",
907
+ "nonexistent",
908
+ "--field",
909
+ "field",
910
+ "--json",
911
+ ]);
912
+ expect(result.exitCode).toBe(1);
913
+ const parsed = JSON.parse(result.stdout);
914
+ expect(parsed.ok).toBe(false);
915
+ expect(parsed.error).toContain("Keychain broker is unreachable");
916
+ });
873
917
  });
874
918
 
875
919
  // =========================================================================
@@ -969,6 +1013,40 @@ describe("assistant credentials CLI", () => {
969
1013
  expect(parsed.ok).toBe(false);
970
1014
  expect(parsed.error).toContain("not found");
971
1015
  });
1016
+
1017
+ test("returns unreachable error when broker is down", async () => {
1018
+ mockBrokerUnreachable = true;
1019
+
1020
+ const result = await runCli([
1021
+ "reveal",
1022
+ "--service",
1023
+ "twilio",
1024
+ "--field",
1025
+ "auth_token",
1026
+ "--json",
1027
+ ]);
1028
+ expect(result.exitCode).toBe(1);
1029
+ const parsed = JSON.parse(result.stdout);
1030
+ expect(parsed.ok).toBe(false);
1031
+ expect(parsed.error).toContain("Keychain broker is unreachable");
1032
+ });
1033
+
1034
+ test("returns credential-not-found when broker is up", async () => {
1035
+ mockBrokerUnreachable = false;
1036
+
1037
+ const result = await runCli([
1038
+ "reveal",
1039
+ "--service",
1040
+ "twilio",
1041
+ "--field",
1042
+ "nonexistent",
1043
+ "--json",
1044
+ ]);
1045
+ expect(result.exitCode).toBe(1);
1046
+ const parsed = JSON.parse(result.stdout);
1047
+ expect(parsed.ok).toBe(false);
1048
+ expect(parsed.error).toBe("Credential not found");
1049
+ });
972
1050
  });
973
1051
 
974
1052
  // =========================================================================