Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/agent/src/agent-loop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
validateToolArguments,
} from "@earendil-works/pi-ai";
import { getDefaultStreamFn } from "./stream-fn.ts";
import { recordToolCall } from "./tool-call-stats.ts";
import type {
AgentContext,
AgentEvent,
Expand Down Expand Up @@ -269,6 +270,7 @@ async function runLoop(
? await failToolCallsFromTruncatedMessage(toolCalls, emit)
: await executeToolCalls(currentContext, message, config, signal, emit);
toolResults.push(...executedToolBatch.messages);
for (const result of executedToolBatch.messages) recordToolCall(result.toolName, result.isError);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Count calls made through runToolCall

When a tool invokes another tool through the exported runToolCall() pipeline, the nested call never reaches this block, so only the outer tool is counted. This occurs in packages/coding-agent/src/core/agent-session.ts, where nested tool calls delegate directly to runToolCall(); for example, a codemode call that invokes read twice records one codemode call and zero read calls or failures. Record outcomes in the common runToolCall/finalization path, while avoiding double-counting model-issued calls.

Useful? React with 👍 / 👎.

hasMoreToolCalls = !executedToolBatch.terminate;

for (const result of toolResults) {
Expand Down
1 change: 1 addition & 0 deletions packages/agent/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export * from "./agent.ts";
export * from "./agent-loop.ts";
export * from "./proxy.ts";
export { setDefaultStreamFn } from "./stream-fn.ts";
export * from "./tool-call-stats.ts";
export * from "./types.ts";
28 changes: 28 additions & 0 deletions packages/agent/src/tool-call-stats.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* Per-tool call counts for the current process, so a session can report which tools the
* agent leaned on and how many of their calls failed.
*/
export interface ToolCallStat {
calls: number;
errors: number;
}

const stats = new Map<string, ToolCallStat>();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Isolate counters per agent session

When multiple Agent instances run in the same Node process, this singleton map merges their calls even though agent state and sessionId are instance-specific. A session reading getToolCallStats() therefore reports tools and failures from unrelated sessions, and calling resetToolCallStats() for one session erases the other sessions' totals, including concurrent runs. Store the counters on the agent/run or key the read, record, and reset APIs by session.

Useful? React with 👍 / 👎.


export function recordToolCall(toolName: string, isError: boolean): void {
const stat = stats.get(toolName) ?? { calls: 0, errors: 0 };
stat.calls += 1;
if (isError) stat.errors += 1;
stats.set(toolName, stat);
}

/** A snapshot, most-called tool first. */
export function getToolCallStats(): Array<{ toolName: string } & ToolCallStat> {
return [...stats.entries()]
.map(([toolName, stat]) => ({ toolName, ...stat }))
.sort((a, b) => b.calls - a.calls);
}

export function resetToolCallStats(): void {
stats.clear();
}
Loading