diff --git a/tools/hwlab-cli/client.test.ts b/tools/hwlab-cli/client.test.ts index 03c8274c..dd668c0c 100644 --- a/tools/hwlab-cli/client.test.ts +++ b/tools/hwlab-cli/client.test.ts @@ -1400,6 +1400,33 @@ test("hwlab-cli client agent trace can render with the Web trace row path", asyn assert.ok(result.payload.body.renderedRowCount >= 1); assert.ok(result.payload.body.rows.some((row: any) => /助手最后一条消息/u.test(row.header))); assert.equal(JSON.stringify(result.payload.body.rows).includes("session:reused"), false); + assert.equal(result.payload.traceStatus, "completed"); + assert.equal(result.payload.rendered.traceStatus, "completed"); + assert.equal(result.payload.rendered.render, "web"); + assert.equal(result.payload.rendered.renderedRowCount, result.payload.body.renderedRowCount); + assert.deepEqual(result.payload.rendered.rows, result.payload.body.rows); + assert.deepEqual(result.payload.data, result.payload.rendered); +}); + +test("hwlab-cli client agent trace exposes missing Web render state without a body-path trap", async () => { + const result = await runHwlabCli(["client", "agent", "trace", "trc_missing", "--base-url", "http://web.test", "--cookie", "hwlab_session=session-a", "--render", "web"], { + fetchImpl: async () => new Response(JSON.stringify({ + status: "missing", + traceId: "trc_missing", + eventCount: 0, + events: [] + }), { status: 200 }) + }); + + assert.equal(result.exitCode, 0); + assert.equal(result.payload.status, "succeeded"); + assert.equal(result.payload.traceStatus, "missing"); + assert.equal(result.payload.body.status, "missing"); + assert.equal(result.payload.rendered.traceStatus, "missing"); + assert.equal(result.payload.rendered.sourceEventCount, 0); + assert.equal(result.payload.rendered.renderedRowCount, 0); + assert.deepEqual(result.payload.rendered.rows, []); + assert.deepEqual(result.payload.data, result.payload.rendered); }); test("hwlab-cli Web trace render does not treat AgentRun result-ready as final assistant text", async () => { diff --git a/tools/src/hwlab-cli-lib.ts b/tools/src/hwlab-cli-lib.ts index c6e94763..e3e51a78 100644 --- a/tools/src/hwlab-cli-lib.ts +++ b/tools/src/hwlab-cli-lib.ts @@ -630,7 +630,7 @@ async function agentCommand(context: any) { const pathName = `/v1/agent/chat/trace/${encodeURIComponent(traceId)}`; const response = await requestJson({ ...context, method: "GET", path: pathName }); const traceBody = traceBodyForCli(response.body, context.parsed); - return responsePayload("client.agent.trace", response, context, { route: route("GET", pathName), traceId, body: traceBody }); + return responsePayload("client.agent.trace", response, context, { route: route("GET", pathName), traceId, body: traceBody, ...traceResponseAliases(traceBody, context.parsed) }); } if (subcommand === "inspect") { const pathName = agentInspectPath(context); @@ -2435,6 +2435,30 @@ function webTraceRenderBody(traceObject: any, parsed: ParsedArgs) { }); } +function traceResponseAliases(traceBody: any, parsed: ParsedArgs) { + if (text(parsed.render) !== "web" || !traceBody || typeof traceBody !== "object") return {}; + const rendered = pruneUndefined({ + traceId: traceBody.traceId ?? null, + traceStatus: traceBody.status ?? null, + render: traceBody.render, + renderer: traceBody.renderer, + sourceEventCount: traceBody.sourceEventCount, + renderedRowCount: traceBody.renderedRowCount, + returnedRowCount: traceBody.returnedRowCount, + noiseEventCount: traceBody.noiseEventCount, + omittedNoiseCount: traceBody.omittedNoiseCount, + rowLimit: traceBody.rowLimit, + rows: traceBody.rows, + eventCountMismatch: traceBody.eventCountMismatch, + assistantText: traceBody.assistantText + }); + return { + traceStatus: traceBody.status ?? null, + rendered, + data: rendered + }; +} + function compactTraceRenderRow(row: any) { return pruneUndefined({ rowId: row?.rowId ?? null,