eve 0.18.2 → 0.20.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.
Files changed (129) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/src/cli/dev/tui/setup-selection-input.js +1 -1
  3. package/dist/src/cli/dev/tui/stream-format.d.ts +4 -0
  4. package/dist/src/cli/dev/tui/stream-format.js +1 -1
  5. package/dist/src/cli/dev/tui/terminal-renderer.js +1 -1
  6. package/dist/src/cli/dev/tui/test/mock-terminal.d.ts +2 -0
  7. package/dist/src/cli/dev/tui/test/mock-terminal.js +1 -1
  8. package/dist/src/compiled/.vendor-stamp.json +2 -1
  9. package/dist/src/compiled/@chat-adapter/slack/index.js +29 -32
  10. package/dist/src/compiled/@chat-adapter/twilio/LICENSE +9 -0
  11. package/dist/src/compiled/@chat-adapter/twilio/api.d.ts +108 -0
  12. package/dist/src/compiled/@chat-adapter/twilio/api.js +1 -0
  13. package/dist/src/compiled/@chat-adapter/twilio/format.d.ts +12 -0
  14. package/dist/src/compiled/@chat-adapter/twilio/format.js +1 -0
  15. package/dist/src/compiled/@chat-adapter/twilio/index.d.ts +84 -0
  16. package/dist/src/compiled/@chat-adapter/twilio/index.js +1 -0
  17. package/dist/src/compiled/@chat-adapter/twilio/voice.d.ts +44 -0
  18. package/dist/src/compiled/@chat-adapter/twilio/voice.js +1 -0
  19. package/dist/src/compiled/@chat-adapter/twilio/webhook.d.ts +18 -0
  20. package/dist/src/compiled/@chat-adapter/twilio/webhook.js +1 -0
  21. package/dist/src/compiled/_chunks/node/chunk-5OX2R7AJ-CxVV-owP.js +1 -0
  22. package/dist/src/compiled/_chunks/node/chunk-PFJ2G64A-B79fSABi.js +1 -0
  23. package/dist/src/compiled/_chunks/node/chunk-QZV7YRVM-Dzw4WEBv.js +1 -0
  24. package/dist/src/compiled/_chunks/node/dist-RHRJZ03Q.js +4 -0
  25. package/dist/src/context/build-base-tool-context.d.ts +9 -0
  26. package/dist/src/context/build-base-tool-context.js +1 -0
  27. package/dist/src/context/build-dynamic-tools.js +1 -1
  28. package/dist/src/context/dynamic-tool-lifecycle.js +1 -1
  29. package/dist/src/evals/cli/eval.js +4 -1
  30. package/dist/src/evals/runner/discover.d.ts +10 -0
  31. package/dist/src/evals/runner/discover.js +1 -1
  32. package/dist/src/execution/durable-session-migrations/turn-workflow.d.ts +2 -0
  33. package/dist/src/execution/node-step.d.ts +2 -0
  34. package/dist/src/execution/node-step.js +1 -1
  35. package/dist/src/execution/sandbox/abort-bound-session.d.ts +6 -0
  36. package/dist/src/execution/sandbox/abort-bound-session.js +1 -0
  37. package/dist/src/execution/sandbox/active-handles.d.ts +29 -0
  38. package/dist/src/execution/sandbox/active-handles.js +1 -0
  39. package/dist/src/execution/sandbox/bindings/docker.d.ts +2 -2
  40. package/dist/src/execution/sandbox/bindings/docker.js +1 -1
  41. package/dist/src/execution/sandbox/bindings/just-bash-runtime.js +1 -1
  42. package/dist/src/execution/sandbox/bindings/microsandbox-lifecycle.js +1 -1
  43. package/dist/src/execution/sandbox/bindings/microsandbox-provider-state.d.ts +11 -0
  44. package/dist/src/execution/sandbox/bindings/microsandbox-provider-state.js +1 -0
  45. package/dist/src/execution/sandbox/bindings/microsandbox-runtime.d.ts +7 -4
  46. package/dist/src/execution/sandbox/bindings/microsandbox-runtime.js +1 -1
  47. package/dist/src/execution/sandbox/bindings/vercel.js +1 -1
  48. package/dist/src/execution/sandbox/ensure.js +1 -1
  49. package/dist/src/execution/sandbox/require-sandbox.d.ts +3 -1
  50. package/dist/src/execution/sandbox/require-sandbox.js +1 -1
  51. package/dist/src/execution/tool-auth.d.ts +2 -1
  52. package/dist/src/execution/tool-auth.js +1 -1
  53. package/dist/src/execution/web-fetch/tool.d.ts +6 -1
  54. package/dist/src/execution/web-fetch/tool.js +1 -1
  55. package/dist/src/execution/workflow-steps.js +1 -1
  56. package/dist/src/harness/action-result-helpers.d.ts +11 -1
  57. package/dist/src/harness/action-result-helpers.js +1 -1
  58. package/dist/src/harness/attachment-staging.js +1 -1
  59. package/dist/src/harness/compaction.d.ts +1 -1
  60. package/dist/src/harness/compaction.js +1 -1
  61. package/dist/src/harness/emission.d.ts +1 -0
  62. package/dist/src/harness/emission.js +1 -1
  63. package/dist/src/harness/execute-tool.d.ts +2 -1
  64. package/dist/src/harness/model-call-error.js +1 -1
  65. package/dist/src/harness/step-hooks.d.ts +1 -1
  66. package/dist/src/harness/step-hooks.js +1 -1
  67. package/dist/src/harness/tool-loop.js +1 -1
  68. package/dist/src/harness/tools.d.ts +2 -3
  69. package/dist/src/harness/tools.js +1 -1
  70. package/dist/src/harness/turn-cancellation.d.ts +8 -0
  71. package/dist/src/harness/turn-cancellation.js +1 -0
  72. package/dist/src/harness/turn-tag-state.d.ts +2 -0
  73. package/dist/src/harness/turn-tag-state.js +1 -1
  74. package/dist/src/harness/types.d.ts +2 -0
  75. package/dist/src/internal/application/package.js +1 -1
  76. package/dist/src/internal/nitro/dev-runtime-source-snapshot-copy.js +1 -1
  77. package/dist/src/internal/nitro/host/configure-nitro-routes.js +1 -1
  78. package/dist/src/internal/nitro/host/create-application-nitro.js +1 -1
  79. package/dist/src/internal/nitro/host/dev-authored-source-watcher.js +1 -1
  80. package/dist/src/internal/nitro/host/sandbox-shutdown-plugin.d.ts +40 -0
  81. package/dist/src/internal/nitro/host/sandbox-shutdown-plugin.js +1 -0
  82. package/dist/src/internal/nitro/host/start-production-server.js +1 -1
  83. package/dist/src/internal/nitro/routes/index.d.ts +7 -2
  84. package/dist/src/internal/nitro/routes/index.js +78 -48
  85. package/dist/src/protocol/message.d.ts +14 -2
  86. package/dist/src/protocol/message.js +2 -2
  87. package/dist/src/public/channels/chat-sdk/chatSdkChannel.d.ts +141 -0
  88. package/dist/src/public/channels/chat-sdk/chatSdkChannel.js +3 -0
  89. package/dist/src/public/channels/chat-sdk/index.d.ts +1 -0
  90. package/dist/src/public/channels/chat-sdk/index.js +1 -0
  91. package/dist/src/public/channels/discord/discordChannel.js +1 -1
  92. package/dist/src/public/channels/index.d.ts +2 -0
  93. package/dist/src/public/channels/slack/api.js +1 -1
  94. package/dist/src/public/channels/twilio/api.d.ts +8 -29
  95. package/dist/src/public/channels/twilio/api.js +1 -1
  96. package/dist/src/public/channels/twilio/inbound.d.ts +10 -19
  97. package/dist/src/public/channels/twilio/inbound.js +2 -2
  98. package/dist/src/public/channels/twilio/routing.d.ts +13 -0
  99. package/dist/src/public/channels/twilio/routing.js +1 -0
  100. package/dist/src/public/channels/twilio/twilioChannel.js +1 -1
  101. package/dist/src/public/channels/twilio/twiml.d.ts +5 -36
  102. package/dist/src/public/channels/twilio/twiml.js +1 -1
  103. package/dist/src/public/channels/twilio/verify.d.ts +12 -33
  104. package/dist/src/public/channels/twilio/verify.js +1 -1
  105. package/dist/src/public/definitions/approval.d.ts +4 -1
  106. package/dist/src/public/definitions/tool.d.ts +7 -0
  107. package/dist/src/public/tools/internal.d.ts +3 -2
  108. package/dist/src/public/tools/internal.js +1 -1
  109. package/dist/src/runtime/connections/mcp-client.d.ts +2 -2
  110. package/dist/src/runtime/connections/mcp-client.js +1 -1
  111. package/dist/src/runtime/connections/openapi-client.d.ts +2 -2
  112. package/dist/src/runtime/connections/openapi-client.js +1 -1
  113. package/dist/src/runtime/connections/types.d.ts +6 -1
  114. package/dist/src/runtime/framework-tools/bash.js +1 -1
  115. package/dist/src/runtime/framework-tools/connection-search-dynamic.js +1 -1
  116. package/dist/src/runtime/framework-tools/glob.js +1 -1
  117. package/dist/src/runtime/framework-tools/grep.js +1 -1
  118. package/dist/src/runtime/framework-tools/read-file.js +1 -1
  119. package/dist/src/runtime/framework-tools/web-fetch.js +1 -1
  120. package/dist/src/runtime/framework-tools/write-file.js +1 -1
  121. package/dist/src/sandbox/state.d.ts +0 -1
  122. package/dist/src/setup/scaffold/create/project.js +1 -1
  123. package/dist/src/shared/dynamic-tool-definition.d.ts +4 -1
  124. package/dist/src/shared/sandbox-backend.d.ts +7 -1
  125. package/dist/src/shared/tool-definition.d.ts +1 -1
  126. package/docs/sandbox.mdx +3 -1
  127. package/docs/tools/human-in-the-loop.md +1 -1
  128. package/docs/tools/overview.mdx +2 -0
  129. package/package.json +7 -1
@@ -1,16 +1,13 @@
1
- /**
2
- * Twilio inbound webhook parsing and prompt shaping.
3
- *
4
- * The channel owns these small data shapes instead of exposing raw
5
- * Twilio webhook payloads as the public API surface.
6
- */
7
- /** Channel-owned representation of one inbound Twilio text message. */
1
+ import { type TwilioMediaPayload } from "#compiled/@chat-adapter/twilio/webhook.js";
2
+ /** Channel-owned representation of one inbound Twilio text or media message. */
8
3
  export interface TwilioTextMessage {
9
4
  readonly from: string;
10
5
  readonly to: string | undefined;
11
6
  readonly body: string;
12
7
  readonly messageSid: string | undefined;
13
8
  readonly accountSid: string | undefined;
9
+ /** MMS media metadata parsed from Twilio's `MediaUrl*` webhook fields. */
10
+ readonly media?: readonly TwilioMediaPayload[];
14
11
  readonly raw: URLSearchParams;
15
12
  }
16
13
  /** Channel-owned representation of one inbound Twilio voice call. */
@@ -31,11 +28,7 @@ export interface TwilioVoiceTranscription {
31
28
  readonly transcriptionSid: string | undefined;
32
29
  readonly raw: URLSearchParams;
33
30
  }
34
- /**
35
- * Inbound identity fields for the model-visible `<twilio_context>` block.
36
- * Stores the caller and receiver numbers, the originating SID, and the turn
37
- * medium (text or voice).
38
- */
31
+ /** Inbound identity fields for the model-visible `<twilio_context>` block. */
39
32
  export interface TwilioInboundContext {
40
33
  readonly from: string;
41
34
  readonly to?: string;
@@ -43,17 +36,15 @@ export interface TwilioInboundContext {
43
36
  readonly callSid?: string;
44
37
  readonly channel: "text" | "voice";
45
38
  }
46
- /** Parses Twilio's incoming-message webhook fields. */
39
+ /** Parses Twilio's incoming-message webhook fields into Eve's text payload. */
47
40
  export declare function parseTwilioTextMessage(params: URLSearchParams): TwilioTextMessage | null;
48
- /** Parses Twilio's incoming-call webhook fields. */
41
+ /** Parses Twilio's incoming-call webhook fields into Eve's voice payload. */
49
42
  export declare function parseTwilioVoiceCall(params: URLSearchParams): TwilioVoiceCall | null;
50
43
  /**
51
- * Parses Twilio voice transcription fields.
44
+ * Parses Twilio speech callbacks.
52
45
  *
53
- * Supports `<Gather input="speech">` (`SpeechResult`), recording
54
- * transcription callbacks (`TranscriptionText`), and real-time
55
- * transcription callbacks (`TranscriptionData` JSON). Real-time partial
56
- * results are ignored until Twilio marks them final.
46
+ * Supports `<Gather input="speech">`, recording transcription callbacks, and
47
+ * real-time transcription callbacks. Real-time partial results are ignored.
57
48
  */
58
49
  export declare function parseTwilioVoiceTranscription(params: URLSearchParams): TwilioVoiceTranscription | null;
59
50
  /** Renders a deterministic `<twilio_context>` block for the model. */
@@ -1,2 +1,2 @@
1
- function parseTwilioTextMessage(e){let t=requiredParam(e,`From`),n=requiredParam(e,`Body`);return!t||!n?null:{accountSid:optionalParam(e,`AccountSid`),body:n,from:t,messageSid:optionalParam(e,`MessageSid`)??optionalParam(e,`SmsMessageSid`),raw:e,to:optionalParam(e,`To`)}}function parseTwilioVoiceCall(e){let t=requiredParam(e,`From`)??requiredParam(e,`Caller`);return t?{accountSid:optionalParam(e,`AccountSid`),callSid:optionalParam(e,`CallSid`),from:t,raw:e,to:optionalParam(e,`To`)??optionalParam(e,`Called`)}:null}function parseTwilioVoiceTranscription(e){let t=requiredParam(e,`From`)??requiredParam(e,`Caller`);if(!t)return null;let n=parseTranscriptionData(optionalParam(e,`TranscriptionData`));if(optionalParam(e,`Final`)===`false`)return null;let r=optionalParam(e,`SpeechResult`)??optionalParam(e,`TranscriptionText`)??n?.transcript??``;return r.trim()?{callSid:optionalParam(e,`CallSid`),confidence:parseConfidence(optionalParam(e,`Confidence`)??n?.confidence),from:t,raw:e,text:r,to:optionalParam(e,`To`)??optionalParam(e,`Called`),transcriptionSid:optionalParam(e,`TranscriptionSid`)}:null}function formatTwilioContextBlock(e){return[`<twilio_context>`,`channel: ${e.channel}`,`response_medium: sms`,`response_instructions: Reply for SMS in plain text. Keep the response concise and avoid Markdown formatting, tables, headings, code fences, and long lists. Ask at most one short follow-up question when more information is needed.`,`from: ${e.from}`,...e.to?[`to: ${e.to}`]:[],...e.messageSid?[`message_sid: ${e.messageSid}`]:[],...e.callSid?[`call_sid: ${e.callSid}`]:[],`</twilio_context>`].join(`
2
- `)}function requiredParam(e,t){let n=e.get(t);return n&&n.trim().length>0?n:null}function optionalParam(e,t){let n=e.get(t);return n===null||n.length===0?void 0:n}function parseTranscriptionData(e){if(!e)return null;try{let t=JSON.parse(e);return{confidence:typeof t.confidence==`number`||typeof t.confidence==`string`?String(t.confidence):void 0,transcript:typeof t.transcript==`string`?t.transcript:void 0}}catch{return null}}function parseConfidence(e){if(e===void 0)return;let t=Number(e);return Number.isFinite(t)?t:void 0}export{formatTwilioContextBlock,parseTwilioTextMessage,parseTwilioVoiceCall,parseTwilioVoiceTranscription};
1
+ import{parseTwilioWebhookBody}from"#compiled/@chat-adapter/twilio/webhook.js";import{parseTwilioVoiceCall as parseTwilioVoiceCall$1,parseTwilioVoiceTranscription as parseTwilioVoiceTranscription$1}from"#compiled/@chat-adapter/twilio/voice.js";function parseTwilioTextMessage(t){let n=parseTwilioWebhookBody(t);return n.kind===`text`?{accountSid:n.accountSid,body:n.body,from:n.from,media:n.media,messageSid:n.messageSid,raw:n.raw,to:n.to}:null}function parseTwilioVoiceCall(e){let n=parseTwilioVoiceCall$1(e);return n?{accountSid:n.accountSid,callSid:n.callSid,from:n.from,raw:n.raw,to:n.to}:null}function parseTwilioVoiceTranscription(e){let t=parseTwilioVoiceTranscription$1(e);return t?.from?{callSid:t.callSid,confidence:t.confidence,from:t.from,raw:t.raw,text:t.text,to:t.to,transcriptionSid:t.transcriptionSid}:null}function formatTwilioContextBlock(e){return[`<twilio_context>`,`channel: ${e.channel}`,`response_medium: sms`,`response_instructions: Reply for SMS in plain text. Keep the response concise and avoid Markdown formatting, tables, headings, code fences, and long lists. Ask at most one short follow-up question when more information is needed.`,`from: ${e.from}`,...e.to?[`to: ${e.to}`]:[],...e.messageSid?[`message_sid: ${e.messageSid}`]:[],...e.callSid?[`call_sid: ${e.callSid}`]:[],`</twilio_context>`].join(`
2
+ `)}export{formatTwilioContextBlock,parseTwilioTextMessage,parseTwilioVoiceCall,parseTwilioVoiceTranscription};
@@ -0,0 +1,13 @@
1
+ import type { TwilioChannelConfig } from "#public/channels/twilio/twilioChannel.js";
2
+ export interface TwilioRoutes {
3
+ readonly messages: string;
4
+ readonly transcription: string;
5
+ readonly voice: string;
6
+ }
7
+ export type TwilioVerifyResult = {
8
+ readonly body: string;
9
+ readonly params: URLSearchParams;
10
+ } | null;
11
+ export declare function buildTwilioRoutes(baseRoute: string): TwilioRoutes;
12
+ export declare function verifyTwilioInbound(req: Request, config: TwilioChannelConfig): Promise<TwilioVerifyResult>;
13
+ export declare function buildTwilioActionUrl(request: Request, config: TwilioChannelConfig, route: string): Promise<string>;
@@ -0,0 +1 @@
1
+ import{createLogger}from"#internal/logging.js";import{verifyTwilioRequest}from"#public/channels/twilio/verify.js";const log=createLogger(`twilio.channel`);function buildTwilioRoutes(e){let t=e.endsWith(`/`)?e.slice(0,-1):e;return{messages:`${t}/messages`,transcription:`${t}/voice/transcription`,voice:`${t}/voice`}}async function verifyTwilioInbound(e,r){try{return await verifyTwilioRequest(e,{authToken:r.credentials?.authToken,webhookUrl:r.webhookUrl})}catch(e){return log.warn(`twilio inbound verification failed`,{error:e}),null}}async function buildTwilioActionUrl(e,t,n){let r=typeof t.publicBaseUrl==`function`?await t.publicBaseUrl(e):t.publicBaseUrl;if(r)return new URL(n,ensureTrailingSlash(r)).toString();let i=new URL(e.url);return i.pathname=n,i.search=``,i.toString()}function ensureTrailingSlash(e){return e.endsWith(`/`)?e:`${e}/`}export{buildTwilioActionUrl,buildTwilioRoutes,verifyTwilioInbound};
@@ -1 +1 @@
1
- import{createLogger}from"#internal/logging.js";import{POST,defineChannel}from"#public/definitions/defineChannel.js";import{verifyTwilioRequest}from"#public/channels/twilio/verify.js";import{callTwilioApi,sendTwilioMessage,twilioContinuationToken,updateTwilioCall}from"#public/channels/twilio/api.js";import{emptyTwilioResponse,gatherSpeechTwilioResponse,sayTwilioResponse}from"#public/channels/twilio/twiml.js";import{defaultEvents,defaultOnText,defaultOnVoice,defaultOnVoiceTranscription}from"#public/channels/twilio/defaults.js";import{formatTwilioContextBlock,parseTwilioTextMessage,parseTwilioVoiceCall,parseTwilioVoiceTranscription}from"#public/channels/twilio/inbound.js";const log=createLogger(`twilio.channel`);function twilioChannel(e){assertAllowFromConfigured(e);let r=buildRoutes(e.route??`/eve/v1/twilio`),i=e.onText??defaultOnText,a=e.onVoice??defaultOnVoice,s=e.onVoiceTranscription??defaultOnVoiceTranscription,l={...defaultEvents,...e.events};return defineChannel({kindHint:`twilio`,state:{from:null,to:null,lastCallSid:null,lastMessageSid:null},metadata(e){return{from:e.from,lastCallSid:e.lastCallSid??null,lastMessageSid:e.lastMessageSid??null,to:e.to}},context(t,n){return rebuildTwilioContext(t,n,e)},routes:[POST(r.messages,async(t,{send:n,waitUntil:r})=>{let a=await verifyInbound(t,e);if(a===null)return new Response(`unauthorized`,{status:401});let o=parseTwilioTextMessage(a.params);return o?await isAllowed(o.from,e.allowFrom)?(r(dispatchText({config:e,message:o,onText:i,send:n})),emptyTwilioResponse()):new Response(`forbidden`,{status:403}):emptyTwilioResponse()}),POST(r.voice,async t=>{let n=await verifyInbound(t,e);if(n===null)return new Response(`unauthorized`,{status:401});let i=parseTwilioVoiceCall(n.params);if(!i)return sayTwilioResponse(`Missing caller information.`);if(!await isAllowed(i.from,e.allowFrom))return new Response(`forbidden`,{status:403});let o=await acceptVoiceCall({call:i,config:e,onVoice:a});if(o===null)return new Response(`forbidden`,{status:403});let s=o??{};return gatherSpeechTwilioResponse({actionUrl:await buildActionUrl(t,e,r.transcription),hints:s.hints??e.voice?.hints,language:s.language??e.voice?.language,profanityFilter:s.profanityFilter??e.voice?.profanityFilter,prompt:s.prompt??e.voice?.prompt??`Please say your message after the tone.`,speechModel:s.speechModel??e.voice?.speechModel,speechTimeout:s.speechTimeout??e.voice?.speechTimeout??`auto`,timeoutSeconds:s.timeoutSeconds??e.voice?.timeoutSeconds,voice:s.voice??e.voice?.voice})}),POST(r.transcription,async(t,{send:n,waitUntil:i})=>{let a=await verifyInbound(t,e);if(a===null)return new Response(`unauthorized`,{status:401});let o=parseTwilioVoiceTranscription(a.params);return o?await isAllowed(o.from,e.allowFrom)?(i(dispatchVoiceTranscription({config:e,onVoiceTranscription:s,send:n,transcription:o})),sayTwilioResponse(e.voice?.acknowledgement??`Thanks. I'll follow up by text.`)):new Response(`forbidden`,{status:403}):gatherSpeechTwilioResponse({actionUrl:await buildActionUrl(t,e,r.transcription),language:e.voice?.language,prompt:e.voice?.prompt??`Please say your message after the tone.`,speechTimeout:e.voice?.speechTimeout??`auto`,timeoutSeconds:e.voice?.timeoutSeconds})})],async receive(t,{send:n}){let r=readString(t.target.phoneNumber);if(!r)throw Error(`twilioChannel().receive requires target.phoneNumber.`);let i=readString(t.target.from)??e.messaging?.from??null;return n(t.message,{auth:t.auth,continuationToken:twilioContinuationToken(r,i??void 0),state:{from:r,lastCallSid:null,lastMessageSid:null,to:i}})},events:l})}function rebuildTwilioContext(e,t,n){return{state:e,twilio:buildTwilioHandle({callSid:e.lastCallSid??void 0,config:n,from:e.from??``,to:e.to??void 0})}}function buildTwilioHandle(e){let t=e.config.api,n=e.config.credentials,r=e.config.messaging?.from??e.to,o=e.config.messaging?.messagingServiceSid,c=e.config.messaging?.statusCallbackUrl;return{callSid:e.callSid,from:e.from,to:e.to,request(e,r){return callTwilioApi({apiBaseUrl:t?.apiBaseUrl,body:r,credentials:n,fetch:t?.fetch,path:e})},sendMessage(i,s){return sendTwilioMessage({apiBaseUrl:t?.apiBaseUrl,body:i,credentials:n,fetch:t?.fetch,from:s?.from??r,messagingServiceSid:s?.messagingServiceSid??o,statusCallbackUrl:s?.statusCallbackUrl??c,to:s?.to??e.from})},updateCall(e,r){return updateTwilioCall({apiBaseUrl:t?.apiBaseUrl,callSid:e,credentials:n,fetch:t?.fetch,twiml:r})}}}function buildRoutes(e){let t=e.endsWith(`/`)?e.slice(0,-1):e;return{messages:`${t}/messages`,transcription:`${t}/voice/transcription`,voice:`${t}/voice`}}function assertAllowFromConfigured(e){if(e?.allowFrom===void 0)throw Error(`twilioChannel requires allowFrom. Use allowFrom: "*" to allow all numbers.`)}async function verifyInbound(e,t){try{return await verifyTwilioRequest(e,{authToken:t.credentials?.authToken,webhookUrl:t.webhookUrl})}catch(e){return log.warn(`twilio inbound verification failed`,{error:e}),null}}async function dispatchText(e){let{message:t}=e,n={twilio:buildTwilioHandle({callSid:void 0,config:e.config,from:t.from,to:t.to})},r;try{r=await e.onText(n,t)}catch(e){log.error(`text handler failed`,{error:e});return}if(r==null)return;let i=formatTwilioContextBlock({channel:`text`,from:t.from,messageSid:t.messageSid,to:t.to});try{await e.send({message:t.body,context:[i]},{auth:r.auth,continuationToken:twilioContinuationToken(t.from,t.to),state:{from:t.from,lastCallSid:null,lastMessageSid:t.messageSid??null,to:t.to??null}})}catch(e){log.error(`text delivery failed`,{error:e})}}async function acceptVoiceCall(e){let{call:t}=e,n={twilio:buildTwilioHandle({callSid:t.callSid,config:e.config,from:t.from,to:t.to})};try{return await e.onVoice(n,t)}catch(e){return log.error(`voice handler failed`,{error:e}),null}}async function dispatchVoiceTranscription(e){let{transcription:t}=e,n={twilio:buildTwilioHandle({callSid:t.callSid,config:e.config,from:t.from,to:t.to})},r;try{r=await e.onVoiceTranscription(n,t)}catch(e){log.error(`voice transcription handler failed`,{error:e});return}if(r==null)return;let i=formatTwilioContextBlock({callSid:t.callSid,channel:`voice`,from:t.from,to:t.to});try{await e.send({message:t.text,context:[i]},{auth:r.auth,continuationToken:twilioContinuationToken(t.from,t.to),state:{from:t.from,lastCallSid:t.callSid??null,lastMessageSid:null,to:t.to??null}})}catch(e){log.error(`voice transcription delivery failed`,{error:e})}}async function isAllowed(e,t){let n=typeof t==`function`?await t():t;return n===`*`?!0:typeof n==`string`?n===e:n.includes(e)}async function buildActionUrl(e,t,n){let r=typeof t.publicBaseUrl==`function`?await t.publicBaseUrl(e):t.publicBaseUrl;if(r)return new URL(n,ensureTrailingSlash(r)).toString();let i=new URL(e.url);return i.pathname=n,i.search=``,i.toString()}function ensureTrailingSlash(e){return e.endsWith(`/`)?e:`${e}/`}function readString(e){return typeof e==`string`&&e.length>0?e:void 0}export{twilioChannel};
1
+ import{createLogger}from"#internal/logging.js";import{GET,POST,defineChannel}from"#public/definitions/defineChannel.js";import"#public/channels/twilio/verify.js";import{callTwilioApi,sendTwilioMessage,twilioContinuationToken,updateTwilioCall}from"#public/channels/twilio/api.js";import{emptyTwilioResponse,gatherSpeechTwilioResponse,sayTwilioResponse}from"#public/channels/twilio/twiml.js";import{defaultEvents,defaultOnText,defaultOnVoice,defaultOnVoiceTranscription}from"#public/channels/twilio/defaults.js";import{formatTwilioContextBlock,parseTwilioTextMessage,parseTwilioVoiceCall,parseTwilioVoiceTranscription}from"#public/channels/twilio/inbound.js";import{buildTwilioActionUrl,buildTwilioRoutes,verifyTwilioInbound}from"#public/channels/twilio/routing.js";const log=createLogger(`twilio.channel`);function twilioChannel(e){assertAllowFromConfigured(e);let i=buildTwilioRoutes(e.route??`/eve/v1/twilio`),a=e.onText??defaultOnText,s=e.onVoice??defaultOnVoice,c=e.onVoiceTranscription??defaultOnVoiceTranscription,l={...defaultEvents,...e.events},u=handleTwilioMessages({config:e,onText:a}),d=handleTwilioVoice({config:e,onVoice:s,routes:i}),f=handleTwilioTranscription({config:e,onVoiceTranscription:c,routes:i});return defineChannel({kindHint:`twilio`,state:{from:null,to:null,lastCallSid:null,lastMessageSid:null},metadata(e){return{from:e.from,lastCallSid:e.lastCallSid??null,lastMessageSid:e.lastMessageSid??null,to:e.to}},context(t,n){return rebuildTwilioContext(t,n,e)},routes:[GET(i.messages,u),POST(i.messages,u),GET(i.voice,d),POST(i.voice,d),GET(i.transcription,f),POST(i.transcription,f)],async receive(t,{send:n}){let r=readString(t.target.phoneNumber);if(!r)throw Error(`twilioChannel().receive requires target.phoneNumber.`);let i=readString(t.target.from)??e.messaging?.from??null;return n(t.message,{auth:t.auth,continuationToken:twilioContinuationToken(r,i??void 0),state:{from:r,lastCallSid:null,lastMessageSid:null,to:i}})},events:l})}function handleTwilioMessages(e){return async(t,{send:n,waitUntil:r})=>{let i=await verifyTwilioInbound(t,e.config);if(i===null)return new Response(`unauthorized`,{status:401});let a=parseTwilioTextMessage(i.params);return a?await isAllowed(a.from,e.config.allowFrom)?(r(dispatchText({config:e.config,message:a,onText:e.onText,send:n})),emptyTwilioResponse()):new Response(`forbidden`,{status:403}):emptyTwilioResponse()}}function handleTwilioVoice(e){return async t=>{let n=await verifyTwilioInbound(t,e.config);if(n===null)return new Response(`unauthorized`,{status:401});let r=parseTwilioVoiceCall(n.params);if(!r)return sayTwilioResponse(`Missing caller information.`);if(!await isAllowed(r.from,e.config.allowFrom))return new Response(`forbidden`,{status:403});let i=await acceptVoiceCall({call:r,config:e.config,onVoice:e.onVoice});if(i===null)return new Response(`forbidden`,{status:403});let a=i??{};return gatherSpeechTwilioResponse({actionUrl:await buildTwilioActionUrl(t,e.config,e.routes.transcription),hints:a.hints??e.config.voice?.hints,language:a.language??e.config.voice?.language,profanityFilter:a.profanityFilter??e.config.voice?.profanityFilter,prompt:a.prompt??e.config.voice?.prompt??`Please say your message after the tone.`,speechModel:a.speechModel??e.config.voice?.speechModel,speechTimeout:a.speechTimeout??e.config.voice?.speechTimeout??`auto`,timeoutSeconds:a.timeoutSeconds??e.config.voice?.timeoutSeconds,voice:a.voice??e.config.voice?.voice})}}function handleTwilioTranscription(e){return async(t,{send:n,waitUntil:r})=>{let i=await verifyTwilioInbound(t,e.config);if(i===null)return new Response(`unauthorized`,{status:401});let a=parseTwilioVoiceTranscription(i.params);return a?await isAllowed(a.from,e.config.allowFrom)?(r(dispatchVoiceTranscription({config:e.config,onVoiceTranscription:e.onVoiceTranscription,send:n,transcription:a})),sayTwilioResponse(e.config.voice?.acknowledgement??`Thanks. I'll follow up by text.`)):new Response(`forbidden`,{status:403}):gatherSpeechTwilioResponse({actionUrl:await buildTwilioActionUrl(t,e.config,e.routes.transcription),language:e.config.voice?.language,prompt:e.config.voice?.prompt??`Please say your message after the tone.`,speechTimeout:e.config.voice?.speechTimeout??`auto`,timeoutSeconds:e.config.voice?.timeoutSeconds})}}function rebuildTwilioContext(e,t,n){return{state:e,twilio:buildTwilioHandle({callSid:e.lastCallSid??void 0,config:n,from:e.from??``,to:e.to??void 0})}}function buildTwilioHandle(e){let t=e.config.api,n=e.config.credentials,r=e.config.messaging?.from??e.to,o=e.config.messaging?.messagingServiceSid,c=e.config.messaging?.statusCallbackUrl;return{callSid:e.callSid,from:e.from,to:e.to,request(e,r){return callTwilioApi({apiBaseUrl:t?.apiBaseUrl,body:r,credentials:n,fetch:t?.fetch,path:e})},sendMessage(i,s){return sendTwilioMessage({apiBaseUrl:t?.apiBaseUrl,body:i,credentials:n,fetch:t?.fetch,from:s?.from??r,messagingServiceSid:s?.messagingServiceSid??o,statusCallbackUrl:s?.statusCallbackUrl??c,to:s?.to??e.from})},updateCall(e,r){return updateTwilioCall({apiBaseUrl:t?.apiBaseUrl,callSid:e,credentials:n,fetch:t?.fetch,twiml:r})}}}function assertAllowFromConfigured(e){if(e?.allowFrom===void 0)throw Error(`twilioChannel requires allowFrom. Use allowFrom: "*" to allow all numbers.`)}async function dispatchText(e){let{message:t}=e,n={twilio:buildTwilioHandle({callSid:void 0,config:e.config,from:t.from,to:t.to})},r;try{r=await e.onText(n,t)}catch(e){log.error(`text handler failed`,{error:e});return}if(r==null)return;let i=formatTwilioContextBlock({channel:`text`,from:t.from,messageSid:t.messageSid,to:t.to});try{await e.send({message:t.body,context:[i]},{auth:r.auth,continuationToken:twilioContinuationToken(t.from,t.to),state:{from:t.from,lastCallSid:null,lastMessageSid:t.messageSid??null,to:t.to??null}})}catch(e){log.error(`text delivery failed`,{error:e})}}async function acceptVoiceCall(e){let{call:t}=e,n={twilio:buildTwilioHandle({callSid:t.callSid,config:e.config,from:t.from,to:t.to})};try{return await e.onVoice(n,t)}catch(e){return log.error(`voice handler failed`,{error:e}),null}}async function dispatchVoiceTranscription(e){let{transcription:t}=e,n={twilio:buildTwilioHandle({callSid:t.callSid,config:e.config,from:t.from,to:t.to})},r;try{r=await e.onVoiceTranscription(n,t)}catch(e){log.error(`voice transcription handler failed`,{error:e});return}if(r==null)return;let i=formatTwilioContextBlock({callSid:t.callSid,channel:`voice`,from:t.from,to:t.to});try{await e.send({message:t.text,context:[i]},{auth:r.auth,continuationToken:twilioContinuationToken(t.from,t.to),state:{from:t.from,lastCallSid:t.callSid??null,lastMessageSid:null,to:t.to??null}})}catch(e){log.error(`voice transcription delivery failed`,{error:e})}}async function isAllowed(e,t){let n=typeof t==`function`?await t():t;return n===`*`?!0:typeof n==`string`?n===e:n.includes(e)}function readString(e){return typeof e==`string`&&e.length>0?e:void 0}export{twilioChannel};
@@ -1,37 +1,6 @@
1
- /**
2
- * Tiny TwiML builders for Twilio webhook responses.
3
- *
4
- * The channel keeps TwiML generation local so callers do not need the
5
- * Twilio SDK just to acknowledge a webhook or gather speech.
6
- */
7
- /** Options for rendering a voice `<Gather input="speech">` response. */
8
- export interface TwilioGatherTwimlOptions {
9
- /** Absolute callback URL that receives the speech result. */
10
- readonly actionUrl: string;
11
- /** Prompt spoken before Twilio starts speech recognition. */
12
- readonly prompt: string;
13
- /** Twilio `<Say voice>` used for the prompt, e.g. `Polly.Joanna-Neural`. */
14
- readonly voice?: string;
15
- /** BCP 47 language used for speech recognition and the nested `<Say>` prompt. */
16
- readonly language?: string;
17
- /** Twilio `<Gather speechModel>` used for speech recognition. */
18
- readonly speechModel?: string;
19
- /** Twilio `<Gather timeout>` in seconds. */
20
- readonly timeoutSeconds?: number;
21
- /** Twilio `<Gather speechTimeout>`, such as `"auto"` or a second count string. */
22
- readonly speechTimeout?: string;
23
- /** Twilio `<Gather hints>` for expected words or phrases. */
24
- readonly hints?: string | readonly string[];
25
- /** Twilio `<Gather profanityFilter>` toggle. */
26
- readonly profanityFilter?: boolean;
27
- }
28
- /** Returns an empty TwiML response, useful for acknowledging inbound SMS without replying. */
29
- export declare function emptyTwilioResponse(): Response;
30
- /** Returns a TwiML response that speaks `message` and then ends the call. */
31
- export declare function sayTwilioResponse(message: string): Response;
32
- /** Returns a TwiML response that asks the caller to speak and posts the transcript to `actionUrl`. */
33
- export declare function gatherSpeechTwilioResponse(options: TwilioGatherTwimlOptions): Response;
34
- /** Wraps a TwiML string in a Twilio-compatible XML response. */
1
+ import { emptyTwilioResponse, escapeXml, gatherSpeechTwilioResponse, sayTwilioResponse, type TwilioGatherSpeechResponseOptions } from "#compiled/@chat-adapter/twilio/voice.js";
2
+ /** Options for rendering a Twilio `<Gather input="speech">` TwiML response. */
3
+ export type TwilioGatherTwimlOptions = TwilioGatherSpeechResponseOptions;
4
+ export { emptyTwilioResponse, escapeXml, gatherSpeechTwilioResponse, sayTwilioResponse };
5
+ /** Wraps a TwiML string in an XML `Response`. */
35
6
  export declare function twimlResponse(twiml: string): Response;
36
- /** Escapes the five XML predefined entities (`&`, `<`, `>`, `"`, `'`) for safe use in TwiML element content or attribute values. Note that `'` becomes `&apos;`. */
37
- export declare function escapeXml(value: string): string;
@@ -1 +1 @@
1
- function emptyTwilioResponse(){return twimlResponse(`<Response></Response>`)}function sayTwilioResponse(e){return twimlResponse(`<Response><Say>${escapeXml(e)}</Say></Response>`)}function gatherSpeechTwilioResponse(e){let t=typeof e.hints==`string`?e.hints:e.hints?.join(`,`),n=[`input="speech"`,`action="${escapeXml(e.actionUrl)}"`,`method="POST"`,`actionOnEmptyResult="true"`,e.language?`language="${escapeXml(e.language)}"`:void 0,e.speechModel?`speechModel="${escapeXml(e.speechModel)}"`:void 0,e.timeoutSeconds===void 0?void 0:`timeout="${e.timeoutSeconds}"`,e.speechTimeout?`speechTimeout="${escapeXml(e.speechTimeout)}"`:void 0,t?`hints="${escapeXml(t)}"`:void 0,e.profanityFilter===void 0?void 0:`profanityFilter="${e.profanityFilter?`true`:`false`}"`].filter(e=>e!==void 0).join(` `),r=[e.voice?`voice="${escapeXml(e.voice)}"`:void 0,e.language?`language="${escapeXml(e.language)}"`:void 0].filter(e=>e!==void 0).join(` `);return twimlResponse(`<Response><Gather ${n}>${r?`<Say ${r}>`:`<Say>`}${escapeXml(e.prompt)}</Say></Gather></Response>`)}function twimlResponse(e){return new Response(e,{status:200,headers:{"content-type":`text/xml;charset=UTF-8`}})}function escapeXml(e){return e.replaceAll(`&`,`&amp;`).replaceAll(`<`,`&lt;`).replaceAll(`>`,`&gt;`).replaceAll(`"`,`&quot;`).replaceAll(`'`,`&apos;`)}export{emptyTwilioResponse,escapeXml,gatherSpeechTwilioResponse,sayTwilioResponse,twimlResponse};
1
+ import{emptyTwilioResponse,escapeXml,gatherSpeechTwilioResponse,sayTwilioResponse,twilioResponse}from"#compiled/@chat-adapter/twilio/voice.js";function twimlResponse(e){return twilioResponse(e)}export{emptyTwilioResponse,escapeXml,gatherSpeechTwilioResponse,sayTwilioResponse,twimlResponse};
@@ -1,43 +1,22 @@
1
- /**
2
- * Twilio inbound-webhook verification.
3
- *
4
- * Twilio signs webhook requests with `X-Twilio-Signature`. For form
5
- * posts, the signed payload is the exact public URL Twilio called plus
6
- * every POST parameter sorted by name and appended as `name + value`.
7
- */
8
- /** Auth token, materialized either directly or from an async secret provider. */
9
- export type TwilioAuthToken = string | (() => string | Promise<string>);
10
- /** Public URL resolver used when the runtime request URL differs from Twilio's configured URL. */
11
- export type TwilioWebhookUrl = string | ((request: Request) => string | Promise<string>);
12
- /**
13
- * Parsed and verified Twilio webhook body.
14
- *
15
- * `params` contains every form parameter Twilio sent. Signature
16
- * validation happens before the channel reads any business fields.
17
- */
18
- export interface TwilioVerifiedRequest {
19
- readonly body: string;
20
- readonly params: URLSearchParams;
21
- }
22
- /** Options for {@link verifyTwilioRequest}. */
1
+ import { type TwilioCredential } from "#compiled/@chat-adapter/twilio/api.js";
2
+ import { type TwilioVerifiedRequest, type TwilioWebhookUrl } from "#compiled/@chat-adapter/twilio/webhook.js";
3
+ /** Twilio auth token, materialized directly or from an async secret provider. */
4
+ export type TwilioAuthToken = TwilioCredential;
5
+ export type { TwilioVerifiedRequest, TwilioWebhookUrl };
6
+ /** Options for verifying Twilio inbound webhooks. */
23
7
  export interface TwilioVerifyOptions {
24
- /** Auth token used to verify the signature. When undefined, falls back to `TWILIO_AUTH_TOKEN`. */
8
+ /** Auth token used to verify the signature. Defaults to `TWILIO_AUTH_TOKEN`. */
25
9
  readonly authToken: TwilioAuthToken | undefined;
26
- /** Public URL Twilio signed. Defaults to `request.url`; set this when a proxy or tunnel rewrites the URL. */
10
+ /** Public URL Twilio signed. Set this when a proxy or tunnel rewrites `request.url`. */
27
11
  readonly webhookUrl?: TwilioWebhookUrl;
28
12
  }
29
13
  /** Resolves a Twilio auth token, falling back to `TWILIO_AUTH_TOKEN`. */
30
14
  export declare function resolveTwilioAuthToken(authToken?: TwilioAuthToken): Promise<string>;
31
15
  /**
32
- * Verifies an inbound Twilio webhook and returns the raw body plus parsed form params.
33
- *
34
- * Consumes the request body, so the passed `Request` cannot be re-read afterward.
35
- * The signed URL comes from `options.webhookUrl` (falling back to `request.url`).
36
- * Set `webhookUrl` when a proxy or tunnel rewrites the URL. A rewritten URL causes
37
- * the signed payload to differ, so the signature check fails.
16
+ * Verifies an inbound Twilio webhook and returns the raw body plus parsed params.
38
17
  *
39
- * Throws when the auth token is missing, the `X-Twilio-Signature` header is absent,
40
- * or the computed signature does not match.
18
+ * This preserves Eve's existing error messages while delegating signature
19
+ * semantics to the Chat SDK Twilio primitive.
41
20
  */
42
21
  export declare function verifyTwilioRequest(request: Request, options: TwilioVerifyOptions): Promise<TwilioVerifiedRequest>;
43
22
  /** Computes Twilio's HMAC-SHA1 request signature. */
@@ -46,5 +25,5 @@ export declare function signTwilioRequest(input: {
46
25
  readonly url: string;
47
26
  readonly params: URLSearchParams;
48
27
  }): string;
49
- /** Builds the string Twilio signs for a form POST webhook. */
28
+ /** Builds the string Twilio signs for a webhook request. */
50
29
  export declare function buildTwilioSignatureBase(url: string, params: URLSearchParams): string;
@@ -1 +1 @@
1
- import{createLogger}from"#internal/logging.js";import{createHmac,timingSafeEqual}from"node:crypto";const log=createLogger(`twilio.verify`);async function resolveTwilioAuthToken(e){let t=e??process.env.TWILIO_AUTH_TOKEN;if(!t)throw Error(`TWILIO_AUTH_TOKEN is required.`);return typeof t==`function`?await t():t}async function verifyTwilioRequest(e,t){let n=await e.text(),r=new URLSearchParams(n),i=await resolveTwilioAuthToken(t.authToken),a=e.headers.get(`x-twilio-signature`)??``;if(!a)throw Error(`twilioChannel: inbound request missing X-Twilio-Signature.`);if(!constantTimeCompare(signTwilioRequest({authToken:i,params:r,url:await resolveWebhookUrl(e,t.webhookUrl)}),a))throw Error(`twilioChannel: inbound request signature mismatch.`);return{body:n,params:r}}function signTwilioRequest(e){let n=buildTwilioSignatureBase(e.url,e.params);return createHmac(`sha1`,e.authToken).update(n).digest(`base64`)}function buildTwilioSignatureBase(e,t){let n=Array.from(t.entries()).sort(([e,t],[n,r])=>{let i=e<n?-1:+(e>n);return i===0?t<r?-1:+(t>r):i}),r=e;for(let[e,t]of n)r+=`${e}${t}`;return r}async function resolveWebhookUrl(e,t){return typeof t==`function`?t(e):t??e.url}function constantTimeCompare(e,t){if(e.length!==t.length)return!1;try{return timingSafeEqual(Buffer.from(e),Buffer.from(t))}catch(e){return log.debug(`timingSafeEqual threw`,{error:e}),!1}}export{buildTwilioSignatureBase,resolveTwilioAuthToken,signTwilioRequest,verifyTwilioRequest};
1
+ import{createHmac}from"node:crypto";import{TwilioApiError,resolveTwilioCredential}from"#compiled/@chat-adapter/twilio/api.js";import{TwilioWebhookVerificationError,twilioSignatureBase,verifyTwilioRequest as verifyTwilioRequest$1}from"#compiled/@chat-adapter/twilio/webhook.js";async function resolveTwilioAuthToken(e){try{return await resolveTwilioCredential(e,`TWILIO_AUTH_TOKEN`)}catch(e){throw e instanceof TwilioApiError&&e.status===0?Error(`TWILIO_AUTH_TOKEN is required.`):e}}async function verifyTwilioRequest(e,t){if(!e.headers.get(`x-twilio-signature`))throw Error(`twilioChannel: inbound request missing X-Twilio-Signature.`);let n=await resolveTwilioAuthToken(t.authToken);try{return await verifyTwilioRequest$1(e,{authToken:n,webhookUrl:t.webhookUrl})}catch(e){throw e instanceof TwilioWebhookVerificationError?Error(`twilioChannel: inbound request signature mismatch.`):e}}function signTwilioRequest(t){let n=buildTwilioSignatureBase(t.url,t.params);return createHmac(`sha1`,t.authToken).update(n).digest(`base64`)}function buildTwilioSignatureBase(e,t){return twilioSignatureBase(e,t)}export{buildTwilioSignatureBase,resolveTwilioAuthToken,signTwilioRequest,verifyTwilioRequest};
@@ -9,10 +9,13 @@ type ApprovalToolInput<TInput> = TInput extends object ? Readonly<TInput> : TInp
9
9
  * `approvedTools` is the set of tool names (or compound approval keys)
10
10
  * already approved at least once in the current session. `toolName` is the
11
11
  * runtime name of the tool being evaluated. `toolInput` is the raw input the
12
- * model passed, available for input-aware decisions.
12
+ * model passed, available for input-aware decisions. `callId` is the id of
13
+ * the call being evaluated — the same `callId` carried by the call's stream
14
+ * events and its `execute` context.
13
15
  */
14
16
  export interface ApprovalContext<TInput = Record<string, unknown>> extends SessionContext {
15
17
  readonly approvedTools: ReadonlySet<string>;
18
+ readonly callId: string;
16
19
  readonly toolInput?: ApprovalToolInput<TInput>;
17
20
  readonly toolName: string;
18
21
  }
@@ -52,6 +52,13 @@ export interface ToolAuthOptions {
52
52
  * resolves that provider inline, which lets one tool use multiple credentials.
53
53
  */
54
54
  export type ToolContext = SessionContext & {
55
+ /** Aborts when the active turn is cancelled. */
56
+ readonly abortSignal: AbortSignal;
57
+ /**
58
+ * Id of the current tool call — the same `callId` carried by the call's
59
+ * stream events and its {@link ApprovalContext}.
60
+ */
61
+ readonly callId: string;
55
62
  /**
56
63
  * Resolves the bearer token for an inline provider. This accepts the same
57
64
  * auth shapes as a connection's `auth` field, including `connect("...")`
@@ -5,8 +5,9 @@ import type { ToolDefinition } from "#public/definitions/tool.js";
5
5
  * {@link ResolvedToolDefinition} so it can be re-exported as a public
6
6
  * {@link ToolDefinition}.
7
7
  *
8
- * Framework tools have the internal `(input) => output` signature.
8
+ * Framework tools have the internal `(input, options) => output` signature.
9
9
  * The public {@link ToolDefinition.execute} expects `(input, ctx)`.
10
- * This wrapper bridges the gap — `ctx` is trailing and omitted.
10
+ * This wrapper bridges the gap — the public `ctx` is mapped back onto the
11
+ * internal execute options.
11
12
  */
12
13
  export declare function toPublicToolDefinition(definition: ResolvedToolDefinition): ToolDefinition;
@@ -1 +1 @@
1
- function toPublicToolDefinition(e){if(!e.execute)throw Error(`Tool "${e.name}" is client-side and cannot be re-exported publicly.`);let t=e.execute,n=e.inputSchema,r={description:e.description,execute:e=>t(e),inputSchema:n??{},outputSchema:e.outputSchema};return e.approval!==void 0&&(r.approval=e.approval),r}export{toPublicToolDefinition};
1
+ function toPublicToolDefinition(e){if(!e.execute)throw Error(`Tool "${e.name}" is client-side and cannot be re-exported publicly.`);let t=e.execute,n=e.inputSchema,r={description:e.description,execute:(e,n)=>t(e,{abortSignal:n.abortSignal,messages:[],toolCallId:n.callId}),inputSchema:n??{},outputSchema:e.outputSchema};return e.approval!==void 0&&(r.approval=e.approval),r}export{toPublicToolDefinition};
@@ -1,7 +1,7 @@
1
1
  import { type MCPClient } from "#compiled/@ai-sdk/mcp/index.js";
2
2
  import type { ToolSet } from "ai";
3
3
  import type { ResolvedConnectionDefinition } from "#runtime/types.js";
4
- import type { ConnectionClient, ConnectionToolMetadata, ToolFilterDefinition } from "#runtime/connections/types.js";
4
+ import type { ConnectionClient, ConnectionToolExecuteOptions, ConnectionToolMetadata, ToolFilterDefinition } from "#runtime/connections/types.js";
5
5
  /**
6
6
  * Wraps one `MCPClient` from `@ai-sdk/mcp` for a single connection.
7
7
  *
@@ -44,7 +44,7 @@ export declare class McpConnectionClient implements ConnectionClient {
44
44
  * so callers re-enter the authorization flow instead of surfacing an
45
45
  * opaque transport error.
46
46
  */
47
- executeTool(toolName: string, args: unknown): Promise<unknown>;
47
+ executeTool(toolName: string, args: unknown, options?: ConnectionToolExecuteOptions): Promise<unknown>;
48
48
  close(): Promise<void>;
49
49
  }
50
50
  /**
@@ -1 +1 @@
1
- import{isObject}from"#shared/guards.js";import{buildCallbackContext}from"#context/build-callback-context.js";import{ConnectionAuthorizationRequiredError}from"#public/connections/errors.js";import{evictScopedToken,resolveScopedToken}from"#runtime/connections/scoped-authorization.js";import{createMCPClient}from"#compiled/@ai-sdk/mcp/index.js";import{resolveConnectionAuthorization}from"#runtime/connections/resolve-authorization.js";var McpConnectionClient=class{#e;#t;#n;#r;#i;constructor(e){this.#i=e}async connect(){if(this.#t!==void 0)return this.#t;if(this.#e!==void 0)return this.#e;this.#e=this.#a();try{return this.#t=await this.#e,this.#t}catch(e){throw this.#e=void 0,e}}async#a(){let e=await resolveHeaders(this.#i),t=this.#i.url;try{return await createMCPClient({transport:{type:`http`,url:t,headers:e}})}catch(n){if(!isMcpHttpFallbackRetryableError(n))throw n;return await createMCPClient({transport:{type:`sse`,url:t,headers:e}})}}async getToolMetadata(){return(await this.#o()).metadata}async getTools(){return(await this.#o()).tools}async executeTool(e,t){try{let{tools:n}=await this.#o(),r=n[e];if(r?.execute===void 0)throw Error(`Tool "${e}" not found in connection "${this.#i.connectionName}".`);return await r.execute(t,{})}catch(e){return await this.#l(e)}}async#o(){if(this.#r!==void 0)return this.#r;if(this.#n!==void 0)return this.#n;this.#n=this.#s();try{return this.#r=await this.#n,this.#r}catch(e){throw this.#n=void 0,e}}async#s(){try{return await this.#c()}catch(e){return await this.#l(e)}}async#c(){let e=await this.connect(),t=await e.listTools(),n=this.#i.tools,r=n===void 0?t.tools:t.tools.filter(e=>passesToolFilter(e.name,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}async#l(e){throw isMcpAuthRequiredError(e)?(await this.#u(),await this.close(),new ConnectionAuthorizationRequiredError(this.#i.connectionName,{message:`Connection "${this.#i.connectionName}" requires authorization (the server rejected the token).`})):e}async#u(){let e=await resolveConnectionAuthorization(this.#i);e!==void 0&&await evictScopedToken({authorization:e,connection:{url:this.#i.url},scope:this.#i.connectionName})}};function isMcpAuthRequiredError(e){return readHttpStatus(e)===401}function isMcpHttpFallbackRetryableError(e){let t=readHttpStatus(e);return t===400||t===404||t===405}function readHttpStatus(t){for(let n of walkErrorChain(t)){if(!isObject(n))continue;let t=readStatusField(n);if(t!==void 0)return t;let r=n.response;if(isObject(r)){let e=readStatusField(r);if(e!==void 0)return e}if(typeof n.message==`string`){let e=/\bHTTP\s+(\d{3})\b/u.exec(n.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(t){let n=t,r=new Set;for(;n!=null&&!r.has(n);){if(r.add(n),yield n,!isObject(n)||!(`cause`in n))return;n=n.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 n={},r,getCallbackContext=()=>r??=buildCallbackContext(),i=await resolveConnectionAuthorization(e,typeof e.authorization==`function`?getCallbackContext():void 0);if(i!==void 0&&(n.Authorization=`Bearer ${(await resolveToken(e,i)).token}`),e.headers!==void 0){let t=await resolveHeadersDefinition(e.headers,getCallbackContext);for(let[r,a]of Object.entries(t)){if(i!==void 0&&r.toLowerCase()===`authorization`)throw Error(`Connection "${e.connectionName}" headers must not include an "Authorization" key when "authorization" is also provided.`);n[r]=a}}return n}async function resolveToken(e,t){return await resolveScopedToken({authorization:t,connection:{url:e.url},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{McpConnectionClient,isMcpAuthRequiredError,passesToolFilter,resolveHeaders};
1
+ import{isObject}from"#shared/guards.js";import{buildCallbackContext}from"#context/build-callback-context.js";import{ConnectionAuthorizationRequiredError}from"#public/connections/errors.js";import{evictScopedToken,resolveScopedToken}from"#runtime/connections/scoped-authorization.js";import{createMCPClient}from"#compiled/@ai-sdk/mcp/index.js";import{resolveConnectionAuthorization}from"#runtime/connections/resolve-authorization.js";var McpConnectionClient=class{#e;#t;#n;#r;#i;constructor(e){this.#i=e}async connect(){if(this.#t!==void 0)return this.#t;if(this.#e!==void 0)return this.#e;this.#e=this.#a();try{return this.#t=await this.#e,this.#t}catch(e){throw this.#e=void 0,e}}async#a(){let e=await resolveHeaders(this.#i),t=this.#i.url;try{return await createMCPClient({transport:{type:`http`,url:t,headers:e}})}catch(n){if(!isMcpHttpFallbackRetryableError(n))throw n;return await createMCPClient({transport:{type:`sse`,url:t,headers:e}})}}async getToolMetadata(){return(await this.#o()).metadata}async getTools(){return(await this.#o()).tools}async executeTool(e,t,n){try{let{tools:r}=await this.#o(),i=r[e];if(i?.execute===void 0)throw Error(`Tool "${e}" not found in connection "${this.#i.connectionName}".`);return await i.execute(t,{abortSignal:n?.abortSignal})}catch(e){return await this.#l(e)}}async#o(){if(this.#r!==void 0)return this.#r;if(this.#n!==void 0)return this.#n;this.#n=this.#s();try{return this.#r=await this.#n,this.#r}catch(e){throw this.#n=void 0,e}}async#s(){try{return await this.#c()}catch(e){return await this.#l(e)}}async#c(){let e=await this.connect(),t=await e.listTools(),n=this.#i.tools,r=n===void 0?t.tools:t.tools.filter(e=>passesToolFilter(e.name,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}async#l(e){throw isMcpAuthRequiredError(e)?(await this.#u(),await this.close(),new ConnectionAuthorizationRequiredError(this.#i.connectionName,{message:`Connection "${this.#i.connectionName}" requires authorization (the server rejected the token).`})):e}async#u(){let e=await resolveConnectionAuthorization(this.#i);e!==void 0&&await evictScopedToken({authorization:e,connection:{url:this.#i.url},scope:this.#i.connectionName})}};function isMcpAuthRequiredError(e){return readHttpStatus(e)===401}function isMcpHttpFallbackRetryableError(e){let t=readHttpStatus(e);return t===400||t===404||t===405}function readHttpStatus(t){for(let n of walkErrorChain(t)){if(!isObject(n))continue;let t=readStatusField(n);if(t!==void 0)return t;let r=n.response;if(isObject(r)){let e=readStatusField(r);if(e!==void 0)return e}if(typeof n.message==`string`){let e=/\bHTTP\s+(\d{3})\b/u.exec(n.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(t){let n=t,r=new Set;for(;n!=null&&!r.has(n);){if(r.add(n),yield n,!isObject(n)||!(`cause`in n))return;n=n.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 n={},r,getCallbackContext=()=>r??=buildCallbackContext(),i=await resolveConnectionAuthorization(e,typeof e.authorization==`function`?getCallbackContext():void 0);if(i!==void 0&&(n.Authorization=`Bearer ${(await resolveToken(e,i)).token}`),e.headers!==void 0){let t=await resolveHeadersDefinition(e.headers,getCallbackContext);for(let[r,a]of Object.entries(t)){if(i!==void 0&&r.toLowerCase()===`authorization`)throw Error(`Connection "${e.connectionName}" headers must not include an "Authorization" key when "authorization" is also provided.`);n[r]=a}}return n}async function resolveToken(e,t){return await resolveScopedToken({authorization:t,connection:{url:e.url},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{McpConnectionClient,isMcpAuthRequiredError,passesToolFilter,resolveHeaders};
@@ -1,7 +1,7 @@
1
1
  import { type ToolSet } from "ai";
2
2
  import type { ResolvedConnectionDefinition } from "#runtime/types.js";
3
3
  import { type OpenApiOperation } from "#runtime/connections/openapi-operations.js";
4
- import type { ConnectionClient, ConnectionToolMetadata } from "#runtime/connections/types.js";
4
+ import type { ConnectionClient, ConnectionToolExecuteOptions, ConnectionToolMetadata } from "#runtime/connections/types.js";
5
5
  interface OpenApiToolCache {
6
6
  readonly metadata: readonly ConnectionToolMetadata[];
7
7
  readonly operations: ReadonlyMap<string, OpenApiOperation>;
@@ -37,7 +37,7 @@ export declare class OpenApiConnectionClient implements ConnectionClient {
37
37
  connect(): Promise<OpenApiToolCache>;
38
38
  getToolMetadata(): Promise<readonly ConnectionToolMetadata[]>;
39
39
  getTools(): Promise<ToolSet>;
40
- executeTool(toolName: string, args: unknown): Promise<OpenApiToolResult>;
40
+ executeTool(toolName: string, args: unknown, options?: ConnectionToolExecuteOptions): Promise<OpenApiToolResult>;
41
41
  close(): Promise<void>;
42
42
  }
43
43
  export {};
@@ -1 +1 @@
1
- import{isObject}from"#shared/guards.js";import{jsonSchema,tool}from"ai";import{passesToolFilter,resolveHeaders}from"#runtime/connections/mcp-client.js";import{HTTP_METHODS,operationDescription,operationName,uniqueName}from"#runtime/connections/openapi-operations.js";import{buildInputSchema,deref,derefSchema,isArray}from"#runtime/connections/openapi-schema.js";import{applySecurity,resolveSecurity}from"#runtime/connections/openapi-security.js";import{extractServerUrl,parseSpecDocument}from"#runtime/connections/openapi-spec.js";const SWAGGER_PARAMETER_SCHEMA_KEYS=[`default`,`enum`,`exclusiveMaximum`,`exclusiveMinimum`,`format`,`items`,`maximum`,`maxItems`,`maxLength`,`minimum`,`minItems`,`minLength`,`multipleOf`,`pattern`,`type`,`uniqueItems`];var OpenApiConnectionClient=class{#e;#t;#n;constructor(e){this.#n=e}async connect(){return this.#r()}async getToolMetadata(){return(await this.#r()).metadata}async getTools(){return(await this.#r()).tools}async executeTool(t,n){let r=await this.#r(),i=r.operations.get(t);if(i===void 0)throw Error(`Tool "${t}" not found in connection "${this.#n.connectionName}".`);return this.#f(i,r.baseUrl,isObject(n)?n:{})}async close(){this.#e=void 0,this.#t=void 0}async#r(){if(this.#t!==void 0)return this.#t;if(this.#e!==void 0)return this.#e;this.#e=this.#i();try{return this.#t=await this.#e,this.#t}catch(e){throw this.#e=void 0,e}}async#i(){let i=await this.#o(),a=this.#a(i),o=this.#s(i),s=this.#n.tools,c=s===void 0?o:o.filter(e=>passesToolFilter(e.toolName,s)),l=[],u=new Map,d={};for(let r of c)u.set(r.toolName,r),l.push({description:r.description,inputSchema:r.inputSchema,name:r.toolName}),d[r.toolName]=tool({description:r.description,inputSchema:jsonSchema(r.inputSchema),execute:async t=>this.#f(r,a,isObject(t)?t:{})});return{metadata:l,operations:u,tools:d,baseUrl:a}}#a(e){let t=this.#n.url;if(typeof t==`string`&&t.trim().length>0)return t;let n=extractServerUrl(e,this.#n.spec);if(n!==void 0)return n;throw Error(`OpenAPI connection "${this.#n.connectionName}" has no base URL: set "baseUrl" or ensure the document declares an absolute "servers" entry or Swagger "host".`)}async#o(){let t=this.#n.spec;if(t===void 0)throw Error(`OpenAPI connection "${this.#n.connectionName}" is missing its "spec" source.`);if(typeof t!=`string`)return t;let n;try{n=await fetch(t,{headers:{accept:`application/json, application/yaml, text/yaml, */*`}})}catch(e){throw Error(`OpenAPI connection "${this.#n.connectionName}" failed to fetch its spec from "${t}": ${String(e)}`)}if(!n.ok)throw Error(`OpenAPI connection "${this.#n.connectionName}" failed to fetch its spec from "${t}": HTTP ${n.status}.`);let r=await n.text(),i;try{i=parseSpecDocument(r)}catch(e){throw Error(`OpenAPI connection "${this.#n.connectionName}" spec at "${t}" could not be parsed as JSON or YAML: ${String(e)}`)}if(!isObject(i))throw Error(`OpenAPI connection "${this.#n.connectionName}" spec at "${t}" is not an OpenAPI document object.`);return i}#s(t){let n=t.paths;if(!isObject(n))return[];let r=[],i=new Set;for(let[u,d]of Object.entries(n)){if(!isObject(d))continue;let n=isArray(d.parameters)?d.parameters:[];for(let f of HTTP_METHODS){let a=d[f];if(!isObject(a))continue;let p=uniqueName(operationName(a,f,u),i),m=isArray(a.parameters)?a.parameters:[],h=[...n,...m],g=this.#c(t,h),_=this.#l(t,a.requestBody,h);r.push({toolName:p,method:f,pathTemplate:u,description:operationDescription(a),parameters:g,requestBody:_,inputSchema:buildInputSchema(g,_),security:resolveSecurity(t,a)})}}return r}#c(t,n){let r=[];for(let i of n){let n=isObject(i)?deref(t,i):i;if(!isObject(n))continue;let a=n.in;if(a!==`path`&&a!==`query`&&a!==`header`&&a!==`cookie`||typeof n.name!=`string`)continue;let o=this.#u(t,n);r.push({name:n.name,location:a,required:n.required===!0||a===`path`,schema:o,description:typeof n.description==`string`?n.description:void 0})}return r}#l(t,n,r=[]){if(isObject(n)){let r=deref(t,n);if(isObject(r)&&isObject(r.content)){let n=r.content,i=`application/json`in n?`application/json`:Object.keys(n)[0];if(i!==void 0){let a=n[i],o=isObject(a)&&isObject(a.schema)?derefSchema(t,a.schema):{};return{required:r.required===!0,contentType:i,schema:o}}}}return this.#d(t,r)}#u(t,n){if(isObject(n.schema))return derefSchema(t,n.schema);let r={};for(let e of SWAGGER_PARAMETER_SCHEMA_KEYS)n[e]!==void 0&&(r[e]=n[e]);return derefSchema(t,r)}#d(t,n){for(let r of n){let n=isObject(r)?deref(t,r):r;if(!isObject(n)||n.in!==`body`)continue;let i=isObject(n.schema)?derefSchema(t,n.schema):{};return{required:n.required===!0,contentType:`application/json`,schema:i}}}async#f(e,t,n){let r=await resolveHeaders(this.#n),a=e.pathTemplate,o=new URLSearchParams,s=[];for(let t of e.parameters){let e=n[t.name];e!=null&&(t.location===`path`?a=a.replace(`{${t.name}}`,encodeURIComponent(String(e))):t.location===`query`?appendQuery(o,t.name,e):t.location===`cookie`?s.push(`${t.name}=${encodeURIComponent(String(e))}`):r[t.name]=String(e))}if(applySecurity(e.security,this.#n,r,o,s),s.length>0){let e=r.cookie??r.Cookie;delete r.Cookie,r.cookie=[e,...s].filter(e=>!!e).join(`; `)}let c=new URL(joinPath(t,a));c.search=o.toString();let l;e.requestBody!==void 0&&n.body!==void 0&&(l=JSON.stringify(n.body),r[`content-type`]=e.requestBody.contentType);let u=await fetch(c,{method:e.method.toUpperCase(),headers:r,body:l});return{status:u.status,statusText:u.statusText,body:await readResponseBody(u)}}};function appendQuery(e,t,n){if(isArray(n)){for(let r of n)e.append(t,String(r));return}e.append(t,String(n))}function joinPath(e,t){return`${e.endsWith(`/`)?e.slice(0,-1):e}${t.startsWith(`/`)?t:`/${t}`}`}async function readResponseBody(e){let t=await e.text();if(t.length===0)return null;if((e.headers.get(`content-type`)??``).includes(`application/json`))try{return JSON.parse(t)}catch{return t}return t}export{OpenApiConnectionClient};
1
+ import{isObject}from"#shared/guards.js";import{jsonSchema,tool}from"ai";import{passesToolFilter,resolveHeaders}from"#runtime/connections/mcp-client.js";import{HTTP_METHODS,operationDescription,operationName,uniqueName}from"#runtime/connections/openapi-operations.js";import{buildInputSchema,deref,derefSchema,isArray}from"#runtime/connections/openapi-schema.js";import{applySecurity,resolveSecurity}from"#runtime/connections/openapi-security.js";import{extractServerUrl,parseSpecDocument}from"#runtime/connections/openapi-spec.js";const SWAGGER_PARAMETER_SCHEMA_KEYS=[`default`,`enum`,`exclusiveMaximum`,`exclusiveMinimum`,`format`,`items`,`maximum`,`maxItems`,`maxLength`,`minimum`,`minItems`,`minLength`,`multipleOf`,`pattern`,`type`,`uniqueItems`];var OpenApiConnectionClient=class{#e;#t;#n;constructor(e){this.#n=e}async connect(){return this.#r()}async getToolMetadata(){return(await this.#r()).metadata}async getTools(){return(await this.#r()).tools}async executeTool(t,n,r){let i=await this.#r(),a=i.operations.get(t);if(a===void 0)throw Error(`Tool "${t}" not found in connection "${this.#n.connectionName}".`);return this.#f(a,i.baseUrl,isObject(n)?n:{},{abortSignal:r?.abortSignal})}async close(){this.#e=void 0,this.#t=void 0}async#r(){if(this.#t!==void 0)return this.#t;if(this.#e!==void 0)return this.#e;this.#e=this.#i();try{return this.#t=await this.#e,this.#t}catch(e){throw this.#e=void 0,e}}async#i(){let i=await this.#o(),a=this.#a(i),o=this.#s(i),s=this.#n.tools,c=s===void 0?o:o.filter(e=>passesToolFilter(e.toolName,s)),l=[],u=new Map,d={};for(let r of c)u.set(r.toolName,r),l.push({description:r.description,inputSchema:r.inputSchema,name:r.toolName}),d[r.toolName]=tool({description:r.description,inputSchema:jsonSchema(r.inputSchema),execute:async(t,n)=>this.#f(r,a,isObject(t)?t:{},{abortSignal:n?.abortSignal})});return{metadata:l,operations:u,tools:d,baseUrl:a}}#a(e){let t=this.#n.url;if(typeof t==`string`&&t.trim().length>0)return t;let n=extractServerUrl(e,this.#n.spec);if(n!==void 0)return n;throw Error(`OpenAPI connection "${this.#n.connectionName}" has no base URL: set "baseUrl" or ensure the document declares an absolute "servers" entry or Swagger "host".`)}async#o(){let t=this.#n.spec;if(t===void 0)throw Error(`OpenAPI connection "${this.#n.connectionName}" is missing its "spec" source.`);if(typeof t!=`string`)return t;let n;try{n=await fetch(t,{headers:{accept:`application/json, application/yaml, text/yaml, */*`}})}catch(e){throw Error(`OpenAPI connection "${this.#n.connectionName}" failed to fetch its spec from "${t}": ${String(e)}`)}if(!n.ok)throw Error(`OpenAPI connection "${this.#n.connectionName}" failed to fetch its spec from "${t}": HTTP ${n.status}.`);let r=await n.text(),i;try{i=parseSpecDocument(r)}catch(e){throw Error(`OpenAPI connection "${this.#n.connectionName}" spec at "${t}" could not be parsed as JSON or YAML: ${String(e)}`)}if(!isObject(i))throw Error(`OpenAPI connection "${this.#n.connectionName}" spec at "${t}" is not an OpenAPI document object.`);return i}#s(t){let n=t.paths;if(!isObject(n))return[];let r=[],i=new Set;for(let[u,d]of Object.entries(n)){if(!isObject(d))continue;let n=isArray(d.parameters)?d.parameters:[];for(let f of HTTP_METHODS){let a=d[f];if(!isObject(a))continue;let p=uniqueName(operationName(a,f,u),i),m=isArray(a.parameters)?a.parameters:[],h=[...n,...m],g=this.#c(t,h),_=this.#l(t,a.requestBody,h);r.push({toolName:p,method:f,pathTemplate:u,description:operationDescription(a),parameters:g,requestBody:_,inputSchema:buildInputSchema(g,_),security:resolveSecurity(t,a)})}}return r}#c(t,n){let r=[];for(let i of n){let n=isObject(i)?deref(t,i):i;if(!isObject(n))continue;let a=n.in;if(a!==`path`&&a!==`query`&&a!==`header`&&a!==`cookie`||typeof n.name!=`string`)continue;let o=this.#u(t,n);r.push({name:n.name,location:a,required:n.required===!0||a===`path`,schema:o,description:typeof n.description==`string`?n.description:void 0})}return r}#l(t,n,r=[]){if(isObject(n)){let r=deref(t,n);if(isObject(r)&&isObject(r.content)){let n=r.content,i=`application/json`in n?`application/json`:Object.keys(n)[0];if(i!==void 0){let a=n[i],o=isObject(a)&&isObject(a.schema)?derefSchema(t,a.schema):{};return{required:r.required===!0,contentType:i,schema:o}}}}return this.#d(t,r)}#u(t,n){if(isObject(n.schema))return derefSchema(t,n.schema);let r={};for(let e of SWAGGER_PARAMETER_SCHEMA_KEYS)n[e]!==void 0&&(r[e]=n[e]);return derefSchema(t,r)}#d(t,n){for(let r of n){let n=isObject(r)?deref(t,r):r;if(!isObject(n)||n.in!==`body`)continue;let i=isObject(n.schema)?derefSchema(t,n.schema):{};return{required:n.required===!0,contentType:`application/json`,schema:i}}}async#f(e,t,n,r){let a=await resolveHeaders(this.#n),o=e.pathTemplate,s=new URLSearchParams,c=[];for(let t of e.parameters){let e=n[t.name];e!=null&&(t.location===`path`?o=o.replace(`{${t.name}}`,encodeURIComponent(String(e))):t.location===`query`?appendQuery(s,t.name,e):t.location===`cookie`?c.push(`${t.name}=${encodeURIComponent(String(e))}`):a[t.name]=String(e))}if(applySecurity(e.security,this.#n,a,s,c),c.length>0){let e=a.cookie??a.Cookie;delete a.Cookie,a.cookie=[e,...c].filter(e=>!!e).join(`; `)}let l=new URL(joinPath(t,o));l.search=s.toString();let u;e.requestBody!==void 0&&n.body!==void 0&&(u=JSON.stringify(n.body),a[`content-type`]=e.requestBody.contentType);let d=await fetch(l,{method:e.method.toUpperCase(),headers:a,body:u,signal:r?.abortSignal});return{status:d.status,statusText:d.statusText,body:await readResponseBody(d)}}};function appendQuery(e,t,n){if(isArray(n)){for(let r of n)e.append(t,String(r));return}e.append(t,String(n))}function joinPath(e,t){return`${e.endsWith(`/`)?e.slice(0,-1):e}${t.startsWith(`/`)?t:`/${t}`}`}async function readResponseBody(e){let t=await e.text();if(t.length===0)return null;if((e.headers.get(`content-type`)??``).includes(`application/json`))try{return JSON.parse(t)}catch{return t}return t}export{OpenApiConnectionClient};
@@ -382,11 +382,16 @@ export interface ConnectionToolMetadata {
382
382
  readonly name: string;
383
383
  readonly outputSchema?: Record<string, unknown>;
384
384
  }
385
+ /** Per-call options for {@link ConnectionClient.executeTool}. */
386
+ export interface ConnectionToolExecuteOptions {
387
+ /** Signal forwarded into the underlying transport. */
388
+ readonly abortSignal?: AbortSignal;
389
+ }
385
390
  /** A live client for a single connection. */
386
391
  export interface ConnectionClient {
387
392
  close(): Promise<void>;
388
393
  connect(): Promise<unknown>;
389
- executeTool(toolName: string, args: unknown): Promise<unknown>;
394
+ executeTool(toolName: string, args: unknown, options?: ConnectionToolExecuteOptions): Promise<unknown>;
390
395
  getToolMetadata(): Promise<readonly ConnectionToolMetadata[]>;
391
396
  getTools(): Promise<ToolSet>;
392
397
  }
@@ -1 +1 @@
1
- import{requireSandboxSession}from"#execution/sandbox/require-sandbox.js";import{executeBashOnSandbox}from"#execution/sandbox/bash-tool.js";const BASH_INPUT_SCHEMA={additionalProperties:!1,properties:{command:{description:`The shell command to execute.`,type:`string`}},required:[`command`],type:`object`},BASH_OUTPUT_SCHEMA={additionalProperties:!1,properties:{exitCode:{type:`number`},stderr:{type:`string`},stdout:{type:`string`},truncated:{type:`boolean`}},required:[`exitCode`,`stderr`,`stdout`,`truncated`],type:`object`};async function executeBash(e){return executeBashOnSandbox(await requireSandboxSession(),e)}const BASH_TOOL_DEFINITION={description:`Execute a shell command in the shared workspace environment.`,execute:executeBash,inputSchema:BASH_INPUT_SCHEMA,logicalPath:`eve:framework/bash`,name:`bash`,outputSchema:BASH_OUTPUT_SCHEMA,sourceId:`eve:bash-tool`,sourceKind:`module`};export{BASH_INPUT_SCHEMA,BASH_OUTPUT_SCHEMA,BASH_TOOL_DEFINITION};
1
+ import{requireSandboxSession}from"#execution/sandbox/require-sandbox.js";import{executeBashOnSandbox}from"#execution/sandbox/bash-tool.js";const BASH_INPUT_SCHEMA={additionalProperties:!1,properties:{command:{description:`The shell command to execute.`,type:`string`}},required:[`command`],type:`object`},BASH_OUTPUT_SCHEMA={additionalProperties:!1,properties:{exitCode:{type:`number`},stderr:{type:`string`},stdout:{type:`string`},truncated:{type:`boolean`}},required:[`exitCode`,`stderr`,`stdout`,`truncated`],type:`object`};async function executeBash(e,t){return executeBashOnSandbox(await requireSandboxSession(t?.abortSignal),e)}const BASH_TOOL_DEFINITION={description:`Execute a shell command in the shared workspace environment.`,execute:executeBash,inputSchema:BASH_INPUT_SCHEMA,logicalPath:`eve:framework/bash`,name:`bash`,outputSchema:BASH_OUTPUT_SCHEMA,sourceId:`eve:bash-tool`,sourceKind:`module`};export{BASH_INPUT_SCHEMA,BASH_OUTPUT_SCHEMA,BASH_TOOL_DEFINITION};
@@ -1 +1 @@
1
- import{createLogger}from"#internal/logging.js";import{loadContext}from"#context/container.js";import{ContextKey}from"#context/key.js";import{ConnectionRegistryKey}from"#context/providers/connection-key.js";import{ConnectionAuthorizationFailedError,isConnectionAuthorizationFailedError,isConnectionAuthorizationRequiredError}from"#public/connections/errors.js";import{getAuthorizationResult,getHookUrl,requestAuthorization}from"#harness/authorization.js";import{supportsInteractiveAuthorization}from"#runtime/connections/types.js";import{resolveAuthorizationCallbackUrl,stampChallengeDisplayName}from"#runtime/connections/scoped-authorization.js";import{resolveConnectionAuthorization}from"#runtime/connections/resolve-authorization.js";import{writeCachedToken}from"#runtime/connections/authorization-tokens.js";import{principalKey,resolveConnectionPrincipal}from"#runtime/connections/principal.js";const logger=createLogger(`framework.connection-search-dynamic`),CONNECTION_SEARCH_OUTPUT_SCHEMA={items:{additionalProperties:!1,properties:{connection:{type:`string`},description:{type:`string`},error:{type:`string`},inputSchema:{type:`object`},needsAuthorization:{type:`boolean`},outputSchema:{type:`object`},qualifiedName:{type:`string`},tool:{type:`string`}},required:[`connection`,`description`],type:`object`},type:`array`},ConnectionSearchResultsKey=new ContextKey(`eve.connectionSearchResults`);function qualifiedConnectionToolName(e,t){return`${e}__${t}`}function tokenize(e){return e.toLowerCase().split(/[\s_\-./]+/).filter(e=>e.length>1)}function scoreMatch(e,t){let n=tokenize(t.name),r=tokenize(t.description),i=0;for(let t of e){for(let e of n)(e.includes(t)||t.includes(e))&&(i+=3);for(let e of r)(e.includes(t)||t.includes(e))&&(i+=1)}return i}async function resolveInteractiveAuth(e,t){let n=e.getConnections().find(e=>e.connectionName===t);if(n===void 0)return;let r=await resolveConnectionAuthorization(n);if(supportsInteractiveAuthorization(r))return r}async function completePendingAuthorizations(e){let n=loadContext(),r=new Set;for(let t of e.getConnections()){let i=getAuthorizationResult(t.connectionName);if(!i)continue;let a=await resolveInteractiveAuth(e,t.connectionName);if(!a)continue;let o=resolveConnectionPrincipal(t.connectionName,a),c=await a.completeAuthorization({callbackUrl:i.hookUrl,connection:{url:t.url??``},principal:o,resume:i.resume,callback:i.callback});writeCachedToken(n,t.connectionName,principalKey(o),c),r.add(t.connectionName)}return r}async function executeConnectionSearch(e){let n=loadContext(),i=n.get(ConnectionRegistryKey);if(i===void 0)return[];let s=await completePendingAuthorizations(i),l=e.limit??10,u=tokenize(e.keywords),d=[],f=[],m=e.connection!==void 0&&e.connection!==``?i.getConnections().filter(t=>t.connectionName===e.connection):i.getConnections(),h=[];for(let e of m){let t;try{t=await i.getClient(e.connectionName).getToolMetadata()}catch(t){if(isConnectionAuthorizationRequiredError(t)){if(s.has(e.connectionName)){logger.warn(`connection still unauthorized after authorization`,{connection:e.connectionName}),f.push({connection:e.connectionName,description:e.description,error:`Authorization for "${e.connectionName}" did not take effect; the token was rejected after sign-in.`});continue}let t=await resolveInteractiveAuth(i,e.connectionName);if(t){let n=getHookUrl(e.connectionName);if(n){let r=resolveConnectionPrincipal(e.connectionName,t),i=resolveAuthorizationCallbackUrl({authorization:t,callbackUrl:n});try{let{challenge:n,resume:a}=await t.startAuthorization({callbackUrl:i,connection:{url:e.url??``},principal:r});h.push({name:e.connectionName,challenge:stampChallengeDisplayName(n,t),hookUrl:i,resume:a})}catch(t){logger.warn(`startAuthorization failed`,{connection:e.connectionName,error:t instanceof Error?t:Error(String(t))})}}}f.push({connection:e.connectionName,description:e.description,needsAuthorization:!0});continue}if(isConnectionAuthorizationFailedError(t)){logger.warn(`connection authorization failed`,{connection:e.connectionName,reason:t.reason,retryable:t.retryable,error:t}),f.push({connection:e.connectionName,description:e.description,error:`Authorization failed for ${e.connectionName}: ${t.message}`});continue}let n=t instanceof Error?t.message:`unknown error`;logger.warn(`failed to load connection tools`,{connection:e.connectionName,error:t instanceof Error?t:Error(n)}),f.push({connection:e.connectionName,description:e.description,error:`Failed to load tools for "${e.connectionName}": ${n}`});continue}for(let n of t){let t=scoreMatch(u,n);t>0&&d.push({item:{connection:e.connectionName,description:n.description,inputSchema:n.inputSchema,outputSchema:n.outputSchema,qualifiedName:qualifiedConnectionToolName(e.connectionName,n.name),tool:n.name},score:t})}}if(h.length>0)return requestAuthorization(h);d.sort((e,t)=>t.score-e.score);let g=d.slice(0,l).map(e=>e.item);if(g.length>0){let e=[...g,...f],t=n.get(ConnectionSearchResultsKey)??[],r=new Map(t.map(e=>[e.qualifiedName,e]));for(let e of g)e.qualifiedName&&r.set(e.qualifiedName,e);return n.set(ConnectionSearchResultsKey,[...r.values()]),e}return m.map(e=>f.find(t=>t.connection===e.connectionName)||{connection:e.connectionName,description:e.description})}function extractDiscoveredTools(e){let t=new Map;for(let n of e){if(n.role!==`tool`)continue;let e=n.content;for(let n of e){if(n.type!==`tool-result`||n.toolName!==`connection_search`)continue;let e=n.output;if(e==null)continue;let r=typeof e==`object`&&`type`in e&&`value`in e?e.value:e;if(Array.isArray(r))for(let e of r)e.tool&&e.qualifiedName&&t.set(e.qualifiedName,e)}}return[...t.values()]}function createConnectionSearchEvents(){return{"step.started":async(e,n)=>{let a=loadContext().get(ConnectionRegistryKey);if(!a||a.getConnections().length===0)return null;let l=a.getConnections().map(e=>e.connectionName),u=extractDiscoveredTools(n.messages),p=loadContext().get(ConnectionSearchResultsKey)??[],h=new Map;for(let e of p)e.qualifiedName&&h.set(e.qualifiedName,e);for(let e of u)e.qualifiedName&&h.set(e.qualifiedName,e);let g=[...h.values()],_={};_.connection_search={description:`Search for tools across your connections. Discovered tools become directly callable by their qualified name (e.g. \`linear__list_issues\`) in your next response. Available connections: ${l.join(`, `)}.`,inputSchema:{type:`object`,additionalProperties:!1,properties:{keywords:{description:`Search keywords and expanded aliases. Distill intent into keywords; avoid stop words like 'a', 'the', 'in'.`,type:`string`},connection:{description:`Optional: limit search to a specific connection name.`,type:`string`},limit:{description:`Max results to return. Default 10.`,type:`number`}},required:[`keywords`]},async execute(e){return executeConnectionSearch(e)},outputSchema:CONNECTION_SEARCH_OUTPUT_SCHEMA};for(let e of g){let n=e.connection,l=e.tool,u=a.getConnectionApproval(n);_[qualifiedConnectionToolName(n,l)]={description:e.description,inputSchema:e.inputSchema??{type:`object`},approval:u,outputSchema:e.outputSchema,async execute(e){let a=loadContext().get(ConnectionRegistryKey),u=a.getConnections().find(e=>e.connectionName===n),p=await resolveInteractiveAuth(a,n),m=!1;if(p){let e=getAuthorizationResult(n);if(e){m=!0;let r=loadContext(),i=resolveConnectionPrincipal(n,p),a=await p.completeAuthorization({callbackUrl:e.hookUrl,connection:{url:u?.url??``},principal:i,resume:e.resume,callback:e.callback});writeCachedToken(r,n,principalKey(i),a)}}try{return await a.getClient(n).executeTool(l,e)}catch(e){if(!isConnectionAuthorizationRequiredError(e)||!p)throw e;if(m)throw new ConnectionAuthorizationFailedError(n,{retryable:!1,reason:`token_rejected_after_authorization`,message:`Connection "${n}" rejected the token immediately after authorization.`});let t=getHookUrl(n);if(!t)throw e;let r=resolveConnectionPrincipal(n,p),a=resolveAuthorizationCallbackUrl({authorization:p,callbackUrl:t}),{challenge:s,resume:l}=await p.startAuthorization({callbackUrl:a,connection:{url:u?.url??``},principal:r});return requestAuthorization([{name:n,challenge:stampChallengeDisplayName(s,p),hookUrl:a,resume:l}])}}}}return _}}}function createConnectionSearchResolver(){let e=createConnectionSearchEvents();return{slug:`connection`,eventNames:Object.keys(e),events:e,sourceId:`eve:connection-search-dynamic`,sourceKind:`module`,logicalPath:`eve:framework/connection-search-dynamic`}}export{createConnectionSearchEvents,createConnectionSearchResolver,extractDiscoveredTools};
1
+ import{createLogger}from"#internal/logging.js";import{loadContext}from"#context/container.js";import{ContextKey}from"#context/key.js";import{ConnectionRegistryKey}from"#context/providers/connection-key.js";import{ConnectionAuthorizationFailedError,isConnectionAuthorizationFailedError,isConnectionAuthorizationRequiredError}from"#public/connections/errors.js";import{getAuthorizationResult,getHookUrl,requestAuthorization}from"#harness/authorization.js";import{supportsInteractiveAuthorization}from"#runtime/connections/types.js";import{resolveAuthorizationCallbackUrl,stampChallengeDisplayName}from"#runtime/connections/scoped-authorization.js";import{resolveConnectionAuthorization}from"#runtime/connections/resolve-authorization.js";import{writeCachedToken}from"#runtime/connections/authorization-tokens.js";import{principalKey,resolveConnectionPrincipal}from"#runtime/connections/principal.js";const logger=createLogger(`framework.connection-search-dynamic`),CONNECTION_SEARCH_OUTPUT_SCHEMA={items:{additionalProperties:!1,properties:{connection:{type:`string`},description:{type:`string`},error:{type:`string`},inputSchema:{type:`object`},needsAuthorization:{type:`boolean`},outputSchema:{type:`object`},qualifiedName:{type:`string`},tool:{type:`string`}},required:[`connection`,`description`],type:`object`},type:`array`},ConnectionSearchResultsKey=new ContextKey(`eve.connectionSearchResults`);function qualifiedConnectionToolName(e,t){return`${e}__${t}`}function tokenize(e){return e.toLowerCase().split(/[\s_\-./]+/).filter(e=>e.length>1)}function scoreMatch(e,t){let n=tokenize(t.name),r=tokenize(t.description),i=0;for(let t of e){for(let e of n)(e.includes(t)||t.includes(e))&&(i+=3);for(let e of r)(e.includes(t)||t.includes(e))&&(i+=1)}return i}async function resolveInteractiveAuth(e,t){let n=e.getConnections().find(e=>e.connectionName===t);if(n===void 0)return;let r=await resolveConnectionAuthorization(n);if(supportsInteractiveAuthorization(r))return r}async function completePendingAuthorizations(e){let n=loadContext(),r=new Set;for(let t of e.getConnections()){let i=getAuthorizationResult(t.connectionName);if(!i)continue;let a=await resolveInteractiveAuth(e,t.connectionName);if(!a)continue;let o=resolveConnectionPrincipal(t.connectionName,a),c=await a.completeAuthorization({callbackUrl:i.hookUrl,connection:{url:t.url??``},principal:o,resume:i.resume,callback:i.callback});writeCachedToken(n,t.connectionName,principalKey(o),c),r.add(t.connectionName)}return r}async function executeConnectionSearch(e){let n=loadContext(),i=n.get(ConnectionRegistryKey);if(i===void 0)return[];let s=await completePendingAuthorizations(i),l=e.limit??10,u=tokenize(e.keywords),d=[],f=[],m=e.connection!==void 0&&e.connection!==``?i.getConnections().filter(t=>t.connectionName===e.connection):i.getConnections(),g=[];for(let e of m){let t;try{t=await i.getClient(e.connectionName).getToolMetadata()}catch(t){if(isConnectionAuthorizationRequiredError(t)){if(s.has(e.connectionName)){logger.warn(`connection still unauthorized after authorization`,{connection:e.connectionName}),f.push({connection:e.connectionName,description:e.description,error:`Authorization for "${e.connectionName}" did not take effect; the token was rejected after sign-in.`});continue}let t=await resolveInteractiveAuth(i,e.connectionName);if(t){let n=getHookUrl(e.connectionName);if(n){let r=resolveConnectionPrincipal(e.connectionName,t),i=resolveAuthorizationCallbackUrl({authorization:t,callbackUrl:n});try{let{challenge:n,resume:a}=await t.startAuthorization({callbackUrl:i,connection:{url:e.url??``},principal:r});g.push({name:e.connectionName,challenge:stampChallengeDisplayName(n,t),hookUrl:i,resume:a})}catch(t){logger.warn(`startAuthorization failed`,{connection:e.connectionName,error:t instanceof Error?t:Error(String(t))})}}}f.push({connection:e.connectionName,description:e.description,needsAuthorization:!0});continue}if(isConnectionAuthorizationFailedError(t)){logger.warn(`connection authorization failed`,{connection:e.connectionName,reason:t.reason,retryable:t.retryable,error:t}),f.push({connection:e.connectionName,description:e.description,error:`Authorization failed for ${e.connectionName}: ${t.message}`});continue}let n=t instanceof Error?t.message:`unknown error`;logger.warn(`failed to load connection tools`,{connection:e.connectionName,error:t instanceof Error?t:Error(n)}),f.push({connection:e.connectionName,description:e.description,error:`Failed to load tools for "${e.connectionName}": ${n}`});continue}for(let n of t){let t=scoreMatch(u,n);t>0&&d.push({item:{connection:e.connectionName,description:n.description,inputSchema:n.inputSchema,outputSchema:n.outputSchema,qualifiedName:qualifiedConnectionToolName(e.connectionName,n.name),tool:n.name},score:t})}}if(g.length>0)return requestAuthorization(g);d.sort((e,t)=>t.score-e.score);let _=d.slice(0,l).map(e=>e.item);if(_.length>0){let e=[..._,...f],t=n.get(ConnectionSearchResultsKey)??[],r=new Map(t.map(e=>[e.qualifiedName,e]));for(let e of _)e.qualifiedName&&r.set(e.qualifiedName,e);return n.set(ConnectionSearchResultsKey,[...r.values()]),e}return m.map(e=>f.find(t=>t.connection===e.connectionName)||{connection:e.connectionName,description:e.description})}function extractDiscoveredTools(e){let t=new Map;for(let n of e){if(n.role!==`tool`)continue;let e=n.content;for(let n of e){if(n.type!==`tool-result`||n.toolName!==`connection_search`)continue;let e=n.output;if(e==null)continue;let r=typeof e==`object`&&`type`in e&&`value`in e?e.value:e;if(Array.isArray(r))for(let e of r)e.tool&&e.qualifiedName&&t.set(e.qualifiedName,e)}}return[...t.values()]}function createConnectionSearchEvents(){return{"step.started":async(e,n)=>{let a=loadContext().get(ConnectionRegistryKey);if(!a||a.getConnections().length===0)return null;let l=a.getConnections().map(e=>e.connectionName),u=extractDiscoveredTools(n.messages),p=loadContext().get(ConnectionSearchResultsKey)??[],g=new Map;for(let e of p)e.qualifiedName&&g.set(e.qualifiedName,e);for(let e of u)e.qualifiedName&&g.set(e.qualifiedName,e);let _=[...g.values()],v={};v.connection_search={description:`Search for tools across your connections. Discovered tools become directly callable by their qualified name (e.g. \`linear__list_issues\`) in your next response. Available connections: ${l.join(`, `)}.`,inputSchema:{type:`object`,additionalProperties:!1,properties:{keywords:{description:`Search keywords and expanded aliases. Distill intent into keywords; avoid stop words like 'a', 'the', 'in'.`,type:`string`},connection:{description:`Optional: limit search to a specific connection name.`,type:`string`},limit:{description:`Max results to return. Default 10.`,type:`number`}},required:[`keywords`]},async execute(e){return executeConnectionSearch(e)},outputSchema:CONNECTION_SEARCH_OUTPUT_SCHEMA};for(let e of _){let n=e.connection,l=e.tool,u=a.getConnectionApproval(n);v[qualifiedConnectionToolName(n,l)]={description:e.description,inputSchema:e.inputSchema??{type:`object`},approval:u,outputSchema:e.outputSchema,async execute(e,a){let u=loadContext().get(ConnectionRegistryKey),p=u.getConnections().find(e=>e.connectionName===n),m=await resolveInteractiveAuth(u,n),h=!1;if(m){let e=getAuthorizationResult(n);if(e){h=!0;let r=loadContext(),i=resolveConnectionPrincipal(n,m),a=await m.completeAuthorization({callbackUrl:e.hookUrl,connection:{url:p?.url??``},principal:i,resume:e.resume,callback:e.callback});writeCachedToken(r,n,principalKey(i),a)}}try{return await u.getClient(n).executeTool(l,e,{abortSignal:a.abortSignal})}catch(e){if(!isConnectionAuthorizationRequiredError(e)||!m)throw e;if(h)throw new ConnectionAuthorizationFailedError(n,{retryable:!1,reason:`token_rejected_after_authorization`,message:`Connection "${n}" rejected the token immediately after authorization.`});let t=getHookUrl(n);if(!t)throw e;let r=resolveConnectionPrincipal(n,m),a=resolveAuthorizationCallbackUrl({authorization:m,callbackUrl:t}),{challenge:s,resume:l}=await m.startAuthorization({callbackUrl:a,connection:{url:p?.url??``},principal:r});return requestAuthorization([{name:n,challenge:stampChallengeDisplayName(s,m),hookUrl:a,resume:l}])}}}}return v}}}function createConnectionSearchResolver(){let e=createConnectionSearchEvents();return{slug:`connection`,eventNames:Object.keys(e),events:e,sourceId:`eve:connection-search-dynamic`,sourceKind:`module`,logicalPath:`eve:framework/connection-search-dynamic`}}export{createConnectionSearchEvents,createConnectionSearchResolver,extractDiscoveredTools};
@@ -1,2 +1,2 @@
1
- import{requireSandboxSession}from"#execution/sandbox/require-sandbox.js";import{executeGlobOnSandbox}from"#execution/sandbox/glob-tool.js";const GLOB_INPUT_SCHEMA={additionalProperties:!1,properties:{limit:{description:`Maximum number of results to return. Defaults to 100.`,maximum:1e3,minimum:1,type:`integer`},path:{description:`The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.`,type:`string`},pattern:{description:`The glob pattern to match files against (e.g. "**/*.ts", "src/**/*.js").`,type:`string`}},required:[`pattern`],type:`object`},GLOB_OUTPUT_SCHEMA={additionalProperties:!1,properties:{content:{type:`string`},count:{type:`integer`},path:{type:`string`},truncated:{type:`boolean`}},required:[`content`,`count`,`path`,`truncated`],type:`object`};async function executeGlob(e){return executeGlobOnSandbox(await requireSandboxSession(),e)}const GLOB_TOOL_DEFINITION={description:[`Fast file pattern matching tool that works with any codebase size.`,``,`Usage:`,`- Supports glob patterns like "**/*.js" or "src/**/*.ts".`,`- Returns matching file paths.`,`- Use this tool when you need to find files by name patterns.`,`- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.`,`- Use the grep tool instead if you need to search file contents.`,`- Call this tool in parallel when you know there are multiple patterns to search for.`].join(`
1
+ import{requireSandboxSession}from"#execution/sandbox/require-sandbox.js";import{executeGlobOnSandbox}from"#execution/sandbox/glob-tool.js";const GLOB_INPUT_SCHEMA={additionalProperties:!1,properties:{limit:{description:`Maximum number of results to return. Defaults to 100.`,maximum:1e3,minimum:1,type:`integer`},path:{description:`The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.`,type:`string`},pattern:{description:`The glob pattern to match files against (e.g. "**/*.ts", "src/**/*.js").`,type:`string`}},required:[`pattern`],type:`object`},GLOB_OUTPUT_SCHEMA={additionalProperties:!1,properties:{content:{type:`string`},count:{type:`integer`},path:{type:`string`},truncated:{type:`boolean`}},required:[`content`,`count`,`path`,`truncated`],type:`object`};async function executeGlob(e,t){return executeGlobOnSandbox(await requireSandboxSession(t?.abortSignal),e)}const GLOB_TOOL_DEFINITION={description:[`Fast file pattern matching tool that works with any codebase size.`,``,`Usage:`,`- Supports glob patterns like "**/*.js" or "src/**/*.ts".`,`- Returns matching file paths.`,`- Use this tool when you need to find files by name patterns.`,`- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.`,`- Use the grep tool instead if you need to search file contents.`,`- Call this tool in parallel when you know there are multiple patterns to search for.`].join(`
2
2
  `),execute:executeGlob,inputSchema:GLOB_INPUT_SCHEMA,logicalPath:`eve:framework/glob`,name:`glob`,outputSchema:GLOB_OUTPUT_SCHEMA,sourceId:`eve:glob-tool`,sourceKind:`module`};export{GLOB_INPUT_SCHEMA,GLOB_OUTPUT_SCHEMA,GLOB_TOOL_DEFINITION};
@@ -1,2 +1,2 @@
1
- import{requireSandboxSession}from"#execution/sandbox/require-sandbox.js";import{executeGrepOnSandbox}from"#execution/sandbox/grep-tool.js";const GREP_INPUT_SCHEMA={additionalProperties:!1,properties:{context:{description:`Number of surrounding context lines to include before and after each match. Defaults to 0.`,minimum:0,type:`integer`},glob:{description:`Filter files by glob pattern (e.g. "*.ts", "*.{ts,tsx}").`,type:`string`},ignoreCase:{description:`Perform case-insensitive search. Defaults to false.`,type:`boolean`},limit:{description:`Maximum number of matches to return per file. Defaults to 100.`,maximum:1e3,minimum:1,type:`integer`},literal:{description:`Treat the pattern as a literal string instead of a regular expression. Defaults to false.`,type:`boolean`},path:{description:`The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.`,type:`string`},pattern:{description:`The regex pattern to search for in file contents (e.g. "log.*Error", "function\\s+\\w+").`,type:`string`}},required:[`pattern`],type:`object`},GREP_OUTPUT_SCHEMA={additionalProperties:!1,properties:{content:{type:`string`},matchCount:{type:`integer`},path:{type:`string`},truncated:{type:`boolean`}},required:[`content`,`matchCount`,`path`,`truncated`],type:`object`};async function executeGrep(e){return executeGrepOnSandbox(await requireSandboxSession(),e)}const GREP_TOOL_DEFINITION={description:[`Fast content search tool that works with any codebase size.`,``,`Usage:`,`- Searches file contents using regular expressions.`,`- Supports full regex syntax (e.g. "log.*Error", "function\\s+\\w+").`,`- Filter files by pattern with the glob parameter (e.g. "*.js", "*.{ts,tsx}").`,`- Returns matching lines with file paths and line numbers.`,`- Use this tool when you need to find files containing specific patterns.`,`- Use the glob tool instead if you only need to find files by name.`,`- Call this tool in parallel when you have multiple independent searches.`,`- Any line longer than 2000 characters is truncated.`].join(`
1
+ import{requireSandboxSession}from"#execution/sandbox/require-sandbox.js";import{executeGrepOnSandbox}from"#execution/sandbox/grep-tool.js";const GREP_INPUT_SCHEMA={additionalProperties:!1,properties:{context:{description:`Number of surrounding context lines to include before and after each match. Defaults to 0.`,minimum:0,type:`integer`},glob:{description:`Filter files by glob pattern (e.g. "*.ts", "*.{ts,tsx}").`,type:`string`},ignoreCase:{description:`Perform case-insensitive search. Defaults to false.`,type:`boolean`},limit:{description:`Maximum number of matches to return per file. Defaults to 100.`,maximum:1e3,minimum:1,type:`integer`},literal:{description:`Treat the pattern as a literal string instead of a regular expression. Defaults to false.`,type:`boolean`},path:{description:`The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.`,type:`string`},pattern:{description:`The regex pattern to search for in file contents (e.g. "log.*Error", "function\\s+\\w+").`,type:`string`}},required:[`pattern`],type:`object`},GREP_OUTPUT_SCHEMA={additionalProperties:!1,properties:{content:{type:`string`},matchCount:{type:`integer`},path:{type:`string`},truncated:{type:`boolean`}},required:[`content`,`matchCount`,`path`,`truncated`],type:`object`};async function executeGrep(e,t){return executeGrepOnSandbox(await requireSandboxSession(t?.abortSignal),e)}const GREP_TOOL_DEFINITION={description:[`Fast content search tool that works with any codebase size.`,``,`Usage:`,`- Searches file contents using regular expressions.`,`- Supports full regex syntax (e.g. "log.*Error", "function\\s+\\w+").`,`- Filter files by pattern with the glob parameter (e.g. "*.js", "*.{ts,tsx}").`,`- Returns matching lines with file paths and line numbers.`,`- Use this tool when you need to find files containing specific patterns.`,`- Use the glob tool instead if you only need to find files by name.`,`- Call this tool in parallel when you have multiple independent searches.`,`- Any line longer than 2000 characters is truncated.`].join(`
2
2
  `),execute:executeGrep,inputSchema:GREP_INPUT_SCHEMA,logicalPath:`eve:framework/grep`,name:`grep`,outputSchema:GREP_OUTPUT_SCHEMA,sourceId:`eve:grep-tool`,sourceKind:`module`};export{GREP_INPUT_SCHEMA,GREP_OUTPUT_SCHEMA,GREP_TOOL_DEFINITION};
@@ -1,2 +1,2 @@
1
- import{requireSandboxSession}from"#execution/sandbox/require-sandbox.js";import{executeReadFileOnSandbox}from"#execution/sandbox/read-file-tool.js";const READ_FILE_INPUT_SCHEMA={additionalProperties:!1,properties:{filePath:{description:`The absolute path to the file to read.`,type:`string`},limit:{description:`Maximum number of lines to return. Defaults to 2000.`,minimum:1,type:`integer`},offset:{description:`1-based line number to start from. Defaults to 1.`,minimum:1,type:`integer`}},required:[`filePath`],type:`object`},READ_FILE_OUTPUT_SCHEMA={additionalProperties:!1,properties:{content:{type:`string`},nextOffset:{minimum:1,type:`integer`},path:{type:`string`},totalLines:{minimum:0,type:`integer`},truncated:{type:`boolean`}},required:[`content`,`path`,`totalLines`,`truncated`],type:`object`};async function executeReadFile(e){return executeReadFileOnSandbox(await requireSandboxSession(),e)}const READ_FILE_TOOL_DEFINITION={description:[`Read a file from the local filesystem. If the path does not exist, an error is returned.`,``,`Usage:`,`- The filePath parameter should be an absolute path.`,`- By default, this tool returns up to 2000 lines from the start of the file.`,`- The offset parameter is the line number to start from (1-indexed).`,`- To read later sections, call this tool again with a larger offset.`,`- Use the grep tool to find specific content in large files or files with long lines.`,`- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.`,'- Contents are returned with each line prefixed by its line number as `<line>: <content>`. For example, if a file has contents "foo\\n", you will receive "1: foo\\n".',`- Any line longer than 2000 characters is truncated.`,`- Call this tool in parallel when you know there are multiple files you want to read.`,`- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.`].join(`
1
+ import{requireSandboxSession}from"#execution/sandbox/require-sandbox.js";import{executeReadFileOnSandbox}from"#execution/sandbox/read-file-tool.js";const READ_FILE_INPUT_SCHEMA={additionalProperties:!1,properties:{filePath:{description:`The absolute path to the file to read.`,type:`string`},limit:{description:`Maximum number of lines to return. Defaults to 2000.`,minimum:1,type:`integer`},offset:{description:`1-based line number to start from. Defaults to 1.`,minimum:1,type:`integer`}},required:[`filePath`],type:`object`},READ_FILE_OUTPUT_SCHEMA={additionalProperties:!1,properties:{content:{type:`string`},nextOffset:{minimum:1,type:`integer`},path:{type:`string`},totalLines:{minimum:0,type:`integer`},truncated:{type:`boolean`}},required:[`content`,`path`,`totalLines`,`truncated`],type:`object`};async function executeReadFile(e,t){return executeReadFileOnSandbox(await requireSandboxSession(t?.abortSignal),e)}const READ_FILE_TOOL_DEFINITION={description:[`Read a file from the local filesystem. If the path does not exist, an error is returned.`,``,`Usage:`,`- The filePath parameter should be an absolute path.`,`- By default, this tool returns up to 2000 lines from the start of the file.`,`- The offset parameter is the line number to start from (1-indexed).`,`- To read later sections, call this tool again with a larger offset.`,`- Use the grep tool to find specific content in large files or files with long lines.`,`- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.`,'- Contents are returned with each line prefixed by its line number as `<line>: <content>`. For example, if a file has contents "foo\\n", you will receive "1: foo\\n".',`- Any line longer than 2000 characters is truncated.`,`- Call this tool in parallel when you know there are multiple files you want to read.`,`- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.`].join(`
2
2
  `),execute:executeReadFile,inputSchema:READ_FILE_INPUT_SCHEMA,logicalPath:`eve:framework/read-file`,name:`read_file`,outputSchema:READ_FILE_OUTPUT_SCHEMA,sourceId:`eve:read-file-tool`,sourceKind:`module`};export{READ_FILE_INPUT_SCHEMA,READ_FILE_OUTPUT_SCHEMA,READ_FILE_TOOL_DEFINITION};
@@ -1,2 +1,2 @@
1
- import{executeWebFetchTool}from"#execution/web-fetch/tool.js";async function executeWebFetch(e){return executeWebFetchTool(e)}const WEB_FETCH_INPUT_SCHEMA={additionalProperties:!1,properties:{format:{description:`The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to "markdown".`,enum:[`markdown`,`text`,`html`],type:`string`},timeout:{description:`Optional timeout in seconds. Defaults to 30, max 120.`,type:`number`},url:{description:`The fully-formed URL to fetch content from. Must start with http:// or https://.`,type:`string`}},required:[`url`],type:`object`},WEB_FETCH_OUTPUT_SCHEMA={additionalProperties:!1,properties:{content:{type:`string`},contentType:{type:`string`},truncated:{type:`boolean`},url:{type:`string`}},required:[`content`,`contentType`,`url`,`truncated`],type:`object`},WEB_FETCH_TOOL_DEFINITION={description:[`Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.`,``,`Usage notes:`,`- The URL must be a fully-formed valid URL starting with http:// or https://`,`- HTML responses are automatically converted to markdown or plain text based on the requested format`,`- Format options: "markdown" (default), "text", or "html"`,`- Default timeout is 30 seconds (max 120 seconds)`,`- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)`,`- This tool is read-only and does not modify any files`].join(`
1
+ import{executeWebFetchTool}from"#execution/web-fetch/tool.js";async function executeWebFetch(e,t){return executeWebFetchTool(e,{abortSignal:t?.abortSignal})}const WEB_FETCH_INPUT_SCHEMA={additionalProperties:!1,properties:{format:{description:`The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to "markdown".`,enum:[`markdown`,`text`,`html`],type:`string`},timeout:{description:`Optional timeout in seconds. Defaults to 30, max 120.`,type:`number`},url:{description:`The fully-formed URL to fetch content from. Must start with http:// or https://.`,type:`string`}},required:[`url`],type:`object`},WEB_FETCH_OUTPUT_SCHEMA={additionalProperties:!1,properties:{content:{type:`string`},contentType:{type:`string`},truncated:{type:`boolean`},url:{type:`string`}},required:[`content`,`contentType`,`url`,`truncated`],type:`object`},WEB_FETCH_TOOL_DEFINITION={description:[`Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.`,``,`Usage notes:`,`- The URL must be a fully-formed valid URL starting with http:// or https://`,`- HTML responses are automatically converted to markdown or plain text based on the requested format`,`- Format options: "markdown" (default), "text", or "html"`,`- Default timeout is 30 seconds (max 120 seconds)`,`- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)`,`- This tool is read-only and does not modify any files`].join(`
2
2
  `),execute:executeWebFetch,inputSchema:WEB_FETCH_INPUT_SCHEMA,logicalPath:`eve:framework/web-fetch`,name:`web_fetch`,outputSchema:WEB_FETCH_OUTPUT_SCHEMA,sourceId:`eve:web-fetch-tool`,sourceKind:`module`};export{WEB_FETCH_INPUT_SCHEMA,WEB_FETCH_OUTPUT_SCHEMA,WEB_FETCH_TOOL_DEFINITION};
@@ -1,2 +1,2 @@
1
- import{requireSandboxSession}from"#execution/sandbox/require-sandbox.js";import{executeWriteFileOnSandbox}from"#execution/sandbox/write-file-tool.js";const WRITE_FILE_INPUT_SCHEMA={additionalProperties:!1,properties:{content:{description:`Complete replacement file contents.`,type:`string`},filePath:{description:`The absolute path to the file to write (must be absolute, not relative).`,type:`string`}},required:[`filePath`,`content`],type:`object`},WRITE_FILE_OUTPUT_SCHEMA={additionalProperties:!1,properties:{existed:{type:`boolean`},path:{type:`string`}},required:[`existed`,`path`],type:`object`};async function executeWriteFile(e){return executeWriteFileOnSandbox(await requireSandboxSession(),e)}const WRITE_FILE_TOOL_DEFINITION={description:[`Writes a file to the local filesystem.`,``,`Usage:`,`- This tool will overwrite the existing file if there is one at the provided path.`,`- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.`,`- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.`,`- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.`,`- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.`].join(`
1
+ import{requireSandboxSession}from"#execution/sandbox/require-sandbox.js";import{executeWriteFileOnSandbox}from"#execution/sandbox/write-file-tool.js";const WRITE_FILE_INPUT_SCHEMA={additionalProperties:!1,properties:{content:{description:`Complete replacement file contents.`,type:`string`},filePath:{description:`The absolute path to the file to write (must be absolute, not relative).`,type:`string`}},required:[`filePath`,`content`],type:`object`},WRITE_FILE_OUTPUT_SCHEMA={additionalProperties:!1,properties:{existed:{type:`boolean`},path:{type:`string`}},required:[`existed`,`path`],type:`object`};async function executeWriteFile(e,t){return executeWriteFileOnSandbox(await requireSandboxSession(t?.abortSignal),e)}const WRITE_FILE_TOOL_DEFINITION={description:[`Writes a file to the local filesystem.`,``,`Usage:`,`- This tool will overwrite the existing file if there is one at the provided path.`,`- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.`,`- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.`,`- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.`,`- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.`].join(`
2
2
  `),execute:executeWriteFile,inputSchema:WRITE_FILE_INPUT_SCHEMA,logicalPath:`eve:framework/write-file`,name:`write_file`,outputSchema:WRITE_FILE_OUTPUT_SCHEMA,sourceId:`eve:write-file-tool`,sourceKind:`module`};export{WRITE_FILE_INPUT_SCHEMA,WRITE_FILE_OUTPUT_SCHEMA,WRITE_FILE_TOOL_DEFINITION};
@@ -28,6 +28,5 @@ export interface SandboxState {
28
28
  */
29
29
  export interface SandboxAccess {
30
30
  captureState(): Promise<SandboxState>;
31
- dispose(): Promise<void>;
32
31
  get(): Promise<SandboxSession | null>;
33
32
  }
@@ -1,4 +1,4 @@
1
- import{pinnedNodeEngineMajor}from"../../node-engine.js";import{pathExists,writeTextFile}from"../files.js";import{resolveVersionToken}from"../version-tokens.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS}from"../update/module-files.js";import{WEB_APP_TEMPLATE_FILES}from"./web-template.js";import{basename,join,resolve}from"node:path";import{mkdir,readdir,stat}from"node:fs/promises";const CURRENT_DIRECTORY_PROJECT_NAME=`.`,ALLOWED_CREATE_IN_PLACE_ENTRIES=new Set([`.DS_Store`,`.git`,`.gitkeep`,`.hg`]),DEFAULT_AI_PACKAGE_VERSION=`^7.0.0`,DEFAULT_CONNECT_PACKAGE_VERSION=`0.2.2`,DEFAULT_ZOD_PACKAGE_VERSION=`4.4.3`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.18.2`,nodeEngine:`>=24`};function resolveEvePackageContract(e=DEFAULT_EVE_PACKAGE_CONTRACT){return{version:resolveVersionToken(`evePackage.version`,e.version),nodeEngine:resolveVersionToken(`evePackage.nodeEngine`,e.nodeEngine)}}function modelProviderSlug(e){let t=(e.split(`/`)[0]??``).replaceAll(/[^A-Za-z0-9._-]/gu,``);return t.length>0?t:`anthropic`}function byokProviderEnvVar(e){let t=modelProviderSlug(e).toUpperCase().replaceAll(/[^A-Z0-9]/gu,`_`);return`${/^[0-9]/.test(t)?`_`:``}${t}_API_KEY`}function agentTemplateFiles(e){return{"agent/agent.ts":BASE_AGENT_TEMPLATE.replaceAll(`__EVE_INIT_MODEL__`,e),"agent/channels/eve.ts":WEB_APP_TEMPLATE_FILES[`agent/channels/eve.ts`],"agent/instructions.md":AGENT_INSTRUCTIONS_TEMPLATE}}function renderTemplate(e,t){return e.replaceAll(`__EVE_INIT_APP_NAME__`,t.appName).replaceAll(`__EVE_INIT_MODEL__`,t.model).replaceAll(`__EVE_INIT_BYOK_PROVIDER__`,modelProviderSlug(t.model)).replaceAll(`__EVE_INIT_BYOK_ENV_VAR__`,byokProviderEnvVar(t.model)).replaceAll(`__EVE_INIT_PACKAGE_VERSION__`,formatEveDependencySpecifier(t.eveVersion)).replaceAll(`__EVE_INIT_AI_SDK_VERSION__`,t.aiPackageVersion).replaceAll(`__EVE_INIT_CONNECT_VERSION__`,t.connectPackageVersion).replaceAll(`__EVE_INIT_ZOD_VERSION__`,t.zodPackageVersion).replaceAll(`__EVE_INIT_TYPESCRIPT_VERSION__`,t.typescriptPackageVersion).replaceAll(`__EVE_INIT_TYPES_NODE_VERSION__`,t.nodeTypesVersion).replaceAll(`__EVE_INIT_NODE_ENGINE__`,t.nodeEngine)}function formatEveDependencySpecifier(e){return/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/.test(e)?`^${e}`:e}const BASE_AGENT_TEMPLATE=`import { defineAgent } from "eve";
1
+ import{pinnedNodeEngineMajor}from"../../node-engine.js";import{pathExists,writeTextFile}from"../files.js";import{resolveVersionToken}from"../version-tokens.js";import{applyPackageManagerWorkspaceConfiguration,isPackageManagerWorkspaceMember,patchWorkspaceRootPackageJson}from"../workspace-root.js";import{SUPPORTED_AUTHORED_MODULE_FILE_EXTENSIONS}from"../update/module-files.js";import{WEB_APP_TEMPLATE_FILES}from"./web-template.js";import{basename,join,resolve}from"node:path";import{mkdir,readdir,stat}from"node:fs/promises";const CURRENT_DIRECTORY_PROJECT_NAME=`.`,ALLOWED_CREATE_IN_PLACE_ENTRIES=new Set([`.DS_Store`,`.git`,`.gitkeep`,`.hg`]),DEFAULT_AI_PACKAGE_VERSION=`^7.0.0`,DEFAULT_CONNECT_PACKAGE_VERSION=`0.2.2`,DEFAULT_ZOD_PACKAGE_VERSION=`4.4.3`,DEFAULT_EVE_PACKAGE_CONTRACT={version:`0.20.0`,nodeEngine:`>=24`};function resolveEvePackageContract(e=DEFAULT_EVE_PACKAGE_CONTRACT){return{version:resolveVersionToken(`evePackage.version`,e.version),nodeEngine:resolveVersionToken(`evePackage.nodeEngine`,e.nodeEngine)}}function modelProviderSlug(e){let t=(e.split(`/`)[0]??``).replaceAll(/[^A-Za-z0-9._-]/gu,``);return t.length>0?t:`anthropic`}function byokProviderEnvVar(e){let t=modelProviderSlug(e).toUpperCase().replaceAll(/[^A-Z0-9]/gu,`_`);return`${/^[0-9]/.test(t)?`_`:``}${t}_API_KEY`}function agentTemplateFiles(e){return{"agent/agent.ts":BASE_AGENT_TEMPLATE.replaceAll(`__EVE_INIT_MODEL__`,e),"agent/channels/eve.ts":WEB_APP_TEMPLATE_FILES[`agent/channels/eve.ts`],"agent/instructions.md":AGENT_INSTRUCTIONS_TEMPLATE}}function renderTemplate(e,t){return e.replaceAll(`__EVE_INIT_APP_NAME__`,t.appName).replaceAll(`__EVE_INIT_MODEL__`,t.model).replaceAll(`__EVE_INIT_BYOK_PROVIDER__`,modelProviderSlug(t.model)).replaceAll(`__EVE_INIT_BYOK_ENV_VAR__`,byokProviderEnvVar(t.model)).replaceAll(`__EVE_INIT_PACKAGE_VERSION__`,formatEveDependencySpecifier(t.eveVersion)).replaceAll(`__EVE_INIT_AI_SDK_VERSION__`,t.aiPackageVersion).replaceAll(`__EVE_INIT_CONNECT_VERSION__`,t.connectPackageVersion).replaceAll(`__EVE_INIT_ZOD_VERSION__`,t.zodPackageVersion).replaceAll(`__EVE_INIT_TYPESCRIPT_VERSION__`,t.typescriptPackageVersion).replaceAll(`__EVE_INIT_TYPES_NODE_VERSION__`,t.nodeTypesVersion).replaceAll(`__EVE_INIT_NODE_ENGINE__`,t.nodeEngine)}function formatEveDependencySpecifier(e){return/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/.test(e)?`^${e}`:e}const BASE_AGENT_TEMPLATE=`import { defineAgent } from "eve";
2
2
 
3
3
  export default defineAgent({
4
4
  model: "__EVE_INIT_MODEL__",
@@ -4,7 +4,10 @@ import type { SessionContext } from "#public/definitions/callback-context.js";
4
4
  import type { Approval } from "#public/definitions/approval.js";
5
5
  import type { SessionAuth } from "#context/keys.js";
6
6
  import type { HandleMessageStreamEvent } from "#protocol/message.js";
7
- type ToolContext = SessionContext;
7
+ type ToolContext = SessionContext & {
8
+ /** Aborts when the active turn is cancelled. */
9
+ readonly abortSignal: AbortSignal;
10
+ };
8
11
  /**
9
12
  * Stream event types allowed for dynamic tool resolvers. Dispatch
10
13
  * supports any event; this extract restricts the public surface until
@@ -11,7 +11,13 @@ export interface SandboxBackendHandle<SO = Record<string, never>> {
11
11
  readonly session: SandboxSession;
12
12
  readonly useSessionFn: SandboxSessionUseFn<SO>;
13
13
  captureState(): Promise<SandboxBackendSessionState>;
14
- dispose(): Promise<void>;
14
+ /**
15
+ * Stops the underlying compute because the eve server is shutting
16
+ * down; nothing may be left running afterwards. The session must
17
+ * remain reattachable from persisted state on the next server start
18
+ * when the backend supports durable sessions.
19
+ */
20
+ shutdown(): Promise<void>;
15
21
  }
16
22
  /**
17
23
  * Serializable per-sandbox reconnect record stored on the harness session.