Files
shockbot/utils/timer.ts
T
pullfrog[bot] fb80343ffd feat(agents): add thinking time logging between tool calls (#244)
* feat(agents): add thinking time logging between tool calls

Adds a ThinkingTimer utility that tracks the gap between tool results and
the next tool call. When the gap exceeds 3 seconds, it logs the duration
with a stopwatch emoji (⏱️ 4.2s).

Uses performance.now() for high-resolution timing and Intl.NumberFormat
for rendering duration in seconds with optional fraction digits.

Integrated across all 5 agents: Claude, Codex, Cursor, Gemini, OpenCode.

Closes #127

* fix: adjusting tests for mocking performance.now.

* fix: reducing diff for claude.

* fix: rm unused args for claude.

* rm unused args for codex.

* fix: rm unused args for gemini.

* fix: rm unused args for opencode.

* mv THINKING_THRESHOLD.

* rev: I decided to pospone node:perf_hooks integration since it requires more comprehensive refactoring.

* fix: using Intl unit formatting.

* tests for ThinkingTimer.

* fix: narrow unit.

* fix: making durationFormatter a class instance property since using one agent per run.

* fix: inverting condition in markToolCall.

* thinking timer improvements and fix actions/checkout v6 auth

- thinking timer: use » chevron and "thought for X seconds" format
- thinking timer: add debug timestamps for sanity checking
- demote PID namespace isolation logs to debug
- remove redundant "setting up git authentication" log
- fix duplicate Authorization header with actions/checkout v6: clean up
  includeIf credential entries that v6 persists via external config files

Co-authored-by: Cursor <cursoragent@cursor.com>

* standardize tool call log prefix to » double chevron

Co-authored-by: Cursor <cursoragent@cursor.com>

* update timer tests for new thinking log format

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
Co-authored-by: Robin Tail <robin_tail@me.com>
Co-authored-by: Colin McDonnell <colinmcd94@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-10 06:17:43 +00:00

50 lines
1.4 KiB
TypeScript

import { log } from "./cli.ts";
export class Timer {
private initialTimestamp: number;
private lastCheckpointTimestamp: number | null = null;
constructor() {
this.initialTimestamp = Date.now();
}
checkpoint(name: string): void {
const now = Date.now();
const duration = this.lastCheckpointTimestamp
? now - this.lastCheckpointTimestamp
: now - this.initialTimestamp;
log.debug(`» ${name}: ${duration}ms`);
this.lastCheckpointTimestamp = now;
}
}
const THINKING_THRESHOLD = 3000; // ms
export class ThinkingTimer {
private readonly durationFormatter = new Intl.NumberFormat("en-US", {
style: "unit",
unit: "second",
unitDisplay: "long",
minimumFractionDigits: 0,
maximumFractionDigits: 1,
});
private lastToolResultTimestamp: number | null = null;
markToolResult(): void {
this.lastToolResultTimestamp = Date.now();
log.debug(`» thinking timer: markToolResult at ${this.lastToolResultTimestamp}`);
}
markToolCall(): void {
const now = Date.now();
log.debug(`» thinking timer: markToolCall at ${now}, lastToolResult=${this.lastToolResultTimestamp}`);
if (this.lastToolResultTimestamp === null) return;
const elapsed = now - this.lastToolResultTimestamp;
if (elapsed < THINKING_THRESHOLD) return;
const seconds = elapsed / 1000;
log.info(`» thought for ${this.durationFormatter.format(seconds)}`);
}
}