/** * Chat message capture handler for user and assistant messages. * * Handles `chat.message` hook events from OpenCode. User messages are * captured as `## USER (turn N)` sections with sequential turn counters. * Assistant messages are transformed into `main_l0_agent` blocks via * {@link AgentTransform}. Thinking blocks are filtered out. * * Turn counters are maintained per-session in an instance-level Map. * All handlers are best-effort — errors are logged, never thrown. * * @module session-tracker/capture/message-capture */ import type { SessionWriter } from "../persistence/session-writer.js"; import type { AgentTransform } from "../transform/agent-transform.js"; import type { SessionIndexWriter } from "../persistence/session-index-writer.js"; import type { OpenCodeClient } from "../../../shared/session-api.js"; /** Shape of the chat.message hook input. */ interface ChatMessageInput { sessionID: string; agent?: string; model?: { providerID: string; modelID: string; }; messageID?: string; variant?: string; } /** Shape of a part within the hook output. */ interface OutputPart { type: string; text?: string; content?: string; } /** Shape of the chat.message hook output. */ interface ChatMessageOutput { message: { role: string; }; parts: OutputPart[]; } /** * Captures user and assistant messages from the `chat.message` hook. * * Maintains per-session turn counters and delegates to {@link SessionWriter} * for persistence and {@link AgentTransform} for metadata extraction. */ export declare class MessageCapture { private client; private sessionWriter; private agentTransform; private projectRoot; private sessionIndexWriter; /** * Per-session turn counters. Keyed by sessionID, values are the next * turn number to assign (1-based). */ private turnCounters; /** Sessions already backfilled from SDK messages during this process. */ private backfilledSessions; /** * @param deps - Injected dependencies. * @param deps.client - The OpenCode SDK client for logging. * @param deps.sessionWriter - The session writer for persistence. * @param deps.agentTransform - The agent metadata transform utility. * @param deps.projectRoot - Absolute path to the project root for file reads. * @param deps.sessionIndexWriter - The session index writer for turn count persistence. */ constructor(deps: { client: OpenCodeClient; sessionWriter: SessionWriter; agentTransform: AgentTransform; projectRoot: string; sessionIndexWriter: SessionIndexWriter; }); /** * Handles a chat.message hook event. * * @param input - Hook input containing sessionID, agent, model metadata. * @param output - Hook output containing the message and response parts. * @returns Promise that resolves when the message has been captured. * * @remarks * - User messages (`role === "user"`) are captured as `## USER (turn N)`. * - Assistant messages (`role === "assistant"`) are transformed to * `main_l0_agent` with name, model, and thinking_duration. * - Thinking blocks (`type === "thinking"`) are filtered out. * - All errors are caught and logged; the hook pipeline is never blocked. */ handleChatMessage(input: ChatMessageInput, output: ChatMessageOutput): Promise; /** * Captures a user message as `## USER (turn N)`. * * Increments the turn counter for the given session, appends * the user's text content to the main session `.md` file, and * persists the updated turn count to `session-continuity.json`. */ private handleUserMessage; /** * Transforms and captures an assistant message as `main_l0_agent`. * * Extracts agent metadata via {@link AgentTransform.extractAssistantMetadata}, * filters out thinking blocks, and appends the agent block to the session `.md`. * * **NOTE: This method is currently unreachable for main sessions.** * The `chat.message` hook provides only `UserMessage` (role: "user") — it * never delivers `AssistantMessage`. Therefore this code path is dead code * for the `chat.message` hook. Assistant text for main sessions is instead * captured via the `session.next.text.ended` event in event-capture.ts. * This method is retained for when/if the SDK adds assistant message support * to the `chat.message` hook, or for alternative hook sources. */ private handleAssistantMessage; /** * Resolves the lastMessage value for frontmatter update. * * Priority: text content > tool summary > model name fallback. * Ensures lastMessage is ALWAYS captured even when assistant * only outputs tool calls with no text content. */ private resolveLastMessage; /** * Returns the next turn number for a session and increments the counter. * * @param sessionID - The session identifier. * @returns The next one-based turn number. */ private nextTurnNumber; /** * Seeds in-memory turn counters from existing session .md file content. * * Prevents duplicate turn numbers on plugin restart by reading the * number of `## USER (turn N)` sections already present in the session file. * Call during SessionTracker.initialize() for each known active session. * * @param sessionID - The session identifier to seed. * @returns Promise that resolves when seeding is complete. */ seedTurnCounters(sessionID: string): Promise; /** * Backfills missed real-human user messages from OpenCode session history. * * This repairs race windows where tool/event hooks are observed but the * initial `chat.message` hook was missed by the plugin process. Only * non-synthetic text from SDK user messages is persisted as human context. * * @param sessionID - Main session identifier to backfill. * @returns Promise that resolves after any missing real-human turns are appended. */ backfillUserTurnsFromSdk(sessionID: string): Promise; /** * Extracts the concatenated text content from an array of output parts. * * FIX: Filter thinking blocks + lấy TOÀN BỘ text + content fields, join với \n * * @param parts - Array of hook output parts. * @returns The concatenated text content, or empty string if no text found. */ private extractTextContent; /** * Reads how many human user turns are already persisted. * * @param sessionID - Session identifier. * @returns Count of persisted user turn headers. */ private readPersistedUserTurnCount; /** * Extracts role from either SDK message shape. * * @param message - Raw SDK message. * @returns Message role, if present. */ private messageRole; /** * Extracts non-synthetic text from a user message. * * @param message - Raw SDK message. * @returns Human-authored text content, or undefined when none exists. */ private extractHumanTextFromSdkMessage; } export {}; //# sourceMappingURL=message-capture.d.ts.map