import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import {
isToolCallBlock,
isToolResultBlock,
resolveToolUseId,
type ToolContentBlock,
} from "../chat/tool-content.js"; import type { SessionEntry } from "../config/sessions.js"; import { normalizeOptionalString } from "../shared/string-coerce.js"; import { attachOpenClawTranscriptMeta } from "./session-utils.fs.js";
type ClaudeCliMessage = NonNullable<ClaudeCliProjectEntry["message"]>;
type ClaudeCliUsage = ClaudeCliMessage["usage"];
type TranscriptLikeMessage = Record<string, unknown>;
type ToolNameRegistry = Map<string, string>;
function cloneJsonValue<T>(value: T): T { return structuredClone(value);
}
function normalizeClaudeCliContent(
content: string | unknown[],
toolNameRegistry: ToolNameRegistry,
): string | unknown[] { if (!Array.isArray(content)) { return cloneJsonValue(content);
}
const normalized: ToolContentBlock[] = []; for (const item of content) { if (!item || typeof item !== "object") {
normalized.push(cloneJsonValue(item as ToolContentBlock)); continue;
} const block = cloneJsonValue(item as ToolContentBlock); const type = typeof block.type === "string" ? block.type : ""; if (type === "tool_use") { const id = normalizeOptionalString(block.id) ?? ""; const name = normalizeOptionalString(block.name) ?? ""; if (id && name) {
toolNameRegistry.set(id, name);
} if (block.input !== undefined && block.arguments === undefined) {
block.arguments = cloneJsonValue(block.input);
}
block.type = "toolcall"; delete block.input;
normalized.push(block); continue;
} if (type === "tool_result") { const toolUseId = resolveToolUseId(block); if (!block.name && toolUseId) { const toolName = toolNameRegistry.get(toolUseId); if (toolName) {
block.name = toolName;
}
}
normalized.push(block); continue;
}
normalized.push(block);
} return normalized;
}
function getMessageBlocks(message: unknown): ToolContentBlock[] | null { if (!message || typeof message !== "object") { returnnull;
} const content = (message as { content?: unknown }).content; return Array.isArray(content) ? (content as ToolContentBlock[]) : null;
}
function isAssistantToolCallMessage(message: unknown): boolean { if (!message || typeof message !== "object") { returnfalse;
} const role = (message as { role?: unknown }).role; if (role !== "assistant") { returnfalse;
} const blocks = getMessageBlocks(message); returnBoolean(blocks && blocks.length > 0 && blocks.every(isToolCallBlock));
}
function isUserToolResultMessage(message: unknown): boolean { if (!message || typeof message !== "object") { returnfalse;
} const role = (message as { role?: unknown }).role; if (role !== "user") { returnfalse;
} const blocks = getMessageBlocks(message); returnBoolean(blocks && blocks.length > 0 && blocks.every(isToolResultBlock));
}
function coalesceClaudeCliToolMessages(messages: TranscriptLikeMessage[]): TranscriptLikeMessage[] { const coalesced: TranscriptLikeMessage[] = []; for (let index = 0; index < messages.length; index += 1) { const current = messages[index]; const next = messages[index + 1]; if (!isAssistantToolCallMessage(current) || !isUserToolResultMessage(next)) {
coalesced.push(current); 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.