@assemblyline-agents/runtime 2.0.0 → 3.0.0
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/LICENSE +1 -1
- package/dist/a2a-client.d.ts +22 -0
- package/dist/a2a-client.d.ts.map +1 -0
- package/dist/a2a-client.js +211 -0
- package/dist/a2a-client.js.map +1 -0
- package/dist/agent-hook-context.d.ts +6 -1
- package/dist/agent-hook-context.d.ts.map +1 -1
- package/dist/agent-hook-context.js +24 -11
- package/dist/agent-hook-context.js.map +1 -1
- package/dist/agent-hook-evaluation.d.ts +6 -1
- package/dist/agent-hook-evaluation.d.ts.map +1 -1
- package/dist/agent-hook-evaluation.js +16 -5
- package/dist/agent-hook-evaluation.js.map +1 -1
- package/dist/agent-hook-run-lifecycle.d.ts +5 -1
- package/dist/agent-hook-run-lifecycle.d.ts.map +1 -1
- package/dist/agent-hook-run-lifecycle.js +11 -13
- package/dist/agent-hook-run-lifecycle.js.map +1 -1
- package/dist/agent-surface.d.ts +32 -0
- package/dist/agent-surface.d.ts.map +1 -0
- package/dist/agent-surface.js +85 -0
- package/dist/agent-surface.js.map +1 -0
- package/dist/attachments.d.ts +5 -1
- package/dist/attachments.d.ts.map +1 -1
- package/dist/attachments.js +58 -32
- package/dist/attachments.js.map +1 -1
- package/dist/audience.d.ts +30 -0
- package/dist/audience.d.ts.map +1 -0
- package/dist/audience.js +68 -0
- package/dist/audience.js.map +1 -0
- package/dist/automation-event-match.d.ts +8 -0
- package/dist/automation-event-match.d.ts.map +1 -0
- package/dist/automation-event-match.js +25 -0
- package/dist/automation-event-match.js.map +1 -0
- package/dist/background-review-trigger.d.ts.map +1 -1
- package/dist/background-review-trigger.js +14 -16
- package/dist/background-review-trigger.js.map +1 -1
- package/dist/background-review-types.d.ts +9 -2
- package/dist/background-review-types.d.ts.map +1 -1
- package/dist/background-review-worker.js +2 -2
- package/dist/background-review-worker.js.map +1 -1
- package/dist/background-review.d.ts.map +1 -1
- package/dist/background-review.js +27 -10
- package/dist/background-review.js.map +1 -1
- package/dist/builtin-tool-executors.d.ts +47 -21
- package/dist/builtin-tool-executors.d.ts.map +1 -1
- package/dist/builtin-tool-executors.js +293 -121
- package/dist/builtin-tool-executors.js.map +1 -1
- package/dist/builtin-tools.d.ts +16 -8
- package/dist/builtin-tools.d.ts.map +1 -1
- package/dist/builtin-tools.js +309 -70
- package/dist/builtin-tools.js.map +1 -1
- package/dist/capability-catalog.d.ts +3 -2
- package/dist/capability-catalog.d.ts.map +1 -1
- package/dist/capability-catalog.js +10 -6
- package/dist/capability-catalog.js.map +1 -1
- package/dist/capability-inspection.d.ts +31 -0
- package/dist/capability-inspection.d.ts.map +1 -0
- package/dist/capability-inspection.js +58 -0
- package/dist/capability-inspection.js.map +1 -0
- package/dist/channel-http-util.d.ts.map +1 -1
- package/dist/channel-http-util.js +6 -2
- package/dist/channel-http-util.js.map +1 -1
- package/dist/channel-ingress.d.ts +16 -1
- package/dist/channel-ingress.d.ts.map +1 -1
- package/dist/channel-ingress.js +117 -7
- package/dist/channel-ingress.js.map +1 -1
- package/dist/channel.d.ts +58 -2
- package/dist/channel.d.ts.map +1 -1
- package/dist/checkpoints.d.ts +1 -1
- package/dist/checkpoints.d.ts.map +1 -1
- package/dist/checkpoints.js +6 -6
- package/dist/checkpoints.js.map +1 -1
- package/dist/child-process-env.d.ts +7 -0
- package/dist/child-process-env.d.ts.map +1 -0
- package/dist/child-process-env.js +41 -0
- package/dist/child-process-env.js.map +1 -0
- package/dist/computer-relay-protocol.d.ts.map +1 -1
- package/dist/computer-relay-protocol.js +4 -4
- package/dist/computer-relay-protocol.js.map +1 -1
- package/dist/concurrency.d.ts +1 -1
- package/dist/concurrency.js +1 -1
- package/dist/connection-auth-util.d.ts +1 -0
- package/dist/connection-auth-util.d.ts.map +1 -1
- package/dist/connection-auth-util.js +1 -1
- package/dist/connection-auth-util.js.map +1 -1
- package/dist/connection-clients.d.ts +11 -3
- package/dist/connection-clients.d.ts.map +1 -1
- package/dist/connection-clients.js +71 -8
- package/dist/connection-clients.js.map +1 -1
- package/dist/connection-event-store.d.ts +98 -0
- package/dist/connection-event-store.d.ts.map +1 -0
- package/dist/connection-event-store.js +162 -0
- package/dist/connection-event-store.js.map +1 -0
- package/dist/connection-events.d.ts +51 -0
- package/dist/connection-events.d.ts.map +1 -0
- package/dist/connection-events.js +343 -0
- package/dist/connection-events.js.map +1 -0
- package/dist/connection-stores.d.ts +3 -0
- package/dist/connection-stores.d.ts.map +1 -1
- package/dist/connection-stores.js +4 -1
- package/dist/connection-stores.js.map +1 -1
- package/dist/connection-util.d.ts +3 -2
- package/dist/connection-util.d.ts.map +1 -1
- package/dist/connection-util.js +18 -1
- package/dist/connection-util.js.map +1 -1
- package/dist/connections.d.ts +54 -5
- package/dist/connections.d.ts.map +1 -1
- package/dist/connections.js +342 -53
- package/dist/connections.js.map +1 -1
- package/dist/context-policy-util.d.ts.map +1 -1
- package/dist/context-policy-util.js +1 -3
- package/dist/context-policy-util.js.map +1 -1
- package/dist/conversation-context.d.ts +6 -2
- package/dist/conversation-context.d.ts.map +1 -1
- package/dist/conversation-context.js +45 -12
- package/dist/conversation-context.js.map +1 -1
- package/dist/conversation-transcript.d.ts +8 -5
- package/dist/conversation-transcript.d.ts.map +1 -1
- package/dist/conversation-transcript.js +24 -5
- package/dist/conversation-transcript.js.map +1 -1
- package/dist/conversation-turn-coalescing.d.ts +22 -0
- package/dist/conversation-turn-coalescing.d.ts.map +1 -0
- package/dist/conversation-turn-coalescing.js +121 -0
- package/dist/conversation-turn-coalescing.js.map +1 -0
- package/dist/conversation-turn-dispatch.d.ts +5 -2
- package/dist/conversation-turn-dispatch.d.ts.map +1 -1
- package/dist/conversation-turn-dispatch.js +59 -22
- package/dist/conversation-turn-dispatch.js.map +1 -1
- package/dist/default-outbound-route.d.ts +29 -0
- package/dist/default-outbound-route.d.ts.map +1 -0
- package/dist/default-outbound-route.js +105 -0
- package/dist/default-outbound-route.js.map +1 -0
- package/dist/delivery-artifacts.d.ts +2 -0
- package/dist/delivery-artifacts.d.ts.map +1 -0
- package/dist/delivery-artifacts.js +34 -0
- package/dist/delivery-artifacts.js.map +1 -0
- package/dist/delivery-files.d.ts +5 -0
- package/dist/delivery-files.d.ts.map +1 -0
- package/dist/delivery-files.js +33 -0
- package/dist/delivery-files.js.map +1 -0
- package/dist/delivery-queue.d.ts +21 -1
- package/dist/delivery-queue.d.ts.map +1 -1
- package/dist/delivery-queue.js +144 -1
- package/dist/delivery-queue.js.map +1 -1
- package/dist/delivery-send.d.ts +2 -2
- package/dist/delivery-send.d.ts.map +1 -1
- package/dist/delivery-send.js +256 -100
- package/dist/delivery-send.js.map +1 -1
- package/dist/durable-files.d.ts +37 -0
- package/dist/durable-files.d.ts.map +1 -0
- package/dist/durable-files.js +63 -0
- package/dist/durable-files.js.map +1 -0
- package/dist/harness-loop.d.ts +6 -1
- package/dist/harness-loop.d.ts.map +1 -1
- package/dist/harness-loop.js +110 -25
- package/dist/harness-loop.js.map +1 -1
- package/dist/heic-transcode-worker.d.ts +2 -0
- package/dist/heic-transcode-worker.d.ts.map +1 -0
- package/dist/heic-transcode-worker.js +59 -0
- package/dist/heic-transcode-worker.js.map +1 -0
- package/dist/heic-transcode.d.ts +18 -0
- package/dist/heic-transcode.d.ts.map +1 -0
- package/dist/heic-transcode.js +99 -0
- package/dist/heic-transcode.js.map +1 -0
- package/dist/human-input-routing.d.ts +22 -0
- package/dist/human-input-routing.d.ts.map +1 -0
- package/dist/human-input-routing.js +26 -0
- package/dist/human-input-routing.js.map +1 -0
- package/dist/in-memory-conversation-turn-store.d.ts +3 -1
- package/dist/in-memory-conversation-turn-store.d.ts.map +1 -1
- package/dist/in-memory-conversation-turn-store.js +41 -2
- package/dist/in-memory-conversation-turn-store.js.map +1 -1
- package/dist/in-memory-run-store.d.ts +8 -0
- package/dist/in-memory-run-store.d.ts.map +1 -1
- package/dist/in-memory-run-store.js +28 -5
- package/dist/in-memory-run-store.js.map +1 -1
- package/dist/in-memory-sandbox-session-store.d.ts +2 -1
- package/dist/in-memory-sandbox-session-store.d.ts.map +1 -1
- package/dist/in-memory-sandbox-session-store.js +65 -1
- package/dist/in-memory-sandbox-session-store.js.map +1 -1
- package/dist/in-memory-small-stores.d.ts +18 -1
- package/dist/in-memory-small-stores.d.ts.map +1 -1
- package/dist/in-memory-small-stores.js +107 -1
- package/dist/in-memory-small-stores.js.map +1 -1
- package/dist/in-memory-workspace-store.d.ts +40 -0
- package/dist/in-memory-workspace-store.d.ts.map +1 -0
- package/dist/in-memory-workspace-store.js +370 -0
- package/dist/in-memory-workspace-store.js.map +1 -0
- package/dist/index.d.ts +73 -36
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +390 -258
- package/dist/index.js.map +1 -1
- package/dist/json-util.d.ts +8 -0
- package/dist/json-util.d.ts.map +1 -1
- package/dist/json-util.js +8 -0
- package/dist/json-util.js.map +1 -1
- package/dist/local-sandbox.d.ts +3 -2
- package/dist/local-sandbox.d.ts.map +1 -1
- package/dist/local-sandbox.js +69 -25
- package/dist/local-sandbox.js.map +1 -1
- package/dist/mcp-client.d.ts.map +1 -1
- package/dist/mcp-client.js +3 -2
- package/dist/mcp-client.js.map +1 -1
- package/dist/media-processor.d.ts +42 -0
- package/dist/media-processor.d.ts.map +1 -0
- package/dist/media-processor.js +2 -0
- package/dist/media-processor.js.map +1 -0
- package/dist/model-media.d.ts +13 -1
- package/dist/model-media.d.ts.map +1 -1
- package/dist/model-media.js +81 -18
- package/dist/model-media.js.map +1 -1
- package/dist/model-visible-interrupts.d.ts +10 -0
- package/dist/model-visible-interrupts.d.ts.map +1 -0
- package/dist/model-visible-interrupts.js +41 -0
- package/dist/model-visible-interrupts.js.map +1 -0
- package/dist/operator-controls.d.ts.map +1 -1
- package/dist/operator-controls.js +16 -3
- package/dist/operator-controls.js.map +1 -1
- package/dist/outbound-http-policy.d.ts +7 -0
- package/dist/outbound-http-policy.d.ts.map +1 -0
- package/dist/outbound-http-policy.js +155 -0
- package/dist/outbound-http-policy.js.map +1 -0
- package/dist/public-api.d.ts +21 -0
- package/dist/public-api.d.ts.map +1 -0
- package/dist/public-api.js +19 -0
- package/dist/public-api.js.map +1 -0
- package/dist/replay-resume.d.ts +8 -0
- package/dist/replay-resume.d.ts.map +1 -1
- package/dist/replay-resume.js +141 -11
- package/dist/replay-resume.js.map +1 -1
- package/dist/resource-projection-util.d.ts +1 -1
- package/dist/resource-projection-util.d.ts.map +1 -1
- package/dist/resource-projection-util.js +21 -10
- package/dist/resource-projection-util.js.map +1 -1
- package/dist/resource-projection.d.ts +8 -1
- package/dist/resource-projection.d.ts.map +1 -1
- package/dist/resource-projection.js +229 -29
- package/dist/resource-projection.js.map +1 -1
- package/dist/run-heartbeat.d.ts +12 -2
- package/dist/run-heartbeat.d.ts.map +1 -1
- package/dist/run-heartbeat.js +31 -4
- package/dist/run-heartbeat.js.map +1 -1
- package/dist/run-outcome.d.ts +9 -5
- package/dist/run-outcome.d.ts.map +1 -1
- package/dist/run-outcome.js +14 -5
- package/dist/run-outcome.js.map +1 -1
- package/dist/run-recovery.d.ts +19 -3
- package/dist/run-recovery.d.ts.map +1 -1
- package/dist/run-recovery.js +205 -34
- package/dist/run-recovery.js.map +1 -1
- package/dist/run-replay.d.ts +35 -0
- package/dist/run-replay.d.ts.map +1 -0
- package/dist/run-replay.js +193 -0
- package/dist/run-replay.js.map +1 -0
- package/dist/run-result.d.ts +4 -0
- package/dist/run-result.d.ts.map +1 -0
- package/dist/run-result.js +18 -0
- package/dist/run-result.js.map +1 -0
- package/dist/run-timeline-query.d.ts +38 -0
- package/dist/run-timeline-query.d.ts.map +1 -0
- package/dist/run-timeline-query.js +99 -0
- package/dist/run-timeline-query.js.map +1 -0
- package/dist/runs-query.d.ts.map +1 -1
- package/dist/runs-query.js +11 -3
- package/dist/runs-query.js.map +1 -1
- package/dist/runtime-bundle.d.ts.map +1 -1
- package/dist/runtime-bundle.js +2 -0
- package/dist/runtime-bundle.js.map +1 -1
- package/dist/runtime-learning.d.ts +15 -13
- package/dist/runtime-learning.d.ts.map +1 -1
- package/dist/runtime-learning.js +79 -58
- package/dist/runtime-learning.js.map +1 -1
- package/dist/runtime-types.d.ts +68 -9
- package/dist/runtime-types.d.ts.map +1 -1
- package/dist/sandbox-fs.d.ts +8 -5
- package/dist/sandbox-fs.d.ts.map +1 -1
- package/dist/sandbox-fs.js +30 -31
- package/dist/sandbox-fs.js.map +1 -1
- package/dist/sandbox-handle.d.ts +25 -0
- package/dist/sandbox-handle.d.ts.map +1 -0
- package/dist/sandbox-handle.js +120 -0
- package/dist/sandbox-handle.js.map +1 -0
- package/dist/sandbox-session-identity.d.ts +14 -0
- package/dist/sandbox-session-identity.d.ts.map +1 -0
- package/dist/sandbox-session-identity.js +33 -0
- package/dist/sandbox-session-identity.js.map +1 -0
- package/dist/sandbox-session-key.d.ts.map +1 -1
- package/dist/sandbox-session-key.js +12 -9
- package/dist/sandbox-session-key.js.map +1 -1
- package/dist/sandbox-session-lifecycle.d.ts.map +1 -1
- package/dist/sandbox-session-lifecycle.js +187 -44
- package/dist/sandbox-session-lifecycle.js.map +1 -1
- package/dist/sandbox-snapshot-policy.js +3 -3
- package/dist/sandbox-snapshot-policy.js.map +1 -1
- package/dist/sandbox-state-sync.d.ts +4 -2
- package/dist/sandbox-state-sync.d.ts.map +1 -1
- package/dist/sandbox-state-sync.js +47 -15
- package/dist/sandbox-state-sync.js.map +1 -1
- package/dist/sandbox-sync-worker.d.ts +11 -2
- package/dist/sandbox-sync-worker.d.ts.map +1 -1
- package/dist/sandbox-sync-worker.js +143 -31
- package/dist/sandbox-sync-worker.js.map +1 -1
- package/dist/schedule-execution.d.ts.map +1 -1
- package/dist/schedule-execution.js +127 -23
- package/dist/schedule-execution.js.map +1 -1
- package/dist/sdk-api-client.d.ts +17 -0
- package/dist/sdk-api-client.d.ts.map +1 -0
- package/dist/sdk-api-client.js +55 -0
- package/dist/sdk-api-client.js.map +1 -0
- package/dist/security.js +2 -2
- package/dist/security.js.map +1 -1
- package/dist/self-improvement.d.ts +5 -1
- package/dist/self-improvement.d.ts.map +1 -1
- package/dist/self-improvement.js +28 -14
- package/dist/self-improvement.js.map +1 -1
- package/dist/skill-bundles.d.ts +55 -0
- package/dist/skill-bundles.d.ts.map +1 -0
- package/dist/skill-bundles.js +134 -0
- package/dist/skill-bundles.js.map +1 -0
- package/dist/source-loader.js +7 -7
- package/dist/source-loader.js.map +1 -1
- package/dist/state-store-helpers.d.ts +2 -0
- package/dist/state-store-helpers.d.ts.map +1 -1
- package/dist/state-store-helpers.js +11 -1
- package/dist/state-store-helpers.js.map +1 -1
- package/dist/state-stores.d.ts +1 -0
- package/dist/state-stores.d.ts.map +1 -1
- package/dist/state-stores.js +26 -7
- package/dist/state-stores.js.map +1 -1
- package/dist/state.d.ts +305 -5
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js +41 -2
- package/dist/state.js.map +1 -1
- package/dist/storage.d.ts +47 -2
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +158 -6
- package/dist/storage.js.map +1 -1
- package/dist/subagent-execution.d.ts +1 -1
- package/dist/subagent-execution.d.ts.map +1 -1
- package/dist/subagent-execution.js +10 -12
- package/dist/subagent-execution.js.map +1 -1
- package/dist/target-instructions.js +1 -1
- package/dist/target-instructions.js.map +1 -1
- package/dist/tool-apis.d.ts.map +1 -1
- package/dist/tool-apis.js +33 -9
- package/dist/tool-apis.js.map +1 -1
- package/dist/tool-execution.d.ts +22 -12
- package/dist/tool-execution.d.ts.map +1 -1
- package/dist/tool-execution.js +368 -238
- package/dist/tool-execution.js.map +1 -1
- package/dist/turn-media.d.ts +29 -0
- package/dist/turn-media.d.ts.map +1 -0
- package/dist/turn-media.js +233 -0
- package/dist/turn-media.js.map +1 -0
- package/dist/usage-reconciliation.d.ts +1 -1
- package/dist/usage-reconciliation.js +1 -1
- package/dist/workspace-id.d.ts +10 -0
- package/dist/workspace-id.d.ts.map +1 -0
- package/dist/workspace-id.js +55 -0
- package/dist/workspace-id.js.map +1 -0
- package/dist/workspace-manifest.d.ts +24 -0
- package/dist/workspace-manifest.d.ts.map +1 -0
- package/dist/workspace-manifest.js +111 -0
- package/dist/workspace-manifest.js.map +1 -0
- package/dist/workspace-operation-limiter.d.ts +16 -0
- package/dist/workspace-operation-limiter.d.ts.map +1 -0
- package/dist/workspace-operation-limiter.js +31 -0
- package/dist/workspace-operation-limiter.js.map +1 -0
- package/dist/workspace-operations.d.ts +145 -0
- package/dist/workspace-operations.d.ts.map +1 -0
- package/dist/workspace-operations.js +420 -0
- package/dist/workspace-operations.js.map +1 -0
- package/dist/workspace-runtime.d.ts +110 -0
- package/dist/workspace-runtime.d.ts.map +1 -0
- package/dist/workspace-runtime.js +240 -0
- package/dist/workspace-runtime.js.map +1 -0
- package/dist/workspace-search.d.ts +55 -0
- package/dist/workspace-search.d.ts.map +1 -0
- package/dist/workspace-search.js +262 -0
- package/dist/workspace-search.js.map +1 -0
- package/dist/workspace-service.d.ts +173 -0
- package/dist/workspace-service.d.ts.map +1 -0
- package/dist/workspace-service.js +619 -0
- package/dist/workspace-service.js.map +1 -0
- package/dist/workspace.d.ts +7 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/workspace.js +7 -0
- package/dist/workspace.js.map +1 -0
- package/package.json +7 -4
package/dist/connections.js
CHANGED
|
@@ -1,16 +1,27 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
|
-
import { ConnectionAuthorizationFailedError, ConnectionAuthorizationRequiredError, isConnectionAuthorizationFailedError, isConnectionAuthorizationRequiredError } from "@assemblyline-agents/core";
|
|
3
|
+
import { ConnectionAuthorizationFailedError, ConnectionAuthorizationRequiredError, isConnectionAuthorizationFailedError, isConnectionAuthorizationRequiredError, resolveSandboxPath, shellQuote } from "@assemblyline-agents/core";
|
|
4
4
|
import { OAUTH_STANDARD_TOKEN_FIELDS, buildOAuthChallenge, connectionPrincipalFor, connectionPrincipalKey, grantToTokenResult, isTokenExpired, normalizeAuthorization, randomUrlToken, resolveHeadersDefinition, storedGrantExpired, tokenCacheKey } from "./connection-auth-util.js";
|
|
5
|
-
import { HttpApiConnectionClient, McpConnectionClient, OpenApiConnectionClient, RelayMcpConnectionClient, readResponseBody } from "./connection-clients.js";
|
|
5
|
+
import { HttpApiConnectionClient, A2AConnectionClient, CredentialConnectionClient, McpConnectionClient, OpenApiConnectionClient, RelayMcpConnectionClient, SdkApiConnectionClient, readResponseBody } from "./connection-clients.js";
|
|
6
6
|
import { SandboxCliConnectionClient } from "./sandbox-cli-client.js";
|
|
7
|
+
import { audienceAllowsConnectionAccess } from "./audience.js";
|
|
7
8
|
import { InMemoryConnectionAuthorizationSessionStore, InMemoryConnectionGrantStore } from "./connection-stores.js";
|
|
8
|
-
import { connectionToolAccess, isLiveConnection, isRecord, liveDefinitionUrl, parseQualifiedName, scoreMatch, stringValue, timestamp, tokenize } from "./connection-util.js";
|
|
9
|
+
import { connectionToolAccess, connectionToolApproval, isLiveConnection, isRecord, liveDefinitionUrl, parseQualifiedName, scoreMatch, stringValue, timestamp, tokenize } from "./connection-util.js";
|
|
9
10
|
export * from "./connection-auth-util.js";
|
|
10
11
|
export * from "./connection-clients.js";
|
|
11
12
|
export * from "./connection-stores.js";
|
|
12
13
|
export * from "./connection-util.js";
|
|
13
14
|
export * from "./computer-relay-protocol.js";
|
|
15
|
+
export class ConnectionConfigurationError extends Error {
|
|
16
|
+
constructor(message) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.name = "ConnectionConfigurationError";
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export function isConnectionConfigurationError(error) {
|
|
22
|
+
return error instanceof ConnectionConfigurationError;
|
|
23
|
+
}
|
|
24
|
+
const MATERIALIZED_CREDENTIAL_ROOT = "/workspace/.assembly-line/credentials/";
|
|
14
25
|
// Compose the channel-visible response for a run parked on connection
|
|
15
26
|
// authorization. The generic "authorization required" string is useless to the
|
|
16
27
|
// human — the challenge URL is the thing they need to click.
|
|
@@ -34,23 +45,55 @@ export async function connectionAuthorizationPrompt(store, authorizationSessionI
|
|
|
34
45
|
export async function materializeConnectionCredentialsIntoSandbox(run, registry, sandbox, appendEvent) {
|
|
35
46
|
if (run.input.channel === "subagent" || typeof run.input.parentRunId === "string")
|
|
36
47
|
return;
|
|
37
|
-
const { materialized, pending } = await registry.materializeCredentials(
|
|
48
|
+
const { materialized, pending } = await registry.materializeCredentials({
|
|
49
|
+
id: run.id,
|
|
50
|
+
input: run.input
|
|
51
|
+
});
|
|
38
52
|
if (materialized.length === 0 && pending.length === 0)
|
|
39
53
|
return;
|
|
40
54
|
for (const credential of materialized) {
|
|
41
55
|
for (const file of credential.files) {
|
|
56
|
+
assertMaterializedCredentialPath(credential.connection, file.path);
|
|
42
57
|
await sandbox.writeFile(file.path, file.content);
|
|
43
|
-
|
|
58
|
+
const physicalPath = resolveSandboxPath(sandbox.root, file.path);
|
|
59
|
+
await sandbox.shell(`chmod ${file.mode.toString(8)} -- ${shellQuote(physicalPath)}`);
|
|
60
|
+
}
|
|
61
|
+
for (const command of credential.commands ?? []) {
|
|
62
|
+
await sandbox.shell(command);
|
|
44
63
|
}
|
|
45
64
|
}
|
|
46
65
|
await appendEvent("connections.credentials_materialized", {
|
|
47
66
|
materialized: materialized.map((credential) => ({
|
|
48
67
|
connection: credential.connection,
|
|
49
|
-
files: credential.files.map((file) => file.path)
|
|
68
|
+
files: credential.files.map((file) => file.path),
|
|
69
|
+
...(credential.audit ? { audit: credential.audit } : {})
|
|
50
70
|
})),
|
|
51
71
|
pending
|
|
52
72
|
});
|
|
53
73
|
}
|
|
74
|
+
function assertMaterializedCredentialPath(connectionName, path) {
|
|
75
|
+
const normalized = path.replace(/\\/g, "/").replace(/\/+/g, "/");
|
|
76
|
+
if (normalized.split("/").includes("..") || !normalized.startsWith(MATERIALIZED_CREDENTIAL_ROOT)) {
|
|
77
|
+
throw new ConnectionConfigurationError(`Connection ${connectionName} materialize files must be under ${MATERIALIZED_CREDENTIAL_ROOT}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function assertRedactedAuditMetadata(connectionName, audit) {
|
|
81
|
+
const visit = (value, path) => {
|
|
82
|
+
if (Array.isArray(value)) {
|
|
83
|
+
value.forEach((item, index) => visit(item, `${path}[${index}]`));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (!value || typeof value !== "object")
|
|
87
|
+
return;
|
|
88
|
+
for (const [key, item] of Object.entries(value)) {
|
|
89
|
+
if (/(?:token|secret|password|authorization|credential)/iu.test(key)) {
|
|
90
|
+
throw new ConnectionConfigurationError(`Connection ${connectionName} audit metadata contains forbidden secret-like key ${path}.${key}.`);
|
|
91
|
+
}
|
|
92
|
+
visit(item, `${path}.${key}`);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
visit(audit, "audit");
|
|
96
|
+
}
|
|
54
97
|
export const CONNECTION_PAIR_TOOL_NAME = "pair";
|
|
55
98
|
// The synthetic host-side pairing tool advertised for connections that define
|
|
56
99
|
// redeemPairingCode. The input field is named `secret` so tool-call event
|
|
@@ -60,7 +103,7 @@ function pairingToolItem(connection) {
|
|
|
60
103
|
connection: connection.name,
|
|
61
104
|
qualifiedName: `${connection.name}__${CONNECTION_PAIR_TOOL_NAME}`,
|
|
62
105
|
tool: CONNECTION_PAIR_TOOL_NAME,
|
|
63
|
-
description: `Pair ${connection.name} by redeeming a one-time pairing packet or claim code. When the user pastes binding/pairing instructions for ${connection.name}, call this with the FULL pasted text as \`secret\`; the host exchanges it and stores the credential durably.
|
|
106
|
+
description: `Pair ${connection.name} by redeeming a one-time pairing packet or claim code. When the user pastes binding/pairing instructions for ${connection.name}, call this with the FULL pasted text as \`secret\`; the host exchanges it and stores the credential durably. Only call this for a supplied packet; the tool's presence does not mean an existing grant is missing.`,
|
|
64
107
|
inputSchema: {
|
|
65
108
|
type: "object",
|
|
66
109
|
additionalProperties: false,
|
|
@@ -112,6 +155,46 @@ export class RuntimeConnectionRegistry {
|
|
|
112
155
|
.filter(isLiveConnection)
|
|
113
156
|
.map((connection) => connection.name);
|
|
114
157
|
}
|
|
158
|
+
/** Resolve the host-only event adapter without exposing it as a model tool. */
|
|
159
|
+
async eventBindingContext(connectionName) {
|
|
160
|
+
const compiled = this.options.connections.find((connection) => connection.name === connectionName);
|
|
161
|
+
if (!compiled || !isLiveConnection(compiled)) {
|
|
162
|
+
throw new ConnectionConfigurationError(`Connection ${connectionName} is not registered as a live connection.`);
|
|
163
|
+
}
|
|
164
|
+
const definition = (await this.resolveConnection(compiled)).definition;
|
|
165
|
+
if (!definition.events)
|
|
166
|
+
throw new ConnectionConfigurationError(`Connection ${connectionName} does not accept provider events.`);
|
|
167
|
+
const context = this.resolverContext();
|
|
168
|
+
let principal;
|
|
169
|
+
if (definition.events.scope === "connection") {
|
|
170
|
+
principal = { type: "app" };
|
|
171
|
+
}
|
|
172
|
+
else if (definition.auth) {
|
|
173
|
+
const authorization = await normalizeAuthorization(definition.auth, context);
|
|
174
|
+
principal = connectionPrincipalFor(compiled, authorization, context);
|
|
175
|
+
}
|
|
176
|
+
else if (compiled.subject === "user") {
|
|
177
|
+
const sessionPrincipal = context.session.principal;
|
|
178
|
+
if (!sessionPrincipal || sessionPrincipal.type !== "user") {
|
|
179
|
+
throw new ConnectionConfigurationError(`Connection ${connectionName} events require an authenticated user principal.`);
|
|
180
|
+
}
|
|
181
|
+
principal = sessionPrincipal;
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
principal = { type: "app" };
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
compiled,
|
|
188
|
+
events: definition.events,
|
|
189
|
+
principal,
|
|
190
|
+
principalKey: connectionPrincipalKey(principal),
|
|
191
|
+
getToken: async () => {
|
|
192
|
+
if (!definition.auth)
|
|
193
|
+
throw new ConnectionConfigurationError(`Connection ${connectionName} event adapter requested a token without connection auth.`);
|
|
194
|
+
return this.resolveToken(compiled, definition.auth, context, liveDefinitionUrl(definition));
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
}
|
|
115
198
|
async search(input, execution) {
|
|
116
199
|
return this.authorizationIntent.run("discover", () => this.withExecutionContext(execution, () => this.searchWithContext(input)));
|
|
117
200
|
}
|
|
@@ -122,15 +205,27 @@ export class RuntimeConnectionRegistry {
|
|
|
122
205
|
const queryTokens = tokenize(keywords);
|
|
123
206
|
const scored = [];
|
|
124
207
|
const failures = [];
|
|
208
|
+
const privateOnly = [];
|
|
125
209
|
// Pairing tools surface even when the connection cannot authenticate yet —
|
|
126
210
|
// redeeming a pasted packet is exactly how an unauthorized connection
|
|
127
211
|
// becomes authorized, so it must not hide behind the failure path.
|
|
128
212
|
const pairable = [];
|
|
129
213
|
for (const connection of target) {
|
|
130
|
-
|
|
131
|
-
|
|
214
|
+
// A personal connection stays registered in a shared run, but its tool
|
|
215
|
+
// catalog, pairing, and authorization state are private-surface only.
|
|
216
|
+
if (!this.connectionAccessible(connection)) {
|
|
217
|
+
privateOnly.push({
|
|
218
|
+
connection: connection.name,
|
|
219
|
+
description: `${connection.name} is registered. Its personal tools, pairing, and authorization status are available only on a private surface such as a direct message; this does not mean a durable grant is missing.`,
|
|
220
|
+
requiresPrivateAudience: true
|
|
221
|
+
});
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
132
224
|
let metadata;
|
|
133
225
|
try {
|
|
226
|
+
if (await this.supportsPairing(connection)) {
|
|
227
|
+
pairable.push(pairingToolItem(connection));
|
|
228
|
+
}
|
|
134
229
|
metadata = await this.getClient(connection.name).getToolMetadata();
|
|
135
230
|
}
|
|
136
231
|
catch (error) {
|
|
@@ -159,23 +254,52 @@ export class RuntimeConnectionRegistry {
|
|
|
159
254
|
scored.sort((a, b) => b.score - a.score);
|
|
160
255
|
const matches = scored.slice(0, limit).map((entry) => entry.item);
|
|
161
256
|
if (matches.length > 0)
|
|
162
|
-
return [...pairable, ...matches, ...failures];
|
|
257
|
+
return [...pairable, ...matches, ...privateOnly, ...failures];
|
|
258
|
+
const privateConnectionNames = new Set(privateOnly.map((item) => item.connection));
|
|
163
259
|
return [
|
|
164
260
|
...pairable,
|
|
261
|
+
...privateOnly,
|
|
165
262
|
...target.map((connection) => ({
|
|
166
263
|
connection: connection.name,
|
|
167
264
|
description: connection.description ?? `${connection.name} connection`
|
|
168
|
-
})),
|
|
265
|
+
})).filter((item) => !privateConnectionNames.has(item.connection)),
|
|
169
266
|
...failures
|
|
170
267
|
];
|
|
171
268
|
}
|
|
269
|
+
// Deliberately does not swallow load errors: a definition module that fails
|
|
270
|
+
// to resolve (for example a deployed artifact missing the connection's
|
|
271
|
+
// package) must surface the real cause instead of silently hiding the
|
|
272
|
+
// pairing tool that would otherwise be the recovery path.
|
|
172
273
|
async supportsPairing(connection) {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
274
|
+
return typeof (await this.resolveConnection(connection)).definition.redeemPairingCode === "function";
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Report which live connections can redeem a pairing code right now, and
|
|
278
|
+
* which cannot because their definition failed to resolve (missing package,
|
|
279
|
+
* unset env, platform mismatch). Backs the core `pair` tool: a connection
|
|
280
|
+
* that cannot even load must still be *diagnosable* from the model — the
|
|
281
|
+
* resolve failure is the answer the user needs, not a hidden tool.
|
|
282
|
+
*/
|
|
283
|
+
async pairingStatus() {
|
|
284
|
+
return this.authorizationIntent.run("discover", async () => {
|
|
285
|
+
const pairable = [];
|
|
286
|
+
const unavailable = [];
|
|
287
|
+
for (const connection of this.options.connections) {
|
|
288
|
+
if (!this.toolAllowed(connection))
|
|
289
|
+
continue;
|
|
290
|
+
try {
|
|
291
|
+
if (await this.supportsPairing(connection))
|
|
292
|
+
pairable.push(connection.name);
|
|
293
|
+
}
|
|
294
|
+
catch (error) {
|
|
295
|
+
unavailable.push({
|
|
296
|
+
connection: connection.name,
|
|
297
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return { pairable, unavailable };
|
|
302
|
+
});
|
|
179
303
|
}
|
|
180
304
|
/**
|
|
181
305
|
* Redeem a user-provided one-time pairing code host-side and persist the
|
|
@@ -187,6 +311,9 @@ export class RuntimeConnectionRegistry {
|
|
|
187
311
|
if (!compiled || !isLiveConnection(compiled)) {
|
|
188
312
|
throw new Error(`Connection ${connectionName} is not registered as a live connection.`);
|
|
189
313
|
}
|
|
314
|
+
if (!this.connectionAccessible(compiled)) {
|
|
315
|
+
throw new Error(`Connection ${connectionName} is personal and pairs only on a private surface such as a direct message.`);
|
|
316
|
+
}
|
|
190
317
|
const definition = (await this.resolveConnection(compiled)).definition;
|
|
191
318
|
const redeem = definition.redeemPairingCode;
|
|
192
319
|
if (!redeem)
|
|
@@ -213,6 +340,9 @@ export class RuntimeConnectionRegistry {
|
|
|
213
340
|
const parsed = parseQualifiedName(qualifiedName);
|
|
214
341
|
if (!parsed)
|
|
215
342
|
return undefined;
|
|
343
|
+
const compiled = this.options.connections.find((connection) => connection.name === parsed.connectionName);
|
|
344
|
+
if (!compiled || !this.toolAllowed(compiled))
|
|
345
|
+
return undefined;
|
|
216
346
|
const metadata = await this.getClient(parsed.connectionName).getToolMetadata();
|
|
217
347
|
return metadata.find((tool) => tool.name === parsed.toolName);
|
|
218
348
|
});
|
|
@@ -221,11 +351,14 @@ export class RuntimeConnectionRegistry {
|
|
|
221
351
|
const parsed = parseQualifiedName(qualifiedName);
|
|
222
352
|
if (!parsed)
|
|
223
353
|
throw new Error(`Invalid connection tool name: ${qualifiedName}`);
|
|
354
|
+
const compiled = this.options.connections.find((connection) => connection.name === parsed.connectionName);
|
|
355
|
+
if (!compiled || !this.toolAllowed(compiled)) {
|
|
356
|
+
throw new Error(`Connection tool ${qualifiedName} is unavailable here: personal connections work only on a private surface such as a direct message.`);
|
|
357
|
+
}
|
|
224
358
|
// "<connection>__pair" is the synthetic host-side pairing tool; it must
|
|
225
359
|
// run before any client/auth resolution (the whole point is that the
|
|
226
360
|
// connection is not authorized yet).
|
|
227
361
|
if (parsed.toolName === CONNECTION_PAIR_TOOL_NAME) {
|
|
228
|
-
const compiled = this.options.connections.find((connection) => connection.name === parsed.connectionName);
|
|
229
362
|
if (compiled && await this.supportsPairing(compiled)) {
|
|
230
363
|
const secret = isRecord(input) && typeof input.secret === "string" ? input.secret : "";
|
|
231
364
|
return this.withExecutionContext(execution, () => this.redeemPairingCode(parsed.connectionName, secret));
|
|
@@ -237,12 +370,27 @@ export class RuntimeConnectionRegistry {
|
|
|
237
370
|
const compiled = this.options.connections.find((connection) => connection.name === connectionName);
|
|
238
371
|
if (!compiled || !isLiveConnection(compiled))
|
|
239
372
|
return undefined;
|
|
373
|
+
if (!this.toolAllowed(compiled)) {
|
|
374
|
+
throw new Error(`Connection tool ${connectionName}__${toolName} is unavailable here: personal connections work only on a private surface such as a direct message.`);
|
|
375
|
+
}
|
|
376
|
+
// Pairing is a synthetic host-side bootstrap operation, not a provider
|
|
377
|
+
// tool. It must bypass the provider connection's ordinary read/write
|
|
378
|
+
// classification or discovery will advertise a tool that execution then
|
|
379
|
+
// rejects before the redeemer can run.
|
|
380
|
+
if (toolName === CONNECTION_PAIR_TOOL_NAME) {
|
|
381
|
+
if (!await this.supportsPairing(compiled)) {
|
|
382
|
+
throw new Error(`Connection ${connectionName} does not support pairing codes.`);
|
|
383
|
+
}
|
|
384
|
+
return undefined;
|
|
385
|
+
}
|
|
240
386
|
const resolved = await this.resolveConnection(compiled);
|
|
387
|
+
if (!("access" in resolved.definition) || !resolved.definition.access)
|
|
388
|
+
return undefined;
|
|
241
389
|
const access = connectionToolAccess(toolName, resolved.definition.access);
|
|
242
390
|
if (access === "none")
|
|
243
391
|
throw new Error(`Tool ${toolName} is not allowed by connection ${connectionName}.`);
|
|
244
392
|
if (access === "write" && resolved.definition.access.write !== false) {
|
|
245
|
-
return resolved.definition.access
|
|
393
|
+
return connectionToolApproval(toolName, resolved.definition.access);
|
|
246
394
|
}
|
|
247
395
|
return resolved.definition.approval;
|
|
248
396
|
}
|
|
@@ -252,18 +400,24 @@ export class RuntimeConnectionRegistry {
|
|
|
252
400
|
return existing;
|
|
253
401
|
const compiled = this.options.connections.find((connection) => connection.name === connectionName);
|
|
254
402
|
if (!compiled || !isLiveConnection(compiled)) {
|
|
255
|
-
throw new
|
|
403
|
+
throw new ConnectionConfigurationError(`Connection ${connectionName} is not registered as a live connection.`);
|
|
256
404
|
}
|
|
257
405
|
const resolved = () => this.resolveConnection(compiled);
|
|
258
|
-
const client = compiled.protocol === "
|
|
259
|
-
? new
|
|
260
|
-
: compiled.protocol === "
|
|
261
|
-
? compiled
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
:
|
|
406
|
+
const client = compiled.protocol === "credential"
|
|
407
|
+
? new CredentialConnectionClient()
|
|
408
|
+
: compiled.protocol === "cli"
|
|
409
|
+
? new SandboxCliConnectionClient(compiled, resolved, this)
|
|
410
|
+
: compiled.protocol === "mcp"
|
|
411
|
+
? compiled.transport === "relay"
|
|
412
|
+
? new RelayMcpConnectionClient(compiled, resolved, this)
|
|
413
|
+
: new McpConnectionClient(compiled, resolved, this)
|
|
414
|
+
: compiled.protocol === "openapi"
|
|
415
|
+
? new OpenApiConnectionClient(compiled, resolved, this)
|
|
416
|
+
: compiled.protocol === "sdk"
|
|
417
|
+
? new SdkApiConnectionClient(compiled, resolved)
|
|
418
|
+
: compiled.protocol === "a2a"
|
|
419
|
+
? new A2AConnectionClient(compiled, resolved, this)
|
|
420
|
+
: new HttpApiConnectionClient(compiled, resolved, this);
|
|
267
421
|
this.clients.set(connectionName, client);
|
|
268
422
|
return client;
|
|
269
423
|
}
|
|
@@ -338,32 +492,38 @@ export class RuntimeConnectionRegistry {
|
|
|
338
492
|
* that persists the rotated grant to the grant store BEFORE anything is
|
|
339
493
|
* rendered — so the sandbox only ever sees the short-lived access token.
|
|
340
494
|
*
|
|
341
|
-
* A connection that cannot resolve
|
|
342
|
-
*
|
|
343
|
-
*
|
|
495
|
+
* A normal connection that cannot resolve is reported as pending. An
|
|
496
|
+
* required or explicitly requested credential-protocol connection throws
|
|
497
|
+
* instead, so a run can never continue after a missing, revoked, or
|
|
498
|
+
* mismatched sandbox credential.
|
|
344
499
|
*/
|
|
345
|
-
async materializeCredentials() {
|
|
346
|
-
return this.authorizationIntent.run("discover", () => this.materializeCredentialsWithContext());
|
|
500
|
+
async materializeCredentials(run) {
|
|
501
|
+
return this.authorizationIntent.run("discover", () => this.materializeCredentialsWithContext(run));
|
|
347
502
|
}
|
|
348
|
-
async materializeCredentialsWithContext() {
|
|
503
|
+
async materializeCredentialsWithContext(run) {
|
|
349
504
|
const materialized = [];
|
|
350
505
|
const pending = [];
|
|
351
506
|
const context = this.resolverContext();
|
|
352
507
|
for (const compiled of this.options.connections) {
|
|
353
|
-
if (!
|
|
508
|
+
if (!this.toolAllowed(compiled))
|
|
354
509
|
continue;
|
|
510
|
+
const failClosed = compiled.protocol === "credential"
|
|
511
|
+
&& (compiled.required || sandboxCredentialWasRequested(run, compiled.name));
|
|
355
512
|
let definition;
|
|
356
513
|
try {
|
|
357
514
|
definition = (await this.resolveConnection(compiled)).definition;
|
|
358
515
|
}
|
|
359
516
|
catch (error) {
|
|
517
|
+
if (failClosed)
|
|
518
|
+
throw error;
|
|
360
519
|
pending.push({ connection: compiled.name, reason: error instanceof Error ? error.message : String(error) });
|
|
361
520
|
continue;
|
|
362
521
|
}
|
|
363
522
|
const spec = definition.materialize;
|
|
364
|
-
if (!spec || spec.files
|
|
523
|
+
if (!spec || ((spec.files?.length ?? 0) === 0 && !spec.resolve))
|
|
365
524
|
continue;
|
|
366
|
-
const
|
|
525
|
+
const staticFiles = spec.files ?? [];
|
|
526
|
+
const needsToken = staticFiles.some((file) => templateUsesToken(file.path) || templateUsesToken(file.content));
|
|
367
527
|
let resolvedToken = { token: "" };
|
|
368
528
|
if (needsToken) {
|
|
369
529
|
if (!definition.auth) {
|
|
@@ -374,6 +534,64 @@ export class RuntimeConnectionRegistry {
|
|
|
374
534
|
resolvedToken = await this.resolveToken(compiled, definition.auth, context, liveDefinitionUrl(definition));
|
|
375
535
|
}
|
|
376
536
|
catch (error) {
|
|
537
|
+
if (failClosed)
|
|
538
|
+
throw error;
|
|
539
|
+
if (isConnectionAuthorizationRequiredError(error)) {
|
|
540
|
+
const authorizeUrl = typeof error.challenge?.url === "string" ? error.challenge.url : undefined;
|
|
541
|
+
pending.push({ connection: compiled.name, reason: `authorization_required: ${error.reason}`, ...(authorizeUrl ? { authorizeUrl } : {}) });
|
|
542
|
+
continue;
|
|
543
|
+
}
|
|
544
|
+
const reason = isConnectionAuthorizationFailedError(error)
|
|
545
|
+
? `${error.reason}: ${error.message}`
|
|
546
|
+
: error instanceof Error
|
|
547
|
+
? error.message
|
|
548
|
+
: String(error);
|
|
549
|
+
pending.push({ connection: compiled.name, reason });
|
|
550
|
+
continue;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
let files = staticFiles;
|
|
554
|
+
let commands = [];
|
|
555
|
+
let audit;
|
|
556
|
+
if (spec.resolve) {
|
|
557
|
+
if (!run) {
|
|
558
|
+
pending.push({ connection: compiled.name, reason: "run_context_required" });
|
|
559
|
+
continue;
|
|
560
|
+
}
|
|
561
|
+
let lazyToken;
|
|
562
|
+
try {
|
|
563
|
+
const authorization = definition.auth
|
|
564
|
+
? await normalizeAuthorization(definition.auth, context)
|
|
565
|
+
: undefined;
|
|
566
|
+
const principal = authorization
|
|
567
|
+
? connectionPrincipalFor(compiled, authorization, context)
|
|
568
|
+
: { type: "app" };
|
|
569
|
+
const resolved = await spec.resolve({
|
|
570
|
+
connectionName: compiled.name,
|
|
571
|
+
agentId: this.options.agentId ?? "",
|
|
572
|
+
run,
|
|
573
|
+
principal,
|
|
574
|
+
session: context.session,
|
|
575
|
+
env: this.options.env,
|
|
576
|
+
fetch: this.options.fetch,
|
|
577
|
+
getToken: async () => {
|
|
578
|
+
if (!definition.auth)
|
|
579
|
+
throw new Error(`Connection ${compiled.name} materialization requested a token without auth.`);
|
|
580
|
+
lazyToken ??= this.resolveToken(compiled, definition.auth, context, liveDefinitionUrl(definition));
|
|
581
|
+
return lazyToken;
|
|
582
|
+
}
|
|
583
|
+
});
|
|
584
|
+
if (!resolved)
|
|
585
|
+
continue;
|
|
586
|
+
files = resolved.files;
|
|
587
|
+
commands = resolved.commands ?? [];
|
|
588
|
+
audit = resolved.audit;
|
|
589
|
+
if (audit)
|
|
590
|
+
assertRedactedAuditMetadata(compiled.name, audit);
|
|
591
|
+
}
|
|
592
|
+
catch (error) {
|
|
593
|
+
if (failClosed)
|
|
594
|
+
throw error;
|
|
377
595
|
if (isConnectionAuthorizationRequiredError(error)) {
|
|
378
596
|
const authorizeUrl = typeof error.challenge?.url === "string" ? error.challenge.url : undefined;
|
|
379
597
|
pending.push({ connection: compiled.name, reason: `authorization_required: ${error.reason}`, ...(authorizeUrl ? { authorizeUrl } : {}) });
|
|
@@ -400,14 +618,18 @@ export class RuntimeConnectionRegistry {
|
|
|
400
618
|
try {
|
|
401
619
|
materialized.push({
|
|
402
620
|
connection: compiled.name,
|
|
403
|
-
files:
|
|
621
|
+
files: files.map((file) => ({
|
|
404
622
|
path: renderCredentialTemplate(file.path, values, compiled.name),
|
|
405
623
|
content: renderCredentialTemplate(file.content, values, compiled.name),
|
|
406
624
|
mode: file.mode ?? 0o600
|
|
407
|
-
}))
|
|
625
|
+
})),
|
|
626
|
+
...(commands.length > 0 ? { commands } : {}),
|
|
627
|
+
...(audit ? { audit } : {})
|
|
408
628
|
});
|
|
409
629
|
}
|
|
410
630
|
catch (error) {
|
|
631
|
+
if (failClosed)
|
|
632
|
+
throw error;
|
|
411
633
|
pending.push({ connection: compiled.name, reason: error instanceof Error ? error.message : String(error) });
|
|
412
634
|
}
|
|
413
635
|
}
|
|
@@ -435,6 +657,11 @@ export class RuntimeConnectionRegistry {
|
|
|
435
657
|
const session = await this.authorizationSessionStore.getSessionByState(state);
|
|
436
658
|
if (!session)
|
|
437
659
|
throw new ConnectionAuthorizationFailedError("unknown", { reason: "unknown_state", retryable: false });
|
|
660
|
+
// Provider callbacks are routinely retried. Once the grant was saved and
|
|
661
|
+
// the session completed, return the durable result without attempting to
|
|
662
|
+
// exchange the one-time authorization code again.
|
|
663
|
+
if (session.status === "completed")
|
|
664
|
+
return session;
|
|
438
665
|
if (session.expiresAt <= Date.now()) {
|
|
439
666
|
await this.authorizationSessionStore.updateSession(session.id, { status: "expired" });
|
|
440
667
|
throw new ConnectionAuthorizationFailedError(session.connectionName, { reason: "authorization_expired", retryable: true });
|
|
@@ -455,7 +682,11 @@ export class RuntimeConnectionRegistry {
|
|
|
455
682
|
if (!resolved.definition.auth) {
|
|
456
683
|
throw new ConnectionAuthorizationFailedError(session.connectionName, { reason: "auth_not_configured", retryable: false });
|
|
457
684
|
}
|
|
458
|
-
const authorization = await normalizeAuthorization(resolved.definition.auth, {
|
|
685
|
+
const authorization = await normalizeAuthorization(resolved.definition.auth, {
|
|
686
|
+
env: this.options.env,
|
|
687
|
+
fetch: this.options.fetch,
|
|
688
|
+
session: session.session
|
|
689
|
+
});
|
|
459
690
|
const connectionContext = { url: compiled.url ?? "" };
|
|
460
691
|
let token;
|
|
461
692
|
if (authorization.kind === "oauth" && authorization.oauth) {
|
|
@@ -544,6 +775,15 @@ export class RuntimeConnectionRegistry {
|
|
|
544
775
|
throw new Error(`OpenAPI connection ${compiled.name} must export description.`);
|
|
545
776
|
return { compiled, definition: raw };
|
|
546
777
|
}
|
|
778
|
+
if (compiled.protocol === "a2a") {
|
|
779
|
+
if (typeof raw.agentCardUrl !== "string")
|
|
780
|
+
throw new Error(`A2A connection ${compiled.name} must export agentCardUrl.`);
|
|
781
|
+
if (typeof raw.description !== "string")
|
|
782
|
+
throw new Error(`A2A connection ${compiled.name} must export description.`);
|
|
783
|
+
if (!raw.access || typeof raw.access !== "object")
|
|
784
|
+
throw new Error(`A2A connection ${compiled.name} must export access.`);
|
|
785
|
+
return { compiled, definition: raw };
|
|
786
|
+
}
|
|
547
787
|
if (compiled.protocol === "http") {
|
|
548
788
|
if (typeof raw.baseUrl !== "string")
|
|
549
789
|
throw new Error(`HTTP API connection ${compiled.name} must export baseUrl.`);
|
|
@@ -553,6 +793,18 @@ export class RuntimeConnectionRegistry {
|
|
|
553
793
|
throw new Error(`HTTP API connection ${compiled.name} must export operations.`);
|
|
554
794
|
return { compiled, definition: raw };
|
|
555
795
|
}
|
|
796
|
+
if (compiled.protocol === "sdk") {
|
|
797
|
+
if (compiled.source.path.startsWith("dynamic://")) {
|
|
798
|
+
throw new Error(`Dynamic SDK API connection ${compiled.name} is not allowed.`);
|
|
799
|
+
}
|
|
800
|
+
if (typeof raw.description !== "string" || !Array.isArray(raw.operations) || typeof raw.execute !== "function") {
|
|
801
|
+
throw new Error(`SDK API connection ${compiled.name} must export description, operations, and execute.`);
|
|
802
|
+
}
|
|
803
|
+
if (!raw.access || typeof raw.access !== "object") {
|
|
804
|
+
throw new Error(`SDK API connection ${compiled.name} must export access.`);
|
|
805
|
+
}
|
|
806
|
+
return { compiled, definition: raw };
|
|
807
|
+
}
|
|
556
808
|
if (compiled.protocol === "cli") {
|
|
557
809
|
if (compiled.source.path.startsWith("dynamic://")) {
|
|
558
810
|
throw new Error(`Dynamic CLI connection ${compiled.name} is not allowed.`);
|
|
@@ -565,6 +817,12 @@ export class RuntimeConnectionRegistry {
|
|
|
565
817
|
}
|
|
566
818
|
return { compiled, definition: raw };
|
|
567
819
|
}
|
|
820
|
+
if (compiled.protocol === "credential") {
|
|
821
|
+
if (typeof raw.description !== "string" || !raw.auth || !raw.materialize || typeof raw.materialize !== "object") {
|
|
822
|
+
throw new Error(`Credential connection ${compiled.name} must export description, auth, and materialize.`);
|
|
823
|
+
}
|
|
824
|
+
return { compiled, definition: raw };
|
|
825
|
+
}
|
|
568
826
|
throw new Error(`Connection ${compiled.name} is declaration-only.`);
|
|
569
827
|
}
|
|
570
828
|
targetConnections(name) {
|
|
@@ -574,6 +832,18 @@ export class RuntimeConnectionRegistry {
|
|
|
574
832
|
return !name || connection.name === name;
|
|
575
833
|
});
|
|
576
834
|
}
|
|
835
|
+
/**
|
|
836
|
+
* The single audience rule: personal (user-subject) connections exist only
|
|
837
|
+
* on private surfaces. Everything else — discovery, execution, pairing,
|
|
838
|
+
* and credential materialization — derives from this one predicate.
|
|
839
|
+
*/
|
|
840
|
+
connectionAccessible(connection) {
|
|
841
|
+
const audience = this.options.audience ?? { private: true };
|
|
842
|
+
return audienceAllowsConnectionAccess(audience, connection.subject);
|
|
843
|
+
}
|
|
844
|
+
toolAllowed(connection) {
|
|
845
|
+
return isLiveConnection(connection) && this.connectionAccessible(connection);
|
|
846
|
+
}
|
|
577
847
|
connectionFailure(connection, error) {
|
|
578
848
|
if (isConnectionAuthorizationRequiredError(error)) {
|
|
579
849
|
return {
|
|
@@ -605,14 +875,14 @@ export class RuntimeConnectionRegistry {
|
|
|
605
875
|
if (cached && !isTokenExpired(cached.token))
|
|
606
876
|
return cached.token;
|
|
607
877
|
const connectionContext = { url: resolvedUrl ?? connection.url ?? "" };
|
|
608
|
-
if (authorization.kind === "oauth") {
|
|
878
|
+
if (authorization.kind === "oauth" || authorization.kind === "interactive") {
|
|
609
879
|
const stored = await this.grantStore.getGrant(connection.name, principalKey);
|
|
610
880
|
if (stored && !storedGrantExpired(stored)) {
|
|
611
881
|
const token = grantToTokenResult(stored);
|
|
612
882
|
this.tokenCache.set(cacheKey, { token, authorization, principal, connection: connectionContext });
|
|
613
883
|
return token;
|
|
614
884
|
}
|
|
615
|
-
if (stored?.refreshToken && authorization.oauth) {
|
|
885
|
+
if (stored?.refreshToken && authorization.kind === "oauth" && authorization.oauth) {
|
|
616
886
|
const refreshed = await this.refreshOAuthGrant(authorization.oauth, connection.name, principal, principalKey, stored.refreshToken);
|
|
617
887
|
// saveGrantFromToken preserved the credential bag on the grant; mirror
|
|
618
888
|
// that on the returned token so template rendering sees it too.
|
|
@@ -621,7 +891,9 @@ export class RuntimeConnectionRegistry {
|
|
|
621
891
|
this.tokenCache.set(cacheKey, { token: refreshed, authorization, principal, connection: connectionContext });
|
|
622
892
|
return refreshed;
|
|
623
893
|
}
|
|
624
|
-
|
|
894
|
+
if (authorization.kind === "oauth") {
|
|
895
|
+
throw await this.createAuthorizationRequiredError(connection, authorization, principal, principalKey, context, connectionContext);
|
|
896
|
+
}
|
|
625
897
|
}
|
|
626
898
|
try {
|
|
627
899
|
const token = await authorization.getToken({ principal, connection: connectionContext });
|
|
@@ -643,6 +915,7 @@ export class RuntimeConnectionRegistry {
|
|
|
643
915
|
resolverContext() {
|
|
644
916
|
return {
|
|
645
917
|
env: this.options.env,
|
|
918
|
+
fetch: this.options.fetch,
|
|
646
919
|
session: this.options.session
|
|
647
920
|
};
|
|
648
921
|
}
|
|
@@ -719,7 +992,7 @@ export class RuntimeConnectionRegistry {
|
|
|
719
992
|
challenge = buildOAuthChallenge(authorization.oauth, oauthChallengeOptions);
|
|
720
993
|
}
|
|
721
994
|
else if (authorization.startAuthorization) {
|
|
722
|
-
const started = await authorization.startAuthorization({ principal, connection: connectionContext, callbackUrl });
|
|
995
|
+
const started = await authorization.startAuthorization({ principal, connection: connectionContext, callbackUrl, state });
|
|
723
996
|
challenge = started.challenge;
|
|
724
997
|
resume = started.resume;
|
|
725
998
|
if (!challenge.url && cause?.challenge?.url)
|
|
@@ -769,7 +1042,7 @@ export class RuntimeConnectionRegistry {
|
|
|
769
1042
|
return new ConnectionAuthorizationRequiredError(connection.name, errorOptions);
|
|
770
1043
|
}
|
|
771
1044
|
callbackUrl() {
|
|
772
|
-
const base = (this.options.callbackBaseUrl ?? this.options.env.
|
|
1045
|
+
const base = (this.options.callbackBaseUrl ?? this.options.env.ASSEMBLY_LINE_PUBLIC_URL ?? "http://localhost").replace(/\/+$/u, "");
|
|
773
1046
|
return `${base}/assembly-line/connections/callback`;
|
|
774
1047
|
}
|
|
775
1048
|
async exchangeOAuthAuthorizationCode(oauth, session, callback) {
|
|
@@ -796,9 +1069,21 @@ export class RuntimeConnectionRegistry {
|
|
|
796
1069
|
}
|
|
797
1070
|
async requestOAuthToken(oauth, fields) {
|
|
798
1071
|
const body = new URLSearchParams();
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
1072
|
+
const headers = {
|
|
1073
|
+
accept: "application/json",
|
|
1074
|
+
"content-type": "application/x-www-form-urlencoded"
|
|
1075
|
+
};
|
|
1076
|
+
if (oauth.tokenEndpointAuthMethod === "client_secret_basic") {
|
|
1077
|
+
if (!oauth.clientSecret) {
|
|
1078
|
+
throw new ConnectionAuthorizationFailedError("oauth", { reason: "missing_client_secret" });
|
|
1079
|
+
}
|
|
1080
|
+
headers.authorization = `Basic ${Buffer.from(`${oauth.clientId}:${oauth.clientSecret}`).toString("base64")}`;
|
|
1081
|
+
}
|
|
1082
|
+
else {
|
|
1083
|
+
body.set("client_id", oauth.clientId);
|
|
1084
|
+
if (oauth.clientSecret)
|
|
1085
|
+
body.set("client_secret", oauth.clientSecret);
|
|
1086
|
+
}
|
|
802
1087
|
for (const [key, value] of Object.entries(oauth.tokenParams ?? {}))
|
|
803
1088
|
body.set(key, value);
|
|
804
1089
|
for (const [key, value] of Object.entries(fields)) {
|
|
@@ -807,10 +1092,7 @@ export class RuntimeConnectionRegistry {
|
|
|
807
1092
|
}
|
|
808
1093
|
const response = await this.request(oauth.tokenUrl, {
|
|
809
1094
|
method: "POST",
|
|
810
|
-
headers
|
|
811
|
-
accept: "application/json",
|
|
812
|
-
"content-type": "application/x-www-form-urlencoded"
|
|
813
|
-
},
|
|
1095
|
+
headers,
|
|
814
1096
|
body
|
|
815
1097
|
});
|
|
816
1098
|
const payload = await readResponseBody(response);
|
|
@@ -879,4 +1161,11 @@ export class RuntimeConnectionRegistry {
|
|
|
879
1161
|
await this.grantStore.saveGrant(grant);
|
|
880
1162
|
}
|
|
881
1163
|
}
|
|
1164
|
+
function sandboxCredentialWasRequested(run, connectionName) {
|
|
1165
|
+
const requests = run?.input.sandboxCredentials;
|
|
1166
|
+
return Boolean(requests
|
|
1167
|
+
&& typeof requests === "object"
|
|
1168
|
+
&& !Array.isArray(requests)
|
|
1169
|
+
&& Object.prototype.hasOwnProperty.call(requests, connectionName));
|
|
1170
|
+
}
|
|
882
1171
|
//# sourceMappingURL=connections.js.map
|