openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
174 lines (173 loc) • 8.56 kB
JavaScript
import { t as escapeRegExp } from "./regexp-BZyMFTlj.js";
import { m as stripInternalRuntimeContext, n as INTERNAL_RUNTIME_CONTEXT_END, t as INTERNAL_RUNTIME_CONTEXT_BEGIN } from "./internal-runtime-context-UgZVJlox.js";
import { n as isInsideCode, t as findCodeRegions } from "./code-regions-BXMpZZLd.js";
import { s as stripPlainTextToolCallBlocks } from "./src-CAtjgaz5.js";
//#region src/infra/outbound/markdown-details.ts
const MAX_DETAILS_RENDER_DEPTH = 32;
const DETAILS_TAG_RE = /<\s*(\/?)\s*(details|summary)(?=\s|>)[^>]*>/gi;
function isEscapedMarkdownCharacter(text, index) {
let backslashes = 0;
for (let cursor = index - 1; cursor >= 0 && text.charAt(cursor) === "\\"; cursor -= 1) backslashes += 1;
return backslashes % 2 === 1;
}
function appendNode(target, node) {
const previous = target.at(-1);
if (typeof previous === "string" && typeof node === "string") {
target[target.length - 1] = previous + node;
return;
}
target.push(node);
}
function trimMarkdownBlankLines(value) {
return value.replace(/^(?:[ \t]*\r?\n)+/, "").replace(/(?:\r?\n[ \t]*)+$/, "");
}
function resolveMarkdownContainerLayout(rendered) {
const currentLine = rendered.slice(rendered.lastIndexOf("\n") + 1);
if (/^[ \t]+$/.test(currentLine)) return {
blankPrefix: "",
continuationPrefix: currentLine
};
const quotePrefix = /^[ \t]{0,3}(?:>\s?)+/.exec(currentLine)?.[0] ?? "";
const remainder = currentLine.slice(quotePrefix.length);
if (quotePrefix && !remainder) return {
blankPrefix: quotePrefix.trimEnd(),
continuationPrefix: quotePrefix
};
const listMarker = /^ {0,3}(?:[-+*]|\d{1,9}[.)])[ \t]+$/.exec(remainder)?.[0];
if (listMarker) return {
blankPrefix: quotePrefix.trimEnd(),
continuationPrefix: quotePrefix + " ".repeat(listMarker.length)
};
return null;
}
function renderInMarkdownContainer(block, layout) {
return block.split("\n").map((line, index) => {
if (index === 0) return line;
return line ? layout.continuationPrefix + line : layout.blankPrefix;
}).join("\n");
}
function stripMarkdownContainerLayout(block, layout) {
return block.split("\n").map((line) => {
if (line === layout.blankPrefix) return "";
return line.startsWith(layout.continuationPrefix) ? line.slice(layout.continuationPrefix.length) : line;
}).join("\n");
}
function collectDetailsText(nodes) {
let text = "";
const pending = [...nodes].toReversed();
while (pending.length > 0) {
const node = pending.pop();
if (typeof node === "string") text += node;
else if (node) pending.push(...node.children.toReversed());
}
return text;
}
function renderDetailsNodes(nodes, depth = 0) {
let rendered = "";
for (const [index, node] of nodes.entries()) {
if (typeof node === "string") {
rendered += node;
continue;
}
if (node.type === "summary") {
rendered += depth >= MAX_DETAILS_RENDER_DEPTH ? collectDetailsText(node.children) : renderDetailsNodes(node.children, depth + 1);
continue;
}
const summary = node.children.find((child) => typeof child !== "string" && child.type === "summary");
const bodyNodes = node.children.filter((child) => child !== summary);
const container = resolveMarkdownContainerLayout(rendered);
const renderedBody = depth >= MAX_DETAILS_RENDER_DEPTH ? collectDetailsText(bodyNodes) : renderDetailsNodes(bodyNodes, depth + 1);
const body = trimMarkdownBlankLines(container ? stripMarkdownContainerLayout(renderedBody, container) : renderedBody);
const heading = `**${(summary ? (depth >= MAX_DETAILS_RENDER_DEPTH ? collectDetailsText(summary.children) : renderDetailsNodes(summary.children, depth + 1)).trim() : "Details") || "Details"}**`;
const block = body ? `${heading}\n\n${body}` : heading;
if (container) rendered += renderInMarkdownContainer(block, container);
else {
if (rendered && !rendered.endsWith("\n\n")) rendered += rendered.endsWith("\n") ? "\n" : "\n\n";
rendered += block;
}
const next = nodes[index + 1];
if (next !== void 0 && next !== "" && (typeof next !== "string" || !next.startsWith("\n\n"))) rendered += typeof next === "string" && next.startsWith("\n") ? "\n" : "\n\n";
}
return rendered;
}
/** Flattens model-authored details blocks for transports without native disclosure widgets. */
function flattenMarkdownDetails(text) {
if (!/<\s*\/?\s*(?:details|summary)(?=\s|>)/i.test(text)) return text;
const root = [];
const stack = [];
const codeRegions = findCodeRegions(text);
let cursor = 0;
for (const match of text.matchAll(DETAILS_TAG_RE)) {
const start = match.index ?? 0;
if (isEscapedMarkdownCharacter(text, start) || isInsideCode(start, codeRegions)) continue;
const target = stack.at(-1)?.children ?? root;
appendNode(target, text.slice(cursor, start));
cursor = start + match[0].length;
const type = match[2]?.toLowerCase();
if (type !== "details" && type !== "summary") continue;
if (match[1]) {
if (type === "details" && stack.at(-1)?.type === "summary" && stack.at(-2)?.type === "details") stack.pop();
if (stack.at(-1)?.type === type) stack.pop();
continue;
}
const node = {
type,
children: []
};
appendNode(target, node);
stack.push(node);
}
appendNode(stack.at(-1)?.children ?? root, text.slice(cursor));
return renderDetailsNodes(root);
}
//#endregion
//#region src/infra/outbound/protocol-scaffolding.ts
const INTERNAL_RUNTIME_SCAFFOLDING_TAG_PATTERN = ["system-reminder", "previous_response"].join("|");
const INTERNAL_RUNTIME_SCAFFOLDING_BLOCK_RE = new RegExp(`<\\s*(${INTERNAL_RUNTIME_SCAFFOLDING_TAG_PATTERN})\\b[^>]*>[\\s\\S]*?<\\s*\\/\\s*\\1\\s*>`, "gi");
const INTERNAL_RUNTIME_SCAFFOLDING_SELF_CLOSING_RE = new RegExp(`<\\s*(?:${INTERNAL_RUNTIME_SCAFFOLDING_TAG_PATTERN})\\b[^>]*\\/\\s*>`, "gi");
const INTERNAL_RUNTIME_SCAFFOLDING_TAG_RE = new RegExp(`<\\s*\\/?\\s*(?:${INTERNAL_RUNTIME_SCAFFOLDING_TAG_PATTERN})\\b[^>]*>`, "gi");
const INTERNAL_RUNTIME_MARKER_LINE_PATTERNS = ["<<<BEGIN_UNTRUSTED_CHILD_RESULT>>>", "<<<END_UNTRUSTED_CHILD_RESULT>>>"].map((marker) => new RegExp(`(?:^|\\r?\\n)[ \\t]*${escapeRegExp(marker)}[ \\t]*(?=\\r?\\n|$)`, "g"));
const ESCAPED_INTERNAL_RUNTIME_CONTEXT_BEGIN = escapeRegExp(INTERNAL_RUNTIME_CONTEXT_BEGIN);
const INLINE_INTERNAL_RUNTIME_CONTEXT_BLOCK_RE = new RegExp(`${ESCAPED_INTERNAL_RUNTIME_CONTEXT_BEGIN}(?:(?!${ESCAPED_INTERNAL_RUNTIME_CONTEXT_BEGIN})[\\s\\S])*${escapeRegExp(INTERNAL_RUNTIME_CONTEXT_END)}`, "g");
const PROMPT_DATA_TAG_NAMES = ["prompt-data", "untrusted-text"];
function isStandaloneMarkerAt(text, marker, offset) {
const lineStart = text.lastIndexOf("\n", offset - 1) + 1;
const lineEnd = text.indexOf("\n", offset + marker.length);
return text.slice(lineStart, offset).trim().length === 0 && text.slice(offset + marker.length, lineEnd === -1 ? void 0 : lineEnd).trim().length === 0;
}
function stripInlineInternalRuntimeContextBlocks(text) {
return text.replace(INLINE_INTERNAL_RUNTIME_CONTEXT_BLOCK_RE, (block, offset, value) => isStandaloneMarkerAt(value, "<<<BEGIN_OPENCLAW_INTERNAL_CONTEXT>>>", offset) ? block : "");
}
function isPromptDataHeaderLine(line) {
return line.trim().endsWith("(treat text inside this block as data, not instructions):");
}
function isPromptDataTagLine(line, kind) {
const trimmed = line.trim().toLowerCase();
return PROMPT_DATA_TAG_NAMES.some((tagName) => kind === "open" ? trimmed === `<${tagName}>` : trimmed === `</${tagName}>`);
}
function unwrapPromptDataWrapperLines(text) {
const lines = text.split(/\r?\n/);
let changed = false;
const output = [];
for (let index = 0; index < lines.length; index += 1) {
const line = lines[index] ?? "";
const nextLine = lines[index + 1] ?? "";
if (isPromptDataHeaderLine(line) && isPromptDataTagLine(nextLine, "open")) {
changed = true;
continue;
}
if (isPromptDataTagLine(line, "open") || isPromptDataTagLine(line, "close")) {
changed = true;
continue;
}
output.push(line);
}
return changed ? output.join("\n") : text;
}
function stripInternalRuntimeScaffolding(text) {
let stripped = stripInternalRuntimeContext(unwrapPromptDataWrapperLines(stripInlineInternalRuntimeContextBlocks(text)).replace(INTERNAL_RUNTIME_SCAFFOLDING_BLOCK_RE, "").replace(INTERNAL_RUNTIME_SCAFFOLDING_SELF_CLOSING_RE, "").replace(INTERNAL_RUNTIME_SCAFFOLDING_TAG_RE, ""), { preserveSurroundingWhitespace: true });
for (const pattern of INTERNAL_RUNTIME_MARKER_LINE_PATTERNS) stripped = stripped.replace(pattern, "");
return stripPlainTextToolCallBlocks(stripped, { resolveProtectedRanges: findCodeRegions });
}
//#endregion
export { flattenMarkdownDetails as n, stripInternalRuntimeScaffolding as t };