ACP: rename stream char limits to output/sessionUpdate

This commit is contained in:
Onur
2026-03-01 18:11:20 +01:00
committed by Onur Solmaz
parent 4664d13857
commit 8292401719
8 changed files with 35 additions and 43 deletions

View File

@@ -450,7 +450,7 @@ describe("createAcpReplyProjector", () => {
enabled: true,
stream: {
deliveryMode: "live",
maxToolSummaryChars: 48,
maxSessionUpdateChars: 48,
tagVisibility: {
tool_call: true,
tool_call_update: true,
@@ -649,7 +649,7 @@ describe("createAcpReplyProjector", () => {
coalesceIdleMs: 0,
maxChunkChars: 256,
deliveryMode: "live",
maxTurnChars: 5,
maxOutputChars: 5,
},
},
}),

View File

@@ -194,7 +194,7 @@ export function createAcpReplyProjector(params: {
const chunker = new EmbeddedBlockChunker(streaming.chunking);
const liveIdleFlushMs = Math.max(streaming.coalescing.idleMs, ACP_LIVE_IDLE_FLUSH_FLOOR_MS);
let emittedTurnChars = 0;
let emittedOutputChars = 0;
let truncationNoticeEmitted = false;
let lastStatusHash: string | undefined;
let lastToolHash: string | undefined;
@@ -262,7 +262,7 @@ export function createAcpReplyProjector(params: {
clearLiveIdleTimer();
blockReplyPipeline.stop();
blockReplyPipeline = createTurnBlockReplyPipeline();
emittedTurnChars = 0;
emittedOutputChars = 0;
truncationNoticeEmitted = false;
lastStatusHash = undefined;
lastToolHash = undefined;
@@ -301,7 +301,7 @@ export function createAcpReplyProjector(params: {
if (!params.shouldSendToolSummaries) {
return;
}
const bounded = truncateText(text.trim(), settings.maxStatusChars);
const bounded = truncateText(text.trim(), settings.maxSessionUpdateChars);
if (!bounded) {
return;
}
@@ -332,7 +332,7 @@ export function createAcpReplyProjector(params: {
}
const renderedToolSummary = renderToolSummaryText(event);
const toolSummary = truncateText(renderedToolSummary, settings.maxToolSummaryChars);
const toolSummary = truncateText(renderedToolSummary, settings.maxSessionUpdateChars);
const hash = hashText(renderedToolSummary);
const toolCallId = event.toolCallId?.trim() || undefined;
const status = normalizeToolStatus(event.status);
@@ -424,14 +424,14 @@ export function createAcpReplyProjector(params: {
text = `${resolveHiddenBoundarySeparatorText(settings.hiddenBoundarySeparator)}${text}`;
}
pendingHiddenBoundary = false;
if (emittedTurnChars >= settings.maxTurnChars) {
if (emittedOutputChars >= settings.maxOutputChars) {
await emitTruncationNotice();
return;
}
const remaining = settings.maxTurnChars - emittedTurnChars;
const remaining = settings.maxOutputChars - emittedOutputChars;
const accepted = remaining < text.length ? text.slice(0, remaining) : text;
if (accepted.length > 0) {
emittedTurnChars += accepted.length;
emittedOutputChars += accepted.length;
lastVisibleOutputTail = accepted.slice(-1);
if (settings.deliveryMode === "live") {
liveBufferText += accepted;

View File

@@ -12,7 +12,8 @@ describe("acp stream settings", () => {
expect(settings.deliveryMode).toBe("final_only");
expect(settings.hiddenBoundarySeparator).toBe("paragraph");
expect(settings.repeatSuppression).toBe(true);
expect(settings.maxTurnChars).toBe(24_000);
expect(settings.maxOutputChars).toBe(24_000);
expect(settings.maxSessionUpdateChars).toBe(320);
});
it("applies explicit stream overrides", () => {
@@ -24,7 +25,8 @@ describe("acp stream settings", () => {
deliveryMode: "final_only",
hiddenBoundarySeparator: "space",
repeatSuppression: false,
maxTurnChars: 500,
maxOutputChars: 500,
maxSessionUpdateChars: 123,
tagVisibility: {
usage_update: true,
},
@@ -35,7 +37,8 @@ describe("acp stream settings", () => {
expect(settings.deliveryMode).toBe("final_only");
expect(settings.hiddenBoundarySeparator).toBe("space");
expect(settings.repeatSuppression).toBe(false);
expect(settings.maxTurnChars).toBe(500);
expect(settings.maxOutputChars).toBe(500);
expect(settings.maxSessionUpdateChars).toBe(123);
expect(settings.tagVisibility.usage_update).toBe(true);
});

View File

@@ -8,9 +8,8 @@ const DEFAULT_ACP_REPEAT_SUPPRESSION = true;
const DEFAULT_ACP_DELIVERY_MODE = "final_only";
const DEFAULT_ACP_HIDDEN_BOUNDARY_SEPARATOR = "paragraph";
const DEFAULT_ACP_HIDDEN_BOUNDARY_SEPARATOR_LIVE = "space";
const DEFAULT_ACP_MAX_TURN_CHARS = 24_000;
const DEFAULT_ACP_MAX_TOOL_SUMMARY_CHARS = 320;
const DEFAULT_ACP_MAX_STATUS_CHARS = 320;
const DEFAULT_ACP_MAX_OUTPUT_CHARS = 24_000;
const DEFAULT_ACP_MAX_SESSION_UPDATE_CHARS = 320;
export const ACP_TAG_VISIBILITY_DEFAULTS: Record<AcpSessionUpdateTag, boolean> = {
agent_message_chunk: true,
@@ -32,9 +31,8 @@ export type AcpProjectionSettings = {
deliveryMode: AcpDeliveryMode;
hiddenBoundarySeparator: AcpHiddenBoundarySeparator;
repeatSuppression: boolean;
maxTurnChars: number;
maxToolSummaryChars: number;
maxStatusChars: number;
maxOutputChars: number;
maxSessionUpdateChars: number;
tagVisibility: Partial<Record<AcpSessionUpdateTag, boolean>>;
};
@@ -109,22 +107,18 @@ export function resolveAcpProjectionSettings(cfg: OpenClawConfig): AcpProjection
hiddenBoundaryFallback,
),
repeatSuppression: clampBoolean(stream?.repeatSuppression, DEFAULT_ACP_REPEAT_SUPPRESSION),
maxTurnChars: clampPositiveInteger(stream?.maxTurnChars, DEFAULT_ACP_MAX_TURN_CHARS, {
maxOutputChars: clampPositiveInteger(stream?.maxOutputChars, DEFAULT_ACP_MAX_OUTPUT_CHARS, {
min: 1,
max: 500_000,
}),
maxToolSummaryChars: clampPositiveInteger(
stream?.maxToolSummaryChars,
DEFAULT_ACP_MAX_TOOL_SUMMARY_CHARS,
maxSessionUpdateChars: clampPositiveInteger(
stream?.maxSessionUpdateChars,
DEFAULT_ACP_MAX_SESSION_UPDATE_CHARS,
{
min: 64,
max: 8_000,
},
),
maxStatusChars: clampPositiveInteger(stream?.maxStatusChars, DEFAULT_ACP_MAX_STATUS_CHARS, {
min: 64,
max: 8_000,
}),
tagVisibility: stream?.tagVisibility ?? {},
};
}

View File

@@ -178,11 +178,10 @@ export const FIELD_HELP: Record<string, string> = {
"ACP delivery style: live streams projected output incrementally, final_only buffers all projected ACP output until terminal turn events.",
"acp.stream.hiddenBoundarySeparator":
"Separator inserted before next visible assistant text when hidden ACP tool lifecycle events occurred (none|space|newline|paragraph). Default: paragraph.",
"acp.stream.maxTurnChars":
"Maximum assistant text characters projected per ACP turn before truncation notice is emitted.",
"acp.stream.maxToolSummaryChars":
"Maximum characters for projected ACP tool lifecycle/progress summary lines.",
"acp.stream.maxStatusChars": "Maximum characters for projected ACP status/meta lines.",
"acp.stream.maxOutputChars":
"Maximum assistant output characters projected per ACP turn before truncation notice is emitted.",
"acp.stream.maxSessionUpdateChars":
"Maximum characters for projected ACP session/update lines (tool/status updates).",
"acp.stream.tagVisibility":
"Per-sessionUpdate visibility overrides for ACP projection (for example usage_update, available_commands_update).",
"acp.runtime.ttlMinutes":

View File

@@ -372,9 +372,8 @@ export const FIELD_LABELS: Record<string, string> = {
"acp.stream.repeatSuppression": "ACP Stream Repeat Suppression",
"acp.stream.deliveryMode": "ACP Stream Delivery Mode",
"acp.stream.hiddenBoundarySeparator": "ACP Stream Hidden Boundary Separator",
"acp.stream.maxTurnChars": "ACP Stream Max Turn Chars",
"acp.stream.maxToolSummaryChars": "ACP Stream Max Tool Summary Chars",
"acp.stream.maxStatusChars": "ACP Stream Max Status Chars",
"acp.stream.maxOutputChars": "ACP Stream Max Output Chars",
"acp.stream.maxSessionUpdateChars": "ACP Stream Max Session Update Chars",
"acp.stream.tagVisibility": "ACP Stream Tag Visibility",
"acp.runtime.ttlMinutes": "ACP Runtime TTL (minutes)",
"acp.runtime.installCommand": "ACP Runtime Install Command",

View File

@@ -16,12 +16,10 @@ export type AcpStreamConfig = {
deliveryMode?: "live" | "final_only";
/** Separator inserted before visible text when hidden tool events occurred. */
hiddenBoundarySeparator?: "none" | "space" | "newline" | "paragraph";
/** Maximum assistant text characters forwarded per turn. */
maxTurnChars?: number;
/** Maximum visible characters for tool summary/meta lines. */
maxToolSummaryChars?: number;
/** Maximum visible characters for status lines. */
maxStatusChars?: number;
/** Maximum assistant output characters forwarded per turn. */
maxOutputChars?: number;
/** Maximum visible characters for projected session/update lines. */
maxSessionUpdateChars?: number;
/**
* Per-sessionUpdate visibility overrides.
* Keys not listed here fall back to OpenClaw defaults.

View File

@@ -349,9 +349,8 @@ export const OpenClawSchema = z
z.literal("paragraph"),
])
.optional(),
maxTurnChars: z.number().int().positive().optional(),
maxToolSummaryChars: z.number().int().positive().optional(),
maxStatusChars: z.number().int().positive().optional(),
maxOutputChars: z.number().int().positive().optional(),
maxSessionUpdateChars: z.number().int().positive().optional(),
tagVisibility: z.record(z.string(), z.boolean()).optional(),
})
.strict()