@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
|
@@ -71,3 +71,77 @@ export function migrateEmbeddingVectorBlob(database: DrizzleDb): void {
|
|
|
71
71
|
.run(checkpointKey, Date.now());
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Drop the vector_blob column from memory_embeddings.
|
|
77
|
+
*
|
|
78
|
+
* NOTE: Binary embedding data stored in vector_blob is lost on rollback.
|
|
79
|
+
* Rows that still have vector_json will continue to work; rows that only
|
|
80
|
+
* had vector_blob will lose their embedding vectors.
|
|
81
|
+
*
|
|
82
|
+
* SQLite does not support DROP COLUMN on all versions, so we rebuild the table.
|
|
83
|
+
*/
|
|
84
|
+
export function downEmbeddingVectorBlob(database: DrizzleDb): void {
|
|
85
|
+
const raw = getSqliteFrom(database);
|
|
86
|
+
|
|
87
|
+
// Check if vector_blob column exists
|
|
88
|
+
const hasColumn = raw
|
|
89
|
+
.query(
|
|
90
|
+
`SELECT 1 FROM pragma_table_info('memory_embeddings') WHERE name = 'vector_blob'`,
|
|
91
|
+
)
|
|
92
|
+
.get();
|
|
93
|
+
if (!hasColumn) return;
|
|
94
|
+
|
|
95
|
+
raw.exec("PRAGMA foreign_keys = OFF");
|
|
96
|
+
try {
|
|
97
|
+
raw.exec("BEGIN");
|
|
98
|
+
|
|
99
|
+
// Get the current columns minus vector_blob
|
|
100
|
+
const columns = raw
|
|
101
|
+
.query(`SELECT name FROM pragma_table_info('memory_embeddings')`)
|
|
102
|
+
.all() as Array<{ name: string }>;
|
|
103
|
+
const keepColumns = columns
|
|
104
|
+
.map((c) => c.name)
|
|
105
|
+
.filter((n) => n !== "vector_blob");
|
|
106
|
+
|
|
107
|
+
// Get the current DDL to understand the table structure
|
|
108
|
+
const ddl = raw
|
|
109
|
+
.query(
|
|
110
|
+
`SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'memory_embeddings'`,
|
|
111
|
+
)
|
|
112
|
+
.get() as { sql: string } | null;
|
|
113
|
+
if (!ddl) {
|
|
114
|
+
raw.exec("ROLLBACK");
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Remove the vector_blob column definition from the DDL
|
|
119
|
+
const newDdl = ddl.sql
|
|
120
|
+
.replace(/,\s*vector_blob\s+BLOB/i, "")
|
|
121
|
+
.replace("memory_embeddings", "memory_embeddings_new");
|
|
122
|
+
|
|
123
|
+
raw.exec(newDdl);
|
|
124
|
+
|
|
125
|
+
const colList = keepColumns.join(", ");
|
|
126
|
+
raw.exec(/*sql*/ `
|
|
127
|
+
INSERT INTO memory_embeddings_new (${colList})
|
|
128
|
+
SELECT ${colList} FROM memory_embeddings
|
|
129
|
+
`);
|
|
130
|
+
|
|
131
|
+
raw.exec(/*sql*/ `DROP TABLE memory_embeddings`);
|
|
132
|
+
raw.exec(
|
|
133
|
+
/*sql*/ `ALTER TABLE memory_embeddings_new RENAME TO memory_embeddings`,
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
raw.exec("COMMIT");
|
|
137
|
+
} catch (e) {
|
|
138
|
+
try {
|
|
139
|
+
raw.exec("ROLLBACK");
|
|
140
|
+
} catch {
|
|
141
|
+
/* no active transaction */
|
|
142
|
+
}
|
|
143
|
+
throw e;
|
|
144
|
+
} finally {
|
|
145
|
+
raw.exec("PRAGMA foreign_keys = ON");
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -105,6 +105,88 @@ export function migrateEmbeddingsNullableVectorJson(database: DrizzleDb): void {
|
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
+
/**
|
|
109
|
+
* Reverse v13: rebuild memory_embeddings with NOT NULL on vector_json.
|
|
110
|
+
*
|
|
111
|
+
* WARNING: Any rows with NULL vector_json will be lost — they cannot satisfy
|
|
112
|
+
* the NOT NULL constraint. This is acceptable because the forward migration
|
|
113
|
+
* only relaxed the constraint; rows written after the forward migration may
|
|
114
|
+
* have NULL vector_json (relying on vector_blob instead).
|
|
115
|
+
*/
|
|
116
|
+
export function downEmbeddingsNullableVectorJson(database: DrizzleDb): void {
|
|
117
|
+
const raw = getSqliteFrom(database);
|
|
118
|
+
|
|
119
|
+
const tableExists = raw
|
|
120
|
+
.query(
|
|
121
|
+
`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'memory_embeddings'`,
|
|
122
|
+
)
|
|
123
|
+
.get();
|
|
124
|
+
if (!tableExists) return;
|
|
125
|
+
|
|
126
|
+
// Check if vector_json already has NOT NULL — already rolled back
|
|
127
|
+
const ddl = raw
|
|
128
|
+
.query(
|
|
129
|
+
`SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'memory_embeddings'`,
|
|
130
|
+
)
|
|
131
|
+
.get() as { sql: string } | null;
|
|
132
|
+
if (ddl && isColumnNotNull(ddl.sql, "vector_json")) return;
|
|
133
|
+
|
|
134
|
+
raw.exec("PRAGMA foreign_keys = OFF");
|
|
135
|
+
try {
|
|
136
|
+
raw.exec("BEGIN");
|
|
137
|
+
|
|
138
|
+
raw.exec(/*sql*/ `
|
|
139
|
+
CREATE TABLE memory_embeddings_new (
|
|
140
|
+
id TEXT PRIMARY KEY,
|
|
141
|
+
target_type TEXT NOT NULL,
|
|
142
|
+
target_id TEXT NOT NULL,
|
|
143
|
+
provider TEXT NOT NULL,
|
|
144
|
+
model TEXT NOT NULL,
|
|
145
|
+
dimensions INTEGER NOT NULL,
|
|
146
|
+
vector_json TEXT NOT NULL,
|
|
147
|
+
vector_blob BLOB,
|
|
148
|
+
content_hash TEXT,
|
|
149
|
+
created_at INTEGER NOT NULL,
|
|
150
|
+
updated_at INTEGER NOT NULL,
|
|
151
|
+
UNIQUE (target_type, target_id, provider, model)
|
|
152
|
+
)
|
|
153
|
+
`);
|
|
154
|
+
// Only copy rows where vector_json is NOT NULL — rows with NULL cannot
|
|
155
|
+
// satisfy the restored constraint and are lost.
|
|
156
|
+
raw.exec(/*sql*/ `
|
|
157
|
+
INSERT OR IGNORE INTO memory_embeddings_new (
|
|
158
|
+
id, target_type, target_id, provider, model, dimensions,
|
|
159
|
+
vector_json, vector_blob, content_hash, created_at, updated_at
|
|
160
|
+
)
|
|
161
|
+
SELECT
|
|
162
|
+
id, target_type, target_id, provider, model, dimensions,
|
|
163
|
+
vector_json, vector_blob, content_hash, created_at, updated_at
|
|
164
|
+
FROM memory_embeddings
|
|
165
|
+
WHERE vector_json IS NOT NULL
|
|
166
|
+
ORDER BY updated_at DESC
|
|
167
|
+
`);
|
|
168
|
+
raw.exec(/*sql*/ `DROP TABLE memory_embeddings`);
|
|
169
|
+
raw.exec(
|
|
170
|
+
/*sql*/ `ALTER TABLE memory_embeddings_new RENAME TO memory_embeddings`,
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
raw.exec(
|
|
174
|
+
/*sql*/ `CREATE INDEX IF NOT EXISTS idx_memory_embeddings_content_hash ON memory_embeddings(content_hash, provider, model)`,
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
raw.exec("COMMIT");
|
|
178
|
+
} catch (e) {
|
|
179
|
+
try {
|
|
180
|
+
raw.exec("ROLLBACK");
|
|
181
|
+
} catch {
|
|
182
|
+
/* no active transaction */
|
|
183
|
+
}
|
|
184
|
+
throw e;
|
|
185
|
+
} finally {
|
|
186
|
+
raw.exec("PRAGMA foreign_keys = ON");
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
108
190
|
/** Check whether a column is declared NOT NULL in a CREATE TABLE DDL string. */
|
|
109
191
|
function isColumnNotNull(ddl: string, column: string): boolean {
|
|
110
192
|
const pattern = new RegExp(`${column}\\s+\\w+.*?NOT\\s+NULL`, "i");
|
|
@@ -336,3 +336,14 @@ export function migrateNormalizePhoneIdentities(database: DrizzleDb): void {
|
|
|
336
336
|
throw e;
|
|
337
337
|
}
|
|
338
338
|
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Reverse v14: no-op — original non-E.164 phone formats are not recoverable.
|
|
342
|
+
*
|
|
343
|
+
* The forward migration normalised phone numbers to E.164. The original
|
|
344
|
+
* formatting (parentheses, dashes, spaces, country-code variants) was
|
|
345
|
+
* discarded during normalisation and cannot be reconstructed.
|
|
346
|
+
*/
|
|
347
|
+
export function downNormalizePhoneIdentities(_database: DrizzleDb): void {
|
|
348
|
+
// Lossy — original phone formats are not recoverable.
|
|
349
|
+
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { getLogger } from "../../util/logger.js";
|
|
2
|
+
import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
|
|
3
|
+
|
|
4
|
+
const logger = getLogger("messages-fts");
|
|
2
5
|
|
|
3
6
|
/**
|
|
4
7
|
* FTS5 virtual table for full-text search over messages.content.
|
|
@@ -21,7 +24,81 @@ import type { DrizzleDb } from "../db-connection.js";
|
|
|
21
24
|
* ALL writes to the messages table to fail until the FTS table is rebuilt.
|
|
22
25
|
* If this happens, `messages_fts` should be dropped and recreated, then
|
|
23
26
|
* backfilled via `migrateMessagesFtsBackfill`.
|
|
27
|
+
*
|
|
28
|
+
* ## Auto-recovery from corruption
|
|
29
|
+
*
|
|
30
|
+
* After creating (or finding an existing) messages_fts table, we probe it
|
|
31
|
+
* with a lightweight MATCH query that exercises the FTS inverted index
|
|
32
|
+
* in O(1). If the probe throws SQLITE_CORRUPT_VTAB or SQLITE_CORRUPT,
|
|
33
|
+
* we force-remove all shadow tables and the vtable entry (falling back
|
|
34
|
+
* to `PRAGMA writable_schema` if DROP TABLE itself fails on the corrupt
|
|
35
|
+
* vtable) and recreate it from scratch. The subsequent
|
|
36
|
+
* `migrateMessagesFtsBackfill` call in db-init.ts will repopulate the
|
|
37
|
+
* index from the messages table — no message data is lost.
|
|
38
|
+
*/
|
|
39
|
+
function isSqliteCorruptionError(err: unknown): boolean {
|
|
40
|
+
const code =
|
|
41
|
+
err != null && typeof err === "object" && "code" in err
|
|
42
|
+
? (err as { code: string }).code
|
|
43
|
+
: undefined;
|
|
44
|
+
return code === "SQLITE_CORRUPT_VTAB" || code === "SQLITE_CORRUPT";
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Force-remove all FTS5 shadow tables, triggers, and the vtable entry.
|
|
49
|
+
*
|
|
50
|
+
* We drop each artifact individually so that a corrupt shadow table
|
|
51
|
+
* doesn't block cleanup of the others. If `DROP TABLE messages_fts`
|
|
52
|
+
* itself fails (FTS5's xDestroy hits a corrupt shadow table), we fall
|
|
53
|
+
* back to `PRAGMA writable_schema` to delete the vtable entry directly
|
|
54
|
+
* from `sqlite_schema`. Without this fallback, `CREATE VIRTUAL TABLE
|
|
55
|
+
* IF NOT EXISTS` would be a no-op and the crash loop would persist.
|
|
24
56
|
*/
|
|
57
|
+
function dropFtsShadowTables(raw: ReturnType<typeof getSqliteFrom>): void {
|
|
58
|
+
const drops = [
|
|
59
|
+
`DROP TRIGGER IF EXISTS messages_fts_ai`,
|
|
60
|
+
`DROP TRIGGER IF EXISTS messages_fts_ad`,
|
|
61
|
+
`DROP TRIGGER IF EXISTS messages_fts_au`,
|
|
62
|
+
`DROP TABLE IF EXISTS messages_fts_config`,
|
|
63
|
+
`DROP TABLE IF EXISTS messages_fts_docsize`,
|
|
64
|
+
`DROP TABLE IF EXISTS messages_fts_content`,
|
|
65
|
+
`DROP TABLE IF EXISTS messages_fts_idx`,
|
|
66
|
+
`DROP TABLE IF EXISTS messages_fts_data`,
|
|
67
|
+
];
|
|
68
|
+
for (const sql of drops) {
|
|
69
|
+
try {
|
|
70
|
+
raw.exec(sql);
|
|
71
|
+
} catch {
|
|
72
|
+
// Shadow table may itself be corrupt — ignore and continue
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Try the normal DROP TABLE path first (lets FTS5 clean up properly).
|
|
77
|
+
try {
|
|
78
|
+
raw.exec(`DROP TABLE IF EXISTS messages_fts`);
|
|
79
|
+
} catch {
|
|
80
|
+
// FTS5's xDestroy failed — force-remove the vtable entry from
|
|
81
|
+
// sqlite_schema so CREATE VIRTUAL TABLE isn't a no-op.
|
|
82
|
+
logger.warn(
|
|
83
|
+
"[messages-fts] DROP TABLE messages_fts failed — removing vtable entry via writable_schema",
|
|
84
|
+
);
|
|
85
|
+
raw.exec(`PRAGMA writable_schema = ON`);
|
|
86
|
+
try {
|
|
87
|
+
raw.exec(
|
|
88
|
+
`DELETE FROM sqlite_schema WHERE type = 'table' AND name = 'messages_fts'`,
|
|
89
|
+
);
|
|
90
|
+
} catch (schemaErr) {
|
|
91
|
+
logger.error(
|
|
92
|
+
{ err: schemaErr },
|
|
93
|
+
"[messages-fts] Failed to remove vtable entry from sqlite_schema",
|
|
94
|
+
);
|
|
95
|
+
throw schemaErr;
|
|
96
|
+
} finally {
|
|
97
|
+
raw.exec(`PRAGMA writable_schema = OFF`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
25
102
|
export function createMessagesFts(database: DrizzleDb): void {
|
|
26
103
|
database.run(/*sql*/ `
|
|
27
104
|
CREATE VIRTUAL TABLE IF NOT EXISTS messages_fts USING fts5(
|
|
@@ -30,6 +107,34 @@ export function createMessagesFts(database: DrizzleDb): void {
|
|
|
30
107
|
)
|
|
31
108
|
`);
|
|
32
109
|
|
|
110
|
+
// Probe the FTS inverted index for corruption. A MATCH query exercises
|
|
111
|
+
// the index structures (not just the content store), so it catches
|
|
112
|
+
// corruption in shadow tables like _idx and _data. On empty tables
|
|
113
|
+
// this returns null gracefully. O(1) with LIMIT 1.
|
|
114
|
+
const raw = getSqliteFrom(database);
|
|
115
|
+
try {
|
|
116
|
+
raw
|
|
117
|
+
.query(`SELECT * FROM messages_fts WHERE messages_fts MATCH 'a' LIMIT 1`)
|
|
118
|
+
.get();
|
|
119
|
+
} catch (err: unknown) {
|
|
120
|
+
if (!isSqliteCorruptionError(err)) {
|
|
121
|
+
throw err;
|
|
122
|
+
}
|
|
123
|
+
logger.warn(
|
|
124
|
+
{ err },
|
|
125
|
+
"[messages-fts] Detected corrupt messages_fts virtual table — dropping and recreating",
|
|
126
|
+
);
|
|
127
|
+
// DROP TABLE on a corrupt vtable can itself throw, so drop the
|
|
128
|
+
// FTS5 shadow tables directly to guarantee cleanup.
|
|
129
|
+
dropFtsShadowTables(raw);
|
|
130
|
+
database.run(/*sql*/ `
|
|
131
|
+
CREATE VIRTUAL TABLE messages_fts USING fts5(
|
|
132
|
+
message_id UNINDEXED,
|
|
133
|
+
content
|
|
134
|
+
)
|
|
135
|
+
`);
|
|
136
|
+
}
|
|
137
|
+
|
|
33
138
|
database.run(/*sql*/ `
|
|
34
139
|
CREATE TRIGGER IF NOT EXISTS messages_fts_ai
|
|
35
140
|
AFTER INSERT ON messages
|
|
@@ -1,6 +1,58 @@
|
|
|
1
1
|
import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
|
|
2
2
|
import { withCrashRecovery } from "./validate-migration-state.js";
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Reverse v15: set guardian_principal_id back to NULL on all rows in
|
|
6
|
+
* channel_guardian_bindings and canonical_guardian_requests.
|
|
7
|
+
*
|
|
8
|
+
* Also un-expires requests that the forward migration expired (sets them
|
|
9
|
+
* back to 'pending'). This is a best-effort reversal — the original status
|
|
10
|
+
* of expired requests cannot be perfectly reconstructed if they were already
|
|
11
|
+
* expired before the forward migration ran, but the forward migration only
|
|
12
|
+
* expired requests that had NULL guardian_principal_id and status = 'pending'.
|
|
13
|
+
*/
|
|
14
|
+
export function downBackfillGuardianPrincipalId(database: DrizzleDb): void {
|
|
15
|
+
const raw = getSqliteFrom(database);
|
|
16
|
+
|
|
17
|
+
// Null out guardian_principal_id on channel_guardian_bindings
|
|
18
|
+
const bindingsExists = raw
|
|
19
|
+
.query(
|
|
20
|
+
`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'channel_guardian_bindings'`,
|
|
21
|
+
)
|
|
22
|
+
.get();
|
|
23
|
+
if (bindingsExists) {
|
|
24
|
+
const colExists = raw
|
|
25
|
+
.query(
|
|
26
|
+
`SELECT 1 FROM pragma_table_info('channel_guardian_bindings') WHERE name = 'guardian_principal_id'`,
|
|
27
|
+
)
|
|
28
|
+
.get();
|
|
29
|
+
if (colExists) {
|
|
30
|
+
raw.exec(
|
|
31
|
+
/*sql*/ `UPDATE channel_guardian_bindings SET guardian_principal_id = NULL`,
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Null out guardian_principal_id on canonical_guardian_requests
|
|
37
|
+
const requestsExists = raw
|
|
38
|
+
.query(
|
|
39
|
+
`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'canonical_guardian_requests'`,
|
|
40
|
+
)
|
|
41
|
+
.get();
|
|
42
|
+
if (requestsExists) {
|
|
43
|
+
const colExists = raw
|
|
44
|
+
.query(
|
|
45
|
+
`SELECT 1 FROM pragma_table_info('canonical_guardian_requests') WHERE name = 'guardian_principal_id'`,
|
|
46
|
+
)
|
|
47
|
+
.get();
|
|
48
|
+
if (colExists) {
|
|
49
|
+
raw.exec(
|
|
50
|
+
/*sql*/ `UPDATE canonical_guardian_requests SET guardian_principal_id = NULL`,
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
4
56
|
/**
|
|
5
57
|
* Backfill guardianPrincipalId for existing channel_guardian_bindings and
|
|
6
58
|
* canonical_guardian_requests rows.
|
|
@@ -1,6 +1,83 @@
|
|
|
1
1
|
import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
|
|
2
2
|
import { withCrashRecovery } from "./validate-migration-state.js";
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Reverse v16: rebuild channel_guardian_bindings to make guardian_principal_id
|
|
6
|
+
* nullable again (removing the NOT NULL constraint added by the forward migration).
|
|
7
|
+
*/
|
|
8
|
+
export function downGuardianPrincipalIdNotNull(database: DrizzleDb): void {
|
|
9
|
+
const raw = getSqliteFrom(database);
|
|
10
|
+
|
|
11
|
+
const tableExists = raw
|
|
12
|
+
.query(
|
|
13
|
+
`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'channel_guardian_bindings'`,
|
|
14
|
+
)
|
|
15
|
+
.get();
|
|
16
|
+
if (!tableExists) return;
|
|
17
|
+
|
|
18
|
+
// Check if guardian_principal_id has NOT NULL — if not, already rolled back
|
|
19
|
+
const colInfo = raw
|
|
20
|
+
.query(
|
|
21
|
+
`SELECT "notnull" FROM pragma_table_info('channel_guardian_bindings') WHERE name = 'guardian_principal_id'`,
|
|
22
|
+
)
|
|
23
|
+
.get() as { notnull: number } | null;
|
|
24
|
+
if (!colInfo || colInfo.notnull === 0) return;
|
|
25
|
+
|
|
26
|
+
raw.exec("PRAGMA foreign_keys = OFF");
|
|
27
|
+
try {
|
|
28
|
+
raw.exec("BEGIN");
|
|
29
|
+
|
|
30
|
+
raw.exec(/*sql*/ `
|
|
31
|
+
CREATE TABLE channel_guardian_bindings_new (
|
|
32
|
+
id TEXT PRIMARY KEY,
|
|
33
|
+
assistant_id TEXT NOT NULL,
|
|
34
|
+
channel TEXT NOT NULL,
|
|
35
|
+
guardian_external_user_id TEXT NOT NULL,
|
|
36
|
+
guardian_delivery_chat_id TEXT NOT NULL,
|
|
37
|
+
guardian_principal_id TEXT,
|
|
38
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
39
|
+
verified_at INTEGER NOT NULL,
|
|
40
|
+
verified_via TEXT NOT NULL DEFAULT 'challenge',
|
|
41
|
+
metadata_json TEXT,
|
|
42
|
+
created_at INTEGER NOT NULL,
|
|
43
|
+
updated_at INTEGER NOT NULL
|
|
44
|
+
)
|
|
45
|
+
`);
|
|
46
|
+
|
|
47
|
+
raw.exec(/*sql*/ `
|
|
48
|
+
INSERT INTO channel_guardian_bindings_new
|
|
49
|
+
SELECT id, assistant_id, channel, guardian_external_user_id,
|
|
50
|
+
guardian_delivery_chat_id, guardian_principal_id,
|
|
51
|
+
status, verified_at, verified_via, metadata_json,
|
|
52
|
+
created_at, updated_at
|
|
53
|
+
FROM channel_guardian_bindings
|
|
54
|
+
`);
|
|
55
|
+
|
|
56
|
+
raw.exec(/*sql*/ `DROP TABLE channel_guardian_bindings`);
|
|
57
|
+
raw.exec(
|
|
58
|
+
/*sql*/ `ALTER TABLE channel_guardian_bindings_new RENAME TO channel_guardian_bindings`,
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
// Recreate the unique index for active bindings
|
|
62
|
+
raw.exec(/*sql*/ `
|
|
63
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_channel_guardian_bindings_active
|
|
64
|
+
ON channel_guardian_bindings(assistant_id, channel)
|
|
65
|
+
WHERE status = 'active'
|
|
66
|
+
`);
|
|
67
|
+
|
|
68
|
+
raw.exec("COMMIT");
|
|
69
|
+
} catch (e) {
|
|
70
|
+
try {
|
|
71
|
+
raw.exec("ROLLBACK");
|
|
72
|
+
} catch {
|
|
73
|
+
/* no active transaction */
|
|
74
|
+
}
|
|
75
|
+
throw e;
|
|
76
|
+
} finally {
|
|
77
|
+
raw.exec("PRAGMA foreign_keys = ON");
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
4
81
|
/**
|
|
5
82
|
* Enforce NOT NULL on channel_guardian_bindings.guardian_principal_id.
|
|
6
83
|
*
|
|
@@ -2,6 +2,19 @@ import { getLogger } from "../../util/logger.js";
|
|
|
2
2
|
import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
|
|
3
3
|
import { withCrashRecovery } from "./validate-migration-state.js";
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Reverse v17: no-op — the original separate columns (relationship, importance,
|
|
7
|
+
* response_expectation, preferred_tone) cannot be reliably restored from the
|
|
8
|
+
* consolidated notes TEXT column.
|
|
9
|
+
*
|
|
10
|
+
* The forward migration concatenated multiple typed fields into a single
|
|
11
|
+
* free-text notes field and then dropped the original columns. Parsing the
|
|
12
|
+
* notes back into structured fields would be lossy and error-prone.
|
|
13
|
+
*/
|
|
14
|
+
export function downContactsNotesColumn(_database: DrizzleDb): void {
|
|
15
|
+
// Lossy — original structured columns cannot be restored from notes text.
|
|
16
|
+
}
|
|
17
|
+
|
|
5
18
|
const log = getLogger("migration-134");
|
|
6
19
|
|
|
7
20
|
export function migrateContactsNotesColumn(database: DrizzleDb): void {
|
|
@@ -1,6 +1,26 @@
|
|
|
1
1
|
import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
|
|
2
2
|
import { withCrashRecovery } from "./validate-migration-state.js";
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Reverse v18: set contacts.last_interaction back to NULL.
|
|
6
|
+
*
|
|
7
|
+
* The forward migration backfilled last_interaction from channel data.
|
|
8
|
+
* Rolling back simply clears the column — the data can be re-derived by
|
|
9
|
+
* re-running the forward migration.
|
|
10
|
+
*/
|
|
11
|
+
export function downBackfillContactInteractionStats(database: DrizzleDb): void {
|
|
12
|
+
const raw = getSqliteFrom(database);
|
|
13
|
+
|
|
14
|
+
const colExists = raw
|
|
15
|
+
.query(
|
|
16
|
+
`SELECT 1 FROM pragma_table_info('contacts') WHERE name = 'last_interaction'`,
|
|
17
|
+
)
|
|
18
|
+
.get();
|
|
19
|
+
if (!colExists) return;
|
|
20
|
+
|
|
21
|
+
raw.exec(/*sql*/ `UPDATE contacts SET last_interaction = NULL`);
|
|
22
|
+
}
|
|
23
|
+
|
|
4
24
|
/**
|
|
5
25
|
* Backfill contacts.last_interaction from the max lastSeenAt across each
|
|
6
26
|
* contact's channels. interactionCount cannot be reliably derived from
|
|
@@ -2,6 +2,58 @@ import { getLogger } from "../../util/logger.js";
|
|
|
2
2
|
import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
|
|
3
3
|
import { withCrashRecovery } from "./validate-migration-state.js";
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Reverse v19: add assistant_id columns back to all 16 tables via
|
|
7
|
+
* ALTER TABLE ADD COLUMN, defaulting to 'self'.
|
|
8
|
+
*
|
|
9
|
+
* This restores the column that the forward migration dropped. All rows
|
|
10
|
+
* get assistant_id = 'self' since that was the only value before dropping.
|
|
11
|
+
*/
|
|
12
|
+
export function downDropAssistantIdColumns(database: DrizzleDb): void {
|
|
13
|
+
const raw = getSqliteFrom(database);
|
|
14
|
+
|
|
15
|
+
const tables = [
|
|
16
|
+
"contacts",
|
|
17
|
+
"assistant_ingress_invites",
|
|
18
|
+
"assistant_inbox_thread_state",
|
|
19
|
+
"call_sessions",
|
|
20
|
+
"channel_guardian_verification_challenges",
|
|
21
|
+
"channel_guardian_approval_requests",
|
|
22
|
+
"channel_guardian_rate_limits",
|
|
23
|
+
"guardian_action_requests",
|
|
24
|
+
"scoped_approval_grants",
|
|
25
|
+
"notification_events",
|
|
26
|
+
"notification_preferences",
|
|
27
|
+
"notification_deliveries",
|
|
28
|
+
"conversation_attention_events",
|
|
29
|
+
"conversation_assistant_attention_state",
|
|
30
|
+
"actor_token_records",
|
|
31
|
+
"actor_refresh_token_records",
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
for (const table of tables) {
|
|
35
|
+
// Skip if table doesn't exist
|
|
36
|
+
const tableExists = raw
|
|
37
|
+
.query(`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ?`)
|
|
38
|
+
.get(table);
|
|
39
|
+
if (!tableExists) continue;
|
|
40
|
+
|
|
41
|
+
// Skip if column already exists (idempotent)
|
|
42
|
+
const colExists = raw
|
|
43
|
+
.query(`SELECT 1 FROM pragma_table_info(?) WHERE name = 'assistant_id'`)
|
|
44
|
+
.get(table);
|
|
45
|
+
if (colExists) continue;
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
raw.exec(
|
|
49
|
+
/*sql*/ `ALTER TABLE ${table} ADD COLUMN assistant_id TEXT NOT NULL DEFAULT 'self'`,
|
|
50
|
+
);
|
|
51
|
+
} catch {
|
|
52
|
+
/* column may already exist from partial run */
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
5
57
|
const log = getLogger("migration-136");
|
|
6
58
|
|
|
7
59
|
/**
|
|
@@ -8,6 +8,19 @@ import { resolvePricingForUsageWithOverrides } from "../../util/pricing.js";
|
|
|
8
8
|
import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
|
|
9
9
|
import { withCrashRecovery } from "./validate-migration-state.js";
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Reverse v20: no-op — cannot reliably identify which llm_usage_events rows
|
|
13
|
+
* were updated by the backfill vs already had correct cache accounting.
|
|
14
|
+
*
|
|
15
|
+
* The forward migration updated input_tokens, output_tokens, cache token
|
|
16
|
+
* columns, estimated_cost_usd, and pricing_status based on request logs.
|
|
17
|
+
* There is no marker distinguishing backfilled rows from naturally-written
|
|
18
|
+
* ones, so a reversal cannot be performed without risking data corruption.
|
|
19
|
+
*/
|
|
20
|
+
export function downBackfillUsageCacheAccounting(_database: DrizzleDb): void {
|
|
21
|
+
// Lossy — cannot identify which rows were backfilled.
|
|
22
|
+
}
|
|
23
|
+
|
|
11
24
|
const log = getLogger("memory-db");
|
|
12
25
|
|
|
13
26
|
const CHECKPOINT_KEY = "migration_backfill_usage_cache_accounting_v1";
|
|
@@ -1,6 +1,60 @@
|
|
|
1
1
|
import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
|
|
2
2
|
import { withCrashRecovery } from "./validate-migration-state.js";
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Reverse v21: rename channel_verification_sessions back to
|
|
6
|
+
* channel_guardian_verification_challenges and recreate old indexes.
|
|
7
|
+
*/
|
|
8
|
+
export function downRenameVerificationTable(database: DrizzleDb): void {
|
|
9
|
+
const raw = getSqliteFrom(database);
|
|
10
|
+
|
|
11
|
+
// Check the new table exists before attempting anything
|
|
12
|
+
const newTableExists = raw
|
|
13
|
+
.query(
|
|
14
|
+
`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'channel_verification_sessions'`,
|
|
15
|
+
)
|
|
16
|
+
.get();
|
|
17
|
+
if (!newTableExists) return;
|
|
18
|
+
|
|
19
|
+
// If the old table already exists, skip (already rolled back)
|
|
20
|
+
const oldTableExists = raw
|
|
21
|
+
.query(
|
|
22
|
+
`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'channel_guardian_verification_challenges'`,
|
|
23
|
+
)
|
|
24
|
+
.get();
|
|
25
|
+
if (oldTableExists) return;
|
|
26
|
+
|
|
27
|
+
// Rename back to old name
|
|
28
|
+
raw.exec(
|
|
29
|
+
/*sql*/ `ALTER TABLE channel_verification_sessions RENAME TO channel_guardian_verification_challenges`,
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
// Drop new-style indexes and recreate old-style ones
|
|
33
|
+
raw.exec(/*sql*/ `DROP INDEX IF EXISTS idx_verification_sessions_lookup`);
|
|
34
|
+
raw.exec(/*sql*/ `DROP INDEX IF EXISTS idx_verification_sessions_active`);
|
|
35
|
+
raw.exec(/*sql*/ `DROP INDEX IF EXISTS idx_verification_sessions_identity`);
|
|
36
|
+
raw.exec(
|
|
37
|
+
/*sql*/ `DROP INDEX IF EXISTS idx_verification_sessions_destination`,
|
|
38
|
+
);
|
|
39
|
+
raw.exec(/*sql*/ `DROP INDEX IF EXISTS idx_verification_sessions_bootstrap`);
|
|
40
|
+
|
|
41
|
+
raw.exec(
|
|
42
|
+
/*sql*/ `CREATE INDEX IF NOT EXISTS idx_channel_guardian_challenges_lookup ON channel_guardian_verification_challenges(channel, challenge_hash, status)`,
|
|
43
|
+
);
|
|
44
|
+
raw.exec(
|
|
45
|
+
/*sql*/ `CREATE INDEX IF NOT EXISTS idx_guardian_sessions_active ON channel_guardian_verification_challenges(channel, status)`,
|
|
46
|
+
);
|
|
47
|
+
raw.exec(
|
|
48
|
+
/*sql*/ `CREATE INDEX IF NOT EXISTS idx_guardian_sessions_identity ON channel_guardian_verification_challenges(channel, expected_external_user_id, expected_chat_id, status)`,
|
|
49
|
+
);
|
|
50
|
+
raw.exec(
|
|
51
|
+
/*sql*/ `CREATE INDEX IF NOT EXISTS idx_guardian_sessions_destination ON channel_guardian_verification_challenges(channel, destination_address)`,
|
|
52
|
+
);
|
|
53
|
+
raw.exec(
|
|
54
|
+
/*sql*/ `CREATE INDEX IF NOT EXISTS idx_guardian_sessions_bootstrap ON channel_guardian_verification_challenges(channel, bootstrap_token_hash, status)`,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
4
58
|
/**
|
|
5
59
|
* One-shot migration: rename channel_guardian_verification_challenges →
|
|
6
60
|
* channel_verification_sessions, including all indexes that reference the
|
|
@@ -1,6 +1,31 @@
|
|
|
1
1
|
import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
|
|
2
2
|
import { withCrashRecovery } from "./validate-migration-state.js";
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Reverse v22: rename verification_session_id back to
|
|
6
|
+
* guardian_verification_session_id in call_sessions.
|
|
7
|
+
*/
|
|
8
|
+
export function downRenameVerificationSessionIdColumn(
|
|
9
|
+
database: DrizzleDb,
|
|
10
|
+
): void {
|
|
11
|
+
const raw = getSqliteFrom(database);
|
|
12
|
+
|
|
13
|
+
const columns = raw.query(`PRAGMA table_info(call_sessions)`).all() as Array<{
|
|
14
|
+
name: string;
|
|
15
|
+
}>;
|
|
16
|
+
const hasNewColumn = columns.some(
|
|
17
|
+
(c) => c.name === "verification_session_id",
|
|
18
|
+
);
|
|
19
|
+
const hasOldColumn = columns.some(
|
|
20
|
+
(c) => c.name === "guardian_verification_session_id",
|
|
21
|
+
);
|
|
22
|
+
if (!hasNewColumn || hasOldColumn) return;
|
|
23
|
+
|
|
24
|
+
raw.exec(
|
|
25
|
+
/*sql*/ `ALTER TABLE call_sessions RENAME COLUMN verification_session_id TO guardian_verification_session_id`,
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
4
29
|
/**
|
|
5
30
|
* One-shot migration: rename the guardian_verification_session_id column
|
|
6
31
|
* in call_sessions to verification_session_id, dropping the "guardian_"
|