/** * Close-matcher for a ` ```thinking ` block that respects nested Markdown code * fences. A naive `indexOf("```")` closes the thinking section at the FIRST * backtick fence inside the reasoning, so an inner ` ```rs … ``` ` code block * leaks its body (and everything after) into the visible channel. This scanner * tracks inner-fence nesting so only the real thinking closer ends the block. * * Distinguishing an inner opener from the closer: a fenced code block opener is * ` ``` ` immediately followed by a language token (`rs`, `tool_code`, `c++` …) * and a newline. The thinking closer is a bare ` ``` `, or ` ``` ` glued to the * visible reply — its remainder is prose (contains whitespace/punctuation), not * a language token. So a top-level fence whose info is a single language-token * word opens a nested block; anything else closes the thinking, and the text * after the fence run is the visible reply. This preserves the long-standing * inline-close behavior (` ```Visible reply ` ends the block) while skipping * language-tagged inner fences. * * Used by both the owned Gemini scanner (live ` ```thinking ` stream) and the * generic `ThinkingInbandScanner` (leaked-idiom healing). * * Limitation: an *info-less* nested fence (a bare ` ``` ` opening a code block * inside the reasoning) is indistinguishable from the thinking closer and ends * the block. Models tag their fences with a language in practice, so this is * strictly better than the previous first-` ``` ` behavior. */ /** Result of feeding bytes to {@link FencedThinkingScanner}. */ export interface FencedThinkingResult { /** Thinking text to emit for this feed (may be empty). */ readonly thinking: string; /** True once the thinking closer has been consumed. */ readonly closed: boolean; /** Bytes after the closing fence (visible reply); only meaningful when {@link closed}. */ readonly rest: string; } /** * Stateful, line-oriented close-matcher for one ` ```thinking ` block. Owns the * partial-line buffer so an ambiguous trailing fence is held until it resolves. * * Streaming stays character-level for ordinary content: a line is emitted as its * bytes arrive, yet retained in the buffer until its newline so the complete * line can be classified ({@link #emitted} tracks how many leading bytes are * already emitted). A top-level fence candidate is held until its info * disambiguates opener (language token) from closer (prose / bare). */ export declare class FencedThinkingScanner { #private; /** * Feed bytes and return thinking deltas plus close state. When `final`, the * held tail resolves: a bare ` ``` ` or a ` ``` ` fence closes the * block (remainder becomes `rest`), otherwise it is unterminated thinking. */ feed(text: string, final: boolean): FencedThinkingResult; }