@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
|
@@ -537,8 +537,8 @@ describe("keychain-broker-client", () => {
|
|
|
537
537
|
writeFileSync(SOCKET_PATH, "");
|
|
538
538
|
expect(client.isAvailable()).toBe(false);
|
|
539
539
|
|
|
540
|
-
// Advance time past the first cooldown (
|
|
541
|
-
fakeNow +=
|
|
540
|
+
// Advance time past the first cooldown (5s)
|
|
541
|
+
fakeNow += 5_001;
|
|
542
542
|
expect(client.isAvailable()).toBe(true);
|
|
543
543
|
|
|
544
544
|
// Now start a real broker and verify the client reconnects
|
|
@@ -558,8 +558,8 @@ describe("keychain-broker-client", () => {
|
|
|
558
558
|
const client = createBrokerClient();
|
|
559
559
|
await client.ping();
|
|
560
560
|
|
|
561
|
-
// Advance past first cooldown (
|
|
562
|
-
fakeNow +=
|
|
561
|
+
// Advance past first cooldown (5s)
|
|
562
|
+
fakeNow += 5_001;
|
|
563
563
|
|
|
564
564
|
// Start broker — reconnection should succeed and reset counters
|
|
565
565
|
const broker = createMockBroker();
|
|
@@ -578,15 +578,15 @@ describe("keychain-broker-client", () => {
|
|
|
578
578
|
rmSync(SOCKET_PATH, { force: true });
|
|
579
579
|
|
|
580
580
|
// This new failure should start from the beginning of the cooldown
|
|
581
|
-
// schedule (
|
|
581
|
+
// schedule (5s), not escalated.
|
|
582
582
|
await client.ping();
|
|
583
583
|
|
|
584
|
-
// Verify cooldown is back to
|
|
584
|
+
// Verify cooldown is back to 5s (not 15s)
|
|
585
585
|
writeFileSync(SOCKET_PATH, "");
|
|
586
586
|
expect(client.isAvailable()).toBe(false);
|
|
587
587
|
|
|
588
|
-
//
|
|
589
|
-
fakeNow +=
|
|
588
|
+
// 5s should be enough to clear cooldown
|
|
589
|
+
fakeNow += 5_001;
|
|
590
590
|
expect(client.isAvailable()).toBe(true);
|
|
591
591
|
}, 15_000);
|
|
592
592
|
|
|
@@ -594,60 +594,73 @@ describe("keychain-broker-client", () => {
|
|
|
594
594
|
const client = createBrokerClient();
|
|
595
595
|
|
|
596
596
|
// First failure round: two attempts (first + immediate retry) ->
|
|
597
|
-
// consecutiveFailures=2, cooldown index = max(2-2,0) = 0 ->
|
|
597
|
+
// consecutiveFailures=2, cooldown index = max(2-2,0) = 0 -> 5s.
|
|
598
598
|
await client.ping();
|
|
599
599
|
|
|
600
600
|
writeFileSync(SOCKET_PATH, "");
|
|
601
601
|
expect(client.isAvailable()).toBe(false);
|
|
602
602
|
|
|
603
|
-
//
|
|
604
|
-
fakeNow +=
|
|
603
|
+
// 5s should clear the first cooldown
|
|
604
|
+
fakeNow += 5_001;
|
|
605
605
|
expect(client.isAvailable()).toBe(true);
|
|
606
606
|
|
|
607
607
|
// Remove socket to trigger another failure. After cooldown elapses,
|
|
608
608
|
// ensureConnected clears unavailableSince and tries connect().
|
|
609
609
|
// This failure increments consecutiveFailures to 3 (no immediate retry
|
|
610
610
|
// since consecutiveFailures > 1 after increment).
|
|
611
|
-
// Cooldown index = max(3-2,0) = 1 ->
|
|
611
|
+
// Cooldown index = max(3-2,0) = 1 -> 15s.
|
|
612
612
|
rmSync(SOCKET_PATH, { force: true });
|
|
613
613
|
await client.ping();
|
|
614
614
|
|
|
615
615
|
writeFileSync(SOCKET_PATH, "");
|
|
616
616
|
expect(client.isAvailable()).toBe(false);
|
|
617
617
|
|
|
618
|
-
fakeNow +=
|
|
619
|
-
expect(client.isAvailable()).toBe(false); //
|
|
618
|
+
fakeNow += 5_001;
|
|
619
|
+
expect(client.isAvailable()).toBe(false); // 5s not enough
|
|
620
620
|
|
|
621
|
-
fakeNow +=
|
|
621
|
+
fakeNow += 10_000; // total 15_001ms since this cooldown started
|
|
622
622
|
expect(client.isAvailable()).toBe(true);
|
|
623
623
|
|
|
624
|
-
// Another failure -> consecutiveFailures=4, index = max(4-2,0) = 2 ->
|
|
624
|
+
// Another failure -> consecutiveFailures=4, index = max(4-2,0) = 2 -> 30s
|
|
625
625
|
rmSync(SOCKET_PATH, { force: true });
|
|
626
626
|
await client.ping();
|
|
627
627
|
|
|
628
628
|
writeFileSync(SOCKET_PATH, "");
|
|
629
629
|
expect(client.isAvailable()).toBe(false);
|
|
630
630
|
|
|
631
|
-
fakeNow +=
|
|
631
|
+
fakeNow += 15_001;
|
|
632
|
+
expect(client.isAvailable()).toBe(false);
|
|
633
|
+
|
|
634
|
+
fakeNow += 15_000; // total 30_001ms
|
|
635
|
+
expect(client.isAvailable()).toBe(true);
|
|
636
|
+
|
|
637
|
+
// Another failure -> consecutiveFailures=5, index = max(5-2,0) = 3 -> 60s
|
|
638
|
+
rmSync(SOCKET_PATH, { force: true });
|
|
639
|
+
await client.ping();
|
|
640
|
+
|
|
641
|
+
writeFileSync(SOCKET_PATH, "");
|
|
642
|
+
expect(client.isAvailable()).toBe(false);
|
|
643
|
+
|
|
644
|
+
fakeNow += 30_001;
|
|
632
645
|
expect(client.isAvailable()).toBe(false);
|
|
633
646
|
|
|
634
|
-
fakeNow +=
|
|
647
|
+
fakeNow += 30_000; // total 60_001ms
|
|
635
648
|
expect(client.isAvailable()).toBe(true);
|
|
636
649
|
|
|
637
|
-
// Another failure -> consecutiveFailures=
|
|
650
|
+
// Another failure -> consecutiveFailures=6, index = min(max(6-2,0), 4) = 4 -> 300s (5min)
|
|
638
651
|
rmSync(SOCKET_PATH, { force: true });
|
|
639
652
|
await client.ping();
|
|
640
653
|
|
|
641
654
|
writeFileSync(SOCKET_PATH, "");
|
|
642
655
|
expect(client.isAvailable()).toBe(false);
|
|
643
656
|
|
|
644
|
-
fakeNow +=
|
|
657
|
+
fakeNow += 60_001;
|
|
645
658
|
expect(client.isAvailable()).toBe(false);
|
|
646
659
|
|
|
647
|
-
fakeNow +=
|
|
660
|
+
fakeNow += 240_000; // total 300_001ms
|
|
648
661
|
expect(client.isAvailable()).toBe(true);
|
|
649
662
|
|
|
650
|
-
// Another failure -> consecutiveFailures=
|
|
663
|
+
// Another failure -> consecutiveFailures=7, index = min(max(7-2,0), 4) = 4 -> 300s (capped)
|
|
651
664
|
rmSync(SOCKET_PATH, { force: true });
|
|
652
665
|
await client.ping();
|
|
653
666
|
|
|
@@ -658,4 +671,130 @@ describe("keychain-broker-client", () => {
|
|
|
658
671
|
expect(client.isAvailable()).toBe(true);
|
|
659
672
|
});
|
|
660
673
|
});
|
|
674
|
+
|
|
675
|
+
// -----------------------------------------------------------------------
|
|
676
|
+
// Connect timeout
|
|
677
|
+
// -----------------------------------------------------------------------
|
|
678
|
+
describe("connect timeout", () => {
|
|
679
|
+
let stopFn: (() => Promise<void>) | null = null;
|
|
680
|
+
|
|
681
|
+
beforeEach(() => {
|
|
682
|
+
writeFileSync(TOKEN_PATH, TEST_TOKEN);
|
|
683
|
+
});
|
|
684
|
+
|
|
685
|
+
afterEach(async () => {
|
|
686
|
+
if (stopFn) {
|
|
687
|
+
await stopFn();
|
|
688
|
+
stopFn = null;
|
|
689
|
+
}
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
test("rejects connect within 3 seconds when broker is unresponsive", async () => {
|
|
693
|
+
// Create a server that accepts connections but never responds
|
|
694
|
+
// (simulates an unresponsive broker process).
|
|
695
|
+
const activeConns = new Set<import("node:net").Socket>();
|
|
696
|
+
const server = createServer((conn) => {
|
|
697
|
+
activeConns.add(conn);
|
|
698
|
+
conn.on("close", () => activeConns.delete(conn));
|
|
699
|
+
// Accept connection but do nothing — no data, no close
|
|
700
|
+
});
|
|
701
|
+
await new Promise<void>((resolve) => {
|
|
702
|
+
server.listen(SOCKET_PATH, () => resolve());
|
|
703
|
+
});
|
|
704
|
+
stopFn = () =>
|
|
705
|
+
new Promise<void>((resolve) => {
|
|
706
|
+
for (const conn of activeConns) conn.destroy();
|
|
707
|
+
activeConns.clear();
|
|
708
|
+
server.close(() => resolve());
|
|
709
|
+
});
|
|
710
|
+
|
|
711
|
+
const client = createBrokerClient();
|
|
712
|
+
const start = Date.now();
|
|
713
|
+
const result = await client.ping();
|
|
714
|
+
const elapsed = Date.now() - start;
|
|
715
|
+
|
|
716
|
+
// Should return null (graceful fallback) and not hang indefinitely.
|
|
717
|
+
// The connect timeout is 3s; allow some slack but it should be well
|
|
718
|
+
// under 10s (the old behavior would hang for REQUEST_TIMEOUT_MS * retries).
|
|
719
|
+
expect(result).toBeNull();
|
|
720
|
+
expect(elapsed).toBeLessThan(10_000);
|
|
721
|
+
}, 15_000);
|
|
722
|
+
|
|
723
|
+
test("successful connect clears the connect timer", async () => {
|
|
724
|
+
// Normal broker that responds to pings — verifies the timer is cleared
|
|
725
|
+
// and doesn't fire after a successful connection.
|
|
726
|
+
const broker = createMockBroker();
|
|
727
|
+
broker.setHandler(() => ({ ok: true, result: { pong: true } }));
|
|
728
|
+
await broker.start();
|
|
729
|
+
stopFn = () => broker.stop();
|
|
730
|
+
|
|
731
|
+
const client = createBrokerClient();
|
|
732
|
+
const result = await client.ping();
|
|
733
|
+
expect(result).toEqual({ pong: true });
|
|
734
|
+
|
|
735
|
+
// Wait a bit past the connect timeout to ensure no stale timer fires
|
|
736
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
737
|
+
|
|
738
|
+
// Client should still work fine
|
|
739
|
+
const result2 = await client.ping();
|
|
740
|
+
expect(result2).toEqual({ pong: true });
|
|
741
|
+
});
|
|
742
|
+
});
|
|
743
|
+
|
|
744
|
+
// -----------------------------------------------------------------------
|
|
745
|
+
// Reduced initial cooldown
|
|
746
|
+
// -----------------------------------------------------------------------
|
|
747
|
+
describe("reduced initial cooldown", () => {
|
|
748
|
+
const originalDateNow = Date.now;
|
|
749
|
+
let fakeNow: number;
|
|
750
|
+
|
|
751
|
+
beforeEach(() => {
|
|
752
|
+
fakeNow = originalDateNow.call(Date);
|
|
753
|
+
Date.now = () => fakeNow;
|
|
754
|
+
writeFileSync(TOKEN_PATH, TEST_TOKEN);
|
|
755
|
+
});
|
|
756
|
+
|
|
757
|
+
afterEach(() => {
|
|
758
|
+
Date.now = originalDateNow;
|
|
759
|
+
});
|
|
760
|
+
|
|
761
|
+
test("first cooldown is 5 seconds, not 30 seconds", async () => {
|
|
762
|
+
const client = createBrokerClient();
|
|
763
|
+
|
|
764
|
+
// Trigger two connection failures (first + immediate retry)
|
|
765
|
+
await client.ping();
|
|
766
|
+
|
|
767
|
+
writeFileSync(SOCKET_PATH, "");
|
|
768
|
+
|
|
769
|
+
// Should still be in cooldown at 4 seconds
|
|
770
|
+
fakeNow += 4_000;
|
|
771
|
+
expect(client.isAvailable()).toBe(false);
|
|
772
|
+
|
|
773
|
+
// Should be available after 5 seconds
|
|
774
|
+
fakeNow += 1_001;
|
|
775
|
+
expect(client.isAvailable()).toBe(true);
|
|
776
|
+
});
|
|
777
|
+
|
|
778
|
+
test("second cooldown is 15 seconds", async () => {
|
|
779
|
+
const client = createBrokerClient();
|
|
780
|
+
|
|
781
|
+
// First failure round -> cooldown 5s
|
|
782
|
+
await client.ping();
|
|
783
|
+
|
|
784
|
+
// Clear first cooldown
|
|
785
|
+
fakeNow += 5_001;
|
|
786
|
+
|
|
787
|
+
// Second failure -> cooldown 15s
|
|
788
|
+
await client.ping();
|
|
789
|
+
|
|
790
|
+
writeFileSync(SOCKET_PATH, "");
|
|
791
|
+
expect(client.isAvailable()).toBe(false);
|
|
792
|
+
|
|
793
|
+
fakeNow += 14_000;
|
|
794
|
+
expect(client.isAvailable()).toBe(false);
|
|
795
|
+
|
|
796
|
+
fakeNow += 1_001;
|
|
797
|
+
expect(client.isAvailable()).toBe(true);
|
|
798
|
+
});
|
|
799
|
+
});
|
|
661
800
|
});
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for adaptive poll interval backoff in the memory jobs worker.
|
|
3
|
+
*
|
|
4
|
+
* Verifies that when no jobs are claimable, the poll interval doubles each
|
|
5
|
+
* tick (1.5s -> 3s -> 6s -> ... -> 30s cap), and resets to 1.5s when work
|
|
6
|
+
* is found.
|
|
7
|
+
*/
|
|
8
|
+
import { describe, expect, mock, test } from "bun:test";
|
|
9
|
+
|
|
10
|
+
// ── Mocks (must precede imports of tested module) ──────────────────
|
|
11
|
+
|
|
12
|
+
mock.module("../util/platform.js", () => ({
|
|
13
|
+
getDataDir: () => "/tmp/test-backoff",
|
|
14
|
+
isMacOS: () => false,
|
|
15
|
+
isLinux: () => true,
|
|
16
|
+
isWindows: () => false,
|
|
17
|
+
getPidPath: () => "/tmp/test-backoff/test.pid",
|
|
18
|
+
getDbPath: () => "/tmp/test-backoff/test.db",
|
|
19
|
+
getLogPath: () => "/tmp/test-backoff/test.log",
|
|
20
|
+
ensureDataDir: () => {},
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
mock.module("../util/logger.js", () => ({
|
|
24
|
+
getLogger: () =>
|
|
25
|
+
new Proxy({} as Record<string, unknown>, {
|
|
26
|
+
get: () => () => {},
|
|
27
|
+
}),
|
|
28
|
+
}));
|
|
29
|
+
|
|
30
|
+
// Mock config — memory disabled so runMemoryJobsOnce returns 0 immediately
|
|
31
|
+
mock.module("../config/loader.js", () => ({
|
|
32
|
+
getConfig: () => ({
|
|
33
|
+
memory: { enabled: false },
|
|
34
|
+
}),
|
|
35
|
+
loadConfig: () => ({
|
|
36
|
+
memory: { enabled: false },
|
|
37
|
+
}),
|
|
38
|
+
}));
|
|
39
|
+
|
|
40
|
+
// Mock jobs-store (accesses DB)
|
|
41
|
+
mock.module("../memory/jobs-store.js", () => ({
|
|
42
|
+
resetRunningJobsToPending: () => 0,
|
|
43
|
+
claimMemoryJobs: () => [],
|
|
44
|
+
completeMemoryJob: () => {},
|
|
45
|
+
deferMemoryJob: () => "deferred",
|
|
46
|
+
failMemoryJob: () => {},
|
|
47
|
+
failStalledJobs: () => 0,
|
|
48
|
+
enqueueCleanupStaleSupersededItemsJob: () => null,
|
|
49
|
+
enqueuePruneOldConversationsJob: () => null,
|
|
50
|
+
}));
|
|
51
|
+
|
|
52
|
+
// Mock db.js (rawRun used in sweepStaleItems)
|
|
53
|
+
mock.module("../memory/db.js", () => ({
|
|
54
|
+
rawRun: () => 0,
|
|
55
|
+
}));
|
|
56
|
+
|
|
57
|
+
import {
|
|
58
|
+
POLL_INTERVAL_MAX_MS,
|
|
59
|
+
POLL_INTERVAL_MIN_MS,
|
|
60
|
+
startMemoryJobsWorker,
|
|
61
|
+
} from "../memory/jobs-worker.js";
|
|
62
|
+
|
|
63
|
+
describe("memory jobs worker adaptive poll interval", () => {
|
|
64
|
+
test("exports expected poll interval constants", () => {
|
|
65
|
+
expect(POLL_INTERVAL_MIN_MS).toBe(1_500);
|
|
66
|
+
expect(POLL_INTERVAL_MAX_MS).toBe(30_000);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("backoff sequence doubles from min to max then caps", () => {
|
|
70
|
+
// Verify the math: starting at 1500, doubling each step, capped at 30000
|
|
71
|
+
const intervals: number[] = [];
|
|
72
|
+
let current = POLL_INTERVAL_MIN_MS;
|
|
73
|
+
for (let i = 0; i < 10; i++) {
|
|
74
|
+
intervals.push(current);
|
|
75
|
+
current = Math.min(current * 2, POLL_INTERVAL_MAX_MS);
|
|
76
|
+
}
|
|
77
|
+
expect(intervals).toEqual([
|
|
78
|
+
1_500, // tick 1
|
|
79
|
+
3_000, // tick 2
|
|
80
|
+
6_000, // tick 3
|
|
81
|
+
12_000, // tick 4
|
|
82
|
+
24_000, // tick 5
|
|
83
|
+
30_000, // tick 6 (capped)
|
|
84
|
+
30_000, // stays capped
|
|
85
|
+
30_000,
|
|
86
|
+
30_000,
|
|
87
|
+
30_000,
|
|
88
|
+
]);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test("worker schedules setTimeout with increasing intervals when idle", async () => {
|
|
92
|
+
const timeoutDelays: number[] = [];
|
|
93
|
+
const originalSetTimeout = globalThis.setTimeout;
|
|
94
|
+
const originalClearTimeout = globalThis.clearTimeout;
|
|
95
|
+
|
|
96
|
+
// Collect pending timer callbacks so we can fire them manually
|
|
97
|
+
const pendingCallbacks: Array<() => void> = [];
|
|
98
|
+
|
|
99
|
+
globalThis.setTimeout = ((fn: () => void, delay?: number) => {
|
|
100
|
+
if (delay !== undefined && delay >= POLL_INTERVAL_MIN_MS) {
|
|
101
|
+
timeoutDelays.push(delay);
|
|
102
|
+
pendingCallbacks.push(fn);
|
|
103
|
+
}
|
|
104
|
+
return 999 as unknown as ReturnType<typeof setTimeout>;
|
|
105
|
+
}) as typeof setTimeout;
|
|
106
|
+
globalThis.clearTimeout = (() => {}) as typeof clearTimeout;
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
const worker = startMemoryJobsWorker();
|
|
110
|
+
|
|
111
|
+
// Wait for the initial tick() promise to settle
|
|
112
|
+
await new Promise((resolve) => originalSetTimeout(resolve, 20));
|
|
113
|
+
|
|
114
|
+
// Fire pending timer callbacks to advance through the backoff sequence.
|
|
115
|
+
// Each callback triggers tick() which is async, so we await a microtask
|
|
116
|
+
// after each to let the promise chain settle and schedule the next timer.
|
|
117
|
+
for (let i = 0; i < 6; i++) {
|
|
118
|
+
const cb = pendingCallbacks.shift();
|
|
119
|
+
if (cb) {
|
|
120
|
+
cb();
|
|
121
|
+
await new Promise((resolve) => originalSetTimeout(resolve, 20));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
worker.stop();
|
|
126
|
+
|
|
127
|
+
// We should have captured several setTimeout calls with increasing delays
|
|
128
|
+
expect(timeoutDelays.length).toBeGreaterThanOrEqual(4);
|
|
129
|
+
|
|
130
|
+
// Intervals should be non-decreasing (backoff)
|
|
131
|
+
for (let i = 1; i < timeoutDelays.length; i++) {
|
|
132
|
+
expect(timeoutDelays[i]).toBeGreaterThanOrEqual(timeoutDelays[i - 1]!);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// All intervals within bounds
|
|
136
|
+
for (const delay of timeoutDelays) {
|
|
137
|
+
expect(delay).toBeGreaterThanOrEqual(POLL_INTERVAL_MIN_MS);
|
|
138
|
+
expect(delay).toBeLessThanOrEqual(POLL_INTERVAL_MAX_MS);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Should eventually reach the cap
|
|
142
|
+
expect(timeoutDelays[timeoutDelays.length - 1]).toBe(
|
|
143
|
+
POLL_INTERVAL_MAX_MS,
|
|
144
|
+
);
|
|
145
|
+
} finally {
|
|
146
|
+
globalThis.setTimeout = originalSetTimeout;
|
|
147
|
+
globalThis.clearTimeout = originalClearTimeout;
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
});
|
|
@@ -540,23 +540,13 @@ describe("Memory regressions", () => {
|
|
|
540
540
|
|
|
541
541
|
test("memory_save sets verificationState to user_confirmed", async () => {
|
|
542
542
|
const { handleMemorySave } = await import("../tools/memory/handlers.js");
|
|
543
|
-
const legacyConfig = {
|
|
544
|
-
...DEFAULT_CONFIG,
|
|
545
|
-
memory: {
|
|
546
|
-
...DEFAULT_CONFIG.memory,
|
|
547
|
-
simplified: {
|
|
548
|
-
...DEFAULT_CONFIG.memory.simplified,
|
|
549
|
-
enabled: false,
|
|
550
|
-
},
|
|
551
|
-
},
|
|
552
|
-
};
|
|
553
543
|
|
|
554
544
|
const result = await handleMemorySave(
|
|
555
545
|
{
|
|
556
546
|
statement: "User explicitly saved this preference",
|
|
557
547
|
kind: "preference",
|
|
558
548
|
},
|
|
559
|
-
|
|
549
|
+
DEFAULT_CONFIG,
|
|
560
550
|
"conv-verify-save",
|
|
561
551
|
"msg-verify-save",
|
|
562
552
|
);
|
|
@@ -573,23 +563,13 @@ describe("Memory regressions", () => {
|
|
|
573
563
|
|
|
574
564
|
test("memory_save in different scopes creates separate items", async () => {
|
|
575
565
|
const { handleMemorySave } = await import("../tools/memory/handlers.js");
|
|
576
|
-
const legacyConfig = {
|
|
577
|
-
...DEFAULT_CONFIG,
|
|
578
|
-
memory: {
|
|
579
|
-
...DEFAULT_CONFIG.memory,
|
|
580
|
-
simplified: {
|
|
581
|
-
...DEFAULT_CONFIG.memory.simplified,
|
|
582
|
-
enabled: false,
|
|
583
|
-
},
|
|
584
|
-
},
|
|
585
|
-
};
|
|
586
566
|
|
|
587
567
|
const sharedArgs = { statement: "I prefer dark mode", kind: "preference" };
|
|
588
568
|
|
|
589
569
|
// Save in the default scope
|
|
590
570
|
const r1 = await handleMemorySave(
|
|
591
571
|
sharedArgs,
|
|
592
|
-
|
|
572
|
+
DEFAULT_CONFIG,
|
|
593
573
|
"conv-scope-1",
|
|
594
574
|
"msg-scope-1",
|
|
595
575
|
"default",
|
|
@@ -600,7 +580,7 @@ describe("Memory regressions", () => {
|
|
|
600
580
|
// Save the identical statement in a private scope
|
|
601
581
|
const r2 = await handleMemorySave(
|
|
602
582
|
sharedArgs,
|
|
603
|
-
|
|
583
|
+
DEFAULT_CONFIG,
|
|
604
584
|
"conv-scope-2",
|
|
605
585
|
"msg-scope-2",
|
|
606
586
|
"private-abc",
|
|
@@ -624,7 +604,7 @@ describe("Memory regressions", () => {
|
|
|
624
604
|
// Saving the same statement again in default scope should dedup (not create a third)
|
|
625
605
|
const r3 = await handleMemorySave(
|
|
626
606
|
sharedArgs,
|
|
627
|
-
|
|
607
|
+
DEFAULT_CONFIG,
|
|
628
608
|
"conv-scope-3",
|
|
629
609
|
"msg-scope-3",
|
|
630
610
|
"default",
|
|
@@ -3227,9 +3207,8 @@ describe("Memory regressions", () => {
|
|
|
3227
3207
|
.filter((j) => JSON.parse(j.payload).messageId === "msg-untrusted-gate");
|
|
3228
3208
|
expect(extractJobs.length).toBe(0);
|
|
3229
3209
|
|
|
3230
|
-
// enqueuedJobs
|
|
3231
|
-
|
|
3232
|
-
const expectedJobs = result.indexedSegments * 2 + 1;
|
|
3210
|
+
// enqueuedJobs should reflect: embed jobs + summary (1), no extract (0)
|
|
3211
|
+
const expectedJobs = result.indexedSegments + 1; // embed per segment + summary
|
|
3233
3212
|
expect(result.enqueuedJobs).toBe(expectedJobs);
|
|
3234
3213
|
});
|
|
3235
3214
|
|
|
@@ -3410,9 +3389,8 @@ describe("Memory regressions", () => {
|
|
|
3410
3389
|
.filter((j) => JSON.parse(j.payload).messageId === "msg-unverified-gate");
|
|
3411
3390
|
expect(extractJobs.length).toBe(0);
|
|
3412
3391
|
|
|
3413
|
-
// enqueuedJobs
|
|
3414
|
-
|
|
3415
|
-
const expectedJobs = result.indexedSegments * 2 + 1;
|
|
3392
|
+
// enqueuedJobs should reflect: embed jobs + summary (1), no extract (0)
|
|
3393
|
+
const expectedJobs = result.indexedSegments + 1; // embed per segment + summary
|
|
3416
3394
|
expect(result.enqueuedJobs).toBe(expectedJobs);
|
|
3417
3395
|
});
|
|
3418
3396
|
|
|
@@ -527,9 +527,9 @@ describe("integration: existing routes unaffected", () => {
|
|
|
527
527
|
});
|
|
528
528
|
|
|
529
529
|
test("GET /v1/health still works (not intercepted by migration routes)", async () => {
|
|
530
|
-
const {
|
|
530
|
+
const { handleDetailedHealth } =
|
|
531
531
|
await import("../runtime/routes/identity-routes.js");
|
|
532
|
-
const res =
|
|
532
|
+
const res = handleDetailedHealth();
|
|
533
533
|
const body = (await res.json()) as Record<string, unknown>;
|
|
534
534
|
|
|
535
535
|
expect(res.status).toBe(200);
|
|
@@ -939,9 +939,9 @@ describe("route policy registration", () => {
|
|
|
939
939
|
|
|
940
940
|
describe("integration: existing routes unaffected", () => {
|
|
941
941
|
test("GET /v1/health still works", async () => {
|
|
942
|
-
const {
|
|
942
|
+
const { handleDetailedHealth } =
|
|
943
943
|
await import("../runtime/routes/identity-routes.js");
|
|
944
|
-
const res =
|
|
944
|
+
const res = handleDetailedHealth();
|
|
945
945
|
const body = (await res.json()) as Record<string, unknown>;
|
|
946
946
|
|
|
947
947
|
expect(res.status).toBe(200);
|
|
@@ -792,9 +792,9 @@ describe("route policy registration", () => {
|
|
|
792
792
|
|
|
793
793
|
describe("integration: existing routes unaffected", () => {
|
|
794
794
|
test("GET /v1/health still works", async () => {
|
|
795
|
-
const {
|
|
795
|
+
const { handleDetailedHealth } =
|
|
796
796
|
await import("../runtime/routes/identity-routes.js");
|
|
797
|
-
const res =
|
|
797
|
+
const res = handleDetailedHealth();
|
|
798
798
|
const body = (await res.json()) as Record<string, unknown>;
|
|
799
799
|
|
|
800
800
|
expect(res.status).toBe(200);
|
|
@@ -684,9 +684,9 @@ describe("route policy registration", () => {
|
|
|
684
684
|
|
|
685
685
|
describe("integration: existing routes unaffected", () => {
|
|
686
686
|
test("GET /v1/health still works (not intercepted by migration routes)", async () => {
|
|
687
|
-
const {
|
|
687
|
+
const { handleDetailedHealth } =
|
|
688
688
|
await import("../runtime/routes/identity-routes.js");
|
|
689
|
-
const res =
|
|
689
|
+
const res = handleDetailedHealth();
|
|
690
690
|
const body = (await res.json()) as Record<string, unknown>;
|
|
691
691
|
|
|
692
692
|
expect(res.status).toBe(200);
|
|
@@ -37,11 +37,6 @@ mock.module("../util/logger.js", () => ({
|
|
|
37
37
|
}),
|
|
38
38
|
}));
|
|
39
39
|
|
|
40
|
-
// Mock security check to always pass
|
|
41
|
-
mock.module("../security/secret-ingress.js", () => ({
|
|
42
|
-
checkIngressForSecrets: () => ({ blocked: false }),
|
|
43
|
-
}));
|
|
44
|
-
|
|
45
40
|
mock.module("../config/env.js", () => ({
|
|
46
41
|
isHttpAuthDisabled: () => true,
|
|
47
42
|
getGatewayInternalBaseUrl: () => "http://127.0.0.1:7830",
|
|
@@ -34,6 +34,10 @@ mock.module("../notifications/conversation-candidates.js", () => ({
|
|
|
34
34
|
serializeCandidatesForPrompt: () => undefined,
|
|
35
35
|
}));
|
|
36
36
|
|
|
37
|
+
mock.module("../prompts/persona-resolver.js", () => ({
|
|
38
|
+
resolveGuardianPersona: () => null,
|
|
39
|
+
}));
|
|
40
|
+
|
|
37
41
|
let configuredProvider: { sendMessage: () => Promise<unknown> } | null = null;
|
|
38
42
|
let extractedToolUse: unknown = null;
|
|
39
43
|
|
|
@@ -36,6 +36,10 @@ mock.module("../notifications/conversation-candidates.js", () => ({
|
|
|
36
36
|
serializeCandidatesForPrompt: () => undefined,
|
|
37
37
|
}));
|
|
38
38
|
|
|
39
|
+
mock.module("../prompts/persona-resolver.js", () => ({
|
|
40
|
+
resolveGuardianPersona: () => null,
|
|
41
|
+
}));
|
|
42
|
+
|
|
39
43
|
// ── Identity context mock ─────────────────────────────────────────────
|
|
40
44
|
|
|
41
45
|
let mockIdentityContext: string | null = null;
|
|
@@ -193,13 +193,12 @@ describe("managed proxy integration — credential precedence", () => {
|
|
|
193
193
|
|
|
194
194
|
const provider = getProvider("anthropic");
|
|
195
195
|
|
|
196
|
-
// Unwrap RetryProvider →
|
|
197
|
-
//
|
|
198
|
-
// and AnthropicProvider stores the SDK client as
|
|
196
|
+
// Unwrap RetryProvider → AnthropicProvider to inspect the Anthropic
|
|
197
|
+
// SDK client's baseURL. RetryProvider stores the inner provider as
|
|
198
|
+
// private `inner` and AnthropicProvider stores the SDK client as
|
|
199
|
+
// private `client`.
|
|
199
200
|
const retryInner = (provider as any).inner;
|
|
200
|
-
|
|
201
|
-
const logfireInner = (retryInner as any).inner ?? retryInner;
|
|
202
|
-
const anthropicClient = (logfireInner as any).client;
|
|
201
|
+
const anthropicClient = (retryInner as any).client;
|
|
203
202
|
|
|
204
203
|
expect(anthropicClient).toBeDefined();
|
|
205
204
|
const baseURL: string = anthropicClient.baseURL;
|
|
@@ -312,9 +312,10 @@ describe("QdrantManager", () => {
|
|
|
312
312
|
expect(existsSync(pidPath)).toBe(false);
|
|
313
313
|
}, 10_000);
|
|
314
314
|
|
|
315
|
-
test("
|
|
315
|
+
test("fails fast with exit code when process exits immediately", async () => {
|
|
316
316
|
const pidPath = join(testDataDir, "qdrant", "qdrant.pid");
|
|
317
317
|
|
|
318
|
+
// GIVEN a Qdrant binary that exits immediately with code 1
|
|
318
319
|
placeFakeBinary("#!/bin/sh\nexit 1");
|
|
319
320
|
|
|
320
321
|
const port = getTestPort();
|
|
@@ -323,9 +324,34 @@ describe("QdrantManager", () => {
|
|
|
323
324
|
...FAST_TIMEOUTS,
|
|
324
325
|
});
|
|
325
326
|
|
|
326
|
-
|
|
327
|
+
// WHEN we start the manager
|
|
328
|
+
const startTime = Date.now();
|
|
329
|
+
await expect(mgr.start()).rejects.toThrow(
|
|
330
|
+
/exited with code \d+ before becoming ready/,
|
|
331
|
+
);
|
|
332
|
+
const elapsed = Date.now() - startTime;
|
|
333
|
+
|
|
334
|
+
// THEN it fails fast (well under the 100ms readyz timeout)
|
|
335
|
+
expect(elapsed).toBeLessThan(FAST_TIMEOUTS.readyzTimeoutMs);
|
|
336
|
+
|
|
337
|
+
// AND the PID file is cleaned up
|
|
327
338
|
expect(existsSync(pidPath)).toBe(false);
|
|
328
339
|
}, 10_000);
|
|
340
|
+
|
|
341
|
+
test("includes stderr in error when process crashes", async () => {
|
|
342
|
+
// GIVEN a Qdrant binary that writes to stderr before crashing
|
|
343
|
+
placeFakeBinary('#!/bin/sh\necho "fatal: storage corrupted" >&2\nexit 1');
|
|
344
|
+
|
|
345
|
+
const port = getTestPort();
|
|
346
|
+
const mgr = new QdrantManager({
|
|
347
|
+
url: `http://127.0.0.1:${port}`,
|
|
348
|
+
...FAST_TIMEOUTS,
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
// WHEN we start the manager
|
|
352
|
+
// THEN the error includes the stderr output
|
|
353
|
+
await expect(mgr.start()).rejects.toThrow("storage corrupted");
|
|
354
|
+
}, 10_000);
|
|
329
355
|
});
|
|
330
356
|
|
|
331
357
|
// ── Binary Detection ─────────────────────────────────────────
|