@vellumai/assistant 0.5.10 → 0.5.12
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/AGENTS.md +8 -0
- package/ARCHITECTURE.md +43 -43
- package/Dockerfile +3 -0
- package/docs/architecture/integrations.md +37 -42
- package/docs/architecture/memory.md +7 -12
- package/docs/credential-execution-service.md +9 -9
- package/docs/skills.md +1 -1
- package/node_modules/@vellumai/ces-contracts/src/__tests__/grants.test.ts +7 -7
- package/node_modules/@vellumai/ces-contracts/src/handles.ts +5 -4
- package/node_modules/@vellumai/credential-storage/src/index.ts +3 -3
- package/node_modules/@vellumai/credential-storage/src/static-credentials.ts +1 -1
- package/openapi.yaml +7208 -0
- package/package.json +2 -1
- package/scripts/generate-openapi.ts +562 -0
- package/src/__tests__/acp-session.test.ts +239 -44
- package/src/__tests__/assistant-feature-flag-guard.test.ts +8 -8
- package/src/__tests__/assistant-feature-flag-guardrails.test.ts +5 -86
- package/src/__tests__/assistant-feature-flags-integration.test.ts +7 -14
- package/src/__tests__/browser-skill-endstate.test.ts +1 -1
- package/src/__tests__/btw-routes.test.ts +8 -0
- package/src/__tests__/bundled-skill-retrieval-guard.test.ts +10 -10
- package/src/__tests__/catalog-cache.test.ts +164 -0
- package/src/__tests__/catalog-search.test.ts +61 -0
- package/src/__tests__/channel-approvals.test.ts +7 -7
- package/src/__tests__/channel-readiness-service.test.ts +41 -0
- package/src/__tests__/cli-command-risk-guard.test.ts +181 -6
- package/src/__tests__/config-schema.test.ts +10 -2
- package/src/__tests__/context-memory-e2e.test.ts +2 -6
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +396 -0
- package/src/__tests__/conversation-error.test.ts +3 -2
- package/src/__tests__/conversation-skill-tools.test.ts +1 -3
- package/src/__tests__/conversation-title-service.test.ts +2 -15
- package/src/__tests__/credential-execution-feature-gates.test.ts +4 -8
- package/src/__tests__/credential-execution-managed-contract.test.ts +8 -8
- package/src/__tests__/credential-security-e2e.test.ts +4 -4
- package/src/__tests__/credential-security-invariants.test.ts +12 -18
- package/src/__tests__/credential-vault-unit.test.ts +32 -34
- package/src/__tests__/credential-vault.test.ts +25 -33
- package/src/__tests__/credentials-cli.test.ts +3 -3
- package/src/__tests__/daemon-credential-client.test.ts +2 -2
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +1 -1
- package/src/__tests__/gateway-only-guard.test.ts +3 -0
- package/src/__tests__/heartbeat-service.test.ts +35 -0
- package/src/__tests__/host-bash-proxy.test.ts +79 -0
- package/src/__tests__/host-cu-proxy.test.ts +90 -0
- package/src/__tests__/host-file-proxy.test.ts +89 -0
- package/src/__tests__/host-shell-tool.test.ts +1 -1
- package/src/__tests__/inline-skill-load-permissions.test.ts +3 -3
- package/src/__tests__/integration-status.test.ts +5 -5
- package/src/__tests__/list-messages-attachments.test.ts +171 -0
- package/src/__tests__/llm-request-log-turn-query.test.ts +64 -0
- package/src/__tests__/log-export-workspace.test.ts +1 -1
- package/src/__tests__/mcp-abort-signal.test.ts +205 -0
- package/src/__tests__/mcp-client-auth.test.ts +1 -1
- package/src/__tests__/memory-lifecycle-e2e.test.ts +2 -2
- package/src/__tests__/memory-recall-log-store.test.ts +182 -0
- package/src/__tests__/memory-recall-quality.test.ts +6 -8
- package/src/__tests__/memory-regressions.test.ts +53 -42
- package/src/__tests__/memory-retrieval.benchmark.test.ts +5 -9
- package/src/__tests__/messaging-send-tool.test.ts +5 -5
- package/src/__tests__/messaging-skill-split.test.ts +2 -17
- package/src/__tests__/notification-telegram-adapter.test.ts +125 -0
- package/src/__tests__/oauth-cli.test.ts +203 -649
- package/src/__tests__/oauth-provider-profiles.test.ts +55 -20
- package/src/__tests__/oauth-scope-policy.test.ts +4 -6
- package/src/__tests__/onboarding-template-contract.test.ts +2 -2
- package/src/__tests__/platform-callback-registration.test.ts +119 -0
- package/src/__tests__/secret-ingress-channel.test.ts +261 -0
- package/src/__tests__/secret-ingress-cli.test.ts +201 -0
- package/src/__tests__/secret-ingress-http.test.ts +312 -0
- package/src/__tests__/secret-ingress.test.ts +283 -0
- package/src/__tests__/secret-onetime-send.test.ts +4 -4
- package/src/__tests__/secret-routes-managed-proxy.test.ts +78 -0
- package/src/__tests__/secure-keys-managed-failover.test.ts +73 -0
- package/src/__tests__/skill-feature-flags-integration.test.ts +4 -4
- package/src/__tests__/skill-feature-flags.test.ts +11 -19
- package/src/__tests__/skill-load-feature-flag.test.ts +1 -1
- package/src/__tests__/skill-load-inline-command.test.ts +3 -3
- package/src/__tests__/skill-load-inline-includes.test.ts +2 -2
- package/src/__tests__/skill-memory.test.ts +2 -4
- package/src/__tests__/skill-projection-feature-flag.test.ts +2 -4
- package/src/__tests__/skill-projection.benchmark.test.ts +1 -3
- package/src/__tests__/skills-uninstall.test.ts +2 -2
- package/src/__tests__/skills.test.ts +16 -2
- package/src/__tests__/slack-channel-config.test.ts +1 -1
- package/src/__tests__/slack-messaging-token-resolution.test.ts +22 -24
- package/src/__tests__/slack-share-routes.test.ts +5 -5
- package/src/__tests__/slack-skill.test.ts +5 -69
- package/src/__tests__/system-prompt.test.ts +39 -0
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +1 -1
- package/src/__tests__/workspace-migration-018-rekey-compound-credential-keys.test.ts +181 -0
- package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +5 -4
- package/src/acp/client-handler.ts +113 -31
- package/src/acp/session-manager.ts +29 -27
- package/src/approvals/guardian-request-resolvers.ts +1 -1
- package/src/cli/AGENTS.md +113 -0
- package/src/cli/commands/autonomy.ts +3 -5
- package/src/cli/commands/browser-relay.ts +2 -17
- package/src/cli/commands/contacts.ts +6 -4
- package/src/cli/commands/conversations.ts +13 -1
- package/src/cli/commands/credential-execution.ts +17 -3
- package/src/cli/commands/credentials.ts +2 -8
- package/src/cli/commands/memory.ts +2 -3
- package/src/cli/commands/oauth/__tests__/connect.test.ts +706 -0
- package/src/cli/commands/oauth/__tests__/disconnect.test.ts +686 -0
- package/src/cli/commands/oauth/__tests__/mode.test.ts +625 -0
- package/src/cli/commands/oauth/__tests__/ping.test.ts +631 -0
- package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +574 -0
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +416 -0
- package/src/cli/commands/oauth/__tests__/status.test.ts +551 -0
- package/src/cli/commands/oauth/__tests__/token.test.ts +420 -0
- package/src/cli/commands/oauth/apps.ts +87 -50
- package/src/cli/commands/oauth/connect.ts +405 -0
- package/src/cli/commands/oauth/disconnect.ts +285 -0
- package/src/cli/commands/oauth/index.ts +62 -20
- package/src/cli/commands/oauth/mode.ts +251 -0
- package/src/cli/commands/oauth/ping.ts +196 -0
- package/src/cli/commands/oauth/providers.ts +589 -55
- package/src/cli/commands/oauth/request.ts +564 -0
- package/src/cli/commands/oauth/shared.ts +114 -0
- package/src/cli/commands/oauth/status.ts +191 -0
- package/src/cli/commands/oauth/token.ts +150 -0
- package/src/cli/commands/platform/connect.ts +104 -0
- package/src/cli/commands/platform/disconnect.ts +118 -0
- package/src/cli/commands/platform/index.ts +252 -0
- package/src/cli/commands/sequence.ts +5 -4
- package/src/cli/commands/shotgun.ts +16 -0
- package/src/cli/commands/skills.ts +173 -41
- package/src/cli/commands/usage.ts +5 -11
- package/src/cli/lib/daemon-credential-client.ts +22 -38
- package/src/cli/program.ts +1 -1
- package/src/cli.ts +82 -17
- package/src/config/assistant-feature-flags.ts +77 -18
- package/src/config/bundled-skills/_shared/CLI_RETRIEVAL_PATTERN.md +1 -1
- package/src/config/bundled-skills/app-builder/tools/app-create.ts +1 -1
- package/src/config/bundled-skills/contacts/tools/google-contacts.ts +1 -1
- package/src/config/bundled-skills/conversations/SKILL.md +20 -0
- package/src/config/bundled-skills/conversations/TOOLS.json +23 -0
- package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +66 -0
- package/src/config/bundled-skills/gmail/SKILL.md +13 -13
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +3 -3
- package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +2 -2
- package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-label.ts +2 -2
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +1 -1
- package/src/config/bundled-skills/google-calendar/SKILL.md +10 -4
- package/src/config/bundled-skills/google-calendar/tools/shared.ts +1 -1
- package/src/config/bundled-skills/messaging/SKILL.md +19 -42
- package/src/config/bundled-skills/messaging/TOOLS.json +9 -9
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +1 -1
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +5 -2
- package/src/config/bundled-skills/messaging/tools/shared.ts +5 -6
- package/src/config/bundled-skills/notifications/SKILL.md +1 -1
- package/src/config/bundled-skills/schedule/SKILL.md +2 -2
- package/src/config/bundled-skills/settings/SKILL.md +5 -3
- package/src/config/bundled-skills/settings/TOOLS.json +17 -0
- package/src/config/bundled-skills/settings/tools/avatar-get.ts +50 -0
- package/src/config/bundled-skills/settings/tools/avatar-remove.ts +7 -0
- package/src/config/bundled-skills/settings/tools/avatar-update.ts +6 -1
- package/src/config/bundled-skills/settings/tools/identity-avatar.ts +55 -0
- package/src/config/bundled-skills/skills-catalog/SKILL.md +3 -3
- package/src/config/bundled-skills/slack/SKILL.md +58 -44
- package/src/config/bundled-tool-registry.ts +7 -19
- package/src/config/env.ts +5 -1
- package/src/config/feature-flag-registry.json +58 -42
- package/src/config/loader.ts +4 -0
- package/src/config/schemas/platform.ts +0 -8
- package/src/config/schemas/security.ts +9 -1
- package/src/config/schemas/services.ts +1 -1
- package/src/config/skill-state.ts +1 -3
- package/src/config/skills.ts +2 -4
- package/src/credential-execution/client.ts +1 -1
- package/src/credential-execution/feature-gates.ts +9 -16
- package/src/credential-execution/process-manager.ts +12 -0
- package/src/daemon/config-watcher.ts +4 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +10 -0
- package/src/daemon/conversation-agent-loop.ts +51 -2
- package/src/daemon/conversation-error.ts +36 -6
- package/src/daemon/conversation-memory.ts +0 -1
- package/src/daemon/conversation-messaging.ts +9 -0
- package/src/daemon/conversation-runtime-assembly.ts +33 -0
- package/src/daemon/conversation-surfaces.ts +120 -14
- package/src/daemon/conversation.ts +5 -0
- package/src/daemon/handlers/config-slack-channel.ts +43 -1
- package/src/daemon/handlers/conversations.ts +41 -33
- package/src/daemon/handlers/skills.ts +148 -3
- package/src/daemon/host-bash-proxy.ts +16 -0
- package/src/daemon/host-cu-proxy.ts +16 -0
- package/src/daemon/host-file-proxy.ts +16 -0
- package/src/daemon/lifecycle.ts +73 -3
- package/src/daemon/message-types/acp.ts +0 -15
- package/src/daemon/message-types/conversations.ts +1 -0
- package/src/daemon/message-types/guardian-actions.ts +2 -0
- package/src/daemon/message-types/host-bash.ts +6 -1
- package/src/daemon/message-types/host-cu.ts +6 -1
- package/src/daemon/message-types/host-file.ts +6 -1
- package/src/daemon/message-types/integrations.ts +0 -1
- package/src/daemon/message-types/memory.ts +0 -1
- package/src/daemon/message-types/messages.ts +9 -1
- package/src/daemon/message-types/schedules.ts +9 -0
- package/src/daemon/server.ts +48 -9
- package/src/email/feature-gate.ts +3 -3
- package/src/heartbeat/heartbeat-service.ts +48 -0
- package/src/hooks/cli.ts +74 -0
- package/src/inbound/platform-callback-registration.ts +68 -19
- package/src/mcp/client.ts +6 -1
- package/src/mcp/manager.ts +2 -1
- package/src/mcp/mcp-oauth-provider.ts +3 -3
- package/src/memory/app-store.ts +3 -3
- package/src/memory/conversation-crud.ts +213 -0
- package/src/memory/conversation-key-store.ts +26 -0
- package/src/memory/conversation-title-service.ts +7 -17
- package/src/memory/db-init.ts +24 -0
- package/src/memory/embedding-local.ts +47 -2
- package/src/memory/indexer.ts +13 -10
- package/src/memory/items-extractor.ts +12 -4
- package/src/memory/job-utils.ts +5 -0
- package/src/memory/jobs-store.ts +10 -2
- package/src/memory/journal-memory.ts +6 -2
- package/src/memory/llm-request-log-store.ts +88 -21
- package/src/memory/memory-recall-log-store.ts +128 -0
- package/src/memory/migrations/194-memory-recall-logs.ts +50 -0
- package/src/memory/migrations/195-oauth-providers-ping-config.ts +23 -0
- package/src/memory/migrations/196-messages-conversation-created-at-index.ts +9 -0
- package/src/memory/migrations/196-strip-integration-prefix-from-provider-keys.ts +186 -0
- package/src/memory/migrations/197-oauth-providers-behavior-columns.ts +29 -0
- package/src/memory/migrations/198-drop-setup-skill-id-column.ts +11 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/retriever.test.ts +4 -5
- package/src/memory/schema/infrastructure.ts +31 -0
- package/src/memory/schema/oauth.ts +14 -0
- package/src/messaging/provider.ts +13 -12
- package/src/messaging/providers/gmail/adapter.ts +44 -35
- package/src/messaging/providers/slack/adapter.ts +63 -33
- package/src/messaging/providers/telegram-bot/adapter.ts +7 -9
- package/src/messaging/providers/whatsapp/adapter.ts +6 -8
- package/src/notifications/adapters/telegram.ts +78 -2
- package/src/oauth/__tests__/identity-verifier.test.ts +464 -0
- package/src/oauth/byo-connection.test.ts +22 -24
- package/src/oauth/connect-orchestrator.ts +79 -64
- package/src/oauth/connect-types.ts +7 -65
- package/src/oauth/connection-resolver.test.ts +13 -13
- package/src/oauth/connection-resolver.ts +3 -4
- package/src/oauth/identity-verifier.ts +177 -0
- package/src/oauth/manual-token-connection.ts +5 -5
- package/src/oauth/oauth-store.ts +251 -5
- package/src/oauth/platform-connection.test.ts +56 -6
- package/src/oauth/platform-connection.ts +8 -1
- package/src/oauth/seed-providers.ts +256 -34
- package/src/permissions/checker.ts +129 -3
- package/src/permissions/trust-client.ts +2 -2
- package/src/platform/client.ts +2 -2
- package/src/prompts/journal-context.ts +6 -1
- package/src/prompts/system-prompt.ts +43 -9
- package/src/prompts/templates/BOOTSTRAP.md +16 -5
- package/src/providers/anthropic/client.ts +139 -28
- package/src/runtime/auth/__tests__/middleware.test.ts +19 -0
- package/src/runtime/auth/route-policy.ts +0 -1
- package/src/runtime/btw-sidechain.ts +7 -1
- package/src/runtime/channel-approvals.ts +2 -2
- package/src/runtime/channel-readiness-service.ts +30 -7
- package/src/runtime/guardian-action-service.ts +7 -2
- package/src/runtime/http-router.ts +31 -0
- package/src/runtime/http-server.ts +26 -7
- package/src/runtime/http-types.ts +9 -0
- package/src/runtime/pending-interactions.ts +21 -3
- package/src/runtime/routes/acp-routes.ts +46 -28
- package/src/runtime/routes/app-management-routes.ts +123 -0
- package/src/runtime/routes/app-routes.ts +31 -0
- package/src/runtime/routes/approval-routes.ts +108 -3
- package/src/runtime/routes/attachment-routes.ts +45 -0
- package/src/runtime/routes/avatar-routes.ts +16 -0
- package/src/runtime/routes/brain-graph-routes.ts +18 -0
- package/src/runtime/routes/btw-routes.ts +20 -0
- package/src/runtime/routes/call-routes.ts +81 -0
- package/src/runtime/routes/channel-readiness-routes.ts +48 -7
- package/src/runtime/routes/channel-routes.ts +18 -0
- package/src/runtime/routes/channel-verification-routes.ts +49 -1
- package/src/runtime/routes/contact-routes.ts +77 -0
- package/src/runtime/routes/conversation-attention-routes.ts +37 -0
- package/src/runtime/routes/conversation-management-routes.ts +125 -0
- package/src/runtime/routes/conversation-query-routes.ts +78 -0
- package/src/runtime/routes/conversation-routes.ts +191 -39
- package/src/runtime/routes/conversation-starter-routes.ts +29 -0
- package/src/runtime/routes/debug-routes.ts +23 -0
- package/src/runtime/routes/diagnostics-routes.ts +30 -0
- package/src/runtime/routes/documents-routes.ts +42 -0
- package/src/runtime/routes/events-routes.ts +10 -0
- package/src/runtime/routes/global-search-routes.ts +35 -0
- package/src/runtime/routes/guardian-action-routes.ts +61 -3
- package/src/runtime/routes/guardian-approval-prompt.ts +77 -2
- package/src/runtime/routes/heartbeat-routes.ts +278 -0
- package/src/runtime/routes/host-bash-routes.ts +16 -1
- package/src/runtime/routes/host-cu-routes.ts +23 -1
- package/src/runtime/routes/host-file-routes.ts +18 -1
- package/src/runtime/routes/identity-routes.ts +35 -0
- package/src/runtime/routes/inbound-message-handler.ts +46 -25
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +21 -8
- package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +30 -2
- package/src/runtime/routes/inbound-stages/transcribe-audio.ts +1 -2
- package/src/runtime/routes/integrations/slack/share.ts +1 -1
- package/src/runtime/routes/integrations/twilio.ts +32 -22
- package/src/runtime/routes/invite-routes.ts +83 -0
- package/src/runtime/routes/log-export-routes.ts +14 -0
- package/src/runtime/routes/memory-item-routes.ts +99 -1
- package/src/runtime/routes/migration-rollback-routes.ts +25 -0
- package/src/runtime/routes/migration-routes.ts +40 -0
- package/src/runtime/routes/notification-routes.ts +20 -0
- package/src/runtime/routes/oauth-apps.ts +13 -4
- package/src/runtime/routes/pairing-routes.ts +15 -0
- package/src/runtime/routes/recording-routes.ts +72 -0
- package/src/runtime/routes/schedule-routes.ts +77 -5
- package/src/runtime/routes/secret-routes.ts +99 -14
- package/src/runtime/routes/settings-routes.ts +102 -19
- package/src/runtime/routes/skills-routes.ts +141 -18
- package/src/runtime/routes/subagents-routes.ts +38 -3
- package/src/runtime/routes/surface-action-routes.ts +66 -24
- package/src/runtime/routes/surface-content-routes.ts +20 -0
- package/src/runtime/routes/telemetry-routes.ts +12 -0
- package/src/runtime/routes/trace-event-routes.ts +25 -0
- package/src/runtime/routes/trust-rules-routes.ts +46 -0
- package/src/runtime/routes/tts-routes.ts +15 -4
- package/src/runtime/routes/upgrade-broadcast-routes.ts +38 -0
- package/src/runtime/routes/usage-routes.ts +59 -0
- package/src/runtime/routes/watch-routes.ts +28 -0
- package/src/runtime/routes/work-items-routes.ts +59 -0
- package/src/runtime/routes/workspace-commit-routes.ts +12 -0
- package/src/runtime/routes/workspace-routes.ts +102 -0
- package/src/schedule/integration-status.ts +2 -2
- package/src/schedule/scheduler.ts +7 -1
- package/src/security/AGENTS.md +7 -0
- package/src/security/ces-rpc-credential-backend.ts +19 -16
- package/src/security/credential-backend.ts +1 -1
- package/src/security/encrypted-store.ts +3 -3
- package/src/security/oauth-completion-page.ts +153 -0
- package/src/security/oauth2.ts +58 -17
- package/src/security/secret-ingress.ts +174 -0
- package/src/security/secret-patterns.ts +133 -0
- package/src/security/secret-scanner.ts +28 -117
- package/src/security/secure-keys.ts +207 -7
- package/src/security/token-manager.ts +3 -6
- package/src/signals/bash.ts +6 -1
- package/src/signals/confirm.ts +12 -8
- package/src/signals/user-message.ts +18 -3
- package/src/skills/catalog-cache.ts +44 -0
- package/src/skills/catalog-search.ts +18 -0
- package/src/skills/skill-memory.ts +1 -2
- package/src/tasks/task-runner.ts +7 -1
- package/src/tools/credentials/broker.ts +1 -1
- package/src/tools/credentials/metadata-store.ts +1 -1
- package/src/tools/credentials/post-connect-hooks.ts +1 -1
- package/src/tools/credentials/vault.ts +36 -48
- package/src/tools/host-terminal/host-shell.ts +16 -3
- package/src/tools/mcp/mcp-tool-factory.ts +2 -1
- package/src/tools/memory/definitions.ts +1 -1
- package/src/tools/memory/handlers.test.ts +2 -4
- package/src/tools/skills/load.ts +1 -1
- package/src/tools/skills/sandbox-runner.ts +16 -3
- package/src/tools/terminal/safe-env.ts +7 -0
- package/src/tools/terminal/shell.ts +16 -3
- package/src/tools/tool-manifest.ts +1 -1
- package/src/util/log-redact.ts +9 -34
- package/src/util/logger.ts +11 -1
- package/src/util/sentry-log-stream.ts +51 -0
- package/src/watcher/providers/github.ts +2 -2
- package/src/watcher/providers/gmail.ts +1 -1
- package/src/watcher/providers/google-calendar.ts +1 -1
- package/src/watcher/providers/linear.ts +2 -2
- package/src/workspace/migrations/011-backfill-installation-id.ts +5 -3
- package/src/workspace/migrations/020-rename-oauth-skill-dirs.ts +119 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/docs/architecture/keychain-broker.md +0 -68
- package/src/cli/commands/oauth/connections.ts +0 -734
- package/src/cli/commands/oauth/platform.ts +0 -525
- package/src/cli/commands/platform.ts +0 -176
- package/src/config/bundled-skills/slack/TOOLS.json +0 -272
- package/src/config/bundled-skills/slack/tools/shared.ts +0 -34
- package/src/config/bundled-skills/slack/tools/slack-add-reaction.ts +0 -27
- package/src/config/bundled-skills/slack/tools/slack-channel-details.ts +0 -38
- package/src/config/bundled-skills/slack/tools/slack-channel-permissions.ts +0 -146
- package/src/config/bundled-skills/slack/tools/slack-configure-channels.ts +0 -105
- package/src/config/bundled-skills/slack/tools/slack-delete-message.ts +0 -26
- package/src/config/bundled-skills/slack/tools/slack-edit-message.ts +0 -27
- package/src/config/bundled-skills/slack/tools/slack-leave-channel.ts +0 -25
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +0 -372
- package/src/oauth/provider-behaviors.ts +0 -634
|
@@ -1,13 +1,24 @@
|
|
|
1
|
-
import { and, eq, gte, inArray, isNull, lte } from "drizzle-orm";
|
|
1
|
+
import { and, eq, gte, inArray, isNull, lte, sql } from "drizzle-orm";
|
|
2
2
|
import { v4 as uuid } from "uuid";
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
5
|
getAssistantMessageIdsInTurn,
|
|
6
6
|
getMessageById,
|
|
7
|
+
getTurnTimeBounds,
|
|
7
8
|
messageMetadataSchema,
|
|
8
9
|
} from "./conversation-crud.js";
|
|
9
10
|
import { getDb } from "./db.js";
|
|
10
|
-
import { llmRequestLogs } from "./schema.js";
|
|
11
|
+
import { llmRequestLogs, messages } from "./schema.js";
|
|
12
|
+
|
|
13
|
+
type LogRow = {
|
|
14
|
+
id: string;
|
|
15
|
+
conversationId: string;
|
|
16
|
+
messageId: string | null;
|
|
17
|
+
provider: string | null;
|
|
18
|
+
requestPayload: string;
|
|
19
|
+
responsePayload: string;
|
|
20
|
+
createdAt: number;
|
|
21
|
+
};
|
|
11
22
|
|
|
12
23
|
export function recordRequestLog(
|
|
13
24
|
conversationId: string,
|
|
@@ -77,15 +88,7 @@ export function backfillMessageIdOnLogs(
|
|
|
77
88
|
* given message IDs, ordered by `createdAt ASC`. Uses the existing
|
|
78
89
|
* `idx_llm_request_logs_message_id` index via `inArray`.
|
|
79
90
|
*/
|
|
80
|
-
function selectLogsByMessageIds(messageIds: string[]):
|
|
81
|
-
id: string;
|
|
82
|
-
conversationId: string;
|
|
83
|
-
messageId: string | null;
|
|
84
|
-
provider: string | null;
|
|
85
|
-
requestPayload: string;
|
|
86
|
-
responsePayload: string;
|
|
87
|
-
createdAt: number;
|
|
88
|
-
}> {
|
|
91
|
+
function selectLogsByMessageIds(messageIds: string[]): LogRow[] {
|
|
89
92
|
if (messageIds.length === 0) return [];
|
|
90
93
|
const db = getDb();
|
|
91
94
|
return db
|
|
@@ -104,26 +107,90 @@ function selectLogsByMessageIds(messageIds: string[]): Array<{
|
|
|
104
107
|
.all();
|
|
105
108
|
}
|
|
106
109
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
110
|
+
/**
|
|
111
|
+
* Find orphaned logs — logs whose `message_id` references a message that no
|
|
112
|
+
* longer exists in the DB. These are left behind when intermediate assistant
|
|
113
|
+
* messages are deleted (e.g. by retry/deleteLastExchange).
|
|
114
|
+
*
|
|
115
|
+
* Scoped to a single conversation and a time range to avoid cross-turn bleed.
|
|
116
|
+
*/
|
|
117
|
+
function selectOrphanedLogsInRange(
|
|
118
|
+
conversationId: string,
|
|
119
|
+
startTime: number,
|
|
120
|
+
endTime: number,
|
|
121
|
+
): LogRow[] {
|
|
122
|
+
if (endTime <= startTime) return [];
|
|
123
|
+
const db = getDb();
|
|
124
|
+
// LEFT JOIN messages → filter where message row IS NULL (orphaned).
|
|
125
|
+
return db
|
|
126
|
+
.select({
|
|
127
|
+
id: llmRequestLogs.id,
|
|
128
|
+
conversationId: llmRequestLogs.conversationId,
|
|
129
|
+
messageId: llmRequestLogs.messageId,
|
|
130
|
+
provider: llmRequestLogs.provider,
|
|
131
|
+
requestPayload: llmRequestLogs.requestPayload,
|
|
132
|
+
responsePayload: llmRequestLogs.responsePayload,
|
|
133
|
+
createdAt: llmRequestLogs.createdAt,
|
|
134
|
+
})
|
|
135
|
+
.from(llmRequestLogs)
|
|
136
|
+
.leftJoin(messages, eq(llmRequestLogs.messageId, messages.id))
|
|
137
|
+
.where(
|
|
138
|
+
and(
|
|
139
|
+
eq(llmRequestLogs.conversationId, conversationId),
|
|
140
|
+
gte(llmRequestLogs.createdAt, startTime),
|
|
141
|
+
lte(llmRequestLogs.createdAt, endTime),
|
|
142
|
+
sql`${messages.id} IS NULL`,
|
|
143
|
+
sql`${llmRequestLogs.messageId} IS NOT NULL`,
|
|
144
|
+
),
|
|
145
|
+
)
|
|
146
|
+
.orderBy(llmRequestLogs.createdAt)
|
|
147
|
+
.all();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function getRequestLogsByMessageId(messageId: string): LogRow[] {
|
|
116
151
|
// Resolve all assistant message IDs in the same turn so the inspector
|
|
117
152
|
// shows every LLM call from the entire agent turn, not just the queried message.
|
|
118
153
|
const turnMessageIds = getAssistantMessageIdsInTurn(messageId);
|
|
119
154
|
const turnLogs = selectLogsByMessageIds(turnMessageIds);
|
|
155
|
+
|
|
156
|
+
// Orphaned-log recovery: intermediate assistant messages within a turn can
|
|
157
|
+
// be deleted (e.g. by retry / deleteLastExchange) while their
|
|
158
|
+
// llm_request_logs rows survive. When that happens the message-ID-based
|
|
159
|
+
// query misses those logs. We detect orphaned logs (logs whose message_id
|
|
160
|
+
// references a deleted message) within the turn's time window and merge
|
|
161
|
+
// them with the message-ID-based results.
|
|
162
|
+
const message = getMessageById(messageId);
|
|
163
|
+
if (message) {
|
|
164
|
+
const bounds = getTurnTimeBounds(message.conversationId, message.createdAt);
|
|
165
|
+
if (bounds) {
|
|
166
|
+
const orphanedLogs = selectOrphanedLogsInRange(
|
|
167
|
+
message.conversationId,
|
|
168
|
+
bounds.startTime,
|
|
169
|
+
bounds.endTime,
|
|
170
|
+
);
|
|
171
|
+
if (orphanedLogs.length > 0) {
|
|
172
|
+
const seen = new Set(turnLogs.map((l) => l.id));
|
|
173
|
+
const merged = [...turnLogs];
|
|
174
|
+
for (const log of orphanedLogs) {
|
|
175
|
+
if (!seen.has(log.id)) {
|
|
176
|
+
merged.push(log);
|
|
177
|
+
seen.add(log.id);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
merged.sort(
|
|
181
|
+
(a, b) => a.createdAt - b.createdAt || a.id.localeCompare(b.id),
|
|
182
|
+
);
|
|
183
|
+
return merged;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
120
188
|
if (turnLogs.length > 0) {
|
|
121
189
|
return turnLogs;
|
|
122
190
|
}
|
|
123
191
|
|
|
124
192
|
// Fork-source fallback: if no logs found for the turn, check whether
|
|
125
193
|
// the queried message was forked from a source and resolve that source's turn.
|
|
126
|
-
const message = getMessageById(messageId);
|
|
127
194
|
if (!message?.metadata) {
|
|
128
195
|
return [];
|
|
129
196
|
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { and, eq, inArray, isNull } from "drizzle-orm";
|
|
2
|
+
import { v4 as uuid } from "uuid";
|
|
3
|
+
|
|
4
|
+
import { getDb } from "./db.js";
|
|
5
|
+
import { memoryRecallLogs } from "./schema.js";
|
|
6
|
+
|
|
7
|
+
export interface RecordMemoryRecallLogParams {
|
|
8
|
+
conversationId: string;
|
|
9
|
+
enabled: boolean;
|
|
10
|
+
degraded: boolean;
|
|
11
|
+
provider?: string;
|
|
12
|
+
model?: string;
|
|
13
|
+
degradationJson?: unknown;
|
|
14
|
+
semanticHits: number;
|
|
15
|
+
mergedCount: number;
|
|
16
|
+
selectedCount: number;
|
|
17
|
+
tier1Count: number;
|
|
18
|
+
tier2Count: number;
|
|
19
|
+
hybridSearchLatencyMs: number;
|
|
20
|
+
sparseVectorUsed: boolean;
|
|
21
|
+
injectedTokens: number;
|
|
22
|
+
latencyMs: number;
|
|
23
|
+
topCandidatesJson: unknown;
|
|
24
|
+
injectedText?: string;
|
|
25
|
+
reason?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function recordMemoryRecallLog(params: RecordMemoryRecallLogParams): void {
|
|
29
|
+
const db = getDb();
|
|
30
|
+
db.insert(memoryRecallLogs)
|
|
31
|
+
.values({
|
|
32
|
+
id: uuid(),
|
|
33
|
+
conversationId: params.conversationId,
|
|
34
|
+
messageId: null,
|
|
35
|
+
enabled: params.enabled ? 1 : 0,
|
|
36
|
+
degraded: params.degraded ? 1 : 0,
|
|
37
|
+
provider: params.provider ?? null,
|
|
38
|
+
model: params.model ?? null,
|
|
39
|
+
degradationJson: params.degradationJson
|
|
40
|
+
? JSON.stringify(params.degradationJson)
|
|
41
|
+
: null,
|
|
42
|
+
semanticHits: params.semanticHits,
|
|
43
|
+
mergedCount: params.mergedCount,
|
|
44
|
+
selectedCount: params.selectedCount,
|
|
45
|
+
tier1Count: params.tier1Count,
|
|
46
|
+
tier2Count: params.tier2Count,
|
|
47
|
+
hybridSearchLatencyMs: params.hybridSearchLatencyMs,
|
|
48
|
+
sparseVectorUsed: params.sparseVectorUsed ? 1 : 0,
|
|
49
|
+
injectedTokens: params.injectedTokens,
|
|
50
|
+
latencyMs: params.latencyMs,
|
|
51
|
+
topCandidatesJson: JSON.stringify(params.topCandidatesJson),
|
|
52
|
+
injectedText: params.injectedText ?? null,
|
|
53
|
+
reason: params.reason ?? null,
|
|
54
|
+
createdAt: Date.now(),
|
|
55
|
+
})
|
|
56
|
+
.run();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function backfillMemoryRecallLogMessageId(
|
|
60
|
+
conversationId: string,
|
|
61
|
+
messageId: string,
|
|
62
|
+
): void {
|
|
63
|
+
const db = getDb();
|
|
64
|
+
db.update(memoryRecallLogs)
|
|
65
|
+
.set({ messageId })
|
|
66
|
+
.where(
|
|
67
|
+
and(
|
|
68
|
+
eq(memoryRecallLogs.conversationId, conversationId),
|
|
69
|
+
isNull(memoryRecallLogs.messageId),
|
|
70
|
+
),
|
|
71
|
+
)
|
|
72
|
+
.run();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface MemoryRecallLog {
|
|
76
|
+
enabled: boolean;
|
|
77
|
+
degraded: boolean;
|
|
78
|
+
provider: string | null;
|
|
79
|
+
model: string | null;
|
|
80
|
+
degradation: unknown | null;
|
|
81
|
+
semanticHits: number;
|
|
82
|
+
mergedCount: number;
|
|
83
|
+
selectedCount: number;
|
|
84
|
+
tier1Count: number;
|
|
85
|
+
tier2Count: number;
|
|
86
|
+
hybridSearchLatencyMs: number;
|
|
87
|
+
sparseVectorUsed: boolean;
|
|
88
|
+
injectedTokens: number;
|
|
89
|
+
latencyMs: number;
|
|
90
|
+
topCandidates: unknown;
|
|
91
|
+
injectedText: string | null;
|
|
92
|
+
reason: string | null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function getMemoryRecallLogByMessageIds(
|
|
96
|
+
messageIds: string[],
|
|
97
|
+
): MemoryRecallLog | null {
|
|
98
|
+
if (messageIds.length === 0) return null;
|
|
99
|
+
const db = getDb();
|
|
100
|
+
const rows = db
|
|
101
|
+
.select()
|
|
102
|
+
.from(memoryRecallLogs)
|
|
103
|
+
.where(inArray(memoryRecallLogs.messageId, messageIds))
|
|
104
|
+
.all();
|
|
105
|
+
if (rows.length === 0) return null;
|
|
106
|
+
const row = rows[0]!;
|
|
107
|
+
return {
|
|
108
|
+
enabled: !!row.enabled,
|
|
109
|
+
degraded: !!row.degraded,
|
|
110
|
+
provider: row.provider,
|
|
111
|
+
model: row.model,
|
|
112
|
+
degradation: row.degradationJson
|
|
113
|
+
? JSON.parse(row.degradationJson)
|
|
114
|
+
: null,
|
|
115
|
+
semanticHits: row.semanticHits,
|
|
116
|
+
mergedCount: row.mergedCount,
|
|
117
|
+
selectedCount: row.selectedCount,
|
|
118
|
+
tier1Count: row.tier1Count,
|
|
119
|
+
tier2Count: row.tier2Count,
|
|
120
|
+
hybridSearchLatencyMs: row.hybridSearchLatencyMs,
|
|
121
|
+
sparseVectorUsed: !!row.sparseVectorUsed,
|
|
122
|
+
injectedTokens: row.injectedTokens,
|
|
123
|
+
latencyMs: row.latencyMs,
|
|
124
|
+
topCandidates: JSON.parse(row.topCandidatesJson),
|
|
125
|
+
injectedText: row.injectedText,
|
|
126
|
+
reason: row.reason,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { DrizzleDb } from "../db-connection.js";
|
|
2
|
+
import { getSqliteFrom } from "../db-connection.js";
|
|
3
|
+
import { withCrashRecovery } from "./validate-migration-state.js";
|
|
4
|
+
|
|
5
|
+
const CHECKPOINT_KEY = "migration_create_memory_recall_logs_v1";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Create the memory_recall_logs table for the inspector memory tab.
|
|
9
|
+
*/
|
|
10
|
+
export function migrateCreateMemoryRecallLogs(database: DrizzleDb): void {
|
|
11
|
+
withCrashRecovery(database, CHECKPOINT_KEY, () => {
|
|
12
|
+
const raw = getSqliteFrom(database);
|
|
13
|
+
|
|
14
|
+
raw.exec(/*sql*/ `
|
|
15
|
+
CREATE TABLE IF NOT EXISTS memory_recall_logs (
|
|
16
|
+
id TEXT PRIMARY KEY,
|
|
17
|
+
conversation_id TEXT NOT NULL,
|
|
18
|
+
message_id TEXT,
|
|
19
|
+
enabled INTEGER NOT NULL,
|
|
20
|
+
degraded INTEGER NOT NULL,
|
|
21
|
+
provider TEXT,
|
|
22
|
+
model TEXT,
|
|
23
|
+
degradation_json TEXT,
|
|
24
|
+
semantic_hits INTEGER NOT NULL,
|
|
25
|
+
merged_count INTEGER NOT NULL,
|
|
26
|
+
selected_count INTEGER NOT NULL,
|
|
27
|
+
tier1_count INTEGER NOT NULL,
|
|
28
|
+
tier2_count INTEGER NOT NULL,
|
|
29
|
+
hybrid_search_latency_ms INTEGER NOT NULL,
|
|
30
|
+
sparse_vector_used INTEGER NOT NULL,
|
|
31
|
+
injected_tokens INTEGER NOT NULL,
|
|
32
|
+
latency_ms INTEGER NOT NULL,
|
|
33
|
+
top_candidates_json TEXT NOT NULL,
|
|
34
|
+
injected_text TEXT,
|
|
35
|
+
reason TEXT,
|
|
36
|
+
created_at INTEGER NOT NULL
|
|
37
|
+
)
|
|
38
|
+
`);
|
|
39
|
+
|
|
40
|
+
raw.exec(/*sql*/ `
|
|
41
|
+
CREATE INDEX IF NOT EXISTS idx_memory_recall_logs_message_id
|
|
42
|
+
ON memory_recall_logs (message_id)
|
|
43
|
+
`);
|
|
44
|
+
|
|
45
|
+
raw.exec(/*sql*/ `
|
|
46
|
+
CREATE INDEX IF NOT EXISTS idx_memory_recall_logs_conversation_id
|
|
47
|
+
ON memory_recall_logs (conversation_id)
|
|
48
|
+
`);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { DrizzleDb } from "../db-connection.js";
|
|
2
|
+
import { getSqliteFrom } from "../db-connection.js";
|
|
3
|
+
|
|
4
|
+
export function migrateOAuthProvidersPingConfig(database: DrizzleDb): void {
|
|
5
|
+
const raw = getSqliteFrom(database);
|
|
6
|
+
try {
|
|
7
|
+
raw.exec(/*sql*/ `ALTER TABLE oauth_providers ADD COLUMN ping_method TEXT`);
|
|
8
|
+
} catch {
|
|
9
|
+
// Column already exists — nothing to do.
|
|
10
|
+
}
|
|
11
|
+
try {
|
|
12
|
+
raw.exec(
|
|
13
|
+
/*sql*/ `ALTER TABLE oauth_providers ADD COLUMN ping_headers TEXT`,
|
|
14
|
+
);
|
|
15
|
+
} catch {
|
|
16
|
+
// Column already exists — nothing to do.
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
raw.exec(/*sql*/ `ALTER TABLE oauth_providers ADD COLUMN ping_body TEXT`);
|
|
20
|
+
} catch {
|
|
21
|
+
// Column already exists — nothing to do.
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { DrizzleDb } from "../db-connection.js";
|
|
2
|
+
|
|
3
|
+
export function migrateMessagesConversationCreatedAtIndex(
|
|
4
|
+
database: DrizzleDb,
|
|
5
|
+
): void {
|
|
6
|
+
database.run(
|
|
7
|
+
/*sql*/ `CREATE INDEX IF NOT EXISTS idx_messages_conversation_created_at ON messages(conversation_id, created_at)`,
|
|
8
|
+
);
|
|
9
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import type { DrizzleDb } from "../db-connection.js";
|
|
2
|
+
import { getSqliteFrom } from "../db-connection.js";
|
|
3
|
+
import { withCrashRecovery } from "./validate-migration-state.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* One-shot migration: strip the `integration:` prefix from provider_key
|
|
7
|
+
* values across all three OAuth tables (oauth_providers, oauth_apps,
|
|
8
|
+
* oauth_connections).
|
|
9
|
+
*
|
|
10
|
+
* Historically provider keys were stored as `integration:google`,
|
|
11
|
+
* `integration:slack`, etc. The codebase is moving to bare-name keys
|
|
12
|
+
* (`google`, `slack`) for simplicity. Providers that were already stored
|
|
13
|
+
* with bare names (e.g. `slack_channel`, `telegram`) are unaffected.
|
|
14
|
+
*
|
|
15
|
+
* If a bare-name key already exists (runtime seed data created it), the
|
|
16
|
+
* old `integration:` rows are orphaned — we delete them instead of
|
|
17
|
+
* renaming to avoid UNIQUE constraint violations.
|
|
18
|
+
*
|
|
19
|
+
* FK constraints require us to update child tables (oauth_apps,
|
|
20
|
+
* oauth_connections) before the parent (oauth_providers), or disable FKs.
|
|
21
|
+
* We disable FKs for safety and update all three tables atomically.
|
|
22
|
+
*/
|
|
23
|
+
export function migrateStripIntegrationPrefixFromProviderKeys(
|
|
24
|
+
database: DrizzleDb,
|
|
25
|
+
): void {
|
|
26
|
+
withCrashRecovery(
|
|
27
|
+
database,
|
|
28
|
+
"migration_strip_integration_prefix_from_provider_keys_v1",
|
|
29
|
+
() => {
|
|
30
|
+
const raw = getSqliteFrom(database);
|
|
31
|
+
|
|
32
|
+
raw.exec("PRAGMA foreign_keys = OFF");
|
|
33
|
+
try {
|
|
34
|
+
// Find all provider keys with the integration: prefix.
|
|
35
|
+
const rows = raw
|
|
36
|
+
.prepare(
|
|
37
|
+
/*sql*/ `SELECT provider_key FROM oauth_providers WHERE provider_key LIKE 'integration:%'`,
|
|
38
|
+
)
|
|
39
|
+
.all() as Array<{ provider_key: string }>;
|
|
40
|
+
|
|
41
|
+
for (const { provider_key: oldKey } of rows) {
|
|
42
|
+
const newKey = oldKey.replace(/^integration:/, "");
|
|
43
|
+
|
|
44
|
+
// Check if the bare-name key already exists (seed data may have created it).
|
|
45
|
+
const bareExists = raw
|
|
46
|
+
.prepare(
|
|
47
|
+
/*sql*/ `SELECT 1 FROM oauth_providers WHERE provider_key = ?`,
|
|
48
|
+
)
|
|
49
|
+
.get(newKey);
|
|
50
|
+
|
|
51
|
+
if (bareExists) {
|
|
52
|
+
// Bare-name provider already exists — delete the old prefixed rows
|
|
53
|
+
// to avoid UNIQUE constraint violations.
|
|
54
|
+
raw
|
|
55
|
+
.prepare(
|
|
56
|
+
/*sql*/ `DELETE FROM oauth_connections WHERE provider_key = ?`,
|
|
57
|
+
)
|
|
58
|
+
.run(oldKey);
|
|
59
|
+
raw
|
|
60
|
+
.prepare(/*sql*/ `DELETE FROM oauth_apps WHERE provider_key = ?`)
|
|
61
|
+
.run(oldKey);
|
|
62
|
+
raw
|
|
63
|
+
.prepare(
|
|
64
|
+
/*sql*/ `DELETE FROM oauth_providers WHERE provider_key = ?`,
|
|
65
|
+
)
|
|
66
|
+
.run(oldKey);
|
|
67
|
+
} else {
|
|
68
|
+
// Rename: update child tables first, then parent.
|
|
69
|
+
raw
|
|
70
|
+
.prepare(
|
|
71
|
+
/*sql*/ `UPDATE oauth_connections SET provider_key = ? WHERE provider_key = ?`,
|
|
72
|
+
)
|
|
73
|
+
.run(newKey, oldKey);
|
|
74
|
+
raw
|
|
75
|
+
.prepare(
|
|
76
|
+
/*sql*/ `UPDATE oauth_apps SET provider_key = ? WHERE provider_key = ?`,
|
|
77
|
+
)
|
|
78
|
+
.run(newKey, oldKey);
|
|
79
|
+
raw
|
|
80
|
+
.prepare(
|
|
81
|
+
/*sql*/ `UPDATE oauth_providers SET provider_key = ? WHERE provider_key = ?`,
|
|
82
|
+
)
|
|
83
|
+
.run(newKey, oldKey);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Also update the watchers table — credential_service stores provider
|
|
88
|
+
// keys like "integration:google" that feed into resolveOAuthConnection().
|
|
89
|
+
raw
|
|
90
|
+
.prepare(
|
|
91
|
+
/*sql*/ `UPDATE watchers SET credential_service = REPLACE(credential_service, 'integration:', '') WHERE credential_service LIKE 'integration:%'`,
|
|
92
|
+
)
|
|
93
|
+
.run();
|
|
94
|
+
} finally {
|
|
95
|
+
raw.exec("PRAGMA foreign_keys = ON");
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Reverse: re-add the `integration:` prefix to provider keys that don't
|
|
103
|
+
* already have one and aren't known bare-name providers.
|
|
104
|
+
*
|
|
105
|
+
* This is a best-effort rollback — we prefix all keys that look like they
|
|
106
|
+
* were originally `integration:` prefixed. Known bare-name keys
|
|
107
|
+
* (`slack_channel`, `telegram`) are left as-is because they never had the
|
|
108
|
+
* prefix.
|
|
109
|
+
*/
|
|
110
|
+
export function migrateStripIntegrationPrefixFromProviderKeysDown(
|
|
111
|
+
database: DrizzleDb,
|
|
112
|
+
): void {
|
|
113
|
+
const raw = getSqliteFrom(database);
|
|
114
|
+
|
|
115
|
+
// Keys that were always bare — never had an integration: prefix.
|
|
116
|
+
const ALWAYS_BARE = new Set(["slack_channel", "telegram"]);
|
|
117
|
+
|
|
118
|
+
raw.exec("PRAGMA foreign_keys = OFF");
|
|
119
|
+
try {
|
|
120
|
+
const rows = raw
|
|
121
|
+
.prepare(
|
|
122
|
+
/*sql*/ `SELECT provider_key FROM oauth_providers WHERE provider_key NOT LIKE 'integration:%'`,
|
|
123
|
+
)
|
|
124
|
+
.all() as Array<{ provider_key: string }>;
|
|
125
|
+
|
|
126
|
+
for (const { provider_key: bareKey } of rows) {
|
|
127
|
+
if (ALWAYS_BARE.has(bareKey)) continue;
|
|
128
|
+
|
|
129
|
+
const prefixedKey = `integration:${bareKey}`;
|
|
130
|
+
|
|
131
|
+
// If the prefixed key already exists, delete the bare rows.
|
|
132
|
+
const prefixedExists = raw
|
|
133
|
+
.prepare(/*sql*/ `SELECT 1 FROM oauth_providers WHERE provider_key = ?`)
|
|
134
|
+
.get(prefixedKey);
|
|
135
|
+
|
|
136
|
+
if (prefixedExists) {
|
|
137
|
+
raw
|
|
138
|
+
.prepare(
|
|
139
|
+
/*sql*/ `DELETE FROM oauth_connections WHERE provider_key = ?`,
|
|
140
|
+
)
|
|
141
|
+
.run(bareKey);
|
|
142
|
+
raw
|
|
143
|
+
.prepare(/*sql*/ `DELETE FROM oauth_apps WHERE provider_key = ?`)
|
|
144
|
+
.run(bareKey);
|
|
145
|
+
raw
|
|
146
|
+
.prepare(/*sql*/ `DELETE FROM oauth_providers WHERE provider_key = ?`)
|
|
147
|
+
.run(bareKey);
|
|
148
|
+
} else {
|
|
149
|
+
raw
|
|
150
|
+
.prepare(
|
|
151
|
+
/*sql*/ `UPDATE oauth_connections SET provider_key = ? WHERE provider_key = ?`,
|
|
152
|
+
)
|
|
153
|
+
.run(prefixedKey, bareKey);
|
|
154
|
+
raw
|
|
155
|
+
.prepare(
|
|
156
|
+
/*sql*/ `UPDATE oauth_apps SET provider_key = ? WHERE provider_key = ?`,
|
|
157
|
+
)
|
|
158
|
+
.run(prefixedKey, bareKey);
|
|
159
|
+
raw
|
|
160
|
+
.prepare(
|
|
161
|
+
/*sql*/ `UPDATE oauth_providers SET provider_key = ? WHERE provider_key = ?`,
|
|
162
|
+
)
|
|
163
|
+
.run(prefixedKey, bareKey);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Reverse the watchers table update — re-add the prefix for keys that
|
|
168
|
+
// aren't known bare-name providers.
|
|
169
|
+
const watcherRows = raw
|
|
170
|
+
.prepare(
|
|
171
|
+
/*sql*/ `SELECT DISTINCT credential_service FROM watchers WHERE credential_service NOT LIKE 'integration:%'`,
|
|
172
|
+
)
|
|
173
|
+
.all() as Array<{ credential_service: string }>;
|
|
174
|
+
|
|
175
|
+
for (const { credential_service: bareKey } of watcherRows) {
|
|
176
|
+
if (ALWAYS_BARE.has(bareKey)) continue;
|
|
177
|
+
raw
|
|
178
|
+
.prepare(
|
|
179
|
+
/*sql*/ `UPDATE watchers SET credential_service = ? WHERE credential_service = ?`,
|
|
180
|
+
)
|
|
181
|
+
.run(`integration:${bareKey}`, bareKey);
|
|
182
|
+
}
|
|
183
|
+
} finally {
|
|
184
|
+
raw.exec("PRAGMA foreign_keys = ON");
|
|
185
|
+
}
|
|
186
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { DrizzleDb } from "../db-connection.js";
|
|
2
|
+
import { getSqliteFrom } from "../db-connection.js";
|
|
3
|
+
|
|
4
|
+
export function migrateOAuthProvidersBehaviorColumns(
|
|
5
|
+
database: DrizzleDb,
|
|
6
|
+
): void {
|
|
7
|
+
const raw = getSqliteFrom(database);
|
|
8
|
+
const columns = [
|
|
9
|
+
"loopback_port INTEGER",
|
|
10
|
+
"injection_templates TEXT",
|
|
11
|
+
"setup_skill_id TEXT",
|
|
12
|
+
"app_type TEXT",
|
|
13
|
+
"setup_notes TEXT",
|
|
14
|
+
"identity_url TEXT",
|
|
15
|
+
"identity_method TEXT",
|
|
16
|
+
"identity_headers TEXT",
|
|
17
|
+
"identity_body TEXT",
|
|
18
|
+
"identity_response_paths TEXT",
|
|
19
|
+
"identity_format TEXT",
|
|
20
|
+
"identity_ok_field TEXT",
|
|
21
|
+
];
|
|
22
|
+
for (const col of columns) {
|
|
23
|
+
try {
|
|
24
|
+
raw.exec(`ALTER TABLE oauth_providers ADD COLUMN ${col}`);
|
|
25
|
+
} catch {
|
|
26
|
+
// Column already exists — nothing to do.
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { DrizzleDb } from "../db-connection.js";
|
|
2
|
+
import { getSqliteFrom } from "../db-connection.js";
|
|
3
|
+
|
|
4
|
+
export function migrateDropSetupSkillIdColumn(database: DrizzleDb): void {
|
|
5
|
+
const raw = getSqliteFrom(database);
|
|
6
|
+
try {
|
|
7
|
+
raw.exec(/*sql*/ `ALTER TABLE oauth_providers DROP COLUMN setup_skill_id`);
|
|
8
|
+
} catch {
|
|
9
|
+
// Column already dropped or doesn't exist — nothing to do.
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -132,6 +132,12 @@ export { migrateCallSessionSkipDisclosure } from "./190-call-session-skip-disclo
|
|
|
132
132
|
export { migrateBackfillAudioAttachmentMimeTypes } from "./191-backfill-audio-attachment-mime-types.js";
|
|
133
133
|
export { migrateContactsUserFileColumn } from "./192-contacts-user-file-column.js";
|
|
134
134
|
export { migrateAddSourceTypeColumns } from "./193-add-source-type-columns.js";
|
|
135
|
+
export { migrateCreateMemoryRecallLogs } from "./194-memory-recall-logs.js";
|
|
136
|
+
export { migrateOAuthProvidersPingConfig } from "./195-oauth-providers-ping-config.js";
|
|
137
|
+
export { migrateMessagesConversationCreatedAtIndex } from "./196-messages-conversation-created-at-index.js";
|
|
138
|
+
export { migrateStripIntegrationPrefixFromProviderKeys } from "./196-strip-integration-prefix-from-provider-keys.js";
|
|
139
|
+
export { migrateOAuthProvidersBehaviorColumns } from "./197-oauth-providers-behavior-columns.js";
|
|
140
|
+
export { migrateDropSetupSkillIdColumn } from "./198-drop-setup-skill-id-column.js";
|
|
135
141
|
export {
|
|
136
142
|
MIGRATION_REGISTRY,
|
|
137
143
|
type MigrationRegistryEntry,
|
|
@@ -38,6 +38,7 @@ import { migrateBackfillInlineAttachmentsToDiskDown } from "./180-backfill-inlin
|
|
|
38
38
|
import { migrateRenameThreadStartersCheckpointsDown } from "./181-rename-thread-starters-checkpoints.js";
|
|
39
39
|
import { migrateBackfillAudioAttachmentMimeTypesDown } from "./191-backfill-audio-attachment-mime-types.js";
|
|
40
40
|
import { migrateAddSourceTypeColumnsDown } from "./193-add-source-type-columns.js";
|
|
41
|
+
import { migrateStripIntegrationPrefixFromProviderKeysDown } from "./196-strip-integration-prefix-from-provider-keys.js";
|
|
41
42
|
|
|
42
43
|
export interface MigrationRegistryEntry {
|
|
43
44
|
/** The checkpoint key written to memory_checkpoints on completion. */
|
|
@@ -333,6 +334,13 @@ export const MIGRATION_REGISTRY: MigrationRegistryEntry[] = [
|
|
|
333
334
|
"Add source_type and source_message_role columns to memory_items with backfill from verification_state and source messages",
|
|
334
335
|
down: migrateAddSourceTypeColumnsDown,
|
|
335
336
|
},
|
|
337
|
+
{
|
|
338
|
+
key: "migration_strip_integration_prefix_from_provider_keys_v1",
|
|
339
|
+
version: 38,
|
|
340
|
+
description:
|
|
341
|
+
"Strip integration: prefix from provider_key across oauth_providers, oauth_apps, and oauth_connections",
|
|
342
|
+
down: migrateStripIntegrationPrefixFromProviderKeysDown,
|
|
343
|
+
},
|
|
336
344
|
];
|
|
337
345
|
|
|
338
346
|
export function getMaxMigrationVersion(): number {
|
|
@@ -331,8 +331,7 @@ describe("Memory Retriever Pipeline", () => {
|
|
|
331
331
|
expect(result.enabled).toBe(true);
|
|
332
332
|
expect(result.degraded).toBe(false);
|
|
333
333
|
expect(result.degradation).toBeUndefined();
|
|
334
|
-
// With
|
|
335
|
-
// scoring below tier thresholds, no candidates are selected.
|
|
334
|
+
// With Qdrant mocked empty, no candidates are found.
|
|
336
335
|
// The pipeline still completes successfully with tier metadata.
|
|
337
336
|
expect(result.tier1Count).toBeDefined();
|
|
338
337
|
expect(result.tier2Count).toBeDefined();
|
|
@@ -345,7 +344,7 @@ describe("Memory Retriever Pipeline", () => {
|
|
|
345
344
|
// Current-conversation segment filtering
|
|
346
345
|
// -----------------------------------------------------------------------
|
|
347
346
|
|
|
348
|
-
test("current-conversation segments are filtered from
|
|
347
|
+
test("current-conversation segments are filtered from search results", async () => {
|
|
349
348
|
const db = getDb();
|
|
350
349
|
const now = Date.now();
|
|
351
350
|
const activeConv = "conv-active";
|
|
@@ -613,7 +612,7 @@ describe("Memory Retriever Pipeline", () => {
|
|
|
613
612
|
|
|
614
613
|
insertConversation(db, convId, now - MS_PER_DAY * 200);
|
|
615
614
|
|
|
616
|
-
// Create a message from 200 days ago
|
|
615
|
+
// Create a message from 200 days ago (staleness test anchor)
|
|
617
616
|
insertMessage(
|
|
618
617
|
db,
|
|
619
618
|
"msg-old",
|
|
@@ -679,7 +678,7 @@ describe("Memory Retriever Pipeline", () => {
|
|
|
679
678
|
// Degradation: Qdrant circuit breaker open
|
|
680
679
|
// -----------------------------------------------------------------------
|
|
681
680
|
|
|
682
|
-
test("Qdrant unavailable: pipeline completes with
|
|
681
|
+
test("Qdrant unavailable: pipeline completes with empty results", async () => {
|
|
683
682
|
seedMemory();
|
|
684
683
|
|
|
685
684
|
// Force the Qdrant circuit breaker open
|