diff --git a/CHANGELOG.md b/CHANGELOG.md index c31abc7e8..58b75347f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,9 @@ - Logging now rolls daily to `/tmp/warelay/warelay-YYYY-MM-DD.log` (or custom dir) and prunes files older than 24h to reduce data retention. - Media server now rejects symlinked files and ensures resolved paths stay inside the media directory, closing traversal via symlinks; added regression test. (Thanks @joaohlisboa) +### Performance +- Web auto-replies using the Pi agent now keep a single long-lived `tau` process in RPC mode instead of spawning per message, eliminating cold-start latency while preserving session/cwd handling. + ## 1.3.0 — 2025-12-02 ### Highlights diff --git a/src/agents/pi.ts b/src/agents/pi.ts index c7359b98e..948afd837 100644 --- a/src/agents/pi.ts +++ b/src/agents/pi.ts @@ -47,7 +47,11 @@ function parsePiJson(raw: string): AgentParseResult { export const piSpec: AgentSpec = { kind: "pi", - isInvocation: (argv) => argv.length > 0 && path.basename(argv[0]) === "pi", + isInvocation: (argv) => { + if (argv.length === 0) return false; + const base = path.basename(argv[0]).replace(/\.(m?js)$/i, ""); + return base === "pi" || base === "tau"; + }, buildArgs: (ctx) => { const argv = [...ctx.argv]; // Non-interactive print + JSON diff --git a/src/auto-reply/command-reply.ts b/src/auto-reply/command-reply.ts index f234a6236..2571de0ee 100644 --- a/src/auto-reply/command-reply.ts +++ b/src/auto-reply/command-reply.ts @@ -6,6 +6,7 @@ import type { AgentMeta } from "../agents/types.js"; import type { WarelayConfig } from "../config/config.js"; import { isVerbose, logVerbose } from "../globals.js"; import { logError } from "../logger.js"; +import { getChildLogger } from "../logging.js"; import { splitMediaFromOutput } from "../media/parse.js"; import { enqueueCommand } from "../process/command-queue.js"; import type { runCommandWithTimeout } from "../process/exec.js"; @@ -100,6 +101,12 @@ export function summarizeClaudeMetadata(payload: unknown): string | undefined { export async function runCommandReply( params: CommandReplyParams, ): Promise { + const logger = getChildLogger({ module: "command-reply" }); + const verboseLog = (msg: string) => { + logger.debug(msg); + if (isVerbose()) logVerbose(msg); + }; + const { reply, templatingCtx, @@ -182,7 +189,7 @@ export async function runCommandReply( systemSent, identityPrefix: agentCfg.identityPrefix, format: agentCfg.format, - }) + }) : argv; logVerbose( @@ -249,17 +256,19 @@ export async function runCommandReply( trimmed = cleanedText; if (mediaFound?.length) { mediaFromCommand = mediaFound; - if (isVerbose()) logVerbose(`MEDIA token extracted: ${mediaFound}`); - } else if (isVerbose()) { - logVerbose("No MEDIA token extracted from final text"); + verboseLog(`MEDIA token extracted: ${mediaFound}`); + } else { + verboseLog("No MEDIA token extracted from final text"); } if (!trimmed && !mediaFromCommand) { const meta = parsed?.meta?.extra?.summary ?? undefined; trimmed = `(command produced no output${meta ? `; ${meta}` : ""})`; - logVerbose("No text/media produced; injecting fallback notice to user"); + verboseLog("No text/media produced; injecting fallback notice to user"); } - logVerbose(`Command auto-reply stdout (trimmed): ${trimmed || ""}`); - logVerbose(`Command auto-reply finished in ${Date.now() - started}ms`); + verboseLog(`Command auto-reply stdout (trimmed): ${trimmed || ""}`); + const elapsed = Date.now() - started; + verboseLog(`Command auto-reply finished in ${elapsed}ms`); + logger.info({ durationMs: elapsed, agent: agentKind, cwd: reply.cwd }, "command auto-reply finished"); if ((code ?? 0) !== 0) { console.error( `Command auto-reply exited with code ${code ?? "unknown"} (signal: ${signal ?? "none"})`, @@ -346,17 +355,16 @@ export async function runCommandReply( killed, agentMeta: parsed?.meta, }; - if (isVerbose()) { - logVerbose(`Command auto-reply meta: ${JSON.stringify(meta)}`); - } + verboseLog(`Command auto-reply meta: ${JSON.stringify(meta)}`); return { payload, meta }; } catch (err) { const elapsed = Date.now() - started; + logger.info({ durationMs: elapsed, agent: agentKind, cwd: reply.cwd }, "command auto-reply failed"); const anyErr = err as { killed?: boolean; signal?: string }; const timeoutHit = anyErr.killed === true || anyErr.signal === "SIGKILL"; const errorObj = err as { stdout?: string; stderr?: string }; if (errorObj.stderr?.trim()) { - logVerbose(`Command auto-reply stderr: ${errorObj.stderr.trim()}`); + verboseLog(`Command auto-reply stderr: ${errorObj.stderr.trim()}`); } if (timeoutHit) { console.error(