import { type RuntimeEventRawSource, RuntimeItemId, type CanonicalRequestType, type EventId, type ProviderApprovalDecision, type ProviderApprovalOption, type ProviderDriverKind, type ProviderRuntimeEvent, type RuntimeRequestId, type ThreadId, type TurnId, } from "@t3tools/contracts"; import { type AcpPermissionRequest, type AcpPlanUpdate, type AcpToolCallState, canonicalItemTypeFromAcpToolKind, } from "./AcpRuntimeModel.ts"; type AcpAdapterRawSource = Extract< RuntimeEventRawSource, "acp.jsonrpc" | `acp.${string}.extension` >; interface AcpEventStamp { readonly eventId: EventId; readonly createdAt: string; } type AcpCanonicalRequestType = Extract< CanonicalRequestType, "exec_command_approval" | "file_read_approval" | "file_change_approval" | "dynamic_tool_call" >; function canonicalRequestTypeFromAcpKind(kind: string | "unknown"): AcpCanonicalRequestType { switch (kind) { case "execute": return "exec_command_approval"; case "read": return "file_read_approval"; case "edit": case "delete": case "move": return "file_change_approval"; default: return "dynamic_tool_call"; } } function runtimeItemStatusFromAcpToolStatus( status: AcpToolCallState["status"], ): "inProgress" | "completed" | "failed" | undefined { switch (status) { case "pending": case "inProgress": return "inProgress"; case "completed": return "completed"; case "failed": return "failed"; default: return undefined; } } export function makeAcpRequestOpenedEvent(input: { readonly stamp: AcpEventStamp; readonly provider: ProviderDriverKind; readonly threadId: ThreadId; readonly turnId: TurnId | undefined; readonly requestId: RuntimeRequestId; readonly permissionRequest: AcpPermissionRequest; readonly approvalOptions?: ReadonlyArray; readonly detail: string; readonly args: unknown; readonly source: AcpAdapterRawSource; readonly method: string; readonly rawPayload: unknown; }): ProviderRuntimeEvent { return { type: "request.opened", ...input.stamp, provider: input.provider, threadId: input.threadId, turnId: input.turnId, requestId: input.requestId, payload: { requestType: canonicalRequestTypeFromAcpKind(input.permissionRequest.kind), detail: input.detail, args: input.args, ...(input.approvalOptions !== undefined ? { options: input.approvalOptions } : {}), }, raw: { source: input.source, method: input.method, payload: input.rawPayload, }, }; } export function makeAcpRequestResolvedEvent(input: { readonly stamp: AcpEventStamp; readonly provider: ProviderDriverKind; readonly threadId: ThreadId; readonly turnId: TurnId | undefined; readonly requestId: RuntimeRequestId; readonly permissionRequest: AcpPermissionRequest; readonly decision: ProviderApprovalDecision; }): ProviderRuntimeEvent { return { type: "request.resolved", ...input.stamp, provider: input.provider, threadId: input.threadId, turnId: input.turnId, requestId: input.requestId, payload: { requestType: canonicalRequestTypeFromAcpKind(input.permissionRequest.kind), decision: input.decision, }, }; } export function makeAcpPlanUpdatedEvent(input: { readonly stamp: AcpEventStamp; readonly provider: ProviderDriverKind; readonly threadId: ThreadId; readonly turnId: TurnId | undefined; readonly payload: AcpPlanUpdate; readonly source: AcpAdapterRawSource; readonly method: string; readonly rawPayload: unknown; }): ProviderRuntimeEvent { return { type: "turn.plan.updated", ...input.stamp, provider: input.provider, threadId: input.threadId, turnId: input.turnId, payload: input.payload, raw: { source: input.source, method: input.method, payload: input.rawPayload, }, }; } export function makeAcpToolCallEvent(input: { readonly stamp: AcpEventStamp; readonly provider: ProviderDriverKind; readonly threadId: ThreadId; readonly turnId: TurnId | undefined; readonly toolCall: AcpToolCallState; readonly rawPayload: unknown; }): ProviderRuntimeEvent { const runtimeStatus = runtimeItemStatusFromAcpToolStatus(input.toolCall.status); return { type: input.toolCall.status === "completed" || input.toolCall.status === "failed" ? "item.completed" : "item.updated", ...input.stamp, provider: input.provider, threadId: input.threadId, turnId: input.turnId, itemId: RuntimeItemId.make(input.toolCall.toolCallId), payload: { itemType: canonicalItemTypeFromAcpToolKind(input.toolCall.kind), ...(runtimeStatus ? { status: runtimeStatus } : {}), ...(input.toolCall.title ? { title: input.toolCall.title } : {}), ...(input.toolCall.detail ? { detail: input.toolCall.detail } : {}), ...(Object.keys(input.toolCall.data).length > 0 ? { data: input.toolCall.data } : {}), }, raw: { source: "acp.jsonrpc", method: "session/update", payload: input.rawPayload, }, }; } export function makeAcpAssistantItemEvent(input: { readonly stamp: AcpEventStamp; readonly provider: ProviderDriverKind; readonly threadId: ThreadId; readonly turnId: TurnId | undefined; readonly itemId: string; readonly lifecycle: "item.started" | "item.completed"; }): ProviderRuntimeEvent { return { type: input.lifecycle, ...input.stamp, provider: input.provider, threadId: input.threadId, turnId: input.turnId, itemId: RuntimeItemId.make(input.itemId), payload: { itemType: "assistant_message", status: input.lifecycle === "item.completed" ? "completed" : "inProgress", }, }; } export function makeAcpContentDeltaEvent(input: { readonly stamp: AcpEventStamp; readonly provider: ProviderDriverKind; readonly threadId: ThreadId; readonly turnId: TurnId | undefined; readonly itemId?: string; readonly streamKind?: "assistant_text" | "reasoning_text"; readonly text: string; readonly rawPayload: unknown; }): ProviderRuntimeEvent { return { type: "content.delta", ...input.stamp, provider: input.provider, threadId: input.threadId, turnId: input.turnId, ...(input.itemId ? { itemId: RuntimeItemId.make(input.itemId) } : {}), payload: { streamKind: input.streamKind ?? "assistant_text", delta: input.text, }, raw: { source: "acp.jsonrpc", method: "session/update", payload: input.rawPayload, }, }; }