import type { AgentMessage } from "@mariozechner/pi-agent-core"; import { normalizeOptionalString } from "../../shared/string-coerce.js"; import { extractToolCallsFromAssistant, extractToolResultId } from "../tool-call-id.js";
function collectAnyToolResultIds(message: AgentMessage): Set<string> { const ids = new Set<string>(); const role = (message as { role?: unknown }).role; if (role === "toolResult") { const toolResultId = extractToolResultId(
message as Extract<AgentMessage, { role: "toolResult" }>,
); if (toolResultId) {
ids.add(toolResultId);
}
} elseif (role === "tool") { const record = message as unknown as Record<string, unknown>; for (const id of extractToolResultMatchIds(record)) {
ids.add(id);
}
}
const content = (message as { content?: unknown }).content; if (!Array.isArray(content)) { return ids;
}
for (const block of content) { if (!block || typeof block !== "object") { continue;
} const record = block as Record<string, unknown>; if (record.type !== "toolResult" && record.type !== "tool") { continue;
} for (const id of extractToolResultMatchIds(record)) {
ids.add(id);
}
}
return ids;
}
function collectTrustedToolResultMatches(message: AgentMessage): Map<string, Set<string>> { const matches = new Map<string, Set<string>>(); const role = (message as { role?: unknown }).role; const addMatch = (ids: Iterable<string>, toolName: string | null) => { for (const id of ids) { const bucket = matches.get(id) ?? new Set<string>(); if (toolName) {
bucket.add(toolName);
}
matches.set(id, bucket);
}
};
if (role === "toolResult") { const record = message as unknown as Record<string, unknown>;
addMatch(
[
...extractToolResultMatchIds(record),
...(() => { const canonicalId = extractToolResultId(
message as Extract<AgentMessage, { role: "toolResult" }>,
); return canonicalId ? [canonicalId] : [];
})(),
],
extractToolResultMatchName(record),
);
} elseif (role === "tool") { const record = message as unknown as Record<string, unknown>;
addMatch(extractToolResultMatchIds(record), extractToolResultMatchName(record));
}
return matches;
}
function collectFutureToolResultMatches(
messages: AgentMessage[],
startIndex: number,
): Map<string, Set<string>> { const matches = new Map<string, Set<string>>(); for (let index = startIndex + 1; index < messages.length; index += 1) { const candidate = messages[index]; if (!candidate || typeof candidate !== "object") { continue;
} if ((candidate as { role?: unknown }).role === "assistant") { break;
} for (const [id, toolNames] of collectTrustedToolResultMatches(candidate)) { const bucket = matches.get(id) ?? new Set<string>(); for (const toolName of toolNames) {
bucket.add(toolName);
}
matches.set(id, bucket);
}
} return matches;
}
function collectFutureToolResultIds(messages: AgentMessage[], startIndex: number): Set<string> { const ids = new Set<string>(); for (let index = startIndex + 1; index < messages.length; index += 1) { const candidate = messages[index]; if (!candidate || typeof candidate !== "object") { continue;
} if ((candidate as { role?: unknown }).role === "assistant") { break;
} for (const id of collectAnyToolResultIds(candidate)) {
ids.add(id);
}
} return ids;
}
/** * Strips dangling tool-call blocks from assistant messages when no later * tool-result span before the next assistant turn resolves them. * This fixes the "tool_use ids found without tool_result blocks" error from Anthropic.
*/ function stripDanglingAnthropicToolUses(messages: AgentMessage[]): AgentMessage[] { const result: AgentMessage[] = [];
for (let i = 0; i < messages.length; i++) { const msg = messages[i]; if (!msg || typeof msg !== "object") {
result.push(msg); continue;
}
const msgRole = (msg as { role?: unknown }).role as string | undefined; if (msgRole !== "assistant") {
result.push(msg); continue;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.