From ecc477d0bebee75d4bdf7e5c27d77e353f441759 Mon Sep 17 00:00:00 2001 From: Chris Farhood Date: Fri, 24 Apr 2026 17:56:10 +0000 Subject: [PATCH] fix: stream raw stream-json to onLog so Paperclip UI renders structured transcript entries (FAR-32) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The prior approach (commit b607657) converted Claude's stream-json into flat plain text before calling onLog. This stripped the structure the Paperclip UI needs — its adapter ui-parser (src/ui-parser.ts, exported via the package's ./ui-parser entry) expects raw stream-json lines and emits structured transcript entries (assistant / thinking / tool_call / tool_result / init / result) that the UI renders as rich blocks, just like claude_local. claude_local passes stdout through unchanged to onLog for the same reason — the server persists raw lines and the UI parser turns them into rendered transcript entries. Mirror that here. formatClaudeStreamLine stays as an internal helper for future CLI use, but is no longer applied in the K8s streaming path. Co-Authored-By: Paperclip --- src/server/execute.ts | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/server/execute.ts b/src/server/execute.ts index 36cc0d1..fa3db62 100644 --- a/src/server/execute.ts +++ b/src/server/execute.ts @@ -19,7 +19,6 @@ import { import { getSelfPodInfo, getBatchApi, getCoreApi, getLogApi } from "./k8s-client.js"; import { buildJobManifest, sanitizeLabelValue } from "./job-manifest.js"; import { LogLineDedupFilter } from "./log-dedup.js"; -import { formatClaudeStreamLine } from "../cli/format-event.js"; import type * as k8s from "@kubernetes/client-node"; import { Writable } from "node:stream"; @@ -354,21 +353,17 @@ export async function streamPodLogsOnce( const writable = new Writable({ write(chunk: Buffer, _encoding, callback) { const text = chunk.toString("utf-8"); - // Always store raw text — parseClaudeStreamJson needs the original - // stream-json lines to extract session IDs, usage, and result events. chunks.push(text); const emitted = dedup ? dedup.filter(text) : text; if (!emitted) { callback(); return; } - // Format each stream-json event into human-readable text before the - // Paperclip server sees it, matching claude_local output style. - // Non-JSON lines (adapter status messages, plain errors) pass through. - const formatted = emitted.split("\n") - .map((line) => formatClaudeStreamLine(line) ?? "") - .join("\n"); - void onLog("stdout", formatted).then(() => callback(), callback); + // Forward raw stream-json lines unchanged. The Paperclip UI uses the + // adapter's ui-parser export (src/ui-parser.ts) to render structured + // transcript entries — pre-formatting here would strip that structure + // and produce flat plain text that looks nothing like claude_local. + void onLog("stdout", emitted).then(() => callback(), callback); }, });