import type { ExtensionAPI } from "@mariozechner/pi-coding-agent"; import { Type } from "@sinclair/typebox"; import { kylinFrameForCall, kylinFrameForResult } from "../../../vera-theme/src/public"; // Provides the `time` tool. Time was previously injected as a prefix on // every interactive user message; that was removed in favour of an // explicit tool so the model reads the clock only when it actually // needs to, and the user input stream stays untouched. function pad(n: number): string { return String(n).padStart(2, "0"); } function formatOffset(date: Date): string { const offsetMinutes = -date.getTimezoneOffset(); const sign = offsetMinutes >= 0 ? "+" : "-"; const abs = Math.abs(offsetMinutes); return `${sign}${pad(Math.floor(abs / 60))}:${pad(abs % 60)}`; } function resolveTimezone(): string { try { return Intl.DateTimeFormat().resolvedOptions().timeZone ?? "Unknown"; } catch { return "Unknown"; } } function formatNow(): string { const now = new Date(); const y = now.getFullYear(); const mo = pad(now.getMonth() + 1); const d = pad(now.getDate()); const h = pad(now.getHours()); const mi = pad(now.getMinutes()); const s = pad(now.getSeconds()); const offset = formatOffset(now); const tz = resolveTimezone(); return [ `${y}-${mo}-${d} ${h}:${mi}:${s}`, `ISO 8601: ${y}-${mo}-${d}T${h}:${mi}:${s}${offset}`, `Timezone: ${tz} (UTC${offset})`, ].join("\n"); } function renderCall(_args: any, theme: any, ctx: any): any { const state = ctx.state as { startedAt?: number }; if (state.startedAt === undefined) state.startedAt = Date.now(); return kylinFrameForCall(ctx, { name: "time", theme, status: "pending", paramSummary: theme.fg("dim", "now"), startedAt: state.startedAt, }); } function renderResult(result: any, opts: any, theme: any, ctx: any): any { const state = ctx.state as { startedAt?: number }; const startedAt = state.startedAt; const paramSummary = theme.fg("dim", "now"); const text = String(result?.content?.find?.((item: any) => item?.type === "text")?.text ?? ""); const lines = text.split("\n"); const headline = lines[0] ?? ""; return kylinFrameForResult(ctx, { name: "time", theme, status: "success", paramSummary, resultSummary: theme.fg("text", headline), expanded: opts.expanded, expandedBody: opts.expanded ? lines : undefined, expandedTotalLines: lines.length, startedAt, }); } export default function timeTool(pi: ExtensionAPI) { pi.registerTool({ name: "time", label: "Time", description: "Return the current local date and time. Output includes year-month-day, hour:minute:second, ISO 8601 with offset, and IANA timezone. Call when the current date or time is genuinely needed; do not assume or fabricate.", parameters: Type.Object({}), renderShell: "self", renderCall, renderResult, async execute(_toolCallId, _params, _signal, _onUpdate, _ctx) { return { content: [{ type: "text" as const, text: formatNow() }], }; }, }); }