UNPKG

eve

Version:

Filesystem-first framework for durable backend AI agents that run anywhere.

1 lines • 5.94 kB
import{createLogger,formatError}from"#internal/logging.js";import{abandonInstrumentationState,instrumentationStateSlot,isInstrumentationStateAbandoned,releaseAllInstrumentationAttemptState,releaseAllInstrumentationState,releaseAllInstrumentationTurnState,takeInstrumentationActionScopes}from"#instrumentation/state.js";import{withInstrumentationDecision,withoutInstrumentationContent}from"#instrumentation/content.js";import{resolveTracePolicy}from"#shared/trace-policy.js";const log=createLogger(`harness.instrumentation-dispatch`);function createInstrumentationDispatcher(e,t){let n=t.handlerTimeoutMs??5e3,r=normalizeDispatchGroups(e),i=[...r.serialBefore,...r.parallel,...r.serialAfter],a=new Set;function forTrace(e){let t=new WeakMap,o=new Map(i.map(t=>[t,resolveTracePolicy(t.tracePolicy,e,e=>{a.has(t)||(a.add(t),log.warn(`instrumentation provider trace policy failed`,{error:formatError(e),provider:t.name}))})])),s=[...o.values()].some(e=>e.action===`record`&&e.recordInputs),c=[...o.values()].some(e=>e.action===`record`&&e.recordOutputs),l=s||c,publish=async e=>{let i=snapshotInstrumentationEvent(withInstrumentationDecision(e,{action:`record`,recordInputs:s,recordOutputs:c}),t,e),a=i.type===`session.completed`||i.type===`session.failed`,l=i.type===`turn.cancelled`||i.type===`turn.failed`;if(a||l){let e=takeInstrumentationActionScopes(i.sessionId,l?i.turnId:void 0),t=terminalActionFailure(i);for(let n of e)await publish({...t,idempotencyKey:n.idempotencyKey,scope:n.scope,type:`action.failed`})}let u=new Map,visibleEvent=e=>{let t=o.get(e);if(t===void 0||t.action===`drop`)return withoutInstrumentationContent(i);if(t.recordInputs&&t.recordOutputs)return i;let n=`${String(t.recordInputs)}:${String(t.recordOutputs)}`,r=u.get(n);return r===void 0&&(r=withInstrumentationDecision(i,t),u.set(n,r)),r},admitted=e=>o.get(e)?.action===`record`;try{try{for(let e of r.serialBefore)admitted(e)&&await dispatchToProvider(e,i,n,()=>visibleEvent(e));let e=r.parallel.filter(admitted);if(e.length===1){let t=e[0];await dispatchToProvider(t,i,n,()=>visibleEvent(t))}else if(e.length>1){let t=(await Promise.allSettled(e.map(e=>dispatchToProvider(e,i,n,()=>visibleEvent(e))))).find(e=>e.status===`rejected`);if(t!==void 0)throw t.reason}}finally{for(let e of r.serialAfter)admitted(e)&&await dispatchToProvider(e,i,n,()=>visibleEvent(e))}}finally{releaseTerminalState(i)}};return{capturesContent:l,capturesInputs:s,capturesOutputs:c,forTrace,publish}}let o=!1;return{capturesContent:!1,forTrace,async publish(e){o||(o=!0,log.debug(`instrumentation event published without trace binding`,{eventType:e.type}))}}}function snapshotInstrumentationEvent(e,t,n){let r=t.get(n);if(r!==void 0)return r;let i=snapshotPlainValue(e,t);return t.set(n,i),i}function snapshotPlainValue(e,t){if(Array.isArray(e)){let n=t.get(e);if(n!==void 0)return n;let r=[];t.set(e,r);for(let n of e)r.push(snapshotPlainValue(n,t));return Object.freeze(r)}if(typeof e!=`object`||!e)return e;let n;try{n=Object.getPrototypeOf(e)}catch{return e}if(n!==Object.prototype&&n!==null)return e;let r=t.get(e);if(r!==void 0)return r;let i={};t.set(e,i);for(let[n,r]of Object.entries(e))i[n]=snapshotPlainValue(r,t);return Object.freeze(i)}function normalizeDispatchGroups(e){if(!Array.isArray(e)){let t=e;return{parallel:t.parallel??[],serialAfter:t.serialAfter??[],serialBefore:t.serialBefore??[]}}return{parallel:[],serialAfter:[],serialBefore:e.map((e,t)=>({...e,name:e.name??`provider-${String(t)}`}))}}async function dispatchToProvider(e,t,n,r){let i=t.type.endsWith(`.started`)||t.type===`input.requested`,a=stateOwner(t),o=e.name,s=e.stateNamespace??o;if(isInstrumentationStateAbandoned(s,t.idempotencyKey))return;let c=e.events?.[t.type];if(c===void 0)return;let l=instrumentationStateSlot(s,t.idempotencyKey,a);try{await withTimeout(async()=>{let t=r();await c(e.projectEvent===void 0?t:await e.projectEvent(t),{state:l})},n,()=>{l.revoke(),i&&abandonInstrumentationState(s,t.idempotencyKey,a)})||log.warn(`instrumentation provider timed out`,{boundary:t.type,provider:o,timeoutMs:n})}catch(e){log.warn(`instrumentation provider failed`,{boundary:t.type,error:formatError(e),provider:o})}finally{l.revoke()}}async function withTimeout(e,t,n){let r;try{return await Promise.race([Promise.resolve(e()).then(()=>!0),new Promise(e=>{r=setTimeout(()=>{n(),e(!1)},t)})])}finally{clearTimeout(r)}}function stateOwner(e){return e.type===`channel.delivery.started`||e.type===`channel.delivery.cancelled`||e.type===`channel.delivery.completed`||e.type===`channel.delivery.failed`||e.type===`memory.operation.started`||e.type===`memory.operation.completed`||e.type===`memory.operation.failed`?{sessionId:e.sessionId,turnId:e.turnId}:`scope`in e?e.type.startsWith(`action.`)||e.type.startsWith(`input.`)?{sessionId:e.scope.sessionId,turnId:e.scope.turnId}:e.type.startsWith(`model.call.`)||e.type.startsWith(`tool.call.`)||e.type.startsWith(`step.attempt.`)?{attemptId:e.scope.attemptId}:{}:{}}function releaseTerminalState(e){isTerminal(e.type)&&releaseAllInstrumentationState(e.idempotencyKey),(e.type===`step.attempt.completed`||e.type===`step.attempt.failed`)&&releaseAllInstrumentationAttemptState(e.scope.attemptId),(e.type===`session.completed`||e.type===`session.failed`)&&releaseAllInstrumentationTurnState(e.sessionId),(e.type===`turn.cancelled`||e.type===`turn.failed`)&&releaseAllInstrumentationTurnState(e.sessionId,e.turnId)}function terminalActionFailure(e){return e.type===`session.failed`||e.type===`turn.failed`?{error:e.error,outcome:`failed`}:e.type===`turn.cancelled`?{error:Error(`The action was cancelled with its turn.`),errorCode:`ACTION_CANCELLED`,outcome:`cancelled`}:{error:Error(`The session completed before the action settled.`),errorCode:`ACTION_ABANDONED`,outcome:`abandoned`}}function isTerminal(e){return e.endsWith(`.completed`)||e.endsWith(`.failed`)||e.endsWith(`.cancelled`)||e===`input.resolved`}export{createInstrumentationDispatcher};