@@ -470,7 +470,7 @@ export class GeminiChat {
470470
471471 private async makeApiCallAndProcessStream (
472472 modelConfigKey : ModelConfigKey ,
473- requestContents : Content [ ] ,
473+ requestContents : readonly Content [ ] ,
474474 prompt_id : string ,
475475 abortSignal : AbortSignal ,
476476 role : LlmRole ,
@@ -489,7 +489,7 @@ export class GeminiChat {
489489 let currentGenerateContentConfig : GenerateContentConfig =
490490 newAvailabilityConfig ;
491491 let lastConfig : GenerateContentConfig = currentGenerateContentConfig ;
492- let lastContentsToUse : Content [ ] = requestContents ;
492+ let lastContentsToUse : Content [ ] = [ ... requestContents ] ;
493493
494494 const getAvailabilityContext = createAvailabilityContextProvider (
495495 this . config ,
@@ -528,9 +528,9 @@ export class GeminiChat {
528528 abortSignal,
529529 } ;
530530
531- let contentsToUse = supportsModernFeatures ( modelToUse )
532- ? contentsForPreviewModel
533- : requestContents ;
531+ let contentsToUse : Content [ ] = supportsModernFeatures ( modelToUse )
532+ ? [ ... contentsForPreviewModel ]
533+ : [ ... requestContents ] ;
534534
535535 const hookSystem = this . config . getHookSystem ( ) ;
536536 if ( hookSystem ) {
@@ -687,16 +687,10 @@ export class GeminiChat {
687687 * @return History contents alternating between user and model for the entire
688688 * chat session.
689689 */
690- getHistory ( curated : boolean = false ) : Content [ ] {
690+ getHistory ( curated : boolean = false ) : readonly Content [ ] {
691691 const history = curated
692692 ? extractCuratedHistory ( this . history )
693693 : this . history ;
694- // Return a shallow copy of the array to prevent callers from mutating
695- // the internal history array (push/pop/splice). Content objects are
696- // shared references — callers MUST NOT mutate them in place.
697- // This replaces a prior structuredClone() which deep-copied the entire
698- // conversation on every call, causing O(n) memory pressure per turn
699- // that compounded into OOM crashes in long-running sessions.
700694 return [ ...history ] ;
701695 }
702696
@@ -714,8 +708,8 @@ export class GeminiChat {
714708 this . history . push ( content ) ;
715709 }
716710
717- setHistory ( history : Content [ ] ) : void {
718- this . history = history ;
711+ setHistory ( history : readonly Content [ ] ) : void {
712+ this . history = [ ... history ] ;
719713 this . lastPromptTokenCount = estimateTokenCountSync (
720714 this . history . flatMap ( ( c ) => c . parts || [ ] ) ,
721715 ) ;
@@ -742,7 +736,9 @@ export class GeminiChat {
742736 // To ensure our requests validate, the first function call in every model
743737 // turn within the active loop must have a `thoughtSignature` property.
744738 // If we do not do this, we will get back 400 errors from the API.
745- ensureActiveLoopHasThoughtSignatures ( requestContents : Content [ ] ) : Content [ ] {
739+ ensureActiveLoopHasThoughtSignatures (
740+ requestContents : readonly Content [ ] ,
741+ ) : readonly Content [ ] {
746742 // First, find the start of the active loop by finding the last user turn
747743 // with a text message, i.e. that is not a function response.
748744 let activeLoopStartIndex = - 1 ;
0 commit comments