@vellumai/assistant 0.5.5 → 0.5.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +16 -2
- package/ARCHITECTURE.md +6 -75
- package/Dockerfile +4 -5
- package/README.md +0 -2
- package/bun.lock +0 -414
- package/docs/architecture/keychain-broker.md +45 -240
- package/docs/architecture/security.md +0 -17
- package/docs/credential-execution-service.md +2 -2
- package/node_modules/@vellumai/ces-contracts/package.json +1 -0
- package/node_modules/@vellumai/ces-contracts/src/rpc.ts +119 -0
- package/node_modules/@vellumai/credential-storage/package.json +1 -0
- package/node_modules/@vellumai/egress-proxy/package.json +1 -0
- package/package.json +2 -3
- package/src/__tests__/actor-token-service.test.ts +1 -2
- package/src/__tests__/assistant-feature-flags-integration.test.ts +30 -29
- package/src/__tests__/browser-skill-endstate.test.ts +6 -5
- package/src/__tests__/btw-routes.test.ts +0 -39
- package/src/__tests__/call-domain.test.ts +0 -128
- package/src/__tests__/ces-rpc-credential-backend.test.ts +199 -0
- package/src/__tests__/channel-approval-routes.test.ts +0 -5
- package/src/__tests__/channel-readiness-service.test.ts +1 -60
- package/src/__tests__/checker.test.ts +4 -2
- package/src/__tests__/cli-command-risk-guard.test.ts +112 -0
- package/src/__tests__/config-schema-cmd.test.ts +0 -1
- package/src/__tests__/config-schema.test.ts +3 -3
- package/src/__tests__/context-window-manager.test.ts +78 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -5
- package/src/__tests__/conversation-init.benchmark.test.ts +0 -2
- package/src/__tests__/conversation-skill-tools.test.ts +0 -54
- package/src/__tests__/conversation-title-service.test.ts +117 -1
- package/src/__tests__/credential-execution-feature-gates.test.ts +28 -14
- package/src/__tests__/credential-execution-managed-contract.test.ts +33 -18
- package/src/__tests__/credential-security-e2e.test.ts +0 -66
- package/src/__tests__/credential-security-invariants.test.ts +4 -45
- package/src/__tests__/credentials-cli.test.ts +78 -0
- package/src/__tests__/db-migration-rollback.test.ts +2015 -1
- package/src/__tests__/docker-signing-key-bootstrap.test.ts +98 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +6 -4
- package/src/__tests__/guardian-routing-state.test.ts +0 -5
- package/src/__tests__/host-shell-tool.test.ts +6 -7
- package/src/__tests__/http-user-message-parity.test.ts +3 -103
- package/src/__tests__/inbound-invite-redemption.test.ts +0 -4
- package/src/__tests__/inline-skill-load-permissions.test.ts +6 -8
- package/src/__tests__/intent-routing.test.ts +0 -13
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +178 -0
- package/src/__tests__/keychain-broker-client.test.ts +161 -22
- package/src/__tests__/memory-jobs-worker-backoff.test.ts +150 -0
- package/src/__tests__/memory-regressions.test.ts +8 -30
- package/src/__tests__/migration-export-http.test.ts +2 -2
- package/src/__tests__/migration-import-commit-http.test.ts +2 -2
- package/src/__tests__/migration-import-preflight-http.test.ts +2 -2
- package/src/__tests__/migration-validate-http.test.ts +2 -2
- package/src/__tests__/non-member-access-request.test.ts +0 -5
- package/src/__tests__/notification-decision-fallback.test.ts +4 -0
- package/src/__tests__/notification-decision-identity.test.ts +4 -0
- package/src/__tests__/permission-types.test.ts +1 -0
- package/src/__tests__/provider-managed-proxy-integration.test.ts +5 -6
- package/src/__tests__/qdrant-manager.test.ts +28 -2
- package/src/__tests__/registry.test.ts +0 -6
- package/src/__tests__/require-fresh-approval.test.ts +4 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -4
- package/src/__tests__/secret-routes-managed-proxy.test.ts +0 -4
- package/src/__tests__/secure-keys.test.ts +83 -263
- package/src/__tests__/shell-identity.test.ts +96 -6
- package/src/__tests__/skill-feature-flags-integration.test.ts +22 -14
- package/src/__tests__/skill-feature-flags.test.ts +46 -45
- package/src/__tests__/skill-load-feature-flag.test.ts +7 -10
- package/src/__tests__/skill-load-inline-command.test.ts +8 -12
- package/src/__tests__/skill-load-inline-includes.test.ts +6 -10
- package/src/__tests__/skill-load-tool.test.ts +0 -2
- package/src/__tests__/skill-projection-feature-flag.test.ts +33 -29
- package/src/__tests__/skills.test.ts +0 -2
- package/src/__tests__/slack-inbound-verification.test.ts +0 -4
- package/src/__tests__/suggestion-routes.test.ts +1 -32
- package/src/__tests__/system-prompt.test.ts +0 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +4 -0
- package/src/__tests__/tool-executor-shell-integration.test.ts +5 -3
- package/src/__tests__/tool-executor.test.ts +4 -0
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +0 -5
- package/src/__tests__/trusted-contact-multichannel.test.ts +0 -4
- package/src/__tests__/update-bulletin.test.ts +0 -2
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +6 -9
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -6
- package/src/__tests__/workspace-migration-015-migrate-credentials-to-keychain.test.ts +252 -0
- package/src/__tests__/workspace-migration-016-migrate-credentials-from-keychain.test.ts +218 -0
- package/src/__tests__/workspace-migration-down-functions.test.ts +1009 -0
- package/src/__tests__/workspace-migrations-runner.test.ts +114 -0
- package/src/calls/audio-store.test.ts +97 -0
- package/src/calls/audio-store.ts +205 -0
- package/src/calls/call-controller.ts +85 -7
- package/src/calls/call-domain.ts +3 -0
- package/src/calls/call-store.ts +10 -3
- package/src/calls/fish-audio-client.ts +117 -0
- package/src/calls/relay-server.ts +27 -0
- package/src/calls/twilio-routes.ts +2 -1
- package/src/calls/types.ts +1 -0
- package/src/calls/voice-ingress-preflight.ts +0 -42
- package/src/calls/voice-quality.ts +26 -5
- package/src/calls/voice-session-bridge.ts +6 -12
- package/src/cli/commands/config.ts +1 -4
- package/src/cli/commands/conversations.ts +0 -18
- package/src/cli/commands/credentials.ts +34 -4
- package/src/cli/commands/oauth/index.ts +7 -0
- package/src/cli/commands/oauth/platform.ts +179 -0
- package/src/cli/commands/platform.ts +3 -3
- package/src/config/assistant-feature-flags.ts +186 -5
- package/src/config/bundled-skills/messaging/SKILL.md +5 -5
- package/src/config/bundled-skills/phone-calls/TOOLS.json +4 -0
- package/src/config/bundled-skills/settings/TOOLS.json +2 -2
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +42 -0
- package/src/config/bundled-tool-registry.ts +1 -11
- package/src/config/env-registry.ts +1 -1
- package/src/config/env.ts +16 -16
- package/src/config/feature-flag-registry.json +48 -16
- package/src/config/loader.ts +98 -31
- package/src/config/schema.ts +4 -25
- package/src/config/schemas/calls.ts +13 -0
- package/src/config/schemas/fish-audio.ts +39 -0
- package/src/config/schemas/memory.ts +0 -4
- package/src/config/schemas/platform.ts +1 -1
- package/src/config/schemas/security.ts +4 -4
- package/src/config/types.ts +0 -1
- package/src/contacts/contact-store.ts +39 -0
- package/src/contacts/types.ts +2 -0
- package/src/context/window-manager.ts +53 -2
- package/src/credential-execution/approval-bridge.ts +1 -0
- package/src/credential-execution/executable-discovery.ts +28 -4
- package/src/credential-execution/feature-gates.ts +16 -0
- package/src/credential-execution/process-manager.ts +38 -0
- package/src/daemon/assistant-attachments.ts +9 -0
- package/src/daemon/config-watcher.ts +6 -4
- package/src/daemon/conversation-agent-loop.ts +0 -60
- package/src/daemon/conversation-memory.ts +0 -117
- package/src/daemon/conversation-runtime-assembly.ts +0 -2
- package/src/daemon/conversation-tool-setup.ts +0 -105
- package/src/daemon/conversation.ts +10 -1
- package/src/daemon/handlers/config-vercel.ts +92 -0
- package/src/daemon/handlers/conversations.ts +0 -11
- package/src/daemon/handlers/skills.ts +2 -15
- package/src/daemon/install-symlink.ts +195 -0
- package/src/daemon/lifecycle.ts +229 -96
- package/src/daemon/message-types/conversations.ts +3 -4
- package/src/daemon/message-types/diagnostics.ts +3 -22
- package/src/daemon/message-types/messages.ts +0 -2
- package/src/daemon/message-types/upgrades.ts +8 -0
- package/src/daemon/server.ts +30 -92
- package/src/events/domain-events.ts +2 -1
- package/src/followups/followup-store.ts +5 -2
- package/src/inbound/platform-callback-registration.ts +3 -3
- package/src/instrument.ts +8 -5
- package/src/memory/conversation-crud.ts +0 -236
- package/src/memory/conversation-title-service.ts +76 -11
- package/src/memory/db-init.ts +15 -11
- package/src/memory/indexer.ts +15 -106
- package/src/memory/items-extractor.ts +15 -1
- package/src/memory/job-handlers/conversation-starters.ts +4 -1
- package/src/memory/job-handlers/embedding.ts +0 -79
- package/src/memory/job-utils.ts +1 -1
- package/src/memory/jobs-store.ts +30 -13
- package/src/memory/jobs-worker.ts +31 -27
- package/src/memory/migrations/001-job-deferrals.ts +19 -0
- package/src/memory/migrations/004-entity-relation-dedup.ts +10 -0
- package/src/memory/migrations/005-fingerprint-scope-unique.ts +76 -0
- package/src/memory/migrations/006-scope-salted-fingerprints.ts +50 -0
- package/src/memory/migrations/007-assistant-id-to-self.ts +10 -0
- package/src/memory/migrations/008-remove-assistant-id-columns.ts +34 -0
- package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +26 -0
- package/src/memory/migrations/014-backfill-inbox-thread-state.ts +10 -0
- package/src/memory/migrations/015-drop-active-search-index.ts +17 -0
- package/src/memory/migrations/019-notification-tables-schema-migration.ts +12 -0
- package/src/memory/migrations/020-rename-macos-ios-channel-to-vellum.ts +121 -0
- package/src/memory/migrations/024-embedding-vector-blob.ts +74 -0
- package/src/memory/migrations/026a-embeddings-nullable-vector-json.ts +82 -0
- package/src/memory/migrations/036-normalize-phone-identities.ts +11 -0
- package/src/memory/migrations/116-messages-fts.ts +106 -1
- package/src/memory/migrations/126-backfill-guardian-principal-id.ts +52 -0
- package/src/memory/migrations/127-guardian-principal-id-not-null.ts +77 -0
- package/src/memory/migrations/134-contacts-notes-column.ts +13 -0
- package/src/memory/migrations/135-backfill-contact-interaction-stats.ts +20 -0
- package/src/memory/migrations/136-drop-assistant-id-columns.ts +52 -0
- package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +13 -0
- package/src/memory/migrations/141-rename-verification-table.ts +54 -0
- package/src/memory/migrations/142-rename-verification-session-id-column.ts +25 -0
- package/src/memory/migrations/143-rename-guardian-verification-values.ts +35 -0
- package/src/memory/migrations/144-rename-voice-to-phone.ts +136 -0
- package/src/memory/migrations/145-drop-accounts-table.ts +32 -0
- package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +14 -1
- package/src/memory/migrations/148-drop-reminders-table.ts +35 -1
- package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +69 -1
- package/src/memory/migrations/162-guardian-timestamps-epoch-ms.ts +290 -0
- package/src/memory/migrations/169-rename-gmail-provider-key-to-google.ts +51 -1
- package/src/memory/migrations/174-rename-thread-starters-table.ts +47 -1
- package/src/memory/migrations/176-drop-capability-card-state.ts +13 -0
- package/src/memory/migrations/180-backfill-inline-attachments-to-disk.ts +16 -0
- package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +28 -1
- package/src/memory/migrations/189-drop-simplified-memory.ts +42 -0
- package/src/memory/migrations/190-call-session-skip-disclosure.ts +15 -0
- package/src/memory/migrations/191-backfill-audio-attachment-mime-types.ts +64 -0
- package/src/memory/migrations/192-contacts-user-file-column.ts +15 -0
- package/src/memory/migrations/index.ts +5 -3
- package/src/memory/migrations/registry.ts +90 -0
- package/src/memory/migrations/validate-migration-state.ts +137 -11
- package/src/memory/qdrant-circuit-breaker.ts +9 -0
- package/src/memory/qdrant-client.ts +4 -6
- package/src/memory/qdrant-manager.ts +64 -7
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/contacts.ts +1 -0
- package/src/memory/schema/conversations.ts +0 -3
- package/src/memory/schema/index.ts +0 -2
- package/src/messaging/draft-store.ts +2 -2
- package/src/notifications/decision-engine.ts +4 -1
- package/src/oauth/connection-resolver.ts +6 -4
- package/src/permissions/checker.ts +0 -38
- package/src/permissions/defaults.ts +3 -3
- package/src/permissions/shell-identity.ts +76 -22
- package/src/permissions/trust-client.ts +2 -13
- package/src/permissions/trust-store.ts +8 -3
- package/src/permissions/types.ts +4 -2
- package/src/platform/client.ts +35 -7
- package/src/prompts/persona-resolver.ts +138 -0
- package/src/prompts/system-prompt.ts +36 -4
- package/src/prompts/templates/users/default.md +1 -0
- package/src/providers/registry.ts +27 -40
- package/src/runtime/auth/__tests__/credential-service.test.ts +0 -1
- package/src/runtime/auth/__tests__/external-assistant-id.test.ts +13 -68
- package/src/runtime/auth/external-assistant-id.ts +13 -59
- package/src/runtime/auth/route-policy.ts +29 -1
- package/src/runtime/auth/token-service.ts +53 -15
- package/src/runtime/channel-readiness-service.ts +1 -16
- package/src/runtime/http-server.ts +29 -2
- package/src/runtime/middleware/error-handler.ts +1 -9
- package/src/runtime/routes/audio-routes.ts +40 -0
- package/src/runtime/routes/btw-routes.ts +0 -17
- package/src/runtime/routes/conversation-management-routes.ts +0 -36
- package/src/runtime/routes/conversation-query-routes.ts +106 -2
- package/src/runtime/routes/conversation-routes.ts +4 -43
- package/src/runtime/routes/diagnostics-routes.ts +1 -477
- package/src/runtime/routes/identity-routes.ts +18 -29
- package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +4 -33
- package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +1 -1
- package/src/runtime/routes/integrations/vercel.ts +89 -0
- package/src/runtime/routes/log-export-routes.ts +5 -0
- package/src/runtime/routes/memory-item-routes.test.ts +221 -3
- package/src/runtime/routes/memory-item-routes.ts +144 -4
- package/src/runtime/routes/migration-rollback-routes.ts +209 -0
- package/src/runtime/routes/migration-routes.ts +17 -1
- package/src/runtime/routes/notification-routes.ts +58 -0
- package/src/runtime/routes/schedule-routes.ts +65 -0
- package/src/runtime/routes/settings-routes.ts +41 -1
- package/src/runtime/routes/tts-routes.ts +86 -0
- package/src/runtime/routes/upgrade-broadcast-routes.ts +175 -0
- package/src/runtime/routes/workspace-commit-routes.ts +62 -0
- package/src/runtime/routes/workspace-routes.test.ts +22 -1
- package/src/runtime/routes/workspace-routes.ts +1 -1
- package/src/runtime/routes/workspace-utils.ts +86 -2
- package/src/schedule/schedule-store.ts +0 -21
- package/src/security/ces-credential-client.ts +59 -22
- package/src/security/ces-rpc-credential-backend.ts +85 -0
- package/src/security/credential-backend.ts +12 -88
- package/src/security/keychain-broker-client.ts +10 -2
- package/src/security/secure-keys.ts +94 -113
- package/src/skills/catalog-install.ts +13 -7
- package/src/skills/inline-command-render.ts +5 -1
- package/src/skills/inline-command-runner.ts +30 -2
- package/src/telemetry/usage-telemetry-reporter.ts +4 -2
- package/src/tools/calls/call-start.ts +1 -0
- package/src/tools/executor.ts +0 -4
- package/src/tools/memory/handlers.ts +1 -129
- package/src/tools/network/script-proxy/session-manager.ts +19 -4
- package/src/tools/network/web-fetch.ts +3 -1
- package/src/tools/permission-checker.ts +18 -0
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/skills/load.ts +9 -2
- package/src/tools/types.ts +0 -8
- package/src/util/errors.ts +0 -12
- package/src/util/platform.ts +8 -55
- package/src/util/xml.ts +8 -0
- package/src/workspace/git-service.ts +5 -2
- package/src/workspace/heartbeat-service.ts +5 -24
- package/src/workspace/migrations/001-avatar-rename.ts +15 -0
- package/src/workspace/migrations/003-seed-device-id.ts +17 -1
- package/src/workspace/migrations/004-extract-collect-usage-data.ts +33 -0
- package/src/workspace/migrations/005-add-send-diagnostics.ts +3 -0
- package/src/workspace/migrations/006-services-config.ts +49 -0
- package/src/workspace/migrations/007-web-search-provider-rename.ts +27 -0
- package/src/workspace/migrations/008-voice-timeout-and-max-steps.ts +3 -0
- package/src/workspace/migrations/009-backfill-conversation-disk-view.ts +4 -0
- package/src/workspace/migrations/010-app-dir-rename.ts +78 -0
- package/src/workspace/migrations/011-backfill-installation-id.ts +11 -0
- package/src/workspace/migrations/012-rename-conversation-disk-view-dirs.ts +44 -0
- package/src/workspace/migrations/013-repair-conversation-disk-view.ts +5 -0
- package/src/workspace/migrations/015-migrate-credentials-to-keychain.ts +153 -0
- package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +156 -0
- package/src/workspace/migrations/016-migrate-credentials-from-keychain.ts +150 -0
- package/src/workspace/migrations/017-seed-persona-dirs.ts +95 -0
- package/src/workspace/migrations/migrate-to-workspace-volume.ts +23 -1
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/workspace/migrations/runner.ts +106 -2
- package/src/workspace/migrations/types.ts +4 -0
- package/src/__tests__/archive-recall.test.ts +0 -560
- package/src/__tests__/claude-code-skill-regression.test.ts +0 -206
- package/src/__tests__/claude-code-tool-profiles.test.ts +0 -99
- package/src/__tests__/conversation-memory-dirty-tail.test.ts +0 -150
- package/src/__tests__/conversation-switch-memory-reduction.test.ts +0 -474
- package/src/__tests__/db-memory-archive-migration.test.ts +0 -372
- package/src/__tests__/db-memory-brief-state-migration.test.ts +0 -213
- package/src/__tests__/db-memory-reducer-checkpoints.test.ts +0 -273
- package/src/__tests__/diagnostics-export.test.ts +0 -288
- package/src/__tests__/local-gateway-health.test.ts +0 -209
- package/src/__tests__/memory-brief-open-loops.test.ts +0 -530
- package/src/__tests__/memory-brief-time.test.ts +0 -285
- package/src/__tests__/memory-brief-wrapper.test.ts +0 -311
- package/src/__tests__/memory-chunk-archive.test.ts +0 -400
- package/src/__tests__/memory-chunk-dual-write.test.ts +0 -453
- package/src/__tests__/memory-episode-archive.test.ts +0 -370
- package/src/__tests__/memory-episode-dual-write.test.ts +0 -626
- package/src/__tests__/memory-observation-archive.test.ts +0 -375
- package/src/__tests__/memory-observation-dual-write.test.ts +0 -318
- package/src/__tests__/memory-reducer-job.test.ts +0 -538
- package/src/__tests__/memory-reducer-scheduling.test.ts +0 -473
- package/src/__tests__/memory-reducer-store.test.ts +0 -728
- package/src/__tests__/memory-reducer-types.test.ts +0 -707
- package/src/__tests__/memory-reducer.test.ts +0 -704
- package/src/__tests__/memory-simplified-config.test.ts +0 -281
- package/src/__tests__/secret-ingress-handler.test.ts +0 -120
- package/src/__tests__/simplified-memory-e2e.test.ts +0 -666
- package/src/__tests__/simplified-memory-runtime.test.ts +0 -616
- package/src/__tests__/swarm-conversation-integration.test.ts +0 -358
- package/src/__tests__/swarm-dag-pathological.test.ts +0 -547
- package/src/__tests__/swarm-orchestrator.test.ts +0 -463
- package/src/__tests__/swarm-plan-validator.test.ts +0 -384
- package/src/__tests__/swarm-recursion.test.ts +0 -197
- package/src/__tests__/swarm-router-planner.test.ts +0 -234
- package/src/__tests__/swarm-tool.test.ts +0 -185
- package/src/__tests__/swarm-worker-backend.test.ts +0 -144
- package/src/__tests__/swarm-worker-runner.test.ts +0 -288
- package/src/commands/__tests__/cc-command-registry.test.ts +0 -396
- package/src/commands/cc-command-registry.ts +0 -248
- package/src/config/bundled-skills/claude-code/SKILL.md +0 -53
- package/src/config/bundled-skills/claude-code/TOOLS.json +0 -47
- package/src/config/bundled-skills/claude-code/tools/claude-code.ts +0 -12
- package/src/config/bundled-skills/orchestration/SKILL.md +0 -33
- package/src/config/bundled-skills/orchestration/TOOLS.json +0 -35
- package/src/config/bundled-skills/orchestration/tools/swarm-delegate.ts +0 -12
- package/src/config/schemas/memory-simplified.ts +0 -101
- package/src/config/schemas/swarm.ts +0 -82
- package/src/logfire.ts +0 -135
- package/src/memory/archive-recall.ts +0 -516
- package/src/memory/archive-store.ts +0 -400
- package/src/memory/brief-formatting.ts +0 -33
- package/src/memory/brief-open-loops.ts +0 -266
- package/src/memory/brief-time.ts +0 -162
- package/src/memory/brief.ts +0 -75
- package/src/memory/job-handlers/backfill-simplified-memory.ts +0 -462
- package/src/memory/job-handlers/reduce-conversation-memory.ts +0 -229
- package/src/memory/migrations/185-memory-brief-state.ts +0 -52
- package/src/memory/migrations/186-memory-archive.ts +0 -109
- package/src/memory/migrations/187-memory-reducer-checkpoints.ts +0 -19
- package/src/memory/reducer-scheduler.ts +0 -242
- package/src/memory/reducer-store.ts +0 -271
- package/src/memory/reducer-types.ts +0 -106
- package/src/memory/reducer.ts +0 -467
- package/src/memory/schema/memory-archive.ts +0 -121
- package/src/memory/schema/memory-brief.ts +0 -55
- package/src/runtime/local-gateway-health.ts +0 -275
- package/src/security/secret-ingress.ts +0 -68
- package/src/swarm/backend-claude-code.ts +0 -225
- package/src/swarm/checkpoint.ts +0 -137
- package/src/swarm/graph-utils.ts +0 -53
- package/src/swarm/index.ts +0 -55
- package/src/swarm/limits.ts +0 -66
- package/src/swarm/orchestrator.ts +0 -424
- package/src/swarm/plan-validator.ts +0 -117
- package/src/swarm/router-planner.ts +0 -162
- package/src/swarm/router-prompts.ts +0 -39
- package/src/swarm/synthesizer.ts +0 -81
- package/src/swarm/types.ts +0 -72
- package/src/swarm/worker-backend.ts +0 -131
- package/src/swarm/worker-prompts.ts +0 -80
- package/src/swarm/worker-runner.ts +0 -170
- package/src/tools/claude-code/claude-code.ts +0 -610
- package/src/tools/swarm/delegate.ts +0 -205
package/src/logfire.ts
DELETED
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
import { getLogfireToken } from "./config/env.js";
|
|
2
|
-
import type {
|
|
3
|
-
Message,
|
|
4
|
-
Provider,
|
|
5
|
-
ProviderResponse,
|
|
6
|
-
SendMessageOptions,
|
|
7
|
-
ToolDefinition,
|
|
8
|
-
} from "./providers/types.js";
|
|
9
|
-
import { getLogger } from "./util/logger.js";
|
|
10
|
-
import { APP_VERSION } from "./version.js";
|
|
11
|
-
|
|
12
|
-
const log = getLogger("logfire");
|
|
13
|
-
|
|
14
|
-
type LogfireModule = typeof import("@pydantic/logfire-node");
|
|
15
|
-
|
|
16
|
-
let logfireEnabled: boolean = !!getLogfireToken();
|
|
17
|
-
|
|
18
|
-
let logfireInstance: LogfireModule | null = null;
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Initialize Logfire for LLM observability.
|
|
22
|
-
* Dynamically imports @pydantic/logfire-node only when enabled.
|
|
23
|
-
* Non-fatal on failure (logs warning and continues).
|
|
24
|
-
*/
|
|
25
|
-
export async function initLogfire(): Promise<void> {
|
|
26
|
-
if (!logfireEnabled) return;
|
|
27
|
-
|
|
28
|
-
try {
|
|
29
|
-
const logfire = await import("@pydantic/logfire-node");
|
|
30
|
-
logfire.configure({
|
|
31
|
-
token: getLogfireToken(),
|
|
32
|
-
serviceName: "vellum-assistant",
|
|
33
|
-
serviceVersion: APP_VERSION,
|
|
34
|
-
});
|
|
35
|
-
logfireInstance = logfire;
|
|
36
|
-
log.info("Logfire initialized");
|
|
37
|
-
} catch (err) {
|
|
38
|
-
log.warn(
|
|
39
|
-
{ err },
|
|
40
|
-
"Failed to initialize Logfire — LLM observability disabled",
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Disable Logfire after early initialization. Called when the user has opted
|
|
47
|
-
* out via the feature flag. Nulls out the instance so future wrapWithLogfire
|
|
48
|
-
* calls become no-ops.
|
|
49
|
-
*/
|
|
50
|
-
export function disableLogfire(): void {
|
|
51
|
-
logfireEnabled = false;
|
|
52
|
-
logfireInstance = null;
|
|
53
|
-
log.info("Logfire disabled by feature flag");
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Wraps a provider with Logfire tracing spans.
|
|
58
|
-
* When logfireEnabled is false, returns the provider as-is (no wrapper allocated).
|
|
59
|
-
*/
|
|
60
|
-
export function wrapWithLogfire(provider: Provider): Provider {
|
|
61
|
-
if (!logfireEnabled) return provider;
|
|
62
|
-
return new LogfireProvider(provider);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Wrapper provider that instruments each sendMessage call with a Logfire span.
|
|
67
|
-
* When Logfire is not initialized, acts as a pure pass-through with zero overhead.
|
|
68
|
-
*/
|
|
69
|
-
class LogfireProvider implements Provider {
|
|
70
|
-
public readonly name: string;
|
|
71
|
-
|
|
72
|
-
constructor(private readonly inner: Provider) {
|
|
73
|
-
this.name = inner.name;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
async sendMessage(
|
|
77
|
-
messages: Message[],
|
|
78
|
-
tools?: ToolDefinition[],
|
|
79
|
-
systemPrompt?: string,
|
|
80
|
-
options?: SendMessageOptions,
|
|
81
|
-
): Promise<ProviderResponse> {
|
|
82
|
-
if (!logfireInstance) {
|
|
83
|
-
return this.inner.sendMessage(messages, tools, systemPrompt, options);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const start = Date.now();
|
|
87
|
-
|
|
88
|
-
return logfireInstance.span(
|
|
89
|
-
`llm.${this.name}`,
|
|
90
|
-
{
|
|
91
|
-
"llm.provider": this.name,
|
|
92
|
-
"llm.message_count": messages.length,
|
|
93
|
-
"llm.tool_count": tools?.length ?? 0,
|
|
94
|
-
},
|
|
95
|
-
{},
|
|
96
|
-
async (span) => {
|
|
97
|
-
try {
|
|
98
|
-
const response = await this.inner.sendMessage(
|
|
99
|
-
messages,
|
|
100
|
-
tools,
|
|
101
|
-
systemPrompt,
|
|
102
|
-
options,
|
|
103
|
-
);
|
|
104
|
-
const durationMs = Date.now() - start;
|
|
105
|
-
|
|
106
|
-
span.setAttributes({
|
|
107
|
-
"llm.model": response.model,
|
|
108
|
-
"llm.stop_reason": response.stopReason,
|
|
109
|
-
"llm.usage.input_tokens": response.usage.inputTokens,
|
|
110
|
-
"llm.usage.output_tokens": response.usage.outputTokens,
|
|
111
|
-
"llm.usage.cache_creation_input_tokens":
|
|
112
|
-
response.usage.cacheCreationInputTokens ?? 0,
|
|
113
|
-
"llm.usage.cache_read_input_tokens":
|
|
114
|
-
response.usage.cacheReadInputTokens ?? 0,
|
|
115
|
-
"llm.duration_ms": durationMs,
|
|
116
|
-
"llm.success": true,
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
return response;
|
|
120
|
-
} catch (error) {
|
|
121
|
-
const durationMs = Date.now() - start;
|
|
122
|
-
span.setAttributes({
|
|
123
|
-
"llm.duration_ms": durationMs,
|
|
124
|
-
"llm.success": false,
|
|
125
|
-
"llm.error.type":
|
|
126
|
-
error instanceof Error ? error.constructor.name : "Unknown",
|
|
127
|
-
"llm.error.message":
|
|
128
|
-
error instanceof Error ? error.message : String(error),
|
|
129
|
-
});
|
|
130
|
-
throw error;
|
|
131
|
-
}
|
|
132
|
-
},
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
@@ -1,516 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Archive recall: retrieval layer over the simplified memory archive tables
|
|
3
|
-
* (memory_observations, memory_chunks, memory_episodes).
|
|
4
|
-
*
|
|
5
|
-
* Two retrieval paths:
|
|
6
|
-
*
|
|
7
|
-
* 1. **Prefetch** — lightweight query run on every turn. Fetches recent
|
|
8
|
-
* episodes and observations to detect whether the user's turn references
|
|
9
|
-
* past context that the archive can answer.
|
|
10
|
-
*
|
|
11
|
-
* 2. **Deeper recall** — triggered when the prefetch surfaces strong hits,
|
|
12
|
-
* or when the user's turn contains explicit past-reference or
|
|
13
|
-
* analogy/debugging-shaped language. Queries all three archive tables
|
|
14
|
-
* and returns up to 3 source-linked bullets wrapped in
|
|
15
|
-
* `<supporting_recall>`.
|
|
16
|
-
*
|
|
17
|
-
* Empty results produce no output (no `<supporting_recall>` tag).
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
import { and, desc, eq, like, or, sql } from "drizzle-orm";
|
|
21
|
-
|
|
22
|
-
import { getLogger } from "../util/logger.js";
|
|
23
|
-
import { getDb } from "./db.js";
|
|
24
|
-
import { memoryChunks, memoryEpisodes, memoryObservations } from "./schema.js";
|
|
25
|
-
|
|
26
|
-
const log = getLogger("memory-archive-recall");
|
|
27
|
-
|
|
28
|
-
// ── Pattern matchers ────────────────────────────────────────────────
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Phrases that signal the user is explicitly referencing a past
|
|
32
|
-
* interaction, artifact, or fact the assistant should recall.
|
|
33
|
-
*/
|
|
34
|
-
const PAST_REFERENCE_PATTERNS = [
|
|
35
|
-
/\b(?:remember|recall|mentioned|talked about|discussed|said|told you|last time|earlier|before|previously)\b/i,
|
|
36
|
-
/\bwhat (?:did|was|were)\b.*\b(?:we|i|you)\b/i,
|
|
37
|
-
/\bdo you (?:know|remember)\b/i,
|
|
38
|
-
];
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Phrases that signal an analogy or debugging-shaped query where
|
|
42
|
-
* historical context would be especially valuable.
|
|
43
|
-
*/
|
|
44
|
-
const ANALOGY_DEBUG_PATTERNS = [
|
|
45
|
-
/\b(?:similar to|like when|same (?:issue|problem|error|bug)|happened before|recurring|déjà vu)\b/i,
|
|
46
|
-
/\b(?:last time.*(?:fix|solve|debug|resolve))\b/i,
|
|
47
|
-
/\b(?:keep (?:getting|seeing|hitting)|again|keeps happening)\b/i,
|
|
48
|
-
];
|
|
49
|
-
|
|
50
|
-
// ── Turn classification ─────────────────────────────────────────────
|
|
51
|
-
|
|
52
|
-
export type RecallTrigger =
|
|
53
|
-
| "explicit_past_reference"
|
|
54
|
-
| "analogy_debug"
|
|
55
|
-
| "strong_prefetch"
|
|
56
|
-
| "none";
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Classify whether a user turn warrants deeper archive recall.
|
|
60
|
-
*/
|
|
61
|
-
export function classifyRecallTrigger(
|
|
62
|
-
userText: string,
|
|
63
|
-
prefetchHitCount: number,
|
|
64
|
-
): RecallTrigger {
|
|
65
|
-
if (PAST_REFERENCE_PATTERNS.some((p) => p.test(userText))) {
|
|
66
|
-
return "explicit_past_reference";
|
|
67
|
-
}
|
|
68
|
-
if (ANALOGY_DEBUG_PATTERNS.some((p) => p.test(userText))) {
|
|
69
|
-
return "analogy_debug";
|
|
70
|
-
}
|
|
71
|
-
if (prefetchHitCount >= 2) {
|
|
72
|
-
return "strong_prefetch";
|
|
73
|
-
}
|
|
74
|
-
return "none";
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// ── Prefetch ────────────────────────────────────────────────────────
|
|
78
|
-
|
|
79
|
-
/** A lightweight prefetch hit from the archive tables. */
|
|
80
|
-
export interface PrefetchHit {
|
|
81
|
-
source: "episode" | "observation" | "chunk";
|
|
82
|
-
id: string;
|
|
83
|
-
content: string;
|
|
84
|
-
createdAt: number;
|
|
85
|
-
conversationId?: string | null;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Lightweight prefetch over recent episodes and observations for the
|
|
90
|
-
* given scope. Returns up to `limit` hits ordered by recency. This is
|
|
91
|
-
* cheap enough to run on every turn.
|
|
92
|
-
*/
|
|
93
|
-
export function prefetchArchive(
|
|
94
|
-
scopeId: string,
|
|
95
|
-
userText: string,
|
|
96
|
-
limit: number = 10,
|
|
97
|
-
): PrefetchHit[] {
|
|
98
|
-
const db = getDb();
|
|
99
|
-
const hits: PrefetchHit[] = [];
|
|
100
|
-
|
|
101
|
-
// Extract meaningful keywords from user text (words >= 4 chars)
|
|
102
|
-
const keywords = extractKeywords(userText);
|
|
103
|
-
if (keywords.length === 0) return hits;
|
|
104
|
-
|
|
105
|
-
try {
|
|
106
|
-
// Query recent episodes whose title or summary contain any keyword
|
|
107
|
-
const episodeConditions = keywords.map((kw) =>
|
|
108
|
-
or(
|
|
109
|
-
like(memoryEpisodes.title, `%${kw}%`),
|
|
110
|
-
like(memoryEpisodes.summary, `%${kw}%`),
|
|
111
|
-
),
|
|
112
|
-
);
|
|
113
|
-
|
|
114
|
-
const episodes = db
|
|
115
|
-
.select({
|
|
116
|
-
id: memoryEpisodes.id,
|
|
117
|
-
title: memoryEpisodes.title,
|
|
118
|
-
summary: memoryEpisodes.summary,
|
|
119
|
-
createdAt: memoryEpisodes.createdAt,
|
|
120
|
-
conversationId: memoryEpisodes.conversationId,
|
|
121
|
-
})
|
|
122
|
-
.from(memoryEpisodes)
|
|
123
|
-
.where(and(eq(memoryEpisodes.scopeId, scopeId), or(...episodeConditions)))
|
|
124
|
-
.orderBy(desc(memoryEpisodes.createdAt))
|
|
125
|
-
.limit(limit)
|
|
126
|
-
.all();
|
|
127
|
-
|
|
128
|
-
for (const ep of episodes) {
|
|
129
|
-
hits.push({
|
|
130
|
-
source: "episode",
|
|
131
|
-
id: ep.id,
|
|
132
|
-
content: `${ep.title}: ${ep.summary}`,
|
|
133
|
-
createdAt: ep.createdAt,
|
|
134
|
-
conversationId: ep.conversationId,
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Query recent observations whose content matches any keyword
|
|
139
|
-
const observationConditions = keywords.map((kw) =>
|
|
140
|
-
like(memoryObservations.content, `%${kw}%`),
|
|
141
|
-
);
|
|
142
|
-
|
|
143
|
-
const observations = db
|
|
144
|
-
.select({
|
|
145
|
-
id: memoryObservations.id,
|
|
146
|
-
content: memoryObservations.content,
|
|
147
|
-
createdAt: memoryObservations.createdAt,
|
|
148
|
-
conversationId: memoryObservations.conversationId,
|
|
149
|
-
})
|
|
150
|
-
.from(memoryObservations)
|
|
151
|
-
.where(
|
|
152
|
-
and(
|
|
153
|
-
eq(memoryObservations.scopeId, scopeId),
|
|
154
|
-
or(...observationConditions),
|
|
155
|
-
),
|
|
156
|
-
)
|
|
157
|
-
.orderBy(desc(memoryObservations.createdAt))
|
|
158
|
-
.limit(limit)
|
|
159
|
-
.all();
|
|
160
|
-
|
|
161
|
-
for (const obs of observations) {
|
|
162
|
-
hits.push({
|
|
163
|
-
source: "observation",
|
|
164
|
-
id: obs.id,
|
|
165
|
-
content: obs.content,
|
|
166
|
-
createdAt: obs.createdAt,
|
|
167
|
-
conversationId: obs.conversationId,
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
} catch (err) {
|
|
171
|
-
log.warn({ err }, "Archive prefetch failed");
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Sort all hits by recency and cap at limit
|
|
175
|
-
hits.sort((a, b) => b.createdAt - a.createdAt);
|
|
176
|
-
return hits.slice(0, limit);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// ── Deeper recall ───────────────────────────────────────────────────
|
|
180
|
-
|
|
181
|
-
/** A source-linked recall bullet for injection. */
|
|
182
|
-
export interface RecallBullet {
|
|
183
|
-
/** Human-readable one-line summary. */
|
|
184
|
-
text: string;
|
|
185
|
-
/** Which archive table sourced this bullet. */
|
|
186
|
-
source: "episode" | "observation" | "chunk";
|
|
187
|
-
/** Row ID in the source table. */
|
|
188
|
-
sourceId: string;
|
|
189
|
-
/** Optional conversation title for provenance. */
|
|
190
|
-
conversationTitle?: string | null;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
export interface ArchiveRecallResult {
|
|
194
|
-
/** The recall trigger that activated deeper recall (or "none"). */
|
|
195
|
-
trigger: RecallTrigger;
|
|
196
|
-
/** Up to 3 source-linked bullets. Empty when no relevant results. */
|
|
197
|
-
bullets: RecallBullet[];
|
|
198
|
-
/** Rendered `<supporting_recall>` block, or empty string. */
|
|
199
|
-
text: string;
|
|
200
|
-
/** Number of prefetch hits examined. */
|
|
201
|
-
prefetchHitCount: number;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* Run archive recall for a user turn.
|
|
206
|
-
*
|
|
207
|
-
* 1. Runs a lightweight prefetch over episodes and observations.
|
|
208
|
-
* 2. Classifies whether deeper recall is warranted.
|
|
209
|
-
* 3. If triggered, queries all three archive tables and assembles
|
|
210
|
-
* up to 3 source-linked bullets.
|
|
211
|
-
* 4. Returns rendered `<supporting_recall>` or empty string.
|
|
212
|
-
*/
|
|
213
|
-
export function buildArchiveRecall(
|
|
214
|
-
scopeId: string,
|
|
215
|
-
userText: string,
|
|
216
|
-
): ArchiveRecallResult {
|
|
217
|
-
// Step 1: prefetch
|
|
218
|
-
const prefetchHits = prefetchArchive(scopeId, userText);
|
|
219
|
-
const prefetchHitCount = prefetchHits.length;
|
|
220
|
-
|
|
221
|
-
// Step 2: classify
|
|
222
|
-
const trigger = classifyRecallTrigger(userText, prefetchHitCount);
|
|
223
|
-
|
|
224
|
-
if (trigger === "none") {
|
|
225
|
-
return {
|
|
226
|
-
trigger,
|
|
227
|
-
bullets: [],
|
|
228
|
-
text: "",
|
|
229
|
-
prefetchHitCount,
|
|
230
|
-
};
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// Step 3: deeper recall
|
|
234
|
-
const bullets = deeperRecall(scopeId, userText, prefetchHits);
|
|
235
|
-
|
|
236
|
-
// Step 4: render
|
|
237
|
-
const text = renderSupportingRecall(bullets);
|
|
238
|
-
|
|
239
|
-
log.debug(
|
|
240
|
-
{
|
|
241
|
-
trigger,
|
|
242
|
-
prefetchHitCount,
|
|
243
|
-
bulletCount: bullets.length,
|
|
244
|
-
},
|
|
245
|
-
"Archive recall completed",
|
|
246
|
-
);
|
|
247
|
-
|
|
248
|
-
return {
|
|
249
|
-
trigger,
|
|
250
|
-
bullets,
|
|
251
|
-
text,
|
|
252
|
-
prefetchHitCount,
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// ── Deeper recall implementation ────────────────────────────────────
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* Query all three archive tables for the user's text and assemble
|
|
260
|
-
* up to 3 source-linked bullets. Prioritizes episodes (narrative
|
|
261
|
-
* summaries) over observations (raw facts) over chunks (indexed text).
|
|
262
|
-
*/
|
|
263
|
-
function deeperRecall(
|
|
264
|
-
scopeId: string,
|
|
265
|
-
userText: string,
|
|
266
|
-
prefetchHits: PrefetchHit[],
|
|
267
|
-
): RecallBullet[] {
|
|
268
|
-
const db = getDb();
|
|
269
|
-
const keywords = extractKeywords(userText);
|
|
270
|
-
if (keywords.length === 0) return [];
|
|
271
|
-
|
|
272
|
-
const bullets: RecallBullet[] = [];
|
|
273
|
-
const seenContent = new Set<string>();
|
|
274
|
-
const MAX_BULLETS = 3;
|
|
275
|
-
|
|
276
|
-
try {
|
|
277
|
-
// --- Episodes: highest signal (narrative summaries) ---
|
|
278
|
-
const episodeConditions = keywords.map((kw) =>
|
|
279
|
-
or(
|
|
280
|
-
like(memoryEpisodes.title, `%${kw}%`),
|
|
281
|
-
like(memoryEpisodes.summary, `%${kw}%`),
|
|
282
|
-
),
|
|
283
|
-
);
|
|
284
|
-
|
|
285
|
-
const episodes = db
|
|
286
|
-
.select({
|
|
287
|
-
id: memoryEpisodes.id,
|
|
288
|
-
title: memoryEpisodes.title,
|
|
289
|
-
summary: memoryEpisodes.summary,
|
|
290
|
-
conversationId: memoryEpisodes.conversationId,
|
|
291
|
-
})
|
|
292
|
-
.from(memoryEpisodes)
|
|
293
|
-
.where(and(eq(memoryEpisodes.scopeId, scopeId), or(...episodeConditions)))
|
|
294
|
-
.orderBy(desc(memoryEpisodes.createdAt))
|
|
295
|
-
.limit(MAX_BULLETS)
|
|
296
|
-
.all();
|
|
297
|
-
|
|
298
|
-
for (const ep of episodes) {
|
|
299
|
-
if (bullets.length >= MAX_BULLETS) break;
|
|
300
|
-
const normalized = normalizeForDedup(ep.summary);
|
|
301
|
-
if (seenContent.has(normalized)) continue;
|
|
302
|
-
seenContent.add(normalized);
|
|
303
|
-
|
|
304
|
-
const convTitle = lookupConversationTitle(db, ep.conversationId);
|
|
305
|
-
bullets.push({
|
|
306
|
-
text: `${ep.title} — ${truncate(ep.summary, 200)}`,
|
|
307
|
-
source: "episode",
|
|
308
|
-
sourceId: ep.id,
|
|
309
|
-
conversationTitle: convTitle,
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// --- Observations: raw factual statements ---
|
|
314
|
-
if (bullets.length < MAX_BULLETS) {
|
|
315
|
-
const observationConditions = keywords.map((kw) =>
|
|
316
|
-
like(memoryObservations.content, `%${kw}%`),
|
|
317
|
-
);
|
|
318
|
-
|
|
319
|
-
const observations = db
|
|
320
|
-
.select({
|
|
321
|
-
id: memoryObservations.id,
|
|
322
|
-
content: memoryObservations.content,
|
|
323
|
-
conversationId: memoryObservations.conversationId,
|
|
324
|
-
})
|
|
325
|
-
.from(memoryObservations)
|
|
326
|
-
.where(
|
|
327
|
-
and(
|
|
328
|
-
eq(memoryObservations.scopeId, scopeId),
|
|
329
|
-
or(...observationConditions),
|
|
330
|
-
),
|
|
331
|
-
)
|
|
332
|
-
.orderBy(desc(memoryObservations.createdAt))
|
|
333
|
-
.limit(MAX_BULLETS)
|
|
334
|
-
.all();
|
|
335
|
-
|
|
336
|
-
for (const obs of observations) {
|
|
337
|
-
if (bullets.length >= MAX_BULLETS) break;
|
|
338
|
-
const normalized = normalizeForDedup(obs.content);
|
|
339
|
-
if (seenContent.has(normalized)) continue;
|
|
340
|
-
seenContent.add(normalized);
|
|
341
|
-
|
|
342
|
-
const convTitle = lookupConversationTitle(db, obs.conversationId);
|
|
343
|
-
bullets.push({
|
|
344
|
-
text: truncate(obs.content, 200),
|
|
345
|
-
source: "observation",
|
|
346
|
-
sourceId: obs.id,
|
|
347
|
-
conversationTitle: convTitle,
|
|
348
|
-
});
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
// --- Chunks: indexed text fragments ---
|
|
353
|
-
if (bullets.length < MAX_BULLETS) {
|
|
354
|
-
const chunkConditions = keywords.map((kw) =>
|
|
355
|
-
like(memoryChunks.content, `%${kw}%`),
|
|
356
|
-
);
|
|
357
|
-
|
|
358
|
-
const chunks = db
|
|
359
|
-
.select({
|
|
360
|
-
id: memoryChunks.id,
|
|
361
|
-
content: memoryChunks.content,
|
|
362
|
-
observationId: memoryChunks.observationId,
|
|
363
|
-
})
|
|
364
|
-
.from(memoryChunks)
|
|
365
|
-
.where(and(eq(memoryChunks.scopeId, scopeId), or(...chunkConditions)))
|
|
366
|
-
.orderBy(desc(memoryChunks.createdAt))
|
|
367
|
-
.limit(MAX_BULLETS)
|
|
368
|
-
.all();
|
|
369
|
-
|
|
370
|
-
for (const chunk of chunks) {
|
|
371
|
-
if (bullets.length >= MAX_BULLETS) break;
|
|
372
|
-
const normalized = normalizeForDedup(chunk.content);
|
|
373
|
-
if (seenContent.has(normalized)) continue;
|
|
374
|
-
seenContent.add(normalized);
|
|
375
|
-
|
|
376
|
-
// Look up the observation's conversationId for provenance
|
|
377
|
-
const obs = db
|
|
378
|
-
.select({ conversationId: memoryObservations.conversationId })
|
|
379
|
-
.from(memoryObservations)
|
|
380
|
-
.where(eq(memoryObservations.id, chunk.observationId))
|
|
381
|
-
.get();
|
|
382
|
-
|
|
383
|
-
const convTitle = obs
|
|
384
|
-
? lookupConversationTitle(db, obs.conversationId)
|
|
385
|
-
: null;
|
|
386
|
-
|
|
387
|
-
bullets.push({
|
|
388
|
-
text: truncate(chunk.content, 200),
|
|
389
|
-
source: "chunk",
|
|
390
|
-
sourceId: chunk.id,
|
|
391
|
-
conversationTitle: convTitle,
|
|
392
|
-
});
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
} catch (err) {
|
|
396
|
-
log.warn({ err }, "Deeper archive recall failed");
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
// Also incorporate prefetch hits that weren't already captured
|
|
400
|
-
for (const hit of prefetchHits) {
|
|
401
|
-
if (bullets.length >= MAX_BULLETS) break;
|
|
402
|
-
const normalized = normalizeForDedup(hit.content);
|
|
403
|
-
if (seenContent.has(normalized)) continue;
|
|
404
|
-
seenContent.add(normalized);
|
|
405
|
-
|
|
406
|
-
bullets.push({
|
|
407
|
-
text: truncate(hit.content, 200),
|
|
408
|
-
source: hit.source,
|
|
409
|
-
sourceId: hit.id,
|
|
410
|
-
});
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
return bullets.slice(0, MAX_BULLETS);
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
// ── Rendering ───────────────────────────────────────────────────────
|
|
417
|
-
|
|
418
|
-
/**
|
|
419
|
-
* Render recall bullets into `<supporting_recall>` XML block.
|
|
420
|
-
* Returns empty string when there are no bullets.
|
|
421
|
-
*/
|
|
422
|
-
export function renderSupportingRecall(bullets: RecallBullet[]): string {
|
|
423
|
-
if (bullets.length === 0) return "";
|
|
424
|
-
|
|
425
|
-
const lines = bullets.map((b) => {
|
|
426
|
-
const provenance = b.conversationTitle
|
|
427
|
-
? ` (from: ${b.conversationTitle})`
|
|
428
|
-
: "";
|
|
429
|
-
return `- ${b.text}${provenance}`;
|
|
430
|
-
});
|
|
431
|
-
|
|
432
|
-
return `<supporting_recall>\n${lines.join("\n")}\n</supporting_recall>`;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
// ── Helpers ─────────────────────────────────────────────────────────
|
|
436
|
-
|
|
437
|
-
/**
|
|
438
|
-
* Extract meaningful keywords from user text for LIKE-based matching.
|
|
439
|
-
* Filters out short words (< 4 chars) and common stop words.
|
|
440
|
-
*/
|
|
441
|
-
export function extractKeywords(text: string): string[] {
|
|
442
|
-
const STOP_WORDS = new Set([
|
|
443
|
-
"about",
|
|
444
|
-
"also",
|
|
445
|
-
"been",
|
|
446
|
-
"could",
|
|
447
|
-
"does",
|
|
448
|
-
"from",
|
|
449
|
-
"have",
|
|
450
|
-
"into",
|
|
451
|
-
"just",
|
|
452
|
-
"know",
|
|
453
|
-
"like",
|
|
454
|
-
"make",
|
|
455
|
-
"more",
|
|
456
|
-
"much",
|
|
457
|
-
"only",
|
|
458
|
-
"over",
|
|
459
|
-
"said",
|
|
460
|
-
"some",
|
|
461
|
-
"than",
|
|
462
|
-
"that",
|
|
463
|
-
"them",
|
|
464
|
-
"then",
|
|
465
|
-
"they",
|
|
466
|
-
"this",
|
|
467
|
-
"very",
|
|
468
|
-
"want",
|
|
469
|
-
"were",
|
|
470
|
-
"what",
|
|
471
|
-
"when",
|
|
472
|
-
"will",
|
|
473
|
-
"with",
|
|
474
|
-
"your",
|
|
475
|
-
]);
|
|
476
|
-
|
|
477
|
-
const words = text
|
|
478
|
-
.toLowerCase()
|
|
479
|
-
.replace(/[^\w\s]/g, " ")
|
|
480
|
-
.split(/\s+/)
|
|
481
|
-
.filter((w) => w.length >= 4 && !STOP_WORDS.has(w));
|
|
482
|
-
|
|
483
|
-
// Deduplicate while preserving order
|
|
484
|
-
return [...new Set(words)];
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
/**
|
|
488
|
-
* Look up a conversation's title for provenance display.
|
|
489
|
-
*/
|
|
490
|
-
function lookupConversationTitle(
|
|
491
|
-
db: ReturnType<typeof getDb>,
|
|
492
|
-
conversationId: string,
|
|
493
|
-
): string | null {
|
|
494
|
-
try {
|
|
495
|
-
const row = db
|
|
496
|
-
.select({ title: sql<string | null>`title` })
|
|
497
|
-
.from(sql`conversations`)
|
|
498
|
-
.where(sql`id = ${conversationId}`)
|
|
499
|
-
.get();
|
|
500
|
-
return row?.title ?? null;
|
|
501
|
-
} catch {
|
|
502
|
-
return null;
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
function truncate(text: string, max: number): string {
|
|
507
|
-
if (text.length <= max) return text;
|
|
508
|
-
return `${text.slice(0, max - 3)}...`;
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
/**
|
|
512
|
-
* Normalize text for content deduplication across sources.
|
|
513
|
-
*/
|
|
514
|
-
function normalizeForDedup(text: string): string {
|
|
515
|
-
return text.toLowerCase().replace(/\s+/g, " ").trim();
|
|
516
|
-
}
|