@@ -301,6 +301,8 @@ export type ChannelProgressDraftLine = {
301301 prefix ?: boolean ;
302302} ;
303303
304+ const progressDraftLineCorrelationKeys = new WeakMap < ChannelProgressDraftLine , string > ( ) ;
305+
304306function compactStrings ( values : readonly ( string | undefined | null ) [ ] ) : string [ ] {
305307 return values . map ( ( value ) => value ?. replace ( / \s + / g, " " ) . trim ( ) ) . filter ( Boolean ) as string [ ] ;
306308}
@@ -322,6 +324,7 @@ function buildNamedProgressLine(
322324 metas : readonly ( string | undefined | null ) [ ] | undefined ,
323325 options ?: ChannelProgressLineOptions ,
324326 fields ?: {
327+ correlationKey ?: string ;
325328 id ?: string ;
326329 status ?: string ;
327330 } ,
@@ -340,7 +343,7 @@ function buildNamedProgressLine(
340343 const detail = text . startsWith ( `${ prefix } : ` )
341344 ? text . slice ( prefix . length + 2 ) . trim ( )
342345 : compactCommandPrefix ;
343- return {
346+ const line = {
344347 ...( fields ?. id ? { id : fields . id } : { } ) ,
345348 kind,
346349 text,
@@ -350,6 +353,18 @@ function buildNamedProgressLine(
350353 ...( fields ?. status ? { status : fields . status } : { } ) ,
351354 toolName : display . name ,
352355 } ;
356+ setProgressDraftLineCorrelationKey ( line , fields ?. correlationKey ) ;
357+ return line ;
358+ }
359+
360+ function setProgressDraftLineCorrelationKey (
361+ line : ChannelProgressDraftLine ,
362+ correlationKey : string | undefined ,
363+ ) : void {
364+ const normalized = correlationKey ?. trim ( ) ;
365+ if ( normalized ) {
366+ progressDraftLineCorrelationKeys . set ( line , normalized ) ;
367+ }
353368}
354369
355370function itemKindToToolName ( kind : string | undefined ) : string | undefined {
@@ -381,34 +396,24 @@ function resolveProgressDraftLineId(
381396 input : {
382397 itemId ?: string ;
383398 toolCallId ?: string ;
384- itemKind ?: string ;
385- name ?: string ;
386399 } ,
387400 params ?: {
388- commandLike ?: boolean ;
389401 useToolCallIdFallback ?: boolean ;
390402 } ,
391403) : string | undefined {
392404 const itemId = input . itemId ?. trim ( ) ;
393405 const toolCallId = input . toolCallId ?. trim ( ) ;
394- const commandLike =
395- params ?. commandLike ??
396- ( normalizeOptionalLowercaseString ( input . itemKind ) === "command" ||
397- isCommandToolName ( input . name ) ) ;
398- if ( commandLike ) {
399- if ( itemId && / ^ c o m m a n d (?: : | - ) / i. test ( itemId ) ) {
400- return itemId ;
401- }
402- if ( toolCallId ) {
403- return `command:${ toolCallId } ` ;
404- }
405- }
406406 if ( itemId ) {
407407 return itemId ;
408408 }
409409 return params ?. useToolCallIdFallback === true ? toolCallId : undefined ;
410410}
411411
412+ function resolveCommandProgressCorrelationKey ( input : { toolCallId ?: string } ) : string | undefined {
413+ const toolCallId = input . toolCallId ?. trim ( ) ;
414+ return toolCallId ? `command:${ toolCallId } ` : undefined ;
415+ }
416+
412417function isEmptyReasoningProgressItem (
413418 input : Extract < ChannelProgressDraftLineInput , { event : "item" } > ,
414419 meta : string | undefined ,
@@ -493,7 +498,12 @@ export function buildChannelProgressDraftLine(
493498 input . phase && ! input . name ? input . phase : undefined ,
494499 ] ,
495500 options ,
496- { id : resolveProgressDraftLineId ( input , { useToolCallIdFallback : true } ) } ,
501+ {
502+ correlationKey : isCommandToolName ( input . name )
503+ ? resolveCommandProgressCorrelationKey ( input )
504+ : undefined ,
505+ id : resolveProgressDraftLineId ( input , { useToolCallIdFallback : true } ) ,
506+ } ,
497507 ) ;
498508 }
499509 case "item" : {
@@ -509,23 +519,30 @@ export function buildChannelProgressDraftLine(
509519 }
510520 if ( name ) {
511521 return buildNamedProgressLine ( input . event , name , [ meta ] , options , {
512- id : resolveProgressDraftLineId ( input , {
513- commandLike : isCommandProgressItem ( input ) ,
514- } ) ,
522+ correlationKey : isCommandProgressItem ( input )
523+ ? resolveCommandProgressCorrelationKey ( input )
524+ : undefined ,
525+ id : resolveProgressDraftLineId ( input ) ,
515526 status : input . status ,
516527 } ) ;
517528 }
518529 const text = compactStrings ( [ meta , input . title ] ) . at ( 0 ) ;
519- const id = resolveProgressDraftLineId ( input , { commandLike : isCommandProgressItem ( input ) } ) ;
520- return text
521- ? {
522- ...( id ? { id } : { } ) ,
523- kind : input . event ,
524- text,
525- label : input . title ?. trim ( ) || input . itemKind ?. trim ( ) || "Update" ,
526- ...( input . status ? { status : input . status } : { } ) ,
527- }
530+ const id = resolveProgressDraftLineId ( input ) ;
531+ const correlationKey = isCommandProgressItem ( input )
532+ ? resolveCommandProgressCorrelationKey ( input )
528533 : undefined ;
534+ if ( ! text ) {
535+ return undefined ;
536+ }
537+ const line = {
538+ ...( id ? { id } : { } ) ,
539+ kind : input . event ,
540+ text,
541+ label : input . title ?. trim ( ) || input . itemKind ?. trim ( ) || "Update" ,
542+ ...( input . status ? { status : input . status } : { } ) ,
543+ } ;
544+ setProgressDraftLineCorrelationKey ( line , correlationKey ) ;
545+ return line ;
529546 }
530547 case "plan" : {
531548 if ( input . phase !== undefined && input . phase !== "update" ) {
@@ -566,10 +583,8 @@ export function buildChannelProgressDraftLine(
566583 [ status , input . title ] ,
567584 options ,
568585 {
569- id : resolveProgressDraftLineId ( input , {
570- commandLike : true ,
571- useToolCallIdFallback : true ,
572- } ) ,
586+ correlationKey : resolveCommandProgressCorrelationKey ( input ) ,
587+ id : resolveProgressDraftLineId ( input , { useToolCallIdFallback : true } ) ,
573588 status,
574589 } ,
575590 ) ;
@@ -1067,10 +1082,10 @@ export function mergeChannelProgressDraftLine<TLine extends string | ChannelProg
10671082 return lines ;
10681083 }
10691084 const maxLines = Math . max ( 1 , params . maxLines ) ;
1070- const lineId = typeof line === "object" ? line . id ?. trim ( ) : undefined ;
1071- if ( lineId ) {
1072- const existingIndex = lines . findIndex (
1073- ( entry ) => typeof entry === "object" && entry . id ?. trim ( ) === lineId ,
1085+ const lineKeys = resolveProgressDraftLineMergeKeys ( line ) ;
1086+ if ( lineKeys . length > 0 ) {
1087+ const existingIndex = lines . findIndex ( ( entry ) =>
1088+ resolveProgressDraftLineMergeKeys ( entry ) . some ( ( entryKey ) => lineKeys . includes ( entryKey ) ) ,
10741089 ) ;
10751090 if ( existingIndex >= 0 ) {
10761091 if ( normalizeChannelProgressDraftLineIdentity ( lines [ existingIndex ] ) === normalized ) {
@@ -1088,6 +1103,16 @@ export function mergeChannelProgressDraftLine<TLine extends string | ChannelProg
10881103 return [ ...lines , line ] . slice ( - maxLines ) ;
10891104}
10901105
1106+ function resolveProgressDraftLineMergeKeys ( line : string | ChannelProgressDraftLine ) : string [ ] {
1107+ if ( typeof line !== "object" ) {
1108+ return [ ] ;
1109+ }
1110+ const keys = [ progressDraftLineCorrelationKeys . get ( line ) , line . id ]
1111+ . map ( ( key ) => key ?. trim ( ) )
1112+ . filter ( ( key ) : key is string => Boolean ( key ) ) ;
1113+ return [ ...new Set ( keys ) ] ;
1114+ }
1115+
10911116export function formatChannelProgressDraftText ( params : {
10921117 /** Channel streaming config source for progress label and bounds. */
10931118 entry ?: StreamingCompatEntry | null ;
0 commit comments