@vellumai/assistant 0.4.48 → 0.4.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +2 -2
- package/README.md +2 -23
- package/docs/architecture/integrations.md +45 -41
- package/docs/architecture/keychain-broker.md +3 -3
- package/docs/runbook-trusted-contacts.md +3 -8
- package/hook-templates/debug-prompt-logger/hook.json +1 -1
- package/hook-templates/debug-prompt-logger/run.sh +1 -3
- package/package.json +1 -1
- package/src/__tests__/actor-token-service.test.ts +0 -1
- package/src/__tests__/anthropic-provider.test.ts +156 -0
- package/src/__tests__/approval-cascade.test.ts +810 -0
- package/src/__tests__/approval-primitive.test.ts +0 -1
- package/src/__tests__/approval-routes-http.test.ts +2 -0
- package/src/__tests__/assistant-attachments.test.ts +12 -34
- package/src/__tests__/assistant-feature-flag-guardrails.test.ts +76 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +0 -1
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +2 -2
- package/src/__tests__/channel-guardian.test.ts +0 -2
- package/src/__tests__/channel-readiness-routes.test.ts +15 -6
- package/src/__tests__/channel-readiness-service.test.ts +10 -9
- package/src/__tests__/checker.test.ts +9 -29
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +1 -1
- package/src/__tests__/computer-use-tools.test.ts +2 -19
- package/src/__tests__/config-watcher.test.ts +0 -1
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
- package/src/__tests__/context-image-dimensions.test.ts +332 -0
- package/src/__tests__/context-token-estimator.test.ts +196 -13
- package/src/__tests__/conversation-attention-store.test.ts +0 -1
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +144 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
- package/src/__tests__/credential-metadata-store.test.ts +64 -73
- package/src/__tests__/credential-security-invariants.test.ts +13 -7
- package/src/__tests__/credential-vault-unit.test.ts +280 -49
- package/src/__tests__/credential-vault.test.ts +138 -16
- package/src/__tests__/credentials-cli.test.ts +71 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
- package/src/__tests__/ephemeral-permissions.test.ts +3 -3
- package/src/__tests__/gateway-only-guard.test.ts +0 -1
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -1
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +0 -1
- package/src/__tests__/guardian-routing-invariants.test.ts +0 -1
- package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -1
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +0 -39
- package/src/__tests__/heartbeat-service.test.ts +0 -1
- package/src/__tests__/host-cu-proxy.test.ts +629 -0
- package/src/__tests__/host-shell-tool.test.ts +27 -15
- package/src/__tests__/http-user-message-parity.test.ts +1 -0
- package/src/__tests__/ingress-url-consistency.test.ts +14 -21
- package/src/__tests__/integration-status.test.ts +32 -51
- package/src/__tests__/intent-routing.test.ts +0 -1
- package/src/__tests__/invite-routes-http.test.ts +10 -9
- package/src/__tests__/keychain-broker-client.test.ts +11 -43
- package/src/__tests__/notification-routing-intent.test.ts +0 -1
- package/src/__tests__/oauth-cli.test.ts +373 -14
- package/src/__tests__/oauth-provider-profiles.test.ts +9 -9
- package/src/__tests__/oauth-scope-policy.test.ts +4 -6
- package/src/__tests__/oauth-store.test.ts +756 -0
- package/src/__tests__/onboarding-starter-tasks.test.ts +0 -1
- package/src/__tests__/provider-error-scenarios.test.ts +0 -1
- package/src/__tests__/provider-streaming.benchmark.test.ts +0 -1
- package/src/__tests__/public-ingress-urls.test.ts +15 -21
- package/src/__tests__/recording-handler.test.ts +3 -4
- package/src/__tests__/registry.test.ts +2 -2
- package/src/__tests__/runtime-events-sse.test.ts +55 -7
- package/src/__tests__/schedule-store.test.ts +0 -1
- package/src/__tests__/scheduler-recurrence.test.ts +0 -1
- package/src/__tests__/scoped-approval-grants.test.ts +0 -1
- package/src/__tests__/scoped-grant-security-matrix.test.ts +0 -1
- package/src/__tests__/secret-ingress-handler.test.ts +0 -1
- package/src/__tests__/send-endpoint-busy.test.ts +21 -6
- package/src/__tests__/sequence-store.test.ts +0 -1
- package/src/__tests__/session-init.benchmark.test.ts +4 -5
- package/src/__tests__/skill-include-graph.test.ts +66 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +0 -1
- package/src/__tests__/skill-load-tool.test.ts +149 -1
- package/src/__tests__/skill-projection-feature-flag.test.ts +0 -1
- package/src/__tests__/skills-uninstall.test.ts +1 -1
- package/src/__tests__/skills.test.ts +3 -3
- package/src/__tests__/slack-channel-config.test.ts +67 -3
- package/src/__tests__/slack-share-routes.test.ts +17 -19
- package/src/__tests__/system-prompt.test.ts +0 -1
- package/src/__tests__/telegram-invite-adapter.test.ts +18 -22
- package/src/__tests__/terminal-tools.test.ts +4 -3
- package/src/__tests__/test-support/computer-use-skill-harness.ts +3 -2
- package/src/__tests__/tool-approval-handler.test.ts +0 -1
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -1
- package/src/__tests__/tool-executor-shell-integration.test.ts +0 -1
- package/src/__tests__/tool-executor.test.ts +0 -1
- package/src/__tests__/tool-grant-request-escalation.test.ts +0 -1
- package/src/__tests__/trust-store-pattern-matches.test.ts +29 -0
- package/src/__tests__/trust-store.test.ts +1 -22
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +0 -1
- package/src/__tests__/twilio-routes.test.ts +0 -16
- package/src/__tests__/verification-control-plane-policy.test.ts +0 -1
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -1
- package/src/agent/ax-tree-compaction.test.ts +235 -0
- package/src/agent/loop.ts +76 -130
- package/src/calls/call-domain.ts +1 -6
- package/src/calls/relay-server.ts +9 -13
- package/src/calls/twilio-config.ts +2 -7
- package/src/calls/twilio-routes.ts +1 -2
- package/src/calls/voice-ingress-preflight.ts +1 -1
- package/src/cli/commands/browser-relay.ts +18 -12
- package/src/cli/commands/completions.ts +0 -3
- package/src/cli/commands/credentials.ts +101 -15
- package/src/cli/commands/oauth/apps.ts +255 -0
- package/src/cli/commands/oauth/connections.ts +299 -0
- package/src/cli/commands/oauth/index.ts +52 -0
- package/src/cli/commands/oauth/providers.ts +242 -0
- package/src/cli/commands/skills.ts +4 -338
- package/src/cli/program.ts +1 -5
- package/src/cli/reference.ts +1 -3
- package/src/config/assistant-feature-flags.ts +0 -3
- package/src/config/bundled-skills/_shared/CLI_RETRIEVAL_PATTERN.md +1 -1
- package/src/config/bundled-skills/computer-use/SKILL.md +3 -6
- package/src/config/bundled-skills/computer-use/TOOLS.json +22 -4
- package/src/config/bundled-skills/google-calendar/calendar-client.ts +21 -16
- package/src/config/bundled-skills/messaging/tools/shared.ts +1 -4
- package/src/config/bundled-skills/settings/SKILL.md +1 -1
- package/src/config/bundled-skills/settings/TOOLS.json +2 -8
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +5 -33
- package/src/config/env-registry.ts +14 -83
- package/src/config/env.ts +11 -50
- package/src/config/feature-flag-registry.json +16 -16
- package/src/config/loader.ts +0 -6
- package/src/config/schema.ts +3 -1
- package/src/config/skills.ts +21 -2
- package/src/context/image-dimensions.ts +229 -0
- package/src/context/token-estimator.ts +75 -12
- package/src/context/window-manager.ts +49 -10
- package/src/daemon/assistant-attachments.ts +1 -13
- package/src/daemon/handlers/config-ingress.ts +8 -33
- package/src/daemon/handlers/config-slack-channel.ts +49 -46
- package/src/daemon/handlers/config-telegram.ts +32 -16
- package/src/daemon/handlers/sessions.ts +10 -24
- package/src/daemon/handlers/shared.ts +0 -130
- package/src/daemon/host-cu-proxy.ts +401 -0
- package/src/daemon/lifecycle.ts +36 -68
- package/src/daemon/message-protocol.ts +3 -0
- package/src/daemon/message-types/computer-use.ts +2 -119
- package/src/daemon/message-types/host-cu.ts +19 -0
- package/src/daemon/message-types/messages.ts +3 -0
- package/src/daemon/server.ts +14 -21
- package/src/daemon/session-agent-loop-handlers.ts +2 -0
- package/src/daemon/session-attachments.ts +1 -2
- package/src/daemon/session-slash.ts +1 -1
- package/src/daemon/session-surfaces.ts +40 -28
- package/src/daemon/session-tool-setup.ts +2 -9
- package/src/daemon/session.ts +138 -15
- package/src/daemon/tool-side-effects.ts +2 -8
- package/src/daemon/watch-handler.ts +2 -2
- package/src/events/tool-metrics-listener.ts +2 -2
- package/src/hooks/manager.ts +1 -4
- package/src/inbound/public-ingress-urls.ts +7 -7
- package/src/logfire.ts +16 -5
- package/src/memory/conversation-key-store.ts +21 -0
- package/src/memory/db-init.ts +4 -0
- package/src/memory/migrations/149-oauth-tables.ts +60 -0
- package/src/memory/migrations/index.ts +1 -0
- package/src/memory/schema/index.ts +1 -0
- package/src/memory/schema/oauth.ts +65 -0
- package/src/messaging/provider.ts +4 -4
- package/src/messaging/providers/gmail/client.ts +82 -2
- package/src/messaging/providers/gmail/people-client.ts +10 -10
- package/src/messaging/providers/telegram-bot/adapter.ts +17 -17
- package/src/messaging/providers/whatsapp/adapter.ts +11 -8
- package/src/messaging/registry.ts +2 -32
- package/src/notifications/copy-composer.ts +0 -5
- package/src/notifications/signal.ts +4 -5
- package/src/oauth/byo-connection.test.ts +126 -25
- package/src/oauth/byo-connection.ts +22 -6
- package/src/oauth/connect-orchestrator.ts +113 -57
- package/src/oauth/connect-types.ts +17 -23
- package/src/oauth/connection-resolver.ts +35 -11
- package/src/oauth/connection.ts +1 -1
- package/src/oauth/manual-token-connection.ts +104 -0
- package/src/oauth/oauth-store.ts +496 -0
- package/src/oauth/platform-connection.test.ts +29 -0
- package/src/oauth/platform-connection.ts +6 -5
- package/src/oauth/provider-behaviors.ts +124 -0
- package/src/oauth/scope-policy.ts +9 -2
- package/src/oauth/seed-providers.ts +161 -0
- package/src/oauth/token-persistence.ts +74 -78
- package/src/permissions/checker.ts +3 -3
- package/src/permissions/defaults.ts +0 -1
- package/src/permissions/prompter.ts +10 -1
- package/src/permissions/trust-store.ts +13 -0
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +3 -1
- package/src/prompts/system-prompt.ts +28 -40
- package/src/providers/anthropic/client.ts +133 -24
- package/src/providers/retry.ts +1 -27
- package/src/runtime/auth/route-policy.ts +0 -3
- package/src/runtime/channel-reply-delivery.ts +0 -40
- package/src/runtime/gateway-client.ts +0 -7
- package/src/runtime/http-server.ts +8 -6
- package/src/runtime/http-types.ts +2 -2
- package/src/runtime/middleware/twilio-validation.ts +1 -11
- package/src/runtime/pending-interactions.ts +14 -12
- package/src/runtime/routes/channel-delivery-routes.ts +0 -1
- package/src/runtime/routes/conversation-routes.ts +73 -19
- package/src/runtime/routes/events-routes.ts +21 -11
- package/src/runtime/routes/host-cu-routes.ts +97 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +12 -111
- package/src/runtime/routes/integrations/slack/share.ts +6 -7
- package/src/runtime/routes/log-export-routes.ts +126 -8
- package/src/runtime/routes/settings-routes.ts +55 -48
- package/src/runtime/routes/surface-action-routes.ts +1 -1
- package/src/runtime/routes/watch-routes.ts +128 -0
- package/src/schedule/integration-status.ts +10 -9
- package/src/security/credential-key.ts +0 -156
- package/src/security/keychain-broker-client.ts +5 -6
- package/src/security/oauth2.ts +1 -1
- package/src/security/token-manager.ts +119 -46
- package/src/skills/catalog-install.ts +358 -0
- package/src/skills/include-graph.ts +32 -0
- package/src/telegram/bot-username.ts +2 -3
- package/src/tools/browser/network-recorder.ts +1 -1
- package/src/tools/browser/network-recording-types.ts +1 -1
- package/src/tools/computer-use/definitions.ts +46 -11
- package/src/tools/computer-use/registry.ts +4 -5
- package/src/tools/credentials/broker.ts +1 -2
- package/src/tools/credentials/metadata-store.ts +17 -121
- package/src/tools/credentials/vault.ts +94 -167
- package/src/tools/registry.ts +2 -7
- package/src/tools/skills/load.ts +62 -3
- package/src/tools/watch/watch-state.ts +0 -12
- package/src/util/logger.ts +7 -41
- package/src/util/platform.ts +9 -28
- package/src/watcher/providers/google-calendar.ts +2 -1
- package/src/__tests__/computer-use-session-compaction.test.ts +0 -143
- package/src/__tests__/computer-use-session-lifecycle.test.ts +0 -322
- package/src/__tests__/computer-use-session-working-dir.test.ts +0 -166
- package/src/__tests__/computer-use-skill-baseline.test.ts +0 -78
- package/src/__tests__/computer-use-skill-endstate.test.ts +0 -105
- package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +0 -249
- package/src/__tests__/ride-shotgun-handler.test.ts +0 -452
- package/src/cli/commands/dev.ts +0 -129
- package/src/cli/commands/map.ts +0 -391
- package/src/cli/commands/oauth.ts +0 -77
- package/src/config/bundled-skills/computer-use/tools/computer-use-request-control.ts +0 -16
- package/src/daemon/computer-use-session.ts +0 -1026
- package/src/daemon/ride-shotgun-handler.ts +0 -569
- package/src/oauth/provider-base-urls.ts +0 -21
- package/src/oauth/provider-profiles.ts +0 -192
- package/src/prompts/computer-use-prompt.ts +0 -98
- package/src/runtime/routes/computer-use-routes.ts +0 -641
- package/src/runtime/telegram-streaming-delivery.test.ts +0 -729
- package/src/runtime/telegram-streaming-delivery.ts +0 -393
- package/src/tools/computer-use/request-computer-control.ts +0 -56
|
@@ -34,7 +34,7 @@ const reasonProperty = {
|
|
|
34
34
|
export const computerUseClickTool: Tool = {
|
|
35
35
|
name: "computer_use_click",
|
|
36
36
|
description:
|
|
37
|
-
"Click
|
|
37
|
+
"Click an element on screen. Prefer element_id (from the accessibility tree) over x/y coordinates.",
|
|
38
38
|
category: "computer-use",
|
|
39
39
|
defaultRiskLevel: RiskLevel.Low,
|
|
40
40
|
executionMode: "proxy",
|
|
@@ -86,7 +86,7 @@ export const computerUseClickTool: Tool = {
|
|
|
86
86
|
export const computerUseTypeTextTool: Tool = {
|
|
87
87
|
name: "computer_use_type_text",
|
|
88
88
|
description:
|
|
89
|
-
"Type text at the current cursor position.
|
|
89
|
+
"Type text at the current cursor position. First click a text field (by element_id) to focus it, then call this tool. If a field shows 'FOCUSED', skip the click.",
|
|
90
90
|
category: "computer-use",
|
|
91
91
|
defaultRiskLevel: RiskLevel.Low,
|
|
92
92
|
executionMode: "proxy",
|
|
@@ -352,13 +352,7 @@ export const computerUseOpenAppTool: Tool = {
|
|
|
352
352
|
export const computerUseRunAppleScriptTool: Tool = {
|
|
353
353
|
name: "computer_use_run_applescript",
|
|
354
354
|
description:
|
|
355
|
-
"
|
|
356
|
-
"Use this for operations that are more reliable through scripting than UI interaction: " +
|
|
357
|
-
"setting a browser URL directly, navigating Finder to a path, querying app state " +
|
|
358
|
-
"(tab count, window titles, document status), or clicking deeply nested menu items. " +
|
|
359
|
-
"The script's return value (if any) will be reported back. " +
|
|
360
|
-
'NEVER use "do shell script" — it is blocked for security. ' +
|
|
361
|
-
"Keep scripts short and targeted to a single operation.",
|
|
355
|
+
"Run an AppleScript command. Prefer this over click/type when possible — it doesn't move the cursor or interrupt the user. Never use 'do shell script' inside AppleScript (blocked for security).",
|
|
362
356
|
category: "computer-use",
|
|
363
357
|
defaultRiskLevel: RiskLevel.Low,
|
|
364
358
|
executionMode: "proxy",
|
|
@@ -395,7 +389,8 @@ export const computerUseRunAppleScriptTool: Tool = {
|
|
|
395
389
|
|
|
396
390
|
export const computerUseDoneTool: Tool = {
|
|
397
391
|
name: "computer_use_done",
|
|
398
|
-
description:
|
|
392
|
+
description:
|
|
393
|
+
"Signal that the computer use task is complete. Provide a summary of what was accomplished. This ends the computer use session.",
|
|
399
394
|
category: "computer-use",
|
|
400
395
|
defaultRiskLevel: RiskLevel.Low,
|
|
401
396
|
executionMode: "proxy",
|
|
@@ -428,7 +423,7 @@ export const computerUseDoneTool: Tool = {
|
|
|
428
423
|
export const computerUseRespondTool: Tool = {
|
|
429
424
|
name: "computer_use_respond",
|
|
430
425
|
description:
|
|
431
|
-
"Respond
|
|
426
|
+
"Respond to the user with a text answer instead of performing computer actions. Use this when you can answer directly without interacting with the screen.",
|
|
432
427
|
category: "computer-use",
|
|
433
428
|
defaultRiskLevel: RiskLevel.Low,
|
|
434
429
|
executionMode: "proxy",
|
|
@@ -458,11 +453,41 @@ export const computerUseRespondTool: Tool = {
|
|
|
458
453
|
execute: proxyExecute,
|
|
459
454
|
};
|
|
460
455
|
|
|
456
|
+
// ---------------------------------------------------------------------------
|
|
457
|
+
// observe
|
|
458
|
+
// ---------------------------------------------------------------------------
|
|
459
|
+
|
|
460
|
+
export const computerUseObserveTool: Tool = {
|
|
461
|
+
name: "computer_use_observe",
|
|
462
|
+
description:
|
|
463
|
+
"Capture the current screen state. Returns the accessibility tree with [ID] element references and optionally a screenshot.\n\nThe accessibility tree shows interactive elements like [3] AXButton 'Save' or [17] AXTextField 'Search'. Use element_id to target these elements in subsequent actions — this is much more reliable than pixel coordinates.\n\nCall this before your first computer use action, or to check screen state without acting.",
|
|
464
|
+
category: "computer-use",
|
|
465
|
+
defaultRiskLevel: RiskLevel.Low,
|
|
466
|
+
executionMode: "proxy",
|
|
467
|
+
|
|
468
|
+
getDefinition(): ToolDefinition {
|
|
469
|
+
return {
|
|
470
|
+
name: this.name,
|
|
471
|
+
description: this.description,
|
|
472
|
+
input_schema: {
|
|
473
|
+
type: "object",
|
|
474
|
+
properties: {
|
|
475
|
+
reason: reasonProperty,
|
|
476
|
+
},
|
|
477
|
+
required: ["reason"],
|
|
478
|
+
},
|
|
479
|
+
};
|
|
480
|
+
},
|
|
481
|
+
|
|
482
|
+
execute: proxyExecute,
|
|
483
|
+
};
|
|
484
|
+
|
|
461
485
|
// ---------------------------------------------------------------------------
|
|
462
486
|
// All tools exported as array for convenience
|
|
463
487
|
// ---------------------------------------------------------------------------
|
|
464
488
|
|
|
465
489
|
export const allComputerUseTools: Tool[] = [
|
|
490
|
+
computerUseObserveTool,
|
|
466
491
|
computerUseClickTool,
|
|
467
492
|
computerUseTypeTextTool,
|
|
468
493
|
computerUseKeyTool,
|
|
@@ -474,3 +499,13 @@ export const allComputerUseTools: Tool[] = [
|
|
|
474
499
|
computerUseDoneTool,
|
|
475
500
|
computerUseRespondTool,
|
|
476
501
|
];
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* Tools safe for the legacy fallback path (no skill projection).
|
|
505
|
+
*
|
|
506
|
+
* Excludes `computer_use_observe` because the macOS client doesn't handle it
|
|
507
|
+
* in the legacy code path — it falls back to `.done` which skips sending an
|
|
508
|
+
* observation, causing the daemon to block on `pendingObservation` until timeout.
|
|
509
|
+
*/
|
|
510
|
+
export const legacyFallbackComputerUseTools: Tool[] =
|
|
511
|
+
allComputerUseTools.filter((t) => t.name !== "computer_use_observe");
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Registers computer-use tools with the daemon's tool registry.
|
|
3
3
|
*
|
|
4
|
-
* The
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* normal startup.
|
|
4
|
+
* The computer_use_* action tools are now provided by the bundled
|
|
5
|
+
* computer-use skill. This module retains registerComputerUseActionTools()
|
|
6
|
+
* for backward compatibility (used by tests), but it is no longer called
|
|
7
|
+
* during normal startup.
|
|
9
8
|
*/
|
|
10
9
|
|
|
11
10
|
import { registerTool } from "../registry.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { v4 as uuid } from "uuid";
|
|
2
2
|
|
|
3
|
-
import { credentialKey
|
|
3
|
+
import { credentialKey } from "../../security/credential-key.js";
|
|
4
4
|
import { getSecureKey } from "../../security/secure-keys.js";
|
|
5
5
|
import { getLogger } from "../../util/logger.js";
|
|
6
6
|
import type {
|
|
@@ -60,7 +60,6 @@ export class CredentialBroker {
|
|
|
60
60
|
* Returns a single-use token on success, or a denial reason on failure.
|
|
61
61
|
*/
|
|
62
62
|
authorize(request: AuthorizeRequest): AuthorizeResult {
|
|
63
|
-
migrateKeys();
|
|
64
63
|
const metadata = getCredentialMetadata(request.service, request.field);
|
|
65
64
|
if (!metadata) {
|
|
66
65
|
return {
|
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
* Persists non-secret metadata about credentials (policy, timestamps, IDs)
|
|
5
5
|
* in a versioned JSON file under protected storage. Secret values remain
|
|
6
6
|
* in the secure key backend only.
|
|
7
|
+
*
|
|
8
|
+
* OAuth-specific fields (expiresAt, grantedScopes, oauth2TokenUrl,
|
|
9
|
+
* oauth2ClientId, oauth2TokenEndpointAuthMethod, hasRefreshToken) are now
|
|
10
|
+
* exclusively managed by the SQLite oauth-store and have been removed
|
|
11
|
+
* from this interface as of v5.
|
|
7
12
|
*/
|
|
8
13
|
|
|
9
14
|
import { randomUUID } from "node:crypto";
|
|
@@ -21,26 +26,16 @@ export interface CredentialMetadata {
|
|
|
21
26
|
allowedTools: string[];
|
|
22
27
|
allowedDomains: string[];
|
|
23
28
|
usageDescription?: string;
|
|
24
|
-
expiresAt?: number;
|
|
25
|
-
grantedScopes?: string[];
|
|
26
|
-
/** OAuth2 token endpoint — enables autonomous token refresh without an IntegrationDefinition. */
|
|
27
|
-
oauth2TokenUrl?: string;
|
|
28
|
-
/** OAuth2 client ID — paired with oauth2TokenUrl for refresh. */
|
|
29
|
-
oauth2ClientId?: string;
|
|
30
|
-
/** How the client authenticates at the token endpoint (client_secret_basic or client_secret_post). */
|
|
31
|
-
oauth2TokenEndpointAuthMethod?: string;
|
|
32
29
|
/** Human-friendly name for this credential (e.g. "fal-primary"). */
|
|
33
30
|
alias?: string;
|
|
34
31
|
/** Templates describing how to inject this credential into proxied requests. */
|
|
35
32
|
injectionTemplates?: CredentialInjectionTemplate[];
|
|
36
|
-
/** Whether a refresh token exists in the secure store for this service. */
|
|
37
|
-
hasRefreshToken?: boolean;
|
|
38
33
|
createdAt: number;
|
|
39
34
|
updatedAt: number;
|
|
40
35
|
}
|
|
41
36
|
|
|
42
37
|
/** Current on-disk schema version. */
|
|
43
|
-
const CURRENT_VERSION =
|
|
38
|
+
const CURRENT_VERSION = 5;
|
|
44
39
|
|
|
45
40
|
interface MetadataFile {
|
|
46
41
|
version: typeof CURRENT_VERSION;
|
|
@@ -88,9 +83,10 @@ function isValidCredentialRecord(
|
|
|
88
83
|
}
|
|
89
84
|
|
|
90
85
|
/**
|
|
91
|
-
* Migrate
|
|
86
|
+
* Migrate any record to v5 by stripping OAuth-specific fields that are
|
|
87
|
+
* now exclusively managed by the SQLite oauth-store.
|
|
92
88
|
*/
|
|
93
|
-
function
|
|
89
|
+
function migrateRecordToV5(
|
|
94
90
|
record: Record<string, unknown>,
|
|
95
91
|
): CredentialMetadata {
|
|
96
92
|
return {
|
|
@@ -107,73 +103,15 @@ function migrateRecordV1toV2(
|
|
|
107
103
|
typeof record.usageDescription === "string"
|
|
108
104
|
? record.usageDescription
|
|
109
105
|
: undefined,
|
|
110
|
-
expiresAt:
|
|
111
|
-
typeof record.expiresAt === "number" ? record.expiresAt : undefined,
|
|
112
|
-
grantedScopes: Array.isArray(record.grantedScopes)
|
|
113
|
-
? (record.grantedScopes as string[])
|
|
114
|
-
: undefined,
|
|
115
|
-
oauth2TokenUrl:
|
|
116
|
-
typeof record.oauth2TokenUrl === "string"
|
|
117
|
-
? record.oauth2TokenUrl
|
|
118
|
-
: undefined,
|
|
119
|
-
oauth2ClientId:
|
|
120
|
-
typeof record.oauth2ClientId === "string"
|
|
121
|
-
? record.oauth2ClientId
|
|
122
|
-
: undefined,
|
|
123
|
-
oauth2TokenEndpointAuthMethod:
|
|
124
|
-
typeof record.oauth2TokenEndpointAuthMethod === "string"
|
|
125
|
-
? record.oauth2TokenEndpointAuthMethod
|
|
126
|
-
: undefined,
|
|
127
106
|
alias: typeof record.alias === "string" ? record.alias : undefined,
|
|
128
107
|
injectionTemplates: Array.isArray(record.injectionTemplates)
|
|
129
108
|
? (record.injectionTemplates as CredentialInjectionTemplate[])
|
|
130
109
|
: undefined,
|
|
131
|
-
hasRefreshToken:
|
|
132
|
-
typeof record.hasRefreshToken === "boolean"
|
|
133
|
-
? record.hasRefreshToken
|
|
134
|
-
: undefined,
|
|
135
110
|
createdAt: record.createdAt as number,
|
|
136
111
|
updatedAt: record.updatedAt as number,
|
|
137
112
|
};
|
|
138
113
|
}
|
|
139
114
|
|
|
140
|
-
/**
|
|
141
|
-
* Migrate a v2 record to v3 by stripping the oauth2ClientSecret field.
|
|
142
|
-
* Client secrets are now read exclusively from the secure key store.
|
|
143
|
-
*/
|
|
144
|
-
function migrateRecordV2toV3(record: CredentialMetadata): CredentialMetadata {
|
|
145
|
-
const { oauth2ClientSecret: _removed, ...rest } =
|
|
146
|
-
record as CredentialMetadata & { oauth2ClientSecret?: string };
|
|
147
|
-
return rest;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Migrate v3 credentials to v4:
|
|
152
|
-
* - Delete ghost `refresh_token` metadata records
|
|
153
|
-
* - Set `hasRefreshToken: true` on corresponding `access_token` records
|
|
154
|
-
*/
|
|
155
|
-
function migrateV3toV4(
|
|
156
|
-
credentials: CredentialMetadata[],
|
|
157
|
-
): CredentialMetadata[] {
|
|
158
|
-
// Collect services that had refresh_token ghost records
|
|
159
|
-
const servicesWithRefresh = new Set<string>();
|
|
160
|
-
for (const c of credentials) {
|
|
161
|
-
if (c.field === "refresh_token") {
|
|
162
|
-
servicesWithRefresh.add(c.service);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Remove all refresh_token records and set hasRefreshToken on access_token records
|
|
167
|
-
const filtered = credentials.filter((c) => c.field !== "refresh_token");
|
|
168
|
-
for (const c of filtered) {
|
|
169
|
-
if (c.field === "access_token" && servicesWithRefresh.has(c.service)) {
|
|
170
|
-
c.hasRefreshToken = true;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
return filtered;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
115
|
function loadFile(): LoadResult {
|
|
178
116
|
const raw = readTextFileSync(getMetadataPath());
|
|
179
117
|
if (raw == null) {
|
|
@@ -189,7 +127,8 @@ function loadFile(): LoadResult {
|
|
|
189
127
|
fileVersion !== 1 &&
|
|
190
128
|
fileVersion !== 2 &&
|
|
191
129
|
fileVersion !== 3 &&
|
|
192
|
-
fileVersion !== 4
|
|
130
|
+
fileVersion !== 4 &&
|
|
131
|
+
fileVersion !== 5
|
|
193
132
|
) {
|
|
194
133
|
// Unrecognized version (future, fractional, negative, zero) — refuse to touch it
|
|
195
134
|
return { unknownVersion: true };
|
|
@@ -201,24 +140,12 @@ function loadFile(): LoadResult {
|
|
|
201
140
|
const validRecords = rawCredentials.filter(isValidCredentialRecord);
|
|
202
141
|
|
|
203
142
|
if (fileVersion < CURRENT_VERSION) {
|
|
204
|
-
//
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
} else if (fileVersion === 2) {
|
|
211
|
-
credentials = migrateV3toV4(
|
|
212
|
-
(validRecords as unknown as CredentialMetadata[]).map(
|
|
213
|
-
migrateRecordV2toV3,
|
|
214
|
-
),
|
|
215
|
-
);
|
|
216
|
-
} else {
|
|
217
|
-
// fileVersion === 3
|
|
218
|
-
credentials = migrateV3toV4(
|
|
219
|
-
validRecords as unknown as CredentialMetadata[],
|
|
220
|
-
);
|
|
221
|
-
}
|
|
143
|
+
// Migrate all older versions to v5 by stripping OAuth-specific fields
|
|
144
|
+
// and removing ghost refresh_token records
|
|
145
|
+
const filtered = validRecords.filter(
|
|
146
|
+
(r) => (r as Record<string, unknown>).field !== "refresh_token",
|
|
147
|
+
);
|
|
148
|
+
const credentials = filtered.map(migrateRecordToV5);
|
|
222
149
|
const migrated: MetadataFile = { version: CURRENT_VERSION, credentials };
|
|
223
150
|
try {
|
|
224
151
|
saveFile(migrated);
|
|
@@ -272,17 +199,10 @@ export function upsertCredentialMetadata(
|
|
|
272
199
|
allowedTools?: string[];
|
|
273
200
|
allowedDomains?: string[];
|
|
274
201
|
usageDescription?: string;
|
|
275
|
-
/** Pass `null` to explicitly clear a previously-set expiry. */
|
|
276
|
-
expiresAt?: number | null;
|
|
277
|
-
grantedScopes?: string[];
|
|
278
|
-
oauth2TokenUrl?: string;
|
|
279
|
-
oauth2ClientId?: string;
|
|
280
|
-
oauth2TokenEndpointAuthMethod?: string;
|
|
281
202
|
/** Pass `null` to explicitly clear a previously-set alias. */
|
|
282
203
|
alias?: string | null;
|
|
283
204
|
/** Pass `null` to explicitly clear injection templates. */
|
|
284
205
|
injectionTemplates?: CredentialInjectionTemplate[] | null;
|
|
285
|
-
hasRefreshToken?: boolean;
|
|
286
206
|
},
|
|
287
207
|
): CredentialMetadata {
|
|
288
208
|
const result = loadFile();
|
|
@@ -305,22 +225,6 @@ export function upsertCredentialMetadata(
|
|
|
305
225
|
existing.allowedDomains = policy.allowedDomains;
|
|
306
226
|
if (policy?.usageDescription !== undefined)
|
|
307
227
|
existing.usageDescription = policy.usageDescription;
|
|
308
|
-
if (policy?.expiresAt !== undefined) {
|
|
309
|
-
if (policy.expiresAt == null) {
|
|
310
|
-
delete existing.expiresAt;
|
|
311
|
-
} else {
|
|
312
|
-
existing.expiresAt = policy.expiresAt;
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
if (policy?.grantedScopes !== undefined)
|
|
316
|
-
existing.grantedScopes = policy.grantedScopes;
|
|
317
|
-
if (policy?.oauth2TokenUrl !== undefined)
|
|
318
|
-
existing.oauth2TokenUrl = policy.oauth2TokenUrl;
|
|
319
|
-
if (policy?.oauth2ClientId !== undefined)
|
|
320
|
-
existing.oauth2ClientId = policy.oauth2ClientId;
|
|
321
|
-
if (policy?.oauth2TokenEndpointAuthMethod !== undefined)
|
|
322
|
-
existing.oauth2TokenEndpointAuthMethod =
|
|
323
|
-
policy.oauth2TokenEndpointAuthMethod;
|
|
324
228
|
if (policy?.alias !== undefined) {
|
|
325
229
|
if (policy.alias == null) {
|
|
326
230
|
delete existing.alias;
|
|
@@ -335,8 +239,6 @@ export function upsertCredentialMetadata(
|
|
|
335
239
|
existing.injectionTemplates = policy.injectionTemplates;
|
|
336
240
|
}
|
|
337
241
|
}
|
|
338
|
-
if (policy?.hasRefreshToken !== undefined)
|
|
339
|
-
existing.hasRefreshToken = policy.hasRefreshToken;
|
|
340
242
|
existing.updatedAt = now;
|
|
341
243
|
saveFile(data);
|
|
342
244
|
return existing;
|
|
@@ -349,14 +251,8 @@ export function upsertCredentialMetadata(
|
|
|
349
251
|
allowedTools: policy?.allowedTools ?? [],
|
|
350
252
|
allowedDomains: policy?.allowedDomains ?? [],
|
|
351
253
|
usageDescription: policy?.usageDescription,
|
|
352
|
-
expiresAt: policy?.expiresAt ?? undefined,
|
|
353
|
-
grantedScopes: policy?.grantedScopes,
|
|
354
|
-
oauth2TokenUrl: policy?.oauth2TokenUrl,
|
|
355
|
-
oauth2ClientId: policy?.oauth2ClientId,
|
|
356
|
-
oauth2TokenEndpointAuthMethod: policy?.oauth2TokenEndpointAuthMethod,
|
|
357
254
|
alias: policy?.alias ?? undefined,
|
|
358
255
|
injectionTemplates: policy?.injectionTemplates ?? undefined,
|
|
359
|
-
hasRefreshToken: policy?.hasRefreshToken,
|
|
360
256
|
createdAt: now,
|
|
361
257
|
updatedAt: now,
|
|
362
258
|
};
|