11import { SeverityNumber } from "@opentelemetry/api-logs"
2- import { SpanStatusCode , trace } from "@opentelemetry/api"
2+ import { SpanStatusCode } from "@opentelemetry/api"
33import type { EventSessionCreated , EventSessionIdle , EventSessionError , EventSessionStatus } from "@opencode-ai/sdk"
4- import { AGENT_NAME , OpenInferenceSpanKind , SemanticConventions , SESSION_ID } from "@arizeai/openinference-semantic-conventions"
5- import { agentAttrs , errorSummary , getSessionAgentMeta , setBoundedMap , isMetricEnabled , isTraceEnabled } from "../util.ts"
4+ import {
5+ AGENT_NAME ,
6+ INPUT_MIME_TYPE ,
7+ INPUT_VALUE ,
8+ LLM_INPUT_MESSAGES ,
9+ MimeType ,
10+ OpenInferenceSpanKind ,
11+ SemanticConventions ,
12+ SESSION_ID ,
13+ } from "@arizeai/openinference-semantic-conventions"
14+ import {
15+ agentAttrs ,
16+ errorSummary ,
17+ getSessionAgentMeta ,
18+ setBoundedMap ,
19+ isMetricEnabled ,
20+ isTraceEnabled ,
21+ resolveSessionTraceContext ,
22+ } from "../util.ts"
623import type { HandlerContext , SessionAgentType } from "../types.ts"
724
825const OPENINFERENCE_SPAN_KIND = SemanticConventions . OPENINFERENCE_SPAN_KIND
926
27+ export function handleRunStarted (
28+ sessionID : string ,
29+ agent : string ,
30+ promptText : string ,
31+ model : string ,
32+ startTime : number ,
33+ ctx : HandlerContext ,
34+ ) {
35+ if ( ! isTraceEnabled ( "session" , ctx ) ) return
36+ const existing = ctx . runSpans . get ( sessionID )
37+ if ( existing ) {
38+ existing . setAttributes ( {
39+ [ AGENT_NAME ] : agent ,
40+ ...( promptText
41+ ? {
42+ [ INPUT_VALUE ] : promptText ,
43+ [ INPUT_MIME_TYPE ] : MimeType . TEXT ,
44+ [ LLM_INPUT_MESSAGES ] : JSON . stringify ( [ { role : "user" , content : promptText } ] ) ,
45+ }
46+ : { } ) ,
47+ model,
48+ } )
49+ return
50+ }
51+
52+ const runSpan = ctx . tracer . startSpan (
53+ `${ ctx . tracePrefix } session` ,
54+ {
55+ startTime,
56+ attributes : {
57+ [ OPENINFERENCE_SPAN_KIND ] : OpenInferenceSpanKind . AGENT ,
58+ [ SESSION_ID ] : sessionID ,
59+ [ AGENT_NAME ] : agent ,
60+ "agent.type" : "primary" ,
61+ "session.is_subagent" : false ,
62+ ...( promptText
63+ ? {
64+ [ INPUT_VALUE ] : promptText ,
65+ [ INPUT_MIME_TYPE ] : MimeType . TEXT ,
66+ [ LLM_INPUT_MESSAGES ] : JSON . stringify ( [ { role : "user" , content : promptText } ] ) ,
67+ }
68+ : { } ) ,
69+ model,
70+ ...ctx . commonAttrs ,
71+ } ,
72+ } ,
73+ ctx . rootContext ( ) ,
74+ )
75+ setBoundedMap ( ctx . runSpans , sessionID , runSpan )
76+ setBoundedMap ( ctx . runSpanContexts , sessionID , runSpan . spanContext ( ) )
77+ setBoundedMap ( ctx . sessionRunRoots , sessionID , sessionID )
78+ }
79+
1080/** Increments the session counter, records start time, starts the root session span, and emits a `session.created` log event. */
1181export function handleSessionCreated ( e : EventSessionCreated , ctx : HandlerContext ) {
1282 const { id : sessionID , time, parentID } = e . properties . info
@@ -18,16 +88,9 @@ export function handleSessionCreated(e: EventSessionCreated, ctx: HandlerContext
1888 }
1989 setBoundedMap ( ctx . sessionTotals , sessionID , { startMs : createdAt , tokens : 0 , cost : 0 , messages : 0 , agent : "unknown" , agentType } )
2090
21- // WARNING: disabling "session" traces while "llm" or "tool" traces remain enabled
22- // leaves those child spans without a local session parent. If OPENCODE_TRACEPARENT
23- // is set, they fall back to that remote parent; otherwise they become root spans.
24- if ( isTraceEnabled ( "session" , ctx ) ) {
25- const parentSpan = parentID ? ctx . sessionSpans . get ( parentID ) : undefined
26- const baseCtx = ctx . rootContext ( )
27- const spanCtx = parentSpan
28- ? trace . setSpan ( baseCtx , parentSpan )
29- : baseCtx
30-
91+ if ( isTraceEnabled ( "session" , ctx ) && parentID ) {
92+ const runRootID = ctx . sessionRunRoots . get ( parentID ) ?? parentID
93+ setBoundedMap ( ctx . sessionRunRoots , sessionID , runRootID )
3194 const sessionSpan = ctx . tracer . startSpan (
3295 `${ ctx . tracePrefix } session` ,
3396 {
@@ -41,9 +104,10 @@ export function handleSessionCreated(e: EventSessionCreated, ctx: HandlerContext
41104 ...ctx . commonAttrs ,
42105 } ,
43106 } ,
44- spanCtx ,
107+ resolveSessionTraceContext ( parentID , ctx ) ,
45108 )
46109 setBoundedMap ( ctx . sessionSpans , sessionID , sessionSpan )
110+ setBoundedMap ( ctx . sessionSpanContexts , sessionID , sessionSpan . spanContext ( ) )
47111 }
48112
49113 ctx . emitLog ( {
@@ -128,6 +192,21 @@ export function handleSessionIdle(e: EventSessionIdle, ctx: HandlerContext) {
128192 sessionSpan . end ( )
129193 ctx . sessionSpans . delete ( sessionID )
130194 }
195+ const runSpan = ctx . runSpans . get ( sessionID )
196+ if ( runSpan ) {
197+ if ( totals ) {
198+ runSpan . setAttributes ( {
199+ [ AGENT_NAME ] : totals . agent ,
200+ "agent.type" : totals . agentType ,
201+ "session.total_tokens" : totals . tokens ,
202+ "session.total_cost_usd" : totals . cost ,
203+ "session.total_messages" : totals . messages ,
204+ } )
205+ }
206+ runSpan . setStatus ( { code : SpanStatusCode . OK } )
207+ runSpan . end ( )
208+ ctx . runSpans . delete ( sessionID )
209+ }
131210
132211 ctx . emitLog ( {
133212 severityNumber : SeverityNumber . INFO ,
@@ -173,6 +252,14 @@ export function handleSessionError(e: EventSessionError, ctx: HandlerContext) {
173252 sessionSpan . end ( )
174253 ctx . sessionSpans . delete ( rawID )
175254 }
255+ const runSpan = ctx . runSpans . get ( rawID )
256+ if ( runSpan ) {
257+ if ( totals ) runSpan . setAttributes ( { [ AGENT_NAME ] : totals . agent , "agent.type" : totals . agentType } )
258+ runSpan . setStatus ( { code : SpanStatusCode . ERROR , message : error } )
259+ runSpan . setAttribute ( "error" , error )
260+ runSpan . end ( )
261+ ctx . runSpans . delete ( rawID )
262+ }
176263 }
177264
178265 ctx . emitLog ( {
0 commit comments