Summary
Propose adding gen_ai.agent.action_ref — a deterministic, recomputable SHA-256 handle — as an optional span attribute in trace_tool_span() inside autogen_core/_telemetry/_genai.py.
Motivation
trace_tool_span() today emits gen_ai.tool.name, gen_ai.tool.call.id, and gen_ai.tool.description. These are producer-scoped: a consumer comparing spans across two independent producers, or auditing a span without trusting the emitting runtime, has no recomputable correlation key.
This gap was raised in #7405 (GuardrailProvider) by multiple contributors, including @rpelevin:
"canonical action_ref: stable identity of the action class and actor, with JCS-normalized consequential input."
The eight attributes proposed in open-telemetry/semantic-conventions-genai#291 address the same producer-scoped gap. A ninth attribute — gen_ai.agent.action_ref — closes the cross-producer case.
Proposed Change
Three additions to autogen_core/_telemetry/_genai.py:
1. Constant:
GEN_AI_AGENT_ACTION_REF = "gen_ai.agent.action_ref"
2. Derivation helper:
def derive_action_ref(
agent_id: str,
action_type: str,
scope: str,
timestamp_ms: int,
) -> str:
"""Derive action_ref per action-ref-v1.
Canonical derivation: SHA-256(RFC 8785 JCS({agent_id, action_type, scope, timestamp_ms})).
For production use the `jcs` package (PyPI) or any RFC 8785-conformant serializer.
The snippet below is correct for the canonical preimage fields (string/int only —
no floats, no unicode escapes) and is included for readability:
import hashlib, json
preimage = json.dumps(
{"action_type": action_type, "agent_id": agent_id,
"scope": scope, "timestamp_ms": timestamp_ms},
sort_keys=True, separators=(",", ":"),
)
return hashlib.sha256(preimage.encode()).hexdigest()
Byte-identity across runtimes is verified by the conformance suites listed below.
"""
3. Optional parameter on trace_tool_span():
@contextmanager
def trace_tool_span(
tool_name: str,
*,
tracer: Optional[trace.Tracer] = None,
parent: Optional[Span] = None,
tool_description: Optional[str] = None,
tool_call_id: Optional[str] = None,
action_ref: Optional[str] = None, # <-- new
) -> Generator[Span, Any, None]:
...
if action_ref is not None:
span_attributes[GEN_AI_AGENT_ACTION_REF] = action_ref
No new dependencies for the attribute constant and parameter. The derivation helper uses only stdlib (hashlib, json) for the reference implementation; jcs is recommended for production. Zero breaking changes — the parameter is optional.
Properties
- Recomputable without a service call: any implementation following the derivation produces the same 32-byte hex from the same four fields. A verifier does not need to call any external service — the correlation is independently verifiable from the preimage fields alone.
- Cross-producer correlation: two independent runtimes emitting spans for the same agent action produce the same
action_ref, enabling cross-framework audit without shared state.
- Additive alongside
gen_ai.tool.call.id: call_id is the runtime's session-scoped handle; action_ref is the verifier's recomputable handle. They serve different consumers.
Normative Reference
Derivation spec: draft-giskard-aeoess-action-ref-00
Three independent production implementations with byte-identical conformance suites:
examples/conformance/presidio/ — vstantch/presidio-hardened-x402 (arXiv 2604.11430) → argentum-core
examples/conformance/aps/ — AEOESS/agent-passport-system (co-author, I-D) → argentum-core
conformance/agentgraph/ — kenneives/AgentGraph (CTEF v0.4 normative) → draft-giskard-aeoess-action-ref
Relation to #7405
GuardrailProvider is the policy enforcement layer. action_ref is the correlation key that binds the pre-execution guardrail decision to the post-execution span — without either layer trusting the other. They compose; neither depends on the other being merged first.
Happy to open a PR directly if useful.
Summary
Propose adding
gen_ai.agent.action_ref— a deterministic, recomputable SHA-256 handle — as an optional span attribute intrace_tool_span()insideautogen_core/_telemetry/_genai.py.Motivation
trace_tool_span()today emitsgen_ai.tool.name,gen_ai.tool.call.id, andgen_ai.tool.description. These are producer-scoped: a consumer comparing spans across two independent producers, or auditing a span without trusting the emitting runtime, has no recomputable correlation key.This gap was raised in #7405 (GuardrailProvider) by multiple contributors, including @rpelevin:
The eight attributes proposed in open-telemetry/semantic-conventions-genai#291 address the same producer-scoped gap. A ninth attribute —
gen_ai.agent.action_ref— closes the cross-producer case.Proposed Change
Three additions to
autogen_core/_telemetry/_genai.py:1. Constant:
2. Derivation helper:
3. Optional parameter on
trace_tool_span():No new dependencies for the attribute constant and parameter. The derivation helper uses only stdlib (
hashlib,json) for the reference implementation;jcsis recommended for production. Zero breaking changes — the parameter is optional.Properties
action_ref, enabling cross-framework audit without shared state.gen_ai.tool.call.id:call_idis the runtime's session-scoped handle;action_refis the verifier's recomputable handle. They serve different consumers.Normative Reference
Derivation spec: draft-giskard-aeoess-action-ref-00
Three independent production implementations with byte-identical conformance suites:
examples/conformance/presidio/— vstantch/presidio-hardened-x402 (arXiv 2604.11430) → argentum-coreexamples/conformance/aps/— AEOESS/agent-passport-system (co-author, I-D) → argentum-coreconformance/agentgraph/— kenneives/AgentGraph (CTEF v0.4 normative) → draft-giskard-aeoess-action-refRelation to #7405
GuardrailProvideris the policy enforcement layer.action_refis the correlation key that binds the pre-execution guardrail decision to the post-execution span — without either layer trusting the other. They compose; neither depends on the other being merged first.Happy to open a PR directly if useful.