import { basename } from "node:path"; export function numberValue(...values: unknown[]): number { for (const value of values) { if (typeof value === "number" && Number.isFinite(value)) return value; } return 0; } export function clamp(value: number, min: number, max: number): number { return Math.max(min, Math.min(max, value)); } export function formatElapsedSpaced(startedAt: number | undefined): string { if (!startedAt) return "0s"; const seconds = Math.max(0, Math.round((Date.now() - startedAt) / 1000)); if (seconds < 60) return `${seconds}s`; const minutes = Math.floor(seconds / 60); if (minutes < 60) return `${minutes}m`; const hours = Math.floor(minutes / 60); const minuteRest = minutes % 60; return minuteRest > 0 ? `${hours}h ${minuteRest}m` : `${hours}h`; } export function formatTokensPrecise(value: number): string { if (!Number.isFinite(value) || value <= 0) return "0"; if (value >= 1_000_000) return `${(value / 1_000_000).toFixed(1)}M`; if (value >= 1000) return `${Math.round(value / 1000)}k`; return `${Math.round(value)}`; } export function truncatePlain(value: string, maxLen: number): string { const normalized = value.replace(/\s+/g, " ").trim(); if (normalized.length <= maxLen) return normalized; if (normalized.includes("/") || normalized.includes("\\")) { return `…/${basename(normalized)}`.slice(0, maxLen); } return `${normalized.slice(0, Math.max(0, maxLen - 1))}…`; } export function extractToolTarget(toolName: string, args: unknown): string | undefined { if (!args || typeof args !== "object") return undefined; const input = args as Record; const candidates = ["path", "file", "url", "pattern", "query", "agent", "label", "description", "task"]; for (const key of candidates) { const value = input[key]; if (typeof value === "string" && value.trim()) return value.trim(); } if (toolName === "bash" && typeof input.command === "string") { return input.command.split("\n")[0]?.trim(); } return undefined; } export function isAgentTool(toolName: string): boolean { const normalized = toolName.toLowerCase(); return normalized === "subagent" || normalized === "task" || normalized.includes("agent"); }