Files
sw-ads-agent/packages/harness/src/pi-event-adapter.ts
T

52 lines
3.7 KiB
TypeScript

import type { AgentSessionEvent } from "@earendil-works/pi-coding-agent";
import type { EventInput, HarnessEventType } from "@agent-studio/shared";
import { reportArtifactSchema } from "@agent-studio/shared";
import { redactValue } from "./errors.js";
export interface PiAdapterContext { runId: string; assistantMessageId: string }
export type MappedEvent = { [T in HarnessEventType]: EventInput<T> }[HarnessEventType];
export function mapPiEvent(event: AgentSessionEvent, context: PiAdapterContext): MappedEvent[] {
switch (event.type) {
case "message_update":
if (event.assistantMessageEvent.type === "text_delta") return [{ type: "assistant.delta", runId: context.runId, payload: { messageId: context.assistantMessageId, delta: event.assistantMessageEvent.delta } }];
return [];
case "message_end":
if (event.message.role === "assistant") return [{ type: "assistant.completed", runId: context.runId, payload: { messageId: context.assistantMessageId } }];
return [];
case "tool_execution_start":
return [{ type: "tool.requested", runId: context.runId, payload: { toolCallId: event.toolCallId, toolName: event.toolName, arguments: event.toolName === "render_swads_daily_report" ? { status: "requested" } : redactValue(event.args) } }];
case "tool_execution_end": {
if (!event.isError && event.toolName === "render_swads_daily_report") {
const details = (event.result as { details?: unknown })?.details;
const wrapped = details && typeof details === "object" && !Array.isArray(details) ? details as Record<string, unknown> : {};
const artifact = reportArtifactSchema.safeParse(wrapped.artifact ?? details);
const markdown = typeof wrapped.markdown === "string" ? wrapped.markdown.slice(0, 6_000) : undefined;
if (artifact.success) return [
{ type: "tool.completed", runId: context.runId, payload: { toolCallId: event.toolCallId, toolName: event.toolName, result: artifact.data } },
{ type: "report.generated", runId: context.runId, payload: { assistantMessageId: context.assistantMessageId, artifact: artifact.data, ...(markdown ? { markdown } : {}) } },
];
return [{ type: "tool.failed", runId: context.runId, payload: { toolCallId: event.toolCallId, toolName: event.toolName, error: "SWADS_INVALID_REPORT" } }];
}
return event.isError
? [{ type: "tool.failed", runId: context.runId, payload: { toolCallId: event.toolCallId, toolName: event.toolName, error: toolFailure(event.result) } }]
: [{ type: "tool.completed", runId: context.runId, payload: { toolCallId: event.toolCallId, toolName: event.toolName, result: summarizeToolResult(event.toolName, event.result) } }];
}
default:
return [];
}
}
function summarizeToolResult(toolName: string, result: unknown): unknown {
if (["read", "grep", "find", "ls"].includes(toolName)) return { status: "success" };
if (toolName === "swads_cli") return redactValue((result as { details?: unknown })?.details ?? { status: "success" });
return redactValue(result);
}
function toolFailure(result: unknown): string {
const content = (result as { content?: Array<{ type?: string; text?: string }> } | null)?.content;
const text = content?.filter((item) => item.type === "text").map((item) => item.text ?? "").join(" ") ?? "";
const codes = ["NO_DATA", "NOT_CONFIGURED", "INVALID_CONFIG", "INVALID_ARGUMENTS", "AUTH_FAILED", "FORBIDDEN", "TOOL_MISSING", "TOOL_DRIFT", "NOT_READ_ONLY", "CAPABILITY_MISSING", "INVALID_RESPONSE", "UPSTREAM_ERROR", "RESPONSE_TOO_LARGE", "TIMEOUT", "ABORTED", "UNAVAILABLE", "PREFLIGHT_REQUIRED", "INVALID_REPORT", "REPORT_FAILED"];
return codes.map((code) => `SWADS_${code}`).find((code) => text === code || text === `Error: ${code}`) ?? "Tool execution failed";
}