@@ -266,7 +266,7 @@ export class WindsurfClient {
266266 * @param {object } opts - { onChunk, onEnd, onError }
267267 */
268268 async cascadeChat ( messages , modelEnum , modelUid , opts = { } ) {
269- const { onChunk, onEnd, onError, signal, reuseEntry, toolPreamble } = opts ;
269+ let { onChunk, onEnd, onError, signal, reuseEntry, toolPreamble } = opts ;
270270 const aborted = ( ) => signal ?. aborted ;
271271 const inputChars = messages . reduce ( ( n , m ) => n + contentToString ( m ?. content ) . length , 0 ) ;
272272
@@ -311,6 +311,42 @@ export class WindsurfClient {
311311 cascadeId = await openCascade ( ) ;
312312 }
313313
314+ // A resumed cascade already contains every prior turn in its trajectory.
315+ // If we poll from step offset 0 again, the old planner-response steps are
316+ // replayed as fresh output and both text and usage grow cumulatively
317+ // across turns (`alpha` -> `alphabeta` -> ...). Store absolute offsets in
318+ // the conversation pool and reuse them here; fall back to a one-shot
319+ // snapshot so entries created before this fix still resume safely.
320+ let stepOffset = Number . isInteger ( reuseEntry ?. stepOffset ) && reuseEntry . stepOffset >= 0
321+ ? reuseEntry . stepOffset
322+ : 0 ;
323+ let generatorOffset = Number . isInteger ( reuseEntry ?. generatorOffset ) && reuseEntry . generatorOffset >= 0
324+ ? reuseEntry . generatorOffset
325+ : 0 ;
326+ if ( reuseEntry ?. cascadeId && ( ! Number . isInteger ( reuseEntry ?. stepOffset ) || ! Number . isInteger ( reuseEntry ?. generatorOffset ) ) ) {
327+ try {
328+ if ( ! Number . isInteger ( reuseEntry ?. stepOffset ) ) {
329+ const resumeStepsResp = await grpcUnary (
330+ this . port , this . csrfToken ,
331+ `${ LS_SERVICE } /GetCascadeTrajectorySteps` ,
332+ grpcFrame ( buildGetTrajectoryStepsRequest ( cascadeId , 0 ) )
333+ ) ;
334+ stepOffset = parseTrajectorySteps ( resumeStepsResp ) . length ;
335+ }
336+ if ( ! Number . isInteger ( reuseEntry ?. generatorOffset ) ) {
337+ const resumeMetaResp = await grpcUnary (
338+ this . port , this . csrfToken ,
339+ `${ LS_SERVICE } /GetCascadeTrajectoryGeneratorMetadata` ,
340+ grpcFrame ( buildGetGeneratorMetadataRequest ( cascadeId , 0 ) ) ,
341+ 5000
342+ ) ;
343+ generatorOffset = parseGeneratorMetadata ( resumeMetaResp ) ?. entryCount || 0 ;
344+ }
345+ } catch ( e ) {
346+ log . warn ( `Cascade resume snapshot failed: ${ e . message } ` ) ;
347+ }
348+ }
349+
314350 let text ;
315351 let images = [ ] ;
316352 const systemMsgs = messages . filter ( m => m . role === 'system' ) ;
@@ -443,7 +479,7 @@ export class WindsurfClient {
443479 pollCount ++ ;
444480
445481 // Get steps
446- const stepsProto = buildGetTrajectoryStepsRequest ( cascadeId , 0 ) ;
482+ const stepsProto = buildGetTrajectoryStepsRequest ( cascadeId , stepOffset ) ;
447483 const stepsResp = await grpcUnary (
448484 this . port , this . csrfToken , `${ LS_SERVICE } /GetCascadeTrajectorySteps` , grpcFrame ( stepsProto )
449485 ) ;
@@ -615,6 +651,7 @@ export class WindsurfClient {
615651 this . port , this . csrfToken , `${ LS_SERVICE } /GetCascadeTrajectorySteps` , grpcFrame ( stepsProto )
616652 ) ;
617653 const finalSteps = parseTrajectorySteps ( finalResp ) ;
654+ lastStepCount = finalSteps . length ;
618655 for ( let i = 0 ; i < finalSteps . length ; i ++ ) {
619656 const step = finalSteps [ i ] ;
620657 const responseText = step . responseText || '' ;
@@ -662,7 +699,7 @@ export class WindsurfClient {
662699 polls : pollCount ,
663700 textLen : totalYielded ,
664701 thinkingLen : totalThinking ,
665- stepCount : Math . max ( yieldedByStep . size , thinkingByStep . size , lastStepCount ) ,
702+ stepCount : stepOffset + Math . max ( yieldedByStep . size , thinkingByStep . size , lastStepCount ) ,
666703 toolCalls : seenToolCallIds . size ,
667704 sawActive,
668705 sawText,
@@ -686,7 +723,7 @@ export class WindsurfClient {
686723 // itself is already formed.
687724 let serverUsage = null ;
688725 try {
689- const metaReq = buildGetGeneratorMetadataRequest ( cascadeId , 0 ) ;
726+ const metaReq = buildGetGeneratorMetadataRequest ( cascadeId , generatorOffset ) ;
690727 const metaResp = await grpcUnary (
691728 this . port , this . csrfToken ,
692729 `${ LS_SERVICE } /GetCascadeTrajectoryGeneratorMetadata` ,
@@ -722,6 +759,10 @@ export class WindsurfClient {
722759 // that iterate over it keep working.
723760 chunks . cascadeId = cascadeId ;
724761 chunks . sessionId = sessionId ;
762+ chunks . stepOffset = stepOffset + Math . max ( yieldedByStep . size , thinkingByStep . size , lastStepCount ) ;
763+ chunks . generatorOffset = serverUsage ?. entryCount != null
764+ ? generatorOffset + serverUsage . entryCount
765+ : null ;
725766 chunks . toolCalls = toolCalls ;
726767 chunks . usage = serverUsage ;
727768 if ( serverUsage ) {
0 commit comments