




























@@ -12,6 +12,9 @@ const log = createSubsystemLogger("agents/harness");
1212const MAX_MIDDLEWARE_CONTENT_BLOCKS = 200;
1313const MAX_MIDDLEWARE_TEXT_CHARS = 100_000;
1414const MAX_MIDDLEWARE_IMAGE_DATA_CHARS = 5_000_000;
15+const MAX_MIDDLEWARE_DETAILS_BYTES = 100_000;
16+const MAX_MIDDLEWARE_DETAILS_DEPTH = 20;
17+const MAX_MIDDLEWARE_DETAILS_KEYS = 1_000;
15181619function isRecord(value: unknown): value is Record<string, unknown> {
1720return value !== null && typeof value === "object" && !Array.isArray(value);
@@ -35,14 +38,71 @@ function isValidMiddlewareContentBlock(value: unknown): boolean {
3538return false;
3639}
374041+function isValidMiddlewareDetails(
42+value: unknown,
43+state: { keys: number; bytes: number; seen: WeakSet<object> } = {
44+keys: 0,
45+bytes: 0,
46+seen: new WeakSet<object>(),
47+},
48+depth = 0,
49+): boolean {
50+if (value === undefined || value === null) {
51+return true;
52+}
53+if (depth > MAX_MIDDLEWARE_DETAILS_DEPTH) {
54+return false;
55+}
56+if (typeof value === "string") {
57+state.bytes += value.length;
58+return state.bytes <= MAX_MIDDLEWARE_DETAILS_BYTES;
59+}
60+if (typeof value === "number" || typeof value === "boolean") {
61+state.bytes += String(value).length;
62+return state.bytes <= MAX_MIDDLEWARE_DETAILS_BYTES;
63+}
64+if (typeof value !== "object") {
65+return false;
66+}
67+if (state.seen.has(value)) {
68+return false;
69+}
70+state.seen.add(value);
71+if (Array.isArray(value)) {
72+state.keys += value.length;
73+if (state.keys > MAX_MIDDLEWARE_DETAILS_KEYS) {
74+return false;
75+}
76+for (const entry of value) {
77+if (!isValidMiddlewareDetails(entry, state, depth + 1)) {
78+return false;
79+}
80+}
81+return true;
82+}
83+for (const [key, entry] of Object.entries(value)) {
84+state.keys += 1;
85+state.bytes += key.length;
86+if (state.keys > MAX_MIDDLEWARE_DETAILS_KEYS || state.bytes > MAX_MIDDLEWARE_DETAILS_BYTES) {
87+return false;
88+}
89+if (!isValidMiddlewareDetails(entry, state, depth + 1)) {
90+return false;
91+}
92+}
93+return true;
94+}
95+3896function isValidMiddlewareToolResult(value: unknown): value is OpenClawAgentToolResult {
3997if (!isRecord(value) || !Array.isArray(value.content)) {
4098return false;
4199}
42100if (value.content.length > MAX_MIDDLEWARE_CONTENT_BLOCKS) {
43101return false;
44102}
45-return value.content.every(isValidMiddlewareContentBlock);
103+return (
104+value.content.every(isValidMiddlewareContentBlock) && isValidMiddlewareDetails(value.details)
105+);
46106}
4710748108function buildMiddlewareFailureResult(): OpenClawAgentToolResult {
@@ -54,7 +114,7 @@ function buildMiddlewareFailureResult(): OpenClawAgentToolResult {
54114},
55115],
56116details: {
57-status: "failed",
117+status: "error",
58118middlewareError: true,
59119},
60120};
@@ -72,17 +132,20 @@ export function createAgentToolResultMiddlewareRunner(
72132for (const handler of handlers) {
73133try {
74134const next = await handler({ ...event, result: current }, ctx);
75-if (next?.result) {
76-if (isValidMiddlewareToolResult(next.result)) {
77-current = next.result;
78-} else {
79-log.warn(
80-`[${ctx.harness}] discarded invalid tool result middleware output for ${truncateUtf16Safe(
81- event.toolName,
82- 120,
83- )}`,
84-);
85-}
135+// Middleware may mutate event.result in place for legacy Pi parity.
136+// Validate the current object after every handler so in-place writes
137+// cannot bypass the same shape and size bounds as returned results.
138+const candidate = next?.result ?? current;
139+if (isValidMiddlewareToolResult(candidate)) {
140+current = candidate;
141+} else {
142+log.warn(
143+`[${ctx.harness}] discarded invalid tool result middleware output for ${truncateUtf16Safe(
144+ event.toolName,
145+ 120,
146+ )}`,
147+);
148+return buildMiddlewareFailureResult();
86149}
87150} catch {
88151log.warn(
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。