@vellumai/assistant 0.4.49 → 0.4.51
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 +24 -33
- package/README.md +3 -3
- package/docs/architecture/integrations.md +2 -2
- package/docs/architecture/keychain-broker.md +6 -6
- package/docs/architecture/memory.md +180 -119
- package/knip.json +32 -0
- package/package.json +3 -2
- package/src/__tests__/agent-loop.test.ts +3 -1
- package/src/__tests__/anthropic-provider.test.ts +114 -23
- package/src/__tests__/approval-cascade.test.ts +1 -15
- package/src/__tests__/approval-routes-http.test.ts +2 -0
- package/src/__tests__/assistant-feature-flag-guard.test.ts +0 -23
- package/src/__tests__/btw-routes.test.ts +61 -5
- package/src/__tests__/canonical-guardian-store.test.ts +95 -0
- package/src/__tests__/checker.test.ts +13 -0
- package/src/__tests__/config-schema.test.ts +1 -68
- package/src/__tests__/config-watcher.test.ts +8 -0
- package/src/__tests__/context-memory-e2e.test.ts +11 -100
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +8 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
- package/src/__tests__/credential-security-e2e.test.ts +1 -0
- package/src/__tests__/credential-security-invariants.test.ts +8 -7
- package/src/__tests__/credential-vault-unit.test.ts +23 -18
- package/src/__tests__/credential-vault.test.ts +30 -18
- package/src/__tests__/credentials-cli.test.ts +257 -82
- package/src/__tests__/cu-unified-flow.test.ts +532 -0
- package/src/__tests__/date-context.test.ts +93 -77
- package/src/__tests__/deterministic-verification-control-plane.test.ts +64 -0
- package/src/__tests__/guardian-routing-invariants.test.ts +93 -0
- package/src/__tests__/history-repair.test.ts +245 -0
- package/src/__tests__/host-cu-proxy.test.ts +165 -3
- package/src/__tests__/http-user-message-parity.test.ts +1 -0
- package/src/__tests__/inbound-invite-redemption.test.ts +36 -7
- package/src/__tests__/integration-status.test.ts +31 -30
- package/src/__tests__/invite-redemption-service.test.ts +166 -13
- package/src/__tests__/invite-routes-http.test.ts +166 -5
- package/src/__tests__/keychain-broker-client.test.ts +4 -4
- package/src/__tests__/list-messages-attachments.test.ts +193 -0
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +56 -18
- package/src/__tests__/memory-lifecycle-e2e.test.ts +244 -387
- package/src/__tests__/memory-recall-quality.test.ts +244 -407
- package/src/__tests__/memory-regressions.experimental.test.ts +126 -101
- package/src/__tests__/memory-regressions.test.ts +477 -2841
- package/src/__tests__/memory-retrieval.benchmark.test.ts +33 -150
- package/src/__tests__/memory-upsert-concurrency.test.ts +5 -244
- package/src/__tests__/mime-builder.test.ts +28 -0
- package/src/__tests__/native-web-search.test.ts +1 -0
- package/src/__tests__/oauth-cli.test.ts +824 -31
- package/src/__tests__/oauth-provider-profiles.test.ts +1 -1
- package/src/__tests__/oauth-store.test.ts +363 -17
- package/src/__tests__/qdrant-collection-migration.test.ts +53 -8
- package/src/__tests__/registry.test.ts +0 -1
- package/src/__tests__/relay-server.test.ts +55 -1
- package/src/__tests__/schedule-tools.test.ts +32 -0
- package/src/__tests__/script-proxy-certs.test.ts +1 -1
- package/src/__tests__/secret-onetime-send.test.ts +1 -0
- package/src/__tests__/secret-routes-managed-proxy.test.ts +183 -0
- package/src/__tests__/secure-keys.test.ts +78 -18
- package/src/__tests__/send-endpoint-busy.test.ts +3 -0
- package/src/__tests__/server-history-render.test.ts +2 -2
- package/src/__tests__/session-abort-tool-results.test.ts +1 -14
- package/src/__tests__/session-agent-loop-overflow.test.ts +1583 -0
- package/src/__tests__/session-agent-loop.test.ts +19 -15
- package/src/__tests__/session-confirmation-signals.test.ts +1 -15
- package/src/__tests__/session-error.test.ts +124 -2
- package/src/__tests__/session-history-web-search.test.ts +918 -0
- package/src/__tests__/session-pre-run-repair.test.ts +1 -14
- package/src/__tests__/session-provider-retry-repair.test.ts +25 -28
- package/src/__tests__/session-queue.test.ts +37 -27
- package/src/__tests__/session-runtime-assembly.test.ts +54 -0
- package/src/__tests__/session-slash-known.test.ts +1 -15
- package/src/__tests__/session-slash-queue.test.ts +1 -15
- package/src/__tests__/session-slash-unknown.test.ts +1 -15
- package/src/__tests__/session-workspace-cache-state.test.ts +3 -33
- package/src/__tests__/session-workspace-injection.test.ts +3 -37
- package/src/__tests__/session-workspace-tool-tracking.test.ts +3 -37
- package/src/__tests__/skills-install-extract.test.ts +93 -0
- package/src/__tests__/skills.test.ts +2 -2
- package/src/__tests__/skillssh-registry.test.ts +451 -0
- package/src/__tests__/slack-channel-config.test.ts +10 -8
- package/src/__tests__/trust-store.test.ts +15 -0
- package/src/__tests__/twilio-config.test.ts +11 -10
- package/src/__tests__/twilio-provider.test.ts +9 -4
- package/src/__tests__/voice-invite-redemption.test.ts +85 -5
- package/src/agent/ax-tree-compaction.test.ts +51 -0
- package/src/agent/loop.ts +39 -12
- package/src/approvals/AGENTS.md +1 -1
- package/src/approvals/guardian-request-resolvers.ts +14 -2
- package/src/bundler/compiler-tools.ts +66 -2
- package/src/calls/call-domain.ts +134 -3
- package/src/calls/call-store.ts +6 -0
- package/src/calls/relay-server.ts +44 -6
- package/src/calls/relay-setup-router.ts +17 -1
- package/src/calls/twilio-config.ts +5 -4
- package/src/calls/twilio-provider.ts +14 -9
- package/src/calls/twilio-rest.ts +10 -7
- package/src/calls/types.ts +3 -1
- package/src/cli/commands/config.ts +14 -9
- package/src/cli/commands/contacts.ts +3 -0
- package/src/cli/commands/credentials.ts +170 -174
- package/src/cli/commands/doctor.ts +11 -8
- package/src/cli/commands/keys.ts +9 -9
- package/src/cli/commands/mcp.ts +46 -59
- package/src/cli/commands/memory.ts +16 -165
- package/src/cli/commands/oauth/apps.ts +68 -10
- package/src/cli/commands/oauth/connections.ts +475 -105
- package/src/cli/commands/oauth/index.ts +3 -3
- package/src/cli/commands/oauth/providers.ts +18 -4
- package/src/cli/commands/sessions.ts +5 -2
- package/src/cli/commands/skills.ts +173 -1
- package/src/cli/http-client.ts +0 -20
- package/src/cli/main-screen.tsx +2 -2
- package/src/cli/program.ts +5 -6
- package/src/cli.ts +20 -22
- package/src/config/__tests__/feature-flag-registry-bundled.test.ts +39 -0
- package/src/config/bundled-skills/computer-use/TOOLS.json +1 -1
- package/src/config/bundled-skills/computer-use/tools/computer-use-observe.ts +12 -0
- package/src/config/bundled-skills/contacts/SKILL.md +35 -11
- package/src/config/bundled-skills/contacts/tools/google-contacts.ts +1 -1
- package/src/config/bundled-skills/gmail/SKILL.md +1 -1
- package/src/config/bundled-skills/gmail/TOOLS.json +52 -0
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +13 -3
- package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +9 -2
- package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-label.ts +9 -2
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +5 -1
- package/src/config/bundled-skills/google-calendar/TOOLS.json +20 -0
- package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +2 -1
- package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +2 -1
- package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +2 -1
- package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +2 -1
- package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +2 -1
- package/src/config/bundled-skills/google-calendar/tools/shared.ts +8 -2
- package/src/config/bundled-skills/messaging/SKILL.md +1 -1
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-auth-test.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-list-conversations.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-mark-read.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-read.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-search.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-sender-digest.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/shared.ts +7 -5
- package/src/config/bundled-skills/slack/tools/shared.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-add-reaction.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-channel-details.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-delete-message.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-edit-message.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-leave-channel.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +1 -1
- package/src/config/bundled-tool-registry.ts +2 -5
- package/src/config/loader.ts +6 -42
- package/src/config/schema.ts +1 -12
- package/src/config/schemas/memory-lifecycle.ts +0 -9
- package/src/config/schemas/memory-processing.ts +0 -180
- package/src/config/schemas/memory-retrieval.ts +32 -104
- package/src/config/schemas/memory.ts +0 -10
- package/src/config/types.ts +0 -4
- package/src/contacts/contact-store.ts +39 -2
- package/src/contacts/contacts-write.ts +9 -0
- package/src/context/window-manager.ts +4 -1
- package/src/daemon/config-watcher.ts +55 -2
- package/src/daemon/daemon-control.ts +1 -1
- package/src/daemon/date-context.ts +114 -31
- package/src/daemon/handlers/config-ingress.ts +2 -2
- package/src/daemon/handlers/config-slack-channel.ts +59 -39
- package/src/daemon/handlers/config-telegram.ts +23 -14
- package/src/daemon/handlers/session-history.ts +1 -358
- package/src/daemon/handlers/sessions.ts +18 -13
- package/src/daemon/handlers/shared.ts +3 -17
- package/src/daemon/handlers/skills.ts +20 -1
- package/src/daemon/history-repair.ts +72 -8
- package/src/daemon/host-cu-proxy.ts +55 -26
- package/src/daemon/lifecycle.ts +39 -4
- package/src/daemon/mcp-reload-service.ts +2 -2
- package/src/daemon/message-types/computer-use.ts +1 -12
- package/src/daemon/message-types/memory.ts +4 -16
- package/src/daemon/message-types/messages.ts +1 -0
- package/src/daemon/message-types/sessions.ts +4 -42
- package/src/daemon/server.ts +6 -1
- package/src/daemon/session-agent-loop-handlers.ts +38 -0
- package/src/daemon/session-agent-loop.ts +334 -48
- package/src/daemon/session-error.ts +89 -6
- package/src/daemon/session-history.ts +17 -7
- package/src/daemon/session-media-retry.ts +6 -2
- package/src/daemon/session-memory.ts +69 -149
- package/src/daemon/session-process.ts +10 -1
- package/src/daemon/session-runtime-assembly.ts +49 -19
- package/src/daemon/session-slash.ts +3 -5
- package/src/daemon/session-surfaces.ts +4 -1
- package/src/daemon/session-tool-setup.ts +7 -1
- package/src/daemon/session.ts +12 -2
- package/src/email/providers/index.ts +2 -2
- package/src/instrument.ts +61 -1
- package/src/media/avatar-router.ts +1 -1
- package/src/memory/admin.ts +2 -191
- package/src/memory/canonical-guardian-store.ts +38 -2
- package/src/memory/conversation-crud.ts +0 -33
- package/src/memory/conversation-queries.ts +25 -83
- package/src/memory/db-init.ts +32 -0
- package/src/memory/embedding-backend.ts +84 -8
- package/src/memory/embedding-types.ts +9 -1
- package/src/memory/indexer.ts +7 -46
- package/src/memory/invite-store.ts +19 -0
- package/src/memory/items-extractor.ts +274 -76
- package/src/memory/job-handlers/backfill.ts +2 -127
- package/src/memory/job-handlers/cleanup.ts +2 -16
- package/src/memory/job-handlers/extraction.ts +2 -138
- package/src/memory/job-handlers/index-maintenance.ts +1 -6
- package/src/memory/job-handlers/summarization.ts +3 -148
- package/src/memory/job-utils.ts +21 -59
- package/src/memory/jobs-store.ts +1 -159
- package/src/memory/jobs-worker.ts +9 -52
- package/src/memory/migrations/104-core-indexes.ts +3 -3
- package/src/memory/migrations/149-oauth-tables.ts +2 -0
- package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +98 -0
- package/src/memory/migrations/151-oauth-providers-ping-url.ts +11 -0
- package/src/memory/migrations/152-memory-item-supersession.ts +44 -0
- package/src/memory/migrations/153-drop-entity-tables.ts +15 -0
- package/src/memory/migrations/154-drop-fts.ts +20 -0
- package/src/memory/migrations/155-drop-conflicts.ts +7 -0
- package/src/memory/migrations/156-call-session-invite-metadata.ts +24 -0
- package/src/memory/migrations/157-invite-contact-id.ts +104 -0
- package/src/memory/migrations/index.ts +8 -0
- package/src/memory/migrations/registry.ts +6 -0
- package/src/memory/qdrant-client.ts +148 -51
- package/src/memory/raw-query.ts +1 -1
- package/src/memory/retriever.test.ts +294 -273
- package/src/memory/retriever.ts +421 -645
- package/src/memory/schema/calls.ts +2 -0
- package/src/memory/schema/contacts.ts +1 -0
- package/src/memory/schema/memory-core.ts +3 -48
- package/src/memory/schema/oauth.ts +2 -0
- package/src/memory/search/formatting.ts +263 -176
- package/src/memory/search/lexical.ts +1 -254
- package/src/memory/search/ranking.ts +0 -455
- package/src/memory/search/semantic.ts +100 -14
- package/src/memory/search/staleness.ts +47 -0
- package/src/memory/search/tier-classifier.ts +21 -0
- package/src/memory/search/types.ts +15 -77
- package/src/memory/task-memory-cleanup.ts +4 -6
- package/src/messaging/provider.ts +1 -1
- package/src/messaging/providers/gmail/adapter.ts +1 -1
- package/src/messaging/providers/gmail/mime-builder.ts +17 -7
- package/src/messaging/providers/telegram-bot/adapter.ts +17 -8
- package/src/messaging/providers/whatsapp/adapter.ts +13 -9
- package/src/messaging/registry.ts +9 -5
- package/src/oauth/byo-connection.test.ts +40 -25
- package/src/oauth/connect-orchestrator.ts +4 -10
- package/src/oauth/connection-resolver.ts +20 -6
- package/src/oauth/manual-token-connection.ts +5 -5
- package/src/oauth/oauth-store.ts +183 -31
- package/src/oauth/platform-connection.test.ts +1 -1
- package/src/oauth/provider-behaviors.ts +503 -4
- package/src/oauth/seed-providers.ts +214 -8
- package/src/oauth/token-persistence.ts +31 -16
- package/src/permissions/defaults.ts +1 -0
- package/src/permissions/trust-store.ts +23 -1
- package/src/playbooks/playbook-compiler.ts +1 -1
- package/src/prompts/system-prompt.ts +18 -2
- package/src/providers/anthropic/client.ts +56 -126
- package/src/providers/types.ts +7 -1
- package/src/runtime/AGENTS.md +9 -0
- package/src/runtime/auth/route-policy.ts +6 -3
- package/src/runtime/channel-readiness-service.ts +48 -40
- package/src/runtime/guardian-reply-router.ts +24 -22
- package/src/runtime/http-server.ts +2 -2
- package/src/runtime/http-types.ts +2 -0
- package/src/runtime/invite-redemption-service.ts +72 -12
- package/src/runtime/invite-service.ts +43 -0
- package/src/runtime/middleware/twilio-validation.ts +1 -1
- package/src/runtime/pending-interactions.ts +2 -2
- package/src/runtime/routes/brain-graph-routes.ts +10 -90
- package/src/runtime/routes/btw-routes.ts +10 -5
- package/src/runtime/routes/conversation-routes.ts +56 -11
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +21 -12
- package/src/runtime/routes/integrations/slack/channel.ts +2 -2
- package/src/runtime/routes/integrations/telegram.ts +2 -2
- package/src/runtime/routes/integrations/twilio.ts +17 -17
- package/src/runtime/routes/invite-routes.ts +29 -4
- package/src/runtime/routes/memory-item-routes.test.ts +754 -0
- package/src/runtime/routes/memory-item-routes.ts +503 -0
- package/src/runtime/routes/secret-routes.ts +17 -0
- package/src/runtime/routes/session-management-routes.ts +3 -3
- package/src/runtime/routes/settings-routes.ts +3 -3
- package/src/runtime/routes/trust-rules-routes.ts +14 -0
- package/src/runtime/routes/workspace-routes.ts +9 -4
- package/src/runtime/routes/workspace-utils.ts +8 -2
- package/src/schedule/integration-status.ts +26 -19
- package/src/security/keychain-broker-client.ts +17 -4
- package/src/security/oauth2.ts +6 -7
- package/src/security/secure-keys.ts +44 -19
- package/src/security/token-manager.ts +46 -39
- package/src/services/vercel-deploy.ts +0 -24
- package/src/signals/confirm.ts +78 -0
- package/src/signals/mcp-reload.ts +18 -0
- package/src/skills/catalog-install.ts +74 -18
- package/src/skills/skillssh-registry.ts +503 -0
- package/src/tools/assets/search.ts +5 -1
- package/src/tools/computer-use/definitions.ts +0 -10
- package/src/tools/computer-use/registry.ts +1 -1
- package/src/tools/credentials/vault.ts +22 -7
- package/src/tools/memory/definitions.ts +4 -13
- package/src/tools/memory/handlers.test.ts +83 -103
- package/src/tools/memory/handlers.ts +50 -85
- package/src/tools/network/script-proxy/session-manager.ts +8 -8
- package/src/tools/schedule/create.ts +10 -3
- package/src/tools/schedule/update.ts +8 -1
- package/src/tools/skills/load.ts +25 -2
- package/src/watcher/provider-types.ts +1 -1
- package/src/watcher/providers/github.ts +1 -1
- package/src/watcher/providers/gmail.ts +3 -3
- package/src/watcher/providers/google-calendar.ts +3 -3
- package/src/watcher/providers/linear.ts +1 -1
- package/src/__tests__/clarification-resolver.test.ts +0 -193
- package/src/__tests__/conflict-intent-tokenization.test.ts +0 -160
- package/src/__tests__/conflict-policy.test.ts +0 -269
- package/src/__tests__/conflict-store.test.ts +0 -372
- package/src/__tests__/contradiction-checker.test.ts +0 -361
- package/src/__tests__/entity-extractor.test.ts +0 -211
- package/src/__tests__/entity-search.test.ts +0 -1117
- package/src/__tests__/profile-compiler.test.ts +0 -392
- package/src/__tests__/session-conflict-gate.test.ts +0 -1228
- package/src/__tests__/session-profile-injection.test.ts +0 -557
- package/src/config/bundled-skills/knowledge-graph/SKILL.md +0 -25
- package/src/config/bundled-skills/knowledge-graph/TOOLS.json +0 -66
- package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +0 -211
- package/src/daemon/session-conflict-gate.ts +0 -167
- package/src/daemon/session-dynamic-profile.ts +0 -77
- package/src/memory/clarification-resolver.ts +0 -417
- package/src/memory/conflict-intent.ts +0 -205
- package/src/memory/conflict-policy.ts +0 -127
- package/src/memory/conflict-store.ts +0 -410
- package/src/memory/contradiction-checker.ts +0 -508
- package/src/memory/entity-extractor.ts +0 -535
- package/src/memory/format-recall.ts +0 -47
- package/src/memory/fts-reconciler.ts +0 -165
- package/src/memory/job-handlers/conflict.ts +0 -200
- package/src/memory/profile-compiler.ts +0 -195
- package/src/memory/recall-cache.ts +0 -117
- package/src/memory/search/entity.ts +0 -535
- package/src/memory/search/query-expansion.test.ts +0 -70
- package/src/memory/search/query-expansion.ts +0 -118
- package/src/runtime/routes/mcp-routes.ts +0 -20
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
export type CandidateType = "segment" | "item" | "summary" | "media";
|
|
2
|
-
export type CandidateSource =
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
| "recency"
|
|
6
|
-
| "entity_direct"
|
|
7
|
-
| "entity_relation"
|
|
8
|
-
| "item_direct";
|
|
2
|
+
export type CandidateSource = "semantic" | "recency";
|
|
3
|
+
|
|
4
|
+
export type StalenessLevel = "fresh" | "aging" | "stale" | "very_stale";
|
|
9
5
|
|
|
10
6
|
export interface Candidate {
|
|
11
7
|
key: string;
|
|
@@ -18,10 +14,11 @@ export interface Candidate {
|
|
|
18
14
|
confidence: number;
|
|
19
15
|
importance: number;
|
|
20
16
|
createdAt: number;
|
|
21
|
-
lexical: number;
|
|
22
17
|
semantic: number;
|
|
23
18
|
recency: number;
|
|
24
19
|
finalScore: number;
|
|
20
|
+
tier?: 1 | 2 | null;
|
|
21
|
+
staleness?: StalenessLevel;
|
|
25
22
|
}
|
|
26
23
|
|
|
27
24
|
export interface MemoryRecallCandiateDebug {
|
|
@@ -29,7 +26,6 @@ export interface MemoryRecallCandiateDebug {
|
|
|
29
26
|
type: CandidateType;
|
|
30
27
|
kind: string;
|
|
31
28
|
finalScore: number;
|
|
32
|
-
lexical: number;
|
|
33
29
|
semantic: number;
|
|
34
30
|
recency: number;
|
|
35
31
|
}
|
|
@@ -39,7 +35,7 @@ export type DegradationReason =
|
|
|
39
35
|
| "qdrant_unavailable"
|
|
40
36
|
| "embedding_generation_failed";
|
|
41
37
|
|
|
42
|
-
export type FallbackSource = "
|
|
38
|
+
export type FallbackSource = "recency";
|
|
43
39
|
|
|
44
40
|
export interface DegradationStatus {
|
|
45
41
|
semanticUnavailable: boolean;
|
|
@@ -54,22 +50,22 @@ export interface MemoryRecallResult {
|
|
|
54
50
|
reason?: string;
|
|
55
51
|
provider?: string;
|
|
56
52
|
model?: string;
|
|
57
|
-
lexicalHits: number;
|
|
58
53
|
semanticHits: number;
|
|
59
54
|
recencyHits: number;
|
|
60
|
-
entityHits: number;
|
|
61
|
-
relationSeedEntityCount: number;
|
|
62
|
-
relationTraversedEdgeCount: number;
|
|
63
|
-
relationNeighborEntityCount: number;
|
|
64
|
-
relationExpandedItemCount: number;
|
|
65
|
-
earlyTerminated: boolean;
|
|
66
55
|
mergedCount: number;
|
|
67
56
|
selectedCount: number;
|
|
68
|
-
rerankApplied: boolean;
|
|
69
57
|
injectedTokens: number;
|
|
70
58
|
injectedText: string;
|
|
71
59
|
latencyMs: number;
|
|
72
60
|
topCandidates: MemoryRecallCandiateDebug[];
|
|
61
|
+
/** Count of tier 1 candidates after demotion. */
|
|
62
|
+
tier1Count?: number;
|
|
63
|
+
/** Count of tier 2 candidates after demotion. */
|
|
64
|
+
tier2Count?: number;
|
|
65
|
+
/** Milliseconds spent in the hybrid search step. */
|
|
66
|
+
hybridSearchMs?: number;
|
|
67
|
+
/** Whether sparse vectors were used in the hybrid search. */
|
|
68
|
+
sparseVectorUsed?: boolean;
|
|
73
69
|
}
|
|
74
70
|
|
|
75
71
|
/**
|
|
@@ -99,67 +95,9 @@ export interface MemoryRecallOptions {
|
|
|
99
95
|
maxInjectTokensOverride?: number;
|
|
100
96
|
}
|
|
101
97
|
|
|
102
|
-
export interface CollectedCandidates {
|
|
103
|
-
lexical: Candidate[];
|
|
104
|
-
recency: Candidate[];
|
|
105
|
-
semantic: Candidate[];
|
|
106
|
-
entity: Candidate[];
|
|
107
|
-
relationSeedEntityCount: number;
|
|
108
|
-
relationTraversedEdgeCount: number;
|
|
109
|
-
relationNeighborEntityCount: number;
|
|
110
|
-
relationExpandedItemCount: number;
|
|
111
|
-
earlyTerminated: boolean;
|
|
112
|
-
/** True when semantic search was attempted but threw an error. */
|
|
113
|
-
semanticSearchFailed: boolean;
|
|
114
|
-
/** True when semantic search was known to be unavailable before retrieval (no vector or breaker open). */
|
|
115
|
-
semanticUnavailable: boolean;
|
|
116
|
-
/** The error that caused semantic search to fail, if any. */
|
|
117
|
-
semanticSearchError?: unknown;
|
|
118
|
-
merged: Candidate[];
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
export interface EntitySearchResult {
|
|
122
|
-
candidates: Candidate[];
|
|
123
|
-
relationSeedEntityCount: number;
|
|
124
|
-
relationTraversedEdgeCount: number;
|
|
125
|
-
relationNeighborEntityCount: number;
|
|
126
|
-
relationExpandedItemCount: number;
|
|
127
|
-
candidateDepths?: Map<string, number>; // candidate key → BFS hop depth (1-based)
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
export interface MatchedEntityRow {
|
|
131
|
-
id: string;
|
|
132
|
-
name: string;
|
|
133
|
-
type: string;
|
|
134
|
-
aliases: string | null;
|
|
135
|
-
mention_count: number;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
98
|
export interface ItemMetadata {
|
|
139
99
|
accessCount: number;
|
|
140
100
|
lastUsedAt: number | null;
|
|
141
101
|
verificationState: string;
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
import type { EntityRelationType, EntityType } from "../entity-extractor.js";
|
|
145
|
-
|
|
146
|
-
export interface TraversalOptions {
|
|
147
|
-
maxEdges: number;
|
|
148
|
-
maxNeighborEntities: number;
|
|
149
|
-
maxDepth?: number; // default 3
|
|
150
|
-
relationTypes?: EntityRelationType[];
|
|
151
|
-
entityTypes?: EntityType[];
|
|
152
|
-
/** When true, only follow source→target edges (frontier must be on source side). */
|
|
153
|
-
directed?: boolean;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
export interface TraversalResult {
|
|
157
|
-
neighborEntityIds: string[];
|
|
158
|
-
traversedEdgeCount: number;
|
|
159
|
-
neighborDepths: Map<string, number>; // entityId → depth (1-based)
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
export interface TraversalStep {
|
|
163
|
-
relationTypes?: EntityRelationType[];
|
|
164
|
-
entityTypes?: EntityType[];
|
|
102
|
+
sourceConversationCount?: number;
|
|
165
103
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { getLogger } from "../util/logger.js";
|
|
2
2
|
import { rawGet, rawRun } from "./raw-query.js";
|
|
3
|
-
import { bumpMemoryVersion } from "./recall-cache.js";
|
|
4
3
|
|
|
5
4
|
const log = getLogger("task-memory-cleanup");
|
|
6
5
|
|
|
@@ -85,7 +84,6 @@ export function invalidateAssistantInferredItemsForConversation(
|
|
|
85
84
|
);
|
|
86
85
|
|
|
87
86
|
if (affected > 0) {
|
|
88
|
-
bumpMemoryVersion();
|
|
89
87
|
log.info(
|
|
90
88
|
{ conversationId, affected },
|
|
91
89
|
"Invalidated assistant-inferred memory items after task failure",
|
|
@@ -96,9 +94,9 @@ export function invalidateAssistantInferredItemsForConversation(
|
|
|
96
94
|
}
|
|
97
95
|
|
|
98
96
|
/**
|
|
99
|
-
* Cancel pending `extract_items`
|
|
100
|
-
*
|
|
101
|
-
*
|
|
97
|
+
* Cancel pending `extract_items` jobs whose messageId belongs to the given
|
|
98
|
+
* conversation. This drains the queue so the worker never processes them,
|
|
99
|
+
* complementing the runtime check in the extraction handler.
|
|
102
100
|
*/
|
|
103
101
|
function cancelPendingExtractionJobsForConversation(
|
|
104
102
|
conversationId: string,
|
|
@@ -109,7 +107,7 @@ function cancelPendingExtractionJobsForConversation(
|
|
|
109
107
|
SET status = 'failed',
|
|
110
108
|
last_error = 'conversation_failed',
|
|
111
109
|
updated_at = ?
|
|
112
|
-
WHERE type IN ('extract_items'
|
|
110
|
+
WHERE type IN ('extract_items')
|
|
113
111
|
AND status IN ('pending', 'running')
|
|
114
112
|
AND json_extract(payload, '$.messageId') IN (
|
|
115
113
|
SELECT id FROM messages WHERE conversation_id = ?
|
|
@@ -87,7 +87,7 @@ export interface MessagingProvider {
|
|
|
87
87
|
* for providers that don't use OAuth (e.g. Telegram bot tokens stored
|
|
88
88
|
* under a non-standard key).
|
|
89
89
|
*/
|
|
90
|
-
isConnected?(): boolean
|
|
90
|
+
isConnected?(): Promise<boolean>;
|
|
91
91
|
|
|
92
92
|
/** Platform-specific capabilities for tool routing (e.g. 'reactions', 'threads', 'labels'). */
|
|
93
93
|
capabilities: Set<string>;
|
|
@@ -86,7 +86,7 @@ function mapGmailMessage(msg: GmailMessage): Message {
|
|
|
86
86
|
export const gmailMessagingProvider: MessagingProvider = {
|
|
87
87
|
id: "gmail",
|
|
88
88
|
displayName: "Gmail",
|
|
89
|
-
credentialService: "integration:
|
|
89
|
+
credentialService: "integration:google",
|
|
90
90
|
capabilities: new Set([
|
|
91
91
|
"threads",
|
|
92
92
|
"labels",
|
|
@@ -21,6 +21,10 @@ export interface MimeMessageOptions {
|
|
|
21
21
|
attachments: MimeAttachment[];
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
function sanitizeHeaderValue(value: string): string {
|
|
25
|
+
return value.replace(/[\r\n]+/g, " ").trim();
|
|
26
|
+
}
|
|
27
|
+
|
|
24
28
|
function toBase64Url(input: Buffer): string {
|
|
25
29
|
return input
|
|
26
30
|
.toString("base64")
|
|
@@ -37,17 +41,23 @@ export function buildMultipartMime(options: MimeMessageOptions): string {
|
|
|
37
41
|
const { to, subject, body, inReplyTo, cc, bcc, attachments } = options;
|
|
38
42
|
const boundary = `----=_Part_${randomBytes(16).toString("hex")}`;
|
|
39
43
|
|
|
44
|
+
const sanitizedTo = sanitizeHeaderValue(to);
|
|
45
|
+
const sanitizedSubject = sanitizeHeaderValue(subject);
|
|
46
|
+
const sanitizedCc = cc ? sanitizeHeaderValue(cc) : undefined;
|
|
47
|
+
const sanitizedBcc = bcc ? sanitizeHeaderValue(bcc) : undefined;
|
|
48
|
+
const sanitizedInReplyTo = inReplyTo ? sanitizeHeaderValue(inReplyTo) : undefined;
|
|
49
|
+
|
|
40
50
|
const headers = [
|
|
41
|
-
`To: ${
|
|
42
|
-
`Subject: ${
|
|
51
|
+
`To: ${sanitizedTo}`,
|
|
52
|
+
`Subject: ${sanitizedSubject}`,
|
|
43
53
|
"MIME-Version: 1.0",
|
|
44
54
|
`Content-Type: multipart/mixed; boundary="${boundary}"`,
|
|
45
55
|
];
|
|
46
|
-
if (
|
|
47
|
-
if (
|
|
48
|
-
if (
|
|
49
|
-
headers.push(`In-Reply-To: ${
|
|
50
|
-
headers.push(`References: ${
|
|
56
|
+
if (sanitizedCc) headers.push(`Cc: ${sanitizedCc}`);
|
|
57
|
+
if (sanitizedBcc) headers.push(`Bcc: ${sanitizedBcc}`);
|
|
58
|
+
if (sanitizedInReplyTo) {
|
|
59
|
+
headers.push(`In-Reply-To: ${sanitizedInReplyTo}`);
|
|
60
|
+
headers.push(`References: ${sanitizedInReplyTo}`);
|
|
51
61
|
}
|
|
52
62
|
|
|
53
63
|
const parts: string[] = [];
|
|
@@ -18,7 +18,7 @@ import type { OAuthConnection } from "../../../oauth/connection.js";
|
|
|
18
18
|
import { getConnectionByProvider } from "../../../oauth/oauth-store.js";
|
|
19
19
|
import { mintDaemonDeliveryToken } from "../../../runtime/auth/token-service.js";
|
|
20
20
|
import { credentialKey } from "../../../security/credential-key.js";
|
|
21
|
-
import {
|
|
21
|
+
import { getSecureKeyAsync } from "../../../security/secure-keys.js";
|
|
22
22
|
import type { MessagingProvider } from "../../provider.js";
|
|
23
23
|
import type {
|
|
24
24
|
ConnectionInfo,
|
|
@@ -44,8 +44,8 @@ function getBearerToken(): string {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
/** Read the Telegram bot token from the credential vault. */
|
|
47
|
-
function getBotToken(): string | undefined {
|
|
48
|
-
return
|
|
47
|
+
async function getBotToken(): Promise<string | undefined> {
|
|
48
|
+
return getSecureKeyAsync(credentialKey("telegram", "bot_token"));
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
export const telegramBotMessagingProvider: MessagingProvider = {
|
|
@@ -55,22 +55,31 @@ export const telegramBotMessagingProvider: MessagingProvider = {
|
|
|
55
55
|
capabilities: new Set(["send"]),
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
|
-
* Custom connectivity check using the oauth_connection record
|
|
59
|
-
*
|
|
58
|
+
* Custom connectivity check using both the oauth_connection record AND
|
|
59
|
+
* actual keychain credentials. The connection row alone can become stale
|
|
60
|
+
* if clearTelegramConfig() returns early on a secure-key deletion error
|
|
61
|
+
* without removing the row. Checking both ensures we don't report
|
|
62
|
+
* Telegram as connected when secrets are missing.
|
|
60
63
|
*
|
|
61
64
|
* Both bot_token and webhook_secret are required — the gateway's
|
|
62
65
|
* /deliver/telegram endpoint rejects requests without the webhook
|
|
63
66
|
* secret, so partial credentials would cause every send to fail.
|
|
64
67
|
*/
|
|
65
|
-
isConnected(): boolean {
|
|
68
|
+
async isConnected(): Promise<boolean> {
|
|
66
69
|
const conn = getConnectionByProvider("telegram");
|
|
67
|
-
|
|
70
|
+
if (!(conn && conn.status === "active")) return false;
|
|
71
|
+
const botToken = await getBotToken();
|
|
72
|
+
if (botToken === undefined) return false;
|
|
73
|
+
const webhookSecret = await getSecureKeyAsync(
|
|
74
|
+
credentialKey("telegram", "webhook_secret"),
|
|
75
|
+
);
|
|
76
|
+
return !!webhookSecret;
|
|
68
77
|
},
|
|
69
78
|
|
|
70
79
|
async testConnection(
|
|
71
80
|
_connectionOrToken: OAuthConnection | string,
|
|
72
81
|
): Promise<ConnectionInfo> {
|
|
73
|
-
const botToken = getBotToken();
|
|
82
|
+
const botToken = await getBotToken();
|
|
74
83
|
if (!botToken) {
|
|
75
84
|
return {
|
|
76
85
|
connected: false,
|
|
@@ -16,7 +16,7 @@ import * as externalConversationStore from "../../../memory/external-conversatio
|
|
|
16
16
|
import type { OAuthConnection } from "../../../oauth/connection.js";
|
|
17
17
|
import { mintDaemonDeliveryToken } from "../../../runtime/auth/token-service.js";
|
|
18
18
|
import { credentialKey } from "../../../security/credential-key.js";
|
|
19
|
-
import {
|
|
19
|
+
import { getSecureKeyAsync } from "../../../security/secure-keys.js";
|
|
20
20
|
import type { MessagingProvider } from "../../provider.js";
|
|
21
21
|
import type {
|
|
22
22
|
ConnectionInfo,
|
|
@@ -42,11 +42,15 @@ function getBearerToken(): string {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
/** Check whether WhatsApp credentials are stored. */
|
|
45
|
-
function hasWhatsAppCredentials(): boolean {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
!!getSecureKey(credentialKey("whatsapp", "access_token"))
|
|
45
|
+
async function hasWhatsAppCredentials(): Promise<boolean> {
|
|
46
|
+
const phoneNumberId = await getSecureKeyAsync(
|
|
47
|
+
credentialKey("whatsapp", "phone_number_id"),
|
|
49
48
|
);
|
|
49
|
+
if (!phoneNumberId) return false;
|
|
50
|
+
const accessToken = await getSecureKeyAsync(
|
|
51
|
+
credentialKey("whatsapp", "access_token"),
|
|
52
|
+
);
|
|
53
|
+
return !!accessToken;
|
|
50
54
|
}
|
|
51
55
|
|
|
52
56
|
export const whatsappMessagingProvider: MessagingProvider = {
|
|
@@ -58,14 +62,14 @@ export const whatsappMessagingProvider: MessagingProvider = {
|
|
|
58
62
|
/**
|
|
59
63
|
* WhatsApp is connected when Meta Cloud API credentials are stored.
|
|
60
64
|
*/
|
|
61
|
-
isConnected(): boolean {
|
|
65
|
+
async isConnected(): Promise<boolean> {
|
|
62
66
|
return hasWhatsAppCredentials();
|
|
63
67
|
},
|
|
64
68
|
|
|
65
69
|
async testConnection(
|
|
66
70
|
_connectionOrToken: OAuthConnection | string,
|
|
67
71
|
): Promise<ConnectionInfo> {
|
|
68
|
-
if (!hasWhatsAppCredentials()) {
|
|
72
|
+
if (!(await hasWhatsAppCredentials())) {
|
|
69
73
|
return {
|
|
70
74
|
connected: false,
|
|
71
75
|
user: "unknown",
|
|
@@ -77,9 +81,9 @@ export const whatsappMessagingProvider: MessagingProvider = {
|
|
|
77
81
|
};
|
|
78
82
|
}
|
|
79
83
|
|
|
80
|
-
const phoneNumberId =
|
|
84
|
+
const phoneNumberId = (await getSecureKeyAsync(
|
|
81
85
|
credentialKey("whatsapp", "phone_number_id"),
|
|
82
|
-
)!;
|
|
86
|
+
))!;
|
|
83
87
|
|
|
84
88
|
return {
|
|
85
89
|
connected: true,
|
|
@@ -23,11 +23,15 @@ export function getMessagingProvider(id: string): MessagingProvider {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
/** Return all registered providers that have stored credentials. */
|
|
26
|
-
export function getConnectedProviders(): MessagingProvider[] {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
export async function getConnectedProviders(): Promise<MessagingProvider[]> {
|
|
27
|
+
const results: MessagingProvider[] = [];
|
|
28
|
+
for (const p of providers.values()) {
|
|
29
|
+
const connected = p.isConnected
|
|
30
|
+
? await p.isConnected()
|
|
31
|
+
: await isProviderConnected(p.credentialService);
|
|
32
|
+
if (connected) results.push(p);
|
|
33
|
+
}
|
|
34
|
+
return results;
|
|
31
35
|
}
|
|
32
36
|
|
|
33
37
|
/** Return all registered provider IDs. */
|
|
@@ -81,7 +81,12 @@ const mockConnections = new Map<
|
|
|
81
81
|
>();
|
|
82
82
|
const mockApps = new Map<
|
|
83
83
|
string,
|
|
84
|
-
{
|
|
84
|
+
{
|
|
85
|
+
id: string;
|
|
86
|
+
providerKey: string;
|
|
87
|
+
clientId: string;
|
|
88
|
+
clientSecretCredentialPath: string;
|
|
89
|
+
}
|
|
85
90
|
>();
|
|
86
91
|
const mockProviders = new Map<
|
|
87
92
|
string,
|
|
@@ -95,6 +100,12 @@ const mockProviders = new Map<
|
|
|
95
100
|
|
|
96
101
|
mock.module("./oauth-store.js", () => ({
|
|
97
102
|
getConnectionByProvider: (service: string) => mockConnections.get(service),
|
|
103
|
+
getConnection: (id: string) => {
|
|
104
|
+
for (const conn of mockConnections.values()) {
|
|
105
|
+
if (conn.id === id) return conn;
|
|
106
|
+
}
|
|
107
|
+
return undefined;
|
|
108
|
+
},
|
|
98
109
|
getApp: (id: string) => mockApps.get(id),
|
|
99
110
|
getProvider: (key: string) => mockProviders.get(key),
|
|
100
111
|
updateConnection: () => {},
|
|
@@ -184,7 +195,7 @@ function setupCredential(
|
|
|
184
195
|
tokenUrl: "https://oauth2.googleapis.com/token",
|
|
185
196
|
// Only well-known providers (gmail) have a baseUrl; custom services don't
|
|
186
197
|
baseUrl:
|
|
187
|
-
service === "integration:
|
|
198
|
+
service === "integration:google"
|
|
188
199
|
? "https://gmail.googleapis.com/gmail/v1/users/me"
|
|
189
200
|
: undefined,
|
|
190
201
|
});
|
|
@@ -192,6 +203,7 @@ function setupCredential(
|
|
|
192
203
|
id: appId,
|
|
193
204
|
providerKey: service,
|
|
194
205
|
clientId: "test-client-id",
|
|
206
|
+
clientSecretCredentialPath: `oauth_app/${appId}/client_secret`,
|
|
195
207
|
});
|
|
196
208
|
mockConnections.set(service, {
|
|
197
209
|
id: connId,
|
|
@@ -212,7 +224,7 @@ function setupCredential(
|
|
|
212
224
|
upsertCredentialMetadata(service, "access_token", {});
|
|
213
225
|
}
|
|
214
226
|
|
|
215
|
-
function createConnection(service = "integration:
|
|
227
|
+
function createConnection(service = "integration:google"): BYOOAuthConnection {
|
|
216
228
|
return new BYOOAuthConnection({
|
|
217
229
|
id: "test-cred-id",
|
|
218
230
|
providerKey: service,
|
|
@@ -230,7 +242,7 @@ function createConnection(service = "integration:gmail"): BYOOAuthConnection {
|
|
|
230
242
|
describe("BYOOAuthConnection", () => {
|
|
231
243
|
describe("request()", () => {
|
|
232
244
|
test("makes authenticated request with Bearer token", async () => {
|
|
233
|
-
setupCredential("integration:
|
|
245
|
+
setupCredential("integration:google");
|
|
234
246
|
const conn = createConnection();
|
|
235
247
|
|
|
236
248
|
const result = await conn.request({
|
|
@@ -254,7 +266,7 @@ describe("BYOOAuthConnection", () => {
|
|
|
254
266
|
});
|
|
255
267
|
|
|
256
268
|
test("appends query parameters", async () => {
|
|
257
|
-
setupCredential("integration:
|
|
269
|
+
setupCredential("integration:google");
|
|
258
270
|
const conn = createConnection();
|
|
259
271
|
|
|
260
272
|
await conn.request({
|
|
@@ -270,7 +282,7 @@ describe("BYOOAuthConnection", () => {
|
|
|
270
282
|
});
|
|
271
283
|
|
|
272
284
|
test("uses per-request baseUrl override", async () => {
|
|
273
|
-
setupCredential("integration:
|
|
285
|
+
setupCredential("integration:google");
|
|
274
286
|
const conn = createConnection();
|
|
275
287
|
|
|
276
288
|
await conn.request({
|
|
@@ -284,7 +296,7 @@ describe("BYOOAuthConnection", () => {
|
|
|
284
296
|
});
|
|
285
297
|
|
|
286
298
|
test("sends JSON body for POST requests", async () => {
|
|
287
|
-
setupCredential("integration:
|
|
299
|
+
setupCredential("integration:google");
|
|
288
300
|
const conn = createConnection();
|
|
289
301
|
|
|
290
302
|
await conn.request({
|
|
@@ -304,7 +316,7 @@ describe("BYOOAuthConnection", () => {
|
|
|
304
316
|
});
|
|
305
317
|
|
|
306
318
|
test("retries once on 401 response", async () => {
|
|
307
|
-
setupCredential("integration:
|
|
319
|
+
setupCredential("integration:google");
|
|
308
320
|
const conn = createConnection();
|
|
309
321
|
|
|
310
322
|
// First call returns 401, second returns 200
|
|
@@ -332,7 +344,7 @@ describe("BYOOAuthConnection", () => {
|
|
|
332
344
|
});
|
|
333
345
|
|
|
334
346
|
test("handles empty response body", async () => {
|
|
335
|
-
setupCredential("integration:
|
|
347
|
+
setupCredential("integration:google");
|
|
336
348
|
const conn = createConnection();
|
|
337
349
|
|
|
338
350
|
globalThis.fetch = mock(() =>
|
|
@@ -349,7 +361,7 @@ describe("BYOOAuthConnection", () => {
|
|
|
349
361
|
});
|
|
350
362
|
|
|
351
363
|
test("handles non-JSON response body", async () => {
|
|
352
|
-
setupCredential("integration:
|
|
364
|
+
setupCredential("integration:google");
|
|
353
365
|
const conn = createConnection();
|
|
354
366
|
|
|
355
367
|
globalThis.fetch = mock(() =>
|
|
@@ -366,7 +378,7 @@ describe("BYOOAuthConnection", () => {
|
|
|
366
378
|
});
|
|
367
379
|
|
|
368
380
|
test("returns response headers", async () => {
|
|
369
|
-
setupCredential("integration:
|
|
381
|
+
setupCredential("integration:google");
|
|
370
382
|
const conn = createConnection();
|
|
371
383
|
|
|
372
384
|
globalThis.fetch = mock(() =>
|
|
@@ -390,7 +402,7 @@ describe("BYOOAuthConnection", () => {
|
|
|
390
402
|
});
|
|
391
403
|
|
|
392
404
|
test("includes custom request headers", async () => {
|
|
393
|
-
setupCredential("integration:
|
|
405
|
+
setupCredential("integration:google");
|
|
394
406
|
const conn = createConnection();
|
|
395
407
|
|
|
396
408
|
await conn.request({
|
|
@@ -409,7 +421,7 @@ describe("BYOOAuthConnection", () => {
|
|
|
409
421
|
describe("proactive token refresh", () => {
|
|
410
422
|
test("refreshes token when near expiry (within 5-minute buffer)", async () => {
|
|
411
423
|
// Set token to expire in 2 minutes (within 5-min buffer)
|
|
412
|
-
setupCredential("integration:
|
|
424
|
+
setupCredential("integration:google", {
|
|
413
425
|
expiresAt: Date.now() + 2 * 60 * 1000,
|
|
414
426
|
});
|
|
415
427
|
const conn = createConnection();
|
|
@@ -433,7 +445,7 @@ describe("BYOOAuthConnection", () => {
|
|
|
433
445
|
|
|
434
446
|
describe("withToken()", () => {
|
|
435
447
|
test("provides valid token to callback", async () => {
|
|
436
|
-
setupCredential("integration:
|
|
448
|
+
setupCredential("integration:google");
|
|
437
449
|
const conn = createConnection();
|
|
438
450
|
|
|
439
451
|
const result = await conn.withToken(async (token) => {
|
|
@@ -444,7 +456,7 @@ describe("BYOOAuthConnection", () => {
|
|
|
444
456
|
});
|
|
445
457
|
|
|
446
458
|
test("retries callback on 401 error", async () => {
|
|
447
|
-
setupCredential("integration:
|
|
459
|
+
setupCredential("integration:google");
|
|
448
460
|
const conn = createConnection();
|
|
449
461
|
|
|
450
462
|
let callCount = 0;
|
|
@@ -476,29 +488,31 @@ describe("BYOOAuthConnection", () => {
|
|
|
476
488
|
});
|
|
477
489
|
|
|
478
490
|
describe("resolveOAuthConnection", () => {
|
|
479
|
-
test("returns a BYOOAuthConnection for valid credential", () => {
|
|
480
|
-
setupCredential("integration:
|
|
481
|
-
const conn = resolveOAuthConnection("integration:
|
|
491
|
+
test("returns a BYOOAuthConnection for valid credential", async () => {
|
|
492
|
+
setupCredential("integration:google");
|
|
493
|
+
const conn = await resolveOAuthConnection("integration:google");
|
|
482
494
|
|
|
483
495
|
expect(conn).toBeInstanceOf(BYOOAuthConnection);
|
|
484
|
-
expect(conn.providerKey).toBe("integration:
|
|
496
|
+
expect(conn.providerKey).toBe("integration:google");
|
|
485
497
|
expect(conn.grantedScopes).toEqual(["read", "write"]);
|
|
486
498
|
});
|
|
487
499
|
|
|
488
|
-
test("throws when no credential metadata exists", () => {
|
|
489
|
-
expect(
|
|
500
|
+
test("throws when no credential metadata exists", async () => {
|
|
501
|
+
await expect(resolveOAuthConnection("integration:unknown")).rejects.toThrow(
|
|
490
502
|
/No credential found for "integration:unknown"/,
|
|
491
503
|
);
|
|
492
504
|
});
|
|
493
505
|
|
|
494
|
-
test("throws when no base URL configured", () => {
|
|
506
|
+
test("throws when no base URL configured", async () => {
|
|
495
507
|
setupCredential("integration:custom-service");
|
|
496
|
-
expect(
|
|
508
|
+
await expect(
|
|
509
|
+
resolveOAuthConnection("integration:custom-service"),
|
|
510
|
+
).rejects.toThrow(
|
|
497
511
|
/No base URL configured for "integration:custom-service"/,
|
|
498
512
|
);
|
|
499
513
|
});
|
|
500
514
|
|
|
501
|
-
test("resolves base URL via app's canonical providerKey for custom credential_service", () => {
|
|
515
|
+
test("resolves base URL via app's canonical providerKey for custom credential_service", async () => {
|
|
502
516
|
// Set up a well-known provider with a baseUrl
|
|
503
517
|
mockProviders.set("github", {
|
|
504
518
|
key: "github",
|
|
@@ -514,6 +528,7 @@ describe("resolveOAuthConnection", () => {
|
|
|
514
528
|
id: appId,
|
|
515
529
|
providerKey: "github",
|
|
516
530
|
clientId: "test-client-id",
|
|
531
|
+
clientSecretCredentialPath: `oauth_app/${appId}/client_secret`,
|
|
517
532
|
});
|
|
518
533
|
|
|
519
534
|
// Connection uses the custom credential service as its providerKey
|
|
@@ -528,7 +543,7 @@ describe("resolveOAuthConnection", () => {
|
|
|
528
543
|
});
|
|
529
544
|
setSecureKey(`oauth_connection/${connId}/access_token`, "ghp-test-token");
|
|
530
545
|
|
|
531
|
-
const conn = resolveOAuthConnection("integration:github-work");
|
|
546
|
+
const conn = await resolveOAuthConnection("integration:github-work");
|
|
532
547
|
|
|
533
548
|
expect(conn).toBeInstanceOf(BYOOAuthConnection);
|
|
534
549
|
expect(conn.providerKey).toBe("integration:github-work");
|
|
@@ -48,7 +48,6 @@ function resolveBehavior(providerKey: string): {
|
|
|
48
48
|
setup?: OAuthProviderBehavior["setup"];
|
|
49
49
|
setupSkillId?: string;
|
|
50
50
|
postConnectHookId?: string;
|
|
51
|
-
injectionTemplates?: OAuthProviderBehavior["injectionTemplates"];
|
|
52
51
|
} {
|
|
53
52
|
const behavior = getProviderBehavior(providerKey);
|
|
54
53
|
if (!behavior) return {};
|
|
@@ -57,7 +56,6 @@ function resolveBehavior(providerKey: string): {
|
|
|
57
56
|
setup: behavior.setup,
|
|
58
57
|
setupSkillId: behavior.setupSkillId,
|
|
59
58
|
postConnectHookId: behavior.postConnectHookId,
|
|
60
|
-
injectionTemplates: behavior.injectionTemplates,
|
|
61
59
|
};
|
|
62
60
|
}
|
|
63
61
|
|
|
@@ -90,8 +88,6 @@ export interface OAuthConnectOptions {
|
|
|
90
88
|
openUrl?: (url: string) => void;
|
|
91
89
|
/** Send a message to the client (e.g. open_url). */
|
|
92
90
|
sendToClient?: (msg: { type: string; [key: string]: unknown }) => void;
|
|
93
|
-
/** Tools allowed to use the resulting credential. */
|
|
94
|
-
allowedTools?: string[];
|
|
95
91
|
|
|
96
92
|
/**
|
|
97
93
|
* Called when the deferred (non-interactive) flow completes — either
|
|
@@ -164,7 +160,7 @@ export async function orchestrateOAuthConnect(
|
|
|
164
160
|
| undefined;
|
|
165
161
|
const callbackTransport =
|
|
166
162
|
(providerRow.callbackTransport as "loopback" | "gateway" | null) ??
|
|
167
|
-
"
|
|
163
|
+
"loopback";
|
|
168
164
|
const loopbackPort = providerRow.loopbackPort;
|
|
169
165
|
|
|
170
166
|
// Resolve scopes via the scope policy engine
|
|
@@ -216,11 +212,7 @@ export async function orchestrateOAuthConnect(
|
|
|
216
212
|
service: resolvedService,
|
|
217
213
|
clientId: options.clientId,
|
|
218
214
|
clientSecret: options.clientSecret,
|
|
219
|
-
tokenUrl,
|
|
220
|
-
tokenEndpointAuthMethod,
|
|
221
215
|
userinfoUrl,
|
|
222
|
-
allowedTools: options.allowedTools,
|
|
223
|
-
wellKnownInjectionTemplates: behavior.injectionTemplates,
|
|
224
216
|
};
|
|
225
217
|
|
|
226
218
|
// -----------------------------------------------------------------------
|
|
@@ -249,7 +241,9 @@ export async function orchestrateOAuthConnect(
|
|
|
249
241
|
oauthConfig,
|
|
250
242
|
callbackTransport === "loopback"
|
|
251
243
|
? { callbackTransport, loopbackPort: loopbackPort ?? undefined }
|
|
252
|
-
:
|
|
244
|
+
: callbackTransport === "gateway"
|
|
245
|
+
? { callbackTransport }
|
|
246
|
+
: undefined,
|
|
253
247
|
);
|
|
254
248
|
|
|
255
249
|
// Fire-and-forget: store tokens when the callback arrives
|