134 lines
3.1 KiB
TypeScript
134 lines
3.1 KiB
TypeScript
/**
|
|
* Wire protocol types and message serialization for remote-control.
|
|
*
|
|
* Converts pi session entries into the simplified RenderMsg format
|
|
* consumed by the browser client.
|
|
*/
|
|
|
|
import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
|
|
|
|
interface RawContent {
|
|
type: string;
|
|
text?: string;
|
|
id?: string;
|
|
name?: string;
|
|
arguments?: unknown;
|
|
}
|
|
|
|
export interface RawMessage {
|
|
role: string;
|
|
content: string | RawContent[];
|
|
model?: string;
|
|
toolName?: string;
|
|
toolCallId?: string;
|
|
isError?: boolean;
|
|
}
|
|
|
|
export interface RenderMsg {
|
|
id: string; // SessionEntry id, or "pending" while streaming
|
|
role: "user" | "assistant" | "tool_result";
|
|
text: string;
|
|
toolCalls?: Array<{ id: string; name: string; args: string }>;
|
|
toolName?: string;
|
|
toolCallId?: string;
|
|
isError?: boolean;
|
|
model?: string;
|
|
}
|
|
|
|
export function serializeMessage(
|
|
id: string,
|
|
msg: RawMessage,
|
|
): RenderMsg | null {
|
|
if (msg.role === "user") {
|
|
const text =
|
|
typeof msg.content === "string"
|
|
? msg.content
|
|
: (msg.content as RawContent[])
|
|
.filter((c) => c.type === "text")
|
|
.map((c) => c.text)
|
|
.join("");
|
|
return { id, role: "user", text };
|
|
}
|
|
|
|
if (msg.role === "assistant") {
|
|
const text = (msg.content as RawContent[])
|
|
.filter((c) => c.type === "text")
|
|
.map((c) => c.text)
|
|
.join("");
|
|
const toolCalls = (msg.content as RawContent[])
|
|
.filter((c) => c.type === "toolCall")
|
|
.map((c) => ({
|
|
id: c.id,
|
|
name: c.name,
|
|
args: JSON.stringify(c.arguments, null, 2),
|
|
}));
|
|
return {
|
|
id,
|
|
role: "assistant",
|
|
text,
|
|
toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
|
|
model: msg.model,
|
|
};
|
|
}
|
|
|
|
if (msg.role === "toolResult") {
|
|
const text = (msg.content as RawContent[])
|
|
.filter((c) => c.type === "text")
|
|
.map((c) => c.text)
|
|
.join("");
|
|
return {
|
|
id,
|
|
role: "tool_result",
|
|
text,
|
|
toolName: msg.toolName,
|
|
toolCallId: msg.toolCallId,
|
|
isError: msg.isError,
|
|
};
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
export function getBranchMessages(ctx: ExtensionContext): RenderMsg[] {
|
|
const branch = ctx.sessionManager.getBranch();
|
|
const out: RenderMsg[] = [];
|
|
for (const entry of branch) {
|
|
if (entry.type !== "message") continue;
|
|
const m = serializeMessage(
|
|
entry.id,
|
|
(entry as { id: string; type: string; message: RawMessage }).message,
|
|
);
|
|
if (m) out.push(m);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
function abbreviateHome(p: string): string {
|
|
const home = process.env.HOME;
|
|
if (home && p === home) return "~";
|
|
if (home && p.startsWith(`${home}/`)) return `~${p.slice(home.length)}`;
|
|
return p;
|
|
}
|
|
|
|
export function buildSyncMessage(ctx: ExtensionContext): {
|
|
type: "sync";
|
|
messages: RenderMsg[];
|
|
state: {
|
|
isStreaming: boolean;
|
|
model: string | undefined;
|
|
cwd: string;
|
|
sessionName: string | undefined;
|
|
};
|
|
} {
|
|
return {
|
|
type: "sync",
|
|
messages: getBranchMessages(ctx),
|
|
state: {
|
|
isStreaming: !ctx.isIdle(),
|
|
model: ctx.model?.id,
|
|
cwd: abbreviateHome(ctx.cwd),
|
|
sessionName: ctx.sessionManager.getSessionName(),
|
|
},
|
|
};
|
|
}
|