eve 0.79.2 → 0.80.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/CHANGELOG.md +21 -0
- package/dist/src/compiled/.vendor-stamp.json +6 -6
- package/dist/src/compiled/@chat-adapter/slack/api.d.ts +6 -0
- package/dist/src/compiled/@chat-adapter/slack/api.js +1 -1
- package/dist/src/compiled/@chat-adapter/slack/blocks.js +1 -1
- package/dist/src/compiled/@chat-adapter/slack/format.js +1 -1
- package/dist/src/compiled/@chat-adapter/slack/index.d.ts +14 -8
- package/dist/src/compiled/@chat-adapter/slack/index.js +33 -32
- package/dist/src/compiled/@chat-adapter/twilio/index.d.ts +37 -1
- package/dist/src/compiled/@chat-adapter/twilio/index.js +2 -2
- package/dist/src/compiled/@linqapp/chat-sdk-adapter/index.js +1 -1
- package/dist/src/compiled/@photon-ai/chat-adapter-imessage/index.js +1 -1
- package/dist/src/compiled/@vercel/connect/index.js +5 -5
- package/dist/src/compiled/@vercel/connect/token.d.ts +11 -1
- package/dist/src/compiled/_chunks/node/chunk-NBSLXVG3-Bt7pDfyn.js +12 -0
- package/dist/src/compiled/_chunks/node/dist-BKsV21Vu.js +6 -0
- package/dist/src/compiled/_chunks/node/dist-CO_n_hUa.js +98 -0
- package/dist/src/compiled/chat/index.js +1 -1
- package/dist/src/compiler/extension-compatibility.d.ts +6 -6
- package/dist/src/compiler/extension-compatibility.js +1 -1
- package/dist/src/compiler/manifest.d.ts +8 -4
- package/dist/src/compiler/manifest.js +1 -1
- package/dist/src/compiler/normalize-connection.js +1 -1
- package/dist/src/compiler/normalize-manifest.js +1 -1
- package/dist/src/context/schedule-collection-tools.js +1 -1
- package/dist/src/execution/catalog/connection-auth.d.ts +4 -2
- package/dist/src/execution/catalog/connection-auth.js +1 -1
- package/dist/src/execution/catalog/connection-entry.js +1 -1
- package/dist/src/execution/dynamic-workflow/tool.js +1 -1
- package/dist/src/execution/session/entry.js +1 -1
- package/dist/src/execution/session-event-stream.js +1 -1
- package/dist/src/execution/stable-workflow-names.d.ts +2 -0
- package/dist/src/execution/stable-workflow-names.js +1 -1
- package/dist/src/execution/tools/agent-router-workflow.js +1 -1
- package/dist/src/execution/web-fetch/execute.js +1 -1
- package/dist/src/execution/workflow-runtime.d.ts +6 -0
- package/dist/src/execution/workflow-runtime.js +1 -1
- package/dist/src/extensions/git/extension/extension.d.ts +3 -1
- package/dist/src/extensions/git/extension/extension.js +1 -1
- package/dist/src/extensions/git/extension/lib/gh-tool.js +1 -1
- package/dist/src/extensions/git/extension/lib/github-shell.d.ts +14 -0
- package/dist/src/extensions/git/extension/lib/github-shell.js +1 -1
- package/dist/src/harness/model-call/call.js +1 -1
- package/dist/src/harness/model-call/errors.d.ts +2 -2
- package/dist/src/harness/model-call/recovery.d.ts +2 -3
- package/dist/src/instrumentation/session-init.d.ts +2 -1
- package/dist/src/instrumentation/session-init.js +1 -1
- package/dist/src/internal/application/stamped-package-version.js +1 -1
- package/dist/src/internal/authored-definition/connection.js +1 -1
- package/dist/src/internal/authored-definition/schedule-collection.js +1 -1
- package/dist/src/internal/nitro/host/application-route-registry.d.ts +7 -2
- package/dist/src/internal/nitro/host/application-route-registry.js +1 -1
- package/dist/src/internal/nitro/host/configure-nitro-routes.js +1 -1
- package/dist/src/internal/nitro/routes/channel-request-instrumentation.d.ts +3 -1
- package/dist/src/internal/nitro/routes/channel-request-instrumentation.js +1 -1
- package/dist/src/internal/nitro/routes/connection-events.d.ts +5 -0
- package/dist/src/internal/nitro/routes/connection-events.js +1 -0
- package/dist/src/internal/workflow-bundle/builder.js +1 -1
- package/dist/src/internal/workflow-bundle/dynamic-tool-ast-references.js +1 -1
- package/dist/src/public/channels/slack/blocks.js +2 -2
- package/dist/src/public/connections/index.d.ts +1 -0
- package/dist/src/public/definitions/connections/events.d.ts +43 -0
- package/dist/src/public/definitions/connections/events.js +1 -0
- package/dist/src/public/definitions/connections/mcp.d.ts +3 -0
- package/dist/src/public/schedules/subscription.d.ts +4 -3
- package/dist/src/runtime/connections/events/actions.d.ts +17 -0
- package/dist/src/runtime/connections/events/actions.js +1 -0
- package/dist/src/runtime/connections/events/delivery.d.ts +10 -0
- package/dist/src/runtime/connections/events/delivery.js +1 -0
- package/dist/src/runtime/connections/events/dispatch-step.d.ts +20 -0
- package/dist/src/runtime/connections/events/dispatch-step.js +1 -0
- package/dist/src/runtime/connections/events/lifecycle-step.d.ts +42 -0
- package/dist/src/runtime/connections/events/lifecycle-step.js +1 -0
- package/dist/src/runtime/connections/events/owner.d.ts +38 -0
- package/dist/src/runtime/connections/events/owner.js +1 -0
- package/dist/src/runtime/connections/events/path.d.ts +4 -0
- package/dist/src/runtime/connections/events/path.js +1 -0
- package/dist/src/runtime/connections/events/state.d.ts +37 -0
- package/dist/src/runtime/connections/events/state.js +1 -0
- package/dist/src/runtime/connections/events/workflow.d.ts +28 -0
- package/dist/src/runtime/connections/events/workflow.js +1 -0
- package/dist/src/runtime/connections/mcp-client.d.ts +4 -2
- package/dist/src/runtime/connections/mcp-client.js +1 -1
- package/dist/src/runtime/connections/openapi-client.js +1 -1
- package/dist/src/runtime/connections/scoped-authorization.js +1 -1
- package/dist/src/runtime/resolve-connection.js +1 -1
- package/dist/src/runtime/schedules/collection-client.d.ts +0 -2
- package/dist/src/runtime/schedules/collection-client.js +1 -1
- package/dist/src/runtime/types.d.ts +11 -0
- package/dist/src/setup/scaffold/create/add-to-project.js +1 -1
- package/dist/src/setup/scaffold/create/project.js +2 -2
- package/dist/src/setup/scaffold/update/channels.js +1 -1
- package/dist/src/setup/scaffold/update/connections.js +2 -2
- package/dist/src/setup/scaffold/version-tokens.d.ts +1 -1
- package/dist/src/setup/scaffold/version-tokens.js +1 -1
- package/dist/src/shared/agent-definition.d.ts +12 -4
- package/dist/src/shared/connection-events.d.ts +84 -0
- package/dist/src/shared/connection-events.js +1 -0
- package/dist/src/shared/connection-types.d.ts +10 -0
- package/dist/src/shared/validate-authorization.js +1 -1
- package/dist/src/tools/provided/ask-question-workflow.js +1 -1
- package/dist/src/tools/provided/no-reply.d.ts +5 -0
- package/dist/src/tools/provided/sleep-workflow.js +1 -1
- package/dist/src/tracing/eve/connection-events.d.ts +25 -0
- package/dist/src/tracing/eve/connection-events.js +1 -0
- package/dist/src/tracing/eve/install-instrumentation-runtime.js +1 -1
- package/dist/src/tracing/eve/mcp.d.ts +1 -0
- package/dist/src/tracing/eve/mcp.js +1 -1
- package/dist/src/tracing/eve/otel-declaration.d.ts +5 -0
- package/docs/agent-config.md +38 -13
- package/docs/code-extension.mdx +28 -0
- package/docs/concepts/built-in-tools.md +2 -0
- package/docs/connections/mcp.mdx +105 -0
- package/docs/guides/instrumentation/otel.mdx +10 -0
- package/docs/sandbox/vercel.mdx +46 -0
- package/package.json +7 -7
- package/dist/src/compiled/_chunks/node/chunk-6QHBXN7B-gl4n6nmi.js +0 -12
- package/dist/src/compiled/_chunks/node/dist-DYDVZ-FS.js +0 -98
- package/dist/src/compiled/_chunks/node/dist-iEFkK9qw.js +0 -6
|
@@ -10,3 +10,4 @@ export { defineMcpClientConnection, type McpClientConnectionDefinition, } from "
|
|
|
10
10
|
export type { ConnectionToolCallDefinition, ProvidedArgumentContext, ProvidedArgumentsDefinition, ProvidedArgumentValue, } from "#public/definitions/connections/tool-call.js";
|
|
11
11
|
export { defineOpenAPIConnection, type OpenAPIConnectionDefinition, type OpenAPISpecSource, } from "#public/definitions/connections/openapi.js";
|
|
12
12
|
export { type ConnectionAuthorizationChallenge, ConnectionAuthorizationFailedError, type ConnectionAuthorizationFailedErrorOptions, ConnectionAuthorizationRequiredError, type ConnectionAuthorizationRequiredErrorOptions, isConnectionAuthorizationFailedError, isConnectionAuthorizationRequiredError, } from "#public/connections/errors.js";
|
|
13
|
+
export type { Experimental_ConnectionEvents, Experimental_ConnectionEvent, Experimental_ConnectionEventOrigin, Experimental_ConnectionEventContext, } from "#public/definitions/connections/events.js";
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { Session } from "#channel/session.js";
|
|
2
|
+
import type { SessionAuthContext } from "#channel/types.js";
|
|
3
|
+
/** Framework-owned origin captured when the authorized caller subscribes. */
|
|
4
|
+
export interface Experimental_ConnectionEventOrigin {
|
|
5
|
+
readonly sessionId: string;
|
|
6
|
+
readonly connectionName: string;
|
|
7
|
+
readonly subscriptionId: string;
|
|
8
|
+
}
|
|
9
|
+
export interface Experimental_ConnectionEventContext {
|
|
10
|
+
readonly origin: Experimental_ConnectionEventOrigin;
|
|
11
|
+
readonly deliveryId: string;
|
|
12
|
+
/** The subscription creator's authenticated session identity. */
|
|
13
|
+
readonly auth: SessionAuthContext | null;
|
|
14
|
+
/** Attaches to an existing session; never creates a replacement session. */
|
|
15
|
+
readonly attachSession: (sessionId: string) => Session;
|
|
16
|
+
}
|
|
17
|
+
export interface Experimental_ConnectionEvent {
|
|
18
|
+
readonly eventId: string;
|
|
19
|
+
readonly name: string;
|
|
20
|
+
readonly timestamp: string;
|
|
21
|
+
readonly data: Readonly<Record<string, unknown>>;
|
|
22
|
+
readonly cursor: string | null;
|
|
23
|
+
}
|
|
24
|
+
/** Opts a static Connect-backed MCP connection into managed webhook events. */
|
|
25
|
+
export interface Experimental_ConnectionEvents {
|
|
26
|
+
/** Called durably after verification and subscription binding checks. */
|
|
27
|
+
readonly onEvent: (input: Experimental_ConnectionEventContext & {
|
|
28
|
+
readonly event: Experimental_ConnectionEvent;
|
|
29
|
+
}) => void | PromiseLike<void>;
|
|
30
|
+
/** The upstream server could not deliver a contiguous event stream. */
|
|
31
|
+
readonly onGap?: (input: Experimental_ConnectionEventContext & {
|
|
32
|
+
readonly cursor: string | null;
|
|
33
|
+
readonly truncated?: true;
|
|
34
|
+
}) => void | PromiseLike<void>;
|
|
35
|
+
/** The upstream subscription ended; no model turn starts automatically. */
|
|
36
|
+
readonly onTerminated?: (input: Experimental_ConnectionEventContext & {
|
|
37
|
+
readonly error: {
|
|
38
|
+
readonly code: number;
|
|
39
|
+
readonly message: string;
|
|
40
|
+
readonly data?: unknown;
|
|
41
|
+
};
|
|
42
|
+
}) => void | PromiseLike<void>;
|
|
43
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Experimental_ConnectionEvents } from "#public/definitions/connections/events.js";
|
|
1
2
|
import type { ConnectionAuthDefinition, HeadersDefinition, ToolFilterDefinition } from "#shared/connection-types.js";
|
|
2
3
|
import type { ConnectionToolCallDefinition } from "#public/definitions/connections/tool-call.js";
|
|
3
4
|
import type { Approval } from "#public/definitions/approval.js";
|
|
@@ -14,6 +15,8 @@ import type { Approval } from "#public/definitions/approval.js";
|
|
|
14
15
|
* servers that require no authentication (e.g. localhost).
|
|
15
16
|
*/
|
|
16
17
|
export interface McpClientConnectionDefinition {
|
|
18
|
+
/** Opt-in Connect webhook callbacks. Enabling this does not subscribe. */
|
|
19
|
+
readonly experimental_events?: Experimental_ConnectionEvents;
|
|
17
20
|
/**
|
|
18
21
|
* The MCP server's HTTP endpoint URL.
|
|
19
22
|
*
|
|
@@ -11,6 +11,7 @@ import type { InferReceiveTarget } from "#channel/receive-target.js";
|
|
|
11
11
|
import type { Session } from "#channel/session.js";
|
|
12
12
|
import { SCHEDULE_COLLECTION_DEFINITION_BRAND } from "#shared/schedule-collection-definition.js";
|
|
13
13
|
import type { ScheduleProviderContext } from "#runtime/schedules/provider-types.js";
|
|
14
|
+
import type { AgentToolExposure } from "#shared/agent-definition.js";
|
|
14
15
|
/** A provider namespace scope, represented by one key or a hierarchical key path. */
|
|
15
16
|
export type ScheduleScopeValue = string | readonly string[];
|
|
16
17
|
/** Return `null` to deny the current schedule operation. */
|
|
@@ -242,8 +243,8 @@ export interface DynamicSchedulesDefinition<TPayload = unknown, TSchema extends
|
|
|
242
243
|
/** On a permanent provider failure or after its retry budget is exhausted. */
|
|
243
244
|
readonly "occurrence.failed"?: ScheduleOccurrenceEventHandler<NoInfer<TPrepared>>;
|
|
244
245
|
};
|
|
245
|
-
/** Expose operation tools directly (default true),
|
|
246
|
-
readonly tool?:
|
|
246
|
+
/** Expose operation tools directly (default true), defer them to the catalog, or disable them. */
|
|
247
|
+
readonly tool?: AgentToolExposure;
|
|
247
248
|
/** Model-call policies keyed by operation. Create and update receive the prepared payload. */
|
|
248
249
|
readonly approval?: ScheduleApprovals<NoInfer<TPrepared>>;
|
|
249
250
|
}
|
|
@@ -257,7 +258,7 @@ type DynamicSchedulesOptions<TInput, TSchema extends StandardSchemaV1<unknown, T
|
|
|
257
258
|
readonly tool: false;
|
|
258
259
|
readonly approval?: never;
|
|
259
260
|
} | {
|
|
260
|
-
readonly tool?: true;
|
|
261
|
+
readonly tool?: true | "deferred";
|
|
261
262
|
readonly approval?: ScheduleApprovals<NoInfer<TPrepared>>;
|
|
262
263
|
});
|
|
263
264
|
/**
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ResolvedConnectionDefinition, ResolvedConnectionEvents } from "#runtime/types.js";
|
|
2
|
+
import type { ConnectionToolExecuteOptions, ConnectionToolMetadata } from "#shared/connection-types.js";
|
|
3
|
+
export declare function isEventAction(name: string): boolean;
|
|
4
|
+
export declare class ConnectionEventActions {
|
|
5
|
+
private readonly connection;
|
|
6
|
+
private readonly events;
|
|
7
|
+
private readonly headers;
|
|
8
|
+
private readonly transportFetch;
|
|
9
|
+
constructor(connection: ResolvedConnectionDefinition, events: ResolvedConnectionEvents, headers: () => Promise<Record<string, string>>, transportFetch?: typeof fetch);
|
|
10
|
+
private scope;
|
|
11
|
+
private withClient;
|
|
12
|
+
private catalog;
|
|
13
|
+
metadata(): Promise<ConnectionToolMetadata[]>;
|
|
14
|
+
/** Existing watches must remain stoppable when upstream discovery fails. */
|
|
15
|
+
recoveryMetadata(): ConnectionToolMetadata[];
|
|
16
|
+
execute(name: string, input: unknown, options: ConnectionToolExecuteOptions): Promise<unknown>;
|
|
17
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createHash}from"node:crypto";import{contextStorage}from"#context/container.js";import{AuthKey,SessionIdKey,SessionTraceSeedKey}from"#context/keys.js";import{defineJsonSchema,toInputSchema}from"#tools/schema.js";import{parseJsonObject}from"#shared/json.js";import{BundleKey}from"#runtime/sessions/runtime-context-keys.js";import{principalOf}from"#execution/session/principal.js";import{connectionEventDestination}from"#runtime/connections/events/path.js";import{withConnectionEventSpan}from"#tracing/eve/connection-events.js";import{createMcpTraceFetch}from"#runtime/connections/mcp-tracing.js";import{createMcpTracing}from"#tracing/eve/mcp.js";import{createMCPClient}from"#compiled/@ai-sdk/mcp/index.js";import{principalKey,resolveConnectionPrincipal}from"#runtime/connections/principal.js";import{sameEventOwner}from"#runtime/connections/events/state.js";const GET=`events_get_subscription`,STOP=`events_unsubscribe`,PREFIX=`events_subscribe_`,MIN_TTL_MS=12e4,MAX_TTL_MS=31536e6,SUBSCRIBE_INPUT_SCHEMA=defineJsonSchema(parseJsonObject(subscribeInputSchema()));function isEventAction(e){return e===GET||e===STOP||e.startsWith(PREFIX)}var ConnectionEventActions=class{connection;events;headers;transportFetch;constructor(e,t,n,r){this.connection=e,this.events=t,this.headers=n,this.transportFetch=r}scope(){let e=contextStorage.getStore();if(e===void 0)throw Error(`Event subscriptions require an active eve session.`);if(e.get(BundleKey)?.nodeId!==void 0)throw Error(`Experimental event subscriptions currently require a root-agent connection.`);let{authorization:t,backend:n,connector:r}=this.events,i=resolveConnectionPrincipal(this.connection.connectionName,t,e),a=e.require(SessionIdKey),o=principalKey(i),s=principalOf(e.get(AuthKey));return{ctx:e,principal:i,sessionId:a,owner:o,executionPrincipal:s,caller:{connectionName:this.connection.connectionName,instanceId:this.connection.instanceId,connector:r,principalKey:o,executionPrincipal:s},backend:n,connector:r}}async withClient(e){this.scope();let t,n={transport:{type:`http`,fetch:createMcpTraceFetch({connectionName:this.connection.connectionName,fetcher:this.transportFetch,getProtocolVersion:()=>t?.initializeResult?.protocolVersion}),url:this.connection.url,headers:await this.headers()},protocolVersionDiscovery:this.connection.protocolVersionDiscovery};t=await createMCPClient(n);try{return await e(t.experimental_events)}finally{await t.close()}}async catalog(e){let t=[],n=new Set,r,i=0;for(let a=0;a<20;a++){let a=await withConnectionEventSpan({ctx:this.scope().ctx,connectionName:this.connection.connectionName,operation:`catalog`},()=>createMcpTracing().list({connectionName:this.connection.connectionName,method:`events/list`,execute:()=>e.list({params:{cursor:r},options:{timeout:15e3}})}));if(i+=a.events.length,i>1e3)throw Error(`MCP event catalog exceeds the 1000-event limit.`);if(t.push(...a.events.filter(e=>e.delivery.includes(`webhook`))),r=a.nextCursor??void 0,r===void 0)return t;if(n.has(r))throw Error(`MCP event catalog returned a repeated cursor.`);n.add(r)}throw Error(`MCP event catalog exceeds the 20-page limit.`)}async metadata(){return this.withClient(async e=>[...(await this.catalog(e)).map(e=>({name:actionName(e.name),description:`Watch ${e.name}. ${e.description??``} Events reach this connection's authored callback for ttlMs milliseconds or until stopped. Set ttlMs to null to watch indefinitely. Watching the same event with the same arguments again returns the existing watch.`,inputSchema:subscribeInputSchema(e.inputSchema)})),...managementMetadata()])}recoveryMetadata(){return this.scope(),managementMetadata().map(e=>({...e,description:`MCP discovery is unavailable. ${e.description}`}))}async execute(e,t,n){let r=parseJsonObject(t),{ctx:i,principal:a,backend:o,sessionId:s,owner:c,executionPrincipal:l,caller:u}=this.scope(),{findConnectionEventOwner:d,startConnectionEventOwner:f,retireConnectionEventOwner:p}=await import(`#runtime/connections/events/owner.js`);if(e===GET||e===STOP){if(typeof r.id!=`string`)throw Error(`id must be a subscription id.`);let t=r.id,n=await d(t);if(n===void 0||!sameEventOwner(n.owner,u))throw Error(`Unknown subscription for this caller and account.`);if(e===STOP){if(!await p(t))throw Error(`Unknown subscription for this caller and account.`);return{id:t,status:`stopping`}}if(n.subscription===void 0)return pendingResult(n.owner);let s=n.subscription.id,c=await o.createAdapter({principal:a,connection:{url:this.connection.url},destination:{path:connectionEventDestination(this.connection.connectionName)}});return subscriptionResult(t,await withConnectionEventSpan({ctx:i,connectionName:this.connection.connectionName,bindingId:t,subscriptionId:s,operation:`get`},()=>c.getSubscription({id:s,options:{timeout:3e4}})))}return this.withClient(async t=>{let a=(await this.catalog(t)).find(t=>actionName(t.name)===e);if(a===void 0)throw Error(`Unknown event subscription action for this account.`);let o=await SUBSCRIBE_INPUT_SCHEMA[`~standard`].validate(r);if(o.issues!==void 0)throw Error(`Invalid subscription input: ${o.issues.map(({message:e})=>e).join(`; `)}`);let{arguments:d,ttlMs:p}=o.value,m=typeof a.inputSchema==`boolean`?{allOf:[a.inputSchema]}:a.inputSchema,h=await toInputSchema(m)[`~standard`].validate(parseJsonObject(d));if(h.issues!==void 0)throw Error(`Event arguments do not match the authorized catalog schema.`);let g=parseJsonObject(h.value),_=createHash(`sha256`).update(canonicalJson([s,this.connection.instanceId,this.connection.connectionName,c,l,a.name,g])).digest(`hex`),v={...u,id:_,sessionId:s,auth:structuredClone(i.get(AuthKey)??null),traceSeed:i.get(SessionTraceSeedKey),request:{name:a.name,arguments:structuredClone(g),ttlMs:p,context:{eve:{version:1,sessionId:s,bindingId:_}}}},y=await f(v,i.require(BundleKey),n.abortSignal);return y===void 0?pendingResult(v):subscriptionResult(_,y)})}};function subscribeInputSchema(e={type:`object`}){return{type:`object`,additionalProperties:!1,required:[`arguments`,`ttlMs`],properties:{arguments:e,ttlMs:{type:[`integer`,`null`],minimum:MIN_TTL_MS,maximum:MAX_TTL_MS,description:`How long to watch, in milliseconds (${MIN_TTL_MS} to ${MAX_TTL_MS}), or null until stopped`}}}}function subscriptionResult(e,t){return{...t,id:e,subscriptionId:t.id}}function pendingResult(e){let{name:t,arguments:n}=e.request;return{id:e.id,name:t,arguments:n,status:`pending`}}function canonicalJson(e){return JSON.stringify(e,(e,t)=>typeof t==`object`&&t&&!Array.isArray(t)?Object.fromEntries(Object.entries(t).sort(([e],[t])=>e<t?-1:1)):t)}function actionName(e){return`${PREFIX}${createHash(`sha256`).update(e).digest(`hex`).slice(0,24)}`}function managementMetadata(){return[GET,STOP].map(e=>({name:e,description:e===GET?`Get one of this caller's subscriptions, by the id its subscribe call returned, with Connect's current status.`:`Stop one of this caller's subscriptions, by the id its subscribe call returned. The watch then cancels upstream and ends; get reports it until it has.`,inputSchema:{type:`object`,properties:{id:{type:`string`}},required:[`id`],additionalProperties:!1}}))}export{ConnectionEventActions,isEventAction};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { SpanContext } from "#compiled/@opentelemetry/api/index.js";
|
|
2
|
+
import type { ConnectionEventDelivery } from "#shared/connection-events.js";
|
|
3
|
+
/** Only the verified receiver constructs this event-handler delivery. */
|
|
4
|
+
export interface ConnectionEventPayload {
|
|
5
|
+
readonly kind: "connection-event";
|
|
6
|
+
readonly connectionName: string;
|
|
7
|
+
readonly bindingId: string;
|
|
8
|
+
readonly requestTraceContext?: SpanContext;
|
|
9
|
+
readonly delivery: ConnectionEventDelivery;
|
|
10
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { EventBinding } from "#runtime/connections/events/state.js";
|
|
2
|
+
import type { ConnectionEventPayload } from "#runtime/connections/events/delivery.js";
|
|
3
|
+
import type { AlsContext } from "#context/container.js";
|
|
4
|
+
/** Re-checks the run's saved binding against the deployed connection before any work runs as its creator. */
|
|
5
|
+
export declare function resolveBinding(ctx: AlsContext, binding: EventBinding): {
|
|
6
|
+
binding: EventBinding;
|
|
7
|
+
connection: import("../../types.ts").ResolvedConnectionDefinition;
|
|
8
|
+
events: import("../../types.ts").ResolvedConnectionEvents;
|
|
9
|
+
principal: import("../../../shared/connection-types.ts").ConnectionPrincipal;
|
|
10
|
+
} | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* Runs one delivery's callback as the watch's creator. A crash before the
|
|
13
|
+
* step completes re-runs the callback, so callbacks are idempotent on
|
|
14
|
+
* deliveryId. A `terminated` delivery then retires the watch.
|
|
15
|
+
*/
|
|
16
|
+
export declare function dispatchConnectionEventStep(input: {
|
|
17
|
+
readonly serializedContext: Record<string, unknown>;
|
|
18
|
+
readonly binding: EventBinding;
|
|
19
|
+
readonly payload: ConnectionEventPayload;
|
|
20
|
+
}): Promise<void>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createAttachSessionFn}from"#channel/session.js";import{AuthKey}from"#context/keys.js";import{BundleKey}from"#runtime/sessions/runtime-context-keys.js";import{createWorkflowRuntime}from"#execution/workflow-runtime.js";import{deserializeContext}from"#context/serialize.js";import{withConnectionEventSpan}from"#tracing/eve/connection-events.js";import{retireConnectionEventOwner}from"#runtime/connections/events/owner.js";import{principalKey,resolveConnectionPrincipalFromAuth}from"#runtime/connections/principal.js";function resolveBinding(e,t){let n=e.require(BundleKey).resolvedAgent.connections.find(e=>e.connectionName===t.connectionName),r=n?.experimental_events;if(n===void 0||r===void 0||t.instanceId!==n.instanceId||r.connector!==t.connector)return;let i=resolveConnectionPrincipalFromAuth(t.connectionName,r.authorization,t.auth);if(principalKey(i)===t.principalKey)return{binding:t,connection:n,events:r,principal:i}}function accepts(e,t){let{delivery:n}=t;return t.connectionName===e.connectionName&&!(`event`in n&&n.event.name!==e.request.name)}async function dispatchConnectionEventStep(e){"use step";let t=await deserializeContext(e.serializedContext),{delivery:n}=e.payload,r=accepts(e.binding,e.payload)?resolveBinding(t,e.binding):void 0;if(r===void 0)return;let{binding:i,connection:a}=r,o=t.require(BundleKey),{callbacks:s}=r.events;t.setVirtualContext(AuthKey,i.auth);let c=`event`in n?`onEvent`:n.control.type===`gap`?`onGap`:`onTerminated`;await withConnectionEventSpan({ctx:t,connectionName:a.connectionName,operation:c,bindingId:i.id,subscriptionId:n.subscriptionId,deliveryId:n.deliveryId,requestTraceContext:e.payload.requestTraceContext},async e=>{let t={auth:i.auth,deliveryId:n.deliveryId,origin:{sessionId:i.sessionId,connectionName:i.connectionName,subscriptionId:n.subscriptionId},attachSession:createAttachSessionFn(createWorkflowRuntime({compiledArtifactsSource:o.compiledArtifactsSource}),{turnPolicy:`queue`,channelKind:`mcp-events`,channelName:i.connectionName,requestId:n.deliveryId,requestTraceContext:e})};`event`in n?await s.onEvent({...t,event:n.event}):n.control.type===`gap`?await s.onGap?.({...t,cursor:n.control.cursor,truncated:n.control.truncated}):await s.onTerminated?.({...t,error:n.control.error})}),`control`in n&&n.control.type===`terminated`&&await retireConnectionEventOwner(i.id)}export{dispatchConnectionEventStep,resolveBinding};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { type ConnectionEventSubscribeOutcome, type ConnectionEventSubscribeResult, type EventBinding } from "#runtime/connections/events/state.js";
|
|
2
|
+
import type { ConnectionEventPayload } from "#runtime/connections/events/delivery.js";
|
|
3
|
+
/**
|
|
4
|
+
* Subscribes from inside the watch run, after its hook is registered, so a
|
|
5
|
+
* delivery that beats the subscribe response finds the binding it runs under.
|
|
6
|
+
* The key is the run's own: a retried step reuses it, and a later run for the
|
|
7
|
+
* same watch, after this one ended, subscribes afresh.
|
|
8
|
+
*/
|
|
9
|
+
export declare function openConnectionEventStep(input: {
|
|
10
|
+
readonly serializedContext: Record<string, unknown>;
|
|
11
|
+
readonly binding: EventBinding;
|
|
12
|
+
}): Promise<ConnectionEventSubscribeResult>;
|
|
13
|
+
/**
|
|
14
|
+
* Connect's definitive refusals reach the model, and the run then ends
|
|
15
|
+
* without a watch. Anything else, such as a timeout after Connect may have
|
|
16
|
+
* created the subscription, throws so the step retries with the same
|
|
17
|
+
* idempotency key.
|
|
18
|
+
*/
|
|
19
|
+
export declare function subscribeConnectionEvent(serializedContext: Record<string, unknown>, binding: EventBinding, idempotencyKey: string): Promise<ConnectionEventSubscribeResult>;
|
|
20
|
+
/**
|
|
21
|
+
* One unsubscribe attempt for a stopped watch. "done" only once Connect has
|
|
22
|
+
* finished cleanup or no longer has the subscription. Anything else, whether
|
|
23
|
+
* Connect still cleaning up, an outage, refused credentials or a connection a
|
|
24
|
+
* redeploy removed, is "pending": the watch keeps its hook and repeats the
|
|
25
|
+
* attempt with its own durable backoff, so Workflow's step retries never
|
|
26
|
+
* decide when cleanup gives up.
|
|
27
|
+
*/
|
|
28
|
+
export declare function unsubscribeConnectionEventStep(input: {
|
|
29
|
+
readonly serializedContext: Record<string, unknown>;
|
|
30
|
+
readonly binding: EventBinding;
|
|
31
|
+
readonly subscriptionId: string;
|
|
32
|
+
}): Promise<"pending" | "done">;
|
|
33
|
+
/** Starts a delivery the watch's run took because it arrived before the subscribe response. */
|
|
34
|
+
export declare function startConnectionEventDeliveryStep(input: {
|
|
35
|
+
readonly binding: EventBinding;
|
|
36
|
+
readonly serializedContext: Record<string, unknown>;
|
|
37
|
+
readonly payload: ConnectionEventPayload;
|
|
38
|
+
}): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Publishes, for a duplicate run's caller, the run that owns the watch.
|
|
41
|
+
*/
|
|
42
|
+
export declare function publishConnectionEventOutcomeStep(outcome: ConnectionEventSubscribeOutcome): Promise<void>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createHash}from"node:crypto";import{getWorkflowMetadata,getWritable}from"#compiled/@workflow/core/index.js";import{deserializeContext}from"#context/serialize.js";import{readHttpStatus}from"#runtime/connections/mcp-client.js";import{connectionEventDestination}from"#runtime/connections/events/path.js";import{withConnectionEventSpan}from"#tracing/eve/connection-events.js";import{startConnectionEventDeliveryRun}from"#runtime/connections/events/owner.js";import{CONNECTION_EVENT_SUBSCRIPTION_NAMESPACE}from"#runtime/connections/events/state.js";import{resolveBinding}from"#runtime/connections/events/dispatch-step.js";async function openConnectionEventStep(e){"use step";let t=createHash(`sha256`).update(JSON.stringify([e.binding.id,getWorkflowMetadata().workflowRunId])).digest(`hex`),n=await subscribeConnectionEvent(e.serializedContext,e.binding,t);return await publishOutcome(n),n}async function subscribeConnectionEvent(e,t,n){let r=await deserializeContext(e),i=resolveBinding(r,t);if(i===void 0)return{error:`This connection no longer accepts the subscription's binding.`};let{connection:a}=i;try{let e=await withConnectionEventSpan({ctx:r,connectionName:a.connectionName,bindingId:t.id,operation:`subscribe`},async()=>(await createAdapter(i)).subscribe({...t.request,idempotencyKey:n,options:{timeout:3e4}}));return{subscription:structuredClone(e)}}catch(e){let t=readHttpStatus(e);if(t===void 0||!isRefusal(t))throw e;return{error:e instanceof Error?e.message:String(e),status:t}}}async function unsubscribeConnectionEventStep(e){"use step";let t=await deserializeContext(e.serializedContext),n=resolveBinding(t,e.binding);if(n===void 0)return`pending`;try{return(await withConnectionEventSpan({ctx:t,connectionName:n.connection.connectionName,bindingId:e.binding.id,subscriptionId:e.subscriptionId,operation:`unsubscribe`},async()=>(await createAdapter(n)).unsubscribe({id:e.subscriptionId,options:{timeout:3e4}}))).cleanupStatus===`pending`?`pending`:`done`}catch(e){let t=readHttpStatus(e);return t===404||t===410?`done`:`pending`}}async function startConnectionEventDeliveryStep(e){"use step";await startConnectionEventDeliveryRun(e)}function createAdapter({connection:e,principal:t,events:n}){return n.backend.createAdapter({principal:t,connection:{url:e.url},destination:{path:connectionEventDestination(e.connectionName)}})}function isRefusal(e){return e>=400&&e<500&&e!==408&&e!==429}async function publishConnectionEventOutcomeStep(e){"use step";await publishOutcome(e)}async function publishOutcome(e){let t=getWritable({namespace:CONNECTION_EVENT_SUBSCRIPTION_NAMESPACE}).getWriter();try{await t.write(e)}finally{t.releaseLock()}}export{openConnectionEventStep,publishConnectionEventOutcomeStep,startConnectionEventDeliveryStep,subscribeConnectionEvent,unsubscribeConnectionEventStep};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type CompiledBundle } from "#runtime/sessions/runtime-context-keys.js";
|
|
2
|
+
import type { ConnectionEventPayload } from "#runtime/connections/events/delivery.js";
|
|
3
|
+
import { type ConnectionEventSubscribeResult, type EventBinding } from "#runtime/connections/events/state.js";
|
|
4
|
+
import type { ManagedEventSubscription } from "#shared/connection-events.js";
|
|
5
|
+
/**
|
|
6
|
+
* Starts the watch's run, which subscribes once its hook is registered. A
|
|
7
|
+
* watch already running under the binding's id answers instead. Returns
|
|
8
|
+
* undefined when the subscription is still being created after the caller
|
|
9
|
+
* stops waiting: asking for the same watch again finds it.
|
|
10
|
+
*/
|
|
11
|
+
export declare function startConnectionEventOwner(binding: EventBinding, bundle: CompiledBundle, signal?: AbortSignal): Promise<ManagedEventSubscription | undefined>;
|
|
12
|
+
/**
|
|
13
|
+
* Routes one verified delivery to its watch. Returns false when the watch
|
|
14
|
+
* has ended or never created this subscription, so the receiver tells
|
|
15
|
+
* Connect to stop. A delivery that beats the subscribe response waits in the
|
|
16
|
+
* watch's own hook, and the watch's run starts it once it knows its
|
|
17
|
+
* subscription.
|
|
18
|
+
*/
|
|
19
|
+
export declare function startConnectionEventDelivery(payload: ConnectionEventPayload, bundle: CompiledBundle): Promise<boolean>;
|
|
20
|
+
/** Runs one delivery's callback in its own short run. */
|
|
21
|
+
export declare function startConnectionEventDeliveryRun(input: {
|
|
22
|
+
readonly binding: EventBinding;
|
|
23
|
+
readonly serializedContext: Record<string, unknown>;
|
|
24
|
+
readonly payload: ConnectionEventPayload;
|
|
25
|
+
}): Promise<void>;
|
|
26
|
+
/** Connect's refusal reaches the caller with its status, so a 401 is classified as a sign-in request. */
|
|
27
|
+
export declare function subscriptionFromOutcome(outcome: ConnectionEventSubscribeResult): ManagedEventSubscription;
|
|
28
|
+
/**
|
|
29
|
+
* A running watch: who owns it and what it subscribed to. The subscription
|
|
30
|
+
* is undefined while it is still being created. An ended watch's hook ended
|
|
31
|
+
* with its run, so it is not found.
|
|
32
|
+
*/
|
|
33
|
+
export declare function findConnectionEventOwner(bindingId: string): Promise<{
|
|
34
|
+
owner: EventBinding;
|
|
35
|
+
subscription: ManagedEventSubscription | undefined;
|
|
36
|
+
} | undefined>;
|
|
37
|
+
/** Asks the watch's run to unsubscribe and end; the run repeats unsubscribe until Connect finishes. */
|
|
38
|
+
export declare function retireConnectionEventOwner(bindingId: string): Promise<boolean>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{ContextContainer}from"#context/container.js";import{AuthKey,SessionIdKey,SessionTraceSeedKey}from"#context/keys.js";import{BundleKey}from"#runtime/sessions/runtime-context-keys.js";import{getHookByToken,getRun,resumeHook}from"#internal/workflow/runtime.js";import{connectionEventDeliveryWorkflowReference,connectionEventWorkflowReference,startWorkflowOnCurrentDeployment}from"#execution/workflow-runtime.js";import{serializeContext}from"#context/serialize.js";import{HookNotFoundError}from"#compiled/@workflow/errors/index.js";import{connectionEventHookToken}from"#runtime/connections/events/path.js";import{CONNECTION_EVENT_SUBSCRIPTION_NAMESPACE}from"#runtime/connections/events/state.js";function bindingContext(e,t){let n=new ContextContainer({localDevRequest:void 0});return n.set(BundleKey,t),n.set(AuthKey,e.auth),e.traceSeed!==void 0&&n.set(SessionTraceSeedKey,e.traceSeed),n.set(SessionIdKey,e.sessionId),serializeContext(n)}async function startConnectionEventOwner(e,t,n){let r=await readSubscribeOutcome((await startWorkflowOnCurrentDeployment(connectionEventWorkflowReference,[{binding:e,serializedContext:bindingContext(e,t)}])).runId,n);return r&&subscriptionFromOutcome(r)}async function startConnectionEventDelivery(e,t){let n=await findConnectionEventOwner(e.bindingId);if(n===void 0)return!1;if(n.subscription===void 0)return resumeWatch(e.bindingId,{kind:`delivery`,payload:e});if(n.subscription.id!==e.delivery.subscriptionId)return!1;let r=n.owner;return await startConnectionEventDeliveryRun({binding:r,serializedContext:bindingContext(r,t),payload:e}),!0}async function startConnectionEventDeliveryRun(e){await startWorkflowOnCurrentDeployment(connectionEventDeliveryWorkflowReference,[e])}function subscriptionFromOutcome(e){if(`error`in e)throw Object.assign(Error(e.error),{status:e.status});return e.subscription}async function findConnectionEventOwner(e){let t;try{t=await getHookByToken(connectionEventHookToken(e))}catch(e){if(HookNotFoundError.is(e))return;throw e}let n=await readPublishedOutcomeNow(t.runId);if(n===void 0||`subscription`in n)return{owner:await t.metadata,subscription:n?.subscription}}async function retireConnectionEventOwner(e){return resumeWatch(e,{kind:`retire`})}async function resumeWatch(e,t){try{return await resumeHook(connectionEventHookToken(e),t),!0}catch(e){if(HookNotFoundError.is(e))return!1;throw e}}async function readSubscribeOutcome(e,t){let n=await readPublishedOutcome(e,t);if(n===void 0||!(`ownerRunId`in n))return n;let r=await readPublishedOutcome(n.ownerRunId,t);if(r!==void 0&&`ownerRunId`in r)throw Error(`Event handler ownership did not resolve.`);return r}async function readPublishedOutcomeNow(e){let t=getRun(e).getReadable({namespace:CONNECTION_EVENT_SUBSCRIPTION_NAMESPACE}),n=await t.getTailIndex();return await t.cancel().catch(()=>{}),n<0?void 0:readPublishedOutcome(e)}async function readPublishedOutcome(e,t){let n=getRun(e).getReadable({namespace:CONNECTION_EVENT_SUBSCRIPTION_NAMESPACE}).getReader(),r=AbortSignal.any([AbortSignal.timeout(9e4),...t===void 0?[]:[t]]),i=new Promise(e=>{let abort=()=>e({value:void 0,abandoned:!0});r.aborted?abort():r.addEventListener(`abort`,abort,{once:!0})});try{let e=await Promise.race([n.read(),i]);if(`abandoned`in e)return;if(e.value===void 0)throw Error(`Event handler ended before subscribing.`);return e.value}finally{await n.cancel().catch(()=>{})}}export{findConnectionEventOwner,retireConnectionEventOwner,startConnectionEventDelivery,startConnectionEventDeliveryRun,startConnectionEventOwner,subscriptionFromOutcome};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function connectionEventRoute(connectionName: string): string;
|
|
2
|
+
export declare function connectionEventDestination(connectionName: string): string;
|
|
3
|
+
/** The watch run's hook: the receiver finds it by the binding id Connect echoes in context. */
|
|
4
|
+
export declare function connectionEventHookToken(bindingId: string): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{EVE_ROUTE_PREFIX}from"#protocol/routes.js";import{joinEveRoutePath}from"#shared/eve-route-path.js";import{EVE_PUBLIC_ROUTE_PREFIX_ENV,normalizePublicRoutePrefix}from"#shared/public-route-prefix.js";function connectionEventRoute(e){return`${EVE_ROUTE_PREFIX}/hooks/${encodeURIComponent(e)}`}function connectionEventDestination(e){return joinEveRoutePath(normalizePublicRoutePrefix(process.env[EVE_PUBLIC_ROUTE_PREFIX_ENV])??``,connectionEventRoute(e))}function connectionEventHookToken(e){return`eve:connection-event:${e}`}export{connectionEventDestination,connectionEventHookToken,connectionEventRoute};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { SessionAuthContext } from "#channel/types.js";
|
|
2
|
+
import type { SessionTraceSeed } from "#context/keys.js";
|
|
3
|
+
import type { ManagedEventSubscribeInput, ManagedEventSubscription } from "#shared/connection-events.js";
|
|
4
|
+
/** Who owns a watch. The handler's hook carries it so management can authorize callers. */
|
|
5
|
+
export type EventOwner = {
|
|
6
|
+
readonly connectionName: string;
|
|
7
|
+
readonly instanceId: string | undefined;
|
|
8
|
+
readonly connector: string;
|
|
9
|
+
readonly principalKey: string;
|
|
10
|
+
readonly executionPrincipal: string;
|
|
11
|
+
};
|
|
12
|
+
/** The handler run's input: everything a delivery needs, saved before subscribing. */
|
|
13
|
+
export interface EventBinding extends EventOwner {
|
|
14
|
+
readonly id: string;
|
|
15
|
+
readonly sessionId: string;
|
|
16
|
+
readonly auth: SessionAuthContext | null;
|
|
17
|
+
readonly traceSeed?: SessionTraceSeed;
|
|
18
|
+
/** The watch's own run adds the idempotency key, so each run subscribes once. */
|
|
19
|
+
readonly request: Omit<ManagedEventSubscribeInput, "idempotencyKey" | "options">;
|
|
20
|
+
}
|
|
21
|
+
/** The run publishes its subscription once, so callers outside it can read what it owns. */
|
|
22
|
+
export declare const CONNECTION_EVENT_SUBSCRIPTION_NAMESPACE = "eve.connection-event.subscription";
|
|
23
|
+
/** What a run's own subscribe produced. */
|
|
24
|
+
export type ConnectionEventSubscribeResult = {
|
|
25
|
+
readonly subscription: ManagedEventSubscription;
|
|
26
|
+
}
|
|
27
|
+
/** Connect refused the subscription; `status` keeps its HTTP status, such as a 401 that asks for sign-in. */
|
|
28
|
+
| {
|
|
29
|
+
readonly error: string;
|
|
30
|
+
readonly status?: number;
|
|
31
|
+
};
|
|
32
|
+
export type ConnectionEventSubscribeOutcome = ConnectionEventSubscribeResult
|
|
33
|
+
/** Another run already owns this binding: read the outcome from that run. */
|
|
34
|
+
| {
|
|
35
|
+
readonly ownerRunId: string;
|
|
36
|
+
};
|
|
37
|
+
export declare function sameEventOwner(left: EventOwner, right: EventOwner): boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const CONNECTION_EVENT_SUBSCRIPTION_NAMESPACE=`eve.connection-event.subscription`;function sameEventOwner(e,t){return e.connectionName===t.connectionName&&e.instanceId===t.instanceId&&e.connector===t.connector&&e.principalKey===t.principalKey&&e.executionPrincipal===t.executionPrincipal}export{CONNECTION_EVENT_SUBSCRIPTION_NAMESPACE,sameEventOwner};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ConnectionEventPayload } from "#runtime/connections/events/delivery.js";
|
|
2
|
+
import type { EventBinding } from "#runtime/connections/events/state.js";
|
|
3
|
+
export type ConnectionEventWatchMessage = {
|
|
4
|
+
readonly kind: "retire";
|
|
5
|
+
}
|
|
6
|
+
/** A delivery that reached the receiver before this run published its subscription. */
|
|
7
|
+
| {
|
|
8
|
+
readonly kind: "delivery";
|
|
9
|
+
readonly payload: ConnectionEventPayload;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Holds one watch: its hook carries the binding every delivery runs under,
|
|
13
|
+
* and the run waits until the watch is stopped, terminated or expires.
|
|
14
|
+
* Deliveries run in their own {@link connectionEventDeliveryWorkflow}, so
|
|
15
|
+
* this run's log only grows by the deliveries that arrive before its
|
|
16
|
+
* subscribe response does. The hook ends with the run: an ended watch is
|
|
17
|
+
* not found.
|
|
18
|
+
*/
|
|
19
|
+
export declare function connectionEventWorkflow(input: {
|
|
20
|
+
readonly binding: EventBinding;
|
|
21
|
+
readonly serializedContext: Record<string, unknown>;
|
|
22
|
+
}): Promise<void>;
|
|
23
|
+
/** Runs one delivery's callback in its own short run. */
|
|
24
|
+
export declare function connectionEventDeliveryWorkflow(input: {
|
|
25
|
+
readonly binding: EventBinding;
|
|
26
|
+
readonly serializedContext: Record<string, unknown>;
|
|
27
|
+
readonly payload: ConnectionEventPayload;
|
|
28
|
+
}): Promise<void>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{parseJsonObject}from"#shared/json.js";import{createHook,sleep}from"#compiled/@workflow/core/index.js";import{claimHookOwnership,disposeHook,isHookConflictError}from"#execution/hook-ownership.js";import{connectionEventHookToken}from"#runtime/connections/events/path.js";import{dispatchConnectionEventStep}from"#runtime/connections/events/dispatch-step.js";import{openConnectionEventStep,publishConnectionEventOutcomeStep,startConnectionEventDeliveryStep,unsubscribeConnectionEventStep}from"#runtime/connections/events/lifecycle-step.js";async function connectionEventWorkflow(e){"use workflow";let{binding:t,serializedContext:n}=e,r=createHook({token:connectionEventHookToken(t.id),metadata:parseJsonObject(t)});try{await claimHookOwnership(r)}catch(e){if(!isHookConflictError(e)||typeof e.conflictingRunId!=`string`)throw e;await publishConnectionEventOutcomeStep({ownerRunId:e.conflictingRunId});return}let i=await openConnectionEventStep({serializedContext:n,binding:t});if(`error`in i){await disposeHook(r);return}let{id:a,expiresAt:o}=i.subscription,s=o===null?void 0:sleep(new Date(o));for(;;){let e=await(s===void 0?r:Promise.race([r,s]));if(e===void 0)return;if(e.kind===`retire`)break;e.payload.delivery.subscriptionId===a&&await startConnectionEventDeliveryStep({binding:t,serializedContext:n,payload:e.payload})}let c=5e3;for(;await unsubscribeConnectionEventStep({serializedContext:n,binding:t,subscriptionId:a})===`pending`;)await sleep(c),c=Math.min(c*2,36e5)}async function connectionEventDeliveryWorkflow(e){"use workflow";await dispatchConnectionEventStep(e)}connectionEventWorkflow.workflowId=`workflow//eve//connectionEventWorkflow`,connectionEventDeliveryWorkflow.workflowId=`workflow//eve//connectionEventDeliveryWorkflow`;export{connectionEventDeliveryWorkflow,connectionEventWorkflow};
|
|
@@ -15,6 +15,7 @@ import type { ConnectionClient, ConnectionToolExecuteOptions, ConnectionToolMeta
|
|
|
15
15
|
*/
|
|
16
16
|
export declare class McpConnectionClient implements ConnectionClient {
|
|
17
17
|
#private;
|
|
18
|
+
readonly eventActions: ConnectionClient["eventActions"];
|
|
18
19
|
constructor(connection: ResolvedConnectionDefinition);
|
|
19
20
|
/**
|
|
20
21
|
* Connects to the MCP server, trying Streamable HTTP first and
|
|
@@ -25,8 +26,8 @@ export declare class McpConnectionClient implements ConnectionClient {
|
|
|
25
26
|
*/
|
|
26
27
|
connect(): Promise<MCPClient>;
|
|
27
28
|
/**
|
|
28
|
-
* Returns cached tool metadata for all tools this connection exposes
|
|
29
|
-
*
|
|
29
|
+
* Returns cached tool metadata for all tools this connection exposes. The
|
|
30
|
+
* catalog applies the connection's tool filter to the listing.
|
|
30
31
|
*
|
|
31
32
|
* The first call fetches tools from the server via `listTools()` and
|
|
32
33
|
* creates the AI SDK tool set in parallel. Subsequent calls return
|
|
@@ -66,6 +67,7 @@ export declare const MCP_WWW_AUTHENTICATE_META_KEY = "mcp/www_authenticate";
|
|
|
66
67
|
* tools without failing the whole HTTP request.
|
|
67
68
|
*/
|
|
68
69
|
export declare function isMcpAuthChallengeResult(result: unknown): boolean;
|
|
70
|
+
export declare function readHttpStatus(error: unknown): number | undefined;
|
|
69
71
|
/**
|
|
70
72
|
* Returns `true` when a tool name passes the configured filter.
|
|
71
73
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isObject}from"#shared/guards.js";import{buildCallbackContext}from"#context/build-callback-context.js";import{ConnectionAuthorizationRequiredError,isConnectionAuthorizationRequiredError}from"#connections/errors.js";import{resolveConnectionAuthorization}from"#runtime/connections/resolve-authorization.js";import{evictScopedToken,resolveScopedToken}from"#runtime/connections/scoped-authorization.js";import{FORWARDED_PRINCIPAL_HEADER,encodeForwardedPrincipalHeader}from"#internal/mcp/forwarded-principal-header.js";import{createMCPClient}from"#compiled/@ai-sdk/mcp/index.js";import{
|
|
1
|
+
import{isObject}from"#shared/guards.js";import{buildCallbackContext}from"#context/build-callback-context.js";import{ConnectionAuthorizationRequiredError,isConnectionAuthorizationRequiredError}from"#connections/errors.js";import{resolveConnectionAuthorization}from"#runtime/connections/resolve-authorization.js";import{evictScopedToken,resolveScopedToken}from"#runtime/connections/scoped-authorization.js";import{FORWARDED_PRINCIPAL_HEADER,encodeForwardedPrincipalHeader}from"#internal/mcp/forwarded-principal-header.js";import{createMcpTraceFetch,withMcpToolCallSpan,withMcpToolsListSpan}from"#runtime/connections/mcp-tracing.js";import{createMCPClient}from"#compiled/@ai-sdk/mcp/index.js";import{ConnectionEventActions,isEventAction}from"#runtime/connections/events/actions.js";import{readTurnPrincipals}from"#context/turn-principals.js";import{omitProvidedArgumentsFromSchema,resolveProvidedArguments}from"#runtime/connections/provided-arguments.js";var McpConnectionClient=class{#e;#t;#n;#r;#i;#a=!1;eventActions;constructor(e){if(this.#i=e,e.experimental_events!==void 0){let t=new ConnectionEventActions(e,e.experimental_events,()=>resolveHeaders(e),this.#s());this.eventActions={ownsName:isEventAction,recoveryMetadata:()=>t.recoveryMetadata(),metadata:async()=>{try{return await t.metadata()}catch(e){return await this.#d(e)}},execute:async(e,n,r)=>{try{return await t.execute(e,n,r)}catch(e){return await this.#d(e)}}}}}async connect(){if(this.#t!==void 0)return this.#t;if(this.#e!==void 0)return this.#e;this.#e=this.#o();try{return this.#t=await this.#e,this.#t}catch(e){throw this.#e=void 0,e}}async#o(){let{anonymous:e,headers:t}=await resolveConnectionHeaders(this.#i,{anonymousWhenAuthorizationRequired:!0});this.#a=e;let n=this.#i.url,r=createMcpTraceFetch({connectionName:this.#i.connectionName,fetcher:this.#s(),getProtocolVersion:()=>this.#t?.initializeResult?.protocolVersion});try{return await createMCPClient({protocolVersionDiscovery:this.#i.protocolVersionDiscovery,transport:{fetch:r,headers:t,type:`http`,url:n}})}catch(e){if(!isMcpHttpFallbackRetryableError(e))throw e;return await createMCPClient({protocolVersionDiscovery:this.#i.protocolVersionDiscovery,transport:{fetch:r,headers:t,type:`sse`,url:n}})}}#s(){if(this.#i.forwardPrincipal!==!0)return;let e=this.#i.connectionName;return async(t,n)=>{let r=readTurnPrincipals();if(r===void 0)return await globalThis.fetch(t,n);let i=new Headers(n?.headers);return i.set(FORWARDED_PRINCIPAL_HEADER,encodeForwardedPrincipalHeader(r,e)),await globalThis.fetch(t,{...n,headers:i,redirect:`error`})}}async getToolMetadata(){return(await this.#c()).metadata}async executeTool(e,t,n){let r;try{let{tools:i}=await this.#c(),a=i[e];if(a?.execute===void 0)throw Error(`Tool "${e}" not found in connection "${this.#i.connectionName}".`);let o=a.execute,s=await resolveProvidedArguments({args:t,callId:n.callId,connection:this.#i,toolName:e});r=await withMcpToolCallSpan({arguments:t,connectionName:this.#i.connectionName,execute:async()=>await o(s,{abortSignal:n.abortSignal}),protocolVersion:this.#t?.initializeResult?.protocolVersion,toolName:e})}catch(e){return await this.#d(e)}return isMcpAuthChallengeResult(r)?await this.#f(`the server asked for sign-in to call "${e}"`):r}async#c(){if(this.#r!==void 0)return this.#r;if(this.#n!==void 0)return this.#n;this.#n=this.#l();try{return this.#r=await this.#n,this.#r}catch(e){throw this.#n=void 0,e}}async#l(){try{return await this.#u()}catch(e){return await this.#d(e)}}async#u(){let e=await this.connect(),t=await withMcpToolsListSpan({connectionName:this.#i.connectionName,execute:()=>e.listTools(),protocolVersion:e.initializeResult?.protocolVersion}),n=Object.keys(this.#i.toolCall?.providedArguments??{}),r=t.tools.map(e=>({...e,inputSchema:omitProvidedArgumentsFromSchema(e.inputSchema,n)})),i=e.toolsFromDefinitions({tools:r});return{metadata:r.map(e=>({annotations:e.annotations,description:e.description??``,inputSchema:e.inputSchema??{},name:e.name,outputSchema:`outputSchema`in e&&e.outputSchema!==void 0?e.outputSchema:void 0})),tools:i}}async close(){this.#t!==void 0&&(await this.#t.close(),this.#t=void 0),this.#e=void 0,this.#n=void 0,this.#r=void 0,this.#a=!1}async#d(e){if(!isMcpAuthRequiredError(e))throw e;return await this.#f(this.#a?`the server requires sign-in`:`the server rejected the token`)}async#f(e){throw this.#a||await this.#p(),await this.close(),new ConnectionAuthorizationRequiredError(this.#i.connectionName,{message:`Connection "${this.#i.connectionName}" requires authorization (${e}).`})}async#p(){let e=await resolveConnectionAuthorization(this.#i);e!==void 0&&await evictScopedToken({authorization:e,connection:{url:this.#i.url},instanceId:this.#i.instanceId,scope:this.#i.connectionName})}};function isMcpAuthRequiredError(e){return readHttpStatus(e)===401}const MCP_WWW_AUTHENTICATE_META_KEY=`mcp/www_authenticate`;function isMcpAuthChallengeResult(e){if(!isObject(e)||e.isError!==!0||!isObject(e._meta))return!1;let t=e._meta[MCP_WWW_AUTHENTICATE_META_KEY];return(typeof t==`string`||Array.isArray(t))&&t.length>0}function isMcpHttpFallbackRetryableError(e){let t=readHttpStatus(e);return t===400||t===404||t===405}function readHttpStatus(e){for(let t of walkErrorChain(e)){if(!isObject(t))continue;let e=readStatusField(t);if(e!==void 0)return e;let n=t.response;if(isObject(n)){let e=readStatusField(n);if(e!==void 0)return e}if(typeof t.message==`string`){let e=/\bHTTP\s+(\d{3})\b/u.exec(t.message);if(e?.[1]!==void 0)return Number(e[1])}}}function readStatusField(e){if(typeof e.status==`number`)return e.status;if(typeof e.statusCode==`number`)return e.statusCode}function*walkErrorChain(e){let t=e,n=new Set;for(;t!=null&&!n.has(t);){if(n.add(t),yield t,!isObject(t)||!(`cause`in t))return;t=t.cause}}function passesToolFilter(e,t){return t===void 0?!0:`allow`in t?t.allow.includes(e):!t.block.includes(e)}async function resolveHeaders(e){let{headers:t}=await resolveConnectionHeaders(e,{anonymousWhenAuthorizationRequired:!1});return t}async function resolveConnectionHeaders(e,t){let n={},r=!1,i,getCallbackContext=()=>i??=buildCallbackContext(),a=await resolveConnectionAuthorization(e,typeof e.authorization==`function`?getCallbackContext():void 0);if(a!==void 0)try{n.Authorization=`Bearer ${(await resolveToken(e,a)).token}`}catch(e){if(!t.anonymousWhenAuthorizationRequired||!isConnectionAuthorizationRequiredError(e))throw e;r=!0}if(e.headers!==void 0){let t=await resolveHeadersDefinition(e.headers,getCallbackContext);for(let[r,i]of Object.entries(t)){if(a!==void 0&&r.toLowerCase()===`authorization`)throw Error(`Connection "${e.connectionName}" headers must not include an "Authorization" key when "authorization" is also provided.`);n[r]=i}}return{anonymous:r,headers:n}}async function resolveToken(e,t){return await resolveScopedToken({authorization:t,connection:{url:e.url},instanceId:e.instanceId,scope:e.connectionName})}async function resolveHeadersDefinition(e,t){if(typeof e==`function`)return{...await e(t())};let n={},r=Object.entries(e);for(let[e,i]of r)n[e]=await resolveHeaderValue(i,t);return n}async function resolveHeaderValue(e,t){return typeof e==`function`?await e(t()):await e}export{MCP_WWW_AUTHENTICATE_META_KEY,McpConnectionClient,isMcpAuthChallengeResult,isMcpAuthRequiredError,passesToolFilter,readHttpStatus,resolveHeaders};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createLogger}from"#internal/logging.js";import{isObject}from"#shared/guards.js";import{isLoopbackHostname}from"#shared/network-address.js";import{parseJsonObject}from"#shared/json.js";import{
|
|
1
|
+
import{createLogger}from"#internal/logging.js";import{isObject}from"#shared/guards.js";import{isLoopbackHostname}from"#shared/network-address.js";import{parseJsonObject}from"#shared/json.js";import{resolveHeaders}from"#runtime/connections/mcp-client.js";import{omitProvidedArgumentsFromSchema,resolveProvidedArguments}from"#runtime/connections/provided-arguments.js";import{HTTP_METHODS,operationDescription,operationName,uniqueName}from"#runtime/connections/openapi-operations.js";import{buildInputSchema,deref,derefSchema,isArray}from"#runtime/connections/openapi-schema.js";import{applySecurity,resolveSecurity}from"#runtime/connections/openapi-security.js";import{extractServerUrl,parseSpecDocument}from"#runtime/connections/openapi-spec.js";const log=createLogger(`runtime.connections.openapi-client`),SWAGGER_PARAMETER_SCHEMA_KEYS=[`default`,`enum`,`exclusiveMaximum`,`exclusiveMinimum`,`format`,`items`,`maximum`,`maxItems`,`maxLength`,`minimum`,`minItems`,`minLength`,`multipleOf`,`pattern`,`type`,`uniqueItems`];var OpenApiConnectionClient=class{#e;#t;#n;constructor(e){this.#n=e}async connect(){return this.#r()}async getToolMetadata(){return(await this.#r()).metadata}async executeTool(e,t,n){let r=await this.#r(),i=r.operations.get(e);if(i===void 0)throw Error(`Tool "${e}" not found in connection "${this.#n.connectionName}".`);return this.#f(i,r.baseUrl,isObject(t)?t:{},{abortSignal:n.abortSignal,callId:n.callId})}async close(){this.#e=void 0,this.#t=void 0}async#r(){if(this.#t!==void 0)return this.#t;if(this.#e!==void 0)return this.#e;this.#e=this.#i();try{return this.#t=await this.#e,this.#t}catch(e){throw this.#e=void 0,e}}async#i(){let e=await this.#o(),t=this.#a(e),n=this.#s(e),r=[],i=new Map,a=Object.keys(this.#n.toolCall?.providedArguments??{});for(let e of n){let t;try{t=parseJsonObject(omitProvidedArgumentsFromSchema(e.inputSchema,a))}catch(t){log.warn(`omitting OpenAPI operation with an invalid input schema`,{connectionName:this.#n.connectionName,error:t,toolName:e.toolName});continue}i.set(e.toolName,e),r.push({description:e.description,inputSchema:t,name:e.toolName})}return{metadata:r,operations:i,baseUrl:t}}#a(e){let t=this.#n.url,n=typeof t==`string`&&t.trim().length>0?t:extractServerUrl(e,this.#n.spec);if(n===void 0)throw Error(`OpenAPI connection "${this.#n.connectionName}" has no base URL: set "baseUrl" or ensure the document declares an absolute "servers" entry or Swagger "host".`);return assertSecureUrl(n,this.#n.connectionName,`base`),n}async#o(){let e=this.#n.spec;if(e===void 0)throw Error(`OpenAPI connection "${this.#n.connectionName}" is missing its "spec" source.`);if(typeof e!=`string`)return e;assertSecureUrl(e,this.#n.connectionName,`spec`);let t;try{t=await fetch(e,{headers:{accept:`application/json, application/yaml, text/yaml, */*`}})}catch(t){throw Error(`OpenAPI connection "${this.#n.connectionName}" failed to fetch its spec from "${e}": ${String(t)}`)}if(t.redirected&&assertSecureUrl(t.url,this.#n.connectionName,`spec`),!t.ok)throw Error(`OpenAPI connection "${this.#n.connectionName}" failed to fetch its spec from "${e}": HTTP ${t.status}.`);let n=await t.text(),r;try{r=parseSpecDocument(n)}catch(t){throw Error(`OpenAPI connection "${this.#n.connectionName}" spec at "${e}" could not be parsed as JSON or YAML: ${String(t)}`)}if(!isObject(r))throw Error(`OpenAPI connection "${this.#n.connectionName}" spec at "${e}" is not an OpenAPI document object.`);return r}#s(e){let t=e.paths;if(!isObject(t))return[];let n=[],r=new Set;for(let[i,a]of Object.entries(t)){if(!isObject(a))continue;let t=isArray(a.parameters)?a.parameters:[];for(let o of HTTP_METHODS){let s=a[o];if(!isObject(s))continue;let c=uniqueName(operationName(s,o,i),r),l=isArray(s.parameters)?s.parameters:[],u=[...t,...l],d=this.#c(e,u),f=this.#l(e,s.requestBody,u);n.push({toolName:c,method:o,pathTemplate:i,description:operationDescription(s),parameters:d,requestBody:f,inputSchema:buildInputSchema(d,f),security:resolveSecurity(e,s)})}}return n}#c(e,t){let n=[];for(let r of t){let t=isObject(r)?deref(e,r):r;if(!isObject(t))continue;let i=t.in;if(i!==`path`&&i!==`query`&&i!==`header`&&i!==`cookie`||typeof t.name!=`string`)continue;let a=this.#u(e,t);n.push({name:t.name,location:i,required:t.required===!0||i===`path`,schema:a,description:typeof t.description==`string`?t.description:void 0})}return n}#l(e,t,n=[]){if(isObject(t)){let n=deref(e,t);if(isObject(n)&&isObject(n.content)){let t=n.content,r=`application/json`in t?`application/json`:Object.keys(t)[0];if(r!==void 0){let i=t[r],a=isObject(i)&&isObject(i.schema)?derefSchema(e,i.schema):{};return{required:n.required===!0,contentType:r,schema:a}}}}return this.#d(e,n)}#u(e,t){if(isObject(t.schema))return derefSchema(e,t.schema);let n={};for(let e of SWAGGER_PARAMETER_SCHEMA_KEYS)t[e]!==void 0&&(n[e]=t[e]);return derefSchema(e,n)}#d(e,t){for(let n of t){let t=isObject(n)?deref(e,n):n;if(!isObject(t)||t.in!==`body`)continue;let r=isObject(t.schema)?derefSchema(e,t.schema):{};return{required:t.required===!0,contentType:`application/json`,schema:r}}}async#f(e,t,n,r){let i=await resolveProvidedArguments({args:n,callId:r.callId,connection:this.#n,toolName:e.toolName}),a=await resolveHeaders(this.#n),o=e.pathTemplate,s=new URLSearchParams,c=[];for(let t of e.parameters){let e=i[t.name];e!=null&&(t.location===`path`?o=o.replace(`{${t.name}}`,encodeURIComponent(String(e))):t.location===`query`?appendQuery(s,t.name,e):t.location===`cookie`?c.push(`${t.name}=${encodeURIComponent(String(e))}`):a[t.name]=String(e))}if(applySecurity(e.security,this.#n,a,s,c),c.length>0){let e=a.cookie??a.Cookie;delete a.Cookie,a.cookie=[e,...c].filter(e=>!!e).join(`; `)}let l=new URL(joinPath(t,o));l.search=s.toString();let u;e.requestBody!==void 0&&i.body!==void 0&&(u=JSON.stringify(i.body),a[`content-type`]=e.requestBody.contentType);let d=await fetch(l,{method:e.method.toUpperCase(),headers:a,body:u,signal:r?.abortSignal});return{status:d.status,statusText:d.statusText,body:await readResponseBody(d)}}};function appendQuery(e,t,n){if(isArray(n)){for(let r of n)e.append(t,String(r));return}e.append(t,String(n))}function joinPath(e,t){return`${e.endsWith(`/`)?e.slice(0,-1):e}${t.startsWith(`/`)?t:`/${t}`}`}function assertSecureUrl(e,t,n){let r;try{r=new URL(e)}catch{throw Error(`OpenAPI connection "${t}" has an invalid ${n} URL "${e}".`)}if(r.protocol!==`https:`&&!(r.protocol===`http:`&&isLoopbackHostname(r.hostname)))throw Error(`OpenAPI connection "${t}" must use https for its ${n} (got "${r.protocol}//${r.host}").`)}async function readResponseBody(e){let t=await e.text();if(t.length===0)return null;if((e.headers.get(`content-type`)??``).includes(`application/json`))try{return JSON.parse(t)}catch{return t}return t}export{OpenApiConnectionClient};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{contextStorage,loadContext}from"#context/container.js";import{AuthKey}from"#context/keys.js";import{ConnectionAuthorizationFailedError,ConnectionAuthorizationRequiredError,isConnectionAuthorizationRequiredError}from"#connections/errors.js";import{consumeAuthorizationResult,createAuthorizationAttempt,requestAuthorization}from"#harness/authorization.js";import{supportsInteractiveAuthorization}from"#shared/connection-types.js";import{isAsyncIterable}from"#shared/async-iterable.js";import{
|
|
1
|
+
import{contextStorage,loadContext}from"#context/container.js";import{AuthKey}from"#context/keys.js";import{ConnectionAuthorizationFailedError,ConnectionAuthorizationRequiredError,isConnectionAuthorizationRequiredError}from"#connections/errors.js";import{consumeAuthorizationResult,createAuthorizationAttempt,requestAuthorization}from"#harness/authorization.js";import{supportsInteractiveAuthorization}from"#shared/connection-types.js";import{isAsyncIterable}from"#shared/async-iterable.js";import{principalKey,resolveConnectionPrincipal,resolveConnectionPrincipalFromAuth}from"#runtime/connections/principal.js";import{evictCachedToken,readCachedToken,writeCachedToken}from"#runtime/connections/authorization-tokens.js";const LOCAL_HTTP_VERCEL_CONNECT_HOSTNAMES=new Set([`127.0.0.1`,`[::1]`]);function createAuthorizationExecution(e={}){let t=new Set;async function complete(n){let r=n.instanceId??n.scope;!t.has(r)&&await(e.completeAuthorization??completeScopedAuthorization)(n)&&t.add(r)}function requireAuth(e,n){throw new ScopedAuthorizationRequiredError(e,t.has(e.instanceId??e.scope),n)}return{complete,isJustAuthorized(e){return t.has(e.instanceId??e.scope)},async getToken(e){await complete(e);try{return await resolveScopedToken(e)}catch(t){if(!isConnectionAuthorizationRequiredError(t))throw t;return requireAuth(e,t)}},async getResponderToken(e){try{return await resolveScopedToken(e)}catch(t){throw isConnectionAuthorizationRequiredError(t)?new ConnectionAuthorizationFailedError(e.scope,{message:`The approver of "${e.scope}" has no token for this provider, and an approved call doesn't start a sign-in.`,reason:`approver_authorization_required`,retryable:!1}):t}},requireAuth,async handleError(e,n){return n!==void 0&&isConnectionAuthorizationRequiredError(e)?await handleAuthorizationError(new ScopedAuthorizationRequiredError(n,t.has(n.instanceId??n.scope),e),{evictToken:!1}):await handleAuthorizationError(e)},run:executeWithAuthorization}}var ScopedAuthorizationRequiredError=class extends Error{scoped;justAuthorized;constructor(e,t,n){super(`Authorization required.`,{cause:n}),this.name=`ScopedAuthorizationRequiredError`,this.scoped=e,this.justAuthorized=t}};function isScopedAuthorizationRequiredError(e){return e instanceof Error&&e.name===`ScopedAuthorizationRequiredError`}async function handleAuthorizationError(e,t={evictToken:!0}){if(!isScopedAuthorizationRequiredError(e))throw e;let{scoped:n}=e;if(e.justAuthorized)throw new ConnectionAuthorizationFailedError(n.scope,{message:`Authorization for "${n.scope}" failed: the service rejected the token immediately after authorization.`,reason:`token_rejected_after_authorization`,retryable:!1});t.evictToken&&await evictScopedToken(n);let r=await startScopedAuthorization(n);if(r!==void 0)return r;throw supportsInteractiveAuthorization(n.authorization)?new ConnectionAuthorizationFailedError(n.scope,{message:`Authorization for "${n.scope}" requires sign-in, but no authorization callback URL could be minted for this run (missing session context).`,reason:`authorization_callback_unavailable`,retryable:!1}):e.cause??new ConnectionAuthorizationRequiredError(n.scope)}function executeWithAuthorization(e){try{let t=e();return isAsyncIterable(t)?handleIterable(t):Promise.resolve(t).catch(handleAuthorizationError)}catch(e){return handleAuthorizationError(e)}}async function*handleIterable(e){try{for await(let t of e)yield t}catch(e){yield await handleAuthorizationError(e)}}async function resolveScopedToken(e){let{scope:t,authorization:n,connection:r}=e,i=e.instanceId??t,a=contextStorage.getStore(),o=resolveScopedPrincipal(e,a);if(a===void 0)return await n.getToken({connection:r,principal:o});let s=principalKey(o),c=readCachedToken(a,i,s);if(c!==void 0)return c;let l=await n.getToken({connection:r,principal:o});return writeCachedToken(a,i,s,l),l}async function evictScopedToken(e){let{scope:t,authorization:n,connection:r}=e,i=e.instanceId??t,a=contextStorage.getStore();if(a===void 0)return;let o;try{o=resolveScopedPrincipal(e,a),evictCachedToken(a,i,principalKey(o))}catch{return}try{await n.evict?.({connection:r,principal:o})}catch{}}async function completeScopedAuthorization(e){let{scope:t,authorization:n,connection:r}=e;if(!supportsInteractiveAuthorization(n))return!1;let i=consumeAuthorizationResult(t,e.instanceId);if(i===void 0)return!1;let a=n,o=loadContext(),s=i.principal??resolveScopedPrincipal(e,o),c=await a.completeAuthorization({callbackUrl:i.hookUrl,connection:r,principal:s,resume:i.resume,callback:i.callback});return writeCachedToken(o,e.instanceId??t,principalKey(s),c),!0}async function startScopedAuthorization(e){let{scope:t,authorization:n,connection:r}=e;if(!supportsInteractiveAuthorization(n))return;let i=createAuthorizationAttempt(t);if(i===void 0)return;let a=n,o=resolveScopedPrincipal(e),s=e.boundResponder??contextStorage.getStore()?.get(AuthKey)??void 0,c=resolveAuthorizationCallbackUrl({authorization:n,callbackUrl:i.hookUrl}),{challenge:l,resume:u}=await a.startAuthorization({callbackUrl:c,connection:r,principal:o});return requestAuthorization([{attemptId:i.attemptId,challenge:stampChallengeDisplayName(l,n),grant:n.vercelConnect?.connector,hookUrl:c,instanceId:e.instanceId,name:t,principal:o,principalId:s?.principalId,requester:s,resume:u}])}function resolveScopedPrincipal(e,t){return e.boundResponder===void 0?resolveConnectionPrincipal(e.scope,e.authorization,t):resolveConnectionPrincipalFromAuth(e.scope,e.authorization,e.boundResponder,t)}function resolveAuthorizationCallbackUrl(e){if(e.authorization.vercelConnect===void 0)return e.callbackUrl;let t;try{t=new URL(e.callbackUrl)}catch{return e.callbackUrl}return t.protocol!==`http:`||!LOCAL_HTTP_VERCEL_CONNECT_HOSTNAMES.has(t.hostname)?e.callbackUrl:(t.hostname=`localhost`,t.toString())}function stampChallengeDisplayName(e,t){let n=t.displayName??e.displayName;return n===e.displayName?e:{...e,displayName:n}}export{completeScopedAuthorization,createAuthorizationExecution,evictScopedToken,handleAuthorizationError,resolveAuthorizationCallbackUrl,resolveScopedToken,stampChallengeDisplayName};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{expectObjectRecord}from"#internal/authored-module.js";import{toErrorMessage}from"#shared/errors.js";import{normalizeMcpClientConnectionDefinition,normalizeOpenApiConnectionDefinition}from"#internal/authored-definition/connection.js";import{readStampedConnectionProtocol}from"#public/definitions/connections/protocol.js";import{connectionDefinitionKey,registerDefinitionSource}from"#internal/authored-definition/source-identity.js";import{normalizeAuthorizationSpec}from"#shared/validate-authorization.js";import{ResolveAgentError,loadResolvedModuleExport}from"#runtime/resolve-helpers.js";import{createConnectionInstanceId}from"#runtime/connections/instance-identity.js";async function resolveConnectionDefinition(e,t,n){try{let r=await loadResolvedModuleExport({definition:e,kindLabel:`connection`,moduleMap:t,nodeId:n}),i=expectObjectRecord(r,`Expected the connection export "${e.exportName??`default`}" from "${e.logicalPath}" to return an object.`);registerDefinitionSource(i,{kind:`connection`,logicalPath:e.logicalPath,name:e.connectionName},connectionDefinitionKey(e.url,e.description));let a=i.auth!==void 0,o=i.headers!==void 0,s=e.protocol===`openapi`?i.operations:i.tools,c={connectionName:e.connectionName,description:e.description,exportName:e.exportName,logicalPath:e.logicalPath,instanceId:createConnectionInstanceId({connectionName:e.connectionName,instanceKey:typeof i.instanceKey==`string`?i.instanceKey:void 0,protocol:e.protocol,sourceId:e.sourceId,url:e.url}),protocol:e.protocol,sourceId:e.sourceId,sourceKind:`module`,url:e.url};if(a){if(typeof i.auth==`function`)c.authorization=i.auth;else try{c.authorization=normalizeAuthorizationSpec(i.auth,`Connection "${e.connectionName}" at "${e.logicalPath}":`)}catch(t){throw new ResolveAgentError(toErrorMessage(t),{logicalPath:e.logicalPath,sourceId:e.sourceId})}}if(e.protocol===`mcp`&&i.protocolVersionDiscovery!==void 0){if(typeof i.protocolVersionDiscovery!=`boolean`)throw Error(`"protocolVersionDiscovery" must be a boolean.`);c.protocolVersionDiscovery=i.protocolVersionDiscovery}if(e.protocol===`mcp`&&i.forwardPrincipal!==void 0){if(typeof i.forwardPrincipal!=`boolean`)throw Error(`"forwardPrincipal" must be a boolean.`);c.forwardPrincipal=i.forwardPrincipal}return o&&(c.headers=i.headers),i.toolCall!==void 0&&(c.toolCall=i.toolCall),s!==void 0&&(c.tools=s),e.protocol===`openapi`&&i.spec!==void 0&&(c.spec=i.spec),typeof i.approval==`function`&&(c.approval=i.approval),c}catch(t){throw t instanceof ResolveAgentError?t:new ResolveAgentError(`Failed to resolve connection "${e.connectionName}" from "${e.logicalPath}": ${toErrorMessage(t)}`,{logicalPath:e.logicalPath,sourceId:e.sourceId})}}function resolveDynamicConnectionValue(e,t){let n=readStampedConnectionProtocol(e),r=`Dynamic connection "${t.connectionName}" from "${t.logicalPath}" must be created by defineMcpClientConnection() or defineOpenAPIConnection().`;if(n===void 0)throw Error(r);if(registerDefinitionSource(e,{kind:`connection`,logicalPath:t.logicalPath,name:t.connectionName}),n===`openapi`){let i=normalizeOpenApiConnectionDefinition(e,r);return assertAuthenticatedDynamicInstanceKey(i.auth,i.instanceKey,r),omitUndefined({approval:i.approval,authorization:i.auth,connectionName:t.connectionName,description:i.description,exportName:t.exportName,headers:i.headers,instanceId:createConnectionInstanceId({connectionName:t.connectionName,instanceKey:i.instanceKey,protocol:n,sourceId:t.sourceId,url:i.baseUrl??``}),logicalPath:t.logicalPath,protocol:n,sourceId:t.sourceId,sourceKind:`module`,spec:i.spec,toolCall:i.toolCall,tools:i.operations,url:i.baseUrl??``})}let i=normalizeMcpClientConnectionDefinition(e,r);return assertAuthenticatedDynamicInstanceKey(i.auth,i.instanceKey,r),omitUndefined({approval:i.approval,authorization:i.auth,connectionName:t.connectionName,description:i.description,exportName:t.exportName,headers:i.headers,instanceId:createConnectionInstanceId({connectionName:t.connectionName,instanceKey:i.instanceKey,protocol:n,sourceId:t.sourceId,url:i.url}),logicalPath:t.logicalPath,protocol:n,sourceId:t.sourceId,sourceKind:`module`,toolCall:i.toolCall,protocolVersionDiscovery:i.protocolVersionDiscovery,forwardPrincipal:i.forwardPrincipal,tools:i.tools,url:i.url})}function assertAuthenticatedDynamicInstanceKey(e,t,n){if(e!==void 0&&t===void 0)throw Error(`${n} Authenticated dynamic connections must set "instanceKey" to a stable, non-secret account or tenant identifier.`)}function omitUndefined(e){return Object.fromEntries(Object.entries(e).filter(([,e])=>e!==void 0))}export{resolveConnectionDefinition,resolveDynamicConnectionValue};
|
|
1
|
+
import{expectObjectRecord}from"#internal/authored-module.js";import{toErrorMessage}from"#shared/errors.js";import{normalizeMcpClientConnectionDefinition,normalizeOpenApiConnectionDefinition}from"#internal/authored-definition/connection.js";import{readStampedConnectionProtocol}from"#public/definitions/connections/protocol.js";import{connectionDefinitionKey,registerDefinitionSource}from"#internal/authored-definition/source-identity.js";import{normalizeAuthorizationSpec}from"#shared/validate-authorization.js";import{ResolveAgentError,loadResolvedModuleExport}from"#runtime/resolve-helpers.js";import{createConnectionInstanceId}from"#runtime/connections/instance-identity.js";async function resolveConnectionDefinition(e,t,n){try{let r=await loadResolvedModuleExport({definition:e,kindLabel:`connection`,moduleMap:t,nodeId:n}),i=expectObjectRecord(r,`Expected the connection export "${e.exportName??`default`}" from "${e.logicalPath}" to return an object.`);registerDefinitionSource(i,{kind:`connection`,logicalPath:e.logicalPath,name:e.connectionName},connectionDefinitionKey(e.url,e.description));let a=i.auth!==void 0,o=i.headers!==void 0,s=e.protocol===`openapi`?i.operations:i.tools,c={connectionName:e.connectionName,description:e.description,exportName:e.exportName,logicalPath:e.logicalPath,instanceId:createConnectionInstanceId({connectionName:e.connectionName,instanceKey:typeof i.instanceKey==`string`?i.instanceKey:void 0,protocol:e.protocol,sourceId:e.sourceId,url:e.url}),protocol:e.protocol,sourceId:e.sourceId,sourceKind:`module`,url:e.url};if(a){if(typeof i.auth==`function`)c.authorization=i.auth;else try{c.authorization=normalizeAuthorizationSpec(i.auth,`Connection "${e.connectionName}" at "${e.logicalPath}":`)}catch(t){throw new ResolveAgentError(toErrorMessage(t),{logicalPath:e.logicalPath,sourceId:e.sourceId})}}if(e.protocol===`mcp`&&i.protocolVersionDiscovery!==void 0){if(typeof i.protocolVersionDiscovery!=`boolean`)throw Error(`"protocolVersionDiscovery" must be a boolean.`);c.protocolVersionDiscovery=i.protocolVersionDiscovery}if(e.protocol===`mcp`&&i.forwardPrincipal!==void 0){if(typeof i.forwardPrincipal!=`boolean`)throw Error(`"forwardPrincipal" must be a boolean.`);c.forwardPrincipal=i.forwardPrincipal}if(e.experimental_events===!0){let t=normalizeMcpClientConnectionDefinition(i,`Connection "${e.connectionName}":`).experimental_events,n=typeof c.authorization==`object`?c.authorization:void 0;if(t===void 0||n?.vercelConnect?.experimental_events===void 0)throw new ResolveAgentError(`Connection "${e.connectionName}": experimental_events requires a static Connect auth provider with the events bridge.`,{logicalPath:e.logicalPath,sourceId:e.sourceId});c.experimental_events={callbacks:t,authorization:n,connector:n.vercelConnect.connector,backend:n.vercelConnect.experimental_events}}return o&&(c.headers=i.headers),i.toolCall!==void 0&&(c.toolCall=i.toolCall),s!==void 0&&(c.tools=s),e.protocol===`openapi`&&i.spec!==void 0&&(c.spec=i.spec),typeof i.approval==`function`&&(c.approval=i.approval),c}catch(t){throw t instanceof ResolveAgentError?t:new ResolveAgentError(`Failed to resolve connection "${e.connectionName}" from "${e.logicalPath}": ${toErrorMessage(t)}`,{logicalPath:e.logicalPath,sourceId:e.sourceId})}}function resolveDynamicConnectionValue(e,t){let n=readStampedConnectionProtocol(e),r=`Dynamic connection "${t.connectionName}" from "${t.logicalPath}" must be created by defineMcpClientConnection() or defineOpenAPIConnection().`;if(n===void 0)throw Error(r);if(registerDefinitionSource(e,{kind:`connection`,logicalPath:t.logicalPath,name:t.connectionName}),n===`openapi`){let i=normalizeOpenApiConnectionDefinition(e,r);return assertAuthenticatedDynamicInstanceKey(i.auth,i.instanceKey,r),omitUndefined({approval:i.approval,authorization:i.auth,connectionName:t.connectionName,description:i.description,exportName:t.exportName,headers:i.headers,instanceId:createConnectionInstanceId({connectionName:t.connectionName,instanceKey:i.instanceKey,protocol:n,sourceId:t.sourceId,url:i.baseUrl??``}),logicalPath:t.logicalPath,protocol:n,sourceId:t.sourceId,sourceKind:`module`,spec:i.spec,toolCall:i.toolCall,tools:i.operations,url:i.baseUrl??``})}let i=normalizeMcpClientConnectionDefinition(e,r);if(i.experimental_events!==void 0)throw Error(`${r} experimental_events requires a statically authored connection so eve can mount its receiver at build time.`);return assertAuthenticatedDynamicInstanceKey(i.auth,i.instanceKey,r),omitUndefined({approval:i.approval,authorization:i.auth,connectionName:t.connectionName,description:i.description,exportName:t.exportName,headers:i.headers,instanceId:createConnectionInstanceId({connectionName:t.connectionName,instanceKey:i.instanceKey,protocol:n,sourceId:t.sourceId,url:i.url}),logicalPath:t.logicalPath,protocol:n,sourceId:t.sourceId,sourceKind:`module`,toolCall:i.toolCall,protocolVersionDiscovery:i.protocolVersionDiscovery,forwardPrincipal:i.forwardPrincipal,tools:i.tools,url:i.url})}function assertAuthenticatedDynamicInstanceKey(e,t,n){if(e!==void 0&&t===void 0)throw Error(`${n} Authenticated dynamic connections must set "instanceKey" to a stable, non-secret account or tenant identifier.`)}function omitUndefined(e){return Object.fromEntries(Object.entries(e).filter(([,e])=>e!==void 0))}export{resolveConnectionDefinition,resolveDynamicConnectionValue};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { StandardSchemaV1 } from "#compiled/@standard-schema/spec/index.js";
|
|
2
|
-
import type { SessionSchedule } from "#context/session-schedule.js";
|
|
3
2
|
import type { ScheduleClient, ScheduleClientCreate, ScheduleClientUpdate, DynamicSchedulesDefinition, ScheduleEnvelope, ScheduleScopeContext } from "#public/schedules/subscription.js";
|
|
4
3
|
export interface ScheduleBoundCallContext extends ScheduleScopeContext {
|
|
5
4
|
readonly application: string;
|
|
@@ -26,4 +25,3 @@ export interface ScheduleCollectionClient<TInput, TPrepared> extends ScheduleCli
|
|
|
26
25
|
update(name: string, patch: ScheduleClientUpdate<TInput>, approved?: PreparedScheduleUpdate): Promise<import("#public/schedules/subscription.js").ScheduleRecord>;
|
|
27
26
|
}
|
|
28
27
|
export declare function createScheduleCollectionClient<TPayload, TPrepared = TPayload>(definition: DynamicSchedulesDefinition<TPayload, StandardSchemaV1<unknown, TPayload>, TPrepared>, callContext: ScheduleBoundCallContext): ScheduleCollectionClient<TPayload, TPrepared>;
|
|
29
|
-
export declare function assertScheduleManagementAllowed(schedule?: SessionSchedule): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createHash,randomUUID}from"node:crypto";import{
|
|
1
|
+
import{createHash,randomUUID}from"node:crypto";import{loadContext}from"#context/container.js";import{projectScheduleRecord}from"#runtime/schedules/record.js";import{createScheduleCollectionPayload,validateSchedulePayload}from"#runtime/schedules/payload.js";import{isDeepStrictEqual}from"node:util";import{BundleKey}from"#runtime/sessions/runtime-context-keys.js";import{resolveScheduleTiming,validateScheduleListLimit,validateScheduleName}from"#runtime/schedules/validation.js";import{dispatchScheduledOccurrence}from"#runtime/schedules/dispatch-occurrence.js";import{byPrincipal}from"#public/schedules/scope.js";function createScheduleCollectionClient(e,t){let n=t.operationId??randomUUID,contextFor=(e,n)=>{let r={...t,operation:e};return n!==void 0&&(r.name=n),r},resolveNamespace=async(r,i)=>{let a=contextFor(r,i),o=e.scope===void 0?byPrincipal(a):await e.scope({...a,operation:r});if(o===null)throw Error(`Schedule ${r} is not available in this context.`);return validateScope(o),{scope:o,provider:{abortSignal:t.abortSignal,collection:t.collection,namespace:deriveScheduleNamespace(t.application,t.collection,o),operationId:n(),target:{key:t.application,deliver:async({payload:n,occurrence:r})=>{await dispatchScheduledOccurrence({bundle:loadContext().require(BundleKey),collection:t.collection,definition:e,occurrence:{...r,collection:t.collection},payload:n})}}}}},prepareEnvelope=async(n,r,i,a)=>{let o=principalReference(t.session.auth.current);if(o===null)throw Error(`Writing a schedule payload requires an authenticated principal.`);let s=await validateSchedulePayload(e.inputSchema,n),c=e.preparePayload===void 0?s:await e.preparePayload(s,{...contextFor(i,a),operation:i,name:a});return createScheduleCollectionPayload({application:t.application,collection:t.collection,envelope:{version:3,payload:c,scope:r,principal:o}}).envelope},prepareUpdate=async(t,n)=>{let r=Object.keys(n).filter(e=>e!==`expression`&&e!==`payload`);if(r.length>0)throw Error(`Schedule update does not support: ${r.join(`, `)}.`);if(n.expression===void 0&&n.payload===void 0)throw Error(`Schedule update requires an expression or replacement payload.`);let i=validateScheduleName(t);n.expression!==void 0&&resolveScheduleTiming(n.expression);let{scope:a,provider:o}=await resolveNamespace(`update`,i);if(await e.provider.get(o,i)===null)throw Error(`Schedule was not found in the authorized scope.`);return{prepared:snapshotPreparedWrite({name:i,namespace:o.namespace,expression:n.expression,envelope:n.payload===void 0?void 0:await prepareEnvelope(n.payload,a,`update`,i)}),provider:o}},prepareCreation=async e=>{let t=validateScheduleName(e.name);resolveScheduleTiming(e.expression);let{scope:n,provider:r}=await resolveNamespace(`create`,t);return{prepared:snapshotPreparedWrite({displayName:t,expression:e.expression,envelope:await prepareEnvelope(e.payload,n,`create`,t)}),provider:r}};return{async prepareCreate(e){return(await prepareCreation(e)).prepared},async create(n,r){let{prepared:i,provider:a}=await prepareCreation(n);if(r!==void 0&&!isDeepStrictEqual(i,r))throw Error(`Schedule creation changed after approval. Request a new creation approval; no schedule was written.`);let o=`${i.displayName.slice(0,218)}--${randomUUID()}`,s=resolveScheduleTiming(n.expression),c=createScheduleCollectionPayload({application:t.application,collection:t.collection,envelope:i.envelope});return projectScheduleRecord(await e.provider.create(a,{expression:s,name:o,payload:c}))},async prepareUpdate(e,t){return(await prepareUpdate(e,t)).prepared},async update(n,r,i){let{prepared:a,provider:o}=await prepareUpdate(n,r);if(i!==void 0&&!isDeepStrictEqual(a,i))throw Error(`Schedule update changed after approval. Request a new update approval; no schedule was written.`);let s={expression:a.expression===void 0?void 0:resolveScheduleTiming(a.expression),payload:a.envelope===void 0?void 0:createScheduleCollectionPayload({application:t.application,collection:t.collection,envelope:a.envelope})};return projectScheduleRecord(await e.provider.update(o,a.name,s))},async delete(t){let n=validateScheduleName(t),{provider:r}=await resolveNamespace(`delete`,n);return await e.provider.delete(r,n)},async disable(t){let n=validateScheduleName(t),{provider:r}=await resolveNamespace(`disable`,n);return projectScheduleRecord(await e.provider.disable(r,n))},async enable(t){let n=validateScheduleName(t),{provider:r}=await resolveNamespace(`enable`,n);return projectScheduleRecord(await e.provider.enable(r,n))},async get(t){let n=validateScheduleName(t),{provider:r}=await resolveNamespace(`get`,n),i=await e.provider.get(r,n);return i===null?null:projectScheduleRecord(i)},async invoke(t){let n=validateScheduleName(t),{provider:r}=await resolveNamespace(`invoke`,n);await e.provider.invoke(r,n)},async list(t={}){let n=validateScheduleListLimit(t.limit),{provider:r}=await resolveNamespace(`list`),i={};t.cursor?.trim()&&(i.cursor=t.cursor.trim()),n!==void 0&&(i.limit=n);let a=await e.provider.list(r,i);return{...a,data:a.data.map(projectScheduleRecord)}}}}function snapshotPreparedWrite(e){let t=JSON.stringify(e);if(Buffer.byteLength(t)>65536)throw Error(`Prepared schedule write exceeds the 64 KB limit, including timing and payload.`);return JSON.parse(t)}function principalReference(e){if(e===null||e.principalType===`anonymous`||e.principalType===`runtime`)return null;let t={type:e.principalType,authenticator:e.authenticator,principalId:e.principalId};return e.issuer!==void 0&&(t.issuer=e.issuer),e.subject!==void 0&&(t.subject=e.subject),t}function validateScope(e){let t=typeof e==`string`?[e]:e;if(t.length===0||t.some(e=>typeof e!=`string`||e.trim().length===0))throw Error(`Schedule scope must be a non-empty string or tuple of non-empty strings.`)}function deriveScheduleNamespace(e,t,n){if(e.trim().length===0)throw Error(`Schedule application must not be empty.`);return`eve-${createHash(`sha256`).update(JSON.stringify([`eve-schedule-namespace-v2`,e,t,n])).digest(`base64url`)}`}export{createScheduleCollectionClient};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { Experimental_ConnectionEvents } from "#public/definitions/connections/events.js";
|
|
2
|
+
import type { ConnectionEventsBackend } from "#shared/connection-events.js";
|
|
1
3
|
import type { ChannelAdapter } from "#channel/adapter.js";
|
|
2
4
|
import type { CompiledChannel } from "#channel/compiled-channel.js";
|
|
3
5
|
import type { NormalizedChannelCorsOptions } from "#channel/cors.js";
|
|
@@ -76,7 +78,16 @@ export type ResolvedScheduleDefinition = Readonly<SourceRef & {
|
|
|
76
78
|
* Both `authorization` and `headers` are optional — a connection to a
|
|
77
79
|
* server that requires no authentication (e.g. localhost) may omit both.
|
|
78
80
|
*/
|
|
81
|
+
/** A connection's event watching, checked once when the connection resolves. */
|
|
82
|
+
export interface ResolvedConnectionEvents {
|
|
83
|
+
readonly callbacks: Experimental_ConnectionEvents;
|
|
84
|
+
/** The static Connect authorization every watch subscribes and runs under. */
|
|
85
|
+
readonly authorization: Readonly<AuthorizationDefinition>;
|
|
86
|
+
readonly connector: string;
|
|
87
|
+
readonly backend: ConnectionEventsBackend;
|
|
88
|
+
}
|
|
79
89
|
export interface ResolvedConnectionDefinition extends ResolvedModuleSourceRef {
|
|
90
|
+
readonly experimental_events?: ResolvedConnectionEvents;
|
|
80
91
|
readonly protocolVersionDiscovery?: boolean;
|
|
81
92
|
/** MCP only: send the turn's principals in `eve-forwarded-principal`. */
|
|
82
93
|
readonly forwardPrincipal?: boolean;
|