feat: Add maxAnnounceChars parameter to sessions_spawn

- Add maxAnnounceChars parameter to sessions_spawn tool schema
- Pass parameter through subagent spawn flow to registry
- Apply truncation with marker when announce message exceeds limit
- Helps prevent message delivery failures on platforms with character limits (e.g., Telegram 4096 chars)

Fixes #46785
This commit is contained in:
Contributor 2026-03-15 11:21:53 +08:00
parent db20141993
commit 5e6204b677
5 changed files with 35 additions and 2 deletions

View File

@ -1170,6 +1170,7 @@ export async function runSubagentAnnounceFlow(params: {
wakeOnDescendantSettle?: boolean;
signal?: AbortSignal;
bestEffortDeliver?: boolean;
maxAnnounceChars?: number;
}): Promise<boolean> {
let didAnnounce = false;
const expectsCompletionMessage = params.expectsCompletionMessage === true;
@ -1395,6 +1396,16 @@ export async function runSubagentAnnounceFlow(params: {
startedAt: params.startedAt,
endedAt: params.endedAt,
});
// Apply maxAnnounceChars truncation if specified
const truncatedFindings =
typeof params.maxAnnounceChars === "number" &&
params.maxAnnounceChars >= 1 &&
findings.length > params.maxAnnounceChars
? findings.slice(0, params.maxAnnounceChars) +
"\n\n[truncated — full output in transcript]"
: findings;
const internalEvents: AgentInternalEvent[] = [
{
type: "task_completion",
@ -1405,7 +1416,7 @@ export async function runSubagentAnnounceFlow(params: {
taskLabel,
status: outcome.status,
statusLabel,
result: findings,
result: truncatedFindings,
statsLine,
replyInstruction,
},

View File

@ -565,6 +565,7 @@ function startSubagentAnnounceCleanupFlow(runId: string, entry: SubagentRunRecor
spawnMode: entry.spawnMode,
expectsCompletionMessage: entry.expectsCompletionMessage,
wakeOnDescendantSettle: entry.wakeOnDescendantSettle === true,
maxAnnounceChars: entry.maxAnnounceChars,
})
.then((didAnnounce) => {
finalizeAnnounceCleanup(didAnnounce);
@ -1171,6 +1172,7 @@ export function registerSubagentRun(params: {
runTimeoutSeconds?: number;
expectsCompletionMessage?: boolean;
spawnMode?: "run" | "session";
maxAnnounceChars?: number;
attachmentsDir?: string;
attachmentsRootDir?: string;
retainAttachmentsOnKeep?: boolean;
@ -1204,6 +1206,10 @@ export function registerSubagentRun(params: {
archiveAtMs,
cleanupHandled: false,
wakeOnDescendantSettle: undefined,
maxAnnounceChars:
typeof params.maxAnnounceChars === "number" && Number.isFinite(params.maxAnnounceChars)
? Math.max(1, Math.floor(params.maxAnnounceChars))
: undefined,
attachmentsDir: params.attachmentsDir,
attachmentsRootDir: params.attachmentsRootDir,
retainAttachmentsOnKeep: params.retainAttachmentsOnKeep,

View File

@ -55,4 +55,6 @@ export type SubagentRunRecord = {
attachmentsDir?: string;
attachmentsRootDir?: string;
retainAttachmentsOnKeep?: boolean;
/** Maximum character limit for announce message delivery (truncates with marker if exceeded). */
maxAnnounceChars?: number;
};

View File

@ -56,6 +56,7 @@ export type SpawnSubagentParams = {
cleanup?: "delete" | "keep";
sandbox?: SpawnSubagentSandboxMode;
expectsCompletionMessage?: boolean;
maxAnnounceChars?: number;
attachments?: Array<{
name: string;
content: string;
@ -708,6 +709,7 @@ export async function spawnSubagentDirect(
runTimeoutSeconds,
expectsCompletionMessage,
spawnMode,
maxAnnounceChars: params.maxAnnounceChars,
attachmentsDir: attachmentAbsDir,
attachmentsRootDir: attachmentRootDir,
retainAttachmentsOnKeep: retainOnSessionKeep,

View File

@ -63,6 +63,13 @@ const SessionsSpawnToolSchema = Type.Object({
mountPath: Type.Optional(Type.String()),
}),
),
maxAnnounceChars: Type.Optional(
Type.Number({
minimum: 1,
description:
"Maximum character limit for the sub-agent completion announce message. If exceeded, the message is truncated with a marker.",
}),
),
});
export function createSessionsSpawnTool(
@ -81,7 +88,7 @@ export function createSessionsSpawnTool(
label: "Sessions",
name: "sessions_spawn",
description:
'Spawn an isolated session (runtime="subagent" or runtime="acp"). mode="run" is one-shot and mode="session" is persistent/thread-bound. Subagents inherit the parent workspace directory automatically.',
'Spawn an isolated session (runtime="subagent" or runtime="acp"). mode="run" is one-shot and mode="session" is persistent/thread-bound. Subagents inherit the parent workspace directory automatically. Use maxAnnounceChars to limit completion message length for platforms with character limits (e.g., Telegram 4096 chars).',
parameters: SessionsSpawnToolSchema,
execute: async (_toolCallId, args) => {
const params = args as Record<string, unknown>;
@ -118,6 +125,10 @@ export function createSessionsSpawnTool(
? Math.max(0, Math.floor(timeoutSecondsCandidate))
: undefined;
const thread = params.thread === true;
const maxAnnounceCharsCandidate =
typeof params.maxAnnounceChars === "number" && Number.isFinite(params.maxAnnounceChars)
? Math.max(1, Math.floor(params.maxAnnounceChars))
: undefined;
const attachments = Array.isArray(params.attachments)
? (params.attachments as Array<{
name: string;
@ -186,6 +197,7 @@ export function createSessionsSpawnTool(
cleanup,
sandbox,
expectsCompletionMessage: true,
maxAnnounceChars: maxAnnounceCharsCandidate,
attachments,
attachMountPath:
params.attachAs && typeof params.attachAs === "object"