From d8467a4f1616ae7dbfa0ee971b67743411969898 Mon Sep 17 00:00:00 2001 From: Levi Neely <141506390+lneely@users.noreply.github.com> Date: Fri, 15 May 2026 20:16:43 +0000 Subject: [PATCH] agent: add ToolCallCount to Core interface; wire tcct accumulator Adds a monotonically increasing tool-call counter (tcct) to the agent. - atomic.Int64 toolCallCount on the agent struct - ToolCallCount() int64 on the Core interface - IncrToolCallCount func() int64 on agentConfig, wired in executeTurn - Incremented in execOne after the pre-tool hook returns (blocked calls don't count) and before cfg.Exec is called (timeout/error still counts) The counter never resets. Consumers use modulo arithmetic: tcct % 10 == 0 -> fires every 10 actual executions The primary consumer is ollie-9p, which exposes tcct as a read-only 9P file that pre_tool / post_tool hooks can inspect via cat. --- pkg/agent/core.go | 11 +++++++++++ pkg/agent/types.go | 8 ++++++++ 2 files changed, 19 insertions(+) diff --git a/pkg/agent/core.go b/pkg/agent/core.go index 0431352..fb82793 100644 --- a/pkg/agent/core.go +++ b/pkg/agent/core.go @@ -217,6 +217,7 @@ type agent struct { cwd string startupMessages []string currentAction atomic.Pointer[actionHandle] + toolCallCount atomic.Int64 fifo PromptFIFO pendingInject atomic.Pointer[string] mu sync.RWMutex @@ -231,6 +232,13 @@ type agent struct { auditLog *olog.Logger } +// ToolCallCount returns the total number of tool calls executed in this +// session. The counter is monotonically increasing and never resets. +// Blocked calls (pre-tool hook exit 2) are not counted. +func (s *agent) ToolCallCount() int64 { + return s.toolCallCount.Load() +} + // SetEnv stores a session-scoped variable and propagates it to the execute server. func (s *agent) SetEnv(key, value string) { s.envMu.Lock() @@ -916,6 +924,9 @@ func (s *agent) executeTurn(ctx context.Context, input string, handler EventHand "result": result, }, s.log) } + s.cfg.IncrToolCallCount = func() int64 { + return s.toolCallCount.Add(1) + } s.cfg.SaveSession = func() { s.saveSession() } s.cfg.AutoCompact = func(ctx context.Context) { if ctx.Err() != nil || s.session == nil { diff --git a/pkg/agent/types.go b/pkg/agent/types.go index 8531306..250f744 100644 --- a/pkg/agent/types.go +++ b/pkg/agent/types.go @@ -121,6 +121,14 @@ type Core interface { // Supported fields: WatchState, WatchUsage, WatchCtxSz, WatchCWD. WaitChange(ctx context.Context, field, current string) (string, bool) + // ToolCallCount returns the total number of tool calls executed in this + // session since the agent was created. The counter is monotonically + // increasing and never resets. Blocked calls (pre-tool hook exit 2) are + // not counted. Timed-out or erroring calls are counted because execution + // was attempted. Use modulo arithmetic in hooks to fire every N calls: + // [ $(($(cat tcct) % 10)) -eq 0 ] && ... + ToolCallCount() int64 + // Close releases resources associated with the session, including its // temporary directory under /tmp/ollie/. Close()