Skip to content

feat(telemetry): emit turn_id and agent_id on turn and tool events#1675

Draft
7Sageer wants to merge 10 commits into
MoonshotAI:mainfrom
7Sageer:fix/turn-lifecycle-telemetry-turn-id
Draft

feat(telemetry): emit turn_id and agent_id on turn and tool events#1675
7Sageer wants to merge 10 commits into
MoonshotAI:mainfrom
7Sageer:fix/turn-lifecycle-telemetry-turn-id

Conversation

@7Sageer

@7Sageer 7Sageer commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator

Related Issue

No linked issue — this is a telemetry data-quality fix; the problem is explained below.

Problem

Turn and tool telemetry events could not be reliably attributed to a specific agent within a session:

  • turn_id was missing from most turn/tool events, and where present it is only a per-agent counter — the main agent and every subagent each restart from 0.
  • No event carried any agent identifier, so once a session spawns a subagent, turn_id collides across agents and there is no way to tell whether a turn_* / tool_call / api_error event came from the main agent or a subagent. Background subagents interleave with the main agent on the timeline, so ordering cannot disambiguate them either.

As a result, downstream analysis (per-turn cost, tool error rates, subagent effectiveness, dedup behavior) cannot attribute events to an agent.

What changed

Make turn and tool telemetry attributable via (session_id, agent_id, turn_id):

  1. Emit turn_id on turn_started / turn_interrupted / turn_ended (v1 and v2) and on tool_call (v1), and clarify in the registry that turn_id is a per-agent index.
  2. Emit agent_id (the main agent is main; subagents use their scope id) on turn_started / turn_interrupted / turn_ended, tool_call, tool_call_dedup_detected, api_error, on subagent_created (the spawned child's agent_id, plus parent_agent_id so a spawn event joins to the child's later events), and on the agent-level settings events model_switch / thinking_toggle / skill_invoked / flow_invoked / yolo_toggle / afk_toggle.

turn_id stays per-agent — it is persisted and referenced by the wire protocol, approvals, and the activity kernel — so this adds a dimension instead of renumbering turns globally, keeping the change additive and low-risk.

How agent_id is sourced:

  • v2 (agent-core-v2): the Agent-scoped AgentTelemetryContextService injects the existing IAgentScopeContext and seeds agent_id from scopeContext.agentId. Turn events pick it up through the ambient context; tool_call / tool_call_dedup_detected / api_error, which are emitted through the App-scope telemetry singleton, pass agent_id explicitly from IAgentScopeContext. The Agent-scoped profile / skill / rpc services do the same for the settings events (model_switch / thinking_toggle / skill_invoked / flow_invoked / yolo_toggle / afk_toggle).
  • v1 (agent-core): each agent's telemetry client is wrapped once at construction with withTelemetryProperties(..., { agent_id: id }), covering every event that agent emits.

agent_id is registered on the affected event definitions in events.ts (with owner/purpose metadata), so it is type-checked and documented like every other telemetry property. v1's wrapper covers every agent-level event and v2 now carries agent_id on the same event names, so the two engines agree on every event they share.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue, or explained the problem above.
  • I have added tests that prove my feature works.
  • Ran gen-changesets skill, or this PR needs no changeset.
  • Ran gen-docs skill, or this PR needs no doc update.

7Sageer added 6 commits July 14, 2026 12:12
The turn lifecycle telemetry events (turn_started, turn_ended,
turn_interrupted) never carried the turn id, while tool_call and
tool_call_dedup_detected did. Any analysis correlating a turn's start,
end, or interruption back to its tool calls had nothing to join on.

Add turn_id (already in scope) to all three track() calls, matching the
existing key/value convention used by tool_call_dedup_detected.

Update the strict turn_started/turn_interrupted assertion to cover it.
…lemetry

Port the v1 fix to agent-core-v2: turn lifecycle telemetry events
(turn_started, turn_ended, turn_interrupted) carried no turn id while
tool_call did, leaving nothing to correlate a turn's start, end, or
interruption back to its tool calls.

Add turn_id to the three event interfaces, the telemetry registry
property docs, and the three track2() calls in AgentLoopService,
matching the existing ToolCallEvent key convention. Extend the
turn telemetry assertions in loop.test.ts to cover it.
turn_id is a per-agent counter, so it collides across the main agent and subagents within a session. Emit each agent's scope id as agent_id on turn_*, tool_call, tool_call_dedup_detected, api_error and subagent_created (plus parent_agent_id) so events become attributable via (session_id, agent_id, turn_id).
@changeset-bot

changeset-bot Bot commented Jul 14, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ccd67e5

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 5 packages
Name Type
@moonshot-ai/agent-core Patch
@moonshot-ai/agent-core-v2 Patch
@moonshot-ai/kimi-code Patch
@moonshot-ai/kap-server Patch
@moonshot-ai/klient Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

7Sageer added 4 commits July 14, 2026 15:27
- subagent_created: parent_tool_call_id, so a child run joins to the tool call that launched it
- permission_policy_decision / permission_approval_result: agent_id, turn_id, tool_call_id
- plan_submitted / plan_resolved / plan_enter_resolved, context_projection_repaired: agent_id
- compaction_finished / compaction_failed: agent_id + optional turn_id
- cron_scheduled / cron_deleted: optional agent_id of the scheduling agent
- api_error: turn_id + request_kind, so compaction request failures are distinguishable from turn requests
- tool_call_repeat: agent_id + optional turn_id; tool_call_dedup_detected stops fabricating turn_id: 0 outside a turn
- background_task_created/completed: task_id on both, unified kind vocabulary ('process' replaces the legacy 'bash' alias on created)
- agent lifecycle: auto-assigned agent-N ids now skip ids persisted by previous runs, so a resumed session cannot reissue agent-0 and collide with earlier telemetry
…telemetry-turn-id

# Conflicts:
#	packages/agent-core-v2/src/agent/fullCompaction/fullCompactionService.ts
#	packages/agent-core-v2/src/agent/loop/loopService.ts
#	packages/agent-core-v2/src/app/telemetry/events.ts
#	packages/agent-core-v2/src/session/agentLifecycle/agentLifecycleService.ts
#	packages/agent-core-v2/test/agent/llmRequester/llmRequesterService.test.ts
#	packages/agent-core-v2/test/agent/loop/loop.test.ts
#	packages/agent-core-v2/test/agent/toolExecutor/toolExecutor.test.ts
#	packages/agent-core-v2/test/session/agentLifecycle/agentLifecycle.test.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant