@robota-sdk/agent-session 3.0.0-beta.76 → 3.0.0-beta.77
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -1
- package/LICENSE +661 -21
- package/README.md +17 -12
- package/dist/node/index.cjs +5 -5
- package/dist/node/index.d.ts +101 -12
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +5 -5
- package/dist/node/index.js.map +1 -1
- package/package.json +5 -4
package/dist/node/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["logger","logger","logger","isRecord","join","existsSync","readFileSync"],"sources":["../../src/session-base.ts","../../src/compaction-orchestrator.ts","../../src/context-window-tracker.ts","../../src/permission-types.ts","../../src/tool-hook-helpers.ts","../../src/permission-enforcer.ts","../../src/session-components.ts","../../src/session-history-ops.ts","../../src/session-lifecycle.ts","../../src/session-tool-execution-bridge.ts","../../src/session-run.ts","../../src/session.ts","../../src/session-logger.ts","../../src/session-log-validation.ts","../../src/session-log-replay.ts","../../src/session-store.ts"],"sourcesContent":["import type { ContextWindowTracker, TAutoCompactThreshold } from './context-window-tracker.js';\nimport type { PermissionEnforcer } from './permission-enforcer.js';\nimport type {\n Robota,\n IAIProvider,\n IContextWindowState,\n IHistoryEntry,\n IToolSchema,\n TModelEffort,\n TPermissionMode,\n TUniversalMessage,\n} from '@robota-sdk/agent-core';\n\nexport abstract class SessionBase {\n protected abstract readonly robota: Robota;\n protected abstract readonly permissionEnforcer: PermissionEnforcer;\n protected abstract readonly contextTracker: ContextWindowTracker;\n protected abstract permissionMode: TPermissionMode;\n protected abstract activePresetId: string;\n protected abstract parallelSubagentsEnabled: boolean;\n protected abstract readonly sessionId: string;\n protected abstract readonly aiProvider: IAIProvider;\n protected abstract readonly toolSchemas: IToolSchema[];\n protected abstract model: string;\n protected abstract systemMessage: string;\n protected abstract messageCount: number;\n protected abstract abortController: AbortController | null;\n\n getPermissionMode(): TPermissionMode {\n return this.permissionMode;\n }\n\n /** Change the active permission mode — future tool calls will use the new mode. */\n setPermissionMode(mode: TPermissionMode): void {\n this.permissionMode = mode;\n }\n\n /** Read the active preset id (PRESET-011 runtime state). */\n getActivePresetId(): string {\n return this.activePresetId;\n }\n\n /**\n * Set the active preset id. PURE STATE — this only records which preset is active;\n * it does not re-apply any preset options (permission/model/persona). Higher layers\n * own re-application (PRESET-012/013/014).\n */\n setActivePresetId(id: string): void {\n this.activePresetId = id;\n }\n\n /** Whether subagent dispatch is currently allowed for this session (PRESET-016 runtime gate). */\n getParallelSubagentsEnabled(): boolean {\n return this.parallelSubagentsEnabled;\n }\n\n /** Toggle subagent dispatch live. Only effective if the agent runtime was built at assembly. */\n setParallelSubagentsEnabled(enabled: boolean): void {\n this.parallelSubagentsEnabled = enabled;\n }\n\n getSessionId(): string {\n return this.sessionId;\n }\n\n getSystemMessage(): string {\n return this.systemMessage;\n }\n\n /** Replace the active system message and propagate to the agent (used by staleness detection). */\n updateSystemMessage(newMessage: string): void {\n this.systemMessage = newMessage;\n this.robota.setModel({\n provider: this.aiProvider.name,\n model: this.model,\n systemMessage: newMessage,\n });\n }\n\n /**\n * Re-apply model options to the live session (PRESET-013 model/effort re-application seam).\n *\n * Propagates model/effort/temperature/maxOutputTokens to the agent via `robota.setModel` so the\n * next call reflects them, and updates `this.model` to keep `getModelId()` accurate. The preset\n * `maxOutputTokens` field maps to the agent's `maxTokens` channel. Absent fields are left untouched.\n */\n async applyModelOptions(options: {\n model?: string;\n effort?: TModelEffort;\n temperature?: number;\n maxOutputTokens?: number;\n }): Promise<void> {\n // `setModel` requires the agent to be fully initialized. On a fresh interactive session the\n // agent initializes lazily on the first `run()`, so a live model change before any message\n // (e.g. `/preset` right after launch) would otherwise hit the \"must be fully initialized\"\n // guard. Bring the agent to a ready state first — idempotent and side-effect-free.\n await this.robota.ensureReady();\n const nextModel = options.model ?? this.model;\n this.robota.setModel({\n provider: this.aiProvider.name,\n model: nextModel,\n systemMessage: this.systemMessage,\n ...(options.effort !== undefined && { effort: options.effort }),\n ...(options.temperature !== undefined && { temperature: options.temperature }),\n ...(options.maxOutputTokens !== undefined && { maxTokens: options.maxOutputTokens }),\n });\n this.model = nextModel;\n }\n\n getToolSchemas(): IToolSchema[] {\n return this.toolSchemas;\n }\n\n getMessageCount(): number {\n return this.messageCount;\n }\n\n /** Get tools that have been session-approved (via \"Allow always\" choice). */\n getSessionAllowedTools(): string[] {\n return this.permissionEnforcer.getSessionAllowedTools();\n }\n\n clearSessionAllowedTools(): void {\n this.permissionEnforcer.clearSessionAllowedTools();\n }\n\n /** Abort the currently running execution. No-op if nothing is running. */\n abort(): void {\n if (this.abortController) {\n this.abortController.abort();\n this.abortController = null;\n }\n }\n\n isRunning(): boolean {\n return this.abortController !== null;\n }\n\n getContextState(): IContextWindowState {\n return this.contextTracker.getContextState();\n }\n\n /** Estimate context usage from current conversation history (used after session restore). */\n syncContextFromHistory(): void {\n this.contextTracker.updateFromHistory(this.robota.getHistory());\n }\n\n getAutoCompactThreshold(): TAutoCompactThreshold {\n return this.contextTracker.getAutoCompactThreshold();\n }\n\n setAutoCompactThreshold(threshold: number | false): void {\n this.contextTracker.setAutoCompactThreshold(threshold);\n }\n\n getHistory(): TUniversalMessage[] {\n return this.robota.getHistory();\n }\n\n getFullHistory(): IHistoryEntry[] {\n return this.robota.getFullHistory();\n }\n\n getSessionTokenUsage(): { inputTokens: number; outputTokens: number } | undefined {\n let inputTokens = 0;\n let outputTokens = 0;\n let found = false;\n for (const entry of this.getFullHistory()) {\n if (entry.category !== 'event' || entry.type !== 'usage-summary') continue;\n const snap = entry.data as { promptTokens?: number; completionTokens?: number } | undefined;\n inputTokens += snap?.promptTokens ?? 0;\n outputTokens += snap?.completionTokens ?? 0;\n found = true;\n }\n return found ? { inputTokens, outputTokens } : undefined;\n }\n\n getModelId(): string {\n return this.model;\n }\n\n /** Add an event entry to history (not a chat message) */\n addHistoryEntry(entry: IHistoryEntry): void {\n this.robota.addHistoryEntry(entry);\n }\n\n /** Inject a message into conversation history without execution (used for session restore). */\n injectMessage(\n role: 'user' | 'assistant' | 'system' | 'tool',\n content: string,\n options?: { toolCallId?: string; name?: string },\n ): void {\n this.robota.injectMessage(role, content, options);\n }\n\n /**\n * Inject a full TUniversalMessage preserving all fields (toolCalls, toolCallId, null content).\n * Used during session restore to correctly reconstruct tool_use+tool_result pairs.\n */\n injectRawMessage(msg: TUniversalMessage): void {\n this.robota.injectRawMessage(msg);\n }\n\n clearHistory(): void {\n this.robota.clearHistory();\n this.contextTracker.reset();\n }\n}\n","/**\n * CompactionOrchestrator — handles conversation compaction (summarization)\n * to free context window space.\n *\n * Extracted from Session to separate compaction logic from conversation management.\n */\n\nimport { randomUUID } from 'node:crypto';\n\nimport { runHooks } from '@robota-sdk/agent-core';\n\nimport type {\n IAIProvider,\n TUniversalMessage,\n THooksConfig,\n IHookInput,\n IHookTypeExecutor,\n} from '@robota-sdk/agent-core';\n\nexport interface ICompactionOptions {\n sessionId: string;\n cwd: string;\n model: string;\n hooks?: Record<string, unknown>;\n compactInstructions?: string;\n /** Additional hook type executors (e.g. prompt, agent) beyond the core defaults. */\n hookTypeExecutors?: IHookTypeExecutor[];\n}\n\nexport class CompactionOrchestrator {\n private readonly sessionId: string;\n private readonly cwd: string;\n private readonly model: string;\n private readonly hooks?: Record<string, unknown>;\n private readonly compactInstructions?: string;\n private readonly hookTypeExecutors?: IHookTypeExecutor[];\n\n constructor(options: ICompactionOptions) {\n this.sessionId = options.sessionId;\n this.cwd = options.cwd;\n this.model = options.model;\n this.hooks = options.hooks;\n this.compactInstructions = options.compactInstructions;\n this.hookTypeExecutors = options.hookTypeExecutors;\n }\n\n /**\n * Run compaction — summarize the conversation to free context space.\n * @param provider - The AI provider to use for summarization\n * @param history - Current conversation history\n * @param instructions - Optional focus instructions for the summary\n * @returns The generated summary string\n */\n async compact(\n provider: IAIProvider,\n history: TUniversalMessage[],\n instructions?: string,\n ): Promise<string> {\n if (history.length === 0) return '';\n\n const trigger: 'auto' | 'manual' = instructions !== undefined ? 'manual' : 'auto';\n\n // Fire PreCompact hook\n const preHookInput: IHookInput = {\n session_id: this.sessionId,\n cwd: this.cwd,\n hook_event_name: 'PreCompact',\n trigger,\n };\n await runHooks(\n this.hooks as THooksConfig | undefined,\n 'PreCompact',\n preHookInput,\n this.hookTypeExecutors,\n );\n\n // Build compaction prompt\n const compactPrompt = this.buildCompactionPrompt(history, instructions);\n\n // Call provider to generate summary\n const summaryMessage = await provider.chat(\n [\n {\n id: randomUUID(),\n role: 'user',\n content: compactPrompt,\n state: 'complete' as const,\n timestamp: new Date(),\n },\n ],\n { model: this.model },\n );\n const summary =\n typeof summaryMessage.content === 'string' ? summaryMessage.content : '(compaction failed)';\n\n return summary;\n }\n\n /** Build the compaction prompt from conversation history */\n private buildCompactionPrompt(history: TUniversalMessage[], instructions?: string): string {\n const instructionBlock = instructions ?? this.compactInstructions ?? '';\n const instructionSection = instructionBlock ? `\\nAdditional focus:\\n${instructionBlock}\\n` : '';\n\n const formattedHistory = history\n .map((msg) => {\n const content = typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content);\n return `${msg.role}: ${content}`;\n })\n .join('\\n');\n\n return [\n 'Summarize the following conversation concisely, preserving:',\n \"- User's original requests and goals\",\n '- Key decisions and conclusions',\n '- Important code changes and file paths',\n '- Current task status and next steps',\n instructionSection,\n \"Drop verbose tool outputs, debugging steps, and exploratory work that didn't lead to results.\",\n '',\n 'Conversation:',\n formattedHistory,\n ].join('\\n');\n }\n}\n","/**\n * ContextWindowTracker — tracks token usage and context window state.\n *\n * Extracted from Session to separate context monitoring from conversation management.\n */\n\nimport { estimateContextTokensFromMessages, getModelContextWindow } from '@robota-sdk/agent-core';\n\nimport type { IContextWindowState, TUniversalMessage } from '@robota-sdk/agent-core';\n\n/** Percentage conversion factor */\nconst PERCENT = 100;\n\n/** Auto-compact when context usage reaches this fraction */\nexport const AUTO_COMPACT_THRESHOLD = 0.835;\n\nexport type TAutoCompactThreshold = number | false;\n\nexport class ContextWindowTracker {\n private contextUsedTokens = 0;\n private readonly contextMaxTokens: number;\n private autoCompactThreshold: TAutoCompactThreshold;\n\n constructor(\n model: string,\n contextMaxTokens?: number,\n autoCompactThreshold?: TAutoCompactThreshold,\n ) {\n this.contextMaxTokens = contextMaxTokens ?? getModelContextWindow(model);\n this.autoCompactThreshold = normalizeAutoCompactThreshold(autoCompactThreshold);\n }\n\n /** Get current context window state */\n getContextState(): IContextWindowState {\n const usedPercentage = Math.min(\n PERCENT,\n (this.contextUsedTokens / this.contextMaxTokens) * PERCENT,\n );\n return {\n maxTokens: this.contextMaxTokens,\n usedTokens: this.contextUsedTokens,\n usedPercentage: Math.round(usedPercentage * PERCENT) / PERCENT,\n remainingPercentage: Math.round((PERCENT - usedPercentage) * PERCENT) / PERCENT,\n };\n }\n\n /** Whether auto-compaction threshold has been exceeded */\n shouldAutoCompact(): boolean {\n if (this.autoCompactThreshold === false) {\n return false;\n }\n return this.getContextState().usedPercentage >= this.autoCompactThreshold * PERCENT;\n }\n\n /** The auto-compaction policy for this tracker. */\n getAutoCompactThreshold(): TAutoCompactThreshold {\n return this.autoCompactThreshold;\n }\n\n /** Update the auto-compaction policy for this tracker. */\n setAutoCompactThreshold(autoCompactThreshold: TAutoCompactThreshold): void {\n this.autoCompactThreshold = normalizeAutoCompactThreshold(autoCompactThreshold);\n }\n\n /**\n * Estimate token usage from conversation history.\n *\n * Uses the shared core estimator (`estimateContextTokensFromMessages`) so session display,\n * /context, auto-compact, and core execution guards reason about the same effective token state.\n * That estimator prefers the provider's actual reported token count (which includes the system\n * prompt and tool schemas) over a raw serialized-history char heuristic, falling back to the\n * serialized estimate only when no provider usage is present on the latest message.\n */\n updateFromHistory(history: TUniversalMessage[]): void {\n this.contextUsedTokens = estimateContextTokensFromMessages(history).usedTokens;\n }\n\n /** Reset token tracking */\n reset(): void {\n this.contextUsedTokens = 0;\n }\n}\n\nfunction normalizeAutoCompactThreshold(\n autoCompactThreshold: TAutoCompactThreshold | undefined,\n): TAutoCompactThreshold {\n if (autoCompactThreshold === undefined) {\n return AUTO_COMPACT_THRESHOLD;\n }\n if (autoCompactThreshold === false) {\n return false;\n }\n if (\n !Number.isFinite(autoCompactThreshold) ||\n autoCompactThreshold <= 0 ||\n autoCompactThreshold > 1\n ) {\n throw new RangeError('autoCompactThreshold must be a number greater than 0 and at most 1.');\n }\n return autoCompactThreshold;\n}\n","/**\n * Permission types — interfaces and type aliases for permission enforcement.\n */\n\nimport type { ISessionLogger } from './session-logger.js';\nimport type { IToolWithEventService, TPermissionMode, TToolArgs } from '@robota-sdk/agent-core';\nimport type { IHookTypeExecutor, ISpinner, ITerminalOutput } from '@robota-sdk/agent-core';\n\nexport type { ISpinner, ITerminalOutput };\n\n/**\n * Permission handler result:\n * - true: allow this invocation\n * - false: deny this invocation\n * - 'allow-session': allow this invocation and auto-approve this tool for the rest of the session\n * - 'allow-project': allow this invocation and persist the approval to .robota/settings.local.json\n */\nexport type TPermissionResult = boolean | 'allow-session' | 'allow-project';\n\n/**\n * Custom permission handler — called when a tool needs user approval.\n * Returns true to allow, false to deny, or 'allow-session' to remember for the session.\n */\nexport type TPermissionHandler = (\n toolName: string,\n toolArgs: TToolArgs,\n) => Promise<TPermissionResult>;\n\nexport interface IPermissionEnforcerOptions {\n sessionId: string;\n cwd: string;\n getPermissionMode: () => TPermissionMode;\n config: {\n permissions: { allow: string[]; deny: string[] };\n hooks?: Record<string, unknown>;\n };\n terminal: ITerminalOutput;\n permissionHandler?: TPermissionHandler;\n promptForApprovalFn?: (\n terminal: ITerminalOutput,\n toolName: string,\n toolArgs: TToolArgs,\n ) => Promise<TPermissionResult>;\n sessionLogger?: ISessionLogger;\n onToolExecution?: (event: {\n type: 'start' | 'end';\n toolName: string;\n toolArgs?: TToolArgs;\n success?: boolean;\n denied?: boolean;\n toolResultData?: string;\n executionId?: string;\n }) => void;\n /** Additional hook type executors (e.g. prompt, agent) beyond the core defaults. */\n hookTypeExecutors?: IHookTypeExecutor[];\n /** Absolute path to session transcript file — passed to PreToolUse hook inputs as transcript_path */\n transcriptPath?: string;\n /** Called when the user selects \"allow for project\" — persists the tool pattern to project settings. */\n onProjectAllowTool?: (toolName: string) => void;\n}\n\n/** Returned when the user denies a permission prompt. success:true prevents ToolExecutionError. */\nexport const PERMISSION_DENIED_RESULT = {\n success: true,\n data: JSON.stringify({\n success: false,\n output: '',\n error: 'Permission denied. The user did not approve this action.',\n }),\n metadata: {},\n} as const;\n\n/** Maximum chars for any single tool output. Matches Claude Code's 30K limit. */\nexport const MAX_TOOL_OUTPUT_CHARS = 30_000;\n","/**\n * Tool hook helpers — stateless utility functions for tool hook execution\n * and output truncation used by PermissionEnforcer.\n */\n\nimport { runHooks, createLogger } from '@robota-sdk/agent-core';\n\nimport { MAX_TOOL_OUTPUT_CHARS } from './permission-types.js';\n\nimport type {\n IToolResult,\n TToolParameters,\n THooksConfig,\n IHookInput,\n IHookTypeExecutor,\n} from '@robota-sdk/agent-core';\n\nconst logger = createLogger('ToolHookHelpers');\n\n/**\n * Truncate tool result data if it exceeds MAX_TOOL_OUTPUT_CHARS.\n * Uses middle-truncation: keeps first and last portions, removes middle.\n */\nexport function truncateToolResult(result: IToolResult): IToolResult {\n if (typeof result.data !== 'string') return result;\n if (result.data.length <= MAX_TOOL_OUTPUT_CHARS) return result;\n\n const halfLimit = Math.floor(MAX_TOOL_OUTPUT_CHARS / 2);\n const head = result.data.substring(0, halfLimit);\n const tail = result.data.substring(result.data.length - halfLimit);\n const originalSize = result.data.length;\n const truncatedData = `${head}\\n\\n[... output truncated: ${originalSize.toLocaleString()} chars total, showing first and last ${halfLimit.toLocaleString()} chars ...]\\n\\n${tail}`;\n\n return { ...result, data: truncatedData };\n}\n\n/** Build a hook input object for tool execution hooks */\nexport function buildHookInput(\n sessionId: string,\n cwd: string,\n toolName: string,\n parameters: TToolParameters,\n permissionMode?: string,\n transcriptPath?: string,\n): IHookInput {\n return {\n session_id: sessionId,\n cwd,\n hook_event_name: 'PreToolUse',\n tool_name: toolName,\n tool_input: parameters as Record<string, string | number | boolean | object>,\n ...(permissionMode !== undefined && { permission_mode: permissionMode }),\n ...(transcriptPath !== undefined && { transcript_path: transcriptPath }),\n };\n}\n\n/** Run PreToolUse hooks; returns a denial IToolResult if blocked, or null to proceed */\nexport async function runPreToolHook(\n hooks: Record<string, unknown> | undefined,\n hookInput: IHookInput,\n hookTypeExecutors: IHookTypeExecutor[] | undefined,\n): Promise<IToolResult | null> {\n const hookResult = await runHooks(\n hooks as THooksConfig | undefined,\n 'PreToolUse',\n hookInput,\n hookTypeExecutors,\n );\n if (hookResult.blocked) {\n return {\n success: true,\n data: JSON.stringify({\n blocked: true,\n reason: hookResult.reason ?? 'Blocked by hook',\n }),\n metadata: {},\n };\n }\n return null;\n}\n\n/** Fire PostToolUse hooks (fire and forget) */\nexport function firePostToolHook(\n hooks: Record<string, unknown> | undefined,\n hookInput: IHookInput,\n result: IToolResult,\n hookTypeExecutors: IHookTypeExecutor[] | undefined,\n): void {\n const postHookInput: IHookInput = {\n ...hookInput,\n hook_event_name: 'PostToolUse',\n tool_output: typeof result.data === 'string' ? result.data : JSON.stringify(result.data),\n };\n runHooks(\n hooks as THooksConfig | undefined,\n 'PostToolUse',\n postHookInput,\n hookTypeExecutors,\n ).catch((error) => logger.warn('hook failed', { error }));\n}\n","/**\n * PermissionEnforcer — handles tool permission checking, hook execution,\n * and tool output truncation.\n *\n * Extracted from Session to separate permission/hook concerns from\n * conversation management.\n */\n\nimport { evaluatePermission } from '@robota-sdk/agent-core';\n\nimport { PERMISSION_DENIED_RESULT } from './permission-types.js';\nimport {\n truncateToolResult,\n buildHookInput,\n runPreToolHook,\n firePostToolHook,\n} from './tool-hook-helpers.js';\n\nimport type {\n IPermissionEnforcerOptions,\n TPermissionHandler,\n TPermissionResult,\n ITerminalOutput,\n ISpinner,\n} from './permission-types.js';\nimport type { ISessionLogger, TSessionLogData } from './session-logger.js';\nimport type {\n IToolWithEventService,\n IToolResult,\n TToolParameters,\n IToolExecutionContext,\n TToolArgs,\n} from '@robota-sdk/agent-core';\n\nexport type { TPermissionHandler, TPermissionResult, ITerminalOutput, ISpinner };\nexport type { IPermissionEnforcerOptions };\n\nexport class PermissionEnforcer {\n private readonly sessionId: string;\n private readonly cwd: string;\n private readonly getPermissionMode: IPermissionEnforcerOptions['getPermissionMode'];\n private readonly config: IPermissionEnforcerOptions['config'];\n private readonly terminal: ITerminalOutput;\n private readonly permissionHandler?: TPermissionHandler;\n private readonly promptForApprovalFn?: IPermissionEnforcerOptions['promptForApprovalFn'];\n private readonly sessionLogger?: ISessionLogger;\n private readonly onToolExecution?: IPermissionEnforcerOptions['onToolExecution'];\n private readonly hookTypeExecutors?: IPermissionEnforcerOptions['hookTypeExecutors'];\n private readonly transcriptPath?: string;\n private readonly sessionAllowedTools = new Set<string>();\n private readonly onProjectAllowTool?: (toolName: string) => void;\n\n constructor(options: IPermissionEnforcerOptions) {\n this.sessionId = options.sessionId;\n this.cwd = options.cwd;\n this.getPermissionMode = options.getPermissionMode;\n this.config = options.config;\n this.terminal = options.terminal;\n this.permissionHandler = options.permissionHandler;\n this.promptForApprovalFn = options.promptForApprovalFn;\n this.sessionLogger = options.sessionLogger;\n this.onToolExecution = options.onToolExecution;\n this.hookTypeExecutors = options.hookTypeExecutors;\n this.transcriptPath = options.transcriptPath;\n this.onProjectAllowTool = options.onProjectAllowTool;\n }\n\n /** Wrap all tools with permission checking */\n wrapTools(tools: IToolWithEventService[]): IToolWithEventService[] {\n return tools.map((tool) => this.wrapToolWithPermission(tool));\n }\n\n /** Get tools that have been session-approved (via \"Allow always\" choice). */\n getSessionAllowedTools(): string[] {\n return [...this.sessionAllowedTools];\n }\n\n /** Clear all session-scoped allow rules. */\n clearSessionAllowedTools(): void {\n this.sessionAllowedTools.clear();\n }\n\n /**\n * Wrap a tool with permission checking.\n * The wrapper intercepts execute() and runs permission evaluation before delegating.\n * If denied, returns a tool result indicating the action was blocked.\n */\n private wrapToolWithPermission(tool: IToolWithEventService): IToolWithEventService {\n const enforcer = this;\n const originalExecute = tool.execute.bind(tool);\n\n const wrappedTool = Object.create(tool) as IToolWithEventService;\n wrappedTool.execute = async (\n parameters: TToolParameters,\n context?: IToolExecutionContext,\n ): Promise<IToolResult> => {\n // Must NEVER throw — if this throws, the execution round records the\n // assistant tool_use in history but never adds a tool_result, which\n // corrupts the conversation and causes a 400 error on the next API call.\n try {\n const toolName = tool.getName();\n enforcer.log('tool_call', {\n tool: toolName,\n args: parameters as Record<string, string | number | boolean | object>,\n });\n\n const hookInput = buildHookInput(\n enforcer.sessionId,\n enforcer.cwd,\n toolName,\n parameters,\n enforcer.getPermissionMode(),\n enforcer.transcriptPath,\n );\n\n const preResult = await runPreToolHook(\n enforcer.config.hooks,\n hookInput,\n enforcer.hookTypeExecutors,\n );\n if (preResult) {\n enforcer.log('tool_blocked', { tool: toolName, reason: 'hook' });\n return preResult;\n }\n\n const allowed = await enforcer.checkPermission(toolName, parameters as TToolArgs);\n if (!allowed) {\n enforcer.log('tool_denied', { tool: toolName, reason: 'permission' });\n enforcer.onToolExecution?.({\n type: 'end',\n toolName,\n toolArgs: parameters as TToolArgs,\n success: false,\n denied: true,\n executionId: context?.executionId,\n });\n return PERMISSION_DENIED_RESULT;\n }\n\n enforcer.onToolExecution?.({\n type: 'start',\n toolName,\n toolArgs: parameters as TToolArgs,\n executionId: context?.executionId,\n });\n\n const result = await originalExecute(parameters, context as IToolExecutionContext);\n\n // Truncate oversized tool output (matches 30K char limit)\n const truncatedResult = truncateToolResult(result);\n\n if (truncatedResult !== result && typeof result.data === 'string') {\n enforcer.terminal.writeLine(\n ` ⚠ Output truncated: ${result.data.length.toLocaleString()} chars total — model sees first and last 15,000 chars`,\n );\n }\n\n enforcer.onToolExecution?.({\n type: 'end',\n toolName,\n toolArgs: parameters as TToolArgs,\n success: truncatedResult.success,\n toolResultData:\n typeof truncatedResult.data === 'string'\n ? truncatedResult.data\n : JSON.stringify(truncatedResult.data),\n executionId: context?.executionId,\n });\n\n const dataSize =\n typeof truncatedResult.data === 'string'\n ? truncatedResult.data.length\n : JSON.stringify(truncatedResult.data).length;\n enforcer.log('tool_result', {\n tool: toolName,\n success: truncatedResult.success,\n dataChars: dataSize,\n truncated: truncatedResult !== result,\n });\n firePostToolHook(\n enforcer.config.hooks,\n hookInput,\n truncatedResult,\n enforcer.hookTypeExecutors,\n );\n return truncatedResult;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return {\n success: true,\n data: JSON.stringify({ success: false, output: '', error: message }),\n metadata: {},\n };\n }\n };\n\n return wrappedTool;\n }\n\n /** Evaluate permission for a tool call using the current mode and config */\n async checkPermission(toolName: string, toolArgs: TToolArgs): Promise<boolean> {\n const decision = evaluatePermission(toolName, toolArgs, this.getPermissionMode(), {\n allow: this.config.permissions.allow,\n deny: this.config.permissions.deny,\n });\n\n if (decision === 'auto') return true;\n if (decision === 'deny') return false;\n\n // Check session-scoped allow list before prompting\n if (this.sessionAllowedTools.has(toolName)) return true;\n\n // 'approve' — prompt the user via custom handler, injected approval fn, or deny\n if (this.permissionHandler) {\n const result = await this.permissionHandler(toolName, toolArgs);\n if (result === 'allow-session') {\n this.sessionAllowedTools.add(toolName);\n return true;\n }\n if (result === 'allow-project') {\n this.sessionAllowedTools.add(toolName);\n this.onProjectAllowTool?.(toolName);\n return true;\n }\n return result;\n }\n if (this.promptForApprovalFn) {\n const result = await this.promptForApprovalFn(this.terminal, toolName, toolArgs);\n if (result === 'allow-session') {\n this.sessionAllowedTools.add(toolName);\n return true;\n }\n if (result === 'allow-project') {\n this.sessionAllowedTools.add(toolName);\n this.onProjectAllowTool?.(toolName);\n return true;\n }\n return result;\n }\n // No approval mechanism available — deny by default\n return false;\n }\n\n /** Delegate session event to the injected logger. */\n private log(event: string, data: TSessionLogData): void {\n this.sessionLogger?.log(this.sessionId, event, data);\n }\n}\n","import { Robota } from '@robota-sdk/agent-core';\n\nimport { CompactionOrchestrator } from './compaction-orchestrator.js';\nimport { ContextWindowTracker } from './context-window-tracker.js';\nimport { PermissionEnforcer } from './permission-enforcer.js';\n\nimport type { ISessionOptions } from './session-types.js';\nimport type {\n IAgentConfig,\n IAIProvider,\n IToolWithEventService,\n TPermissionMode,\n} from '@robota-sdk/agent-core';\n\nexport function buildPermissionEnforcer(\n options: ISessionOptions,\n sessionId: string,\n cwd: string,\n getPermissionMode: () => TPermissionMode,\n transcriptPath: string | undefined,\n): PermissionEnforcer {\n return new PermissionEnforcer({\n sessionId,\n cwd,\n getPermissionMode,\n config: {\n permissions: options.permissions ?? { allow: [], deny: [] },\n hooks: options.hooks,\n },\n terminal: options.terminal,\n permissionHandler: options.permissionHandler,\n promptForApprovalFn: options.promptForApproval,\n sessionLogger: options.sessionLogger,\n onToolExecution: options.onToolExecution,\n hookTypeExecutors: options.hookTypeExecutors,\n transcriptPath,\n onProjectAllowTool: options.onProjectAllowTool,\n });\n}\n\nexport function buildSessionTrackers(\n options: ISessionOptions,\n model: string,\n sessionId: string,\n cwd: string,\n): { contextTracker: ContextWindowTracker; compactionOrchestrator: CompactionOrchestrator } {\n const contextTracker = new ContextWindowTracker(\n model,\n options.contextMaxTokens,\n options.autoCompactThreshold,\n );\n const compactionOrchestrator = new CompactionOrchestrator({\n sessionId,\n cwd,\n model,\n hooks: options.hooks,\n compactInstructions: options.compactInstructions,\n hookTypeExecutors: options.hookTypeExecutors,\n });\n return { contextTracker, compactionOrchestrator };\n}\n\nexport function buildRobota(\n options: ISessionOptions,\n permissionEnforcer: PermissionEnforcer,\n tools: IToolWithEventService[],\n provider: IAIProvider,\n model: string,\n systemMessage: string,\n): Robota {\n const wrappedTools = permissionEnforcer.wrapTools(tools);\n const agentConfig: IAgentConfig = {\n name: options.agentName ?? 'agent',\n aiProviders: [provider],\n defaultModel: {\n provider: provider.name,\n model,\n systemMessage,\n ...(options.effort !== undefined && { effort: options.effort }),\n },\n systemMessage,\n tools: wrappedTools,\n logging: { enabled: false },\n ...(options.providerTimeout !== undefined && { timeout: options.providerTimeout }),\n ...(options.responseFormat ? { responseFormat: options.responseFormat } : {}),\n };\n return new Robota(agentConfig);\n}\n","/**\n * Session history operations — compaction and persistence helpers.\n *\n * Extracted from Session to keep session.ts under the 300-line limit.\n * Each function receives its dependencies explicitly.\n */\n\nimport { runHooks, createLogger } from '@robota-sdk/agent-core';\n\nimport type { CompactionOrchestrator } from './compaction-orchestrator.js';\nimport type { ContextWindowTracker } from './context-window-tracker.js';\nimport type { TSessionLogData } from './session-logger.js';\nimport type { ISessionRecord, ISessionStore } from './session-store.js';\nimport type { ICompactEvent, TCompactTrigger } from './session-types.js';\nimport type { IToolSchema } from '@robota-sdk/agent-core';\nimport type { Robota } from '@robota-sdk/agent-core';\nimport type {\n IAIProvider,\n THooksConfig,\n IHookInput,\n IHookTypeExecutor,\n} from '@robota-sdk/agent-core';\n\nconst logger = createLogger('SessionHistoryOps');\n\n/** Dependencies for compact() */\nexport interface ICompactContext {\n sessionId: string;\n cwd: string;\n systemMessage: string;\n robota: Robota;\n aiProvider: IAIProvider;\n compactionOrchestrator: CompactionOrchestrator;\n contextTracker: ContextWindowTracker;\n hooks: Record<string, unknown> | undefined;\n hookTypeExecutors: IHookTypeExecutor[] | undefined;\n onCompactCallback: ((summary: string) => void) | undefined;\n onCompactEventCallback: ((event: ICompactEvent) => void) | undefined;\n trigger: TCompactTrigger;\n log: (event: string, data: TSessionLogData) => void;\n}\n\n/**\n * Summarize the conversation to free context space.\n *\n * @param instructions - Optional focus instructions for the summary\n * @param ctx - Session state and callbacks\n */\nexport async function compact(\n instructions: string | undefined,\n ctx: ICompactContext,\n): Promise<void> {\n const history = ctx.robota.getHistory();\n if (history.length === 0) return;\n\n ctx.contextTracker.updateFromHistory(history);\n const before = ctx.contextTracker.getContextState();\n\n // Exclude system messages from compaction — they are preserved and re-injected after\n const nonSystemHistory = history.filter((msg) => msg.role !== 'system');\n const summary = await ctx.compactionOrchestrator.compact(\n ctx.aiProvider,\n nonSystemHistory,\n instructions,\n );\n\n // Clear history, re-inject system message, then inject summary.\n // System message must persist across compactions — it contains project context\n // (cwd, AGENTS.md, CLAUDE.md) that the AI needs for every response.\n ctx.robota.clearHistory();\n ctx.robota.injectMessage('system', ctx.systemMessage);\n ctx.robota.injectMessage('assistant', `[Context Summary]\\n${summary}`);\n\n // Reset token tracking based on the new shorter history\n ctx.contextTracker.updateFromHistory(ctx.robota.getHistory());\n\n // Fire PostCompact hook after history replacement is complete\n const postHookInput: IHookInput = {\n session_id: ctx.sessionId,\n cwd: ctx.cwd,\n hook_event_name: 'PostCompact',\n trigger: ctx.trigger,\n compact_summary: summary,\n };\n runHooks(\n ctx.hooks as THooksConfig | undefined,\n 'PostCompact',\n postHookInput,\n ctx.hookTypeExecutors,\n ).catch((error) => logger.warn('hook failed', { error }));\n\n // Notify via callback after compaction is fully complete\n const after = ctx.contextTracker.getContextState();\n ctx.log('context_compact', {\n trigger: ctx.trigger,\n before,\n after,\n });\n ctx.onCompactEventCallback?.({ trigger: ctx.trigger, before, after });\n if (ctx.onCompactCallback) {\n ctx.onCompactCallback(summary);\n }\n}\n\n/** Dependencies for persistSession() */\nexport interface IPersistContext {\n sessionId: string;\n cwd: string;\n systemPrompt: string;\n toolSchemas: IToolSchema[];\n sessionStore: ISessionStore;\n robota: Robota;\n getFullHistory: () => Array<{\n id: string;\n timestamp: Date;\n category: string;\n type: string;\n data?: unknown;\n }>;\n}\n\n/** Persist the current session to the store */\nexport function persistSession(ctx: IPersistContext): void {\n const history = ctx.robota.getHistory();\n const now = new Date().toISOString();\n\n const existing = ctx.sessionStore.load(ctx.sessionId);\n\n const record: ISessionRecord = {\n id: ctx.sessionId,\n name: existing?.name,\n cwd: ctx.cwd,\n createdAt: existing?.createdAt ?? now,\n updatedAt: now,\n messages: history,\n history: ctx.getFullHistory(),\n systemPrompt: ctx.systemPrompt,\n toolSchemas: ctx.toolSchemas,\n };\n\n ctx.sessionStore.save(record);\n}\n","/**\n * Session lifecycle helpers — provider configuration and session start hooks.\n *\n * Extracted from Session to keep session.ts under the 300-line limit.\n * All functions receive their dependencies explicitly.\n */\n\nimport { runHooks, createLogger } from '@robota-sdk/agent-core';\n\nimport type { TSessionLogData } from './session-logger.js';\nimport type { ISessionOptions } from './session-types.js';\nimport type {\n IAIProvider,\n TSessionEndReason,\n THooksConfig,\n IHookInput,\n IHookTypeExecutor,\n} from '@robota-sdk/agent-core';\n\nconst logger = createLogger('SessionLifecycle');\n\n/**\n * Configure provider-specific features: streaming, web tools, server tool logging.\n * Mutates the provider object in-place.\n */\nexport function configureProvider(\n provider: IAIProvider,\n _options: ISessionOptions,\n log: (event: string, data: TSessionLogData) => void,\n): void {\n provider.configureNativeWebTools?.({ webSearch: true });\n\n // Wire server tool logging\n if ('onServerToolUse' in provider) {\n (\n provider as { onServerToolUse?: (name: string, input: Record<string, string>) => void }\n ).onServerToolUse = (name: string, input: Record<string, string>) => {\n log('server_tool', { tool: name, ...input });\n };\n }\n}\n\n/**\n * Fire SessionStart hook asynchronously.\n * Calls onStdout when the hook produces stdout (used to seed the first run()).\n */\nexport function fireSessionStartHook(\n sessionId: string,\n cwd: string,\n hooks: Record<string, unknown> | undefined,\n hookTypeExecutors: IHookTypeExecutor[] | undefined,\n onStdout: (stdout: string) => void,\n permissionMode?: string,\n transcriptPath?: string,\n): void {\n const hookInput: IHookInput = {\n session_id: sessionId,\n cwd,\n hook_event_name: 'SessionStart',\n ...(permissionMode !== undefined && { permission_mode: permissionMode }),\n ...(transcriptPath !== undefined && { transcript_path: transcriptPath }),\n env: {\n CLAUDE_PROJECT_DIR: cwd,\n CLAUDE_SESSION_ID: sessionId,\n },\n };\n runHooks(hooks as THooksConfig | undefined, 'SessionStart', hookInput, hookTypeExecutors)\n .then((result) => {\n if (result.stdout) {\n onStdout(result.stdout);\n }\n })\n .catch((error) => logger.warn('SessionStart hook failed', { error }));\n}\n\n/** Fire SessionEnd hook and wait for hook completion before process exit. */\nexport async function fireSessionEndHook(\n sessionId: string,\n cwd: string,\n reason: TSessionEndReason,\n hooks: Record<string, unknown> | undefined,\n hookTypeExecutors: IHookTypeExecutor[] | undefined,\n permissionMode?: string,\n transcriptPath?: string,\n): Promise<void> {\n const hookInput: IHookInput = {\n session_id: sessionId,\n cwd,\n hook_event_name: 'SessionEnd',\n reason,\n ...(permissionMode !== undefined && { permission_mode: permissionMode }),\n ...(transcriptPath !== undefined && { transcript_path: transcriptPath }),\n env: {\n CLAUDE_PROJECT_DIR: cwd,\n CLAUDE_SESSION_ID: sessionId,\n },\n };\n await runHooks(hooks as THooksConfig | undefined, 'SessionEnd', hookInput, hookTypeExecutors);\n}\n","import type { ISessionOptions } from './session-types.js';\nimport type { TExecutionEventData, TToolArgs } from '@robota-sdk/agent-core';\n\nconst UNKNOWN_TOOL_ERROR_CODE = 'unknown_tool';\n\ntype TToolExecutionCallback = NonNullable<ISessionOptions['onToolExecution']>;\n\nexport interface IToolExecutionBridge {\n knownToolNames: ReadonlySet<string>;\n unknownToolCallIds: Set<string>;\n onToolExecution?: TToolExecutionCallback;\n}\n\nexport function createToolExecutionBridge(options: {\n knownToolNames: readonly string[];\n onToolExecution?: TToolExecutionCallback;\n}): IToolExecutionBridge {\n return {\n knownToolNames: new Set(options.knownToolNames),\n unknownToolCallIds: new Set<string>(),\n ...(options.onToolExecution && { onToolExecution: options.onToolExecution }),\n };\n}\n\nexport function forwardToolExecutionEvent(\n bridge: IToolExecutionBridge,\n event: string,\n data: TExecutionEventData,\n): void {\n if (!bridge.onToolExecution) return;\n if (event === 'tool_execution_request') {\n forwardUnknownToolStart(bridge, data);\n return;\n }\n if (event === 'tool_execution_result') {\n forwardUnknownToolEnd(bridge, data);\n }\n}\n\nfunction forwardUnknownToolStart(bridge: IToolExecutionBridge, data: TExecutionEventData): void {\n const toolName = getString(data.toolName);\n const toolCallId = getString(data.toolCallId);\n if (!toolName || !toolCallId || bridge.knownToolNames.has(toolName)) return;\n\n bridge.unknownToolCallIds.add(toolCallId);\n bridge.onToolExecution?.({\n type: 'start',\n toolName,\n toolArgs: toToolArgs(data.parameters),\n });\n}\n\nfunction forwardUnknownToolEnd(bridge: IToolExecutionBridge, data: TExecutionEventData): void {\n const toolName = getString(data.toolName);\n const toolCallId = getString(data.toolCallId);\n if (!toolName || !toolCallId) return;\n\n const metadata = getRecord(data.metadata);\n const isUnknown =\n bridge.unknownToolCallIds.has(toolCallId) || metadata?.errorCode === UNKNOWN_TOOL_ERROR_CODE;\n if (!isUnknown) return;\n\n bridge.unknownToolCallIds.delete(toolCallId);\n const error = getString(data.error) ?? `Tool \"${toolName}\" is not registered.`;\n bridge.onToolExecution?.({\n type: 'end',\n toolName,\n success: false,\n toolResultData: JSON.stringify({\n success: false,\n error,\n errorCode: UNKNOWN_TOOL_ERROR_CODE,\n requestedTool: getString(metadata?.requestedTool) ?? toolName,\n availableTools: getStringArray(metadata?.availableTools),\n }),\n });\n}\n\nfunction toToolArgs(value: unknown): TToolArgs | undefined {\n const record = getRecord(value);\n if (!record) return undefined;\n\n const args: TToolArgs = {};\n for (const [key, item] of Object.entries(record)) {\n if (\n typeof item === 'string' ||\n typeof item === 'number' ||\n typeof item === 'boolean' ||\n (typeof item === 'object' && item !== null)\n ) {\n args[key] = item;\n }\n }\n return args;\n}\n\nfunction getString(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\nfunction getRecord(value: unknown): Record<string, unknown> | undefined {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined;\n}\n\nfunction getStringArray(value: unknown): string[] {\n if (!Array.isArray(value)) return [];\n return value.filter((item): item is string => typeof item === 'string');\n}\n","/**\n * Session run — core execution logic for a single agent turn.\n *\n * Extracted from Session to keep session.ts under the 300-line limit.\n * Stateless: all mutable state is passed in via IRunContext.\n */\n\nimport {\n CONTEXT_ESTIMATE_CHARS_PER_TOKEN,\n createLogger,\n createUserMessage,\n getProviderCapabilities,\n runHooks,\n} from '@robota-sdk/agent-core';\n\nimport {\n createToolExecutionBridge,\n forwardToolExecutionEvent,\n} from './session-tool-execution-bridge.js';\n\nimport type { ContextWindowTracker } from './context-window-tracker.js';\nimport type { TSessionLogData } from './session-logger.js';\nimport type { ISessionOptions } from './session-types.js';\nimport type {\n IAIProvider,\n IContextWindowState,\n THooksConfig,\n IHookTypeExecutor,\n TTextDeltaCallback,\n} from '@robota-sdk/agent-core';\nimport type { Robota } from '@robota-sdk/agent-core';\n\nconst logger = createLogger('SessionRun');\n\n/** Dependencies injected by Session.run() */\nexport interface IRunContext {\n sessionId: string;\n cwd: string;\n model: string;\n /** Current permission mode — passed to all hook inputs as permission_mode */\n permissionMode?: string;\n /** Absolute path to session transcript file — passed to all hook inputs as transcript_path */\n transcriptPath?: string;\n robota: Robota;\n aiProvider: IAIProvider;\n contextTracker: ContextWindowTracker;\n hooks: Record<string, unknown> | undefined;\n hookTypeExecutors: IHookTypeExecutor[] | undefined;\n sessionStartStdout: string;\n log: (event: string, data: TSessionLogData) => void;\n compact: () => Promise<void>;\n persistSession: () => void;\n getSessionStore: () => boolean;\n clearSessionStartStdout: () => void;\n maxTurns?: number;\n onTextDelta?: TTextDeltaCallback;\n onContextUpdate?: (state: IContextWindowState) => void;\n onToolExecution?: ISessionOptions['onToolExecution'];\n knownToolNames?: readonly string[];\n}\n\n/**\n * Execute a single agent turn: run hooks, send message to AI, log results.\n *\n * @param message - The processed message to send to the AI\n * @param rawInput - Optional raw user input (used for hook prompt field)\n * @param ctx - Session state and callbacks\n * @param abortSignal - AbortSignal from the session's AbortController\n */\nexport async function executeRun(\n message: string,\n rawInput: string | undefined,\n ctx: IRunContext,\n abortSignal: AbortSignal,\n): Promise<string> {\n // Auto-compact BEFORE processing the new message (not after).\n // This prevents compaction from interfering with the current response stream.\n ctx.contextTracker.updateFromHistory(ctx.robota.getHistory());\n if (ctx.contextTracker.shouldAutoCompact()) {\n // Providers store onTextDelta as an instance property for their own internal streaming.\n // Compaction calls provider.chat() without passing onTextDelta in options, so the\n // provider falls back to this.onTextDelta. Temporarily clearing it prevents compaction\n // summary text from streaming to the UI. This workaround stays until provider packages\n // remove the instance-level onTextDelta property.\n const provider = ctx.aiProvider as { onTextDelta?: unknown };\n const savedDelta = provider.onTextDelta;\n provider.onTextDelta = undefined;\n try {\n await ctx.compact();\n } finally {\n provider.onTextDelta = savedDelta;\n }\n }\n\n ctx.log('user', { content: message });\n\n // Fire UserPromptSubmit hook before AI processes input\n const hookResult = await runHooks(\n ctx.hooks as THooksConfig | undefined,\n 'UserPromptSubmit',\n {\n session_id: ctx.sessionId,\n cwd: ctx.cwd,\n hook_event_name: 'UserPromptSubmit',\n user_message: rawInput ?? message,\n prompt: rawInput ?? message,\n ...(ctx.permissionMode !== undefined && { permission_mode: ctx.permissionMode }),\n ...(ctx.transcriptPath !== undefined && { transcript_path: ctx.transcriptPath }),\n env: {\n CLAUDE_PROJECT_DIR: ctx.cwd,\n CLAUDE_SESSION_ID: ctx.sessionId,\n },\n },\n ctx.hookTypeExecutors,\n );\n\n // Inject hook stdout into user message (e.g., plugin path info)\n const hookStdout = [ctx.sessionStartStdout, hookResult.stdout].filter(Boolean).join('\\n');\n const enrichedMessage = hookStdout\n ? `<system-reminder>\\n${hookStdout}\\n</system-reminder>\\n${message}`\n : message;\n // Clear sessionStart stdout after first injection\n ctx.clearSessionStartStdout();\n\n const history = ctx.robota.getHistory();\n const historyJson = JSON.stringify(history);\n const providerCapabilities = getProviderCapabilities(ctx.aiProvider);\n ctx.log('pre_run', {\n historyLength: history.length,\n historyChars: historyJson.length,\n historyEstTokens: Math.ceil(historyJson.length / CONTEXT_ESTIMATE_CHARS_PER_TOKEN),\n input: enrichedMessage,\n history,\n model: ctx.model,\n provider: ctx.aiProvider.name,\n maxTokens: ctx.contextTracker.getContextState().maxTokens,\n nativeWebSearchSupported: providerCapabilities.nativeWebTools.webSearch.supported,\n nativeWebSearchEnabled: providerCapabilities.nativeWebTools.webSearch.enabled,\n nativeWebFetchSupported: providerCapabilities.nativeWebTools.webFetch.supported,\n nativeWebFetchEnabled: providerCapabilities.nativeWebTools.webFetch.enabled,\n });\n ctx.contextTracker.updateFromHistory([...history, createUserMessage(enrichedMessage)]);\n ctx.onContextUpdate?.(ctx.contextTracker.getContextState());\n\n let response: string;\n try {\n const toolExecutionBridge = createToolExecutionBridge({\n knownToolNames: ctx.knownToolNames ?? [],\n ...(ctx.onToolExecution && { onToolExecution: ctx.onToolExecution }),\n });\n const onTextDelta = ctx.onTextDelta\n ? (delta: string): void => {\n ctx.log('text_delta', { delta });\n ctx.onTextDelta?.(delta);\n }\n : undefined;\n\n response = await ctx.robota.run(enrichedMessage, {\n signal: abortSignal,\n maxExecutionRounds: ctx.maxTurns ?? 0,\n onExecutionEvent: (event, data) => {\n ctx.log(event, data as TSessionLogData);\n forwardToolExecutionEvent(toolExecutionBridge, event, data);\n // BEHAVIOR-002: recompute and emit context per agentic round so the status bar\n // climbs live during a turn instead of jumping once at completion. The agent loop\n // runs entirely inside this single robota.run() call; assistant_message_committed\n // fires once per round with the round's usage already committed to history, which is\n // the right cadence — frequent enough to feel live, sparse enough to avoid render flooding.\n if (event === 'assistant_message_committed') {\n ctx.contextTracker.updateFromHistory(ctx.robota.getHistory());\n ctx.onContextUpdate?.(ctx.contextTracker.getContextState());\n }\n },\n ...(onTextDelta && { onTextDelta }),\n });\n\n // If execution was interrupted (abort fired during execution),\n // throw AbortError so the caller (useSubmitHandler) shows \"Cancelled.\"\n if (abortSignal.aborted) {\n throw new DOMException('Aborted', 'AbortError');\n }\n } catch (error) {\n ctx.log('error', {\n message: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? (error.stack ?? '') : '',\n historyLength: ctx.robota.getHistory().length,\n });\n runHooks(\n ctx.hooks as THooksConfig | undefined,\n 'StopFailure',\n {\n session_id: ctx.sessionId,\n cwd: ctx.cwd,\n hook_event_name: 'StopFailure',\n reason: error instanceof Error ? error.message : String(error),\n stop_hook_active: false,\n ...(ctx.permissionMode !== undefined && { permission_mode: ctx.permissionMode }),\n ...(ctx.transcriptPath !== undefined && { transcript_path: ctx.transcriptPath }),\n env: {\n CLAUDE_PROJECT_DIR: ctx.cwd,\n CLAUDE_SESSION_ID: ctx.sessionId,\n },\n },\n ctx.hookTypeExecutors,\n ).catch((error) => logger.warn('hook failed', { error }));\n throw error;\n }\n\n // Log the response and full history structure\n const postHistory = ctx.robota.getHistory();\n const historyStructure = postHistory.map((msg) => {\n const hasToolCalls =\n 'toolCalls' in msg && Array.isArray(msg.toolCalls) && msg.toolCalls.length > 0;\n const toolCallNames = hasToolCalls\n ? (msg.toolCalls as Array<{ function: { name: string } }>).map((tc) => tc.function.name)\n : [];\n return {\n role: msg.role,\n contentLength: typeof msg.content === 'string' ? msg.content.length : 0,\n hasToolCalls,\n toolCallNames,\n ...(msg.metadata ? { metadata: msg.metadata } : {}),\n };\n });\n ctx.log('assistant', {\n content: response,\n historyLength: postHistory.length,\n estimatedChars: JSON.stringify(postHistory).length,\n history: postHistory,\n historyStructure,\n });\n\n // Update token usage from the latest assistant message metadata\n ctx.contextTracker.updateFromHistory(postHistory);\n\n const ctxState = ctx.contextTracker.getContextState();\n ctx.onContextUpdate?.(ctxState);\n ctx.log('context', {\n maxTokens: ctxState.maxTokens,\n usedTokens: ctxState.usedTokens,\n usedPercentage: ctxState.usedPercentage,\n remainingPercentage: ctxState.remainingPercentage,\n });\n\n // Fire Stop hook after AI response is complete (informational, fire and forget)\n runHooks(\n ctx.hooks as THooksConfig | undefined,\n 'Stop',\n {\n session_id: ctx.sessionId,\n cwd: ctx.cwd,\n hook_event_name: 'Stop',\n response: response.substring(0, 500),\n last_assistant_message: response,\n stop_hook_active: false,\n ...(ctx.permissionMode !== undefined && { permission_mode: ctx.permissionMode }),\n ...(ctx.transcriptPath !== undefined && { transcript_path: ctx.transcriptPath }),\n env: {\n CLAUDE_PROJECT_DIR: ctx.cwd,\n CLAUDE_SESSION_ID: ctx.sessionId,\n },\n },\n ctx.hookTypeExecutors,\n ).catch((error) => logger.warn('hook failed', { error }));\n\n if (ctx.getSessionStore()) {\n ctx.persistSession();\n }\n\n return response;\n}\n","import { TRUST_TO_MODE } from '@robota-sdk/agent-core';\n\nimport { SessionBase } from './session-base.js';\nimport {\n buildPermissionEnforcer,\n buildRobota,\n buildSessionTrackers,\n} from './session-components.js';\nimport { compact, persistSession } from './session-history-ops.js';\nimport {\n configureProvider,\n fireSessionEndHook,\n fireSessionStartHook,\n} from './session-lifecycle.js';\nimport { executeRun } from './session-run.js';\n\nimport type { CompactionOrchestrator } from './compaction-orchestrator.js';\nimport type { ContextWindowTracker } from './context-window-tracker.js';\nimport type { PermissionEnforcer } from './permission-enforcer.js';\nimport type {\n TPermissionHandler,\n TPermissionResult,\n ITerminalOutput,\n ISpinner,\n} from './permission-types.js';\nimport type { ISessionLogger, TSessionLogData } from './session-logger.js';\nimport type { IRunContext } from './session-run.js';\nimport type { ISessionStore } from './session-store.js';\nimport type {\n ICompactEvent,\n ISessionOptions,\n ISessionShutdownOptions,\n TCompactTrigger,\n} from './session-types.js';\nimport type {\n IAIProvider,\n IContextWindowState,\n IToolSchema,\n TPermissionMode,\n IHookTypeExecutor,\n} from '@robota-sdk/agent-core';\nimport type { Robota } from '@robota-sdk/agent-core';\n\nexport type {\n ICompactEvent,\n TPermissionHandler,\n TPermissionResult,\n ITerminalOutput,\n ISpinner,\n ISessionOptions,\n ISessionShutdownOptions,\n TCompactTrigger,\n};\nexport type { TAutoCompactThreshold } from './context-window-tracker.js';\n\nconst ID_RADIX = 36;\nconst ID_RANDOM_LENGTH = 9;\n\n/** Wraps a Robota agent with project context, permission state, and optional persistence. */\nexport class Session extends SessionBase {\n protected readonly robota: Robota;\n protected readonly permissionEnforcer: PermissionEnforcer;\n protected readonly contextTracker: ContextWindowTracker;\n protected permissionMode: TPermissionMode;\n protected activePresetId: string;\n protected parallelSubagentsEnabled: boolean;\n protected readonly sessionId: string;\n protected aiProvider: IAIProvider;\n protected readonly toolSchemas: IToolSchema[];\n protected model: string;\n protected systemMessage: string;\n protected messageCount = 0;\n protected abortController: AbortController | null = null;\n private readonly terminal: ITerminalOutput;\n private readonly sessionStore?: ISessionStore;\n private readonly cwd: string;\n private readonly hooks?: Record<string, unknown>;\n private readonly hookTypeExecutors?: IHookTypeExecutor[];\n private readonly onTextDeltaCallback?: (delta: string) => void;\n private readonly onContextUpdateCallback?: (state: IContextWindowState) => void;\n private readonly onToolExecutionCallback?: ISessionOptions['onToolExecution'];\n private readonly onCompactCallback?: (summary: string) => void;\n private readonly onCompactEventCallback?: ISessionOptions['onCompactEvent'];\n private readonly sessionLogger?: ISessionLogger;\n private readonly maxTurns?: number;\n private readonly compactionOrchestrator: CompactionOrchestrator;\n private shutdownPromise: Promise<void> | null = null;\n /** Stdout collected from SessionStart hooks, injected on first run(). */\n private sessionStartStdout = '';\n /** Absolute path to the session transcript file, if file-backed storage is active. */\n private readonly transcriptPath: string | undefined;\n\n constructor(options: ISessionOptions) {\n super();\n const { tools, provider, systemMessage } = options;\n\n this.terminal = options.terminal;\n this.sessionStore = options.sessionStore;\n this.systemMessage = systemMessage;\n this.toolSchemas = tools.map((tool) => tool.schema);\n this.cwd = process.cwd();\n this.sessionLogger = options.sessionLogger;\n this.hooks = options.hooks;\n this.hookTypeExecutors = options.hookTypeExecutors;\n this.onTextDeltaCallback = options.onTextDelta;\n this.onContextUpdateCallback = options.onContextUpdate;\n this.onToolExecutionCallback = options.onToolExecution;\n this.onCompactCallback = options.onCompact;\n this.onCompactEventCallback = options.onCompactEvent;\n this.maxTurns = options.maxTurns;\n this.model = options.model ?? 'claude-sonnet-4-5';\n this.sessionId =\n options.sessionId ??\n `session_${Date.now()}_${Math.random().toString(ID_RADIX).substr(2, ID_RANDOM_LENGTH)}`;\n this.permissionMode =\n options.permissionMode ??\n (options.defaultTrustLevel ? TRUST_TO_MODE[options.defaultTrustLevel] : undefined) ??\n 'default';\n this.activePresetId = options.activePresetId ?? 'default';\n // PRESET-016: default true preserves the current behavior — subagent dispatch is allowed\n // unless a preset explicitly disables it.\n this.parallelSubagentsEnabled = options.enableParallelSubagents ?? true;\n this.transcriptPath = options.sessionStore?.getFilePath?.(this.sessionId);\n this.log('session_init', {\n cwd: this.cwd,\n systemPromptLength: systemMessage.length,\n systemPrompt: systemMessage,\n toolSchemas: this.toolSchemas,\n model: this.model,\n provider: provider.name,\n });\n this.aiProvider = provider;\n configureProvider(provider, options, (event, data) => this.log(event, data));\n this.permissionEnforcer = buildPermissionEnforcer(\n options,\n this.sessionId,\n this.cwd,\n () => this.permissionMode,\n this.transcriptPath,\n );\n const { contextTracker, compactionOrchestrator } = buildSessionTrackers(\n options,\n this.model,\n this.sessionId,\n this.cwd,\n );\n this.contextTracker = contextTracker;\n this.compactionOrchestrator = compactionOrchestrator;\n this.robota = buildRobota(\n options,\n this.permissionEnforcer,\n tools,\n provider,\n this.model,\n systemMessage,\n );\n fireSessionStartHook(\n this.sessionId,\n this.cwd,\n this.hooks,\n this.hookTypeExecutors,\n (stdout) => void (this.sessionStartStdout = stdout),\n this.permissionMode,\n this.transcriptPath,\n );\n }\n\n async run(message: string, rawInput?: string): Promise<string> {\n this.abortController = new AbortController();\n const { signal } = this.abortController;\n try {\n const response = await executeRun(message, rawInput, this.buildRunContext(), signal);\n this.messageCount += 1;\n return response;\n } finally {\n this.abortController = null;\n }\n }\n\n private log(event: string, data: TSessionLogData): void {\n this.sessionLogger?.log(this.sessionId, event, data);\n }\n\n private persistSessionInternal(): void {\n if (!this.sessionStore) return;\n persistSession({\n sessionId: this.sessionId,\n cwd: this.cwd,\n systemPrompt: this.systemMessage,\n toolSchemas: this.toolSchemas,\n sessionStore: this.sessionStore,\n robota: this.robota,\n getFullHistory: () => this.getFullHistory(),\n });\n }\n\n /** Gracefully end the session and fire SessionEnd hooks once. */\n shutdown(options: ISessionShutdownOptions = {}): Promise<void> {\n if (this.shutdownPromise) return this.shutdownPromise;\n const reason = options.reason ?? 'other';\n this.shutdownPromise = (async () => {\n this.abort();\n this.log('session_shutdown', { reason });\n this.persistSessionInternal();\n await fireSessionEndHook(\n this.sessionId,\n this.cwd,\n reason,\n this.hooks,\n this.hookTypeExecutors,\n this.permissionMode,\n this.transcriptPath,\n );\n })();\n return this.shutdownPromise;\n }\n\n swapProvider(newProvider: IAIProvider, model: string): void {\n this.robota.swapDefaultProvider(newProvider, model);\n newProvider.configureNativeWebTools?.({ webSearch: true });\n if ('onServerToolUse' in newProvider) {\n (\n newProvider as { onServerToolUse?: (name: string, input: Record<string, string>) => void }\n ).onServerToolUse = (name: string, input: Record<string, string>) =>\n this.log('server_tool', { tool: name, ...input });\n }\n this.aiProvider = newProvider;\n }\n\n async compact(instructions?: string, trigger: TCompactTrigger = 'manual'): Promise<void> {\n await compact(instructions, {\n sessionId: this.sessionId,\n cwd: this.cwd,\n systemMessage: this.systemMessage,\n robota: this.robota,\n aiProvider: this.aiProvider,\n compactionOrchestrator: this.compactionOrchestrator,\n contextTracker: this.contextTracker,\n hooks: this.hooks,\n hookTypeExecutors: this.hookTypeExecutors,\n onCompactCallback: this.onCompactCallback,\n onCompactEventCallback: this.onCompactEventCallback,\n trigger,\n log: (event, data) => this.log(event, data),\n });\n }\n\n private buildRunContext(): IRunContext {\n return {\n sessionId: this.sessionId,\n cwd: this.cwd,\n model: this.model,\n robota: this.robota,\n aiProvider: this.aiProvider,\n contextTracker: this.contextTracker,\n hooks: this.hooks,\n hookTypeExecutors: this.hookTypeExecutors,\n sessionStartStdout: this.sessionStartStdout,\n log: (event: string, data: TSessionLogData) => this.log(event, data),\n compact: () => this.compact(undefined, 'auto'),\n persistSession: () => this.persistSessionInternal(),\n getSessionStore: () => !!this.sessionStore,\n clearSessionStartStdout: () => void (this.sessionStartStdout = ''),\n permissionMode: this.permissionMode,\n transcriptPath: this.transcriptPath,\n maxTurns: this.maxTurns,\n onTextDelta: this.onTextDeltaCallback,\n onContextUpdate: this.onContextUpdateCallback,\n onToolExecution: this.onToolExecutionCallback,\n knownToolNames: this.toolSchemas.map((tool) => tool.name),\n };\n }\n}\n","/**\n * Session Logger — pluggable logging interface for session events.\n *\n * ISessionLogger defines the contract. FileSessionLogger is the default\n * implementation that writes JSONL to disk. Consumers can implement their\n * own (e.g., remote, database, silent) and inject via Session constructor.\n */\n\nimport { createHash } from 'node:crypto';\nimport { mkdirSync, appendFileSync, existsSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\n/** Session log event data — extensible record of event metadata. */\nexport type TSessionLogValue = string | number | boolean | object | null | undefined;\nexport type TSessionLogData = Record<string, TSessionLogValue>;\n\nexport interface IExternalPayloadReference {\n kind: 'external-payload';\n encoding: 'json';\n sha256: string;\n byteLength: number;\n relativePath: string;\n}\n\nexport interface IFileSessionLoggerOptions {\n externalPayloadThresholdBytes?: number;\n redactedValue?: string;\n}\n\nconst BYTES_PER_KIB = 1024;\nconst DEFAULT_EXTERNAL_PAYLOAD_THRESHOLD_KIB = 32;\nconst DEFAULT_EXTERNAL_PAYLOAD_THRESHOLD_BYTES =\n DEFAULT_EXTERNAL_PAYLOAD_THRESHOLD_KIB * BYTES_PER_KIB;\nconst DEFAULT_REDACTED_VALUE = '[REDACTED]';\nconst SENSITIVE_KEY_PATTERN =\n /^(api[-_]?key|authorization|access[-_]?token|refresh[-_]?token|secret|password|x[-_]?api[-_]?key)$/i;\n\n/**\n * Session logger interface — injected into Session for pluggable logging.\n *\n * Implementations decide where and how to persist session events.\n * The Session class calls log() for every significant action.\n */\nexport interface ISessionLogger {\n /** Log a session event with structured data. */\n log(sessionId: string, event: string, data: TSessionLogData): void;\n}\n\n/**\n * File-based session logger — writes JSONL to {logDir}/{sessionId}.jsonl.\n *\n * This is the default implementation used by the CLI.\n * Each line is a self-contained JSON object with timestamp, sessionId, event, and data.\n */\nexport class FileSessionLogger implements ISessionLogger {\n private readonly logDir: string;\n private readonly options: Required<IFileSessionLoggerOptions>;\n\n constructor(logDir: string, options: IFileSessionLoggerOptions = {}) {\n this.logDir = logDir;\n this.options = {\n externalPayloadThresholdBytes:\n options.externalPayloadThresholdBytes ?? DEFAULT_EXTERNAL_PAYLOAD_THRESHOLD_BYTES,\n redactedValue: options.redactedValue ?? DEFAULT_REDACTED_VALUE,\n };\n try {\n mkdirSync(logDir, { recursive: true });\n } catch {\n // Best-effort: logging disabled if directory cannot be created\n }\n }\n\n log(sessionId: string, event: string, data: TSessionLogData): void {\n try {\n const normalizedData = normalizeLogData(sessionId, this.logDir, data, this.options);\n const entry = JSON.stringify({\n timestamp: new Date().toISOString(),\n sessionId,\n event,\n ...normalizedData,\n });\n const logFile = join(this.logDir, `${sessionId}.jsonl`);\n appendFileSync(logFile, entry + '\\n');\n } catch {\n // Logging failure must never break the session\n }\n }\n}\n\nfunction normalizeLogData(\n sessionId: string,\n logDir: string,\n data: TSessionLogData,\n options: Required<IFileSessionLoggerOptions>,\n): TSessionLogData {\n const normalized: TSessionLogData = {};\n for (const [key, value] of Object.entries(data)) {\n normalized[key] = normalizeLogValue(sessionId, logDir, key, value, options);\n }\n return normalized;\n}\n\nfunction normalizeLogValue(\n sessionId: string,\n logDir: string,\n key: string,\n value: TSessionLogValue,\n options: Required<IFileSessionLoggerOptions>,\n): TSessionLogValue {\n if (SENSITIVE_KEY_PATTERN.test(key)) {\n return options.redactedValue;\n }\n if (\n value === null ||\n value === undefined ||\n typeof value === 'string' ||\n typeof value === 'number'\n ) {\n return maybeExternalizePayload(sessionId, logDir, value, options);\n }\n if (typeof value === 'boolean') {\n return value;\n }\n if (value instanceof Date) {\n return value.toISOString();\n }\n if (Array.isArray(value)) {\n const normalizedArray = value.map((item) =>\n normalizeLogValue(sessionId, logDir, key, item as TSessionLogValue, options),\n );\n return maybeExternalizePayload(sessionId, logDir, normalizedArray, options);\n }\n if (typeof value === 'object') {\n const record = value as Record<string, TSessionLogValue>;\n const normalizedRecord: Record<string, TSessionLogValue> = {};\n for (const [childKey, childValue] of Object.entries(record)) {\n normalizedRecord[childKey] = normalizeLogValue(\n sessionId,\n logDir,\n childKey,\n childValue,\n options,\n );\n }\n return maybeExternalizePayload(sessionId, logDir, normalizedRecord, options);\n }\n return String(value);\n}\n\nfunction maybeExternalizePayload(\n sessionId: string,\n logDir: string,\n value: TSessionLogValue,\n options: Required<IFileSessionLoggerOptions>,\n): TSessionLogValue {\n const serialized = JSON.stringify(value);\n if (serialized === undefined) {\n return value;\n }\n const byteLength = Buffer.byteLength(serialized);\n if (byteLength <= options.externalPayloadThresholdBytes) {\n return value;\n }\n\n const sha256 = createHash('sha256').update(serialized).digest('hex');\n const payloadDirName = `${sessionId}.payloads`;\n const payloadFileName = `${sha256}.json`;\n const relativePath = join(payloadDirName, payloadFileName);\n const payloadDir = join(logDir, payloadDirName);\n const payloadPath = join(logDir, relativePath);\n mkdirSync(payloadDir, { recursive: true });\n if (!existsSync(payloadPath)) {\n writeFileSync(payloadPath, serialized, 'utf-8');\n }\n return {\n kind: 'external-payload',\n encoding: 'json',\n sha256,\n byteLength,\n relativePath,\n } satisfies IExternalPayloadReference;\n}\n\n/** No-op logger — used when logging is disabled. */\nexport class SilentSessionLogger implements ISessionLogger {\n log(): void {\n // intentionally empty\n }\n}\n","import type { ISessionLogEntry } from './session-log-replay.js';\nimport type { TUniversalValue } from '@robota-sdk/agent-core';\n\nexport interface ISessionReplayValidationIssue {\n code:\n | 'PROVIDER_RESPONSE_RAW_MISSING'\n | 'PROVIDER_NATIVE_RAW_PAYLOAD_MISSING'\n | 'PROVIDER_RESPONSE_NORMALIZED_MISSING'\n | 'TOOL_RESULT_MISSING'\n | 'PAYLOAD_REFERENCE_INVALID';\n message: string;\n eventIndex?: number;\n executionId?: string;\n round?: number;\n toolCallId?: string;\n}\n\nexport interface ISessionReplayValidationResult {\n ok: boolean;\n issues: ISessionReplayValidationIssue[];\n}\n\nexport function validateSessionReplayLogEntries(\n entries: readonly ISessionLogEntry[],\n): ISessionReplayValidationResult {\n const issues: ISessionReplayValidationIssue[] = [];\n const providerEvents = createProviderReplayEventIndex();\n const toolEvents = createToolReplayEventIndex();\n\n entries.forEach((entry, index) => {\n collectPayloadReferenceIssues(entry, index, issues);\n collectProviderReplayEvent(providerEvents, entry, index);\n collectToolReplayEvent(toolEvents, entry, index);\n });\n\n appendProviderReplayIssues(providerEvents, issues);\n appendToolReplayIssues(toolEvents, issues);\n\n return { ok: issues.length === 0, issues };\n}\n\ninterface IProviderReplayRequest {\n executionId: string;\n round: number;\n index: number;\n}\n\ninterface IProviderReplayEventIndex {\n requests: Map<string, IProviderReplayRequest>;\n nativeRawPayloads: Set<string>;\n rawResponses: Set<string>;\n normalizedResponses: Set<string>;\n}\n\ninterface IToolReplayRequest {\n executionId: string;\n toolCallId: string;\n index: number;\n}\n\ninterface IToolReplayEventIndex {\n requests: Map<string, IToolReplayRequest>;\n results: Set<string>;\n}\n\nfunction createProviderReplayEventIndex(): IProviderReplayEventIndex {\n return {\n requests: new Map<string, IProviderReplayRequest>(),\n nativeRawPayloads: new Set<string>(),\n rawResponses: new Set<string>(),\n normalizedResponses: new Set<string>(),\n };\n}\n\nfunction createToolReplayEventIndex(): IToolReplayEventIndex {\n return {\n requests: new Map<string, IToolReplayRequest>(),\n results: new Set<string>(),\n };\n}\n\nfunction collectProviderReplayEvent(\n events: IProviderReplayEventIndex,\n entry: ISessionLogEntry,\n index: number,\n): void {\n const key = providerKey(entry);\n if (!key) return;\n if (entry.event === 'provider_request') {\n events.requests.set(key.key, {\n executionId: key.executionId,\n round: key.round,\n index,\n });\n }\n if (entry.event === 'provider_response_raw') {\n events.rawResponses.add(key.key);\n }\n if (\n entry.event === 'provider_native_raw_payload' &&\n (entry.payloadKind === 'response' || entry.payloadKind === 'stream_event')\n ) {\n events.nativeRawPayloads.add(key.key);\n }\n if (entry.event === 'provider_response_normalized') {\n events.normalizedResponses.add(key.key);\n }\n}\n\nfunction collectToolReplayEvent(\n events: IToolReplayEventIndex,\n entry: ISessionLogEntry,\n index: number,\n): void {\n const key = toolKey(entry);\n if (!key) return;\n if (entry.event === 'tool_execution_request') {\n events.requests.set(key.key, {\n executionId: key.executionId,\n toolCallId: key.toolCallId,\n index,\n });\n }\n if (entry.event === 'tool_execution_result') {\n events.results.add(key.key);\n }\n}\n\nfunction appendProviderReplayIssues(\n events: IProviderReplayEventIndex,\n issues: ISessionReplayValidationIssue[],\n): void {\n for (const [key, request] of events.requests) {\n if (!events.nativeRawPayloads.has(key)) {\n issues.push({\n code: 'PROVIDER_NATIVE_RAW_PAYLOAD_MISSING',\n message: `Provider request ${key} has no provider-native raw response or stream payload event.`,\n eventIndex: request.index,\n executionId: request.executionId,\n round: request.round,\n });\n }\n if (!events.rawResponses.has(key)) {\n issues.push({\n code: 'PROVIDER_RESPONSE_RAW_MISSING',\n message: `Provider request ${key} has no raw response event.`,\n eventIndex: request.index,\n executionId: request.executionId,\n round: request.round,\n });\n }\n if (!events.normalizedResponses.has(key)) {\n issues.push({\n code: 'PROVIDER_RESPONSE_NORMALIZED_MISSING',\n message: `Provider request ${key} has no normalized response event.`,\n eventIndex: request.index,\n executionId: request.executionId,\n round: request.round,\n });\n }\n }\n}\n\nfunction appendToolReplayIssues(\n events: IToolReplayEventIndex,\n issues: ISessionReplayValidationIssue[],\n): void {\n for (const [key, request] of events.requests) {\n if (!events.results.has(key)) {\n issues.push({\n code: 'TOOL_RESULT_MISSING',\n message: `Tool request ${key} has no terminal result event.`,\n eventIndex: request.index,\n executionId: request.executionId,\n toolCallId: request.toolCallId,\n });\n }\n }\n}\n\nfunction providerKey(\n entry: ISessionLogEntry,\n): { key: string; executionId: string; round: number } | undefined {\n if (typeof entry.executionId !== 'string') return undefined;\n const round = typeof entry.round === 'number' ? entry.round : Number(entry.round);\n if (!Number.isFinite(round)) return undefined;\n return { key: `${entry.executionId}:${round}`, executionId: entry.executionId, round };\n}\n\nfunction toolKey(\n entry: ISessionLogEntry,\n): { key: string; executionId: string; toolCallId: string } | undefined {\n if (typeof entry.executionId !== 'string') return undefined;\n const toolCallId =\n typeof entry.toolCallId === 'string'\n ? entry.toolCallId\n : typeof entry.toolExecutionId === 'string'\n ? entry.toolExecutionId\n : undefined;\n if (!toolCallId) return undefined;\n return { key: `${entry.executionId}:${toolCallId}`, executionId: entry.executionId, toolCallId };\n}\n\nfunction collectPayloadReferenceIssues(\n value: TUniversalValue,\n eventIndex: number,\n issues: ISessionReplayValidationIssue[],\n): void {\n if (Array.isArray(value)) {\n value.forEach((item) => collectPayloadReferenceIssues(item, eventIndex, issues));\n return;\n }\n if (!isRecord(value)) return;\n if (value.kind === 'external-payload') {\n if (\n value.encoding !== 'json' ||\n typeof value.sha256 !== 'string' ||\n typeof value.relativePath !== 'string' ||\n typeof value.byteLength !== 'number'\n ) {\n issues.push({\n code: 'PAYLOAD_REFERENCE_INVALID',\n message: 'External payload reference is missing required replay fields.',\n eventIndex,\n });\n }\n return;\n }\n Object.values(value).forEach((child) => collectPayloadReferenceIssues(child, eventIndex, issues));\n}\n\nfunction isRecord(value: TUniversalValue): value is Record<string, TUniversalValue> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n","import { existsSync, readFileSync } from 'node:fs';\n\nimport { messageToHistoryEntry } from '@robota-sdk/agent-core';\n\nimport type { IHistoryEntry, TUniversalMessage, TUniversalValue } from '@robota-sdk/agent-core';\n\nexport interface ISessionLogEntry extends Record<string, TUniversalValue> {\n timestamp: string;\n sessionId: string;\n event: string;\n}\n\nexport interface ISessionReplayRecord {\n sessionId: string | undefined;\n cwd: string | undefined;\n createdAt: string | undefined;\n updatedAt: string | undefined;\n messages: TUniversalMessage[];\n history: IHistoryEntry[];\n backgroundTaskEvents: object[];\n backgroundJobGroupEvents: object[];\n memoryEvents: object[];\n}\n\nexport { validateSessionReplayLogEntries } from './session-log-validation.js';\nexport type {\n ISessionReplayValidationIssue,\n ISessionReplayValidationResult,\n} from './session-log-validation.js';\n\nexport function loadSessionLogEntries(logFile: string): ISessionLogEntry[] {\n if (!existsSync(logFile)) {\n return [];\n }\n return readFileSync(logFile, 'utf-8')\n .split('\\n')\n .map((line) => line.trim())\n .filter((line) => line.length > 0)\n .map((line) => JSON.parse(line) as ISessionLogEntry);\n}\n\nexport function replaySessionLogEntries(\n entries: readonly ISessionLogEntry[],\n): ISessionReplayRecord {\n const messages: TUniversalMessage[] = [];\n const history: IHistoryEntry[] = [];\n const auxiliaryEvents: IAuxiliaryReplayEvents = {\n backgroundTaskEvents: [],\n backgroundJobGroupEvents: [],\n memoryEvents: [],\n };\n let sessionId: string | undefined;\n let cwd: string | undefined;\n let createdAt: string | undefined;\n let updatedAt: string | undefined;\n\n for (const entry of entries) {\n sessionId = sessionId ?? entry.sessionId;\n createdAt = createdAt ?? entry.timestamp;\n updatedAt = entry.timestamp;\n\n if (entry.event === 'session_init') {\n cwd = typeof entry.cwd === 'string' ? entry.cwd : cwd;\n }\n\n if (entry.event === 'history_mutation' && entry.mutation === 'append_message') {\n const message = normalizeLogMessage(entry.message);\n if (message) {\n messages.push(message);\n history.push(messageToHistoryEntry(message));\n }\n }\n\n collectAuxiliaryReplayEvent(entry, auxiliaryEvents);\n }\n\n return {\n sessionId,\n cwd,\n createdAt,\n updatedAt,\n messages,\n history,\n backgroundTaskEvents: auxiliaryEvents.backgroundTaskEvents,\n backgroundJobGroupEvents: auxiliaryEvents.backgroundJobGroupEvents,\n memoryEvents: auxiliaryEvents.memoryEvents,\n };\n}\n\ninterface IAuxiliaryReplayEvents {\n backgroundTaskEvents: object[];\n backgroundJobGroupEvents: object[];\n memoryEvents: object[];\n}\n\nfunction collectAuxiliaryReplayEvent(\n entry: ISessionLogEntry,\n auxiliaryEvents: IAuxiliaryReplayEvents,\n): void {\n if (entry.event === 'background_task_event') {\n pushObjectPayload(auxiliaryEvents.backgroundTaskEvents, entry, 'backgroundEvent', 'data');\n return;\n }\n if (entry.event === 'background_job_group_event') {\n pushObjectPayload(\n auxiliaryEvents.backgroundJobGroupEvents,\n entry,\n 'backgroundJobGroupEvent',\n 'data',\n );\n return;\n }\n if (entry.event === 'memory_event') {\n pushObjectPayload(auxiliaryEvents.memoryEvents, entry, 'memoryEvent', 'data');\n }\n}\n\nfunction normalizeLogMessage(value: TUniversalValue): TUniversalMessage | undefined {\n if (!isRecord(value)) return undefined;\n const role = value.role;\n if (role !== 'user' && role !== 'assistant' && role !== 'system' && role !== 'tool') {\n return undefined;\n }\n const id = typeof value.id === 'string' ? value.id : `${role}-${Date.now()}`;\n const timestamp =\n value.timestamp instanceof Date\n ? value.timestamp\n : new Date(typeof value.timestamp === 'string' ? value.timestamp : Date.now());\n return {\n ...value,\n id,\n role,\n timestamp,\n } as TUniversalMessage;\n}\n\nfunction isRecord(value: TUniversalValue): value is Record<string, TUniversalValue> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction getObjectPayload(entry: ISessionLogEntry, key: string): object | undefined {\n const value = entry[key];\n if (\n typeof value !== 'object' ||\n value === null ||\n Array.isArray(value) ||\n value instanceof Date\n ) {\n return undefined;\n }\n return value;\n}\n\nfunction pushObjectPayload(\n target: object[],\n entry: ISessionLogEntry,\n primaryKey: string,\n fallbackKey: string,\n): void {\n const payload = getObjectPayload(entry, primaryKey) ?? getObjectPayload(entry, fallbackKey);\n if (payload) target.push(payload);\n}\n","/**\n * SessionStore — persists conversation sessions as JSON files.\n *\n * Sessions are stored at `~/.robota/sessions/{id}.json` by default.\n * Consumers can inject a project-local directory such as `.robota/sessions`.\n * The store directory is created on first write if it does not exist.\n */\n\nimport { readFileSync, writeFileSync, existsSync, mkdirSync, unlinkSync, readdirSync } from 'fs';\nimport { join } from 'path';\n\nimport type { IToolSchema } from '@robota-sdk/agent-core';\n\n/** A persisted session record */\nexport interface ISessionRecord {\n /** Unique session identifier */\n id: string;\n /** Optional human-readable session name */\n name?: string;\n /** Working directory when the session was created */\n cwd: string;\n /** ISO-8601 creation timestamp */\n createdAt: string;\n /** ISO-8601 last-updated timestamp */\n updatedAt: string;\n /** Conversation messages (opaque to the store) */\n messages: unknown[];\n /** Full UI timeline (chat + events) for rendering restoration */\n history?: unknown[];\n /** Exact system prompt used to create the session. */\n systemPrompt?: string;\n /** Tool schemas registered for the session. */\n toolSchemas?: IToolSchema[];\n /** Latest background task snapshots for resume/debugging. */\n backgroundTasks?: unknown[];\n /** Durable non-streaming background task events for resume/debugging. */\n backgroundTaskEvents?: unknown[];\n /** Latest background job group snapshots for resume/debugging. */\n backgroundJobGroups?: unknown[];\n /** Durable background job group events for resume/debugging. */\n backgroundJobGroupEvents?: unknown[];\n /** Durable skill activation events for resume/debugging. */\n skillActivationEvents?: unknown[];\n /** Durable automatic memory events for resume/debugging. */\n memoryEvents?: unknown[];\n /** Memory references used by the latest prompt turn. */\n usedMemoryReferences?: unknown[];\n /** SDK-owned context reference inventory for resume/debugging. */\n contextReferences?: unknown[];\n /** Provider sandbox snapshot identifier for workspace hydration on resume. */\n sandboxSnapshotId?: string;\n}\n\n/** Minimal persistence port consumed by Session. */\nexport interface ISessionStore {\n save(session: ISessionRecord): void;\n load(id: string): ISessionRecord | undefined;\n list(): ISessionRecord[];\n delete(id: string): void;\n /** Return the absolute file path for a session file, if the store is file-backed. */\n getFilePath?(id: string): string;\n}\n\n/**\n * Return the current user home directory.\n * Reads process.env.HOME at call time so tests can override it.\n */\nfunction getHomeDir(): string {\n return process.env.HOME ?? process.env.USERPROFILE ?? '/';\n}\n\n/**\n * Persistent session store backed by individual JSON files.\n *\n * Construct with a custom `baseDir` to redirect storage (useful in tests).\n */\nexport class SessionStore implements ISessionStore {\n private readonly baseDir: string;\n\n constructor(baseDir?: string) {\n this.baseDir = baseDir ?? join(getHomeDir(), '.robota', 'sessions');\n }\n\n /** Ensure the storage directory exists */\n private ensureDir(): void {\n if (!existsSync(this.baseDir)) {\n mkdirSync(this.baseDir, { recursive: true });\n }\n }\n\n /** Absolute path to a session's JSON file */\n private filePath(id: string): string {\n return join(this.baseDir, `${id}.json`);\n }\n\n /** Return the absolute file path for a session — implements ISessionStore.getFilePath */\n getFilePath(id: string): string {\n return this.filePath(id);\n }\n\n /**\n * Persist a session record to disk.\n * Creates the storage directory if needed.\n */\n save(session: ISessionRecord): void {\n this.ensureDir();\n writeFileSync(this.filePath(session.id), JSON.stringify(session, null, 2), 'utf-8');\n }\n\n /**\n * Load a session by its ID.\n * Returns `undefined` when the session file does not exist or is corrupt.\n */\n load(id: string): ISessionRecord | undefined {\n const path = this.filePath(id);\n if (!existsSync(path)) {\n return undefined;\n }\n try {\n const raw = readFileSync(path, 'utf-8');\n return JSON.parse(raw) as ISessionRecord;\n } catch {\n // allow-fallback: corrupt session file is unrecoverable; treat as missing to avoid crash on --continue/--resume\n return undefined;\n }\n }\n\n /**\n * List all persisted sessions, sorted by `updatedAt` descending (most recent first).\n */\n list(): ISessionRecord[] {\n if (!existsSync(this.baseDir)) {\n return [];\n }\n\n const files = readdirSync(this.baseDir).filter((f) => f.endsWith('.json'));\n const sessions: ISessionRecord[] = [];\n\n for (const file of files) {\n try {\n const raw = readFileSync(join(this.baseDir, file), 'utf-8');\n const record = JSON.parse(raw) as ISessionRecord;\n sessions.push(record);\n } catch {\n // Skip malformed files\n }\n }\n\n return sessions.sort(\n (a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime(),\n );\n }\n\n /**\n * Delete a session by its ID.\n * No-ops silently if the session does not exist.\n */\n delete(id: string): void {\n const path = this.filePath(id);\n if (existsSync(path)) {\n unlinkSync(path);\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["logger","logger","logger","isRecord","join","existsSync","readFileSync"],"sources":["../../src/session-base.ts","../../src/compaction-orchestrator.ts","../../src/context-window-tracker.ts","../../src/permission-types.ts","../../src/tool-hook-helpers.ts","../../src/permission-enforcer.ts","../../src/session-components.ts","../../src/session-history-ops.ts","../../src/session-lifecycle.ts","../../src/session-tool-execution-bridge.ts","../../src/session-run.ts","../../src/session.ts","../../src/session-logger.ts","../../src/session-log-events.ts","../../src/session-log-validation.ts","../../src/session-log-replay.ts","../../src/session-store.ts"],"sourcesContent":["import type { ContextWindowTracker, TAutoCompactThreshold } from './context-window-tracker.js';\nimport type { PermissionEnforcer } from './permission-enforcer.js';\nimport type {\n Robota,\n IAIProvider,\n IContextWindowState,\n IHistoryEntry,\n IToolSchema,\n TModelEffort,\n TPermissionMode,\n TUniversalMessage,\n} from '@robota-sdk/agent-core';\n\nexport abstract class SessionBase {\n protected abstract readonly robota: Robota;\n protected abstract readonly permissionEnforcer: PermissionEnforcer;\n protected abstract readonly contextTracker: ContextWindowTracker;\n protected abstract permissionMode: TPermissionMode;\n protected abstract activePresetId: string;\n protected abstract parallelSubagentsEnabled: boolean;\n protected abstract readonly sessionId: string;\n protected abstract readonly aiProvider: IAIProvider;\n protected abstract readonly toolSchemas: IToolSchema[];\n protected abstract model: string;\n protected abstract systemMessage: string;\n protected abstract messageCount: number;\n protected abstract abortController: AbortController | null;\n\n getPermissionMode(): TPermissionMode {\n return this.permissionMode;\n }\n\n /** Change the active permission mode — future tool calls will use the new mode. */\n setPermissionMode(mode: TPermissionMode): void {\n this.permissionMode = mode;\n }\n\n /** Read the active preset id (PRESET-011 runtime state). */\n getActivePresetId(): string {\n return this.activePresetId;\n }\n\n /**\n * Set the active preset id. PURE STATE — this only records which preset is active;\n * it does not re-apply any preset options (permission/model/persona). Higher layers\n * own re-application (PRESET-012/013/014).\n */\n setActivePresetId(id: string): void {\n this.activePresetId = id;\n }\n\n /** Whether subagent dispatch is currently allowed for this session (PRESET-016 runtime gate). */\n getParallelSubagentsEnabled(): boolean {\n return this.parallelSubagentsEnabled;\n }\n\n /** Toggle subagent dispatch live. Only effective if the agent runtime was built at assembly. */\n setParallelSubagentsEnabled(enabled: boolean): void {\n this.parallelSubagentsEnabled = enabled;\n }\n\n getSessionId(): string {\n return this.sessionId;\n }\n\n getSystemMessage(): string {\n return this.systemMessage;\n }\n\n /**\n * Replace the active system message and propagate it so the next provider request carries it.\n * Records the live value on `this.systemMessage` (re-injected on compaction) and delegates to\n * `Robota.updateSystemPrompt`, which updates the single-source `config.systemMessage` and the live\n * conversation store head. The system prompt is an agent-level concern, not model config, so this\n * does not route through `setModel`. Used by persona application, the self-verification toggle, and\n * AGENTS.md/CLAUDE.md staleness refresh.\n */\n updateSystemMessage(newMessage: string): void {\n this.systemMessage = newMessage;\n this.robota.updateSystemPrompt(newMessage);\n }\n\n /**\n * Re-apply model options to the live session (PRESET-013 model/effort re-application seam).\n *\n * Propagates model/effort/temperature/maxOutputTokens to the agent via `robota.setModel` so the\n * next call reflects them, and updates `this.model` to keep `getModelId()` accurate. The preset\n * `maxOutputTokens` field maps to the agent's `maxTokens` channel. Absent fields are left untouched.\n */\n async applyModelOptions(options: {\n model?: string;\n effort?: TModelEffort;\n temperature?: number;\n maxOutputTokens?: number;\n }): Promise<void> {\n // `setModel` requires the agent to be fully initialized. On a fresh interactive session the\n // agent initializes lazily on the first `run()`, so a live model change before any message\n // (e.g. `/preset` right after launch) would otherwise hit the \"must be fully initialized\"\n // guard. Bring the agent to a ready state first — idempotent and side-effect-free.\n await this.robota.ensureReady();\n const nextModel = options.model ?? this.model;\n // The system prompt is not model config; it is updated independently via updateSystemMessage.\n this.robota.setModel({\n provider: this.aiProvider.name,\n model: nextModel,\n ...(options.effort !== undefined && { effort: options.effort }),\n ...(options.temperature !== undefined && { temperature: options.temperature }),\n ...(options.maxOutputTokens !== undefined && { maxTokens: options.maxOutputTokens }),\n });\n this.model = nextModel;\n }\n\n getToolSchemas(): IToolSchema[] {\n return this.toolSchemas;\n }\n\n getMessageCount(): number {\n return this.messageCount;\n }\n\n /** Get tools that have been session-approved (via \"Allow always\" choice). */\n getSessionAllowedTools(): string[] {\n return this.permissionEnforcer.getSessionAllowedTools();\n }\n\n clearSessionAllowedTools(): void {\n this.permissionEnforcer.clearSessionAllowedTools();\n }\n\n /** Abort the currently running execution. No-op if nothing is running. */\n abort(): void {\n if (this.abortController) {\n this.abortController.abort();\n this.abortController = null;\n }\n }\n\n isRunning(): boolean {\n return this.abortController !== null;\n }\n\n getContextState(): IContextWindowState {\n return this.contextTracker.getContextState();\n }\n\n /** Estimate context usage from current conversation history (used after session restore). */\n syncContextFromHistory(): void {\n this.contextTracker.updateFromHistory(this.robota.getHistory());\n }\n\n getAutoCompactThreshold(): TAutoCompactThreshold {\n return this.contextTracker.getAutoCompactThreshold();\n }\n\n setAutoCompactThreshold(threshold: number | false): void {\n this.contextTracker.setAutoCompactThreshold(threshold);\n }\n\n getHistory(): TUniversalMessage[] {\n return this.robota.getHistory();\n }\n\n getFullHistory(): IHistoryEntry[] {\n return this.robota.getFullHistory();\n }\n\n getSessionTokenUsage(): { inputTokens: number; outputTokens: number } | undefined {\n let inputTokens = 0;\n let outputTokens = 0;\n let found = false;\n for (const entry of this.getFullHistory()) {\n if (entry.category !== 'event' || entry.type !== 'usage-summary') continue;\n const snap = entry.data as { promptTokens?: number; completionTokens?: number } | undefined;\n inputTokens += snap?.promptTokens ?? 0;\n outputTokens += snap?.completionTokens ?? 0;\n found = true;\n }\n return found ? { inputTokens, outputTokens } : undefined;\n }\n\n getModelId(): string {\n return this.model;\n }\n\n /** Add an event entry to history (not a chat message) */\n addHistoryEntry(entry: IHistoryEntry): void {\n this.robota.addHistoryEntry(entry);\n }\n\n /** Inject a message into conversation history without execution (used for session restore). */\n injectMessage(\n role: 'user' | 'assistant' | 'system' | 'tool',\n content: string,\n options?: { toolCallId?: string; name?: string },\n ): void {\n this.robota.injectMessage(role, content, options);\n }\n\n /**\n * Inject a full TUniversalMessage preserving all fields (toolCalls, toolCallId, null content).\n * Used during session restore to correctly reconstruct tool_use+tool_result pairs.\n */\n injectRawMessage(msg: TUniversalMessage): void {\n this.robota.injectRawMessage(msg);\n }\n\n clearHistory(): void {\n this.robota.clearHistory();\n this.contextTracker.reset();\n }\n}\n","/**\n * CompactionOrchestrator — handles conversation compaction (summarization)\n * to free context window space.\n *\n * Extracted from Session to separate compaction logic from conversation management.\n */\n\nimport { randomUUID } from 'node:crypto';\n\nimport { runHooks } from '@robota-sdk/agent-core';\n\nimport type {\n IAIProvider,\n TUniversalMessage,\n THooksConfig,\n IHookInput,\n IHookTypeExecutor,\n} from '@robota-sdk/agent-core';\n\n/**\n * Thrown when a compaction summary is invalid (non-string or empty provider content).\n * Conversation history is append-only source data — callers must not clear or replace\n * it when this is thrown (see SPEC § Compaction Failure Contract).\n */\nexport class CompactionError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'CompactionError';\n }\n}\n\nexport interface ICompactionOptions {\n sessionId: string;\n cwd: string;\n model: string;\n hooks?: Record<string, unknown>;\n compactInstructions?: string;\n /** Additional hook type executors (e.g. prompt, agent) beyond the core defaults. */\n hookTypeExecutors?: IHookTypeExecutor[];\n}\n\nexport class CompactionOrchestrator {\n private readonly sessionId: string;\n private readonly cwd: string;\n private readonly model: string;\n private readonly hooks?: Record<string, unknown>;\n private readonly compactInstructions?: string;\n private readonly hookTypeExecutors?: IHookTypeExecutor[];\n\n constructor(options: ICompactionOptions) {\n this.sessionId = options.sessionId;\n this.cwd = options.cwd;\n this.model = options.model;\n this.hooks = options.hooks;\n this.compactInstructions = options.compactInstructions;\n this.hookTypeExecutors = options.hookTypeExecutors;\n }\n\n /**\n * Run compaction — summarize the conversation to free context space.\n * @param provider - The AI provider to use for summarization\n * @param history - Current conversation history\n * @param instructions - Optional focus instructions for the summary\n * @returns The generated summary string (always a non-empty string)\n * @throws {CompactionError} when the provider returns a non-string or empty summary —\n * callers must leave the conversation history untouched in that case\n */\n async compact(\n provider: IAIProvider,\n history: TUniversalMessage[],\n instructions?: string,\n ): Promise<string> {\n if (history.length === 0) return '';\n\n const trigger: 'auto' | 'manual' = instructions !== undefined ? 'manual' : 'auto';\n\n // Fire PreCompact hook\n const preHookInput: IHookInput = {\n session_id: this.sessionId,\n cwd: this.cwd,\n hook_event_name: 'PreCompact',\n trigger,\n };\n await runHooks(\n this.hooks as THooksConfig | undefined,\n 'PreCompact',\n preHookInput,\n this.hookTypeExecutors,\n );\n\n // Build compaction prompt\n const compactPrompt = this.buildCompactionPrompt(history, instructions);\n\n // Call provider to generate summary\n const summaryMessage = await provider.chat(\n [\n {\n id: randomUUID(),\n role: 'user',\n content: compactPrompt,\n state: 'complete' as const,\n timestamp: new Date(),\n },\n ],\n { model: this.model },\n );\n if (typeof summaryMessage.content !== 'string' || summaryMessage.content.trim() === '') {\n throw new CompactionError(\n `Compaction produced an invalid summary (provider=${provider.name}, content type=${typeof summaryMessage.content}); conversation history preserved untouched`,\n );\n }\n\n return summaryMessage.content;\n }\n\n /** Build the compaction prompt from conversation history */\n private buildCompactionPrompt(history: TUniversalMessage[], instructions?: string): string {\n const instructionBlock = instructions ?? this.compactInstructions ?? '';\n const instructionSection = instructionBlock ? `\\nAdditional focus:\\n${instructionBlock}\\n` : '';\n\n const formattedHistory = history\n .map((msg) => {\n const content = typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content);\n return `${msg.role}: ${content}`;\n })\n .join('\\n');\n\n return [\n 'Summarize the following conversation concisely, preserving:',\n \"- User's original requests and goals\",\n '- Key decisions and conclusions',\n '- Important code changes and file paths',\n '- Current task status and next steps',\n instructionSection,\n \"Drop verbose tool outputs, debugging steps, and exploratory work that didn't lead to results.\",\n '',\n 'Conversation:',\n formattedHistory,\n ].join('\\n');\n }\n}\n","/**\n * ContextWindowTracker — tracks token usage and context window state.\n *\n * Extracted from Session to separate context monitoring from conversation management.\n */\n\nimport { estimateContextTokensFromMessages, getModelContextWindow } from '@robota-sdk/agent-core';\n\nimport type { IContextWindowState, TUniversalMessage } from '@robota-sdk/agent-core';\n\n/** Percentage conversion factor */\nconst PERCENT = 100;\n\n/** Auto-compact when context usage reaches this fraction */\nexport const AUTO_COMPACT_THRESHOLD = 0.835;\n\nexport type TAutoCompactThreshold = number | false;\n\nexport class ContextWindowTracker {\n private contextUsedTokens = 0;\n private readonly contextMaxTokens: number;\n private autoCompactThreshold: TAutoCompactThreshold;\n\n constructor(\n model: string,\n contextMaxTokens?: number,\n autoCompactThreshold?: TAutoCompactThreshold,\n ) {\n this.contextMaxTokens = contextMaxTokens ?? getModelContextWindow(model);\n this.autoCompactThreshold = normalizeAutoCompactThreshold(autoCompactThreshold);\n }\n\n /** Get current context window state */\n getContextState(): IContextWindowState {\n const usedPercentage = Math.min(\n PERCENT,\n (this.contextUsedTokens / this.contextMaxTokens) * PERCENT,\n );\n return {\n maxTokens: this.contextMaxTokens,\n usedTokens: this.contextUsedTokens,\n usedPercentage: Math.round(usedPercentage * PERCENT) / PERCENT,\n remainingPercentage: Math.round((PERCENT - usedPercentage) * PERCENT) / PERCENT,\n };\n }\n\n /** Whether auto-compaction threshold has been exceeded */\n shouldAutoCompact(): boolean {\n if (this.autoCompactThreshold === false) {\n return false;\n }\n return this.getContextState().usedPercentage >= this.autoCompactThreshold * PERCENT;\n }\n\n /** The auto-compaction policy for this tracker. */\n getAutoCompactThreshold(): TAutoCompactThreshold {\n return this.autoCompactThreshold;\n }\n\n /** Update the auto-compaction policy for this tracker. */\n setAutoCompactThreshold(autoCompactThreshold: TAutoCompactThreshold): void {\n this.autoCompactThreshold = normalizeAutoCompactThreshold(autoCompactThreshold);\n }\n\n /**\n * Estimate token usage from conversation history.\n *\n * Uses the shared core estimator (`estimateContextTokensFromMessages`) so session display,\n * /context, auto-compact, and core execution guards reason about the same effective token state.\n * That estimator prefers the provider's actual reported token count (which includes the system\n * prompt and tool schemas) over a raw serialized-history char heuristic, falling back to the\n * serialized estimate only when no provider usage is present on the latest message.\n */\n updateFromHistory(history: TUniversalMessage[]): void {\n this.contextUsedTokens = estimateContextTokensFromMessages(history).usedTokens;\n }\n\n /** Reset token tracking */\n reset(): void {\n this.contextUsedTokens = 0;\n }\n}\n\nfunction normalizeAutoCompactThreshold(\n autoCompactThreshold: TAutoCompactThreshold | undefined,\n): TAutoCompactThreshold {\n if (autoCompactThreshold === undefined) {\n return AUTO_COMPACT_THRESHOLD;\n }\n if (autoCompactThreshold === false) {\n return false;\n }\n if (\n !Number.isFinite(autoCompactThreshold) ||\n autoCompactThreshold <= 0 ||\n autoCompactThreshold > 1\n ) {\n throw new RangeError('autoCompactThreshold must be a number greater than 0 and at most 1.');\n }\n return autoCompactThreshold;\n}\n","/**\n * Permission types — interfaces and type aliases for permission enforcement.\n */\n\nimport type { ISessionLogger } from './session-logger.js';\nimport type { IToolWithEventService, TPermissionMode, TToolArgs } from '@robota-sdk/agent-core';\nimport type { IHookTypeExecutor, ISpinner, ITerminalOutput } from '@robota-sdk/agent-core';\n\nexport type { ISpinner, ITerminalOutput };\n\n/**\n * Permission handler result:\n * - true: allow this invocation\n * - false: deny this invocation\n * - 'allow-session': allow this invocation and auto-approve this tool for the rest of the session\n * - 'allow-project': allow this invocation and persist the approval to .robota/settings.local.json\n */\nexport type TPermissionResult = boolean | 'allow-session' | 'allow-project';\n\n/**\n * Custom permission handler — called when a tool needs user approval.\n * Returns true to allow, false to deny, or 'allow-session' to remember for the session.\n */\nexport type TPermissionHandler = (\n toolName: string,\n toolArgs: TToolArgs,\n) => Promise<TPermissionResult>;\n\nexport interface IPermissionEnforcerOptions {\n sessionId: string;\n cwd: string;\n getPermissionMode: () => TPermissionMode;\n config: {\n permissions: { allow: string[]; deny: string[] };\n hooks?: Record<string, unknown>;\n };\n terminal: ITerminalOutput;\n permissionHandler?: TPermissionHandler;\n promptForApprovalFn?: (\n terminal: ITerminalOutput,\n toolName: string,\n toolArgs: TToolArgs,\n ) => Promise<TPermissionResult>;\n sessionLogger?: ISessionLogger;\n onToolExecution?: (event: {\n type: 'start' | 'end';\n toolName: string;\n toolArgs?: TToolArgs;\n success?: boolean;\n denied?: boolean;\n toolResultData?: string;\n executionId?: string;\n }) => void;\n /** Additional hook type executors (e.g. prompt, agent) beyond the core defaults. */\n hookTypeExecutors?: IHookTypeExecutor[];\n /** Absolute path to session transcript file — passed to PreToolUse hook inputs as transcript_path */\n transcriptPath?: string;\n /** Called when the user selects \"allow for project\" — persists the tool pattern to project settings. */\n onProjectAllowTool?: (toolName: string) => void;\n}\n\n/** Returned when the user denies a permission prompt. success:true prevents ToolExecutionError. */\nexport const PERMISSION_DENIED_RESULT = {\n success: true,\n data: JSON.stringify({\n success: false,\n output: '',\n error: 'Permission denied. The user did not approve this action.',\n }),\n metadata: {},\n} as const;\n\n/** Maximum chars for any single tool output. Matches Claude Code's 30K limit. */\nexport const MAX_TOOL_OUTPUT_CHARS = 30_000;\n","/**\n * Tool hook helpers — stateless utility functions for tool hook execution\n * and output truncation used by PermissionEnforcer.\n */\n\nimport { runHooks, createLogger } from '@robota-sdk/agent-core';\n\nimport { MAX_TOOL_OUTPUT_CHARS } from './permission-types.js';\n\nimport type {\n IToolResult,\n TToolParameters,\n THooksConfig,\n IHookInput,\n IHookTypeExecutor,\n} from '@robota-sdk/agent-core';\n\nconst logger = createLogger('ToolHookHelpers');\n\n/**\n * Truncate tool result data if it exceeds MAX_TOOL_OUTPUT_CHARS.\n * Uses middle-truncation: keeps first and last portions, removes middle.\n */\nexport function truncateToolResult(result: IToolResult): IToolResult {\n if (typeof result.data !== 'string') return result;\n if (result.data.length <= MAX_TOOL_OUTPUT_CHARS) return result;\n\n const halfLimit = Math.floor(MAX_TOOL_OUTPUT_CHARS / 2);\n const head = result.data.substring(0, halfLimit);\n const tail = result.data.substring(result.data.length - halfLimit);\n const originalSize = result.data.length;\n const truncatedData = `${head}\\n\\n[... output truncated: ${originalSize.toLocaleString()} chars total, showing first and last ${halfLimit.toLocaleString()} chars ...]\\n\\n${tail}`;\n\n return { ...result, data: truncatedData };\n}\n\n/** Build a hook input object for tool execution hooks */\nexport function buildHookInput(\n sessionId: string,\n cwd: string,\n toolName: string,\n parameters: TToolParameters,\n permissionMode?: string,\n transcriptPath?: string,\n): IHookInput {\n return {\n session_id: sessionId,\n cwd,\n hook_event_name: 'PreToolUse',\n tool_name: toolName,\n tool_input: parameters as Record<string, string | number | boolean | object>,\n ...(permissionMode !== undefined && { permission_mode: permissionMode }),\n ...(transcriptPath !== undefined && { transcript_path: transcriptPath }),\n };\n}\n\n/** Run PreToolUse hooks; returns a denial IToolResult if blocked, or null to proceed */\nexport async function runPreToolHook(\n hooks: Record<string, unknown> | undefined,\n hookInput: IHookInput,\n hookTypeExecutors: IHookTypeExecutor[] | undefined,\n): Promise<IToolResult | null> {\n const hookResult = await runHooks(\n hooks as THooksConfig | undefined,\n 'PreToolUse',\n hookInput,\n hookTypeExecutors,\n );\n if (hookResult.blocked) {\n return {\n success: true,\n data: JSON.stringify({\n blocked: true,\n reason: hookResult.reason ?? 'Blocked by hook',\n }),\n metadata: {},\n };\n }\n return null;\n}\n\n/** Fire PostToolUse hooks (fire and forget) */\nexport function firePostToolHook(\n hooks: Record<string, unknown> | undefined,\n hookInput: IHookInput,\n result: IToolResult,\n hookTypeExecutors: IHookTypeExecutor[] | undefined,\n): void {\n const postHookInput: IHookInput = {\n ...hookInput,\n hook_event_name: 'PostToolUse',\n tool_output: typeof result.data === 'string' ? result.data : JSON.stringify(result.data),\n };\n runHooks(\n hooks as THooksConfig | undefined,\n 'PostToolUse',\n postHookInput,\n hookTypeExecutors,\n ).catch((error) => logger.warn('hook failed', { error }));\n}\n","/**\n * PermissionEnforcer — handles tool permission checking, hook execution,\n * and tool output truncation.\n *\n * Extracted from Session to separate permission/hook concerns from\n * conversation management.\n */\n\nimport { evaluatePermission } from '@robota-sdk/agent-core';\n\nimport { PERMISSION_DENIED_RESULT } from './permission-types.js';\nimport {\n truncateToolResult,\n buildHookInput,\n runPreToolHook,\n firePostToolHook,\n} from './tool-hook-helpers.js';\n\nimport type {\n IPermissionEnforcerOptions,\n TPermissionHandler,\n TPermissionResult,\n ITerminalOutput,\n ISpinner,\n} from './permission-types.js';\nimport type { ISessionLogger, TSessionLogData } from './session-logger.js';\nimport type {\n IToolWithEventService,\n IToolResult,\n TToolParameters,\n IToolExecutionContext,\n TToolArgs,\n} from '@robota-sdk/agent-core';\n\nexport type { TPermissionHandler, TPermissionResult, ITerminalOutput, ISpinner };\nexport type { IPermissionEnforcerOptions };\n\nexport class PermissionEnforcer {\n private readonly sessionId: string;\n private readonly cwd: string;\n private readonly getPermissionMode: IPermissionEnforcerOptions['getPermissionMode'];\n private readonly config: IPermissionEnforcerOptions['config'];\n private readonly terminal: ITerminalOutput;\n private readonly permissionHandler?: TPermissionHandler;\n private readonly promptForApprovalFn?: IPermissionEnforcerOptions['promptForApprovalFn'];\n private readonly sessionLogger?: ISessionLogger;\n private readonly onToolExecution?: IPermissionEnforcerOptions['onToolExecution'];\n private readonly hookTypeExecutors?: IPermissionEnforcerOptions['hookTypeExecutors'];\n private readonly transcriptPath?: string;\n private readonly sessionAllowedTools = new Set<string>();\n private readonly onProjectAllowTool?: (toolName: string) => void;\n\n constructor(options: IPermissionEnforcerOptions) {\n this.sessionId = options.sessionId;\n this.cwd = options.cwd;\n this.getPermissionMode = options.getPermissionMode;\n this.config = options.config;\n this.terminal = options.terminal;\n this.permissionHandler = options.permissionHandler;\n this.promptForApprovalFn = options.promptForApprovalFn;\n this.sessionLogger = options.sessionLogger;\n this.onToolExecution = options.onToolExecution;\n this.hookTypeExecutors = options.hookTypeExecutors;\n this.transcriptPath = options.transcriptPath;\n this.onProjectAllowTool = options.onProjectAllowTool;\n }\n\n /** Wrap all tools with permission checking */\n wrapTools(tools: IToolWithEventService[]): IToolWithEventService[] {\n return tools.map((tool) => this.wrapToolWithPermission(tool));\n }\n\n /** Get tools that have been session-approved (via \"Allow always\" choice). */\n getSessionAllowedTools(): string[] {\n return [...this.sessionAllowedTools];\n }\n\n /** Clear all session-scoped allow rules. */\n clearSessionAllowedTools(): void {\n this.sessionAllowedTools.clear();\n }\n\n /**\n * Wrap a tool with permission checking.\n * The wrapper intercepts execute() and runs permission evaluation before delegating.\n * If denied, returns a tool result indicating the action was blocked.\n */\n private wrapToolWithPermission(tool: IToolWithEventService): IToolWithEventService {\n const enforcer = this;\n const originalExecute = tool.execute.bind(tool);\n\n const wrappedTool = Object.create(tool) as IToolWithEventService;\n wrappedTool.execute = async (\n parameters: TToolParameters,\n context?: IToolExecutionContext,\n ): Promise<IToolResult> => {\n // Must NEVER throw — if this throws, the execution round records the\n // assistant tool_use in history but never adds a tool_result, which\n // corrupts the conversation and causes a 400 error on the next API call.\n try {\n const toolName = tool.getName();\n enforcer.log('tool_call', {\n tool: toolName,\n args: parameters as Record<string, string | number | boolean | object>,\n });\n\n const hookInput = buildHookInput(\n enforcer.sessionId,\n enforcer.cwd,\n toolName,\n parameters,\n enforcer.getPermissionMode(),\n enforcer.transcriptPath,\n );\n\n const preResult = await runPreToolHook(\n enforcer.config.hooks,\n hookInput,\n enforcer.hookTypeExecutors,\n );\n if (preResult) {\n enforcer.log('tool_blocked', { tool: toolName, reason: 'hook' });\n return preResult;\n }\n\n const allowed = await enforcer.checkPermission(toolName, parameters as TToolArgs);\n if (!allowed) {\n enforcer.log('tool_denied', { tool: toolName, reason: 'permission' });\n enforcer.onToolExecution?.({\n type: 'end',\n toolName,\n toolArgs: parameters as TToolArgs,\n success: false,\n denied: true,\n executionId: context?.executionId,\n });\n return PERMISSION_DENIED_RESULT;\n }\n\n enforcer.onToolExecution?.({\n type: 'start',\n toolName,\n toolArgs: parameters as TToolArgs,\n executionId: context?.executionId,\n });\n\n const result = await originalExecute(parameters, context as IToolExecutionContext);\n\n // Truncate oversized tool output (matches 30K char limit)\n const truncatedResult = truncateToolResult(result);\n\n if (truncatedResult !== result && typeof result.data === 'string') {\n enforcer.terminal.writeLine(\n ` ⚠ Output truncated: ${result.data.length.toLocaleString()} chars total — model sees first and last 15,000 chars`,\n );\n }\n\n enforcer.onToolExecution?.({\n type: 'end',\n toolName,\n toolArgs: parameters as TToolArgs,\n success: truncatedResult.success,\n toolResultData:\n typeof truncatedResult.data === 'string'\n ? truncatedResult.data\n : JSON.stringify(truncatedResult.data),\n executionId: context?.executionId,\n });\n\n const dataSize =\n typeof truncatedResult.data === 'string'\n ? truncatedResult.data.length\n : JSON.stringify(truncatedResult.data).length;\n enforcer.log('tool_result', {\n tool: toolName,\n success: truncatedResult.success,\n dataChars: dataSize,\n truncated: truncatedResult !== result,\n });\n firePostToolHook(\n enforcer.config.hooks,\n hookInput,\n truncatedResult,\n enforcer.hookTypeExecutors,\n );\n return truncatedResult;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return {\n success: true,\n data: JSON.stringify({ success: false, output: '', error: message }),\n metadata: {},\n };\n }\n };\n\n return wrappedTool;\n }\n\n /** Evaluate permission for a tool call using the current mode and config */\n async checkPermission(toolName: string, toolArgs: TToolArgs): Promise<boolean> {\n const decision = evaluatePermission(toolName, toolArgs, this.getPermissionMode(), {\n allow: this.config.permissions.allow,\n deny: this.config.permissions.deny,\n });\n\n if (decision === 'auto') return true;\n if (decision === 'deny') return false;\n\n // Check session-scoped allow list before prompting\n if (this.sessionAllowedTools.has(toolName)) return true;\n\n // 'approve' — prompt the user via custom handler, injected approval fn, or deny\n if (this.permissionHandler) {\n const result = await this.permissionHandler(toolName, toolArgs);\n if (result === 'allow-session') {\n this.sessionAllowedTools.add(toolName);\n return true;\n }\n if (result === 'allow-project') {\n this.sessionAllowedTools.add(toolName);\n this.onProjectAllowTool?.(toolName);\n return true;\n }\n return result;\n }\n if (this.promptForApprovalFn) {\n const result = await this.promptForApprovalFn(this.terminal, toolName, toolArgs);\n if (result === 'allow-session') {\n this.sessionAllowedTools.add(toolName);\n return true;\n }\n if (result === 'allow-project') {\n this.sessionAllowedTools.add(toolName);\n this.onProjectAllowTool?.(toolName);\n return true;\n }\n return result;\n }\n // No approval mechanism available — deny by default\n return false;\n }\n\n /** Delegate session event to the injected logger. */\n private log(event: string, data: TSessionLogData): void {\n this.sessionLogger?.log(this.sessionId, event, data);\n }\n}\n","import { Robota } from '@robota-sdk/agent-core';\n\nimport { CompactionOrchestrator } from './compaction-orchestrator.js';\nimport { ContextWindowTracker } from './context-window-tracker.js';\nimport { PermissionEnforcer } from './permission-enforcer.js';\n\nimport type { ISessionOptions } from './session-types.js';\nimport type {\n IAgentConfig,\n IAIProvider,\n IToolWithEventService,\n TPermissionMode,\n} from '@robota-sdk/agent-core';\n\nexport function buildPermissionEnforcer(\n options: ISessionOptions,\n sessionId: string,\n cwd: string,\n getPermissionMode: () => TPermissionMode,\n transcriptPath: string | undefined,\n): PermissionEnforcer {\n return new PermissionEnforcer({\n sessionId,\n cwd,\n getPermissionMode,\n config: {\n permissions: options.permissions ?? { allow: [], deny: [] },\n hooks: options.hooks,\n },\n terminal: options.terminal,\n permissionHandler: options.permissionHandler,\n promptForApprovalFn: options.promptForApproval,\n sessionLogger: options.sessionLogger,\n onToolExecution: options.onToolExecution,\n hookTypeExecutors: options.hookTypeExecutors,\n transcriptPath,\n onProjectAllowTool: options.onProjectAllowTool,\n });\n}\n\nexport function buildSessionTrackers(\n options: ISessionOptions,\n model: string,\n sessionId: string,\n cwd: string,\n): { contextTracker: ContextWindowTracker; compactionOrchestrator: CompactionOrchestrator } {\n const contextTracker = new ContextWindowTracker(\n model,\n options.contextMaxTokens,\n options.autoCompactThreshold,\n );\n const compactionOrchestrator = new CompactionOrchestrator({\n sessionId,\n cwd,\n model,\n hooks: options.hooks,\n compactInstructions: options.compactInstructions,\n hookTypeExecutors: options.hookTypeExecutors,\n });\n return { contextTracker, compactionOrchestrator };\n}\n\nexport function buildRobota(\n options: ISessionOptions,\n permissionEnforcer: PermissionEnforcer,\n tools: IToolWithEventService[],\n provider: IAIProvider,\n model: string,\n systemMessage: string,\n): Robota {\n const wrappedTools = permissionEnforcer.wrapTools(tools);\n const agentConfig: IAgentConfig = {\n name: options.agentName ?? 'agent',\n aiProviders: [provider],\n defaultModel: {\n provider: provider.name,\n model,\n ...(options.effort !== undefined && { effort: options.effort }),\n },\n // Single source of truth for the system prompt (agent-level, not model config).\n systemMessage,\n tools: wrappedTools,\n logging: { enabled: false },\n ...(options.providerTimeout !== undefined && { timeout: options.providerTimeout }),\n ...(options.responseFormat ? { responseFormat: options.responseFormat } : {}),\n // CMD-005: the \"ask the user\" port rides the agent config into tool execution contexts.\n ...(options.ask ? { ask: options.ask } : {}),\n };\n return new Robota(agentConfig);\n}\n","/**\n * Session history operations — compaction and persistence helpers.\n *\n * Extracted from Session to keep session.ts under the 300-line limit.\n * Each function receives its dependencies explicitly.\n */\n\nimport { runHooks, createLogger } from '@robota-sdk/agent-core';\n\nimport type { CompactionOrchestrator } from './compaction-orchestrator.js';\nimport type { ContextWindowTracker } from './context-window-tracker.js';\nimport type { TSessionLogData } from './session-logger.js';\nimport type { ISessionRecord, ISessionStore } from './session-store.js';\nimport type { ICompactEvent, TCompactTrigger } from './session-types.js';\nimport type { IToolSchema } from '@robota-sdk/agent-core';\nimport type { Robota } from '@robota-sdk/agent-core';\nimport type {\n IAIProvider,\n THooksConfig,\n IHookInput,\n IHookTypeExecutor,\n} from '@robota-sdk/agent-core';\n\nconst logger = createLogger('SessionHistoryOps');\n\n/** Dependencies for compact() */\nexport interface ICompactContext {\n sessionId: string;\n cwd: string;\n systemMessage: string;\n robota: Robota;\n aiProvider: IAIProvider;\n compactionOrchestrator: CompactionOrchestrator;\n contextTracker: ContextWindowTracker;\n hooks: Record<string, unknown> | undefined;\n hookTypeExecutors: IHookTypeExecutor[] | undefined;\n onCompactCallback: ((summary: string) => void) | undefined;\n onCompactEventCallback: ((event: ICompactEvent) => void) | undefined;\n trigger: TCompactTrigger;\n log: (event: string, data: TSessionLogData) => void;\n}\n\n/**\n * Summarize the conversation to free context space.\n *\n * @param instructions - Optional focus instructions for the summary\n * @param ctx - Session state and callbacks\n */\nexport async function compact(\n instructions: string | undefined,\n ctx: ICompactContext,\n): Promise<void> {\n const history = ctx.robota.getHistory();\n if (history.length === 0) return;\n\n ctx.contextTracker.updateFromHistory(history);\n const before = ctx.contextTracker.getContextState();\n\n // Exclude system messages from compaction — they are preserved and re-injected after\n const nonSystemHistory = history.filter((msg) => msg.role !== 'system');\n const summary = await ctx.compactionOrchestrator.compact(\n ctx.aiProvider,\n nonSystemHistory,\n instructions,\n );\n\n // Clear history, re-inject system message, then inject summary.\n // System message must persist across compactions — it contains project context\n // (cwd, AGENTS.md, CLAUDE.md) that the AI needs for every response.\n ctx.robota.clearHistory();\n ctx.robota.injectMessage('system', ctx.systemMessage);\n ctx.robota.injectMessage('assistant', `[Context Summary]\\n${summary}`);\n\n // Reset token tracking based on the new shorter history\n ctx.contextTracker.updateFromHistory(ctx.robota.getHistory());\n\n // Fire PostCompact hook after history replacement is complete\n const postHookInput: IHookInput = {\n session_id: ctx.sessionId,\n cwd: ctx.cwd,\n hook_event_name: 'PostCompact',\n trigger: ctx.trigger,\n compact_summary: summary,\n };\n runHooks(\n ctx.hooks as THooksConfig | undefined,\n 'PostCompact',\n postHookInput,\n ctx.hookTypeExecutors,\n ).catch((error) => logger.warn('hook failed', { error }));\n\n // Notify via callback after compaction is fully complete\n const after = ctx.contextTracker.getContextState();\n ctx.log('context_compact', {\n trigger: ctx.trigger,\n before,\n after,\n });\n ctx.onCompactEventCallback?.({ trigger: ctx.trigger, before, after });\n if (ctx.onCompactCallback) {\n ctx.onCompactCallback(summary);\n }\n}\n\n/** Dependencies for persistSession() */\nexport interface IPersistContext {\n sessionId: string;\n cwd: string;\n systemPrompt: string;\n toolSchemas: IToolSchema[];\n sessionStore: ISessionStore;\n robota: Robota;\n getFullHistory: () => Array<{\n id: string;\n timestamp: Date;\n category: string;\n type: string;\n data?: unknown;\n }>;\n}\n\n/** Persist the current session to the store */\nexport function persistSession(ctx: IPersistContext): void {\n const history = ctx.robota.getHistory();\n const now = new Date().toISOString();\n\n const existing = ctx.sessionStore.load(ctx.sessionId);\n\n const record: ISessionRecord = {\n id: ctx.sessionId,\n name: existing?.name,\n cwd: ctx.cwd,\n createdAt: existing?.createdAt ?? now,\n updatedAt: now,\n messages: history,\n history: ctx.getFullHistory(),\n systemPrompt: ctx.systemPrompt,\n toolSchemas: ctx.toolSchemas,\n };\n\n ctx.sessionStore.save(record);\n}\n","/**\n * Session lifecycle helpers — provider configuration and session start hooks.\n *\n * Extracted from Session to keep session.ts under the 300-line limit.\n * All functions receive their dependencies explicitly.\n */\n\nimport { runHooks, createLogger } from '@robota-sdk/agent-core';\n\nimport type { TSessionLogData } from './session-logger.js';\nimport type { ISessionOptions } from './session-types.js';\nimport type {\n IAIProvider,\n TSessionEndReason,\n THooksConfig,\n IHookInput,\n IHookTypeExecutor,\n} from '@robota-sdk/agent-core';\n\nconst logger = createLogger('SessionLifecycle');\n\n/**\n * Configure provider-specific features: streaming, web tools, server tool logging.\n * Mutates the provider object in-place.\n */\nexport function configureProvider(\n provider: IAIProvider,\n _options: ISessionOptions,\n log: (event: string, data: TSessionLogData) => void,\n): void {\n provider.configureNativeWebTools?.({ webSearch: true });\n\n // Wire server tool logging\n if ('onServerToolUse' in provider) {\n (\n provider as { onServerToolUse?: (name: string, input: Record<string, string>) => void }\n ).onServerToolUse = (name: string, input: Record<string, string>) => {\n log('server_tool', { tool: name, ...input });\n };\n }\n}\n\n/**\n * Fire SessionStart hook asynchronously.\n * Calls onStdout when the hook produces stdout (used to seed the first run()).\n */\nexport function fireSessionStartHook(\n sessionId: string,\n cwd: string,\n hooks: Record<string, unknown> | undefined,\n hookTypeExecutors: IHookTypeExecutor[] | undefined,\n onStdout: (stdout: string) => void,\n permissionMode?: string,\n transcriptPath?: string,\n): void {\n const hookInput: IHookInput = {\n session_id: sessionId,\n cwd,\n hook_event_name: 'SessionStart',\n ...(permissionMode !== undefined && { permission_mode: permissionMode }),\n ...(transcriptPath !== undefined && { transcript_path: transcriptPath }),\n env: {\n CLAUDE_PROJECT_DIR: cwd,\n CLAUDE_SESSION_ID: sessionId,\n },\n };\n runHooks(hooks as THooksConfig | undefined, 'SessionStart', hookInput, hookTypeExecutors)\n .then((result) => {\n if (result.stdout) {\n onStdout(result.stdout);\n }\n })\n .catch((error) => logger.warn('SessionStart hook failed', { error }));\n}\n\n/** Fire SessionEnd hook and wait for hook completion before process exit. */\nexport async function fireSessionEndHook(\n sessionId: string,\n cwd: string,\n reason: TSessionEndReason,\n hooks: Record<string, unknown> | undefined,\n hookTypeExecutors: IHookTypeExecutor[] | undefined,\n permissionMode?: string,\n transcriptPath?: string,\n): Promise<void> {\n const hookInput: IHookInput = {\n session_id: sessionId,\n cwd,\n hook_event_name: 'SessionEnd',\n reason,\n ...(permissionMode !== undefined && { permission_mode: permissionMode }),\n ...(transcriptPath !== undefined && { transcript_path: transcriptPath }),\n env: {\n CLAUDE_PROJECT_DIR: cwd,\n CLAUDE_SESSION_ID: sessionId,\n },\n };\n await runHooks(hooks as THooksConfig | undefined, 'SessionEnd', hookInput, hookTypeExecutors);\n}\n","import type { ISessionOptions } from './session-types.js';\nimport type { TExecutionEventData, TToolArgs } from '@robota-sdk/agent-core';\n\nconst UNKNOWN_TOOL_ERROR_CODE = 'unknown_tool';\n\ntype TToolExecutionCallback = NonNullable<ISessionOptions['onToolExecution']>;\n\nexport interface IToolExecutionBridge {\n knownToolNames: ReadonlySet<string>;\n unknownToolCallIds: Set<string>;\n onToolExecution?: TToolExecutionCallback;\n}\n\nexport function createToolExecutionBridge(options: {\n knownToolNames: readonly string[];\n onToolExecution?: TToolExecutionCallback;\n}): IToolExecutionBridge {\n return {\n knownToolNames: new Set(options.knownToolNames),\n unknownToolCallIds: new Set<string>(),\n ...(options.onToolExecution && { onToolExecution: options.onToolExecution }),\n };\n}\n\nexport function forwardToolExecutionEvent(\n bridge: IToolExecutionBridge,\n event: string,\n data: TExecutionEventData,\n): void {\n if (!bridge.onToolExecution) return;\n if (event === 'tool_execution_request') {\n forwardUnknownToolStart(bridge, data);\n return;\n }\n if (event === 'tool_execution_result') {\n forwardUnknownToolEnd(bridge, data);\n }\n}\n\nfunction forwardUnknownToolStart(bridge: IToolExecutionBridge, data: TExecutionEventData): void {\n const toolName = getString(data.toolName);\n const toolCallId = getString(data.toolCallId);\n if (!toolName || !toolCallId || bridge.knownToolNames.has(toolName)) return;\n\n bridge.unknownToolCallIds.add(toolCallId);\n bridge.onToolExecution?.({\n type: 'start',\n toolName,\n toolArgs: toToolArgs(data.parameters),\n });\n}\n\nfunction forwardUnknownToolEnd(bridge: IToolExecutionBridge, data: TExecutionEventData): void {\n const toolName = getString(data.toolName);\n const toolCallId = getString(data.toolCallId);\n if (!toolName || !toolCallId) return;\n\n const metadata = getRecord(data.metadata);\n const isUnknown =\n bridge.unknownToolCallIds.has(toolCallId) || metadata?.errorCode === UNKNOWN_TOOL_ERROR_CODE;\n if (!isUnknown) return;\n\n bridge.unknownToolCallIds.delete(toolCallId);\n const error = getString(data.error) ?? `Tool \"${toolName}\" is not registered.`;\n bridge.onToolExecution?.({\n type: 'end',\n toolName,\n success: false,\n toolResultData: JSON.stringify({\n success: false,\n error,\n errorCode: UNKNOWN_TOOL_ERROR_CODE,\n requestedTool: getString(metadata?.requestedTool) ?? toolName,\n availableTools: getStringArray(metadata?.availableTools),\n }),\n });\n}\n\nfunction toToolArgs(value: unknown): TToolArgs | undefined {\n const record = getRecord(value);\n if (!record) return undefined;\n\n const args: TToolArgs = {};\n for (const [key, item] of Object.entries(record)) {\n if (\n typeof item === 'string' ||\n typeof item === 'number' ||\n typeof item === 'boolean' ||\n (typeof item === 'object' && item !== null)\n ) {\n args[key] = item;\n }\n }\n return args;\n}\n\nfunction getString(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\nfunction getRecord(value: unknown): Record<string, unknown> | undefined {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined;\n}\n\nfunction getStringArray(value: unknown): string[] {\n if (!Array.isArray(value)) return [];\n return value.filter((item): item is string => typeof item === 'string');\n}\n","/**\n * Session run — core execution logic for a single agent turn.\n *\n * Extracted from Session to keep session.ts under the 300-line limit.\n * Stateless: all mutable state is passed in via IRunContext.\n */\n\nimport {\n CONTEXT_ESTIMATE_CHARS_PER_TOKEN,\n createLogger,\n createUserMessage,\n getProviderCapabilities,\n runHooks,\n} from '@robota-sdk/agent-core';\n\nimport {\n createToolExecutionBridge,\n forwardToolExecutionEvent,\n} from './session-tool-execution-bridge.js';\n\nimport type { ContextWindowTracker } from './context-window-tracker.js';\nimport type { TSessionLogData } from './session-logger.js';\nimport type { ISessionOptions } from './session-types.js';\nimport type {\n IAIProvider,\n IContextWindowState,\n THooksConfig,\n IHookTypeExecutor,\n TTextDeltaCallback,\n} from '@robota-sdk/agent-core';\nimport type { Robota } from '@robota-sdk/agent-core';\n\nconst logger = createLogger('SessionRun');\n\n/** Dependencies injected by Session.run() */\nexport interface IRunContext {\n sessionId: string;\n cwd: string;\n model: string;\n /** Current permission mode — passed to all hook inputs as permission_mode */\n permissionMode?: string;\n /** Absolute path to session transcript file — passed to all hook inputs as transcript_path */\n transcriptPath?: string;\n robota: Robota;\n aiProvider: IAIProvider;\n contextTracker: ContextWindowTracker;\n hooks: Record<string, unknown> | undefined;\n hookTypeExecutors: IHookTypeExecutor[] | undefined;\n sessionStartStdout: string;\n log: (event: string, data: TSessionLogData) => void;\n compact: () => Promise<void>;\n persistSession: () => void;\n getSessionStore: () => boolean;\n clearSessionStartStdout: () => void;\n maxTurns?: number;\n onTextDelta?: TTextDeltaCallback;\n onContextUpdate?: (state: IContextWindowState) => void;\n onToolExecution?: ISessionOptions['onToolExecution'];\n knownToolNames?: readonly string[];\n}\n\n/**\n * Execute a single agent turn: run hooks, send message to AI, log results.\n *\n * @param message - The processed message to send to the AI\n * @param rawInput - Optional raw user input (used for hook prompt field)\n * @param ctx - Session state and callbacks\n * @param abortSignal - AbortSignal from the session's AbortController\n */\nexport async function executeRun(\n message: string,\n rawInput: string | undefined,\n ctx: IRunContext,\n abortSignal: AbortSignal,\n): Promise<string> {\n // Auto-compact BEFORE processing the new message (not after).\n // This prevents compaction from interfering with the current response stream.\n ctx.contextTracker.updateFromHistory(ctx.robota.getHistory());\n if (ctx.contextTracker.shouldAutoCompact()) {\n // Providers store onTextDelta as an instance property for their own internal streaming.\n // Compaction calls provider.chat() without passing onTextDelta in options, so the\n // provider falls back to this.onTextDelta. Temporarily clearing it prevents compaction\n // summary text from streaming to the UI. This workaround stays until provider packages\n // remove the instance-level onTextDelta property.\n const provider = ctx.aiProvider as { onTextDelta?: unknown };\n const savedDelta = provider.onTextDelta;\n provider.onTextDelta = undefined;\n try {\n await ctx.compact();\n } finally {\n provider.onTextDelta = savedDelta;\n }\n }\n\n ctx.log('user', { content: message });\n\n // Fire UserPromptSubmit hook before AI processes input\n const hookResult = await runHooks(\n ctx.hooks as THooksConfig | undefined,\n 'UserPromptSubmit',\n {\n session_id: ctx.sessionId,\n cwd: ctx.cwd,\n hook_event_name: 'UserPromptSubmit',\n user_message: rawInput ?? message,\n prompt: rawInput ?? message,\n ...(ctx.permissionMode !== undefined && { permission_mode: ctx.permissionMode }),\n ...(ctx.transcriptPath !== undefined && { transcript_path: ctx.transcriptPath }),\n env: {\n CLAUDE_PROJECT_DIR: ctx.cwd,\n CLAUDE_SESSION_ID: ctx.sessionId,\n },\n },\n ctx.hookTypeExecutors,\n );\n\n // Inject hook stdout into user message (e.g., plugin path info)\n const hookStdout = [ctx.sessionStartStdout, hookResult.stdout].filter(Boolean).join('\\n');\n const enrichedMessage = hookStdout\n ? `<system-reminder>\\n${hookStdout}\\n</system-reminder>\\n${message}`\n : message;\n // Clear sessionStart stdout after first injection\n ctx.clearSessionStartStdout();\n\n const history = ctx.robota.getHistory();\n const historyJson = JSON.stringify(history);\n const providerCapabilities = getProviderCapabilities(ctx.aiProvider);\n ctx.log('pre_run', {\n historyLength: history.length,\n historyChars: historyJson.length,\n historyEstTokens: Math.ceil(historyJson.length / CONTEXT_ESTIMATE_CHARS_PER_TOKEN),\n input: enrichedMessage,\n history,\n model: ctx.model,\n provider: ctx.aiProvider.name,\n maxTokens: ctx.contextTracker.getContextState().maxTokens,\n nativeWebSearchSupported: providerCapabilities.nativeWebTools.webSearch.supported,\n nativeWebSearchEnabled: providerCapabilities.nativeWebTools.webSearch.enabled,\n nativeWebFetchSupported: providerCapabilities.nativeWebTools.webFetch.supported,\n nativeWebFetchEnabled: providerCapabilities.nativeWebTools.webFetch.enabled,\n });\n ctx.contextTracker.updateFromHistory([...history, createUserMessage(enrichedMessage)]);\n ctx.onContextUpdate?.(ctx.contextTracker.getContextState());\n\n let response: string;\n try {\n const toolExecutionBridge = createToolExecutionBridge({\n knownToolNames: ctx.knownToolNames ?? [],\n ...(ctx.onToolExecution && { onToolExecution: ctx.onToolExecution }),\n });\n const onTextDelta = ctx.onTextDelta\n ? (delta: string): void => {\n ctx.log('text_delta', { delta });\n ctx.onTextDelta?.(delta);\n }\n : undefined;\n\n response = await ctx.robota.run(enrichedMessage, {\n signal: abortSignal,\n maxExecutionRounds: ctx.maxTurns ?? 0,\n onExecutionEvent: (event, data) => {\n ctx.log(event, data as TSessionLogData);\n forwardToolExecutionEvent(toolExecutionBridge, event, data);\n // BEHAVIOR-002: recompute and emit context per agentic round so the status bar\n // climbs live during a turn instead of jumping once at completion. The agent loop\n // runs entirely inside this single robota.run() call; assistant_message_committed\n // fires once per round with the round's usage already committed to history, which is\n // the right cadence — frequent enough to feel live, sparse enough to avoid render flooding.\n if (event === 'assistant_message_committed') {\n ctx.contextTracker.updateFromHistory(ctx.robota.getHistory());\n ctx.onContextUpdate?.(ctx.contextTracker.getContextState());\n }\n },\n ...(onTextDelta && { onTextDelta }),\n });\n\n // If execution was interrupted (abort fired during execution),\n // throw AbortError so the caller (useSubmitHandler) shows \"Cancelled.\"\n if (abortSignal.aborted) {\n throw new DOMException('Aborted', 'AbortError');\n }\n } catch (error) {\n ctx.log('error', {\n message: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? (error.stack ?? '') : '',\n historyLength: ctx.robota.getHistory().length,\n });\n runHooks(\n ctx.hooks as THooksConfig | undefined,\n 'StopFailure',\n {\n session_id: ctx.sessionId,\n cwd: ctx.cwd,\n hook_event_name: 'StopFailure',\n reason: error instanceof Error ? error.message : String(error),\n stop_hook_active: false,\n ...(ctx.permissionMode !== undefined && { permission_mode: ctx.permissionMode }),\n ...(ctx.transcriptPath !== undefined && { transcript_path: ctx.transcriptPath }),\n env: {\n CLAUDE_PROJECT_DIR: ctx.cwd,\n CLAUDE_SESSION_ID: ctx.sessionId,\n },\n },\n ctx.hookTypeExecutors,\n ).catch((error) => logger.warn('hook failed', { error }));\n throw error;\n }\n\n // Log the response and full history structure\n const postHistory = ctx.robota.getHistory();\n const historyStructure = postHistory.map((msg) => {\n const hasToolCalls =\n 'toolCalls' in msg && Array.isArray(msg.toolCalls) && msg.toolCalls.length > 0;\n const toolCallNames = hasToolCalls\n ? (msg.toolCalls as Array<{ function: { name: string } }>).map((tc) => tc.function.name)\n : [];\n return {\n role: msg.role,\n contentLength: typeof msg.content === 'string' ? msg.content.length : 0,\n hasToolCalls,\n toolCallNames,\n ...(msg.metadata ? { metadata: msg.metadata } : {}),\n };\n });\n ctx.log('assistant', {\n content: response,\n historyLength: postHistory.length,\n estimatedChars: JSON.stringify(postHistory).length,\n history: postHistory,\n historyStructure,\n });\n\n // Update token usage from the latest assistant message metadata\n ctx.contextTracker.updateFromHistory(postHistory);\n\n const ctxState = ctx.contextTracker.getContextState();\n ctx.onContextUpdate?.(ctxState);\n ctx.log('context', {\n maxTokens: ctxState.maxTokens,\n usedTokens: ctxState.usedTokens,\n usedPercentage: ctxState.usedPercentage,\n remainingPercentage: ctxState.remainingPercentage,\n });\n\n // Fire Stop hook after AI response is complete (informational, fire and forget)\n runHooks(\n ctx.hooks as THooksConfig | undefined,\n 'Stop',\n {\n session_id: ctx.sessionId,\n cwd: ctx.cwd,\n hook_event_name: 'Stop',\n response: response.substring(0, 500),\n last_assistant_message: response,\n stop_hook_active: false,\n ...(ctx.permissionMode !== undefined && { permission_mode: ctx.permissionMode }),\n ...(ctx.transcriptPath !== undefined && { transcript_path: ctx.transcriptPath }),\n env: {\n CLAUDE_PROJECT_DIR: ctx.cwd,\n CLAUDE_SESSION_ID: ctx.sessionId,\n },\n },\n ctx.hookTypeExecutors,\n ).catch((error) => logger.warn('hook failed', { error }));\n\n if (ctx.getSessionStore()) {\n ctx.persistSession();\n }\n\n return response;\n}\n","import { TRUST_TO_MODE } from '@robota-sdk/agent-core';\n\nimport { SessionBase } from './session-base.js';\nimport {\n buildPermissionEnforcer,\n buildRobota,\n buildSessionTrackers,\n} from './session-components.js';\nimport { compact, persistSession } from './session-history-ops.js';\nimport {\n configureProvider,\n fireSessionEndHook,\n fireSessionStartHook,\n} from './session-lifecycle.js';\nimport { executeRun } from './session-run.js';\n\nimport type { CompactionOrchestrator } from './compaction-orchestrator.js';\nimport type { ContextWindowTracker } from './context-window-tracker.js';\nimport type { PermissionEnforcer } from './permission-enforcer.js';\nimport type {\n TPermissionHandler,\n TPermissionResult,\n ITerminalOutput,\n ISpinner,\n} from './permission-types.js';\nimport type { ISessionLogger, TSessionLogData } from './session-logger.js';\nimport type { IRunContext } from './session-run.js';\nimport type { ISessionStore } from './session-store.js';\nimport type {\n ICompactEvent,\n ISessionOptions,\n ISessionShutdownOptions,\n TCompactTrigger,\n} from './session-types.js';\nimport type {\n IAIProvider,\n IContextWindowState,\n IToolSchema,\n TPermissionMode,\n IHookTypeExecutor,\n} from '@robota-sdk/agent-core';\nimport type { Robota } from '@robota-sdk/agent-core';\n\nexport type {\n ICompactEvent,\n TPermissionHandler,\n TPermissionResult,\n ITerminalOutput,\n ISpinner,\n ISessionOptions,\n ISessionShutdownOptions,\n TCompactTrigger,\n};\nexport type { TAutoCompactThreshold } from './context-window-tracker.js';\n\nconst ID_RADIX = 36;\nconst ID_RANDOM_LENGTH = 9;\n\n/** Wraps a Robota agent with project context, permission state, and optional persistence. */\nexport class Session extends SessionBase {\n protected readonly robota: Robota;\n protected readonly permissionEnforcer: PermissionEnforcer;\n protected readonly contextTracker: ContextWindowTracker;\n protected permissionMode: TPermissionMode;\n protected activePresetId: string;\n protected parallelSubagentsEnabled: boolean;\n protected readonly sessionId: string;\n protected aiProvider: IAIProvider;\n protected readonly toolSchemas: IToolSchema[];\n protected model: string;\n protected systemMessage: string;\n protected messageCount = 0;\n protected abortController: AbortController | null = null;\n private readonly terminal: ITerminalOutput;\n private readonly sessionStore?: ISessionStore;\n private readonly cwd: string;\n private readonly hooks?: Record<string, unknown>;\n private readonly hookTypeExecutors?: IHookTypeExecutor[];\n private readonly onTextDeltaCallback?: (delta: string) => void;\n private readonly onContextUpdateCallback?: (state: IContextWindowState) => void;\n private readonly onToolExecutionCallback?: ISessionOptions['onToolExecution'];\n private readonly onCompactCallback?: (summary: string) => void;\n private readonly onCompactEventCallback?: ISessionOptions['onCompactEvent'];\n private readonly sessionLogger?: ISessionLogger;\n private readonly maxTurns?: number;\n private readonly compactionOrchestrator: CompactionOrchestrator;\n private shutdownPromise: Promise<void> | null = null;\n /** Stdout collected from SessionStart hooks, injected on first run(). */\n private sessionStartStdout = '';\n /** Absolute path to the session transcript file, if file-backed storage is active. */\n private readonly transcriptPath: string | undefined;\n\n constructor(options: ISessionOptions) {\n super();\n const { tools, provider, systemMessage } = options;\n\n this.terminal = options.terminal;\n this.sessionStore = options.sessionStore;\n this.systemMessage = systemMessage;\n this.toolSchemas = tools.map((tool) => tool.schema);\n this.cwd = process.cwd();\n this.sessionLogger = options.sessionLogger;\n this.hooks = options.hooks;\n this.hookTypeExecutors = options.hookTypeExecutors;\n this.onTextDeltaCallback = options.onTextDelta;\n this.onContextUpdateCallback = options.onContextUpdate;\n this.onToolExecutionCallback = options.onToolExecution;\n this.onCompactCallback = options.onCompact;\n this.onCompactEventCallback = options.onCompactEvent;\n this.maxTurns = options.maxTurns;\n this.model = options.model ?? 'claude-sonnet-4-5';\n this.sessionId =\n options.sessionId ??\n `session_${Date.now()}_${Math.random().toString(ID_RADIX).substr(2, ID_RANDOM_LENGTH)}`;\n this.permissionMode =\n options.permissionMode ??\n (options.defaultTrustLevel ? TRUST_TO_MODE[options.defaultTrustLevel] : undefined) ??\n 'default';\n this.activePresetId = options.activePresetId ?? 'default';\n // PRESET-016: default true preserves the current behavior — subagent dispatch is allowed\n // unless a preset explicitly disables it.\n this.parallelSubagentsEnabled = options.enableParallelSubagents ?? true;\n this.transcriptPath = options.sessionStore?.getFilePath?.(this.sessionId);\n this.log('session_init', {\n cwd: this.cwd,\n systemPromptLength: systemMessage.length,\n systemPrompt: systemMessage,\n toolSchemas: this.toolSchemas,\n model: this.model,\n provider: provider.name,\n });\n this.aiProvider = provider;\n configureProvider(provider, options, (event, data) => this.log(event, data));\n this.permissionEnforcer = buildPermissionEnforcer(\n options,\n this.sessionId,\n this.cwd,\n () => this.permissionMode,\n this.transcriptPath,\n );\n const { contextTracker, compactionOrchestrator } = buildSessionTrackers(\n options,\n this.model,\n this.sessionId,\n this.cwd,\n );\n this.contextTracker = contextTracker;\n this.compactionOrchestrator = compactionOrchestrator;\n this.robota = buildRobota(\n options,\n this.permissionEnforcer,\n tools,\n provider,\n this.model,\n systemMessage,\n );\n fireSessionStartHook(\n this.sessionId,\n this.cwd,\n this.hooks,\n this.hookTypeExecutors,\n (stdout) => void (this.sessionStartStdout = stdout),\n this.permissionMode,\n this.transcriptPath,\n );\n }\n\n async run(message: string, rawInput?: string): Promise<string> {\n this.abortController = new AbortController();\n const { signal } = this.abortController;\n try {\n const response = await executeRun(message, rawInput, this.buildRunContext(), signal);\n this.messageCount += 1;\n return response;\n } finally {\n this.abortController = null;\n }\n }\n\n private log(event: string, data: TSessionLogData): void {\n this.sessionLogger?.log(this.sessionId, event, data);\n }\n\n private persistSessionInternal(): void {\n if (!this.sessionStore) return;\n persistSession({\n sessionId: this.sessionId,\n cwd: this.cwd,\n systemPrompt: this.systemMessage,\n toolSchemas: this.toolSchemas,\n sessionStore: this.sessionStore,\n robota: this.robota,\n getFullHistory: () => this.getFullHistory(),\n });\n }\n\n /**\n * Gracefully end the session and fire SessionEnd hooks once — **best-effort** (CORE-013\n * disposal convention): never rejects, so `void session.shutdown()` cannot become an\n * unhandled rejection. Step failures are recorded to the session log and remaining steps\n * still run.\n */\n shutdown(options: ISessionShutdownOptions = {}): Promise<void> {\n if (this.shutdownPromise) return this.shutdownPromise;\n const reason = options.reason ?? 'other';\n const step = async (label: string, run: () => Promise<void> | void): Promise<void> => {\n try {\n await run();\n } catch (error) {\n // allow-fallback: best-effort disposal IS the contract — the failure is logged and remaining shutdown steps still run (CORE-013 convention)\n this.log('session_shutdown_step_error', {\n step: label,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n };\n this.shutdownPromise = (async () => {\n await step('abort', () => this.abort());\n this.log('session_shutdown', { reason });\n await step('persist', () => this.persistSessionInternal());\n await step('session-end-hook', () =>\n fireSessionEndHook(\n this.sessionId,\n this.cwd,\n reason,\n this.hooks,\n this.hookTypeExecutors,\n this.permissionMode,\n this.transcriptPath,\n ),\n );\n // CORE-022 (SPEC § Disposal Chain Contract): shutdown drives agent destruction —\n // plugins are disposed so no timers/listeners survive and the process can exit.\n await step('destroy-agent', async () => {\n await this.robota.destroy();\n });\n })();\n return this.shutdownPromise;\n }\n\n swapProvider(newProvider: IAIProvider, model: string): void {\n this.robota.swapDefaultProvider(newProvider, model);\n newProvider.configureNativeWebTools?.({ webSearch: true });\n if ('onServerToolUse' in newProvider) {\n (\n newProvider as { onServerToolUse?: (name: string, input: Record<string, string>) => void }\n ).onServerToolUse = (name: string, input: Record<string, string>) =>\n this.log('server_tool', { tool: name, ...input });\n }\n this.aiProvider = newProvider;\n }\n\n async compact(instructions?: string, trigger: TCompactTrigger = 'manual'): Promise<void> {\n await compact(instructions, {\n sessionId: this.sessionId,\n cwd: this.cwd,\n systemMessage: this.systemMessage,\n robota: this.robota,\n aiProvider: this.aiProvider,\n compactionOrchestrator: this.compactionOrchestrator,\n contextTracker: this.contextTracker,\n hooks: this.hooks,\n hookTypeExecutors: this.hookTypeExecutors,\n onCompactCallback: this.onCompactCallback,\n onCompactEventCallback: this.onCompactEventCallback,\n trigger,\n log: (event, data) => this.log(event, data),\n });\n }\n\n private buildRunContext(): IRunContext {\n return {\n sessionId: this.sessionId,\n cwd: this.cwd,\n model: this.model,\n robota: this.robota,\n aiProvider: this.aiProvider,\n contextTracker: this.contextTracker,\n hooks: this.hooks,\n hookTypeExecutors: this.hookTypeExecutors,\n sessionStartStdout: this.sessionStartStdout,\n log: (event: string, data: TSessionLogData) => this.log(event, data),\n compact: () => this.compact(undefined, 'auto'),\n persistSession: () => this.persistSessionInternal(),\n getSessionStore: () => !!this.sessionStore,\n clearSessionStartStdout: () => void (this.sessionStartStdout = ''),\n permissionMode: this.permissionMode,\n transcriptPath: this.transcriptPath,\n maxTurns: this.maxTurns,\n onTextDelta: this.onTextDeltaCallback,\n onContextUpdate: this.onContextUpdateCallback,\n onToolExecution: this.onToolExecutionCallback,\n knownToolNames: this.toolSchemas.map((tool) => tool.name),\n };\n }\n}\n","/**\n * Session Logger — pluggable logging interface for session events.\n *\n * ISessionLogger defines the contract. FileSessionLogger is the default\n * implementation that writes JSONL to disk. Consumers can implement their\n * own (e.g., remote, database, silent) and inject via Session constructor.\n */\n\nimport { createHash } from 'node:crypto';\nimport { mkdirSync, appendFileSync, existsSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\n/** Session log event data — extensible record of event metadata. */\nexport type TSessionLogValue = string | number | boolean | object | null | undefined;\nexport type TSessionLogData = Record<string, TSessionLogValue>;\n\nexport interface IExternalPayloadReference {\n kind: 'external-payload';\n encoding: 'json';\n sha256: string;\n byteLength: number;\n relativePath: string;\n}\n\nexport interface IFileSessionLoggerOptions {\n externalPayloadThresholdBytes?: number;\n redactedValue?: string;\n}\n\nconst BYTES_PER_KIB = 1024;\nconst DEFAULT_EXTERNAL_PAYLOAD_THRESHOLD_KIB = 32;\nconst DEFAULT_EXTERNAL_PAYLOAD_THRESHOLD_BYTES =\n DEFAULT_EXTERNAL_PAYLOAD_THRESHOLD_KIB * BYTES_PER_KIB;\nconst DEFAULT_REDACTED_VALUE = '[REDACTED]';\nconst SENSITIVE_KEY_PATTERN =\n /^(api[-_]?key|authorization|access[-_]?token|refresh[-_]?token|secret|password|x[-_]?api[-_]?key)$/i;\n\n/**\n * Session logger interface — injected into Session for pluggable logging.\n *\n * Implementations decide where and how to persist session events.\n * The Session class calls log() for every significant action.\n */\nexport interface ISessionLogger {\n /** Log a session event with structured data. */\n log(sessionId: string, event: string, data: TSessionLogData): void;\n}\n\n/**\n * File-based session logger — writes JSONL to {logDir}/{sessionId}.jsonl.\n *\n * This is the default implementation used by the CLI.\n * Each line is a self-contained JSON object with timestamp, sessionId, event, and data.\n */\nexport class FileSessionLogger implements ISessionLogger {\n private readonly logDir: string;\n private readonly options: Required<IFileSessionLoggerOptions>;\n\n constructor(logDir: string, options: IFileSessionLoggerOptions = {}) {\n this.logDir = logDir;\n this.options = {\n externalPayloadThresholdBytes:\n options.externalPayloadThresholdBytes ?? DEFAULT_EXTERNAL_PAYLOAD_THRESHOLD_BYTES,\n redactedValue: options.redactedValue ?? DEFAULT_REDACTED_VALUE,\n };\n try {\n mkdirSync(logDir, { recursive: true });\n } catch {\n // Best-effort: logging disabled if directory cannot be created\n }\n }\n\n log(sessionId: string, event: string, data: TSessionLogData): void {\n try {\n const normalizedData = normalizeLogData(sessionId, this.logDir, data, this.options);\n const entry = JSON.stringify({\n timestamp: new Date().toISOString(),\n sessionId,\n event,\n ...normalizedData,\n });\n const logFile = join(this.logDir, `${sessionId}.jsonl`);\n appendFileSync(logFile, entry + '\\n');\n } catch {\n // Logging failure must never break the session\n }\n }\n}\n\nfunction normalizeLogData(\n sessionId: string,\n logDir: string,\n data: TSessionLogData,\n options: Required<IFileSessionLoggerOptions>,\n): TSessionLogData {\n const normalized: TSessionLogData = {};\n for (const [key, value] of Object.entries(data)) {\n normalized[key] = normalizeLogValue(sessionId, logDir, key, value, options);\n }\n return normalized;\n}\n\nfunction normalizeLogValue(\n sessionId: string,\n logDir: string,\n key: string,\n value: TSessionLogValue,\n options: Required<IFileSessionLoggerOptions>,\n): TSessionLogValue {\n if (SENSITIVE_KEY_PATTERN.test(key)) {\n return options.redactedValue;\n }\n if (\n value === null ||\n value === undefined ||\n typeof value === 'string' ||\n typeof value === 'number'\n ) {\n return maybeExternalizePayload(sessionId, logDir, value, options);\n }\n if (typeof value === 'boolean') {\n return value;\n }\n if (value instanceof Date) {\n return value.toISOString();\n }\n if (Array.isArray(value)) {\n const normalizedArray = value.map((item) =>\n normalizeLogValue(sessionId, logDir, key, item as TSessionLogValue, options),\n );\n return maybeExternalizePayload(sessionId, logDir, normalizedArray, options);\n }\n if (typeof value === 'object') {\n const record = value as Record<string, TSessionLogValue>;\n const normalizedRecord: Record<string, TSessionLogValue> = {};\n for (const [childKey, childValue] of Object.entries(record)) {\n normalizedRecord[childKey] = normalizeLogValue(\n sessionId,\n logDir,\n childKey,\n childValue,\n options,\n );\n }\n return maybeExternalizePayload(sessionId, logDir, normalizedRecord, options);\n }\n return String(value);\n}\n\nfunction maybeExternalizePayload(\n sessionId: string,\n logDir: string,\n value: TSessionLogValue,\n options: Required<IFileSessionLoggerOptions>,\n): TSessionLogValue {\n const serialized = JSON.stringify(value);\n if (serialized === undefined) {\n return value;\n }\n const byteLength = Buffer.byteLength(serialized);\n if (byteLength <= options.externalPayloadThresholdBytes) {\n return value;\n }\n\n const sha256 = createHash('sha256').update(serialized).digest('hex');\n const payloadDirName = `${sessionId}.payloads`;\n const payloadFileName = `${sha256}.json`;\n const relativePath = join(payloadDirName, payloadFileName);\n const payloadDir = join(logDir, payloadDirName);\n const payloadPath = join(logDir, relativePath);\n mkdirSync(payloadDir, { recursive: true });\n if (!existsSync(payloadPath)) {\n writeFileSync(payloadPath, serialized, 'utf-8');\n }\n return {\n kind: 'external-payload',\n encoding: 'json',\n sha256,\n byteLength,\n relativePath,\n } satisfies IExternalPayloadReference;\n}\n\n/** No-op logger — used when logging is disabled. */\nexport class SilentSessionLogger implements ISessionLogger {\n log(): void {\n // intentionally empty\n }\n}\n","/**\n * INFRA-017: typed contract for session-log event names + replay keys (SSOT).\n *\n * The `FileSessionLogger` writes JSONL lines `{ timestamp, sessionId, event, ...data }`. The event\n * names were previously implicit string literals scattered across the session/execution code. This\n * module names them once so the writer, the replay validator (`session-log-validation.ts`), and the\n * session-log replay provider (INFRA-017 / TEST-008) share one type-safe schema — without changing\n * what is written (it formalizes the existing format, it does not add a new one).\n *\n * The **replay substrate** is the provider/tool execution layer, keyed deterministically:\n * a `provider_request` (executionId + round) is answered by its recorded\n * `provider_native_raw_payload` / `provider_response_normalized`; a `tool_execution_request`\n * (executionId + toolCallId) by its `tool_execution_result`. `validateSessionReplayLogEntries`\n * proves a log carries all of these (i.e. is replay-complete).\n */\n\n/** Canonical session-log event names. */\nexport const SESSION_LOG_EVENT = {\n // Session lifecycle / context\n sessionInit: 'session_init',\n sessionShutdown: 'session_shutdown',\n context: 'context',\n contextCompact: 'context_compact',\n error: 'error',\n\n // Canonical conversation substrate (resume): history mutations append messages.\n historyMutation: 'history_mutation',\n\n // Provider replay substrate (keyed by executionId + round).\n providerRequest: 'provider_request',\n providerNativeRawPayload: 'provider_native_raw_payload',\n providerResponseRaw: 'provider_response_raw',\n providerResponseNormalized: 'provider_response_normalized',\n\n // Tool replay substrate (keyed by executionId + toolCallId).\n toolExecutionRequest: 'tool_execution_request',\n toolExecutionResult: 'tool_execution_result',\n\n // Observability (display/debug; not the replay substrate).\n user: 'user',\n preRun: 'pre_run',\n textDelta: 'text_delta',\n assistant: 'assistant',\n toolCall: 'tool_call',\n toolResult: 'tool_result',\n toolBlocked: 'tool_blocked',\n toolDenied: 'tool_denied',\n serverTool: 'server_tool',\n} as const;\n\nexport type TSessionLogEventName = (typeof SESSION_LOG_EVENT)[keyof typeof SESSION_LOG_EVENT];\n\n/** Common envelope written for every line by `FileSessionLogger`. */\nexport interface ISessionLogLine {\n readonly timestamp: string;\n readonly sessionId: string;\n readonly event: string;\n readonly [key: string]: unknown;\n}\n\n/** Replay correlation key for a provider call. */\nexport interface IProviderEventKey {\n readonly executionId: string;\n readonly round: number;\n}\n\n/** Replay correlation key for a tool execution. */\nexport interface IToolEventKey {\n readonly executionId: string;\n readonly toolCallId: string;\n}\n\n/** Narrow a raw log line to a specific event name. */\nexport function isSessionLogEvent<TName extends TSessionLogEventName>(\n line: ISessionLogLine,\n name: TName,\n): line is ISessionLogLine & { event: TName } {\n return line.event === name;\n}\n","import type { ISessionLogEntry } from './session-log-replay.js';\nimport type { TUniversalValue } from '@robota-sdk/agent-core';\n\nexport interface ISessionReplayValidationIssue {\n code:\n | 'PROVIDER_RESPONSE_RAW_MISSING'\n | 'PROVIDER_NATIVE_RAW_PAYLOAD_MISSING'\n | 'PROVIDER_RESPONSE_NORMALIZED_MISSING'\n | 'TOOL_RESULT_MISSING'\n | 'PAYLOAD_REFERENCE_INVALID';\n message: string;\n eventIndex?: number;\n executionId?: string;\n round?: number;\n toolCallId?: string;\n}\n\nexport interface ISessionReplayValidationResult {\n ok: boolean;\n issues: ISessionReplayValidationIssue[];\n}\n\nexport function validateSessionReplayLogEntries(\n entries: readonly ISessionLogEntry[],\n): ISessionReplayValidationResult {\n const issues: ISessionReplayValidationIssue[] = [];\n const providerEvents = createProviderReplayEventIndex();\n const toolEvents = createToolReplayEventIndex();\n\n entries.forEach((entry, index) => {\n collectPayloadReferenceIssues(entry, index, issues);\n collectProviderReplayEvent(providerEvents, entry, index);\n collectToolReplayEvent(toolEvents, entry, index);\n });\n\n appendProviderReplayIssues(providerEvents, issues);\n appendToolReplayIssues(toolEvents, issues);\n\n return { ok: issues.length === 0, issues };\n}\n\ninterface IProviderReplayRequest {\n executionId: string;\n round: number;\n index: number;\n}\n\ninterface IProviderReplayEventIndex {\n requests: Map<string, IProviderReplayRequest>;\n nativeRawPayloads: Set<string>;\n rawResponses: Set<string>;\n normalizedResponses: Set<string>;\n}\n\ninterface IToolReplayRequest {\n executionId: string;\n toolCallId: string;\n index: number;\n}\n\ninterface IToolReplayEventIndex {\n requests: Map<string, IToolReplayRequest>;\n results: Set<string>;\n}\n\nfunction createProviderReplayEventIndex(): IProviderReplayEventIndex {\n return {\n requests: new Map<string, IProviderReplayRequest>(),\n nativeRawPayloads: new Set<string>(),\n rawResponses: new Set<string>(),\n normalizedResponses: new Set<string>(),\n };\n}\n\nfunction createToolReplayEventIndex(): IToolReplayEventIndex {\n return {\n requests: new Map<string, IToolReplayRequest>(),\n results: new Set<string>(),\n };\n}\n\nfunction collectProviderReplayEvent(\n events: IProviderReplayEventIndex,\n entry: ISessionLogEntry,\n index: number,\n): void {\n const key = providerKey(entry);\n if (!key) return;\n if (entry.event === 'provider_request') {\n events.requests.set(key.key, {\n executionId: key.executionId,\n round: key.round,\n index,\n });\n }\n if (entry.event === 'provider_response_raw') {\n events.rawResponses.add(key.key);\n }\n if (\n entry.event === 'provider_native_raw_payload' &&\n (entry.payloadKind === 'response' || entry.payloadKind === 'stream_event')\n ) {\n events.nativeRawPayloads.add(key.key);\n }\n if (entry.event === 'provider_response_normalized') {\n events.normalizedResponses.add(key.key);\n }\n}\n\nfunction collectToolReplayEvent(\n events: IToolReplayEventIndex,\n entry: ISessionLogEntry,\n index: number,\n): void {\n const key = toolKey(entry);\n if (!key) return;\n if (entry.event === 'tool_execution_request') {\n events.requests.set(key.key, {\n executionId: key.executionId,\n toolCallId: key.toolCallId,\n index,\n });\n }\n if (entry.event === 'tool_execution_result') {\n events.results.add(key.key);\n }\n}\n\nfunction appendProviderReplayIssues(\n events: IProviderReplayEventIndex,\n issues: ISessionReplayValidationIssue[],\n): void {\n for (const [key, request] of events.requests) {\n if (!events.nativeRawPayloads.has(key)) {\n issues.push({\n code: 'PROVIDER_NATIVE_RAW_PAYLOAD_MISSING',\n message: `Provider request ${key} has no provider-native raw response or stream payload event.`,\n eventIndex: request.index,\n executionId: request.executionId,\n round: request.round,\n });\n }\n if (!events.rawResponses.has(key)) {\n issues.push({\n code: 'PROVIDER_RESPONSE_RAW_MISSING',\n message: `Provider request ${key} has no raw response event.`,\n eventIndex: request.index,\n executionId: request.executionId,\n round: request.round,\n });\n }\n if (!events.normalizedResponses.has(key)) {\n issues.push({\n code: 'PROVIDER_RESPONSE_NORMALIZED_MISSING',\n message: `Provider request ${key} has no normalized response event.`,\n eventIndex: request.index,\n executionId: request.executionId,\n round: request.round,\n });\n }\n }\n}\n\nfunction appendToolReplayIssues(\n events: IToolReplayEventIndex,\n issues: ISessionReplayValidationIssue[],\n): void {\n for (const [key, request] of events.requests) {\n if (!events.results.has(key)) {\n issues.push({\n code: 'TOOL_RESULT_MISSING',\n message: `Tool request ${key} has no terminal result event.`,\n eventIndex: request.index,\n executionId: request.executionId,\n toolCallId: request.toolCallId,\n });\n }\n }\n}\n\nfunction providerKey(\n entry: ISessionLogEntry,\n): { key: string; executionId: string; round: number } | undefined {\n if (typeof entry.executionId !== 'string') return undefined;\n const round = typeof entry.round === 'number' ? entry.round : Number(entry.round);\n if (!Number.isFinite(round)) return undefined;\n return { key: `${entry.executionId}:${round}`, executionId: entry.executionId, round };\n}\n\nfunction toolKey(\n entry: ISessionLogEntry,\n): { key: string; executionId: string; toolCallId: string } | undefined {\n if (typeof entry.executionId !== 'string') return undefined;\n const toolCallId =\n typeof entry.toolCallId === 'string'\n ? entry.toolCallId\n : typeof entry.toolExecutionId === 'string'\n ? entry.toolExecutionId\n : undefined;\n if (!toolCallId) return undefined;\n return { key: `${entry.executionId}:${toolCallId}`, executionId: entry.executionId, toolCallId };\n}\n\nfunction collectPayloadReferenceIssues(\n value: TUniversalValue,\n eventIndex: number,\n issues: ISessionReplayValidationIssue[],\n): void {\n if (Array.isArray(value)) {\n value.forEach((item) => collectPayloadReferenceIssues(item, eventIndex, issues));\n return;\n }\n if (!isRecord(value)) return;\n if (value.kind === 'external-payload') {\n if (\n value.encoding !== 'json' ||\n typeof value.sha256 !== 'string' ||\n typeof value.relativePath !== 'string' ||\n typeof value.byteLength !== 'number'\n ) {\n issues.push({\n code: 'PAYLOAD_REFERENCE_INVALID',\n message: 'External payload reference is missing required replay fields.',\n eventIndex,\n });\n }\n return;\n }\n Object.values(value).forEach((child) => collectPayloadReferenceIssues(child, eventIndex, issues));\n}\n\nfunction isRecord(value: TUniversalValue): value is Record<string, TUniversalValue> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n","import { existsSync, readFileSync } from 'node:fs';\n\nimport { messageToHistoryEntry } from '@robota-sdk/agent-core';\n\nimport type { IHistoryEntry, TUniversalMessage, TUniversalValue } from '@robota-sdk/agent-core';\n\nexport interface ISessionLogEntry extends Record<string, TUniversalValue> {\n timestamp: string;\n sessionId: string;\n event: string;\n}\n\nexport interface ISessionReplayRecord {\n sessionId: string | undefined;\n cwd: string | undefined;\n createdAt: string | undefined;\n updatedAt: string | undefined;\n messages: TUniversalMessage[];\n history: IHistoryEntry[];\n backgroundTaskEvents: object[];\n backgroundJobGroupEvents: object[];\n memoryEvents: object[];\n}\n\nexport { validateSessionReplayLogEntries } from './session-log-validation.js';\nexport type {\n ISessionReplayValidationIssue,\n ISessionReplayValidationResult,\n} from './session-log-validation.js';\n\nexport function loadSessionLogEntries(logFile: string): ISessionLogEntry[] {\n if (!existsSync(logFile)) {\n return [];\n }\n return readFileSync(logFile, 'utf-8')\n .split('\\n')\n .map((line) => line.trim())\n .filter((line) => line.length > 0)\n .map((line) => JSON.parse(line) as ISessionLogEntry);\n}\n\nexport function replaySessionLogEntries(\n entries: readonly ISessionLogEntry[],\n): ISessionReplayRecord {\n const messages: TUniversalMessage[] = [];\n const history: IHistoryEntry[] = [];\n const auxiliaryEvents: IAuxiliaryReplayEvents = {\n backgroundTaskEvents: [],\n backgroundJobGroupEvents: [],\n memoryEvents: [],\n };\n let sessionId: string | undefined;\n let cwd: string | undefined;\n let createdAt: string | undefined;\n let updatedAt: string | undefined;\n\n for (const entry of entries) {\n sessionId = sessionId ?? entry.sessionId;\n createdAt = createdAt ?? entry.timestamp;\n updatedAt = entry.timestamp;\n\n if (entry.event === 'session_init') {\n cwd = typeof entry.cwd === 'string' ? entry.cwd : cwd;\n }\n\n if (entry.event === 'history_mutation' && entry.mutation === 'append_message') {\n const message = normalizeLogMessage(entry.message);\n if (message) {\n messages.push(message);\n history.push(messageToHistoryEntry(message));\n }\n }\n\n collectAuxiliaryReplayEvent(entry, auxiliaryEvents);\n }\n\n return {\n sessionId,\n cwd,\n createdAt,\n updatedAt,\n messages,\n history,\n backgroundTaskEvents: auxiliaryEvents.backgroundTaskEvents,\n backgroundJobGroupEvents: auxiliaryEvents.backgroundJobGroupEvents,\n memoryEvents: auxiliaryEvents.memoryEvents,\n };\n}\n\ninterface IAuxiliaryReplayEvents {\n backgroundTaskEvents: object[];\n backgroundJobGroupEvents: object[];\n memoryEvents: object[];\n}\n\nfunction collectAuxiliaryReplayEvent(\n entry: ISessionLogEntry,\n auxiliaryEvents: IAuxiliaryReplayEvents,\n): void {\n if (entry.event === 'background_task_event') {\n pushObjectPayload(auxiliaryEvents.backgroundTaskEvents, entry, 'backgroundEvent', 'data');\n return;\n }\n if (entry.event === 'background_job_group_event') {\n pushObjectPayload(\n auxiliaryEvents.backgroundJobGroupEvents,\n entry,\n 'backgroundJobGroupEvent',\n 'data',\n );\n return;\n }\n if (entry.event === 'memory_event') {\n pushObjectPayload(auxiliaryEvents.memoryEvents, entry, 'memoryEvent', 'data');\n }\n}\n\nfunction normalizeLogMessage(value: TUniversalValue): TUniversalMessage | undefined {\n if (!isRecord(value)) return undefined;\n const role = value.role;\n if (role !== 'user' && role !== 'assistant' && role !== 'system' && role !== 'tool') {\n return undefined;\n }\n const id = typeof value.id === 'string' ? value.id : `${role}-${Date.now()}`;\n const timestamp =\n value.timestamp instanceof Date\n ? value.timestamp\n : new Date(typeof value.timestamp === 'string' ? value.timestamp : Date.now());\n return {\n ...value,\n id,\n role,\n timestamp,\n } as TUniversalMessage;\n}\n\nfunction isRecord(value: TUniversalValue): value is Record<string, TUniversalValue> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction getObjectPayload(entry: ISessionLogEntry, key: string): object | undefined {\n const value = entry[key];\n if (\n typeof value !== 'object' ||\n value === null ||\n Array.isArray(value) ||\n value instanceof Date\n ) {\n return undefined;\n }\n return value;\n}\n\nfunction pushObjectPayload(\n target: object[],\n entry: ISessionLogEntry,\n primaryKey: string,\n fallbackKey: string,\n): void {\n const payload = getObjectPayload(entry, primaryKey) ?? getObjectPayload(entry, fallbackKey);\n if (payload) target.push(payload);\n}\n","/**\n * SessionStore — persists conversation sessions as JSON files.\n *\n * Sessions are stored at `~/.robota/sessions/{id}.json` by default.\n * Consumers can inject a project-local directory such as `.robota/sessions`.\n * The store directory is created on first write if it does not exist.\n */\n\nimport {\n readFileSync,\n writeFileSync,\n existsSync,\n mkdirSync,\n unlinkSync,\n readdirSync,\n renameSync,\n} from 'fs';\nimport { join } from 'path';\n\nimport type { IToolSchema } from '@robota-sdk/agent-core';\n\n/** A persisted session record */\nexport interface ISessionRecord {\n /** Unique session identifier */\n id: string;\n /** Optional human-readable session name */\n name?: string;\n /** Working directory when the session was created */\n cwd: string;\n /** ISO-8601 creation timestamp */\n createdAt: string;\n /** ISO-8601 last-updated timestamp */\n updatedAt: string;\n /** Conversation messages (opaque to the store) */\n messages: unknown[];\n /** Full UI timeline (chat + events) for rendering restoration */\n history?: unknown[];\n /** Exact system prompt used to create the session. */\n systemPrompt?: string;\n /** Tool schemas registered for the session. */\n toolSchemas?: IToolSchema[];\n /** Latest background task snapshots for resume/debugging. */\n backgroundTasks?: unknown[];\n /** Durable non-streaming background task events for resume/debugging. */\n backgroundTaskEvents?: unknown[];\n /** Latest background job group snapshots for resume/debugging. */\n backgroundJobGroups?: unknown[];\n /** Durable background job group events for resume/debugging. */\n backgroundJobGroupEvents?: unknown[];\n /** Durable skill activation events for resume/debugging. */\n skillActivationEvents?: unknown[];\n /** Durable automatic memory events for resume/debugging. */\n memoryEvents?: unknown[];\n /** Memory references used by the latest prompt turn. */\n usedMemoryReferences?: unknown[];\n /** SDK-owned context reference inventory for resume/debugging. */\n contextReferences?: unknown[];\n /** Provider sandbox snapshot identifier for workspace hydration on resume. */\n sandboxSnapshotId?: string;\n}\n\n/** Minimal persistence port consumed by Session. */\nexport interface ISessionStore {\n save(session: ISessionRecord): void;\n load(id: string): ISessionRecord | undefined;\n list(): ISessionRecord[];\n delete(id: string): void;\n /** Return the absolute file path for a session file, if the store is file-backed. */\n getFilePath?(id: string): string;\n}\n\n/**\n * Return the current user home directory.\n * Reads process.env.HOME at call time so tests can override it.\n */\nfunction getHomeDir(): string {\n return process.env.HOME ?? process.env.USERPROFILE ?? '/';\n}\n\n/**\n * Persistent session store backed by individual JSON files.\n *\n * Construct with a custom `baseDir` to redirect storage (useful in tests).\n */\nexport class SessionStore implements ISessionStore {\n private readonly baseDir: string;\n\n constructor(baseDir?: string) {\n this.baseDir = baseDir ?? join(getHomeDir(), '.robota', 'sessions');\n }\n\n /** Ensure the storage directory exists */\n private ensureDir(): void {\n if (!existsSync(this.baseDir)) {\n mkdirSync(this.baseDir, { recursive: true });\n }\n }\n\n /** Absolute path to a session's JSON file */\n private filePath(id: string): string {\n return join(this.baseDir, `${id}.json`);\n }\n\n /** Return the absolute file path for a session — implements ISessionStore.getFilePath */\n getFilePath(id: string): string {\n return this.filePath(id);\n }\n\n /**\n * Persist a session record to disk atomically (CORE-019).\n * Creates the storage directory if needed.\n *\n * Bytes go to a same-directory temp file first, then move into place with rename —\n * a crash mid-write can therefore never leave a truncated JSON where the previous\n * record used to be. Same-directory is load-bearing: cross-device rename is a copy.\n */\n save(session: ISessionRecord): void {\n this.ensureDir();\n const finalPath = this.filePath(session.id);\n const tempPath = `${finalPath}.${process.pid}.tmp`;\n const serialized = JSON.stringify(session, null, 2);\n writeFileSync(tempPath, serialized, 'utf-8');\n try {\n renameSync(tempPath, finalPath);\n } catch (error) {\n unlinkSync(tempPath);\n throw error;\n }\n }\n\n /**\n * Load a session by its ID.\n * Returns `undefined` when the session file does not exist or is corrupt.\n */\n load(id: string): ISessionRecord | undefined {\n const path = this.filePath(id);\n if (!existsSync(path)) {\n return undefined;\n }\n try {\n const raw = readFileSync(path, 'utf-8');\n return JSON.parse(raw) as ISessionRecord;\n } catch {\n // allow-fallback: corrupt session file is unrecoverable; treat as missing to avoid crash on --continue/--resume\n return undefined;\n }\n }\n\n /**\n * List all persisted sessions, sorted by `updatedAt` descending (most recent first).\n */\n list(): ISessionRecord[] {\n if (!existsSync(this.baseDir)) {\n return [];\n }\n\n const files = readdirSync(this.baseDir).filter((f) => f.endsWith('.json'));\n const sessions: ISessionRecord[] = [];\n\n for (const file of files) {\n try {\n const raw = readFileSync(join(this.baseDir, file), 'utf-8');\n const record = JSON.parse(raw) as ISessionRecord;\n sessions.push(record);\n } catch {\n // Skip malformed files\n }\n }\n\n return sessions.sort(\n (a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime(),\n );\n }\n\n /**\n * Delete a session by its ID.\n * No-ops silently if the session does not exist.\n */\n delete(id: string): void {\n const path = this.filePath(id);\n if (existsSync(path)) {\n unlinkSync(path);\n }\n }\n}\n"],"mappings":"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"}
|