Files
orca/src/main/plugins/plugin-worker-output-buffer.ts
T
d04b05b5c8 Detach retained CI and terminal tails from oversized strings (#20960)
* fix(memory): detach retained CI and terminal tails from oversized strings

* fix(terminal): detach retained error and reattach string slices

* fix(terminal): release oversized recent-output backing strings

* fix(terminal): release backing strings held by PTY detectors

* fix(memory): own bounded Claude background task labels

* fix: detach retained terminal mode scan tails

* fix: own retained plugin worker output strings

* fix: own incomplete OSC 133 carry strings

---------

Co-authored-by: m4air <m4air@Mac.localdomain>
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-09-17 20:34:19 -07:00

74 lines
2.0 KiB
TypeScript

import type { Readable } from 'node:stream'
import { ownRetainedString } from '../../shared/own-retained-string'
type PluginWorkerOutputSink = (level: 'info' | 'warn' | 'error', line: string) => void
export const PLUGIN_WORKER_OUTPUT_LINE_LIMIT = 8192
const TRUNCATION_SUFFIX = '… [truncated]'
/** Keeps a worker's unterminated output bounded even if it never writes a newline. */
export function pipePluginWorkerOutput(
stream: Readable | null,
level: 'info' | 'error',
log: PluginWorkerOutputSink
): void {
if (!stream) {
return
}
let buffered = ''
let discarding = false
function emit(line: string, truncated = false): void {
if (line.trim().length > 0) {
log(
level,
ownRetainedString(
truncated
? `${line.slice(0, PLUGIN_WORKER_OUTPUT_LINE_LIMIT - TRUNCATION_SUFFIX.length)}${TRUNCATION_SUFFIX}`
: line
)
)
}
}
stream.setEncoding('utf8')
stream.on('data', (chunk: string) => {
let remaining = chunk
while (remaining.length > 0) {
if (discarding) {
const newline = remaining.indexOf('\n')
if (newline === -1) {
return
}
discarding = false
remaining = remaining.slice(newline + 1)
continue
}
const newline = remaining.indexOf('\n')
const segment = newline === -1 ? remaining : remaining.slice(0, newline)
const available = PLUGIN_WORKER_OUTPUT_LINE_LIMIT - buffered.length
if (segment.length > available) {
emit(buffered + segment.slice(0, available), true)
buffered = ''
discarding = newline === -1
} else {
buffered += newline === -1 ? ownRetainedString(segment) : segment
if (newline !== -1) {
emit(buffered)
buffered = ''
}
}
if (newline === -1) {
return
}
remaining = remaining.slice(newline + 1)
}
})
stream.on('end', () => {
if (!discarding) {
emit(buffered)
}
buffered = ''
})
}