From 3c5e68e80faef28971d9e4f22d26dc5b0879ac7a Mon Sep 17 00:00:00 2001 From: Moeed Ahmed Date: Sat, 9 May 2026 15:08:47 +0100 Subject: [PATCH] fix: bound subagent completion context --- .../telegram/src/bot-handlers.runtime.ts | 4 ++-- src/agents/internal-events.ts | 16 ++++++++++++++- ...ded-helpers.sanitizeuserfacingtext.test.ts | 20 +++++++++++++++++++ src/agents/subagent-announce-delivery.test.ts | 1 + 4 files changed, 38 insertions(+), 3 deletions(-) diff --git a/extensions/telegram/src/bot-handlers.runtime.ts b/extensions/telegram/src/bot-handlers.runtime.ts index 92f292a8b14..3a6fdfbff1b 100644 --- a/extensions/telegram/src/bot-handlers.runtime.ts +++ b/extensions/telegram/src/bot-handlers.runtime.ts @@ -300,7 +300,7 @@ export const registerTelegramHandlers = ({ if (!isMultiToggleButton(button) || !isSelectedMultiButton(button)) { return []; } - return [button.callback_data!.slice(`${MULTI_SELECT_PREFIX}toggle|`.length)]; + return [button.callback_data.slice(`${MULTI_SELECT_PREFIX}toggle|`.length)]; }), ); const updateMultiSelectKeyboard = ( @@ -313,7 +313,7 @@ export const registerTelegramHandlers = ({ if (!isMultiToggleButton(button)) { return button; } - const buttonValue = button.callback_data!.slice(`${MULTI_SELECT_PREFIX}toggle|`.length); + const buttonValue = button.callback_data.slice(`${MULTI_SELECT_PREFIX}toggle|`.length); const baseText = stripMultiSelectPrefix(button.text); const selected = action === "clear" diff --git a/src/agents/internal-events.ts b/src/agents/internal-events.ts index 6ecb489e138..13e60b0237e 100644 --- a/src/agents/internal-events.ts +++ b/src/agents/internal-events.ts @@ -25,6 +25,8 @@ type AgentTaskCompletionInternalEvent = { replyInstruction: string; }; +const MAX_CHILD_RESULT_PROMPT_CHARS = 4_000; + export type AgentInternalEvent = AgentTaskCompletionInternalEvent; export { INTERNAL_RUNTIME_CONTEXT_BEGIN, INTERNAL_RUNTIME_CONTEXT_END }; @@ -41,11 +43,23 @@ function sanitizeMultilineField(value: string, fallback: string): string { return sanitized || fallback; } +function truncateChildResultForPrompt(value: string): string { + if (value.length <= MAX_CHILD_RESULT_PROMPT_CHARS) { + return value; + } + return [ + value.slice(0, MAX_CHILD_RESULT_PROMPT_CHARS).trimEnd(), + "", + `[child result truncated: ${value.length - MAX_CHILD_RESULT_PROMPT_CHARS} additional characters omitted]`, + ].join("\n"); +} + function formatChildResultDataBlock(value: string): string { + const safeValue = truncateChildResultForPrompt(value); return ( wrapPromptDataBlock({ label: "Child result", - text: value, + text: safeValue, }) || "Child result: (no output)" ); } diff --git a/src/agents/pi-embedded-helpers.sanitizeuserfacingtext.test.ts b/src/agents/pi-embedded-helpers.sanitizeuserfacingtext.test.ts index 05236f98f5f..24e4bf822f3 100644 --- a/src/agents/pi-embedded-helpers.sanitizeuserfacingtext.test.ts +++ b/src/agents/pi-embedded-helpers.sanitizeuserfacingtext.test.ts @@ -362,6 +362,26 @@ describe("sanitizeUserFacingText", () => { ); }); + it("bounds large child completion results before injecting internal context", () => { + const internal = formatAgentInternalEventsForPrompt([ + { + type: "task_completion", + source: "subagent", + childSessionKey: "agent:main:subagent:test", + childSessionId: "sess_1", + announceType: "subagent task", + taskLabel: "Investigate issue", + status: "ok", + statusLabel: "completed successfully", + result: "x".repeat(6_000), + replyInstruction: "Reply to the user in your own words.", + }, + ]); + + expect(internal).toContain("[child result truncated: 2000 additional characters omitted]"); + expect(internal.length).toBeLessThan(5_000); + }); + it("does not strip inline delimiter mentions that are not standalone marker lines", () => { const input = `Note: ${INTERNAL_RUNTIME_CONTEXT_BEGIN} appears inline and should stay.`; expect(sanitizeUserFacingText(input)).toBe(input); diff --git a/src/agents/subagent-announce-delivery.test.ts b/src/agents/subagent-announce-delivery.test.ts index 06c22c95295..eac0682a64a 100644 --- a/src/agents/subagent-announce-delivery.test.ts +++ b/src/agents/subagent-announce-delivery.test.ts @@ -1237,6 +1237,7 @@ describe("deliverSubagentAnnouncement completion delivery", () => { childSessionKey: "agent:openclaw:subagent:child-123", childSessionId: "child-123", announceType: "subagent task", + taskLabel: "channel completion smoke", status: "ok", statusLabel: "completed successfully", result: "Raw child result that should stay internal.",