@@ -191,6 +191,7 @@ export type SessionTranscriptUsageSnapshot = {
191191 promptTokens ?: number ;
192192 outputTokens ?: number ;
193193 trailingBytesTokens ?: number ;
194+ hasPostUsageCompactionMarker ?: boolean ;
194195} ;
195196
196197// Keep a generous near-threshold window so large assistant outputs still trigger
@@ -220,6 +221,58 @@ function parseUsageFromTranscriptLine(line: string): ReturnType<typeof normalize
220221 return undefined ;
221222}
222223
224+ function collectTranscriptText ( value : unknown ) : string {
225+ if ( typeof value === "string" ) {
226+ return value ;
227+ }
228+ if ( ! Array . isArray ( value ) ) {
229+ return "" ;
230+ }
231+ return value
232+ . map ( ( item ) => {
233+ if ( ! item || typeof item !== "object" || Array . isArray ( item ) ) {
234+ return "" ;
235+ }
236+ const text = ( item as { text ?: unknown } ) . text ;
237+ return typeof text === "string" ? text : "" ;
238+ } )
239+ . filter ( Boolean )
240+ . join ( "\n" ) ;
241+ }
242+
243+ function transcriptLineHasPostUsageCompactionMarker ( line : string ) : boolean {
244+ const trimmed = line . trim ( ) ;
245+ if ( ! trimmed ) {
246+ return false ;
247+ }
248+ try {
249+ const parsed = JSON . parse ( trimmed ) as {
250+ type ?: unknown ;
251+ message ?: { content ?: unknown } ;
252+ payload ?: { type ?: unknown ; text ?: unknown } ;
253+ } ;
254+ if ( parsed . type === "compaction" || parsed . type === "session.compacted" ) {
255+ return true ;
256+ }
257+ const payloadType = parsed . payload ?. type ;
258+ if ( payloadType === "compaction" || payloadType === "session.compacted" ) {
259+ return true ;
260+ }
261+ const text = [
262+ collectTranscriptText ( parsed . message ?. content ) ,
263+ collectTranscriptText ( parsed . payload ?. text ) ,
264+ ]
265+ . filter ( Boolean )
266+ . join ( "\n" ) ;
267+ return (
268+ text . includes ( "[Post-compaction context refresh]" ) ||
269+ text . includes ( "Session was just compacted." )
270+ ) ;
271+ } catch {
272+ return false ;
273+ }
274+ }
275+
223276function resolveSessionLogPath (
224277 sessionId ?: string ,
225278 sessionEntry ?: SessionEntry ,
@@ -257,6 +310,7 @@ function deriveTranscriptUsageSnapshot(
257310 | {
258311 usage : ReturnType < typeof normalizeUsage > | undefined ;
259312 trailingBytes ?: number ;
313+ hasPostUsageCompactionMarker ?: boolean ;
260314 }
261315 | undefined ,
262316) : SessionTranscriptUsageSnapshot | undefined {
@@ -276,6 +330,7 @@ function deriveTranscriptUsageSnapshot(
276330 return {
277331 promptTokens,
278332 outputTokens,
333+ hasPostUsageCompactionMarker : snapshot . hasPostUsageCompactionMarker === true ? true : undefined ,
279334 trailingBytesTokens :
280335 typeof snapshot . trailingBytes === "number" &&
281336 Number . isFinite ( snapshot . trailingBytes ) &&
@@ -360,6 +415,7 @@ async function readLastNonzeroUsageFromSessionLog(logPath: string) {
360415 const stat = await handle . stat ( ) ;
361416 let position = stat . size ;
362417 let leadingPartial = "" ;
418+ let postUsageCompactionMarkerSeen = false ;
363419 while ( position > 0 ) {
364420 const chunkSize = Math . min ( TRANSCRIPT_TAIL_CHUNK_BYTES , position ) ;
365421 const start = position - chunkSize ;
@@ -384,16 +440,23 @@ async function readLastNonzeroUsageFromSessionLog(logPath: string) {
384440 return {
385441 usage,
386442 trailingBytes : suffixBytesOutsideCombined + trailingBytesInChunk ,
443+ hasPostUsageCompactionMarker :
444+ postUsageCompactionMarkerSeen ||
445+ trailingLines . some ( transcriptLineHasPostUsageCompactionMarker ) ,
387446 } ;
388447 }
389448 }
449+ if ( ! postUsageCompactionMarkerSeen ) {
450+ postUsageCompactionMarkerSeen = lines . some ( transcriptLineHasPostUsageCompactionMarker ) ;
451+ }
390452 position = start ;
391453 }
392454 const usage = parseUsageFromTranscriptLine ( leadingPartial ) ;
393455 return usage
394456 ? {
395457 usage,
396458 trailingBytes : Math . max ( 0 , stat . size - Buffer . byteLength ( leadingPartial , "utf8" ) ) ,
459+ hasPostUsageCompactionMarker : postUsageCompactionMarkerSeen ,
397460 }
398461 : undefined ;
399462 } finally {
@@ -461,6 +524,7 @@ async function estimatePromptTokensFromSessionTranscript(params: {
461524 const outputTokens = snapshot . usage ?. outputTokens ;
462525 const rawUsagePromptTokens = Math . ceil ( promptTokens ) ;
463526 const boundedUsagePromptTokens =
527+ snapshot . usage ?. hasPostUsageCompactionMarker === true &&
464528 typeof estimatedMessageTokens === "number" &&
465529 rawUsagePromptTokens > estimatedMessageTokens * 2 + 10_000
466530 ? estimatedMessageTokens
0 commit comments