@vellumai/assistant 0.5.9 → 0.5.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +9 -1
- package/ARCHITECTURE.md +48 -48
- package/Dockerfile +2 -0
- package/README.md +1 -1
- package/docs/architecture/integrations.md +6 -13
- package/docs/architecture/memory.md +7 -12
- package/docs/architecture/security.md +5 -5
- package/docs/credential-execution-service.md +9 -9
- package/docs/skills.md +1 -1
- package/node_modules/@vellumai/credential-storage/src/index.ts +2 -2
- package/node_modules/@vellumai/credential-storage/src/static-credentials.ts +1 -1
- package/openapi.yaml +7130 -0
- package/package.json +2 -1
- package/scripts/generate-openapi.ts +562 -0
- package/src/__tests__/acp-session.test.ts +239 -44
- package/src/__tests__/assistant-feature-flag-guard.test.ts +8 -8
- package/src/__tests__/assistant-feature-flag-guardrails.test.ts +5 -86
- package/src/__tests__/assistant-feature-flags-integration.test.ts +7 -14
- package/src/__tests__/browser-skill-endstate.test.ts +1 -1
- package/src/__tests__/btw-routes.test.ts +8 -0
- package/src/__tests__/bundled-skill-retrieval-guard.test.ts +10 -10
- package/src/__tests__/channel-approvals.test.ts +7 -7
- package/src/__tests__/channel-readiness-service.test.ts +41 -0
- package/src/__tests__/config-schema.test.ts +10 -2
- package/src/__tests__/context-memory-e2e.test.ts +2 -6
- package/src/__tests__/conversation-skill-tools.test.ts +1 -3
- package/src/__tests__/conversation-title-service.test.ts +2 -15
- package/src/__tests__/credential-execution-feature-gates.test.ts +4 -8
- package/src/__tests__/credential-execution-managed-contract.test.ts +8 -8
- package/src/__tests__/credential-security-e2e.test.ts +4 -4
- package/src/__tests__/credential-security-invariants.test.ts +3 -3
- package/src/__tests__/credentials-cli.test.ts +3 -3
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +1 -1
- package/src/__tests__/gateway-only-guard.test.ts +3 -0
- package/src/__tests__/heartbeat-service.test.ts +35 -0
- package/src/__tests__/host-shell-tool.test.ts +1 -1
- package/src/__tests__/inline-skill-load-permissions.test.ts +3 -3
- package/src/__tests__/llm-request-log-turn-query.test.ts +64 -0
- package/src/__tests__/log-export-workspace.test.ts +1 -1
- package/src/__tests__/mcp-client-auth.test.ts +1 -1
- package/src/__tests__/memory-lifecycle-e2e.test.ts +2 -2
- package/src/__tests__/memory-recall-log-store.test.ts +182 -0
- package/src/__tests__/memory-recall-quality.test.ts +6 -8
- package/src/__tests__/memory-regressions.test.ts +53 -42
- package/src/__tests__/memory-retrieval.benchmark.test.ts +5 -9
- package/src/__tests__/messaging-skill-split.test.ts +2 -17
- package/src/__tests__/oauth-cli.test.ts +98 -551
- package/src/__tests__/platform-callback-registration.test.ts +119 -0
- package/src/__tests__/secret-ingress-channel.test.ts +261 -0
- package/src/__tests__/secret-ingress-cli.test.ts +201 -0
- package/src/__tests__/secret-ingress-http.test.ts +312 -0
- package/src/__tests__/secret-ingress.test.ts +283 -0
- package/src/__tests__/secret-onetime-send.test.ts +4 -4
- package/src/__tests__/skill-feature-flags-integration.test.ts +4 -4
- package/src/__tests__/skill-feature-flags.test.ts +11 -19
- package/src/__tests__/skill-load-feature-flag.test.ts +1 -1
- package/src/__tests__/skill-load-inline-command.test.ts +3 -3
- package/src/__tests__/skill-load-inline-includes.test.ts +2 -2
- package/src/__tests__/skill-memory.test.ts +2 -4
- package/src/__tests__/skill-projection-feature-flag.test.ts +2 -4
- package/src/__tests__/skill-projection.benchmark.test.ts +1 -3
- package/src/__tests__/skills.test.ts +16 -2
- package/src/__tests__/slack-channel-config.test.ts +1 -1
- package/src/__tests__/slack-skill.test.ts +5 -69
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +1 -1
- package/src/__tests__/workspace-migration-015-migrate-credentials-to-keychain.test.ts +5 -238
- package/src/__tests__/workspace-migration-016-migrate-credentials-from-keychain.test.ts +5 -206
- package/src/__tests__/workspace-migration-018-rekey-compound-credential-keys.test.ts +181 -0
- package/src/__tests__/workspace-migrations-runner.test.ts +15 -7
- package/src/acp/client-handler.ts +113 -31
- package/src/acp/session-manager.ts +29 -27
- package/src/approvals/guardian-request-resolvers.ts +1 -1
- package/src/cli/AGENTS.md +73 -0
- package/src/cli/commands/autonomy.ts +3 -5
- package/src/cli/commands/credential-execution.ts +1 -2
- package/src/cli/commands/credentials.ts +4 -4
- package/src/cli/commands/memory.ts +2 -3
- package/src/cli/commands/oauth/__tests__/connect.test.ts +785 -0
- package/src/cli/commands/oauth/__tests__/disconnect.test.ts +760 -0
- package/src/cli/commands/oauth/__tests__/mode.test.ts +672 -0
- package/src/cli/commands/oauth/__tests__/ping.test.ts +690 -0
- package/src/cli/commands/oauth/__tests__/status.test.ts +579 -0
- package/src/cli/commands/oauth/__tests__/token.test.ts +467 -0
- package/src/cli/commands/oauth/apps.ts +29 -11
- package/src/cli/commands/oauth/connect.ts +373 -0
- package/src/cli/commands/oauth/connections.ts +14 -493
- package/src/cli/commands/oauth/disconnect.ts +333 -0
- package/src/cli/commands/oauth/index.ts +62 -10
- package/src/cli/commands/oauth/mode.ts +263 -0
- package/src/cli/commands/oauth/ping.ts +222 -0
- package/src/cli/commands/oauth/providers.ts +30 -3
- package/src/cli/commands/oauth/request.ts +576 -0
- package/src/cli/commands/oauth/shared.ts +132 -0
- package/src/cli/commands/oauth/status.ts +202 -0
- package/src/cli/commands/oauth/token.ts +159 -0
- package/src/cli/commands/platform.ts +20 -14
- package/src/cli.ts +82 -17
- package/src/config/assistant-feature-flags.ts +74 -11
- package/src/config/bundled-skills/_shared/CLI_RETRIEVAL_PATTERN.md +1 -1
- package/src/config/bundled-skills/app-builder/tools/app-create.ts +1 -1
- package/src/config/bundled-skills/messaging/SKILL.md +13 -36
- package/src/config/bundled-skills/messaging/TOOLS.json +9 -9
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +1 -1
- package/src/config/bundled-skills/notifications/SKILL.md +1 -1
- package/src/config/bundled-skills/schedule/SKILL.md +2 -2
- package/src/config/bundled-skills/settings/SKILL.md +5 -3
- package/src/config/bundled-skills/settings/TOOLS.json +17 -0
- package/src/config/bundled-skills/settings/tools/avatar-get.ts +50 -0
- package/src/config/bundled-skills/settings/tools/avatar-remove.ts +7 -0
- package/src/config/bundled-skills/settings/tools/avatar-update.ts +6 -1
- package/src/config/bundled-skills/settings/tools/identity-avatar.ts +55 -0
- package/src/config/bundled-skills/skills-catalog/SKILL.md +3 -3
- package/src/config/bundled-skills/slack/SKILL.md +58 -44
- package/src/config/bundled-tool-registry.ts +2 -19
- package/src/config/env.ts +5 -1
- package/src/config/feature-flag-registry.json +57 -41
- package/src/config/loader.ts +4 -0
- package/src/config/schemas/platform.ts +0 -8
- package/src/config/schemas/security.ts +9 -1
- package/src/config/schemas/services.ts +1 -1
- package/src/config/skill-state.ts +1 -3
- package/src/config/skills.ts +2 -4
- package/src/credential-execution/feature-gates.ts +9 -16
- package/src/credential-execution/process-manager.ts +12 -0
- package/src/daemon/config-watcher.ts +4 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +10 -0
- package/src/daemon/conversation-agent-loop.ts +49 -2
- package/src/daemon/conversation-memory.ts +0 -1
- package/src/daemon/handlers/config-slack-channel.ts +43 -1
- package/src/daemon/handlers/conversations.ts +41 -33
- package/src/daemon/lifecycle.ts +28 -5
- package/src/daemon/message-types/acp.ts +0 -15
- package/src/daemon/message-types/memory.ts +0 -1
- package/src/daemon/message-types/messages.ts +9 -1
- package/src/daemon/message-types/schedules.ts +9 -0
- package/src/daemon/server.ts +19 -7
- package/src/email/feature-gate.ts +3 -3
- package/src/heartbeat/heartbeat-service.ts +48 -0
- package/src/inbound/platform-callback-registration.ts +61 -7
- package/src/mcp/mcp-oauth-provider.ts +3 -3
- package/src/memory/app-store.ts +3 -3
- package/src/memory/conversation-crud.ts +124 -0
- package/src/memory/conversation-title-service.ts +7 -17
- package/src/memory/db-init.ts +8 -0
- package/src/memory/embedding-local.ts +47 -2
- package/src/memory/indexer.ts +13 -10
- package/src/memory/items-extractor.ts +12 -4
- package/src/memory/job-utils.ts +5 -0
- package/src/memory/jobs-store.ts +10 -2
- package/src/memory/journal-memory.ts +6 -2
- package/src/memory/llm-request-log-store.ts +88 -21
- package/src/memory/memory-recall-log-store.ts +128 -0
- package/src/memory/migrations/194-memory-recall-logs.ts +50 -0
- package/src/memory/migrations/195-oauth-providers-ping-config.ts +23 -0
- package/src/memory/migrations/index.ts +2 -0
- package/src/memory/migrations/validate-migration-state.ts +14 -1
- package/src/memory/retriever.test.ts +4 -5
- package/src/memory/schema/infrastructure.ts +31 -0
- package/src/memory/schema/oauth.ts +3 -0
- package/src/messaging/providers/telegram-bot/adapter.ts +1 -1
- package/src/oauth/connect-orchestrator.ts +54 -0
- package/src/oauth/manual-token-connection.ts +5 -5
- package/src/oauth/oauth-store.ts +26 -5
- package/src/oauth/seed-providers.ts +10 -1
- package/src/permissions/checker.ts +2 -2
- package/src/permissions/trust-client.ts +2 -2
- package/src/platform/client.ts +2 -2
- package/src/prompts/journal-context.ts +6 -1
- package/src/providers/anthropic/client.ts +143 -1
- package/src/runtime/auth/__tests__/middleware.test.ts +19 -0
- package/src/runtime/auth/route-policy.ts +0 -1
- package/src/runtime/btw-sidechain.ts +7 -1
- package/src/runtime/channel-approvals.ts +2 -2
- package/src/runtime/channel-readiness-service.ts +30 -7
- package/src/runtime/http-router.ts +31 -0
- package/src/runtime/http-server.ts +21 -4
- package/src/runtime/http-types.ts +2 -0
- package/src/runtime/pending-interactions.ts +21 -3
- package/src/runtime/routes/acp-routes.ts +46 -28
- package/src/runtime/routes/app-management-routes.ts +123 -0
- package/src/runtime/routes/app-routes.ts +31 -0
- package/src/runtime/routes/approval-routes.ts +108 -3
- package/src/runtime/routes/attachment-routes.ts +45 -0
- package/src/runtime/routes/avatar-routes.ts +16 -0
- package/src/runtime/routes/brain-graph-routes.ts +18 -0
- package/src/runtime/routes/btw-routes.ts +20 -0
- package/src/runtime/routes/call-routes.ts +81 -0
- package/src/runtime/routes/channel-readiness-routes.ts +48 -7
- package/src/runtime/routes/channel-routes.ts +18 -0
- package/src/runtime/routes/channel-verification-routes.ts +49 -1
- package/src/runtime/routes/contact-routes.ts +77 -0
- package/src/runtime/routes/conversation-attention-routes.ts +37 -0
- package/src/runtime/routes/conversation-management-routes.ts +94 -0
- package/src/runtime/routes/conversation-query-routes.ts +78 -0
- package/src/runtime/routes/conversation-routes.ts +115 -38
- package/src/runtime/routes/conversation-starter-routes.ts +29 -0
- package/src/runtime/routes/debug-routes.ts +23 -0
- package/src/runtime/routes/diagnostics-routes.ts +30 -0
- package/src/runtime/routes/documents-routes.ts +42 -0
- package/src/runtime/routes/events-routes.ts +10 -0
- package/src/runtime/routes/global-search-routes.ts +35 -0
- package/src/runtime/routes/guardian-action-routes.ts +47 -2
- package/src/runtime/routes/guardian-approval-prompt.ts +77 -2
- package/src/runtime/routes/heartbeat-routes.ts +278 -0
- package/src/runtime/routes/host-bash-routes.ts +16 -1
- package/src/runtime/routes/host-cu-routes.ts +23 -1
- package/src/runtime/routes/host-file-routes.ts +18 -1
- package/src/runtime/routes/identity-routes.ts +35 -0
- package/src/runtime/routes/inbound-message-handler.ts +46 -25
- package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +30 -2
- package/src/runtime/routes/inbound-stages/transcribe-audio.ts +1 -2
- package/src/runtime/routes/integrations/twilio.ts +32 -22
- package/src/runtime/routes/invite-routes.ts +83 -0
- package/src/runtime/routes/log-export-routes.ts +14 -0
- package/src/runtime/routes/memory-item-routes.ts +99 -1
- package/src/runtime/routes/migration-rollback-routes.ts +25 -0
- package/src/runtime/routes/migration-routes.ts +40 -0
- package/src/runtime/routes/notification-routes.ts +20 -0
- package/src/runtime/routes/oauth-apps.ts +11 -3
- package/src/runtime/routes/pairing-routes.ts +15 -0
- package/src/runtime/routes/recording-routes.ts +72 -0
- package/src/runtime/routes/schedule-routes.ts +77 -5
- package/src/runtime/routes/secret-routes.ts +63 -1
- package/src/runtime/routes/settings-routes.ts +91 -1
- package/src/runtime/routes/skills-routes.ts +98 -16
- package/src/runtime/routes/subagents-routes.ts +38 -3
- package/src/runtime/routes/surface-action-routes.ts +66 -24
- package/src/runtime/routes/surface-content-routes.ts +20 -0
- package/src/runtime/routes/telemetry-routes.ts +12 -0
- package/src/runtime/routes/trace-event-routes.ts +25 -0
- package/src/runtime/routes/trust-rules-routes.ts +46 -0
- package/src/runtime/routes/tts-routes.ts +15 -4
- package/src/runtime/routes/upgrade-broadcast-routes.ts +38 -0
- package/src/runtime/routes/usage-routes.ts +59 -0
- package/src/runtime/routes/watch-routes.ts +28 -0
- package/src/runtime/routes/work-items-routes.ts +59 -0
- package/src/runtime/routes/workspace-commit-routes.ts +12 -0
- package/src/runtime/routes/workspace-routes.ts +102 -0
- package/src/schedule/scheduler.ts +7 -1
- package/src/security/AGENTS.md +7 -0
- package/src/security/credential-backend.ts +1 -1
- package/src/security/encrypted-store.ts +3 -3
- package/src/security/oauth2.ts +55 -0
- package/src/security/secret-ingress.ts +174 -0
- package/src/security/secret-patterns.ts +133 -0
- package/src/security/secret-scanner.ts +28 -117
- package/src/signals/confirm.ts +12 -8
- package/src/signals/user-message.ts +18 -3
- package/src/skills/skill-memory.ts +1 -2
- package/src/tasks/task-runner.ts +7 -1
- package/src/tools/credentials/broker.ts +1 -1
- package/src/tools/credentials/metadata-store.ts +1 -1
- package/src/tools/credentials/vault.ts +2 -3
- package/src/tools/memory/definitions.ts +1 -1
- package/src/tools/memory/handlers.test.ts +2 -4
- package/src/tools/skills/load.ts +1 -1
- package/src/tools/terminal/safe-env.ts +7 -0
- package/src/tools/tool-manifest.ts +1 -1
- package/src/util/log-redact.ts +9 -34
- package/src/workspace/migrations/015-migrate-credentials-to-keychain.ts +13 -148
- package/src/workspace/migrations/016-migrate-credentials-from-keychain.ts +7 -145
- package/src/workspace/migrations/AGENTS.md +11 -0
- package/src/workspace/migrations/runner.ts +16 -6
- package/src/workspace/migrations/types.ts +7 -0
- package/docs/architecture/keychain-broker.md +0 -69
- package/src/__tests__/keychain-broker-client.test.ts +0 -800
- package/src/cli/commands/oauth/platform.ts +0 -525
- package/src/config/bundled-skills/slack/TOOLS.json +0 -272
- package/src/config/bundled-skills/slack/tools/shared.ts +0 -34
- package/src/config/bundled-skills/slack/tools/slack-add-reaction.ts +0 -27
- package/src/config/bundled-skills/slack/tools/slack-channel-details.ts +0 -38
- package/src/config/bundled-skills/slack/tools/slack-channel-permissions.ts +0 -146
- package/src/config/bundled-skills/slack/tools/slack-configure-channels.ts +0 -105
- package/src/config/bundled-skills/slack/tools/slack-delete-message.ts +0 -26
- package/src/config/bundled-skills/slack/tools/slack-edit-message.ts +0 -27
- package/src/config/bundled-skills/slack/tools/slack-leave-channel.ts +0 -25
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +0 -372
- package/src/security/keychain-broker-client.ts +0 -446
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { and, eq, inArray, isNull } from "drizzle-orm";
|
|
2
|
+
import { v4 as uuid } from "uuid";
|
|
3
|
+
|
|
4
|
+
import { getDb } from "./db.js";
|
|
5
|
+
import { memoryRecallLogs } from "./schema.js";
|
|
6
|
+
|
|
7
|
+
export interface RecordMemoryRecallLogParams {
|
|
8
|
+
conversationId: string;
|
|
9
|
+
enabled: boolean;
|
|
10
|
+
degraded: boolean;
|
|
11
|
+
provider?: string;
|
|
12
|
+
model?: string;
|
|
13
|
+
degradationJson?: unknown;
|
|
14
|
+
semanticHits: number;
|
|
15
|
+
mergedCount: number;
|
|
16
|
+
selectedCount: number;
|
|
17
|
+
tier1Count: number;
|
|
18
|
+
tier2Count: number;
|
|
19
|
+
hybridSearchLatencyMs: number;
|
|
20
|
+
sparseVectorUsed: boolean;
|
|
21
|
+
injectedTokens: number;
|
|
22
|
+
latencyMs: number;
|
|
23
|
+
topCandidatesJson: unknown;
|
|
24
|
+
injectedText?: string;
|
|
25
|
+
reason?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function recordMemoryRecallLog(params: RecordMemoryRecallLogParams): void {
|
|
29
|
+
const db = getDb();
|
|
30
|
+
db.insert(memoryRecallLogs)
|
|
31
|
+
.values({
|
|
32
|
+
id: uuid(),
|
|
33
|
+
conversationId: params.conversationId,
|
|
34
|
+
messageId: null,
|
|
35
|
+
enabled: params.enabled ? 1 : 0,
|
|
36
|
+
degraded: params.degraded ? 1 : 0,
|
|
37
|
+
provider: params.provider ?? null,
|
|
38
|
+
model: params.model ?? null,
|
|
39
|
+
degradationJson: params.degradationJson
|
|
40
|
+
? JSON.stringify(params.degradationJson)
|
|
41
|
+
: null,
|
|
42
|
+
semanticHits: params.semanticHits,
|
|
43
|
+
mergedCount: params.mergedCount,
|
|
44
|
+
selectedCount: params.selectedCount,
|
|
45
|
+
tier1Count: params.tier1Count,
|
|
46
|
+
tier2Count: params.tier2Count,
|
|
47
|
+
hybridSearchLatencyMs: params.hybridSearchLatencyMs,
|
|
48
|
+
sparseVectorUsed: params.sparseVectorUsed ? 1 : 0,
|
|
49
|
+
injectedTokens: params.injectedTokens,
|
|
50
|
+
latencyMs: params.latencyMs,
|
|
51
|
+
topCandidatesJson: JSON.stringify(params.topCandidatesJson),
|
|
52
|
+
injectedText: params.injectedText ?? null,
|
|
53
|
+
reason: params.reason ?? null,
|
|
54
|
+
createdAt: Date.now(),
|
|
55
|
+
})
|
|
56
|
+
.run();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function backfillMemoryRecallLogMessageId(
|
|
60
|
+
conversationId: string,
|
|
61
|
+
messageId: string,
|
|
62
|
+
): void {
|
|
63
|
+
const db = getDb();
|
|
64
|
+
db.update(memoryRecallLogs)
|
|
65
|
+
.set({ messageId })
|
|
66
|
+
.where(
|
|
67
|
+
and(
|
|
68
|
+
eq(memoryRecallLogs.conversationId, conversationId),
|
|
69
|
+
isNull(memoryRecallLogs.messageId),
|
|
70
|
+
),
|
|
71
|
+
)
|
|
72
|
+
.run();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface MemoryRecallLog {
|
|
76
|
+
enabled: boolean;
|
|
77
|
+
degraded: boolean;
|
|
78
|
+
provider: string | null;
|
|
79
|
+
model: string | null;
|
|
80
|
+
degradation: unknown | null;
|
|
81
|
+
semanticHits: number;
|
|
82
|
+
mergedCount: number;
|
|
83
|
+
selectedCount: number;
|
|
84
|
+
tier1Count: number;
|
|
85
|
+
tier2Count: number;
|
|
86
|
+
hybridSearchLatencyMs: number;
|
|
87
|
+
sparseVectorUsed: boolean;
|
|
88
|
+
injectedTokens: number;
|
|
89
|
+
latencyMs: number;
|
|
90
|
+
topCandidates: unknown;
|
|
91
|
+
injectedText: string | null;
|
|
92
|
+
reason: string | null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function getMemoryRecallLogByMessageIds(
|
|
96
|
+
messageIds: string[],
|
|
97
|
+
): MemoryRecallLog | null {
|
|
98
|
+
if (messageIds.length === 0) return null;
|
|
99
|
+
const db = getDb();
|
|
100
|
+
const rows = db
|
|
101
|
+
.select()
|
|
102
|
+
.from(memoryRecallLogs)
|
|
103
|
+
.where(inArray(memoryRecallLogs.messageId, messageIds))
|
|
104
|
+
.all();
|
|
105
|
+
if (rows.length === 0) return null;
|
|
106
|
+
const row = rows[0]!;
|
|
107
|
+
return {
|
|
108
|
+
enabled: !!row.enabled,
|
|
109
|
+
degraded: !!row.degraded,
|
|
110
|
+
provider: row.provider,
|
|
111
|
+
model: row.model,
|
|
112
|
+
degradation: row.degradationJson
|
|
113
|
+
? JSON.parse(row.degradationJson)
|
|
114
|
+
: null,
|
|
115
|
+
semanticHits: row.semanticHits,
|
|
116
|
+
mergedCount: row.mergedCount,
|
|
117
|
+
selectedCount: row.selectedCount,
|
|
118
|
+
tier1Count: row.tier1Count,
|
|
119
|
+
tier2Count: row.tier2Count,
|
|
120
|
+
hybridSearchLatencyMs: row.hybridSearchLatencyMs,
|
|
121
|
+
sparseVectorUsed: !!row.sparseVectorUsed,
|
|
122
|
+
injectedTokens: row.injectedTokens,
|
|
123
|
+
latencyMs: row.latencyMs,
|
|
124
|
+
topCandidates: JSON.parse(row.topCandidatesJson),
|
|
125
|
+
injectedText: row.injectedText,
|
|
126
|
+
reason: row.reason,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { DrizzleDb } from "../db-connection.js";
|
|
2
|
+
import { getSqliteFrom } from "../db-connection.js";
|
|
3
|
+
import { withCrashRecovery } from "./validate-migration-state.js";
|
|
4
|
+
|
|
5
|
+
const CHECKPOINT_KEY = "migration_create_memory_recall_logs_v1";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Create the memory_recall_logs table for the inspector memory tab.
|
|
9
|
+
*/
|
|
10
|
+
export function migrateCreateMemoryRecallLogs(database: DrizzleDb): void {
|
|
11
|
+
withCrashRecovery(database, CHECKPOINT_KEY, () => {
|
|
12
|
+
const raw = getSqliteFrom(database);
|
|
13
|
+
|
|
14
|
+
raw.exec(/*sql*/ `
|
|
15
|
+
CREATE TABLE IF NOT EXISTS memory_recall_logs (
|
|
16
|
+
id TEXT PRIMARY KEY,
|
|
17
|
+
conversation_id TEXT NOT NULL,
|
|
18
|
+
message_id TEXT,
|
|
19
|
+
enabled INTEGER NOT NULL,
|
|
20
|
+
degraded INTEGER NOT NULL,
|
|
21
|
+
provider TEXT,
|
|
22
|
+
model TEXT,
|
|
23
|
+
degradation_json TEXT,
|
|
24
|
+
semantic_hits INTEGER NOT NULL,
|
|
25
|
+
merged_count INTEGER NOT NULL,
|
|
26
|
+
selected_count INTEGER NOT NULL,
|
|
27
|
+
tier1_count INTEGER NOT NULL,
|
|
28
|
+
tier2_count INTEGER NOT NULL,
|
|
29
|
+
hybrid_search_latency_ms INTEGER NOT NULL,
|
|
30
|
+
sparse_vector_used INTEGER NOT NULL,
|
|
31
|
+
injected_tokens INTEGER NOT NULL,
|
|
32
|
+
latency_ms INTEGER NOT NULL,
|
|
33
|
+
top_candidates_json TEXT NOT NULL,
|
|
34
|
+
injected_text TEXT,
|
|
35
|
+
reason TEXT,
|
|
36
|
+
created_at INTEGER NOT NULL
|
|
37
|
+
)
|
|
38
|
+
`);
|
|
39
|
+
|
|
40
|
+
raw.exec(/*sql*/ `
|
|
41
|
+
CREATE INDEX IF NOT EXISTS idx_memory_recall_logs_message_id
|
|
42
|
+
ON memory_recall_logs (message_id)
|
|
43
|
+
`);
|
|
44
|
+
|
|
45
|
+
raw.exec(/*sql*/ `
|
|
46
|
+
CREATE INDEX IF NOT EXISTS idx_memory_recall_logs_conversation_id
|
|
47
|
+
ON memory_recall_logs (conversation_id)
|
|
48
|
+
`);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { DrizzleDb } from "../db-connection.js";
|
|
2
|
+
import { getSqliteFrom } from "../db-connection.js";
|
|
3
|
+
|
|
4
|
+
export function migrateOAuthProvidersPingConfig(database: DrizzleDb): void {
|
|
5
|
+
const raw = getSqliteFrom(database);
|
|
6
|
+
try {
|
|
7
|
+
raw.exec(/*sql*/ `ALTER TABLE oauth_providers ADD COLUMN ping_method TEXT`);
|
|
8
|
+
} catch {
|
|
9
|
+
// Column already exists — nothing to do.
|
|
10
|
+
}
|
|
11
|
+
try {
|
|
12
|
+
raw.exec(
|
|
13
|
+
/*sql*/ `ALTER TABLE oauth_providers ADD COLUMN ping_headers TEXT`,
|
|
14
|
+
);
|
|
15
|
+
} catch {
|
|
16
|
+
// Column already exists — nothing to do.
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
raw.exec(/*sql*/ `ALTER TABLE oauth_providers ADD COLUMN ping_body TEXT`);
|
|
20
|
+
} catch {
|
|
21
|
+
// Column already exists — nothing to do.
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -132,6 +132,8 @@ export { migrateCallSessionSkipDisclosure } from "./190-call-session-skip-disclo
|
|
|
132
132
|
export { migrateBackfillAudioAttachmentMimeTypes } from "./191-backfill-audio-attachment-mime-types.js";
|
|
133
133
|
export { migrateContactsUserFileColumn } from "./192-contacts-user-file-column.js";
|
|
134
134
|
export { migrateAddSourceTypeColumns } from "./193-add-source-type-columns.js";
|
|
135
|
+
export { migrateCreateMemoryRecallLogs } from "./194-memory-recall-logs.js";
|
|
136
|
+
export { migrateOAuthProvidersPingConfig } from "./195-oauth-providers-ping-config.js";
|
|
135
137
|
export {
|
|
136
138
|
MIGRATION_REGISTRY,
|
|
137
139
|
type MigrationRegistryEntry,
|
|
@@ -98,7 +98,20 @@ export function withCrashRecovery(
|
|
|
98
98
|
)
|
|
99
99
|
.run(checkpointKey, Date.now());
|
|
100
100
|
|
|
101
|
-
|
|
101
|
+
try {
|
|
102
|
+
migrationFn();
|
|
103
|
+
} catch (error) {
|
|
104
|
+
log.error(
|
|
105
|
+
{ checkpointKey, error },
|
|
106
|
+
`Memory migration failed: ${checkpointKey} — marking as failed and continuing`,
|
|
107
|
+
);
|
|
108
|
+
raw
|
|
109
|
+
.query(
|
|
110
|
+
`UPDATE memory_checkpoints SET value = 'failed', updated_at = ? WHERE key = ?`,
|
|
111
|
+
)
|
|
112
|
+
.run(Date.now(), checkpointKey);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
102
115
|
|
|
103
116
|
raw
|
|
104
117
|
.query(
|
|
@@ -331,8 +331,7 @@ describe("Memory Retriever Pipeline", () => {
|
|
|
331
331
|
expect(result.enabled).toBe(true);
|
|
332
332
|
expect(result.degraded).toBe(false);
|
|
333
333
|
expect(result.degradation).toBeUndefined();
|
|
334
|
-
// With
|
|
335
|
-
// scoring below tier thresholds, no candidates are selected.
|
|
334
|
+
// With Qdrant mocked empty, no candidates are found.
|
|
336
335
|
// The pipeline still completes successfully with tier metadata.
|
|
337
336
|
expect(result.tier1Count).toBeDefined();
|
|
338
337
|
expect(result.tier2Count).toBeDefined();
|
|
@@ -345,7 +344,7 @@ describe("Memory Retriever Pipeline", () => {
|
|
|
345
344
|
// Current-conversation segment filtering
|
|
346
345
|
// -----------------------------------------------------------------------
|
|
347
346
|
|
|
348
|
-
test("current-conversation segments are filtered from
|
|
347
|
+
test("current-conversation segments are filtered from search results", async () => {
|
|
349
348
|
const db = getDb();
|
|
350
349
|
const now = Date.now();
|
|
351
350
|
const activeConv = "conv-active";
|
|
@@ -613,7 +612,7 @@ describe("Memory Retriever Pipeline", () => {
|
|
|
613
612
|
|
|
614
613
|
insertConversation(db, convId, now - MS_PER_DAY * 200);
|
|
615
614
|
|
|
616
|
-
// Create a message from 200 days ago
|
|
615
|
+
// Create a message from 200 days ago (staleness test anchor)
|
|
617
616
|
insertMessage(
|
|
618
617
|
db,
|
|
619
618
|
"msg-old",
|
|
@@ -679,7 +678,7 @@ describe("Memory Retriever Pipeline", () => {
|
|
|
679
678
|
// Degradation: Qdrant circuit breaker open
|
|
680
679
|
// -----------------------------------------------------------------------
|
|
681
680
|
|
|
682
|
-
test("Qdrant unavailable: pipeline completes with
|
|
681
|
+
test("Qdrant unavailable: pipeline completes with empty results", async () => {
|
|
683
682
|
seedMemory();
|
|
684
683
|
|
|
685
684
|
// Force the Qdrant circuit breaker open
|
|
@@ -121,6 +121,37 @@ export const llmRequestLogs = sqliteTable(
|
|
|
121
121
|
(table) => [index("idx_llm_request_logs_message_id").on(table.messageId)],
|
|
122
122
|
);
|
|
123
123
|
|
|
124
|
+
export const memoryRecallLogs = sqliteTable(
|
|
125
|
+
"memory_recall_logs",
|
|
126
|
+
{
|
|
127
|
+
id: text("id").primaryKey(),
|
|
128
|
+
conversationId: text("conversation_id").notNull(),
|
|
129
|
+
messageId: text("message_id"),
|
|
130
|
+
enabled: integer("enabled").notNull(),
|
|
131
|
+
degraded: integer("degraded").notNull(),
|
|
132
|
+
provider: text("provider"),
|
|
133
|
+
model: text("model"),
|
|
134
|
+
degradationJson: text("degradation_json"),
|
|
135
|
+
semanticHits: integer("semantic_hits").notNull(),
|
|
136
|
+
mergedCount: integer("merged_count").notNull(),
|
|
137
|
+
selectedCount: integer("selected_count").notNull(),
|
|
138
|
+
tier1Count: integer("tier1_count").notNull(),
|
|
139
|
+
tier2Count: integer("tier2_count").notNull(),
|
|
140
|
+
hybridSearchLatencyMs: integer("hybrid_search_latency_ms").notNull(),
|
|
141
|
+
sparseVectorUsed: integer("sparse_vector_used").notNull(),
|
|
142
|
+
injectedTokens: integer("injected_tokens").notNull(),
|
|
143
|
+
latencyMs: integer("latency_ms").notNull(),
|
|
144
|
+
topCandidatesJson: text("top_candidates_json").notNull(),
|
|
145
|
+
injectedText: text("injected_text"),
|
|
146
|
+
reason: text("reason"),
|
|
147
|
+
createdAt: integer("created_at").notNull(),
|
|
148
|
+
},
|
|
149
|
+
(table) => [
|
|
150
|
+
index("idx_memory_recall_logs_message_id").on(table.messageId),
|
|
151
|
+
index("idx_memory_recall_logs_conversation_id").on(table.conversationId),
|
|
152
|
+
],
|
|
153
|
+
);
|
|
154
|
+
|
|
124
155
|
export const llmUsageEvents = sqliteTable(
|
|
125
156
|
"llm_usage_events",
|
|
126
157
|
{
|
|
@@ -18,6 +18,9 @@ export const oauthProviders = sqliteTable("oauth_providers", {
|
|
|
18
18
|
extraParams: text("extra_params"),
|
|
19
19
|
callbackTransport: text("callback_transport"),
|
|
20
20
|
pingUrl: text("ping_url"),
|
|
21
|
+
pingMethod: text("ping_method"),
|
|
22
|
+
pingHeaders: text("ping_headers"),
|
|
23
|
+
pingBody: text("ping_body"),
|
|
21
24
|
managedServiceConfigKey: text("managed_service_config_key"),
|
|
22
25
|
displayName: text("display_name"),
|
|
23
26
|
description: text("description"),
|
|
@@ -56,7 +56,7 @@ export const telegramBotMessagingProvider: MessagingProvider = {
|
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
58
|
* Custom connectivity check using both the oauth_connection record AND
|
|
59
|
-
* actual
|
|
59
|
+
* actual stored credentials. The connection row alone can become stale
|
|
60
60
|
* if clearTelegramConfig() returns early on a secure-key deletion error
|
|
61
61
|
* without removing the row. Checking both ensures we don't report
|
|
62
62
|
* Telegram as connected when secrets are missing.
|
|
@@ -120,6 +120,16 @@ export async function orchestrateOAuthConnect(
|
|
|
120
120
|
options: OAuthConnectOptions,
|
|
121
121
|
): Promise<OAuthConnectResult> {
|
|
122
122
|
const resolvedService = resolveService(options.service);
|
|
123
|
+
log.info(
|
|
124
|
+
{
|
|
125
|
+
rawService: options.service,
|
|
126
|
+
resolvedService,
|
|
127
|
+
isInteractive: options.isInteractive,
|
|
128
|
+
hasOpenUrl: !!options.openUrl,
|
|
129
|
+
hasSendToClient: !!options.sendToClient,
|
|
130
|
+
},
|
|
131
|
+
"orchestrateOAuthConnect: starting",
|
|
132
|
+
);
|
|
123
133
|
|
|
124
134
|
// Read provider config from the DB
|
|
125
135
|
const providerRow = getProvider(resolvedService);
|
|
@@ -199,6 +209,20 @@ export async function orchestrateOAuthConnect(
|
|
|
199
209
|
};
|
|
200
210
|
}
|
|
201
211
|
|
|
212
|
+
log.info(
|
|
213
|
+
{
|
|
214
|
+
service: resolvedService,
|
|
215
|
+
authUrl,
|
|
216
|
+
tokenUrl,
|
|
217
|
+
scopeCount: finalScopes.length,
|
|
218
|
+
callbackTransport,
|
|
219
|
+
loopbackPort,
|
|
220
|
+
hasClientSecret: !!options.clientSecret,
|
|
221
|
+
clientIdPrefix: options.clientId.substring(0, 12) + "…",
|
|
222
|
+
},
|
|
223
|
+
"orchestrateOAuthConnect: resolved provider config",
|
|
224
|
+
);
|
|
225
|
+
|
|
202
226
|
const oauthConfig = {
|
|
203
227
|
authUrl,
|
|
204
228
|
tokenUrl,
|
|
@@ -331,19 +355,31 @@ export async function orchestrateOAuthConnect(
|
|
|
331
355
|
// -----------------------------------------------------------------------
|
|
332
356
|
// Interactive path — open browser, block until completion
|
|
333
357
|
// -----------------------------------------------------------------------
|
|
358
|
+
log.info(
|
|
359
|
+
{ service: resolvedService, callbackTransport, loopbackPort },
|
|
360
|
+
"orchestrateOAuthConnect: entering interactive path",
|
|
361
|
+
);
|
|
334
362
|
try {
|
|
335
363
|
const { tokens, grantedScopes, rawTokenResponse } = await startOAuth2Flow(
|
|
336
364
|
oauthConfig,
|
|
337
365
|
{
|
|
338
366
|
openUrl: (url) => {
|
|
367
|
+
log.info(
|
|
368
|
+
{ service: resolvedService, urlLength: url.length },
|
|
369
|
+
"orchestrateOAuthConnect: openUrl callback fired, delivering auth URL to client",
|
|
370
|
+
);
|
|
339
371
|
if (options.openUrl) {
|
|
372
|
+
log.info("orchestrateOAuthConnect: using options.openUrl");
|
|
340
373
|
options.openUrl(url);
|
|
341
374
|
} else if (options.sendToClient) {
|
|
375
|
+
log.info("orchestrateOAuthConnect: using sendToClient with open_url event");
|
|
342
376
|
options.sendToClient({
|
|
343
377
|
type: "open_url",
|
|
344
378
|
url,
|
|
345
379
|
title: `Connect ${resolvedService}`,
|
|
346
380
|
});
|
|
381
|
+
} else {
|
|
382
|
+
log.warn("orchestrateOAuthConnect: no openUrl or sendToClient available — auth URL will not reach the user");
|
|
347
383
|
}
|
|
348
384
|
},
|
|
349
385
|
},
|
|
@@ -354,6 +390,11 @@ export async function orchestrateOAuthConnect(
|
|
|
354
390
|
: undefined,
|
|
355
391
|
);
|
|
356
392
|
|
|
393
|
+
log.info(
|
|
394
|
+
{ service: resolvedService, grantedScopeCount: grantedScopes.length },
|
|
395
|
+
"orchestrateOAuthConnect: interactive flow completed, exchanged code for tokens",
|
|
396
|
+
);
|
|
397
|
+
|
|
357
398
|
// Parse account identifier from the provider's identity endpoint.
|
|
358
399
|
// Best-effort — format varies by provider and may fail.
|
|
359
400
|
let parsedAccountIdentifier: string | undefined;
|
|
@@ -362,6 +403,10 @@ export async function orchestrateOAuthConnect(
|
|
|
362
403
|
parsedAccountIdentifier = await behavior.identityVerifier(
|
|
363
404
|
tokens.accessToken,
|
|
364
405
|
);
|
|
406
|
+
log.info(
|
|
407
|
+
{ service: resolvedService, parsedAccountIdentifier },
|
|
408
|
+
"orchestrateOAuthConnect: identity verified",
|
|
409
|
+
);
|
|
365
410
|
} catch {
|
|
366
411
|
// Non-fatal
|
|
367
412
|
}
|
|
@@ -375,6 +420,11 @@ export async function orchestrateOAuthConnect(
|
|
|
375
420
|
parsedAccountIdentifier,
|
|
376
421
|
});
|
|
377
422
|
|
|
423
|
+
log.info(
|
|
424
|
+
{ service: resolvedService, accountInfo },
|
|
425
|
+
"orchestrateOAuthConnect: tokens stored, connect complete",
|
|
426
|
+
);
|
|
427
|
+
|
|
378
428
|
return {
|
|
379
429
|
success: true,
|
|
380
430
|
deferred: false,
|
|
@@ -384,6 +434,10 @@ export async function orchestrateOAuthConnect(
|
|
|
384
434
|
} catch (err: unknown) {
|
|
385
435
|
const message =
|
|
386
436
|
err instanceof Error ? err.message : "Unknown error during OAuth flow";
|
|
437
|
+
log.error(
|
|
438
|
+
{ service: resolvedService, err },
|
|
439
|
+
"orchestrateOAuthConnect: interactive flow failed",
|
|
440
|
+
);
|
|
387
441
|
return {
|
|
388
442
|
success: false,
|
|
389
443
|
error: `Error connecting "${resolvedService}": ${message}`,
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Helpers for managing oauth_connection records for non-OAuth (manual-token)
|
|
3
3
|
* providers like slack_channel and telegram.
|
|
4
4
|
*
|
|
5
|
-
* These providers store credentials via the
|
|
6
|
-
* also maintain an oauth_connection row so that getConnectionByProvider()
|
|
7
|
-
* be used as the single source of truth for connection status across the
|
|
5
|
+
* These providers store credentials via the credential store (setSecureKeyAsync)
|
|
6
|
+
* but also maintain an oauth_connection row so that getConnectionByProvider()
|
|
7
|
+
* can be used as the single source of truth for connection status across the
|
|
8
8
|
* codebase.
|
|
9
9
|
*/
|
|
10
10
|
|
|
@@ -57,7 +57,7 @@ export async function ensureManualTokenConnection(
|
|
|
57
57
|
* Remove the oauth_connection row for a manual-token provider.
|
|
58
58
|
*
|
|
59
59
|
* Note: This only removes the oauth_connection row. The caller is still
|
|
60
|
-
* responsible for deleting the
|
|
60
|
+
* responsible for deleting the stored credentials separately.
|
|
61
61
|
*/
|
|
62
62
|
export function removeManualTokenConnection(providerKey: string): void {
|
|
63
63
|
const conn = getConnectionByProvider(providerKey);
|
|
@@ -114,7 +114,7 @@ export async function syncManualTokenConnection(
|
|
|
114
114
|
|
|
115
115
|
/**
|
|
116
116
|
* Backfill oauth_connection rows for manual-token providers that already
|
|
117
|
-
* have valid
|
|
117
|
+
* have valid stored credentials but are missing connection records.
|
|
118
118
|
*
|
|
119
119
|
* This handles the upgrade path from installations that stored credentials
|
|
120
120
|
* before the oauth_connection migration. Without this, existing Telegram
|
package/src/oauth/oauth-store.ts
CHANGED
|
@@ -45,11 +45,12 @@ export type OAuthConnectionRow = typeof oauthConnections.$inferSelect;
|
|
|
45
45
|
* Seed well-known provider profiles into the database. Uses INSERT … ON
|
|
46
46
|
* CONFLICT DO UPDATE so that implementation fields (authUrl, tokenUrl,
|
|
47
47
|
* tokenEndpointAuthMethod, userinfoUrl, extraParams, callbackTransport,
|
|
48
|
-
* pingUrl,
|
|
49
|
-
* description, dashboardUrl,
|
|
50
|
-
* propagate to existing
|
|
51
|
-
*
|
|
52
|
-
* only written on the
|
|
48
|
+
* pingUrl, pingMethod, pingHeaders, pingBody, managedServiceConfigKey)
|
|
49
|
+
* and display metadata (displayName, description, dashboardUrl,
|
|
50
|
+
* clientIdPlaceholder, requiresClientSecret) propagate to existing
|
|
51
|
+
* installations on every startup, while user-customizable fields
|
|
52
|
+
* (defaultScopes, scopePolicy, baseUrl) are only written on the
|
|
53
|
+
* initial insert.
|
|
53
54
|
*/
|
|
54
55
|
export function seedProviders(
|
|
55
56
|
profiles: Array<{
|
|
@@ -59,6 +60,9 @@ export function seedProviders(
|
|
|
59
60
|
tokenEndpointAuthMethod?: string;
|
|
60
61
|
userinfoUrl?: string;
|
|
61
62
|
pingUrl?: string;
|
|
63
|
+
pingMethod?: string;
|
|
64
|
+
pingHeaders?: Record<string, string>;
|
|
65
|
+
pingBody?: unknown;
|
|
62
66
|
baseUrl?: string;
|
|
63
67
|
defaultScopes: string[];
|
|
64
68
|
scopePolicy: Record<string, unknown>;
|
|
@@ -80,6 +84,10 @@ export function seedProviders(
|
|
|
80
84
|
const tokenEndpointAuthMethod = p.tokenEndpointAuthMethod ?? null;
|
|
81
85
|
const userinfoUrl = p.userinfoUrl ?? null;
|
|
82
86
|
const pingUrl = p.pingUrl ?? null;
|
|
87
|
+
const pingMethod = p.pingMethod ?? null;
|
|
88
|
+
const pingHeaders = p.pingHeaders ? JSON.stringify(p.pingHeaders) : null;
|
|
89
|
+
const pingBody =
|
|
90
|
+
p.pingBody !== undefined ? JSON.stringify(p.pingBody) : null;
|
|
83
91
|
const baseUrl = p.baseUrl ?? null;
|
|
84
92
|
const defaultScopes = JSON.stringify(p.defaultScopes);
|
|
85
93
|
const scopePolicy = JSON.stringify(p.scopePolicy);
|
|
@@ -105,6 +113,9 @@ export function seedProviders(
|
|
|
105
113
|
extraParams,
|
|
106
114
|
callbackTransport,
|
|
107
115
|
pingUrl,
|
|
116
|
+
pingMethod,
|
|
117
|
+
pingHeaders,
|
|
118
|
+
pingBody,
|
|
108
119
|
managedServiceConfigKey,
|
|
109
120
|
displayName,
|
|
110
121
|
description,
|
|
@@ -124,6 +135,9 @@ export function seedProviders(
|
|
|
124
135
|
extraParams,
|
|
125
136
|
callbackTransport,
|
|
126
137
|
pingUrl,
|
|
138
|
+
pingMethod,
|
|
139
|
+
pingHeaders,
|
|
140
|
+
pingBody,
|
|
127
141
|
managedServiceConfigKey,
|
|
128
142
|
displayName,
|
|
129
143
|
description,
|
|
@@ -164,6 +178,9 @@ export function registerProvider(params: {
|
|
|
164
178
|
tokenEndpointAuthMethod?: string;
|
|
165
179
|
userinfoUrl?: string;
|
|
166
180
|
pingUrl?: string;
|
|
181
|
+
pingMethod?: string;
|
|
182
|
+
pingHeaders?: Record<string, string>;
|
|
183
|
+
pingBody?: unknown;
|
|
167
184
|
baseUrl?: string;
|
|
168
185
|
defaultScopes: string[];
|
|
169
186
|
scopePolicy: Record<string, unknown>;
|
|
@@ -196,6 +213,10 @@ export function registerProvider(params: {
|
|
|
196
213
|
extraParams: params.extraParams ? JSON.stringify(params.extraParams) : null,
|
|
197
214
|
callbackTransport: params.callbackTransport ?? null,
|
|
198
215
|
pingUrl: params.pingUrl ?? null,
|
|
216
|
+
pingMethod: params.pingMethod ?? null,
|
|
217
|
+
pingHeaders: params.pingHeaders ? JSON.stringify(params.pingHeaders) : null,
|
|
218
|
+
pingBody:
|
|
219
|
+
params.pingBody !== undefined ? JSON.stringify(params.pingBody) : null,
|
|
199
220
|
managedServiceConfigKey: params.managedServiceConfigKey ?? null,
|
|
200
221
|
displayName: params.displayName ?? null,
|
|
201
222
|
description: params.description ?? null,
|
|
@@ -6,7 +6,8 @@ import { seedProviders } from "./oauth-store.js";
|
|
|
6
6
|
* These values are upserted into the `oauth_providers` SQLite table on
|
|
7
7
|
* every startup. Only Vellum implementation fields (authUrl, tokenUrl,
|
|
8
8
|
* tokenEndpointAuthMethod, userinfoUrl, extraParams, callbackTransport,
|
|
9
|
-
* pingUrl,
|
|
9
|
+
* pingUrl, pingMethod, pingHeaders, pingBody, managedServiceConfigKey)
|
|
10
|
+
* and display metadata (displayName,
|
|
10
11
|
* description, dashboardUrl, clientIdPlaceholder, requiresClientSecret)
|
|
11
12
|
* are overwritten on subsequent startups — user-customizable
|
|
12
13
|
* fields (defaultScopes, scopePolicy, baseUrl) are only
|
|
@@ -25,6 +26,9 @@ const PROVIDER_SEED_DATA: Record<
|
|
|
25
26
|
tokenEndpointAuthMethod?: string;
|
|
26
27
|
userinfoUrl?: string;
|
|
27
28
|
pingUrl?: string;
|
|
29
|
+
pingMethod?: string;
|
|
30
|
+
pingHeaders?: Record<string, string>;
|
|
31
|
+
pingBody?: unknown;
|
|
28
32
|
baseUrl?: string;
|
|
29
33
|
defaultScopes: string[];
|
|
30
34
|
scopePolicy: {
|
|
@@ -117,6 +121,7 @@ const PROVIDER_SEED_DATA: Record<
|
|
|
117
121
|
authUrl: "https://api.notion.com/v1/oauth/authorize",
|
|
118
122
|
tokenUrl: "https://api.notion.com/v1/oauth/token",
|
|
119
123
|
pingUrl: "https://api.notion.com/v1/users/me",
|
|
124
|
+
pingHeaders: { "Notion-Version": "2022-06-28" },
|
|
120
125
|
baseUrl: "https://api.notion.com",
|
|
121
126
|
displayName: "Notion",
|
|
122
127
|
description: "Pages and databases",
|
|
@@ -187,6 +192,9 @@ const PROVIDER_SEED_DATA: Record<
|
|
|
187
192
|
authUrl: "https://linear.app/oauth/authorize",
|
|
188
193
|
tokenUrl: "https://api.linear.app/oauth/token",
|
|
189
194
|
pingUrl: "https://api.linear.app/graphql",
|
|
195
|
+
pingMethod: "POST",
|
|
196
|
+
pingHeaders: { "Content-Type": "application/json" },
|
|
197
|
+
pingBody: { query: "{ viewer { id name email } }" },
|
|
190
198
|
baseUrl: "https://api.linear.app",
|
|
191
199
|
displayName: "Linear",
|
|
192
200
|
description: "Issues and projects",
|
|
@@ -280,6 +288,7 @@ const PROVIDER_SEED_DATA: Record<
|
|
|
280
288
|
authUrl: "https://www.dropbox.com/oauth2/authorize",
|
|
281
289
|
tokenUrl: "https://api.dropboxapi.com/oauth2/token",
|
|
282
290
|
pingUrl: "https://api.dropboxapi.com/2/users/get_current_account",
|
|
291
|
+
pingMethod: "POST",
|
|
283
292
|
baseUrl: "https://api.dropboxapi.com/2",
|
|
284
293
|
displayName: "Dropbox",
|
|
285
294
|
description: "Files and folders",
|
|
@@ -444,7 +444,7 @@ async function buildCommandCandidates(
|
|
|
444
444
|
// through to the permissive skill_load:* allow rule.
|
|
445
445
|
const config = getConfig();
|
|
446
446
|
const inlineEnabled = isAssistantFeatureFlagEnabled(
|
|
447
|
-
"
|
|
447
|
+
"inline-skill-commands",
|
|
448
448
|
config,
|
|
449
449
|
);
|
|
450
450
|
|
|
@@ -1107,7 +1107,7 @@ function skillLoadAllowlistStrategy(
|
|
|
1107
1107
|
// Check whether this is a dynamic (inline-command) skill load
|
|
1108
1108
|
const config = getConfig();
|
|
1109
1109
|
const inlineEnabled = isAssistantFeatureFlagEnabled(
|
|
1110
|
-
"
|
|
1110
|
+
"inline-skill-commands",
|
|
1111
1111
|
config,
|
|
1112
1112
|
);
|
|
1113
1113
|
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
import type { TrustRule } from "@vellumai/ces-contracts";
|
|
14
14
|
|
|
15
15
|
import { getGatewayInternalBaseUrl } from "../config/env.js";
|
|
16
|
-
import {
|
|
16
|
+
import { mintEdgeRelayToken } from "../runtime/auth/token-service.js";
|
|
17
17
|
import { getLogger } from "../util/logger.js";
|
|
18
18
|
|
|
19
19
|
const log = getLogger("trust-client");
|
|
@@ -35,7 +35,7 @@ export interface AcceptStarterBundleResult {
|
|
|
35
35
|
|
|
36
36
|
function authHeaders(): Record<string, string> {
|
|
37
37
|
return {
|
|
38
|
-
Authorization: `Bearer ${
|
|
38
|
+
Authorization: `Bearer ${mintEdgeRelayToken()}`,
|
|
39
39
|
"Content-Type": "application/json",
|
|
40
40
|
};
|
|
41
41
|
}
|
package/src/platform/client.ts
CHANGED
|
@@ -30,7 +30,7 @@ export class VellumPlatformClient {
|
|
|
30
30
|
*
|
|
31
31
|
* First tries the in-memory managed proxy context (available when the daemon
|
|
32
32
|
* has rehydrated env overrides). Falls back to reading platform credentials
|
|
33
|
-
* directly from the
|
|
33
|
+
* directly from the credential store so that standalone CLI invocations work
|
|
34
34
|
* without the daemon having run its rehydration step.
|
|
35
35
|
*
|
|
36
36
|
* Returns `null` when auth prerequisites are missing (not logged in, no API
|
|
@@ -44,7 +44,7 @@ export class VellumPlatformClient {
|
|
|
44
44
|
let apiKey = ctx.enabled ? ctx.assistantApiKey : "";
|
|
45
45
|
let assistantId = getPlatformAssistantId();
|
|
46
46
|
|
|
47
|
-
// Fall back to
|
|
47
|
+
// Fall back to credential store for values not yet rehydrated (standalone CLI).
|
|
48
48
|
if (!baseUrl) {
|
|
49
49
|
baseUrl =
|
|
50
50
|
(await getSecureKeyAsync(
|
|
@@ -88,7 +88,9 @@ export function buildJournalContext(
|
|
|
88
88
|
const filepath = join(journalDir, f);
|
|
89
89
|
const stat = statSync(filepath);
|
|
90
90
|
if (!stat.isFile()) return [];
|
|
91
|
-
|
|
91
|
+
// Fall back to mtimeMs when birthtimeMs is unavailable (returns 0 on Linux ext4, NFS, Docker overlayfs)
|
|
92
|
+
const birthtimeMs = stat.birthtimeMs > 0 ? stat.birthtimeMs : stat.mtimeMs;
|
|
93
|
+
return [{ filename: f, filepath, birthtimeMs }];
|
|
92
94
|
} catch {
|
|
93
95
|
return [];
|
|
94
96
|
}
|
|
@@ -129,5 +131,8 @@ export function buildJournalContext(
|
|
|
129
131
|
sections.push(header + "\n" + content);
|
|
130
132
|
}
|
|
131
133
|
|
|
134
|
+
// If all readFileSync calls failed, sections only contains the header — return null
|
|
135
|
+
if (sections.length === 1) return null;
|
|
136
|
+
|
|
132
137
|
return sections.join("\n\n");
|
|
133
138
|
}
|