()
| 1930 | } |
| 1931 | |
| 1932 | private trimTranscriptWindow(): boolean { |
| 1933 | if (!TRANSCRIPT_WINDOW_ENABLED || TRANSCRIPT_MAX_TURNS <= 0) return false; |
| 1934 | // Session replay already caps history to its own turn limit; trimming during |
| 1935 | // replay would shrink it further and fight that limit. |
| 1936 | if (this.state.appState.isReplaying) return false; |
| 1937 | |
| 1938 | const children = this.state.transcriptContainer.children; |
| 1939 | |
| 1940 | // Trim whole turns by *position* in the child list rather than by entry |
| 1941 | // lookup — otherwise only the (registered) user message would be removed and |
| 1942 | // the rest of the turn would be left behind. |
| 1943 | const boundaries: number[] = []; |
| 1944 | for (let i = 0; i < children.length; i++) { |
| 1945 | if (this.isTurnBoundaryComponent(children[i]!)) boundaries.push(i); |
| 1946 | } |
| 1947 | |
| 1948 | const turns = groupTurns(this.state.transcriptEntries); |
| 1949 | |
| 1950 | const toRemove = turnsToTrim(turns, TRANSCRIPT_MAX_TURNS, TRANSCRIPT_HYSTERESIS); |
| 1951 | if (toRemove.size === 0) return false; |
| 1952 | |
| 1953 | // Reclaim image bytes referenced by trimmed user messages. The transcript |
| 1954 | // renders historical thumbnails via imageStore.get(id), so an attachment can |
| 1955 | // only be dropped once its owning user message leaves the transcript. |
| 1956 | for (const entry of toRemove) { |
| 1957 | if (entry.kind === 'user' && entry.imageAttachmentIds !== undefined) { |
| 1958 | this.imageStore.removeMany(entry.imageAttachmentIds); |
| 1959 | } |
| 1960 | } |
| 1961 | |
| 1962 | let boundariesToRemove = 0; |
| 1963 | for (const entry of toRemove) { |
| 1964 | if ( |
| 1965 | (entry.kind === 'user' || |
| 1966 | entry.kind === 'skill_activation' || |
| 1967 | entry.kind === 'plugin_command') && |
| 1968 | entry.turnId === undefined |
| 1969 | ) { |
| 1970 | boundariesToRemove++; |
| 1971 | } |
| 1972 | } |
| 1973 | if (boundariesToRemove === 0) { |
| 1974 | this.state.transcriptEntries = this.state.transcriptEntries.filter((e) => !toRemove.has(e)); |
| 1975 | return true; |
| 1976 | } |
| 1977 | |
| 1978 | let boundariesSeen = 0; |
| 1979 | let cutoff = 0; |
| 1980 | for (let i = 0; i < children.length; i++) { |
| 1981 | if (this.isTurnBoundaryComponent(children[i]!)) { |
| 1982 | if (boundariesSeen === boundariesToRemove) { |
| 1983 | cutoff = i; |
| 1984 | break; |
| 1985 | } |
| 1986 | boundariesSeen++; |
| 1987 | } |
| 1988 | } |
| 1989 |
no test coverage detected