Project Runner PRP events into task chat

This commit is contained in:
Dotta committed 2026-08-21 08:36:00 -05:00
1 parent 00b9012a28
commit d2c709ed19
6 files changed
+83 -5

No files matched your search

@@ -0,0 +1,23 @@
import { describe, expect, it } from "vitest";
import { paperclipRunnerUIAdapter } from "./index";
describe("paperclip runner transcript projection", () => {
it("renders committed PRP semantic tool items with the existing chat parts", () => {
const started = paperclipRunnerUIAdapter.parseStdoutLine(JSON.stringify({
type: "paperclip.prp.event",
event: {
eventType: "item.started",
payload: { item: { type: "tool_use", id: "call-1", name: "get_task_context", input: {} } },
},
}), "2026-08-21T12:00:00.000Z");
const completed = paperclipRunnerUIAdapter.parseStdoutLine(JSON.stringify({
type: "paperclip.prp.event",
event: {
eventType: "item.completed",
payload: { item: { type: "tool_result", id: "call-1", tool_use_id: "call-1", result: { ok: true } } },
},
}), "2026-08-21T12:00:01.000Z");
expect(started).toEqual([expect.objectContaining({ kind: "tool_call", name: "get_task_context", toolUseId: "call-1" })]);
expect(completed).toEqual([expect.objectContaining({ kind: "tool_result", toolUseId: "call-1", isError: false })]);
});
});
+31 -1
View File
@@ -2,10 +2,40 @@ import type { UIAdapterModule } from "../types";
import { parseCodexStdoutLine, buildPaperclipRunnerConfig } from "@paperclipai/adapter-codex-local/ui";
import { CodexLocalConfigFields } from "../codex-local/config-fields";
function parsePaperclipRunnerStdoutLine(line: string, ts: string) {
let parsed: unknown;
try {
parsed = JSON.parse(line);
} catch {
return parseCodexStdoutLine(line, ts);
}
if (typeof parsed !== "object" || parsed === null || (parsed as { type?: unknown }).type !== "paperclip.prp.event") {
return parseCodexStdoutLine(line, ts);
}
const event = (parsed as { event?: unknown }).event;
if (typeof event !== "object" || event === null) return [];
const candidate = event as { eventType?: unknown; payload?: unknown };
const eventType = typeof candidate.eventType === "string" ? candidate.eventType : "";
const payload = typeof candidate.payload === "object" && candidate.payload !== null
? candidate.payload as Record<string, unknown>
: {};
if ((eventType === "item.started" || eventType === "item.completed") && payload.item) {
return parseCodexStdoutLine(JSON.stringify({ type: eventType, item: payload.item }), ts);
}
if (eventType === "turn.started") return parseCodexStdoutLine(JSON.stringify({ type: "turn.started" }), ts);
if (eventType === "turn.completed") {
return parseCodexStdoutLine(JSON.stringify({ type: "turn.completed", result: payload.result ?? "" }), ts);
}
if (eventType === "harness.diagnostic") {
return [{ kind: "system" as const, ts, text: String(payload.message ?? payload.code ?? "Runner diagnostic") }];
}
return [];
}
export const paperclipRunnerUIAdapter: UIAdapterModule = {
type: "paperclip_runner",
label: "Paperclip Runner",
parseStdoutLine: parseCodexStdoutLine,
parseStdoutLine: parsePaperclipRunnerStdoutLine,
ConfigFields: CodexLocalConfigFields,
buildAdapterConfig: buildPaperclipRunnerConfig,
};