





















@@ -24,6 +24,10 @@ const NESTED_TOOL_RESULT_BLOCK_TYPES = new Set(["toolresult", "tool_result"]);
24242525type MiddlewareContentBlock = OpenClawAgentToolResult["content"][number];
2626type MiddlewareContentCoerceState = { depth: number; seen: Set<object> };
27+type MiddlewareToolResultCoerceOptions = {
28+sanitizeContent?: boolean;
29+sanitizeDetails?: boolean;
30+};
27312832function isValidMiddlewareContentBlock(value: unknown): boolean {
2933if (!isRecord(value) || typeof value.type !== "string") {
@@ -158,6 +162,7 @@ function stringifyMiddlewareTextPayload(value: unknown): string | undefined {
158162function coerceMiddlewareText(
159163value: unknown,
160164state: MiddlewareContentCoerceState = createMiddlewareContentCoerceState(),
165+options: MiddlewareToolResultCoerceOptions = {},
161166): string | undefined {
162167if (typeof value === "string") {
163168return value;
@@ -173,14 +178,14 @@ function coerceMiddlewareText(
173178return undefined;
174179}
175180for (const key of ["text", "output", "result", "message"]) {
176-const text = coerceMiddlewareText(value[key], nextState);
181+const text = coerceMiddlewareText(value[key], nextState, options);
177182if (text !== undefined) {
178183return text;
179184}
180185}
181186const content = value.content;
182187if (Array.isArray(content)) {
183-const chunks = coerceMiddlewareContentArray(content, nextState)
188+const chunks = coerceMiddlewareContentArray(content, nextState, options)
184189.filter(
185190(block): block is Extract<MiddlewareContentBlock, { type: "text" }> =>
186191block.type === "text",
@@ -224,6 +229,7 @@ function appendMiddlewareContentBlock(
224229function coerceMiddlewareContentArray(
225230content: unknown[],
226231state: MiddlewareContentCoerceState,
232+options: MiddlewareToolResultCoerceOptions = {},
227233): MiddlewareContentBlock[] {
228234const blocks: MiddlewareContentBlock[] = [];
229235let inspectedBlocks = 0;
@@ -235,7 +241,7 @@ function coerceMiddlewareContentArray(
235241) {
236242break;
237243}
238-const coercedBlocks = coerceMiddlewareContentBlocks(entry, state);
244+const coercedBlocks = coerceMiddlewareContentBlocks(entry, state, options);
239245if (coercedBlocks.length > 0) {
240246for (const block of coercedBlocks) {
241247appendMiddlewareContentBlock(blocks, block);
@@ -245,7 +251,7 @@ function coerceMiddlewareContentArray(
245251}
246252continue;
247253}
248-const text = coerceMiddlewareText(entry, state);
254+const text = coerceMiddlewareText(entry, state, options);
249255if (text) {
250256appendMiddlewareContentBlock(blocks, {
251257type: "text",
@@ -259,10 +265,22 @@ function coerceMiddlewareContentArray(
259265function coerceMiddlewareContentBlocks(
260266value: unknown,
261267state: MiddlewareContentCoerceState = createMiddlewareContentCoerceState(),
268+options: MiddlewareToolResultCoerceOptions = {},
262269): MiddlewareContentBlock[] {
263270if (isValidMiddlewareContentBlock(value)) {
264271return [value as MiddlewareContentBlock];
265272}
273+// Tool emitters can produce legitimate transcript text larger than the
274+// middleware cap. Normalize that only before the first handler; handlers
275+// remain fail-closed if they return an oversized replacement.
276+if (
277+options.sanitizeContent === true &&
278+isRecord(value) &&
279+value.type === "text" &&
280+typeof value.text === "string"
281+) {
282+return [{ type: "text", text: truncateUtf16Safe(value.text, MAX_MIDDLEWARE_TEXT_CHARS) }];
283+}
266284if (!isRecord(value) || typeof value.type !== "string") {
267285return [];
268286}
@@ -273,9 +291,10 @@ function coerceMiddlewareContentBlocks(
273291const content = value.content;
274292if (Array.isArray(content) && content.length > 0) {
275293const nextState = descendMiddlewareContentCoerceState(value, state);
276-return nextState ? coerceMiddlewareContentArray(content, nextState) : [];
294+return nextState ? coerceMiddlewareContentArray(content, nextState, options) : [];
277295}
278-const text = coerceMiddlewareText(content, state) ?? coerceMiddlewareText(value, state);
296+const text =
297+coerceMiddlewareText(content, state, options) ?? coerceMiddlewareText(value, state, options);
279298if (!text) {
280299return [];
281300}
@@ -289,7 +308,7 @@ function coerceMiddlewareContentBlocks(
289308290309function coerceMiddlewareToolResult(
291310value: unknown,
292-options: { sanitizeDetails?: boolean } = {},
311+options: MiddlewareToolResultCoerceOptions = {},
293312): OpenClawAgentToolResult | undefined {
294313if (isValidMiddlewareToolResult(value)) {
295314return value;
@@ -305,7 +324,7 @@ function coerceMiddlewareToolResult(
305324if (inspectedBlocks > MAX_MIDDLEWARE_CONTENT_BLOCKS) {
306325break;
307326}
308-for (const coerced of coerceMiddlewareContentBlocks(block, state)) {
327+for (const coerced of coerceMiddlewareContentBlocks(block, state, options)) {
309328content.push(coerced);
310329if (content.length >= MAX_MIDDLEWARE_CONTENT_BLOCKS) {
311330break;
@@ -379,7 +398,10 @@ function sanitizeMiddlewareDetailsValue(value: unknown): unknown {
379398 * subsequent middleware-side mutations are still validated strictly.
380399 */
381400function sanitizeToolResultForMiddleware(result: OpenClawAgentToolResult): OpenClawAgentToolResult {
382-const coerced = coerceMiddlewareToolResult(result, { sanitizeDetails: true });
401+const coerced = coerceMiddlewareToolResult(result, {
402+sanitizeContent: true,
403+sanitizeDetails: true,
404+});
383405if (coerced) {
384406return coerced;
385407}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。