@@ -1747,6 +1747,15 @@ async function sendJobConclusionSpan(spanName, options = {}) {
17471747 if ( typeof runtimeMetrics . estimatedCostUsd === "number" ) {
17481748 attributes . push ( buildAttr ( "gh-aw.estimated_cost_usd" , runtimeMetrics . estimatedCostUsd ) ) ;
17491749 }
1750+ if ( jobName === "agent" ) {
1751+ // Emit OTel GenAI semantic attributes on agent conclusion spans even when the
1752+ // dedicated agent sub-span is missing, so LLM activity remains discoverable.
1753+ attributes . push ( buildAttr ( "gen_ai.operation.name" , "chat" ) ) ;
1754+ if ( workflowName ) attributes . push ( buildAttr ( "gen_ai.workflow.name" , workflowName ) ) ;
1755+ if ( runtimeMetrics . stopReason ) {
1756+ attributes . push ( buildArrayAttr ( "gen_ai.response.finish_reasons" , [ runtimeMetrics . stopReason ] ) ) ;
1757+ }
1758+ }
17501759
17511760 if ( agentConclusion ) {
17521761 attributes . push ( buildAttr ( "gh-aw.agent.conclusion" , agentConclusion ) ) ;
@@ -1939,20 +1948,8 @@ async function sendJobConclusionSpan(spanName, options = {}) {
19391948 // Token-usage attributes are included here (and only here) to prevent
19401949 // double-counting with the conclusion span.
19411950 const agentAttributes = [ ...attributes , ...usageAttrs ] ;
1942- // gen_ai.operation.name is Required by the OTel GenAI spec for inference spans.
1943- // All gh-aw agent executions are chat-style LLM completions.
1944- agentAttributes . push ( buildAttr ( "gen_ai.operation.name" , "chat" ) ) ;
1945- // gen_ai.request.model is already present in agentAttributes via the spread above
1946- // (added to attributes at the top of this function); do not push again.
1947- // gen_ai.workflow.name identifies the agentic workflow, matching the OTel spec example
1948- // use-cases (e.g. "multi_agent_rag", "customer_support_pipeline").
1949- if ( workflowName ) agentAttributes . push ( buildAttr ( "gen_ai.workflow.name" , workflowName ) ) ;
1950- // gen_ai.response.finish_reasons is a standard OTel GenAI response attribute (array of strings).
1951- // It exposes the stop_reason from the agent's result line so operators can detect truncated
1952- // runs (e.g. "max_tokens") that would otherwise silently appear as STATUS_OK.
1953- if ( runtimeMetrics . stopReason ) {
1954- agentAttributes . push ( buildArrayAttr ( "gen_ai.response.finish_reasons" , [ runtimeMetrics . stopReason ] ) ) ;
1955- }
1951+ // gen_ai.operation.name / gen_ai.workflow.name / gen_ai.response.finish_reasons
1952+ // are already included via the shared attributes list above.
19561953
19571954 const agentPayload = buildOTLPPayload ( {
19581955 traceId,
0 commit comments