eve 0.79.1 → 0.80.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/dist/src/channel/pending-authorization.d.ts +10 -0
  3. package/dist/src/channel/pending-authorization.js +1 -0
  4. package/dist/src/compiled/.vendor-stamp.json +7 -7
  5. package/dist/src/compiled/@chat-adapter/slack/api.d.ts +6 -0
  6. package/dist/src/compiled/@chat-adapter/slack/api.js +1 -1
  7. package/dist/src/compiled/@chat-adapter/slack/blocks.js +1 -1
  8. package/dist/src/compiled/@chat-adapter/slack/format.js +1 -1
  9. package/dist/src/compiled/@chat-adapter/slack/index.d.ts +14 -8
  10. package/dist/src/compiled/@chat-adapter/slack/index.js +33 -32
  11. package/dist/src/compiled/@chat-adapter/twilio/index.d.ts +37 -1
  12. package/dist/src/compiled/@chat-adapter/twilio/index.js +2 -2
  13. package/dist/src/compiled/@eve/tracing/index.js +1 -1
  14. package/dist/src/compiled/@eve/tracing/otel.js +1 -1
  15. package/dist/src/compiled/@linqapp/chat-sdk-adapter/index.js +1 -1
  16. package/dist/src/compiled/@photon-ai/chat-adapter-imessage/index.js +1 -1
  17. package/dist/src/compiled/@vercel/connect/index.js +5 -5
  18. package/dist/src/compiled/@vercel/connect/token.d.ts +11 -1
  19. package/dist/src/compiled/_chunks/node/chunk-NBSLXVG3-Bt7pDfyn.js +12 -0
  20. package/dist/src/compiled/_chunks/node/dist-BKsV21Vu.js +6 -0
  21. package/dist/src/compiled/_chunks/node/dist-CO_n_hUa.js +98 -0
  22. package/dist/src/compiled/_chunks/workflow/{mcp-C01bDEX-.js → mcp-Bwx_LQ4q.js} +4 -4
  23. package/dist/src/compiled/chat/index.js +1 -1
  24. package/dist/src/compiler/extension-compatibility.d.ts +18 -18
  25. package/dist/src/compiler/extension-compatibility.js +1 -1
  26. package/dist/src/compiler/manifest.d.ts +8 -4
  27. package/dist/src/compiler/manifest.js +1 -1
  28. package/dist/src/compiler/normalize-connection.js +1 -1
  29. package/dist/src/compiler/normalize-manifest.js +1 -1
  30. package/dist/src/context/schedule-collection-tools.js +1 -1
  31. package/dist/src/execution/catalog/connection-auth.d.ts +4 -2
  32. package/dist/src/execution/catalog/connection-auth.js +1 -1
  33. package/dist/src/execution/catalog/connection-entry.js +1 -1
  34. package/dist/src/execution/dynamic-workflow/tool.js +1 -1
  35. package/dist/src/execution/proxied-deliver-step.js +1 -1
  36. package/dist/src/execution/publish-session-events.d.ts +13 -3
  37. package/dist/src/execution/publish-session-events.js +1 -1
  38. package/dist/src/execution/sandbox/bindings/microsandbox-runtime.js +1 -1
  39. package/dist/src/execution/session/active-turn.d.ts +2 -1
  40. package/dist/src/execution/session/active-turn.js +1 -1
  41. package/dist/src/execution/session/entry.js +1 -1
  42. package/dist/src/execution/session/turn-step.js +1 -1
  43. package/dist/src/execution/session/turn.d.ts +1 -0
  44. package/dist/src/execution/session/turn.js +1 -1
  45. package/dist/src/execution/session-event-stream.js +1 -1
  46. package/dist/src/execution/stable-workflow-names.d.ts +2 -0
  47. package/dist/src/execution/stable-workflow-names.js +1 -1
  48. package/dist/src/execution/tasks/task-id-input.js +1 -1
  49. package/dist/src/execution/tools/agent-router-workflow.js +1 -1
  50. package/dist/src/execution/tools/workflow/apply-run-messages-step.d.ts +6 -2
  51. package/dist/src/execution/tools/workflow/apply-run-messages-step.js +1 -1
  52. package/dist/src/execution/tools/workflow/body.d.ts +10 -1
  53. package/dist/src/execution/tools/workflow/body.js +1 -1
  54. package/dist/src/execution/tools/workflow/owner-inbox.js +1 -1
  55. package/dist/src/execution/tools/workflow/serve.js +1 -1
  56. package/dist/src/execution/web-fetch/execute.js +1 -1
  57. package/dist/src/execution/workflow-runtime.d.ts +6 -0
  58. package/dist/src/execution/workflow-runtime.js +1 -1
  59. package/dist/src/extensions/git/extension/extension.d.ts +3 -1
  60. package/dist/src/extensions/git/extension/extension.js +1 -1
  61. package/dist/src/extensions/git/extension/lib/gh-tool.js +1 -1
  62. package/dist/src/extensions/git/extension/lib/github-shell.d.ts +14 -0
  63. package/dist/src/extensions/git/extension/lib/github-shell.js +1 -1
  64. package/dist/src/harness/action-result-helpers.js +1 -1
  65. package/dist/src/harness/attachment-staging.js +1 -1
  66. package/dist/src/harness/compaction/engine.js +3 -1
  67. package/dist/src/harness/compaction/prompt.d.ts +7 -0
  68. package/dist/src/harness/compaction/prompt.js +1 -1
  69. package/dist/src/harness/hitl/approvals.js +1 -1
  70. package/dist/src/harness/hitl/approved-calls.js +1 -1
  71. package/dist/src/harness/input-request-resolution.d.ts +2 -1
  72. package/dist/src/harness/input-request-resolution.js +1 -1
  73. package/dist/src/harness/model-call/call.js +1 -1
  74. package/dist/src/harness/model-call/errors.d.ts +2 -2
  75. package/dist/src/harness/model-call/recovery.d.ts +2 -3
  76. package/dist/src/instrumentation/native-events.js +1 -1
  77. package/dist/src/instrumentation/session-init.d.ts +2 -1
  78. package/dist/src/instrumentation/session-init.js +1 -1
  79. package/dist/src/internal/application/optional-package-import.d.ts +5 -0
  80. package/dist/src/internal/application/optional-package-import.js +1 -1
  81. package/dist/src/internal/application/stamped-package-version.js +1 -1
  82. package/dist/src/internal/authored-definition/connection.js +1 -1
  83. package/dist/src/internal/authored-definition/schedule-collection.js +1 -1
  84. package/dist/src/internal/nitro/host/application-route-registry.d.ts +7 -2
  85. package/dist/src/internal/nitro/host/application-route-registry.js +1 -1
  86. package/dist/src/internal/nitro/host/configure-nitro-routes.js +1 -1
  87. package/dist/src/internal/nitro/host/start-production-server.js +2 -2
  88. package/dist/src/internal/nitro/routes/channel-request-instrumentation.d.ts +3 -1
  89. package/dist/src/internal/nitro/routes/channel-request-instrumentation.js +1 -1
  90. package/dist/src/internal/nitro/routes/connection-events.d.ts +5 -0
  91. package/dist/src/internal/nitro/routes/connection-events.js +1 -0
  92. package/dist/src/internal/workflow-bundle/builder.js +1 -1
  93. package/dist/src/internal/workflow-bundle/dynamic-tool-ast-references.js +1 -1
  94. package/dist/src/protocol/message.d.ts +2 -0
  95. package/dist/src/protocol/message.js +1 -1
  96. package/dist/src/public/channels/slack/blocks.js +2 -2
  97. package/dist/src/public/channels/teams/authorization.d.ts +53 -0
  98. package/dist/src/public/channels/teams/authorization.js +1 -0
  99. package/dist/src/public/channels/teams/defaults.js +2 -2
  100. package/dist/src/public/channels/teams/hitl.d.ts +2 -1
  101. package/dist/src/public/channels/teams/teamsChannel.js +1 -1
  102. package/dist/src/public/channels/telegram/authorization-callback.js +1 -1
  103. package/dist/src/public/connections/index.d.ts +1 -0
  104. package/dist/src/public/definitions/connections/events.d.ts +43 -0
  105. package/dist/src/public/definitions/connections/events.js +1 -0
  106. package/dist/src/public/definitions/connections/mcp.d.ts +3 -0
  107. package/dist/src/public/next/index.js +1 -1
  108. package/dist/src/public/next/server.d.ts +11 -2
  109. package/dist/src/public/next/server.js +1 -1
  110. package/dist/src/public/schedules/subscription.d.ts +4 -3
  111. package/dist/src/runtime/connections/events/actions.d.ts +17 -0
  112. package/dist/src/runtime/connections/events/actions.js +1 -0
  113. package/dist/src/runtime/connections/events/delivery.d.ts +10 -0
  114. package/dist/src/runtime/connections/events/delivery.js +1 -0
  115. package/dist/src/runtime/connections/events/dispatch-step.d.ts +20 -0
  116. package/dist/src/runtime/connections/events/dispatch-step.js +1 -0
  117. package/dist/src/runtime/connections/events/lifecycle-step.d.ts +42 -0
  118. package/dist/src/runtime/connections/events/lifecycle-step.js +1 -0
  119. package/dist/src/runtime/connections/events/owner.d.ts +38 -0
  120. package/dist/src/runtime/connections/events/owner.js +1 -0
  121. package/dist/src/runtime/connections/events/path.d.ts +4 -0
  122. package/dist/src/runtime/connections/events/path.js +1 -0
  123. package/dist/src/runtime/connections/events/state.d.ts +37 -0
  124. package/dist/src/runtime/connections/events/state.js +1 -0
  125. package/dist/src/runtime/connections/events/workflow.d.ts +28 -0
  126. package/dist/src/runtime/connections/events/workflow.js +1 -0
  127. package/dist/src/runtime/connections/mcp-client.d.ts +4 -2
  128. package/dist/src/runtime/connections/mcp-client.js +1 -1
  129. package/dist/src/runtime/connections/openapi-client.js +1 -1
  130. package/dist/src/runtime/connections/scoped-authorization.js +1 -1
  131. package/dist/src/runtime/resolve-connection.js +1 -1
  132. package/dist/src/runtime/schedules/collection-client.d.ts +0 -2
  133. package/dist/src/runtime/schedules/collection-client.js +1 -1
  134. package/dist/src/runtime/types.d.ts +11 -0
  135. package/dist/src/setup/scaffold/create/add-to-project.js +1 -1
  136. package/dist/src/setup/scaffold/create/project.js +2 -2
  137. package/dist/src/setup/scaffold/update/channels.js +1 -1
  138. package/dist/src/setup/scaffold/update/connections.js +2 -2
  139. package/dist/src/setup/scaffold/version-tokens.d.ts +1 -1
  140. package/dist/src/setup/scaffold/version-tokens.js +1 -1
  141. package/dist/src/shared/action-types.d.ts +1 -0
  142. package/dist/src/shared/action-types.js +1 -1
  143. package/dist/src/shared/agent-definition.d.ts +12 -4
  144. package/dist/src/shared/connection-events.d.ts +84 -0
  145. package/dist/src/shared/connection-events.js +1 -0
  146. package/dist/src/shared/connection-types.d.ts +10 -0
  147. package/dist/src/shared/validate-authorization.js +1 -1
  148. package/dist/src/tools/provided/ask-question-workflow.js +1 -1
  149. package/dist/src/tools/provided/no-reply.d.ts +5 -0
  150. package/dist/src/tools/provided/sleep-workflow.js +1 -1
  151. package/dist/src/tools/signature.d.ts +4 -2
  152. package/dist/src/tools/signature.js +1 -1
  153. package/dist/src/tracing/eve/connection-events.d.ts +25 -0
  154. package/dist/src/tracing/eve/connection-events.js +1 -0
  155. package/dist/src/tracing/eve/install-instrumentation-runtime.js +1 -1
  156. package/dist/src/tracing/eve/mcp.d.ts +1 -0
  157. package/dist/src/tracing/eve/mcp.js +1 -1
  158. package/dist/src/tracing/eve/otel-declaration.d.ts +5 -0
  159. package/docs/agent-config.md +38 -13
  160. package/docs/channels/teams.mdx +6 -0
  161. package/docs/code-extension.mdx +28 -0
  162. package/docs/concepts/built-in-tools.md +3 -1
  163. package/docs/connections/mcp.mdx +105 -0
  164. package/docs/guides/hooks.md +4 -0
  165. package/docs/guides/instrumentation/instrumentation.mdx +2 -0
  166. package/docs/guides/instrumentation/otel.mdx +10 -0
  167. package/docs/sandbox/vercel.mdx +46 -0
  168. package/package.json +7 -7
  169. package/dist/src/compiled/_chunks/node/chunk-6QHBXN7B-gl4n6nmi.js +0 -12
  170. package/dist/src/compiled/_chunks/node/dist-DYDVZ-FS.js +0 -98
  171. package/dist/src/compiled/_chunks/node/dist-iEFkK9qw.js +0 -6
@@ -1 +1 @@
1
- import{createLogger,formatError}from"#internal/logging.js";import{contextStorage}from"#context/container.js";import{ConversationIdKey}from"#context/keys.js";import{resolveInstrumentationEnvironment}from"#internal/application/dev-environment.js";import{readConversationId}from"#shared/conversation-identity.js";import{ContextAgentTraceStateStore}from"#tracing/eve/agent-trace-context-store.js";import{registerInstrumentationRuntime}from"#instrumentation/runtime.js";import{createInstrumentationHooks}from"#instrumentation/lifecycle.js";import{AgentSpanIdGenerator,otelTelemetry}from"#compiled/@eve/tracing/index.js";import{hasConversationRelease}from"#tracing/local/traces.js";import{createAgentOtelInstrumentation}from"#tracing/eve/agent-otel-provider.js";import{registerOtel}from"#tracing/eve/otel-registration.js";import{eveOutputMapping}from"#tracing/eve/profile.js";const log=createLogger(`tracing.install-instrumentation-runtime`);function installInstrumentationRuntime(e){let t=[],n=[],r,i,a,runInContext=(e,t)=>t(),o;if(e.collected.declared){r=registerOtel({serviceName:e.serviceName,otel:e.collected.configuration}),o=r.idGenerator;let s=createAgentOtelInstrumentation({environment:resolveInstrumentationEnvironment(),frameworkVersion:e.frameworkVersion,telemetry:otelTelemetry({provider:r.provider,tracerName:`eve.agent`,tracerVersion:e.frameworkVersion,idGenerator:o,samplesTrace:r.samplesTrace,mapping:eveOutputMapping()}),idGenerator:o,recordInputs:e.collected.settings.recordInputs,recordOutputs:e.collected.settings.recordOutputs,stateStore:new ContextAgentTraceStateStore,tracePolicy:e.collected.settings.tracePolicy});t.push({...s.hook,stateNamespace:`internal:otel`}),i=s.prepareSessionTrace,a=s.prepareTurnTrace,runInContext=s.runInContext;let c=e.collected.configuration.spanProcessors.filter(isSpanProcessor).filter(hasConversationRelease);c.length>0&&n.push(sessionReleaseProvider(c))}else o=new AgentSpanIdGenerator;let s=[...t,...e.providers,...n],c;return registerInstrumentationRuntime({forceFlush:async()=>{await settleAll(s.map(e=>()=>e.flush?.())),await settleAll(r===void 0?[]:[r.forceFlush])},hooks:createInstrumentationHooks({parallel:e.providers,serialAfter:n,serialBefore:t}),idGenerator:o,memoryOperations:r!==void 0||e.providers.some(hasMemoryOperationHandler),otelSettings:e.collected.declared?e.collected.settings:void 0,ownsAgentSpans:r!==void 0,prepareSessionTrace:i,prepareTurnTrace:a,runtimeContextResolvers:e.runtimeContextResolvers,runInContext,samplesTrace:r?.samplesTrace,shutdown:()=>(c??=(async()=>{await settleAll(s.map(e=>()=>e.shutdown?.())),await settleAll(r===void 0?[]:[r.shutdown])})(),c)})}function hasMemoryOperationHandler(e){return e.events?.[`memory.operation.started`]!==void 0||e.events?.[`memory.operation.completed`]!==void 0||e.events?.[`memory.operation.failed`]!==void 0}function isSpanProcessor(e){return e!==`auto`}function sessionReleaseProvider(e){let release=async t=>{let n=readConversationId(contextStorage.getStore()?.get(ConversationIdKey))??t.sessionId;n===t.sessionId&&await Promise.all(e.map(e=>e.releaseConversation(n)))};return{events:{"session.completed":release,"session.failed":release},name:`eve.conversation-release`,stateNamespace:`internal:conversation-release`}}async function settleAll(e){let t=await Promise.allSettled(e.map(async e=>e()));for(let e of t)e.status===`rejected`&&log.warn(`instrumentation drain failed`,{error:formatError(e.reason)})}export{installInstrumentationRuntime};
1
+ import{createLogger,formatError}from"#internal/logging.js";import{contextStorage}from"#context/container.js";import{ConversationIdKey}from"#context/keys.js";import{resolveInstrumentationEnvironment}from"#internal/application/dev-environment.js";import{readConversationId}from"#shared/conversation-identity.js";import{ContextAgentTraceStateStore}from"#tracing/eve/agent-trace-context-store.js";import{registerInstrumentationRuntime}from"#instrumentation/runtime.js";import{createInstrumentationHooks}from"#instrumentation/lifecycle.js";import{AgentSpanIdGenerator,otelTelemetry}from"#compiled/@eve/tracing/index.js";import{hasConversationRelease}from"#tracing/local/traces.js";import{eveOutputMapping}from"#tracing/eve/profile.js";import{createAgentOtelInstrumentation}from"#tracing/eve/agent-otel-provider.js";import{registerOtel}from"#tracing/eve/otel-registration.js";const log=createLogger(`tracing.install-instrumentation-runtime`);function installInstrumentationRuntime(e){let t=[],n=[],r,i,a,runInContext=(e,t)=>t(),o;if(e.collected.declared){r=registerOtel({serviceName:e.serviceName,otel:e.collected.configuration}),o=r.idGenerator;let s=createAgentOtelInstrumentation({environment:resolveInstrumentationEnvironment(),frameworkVersion:e.frameworkVersion,telemetry:otelTelemetry({provider:r.provider,tracerName:`eve.agent`,tracerVersion:e.frameworkVersion,idGenerator:o,samplesTrace:r.samplesTrace,mapping:eveOutputMapping()}),idGenerator:o,recordInputs:e.collected.settings.recordInputs,recordOutputs:e.collected.settings.recordOutputs,stateStore:new ContextAgentTraceStateStore,tracePolicy:e.collected.settings.tracePolicy});t.push({...s.hook,stateNamespace:`internal:otel`}),i=s.prepareSessionTrace,a=s.prepareTurnTrace,runInContext=s.runInContext;let c=e.collected.configuration.spanProcessors.filter(isSpanProcessor).filter(hasConversationRelease);c.length>0&&n.push(sessionReleaseProvider(c))}else o=new AgentSpanIdGenerator;let s=[...t,...e.providers,...n],c;return registerInstrumentationRuntime({forceFlush:async()=>{await settleAll(s.map(e=>()=>e.flush?.())),await settleAll(r===void 0?[]:[r.forceFlush])},hooks:createInstrumentationHooks({parallel:e.providers,serialAfter:n,serialBefore:t}),idGenerator:o,memoryOperations:r!==void 0||e.providers.some(hasMemoryOperationHandler),otelSettings:e.collected.declared?e.collected.settings:void 0,ownsAgentSpans:r!==void 0,prepareSessionTrace:i,prepareTurnTrace:a,runtimeContextResolvers:e.runtimeContextResolvers,runInContext,samplesTrace:r?.samplesTrace,shutdown:()=>(c??=(async()=>{await settleAll(s.map(e=>()=>e.shutdown?.())),await settleAll(r===void 0?[]:[r.shutdown])})(),c)})}function hasMemoryOperationHandler(e){return e.events?.[`memory.operation.started`]!==void 0||e.events?.[`memory.operation.completed`]!==void 0||e.events?.[`memory.operation.failed`]!==void 0}function isSpanProcessor(e){return e!==`auto`}function sessionReleaseProvider(e){let release=async t=>{let n=readConversationId(contextStorage.getStore()?.get(ConversationIdKey))??t.sessionId;n===t.sessionId&&await Promise.all(e.map(e=>e.releaseConversation(n)))};return{events:{"session.completed":release,"session.failed":release},name:`eve.conversation-release`,stateNamespace:`internal:conversation-release`}}async function settleAll(e){let t=await Promise.allSettled(e.map(async e=>e()));for(let e of t)e.status===`rejected`&&log.warn(`instrumentation drain failed`,{error:formatError(e.reason)})}export{installInstrumentationRuntime};
@@ -13,6 +13,7 @@ export declare function createMcpTraceFetch(input: {
13
13
  export declare const MCP_SERIALIZER: ContentSerializer;
14
14
  export declare function createMcpTracing(): {
15
15
  list<T>(input: {
16
+ readonly method?: "tools/list" | "events/list";
16
17
  readonly connectionName: string;
17
18
  readonly execute: () => Promise<T>;
18
19
  readonly protocolVersion?: string;
@@ -1 +1 @@
1
- import{eveOutputMapping}from"./profile.js";import{context,propagation,trace}from"#compiled/@opentelemetry/api/index.js";import{activeTraceOperation,contentAttribute,mcpLifecycle,truncateTelemetryText}from"#compiled/@eve/tracing/otel.js";import{otelTelemetry}from"#compiled/@eve/tracing/index.js";function isObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}const MAX_MCP_TRACE_REQUEST_BYTES=1048576,MAX_MCP_TRACE_CONTEXT_BYTES=8192,utf8Encoder=new TextEncoder,traceContextSetter={set(e,t,n){e[t]=n}};function createMcpTraceFetch(e){let t=e.fetcher??((e,t)=>globalThis.fetch(e,t)),n=e.getActiveContext??(()=>context.active()),r=e.injectContext??injectOpenTelemetryTraceContext;return async(i,a)=>{let o=readJsonRpcRequest(a?.body);if(o===void 0)return await t(i,a);let s=n(),c=injectMcpTraceContext(o,s,r,e.filterCarrier),l=o.method===`tools/call`||o.method===`tools/list`&&activeTraceOperation(s)?.type===`mcp`;l&&activeTraceOperation(s)?.mcp?.update(mcpRequestMetadata({connectionName:e.connectionName,message:o,params:isObject(o.params)?o.params:{},protocolVersion:headerValue(a?.headers,`mcp-protocol-version`)??e.getProtocolVersion()}));let u=await t(i,c===void 0?a:{...a,body:JSON.stringify(c)});if(l){let e=u.headers.get(`mcp-session-id`);e!==null&&activeTraceOperation(s)?.mcp?.update({sessionId:truncateTelemetryText(e,512)})}return u}}function injectMcpTraceContext(e,t,n=injectOpenTelemetryTraceContext,r){if(!withinTraceRequestLimit(JSON.stringify(e))||e.params!==void 0&&!isObject(e.params))return;let i={};if(n(t,i),!withinTraceContextLimit(i))return;r?.(i);let a=isObject(e.params)?e.params:{},o=isObject(a._meta)?a._meta:{};return{...e,params:{...a,_meta:{...o,...i}}}}const noContent=()=>void 0,MCP_SERIALIZER={json:contentAttribute,text:noContent,inputMessages:noContent,instructions:noContent,toolDefinitions:noContent,outputMessages:noContent,toolResults:noContent};function createMcpTracing(){let e=otelTelemetry({tracerName:`eve.mcp`,mapping:eveOutputMapping()});function start(t){let n=e.startSpan({type:`mcp`,operationId:`${t.connectionName}:${t.method}`,name:t.method===`tools/call`?`tools/call ${t.toolName??`unknown`}`:t.method,kind:`CLIENT`,parent:trace.getSpan(t.executionContext)?.spanContext(),attributes:{"agent.connection.name":t.connectionName,"mcp.method.name":t.method,"network.protocol.name":`http`,"network.transport":`tcp`,"mcp.protocol.version":t.protocolVersion,"gen_ai.operation.name":t.method===`tools/call`?`execute_tool`:void 0,"gen_ai.tool.name":t.toolName}},{context:t.executionContext}),r=mcpLifecycle({serializer:MCP_SERIALIZER,...t.capture,write(e){for(let[t,r]of Object.entries(e))r!==void 0&&n.setAttribute(t,r)},error:n.fail});return{...r,end:n.end,run(i){return e.run({type:`mcp`,reference:n.reference,capture:t.capture,mcp:r},i,t.executionContext)}}}return{async list(e){let t=context.active(),n=start({method:`tools/list`,connectionName:e.connectionName,protocolVersion:e.protocolVersion,executionContext:t,capture:activeTraceOperation()?.capture??{emit:!0,recordInputs:!1,recordOutputs:!1}});try{return await n.run(async()=>{try{return await e.execute()}catch(e){let t=jsonRpcErrorCode(e);throw t!==void 0&&n.update({statusCode:t}),n.error(e,errorType(e,t)),e}})}finally{n.end()}},async call(e){let t=context.active(),n=activeTraceOperation();if(n?.type===`tool`&&n.mcp!==void 0)return n.mcp.update({...e,method:`tools/call`}),await runMcpToolCall(e,void 0);let r=start({method:`tools/call`,connectionName:e.connectionName,toolName:truncateTelemetryText(e.toolName,128),protocolVersion:e.protocolVersion,executionContext:t,capture:n?.capture??{emit:!0,recordInputs:!1,recordOutputs:!1}});try{return await r.run(()=>runMcpToolCall(e,r))}finally{r.end()}}}}function runMcpToolCall(e,t){return t!==void 0&&t.arguments(e.arguments),e.execute().then(e=>(isToolErrorResult(e)&&activeTraceOperation()?.mcp?.error(void 0,`tool_error`),t!==void 0&&t.result(e),e)).catch(e=>{let n=jsonRpcErrorCode(e);throw n!==void 0&&activeTraceOperation()?.mcp?.update({statusCode:n}),t!==void 0&&activeTraceOperation()?.mcp?.error(e,errorType(e,n)),e})}function mcpRequestMetadata(e){let t=e.method??e.message?.method,n=e.params??(isObject(e.message?.params)?e.message.params:{}),r=e.toolName??(typeof n.name==`string`?n.name:void 0),i=e.message?.id,a=n._meta,o=e.protocolVersion??(isObject(a)&&typeof a[`io.modelcontextprotocol/protocolVersion`]==`string`?a[`io.modelcontextprotocol/protocolVersion`]:void 0);return{connectionName:e.connectionName,method:t??`unknown`,toolName:r,protocolVersion:o,requestId:typeof i==`number`||typeof i==`string`?String(i):void 0}}function injectOpenTelemetryTraceContext(e,t){propagation.inject(e,t,traceContextSetter)}function readJsonRpcRequest(e){if(typeof e==`string`&&withinTraceRequestLimit(e))try{let t=JSON.parse(e);return isObject(t)&&typeof t.method==`string`?t:void 0}catch{return}}function withinTraceRequestLimit(e){return e.length<=MAX_MCP_TRACE_REQUEST_BYTES&&utf8Encoder.encode(e).byteLength<=MAX_MCP_TRACE_REQUEST_BYTES}function withinTraceContextLimit(e){let t=0;for(let[n,r]of Object.entries(e))if(n.length+r.length>MAX_MCP_TRACE_CONTEXT_BYTES||(t+=utf8Encoder.encode(n).byteLength+utf8Encoder.encode(r).byteLength+2,t>MAX_MCP_TRACE_CONTEXT_BYTES))return!1;return t>0}function headerValue(e,t){if(e!==void 0)try{return new Headers(e).get(t)??void 0}catch{return}}function isToolErrorResult(e){return isObject(e)&&e.isError===!0}function jsonRpcErrorCode(e){if(!isObject(e))return;let t=e.code;return typeof t==`number`||typeof t==`string`?String(t):void 0}function errorType(e,t){return t===void 0?e instanceof Error?e.name||`Error`:void 0:t}export{MCP_SERIALIZER,createMcpTraceFetch,createMcpTracing};
1
+ import{eveOutputMapping}from"./profile.js";import{context,propagation,trace}from"#compiled/@opentelemetry/api/index.js";import{activeTraceOperation,contentAttribute,mcpLifecycle,truncateTelemetryText}from"#compiled/@eve/tracing/otel.js";import{otelTelemetry}from"#compiled/@eve/tracing/index.js";function isObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}const MAX_MCP_TRACE_REQUEST_BYTES=1048576,MAX_MCP_TRACE_CONTEXT_BYTES=8192,utf8Encoder=new TextEncoder,traceContextSetter={set(e,t,n){e[t]=n}};function createMcpTraceFetch(e){let t=e.fetcher??((e,t)=>globalThis.fetch(e,t)),n=e.getActiveContext??(()=>context.active()),r=e.injectContext??injectOpenTelemetryTraceContext;return async(i,a)=>{let o=readJsonRpcRequest(a?.body);if(o===void 0)return await t(i,a);let s=n(),c=injectMcpTraceContext(o,s,r,e.filterCarrier),l=o.method===`tools/call`||(o.method===`tools/list`||o.method===`events/list`)&&activeTraceOperation(s)?.type===`mcp`;l&&activeTraceOperation(s)?.mcp?.update(mcpRequestMetadata({connectionName:e.connectionName,message:o,params:isObject(o.params)?o.params:{},protocolVersion:headerValue(a?.headers,`mcp-protocol-version`)??e.getProtocolVersion()}));let u=await t(i,c===void 0?a:{...a,body:JSON.stringify(c)});if(l){let e=u.headers.get(`mcp-session-id`);e!==null&&activeTraceOperation(s)?.mcp?.update({sessionId:truncateTelemetryText(e,512)})}return u}}function injectMcpTraceContext(e,t,n=injectOpenTelemetryTraceContext,r){if(!withinTraceRequestLimit(JSON.stringify(e))||e.params!==void 0&&!isObject(e.params))return;let i={};if(n(t,i),!withinTraceContextLimit(i))return;r?.(i);let a=isObject(e.params)?e.params:{},o=isObject(a._meta)?a._meta:{};return{...e,params:{...a,_meta:{...o,...i}}}}const noContent=()=>void 0,MCP_SERIALIZER={json:contentAttribute,text:noContent,inputMessages:noContent,instructions:noContent,toolDefinitions:noContent,outputMessages:noContent,toolResults:noContent};function createMcpTracing(){let e=otelTelemetry({tracerName:`eve.mcp`,mapping:eveOutputMapping()});function start(t){let n=e.startSpan({type:`mcp`,operationId:`${t.connectionName}:${t.method}`,name:t.method===`tools/call`?`tools/call ${t.toolName??`unknown`}`:t.method,kind:`CLIENT`,parent:trace.getSpan(t.executionContext)?.spanContext(),attributes:{"agent.connection.name":t.connectionName,"mcp.method.name":t.method,"network.protocol.name":`http`,"network.transport":`tcp`,"mcp.protocol.version":t.protocolVersion,"gen_ai.operation.name":t.method===`tools/call`?`execute_tool`:void 0,"gen_ai.tool.name":t.toolName}},{context:t.executionContext}),r=mcpLifecycle({serializer:MCP_SERIALIZER,...t.capture,write(e){for(let[t,r]of Object.entries(e))r!==void 0&&n.setAttribute(t,r)},error:n.fail});return{...r,end:n.end,run(i){return e.run({type:`mcp`,reference:n.reference,capture:t.capture,mcp:r},i,t.executionContext)}}}return{async list(e){let t=context.active(),n=start({method:e.method??`tools/list`,connectionName:e.connectionName,protocolVersion:e.protocolVersion,executionContext:t,capture:activeTraceOperation()?.capture??{emit:!0,recordInputs:!1,recordOutputs:!1}});try{return await n.run(async()=>{try{return await e.execute()}catch(e){let t=jsonRpcErrorCode(e);throw t!==void 0&&n.update({statusCode:t}),n.error(e,errorType(e,t)),e}})}finally{n.end()}},async call(e){let t=context.active(),n=activeTraceOperation();if(n?.type===`tool`&&n.mcp!==void 0)return n.mcp.update({...e,method:`tools/call`}),await runMcpToolCall(e,void 0);let r=start({method:`tools/call`,connectionName:e.connectionName,toolName:truncateTelemetryText(e.toolName,128),protocolVersion:e.protocolVersion,executionContext:t,capture:n?.capture??{emit:!0,recordInputs:!1,recordOutputs:!1}});try{return await r.run(()=>runMcpToolCall(e,r))}finally{r.end()}}}}function runMcpToolCall(e,t){return t!==void 0&&t.arguments(e.arguments),e.execute().then(e=>(isToolErrorResult(e)&&activeTraceOperation()?.mcp?.error(void 0,`tool_error`),t!==void 0&&t.result(e),e)).catch(e=>{let n=jsonRpcErrorCode(e);throw n!==void 0&&activeTraceOperation()?.mcp?.update({statusCode:n}),t!==void 0&&activeTraceOperation()?.mcp?.error(e,errorType(e,n)),e})}function mcpRequestMetadata(e){let t=e.method??e.message?.method,n=e.params??(isObject(e.message?.params)?e.message.params:{}),r=e.toolName??(typeof n.name==`string`?n.name:void 0),i=e.message?.id,a=n._meta,o=e.protocolVersion??(isObject(a)&&typeof a[`io.modelcontextprotocol/protocolVersion`]==`string`?a[`io.modelcontextprotocol/protocolVersion`]:void 0);return{connectionName:e.connectionName,method:t??`unknown`,toolName:r,protocolVersion:o,requestId:typeof i==`number`||typeof i==`string`?String(i):void 0}}function injectOpenTelemetryTraceContext(e,t){propagation.inject(e,t,traceContextSetter)}function readJsonRpcRequest(e){if(typeof e==`string`&&withinTraceRequestLimit(e))try{let t=JSON.parse(e);return isObject(t)&&typeof t.method==`string`?t:void 0}catch{return}}function withinTraceRequestLimit(e){return e.length<=MAX_MCP_TRACE_REQUEST_BYTES&&utf8Encoder.encode(e).byteLength<=MAX_MCP_TRACE_REQUEST_BYTES}function withinTraceContextLimit(e){let t=0;for(let[n,r]of Object.entries(e))if(n.length+r.length>MAX_MCP_TRACE_CONTEXT_BYTES||(t+=utf8Encoder.encode(n).byteLength+utf8Encoder.encode(r).byteLength+2,t>MAX_MCP_TRACE_CONTEXT_BYTES))return!1;return t>0}function headerValue(e,t){if(e!==void 0)try{return new Headers(e).get(t)??void 0}catch{return}}function isToolErrorResult(e){return isObject(e)&&e.isError===!0}function jsonRpcErrorCode(e){if(!isObject(e))return;let t=e.code;return typeof t==`number`||typeof t==`string`?String(t):void 0}function errorType(e,t){return t===void 0?e instanceof Error?e.name||`Error`:void 0:t}export{MCP_SERIALIZER,createMcpTraceFetch,createMcpTracing};
@@ -44,6 +44,11 @@ export interface OtelOptions {
44
44
  * lifecycle providers. The policy may be invoked more than once
45
45
  * while a new session is being prepared, so it must be deterministic for
46
46
  * consistent results.
47
+ * Unless `experimental.workflow.retention` is explicit, the session's
48
+ * resolved decision also selects Workflow retention: both content directions
49
+ * use the World's default; dropped traces or either excluded direction
50
+ * request deletion on completion. This also removes completed transcripts,
51
+ * but does not delete exported traces.
47
52
  */
48
53
  readonly tracePolicy?: TraceCapturePolicy;
49
54
  /**
@@ -263,7 +263,8 @@ duration. Process restarts, ordinary messages, compaction handoffs, and failed
263
263
  or skipped handoffs keep the existing deadline. At the deadline, eve lets an active turn settle,
264
264
  then emits `session.completed` and releases every continuation address; the next
265
265
  qualifying channel message starts fresh. Set it to `false` to disable the
266
- timeout. Expiration does not delete stored session data.
266
+ timeout. Session expiration follows [run-data retention](#run-data-retention);
267
+ a zero-retention session loses its stored payloads when it ends.
267
268
 
268
269
  Input tokens, output tokens, and model token cost are checked independently.
269
270
  The model call that crosses a limit is allowed to finish because exact usage
@@ -409,11 +410,24 @@ for the retry behavior.
409
410
 
410
411
  ## Run data retention
411
412
 
412
- The runtime keeps each run's data after the run finishes: model and tool
413
- payloads, streamed output, and the event log eve replays from. How long it keeps
414
- them is the World's decision, and on Vercel that follows your team's plan. Set
415
- `experimental.workflow.retention` to `0` to have it deleted as soon as the run
416
- finishes instead:
413
+ By default, eve derives run-data retention from the session's resolved
414
+ [OpenTelemetry trace policy](./observability/otel#configure-opentelemetry):
415
+
416
+ - When the policy allows both inputs and outputs, eve uses the World's default
417
+ retention period.
418
+ - When the policy drops the trace or excludes either content direction, eve
419
+ requests deletion as soon as the run ends with Workflow's
420
+ `experimental_retention: 0`. Workflow cannot delete inputs and outputs separately.
421
+
422
+ Without a custom policy, public conversations and development sessions use the
423
+ World's default. Private and unknown-audience sessions outside development request
424
+ `0`, even when no OpenTelemetry destination is installed. This decision is made
425
+ when the session starts; destination export filters and lifecycle-provider
426
+ policies do not change it.
427
+
428
+ Set `experimental.workflow.retention` explicitly to override this behavior.
429
+ Use `0` to request deletion regardless of the tracing policy, or `"default"` to
430
+ keep run data for the World's default period regardless of the tracing policy:
417
431
 
418
432
  ```ts title="agent/agent.ts"
419
433
  import { defineAgent } from "eve";
@@ -428,18 +442,29 @@ export default defineAgent({
428
442
  });
429
443
  ```
430
444
 
431
- This applies to every run that owns the session, including successor owners
445
+ The resolved value applies to every run that owns the session, including successor owners
432
446
  started after a deployment handoff, and to the run that collects session
433
447
  activity. Runs eve starts for other purposes keep the world's default: session
434
448
  timeouts and [workflow tools](./tools/workflows).
435
449
 
436
450
  The value applies per agent. A [subagent](./subagents) that runs its own session
437
- uses its own value, unlike `experimental.workflow.world`, which is root-only.
438
-
439
- Custom Worlds used with eve might not support this feature, in which case
440
- it falls back to the World's default retention period.
441
-
442
- > ⚠️ **At `0`, a finished session's output is usually gone before you can read it.** Since data is deleted immediately before it can be read back, results and transcripts become unreadable and a client polling for a finished session's output can see it disappear.
451
+ uses its own explicit value or derives retention from its resolved trace decision,
452
+ including any inherited content ceiling. `experimental.workflow.world` is root-only.
453
+
454
+ At `0`, supporting Worlds delete run and step inputs, outputs, and errors;
455
+ event payloads; and stream contents when the run reaches a terminal state. The
456
+ data remains available while the session is active. Run, step, and event records
457
+ remain for the World's default period, including IDs, timestamps, statuses,
458
+ step names, and attributes. Attributes can contain content, including eve's
459
+ session title. This option does not delete local traces, exported telemetry, or
460
+ data stored by tools, channels, or model providers.
461
+
462
+ The Workflow World enforces deletion, not eve or the SDK. A World older than
463
+ spec version 4 rejects `0`; a spec version 4 World without retention support
464
+ keeps the data. Confirm support with your World provider. See
465
+ [Workflow data retention](https://workflow-sdk.dev/docs/observability/retention).
466
+
467
+ > ⚠️ **At `0`, completed session transcripts become unreadable.** Deletion races reads of finished runs. Depending on the World, reading deleted session history returns either an empty stream or `410` with `code: "session_expired"`. Persist anything you need to keep from inside a tool, or set `retention: "default"` to preserve completed-session reads independently of tracing.
443
468
 
444
469
  ## Other defineAgent fields
445
470
 
@@ -87,6 +87,12 @@ By default, submissions use the Teams identity of the user who clicked the card.
87
87
 
88
88
  When a tool approval settles, eve replaces its Adaptive Card with the outcome and responder name. A response rejected by an approval policy leaves the shared card unchanged.
89
89
 
90
+ ### Connection sign-ins
91
+
92
+ When a connection needs the person to sign in, eve's `authorization.required` handler posts an Adaptive Card. In a personal chat, the card includes the sign-in link and any confirmation code. In group chats and channels, the shared card only says who has to sign in and has a **Sign in** button (`Action.Execute`). When someone presses it, eve resolves who they are the same way it does for HITL submissions (`onInputResponse`). If that's the person who needs to sign in, eve sends back a card with the link and code, and Teams shows that card only to them. Anyone else is told they can't open it. That way another member can't finish the sign-in with their own account and have it saved under the requester's identity.
93
+
94
+ If you customize `onMessage` without also setting `onInputResponse`, nobody can open a sign-in from a group chat or channel, because eve rejects every press.
95
+
90
96
  For invokes that aren't HITL, handle them in `onInvoke(ctx, activity)`.
91
97
 
92
98
  ### Proactive sessions
@@ -103,9 +103,37 @@ Configure `github` to broker GitHub access through Connect instead. The extensio
103
103
  | `github.connector` | With `github` | Connector that mints a GitHub token |
104
104
  | `github.org` | With `github` | Organization whose repositories the `gh` tool can access |
105
105
  | `github.broker` | With `github` | `broker(sandbox, rules)` callback that installs, then removes, the GitHub credential rules |
106
+ | `github.authorize` | No | `authorize({ auth, repository, token })` callback that allows or denies each `gh` tool invocation |
106
107
 
107
108
  The `gh` tool requests a token scoped to exactly one repository in `github.org` for each invocation. The sandbox process receives a placeholder `GH_TOKEN`, and the firewall exchanges it on matching GitHub requests. Your `github.broker` callback receives the header-transform rules for the command, then `null` to remove the lease. The callback must preserve your other network rules. GitHub authentication has no command-delivery option.
108
109
 
110
+ `github.authorize` runs during each `gh` tool execution, not when the session starts. The order is: validate the command and repository, mint the repository-scoped app token, await `authorize({ auth, repository, token })`, install the credential lease, then run the command. `auth` is the session's current caller at tool invocation, or `null`. Returning `false` stops execution before the lease is installed or the command runs. The decision applies to that invocation only; it is neither cached for the session nor repeated for each GitHub API request.
111
+
112
+ For example, use the token to check collaborator status (allowed commands still use the app token's permissions, not the collaborator's):
113
+
114
+ ```ts title="agent/extensions/git.ts"
115
+ import git from "eve/extensions/git";
116
+
117
+ import { brokerGitHubLease } from "../lib/github";
118
+
119
+ export default git({
120
+ github: {
121
+ connector: "github/my-agent",
122
+ org: "acme",
123
+ broker: brokerGitHubLease,
124
+ // GitHub channel sessions carry the commenter's login as `auth.subject`.
125
+ async authorize({ auth, repository, token }) {
126
+ if (auth?.authenticator !== "github-webhook" || !auth.subject) return false;
127
+ const response = await fetch(
128
+ `https://api.github.com/repos/${repository}/collaborators/${auth.subject}`,
129
+ { headers: { authorization: `Bearer ${token}` } },
130
+ );
131
+ return response.status === 204;
132
+ },
133
+ },
134
+ });
135
+ ```
136
+
109
137
  ### Migrate from `code({ github })`
110
138
 
111
139
  `code({ github })` is deprecated. While it is set, the code extension still contributes `code__gh`, its GitHub instructions, and the signed-commit `code__pr` skill. Without it, the code extension contributes no GitHub tool, instructions, or pull request skill. Move the `github` option to `git({ github })`, and import `gh` and the GitHub sandbox helpers from `eve/extensions/git/tools` and `eve/extensions/git/sandbox`.
@@ -310,7 +310,7 @@ These tools let the model reach what isn't in its tool list. `eve__search` finds
310
310
 
311
311
  Entries the agent declares count from the session's first step. An entry a dynamic resolver returns counts from the step where it first appears, so a resolver in `agent/tools/` that returns only tools without `deferred: true` adds no catalog tools. Once a session's tool list includes a catalog tool, the session keeps it, even after a resolver stops returning the entry that brought it. The catalog tools change only when the session first reaches a new kind of entry, such as its first deferred tool or its first connection.
312
312
 
313
- - `eve__search({ query, limit? })` returns the best matches, up to `limit` (default 10, at most 50). `query` is required and must contain a word. A tool match has its exact `tool` name, its `description`, and a TypeScript `signature` rendered from its schemas. A deferred skill's match has its `skill` name, its `description`, and the `path` of its `SKILL.md` when it has supporting files. A result with `tool` is called with `eve__tool`, and one with `skill` is loaded with `eve__skill`. Next to `results`, `searched` counts the entries the query covered, the whole catalog or one namespace, and `remaining` counts the matches past `limit`. A connection behind sign-in counts as its one sign-in result until the user signs in, and an unavailable connection counts as none. Tools and skills rank together, in tiers: an entry whose name is exactly the query, then one whose name after its last `__` is the query, such as `linear__create_issue` or `sre__create_issue` for `create_issue`, then the tools of a connection named by the query, then entries whose names, or names after their last `__`, start with the query, then keyword matches in names, parameters, and descriptions. So `eve__search({ query: "linear" })` lists the `linear` connection's tools first. One-letter and filler words such as `a` and `the` don't count toward keyword matches unless the query has nothing else.
313
+ - `eve__search({ query, limit? })` returns the best matches, up to `limit` (default 10, at most 50). `query` is required and must contain a word. A tool match has its exact `tool` name, its `description`, and a TypeScript `signature` rendered from its schemas. A very long signature drops its property descriptions before any property name. A deferred skill's match has its `skill` name, its `description`, and the `path` of its `SKILL.md` when it has supporting files. A result with `tool` is called with `eve__tool`, and one with `skill` is loaded with `eve__skill`. Next to `results`, `searched` counts the entries the query covered, the whole catalog or one namespace, and `remaining` counts the matches past `limit`. A connection behind sign-in counts as its one sign-in result until the user signs in, and an unavailable connection counts as none. Tools and skills rank together, in tiers: an entry whose name is exactly the query, then one whose name after its last `__` is the query, such as `linear__create_issue` or `sre__create_issue` for `create_issue`, then the tools of a connection named by the query, then entries whose names, or names after their last `__`, start with the query, then keyword matches in names, parameters, and descriptions. So `eve__search({ query: "linear" })` lists the `linear` connection's tools first. One-letter and filler words such as `a` and `the` don't count toward keyword matches unless the query has nothing else.
314
314
  - A query whose first word contains `__` searches one namespace: everything before its last `__`, without trailing underscores. The namespace only filters. `eve__search` keeps names under `<namespace>__` and anything named exactly the namespace, such as a connection's sign-in entry, then ranks them by the whole query, so `eve__search({ query: "linear__" })` returns only the `linear` connection's tools, or its sign-in result, and an exact name that contains `__` still ranks first. eve lists only the connections that can own names in that namespace, so other connections make no network call and don't appear in `unavailable`. A namespace that matches nothing fails with the closest connection names. Characters that can't appear in a name, such as a leading `^`, are ignored; `query` isn't a regular expression.
315
315
  - `eve__search` never asks the user to sign in. A connection whose server won't list its tools until the user signs in appears as one result named after the connection, such as `linear`; a connection whose server lists its tools without a token is searched like any other. A connection whose tools fail to load, or don't load within 10 seconds, appears under `unavailable` with its `error`, such as `"crm" did not list its tools within 10s. Try again later.` Matches from everything else are still returned, so a slow server doesn't hold up the search. An `eve__tool` call on that connection's tools fails with the same error.
316
316
  - `eve__tool({ name: "linear" })`, with a connection's own name, asks the user to sign in to that connection when its tools need it, and returns once they can be listed, so the next `eve__search` finds them. On a server that lists its tools without sign-in, it confirms the tools are available without asking.
@@ -422,6 +422,8 @@ export default noReply();
422
422
 
423
423
  `no_reply` is a `defineTool` tool with [`endsTurn: true`](/docs/tools#end-the-turn-after-a-tool-call), so the turn ends only when no other tool runs in the same step. Only root sessions receive it. Slack clears the thread status when the turn completes. Remove the file to remove the tool.
424
424
 
425
+ A root session also accepts a successful model call that returns no text with `finishReason: "stop"`, without retrying or reporting a model-call failure. A turn that settles with no text completes without a final message. Use `no_reply` when the agent should record an explicit choice to stay quiet, including an optional reason. Delegated sessions still retry an empty reply once and report a failure if it remains empty, so the caller can act on the failure.
426
+
425
427
  ### `sleep`
426
428
 
427
429
  `sleep` pauses and durably resumes the current turn. The model calls it with `{ seconds }`; the wait does not hold an application runtime open. Concurrent calls run in parallel, and the turn resumes after the longest wait. A steering message, the default for a new message, ends the wait early: `sleep` returns `{ interrupted: true }`, which the model reads as `Stopped early because a new message arrived.`, followed by the message. Add it:
@@ -205,6 +205,111 @@ An MCP tool call returns the tool's `structuredContent` when the server sends it
205
205
 
206
206
  When a remote tool returns more data than the model needs, narrow the result at the MCP server. Prefer a purpose-built search or summary tool, or return a stable handle that another call can use to fetch a smaller slice. If you do not control the server and an equivalent upstream API is available, replace only that operation with an authored tool that stores the full payload outside model context and projects the needed fields through `toModelOutput`. Block the original MCP operation through [`tools.block`](#tool-filters) so the model does not see duplicate tools.
207
207
 
208
+ ## Events (experimental)
209
+
210
+ An MCP connection can watch the server's events and run your code when one arrives. Vercel Connect holds the upstream subscription and forwards each event to your agent; eve runs your callback.
211
+
212
+ Requirements:
213
+
214
+ - A statically authored MCP connection with a static `connect()` auth provider. Dynamic connections and auth resolvers are not supported.
215
+ - `@vercel/connect@2.5.2` or later.
216
+ - An MCP server that supports events over Streamable HTTP.
217
+
218
+ ### Handle events
219
+
220
+ Add `experimental_events` to the connection. In this example, a user asks the agent to "watch the support inbox and draft replies to new bug reports". The model subscribes to Bird's `email_mailbox.message_received` event for that mailbox. Each new email then starts a turn in the same conversation, with instructions to triage it:
221
+
222
+ ```ts title="agent/connections/bird.ts"
223
+ import { connect } from "@vercel/connect/eve";
224
+ import { defineMcpClientConnection } from "eve/connections";
225
+
226
+ export default defineMcpClientConnection({
227
+ url: "https://mcp.bird.com/",
228
+ description: "Bird: email, SMS, and contacts. Can watch a mailbox for new mail.",
229
+ auth: connect("bird/bird"),
230
+ experimental_events: {
231
+ async onEvent({ event, origin, auth, attachSession }) {
232
+ if (event.name !== "email_mailbox.message_received") return;
233
+ await attachSession(origin.sessionId).send(
234
+ [
235
+ "A new email arrived in the mailbox you are watching:",
236
+ JSON.stringify(event.data),
237
+ "If it is a bug report, look up the sender's earlier threads, draft a reply " +
238
+ "that says what we know and what happens next, and ask me before sending it. " +
239
+ "Otherwise, summarize it in one line.",
240
+ ].join("\n\n"),
241
+ { auth, turnPolicy: "queue" },
242
+ );
243
+ },
244
+ },
245
+ });
246
+ ```
247
+
248
+ The model does the work with the connection's own tools: it reads the thread, drafts the reply, and waits for the user's approval in the conversation. `turnPolicy: "queue"` lets a burst of emails run one turn after another instead of interrupting the user.
249
+
250
+ `onEvent` is required. Defining it does not subscribe to anything: the model subscribes at runtime.
251
+
252
+ Callbacks run outside any conversation, as the user who subscribed (`auth`). Nothing enters a session unless you call `attachSession(sessionId).send(...)`, and `attachSession` never creates a session.
253
+
254
+ Each delivery runs in its own durable workflow, so callbacks can run concurrently, out of order, and more than once. Use `deliveryId` to deduplicate side effects:
255
+
256
+ ```ts
257
+ async onEvent({ event, deliveryId }) {
258
+ await fetch("https://crm.example/notes", {
259
+ method: "POST",
260
+ headers: { "Idempotency-Key": deliveryId },
261
+ body: JSON.stringify(event.data),
262
+ });
263
+ },
264
+ ```
265
+
266
+ ### Handle missed events and ended subscriptions
267
+
268
+ ```ts
269
+ experimental_events: {
270
+ async onEvent({ event }) {
271
+ // ...
272
+ },
273
+ async onGap({ origin, auth, attachSession }) {
274
+ // The server could not deliver every event. Re-read the source.
275
+ await attachSession(origin.sessionId).send(
276
+ "Some Bird events were missed. Re-check the mailbox.",
277
+ { auth },
278
+ );
279
+ },
280
+ async onTerminated({ origin, auth, error, attachSession }) {
281
+ // The upstream subscription ended. The watch ends when this returns.
282
+ await attachSession(origin.sessionId).send(
283
+ `The Bird watch ended: ${error.message}`,
284
+ { auth },
285
+ );
286
+ },
287
+ },
288
+ ```
289
+
290
+ ### What the model can call
291
+
292
+ `eve__search` lists three kinds of actions for the connection:
293
+
294
+ | Action | Input | Result |
295
+ | ------------------------- | ---------------------- | --------------------------------------- |
296
+ | `events_subscribe_<hash>` | `{ arguments, ttlMs }` | `{ id, status, expiresAt, ... }` |
297
+ | `events_get_subscription` | `{ id }` | The subscription, or unknown once ended |
298
+ | `events_unsubscribe` | `{ id }` | `{ id, status: "stopping" }` |
299
+
300
+ There is one subscribe action per event the server offers; its description names the event. `ttlMs` is required: an integer from `120000` (2 minutes) to `31536000000` (365 days), or `null` to watch until stopped. `expiresAt` is the resulting deadline, or `null`.
301
+
302
+ - **Pending.** If Connect has not confirmed in time, subscribe returns the `id` with `status: "pending"`. Call get with that `id` later.
303
+ - **Repeats.** Subscribing again to the same event with the same arguments from the same session returns the existing watch. Another session gets its own watch.
304
+ - **Stopping.** Get and stop accept the `id` from any of the creator's sessions. After stop, eve keeps retrying Connect's unsubscribe until cleanup completes; until then get still reports the watch and stop can be called again.
305
+ - **Lifetime.** A watch outlives the session that created it. It ends on stop, at `expiresAt`, or when the upstream subscription terminates.
306
+
307
+ `tools.allow` and `tools.block` apply to these actions. An allow list hides any event action it does not name, so copy the exact `events_subscribe_<hash>` names from `eve__search`. `toolCall.providedArguments` does not apply to event actions.
308
+
309
+ ### Receiver
310
+
311
+ eve mounts `POST /eve/v1/hooks/:connectionName`, under any configured agent or public prefix. It accepts only Connect's delivery token and only deliveries for a live watch's subscription; it answers `410` once the watch has ended. The callback's identity always comes from the saved watch, never from the event payload.
312
+
208
313
  ## Troubleshooting
209
314
 
210
315
  | Symptom | Check |
@@ -219,6 +219,10 @@ export default defineHook({
219
219
 
220
220
  The remaining subscribers for the event still run. Then eve cancels the turn the same way [`session.cancel()`](./client/streaming) does: in-flight model and tool work is aborted, delegated child turns are cancelled, and the turn ends with `turn.cancelled` followed by `session.waiting`. No failure event is emitted. A cancel from `turn.started` or `step.started` takes effect before that model call. In a conversation, the next message starts a new turn. A delegated task reports the cancellation to its caller.
221
221
 
222
+ A plain-text message that answers a pending question, such as one from `ask_question`, is recorded as `message.received` before eve delivers it. Calling `ctx.cancel()` from that event's hook cancels the turn instead: the question resolves as `cancelled`, and the waiting tool call never receives the text.
223
+
224
+ The same goes for the `action.partial` a workflow tool reports while the turn waits on it: `ctx.cancel()` from its hook cancels the turn, and the run's result never reaches the model.
225
+
222
226
  `ctx.cancel()` returns `void` rather than a promise. The turn stops after the hook returns, so there is nothing to await. Call it before the handler's promise settles: eve ignores a call from work the handler does not await and logs a warning.
223
227
 
224
228
  `ctx.cancel()` only stops a running turn. eve logs a warning and ignores the call on `step.failed`, `turn.completed`, `turn.failed`, `turn.cancelled`, `turn.waiting`, `session.waiting`, `session.completed`, `session.failed`, `context.cleared`, `task.started`, `task.settled`, and `agent.started`, and during clear or compact requests.
@@ -170,6 +170,8 @@ Each call emits one `tool.call.*` lifecycle to authored providers. SDK execution
170
170
 
171
171
  For local and remote subagents, `tool.call.completed` and `tool.call.failed` include the child turn's `usage` directly. Token counts are always present when the child reports usage; `usage.costUsd` is present only when the model provider reports a price.
172
172
 
173
+ When a tool throws and outputs are recorded, `tool.call.failed` carries an `Error` with the thrown error's `name` and `message`, so a provider can classify the failure by `error.name`. It is rebuilt from the durable session record rather than being the object the tool threw, so `instanceof` checks against your own error classes do not match.
174
+
173
175
  When upgrading, replace `action.started`, `action.completed`, and `action.failed` handlers with their `tool.call.*` equivalents. Read `event.toolName` instead of `event.name` on start events. The OpenTelemetry trace schema remains version 4.
174
176
 
175
177
  Handlers from different files run concurrently and are failure-isolated. Do
@@ -38,6 +38,16 @@ does not affect lifecycle instrumentation created with `defineInstrumentation()`
38
38
  See [Audience](../channels/overview#audience) for how channels classify the
39
39
  conversation passed to this policy.
40
40
 
41
+ The session's resolved capture decision also selects its default
42
+ [Workflow run-data retention](/docs/agent-config#run-data-retention). Allowing
43
+ both inputs and outputs uses the World's default; dropping the trace or excluding
44
+ either direction requests deletion of durable payloads and streams when the run
45
+ ends. This makes completed private and unknown-audience transcripts unreadable
46
+ under the default policy outside development. Set
47
+ `experimental.workflow.retention: "default"` on the agent to retain completed
48
+ sessions independently of tracing. Destination `exportPolicy` filters do not
49
+ change Workflow retention, and Workflow deletion does not delete exported traces.
50
+
41
51
  ## Instrument Node.js dependencies
42
52
 
43
53
  Pass OpenTelemetry instrumentation instances to `otel({ instrumentations })`.
@@ -104,6 +104,52 @@ const sandbox = await ctx.getSandbox(environment);
104
104
  const sandboxName = sandbox.name;
105
105
  ```
106
106
 
107
+ ## Mount a Drive
108
+
109
+ A [Vercel Sandbox Drive](https://vercel.com/docs/sandbox/concepts/drives) stores files independently of a sandbox. Create or retrieve the Drive before calling `open()`, then pass it in `mounts` under an absolute path:
110
+
111
+ ```ts
112
+ import { defineSandbox } from "eve/sandbox";
113
+ import { Drive, VercelSandbox } from "eve/sandbox/vercel";
114
+
115
+ const REGION = "iad1";
116
+
117
+ export const environment = VercelSandbox.environment({ region: REGION });
118
+
119
+ export default defineSandbox(async () => {
120
+ const drive = await Drive.getOrCreate({
121
+ name: "agent-workspace",
122
+ region: REGION,
123
+ });
124
+
125
+ return environment.open({
126
+ mounts: {
127
+ "/data": drive,
128
+ },
129
+ });
130
+ });
131
+ ```
132
+
133
+ This example mounts the Drive read-write at `/data`. Files written there remain on the Drive after the sandbox stops. Keep Drive mounts separate from eve's managed `/workspace` directory.
134
+
135
+ Set the same region on the environment and the Drive. The environment region places the prepared snapshot and each session sandbox together with the Drive.
136
+
137
+ A Drive supports one active read-write mount. Because each eve session gets its own sandbox, the fixed `agent-workspace` name works only when one eve session uses the Drive at a time. If sessions can overlap, derive an opaque, stable Drive name per owner or tenant. The [Vercel guide to using Drives with eve](https://vercel.com/kb/guide/vercel-drives-eve-agent) shows how to derive a Drive name from the session initiator.
138
+
139
+ To share a point-in-time view across concurrent sandboxes, mount a read-only snapshot instead:
140
+
141
+ ```ts
142
+ return environment.open({
143
+ mounts: {
144
+ "/data": drive.snapshot(),
145
+ },
146
+ });
147
+ ```
148
+
149
+ Before mounting `drive.snapshot()`, seed a new Drive by mounting it read-write and writing at least one file. An uninitialized Drive cannot be mounted as a snapshot.
150
+
151
+ Vercel allows up to four non-overlapping, absolute Drive mount paths per sandbox. See the Vercel documentation for [Drive lifecycle, limits, and pricing](https://vercel.com/docs/sandbox/concepts/drives).
152
+
107
153
  ## Live sandbox options
108
154
 
109
155
  `open()` also accepts Vercel compute resources, timeout, and Drive mounts. Configure project credentials and custom fetch behavior on the environment, not in `open()`, because resume does not reconstruct start-only options.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eve",
3
- "version": "0.79.1",
3
+ "version": "0.80.0",
4
4
  "private": false,
5
5
  "description": "Filesystem-first framework for durable backend AI agents that run anywhere.",
6
6
  "keywords": [
@@ -569,9 +569,9 @@
569
569
  "@ai-sdk/provider": "^4.0.26",
570
570
  "@ai-sdk/provider-utils": "^5.0.58",
571
571
  "@cfworker/json-schema": "4.1.1",
572
- "@chat-adapter/slack": "4.41.0",
573
- "@chat-adapter/state-memory": "4.41.0",
574
- "@chat-adapter/twilio": "4.41.0",
572
+ "@chat-adapter/slack": "4.42.0",
573
+ "@chat-adapter/state-memory": "4.42.0",
574
+ "@chat-adapter/twilio": "4.42.0",
575
575
  "@clack/core": "1.3.1",
576
576
  "@eve/catalog": "0.0.1",
577
577
  "@eve/tracing": "0.0.0",
@@ -592,7 +592,7 @@
592
592
  "@types/react": "19.2.15",
593
593
  "@types/react-test-renderer": "19.1.0",
594
594
  "@vercel/blob": "2.8.0",
595
- "@vercel/connect": "2.3.3",
595
+ "@vercel/connect": "2.5.2",
596
596
  "@vercel/detect-agent": "1.2.3",
597
597
  "@vercel/oidc": "3.8.0",
598
598
  "@vercel/otel": "2.1.3",
@@ -607,7 +607,7 @@
607
607
  "@workflow/world-local": "5.1.0",
608
608
  "@workflow/world-vercel": "5.2.0",
609
609
  "ai": "^7.0.137",
610
- "chat": "4.41.0",
610
+ "chat": "4.42.0",
611
611
  "chokidar": "5.0.0",
612
612
  "commander": "14.0.3",
613
613
  "dd-trace": "6.13.0",
@@ -640,7 +640,7 @@
640
640
  "@opentelemetry/api": "^1.0.0",
641
641
  "ai": "^7.0.137",
642
642
  "braintrust": "^3.0.0",
643
- "chat": "^4.41.0",
643
+ "chat": "^4.42.0",
644
644
  "dd-trace": "^6.13.0",
645
645
  "just-bash": "^3.1.0",
646
646
  "microsandbox": "^0.5.0"
@@ -1,12 +0,0 @@
1
- var e=/[<>|]/,t=/[\^|>]/,n=/^[A-Z0-9_]+$/,r=/(?<![<\w])@([A-Z][A-Z0-9_]+)/g,i=3e3,a=/^[ \t]+/,o=/^(?:&gt;|&lt;|#{1,6}(?=[ \t\n]|$)|[-+*](?=[ \t\n]|$)|`{3,}|~{3,}|(?:[-*_][ \t]*){3,}(?=\n|$))/,s=/^(\d{1,9})([.)])(?=[ \t\n]|$)/;function c(e){return e.replace(/&/g,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`)}function l(e){return e.replace(/&lt;/g,`<`).replace(/&gt;/g,`>`).replace(/&amp;/g,`&`)}function u(e,t={}){return O(e),{...t.emoji===void 0?{}:{emoji:t.emoji},text:e,type:`plain_text`}}function d(e,t={}){return O(e),{text:e,type:`mrkdwn`,...t.verbatim===void 0?{}:{verbatim:t.verbatim}}}function f(e){return k(e,`userId`),`<@${e}>`}function p(e){return k(e,`channelId`),`<#${e}>`}function m(e){return k(e,`userGroupId`),`<!subteam^${e}>`}function h(e){return`<!${e}>`}function g(e,t){return A(e,`url`),t?`<${e}|${c(t)}>`:`<${e}>`}function _(e,t,n,r={}){j(t,`token`);let i=e instanceof Date?Math.floor(e.getTime()/1e3):e;if(!Number.isInteger(i))throw TypeError(`timestamp must be an integer unix timestamp or Date`);return`<!date^${i}^${t}${r.link?`^${M(r.link)}`:``}|${c(n)}>`}function v(e){return l(e.includes("```")?x(e):b(e))}function y(e){let t=e.replace(/<@([A-Z0-9_]+)\|([^<>]+)>/g,`@$2`);return t=t.replace(/<@([A-Z0-9_]+)>/g,`@$1`),t=t.replace(/<#([A-Z0-9_]+)\|([^<>]+)>/g,`#$2 ($1)`),t=t.replace(/<#([A-Z0-9_]+)>/g,`#$1`),t=t.replace(/<(?!https?:\/\/)([^<>|]+)\|(https?:\/\/[^|<>]+)>/g,`<$2|$1>`),t=t.replace(/<(https?:\/\/[^|<>]+)\|([^<>]+)>/g,`[$2]($1)`),t=t.replace(/<(https?:\/\/[^<>]+)>/g,`$1`),t}function b(e){let t=y(e);return t=t.replace(/(?<![_*\\])\*([^*\n]+)\*(?![_*])/g,`**$1**`),t=t.replace(/(?<!~)~([^~\n]+)~(?!~)/g,`~~$1~~`),t}function x(e){let t=``,n=0,r=0,i=!1,a=r=>{let a=e.slice(n,r);i&&=(a=T(a),!1),t+=b(a)};for(;r<e.length;){let o=e[r];if(o===`<`){let t=S(e,r);r=t===-1?r+1:t;continue}if(o!=="`"){r+=1;continue}if(!e.startsWith("```",r)){let t=C(e,r);r=t===-1?r+1:t;continue}let s=r+3,c=e.indexOf("```",s);if(c===-1||w(e,r)){r=s;continue}a(r),t.length>0&&!t.endsWith(`
2
- `)&&(t+=`
3
- `);let l=e.slice(s,c);t+="```",l.startsWith(`
4
- `)||(t+=`
5
- `),t+=y(l),l.endsWith(`
6
- `)||(t+=`
7
- `),t+="```",r=c+3,n=r,r<e.length&&e[r]!==`
8
- `&&(t+=`
9
- `,i=!0)}return a(e.length),t}function S(e,t){let n=t+1;for(;n<e.length&&e[n]!==`>`&&e[n]!==`
10
- `&&e[n]!==`\r`;)n+=1;return e[n]===`>`?n+1:-1}function C(e,t){let n=e.indexOf("`",t+1);if(n===-1)return-1;let r=e.indexOf(`
11
- `,t+1);return r!==-1&&r<n?-1:n+1}function w(e,t){let n=e.lastIndexOf(`
12
- `,t-1)+1;return e.slice(n,t).trimStart().startsWith(`&gt;`)}function T(e){let t=a.exec(e)?.[0]??``,n=t.length>0?` `:``,r=e.slice(t.length);return o.test(r)?`${n}\\${r}`:n+r.replace(s,`$1\\$2`)}function E(e){return e.replace(/\*\*(.+?)\*\*/g,`*$1*`)}function D(e){return e.replace(r,`<@$1>`)}function O(e){if(e.length<1||e.length>i)throw TypeError(`text must be between 1 and ${i} characters`)}function k(e,t){if(!n.test(e))throw TypeError(`${t} must be a Slack ID`)}function A(t,n){if(e.test(t))throw TypeError(`${n} cannot contain Slack control characters`)}function j(e,n){if(t.test(e))throw TypeError(`${n} cannot contain Slack date control characters`)}function M(e){return j(e,`link`),e}export{_ as a,f as c,E as d,v as f,p as i,m as l,u as n,g as o,l as p,c as r,h as s,d as t,D as u};