/** * Tool output reference registry. * * When enabled via `RunConfig.toolOutputReferences.enabled`, ToolNode * stores each successful tool output under a stable key * (`toolturn`) where `idx` is the tool's position within a * ToolNode batch and `turn` is the batch index within the run * (incremented once per ToolNode invocation). * * Subsequent tool calls can pipe a previous output into their args by * embedding `{{toolturn}}` inside any string argument; * {@link ToolOutputReferenceRegistry.resolve} walks the args and * substitutes the placeholders immediately before invocation. * * The registry stores the *raw, untruncated* tool output so a later * `{{…}}` substitution pipes the full payload into the next tool — * even when the LLM only saw a head+tail-truncated preview in * `ToolMessage.content`. Outputs are stored without any annotation * (the `_ref` key or the `[ref: ...]` prefix seen by the LLM is * strictly a UX signal attached to `ToolMessage.content`). Keeping the * registry pristine means downstream bash/jq piping receives the * complete, verbatim output with no injected fields. */ import type { BaseMessage } from '@langchain/core/messages'; /** * Non-global matcher for a single `{{toolturn}}` placeholder. * Exported for consumers that want to detect references (e.g., syntax * highlighting, docs). The stateful `g` variant lives inside the * registry so nobody trips on `lastIndex`. */ export declare const TOOL_OUTPUT_REF_PATTERN: RegExp; /** Object key used when a parsed-object output has `_ref` injected. */ export declare const TOOL_OUTPUT_REF_KEY = "_ref"; /** * Object key used to carry unresolved reference warnings on a parsed- * object output. Using a dedicated field instead of a trailing text * line keeps the annotated `ToolMessage.content` parseable as JSON for * downstream consumers that rely on the object shape. */ export declare const TOOL_OUTPUT_UNRESOLVED_KEY = "_unresolved_refs"; /** Single-line prefix prepended to non-object tool outputs so the LLM sees the reference key. */ export declare function buildReferencePrefix(key: string): string; /** Stable registry key for a tool output. */ export declare function buildReferenceKey(toolIndex: number, turn: number): string; export type ToolOutputReferenceRegistryOptions = { /** Maximum characters stored per registered output. */ maxOutputSize?: number; /** Maximum total characters retained across all registered outputs. */ maxTotalSize?: number; /** * Upper bound on the number of concurrently-tracked runs. When * exceeded, the oldest run bucket is evicted (FIFO). Defaults to 32. */ maxActiveRuns?: number; }; /** * Result of resolving placeholders in tool args. */ export type ResolveResult = { /** Arguments with placeholders replaced. Same shape as the input. */ resolved: T; /** Reference keys that were referenced but had no stored value. */ unresolved: string[]; }; /** * Read-only view over a frozen registry snapshot. Returned by * {@link ToolOutputReferenceRegistry.snapshot} for callers that need * to resolve placeholders against the registry state at a specific * point in time, ignoring any subsequent registrations. */ export interface ToolOutputResolveView { resolve(args: T): ResolveResult; } /** * Pre-resolved arg map keyed by `toolCallId`. Used by the mixed * direct+event dispatch path to feed event calls' resolved args * (captured pre-batch) into the dispatcher without re-resolving * against the now-stale live registry. */ export type PreResolvedArgsMap = Map; unresolved: string[]; }>; /** * Per-call sink for resolved args, keyed by `toolCallId`. Threaded * as a per-batch local map so concurrent `ToolNode.run()` calls do * not race on shared sink state. */ export type ResolvedArgsByCallId = Map>; /** * Ordered map of reference-key → stored output, partitioned by run so * concurrent / interleaved runs sharing one registry cannot leak * outputs between each other. * * Each public method takes a `runId` which selects the run's bucket. * Hosts typically get one registry per run via `Graph`, in which * case only a single bucket is ever populated; the partitioning * exists so the registry also behaves correctly when a single * instance is reused directly. */ export declare class ToolOutputReferenceRegistry { private runStates; private readonly maxOutputSize; private readonly maxTotalSize; private readonly maxActiveRuns; /** * Local stateful matcher used only by `replaceInString`. Kept * off-module so callers of the exported `TOOL_OUTPUT_REF_PATTERN` * never see a stale `lastIndex`. */ private static readonly PLACEHOLDER_MATCHER; constructor(options?: ToolOutputReferenceRegistryOptions); private keyFor; private getOrCreate; /** Registers (or replaces) the output stored under `key` for `runId`. */ set(runId: string | undefined, key: string, value: string): void; /** Returns the stored value for `key` in `runId`'s bucket, or `undefined`. */ get(runId: string | undefined, key: string): string | undefined; /** * Returns `true` when `key` is currently stored in `runId`'s bucket. * Used by {@link annotateMessagesForLLM} to gate transient annotation * on whether the registry still owns the referenced output (a stale * `_refKey` from a prior run silently no-ops here). */ has(runId: string | undefined, key: string): boolean; /** Total number of registered outputs across every run bucket. */ get size(): number; /** Maximum characters retained per output (post-clip). */ get perOutputLimit(): number; /** Maximum total characters retained *per run*. */ get totalLimit(): number; /** Drops every run's state. */ clear(): void; /** * Explicitly release `runId`'s state. Safe to call when a run has * finished. Hosts sharing one registry across runs should call this * to reclaim memory deterministically; otherwise LRU eviction kicks * in when `maxActiveRuns` runs accumulate. */ releaseRun(runId: string | undefined): void; /** * Claims the next batch turn synchronously from `runId`'s bucket. * * Must be called once at the start of each ToolNode batch before * any `await`, so concurrent invocations within the same run see * distinct turn values (reads are effectively atomic by JS's * single-threaded execution of the sync prefix). * * If `runId` is missing the anonymous bucket is dropped and a * fresh one created so each anonymous call behaves as its own run. */ nextTurn(runId: string | undefined): number; /** * Records that `toolName` has been warned about in `runId` (returns * `true` on the first call per run, `false` after). Used by * ToolNode to emit one log line per offending tool per run when a * `ToolMessage.content` isn't a string. */ claimWarnOnce(runId: string | undefined, toolName: string): boolean; /** * Walks `args` and replaces every `{{toolturn}}` placeholder in * string values with the stored output *from `runId`'s bucket*. Non- * string values and object keys are left untouched. Unresolved * references are left in-place and reported so the caller can * surface them to the LLM. When no placeholder appears anywhere in * the serialized args, the original input is returned without * walking the tree. */ resolve(runId: string | undefined, args: T): ResolveResult; /** * Captures a frozen snapshot of `runId`'s current entries and * returns a view that resolves placeholders against *only* that * snapshot. The snapshot is decoupled from the live registry, so * subsequent `set()` calls (for example, same-turn direct outputs * registering while an event branch is still in flight) are * invisible to the snapshot's `resolve`. Used by the mixed * direct+event dispatch path to preserve same-turn isolation when * a `PreToolUse` hook rewrites event args after directs have * completed. */ snapshot(runId: string | undefined): ToolOutputResolveView; private resolveAgainst; private transform; private replaceInString; private evictWithinBucket; } /** * Annotates `content` with a reference key and/or unresolved-ref * warnings so the LLM sees both alongside the tool output. * * Behavior: * - If `content` parses as a plain (non-array, non-null) JSON object * and the object does not already have a conflicting `_ref` key, * the reference key and (when present) `_unresolved_refs` array * are injected as object fields, preserving JSON validity for * downstream consumers that parse the output. * - Otherwise (string output, JSON array/primitive, parse failure, * or `_ref` collision), a `[ref: ]\n` prefix line is * prepended and unresolved refs are appended as a trailing * `[unresolved refs: …]` line. * * The annotated string is what the LLM sees as `ToolMessage.content`. * The *original* (un-annotated) value is what gets stored in the * registry, so downstream piping remains pristine. * * @param content Raw (post-truncation) tool output. * @param key Reference key for this output, or undefined when * there is nothing to register (errors etc.). * @param unresolved Reference keys that failed to resolve during * argument substitution. Surfaced so the LLM can * self-correct its next tool call. */ export declare function annotateToolOutputWithReference(content: string, key: string | undefined, unresolved?: string[]): string; /** * Lazy projection that, given a registry and a runId, returns a new * `messages` array where each `ToolMessage` carrying ref metadata is * projected into a transient copy with annotated content (when the ref * is live in the registry) and with the framework-owned `additional_ * kwargs` keys (`_refKey`, `_refScope`, `_unresolvedRefs`) stripped * regardless of whether annotation applied. The original input array * and its messages are never mutated. * * Annotation is gated on registry presence: a stale `_refKey` from a * prior run (e.g. one that survived in persisted history) silently * no-ops on the *content* side. The strip-metadata side still runs so * stale framework keys never leak onto the wire under any custom or * future provider serializer that might transmit `additional_kwargs`. * `_unresolvedRefs` is always meaningful and is not gated. * * **Feature-disabled fast path:** when the host hasn't enabled the * tool-output-reference feature, the registry is `undefined` and this * function returns the input array reference-equal *without iterating * a single message*. The loop is exclusive to the feature-enabled * code path. */ export declare function annotateMessagesForLLM(messages: BaseMessage[], registry: ToolOutputReferenceRegistry | undefined, runId: string | undefined): BaseMessage[];