import { UserMessageParams, type AgentToolBlockUpdateParams, type ToolBlockUpdateCallbackParams, type AddNotificationMessageParams } from "../utils/messageOperations.js"; import type { Message, Usage } from "../types/index.js"; import { SessionData } from "../services/session.js"; import { ChatCompletionMessageFunctionToolCall } from "openai/resources.js"; import type { MemoryRule } from "../types/memoryRule.js"; import { Container } from "../utils/container.js"; export interface MessageManagerCallbacks { onMessagesChange?: (messages: Message[]) => void; onSessionIdChange?: (sessionId: string) => void; onLatestTotalTokensChange?: (latestTotalTokens: number) => void; onUsagesChange?: (usages: Usage[]) => void; onUserMessageAdded?: (params: UserMessageParams) => void; onAssistantMessageAdded?: (messageId: string) => void; onAssistantContentUpdated?: (params: { messageId: string; chunk: string; accumulated: string; stage: "streaming" | "end"; }) => void; onAssistantReasoningUpdated?: (params: { messageId: string; chunk: string; accumulated: string; stage: "streaming" | "end"; }) => void; onToolBlockUpdated?: (params: ToolBlockUpdateCallbackParams) => void; onErrorBlockAdded?: (error: string) => void; onCompactBlockAdded?: (content: string) => void; onCompactionStateChange?: (isCompacting: boolean) => void; onAddBangMessage?: (command: string) => void; onUpdateBangMessage?: (command: string, output: string) => void; onCompleteBangMessage?: (command: string, exitCode: number) => void; onInfoBlockAdded?: (content: string) => void; onShowRewind?: () => void; onFileHistoryBlockAdded?: (snapshots: import("../types/reversion.js").FileSnapshot[]) => void; onNotificationMessageAdded?: (params: { taskId: string; taskType: "shell" | "agent" | "workflow"; status: "completed" | "failed" | "killed" | "aborted"; summary: string; }) => void; } export interface MessageManagerOptions { callbacks: MessageManagerCallbacks; workdir: string; sessionType?: "main" | "subagent"; subagentType?: string; } export declare class MessageManager { private container; private sessionId; private messages; private latestTotalTokens; private workdir; private encodedWorkdir; private callbacks; private transcriptPath; private savedMessageCount; private pendingFileReadTriggers; private loadedRuleIds; private recentFileReads; private invokedSkills; private sessionType; private subagentType?; private _usages; constructor(container: Container, options: MessageManagerOptions); private get memoryRuleManager(); private get memoryService(); getSessionId(): string; getRootSessionId(): string; getMessages(): Message[]; getUsages(): Usage[]; getLatestTotalTokens(): number; getWorkdir(): string; /** * Process pending file read triggers: match against conditional rules, * return rules not yet loaded (dedup via loadedRuleIds), mark them as loaded. * Clears triggers after processing. */ processTriggeredRules(): MemoryRule[]; getSessionDir(): string; getTranscriptPath(): string; /** * Get combined memory content (project memory + user memory + unconditional rules) * Note: conditional rules are handled separately via processTriggeredRules(). */ getCombinedMemory(): Promise; /** * Get memory content for message-array injection: * - prependContent: AGENTS.md + user memory + unconditional rules (for head injection) * Conditional rules are handled separately via processTriggeredRules(). */ getMemoryForInjection(): Promise<{ prependContent: string; }>; /** * Compute the transcript path using cached encoded workdir * Called during construction and when sessionId changes */ private computeTranscriptPath; setSessionId(sessionId: string): void; /** * Create session if needed (async helper) */ private createSessionIfNeeded; /** * Record a file path read via the Read tool, to be matched against * conditional memory rules before the next AI call. */ triggerFileRead(filePath: string): void; /** * Check if a file has been read (for read-before-edit enforcement). * Uses recentFileReads Map populated from Read tool execution. */ hasFileBeenRead(filePath: string): boolean; setMessages(messages: Message[]): void; /** * Save current session */ saveSession(): Promise; setlatestTotalTokens(latestTotalTokens: number): void; /** * Clear messages */ clearMessages(): void; /** * Trigger the rewind UI callback */ triggerShowRewind(): void; initializeFromSession(sessionData: SessionData): void; addUserMessage(params: UserMessageParams): string; /** * Update an existing user message by its ID. */ updateUserMessage(id: string, params: Partial): void; addAssistantMessage(content?: string, toolCalls?: ChatCompletionMessageFunctionToolCall[], usage?: Usage, additionalFields?: Record): void; mergeAssistantAdditionalFields(additionalFields: Record): void; updateToolBlock(params: AgentToolBlockUpdateParams): void; /** * Add a tool block to a specific message by ID. */ addToolBlockToMessage(messageId: string, params: Omit): string; addErrorBlock(error: string): void; addInfoBlock(content: string): void; /** * Compact messages and update session — append compact message to same file (append-only). * After compaction, in-memory messages are [compactMessage, ...lastThreeMessages]. */ compactMessagesAndUpdateSession(compactedContent: string, usage?: Usage): Promise; addFileHistoryBlock(snapshots: import("../types/reversion.js").FileSnapshot[]): void; addBangMessage(command: string): void; updateBangMessage(command: string, output: string): void; completeBangMessage(command: string, exitCode: number, output?: string): void; addNotificationMessage(params: Omit): void; /** * Rebuild usage array from messages containing usage metadata * Called during session restoration to reconstruct usage tracking */ rebuildUsageFromMessages(messages: Message[]): void; /** * Add usage data to the tracking array and trigger callbacks * @param usage Usage data from AI operations */ addUsage(usage: Usage): void; /** * Trigger usage change callback with all usage data from assistant messages */ triggerUsageChange(): void; /** * Finalize a streaming block of the given type by setting its stage to "end". * Fires the corresponding incremental callback with chunk="" to signal finalization. * Does NOT call onMessagesChange — the caller is responsible for that. * Returns true if a block was finalized. */ private finalizeStreamingBlock; /** * Update the current assistant message content during streaming * This method updates the last assistant message's content without creating a new message * FR-001: Tracks and provides both chunk (new content) and accumulated (total content) */ updateCurrentMessageContent(newAccumulatedContent: string): void; /** * Update the current assistant message reasoning during streaming * This method updates the last assistant message's reasoning content without creating a new message */ updateCurrentMessageReasoning(newAccumulatedReasoning: string): void; /** * Finalize streaming text/reasoning blocks by setting their stage to "end". * Called when a new block (e.g. tool) is appended during streaming, or when streaming completes. * Fires incremental callbacks for each finalized block. */ finalizeStreamingBlocks(): void; /** * Finalize any tool blocks still in a non-terminal stage (start/streaming/running) * by marking them as ended with an error. Called when the AI call is aborted or * fails mid-tool-stream so the UI stops showing the "running" spinner. * Fires onToolBlockUpdated for each finalized tool block. */ finalizeAbortedToolBlocks(error?: string): void; /** * Remove the last user message from the conversation * Used for hook error handling when the user prompt needs to be erased */ removeLastUserMessage(): void; getFullMessageThread(): Promise<{ messages: Message[]; sessionIds: string[]; }>; /** * Truncate history to a specific index and revert file changes. * @param index - The index of the user message to truncate to. * @param reversionManager - Optional ReversionManager to handle file rollbacks. */ truncateHistory(index: number, reversionManager?: import("./reversionManager.js").ReversionManager): Promise; /** * Rewrite the session file with the current messages. */ private rewriteSessionFile; /** * Extract file read contents from tool result blocks in a message. */ private extractFileReadsFromMessage; /** * Get recent file read contents, sorted by timestamp (newest first). * @param maxFiles - Maximum number of files to return * @param maxTokensPerFile - Maximum tokens per file (CJK-aware estimation) * @returns Array of { path, content } sorted by recency */ getRecentFileReads(maxFiles?: number, maxTokensPerFile?: number): Array<{ path: string; content: string; }>; /** * Extract skill invocations from tool blocks in a message. */ private extractSkillInvocationsFromMessage; /** * Get recently invoked skill names, sorted by timestamp (newest first). * @param maxSkills - Maximum number of skills to return * @returns Array of skill names sorted by recency */ getInvokedSkillNames(maxSkills?: number): string[]; /** * Clear all invoked skills (e.g., after compaction). */ clearInvokedSkills(): void; /** * Clear the loaded rule IDs set (e.g., after compaction resets context). */ clearLoadedRuleIds(): void; /** * Rebuild loadedRuleIds from persisted messages (session restore / post-compaction). * Scans for markers in user meta message content. */ rebuildLoadedRuleIds(): void; }