@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.
- package/.env.example +16 -2
- package/ARCHITECTURE.md +6 -75
- package/Dockerfile +1 -1
- package/README.md +0 -2
- package/bun.lock +0 -414
- package/docs/architecture/keychain-broker.md +45 -240
- package/docs/architecture/security.md +0 -17
- package/docs/credential-execution-service.md +2 -2
- package/node_modules/@vellumai/ces-contracts/package.json +1 -0
- package/node_modules/@vellumai/ces-contracts/src/rpc.ts +119 -0
- package/node_modules/@vellumai/credential-storage/package.json +1 -0
- package/node_modules/@vellumai/egress-proxy/package.json +1 -0
- package/package.json +2 -3
- package/src/__tests__/actor-token-service.test.ts +0 -114
- package/src/__tests__/assistant-feature-flags-integration.test.ts +30 -29
- package/src/__tests__/browser-skill-endstate.test.ts +6 -5
- package/src/__tests__/btw-routes.test.ts +0 -39
- package/src/__tests__/call-domain.test.ts +0 -128
- package/src/__tests__/ces-rpc-credential-backend.test.ts +199 -0
- package/src/__tests__/channel-approval-routes.test.ts +0 -5
- package/src/__tests__/channel-readiness-service.test.ts +1 -60
- package/src/__tests__/checker.test.ts +4 -2
- package/src/__tests__/cli-command-risk-guard.test.ts +112 -0
- package/src/__tests__/config-schema-cmd.test.ts +0 -1
- package/src/__tests__/config-schema.test.ts +1 -1
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -5
- package/src/__tests__/conversation-init.benchmark.test.ts +0 -2
- package/src/__tests__/conversation-skill-tools.test.ts +0 -54
- package/src/__tests__/conversation-title-service.test.ts +87 -0
- package/src/__tests__/credential-execution-feature-gates.test.ts +28 -14
- package/src/__tests__/credential-execution-managed-contract.test.ts +33 -18
- package/src/__tests__/credential-security-e2e.test.ts +0 -66
- package/src/__tests__/credential-security-invariants.test.ts +4 -45
- package/src/__tests__/credentials-cli.test.ts +78 -0
- package/src/__tests__/db-migration-rollback.test.ts +2015 -1
- package/src/__tests__/docker-signing-key-bootstrap.test.ts +34 -143
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +6 -4
- package/src/__tests__/guardian-routing-state.test.ts +0 -5
- package/src/__tests__/host-shell-tool.test.ts +6 -7
- package/src/__tests__/http-user-message-parity.test.ts +3 -103
- package/src/__tests__/inbound-invite-redemption.test.ts +0 -4
- package/src/__tests__/inline-skill-load-permissions.test.ts +6 -8
- package/src/__tests__/intent-routing.test.ts +0 -13
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +178 -0
- package/src/__tests__/keychain-broker-client.test.ts +161 -22
- package/src/__tests__/memory-jobs-worker-backoff.test.ts +150 -0
- package/src/__tests__/migration-export-http.test.ts +2 -2
- package/src/__tests__/migration-import-commit-http.test.ts +2 -2
- package/src/__tests__/migration-import-preflight-http.test.ts +2 -2
- package/src/__tests__/migration-validate-http.test.ts +2 -2
- package/src/__tests__/non-member-access-request.test.ts +0 -5
- package/src/__tests__/notification-decision-fallback.test.ts +4 -0
- package/src/__tests__/notification-decision-identity.test.ts +4 -0
- package/src/__tests__/permission-types.test.ts +1 -0
- package/src/__tests__/provider-managed-proxy-integration.test.ts +5 -6
- package/src/__tests__/qdrant-manager.test.ts +28 -2
- package/src/__tests__/registry.test.ts +0 -6
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -4
- package/src/__tests__/secret-routes-managed-proxy.test.ts +0 -4
- package/src/__tests__/secure-keys.test.ts +83 -263
- package/src/__tests__/shell-identity.test.ts +96 -6
- package/src/__tests__/skill-feature-flags-integration.test.ts +22 -14
- package/src/__tests__/skill-feature-flags.test.ts +46 -45
- package/src/__tests__/skill-load-feature-flag.test.ts +7 -10
- package/src/__tests__/skill-load-inline-command.test.ts +8 -12
- package/src/__tests__/skill-load-inline-includes.test.ts +6 -10
- package/src/__tests__/skill-load-tool.test.ts +0 -2
- package/src/__tests__/skill-projection-feature-flag.test.ts +33 -29
- package/src/__tests__/skills.test.ts +0 -2
- package/src/__tests__/slack-inbound-verification.test.ts +0 -4
- package/src/__tests__/suggestion-routes.test.ts +1 -32
- package/src/__tests__/system-prompt.test.ts +0 -1
- package/src/__tests__/tool-executor-shell-integration.test.ts +5 -3
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +0 -5
- package/src/__tests__/trusted-contact-multichannel.test.ts +0 -4
- package/src/__tests__/update-bulletin.test.ts +0 -2
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +6 -9
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -6
- package/src/__tests__/workspace-migration-015-migrate-credentials-to-keychain.test.ts +252 -0
- package/src/__tests__/workspace-migration-016-migrate-credentials-from-keychain.test.ts +218 -0
- package/src/__tests__/workspace-migration-down-functions.test.ts +1009 -0
- package/src/__tests__/workspace-migrations-runner.test.ts +114 -0
- package/src/calls/audio-store.test.ts +97 -0
- package/src/calls/audio-store.ts +205 -0
- package/src/calls/call-controller.ts +85 -7
- package/src/calls/call-domain.ts +3 -0
- package/src/calls/call-store.ts +10 -3
- package/src/calls/fish-audio-client.ts +117 -0
- package/src/calls/relay-server.ts +27 -0
- package/src/calls/twilio-routes.ts +2 -1
- package/src/calls/types.ts +1 -0
- package/src/calls/voice-ingress-preflight.ts +0 -42
- package/src/calls/voice-quality.ts +26 -5
- package/src/calls/voice-session-bridge.ts +6 -12
- package/src/cli/commands/config.ts +1 -4
- package/src/cli/commands/credentials.ts +34 -4
- package/src/cli/commands/oauth/index.ts +7 -0
- package/src/cli/commands/oauth/platform.ts +179 -0
- package/src/cli/commands/platform.ts +3 -3
- package/src/config/assistant-feature-flags.ts +186 -5
- package/src/config/bundled-skills/messaging/SKILL.md +5 -5
- package/src/config/bundled-skills/phone-calls/TOOLS.json +4 -0
- package/src/config/bundled-skills/settings/TOOLS.json +2 -2
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +42 -0
- package/src/config/bundled-tool-registry.ts +1 -11
- package/src/config/env-registry.ts +1 -1
- package/src/config/env.ts +8 -14
- package/src/config/feature-flag-registry.json +48 -8
- package/src/config/loader.ts +98 -31
- package/src/config/schema.ts +4 -13
- package/src/config/schemas/calls.ts +13 -0
- package/src/config/schemas/fish-audio.ts +39 -0
- package/src/config/schemas/security.ts +0 -4
- package/src/config/types.ts +0 -1
- package/src/contacts/contact-store.ts +39 -0
- package/src/contacts/types.ts +2 -0
- package/src/credential-execution/approval-bridge.ts +1 -0
- package/src/credential-execution/executable-discovery.ts +28 -4
- package/src/credential-execution/feature-gates.ts +16 -0
- package/src/credential-execution/process-manager.ts +38 -0
- package/src/daemon/assistant-attachments.ts +9 -0
- package/src/daemon/config-watcher.ts +5 -0
- package/src/daemon/conversation-tool-setup.ts +0 -105
- package/src/daemon/conversation.ts +10 -1
- package/src/daemon/handlers/config-vercel.ts +92 -0
- package/src/daemon/handlers/skills.ts +2 -15
- package/src/daemon/install-symlink.ts +195 -0
- package/src/daemon/lifecycle.ts +227 -51
- package/src/daemon/message-types/conversations.ts +3 -4
- package/src/daemon/message-types/diagnostics.ts +3 -22
- package/src/daemon/message-types/messages.ts +0 -2
- package/src/daemon/message-types/upgrades.ts +8 -0
- package/src/daemon/server.ts +30 -92
- package/src/events/domain-events.ts +2 -1
- package/src/inbound/platform-callback-registration.ts +3 -3
- package/src/instrument.ts +8 -5
- package/src/memory/conversation-title-service.ts +50 -1
- package/src/memory/db-init.ts +12 -0
- package/src/memory/items-extractor.ts +15 -1
- package/src/memory/job-handlers/conversation-starters.ts +4 -1
- package/src/memory/jobs-store.ts +30 -5
- package/src/memory/jobs-worker.ts +31 -7
- package/src/memory/migrations/001-job-deferrals.ts +19 -0
- package/src/memory/migrations/004-entity-relation-dedup.ts +10 -0
- package/src/memory/migrations/005-fingerprint-scope-unique.ts +76 -0
- package/src/memory/migrations/006-scope-salted-fingerprints.ts +50 -0
- package/src/memory/migrations/007-assistant-id-to-self.ts +10 -0
- package/src/memory/migrations/008-remove-assistant-id-columns.ts +34 -0
- package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +26 -0
- package/src/memory/migrations/014-backfill-inbox-thread-state.ts +10 -0
- package/src/memory/migrations/015-drop-active-search-index.ts +17 -0
- package/src/memory/migrations/019-notification-tables-schema-migration.ts +12 -0
- package/src/memory/migrations/020-rename-macos-ios-channel-to-vellum.ts +121 -0
- package/src/memory/migrations/024-embedding-vector-blob.ts +74 -0
- package/src/memory/migrations/026a-embeddings-nullable-vector-json.ts +82 -0
- package/src/memory/migrations/036-normalize-phone-identities.ts +11 -0
- package/src/memory/migrations/116-messages-fts.ts +106 -1
- package/src/memory/migrations/126-backfill-guardian-principal-id.ts +52 -0
- package/src/memory/migrations/127-guardian-principal-id-not-null.ts +77 -0
- package/src/memory/migrations/134-contacts-notes-column.ts +13 -0
- package/src/memory/migrations/135-backfill-contact-interaction-stats.ts +20 -0
- package/src/memory/migrations/136-drop-assistant-id-columns.ts +52 -0
- package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +13 -0
- package/src/memory/migrations/141-rename-verification-table.ts +54 -0
- package/src/memory/migrations/142-rename-verification-session-id-column.ts +25 -0
- package/src/memory/migrations/143-rename-guardian-verification-values.ts +35 -0
- package/src/memory/migrations/144-rename-voice-to-phone.ts +136 -0
- package/src/memory/migrations/145-drop-accounts-table.ts +32 -0
- package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +14 -1
- package/src/memory/migrations/148-drop-reminders-table.ts +35 -1
- package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +69 -1
- package/src/memory/migrations/162-guardian-timestamps-epoch-ms.ts +290 -0
- package/src/memory/migrations/169-rename-gmail-provider-key-to-google.ts +51 -1
- package/src/memory/migrations/174-rename-thread-starters-table.ts +47 -1
- package/src/memory/migrations/176-drop-capability-card-state.ts +13 -0
- package/src/memory/migrations/180-backfill-inline-attachments-to-disk.ts +16 -0
- package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +28 -1
- package/src/memory/migrations/190-call-session-skip-disclosure.ts +15 -0
- package/src/memory/migrations/191-backfill-audio-attachment-mime-types.ts +64 -0
- package/src/memory/migrations/192-contacts-user-file-column.ts +15 -0
- package/src/memory/migrations/index.ts +4 -0
- package/src/memory/migrations/registry.ts +90 -0
- package/src/memory/migrations/validate-migration-state.ts +137 -11
- package/src/memory/qdrant-circuit-breaker.ts +9 -0
- package/src/memory/qdrant-manager.ts +64 -7
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/contacts.ts +1 -0
- package/src/notifications/decision-engine.ts +4 -1
- package/src/oauth/connection-resolver.ts +6 -4
- package/src/permissions/checker.ts +0 -38
- package/src/permissions/shell-identity.ts +76 -22
- package/src/permissions/types.ts +4 -2
- package/src/platform/client.ts +35 -7
- package/src/prompts/persona-resolver.ts +138 -0
- package/src/prompts/system-prompt.ts +36 -4
- package/src/prompts/templates/users/default.md +1 -0
- package/src/providers/registry.ts +27 -40
- package/src/runtime/auth/__tests__/credential-service.test.ts +0 -1
- package/src/runtime/auth/__tests__/external-assistant-id.test.ts +13 -68
- package/src/runtime/auth/external-assistant-id.ts +13 -59
- package/src/runtime/auth/route-policy.ts +15 -1
- package/src/runtime/auth/token-service.ts +43 -138
- package/src/runtime/channel-readiness-service.ts +1 -16
- package/src/runtime/http-server.ts +27 -2
- package/src/runtime/middleware/error-handler.ts +1 -9
- package/src/runtime/routes/audio-routes.ts +40 -0
- package/src/runtime/routes/btw-routes.ts +0 -17
- package/src/runtime/routes/conversation-query-routes.ts +63 -1
- package/src/runtime/routes/conversation-routes.ts +4 -44
- package/src/runtime/routes/diagnostics-routes.ts +1 -477
- package/src/runtime/routes/identity-routes.ts +18 -29
- package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +4 -33
- package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +1 -1
- package/src/runtime/routes/integrations/vercel.ts +89 -0
- package/src/runtime/routes/log-export-routes.ts +5 -0
- package/src/runtime/routes/memory-item-routes.ts +24 -6
- package/src/runtime/routes/migration-rollback-routes.ts +209 -0
- package/src/runtime/routes/migration-routes.ts +17 -1
- package/src/runtime/routes/notification-routes.ts +58 -0
- package/src/runtime/routes/schedule-routes.ts +65 -0
- package/src/runtime/routes/settings-routes.ts +41 -1
- package/src/runtime/routes/tts-routes.ts +86 -0
- package/src/runtime/routes/upgrade-broadcast-routes.ts +26 -2
- package/src/runtime/routes/workspace-commit-routes.ts +62 -0
- package/src/runtime/routes/workspace-routes.test.ts +22 -1
- package/src/runtime/routes/workspace-routes.ts +1 -1
- package/src/runtime/routes/workspace-utils.ts +86 -2
- package/src/security/ces-credential-client.ts +59 -22
- package/src/security/ces-rpc-credential-backend.ts +85 -0
- package/src/security/credential-backend.ts +12 -88
- package/src/security/keychain-broker-client.ts +10 -2
- package/src/security/secure-keys.ts +94 -113
- package/src/skills/catalog-install.ts +13 -7
- package/src/telemetry/usage-telemetry-reporter.ts +4 -2
- package/src/tools/calls/call-start.ts +1 -0
- package/src/tools/executor.ts +0 -4
- package/src/tools/network/script-proxy/session-manager.ts +19 -4
- package/src/tools/network/web-fetch.ts +3 -1
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/types.ts +0 -8
- package/src/util/errors.ts +0 -12
- package/src/util/platform.ts +3 -50
- package/src/workspace/git-service.ts +5 -2
- package/src/workspace/migrations/001-avatar-rename.ts +15 -0
- package/src/workspace/migrations/003-seed-device-id.ts +17 -1
- package/src/workspace/migrations/004-extract-collect-usage-data.ts +33 -0
- package/src/workspace/migrations/005-add-send-diagnostics.ts +3 -0
- package/src/workspace/migrations/006-services-config.ts +49 -0
- package/src/workspace/migrations/007-web-search-provider-rename.ts +27 -0
- package/src/workspace/migrations/008-voice-timeout-and-max-steps.ts +3 -0
- package/src/workspace/migrations/009-backfill-conversation-disk-view.ts +4 -0
- package/src/workspace/migrations/010-app-dir-rename.ts +78 -0
- package/src/workspace/migrations/011-backfill-installation-id.ts +11 -0
- package/src/workspace/migrations/012-rename-conversation-disk-view-dirs.ts +44 -0
- package/src/workspace/migrations/013-repair-conversation-disk-view.ts +5 -0
- package/src/workspace/migrations/015-migrate-credentials-to-keychain.ts +153 -0
- package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +156 -0
- package/src/workspace/migrations/016-migrate-credentials-from-keychain.ts +150 -0
- package/src/workspace/migrations/017-seed-persona-dirs.ts +95 -0
- package/src/workspace/migrations/migrate-to-workspace-volume.ts +23 -1
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/workspace/migrations/runner.ts +106 -2
- package/src/workspace/migrations/types.ts +4 -0
- package/src/__tests__/claude-code-skill-regression.test.ts +0 -206
- package/src/__tests__/claude-code-tool-profiles.test.ts +0 -99
- package/src/__tests__/diagnostics-export.test.ts +0 -288
- package/src/__tests__/local-gateway-health.test.ts +0 -209
- package/src/__tests__/secret-ingress-handler.test.ts +0 -120
- package/src/__tests__/swarm-conversation-integration.test.ts +0 -358
- package/src/__tests__/swarm-dag-pathological.test.ts +0 -547
- package/src/__tests__/swarm-orchestrator.test.ts +0 -463
- package/src/__tests__/swarm-plan-validator.test.ts +0 -384
- package/src/__tests__/swarm-recursion.test.ts +0 -197
- package/src/__tests__/swarm-router-planner.test.ts +0 -234
- package/src/__tests__/swarm-tool.test.ts +0 -185
- package/src/__tests__/swarm-worker-backend.test.ts +0 -144
- package/src/__tests__/swarm-worker-runner.test.ts +0 -288
- package/src/commands/__tests__/cc-command-registry.test.ts +0 -396
- package/src/commands/cc-command-registry.ts +0 -248
- package/src/config/bundled-skills/claude-code/SKILL.md +0 -53
- package/src/config/bundled-skills/claude-code/TOOLS.json +0 -47
- package/src/config/bundled-skills/claude-code/tools/claude-code.ts +0 -12
- package/src/config/bundled-skills/orchestration/SKILL.md +0 -33
- package/src/config/bundled-skills/orchestration/TOOLS.json +0 -35
- package/src/config/bundled-skills/orchestration/tools/swarm-delegate.ts +0 -12
- package/src/config/schemas/swarm.ts +0 -82
- package/src/logfire.ts +0 -135
- package/src/runtime/local-gateway-health.ts +0 -275
- package/src/security/secret-ingress.ts +0 -68
- package/src/swarm/backend-claude-code.ts +0 -225
- package/src/swarm/checkpoint.ts +0 -137
- package/src/swarm/graph-utils.ts +0 -53
- package/src/swarm/index.ts +0 -55
- package/src/swarm/limits.ts +0 -66
- package/src/swarm/orchestrator.ts +0 -424
- package/src/swarm/plan-validator.ts +0 -117
- package/src/swarm/router-planner.ts +0 -162
- package/src/swarm/router-prompts.ts +0 -39
- package/src/swarm/synthesizer.ts +0 -81
- package/src/swarm/types.ts +0 -72
- package/src/swarm/worker-backend.ts +0 -131
- package/src/swarm/worker-prompts.ts +0 -80
- package/src/swarm/worker-runner.ts +0 -170
- package/src/tools/claude-code/claude-code.ts +0 -610
- 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
|
-
});
|