import type { AcpSessionUpdateTag } from "../../acp/runtime/types.js" ;
import type { OpenClawConfig } from "../../config/types.openclaw.js" ;
import { clampPositiveInteger, resolveEffectiveBlockStreamingConfig } from "./block-streaming.js" ;
const DEFAULT_ACP_STREAM_COALESCE_IDLE_MS = 350 ;
const DEFAULT_ACP_STREAM_MAX_CHUNK_CHARS = 1800 ;
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_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 ,
tool_call: false ,
tool_call_update: false ,
usage_update: false ,
available_commands_update: false ,
current_mode_update: false ,
config_option_update: false ,
session_info_update: false ,
plan: false ,
agent_thought_chunk: false ,
};
export type AcpDeliveryMode = "live" | "final_only" ;
export type AcpHiddenBoundarySeparator = "none" | "space" | "newline" | "paragraph" ;
export type AcpProjectionSettings = {
deliveryMode: AcpDeliveryMode;
hiddenBoundarySeparator: AcpHiddenBoundarySeparator;
repeatSuppression: boolean ;
maxOutputChars: number;
maxSessionUpdateChars: number;
tagVisibility: Partial<Record<AcpSessionUpdateTag, boolean >>;
};
function clampBoolean(value: unknown, fallback: boolean ): boolean {
return typeof value === "boolean" ? value : fallback;
}
function resolveAcpDeliveryMode(value: unknown): AcpDeliveryMode {
if (value === "live" || value === "final_only" ) {
return value;
}
return DEFAULT_ACP_DELIVERY_MODE;
}
function resolveAcpHiddenBoundarySeparator(
value: unknown,
fallback: AcpHiddenBoundarySeparator,
): AcpHiddenBoundarySeparator {
if (value === "none" || value === "space" || value === "newline" || value === "paragraph" ) {
return value;
}
return fallback;
}
function resolveAcpStreamCoalesceIdleMs(cfg: OpenClawConfig): number {
return clampPositiveInteger(
cfg.acp?.stream?.coalesceIdleMs,
DEFAULT_ACP_STREAM_COALESCE_IDLE_MS,
{
min: 0 ,
max: 5 _000 ,
},
);
}
function resolveAcpStreamMaxChunkChars(cfg: OpenClawConfig): number {
return clampPositiveInteger(cfg.acp?.stream?.maxChunkChars, DEFAULT_ACP_STREAM_MAX_CHUNK_CHARS, {
min: 50 ,
max: 4 _000 ,
});
}
export function resolveAcpProjectionSettings(cfg: OpenClawConfig): AcpProjectionSettings {
const stream = cfg.acp?.stream;
const deliveryMode = resolveAcpDeliveryMode(stream?.deliveryMode);
const hiddenBoundaryFallback: AcpHiddenBoundarySeparator =
deliveryMode === "live"
? DEFAULT_ACP_HIDDEN_BOUNDARY_SEPARATOR_LIVE
: DEFAULT_ACP_HIDDEN_BOUNDARY_SEPARATOR;
return {
deliveryMode,
hiddenBoundarySeparator: resolveAcpHiddenBoundarySeparator(
stream?.hiddenBoundarySeparator,
hiddenBoundaryFallback,
),
repeatSuppression: clampBoolean(stream?.repeatSuppression, DEFAULT_ACP_REPEAT_SUPPRESSION),
maxOutputChars: clampPositiveInteger(stream?.maxOutputChars, DEFAULT_ACP_MAX_OUTPUT_CHARS, {
min: 1 ,
max: 500 _000 ,
}),
maxSessionUpdateChars: clampPositiveInteger(
stream?.maxSessionUpdateChars,
DEFAULT_ACP_MAX_SESSION_UPDATE_CHARS,
{
min: 64 ,
max: 8 _000 ,
},
),
tagVisibility: stream?.tagVisibility ?? {},
};
}
export function resolveAcpStreamingConfig(params: {
cfg: OpenClawConfig;
provider?: string;
accountId?: string;
deliveryMode?: AcpDeliveryMode;
}) {
const resolved = resolveEffectiveBlockStreamingConfig({
cfg: params.cfg,
provider: params.provider,
accountId: params.accountId,
maxChunkChars: resolveAcpStreamMaxChunkChars(params.cfg),
coalesceIdleMs: resolveAcpStreamCoalesceIdleMs(params.cfg),
});
// In live mode, ACP text deltas should flush promptly and never be held
// behind large generic min-char thresholds.
if (params.deliveryMode === "live" ) {
return {
chunking: {
...resolved.chunking,
minChars: 1 ,
},
coalescing: {
...resolved.coalescing,
minChars: 1 ,
// ACP delta streams already carry spacing/newlines; preserve exact text.
joiner: "" ,
},
};
}
return resolved;
}
export function isAcpTagVisible(
settings: AcpProjectionSettings,
tag: AcpSessionUpdateTag | undefined,
): boolean {
if (!tag) {
return true ;
}
const override = settings.tagVisibility[tag];
if (typeof override === "boolean" ) {
return override;
}
if (Object.prototype.hasOwnProperty.call(ACP_TAG_VISIBILITY_DEFAULTS, tag)) {
return ACP_TAG_VISIBILITY_DEFAULTS[tag];
}
return true ;
}
Messung V0.5 in Prozent C=99 H=82 G=90
¤ Dauer der Verarbeitung: 0.9 Sekunden
(vorverarbeitet am 2026-06-05)
¤
*© Formatika GbR, Deutschland