/**
* Parser for tool calls that a model leaked into plain text output as
* antml-style XML markup instead of emitting them through the native
* `tool_calls` stream.
*
* Observed shape (OpenRouter models occasionally emit this despite native
* tools being passed in the request):
*
* wrapper-open tag
* invoke-open tag with name attribute
* parameter-open tag with name attribute ... value ... parameter-close tag
* invoke-close tag
* wrapper-close tag
*
* where the wrapper tag is `function_calls` (optionally with an `antml:`
* namespace prefix) and invoke/parameter tags follow the same convention.
*
* Models also emit the same markup WITHOUT the wrapper — orphan
* `…` blocks straight in prose (pi-style) — or
* truncated blocks that never receive their closing tags before the stream
* ends. Both shapes are recovered: orphan blocks during streaming, truncated
* parameter-complete tails at end of stream (see salvageLeakedToolCallTail).
*
* Everything here is pure string logic — no provider imports — so the tests
* cover it without any streaming harness.
*/
/**
* Floor for how much tail a caller should expect a partial opener to occupy
* (the longest opener tag is the antml-prefixed one, 22 chars, plus margin).
* Partial detection anchors at the last `<` of the buffered text, so an
* invoke opener whose name attribute is still open can hold back more than
* this when the tool name is long — it is a floor, not a cap.
*/
export declare const LEAK_TAIL_LENGTH = 32;
export type LeakedToolCall = {
name: string;
args: Record;
};
export type LeakOpenerMatch = "full" | "partial" | "none";
/**
* Locate a leaked opener tag in `text` — either the wrapper
* (``) or an orphan invoke (``, pi-style
* markup emitted without the wrapper).
* - "full": a complete opener tag exists; `index` points at its `<`.
* - "partial": the text ENDS mid-opener (an opener split across streaming
* deltas); `index` points at the start of that tail. A partial always
* starts at the last `<` — the tail matcher admits no further `<` — so the
* scan is a single anchored match there instead of a prefix enumeration.
* - "none": no opener, complete or partial.
*/
export declare function findLeakedToolCallOpener(text: string): {
match: LeakOpenerMatch;
index: number;
};
/**
* Extract tool calls from a leaked wrapper block.
*
* Returns:
* - `complete: true` → the wrapper closer was found; `calls` holds every
* invoke in order and `remaining` is the text outside the block (prose
* before + after). `consumed` is the raw block text (opener→closer
* inclusive) so callers can fall back to emitting it as text when the
* parsed tool names turn out to be unknown.
* When no wrapper opener exists at all, orphan invoke blocks (pi-style, no
* wrapper) are parsed instead — but only when EVERY invoke opener has a
* matching closer; otherwise `complete: false` and `remaining`/`consumed`
* echo the input so nothing is cut prematurely. (A wrapper opener blocks the
* orphan path: wrapper-less invokes around a wrapper block are picked up
* later from `remaining`, once the wrapper block itself is consumed.)
*/
export declare function extractLeakedToolCalls(text: string): {
calls: LeakedToolCall[];
complete: boolean;
remaining: string;
consumed: string;
};
/**
* End-of-stream recovery for a leaked buffer that never received its closing
* tags (stream cut off, or a model that forgets `` / the wrapper
* closer). Parses invoke blocks tolerantly: a missing closer is fine as long
* as every `` value is fully closed. A truncated parameter value
* means the model was cut mid-argument — guessing at the value is unsafe, so
* the whole tail degrades to plain text (null).
*
* Returns the parsed calls (leading wrapper skeleton dropped), or null when
* nothing salvageable remains. Callers still validate tool names before
* emitting synthetic tool calls.
*/
export declare function salvageLeakedToolCallTail(text: string): LeakedToolCall[] | null;
/**
* Coerce a leaked parameter string into a JSON value when it clearly parses
* as one — native `tool_calls` arguments are real JSON, so a leaked
* `[1,2]` should arrive as an array, not a
* string. Anything that does not fully parse stays a string.
*/
export declare function coerceLeakedParameterValue(raw: string): unknown;
//# sourceMappingURL=leaked-tool-call-parser.d.ts.map