eve 0.79.1 → 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 +39 -0
- package/dist/src/channel/pending-authorization.d.ts +10 -0
- package/dist/src/channel/pending-authorization.js +1 -0
- package/dist/src/compiled/.vendor-stamp.json +7 -7
- 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/@eve/tracing/index.js +1 -1
- package/dist/src/compiled/@eve/tracing/otel.js +1 -1
- 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/_chunks/workflow/{mcp-C01bDEX-.js → mcp-Bwx_LQ4q.js} +4 -4
- package/dist/src/compiled/chat/index.js +1 -1
- package/dist/src/compiler/extension-compatibility.d.ts +18 -18
- 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/proxied-deliver-step.js +1 -1
- package/dist/src/execution/publish-session-events.d.ts +13 -3
- package/dist/src/execution/publish-session-events.js +1 -1
- package/dist/src/execution/sandbox/bindings/microsandbox-runtime.js +1 -1
- package/dist/src/execution/session/active-turn.d.ts +2 -1
- package/dist/src/execution/session/active-turn.js +1 -1
- package/dist/src/execution/session/entry.js +1 -1
- package/dist/src/execution/session/turn-step.js +1 -1
- package/dist/src/execution/session/turn.d.ts +1 -0
- package/dist/src/execution/session/turn.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/tasks/task-id-input.js +1 -1
- package/dist/src/execution/tools/agent-router-workflow.js +1 -1
- package/dist/src/execution/tools/workflow/apply-run-messages-step.d.ts +6 -2
- package/dist/src/execution/tools/workflow/apply-run-messages-step.js +1 -1
- package/dist/src/execution/tools/workflow/body.d.ts +10 -1
- package/dist/src/execution/tools/workflow/body.js +1 -1
- package/dist/src/execution/tools/workflow/owner-inbox.js +1 -1
- package/dist/src/execution/tools/workflow/serve.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/action-result-helpers.js +1 -1
- package/dist/src/harness/attachment-staging.js +1 -1
- package/dist/src/harness/compaction/engine.js +3 -1
- package/dist/src/harness/compaction/prompt.d.ts +7 -0
- package/dist/src/harness/compaction/prompt.js +1 -1
- package/dist/src/harness/hitl/approvals.js +1 -1
- package/dist/src/harness/hitl/approved-calls.js +1 -1
- package/dist/src/harness/input-request-resolution.d.ts +2 -1
- package/dist/src/harness/input-request-resolution.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/native-events.js +1 -1
- package/dist/src/instrumentation/session-init.d.ts +2 -1
- package/dist/src/instrumentation/session-init.js +1 -1
- package/dist/src/internal/application/optional-package-import.d.ts +5 -0
- package/dist/src/internal/application/optional-package-import.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/host/start-production-server.js +2 -2
- 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/protocol/message.d.ts +2 -0
- package/dist/src/protocol/message.js +1 -1
- package/dist/src/public/channels/slack/blocks.js +2 -2
- package/dist/src/public/channels/teams/authorization.d.ts +53 -0
- package/dist/src/public/channels/teams/authorization.js +1 -0
- package/dist/src/public/channels/teams/defaults.js +2 -2
- package/dist/src/public/channels/teams/hitl.d.ts +2 -1
- package/dist/src/public/channels/teams/teamsChannel.js +1 -1
- package/dist/src/public/channels/telegram/authorization-callback.js +1 -1
- 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/next/index.js +1 -1
- package/dist/src/public/next/server.d.ts +11 -2
- package/dist/src/public/next/server.js +1 -1
- 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/action-types.d.ts +1 -0
- package/dist/src/shared/action-types.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/tools/signature.d.ts +4 -2
- package/dist/src/tools/signature.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/channels/teams.mdx +6 -0
- package/docs/code-extension.mdx +28 -0
- package/docs/concepts/built-in-tools.md +3 -1
- package/docs/connections/mcp.mdx +105 -0
- package/docs/guides/hooks.md +4 -0
- package/docs/guides/instrumentation/instrumentation.mdx +2 -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
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createLogger,logError}from"#internal/logging.js";import{readNonEmptyString}from"#shared/guards.js";import{defaultDeliverResult}from"#channel/adapter.js";import{parseJsonObject}from"#shared/json.js";import{POST,defineChannel}from"#public/definitions/channel.js";import{callTeamsConnectorApi,normalizeTeamsContinuationAddress,normalizeTeamsPostInput,replyToTeamsActivity,sendTeamsActivity,teamsContinuationToken,triggerTeamsTypingIndicator,updateTeamsActivity}from"#public/channels/teams/api.js";import{deriveTeamsInputResponses,isTeamsInputResponseActivity,isTeamsToolApprovalResponseActivity,readTeamsInputReplyToActivityId,readTeamsToolApprovalPrompt,teamsInvokeResponse}from"#public/channels/teams/hitl.js";import{formatTeamsContextBlock,parseTeamsActivity,teamsThreadRootActivityId}from"#public/channels/teams/inbound.js";import{buildTeamsTurnMessage,collectTeamsFileParts,createTeamsFetchFile,normalizeTeamsFilesPolicy}from"#public/channels/teams/attachments.js";import{defaultEvents,defaultOnMessage,defaultTeamsAuth,teamsMentionUser}from"#public/channels/teams/defaults.js";import{verifyTeamsRequest}from"#public/channels/teams/verify.js";const log=createLogger(`teams.channel`);function teamsChannel(e={}){let t=normalizeTeamsFilesPolicy(e.files),n=e.onMessage??defaultOnMessage,r=e.onInputResponse??(e.onMessage===void 0?defaultOnInputResponse:rejectInput),i={...defaultEvents,...e.events};return defineChannel({kindHint:`teams`,turnPolicy:e.turnPolicy,uploadPolicy:e.files?.uploadPolicy,state:initialTeamsState(),fetchFile:createTeamsFetchFile(t,{...e.api,credentials:e.credentials}),metadata:e=>({channelId:e.channelId,conversationType:e.conversationType,teamId:e.teamId}),audience:({state:e})=>teamsAudience(e.conversationType),context(t,n){return rebuildTeamsContext(t,n,e)},deliver(e,t){let n=e.state,r=n?.pendingPromptCards;r!==void 0&&(t.state.pendingPromptCards={...r,...t.state.pendingPromptCards});let i=n?.approvalResponderAccounts;return i!==void 0&&(t.state.approvalResponderAccounts={...t.state.approvalResponderAccounts,...i}),defaultDeliverResult(e)},routes:[POST(e.route??`/eve/v1/teams`,async(i,{from:a,resolveSession:o,waitUntil:s})=>{let c=await verifyInbound(i,e.credentials);if(c===null)return new Response(`unauthorized`,{status:401});let l;try{l=parseJsonObject(JSON.parse(c))}catch(e){return log.warn(`inbound Teams body is not valid JSON`,{error:e}),teamsOk()}let u=parseTeamsActivity(l);return u===null?teamsOk():u.type===`message`?(s(isTeamsInputResponseActivity(u)?dispatchInputResponses({activity:u,config:e,from:a,onInputResponse:r}):dispatchMessage({activity:u,config:e,filesPolicy:t,onMessage:n,from:a,resolveSession:o})),teamsOk()):u.type===`invoke`?handleInvoke({activity:u,config:e,onInputResponse:r,waitUntil:s,from:a}):teamsOk()})],async receive(t,{from:n}){let r=t.target,i=readNonEmptyString(r.serviceUrl),a=readNonEmptyString(r.conversationId);if(!i||!a)throw Error(`teamsChannel().receive requires target.serviceUrl and target.conversationId.`);let o=readNonEmptyString(r.conversationType)??null,s=readNonEmptyString(r.replyToActivityId)??null,c=r.initialMessage;if(c!==void 0&&s!==null)throw Error("teamsChannel().receive: `replyToActivityId` and `initialMessage` are mutually exclusive.");let l={...initialTeamsState(),channelId:readNonEmptyString(r.channelId)??null,conversationId:a,conversationType:o,replyToActivityId:s,serviceUrl:i,teamId:readNonEmptyString(r.teamId)??null,tenantId:readNonEmptyString(r.tenantId)??null};if(c!==void 0){let t=await buildTeamsBinding({config:e,state:l}).thread.post(c);o!==`personal`&&t.id&&(s=t.id,l.replyToActivityId=t.id)}return n(teamsContinuationToken({conversationId:a,replyToActivityId:s,tenantId:l.tenantId})).send(t.message,{auth:t.auth,state:l})},events:i})}function teamsAudience(e){return e===`personal`||e===`groupChat`?`private`:`unknown`}function rebuildTeamsContext(e,t,n){return{...buildTeamsBinding({config:n,session:t,state:e}),adaptiveCardVersion:n.adaptiveCardVersion??`1.5`,state:e}}function buildTeamsBinding(e){let t=buildTeamsHandle(e);return{teams:t,thread:{mentionUser:teamsMentionUser,post(e){return t.sendActivity(e)},async startTyping(){try{await t.startTyping()}catch(e){logError(log,`Teams typing indicator failed — swallowed`,e)}},update(e,n){return t.updateActivity(e,n)}}}}function buildTeamsHandle(e){let t=e.state,n=e.config.api,r=e.config.credentials;function requireAddress(){let e=t.conversationId??``,n=t.serviceUrl??``;if(!e||!n)throw Error(`teamsChannel: missing serviceUrl or conversationId for outbound message.`);return{conversationId:e,serviceUrl:n}}function anchor(n){if(!n.id||t.replyToActivityId||t.conversationType===`personal`)return;t.replyToActivityId=n.id;let r=t.conversationId;r&&e.session?.continuation?.alias(teamsContinuationToken({conversationId:r,replyToActivityId:n.id,tenantId:t.tenantId}))}async function send(e){let i=requireAddress(),a=buildOutboundActivity(t,e),o=t.replyToActivityId===null?await sendTeamsActivity({...n,body:a,credentials:r,conversationId:i.conversationId,serviceUrl:i.serviceUrl}):await replyToTeamsActivity({...n,body:a,credentials:r,activityId:t.replyToActivityId,conversationId:i.conversationId,serviceUrl:i.serviceUrl});return anchor(o),o}return{channelId:t.channelId??void 0,conversationId:t.conversationId??``,conversationType:t.conversationType??void 0,replyToActivityId:t.replyToActivityId??void 0,serviceUrl:t.serviceUrl??``,teamId:t.teamId??void 0,tenantId:t.tenantId??void 0,request(e,t,i){let a=requireAddress();return callTeamsConnectorApi({...n,body:t,credentials:r,method:i?.method,path:e,serviceUrl:a.serviceUrl})},sendActivity:send,replyToActivity(e){let i=requireAddress(),a=t.replyToActivityId??``;if(!a)throw Error(`teamsChannel: missing reply activity id.`);return replyToTeamsActivity({...n,body:buildOutboundActivity(t,e),credentials:r,activityId:a,conversationId:i.conversationId,serviceUrl:i.serviceUrl})},updateActivity(e,i){let a=requireAddress();return updateTeamsActivity({...n,body:buildUpdateActivity(t,i),credentials:r,activityId:e,conversationId:normalizeTeamsContinuationAddress({conversationId:a.conversationId}).conversationId,serviceUrl:a.serviceUrl})},async startTyping(){let e=requireAddress();await triggerTeamsTypingIndicator({...n,credentials:r,conversationId:e.conversationId,serviceUrl:e.serviceUrl})}}}async function verifyInbound(e,t){try{return await verifyTeamsRequest(e,{appId:t?.webhookVerifier?void 0:t?.appId,webhookVerifier:t?.webhookVerifier})}catch(e){return log.warn(`teams inbound verification failed`,{error:e}),null}}async function dispatchMessage(e){let t=stateFromActivity(e.activity),n=buildTeamsBinding({config:e.config,state:t}),r=stateToken(t),i={...n,isSubscribed:async()=>await e.resolveSession(r)!==void 0},a;try{a=await e.onMessage(i,e.activity)}catch(e){log.error(`Teams message handler failed`,{error:e});return}if(a==null)return;let o=collectTeamsFileParts(e.activity.attachments,e.filesPolicy),s=buildTeamsTurnMessage(e.activity.text,o),c={activityId:e.activity.id,botId:e.activity.recipient.id,channelId:e.activity.teamsChannelId,conversationId:e.activity.conversation.id,conversationType:e.activity.conversationType,isMentioned:e.activity.isBotMentioned,scope:e.activity.scope,teamId:e.activity.teamId,tenantId:e.activity.tenantId,userId:e.activity.from.id,userName:e.activity.from.name},l=a.context??[];try{await e.from(r).send(s,{auth:a.auth,context:[formatTeamsContextBlock(c),...l],state:t,title:a.title})}catch(e){log.error(`Teams message delivery failed`,{error:e})}}async function handleInvoke(e){if(isTeamsInputResponseActivity(e.activity))return e.waitUntil(dispatchInputResponses({activity:e.activity,config:e.config,onInputResponse:e.onInputResponse,from:e.from})),Response.json(teamsInvokeResponse());if(e.config.onInvoke===void 0)return teamsOk();let t=buildTeamsBinding({config:e.config,state:stateFromActivity(e.activity)}),n=await e.config.onInvoke(t,e.activity);return n instanceof Response?n:n&&typeof n==`object`?Response.json(n):teamsOk()}async function dispatchInputResponses(e){let t=deriveTeamsInputResponses(e.activity);if(t.length===0)return;let n=stateFromActivity(e.activity),r=buildTeamsBinding({config:e.config,state:n}),i;try{i=await e.onInputResponse(r,e.activity)}catch(e){log.error(`Teams input response authorization failed`,{error:e});return}if(i!==null)try{await e.from(resolveInputContinuationToken(e.activity,n)).respond(t,{auth:i.auth,state:approvalResponseStatePatch(e.activity,i.auth)})}catch(e){log.error(`Teams input response delivery failed`,{error:e})}}function stateFromActivity(e){let t=normalizeTeamsContinuationAddress({conversationId:e.conversation.id,replyToActivityId:teamsThreadRootActivityId(e)});return{bot:e.recipient,channelId:e.teamsChannelId??null,conversationId:e.conversation.id,conversationType:e.conversationType??e.scope,pendingAuthActivityId:null,replyToActivityId:t.replyToActivityId,serviceUrl:e.serviceUrl,teamId:e.teamId??null,tenantId:e.tenantId??null,triggeringUser:e.from}}function resolveInputContinuationToken(e,t){let n=readTeamsInputReplyToActivityId(e);return n===null?stateToken(t):teamsContinuationToken({conversationId:e.conversation.id,replyToActivityId:n,tenantId:e.tenantId})}function approvalResponseStatePatch(e,t){if(t?.principalId===void 0||!isTeamsToolApprovalResponseActivity(e))return;let n=deriveTeamsInputResponses(e)[0]?.requestId,r=e.replyToId;return n===void 0||r===void 0?{approvalResponderAccounts:{[t.principalId]:e.from}}:{approvalResponderAccounts:{[t.principalId]:e.from},pendingPromptCards:{[n]:{activityId:r,prompt:readTeamsToolApprovalPrompt(e)??`Tool approval`}}}}function defaultOnInputResponse(e,t){return{auth:defaultTeamsAuth(t)}}function rejectInput(){return null}function initialTeamsState(){return{bot:null,channelId:null,conversationId:null,conversationType:null,approvalResponderAccounts:{},pendingPromptCards:{},pendingAuthActivityId:null,replyToActivityId:null,serviceUrl:null,teamId:null,tenantId:null,triggeringUser:null}}function stateToken(e){let t=e.conversationId??``;if(!t)throw Error(`teamsChannel: missing conversation id.`);return teamsContinuationToken({conversationId:t,replyToActivityId:e.replyToActivityId,tenantId:e.tenantId})}function buildOutboundActivity(e,t){if(typeof t!=`string`&&`type`in t&&t.type===`typing`)return t;let n=normalizeTeamsPostInput(t),r=mergeChannelData(e,n.channelData);return{...n,channelData:r,conversation:e.conversationId?{id:e.conversationId}:void 0,from:e.bot??void 0,replyToId:e.replyToActivityId??void 0,type:`message`}}function buildUpdateActivity(e,t){if(typeof t!=`string`&&`type`in t&&t.type===`typing`)throw Error(`teamsChannel: cannot replace an activity with a typing indicator.`);let{channelData:n,conversation:r,from:i,recipient:a,replyToId:o,type:s,...c}=normalizeTeamsPostInput(t);return{...c,type:`message`}}function mergeChannelData(e,t){let n={...t};return e.tenantId&&(n.tenant={id:e.tenantId}),e.teamId&&(n.team={id:e.teamId}),e.channelId&&(n.channel={id:e.channelId}),Object.keys(n).length>0?parseJsonObject(n):void 0}function teamsOk(){return new Response(`ok`,{status:200})}export{teamsChannel};
|
|
1
|
+
import{createLogger,logError}from"#internal/logging.js";import{readNonEmptyString}from"#shared/guards.js";import{defaultDeliverResult}from"#channel/adapter.js";import{parseJsonObject}from"#shared/json.js";import{POST,defineChannel}from"#public/definitions/channel.js";import{callTeamsConnectorApi,normalizeTeamsContinuationAddress,normalizeTeamsPostInput,replyToTeamsActivity,sendTeamsActivity,teamsContinuationToken,triggerTeamsTypingIndicator,updateTeamsActivity}from"#public/channels/teams/api.js";import{deriveTeamsInputResponses,isTeamsInputResponseActivity,isTeamsToolApprovalResponseActivity,readTeamsInputReplyToActivityId,readTeamsToolApprovalPrompt,teamsInvokeResponse}from"#public/channels/teams/hitl.js";import{answerTeamsAuthorizationReveal,readTeamsAuthorizationReveal}from"#public/channels/teams/authorization.js";import{formatTeamsContextBlock,parseTeamsActivity,teamsThreadRootActivityId}from"#public/channels/teams/inbound.js";import{buildTeamsTurnMessage,collectTeamsFileParts,createTeamsFetchFile,normalizeTeamsFilesPolicy}from"#public/channels/teams/attachments.js";import{defaultEvents,defaultOnMessage,defaultTeamsAuth,teamsMentionUser}from"#public/channels/teams/defaults.js";import{verifyTeamsRequest}from"#public/channels/teams/verify.js";const log=createLogger(`teams.channel`);function teamsChannel(e={}){let t=normalizeTeamsFilesPolicy(e.files),n=e.onMessage??defaultOnMessage,r=e.onInputResponse??(e.onMessage===void 0?defaultOnInputResponse:rejectInput),i={...defaultEvents,...e.events};return defineChannel({kindHint:`teams`,turnPolicy:e.turnPolicy,uploadPolicy:e.files?.uploadPolicy,state:initialTeamsState(),fetchFile:createTeamsFetchFile(t,{...e.api,credentials:e.credentials}),metadata:e=>({channelId:e.channelId,conversationType:e.conversationType,teamId:e.teamId}),audience:({state:e})=>teamsAudience(e.conversationType),context(t,n){return rebuildTeamsContext(t,n,e)},deliver(e,t){let n=e.state,r=n?.pendingPromptCards;r!==void 0&&(t.state.pendingPromptCards={...r,...t.state.pendingPromptCards});let i=n?.approvalResponderAccounts;return i!==void 0&&(t.state.approvalResponderAccounts={...t.state.approvalResponderAccounts,...i}),defaultDeliverResult(e)},routes:[POST(e.route??`/eve/v1/teams`,async(i,{from:a,resolveSession:o,waitUntil:s})=>{let c=await verifyInbound(i,e.credentials);if(c===null)return new Response(`unauthorized`,{status:401});let l;try{l=parseJsonObject(JSON.parse(c))}catch(e){return log.warn(`inbound Teams body is not valid JSON`,{error:e}),teamsOk()}let u=parseTeamsActivity(l);return u===null?teamsOk():u.type===`message`?(s(isTeamsInputResponseActivity(u)?dispatchInputResponses({activity:u,config:e,from:a,onInputResponse:r}):dispatchMessage({activity:u,config:e,filesPolicy:t,onMessage:n,from:a,resolveSession:o})),teamsOk()):u.type===`invoke`?handleInvoke({activity:u,config:e,onInputResponse:r,resolveSession:o,waitUntil:s,from:a}):teamsOk()})],async receive(t,{from:n}){let r=t.target,i=readNonEmptyString(r.serviceUrl),a=readNonEmptyString(r.conversationId);if(!i||!a)throw Error(`teamsChannel().receive requires target.serviceUrl and target.conversationId.`);let o=readNonEmptyString(r.conversationType)??null,s=readNonEmptyString(r.replyToActivityId)??null,c=r.initialMessage;if(c!==void 0&&s!==null)throw Error("teamsChannel().receive: `replyToActivityId` and `initialMessage` are mutually exclusive.");let l={...initialTeamsState(),channelId:readNonEmptyString(r.channelId)??null,conversationId:a,conversationType:o,replyToActivityId:s,serviceUrl:i,teamId:readNonEmptyString(r.teamId)??null,tenantId:readNonEmptyString(r.tenantId)??null};if(c!==void 0){let t=await buildTeamsBinding({config:e,state:l}).thread.post(c);o!==`personal`&&t.id&&(s=t.id,l.replyToActivityId=t.id)}return n(teamsContinuationToken({conversationId:a,replyToActivityId:s,tenantId:l.tenantId})).send(t.message,{auth:t.auth,state:l})},events:i})}function teamsAudience(e){return e===`personal`||e===`groupChat`?`private`:`unknown`}function rebuildTeamsContext(e,t,n){return{...buildTeamsBinding({config:n,session:t,state:e}),adaptiveCardVersion:n.adaptiveCardVersion??`1.5`,state:e}}function buildTeamsBinding(e){let t=buildTeamsHandle(e);return{teams:t,thread:{mentionUser:teamsMentionUser,post(e){return t.sendActivity(e)},async startTyping(){try{await t.startTyping()}catch(e){logError(log,`Teams typing indicator failed — swallowed`,e)}},update(e,n){return t.updateActivity(e,n)}}}}function buildTeamsHandle(e){let t=e.state,n=e.config.api,r=e.config.credentials;function requireAddress(){let e=t.conversationId??``,n=t.serviceUrl??``;if(!e||!n)throw Error(`teamsChannel: missing serviceUrl or conversationId for outbound message.`);return{conversationId:e,serviceUrl:n}}function anchor(n){if(!n.id||t.replyToActivityId||t.conversationType===`personal`)return;t.replyToActivityId=n.id;let r=t.conversationId;r&&e.session?.continuation?.alias(teamsContinuationToken({conversationId:r,replyToActivityId:n.id,tenantId:t.tenantId}))}async function send(e){let i=requireAddress(),a=buildOutboundActivity(t,e),o=t.replyToActivityId===null?await sendTeamsActivity({...n,body:a,credentials:r,conversationId:i.conversationId,serviceUrl:i.serviceUrl}):await replyToTeamsActivity({...n,body:a,credentials:r,activityId:t.replyToActivityId,conversationId:i.conversationId,serviceUrl:i.serviceUrl});return anchor(o),o}return{channelId:t.channelId??void 0,conversationId:t.conversationId??``,conversationType:t.conversationType??void 0,replyToActivityId:t.replyToActivityId??void 0,serviceUrl:t.serviceUrl??``,teamId:t.teamId??void 0,tenantId:t.tenantId??void 0,request(e,t,i){let a=requireAddress();return callTeamsConnectorApi({...n,body:t,credentials:r,method:i?.method,path:e,serviceUrl:a.serviceUrl})},sendActivity:send,replyToActivity(e){let i=requireAddress(),a=t.replyToActivityId??``;if(!a)throw Error(`teamsChannel: missing reply activity id.`);return replyToTeamsActivity({...n,body:buildOutboundActivity(t,e),credentials:r,activityId:a,conversationId:i.conversationId,serviceUrl:i.serviceUrl})},updateActivity(e,i){let a=requireAddress();return updateTeamsActivity({...n,body:buildUpdateActivity(t,i),credentials:r,activityId:e,conversationId:normalizeTeamsContinuationAddress({conversationId:a.conversationId}).conversationId,serviceUrl:a.serviceUrl})},async startTyping(){let e=requireAddress();await triggerTeamsTypingIndicator({...n,credentials:r,conversationId:e.conversationId,serviceUrl:e.serviceUrl})}}}async function verifyInbound(e,t){try{return await verifyTeamsRequest(e,{appId:t?.webhookVerifier?void 0:t?.appId,webhookVerifier:t?.webhookVerifier})}catch(e){return log.warn(`teams inbound verification failed`,{error:e}),null}}async function dispatchMessage(e){let t=stateFromActivity(e.activity),n=buildTeamsBinding({config:e.config,state:t}),r=stateToken(t),i={...n,isSubscribed:async()=>await e.resolveSession(r)!==void 0},a;try{a=await e.onMessage(i,e.activity)}catch(e){log.error(`Teams message handler failed`,{error:e});return}if(a==null)return;let o=collectTeamsFileParts(e.activity.attachments,e.filesPolicy),s=buildTeamsTurnMessage(e.activity.text,o),c={activityId:e.activity.id,botId:e.activity.recipient.id,channelId:e.activity.teamsChannelId,conversationId:e.activity.conversation.id,conversationType:e.activity.conversationType,isMentioned:e.activity.isBotMentioned,scope:e.activity.scope,teamId:e.activity.teamId,tenantId:e.activity.tenantId,userId:e.activity.from.id,userName:e.activity.from.name},l=a.context??[];try{await e.from(r).send(s,{auth:a.auth,context:[formatTeamsContextBlock(c),...l],state:t,title:a.title})}catch(e){log.error(`Teams message delivery failed`,{error:e})}}async function handleInvoke(e){let t=readTeamsAuthorizationReveal(e.activity);if(t!==null)return Response.json(await revealAuthorization({...e,reveal:t}));if(isTeamsInputResponseActivity(e.activity))return e.waitUntil(dispatchInputResponses({activity:e.activity,config:e.config,onInputResponse:e.onInputResponse,from:e.from})),Response.json(teamsInvokeResponse());if(e.config.onInvoke===void 0)return teamsOk();let n=buildTeamsBinding({config:e.config,state:stateFromActivity(e.activity)}),r=await e.config.onInvoke(n,e.activity);return r instanceof Response?r:r&&typeof r==`object`?Response.json(r):teamsOk()}async function revealAuthorization(e){let t=stateFromActivity(e.activity),n=buildTeamsBinding({config:e.config,state:t}),r=null;try{r=(await e.onInputResponse(n,e.activity))?.auth??null}catch(e){log.error(`Teams sign-in press authorization failed`,{error:e})}let i=cardContinuationToken(e.activity,t,e.reveal.replyToActivityId??null);return await answerTeamsAuthorizationReveal({adaptiveCardVersion:e.config.adaptiveCardVersion??`1.5`,auth:r,presser:e.activity.from,resolveSession:()=>e.resolveSession(i),reveal:e.reveal})}async function dispatchInputResponses(e){let t=deriveTeamsInputResponses(e.activity);if(t.length===0)return;let n=stateFromActivity(e.activity),r=buildTeamsBinding({config:e.config,state:n}),i;try{i=await e.onInputResponse(r,e.activity)}catch(e){log.error(`Teams input response authorization failed`,{error:e});return}if(i!==null)try{await e.from(cardContinuationToken(e.activity,n,readTeamsInputReplyToActivityId(e.activity))).respond(t,{auth:i.auth,state:approvalResponseStatePatch(e.activity,i.auth)})}catch(e){log.error(`Teams input response delivery failed`,{error:e})}}function stateFromActivity(e){let t=normalizeTeamsContinuationAddress({conversationId:e.conversation.id,replyToActivityId:teamsThreadRootActivityId(e)});return{bot:e.recipient,channelId:e.teamsChannelId??null,conversationId:e.conversation.id,conversationType:e.conversationType??e.scope,pendingAuthActivityId:null,replyToActivityId:t.replyToActivityId,serviceUrl:e.serviceUrl,teamId:e.teamId??null,tenantId:e.tenantId??null,triggeringUser:e.from}}function cardContinuationToken(e,t,n){return n===null?stateToken(t):teamsContinuationToken({conversationId:e.conversation.id,replyToActivityId:n,tenantId:e.tenantId})}function approvalResponseStatePatch(e,t){if(t?.principalId===void 0||!isTeamsToolApprovalResponseActivity(e))return;let n=deriveTeamsInputResponses(e)[0]?.requestId,r=e.replyToId;return n===void 0||r===void 0?{approvalResponderAccounts:{[t.principalId]:e.from}}:{approvalResponderAccounts:{[t.principalId]:e.from},pendingPromptCards:{[n]:{activityId:r,prompt:readTeamsToolApprovalPrompt(e)??`Tool approval`}}}}function defaultOnInputResponse(e,t){return{auth:defaultTeamsAuth(t)}}function rejectInput(){return null}function initialTeamsState(){return{bot:null,channelId:null,conversationId:null,conversationType:null,approvalResponderAccounts:{},pendingPromptCards:{},pendingAuthActivityId:null,replyToActivityId:null,serviceUrl:null,teamId:null,tenantId:null,triggeringUser:null}}function stateToken(e){let t=e.conversationId??``;if(!t)throw Error(`teamsChannel: missing conversation id.`);return teamsContinuationToken({conversationId:t,replyToActivityId:e.replyToActivityId,tenantId:e.tenantId})}function buildOutboundActivity(e,t){if(typeof t!=`string`&&`type`in t&&t.type===`typing`)return t;let n=normalizeTeamsPostInput(t),r=mergeChannelData(e,n.channelData);return{...n,channelData:r,conversation:e.conversationId?{id:e.conversationId}:void 0,from:e.bot??void 0,replyToId:e.replyToActivityId??void 0,type:`message`}}function buildUpdateActivity(e,t){if(typeof t!=`string`&&`type`in t&&t.type===`typing`)throw Error(`teamsChannel: cannot replace an activity with a typing indicator.`);let{channelData:n,conversation:r,from:i,recipient:a,replyToId:o,type:s,...c}=normalizeTeamsPostInput(t);return{...c,type:`message`}}function mergeChannelData(e,t){let n={...t};return e.tenantId&&(n.tenant={id:e.tenantId}),e.teamId&&(n.team={id:e.teamId}),e.channelId&&(n.channel={id:e.channelId}),Object.keys(n).length>0?parseJsonObject(n):void 0}function teamsOk(){return new Response(`ok`,{status:200})}export{teamsChannel};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createLogger}from"#internal/logging.js";import{TELEGRAM_AUTHORIZATION_CALLBACK_PREFIX,renderTelegramAuthorizationPrompt}from"#public/channels/telegram/authorization.js";const log=createLogger(`telegram.authorization-callback`);async function dispatchTelegramAuthorizationCallback(e){if(e.query.data?.slice(TELEGRAM_AUTHORIZATION_CALLBACK_PREFIX.length)!==e.query.from.id){await e.telegram.telegram.answerCallbackQuery({callbackQueryId:e.query.id,showAlert:!0,text:`Only the requester can authorize this connection.`});return}if(e.query.message&&e.state.chatId)try{let t=await e.resolveSession(e.continuationToken);if(t===void 0){await inactiveAuthorization(e.telegram.telegram,e.query.id);return}let n=await findPendingAuthorization(t);if(n===void 0){await inactiveAuthorization(e.telegram.telegram,e.query.id);return}await e.telegram.telegram.postEphemeral(e.query.from.id,renderTelegramAuthorizationPrompt(n
|
|
1
|
+
import{createLogger}from"#internal/logging.js";import{findPendingAuthorization}from"#channel/pending-authorization.js";import{TELEGRAM_AUTHORIZATION_CALLBACK_PREFIX,renderTelegramAuthorizationPrompt}from"#public/channels/telegram/authorization.js";const log=createLogger(`telegram.authorization-callback`);async function dispatchTelegramAuthorizationCallback(e){if(e.query.data?.slice(TELEGRAM_AUTHORIZATION_CALLBACK_PREFIX.length)!==e.query.from.id){await e.telegram.telegram.answerCallbackQuery({callbackQueryId:e.query.id,showAlert:!0,text:`Only the requester can authorize this connection.`});return}if(e.query.message&&e.state.chatId)try{let t=await e.resolveSession(e.continuationToken);if(t===void 0){await inactiveAuthorization(e.telegram.telegram,e.query.id);return}let n=await findPendingAuthorization(t,e=>e.candidateId===void 0);if(n===void 0){await inactiveAuthorization(e.telegram.telegram,e.query.id);return}await e.telegram.telegram.postEphemeral(e.query.from.id,renderTelegramAuthorizationPrompt(n),{callbackQueryId:e.query.id}),await e.telegram.telegram.answerCallbackQuery({callbackQueryId:e.query.id,text:`Sign-in prompt sent privately.`})}catch(e){log.error(`Telegram authorization callback delivery failed`,{error:e})}}async function inactiveAuthorization(e,t){await e.answerCallbackQuery({callbackQueryId:t,showAlert:!0,text:`This authorization request is no longer active.`})}export{dispatchTelegramAuthorizationCallback};
|
|
@@ -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
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{EVE_NEXT_HOST,resolveEveDestinationPrefix}from"./server.js";import{ensureEveVercelOutputConfig}from"./vercel-output-config.js";import{EVE_ROUTE_PREFIX}from"#protocol/routes.js";import{joinEveRoutePath}from"#shared/eve-route-path.js";import{resolve}from"node:path";import{findEveProjectContext}from"#internal/project-context.js";import{assertValidPublicAgentName}from"#internal/agent-name.js";import{quoteVercelShellArgument,toVercelRelativePath}from"#internal/vercel/build-command.js";import{resolveEveBinaryPath}from"#shared/resolve-eve-binary.js";import{resolveDevServerTimeout}from"#shared/framework-eve-server.js";const EVE_NEXT_SERVICE_PREFIX=`/_eve_internal/eve`,EVE_NEXT_PRODUCTION_PORT_ENV=`EVE_NEXT_PRODUCTION_PORT`;function resolveApplicationRoot(e){return e===void 0||e.length===0?process.cwd():resolve(process.cwd(),e)}function normalizeRoutePrefix(e){let t=(e.startsWith(`/`)?e:`/${e}`).replace(/\/+$/,``);if(t.length===0)throw Error(`eve Next.js service prefix cannot resolve to the root route.`);return t}function joinRoutePrefix(e,t){return`${e.replace(/\/+$/,``)}/${t.replace(/^\/+/,``)}`}function createNamedAgentRoutePrefix(e){return joinRoutePrefix(`/eve`,e)}function createNamedAgentServicePrefix(e,t){return joinRoutePrefix(e,t)}function createAgentRewriteSource(e){return joinEveRoutePath(e,`${EVE_ROUTE_PREFIX}/:path+`)}function normalizeOrigin(e){return new URL(e.trim()).origin}function readLocalProductionPort(e){let t=process.env[EVE_NEXT_PRODUCTION_PORT_ENV],n=t===void 0||t.trim().length===0?4274:Number.parseInt(t,10);if(t!==void 0&&t.trim().length>0&&String(n)!==t.trim())throw Error(`${EVE_NEXT_PRODUCTION_PORT_ENV} must be an integer between 1 and 65535.`);let r=n+e;if(r<1||r>65535)throw Error(`${EVE_NEXT_PRODUCTION_PORT_ENV} plus the eve agent count exceeds 65535.`);return r}function resolveProductionDestination(e){if(process.env.VERCEL)return{destinationPrefix:e.servicePrefix};let t=process.env.EVE_NEXT_PRODUCTION_ORIGIN;if(t!==void 0&&t.trim().length>0)return{destinationPrefix:joinRoutePrefix(normalizeOrigin(t),e.servicePrefix)};let n=`http://127.0.0.1:${String(readLocalProductionPort(e.localProductionPortOffset))}`;return{destinationPrefix:n,localServerOrigin:n}}function createEveRewriteRule(e){let t=createAgentRewriteSource(e.publicRoutePrefix);return{destination:joinRoutePrefix(e.destinationPrefix,`${EVE_ROUTE_PREFIX}/:path+`),source:t}}async function resolveExistingRewrites(e){return await e?.()}function mergeRewriteRules(e,t){return e===void 0?{beforeFiles:t}:isRewriteSections(e)?{...e,beforeFiles:[...t,...e.beforeFiles??[]]}:{afterFiles:e,beforeFiles:t}}function isRewriteSections(e){return!Array.isArray(e)}async function resolveNextConfig(e,t,n){return typeof e==`function`?await e(t,n):e}function assertValidAgentName(e){assertValidPublicAgentName(e,`eve Next.js agent name`)}function assertValidWithEveOptions(e){if(e.eveRoot!==void 0&&e.agents!==void 0)throw Error(`withEve cannot combine eveRoot with agents. Use one configuration form.`);if(e.agents===void 0)return;let t=Object.keys(e.agents);if(t.length===0)throw Error(`withEve agents must contain at least one named eve agent.`);for(let e of t)assertValidAgentName(e)}function createDefaultBuildCommand(e){let t=toVercelRelativePath(e.agentRoot,resolveEveBinaryPath(e.agentRoot));return`node ${quoteVercelShellArgument(t)} build`}async function normalizeAgentsConfig(e){let t=normalizeRoutePrefix(e.servicePrefix??`/_eve_internal/eve`),resolveBuildCommand=(t,n)=>n??e.eveBuildCommand??createDefaultBuildCommand({agentRoot:t});if(e.agents===void 0){let n=resolveApplicationRoot(e.eveRoot),r=await findEveProjectContext(n);if(r?.kind===`workspace`&&r.workspace.root===n&&r.workspace.members.length>0)return r.workspace.members.map((e,n)=>({appRoot:e.appRoot,buildCommand:resolveBuildCommand(e.appRoot,void 0),localProductionPortOffset:n,name:e.name,publicRoutePrefix:createNamedAgentRoutePrefix(e.name),servicePrefix:createNamedAgentServicePrefix(t,e.name),workspaceMember:!0}));let i=n;return[{appRoot:i,buildCommand:resolveBuildCommand(i,void 0),localProductionPortOffset:0,publicRoutePrefix:``,servicePrefix:t}]}let n=Object.entries(e.agents);if(n.length===0)throw Error(`withEve agents must contain at least one named eve agent.`);return n.map(([e,n],r)=>{assertValidAgentName(e);let i=typeof n==`string`?{root:n}:n,a=resolveApplicationRoot(i.root);return{appRoot:a,buildCommand:resolveBuildCommand(a,i.buildCommand),localProductionPortOffset:r,name:e,publicRoutePrefix:createNamedAgentRoutePrefix(e),servicePrefix:normalizeRoutePrefix(i.servicePrefix??createNamedAgentServicePrefix(t,e))}})}async function buildAgentWorkspaceExtensions(e){let[{buildWorkspaceExtensions:t},{DiscoveryProjectResolutionError:n}]=await Promise.all([import(`#internal/nitro/host/workspace-extensions.js`),import(`#discover/project.js`)]);for(let r of e)try{await t(r.appRoot)}catch(e){if(e instanceof n)continue;throw e}}function withEve(e,t={}){let n=process.cwd(),r=resolveDevServerTimeout(t.devServerTimeoutMs,EVE_NEXT_HOST);return assertValidWithEveOptions(t),async function(i,a){let[o,s]=await Promise.all([normalizeAgentsConfig(t),resolveNextConfig(e,i,a)]);i===`phase-production-build`&&await buildAgentWorkspaceExtensions(o);let c=s.rewrites,l=await ensureEveVercelOutputConfig({agents:o.map(e=>{let t={appRoot:e.appRoot,buildCommand:e.buildCommand,name:e.name,publicRoutePrefix:e.publicRoutePrefix,servicePrefix:e.servicePrefix};return e.workspaceMember===!0&&(t.workspaceMember=!0),t}),nextRoot:n});if(process.env.VERCEL)return s;let u=new Map(l.agents.map(e=>[e.name,e])),d=o.map(e=>{let t=u.get(e.name),n=resolveProductionDestination({localProductionPortOffset:e.localProductionPortOffset,servicePrefix:t?.servicePrefix??e.servicePrefix});return{...e,productionDestination:n}});return{...s,async rewrites(){let[e,t]=await Promise.all([resolveExistingRewrites(c),Promise.all(d.map(async e=>createEveRewriteRule({destinationPrefix:await resolveEveDestinationPrefix({appRoot:e.appRoot,devServerTimeoutMs:r,logLabel:e.name,
|
|
1
|
+
import{EVE_NEXT_HOST,ensureEveProductionServer,resolveEveDestinationPrefix}from"./server.js";import{ensureEveVercelOutputConfig}from"./vercel-output-config.js";import{EVE_ROUTE_PREFIX}from"#protocol/routes.js";import{joinEveRoutePath}from"#shared/eve-route-path.js";import{resolve}from"node:path";import{findEveProjectContext}from"#internal/project-context.js";import{assertValidPublicAgentName}from"#internal/agent-name.js";import{quoteVercelShellArgument,toVercelRelativePath}from"#internal/vercel/build-command.js";import{resolveEveBinaryPath}from"#shared/resolve-eve-binary.js";import{resolveDevServerTimeout}from"#shared/framework-eve-server.js";const EVE_NEXT_SERVICE_PREFIX=`/_eve_internal/eve`,EVE_NEXT_PRODUCTION_PORT_ENV=`EVE_NEXT_PRODUCTION_PORT`;function resolveApplicationRoot(e){return e===void 0||e.length===0?process.cwd():resolve(process.cwd(),e)}function normalizeRoutePrefix(e){let t=(e.startsWith(`/`)?e:`/${e}`).replace(/\/+$/,``);if(t.length===0)throw Error(`eve Next.js service prefix cannot resolve to the root route.`);return t}function joinRoutePrefix(e,t){return`${e.replace(/\/+$/,``)}/${t.replace(/^\/+/,``)}`}function createNamedAgentRoutePrefix(e){return joinRoutePrefix(`/eve`,e)}function createNamedAgentServicePrefix(e,t){return joinRoutePrefix(e,t)}function createAgentRewriteSource(e){return joinEveRoutePath(e,`${EVE_ROUTE_PREFIX}/:path+`)}function normalizeOrigin(e){return new URL(e.trim()).origin}function readLocalProductionPort(e){let t=process.env[EVE_NEXT_PRODUCTION_PORT_ENV],n=t===void 0||t.trim().length===0?4274:Number.parseInt(t,10);if(t!==void 0&&t.trim().length>0&&String(n)!==t.trim())throw Error(`${EVE_NEXT_PRODUCTION_PORT_ENV} must be an integer between 1 and 65535.`);let r=n+e;if(r<1||r>65535)throw Error(`${EVE_NEXT_PRODUCTION_PORT_ENV} plus the eve agent count exceeds 65535.`);return r}function resolveProductionDestination(e){if(process.env.VERCEL)return{destinationPrefix:e.servicePrefix};let t=process.env.EVE_NEXT_PRODUCTION_ORIGIN;if(t!==void 0&&t.trim().length>0)return{destinationPrefix:joinRoutePrefix(normalizeOrigin(t),e.servicePrefix)};let n=`http://127.0.0.1:${String(readLocalProductionPort(e.localProductionPortOffset))}`;return{destinationPrefix:n,localServerOrigin:n}}function createEveRewriteRule(e){let t=createAgentRewriteSource(e.publicRoutePrefix);return{destination:joinRoutePrefix(e.destinationPrefix,`${EVE_ROUTE_PREFIX}/:path+`),source:t}}async function resolveExistingRewrites(e){return await e?.()}function mergeRewriteRules(e,t){return e===void 0?{beforeFiles:t}:isRewriteSections(e)?{...e,beforeFiles:[...t,...e.beforeFiles??[]]}:{afterFiles:e,beforeFiles:t}}function isRewriteSections(e){return!Array.isArray(e)}async function resolveNextConfig(e,t,n){return typeof e==`function`?await e(t,n):e}function assertValidAgentName(e){assertValidPublicAgentName(e,`eve Next.js agent name`)}function assertValidWithEveOptions(e){if(e.eveRoot!==void 0&&e.agents!==void 0)throw Error(`withEve cannot combine eveRoot with agents. Use one configuration form.`);if(e.agents===void 0)return;let t=Object.keys(e.agents);if(t.length===0)throw Error(`withEve agents must contain at least one named eve agent.`);for(let e of t)assertValidAgentName(e)}function createDefaultBuildCommand(e){let t=toVercelRelativePath(e.agentRoot,resolveEveBinaryPath(e.agentRoot));return`node ${quoteVercelShellArgument(t)} build`}async function normalizeAgentsConfig(e){let t=normalizeRoutePrefix(e.servicePrefix??`/_eve_internal/eve`),resolveBuildCommand=(t,n)=>n??e.eveBuildCommand??createDefaultBuildCommand({agentRoot:t});if(e.agents===void 0){let n=resolveApplicationRoot(e.eveRoot),r=await findEveProjectContext(n);if(r?.kind===`workspace`&&r.workspace.root===n&&r.workspace.members.length>0)return r.workspace.members.map((e,n)=>({appRoot:e.appRoot,buildCommand:resolveBuildCommand(e.appRoot,void 0),localProductionPortOffset:n,name:e.name,publicRoutePrefix:createNamedAgentRoutePrefix(e.name),servicePrefix:createNamedAgentServicePrefix(t,e.name),workspaceMember:!0}));let i=n;return[{appRoot:i,buildCommand:resolveBuildCommand(i,void 0),localProductionPortOffset:0,publicRoutePrefix:``,servicePrefix:t}]}let n=Object.entries(e.agents);if(n.length===0)throw Error(`withEve agents must contain at least one named eve agent.`);return n.map(([e,n],r)=>{assertValidAgentName(e);let i=typeof n==`string`?{root:n}:n,a=resolveApplicationRoot(i.root);return{appRoot:a,buildCommand:resolveBuildCommand(a,i.buildCommand),localProductionPortOffset:r,name:e,publicRoutePrefix:createNamedAgentRoutePrefix(e),servicePrefix:normalizeRoutePrefix(i.servicePrefix??createNamedAgentServicePrefix(t,e))}})}async function buildAgentWorkspaceExtensions(e){let[{buildWorkspaceExtensions:t},{DiscoveryProjectResolutionError:n}]=await Promise.all([import(`#internal/nitro/host/workspace-extensions.js`),import(`#discover/project.js`)]);for(let r of e)try{await t(r.appRoot)}catch(e){if(e instanceof n)continue;throw e}}function withEve(e,t={}){let n=process.cwd(),r=resolveDevServerTimeout(t.devServerTimeoutMs,EVE_NEXT_HOST);return assertValidWithEveOptions(t),async function(i,a){let[o,s]=await Promise.all([normalizeAgentsConfig(t),resolveNextConfig(e,i,a)]);i===`phase-production-build`&&await buildAgentWorkspaceExtensions(o);let c=s.rewrites,l=await ensureEveVercelOutputConfig({agents:o.map(e=>{let t={appRoot:e.appRoot,buildCommand:e.buildCommand,name:e.name,publicRoutePrefix:e.publicRoutePrefix,servicePrefix:e.servicePrefix};return e.workspaceMember===!0&&(t.workspaceMember=!0),t}),nextRoot:n});if(process.env.VERCEL)return s;let u=new Map(l.agents.map(e=>[e.name,e])),d=o.map(e=>{let t=u.get(e.name),n=resolveProductionDestination({localProductionPortOffset:e.localProductionPortOffset,servicePrefix:t?.servicePrefix??e.servicePrefix});return{...e,productionDestination:n}});return i===`phase-production-server`&&await Promise.all(d.map(async({appRoot:e,productionDestination:t})=>{t.localServerOrigin!==void 0&&await ensureEveProductionServer({appRoot:e,origin:t.localServerOrigin})})),{...s,async rewrites(){let[e,t]=await Promise.all([resolveExistingRewrites(c),Promise.all(d.map(async e=>createEveRewriteRule({destinationPrefix:await resolveEveDestinationPrefix({appRoot:e.appRoot,devServerTimeoutMs:r,logLabel:e.name,productionDestinationPrefix:e.productionDestination.destinationPrefix,workspaceAgentName:e.workspaceMember===!0?e.name:void 0}),publicRoutePrefix:e.publicRoutePrefix})))]);return mergeRewriteRules(e,t)}}}}export{EVE_NEXT_SERVICE_PREFIX,withEve};
|
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
import { type EveFrameworkHost } from "#shared/framework-eve-server.js";
|
|
2
2
|
export declare const NEXT_PHASE_PRODUCTION_BUILD = "phase-production-build";
|
|
3
|
+
export declare const NEXT_PHASE_PRODUCTION_SERVER = "phase-production-server";
|
|
3
4
|
export declare const EVE_NEXT_HOST: EveFrameworkHost;
|
|
5
|
+
/**
|
|
6
|
+
* Start the built eve server for `appRoot` on `origin` once per process. Named
|
|
7
|
+
* agents may share a root, each on its own port, so both identify the server.
|
|
8
|
+
* `next start` calls this while loading config, because Next serves the
|
|
9
|
+
* rewrites saved by `next build` and never calls `rewrites()` again.
|
|
10
|
+
*/
|
|
11
|
+
export declare function ensureEveProductionServer(input: {
|
|
12
|
+
readonly appRoot: string;
|
|
13
|
+
readonly origin: string;
|
|
14
|
+
}): Promise<void>;
|
|
4
15
|
export declare function resolveEveDestinationPrefix(input: {
|
|
5
16
|
readonly appRoot: string;
|
|
6
17
|
readonly devServerTimeoutMs?: number;
|
|
7
18
|
readonly logLabel?: string;
|
|
8
|
-
readonly phase: string;
|
|
9
19
|
readonly productionDestinationPrefix: string;
|
|
10
|
-
readonly productionServerOrigin?: string;
|
|
11
20
|
/** Workspace member selected when spawning the local eve dev server. */
|
|
12
21
|
readonly workspaceAgentName?: string;
|
|
13
22
|
}): Promise<string>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{resolveSharedEveDevServer,startEveProductionServer}from"#shared/framework-eve-server.js";const NEXT_PHASE_PRODUCTION_BUILD=`phase-production-build`,EVE_NEXT_HOST={label:`Next.js`,slug:`next`},globalStateSymbol=Symbol.for(`eve.next.state`);function getGlobalState(){let e=globalThis;return e[globalStateSymbol]??={servers:new Map},e[globalStateSymbol]}function readEveBaseUrlEnvironment(){let e=process.env.EVE_BASE_URL;if(e!==void 0&&e.trim().length!==0)return new URL(e).origin}async function
|
|
1
|
+
import{resolveSharedEveDevServer,startEveProductionServer}from"#shared/framework-eve-server.js";const NEXT_PHASE_PRODUCTION_BUILD=`phase-production-build`,NEXT_PHASE_PRODUCTION_SERVER=`phase-production-server`,EVE_NEXT_HOST={label:`Next.js`,slug:`next`},globalStateSymbol=Symbol.for(`eve.next.state`);function getGlobalState(){let e=globalThis;return e[globalStateSymbol]??={servers:new Map},e[globalStateSymbol]}function readEveBaseUrlEnvironment(){let e=process.env.EVE_BASE_URL;if(e!==void 0&&e.trim().length!==0)return new URL(e).origin}async function ensureEveProductionServer(e){let t=getGlobalState(),n=`production:${e.appRoot}:${e.origin}`,r=t.servers.get(n);r===void 0&&(r=startEveProductionServer({appRoot:e.appRoot,host:EVE_NEXT_HOST,origin:e.origin}).catch(e=>{throw t.servers.delete(n),e}),t.servers.set(n,r)),await r}async function resolveEveDestinationPrefix(e){if(process.env.NODE_ENV===`production`)return e.productionDestinationPrefix;let t=readEveBaseUrlEnvironment();if(t!==void 0)return t;if(process.env.NODE_ENV!==`development`)return e.productionDestinationPrefix;let n=getGlobalState(),r=`dev:${e.appRoot}`,i=n.servers.get(r);return i===void 0&&(i=resolveSharedEveDevServer({appRoot:e.appRoot,host:EVE_NEXT_HOST,logLabel:e.logLabel,timeoutMs:e.devServerTimeoutMs,workspaceAgentName:e.workspaceAgentName}).catch(e=>{throw n.servers.delete(r),e}),n.servers.set(r,i)),(await i).origin}export{EVE_NEXT_HOST,NEXT_PHASE_PRODUCTION_BUILD,NEXT_PHASE_PRODUCTION_SERVER,ensureEveProductionServer,resolveEveDestinationPrefix};
|
|
@@ -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};
|