UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

225 lines (224 loc) 8.36 kB
import { t as truncateUtf8Prefix } from "./utf8-truncate-CCgJrv8A.js"; import { r as jsonUtf8Bytes } from "./json-utf8-bytes-fm9i4b7G.js"; import { s as toolResultFitsBudget } from "./tool-result-limits-CFk9DkYv.js"; import { t as renderToolSearchControlText } from "./tool-search-control-result-Dr6yxXnu.js"; //#region src/agents/code-mode-json.ts function toCodeModeJsonSafe(value) { if (value === void 0) return null; if (value === null || typeof value === "string" || typeof value === "boolean") return value; try { const serialized = JSON.stringify(value); return serialized === void 0 ? null : JSON.parse(serialized); } catch { if (value instanceof Error) return { name: value.name, message: value.message }; switch (typeof value) { case "number": return value; case "bigint": case "symbol": case "function": return String(value); default: return Object.prototype.toString.call(value); } } } const EMPTY_CODE_MODE_OUTPUT = { count: 0, source: { kind: "complete", json: "[]" } }; function sourceBytes(source) { return source.kind === "prefix" ? source.originalBytes : Buffer.byteLength(source.json, "utf8"); } function retainSource(json, originalBytes, maxBytes) { return originalBytes <= maxBytes ? { kind: "complete", json } : { kind: "prefix", json: truncateUtf8Prefix(json, maxBytes), originalBytes }; } /** Capture after guest conversion, before any public projection discards source facts. */ function captureCodeModeValue(value, maxBytes) { const json = JSON.stringify(toCodeModeJsonSafe(value)) ?? "null"; return retainSource(json, Buffer.byteLength(json, "utf8"), maxBytes); } function captureCodeModeOutput(output, maxBytes) { if (output.length === 0) return EMPTY_CODE_MODE_OUTPUT; const json = JSON.stringify(output.map(toCodeModeJsonSafe)); return { count: output.length, source: retainSource(json, Buffer.byteLength(json, "utf8"), maxBytes) }; } const TRUNCATION_GUIDANCE = "Output truncated; rerun with narrower args."; function createJsonPrefixFitter(text, maxBytes, overhead) { const bytes = Buffer.byteLength(text, "utf8"); let encoded; let completeBytes; return (limit) => { if (limit <= 0) return ""; if (bytes <= limit && (completeBytes ??= jsonUtf8Bytes(text)) + overhead(bytes) <= limit) return text; encoded ??= Buffer.from(text.slice(0, Math.ceil(Math.min(bytes, maxBytes)))); let end = 0; let jsonBytes = 2; while (end < encoded.byteLength) { const byte = encoded[end]; const width = byte < 128 ? 1 : byte < 224 ? 2 : byte < 240 ? 3 : 4; const next = end + width; if (next >= bytes || next > limit) break; jsonBytes += byte === 34 || byte === 92 || byte === 8 || byte === 9 || byte === 10 || byte === 12 || byte === 13 ? 2 : byte < 32 ? 6 : width; if (jsonBytes + overhead(next) > limit) break; end = next; } return encoded.subarray(0, end).toString("utf8"); }; } function createTruncationMarker(source, maxBytes) { const originalBytes = sourceBytes(source); const marker = { truncated: true, omittedBytes: originalBytes, guidance: TRUNCATION_GUIDANCE, prefix: "" }; const fixedBytes = jsonUtf8Bytes(marker) - 2 - String(originalBytes).length; const fit = createJsonPrefixFitter(source.json, maxBytes, (prefixBytes) => fixedBytes + String(originalBytes - prefixBytes).length); return (limit) => { const prefix = fit(limit); return { ...marker, omittedBytes: originalBytes - Buffer.byteLength(prefix, "utf8"), prefix }; }; } /** Nested bridge markers are ordinary guest data when later emitted or returned. */ function boundCodeModeValue(value, maxBytes) { const source = captureCodeModeValue(value, maxBytes); return source.kind === "complete" && sourceBytes(source) <= maxBytes ? JSON.parse(source.json) : createTruncationMarker(source, maxBytes)(maxBytes); } function createErrorFitter(error, maxBytes) { const suffix = " [error truncated]"; const fit = createJsonPrefixFitter(error, maxBytes, () => 18); return (limit) => `${fit(limit)}${suffix}`; } function boundCodeModeError(error, maxBytes) { return jsonUtf8Bytes(error) <= maxBytes ? error : createErrorFitter(error, maxBytes)(maxBytes); } /** One bounded cumulative source and delivery receipt, shared across every worker leg. */ var CodeModeOutputState = class { constructor(maxBytes, modelBudget) { this.maxBytes = maxBytes; this.modelBudget = modelBudget; this.source = EMPTY_CODE_MODE_OUTPUT; this.delivered = { kind: "entries", count: 0 }; } append(leg) { if (leg.count === 0) return; if (this.source.count === 0) { this.source = leg; return; } const previous = this.source.source; const originalBytes = sourceBytes(previous) + sourceBytes(leg.source) - 1; const json = previous.kind === "prefix" ? previous.json : previous.json.slice(0, -1) + "," + leg.source.json.slice(1); this.source = { count: this.source.count + leg.count, source: retainSource(json, originalBytes, this.maxBytes) }; } take(params = {}) { return this.takeResult({}, params); } takeResult(metadata, params = {}, networkContent = false) { const project = this.createProjector(params); const fits = (candidate) => { const rendered = renderToolSearchControlText(JSON.stringify({ ...metadata, ...candidate.channels }, null, 2), networkContent); return !rendered.truncated && toolResultFitsBudget(rendered.text, this.modelBudget); }; let projection = project(this.maxBytes); if ((this.modelBudget || networkContent) && !fits(projection)) { let low = 0; let high = this.maxBytes - 1; let best; while (low <= high) { const middle = Math.floor((low + high) / 2); const candidate = project(middle); if (fits(candidate)) { best = candidate; low = middle + 1; } else high = middle - 1; } if (!best) throw new Error("Model tool-result budget cannot fit Code Mode status; use a larger model context."); projection = best; } const prior = this.delivered; const { channels, receipt } = projection; this.delivered = receipt; const output = receipt.kind === "entries" ? channels.output.slice(prior.kind === "entries" ? prior.count : 0) : prior.kind === "summary" && prior.originalBytes === receipt.originalBytes && prior.prefixBytes === receipt.prefixBytes ? [] : channels.output; return { ...metadata, ...channels, output }; } createProjector(params) { const { count, source } = this.source; const { value, error: fullError } = params; const outputBytes = count === 0 ? 0 : sourceBytes(source); const valueBytes = value === void 0 ? 0 : sourceBytes(value); const errorBytes = fullError === void 0 ? 0 : jsonUtf8Bytes(fullError); let outputMarker; let valueMarker; let errorFitter; return (maxBytes) => { const errorAllowance = maxBytes - Math.min(outputBytes + valueBytes, Math.floor(maxBytes / 2)); const error = fullError === void 0 || errorBytes <= errorAllowance ? fullError : (errorFitter ??= createErrorFitter(fullError, this.maxBytes))(errorAllowance); const remaining = maxBytes - (error === void 0 ? 0 : jsonUtf8Bytes(error)); const outputAllowance = remaining - Math.min(valueBytes, Math.floor(remaining / 2)); let output; let chargedOutputBytes; let receipt; if (outputBytes <= outputAllowance) { output = JSON.parse(source.json); chargedOutputBytes = outputBytes; receipt = { kind: "entries", count }; } else { const marker = (outputMarker ??= createTruncationMarker(source, this.maxBytes))(outputAllowance - 2); const prefixBytes = Buffer.byteLength(marker.prefix, "utf8"); output = [marker]; chargedOutputBytes = jsonUtf8Bytes([marker]); receipt = { kind: "summary", originalBytes: outputBytes, prefixBytes }; } return { receipt, channels: { output, ...value === void 0 ? {} : { value: value.kind === "complete" && valueBytes <= remaining - chargedOutputBytes ? JSON.parse(value.json) : (valueMarker ??= createTruncationMarker(value, this.maxBytes))(remaining - chargedOutputBytes) }, ...error === void 0 ? {} : { error } } }; }; } }; //#endregion export { captureCodeModeOutput as a, boundCodeModeValue as i, EMPTY_CODE_MODE_OUTPUT as n, captureCodeModeValue as o, boundCodeModeError as r, toCodeModeJsonSafe as s, CodeModeOutputState as t };