feat(gateway): enrich agent WS logs

This commit is contained in:
Peter Steinberger
2025-12-20 14:54:38 +00:00
parent f508fd3fa2
commit d95c09d94a
2 changed files with 86 additions and 11 deletions

View File

@@ -418,6 +418,73 @@ function formatForLog(value: unknown): string {
}
}
function compactPreview(input: string, maxLen = 160): string {
const oneLine = input.replace(/\s+/g, " ").trim();
if (oneLine.length <= maxLen) return oneLine;
return `${oneLine.slice(0, Math.max(0, maxLen - 1))}`;
}
function summarizeAgentEventForWsLog(
payload: unknown,
): Record<string, unknown> {
if (!payload || typeof payload !== "object") return {};
const rec = payload as Record<string, unknown>;
const runId = typeof rec.runId === "string" ? rec.runId : undefined;
const stream = typeof rec.stream === "string" ? rec.stream : undefined;
const seq = typeof rec.seq === "number" ? rec.seq : undefined;
const data =
rec.data && typeof rec.data === "object"
? (rec.data as Record<string, unknown>)
: undefined;
const extra: Record<string, unknown> = {};
if (runId) extra.run = shortId(runId);
if (stream) extra.stream = stream;
if (seq !== undefined) extra.aseq = seq;
if (!data) return extra;
if (stream === "assistant") {
const text = typeof data.text === "string" ? data.text : undefined;
if (text?.trim()) extra.text = compactPreview(text);
const mediaUrls = Array.isArray(data.mediaUrls)
? data.mediaUrls
: undefined;
if (mediaUrls && mediaUrls.length > 0) extra.media = mediaUrls.length;
return extra;
}
if (stream === "tool") {
const phase = typeof data.phase === "string" ? data.phase : undefined;
const name = typeof data.name === "string" ? data.name : undefined;
if (phase || name) extra.tool = `${phase ?? "?"}:${name ?? "?"}`;
const toolCallId =
typeof data.toolCallId === "string" ? data.toolCallId : undefined;
if (toolCallId) extra.call = shortId(toolCallId);
const meta = typeof data.meta === "string" ? data.meta : undefined;
if (meta?.trim()) extra.meta = meta;
if (typeof data.isError === "boolean") extra.err = data.isError;
return extra;
}
if (stream === "job") {
const state = typeof data.state === "string" ? data.state : undefined;
if (state) extra.state = state;
if (data.to === null) extra.to = null;
else if (typeof data.to === "string") extra.to = data.to;
if (typeof data.durationMs === "number")
extra.ms = Math.round(data.durationMs);
if (typeof data.aborted === "boolean") extra.aborted = data.aborted;
const error = typeof data.error === "string" ? data.error : undefined;
if (error?.trim()) extra.error = compactPreview(error, 120);
return extra;
}
const reason = typeof data.reason === "string" ? data.reason : undefined;
if (reason?.trim()) extra.reason = reason;
return extra;
}
function normalizeVoiceWakeTriggers(input: unknown): string[] {
const raw = Array.isArray(input) ? input : [];
const cleaned = raw
@@ -1117,14 +1184,18 @@ export async function startGatewayServer(
seq: eventSeq,
stateVersion: opts?.stateVersion,
});
logWs("out", "event", {
const logMeta: Record<string, unknown> = {
event,
seq: eventSeq,
clients: clients.size,
dropIfSlow: opts?.dropIfSlow,
presenceVersion: opts?.stateVersion?.presence,
healthVersion: opts?.stateVersion?.health,
});
};
if (event === "agent") {
Object.assign(logMeta, summarizeAgentEventForWsLog(payload));
}
logWs("out", "event", logMeta);
for (const c of clients) {
const slow = c.socket.bufferedAmount > MAX_BUFFERED_BYTES;
if (slow && opts?.dropIfSlow) continue;
@@ -1802,8 +1873,7 @@ export async function startGatewayServer(
typeof obj.sessionKey === "string" ? obj.sessionKey.trim() : "";
const mainKey =
(loadConfig().inbound?.session?.mainKey ?? "main").trim() || "main";
const sessionKey =
sessionKeyRaw.length > 0 ? sessionKeyRaw : mainKey;
const sessionKey = sessionKeyRaw.length > 0 ? sessionKeyRaw : mainKey;
const { storePath, store, entry } = loadSessionEntry(sessionKey);
const now = Date.now();
const sessionId = entry?.sessionId ?? randomUUID();
@@ -2061,6 +2131,8 @@ export async function startGatewayServer(
);
}
const tailnetDns = await resolveTailnetDnsHint();
try {
const sshPortEnv = process.env.CLAWDIS_SSH_PORT?.trim();
const sshPortParsed = sshPortEnv ? Number.parseInt(sshPortEnv, 10) : NaN;
@@ -2069,8 +2141,6 @@ export async function startGatewayServer(
? sshPortParsed
: undefined;
const tailnetDns = await resolveTailnetDnsHint();
const bonjour = await startGatewayBonjourAdvertiser({
instanceName: formatBonjourInstanceName(machineDisplayName),
gatewayPort: port,

View File

@@ -1738,11 +1738,16 @@ describe("web auto-reply", () => {
const resolver = vi
.fn()
.mockImplementation(async (_ctx, opts?: { onToolResult?: Function }) => {
await opts?.onToolResult?.({ text: "[🛠️ tool1]" });
await opts?.onToolResult?.({ text: "[🛠️ tool2]" });
return { text: "final" };
});
.mockImplementation(
async (
_ctx,
opts?: { onToolResult?: (r: { text: string }) => Promise<void> },
) => {
await opts?.onToolResult?.({ text: "[🛠️ tool1]" });
await opts?.onToolResult?.({ text: "[🛠️ tool2]" });
return { text: "final" };
},
);
await monitorWebProvider(false, listenerFactory, false, resolver);
expect(capturedOnMessage).toBeDefined();