@vellumai/assistant 0.4.52 → 0.4.54
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 +2 -2
- package/bun.lock +62 -349
- package/docs/architecture/integrations.md +1 -1
- package/docs/architecture/keychain-broker.md +91 -40
- package/docs/architecture/memory.md +3 -3
- package/docs/architecture/security.md +2 -2
- package/knip.json +7 -29
- package/package.json +2 -9
- package/src/__tests__/agent-loop.test.ts +1 -1
- package/src/__tests__/app-git-history.test.ts +0 -2
- package/src/__tests__/app-git-service.test.ts +1 -6
- package/src/__tests__/approval-cascade.test.ts +3 -2
- package/src/__tests__/approval-routes-http.test.ts +0 -1
- package/src/__tests__/asset-materialize-tool.test.ts +0 -1
- package/src/__tests__/asset-search-tool.test.ts +0 -1
- package/src/__tests__/assistant-events-sse-hardening.test.ts +0 -1
- package/src/__tests__/attachments-store.test.ts +0 -1
- package/src/__tests__/avatar-e2e.test.ts +5 -1
- package/src/__tests__/browser-fill-credential.test.ts +4 -6
- package/src/__tests__/btw-routes.test.ts +39 -0
- package/src/__tests__/call-controller.test.ts +0 -1
- package/src/__tests__/call-domain.test.ts +1 -1
- package/src/__tests__/call-routes-http.test.ts +1 -3
- package/src/__tests__/canonical-guardian-store.test.ts +33 -2
- package/src/__tests__/channel-guardian.test.ts +4 -4
- package/src/__tests__/channel-readiness-routes.test.ts +0 -1
- package/src/__tests__/channel-readiness-service.test.ts +1 -1
- package/src/__tests__/checker.test.ts +13 -11
- package/src/__tests__/claude-code-skill-regression.test.ts +5 -2
- package/src/__tests__/claude-code-tool-profiles.test.ts +7 -3
- package/src/__tests__/config-loader-backfill.test.ts +1 -5
- package/src/__tests__/config-schema.test.ts +9 -46
- package/src/__tests__/config-watcher.test.ts +11 -3
- package/src/__tests__/conversation-routes-slash-commands.test.ts +0 -1
- package/src/__tests__/credential-broker-browser-fill.test.ts +27 -24
- package/src/__tests__/credential-broker-server-use.test.ts +76 -40
- package/src/__tests__/credential-security-e2e.test.ts +1 -6
- package/src/__tests__/credential-security-invariants.test.ts +27 -8
- package/src/__tests__/credential-vault-unit.test.ts +32 -16
- package/src/__tests__/credential-vault.test.ts +40 -28
- package/src/__tests__/credentials-cli.test.ts +1 -21
- package/src/__tests__/email-invite-adapter.test.ts +0 -1
- package/src/__tests__/error-handler-friendly-messages.test.ts +4 -5
- package/src/__tests__/fixtures/credential-security-fixtures.ts +3 -3
- package/src/__tests__/fixtures/media-reuse-fixtures.ts +3 -79
- package/src/__tests__/gateway-only-enforcement.test.ts +1 -23
- package/src/__tests__/guardian-action-conversation-turn.test.ts +8 -8
- package/src/__tests__/guardian-action-late-reply.test.ts +13 -14
- package/src/__tests__/guardian-action-store.test.ts +0 -57
- package/src/__tests__/guardian-outbound-http.test.ts +1 -1
- package/src/__tests__/guardian-verification-voice-binding.test.ts +1 -3
- package/src/__tests__/hooks-blocking.test.ts +1 -1
- package/src/__tests__/hooks-config.test.ts +5 -29
- package/src/__tests__/hooks-discovery.test.ts +1 -1
- package/src/__tests__/hooks-integration.test.ts +1 -1
- package/src/__tests__/hooks-manager.test.ts +1 -1
- package/src/__tests__/hooks-runner.test.ts +1 -23
- package/src/__tests__/hooks-settings.test.ts +1 -1
- package/src/__tests__/hooks-templates.test.ts +1 -1
- package/src/__tests__/host-shell-tool.test.ts +0 -1
- package/src/__tests__/http-user-message-parity.test.ts +19 -0
- package/src/__tests__/integration-status.test.ts +0 -1
- package/src/__tests__/invite-routes-http.test.ts +0 -3
- package/src/__tests__/list-messages-attachments.test.ts +0 -1
- package/src/__tests__/llm-usage-store.test.ts +50 -0
- package/src/__tests__/log-export-workspace.test.ts +233 -0
- package/src/__tests__/managed-proxy-context.test.ts +41 -41
- package/src/__tests__/managed-skill-lifecycle.test.ts +0 -1
- package/src/__tests__/media-generate-image.test.ts +9 -4
- package/src/__tests__/media-reuse-story.e2e.test.ts +1 -7
- package/src/__tests__/memory-regressions.experimental.test.ts +4 -4
- package/src/__tests__/memory-regressions.test.ts +27 -28
- package/src/__tests__/memory-retrieval.benchmark.test.ts +1 -1
- package/src/__tests__/memory-upsert-concurrency.test.ts +4 -4
- package/src/__tests__/migration-cross-version-compatibility.test.ts +0 -1
- package/src/__tests__/migration-export-http.test.ts +0 -1
- package/src/__tests__/migration-import-commit-http.test.ts +0 -1
- package/src/__tests__/migration-import-preflight-http.test.ts +0 -1
- package/src/__tests__/migration-validate-http.test.ts +0 -1
- package/src/__tests__/notification-decision-fallback.test.ts +1 -1
- package/src/__tests__/notification-schedule-dedup.test.ts +237 -0
- package/src/__tests__/oauth-cli.test.ts +2 -14
- package/src/__tests__/oauth-store.test.ts +3 -7
- package/src/__tests__/oauth2-gateway-transport.test.ts +5 -4
- package/src/__tests__/onboarding-starter-tasks.test.ts +1 -1
- package/src/__tests__/onboarding-template-contract.test.ts +1 -2
- package/src/__tests__/openai-provider.test.ts +7 -7
- package/src/__tests__/platform.test.ts +14 -4
- package/src/__tests__/pricing.test.ts +0 -234
- package/src/__tests__/provider-commit-message-generator.test.ts +19 -15
- package/src/__tests__/provider-fail-open-selection.test.ts +67 -62
- package/src/__tests__/provider-managed-proxy-integration.test.ts +88 -85
- package/src/__tests__/provider-registry-ollama.test.ts +10 -4
- package/src/__tests__/public-ingress-urls.test.ts +1 -1
- package/src/__tests__/recording-handler.test.ts +0 -1
- package/src/__tests__/registry.test.ts +3 -103
- package/src/__tests__/relay-server.test.ts +0 -1
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
- package/src/__tests__/runtime-events-sse-parity.test.ts +0 -1
- package/src/__tests__/runtime-events-sse.test.ts +0 -1
- package/src/__tests__/script-proxy-injection-runtime.test.ts +2 -7
- package/src/__tests__/secret-onetime-send.test.ts +1 -6
- package/src/__tests__/secret-routes-managed-proxy.test.ts +6 -14
- package/src/__tests__/secret-scanner-executor.test.ts +0 -1
- package/src/__tests__/secure-keys.test.ts +241 -229
- package/src/__tests__/send-endpoint-busy.test.ts +0 -1
- package/src/__tests__/session-abort-tool-results.test.ts +3 -2
- package/src/__tests__/session-agent-loop-overflow.test.ts +1012 -838
- package/src/__tests__/session-agent-loop.test.ts +2 -2
- package/src/__tests__/session-confirmation-signals.test.ts +3 -2
- package/src/__tests__/session-error.test.ts +5 -4
- package/src/__tests__/session-history-web-search.test.ts +34 -9
- package/src/__tests__/session-messaging-secret-redirect.test.ts +1 -7
- package/src/__tests__/session-pre-run-repair.test.ts +3 -2
- package/src/__tests__/session-provider-retry-repair.test.ts +31 -27
- package/src/__tests__/session-queue.test.ts +5 -5
- package/src/__tests__/session-runtime-assembly.test.ts +118 -0
- package/src/__tests__/session-slash-known.test.ts +31 -14
- package/src/__tests__/session-slash-queue.test.ts +3 -2
- package/src/__tests__/session-slash-unknown.test.ts +3 -2
- package/src/__tests__/session-workspace-cache-state.test.ts +3 -1
- package/src/__tests__/session-workspace-injection.test.ts +3 -2
- package/src/__tests__/session-workspace-tool-tracking.test.ts +3 -2
- package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -1
- package/src/__tests__/skill-projection-feature-flag.test.ts +0 -1
- package/src/__tests__/skill-script-runner-sandbox.test.ts +0 -1
- package/src/__tests__/skillssh-registry.test.ts +21 -0
- package/src/__tests__/slack-channel-config.test.ts +1 -7
- package/src/__tests__/slack-share-routes.test.ts +1 -1
- package/src/__tests__/swarm-recursion.test.ts +4 -1
- package/src/__tests__/swarm-session-integration.test.ts +24 -14
- package/src/__tests__/swarm-tool.test.ts +4 -2
- package/src/__tests__/task-compiler.test.ts +1 -1
- package/src/__tests__/telegram-bot-username-resolution.test.ts +2 -4
- package/src/__tests__/test-support/browser-skill-harness.ts +0 -18
- package/src/__tests__/test-support/computer-use-skill-harness.ts +0 -23
- package/src/__tests__/token-estimator-accuracy.benchmark.test.ts +1521 -0
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +0 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -1
- package/src/__tests__/tool-executor-shell-integration.test.ts +0 -1
- package/src/__tests__/tool-executor.test.ts +1 -2
- package/src/__tests__/trust-store.test.ts +8 -83
- package/src/__tests__/twilio-config.test.ts +0 -1
- package/src/__tests__/twilio-provider.test.ts +0 -5
- package/src/__tests__/twilio-routes.test.ts +2 -3
- package/src/__tests__/usage-cache-backfill-migration.test.ts +10 -10
- package/src/__tests__/verification-control-plane-policy.test.ts +0 -1
- package/src/__tests__/voice-quality.test.ts +2 -1
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -1
- package/src/__tests__/web-search.test.ts +1 -1
- package/src/agent/loop.ts +17 -1
- package/src/bundler/app-bundler.ts +40 -24
- package/src/calls/call-controller.ts +16 -0
- package/src/calls/guardian-question-copy.ts +1 -1
- package/src/calls/relay-server.ts +29 -13
- package/src/calls/voice-control-protocol.ts +1 -0
- package/src/calls/voice-quality.ts +1 -1
- package/src/calls/voice-session-bridge.ts +9 -3
- package/src/channels/types.ts +16 -0
- package/src/cli/commands/bash.ts +173 -0
- package/src/cli/commands/doctor.ts +15 -57
- package/src/cli/commands/memory.ts +3 -5
- package/src/cli/commands/oauth/connections.ts +4 -2
- package/src/cli/commands/oauth/providers.ts +1 -13
- package/src/cli/commands/sessions.ts +1 -1
- package/src/cli/commands/usage.ts +359 -0
- package/src/cli/http-client.ts +22 -12
- package/src/cli/program.ts +4 -0
- package/src/cli/reference.ts +2 -0
- package/src/cli.ts +251 -181
- package/src/config/assistant-feature-flags.ts +0 -7
- package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +1 -1
- package/src/config/bundled-skills/claude-code/SKILL.md +1 -1
- package/src/config/bundled-skills/claude-code/TOOLS.json +1 -1
- package/src/config/bundled-skills/gmail/SKILL.md +0 -1
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +4 -3
- package/src/config/bundled-skills/media-processing/services/reduce.ts +1 -1
- package/src/config/bundled-skills/media-processing/tools/analyze-keyframes.ts +3 -5
- package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +2 -3
- package/src/config/bundled-skills/messaging/SKILL.md +0 -1
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +1 -1
- package/src/config/bundled-skills/sequences/SKILL.md +0 -1
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +5 -6
- package/src/config/env.ts +13 -0
- package/src/config/feature-flag-registry.json +15 -39
- package/src/config/loader.ts +7 -135
- package/src/config/schema.ts +0 -6
- package/src/config/schemas/channels.ts +1 -0
- package/src/config/schemas/elevenlabs.ts +2 -2
- package/src/config/schemas/security.ts +1 -2
- package/src/config/skills.ts +1 -1
- package/src/contacts/contact-store.ts +21 -75
- package/src/contacts/contacts-write.ts +6 -6
- package/src/contacts/types.ts +2 -0
- package/src/context/token-estimator.ts +35 -2
- package/src/context/window-manager.ts +16 -2
- package/src/daemon/approved-devices-store.ts +0 -44
- package/src/daemon/classifier.ts +1 -1
- package/src/daemon/config-watcher.ts +35 -11
- package/src/daemon/context-overflow-reducer.ts +13 -2
- package/src/daemon/handlers/config-ingress.ts +25 -8
- package/src/daemon/handlers/config-model.ts +22 -16
- package/src/daemon/handlers/config-telegram.ts +18 -6
- package/src/daemon/handlers/dictation.ts +0 -429
- package/src/daemon/handlers/sessions.ts +4 -116
- package/src/daemon/handlers/skills.ts +2 -201
- package/src/daemon/lifecycle.ts +21 -20
- package/src/daemon/message-types/contacts.ts +2 -0
- package/src/daemon/message-types/integrations.ts +1 -0
- package/src/daemon/message-types/sessions.ts +2 -0
- package/src/daemon/parse-actual-tokens-from-error.test.ts +75 -0
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/server.ts +42 -5
- package/src/daemon/session-agent-loop-handlers.ts +1 -1
- package/src/daemon/session-agent-loop.ts +27 -79
- package/src/daemon/session-error.ts +5 -4
- package/src/daemon/session-process.ts +17 -10
- package/src/daemon/session-runtime-assembly.ts +50 -0
- package/src/daemon/session-slash.ts +34 -22
- package/src/daemon/session.ts +1 -0
- package/src/daemon/shutdown-handlers.ts +15 -0
- package/src/daemon/watch-handler.ts +2 -2
- package/src/email/guardrails.ts +1 -1
- package/src/email/service.ts +0 -5
- package/src/events/domain-events.ts +1 -0
- package/src/hooks/templates.ts +1 -1
- package/src/media/app-icon-generator.ts +4 -3
- package/src/media/avatar-router.ts +5 -4
- package/src/media/gemini-image-service.ts +5 -5
- package/src/memory/admin.ts +2 -2
- package/src/memory/app-git-service.ts +0 -7
- package/src/memory/canonical-guardian-store.ts +25 -3
- package/src/memory/conversation-crud.ts +1 -1
- package/src/memory/conversation-title-service.ts +2 -2
- package/src/memory/db-init.ts +12 -0
- package/src/memory/embedding-backend.ts +46 -33
- package/src/memory/external-conversation-store.ts +0 -30
- package/src/memory/guardian-action-store.ts +0 -31
- package/src/memory/guardian-approvals.ts +1 -56
- package/src/memory/indexer.ts +4 -3
- package/src/memory/items-extractor.ts +1 -1
- package/src/memory/job-handlers/backfill.ts +5 -2
- package/src/memory/job-handlers/index-maintenance.ts +2 -2
- package/src/memory/job-handlers/media-processing.ts +2 -2
- package/src/memory/job-handlers/summarization.ts +1 -1
- package/src/memory/job-utils.ts +1 -2
- package/src/memory/jobs-worker.ts +2 -2
- package/src/memory/llm-usage-store.ts +57 -11
- package/src/memory/media-store.ts +4 -535
- package/src/memory/migrations/032-guardian-delivery-conversation-index.ts +2 -2
- package/src/memory/migrations/110-channel-guardian.ts +0 -1
- package/src/memory/migrations/158-channel-interaction-columns.ts +18 -0
- package/src/memory/migrations/159-drop-contact-interaction-columns.ts +16 -0
- package/src/memory/migrations/160-drop-loopback-port-column.ts +13 -0
- package/src/memory/migrations/index.ts +3 -0
- package/src/memory/published-pages-store.ts +0 -83
- package/src/memory/qdrant-circuit-breaker.ts +0 -8
- package/src/memory/retriever.test.ts +19 -12
- package/src/memory/retriever.ts +1 -1
- package/src/memory/schema/contacts.ts +2 -2
- package/src/memory/schema/oauth.ts +0 -1
- package/src/memory/search/semantic.ts +1 -8
- package/src/memory/shared-app-links-store.ts +0 -15
- package/src/messaging/registry.ts +0 -5
- package/src/messaging/style-analyzer.ts +1 -1
- package/src/notifications/copy-composer.ts +5 -13
- package/src/notifications/decision-engine.ts +2 -2
- package/src/notifications/deliveries-store.ts +0 -39
- package/src/notifications/guardian-question-mode.ts +6 -10
- package/src/notifications/preference-extractor.ts +1 -1
- package/src/oauth/byo-connection.test.ts +29 -20
- package/src/oauth/connect-orchestrator.ts +5 -3
- package/src/oauth/connect-types.ts +9 -2
- package/src/oauth/manual-token-connection.ts +9 -7
- package/src/oauth/oauth-store.ts +2 -8
- package/src/oauth/provider-behaviors.ts +11 -1
- package/src/oauth/seed-providers.ts +13 -5
- package/src/permissions/checker.ts +21 -2
- package/src/permissions/shell-identity.ts +0 -5
- package/src/permissions/trust-store.ts +0 -37
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +1 -1
- package/src/prompts/system-prompt.ts +5 -14
- package/src/prompts/templates/BOOTSTRAP.md +1 -3
- package/src/providers/anthropic/client.ts +16 -8
- package/src/providers/managed-proxy/constants.ts +9 -11
- package/src/providers/managed-proxy/context.ts +14 -9
- package/src/providers/provider-send-message.ts +4 -52
- package/src/providers/registry.ts +29 -57
- package/src/providers/types.ts +1 -1
- package/src/runtime/actor-token-store.ts +0 -23
- package/src/runtime/auth/route-policy.ts +4 -0
- package/src/runtime/channel-invite-transports/telegram.ts +12 -6
- package/src/runtime/channel-retry-sweep.ts +6 -0
- package/src/runtime/http-router.ts +5 -1
- package/src/runtime/http-server.ts +101 -4
- package/src/runtime/http-types.ts +1 -0
- package/src/runtime/invite-instruction-generator.ts +25 -51
- package/src/runtime/invite-service.ts +0 -20
- package/src/runtime/middleware/error-handler.ts +1 -2
- package/src/runtime/routes/app-management-routes.ts +1 -0
- package/src/runtime/routes/attachment-routes.ts +1 -1
- package/src/runtime/routes/brain-graph-routes.ts +1 -1
- package/src/runtime/routes/btw-routes.ts +20 -1
- package/src/runtime/routes/call-routes.ts +1 -1
- package/src/runtime/routes/conversation-routes.ts +64 -24
- package/src/runtime/routes/debug-routes.ts +1 -1
- package/src/runtime/routes/diagnostics-routes.ts +2 -2
- package/src/runtime/routes/documents-routes.ts +3 -3
- package/src/runtime/routes/global-search-routes.ts +1 -1
- package/src/runtime/routes/guardian-bootstrap-routes.ts +0 -20
- package/src/runtime/routes/guardian-refresh-routes.ts +0 -20
- package/src/runtime/routes/inbound-message-handler.ts +10 -2
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +4 -0
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +5 -5
- package/src/runtime/routes/integrations/slack/share.ts +5 -5
- package/src/runtime/routes/log-export-routes.ts +122 -10
- package/src/runtime/routes/secret-routes.ts +4 -4
- package/src/runtime/routes/session-query-routes.ts +3 -3
- package/src/runtime/routes/settings-routes.ts +53 -0
- package/src/runtime/routes/trust-rules-routes.ts +1 -1
- package/src/runtime/routes/workspace-routes.ts +3 -0
- package/src/runtime/verification-templates.ts +1 -1
- package/src/security/credential-backend.ts +148 -0
- package/src/security/oauth2.ts +5 -5
- package/src/security/secret-allowlist.ts +1 -1
- package/src/security/secure-keys.ts +98 -160
- package/src/security/token-manager.ts +0 -7
- package/src/sequence/guardrails.ts +0 -4
- package/src/sequence/store.ts +1 -20
- package/src/sequence/types.ts +1 -36
- package/src/signals/bash.ts +157 -0
- package/src/signals/cancel.ts +69 -0
- package/src/signals/conversation-undo.ts +127 -0
- package/src/signals/trust-rule.ts +174 -0
- package/src/skills/clawhub.ts +5 -5
- package/src/skills/managed-store.ts +4 -4
- package/src/skills/skillssh-registry.ts +6 -1
- package/src/swarm/backend-claude-code.ts +6 -6
- package/src/swarm/worker-backend.ts +1 -1
- package/src/swarm/worker-runner.ts +1 -1
- package/src/telegram/bot-username.ts +11 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +366 -0
- package/src/telemetry/usage-telemetry-reporter.ts +181 -0
- package/src/tools/claude-code/claude-code.ts +6 -6
- package/src/tools/credentials/broker.ts +7 -5
- package/src/tools/credentials/vault.ts +11 -6
- package/src/tools/memory/handlers.test.ts +24 -26
- package/src/tools/memory/handlers.ts +1 -13
- package/src/tools/network/__tests__/web-search.test.ts +18 -86
- package/src/tools/network/web-search.ts +9 -15
- package/src/tools/registry.ts +5 -100
- package/src/tools/terminal/parser.ts +34 -4
- package/src/tools/tool-manifest.ts +0 -10
- package/src/usage/actors.ts +0 -12
- package/src/util/canonicalize-identity.ts +0 -9
- package/src/util/errors.ts +0 -3
- package/src/util/platform.ts +31 -8
- package/src/util/pricing.ts +0 -39
- package/src/watcher/constants.ts +0 -7
- package/src/watcher/providers/linear.ts +1 -1
- package/src/work-items/work-item-store.ts +4 -4
- package/src/workspace/commit-message-provider.ts +1 -1
- package/src/workspace/git-service.ts +44 -1
- package/src/workspace/provider-commit-message-generator.ts +11 -7
- package/src/__tests__/fixtures/proxy-fixtures.ts +0 -147
- package/src/browser-extension-relay/client.ts +0 -155
- package/src/contacts/index.ts +0 -18
- package/src/daemon/tls-certs.ts +0 -270
- package/src/errors.ts +0 -41
- package/src/events/index.ts +0 -18
- package/src/followups/index.ts +0 -10
- package/src/playbooks/index.ts +0 -10
- package/src/runtime/auth/index.ts +0 -44
- package/src/tasks/candidate-store.ts +0 -95
- package/src/tools/browser/api-map.ts +0 -313
- package/src/tools/browser/auto-navigate.ts +0 -469
- package/src/tools/browser/headless-browser.ts +0 -590
- package/src/tools/browser/recording-store.ts +0 -75
- package/src/tools/computer-use/registry.ts +0 -21
- package/src/tools/tasks/index.ts +0 -27
|
@@ -22,89 +22,6 @@ export interface PublishedPageRecord {
|
|
|
22
22
|
projectSlug: string | null;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
export function createPublishedPage(record: {
|
|
26
|
-
id: string;
|
|
27
|
-
deploymentId: string;
|
|
28
|
-
publicUrl: string;
|
|
29
|
-
pageTitle?: string;
|
|
30
|
-
htmlHash: string;
|
|
31
|
-
appId?: string;
|
|
32
|
-
projectSlug?: string;
|
|
33
|
-
}): void {
|
|
34
|
-
const db = getDb();
|
|
35
|
-
db.insert(publishedPages)
|
|
36
|
-
.values({
|
|
37
|
-
id: record.id,
|
|
38
|
-
deploymentId: record.deploymentId,
|
|
39
|
-
publicUrl: record.publicUrl,
|
|
40
|
-
pageTitle: record.pageTitle ?? null,
|
|
41
|
-
htmlHash: record.htmlHash,
|
|
42
|
-
publishedAt: Date.now(),
|
|
43
|
-
status: "active",
|
|
44
|
-
appId: record.appId ?? null,
|
|
45
|
-
projectSlug: record.projectSlug ?? null,
|
|
46
|
-
})
|
|
47
|
-
.run();
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export function getPublishedPageByDeploymentId(
|
|
51
|
-
deploymentId: string,
|
|
52
|
-
): PublishedPageRecord | null {
|
|
53
|
-
const db = getDb();
|
|
54
|
-
const row = db
|
|
55
|
-
.select()
|
|
56
|
-
.from(publishedPages)
|
|
57
|
-
.where(eq(publishedPages.deploymentId, deploymentId))
|
|
58
|
-
.get();
|
|
59
|
-
|
|
60
|
-
return row ?? null;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export function getPublishedPageByHash(
|
|
64
|
-
hash: string,
|
|
65
|
-
): PublishedPageRecord | null {
|
|
66
|
-
const db = getDb();
|
|
67
|
-
const row = db
|
|
68
|
-
.select()
|
|
69
|
-
.from(publishedPages)
|
|
70
|
-
.where(
|
|
71
|
-
and(
|
|
72
|
-
eq(publishedPages.htmlHash, hash),
|
|
73
|
-
eq(publishedPages.status, "active"),
|
|
74
|
-
),
|
|
75
|
-
)
|
|
76
|
-
.get();
|
|
77
|
-
|
|
78
|
-
return row ?? null;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export function listPublishedPages(): PublishedPageRecord[] {
|
|
82
|
-
const db = getDb();
|
|
83
|
-
return db
|
|
84
|
-
.select()
|
|
85
|
-
.from(publishedPages)
|
|
86
|
-
.where(eq(publishedPages.status, "active"))
|
|
87
|
-
.all();
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export function markDeleted(id: string): boolean {
|
|
91
|
-
const db = getDb();
|
|
92
|
-
const existing = db
|
|
93
|
-
.select({ id: publishedPages.id })
|
|
94
|
-
.from(publishedPages)
|
|
95
|
-
.where(eq(publishedPages.id, id))
|
|
96
|
-
.get();
|
|
97
|
-
|
|
98
|
-
if (!existing) return false;
|
|
99
|
-
|
|
100
|
-
db.update(publishedPages)
|
|
101
|
-
.set({ status: "deleted" })
|
|
102
|
-
.where(eq(publishedPages.id, id))
|
|
103
|
-
.run();
|
|
104
|
-
|
|
105
|
-
return true;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
25
|
export function getActivePublishedPageByAppId(
|
|
109
26
|
appId: string,
|
|
110
27
|
): PublishedPageRecord | null {
|
|
@@ -108,11 +108,3 @@ export function _resetQdrantBreaker(): void {
|
|
|
108
108
|
openedAt = 0;
|
|
109
109
|
halfOpenProbeInFlight = false;
|
|
110
110
|
}
|
|
111
|
-
|
|
112
|
-
/** @internal Test-only: get breaker state */
|
|
113
|
-
export function _getQdrantBreakerState(): {
|
|
114
|
-
state: BreakerState;
|
|
115
|
-
consecutiveFailures: number;
|
|
116
|
-
} {
|
|
117
|
-
return { state: breakerState, consecutiveFailures };
|
|
118
|
-
}
|
|
@@ -374,7 +374,14 @@ describe("Memory Retriever Pipeline", () => {
|
|
|
374
374
|
const convId = "conv-superseded";
|
|
375
375
|
|
|
376
376
|
insertConversation(db, convId, now - 60_000);
|
|
377
|
-
insertMessage(
|
|
377
|
+
insertMessage(
|
|
378
|
+
db,
|
|
379
|
+
"msg-s1",
|
|
380
|
+
convId,
|
|
381
|
+
"user",
|
|
382
|
+
"test superseded",
|
|
383
|
+
now - 50_000,
|
|
384
|
+
);
|
|
378
385
|
|
|
379
386
|
insertSegment(
|
|
380
387
|
db,
|
|
@@ -535,12 +542,6 @@ describe("Memory Retriever Pipeline", () => {
|
|
|
535
542
|
|
|
536
543
|
const requiredEmbedConfig: AssistantConfig = {
|
|
537
544
|
...TEST_CONFIG,
|
|
538
|
-
apiKeys: {
|
|
539
|
-
...TEST_CONFIG.apiKeys,
|
|
540
|
-
openai: "",
|
|
541
|
-
gemini: "",
|
|
542
|
-
ollama: "",
|
|
543
|
-
},
|
|
544
545
|
memory: {
|
|
545
546
|
...TEST_CONFIG.memory,
|
|
546
547
|
embeddings: {
|
|
@@ -595,7 +596,8 @@ describe("Memory Retriever Pipeline", () => {
|
|
|
595
596
|
role: "user" | "assistant";
|
|
596
597
|
content: Array<{ type: string; text?: string }>;
|
|
597
598
|
};
|
|
598
|
-
const recallText =
|
|
599
|
+
const recallText =
|
|
600
|
+
"<memory_context>\n\n<relevant_context>\nsome context\n</relevant_context>\n\n</memory_context>";
|
|
599
601
|
|
|
600
602
|
const msgs: Msg[] = [
|
|
601
603
|
{
|
|
@@ -615,7 +617,9 @@ describe("Memory Retriever Pipeline", () => {
|
|
|
615
617
|
const cleaned = stripMemoryRecallMessages(msgs, recallText);
|
|
616
618
|
expect(cleaned).toHaveLength(1);
|
|
617
619
|
expect(cleaned[0].role).toBe("user");
|
|
618
|
-
expect(cleaned[0].content[0].text).toBe(
|
|
620
|
+
expect(cleaned[0].content[0].text).toBe(
|
|
621
|
+
"Hello, what do you know about me?",
|
|
622
|
+
);
|
|
619
623
|
});
|
|
620
624
|
|
|
621
625
|
test("stripMemoryRecallMessages: handles <memory_context> with slightly different content", () => {
|
|
@@ -623,8 +627,10 @@ describe("Memory Retriever Pipeline", () => {
|
|
|
623
627
|
role: "user" | "assistant";
|
|
624
628
|
content: Array<{ type: string; text?: string }>;
|
|
625
629
|
};
|
|
626
|
-
const originalRecall =
|
|
627
|
-
|
|
630
|
+
const originalRecall =
|
|
631
|
+
"<memory_context>\n\n<relevant_context>\noriginal\n</relevant_context>\n\n</memory_context>";
|
|
632
|
+
const actualRecall =
|
|
633
|
+
"<memory_context>\n\n<relevant_context>\nslightly different\n</relevant_context>\n\n</memory_context>";
|
|
628
634
|
|
|
629
635
|
const msgs: Msg[] = [
|
|
630
636
|
{
|
|
@@ -663,7 +669,8 @@ describe("Memory Retriever Pipeline", () => {
|
|
|
663
669
|
},
|
|
664
670
|
];
|
|
665
671
|
|
|
666
|
-
const recallText =
|
|
672
|
+
const recallText =
|
|
673
|
+
"<memory_context>\n\n<relevant_context>\ntest\n</relevant_context>\n\n</memory_context>";
|
|
667
674
|
const result = injectMemoryRecallAsSeparateMessage(msgs, recallText);
|
|
668
675
|
|
|
669
676
|
expect(result).toHaveLength(3);
|
package/src/memory/retriever.ts
CHANGED
|
@@ -166,7 +166,7 @@ async function generateQueryEmbedding(
|
|
|
166
166
|
signal: AbortSignal | undefined,
|
|
167
167
|
start: number,
|
|
168
168
|
): Promise<EmbeddingResult | { earlyExit: MemoryRecallResult }> {
|
|
169
|
-
const backendStatus = getMemoryBackendStatus(config);
|
|
169
|
+
const backendStatus = await getMemoryBackendStatus(config);
|
|
170
170
|
let queryVector: number[] | null = null;
|
|
171
171
|
let provider: string | undefined;
|
|
172
172
|
let model: string | undefined;
|
|
@@ -6,8 +6,6 @@ export const contacts = sqliteTable("contacts", {
|
|
|
6
6
|
id: text("id").primaryKey(),
|
|
7
7
|
displayName: text("display_name").notNull(),
|
|
8
8
|
notes: text("notes"),
|
|
9
|
-
lastInteraction: integer("last_interaction"), // epoch ms
|
|
10
|
-
interactionCount: integer("interaction_count").notNull().default(0),
|
|
11
9
|
createdAt: integer("created_at").notNull(),
|
|
12
10
|
updatedAt: integer("updated_at").notNull(),
|
|
13
11
|
role: text("role").notNull().default("contact"), // 'guardian' | 'contact'
|
|
@@ -37,6 +35,8 @@ export const contactChannels = sqliteTable(
|
|
|
37
35
|
revokedReason: text("revoked_reason"),
|
|
38
36
|
blockedReason: text("blocked_reason"),
|
|
39
37
|
lastSeenAt: integer("last_seen_at"), // epoch ms
|
|
38
|
+
interactionCount: integer("interaction_count").notNull().default(0),
|
|
39
|
+
lastInteraction: integer("last_interaction"),
|
|
40
40
|
updatedAt: integer("updated_at"), // epoch ms
|
|
41
41
|
createdAt: integer("created_at").notNull(),
|
|
42
42
|
},
|
|
@@ -17,7 +17,6 @@ export const oauthProviders = sqliteTable("oauth_providers", {
|
|
|
17
17
|
scopePolicy: text("scope_policy").notNull().default("{}"),
|
|
18
18
|
extraParams: text("extra_params"),
|
|
19
19
|
callbackTransport: text("callback_transport"),
|
|
20
|
-
loopbackPort: integer("loopback_port"),
|
|
21
20
|
pingUrl: text("ping_url"),
|
|
22
21
|
createdAt: integer("created_at").notNull(),
|
|
23
22
|
updatedAt: integer("updated_at").notNull(),
|
|
@@ -2,11 +2,7 @@ import { inArray } from "drizzle-orm";
|
|
|
2
2
|
|
|
3
3
|
import { getLogger } from "../../util/logger.js";
|
|
4
4
|
import { getDb } from "../db.js";
|
|
5
|
-
import {
|
|
6
|
-
_getQdrantBreakerState,
|
|
7
|
-
_resetQdrantBreaker,
|
|
8
|
-
withQdrantBreaker,
|
|
9
|
-
} from "../qdrant-circuit-breaker.js";
|
|
5
|
+
import { withQdrantBreaker } from "../qdrant-circuit-breaker.js";
|
|
10
6
|
import type {
|
|
11
7
|
QdrantSearchResult,
|
|
12
8
|
QdrantSparseVector,
|
|
@@ -24,9 +20,6 @@ import type { Candidate } from "./types.js";
|
|
|
24
20
|
|
|
25
21
|
const _log = getLogger("semantic-search");
|
|
26
22
|
|
|
27
|
-
// Re-export for tests that depend on these from this module
|
|
28
|
-
export { _getQdrantBreakerState, _resetQdrantBreaker };
|
|
29
|
-
|
|
30
23
|
export async function semanticSearch(
|
|
31
24
|
queryVector: number[],
|
|
32
25
|
_provider: string,
|
|
@@ -104,21 +104,6 @@ export function getSharedAppLink(
|
|
|
104
104
|
};
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
export function deleteSharedAppLink(id: string): boolean {
|
|
108
|
-
const db = getDb();
|
|
109
|
-
const existing = db
|
|
110
|
-
.select({ id: sharedAppLinks.id })
|
|
111
|
-
.from(sharedAppLinks)
|
|
112
|
-
.where(eq(sharedAppLinks.id, id))
|
|
113
|
-
.get();
|
|
114
|
-
|
|
115
|
-
if (!existing) return false;
|
|
116
|
-
|
|
117
|
-
db.delete(sharedAppLinks).where(eq(sharedAppLinks.id, id)).run();
|
|
118
|
-
|
|
119
|
-
return true;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
107
|
export function deleteSharedAppLinkByToken(shareToken: string): boolean {
|
|
123
108
|
const db = getDb();
|
|
124
109
|
const existing = db
|
|
@@ -127,7 +127,7 @@ export async function extractStylePatterns(
|
|
|
127
127
|
.map((e, i) => `--- Message ${i + 1} ---\n${e}`)
|
|
128
128
|
.join("\n\n");
|
|
129
129
|
|
|
130
|
-
const provider = getConfiguredProvider();
|
|
130
|
+
const provider = await getConfiguredProvider();
|
|
131
131
|
if (!provider) {
|
|
132
132
|
return { stylePatterns: [], contactObservations: [] };
|
|
133
133
|
}
|
|
@@ -117,21 +117,10 @@ export function buildAccessRequestIdentityLine(
|
|
|
117
117
|
return `${parts.join(" ")} is requesting access to the assistant.`;
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
export function buildAccessRequestDecisionDirective(
|
|
121
|
-
requestCode: string,
|
|
122
|
-
): string {
|
|
123
|
-
const code = requestCode.toUpperCase();
|
|
124
|
-
return `Reply "${code} approve" to grant access or "${code} reject" to deny.`;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
120
|
export function buildAccessRequestInviteDirective(): string {
|
|
128
121
|
return 'Reply "open invite flow" to start Trusted Contacts invite flow.';
|
|
129
122
|
}
|
|
130
123
|
|
|
131
|
-
export function buildAccessRequestRevokedNote(): string {
|
|
132
|
-
return "Note: this user was previously revoked.";
|
|
133
|
-
}
|
|
134
|
-
|
|
135
124
|
/**
|
|
136
125
|
* Normalize text before running directive-matching regexes.
|
|
137
126
|
*
|
|
@@ -222,10 +211,13 @@ export function buildAccessRequestContractText(
|
|
|
222
211
|
const lines: string[] = [];
|
|
223
212
|
lines.push(buildAccessRequestIdentityLine(payload));
|
|
224
213
|
if (previousMemberStatus === "revoked") {
|
|
225
|
-
lines.push(
|
|
214
|
+
lines.push("Note: this user was previously revoked.");
|
|
226
215
|
}
|
|
227
216
|
if (requestCode) {
|
|
228
|
-
|
|
217
|
+
const code = requestCode.toUpperCase();
|
|
218
|
+
lines.push(
|
|
219
|
+
`Reply "${code} approve" to grant access or "${code} reject" to deny.`,
|
|
220
|
+
);
|
|
229
221
|
}
|
|
230
222
|
lines.push(buildAccessRequestInviteDirective());
|
|
231
223
|
return lines.join("\n");
|
|
@@ -711,7 +711,7 @@ export async function evaluateSignal(
|
|
|
711
711
|
);
|
|
712
712
|
}
|
|
713
713
|
|
|
714
|
-
const provider = getConfiguredProvider();
|
|
714
|
+
const provider = await getConfiguredProvider();
|
|
715
715
|
if (!provider) {
|
|
716
716
|
log.warn(
|
|
717
717
|
"Configured provider unavailable for notification decision, using fallback",
|
|
@@ -767,7 +767,7 @@ async function classifyWithLLM(
|
|
|
767
767
|
modelIntent: ModelIntent,
|
|
768
768
|
candidateSet?: ThreadCandidateSet,
|
|
769
769
|
): Promise<NotificationDecision> {
|
|
770
|
-
const provider = getConfiguredProvider()!;
|
|
770
|
+
const provider = (await getConfiguredProvider())!;
|
|
771
771
|
const { signal: abortSignal, cleanup } = createTimeout(DECISION_TIMEOUT_MS);
|
|
772
772
|
|
|
773
773
|
const candidateContext = candidateSet
|
|
@@ -159,45 +159,6 @@ export function updateDeliveryStatus(
|
|
|
159
159
|
return rawChanges() > 0;
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
-
/**
|
|
163
|
-
* Update a delivery record with the client-side outcome of posting the
|
|
164
|
-
* notification via UNUserNotificationCenter.add().
|
|
165
|
-
*
|
|
166
|
-
* Returns true if a row was updated, false otherwise (e.g. unknown deliveryId).
|
|
167
|
-
*/
|
|
168
|
-
export function updateDeliveryClientOutcome(
|
|
169
|
-
deliveryId: string,
|
|
170
|
-
success: boolean,
|
|
171
|
-
error?: { code?: string; message?: string },
|
|
172
|
-
): boolean {
|
|
173
|
-
const db = getDb();
|
|
174
|
-
const now = Date.now();
|
|
175
|
-
|
|
176
|
-
const updates: Record<string, unknown> = {
|
|
177
|
-
clientDeliveryStatus: success ? "delivered" : "client_failed",
|
|
178
|
-
clientDeliveryAt: now,
|
|
179
|
-
updatedAt: now,
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
if (success) {
|
|
183
|
-
// Clear any stale error from previous failed attempts
|
|
184
|
-
updates.clientDeliveryError = null;
|
|
185
|
-
} else if (error?.message) {
|
|
186
|
-
updates.clientDeliveryError = error.code
|
|
187
|
-
? `[${error.code}] ${error.message}`
|
|
188
|
-
: error.message;
|
|
189
|
-
} else if (error?.code) {
|
|
190
|
-
updates.clientDeliveryError = error.code;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
db.update(notificationDeliveries)
|
|
194
|
-
.set(updates)
|
|
195
|
-
.where(eq(notificationDeliveries.id, deliveryId))
|
|
196
|
-
.run();
|
|
197
|
-
|
|
198
|
-
return rawChanges() > 0;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
162
|
/** Check whether a delivery already exists for a given decision+channel pair. */
|
|
202
163
|
export function findDeliveryByDecisionAndChannel(
|
|
203
164
|
decisionId: string,
|
|
@@ -6,15 +6,11 @@
|
|
|
6
6
|
* fields like `toolName`.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
export const GUARDIAN_QUESTION_REQUEST_KINDS = {
|
|
10
|
-
pending_question: "pending_question",
|
|
11
|
-
tool_approval: "tool_approval",
|
|
12
|
-
tool_grant_request: "tool_grant_request",
|
|
13
|
-
access_request: "access_request",
|
|
14
|
-
} as const;
|
|
15
|
-
|
|
16
9
|
export type GuardianQuestionRequestKind =
|
|
17
|
-
|
|
10
|
+
| "pending_question"
|
|
11
|
+
| "tool_approval"
|
|
12
|
+
| "tool_grant_request"
|
|
13
|
+
| "access_request";
|
|
18
14
|
export type GuardianQuestionInstructionMode = "approval" | "answer";
|
|
19
15
|
|
|
20
16
|
interface GuardianRequestKindModeConfig {
|
|
@@ -151,7 +147,7 @@ function nonEmptyString(value: unknown): string | null {
|
|
|
151
147
|
return trimmed.length > 0 ? trimmed : null;
|
|
152
148
|
}
|
|
153
149
|
|
|
154
|
-
|
|
150
|
+
function parseGuardianQuestionRequestKind(
|
|
155
151
|
payload: Record<string, unknown>,
|
|
156
152
|
): GuardianQuestionRequestKind | null {
|
|
157
153
|
const raw = nonEmptyString(payload.requestKind);
|
|
@@ -241,7 +237,7 @@ export function parseGuardianQuestionPayload(
|
|
|
241
237
|
}
|
|
242
238
|
}
|
|
243
239
|
|
|
244
|
-
|
|
240
|
+
function resolveGuardianInstructionModeForRequestKind(
|
|
245
241
|
requestKind: GuardianQuestionRequestKind,
|
|
246
242
|
toolName?: string | null,
|
|
247
243
|
): GuardianQuestionInstructionMode {
|
|
@@ -140,7 +140,7 @@ const EXTRACTION_TOOL = {
|
|
|
140
140
|
export async function extractPreferences(
|
|
141
141
|
message: string,
|
|
142
142
|
): Promise<ExtractionResult> {
|
|
143
|
-
const provider = getConfiguredProvider();
|
|
143
|
+
const provider = await getConfiguredProvider();
|
|
144
144
|
if (!provider) {
|
|
145
145
|
log.debug("No provider available for preference extraction");
|
|
146
146
|
return { detected: false, preferences: [] };
|
|
@@ -117,7 +117,7 @@ mock.module("./oauth-store.js", () => ({
|
|
|
117
117
|
// Imports (after mocks)
|
|
118
118
|
// ---------------------------------------------------------------------------
|
|
119
119
|
|
|
120
|
-
import {
|
|
120
|
+
import { setSecureKeyAsync } from "../security/secure-keys.js";
|
|
121
121
|
import {
|
|
122
122
|
_resetInflightRefreshes,
|
|
123
123
|
_resetRefreshBreakers,
|
|
@@ -183,7 +183,7 @@ afterEach(() => {
|
|
|
183
183
|
}
|
|
184
184
|
});
|
|
185
185
|
|
|
186
|
-
function setupCredential(
|
|
186
|
+
async function setupCredential(
|
|
187
187
|
service: string,
|
|
188
188
|
opts?: { expiresAt?: number; grantedScopes?: string[] },
|
|
189
189
|
) {
|
|
@@ -214,13 +214,19 @@ function setupCredential(
|
|
|
214
214
|
accountInfo: null,
|
|
215
215
|
});
|
|
216
216
|
// Store access token in oauth-store key format
|
|
217
|
-
|
|
217
|
+
await setSecureKeyAsync(
|
|
218
|
+
`oauth_connection/${connId}/access_token`,
|
|
219
|
+
"test-access-token",
|
|
220
|
+
);
|
|
218
221
|
// Store refresh token and client_secret in secure keys (token-manager reads them)
|
|
219
|
-
|
|
222
|
+
await setSecureKeyAsync(
|
|
220
223
|
`oauth_connection/${connId}/refresh_token`,
|
|
221
224
|
"test-refresh-token",
|
|
222
225
|
);
|
|
223
|
-
|
|
226
|
+
await setSecureKeyAsync(
|
|
227
|
+
`oauth_app/${appId}/client_secret`,
|
|
228
|
+
"test-client-secret",
|
|
229
|
+
);
|
|
224
230
|
upsertCredentialMetadata(service, "access_token", {});
|
|
225
231
|
}
|
|
226
232
|
|
|
@@ -242,7 +248,7 @@ function createConnection(service = "integration:google"): BYOOAuthConnection {
|
|
|
242
248
|
describe("BYOOAuthConnection", () => {
|
|
243
249
|
describe("request()", () => {
|
|
244
250
|
test("makes authenticated request with Bearer token", async () => {
|
|
245
|
-
setupCredential("integration:google");
|
|
251
|
+
await setupCredential("integration:google");
|
|
246
252
|
const conn = createConnection();
|
|
247
253
|
|
|
248
254
|
const result = await conn.request({
|
|
@@ -266,7 +272,7 @@ describe("BYOOAuthConnection", () => {
|
|
|
266
272
|
});
|
|
267
273
|
|
|
268
274
|
test("appends query parameters", async () => {
|
|
269
|
-
setupCredential("integration:google");
|
|
275
|
+
await setupCredential("integration:google");
|
|
270
276
|
const conn = createConnection();
|
|
271
277
|
|
|
272
278
|
await conn.request({
|
|
@@ -282,7 +288,7 @@ describe("BYOOAuthConnection", () => {
|
|
|
282
288
|
});
|
|
283
289
|
|
|
284
290
|
test("uses per-request baseUrl override", async () => {
|
|
285
|
-
setupCredential("integration:google");
|
|
291
|
+
await setupCredential("integration:google");
|
|
286
292
|
const conn = createConnection();
|
|
287
293
|
|
|
288
294
|
await conn.request({
|
|
@@ -296,7 +302,7 @@ describe("BYOOAuthConnection", () => {
|
|
|
296
302
|
});
|
|
297
303
|
|
|
298
304
|
test("sends JSON body for POST requests", async () => {
|
|
299
|
-
setupCredential("integration:google");
|
|
305
|
+
await setupCredential("integration:google");
|
|
300
306
|
const conn = createConnection();
|
|
301
307
|
|
|
302
308
|
await conn.request({
|
|
@@ -316,7 +322,7 @@ describe("BYOOAuthConnection", () => {
|
|
|
316
322
|
});
|
|
317
323
|
|
|
318
324
|
test("retries once on 401 response", async () => {
|
|
319
|
-
setupCredential("integration:google");
|
|
325
|
+
await setupCredential("integration:google");
|
|
320
326
|
const conn = createConnection();
|
|
321
327
|
|
|
322
328
|
// First call returns 401, second returns 200
|
|
@@ -344,7 +350,7 @@ describe("BYOOAuthConnection", () => {
|
|
|
344
350
|
});
|
|
345
351
|
|
|
346
352
|
test("handles empty response body", async () => {
|
|
347
|
-
setupCredential("integration:google");
|
|
353
|
+
await setupCredential("integration:google");
|
|
348
354
|
const conn = createConnection();
|
|
349
355
|
|
|
350
356
|
globalThis.fetch = mock(() =>
|
|
@@ -361,7 +367,7 @@ describe("BYOOAuthConnection", () => {
|
|
|
361
367
|
});
|
|
362
368
|
|
|
363
369
|
test("handles non-JSON response body", async () => {
|
|
364
|
-
setupCredential("integration:google");
|
|
370
|
+
await setupCredential("integration:google");
|
|
365
371
|
const conn = createConnection();
|
|
366
372
|
|
|
367
373
|
globalThis.fetch = mock(() =>
|
|
@@ -378,7 +384,7 @@ describe("BYOOAuthConnection", () => {
|
|
|
378
384
|
});
|
|
379
385
|
|
|
380
386
|
test("returns response headers", async () => {
|
|
381
|
-
setupCredential("integration:google");
|
|
387
|
+
await setupCredential("integration:google");
|
|
382
388
|
const conn = createConnection();
|
|
383
389
|
|
|
384
390
|
globalThis.fetch = mock(() =>
|
|
@@ -402,7 +408,7 @@ describe("BYOOAuthConnection", () => {
|
|
|
402
408
|
});
|
|
403
409
|
|
|
404
410
|
test("includes custom request headers", async () => {
|
|
405
|
-
setupCredential("integration:google");
|
|
411
|
+
await setupCredential("integration:google");
|
|
406
412
|
const conn = createConnection();
|
|
407
413
|
|
|
408
414
|
await conn.request({
|
|
@@ -421,7 +427,7 @@ describe("BYOOAuthConnection", () => {
|
|
|
421
427
|
describe("proactive token refresh", () => {
|
|
422
428
|
test("refreshes token when near expiry (within 5-minute buffer)", async () => {
|
|
423
429
|
// Set token to expire in 2 minutes (within 5-min buffer)
|
|
424
|
-
setupCredential("integration:google", {
|
|
430
|
+
await setupCredential("integration:google", {
|
|
425
431
|
expiresAt: Date.now() + 2 * 60 * 1000,
|
|
426
432
|
});
|
|
427
433
|
const conn = createConnection();
|
|
@@ -445,7 +451,7 @@ describe("BYOOAuthConnection", () => {
|
|
|
445
451
|
|
|
446
452
|
describe("withToken()", () => {
|
|
447
453
|
test("provides valid token to callback", async () => {
|
|
448
|
-
setupCredential("integration:google");
|
|
454
|
+
await setupCredential("integration:google");
|
|
449
455
|
const conn = createConnection();
|
|
450
456
|
|
|
451
457
|
const result = await conn.withToken(async (token) => {
|
|
@@ -456,7 +462,7 @@ describe("BYOOAuthConnection", () => {
|
|
|
456
462
|
});
|
|
457
463
|
|
|
458
464
|
test("retries callback on 401 error", async () => {
|
|
459
|
-
setupCredential("integration:google");
|
|
465
|
+
await setupCredential("integration:google");
|
|
460
466
|
const conn = createConnection();
|
|
461
467
|
|
|
462
468
|
let callCount = 0;
|
|
@@ -489,7 +495,7 @@ describe("BYOOAuthConnection", () => {
|
|
|
489
495
|
|
|
490
496
|
describe("resolveOAuthConnection", () => {
|
|
491
497
|
test("returns a BYOOAuthConnection for valid credential", async () => {
|
|
492
|
-
setupCredential("integration:google");
|
|
498
|
+
await setupCredential("integration:google");
|
|
493
499
|
const conn = await resolveOAuthConnection("integration:google");
|
|
494
500
|
|
|
495
501
|
expect(conn).toBeInstanceOf(BYOOAuthConnection);
|
|
@@ -504,7 +510,7 @@ describe("resolveOAuthConnection", () => {
|
|
|
504
510
|
});
|
|
505
511
|
|
|
506
512
|
test("throws when no base URL configured", async () => {
|
|
507
|
-
setupCredential("integration:custom-service");
|
|
513
|
+
await setupCredential("integration:custom-service");
|
|
508
514
|
await expect(
|
|
509
515
|
resolveOAuthConnection("integration:custom-service"),
|
|
510
516
|
).rejects.toThrow(
|
|
@@ -541,7 +547,10 @@ describe("resolveOAuthConnection", () => {
|
|
|
541
547
|
grantedScopes: JSON.stringify(["repo"]),
|
|
542
548
|
accountInfo: null,
|
|
543
549
|
});
|
|
544
|
-
|
|
550
|
+
await setSecureKeyAsync(
|
|
551
|
+
`oauth_connection/${connId}/access_token`,
|
|
552
|
+
"ghp-test-token",
|
|
553
|
+
);
|
|
545
554
|
|
|
546
555
|
const conn = await resolveOAuthConnection("integration:github-work");
|
|
547
556
|
|
|
@@ -48,6 +48,7 @@ function resolveBehavior(providerKey: string): {
|
|
|
48
48
|
setup?: OAuthProviderBehavior["setup"];
|
|
49
49
|
setupSkillId?: string;
|
|
50
50
|
postConnectHookId?: string;
|
|
51
|
+
loopbackPort?: number;
|
|
51
52
|
} {
|
|
52
53
|
const behavior = getProviderBehavior(providerKey);
|
|
53
54
|
if (!behavior) return {};
|
|
@@ -56,6 +57,7 @@ function resolveBehavior(providerKey: string): {
|
|
|
56
57
|
setup: behavior.setup,
|
|
57
58
|
setupSkillId: behavior.setupSkillId,
|
|
58
59
|
postConnectHookId: behavior.postConnectHookId,
|
|
60
|
+
loopbackPort: behavior.loopbackPort,
|
|
59
61
|
};
|
|
60
62
|
}
|
|
61
63
|
|
|
@@ -161,7 +163,7 @@ export async function orchestrateOAuthConnect(
|
|
|
161
163
|
const callbackTransport =
|
|
162
164
|
(providerRow.callbackTransport as "loopback" | "gateway" | null) ??
|
|
163
165
|
"loopback";
|
|
164
|
-
const loopbackPort =
|
|
166
|
+
const loopbackPort = behavior.loopbackPort;
|
|
165
167
|
|
|
166
168
|
// Resolve scopes via the scope policy engine
|
|
167
169
|
const scopeProfile = {
|
|
@@ -240,7 +242,7 @@ export async function orchestrateOAuthConnect(
|
|
|
240
242
|
const prepared = await prepareOAuth2Flow(
|
|
241
243
|
oauthConfig,
|
|
242
244
|
callbackTransport === "loopback"
|
|
243
|
-
? { callbackTransport, loopbackPort
|
|
245
|
+
? { callbackTransport, loopbackPort }
|
|
244
246
|
: callbackTransport === "gateway"
|
|
245
247
|
? { callbackTransport }
|
|
246
248
|
: undefined,
|
|
@@ -345,7 +347,7 @@ export async function orchestrateOAuthConnect(
|
|
|
345
347
|
},
|
|
346
348
|
},
|
|
347
349
|
callbackTransport === "loopback"
|
|
348
|
-
? { callbackTransport, loopbackPort
|
|
350
|
+
? { callbackTransport, loopbackPort }
|
|
349
351
|
: callbackTransport === "gateway"
|
|
350
352
|
? { callbackTransport }
|
|
351
353
|
: undefined,
|
|
@@ -34,8 +34,8 @@ export interface OAuthScopePolicy {
|
|
|
34
34
|
* Code-side behavioral configuration for a well-known OAuth provider.
|
|
35
35
|
*
|
|
36
36
|
* Protocol-level fields (authUrl, tokenUrl, defaultScopes, scopePolicy,
|
|
37
|
-
* tokenEndpointAuthMethod, callbackTransport,
|
|
38
|
-
*
|
|
37
|
+
* tokenEndpointAuthMethod, callbackTransport, userinfoUrl, extraParams)
|
|
38
|
+
* are stored in the `oauth_providers` DB table. This
|
|
39
39
|
* interface contains only fields that require code references (functions,
|
|
40
40
|
* templates, skill IDs) and cannot be serialised to a DB row.
|
|
41
41
|
*/
|
|
@@ -74,6 +74,13 @@ export interface OAuthProviderBehavior {
|
|
|
74
74
|
* agent to load this skill rather than embedding instructions inline.
|
|
75
75
|
*/
|
|
76
76
|
setupSkillId?: string;
|
|
77
|
+
/**
|
|
78
|
+
* Fixed port for the loopback OAuth callback server. When set, the
|
|
79
|
+
* server binds to this port instead of an OS-assigned random port.
|
|
80
|
+
* Required for providers that need pre-registered redirect URIs
|
|
81
|
+
* (e.g. Slack, Notion).
|
|
82
|
+
*/
|
|
83
|
+
loopbackPort?: number;
|
|
77
84
|
}
|
|
78
85
|
|
|
79
86
|
// ---------------------------------------------------------------------------
|