@vellumai/assistant 0.5.6 → 0.5.8
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 +3 -2
- package/README.md +0 -2
- package/bun.lock +0 -414
- package/docker-entrypoint.sh +9 -0
- package/docs/architecture/keychain-broker.md +45 -240
- package/docs/architecture/memory.md +13 -11
- 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/error.ts +1 -1
- package/node_modules/@vellumai/ces-contracts/src/grants.ts +1 -1
- package/node_modules/@vellumai/ces-contracts/src/handles.ts +1 -1
- package/node_modules/@vellumai/ces-contracts/src/index.ts +1 -1
- package/node_modules/@vellumai/ces-contracts/src/rpc.ts +120 -1
- package/node_modules/@vellumai/credential-storage/package.json +1 -0
- package/node_modules/@vellumai/egress-proxy/package.json +1 -0
- package/package.json +2 -3
- package/src/__tests__/actor-token-service.test.ts +0 -114
- package/src/__tests__/approval-cascade.test.ts +0 -1
- package/src/__tests__/assistant-feature-flags-integration.test.ts +30 -29
- package/src/__tests__/browser-fill-credential.test.ts +1 -1
- package/src/__tests__/browser-skill-endstate.test.ts +6 -5
- package/src/__tests__/btw-routes.test.ts +0 -39
- package/src/__tests__/call-controller.test.ts +0 -1
- package/src/__tests__/call-domain.test.ts +0 -128
- package/src/__tests__/ces-rpc-credential-backend.test.ts +199 -0
- package/src/__tests__/ces-startup-timeout.test.ts +40 -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 -2
- package/src/__tests__/config-schema.test.ts +3 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +0 -1
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +0 -2
- package/src/__tests__/conversation-agent-loop.test.ts +2 -4
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -5
- package/src/__tests__/conversation-confirmation-signals.test.ts +0 -1
- package/src/__tests__/conversation-error.test.ts +15 -1
- package/src/__tests__/conversation-init.benchmark.test.ts +0 -2
- package/src/__tests__/conversation-messaging-secret-redirect.test.ts +1 -1
- package/src/__tests__/conversation-pre-run-repair.test.ts +0 -1
- package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -1
- package/src/__tests__/conversation-queue.test.ts +0 -1
- package/src/__tests__/conversation-skill-tools.test.ts +0 -54
- package/src/__tests__/conversation-slash-queue.test.ts +0 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +0 -1
- package/src/__tests__/conversation-title-service.test.ts +87 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +0 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -1
- package/src/__tests__/credential-execution-client.test.ts +5 -2
- package/src/__tests__/credential-execution-feature-gates.test.ts +59 -30
- package/src/__tests__/credential-execution-managed-contract.test.ts +35 -20
- package/src/__tests__/credential-security-e2e.test.ts +1 -67
- package/src/__tests__/credential-security-invariants.test.ts +6 -50
- package/src/__tests__/credentials-cli.test.ts +82 -3
- package/src/__tests__/daemon-credential-client.test.ts +123 -0
- package/src/__tests__/db-migration-rollback.test.ts +2015 -1
- package/src/__tests__/deterministic-verification-control-plane.test.ts +1 -0
- package/src/__tests__/docker-signing-key-bootstrap.test.ts +34 -143
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +6 -4
- package/src/__tests__/gateway-client-managed-outbound.test.ts +79 -1
- 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__/journal-context.test.ts +335 -0
- package/src/__tests__/keychain-broker-client.test.ts +161 -22
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -3
- package/src/__tests__/memory-jobs-worker-backoff.test.ts +150 -0
- package/src/__tests__/memory-lifecycle-e2e.test.ts +70 -25
- package/src/__tests__/memory-recall-quality.test.ts +48 -17
- package/src/__tests__/memory-regressions.test.ts +408 -363
- package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -3
- 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 +2 -7
- package/src/__tests__/notification-decision-fallback.test.ts +4 -0
- package/src/__tests__/notification-decision-identity.test.ts +4 -0
- package/src/__tests__/notification-decision-strategy.test.ts +71 -0
- package/src/__tests__/oauth-cli.test.ts +5 -1
- package/src/__tests__/permission-types.test.ts +1 -0
- package/src/__tests__/provider-commit-message-generator.test.ts +0 -37
- package/src/__tests__/provider-error-scenarios.test.ts +0 -267
- package/src/__tests__/provider-managed-proxy-integration.test.ts +5 -6
- package/src/__tests__/provider-streaming.benchmark.test.ts +2 -81
- package/src/__tests__/qdrant-manager.test.ts +28 -2
- package/src/__tests__/registry.test.ts +0 -6
- package/src/__tests__/relay-server.test.ts +1 -2
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -4
- package/src/__tests__/script-proxy-injection-runtime.test.ts +1 -1
- package/src/__tests__/secret-onetime-send.test.ts +1 -1
- package/src/__tests__/secret-routes-managed-proxy.test.ts +0 -4
- package/src/__tests__/secure-keys.test.ts +95 -272
- 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-memory.test.ts +17 -3
- 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__/stale-approval-dedup.test.ts +171 -0
- package/src/__tests__/stt-hints.test.ts +437 -0
- package/src/__tests__/suggestion-routes.test.ts +1 -32
- package/src/__tests__/system-prompt.test.ts +0 -1
- package/src/__tests__/task-memory-cleanup.test.ts +14 -0
- package/src/__tests__/tool-executor-shell-integration.test.ts +5 -3
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +0 -5
- package/src/__tests__/trusted-contact-multichannel.test.ts +0 -4
- package/src/__tests__/twilio-routes-twiml.test.ts +139 -1
- 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-quality.test.ts +58 -0
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -7
- 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 +220 -0
- package/src/__tests__/workspace-migration-down-functions.test.ts +1009 -0
- package/src/__tests__/workspace-migrations-runner.test.ts +114 -0
- package/src/acp/agent-process.ts +9 -1
- package/src/agent/loop.ts +1 -1
- package/src/approvals/guardian-request-resolvers.ts +164 -38
- package/src/calls/__tests__/tts-text-sanitizer.test.ts +254 -0
- package/src/calls/audio-store.test.ts +97 -0
- package/src/calls/audio-store.ts +205 -0
- package/src/calls/call-controller.ts +90 -8
- package/src/calls/call-domain.ts +3 -0
- package/src/calls/call-store.ts +10 -3
- package/src/calls/fish-audio-client.ts +129 -0
- package/src/calls/relay-server.ts +27 -0
- package/src/calls/stt-hints.ts +189 -0
- package/src/calls/tts-text-sanitizer.ts +61 -0
- package/src/calls/twilio-routes.ts +34 -5
- package/src/calls/types.ts +1 -0
- package/src/calls/voice-ingress-preflight.ts +0 -42
- package/src/calls/voice-quality.ts +38 -5
- package/src/calls/voice-session-bridge.ts +7 -12
- package/src/cli/commands/avatar.ts +2 -2
- package/src/cli/commands/config.ts +1 -4
- package/src/cli/commands/credentials.ts +128 -82
- package/src/cli/commands/doctor.ts +2 -2
- package/src/cli/commands/keys.ts +7 -7
- package/src/cli/commands/memory.ts +1 -1
- package/src/cli/commands/oauth/connections.ts +11 -29
- package/src/cli/commands/oauth/index.ts +7 -0
- package/src/cli/commands/oauth/platform.ts +525 -0
- package/src/cli/commands/platform.ts +3 -3
- package/src/cli/lib/daemon-credential-client.ts +284 -0
- package/src/cli.ts +1 -1
- package/src/config/assistant-feature-flags.ts +186 -5
- package/src/config/bundled-skills/AGENTS.md +34 -0
- package/src/config/bundled-skills/acp/SKILL.md +10 -0
- package/src/config/bundled-skills/app-builder/SKILL.md +0 -4
- package/src/config/bundled-skills/messaging/SKILL.md +5 -5
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +2 -2
- package/src/config/bundled-skills/phone-calls/TOOLS.json +4 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +1 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +1 -0
- package/src/config/bundled-skills/settings/SKILL.md +15 -2
- package/src/config/bundled-skills/settings/TOOLS.json +47 -2
- package/src/config/bundled-skills/settings/tools/avatar-remove.ts +59 -0
- package/src/config/bundled-skills/settings/tools/avatar-update.ts +80 -0
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +42 -0
- package/src/config/bundled-skills/slack/SKILL.md +1 -1
- package/src/config/bundled-tool-registry.ts +5 -11
- package/src/config/defaults.ts +0 -2
- package/src/config/env-registry.ts +5 -5
- package/src/config/env.ts +21 -14
- package/src/config/feature-flag-registry.json +49 -9
- package/src/config/loader.ts +106 -42
- package/src/config/schema.ts +9 -29
- package/src/config/schemas/calls.ts +30 -0
- package/src/config/schemas/fish-audio.ts +39 -0
- package/src/config/schemas/inference.ts +2 -2
- package/src/config/schemas/journal.ts +16 -0
- package/src/config/schemas/memory-processing.ts +2 -2
- package/src/config/schemas/security.ts +0 -4
- package/src/config/types.ts +1 -1
- package/src/contacts/contact-store.ts +39 -0
- package/src/contacts/types.ts +2 -0
- package/src/credential-execution/approval-bridge.ts +1 -0
- package/src/credential-execution/executable-discovery.ts +28 -4
- package/src/credential-execution/feature-gates.ts +16 -0
- package/src/credential-execution/process-manager.ts +38 -0
- package/src/credential-execution/startup-timeout.ts +36 -0
- package/src/daemon/approval-generators.ts +3 -9
- package/src/daemon/assistant-attachments.ts +9 -0
- package/src/daemon/config-watcher.ts +5 -0
- package/src/daemon/conversation-error.ts +13 -1
- package/src/daemon/conversation-memory.ts +1 -2
- package/src/daemon/conversation-process.ts +18 -1
- package/src/daemon/conversation-surfaces.ts +30 -1
- package/src/daemon/conversation-tool-setup.ts +0 -105
- package/src/daemon/conversation.ts +21 -1
- package/src/daemon/guardian-action-generators.ts +3 -9
- package/src/daemon/handlers/config-vercel.ts +92 -0
- package/src/daemon/handlers/skills.ts +2 -15
- package/src/daemon/install-symlink.ts +195 -0
- package/src/daemon/lifecycle.ts +234 -51
- package/src/daemon/message-types/conversations.ts +4 -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 +32 -95
- package/src/events/domain-events.ts +2 -1
- package/src/inbound/platform-callback-registration.ts +3 -3
- package/src/instrument.ts +8 -5
- package/src/memory/app-store.ts +31 -0
- package/src/memory/conversation-title-service.ts +50 -1
- package/src/memory/db-init.ts +16 -0
- package/src/memory/indexer.ts +19 -10
- package/src/memory/items-extractor.ts +328 -321
- package/src/memory/job-handlers/conversation-starters.ts +4 -1
- package/src/memory/job-handlers/summarization.ts +26 -16
- package/src/memory/jobs-store.ts +63 -6
- package/src/memory/jobs-worker.ts +31 -7
- package/src/memory/journal-memory.ts +214 -0
- package/src/memory/migrations/001-job-deferrals.ts +19 -0
- package/src/memory/migrations/004-entity-relation-dedup.ts +10 -0
- package/src/memory/migrations/005-fingerprint-scope-unique.ts +76 -0
- package/src/memory/migrations/006-scope-salted-fingerprints.ts +50 -0
- package/src/memory/migrations/007-assistant-id-to-self.ts +10 -0
- package/src/memory/migrations/008-remove-assistant-id-columns.ts +34 -0
- package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +26 -0
- package/src/memory/migrations/014-backfill-inbox-thread-state.ts +10 -0
- package/src/memory/migrations/015-drop-active-search-index.ts +17 -0
- package/src/memory/migrations/019-notification-tables-schema-migration.ts +12 -0
- package/src/memory/migrations/020-rename-macos-ios-channel-to-vellum.ts +121 -0
- package/src/memory/migrations/024-embedding-vector-blob.ts +74 -0
- package/src/memory/migrations/026a-embeddings-nullable-vector-json.ts +82 -0
- package/src/memory/migrations/036-normalize-phone-identities.ts +11 -0
- package/src/memory/migrations/116-messages-fts.ts +106 -1
- package/src/memory/migrations/126-backfill-guardian-principal-id.ts +52 -0
- package/src/memory/migrations/127-guardian-principal-id-not-null.ts +77 -0
- package/src/memory/migrations/134-contacts-notes-column.ts +13 -0
- package/src/memory/migrations/135-backfill-contact-interaction-stats.ts +20 -0
- package/src/memory/migrations/136-drop-assistant-id-columns.ts +52 -0
- package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +13 -0
- package/src/memory/migrations/141-rename-verification-table.ts +54 -0
- package/src/memory/migrations/142-rename-verification-session-id-column.ts +25 -0
- package/src/memory/migrations/143-rename-guardian-verification-values.ts +35 -0
- package/src/memory/migrations/144-rename-voice-to-phone.ts +136 -0
- package/src/memory/migrations/145-drop-accounts-table.ts +32 -0
- package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +14 -1
- package/src/memory/migrations/148-drop-reminders-table.ts +35 -1
- package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +69 -1
- package/src/memory/migrations/162-guardian-timestamps-epoch-ms.ts +290 -0
- package/src/memory/migrations/169-rename-gmail-provider-key-to-google.ts +51 -1
- package/src/memory/migrations/174-rename-thread-starters-table.ts +47 -1
- package/src/memory/migrations/176-drop-capability-card-state.ts +13 -0
- package/src/memory/migrations/180-backfill-inline-attachments-to-disk.ts +16 -0
- package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +28 -1
- package/src/memory/migrations/190-call-session-skip-disclosure.ts +15 -0
- package/src/memory/migrations/191-backfill-audio-attachment-mime-types.ts +64 -0
- package/src/memory/migrations/192-contacts-user-file-column.ts +15 -0
- package/src/memory/migrations/193-add-source-type-columns.ts +81 -0
- package/src/memory/migrations/index.ts +5 -0
- package/src/memory/migrations/registry.ts +98 -0
- package/src/memory/migrations/validate-migration-state.ts +137 -11
- package/src/memory/qdrant-circuit-breaker.ts +9 -0
- package/src/memory/qdrant-manager.ts +64 -7
- package/src/memory/retriever.test.ts +37 -25
- package/src/memory/retriever.ts +24 -49
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/contacts.ts +1 -0
- package/src/memory/schema/memory-core.ts +2 -0
- package/src/memory/search/formatting.ts +7 -44
- package/src/memory/search/staleness.ts +4 -0
- package/src/memory/search/tier-classifier.ts +10 -2
- package/src/memory/search/types.ts +2 -5
- package/src/memory/task-memory-cleanup.ts +4 -3
- package/src/notifications/adapters/slack.ts +168 -6
- package/src/notifications/broadcaster.ts +1 -0
- package/src/notifications/copy-composer.ts +59 -2
- package/src/notifications/decision-engine.ts +4 -1
- package/src/notifications/signal.ts +2 -0
- package/src/notifications/types.ts +2 -0
- package/src/oauth/connection-resolver.ts +6 -4
- package/src/permissions/checker.ts +0 -38
- package/src/permissions/shell-identity.ts +76 -22
- package/src/permissions/types.ts +4 -2
- package/src/platform/client.ts +35 -7
- package/src/prompts/journal-context.ts +133 -0
- package/src/prompts/persona-resolver.ts +194 -0
- package/src/prompts/system-prompt.ts +44 -4
- package/src/prompts/templates/SOUL.md +10 -0
- package/src/prompts/templates/users/default.md +1 -0
- package/src/providers/provider-send-message.ts +3 -32
- package/src/providers/registry.ts +29 -179
- package/src/providers/types.ts +1 -1
- package/src/runtime/access-request-helper.ts +4 -0
- 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/__tests__/guard-tests.test.ts +9 -50
- package/src/runtime/auth/external-assistant-id.ts +13 -59
- package/src/runtime/auth/route-policy.ts +17 -1
- package/src/runtime/auth/token-service.ts +43 -138
- package/src/runtime/channel-readiness-service.ts +1 -16
- package/src/runtime/gateway-client.ts +47 -4
- package/src/runtime/guardian-decision-types.ts +45 -4
- package/src/runtime/http-server.ts +31 -3
- package/src/runtime/middleware/error-handler.ts +1 -9
- package/src/runtime/routes/access-request-decision.ts +2 -2
- package/src/runtime/routes/app-management-routes.ts +2 -1
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +219 -30
- package/src/runtime/routes/approval-strategies/guardian-text-engine-strategy.ts +37 -14
- package/src/runtime/routes/audio-routes.ts +40 -0
- package/src/runtime/routes/btw-routes.ts +0 -17
- package/src/runtime/routes/channel-readiness-routes.ts +9 -4
- package/src/runtime/routes/conversation-query-routes.ts +63 -1
- package/src/runtime/routes/conversation-routes.ts +4 -44
- package/src/runtime/routes/debug-routes.ts +12 -9
- package/src/runtime/routes/diagnostics-routes.ts +1 -477
- package/src/runtime/routes/guardian-approval-interception.ts +168 -11
- package/src/runtime/routes/guardian-approval-prompt.ts +6 -1
- package/src/runtime/routes/guardian-approval-reply-helpers.ts +103 -21
- package/src/runtime/routes/identity-routes.ts +19 -30
- package/src/runtime/routes/inbound-message-handler.ts +31 -1
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +64 -5
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +52 -40
- 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/twilio.ts +52 -10
- 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 +3 -3
- package/src/runtime/routes/memory-item-routes.ts +46 -14
- 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/secret-routes.ts +141 -10
- package/src/runtime/routes/settings-routes.ts +41 -1
- package/src/runtime/routes/tts-routes.ts +96 -0
- package/src/runtime/routes/upgrade-broadcast-routes.ts +26 -2
- package/src/runtime/routes/workspace-commit-routes.ts +62 -0
- package/src/runtime/routes/workspace-routes.test.ts +22 -1
- package/src/runtime/routes/workspace-routes.ts +1 -1
- package/src/runtime/routes/workspace-utils.ts +86 -2
- package/src/security/ces-credential-client.ts +75 -29
- package/src/security/ces-rpc-credential-backend.ts +86 -0
- package/src/security/credential-backend.ts +22 -92
- package/src/security/keychain-broker-client.ts +10 -2
- package/src/security/secure-keys.ts +113 -115
- package/src/skills/catalog-install.ts +6 -32
- package/src/skills/skill-memory.ts +1 -0
- package/src/subagent/manager.ts +2 -5
- package/src/telemetry/usage-telemetry-reporter.ts +4 -2
- package/src/tools/acp/spawn.ts +78 -1
- package/src/tools/calls/call-start.ts +1 -0
- package/src/tools/credentials/vault.ts +5 -3
- package/src/tools/executor.ts +0 -4
- package/src/tools/memory/definitions.ts +3 -2
- package/src/tools/memory/handlers.ts +10 -7
- package/src/tools/network/script-proxy/session-manager.ts +19 -4
- package/src/tools/network/web-fetch.ts +3 -1
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/terminal/safe-env.ts +1 -0
- package/src/tools/types.ts +0 -8
- package/src/util/browser.ts +15 -0
- package/src/util/errors.ts +0 -12
- package/src/util/platform.ts +4 -51
- package/src/workspace/git-service.ts +5 -2
- package/src/workspace/migrations/001-avatar-rename.ts +15 -0
- package/src/workspace/migrations/003-seed-device-id.ts +17 -1
- package/src/workspace/migrations/004-extract-collect-usage-data.ts +33 -0
- package/src/workspace/migrations/005-add-send-diagnostics.ts +3 -0
- package/src/workspace/migrations/006-services-config.ts +49 -0
- package/src/workspace/migrations/007-web-search-provider-rename.ts +27 -0
- package/src/workspace/migrations/008-voice-timeout-and-max-steps.ts +3 -0
- package/src/workspace/migrations/009-backfill-conversation-disk-view.ts +4 -0
- package/src/workspace/migrations/010-app-dir-rename.ts +78 -0
- package/src/workspace/migrations/011-backfill-installation-id.ts +11 -0
- package/src/workspace/migrations/012-rename-conversation-disk-view-dirs.ts +44 -0
- package/src/workspace/migrations/013-repair-conversation-disk-view.ts +5 -0
- package/src/workspace/migrations/015-migrate-credentials-to-keychain.ts +153 -0
- package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +156 -0
- package/src/workspace/migrations/016-migrate-credentials-from-keychain.ts +150 -0
- package/src/workspace/migrations/017-seed-persona-dirs.ts +96 -0
- package/src/workspace/migrations/018-rekey-compound-credential-keys.ts +184 -0
- package/src/workspace/migrations/019-scope-journal-to-guardian.ts +103 -0
- package/src/workspace/migrations/migrate-to-workspace-volume.ts +27 -5
- package/src/workspace/migrations/registry.ts +12 -0
- package/src/workspace/migrations/runner.ts +106 -2
- package/src/workspace/migrations/types.ts +4 -0
- package/src/workspace/provider-commit-message-generator.ts +12 -21
- package/src/__tests__/claude-code-skill-regression.test.ts +0 -206
- package/src/__tests__/claude-code-tool-profiles.test.ts +0 -99
- package/src/__tests__/diagnostics-export.test.ts +0 -288
- package/src/__tests__/local-gateway-health.test.ts +0 -209
- package/src/__tests__/provider-fail-open-selection.test.ts +0 -271
- package/src/__tests__/provider-failover-actual-provider.test.ts +0 -66
- package/src/__tests__/secret-ingress-handler.test.ts +0 -120
- package/src/__tests__/swarm-conversation-integration.test.ts +0 -358
- package/src/__tests__/swarm-dag-pathological.test.ts +0 -547
- package/src/__tests__/swarm-orchestrator.test.ts +0 -463
- package/src/__tests__/swarm-plan-validator.test.ts +0 -384
- package/src/__tests__/swarm-recursion.test.ts +0 -197
- package/src/__tests__/swarm-router-planner.test.ts +0 -234
- package/src/__tests__/swarm-tool.test.ts +0 -185
- package/src/__tests__/swarm-worker-backend.test.ts +0 -144
- package/src/__tests__/swarm-worker-runner.test.ts +0 -288
- package/src/commands/__tests__/cc-command-registry.test.ts +0 -396
- package/src/commands/cc-command-registry.ts +0 -248
- package/src/config/bundled-skills/claude-code/SKILL.md +0 -53
- package/src/config/bundled-skills/claude-code/TOOLS.json +0 -47
- package/src/config/bundled-skills/claude-code/tools/claude-code.ts +0 -12
- package/src/config/bundled-skills/orchestration/SKILL.md +0 -33
- package/src/config/bundled-skills/orchestration/TOOLS.json +0 -35
- package/src/config/bundled-skills/orchestration/tools/swarm-delegate.ts +0 -12
- package/src/config/schemas/swarm.ts +0 -82
- package/src/logfire.ts +0 -135
- package/src/memory/search/lexical.ts +0 -48
- package/src/providers/failover.ts +0 -186
- 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
|
});
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
* - compaction.summaryCalls: 2-6
|
|
6
6
|
* - compaction.estimatedInputTokens: < previousEstimatedInputTokens
|
|
7
7
|
* - recall.injectedTokens: <= computed dynamic budget
|
|
8
|
-
* - recall.recencyHits: > 0
|
|
9
8
|
* - recall.enabled: true
|
|
10
9
|
*/
|
|
11
10
|
import { mkdtempSync, rmSync } from "node:fs";
|
|
@@ -303,8 +302,6 @@ describe("Memory context benchmark", () => {
|
|
|
303
302
|
{ maxInjectTokensOverride: recallBudget },
|
|
304
303
|
);
|
|
305
304
|
|
|
306
|
-
// Recency search finds conversation-scoped segments.
|
|
307
|
-
expect(recall.recencyHits).toBeGreaterThan(0);
|
|
308
305
|
expect(recall.enabled).toBe(true);
|
|
309
306
|
// With Qdrant mock returning a high-scoring result, content should be injected.
|
|
310
307
|
expect(recall.selectedCount).toBeGreaterThan(0);
|
|
@@ -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
|
+
});
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Memory lifecycle E2E regression test.
|
|
3
3
|
*
|
|
4
4
|
* Verifies the new memory pipeline end-to-end:
|
|
5
|
-
* -
|
|
5
|
+
* - Standard-kind enum items (identity, preference, project, decision, constraint, event, journal, capability, ...)
|
|
6
6
|
* - Supersession chains (supersedes/supersededBy fields)
|
|
7
7
|
* - Hybrid search retrieval
|
|
8
8
|
* - Two-layer XML injection format (<memory_context> with sections)
|
|
@@ -44,10 +44,31 @@ mock.module("../util/logger.js", () => ({
|
|
|
44
44
|
}),
|
|
45
45
|
}));
|
|
46
46
|
|
|
47
|
+
// Stub the local embedding backend so the real ONNX model never loads
|
|
48
|
+
mock.module("../memory/embedding-local.js", () => ({
|
|
49
|
+
LocalEmbeddingBackend: class {
|
|
50
|
+
readonly provider = "local" as const;
|
|
51
|
+
readonly model: string;
|
|
52
|
+
constructor(model: string) {
|
|
53
|
+
this.model = model;
|
|
54
|
+
}
|
|
55
|
+
async embed(texts: string[]): Promise<number[][]> {
|
|
56
|
+
return texts.map(() => new Array(384).fill(0));
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
}));
|
|
60
|
+
|
|
61
|
+
// Dynamic Qdrant mock: tests can push results to be returned by searchWithFilter/hybridSearch
|
|
62
|
+
let mockQdrantResults: Array<{
|
|
63
|
+
id: string;
|
|
64
|
+
score: number;
|
|
65
|
+
payload: Record<string, unknown>;
|
|
66
|
+
}> = [];
|
|
67
|
+
|
|
47
68
|
mock.module("../memory/qdrant-client.js", () => ({
|
|
48
69
|
getQdrantClient: () => ({
|
|
49
|
-
searchWithFilter: async () =>
|
|
50
|
-
hybridSearch: async () =>
|
|
70
|
+
searchWithFilter: async () => mockQdrantResults,
|
|
71
|
+
hybridSearch: async () => mockQdrantResults,
|
|
51
72
|
upsertPoints: async () => {},
|
|
52
73
|
deletePoints: async () => {},
|
|
53
74
|
}),
|
|
@@ -60,7 +81,6 @@ const TEST_CONFIG = {
|
|
|
60
81
|
...DEFAULT_CONFIG.memory,
|
|
61
82
|
embeddings: {
|
|
62
83
|
...DEFAULT_CONFIG.memory.embeddings,
|
|
63
|
-
provider: "openai" as const,
|
|
64
84
|
required: false,
|
|
65
85
|
},
|
|
66
86
|
extraction: {
|
|
@@ -115,6 +135,7 @@ describe("Memory lifecycle E2E regression", () => {
|
|
|
115
135
|
db.run("DELETE FROM conversations");
|
|
116
136
|
db.run("DELETE FROM memory_jobs");
|
|
117
137
|
db.run("DELETE FROM memory_checkpoints");
|
|
138
|
+
mockQdrantResults = [];
|
|
118
139
|
resetCleanupScheduleThrottle();
|
|
119
140
|
resetStaleSweepThrottle();
|
|
120
141
|
});
|
|
@@ -128,7 +149,7 @@ describe("Memory lifecycle E2E regression", () => {
|
|
|
128
149
|
}
|
|
129
150
|
});
|
|
130
151
|
|
|
131
|
-
test("extraction produces items with
|
|
152
|
+
test("extraction produces items with standard-kind enum and supersession chains form correctly", async () => {
|
|
132
153
|
const db = getDb();
|
|
133
154
|
const now = 1_701_100_000_000;
|
|
134
155
|
const conversationId = "conv-memory-lifecycle";
|
|
@@ -165,7 +186,7 @@ describe("Memory lifecycle E2E regression", () => {
|
|
|
165
186
|
])
|
|
166
187
|
.run();
|
|
167
188
|
|
|
168
|
-
// Seed items using the
|
|
189
|
+
// Seed items using the standard-kind enum
|
|
169
190
|
const kinds = [
|
|
170
191
|
"identity",
|
|
171
192
|
"preference",
|
|
@@ -299,12 +320,8 @@ describe("Memory lifecycle E2E regression", () => {
|
|
|
299
320
|
TEST_CONFIG,
|
|
300
321
|
);
|
|
301
322
|
|
|
302
|
-
//
|
|
303
|
-
//
|
|
304
|
-
// (threshold > 0.6) because semantic=0 with Qdrant mocked empty.
|
|
305
|
-
// Verify recency search ran successfully.
|
|
306
|
-
expect(recall.recencyHits).toBeGreaterThan(0);
|
|
307
|
-
// Candidates exist but don't pass tier classification, so injectedText is empty
|
|
323
|
+
// Without semantic search (Qdrant mocked empty), no candidates pass
|
|
324
|
+
// tier classification (threshold > 0.6).
|
|
308
325
|
expect(recall.enabled).toBe(true);
|
|
309
326
|
});
|
|
310
327
|
|
|
@@ -343,15 +360,47 @@ describe("Memory lifecycle E2E regression", () => {
|
|
|
343
360
|
})
|
|
344
361
|
.run();
|
|
345
362
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
363
|
+
// Seed a memory item so the semantic search path can find it
|
|
364
|
+
db.insert(memoryItems)
|
|
365
|
+
.values({
|
|
366
|
+
id: "item-timezone-pref",
|
|
367
|
+
kind: "preference",
|
|
368
|
+
subject: "timezone preference",
|
|
369
|
+
statement: "My preferred timezone is America/Los_Angeles.",
|
|
370
|
+
status: "active",
|
|
371
|
+
confidence: 0.9,
|
|
372
|
+
importance: 0.8,
|
|
373
|
+
fingerprint: "fp-item-timezone-pref",
|
|
374
|
+
firstSeenAt: now + 10,
|
|
375
|
+
lastSeenAt: now + 10,
|
|
376
|
+
})
|
|
377
|
+
.run();
|
|
378
|
+
|
|
379
|
+
db.insert(memoryItemSources)
|
|
380
|
+
.values({
|
|
381
|
+
memoryItemId: "item-timezone-pref",
|
|
382
|
+
messageId: "msg-injection-seed",
|
|
383
|
+
evidence: "timezone preference evidence",
|
|
384
|
+
createdAt: now + 10,
|
|
385
|
+
})
|
|
386
|
+
.run();
|
|
387
|
+
|
|
388
|
+
// Mock Qdrant to return the timezone preference item
|
|
389
|
+
mockQdrantResults = [
|
|
390
|
+
{
|
|
391
|
+
id: "emb-timezone-pref",
|
|
392
|
+
score: 0.92,
|
|
393
|
+
payload: {
|
|
394
|
+
target_type: "item",
|
|
395
|
+
target_id: "item-timezone-pref",
|
|
396
|
+
text: "My preferred timezone is America/Los_Angeles.",
|
|
397
|
+
kind: "preference",
|
|
398
|
+
status: "active",
|
|
399
|
+
created_at: now + 10,
|
|
400
|
+
last_seen_at: now + 10,
|
|
401
|
+
},
|
|
402
|
+
},
|
|
403
|
+
];
|
|
355
404
|
|
|
356
405
|
const recall = await buildMemoryRecall(
|
|
357
406
|
"timezone",
|
|
@@ -359,10 +408,6 @@ describe("Memory lifecycle E2E regression", () => {
|
|
|
359
408
|
TEST_CONFIG,
|
|
360
409
|
);
|
|
361
410
|
|
|
362
|
-
// The recency-only promotion path (Step 6 in retriever) ensures the
|
|
363
|
-
// seeded segment reaches tier 2 and is injected even without semantic
|
|
364
|
-
// search. Verify structure of the two-layer XML format.
|
|
365
|
-
expect(recall.recencyHits).toBeGreaterThan(0);
|
|
366
411
|
expect(recall.enabled).toBe(true);
|
|
367
412
|
expect(recall.injectedText.length).toBeGreaterThan(0);
|
|
368
413
|
expect(recall.injectedTokens).toBeGreaterThan(0);
|