@vellumai/assistant 0.9.0 → 0.10.0-dev.202606192226.8ed8a6a
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/ARCHITECTURE.md +19 -35
- package/bun.lock +7 -8
- package/docs/activation-funnel-telemetry.md +28 -22
- package/docs/architecture/security.md +29 -28
- package/docs/stt-provider-onboarding.md +3 -5
- package/docs/workflows-testing.md +13 -44
- package/docs/workflows.md +3 -5
- package/node_modules/@vellumai/ces-client/src/__tests__/ces-client.test.ts +47 -0
- package/node_modules/@vellumai/ces-client/src/rpc-client.ts +28 -5
- package/node_modules/@vellumai/environments/src/seeds.ts +2 -5
- package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +97 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +10 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +32 -6
- package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +119 -0
- package/node_modules/@vellumai/gateway-client/src/types.ts +15 -84
- package/openapi.yaml +1128 -119
- package/package.json +2 -1
- package/scripts/sync-llm-catalog.ts +6 -15
- package/scripts/sync-web-search-catalog.ts +3 -11
- package/src/__tests__/access-request-card-view.test.ts +98 -0
- package/src/__tests__/access-request-seed-content-blocks.test.ts +2 -4
- package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +72 -32
- package/src/__tests__/agent-loop-compaction-strip.test.ts +241 -0
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +16 -13
- package/src/__tests__/agent-loop-output-hooks.test.ts +69 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +25 -0
- package/src/__tests__/agent-loop-pushes-post-hook-prompt.test.ts +306 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +2 -3
- package/src/__tests__/app-compiler.test.ts +15 -1
- package/src/__tests__/app-dir-path-guard.test.ts +0 -1
- package/src/__tests__/approval-cascade.test.ts +6 -0
- package/src/__tests__/approval-routes-http.test.ts +128 -0
- package/src/__tests__/assistant-feature-flag-guard.test.ts +1 -4
- package/src/__tests__/assistant-feature-flag-guardrails.test.ts +0 -2
- package/src/__tests__/auth-fallback-events-store.test.ts +6 -14
- package/src/__tests__/avatar-identity-sync.test.ts +2 -27
- package/src/__tests__/btw-routes.test.ts +6 -8
- package/src/__tests__/call-pointer-messages.test.ts +28 -0
- package/src/__tests__/cancel-clears-processing.test.ts +89 -0
- package/src/__tests__/channel-approval-routes.test.ts +0 -4
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +5 -15
- package/src/__tests__/checker.test.ts +0 -3
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +3 -4
- package/src/__tests__/compaction-events.test.ts +6 -0
- package/src/__tests__/compactor-image-manifest-trust.test.ts +21 -1
- package/src/__tests__/compactor-summary-call-truncation.test.ts +223 -0
- package/src/__tests__/config-loader-backfill.test.ts +268 -27
- package/src/__tests__/config-schema.test.ts +35 -0
- package/src/__tests__/config-watcher.test.ts +0 -18
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -2
- package/src/__tests__/contact-store-user-file.test.ts +0 -6
- package/src/__tests__/contacts-tools.test.ts +29 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +6 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +22 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop.test.ts +58 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
- package/src/__tests__/conversation-confirmation-signals.test.ts +6 -0
- package/src/__tests__/conversation-lifecycle.test.ts +7 -9
- package/src/__tests__/conversation-load-history-repair.test.ts +101 -0
- package/src/__tests__/conversation-process-callsite.test.ts +6 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +6 -0
- package/src/__tests__/conversation-queue.test.ts +6 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +15 -12
- package/src/__tests__/conversation-slash-queue.test.ts +6 -0
- package/src/__tests__/conversation-slash-unknown.test.ts +6 -0
- package/src/__tests__/conversation-speed-override.test.ts +6 -0
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -3
- package/src/__tests__/conversation-title-service.test.ts +62 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +6 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +6 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +6 -0
- package/src/__tests__/credential-broker.test.ts +449 -1
- package/src/__tests__/credential-execution-shell-lockdown.test.ts +18 -11
- package/src/__tests__/credential-execution-tools.test.ts +0 -1
- package/src/__tests__/credential-prompt-route.test.ts +4 -4
- package/src/__tests__/credential-routes.test.ts +360 -0
- package/src/__tests__/credential-security-invariants.test.ts +4 -13
- package/src/__tests__/disk-pressure-policy.test.ts +12 -0
- package/src/__tests__/disk-pressure-tools.test.ts +41 -1
- package/src/__tests__/disk-usage.test.ts +65 -0
- package/src/__tests__/document-tool-security.test.ts +9 -6
- package/src/__tests__/document-update-default-surface.test.ts +202 -0
- package/src/__tests__/dynamic-page-surface.test.ts +152 -1
- package/src/__tests__/fixtures/credential-security-fixtures.ts +2 -33
- package/src/__tests__/gateway-flag-listener.test.ts +110 -1
- package/src/__tests__/gateway-only-guard.test.ts +3 -7
- package/src/__tests__/gemini-count-tokens.test.ts +70 -0
- package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
- package/src/__tests__/guardian-card-withdrawal.test.ts +403 -0
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +5 -3
- package/src/__tests__/guardian-grant-minting.test.ts +3 -35
- package/src/__tests__/guardian-routing-invariants.test.ts +64 -26
- package/src/__tests__/guardian-routing-state.test.ts +0 -1
- package/src/__tests__/headless-browser-mode.test.ts +10 -0
- package/src/__tests__/headless-browser-navigate.test.ts +8 -3
- package/src/__tests__/helpers/create-guardian-binding.ts +0 -1
- package/src/__tests__/host-browser-proxy.test.ts +87 -0
- package/src/__tests__/identity-routes.test.ts +0 -189
- package/src/__tests__/inbound-invite-redemption.test.ts +4 -4
- package/src/__tests__/injector-disk-pressure.test.ts +10 -5
- package/src/__tests__/injector-v3-suppression.test.ts +27 -20
- package/src/__tests__/internal-telemetry-routes.test.ts +6 -14
- package/src/__tests__/invite-redemption-service.test.ts +4 -7
- package/src/__tests__/llm-callsite-catalog.test.ts +5 -6
- package/src/__tests__/llm-catalog-parity.test.ts +30 -23
- package/src/__tests__/llm-resolver.test.ts +70 -24
- package/src/__tests__/llm-schema.test.ts +1 -0
- package/src/__tests__/managed-profile-guard.test.ts +163 -4
- package/src/__tests__/mcp-health-check.test.ts +6 -7
- package/src/__tests__/media-stream-server-integration.test.ts +317 -13
- package/src/__tests__/oauth-provider-seed-logos.test.ts +4 -6
- package/src/__tests__/onboarding-persona-write.test.ts +1 -1
- package/src/__tests__/path-policy.test.ts +34 -0
- package/src/__tests__/persona-resolver.test.ts +49 -14
- package/src/__tests__/plugin-api-model-profiles.test.ts +178 -0
- package/src/__tests__/plugin-api-provider.test.ts +24 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +6 -3
- package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +214 -0
- package/src/__tests__/provider-send-message-override-profile.test.ts +76 -0
- package/src/__tests__/reaction-persistence.test.ts +150 -29
- package/src/__tests__/registry.test.ts +2 -7
- package/src/__tests__/relay-server.test.ts +285 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
- package/src/__tests__/schedule-routes-workflow-validation.test.ts +1 -10
- package/src/__tests__/schedule-routes.test.ts +0 -30
- package/src/__tests__/schedule-tools.test.ts +2 -18
- package/src/__tests__/scheduler-reuse-conversation.test.ts +8 -5
- package/src/__tests__/secret-response-routing.test.ts +35 -0
- package/src/__tests__/secret-routes-platform-proxy.test.ts +16 -2
- package/src/__tests__/skill-execute-input.test.ts +177 -1
- package/src/__tests__/skill-load-tool.test.ts +72 -0
- package/src/__tests__/skill-runtime-path.test.ts +2 -3
- package/src/__tests__/skills.test.ts +51 -0
- package/src/__tests__/slack-notification-approval-card.test.ts +176 -0
- package/src/__tests__/slack-reaction-canonical-approval.test.ts +285 -0
- package/src/__tests__/subagent-tools.test.ts +266 -0
- package/src/__tests__/surface-completion-nudge-hook.test.ts +367 -0
- package/src/__tests__/system-storage-cleanup-skill.test.ts +56 -0
- package/src/__tests__/task-progress-nudge-hook.test.ts +1 -1
- package/src/__tests__/title-generate-hook.test.ts +100 -3
- package/src/__tests__/token-estimator-accuracy.benchmark.test.ts +1 -29
- package/src/__tests__/token-manager.test.ts +519 -0
- package/src/__tests__/tool-approval-seed-content-blocks.test.ts +1 -1
- package/src/__tests__/tool-audit-listener.test.ts +7 -7
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +6 -3
- package/src/__tests__/tool-executor.test.ts +0 -79
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +4 -2
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +220 -3
- package/src/__tests__/trusted-contact-multichannel.test.ts +3 -3
- package/src/__tests__/trusted-contact-verification.test.ts +8 -10
- package/src/__tests__/twilio-routes.test.ts +81 -1
- package/src/__tests__/voice-invite-redemption.test.ts +2 -3
- package/src/__tests__/weak-open-model.test.ts +30 -0
- package/src/__tests__/web-search-catalog-parity.test.ts +6 -25
- package/src/__tests__/workspace-greetings.test.ts +152 -0
- package/src/__tests__/workspace-migration-105-enable-memory-v3-live-for-new-workspaces.test.ts +149 -0
- package/src/__tests__/workspace-migration-108-drop-balanced-economy-profile.test.ts +285 -0
- package/src/__tests__/workspace-migration-add-send-diagnostics.test.ts +1 -1
- package/src/__tests__/workspace-migration-drop-collect-usage-data.test.ts +118 -0
- package/src/__tests__/workspace-migration-drop-send-diagnostics.test.ts +118 -0
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +0 -4
- package/src/agent/loop.ts +75 -29
- package/src/api/README.md +6 -6
- package/src/api/events/tool-result.ts +6 -0
- package/src/api/events/workflow-completed.ts +53 -0
- package/src/api/events/workflow-leaf-finished.ts +38 -0
- package/src/api/events/workflow-leaf-started.ts +35 -0
- package/src/api/events/workflow-progress.ts +32 -0
- package/src/api/events/workflow-started.ts +31 -0
- package/src/api/index.ts +40 -0
- package/src/api/responses/conversation-message.ts +28 -4
- package/src/api/responses/home.ts +26 -4
- package/src/api/responses/workflow-journal.ts +53 -0
- package/src/approvals/guardian-card-withdrawal.ts +145 -0
- package/src/approvals/guardian-decision-primitive.ts +26 -3
- package/src/approvals/guardian-request-resolvers.ts +183 -80
- package/src/calls/__tests__/channel-admission-reader.test.ts +132 -0
- package/src/calls/__tests__/relay-setup-router.test.ts +350 -0
- package/src/calls/call-pointer-messages.ts +10 -4
- package/src/calls/channel-admission-reader.ts +104 -0
- package/src/calls/guardian-dispatch.ts +17 -45
- package/src/calls/media-stream-server.ts +84 -2
- package/src/calls/relay-access-wait.ts +1 -1
- package/src/calls/relay-server.ts +66 -0
- package/src/calls/relay-setup-router.ts +82 -1
- package/src/calls/twilio-routes.ts +17 -8
- package/src/calls/voice-session-bridge.ts +2 -2
- package/src/cli/commands/clients.ts +3 -0
- package/src/cli/commands/db/status.ts +37 -3
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2-compare-render.test.ts +1 -1
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2.test.ts +8 -7
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v3.test.ts +5 -4
- package/src/cli/commands/memory/index.ts +30 -0
- package/src/cli/commands/{memory-v2-compare-render.ts → memory/memory-v2-compare-render.ts} +1 -1
- package/src/cli/commands/{memory-v2.ts → memory/memory-v2.ts} +6 -15
- package/src/cli/commands/{memory-v3.ts → memory/memory-v3.ts} +97 -11
- package/src/cli/commands/oauth/status.test.ts +36 -0
- package/src/cli/commands/oauth/status.ts +23 -3
- package/src/cli/commands/plugins.ts +400 -14
- package/src/cli/lib/__tests__/diff-plugin.test.ts +443 -0
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +31 -0
- package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +443 -0
- package/src/cli/lib/__tests__/plugin-pin-history.test.ts +162 -0
- package/src/cli/lib/__tests__/plugin-surfaces.test.ts +111 -0
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +295 -2
- package/src/cli/lib/diff-plugin.ts +346 -0
- package/src/cli/lib/inspect-plugin.ts +12 -1
- package/src/cli/lib/install-from-github.ts +128 -19
- package/src/cli/lib/merge-plugin-tree.ts +328 -0
- package/src/cli/lib/plugin-fingerprint.ts +14 -0
- package/src/cli/lib/plugin-marketplace.ts +11 -0
- package/src/cli/lib/plugin-pin-history.ts +257 -0
- package/src/cli/lib/plugin-surfaces.ts +104 -0
- package/src/cli/lib/upgrade-plugin.ts +298 -10
- package/src/cli/program.ts +2 -6
- package/src/config/__tests__/sync-gated-profiles.test.ts +368 -0
- package/src/config/assistant-feature-flags.ts +22 -7
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +0 -1
- package/src/config/bundled-skills/document-editor/SKILL.md +3 -3
- package/src/config/bundled-skills/document-editor/TOOLS.json +2 -2
- package/src/config/bundled-skills/messaging/SKILL.md +6 -4
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +9 -8
- package/src/config/bundled-skills/subagent/SKILL.md +4 -0
- package/src/config/bundled-skills/subagent/TOOLS.json +4 -0
- package/src/config/bundled-skills/system-storage-cleanup/SKILL.md +74 -0
- package/src/config/bundled-skills/workflows/SKILL.md +14 -8
- package/src/config/bundled-tool-registry.ts +2 -7
- package/src/config/call-site-defaults.ts +15 -2
- package/src/config/feature-flag-registry.json +46 -31
- package/src/config/inference-profile-validation.ts +26 -0
- package/src/config/llm-resolver.ts +3 -0
- package/src/config/loader.ts +4 -0
- package/src/config/memory-v3-gate.ts +11 -0
- package/src/config/profile-order.ts +28 -0
- package/src/config/schema.ts +8 -6
- package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
- package/src/config/schemas/call-site-catalog.ts +7 -0
- package/src/config/schemas/channels.ts +11 -0
- package/src/config/schemas/elevenlabs.ts +0 -1
- package/src/config/schemas/llm.ts +31 -0
- package/src/config/schemas/memory-lifecycle.ts +3 -7
- package/src/config/schemas/memory-v3.ts +6 -0
- package/src/config/schemas/platform.ts +0 -8
- package/src/config/schemas/services.ts +18 -0
- package/src/config/seed-inference-profiles.ts +109 -44
- package/src/config/skills.ts +21 -0
- package/src/config/sync-gated-profiles.ts +220 -0
- package/src/contacts/contact-store.ts +89 -106
- package/src/contacts/contacts-write.ts +5 -22
- package/src/contacts/types.ts +0 -1
- package/src/context/compactor.ts +88 -54
- package/src/context/strip-injections.ts +58 -10
- package/src/context/token-estimator.ts +1 -1
- package/src/credential-execution/process-manager.ts +55 -14
- package/src/credential-execution/prompted-credential.ts +2 -3
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -2
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +44 -0
- package/src/daemon/config-watcher.ts +0 -4
- package/src/daemon/conversation-agent-loop-handlers.ts +2 -0
- package/src/daemon/conversation-agent-loop.ts +114 -22
- package/src/daemon/conversation-history.ts +1 -1
- package/src/daemon/conversation-lifecycle.ts +3 -5
- package/src/daemon/conversation-process.ts +13 -5
- package/src/daemon/conversation-runtime-assembly.ts +13 -15
- package/src/daemon/conversation-slash.ts +2 -23
- package/src/daemon/conversation-surfaces.ts +26 -0
- package/src/daemon/conversation-tool-setup.ts +53 -19
- package/src/daemon/conversation.ts +131 -20
- package/src/daemon/disk-pressure-policy.ts +5 -3
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -1
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -1
- package/src/daemon/handlers/config-a2a.ts +0 -2
- package/src/daemon/handlers/config-channels.ts +15 -16
- package/src/daemon/handlers/config-slack-channel.ts +22 -3
- package/src/daemon/handlers/conversations.ts +107 -0
- package/src/daemon/host-browser-proxy.ts +41 -0
- package/src/daemon/lifecycle.ts +55 -27
- package/src/daemon/memory-v2-startup.test.ts +131 -0
- package/src/daemon/memory-v2-startup.ts +100 -1
- package/src/daemon/message-provenance.ts +2 -0
- package/src/daemon/message-types/contacts.ts +0 -1
- package/src/daemon/message-types/conversations.ts +3 -3
- package/src/daemon/message-types/sync.ts +0 -1
- package/src/daemon/message-types/web-activity.ts +7 -1
- package/src/daemon/message-types/workflows.ts +83 -1
- package/src/daemon/orphan-reaper.test.ts +0 -19
- package/src/daemon/orphan-reaper.ts +2 -24
- package/src/daemon/server.ts +0 -10
- package/src/daemon/tool-setup-types.ts +4 -0
- package/src/daemon/trust-context.ts +1 -1
- package/src/daemon/wake-conversation-ops.ts +1 -0
- package/src/events/tool-audit-listener.ts +2 -2
- package/src/home/feed-source-enrichment.test.ts +151 -0
- package/src/home/feed-source-enrichment.ts +176 -0
- package/src/home/relationship-state.ts +2 -4
- package/src/instrument.ts +18 -6
- package/src/ipc/__tests__/binary-result-ipc.test.ts +81 -0
- package/src/ipc/__tests__/clients-list-ipc.test.ts +20 -0
- package/src/ipc/assistant-server.ts +37 -4
- package/src/ipc/gateway-flag-listener.ts +18 -2
- package/src/memory/__tests__/auto-analysis-enqueue.test.ts +5 -16
- package/src/memory/__tests__/db-logs-attach.test.ts +122 -0
- package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +7 -11
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +37 -7
- package/src/memory/__tests__/memory-retrospective-job.test.ts +229 -401
- package/src/memory/__tests__/onboarding-events-store.test.ts +7 -7
- package/src/memory/auth-fallback-events-store.ts +2 -2
- package/src/memory/auto-analysis-enqueue.ts +3 -5
- package/src/memory/bookmark-crud.ts +1 -2
- package/src/memory/canonical-guardian-store.ts +39 -1
- package/src/memory/conversation-crud.ts +9 -4
- package/src/memory/conversation-key-store.ts +17 -2
- package/src/memory/conversation-title-service.ts +64 -7
- package/src/memory/db-async-query.ts +20 -2
- package/src/memory/db-connection.ts +49 -5
- package/src/memory/db-init.ts +17 -17
- package/src/memory/embedding-backend.ts +38 -1
- package/src/memory/embedding-billing-breaker.ts +96 -0
- package/src/memory/jobs-store.ts +25 -13
- package/src/memory/jobs-worker.ts +54 -1
- package/src/memory/lifecycle-events-store.ts +2 -2
- package/src/memory/memory-retrospective-constants.ts +4 -4
- package/src/memory/memory-retrospective-enqueue.ts +31 -6
- package/src/memory/memory-retrospective-job.ts +28 -227
- package/src/memory/migrations/129-contact-channels-access-fields.ts +18 -9
- package/src/memory/migrations/131-drop-legacy-member-guardian-tables.ts +14 -2
- package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +10 -0
- package/src/memory/migrations/291-contact-channels-renormalize-addresses.ts +72 -0
- package/src/memory/migrations/292-schedule-default-no-reuse-conversation.test.ts +67 -0
- package/src/memory/migrations/292-schedule-default-no-reuse-conversation.ts +25 -0
- package/src/memory/migrations/293-workflow-journal-leaf-tokens.ts +32 -0
- package/src/memory/migrations/294-drop-external-user-id.ts +31 -0
- package/src/memory/migrations/295-drop-approval-prompt-ts-tracker.ts +20 -0
- package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.test.ts +110 -0
- package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.ts +68 -0
- package/src/memory/migrations/__tests__/131-drop-legacy-member-guardian-tables.test.ts +154 -0
- package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +31 -0
- package/src/memory/migrations/__tests__/291-contact-channels-renormalize-addresses.test.ts +341 -0
- package/src/memory/migrations/__tests__/run-migrations.test.ts +52 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/migrations/run-migrations.ts +41 -0
- package/src/memory/migrations/validate-migration-state.ts +1 -1
- package/src/memory/onboarding-events-store.ts +3 -3
- package/src/memory/schema/contacts.ts +0 -5
- package/src/memory/skill-loaded-events-store.test.ts +7 -15
- package/src/memory/skill-loaded-events-store.ts +2 -2
- package/src/memory/tool-executed-events-store.test.ts +7 -7
- package/src/memory/turn-trace-store.test.ts +736 -0
- package/src/memory/turn-trace-store.ts +364 -0
- package/src/memory/v2/__tests__/consolidation-job.test.ts +8 -0
- package/src/memory/v2/__tests__/skill-content.test.ts +30 -0
- package/src/memory/v2/consolidation-job.ts +2 -2
- package/src/memory/v2/skill-content.ts +25 -7
- package/src/memory/v2/skill-store.ts +7 -1
- package/src/memory/v3-eval/__tests__/eval-packets.test.ts +248 -0
- package/src/memory/v3-eval/eval-packets.ts +546 -0
- package/src/messaging/providers/slack/adapter.ts +1 -1
- package/src/messaging/providers/slack/api.ts +31 -0
- package/src/messaging/providers/slack/send.test.ts +114 -2
- package/src/messaging/providers/slack/send.ts +30 -7
- package/src/messaging/providers/slack/withdraw.test.ts +200 -0
- package/src/messaging/providers/slack/withdraw.ts +161 -0
- package/src/notifications/AGENTS.md +2 -0
- package/src/notifications/access-request-copy.ts +72 -59
- package/src/notifications/adapters/shared.ts +29 -0
- package/src/notifications/adapters/slack.ts +58 -103
- package/src/notifications/adapters/telegram.ts +2 -20
- package/src/notifications/approval-card-data.ts +333 -0
- package/src/notifications/broadcaster.ts +16 -3
- package/src/notifications/canonical-delivery-recorder.ts +139 -0
- package/src/notifications/copy-composer.ts +3 -3
- package/src/notifications/decision-engine.ts +4 -2
- package/src/notifications/destination-resolver.ts +4 -6
- package/src/notifications/guardian-question-mode.ts +10 -0
- package/src/notifications/home-feed-side-effect.ts +7 -16
- package/src/notifications/notification-utils.ts +19 -20
- package/src/notifications/signal.ts +79 -43
- package/src/notifications/types.ts +98 -121
- package/src/oauth/AGENTS.md +5 -24
- package/src/permissions/checker.test.ts +51 -0
- package/src/permissions/checker.ts +185 -26
- package/src/permissions/ipc-risk-types.ts +24 -0
- package/src/permissions/question-prompter.test.ts +27 -0
- package/src/permissions/question-prompter.ts +4 -0
- package/src/permissions/secret-prompter.ts +15 -1
- package/src/platform/client.test.ts +119 -0
- package/src/platform/client.ts +66 -0
- package/src/platform/consent-cache.test.ts +267 -0
- package/src/platform/consent-cache.ts +174 -0
- package/src/plugin-api/constants.ts +1 -1
- package/src/plugin-api/index.ts +33 -1
- package/src/plugin-api/model-profiles.ts +33 -0
- package/src/plugin-api/types.ts +50 -2
- package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +56 -0
- package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +43 -0
- package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +137 -0
- package/src/plugins/defaults/advisor/__tests__/consult.test.ts +153 -0
- package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +138 -0
- package/src/plugins/defaults/advisor/__tests__/transcript.test.ts +147 -0
- package/src/plugins/defaults/advisor/advisor-gate.ts +29 -0
- package/src/plugins/defaults/advisor/advisor-state-store.ts +94 -0
- package/src/plugins/defaults/advisor/config.ts +21 -0
- package/src/plugins/defaults/advisor/consult.ts +93 -0
- package/src/plugins/defaults/advisor/hooks/post-model-call.ts +34 -0
- package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +30 -0
- package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +19 -0
- package/src/plugins/defaults/advisor/package.json +14 -0
- package/src/plugins/defaults/advisor/steering.ts +67 -0
- package/src/plugins/defaults/advisor/tools/advisor.ts +65 -0
- package/src/plugins/defaults/advisor/transcript.ts +76 -0
- package/src/plugins/defaults/compaction/window-manager.ts +18 -0
- package/src/plugins/defaults/index.ts +60 -0
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +22 -9
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/memory-retrieval/injectors.ts +4 -4
- package/src/plugins/defaults/memory-retrieval/tail-reinjection-strip.ts +64 -0
- package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +29 -21
- package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +129 -9
- package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +31 -4
- package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +77 -2
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +7 -10
- package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +144 -11
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +32 -20
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +56 -3
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +23 -2
- package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +276 -0
- package/src/plugins/defaults/surface-completion-nudge/hooks/stop.ts +22 -0
- package/src/plugins/defaults/surface-completion-nudge/nudge-state-store.ts +46 -0
- package/src/plugins/defaults/surface-completion-nudge/package.json +14 -0
- package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +3 -13
- package/src/plugins/defaults/title-generate/hooks/stop.ts +56 -21
- package/src/prompts/persona-resolver.ts +14 -4
- package/src/prompts/templates/system-sections.ts +7 -2
- package/src/providers/__tests__/count-tokens-forwarding.test.ts +98 -0
- package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
- package/src/providers/__tests__/provider-secret-catalog.test.ts +1 -0
- package/src/providers/__tests__/retry-callsite.test.ts +176 -0
- package/src/providers/anthropic/client.ts +230 -175
- package/src/providers/atlascloud/client.ts +85 -0
- package/src/providers/call-site-routing.ts +10 -0
- package/src/providers/fetch-provider-catalog.ts +85 -0
- package/src/providers/gemini/client.ts +43 -0
- package/src/providers/inference/adapter-factory.ts +3 -0
- package/src/providers/model-catalog.ts +58 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +33 -0
- package/src/providers/openai/chat-completions-provider.ts +7 -0
- package/src/providers/openai/responses-provider.ts +10 -0
- package/src/providers/provider-send-message.ts +11 -3
- package/src/providers/ratelimit.ts +9 -0
- package/src/providers/retry.ts +62 -12
- package/src/providers/search-provider-catalog.ts +10 -0
- package/src/providers/types.ts +16 -0
- package/src/providers/usage-tracking.ts +7 -0
- package/src/providers/weak-open-model.ts +22 -0
- package/src/runtime/AGENTS.md +0 -1
- package/src/runtime/__tests__/agent-wake.test.ts +227 -0
- package/src/runtime/__tests__/client-health.test.ts +44 -0
- package/src/runtime/access-request-helper.ts +21 -53
- package/src/runtime/actor-trust-resolver.ts +59 -63
- package/src/runtime/agent-wake.ts +71 -0
- package/src/runtime/assistant-event-hub.ts +18 -4
- package/src/runtime/auth/__tests__/route-policy.test.ts +12 -0
- package/src/runtime/auth/require-bound-guardian.ts +1 -4
- package/src/runtime/btw-sidechain.ts +3 -6
- package/src/runtime/capabilities.test.ts +120 -0
- package/src/runtime/capabilities.ts +197 -0
- package/src/runtime/channel-approval-types.ts +22 -45
- package/src/runtime/channel-invite-transports/telegram.ts +4 -4
- package/src/runtime/channel-retry-sweep.ts +1 -0
- package/src/runtime/channel-verification-service.ts +3 -3
- package/src/runtime/client-health.ts +26 -0
- package/src/runtime/confirmation-request-guardian-bridge.ts +38 -29
- package/src/runtime/effective-capabilities.test.ts +128 -0
- package/src/runtime/effective-capabilities.ts +84 -0
- package/src/runtime/guardian-reply-router.ts +106 -21
- package/src/runtime/invite-redemption-service.ts +9 -25
- package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +126 -0
- package/src/runtime/migrations/vbundle-builder.ts +49 -20
- package/src/runtime/pending-interactions.ts +35 -0
- package/src/runtime/routes/__tests__/client-routes.test.ts +13 -0
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +67 -0
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +383 -1
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/approval-routes.ts +40 -9
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +2 -2
- package/src/runtime/routes/assets/vellum-design-system.css +1959 -0
- package/src/runtime/routes/browser-tabs-routes.ts +9 -0
- package/src/runtime/routes/btw-routes.ts +1 -27
- package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +17 -8
- package/src/runtime/routes/client-routes.ts +10 -0
- package/src/runtime/routes/contact-routes.ts +31 -8
- package/src/runtime/routes/conversation-compaction-routes.ts +1 -1
- package/src/runtime/routes/conversation-management-routes.ts +80 -1
- package/src/runtime/routes/conversation-query-routes.ts +68 -22
- package/src/runtime/routes/conversation-routes.ts +39 -14
- package/src/runtime/routes/credential-routes.ts +40 -16
- package/src/runtime/routes/empty-state-greeting-cache.ts +1 -2
- package/src/runtime/routes/events-routes.ts +1 -3
- package/src/runtime/routes/guardian-approval-interception.ts +14 -73
- package/src/runtime/routes/guardian-approval-prompt.ts +22 -4
- package/src/runtime/routes/home-feed-routes.ts +8 -3
- package/src/runtime/routes/identity-routes.ts +1 -296
- package/src/runtime/routes/inbound-message-handler.ts +214 -228
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +89 -7
- package/src/runtime/routes/inbound-stages/admission-policy.test.ts +154 -0
- package/src/runtime/routes/inbound-stages/admission-policy.ts +140 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +3 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +11 -6
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -2
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +1 -2
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +7 -7
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +47 -28
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +358 -0
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +8 -0
- package/src/runtime/routes/integrations/slack/channel.ts +36 -0
- package/src/runtime/routes/internal-telemetry-routes.ts +1 -1
- package/src/runtime/routes/mcp-auth-routes.ts +233 -41
- package/src/runtime/routes/memory-eval-routes.ts +87 -0
- package/src/runtime/routes/notification-routes.ts +122 -133
- package/src/runtime/routes/platform-routes.ts +2 -2
- package/src/runtime/routes/plugins-routes.ts +366 -11
- package/src/runtime/routes/schedule-routes.ts +0 -22
- package/src/runtime/routes/secret-routes.ts +15 -0
- package/src/runtime/routes/surface-action-routes.ts +2 -1
- package/src/runtime/routes/tool-call-question-enrichment.test.ts +146 -0
- package/src/runtime/routes/tool-call-question-enrichment.ts +66 -0
- package/src/runtime/routes/workflow-routes.test.ts +229 -44
- package/src/runtime/routes/workflow-routes.ts +131 -29
- package/src/runtime/routes/workspace-greetings.ts +55 -0
- package/src/runtime/sync/resource-sync-events.ts +1 -11
- package/src/runtime/tool-grant-request-helper.ts +18 -16
- package/src/runtime/trust-context-resolver.ts +8 -5
- package/src/schedule/inference-profile.ts +2 -14
- package/src/schedule/schedule-store.ts +1 -1
- package/src/schedule/scheduler-types.ts +5 -1
- package/src/security/__tests__/provider-key-env-fallback.test.ts +6 -0
- package/src/security/secret-patterns.ts +3 -0
- package/src/subagent/manager.ts +17 -4
- package/src/subagent/types.ts +6 -0
- package/src/telemetry/trace-collection-policy.test.ts +28 -0
- package/src/telemetry/trace-collection-policy.ts +30 -0
- package/src/telemetry/types.ts +89 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +586 -36
- package/src/telemetry/usage-telemetry-reporter.ts +148 -41
- package/src/tools/AGENTS.md +3 -3
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +31 -0
- package/src/tools/browser/browser-execution.ts +30 -19
- package/src/tools/document/document-tool.ts +30 -6
- package/src/tools/executor.ts +5 -3
- package/src/tools/host-terminal/host-shell.ts +5 -4
- package/src/tools/memory/register.ts +2 -2
- package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +360 -0
- package/src/tools/network/__tests__/web-search.test.ts +143 -0
- package/src/tools/network/web-fetch.ts +372 -1
- package/src/tools/network/web-search-error.ts +1 -1
- package/src/tools/network/web-search.ts +213 -10
- package/src/tools/permission-checker.ts +4 -3
- package/src/tools/registry.ts +20 -0
- package/src/tools/schedule/create.ts +7 -12
- package/src/tools/schedule/update.ts +4 -11
- package/src/tools/shared/filesystem/path-policy.ts +39 -13
- package/src/tools/side-effects.ts +2 -17
- package/src/tools/skills/execute.ts +99 -2
- package/src/tools/skills/load.ts +43 -2
- package/src/tools/subagent/spawn.ts +64 -15
- package/src/tools/terminal/shell.ts +10 -4
- package/src/tools/tool-approval-handler.ts +18 -13
- package/src/tools/tool-manifest.ts +0 -2
- package/src/tools/types.ts +10 -0
- package/src/tools/ui-surface/definitions.ts +64 -3
- package/src/tools/verification-control-plane-policy.ts +3 -1
- package/src/tools/workflows/run-workflow.test.ts +8 -18
- package/src/tools/workflows/run-workflow.ts +1 -0
- package/src/util/disk-usage.ts +78 -23
- package/src/util/logs-db-path.ts +22 -0
- package/src/util/platform.ts +10 -3
- package/src/watcher/telemetry.ts +2 -2
- package/src/workflows/capabilities.ts +2 -3
- package/src/workflows/engine.test.ts +175 -1
- package/src/workflows/engine.ts +82 -0
- package/src/workflows/journal-store.test.ts +70 -0
- package/src/workflows/journal-store.ts +18 -3
- package/src/workflows/run-manager.test.ts +171 -28
- package/src/workflows/run-manager.ts +66 -24
- package/src/workspace/migrations/105-enable-memory-v3-live-for-new-workspaces.ts +63 -0
- package/src/workspace/migrations/106-drop-collect-usage-data.ts +47 -0
- package/src/workspace/migrations/107-drop-send-diagnostics.ts +47 -0
- package/src/workspace/migrations/108-drop-balanced-economy-profile.ts +129 -0
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/__tests__/app-control-no-global-cgevent.test.ts +0 -98
- package/src/__tests__/credential-security-e2e.test.ts +0 -362
- package/src/__tests__/credential-vault-unit.test.ts +0 -1528
- package/src/__tests__/credential-vault.test.ts +0 -1706
- package/src/__tests__/identity-intro-cache.test.ts +0 -315
- package/src/__tests__/secret-onetime-send.test.ts +0 -182
- package/src/cli/commands/__tests__/task.test.ts +0 -914
- package/src/cli/commands/task.ts +0 -771
- package/src/config/bundled-skills/personal-page/SKILL.md +0 -57
- package/src/config/bundled-skills/personal-page/TOOLS.json +0 -27
- package/src/config/bundled-skills/personal-page/tools/app-refresh.ts +0 -17
- package/src/config/preloaded-apps/personal-page/src/components/About.tsx +0 -22
- package/src/config/preloaded-apps/personal-page/src/components/App.tsx +0 -16
- package/src/config/preloaded-apps/personal-page/src/components/Features.tsx +0 -77
- package/src/config/preloaded-apps/personal-page/src/components/Hero.tsx +0 -57
- package/src/config/preloaded-apps/personal-page/src/components/Pending.tsx +0 -28
- package/src/config/preloaded-apps/personal-page/src/components/animations.tsx +0 -234
- package/src/config/preloaded-apps/personal-page/src/components/icons.tsx +0 -48
- package/src/config/preloaded-apps/personal-page/src/components/media.ts +0 -16
- package/src/config/preloaded-apps/personal-page/src/index.html +0 -20
- package/src/config/preloaded-apps/personal-page/src/main.tsx +0 -7
- package/src/config/preloaded-apps/personal-page/src/profile-data.ts +0 -82
- package/src/config/preloaded-apps/personal-page/src/styles.css +0 -759
- package/src/memory/__tests__/preloaded-apps.test.ts +0 -85
- package/src/memory/preloaded-apps.ts +0 -116
- package/src/notifications/tool-approval-copy.ts +0 -142
- package/src/runtime/routes/approval-prompt-ts-tracker.ts +0 -78
- package/src/runtime/routes/identity-intro-cache.ts +0 -172
- package/src/tools/credentials/vault.ts +0 -712
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
2
|
+
import { describe, expect, test } from "bun:test";
|
|
3
|
+
|
|
4
|
+
import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
5
|
+
|
|
6
|
+
import { getSqliteFrom } from "../../db-connection.js";
|
|
7
|
+
import * as schema from "../../schema.js";
|
|
8
|
+
import { migrateDropLegacyMemberGuardianTables } from "../131-drop-legacy-member-guardian-tables.js";
|
|
9
|
+
|
|
10
|
+
function createTestDb() {
|
|
11
|
+
const sqlite = new Database(":memory:");
|
|
12
|
+
sqlite.exec("PRAGMA journal_mode=WAL");
|
|
13
|
+
return drizzle(sqlite, { schema });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function bootstrap(db: ReturnType<typeof createTestDb>): void {
|
|
17
|
+
const raw = getSqliteFrom(db);
|
|
18
|
+
raw.exec(/*sql*/ `
|
|
19
|
+
CREATE TABLE contacts (
|
|
20
|
+
id TEXT PRIMARY KEY,
|
|
21
|
+
display_name TEXT NOT NULL,
|
|
22
|
+
role TEXT NOT NULL DEFAULT 'contact',
|
|
23
|
+
principal_id TEXT,
|
|
24
|
+
created_at INTEGER NOT NULL,
|
|
25
|
+
updated_at INTEGER NOT NULL
|
|
26
|
+
)
|
|
27
|
+
`);
|
|
28
|
+
raw.exec(/*sql*/ `
|
|
29
|
+
CREATE TABLE contact_channels (
|
|
30
|
+
id TEXT PRIMARY KEY,
|
|
31
|
+
contact_id TEXT NOT NULL,
|
|
32
|
+
type TEXT NOT NULL,
|
|
33
|
+
address TEXT NOT NULL,
|
|
34
|
+
external_user_id TEXT,
|
|
35
|
+
external_chat_id TEXT,
|
|
36
|
+
status TEXT NOT NULL DEFAULT 'unverified',
|
|
37
|
+
policy TEXT NOT NULL DEFAULT 'allow',
|
|
38
|
+
invite_id TEXT,
|
|
39
|
+
revoked_reason TEXT,
|
|
40
|
+
blocked_reason TEXT,
|
|
41
|
+
last_seen_at INTEGER,
|
|
42
|
+
verified_at INTEGER,
|
|
43
|
+
verified_via TEXT,
|
|
44
|
+
created_at INTEGER NOT NULL,
|
|
45
|
+
updated_at INTEGER
|
|
46
|
+
)
|
|
47
|
+
`);
|
|
48
|
+
raw.exec(/*sql*/ `
|
|
49
|
+
CREATE INDEX idx_contact_channels_type_ext_user
|
|
50
|
+
ON contact_channels(type, external_user_id)
|
|
51
|
+
`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function createLegacyTables(raw: Database): void {
|
|
55
|
+
raw.exec(/*sql*/ `
|
|
56
|
+
CREATE TABLE channel_guardian_bindings (
|
|
57
|
+
id TEXT PRIMARY KEY,
|
|
58
|
+
channel TEXT NOT NULL,
|
|
59
|
+
status TEXT NOT NULL,
|
|
60
|
+
guardian_external_user_id TEXT,
|
|
61
|
+
guardian_principal_id TEXT,
|
|
62
|
+
guardian_delivery_chat_id TEXT,
|
|
63
|
+
metadata_json TEXT,
|
|
64
|
+
verified_at INTEGER,
|
|
65
|
+
verified_via TEXT,
|
|
66
|
+
created_at INTEGER NOT NULL
|
|
67
|
+
)
|
|
68
|
+
`);
|
|
69
|
+
raw.exec(/*sql*/ `
|
|
70
|
+
CREATE TABLE assistant_ingress_members (
|
|
71
|
+
id TEXT PRIMARY KEY,
|
|
72
|
+
source_channel TEXT NOT NULL,
|
|
73
|
+
external_user_id TEXT,
|
|
74
|
+
external_chat_id TEXT,
|
|
75
|
+
display_name TEXT,
|
|
76
|
+
username TEXT,
|
|
77
|
+
status TEXT NOT NULL,
|
|
78
|
+
policy TEXT,
|
|
79
|
+
invite_id TEXT,
|
|
80
|
+
revoked_reason TEXT,
|
|
81
|
+
blocked_reason TEXT,
|
|
82
|
+
last_seen_at INTEGER,
|
|
83
|
+
created_at INTEGER NOT NULL,
|
|
84
|
+
updated_at INTEGER
|
|
85
|
+
)
|
|
86
|
+
`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function tableExists(raw: Database, name: string): boolean {
|
|
90
|
+
return !!raw
|
|
91
|
+
.prepare(`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ?`)
|
|
92
|
+
.get(name);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function seedLegacyRows(raw: Database): void {
|
|
96
|
+
raw.run(
|
|
97
|
+
`INSERT INTO channel_guardian_bindings
|
|
98
|
+
(id, channel, status, guardian_external_user_id, guardian_principal_id,
|
|
99
|
+
guardian_delivery_chat_id, metadata_json, verified_at, verified_via, created_at)
|
|
100
|
+
VALUES ('g1','telegram','active','U-guardian','prin-1','chat-1',NULL,1000,'telegram',1000)`,
|
|
101
|
+
);
|
|
102
|
+
raw.run(
|
|
103
|
+
`INSERT INTO assistant_ingress_members
|
|
104
|
+
(id, source_channel, external_user_id, external_chat_id, display_name,
|
|
105
|
+
username, status, policy, invite_id, revoked_reason, blocked_reason,
|
|
106
|
+
last_seen_at, created_at, updated_at)
|
|
107
|
+
VALUES ('m1','slack','U-member',NULL,'Member One','member1','active','allow',
|
|
108
|
+
NULL,NULL,NULL,2000,2000,2000)`,
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
describe("migration 131 — drop legacy member/guardian tables", () => {
|
|
113
|
+
test("syncs stragglers then drops the legacy tables when the column is present", () => {
|
|
114
|
+
const db = createTestDb();
|
|
115
|
+
const raw = getSqliteFrom(db);
|
|
116
|
+
bootstrap(db);
|
|
117
|
+
createLegacyTables(raw);
|
|
118
|
+
seedLegacyRows(raw);
|
|
119
|
+
|
|
120
|
+
migrateDropLegacyMemberGuardianTables(db);
|
|
121
|
+
|
|
122
|
+
// The straggler rows were synced into contact_channels.
|
|
123
|
+
const synced = raw
|
|
124
|
+
.prepare(`SELECT external_user_id FROM contact_channels ORDER BY id`)
|
|
125
|
+
.all() as { external_user_id: string | null }[];
|
|
126
|
+
expect(synced.map((r) => r.external_user_id).sort()).toEqual([
|
|
127
|
+
"U-guardian",
|
|
128
|
+
"U-member",
|
|
129
|
+
]);
|
|
130
|
+
|
|
131
|
+
// The legacy tables were removed.
|
|
132
|
+
expect(tableExists(raw, "channel_guardian_bindings")).toBe(false);
|
|
133
|
+
expect(tableExists(raw, "assistant_ingress_members")).toBe(false);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test("drops the legacy tables without throwing when external_user_id is absent (re-run after migration 294)", () => {
|
|
137
|
+
const db = createTestDb();
|
|
138
|
+
const raw = getSqliteFrom(db);
|
|
139
|
+
bootstrap(db);
|
|
140
|
+
createLegacyTables(raw);
|
|
141
|
+
seedLegacyRows(raw);
|
|
142
|
+
|
|
143
|
+
// Simulate a later startup where migration 294 has already dropped the
|
|
144
|
+
// index + column. The sync references external_user_id, so 131 must skip it
|
|
145
|
+
// and still drop the tables rather than failing on every boot.
|
|
146
|
+
raw.run("DROP INDEX IF EXISTS idx_contact_channels_type_ext_user");
|
|
147
|
+
raw.run("ALTER TABLE contact_channels DROP COLUMN external_user_id");
|
|
148
|
+
|
|
149
|
+
expect(() => migrateDropLegacyMemberGuardianTables(db)).not.toThrow();
|
|
150
|
+
|
|
151
|
+
expect(tableExists(raw, "channel_guardian_bindings")).toBe(false);
|
|
152
|
+
expect(tableExists(raw, "assistant_ingress_members")).toBe(false);
|
|
153
|
+
});
|
|
154
|
+
});
|
|
@@ -568,4 +568,35 @@ describe("migration 287 — dedup case collisions + drop ext_user indexes", () =
|
|
|
568
568
|
// Original casing preserved
|
|
569
569
|
expect(channels[0]!.address).toBe("U999");
|
|
570
570
|
});
|
|
571
|
+
|
|
572
|
+
test("no-op when external_user_id column is absent (re-run after migration 294)", () => {
|
|
573
|
+
const db = createTestDb();
|
|
574
|
+
const raw = getSqliteFrom(db);
|
|
575
|
+
bootstrap(db);
|
|
576
|
+
|
|
577
|
+
insertContact(raw, "c1");
|
|
578
|
+
insertChannel(raw, {
|
|
579
|
+
id: "ch1",
|
|
580
|
+
contactId: "c1",
|
|
581
|
+
type: "slack",
|
|
582
|
+
address: "U999",
|
|
583
|
+
externalUserId: "U999",
|
|
584
|
+
status: "active",
|
|
585
|
+
updatedAt: 1000,
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
// Simulate a later startup where migration 294 has already dropped the
|
|
589
|
+
// index and column. Migration steps re-run on every startup, so this must
|
|
590
|
+
// tolerate the dropped column rather than throwing "no such column".
|
|
591
|
+
raw.run("DROP INDEX IF EXISTS idx_contact_channels_type_ext_user");
|
|
592
|
+
raw.run("ALTER TABLE contact_channels DROP COLUMN external_user_id");
|
|
593
|
+
|
|
594
|
+
expect(() => migrateContactChannelsUniqueExtUser(db)).not.toThrow();
|
|
595
|
+
|
|
596
|
+
const rows = raw
|
|
597
|
+
.prepare("SELECT id, address FROM contact_channels")
|
|
598
|
+
.all() as { id: string; address: string }[];
|
|
599
|
+
expect(rows).toHaveLength(1);
|
|
600
|
+
expect(rows[0]!.address).toBe("U999");
|
|
601
|
+
});
|
|
571
602
|
});
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
2
|
+
import { describe, expect, test } from "bun:test";
|
|
3
|
+
|
|
4
|
+
import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
5
|
+
|
|
6
|
+
import { getSqliteFrom } from "../../db-connection.js";
|
|
7
|
+
import * as schema from "../../schema.js";
|
|
8
|
+
import { migrateContactChannelsRenormalizeAddresses } from "../291-contact-channels-renormalize-addresses.js";
|
|
9
|
+
|
|
10
|
+
interface ChannelRow {
|
|
11
|
+
id: string;
|
|
12
|
+
contact_id: string;
|
|
13
|
+
type: string;
|
|
14
|
+
address: string;
|
|
15
|
+
external_user_id: string | null;
|
|
16
|
+
status: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function createTestDb() {
|
|
20
|
+
const sqlite = new Database(":memory:");
|
|
21
|
+
sqlite.exec("PRAGMA journal_mode=WAL");
|
|
22
|
+
sqlite.exec("PRAGMA foreign_keys = ON");
|
|
23
|
+
return drizzle(sqlite, { schema });
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function bootstrap(db: ReturnType<typeof createTestDb>): void {
|
|
27
|
+
const raw = getSqliteFrom(db);
|
|
28
|
+
raw.exec(/*sql*/ `
|
|
29
|
+
CREATE TABLE contacts (
|
|
30
|
+
id TEXT PRIMARY KEY,
|
|
31
|
+
display_name TEXT NOT NULL,
|
|
32
|
+
created_at INTEGER NOT NULL,
|
|
33
|
+
updated_at INTEGER NOT NULL,
|
|
34
|
+
role TEXT NOT NULL DEFAULT 'contact',
|
|
35
|
+
contact_type TEXT NOT NULL DEFAULT 'human'
|
|
36
|
+
)
|
|
37
|
+
`);
|
|
38
|
+
raw.exec(/*sql*/ `
|
|
39
|
+
CREATE TABLE contact_channels (
|
|
40
|
+
id TEXT PRIMARY KEY,
|
|
41
|
+
contact_id TEXT NOT NULL REFERENCES contacts(id) ON DELETE CASCADE,
|
|
42
|
+
type TEXT NOT NULL,
|
|
43
|
+
address TEXT NOT NULL,
|
|
44
|
+
is_primary INTEGER NOT NULL DEFAULT 0,
|
|
45
|
+
external_user_id TEXT,
|
|
46
|
+
external_chat_id TEXT,
|
|
47
|
+
status TEXT NOT NULL DEFAULT 'unverified',
|
|
48
|
+
policy TEXT NOT NULL DEFAULT 'allow',
|
|
49
|
+
interaction_count INTEGER NOT NULL DEFAULT 0,
|
|
50
|
+
updated_at INTEGER,
|
|
51
|
+
created_at INTEGER NOT NULL
|
|
52
|
+
)
|
|
53
|
+
`);
|
|
54
|
+
raw.exec(/*sql*/ `
|
|
55
|
+
CREATE UNIQUE INDEX idx_contact_channels_type_address
|
|
56
|
+
ON contact_channels(type, address)
|
|
57
|
+
`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function insertContact(raw: Database, id: string): void {
|
|
61
|
+
const now = Date.now();
|
|
62
|
+
raw
|
|
63
|
+
.query(
|
|
64
|
+
`INSERT INTO contacts (id, display_name, created_at, updated_at) VALUES (?, ?, ?, ?)`,
|
|
65
|
+
)
|
|
66
|
+
.run(id, `Contact ${id}`, now, now);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function insertChannel(
|
|
70
|
+
raw: Database,
|
|
71
|
+
opts: {
|
|
72
|
+
id: string;
|
|
73
|
+
contactId: string;
|
|
74
|
+
type: string;
|
|
75
|
+
address: string;
|
|
76
|
+
externalUserId: string | null;
|
|
77
|
+
status: string;
|
|
78
|
+
},
|
|
79
|
+
): void {
|
|
80
|
+
const now = Date.now();
|
|
81
|
+
raw
|
|
82
|
+
.query(
|
|
83
|
+
`INSERT INTO contact_channels (id, contact_id, type, address, external_user_id, status, updated_at, created_at)
|
|
84
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
85
|
+
)
|
|
86
|
+
.run(
|
|
87
|
+
opts.id,
|
|
88
|
+
opts.contactId,
|
|
89
|
+
opts.type,
|
|
90
|
+
opts.address,
|
|
91
|
+
opts.externalUserId,
|
|
92
|
+
opts.status,
|
|
93
|
+
now,
|
|
94
|
+
now,
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
describe("migration 291 — renormalize addresses", () => {
|
|
99
|
+
test("restores Slack address casing from external_user_id", () => {
|
|
100
|
+
const db = createTestDb();
|
|
101
|
+
bootstrap(db);
|
|
102
|
+
const raw = getSqliteFrom(db);
|
|
103
|
+
|
|
104
|
+
insertContact(raw, "c1");
|
|
105
|
+
insertChannel(raw, {
|
|
106
|
+
id: "ch1",
|
|
107
|
+
contactId: "c1",
|
|
108
|
+
type: "slack",
|
|
109
|
+
address: "u12345abc",
|
|
110
|
+
externalUserId: "U12345ABC",
|
|
111
|
+
status: "active",
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
migrateContactChannelsRenormalizeAddresses(db);
|
|
115
|
+
|
|
116
|
+
const rows = raw
|
|
117
|
+
.prepare("SELECT * FROM contact_channels")
|
|
118
|
+
.all() as ChannelRow[];
|
|
119
|
+
expect(rows).toHaveLength(1);
|
|
120
|
+
expect(rows[0].address).toBe("U12345ABC");
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test("preserves lowercase email addresses (does not overwrite with mixed-case external_user_id)", () => {
|
|
124
|
+
const db = createTestDb();
|
|
125
|
+
bootstrap(db);
|
|
126
|
+
const raw = getSqliteFrom(db);
|
|
127
|
+
|
|
128
|
+
insertContact(raw, "c1");
|
|
129
|
+
insertChannel(raw, {
|
|
130
|
+
id: "ch1",
|
|
131
|
+
contactId: "c1",
|
|
132
|
+
type: "email",
|
|
133
|
+
address: "user@example.com",
|
|
134
|
+
// generic-examples:ignore-next-line — reason: testing mixed-case email normalization
|
|
135
|
+
externalUserId: "User@Example.com",
|
|
136
|
+
status: "active",
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
migrateContactChannelsRenormalizeAddresses(db);
|
|
140
|
+
|
|
141
|
+
const rows = raw
|
|
142
|
+
.prepare("SELECT * FROM contact_channels")
|
|
143
|
+
.all() as ChannelRow[];
|
|
144
|
+
expect(rows).toHaveLength(1);
|
|
145
|
+
expect(rows[0].address).toBe("user@example.com");
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test("lowercases email address if it was stored incorrectly", () => {
|
|
149
|
+
const db = createTestDb();
|
|
150
|
+
bootstrap(db);
|
|
151
|
+
const raw = getSqliteFrom(db);
|
|
152
|
+
|
|
153
|
+
insertContact(raw, "c1");
|
|
154
|
+
insertChannel(raw, {
|
|
155
|
+
id: "ch1",
|
|
156
|
+
contactId: "c1",
|
|
157
|
+
type: "email",
|
|
158
|
+
// generic-examples:ignore-next-line — reason: testing mixed-case email stored incorrectly
|
|
159
|
+
address: "User@Example.com",
|
|
160
|
+
// generic-examples:ignore-next-line — reason: testing mixed-case email stored incorrectly
|
|
161
|
+
externalUserId: "User@Example.com",
|
|
162
|
+
status: "active",
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
migrateContactChannelsRenormalizeAddresses(db);
|
|
166
|
+
|
|
167
|
+
const rows = raw
|
|
168
|
+
.prepare("SELECT * FROM contact_channels")
|
|
169
|
+
.all() as ChannelRow[];
|
|
170
|
+
expect(rows).toHaveLength(1);
|
|
171
|
+
expect(rows[0].address).toBe("user@example.com");
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test("removes cross-column blocker before normalization", () => {
|
|
175
|
+
const db = createTestDb();
|
|
176
|
+
bootstrap(db);
|
|
177
|
+
const raw = getSqliteFrom(db);
|
|
178
|
+
|
|
179
|
+
insertContact(raw, "c1");
|
|
180
|
+
insertContact(raw, "c2");
|
|
181
|
+
// Blocker: NULL external_user_id, address matches normalizer's external_user_id
|
|
182
|
+
insertChannel(raw, {
|
|
183
|
+
id: "blocker",
|
|
184
|
+
contactId: "c1",
|
|
185
|
+
type: "slack",
|
|
186
|
+
address: "u12345abc",
|
|
187
|
+
externalUserId: null,
|
|
188
|
+
status: "unverified",
|
|
189
|
+
});
|
|
190
|
+
// Normalizer: has external_user_id that will become its address
|
|
191
|
+
insertChannel(raw, {
|
|
192
|
+
id: "normalizer",
|
|
193
|
+
contactId: "c2",
|
|
194
|
+
type: "slack",
|
|
195
|
+
address: "old-address",
|
|
196
|
+
externalUserId: "U12345ABC",
|
|
197
|
+
status: "active",
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
migrateContactChannelsRenormalizeAddresses(db);
|
|
201
|
+
|
|
202
|
+
const rows = raw
|
|
203
|
+
.prepare("SELECT * FROM contact_channels ORDER BY id")
|
|
204
|
+
.all() as ChannelRow[];
|
|
205
|
+
expect(rows).toHaveLength(1);
|
|
206
|
+
expect(rows[0].id).toBe("normalizer");
|
|
207
|
+
expect(rows[0].address).toBe("U12345ABC");
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
test("idempotent — safe to run twice", () => {
|
|
211
|
+
const db = createTestDb();
|
|
212
|
+
bootstrap(db);
|
|
213
|
+
const raw = getSqliteFrom(db);
|
|
214
|
+
|
|
215
|
+
insertContact(raw, "c1");
|
|
216
|
+
insertChannel(raw, {
|
|
217
|
+
id: "ch1",
|
|
218
|
+
contactId: "c1",
|
|
219
|
+
type: "slack",
|
|
220
|
+
address: "u12345abc",
|
|
221
|
+
externalUserId: "U12345ABC",
|
|
222
|
+
status: "active",
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
migrateContactChannelsRenormalizeAddresses(db);
|
|
226
|
+
migrateContactChannelsRenormalizeAddresses(db);
|
|
227
|
+
|
|
228
|
+
const rows = raw
|
|
229
|
+
.prepare("SELECT * FROM contact_channels")
|
|
230
|
+
.all() as ChannelRow[];
|
|
231
|
+
expect(rows).toHaveLength(1);
|
|
232
|
+
expect(rows[0].address).toBe("U12345ABC");
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
test("does not overwrite canonical E.164 phone address with raw external_user_id", () => {
|
|
236
|
+
const db = createTestDb();
|
|
237
|
+
bootstrap(db);
|
|
238
|
+
const raw = getSqliteFrom(db);
|
|
239
|
+
|
|
240
|
+
insertContact(raw, "c1");
|
|
241
|
+
// Phone channel whose address was canonicalized to E.164 (+1 prefix)
|
|
242
|
+
// but external_user_id still holds the raw 10-digit number.
|
|
243
|
+
insertChannel(raw, {
|
|
244
|
+
id: "ch1",
|
|
245
|
+
contactId: "c1",
|
|
246
|
+
type: "phone",
|
|
247
|
+
// generic-examples:ignore-next-line — reason: testing E.164 canonicalization preservation
|
|
248
|
+
address: "+15550101234",
|
|
249
|
+
// generic-examples:ignore-next-line — reason: testing raw 10-digit phone before E.164 normalization
|
|
250
|
+
externalUserId: "5550101234",
|
|
251
|
+
status: "active",
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
migrateContactChannelsRenormalizeAddresses(db);
|
|
255
|
+
|
|
256
|
+
const rows = raw
|
|
257
|
+
.prepare("SELECT * FROM contact_channels")
|
|
258
|
+
.all() as ChannelRow[];
|
|
259
|
+
expect(rows).toHaveLength(1);
|
|
260
|
+
// generic-examples:ignore-next-line — reason: verifying E.164 address preserved
|
|
261
|
+
expect(rows[0].address).toBe("+15550101234");
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
test("does not overwrite canonical WhatsApp address with raw external_user_id", () => {
|
|
265
|
+
const db = createTestDb();
|
|
266
|
+
bootstrap(db);
|
|
267
|
+
const raw = getSqliteFrom(db);
|
|
268
|
+
|
|
269
|
+
insertContact(raw, "c1");
|
|
270
|
+
insertChannel(raw, {
|
|
271
|
+
id: "ch1",
|
|
272
|
+
contactId: "c1",
|
|
273
|
+
type: "whatsapp",
|
|
274
|
+
address: "+447911123456",
|
|
275
|
+
externalUserId: "447911123456",
|
|
276
|
+
status: "active",
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
migrateContactChannelsRenormalizeAddresses(db);
|
|
280
|
+
|
|
281
|
+
const rows = raw
|
|
282
|
+
.prepare("SELECT * FROM contact_channels")
|
|
283
|
+
.all() as ChannelRow[];
|
|
284
|
+
expect(rows).toHaveLength(1);
|
|
285
|
+
expect(rows[0].address).toBe("+447911123456");
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
test("no-op when address already matches external_user_id", () => {
|
|
289
|
+
const db = createTestDb();
|
|
290
|
+
bootstrap(db);
|
|
291
|
+
const raw = getSqliteFrom(db);
|
|
292
|
+
|
|
293
|
+
insertContact(raw, "c1");
|
|
294
|
+
insertChannel(raw, {
|
|
295
|
+
id: "ch1",
|
|
296
|
+
contactId: "c1",
|
|
297
|
+
type: "slack",
|
|
298
|
+
address: "U12345ABC",
|
|
299
|
+
externalUserId: "U12345ABC",
|
|
300
|
+
status: "active",
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
migrateContactChannelsRenormalizeAddresses(db);
|
|
304
|
+
|
|
305
|
+
const rows = raw
|
|
306
|
+
.prepare("SELECT * FROM contact_channels")
|
|
307
|
+
.all() as ChannelRow[];
|
|
308
|
+
expect(rows).toHaveLength(1);
|
|
309
|
+
expect(rows[0].address).toBe("U12345ABC");
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
test("no-op when external_user_id column is absent (re-run after migration 294)", () => {
|
|
313
|
+
const db = createTestDb();
|
|
314
|
+
bootstrap(db);
|
|
315
|
+
const raw = getSqliteFrom(db);
|
|
316
|
+
|
|
317
|
+
insertContact(raw, "c1");
|
|
318
|
+
insertChannel(raw, {
|
|
319
|
+
id: "ch1",
|
|
320
|
+
contactId: "c1",
|
|
321
|
+
type: "slack",
|
|
322
|
+
address: "u12345abc",
|
|
323
|
+
externalUserId: "U12345ABC",
|
|
324
|
+
status: "active",
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
// Simulate a later startup where migration 294 has already dropped the
|
|
328
|
+
// index and column. Migration steps re-run on every startup, so this must
|
|
329
|
+
// tolerate the dropped column rather than throwing "no such column".
|
|
330
|
+
raw.run("DROP INDEX IF EXISTS idx_contact_channels_type_ext_user");
|
|
331
|
+
raw.run("ALTER TABLE contact_channels DROP COLUMN external_user_id");
|
|
332
|
+
|
|
333
|
+
expect(() => migrateContactChannelsRenormalizeAddresses(db)).not.toThrow();
|
|
334
|
+
|
|
335
|
+
const rows = raw
|
|
336
|
+
.prepare("SELECT id, address FROM contact_channels")
|
|
337
|
+
.all() as { id: string; address: string }[];
|
|
338
|
+
expect(rows).toHaveLength(1);
|
|
339
|
+
expect(rows[0].address).toBe("u12345abc");
|
|
340
|
+
});
|
|
341
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
2
|
+
import { describe, expect, test } from "bun:test";
|
|
3
|
+
|
|
4
|
+
import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
5
|
+
|
|
6
|
+
import * as schema from "../../schema.js";
|
|
7
|
+
import { type MigrationStep, runMigrationSteps } from "../run-migrations.js";
|
|
8
|
+
|
|
9
|
+
function createTestDb() {
|
|
10
|
+
const sqlite = new Database(":memory:");
|
|
11
|
+
sqlite.exec("PRAGMA journal_mode=WAL");
|
|
12
|
+
sqlite.exec("PRAGMA foreign_keys = ON");
|
|
13
|
+
return drizzle(sqlite, { schema });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
describe("runMigrationSteps — checkpointing", () => {
|
|
17
|
+
// Skipped: documents the post-checkpointing contract and fails against the
|
|
18
|
+
// current behavior, which re-runs every forward step on every boot. Promote
|
|
19
|
+
// to `test` when true checkpointing of initial migrations lands.
|
|
20
|
+
test.skip("does not re-execute a step that was already applied on a prior run", () => {
|
|
21
|
+
/**
|
|
22
|
+
* Booting an already-migrated database must not re-run forward migration
|
|
23
|
+
* steps. With true checkpointing each step's body executes at most once
|
|
24
|
+
* across boots; without it every step re-runs on every startup — the
|
|
25
|
+
* ~200-step re-probe that floors daemon startup at ~30s.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
// GIVEN three dummy migration steps that count how often their body runs
|
|
29
|
+
const calls = { a: 0, b: 0, c: 0 };
|
|
30
|
+
const steps: MigrationStep[] = [
|
|
31
|
+
function dummyMigrationA() {
|
|
32
|
+
calls.a++;
|
|
33
|
+
},
|
|
34
|
+
function dummyMigrationB() {
|
|
35
|
+
calls.b++;
|
|
36
|
+
},
|
|
37
|
+
function dummyMigrationC() {
|
|
38
|
+
calls.c++;
|
|
39
|
+
},
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
// AND a database that has already been migrated once
|
|
43
|
+
const db = createTestDb();
|
|
44
|
+
runMigrationSteps(db, steps);
|
|
45
|
+
|
|
46
|
+
// WHEN the same steps run again against the already-migrated database
|
|
47
|
+
runMigrationSteps(db, steps);
|
|
48
|
+
|
|
49
|
+
// THEN no step body executes a second time
|
|
50
|
+
expect(calls).toEqual({ a: 1, b: 1, c: 1 });
|
|
51
|
+
});
|
|
52
|
+
});
|
|
@@ -282,6 +282,12 @@ export { migrateConversationOriginChannelIndex } from "./287-conversation-origin
|
|
|
282
282
|
export { migrateBackfillOriginChannelFromBindings } from "./288-backfill-origin-channel-from-bindings.js";
|
|
283
283
|
export { migrateContactChannelsUniqueExtUser } from "./289-contact-channels-unique-ext-user.js";
|
|
284
284
|
export { migrateScheduleCapabilities } from "./290-schedule-capabilities.js";
|
|
285
|
+
export { migrateContactChannelsRenormalizeAddresses } from "./291-contact-channels-renormalize-addresses.js";
|
|
286
|
+
export { migrateScheduleDefaultNoReuseConversation } from "./292-schedule-default-no-reuse-conversation.js";
|
|
287
|
+
export { migrateWorkflowJournalLeafTokens } from "./293-workflow-journal-leaf-tokens.js";
|
|
288
|
+
export { migrateDropExternalUserId } from "./294-drop-external-user-id.js";
|
|
289
|
+
export { dropApprovalPromptTsTrackerTable } from "./295-drop-approval-prompt-ts-tracker.js";
|
|
290
|
+
export { migrateRewriteBalancedEconomyProfilePins } from "./296-rewrite-balanced-economy-profile-pins.js";
|
|
285
291
|
export {
|
|
286
292
|
MIGRATION_REGISTRY,
|
|
287
293
|
type MigrationRegistryEntry,
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { getLogger } from "../../util/logger.js";
|
|
2
|
+
import type { DrizzleDb } from "../db-connection.js";
|
|
3
|
+
|
|
4
|
+
const log = getLogger("db-init");
|
|
5
|
+
|
|
6
|
+
/** A single forward migration step, identified for logging by its `.name`. */
|
|
7
|
+
export type MigrationStep = (database: DrizzleDb) => void;
|
|
8
|
+
|
|
9
|
+
export interface MigrationRunResult {
|
|
10
|
+
/** Steps whose body threw. */
|
|
11
|
+
failed: string[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Run the ordered list of forward migration steps against the database.
|
|
16
|
+
*
|
|
17
|
+
* Individual step failures are caught and logged so one broken migration does
|
|
18
|
+
* not prevent independent later ones from succeeding.
|
|
19
|
+
*/
|
|
20
|
+
export function runMigrationSteps(
|
|
21
|
+
database: DrizzleDb,
|
|
22
|
+
steps: MigrationStep[],
|
|
23
|
+
): MigrationRunResult {
|
|
24
|
+
const failed: string[] = [];
|
|
25
|
+
|
|
26
|
+
for (const step of steps) {
|
|
27
|
+
try {
|
|
28
|
+
log.debug({ migration: step.name }, `Starting migration: ${step.name}`);
|
|
29
|
+
step(database);
|
|
30
|
+
log.debug({ migration: step.name }, `Migration succeeded: ${step.name}`);
|
|
31
|
+
} catch (err) {
|
|
32
|
+
failed.push(step.name);
|
|
33
|
+
log.error(
|
|
34
|
+
{ err, migration: step.name },
|
|
35
|
+
`Migration failed: ${step.name}`,
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return { failed };
|
|
41
|
+
}
|
|
@@ -220,7 +220,7 @@ export function validateMigrationState(
|
|
|
220
220
|
// registry entry — these are from a newer version of the daemon.
|
|
221
221
|
//
|
|
222
222
|
// The memory_checkpoints table is a general-purpose key-value store also
|
|
223
|
-
// used by non-migration subsystems (e.g., "
|
|
223
|
+
// used by non-migration subsystems (e.g., "empty_state:greeting:text",
|
|
224
224
|
// "conversation_starters:item_count_at_last_gen"). Filter to only keys
|
|
225
225
|
// that follow migration naming conventions before comparing against the
|
|
226
226
|
// registry to avoid false-positive warnings.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { and, asc, eq, gt, or } from "drizzle-orm";
|
|
2
2
|
import { v4 as uuid } from "uuid";
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { getCachedShareAnalytics } from "../platform/consent-cache.js";
|
|
5
5
|
import {
|
|
6
6
|
ACTIVATION_AB_VARIANT,
|
|
7
7
|
ACTIVATION_FUNNEL_VERSION,
|
|
@@ -58,7 +58,7 @@ function insertOnboardingEvent(event: OnboardingEvent): OnboardingEvent {
|
|
|
58
58
|
export function recordOnboardingEvent(
|
|
59
59
|
params: RecordOnboardingEventParams,
|
|
60
60
|
): OnboardingEvent | null {
|
|
61
|
-
if (!
|
|
61
|
+
if (!getCachedShareAnalytics()) return null;
|
|
62
62
|
return insertOnboardingEvent({
|
|
63
63
|
id: uuid(),
|
|
64
64
|
createdAt: Date.now(),
|
|
@@ -93,7 +93,7 @@ export function recordActivationEvent(params: {
|
|
|
93
93
|
userId?: string | null;
|
|
94
94
|
abVariant?: string;
|
|
95
95
|
}): OnboardingEvent | null {
|
|
96
|
-
if (!
|
|
96
|
+
if (!getCachedShareAnalytics()) return null;
|
|
97
97
|
const createdAt = Date.now();
|
|
98
98
|
return insertOnboardingEvent({
|
|
99
99
|
id: uuid(),
|
|
@@ -26,7 +26,6 @@ export const contactChannels = sqliteTable(
|
|
|
26
26
|
isPrimary: integer("is_primary", { mode: "boolean" })
|
|
27
27
|
.notNull()
|
|
28
28
|
.default(false),
|
|
29
|
-
externalUserId: text("external_user_id"), // channel-native user ID (e.g., Telegram numeric ID, E.164 phone)
|
|
30
29
|
externalChatId: text("external_chat_id"), // delivery/notification routing address (e.g., Telegram chat ID)
|
|
31
30
|
status: text("status").notNull().default("unverified"), // 'active' | 'pending' | 'revoked' | 'blocked' | 'unverified'
|
|
32
31
|
policy: text("policy").notNull().default("allow"), // 'allow' | 'deny' | 'escalate'
|
|
@@ -42,10 +41,6 @@ export const contactChannels = sqliteTable(
|
|
|
42
41
|
createdAt: integer("created_at").notNull(),
|
|
43
42
|
},
|
|
44
43
|
(table) => [
|
|
45
|
-
index("idx_contact_channels_type_ext_user").on(
|
|
46
|
-
table.type,
|
|
47
|
-
table.externalUserId,
|
|
48
|
-
),
|
|
49
44
|
index("idx_contact_channels_type_ext_chat").on(
|
|
50
45
|
table.type,
|
|
51
46
|
table.externalChatId,
|