11import { hasOutboundReplyContent } from "openclaw/plugin-sdk/reply-payload" ;
22import { DEFAULT_HEARTBEAT_ACK_MAX_CHARS } from "../../auto-reply/heartbeat.js" ;
3+ import { getReplyPayloadMetadata } from "../../auto-reply/reply-payload.js" ;
34import type { ReplyPayload } from "../../auto-reply/reply-payload.js" ;
45import { normalizeOptionalString } from "../../shared/string-coerce.js" ;
56import { truncateUtf16Safe } from "../../utils.js" ;
@@ -151,6 +152,9 @@ export function pickSummaryFromPayloads(
151152 }
152153 }
153154 for ( let i = payloads . length - 1 ; i >= 0 ; i -- ) {
155+ if ( isNonTerminalToolErrorWarning ( payloads [ i ] ) ) {
156+ continue ;
157+ }
154158 const summary = pickSummaryFromOutput ( payloads [ i ] ?. text ) ;
155159 if ( summary ) {
156160 return summary ;
@@ -172,6 +176,9 @@ export function pickLastNonEmptyTextFromPayloads(
172176 }
173177 }
174178 for ( let i = payloads . length - 1 ; i >= 0 ; i -- ) {
179+ if ( isNonTerminalToolErrorWarning ( payloads [ i ] ) ) {
180+ continue ;
181+ }
175182 const clean = ( payloads [ i ] ?. text ?? "" ) . trim ( ) ;
176183 if ( clean ) {
177184 return clean ;
@@ -249,14 +256,26 @@ function isCronMessagePresentationWarning(text: string | undefined): boolean {
249256 ) ;
250257}
251258
259+ function isNonTerminalToolErrorWarning ( payload : object | undefined ) : boolean {
260+ return Boolean ( payload && getReplyPayloadMetadata ( payload ) ?. nonTerminalToolErrorWarning ) ;
261+ }
262+
263+ function isSuccessfulCronPayload ( payload : DeliveryPayload | undefined ) : boolean {
264+ return (
265+ payload ?. isError !== true &&
266+ ( isDeliverablePayload ( payload ) || payloadHasStructuredDeliveryContent ( payload ) )
267+ ) ;
268+ }
269+
252270export function resolveCronPayloadOutcome ( params : {
253271 payloads : DeliveryPayload [ ] ;
254272 runLevelError ?: unknown ;
255273 failureSignal ?: CronFailureSignal | undefined ;
256274 finalAssistantVisibleText ?: string | undefined ;
257275 preferFinalAssistantVisibleText ?: boolean ;
258276} ) : CronPayloadOutcome {
259- const firstText = params . payloads [ 0 ] ?. text ?? "" ;
277+ const firstText =
278+ params . payloads . find ( ( payload ) => ! isNonTerminalToolErrorWarning ( payload ) ) ?. text ?? "" ;
260279 const fallbackSummary =
261280 pickSummaryFromPayloads ( params . payloads ) ?? pickSummaryFromOutput ( firstText ) ;
262281 const fallbackOutputText = pickLastNonEmptyTextFromPayloads ( params . payloads ) ;
@@ -277,23 +296,33 @@ export function resolveCronPayloadOutcome(params: {
277296 const hasSuccessfulPayloadAfterLastError =
278297 ! params . runLevelError &&
279298 lastErrorPayloadIndex >= 0 &&
280- params . payloads
281- . slice ( lastErrorPayloadIndex + 1 )
282- . some ( ( payload ) => payload ?. isError !== true && Boolean ( payload ?. text ?. trim ( ) ) ) ;
299+ params . payloads . slice ( lastErrorPayloadIndex + 1 ) . some ( isSuccessfulCronPayload ) ;
283300 const hasSuccessfulPayloadBeforeLastError =
284301 ! params . runLevelError &&
285302 lastErrorPayloadIndex > 0 &&
286- params . payloads
287- . slice ( 0 , lastErrorPayloadIndex )
288- . some ( ( payload ) => payload ?. isError !== true && Boolean ( payload ?. text ?. trim ( ) ) ) ;
303+ params . payloads . slice ( 0 , lastErrorPayloadIndex ) . some ( isSuccessfulCronPayload ) ;
304+ const lastErrorPayload =
305+ lastErrorPayloadIndex >= 0 ? params . payloads [ lastErrorPayloadIndex ] : undefined ;
306+ const hasRecoveringTerminalOutput =
307+ normalizedFinalAssistantVisibleText !== undefined ||
308+ hasSuccessfulPayloadAfterLastError ||
309+ hasSuccessfulPayloadBeforeLastError ;
310+ const hasNonTerminalToolErrorWarning =
311+ ! params . runLevelError &&
312+ params . failureSignal ?. fatalForCron !== true &&
313+ hasRecoveringTerminalOutput &&
314+ isNonTerminalToolErrorWarning ( lastErrorPayload ) ;
289315 const hasPendingPresentationWarning =
290316 ! params . runLevelError &&
291317 params . failureSignal ?. fatalForCron !== true &&
292318 lastErrorPayloadIndex >= 0 &&
293319 isCronMessagePresentationWarning ( lastErrorPayloadText ) &&
294320 ( normalizedFinalAssistantVisibleText !== undefined || hasSuccessfulPayloadBeforeLastError ) ;
295321 const hasFatalStructuredErrorPayload =
296- hasErrorPayload && ! hasSuccessfulPayloadAfterLastError && ! hasPendingPresentationWarning ;
322+ hasErrorPayload &&
323+ ! hasSuccessfulPayloadAfterLastError &&
324+ ! hasPendingPresentationWarning &&
325+ ! hasNonTerminalToolErrorWarning ;
297326 const hasStructuredDeliveryPayloads = selectedDeliveryPayloads . some ( ( payload ) =>
298327 payloadHasStructuredDeliveryContent ( payload ) ,
299328 ) ;
@@ -324,10 +353,13 @@ export function resolveCronPayloadOutcome(params: {
324353 { field : "synthesizedText" , text : synthesizedText } ,
325354 { field : "fallbackSummary" , text : fallbackSummary } ,
326355 { field : "fallbackOutputText" , text : fallbackOutputText } ,
327- ...params . payloads . map ( ( payload , index ) => ( {
328- field : `payloads[${ index } ].text` ,
329- text : payload ?. text ,
330- } ) ) ,
356+ ...params . payloads
357+ . map ( ( payload , index ) => ( { payload, index } ) )
358+ . filter ( ( { payload } ) => ! isNonTerminalToolErrorWarning ( payload ) )
359+ . map ( ( { payload, index } ) => ( {
360+ field : `payloads[${ index } ].text` ,
361+ text : payload ?. text ,
362+ } ) ) ,
331363 ] ) ;
332364 const failureSignal = normalizeCronFailureSignal ( params . failureSignal ) ;
333365 const runLevelError = formatCronRunLevelError ( params . runLevelError ) ;
0 commit comments