@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/memory/brief-time.ts
DELETED
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Deterministic compiler for the "Time-Relevant Context" section of the
|
|
3
|
-
* memory brief. Reads active `time_contexts` rows plus due-soon live
|
|
4
|
-
* schedule jobs, sorts them by urgency bucket, and caps the output.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { and, eq, gte, lte } from "drizzle-orm";
|
|
8
|
-
|
|
9
|
-
import { getDueSoonSchedules } from "../schedule/schedule-store.js";
|
|
10
|
-
import type { BriefEntry } from "./brief-formatting.js";
|
|
11
|
-
import { renderBriefSection } from "./brief-formatting.js";
|
|
12
|
-
import type { DrizzleDb } from "./db-connection.js";
|
|
13
|
-
import { timeContexts } from "./schema/memory-brief.js";
|
|
14
|
-
|
|
15
|
-
const MAX_ENTRIES = 3;
|
|
16
|
-
const SEVEN_DAYS_MS = 7 * 24 * 60 * 60 * 1000;
|
|
17
|
-
const ONE_DAY_MS = 24 * 60 * 60 * 1000;
|
|
18
|
-
|
|
19
|
-
/** Urgency buckets — lower number = higher priority. */
|
|
20
|
-
const enum Bucket {
|
|
21
|
-
HappeningNow = 0,
|
|
22
|
-
Overdue = 1,
|
|
23
|
-
Within24h = 2,
|
|
24
|
-
Within7d = 3,
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
interface Candidate {
|
|
28
|
-
bucket: Bucket;
|
|
29
|
-
/** Epoch ms timestamp used for secondary sort within a bucket. */
|
|
30
|
-
sortKey: number;
|
|
31
|
-
entry: BriefEntry;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// ────────────────────────────────────────────────────────────────────
|
|
35
|
-
// Public API
|
|
36
|
-
// ────────────────────────────────────────────────────────────────────
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Compile the time-relevant brief section.
|
|
40
|
-
*
|
|
41
|
-
* @param db Drizzle database instance
|
|
42
|
-
* @param now Current epoch-ms timestamp (injectable for deterministic tests)
|
|
43
|
-
* @returns Markdown string for the section, or `null` if nothing qualifies
|
|
44
|
-
*/
|
|
45
|
-
export function compileTimeBrief(
|
|
46
|
-
db: DrizzleDb,
|
|
47
|
-
scopeId: string,
|
|
48
|
-
now: number,
|
|
49
|
-
): string | null {
|
|
50
|
-
const candidates: Candidate[] = [];
|
|
51
|
-
|
|
52
|
-
collectTimeContexts(db, scopeId, now, candidates);
|
|
53
|
-
collectDueSoonSchedules(now, candidates);
|
|
54
|
-
|
|
55
|
-
// Sort: primary = bucket ascending, secondary = sortKey ascending (sooner first)
|
|
56
|
-
candidates.sort((a, b) => a.bucket - b.bucket || a.sortKey - b.sortKey);
|
|
57
|
-
|
|
58
|
-
const entries = candidates.slice(0, MAX_ENTRIES).map((c) => c.entry);
|
|
59
|
-
return renderBriefSection("Time-Relevant Context", entries, MAX_ENTRIES);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// ────────────────────────────────────────────────────────────────────
|
|
63
|
-
// Internal collectors
|
|
64
|
-
// ────────────────────────────────────────────────────────────────────
|
|
65
|
-
|
|
66
|
-
function collectTimeContexts(
|
|
67
|
-
db: DrizzleDb,
|
|
68
|
-
scopeId: string,
|
|
69
|
-
now: number,
|
|
70
|
-
out: Candidate[],
|
|
71
|
-
): void {
|
|
72
|
-
// Active time contexts: scopeId match AND activeFrom <= now AND activeUntil >= now
|
|
73
|
-
// Uses idx_time_contexts_scope_active_until composite index
|
|
74
|
-
const rows = db
|
|
75
|
-
.select()
|
|
76
|
-
.from(timeContexts)
|
|
77
|
-
.where(
|
|
78
|
-
and(
|
|
79
|
-
eq(timeContexts.scopeId, scopeId),
|
|
80
|
-
lte(timeContexts.activeFrom, now),
|
|
81
|
-
gte(timeContexts.activeUntil, now),
|
|
82
|
-
),
|
|
83
|
-
)
|
|
84
|
-
.all();
|
|
85
|
-
|
|
86
|
-
for (const row of rows) {
|
|
87
|
-
const remaining = row.activeUntil - now;
|
|
88
|
-
let bucket: Bucket;
|
|
89
|
-
|
|
90
|
-
if (row.activeFrom <= now && row.activeUntil >= now) {
|
|
91
|
-
// Currently active — classify by how much time remains
|
|
92
|
-
if (remaining <= ONE_DAY_MS) {
|
|
93
|
-
bucket = Bucket.HappeningNow;
|
|
94
|
-
} else if (remaining <= SEVEN_DAYS_MS) {
|
|
95
|
-
bucket = Bucket.Within24h;
|
|
96
|
-
} else {
|
|
97
|
-
bucket = Bucket.Within7d;
|
|
98
|
-
}
|
|
99
|
-
} else {
|
|
100
|
-
bucket = Bucket.Within7d;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
out.push({
|
|
104
|
-
bucket,
|
|
105
|
-
sortKey: row.activeUntil,
|
|
106
|
-
entry: { text: row.summary },
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
function collectDueSoonSchedules(now: number, out: Candidate[]): void {
|
|
112
|
-
const jobs = getDueSoonSchedules(now, SEVEN_DAYS_MS);
|
|
113
|
-
|
|
114
|
-
for (const job of jobs) {
|
|
115
|
-
const delta = job.nextRunAt - now;
|
|
116
|
-
let bucket: Bucket;
|
|
117
|
-
|
|
118
|
-
if (delta <= 0) {
|
|
119
|
-
bucket = Bucket.Overdue;
|
|
120
|
-
} else if (delta <= ONE_DAY_MS) {
|
|
121
|
-
bucket = Bucket.Within24h;
|
|
122
|
-
} else {
|
|
123
|
-
bucket = Bucket.Within7d;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const label = formatScheduleLabel(job.name, job.nextRunAt, now);
|
|
127
|
-
out.push({
|
|
128
|
-
bucket,
|
|
129
|
-
sortKey: job.nextRunAt,
|
|
130
|
-
entry: { text: label },
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// ────────────────────────────────────────────────────────────────────
|
|
136
|
-
// Formatting
|
|
137
|
-
// ────────────────────────────────────────────────────────────────────
|
|
138
|
-
|
|
139
|
-
function formatScheduleLabel(
|
|
140
|
-
name: string,
|
|
141
|
-
nextRunAt: number,
|
|
142
|
-
now: number,
|
|
143
|
-
): string {
|
|
144
|
-
const delta = nextRunAt - now;
|
|
145
|
-
|
|
146
|
-
if (delta <= 0) {
|
|
147
|
-
return `Scheduled: "${name}" — overdue`;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const minutes = Math.round(delta / 60_000);
|
|
151
|
-
if (minutes < 60) {
|
|
152
|
-
return `Scheduled: "${name}" — in ${minutes} minute${minutes === 1 ? "" : "s"}`;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const hours = Math.round(delta / 3_600_000);
|
|
156
|
-
if (hours < 24) {
|
|
157
|
-
return `Scheduled: "${name}" — in ${hours} hour${hours === 1 ? "" : "s"}`;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
const days = Math.round(delta / 86_400_000);
|
|
161
|
-
return `Scheduled: "${name}" — in ${days} day${days === 1 ? "" : "s"}`;
|
|
162
|
-
}
|
package/src/memory/brief.ts
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Top-level memory brief composer.
|
|
3
|
-
*
|
|
4
|
-
* Composes the "Time-Relevant Context" and "Open Loops" sections into a
|
|
5
|
-
* single `<memory_brief>` XML-wrapped block. Omits empty sections and
|
|
6
|
-
* returns an empty string when neither section has content.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { renderBriefSection } from "./brief-formatting.js";
|
|
10
|
-
import type { OpenLoopBriefResult } from "./brief-open-loops.js";
|
|
11
|
-
import { compileOpenLoopBrief } from "./brief-open-loops.js";
|
|
12
|
-
import { compileTimeBrief } from "./brief-time.js";
|
|
13
|
-
import type { DrizzleDb } from "./db-connection.js";
|
|
14
|
-
|
|
15
|
-
/** Maximum number of open-loop bullets to include in the brief. */
|
|
16
|
-
const MAX_OPEN_LOOP_ENTRIES = 5;
|
|
17
|
-
|
|
18
|
-
export interface MemoryBriefResult {
|
|
19
|
-
/** Rendered `<memory_brief>` block, or empty string if nothing to show. */
|
|
20
|
-
text: string;
|
|
21
|
-
/** Forwarded from `compileOpenLoopBrief` for downstream tracking. */
|
|
22
|
-
resurfacedLoopId: string | null;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Compile the full memory brief block.
|
|
27
|
-
*
|
|
28
|
-
* @param db Drizzle database instance
|
|
29
|
-
* @param scopeId Memory scope (e.g. assistant instance ID)
|
|
30
|
-
* @param userMessageId Current user message ID — used for deterministic
|
|
31
|
-
* open-loop resurfacing
|
|
32
|
-
* @param now Current epoch-ms timestamp (injectable for tests)
|
|
33
|
-
* @returns `{ text, resurfacedLoopId }` — `text` is the
|
|
34
|
-
* rendered `<memory_brief>` block or empty string
|
|
35
|
-
*/
|
|
36
|
-
export function compileMemoryBrief(
|
|
37
|
-
db: DrizzleDb,
|
|
38
|
-
scopeId: string,
|
|
39
|
-
userMessageId: string,
|
|
40
|
-
now: number = Date.now(),
|
|
41
|
-
): MemoryBriefResult {
|
|
42
|
-
// Compile individual sections
|
|
43
|
-
const timeSection = compileTimeBrief(db, scopeId, now);
|
|
44
|
-
|
|
45
|
-
const openLoopResult: OpenLoopBriefResult = compileOpenLoopBrief(
|
|
46
|
-
scopeId,
|
|
47
|
-
userMessageId,
|
|
48
|
-
now,
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
// Convert open-loop bullets to a rendered section via the shared helper
|
|
52
|
-
const openLoopEntries = openLoopResult.bullets.map((b) => ({
|
|
53
|
-
text: b.summary,
|
|
54
|
-
}));
|
|
55
|
-
const openLoopSection = renderBriefSection(
|
|
56
|
-
"Open Loops",
|
|
57
|
-
openLoopEntries,
|
|
58
|
-
MAX_OPEN_LOOP_ENTRIES,
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
// Collect non-empty sections
|
|
62
|
-
const sections: string[] = [];
|
|
63
|
-
if (timeSection) sections.push(timeSection);
|
|
64
|
-
if (openLoopSection) sections.push(openLoopSection);
|
|
65
|
-
|
|
66
|
-
// If no sections have content, return empty
|
|
67
|
-
if (sections.length === 0) {
|
|
68
|
-
return { text: "", resurfacedLoopId: openLoopResult.resurfacedLoopId };
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const body = sections.join("\n\n");
|
|
72
|
-
const text = `<memory_brief>\n${body}\n</memory_brief>`;
|
|
73
|
-
|
|
74
|
-
return { text, resurfacedLoopId: openLoopResult.resurfacedLoopId };
|
|
75
|
-
}
|
|
@@ -1,462 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Backfill job handler: migrates legacy memory rows into the simplified memory
|
|
3
|
-
* system without deleting the old tables.
|
|
4
|
-
*
|
|
5
|
-
* Migration mapping:
|
|
6
|
-
* - `memory_segments` -> `memory_chunks` (via `memory_observations`)
|
|
7
|
-
* - `memory_summaries` -> `memory_episodes`
|
|
8
|
-
* - Active/high-confidence `memory_items` -> `memory_observations`,
|
|
9
|
-
* plus `time_contexts` or `open_loops` when the mapping is unambiguous.
|
|
10
|
-
*
|
|
11
|
-
* The handler is idempotent: content-hash deduplication on chunks and
|
|
12
|
-
* checkpoint tracking prevent double-writes on re-runs.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import { eq } from "drizzle-orm";
|
|
16
|
-
import { v4 as uuid } from "uuid";
|
|
17
|
-
|
|
18
|
-
import { estimateTextTokens } from "../../context/token-estimator.js";
|
|
19
|
-
import { getLogger } from "../../util/logger.js";
|
|
20
|
-
import {
|
|
21
|
-
computeChunkContentHash,
|
|
22
|
-
insertObservation,
|
|
23
|
-
} from "../archive-store.js";
|
|
24
|
-
import { getMemoryCheckpoint, setMemoryCheckpoint } from "../checkpoints.js";
|
|
25
|
-
import { getDb, rawAll } from "../db.js";
|
|
26
|
-
import type { MemoryJob } from "../jobs-store.js";
|
|
27
|
-
import { enqueueMemoryJob } from "../jobs-store.js";
|
|
28
|
-
import {
|
|
29
|
-
conversations,
|
|
30
|
-
memoryChunks,
|
|
31
|
-
memoryEpisodes,
|
|
32
|
-
memoryObservations,
|
|
33
|
-
openLoops,
|
|
34
|
-
timeContexts,
|
|
35
|
-
} from "../schema.js";
|
|
36
|
-
|
|
37
|
-
const log = getLogger("backfill-simplified-memory");
|
|
38
|
-
|
|
39
|
-
/** Checkpoint keys for tracking backfill progress. */
|
|
40
|
-
const CHECKPOINT_SEGMENTS = "simplified_backfill:segments:last_id";
|
|
41
|
-
const CHECKPOINT_SUMMARIES = "simplified_backfill:summaries:last_id";
|
|
42
|
-
const CHECKPOINT_ITEMS = "simplified_backfill:items:last_id";
|
|
43
|
-
const CHECKPOINT_COMPLETE = "simplified_backfill:complete";
|
|
44
|
-
|
|
45
|
-
/** Batch size for each migration pass. */
|
|
46
|
-
const BATCH_SIZE = 200;
|
|
47
|
-
|
|
48
|
-
// ── Legacy row types ──────────────────────────────────────────────────
|
|
49
|
-
|
|
50
|
-
interface LegacySegment {
|
|
51
|
-
id: string;
|
|
52
|
-
message_id: string;
|
|
53
|
-
conversation_id: string;
|
|
54
|
-
role: string;
|
|
55
|
-
text: string;
|
|
56
|
-
token_estimate: number;
|
|
57
|
-
scope_id: string;
|
|
58
|
-
content_hash: string | null;
|
|
59
|
-
created_at: number;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
interface LegacySummary {
|
|
63
|
-
id: string;
|
|
64
|
-
scope: string;
|
|
65
|
-
scope_key: string;
|
|
66
|
-
summary: string;
|
|
67
|
-
token_estimate: number;
|
|
68
|
-
scope_id: string;
|
|
69
|
-
start_at: number;
|
|
70
|
-
end_at: number;
|
|
71
|
-
created_at: number;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
interface LegacyItem {
|
|
75
|
-
id: string;
|
|
76
|
-
kind: string;
|
|
77
|
-
subject: string;
|
|
78
|
-
statement: string;
|
|
79
|
-
status: string;
|
|
80
|
-
confidence: number;
|
|
81
|
-
scope_id: string;
|
|
82
|
-
first_seen_at: number;
|
|
83
|
-
last_seen_at: number;
|
|
84
|
-
valid_from: number | null;
|
|
85
|
-
invalid_at: number | null;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// ── Entry point ───────────────────────────────────────────────────────
|
|
89
|
-
|
|
90
|
-
export async function backfillSimplifiedMemoryJob(
|
|
91
|
-
job: MemoryJob,
|
|
92
|
-
): Promise<void> {
|
|
93
|
-
const force = job.payload.force === true;
|
|
94
|
-
|
|
95
|
-
if (!force) {
|
|
96
|
-
const complete = getMemoryCheckpoint(CHECKPOINT_COMPLETE);
|
|
97
|
-
if (complete === "true") {
|
|
98
|
-
log.debug("Simplified memory backfill already complete, skipping");
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if (force) {
|
|
104
|
-
// Reset all checkpoints so the backfill restarts from scratch
|
|
105
|
-
setMemoryCheckpoint(CHECKPOINT_SEGMENTS, "");
|
|
106
|
-
setMemoryCheckpoint(CHECKPOINT_SUMMARIES, "");
|
|
107
|
-
setMemoryCheckpoint(CHECKPOINT_ITEMS, "");
|
|
108
|
-
setMemoryCheckpoint(CHECKPOINT_COMPLETE, "false");
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
let hasMore = false;
|
|
112
|
-
|
|
113
|
-
// ── Phase 1: memory_segments -> memory_observations + memory_chunks
|
|
114
|
-
hasMore = migrateSegments();
|
|
115
|
-
if (hasMore) {
|
|
116
|
-
enqueueMemoryJob("backfill_simplified_memory", {});
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// ── Phase 2: memory_summaries -> memory_episodes
|
|
121
|
-
hasMore = migrateSummaries();
|
|
122
|
-
if (hasMore) {
|
|
123
|
-
enqueueMemoryJob("backfill_simplified_memory", {});
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// ── Phase 3: active memory_items -> memory_observations (+ brief-state)
|
|
128
|
-
hasMore = migrateItems();
|
|
129
|
-
if (hasMore) {
|
|
130
|
-
enqueueMemoryJob("backfill_simplified_memory", {});
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// All phases complete
|
|
135
|
-
setMemoryCheckpoint(CHECKPOINT_COMPLETE, "true");
|
|
136
|
-
log.info("Simplified memory backfill completed");
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// ── Phase 1: Segments ─────────────────────────────────────────────────
|
|
140
|
-
|
|
141
|
-
function migrateSegments(): boolean {
|
|
142
|
-
const lastId = getMemoryCheckpoint(CHECKPOINT_SEGMENTS) ?? "";
|
|
143
|
-
|
|
144
|
-
const segments = rawAll<LegacySegment>(
|
|
145
|
-
`SELECT id, message_id, conversation_id, role, text, token_estimate,
|
|
146
|
-
scope_id, content_hash, created_at
|
|
147
|
-
FROM memory_segments
|
|
148
|
-
WHERE id > ?
|
|
149
|
-
ORDER BY id ASC
|
|
150
|
-
LIMIT ?`,
|
|
151
|
-
lastId,
|
|
152
|
-
BATCH_SIZE,
|
|
153
|
-
);
|
|
154
|
-
|
|
155
|
-
if (segments.length === 0) return false;
|
|
156
|
-
|
|
157
|
-
for (const seg of segments) {
|
|
158
|
-
try {
|
|
159
|
-
// Insert as an observation — insertObservation handles chunk dedup
|
|
160
|
-
insertObservation({
|
|
161
|
-
conversationId: seg.conversation_id,
|
|
162
|
-
messageId: seg.message_id,
|
|
163
|
-
role: seg.role,
|
|
164
|
-
content: seg.text,
|
|
165
|
-
scopeId: seg.scope_id,
|
|
166
|
-
modality: "text",
|
|
167
|
-
source: "backfill:segment",
|
|
168
|
-
});
|
|
169
|
-
} catch (err) {
|
|
170
|
-
// Log and continue — individual failures should not block the batch
|
|
171
|
-
log.warn(
|
|
172
|
-
{ err, segmentId: seg.id },
|
|
173
|
-
"Failed to migrate segment, skipping",
|
|
174
|
-
);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const lastSegment = segments[segments.length - 1];
|
|
179
|
-
setMemoryCheckpoint(CHECKPOINT_SEGMENTS, lastSegment.id);
|
|
180
|
-
|
|
181
|
-
log.debug(
|
|
182
|
-
{ migrated: segments.length, lastId: lastSegment.id },
|
|
183
|
-
"Migrated segment batch",
|
|
184
|
-
);
|
|
185
|
-
|
|
186
|
-
return segments.length === BATCH_SIZE;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// ── Phase 2: Summaries ────────────────────────────────────────────────
|
|
190
|
-
|
|
191
|
-
function migrateSummaries(): boolean {
|
|
192
|
-
const lastId = getMemoryCheckpoint(CHECKPOINT_SUMMARIES) ?? "";
|
|
193
|
-
|
|
194
|
-
const summaries = rawAll<LegacySummary>(
|
|
195
|
-
`SELECT id, scope, scope_key, summary, token_estimate, scope_id,
|
|
196
|
-
start_at, end_at, created_at
|
|
197
|
-
FROM memory_summaries
|
|
198
|
-
WHERE id > ?
|
|
199
|
-
ORDER BY id ASC
|
|
200
|
-
LIMIT ?`,
|
|
201
|
-
lastId,
|
|
202
|
-
BATCH_SIZE,
|
|
203
|
-
);
|
|
204
|
-
|
|
205
|
-
if (summaries.length === 0) return false;
|
|
206
|
-
|
|
207
|
-
const db = getDb();
|
|
208
|
-
const now = Date.now();
|
|
209
|
-
|
|
210
|
-
for (const sum of summaries) {
|
|
211
|
-
try {
|
|
212
|
-
// Derive a conversation ID from the scope_key if it looks like a conversation summary.
|
|
213
|
-
// scope_key format: "conversation:<conversationId>" or "<scope>:<key>"
|
|
214
|
-
const conversationId = extractConversationId(sum.scope, sum.scope_key);
|
|
215
|
-
if (!conversationId) {
|
|
216
|
-
log.debug(
|
|
217
|
-
{ summaryId: sum.id, scope: sum.scope, scopeKey: sum.scope_key },
|
|
218
|
-
"Skipping non-conversation summary",
|
|
219
|
-
);
|
|
220
|
-
continue;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
const episodeId = uuid();
|
|
224
|
-
const title = buildEpisodeTitle(sum.scope, sum.scope_key);
|
|
225
|
-
|
|
226
|
-
db.insert(memoryEpisodes)
|
|
227
|
-
.values({
|
|
228
|
-
id: episodeId,
|
|
229
|
-
scopeId: sum.scope_id,
|
|
230
|
-
conversationId,
|
|
231
|
-
title,
|
|
232
|
-
summary: sum.summary,
|
|
233
|
-
tokenEstimate: sum.token_estimate,
|
|
234
|
-
source: "backfill:summary",
|
|
235
|
-
startAt: sum.start_at,
|
|
236
|
-
endAt: sum.end_at,
|
|
237
|
-
createdAt: now,
|
|
238
|
-
updatedAt: now,
|
|
239
|
-
})
|
|
240
|
-
.onConflictDoNothing()
|
|
241
|
-
.run();
|
|
242
|
-
|
|
243
|
-
// Enqueue embedding for the new episode
|
|
244
|
-
enqueueMemoryJob("embed_episode", { episodeId });
|
|
245
|
-
} catch (err) {
|
|
246
|
-
log.warn(
|
|
247
|
-
{ err, summaryId: sum.id },
|
|
248
|
-
"Failed to migrate summary, skipping",
|
|
249
|
-
);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
const lastSummary = summaries[summaries.length - 1];
|
|
254
|
-
setMemoryCheckpoint(CHECKPOINT_SUMMARIES, lastSummary.id);
|
|
255
|
-
|
|
256
|
-
log.debug(
|
|
257
|
-
{ migrated: summaries.length, lastId: lastSummary.id },
|
|
258
|
-
"Migrated summary batch",
|
|
259
|
-
);
|
|
260
|
-
|
|
261
|
-
return summaries.length === BATCH_SIZE;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// ── Phase 3: Items ────────────────────────────────────────────────────
|
|
265
|
-
|
|
266
|
-
/** Sentinel conversation ID for legacy items that have no conversation linkage. */
|
|
267
|
-
const LEGACY_SENTINEL_CONVERSATION_ID = "__legacy_backfill__";
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* Ensure the legacy sentinel conversation row exists. This is needed because
|
|
271
|
-
* memory_observations has a FK constraint on conversation_id.
|
|
272
|
-
*/
|
|
273
|
-
function ensureLegacySentinelConversation(): void {
|
|
274
|
-
const db = getDb();
|
|
275
|
-
const existing = db
|
|
276
|
-
.select({ id: conversations.id })
|
|
277
|
-
.from(conversations)
|
|
278
|
-
.where(eq(conversations.id, LEGACY_SENTINEL_CONVERSATION_ID))
|
|
279
|
-
.get();
|
|
280
|
-
if (existing) return;
|
|
281
|
-
|
|
282
|
-
const now = Date.now();
|
|
283
|
-
db.insert(conversations)
|
|
284
|
-
.values({
|
|
285
|
-
id: LEGACY_SENTINEL_CONVERSATION_ID,
|
|
286
|
-
title: "[Legacy Memory Backfill]",
|
|
287
|
-
createdAt: now,
|
|
288
|
-
updatedAt: now,
|
|
289
|
-
})
|
|
290
|
-
.run();
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
function migrateItems(): boolean {
|
|
294
|
-
const lastId = getMemoryCheckpoint(CHECKPOINT_ITEMS) ?? "";
|
|
295
|
-
|
|
296
|
-
const items = rawAll<LegacyItem>(
|
|
297
|
-
`SELECT id, kind, subject, statement, status, confidence, scope_id,
|
|
298
|
-
first_seen_at, last_seen_at, valid_from, invalid_at
|
|
299
|
-
FROM memory_items
|
|
300
|
-
WHERE id > ?
|
|
301
|
-
AND status = 'active'
|
|
302
|
-
AND confidence >= 0.5
|
|
303
|
-
AND invalid_at IS NULL
|
|
304
|
-
ORDER BY id ASC
|
|
305
|
-
LIMIT ?`,
|
|
306
|
-
lastId,
|
|
307
|
-
BATCH_SIZE,
|
|
308
|
-
);
|
|
309
|
-
|
|
310
|
-
if (items.length === 0) return false;
|
|
311
|
-
|
|
312
|
-
// Ensure the sentinel conversation exists for items without conversation linkage
|
|
313
|
-
ensureLegacySentinelConversation();
|
|
314
|
-
|
|
315
|
-
const db = getDb();
|
|
316
|
-
const now = Date.now();
|
|
317
|
-
|
|
318
|
-
for (const item of items) {
|
|
319
|
-
try {
|
|
320
|
-
// Every active item becomes an observation
|
|
321
|
-
const observationId = uuid();
|
|
322
|
-
const observationContent = `[${item.kind}] ${item.subject}: ${item.statement}`;
|
|
323
|
-
|
|
324
|
-
db.insert(memoryObservations)
|
|
325
|
-
.values({
|
|
326
|
-
id: observationId,
|
|
327
|
-
scopeId: item.scope_id,
|
|
328
|
-
conversationId: LEGACY_SENTINEL_CONVERSATION_ID,
|
|
329
|
-
role: "user",
|
|
330
|
-
content: observationContent,
|
|
331
|
-
modality: "text",
|
|
332
|
-
source: "backfill:item",
|
|
333
|
-
createdAt: now,
|
|
334
|
-
})
|
|
335
|
-
.run();
|
|
336
|
-
|
|
337
|
-
// Create a chunk for the observation (with dedup)
|
|
338
|
-
const contentHash = computeChunkContentHash(
|
|
339
|
-
item.scope_id,
|
|
340
|
-
observationContent,
|
|
341
|
-
);
|
|
342
|
-
const chunkId = uuid();
|
|
343
|
-
const tokenEstimate = estimateTextTokens(observationContent);
|
|
344
|
-
|
|
345
|
-
db.insert(memoryChunks)
|
|
346
|
-
.values({
|
|
347
|
-
id: chunkId,
|
|
348
|
-
scopeId: item.scope_id,
|
|
349
|
-
observationId,
|
|
350
|
-
content: observationContent,
|
|
351
|
-
tokenEstimate,
|
|
352
|
-
contentHash,
|
|
353
|
-
createdAt: now,
|
|
354
|
-
})
|
|
355
|
-
.onConflictDoNothing({
|
|
356
|
-
target: [memoryChunks.scopeId, memoryChunks.contentHash],
|
|
357
|
-
})
|
|
358
|
-
.run();
|
|
359
|
-
|
|
360
|
-
// Enqueue embedding for the observation's chunk
|
|
361
|
-
enqueueMemoryJob("embed_chunk", { chunkId, scopeId: item.scope_id });
|
|
362
|
-
|
|
363
|
-
// ── Brief-state: map unambiguous items to time_contexts or open_loops
|
|
364
|
-
mapItemToBriefState(item, now);
|
|
365
|
-
} catch (err) {
|
|
366
|
-
log.warn({ err, itemId: item.id }, "Failed to migrate item, skipping");
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
const lastItem = items[items.length - 1];
|
|
371
|
-
setMemoryCheckpoint(CHECKPOINT_ITEMS, lastItem.id);
|
|
372
|
-
|
|
373
|
-
log.debug(
|
|
374
|
-
{ migrated: items.length, lastId: lastItem.id },
|
|
375
|
-
"Migrated item batch",
|
|
376
|
-
);
|
|
377
|
-
|
|
378
|
-
return items.length === BATCH_SIZE;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
// ── Brief-state mapping ───────────────────────────────────────────────
|
|
382
|
-
|
|
383
|
-
/**
|
|
384
|
-
* Map a legacy memory item to `time_contexts` or `open_loops` when the
|
|
385
|
-
* mapping is unambiguous.
|
|
386
|
-
*
|
|
387
|
-
* - Items with `valid_from` and a future `invalid_at` -> time_context
|
|
388
|
-
* - `event` kind items with future timestamps -> open_loop
|
|
389
|
-
*/
|
|
390
|
-
function mapItemToBriefState(item: LegacyItem, now: number): void {
|
|
391
|
-
const db = getDb();
|
|
392
|
-
|
|
393
|
-
// Time-bounded items -> time_contexts
|
|
394
|
-
if (
|
|
395
|
-
item.valid_from != null &&
|
|
396
|
-
item.invalid_at != null &&
|
|
397
|
-
item.invalid_at > now
|
|
398
|
-
) {
|
|
399
|
-
db.insert(timeContexts)
|
|
400
|
-
.values({
|
|
401
|
-
id: uuid(),
|
|
402
|
-
scopeId: item.scope_id,
|
|
403
|
-
summary: `${item.subject}: ${item.statement}`,
|
|
404
|
-
source: "backfill:item",
|
|
405
|
-
activeFrom: item.valid_from,
|
|
406
|
-
activeUntil: item.invalid_at,
|
|
407
|
-
createdAt: now,
|
|
408
|
-
updatedAt: now,
|
|
409
|
-
})
|
|
410
|
-
.run();
|
|
411
|
-
return;
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
// Event items with future last_seen_at -> open_loops
|
|
415
|
-
if (item.kind === "event" && item.last_seen_at > now) {
|
|
416
|
-
db.insert(openLoops)
|
|
417
|
-
.values({
|
|
418
|
-
id: uuid(),
|
|
419
|
-
scopeId: item.scope_id,
|
|
420
|
-
summary: `${item.subject}: ${item.statement}`,
|
|
421
|
-
source: "backfill:item",
|
|
422
|
-
status: "open",
|
|
423
|
-
dueAt: item.last_seen_at,
|
|
424
|
-
createdAt: now,
|
|
425
|
-
updatedAt: now,
|
|
426
|
-
})
|
|
427
|
-
.run();
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
// ── Helpers ───────────────────────────────────────────────────────────
|
|
432
|
-
|
|
433
|
-
/**
|
|
434
|
-
* Extract a conversation ID from the summary's scope and scope_key.
|
|
435
|
-
* Returns null for non-conversation summaries.
|
|
436
|
-
*/
|
|
437
|
-
function extractConversationId(scope: string, scopeKey: string): string | null {
|
|
438
|
-
// Conversation summaries use scope "conversation" with scope_key as the ID
|
|
439
|
-
if (scope === "conversation") return scopeKey;
|
|
440
|
-
|
|
441
|
-
// Some summaries use "conversation:<id>" as scope_key
|
|
442
|
-
const match = scopeKey.match(/^conversation:(.+)$/);
|
|
443
|
-
if (match) return match[1];
|
|
444
|
-
|
|
445
|
-
return null;
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
/**
|
|
449
|
-
* Build a human-readable episode title from the summary's scope metadata.
|
|
450
|
-
*/
|
|
451
|
-
function buildEpisodeTitle(scope: string, scopeKey: string): string {
|
|
452
|
-
if (scope === "conversation") {
|
|
453
|
-
return `Conversation summary`;
|
|
454
|
-
}
|
|
455
|
-
if (scope === "weekly") {
|
|
456
|
-
return `Weekly summary (${scopeKey})`;
|
|
457
|
-
}
|
|
458
|
-
if (scope === "monthly") {
|
|
459
|
-
return `Monthly summary (${scopeKey})`;
|
|
460
|
-
}
|
|
461
|
-
return `${scope} summary`;
|
|
462
|
-
}
|