@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
@@ -1,120 +0,0 @@
1
- import { beforeEach, describe, expect, mock, test } from "bun:test";
2
-
3
- const mockConfig = {
4
- secretDetection: {
5
- enabled: true,
6
- action: "block" as "redact" | "warn" | "block",
7
- entropyThreshold: 4.0,
8
- blockIngress: true,
9
- },
10
- };
11
-
12
- mock.module("../config/loader.js", () => ({
13
- getConfig: () => mockConfig,
14
- loadConfig: () => mockConfig,
15
- loadRawConfig: () => ({ secretDetection: { ...mockConfig.secretDetection } }),
16
- saveRawConfig: () => {},
17
- invalidateConfigCache: () => {},
18
- }));
19
-
20
- mock.module("../util/logger.js", () => ({
21
- getLogger: () =>
22
- new Proxy({} as Record<string, unknown>, {
23
- get: () => () => {},
24
- }),
25
- }));
26
-
27
- const { checkIngressForSecrets } =
28
- await import("../security/secret-ingress.js");
29
-
30
- // Build test fixtures at runtime to avoid tripping the pre-commit secret hook.
31
- // These are well-known fake AWS/GitHub patterns used across the test suite.
32
- const AWS_KEY = ["AKIA", "IOSFODNN7", "REALKEY"].join("");
33
- const GH_TOKEN = ["ghp_", "ABCDEFghijklMN01234567", "89abcdefghijkl"].join("");
34
- const TG_TOKEN = ["123456789", ":", "ABCDefGHIJklmnopQRSTuvwxyz012345678"].join(
35
- "",
36
- );
37
-
38
- describe("secret ingress handler", () => {
39
- beforeEach(() => {
40
- mockConfig.secretDetection = {
41
- enabled: true,
42
- action: "block",
43
- entropyThreshold: 4.0,
44
- blockIngress: true,
45
- };
46
- });
47
-
48
- test("blocks message containing an AWS key", () => {
49
- const result = checkIngressForSecrets(`Here is my key: ${AWS_KEY}`);
50
- expect(result.blocked).toBe(true);
51
- expect(result.detectedTypes).toContain("AWS Access Key");
52
- expect(result.userNotice).toBeDefined();
53
- expect(result.userNotice).not.toContain(AWS_KEY);
54
- });
55
-
56
- test("allows normal text through", () => {
57
- const result = checkIngressForSecrets(
58
- "Hello, can you help me write a function?",
59
- );
60
- expect(result.blocked).toBe(false);
61
- expect(result.detectedTypes).toHaveLength(0);
62
- expect(result.userNotice).toBeUndefined();
63
- });
64
-
65
- test("does not block when detection is disabled", () => {
66
- mockConfig.secretDetection.enabled = false;
67
- const result = checkIngressForSecrets(`Here is my key: ${AWS_KEY}`);
68
- expect(result.blocked).toBe(false);
69
- });
70
-
71
- test("does not block when blockIngress is false", () => {
72
- mockConfig.secretDetection.blockIngress = false;
73
- const result = checkIngressForSecrets(`Here is my key: ${AWS_KEY}`);
74
- expect(result.blocked).toBe(false);
75
- });
76
-
77
- test("blocks regardless of output action when blockIngress is true", () => {
78
- mockConfig.secretDetection.action = "warn";
79
- mockConfig.secretDetection.blockIngress = true;
80
- const resultWarn = checkIngressForSecrets(`Here is my key: ${AWS_KEY}`);
81
- expect(resultWarn.blocked).toBe(true);
82
-
83
- mockConfig.secretDetection.action = "redact";
84
- const resultRedact = checkIngressForSecrets(`Here is my key: ${AWS_KEY}`);
85
- expect(resultRedact.blocked).toBe(true);
86
- });
87
-
88
- test("user notice never contains the secret value", () => {
89
- const result = checkIngressForSecrets(`Use this: ${AWS_KEY}`);
90
- expect(result.blocked).toBe(true);
91
- expect(result.userNotice).not.toContain(AWS_KEY);
92
- });
93
-
94
- test("detects multiple secret types", () => {
95
- const msg = `AWS: ${AWS_KEY} and GH: ${GH_TOKEN}`;
96
- const result = checkIngressForSecrets(msg);
97
- expect(result.blocked).toBe(true);
98
- expect(result.detectedTypes.length).toBeGreaterThanOrEqual(2);
99
- });
100
-
101
- test("blocks message containing a Telegram bot token", () => {
102
- const result = checkIngressForSecrets(`Here is my bot token: ${TG_TOKEN}`);
103
- expect(result.blocked).toBe(true);
104
- expect(result.detectedTypes).toContain("Telegram Bot Token");
105
- expect(result.userNotice).not.toContain(TG_TOKEN);
106
- });
107
-
108
- test("empty content passes through", () => {
109
- const result = checkIngressForSecrets("");
110
- expect(result.blocked).toBe(false);
111
- });
112
-
113
- test("respects configured entropyThreshold from config", () => {
114
- // Pattern-matched secrets (AWS keys) should still be caught regardless of threshold
115
- mockConfig.secretDetection.entropyThreshold = 100.0;
116
- const result = checkIngressForSecrets(`Here is my key: ${AWS_KEY}`);
117
- expect(result.blocked).toBe(true);
118
- expect(result.detectedTypes).toContain("AWS Access Key");
119
- });
120
- });
@@ -1,358 +0,0 @@
1
- import { beforeEach, describe, expect, mock, test } from "bun:test";
2
-
3
- // ---------------------------------------------------------------------------
4
- // Mocks — declared before imports that depend on them
5
- // ---------------------------------------------------------------------------
6
-
7
- mock.module("../util/logger.js", () => ({
8
- getLogger: () =>
9
- new Proxy({} as Record<string, unknown>, {
10
- get: () => () => {},
11
- }),
12
- }));
13
-
14
- mock.module("../hooks/manager.js", () => ({
15
- getHookManager: () => ({
16
- trigger: async () => ({ blocked: false }),
17
- }),
18
- }));
19
-
20
- let swarmEnabled = true;
21
-
22
- mock.module("../config/loader.js", () => ({
23
- getConfig: () => ({
24
- ui: {},
25
-
26
- provider: "anthropic",
27
- providerOrder: ["anthropic"],
28
- swarm: {
29
- enabled: swarmEnabled,
30
- maxWorkers: 2,
31
- maxTasks: 4,
32
- maxRetriesPerTask: 1,
33
- workerTimeoutSec: 900,
34
- roleTimeoutsSec: {},
35
- plannerModelIntent: "latency-optimized",
36
- synthesizerModelIntent: "quality-optimized",
37
- },
38
- services: {
39
- inference: {
40
- mode: "your-own",
41
- provider: "anthropic",
42
- model: "claude-opus-4-6",
43
- },
44
- "image-generation": {
45
- mode: "your-own",
46
- provider: "gemini",
47
- model: "gemini-3.1-flash-image-preview",
48
- },
49
- "web-search": { mode: "your-own", provider: "inference-provider-native" },
50
- },
51
- }),
52
- }));
53
-
54
- const mockTestProvider = {
55
- name: "test",
56
- async sendMessage() {
57
- return {
58
- content: [
59
- {
60
- type: "text",
61
- text: '{"tasks":[{"id":"t1","role":"coder","objective":"Do it","dependencies":[]}]}',
62
- },
63
- ],
64
- model: "test",
65
- usage: { inputTokens: 10, outputTokens: 10 },
66
- stopReason: "end_turn",
67
- };
68
- },
69
- };
70
- let mockAnthropicKey: string | undefined = "test-api-key";
71
- mock.module("../security/secure-keys.js", () => ({
72
- getSecureKeyAsync: async () => mockAnthropicKey,
73
- getProviderKeyAsync: async () => mockAnthropicKey,
74
- }));
75
-
76
- mock.module("../providers/registry.js", () => ({
77
- getProvider: () => mockTestProvider,
78
- getFailoverProvider: () => mockTestProvider,
79
- }));
80
-
81
- mock.module("@anthropic-ai/claude-agent-sdk", () => ({
82
- query: () => ({
83
- async *[Symbol.asyncIterator]() {
84
- yield {
85
- type: "result" as const,
86
- session_id: "test-session",
87
- subtype: "success" as const,
88
- result:
89
- '```json\n{"summary":"Done","artifacts":[],"issues":[],"nextSteps":[]}\n```',
90
- };
91
- },
92
- }),
93
- }));
94
-
95
- import type { AgentEvent } from "../agent/loop.js";
96
- import { AgentLoop } from "../agent/loop.js";
97
- import type { Message, ProviderResponse } from "../providers/types.js";
98
- import {
99
- _resetSwarmActive,
100
- swarmDelegateTool,
101
- } from "../tools/swarm/delegate.js";
102
- import type { ToolContext } from "../tools/types.js";
103
-
104
- function makeContext(overrides?: Partial<ToolContext>): ToolContext {
105
- return {
106
- conversationId: "test-conv",
107
- workingDir: "/tmp/test",
108
- trustClass: "guardian",
109
- onOutput: () => {},
110
- ...overrides,
111
- } as ToolContext;
112
- }
113
-
114
- // ---------------------------------------------------------------------------
115
- // 1. Agent loop + swarm_delegate integration
116
- // ---------------------------------------------------------------------------
117
-
118
- describe("swarm through AgentLoop", () => {
119
- beforeEach(() => {
120
- _resetSwarmActive();
121
- swarmEnabled = true;
122
- });
123
-
124
- test("agent loop calls swarm_delegate and receives tool result", async () => {
125
- let turnCount = 0;
126
-
127
- // Provider that emits swarm_delegate tool_use on turn 1, then text on turn 2
128
- const mockProvider = {
129
- name: "test",
130
- async sendMessage(_messages: Message[]) {
131
- turnCount++;
132
- if (turnCount === 1) {
133
- return {
134
- content: [
135
- {
136
- type: "tool_use" as const,
137
- id: "tu-1",
138
- name: "swarm_delegate",
139
- input: { objective: "Build a feature with tests" },
140
- },
141
- ],
142
- model: "test",
143
- usage: { inputTokens: 10, outputTokens: 10 },
144
- stopReason: "tool_use",
145
- } as ProviderResponse;
146
- }
147
- return {
148
- content: [{ type: "text" as const, text: "All done." }],
149
- model: "test",
150
- usage: { inputTokens: 10, outputTokens: 10 },
151
- stopReason: "end_turn",
152
- } as ProviderResponse;
153
- },
154
- };
155
-
156
- const events: AgentEvent[] = [];
157
- const toolExecutor = async (
158
- _name: string,
159
- input: Record<string, unknown>,
160
- onOutput?: (chunk: string) => void,
161
- ) => {
162
- const result = await swarmDelegateTool.execute(
163
- input,
164
- makeContext({ onOutput }),
165
- );
166
- return result;
167
- };
168
-
169
- const tools = [swarmDelegateTool.getDefinition()];
170
-
171
- const loop = new AgentLoop(
172
- mockProvider,
173
- "system prompt",
174
- {},
175
- tools,
176
- toolExecutor,
177
- );
178
- const messages: Message[] = [
179
- { role: "user", content: [{ type: "text", text: "Build a feature" }] },
180
- ];
181
-
182
- const history = await loop.run(messages, (e) => {
183
- events.push(e);
184
- });
185
-
186
- // Should have tool_use event
187
- const toolUseEvents = events.filter((e) => e.type === "tool_use");
188
- expect(toolUseEvents.length).toBe(1);
189
- expect(toolUseEvents[0].type === "tool_use" && toolUseEvents[0].name).toBe(
190
- "swarm_delegate",
191
- );
192
-
193
- // Should have tool_result event
194
- const toolResultEvents = events.filter((e) => e.type === "tool_result");
195
- expect(toolResultEvents.length).toBe(1);
196
- expect(
197
- toolResultEvents[0].type === "tool_result" &&
198
- !toolResultEvents[0].isError,
199
- ).toBe(true);
200
-
201
- // Should have progress output chunks
202
- const chunks = events.filter((e) => e.type === "tool_output_chunk");
203
- expect(chunks.length).toBeGreaterThan(0);
204
-
205
- // History should contain assistant + tool_result + final assistant
206
- expect(history.length).toBeGreaterThanOrEqual(4);
207
- });
208
-
209
- test("agent loop handles aborted swarm gracefully", async () => {
210
- const controller = new AbortController();
211
-
212
- const mockProvider = {
213
- name: "test",
214
- async sendMessage() {
215
- // Abort after model responds with tool_use
216
- controller.abort();
217
- return {
218
- content: [
219
- {
220
- type: "tool_use" as const,
221
- id: "tu-abort",
222
- name: "swarm_delegate",
223
- input: { objective: "Should be cancelled" },
224
- },
225
- ],
226
- model: "test",
227
- usage: { inputTokens: 10, outputTokens: 10 },
228
- stopReason: "tool_use",
229
- } as ProviderResponse;
230
- },
231
- };
232
-
233
- const events: AgentEvent[] = [];
234
- const toolExecutor = async (
235
- _name: string,
236
- input: Record<string, unknown>,
237
- onOutput?: (chunk: string) => void,
238
- ) => {
239
- return swarmDelegateTool.execute(
240
- input,
241
- makeContext({ onOutput, signal: controller.signal }),
242
- );
243
- };
244
-
245
- const tools = [swarmDelegateTool.getDefinition()];
246
- const loop = new AgentLoop(mockProvider, "system", {}, tools, toolExecutor);
247
- const messages: Message[] = [
248
- { role: "user", content: [{ type: "text", text: "go" }] },
249
- ];
250
-
251
- // Should not hang or throw
252
- const history = await loop.run(
253
- messages,
254
- (e) => {
255
- events.push(e);
256
- },
257
- controller.signal,
258
- );
259
- expect(history.length).toBeGreaterThanOrEqual(1);
260
- });
261
- });
262
-
263
- // ---------------------------------------------------------------------------
264
- // 2. Regression tests for swarm-specific behaviors
265
- // ---------------------------------------------------------------------------
266
-
267
- describe("swarm regression tests", () => {
268
- beforeEach(() => {
269
- _resetSwarmActive();
270
- swarmEnabled = true;
271
- });
272
-
273
- test("swarm_delegate returns graceful message when disabled", async () => {
274
- swarmEnabled = false;
275
- const result = await swarmDelegateTool.execute(
276
- { objective: "Some task" },
277
- makeContext(),
278
- );
279
- expect(result.isError).toBe(false);
280
- expect(result.content).toContain("disabled");
281
- swarmEnabled = true;
282
- });
283
-
284
- test("recursion guard blocks concurrent invocation", async () => {
285
- const ctx = makeContext();
286
-
287
- // Start first swarm without awaiting — execute runs synchronously up to
288
- // its first internal `await`, which adds the sessionKey to activeSessions
289
- // before yielding back to us.
290
- const first = swarmDelegateTool.execute({ objective: "First" }, ctx);
291
-
292
- // While the first call is still in-flight, a second call on the same
293
- // session should be rejected by the recursion guard.
294
- const result2 = await swarmDelegateTool.execute(
295
- { objective: "Second" },
296
- ctx,
297
- );
298
- expect(result2.isError).toBe(true);
299
- expect(result2.content).toContain("already executing");
300
-
301
- // Let the first call finish and verify it succeeded
302
- const result1 = await first;
303
- expect(result1.isError).toBeFalsy();
304
-
305
- // After the first call completes, the guard should be released —
306
- // a subsequent call must succeed.
307
- const result3 = await swarmDelegateTool.execute(
308
- { objective: "Third" },
309
- ctx,
310
- );
311
- expect(result3.isError).toBeFalsy();
312
- });
313
-
314
- test("worker backend reports unavailable when no API key", async () => {
315
- const prevKey = mockAnthropicKey;
316
- const prevEnv = process.env.ANTHROPIC_API_KEY;
317
- mockAnthropicKey = undefined;
318
- delete process.env.ANTHROPIC_API_KEY;
319
- try {
320
- const result = await swarmDelegateTool.execute(
321
- { objective: "Task without key" },
322
- makeContext(),
323
- );
324
-
325
- // The tool should still complete — the orchestrator handles backend failures
326
- // Tasks should fail because no backend is available
327
- expect(result.content).toBeTruthy();
328
- expect(result.content).toContain("failed");
329
- } finally {
330
- mockAnthropicKey = prevKey;
331
- if (prevEnv !== undefined) {
332
- process.env.ANTHROPIC_API_KEY = prevEnv;
333
- }
334
- }
335
- });
336
-
337
- test("progress chunks stream through onOutput", async () => {
338
- const outputs: string[] = [];
339
- await swarmDelegateTool.execute(
340
- { objective: "Track progress" },
341
- makeContext({ onOutput: (text: string) => outputs.push(text) }),
342
- );
343
-
344
- // Should have planning and execution output
345
- expect(outputs.some((o) => o.includes("Planning"))).toBe(true);
346
- expect(outputs.some((o) => o.includes("Plan:"))).toBe(true);
347
- expect(outputs.some((o) => o.includes("Executing"))).toBe(true);
348
- });
349
-
350
- test("result includes task stats", async () => {
351
- const result = await swarmDelegateTool.execute(
352
- { objective: "Check stats" },
353
- makeContext(),
354
- );
355
- expect(result.content).toContain("Tasks:");
356
- expect(result.content).toContain("Duration:");
357
- });
358
- });