import type { AgentMessage } from "@earendil-works/pi-agent-core"; import { sessionEntryToContextMessages, type SessionEntry, } from "@earendil-works/pi-coding-agent"; import { isRemoteCompactionData, type RemoteCompactionData, type RemoteModelIdentity, } from "./remote-compaction.ts"; export const REMOTE_ENTRY_TYPE = "pi-smart-compaction-remote"; export interface RemoteCompactionAttachment extends RemoteCompactionData { compactionEntryId: string; coverageThroughEntryId: string; } export interface LocatedRemoteCheckpoint { checkpointId: string; coverageIndex: number; data: RemoteCompactionData; identity: RemoteModelIdentity; } function isRecord(value: unknown): value is Record { return typeof value === "object" && value !== null && !Array.isArray(value); } export function isRemoteAttachment(value: unknown): value is RemoteCompactionAttachment { return ( isRemoteCompactionData(value) && isRecord(value) && typeof value.compactionEntryId === "string" && typeof value.coverageThroughEntryId === "string" ); } function latestFormalIndex(branch: SessionEntry[]): number { for (let index = branch.length - 1; index >= 0; index--) { if (branch[index].type === "compaction") return index; } return -1; } function entryMessages(entry: SessionEntry): AgentMessage[] { if (entry.type === "compaction") return []; return sessionEntryToContextMessages(entry); } function remoteIdentity(data: RemoteCompactionData): RemoteModelIdentity { return { provider: data.provider, api: data.api, model: data.model, ...(data.baseUrl ? { baseUrl: data.baseUrl } : {}), }; } export function findActiveRemoteCheckpoint( branch: SessionEntry[], entries: SessionEntry[] = branch, ): LocatedRemoteCheckpoint | undefined { const formalIndex = latestFormalIndex(branch); if (formalIndex < 0) return undefined; const formal = branch[formalIndex]; for (let index = entries.length - 1; index >= 0; index--) { const entry = entries[index]; if (entry.type !== "custom" || entry.customType !== REMOTE_ENTRY_TYPE) continue; if (!isRemoteAttachment(entry.data)) continue; const attachment = entry.data; if (attachment.compactionEntryId !== formal.id) continue; const coverageIndex = branch.findIndex( (candidate) => candidate.id === attachment.coverageThroughEntryId, ); if (coverageIndex < 0 || coverageIndex > formalIndex) continue; return { checkpointId: entry.id, coverageIndex, data: attachment, identity: remoteIdentity(attachment), }; } return undefined; } export function messagesAfterCoverage( branch: SessionEntry[], remote: LocatedRemoteCheckpoint, ): AgentMessage[] { return branch.slice(remote.coverageIndex + 1).flatMap(entryMessages); } export function hasForeignAssistantTurn( messages: AgentMessage[], identity: RemoteModelIdentity, ): boolean { return messages.some((message) => message.role === "assistant" && ( message.provider !== identity.provider || message.api !== identity.api || message.model !== identity.model ), ); }