@vellumai/assistant 0.5.5 → 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 +4 -5
- 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 +1 -2
- 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 +3 -3
- package/src/__tests__/context-window-manager.test.ts +78 -0
- 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 +117 -1
- 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 +98 -0
- 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__/memory-regressions.test.ts +8 -30
- 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__/require-fresh-approval.test.ts +4 -0
- 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-lifecycle-events.test.ts +4 -0
- package/src/__tests__/tool-executor-shell-integration.test.ts +5 -3
- package/src/__tests__/tool-executor.test.ts +4 -0
- 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/conversations.ts +0 -18
- 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 +16 -16
- package/src/config/feature-flag-registry.json +48 -16
- package/src/config/loader.ts +98 -31
- package/src/config/schema.ts +4 -25
- package/src/config/schemas/calls.ts +13 -0
- package/src/config/schemas/fish-audio.ts +39 -0
- package/src/config/schemas/memory.ts +0 -4
- package/src/config/schemas/platform.ts +1 -1
- package/src/config/schemas/security.ts +4 -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/context/window-manager.ts +53 -2
- 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 +6 -4
- package/src/daemon/conversation-agent-loop.ts +0 -60
- package/src/daemon/conversation-memory.ts +0 -117
- package/src/daemon/conversation-runtime-assembly.ts +0 -2
- 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/conversations.ts +0 -11
- package/src/daemon/handlers/skills.ts +2 -15
- package/src/daemon/install-symlink.ts +195 -0
- package/src/daemon/lifecycle.ts +229 -96
- 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/followups/followup-store.ts +5 -2
- package/src/inbound/platform-callback-registration.ts +3 -3
- package/src/instrument.ts +8 -5
- package/src/memory/conversation-crud.ts +0 -236
- package/src/memory/conversation-title-service.ts +76 -11
- package/src/memory/db-init.ts +15 -11
- package/src/memory/indexer.ts +15 -106
- package/src/memory/items-extractor.ts +15 -1
- package/src/memory/job-handlers/conversation-starters.ts +4 -1
- package/src/memory/job-handlers/embedding.ts +0 -79
- package/src/memory/job-utils.ts +1 -1
- package/src/memory/jobs-store.ts +30 -13
- package/src/memory/jobs-worker.ts +31 -27
- 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/189-drop-simplified-memory.ts +42 -0
- 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 +5 -3
- 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-client.ts +4 -6
- 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/memory/schema/conversations.ts +0 -3
- package/src/memory/schema/index.ts +0 -2
- package/src/messaging/draft-store.ts +2 -2
- 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/defaults.ts +3 -3
- package/src/permissions/shell-identity.ts +76 -22
- package/src/permissions/trust-client.ts +2 -13
- package/src/permissions/trust-store.ts +8 -3
- 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 +29 -1
- package/src/runtime/auth/token-service.ts +53 -15
- package/src/runtime/channel-readiness-service.ts +1 -16
- package/src/runtime/http-server.ts +29 -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-management-routes.ts +0 -36
- package/src/runtime/routes/conversation-query-routes.ts +106 -2
- package/src/runtime/routes/conversation-routes.ts +4 -43
- 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.test.ts +221 -3
- package/src/runtime/routes/memory-item-routes.ts +144 -4
- 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 +175 -0
- 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/schedule/schedule-store.ts +0 -21
- 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/skills/inline-command-render.ts +5 -1
- package/src/skills/inline-command-runner.ts +30 -2
- 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/memory/handlers.ts +1 -129
- package/src/tools/network/script-proxy/session-manager.ts +19 -4
- package/src/tools/network/web-fetch.ts +3 -1
- package/src/tools/permission-checker.ts +18 -0
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/skills/load.ts +9 -2
- package/src/tools/types.ts +0 -8
- package/src/util/errors.ts +0 -12
- package/src/util/platform.ts +8 -55
- package/src/util/xml.ts +8 -0
- package/src/workspace/git-service.ts +5 -2
- package/src/workspace/heartbeat-service.ts +5 -24
- 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__/archive-recall.test.ts +0 -560
- 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__/conversation-memory-dirty-tail.test.ts +0 -150
- package/src/__tests__/conversation-switch-memory-reduction.test.ts +0 -474
- package/src/__tests__/db-memory-archive-migration.test.ts +0 -372
- package/src/__tests__/db-memory-brief-state-migration.test.ts +0 -213
- package/src/__tests__/db-memory-reducer-checkpoints.test.ts +0 -273
- package/src/__tests__/diagnostics-export.test.ts +0 -288
- package/src/__tests__/local-gateway-health.test.ts +0 -209
- package/src/__tests__/memory-brief-open-loops.test.ts +0 -530
- package/src/__tests__/memory-brief-time.test.ts +0 -285
- package/src/__tests__/memory-brief-wrapper.test.ts +0 -311
- package/src/__tests__/memory-chunk-archive.test.ts +0 -400
- package/src/__tests__/memory-chunk-dual-write.test.ts +0 -453
- package/src/__tests__/memory-episode-archive.test.ts +0 -370
- package/src/__tests__/memory-episode-dual-write.test.ts +0 -626
- package/src/__tests__/memory-observation-archive.test.ts +0 -375
- package/src/__tests__/memory-observation-dual-write.test.ts +0 -318
- package/src/__tests__/memory-reducer-job.test.ts +0 -538
- package/src/__tests__/memory-reducer-scheduling.test.ts +0 -473
- package/src/__tests__/memory-reducer-store.test.ts +0 -728
- package/src/__tests__/memory-reducer-types.test.ts +0 -707
- package/src/__tests__/memory-reducer.test.ts +0 -704
- package/src/__tests__/memory-simplified-config.test.ts +0 -281
- package/src/__tests__/secret-ingress-handler.test.ts +0 -120
- package/src/__tests__/simplified-memory-e2e.test.ts +0 -666
- package/src/__tests__/simplified-memory-runtime.test.ts +0 -616
- 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/memory-simplified.ts +0 -101
- package/src/config/schemas/swarm.ts +0 -82
- package/src/logfire.ts +0 -135
- package/src/memory/archive-recall.ts +0 -516
- package/src/memory/archive-store.ts +0 -400
- package/src/memory/brief-formatting.ts +0 -33
- package/src/memory/brief-open-loops.ts +0 -266
- package/src/memory/brief-time.ts +0 -162
- package/src/memory/brief.ts +0 -75
- package/src/memory/job-handlers/backfill-simplified-memory.ts +0 -462
- package/src/memory/job-handlers/reduce-conversation-memory.ts +0 -229
- package/src/memory/migrations/185-memory-brief-state.ts +0 -52
- package/src/memory/migrations/186-memory-archive.ts +0 -109
- package/src/memory/migrations/187-memory-reducer-checkpoints.ts +0 -19
- package/src/memory/reducer-scheduler.ts +0 -242
- package/src/memory/reducer-store.ts +0 -271
- package/src/memory/reducer-types.ts +0 -106
- package/src/memory/reducer.ts +0 -467
- package/src/memory/schema/memory-archive.ts +0 -121
- package/src/memory/schema/memory-brief.ts +0 -55
- 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
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Covers:
|
|
6
6
|
* - Flag OFF blocks all exposure paths
|
|
7
7
|
* - Missing persisted value falls back to code default
|
|
8
|
-
* -
|
|
8
|
+
* - Protected feature-flags.json is the sole override mechanism
|
|
9
9
|
* - Undeclared keys default to enabled
|
|
10
10
|
*/
|
|
11
11
|
import {
|
|
@@ -101,7 +101,6 @@ mock.module("../config/loader.js", () => ({
|
|
|
101
101
|
invalidateConfigCache: () => {},
|
|
102
102
|
getNestedValue: () => undefined,
|
|
103
103
|
setNestedValue: () => {},
|
|
104
|
-
syncConfigToLockfile: () => {},
|
|
105
104
|
}));
|
|
106
105
|
|
|
107
106
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
@@ -120,7 +119,7 @@ mock.module("../tools/credentials/metadata-store.js", () => ({
|
|
|
120
119
|
}));
|
|
121
120
|
|
|
122
121
|
const { buildSystemPrompt } = await import("../prompts/system-prompt.js");
|
|
123
|
-
const { isAssistantFeatureFlagEnabled } =
|
|
122
|
+
const { isAssistantFeatureFlagEnabled, _setOverridesForTesting } =
|
|
124
123
|
await import("../config/assistant-feature-flags.js");
|
|
125
124
|
const { skillFlagKey } = await import("../config/skill-state.js");
|
|
126
125
|
|
|
@@ -130,6 +129,7 @@ const { skillFlagKey } = await import("../config/skill-state.js");
|
|
|
130
129
|
|
|
131
130
|
beforeEach(() => {
|
|
132
131
|
mkdirSync(TEST_DIR, { recursive: true });
|
|
132
|
+
_setOverridesForTesting({});
|
|
133
133
|
currentConfig = {
|
|
134
134
|
services: {
|
|
135
135
|
inference: {
|
|
@@ -148,6 +148,7 @@ beforeEach(() => {
|
|
|
148
148
|
});
|
|
149
149
|
|
|
150
150
|
afterEach(() => {
|
|
151
|
+
_setOverridesForTesting({});
|
|
151
152
|
if (existsSync(TEST_DIR)) {
|
|
152
153
|
rmSync(TEST_DIR, { recursive: true, force: true });
|
|
153
154
|
}
|
|
@@ -198,11 +199,12 @@ describe("buildSystemPrompt assistant feature flag filtering", () => {
|
|
|
198
199
|
"browser",
|
|
199
200
|
);
|
|
200
201
|
|
|
202
|
+
_setOverridesForTesting({
|
|
203
|
+
[DECLARED_FLAG_KEY]: false,
|
|
204
|
+
"feature_flags.browser.enabled": true,
|
|
205
|
+
});
|
|
206
|
+
|
|
201
207
|
currentConfig = {
|
|
202
|
-
assistantFeatureFlagValues: {
|
|
203
|
-
[DECLARED_FLAG_KEY]: false,
|
|
204
|
-
"feature_flags.browser.enabled": true,
|
|
205
|
-
},
|
|
206
208
|
services: {
|
|
207
209
|
inference: {
|
|
208
210
|
mode: "your-own",
|
|
@@ -282,11 +284,12 @@ describe("buildSystemPrompt assistant feature flag filtering", () => {
|
|
|
282
284
|
"email-channel",
|
|
283
285
|
);
|
|
284
286
|
|
|
287
|
+
_setOverridesForTesting({
|
|
288
|
+
[DECLARED_FLAG_KEY]: false,
|
|
289
|
+
"feature_flags.email-channel.enabled": false,
|
|
290
|
+
});
|
|
291
|
+
|
|
285
292
|
currentConfig = {
|
|
286
|
-
assistantFeatureFlagValues: {
|
|
287
|
-
[DECLARED_FLAG_KEY]: false,
|
|
288
|
-
"feature_flags.email-channel.enabled": false,
|
|
289
|
-
},
|
|
290
293
|
services: {
|
|
291
294
|
inference: {
|
|
292
295
|
mode: "your-own",
|
|
@@ -311,7 +314,7 @@ describe("buildSystemPrompt assistant feature flag filtering", () => {
|
|
|
311
314
|
expect(result).not.toContain("**email-channel**");
|
|
312
315
|
});
|
|
313
316
|
|
|
314
|
-
test("
|
|
317
|
+
test("file-based overrides control visibility", () => {
|
|
315
318
|
createSkillOnDisk(
|
|
316
319
|
DECLARED_SKILL_ID,
|
|
317
320
|
"Contacts",
|
|
@@ -319,8 +322,9 @@ describe("buildSystemPrompt assistant feature flag filtering", () => {
|
|
|
319
322
|
DECLARED_FLAG_ID,
|
|
320
323
|
);
|
|
321
324
|
|
|
325
|
+
_setOverridesForTesting({ [DECLARED_FLAG_KEY]: true });
|
|
326
|
+
|
|
322
327
|
currentConfig = {
|
|
323
|
-
assistantFeatureFlagValues: { [DECLARED_FLAG_KEY]: true },
|
|
324
328
|
services: {
|
|
325
329
|
inference: {
|
|
326
330
|
mode: "your-own",
|
|
@@ -352,8 +356,9 @@ describe("buildSystemPrompt assistant feature flag filtering", () => {
|
|
|
352
356
|
"browser",
|
|
353
357
|
);
|
|
354
358
|
|
|
359
|
+
_setOverridesForTesting({ "feature_flags.browser.enabled": false });
|
|
360
|
+
|
|
355
361
|
currentConfig = {
|
|
356
|
-
assistantFeatureFlagValues: { "feature_flags.browser.enabled": false },
|
|
357
362
|
services: {
|
|
358
363
|
inference: {
|
|
359
364
|
mode: "your-own",
|
|
@@ -446,18 +451,16 @@ describe("buildSystemPrompt assistant feature flag filtering", () => {
|
|
|
446
451
|
// ---------------------------------------------------------------------------
|
|
447
452
|
|
|
448
453
|
describe("isAssistantFeatureFlagEnabled", () => {
|
|
449
|
-
test("reads from
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
} as any;
|
|
454
|
+
test("reads from file-based overrides", () => {
|
|
455
|
+
_setOverridesForTesting({ [DECLARED_FLAG_KEY]: true });
|
|
456
|
+
const config = {} as any;
|
|
453
457
|
|
|
454
458
|
expect(isAssistantFeatureFlagEnabled(DECLARED_FLAG_KEY, config)).toBe(true);
|
|
455
459
|
});
|
|
456
460
|
|
|
457
|
-
test("explicit false override in
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
} as any;
|
|
461
|
+
test("explicit false override in file-based overrides", () => {
|
|
462
|
+
_setOverridesForTesting({ [DECLARED_FLAG_KEY]: false });
|
|
463
|
+
const config = {} as any;
|
|
461
464
|
|
|
462
465
|
expect(isAssistantFeatureFlagEnabled(DECLARED_FLAG_KEY, config)).toBe(
|
|
463
466
|
false,
|
|
@@ -483,10 +486,9 @@ describe("isAssistantFeatureFlagEnabled", () => {
|
|
|
483
486
|
).toBe(true);
|
|
484
487
|
});
|
|
485
488
|
|
|
486
|
-
test("undeclared flag respects persisted
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
} as any;
|
|
489
|
+
test("undeclared flag respects persisted override", () => {
|
|
490
|
+
_setOverridesForTesting({ "feature_flags.browser.enabled": false });
|
|
491
|
+
const config = {} as any;
|
|
490
492
|
|
|
491
493
|
expect(
|
|
492
494
|
isAssistantFeatureFlagEnabled("feature_flags.browser.enabled", config),
|
|
@@ -496,9 +498,8 @@ describe("isAssistantFeatureFlagEnabled", () => {
|
|
|
496
498
|
|
|
497
499
|
describe("isAssistantFeatureFlagEnabled with skillFlagKey", () => {
|
|
498
500
|
test("resolves skill flag via canonical path", () => {
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
} as any;
|
|
501
|
+
_setOverridesForTesting({ [DECLARED_FLAG_KEY]: false });
|
|
502
|
+
const config = {} as any;
|
|
502
503
|
|
|
503
504
|
expect(
|
|
504
505
|
isAssistantFeatureFlagEnabled(
|
|
@@ -7,13 +7,10 @@
|
|
|
7
7
|
import { afterAll, beforeAll, describe, expect, mock, test } from "bun:test";
|
|
8
8
|
|
|
9
9
|
mock.module("../config/loader.js", () => ({
|
|
10
|
-
getConfig: () => ({
|
|
11
|
-
assistantFeatureFlagValues: {
|
|
12
|
-
"feature_flags.browser.enabled": true,
|
|
13
|
-
},
|
|
14
|
-
}),
|
|
10
|
+
getConfig: () => ({}),
|
|
15
11
|
}));
|
|
16
12
|
|
|
13
|
+
import { _setOverridesForTesting } from "../config/assistant-feature-flags.js";
|
|
17
14
|
import {
|
|
18
15
|
projectSkillTools,
|
|
19
16
|
resetSkillToolProjection,
|
|
@@ -35,11 +32,15 @@ import {
|
|
|
35
32
|
|
|
36
33
|
afterAll(() => {
|
|
37
34
|
__resetRegistryForTesting();
|
|
35
|
+
_setOverridesForTesting({});
|
|
38
36
|
});
|
|
39
37
|
|
|
40
38
|
describe("browser skill migration end-state", () => {
|
|
41
39
|
beforeAll(async () => {
|
|
42
40
|
__resetRegistryForTesting();
|
|
41
|
+
_setOverridesForTesting({
|
|
42
|
+
"feature_flags.browser.enabled": true,
|
|
43
|
+
});
|
|
43
44
|
await initializeTools();
|
|
44
45
|
});
|
|
45
46
|
|
|
@@ -54,17 +54,6 @@ mock.module("../daemon/conversation-tool-setup.js", () => ({
|
|
|
54
54
|
buildToolDefinitions: () => MOCK_TOOLS,
|
|
55
55
|
}));
|
|
56
56
|
|
|
57
|
-
const mockCheckIngressForSecrets = mock((content: string) => ({
|
|
58
|
-
blocked: false,
|
|
59
|
-
userNotice: "",
|
|
60
|
-
detectedTypes: [] as string[],
|
|
61
|
-
normalizedContent: content,
|
|
62
|
-
}));
|
|
63
|
-
|
|
64
|
-
mock.module("../security/secret-ingress.js", () => ({
|
|
65
|
-
checkIngressForSecrets: mockCheckIngressForSecrets,
|
|
66
|
-
}));
|
|
67
|
-
|
|
68
57
|
const MOCK_SYSTEM_PROMPT = "You are a helpful assistant.";
|
|
69
58
|
const mockBuildSystemPrompt = mock(() => MOCK_SYSTEM_PROMPT);
|
|
70
59
|
|
|
@@ -236,34 +225,6 @@ describe("POST /v1/btw", () => {
|
|
|
236
225
|
expect(body.error.message).toContain("content");
|
|
237
226
|
});
|
|
238
227
|
|
|
239
|
-
test("returns 422 when content includes a blocked secret", async () => {
|
|
240
|
-
mockCheckIngressForSecrets.mockReturnValueOnce({
|
|
241
|
-
blocked: true,
|
|
242
|
-
userNotice: "Secret detected",
|
|
243
|
-
detectedTypes: ["api_key"],
|
|
244
|
-
normalizedContent: "sk-test-123",
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
const provider = makeMockProvider();
|
|
248
|
-
const session = makeMockSession(provider);
|
|
249
|
-
const res = await callHandler(
|
|
250
|
-
{ conversationKey: "key", content: "sk-test-123" },
|
|
251
|
-
{ sendMessageDeps: makeSendMessageDeps(session) },
|
|
252
|
-
);
|
|
253
|
-
|
|
254
|
-
expect(res.status).toBe(422);
|
|
255
|
-
const body = (await res.json()) as {
|
|
256
|
-
accepted: boolean;
|
|
257
|
-
error: string;
|
|
258
|
-
message: string;
|
|
259
|
-
detectedTypes: string[];
|
|
260
|
-
};
|
|
261
|
-
expect(body.accepted).toBe(false);
|
|
262
|
-
expect(body.error).toBe("secret_blocked");
|
|
263
|
-
expect(body.detectedTypes).toEqual(["api_key"]);
|
|
264
|
-
expect(provider.sendMessage).not.toHaveBeenCalled();
|
|
265
|
-
});
|
|
266
|
-
|
|
267
228
|
// -- Service unavailability (503) --
|
|
268
229
|
|
|
269
230
|
test("returns 503 when sendMessageDeps is unavailable", async () => {
|
|
@@ -40,46 +40,6 @@ let twilioInitiateCallArgs: Array<Record<string, unknown>> = [];
|
|
|
40
40
|
let mockIngressEnabled = true;
|
|
41
41
|
let mockIngressPublicBaseUrl = "https://test.example.com";
|
|
42
42
|
|
|
43
|
-
interface MockGatewayHealthResult {
|
|
44
|
-
target: string;
|
|
45
|
-
healthy: boolean;
|
|
46
|
-
localDeployment: boolean;
|
|
47
|
-
error?: string;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
interface MockEnsureLocalGatewayReadyResult extends MockGatewayHealthResult {
|
|
51
|
-
recovered: boolean;
|
|
52
|
-
recoveryAttempted: boolean;
|
|
53
|
-
recoverySkipped: boolean;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
let probeLocalGatewayHealthResults: MockGatewayHealthResult[] = [];
|
|
57
|
-
let probeLocalGatewayHealthCallCount = 0;
|
|
58
|
-
let ensureLocalGatewayReadyResult: MockEnsureLocalGatewayReadyResult;
|
|
59
|
-
let ensureLocalGatewayReadyCallCount = 0;
|
|
60
|
-
|
|
61
|
-
function makeGatewayHealthResult(
|
|
62
|
-
overrides: Partial<MockGatewayHealthResult> = {},
|
|
63
|
-
): MockGatewayHealthResult {
|
|
64
|
-
return {
|
|
65
|
-
target: "http://127.0.0.1:7830",
|
|
66
|
-
healthy: true,
|
|
67
|
-
localDeployment: true,
|
|
68
|
-
...overrides,
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function makeRecoveryResult(
|
|
73
|
-
overrides: Partial<MockEnsureLocalGatewayReadyResult> = {},
|
|
74
|
-
): MockEnsureLocalGatewayReadyResult {
|
|
75
|
-
return {
|
|
76
|
-
...makeGatewayHealthResult(),
|
|
77
|
-
recovered: false,
|
|
78
|
-
recoveryAttempted: false,
|
|
79
|
-
recoverySkipped: false,
|
|
80
|
-
...overrides,
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
43
|
|
|
84
44
|
mock.module("../calls/twilio-config.js", () => ({
|
|
85
45
|
getTwilioConfig: (assistantId?: string) => ({
|
|
@@ -160,18 +120,6 @@ mock.module("../memory/conversation-title-service.js", () => ({
|
|
|
160
120
|
queueGenerateConversationTitle: () => {},
|
|
161
121
|
}));
|
|
162
122
|
|
|
163
|
-
mock.module("../runtime/local-gateway-health.js", () => ({
|
|
164
|
-
probeLocalGatewayHealth: async () => {
|
|
165
|
-
probeLocalGatewayHealthCallCount++;
|
|
166
|
-
const next =
|
|
167
|
-
probeLocalGatewayHealthResults.shift() ?? makeGatewayHealthResult();
|
|
168
|
-
return next;
|
|
169
|
-
},
|
|
170
|
-
ensureLocalGatewayReady: async () => {
|
|
171
|
-
ensureLocalGatewayReadyCallCount++;
|
|
172
|
-
return ensureLocalGatewayReadyResult;
|
|
173
|
-
},
|
|
174
|
-
}));
|
|
175
123
|
|
|
176
124
|
mock.module("../daemon/handlers/config-ingress.js", () => ({
|
|
177
125
|
computeGatewayTarget: () => "http://127.0.0.1:7830",
|
|
@@ -204,13 +152,6 @@ beforeEach(() => {
|
|
|
204
152
|
twilioInitiateCallArgs = [];
|
|
205
153
|
mockIngressEnabled = true;
|
|
206
154
|
mockIngressPublicBaseUrl = "https://test.example.com";
|
|
207
|
-
probeLocalGatewayHealthResults = [
|
|
208
|
-
makeGatewayHealthResult(),
|
|
209
|
-
makeGatewayHealthResult(),
|
|
210
|
-
];
|
|
211
|
-
probeLocalGatewayHealthCallCount = 0;
|
|
212
|
-
ensureLocalGatewayReadyResult = makeRecoveryResult();
|
|
213
|
-
ensureLocalGatewayReadyCallCount = 0;
|
|
214
155
|
});
|
|
215
156
|
|
|
216
157
|
let ensuredConvIds = new Set<string>();
|
|
@@ -446,9 +387,6 @@ describe("startCall — pointer message regression", () => {
|
|
|
446
387
|
expect(result.status).toBe(503);
|
|
447
388
|
expect(result.error).toContain("Public ingress");
|
|
448
389
|
}
|
|
449
|
-
expect(probeLocalGatewayHealthCallCount).toBe(0);
|
|
450
|
-
expect(ensureLocalGatewayReadyCallCount).toBe(0);
|
|
451
|
-
// No reconcile calls expected (reconcile triggers removed).
|
|
452
390
|
expect(twilioInitiateCallCount).toBe(0);
|
|
453
391
|
|
|
454
392
|
await new Promise((r) => setTimeout(r, 50));
|
|
@@ -459,72 +397,6 @@ describe("startCall — pointer message regression", () => {
|
|
|
459
397
|
expect(text!).toContain("failed");
|
|
460
398
|
});
|
|
461
399
|
|
|
462
|
-
test("never dials Twilio when the local callback gateway stays unhealthy", async () => {
|
|
463
|
-
const convId = "conv-domain-preflight-unhealthy";
|
|
464
|
-
ensureConversation(convId);
|
|
465
|
-
probeLocalGatewayHealthResults = [
|
|
466
|
-
makeGatewayHealthResult({
|
|
467
|
-
healthy: false,
|
|
468
|
-
error: "Gateway health check returned HTTP 503",
|
|
469
|
-
}),
|
|
470
|
-
makeGatewayHealthResult({
|
|
471
|
-
healthy: false,
|
|
472
|
-
error: "Gateway health check returned HTTP 503",
|
|
473
|
-
}),
|
|
474
|
-
];
|
|
475
|
-
ensureLocalGatewayReadyResult = makeRecoveryResult({
|
|
476
|
-
healthy: false,
|
|
477
|
-
error: "Gateway health check returned HTTP 503",
|
|
478
|
-
recovered: false,
|
|
479
|
-
recoveryAttempted: true,
|
|
480
|
-
});
|
|
481
|
-
|
|
482
|
-
const result = await startCall({
|
|
483
|
-
phoneNumber: "+15559876543",
|
|
484
|
-
task: "Test call",
|
|
485
|
-
conversationId: convId,
|
|
486
|
-
});
|
|
487
|
-
|
|
488
|
-
expect(result.ok).toBe(false);
|
|
489
|
-
if (!result.ok) {
|
|
490
|
-
expect(result.status).toBe(503);
|
|
491
|
-
expect(result.error).toContain("still unhealthy");
|
|
492
|
-
}
|
|
493
|
-
expect(probeLocalGatewayHealthCallCount).toBe(2);
|
|
494
|
-
expect(ensureLocalGatewayReadyCallCount).toBe(1);
|
|
495
|
-
// No reconcile calls expected (reconcile triggers removed).
|
|
496
|
-
expect(twilioInitiateCallCount).toBe(0);
|
|
497
|
-
});
|
|
498
|
-
|
|
499
|
-
test("can recover a local gateway and then place exactly one outbound Twilio call", async () => {
|
|
500
|
-
const convId = "conv-domain-preflight-recovery";
|
|
501
|
-
ensureConversation(convId);
|
|
502
|
-
probeLocalGatewayHealthResults = [
|
|
503
|
-
makeGatewayHealthResult({
|
|
504
|
-
healthy: false,
|
|
505
|
-
error: "Gateway health check returned HTTP 503",
|
|
506
|
-
}),
|
|
507
|
-
makeGatewayHealthResult(),
|
|
508
|
-
];
|
|
509
|
-
ensureLocalGatewayReadyResult = makeRecoveryResult({
|
|
510
|
-
healthy: true,
|
|
511
|
-
recovered: true,
|
|
512
|
-
recoveryAttempted: true,
|
|
513
|
-
});
|
|
514
|
-
|
|
515
|
-
const result = await startCall({
|
|
516
|
-
phoneNumber: "+15559876543",
|
|
517
|
-
task: "Test call",
|
|
518
|
-
conversationId: convId,
|
|
519
|
-
});
|
|
520
|
-
|
|
521
|
-
expect(result.ok).toBe(true);
|
|
522
|
-
expect(probeLocalGatewayHealthCallCount).toBe(2);
|
|
523
|
-
expect(ensureLocalGatewayReadyCallCount).toBe(1);
|
|
524
|
-
// Gateway reconcile triggers have been removed; the gateway reads
|
|
525
|
-
// credentials and config via TTL caches.
|
|
526
|
-
expect(twilioInitiateCallCount).toBe(1);
|
|
527
|
-
});
|
|
528
400
|
|
|
529
401
|
test("failed call writes a failed pointer to the initiating conversation", async () => {
|
|
530
402
|
const convId = "conv-domain-ptr-fail";
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { CesRpcMethod } from "@vellumai/ces-contracts";
|
|
4
|
+
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// Mock state
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
const callFn = mock(
|
|
10
|
+
async (_method: string, _request: unknown): Promise<unknown> => ({}),
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
const isReadyFn = mock((): boolean => true);
|
|
14
|
+
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// Mock modules — before importing module under test
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
mock.module("../util/logger.js", () => ({
|
|
20
|
+
getLogger: () =>
|
|
21
|
+
new Proxy({} as Record<string, unknown>, {
|
|
22
|
+
get: () => () => {},
|
|
23
|
+
}),
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
// Import after mocking
|
|
27
|
+
import type { CesClient } from "../credential-execution/client.js";
|
|
28
|
+
import { CesRpcCredentialBackend } from "../security/ces-rpc-credential-backend.js";
|
|
29
|
+
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// Helpers
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
|
|
34
|
+
function createMockClient(): CesClient {
|
|
35
|
+
return {
|
|
36
|
+
handshake: mock(async () => ({ accepted: true })),
|
|
37
|
+
call: callFn as CesClient["call"],
|
|
38
|
+
updateAssistantApiKey: mock(async () => ({ updated: true })),
|
|
39
|
+
isReady: isReadyFn,
|
|
40
|
+
close: mock(() => {}),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// Tests
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
|
|
48
|
+
describe("CesRpcCredentialBackend", () => {
|
|
49
|
+
let client: CesClient;
|
|
50
|
+
let backend: CesRpcCredentialBackend;
|
|
51
|
+
|
|
52
|
+
beforeEach(() => {
|
|
53
|
+
callFn.mockClear();
|
|
54
|
+
isReadyFn.mockClear();
|
|
55
|
+
|
|
56
|
+
isReadyFn.mockReturnValue(true);
|
|
57
|
+
|
|
58
|
+
client = createMockClient();
|
|
59
|
+
backend = new CesRpcCredentialBackend(client);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("has name 'ces-rpc'", () => {
|
|
63
|
+
expect(backend.name).toBe("ces-rpc");
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// -------------------------------------------------------------------------
|
|
67
|
+
// isAvailable
|
|
68
|
+
// -------------------------------------------------------------------------
|
|
69
|
+
|
|
70
|
+
describe("isAvailable", () => {
|
|
71
|
+
test("returns true when client is ready", () => {
|
|
72
|
+
isReadyFn.mockReturnValue(true);
|
|
73
|
+
expect(backend.isAvailable()).toBe(true);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("returns false when client is not ready", () => {
|
|
77
|
+
isReadyFn.mockReturnValue(false);
|
|
78
|
+
expect(backend.isAvailable()).toBe(false);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// -------------------------------------------------------------------------
|
|
83
|
+
// get
|
|
84
|
+
// -------------------------------------------------------------------------
|
|
85
|
+
|
|
86
|
+
describe("get", () => {
|
|
87
|
+
test("delegates to CesRpcMethod.GetCredential and returns value when found", async () => {
|
|
88
|
+
callFn.mockResolvedValue({ found: true, value: "my-secret" });
|
|
89
|
+
|
|
90
|
+
const result = await backend.get("test-account");
|
|
91
|
+
|
|
92
|
+
expect(callFn).toHaveBeenCalledWith(CesRpcMethod.GetCredential, {
|
|
93
|
+
account: "test-account",
|
|
94
|
+
});
|
|
95
|
+
expect(result).toEqual({ value: "my-secret", unreachable: false });
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("returns undefined value when credential not found", async () => {
|
|
99
|
+
callFn.mockResolvedValue({ found: false });
|
|
100
|
+
|
|
101
|
+
const result = await backend.get("missing-account");
|
|
102
|
+
|
|
103
|
+
expect(callFn).toHaveBeenCalledWith(CesRpcMethod.GetCredential, {
|
|
104
|
+
account: "missing-account",
|
|
105
|
+
});
|
|
106
|
+
expect(result).toEqual({ value: undefined, unreachable: false });
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("returns unreachable when RPC call throws", async () => {
|
|
110
|
+
callFn.mockRejectedValue(new Error("transport error"));
|
|
111
|
+
|
|
112
|
+
const result = await backend.get("broken-account");
|
|
113
|
+
|
|
114
|
+
expect(result).toEqual({ value: undefined, unreachable: true });
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// -------------------------------------------------------------------------
|
|
119
|
+
// set
|
|
120
|
+
// -------------------------------------------------------------------------
|
|
121
|
+
|
|
122
|
+
describe("set", () => {
|
|
123
|
+
test("delegates to CesRpcMethod.SetCredential and returns true on success", async () => {
|
|
124
|
+
callFn.mockResolvedValue({ ok: true });
|
|
125
|
+
|
|
126
|
+
const result = await backend.set("test-account", "new-secret");
|
|
127
|
+
|
|
128
|
+
expect(callFn).toHaveBeenCalledWith(CesRpcMethod.SetCredential, {
|
|
129
|
+
account: "test-account",
|
|
130
|
+
value: "new-secret",
|
|
131
|
+
});
|
|
132
|
+
expect(result).toBe(true);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test("returns false when RPC call throws", async () => {
|
|
136
|
+
callFn.mockRejectedValue(new Error("transport error"));
|
|
137
|
+
|
|
138
|
+
const result = await backend.set("test-account", "new-secret");
|
|
139
|
+
|
|
140
|
+
expect(result).toBe(false);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// -------------------------------------------------------------------------
|
|
145
|
+
// delete
|
|
146
|
+
// -------------------------------------------------------------------------
|
|
147
|
+
|
|
148
|
+
describe("delete", () => {
|
|
149
|
+
test("delegates to CesRpcMethod.DeleteCredential and returns the result", async () => {
|
|
150
|
+
callFn.mockResolvedValue({ result: "deleted" });
|
|
151
|
+
|
|
152
|
+
const result = await backend.delete("test-account");
|
|
153
|
+
|
|
154
|
+
expect(callFn).toHaveBeenCalledWith(CesRpcMethod.DeleteCredential, {
|
|
155
|
+
account: "test-account",
|
|
156
|
+
});
|
|
157
|
+
expect(result).toBe("deleted");
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
test("returns not-found result from CES", async () => {
|
|
161
|
+
callFn.mockResolvedValue({ result: "not-found" });
|
|
162
|
+
|
|
163
|
+
const result = await backend.delete("nonexistent-account");
|
|
164
|
+
|
|
165
|
+
expect(result).toBe("not-found");
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test("returns 'error' when RPC call throws", async () => {
|
|
169
|
+
callFn.mockRejectedValue(new Error("transport error"));
|
|
170
|
+
|
|
171
|
+
const result = await backend.delete("test-account");
|
|
172
|
+
|
|
173
|
+
expect(result).toBe("error");
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// -------------------------------------------------------------------------
|
|
178
|
+
// list
|
|
179
|
+
// -------------------------------------------------------------------------
|
|
180
|
+
|
|
181
|
+
describe("list", () => {
|
|
182
|
+
test("delegates to CesRpcMethod.ListCredentials and returns accounts", async () => {
|
|
183
|
+
callFn.mockResolvedValue({ accounts: ["account-a", "account-b"] });
|
|
184
|
+
|
|
185
|
+
const result = await backend.list();
|
|
186
|
+
|
|
187
|
+
expect(callFn).toHaveBeenCalledWith(CesRpcMethod.ListCredentials, {});
|
|
188
|
+
expect(result).toEqual(["account-a", "account-b"]);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test("returns empty array when RPC call throws", async () => {
|
|
192
|
+
callFn.mockRejectedValue(new Error("transport error"));
|
|
193
|
+
|
|
194
|
+
const result = await backend.list();
|
|
195
|
+
|
|
196
|
+
expect(result).toEqual([]);
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
});
|
|
@@ -38,11 +38,6 @@ mock.module("../util/logger.js", () => ({
|
|
|
38
38
|
}),
|
|
39
39
|
}));
|
|
40
40
|
|
|
41
|
-
// Mock security check to always pass
|
|
42
|
-
mock.module("../security/secret-ingress.js", () => ({
|
|
43
|
-
checkIngressForSecrets: () => ({ blocked: false }),
|
|
44
|
-
}));
|
|
45
|
-
|
|
46
41
|
// Mock render to return the raw content as text
|
|
47
42
|
mock.module("../daemon/handlers/shared.js", () => ({
|
|
48
43
|
renderHistoryContent: (content: unknown) => ({
|