import type { SessionEntry } from "@earendil-works/pi-coding-agent"; interface TextBlock { type?: string; text?: string; } function messageText(entry: SessionEntry, role: "user" | "assistant"): string | undefined { if (entry.type !== "message" || entry.message.role !== role) return undefined; const content = entry.message.content; if (typeof content === "string") return content; if (Array.isArray(content)) { return (content as TextBlock[]) .filter((block) => block?.type === "text") .map((block) => block.text ?? "") .join(""); } return undefined; } export function userMessageText(entry: SessionEntry): string | undefined { return messageText(entry, "user"); } export function lastAssistantMessageText(entries: SessionEntry[]): string | undefined { for (let i = entries.length - 1; i >= 0; i--) { const text = messageText(entries[i]!, "assistant"); if (text !== undefined && text.trim() !== "") return text; } return undefined; } function countMatches(entries: SessionEntry[], expected: string[], leadingOnly: boolean): number { let matched = 0; for (const entry of entries) { const text = userMessageText(entry); if (text === undefined) continue; if (matched < expected.length && text === expected[matched]) matched++; else if (leadingOnly) break; } return matched; } export function countPhaseMatches(entries: SessionEntry[], expected: string[], leading = false): number { return countMatches(entries, expected, leading); } function lastNonUserMessage(entries: SessionEntry[], from: number, to: number): SessionEntry | undefined { for (let i = to; i >= from; i--) { const entry = entries[i]!; if (entry.type === "message" && entry.message.role !== "user") return entry; } return undefined; } function lastNonUserBeforeNextUser(entries: SessionEntry[], from: number): SessionEntry | undefined { for (let i = from; i < entries.length; i++) { if (userMessageText(entries[i]) !== undefined) { return lastNonUserMessage(entries, from, i - 1); } } return lastNonUserMessage(entries, from, entries.length - 1); } export function findAnchorAfterMessage(entries: SessionEntry[], messageText: string): SessionEntry | undefined { let firstUserIndex = -1; for (let i = 0; i < entries.length; i++) { if (userMessageText(entries[i]) !== undefined) { firstUserIndex = i; break; } } for (let i = entries.length - 1; i >= 0; i--) { if (userMessageText(entries[i]) === messageText) { const anchor = lastNonUserBeforeNextUser(entries, i + 1); if (anchor !== undefined) return anchor; } } if (firstUserIndex === -1) return undefined; return lastNonUserBeforeNextUser(entries, firstUserIndex + 1); } export function countUserTextMatches(entries: SessionEntry[], text: string): number { return entries.filter((entry) => userMessageText(entry) === text).length; } export function lastAssistantStopReason(entries: SessionEntry[]): string | undefined { for (let i = entries.length - 1; i >= 0; i--) { const entry = entries[i]!; if (entry.type !== "message") continue; const message = entry.message as unknown as Record; if (message["role"] === "assistant") return message["stopReason"] as string | undefined; } return undefined; }