eve 0.81.0 → 0.81.1

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 (76) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/src/compiled/.vendor-stamp.json +2 -2
  3. package/dist/src/compiled/@eve/catalog/index.js +1 -1
  4. package/dist/src/eve-channel/create-request.d.ts +2 -1
  5. package/dist/src/eve-channel/index.js +1 -1
  6. package/dist/src/eve-channel/request.d.ts +2 -1
  7. package/dist/src/eve-channel/request.js +1 -2
  8. package/dist/src/eve-channel/support.d.ts +9 -1
  9. package/dist/src/eve-channel/support.js +2 -1
  10. package/dist/src/eve-channel/types.d.ts +18 -0
  11. package/dist/src/execution/dynamic-workflow/tool.js +1 -1
  12. package/dist/src/execution/hook-ownership.d.ts +8 -0
  13. package/dist/src/execution/hook-ownership.js +1 -1
  14. package/dist/src/execution/session/entry-input.d.ts +19 -5
  15. package/dist/src/execution/session/entry-input.js +1 -1
  16. package/dist/src/execution/session/entry.js +1 -1
  17. package/dist/src/execution/session/handoff-steps.d.ts +75 -13
  18. package/dist/src/execution/session/handoff-steps.js +1 -1
  19. package/dist/src/execution/session/handoff.d.ts +69 -27
  20. package/dist/src/execution/session/handoff.js +1 -1
  21. package/dist/src/execution/session/program.js +1 -1
  22. package/dist/src/execution/session/run-watch.d.ts +22 -0
  23. package/dist/src/execution/session/run-watch.js +1 -0
  24. package/dist/src/execution/session-inbox/address.d.ts +4 -1
  25. package/dist/src/execution/session-inbox/end-stranded-session.js +1 -1
  26. package/dist/src/execution/session-inbox/hook-release.d.ts +9 -2
  27. package/dist/src/execution/session-inbox/hook-release.js +1 -1
  28. package/dist/src/execution/session-inbox/inbox.d.ts +66 -6
  29. package/dist/src/execution/session-inbox/inbox.js +1 -1
  30. package/dist/src/execution/session-inbox/resume.d.ts +4 -3
  31. package/dist/src/execution/tools/agent-router-workflow.js +1 -1
  32. package/dist/src/execution/workflow-runtime.d.ts +6 -11
  33. package/dist/src/execution/workflow-runtime.js +1 -1
  34. package/dist/src/harness/model-call/call.js +1 -1
  35. package/dist/src/instrumentation/content.d.ts +1 -1
  36. package/dist/src/instrumentation/content.js +1 -1
  37. package/dist/src/instrumentation/lifecycle.d.ts +4 -2
  38. package/dist/src/internal/application/stamped-package-version.js +1 -1
  39. package/dist/src/internal/authored-definition/connection.js +1 -1
  40. package/dist/src/internal/mcp/skills.js +1 -1
  41. package/dist/src/internal/workflow/development-world-client.js +1 -1
  42. package/dist/src/public/channels/chat-sdk/messageToUserContent.d.ts +2 -1
  43. package/dist/src/public/channels/chat-sdk/messageToUserContent.js +1 -1
  44. package/dist/src/public/channels/photon/attachments.d.ts +5 -2
  45. package/dist/src/public/channels/photon/attachments.js +1 -1
  46. package/dist/src/public/channels/photon/photonIMessageChannel.js +1 -1
  47. package/dist/src/public/channels/slack/api.d.ts +8 -1
  48. package/dist/src/public/channels/slack/api.js +1 -1
  49. package/dist/src/public/channels/slack/slackChannel.js +1 -1
  50. package/dist/src/setup/scaffold/create/project.js +1 -1
  51. package/dist/src/shared/gateway-routing.d.ts +20 -0
  52. package/dist/src/shared/gateway-routing.js +1 -0
  53. package/dist/src/shared/skill-package.js +1 -1
  54. package/dist/src/tools/provided/ask-question-workflow.js +1 -1
  55. package/dist/src/tools/provided/sleep-workflow.js +1 -1
  56. package/dist/src/tracing/eve/agent-otel-provider.js +1 -1
  57. package/dist/src/tracing/eve/gateway.d.ts +5 -0
  58. package/dist/src/tracing/eve/gateway.js +1 -1
  59. package/dist/src/tracing/eve/operation-input.d.ts +0 -7
  60. package/dist/src/tracing/eve/operation-input.js +1 -1
  61. package/dist/src/tracing/eve/policy.d.ts +1 -1
  62. package/dist/src/tracing/eve/policy.js +1 -1
  63. package/dist/src/tracing/eve/session.d.ts +2 -0
  64. package/dist/src/tracing/eve/session.js +1 -1
  65. package/docs/README.md +1 -0
  66. package/docs/agent-config.md +22 -0
  67. package/docs/channels/chat-sdk.mdx +4 -2
  68. package/docs/channels/eve.mdx +27 -0
  69. package/docs/channels/photon.mdx +4 -2
  70. package/docs/channels/slack.mdx +2 -0
  71. package/docs/concepts/execution-model-and-durability.mdx +16 -7
  72. package/docs/guides/instrumentation/otel.mdx +20 -0
  73. package/docs/patterns/meta.json +1 -0
  74. package/docs/patterns/voice-notes.md +67 -0
  75. package/docs/sandbox/index.mdx +2 -0
  76. package/package.json +1 -1
@@ -1 +1 @@
1
- import{isObject}from"#shared/guards.js";import{isUserMessageKind}from"#harness/messages.js";import{resolveInstrumentationEnvironment}from"#internal/application/dev-environment.js";import{isSampledTrace}from"#shared/trace-policy.js";import{ROOT_CONTEXT,context,trace}from"#compiled/@opentelemetry/api/index.js";import{gatewayCostAttributes,readGatewayCostData}from"#tracing/eve/gateway.js";import{createAgentToolInstrumentation}from"#tracing/eve/agent-tool-instrumentation.js";import{withChannelAudience}from"#tracing/eve/channel-audience-context.js";import{createEveTracing}from"#tracing/eve/operation-input.js";import{createAgentApprovalInstrumentation}from"#tracing/eve/agent-approval-instrumentation.js";import{createAgentChannelDeliveryInstrumentation}from"#tracing/eve/agent-channel-delivery-instrumentation.js";import{eveActivationMetadata}from"#tracing/eve/metadata.js";import{createAgentMemoryInstrumentation}from"#tracing/eve/agent-memory-instrumentation.js";import{createEveSessionTracing}from"#tracing/eve/session.js";import{createEveCapturePolicy}from"#tracing/eve/policy.js";function createAgentOtelInstrumentation(e){let t=e.environment??resolveInstrumentationEnvironment(),n=e.recordInputs??!1,r=e.recordOutputs??!1,i=createEveTracing(e),a=new Map,o=new Map,s=createEveSessionTracing({...e,environment:t,tracing:i}),{prepareSessionTrace:c,prepareTurnTrace:l}=s;async function attemptFor(e){let t=a.get(e.attemptId);if(t!==void 0)return t;let n=await s.turnFor(e.sessionId,e.turnId,withChannelAudience(ROOT_CONTEXT,e.channelAudience)),r={stepIndex:e.stepIndex,attempt:e.attemptIndex};return n?.findAttempt(r)??(n?.finished===!1?n.attempt(r):void 0)}let u=createAgentToolInstrumentation({tracing:i,attemptFor,stepFor:e=>a.get(e.attemptId),recordInputs:n,recordOutputs:r,stateStore:e.stateStore}),d=createAgentApprovalInstrumentation({tracing:i,actionStateFor:u.stateFor}),f=createAgentMemoryInstrumentation({...e,environment:t,tracing:i}),p=createEveCapturePolicy({stateStore:e.stateStore,environment:t,recordInputs:n,recordOutputs:r});function turnState(t){return e.stateStore.get(`turn`,JSON.stringify([t.sessionId,t.turnId]))}let onSessionStarted=async e=>{await c(e)},onTurnStarted=async e=>{await l(e)},onStepStarted=async t=>{let n=await turnState(t.scope);if(n===void 0||!isSampledTrace(n.context))return;let r=await e.stateStore.get(`session`,t.scope.sessionId),i=await s.turnFor(t.scope.sessionId,t.scope.turnId,withChannelAudience(ROOT_CONTEXT,t.scope.channelAudience));if(i===void 0||i.finished)return;let o=trace.getSpan(context.active())?.spanContext(),c=await i.attempt({stepIndex:t.scope.stepIndex,attempt:t.scope.attemptIndex,runtimeContext:t.runtimeContext,channel:eveActivationMetadata({session:r,turn:n,sessionId:t.scope.sessionId}).channel,links:o===void 0||o.traceId===n.context.traceId?void 0:[{relationship:`execution.delivery`,context:o}]});a.set(t.scope.attemptId,c)},onStepTerminal=async e=>{let t=e.type===`step.attempt.failed`?{error:e.error}:void 0;await drainOpenSpans(e),await u.drain(e.scope.attemptId,t);let n=a.get(e.scope.attemptId)??await attemptFor(e.scope);a.delete(e.scope.attemptId),t===void 0?await n?.complete():(await n?.fail(t.error),await u.forgetAttempt(e.scope))},onSessionTransition=async t=>{await s.sessionTransition(t),(t.type===`session.completed`||t.type===`session.failed`)&&(await u.deleteForSession(t.sessionId),await e.stateStore.delete(`session`,t.sessionId))},onModelCallStarted=async e=>{let t=a.get(e.scope.attemptId);if(t===void 0)return;let r=await t.modelCall({provider:e.model.provider,modelId:e.model.modelId,messages:n?e.input?.messages.map(e=>isObject(e)&&e.role===`user`?{...e,kind:isUserMessageKind(e.kind)?e.kind:void 0}:e):void 0,instructions:n?e.input?.instructions:void 0,tools:n?e.input?.tools:void 0,runtimeContext:e.runtimeContext},e.idempotencyKey),i=o.get(e.scope.attemptId);i===void 0&&o.set(e.scope.attemptId,i=new Map),i.set(e.idempotencyKey,r)},onModelCallTerminal=async e=>{let t=o.get(e.scope.attemptId),n=t?.get(e.idempotencyKey);t?.delete(e.idempotencyKey),n!==void 0&&(e.type===`model.call.failed`?await n.fail(e.error):await n.complete({outcome:`completed`,result:{...e,content:r?e.content:void 0}}))},m=createAgentChannelDeliveryInstrumentation({recordInputs:n,stateStore:e.stateStore}),onStepMetadata=e=>{let t=a.get(e.scope.attemptId);if(t===void 0)return;let n=readGatewayCostData(e.providerMetadata);n!==void 0&&t.attributes(gatewayCostAttributes(n))};return{hook:{events:{...m,...d,"step.attempt.completed":onStepTerminal,"step.attempt.failed":onStepTerminal,"step.attempt.metadata":onStepMetadata,"step.attempt.started":onStepStarted,"model.call.completed":onModelCallTerminal,"model.call.failed":onModelCallTerminal,"model.call.started":onModelCallStarted,"session.completed":onSessionTransition,"session.failed":onSessionTransition,"session.started":onSessionStarted,"session.waiting":onSessionTransition,...u.events,"turn.cancelled":s.turnTerminal,"turn.completed":s.turnTerminal,"turn.failed":s.turnTerminal,"turn.started":onTurnStarted},name:`eve.otel`,projectEvent:p.projectEvent,tracePolicy:()=>({emit:!0,recordInputs:n,recordOutputs:r})},prepareSessionTrace:c,prepareTurnTrace:l,async runInContext(t,n){if(t.type===`memory.operation`)return f.runInContext(t,n);let r={emit:!0,...await p.forOperation(t.scope)},inTurn=async()=>{let i=await s.turnFor(t.scope.sessionId,t.scope.turnId,withChannelAudience(ROOT_CONTEXT,t.scope.channelAudience));if(i!==void 0&&!i.finished)return i.run(n,r);let{sessionId:a,turnId:o}=t.scope;return e.telemetry.suppressed!==void 0&&await s.dropped(a,o)?e.telemetry.suppressed(n):n()};if(t.type===`tool.call`)return u.runInContext(t,n,r,inTurn);let i=o.get(t.scope.attemptId)?.get(t.idempotencyKey);return i!==void 0&&!i.finished?i.run(n,r):inTurn()}};async function drainOpenSpans(e){for(let t of o.get(e.scope.attemptId)?.values()??[])e.type===`step.attempt.failed`?await t.fail(e.error):await t.complete({outcome:`completed`,result:{finishReason:`unknown`,usage:{}}});o.delete(e.scope.attemptId)}}export{createAgentOtelInstrumentation};
1
+ import{isObject}from"#shared/guards.js";import{isUserMessageKind}from"#harness/messages.js";import{resolveInstrumentationEnvironment}from"#internal/application/dev-environment.js";import{isSampledTrace}from"#shared/trace-policy.js";import{ROOT_CONTEXT,context,trace}from"#compiled/@opentelemetry/api/index.js";import{gatewayCostAttributes,gatewayRoutingAttributes,readGatewayCostData}from"#tracing/eve/gateway.js";import{createAgentToolInstrumentation}from"#tracing/eve/agent-tool-instrumentation.js";import{withChannelAudience}from"#tracing/eve/channel-audience-context.js";import{createEveTracing}from"#tracing/eve/operation-input.js";import{createAgentApprovalInstrumentation}from"#tracing/eve/agent-approval-instrumentation.js";import{createAgentChannelDeliveryInstrumentation}from"#tracing/eve/agent-channel-delivery-instrumentation.js";import{eveActivationMetadata}from"#tracing/eve/metadata.js";import{createAgentMemoryInstrumentation}from"#tracing/eve/agent-memory-instrumentation.js";import{createEveSessionTracing}from"#tracing/eve/session.js";import{createEveCapturePolicy}from"#tracing/eve/policy.js";function createAgentOtelInstrumentation(e){let t=e.environment??resolveInstrumentationEnvironment(),n=e.recordInputs??!1,r=e.recordOutputs??!1,i=createEveTracing(e),a=createEveCapturePolicy({stateStore:e.stateStore,environment:t,recordInputs:n,recordOutputs:r}),o=new Map,s=new Map,c=createEveSessionTracing({...e,environment:t,tracing:i,capturePolicy:a}),{prepareSessionTrace:l,prepareTurnTrace:u}=c;async function attemptFor(e){let t=o.get(e.attemptId);if(t!==void 0)return t;let n=await c.turnFor(e.sessionId,e.turnId,withChannelAudience(ROOT_CONTEXT,e.channelAudience)),r={stepIndex:e.stepIndex,attempt:e.attemptIndex};return n?.findAttempt(r)??(n?.finished===!1?n.attempt(r):void 0)}let d=createAgentToolInstrumentation({tracing:i,attemptFor,stepFor:e=>o.get(e.attemptId),recordInputs:n,recordOutputs:r,stateStore:e.stateStore}),f=createAgentApprovalInstrumentation({tracing:i,actionStateFor:d.stateFor}),p=createAgentMemoryInstrumentation({...e,environment:t,tracing:i});function turnState(t){return e.stateStore.get(`turn`,JSON.stringify([t.sessionId,t.turnId]))}let onSessionStarted=async e=>{await l(e)},onTurnStarted=async e=>{await u(e)},onStepStarted=async t=>{let n=await turnState(t.scope);if(n===void 0||!isSampledTrace(n.context))return;let r=await e.stateStore.get(`session`,t.scope.sessionId),i=await c.turnFor(t.scope.sessionId,t.scope.turnId,withChannelAudience(ROOT_CONTEXT,t.scope.channelAudience));if(i===void 0||i.finished)return;let a=trace.getSpan(context.active())?.spanContext(),s=await i.attempt({stepIndex:t.scope.stepIndex,attempt:t.scope.attemptIndex,runtimeContext:t.runtimeContext,channel:eveActivationMetadata({session:r,turn:n,sessionId:t.scope.sessionId}).channel,links:a===void 0||a.traceId===n.context.traceId?void 0:[{relationship:`execution.delivery`,context:a}]});o.set(t.scope.attemptId,s)},onStepTerminal=async e=>{let t=e.type===`step.attempt.failed`?{error:e.error}:void 0;await drainOpenSpans(e),await d.drain(e.scope.attemptId,t);let n=o.get(e.scope.attemptId)??await attemptFor(e.scope);o.delete(e.scope.attemptId),t===void 0?await n?.complete():(await n?.fail(t.error),await d.forgetAttempt(e.scope))},onSessionTransition=async t=>{await c.sessionTransition(t),(t.type===`session.completed`||t.type===`session.failed`)&&(await d.deleteForSession(t.sessionId),await e.stateStore.delete(`session`,t.sessionId))},onModelCallStarted=async e=>{let t=o.get(e.scope.attemptId);if(t===void 0)return;let r=await t.modelCall({provider:e.model.provider,modelId:e.model.modelId,messages:n?e.input?.messages.map(e=>isObject(e)&&e.role===`user`?{...e,kind:isUserMessageKind(e.kind)?e.kind:void 0}:e):void 0,instructions:n?e.input?.instructions:void 0,tools:n?e.input?.tools:void 0,runtimeContext:e.runtimeContext},e.idempotencyKey),i=s.get(e.scope.attemptId);i===void 0&&s.set(e.scope.attemptId,i=new Map),i.set(e.idempotencyKey,r)},onModelCallTerminal=async e=>{let t=s.get(e.scope.attemptId),n=t?.get(e.idempotencyKey);t?.delete(e.idempotencyKey),n!==void 0&&(e.type===`model.call.failed`?await n.fail(e.error):await n.complete({outcome:`completed`,result:{...e,content:r?e.content:void 0}}))},m=createAgentChannelDeliveryInstrumentation({recordInputs:n,stateStore:e.stateStore}),onStepMetadata=e=>{let t=o.get(e.scope.attemptId);if(t===void 0)return;let n=readGatewayCostData(e.providerMetadata);n!==void 0&&t.attributes(gatewayCostAttributes(n));let r=gatewayRoutingAttributes(e.providerMetadata);r!==void 0&&t.attributes(r)};return{hook:{events:{...m,...f,"step.attempt.completed":onStepTerminal,"step.attempt.failed":onStepTerminal,"step.attempt.metadata":onStepMetadata,"step.attempt.started":onStepStarted,"model.call.completed":onModelCallTerminal,"model.call.failed":onModelCallTerminal,"model.call.started":onModelCallStarted,"session.completed":onSessionTransition,"session.failed":onSessionTransition,"session.started":onSessionStarted,"session.waiting":onSessionTransition,...d.events,"turn.cancelled":c.turnTerminal,"turn.completed":c.turnTerminal,"turn.failed":c.turnTerminal,"turn.started":onTurnStarted},name:`eve.otel`,projectEvent:a.projectEvent,tracePolicy:()=>({emit:!0,recordInputs:n,recordOutputs:r})},prepareSessionTrace:l,prepareTurnTrace:u,async runInContext(t,n){if(t.type===`memory.operation`)return p.runInContext(t,n);let r={emit:!0,...await a.forOperation(t.scope)},inTurn=async()=>{let i=await c.turnFor(t.scope.sessionId,t.scope.turnId,withChannelAudience(ROOT_CONTEXT,t.scope.channelAudience));if(i!==void 0&&!i.finished)return i.run(n,r);let{sessionId:a,turnId:o}=t.scope;return e.telemetry.suppressed!==void 0&&await c.dropped(a,o)?e.telemetry.suppressed(n):n()};if(t.type===`tool.call`)return d.runInContext(t,n,r,inTurn);let i=s.get(t.scope.attemptId)?.get(t.idempotencyKey);return i!==void 0&&!i.finished?i.run(n,r):inTurn()}};async function drainOpenSpans(e){for(let t of s.get(e.scope.attemptId)?.values()??[])e.type===`step.attempt.failed`?await t.fail(e.error):await t.complete({outcome:`completed`,result:{finishReason:`unknown`,usage:{}}});s.delete(e.scope.attemptId)}}export{createAgentOtelInstrumentation};
@@ -15,3 +15,8 @@ export declare function readGatewayCostData(providerMetadata: Readonly<Record<st
15
15
  outputCost: number | undefined;
16
16
  generationId: string | undefined;
17
17
  } | undefined;
18
+ /**
19
+ * Converts Gateway session affinity metadata to span attributes, so a step can
20
+ * be matched to its session in the Gateway logs Sessions view.
21
+ */
22
+ export declare function gatewayRoutingAttributes(providerMetadata: Readonly<Record<string, unknown>> | undefined): Attributes | undefined;
@@ -1 +1 @@
1
- import{stripEveTraceBaggage}from"#protocol/baggage.js";import{context,createContextKey,propagation,trace}from"#compiled/@opentelemetry/api/index.js";const SUPPRESS_TRACING_KEY=createContextKey(`OpenTelemetry SDK Context Key SUPPRESS_TRACING`),traceContextSetter={set(e,t,n){e[t]=n}};function createGatewayTraceContextHeaders(){let e=context.active();if(e.getValue(SUPPRESS_TRACING_KEY)===!0||trace.getSpan(e)===void 0)return;let t={};return propagation.inject(e,t,traceContextSetter),stripEveTraceBaggage(t),Object.keys(t).length===0?void 0:t}function gatewayCostAttributes(e){return{"gen_ai.usage.cost":e.cost,"gen_ai.usage.gateway_cost":e.gatewayCost,"gen_ai.usage.input_cost":e.inputCost,"gen_ai.usage.output_cost":e.outputCost,"gen_ai.generation.id":e.generationId}}function readGatewayCostData(e){if(e===void 0)return;let t=e.gateway;if(typeof t!=`object`||!t||Array.isArray(t))return;let n=t;return{cost:readUsd(n.cost),gatewayCost:readUsd(n.gatewayCost),inputCost:readUsd(n.inputInferenceCost),outputCost:readUsd(n.outputInferenceCost),generationId:typeof n.generationId==`string`&&n.generationId.length>0?n.generationId:void 0}}function readUsd(e){if(typeof e!=`string`||e.trim()===``)return;let t=Number(e);return Number.isFinite(t)?t:void 0}export{createGatewayTraceContextHeaders,gatewayCostAttributes,readGatewayCostData};
1
+ import{isObject}from"#shared/guards.js";import{stripEveTraceBaggage}from"#protocol/baggage.js";import{context,createContextKey,propagation,trace}from"#compiled/@opentelemetry/api/index.js";import{readGatewayRouting}from"#shared/gateway-routing.js";const SUPPRESS_TRACING_KEY=createContextKey(`OpenTelemetry SDK Context Key SUPPRESS_TRACING`),traceContextSetter={set(e,t,n){e[t]=n}};function createGatewayTraceContextHeaders(){let e=context.active();if(e.getValue(SUPPRESS_TRACING_KEY)===!0||trace.getSpan(e)===void 0)return;let t={};return propagation.inject(e,t,traceContextSetter),stripEveTraceBaggage(t),Object.keys(t).length===0?void 0:t}function gatewayCostAttributes(e){return{"gen_ai.usage.cost":e.cost,"gen_ai.usage.gateway_cost":e.gatewayCost,"gen_ai.usage.input_cost":e.inputCost,"gen_ai.usage.output_cost":e.outputCost,"gen_ai.generation.id":e.generationId}}function readGatewayCostData(e){if(e===void 0)return;let t=e.gateway;if(typeof t!=`object`||!t||Array.isArray(t))return;let n=t;return{cost:readUsd(n.cost),gatewayCost:readUsd(n.gatewayCost),inputCost:readUsd(n.inputInferenceCost),outputCost:readUsd(n.outputInferenceCost),generationId:typeof n.generationId==`string`&&n.generationId.length>0?n.generationId:void 0}}function gatewayRoutingAttributes(e){let t=e?.gateway,n=readGatewayRouting(isObject(t)?t.routing:void 0);if(n!==void 0)return{"vercel.ai_gateway.client_session_id":n.clientSessionId,"vercel.ai_gateway.client_session_id_source":n.clientSessionIdSource,"vercel.ai_gateway.affinity.outcome":n.affinity?.outcome,"vercel.ai_gateway.affinity.pinned_provider":n.affinity?.pinnedProvider,"vercel.ai_gateway.affinity.displaced_provider":n.affinity?.displacedProvider}}function readUsd(e){if(typeof e!=`string`||e.trim()===``)return;let t=Number(e);return Number.isFinite(t)?t:void 0}export{createGatewayTraceContextHeaders,gatewayCostAttributes,gatewayRoutingAttributes,readGatewayCostData};
@@ -1,4 +1,3 @@
1
- import type { SpanContext } from "#compiled/@opentelemetry/api/index.js";
2
1
  import type { AgentTraceStateStore } from "#tracing/eve/agent-trace-state.js";
3
2
  import { type AgentTelemetry, type AgentRun } from "#compiled/@vercel/agent-runs/index.js";
4
3
  export declare function traceSessionIdOf(scope: {
@@ -17,12 +16,6 @@ export declare function eveTurnIdentity(input: {
17
16
  runId: string;
18
17
  turnId: string;
19
18
  };
20
- /** Content flags are recorded by eve's content processor, not withheld by the library. */
21
- export declare function eveCapture(reference: SpanContext | undefined): {
22
- emit: boolean;
23
- recordInputs: boolean;
24
- recordOutputs: boolean;
25
- };
26
19
  /** eve's agent tracing, checkpointed in its workflow-context trace state. */
27
20
  export declare function createEveTracing(input: {
28
21
  readonly telemetry: AgentTelemetry;
@@ -1 +1 @@
1
- import{resolveConversationId}from"#shared/conversation-identity.js";import{createAgentRun}from"#compiled/@vercel/agent-runs/index.js";function traceSessionIdOf(e){return e.traceSessionId??e.rootSessionId??e.sessionId}function checkpointContent(e){if(e!==void 0)try{return JSON.parse(e)}catch{return}}function eveTurnIdentity(e){return{conversationId:resolveConversationId(e.rootSessionId??e.sessionId),runId:e.sessionId,turnId:e.turnId}}function eveCapture(e){return{emit:!!((e?.traceFlags??1)&1),recordInputs:!0,recordOutputs:!0}}function createEveTracing(e){return createAgentRun({telemetry:e.telemetry,checkpointer:{get:t=>e.stateStore.get(`checkpoint`,t),set:(t,n)=>e.stateStore.set(`checkpoint`,t,n),delete:t=>e.stateStore.delete(`checkpoint`,t)}})}export{checkpointContent,createEveTracing,eveCapture,eveTurnIdentity,traceSessionIdOf};
1
+ import{resolveConversationId}from"#shared/conversation-identity.js";import{createAgentRun}from"#compiled/@vercel/agent-runs/index.js";function traceSessionIdOf(e){return e.traceSessionId??e.rootSessionId??e.sessionId}function checkpointContent(e){if(e!==void 0)try{return JSON.parse(e)}catch{return}}function eveTurnIdentity(e){return{conversationId:resolveConversationId(e.rootSessionId??e.sessionId),runId:e.sessionId,turnId:e.turnId}}function createEveTracing(e){return createAgentRun({telemetry:e.telemetry,checkpointer:{get:t=>e.stateStore.get(`checkpoint`,t),set:(t,n)=>e.stateStore.set(`checkpoint`,t,n),delete:t=>e.stateStore.delete(`checkpoint`,t)}})}export{checkpointContent,createEveTracing,eveTurnIdentity,traceSessionIdOf};
@@ -8,7 +8,7 @@ export declare function createEveCapturePolicy(input: {
8
8
  readonly recordOutputs: boolean;
9
9
  }): {
10
10
  projectEvent(event: InstrumentationEvent): Promise<InstrumentationEvent>;
11
- forOperation(scope: InstrumentationAttemptScope): Promise<{
11
+ forOperation(scope: Pick<InstrumentationAttemptScope, "sessionId" | "channelAudience">): Promise<{
12
12
  recordInputs: boolean;
13
13
  recordOutputs: boolean;
14
14
  }>;
@@ -1 +1 @@
1
- import{normalizeChannelAudience}from"#shared/channel-audience.js";import{applyLiveDeliveryAudienceCeiling,resolveForwardedTraceSeed}from"#shared/forwarded-trace-policy.js";import{contextStorage}from"#context/container.js";import{SessionTraceSeedKey}from"#context/keys.js";import{isSampledTrace,resolveTracePolicyDecision}from"#shared/trace-policy.js";import{withoutInstrumentationContent}from"#instrumentation/content.js";import{readInstrumentationDecision}from"#shared/instrumentation-decision.js";import{instrumentationEventForTraceDecision}from"#instrumentation/content-policy.js";function createEveCapturePolicy(e){return{async projectEvent(t){let n=await e.stateStore.get(`session`,`scope`in t?t.scope.sessionId:t.sessionId),r=audienceForEvent(t,n?.channelAudience),i=`traceSeed`in t?t.traceSeed:void 0,a=contextStorage.getStore()?.get(SessionTraceSeedKey),o=resolveForwardedTraceSeed(a),s=resolveForwardedTraceSeed(i,o?.forwardedTracePolicy),decisionForTrace=t=>t===void 0?void 0:resolveTracePolicyDecision(isSampledTrace(t),{audience:r,environment:e.environment}),c=s?.decision??o?.decision??readInstrumentationDecision(n?.decision)??decisionForTrace(i)??decisionForTrace(a)??decisionForTrace(n?.context);if(c===void 0)return withoutInstrumentationContent(t);let l=s===void 0||!(`traceSeed`in t)||t.traceSeed===void 0?t:{...t,traceSeed:{...t.traceSeed,decision:s.decision,traceFlags:s.traceFlags}};return instrumentationEventForTraceDecision(l,applyLiveDeliveryAudienceCeiling(c.action===`drop`?c:{...c,recordInputs:e.recordInputs&&c.recordInputs,recordOutputs:e.recordOutputs&&c.recordOutputs},r,s?.forwardedTracePolicy??o?.forwardedTracePolicy,e.environment),{audience:r,environment:e.environment},{applyAudienceCeiling:!1})},async forOperation(t){let n=await e.stateStore.get(`session`,t.sessionId),r=resolveForwardedTraceSeed(contextStorage.getStore()?.get(SessionTraceSeedKey)),i=r?.decision??n?.decision,a=i===void 0?void 0:applyLiveDeliveryAudienceCeiling(i,normalizeChannelAudience(t.channelAudience),r?.forwardedTracePolicy,e.environment);return{recordInputs:e.recordInputs&&a?.action===`record`&&a.recordInputs,recordOutputs:e.recordOutputs&&a?.action===`record`&&a.recordOutputs}}}}function audienceForEvent(e,t){return`delivery`in e?normalizeChannelAudience(e.delivery.channelAudience):`scope`in e&&e.scope.channelAudience!==void 0?normalizeChannelAudience(e.scope.channelAudience):e.type===`session.started`?normalizeChannelAudience(e.channelAudience):normalizeChannelAudience(t)}export{createEveCapturePolicy};
1
+ import{normalizeChannelAudience}from"#shared/channel-audience.js";import{ConversationContextKey}from"#shared/conversation-context.js";import{applyLiveDeliveryAudienceCeiling,resolveForwardedTraceSeed}from"#shared/forwarded-trace-policy.js";import{contextStorage}from"#context/container.js";import{SessionTraceSeedKey}from"#context/keys.js";import{isSampledTrace,resolveTracePolicyDecision}from"#shared/trace-policy.js";import{withoutInstrumentationContent}from"#instrumentation/content.js";import{readInstrumentationDecision}from"#shared/instrumentation-decision.js";import{instrumentationEventForTraceDecision}from"#instrumentation/content-policy.js";function createEveCapturePolicy(e){return{async projectEvent(t){let n=await e.stateStore.get(`session`,`scope`in t?t.scope.sessionId:t.sessionId),r=audienceForEvent(t,n?.channelAudience),i=`traceSeed`in t?t.traceSeed:void 0,a=contextStorage.getStore()?.get(SessionTraceSeedKey),o=resolveForwardedTraceSeed(a),s=resolveForwardedTraceSeed(i,o?.forwardedTracePolicy),decisionForTrace=t=>t===void 0?void 0:resolveTracePolicyDecision(isSampledTrace(t),{audience:r,environment:e.environment}),c=s?.decision??o?.decision??readInstrumentationDecision(n?.decision)??decisionForTrace(i)??decisionForTrace(a)??decisionForTrace(n?.context);if(c===void 0)return withoutInstrumentationContent(t);let l=s===void 0||!(`traceSeed`in t)||t.traceSeed===void 0?t:{...t,traceSeed:{...t.traceSeed,decision:s.decision,traceFlags:s.traceFlags}};return instrumentationEventForTraceDecision(l,applyLiveDeliveryAudienceCeiling(c.action===`drop`?c:{...c,recordInputs:e.recordInputs&&c.recordInputs,recordOutputs:e.recordOutputs&&c.recordOutputs},r,s?.forwardedTracePolicy??o?.forwardedTracePolicy,e.environment),{audience:r,environment:e.environment},{applyAudienceCeiling:!1})},async forOperation(t){let n=await e.stateStore.get(`session`,t.sessionId),r=contextStorage.getStore(),i=resolveForwardedTraceSeed(r?.get(SessionTraceSeedKey)),a=i?.decision??n?.decision,o=a===void 0?void 0:applyLiveDeliveryAudienceCeiling(a,normalizeChannelAudience(t.channelAudience??r?.get(ConversationContextKey)?.audience??n?.channelAudience),i?.forwardedTracePolicy,e.environment);return{recordInputs:e.recordInputs&&o?.action===`record`&&o.recordInputs,recordOutputs:e.recordOutputs&&o?.action===`record`&&o.recordOutputs}}}}function audienceForEvent(e,t){return`delivery`in e?normalizeChannelAudience(e.delivery.channelAudience):`scope`in e&&e.scope.channelAudience!==void 0?normalizeChannelAudience(e.scope.channelAudience):e.type===`session.started`?normalizeChannelAudience(e.channelAudience):normalizeChannelAudience(t)}export{createEveCapturePolicy};
@@ -1,6 +1,7 @@
1
1
  import { type InstrumentationSessionStartedEvent, type InstrumentationTraceSeed, type InstrumentationTurnStartedEvent, type InstrumentationTurnTerminalEvent, type InstrumentationSessionTransitionEvent } from "#instrumentation/lifecycle.js";
2
2
  import type { TraceCapturePolicy } from "#tracing/eve/otel-declaration.js";
3
3
  import type { AgentSessionTraceState, AgentTraceStateStore } from "#tracing/eve/agent-trace-state.js";
4
+ import type { createEveCapturePolicy } from "#tracing/eve/policy.js";
4
5
  import type { ConversationEnvironment } from "#shared/conversation-context.js";
5
6
  import type { AgentSpanIdGenerator, AgentRun, ExecutionContext, TurnOperation } from "#compiled/@vercel/agent-runs/index.js";
6
7
  interface EveSessionTracingInput {
@@ -10,6 +11,7 @@ interface EveSessionTracingInput {
10
11
  readonly frameworkVersion: string;
11
12
  readonly stateStore: AgentTraceStateStore;
12
13
  readonly tracePolicy?: TraceCapturePolicy;
14
+ readonly capturePolicy: ReturnType<typeof createEveCapturePolicy>;
13
15
  }
14
16
  type SessionMetadata = Omit<InstrumentationSessionStartedEvent, "idempotencyKey" | "type">;
15
17
  interface EveSessionTracing {
@@ -1 +1 @@
1
- import{normalizeChannelAudience}from"#shared/channel-audience.js";import{normalizeInstrumentationChannelKind}from"#internal/instrumentation.js";import{withOperationConversation}from"#tracing/eve/conversation-context.js";import{isSampledTrace,resolveTracePolicy,resolveTracePolicyDecision}from"#shared/trace-policy.js";import{ROOT_CONTEXT}from"#compiled/@opentelemetry/api/index.js";import"#instrumentation/lifecycle.js";import{readInstrumentationDecision}from"#shared/instrumentation-decision.js";import{applyPrincipalTraceDecision}from"#instrumentation/principal-summary.js";import{withChannelAudience}from"#tracing/eve/channel-audience-context.js";import{eveCapture,eveTurnIdentity,traceSessionIdOf}from"#tracing/eve/operation-input.js";import{eveActivationMetadata}from"#tracing/eve/metadata.js";function createEveSessionTracing(e){let ensureSessionContext=async t=>{let n=await e.stateStore.get(`session`,t.sessionId);if(n===void 0){let r=normalizeChannelAudience(t.channelAudience),i=resolveSessionTraceDecision(t,r,e.environment,e.tracePolicy);n={agentName:t.agentName,channelAudience:r,channelKind:t.channelKind,channelType:t.channelType,decision:i,context:initialSessionContext(e,t,i),parentLineage:t.parentLineage,rootSessionId:t.rootSessionId,traceSessionId:traceSessionIdOf(t),scheduleId:t.scheduleId,title:t.title},await e.stateStore.set(`session`,t.sessionId,n)}return n},prepareSessionTrace=async e=>{let t=await ensureSessionContext(e);return portableSpanContext(t.context,t.decision)},turnKey=(e,t)=>JSON.stringify([e,t]);async function turnFor(t,n,r){let i=await e.stateStore.get(`turn`,turnKey(t,n));if(i===void 0||!isSampledTrace(i.context))return;let a=await e.stateStore.get(`session`,t);return e.tracing.resume({identity:eveTurnIdentity({sessionId:t,rootSessionId:i.rootSessionId,turnId:n}),context:r??hostContext(a?.channelAudience,{sessionId:t,...i})})}async function dropped(t,n){let r=await e.stateStore.get(`turn`,turnKey(t,n));return r!==void 0&&!isSampledTrace(r.context)}let prepareTurnTrace=async t=>{let n=await e.stateStore.get(`turn`,turnKey(t.sessionId,t.turnId));if(n!==void 0){let r=await e.stateStore.get(`session`,t.sessionId);return portableSpanContext(n.context,r?.decision)}let r=await ensureSessionContext(t),i=t.sequence===0,a=i?t.parentTraceContext:void 0,o=r.decision?.action===`record`,s=a!==void 0&&!(`isRemote`in a&&a.isRemote===!0),c=i?{...r.context,traceId:s?a.traceId:r.context.traceId,isRemote:!1}:{traceId:e.idGenerator.deriveTraceId(`turn:${t.idempotencyKey}`),spanId:e.idGenerator.deriveSpanId(`turn:${t.idempotencyKey}`),traceFlags:+!!o,isRemote:!1},l=r.agentName??(t.parentLineage??r.parentLineage)?.subagentName,u={agentName:l,caller:a===void 0?void 0:adoptedSpanContext(a),context:c,currentPrincipal:applyPrincipalTraceDecision(t.currentPrincipal,r.decision),initiatorPrincipal:applyPrincipalTraceDecision(t.initiatorPrincipal,r.decision),parentLineage:t.parentLineage??r.parentLineage,rootSessionId:t.rootSessionId,traceSessionId:traceSessionIdOf(t),sequence:t.sequence,startTimeMs:Date.now(),subagentName:(t.parentLineage??r.parentLineage)?.subagentName};if(isSampledTrace(c)){let n=await e.tracing.turn({...eveActivationMetadata({session:r,turn:u,sessionId:t.sessionId}),agentName:l,identity:eveTurnIdentity({...t,sessionId:t.sessionId}),framework:{name:`eve`,version:e.frameworkVersion},capture:eveCapture(c),reference:c,parent:s?adoptedSpanContext(a):void 0,links:activationLinks(u),startTimeMs:u.startTimeMs,context:hostContext(r.channelAudience,t)});u={...u,context:{...c,traceFlags:n.reference.traceFlags}}}return await e.stateStore.set(`turn`,turnKey(t.sessionId,t.turnId),u),portableSpanContext(u.context,r.decision)};async function recordTerminal(t,n,r,i){await e.stateStore.update(`turn`,turnKey(t,n),e=>({...e,terminal:r.outcome===`failed`?{outcome:`failed`,errorName:i instanceof Error?i.name:void 0,errorMessage:i instanceof Error?i.message:void 0}:r}))}return{ensureSessionContext,prepareSessionTrace,prepareTurnTrace,turnFor,dropped,async turnTerminal(e){await recordTerminal(e.sessionId,e.turnId,{outcome:e.type===`turn.failed`?`failed`:e.type===`turn.cancelled`?`cancelled`:`completed`},e.type===`turn.failed`?e.error:void 0)},async sessionTransition(t){if(t.turnId===void 0)return;t.type===`session.failed`&&await recordTerminal(t.sessionId,t.turnId,{outcome:`failed`},t.error);let n=await e.stateStore.get(`turn`,turnKey(t.sessionId,t.turnId));if(n===void 0)return;let r=await e.stateStore.get(`session`,t.sessionId),i=await turnFor(t.sessionId,t.turnId);if(i!==void 0){let e=eveActivationMetadata({session:r,turn:n,sessionId:t.sessionId});i.attributes(e.attributes??{}),i.links(activationLinks(n)??[]);let a=n.terminal;if(a===void 0)await i.complete({outcomeUnknown:!0});else if(a.outcome===`failed`){let e=Error(a.errorMessage);e.name=a.errorName??`Error`,await i.complete({outcome:`failed`,failed:!0,error:e,errorType:a.errorName})}else await i.complete({outcome:a.outcome})}await e.stateStore.delete(`turn`,turnKey(t.sessionId,t.turnId))}}}function hostContext(e,t){return withOperationConversation(withChannelAudience(ROOT_CONTEXT,e),t)}function activationLinks(e){let t=[];return e.caller?.isRemote===!0&&t.push({context:e.caller,relationship:`agent.dispatch`}),e.channelDelivery?.requestTraceContext!==void 0&&t.push({context:e.channelDelivery.requestTraceContext,relationship:`channel.request`}),t.length===0?void 0:t}function portableSpanContext(e,t){return{decision:t,spanId:e.spanId,traceFlags:e.traceFlags,traceId:e.traceId}}function adoptedSpanContext(e){return{isRemote:`isRemote`in e&&e.isRemote===!0,spanId:e.spanId,traceFlags:e.traceFlags,traceId:e.traceId}}function initialSessionContext(e,t,n){let r=t.traceSeed;if(r!==void 0)return{...adoptedSpanContext(r),traceFlags:n.action===`drop`?0:r.traceFlags};let i=`session:${t.sessionId}`,a=t.parentTraceContext;return{traceId:a!==void 0&&!(`isRemote`in a&&a.isRemote===!0)?a.traceId:e.idGenerator.deriveTraceId(i),spanId:e.idGenerator.deriveSpanId(i),traceFlags:+(n.action===`record`)}}function resolveSessionTraceDecision(e,t,n,r){let i={audience:t,environment:n};return e.parentTraceContext!==void 0&&!isSampledTrace(e.parentTraceContext)?{action:`drop`}:e.traceSeed?.decision===void 0?e.traceSeed===void 0?e.parentTraceContext===void 0?e.agentName===void 0?r===void 0?resolveTracePolicyDecision(!0,i):{action:`drop`}:resolveTracePolicy(r,{agentName:e.agentName,audience:t,channel:{kind:normalizeInstrumentationChannelKind(e.channelKind??e.channelType)},environment:n,principalType:`unknown`}):resolveTracePolicyDecision(isSampledTrace(e.parentTraceContext),i):resolveTracePolicyDecision(isSampledTrace(e.traceSeed),i):readInstrumentationDecision(e.traceSeed.decision)??{action:`drop`}}export{createEveSessionTracing};
1
+ import{normalizeChannelAudience}from"#shared/channel-audience.js";import{normalizeInstrumentationChannelKind}from"#internal/instrumentation.js";import{withOperationConversation}from"#tracing/eve/conversation-context.js";import{isSampledTrace,resolveTracePolicy,resolveTracePolicyDecision}from"#shared/trace-policy.js";import{ROOT_CONTEXT}from"#compiled/@opentelemetry/api/index.js";import"#instrumentation/lifecycle.js";import{readInstrumentationDecision}from"#shared/instrumentation-decision.js";import{applyPrincipalTraceDecision}from"#instrumentation/principal-summary.js";import{withChannelAudience}from"#tracing/eve/channel-audience-context.js";import{eveTurnIdentity,traceSessionIdOf}from"#tracing/eve/operation-input.js";import{eveActivationMetadata}from"#tracing/eve/metadata.js";function createEveSessionTracing(e){let ensureSessionContext=async t=>{let n=await e.stateStore.get(`session`,t.sessionId);if(n===void 0){let r=normalizeChannelAudience(t.channelAudience),i=resolveSessionTraceDecision(t,r,e.environment,e.tracePolicy);n={agentName:t.agentName,channelAudience:r,channelKind:t.channelKind,channelType:t.channelType,decision:i,context:initialSessionContext(e,t,i),parentLineage:t.parentLineage,rootSessionId:t.rootSessionId,traceSessionId:traceSessionIdOf(t),scheduleId:t.scheduleId,title:t.title},await e.stateStore.set(`session`,t.sessionId,n)}return n},prepareSessionTrace=async e=>{let t=await ensureSessionContext(e);return portableSpanContext(t.context,t.decision)},turnKey=(e,t)=>JSON.stringify([e,t]);async function turnFor(t,n,r){let i=await e.stateStore.get(`turn`,turnKey(t,n));if(i===void 0||!isSampledTrace(i.context))return;let a=await e.stateStore.get(`session`,t);return e.tracing.resume({identity:eveTurnIdentity({sessionId:t,rootSessionId:i.rootSessionId,turnId:n}),context:r??hostContext(a?.channelAudience,{sessionId:t,...i})})}async function dropped(t,n){let r=await e.stateStore.get(`turn`,turnKey(t,n));return r!==void 0&&!isSampledTrace(r.context)}let prepareTurnTrace=async t=>{let n=await e.stateStore.get(`turn`,turnKey(t.sessionId,t.turnId));if(n!==void 0){let r=await e.stateStore.get(`session`,t.sessionId);return portableSpanContext(n.context,r?.decision)}let r=await ensureSessionContext(t),i=t.sequence===0,a=i?t.parentTraceContext:void 0,o=r.decision?.action===`record`,s=a!==void 0&&!(`isRemote`in a&&a.isRemote===!0),c=i?{...r.context,traceId:s?a.traceId:r.context.traceId,isRemote:!1}:{traceId:e.idGenerator.deriveTraceId(`turn:${t.idempotencyKey}`),spanId:e.idGenerator.deriveSpanId(`turn:${t.idempotencyKey}`),traceFlags:+!!o,isRemote:!1},l=r.agentName??(t.parentLineage??r.parentLineage)?.subagentName,u={agentName:l,caller:a===void 0?void 0:adoptedSpanContext(a),context:c,currentPrincipal:applyPrincipalTraceDecision(t.currentPrincipal,r.decision),initiatorPrincipal:applyPrincipalTraceDecision(t.initiatorPrincipal,r.decision),parentLineage:t.parentLineage??r.parentLineage,rootSessionId:t.rootSessionId,traceSessionId:traceSessionIdOf(t),sequence:t.sequence,startTimeMs:Date.now(),subagentName:(t.parentLineage??r.parentLineage)?.subagentName};if(isSampledTrace(c)){let n=await e.tracing.turn({...eveActivationMetadata({session:r,turn:u,sessionId:t.sessionId}),agentName:l,identity:eveTurnIdentity({...t,sessionId:t.sessionId}),framework:{name:`eve`,version:e.frameworkVersion},capture:{emit:!0,...await e.capturePolicy.forOperation(t)},reference:c,parent:s?adoptedSpanContext(a):void 0,links:activationLinks(u),startTimeMs:u.startTimeMs,context:hostContext(r.channelAudience,t)});u={...u,context:{...c,traceFlags:n.reference.traceFlags}}}return await e.stateStore.set(`turn`,turnKey(t.sessionId,t.turnId),u),portableSpanContext(u.context,r.decision)};async function recordTerminal(t,n,r,i){await e.stateStore.update(`turn`,turnKey(t,n),e=>({...e,terminal:r.outcome===`failed`?{outcome:`failed`,errorName:i instanceof Error?i.name:void 0,errorMessage:i instanceof Error?i.message:void 0}:r}))}return{ensureSessionContext,prepareSessionTrace,prepareTurnTrace,turnFor,dropped,async turnTerminal(e){await recordTerminal(e.sessionId,e.turnId,{outcome:e.type===`turn.failed`?`failed`:e.type===`turn.cancelled`?`cancelled`:`completed`},e.type===`turn.failed`?e.error:void 0)},async sessionTransition(t){if(t.turnId===void 0)return;t.type===`session.failed`&&await recordTerminal(t.sessionId,t.turnId,{outcome:`failed`},t.error);let n=await e.stateStore.get(`turn`,turnKey(t.sessionId,t.turnId));if(n===void 0)return;let r=await e.stateStore.get(`session`,t.sessionId),i=await turnFor(t.sessionId,t.turnId);if(i!==void 0){let e=eveActivationMetadata({session:r,turn:n,sessionId:t.sessionId});i.attributes(e.attributes??{}),i.links(activationLinks(n)??[]);let a=n.terminal;if(a===void 0)await i.complete({outcomeUnknown:!0});else if(a.outcome===`failed`){let e=Error(a.errorMessage);e.name=a.errorName??`Error`,await i.complete({outcome:`failed`,failed:!0,error:e,errorType:a.errorName})}else await i.complete({outcome:a.outcome})}await e.stateStore.delete(`turn`,turnKey(t.sessionId,t.turnId))}}}function hostContext(e,t){return withOperationConversation(withChannelAudience(ROOT_CONTEXT,e),t)}function activationLinks(e){let t=[];return e.caller?.isRemote===!0&&t.push({context:e.caller,relationship:`agent.dispatch`}),e.channelDelivery?.requestTraceContext!==void 0&&t.push({context:e.channelDelivery.requestTraceContext,relationship:`channel.request`}),t.length===0?void 0:t}function portableSpanContext(e,t){return{decision:t,spanId:e.spanId,traceFlags:e.traceFlags,traceId:e.traceId}}function adoptedSpanContext(e){return{isRemote:`isRemote`in e&&e.isRemote===!0,spanId:e.spanId,traceFlags:e.traceFlags,traceId:e.traceId}}function initialSessionContext(e,t,n){let r=t.traceSeed;if(r!==void 0)return{...adoptedSpanContext(r),traceFlags:n.action===`drop`?0:r.traceFlags};let i=`session:${t.sessionId}`,a=t.parentTraceContext;return{traceId:a!==void 0&&!(`isRemote`in a&&a.isRemote===!0)?a.traceId:e.idGenerator.deriveTraceId(i),spanId:e.idGenerator.deriveSpanId(i),traceFlags:+(n.action===`record`)}}function resolveSessionTraceDecision(e,t,n,r){let i={audience:t,environment:n};return e.parentTraceContext!==void 0&&!isSampledTrace(e.parentTraceContext)?{action:`drop`}:e.traceSeed?.decision===void 0?e.traceSeed===void 0?e.parentTraceContext===void 0?e.agentName===void 0?r===void 0?resolveTracePolicyDecision(!0,i):{action:`drop`}:resolveTracePolicy(r,{agentName:e.agentName,audience:t,channel:{kind:normalizeInstrumentationChannelKind(e.channelKind??e.channelType)},environment:n,principalType:`unknown`}):resolveTracePolicyDecision(isSampledTrace(e.parentTraceContext),i):resolveTracePolicyDecision(isSampledTrace(e.traceSeed),i):readInstrumentationDecision(e.traceSeed.decision)??{action:`drop`}}export{createEveSessionTracing};
package/docs/README.md CHANGED
@@ -25,6 +25,7 @@ Important naming note:
25
25
  | Package a procedure the agent loads only when it applies | [Skills](./skills.mdx) |
26
26
  | Carry state across turns, or shape what the model sees | [State](./concepts/state.md), [Context Control](./concepts/context-control.md) |
27
27
  | Run commands or untrusted code in isolation | [Sandboxes](./sandbox/index.mdx) |
28
+ | Answer voice notes and other audio attachments | [Voice Notes](./patterns/voice-notes.md) |
28
29
  | Delegate work to a specialist child agent | [Subagents](./subagents/index.mdx) |
29
30
  | Run work on a recurring schedule | [Schedules](./schedules.mdx) |
30
31
  | Install an existing integration instead of writing one | [Add Integrations](./install-integrations.mdx) |
@@ -67,6 +67,28 @@ Gateway, share that ID so their Gateway generations can be found together.
67
67
  Direct-provider calls do not receive the option. An authored `gateway.sessionId`
68
68
  takes precedence; AI Gateway hashes IDs longer than 256 characters.
69
69
 
70
+ AI Gateway also uses this ID for
71
+ [session affinity](https://vercel.com/docs/ai-gateway/models-and-providers/session-affinity),
72
+ which tries the provider that served the session's previous call first, among
73
+ the providers your routing settings allow, so that provider can read the prompt
74
+ from its cache. The conversation
75
+ appears as one session in the AI Gateway logs Sessions view. Affinity can move
76
+ the pinned provider ahead of your `order`. To keep `order` exactly, set
77
+ `sessionAffinity: false`:
78
+
79
+ ```ts title="agent/agent.ts"
80
+ import { defineAgent } from "eve";
81
+
82
+ export default defineAgent({
83
+ model: "anthropic/claude-opus-5.5",
84
+ modelOptions: {
85
+ providerOptions: {
86
+ gateway: { order: ["bedrock", "anthropic"], sessionAffinity: false },
87
+ },
88
+ },
89
+ });
90
+ ```
91
+
70
92
  For direct OpenAI and `chatgpt()` model calls, eve sets
71
93
  `providerOptions.openai.promptCacheKey` to a SHA-256 hash of the session ID,
72
94
  which helps OpenAI route a session's calls to the cache that holds its prompt
@@ -140,7 +140,7 @@ This policy controls overlapping eve turns. Chat SDK's separate `concurrency` op
140
140
 
141
141
  The default handlers post the agent's reply back to the thread — no `events` override required. Completed assistant messages are posted as markdown (`{ markdown: … }`) so adapters render rich text and email HTML rather than a raw string.
142
142
 
143
- Streaming is on by default: the channel posts an initial message and edits it as tokens arrive (`message.appended`), throttled by `streamingEditIntervalMs` (default `1000`). Set `streaming: false` for surfaces that deliver one message per turn — email, for example — so the reply posts once on completion instead of editing:
143
+ Streaming is on by default: the channel posts an initial message and edits it as tokens arrive (`message.appended`), throttled by `streamingEditIntervalMs` (default `1000`). Set `streaming: false` for surfaces that deliver one message per turn or can't edit sent messages — email and WhatsApp, for example — so the reply posts once on completion instead of editing:
144
144
 
145
145
  ```ts
146
146
  chatSdkChannel({
@@ -245,7 +245,9 @@ bot.onNewMention(async (thread, message) => {
245
245
  });
246
246
  ```
247
247
 
248
- When the adapter can download an attachment (it sets `fetchData`, as the Slack, Google Chat, WhatsApp, and Twilio adapters do), eve downloads it with the adapter's credentials when the turn runs, not in the webhook, and passes the bytes. Platform file URLs usually need those credentials, so the model provider couldn't open them. eve rebuilds the download with the adapter's `rehydrateAttachment`; an adapter without one can't download after the webhook returns, so its files reach the agent as a short note. A failed download, or a file over 25 MB, also becomes a note. An attachment with only a URL is downloaded by eve when the URL is public `https:`; otherwise it becomes a note.
248
+ When the adapter can download an attachment (it sets `fetchData`, as the Slack, Google Chat, WhatsApp, and Twilio adapters do), eve downloads it with the adapter's credentials when the turn runs, not in the webhook, and passes the bytes. Platform file URLs usually need those credentials, so the model provider couldn't open them. eve rebuilds the download with the adapter's `rehydrateAttachment`; an adapter without one can't download after the webhook returns, so its files reach the agent as a short note. A failed download, or a file over 25 MB, also becomes a note. An attachment with only a URL is downloaded by eve when the URL is public `https:`; otherwise it becomes a note. An attachment with neither a download nor a URL also becomes a note, so the agent still knows a file was sent.
249
+
250
+ Audio attachments, such as voice notes, reach the model as a file label, not as sound. To answer them, give the agent a transcription tool; see [Voice notes](../patterns/voice-notes).
249
251
 
250
252
  See [File uploads](./custom#file-uploads) for how eve stages remote file URLs before the model call.
251
253
 
@@ -341,6 +341,33 @@ export default eveChannel({
341
341
 
342
342
  `onMessage` must return an auth result. Return `title` alongside `auth` to set the title when the dispatch creates a session or sends the first message to a prewarmed session. A successful canonical eve HTTP message always dispatches and therefore always produces or continues a session.
343
343
 
344
+ ### Turn context
345
+
346
+ `context` strings become user messages in durable session history. For context that applies to one turn only, return `clientContext` instead. It replaces the request's [`clientContext`](../guides/client/messages) for that turn: it reaches every model call in the turn and is not kept in session history.
347
+
348
+ `ctx.eve.clientContext` holds the request's `clientContext` exactly as the client sent it, or `undefined` when it sent none. Use it to check or correct client-supplied state before the model sees it:
349
+
350
+ ```ts title="agent/channels/eve.ts"
351
+ import { eveChannel, defaultEveAuth } from "eve/channels/eve";
352
+ import { vercelOidc } from "eve/channels/auth";
353
+
354
+ import { getVerifiedPlan } from "../lib/billing";
355
+
356
+ export default eveChannel({
357
+ auth: [vercelOidc()],
358
+ async onMessage(ctx) {
359
+ const auth = defaultEveAuth(ctx);
360
+ const plan = await getVerifiedPlan(auth);
361
+ return { auth, clientContext: [`The user's plan is ${plan}.`] };
362
+ },
363
+ });
364
+ ```
365
+
366
+ - Omit `clientContext` to keep the request's context. Return `[]` to drop it.
367
+ - The returned value takes the same shape as request `clientContext` (`ClientContextValue`, exported from `eve/channels/eve`): a string, an array of strings, or a JSON object. eve turns it into model messages the same way.
368
+ - eve labels client-supplied entries `Client context:` for the model. Returned context is sent without the label.
369
+ - `onMessage` runs only for requests with a message. An input response keeps the context its request sent.
370
+
344
371
  ## Clients
345
372
 
346
373
  The browser side of this API lives in the [Frontend](../guides/frontend/overview) docs, where `useEveAgent` drives the eve channel from React UI.
@@ -34,8 +34,10 @@ Set `turnPolicy: "queue"` when every response should finish before Photon starts
34
34
 
35
35
  Images and other files people send reach the agent as file parts. Photon's
36
36
  webhook lists each file without its bytes, so eve downloads it from Photon by
37
- its attachment id with the project's credentials. A failed download, or a file
38
- over 25 MB, reaches the agent as a short note.
37
+ its attachment id with the project's credentials. The webhook omits the id
38
+ for voice notes, so eve refetches those messages from Photon to get it. A
39
+ failed download, a file over 25 MB, or a file eve can't find an id for reaches
40
+ the agent as a short note.
39
41
 
40
42
  Customize inbound dispatch with `onMessage`. Return `null` to ignore a message, or return `title` alongside `auth` to set the title when the dispatch starts a run:
41
43
 
@@ -750,6 +750,8 @@ The agent receives the files attached to the message that reaches it. When a men
750
750
 
751
751
  Images, PDFs, audio, video, and other files all reach the agent. See [Inbound attachments](../sandbox#inbound-attachments) for which files the model reads directly and which it opens with tools. Files shared through an integration such as Google Drive or Dropbox are links to the other service, not Slack files, so eve skips them; their link stays in the message text. A download that fails or takes longer than 30 seconds reaches the model as a note.
752
752
 
753
+ Slack voice clips reach the model as an audio file label, not as sound. To answer them, give the agent a transcription tool; see [Voice notes](../patterns/voice-notes).
754
+
753
755
  Private file downloads resolve credentials from the session's app installation workspace and require the Slack bot token's `files:read` scope. Add the scope and reinstall the Slack app before sending attachments.
754
756
 
755
757
  Downloads cover Slack's own file hosts plus URLs under a configured [`api.fileBaseUrl`](#point-the-channel-at-a-different-slack-api-host), and travel on `api.fetch` when one is configured.
@@ -27,7 +27,11 @@ When a Vercel production deployment changes, a new delivery to an idle session h
27
27
 
28
28
  Before taking ownership, the target deployment upgrades the checkpoint when an older eve release wrote it, then checks that no pending work remains. A deployment accepts checkpoints from eve 0.66.0 and later, so upgrading eve does not leave long-lived sessions, such as a Slack thread, on their old deployment. An older release cannot read a newer checkpoint: after a rollback, a session that already moved to the newer deployment stays there. Extension state saved before eve 0.69 moves to its mount when exactly one mount of that extension package defines that state. State that the target deployment no longer defines, such as a removed `defineState` key, is dropped, and the target deployment's runtime logs show a `dropping unknown context key during deserialization` warning. When two or more mounts of one extension package define that state, eve cannot choose an owner, and the session stays on its current deployment. Idle subagent sessions that eve 0.66–0.68 kept for later reuse are stopped after the handoff. Authored session state remains opaque to this validation.
29
29
 
30
- If the target deployment rejects the session, the previous owner keeps it and processes the triggering delivery. When the target deployment cannot read the checkpoint, its Vercel runtime logs show a `session handoff refused` warning with the checkpoint version and the reason. Owners on this release or later also write a `session handoff failed` warning to their own runtime logs, with the target deployment and the reason.
30
+ Messages that arrive while a handoff is in progress are accepted right away and are not lost. The new owner processes them after the triggering delivery, in the order they arrived, whichever run accepted them.
31
+
32
+ The previous owner forwards the messages it accepted as soon as the new owner has taken over the session. Until they arrive, the new owner holds back everything it receives itself, including replies to tool runs and subagents that its first turn started, and processes it afterwards. A cancel still stops the new owner's running turn as soon as it arrives. If either run ends before the handoff settles, the other stops waiting for it.
33
+
34
+ If the target deployment rejects the session, the previous owner keeps it and processes the triggering delivery, followed by any messages that arrived during the attempt. When the target deployment cannot read the checkpoint, its Vercel runtime logs show a `session handoff refused` warning with the checkpoint version and the reason. Owners on this release or later also write a `session handoff failed` warning to their own runtime logs, with the target deployment and the reason.
31
35
 
32
36
  eve does not move live work between deployments. A session with a running turn, pending human input or authorization, queued messages, or active tasks and subagents stays on its current deployment. After that work settles, a later delivery can move the idle session; the earlier deployment signal is not remembered.
33
37
 
@@ -35,7 +39,9 @@ eve does not move live work between deployments. A session with a running turn,
35
39
 
36
40
  Every workflow run records each step it executes, so a session that ran on one run for weeks would carry its whole history in that run. To bound this, eve moves a session to a fresh run on its current deployment after [compaction](./default-harness#compaction) rewrites the conversation history. This applies to automatic compaction during a turn and to a client or channel compaction request.
37
41
 
38
- The move happens once the turn settles and nothing else is waiting: no queued messages, running tasks or subagents, or pending human input. The new run receives the compacted history and settled state, then waits for the next message. If a single message reaches an otherwise idle session before the move completes, eve moves the session with that message, and the message's turn runs on the new run. If work is still pending, eve tries again after a later turn settles. The session keeps its ID, event stream, continuation addresses, and deadline; compaction never extends [`sessionTimeoutMs`](../agent-config#runtime-limits). If the new run cannot take ownership, the current run keeps the session, writes a `session handoff failed` warning to its runtime logs, and tries again later.
42
+ The move happens once the turn settles and nothing else is waiting: no queued messages, running tasks or subagents, or pending human input. The new run receives the compacted history and settled state, then waits for the next message. A message that is already waiting when the turn settles runs on the current run first, and the session moves at the next point where nothing is waiting. A message that arrives after the move starts is accepted right away and runs once, on the new run. If work is still pending, eve tries again after a later turn settles. The session keeps its ID, event stream, continuation addresses, and deadline; compaction never extends [`sessionTimeoutMs`](../agent-config#runtime-limits). If the new run cannot take ownership, the current run keeps the session, writes a `session handoff failed` warning to its runtime logs, and tries again later.
43
+
44
+ Both kinds of handoff need a Workflow world that can move a session's addresses from one run to another without releasing them, and can keep a released address reserved for a while (the `hookForceClaim` and `hookRetention` capabilities). Vercel Workflow, the local world, and `@workflow/world-postgres` 5.0.4 and later provide both. On a world without them, sessions never move between runs: deployment and compaction handoffs are skipped, and a session's run history grows for as long as the session lives. The first skipped attempt writes a `session handoff unavailable` warning to the runtime logs.
39
45
 
40
46
  ### Stranded sessions
41
47
 
@@ -253,11 +259,14 @@ the returned handle. That identity lookup does not block workflow resumption.
253
259
  ID-addressed delivery already knows the session ID and needs no hook metadata
254
260
  lookup. An ambiguous delivery failure does not trigger session creation.
255
261
 
256
- While a handoff is in progress, the session's addresses are briefly unowned.
257
- The releasing owner leaves a short-lived marker for each address, so a
258
- delivery that lands in that interval retries until the successor claims the
259
- address instead of being reported as an inactive session. A channel therefore
260
- never starts a replacement session for an address that is mid-handoff.
262
+ A handoff never leaves the session's addresses without an owner. The previous
263
+ owner keeps every address until the new run takes all of them over, so a
264
+ delivery during a handoff is accepted immediately and is never reported as an
265
+ inactive session. An owner still running an earlier eve release gives up its
266
+ addresses briefly during a handoff and leaves a short-lived marker for each
267
+ one; a delivery that lands in that interval retries until the successor
268
+ claims the address. Either way, a channel never starts a replacement session
269
+ for an address that is changing owners.
261
270
 
262
271
  When a session is waiting, a delivery through its ID or any claimed channel address wakes it and starts the next turn. Message sends default to `turnPolicy: "steer"`. Before assistant output begins, the session owner signals pending model generation to stop, then applies the correction within the same turn and with the same turn ID. This includes model requests that perform provider-managed search before answering; interrupted search work may run again. Reasoning and search progress do not count as assistant output.
263
272
 
@@ -188,6 +188,26 @@ Gateway-routed model calls propagate the configured OpenTelemetry context at
188
188
  each provider request. Eve-owned baggage is removed before forwarding. Gateway
189
189
  controls whether its spans are sampled and which destinations receive them.
190
190
 
191
+ When AI Gateway reports
192
+ [session affinity](https://vercel.com/docs/ai-gateway/models-and-providers/session-affinity)
193
+ for a model call, the `agent.step` span records it. These attributes appear even
194
+ when the trace content policy omits outputs.
195
+
196
+ `vercel.ai_gateway.client_session_id` is the session ID AI Gateway used for
197
+ affinity. It is not the eve session ID that `vercel.session_id` records. It
198
+ equals `gen_ai.conversation.id` unless you set `gateway.sessionId` yourself or
199
+ the ID is longer than 256 characters, in which case AI Gateway hashes it. An
200
+ authored `gateway.sessionId` is recorded verbatim, so don't put personal data in
201
+ it.
202
+
203
+ | Attribute | Value |
204
+ | ----------------------------------------------- | --------------------------------------------------------------- |
205
+ | `vercel.ai_gateway.client_session_id` | Session ID AI Gateway used |
206
+ | `vercel.ai_gateway.client_session_id_source` | `explicit`, `agent`, or `fingerprint` |
207
+ | `vercel.ai_gateway.affinity.outcome` | What affinity did, such as `promoted`, `confirmed`, or `no_pin` |
208
+ | `vercel.ai_gateway.affinity.pinned_provider` | Provider the session's pin named |
209
+ | `vercel.ai_gateway.affinity.displaced_provider` | Provider that would have gone first, when the pin moved it |
210
+
191
211
  ## Local traces
192
212
 
193
213
  `eve dev` records local traces by default. Omitting
@@ -6,6 +6,7 @@
6
6
  "durable-cross-channel-notifications",
7
7
  "multi-tenant-auth",
8
8
  "multi-tenant-approvals",
9
+ "voice-notes",
9
10
  "../protocols/ucp"
10
11
  ]
11
12
  }
@@ -0,0 +1,67 @@
1
+ ---
2
+ title: "Voice Notes"
3
+ description: "Transcribe voice notes, voice messages, and other audio attachments with an AI Gateway transcription model so an eve agent can answer them."
4
+ ---
5
+
6
+ Voice notes and other audio files reach your agent, but by default the model does not transcribe them. To give your agent the ability to answer a voice note, you will need to add a tool that transcribes the staged file.
7
+
8
+ This works for any channel that delivers audio as a file, such as iMessage voice notes through [Photon](../channels/photon), [Slack](../channels/slack#attachments) voice clips, and files sent through the [eve](../channels/eve) and [Chat SDK](../channels/chat-sdk#attachments) channels. See [Inbound attachments](../sandbox#inbound-attachments) for how eve stages files.
9
+
10
+ ## Add a transcription tool
11
+
12
+ ```ts title="agent/tools/transcribe_audio.ts"
13
+ import { posix } from "node:path";
14
+ import { transcribe } from "ai";
15
+ import { defineTool } from "eve/tools";
16
+ import { z } from "zod";
17
+
18
+ const TRANSCRIPTION_MODEL = "openai/whisper-1";
19
+ const ATTACHMENTS_DIR = "/workspace/.eve/attachments/";
20
+
21
+ export default defineTool({
22
+ description:
23
+ "Transcribe an audio attachment, such as a voice note, to text. Pass the path from its `Attached file` label.",
24
+ inputSchema: z.object({
25
+ path: z.string().describe(`Attachment path under ${ATTACHMENTS_DIR}`),
26
+ }),
27
+ outputSchema: z.object({ transcript: z.string() }),
28
+ async execute({ path }, ctx) {
29
+ const file = posix.normalize(path);
30
+ if (!file.startsWith(ATTACHMENTS_DIR)) {
31
+ throw new Error(`Only attachments under ${ATTACHMENTS_DIR} can be transcribed.`);
32
+ }
33
+
34
+ const sandbox = await ctx.getSandbox();
35
+ const audio = await sandbox.readBinaryFile({ path: file, abortSignal: ctx.abortSignal });
36
+ if (!audio) {
37
+ throw new Error(`Attachment not found: ${file}. Ask the user to send the voice note again.`);
38
+ }
39
+
40
+ try {
41
+ const { text } = await transcribe({
42
+ model: TRANSCRIPTION_MODEL,
43
+ audio,
44
+ abortSignal: ctx.abortSignal,
45
+ });
46
+ return { transcript: text.trim() };
47
+ } catch (error) {
48
+ const reason = error instanceof Error ? error.message : String(error);
49
+ throw new Error(
50
+ `Transcription failed (${reason}). Tell the user you couldn't understand the voice note and ask them to type it.`,
51
+ );
52
+ }
53
+ },
54
+ });
55
+ ```
56
+
57
+ `transcribe()` uses the same credentials as your eve agent.
58
+
59
+ ## Tell the agent when to transcribe
60
+
61
+ ```md title="agent/instructions.md"
62
+ When a message has an audio attachment (`Attached file ... (audio/...)`), such as a voice note, call `transcribe_audio` with its path first and treat the transcript as what the user said. Do not mention the transcription step unless it fails.
63
+ ```
64
+
65
+ ## When transcription fails
66
+
67
+ When `execute` throws, eve gives the error to the model as a tool error, which it can relay to the user. See [When a tool throws](../tools#when-a-tool-throws). A file over the channel's upload limit, 25 MB by default, or a file eve can't download reaches the agent as a short note instead of a file.
@@ -162,6 +162,8 @@ Before every model call, eve reads the referenced files back from the sandbox. E
162
162
 
163
163
  Other files, such as CSV, HEIC, audio, and video, reach the model as the label alone. eve checks image and PDF media types against the file's bytes when it stages the file. A file whose bytes don't match its declared image or PDF type is staged as `application/octet-stream`, so a provider never receives bytes it would reject.
164
164
 
165
+ To let the agent answer voice notes and other audio, add a tool that transcribes the staged file. See [Voice notes](/docs/patterns/voice-notes).
166
+
165
167
  If a referenced file is missing, or its bytes no longer match what eve staged because something in the sandbox overwrote it, the model receives a `FileNotFound` note in its place.
166
168
 
167
169
  A file that eve can't stage never enters session history. When a channel's `fetchFile` fails, or a file part's `data` is not bytes, base64, a `data:` URL, or a URL, the model receives a note that names the file instead.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eve",
3
- "version": "0.81.0",
3
+ "version": "0.81.1",
4
4
  "private": false,
5
5
  "description": "Filesystem-first framework for durable backend AI agents that run anywhere.",
6
6
  "keywords": [