eve 0.33.3 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/dist/src/channel/channel-address.d.ts +3 -6
- package/dist/src/channel/channel-address.js +1 -1
- package/dist/src/channel/channel-operations.d.ts +2 -3
- package/dist/src/channel/delivery-metadata.d.ts +9 -0
- package/dist/src/channel/delivery-metadata.js +1 -0
- package/dist/src/channel/routes.d.ts +1 -0
- package/dist/src/channel/session.d.ts +3 -4
- package/dist/src/channel/session.js +1 -1
- package/dist/src/channel/types.d.ts +20 -3
- package/dist/src/chunks/{use-eve-agent-B-Hky2f4.js → use-eve-agent-BNdcle_M.js} +33 -0
- package/dist/src/chunks/{use-eve-agent-3oBnxzhV.js → use-eve-agent-FJkEMXYk.js} +33 -0
- package/dist/src/cli/dev/tui/traces/trace-conversation.js +1 -1
- package/dist/src/client/message-reducer.js +1 -1
- package/dist/src/compiled/.vendor-stamp.json +1 -1
- package/dist/src/compiled/@opentelemetry/api/index.d.ts +32 -3
- package/dist/src/compiled/@vercel/otel/index.d.ts +45 -2
- package/dist/src/compiler/extension-compatibility.d.ts +12 -12
- package/dist/src/compiler/extension-compatibility.js +1 -1
- package/dist/src/compiler/manifest.js +1 -1
- package/dist/src/compiler/normalize-agent-config.js +1 -1
- package/dist/src/context/build-dynamic-tools.d.ts +7 -0
- package/dist/src/context/build-dynamic-tools.js +1 -1
- package/dist/src/context/dynamic-tool-lifecycle.d.ts +7 -0
- package/dist/src/context/dynamic-tool-lifecycle.js +1 -1
- package/dist/src/context/keys.d.ts +13 -1
- package/dist/src/context/keys.js +1 -1
- package/dist/src/discover/filesystem.d.ts +1 -1
- package/dist/src/discover/filesystem.js +1 -1
- package/dist/src/evals/cli/eval.js +2 -2
- package/dist/src/evals/runner/derive-run-facts.js +1 -1
- package/dist/src/execution/dispatch-runtime-actions-step.d.ts +1 -5
- package/dist/src/execution/dispatch-runtime-actions-step.js +1 -1
- package/dist/src/execution/dispatch-turn-step.d.ts +5 -0
- package/dist/src/execution/dispatch-turn-step.js +1 -0
- package/dist/src/execution/durable-session-migrations/turn-workflow.d.ts +1 -0
- package/dist/src/execution/node-step.js +1 -1
- package/dist/src/execution/parked-delivery-wait.d.ts +1 -1
- package/dist/src/execution/parked-delivery-wait.js +1 -1
- package/dist/src/execution/remote-agent-dispatch.d.ts +2 -1
- package/dist/src/execution/remote-agent-dispatch.js +1 -1
- package/dist/src/execution/route-child-delivery.d.ts +3 -4
- package/dist/src/execution/route-child-delivery.js +1 -1
- package/dist/src/execution/route-proxied-deliver-step.d.ts +34 -0
- package/dist/src/execution/route-proxied-deliver-step.js +1 -0
- package/dist/src/execution/runtime-context.js +1 -1
- package/dist/src/execution/session-command-inbox.d.ts +2 -2
- package/dist/src/execution/session-command-wire.js +1 -1
- package/dist/src/execution/settle-cancelled-turn-step.js +1 -1
- package/dist/src/execution/subagent-adapter.js +1 -1
- package/dist/src/execution/subagent-event-proxy-step.js +1 -1
- package/dist/src/execution/tool-auth.d.ts +9 -0
- package/dist/src/execution/tool-auth.js +1 -1
- package/dist/src/execution/turn-control-receiver.js +1 -1
- package/dist/src/execution/turn-dispatch.js +1 -1
- package/dist/src/execution/turn-workflow.js +1 -1
- package/dist/src/execution/workflow-entry.js +1 -1
- package/dist/src/execution/workflow-runtime.js +1 -1
- package/dist/src/execution/workflow-steps.d.ts +1 -27
- package/dist/src/execution/workflow-steps.js +1 -1
- package/dist/src/harness/ai-sdk-hook-bridge.js +1 -1
- package/dist/src/harness/ai-sdk-telemetry.d.ts +19 -0
- package/dist/src/harness/ai-sdk-telemetry.js +1 -0
- package/dist/src/harness/approval-candidates.d.ts +109 -0
- package/dist/src/harness/approval-candidates.js +1 -0
- package/dist/src/harness/approval-delivery-coordinator.d.ts +39 -0
- package/dist/src/harness/approval-delivery-coordinator.js +1 -0
- package/dist/src/harness/authorization.d.ts +1 -0
- package/dist/src/harness/authorization.js +1 -1
- package/dist/src/harness/channel-delivery-instrumentation.d.ts +25 -0
- package/dist/src/harness/channel-delivery-instrumentation.js +1 -0
- package/dist/src/harness/execute-tool.d.ts +1 -0
- package/dist/src/harness/hitl/approval-input-requests.js +1 -1
- package/dist/src/harness/hitl/pending-input-resolution.d.ts +3 -0
- package/dist/src/harness/hitl/pending-input-resolution.js +1 -1
- package/dist/src/harness/hitl/question-input-requests.js +1 -1
- package/dist/src/harness/hitl/session-limit-input-requests.js +1 -1
- package/dist/src/harness/input-extraction.js +1 -1
- package/dist/src/harness/input-request-resolution.d.ts +22 -0
- package/dist/src/harness/input-request-resolution.js +1 -0
- package/dist/src/harness/input-requests.d.ts +1 -0
- package/dist/src/harness/input-requests.js +1 -1
- package/dist/src/harness/instrumentation-config.d.ts +9 -4
- package/dist/src/harness/instrumentation-config.js +1 -1
- package/dist/src/harness/instrumentation-content.d.ts +5 -0
- package/dist/src/harness/instrumentation-content.js +1 -0
- package/dist/src/harness/instrumentation-dispatch.d.ts +2 -0
- package/dist/src/harness/instrumentation-dispatch.js +1 -0
- package/dist/src/harness/instrumentation-lifecycle.d.ts +388 -65
- package/dist/src/harness/instrumentation-lifecycle.js +1 -1
- package/dist/src/harness/instrumentation-native-events.d.ts +10 -1
- package/dist/src/harness/instrumentation-native-events.js +1 -1
- package/dist/src/harness/instrumentation-providers.d.ts +50 -0
- package/dist/src/harness/instrumentation-providers.js +1 -0
- package/dist/src/harness/instrumentation-runtime.d.ts +3 -0
- package/dist/src/harness/instrumentation-runtime.js +1 -1
- package/dist/src/harness/instrumentation-setup-context.d.ts +12 -0
- package/dist/src/harness/instrumentation-setup-context.js +1 -0
- package/dist/src/harness/instrumentation-state.d.ts +59 -0
- package/dist/src/harness/instrumentation-state.js +1 -0
- package/dist/src/harness/messages.d.ts +2 -1
- package/dist/src/harness/messages.js +1 -1
- package/dist/src/harness/pending-input-batches.d.ts +2 -0
- package/dist/src/harness/pending-input-batches.js +1 -1
- package/dist/src/harness/runtime-action-settlement-state.d.ts +3 -0
- package/dist/src/harness/runtime-action-settlement-state.js +1 -0
- package/dist/src/harness/runtime-actions.js +1 -1
- package/dist/src/harness/stale-input-responses.js +1 -1
- package/dist/src/harness/tool-call-action.d.ts +9 -0
- package/dist/src/harness/tool-call-action.js +1 -0
- package/dist/src/harness/tool-loop.js +2 -2
- package/dist/src/harness/tools.js +1 -1
- package/dist/src/harness/types.d.ts +15 -1
- package/dist/src/internal/application/compiled-artifacts.d.ts +13 -4
- package/dist/src/internal/application/compiled-artifacts.js +5 -4
- package/dist/src/internal/application/dev-environment.d.ts +12 -0
- package/dist/src/internal/application/dev-environment.js +1 -1
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/internal/authored-definition/approval.d.ts +3 -0
- package/dist/src/internal/authored-definition/approval.js +1 -0
- package/dist/src/internal/authored-definition/connection.js +1 -1
- package/dist/src/internal/authored-definition/core.js +1 -1
- package/dist/src/internal/authored-definition/schema-backed.js +1 -1
- package/dist/src/internal/instrumentation-layout.d.ts +28 -0
- package/dist/src/internal/instrumentation-layout.js +1 -0
- package/dist/src/internal/materialized-authored-modules.d.ts +13 -2
- package/dist/src/internal/materialized-authored-modules.js +1 -1
- package/dist/src/internal/nitro/host/create-application-nitro.js +1 -1
- package/dist/src/internal/nitro/host/dev-host-fingerprint.js +1 -1
- package/dist/src/internal/nitro/host/local-tracing-runtime-plugin.d.ts +5 -1
- package/dist/src/internal/nitro/host/local-tracing-runtime-plugin.js +1 -1
- package/dist/src/internal/nitro/routes/channel-dispatch.js +1 -1
- package/dist/src/internal/nitro/routes/channel-request-instrumentation.d.ts +2 -3
- package/dist/src/internal/nitro/routes/channel-request-instrumentation.js +1 -1
- package/dist/src/packages/eve-catalog/src/index.js +1 -1
- package/dist/src/protocol/message.d.ts +36 -1
- package/dist/src/protocol/message.js +1 -1
- package/dist/src/protocol/traceparent.d.ts +10 -0
- package/dist/src/protocol/traceparent.js +1 -0
- package/dist/src/public/channels/eve.js +1 -1
- package/dist/src/public/channels/github/defaults.d.ts +1 -0
- package/dist/src/public/channels/github/defaults.js +5 -2
- package/dist/src/public/channels/github/githubChannel.d.ts +3 -3
- package/dist/src/public/channels/github/githubChannel.js +1 -1
- package/dist/src/public/channels/slack/defaults.js +3 -3
- package/dist/src/public/channels/slack/interactions.js +1 -1
- package/dist/src/public/channels/slack/slackChannel.d.ts +11 -3
- package/dist/src/public/channels/slack/slackChannel.js +1 -1
- package/dist/src/public/definitions/approval.d.ts +57 -16
- package/dist/src/public/definitions/approval.js +1 -1
- package/dist/src/public/definitions/channel.d.ts +2 -2
- package/dist/src/public/definitions/channel.js +1 -1
- package/dist/src/public/definitions/hook.d.ts +4 -2
- package/dist/src/public/instrumentation/index.d.ts +32 -17
- package/dist/src/public/instrumentation/index.js +1 -1
- package/dist/src/public/instrumentation/otel.d.ts +31 -0
- package/dist/src/public/instrumentation/otel.js +1 -0
- package/dist/src/public/instrumentation/provider.d.ts +117 -0
- package/dist/src/public/instrumentation/provider.js +1 -0
- package/dist/src/public/tools/approval/approval-helpers.d.ts +4 -4
- package/dist/src/public/tools/index.d.ts +1 -1
- package/dist/src/runtime/actions/results.d.ts +3 -3
- package/dist/src/runtime/attributes/emit.js +1 -1
- package/dist/src/runtime/connections/principal.d.ts +3 -0
- package/dist/src/runtime/connections/principal.js +1 -1
- package/dist/src/runtime/connections/scoped-authorization.d.ts +3 -5
- package/dist/src/runtime/connections/scoped-authorization.js +1 -1
- package/dist/src/runtime/connections/types.d.ts +5 -0
- package/dist/src/runtime/resolve-agent.js +1 -1
- package/dist/src/runtime/resolve-tool.js +1 -1
- package/dist/src/setup/index.d.ts +1 -0
- package/dist/src/setup/index.js +1 -1
- package/dist/src/setup/scaffold/create/project.js +1 -1
- package/dist/src/setup/scaffold/version-tokens.js +1 -1
- package/dist/src/setup/verified-remote-agent.d.ts +30 -0
- package/dist/src/setup/verified-remote-agent.js +1 -0
- package/dist/src/shared/agent-definition.d.ts +9 -0
- package/dist/src/shared/channel-definition.d.ts +3 -1
- package/dist/src/svelte/index.js +1 -1
- package/dist/src/svelte/use-eve-agent.js +1 -1
- package/dist/src/tracing/agent-action-instrumentation.d.ts +31 -0
- package/dist/src/tracing/agent-action-instrumentation.js +1 -0
- package/dist/src/tracing/agent-approval-instrumentation.d.ts +13 -0
- package/dist/src/tracing/agent-approval-instrumentation.js +1 -0
- package/dist/src/tracing/agent-channel-delivery-instrumentation.d.ts +13 -0
- package/dist/src/tracing/agent-channel-delivery-instrumentation.js +1 -0
- package/dist/src/tracing/agent-otel-content.d.ts +6 -0
- package/dist/src/tracing/agent-otel-content.js +2 -2
- package/dist/src/tracing/agent-otel-provider.d.ts +7 -7
- package/dist/src/tracing/agent-otel-provider.js +2 -2
- package/dist/src/tracing/agent-otel-usage.d.ts +4 -0
- package/dist/src/tracing/agent-otel-usage.js +1 -0
- package/dist/src/tracing/agent-span-id-generator.d.ts +2 -0
- package/dist/src/tracing/agent-span-id-generator.js +1 -1
- package/dist/src/tracing/agent-tool-instrumentation.d.ts +28 -0
- package/dist/src/tracing/agent-tool-instrumentation.js +1 -0
- package/dist/src/tracing/agent-trace-context-store.d.ts +8 -1
- package/dist/src/tracing/agent-trace-context-store.js +1 -1
- package/dist/src/tracing/agent-trace-span-processor.d.ts +0 -1
- package/dist/src/tracing/agent-trace-span-processor.js +1 -1
- package/dist/src/tracing/agent-trace-state.d.ts +33 -3
- package/dist/src/tracing/agent-trace-state.js +1 -1
- package/dist/src/tracing/batch-span-processor.d.ts +20 -0
- package/dist/src/tracing/batch-span-processor.js +1 -0
- package/dist/src/tracing/content-attributes.d.ts +28 -0
- package/dist/src/tracing/content-attributes.js +1 -0
- package/dist/src/tracing/content-span-processor.d.ts +19 -0
- package/dist/src/tracing/content-span-processor.js +1 -0
- package/dist/src/tracing/install-instrumentation-runtime.d.ts +19 -0
- package/dist/src/tracing/install-instrumentation-runtime.js +1 -0
- package/dist/src/tracing/local-instrumentation-runtime.js +1 -1
- package/dist/src/tracing/local-traces.d.ts +56 -0
- package/dist/src/tracing/local-traces.js +1 -0
- package/dist/src/tracing/otel-declaration.d.ts +150 -0
- package/dist/src/tracing/otel-declaration.js +1 -0
- package/dist/src/tracing/otel-registration.d.ts +21 -0
- package/dist/src/tracing/otel-registration.js +1 -0
- package/dist/src/tracing/vercel-runtime-span-exporter.d.ts +5 -0
- package/dist/src/tracing/vercel-runtime-span-exporter.js +1 -0
- package/dist/src/vue/index.js +1 -1
- package/dist/src/vue/use-eve-agent.js +1 -1
- package/docs/guides/instrumentation.md +22 -0
- package/package.json +7 -1
- package/dist/src/harness/otel-integration.d.ts +0 -12
- package/dist/src/harness/otel-integration.js +0 -1
package/dist/src/svelte/index.js
CHANGED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type Context, type SpanContext, type Tracer } from "#compiled/@opentelemetry/api/index.js";
|
|
2
|
+
import type { InstrumentationActionStartedEvent, InstrumentationAttemptScope, InstrumentationProviderDefinition } from "#harness/instrumentation-lifecycle.js";
|
|
3
|
+
import type { AgentSpanIdGenerator } from "#tracing/agent-span-id-generator.js";
|
|
4
|
+
import type { AgentTraceStateStore } from "#tracing/agent-trace-state.js";
|
|
5
|
+
export interface AgentActionInstrumentation {
|
|
6
|
+
readonly events: Pick<NonNullable<InstrumentationProviderDefinition["events"]>, "action.completed" | "action.failed" | "action.started">;
|
|
7
|
+
deleteForSession(sessionId: string): void | PromiseLike<void>;
|
|
8
|
+
deleteForTurn(sessionId: string, turnId: string): void | PromiseLike<void>;
|
|
9
|
+
failForAttempt(scope: InstrumentationAttemptScope, error: unknown): Promise<void>;
|
|
10
|
+
contextFor(sessionId: string, turnId: string, callId: string): Promise<AgentActionContext | undefined>;
|
|
11
|
+
}
|
|
12
|
+
export interface AgentActionContext {
|
|
13
|
+
readonly context: Context;
|
|
14
|
+
readonly spanContext: SpanContext;
|
|
15
|
+
}
|
|
16
|
+
/** Builds durable `agent.action` spans around eve's runtime dispatch boundary. */
|
|
17
|
+
export declare function createAgentActionInstrumentation(input: {
|
|
18
|
+
readonly frameworkVersion: string;
|
|
19
|
+
readonly idGenerator: AgentSpanIdGenerator;
|
|
20
|
+
readonly recordInputs: boolean;
|
|
21
|
+
readonly recordOutputs: boolean;
|
|
22
|
+
readonly resolveParent: (event: InstrumentationActionStartedEvent) => {
|
|
23
|
+
readonly context: Context;
|
|
24
|
+
readonly spanContext: SpanContext;
|
|
25
|
+
} | undefined | PromiseLike<{
|
|
26
|
+
readonly context: Context;
|
|
27
|
+
readonly spanContext: SpanContext;
|
|
28
|
+
} | undefined>;
|
|
29
|
+
readonly stateStore: AgentTraceStateStore;
|
|
30
|
+
readonly tracer: Tracer;
|
|
31
|
+
}): AgentActionInstrumentation;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{ROOT_CONTEXT,SpanStatusCode,trace}from"#compiled/@opentelemetry/api/index.js";import{actionIdempotencyKey}from"#harness/instrumentation-lifecycle.js";import{contentAttribute}from"#tracing/agent-otel-content.js";import{setAgentUsage}from"#tracing/agent-otel-usage.js";function createAgentActionInstrumentation(e){let t=new Map,onStarted=async n=>{let r=await e.resolveParent(n);if(r===void 0)return;let a=await e.stateStore.getAction(n.idempotencyKey)??{attemptIndex:n.scope.attemptIndex,callId:n.callId,inputAttribute:e.recordInputs?contentAttribute(n.input,!1):void 0,kind:n.kind,name:n.name,parent:{spanId:r.spanContext.spanId,traceFlags:r.spanContext.traceFlags,traceId:r.spanContext.traceId},rootSessionId:n.scope.rootSessionId??n.scope.sessionId,sessionId:n.scope.sessionId,spanId:e.idGenerator.deriveSpanId(`action:${n.idempotencyKey}`),startTimeMs:Date.now(),stepIndex:n.scope.stepIndex,turnId:n.scope.turnId};await e.stateStore.setAction(n.idempotencyKey,a);let o=t.get(n.scope.attemptId)??new Set;o.add(n.idempotencyKey),t.set(n.scope.attemptId,o)},onTerminal=async t=>{let n=await e.stateStore.getAction(t.idempotencyKey);if(n!==void 0)try{let r=startSpan(n);if(r.setAttribute(`agent.action.outcome`,t.outcome),t.type===`action.failed`)t.errorCode!==void 0&&(r.setAttribute(`agent.action.error.code`,t.errorCode),r.setAttribute(`error.type`,t.errorCode)),recordError(r,t.error);else if(t.output.type===`error`)recordError(r,t.output.error);else if(t.usage!==void 0&&setAgentUsage(r,t.usage),e.recordOutputs){let e=contentAttribute(t.output.output,!1);e!==void 0&&r.setAttribute(`gen_ai.tool.call.result`,e)}r.end(t.acceptedAtMs)}finally{await e.stateStore.deleteAction(t.idempotencyKey),forget(t.idempotencyKey)}},startSpan=t=>{let n=e.idGenerator.withSpanId(t.spanId,()=>e.tracer.startSpan(`agent.action`,{attributes:{"agent.action.call_id":t.callId,"agent.action.kind":t.kind,"agent.action.name":t.name,"agent.framework.name":`eve`,"agent.framework.version":e.frameworkVersion,"agent.root.session.id":t.rootSessionId,"agent.session.id":t.sessionId,"agent.step.attempt":t.attemptIndex,"agent.step.index":t.stepIndex,"agent.turn.id":t.turnId},startTime:t.startTimeMs},contextFromActionState(t)));return t.inputAttribute!==void 0&&n.setAttribute(`gen_ai.tool.call.arguments`,t.inputAttribute),n};return{async contextFor(t,n,i){let a=actionIdempotencyKey(t,n,i),o=await e.stateStore.getAction(a);if(o!==void 0)return actionContext(o);let s=await e.stateStore.findAction(t,i);return s===void 0?void 0:actionContext(s)},deleteForSession:t=>e.stateStore.deleteActions(t),deleteForTurn:(t,n)=>e.stateStore.deleteActions(t,n),async failForAttempt(n,r){let i=t.get(n.attemptId);if(i!==void 0){t.delete(n.attemptId);for(let t of i){let n=await e.stateStore.getAction(t);if(n===void 0)continue;let i=startSpan(n);recordError(i,r),i.end(),await e.stateStore.deleteAction(t)}}},events:{"action.completed":onTerminal,"action.failed":onTerminal,"action.started":onStarted}};function forget(e){for(let[n,r]of t)r.delete(e),r.size===0&&t.delete(n)}}function actionContext(t){let r={isRemote:!1,spanId:t.spanId,traceFlags:t.parent.traceFlags,traceId:t.parent.traceId};return{context:trace.setSpan(ROOT_CONTEXT,trace.wrapSpanContext(r)),spanContext:r}}function contextFromActionState(t){return trace.setSpan(ROOT_CONTEXT,trace.wrapSpanContext({...t.parent,isRemote:!1}))}function recordError(e,n){n instanceof Error?(e.recordException(n),e.setStatus({code:SpanStatusCode.ERROR,message:n.message})):e.setStatus({code:SpanStatusCode.ERROR})}export{createAgentActionInstrumentation};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type Tracer } from "#compiled/@opentelemetry/api/index.js";
|
|
2
|
+
import type { InstrumentationProviderDefinition } from "#harness/instrumentation-lifecycle.js";
|
|
3
|
+
import type { AgentActionContext } from "#tracing/agent-action-instrumentation.js";
|
|
4
|
+
import type { AgentSpanIdGenerator } from "#tracing/agent-span-id-generator.js";
|
|
5
|
+
/** Builds durable approval wait spans under their originating runtime action. */
|
|
6
|
+
export declare function createAgentApprovalInstrumentation(input: {
|
|
7
|
+
readonly actionContextFor: (sessionId: string, turnId: string, callId: string) => Promise<AgentActionContext | undefined>;
|
|
8
|
+
readonly frameworkVersion: string;
|
|
9
|
+
readonly idGenerator: AgentSpanIdGenerator;
|
|
10
|
+
readonly recordInputs: boolean;
|
|
11
|
+
readonly recordOutputs: boolean;
|
|
12
|
+
readonly tracer: Tracer;
|
|
13
|
+
}): Pick<NonNullable<InstrumentationProviderDefinition["events"]>, "input.requested" | "input.resolved">;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{ROOT_CONTEXT,SpanStatusCode,trace}from"#compiled/@opentelemetry/api/index.js";import{contentAttribute}from"#tracing/agent-otel-content.js";function createAgentApprovalInstrumentation(t){return{"input.requested":async(e,n)=>{if(e.kind!==`tool-approval`||readState(n.state.get())!==void 0)return;let i=await t.actionContextFor(e.scope.sessionId,e.scope.turnId,e.action.callId);if(i===void 0)return;let a={actionCallId:e.action.callId,actionName:e.action.name,attemptIndex:e.scope.attemptIndex,parent:{spanId:i.spanContext.spanId,traceFlags:i.spanContext.traceFlags,traceId:i.spanContext.traceId},requestId:e.requestId,rootSessionId:e.scope.rootSessionId??e.scope.sessionId,sessionId:e.scope.sessionId,startTimeMs:Date.now(),stepIndex:e.scope.stepIndex,turnId:e.scope.turnId},o=t.recordInputs?contentAttribute(e.request,!1):void 0;o!==void 0&&(a.requestAttribute=o),n.state.set(a)},"input.resolved":(i,a)=>{let o=readState(a.state.get());if(o===void 0)return;let s=t.idGenerator.withSpanId(t.idGenerator.deriveSpanId(`approval:${i.idempotencyKey}`),()=>t.tracer.startSpan(`agent.approval`,{attributes:{"agent.action.call_id":o.actionCallId,"agent.action.name":o.actionName,"agent.approval.kind":`tool-approval`,"agent.approval.outcome":i.outcome,"agent.approval.request_id":o.requestId,"agent.framework.name":`eve`,"agent.framework.version":t.frameworkVersion,"agent.root.session.id":o.rootSessionId,"agent.session.id":o.sessionId,"agent.step.attempt":o.attemptIndex,"agent.step.index":o.stepIndex,"agent.turn.id":o.turnId},startTime:o.startTimeMs},trace.setSpan(ROOT_CONTEXT,trace.wrapSpanContext({...o.parent,isRemote:!1}))));if(o.requestAttribute!==void 0&&s.setAttribute(`agent.approval.request`,o.requestAttribute),t.recordOutputs&&i.response!==void 0){let e=contentAttribute(i.response,!1);e!==void 0&&s.setAttribute(`agent.approval.response`,e)}i.outcome===`failed`&&recordError(s,i.error),s.end()}}}function readState(e){if(typeof e!=`object`||!e||Array.isArray(e))return;let t=e,n=t.parent;if(typeof n!=`object`||!n||Array.isArray(n))return;let r=n;if(typeof t.actionCallId!=`string`||typeof t.actionName!=`string`||typeof t.attemptIndex!=`number`||typeof r.spanId!=`string`||typeof r.traceFlags!=`number`||typeof r.traceId!=`string`||typeof t.requestId!=`string`||typeof t.rootSessionId!=`string`||typeof t.sessionId!=`string`||typeof t.startTimeMs!=`number`||typeof t.stepIndex!=`number`||typeof t.turnId!=`string`)return;let i=t.requestAttribute;if(!(i!==void 0&&typeof i!=`string`))return{actionCallId:t.actionCallId,actionName:t.actionName,attemptIndex:t.attemptIndex,parent:{isRemote:!1,spanId:r.spanId,traceFlags:r.traceFlags,traceId:r.traceId},requestAttribute:i,requestId:t.requestId,rootSessionId:t.rootSessionId,sessionId:t.sessionId,startTimeMs:t.startTimeMs,stepIndex:t.stepIndex,turnId:t.turnId}}function recordError(e,n){n instanceof Error?(e.recordException(n),e.setStatus({code:SpanStatusCode.ERROR,message:n.message})):e.setStatus({code:SpanStatusCode.ERROR})}export{createAgentApprovalInstrumentation};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type Tracer } from "#compiled/@opentelemetry/api/index.js";
|
|
2
|
+
import type { InstrumentationProviderDefinition, InstrumentationSessionStartedEvent } from "#harness/instrumentation-lifecycle.js";
|
|
3
|
+
import type { AgentSpanIdGenerator } from "#tracing/agent-span-id-generator.js";
|
|
4
|
+
import type { AgentSessionTraceState, AgentTraceStateStore } from "#tracing/agent-trace-state.js";
|
|
5
|
+
/** Builds durable channel delivery spans around the turn that consumes each request. */
|
|
6
|
+
export declare function createAgentChannelDeliveryInstrumentation(input: {
|
|
7
|
+
readonly ensureSessionContext: (event: InstrumentationSessionStartedEvent) => Promise<AgentSessionTraceState>;
|
|
8
|
+
readonly frameworkVersion: string;
|
|
9
|
+
readonly idGenerator: AgentSpanIdGenerator;
|
|
10
|
+
readonly recordInputs: boolean;
|
|
11
|
+
readonly stateStore: AgentTraceStateStore;
|
|
12
|
+
readonly tracer: Tracer;
|
|
13
|
+
}): Pick<NonNullable<InstrumentationProviderDefinition["events"]>, "channel.delivery.cancelled" | "channel.delivery.completed" | "channel.delivery.failed" | "channel.delivery.started">;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{ROOT_CONTEXT,SpanKind,SpanStatusCode,trace}from"#compiled/@opentelemetry/api/index.js";import{sessionIdempotencyKey}from"#harness/instrumentation-lifecycle.js";import{contentAttribute}from"#tracing/agent-otel-content.js";function createAgentChannelDeliveryInstrumentation(e){let onStarted=async(t,n)=>{let r=await e.ensureSessionContext({agentName:t.agentName,channelKind:t.delivery.channelKind,idempotencyKey:sessionIdempotencyKey(t.sessionId),parentTraceContext:t.parentTraceContext,rootSessionId:t.rootSessionId,sessionId:t.sessionId,type:`session.started`}),o=e.recordInputs?contentAttribute(t.input,!1):void 0,s={parent:{isRemote:r.context.isRemote??!1,spanId:r.context.spanId,traceFlags:r.context.traceFlags,traceId:r.context.traceId},spanId:e.idGenerator.deriveSpanId(`channel-delivery:${t.idempotencyKey}`),startTimeMs:Date.now(),window:r.window};o!==void 0&&(s.inputAttribute=o),t.delivery.requestTraceContext!==void 0&&(s.requestTraceContext={spanId:t.delivery.requestTraceContext.spanId,traceFlags:t.delivery.requestTraceContext.traceFlags,traceId:t.delivery.requestTraceContext.traceId}),n.state.set(s)},onTerminal=async(n,r)=>{let i=readState(r.state.get());if(i===void 0)return;let a=n.turnId===void 0?void 0:await e.stateStore.getTurn(n.sessionId,n.turnId),o=await e.stateStore.getSession(n.sessionId),s=a===void 0?i.parent:{isRemote:a.parentIsRemote??!1,spanId:a.parentSpanId,traceFlags:a.context.traceFlags,traceId:a.context.traceId},c=a===void 0?i.startTimeMs:Math.max(i.startTimeMs,a.startTimeMs),l=i.requestTraceContext,u=e.idGenerator.withSpanId(i.spanId,()=>e.tracer.startSpan(`agent.channel.delivery`,{attributes:{"agent.channel.delivery.id":n.delivery.deliveryId,"agent.channel.delivery.outcome":n.outcome,"agent.channel.kind":n.delivery.channelKind,"agent.channel.name":n.delivery.channelName,"agent.channel.request.id":n.delivery.requestId,"agent.framework.name":`eve`,"agent.framework.version":e.frameworkVersion,"agent.name":n.agentName,"agent.root.session.id":n.rootSessionId,"agent.session.id":n.sessionId,"agent.session.window":o?.window??i.window,"agent.turn.id":n.turnId,"agent.turn.sequence":n.sequence},kind:SpanKind.CONSUMER,links:l===void 0?void 0:[{attributes:{"eve.link.type":`channel.request`},context:l}],startTime:c},contextFromSpanContext(s)));if(u.addEvent(`channel.delivery.started`,void 0,c),u.addEvent(n.type),i.inputAttribute!==void 0&&u.setAttribute(`agent.channel.delivery.input`,i.inputAttribute),n.outcome===`failed`){let e=n.errorCode??(n.error instanceof Error?n.error.name:`Error`);u.setAttribute(`error.type`,e),n.errorCode!==void 0&&u.setAttribute(`agent.channel.delivery.error.code`,n.errorCode),recordError(u,n.error)}u.end()};return{"channel.delivery.cancelled":onTerminal,"channel.delivery.completed":onTerminal,"channel.delivery.failed":onTerminal,"channel.delivery.started":onStarted}}function readState(e){if(!isRecord(e)||!isSpanContext(e.parent)||typeof e.spanId!=`string`||typeof e.startTimeMs!=`number`||typeof e.window!=`number`)return;let t=isSpanContext(e.requestTraceContext)?e.requestTraceContext:void 0;return{inputAttribute:typeof e.inputAttribute==`string`?e.inputAttribute:void 0,parent:e.parent,requestTraceContext:t,spanId:e.spanId,startTimeMs:e.startTimeMs,window:e.window}}function isSpanContext(e){return isRecord(e)&&typeof e.spanId==`string`&&typeof e.traceFlags==`number`&&typeof e.traceId==`string`}function isRecord(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function contextFromSpanContext(t){return trace.setSpan(ROOT_CONTEXT,trace.wrapSpanContext(t))}function recordError(e,t){t instanceof Error?(e.recordException(t),e.setStatus({code:SpanStatusCode.ERROR,message:t.message})):e.setStatus({code:SpanStatusCode.ERROR})}export{createAgentChannelDeliveryInstrumentation};
|
|
@@ -19,6 +19,12 @@ export declare function contentAttribute(value: unknown, strip?: boolean): strin
|
|
|
19
19
|
* the cap is truncated at the text level rather than the JSON level.
|
|
20
20
|
*/
|
|
21
21
|
export declare function messagesContentAttribute(messages: unknown): string | undefined;
|
|
22
|
+
/** Serializes model messages using the OpenTelemetry GenAI message schema. */
|
|
23
|
+
export declare function genAiInputMessagesAttribute(messages: unknown): string | undefined;
|
|
24
|
+
/** Serializes the system prompt using the OpenTelemetry GenAI instruction schema. */
|
|
25
|
+
export declare function genAiSystemInstructionsAttribute(instructions: unknown): string | undefined;
|
|
26
|
+
/** Serializes one model response using the OpenTelemetry GenAI message schema. */
|
|
27
|
+
export declare function genAiOutputMessagesAttribute(content: readonly unknown[], finishReason: string): string | undefined;
|
|
22
28
|
/**
|
|
23
29
|
* Serializes provider-executed tool results, keeping the attribute valid
|
|
24
30
|
* JSON under the cap: when the full payload is too big, each entry's input
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const CONTENT_ATTRIBUTE_LIMIT=32*1024,CONTENT_NOISE_KEYS=new Set([`providerOptions`,`providerMetadata`]),TRUNCATED_MESSAGES_KEY=`eve.truncated`;function contentAttribute(e,t=!0){if(e===void 0)return;let n=t?stripContentNoise(e,0):e,r;try{r=JSON.stringify(n)}catch{return}if(r!==void 0)return textContentAttribute(r)}function messagesContentAttribute(e){if(!Array.isArray(e))return contentAttribute(e);let t=stripContentNoise(e,0),r=stringifyContent(t);if(r!==void 0&&r.length<=32768)return r;for(let e=1;e<t.length;e+=1){let r=stringifyContent([{[TRUNCATED_MESSAGES_KEY]:{omittedMessages:e}},...t.slice(e)]);if(r!==void 0&&r.length<=32768)return r}return truncateSingleMessage(t)}function toolResultsContentAttribute(t){if(t.length===0)return;let n=stringifyContent(t);if(n!==void 0&&n.length<=32768)return n;for(let n=CONTENT_ATTRIBUTE_LIMIT;n>=0;n=n>=256?Math.floor(n/2):-1){let e=stringifyContent(t.map(e=>cappedToolResult(e,n)));if(e!==void 0&&e.length<=32768)return e}}function cappedToolResult(e,t){let n={toolName:e.toolName};for(let r of[`input`,`output`,`error`]){if(!(r in e))continue;let i=e[r],a=typeof i==`string`?i:stringifyContent(i)??``;n[r]=a.length<=t?a:`${a.slice(0,t)}… [truncated]`}return n}function systemPromptAttribute(e){if(typeof e==`string`)return textContentAttribute(e);if(!isRecord(e)&&!Array.isArray(e))return;let t=Array.isArray(e)?e:[e],n=[];for(let e of t)if(isRecord(e)){if(typeof e.content==`string`)n.push(e.content);else if(Array.isArray(e.content))for(let t of e.content)isRecord(t)&&t.type===`text`&&typeof t.text==`string`&&n.push(t.text)}let r=n.join(`
|
|
1
|
+
const CONTENT_ATTRIBUTE_LIMIT=32*1024,CONTENT_NOISE_KEYS=new Set([`providerOptions`,`providerMetadata`]),TRUNCATED_MESSAGES_KEY=`eve.truncated`;function contentAttribute(e,t=!0){if(e===void 0)return;let n=t?stripContentNoise(e,0):e,r;try{r=JSON.stringify(n)}catch{return}if(r!==void 0)return textContentAttribute(r)}function messagesContentAttribute(e){if(!Array.isArray(e))return contentAttribute(e);let t=stripContentNoise(e,0),r=stringifyContent(t);if(r!==void 0&&r.length<=32768)return r;for(let e=1;e<t.length;e+=1){let r=stringifyContent([{[TRUNCATED_MESSAGES_KEY]:{omittedMessages:e}},...t.slice(e)]);if(r!==void 0&&r.length<=32768)return r}return truncateSingleMessage(t)}function genAiInputMessagesAttribute(e){if(!Array.isArray(e))return;let t=e.flatMap(e=>{if(!isRecord(e)||e.role===`system`||typeof e.role!=`string`)return[];let t=semanticParts(e.content);return t.length===0?[]:[{parts:t,role:e.role}]});for(let e=0;e<t.length;e+=1){let n=semanticJsonAttribute(t.slice(e));if(n!==void 0)return n}return semanticJsonAttribute([])}function genAiSystemInstructionsAttribute(e){let t=systemPromptAttribute(e);return t===void 0?void 0:semanticJsonAttribute([{content:t,type:`text`}])}function genAiOutputMessagesAttribute(e,t){let n=semanticParts(e);return semanticJsonAttribute([{finish_reason:t===`tool-calls`?`tool_call`:t,parts:n,role:`assistant`}])}function toolResultsContentAttribute(t){if(t.length===0)return;let n=stringifyContent(t);if(n!==void 0&&n.length<=32768)return n;for(let n=CONTENT_ATTRIBUTE_LIMIT;n>=0;n=n>=256?Math.floor(n/2):-1){let e=stringifyContent(t.map(e=>cappedToolResult(e,n)));if(e!==void 0&&e.length<=32768)return e}}function cappedToolResult(e,t){let n={toolName:e.toolName};for(let r of[`input`,`output`,`error`]){if(!(r in e))continue;let i=e[r],a=typeof i==`string`?i:stringifyContent(i)??``;n[r]=a.length<=t?a:`${a.slice(0,t)}… [truncated]`}return n}function systemPromptAttribute(e){if(typeof e==`string`)return textContentAttribute(e);if(!isRecord(e)&&!Array.isArray(e))return;let t=Array.isArray(e)?e:[e],n=[];for(let e of t)if(isRecord(e)){if(typeof e.content==`string`)n.push(e.content);else if(Array.isArray(e.content))for(let t of e.content)isRecord(t)&&t.type===`text`&&typeof t.text==`string`&&n.push(t.text)}let r=n.join(`
|
|
2
2
|
|
|
3
3
|
`).trim();return r.length===0?void 0:textContentAttribute(r)}function textContentAttribute(t){if(t.length!==0)return t.length<=32768?t:`${t.slice(0,CONTENT_ATTRIBUTE_LIMIT)}… [truncated]`}function stripContentNoise(e,n){if(n>32||typeof e!=`object`||!e)return e;if(Array.isArray(e))return e.map(e=>stripContentNoise(e,n+1));let r={};for(let[i,a]of Object.entries(e))CONTENT_NOISE_KEYS.has(i)||(r[i]=stripContentNoise(a,n+1));return r}function truncateSingleMessage(t){if(t.length===0)return;let r=t[t.length-1];if(!isRecord(r))return;let i={[TRUNCATED_MESSAGES_KEY]:{omittedMessages:t.length-1}},a=typeof r.role==`string`?r.role:`user`,o=``;if(typeof r.content==`string`)o=r.content;else if(Array.isArray(r.content))for(let e of r.content)isRecord(e)&&e.type===`text`&&typeof e.text==`string`&&(o+=(o?`
|
|
4
|
-
`:``)+e.text);for(let t=Math.min(o.length,CONTENT_ATTRIBUTE_LIMIT);t>0;t-=256){let e=stringifyContent([i,{role:a,content:t>=o.length?o:`${o.slice(0,t)}… [truncated]`}]);if(e!==void 0&&e.length<=32768)return e}return stringifyContent([i,{role:a,content:``}])}function stringifyContent(e){try{return JSON.stringify(e)}catch{return}}function isRecord(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}export{CONTENT_ATTRIBUTE_LIMIT,contentAttribute,messagesContentAttribute,systemPromptAttribute,textContentAttribute,toolResultsContentAttribute};
|
|
4
|
+
`:``)+e.text);for(let t=Math.min(o.length,CONTENT_ATTRIBUTE_LIMIT);t>0;t-=256){let e=stringifyContent([i,{role:a,content:t>=o.length?o:`${o.slice(0,t)}… [truncated]`}]);if(e!==void 0&&e.length<=32768)return e}return stringifyContent([i,{role:a,content:``}])}function stringifyContent(e){try{return JSON.stringify(e)}catch{return}}function semanticJsonAttribute(e){let t=stringifyContent(e);return t!==void 0&&t.length<=32768?t:void 0}function semanticParts(e){if(typeof e==`string`)return[{content:e,type:`text`}];if(!Array.isArray(e))return[];let t=[];for(let n of e){let e=semanticPart(n);e!==void 0&&t.push(e)}return t}function semanticPart(e){if(!(!isRecord(e)||typeof e.type!=`string`))switch(e.type){case`text`:case`reasoning`:{let t=typeof e.text==`string`?e.text:e.content;return typeof t==`string`?{content:t,type:e.type}:void 0}case`tool-call`:return{arguments:e.input,id:stringValue(e.toolCallId)??stringValue(e.callId)??null,name:e.toolName,type:`tool_call`};case`tool-result`:return{id:stringValue(e.toolCallId)??stringValue(e.callId)??null,response:toolResponse(e.output),type:`tool_call_response`};case`tool-error`:return{id:stringValue(e.toolCallId)??stringValue(e.callId)??null,response:e.error instanceof Error?e.error.message:e.error,type:`tool_call_response`};default:return{type:e.type}}}function toolResponse(e){return isRecord(e)?(e.type===`text`||e.type===`error-text`||e.type===`json`||e.type===`error-json`)&&`value`in e?e.value:e.type===`execution-denied`?{denied:!0,reason:e.reason}:e:e}function stringValue(e){return typeof e==`string`?e:void 0}function isRecord(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}export{CONTENT_ATTRIBUTE_LIMIT,contentAttribute,genAiInputMessagesAttribute,genAiOutputMessagesAttribute,genAiSystemInstructionsAttribute,messagesContentAttribute,systemPromptAttribute,textContentAttribute,toolResultsContentAttribute};
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { type Tracer } from "#compiled/@opentelemetry/api/index.js";
|
|
2
|
+
import { type AgentTraceStateStore } from "#tracing/agent-trace-state.js";
|
|
2
3
|
import type { AgentSpanIdGenerator } from "#tracing/agent-span-id-generator.js";
|
|
3
|
-
import type { AgentTraceStateStore } from "#tracing/agent-trace-state.js";
|
|
4
4
|
import type { InstrumentationContextRunner, InstrumentationProviderDefinition } from "#harness/instrumentation-lifecycle.js";
|
|
5
|
-
/** Sized so an ordinary session stays one trace and only an outsized one rolls. */
|
|
6
|
-
export declare const SESSION_WINDOW_TURN_LIMIT = 200;
|
|
7
5
|
export interface AgentOtelInstrumentationInput {
|
|
8
6
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
7
|
+
* Whether to write model prompts and tool call inputs onto spans at all.
|
|
8
|
+
* This is the union across destinations, not one destination's policy: a
|
|
9
|
+
* destination that declined drops these on its way out instead.
|
|
12
10
|
*/
|
|
13
|
-
readonly
|
|
11
|
+
readonly recordInputs?: boolean;
|
|
12
|
+
/** The same, for model responses and tool call outputs. */
|
|
13
|
+
readonly recordOutputs?: boolean;
|
|
14
14
|
readonly frameworkVersion: string;
|
|
15
15
|
readonly idGenerator: AgentSpanIdGenerator;
|
|
16
16
|
readonly stateStore: AgentTraceStateStore;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{ROOT_CONTEXT,SpanStatusCode,context,trace}from"#compiled/@opentelemetry/api/index.js";import{contentAttribute,messagesContentAttribute,systemPromptAttribute,textContentAttribute,toolResultsContentAttribute}from"#tracing/agent-otel-content.js";
|
|
2
|
-
`));
|
|
1
|
+
import{ROOT_CONTEXT,SpanStatusCode,context,trace}from"#compiled/@opentelemetry/api/index.js";import{sessionIdempotencyKey}from"#harness/instrumentation-lifecycle.js";import{contentAttribute,genAiInputMessagesAttribute,genAiOutputMessagesAttribute,genAiSystemInstructionsAttribute,messagesContentAttribute,systemPromptAttribute,textContentAttribute,toolResultsContentAttribute}from"#tracing/agent-otel-content.js";import{setAgentUsage}from"#tracing/agent-otel-usage.js";import{SESSION_WINDOW_TURN_LIMIT}from"#tracing/agent-trace-state.js";import{createAgentActionInstrumentation}from"#tracing/agent-action-instrumentation.js";import{createAgentApprovalInstrumentation}from"#tracing/agent-approval-instrumentation.js";import{createAgentChannelDeliveryInstrumentation}from"#tracing/agent-channel-delivery-instrumentation.js";import{createAgentToolInstrumentation}from"#tracing/agent-tool-instrumentation.js";function createAgentOtelInstrumentation(e){let t=e.recordInputs??!0,d=e.recordOutputs??!0,f=new WeakMap,p=new Map,m=new WeakMap,h=new WeakMap,g=createAgentActionInstrumentation({frameworkVersion:e.frameworkVersion,idGenerator:e.idGenerator,recordInputs:t,recordOutputs:d,resolveParent:async t=>{let n=await e.stateStore.getTurn(t.scope.sessionId,t.scope.turnId);return n===void 0?void 0:{context:contextFromSpanContext(n.context),spanContext:n.context}},stateStore:e.stateStore,tracer:e.tracer}),_=createAgentApprovalInstrumentation({actionContextFor:g.contextFor,frameworkVersion:e.frameworkVersion,idGenerator:e.idGenerator,recordInputs:t,recordOutputs:d,tracer:e.tracer}),v=createAgentToolInstrumentation({actionContextFor:g.contextFor,idGenerator:e.idGenerator,recordInputs:t,recordOutputs:d,resolveFallback:e=>{let t=p.get(e.scope.attemptId)??e.scope,n=m.get(t)?.step;return n===void 0?void 0:{context:n.context,spanContext:n.span.spanContext()}},tracer:e.tracer}),onSessionStarted=async e=>{await ensureSessionContext(e)},onTurnStarted=async t=>{let n=advanceSessionWindow(t.sessionId,await ensureSessionContext({agentName:void 0,channelKind:void 0,idempotencyKey:sessionIdempotencyKey(t.sessionId),parentTraceContext:t.parentTraceContext,rootSessionId:t.rootSessionId,sessionId:t.sessionId,type:`session.started`})),r={isRemote:!1,spanId:e.idGenerator.deriveSpanId(`turn:${t.idempotencyKey}`),traceFlags:n.context.traceFlags,traceId:n.context.traceId};await e.stateStore.setSession(t.sessionId,{...n,turnsInWindow:n.turnsInWindow+1}),await e.stateStore.setTurn(t.sessionId,t.turnId,{context:r,lineage:t.parentLineage,parentIsRemote:n.context.isRemote,parentSpanId:n.context.spanId,rootSessionId:t.rootSessionId,sequence:t.sequence,startTimeMs:Date.now()})},onStepStarted=async t=>{let i=await e.stateStore.getTurn(t.scope.sessionId,t.scope.turnId);if(i===void 0)return;let a=contextFromSpanContext(i.context),o=trace.getSpan(context.active())?.spanContext(),s=e.tracer.startSpan(`agent.step`,{attributes:{"agent.session.id":t.scope.sessionId,"agent.framework.name":`eve`,"agent.framework.version":e.frameworkVersion,"agent.root.session.id":t.scope.rootSessionId??t.scope.sessionId,"agent.step.attempt":t.scope.attemptIndex,"agent.step.index":t.scope.stepIndex,"agent.turn.id":t.scope.turnId,"agent.name":t.scope.functionId},links:o===void 0||o.traceId===i.context.traceId?void 0:[{attributes:{"eve.link.type":`workflow.delivery`},context:o}]},a);s.addEvent(`step.started`);let c=trace.setSpan(a,s),l=t.operation.operationId,u=e.tracer.startSpan(l,{attributes:{"ai.operation.name":l,"ai.provider.name":t.operation.provider,"ai.request.model":t.operation.modelId}},c);m.set(t.scope,{operation:{context:trace.setSpan(c,u),name:l,span:u},step:{context:c,span:s}}),p.set(t.scope.attemptId,t.scope)},onStepTerminal=async e=>{let t=p.get(e.scope.attemptId)??e.scope;f.delete(t),drainOpenSpans({...e,scope:t}),v.drain(e.scope.attemptId,e.type===`step.attempt.failed`?{error:e.error}:void 0),e.type===`step.attempt.failed`&&await g.failForAttempt(t,e.error),p.delete(e.scope.attemptId);let n=m.get(t);n!==void 0&&(n.step.span.addEvent(e.type===`step.attempt.completed`?`step.completed`:`step.failed`),e.type===`step.attempt.failed`&&(recordError(n.operation.span,e.error),recordError(n.step.span,e.error)),n.operation.span.end(),n.step.span.end(),m.delete(t))},onTurnTerminal=async t=>{(t.type===`turn.cancelled`||t.type===`turn.failed`)&&await g.deleteForTurn(t.sessionId,t.turnId);let n=await e.stateStore.getTurn(t.sessionId,t.turnId);n!==void 0&&await e.stateStore.setTurn(t.sessionId,t.turnId,{...n,terminal:t.type===`turn.failed`?{error:t.error,type:t.type}:{type:t.type}})},onSessionTransition=async t=>{if(t.turnId!==void 0){let n=await e.stateStore.getTurn(t.sessionId,t.turnId);if(n!==void 0){let r=await e.stateStore.getSession(t.sessionId),i=e.idGenerator.withSpanId(n.context.spanId,()=>e.tracer.startSpan(`agent.turn`,{attributes:{"agent.framework.name":`eve`,"agent.framework.version":e.frameworkVersion,"agent.name":r?.agentName,...parentLineageAttributes(n.lineage),"agent.root.session.id":n.rootSessionId,"agent.session.id":t.sessionId,"agent.session.window":r?.window,"agent.turn.id":t.turnId,"agent.turn.sequence":n.sequence},startTime:n.startTimeMs},contextFromSpanContext({isRemote:n.parentIsRemote??!1,spanId:n.parentSpanId,traceFlags:n.context.traceFlags,traceId:n.context.traceId})));i.addEvent(`turn.started`,void 0,n.startTimeMs),n.terminal!==void 0&&(i.addEvent(n.terminal.type),n.terminal.type===`turn.failed`&&recordError(i,n.terminal.error)),i.end(),await e.stateStore.deleteTurn(t.sessionId,t.turnId)}}(t.type===`session.completed`||t.type===`session.failed`)&&(await g.deleteForSession(t.sessionId),await e.stateStore.deleteSession(t.sessionId))},onModelCallStarted=n=>{let i=m.get(n.scope);if(i===void 0)return;i.step.span.setAttribute(`agent.model.id`,n.model.modelId),i.step.span.setAttribute(`agent.model.provider`,n.model.provider);let a=e.tracer.startSpan(modelSpanName(n.model.modelId),{attributes:{"gen_ai.agent.name":n.scope.functionId,"gen_ai.operation.name":`chat`,"gen_ai.provider.name":n.model.provider,"gen_ai.request.model":n.model.modelId}},i.operation.context);if(t&&n.input!==void 0){let e=messagesContentAttribute(n.input.messages);e!==void 0&&a.setAttribute(`ai.prompt.messages`,e);let t=genAiInputMessagesAttribute(n.input.messages);t!==void 0&&a.setAttribute(`gen_ai.input.messages`,t);let r=systemPromptAttribute(n.input.instructions);r!==void 0&&a.setAttribute(`ai.prompt.system`,r);let i=genAiSystemInstructionsAttribute(n.input.instructions);i!==void 0&&a.setAttribute(`gen_ai.system_instructions`,i)}let s={context:trace.setSpan(i.operation.context,a),span:a};getExecutionContexts(n.scope).set(n.idempotencyKey,s.context),getSpanStates(h,n.scope).set(n.idempotencyKey,s)},onModelCallTerminal=e=>{f.get(e.scope)?.delete(e.idempotencyKey);let t=takeSpanState(h,e.scope,e.idempotencyKey);if(t!==void 0){if(e.type===`model.call.failed`)recordError(t.span,e.error);else{setAgentUsage(t.span,e.usage),t.span.setAttribute(`gen_ai.response.finish_reasons`,[e.finishReason]);let n=m.get(e.scope);if(n!==void 0&&setAgentUsage(n.step.span,e.usage),d){t.span.setAttribute(`ai.response.finish_reason`,e.finishReason);let n=e.content??[],r=genAiOutputMessagesAttribute(n,e.finishReason);r!==void 0&&t.span.setAttribute(`gen_ai.output.messages`,r);let i=textContentAttribute(n.filter(e=>e.type===`reasoning`).map(e=>e.text).filter(e=>e.trim().length>0).join(`
|
|
2
|
+
`));i!==void 0&&t.span.setAttribute(`ai.response.reasoning`,i);let o=textContentAttribute(n.filter(e=>e.type===`text`).map(e=>e.text).join(``));o!==void 0&&t.span.setAttribute(`ai.response.text`,o);let c=n.filter(e=>e.type===`tool-call`).map(e=>({callId:e.callId,input:e.input,toolName:e.toolName}));if(c.length>0){let e=contentAttribute(c,!1);e!==void 0&&t.span.setAttribute(`ai.response.tool_calls`,e)}let l=n.filter(e=>e.type===`tool-result`||e.type===`tool-error`).map(e=>e.type===`tool-result`?{callId:e.callId,input:e.input,output:e.output,toolName:e.toolName}:{callId:e.callId,error:errorText(e.error),input:e.input,toolName:e.toolName});if(l.length>0){let e=toolResultsContentAttribute(l);e!==void 0&&t.span.setAttribute(`ai.response.tool_results`,e)}}}t.span.end()}},openSessionWindow=t=>{let n=e.tracer.startSpan(`agent.session`,{attributes:{"agent.framework.name":`eve`,"agent.framework.version":e.frameworkVersion,"agent.name":t.agentName,"agent.root.session.id":t.rootSessionId,"agent.session.id":t.sessionId,"agent.session.window":t.index,"agent.trace.schema.version":1,...t.previousTraceId===void 0?{}:{"agent.session.window.previous.trace.id":t.previousTraceId}},root:!0});return n.addEvent(t.index===0?`session.started`:`session.window.opened`),n.end(),n.spanContext()},ensureSessionContext=async t=>{let n=await e.stateStore.getSession(t.sessionId);return n===void 0&&(n={agentName:t.agentName,channelKind:t.channelKind,context:t.parentTraceContext===void 0?openSessionWindow({agentName:t.agentName,index:0,rootSessionId:t.rootSessionId,sessionId:t.sessionId}):adoptedSpanContext(t.parentTraceContext),rootSessionId:t.rootSessionId,turnsInWindow:0,window:0},await e.stateStore.setSession(t.sessionId,n)),n},y=createAgentChannelDeliveryInstrumentation({ensureSessionContext,frameworkVersion:e.frameworkVersion,idGenerator:e.idGenerator,recordInputs:t,stateStore:e.stateStore,tracer:e.tracer}),advanceSessionWindow=(e,t)=>{if(t.turnsInWindow<SESSION_WINDOW_TURN_LIMIT)return t;let n=t.window+1;return{...t,context:openSessionWindow({agentName:t.agentName,index:n,previousTraceId:t.context.traceId,rootSessionId:t.rootSessionId,sessionId:e}),turnsInWindow:0,window:n}},onStepMetadata=e=>{let t=m.get(e.scope);if(t===void 0)return;let n=readGatewayCost(e.providerMetadata);if(n!==void 0)for(let[e,r]of Object.entries(n))t.step.span.setAttribute(e,r)};return{hook:{capture:t||d?`content`:`metadata`,events:{...y,"action.completed":g.events[`action.completed`],"action.failed":g.events[`action.failed`],async"action.started"(e,t){await g.events[`action.started`](e,t),await v.actionStarted(e)},..._,"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,...v.events,"turn.cancelled":onTurnTerminal,"turn.completed":onTurnTerminal,"turn.failed":onTurnTerminal,"turn.started":onTurnStarted},name:`eve.otel`},runInContext(e,t){let r=p.get(e.scope.attemptId)??e.scope,i=f.get(r),a=e.type===`model.call`?i?.get(e.idempotencyKey):v.contextFor(e.scope.attemptId,e.idempotencyKey);return a===void 0?t():context.with(a,t)}};function getExecutionContexts(e){let t=f.get(e);return t===void 0&&(t=new Map,f.set(e,t)),t}function drainOpenSpans(e){for(let t of h.get(e.scope)?.values()??[])e.type===`step.attempt.failed`&&recordError(t.span,e.error),t.span.end();h.delete(e.scope)}}function getSpanStates(e,t){let n=e.get(t);return n===void 0&&(n=new Map,e.set(t,n)),n}function takeSpanState(e,t,n){let r=e.get(t),i=r?.get(n);if(r!==void 0)return r.delete(n),r.size===0&&e.delete(t),i}function parentLineageAttributes(e){if(e===void 0)return{};let t={"agent.parent.call_id":e.callId,"agent.parent.session.id":e.sessionId,"agent.parent.turn.id":e.turnId};return e.subagentName!==void 0&&(t[`agent.subagent.name`]=e.subagentName),t}function adoptedSpanContext(e){return{isRemote:`isRemote`in e&&e.isRemote===!0,spanId:e.spanId,traceFlags:e.traceFlags,traceId:e.traceId}}function contextFromSpanContext(t){return trace.setSpan(ROOT_CONTEXT,trace.wrapSpanContext(t))}function readGatewayCost(e){let t=e.gateway;if(!isRecord(t))return;let n={},r=readUsd(t.cost);r!==void 0&&(n[`gen_ai.usage.cost`]=r);let i=readUsd(t.gatewayCost);i!==void 0&&(n[`gen_ai.usage.gateway_cost`]=i);let a=readUsd(t.inputInferenceCost);a!==void 0&&(n[`gen_ai.usage.input_cost`]=a);let o=readUsd(t.outputInferenceCost);return o!==void 0&&(n[`gen_ai.usage.output_cost`]=o),typeof t.generationId==`string`&&t.generationId.length>0&&(n[`gen_ai.generation.id`]=t.generationId),Object.keys(n).length===0?void 0:n}function readUsd(e){if(typeof e!=`string`)return;let t=Number(e);return Number.isFinite(t)?t:void 0}function isRecord(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function modelSpanName(e){return`chat ${e}`}function errorText(e){return e instanceof Error?e.message:e}function recordError(e,n){n instanceof Error?(e.recordException(n),e.setStatus({code:SpanStatusCode.ERROR,message:n.message})):e.setStatus({code:SpanStatusCode.ERROR})}export{createAgentOtelInstrumentation};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Span } from "#compiled/@opentelemetry/api/index.js";
|
|
2
|
+
import type { InstrumentationUsage } from "#harness/instrumentation-lifecycle.js";
|
|
3
|
+
/** Applies eve's structural token usage attributes to an agent span. */
|
|
4
|
+
export declare function setAgentUsage(span: Span, usage: InstrumentationUsage): void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function setAgentUsage(e,t){t.inputTokens!==void 0&&e.setAttribute(`agent.usage.input_tokens`,t.inputTokens),t.outputTokens!==void 0&&e.setAttribute(`agent.usage.output_tokens`,t.outputTokens);let n=t.inputTokenDetails;n?.cacheReadTokens!==void 0&&e.setAttribute(`gen_ai.usage.cache_read.input_tokens`,n.cacheReadTokens),n?.cacheWriteTokens!==void 0&&e.setAttribute(`gen_ai.usage.cache_creation.input_tokens`,n.cacheWriteTokens)}export{setAgentUsage};
|
|
@@ -8,6 +8,8 @@ export declare class AgentSpanIdGenerator {
|
|
|
8
8
|
#private;
|
|
9
9
|
/** Reserves a span id for a span emitted later via {@link withSpanId}. */
|
|
10
10
|
allocateSpanId(): string;
|
|
11
|
+
/** Derives one span id for a replay-stable instrumentation event. */
|
|
12
|
+
deriveSpanId(key: string): string;
|
|
11
13
|
generateSpanId(): string;
|
|
12
14
|
generateTraceId(): string;
|
|
13
15
|
/** Runs `startSpan` so the next span started carries `spanId`. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var AgentSpanIdGenerator=class{#e;allocateSpanId(){return randomHexId(16)}generateSpanId(){let e=this.#e;return e===void 0?randomHexId(16):(this.#e=void 0,e)}generateTraceId(){return randomHexId(32)}withSpanId(e,t){this.#e=e;try{return t()}finally{this.#e=void 0}}};function randomHexId(e){let t;do{t=``;for(let n=0;n<e;n+=1)t+=`0123456789abcdef`[Math.trunc(Math.random()*16)]}while(!/[1-9a-f]/u.test(t));return t}export{AgentSpanIdGenerator};
|
|
1
|
+
import{createHash}from"node:crypto";var AgentSpanIdGenerator=class{#e;allocateSpanId(){return randomHexId(16)}deriveSpanId(t){let n=createHash(`sha256`).update(t).digest(`hex`).slice(0,16);return/^0+$/u.test(n)?`0000000000000001`:n}generateSpanId(){let e=this.#e;return e===void 0?randomHexId(16):(this.#e=void 0,e)}generateTraceId(){return randomHexId(32)}withSpanId(e,t){this.#e=e;try{return t()}finally{this.#e=void 0}}};function randomHexId(e){let t;do{t=``;for(let n=0;n<e;n+=1)t+=`0123456789abcdef`[Math.trunc(Math.random()*16)]}while(!/[1-9a-f]/u.test(t));return t}export{AgentSpanIdGenerator};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type Context, type SpanContext, type Tracer } from "#compiled/@opentelemetry/api/index.js";
|
|
2
|
+
import type { InstrumentationActionStartedEvent, InstrumentationToolCallStartedEvent, InstrumentationToolCallTerminalEvent } from "#harness/instrumentation-lifecycle.js";
|
|
3
|
+
import type { AgentSpanIdGenerator } from "#tracing/agent-span-id-generator.js";
|
|
4
|
+
import type { AgentActionContext } from "#tracing/agent-action-instrumentation.js";
|
|
5
|
+
export interface AgentToolInstrumentation {
|
|
6
|
+
actionStarted(event: InstrumentationActionStartedEvent): Promise<void>;
|
|
7
|
+
contextFor(attemptId: string, idempotencyKey: string): Context | undefined;
|
|
8
|
+
drain(attemptId: string, failure?: {
|
|
9
|
+
readonly error: unknown;
|
|
10
|
+
}): void;
|
|
11
|
+
readonly events: {
|
|
12
|
+
readonly "tool.call.completed": (event: InstrumentationToolCallTerminalEvent) => Promise<void>;
|
|
13
|
+
readonly "tool.call.failed": (event: InstrumentationToolCallTerminalEvent) => Promise<void>;
|
|
14
|
+
readonly "tool.call.started": (event: InstrumentationToolCallStartedEvent) => Promise<void>;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/** Keeps SDK tool spans parented to actions even when SDK telemetry wins the event race. */
|
|
18
|
+
export declare function createAgentToolInstrumentation(input: {
|
|
19
|
+
readonly actionContextFor: (sessionId: string, turnId: string, callId: string) => Promise<AgentActionContext | undefined>;
|
|
20
|
+
readonly idGenerator: AgentSpanIdGenerator;
|
|
21
|
+
readonly recordInputs: boolean;
|
|
22
|
+
readonly recordOutputs: boolean;
|
|
23
|
+
readonly resolveFallback: (event: InstrumentationToolCallStartedEvent) => {
|
|
24
|
+
readonly context: Context;
|
|
25
|
+
readonly spanContext: SpanContext;
|
|
26
|
+
} | undefined;
|
|
27
|
+
readonly tracer: Tracer;
|
|
28
|
+
}): AgentToolInstrumentation;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{ROOT_CONTEXT,SpanStatusCode,trace}from"#compiled/@opentelemetry/api/index.js";import{actionIdempotencyKey}from"#harness/instrumentation-lifecycle.js";import{contentAttribute}from"#tracing/agent-otel-content.js";function createAgentToolInstrumentation(e){let t=new Map,n=new Map,onStarted=async t=>{let n=actionIdempotencyKey(t.scope.sessionId,t.scope.turnId,t.callId),i=e.resolveFallback(t),a=i===void 0?void 0:reserve(t,n,i),o=await e.actionContextFor(t.scope.sessionId,t.scope.turnId,t.callId);if(a===void 0){if(o===void 0)return;a=reserve(t,n,o)}o!==void 0&&startSpan(a,o.context)},onTerminal=async t=>{let r=n.get(t.scope.attemptId)?.get(t.idempotencyKey);if(r!==void 0){if(r.terminal=t,r.span===void 0){let t=await e.actionContextFor(r.event.scope.sessionId,r.event.scope.turnId,r.event.callId);t!==void 0&&startSpan(r,t.context)}finishIfReady(r)}};return{async actionStarted(n){let r=t.get(n.idempotencyKey);if(r===void 0||r.span!==void 0||r.finished===!0)return;let i=await e.actionContextFor(n.scope.sessionId,n.scope.turnId,n.callId);i!==void 0&&(startSpan(r,i.context),finishIfReady(r))},contextFor:(e,t)=>n.get(e)?.get(t)?.context,drain(e,t){let r=n.get(e);if(r!==void 0){for(let e of r.values())e.finished!==!0&&(e.span===void 0&&startSpan(e,e.fallbackParent),finish(e,t));n.delete(e)}},events:{"tool.call.completed":onTerminal,"tool.call.failed":onTerminal,"tool.call.started":onStarted}};function getAttemptStates(e){let t=n.get(e);return t===void 0&&(t=new Map,n.set(e,t)),t}function reserve(n,r,i){let a=e.idGenerator.deriveSpanId(`tool:${n.idempotencyKey}`),o={actionKey:r,attemptId:n.scope.attemptId,context:contextFromSpanContext({isRemote:!1,spanId:a,traceFlags:i.spanContext.traceFlags,traceId:i.spanContext.traceId}),event:n,fallbackParent:i.context,idempotencyKey:n.idempotencyKey,spanId:a,startTimeMs:Date.now()};return getAttemptStates(n.scope.attemptId).set(n.idempotencyKey,o),t.set(r,o),o}function startSpan(t,n){if(!(t.span!==void 0||t.finished===!0)&&(t.span=e.idGenerator.withSpanId(t.spanId,()=>e.tracer.startSpan(`ai.toolCall`,{attributes:toolAttributes(t.event),startTime:t.startTimeMs},n)),e.recordInputs)){let e=contentAttribute(t.event.input,!1);e!==void 0&&t.span.setAttribute(`gen_ai.tool.call.arguments`,e)}}function finishIfReady(e){e.span===void 0||e.terminal===void 0||finish(e)}function finish(r,a){let o=r.span;if(o===void 0||r.finished===!0)return;r.finished=!0;let s=r.terminal;if(a!==void 0)recordError(o,a.error);else if(s?.type===`tool.call.failed`)recordError(o,s.error);else if(s?.output.type===`error`)recordError(o,s.output.error);else if(s!==void 0&&e.recordOutputs){let e=contentAttribute(s.output.output,!1);e!==void 0&&o.setAttribute(`gen_ai.tool.call.result`,e)}o.end(),t.delete(r.actionKey);let c=n.get(r.attemptId);c?.delete(r.idempotencyKey),c?.size===0&&n.delete(r.attemptId)}}function toolAttributes(e){return{"gen_ai.operation.name":`execute_tool`,"gen_ai.tool.call.id":e.callId,"gen_ai.tool.name":e.toolName}}function contextFromSpanContext(t){return trace.setSpan(ROOT_CONTEXT,trace.wrapSpanContext(t))}function recordError(e,n){n instanceof Error?(e.recordException(n),e.setStatus({code:SpanStatusCode.ERROR,message:n.message})):e.setStatus({code:SpanStatusCode.ERROR})}export{createAgentToolInstrumentation};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SpanContext } from "#compiled/@opentelemetry/api/index.js";
|
|
2
|
-
import type { AgentSessionTraceState, AgentTraceStateStore, AgentTurnTraceState } from "#tracing/agent-trace-state.js";
|
|
2
|
+
import type { AgentActionTraceState, AgentSessionTraceState, AgentTraceStateStore, AgentTurnTraceState } from "#tracing/agent-trace-state.js";
|
|
3
3
|
/** Keeps only framework trace state from an interrupted step's context changes. */
|
|
4
4
|
export declare function preserveSerializedAgentTraceState(original: Record<string, unknown>, interrupted: Record<string, unknown>): Record<string, unknown>;
|
|
5
5
|
/**
|
|
@@ -8,12 +8,19 @@ export declare function preserveSerializedAgentTraceState(original: Record<strin
|
|
|
8
8
|
* to the ambient session.
|
|
9
9
|
*/
|
|
10
10
|
export declare function readSessionTraceContext(serializedContext: Readonly<Record<string, unknown>>, sessionId: string): SpanContext | undefined;
|
|
11
|
+
/** Reads the durable action span that should parent a dispatched child agent. */
|
|
12
|
+
export declare function readActionTraceContext(serializedContext: Readonly<Record<string, unknown>>, sessionId: string, turnId: string, callId: string): SpanContext | undefined;
|
|
11
13
|
/** Durable trace state backed by eve's serialized Workflow context. */
|
|
12
14
|
export declare class ContextAgentTraceStateStore implements AgentTraceStateStore {
|
|
15
|
+
deleteAction(idempotencyKey: string): void;
|
|
16
|
+
deleteActions(sessionId: string, turnId?: string): void;
|
|
13
17
|
deleteSession(sessionId: string): void;
|
|
14
18
|
deleteTurn(sessionId: string, turnId: string): void;
|
|
19
|
+
findAction(sessionId: string, callId: string): AgentActionTraceState | undefined;
|
|
20
|
+
getAction(idempotencyKey: string): AgentActionTraceState | undefined;
|
|
15
21
|
getSession(sessionId: string): AgentSessionTraceState | undefined;
|
|
16
22
|
getTurn(sessionId: string, turnId: string): AgentTurnTraceState | undefined;
|
|
23
|
+
setAction(idempotencyKey: string, value: AgentActionTraceState): void;
|
|
17
24
|
setSession(sessionId: string, value: AgentSessionTraceState): void;
|
|
18
25
|
setTurn(sessionId: string, turnId: string, value: AgentTurnTraceState): void;
|
|
19
26
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{contextStorage,loadContext}from"#context/container.js";import{ContextKey}from"#context/key.js";const AgentTraceContextKey=new ContextKey(`eve.harness.agentTrace`,{codec:{deserialize:deserializeState,serialize:serializeState}});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 deserializeState(n).sessions[t]?.context}var ContextAgentTraceStateStore=class{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}})}getSession(t){return contextStorage.getStore()?.get(AgentTraceContextKey)?.sessions[t]}getTurn(t,n){return contextStorage.getStore()?.get(AgentTraceContextKey)?.turns[turnKey(t,n)]}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??{sessions:{},turns:{}}))}function turnKey(e,t){return`${e}\0${t}`}function serializeState(e){return{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:{error:serializeError(t.terminal.error),type:t.terminal.type}}]))}}function deserializeState(e){return isRecord(e)?{sessions:deserializeRecord(e.sessions,e=>{if(!(!isRecord(e)||!isSpanContext(e.context)))return{agentName:typeof e.agentName==`string`?e.agentName:void 0,channelKind:typeof e.channelKind==`string`?e.channelKind:void 0,context:e.context,rootSessionId:typeof e.rootSessionId==`string`?e.rootSessionId:``,turnsInWindow:typeof e.turnsInWindow==`number`?e.turnsInWindow:0,window:typeof e.window==`number`?e.window:0}}),turns:deserializeRecord(e.turns,e=>{if(!(!isRecord(e)||!isSpanContext(e.context))&&!(typeof e.parentSpanId!=`string`||typeof e.startTimeMs!=`number`))return{context:e.context,lineage:deserializeLineage(e.lineage),parentSpanId:e.parentSpanId,rootSessionId:typeof e.rootSessionId==`string`?e.rootSessionId:``,sequence:typeof e.sequence==`number`?e.sequence:0,startTimeMs:e.startTimeMs,terminal:deserializeTerminal(e.terminal)}})}:{sessions:{},turns:{}}}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 deserializeLineage(e){if(!(!isRecord(e)||typeof e.callId!=`string`||typeof e.sessionId!=`string`||typeof e.turnId!=`string`))return{callId:e.callId,sessionId:e.sessionId,subagentName:typeof e.subagentName==`string`?e.subagentName:void 0,turnId:e.turnId}}function deserializeTerminal(e){if(!isRecord(e)||typeof e.type!=`string`)return;let t=e.type;if(isTurnTerminalType(t))return{error:deserializeError(e.error),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 isRecord(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}export{ContextAgentTraceStateStore,preserveSerializedAgentTraceState,readSessionTraceContext};
|
|
1
|
+
import{contextStorage,loadContext}from"#context/container.js";import{ContextKey}from"#context/key.js";const AgentTraceContextKey=new ContextKey(`eve.harness.agentTrace`,{codec:{deserialize:deserializeState,serialize:serializeState}});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 deserializeState(n).sessions[t]?.context}function readActionTraceContext(e,t,n,i){let a=e[AgentTraceContextKey.name];if(a===void 0)return;let o=Object.values(deserializeState(a).actions).find(e=>e.sessionId===t&&e.turnId===n&&e.callId===i);if(o!==void 0)return{isRemote:!1,spanId:o.spanId,traceFlags:o.parent.traceFlags,traceId:o.parent.traceId}}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(t,n){return Object.values(contextStorage.getStore()?.get(AgentTraceContextKey)?.actions??{}).find(e=>e.sessionId===t&&e.callId===n)}getAction(t){return contextStorage.getStore()?.get(AgentTraceContextKey)?.actions[t]}getSession(t){return contextStorage.getStore()?.get(AgentTraceContextKey)?.sessions[t]}getTurn(t,n){return contextStorage.getStore()?.get(AgentTraceContextKey)?.turns[turnKey(t,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(e){return isRecord(e)?{actions:deserializeRecord(e.actions,deserializeAction),sessions:deserializeRecord(e.sessions,e=>{if(!(!isRecord(e)||!isSpanContext(e.context)))return{agentName:typeof e.agentName==`string`?e.agentName:void 0,channelKind:typeof e.channelKind==`string`?e.channelKind:void 0,context:e.context,rootSessionId:typeof e.rootSessionId==`string`?e.rootSessionId:``,turnsInWindow:typeof e.turnsInWindow==`number`?e.turnsInWindow:0,window:typeof e.window==`number`?e.window:0}}),turns:deserializeRecord(e.turns,e=>{if(!(!isRecord(e)||!isSpanContext(e.context))&&!(typeof e.parentSpanId!=`string`||typeof e.startTimeMs!=`number`))return{context:e.context,lineage:deserializeLineage(e.lineage),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,terminal:deserializeTerminal(e.terminal)}})}:{actions:{},sessions:{},turns:{}}}function deserializeAction(e){if(!(!isRecord(e)||typeof e.attemptIndex!=`number`||typeof e.callId!=`string`||!isActionKind(e.kind)||typeof e.name!=`string`||!isSpanContext(e.parent)||typeof e.rootSessionId!=`string`||typeof e.sessionId!=`string`||typeof e.spanId!=`string`||typeof e.startTimeMs!=`number`||typeof e.stepIndex!=`number`||typeof e.turnId!=`string`))return{attemptIndex:e.attemptIndex,callId:e.callId,inputAttribute:typeof e.inputAttribute==`string`?e.inputAttribute:void 0,kind:e.kind,name:e.name,parent:e.parent,rootSessionId:e.rootSessionId,sessionId:e.sessionId,spanId:e.spanId,startTimeMs:e.startTimeMs,stepIndex:e.stepIndex,turnId:e.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 deserializeLineage(e){if(!(!isRecord(e)||typeof e.callId!=`string`||typeof e.sessionId!=`string`||typeof e.turnId!=`string`))return{callId:e.callId,sessionId:e.sessionId,subagentName:typeof e.subagentName==`string`?e.subagentName:void 0,turnId:e.turnId}}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 isRecord(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}export{ContextAgentTraceStateStore,preserveSerializedAgentTraceState,readActionTraceContext,readSessionTraceContext};
|
|
@@ -4,7 +4,6 @@ export declare class AgentTraceSpanProcessor implements SpanProcessor {
|
|
|
4
4
|
#private;
|
|
5
5
|
constructor(children: readonly SpanProcessor[]);
|
|
6
6
|
forceFlush(): Promise<void>;
|
|
7
|
-
isAttached(): boolean;
|
|
8
7
|
onStart(span: unknown, parentContext: unknown): void;
|
|
9
8
|
onEnd(span: unknown): void;
|
|
10
9
|
/** Trace ids whose session is still open, so retention never evicts them. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var AgentTraceSpanProcessor=class{#e;#t=new Set;#n=new Map
|
|
1
|
+
var AgentTraceSpanProcessor=class{#e;#t=new Set;#n=new Map;constructor(e){this.#e=e}async forceFlush(){await Promise.all(this.#e.map(e=>e.forceFlush()))}onStart(e,t){if(!isSpanLike(e))return;let n=e.attributes[`agent.session.id`];if(typeof n==`string`){let t=e.attributes[`agent.root.session.id`],r=typeof t==`string`?t:n,i=e.spanContext().traceId;this.#t.add(i);let a=this.#n.get(r)??new Set;a.add(i),this.#n.set(r,a)}if(this.#r(e))for(let n of this.#e)n.onStart(e,t)}onEnd(e){if(!(!isSpanLike(e)||!this.#r(e)))for(let t of this.#e)t.onEnd(e)}activeTraceIds(){return this.#t}releaseSession(e){let t=this.#n.get(e);if(t===void 0)return!1;for(let e of t)this.#t.delete(e);return this.#n.delete(e),!0}async shutdown(){await Promise.all(this.#e.map(e=>e.shutdown()))}#r(e){return e.instrumentationScope?.name!==`workflow`&&this.#t.has(e.spanContext().traceId)}};function isSpanLike(e){return typeof e==`object`&&!!e&&`attributes`in e&&`spanContext`in e&&typeof e.spanContext==`function`}export{AgentTraceSpanProcessor};
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { SpanContext } from "#compiled/@opentelemetry/api/index.js";
|
|
2
|
-
import type { InstrumentationParentLineage,
|
|
2
|
+
import type { InstrumentationActionKind, InstrumentationParentLineage, InstrumentationTraceContext, InstrumentationTurnFailedEvent, InstrumentationTurnSettledEvent } from "#harness/instrumentation-lifecycle.js";
|
|
3
|
+
/** Sized so an ordinary session stays one trace and only an outsized one rolls. */
|
|
4
|
+
export declare const SESSION_WINDOW_TURN_LIMIT = 200;
|
|
3
5
|
export interface AgentSessionTraceState {
|
|
4
6
|
readonly agentName?: string;
|
|
5
7
|
readonly channelKind?: string;
|
|
@@ -11,31 +13,59 @@ export interface AgentSessionTraceState {
|
|
|
11
13
|
export interface AgentTurnTraceState {
|
|
12
14
|
readonly context: SpanContext;
|
|
13
15
|
readonly lineage?: InstrumentationParentLineage;
|
|
16
|
+
readonly parentIsRemote?: boolean;
|
|
14
17
|
readonly parentSpanId: string;
|
|
15
18
|
readonly rootSessionId: string;
|
|
16
19
|
readonly sequence: number;
|
|
17
20
|
readonly startTimeMs: number;
|
|
18
21
|
readonly terminal?: {
|
|
19
|
-
readonly error
|
|
20
|
-
readonly type:
|
|
22
|
+
readonly error: unknown;
|
|
23
|
+
readonly type: InstrumentationTurnFailedEvent["type"];
|
|
24
|
+
} | {
|
|
25
|
+
readonly type: InstrumentationTurnSettledEvent["type"];
|
|
21
26
|
};
|
|
22
27
|
}
|
|
28
|
+
export interface AgentActionTraceState {
|
|
29
|
+
readonly attemptIndex: number;
|
|
30
|
+
readonly callId: string;
|
|
31
|
+
readonly inputAttribute?: string;
|
|
32
|
+
readonly kind: InstrumentationActionKind;
|
|
33
|
+
readonly name: string;
|
|
34
|
+
readonly parent: InstrumentationTraceContext;
|
|
35
|
+
readonly rootSessionId: string;
|
|
36
|
+
readonly sessionId: string;
|
|
37
|
+
readonly spanId: string;
|
|
38
|
+
readonly startTimeMs: number;
|
|
39
|
+
readonly stepIndex: number;
|
|
40
|
+
readonly turnId: string;
|
|
41
|
+
}
|
|
23
42
|
/** Provider-owned serializable storage for durable agent trace state. */
|
|
24
43
|
export interface AgentTraceStateStore {
|
|
44
|
+
deleteAction(idempotencyKey: string): void | PromiseLike<void>;
|
|
45
|
+
deleteActions(sessionId: string, turnId?: string): void | PromiseLike<void>;
|
|
25
46
|
deleteSession(sessionId: string): void | PromiseLike<void>;
|
|
26
47
|
deleteTurn(sessionId: string, turnId: string): void | PromiseLike<void>;
|
|
48
|
+
findAction(sessionId: string, callId: string): AgentActionTraceState | undefined | PromiseLike<AgentActionTraceState | undefined>;
|
|
49
|
+
getAction(idempotencyKey: string): AgentActionTraceState | undefined | PromiseLike<AgentActionTraceState | undefined>;
|
|
27
50
|
getSession(sessionId: string): AgentSessionTraceState | undefined | PromiseLike<AgentSessionTraceState | undefined>;
|
|
28
51
|
getTurn(sessionId: string, turnId: string): AgentTurnTraceState | undefined | PromiseLike<AgentTurnTraceState | undefined>;
|
|
52
|
+
setAction(idempotencyKey: string, state: AgentActionTraceState): void | PromiseLike<void>;
|
|
29
53
|
setSession(sessionId: string, state: AgentSessionTraceState): void | PromiseLike<void>;
|
|
30
54
|
setTurn(sessionId: string, turnId: string, state: AgentTurnTraceState): void | PromiseLike<void>;
|
|
31
55
|
}
|
|
32
56
|
/** In-memory trace state used by tests and non-durable runtimes. */
|
|
33
57
|
export declare class InMemoryAgentTraceStateStore implements AgentTraceStateStore {
|
|
34
58
|
#private;
|
|
59
|
+
deleteAction(idempotencyKey: string): void;
|
|
60
|
+
deleteActions(sessionId: string, turnId?: string): void;
|
|
35
61
|
deleteSession(sessionId: string): void;
|
|
36
62
|
deleteTurn(sessionId: string, turnId: string): void;
|
|
63
|
+
findAction(sessionId: string, callId: string): AgentActionTraceState | undefined;
|
|
64
|
+
getAction(idempotencyKey: string): AgentActionTraceState | undefined;
|
|
37
65
|
getSession(sessionId: string): AgentSessionTraceState | undefined;
|
|
38
66
|
getTurn(sessionId: string, turnId: string): AgentTurnTraceState | undefined;
|
|
67
|
+
setAction(idempotencyKey: string, state: AgentActionTraceState): void;
|
|
39
68
|
setSession(sessionId: string, state: AgentSessionTraceState): void;
|
|
40
69
|
setTurn(sessionId: string, turnId: string, state: AgentTurnTraceState): void;
|
|
41
70
|
}
|
|
71
|
+
export declare function turnKey(sessionId: string, turnId: string): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var InMemoryAgentTraceStateStore=class{#e=new Map;#t=new Map;
|
|
1
|
+
const SESSION_WINDOW_TURN_LIMIT=200;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,SESSION_WINDOW_TURN_LIMIT,turnKey};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { SpanExporter, SpanProcessor } from "#compiled/@vercel/otel/index.js";
|
|
2
|
+
/** @internal — exposed for tests; authors get the defaults. */
|
|
3
|
+
export interface BatchSpanProcessorOptions {
|
|
4
|
+
readonly exportTimeoutMillis?: number;
|
|
5
|
+
readonly maxExportBatchSize?: number;
|
|
6
|
+
readonly maxQueueSize?: number;
|
|
7
|
+
readonly scheduledDelayMillis?: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Buffers ended spans and hands them to one exporter in batches.
|
|
11
|
+
*
|
|
12
|
+
* eve owns this rather than taking `@opentelemetry/sdk-trace-base` as a runtime
|
|
13
|
+
* dependency: an exporter needs exactly one thing done for it, and the
|
|
14
|
+
* dependency would arrive with the whole SDK behind it.
|
|
15
|
+
*
|
|
16
|
+
* A full queue drops the newest span rather than growing without bound. An
|
|
17
|
+
* exporter that has stopped draining is already losing telemetry; taking the
|
|
18
|
+
* agent's memory with it would turn that into an outage.
|
|
19
|
+
*/
|
|
20
|
+
export declare function batchSpanProcessor(exporter: SpanExporter, options?: BatchSpanProcessorOptions): SpanProcessor;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createLogger,formatError}from"#internal/logging.js";import{TraceFlags,context,createContextKey}from"#compiled/@opentelemetry/api/index.js";const log=createLogger(`harness.batch-span-processor`),SUPPRESS_TRACING_KEY=createContextKey(`OpenTelemetry SDK Context Key SUPPRESS_TRACING`),DEFAULTS={exportTimeoutMillis:3e4,maxExportBatchSize:512,maxQueueSize:2048,scheduledDelayMillis:5e3};function batchSpanProcessor(e,n={}){let i=n.exportTimeoutMillis??DEFAULTS.exportTimeoutMillis,o=n.maxExportBatchSize??DEFAULTS.maxExportBatchSize,s=n.maxQueueSize??DEFAULTS.maxQueueSize,c=n.scheduledDelayMillis??DEFAULTS.scheduledDelayMillis,l=[],u,d=Promise.resolve(),f=!1;function cancelTimer(){u!==void 0&&(clearTimeout(u),u=void 0)}function scheduleDrain(){u===void 0&&(u=setTimeout(()=>{u=void 0,drain()},c),u.unref?.())}function drain(){return waitForExporter(pendingDrain(),`span export`).then(()=>void 0)}async function exportBatch(n){try{await new Promise((t,i)=>{try{context.with(context.active().setValue(SUPPRESS_TRACING_KEY,!0),()=>{e.export(n,e=>{if(e.code===0){t();return}i(e.error??Error(`Span export failed.`))})})}catch(e){i(e instanceof Error?e:Error(String(e)))}})}catch(e){log.warn(`span export failed`,{error:formatError(e)})}}return{async forceFlush(){f||!await waitForExporter(pendingDrain(),`span export`)||e.forceFlush===void 0||await waitForExporter(settleExporter(`exporter flush`,e.forceFlush),`exporter flush`)},onEnd(e){if(!f&&isSampled(e)&&!(l.length>=s)){if(l.push(e),l.length>=o){drain();return}scheduleDrain()}},onStart(){},async shutdown(){f=!0;let t=pendingDrain();if(!await waitForExporter(t,`span export`)){t.then(()=>settleExporter(`exporter shutdown`,e.shutdown));return}await waitForExporter(settleExporter(`exporter shutdown`,e.shutdown),`exporter shutdown`)}};function pendingDrain(){return cancelTimer(),d=d.then(async()=>{for(;l.length>0;)await exportBatch(l.splice(0,o))}),d}async function waitForExporter(e,t){let n,r=new Promise(e=>{n=setTimeout(()=>{log.warn(`${t} timed out; preserving exporter serialization`,{timeoutMillis:i}),e(!1)},i),n.unref?.()}),a=e.then(()=>!0);try{return await Promise.race([a,r])}finally{clearTimeout(n)}}async function settleExporter(n,r){try{await r.call(e)}catch(e){log.warn(`${n} failed`,{error:formatError(e)})}}}function isSampled(e){if(typeof e!=`object`||!e||!(`spanContext`in e))return!1;let t=e.spanContext;if(typeof t!=`function`)return!1;let r=Reflect.apply(t,e,[]);return typeof r.traceFlags==`number`&&(r.traceFlags&TraceFlags.SAMPLED)===TraceFlags.SAMPLED}export{batchSpanProcessor};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which span attributes carry conversation content, and in which direction.
|
|
3
|
+
*
|
|
4
|
+
* Two vocabularies land on the same spans: the ones eve sets on its own
|
|
5
|
+
* `agent.*` spans, and the ones the AI SDK's OpenTelemetry integration sets on
|
|
6
|
+
* the model-call spans beneath them. A destination that declined content has to
|
|
7
|
+
* be rid of both, so both are listed here rather than in the module that writes
|
|
8
|
+
* each.
|
|
9
|
+
*
|
|
10
|
+
* Listed by name rather than by prefix because the prefixes are shared with
|
|
11
|
+
* metadata that must survive: `ai.response.finish_reason` and
|
|
12
|
+
* `gen_ai.tool.name` say what happened, not what was said. The cost is that a
|
|
13
|
+
* new content attribute in a dependency is not covered until it is added here.
|
|
14
|
+
*/
|
|
15
|
+
/** What one destination is willing to receive. */
|
|
16
|
+
export interface ResolvedContentOptions {
|
|
17
|
+
readonly recordInputs: boolean;
|
|
18
|
+
readonly recordOutputs: boolean;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The attributes with the declined content removed, or `undefined` when there
|
|
22
|
+
* was none to remove.
|
|
23
|
+
*
|
|
24
|
+
* The `undefined` return is what lets the caller forward the original span
|
|
25
|
+
* untouched in the common case, so a destination that declined a direction the
|
|
26
|
+
* span never carried costs one pass over its keys and no allocation.
|
|
27
|
+
*/
|
|
28
|
+
export declare function withoutDeclinedContent(attributes: Readonly<Record<string, unknown>>, content: ResolvedContentOptions): Record<string, unknown> | undefined;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const INPUT_CONTENT_ATTRIBUTES=new Set([`agent.channel.delivery.input`,`ai.documents`,`ai.prompt`,`ai.prompt.messages`,`ai.prompt.system`,`ai.prompt.toolChoice`,`ai.prompt.tools`,`ai.value`,`ai.values`,`gen_ai.input.messages`,`gen_ai.system_instructions`,`gen_ai.tool.call.arguments`,`gen_ai.tool.definitions`]),OUTPUT_CONTENT_ATTRIBUTES=new Set([`ai.embedding`,`ai.embeddings`,`ai.ranking`,`ai.response.files`,`ai.response.object`,`ai.response.reasoning`,`ai.response.text`,`ai.response.toolCalls`,`ai.response.tool_calls`,`ai.response.tool_results`,`ai.toolCall.args`,`ai.toolCall.result`,`gen_ai.output.messages`,`gen_ai.tool.call.result`]);function isDeclined(n,r){return!r.recordInputs&&INPUT_CONTENT_ATTRIBUTES.has(n)?!0:!r.recordOutputs&&OUTPUT_CONTENT_ATTRIBUTES.has(n)}function withoutDeclinedContent(e,t){let n=Object.keys(e);if(!n.some(e=>isDeclined(e,t)))return;let r={};for(let i of n)isDeclined(i,t)||(r[i]=e[i]);return r}export{withoutDeclinedContent};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { SpanProcessor } from "#compiled/@vercel/otel/index.js";
|
|
2
|
+
import { type ResolvedContentOptions } from "#tracing/content-attributes.js";
|
|
3
|
+
/**
|
|
4
|
+
* Puts one destination's content policy in front of it.
|
|
5
|
+
*
|
|
6
|
+
* Content is written onto a span if any destination wants it, so the span
|
|
7
|
+
* reaching a destination that declined still carries it. This cannot strip the
|
|
8
|
+
* attribute in place: that span object is shared with every other processor in
|
|
9
|
+
* the pipeline, and editing it would strip the attribute everywhere. So it
|
|
10
|
+
* gives the destination a facade whose attributes omit what it declined.
|
|
11
|
+
*
|
|
12
|
+
* One facade follows the original from start through end. This preserves the
|
|
13
|
+
* object identity stateful processors key on without ever exposing the original
|
|
14
|
+
* span or its attribute map. Methods stay bound to the original, so private SDK
|
|
15
|
+
* state remains reachable without eve knowing that SDK's concrete span shape.
|
|
16
|
+
*
|
|
17
|
+
* @internal
|
|
18
|
+
*/
|
|
19
|
+
export declare function contentFilteringProcessor(downstream: SpanProcessor, content: ResolvedContentOptions): SpanProcessor;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{hasSessionRelease}from"#tracing/local-traces.js";import{withoutDeclinedContent}from"#tracing/content-attributes.js";function contentFilteringProcessor(t,n){if(n.recordInputs&&n.recordOutputs)return t;let r=new WeakMap,i={forceFlush:()=>t.forceFlush(),onEnd:e=>{if(typeof e!=`object`||!e){t.onEnd(e);return}let i=facadeFor(e,n,r);i.refresh();try{t.onEnd(i.value)}finally{r.delete(e)}},onStart:(e,i)=>{if(typeof e!=`object`||!e){t.onStart(e,i);return}t.onStart(facadeFor(e,n,r).value,i)},shutdown:()=>t.shutdown()};return hasSessionRelease(t)?{...i,releaseSession:e=>t.releaseSession(e)}:i}function facadeFor(e,t,n){let r=n.get(e);if(r!==void 0)return r;let i={},a=[],o={},s=Object.create(Reflect.getPrototypeOf(e)),c=new Map,refresh=()=>{refreshAttributes(i,e,t),refreshEvents(a,e,t),refreshStatus(o,e,t)},l,readOriginal=t=>{let n=Reflect.get(e,t,e);if(typeof n!=`function`||t===`constructor`)return n;let r=c.get(t);if(r!==void 0)return r;let created=(...t)=>{let r=Reflect.apply(n,e,t);return refresh(),r===e?l:r};return c.set(t,created),created};Object.defineProperty(s,"attributes",{configurable:!0,enumerable:!0,value:i}),Object.defineProperty(s,"events",{configurable:!0,enumerable:!0,value:a}),Object.defineProperty(s,"status",{configurable:!0,enumerable:!0,value:o});for(let t of Reflect.ownKeys(e)){if(t===`attributes`||t===`events`||t===`status`)continue;let n=Reflect.getOwnPropertyDescriptor(e,t);Object.defineProperty(s,t,{configurable:!0,enumerable:n?.enumerable??!1,get:()=>readOriginal(t)})}l=new Proxy(s,{get:(e,t,n)=>Object.hasOwn(e,t)?Reflect.get(e,t,n):readOriginal(t)});let u={refresh,value:l};return u.refresh(),n.set(e,u),u}function refreshAttributes(e,n,r){for(let t of Object.keys(e))delete e[t];let i=n.attributes;if(typeof i!=`object`||!i)return;let a=withoutDeclinedContent(i,r);Object.assign(e,a??i)}function refreshEvents(e,t,n){e.length=0;let r=t.events;if(Array.isArray(r)){if(n.recordOutputs){e.push(...r);return}for(let t of r){if(typeof t!=`object`||!t)continue;let n=t;n.name!==`exception`&&e.push({...n,attributes:void 0})}}}function refreshStatus(e,t,n){for(let t of Object.keys(e))delete e[t];let r=t.status;if(typeof r!=`object`||!r)return;let i=r;i.code!==void 0&&(e.code=i.code),n.recordOutputs&&i.message!==void 0&&(e.message=i.message)}export{contentFilteringProcessor};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type InstrumentationProviderDefinition } from "#harness/instrumentation-lifecycle.js";
|
|
2
|
+
import { type InstrumentationRuntime } from "#harness/instrumentation-runtime.js";
|
|
3
|
+
import type { CollectedOtel } from "#tracing/otel-declaration.js";
|
|
4
|
+
/**
|
|
5
|
+
* Installs the process instrumentation runtime around a collected pipeline.
|
|
6
|
+
*
|
|
7
|
+
* Both layouts land here. `eve dev`'s zero-config default and an authored
|
|
8
|
+
* `agent/instrumentation/` directory differ only in where the declared values
|
|
9
|
+
* came from, so sharing the install keeps them on one runtime path.
|
|
10
|
+
*
|
|
11
|
+
* A directory that declared no OpenTelemetry still gets a bus: its providers
|
|
12
|
+
* see every event, they just have no spans to hang them on.
|
|
13
|
+
*/
|
|
14
|
+
export declare function installInstrumentationRuntime(input: {
|
|
15
|
+
readonly collected: CollectedOtel;
|
|
16
|
+
readonly frameworkVersion: string;
|
|
17
|
+
readonly providers: readonly InstrumentationProviderDefinition[];
|
|
18
|
+
readonly serviceName: string;
|
|
19
|
+
}): InstrumentationRuntime;
|
|
@@ -0,0 +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{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,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)});t.push({...r.hook,stateNamespace:`internal:otel`}),runInContext=r.runInContext;let i=e.collected.pipeline.spanProcessors.filter(isSpanProcessor).filter(hasSessionRelease);i.length>0&&a.push(sessionReleaseProvider(i))}let s=[...t,...e.providers,...a],c;return registerInstrumentationRuntime({forceFlush:()=>settleAll([...o===void 0?[]:[o.forceFlush],...s.map(e=>()=>e.flush?.())]),hooks:createInstrumentationHooks({parallel:e.providers,serialAfter:a,serialBefore:t}),otelSettings:e.collected.declared?e.collected.settings:void 0,runInContext,shutdown:()=>(c??=settleAll([...o===void 0?[]:[o.shutdown],...s.map(e=>()=>e.shutdown?.())]),c)})}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 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{getInstrumentationRuntime}from"#harness/instrumentation-runtime.js";import{installInstrumentationRuntime}from"#tracing/install-instrumentation-runtime.js";import{collectOtelPipeline,otel,otelIntegration}from"#tracing/otel-declaration.js";import{createLocalTracesProcessor,resolveLocalTracesContent}from"#tracing/local-traces.js";function installLocalInstrumentationRuntime(e){let t=getInstrumentationRuntime();if(t!==void 0)return t;let n=createLocalTracesProcessor({appRoot:e.appRoot});return installInstrumentationRuntime({collected:collectOtelPipeline([otel(),otelIntegration({...resolveLocalTracesContent(),spanProcessors:[n]})]),frameworkVersion:e.frameworkVersion,providers:[],serviceName:e.serviceName})}export{installLocalInstrumentationRuntime};
|