eve 0.47.3 → 0.47.5

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 (178) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/bin/eve.js +4 -0
  3. package/dist/src/cli/dev/tui/traces/trace-conversation.js +1 -1
  4. package/dist/src/compiled/.vendor-stamp.json +1 -1
  5. package/dist/src/compiled/@opentelemetry/api/index.d.ts +37 -0
  6. package/dist/src/compiled/@vercel/otel/index.d.ts +11 -0
  7. package/dist/src/compiler/extension-compatibility.d.ts +7 -5
  8. package/dist/src/compiler/extension-compatibility.js +1 -1
  9. package/dist/src/compiler/manifest.d.ts +14 -2
  10. package/dist/src/compiler/manifest.js +1 -1
  11. package/dist/src/compiler/normalize-connection.d.ts +9 -2
  12. package/dist/src/compiler/normalize-connection.js +1 -1
  13. package/dist/src/compiler/normalize-manifest.js +1 -1
  14. package/dist/src/compiler/validate-artifact.js +1 -1
  15. package/dist/src/context/dynamic-connection-lifecycle.d.ts +9 -0
  16. package/dist/src/context/dynamic-connection-lifecycle.js +1 -0
  17. package/dist/src/context/keys.d.ts +3 -1
  18. package/dist/src/context/memory-tools.js +1 -1
  19. package/dist/src/context/providers/connection.js +1 -1
  20. package/dist/src/dynamic/definition.d.ts +6 -3
  21. package/dist/src/dynamic/definition.js +1 -1
  22. package/dist/src/execution/authorization-callback-match.js +1 -1
  23. package/dist/src/execution/cancelled-turn-message.d.ts +2 -0
  24. package/dist/src/execution/cancelled-turn-message.js +1 -0
  25. package/dist/src/execution/dynamic-connections.d.ts +9 -0
  26. package/dist/src/execution/dynamic-connections.js +1 -0
  27. package/dist/src/execution/eve-workflow-attributes.js +1 -1
  28. package/dist/src/execution/node-step.d.ts +2 -0
  29. package/dist/src/execution/node-step.js +1 -1
  30. package/dist/src/execution/settle-cancelled-turn-step.js +1 -1
  31. package/dist/src/execution/tasks/parent/tool-execution.js +1 -1
  32. package/dist/src/execution/tools/connection-search.js +2 -2
  33. package/dist/src/execution/tools/load-skill.js +1 -1
  34. package/dist/src/execution/tools/subagent/local.js +1 -1
  35. package/dist/src/execution/turn-workflow.js +1 -1
  36. package/dist/src/execution/workflow-runtime.js +1 -1
  37. package/dist/src/execution/workflow-steps.js +1 -1
  38. package/dist/src/execution/workflow-trace-context.d.ts +3 -6
  39. package/dist/src/execution/workflow-trace-context.js +1 -1
  40. package/dist/src/harness/authorization.d.ts +11 -4
  41. package/dist/src/harness/authorization.js +1 -1
  42. package/dist/src/harness/inline-tool-authorization.d.ts +13 -1
  43. package/dist/src/harness/inline-tool-authorization.js +1 -1
  44. package/dist/src/harness/model-call-error.js +1 -1
  45. package/dist/src/harness/semantic-errors/rule.d.ts +5 -2
  46. package/dist/src/harness/semantic-errors/rules/gateway.js +1 -1
  47. package/dist/src/harness/tool-loop.js +2 -2
  48. package/dist/src/harness/turn-tag-state.d.ts +1 -1
  49. package/dist/src/harness/types.d.ts +2 -2
  50. package/dist/src/{harness → instrumentation}/ai-sdk-hook-bridge.d.ts +1 -1
  51. package/dist/src/instrumentation/ai-sdk-hook-bridge.js +1 -0
  52. package/dist/src/{harness/channel-delivery-instrumentation.d.ts → instrumentation/channel-delivery.d.ts} +5 -7
  53. package/dist/src/instrumentation/channel-delivery.js +1 -0
  54. package/dist/src/{harness/instrumentation → instrumentation}/config.d.ts +0 -10
  55. package/dist/src/instrumentation/config.js +1 -0
  56. package/dist/src/{harness/instrumentation → instrumentation}/content-policy.d.ts +1 -1
  57. package/dist/src/instrumentation/content-policy.js +1 -0
  58. package/dist/src/{harness/instrumentation → instrumentation}/content.d.ts +1 -1
  59. package/dist/src/{harness/instrumentation → instrumentation}/dispatch.d.ts +1 -1
  60. package/dist/src/instrumentation/dispatch.js +1 -0
  61. package/dist/src/{harness/instrumentation → instrumentation}/lifecycle.d.ts +26 -33
  62. package/dist/src/instrumentation/lifecycle.js +1 -0
  63. package/dist/src/{harness/instrumentation → instrumentation}/native-events.d.ts +1 -1
  64. package/dist/src/instrumentation/native-events.js +1 -0
  65. package/dist/src/{harness → instrumentation}/parent-lineage.d.ts +1 -1
  66. package/dist/src/{harness → instrumentation}/prepare-trace-context.d.ts +8 -5
  67. package/dist/src/instrumentation/prepare-trace-context.js +1 -0
  68. package/dist/src/instrumentation/provider.d.ts +13 -9
  69. package/dist/src/{harness/instrumentation → instrumentation}/providers.d.ts +1 -1
  70. package/dist/src/instrumentation/providers.js +1 -0
  71. package/dist/src/{harness/instrumentation → instrumentation}/runtime-context.d.ts +13 -2
  72. package/dist/src/instrumentation/runtime-context.js +1 -0
  73. package/dist/src/instrumentation/runtime.d.ts +100 -0
  74. package/dist/src/instrumentation/runtime.js +1 -0
  75. package/dist/src/{harness/instrumentation → instrumentation}/state.d.ts +2 -11
  76. package/dist/src/internal/application/compiled-artifacts.js +3 -3
  77. package/dist/src/internal/application/package.js +1 -1
  78. package/dist/src/internal/authored-definition/connection.js +1 -1
  79. package/dist/src/internal/instrumentation.d.ts +1 -1
  80. package/dist/src/internal/nitro/routes/channel-request-instrumentation.d.ts +4 -6
  81. package/dist/src/internal/nitro/routes/channel-request-instrumentation.js +1 -1
  82. package/dist/src/packages/eve-catalog/src/index.js +1 -1
  83. package/dist/src/public/channels/slack/defaults.js +6 -3
  84. package/dist/src/public/channels/slack/index.d.ts +1 -1
  85. package/dist/src/public/channels/slack/interactions.d.ts +8 -5
  86. package/dist/src/public/channels/slack/interactions.js +1 -1
  87. package/dist/src/public/channels/slack/slackChannel.d.ts +66 -1
  88. package/dist/src/public/channels/slack/slash-command.d.ts +9 -0
  89. package/dist/src/public/channels/slack/slash-command.js +1 -0
  90. package/dist/src/public/connections/index.d.ts +2 -0
  91. package/dist/src/public/connections/index.js +1 -1
  92. package/dist/src/public/definitions/connections/dynamic.d.ts +23 -0
  93. package/dist/src/public/definitions/connections/dynamic.js +1 -0
  94. package/dist/src/public/definitions/connections/mcp.d.ts +8 -0
  95. package/dist/src/public/definitions/connections/openapi.d.ts +8 -0
  96. package/dist/src/public/definitions/connections/protocol.d.ts +6 -0
  97. package/dist/src/public/definitions/connections/protocol.js +1 -1
  98. package/dist/src/runtime/connections/authorization-tokens.d.ts +11 -10
  99. package/dist/src/runtime/connections/instance-identity.d.ts +12 -0
  100. package/dist/src/runtime/connections/instance-identity.js +1 -0
  101. package/dist/src/runtime/connections/mcp-client.js +1 -1
  102. package/dist/src/runtime/connections/registry.d.ts +2 -0
  103. package/dist/src/runtime/connections/registry.js +1 -1
  104. package/dist/src/runtime/connections/scoped-authorization.d.ts +10 -6
  105. package/dist/src/runtime/connections/scoped-authorization.js +1 -1
  106. package/dist/src/runtime/resolve-agent.js +1 -1
  107. package/dist/src/runtime/resolve-connection.d.ts +5 -0
  108. package/dist/src/runtime/resolve-connection.js +1 -1
  109. package/dist/src/runtime/resolve-dynamic-connection.d.ts +5 -0
  110. package/dist/src/runtime/resolve-dynamic-connection.js +1 -0
  111. package/dist/src/runtime/types.d.ts +11 -0
  112. package/dist/src/setup/flows/registry.js +1 -1
  113. package/dist/src/setup/scaffold/create/project.js +1 -1
  114. package/dist/src/shared/trace-policy.d.ts +20 -0
  115. package/dist/src/shared/trace-policy.js +1 -0
  116. package/dist/src/tracing/agent-action-instrumentation.d.ts +1 -1
  117. package/dist/src/tracing/agent-action-instrumentation.js +1 -1
  118. package/dist/src/tracing/agent-approval-instrumentation.d.ts +1 -1
  119. package/dist/src/tracing/agent-channel-delivery-instrumentation.d.ts +1 -1
  120. package/dist/src/tracing/agent-channel-delivery-instrumentation.js +1 -1
  121. package/dist/src/tracing/agent-otel-provider.d.ts +2 -2
  122. package/dist/src/tracing/agent-otel-provider.js +2 -2
  123. package/dist/src/tracing/agent-otel-runtime-context.d.ts +5 -0
  124. package/dist/src/tracing/agent-otel-runtime-context.js +1 -0
  125. package/dist/src/tracing/agent-otel-session-context.d.ts +1 -1
  126. package/dist/src/tracing/agent-otel-session-context.js +1 -1
  127. package/dist/src/tracing/agent-otel-usage.d.ts +4 -2
  128. package/dist/src/tracing/agent-otel-usage.js +1 -1
  129. package/dist/src/tracing/agent-span-id-generator.d.ts +1 -1
  130. package/dist/src/tracing/agent-tool-instrumentation.d.ts +1 -1
  131. package/dist/src/tracing/agent-tool-instrumentation.js +1 -1
  132. package/dist/src/tracing/agent-trace-context-store.d.ts +3 -0
  133. package/dist/src/tracing/agent-trace-context-store.js +1 -1
  134. package/dist/src/tracing/agent-trace-context.d.ts +3 -0
  135. package/dist/src/tracing/agent-trace-context.js +1 -0
  136. package/dist/src/tracing/agent-trace-state.d.ts +8 -1
  137. package/dist/src/tracing/agent-trace-state.js +1 -1
  138. package/dist/src/tracing/install-instrumentation-runtime.d.ts +3 -2
  139. package/dist/src/tracing/install-instrumentation-runtime.js +1 -1
  140. package/dist/src/tracing/local-instrumentation-runtime.d.ts +1 -1
  141. package/dist/src/tracing/local-instrumentation-runtime.js +1 -1
  142. package/dist/src/tracing/local-trace-reader.d.ts +1 -0
  143. package/dist/src/tracing/local-trace-reader.js +1 -1
  144. package/dist/src/tracing/otel-declaration.d.ts +13 -16
  145. package/dist/src/tracing/otel-declaration.js +1 -1
  146. package/dist/src/tracing/otel-registration.js +1 -1
  147. package/dist/src/tracing/sampled-trace.d.ts +1 -14
  148. package/dist/src/tracing/sampled-trace.js +1 -1
  149. package/docs/channels/slack.mdx +25 -5
  150. package/docs/connections/overview.mdx +46 -2
  151. package/docs/extensions.md +2 -2
  152. package/docs/getting-started.mdx +1 -1
  153. package/docs/guides/dynamic-capabilities.md +91 -5
  154. package/docs/memory.md +7 -4
  155. package/docs/reference/cli.md +4 -4
  156. package/docs/reference/typescript-api.md +32 -23
  157. package/package.json +1 -1
  158. package/dist/src/execution/subagent-usage-span.d.ts +0 -9
  159. package/dist/src/execution/subagent-usage-span.js +0 -1
  160. package/dist/src/harness/ai-sdk-hook-bridge.js +0 -1
  161. package/dist/src/harness/channel-delivery-instrumentation.js +0 -1
  162. package/dist/src/harness/instrumentation/config.js +0 -1
  163. package/dist/src/harness/instrumentation/content-policy.js +0 -1
  164. package/dist/src/harness/instrumentation/dispatch.js +0 -1
  165. package/dist/src/harness/instrumentation/lifecycle.js +0 -1
  166. package/dist/src/harness/instrumentation/native-events.js +0 -1
  167. package/dist/src/harness/instrumentation/providers.js +0 -1
  168. package/dist/src/harness/instrumentation/runtime-context.js +0 -1
  169. package/dist/src/harness/instrumentation/runtime.d.ts +0 -21
  170. package/dist/src/harness/instrumentation/runtime.js +0 -1
  171. package/dist/src/harness/prepare-trace-context.js +0 -1
  172. /package/dist/src/{harness → instrumentation}/ai-sdk-telemetry.d.ts +0 -0
  173. /package/dist/src/{harness → instrumentation}/ai-sdk-telemetry.js +0 -0
  174. /package/dist/src/{harness/instrumentation → instrumentation}/content.js +0 -0
  175. /package/dist/src/{harness → instrumentation}/parent-lineage.js +0 -0
  176. /package/dist/src/{harness/instrumentation → instrumentation}/setup-context.d.ts +0 -0
  177. /package/dist/src/{harness/instrumentation → instrumentation}/setup-context.js +0 -0
  178. /package/dist/src/{harness/instrumentation → instrumentation}/state.js +0 -0
@@ -1 +1 @@
1
- import{normalizeChannelAudience}from"#shared/channel-audience.js";import{contextStorage,loadContext}from"#context/container.js";import{SessionTraceSeedKey}from"#context/keys.js";import{ContextKey}from"#context/key.js";const AgentTraceContextKey=new ContextKey(`eve.harness.agentTrace`,{codec:{deserialize:deserializeState,serialize:serializeState}});function readCurrentSessionTraceDecision(e){return contextStorage.getStore()?.get(AgentTraceContextKey)?.sessions[e]?.decision}function preserveSerializedAgentTraceState(e,t){let n=t[AgentTraceContextKey.name];return n===void 0?e:{...e,[AgentTraceContextKey.name]:n}}function readSessionTraceContext(e,t){let n=e[AgentTraceContextKey.name];if(n===void 0)return;let r=deserializeState(n).sessions[t];return r===void 0?void 0:withTraceDecision(e,r.context,r.decision)}function readActionTraceContext(e,t,n,r){let i=e[AgentTraceContextKey.name];if(i===void 0)return;let o=deserializeState(i),s=Object.values(o.actions).find(e=>e.sessionId===t&&e.turnId===n&&e.callId===r);if(s!==void 0)return withTraceDecision(e,{isRemote:!1,spanId:s.spanId,traceFlags:s.parent.traceFlags,traceId:s.parent.traceId},o.sessions[s.sessionId]?.decision)}function withTraceDecision(e,t,n){let i=e[SessionTraceSeedKey.name],a=n??i?.decision;return a===void 0?t:{...t,decision:a}}var ContextAgentTraceStateStore=class{deleteAction(e){updateState(t=>{let n={...t.actions};return delete n[e],{...t,actions:n}})}deleteActions(e,t){updateState(n=>{let r={...n.actions};for(let[n,i]of Object.entries(r))i.sessionId===e&&(t===void 0||i.turnId===t)&&delete r[n];return{...n,actions:r}})}deleteSession(e){updateState(t=>{let n={...t.sessions};return delete n[e],{...t,sessions:n}})}deleteTurn(e,t){updateState(n=>{let r={...n.turns};return delete r[turnKey(e,t)],{...n,turns:r}})}findAction(e,n){return Object.values(contextStorage.getStore()?.get(AgentTraceContextKey)?.actions??{}).find(t=>t.sessionId===e&&t.callId===n)}getAction(e){return contextStorage.getStore()?.get(AgentTraceContextKey)?.actions[e]}getSession(e){return contextStorage.getStore()?.get(AgentTraceContextKey)?.sessions[e]}getTurn(e,n){return contextStorage.getStore()?.get(AgentTraceContextKey)?.turns[turnKey(e,n)]}setAction(e,t){updateState(n=>({...n,actions:{...n.actions,[e]:t}}))}setSession(e,t){updateState(n=>({...n,sessions:{...n.sessions,[e]:t}}))}setTurn(e,t,n){updateState(r=>({...r,turns:{...r.turns,[turnKey(e,t)]:n}}))}};function updateState(e){loadContext().set(AgentTraceContextKey,t=>e(t??{actions:{},sessions:{},turns:{}}))}function turnKey(e,t){return`${e}\0${t}`}function serializeState(e){return{actions:e.actions,sessions:Object.fromEntries(Object.entries(e.sessions).map(([e,t])=>[e,{...t,context:serializeSpanContext(t.context)}])),turns:Object.fromEntries(Object.entries(e.turns).map(([e,t])=>[e,{...t,context:serializeSpanContext(t.context),terminal:t.terminal===void 0?void 0:t.terminal.type===`turn.failed`?{error:serializeError(t.terminal.error),type:t.terminal.type}:{type:t.terminal.type}}]))}}function deserializeState(t){return isRecord(t)?{actions:deserializeRecord(t.actions,deserializeAction),sessions:deserializeRecord(t.sessions,t=>{if(!(!isRecord(t)||!isSpanContext(t.context)))return{agentName:typeof t.agentName==`string`?t.agentName:void 0,channelAudience:normalizeChannelAudience(t.channelAudience),channelKind:typeof t.channelKind==`string`?t.channelKind:void 0,context:t.context,decision:deserializeInstrumentationDecision(t.decision),rootSessionId:typeof t.rootSessionId==`string`?t.rootSessionId:``}}),turns:deserializeRecord(t.turns,e=>{if(!(!isRecord(e)||!isSpanContext(e.context))&&!(typeof e.parentSpanId!=`string`||typeof e.startTimeMs!=`number`))return{context:e.context,parentIsRemote:typeof e.parentIsRemote==`boolean`?e.parentIsRemote:void 0,parentSpanId:e.parentSpanId,rootSessionId:typeof e.rootSessionId==`string`?e.rootSessionId:``,sequence:typeof e.sequence==`number`?e.sequence:0,startTimeMs:e.startTimeMs,subagentName:typeof e.subagentName==`string`?e.subagentName:void 0,terminal:deserializeTerminal(e.terminal)}})}:{actions:{},sessions:{},turns:{}}}function deserializeAction(t){if(!(!isRecord(t)||typeof t.attemptIndex!=`number`||typeof t.callId!=`string`||!isActionKind(t.kind)||typeof t.name!=`string`||!isSpanContext(t.parent)||typeof t.rootSessionId!=`string`||typeof t.sessionId!=`string`||typeof t.spanId!=`string`||typeof t.startTimeMs!=`number`||typeof t.stepIndex!=`number`||typeof t.turnId!=`string`))return{attemptIndex:t.attemptIndex,callId:t.callId,channelAudience:normalizeChannelAudience(t.channelAudience),inputAttribute:typeof t.inputAttribute==`string`?t.inputAttribute:void 0,kind:t.kind,name:t.name,parent:t.parent,rootSessionId:t.rootSessionId,sessionId:t.sessionId,spanId:t.spanId,startTimeMs:t.startTimeMs,stepIndex:t.stepIndex,turnId:t.turnId}}function isActionKind(e){return e===`load-skill`||e===`remote-agent-call`||e===`subagent-call`||e===`tool-call`}function deserializeRecord(e,t){if(!isRecord(e))return{};let n={};for(let[r,i]of Object.entries(e)){let e=t(i);e!==void 0&&(n[r]=e)}return n}function deserializeTerminal(e){if(!isRecord(e)||typeof e.type!=`string`)return;let t=e.type;if(isTurnTerminalType(t))return t===`turn.failed`?{error:deserializeError(e.error),type:t}:{type:t}}function serializeSpanContext(e){return{isRemote:e.isRemote,spanId:e.spanId,traceFlags:e.traceFlags,traceId:e.traceId}}function isSpanContext(e){return isRecord(e)&&typeof e.spanId==`string`&&typeof e.traceFlags==`number`&&typeof e.traceId==`string`}function serializeError(e){return e instanceof Error?{message:e.message,name:e.name,stack:e.stack}:void 0}function deserializeError(e){if(!isRecord(e)||typeof e.message!=`string`)return;let t=Error(e.message);return typeof e.name==`string`&&(t.name=e.name),typeof e.stack==`string`&&(t.stack=e.stack),t}function isTurnTerminalType(e){return e===`turn.cancelled`||e===`turn.completed`||e===`turn.failed`}function deserializeInstrumentationDecision(e){if(isRecord(e))return e.action===`drop`?{action:`drop`}:e.action===`record`&&typeof e.recordInputs==`boolean`&&typeof e.recordOutputs==`boolean`?{action:`record`,recordInputs:e.recordInputs,recordOutputs:e.recordOutputs}:void 0}function isRecord(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}export{ContextAgentTraceStateStore,preserveSerializedAgentTraceState,readActionTraceContext,readCurrentSessionTraceDecision,readSessionTraceContext};
1
+ import{normalizeChannelAudience}from"#shared/channel-audience.js";import{contextStorage,loadContext}from"#context/container.js";import{SessionTraceSeedKey}from"#context/keys.js";import{ContextKey}from"#context/key.js";const AgentTraceContextKey=new ContextKey(`eve.harness.agentTrace`,{codec:{deserialize:deserializeState,serialize:serializeState}});function readCurrentSessionTraceDecision(e){let n=contextStorage.getStore();return n===void 0?void 0:readSessionTraceDecision(n,e)}function readSessionTraceDecision(e,t){return e.get(AgentTraceContextKey)?.sessions[t]?.decision}function preserveSerializedAgentTraceState(e,t){let n=t[AgentTraceContextKey.name];return n===void 0?e:{...e,[AgentTraceContextKey.name]:n}}function readSessionTraceContext(e,t){let n=e[AgentTraceContextKey.name];if(n===void 0)return;let r=deserializeState(n).sessions[t];return r===void 0?void 0:withTraceDecision(e,r.context,r.decision)}function readActionTraceContext(e,t,n,r){let i=e[AgentTraceContextKey.name];if(i===void 0)return;let o=deserializeState(i),s=Object.values(o.actions).find(e=>e.sessionId===t&&e.turnId===n&&e.callId===r);if(s!==void 0)return withTraceDecision(e,{isRemote:!1,spanId:s.spanId,traceFlags:s.parent.traceFlags,traceId:s.parent.traceId},o.sessions[s.sessionId]?.decision)}function withTraceDecision(e,t,n){let i=e[SessionTraceSeedKey.name],a=n??i?.decision;return a===void 0?t:{...t,decision:a}}var ContextAgentTraceStateStore=class{deleteAction(e){updateState(t=>{let n={...t.actions};return delete n[e],{...t,actions:n}})}deleteActions(e,t){updateState(n=>{let r={...n.actions};for(let[n,i]of Object.entries(r))i.sessionId===e&&(t===void 0||i.turnId===t)&&delete r[n];return{...n,actions:r}})}deleteSession(e){updateState(t=>{let n={...t.sessions};return delete n[e],{...t,sessions:n}})}deleteTurn(e,t){updateState(n=>{let r={...n.turns};return delete r[turnKey(e,t)],{...n,turns:r}})}findAction(e,n){return Object.values(contextStorage.getStore()?.get(AgentTraceContextKey)?.actions??{}).find(t=>t.sessionId===e&&t.callId===n)}getAction(e){return contextStorage.getStore()?.get(AgentTraceContextKey)?.actions[e]}getSession(e){return contextStorage.getStore()?.get(AgentTraceContextKey)?.sessions[e]}getTurn(e,n){return contextStorage.getStore()?.get(AgentTraceContextKey)?.turns[turnKey(e,n)]}setAction(e,t){updateState(n=>({...n,actions:{...n.actions,[e]:t}}))}setSession(e,t){updateState(n=>({...n,sessions:{...n.sessions,[e]:t}}))}setTurn(e,t,n){updateState(r=>({...r,turns:{...r.turns,[turnKey(e,t)]:n}}))}updateTurn(e,t,n){updateState(r=>{let i=turnKey(e,t),a=r.turns[i];return a===void 0?r:{...r,turns:{...r.turns,[i]:n(a)}}})}};function updateState(e){loadContext().set(AgentTraceContextKey,t=>e(t??{actions:{},sessions:{},turns:{}}))}function turnKey(e,t){return`${e}\0${t}`}function serializeState(e){return{actions:e.actions,sessions:Object.fromEntries(Object.entries(e.sessions).map(([e,t])=>[e,{...t,context:serializeSpanContext(t.context)}])),turns:Object.fromEntries(Object.entries(e.turns).map(([e,t])=>[e,{...t,context:serializeSpanContext(t.context),terminal:t.terminal===void 0?void 0:t.terminal.type===`turn.failed`?{error:serializeError(t.terminal.error),type:t.terminal.type}:{type:t.terminal.type}}]))}}function deserializeState(t){return isRecord(t)?{actions:deserializeRecord(t.actions,deserializeAction),sessions:deserializeRecord(t.sessions,t=>{if(!(!isRecord(t)||!isSpanContext(t.context)))return{agentName:typeof t.agentName==`string`?t.agentName:void 0,channelAudience:normalizeChannelAudience(t.channelAudience),channelKind:typeof t.channelKind==`string`?t.channelKind:void 0,context:t.context,decision:deserializeInstrumentationDecision(t.decision),rootSessionId:typeof t.rootSessionId==`string`?t.rootSessionId:``}}),turns:deserializeRecord(t.turns,e=>{if(!(!isRecord(e)||!isSpanContext(e.context))&&!(typeof e.parentSpanId!=`string`||typeof e.startTimeMs!=`number`))return{context:e.context,modelUsage:deserializeModelUsage(e.modelUsage),parentIsRemote:typeof e.parentIsRemote==`boolean`?e.parentIsRemote:void 0,parentSpanId:e.parentSpanId,rootSessionId:typeof e.rootSessionId==`string`?e.rootSessionId:``,sequence:typeof e.sequence==`number`?e.sequence:0,startTimeMs:e.startTimeMs,subagentName:typeof e.subagentName==`string`?e.subagentName:void 0,terminal:deserializeTerminal(e.terminal)}})}:{actions:{},sessions:{},turns:{}}}function deserializeModelUsage(e){if(!isRecord(e))return;let t=typeof e.inputTokens==`number`?e.inputTokens:void 0,n=typeof e.outputTokens==`number`?e.outputTokens:void 0;return t===void 0&&n===void 0?void 0:{inputTokens:t,outputTokens:n}}function deserializeAction(t){if(!(!isRecord(t)||typeof t.attemptIndex!=`number`||typeof t.callId!=`string`||!isActionKind(t.kind)||typeof t.name!=`string`||!isSpanContext(t.parent)||typeof t.rootSessionId!=`string`||typeof t.sessionId!=`string`||typeof t.spanId!=`string`||typeof t.startTimeMs!=`number`||typeof t.stepIndex!=`number`||typeof t.turnId!=`string`))return{attemptIndex:t.attemptIndex,callId:t.callId,channelAudience:normalizeChannelAudience(t.channelAudience),inputAttribute:typeof t.inputAttribute==`string`?t.inputAttribute:void 0,kind:t.kind,name:t.name,parent:t.parent,rootSessionId:t.rootSessionId,sessionId:t.sessionId,spanId:t.spanId,startTimeMs:t.startTimeMs,stepIndex:t.stepIndex,turnId:t.turnId}}function isActionKind(e){return e===`load-skill`||e===`remote-agent-call`||e===`subagent-call`||e===`tool-call`}function deserializeRecord(e,t){if(!isRecord(e))return{};let n={};for(let[r,i]of Object.entries(e)){let e=t(i);e!==void 0&&(n[r]=e)}return n}function deserializeTerminal(e){if(!isRecord(e)||typeof e.type!=`string`)return;let t=e.type;if(isTurnTerminalType(t))return t===`turn.failed`?{error:deserializeError(e.error),type:t}:{type:t}}function serializeSpanContext(e){return{isRemote:e.isRemote,spanId:e.spanId,traceFlags:e.traceFlags,traceId:e.traceId}}function isSpanContext(e){return isRecord(e)&&typeof e.spanId==`string`&&typeof e.traceFlags==`number`&&typeof e.traceId==`string`}function serializeError(e){return e instanceof Error?{message:e.message,name:e.name,stack:e.stack}:void 0}function deserializeError(e){if(!isRecord(e)||typeof e.message!=`string`)return;let t=Error(e.message);return typeof e.name==`string`&&(t.name=e.name),typeof e.stack==`string`&&(t.stack=e.stack),t}function isTurnTerminalType(e){return e===`turn.cancelled`||e===`turn.completed`||e===`turn.failed`}function deserializeInstrumentationDecision(e){if(isRecord(e))return e.action===`drop`?{action:`drop`}:e.action===`record`&&typeof e.recordInputs==`boolean`&&typeof e.recordOutputs==`boolean`?{action:`record`,recordInputs:e.recordInputs,recordOutputs:e.recordOutputs}:void 0}function isRecord(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}export{ContextAgentTraceStateStore,preserveSerializedAgentTraceState,readActionTraceContext,readCurrentSessionTraceDecision,readSessionTraceContext,readSessionTraceDecision};
@@ -0,0 +1,3 @@
1
+ import { type Context } from "#compiled/@opentelemetry/api/index.js";
2
+ export declare function isAgentTraceContext(context: unknown): boolean;
3
+ export declare function markAgentTraceContext(context: Context): Context;
@@ -0,0 +1 @@
1
+ import{createContextKey}from"#compiled/@opentelemetry/api/index.js";const AGENT_TRACE_KEY=createContextKey(`eve.agent.trace`);function isAgentTraceContext(e){if(typeof e!=`object`||!e)return!1;let t=Reflect.get(e,`getValue`);return typeof t==`function`&&Reflect.apply(t,e,[AGENT_TRACE_KEY])===!0}function markAgentTraceContext(e){return e.setValue(AGENT_TRACE_KEY,!0)}export{isAgentTraceContext,markAgentTraceContext};
@@ -1,5 +1,5 @@
1
1
  import type { SpanContext } from "#compiled/@opentelemetry/api/index.js";
2
- import type { InstrumentationActionKind, InstrumentationTraceContext, InstrumentationTurnFailedEvent, InstrumentationTurnSettledEvent } from "#harness/instrumentation/lifecycle.js";
2
+ import type { InstrumentationActionKind, InstrumentationTraceContext, InstrumentationTurnFailedEvent, InstrumentationTurnSettledEvent } from "#instrumentation/lifecycle.js";
3
3
  import type { ChannelAudience } from "#shared/channel-audience.js";
4
4
  import type { InstrumentationDecision } from "#shared/instrumentation-decision.js";
5
5
  export interface AgentSessionTraceState {
@@ -12,6 +12,10 @@ export interface AgentSessionTraceState {
12
12
  }
13
13
  export interface AgentTurnTraceState {
14
14
  readonly context: SpanContext;
15
+ readonly modelUsage?: {
16
+ readonly inputTokens?: number;
17
+ readonly outputTokens?: number;
18
+ };
15
19
  readonly parentIsRemote?: boolean;
16
20
  readonly parentSpanId: string;
17
21
  readonly rootSessionId: string;
@@ -53,6 +57,8 @@ export interface AgentTraceStateStore {
53
57
  setAction(idempotencyKey: string, state: AgentActionTraceState): void | PromiseLike<void>;
54
58
  setSession(sessionId: string, state: AgentSessionTraceState): void | PromiseLike<void>;
55
59
  setTurn(sessionId: string, turnId: string, state: AgentTurnTraceState): void | PromiseLike<void>;
60
+ /** Atomically updates an existing turn and does nothing after that turn is deleted. */
61
+ updateTurn(sessionId: string, turnId: string, update: (state: AgentTurnTraceState) => AgentTurnTraceState): void | PromiseLike<void>;
56
62
  }
57
63
  /** In-memory trace state used by tests and non-durable runtimes. */
58
64
  export declare class InMemoryAgentTraceStateStore implements AgentTraceStateStore {
@@ -68,5 +74,6 @@ export declare class InMemoryAgentTraceStateStore implements AgentTraceStateStor
68
74
  setAction(idempotencyKey: string, state: AgentActionTraceState): void;
69
75
  setSession(sessionId: string, state: AgentSessionTraceState): void;
70
76
  setTurn(sessionId: string, turnId: string, state: AgentTurnTraceState): void;
77
+ updateTurn(sessionId: string, turnId: string, update: (state: AgentTurnTraceState) => AgentTurnTraceState): void;
71
78
  }
72
79
  export declare function turnKey(sessionId: string, turnId: string): string;
@@ -1 +1 @@
1
- var InMemoryAgentTraceStateStore=class{#e=new Map;#t=new Map;#n=new Map;deleteAction(e){this.#e.delete(e)}deleteActions(e,t){for(let[n,r]of this.#e)r.sessionId===e&&(t===void 0||r.turnId===t)&&this.#e.delete(n)}deleteSession(e){this.#t.delete(e)}deleteTurn(e,t){this.#n.delete(turnKey(e,t))}findAction(e,t){return[...this.#e.values()].find(n=>n.sessionId===e&&n.callId===t)}getAction(e){return this.#e.get(e)}getSession(e){return this.#t.get(e)}getTurn(e,t){return this.#n.get(turnKey(e,t))}setAction(e,t){this.#e.set(e,t)}setSession(e,t){this.#t.set(e,t)}setTurn(e,t,n){this.#n.set(turnKey(e,t),n)}};function turnKey(e,t){return`${e}:${t}`}export{InMemoryAgentTraceStateStore,turnKey};
1
+ var InMemoryAgentTraceStateStore=class{#e=new Map;#t=new Map;#n=new Map;deleteAction(e){this.#e.delete(e)}deleteActions(e,t){for(let[n,r]of this.#e)r.sessionId===e&&(t===void 0||r.turnId===t)&&this.#e.delete(n)}deleteSession(e){this.#t.delete(e)}deleteTurn(e,t){this.#n.delete(turnKey(e,t))}findAction(e,t){return[...this.#e.values()].find(n=>n.sessionId===e&&n.callId===t)}getAction(e){return this.#e.get(e)}getSession(e){return this.#t.get(e)}getTurn(e,t){return this.#n.get(turnKey(e,t))}setAction(e,t){this.#e.set(e,t)}setSession(e,t){this.#t.set(e,t)}setTurn(e,t,n){this.#n.set(turnKey(e,t),n)}updateTurn(e,t,n){let r=turnKey(e,t),i=this.#n.get(r);i!==void 0&&this.#n.set(r,n(i))}};function turnKey(e,t){return`${e}:${t}`}export{InMemoryAgentTraceStateStore,turnKey};
@@ -1,5 +1,5 @@
1
- import { type InstrumentationProviderDefinition } from "#harness/instrumentation/lifecycle.js";
2
- import { type InstrumentationRuntime } from "#harness/instrumentation/runtime.js";
1
+ import { type InstrumentationProviderDefinition } from "#instrumentation/lifecycle.js";
2
+ import { type InstrumentationRuntime } from "#instrumentation/runtime.js";
3
3
  import type { CollectedOtel, RuntimeContextResolver } from "#tracing/otel-declaration.js";
4
4
  /**
5
5
  * Installs the process instrumentation runtime around a collected pipeline.
@@ -14,6 +14,7 @@ import type { CollectedOtel, RuntimeContextResolver } from "#tracing/otel-declar
14
14
  export declare function installInstrumentationRuntime(input: {
15
15
  readonly collected: CollectedOtel;
16
16
  readonly frameworkVersion: string;
17
+ readonly instrumentationProviders?: boolean;
17
18
  readonly providers: readonly InstrumentationProviderDefinition[];
18
19
  readonly runtimeContextResolvers?: readonly RuntimeContextResolver[];
19
20
  readonly serviceName: string;
@@ -1 +1 @@
1
- import{createLogger,formatError}from"#internal/logging.js";import{ContextAgentTraceStateStore}from"#tracing/agent-trace-context-store.js";import{registerInstrumentationRuntime}from"#harness/instrumentation/runtime.js";import{trace}from"#compiled/@opentelemetry/api/index.js";import{createInstrumentationHooks}from"#harness/instrumentation/lifecycle.js";import{hasSessionRelease}from"#tracing/local-traces.js";import{AgentSpanIdGenerator}from"#tracing/agent-span-id-generator.js";import{createAgentOtelInstrumentation}from"#tracing/agent-otel-provider.js";import{registerOtelPipeline}from"#tracing/otel-registration.js";const log=createLogger(`tracing.install-instrumentation-runtime`);function installInstrumentationRuntime(e){let t=[],a=[],o,s,c,runInContext=(e,t)=>t();if(e.collected.declared){o=registerOtelPipeline({pipeline:e.collected.pipeline,serviceName:e.serviceName});let r=createAgentOtelInstrumentation({frameworkVersion:e.frameworkVersion,idGenerator:o.idGenerator,recordInputs:e.collected.settings.recordInputs,recordOutputs:e.collected.settings.recordOutputs,stateStore:new ContextAgentTraceStateStore,tracer:trace.getTracer(`eve.agent`,e.frameworkVersion),tracePolicy:e.collected.settings.tracePolicy});t.push({...r.hook,stateNamespace:`internal:otel`}),s=r.prepareSessionTrace,c=r.prepareTurnTrace,runInContext=r.runInContext;let i=e.collected.pipeline.spanProcessors.filter(isSpanProcessor).filter(hasSessionRelease);i.length>0&&a.push(sessionReleaseProvider(i))}let l=[...t,...e.providers,...a],u;return registerInstrumentationRuntime({forceFlush:()=>settleAll([...o===void 0?[]:[o.forceFlush],...l.map(e=>()=>e.flush?.())]),hooks:createInstrumentationHooks({parallel:e.providers,serialAfter:a,serialBefore:t}),idGenerator:o?.idGenerator??new AgentSpanIdGenerator,otelSettings:e.collected.declared?e.collected.settings:void 0,prepareSessionTrace:s,prepareTurnTrace:c,runtimeContextResolvers:e.runtimeContextResolvers,runInContext,shutdown:()=>(u??=settleAll([...o===void 0?[]:[o.shutdown],...l.map(e=>()=>e.shutdown?.())]),u)})}function isSpanProcessor(e){return e!==`auto`}function sessionReleaseProvider(e){let release=async t=>{await Promise.all(e.map(e=>e.releaseSession(t.sessionId)))};return{events:{"session.completed":release,"session.failed":release},name:`eve.session-release`,stateNamespace:`internal:session-release`}}async function settleAll(e){let n=await Promise.allSettled(e.map(async e=>e()));for(let e of n)e.status===`rejected`&&log.warn(`instrumentation drain failed`,{error:formatError(e.reason)})}export{installInstrumentationRuntime};
1
+ import{createLogger,formatError}from"#internal/logging.js";import{ContextAgentTraceStateStore}from"#tracing/agent-trace-context-store.js";import{registerInstrumentationRuntime}from"#instrumentation/runtime.js";import{trace}from"#compiled/@opentelemetry/api/index.js";import{createInstrumentationHooks}from"#instrumentation/lifecycle.js";import{hasSessionRelease}from"#tracing/local-traces.js";import{AgentSpanIdGenerator}from"#tracing/agent-span-id-generator.js";import{createAgentOtelInstrumentation}from"#tracing/agent-otel-provider.js";import{registerOtelPipeline}from"#tracing/otel-registration.js";const log=createLogger(`tracing.install-instrumentation-runtime`);function installInstrumentationRuntime(e){let t=[],a=[],o,s,c,runInContext=(e,t)=>t();if(e.collected.declared){o=registerOtelPipeline({pipeline:e.collected.pipeline,serviceName:e.serviceName});let r=createAgentOtelInstrumentation({frameworkVersion:e.frameworkVersion,idGenerator:o.idGenerator,recordInputs:e.collected.settings.recordInputs,recordOutputs:e.collected.settings.recordOutputs,stateStore:new ContextAgentTraceStateStore,tracer:trace.getTracer(`eve.agent`,e.frameworkVersion),tracePolicy:e.collected.settings.tracePolicy});t.push({...r.hook,stateNamespace:`internal:otel`}),s=r.prepareSessionTrace,c=r.prepareTurnTrace,runInContext=r.runInContext;let i=e.collected.pipeline.spanProcessors.filter(isSpanProcessor).filter(hasSessionRelease);i.length>0&&a.push(sessionReleaseProvider(i))}let l=[...t,...e.providers,...a],u;return registerInstrumentationRuntime({forceFlush:()=>settleAll([...o===void 0?[]:[o.forceFlush],...l.map(e=>()=>e.flush?.())]),hooks:createInstrumentationHooks({parallel:e.providers,serialAfter:a,serialBefore:t}),idGenerator:o?.idGenerator??new AgentSpanIdGenerator,instrumentationProviders:e.instrumentationProviders,otelSettings:e.collected.declared?e.collected.settings:void 0,prepareSessionTrace:s,prepareTurnTrace:c,runtimeContextResolvers:e.runtimeContextResolvers,runInContext,shutdown:()=>(u??=settleAll([...o===void 0?[]:[o.shutdown],...l.map(e=>()=>e.shutdown?.())]),u)})}function isSpanProcessor(e){return e!==`auto`}function sessionReleaseProvider(e){let release=async t=>{await Promise.all(e.map(e=>e.releaseSession(t.sessionId)))};return{events:{"session.completed":release,"session.failed":release},name:`eve.session-release`,stateNamespace:`internal:session-release`}}async function settleAll(e){let n=await Promise.allSettled(e.map(async e=>e()));for(let e of n)e.status===`rejected`&&log.warn(`instrumentation drain failed`,{error:formatError(e.reason)})}export{installInstrumentationRuntime};
@@ -1,4 +1,4 @@
1
- import { type InstrumentationRuntime } from "#harness/instrumentation/runtime.js";
1
+ import { type InstrumentationRuntime } from "#instrumentation/runtime.js";
2
2
  import { type TraceCapturePolicy } from "#tracing/otel-declaration.js";
3
3
  /** Zero-config local tracing keeps unclassified HTTP/TUI sessions observable. @internal */
4
4
  export declare const localTracePolicy: TraceCapturePolicy;
@@ -1 +1 @@
1
- import{getInstrumentationRuntime}from"#harness/instrumentation/runtime.js";import{installInstrumentationRuntime}from"#tracing/install-instrumentation-runtime.js";import{collectOtelPipeline,managedOtelIntegration,otel}from"#tracing/otel-declaration.js";import{createLocalTracesProcessor,resolveLocalTracesContent}from"#tracing/local-traces.js";const localTracePolicy=({audience:e})=>e===`public`||e===`unknown`;function installLocalInstrumentationRuntime(t){let n=getInstrumentationRuntime();if(n!==void 0)return n;let r=createLocalTracesProcessor({appRoot:t.appRoot});return installInstrumentationRuntime({collected:collectOtelPipeline([otel({tracePolicy:localTracePolicy}),managedOtelIntegration({...resolveLocalTracesContent(),spanProcessors:[r]})]),frameworkVersion:t.frameworkVersion,providers:[],serviceName:t.serviceName})}export{installLocalInstrumentationRuntime,localTracePolicy};
1
+ import{getInstrumentationRuntime}from"#instrumentation/runtime.js";import{installInstrumentationRuntime}from"#tracing/install-instrumentation-runtime.js";import{collectOtelPipeline,managedOtelIntegration,otel}from"#tracing/otel-declaration.js";import{createLocalTracesProcessor,resolveLocalTracesContent}from"#tracing/local-traces.js";const localTracePolicy=({audience:e})=>e===`public`||e===`unknown`;function installLocalInstrumentationRuntime(t){let n=getInstrumentationRuntime();if(n!==void 0)return n;let r=createLocalTracesProcessor({appRoot:t.appRoot});return installInstrumentationRuntime({collected:collectOtelPipeline([otel({tracePolicy:localTracePolicy}),managedOtelIntegration({...resolveLocalTracesContent(),spanProcessors:[r]})]),frameworkVersion:t.frameworkVersion,providers:[],serviceName:t.serviceName})}export{installLocalInstrumentationRuntime,localTracePolicy};
@@ -78,5 +78,6 @@ export declare function assembleLocalTrace(traceId: string, spans: readonly Loca
78
78
  * span labels. Raw values — callers sanitize for their output surface.
79
79
  */
80
80
  export declare function describeLocalTraceSpan(span: LocalTraceSpan): string[];
81
+ export declare function isAgentTurnSpan(span: LocalTraceSpan): boolean;
81
82
  /** Parses one OTLP/JSON segment file into spans belonging to `expectedTraceId`. */
82
83
  export declare function parseLocalTraceSegment(content: string, expectedTraceId: string): LocalTraceSpan[];
@@ -1 +1 @@
1
- import{readFile,readdir,stat}from"node:fs/promises";import{join}from"node:path";import{resolveLocalTraceSchemaDirectory,resolveLocalTraceSegmentsDirectory}from"#tracing/local-trace-span-processor.js";const TRACE_ID_PATTERN=/^[0-9a-f]{32}$/u,SPAN_FILE_PATTERN=/^[0-9a-f]{16}\.otlp\.json$/u;async function listLocalTraceIds(e){let n;try{n=await readdir(resolveLocalTraceSchemaDirectory(e),{withFileTypes:!0})}catch(e){if(isMissing(e))return[];throw e}return n.filter(e=>e.isDirectory()&&TRACE_ID_PATTERN.test(e.name)).map(e=>e.name)}async function readLocalTraceActivityMs(e,t){try{return(await stat(resolveLocalTraceSegmentsDirectory(e,t))).mtimeMs}catch(e){if(isMissing(e))return;throw e}}async function listLocalTraceSegments(e,n){let r;try{r=await readdir(resolveLocalTraceSegmentsDirectory(e,n),{withFileTypes:!0})}catch(e){if(isMissing(e))return;throw e}return r.filter(e=>e.isFile()&&SPAN_FILE_PATTERN.test(e.name)).map(e=>e.name).sort((e,t)=>e.localeCompare(t))}async function readLocalTraceSegment(t,i,o){let s=join(resolveLocalTraceSegmentsDirectory(t,i),o),c;try{if((await stat(s)).size>8388608)return[];c=await readFile(s,`utf8`)}catch{return[]}return parseLocalTraceSegment(c,i)}async function listLocalTraces(e){let t=[];for(let n of await listLocalTraceIds(e)){let r=await readLocalTrace(e,n).catch(()=>void 0);r!==void 0&&t.push(r)}return t.sort((e,t)=>e.startTimeNs===t.startTimeNs?e.traceId.localeCompare(t.traceId):e.startTimeNs>t.startTimeNs?-1:1)}async function readLocalTrace(e,t){let n=await listLocalTraceSegments(e,t);if(n===void 0)return;let r=new Map;for(let i of n)for(let n of await readLocalTraceSegment(e,t,i))r.has(n.spanId)||r.set(n.spanId,n);if(r.size!==0)return assembleLocalTrace(t,[...r.values()])}function compareLocalTraceSpans(e,t){return e.startTimeNs===t.startTimeNs?e.endTimeNs===t.endTimeNs?e.name.localeCompare(t.name)||e.spanId.localeCompare(t.spanId):e.endTimeNs<t.endTimeNs?-1:1:e.startTimeNs<t.startTimeNs?-1:1}function assembleLocalTrace(e,t){let n=[...t].sort(compareLocalTraceSpans),r=n.map(e=>e.attributes),i=distinctAttributes(r,`agent.session.id`);return{agentName:firstAttribute(r,`agent.name`),endTimeNs:n.reduce((e,t)=>t.endTimeNs>e?t.endTimeNs:e,0n),sessionId:i[0],sessionIds:i,spans:n,startTimeNs:n.reduce((e,t)=>t.startTimeNs<e?t.startTimeNs:e,n[0].startTimeNs),traceId:e}}function describeLocalTraceSpan(e){let t=[],n=stringSpanAttribute(e,`agent.turn.id`),r=valueSpanAttribute(e,`agent.step.index`),i=valueSpanAttribute(e,`agent.step.attempt`),a=stringSpanAttribute(e,`agent.action.kind`),o=stringSpanAttribute(e,`agent.action.name`),s=stringSpanAttribute(e,`agent.model.id`)??stringSpanAttribute(e,`gen_ai.request.model`);return e.name===`agent.turn`&&n!==void 0&&t.push(n),e.name===`agent.step`&&r!==void 0&&t.push(`step ${String(r)}${i===void 0?``:`, attempt ${String(i)}`}`),e.name===`agent.action`&&o!==void 0&&t.push(`${a??`action`}: ${o}`),s!==void 0&&e.name.includes(`do`)&&t.push(`model ${s}`),t}function parseLocalTraceSegment(e,t){try{let n=JSON.parse(e);if(!isRecord(n)||!Array.isArray(n.resourceSpans))return[];let r=[];for(let e of n.resourceSpans)if(!(!isRecord(e)||!Array.isArray(e.scopeSpans)))for(let n of e.scopeSpans){if(!isRecord(n)||!Array.isArray(n.spans))continue;let e=isRecord(n.scope)&&typeof n.scope.name==`string`?n.scope.name:void 0;for(let i of n.spans){let n=parseLocalTraceSpan(i,t,e);n!==void 0&&r.push(n)}}return r}catch{return[]}}function parseLocalTraceSpan(e,t,n){if(!isRecord(e)||e.traceId!==t||typeof e.spanId!=`string`||!/^[0-9a-f]{16}$/u.test(e.spanId)||typeof e.name!=`string`)return;let r=parseNanos(e.startTimeUnixNano),i=parseNanos(e.endTimeUnixNano);if(!(r===void 0||i===void 0||i<r))return{attributes:parseAttributes(e.attributes),endTimeNs:i,events:parseEvents(e.events),kind:typeof e.kind==`number`?e.kind:void 0,name:e.name,parentSpanId:typeof e.parentSpanId==`string`&&/^[0-9a-f]{16}$/u.test(e.parentSpanId)?e.parentSpanId:void 0,scope:n,spanId:e.spanId,startTimeNs:r,statusCode:parseStatusCode(e.status),statusMessage:parseStatusMessage(e.status),traceId:t}}function parseEvents(e){if(!Array.isArray(e))return[];let t=[];for(let n of e){if(!isRecord(n)||typeof n.name!=`string`)continue;let e=parseNanos(n.timeUnixNano);e!==void 0&&t.push({attributes:parseAttributes(n.attributes),name:n.name,timeNs:e})}return t.sort((e,t)=>e.timeNs===t.timeNs?e.name.localeCompare(t.name):e.timeNs<t.timeNs?-1:1)}function parseAttributes(e){if(!Array.isArray(e))return{};let t={};for(let n of e){if(!isRecord(n)||typeof n.key!=`string`||!isRecord(n.value))continue;let e=parseAnyValue(n.value);e!==void 0&&(t[n.key]=e)}return t}function parseAnyValue(e){for(let t of[`stringValue`,`boolValue`,`intValue`,`doubleValue`])if(t in e)return e[t];if(isRecord(e.arrayValue)&&Array.isArray(e.arrayValue.values))return e.arrayValue.values.filter(isRecord).map(e=>parseAnyValue(e))}function parseNanos(e){if(!(typeof e!=`string`&&typeof e!=`number`))try{let t=BigInt(e);return t>=0n&&t<=18446744073709551615n?t:void 0}catch{return}}function parseStatusCode(e){return isRecord(e)?typeof e.code==`number`?e.code:e.code===`STATUS_CODE_ERROR`?2:0:0}function parseStatusMessage(e){if(!(!isRecord(e)||typeof e.message!=`string`))return e.message.length===0?void 0:e.message}function firstAttribute(e,t){for(let n of e){let e=n[t];if(typeof e==`string`&&e.length>0)return e}}function distinctAttributes(e,t){let n=new Set;for(let r of e){let e=r[t];typeof e==`string`&&e.length>0&&n.add(e)}return[...n]}function stringSpanAttribute(e,t){let n=e.attributes[t];return typeof n==`string`?n:void 0}function valueSpanAttribute(e,t){let n=e.attributes[t];return typeof n==`string`||typeof n==`number`?n:void 0}function isMissing(e){return e.code===`ENOENT`}function isRecord(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}export{assembleLocalTrace,compareLocalTraceSpans,describeLocalTraceSpan,listLocalTraceIds,listLocalTraceSegments,listLocalTraces,parseLocalTraceSegment,readLocalTraceActivityMs,readLocalTraceSegment};
1
+ import{readFile,readdir,stat}from"node:fs/promises";import{join}from"node:path";import{resolveLocalTraceSchemaDirectory,resolveLocalTraceSegmentsDirectory}from"#tracing/local-trace-span-processor.js";const TRACE_ID_PATTERN=/^[0-9a-f]{32}$/u,SPAN_FILE_PATTERN=/^[0-9a-f]{16}\.otlp\.json$/u;async function listLocalTraceIds(e){let n;try{n=await readdir(resolveLocalTraceSchemaDirectory(e),{withFileTypes:!0})}catch(e){if(isMissing(e))return[];throw e}return n.filter(e=>e.isDirectory()&&TRACE_ID_PATTERN.test(e.name)).map(e=>e.name)}async function readLocalTraceActivityMs(e,t){try{return(await stat(resolveLocalTraceSegmentsDirectory(e,t))).mtimeMs}catch(e){if(isMissing(e))return;throw e}}async function listLocalTraceSegments(e,n){let r;try{r=await readdir(resolveLocalTraceSegmentsDirectory(e,n),{withFileTypes:!0})}catch(e){if(isMissing(e))return;throw e}return r.filter(e=>e.isFile()&&SPAN_FILE_PATTERN.test(e.name)).map(e=>e.name).sort((e,t)=>e.localeCompare(t))}async function readLocalTraceSegment(t,i,o){let s=join(resolveLocalTraceSegmentsDirectory(t,i),o),c;try{if((await stat(s)).size>8388608)return[];c=await readFile(s,`utf8`)}catch{return[]}return parseLocalTraceSegment(c,i)}async function listLocalTraces(e){let t=[];for(let n of await listLocalTraceIds(e)){let r=await readLocalTrace(e,n).catch(()=>void 0);r!==void 0&&t.push(r)}return t.sort((e,t)=>e.startTimeNs===t.startTimeNs?e.traceId.localeCompare(t.traceId):e.startTimeNs>t.startTimeNs?-1:1)}async function readLocalTrace(e,t){let n=await listLocalTraceSegments(e,t);if(n===void 0)return;let r=new Map;for(let i of n)for(let n of await readLocalTraceSegment(e,t,i))r.has(n.spanId)||r.set(n.spanId,n);if(r.size!==0)return assembleLocalTrace(t,[...r.values()])}function compareLocalTraceSpans(e,t){return e.startTimeNs===t.startTimeNs?e.endTimeNs===t.endTimeNs?e.name.localeCompare(t.name)||e.spanId.localeCompare(t.spanId):e.endTimeNs<t.endTimeNs?-1:1:e.startTimeNs<t.startTimeNs?-1:1}function assembleLocalTrace(e,t){let n=[...t].sort(compareLocalTraceSpans),r=n.map(e=>e.attributes),i=distinctAttributes(r,`agent.session.id`);return{agentName:firstAttribute(r,`agent.name`),endTimeNs:n.reduce((e,t)=>t.endTimeNs>e?t.endTimeNs:e,0n),sessionId:i[0],sessionIds:i,spans:n,startTimeNs:n.reduce((e,t)=>t.startTimeNs<e?t.startTimeNs:e,n[0].startTimeNs),traceId:e}}function describeLocalTraceSpan(e){let t=[],n=stringSpanAttribute(e,`agent.turn.id`),r=valueSpanAttribute(e,`agent.step.index`),i=valueSpanAttribute(e,`agent.step.attempt`),a=stringSpanAttribute(e,`agent.action.kind`),o=stringSpanAttribute(e,`agent.action.name`),s=stringSpanAttribute(e,`agent.model.id`)??stringSpanAttribute(e,`gen_ai.request.model`);return isAgentTurnSpan(e)&&n!==void 0&&t.push(n),e.name===`agent.step`&&r!==void 0&&t.push(`step ${String(r)}${i===void 0?``:`, attempt ${String(i)}`}`),e.name===`agent.action`&&o!==void 0&&t.push(`${a??`action`}: ${o}`),s!==void 0&&e.name.includes(`do`)&&t.push(`model ${s}`),t}function isAgentTurnSpan(e){return e.name===`agent.turn`||stringSpanAttribute(e,`gen_ai.operation.name`)===`invoke_agent`&&stringSpanAttribute(e,`agent.turn.id`)!==void 0}function parseLocalTraceSegment(e,t){try{let n=JSON.parse(e);if(!isRecord(n)||!Array.isArray(n.resourceSpans))return[];let r=[];for(let e of n.resourceSpans)if(!(!isRecord(e)||!Array.isArray(e.scopeSpans)))for(let n of e.scopeSpans){if(!isRecord(n)||!Array.isArray(n.spans))continue;let e=isRecord(n.scope)&&typeof n.scope.name==`string`?n.scope.name:void 0;for(let i of n.spans){let n=parseLocalTraceSpan(i,t,e);n!==void 0&&r.push(n)}}return r}catch{return[]}}function parseLocalTraceSpan(e,t,n){if(!isRecord(e)||e.traceId!==t||typeof e.spanId!=`string`||!/^[0-9a-f]{16}$/u.test(e.spanId)||typeof e.name!=`string`)return;let r=parseNanos(e.startTimeUnixNano),i=parseNanos(e.endTimeUnixNano);if(!(r===void 0||i===void 0||i<r))return{attributes:parseAttributes(e.attributes),endTimeNs:i,events:parseEvents(e.events),kind:typeof e.kind==`number`?e.kind:void 0,name:e.name,parentSpanId:typeof e.parentSpanId==`string`&&/^[0-9a-f]{16}$/u.test(e.parentSpanId)?e.parentSpanId:void 0,scope:n,spanId:e.spanId,startTimeNs:r,statusCode:parseStatusCode(e.status),statusMessage:parseStatusMessage(e.status),traceId:t}}function parseEvents(e){if(!Array.isArray(e))return[];let t=[];for(let n of e){if(!isRecord(n)||typeof n.name!=`string`)continue;let e=parseNanos(n.timeUnixNano);e!==void 0&&t.push({attributes:parseAttributes(n.attributes),name:n.name,timeNs:e})}return t.sort((e,t)=>e.timeNs===t.timeNs?e.name.localeCompare(t.name):e.timeNs<t.timeNs?-1:1)}function parseAttributes(e){if(!Array.isArray(e))return{};let t={};for(let n of e){if(!isRecord(n)||typeof n.key!=`string`||!isRecord(n.value))continue;let e=parseAnyValue(n.value);e!==void 0&&(t[n.key]=e)}return t}function parseAnyValue(e){for(let t of[`stringValue`,`boolValue`,`intValue`,`doubleValue`])if(t in e)return e[t];if(isRecord(e.arrayValue)&&Array.isArray(e.arrayValue.values))return e.arrayValue.values.filter(isRecord).map(e=>parseAnyValue(e))}function parseNanos(e){if(!(typeof e!=`string`&&typeof e!=`number`))try{let t=BigInt(e);return t>=0n&&t<=18446744073709551615n?t:void 0}catch{return}}function parseStatusCode(e){return isRecord(e)?typeof e.code==`number`?e.code:e.code===`STATUS_CODE_ERROR`?2:0:0}function parseStatusMessage(e){if(!(!isRecord(e)||typeof e.message!=`string`))return e.message.length===0?void 0:e.message}function firstAttribute(e,t){for(let n of e){let e=n[t];if(typeof e==`string`&&e.length>0)return e}}function distinctAttributes(e,t){let n=new Set;for(let r of e){let e=r[t];typeof e==`string`&&e.length>0&&n.add(e)}return[...n]}function stringSpanAttribute(e,t){let n=e.attributes[t];return typeof n==`string`?n:void 0}function valueSpanAttribute(e,t){let n=e.attributes[t];return typeof n==`string`||typeof n==`number`?n:void 0}function isMissing(e){return e.code===`ENOENT`}function isRecord(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}export{assembleLocalTrace,compareLocalTraceSpans,describeLocalTraceSpan,isAgentTurnSpan,listLocalTraceIds,listLocalTraceSegments,listLocalTraces,parseLocalTraceSegment,readLocalTraceActivityMs,readLocalTraceSegment};
@@ -1,9 +1,10 @@
1
- import type { PropagatorOrName, SamplerOrName, SpanExporter, SpanProcessor, SpanProcessorOrName } from "#compiled/@vercel/otel/index.js";
1
+ import type { MetricReader, PropagatorOrName, SamplerOrName, SpanExporter, SpanProcessor, SpanProcessorOrName } from "#compiled/@vercel/otel/index.js";
2
2
  import { type InstrumentationProvider } from "#public/instrumentation/provider.js";
3
3
  import type { InstrumentationRuntimeContextInput } from "#public/instrumentation/index.js";
4
4
  import type { JsonObject } from "#shared/json.js";
5
5
  import type { ResolvedContentOptions } from "#tracing/content-attributes.js";
6
- import type { ChannelAudience } from "#shared/channel-audience.js";
6
+ import type { TraceCapturePolicy } from "#shared/trace-policy.js";
7
+ export type { TraceCaptureContext, TraceCapturePolicy, TracePolicyDecision, } from "#shared/trace-policy.js";
7
8
  import { type SpanExportPolicy } from "#tracing/span-export-policy.js";
8
9
  export type { SpanAttributeDecision, SpanExportAttributeValue, SpanExportContext, SpanExportPolicy, SpanExportPredicate, } from "#tracing/span-export-policy.js";
9
10
  export { composeSpanExportPolicies, redactSpanInputs, redactSpanOutputs, } from "#tracing/span-export-policy.js";
@@ -82,25 +83,19 @@ export interface ContentOptions {
82
83
  readonly recordInputs?: boolean;
83
84
  readonly recordOutputs?: boolean;
84
85
  }
85
- export interface TraceCaptureContext {
86
- readonly agentName?: string;
87
- readonly audience: ChannelAudience;
88
- readonly channelType?: string;
89
- }
90
- export type TracePolicyDecision = {
91
- readonly emit: false;
92
- } | {
93
- readonly emit: true;
94
- readonly recordInputs: boolean;
95
- readonly recordOutputs: boolean;
96
- };
97
- export type TraceCapturePolicy = (trace: TraceCaptureContext) => TracePolicyDecision | boolean;
98
- /** Where one `otelIntegration()` sends spans. */
86
+ /** Where one `otelIntegration()` sends spans and metrics. */
99
87
  export interface OtelIntegrationOptions extends ContentOptions {
100
88
  /** Merged into the pipeline in declaration order. */
101
89
  readonly spanProcessors?: readonly SpanProcessor[];
102
90
  /** Wrapped in eve's batching processor and appended after `spanProcessors`. */
103
91
  readonly traceExporter?: SpanExporter;
92
+ /**
93
+ * Metric readers collected into the process's one meter provider in
94
+ * declaration order. Without any, the meter provider is not created and
95
+ * `metrics.getMeter()` returns a no-op meter. Readers come from the app's
96
+ * own `@opentelemetry/sdk-metrics` install.
97
+ */
98
+ readonly metricReaders?: readonly MetricReader[];
104
99
  /**
105
100
  * Contributes runtime context that the AI SDK merges into telemetry spans
106
101
  * for each model call. Child spans inherit the values, so a destination can
@@ -128,6 +123,7 @@ export interface OtelIntegration extends InstrumentationProvider {
128
123
  readonly [OTEL_INTEGRATION]: true;
129
124
  /** @deprecated Content is captured upstream and redacted by destination policies. */
130
125
  readonly content: ResolvedContentOptions;
126
+ readonly metricReaders: readonly MetricReader[];
131
127
  readonly runtimeContext?: (input: InstrumentationRuntimeContextInput) => JsonObject | undefined;
132
128
  readonly spanProcessors: readonly SpanProcessorOrName[];
133
129
  }
@@ -160,6 +156,7 @@ export declare function isOtelIntegration(value: unknown): value is OtelIntegrat
160
156
  /** The one pipeline a process can register. @internal */
161
157
  export interface OtelPipeline {
162
158
  readonly instrumentations?: readonly unknown[];
159
+ readonly metricReaders?: readonly MetricReader[];
163
160
  readonly propagators?: readonly PropagatorOrName[];
164
161
  readonly resource?: Readonly<Record<string, unknown>>;
165
162
  readonly sampler?: SamplerOrName;
@@ -1 +1 @@
1
- import{PROVIDER}from"#public/instrumentation/provider.js";import{composeSpanExportPolicies,composeSpanExportPolicies as composeSpanExportPolicies$1,redactSpanInputs,redactSpanInputs as redactSpanInputs$1,redactSpanOutputs,redactSpanOutputs as redactSpanOutputs$1}from"#tracing/span-export-policy.js";import{batchSpanProcessor}from"#tracing/batch-span-processor.js";import{contentFilteringProcessor}from"#tracing/content-span-processor.js";import{vercelRuntimeSpanProcessor}from"#tracing/vercel-runtime-span-exporter.js";const OTEL_DECLARATION=Symbol.for(`eve.instrumentation.otel`),OTEL_INTEGRATION=Symbol.for(`eve.instrumentation.otel-integration`);function otel(t={}){return{[OTEL_DECLARATION]:!0,[PROVIDER]:!0,options:t}}function otelIntegration(e={}){return createOtelIntegration(e)}function managedOtelIntegration(e={}){return createOtelIntegration(e,e.exportPolicy)}function createOtelIntegration(t,n){let r=t.spanProcessors??[],i=t.traceExporter===void 0?r:[...r,batchSpanProcessor(t.traceExporter)];return{[OTEL_INTEGRATION]:!0,[PROVIDER]:!0,content:resolveContentOptions(t),runtimeContext:t.runtimeContext,spanProcessors:i.map(e=>withExportPolicies(e,legacyContentRedactionPolicy(t),n))}}function agentRunsIntegration(t={}){return{[OTEL_INTEGRATION]:!0,[PROVIDER]:!0,content:resolveContentOptions(t),spanProcessors:[withExportPolicies(vercelRuntimeSpanProcessor(),legacyContentRedactionPolicy(t),t.exportPolicy)]}}function legacyContentRedactionPolicy(e){let t=[];return e.recordInputs===!1&&t.push(redactSpanInputs$1()),e.recordOutputs===!1&&t.push(redactSpanOutputs$1()),t.length===0?void 0:composeSpanExportPolicies$1(...t)}function resolveContentOptions(e){return{recordInputs:e.recordInputs!==!1,recordOutputs:e.recordOutputs!==!1}}function withExportPolicies(e,...t){let n=e;for(let e of t.toReversed())n=contentFilteringProcessor(n,e);return n}function isOtelDeclaration(e){return typeof e==`object`&&!!e&&e[OTEL_DECLARATION]===!0}function isOtelIntegration(e){return typeof e==`object`&&!!e&&e[OTEL_INTEGRATION]===!0}function collectOtelPipeline(e){let t=[],n=[],r,i=!1,a=!1;for(let o of e){if(isOtelIntegration(o)){i=!0,a=!0,t.push(...o.spanProcessors),o.runtimeContext!==void 0&&n.push(o.runtimeContext);continue}if(isOtelDeclaration(o)){if(r!==void 0)throw Error("Instrumentation declares `otel()` more than once. One process has one OpenTelemetry tracer provider, so it has one resource, one sampler, and one propagator set — declare them in a single `otel()`.");i=!0,r=o}}let o=r?.options??{},s={functionId:o.functionId,recordInputs:a,recordOutputs:a,traceChannelRequests:o.traceChannelRequests===!0};return o.tracePolicy!==void 0&&Object.assign(s,{tracePolicy:o.tracePolicy}),{declared:i,pipeline:{instrumentations:o.instrumentations,propagators:o.propagators,resource:o.resource,sampler:o.sampler,spanProcessors:t},runtimeContextResolvers:n,settings:s}}export{agentRunsIntegration,collectOtelPipeline,composeSpanExportPolicies,isOtelDeclaration,isOtelIntegration,managedOtelIntegration,otel,otelIntegration,redactSpanInputs,redactSpanOutputs,resolveContentOptions};
1
+ import{PROVIDER}from"#public/instrumentation/provider.js";import{composeSpanExportPolicies,composeSpanExportPolicies as composeSpanExportPolicies$1,redactSpanInputs,redactSpanInputs as redactSpanInputs$1,redactSpanOutputs,redactSpanOutputs as redactSpanOutputs$1}from"#tracing/span-export-policy.js";import{batchSpanProcessor}from"#tracing/batch-span-processor.js";import{contentFilteringProcessor}from"#tracing/content-span-processor.js";import{vercelRuntimeSpanProcessor}from"#tracing/vercel-runtime-span-exporter.js";const OTEL_DECLARATION=Symbol.for(`eve.instrumentation.otel`),OTEL_INTEGRATION=Symbol.for(`eve.instrumentation.otel-integration`);function otel(t={}){return{[OTEL_DECLARATION]:!0,[PROVIDER]:!0,options:t}}function otelIntegration(e={}){return createOtelIntegration(e)}function managedOtelIntegration(e={}){return createOtelIntegration(e,e.exportPolicy)}function createOtelIntegration(t,n){let r=t.spanProcessors??[],i=t.traceExporter===void 0?r:[...r,batchSpanProcessor(t.traceExporter)];return{[OTEL_INTEGRATION]:!0,[PROVIDER]:!0,content:resolveContentOptions(t),metricReaders:t.metricReaders??[],runtimeContext:t.runtimeContext,spanProcessors:i.map(e=>withExportPolicies(e,legacyContentRedactionPolicy(t),n))}}function agentRunsIntegration(t={}){return{[OTEL_INTEGRATION]:!0,[PROVIDER]:!0,content:resolveContentOptions(t),metricReaders:[],spanProcessors:[withExportPolicies(vercelRuntimeSpanProcessor(),legacyContentRedactionPolicy(t),t.exportPolicy)]}}function legacyContentRedactionPolicy(e){let t=[];return e.recordInputs===!1&&t.push(redactSpanInputs$1()),e.recordOutputs===!1&&t.push(redactSpanOutputs$1()),t.length===0?void 0:composeSpanExportPolicies$1(...t)}function resolveContentOptions(e){return{recordInputs:e.recordInputs!==!1,recordOutputs:e.recordOutputs!==!1}}function withExportPolicies(e,...t){let n=e;for(let e of t.toReversed())n=contentFilteringProcessor(n,e);return n}function isOtelDeclaration(e){return typeof e==`object`&&!!e&&e[OTEL_DECLARATION]===!0}function isOtelIntegration(e){return typeof e==`object`&&!!e&&e[OTEL_INTEGRATION]===!0}function collectOtelPipeline(e){let t=[],n=[],r=[],i,a=!1,o=!1;for(let s of e){if(isOtelIntegration(s)){a=!0,o=!0,t.push(...s.spanProcessors),n.push(...s.metricReaders),s.runtimeContext!==void 0&&r.push(s.runtimeContext);continue}if(isOtelDeclaration(s)){if(i!==void 0)throw Error("Instrumentation declares `otel()` more than once. One process has one OpenTelemetry tracer provider, so it has one resource, one sampler, and one propagator set — declare them in a single `otel()`.");a=!0,i=s}}let s=i?.options??{},c={functionId:s.functionId,recordInputs:o,recordOutputs:o,traceChannelRequests:s.traceChannelRequests===!0};return s.tracePolicy!==void 0&&Object.assign(c,{tracePolicy:s.tracePolicy}),{declared:a,pipeline:{instrumentations:s.instrumentations,metricReaders:n.length>0?n:void 0,propagators:s.propagators,resource:s.resource,sampler:s.sampler,spanProcessors:t},runtimeContextResolvers:r,settings:c}}export{agentRunsIntegration,collectOtelPipeline,composeSpanExportPolicies,isOtelDeclaration,isOtelIntegration,managedOtelIntegration,otel,otelIntegration,redactSpanInputs,redactSpanOutputs,resolveContentOptions};
@@ -1 +1 @@
1
- import{createRequire}from"node:module";import{context,propagation,trace}from"#compiled/@opentelemetry/api/index.js";import{AgentSpanIdGenerator}from"#tracing/agent-span-id-generator.js";import{registerOTel}from"#compiled/@vercel/otel/index.js";const REGISTRATION_SPAN_NAME=`eve.otel.registration`,REPLAY_DEDUPLICATION_LIMIT=1e5,require=createRequire(import.meta.url);var RegistrationMarkerPropagator=class{#e=!1;extract(e){return e}fields(){return[]}inject(){this.#e=!0}isInstalled(){return this.#e=!1,propagation.inject(context.active(),{},{set:()=>{}}),this.#e}},PrivateSpanFilteringProcessor=class{endedSpans=new Set;forwardedSpans=new Set;pendingByParent=new Map;processors;startedSpans=new Set;constructor(e){this.processors=e}async forceFlush(){await Promise.all(this.processors.map(e=>e.forceFlush()))}onEnd(e){if(isRegistrationSpan(e))return;let t=spanIdentity(e);if(t!==void 0){if(this.endedSpans.has(t))return;if(this.endedSpans.size>=REPLAY_DEDUPLICATION_LIMIT){let e=this.endedSpans.values().next().value;e!==void 0&&this.endedSpans.delete(e)}this.endedSpans.add(t)}let n=parentIdentity(e);if(n!==void 0&&this.startedSpans.has(n)&&!this.forwardedSpans.has(n)){let t=this.pendingByParent.get(n)??[];t.push(e),this.pendingByParent.set(n,t);return}this.forward(e,t)}onStart(e,t){if(isRegistrationSpan(e))return;let n=spanIdentity(e);n!==void 0&&addBounded(this.startedSpans,n);for(let n of this.processors)n.onStart(e,t)}async shutdown(){await Promise.all(this.processors.map(e=>e.shutdown()))}forward(e,t){for(let t of this.processors)t.onEnd(e);if(t===void 0)return;addBounded(this.forwardedSpans,t);let n=this.pendingByParent.get(t);if(n!==void 0){this.pendingByParent.delete(t);for(let e of n)this.forward(e,spanIdentity(e))}}};function registerOtelPipeline(e){let{pipeline:t}=e,n=captureOptionalPeerTracerProxy(),r=new AgentSpanIdGenerator,i=new RegistrationMarkerPropagator,a={attributes:t.resource,autoDetectResources:!1,idGenerator:r,instrumentations:t.instrumentations??[],propagators:[...t.propagators??[`auto`],i],serviceName:e.serviceName,spanProcessors:t.spanProcessors.map(e=>isSpanProcessor(e)?new PrivateSpanFilteringProcessor([e]):e)};registerOTel(t.sampler===void 0?a:{...a,traceSampler:t.sampler});let o=globalTracerUses(r),s=i.isInstalled();if((!o||!s)&&rollbackRegistration({ownsPropagator:s,ownsTracer:o}),!o)throw Error("eve could not register OpenTelemetry because another runtime already owns the global tracer provider. Remove the other `registerOTel` call, or move its exporters into eve's `otelIntegration({ spanProcessors: [...] })`.");if(!s)throw Error("eve could not register OpenTelemetry because another runtime already owns the global propagator. Remove the other global propagator registration and declare propagators through eve's `otel()` instead.");let c=runtimeTracerProvider();if(typeof c.forceFlush!=`function`||typeof c.shutdown!=`function`)throw rollbackRegistration({ownsPropagator:s,ownsTracer:o}),Error(`The registered OpenTelemetry tracer provider has no lifecycle methods.`);return n?.setDelegate(c),{forceFlush:()=>c.forceFlush(),idGenerator:r,shutdown:()=>c.shutdown()}}function captureOptionalPeerTracerProxy(){try{let e=require("@opentelemetry/api"),t=e.trace.getTracerProvider();return t instanceof e.ProxyTracerProvider?t:void 0}catch{return}}function rollbackRegistration(e){if(e.ownsPropagator&&propagation.disable(),!e.ownsTracer)return;let t=runtimeTracerProvider();if(typeof t.shutdown==`function`)try{t.shutdown().catch(()=>{})}catch{}trace.disable()}function runtimeTracerProvider(){let e=trace.getTracerProvider();return e.getDelegate?.()??e}function globalTracerUses(e){let t=e.allocateSpanId();return e.withSpanId(t,()=>trace.getTracer(`eve.registration`).startSpan(REGISTRATION_SPAN_NAME)).spanContext().spanId===t}function isRegistrationSpan(e){return typeof e==`object`&&!!e&&`name`in e&&e.name===REGISTRATION_SPAN_NAME}function spanIdentity(e){if(typeof e!=`object`||!e||!(`spanContext`in e)||typeof e.spanContext!=`function`)return;let t=e.spanContext();return typeof t.traceId==`string`&&typeof t.spanId==`string`?`${t.traceId}:${t.spanId}`:void 0}function parentIdentity(e){if(typeof e!=`object`||!e||!(`parentSpanContext`in e))return;let t=e.parentSpanContext;return typeof t?.traceId==`string`&&typeof t.spanId==`string`?`${t.traceId}:${t.spanId}`:void 0}function addBounded(e,t){if(e.size>=REPLAY_DEDUPLICATION_LIMIT){let t=e.values().next().value;t!==void 0&&e.delete(t)}e.add(t)}function isSpanProcessor(e){return e!==`auto`}export{registerOtelPipeline};
1
+ import{createRequire}from"node:module";import{context,propagation,trace}from"#compiled/@opentelemetry/api/index.js";import{AgentSpanIdGenerator}from"#tracing/agent-span-id-generator.js";import{registerOTel}from"#compiled/@vercel/otel/index.js";const REGISTRATION_SPAN_NAME=`eve.otel.registration`,REPLAY_DEDUPLICATION_LIMIT=1e5,require=createRequire(import.meta.url);var RegistrationMarkerPropagator=class{#e=!1;extract(e){return e}fields(){return[]}inject(){this.#e=!0}isInstalled(){return this.#e=!1,propagation.inject(context.active(),{},{set:()=>{}}),this.#e}},PrivateSpanFilteringProcessor=class{endedSpans=new Set;forwardedSpans=new Set;pendingByParent=new Map;processors;startedSpans=new Set;constructor(e){this.processors=e}async forceFlush(){await Promise.all(this.processors.map(e=>e.forceFlush()))}onEnd(e){if(isRegistrationSpan(e))return;let t=spanIdentity(e);if(t!==void 0){if(this.endedSpans.has(t))return;if(this.endedSpans.size>=REPLAY_DEDUPLICATION_LIMIT){let e=this.endedSpans.values().next().value;e!==void 0&&this.endedSpans.delete(e)}this.endedSpans.add(t)}let n=parentIdentity(e);if(n!==void 0&&this.startedSpans.has(n)&&!this.forwardedSpans.has(n)){let t=this.pendingByParent.get(n)??[];t.push(e),this.pendingByParent.set(n,t);return}this.forward(e,t)}onStart(e,t){if(isRegistrationSpan(e))return;let n=spanIdentity(e);n!==void 0&&addBounded(this.startedSpans,n);for(let n of this.processors)n.onStart(e,t)}async shutdown(){await Promise.all(this.processors.map(e=>e.shutdown()))}forward(e,t){for(let t of this.processors)t.onEnd(e);if(t===void 0)return;addBounded(this.forwardedSpans,t);let n=this.pendingByParent.get(t);if(n!==void 0){this.pendingByParent.delete(t);for(let e of n)this.forward(e,spanIdentity(e))}}};function registerOtelPipeline(e){let{pipeline:t}=e,n=captureOptionalPeerTracerProxy(),r=new AgentSpanIdGenerator,i=new RegistrationMarkerPropagator,a={attributes:t.resource,autoDetectResources:!1,idGenerator:r,instrumentations:t.instrumentations??[],metricReaders:t.metricReaders,propagators:[...t.propagators??[`auto`],i],serviceName:e.serviceName,spanProcessors:t.spanProcessors.map(e=>isSpanProcessor(e)?new PrivateSpanFilteringProcessor([e]):e)};registerOTel(t.sampler===void 0?a:{...a,traceSampler:t.sampler});let o=globalTracerUses(r),s=i.isInstalled();if((!o||!s)&&rollbackRegistration({ownsPropagator:s,ownsTracer:o}),!o)throw Error("eve could not register OpenTelemetry because another runtime already owns the global tracer provider. Remove the other `registerOTel` call, or move its exporters into eve's `otelIntegration({ spanProcessors: [...] })`.");if(!s)throw Error("eve could not register OpenTelemetry because another runtime already owns the global propagator. Remove the other global propagator registration and declare propagators through eve's `otel()` instead.");let c=runtimeTracerProvider();if(typeof c.forceFlush!=`function`||typeof c.shutdown!=`function`)throw rollbackRegistration({ownsPropagator:s,ownsTracer:o}),Error(`The registered OpenTelemetry tracer provider has no lifecycle methods.`);return n?.setDelegate(c),{forceFlush:()=>c.forceFlush(),idGenerator:r,shutdown:()=>c.shutdown()}}function captureOptionalPeerTracerProxy(){try{let e=require("@opentelemetry/api"),t=e.trace.getTracerProvider();return t instanceof e.ProxyTracerProvider?t:void 0}catch{return}}function rollbackRegistration(e){if(e.ownsPropagator&&propagation.disable(),!e.ownsTracer)return;let t=runtimeTracerProvider();if(typeof t.shutdown==`function`)try{t.shutdown().catch(()=>{})}catch{}trace.disable()}function runtimeTracerProvider(){let e=trace.getTracerProvider();return e.getDelegate?.()??e}function globalTracerUses(e){let t=e.allocateSpanId();return e.withSpanId(t,()=>trace.getTracer(`eve.registration`).startSpan(REGISTRATION_SPAN_NAME)).spanContext().spanId===t}function isRegistrationSpan(e){return typeof e==`object`&&!!e&&`name`in e&&e.name===REGISTRATION_SPAN_NAME}function spanIdentity(e){if(typeof e!=`object`||!e||!(`spanContext`in e)||typeof e.spanContext!=`function`)return;let t=e.spanContext();return typeof t.traceId==`string`&&typeof t.spanId==`string`?`${t.traceId}:${t.spanId}`:void 0}function parentIdentity(e){if(typeof e!=`object`||!e||!(`parentSpanContext`in e))return;let t=e.parentSpanContext;return typeof t?.traceId==`string`&&typeof t.spanId==`string`?`${t.traceId}:${t.spanId}`:void 0}function addBounded(e,t){if(e.size>=REPLAY_DEDUPLICATION_LIMIT){let t=e.values().next().value;t!==void 0&&e.delete(t)}e.add(t)}function isSpanProcessor(e){return e!==`auto`}export{registerOtelPipeline};
@@ -1,17 +1,4 @@
1
- import type { ChannelAudience } from "#shared/channel-audience.js";
2
- import { type InstrumentationDecision } from "#shared/instrumentation-decision.js";
3
- import type { TraceCapturePolicy, TracePolicyDecision } from "#tracing/otel-declaration.js";
1
+ export { resolveTracePolicy, resolveTracePolicyDecision } from "#shared/trace-policy.js";
4
2
  export declare function isSampledTrace(context: {
5
3
  readonly traceFlags: number;
6
4
  }): boolean;
7
- export declare function evaluateTracePolicy(policy: TraceCapturePolicy | undefined, trace: {
8
- readonly agentName?: string;
9
- readonly audience: ChannelAudience;
10
- readonly channelType?: string;
11
- }): boolean;
12
- export declare function resolveTracePolicy(policy: TraceCapturePolicy | undefined, trace: {
13
- readonly agentName?: string;
14
- readonly audience: ChannelAudience;
15
- readonly channelType?: string;
16
- }): InstrumentationDecision;
17
- export declare function resolveTracePolicyDecision(decision: TracePolicyDecision | boolean, audience: ChannelAudience): InstrumentationDecision;
@@ -1 +1 @@
1
- import{shouldCaptureInstrumentationContent}from"#shared/instrumentation-content.js";import{DROP_INSTRUMENTATION}from"#shared/instrumentation-decision.js";function isSampledTrace(e){return(e.traceFlags&1)==1}function evaluateTracePolicy(e,t){return resolveTracePolicy(e,t).action===`record`}function resolveTracePolicy(e,n){try{let t=e?.({agentName:n.agentName,audience:n.audience,channelType:n.channelType});return resolveTracePolicyDecision(t??{emit:!0,recordInputs:n.audience===`public`,recordOutputs:n.audience===`public`},n.audience)}catch{return DROP_INSTRUMENTATION}}function resolveTracePolicyDecision(n,r){if(n===!1)return DROP_INSTRUMENTATION;if(n===!0){let t=shouldCaptureInstrumentationContent(r);return{action:`record`,recordInputs:t,recordOutputs:t}}return n.emit?{action:`record`,recordInputs:n.recordInputs,recordOutputs:n.recordOutputs}:DROP_INSTRUMENTATION}export{evaluateTracePolicy,isSampledTrace,resolveTracePolicy,resolveTracePolicyDecision};
1
+ import{resolveTracePolicy,resolveTracePolicyDecision}from"#shared/trace-policy.js";function isSampledTrace(e){return(e.traceFlags&1)==1}export{isSampledTrace,resolveTracePolicy,resolveTracePolicyDecision};
@@ -1,10 +1,10 @@
1
1
  ---
2
2
  title: "Slack"
3
- description: "Reach your agent from Slack app mentions and DMs with Vercel Connect-managed credentials, threaded replies, and interactive buttons."
3
+ description: "Reach your agent from Slack app mentions, DMs, slash commands, and interactive callbacks."
4
4
  type: integration
5
5
  ---
6
6
 
7
- The Slack channel puts your agent inside a workspace. It answers `@mentions` and DMs, replies in threads, shows typing indicators, and turns human-in-the-loop (HITL) prompts into buttons. [Vercel Connect](https://vercel.com/kb/guide/vercel-connect) is the recommended setup: it manages the Slack bot token, verifies inbound requests, supports token rotation and multiple workspace installations, and forwards events to your agent without copying Slack secrets into your project environment. If you cannot use Vercel Connect, you can provide the bot token and signing secret through environment variables. See [Channels](./overview) for the contract this builds on.
7
+ The Slack channel puts your agent inside a workspace. It handles `@mentions`, DMs, slash commands, shortcuts, and interactive callbacks; replies in threads; shows typing indicators; and turns human-in-the-loop (HITL) prompts into buttons. [Vercel Connect](https://vercel.com/kb/guide/vercel-connect) is the recommended setup: it manages the Slack bot token, verifies inbound requests, supports token rotation and multiple workspace installations, and forwards events to your agent without copying Slack secrets into your project environment. If you cannot use Vercel Connect, you can provide the bot token and signing secret through environment variables. See [Channels](./overview) for the contract this builds on.
8
8
 
9
9
  ## Add the channel
10
10
 
@@ -57,7 +57,8 @@ This option does not create or install the Slack app. Configure it in [Slack app
57
57
  2. Install or reinstall the app, copy its Bot User OAuth Token to `SLACK_BOT_TOKEN`, and copy the signing secret from **Basic Information** to `SLACK_SIGNING_SECRET`.
58
58
  3. Set both values in `.env.local` for local development or in the target runtime's environment, then start or deploy the agent at a public URL.
59
59
  4. Under **Event Subscriptions**, set the Request URL to `https://your-agent.example/eve/v1/slack`. Subscribe to `app_mention`, plus `message.im` for DMs.
60
- 5. Under **Interactivity & Shortcuts**, use the same Request URL when the agent uses buttons or HITL prompts.
60
+ 5. Under **Interactivity & Shortcuts**, use the same Request URL when the agent uses buttons, shortcuts, or HITL prompts.
61
+ 6. Under **Slash Commands**, create each command your app handles and use the same Request URL.
61
62
 
62
63
  Add the matching message events and history scopes if you also want unmentioned thread replies, as described in [Continue conversations without repeated mentions](#continue-conversations-without-repeated-mentions).
63
64
 
@@ -156,6 +157,25 @@ Only the first available handler runs. A message hook returning `null` drops the
156
157
 
157
158
  `onInteraction(action, ctx)` separately handles user-owned `block_actions` callbacks. eve-owned HITL buttons and modal submissions go through `onInputResponse(ctx, submission)` instead. eve attaches the triggering Slack user id to the same model message as its text, preserving speaker attribution without profile lookups.
158
159
 
160
+ Use `onShortcut(shortcut, ctx)` for message shortcuts and global shortcuts configured under **Interactivity & Shortcuts** in your Slack app. The handler receives the shortcut callback ID, user, workspace, and trigger ID. Message shortcuts also include the selected message and channel. The context exposes workspace-scoped Slack API access because global shortcuts have no channel or message. eve acknowledges shortcut requests immediately and keeps their handlers alive in the background.
161
+
162
+ Use `onSlashCommand(command, ctx)` for commands configured under **Slash Commands** in your Slack app. The handler receives the command name, argument text, invoking user, channel, workspace, trigger ID, and response URL. It also receives workspace-scoped Slack API access. eve acknowledges slash commands immediately with an empty `200 OK` response and keeps the handler alive in the background:
163
+
164
+ ```ts title="agent/channels/slack.ts"
165
+ import { slackChannel } from "eve/channels/slack";
166
+
167
+ export default slackChannel({
168
+ async onSlashCommand(command, ctx) {
169
+ if (command.command !== "/ask") return;
170
+ await ctx.slack.request("chat.postEphemeral", {
171
+ channel: command.channelId,
172
+ user: command.user.id,
173
+ text: `Received: ${command.text}`,
174
+ });
175
+ },
176
+ });
177
+ ```
178
+
159
179
  The model-visible `<slack_message>` includes `sender_id`, `bot_user_id` when available, and the `is_mentioned` value from `ctx.isBotMentioned()`. Its `<content>` keeps `<@USER_ID>` intact while converting other mrkdwn to Markdown; routing is unchanged.
160
180
 
161
181
  When a Slack message shares another Slack message, eve extracts the forwarded message body from Slack's message-unfurl attachment and includes it in `<content>`. The original top-level comment, when present, remains alongside the forwarded content.
@@ -360,11 +380,11 @@ const session = await ctx.resolveSession({ target });
360
380
 
361
381
  `onEvent` is the raw fallback after the message hooks. If an event is not claimed by `onAppMention`, `onDirectMessage`, or `onMessage`, an authored `onEvent` receives it; otherwise eve applies the built-in mention/DM default or ignores it.
362
382
 
363
- `onEvent` covers JSON `event_callback` deliveries only. URL verification, slash commands, and interactive payloads do not reach it. Add every desired event and required OAuth scope to the Slack app's Event Subscriptions configuration; eve can only handle events Slack sends.
383
+ `onEvent` covers JSON `event_callback` deliveries only. URL verification, slash commands, and interactive payloads do not reach it. Slash commands reach `onSlashCommand`; add every desired event and required OAuth scope to the Slack app's Event Subscriptions configuration so other callbacks reach their handlers.
364
384
 
365
385
  ### Slack API calls outside a handler
366
386
 
367
- Inside inbound webhook handlers such as `onAppMention`, `onEvent`, `onInteraction`, and `onInputResponse`, `ctx.slack.request(operation, body)` is the raw-API escape hatch. Inside an `events` handler, use `channel.slack.request(...)` instead. Outside those contexts there is no handle — a schedule resolving reactions on old
387
+ Inside inbound webhook handlers such as `onAppMention`, `onEvent`, `onInteraction`, `onShortcut`, `onSlashCommand`, and `onInputResponse`, `ctx.slack.request(operation, body)` is the raw-API escape hatch. Inside an `events` handler, use `channel.slack.request(...)` instead. Outside those contexts there is no handle — a schedule resolving reactions on old
368
388
  messages, for example, has no inbound Slack request. For that, call the
369
389
  same primitive the handle uses directly:
370
390
 
@@ -6,7 +6,7 @@ url: /connections
6
6
 
7
7
  A connection wires an agent into an external server you don't author, either an MCP server (Linear, GitHub, a warehouse) or any HTTP API with an OpenAPI document. eve handles the parts you'd otherwise hand-roll, discovering the remote tools, surfacing them to the model, and brokering auth. To instead let eve communicate with users through an external service, use a [channel](/docs/channels/overview).
8
8
 
9
- Connections live under `agent/connections/`. The runtime name comes from the filename, so `agent/connections/linear.ts` registers as `"linear"`. The model never sees a connection's URL or credentials. It discovers tools through the built-in `connection_search` and calls them by their qualified name, `<connection>__<tool>` (e.g. `linear__list_issues`).
9
+ Connections live under `agent/connections/`. A static connection's runtime name comes from the filename, so `agent/connections/linear.ts` registers as `"linear"`. A dynamic connection file can instead resolve a caller-specific connection set at session or turn boundaries. The model never sees a connection's URL or credentials. It discovers tools through the built-in `connection_search` and calls them by their qualified name, `<connection>__<tool>` (e.g. `linear__list_issues`).
10
10
 
11
11
  ## MCP connections
12
12
 
@@ -20,6 +20,44 @@ Use an OpenAPI connection when the service exposes an OpenAPI 3.x document. eve
20
20
 
21
21
  Read [OpenAPI connections](/docs/connections/openapi) for `defineOpenAPIConnection`, `baseUrl`, and operation filters.
22
22
 
23
+ ## Dynamic connections
24
+
25
+ Use `defineDynamic` when the connection set depends on the authenticated user,
26
+ tenant, or another runtime lookup. Import it with the connection factories from
27
+ `eve/connections`, then return one connection definition, a map of definitions,
28
+ or `null` from `session.started` or `turn.started`.
29
+
30
+ ```ts title="agent/connections/accounts.ts"
31
+ import { defineDynamic, defineMcpClientConnection } from "eve/connections";
32
+ import { listAccounts } from "../lib/accounts";
33
+
34
+ export default defineDynamic({
35
+ events: {
36
+ "session.started": async (_event, ctx) => {
37
+ const accounts = await listAccounts(ctx.session.auth.current);
38
+ return Object.fromEntries(
39
+ accounts.map((account) => [
40
+ account.slug,
41
+ defineMcpClientConnection({
42
+ url: account.mcpUrl,
43
+ description: account.description,
44
+ instanceKey: account.id,
45
+ auth: account.auth,
46
+ }),
47
+ ]),
48
+ );
49
+ },
50
+ },
51
+ });
52
+ ```
53
+
54
+ For a single returned definition, the filename supplies the connection name.
55
+ For a map, each bare map key becomes a connection name. Every entry must use
56
+ `defineMcpClientConnection` or `defineOpenAPIConnection`. Authenticated entries
57
+ must also set `instanceKey` to a stable, non-secret account or tenant identifier.
58
+ See [Dynamic capabilities](/docs/guides/dynamic-capabilities#dynamic-connections)
59
+ for event scope, naming, conflicts, and durable recovery behavior.
60
+
23
61
  ## Static-token auth
24
62
 
25
63
  `getToken` returns a `TokenResult` (`{ token, expiresAt? }`), and eve sends it as `Authorization: Bearer <token>` on every request. Because it runs on each connection attempt, you can mint a fresh token from wherever you keep secrets, including an env var, a secrets manager, an internal vault, or your own OAuth exchange:
@@ -245,7 +283,13 @@ export default defineMcpClientConnection({
245
283
 
246
284
  Drop `resume` when the provider keeps flow state server-side, so nothing has to cross the step boundary.
247
285
 
248
- `displayName` is presentation-only. The connection's path-derived name still keys the authorization flow, token cache, and callback URL. You can also set `displayName` on the `auth` definition itself (e.g. `auth: { ...connect("salesforce/myagent"), displayName: "Salesforce" }`); that definition-level value wins over one the strategy stamps on the challenge, and channels fall back to title-casing the connection name when neither is set.
286
+ `displayName` is presentation-only. The connection's resolved name still keys
287
+ the callback URL, while eve scopes token caching and authorization completion
288
+ to the opaque resolved instance identity. You can also set `displayName` on the
289
+ `auth` definition itself (e.g.
290
+ `auth: { ...connect("salesforce/myagent"), displayName: "Salesforce" }`); that
291
+ definition-level value wins over one the strategy stamps on the challenge, and
292
+ channels fall back to title-casing the connection name when neither is set.
249
293
 
250
294
  ### Signaling authorization state
251
295
 
@@ -39,7 +39,7 @@ An extension uses the same file conventions as an agent for its contributions:
39
39
  lib/http.ts
40
40
  ```
41
41
 
42
- Each listed slot accepts the same authored forms as its agent counterpart. Static and dynamic tools, skills, and instructions all work in an extension: `extension/instructions.ts` is as valid as `extension/instructions.md`, and `extension/tools/` can contain `defineDynamic(...)`.
42
+ Each listed slot accepts the same authored forms as its agent counterpart. Static and dynamic tools, connections, skills, and instructions all work in an extension: `extension/instructions.ts` is as valid as `extension/instructions.md`, and `extension/connections/` can contain `defineDynamic(...)`.
43
43
 
44
44
  Names come from paths, so call the tool `search`, not `crm_search`; the consumer's mount adds the `crm__` prefix. The same prefix applies to channel, schedule, and parent-visible subagent IDs, while channel route paths and schedule cron expressions stay unchanged. Keep shared code in `extension/lib/`.
45
45
 
@@ -342,7 +342,7 @@ At build time, eve checks the extension's generated capability metadata. If the
342
342
 
343
343
  - [Integrations](/integrations): browse ready-to-install extensions using the Extensions filter
344
344
  - [Tools](/docs/tools): static tools, approval, and tool output
345
- - [Dynamic capabilities](/docs/guides/dynamic-capabilities): dynamic tools, skills, and instructions
345
+ - [Dynamic capabilities](/docs/guides/dynamic-capabilities): dynamic connections, tools, skills, and instructions
346
346
  - [Instructions](/docs/instructions): static and TypeScript instructions
347
347
  - [Skills](/docs/skills): package procedures and supporting files
348
348
  - [Connections](/docs/connections): integrate external services
@@ -106,7 +106,7 @@ Each path under `agent/` has a specific purpose. Root agents can use every path
106
106
  | `instructions.md` / `instructions.ts` / `instructions/` | Base system prompt | Optional | A flat file or directory of `.md` and `.ts` files. Static sources compose at build time. Dynamic sources resolve at runtime. Required on the root, optional on subagents. |
107
107
  | `instrumentation.ts` | Telemetry config | No | OTel exporter and AI SDK span settings, auto-discovered and run before agent code. Root-only. |
108
108
  | `channels/` | HTTP and messaging entry points | No | Root-only. |
109
- | `connections/` | External MCP and OpenAPI services | Yes | One connection per file; its name comes from the filename. |
109
+ | `connections/` | External MCP and OpenAPI services | Yes | Static files define one path-named connection. Dynamic files can resolve a caller-specific connection set at runtime. |
110
110
  | `hooks/` | Lifecycle and stream-event subscribers | Yes | Module-backed only. Recursive directories are supported. |
111
111
  | `skills/` | On-demand procedures and capability packs | Yes | Flat markdown, module-backed skills, or packaged skills. Runtime files are seeded under `$HOME/.agents/skills/`, with `/workspace/skills/` as a fallback. |
112
112
  | `lib/` | Shared authored helper code | Yes | Import-only; not mounted into the workspace. |
@@ -1,9 +1,9 @@
1
1
  ---
2
2
  title: "Dynamic Capabilities"
3
- description: "Resolve models, subagents, tools, skills, and instructions at runtime with defineDynamic resolver events."
3
+ description: "Resolve models, subagents, connections, tools, skills, and instructions at runtime with defineDynamic resolver events."
4
4
  ---
5
5
 
6
- `defineDynamic` resolves the model, subagents, tools, skills, and instructions at runtime from a session event instead of declaring them up front. Reach for it when the right capability isn't known until the session starts, because it hinges on who the caller is, what tenant they belong to, feature flags, or external data. The [subagents](../subagents), [tools](../tools), [skills](../skills), and [instructions](../instructions) guides each point here for their dynamic form.
6
+ `defineDynamic` resolves the model, subagents, connections, tools, skills, and instructions at runtime from a session event instead of declaring them up front. Reach for it when the right capability isn't known until the session starts, because it hinges on who the caller is, what tenant they belong to, feature flags, or external data. The [subagents](../subagents), [connections](../connections), [tools](../tools), [skills](../skills), and [instructions](../instructions) guides each point here for their dynamic form.
7
7
 
8
8
  eve evaluates a dynamic definition module once during compilation to classify and validate it, then retains that module as a runtime entry so its event handlers can run. Its top-level code therefore runs in both phases; keep caller-specific work inside the handlers. See [Authored module lifecycle](../reference/typescript-api#authored-module-lifecycle).
9
9
 
@@ -20,8 +20,8 @@ full contract.
20
20
 
21
21
  Dynamic models do not compile a default model or model metadata. When a
22
22
  resolver first selects a model, eve normalizes the selection and resolves any
23
- omitted context-window metadata from the AI Gateway catalog. Dynamic tools,
24
- skills, instructions, and subagents may return `null` to omit a capability.
23
+ omitted context-window metadata from the AI Gateway catalog. Dynamic connections,
24
+ tools, skills, instructions, and subagents may return `null` to omit a capability.
25
25
 
26
26
  ### Route image inputs to a vision model
27
27
 
@@ -132,6 +132,91 @@ availability as capability composition, not as the only authorization
132
132
  boundary: sensitive child tools still need their own authorization and
133
133
  approval checks.
134
134
 
135
+ ## Dynamic connections
136
+
137
+ Use a dynamic connection when the available MCP servers or OpenAPI services
138
+ depend on the authenticated caller. A handler returns one
139
+ `defineMcpClientConnection(...)` or `defineOpenAPIConnection(...)`, a map of
140
+ connection definitions, or `null`. Wrap every returned connection in its
141
+ protocol helper. Connection resolvers receive `ctx.session` and
142
+ `ctx.channel.kind`; they do not receive conversation messages, delivery
143
+ payloads, tool inputs, model outputs, continuation tokens, or free-form channel
144
+ metadata. Select accounts and endpoints from authenticated session identity or
145
+ application-owned data.
146
+
147
+ This example exposes one MCP connection for each cloud account enabled for the
148
+ current user:
149
+
150
+ ```ts title="agent/connections/accounts.ts"
151
+ import { defineDynamic, defineMcpClientConnection } from "eve/connections";
152
+ import { listEnabledAccounts, mintAccountToken } from "../lib/accounts";
153
+
154
+ export default defineDynamic({
155
+ events: {
156
+ "session.started": async (_event, ctx) => {
157
+ const principal = ctx.session.auth.current;
158
+ if (principal?.principalType !== "user") return null;
159
+
160
+ const accounts = await listEnabledAccounts(principal);
161
+ return Object.fromEntries(
162
+ accounts.map((account) => [
163
+ account.slug,
164
+ defineMcpClientConnection({
165
+ url: "https://mcp.cloud.example.com",
166
+ description: `${account.label} (${account.accountId})`,
167
+ instanceKey: account.accountId,
168
+ auth: {
169
+ principalType: "user",
170
+ getToken: ({ principal }) => mintAccountToken(principal, account),
171
+ },
172
+ }),
173
+ ]),
174
+ );
175
+ },
176
+ },
177
+ });
178
+ ```
179
+
180
+ The returned definitions use the same auth, headers, filtering, provided
181
+ arguments, and approval options as static [MCP](../connections/mcp) and
182
+ [OpenAPI](../connections/openapi) connections. Each resolved connection joins
183
+ the per-step connection registry, appears in `connection_search`, and exposes
184
+ discovered tools as `<connection>__<tool>`.
185
+
186
+ Set `instanceKey` on every authenticated dynamic connection. Use a stable,
187
+ non-secret account or tenant identifier, and change it whenever the endpoint,
188
+ account, or auth provider changes. eve hashes the value before storing the
189
+ resolved instance identity in durable authorization state. If a parked sign-in
190
+ callback resumes after the resolver selects a different instance, eve rejects
191
+ the callback instead of passing it to the new connection or reusing its token.
192
+
193
+ ### Naming and conflicts
194
+
195
+ | Return shape | File | Connection name(s) |
196
+ | ----------------------------- | ------------------------------- | ----------------------- |
197
+ | single connection definition | `agent/connections/accounts.ts` | `accounts` |
198
+ | map `{ production, staging }` | `agent/connections/accounts.ts` | `production`, `staging` |
199
+
200
+ A map key must be a legal connection name: lowercase ASCII letters, digits,
201
+ and dashes, starting with a letter, up to 64 characters. Map keys are bare;
202
+ eve does not prefix them with the file slug. A dynamic connection overrides a
203
+ same-named static connection. Two effective dynamic resolvers cannot emit the
204
+ same name; namespace one map key to remove the ambiguity.
205
+
206
+ ### Events and recovery
207
+
208
+ Dynamic connections support `session.started` and `turn.started`. A turn result
209
+ replaces that file's session result for the turn, including when the turn
210
+ handler returns `null`. A throwing or invalid handler fails the lifecycle
211
+ without rebuilding the registry, so a static connection shadowed by the
212
+ dynamic result cannot reappear as a fallback.
213
+
214
+ eve may run the active session and turn handlers again when a parked turn
215
+ resumes or a durable step retries. This rebuilds live auth, header, approval,
216
+ and provided-argument callbacks without serializing them into workflow state.
217
+ Keep connection resolvers idempotent, and keep external side effects outside
218
+ the handler.
219
+
135
220
  ## Dynamic tools
136
221
 
137
222
  Pass `defineDynamic` an `events` object whose handlers return either a single `defineTool(...)`, a `Record<string, defineTool(...)>`, or `null` for no tools. Wrap every entry in `defineTool()`. eve records durable descriptors for `execute`, approval request and response policies, and `toModelOutput`, so a parked call can reconstruct the same callbacks in a fresh process.
@@ -189,7 +274,7 @@ A single return produces one tool named after the file slug, identical to a stat
189
274
 
190
275
  ### Conflicts
191
276
 
192
- A dynamic tool or skill whose name matches an **authored** one **overrides** it — a per-caller resolver can replace a built-in by name. Two **dynamic** resolvers emitting the same name is a genuine ambiguity and throws; namespace one of the keys manually to resolve it.
277
+ A dynamic connection, tool, or skill whose name matches an **authored** one **overrides** it — a per-caller resolver can replace a static capability by name. Two **dynamic** resolvers of the same capability type emitting the same name is a genuine ambiguity and throws; namespace one of the keys manually to resolve it.
193
278
 
194
279
  ### Events
195
280
 
@@ -310,6 +395,7 @@ Dynamic system content that changes frequently can reduce provider prompt-cache
310
395
  ## What to read next
311
396
 
312
397
  - Conditionally expose a specialist → [Subagents](../subagents)
398
+ - Resolve caller-specific external services → [Connections](../connections)
313
399
  - The static tool basics this builds on → [Tools](../tools)
314
400
  - The built-in tools and how to override them → [Built-in tools](../concepts/built-in-tools)
315
401
  - Authenticate a tool or connection to an external service → [Auth & route protection](./auth-and-route-protection)