fb80343ffd
* 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>
50 lines
1.4 KiB
TypeScript
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)}`);
|
|
}
|
|
}
|