@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
|
@@ -337,10 +337,7 @@ describe("Memory Retriever Pipeline", () => {
|
|
|
337
337
|
expect(result.tier1Count).toBeDefined();
|
|
338
338
|
expect(result.tier2Count).toBeDefined();
|
|
339
339
|
expect(result.hybridSearchMs).toBeDefined();
|
|
340
|
-
//
|
|
341
|
-
expect(result.recencyHits).toBeGreaterThan(0);
|
|
342
|
-
// …but they are filtered out because they belong to the active
|
|
343
|
-
// conversation and are already present in the conversation history.
|
|
340
|
+
// Without semantic search, no candidates are found.
|
|
344
341
|
expect(result.mergedCount).toBe(0);
|
|
345
342
|
});
|
|
346
343
|
|
|
@@ -403,11 +400,7 @@ describe("Memory Retriever Pipeline", () => {
|
|
|
403
400
|
);
|
|
404
401
|
|
|
405
402
|
expect(result.enabled).toBe(true);
|
|
406
|
-
//
|
|
407
|
-
expect(result.recencyHits).toBeGreaterThan(0);
|
|
408
|
-
// But they are filtered out of merged results; only other-conversation
|
|
409
|
-
// segments would survive (none in this case since recency is scoped to
|
|
410
|
-
// the active conversation).
|
|
403
|
+
// Without semantic search, no candidates are found.
|
|
411
404
|
expect(result.mergedCount).toBe(0);
|
|
412
405
|
});
|
|
413
406
|
|
|
@@ -506,10 +499,6 @@ describe("Memory Retriever Pipeline", () => {
|
|
|
506
499
|
);
|
|
507
500
|
|
|
508
501
|
expect(result.enabled).toBe(true);
|
|
509
|
-
// Recency search finds segments from this conversation
|
|
510
|
-
expect(result.recencyHits).toBeGreaterThan(0);
|
|
511
|
-
// Compacted segments survive filtering — they are no longer in context
|
|
512
|
-
expect(result.mergedCount).toBeGreaterThan(0);
|
|
513
502
|
});
|
|
514
503
|
|
|
515
504
|
// -----------------------------------------------------------------------
|
|
@@ -716,9 +705,7 @@ describe("Memory Retriever Pipeline", () => {
|
|
|
716
705
|
expect(result.enabled).toBe(true);
|
|
717
706
|
// Semantic/hybrid search should be skipped
|
|
718
707
|
expect(result.semanticHits).toBe(0);
|
|
719
|
-
//
|
|
720
|
-
expect(result.recencyHits).toBeGreaterThan(0);
|
|
721
|
-
// …but current-conversation segments are filtered out
|
|
708
|
+
// Without semantic search, no candidates are found.
|
|
722
709
|
expect(result.mergedCount).toBe(0);
|
|
723
710
|
});
|
|
724
711
|
|
|
@@ -752,7 +739,7 @@ describe("Memory Retriever Pipeline", () => {
|
|
|
752
739
|
expect(result.degradation).toBeDefined();
|
|
753
740
|
expect(result.degradation!.semanticUnavailable).toBe(true);
|
|
754
741
|
expect(result.degradation!.reason).toBe("embedding_provider_down");
|
|
755
|
-
expect(result.degradation!.fallbackSources).
|
|
742
|
+
expect(result.degradation!.fallbackSources).toEqual([]);
|
|
756
743
|
});
|
|
757
744
|
|
|
758
745
|
// -----------------------------------------------------------------------
|
|
@@ -864,9 +851,7 @@ describe("Memory Retriever Pipeline", () => {
|
|
|
864
851
|
// pipeline proceeds non-degraded end-to-end.
|
|
865
852
|
expect(result.enabled).toBe(true);
|
|
866
853
|
expect(result.degraded).toBe(false);
|
|
867
|
-
//
|
|
868
|
-
expect(result.recencyHits).toBeGreaterThan(0);
|
|
869
|
-
// Current-conversation segments are filtered out of merged results
|
|
854
|
+
// Without semantic search, no candidates are found.
|
|
870
855
|
expect(result.mergedCount).toBe(0);
|
|
871
856
|
});
|
|
872
857
|
|
|
@@ -1142,8 +1127,7 @@ describe("Memory Retriever Pipeline", () => {
|
|
|
1142
1127
|
);
|
|
1143
1128
|
|
|
1144
1129
|
// Simulate Qdrant returning the parent-conversation segment as a
|
|
1145
|
-
// semantic hit so it enters the candidate map
|
|
1146
|
-
// to forkConv and would never find it).
|
|
1130
|
+
// semantic hit so it enters the candidate map.
|
|
1147
1131
|
mockQdrantResults.push({
|
|
1148
1132
|
id: "qdrant-fork-1",
|
|
1149
1133
|
score: 0.9,
|
|
@@ -1264,6 +1248,35 @@ describe("Memory Retriever Pipeline", () => {
|
|
|
1264
1248
|
now - 50_000,
|
|
1265
1249
|
);
|
|
1266
1250
|
|
|
1251
|
+
// Simulate Qdrant returning both segments as semantic hits so they
|
|
1252
|
+
// enter the candidate map (recency search was removed).
|
|
1253
|
+
mockQdrantResults.push(
|
|
1254
|
+
{
|
|
1255
|
+
id: "qdrant-compact-fork-1",
|
|
1256
|
+
score: 0.9,
|
|
1257
|
+
payload: {
|
|
1258
|
+
target_type: "segment",
|
|
1259
|
+
target_id: "seg-compact-fork",
|
|
1260
|
+
text: "compacted parent topic detail",
|
|
1261
|
+
created_at: now - 100_000,
|
|
1262
|
+
message_id: "fork-compact-msg-1",
|
|
1263
|
+
conversation_id: forkConv,
|
|
1264
|
+
},
|
|
1265
|
+
},
|
|
1266
|
+
{
|
|
1267
|
+
id: "qdrant-compact-fork-2",
|
|
1268
|
+
score: 0.85,
|
|
1269
|
+
payload: {
|
|
1270
|
+
target_type: "segment",
|
|
1271
|
+
target_id: "seg-in-context-fork",
|
|
1272
|
+
text: "recent fork topic detail",
|
|
1273
|
+
created_at: now - 50_000,
|
|
1274
|
+
message_id: "fork-compact-msg-3",
|
|
1275
|
+
conversation_id: forkConv,
|
|
1276
|
+
},
|
|
1277
|
+
},
|
|
1278
|
+
);
|
|
1279
|
+
|
|
1267
1280
|
const result = await buildMemoryRecall(
|
|
1268
1281
|
"compacted parent topic",
|
|
1269
1282
|
forkConv,
|
|
@@ -1273,7 +1286,7 @@ describe("Memory Retriever Pipeline", () => {
|
|
|
1273
1286
|
expect(result.enabled).toBe(true);
|
|
1274
1287
|
// The segment from the compacted fork message survives filtering
|
|
1275
1288
|
// (its source message is no longer in context). The in-context segment
|
|
1276
|
-
// is filtered out.
|
|
1289
|
+
// is filtered out. Semantic search returns both, but only the compacted
|
|
1277
1290
|
// one survives step 5b.
|
|
1278
1291
|
expect(result.mergedCount).toBeGreaterThan(0);
|
|
1279
1292
|
});
|
|
@@ -1342,8 +1355,7 @@ describe("Memory Retriever Pipeline", () => {
|
|
|
1342
1355
|
);
|
|
1343
1356
|
|
|
1344
1357
|
// Simulate Qdrant returning the grandparent segment as a semantic hit
|
|
1345
|
-
// so it enters the candidate map
|
|
1346
|
-
// and would never find it).
|
|
1358
|
+
// so it enters the candidate map.
|
|
1347
1359
|
mockQdrantResults.push({
|
|
1348
1360
|
id: "qdrant-gp-1",
|
|
1349
1361
|
score: 0.9,
|
package/src/memory/retriever.ts
CHANGED
|
@@ -29,7 +29,6 @@ import {
|
|
|
29
29
|
IDENTITY_KINDS,
|
|
30
30
|
PREFERENCE_KINDS,
|
|
31
31
|
} from "./search/formatting.js";
|
|
32
|
-
import { recencySearch } from "./search/lexical.js";
|
|
33
32
|
import { isQdrantConnectionError, semanticSearch } from "./search/semantic.js";
|
|
34
33
|
import { applyStaleDemotion, computeStaleness } from "./search/staleness.js";
|
|
35
34
|
import {
|
|
@@ -139,7 +138,7 @@ function buildDegradationStatus(
|
|
|
139
138
|
return {
|
|
140
139
|
semanticUnavailable: true,
|
|
141
140
|
reason,
|
|
142
|
-
fallbackSources: [
|
|
141
|
+
fallbackSources: [],
|
|
143
142
|
};
|
|
144
143
|
}
|
|
145
144
|
|
|
@@ -243,13 +242,12 @@ async function generateQueryEmbedding(
|
|
|
243
242
|
* 1. Build query text (caller provides via buildMemoryQuery)
|
|
244
243
|
* 2. Generate dense + sparse embeddings
|
|
245
244
|
* 3. Hybrid search on Qdrant (dense + sparse RRF fusion)
|
|
246
|
-
* 4.
|
|
247
|
-
* 5.
|
|
248
|
-
* 6.
|
|
249
|
-
* 7.
|
|
250
|
-
* 8.
|
|
251
|
-
* 9.
|
|
252
|
-
* 10. Build two-layer XML injection with budget allocation
|
|
245
|
+
* 4. Deduplicate results
|
|
246
|
+
* 5. Classify tiers (score > 0.6 → tier 1, > 0.4 → tier 2)
|
|
247
|
+
* 6. Enrich item candidates with metadata for staleness
|
|
248
|
+
* 7. Compute staleness per item
|
|
249
|
+
* 8. Demote very_stale tier 1 → tier 2
|
|
250
|
+
* 9. Build two-layer XML injection with budget allocation
|
|
253
251
|
*/
|
|
254
252
|
export async function buildMemoryRecall(
|
|
255
253
|
query: string,
|
|
@@ -327,21 +325,15 @@ export async function buildMemoryRecall(
|
|
|
327
325
|
if (isQdrantConnectionError(err)) {
|
|
328
326
|
log.warn({ err }, "Qdrant unavailable — hybrid search disabled");
|
|
329
327
|
} else {
|
|
330
|
-
log.warn({ err }, "Hybrid search failed
|
|
328
|
+
log.warn({ err }, "Hybrid search failed");
|
|
331
329
|
}
|
|
332
330
|
}
|
|
333
331
|
}
|
|
334
332
|
const hybridSearchMs = Date.now() - hybridSearchStart;
|
|
335
333
|
|
|
336
|
-
// ── Step 4:
|
|
337
|
-
const recencyLimit = 5;
|
|
338
|
-
const recencyCandidates = conversationId
|
|
339
|
-
? recencySearch(conversationId, recencyLimit, excludeMessageIds, scopeIds)
|
|
340
|
-
: [];
|
|
341
|
-
|
|
342
|
-
// ── Step 5: Merge and deduplicate ──────────────────────────────
|
|
334
|
+
// ── Step 4: Deduplicate ────────────────────────────────────────
|
|
343
335
|
const candidateMap = new Map<string, Candidate>();
|
|
344
|
-
for (const c of [...hybridCandidates
|
|
336
|
+
for (const c of [...hybridCandidates]) {
|
|
345
337
|
const existing = candidateMap.get(c.key);
|
|
346
338
|
if (!existing) {
|
|
347
339
|
candidateMap.set(c.key, { ...c });
|
|
@@ -356,8 +348,7 @@ export async function buildMemoryRecall(
|
|
|
356
348
|
existing.text = c.text;
|
|
357
349
|
}
|
|
358
350
|
// Propagate metadata that the first source may lack (e.g. legacy
|
|
359
|
-
// Qdrant points missing conversation_id / message_id).
|
|
360
|
-
// source always has these from the DB, so merging fills the gap.
|
|
351
|
+
// Qdrant points missing conversation_id / message_id).
|
|
361
352
|
if (c.conversationId && !existing.conversationId) {
|
|
362
353
|
existing.conversationId = c.conversationId;
|
|
363
354
|
}
|
|
@@ -366,7 +357,7 @@ export async function buildMemoryRecall(
|
|
|
366
357
|
}
|
|
367
358
|
}
|
|
368
359
|
|
|
369
|
-
// ── Step
|
|
360
|
+
// ── Step 4b: Filter out current-conversation segments still in context ──
|
|
370
361
|
// Segments whose source message is still in the conversation's context
|
|
371
362
|
// window are redundant (already visible to the model). However, segments
|
|
372
363
|
// from messages that were removed by context compaction should be kept —
|
|
@@ -443,39 +434,26 @@ export async function buildMemoryRecall(
|
|
|
443
434
|
// Compute RRF-style final scores for the merged candidates
|
|
444
435
|
const allCandidates = [...candidateMap.values()];
|
|
445
436
|
for (const c of allCandidates) {
|
|
446
|
-
//
|
|
447
|
-
//
|
|
448
|
-
|
|
437
|
+
// Multiplicative scoring: importance, confidence, and recency amplify semantic
|
|
438
|
+
// relevance but can't substitute for it. An irrelevant item (semantic ≈ 0)
|
|
439
|
+
// stays low regardless of metadata. Multiplier range: 0.4 (all zero) to 1.0.
|
|
440
|
+
const metadataMultiplier =
|
|
441
|
+
0.4 + c.importance * 0.25 + c.confidence * 0.15 + c.recency * 0.2;
|
|
442
|
+
c.finalScore = c.semantic * metadataMultiplier;
|
|
449
443
|
}
|
|
450
444
|
allCandidates.sort((a, b) => b.finalScore - a.finalScore);
|
|
451
445
|
|
|
452
|
-
// ── Step
|
|
453
|
-
// Recency-only candidates (semantic=0) can never reach the tier 2 threshold
|
|
454
|
-
// (>0.6) since their max finalScore is 0.3. Promote them directly to tier 2
|
|
455
|
-
// so recent conversation context is preserved even without semantic signal.
|
|
456
|
-
const recencyOnlyKeys = new Set(
|
|
457
|
-
allCandidates
|
|
458
|
-
.filter((c) => c.semantic === 0 && c.recency > 0)
|
|
459
|
-
.map((c) => c.key),
|
|
460
|
-
);
|
|
446
|
+
// ── Step 5: Tier classification ─────────────────────────────────
|
|
461
447
|
const tiered = classifyTiers(allCandidates);
|
|
462
|
-
if (recencyOnlyKeys.size > 0) {
|
|
463
|
-
const alreadyTiered = new Set(tiered.map((c) => c.key));
|
|
464
|
-
for (const c of allCandidates) {
|
|
465
|
-
if (recencyOnlyKeys.has(c.key) && !alreadyTiered.has(c.key)) {
|
|
466
|
-
tiered.push({ ...c, tier: 2 });
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
448
|
|
|
471
|
-
// ── Step
|
|
449
|
+
// ── Step 5b: Enrich candidates with source labels ──────────────
|
|
472
450
|
enrichSourceLabels(tiered);
|
|
473
451
|
|
|
474
|
-
// ── Step
|
|
452
|
+
// ── Step 6: Enrich with item metadata for staleness ─────────────
|
|
475
453
|
const itemIds = tiered.filter((c) => c.type === "item").map((c) => c.id);
|
|
476
454
|
const itemMetadataMap = enrichItemMetadata(itemIds);
|
|
477
455
|
|
|
478
|
-
// ── Step
|
|
456
|
+
// ── Step 7: Compute staleness per item ──────────────────────────
|
|
479
457
|
const now = Date.now();
|
|
480
458
|
for (const c of tiered) {
|
|
481
459
|
if (c.type !== "item") continue;
|
|
@@ -492,10 +470,10 @@ export async function buildMemoryRecall(
|
|
|
492
470
|
c.staleness = level;
|
|
493
471
|
}
|
|
494
472
|
|
|
495
|
-
// ── Step
|
|
473
|
+
// ── Step 8: Demote very_stale tier 1 → tier 2 ──────────────────
|
|
496
474
|
const afterDemotion = applyStaleDemotion(tiered);
|
|
497
475
|
|
|
498
|
-
// ── Step
|
|
476
|
+
// ── Step 9: Budget allocation and two-layer injection ──────────
|
|
499
477
|
const maxInjectTokens = Math.max(
|
|
500
478
|
1,
|
|
501
479
|
Math.floor(
|
|
@@ -581,7 +559,6 @@ export async function buildMemoryRecall(
|
|
|
581
559
|
{
|
|
582
560
|
query: truncate(query, 120),
|
|
583
561
|
hybridHits: hybridCandidates.length,
|
|
584
|
-
recencyHits: recencyCandidates.length,
|
|
585
562
|
mergedCount: allCandidates.length,
|
|
586
563
|
tier1Count,
|
|
587
564
|
tier2Count,
|
|
@@ -602,7 +579,6 @@ export async function buildMemoryRecall(
|
|
|
602
579
|
provider: embeddingResult.provider,
|
|
603
580
|
model: embeddingResult.model,
|
|
604
581
|
semanticHits: hybridCandidates.length,
|
|
605
|
-
recencyHits: recencyCandidates.length,
|
|
606
582
|
mergedCount: allCandidates.length,
|
|
607
583
|
selectedCount,
|
|
608
584
|
injectedTokens: estimateTextTokens(injectedText),
|
|
@@ -887,7 +863,6 @@ function emptyResult(
|
|
|
887
863
|
provider: init.provider,
|
|
888
864
|
model: init.model,
|
|
889
865
|
semanticHits: 0,
|
|
890
|
-
recencyHits: 0,
|
|
891
866
|
mergedCount: 0,
|
|
892
867
|
selectedCount: 0,
|
|
893
868
|
injectedTokens: 0,
|
|
@@ -27,6 +27,7 @@ export const callSessions = sqliteTable(
|
|
|
27
27
|
inviteGuardianName: text("invite_guardian_name"),
|
|
28
28
|
callerIdentityMode: text("caller_identity_mode"),
|
|
29
29
|
callerIdentitySource: text("caller_identity_source"),
|
|
30
|
+
skipDisclosure: integer("skip_disclosure").notNull().default(0),
|
|
30
31
|
initiatedFromConversationId: text("initiated_from_conversation_id"),
|
|
31
32
|
startedAt: integer("started_at"),
|
|
32
33
|
endedAt: integer("ended_at"),
|
|
@@ -10,6 +10,7 @@ export const contacts = sqliteTable("contacts", {
|
|
|
10
10
|
updatedAt: integer("updated_at").notNull(),
|
|
11
11
|
role: text("role").notNull().default("contact"), // 'guardian' | 'contact'
|
|
12
12
|
principalId: text("principal_id"), // internal auth principal (nullable)
|
|
13
|
+
userFile: text("user_file"), // workspace-relative path to per-user persona file
|
|
13
14
|
contactType: text("contact_type").notNull().default("human"), // 'human' | 'assistant'
|
|
14
15
|
});
|
|
15
16
|
|
|
@@ -56,6 +56,8 @@ export const memoryItems = sqliteTable(
|
|
|
56
56
|
supersedes: text("supersedes"),
|
|
57
57
|
supersededBy: text("superseded_by"),
|
|
58
58
|
overrideConfidence: text("override_confidence").default("inferred"),
|
|
59
|
+
sourceType: text("source_type").notNull().default("extraction"),
|
|
60
|
+
sourceMessageRole: text("source_message_role"),
|
|
59
61
|
},
|
|
60
62
|
(table) => [
|
|
61
63
|
index("idx_memory_items_scope_id").on(table.scopeId),
|
|
@@ -80,15 +80,6 @@ export const PREFERENCE_KINDS = new Set(["preference", "constraint"]);
|
|
|
80
80
|
/** Kinds classified as capabilities for the <available_capabilities> section. */
|
|
81
81
|
export const CAPABILITY_KINDS = new Set(["capability"]);
|
|
82
82
|
|
|
83
|
-
/** Per-item token budget for tier 1 items. */
|
|
84
|
-
const TIER1_PER_ITEM_TOKENS = 150;
|
|
85
|
-
|
|
86
|
-
/** Per-item token budget for tier 2 items. */
|
|
87
|
-
const TIER2_PER_ITEM_TOKENS = 100;
|
|
88
|
-
|
|
89
|
-
/** Approximate chars-per-token for truncation (matches token-estimator). */
|
|
90
|
-
const CHARS_PER_TOKEN = 4;
|
|
91
|
-
|
|
92
83
|
/**
|
|
93
84
|
* Build a two-layer XML injection block from tiered candidates.
|
|
94
85
|
*
|
|
@@ -151,38 +142,21 @@ export function buildTwoLayerInjection(params: {
|
|
|
151
142
|
: Infinity;
|
|
152
143
|
|
|
153
144
|
// Render tier 1 items first (identity, relevant context, preferences)
|
|
154
|
-
const identityLines = renderPlainStatements(
|
|
155
|
-
identityItems,
|
|
156
|
-
TIER1_PER_ITEM_TOKENS,
|
|
157
|
-
remainingTokens,
|
|
158
|
-
);
|
|
145
|
+
const identityLines = renderPlainStatements(identityItems, remainingTokens);
|
|
159
146
|
remainingTokens -= estimateTextTokens(identityLines.join("\n"));
|
|
160
147
|
|
|
161
|
-
const relevantEpisodes = renderEpisodes(
|
|
162
|
-
tier1Candidates,
|
|
163
|
-
TIER1_PER_ITEM_TOKENS,
|
|
164
|
-
remainingTokens,
|
|
165
|
-
);
|
|
148
|
+
const relevantEpisodes = renderEpisodes(tier1Candidates, remainingTokens);
|
|
166
149
|
remainingTokens -= estimateTextTokens(relevantEpisodes.join("\n"));
|
|
167
150
|
|
|
168
|
-
const preferenceLines = renderPlainStatements(
|
|
169
|
-
preferences,
|
|
170
|
-
TIER1_PER_ITEM_TOKENS,
|
|
171
|
-
remainingTokens,
|
|
172
|
-
);
|
|
151
|
+
const preferenceLines = renderPlainStatements(preferences, remainingTokens);
|
|
173
152
|
remainingTokens -= estimateTextTokens(preferenceLines.join("\n"));
|
|
174
153
|
|
|
175
|
-
const capabilityLines = renderPlainStatements(
|
|
176
|
-
capabilities,
|
|
177
|
-
TIER1_PER_ITEM_TOKENS,
|
|
178
|
-
remainingTokens,
|
|
179
|
-
);
|
|
154
|
+
const capabilityLines = renderPlainStatements(capabilities, remainingTokens);
|
|
180
155
|
remainingTokens -= estimateTextTokens(capabilityLines.join("\n"));
|
|
181
156
|
|
|
182
157
|
// Tier 2 uses remaining budget
|
|
183
158
|
const possiblyRelevantEpisodes = renderEpisodesWithStaleness(
|
|
184
159
|
tier2Candidates,
|
|
185
|
-
TIER2_PER_ITEM_TOKENS,
|
|
186
160
|
remainingTokens,
|
|
187
161
|
);
|
|
188
162
|
|
|
@@ -229,15 +203,13 @@ export function buildTwoLayerInjection(params: {
|
|
|
229
203
|
*/
|
|
230
204
|
function renderPlainStatements(
|
|
231
205
|
items: TieredCandidate[],
|
|
232
|
-
perItemBudgetTokens: number,
|
|
233
206
|
remainingBudget: number,
|
|
234
207
|
): string[] {
|
|
235
208
|
const lines: string[] = [];
|
|
236
209
|
let used = 0;
|
|
237
210
|
for (const item of items) {
|
|
238
211
|
if (used >= remainingBudget) break;
|
|
239
|
-
const
|
|
240
|
-
const text = escapeXmlTags(truncate(item.text, maxChars));
|
|
212
|
+
const text = escapeXmlTags(item.text);
|
|
241
213
|
const tokens = estimateTextTokens(text);
|
|
242
214
|
if (used + tokens > remainingBudget) break;
|
|
243
215
|
lines.push(text);
|
|
@@ -251,15 +223,13 @@ function renderPlainStatements(
|
|
|
251
223
|
*/
|
|
252
224
|
function renderEpisodes(
|
|
253
225
|
items: TieredCandidate[],
|
|
254
|
-
perItemBudgetTokens: number,
|
|
255
226
|
remainingBudget: number,
|
|
256
227
|
): string[] {
|
|
257
228
|
const lines: string[] = [];
|
|
258
229
|
let used = 0;
|
|
259
230
|
for (const item of items) {
|
|
260
231
|
if (used >= remainingBudget) break;
|
|
261
|
-
const
|
|
262
|
-
const text = escapeXmlTags(truncate(item.text, maxChars));
|
|
232
|
+
const text = escapeXmlTags(item.text);
|
|
263
233
|
const sourceAttr = buildSourceAttr(item);
|
|
264
234
|
const line = `<episode${sourceAttr}>\n${text}\n</episode>`;
|
|
265
235
|
const tokens = estimateTextTokens(line);
|
|
@@ -275,15 +245,13 @@ function renderEpisodes(
|
|
|
275
245
|
*/
|
|
276
246
|
function renderEpisodesWithStaleness(
|
|
277
247
|
items: TieredCandidate[],
|
|
278
|
-
perItemBudgetTokens: number,
|
|
279
248
|
remainingBudget: number,
|
|
280
249
|
): string[] {
|
|
281
250
|
const lines: string[] = [];
|
|
282
251
|
let used = 0;
|
|
283
252
|
for (const item of items) {
|
|
284
253
|
if (used >= remainingBudget) break;
|
|
285
|
-
const
|
|
286
|
-
const text = escapeXmlTags(truncate(item.text, maxChars));
|
|
254
|
+
const text = escapeXmlTags(item.text);
|
|
287
255
|
const sourceAttr = buildSourceAttr(item);
|
|
288
256
|
const stalenessAttr =
|
|
289
257
|
item.staleness && item.staleness !== "fresh"
|
|
@@ -347,8 +315,3 @@ function formatShortDate(epochMs: number): string {
|
|
|
347
315
|
}
|
|
348
316
|
return `${month} ${day} ${date.getFullYear()}`;
|
|
349
317
|
}
|
|
350
|
-
|
|
351
|
-
function truncate(text: string, max: number): string {
|
|
352
|
-
if (text.length <= max) return text;
|
|
353
|
-
return `${text.slice(0, max - 3)}...`;
|
|
354
|
-
}
|
|
@@ -8,6 +8,10 @@ const BASE_LIFETIME_MS: Record<string, number> = {
|
|
|
8
8
|
project: 14 * 86_400_000, // 2 weeks
|
|
9
9
|
decision: 14 * 86_400_000, // 2 weeks
|
|
10
10
|
event: 3 * 86_400_000, // 3 days
|
|
11
|
+
// Journals are experiential reflections and forward-looking notes — more
|
|
12
|
+
// durable than ephemeral events or decisions, but not as permanent as
|
|
13
|
+
// identity. 90 days mirrors "preference" lifetime.
|
|
14
|
+
journal: 90 * 86_400_000, // 3 months
|
|
11
15
|
capability: Infinity,
|
|
12
16
|
};
|
|
13
17
|
|
|
@@ -8,9 +8,17 @@ export interface TieredCandidate extends Candidate {
|
|
|
8
8
|
sourceLabel?: string;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Map a composite relevance score to an injection tier.
|
|
13
|
+
*
|
|
14
|
+
* Thresholds are intentionally set lower than raw-embedding ceilings because
|
|
15
|
+
* the multiplicative scoring pipeline (semantic × recency × metadata) compresses
|
|
16
|
+
* the effective score range. Lowering the gates lets moderately-relevant items
|
|
17
|
+
* surface rather than being silently dropped.
|
|
18
|
+
*/
|
|
11
19
|
export function classifyTier(score: number): Tier | null {
|
|
12
|
-
if (score > 0.
|
|
13
|
-
if (score > 0.
|
|
20
|
+
if (score > 0.6) return 1;
|
|
21
|
+
if (score > 0.4) return 2;
|
|
14
22
|
return null;
|
|
15
23
|
}
|
|
16
24
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type CandidateType = "segment" | "item" | "summary" | "media";
|
|
2
|
-
export type CandidateSource = "semantic"
|
|
2
|
+
export type CandidateSource = "semantic";
|
|
3
3
|
|
|
4
4
|
export type StalenessLevel = "fresh" | "aging" | "stale" | "very_stale";
|
|
5
5
|
|
|
@@ -39,12 +39,10 @@ export type DegradationReason =
|
|
|
39
39
|
| "qdrant_unavailable"
|
|
40
40
|
| "embedding_generation_failed";
|
|
41
41
|
|
|
42
|
-
export type FallbackSource = "recency";
|
|
43
|
-
|
|
44
42
|
export interface DegradationStatus {
|
|
45
43
|
semanticUnavailable: boolean;
|
|
46
44
|
reason: DegradationReason;
|
|
47
|
-
fallbackSources:
|
|
45
|
+
fallbackSources: string[];
|
|
48
46
|
}
|
|
49
47
|
|
|
50
48
|
export interface MemoryRecallResult {
|
|
@@ -55,7 +53,6 @@ export interface MemoryRecallResult {
|
|
|
55
53
|
provider?: string;
|
|
56
54
|
model?: string;
|
|
57
55
|
semanticHits: number;
|
|
58
|
-
recencyHits: number;
|
|
59
56
|
mergedCount: number;
|
|
60
57
|
selectedCount: number;
|
|
61
58
|
injectedTokens: number;
|
|
@@ -24,14 +24,14 @@ export function isConversationFailed(conversationId: string): boolean {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
|
-
* Invalidate
|
|
27
|
+
* Invalidate assistant-extracted memory items sourced *exclusively* from
|
|
28
28
|
* messages in the given conversation. Called when a background task or
|
|
29
29
|
* schedule fails — the assistant's optimistic claims (e.g., "I booked an
|
|
30
30
|
* appointment") are not trustworthy if the task didn't complete.
|
|
31
31
|
*
|
|
32
32
|
* The failed state is derived from durable storage (task_runs / cron_runs),
|
|
33
33
|
* so any pending or future extraction jobs for this conversation are blocked
|
|
34
|
-
* from creating new
|
|
34
|
+
* from creating new assistant-extracted items — even after daemon restarts.
|
|
35
35
|
*
|
|
36
36
|
* Items that also have sources from other conversations are left alone
|
|
37
37
|
* only when those conversations come from non-failed task/schedule runs
|
|
@@ -55,7 +55,8 @@ export function invalidateAssistantInferredItemsForConversation(
|
|
|
55
55
|
`UPDATE memory_items
|
|
56
56
|
SET status = 'invalidated',
|
|
57
57
|
invalid_at = ?
|
|
58
|
-
WHERE
|
|
58
|
+
WHERE source_type = 'extraction'
|
|
59
|
+
AND source_message_role = 'assistant'
|
|
59
60
|
AND status = 'active'
|
|
60
61
|
AND id IN (
|
|
61
62
|
SELECT mis.memory_item_id
|