perf(pi): reuse tau rpc for command auto-replies

This commit is contained in:
Peter Steinberger
2025-12-02 20:09:51 +00:00
parent a34271adf9
commit b172b538fc
17 changed files with 695 additions and 93 deletions

View File

@@ -6,9 +6,11 @@ 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";
import { runPiRpc } from "../process/tau-rpc.js";
import { applyTemplate, type TemplateContext } from "./templating.js";
import type { ReplyPayload } from "./types.js";
@@ -99,6 +101,12 @@ export function summarizeClaudeMetadata(payload: unknown): string | undefined {
export async function runCommandReply(
params: CommandReplyParams,
): Promise<CommandReplyResult> {
const logger = getChildLogger({ module: "command-reply" });
const verboseLog = (msg: string) => {
logger.debug(msg);
if (isVerbose()) logVerbose(msg);
};
const {
reply,
templatingCtx,
@@ -133,14 +141,25 @@ export async function runCommandReply(
// Session args prepared (templated) and injected generically
if (reply.session) {
const defaultNew =
agentCfg.kind === "claude"
? ["--session-id", "{{SessionId}}"]
: ["--session", "{{SessionId}}"];
const defaultResume =
agentCfg.kind === "claude"
? ["--resume", "{{SessionId}}"]
: ["--session", "{{SessionId}}"];
const defaultSessionArgs = (() => {
switch (agentCfg.kind) {
case "claude":
return {
newArgs: ["--session-id", "{{SessionId}}"],
resumeArgs: ["--resume", "{{SessionId}}"],
};
case "gemini":
// Gemini CLI supports --resume <id>; starting a new session needs no flag.
return { newArgs: [], resumeArgs: ["--resume", "{{SessionId}}"] };
default:
return {
newArgs: ["--session", "{{SessionId}}"],
resumeArgs: ["--session", "{{SessionId}}"],
};
}
})();
const defaultNew = defaultSessionArgs.newArgs;
const defaultResume = defaultSessionArgs.resumeArgs;
const sessionArgList = (
isNewSession
? (reply.session.sessionArgNew ?? defaultNew)
@@ -170,7 +189,7 @@ export async function runCommandReply(
systemSent,
identityPrefix: agentCfg.identityPrefix,
format: agentCfg.format,
})
})
: argv;
logVerbose(
@@ -181,20 +200,41 @@ export async function runCommandReply(
let queuedMs: number | undefined;
let queuedAhead: number | undefined;
try {
const { stdout, stderr, code, signal, killed } = await enqueue(
() => commandRunner(finalArgv, { timeoutMs, cwd: reply.cwd }),
{
onWait: (waitMs, ahead) => {
queuedMs = waitMs;
queuedAhead = ahead;
if (isVerbose()) {
logVerbose(
`Command auto-reply queued for ${waitMs}ms (${queuedAhead} ahead)`,
);
const run = async () => {
// Prefer long-lived tau RPC for pi agent to avoid cold starts.
if (agentKind === "pi") {
const body = finalArgv[bodyIndex] ?? "";
// Build rpc args without the prompt body; force --mode rpc.
const rpcArgv = (() => {
const copy = [...finalArgv];
copy.splice(bodyIndex, 1);
const modeIdx = copy.findIndex((a) => a === "--mode");
if (modeIdx >= 0 && copy[modeIdx + 1]) {
copy.splice(modeIdx, 2, "--mode", "rpc");
} else if (!copy.includes("--mode")) {
copy.splice(copy.length - 1, 0, "--mode", "rpc");
}
},
return copy;
})();
return await runPiRpc({
argv: rpcArgv,
cwd: reply.cwd,
prompt: body,
timeoutMs,
});
}
return await commandRunner(finalArgv, { timeoutMs, cwd: reply.cwd });
};
const { stdout, stderr, code, signal, killed } = await enqueue(run, {
onWait: (waitMs, ahead) => {
queuedMs = waitMs;
queuedAhead = ahead;
if (isVerbose()) {
logVerbose(`Command auto-reply queued for ${waitMs}ms (${queuedAhead} ahead)`);
}
},
);
});
const rawStdout = stdout.trim();
let mediaFromCommand: string[] | undefined;
let trimmed = rawStdout;
@@ -214,17 +254,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 || "<empty>"}`);
logVerbose(`Command auto-reply finished in ${Date.now() - started}ms`);
verboseLog(`Command auto-reply stdout (trimmed): ${trimmed || "<empty>"}`);
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"})`,
@@ -311,17 +353,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(