/** * `tool_activity` events (#357, doc/agent-exec-protocol.md §3.2) — start/end * pairs for NATIVE tool calls (Bash, Edit, Write, Read, ...) on `agent exec` * stdout, correlated by the SDK's own tool_use id, in every access mode * (observability only — never affects the canUseTool allow/deny decision). * * New module rather than a growth of orgrt/agent-exec.ts (already at this * project's 500-line file cap) — agent-exec.ts only owns wiring: it feeds * every 'tool_use'/'tool_result' AgentMessage to a ToolActivityTracker and * emits whatever it produces. * * Deliberately message-stream-driven, not canUseTool-driven: start/end * pairing (onMessage/onToolUse/onToolResult) reads ONLY the AgentMessage * stream every access mode already produces. `wrapCanUseTool`/`markDenied` * are a separate, optional add-on solely for the scoped-mode `denied:true` * end field — #355's `--access full` runs the SDK with * `permissionMode:'bypassPermissions'`, under which canUseTool is shadowed * and never invoked at all (`CLAUDE_SDK_CAN_USE_TOOL_SHADOWED`); this * tracker degrades safely there (the `denied` set simply stays empty, `ok` * still comes from the real tool_result's `is_error`) rather than losing * start/end pairing, which would be the case if it were built from * canUseTool call sites instead of the message stream. * * Bridged (`--tools stdio`) calls keep their existing `tool_call`/ * `tool_result` frames (§4) untouched: the Claude SDK reports those with the * `mcp__org__` prefix agent-exec.ts registers them under (see its own * `allowedToolNames`), so this module simply skips any tool_use/tool_result * whose name carries that prefix. * * Fidelity varies by runtime (doc §9, runner-registry.ts's * `toolActivityFidelity`): * - `full` (claude, codex, opencode, antigravity, kimicode, grok, qwen, * copilot): start/end * pairs correlated by a real id — any runner that yields the rich * `tool_use` shape (id + name) and a matching `tool_result` gets them, * whatever its runtime. ClaudeAgentRunner gates its rich shape behind * extras.includePartialMessages (agent-exec.ts sets it, session.ts, the * org runtime, never does). * - `start-only` (crush/pi): a lightweight * `{type:'tool_use', text: toolName}` liveness signal with no id — * mapped best-effort to a START-ONLY tool_activity under a locally- * minted id (no matching end is possible without one, and none is * emitted). A `full` runner may still send one for a call it cannot pair. * - `none` (vercel/hermes/qwen-rpc/pi-rpc): no tool_activity at all. * * Rev 19: every start carries `kind` (tool-kind.ts) — the runner's own * `AgentMessage.kind` when set, else derived from the tool name — and an * end carries `exit_code` when the runner reported one. * * Rev 24 (#387, doc §3.2.1): with `synthesizeSubagents`, a `kind:"task"` * call with a real id also yields `subagent started` right after its start * and `subagent finished` right after its end — the same shape claude's * SDK-based events have, at lower fidelity (no `progress`, no `usage`). * agent-exec.ts turns it on for every runtime not in * NATIVE_SUBAGENT_RUNTIMES, so claude never gets both. */ import type { AgentMessage } from './agent-runner.js'; /** Runtimes whose runner yields real `subagent` AgentMessages (claude: the * Agent SDK's task_* messages, agent-runner-claude-subagent.ts). Every other * runtime gets them synthesized from its task-kind tool calls (rev 24). */ export declare const NATIVE_SUBAGENT_RUNTIMES: ReadonlySet; export declare function isBridgedToolName(name: string | undefined): boolean; type CanUseTool = (toolName: string, input: Record, meta?: { toolUseId?: string; }) => Promise; export declare class ToolActivityTracker { private emit; private fidelity; private synthesizeSubagents; /** ids with an emitted start and no emitted end yet — closed with * `cancelled:true` if the turn ends before a matching tool_result. */ private open; /** ids the caller's own canUseTool denied, recorded via wrapCanUseTool * below at the moment of decision — the runner has no policy concept of * its own to source this from. Consumed (checked-and-removed) by the * matching tool_result. */ private denied; private syntheticCounter; /** Count of every `tool_activity` "start" emitted (native rich-shape calls * and vendor lightweight liveness signals alike) — the "number of native * tool calls" the full-access audit log records per turn (#360). */ private startCount; /** Task-kind ids a synthesized `subagent started` was emitted for. */ private subagents; /** `fidelity` is the runtime's own `RunnerSpec.toolActivityFidelity` * (runner-registry.ts) — gates the best-effort vendor-lightweight * mapping below so a runner with no REAL per-tool signal (e.g. hermes's * single fixed "turn started" placeholder ping, fidelity `"none"`) * never produces a misleading tool_activity event. */ constructor(emit: (ev: Record) => void, fidelity?: 'full' | 'start-only' | 'none', synthesizeSubagents?: boolean); /** Wraps a canUseTool so a `deny` decision is also recorded by id, purely * as an observation — the returned decision is passed through unchanged. */ wrapCanUseTool(raw: CanUseTool): CanUseTool; /** m.type === 'tool_use' | 'tool_result' — the only two AgentMessage * types this tracker acts on. */ onMessage(m: AgentMessage): void; private onToolUse; private onToolResult; /** Rev 24: `subagent started` for a task-kind call. The optional fields * come only from input keys the runtime's task tool really has: * `subagent_type` (opencode), `description` (opencode, dsh), `prompt` * (opencode, dsh) or `task` (cline's spawn_agent/team_* delegation text). */ private subagentStarted; /** Rev 24: `subagent finished` for a synthesized start; `summary` is the * call's output cut to MAX_SUMMARY_CHARS, omitted when empty. */ private subagentFinished; /** Total `tool_activity` "start" events emitted so far this turn — the * full-access audit log's `toolCalls` field (#360, full-access-audit.ts). */ get toolCallCount(): number; /** Cancel/timeout: close every still-open id before `done` (§3.2). */ closeInFlight(): void; } export {}; //# sourceMappingURL=tool-activity.d.ts.map