import { ContentStore } from './content-store.js'; import { ContextBudget } from './context-budget.js'; /** * Types of stream-json events we care about. */ export type StreamEventType = 'assistant' | 'user' | 'tool_use' | 'tool_result' | 'system' | 'error' | 'unknown'; /** * Parsed stream-json message structure. */ export interface StreamMessage { type?: string; subtype?: string; message?: { content?: Array<{ type: string; text?: string; content?: string; id?: string; tool_use_id?: string; name?: string; input?: Record; is_error?: boolean; }>; }; result?: string; content?: string; tool_use_id?: string; name?: string; output?: string; tool_use_result?: { stdout?: string; stderr?: string; }; } /** * Result of processing a stream line. */ export interface ProcessedLine { /** Original or modified JSON line */ line: string; /** Whether the line was modified */ modified: boolean; /** If content was externalized, the stored content ID */ storedContentId?: string; /** Event type detected */ eventType: StreamEventType; /** Error message if parsing failed */ error?: string; } /** * Configuration for StreamProcessor. */ export interface StreamProcessorConfig { /** Context budget for tracking token usage */ budget: ContextBudget; /** Content store for externalizing large content */ contentStore: ContentStore; /** Callback when content is externalized */ onExternalize?: (contentId: string, originalSize: number, summary: string) => void; /** * Minimum tokens for automatic externalization (default: 100). * Tool results above this threshold are always externalized regardless of budget. * Set to Infinity to disable always-externalize (revert to budget-only behavior). */ minExternalizeTokens?: number; } /** * Extract the content a write-side tool authored, for content-store indexing (task 103). * Tool inputs are otherwise discarded at the stream layer, so a file created via * Write/Edit was invisible to RAG unless something later read it back. * Returns null for tools that don't author file content. */ export declare function extractAuthoredContent(toolName: string, input: Record): { content: string; filePath?: string; summary: string; } | null; /** * Strip the line-number prefixes the harness adds to built-in Read tool results * (cat -n style: ` 123\t…` or ` 123→…`) before storing (task 115). Stored text * then matches what read_file would store for the same file, so chunking, * keyword scanning, and embeddings behave identically for both read paths. * Conservative: only strips when ≥90% of non-empty lines carry the prefix AND * the numbers are non-decreasing — a file that genuinely starts lines with * numbers (CSV, logs) doesn't fit that shape and passes through untouched. */ export declare function stripReadLineNumbers(content: string): string; /** * StreamProcessor intercepts stream-json output and externalizes large content. * * It tracks: * - Current tool invocations (to associate results with tools) * - Token budget consumption * - Content externalization * * Usage: * ``` * const processor = new StreamProcessor({ * budget: new ContextBudget(), * contentStore: new ContentStore(basePath), * }); * * for (const line of streamLines) { * const result = await processor.processLine(line); * // Use result.line (original or modified) * } * ``` */ export declare class StreamProcessor { private budget; private contentStore; private onExternalize?; private minExternalizeTokens; /** Track active tool calls by tool_use_id */ private activeTools; /** Track the last tool_use event for associating with results */ private lastToolUse; constructor(config: StreamProcessorConfig); /** * Process a single stream-json line. * Returns the original or modified line. */ processLine(jsonLine: string): Promise; /** * Detect the type of stream event. */ private detectEventType; /** * Track a tool_use event. * Write-side tool inputs (Write/Edit/MultiEdit/NotebookEdit) are also stored * to the ContentStore — otherwise a file the agent authors never enters the * RAG pool unless something later reads it back (task 103). */ private handleToolUse; /** * Store a write-side tool input to the ContentStore (store-only: no budget * consumption, no line modification — the input is already inline in the * conversation). Trivial inputs are skipped; identical re-writes dedupe on * content hash inside store(). Errors never fail stream processing. */ private storeAuthoredInput; /** * Process a tool_result event. * ALWAYS stores content to ContentStore for RAG indexing. * Only replaces inline content with [STORED:xxx] if it exceeds budget. */ private handleToolResult; /** * Create the replacement content string for externalized content. */ private createReplacementContent; /** * Create a modified message with replacement content. */ private createModifiedMessage; /** * Reset processor state for a new turn. */ reset(): void; /** * Get current budget stats. */ getBudgetStats(): { consumed: number; limit: number; remaining: number; utilizationPercent: number; }; } /** * Check if a user input should be externalized. */ export declare function shouldExternalizeUserInput(content: string, budget: ContextBudget): boolean; /** * Externalize user input and return replacement message. */ export declare function externalizeUserInput(content: string, contentStore: ContentStore): Promise<{ replacement: string; contentId: string; }>; //# sourceMappingURL=stream-processor.d.ts.map