@robota-sdk/agent-session 3.0.0-beta.78 → 3.0.0-beta.81
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 +652 -0
- package/README.md +117 -51
- package/dist/node/index.cjs +9 -6
- package/dist/node/index.d.cts +1620 -0
- package/dist/node/index.d.cts.map +1 -0
- package/dist/node/index.d.ts +1144 -148
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +9 -6
- package/dist/node/index.js.map +1 -1
- package/package.json +35 -20
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-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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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["logger","logger","logger","logger","logger","DEFAULT_REDACTED_VALUE","logger","SHA256_PATTERN","isPlainRecord","isPlainRecord","resolve","existsSync","readFileSync","readdirSync"],"sources":["../../src/execution-root.ts","../../src/turn-claim.ts","../../src/session-base.ts","../../src/conversation-transcript.ts","../../src/compaction-orchestrator.ts","../../src/context-window-tracker.ts","../../src/abortable-approval.ts","../../src/auto-mode-gate.ts","../../src/consent-scope.ts","../../src/permission-types.ts","../../src/tool-hook-helpers.ts","../../src/permission-denial-log.ts","../../src/tool-argument-canonicalisation.ts","../../src/tool-permission-wrapper.ts","../../src/workspace-path-resolver.ts","../../src/permission-enforcer.ts","../../src/session-components.ts","../../src/session-history-ops.ts","../../src/session-id.ts","../../src/session-lifecycle.ts","../../src/session-run-options.ts","../../src/session-tool-execution-bridge.ts","../../src/session-run.ts","../../src/session-runtime-tools.ts","../../src/session.ts","../../src/session-record-codec/decode-outcome.ts","../../src/session-record-codec/scalars.ts","../../src/session-record-codec/message-decoders.ts","../../src/session-record-codec/background-task-members.ts","../../src/session-record-codec/background-group-decoders.ts","../../src/session-record-codec/background-task-decoders.ts","../../src/session-record-codec/background-task-event-decoders.ts","../../src/session-record-codec/event-decoders.ts","../../src/session-record-codec/goal-plan-branch-decoders.ts","../../src/session-record-codec/session-loop-decoders.ts","../../src/session-record-codec/tool-schema-decoders.ts","../../src/session-record-codec/record-optional-members.ts","../../src/session-record-codec/record-decoder.ts","../../src/session-artifact.ts","../../src/scrub-sensitive.ts","../../src/session-log-events.ts","../../src/session-log-payload.ts","../../src/session-logger.ts","../../src/session-log-sinks.ts","../../src/session-log-codec/field-decoders.ts","../../src/session-log-codec/payload-shapes.ts","../../src/session-log-codec/index.ts","../../src/external-payload-resolution-contracts.ts","../../src/session-log-sources.ts","../../src/tool-result-spill-store.ts","../../src/prompt-history-file.ts","../../src/external-payload-file-reader.ts","../../src/external-payload-resolver.ts","../../src/session-log-validation.ts","../../src/session-log-replay.ts","../../src/session-store.ts","../../src/checkpoint-tree.ts"],"sourcesContent":["import { isAbsolute } from 'node:path';\n\n/**\n * The session's execution root. ARCH-010.\n *\n * `Session` used to read `process.cwd()` in its constructor, and that ambient value became the\n * session's identity everywhere it matters — every hook input, `CLAUDE_PROJECT_DIR`, the permission\n * enforcer's root, the persisted record. A session could not be TOLD where it ran, so a subagent ran\n * in its parent's directory rather than its own workspace, while the subagent spawn contract had\n * declared `cwd` required all along.\n *\n * Making it a required TypeScript field is necessary and not sufficient. This package's tsconfig\n * excludes `*.test.ts`, and a JavaScript consumer is not type-checked at all — so without a runtime\n * check the field would simply be `undefined`, and the session would report a root it does not have\n * while everything downstream quietly used nothing. Silence is not success: refuse instead.\n */\nexport function requireExecutionRoot(cwd: unknown): string {\n if (typeof cwd !== 'string' || cwd.length === 0) {\n throw new Error(\n 'Session requires `cwd`: the absolute path this session executes in (ARCH-010). It feeds ' +\n 'every hook input, CLAUDE_PROJECT_DIR, the permission root and the persisted record. Pass ' +\n '`process.cwd()` explicitly if that is genuinely what you mean.',\n );\n }\n // ABSOLUTE, not merely present. A relative root is resolved against `process.cwd()` by everything\n // downstream, so accepting one would let the ambient read this change removes back in through the\n // VALUE instead of through its absence — the same defect wearing a different shape.\n if (!isAbsolute(cwd)) {\n throw new Error(\n `Session requires an ABSOLUTE \\`cwd\\`; got ${JSON.stringify(cwd)} (ARCH-010). A relative ` +\n 'root is resolved against the process directory downstream, which is the ambient value this ' +\n 'field exists to replace. Resolve it at your composition root.',\n );\n }\n return cwd;\n}\n","/**\n * Thrown when a turn is started on a session that is already running one. RUNTIME-003.\n *\n * A distinct type rather than a bare `Error`, because the point of giving the session a claim is to\n * let consumers STOP maintaining their own busy flags — and a consumer that has to regex-match an\n * error message to tell \"busy, retry later\" apart from a provider failure has not been given\n * anything it can act on. Follows `CompactionError`, this package's existing precedent.\n */\nexport class SessionBusyError extends Error {\n /** Always `true`: the caller can run this turn later; nothing about the session is broken. */\n readonly recoverable = true;\n\n constructor(message: string) {\n super(message);\n this.name = 'SessionBusyError';\n }\n}\n\n/**\n * The identity of the turn a session is currently running. RUNTIME-003.\n *\n * A session used to express \"is something running?\" as a bare `AbortController | null` field that\n * `run()` overwrote on entry. That field was doing three jobs at once — cancellation channel, busy\n * flag, and turn identity — and it could only do them for ONE turn, so a second concurrent `run()`\n * orphaned the first: `abort()` reached only whichever turn held the field, and the first turn to\n * finish cleared it in its `finally`, making `abort()` on the survivor a silent no-op. `isRunning()`\n * read the same field, so it answered about whichever turn happened to own it. That is why consumers\n * of this library grew their own busy flags rather than trusting it.\n *\n * The fix is to give the unit of work an OWNER. A claim is taken synchronously, it belongs to the\n * caller that took it, and ONLY that caller releases it.\n *\n * REFUSAL, not pre-emption. A session is a single conversation: two turns interleaving on it produce\n * a history neither of them wrote, and silently cancelling the first would discard work the caller\n * never asked to abandon. `claim()` throws {@link SessionBusyError}, whose message names the three\n * ways forward.\n */\nexport class TurnClaim {\n private controller: AbortController | null = null;\n\n /**\n * Take the claim for a new turn, or throw if one is already held.\n *\n * MUST be called before the first `await` in the turn — a check that yields first is not a claim,\n * it is a TOCTOU window, and two callers can pass it in the same tick.\n *\n * @throws {SessionBusyError} if a turn is already running, INCLUDING one that has been aborted but\n * has not finished unwinding. See {@link abort} for why that case is not an exception.\n */\n claim(): AbortController {\n if (this.controller !== null) {\n throw new SessionBusyError(\n 'This session is already running a turn. A session is a single conversation: await the ' +\n 'turn in flight, abort() it and await it, or use a separate session for concurrent work.',\n );\n }\n this.controller = new AbortController();\n return this.controller;\n }\n\n /**\n * Release the claim — but only if `controller` is still the one holding it.\n *\n * The ownership check is what stops the original defect from reappearing in a new shape: a turn\n * that released unconditionally in its `finally` could free a claim a LATER turn already took, and\n * `isRunning()` would then report idle while a turn was in flight.\n */\n release(controller: AbortController): void {\n if (this.controller === controller) {\n this.controller = null;\n }\n }\n\n /**\n * Signal the running turn to stop. Idempotent; a no-op if nothing is running.\n *\n * This does NOT release the claim, and that is deliberate. An earlier version cleared it here, so\n * `isRunning()` answered `false` the instant `abort()` returned — while the aborted turn was still\n * unwinding, still able to write history and finish tool calls. A new `run()` could then claim the\n * session and interleave with it: exactly the two-turns-on-one-session defect RUNTIME-003 is\n * about, just moved behind the abort boundary. Review of the first draft caught it.\n *\n * A turn is not over when it is asked to stop; it is over when it has stopped. The claim is\n * therefore held until the owning turn's `finally` releases it, and until then `isRunning()` says\n * `true` and a further `run()` is refused. Cancel and restart is `abort()`, then AWAIT the turn,\n * then `run()` — which is what every caller in this repo already does.\n */\n abort(): void {\n this.controller?.abort();\n }\n\n isRunning(): boolean {\n return this.controller !== null;\n }\n}\n","import { requireExecutionRoot } from './execution-root.js';\nimport { TurnClaim } from './turn-claim.js';\n\nimport type { ContextWindowTracker, TAutoCompactThreshold } from './context-window-tracker.js';\nimport type { PermissionEnforcer } from './permission-enforcer.js';\nimport type { IPermissionDenial } from './permission-denial-log.js';\nimport type {\n Robota,\n IAIProvider,\n IContextWindowState,\n IHistoryEntry,\n IToolSchema,\n TModelEffort,\n TModelEffortSelection,\n TPermissionMode,\n TToolParameters,\n TUniversalMessage,\n} from '@robota-sdk/agent-core';\n\nexport abstract class SessionBase {\n protected abstract readonly agent: 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 /** ARCH-010: the session's execution root — owned here, with the check that it was supplied. */\n protected readonly cwd: string;\n\n protected constructor(cwd: string) {\n this.cwd = requireExecutionRoot(cwd);\n }\n /**\n * RUNTIME-003: the turn currently running, and its owner. Was a bare `AbortController | null` that\n * `run()` overwrote, which is why `abort()` and `isRunning()` below could answer about a turn that\n * was not the one in flight. See `turn-claim.ts`.\n */\n protected readonly turnClaim = new TurnClaim();\n private readonly permissionModeGuards = new Set<(next: TPermissionMode) => void>();\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 for (const guard of this.permissionModeGuards) guard(mode);\n this.permissionMode = mode;\n }\n\n /** Register a synchronous policy check at the single session-mode mutation boundary. */\n addPermissionModeGuard(guard: (next: TPermissionMode) => void): () => void {\n this.permissionModeGuards.add(guard);\n return () => this.permissionModeGuards.delete(guard);\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 /**\n * The session's execution root (ARCH-010).\n *\n * Readable because a caller that derives something FROM the session — a fork, a subagent, a hook\n * input — must be able to ask which root this session actually runs in. Re-deriving it from\n * `process.cwd()` is how the two silently diverged.\n */\n getCwd(): string {\n return this.cwd;\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.agent.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?: TModelEffortSelection;\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.agent.ensureReady();\n const nextModel = options.model ?? this.model;\n // The system prompt is not model config; it is updated independently via updateSystemMessage.\n this.agent.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 /** Read the selection for the next model call; provider default remains `auto`. */\n getModelEffort(): TModelEffortSelection {\n // Some lightweight session doubles intentionally implement only the execution surface. Keep\n // this read-only projection total for those callers; the real Robota instance exposes getModel.\n const getModel = (\n this.agent as Robota & { getModel?: () => { effort?: TModelEffortSelection } }\n ).getModel;\n if (getModel === undefined) return 'auto';\n try {\n return getModel.call(this.agent).effort ?? 'auto';\n } catch (error) {\n // Preserve the agent's own [LIFECYCLE] error when a destroyed agent is subsequently run.\n if (error instanceof Error && /disposed/i.test(error.message)) return 'auto';\n throw error;\n }\n }\n\n /** Run an operation with a temporary effort override and restore it on every exit path. */\n async withScopedModelEffort<T>(effort: TModelEffort, operation: () => Promise<T>): Promise<T> {\n const previous = this.getModelEffort();\n await this.applyModelOptions({ effort });\n try {\n return await operation();\n } finally {\n await this.applyModelOptions({ effort: previous });\n }\n }\n\n /**\n * Re-apply the agent's identity label to a LIVE session.\n *\n * ARCH-040 (issue #1820): a preset's `agentName` reached the agent only at construction, so\n * starting with a preset set the name while switching to the SAME preset mid-session left the old\n * one — one preset with two answers, decided by when it was chosen.\n *\n * Goes through `updateConfiguration`, the agent's own config seam: the agent's `name` reads THROUGH\n * its config, so writing the config is the whole rename and no copy is left stale.\n */\n async applyAgentName(name: string): Promise<void> {\n await this.agent.updateConfiguration({ name });\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 /**\n * ARCH-040 Group C (issue #1934): re-apply a preset's tool lists to the live enforcer.\n *\n * The BASE it composes onto is the session's configured rules minus whatever a previous preset\n * contributed — which is why the enforcer keeps the original: an allowlist REPLACES the preset\n * layer's contribution rather than accumulating across successive `/preset` switches, while a\n * denial UNIONS because it must not be weakened by a later layer that forgot to repeat it.\n */\n applyPresetToolLists(preset: {\n allowedTools?: readonly string[];\n deniedTools?: readonly string[];\n }): void {\n this.permissionEnforcer.applyPresetToolLists(preset);\n }\n\n /**\n * The rules this session's gate reads right now — settings, preset lists and command auto-allows\n * together — so a subagent inherits what the parent actually enforces (issue #3081). Session-scoped\n * \"allow always\" consent is not included: it was given for this session's context.\n */\n getPermissionRules(): { allow: string[]; deny: string[]; ask: string[] } {\n const rules = this.permissionEnforcer.currentPermissionRules();\n return { allow: [...rules.allow], deny: [...rules.deny], ask: [...rules.ask] };\n }\n\n getSessionAllowedTools(): string[] {\n return this.permissionEnforcer.getSessionAllowedTools();\n }\n\n /**\n * Decide an action that reaches `toolName`'s effect by another route (a command that starts a\n * process), so that route cannot be a way around the tool's own permission: the PreToolUse hooks\n * and guardrails, then the rules, mode, remembered consent and the prompt. The command sandbox's\n * auto-approval never applies, because the action does not run inside that sandbox.\n */\n checkToolPermission(\n toolName: string,\n toolParameters: TToolParameters,\n signal?: AbortSignal,\n ): Promise<boolean> {\n return this.permissionEnforcer.checkDelegatedToolCall(toolName, toolParameters, signal);\n }\n\n /** `auto` mode hands decisions to a classifier, so a session without one cannot enter it. */\n protected requireClassifierFor(mode: TPermissionMode): void {\n if (mode === 'auto' && !this.permissionEnforcer.hasPermissionClassifier()) {\n throw new Error('Auto mode is unavailable: this session has no permission classifier.');\n }\n }\n\n /**\n * Let the call behind a classifier denial (by its index in the recent denials) run once when the\n * model tries it again. Returns the denial, or `undefined` when the index names no classifier\n * denial.\n */\n retryPermissionDenial(index: number): IPermissionDenial | undefined {\n return this.permissionEnforcer.allowRetryOfDenial(index);\n }\n\n /** The calls this session refused, most recent first (issue #3082). */\n getRecentPermissionDenials(): readonly IPermissionDenial[] {\n return this.permissionEnforcer.getRecentDenials();\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 this.turnClaim.abort();\n }\n\n isRunning(): boolean {\n return this.turnClaim.isRunning();\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.agent.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.agent.getHistory();\n }\n\n getFullHistory(): IHistoryEntry[] {\n return this.agent.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 /**\n * The tool schemas the model is offered at the next request (CLI-1990).\n *\n * The offered set, not the registered one: a deferred tool that has not been loaded is absent,\n * because it is absent from the request. `/context` reads this to report what the tool schemas\n * actually cost, which is the only surface that makes deferral's saving observable.\n */\n getOfferedToolSchemas(): IToolSchema[] {\n return this.agent.getOfferedToolSchemas();\n }\n\n /** The provider the session sends its turns to now; a provider switch replaces it. */\n getProvider(): IAIProvider {\n return this.aiProvider;\n }\n\n getProviderId(): string {\n return this.aiProvider.name;\n }\n\n /** Add an event entry to history (not a chat message) */\n addHistoryEntry(entry: IHistoryEntry): void {\n this.agent.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.agent.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.agent.injectRawMessage(msg);\n }\n\n clearHistory(): void {\n this.agent.clearHistory();\n this.contextTracker.reset();\n }\n}\n","/**\n * The one text rendering of a conversation that is handed to a model as prompt input.\n *\n * Compaction summarises it and the advisor reads it. Both need the same things kept: which tool the\n * model called with which arguments, what came back, and who wrote each user message — a message a\n * peer session sent is not the operator's own words and must not read as if it were.\n *\n * Every message is exactly one line: the label is written here and the content is JSON-encoded, so\n * no newline inside a message, a tool result or a peer's text can start a line of its own. That is\n * what keeps a peer from writing an unmarked `user:` line or a tool result from closing a block the\n * reader was told to trust.\n */\n\nimport { peerDriverOf, printablePeerDriver } from '@robota-sdk/agent-core';\n\nimport type { TUniversalMessage } from '@robota-sdk/agent-core';\n\nfunction encode(content: unknown): string {\n return JSON.stringify(typeof content === 'string' ? content : (content ?? ''));\n}\n\n/**\n * The same peer attribution the model request carries (agent-core `peerDriverOf`), and the same\n * printable form of the id, which arrives from the sending side.\n */\nfunction userLabel(message: TUniversalMessage): string {\n const peer = peerDriverOf(message);\n return peer ? `user [from ${JSON.stringify(printablePeerDriver(peer))}]` : 'user';\n}\n\nfunction formatMessage(message: TUniversalMessage): string[] {\n switch (message.role) {\n case 'user':\n return [`${userLabel(message)}: ${encode(message.content)}`];\n case 'assistant': {\n const lines: string[] = [];\n if (message.content !== null && message.content !== '') {\n lines.push(`assistant: ${encode(message.content)}`);\n }\n for (const call of message.toolCalls ?? []) {\n lines.push(\n `assistant tool call ${JSON.stringify(call.function.name)} [${JSON.stringify(call.id)}]: ${encode(call.function.arguments)}`,\n );\n }\n return lines.length > 0 ? lines : [`assistant: \"\"`];\n }\n case 'tool':\n return [\n `tool result${message.name ? ` ${JSON.stringify(message.name)}` : ''} [${JSON.stringify(message.toolCallId)}]: ${encode(message.content)}`,\n ];\n case 'system':\n return [`system: ${encode(message.content)}`];\n }\n}\n\n/**\n * Render each message as one entry, in order. An entry is one line, except an assistant message\n * with several tool calls, which is one line per call. One entry per message so a caller that must\n * drop the oldest part of a long conversation can drop whole messages.\n */\nexport function formatConversationEntries(history: readonly TUniversalMessage[]): string[] {\n return history.map((message) => formatMessage(message).join('\\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 { formatConversationEntries } from './conversation-transcript.js';\n\nimport type { TCompactTrigger } from './session-types.js';\nimport type {\n IAIProvider,\n TUniversalMessage,\n THooksConfig,\n IHookInput,\n IHookTypeExecutor,\n ISubprocessTraceEnv,\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\n/**\n * Default base template for the compaction summarization prompt — the one model-facing\n * prompt surface this package owns (declared in SPEC § Boundaries). Intentionally\n * domain-neutral: it must not assume a software-development conversation. Replaceable\n * wholesale via {@link ICompactionOptions.basePrompt}.\n */\nexport const DEFAULT_COMPACTION_PROMPT = [\n 'Summarize the following conversation concisely, preserving:',\n \"- User's original requests and goals\",\n '- Key decisions, conclusions, and important state',\n '- Identifiers, names, and references needed to continue the work',\n '- Current task status and next steps',\n \"Drop verbose intermediate outputs and exploratory work that didn't lead to results.\",\n].join('\\n');\n\nexport interface ICompactionOptions {\n sessionId: string;\n cwd: string;\n model: string;\n hooks?: Record<string, unknown>;\n compactInstructions?: string;\n /**\n * Replaces the entire base instruction template of the compaction prompt\n * (default: {@link DEFAULT_COMPACTION_PROMPT}). Focus instructions and the\n * formatted conversation are appended after it.\n */\n basePrompt?: 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 basePrompt?: 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.basePrompt = options.basePrompt;\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 - The messages to summarise. Must not be empty: whether there is anything worth\n * compacting is the caller's judgement, made before it commits to replacing the conversation\n * (CORE-031).\n * @param instructions - Optional focus instructions for the summary\n * @param signal - The turn's cancellation signal (RUNTIME-004). Checked before the provider call\n * and again after it: an abort throws rather than returning, so the caller's existing\n * leave-history-untouched path covers a cancel as well as a failure.\n * @param hookTraceEnv - The prompt's trace for PreCompact's command hooks, only inside a prompt\n * @returns The generated summary string (always a non-empty string)\n * @throws {CompactionError} when `history` is empty, or when the provider returns a non-string or\n * empty summary — callers must leave the conversation history untouched in every such case\n */\n async compact(\n provider: IAIProvider,\n history: TUniversalMessage[],\n instructions?: string,\n signal?: AbortSignal,\n trigger: TCompactTrigger = 'manual',\n hookTraceEnv?: ISubprocessTraceEnv,\n ): Promise<string> {\n // RUNTIME-004: FIRST, before the emptiness check. Review found that ordering the other way\n // returned a summary for an already-cancelled turn — and the caller replaces the conversation\n // with whatever this returns, so a cancel could still clear it and inject an empty summary.\n signal?.throwIfAborted();\n // CORE-031: this used to `return ''`, contradicting the contract two lines of docblock above it\n // (\"always a non-empty string\") — and the caller wrote that empty string over the conversation as\n // a summary. Deciding that an empty conversation is a no-op is the CALLER's judgement, made\n // before it commits to replacing anything; by the time execution is here, the caller has already\n // decided there is something to summarise, so an empty history means that decision was wrong.\n if (history.length === 0) {\n throw new CompactionError(\n 'Compaction was asked to summarise an empty history; conversation history preserved untouched',\n );\n }\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 hookTraceEnv,\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 {\n model: this.model,\n toolChoice: 'none',\n // The history was sized to this model's window; a smaller one could not read it all.\n preserveContextWindow: true,\n ...(signal !== undefined ? { signal } : {}),\n },\n );\n // RUNTIME-004: the caller REPLACES the whole conversation with what this returns, so returning a\n // summary after a cancel is what destroyed it. Throwing puts an abort on the same path CORE-019\n // already built for an invalid summary — history left untouched.\n signal?.throwIfAborted();\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 = formatConversationEntries(history).join('\\n');\n\n return [\n this.basePrompt ?? DEFAULT_COMPACTION_PROMPT,\n instructionSection,\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","import type { TPermissionResult } from './permission-types.js';\nimport type { ITerminalOutput, TToolArgs } from '@robota-sdk/agent-core';\n\n/**\n * Wait for an approval, or for the turn to be cancelled — whichever happens first.\n *\n * RUNTIME-005: a human-approval prompt is a wait with no natural end. `abort()` reached the provider\n * and the tool-start check but not this, so a turn parked here ran until somebody answered, and since\n * RUNTIME-003 holds the session's claim until the turn unwinds, the session stayed busy with no way\n * for the caller to clear it short of discarding it.\n *\n * A cancelled approval resolves to `false`, NOT to a rejection and never to `true`. Two reasons, and\n * the first is the load-bearing one:\n *\n * - **Fail closed.** If cancelling read as approval, aborting a turn would become a way to run an\n * unapproved tool. Denial is the same answer the enforcer already gives when no approver is\n * attached.\n * - The caller (`permission-enforcer`'s tool wrapper) must never throw — a throw there records an\n * assistant `tool_use` with no matching `tool_result` and corrupts the conversation.\n *\n * The listener is removed on every path, so an approval that arrives after the abort does not keep a\n * handler alive on a long-lived signal.\n */\nasync function raceAbort(\n approval: Promise<TPermissionResult>,\n signal?: AbortSignal,\n): Promise<TPermissionResult> {\n if (signal === undefined) return approval;\n if (signal.aborted) return false;\n\n let onAbort: (() => void) | undefined;\n try {\n return await Promise.race([\n approval,\n new Promise<TPermissionResult>((resolve) => {\n onAbort = (): void => resolve(false);\n signal.addEventListener('abort', onAbort, { once: true });\n }),\n ]);\n } finally {\n if (onAbort !== undefined) signal.removeEventListener('abort', onAbort);\n }\n}\n\n/**\n * What an approval RESULT means, in one place.\n *\n * The two prompt paths — a consumer `permissionHandler` and an injected `promptForApprovalFn` —\n * interpreted `allow-session` / `allow-project` identically, in two copies. Two readings of \"does\n * this answer grant permission\" that can drift is the shape this repository keeps removing; and the\n * caller now only has to know WHICH prompt to run, not what its answer implies.\n *\n * Side effects are returned rather than performed, so this stays a pure decision the enforcer applies.\n */\ninterface IApprovalOutcome {\n allowed: boolean;\n rememberForSession: boolean;\n rememberForProject: boolean;\n}\n\nfunction interpretApproval(result: TPermissionResult): IApprovalOutcome {\n if (result === 'allow-session') {\n return { allowed: true, rememberForSession: true, rememberForProject: false };\n }\n if (result === 'allow-project') {\n return { allowed: true, rememberForSession: true, rememberForProject: true };\n }\n return { allowed: result === true, rememberForSession: false, rememberForProject: false };\n}\n\n/**\n * The whole human-approval path: allow-list, cancellation, prompt, and what the answer means.\n *\n * Moved out of the enforcer when RUNTIME-005 pushed it past its size ceiling, and the seam is real\n * rather than convenient — this decides whether a human said yes, while the enforcer decides whether\n * a human is asked at all. Side effects come back as flags so the enforcer keeps ownership of its own\n * allow lists.\n *\n * A human-approval prompt is a wait with NO NATURAL END, which is why the signal matters here more\n * than anywhere else in the permission path: without it an aborted turn parked on a prompt ran until\n * somebody answered, and the session stayed claimed. Cancelling DENIES — a cancelled approval must\n * never read as approval, or aborting a turn becomes a way to run an unapproved tool.\n */\nexport interface IApprovalRequest {\n toolName: string;\n toolArgs: TToolArgs;\n alreadyAllowed: boolean;\n /** A consumer-supplied handler. Takes precedence over `injectedPrompt`, as it always did. */\n handler?: (toolName: string, toolArgs: TToolArgs) => Promise<TPermissionResult>;\n injectedPrompt?: (\n terminal: ITerminalOutput,\n toolName: string,\n toolArgs: TToolArgs,\n ) => Promise<TPermissionResult>;\n terminal?: ITerminalOutput;\n signal?: AbortSignal;\n}\n\nexport async function decideApproval(request: IApprovalRequest): Promise<IApprovalOutcome> {\n const denied: IApprovalOutcome = {\n allowed: false,\n rememberForSession: false,\n rememberForProject: false,\n };\n if (request.alreadyAllowed) {\n return { allowed: true, rememberForSession: false, rememberForProject: false };\n }\n // RUNTIME-005: a turn already cancelled asks nobody. Prompting here would put a question on screen\n // for work that is not going to happen.\n if (request.signal?.aborted === true) return denied;\n // Which prompt to run is this module's business too: the enforcer supplies the approvers it has\n // and does not decide between them.\n const prompt = request.handler\n ? (): Promise<TPermissionResult> => request.handler!(request.toolName, request.toolArgs)\n : request.injectedPrompt && request.terminal\n ? (): Promise<TPermissionResult> =>\n request.injectedPrompt!(request.terminal!, request.toolName, request.toolArgs)\n : undefined;\n // No approval mechanism available — deny by default.\n if (prompt === undefined) return denied;\n return interpretApproval(await raceAbort(prompt(), request.signal));\n}\n","/**\n * `auto` permission mode: a model classifier decides the calls the mode itself does not (issue\n * #3082).\n *\n * The permission gate still runs first — deny rules, the ceiling, and the reads and workspace edits\n * the mode approves. What it would have asked a person about goes to the classifier instead, unless\n * the user's own `ask` rule names it. A block returns its reason to the model so it can take another\n * route. When the classifier keeps refusing, the mode pauses and asks a person, because a model\n * that is blocked over and over is either stuck or being steered, and neither is for the classifier\n * to resolve.\n */\n\nimport type { TToolArgs } from '@robota-sdk/agent-core';\n\n/** The call the classifier judges. */\nexport interface IClassifiedCall {\n readonly toolName: string;\n readonly toolArgs: TToolArgs;\n readonly cwd: string;\n}\n\nexport interface IClassifierVerdict {\n readonly decision: 'allow' | 'block';\n /** Short, for the model and the user: which rule, and why. */\n readonly reason: string;\n}\n\n/**\n * Judges one call. `undefined` means no usable verdict (an error, a refusal, output that does not\n * parse): the call is not run. It counts toward a run of refusals, so a classifier that cannot\n * answer hands the decision to a person instead of refusing every call.\n */\nexport interface IPermissionClassifier {\n classify(call: IClassifiedCall, signal?: AbortSignal): Promise<IClassifierVerdict | undefined>;\n}\n\n/** Refusals in a row (blocks or unusable verdicts), and blocks in the session, that pause the mode. */\nexport const CONSECUTIVE_BLOCK_LIMIT = 3;\nexport const TOTAL_BLOCK_LIMIT = 20;\n\n/** `reason` is for the user (`/permissions`); `message` is what the model is told. */\nexport type TAutoModeJudgement =\n | { readonly kind: 'allow' }\n | { readonly kind: 'block'; readonly reason: string; readonly message: string }\n | { readonly kind: 'unusable'; readonly reason: string; readonly message: string };\n\nfunction callKey(toolName: string, toolArgs: TToolArgs): string {\n return `${toolName}\\u0000${JSON.stringify(toolArgs)}`;\n}\n\nexport class AutoModeGate {\n private consecutive = 0;\n private total = 0;\n private paused = false;\n /** Calls a person allowed to be retried once after the classifier blocked them. */\n private readonly retries = new Set<string>();\n\n constructor(private readonly classifier: IPermissionClassifier) {}\n\n /** The mode asks a person until one approves. */\n isPaused(): boolean {\n return this.paused;\n }\n\n /** A person approved while paused: the classifier decides again. */\n resume(): void {\n this.paused = false;\n this.consecutive = 0;\n }\n\n /** Let this exact call through once, without the classifier. */\n grantRetry(toolName: string, toolArgs: TToolArgs): void {\n this.retries.add(callKey(toolName, toolArgs));\n }\n\n /** Consume a retry grant for this exact call, if there is one. */\n takeRetry(toolName: string, toolArgs: TToolArgs): boolean {\n return this.retries.delete(callKey(toolName, toolArgs));\n }\n\n async judge(call: IClassifiedCall, signal?: AbortSignal): Promise<TAutoModeJudgement> {\n let verdict: IClassifierVerdict | undefined;\n try {\n verdict = await this.classifier.classify(call, signal);\n } catch {\n // allow-fallback: an unusable verdict is a denial that is reported, not a crash\n verdict = undefined;\n }\n if (verdict === undefined) {\n // A turn the user cancelled is not the classifier failing to answer.\n if (signal?.aborted !== true) this.consecutive += 1;\n const pause = this.consecutive >= CONSECUTIVE_BLOCK_LIMIT;\n if (pause) this.paused = true;\n return {\n kind: 'unusable',\n reason: 'no usable verdict',\n message:\n 'The auto-mode classifier gave no usable verdict, so the call was not run. Try again, ' +\n 'or ask the user to approve it.' +\n (pause ? ' Auto mode is paused: the next calls ask the user.' : ''),\n };\n }\n if (verdict.decision === 'allow') {\n this.consecutive = 0;\n return { kind: 'allow' };\n }\n this.consecutive += 1;\n this.total += 1;\n const pause = this.consecutive >= CONSECUTIVE_BLOCK_LIMIT || this.total >= TOTAL_BLOCK_LIMIT;\n if (this.total >= TOTAL_BLOCK_LIMIT) this.total = 0;\n if (pause) this.paused = true;\n return {\n kind: 'block',\n reason: verdict.reason,\n message:\n `Blocked by the auto-mode classifier: ${verdict.reason}. Do not retry the same action; ` +\n 'take another approach or ask the user.' +\n (pause ? ' Auto mode is paused after repeated blocks: the next calls ask the user.' : ''),\n };\n }\n}\n","/**\n * What \"don't ask again\" GRANTS, as a permission pattern (issue #2351).\n *\n * Session and project consent used to be keyed on the tool NAME: approving `Bash` for `git status`\n * allowed every later `Bash`, approving `WebFetch` for one benign URL allowed every host, and the\n * project-level record (`Tool(*)`) outlived the session. The user was shown one argument and\n * granted all of them, and the prompt never said so.\n *\n * The consent record is now a pattern in the gate's own grammar, projected from the argument by the\n * kind the tool's permission profile declares (CORE-049), so the record, the prompt and the\n * `permissions.allow` rules all speak one language and are matched by one matcher:\n *\n * - `path` → the containing directory: `Read(/w/src/**)` — one approval covers a tree, not a file\n * and not the filesystem;\n * - `url` → the origin: `WebFetch(https://example.com/**)` — a host, not every host;\n * - `command` → the program: `Bash(git *)` — `argv[0]`, the projection the issue names;\n * - `text`, or no declared argument → the tool name: a search query or a glob pattern is not a\n * blast radius, and a tool that declares no argument cannot be narrowed at all.\n *\n * Exact-argument consent would prompt constantly; unbounded consent is what existed. A projection is\n * the design work, and it is stated here once so both prompt surfaces can print it verbatim.\n */\n\nimport { getToolPermissionProfile } from '@robota-sdk/agent-core';\n\nimport type { TToolArgs } from '@robota-sdk/agent-core';\n\n/** Lexical directory of a path, separators normalised; `/` → `/`, `a` → `.`. */\nfunction directoryOf(path: string): string {\n const slashed = path.replace(/\\\\/g, '/');\n const cut = slashed.lastIndexOf('/');\n if (cut < 0) return '.';\n if (cut === 0) return '/';\n return slashed.slice(0, cut);\n}\n\n/** The pattern text (inside the parentheses) consent covers, or undefined for name-only consent. */\nfunction scopeArgument(kind: string, value: string): string | undefined {\n switch (kind) {\n case 'path': {\n const dir = directoryOf(value);\n return dir === '/' ? '/**' : `${dir}/**`;\n }\n case 'url': {\n try {\n const url = new URL(value);\n return `${url.protocol}//${url.host}/**`;\n } catch {\n // allow-fallback: a URL the platform parser refuses is consented to EXACTLY as written —\n // the strict direction — never widened to the tool name.\n return value;\n }\n }\n case 'command': {\n const argv0 = value.trim().split(/\\s+/)[0];\n return argv0 ? `${argv0} *` : undefined;\n }\n default:\n return undefined;\n }\n}\n\n/** The permission pattern a \"don't ask again\" answer for this invocation grants. */\nexport function consentScopeFor(toolName: string, toolArgs: TToolArgs): string {\n const argument = getToolPermissionProfile(toolName).argument;\n if (argument === undefined) return toolName;\n const value = toolArgs[argument.key];\n if (typeof value !== 'string' || value === '') return toolName;\n const scoped = scopeArgument(argument.kind, value);\n return scoped === undefined ? toolName : `${toolName}(${scoped})`;\n}\n","/**\n * Permission types — interfaces and type aliases for permission enforcement.\n */\n\nimport type { IPermissionClassifier } from './auto-mode-gate.js';\nimport type { ISessionLogger } from './session-logger.js';\nimport type { TPermissionMode, TToolArgs } from '@robota-sdk/agent-core';\nimport type {\n IHookTypeExecutor,\n ISpinner,\n ITerminalOutput,\n IToolResult,\n TBackgroundPermissionPolicy,\n} from '@robota-sdk/agent-core';\nimport type { TPermissionResultValue } from '@robota-sdk/agent-interface-session';\n\nexport type { ISpinner, ITerminalOutput };\n\n/** The part of a sandbox client the permission gate consults. */\nexport interface ICommandSandboxApproval {\n /**\n * Whether `toolName` runs `shellCommand` inside the sandbox and the sandbox's settings let it\n * proceed without a prompt. Only a tool the sandbox actually wraps may answer yes.\n */\n autoApproves(toolName: string, shellCommand: string): boolean;\n}\n\n/**\n * Permission handler result (issue #2052: the union is OWNED by `agent-interface-session` as\n * `TPermissionResultValue`; this name is the session-layer alias, not a second declaration):\n * - true: allow this invocation\n * - false: deny this invocation\n * - 'allow-session': allow this invocation and auto-approve the CONSENT SCOPE — the pattern\n * `consentScopeFor` projects from this invocation's argument (issue #2351), e.g. `Bash(git *)` —\n * for the rest of the session\n * - 'allow-project': allow this invocation and persist that same scope pattern to the project's\n * local settings; the storage location is owned by the consuming layer (via `onProjectAllowTool`)\n */\nexport type TPermissionResult = TPermissionResultValue;\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 /** `ask` patterns always ask, in every mode including bypassPermissions (issue #3081). */\n permissions: { allow: string[]; deny: string[]; ask?: string[] };\n hooks?: Record<string, unknown>;\n };\n /**\n * The OS sandbox the shell tools run under, when there is one: whether it confines a command and\n * lets it run without a prompt.\n */\n commandSandbox?: ICommandSandboxApproval;\n /** Where `~` and `$HOME` point for critical-path removal checks. Defaults to the OS home directory. */\n homeDirectory?: string;\n /**\n * ARCH-040 Group C (issue #1934): the rules BEFORE any preset contributed.\n *\n * Supplied by the composition root, never derived here. `config.permissions` already carries the\n * STARTUP preset's patterns, so capturing a base from it on the first live `/preset` would keep\n * the first preset's allowlist through every later switch — the accumulation the replace rule\n * exists to prevent, arriving through the base rather than through the merge. Absent ⇒ no preset\n * contributed, and `config.permissions` is itself the preset-free base.\n */\n presetFreePermissions?: { allow: readonly string[]; deny: readonly string[] };\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 * CORE-025: a background/subagent task permission policy. It adds a ceiling (checked before bypass),\n * an ask-everything flag and the task's own lists to the one evaluator, so `deny`/`preapproved`/\n * `inherit-allowlist` still bind under a permissive mode. Absent → no policy constraints.\n */\n permissionPolicy?: TBackgroundPermissionPolicy;\n /**\n * CORE-025: the task's OWN declared allow/deny rules (distinct from the parent session's `config.permissions`\n * which `inherit-allowlist` inherits). `preapproved` consults these.\n */\n taskPermissions?: { allow?: readonly string[]; deny?: readonly string[] };\n /**\n * Judges, in `auto` mode, the calls the mode would otherwise ask a person about. Absent → the\n * session cannot enter `auto`.\n */\n permissionClassifier?: IPermissionClassifier;\n}\n\n/**\n * How a tool call ended, when it did not simply succeed (CORE-027).\n *\n * Three outcomes used to be indistinguishable from success at this type — a tool that THREW, a user\n * DENIAL, and a hook BLOCK — because each was returned as `{ success: true, data: '{\"success\":\n * false,…}' }`. Every consumer above had to parse English out of a string and guess, and all three\n * guesses were the same one.\n *\n * The framing the audit stated is the one kept here: **\"never throw\" is correct and \"encode the\n * failure as success\" is not — they are independent decisions.** Nothing below starts throwing.\n */\nexport type TToolFailureOutcome = 'threw' | 'denied' | 'hook-blocked';\n\n/**\n * The failure envelope. `success: false` is the part consumers branch on; `outcome` is the part they\n * branch on when they need to know WHICH failure, and neither requires reading `data`.\n *\n * `data` keeps the JSON string it always carried, because the model is shown that text and changing\n * what it sees is a separate decision from making the envelope honest.\n */\nexport interface IToolFailureResult extends IToolResult {\n success: false;\n outcome: TToolFailureOutcome;\n error: string;\n data: string;\n metadata: Record<string, never>;\n}\n\nexport function toolFailure(\n outcome: TToolFailureOutcome,\n error: string,\n /**\n * The payload, kept in the shape callers already parse.\n *\n * A CORRECTION, because the first version of this comment claimed something false and review\n * caught it. It said the model still sees `{ blocked: true, reason }` because `data` was\n * unchanged. It does not. `success: false` now reaches `ToolManager.executeTool`, which throws\n * `ToolExecutionError`; `ToolExecutionService` catches that and returns `{ success: false, error }`,\n * and the history writer renders a failed result as `Error: <message>`. So what the model reads\n * changed from the JSON payload to one error line.\n *\n * That change is INTENDED and is the point of the item: a blocked call is a failure, and the model\n * being told `Error: Blocked by hook — <reason>` is more honest than being handed a success-shaped\n * envelope it has to introspect. What was wrong was the claim that nothing changed, not the change.\n * The reason travels in `error`, so nothing is lost.\n */\n data?: unknown,\n): IToolFailureResult {\n return {\n success: false as const,\n outcome,\n error,\n data: JSON.stringify(data ?? { success: false, output: '', error }),\n metadata: {},\n };\n}\n\n/**\n * What a crash announcement looks like, named rather than widened.\n *\n * The first version typed `announce` as taking a `Record<string, unknown>`, which the real\n * `onToolExecution` cannot be assigned to — parameter positions are contravariant — so the call site\n * reached for `as never`. A cast at a boundary is the boundary's type being wrong; this is the\n * subset of the event this function actually emits.\n */\nexport interface IToolCrashAnnouncement {\n type: 'end';\n toolName: string;\n toolArgs: TToolArgs;\n success: false;\n executionId?: string;\n}\n\n/**\n * The crash path, as one call: announce the failure to the listener and return an honest envelope.\n *\n * It lives beside the envelope rather than in the enforcer because the enforcer is at its size\n * ceiling and this is the same subject — what a failed tool call looks like. Two things happen here\n * and both are the point: before CORE-027 the catch returned `success: true` AND emitted no end\n * event at all, so a crash was invisible to the caller and to anything watching.\n */\nexport function reportToolCrash(\n error: unknown,\n announce: ((event: IToolCrashAnnouncement) => void) | undefined,\n where: { toolName: string; toolArgs: TToolArgs; executionId?: string },\n): IToolFailureResult {\n const message = error instanceof Error ? error.message : String(error);\n announce?.({\n type: 'end',\n toolName: where.toolName,\n toolArgs: where.toolArgs,\n success: false,\n executionId: where.executionId,\n });\n return toolFailure('threw', message);\n}\n\n/** A refusal that tells the model why, so it can take another route. */\nexport interface IPermissionRefusal {\n readonly message: string;\n}\n\n/** Returned when the user denies a permission prompt. */\nexport const PERMISSION_DENIED_RESULT = toolFailure(\n 'denied',\n 'Permission denied. The user did not approve this action.',\n);\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, isEnforcing, wasToolResultAdmitted } from '@robota-sdk/agent-core';\n\nimport { MAX_TOOL_OUTPUT_CHARS, toolFailure } from './permission-types.js';\n\nimport type {\n IToolResult,\n TToolParameters,\n THooksConfig,\n IHookInput,\n IHookTypeExecutor,\n ISubprocessTraceEnv,\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 (wasToolResultAdmitted(result)) return result;\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/**\n * Run PreToolUse hooks; returns a denial IToolResult if blocked, or null to proceed. `hookTraceEnv`\n * names the prompt root, never the tool body's span.\n */\nexport async function runPreToolHook(\n hooks: Record<string, unknown> | undefined,\n hookInput: IHookInput,\n hookTypeExecutors: IHookTypeExecutor[] | undefined,\n hookTraceEnv?: ISubprocessTraceEnv,\n): Promise<IToolResult | null> {\n const hookResult = await runHooks(\n hooks as THooksConfig | undefined,\n 'PreToolUse',\n hookInput,\n hookTypeExecutors,\n hookTraceEnv,\n );\n if (hookResult.blocked) {\n // CORE-027, the third of the three outcomes the failure type names. This path was left behind by\n // the first pass: `permission-types.ts` declared `hook-blocked` while this still returned\n // `success: true`, so the type promised a distinction the code did not make — in the file that\n // exists to end exactly that.\n const reason = hookResult.reason ?? 'Blocked by hook';\n return toolFailure('hook-blocked', reason, { blocked: true, reason });\n }\n\n // SEC-016. A hook that reached NO verdict is not a hook that approved. Issue #2083 made that\n // distinction representable; this is where it starts costing something.\n //\n // Guarded by the policy rather than by a literal `true`, so the posture is stated in ONE place and\n // this boundary cannot drift from it. Note what this does NOT buy: the event is hardcoded here\n // because this function is `runPreToolHook`, so a future enforcing event needs its own boundary\n // and does not inherit anything — an earlier version of this comment claimed otherwise and review\n // caught it. What the indirection does buy is that flipping `PreToolUse` to advisory in the table\n // turns this gate off.\n //\n // The `errors` / `unknownHookTypes` gate is covered by\n // `__tests__/tool-hook-helpers.test.ts`, which fails if this handling is removed.\n //\n // The check stays HERE rather than inside `runHooks`, because the runner reports outcomes and must\n // not decide policy — the same split issue #2083 established between the decoder and the runner.\n if (isEnforcing('PreToolUse')) {\n // Bind the array, not `errors?.[0]`: narrowing the element does not narrow the collection, and\n // the count below needs the collection. The earlier shape needed a `?? 1` fallback that could\n // never be taken, which reads as though the array might be absent here.\n const failures = hookResult.errors;\n const failure = failures?.[0];\n // ONE binding for one field. Two bindings, each defended differently against `undefined`, is a\n // drift surface: the two branches below disagreed about whether the field could be absent.\n const unregistered = hookResult.unknownHookTypes ?? [];\n\n // A configured hook type with no registered executor ran NOTHING. Before SEC-016 the runner\n // reported it and the gate proceeded, so a config declaring a guardrail with no registry\n // silently disabled itself. Startup rejection of such a config is issue #2099; this is the\n // runtime half.\n //\n // Denying is deliberate and is the approved SEC-016 semantics: a PreToolUse hook the user wrote\n // as a gate must not be silently skipped. But note WHICH configs land here, because it is wider\n // than a mistake — `prompt`, `agent` and `guardrail` are accepted by the config schema while no\n // product surface supplies the `providerFactory` / `sessionFactory` / `guardrails` those\n // executors need, so such a config validates and can never run. That gap is issue #2245; it is\n // the reason this text says what to DO rather than only what happened.\n //\n // Built once and used by both branches. The earlier shape wrote the cause twice — a full\n // sentence in the standalone branch and a shorter one appended to the error branch — so the\n // operator with TWO faults got less guidance than the one with a single fault, which is exactly\n // backwards, and the two wordings were free to drift apart.\n const unregisteredReason =\n unregistered.length > 0\n ? `Hook type(s) with no registered executor: ${unregistered.join(', ')}. ` +\n 'Nothing evaluated this gate, so the tool call is denied rather than silently allowed. ' +\n 'Remove the hook from the PreToolUse configuration, or supply an executor for its type.'\n : '';\n\n if (failures !== undefined && failure !== undefined) {\n // The reason names the kind, the executor and the failure text, because a fail-closed gate\n // turns a misconfigured hook into a hard stop: whoever hits it needs enough to fix it.\n const others = failures.length - 1;\n const reason =\n `Hook could not evaluate (${failure.kind}, source: ${failure.source}): ${failure.reason}.` +\n // Naming only the first would hide that several gates failed; the count is the cheap half of\n // that, and the reason line stays one line.\n (others > 0 ? ` (+${others} more hook failure(s))` : '') +\n // Both causes in ONE reason. This branch returns before the unregistered branch, so a turn\n // carrying both used to report only the error — the operator fixed the named cause, retried,\n // and hit a second denial with no warning it was queued. A fail-closed gate that reveals its\n // reasons one per attempt is a gate you debug by being repeatedly stopped.\n (unregisteredReason !== '' ? ` Also unevaluated — ${unregisteredReason}` : '');\n return toolFailure('hook-blocked', reason, { blocked: true, reason });\n }\n\n if (unregisteredReason !== '') {\n return toolFailure('hook-blocked', unregisteredReason, {\n blocked: true,\n reason: unregisteredReason,\n });\n }\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 hookTraceEnv?: ISubprocessTraceEnv,\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 hookTraceEnv,\n ).catch((error) => logger.warn('hook failed', { error }));\n}\n","/**\n * The calls this session refused, most recent first, so `/permissions` can show what was blocked\n * and why without the user scrolling back through the transcript (issue #3082).\n *\n * In memory and bounded: it answers \"what did I just block\", not an audit trail — the\n * `PermissionDecision` hook and the session log are the durable records.\n */\n\nimport { getToolPermissionProfile } from '@robota-sdk/agent-core';\n\nimport type { TToolArgs } from '@robota-sdk/agent-core';\n\n/**\n * Why a call was refused:\n * - `policy` — the gate answered deny: a deny rule, a background ceiling, or plan mode;\n * - `user` — a person was asked and declined, or the turn was cancelled while asking;\n * - `no-approver` — the call needed a person and none was attached;\n * - `classifier` — in `auto` mode, the classifier blocked the call or gave no usable verdict.\n */\nexport type TPermissionDenialReason = 'policy' | 'user' | 'no-approver' | 'classifier';\n\nexport interface IPermissionDenial {\n readonly toolName: string;\n /** The argument the tool's permission profile names (command, path, URL), when it has one. */\n readonly argument?: string;\n readonly reason: TPermissionDenialReason;\n /** The classifier's reason, for a `classifier` denial. */\n readonly detail?: string;\n /** Epoch milliseconds. */\n readonly at: number;\n}\n\nconst DEFAULT_CAPACITY = 20;\nconst ARGUMENT_DISPLAY_LIMIT = 200;\n\nfunction argumentOf(toolName: string, toolArgs: TToolArgs): string | undefined {\n const key = getToolPermissionProfile(toolName).argument?.key;\n if (key === undefined) return undefined;\n const value = toolArgs[key];\n if (typeof value !== 'string') return undefined;\n return value.length > ARGUMENT_DISPLAY_LIMIT\n ? `${value.slice(0, ARGUMENT_DISPLAY_LIMIT)}…`\n : value;\n}\n\nexport class PermissionDenialLog {\n private readonly entries: IPermissionDenial[] = [];\n /** The full call behind each entry, same order, so a denial can be retried exactly. */\n private readonly calls: { toolName: string; toolArgs: TToolArgs }[] = [];\n\n constructor(\n private readonly capacity: number = DEFAULT_CAPACITY,\n private readonly now: () => number = Date.now,\n ) {}\n\n record(\n toolName: string,\n toolArgs: TToolArgs,\n reason: TPermissionDenialReason,\n detail?: string,\n ): void {\n const argument = argumentOf(toolName, toolArgs);\n this.entries.unshift({\n toolName,\n ...(argument !== undefined ? { argument } : {}),\n reason,\n ...(detail !== undefined ? { detail } : {}),\n at: this.now(),\n });\n this.calls.unshift({ toolName, toolArgs });\n if (this.calls.length > this.capacity) this.calls.length = this.capacity;\n if (this.entries.length > this.capacity) this.entries.length = this.capacity;\n }\n\n list(): readonly IPermissionDenial[] {\n return [...this.entries];\n }\n\n /** The call behind the entry at `index` in {@link list}. */\n callAt(index: number): { toolName: string; toolArgs: TToolArgs } | undefined {\n return this.calls[index];\n }\n}\n","/**\n * Canonicalise a tool invocation's arguments BEFORE the permission gate sees them (issue #2429).\n *\n * `Read`, `Write` and `Edit` declare `filePath` absolute, but nothing makes the model comply. A\n * relative `filePath` reaching the gate as written cannot be compared with an absolute pattern —\n * `Read(/w/**)` against `src/x` — and CORE-049 answers that case \"unevaluable\" (a deny prompts, an\n * allow does not auto-approve) rather than guessing the base. The base is not a guess here: the\n * session's `cwd` is the containment root the tool itself anchors a relative path to, so resolving\n * against it produces the exact path the tool will open, and the gate judges that.\n *\n * Which argument is a path is what the tool's registered permission profile declares\n * (`argument.kind === 'path'`), so this module names no tool. The canonical parameters are what\n * the gate, the logs and the tool all receive — one input, not one for the decision and another\n * for the action.\n */\n\nimport { isAbsolute, resolve } from 'node:path';\n\nimport { getToolPermissionProfile } from '@robota-sdk/agent-core';\n\nimport type { TToolParameters } from '@robota-sdk/agent-core';\n\nexport function canonicaliseToolArguments(\n toolName: string,\n parameters: TToolParameters,\n cwd: string,\n): TToolParameters {\n const argument = getToolPermissionProfile(toolName).argument;\n if (argument === undefined || argument.kind !== 'path') return parameters;\n const value = parameters[argument.key];\n if (typeof value !== 'string' || value === '' || isAbsolute(value)) return parameters;\n return { ...parameters, [argument.key]: resolve(cwd, value) };\n}\n","import { PERMISSION_DENIED_RESULT, reportToolCrash, toolFailure } from './permission-types.js';\nimport {\n createLogger,\n isAbortFailure,\n TOOL_BODY_EVENTS,\n TOOL_PERMISSION_EVENTS,\n} from '@robota-sdk/agent-core';\nimport { canonicaliseToolArguments } from './tool-argument-canonicalisation.js';\nimport {\n buildHookInput,\n firePostToolHook,\n runPreToolHook,\n truncateToolResult,\n} from './tool-hook-helpers.js';\n\nimport type { IPermissionEnforcerOptions, IPermissionRefusal } from './permission-types.js';\nimport type { TSessionLogData } from './session-logger.js';\nimport type {\n IToolExecutionContext,\n IToolResult,\n IToolWithEventService,\n ITerminalOutput,\n TToolArgs,\n TToolParameters,\n} from '@robota-sdk/agent-core';\n\nconst logger = createLogger('ToolBodyTrace');\n\n/** Never let a permission observation break the tool_result it merely watches. */\nfunction emitPermissionDecision(\n context: IToolExecutionContext | undefined,\n decision: 'allowed' | 'denied' | 'hook-blocked',\n): void {\n try {\n context?.eventService?.emit(TOOL_PERMISSION_EVENTS.DECIDED, {\n timestamp: new Date(),\n executionId: context.executionId,\n decidedAt: new Date().toISOString(),\n decision,\n });\n } catch (error) {\n logger.warn(\n 'tool permission observation failed',\n error instanceof Error ? error : new Error(String(error)),\n );\n }\n}\n\n/** Exactly what the wrapper reads from the enforcer — no more, and named so it cannot quietly grow. */\nexport interface IToolWrapperDeps {\n readonly sessionId: string;\n readonly cwd: string;\n readonly config: IPermissionEnforcerOptions['config'];\n readonly terminal: ITerminalOutput;\n readonly transcriptPath?: string;\n readonly onToolExecution?: IPermissionEnforcerOptions['onToolExecution'];\n readonly hookTypeExecutors?: IPermissionEnforcerOptions['hookTypeExecutors'];\n getPermissionMode: IPermissionEnforcerOptions['getPermissionMode'];\n log(event: string, detail: TSessionLogData): void;\n checkPermission(\n toolName: string,\n toolArgs: TToolArgs,\n signal?: AbortSignal,\n interaction?: IToolExecutionContext['permissionInteraction'],\n hookTraceEnv?: IToolExecutionContext['hookTraceEnv'],\n ): Promise<boolean | IPermissionRefusal>;\n}\n\n/**\n * Wrap one tool so every call passes the permission gate, the hooks and the truncation limit.\n *\n * Extracted from `PermissionEnforcer` because the file-size ratchet refused to let it grow further,\n * and a ratchet that says \"split instead of extending\" is asking for exactly this. It takes what it\n * needs as `deps` rather than the enforcer itself: the ten members it reads are the honest surface\n * of this function, and naming them is what makes the extraction a boundary rather than a move.\n */\nexport function wrapToolWithPermission(\n tool: IToolWithEventService,\n enforcer: IToolWrapperDeps,\n): IToolWithEventService {\n const originalExecute = tool.execute.bind(tool);\n\n const wrappedTool = Object.create(tool) as IToolWithEventService;\n wrappedTool.execute = async (\n rawParameters: TToolParameters,\n context?: IToolExecutionContext,\n ): Promise<IToolResult> => {\n // Issue #2429: the gate, the hooks, the logs and the tool all see ONE canonical form of the\n // arguments — a relative path argument resolved against the session root — so a pattern judges\n // the path the tool will actually open. Canonicalising needs the tool's name, which is read\n // inside the try below, so until then this holds the raw form.\n let parameters: TToolParameters = rawParameters;\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 // Read INSIDE the try, and held for the catch. Hoisting it out put an unguarded call above\n // the comment that says this function must never throw — a tool whose `getName` is missing or\n // throws would have propagated, which is the corruption that comment exists to prevent. The\n // catch needs the name only to announce the failure, and a call that has not reached it yet\n // has nothing to announce.\n let toolName = '(unknown)';\n\n try {\n toolName = tool.getName();\n parameters = canonicaliseToolArguments(toolName, rawParameters, enforcer.cwd);\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 context?.hookTraceEnv,\n );\n if (preResult) {\n enforcer.log('tool_blocked', { tool: toolName, reason: 'hook' });\n emitPermissionDecision(context, 'hook-blocked');\n return preResult;\n }\n\n // RUNTIME-005: the turn's signal reaches this wrapper (CORE-018) and stopped here.\n const verdict = await enforcer.checkPermission(\n toolName,\n parameters as TToolArgs,\n context?.signal,\n context?.permissionInteraction,\n context?.hookTraceEnv,\n );\n if (verdict !== true) {\n enforcer.log('tool_denied', { tool: toolName, reason: 'permission' });\n emitPermissionDecision(context, 'denied');\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 // A refusal that carries its reason (the auto-mode classifier) hands it to the model.\n return typeof verdict === 'object'\n ? toolFailure('denied', verdict.message)\n : PERMISSION_DENIED_RESULT;\n }\n\n emitPermissionDecision(context, 'allowed');\n context?.signal?.throwIfAborted();\n enforcer.onToolExecution?.({\n type: 'start',\n toolName,\n toolArgs: parameters as TToolArgs,\n executionId: context?.executionId,\n });\n\n // The observation covers ONLY the awaited body, never approval, hooks, truncation or a\n // detached continuation. A pre-start denial/abort therefore has no tool-body span.\n const startedAtMs = Date.now();\n let outcome: 'success' | 'failure' | 'interrupted' = 'failure';\n let result: IToolResult;\n try {\n result = await originalExecute(parameters, context as IToolExecutionContext);\n outcome = context?.signal?.aborted ? 'interrupted' : result.success ? 'success' : 'failure';\n } catch (error) {\n outcome = context?.signal?.aborted || isAbortFailure(error) ? 'interrupted' : 'failure';\n throw error;\n } finally {\n try {\n context?.eventService?.emit(TOOL_BODY_EVENTS.COMPLETED, {\n timestamp: new Date(),\n executionId: context.executionId,\n startedAt: new Date(startedAtMs).toISOString(),\n endedAt: new Date(Math.max(Date.now(), startedAtMs)).toISOString(),\n outcome,\n ...(typeof context.toolBodyId === 'string' ? { toolBodyId: context.toolBodyId } : {}),\n });\n } catch (error) {\n // An observer must never turn a completed tool body into a missing tool_result.\n logger.warn(\n 'tool body observation failed',\n error instanceof Error ? error : new Error(String(error)),\n );\n }\n }\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 ?? 0);\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 context?.hookTraceEnv,\n );\n return truncatedResult;\n } catch (err) {\n // CORE-027 — beside the envelope it returns, in `permission-types.ts`.\n return reportToolCrash(err, enforcer.onToolExecution, {\n toolName,\n toolArgs: parameters as TToolArgs,\n executionId: context?.executionId,\n });\n }\n };\n\n // SELFHOST-004: the wrapper runs `originalExecute` (bound to the ORIGINAL tool), which reads the\n // ORIGINAL tool's `eventService` (e.g. the `FunctionTool` span-completion emit). Because\n // `Object.create(tool)` would shadow a `setEventService` call onto the wrapper instance, forward it\n // to the original tool — otherwise an injected event bus never reaches the tool and spans never fire.\n wrappedTool.setEventService = (eventService) => {\n tool.setEventService(eventService);\n };\n\n return wrappedTool;\n}\n","/**\n * Where a path a shell command names really is, for the gate's read-only command check (issue\n * #3082). Symlinks are followed, as `Read`'s own containment check does: a link committed inside\n * the workspace can point anywhere, so a string check alone cannot say a command stays inside.\n */\n\nimport { realpathSync } from 'node:fs';\nimport { dirname, isAbsolute, relative, resolve } from 'node:path';\n\nimport type { TResolveInWorkspace } from '@robota-sdk/agent-core';\n\n/** The real path of `path`, or of its nearest existing ancestor with the rest appended. */\nfunction realOrNearest(path: string): string {\n let existing = path;\n const rest: string[] = [];\n for (;;) {\n try {\n const real = realpathSync(existing);\n return rest.length === 0 ? real : resolve(real, ...rest.reverse());\n } catch {\n const parent = dirname(existing);\n if (parent === existing) return path;\n rest.push(existing.slice(parent.length).replace(/^[\\\\/]/, ''));\n existing = parent;\n }\n }\n}\n\nfunction isInside(root: string, path: string): boolean {\n const fromRoot = relative(root, path);\n return fromRoot === '' || (!fromRoot.startsWith('..') && !isAbsolute(fromRoot));\n}\n\nexport function createWorkspacePathResolver(cwd: string): TResolveInWorkspace {\n return (base, path) => {\n const root = realOrNearest(cwd);\n const real = realOrNearest(resolve(base ?? root, path));\n return isInside(root, real) ? real : undefined;\n };\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 { homedir } from 'node:os';\n\nimport {\n allowRulesForAutoMode,\n applyPresetToolLists,\n evaluatePermission,\n findInvalidPermissionPatterns,\n findPermissionPatternWarnings,\n getToolPermissionProfile,\n isToolDeniedOutright,\n matchesAnyPattern,\n projectPermissionPolicy,\n registerToolPermissionProfile,\n requiresFreshApproval,\n runHooks,\n} from '@robota-sdk/agent-core';\n\nimport { decideApproval } from './abortable-approval.js';\nimport { AutoModeGate } from './auto-mode-gate.js';\nimport { consentScopeFor } from './consent-scope.js';\nimport { buildHookInput, runPreToolHook } from './tool-hook-helpers.js';\nimport { PermissionDenialLog } from './permission-denial-log.js';\nimport { wrapToolWithPermission } from './tool-permission-wrapper.js';\nimport { createWorkspacePathResolver } from './workspace-path-resolver.js';\n\nimport type {\n IPermissionEnforcerOptions,\n IPermissionRefusal,\n TPermissionHandler,\n TPermissionResult,\n ITerminalOutput,\n ISpinner,\n} from './permission-types.js';\nimport type { IPermissionDenial } from './permission-denial-log.js';\nimport type { ISessionLogger, TSessionLogData } from './session-logger.js';\nimport type { IToolWrapperDeps } from './tool-permission-wrapper.js';\nimport type {\n IToolExecutionContext,\n IToolWithEventService,\n TToolArgs,\n TToolParameters,\n THooksConfig,\n TResolveInWorkspace,\n} from '@robota-sdk/agent-core';\n\nexport type { TPermissionHandler, TPermissionResult, ITerminalOutput, ISpinner };\nexport type { IPermissionEnforcerOptions };\n\n/**\n * Throw naming every malformed permission pattern and why (issue #2428). Allow rules are held to\n * the narrower allow grammar (issue #3081).\n */\nfunction assertPermissionPatternsEvaluable(rules: {\n allow: readonly string[];\n restrictive: readonly string[];\n}): void {\n const problems = [\n ...findInvalidPermissionPatterns(rules.allow, 'allow'),\n ...findInvalidPermissionPatterns(rules.restrictive, 'deny'),\n ];\n if (problems.length === 0) return;\n const listed = problems.map(({ pattern, reason }) => `\"${pattern}\" ${reason}`).join('; ');\n throw new Error(\n `Invalid permission pattern(s) in permissions.allow/deny/ask: ${listed}. ` +\n 'Fix the pattern where it is configured (issue #2428).',\n );\n}\n\n/** How a decision's call will run, where that changes the answer. */\ninterface IDecisionScope {\n /** `false` when the call will NOT run inside the command sandbox, so its approval cannot apply. */\n readonly sandboxed?: boolean;\n}\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 /**\n * Issue #2351: consent is remembered as PATTERNS (`consentScopeFor`), not tool names, and read\n * back through the gate's own matcher — approving one argument does not allow every argument.\n */\n private readonly sessionAllowedTools = new Set<string>();\n /** The configured rules before any preset contributed — see {@link applyPresetToolLists}. */\n private readonly presetFreeRules: { allow: readonly string[]; deny: readonly string[] };\n private readonly onProjectAllowTool?: (toolName: string) => void;\n private readonly permissionPolicy?: IPermissionEnforcerOptions['permissionPolicy'];\n private readonly taskPermissions?: IPermissionEnforcerOptions['taskPermissions'];\n private readonly homeDirectory: string;\n private readonly resolveInWorkspace: TResolveInWorkspace;\n private readonly commandSandbox?: IPermissionEnforcerOptions['commandSandbox'];\n private readonly denials = new PermissionDenialLog();\n /** A turn a peer's message started is in progress: the one place the reply to that peer exists. */\n private peerTurn = false;\n private readonly autoMode?: AutoModeGate;\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 // Absent ⇒ no preset contributed, so the configured rules ARE the preset-free base. Copied, not\n // aliased: `applyPresetToolLists` writes back into `config.permissions`, and a shared array\n // would make the base track its own output.\n this.presetFreeRules = options.presetFreePermissions ?? {\n allow: [...options.config.permissions.allow],\n deny: [...options.config.permissions.deny],\n };\n // Issue #2428: a pattern the gate could never evaluate is refused HERE, with the pattern and\n // the reason, before any turn — not discovered one unevaluable prompt at a time at the gate.\n assertPermissionPatternsEvaluable(this.configuredRules(options));\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 this.permissionPolicy = options.permissionPolicy;\n this.taskPermissions = options.taskPermissions;\n this.homeDirectory = options.homeDirectory ?? homedir();\n this.resolveInWorkspace = createWorkspacePathResolver(options.cwd);\n this.commandSandbox = options.commandSandbox;\n if (options.permissionClassifier !== undefined) {\n this.autoMode = new AutoModeGate(options.permissionClassifier);\n }\n }\n\n /**\n * Start a turn, which a peer's message started when `peerTurn` is true. That decides only whether\n * the reply to the peer exists; every other call is decided exactly as in any turn.\n */\n beginTurn(peerTurn: boolean): void {\n this.peerTurn = peerTurn;\n }\n\n /** End the turn; the reply to a peer is gone until the next peer turn begins. */\n endTurn(): void {\n this.peerTurn = false;\n }\n\n /** Whether `auto` mode can run here: it needs a classifier to decide for it. */\n hasPermissionClassifier(): boolean {\n return this.autoMode !== undefined;\n }\n\n /**\n * Let the call behind a classifier denial run once, unjudged, when the model tries it again.\n * Returns the denial, or `undefined` when `index` names no classifier denial.\n */\n allowRetryOfDenial(index: number): IPermissionDenial | undefined {\n const denial = this.denials.list()[index];\n const call = this.denials.callAt(index);\n if (denial?.reason !== 'classifier' || call === undefined || this.autoMode === undefined) {\n return undefined;\n }\n this.autoMode.grantRetry(call.toolName, call.toolArgs);\n return denial;\n }\n\n /** Every configured pattern, split by the grammar it is held to. */\n private configuredRules(\n options: Pick<IPermissionEnforcerOptions, 'config' | 'taskPermissions'> = {\n config: this.config,\n ...(this.taskPermissions !== undefined ? { taskPermissions: this.taskPermissions } : {}),\n },\n ): { allow: string[]; restrictive: string[] } {\n return {\n allow: [...options.config.permissions.allow, ...(options.taskPermissions?.allow ?? [])],\n restrictive: [\n ...options.config.permissions.deny,\n ...(options.config.permissions.ask ?? []),\n ...(options.taskPermissions?.deny ?? []),\n ],\n };\n }\n\n /**\n * Whether the model is shown this tool at all. A bare-name deny (`Tool`, `Tool(*)`, a name glob)\n * removes it rather than offering it and refusing every call (issue #3081). Read live, so a\n * `/preset` that denies a tool hides it from the next round.\n */\n isToolVisible(toolName: string): boolean {\n // The reply exists in a peer turn alone; a peer turn is otherwise shown what any turn is.\n if (!this.peerTurn && getToolPermissionProfile(toolName).repliesToPeer === true) return false;\n return !isToolDeniedOutright(toolName, [\n ...this.config.permissions.deny,\n ...(this.taskPermissions?.deny ?? []),\n ]);\n }\n\n /**\n * Tell the gate each tool's parameter names — the schema is what makes `Tool(name:value)` a\n * parameter rule — then re-check the rules against them, before any turn runs.\n */\n private registerToolParameters(tools: readonly IToolWithEventService[]): void {\n for (const tool of tools) {\n // Read defensively: a tool without a schema has no parameters to name.\n const schema = (tool as Partial<Pick<IToolWithEventService, 'schema'>>).schema;\n if (schema === undefined) continue;\n const properties = schema.parameters?.properties ?? {};\n registerToolPermissionProfile(schema.name, { parameters: Object.keys(properties) });\n }\n const rules = this.configuredRules();\n assertPermissionPatternsEvaluable(rules);\n for (const { pattern, reason } of findPermissionPatternWarnings(rules.restrictive)) {\n this.terminal.writeLine(` ⚠ Permission rule \"${pattern}\" ${reason}.`);\n }\n }\n\n /** Wrap all tools with permission checking */\n wrapTools(tools: IToolWithEventService[]): IToolWithEventService[] {\n this.registerToolParameters(tools);\n // Built explicitly rather than cast. A blind assertion here would compile only by silencing the\n // private-member mismatch, and this repository counts and ratchets those. Naming the ten members\n // is what makes the extraction a boundary: if the wrapper starts reading an eleventh, this stops\n // compiling instead of quietly widening.\n const deps: IToolWrapperDeps = {\n sessionId: this.sessionId,\n cwd: this.cwd,\n config: this.config,\n terminal: this.terminal,\n transcriptPath: this.transcriptPath,\n onToolExecution: this.onToolExecution,\n hookTypeExecutors: this.hookTypeExecutors,\n getPermissionMode: this.getPermissionMode,\n log: (event, detail) => this.log(event, detail),\n checkPermission: (toolName, toolArgs, signal, interaction, hookTraceEnv) =>\n this.decidePermission(toolName, toolArgs, signal, interaction, hookTraceEnv),\n };\n\n return tools.map((tool) => wrapToolWithPermission(tool, deps));\n }\n\n /** The consent patterns granted this session via \"Allow always\" — e.g. `Bash(git *)` (issue #2351). */\n getSessionAllowedTools(): string[] {\n return [...this.sessionAllowedTools];\n }\n\n /** The calls this session refused, most recent first (issue #3082). */\n getRecentDenials(): readonly IPermissionDenial[] {\n return this.denials.list();\n }\n\n /** Clear all session-scoped allow rules. */\n clearSessionAllowedTools(): void {\n this.sessionAllowedTools.clear();\n }\n\n /**\n * Replace the configured permission rules on a LIVE session (ARCH-040 Group C, issue #1934).\n *\n * The seam is this small because `checkPermission` reads `this.config.permissions` on every call\n * rather than snapshotting it at construction — so the next call sees the new rules and nothing\n * needs re-wiring. Without a seam the startup path could apply a preset's tool lists and the live\n * `/preset` path could not, which is the divergence `scan-preset-projection` exists to measure:\n * one session holding two answers for the same preset depending on WHEN it was chosen.\n *\n * **A call already in flight runs to completion.** `checkPermission` is awaited BEFORE the tool\n * executes, so such a call has already passed its gate, and a gate is a decision at a point in\n * time. There is also no rollback for a partially applied tool — a file already written stays\n * written — so a revocation that cannot undo is a stop, not a denial. Building one would rest on\n * the cancellation path, which RUNTIME-004 records as declared at four layers and honoured at none.\n *\n * A newly applied denial DOES outrank an earlier \"always allow\": `evaluatePermission` answers\n * `deny` before `promptForApproval` — the only reader of `sessionAllowedTools` — is reached. That\n * is not new behaviour here; it is the existing precedence, and it agrees with the combine rule\n * that a denial is not weakened by a later layer.\n */\n /**\n * The rules the next `checkPermission` will read.\n *\n * Exposed so a case can assert what a live re-application PRODUCED, not merely that the method\n * exists. Review found the first cut composing onto a contaminated base and no test could see it,\n * because nothing could look at the rules.\n */\n currentPermissionRules(): {\n allow: readonly string[];\n deny: readonly string[];\n ask: readonly string[];\n } {\n return {\n allow: [...this.config.permissions.allow],\n deny: [...this.config.permissions.deny],\n ask: [...(this.config.permissions.ask ?? [])],\n };\n }\n\n applyPresetToolLists(preset: {\n allowedTools?: readonly string[];\n deniedTools?: readonly string[];\n }): void {\n // The BASE is what the session was configured with independently of any preset — SUPPLIED, not\n // captured here. Capturing it lazily read `config.permissions` after the startup preset's\n // patterns were already baked in, so the first preset's allowlist survived every later switch:\n // the accumulation the replace rule exists to prevent, arriving through the base rather than\n // through the merge. Review found it; the comment two lines up had described the failure exactly\n // and the code still had it.\n const next = applyPresetToolLists(this.presetFreeRules, preset);\n this.config.permissions.allow = next.allow;\n this.config.permissions.deny = next.deny;\n }\n\n /** Evaluate permission for a tool call. `signal` — RUNTIME-005; see `decideApproval` for why a\n * cancelled approval denies. */\n async checkPermission(\n toolName: string,\n toolArgs: TToolArgs,\n signal?: AbortSignal,\n interaction: IToolExecutionContext['permissionInteraction'] = 'interactive',\n hookTraceEnv?: IToolExecutionContext['hookTraceEnv'],\n ): Promise<boolean> {\n return (\n (await this.decidePermission(toolName, toolArgs, signal, interaction, hookTraceEnv)) === true\n );\n }\n\n /**\n * Decide an action that has `toolName`'s effect but does not run through that tool — a command\n * that starts a process, say. It passes what the tool call would: the PreToolUse hooks (so\n * guardrails apply), then the gate's rules, mode, remembered consent and prompt. It never takes\n * the command sandbox's auto-approval, because the action does not run inside that sandbox.\n */\n async checkDelegatedToolCall(\n toolName: string,\n toolParameters: TToolParameters,\n signal?: AbortSignal,\n ): Promise<boolean> {\n const hookInput = buildHookInput(\n this.sessionId,\n this.cwd,\n toolName,\n toolParameters,\n this.getPermissionMode(),\n this.transcriptPath,\n );\n const blocked = await runPreToolHook(this.config.hooks, hookInput, this.hookTypeExecutors);\n if (blocked) {\n this.log('tool_blocked', { tool: toolName, reason: 'hook', delegated: true });\n return false;\n }\n const decision = await this.decidePermission(\n toolName,\n toolParameters as TToolArgs,\n signal,\n 'interactive',\n undefined,\n { sandboxed: false },\n );\n return decision === true;\n }\n\n /** {@link checkPermission}, keeping the reason a refusal carries for the model. */\n private async decidePermission(\n toolName: string,\n toolArgs: TToolArgs,\n signal?: AbortSignal,\n interaction: IToolExecutionContext['permissionInteraction'] = 'interactive',\n hookTraceEnv?: IToolExecutionContext['hookTraceEnv'],\n scope: IDecisionScope = {},\n ): Promise<boolean | IPermissionRefusal> {\n // Issue #3081: ONE evaluator for every caller. A background/subagent policy (CORE-025) only\n // adds a ceiling, an ask-everything flag and the task's own lists; the ceiling is checked before\n // bypassPermissions, so a policy still binds under a permissive mode.\n const policy =\n this.permissionPolicy !== undefined\n ? projectPermissionPolicy(this.permissionPolicy, {\n taskAllow: this.taskPermissions?.allow,\n taskDeny: this.taskPermissions?.deny,\n parentAllow: this.config.permissions.allow,\n })\n : undefined;\n\n const mode = this.getPermissionMode();\n const allow = [...this.config.permissions.allow, ...(policy?.allow ?? [])];\n const rules = {\n // An allow rule that lets any code run would carry every call past the classifier.\n allow: mode === 'auto' ? allowRulesForAutoMode(allow) : allow,\n deny: [...this.config.permissions.deny, ...(policy?.deny ?? [])],\n ask: this.config.permissions.ask ?? [],\n };\n const where = { cwd: this.cwd, homeDirectory: this.homeDirectory };\n const decision = evaluatePermission(toolName, toolArgs, mode, rules, {\n ...where,\n resolveInWorkspace: this.resolveInWorkspace,\n sandboxAutoApproved:\n scope.sandboxed !== false && this.sandboxAutoApproves(toolName, toolArgs),\n ...(policy?.ceiling !== undefined ? { ceiling: policy.ceiling } : {}),\n askAll: policy?.askAll ?? false,\n ...(this.peerTurn ? { peerTurn: true } : {}),\n });\n\n // SELFHOST-009: fire PermissionDecision (INFORMATIONAL-ONLY, non-blocking) right after the\n // decision is made. Fire-and-forget — the hook cannot change the outcome that follows.\n this.firePermissionDecisionHook(toolName, toolArgs, decision, hookTraceEnv);\n\n if (decision === 'auto') return true;\n if (decision === 'deny') {\n this.denials.record(toolName, toolArgs, 'policy');\n return false;\n }\n\n // 'approve' — route to the human-approval path. An ask that must reach a person every time is\n // not answered by a remembered consent, and does not create one (issue #3081).\n const fresh = requiresFreshApproval(toolName, toolArgs, rules, where);\n // In auto mode the classifier stands in for the person, except where a person is required: an\n // ask rule, a critical removal or protected path, or a policy that asks about everything.\n if (mode === 'auto' && this.autoMode !== undefined && !fresh && policy?.askAll !== true) {\n return this.decideInAutoMode(\n this.autoMode,\n toolName,\n toolArgs,\n signal,\n interaction,\n hookTraceEnv,\n scope,\n );\n }\n return this.promptForApproval(toolName, toolArgs, signal, interaction, fresh);\n }\n\n private async decideInAutoMode(\n gate: AutoModeGate,\n toolName: string,\n toolArgs: TToolArgs,\n signal: AbortSignal | undefined,\n interaction: IToolExecutionContext['permissionInteraction'],\n hookTraceEnv: IToolExecutionContext['hookTraceEnv'],\n scope: IDecisionScope,\n ): Promise<boolean | IPermissionRefusal> {\n if (gate.takeRetry(toolName, toolArgs)) return true;\n // A consent given this session still answers, unless it is one no auto-mode rule could be.\n if (\n matchesAnyPattern(toolName, toolArgs, allowRulesForAutoMode([...this.sessionAllowedTools]))\n ) {\n return true;\n }\n if (gate.isPaused()) {\n const allowed = await this.promptForApproval(toolName, toolArgs, signal, interaction, true);\n if (allowed) gate.resume();\n return allowed;\n }\n const judgement = await gate.judge({ toolName, toolArgs, cwd: this.cwd }, signal);\n if (signal?.aborted === true) return false;\n // The user left auto mode while the classifier was deciding: decide again under the new mode.\n if (this.getPermissionMode() !== 'auto') {\n return this.decidePermission(toolName, toolArgs, signal, interaction, hookTraceEnv, scope);\n }\n if (judgement.kind === 'allow') return true;\n this.denials.record(toolName, toolArgs, 'classifier', judgement.reason);\n return { message: judgement.message };\n }\n\n /**\n * The human-approval path: session-scoped allow list → custom handler → injected approval fn → fail-closed\n * deny. Every `approve` decision comes here, whoever the caller, so every ask fails closed identically\n * when no approver is attached (e.g. a detached background task).\n */\n private async promptForApproval(\n toolName: string,\n toolArgs: TToolArgs,\n signal?: AbortSignal,\n interaction: IToolExecutionContext['permissionInteraction'] = 'interactive',\n fresh = false,\n ): Promise<boolean> {\n const scope = consentScopeFor(toolName, toolArgs);\n const cancelledBeforeAsking = signal?.aborted === true;\n const hasApprover =\n interaction === 'interactive' &&\n (this.permissionHandler !== undefined || this.promptForApprovalFn !== undefined);\n const outcome = await decideApproval({\n toolName,\n alreadyAllowed:\n !fresh && matchesAnyPattern(toolName, toolArgs, [...this.sessionAllowedTools]),\n ...(interaction === 'interactive' && this.permissionHandler\n ? { handler: this.permissionHandler }\n : {}),\n ...(interaction === 'interactive' && this.promptForApprovalFn\n ? { injectedPrompt: this.promptForApprovalFn, terminal: this.terminal }\n : {}),\n toolArgs,\n ...(signal ? { signal } : {}),\n });\n // A turn cancelled before anyone was asked is not a refusal of this call.\n if (!outcome.allowed && !cancelledBeforeAsking) {\n this.denials.record(toolName, toolArgs, hasApprover ? 'user' : 'no-approver');\n }\n // A fresh-approval answer covers this call only: remembering its wide scope would let it answer\n // the next critical removal or protected write too.\n if (fresh) return outcome.allowed;\n if (outcome.rememberForProject) {\n if (this.onProjectAllowTool === undefined) {\n throw new Error('Project-wide permission persistence is unavailable for this session.');\n }\n this.onProjectAllowTool(scope);\n }\n if (outcome.rememberForSession) this.sessionAllowedTools.add(scope);\n return outcome.allowed;\n }\n\n /**\n * SELFHOST-009: fire the PermissionDecision hook (informational-only, non-blocking) via the shared\n * `runHooks` path. Fire-and-forget — the result is never awaited or consulted, so it cannot gate the\n * permission outcome. The sole blocking gate remains PreToolUse (`runPreToolHook`).\n */\n private firePermissionDecisionHook(\n toolName: string,\n toolArgs: TToolArgs,\n decision: string,\n hookTraceEnv: IToolExecutionContext['hookTraceEnv'],\n ): void {\n const permissionMode = this.getPermissionMode();\n void runHooks(\n this.config.hooks as THooksConfig | undefined,\n 'PermissionDecision',\n {\n session_id: this.sessionId,\n cwd: this.cwd,\n hook_event_name: 'PermissionDecision',\n tool_name: toolName,\n tool_input: toolArgs as Record<string, string | number | boolean | object>,\n permission_decision: decision,\n ...(permissionMode !== undefined && { permission_mode: permissionMode }),\n ...(this.transcriptPath !== undefined && { transcript_path: this.transcriptPath }),\n env: {\n CLAUDE_PROJECT_DIR: this.cwd,\n CLAUDE_SESSION_ID: this.sessionId,\n },\n },\n this.hookTypeExecutors,\n hookTraceEnv,\n ).catch(() => undefined);\n }\n\n /** Whether the OS sandbox confines this shell command and lets it run without a prompt. */\n private sandboxAutoApproves(toolName: string, toolArgs: TToolArgs): boolean {\n if (this.commandSandbox === undefined) return false;\n const argument = getToolPermissionProfile(toolName).argument;\n if (argument?.kind !== 'command') return false;\n const command = toolArgs[argument.key];\n return typeof command === 'string' && this.commandSandbox.autoApproves(toolName, command);\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 IEventService,\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 // Top level, beside `config` and not inside it — the constructor reads\n // `options.presetFreePermissions`. Nested, it was silently `undefined` on the only wiring that\n // matters, so the enforcer fell back to `config.permissions` (startup preset already baked in)\n // and reproduced the exact bug this change fixes. Excess-property checking does not catch this:\n // the conditional-spread idiom suppresses it.\n ...(options.presetFreePermissions !== undefined\n ? { presetFreePermissions: options.presetFreePermissions }\n : {}),\n terminal: options.terminal,\n permissionHandler: options.permissionHandler,\n ...(options.commandSandbox !== undefined ? { commandSandbox: options.commandSandbox } : {}),\n ...(options.permissionClassifier !== undefined\n ? { permissionClassifier: options.permissionClassifier }\n : {}),\n promptForApprovalFn: options.promptForApproval,\n sessionLogger: options.sessionLogger,\n onToolExecution: options.onToolExecution,\n hookTypeExecutors: options.hookTypeExecutors,\n transcriptPath,\n onProjectAllowTool: options.onProjectAllowTool,\n // CORE-025: forward the background/subagent task permission policy + its own allow/deny lists.\n permissionPolicy: options.permissionPolicy,\n taskPermissions: options.taskPermissions,\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 basePrompt: options.compactionBasePrompt,\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 eventService: IEventService,\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 // ARCH-040: the same two channels `applyModelOptions` writes on a live session, so the\n // startup answer and the mid-session answer are the same answer.\n ...(options.temperature !== undefined && { temperature: options.temperature }),\n ...(options.maxOutputTokens !== undefined && { maxTokens: options.maxOutputTokens }),\n },\n // Single source of truth for the system prompt (agent-level, not model config).\n systemMessage,\n tools: wrappedTools,\n // Issue #3081: a tool denied outright by name is withheld from the model, not offered and refused.\n isToolVisible: (toolName) => permissionEnforcer.isToolVisible(toolName),\n logging: { enabled: false },\n // SELFHOST-004: the session-owned observable event bus. Tools (incl. the FunctionTool span\n // emit) are wired to it via the agent, so the interactive turn can subscribe to span-completion\n // events and project them onto session history. Absent this, the agent falls back to the no-op\n // default event service and no span events fire.\n eventService,\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 // NEUT-005: surface-injected remediation wording for the core's hard-capacity notice.\n ...(options.contextCapacityHint !== undefined\n ? { contextCapacityHint: options.contextCapacityHint }\n : {}),\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 { 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 ISubprocessTraceEnv,\n} from '@robota-sdk/agent-core';\nimport type {\n IInteractiveSessionRecord,\n TSessionLoadOutcome,\n IInteractiveSessionStore,\n} from '@robota-sdk/agent-interface-session';\n\nconst logger = createLogger('SessionHistoryOps');\n\n/** Dependencies for compact() */\nexport interface ICompactContext {\n sessionId: string;\n cwd: string;\n systemMessage: string;\n agent: 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 /** The prompt's trace for both compaction hooks, present only for a compaction inside a prompt. */\n hookTraceEnv?: ISubprocessTraceEnv;\n log: (event: string, data: TSessionLogData) => void;\n}\n\n/** What compaction needs beyond the run context it shares eight fields with. */\nexport interface ICompactExtras {\n systemMessage: string;\n compactionOrchestrator: CompactionOrchestrator;\n onCompactCallback: ((summary: string) => void) | undefined;\n onCompactEventCallback: ((event: ICompactEvent) => void) | undefined;\n trigger: TCompactTrigger;\n hookTraceEnv?: ISubprocessTraceEnv;\n}\n\n/**\n * Assemble the compaction context from the run context plus the five fields only it needs.\n *\n * The eight shared fields were written out twice in `Session`, once per context — the duplication\n * this module is the natural owner of, since it is the one that consumes the result.\n */\nexport function buildCompactContext(\n run: Omit<ICompactContext, keyof ICompactExtras>,\n extras: ICompactExtras,\n): ICompactContext {\n return { ...run, ...extras };\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 * @param signal - The turn's cancellation signal (RUNTIME-004). When it aborts the orchestrator\n * THROWS, so this propagates and the history replacement below is never reached — the conversation\n * is append-only source data and a cancel must not replace it with a summary the user asked not to\n * produce. The error is an `AbortError`, which `isAbortFailure` already classifies as the user's\n * own cancellation rather than a failed turn.\n */\nexport async function compact(\n instructions: string | undefined,\n ctx: ICompactContext,\n signal?: AbortSignal,\n): Promise<void> {\n // RUNTIME-004: before the CORE-031 guard, so a cancelled turn is reported as cancelled whether or\n // not there was anything to compact. The orchestrator used to make this check for us, and the early\n // return below would otherwise resolve an aborted compaction quietly — a narrower abort contract\n // (\"rejects if cancelled AND there was work\") for no gain over the one already in force.\n signal?.throwIfAborted();\n\n const history = ctx.agent.getHistory();\n\n // Exclude system messages from compaction — they are preserved and re-injected after\n const nonSystemHistory = history.filter((msg) => msg.role !== 'system');\n // CORE-031: guard on what will actually be compacted, not on the full history. Guarding on\n // `history` and then compacting `nonSystemHistory` let a system-messages-only conversation through\n // — a fresh session before its first turn holds exactly that — and the replacement below wrote an\n // empty `[Context Summary]` over it. There is nothing to summarise here, and nothing to summarise\n // is a no-op, not a failure: the conversation is left exactly as it was found.\n if (nonSystemHistory.length === 0) return;\n\n ctx.contextTracker.updateFromHistory(history);\n const before = ctx.contextTracker.getContextState();\n\n // RUNTIME-004: the orchestrator throws if the turn was cancelled, so the history replacement below\n // is not reached — the same guarantee CORE-019 gives for an invalid summary.\n const summary = await ctx.compactionOrchestrator.compact(\n ctx.aiProvider,\n nonSystemHistory,\n instructions,\n signal,\n ctx.trigger,\n ctx.hookTraceEnv,\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.agent.clearHistory();\n ctx.agent.injectMessage('system', ctx.systemMessage);\n ctx.agent.injectMessage('assistant', `[Context Summary]\\n${summary}`);\n\n // Reset token tracking based on the new shorter history\n ctx.contextTracker.updateFromHistory(ctx.agent.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 ctx.hookTraceEnv,\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: IInteractiveSessionStore;\n agent: Robota;\n getFullHistory: () => Array<{\n id: string;\n timestamp: Date;\n category: string;\n type: string;\n data?: unknown;\n }>;\n}\n\n/**\n * Persist the current session to the store.\n *\n * ## Why this can decline to write (TRANS-007)\n *\n * The existing record is read to preserve the members this function does not own. When `load`\n * answered `undefined` for both \"no record\" and \"the file is damaged\", the spread contributed\n * nothing in the damaged case and this function OVERWROTE a recoverable file with a fresh, nearly\n * empty one — on the next autosave, which is however long the user keeps typing.\n *\n * So a non-`valid` load is no longer treated as \"no prior record\". `missing` is the only outcome\n * that legitimately means there is nothing to preserve; `corrupt` and `unsupported` mean there IS\n * something and this build cannot read it, and writing over it destroys the only copy.\n *\n * Returning silently is deliberate over throwing: this runs on an autosave path, and turning a\n * damaged file into a crashed session helps nobody. The outcome is reported to the caller so a\n * surface can say something; the guarantee this function makes is that it does not destroy.\n */\nexport function persistSession(ctx: IPersistContext): TSessionLoadOutcome {\n const history = ctx.agent.getHistory();\n const now = new Date().toISOString();\n\n const outcome = ctx.sessionStore.load(ctx.sessionId);\n if (outcome.status !== 'valid' && outcome.status !== 'missing') {\n return outcome;\n }\n const existing = outcome.status === 'valid' ? outcome.record : undefined;\n\n const record: IInteractiveSessionRecord = {\n ...existing,\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 return { status: 'valid', record };\n}\n","import { randomUUID } from 'node:crypto';\n\n/**\n * SEC-006 — session ids are used as PATH SEGMENTS (`<baseDir>/<id>.json`, `<logDir>/<id>.jsonl`,\n * `<rootDir>/<id>/` for checkpoints), and at least one caller supplies one from an untrusted source:\n * `POST /api/playground/sessions` reads `resumeSessionId` from an unauthenticated HTTP body and checks\n * only `typeof === 'string'`. An id of `../../x` therefore escaped the store directory on both read\n * and write.\n *\n * The guard lives at the id boundary rather than at each `join()` so every sink inherits it — the store,\n * the JSONL logger and the replay-log reader are three separate sinks on the same value.\n *\n * REJECT rather than sanitize: silently rewriting `../x` to `__x` would alias two distinct ids onto one\n * file, quietly cross-linking sessions. A malformed id is a bug or an attack; both should be loud.\n */\n\n/** Ids the app generates: `session_<uuid>`. This matches the path-component guard below. */\nconst SAFE_SESSION_ID = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;\n\n/** Generous, but bounded well under every filesystem's per-component limit (255 bytes). */\nconst MAX_SESSION_ID_LENGTH = 128;\n\n/** Generate a fresh session id for callers that do not provide one explicitly. */\nexport function createSessionId(): string {\n return `session_${randomUUID()}`;\n}\n\n/**\n * Whether `id` is safe to interpolate into a filesystem path as a single component.\n *\n * The pattern admits no `/`, no `\\` and no `:`, so the value cannot introduce a path separator or a\n * Windows drive qualifier; and because it must start with an alphanumeric, it can be neither `.` nor\n * `..`. With no separator available, an embedded `..` cannot form a traversal component.\n */\nexport function isSafeSessionId(id: string): boolean {\n return id.length > 0 && id.length <= MAX_SESSION_ID_LENGTH && SAFE_SESSION_ID.test(id);\n}\n\n/** Throw unless `id` is safe to use as a path component. */\nexport function assertSafeSessionId(id: string): void {\n if (!isSafeSessionId(id)) {\n throw new Error(\n `Invalid session id: ${JSON.stringify(id)}. A session id must be 1-${MAX_SESSION_ID_LENGTH} ` +\n 'characters of letters, digits, dot, underscore or hyphen, starting with a letter or digit.',\n );\n }\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 { ISessionRunOptions } from './session-types.js';\nimport type { IRunOptions } from '@robota-sdk/agent-core';\n\n/** The subset actually present, ready to spread into the agent-core run options. */\nexport function perTurnRunOptions(\n options?: ISessionRunOptions,\n): Pick<\n IRunOptions,\n 'ephemeralSystemContext' | 'driverId' | 'toolChoice' | 'traceContext' | 'withholdHostedTools'\n> {\n return {\n ...(options?.ephemeralSystemContext !== undefined && {\n ephemeralSystemContext: options.ephemeralSystemContext,\n }),\n ...(options?.driverId !== undefined && { driverId: options.driverId }),\n ...(options?.toolChoice !== undefined && { toolChoice: options.toolChoice }),\n ...(options?.traceContext !== undefined && { traceContext: options.traceContext }),\n // What a message-triggered turn does is decided by the ordinary permissions, and a provider's\n // hosted tools run at the vendor without reaching them — so such a turn has none.\n ...(options?.peerTurn === true && { withholdHostedTools: true }),\n };\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 PROVIDER_CALL_EVENTS,\n PROVIDER_FALLBACK_EVENTS,\n readModelFallbackNotice,\n createLogger,\n createUserMessage,\n getProviderCapabilities,\n isModelEffort,\n runHooks,\n traceEnvFor,\n} from '@robota-sdk/agent-core';\n\nimport { perTurnRunOptions } from './session-run-options.js';\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 {\n IProviderCallTraceObservation,\n ISessionOptions,\n ISessionRunOptions,\n} from './session-types.js';\nimport type {\n IAIProvider,\n IContextWindowState,\n IModelFallbackNotice,\n THooksConfig,\n IHookTypeExecutor,\n ISubprocessTraceEnv,\n TTextDeltaCallback,\n TModelEffortSelection,\n} from '@robota-sdk/agent-core';\nimport type { Robota } from '@robota-sdk/agent-core';\n\nconst logger = createLogger('SessionRun');\n\n/**\n * SELFHOST-009: fire an INFORMATIONAL-ONLY model-call hook event mapped from a provider-call\n * execution event the turn owner already observes. Fire-and-forget — `onExecutionEvent` is a void,\n * un-awaited callback, so this `runHooks` call cannot block or mutate `provider.chat()`. Its result\n * is never consulted for gating; only PreToolUse gates.\n */\nfunction fireModelCallHook(\n ctx: IRunContext,\n hookEvent: 'PreModelCall' | 'PostModelCall',\n data: Record<string, unknown>,\n hookTraceEnv: ISubprocessTraceEnv | undefined,\n): void {\n const model = typeof data['model'] === 'string' ? (data['model'] as string) : ctx.model;\n const provider =\n typeof data['provider'] === 'string' ? (data['provider'] as string) : ctx.aiProvider.name;\n const rawEffort = data['effort'];\n const effort =\n typeof rawEffort === 'string' && isModelEffort(rawEffort)\n ? rawEffort\n : (ctx.effort ??\n (typeof ctx.agent.getModel === 'function' ? ctx.agent.getModel().effort : undefined) ??\n 'high');\n const round = typeof data['round'] === 'number' ? (data['round'] as number) : undefined;\n void runHooks(\n ctx.hooks as THooksConfig | undefined,\n hookEvent,\n {\n session_id: ctx.sessionId,\n cwd: ctx.cwd,\n hook_event_name: hookEvent,\n model,\n provider,\n effort,\n ...(round !== undefined && { round }),\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 hookTraceEnv,\n ).catch((error) => logger.warn('hook failed', { error }));\n}\n\n/** Dependencies injected by Session.run() */\nexport interface IRunContext {\n sessionId: string;\n cwd: string;\n model: string;\n /** Model-effort selection for informational model-call hooks. */\n effort?: TModelEffortSelection;\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 agent: 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 /** RUNTIME-004: abort must not rewrite history. `hookTraceEnv` is the prompt's, for PreCompact. */\n compact: (signal?: AbortSignal, hookTraceEnv?: ISubprocessTraceEnv) => 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 emitProviderCallCompleted?: (observation: IProviderCallTraceObservation) => void;\n /** Tell the session's owner a request moved to another model, so it can say so. */\n emitProviderFallback?: (notice: IModelFallbackNotice) => void;\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 runOptions?: ISessionRunOptions,\n): Promise<string> {\n // Command hooks fired on this prompt's path name its root span; hooks elsewhere get nothing.\n const traceContext = runOptions?.traceContext;\n const hookTraceEnv = traceContext\n ? traceEnvFor('hooks', traceContext, traceContext.parentSpanId)\n : undefined;\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.agent.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 (hookTraceEnv ? ctx.compact(abortSignal, hookTraceEnv) : ctx.compact(abortSignal));\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 hookTraceEnv,\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.agent.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 let calledModel: Record<string, unknown> = {};\n response = await ctx.agent.run(enrichedMessage, {\n signal: abortSignal,\n maxExecutionRounds: ctx.maxTurns ?? 0,\n // Thin pass-through of the per-turn options to agent-core (SELFHOST-008 P3, PEER-007).\n ...perTurnRunOptions(runOptions),\n onExecutionEvent: (event, data) => {\n // This new local observability signal is persisted by the interactive history owner;\n // it is not a replay-substrate session-log event.\n if (event !== PROVIDER_CALL_EVENTS.COMPLETED) ctx.log(event, data as TSessionLogData);\n forwardToolExecutionEvent(toolExecutionBridge, event, data);\n // SELFHOST-009: fire the informational-only model-call events from the provider-call\n // execution events the turn owner already observes. provider_request → PreModelCall (before\n // provider.chat() returns); provider_response_normalized → PostModelCall (the SINGLE\n // canonical source — NOT provider_response_raw, which would double-fire per round). Both are\n // fire-and-forget: this callback is void/un-awaited, so they cannot gate/mutate the call.\n if (event === 'provider_request') {\n const request = data as Record<string, unknown>;\n calledModel = { model: request['model'], provider: request['provider'] };\n fireModelCallHook(ctx, 'PreModelCall', request, hookTraceEnv);\n } else if (event === 'provider_response_normalized') {\n // Named after the model the request was last sent to, which answered it.\n fireModelCallHook(\n ctx,\n 'PostModelCall',\n { ...(data as Record<string, unknown>), ...calledModel },\n hookTraceEnv,\n );\n } else if (event === PROVIDER_FALLBACK_EVENTS.SWITCHED) {\n const notice = readModelFallbackNotice(data as Record<string, unknown>);\n if (notice !== undefined) {\n // The call to the model that failed ends here, so its PreModelCall gets its PostModelCall\n // before the request is announced again for the next model.\n fireModelCallHook(\n ctx,\n 'PostModelCall',\n { round: data['round'], model: notice.from.model, provider: notice.from.provider },\n hookTraceEnv,\n );\n ctx.emitProviderFallback?.(notice);\n }\n } else if (event === PROVIDER_CALL_EVENTS.COMPLETED && ctx.emitProviderCallCompleted) {\n // Forward an allowlist, not the generic event envelope, across the session boundary.\n const observation = data as Record<string, unknown>;\n if (\n Number.isSafeInteger(observation['round']) &&\n (observation['round'] as number) > 0 &&\n typeof observation['startedAt'] === 'string' &&\n typeof observation['endedAt'] === 'string' &&\n (observation['outcome'] === 'success' ||\n observation['outcome'] === 'failure' ||\n observation['outcome'] === 'interrupted')\n ) {\n ctx.emitProviderCallCompleted({\n round: observation['round'] as number,\n startedAt: observation['startedAt'],\n endedAt: observation['endedAt'],\n outcome: observation['outcome'],\n ...(typeof observation['callId'] === 'string' &&\n /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(observation['callId']) &&\n { callId: observation['callId'] }),\n ...((observation['disposition'] === 'invoked' ||\n observation['disposition'] === 'cache-hit' ||\n observation['disposition'] === 'preflight-refused') &&\n { disposition: observation['disposition'] }),\n ...(typeof observation['providerId'] === 'string' &&\n observation['providerId'].length > 0 && observation['providerId'].length <= 128 &&\n [...observation['providerId']].every((char) => char.charCodeAt(0) >= 32) &&\n { providerId: observation['providerId'] }),\n ...(typeof observation['modelId'] === 'string' &&\n observation['modelId'].length > 0 && observation['modelId'].length <= 128 &&\n [...observation['modelId']].every((char) => char.charCodeAt(0) >= 32) &&\n { modelId: observation['modelId'] }),\n ...((observation['usageProvenance'] === 'complete' ||\n observation['usageProvenance'] === 'partial' ||\n observation['usageProvenance'] === 'absent') &&\n { usageProvenance: observation['usageProvenance'] }),\n ...(observation['usageProvenance'] === 'complete' &&\n typeof observation['promptTokens'] === 'number' &&\n Number.isSafeInteger(observation['promptTokens']) && observation['promptTokens'] >= 0 &&\n typeof observation['completionTokens'] === 'number' &&\n Number.isSafeInteger(observation['completionTokens']) && observation['completionTokens'] >= 0 &&\n typeof observation['totalTokens'] === 'number' &&\n Number.isSafeInteger(observation['totalTokens']) &&\n observation['totalTokens'] === observation['promptTokens'] + observation['completionTokens'] &&\n {\n promptTokens: observation['promptTokens'],\n completionTokens: observation['completionTokens'],\n totalTokens: observation['totalTokens'],\n }),\n ...(observation['disposition'] === 'invoked' &&\n typeof observation['providerRequestId'] === 'string' &&\n { providerRequestId: observation['providerRequestId'] }),\n });\n }\n }\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.agent.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.agent.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 hookTraceEnv,\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.agent.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 hookTraceEnv,\n ).catch((error) => logger.warn('hook failed', { error }));\n\n if (ctx.getSessionStore()) {\n ctx.persistSession();\n }\n\n return response;\n}\n","import { randomUUID } from 'node:crypto';\n\nimport type { TurnClaim } from './turn-claim.js';\nimport type { Robota, IToolExecutionResult, TToolParameters } from '@robota-sdk/agent-core';\n\n/** Direct calls share the session claim and retain their completion for graceful disposal. */\nexport class SessionRuntimeTools {\n private execution: Promise<IToolExecutionResult> | null = null;\n\n constructor(\n private readonly agent: Robota,\n private readonly claim: TurnClaim,\n private readonly sessionId: string,\n ) {}\n\n async invoke(\n name: string,\n parameters: TToolParameters,\n signal?: AbortSignal,\n ): Promise<IToolExecutionResult> {\n const controller = this.claim.claim();\n const unlink = linkCancellation(controller, signal);\n try {\n controller.signal.throwIfAborted();\n this.execution = this.agent.invokeRuntimeTool(name, parameters, {\n toolName: name,\n parameters,\n executionId: randomUUID(),\n sessionId: this.sessionId,\n signal: controller.signal,\n permissionInteraction: 'deny',\n });\n return await this.execution;\n } finally {\n this.execution = null;\n unlink();\n this.claim.release(controller);\n }\n }\n\n async drain(): Promise<void> {\n await this.execution;\n }\n}\n\n/** Link only this operation's signal; releasing it never cancels another claim. */\nexport function linkCancellation(controller: AbortController, signal?: AbortSignal): () => void {\n const abort = (): void => controller.abort(signal?.reason);\n if (signal?.aborted) abort();\n else signal?.addEventListener('abort', abort, { once: true });\n return () => signal?.removeEventListener('abort', abort);\n}\n","import {\n TRUST_TO_MODE,\n ObservableEventService,\n PROVIDER_CALL_EVENTS,\n PROVIDER_FALLBACK_EVENTS,\n} 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 { buildCompactContext, compact, persistSession } from './session-history-ops.js';\nimport { createSessionId } from './session-id.js';\nimport {\n configureProvider,\n fireSessionEndHook,\n fireSessionStartHook,\n} from './session-lifecycle.js';\nimport { executeRun } from './session-run.js';\nimport { SessionRuntimeTools, linkCancellation } from './session-runtime-tools.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 {\n ICompactEvent,\n ISessionOptions,\n ISessionShutdownOptions,\n ISessionRunOptions,\n TCompactTrigger,\n} from './session-types.js';\nimport type {\n IAIProvider,\n IContextWindowState,\n IEventService,\n IToolSchema,\n IToolExecutionResult,\n IToolWithEventService,\n TToolParameters,\n TPermissionMode,\n IHookTypeExecutor,\n ISubprocessTraceEnv,\n} from '@robota-sdk/agent-core';\nimport type { Robota } from '@robota-sdk/agent-core';\nimport type { IInteractiveSessionStore } from '@robota-sdk/agent-interface-session';\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\n/** Wraps a Robota agent with project context, permission state, and optional persistence. */\nexport class Session extends SessionBase {\n protected readonly agent: Robota;\n /**\n * SELFHOST-004: session-owned observable event bus. Injected into the agent so tools (incl. the\n * `FunctionTool` span-completion emit) publish here; the interactive turn subscribes to it to\n * project per-operation spans onto session history. Exposed read-only via {@link getEventService}.\n */\n protected readonly eventService: IEventService = new ObservableEventService();\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 private readonly terminal: ITerminalOutput;\n private readonly sessionStore?: IInteractiveSessionStore;\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 readonly runtimeTools: SessionRuntimeTools;\n private readonly wrapAddedTools: ISessionOptions['wrapAddedTools'];\n /** Tools added while a turn ran, applied when the next one starts. */\n private readonly pendingTools: IToolWithEventService[] = [];\n /** The last tool change; the next one waits for it. */\n private toolChange: Promise<void> = Promise.resolve();\n private shuttingDown = false;\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(options.cwd);\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.wrapAddedTools = options.wrapAddedTools;\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 = options.sessionId ?? createSessionId();\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.transcriptPath;\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 this.requireClassifierFor(this.permissionMode);\n this.addPermissionModeGuard((next) => this.requireClassifierFor(next));\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.agent = buildRobota(\n options,\n this.permissionEnforcer,\n tools,\n provider,\n this.model,\n systemMessage,\n this.eventService,\n );\n this.runtimeTools = new SessionRuntimeTools(this.agent, this.turnClaim, this.sessionId);\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 /**\n * @param options.ephemeralSystemContext SELFHOST-008 P3 — a transient system-role block included in this\n * turn's model call only, never persisted to history (thin pass-through to agent-core `IRunOptions`).\n * REJECTS with `SessionBusyError` if a turn is in flight — RUNTIME-003; see `turn-claim.ts`.\n */\n async run(message: string, rawInput?: string, options?: ISessionRunOptions): Promise<string> {\n if (this.shuttingDown) throw new Error('[LIFECYCLE] Session is shutting down');\n const controller = this.turnClaim.claim(); // Synchronously, before any await.\n const unlink = linkCancellation(controller, options?.signal);\n const { signal } = controller;\n // Whether the reply to a peer exists is decided per turn.\n this.permissionEnforcer.beginTurn(options?.peerTurn === true);\n try {\n signal.throwIfAborted();\n // Tools added while the last turn ran join at this boundary, before any request of this turn;\n // a change already in flight finishes first, so the turn never sees a list mid-update.\n await this.serializeToolChange(() => this.applyPendingTools());\n const response = await executeRun(message, rawInput, this.buildRunContext(), signal, options);\n this.messageCount += 1;\n return response;\n } finally {\n this.permissionEnforcer.endTurn();\n unlink();\n this.turnClaim.release(controller);\n }\n }\n\n /**\n * Make tools available from the next turn on — for a capability that became usable mid-session,\n * such as an MCP server connected after its sign-in. Each goes through the same wrappers and\n * permission gate as a tool present from the start. A tool whose name the session already has, or\n * has queued, is left out rather than replacing the one the conversation has been using.\n *\n * The tool list is part of what a provider caches a prompt by, and a turn's rounds must all see\n * the same list: while a turn runs, the tools wait and are applied when the next turn starts;\n * otherwise they are applied now. Calls are serialized, so two concurrent ones both land.\n * Resolves to the names that will be offered.\n */\n addTools(tools: readonly IToolWithEventService[]): Promise<readonly string[]> {\n if (this.shuttingDown) {\n return Promise.reject(new Error('[LIFECYCLE] Session is shutting down'));\n }\n return this.serializeToolChange(async () => {\n const known = new Set([\n ...this.toolSchemas.map((schema) => schema.name),\n ...this.pendingTools.map((tool) => tool.schema.name),\n ]);\n const fresh: IToolWithEventService[] = [];\n for (const tool of tools) {\n if (known.has(tool.schema.name)) continue;\n known.add(tool.schema.name);\n fresh.push(tool);\n }\n if (fresh.length === 0) return [];\n this.pendingTools.push(...fresh);\n if (!this.turnClaim.isRunning()) await this.applyPendingTools();\n return fresh.map((tool) => tool.schema.name);\n });\n }\n\n /** Runs `change` after every tool change before it, so each reads the list the last one wrote. */\n private serializeToolChange<T>(change: () => Promise<T>): Promise<T> {\n const result = this.toolChange.then(change);\n this.toolChange = result.then(\n () => undefined,\n () => undefined,\n );\n return result;\n }\n\n /** Registers the queued tools with the agent. Only ever called inside `serializeToolChange`. */\n private async applyPendingTools(): Promise<void> {\n if (this.pendingTools.length === 0) return;\n const fresh = this.pendingTools.splice(0);\n await this.agent.ensureReady();\n const wrapped = this.permissionEnforcer.wrapTools(this.wrapAddedTools?.(fresh) ?? fresh);\n await this.agent.updateTools([...(this.agent.getConfig().tools ?? []), ...wrapped]);\n this.toolSchemas.push(...wrapped.map((tool) => tool.schema));\n }\n\n async listRuntimeTools(): Promise<IToolSchema[]> {\n if (this.shuttingDown) throw new Error('[LIFECYCLE] Session is shutting down');\n return this.agent.listRuntimeTools();\n }\n\n async invokeRuntimeTool(\n name: string,\n parameters: TToolParameters,\n options?: { signal?: AbortSignal },\n ): Promise<IToolExecutionResult> {\n if (this.shuttingDown) throw new Error('[LIFECYCLE] Session is shutting down');\n return this.runtimeTools.invoke(name, parameters, options?.signal);\n }\n\n /**\n * SELFHOST-004: the session-owned observable event bus the agent's tools publish to. The interactive\n * turn subscribes to it to collect span-completion events and project them onto session history.\n */\n getEventService(): IEventService {\n return this.eventService;\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 agent: this.agent,\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 this.shuttingDown = true;\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 await step('drain-direct-tool', () => this.runtimeTools.drain());\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.agent.destroy();\n });\n })();\n return this.shutdownPromise;\n }\n\n swapProvider(newProvider: IAIProvider, model: string): void {\n this.agent.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(\n instructions?: string,\n trigger: TCompactTrigger = 'manual',\n signal?: AbortSignal,\n ): Promise<void> {\n await this.compactWith(instructions, trigger, signal);\n }\n\n /** `hookTraceEnv` reaches PreCompact only for a compaction inside a prompt (see `executeRun`). */\n private async compactWith(\n instructions: string | undefined,\n trigger: TCompactTrigger,\n signal?: AbortSignal,\n hookTraceEnv?: ISubprocessTraceEnv,\n ): Promise<void> {\n const extras = {\n systemMessage: this.systemMessage,\n compactionOrchestrator: this.compactionOrchestrator,\n onCompactCallback: this.onCompactCallback,\n onCompactEventCallback: this.onCompactEventCallback,\n trigger,\n ...(hookTraceEnv ? { hookTraceEnv } : {}),\n };\n await compact(instructions, buildCompactContext(this.buildRunContext(), extras), signal);\n }\n\n private buildRunContext(): IRunContext {\n return {\n sessionId: this.sessionId,\n cwd: this.cwd,\n model: this.model,\n effort: this.getModelEffort(),\n agent: this.agent,\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: (signal, hookTraceEnv) => this.compactWith(undefined, 'auto', signal, hookTraceEnv),\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 emitProviderCallCompleted: (observation) =>\n this.eventService.emit(\n PROVIDER_CALL_EVENTS.COMPLETED,\n { timestamp: new Date(), ...observation },\n {\n ownerType: 'session',\n ownerId: this.sessionId,\n ownerPath: [{ type: 'session', id: this.sessionId }],\n },\n ),\n emitProviderFallback: (notice) =>\n this.eventService.emit(\n PROVIDER_FALLBACK_EVENTS.SWITCHED,\n {\n timestamp: new Date(),\n fromProvider: notice.from.provider,\n fromModel: notice.from.model,\n toProvider: notice.to.provider,\n toModel: notice.to.model,\n reason: notice.reason,\n },\n {\n ownerType: 'session',\n ownerId: this.sessionId,\n ownerPath: [{ type: 'session', id: this.sessionId }],\n },\n ),\n knownToolNames: this.toolSchemas.map((tool) => tool.name),\n };\n }\n}\n","/**\n * TRANS-005 (#2081) — the outcome vocabulary the session-record decoder answers in.\n *\n * ## Why an outcome and not an exception or `undefined`\n *\n * The defect this codec replaces is a store that answers \"is this a valid record?\" by returning\n * `undefined`, which its caller then reads as \"no such session\" and repairs by replaying a partial\n * reconstruction. Corruption and absence became the same answer, and the repair silently dropped\n * fields. A type that cannot spell that confusion is the fix: this outcome distinguishes a value\n * that failed to decode (`corrupt`) from one written by a build this one does not implement\n * (`unsupported`), and deliberately has NO `missing` member.\n *\n * `missing` is a property of a STORE, not of a value — a file that is not there never reaches a\n * decoder. The store composes its own `missing` with these three.\n */\n\nimport type {\n IInteractiveSessionRecord,\n ISessionRecordDecodeIssue,\n} from '@robota-sdk/agent-interface-session';\n\n/** What a decode of a persisted session record can conclude. */\nexport type TSessionRecordDecodeOutcome =\n | { readonly status: 'valid'; readonly record: IInteractiveSessionRecord }\n | { readonly status: 'corrupt'; readonly issues: readonly ISessionRecordDecodeIssue[] }\n | { readonly status: 'unsupported'; readonly schemaVersion: number | undefined };\n\n/**\n * Re-stated for readers of this module: `ISessionRecordDecodeIssue` is declared with the record it\n * describes, in the contract package (TRANS-007). The TYPE is a contract; this module owns the\n * MECHANISM that produces it.\n */\n\n/**\n * The accumulator a decode pass writes into.\n *\n * Every decoder takes it and appends; none of them returns early on the first failure, so one call\n * reports the whole shape of the damage rather than the first symptom of it.\n */\nexport type TDecodeIssues = ISessionRecordDecodeIssue[];\n\n/** `messages` + `id` → `messages.id`; a root-level key keeps its bare name. */\nexport function atKey(parent: string, key: string): string {\n return parent.length === 0 ? key : `${parent}.${key}`;\n}\n\n/** `messages` + `2` → `messages[2]`. */\nexport function atIndex(parent: string, index: number): string {\n return `${parent}[${index}]`;\n}\n\nexport function addIssue(issues: TDecodeIssues, path: string, message: string): void {\n issues.push({ path, message });\n}\n\n/**\n * A short, non-throwing description of what was actually found, for the human half of an issue.\n *\n * It names the KIND rather than printing the value: a session record carries prompts and tool\n * output, and an error string is exactly the kind of thing that ends up in a log.\n */\nexport function describeValue(value: unknown): string {\n if (value === null) return 'null';\n if (Array.isArray(value)) return 'an array';\n if (value instanceof Date) return 'a Date';\n switch (typeof value) {\n case 'undefined':\n return 'nothing';\n case 'string':\n return 'a string';\n case 'number':\n return 'a number';\n case 'boolean':\n return 'a boolean';\n case 'object':\n return 'an object';\n default:\n return `a ${typeof value}`;\n }\n}\n\n/**\n * Assign an optional member only when it decoded to a value.\n *\n * Writing `undefined` instead would make an absent member present-and-undefined, which survives\n * `JSON.stringify` as an omission but not as an identity — a decoded record must be the same shape\n * as the one that was persisted, not a wider one.\n */\nexport function setOptional<TTarget extends object, TKey extends keyof TTarget>(\n target: TTarget,\n key: TKey,\n value: TTarget[TKey] | undefined,\n): void {\n if (value !== undefined) target[key] = value;\n}\n","/**\n * TRANS-005 (#2081) — the leaf decoders every nested session-record decoder is built from.\n *\n * Each one takes the raw value, the path it sits at, and the issue accumulator; each returns the\n * decoded value or `undefined` after recording why. `undefined` is NOT \"the field was absent\" — an\n * absent optional member is handled by {@link decodeOptional}, and the caller decides validity by\n * asking whether any issue was recorded, never by testing a return value.\n */\n\nimport { addIssue, atIndex, atKey, describeValue } from './decode-outcome.js';\n\nimport type { TDecodeIssues } from './decode-outcome.js';\nimport type { TUniversalValue } from '@robota-sdk/agent-core';\nimport type { TBackgroundPrimitive } from '@robota-sdk/agent-interface-execution';\n\n/** Decode an optional member: absent stays absent, present is decoded, `null` is a defect. */\nexport function decodeOptional<TValue>(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n decode: (value: unknown, path: string, issues: TDecodeIssues) => TValue | undefined,\n): TValue | undefined {\n if (value === undefined) return undefined;\n return decode(value, path, issues);\n}\n\nexport function decodeString(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): string | undefined {\n if (typeof value === 'string') return value;\n addIssue(issues, path, `expected a string, received ${describeValue(value)}`);\n return undefined;\n}\n\nexport function decodeNumber(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): number | undefined {\n if (typeof value === 'number' && Number.isFinite(value)) return value;\n addIssue(issues, path, `expected a finite number, received ${describeValue(value)}`);\n return undefined;\n}\n\nexport function decodeInteger(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): number | undefined {\n if (typeof value === 'number' && Number.isInteger(value)) return value;\n addIssue(issues, path, `expected an integer, received ${describeValue(value)}`);\n return undefined;\n}\n\nexport function decodeBoolean(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): boolean | undefined {\n if (typeof value === 'boolean') return value;\n addIssue(issues, path, `expected a boolean, received ${describeValue(value)}`);\n return undefined;\n}\n\n/** Decode a member of a string-literal union, naming the permitted members when it is not one. */\nexport function decodeLiteral<TLiteral extends string>(\n value: unknown,\n allowed: readonly TLiteral[],\n path: string,\n issues: TDecodeIssues,\n): TLiteral | undefined {\n if (typeof value === 'string' && (allowed as readonly string[]).includes(value)) {\n return value as TLiteral;\n }\n addIssue(\n issues,\n path,\n `expected one of ${allowed.join(' | ')}, received ${describeValue(value)}`,\n );\n return undefined;\n}\n\n/**\n * Decode a member the contract declares `string` but means as an instant.\n *\n * It stays a string — the contract says so — but must be one a date can be read from, because the\n * session list is ordered by `new Date(updatedAt).getTime()` and an unparseable string sorts as\n * `NaN`, which is an unstable order rather than an error anyone sees.\n *\n * ## The limit, stated because a caller will otherwise over-read it\n *\n * This accepts whatever `Date.parse` accepts, which is wider than ISO-8601: `'2026'` parses, and so\n * do several implementation-defined forms. So the guarantee is exactly \"a date can be read from\n * this\", NOT \"this is a well-formed instant\" — a record carrying `'2026'` decodes, and reads back as\n * midnight on the first of January.\n *\n * Tightening it to strict ISO-8601 is deliberately NOT done here: the contract these members belong\n * to declares them `string` and says nothing about their format, and a decoder that refuses a value\n * its own contract permits is inventing a stricter contract than the one it decodes. A consumer that\n * needs a well-formed instant validates for itself; this one guarantees only that ordering by date\n * will not silently produce `NaN`.\n */\nexport function decodeTimestampString(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): string | undefined {\n if (typeof value !== 'string') {\n addIssue(\n issues,\n path,\n `expected an ISO-8601 timestamp string, received ${describeValue(value)}`,\n );\n return undefined;\n }\n if (Number.isNaN(Date.parse(value))) {\n addIssue(issues, path, 'expected a timestamp a date can be parsed from');\n return undefined;\n }\n return value;\n}\n\n/**\n * Decode a member the contract declares `Date`.\n *\n * JSON has no date type, so a persisted `Date` arrives as a string and every consumer that calls a\n * `Date` method on it fails at the call rather than at the load. This is the one place that gap is\n * closed: an ISO-8601 string OR a live `Date` decodes to a `Date`, and nothing else does.\n */\nexport function decodeDate(value: unknown, path: string, issues: TDecodeIssues): Date | undefined {\n if (value instanceof Date) {\n if (Number.isNaN(value.getTime())) {\n addIssue(issues, path, 'expected a valid Date, received an invalid one');\n return undefined;\n }\n return value;\n }\n if (typeof value === 'string' && !Number.isNaN(Date.parse(value))) return new Date(value);\n addIssue(\n issues,\n path,\n `expected a Date or an ISO-8601 timestamp string, received ${describeValue(value)}`,\n );\n return undefined;\n}\n\n/** Decode an array, decoding each element at its own indexed path. */\nexport function decodeArray<TItem>(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n decodeItem: (value: unknown, path: string, issues: TDecodeIssues) => TItem | undefined,\n): TItem[] | undefined {\n if (!Array.isArray(value)) {\n addIssue(issues, path, `expected an array, received ${describeValue(value)}`);\n return undefined;\n }\n const decoded: TItem[] = [];\n value.forEach((item, index) => {\n const element = decodeItem(item, atIndex(path, index), issues);\n if (element !== undefined) decoded.push(element);\n });\n return decoded;\n}\n\nexport function decodeStringArray(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): string[] | undefined {\n return decodeArray(value, path, issues, decodeString);\n}\n\n/**\n * Decode an object whose members are DECLARED, rejecting any key the contract does not name.\n *\n * A persisted record is written by this build's own code at a known version, so an unrecognised key\n * means the shape drifted — which is what the envelope version exists to report. Silently ignoring\n * it is how a field goes missing without anyone learning that it did.\n */\nexport function decodeDeclaredObject(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n declaredKeys: readonly string[],\n): Record<string, unknown> | undefined {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n addIssue(issues, path, `expected an object, received ${describeValue(value)}`);\n return undefined;\n }\n const record = value as Record<string, unknown>;\n const declared = new Set(declaredKeys);\n for (const key of Object.keys(record)) {\n if (!declared.has(key)) {\n addIssue(issues, atKey(path, key), 'unknown key; the record contract does not declare it');\n }\n }\n return record;\n}\n\n/**\n * Decode a map the contract leaves OPEN — `metadata`, `data`, a schema's `properties`.\n *\n * The key set is the author's, not the contract's, so an unrecognised key here is data rather than\n * drift. Only the VALUES are constrained.\n */\nexport function decodeOpenMap<TValue>(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n decodeValue: (value: unknown, path: string, issues: TDecodeIssues) => TValue | undefined,\n): Record<string, TValue> | undefined {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n addIssue(issues, path, `expected an object, received ${describeValue(value)}`);\n return undefined;\n }\n const decoded: Record<string, TValue> = {};\n for (const [key, member] of Object.entries(value as Record<string, unknown>)) {\n const element = decodeValue(member, atKey(path, key), issues);\n if (element !== undefined) decoded[key] = element;\n }\n return decoded;\n}\n\n/**\n * Decode a value on the universal payload axis.\n *\n * `TUniversalValue` admits `Date`, and inside an open map a persisted date is a string that cannot\n * be told from any other string. Reviving by shape would turn a user's date-like text into a `Date`,\n * so this decoder does not revive: through persistence, that member of the axis is unreachable. A\n * live `Date` handed in from memory is still accepted.\n */\nexport function decodeUniversalValue(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): TUniversalValue | undefined {\n if (value === null || value instanceof Date) return value;\n if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {\n return value;\n }\n if (Array.isArray(value)) {\n const decoded: TUniversalValue[] = [];\n value.forEach((item, index) => {\n const element = decodeUniversalValue(item, atIndex(path, index), issues);\n if (element !== undefined) decoded.push(element);\n });\n return decoded;\n }\n if (typeof value === 'object') {\n return decodeOpenMap(value, path, issues, decodeUniversalValue);\n }\n addIssue(issues, path, `expected a JSON-compatible value, received ${describeValue(value)}`);\n return undefined;\n}\n\nexport function decodeBackgroundPrimitive(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): TBackgroundPrimitive | undefined {\n if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {\n return value;\n }\n addIssue(issues, path, `expected a string, number or boolean, received ${describeValue(value)}`);\n return undefined;\n}\n","/**\n * TRANS-005 (#2081) — decoders for the conversation half of a persisted session record:\n * message parts, tool calls, the four `TUniversalMessage` variants, and history entries.\n *\n * The message variants are discriminated by `role`, and each variant declares its OWN key set — a\n * `toolCallId` on a user message is a defect, not a spare field, because only the tool variant\n * declares one.\n */\n\nimport { addIssue, atKey, describeValue, setOptional } from './decode-outcome.js';\nimport {\n decodeArray,\n decodeDate,\n decodeLiteral,\n decodeDeclaredObject,\n decodeOpenMap,\n decodeOptional,\n decodeString,\n decodeUniversalValue,\n} from './scalars.js';\n\nimport type { TDecodeIssues } from './decode-outcome.js';\nimport type {\n IHistoryEntry,\n ISystemMessage,\n IToolCall,\n IUserMessage,\n TMessageState,\n TUniversalMessage,\n TUniversalMessageMetadata,\n TUniversalMessagePart,\n} from '@robota-sdk/agent-core';\n\nconst MESSAGE_STATES = ['complete', 'interrupted'] as const satisfies readonly TMessageState[];\n\nconst BASE_MESSAGE_KEYS = ['id', 'timestamp', 'state', 'metadata', 'role', 'content', 'parts'];\n\nconst MESSAGE_KEYS_BY_ROLE: Record<TUniversalMessage['role'], readonly string[]> = {\n user: [...BASE_MESSAGE_KEYS, 'name'],\n assistant: [...BASE_MESSAGE_KEYS, 'toolCalls'],\n system: [...BASE_MESSAGE_KEYS, 'name'],\n tool: [...BASE_MESSAGE_KEYS, 'toolCallId', 'name'],\n};\n\n/**\n * A metadata value: the declared union, checked member by member.\n *\n * `string[]`, `number[]` and `Record<string, number>` are distinguished by their contents rather\n * than by a tag, so a mixed array is a defect — the union has no member for it.\n */\nfunction decodeMetadataValue(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): TUniversalMessageMetadata[string] | undefined {\n if (value instanceof Date) return value;\n if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {\n return value;\n }\n if (Array.isArray(value)) {\n if (value.every((item) => typeof item === 'string')) return value as string[];\n if (value.every((item) => typeof item === 'number')) return value as number[];\n addIssue(issues, path, 'expected an array of only strings or only numbers');\n return undefined;\n }\n if (typeof value === 'object' && value !== null) {\n const numbers: Record<string, number> = {};\n for (const [key, member] of Object.entries(value as Record<string, unknown>)) {\n if (typeof member !== 'number') {\n addIssue(issues, atKey(path, key), `expected a number, received ${describeValue(member)}`);\n continue;\n }\n numbers[key] = member;\n }\n return numbers;\n }\n addIssue(issues, path, `expected a metadata value, received ${describeValue(value)}`);\n return undefined;\n}\n\nfunction decodeMetadata(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): TUniversalMessageMetadata | undefined {\n return decodeOpenMap(value, path, issues, decodeMetadataValue);\n}\n\nfunction decodeMessagePart(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): TUniversalMessagePart | undefined {\n const kind = decodeLiteral(\n (value as { type?: unknown } | null)?.type,\n ['text', 'image_inline', 'image_uri'],\n atKey(path, 'type'),\n issues,\n );\n if (kind === undefined) return undefined;\n\n if (kind === 'text') {\n const raw = decodeDeclaredObject(value, path, issues, ['type', 'text']);\n if (raw === undefined) return undefined;\n const text = decodeString(raw['text'], atKey(path, 'text'), issues);\n return text === undefined ? undefined : { type: 'text', text };\n }\n\n if (kind === 'image_inline') {\n const raw = decodeDeclaredObject(value, path, issues, ['type', 'mimeType', 'data']);\n if (raw === undefined) return undefined;\n const mimeType = decodeString(raw['mimeType'], atKey(path, 'mimeType'), issues);\n const data = decodeString(raw['data'], atKey(path, 'data'), issues);\n if (mimeType === undefined || data === undefined) return undefined;\n return { type: 'image_inline', mimeType, data };\n }\n\n const raw = decodeDeclaredObject(value, path, issues, ['type', 'uri', 'mimeType']);\n if (raw === undefined) return undefined;\n const uri = decodeString(raw['uri'], atKey(path, 'uri'), issues);\n if (uri === undefined) return undefined;\n const part: TUniversalMessagePart = { type: 'image_uri', uri };\n setOptional(\n part,\n 'mimeType',\n decodeOptional(raw['mimeType'], atKey(path, 'mimeType'), issues, decodeString),\n );\n return part;\n}\n\nfunction decodeToolCall(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): IToolCall | undefined {\n const raw = decodeDeclaredObject(value, path, issues, ['id', 'type', 'function']);\n if (raw === undefined) return undefined;\n const id = decodeString(raw['id'], atKey(path, 'id'), issues);\n const type = decodeLiteral(raw['type'], ['function'], atKey(path, 'type'), issues);\n const fnPath = atKey(path, 'function');\n const fn = decodeDeclaredObject(raw['function'], fnPath, issues, ['name', 'arguments']);\n const name =\n fn === undefined ? undefined : decodeString(fn['name'], atKey(fnPath, 'name'), issues);\n const args =\n fn === undefined\n ? undefined\n : decodeString(fn['arguments'], atKey(fnPath, 'arguments'), issues);\n if (id === undefined || type === undefined || name === undefined || args === undefined) {\n return undefined;\n }\n return { id, type, function: { name, arguments: args } };\n}\n\n/** Decode the members every message variant shares, into a partial the variant completes. */\nfunction decodeMessageBase(\n raw: Record<string, unknown>,\n path: string,\n issues: TDecodeIssues,\n): { id: string; timestamp: Date; state: TMessageState } | undefined {\n const id = decodeString(raw['id'], atKey(path, 'id'), issues);\n const timestamp = decodeDate(raw['timestamp'], atKey(path, 'timestamp'), issues);\n const state = decodeLiteral(raw['state'], MESSAGE_STATES, atKey(path, 'state'), issues);\n if (id === undefined || timestamp === undefined || state === undefined) return undefined;\n return { id, timestamp, state };\n}\n\nfunction applySharedOptionalMembers(\n message: TUniversalMessage,\n raw: Record<string, unknown>,\n path: string,\n issues: TDecodeIssues,\n): void {\n setOptional(\n message,\n 'metadata',\n decodeOptional(raw['metadata'], atKey(path, 'metadata'), issues, decodeMetadata),\n );\n setOptional(\n message,\n 'parts',\n decodeOptional(raw['parts'], atKey(path, 'parts'), issues, (value, partsPath, sink) =>\n decodeArray(value, partsPath, sink, decodeMessagePart),\n ),\n );\n}\n\nexport function decodeMessage(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): TUniversalMessage | undefined {\n const role = decodeLiteral(\n (value as { role?: unknown } | null)?.role,\n ['user', 'assistant', 'system', 'tool'],\n atKey(path, 'role'),\n issues,\n );\n if (role === undefined) return undefined;\n\n const raw = decodeDeclaredObject(value, path, issues, MESSAGE_KEYS_BY_ROLE[role]);\n if (raw === undefined) return undefined;\n const base = decodeMessageBase(raw, path, issues);\n if (base === undefined) return undefined;\n const contentPath = atKey(path, 'content');\n\n if (role === 'assistant') {\n // The one variant whose content is nullable: an assistant turn that only calls tools has none.\n const content =\n raw['content'] === null ? null : decodeString(raw['content'], contentPath, issues);\n if (content === undefined) return undefined;\n const message: TUniversalMessage = { ...base, role, content };\n applySharedOptionalMembers(message, raw, path, issues);\n setOptional(\n message,\n 'toolCalls',\n decodeOptional(raw['toolCalls'], atKey(path, 'toolCalls'), issues, (value, callsPath, sink) =>\n decodeArray(value, callsPath, sink, decodeToolCall),\n ),\n );\n return message;\n }\n\n const content = decodeString(raw['content'], contentPath, issues);\n if (content === undefined) return undefined;\n\n if (role === 'tool') {\n const toolCallId = decodeString(raw['toolCallId'], atKey(path, 'toolCallId'), issues);\n if (toolCallId === undefined) return undefined;\n const message: TUniversalMessage = { ...base, role, content, toolCallId };\n applySharedOptionalMembers(message, raw, path, issues);\n setOptional(\n message,\n 'name',\n decodeOptional(raw['name'], atKey(path, 'name'), issues, decodeString),\n );\n return message;\n }\n\n // `role` is narrowed to `'user' | 'system'` here, and both variants declare `name` — the union of\n // the two is what makes that assignment checkable rather than a cast through the wider union.\n const message: IUserMessage | ISystemMessage = { ...base, role, content };\n applySharedOptionalMembers(message, raw, path, issues);\n setOptional(\n message,\n 'name',\n decodeOptional(raw['name'], atKey(path, 'name'), issues, decodeString),\n );\n return message;\n}\n\n/**\n * A history entry. `data` is declared `unknown` by the contract, so it is checked only for being a\n * JSON-compatible value — its key set belongs to whatever wrote the entry, not to this contract.\n */\nexport function decodeHistoryEntry(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): IHistoryEntry | undefined {\n const raw = decodeDeclaredObject(value, path, issues, [\n 'id',\n 'timestamp',\n 'category',\n 'type',\n 'data',\n ]);\n if (raw === undefined) return undefined;\n const id = decodeString(raw['id'], atKey(path, 'id'), issues);\n const timestamp = decodeDate(raw['timestamp'], atKey(path, 'timestamp'), issues);\n const category = decodeString(raw['category'], atKey(path, 'category'), issues);\n const type = decodeString(raw['type'], atKey(path, 'type'), issues);\n if (id === undefined || timestamp === undefined || category === undefined || type === undefined) {\n return undefined;\n }\n const entry: IHistoryEntry = { id, timestamp, category, type };\n setOptional(\n entry,\n 'data',\n decodeOptional(raw['data'], atKey(path, 'data'), issues, decodeUniversalValue),\n );\n return entry;\n}\n","/**\n * TRANS-005 (#2081) — the member contracts a background-task state is built from: the literal\n * unions it selects from, its error, its result (with token usage), and its schedule.\n *\n * They live beside the state decoder rather than inside it because the task EVENT union carries the\n * same members, and one decoder per contract is the property this codec exists to establish.\n */\n\nimport { addIssue, atKey, setOptional } from './decode-outcome.js';\nimport {\n decodeBackgroundPrimitive,\n decodeBoolean,\n decodeDeclaredObject,\n decodeInteger,\n decodeLiteral,\n decodeOpenMap,\n decodeOptional,\n decodeString,\n} from './scalars.js';\n\nimport type { TDecodeIssues } from './decode-outcome.js';\nimport type { ITokenUsage } from '@robota-sdk/agent-core';\nimport type {\n IAgentBackgroundTaskResult,\n IBackgroundTaskError,\n IBackgroundTaskResult,\n IBackgroundTaskSchedule,\n IProcessBackgroundTaskResult,\n IScheduledBackgroundTaskResult,\n IToolInvocationBackgroundTaskResult,\n TBackgroundPrimitive,\n TBackgroundTaskErrorCategory,\n TBackgroundTaskIsolation,\n TBackgroundTaskKind,\n TBackgroundTaskMode,\n TBackgroundTaskStatus,\n TBackgroundTaskTimeoutReason,\n} from '@robota-sdk/agent-interface-execution';\n\nexport const TASK_KINDS = [\n 'agent',\n 'process',\n 'scheduled',\n // Contained — DATA-010.\n 'tool-invocation',\n] as const satisfies readonly TBackgroundTaskKind[];\nexport const TASK_MODES = [\n 'foreground',\n 'background',\n] as const satisfies readonly TBackgroundTaskMode[];\nexport const TASK_ISOLATIONS = [\n 'none',\n 'worktree',\n] as const satisfies readonly TBackgroundTaskIsolation[];\nexport const TASK_STATUSES = [\n 'queued',\n 'running',\n 'waiting_permission',\n 'sleeping',\n 'paused',\n 'completed',\n 'failed',\n 'cancelled',\n] as const satisfies readonly TBackgroundTaskStatus[];\nexport const TASK_TIMEOUT_REASONS = [\n 'idle',\n 'max_runtime',\n 'output_limit',\n 'repetition',\n 'stale_worker',\n] as const satisfies readonly TBackgroundTaskTimeoutReason[];\nconst TASK_ERROR_CATEGORIES = [\n 'validation',\n 'capacity',\n 'permission',\n 'timeout',\n 'runner',\n 'crash',\n 'provider',\n 'process',\n] as const satisfies readonly TBackgroundTaskErrorCategory[];\n\nexport function decodePrimitiveMap(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): Record<string, TBackgroundPrimitive> | undefined {\n return decodeOpenMap(value, path, issues, decodeBackgroundPrimitive);\n}\n\nfunction decodeStringMap(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): Record<string, string> | undefined {\n return decodeOpenMap(value, path, issues, decodeString);\n}\n\nexport function decodeBackgroundTaskError(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): IBackgroundTaskError | undefined {\n const raw = decodeDeclaredObject(value, path, issues, ['category', 'message', 'recoverable']);\n if (raw === undefined) return undefined;\n const category = decodeLiteral(\n raw['category'],\n TASK_ERROR_CATEGORIES,\n atKey(path, 'category'),\n issues,\n );\n const message = decodeString(raw['message'], atKey(path, 'message'), issues);\n const recoverable = decodeBoolean(raw['recoverable'], atKey(path, 'recoverable'), issues);\n if (category === undefined || message === undefined || recoverable === undefined)\n return undefined;\n return { category, message, recoverable };\n}\n\nfunction decodeTokenUsage(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): ITokenUsage | undefined {\n const raw = decodeDeclaredObject(value, path, issues, [\n 'promptTokens',\n 'completionTokens',\n 'totalTokens',\n ]);\n if (raw === undefined) return undefined;\n const promptTokens = decodeInteger(raw['promptTokens'], atKey(path, 'promptTokens'), issues);\n const completionTokens = decodeInteger(\n raw['completionTokens'],\n atKey(path, 'completionTokens'),\n issues,\n );\n const totalTokens = decodeInteger(raw['totalTokens'], atKey(path, 'totalTokens'), issues);\n if (promptTokens === undefined || completionTokens === undefined || totalTokens === undefined) {\n return undefined;\n }\n return { promptTokens, completionTokens, totalTokens };\n}\n\nexport function decodeBackgroundTaskResult(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): IBackgroundTaskResult | undefined {\n const raw = decodeDeclaredObject(value, path, issues, [\n 'taskId',\n 'kind',\n 'output',\n 'exitCode',\n 'signalCode',\n 'metadata',\n 'usage',\n ]);\n if (raw === undefined) return undefined;\n const taskId = decodeString(raw['taskId'], atKey(path, 'taskId'), issues);\n const kind = decodeLiteral(raw['kind'], TASK_KINDS, atKey(path, 'kind'), issues);\n const output = decodeString(raw['output'], atKey(path, 'output'), issues);\n if (taskId === undefined || kind === undefined || output === undefined) return undefined;\n const exitCode = decodeOptional(raw['exitCode'], atKey(path, 'exitCode'), issues, decodeInteger);\n const signalCode = decodeOptional(\n raw['signalCode'],\n atKey(path, 'signalCode'),\n issues,\n decodeString,\n );\n const metadata = decodeOptional(\n raw['metadata'],\n atKey(path, 'metadata'),\n issues,\n decodePrimitiveMap,\n );\n const usage = decodeOptional(raw['usage'], atKey(path, 'usage'), issues, decodeTokenUsage);\n // #2079: `exitCode`/`signalCode` are process-only and `usage` is agent-only — a persisted result\n // carrying a field outside its own kind is corrupt, reported the same way the #3041 taskId/kind\n // identity check is: an issue at the offending field's own path, not a thrown error. The switch\n // below then builds only the fields that belong to the decoded `kind`, so a foreign field never\n // reaches the returned object even though it was read (and flagged) above.\n if (kind !== 'process' && exitCode !== undefined) {\n addIssue(issues, atKey(path, 'exitCode'), `must not be set for a '${kind}' result`);\n }\n if (kind !== 'process' && signalCode !== undefined) {\n addIssue(issues, atKey(path, 'signalCode'), `must not be set for a '${kind}' result`);\n }\n if (kind !== 'agent' && usage !== undefined) {\n addIssue(issues, atKey(path, 'usage'), `must not be set for a '${kind}' result`);\n }\n switch (kind) {\n case 'process': {\n const result: IProcessBackgroundTaskResult = { taskId, kind, output };\n setOptional(result, 'exitCode', exitCode);\n setOptional(result, 'signalCode', signalCode);\n setOptional(result, 'metadata', metadata);\n return result;\n }\n case 'agent': {\n const result: IAgentBackgroundTaskResult = { taskId, kind, output };\n setOptional(result, 'metadata', metadata);\n setOptional(result, 'usage', usage);\n return result;\n }\n case 'scheduled': {\n const result: IScheduledBackgroundTaskResult = { taskId, kind, output };\n setOptional(result, 'metadata', metadata);\n return result;\n }\n case 'tool-invocation': {\n const result: IToolInvocationBackgroundTaskResult = { taskId, kind, output };\n setOptional(result, 'metadata', metadata);\n return result;\n }\n }\n}\n\nexport function decodeBackgroundTaskSchedule(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): IBackgroundTaskSchedule | undefined {\n const raw = decodeDeclaredObject(value, path, issues, [\n 'cronExpression',\n 'agentInstruction',\n 'command',\n 'shell',\n 'env',\n ]);\n if (raw === undefined) return undefined;\n const cronExpression = decodeString(raw['cronExpression'], atKey(path, 'cronExpression'), issues);\n if (cronExpression === undefined) return undefined;\n const schedule: IBackgroundTaskSchedule = { cronExpression };\n for (const key of ['agentInstruction', 'command', 'shell'] as const) {\n setOptional(schedule, key, decodeOptional(raw[key], atKey(path, key), issues, decodeString));\n }\n setOptional(\n schedule,\n 'env',\n decodeOptional(raw['env'], atKey(path, 'env'), issues, decodeStringMap),\n );\n return schedule;\n}\n","/**\n * TRANS-005 (#2081) — the job-group half of the persisted background state: a group, the per-task\n * result envelopes it collects, and the three-variant group event union.\n *\n * A group's `results` are decoded even when the group is still `running`, because the array is\n * present from creation and a half-filled one is normal rather than a defect.\n */\n\nimport { TASK_STATUSES, decodeBackgroundTaskError } from './background-task-members.js';\nimport { atKey, setOptional } from './decode-outcome.js';\nimport {\n decodeArray,\n decodeDeclaredObject,\n decodeLiteral,\n decodeOptional,\n decodeString,\n decodeStringArray,\n decodeTimestampString,\n} from './scalars.js';\n\nimport type { TDecodeIssues } from './decode-outcome.js';\nimport type {\n IBackgroundJobGroupState,\n IBackgroundJobResultEnvelope,\n TBackgroundJobGroupEvent,\n TBackgroundJobGroupStatus,\n TBackgroundJobWaitPolicy,\n} from '@robota-sdk/agent-interface-execution';\n\nconst WAIT_POLICIES = [\n 'detached',\n 'wait_all',\n 'wait_any',\n 'manual',\n] as const satisfies readonly TBackgroundJobWaitPolicy[];\n\nconst GROUP_STATUSES = [\n 'running',\n 'completed',\n] as const satisfies readonly TBackgroundJobGroupStatus[];\n\nconst GROUP_EVENT_TYPES = [\n 'background_job_group_created',\n 'background_job_group_updated',\n 'background_job_group_completed',\n] as const;\n\nfunction decodeJobResultEnvelope(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): IBackgroundJobResultEnvelope | undefined {\n const raw = decodeDeclaredObject(value, path, issues, [\n 'taskId',\n 'label',\n 'status',\n 'summary',\n 'outputRef',\n 'error',\n 'startedAt',\n 'completedAt',\n ]);\n if (raw === undefined) return undefined;\n const taskId = decodeString(raw['taskId'], atKey(path, 'taskId'), issues);\n const label = decodeString(raw['label'], atKey(path, 'label'), issues);\n const status = decodeLiteral(raw['status'], TASK_STATUSES, atKey(path, 'status'), issues);\n if (taskId === undefined || label === undefined || status === undefined) return undefined;\n const envelope: IBackgroundJobResultEnvelope = { taskId, label, status };\n for (const key of ['summary', 'outputRef'] as const) {\n setOptional(envelope, key, decodeOptional(raw[key], atKey(path, key), issues, decodeString));\n }\n for (const key of ['startedAt', 'completedAt'] as const) {\n setOptional(\n envelope,\n key,\n decodeOptional(raw[key], atKey(path, key), issues, decodeTimestampString),\n );\n }\n setOptional(\n envelope,\n 'error',\n decodeOptional(raw['error'], atKey(path, 'error'), issues, decodeBackgroundTaskError),\n );\n return envelope;\n}\n\nexport function decodeJobGroupState(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): IBackgroundJobGroupState | undefined {\n const raw = decodeDeclaredObject(value, path, issues, [\n 'id',\n 'parentSessionId',\n 'waitPolicy',\n 'taskIds',\n 'status',\n 'createdAt',\n 'updatedAt',\n 'label',\n 'completedAt',\n 'results',\n ]);\n if (raw === undefined) return undefined;\n const id = decodeString(raw['id'], atKey(path, 'id'), issues);\n const parentSessionId = decodeString(\n raw['parentSessionId'],\n atKey(path, 'parentSessionId'),\n issues,\n );\n const waitPolicy = decodeLiteral(\n raw['waitPolicy'],\n WAIT_POLICIES,\n atKey(path, 'waitPolicy'),\n issues,\n );\n const taskIds = decodeStringArray(raw['taskIds'], atKey(path, 'taskIds'), issues);\n const status = decodeLiteral(raw['status'], GROUP_STATUSES, atKey(path, 'status'), issues);\n const createdAt = decodeTimestampString(raw['createdAt'], atKey(path, 'createdAt'), issues);\n const updatedAt = decodeTimestampString(raw['updatedAt'], atKey(path, 'updatedAt'), issues);\n const results = decodeArray(\n raw['results'],\n atKey(path, 'results'),\n issues,\n decodeJobResultEnvelope,\n );\n if (\n id === undefined ||\n parentSessionId === undefined ||\n waitPolicy === undefined ||\n taskIds === undefined ||\n status === undefined ||\n createdAt === undefined ||\n updatedAt === undefined ||\n results === undefined\n ) {\n return undefined;\n }\n const group: IBackgroundJobGroupState = {\n id,\n parentSessionId,\n waitPolicy,\n taskIds,\n status,\n createdAt,\n updatedAt,\n results,\n };\n setOptional(\n group,\n 'label',\n decodeOptional(raw['label'], atKey(path, 'label'), issues, decodeString),\n );\n setOptional(\n group,\n 'completedAt',\n decodeOptional(raw['completedAt'], atKey(path, 'completedAt'), issues, decodeTimestampString),\n );\n return group;\n}\n\nexport function decodeBackgroundJobGroupEvent(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): TBackgroundJobGroupEvent | undefined {\n const type = decodeLiteral(\n (value as { type?: unknown } | null)?.type,\n GROUP_EVENT_TYPES,\n atKey(path, 'type'),\n issues,\n );\n if (type === undefined) return undefined;\n const raw = decodeDeclaredObject(value, path, issues, ['type', 'group']);\n if (raw === undefined) return undefined;\n const group = decodeJobGroupState(raw['group'], atKey(path, 'group'), issues);\n return group === undefined ? undefined : { type, group };\n}\n","/**\n * TRANS-005 (#2081) — the background-task state decoder.\n *\n * The state carries thirteen optional plain-string members and four optional timestamp members.\n * They are decoded from key tables rather than written out one call at a time: a table is checked\n * against the contract by the key-parity test, whereas seventeen near-identical statements are\n * checked by whoever reads them.\n */\n\nimport {\n TASK_ISOLATIONS,\n TASK_KINDS,\n TASK_MODES,\n TASK_STATUSES,\n TASK_TIMEOUT_REASONS,\n decodeBackgroundTaskError,\n decodeBackgroundTaskResult,\n decodeBackgroundTaskSchedule,\n decodePrimitiveMap,\n} from './background-task-members.js';\nimport { addIssue, atKey, setOptional } from './decode-outcome.js';\nimport {\n decodeBoolean,\n decodeDeclaredObject,\n decodeInteger,\n decodeLiteral,\n decodeOptional,\n decodeString,\n decodeTimestampString,\n} from './scalars.js';\n\nimport type { TDecodeIssues } from './decode-outcome.js';\nimport type {\n IAgentBackgroundTaskState,\n IBackgroundTaskState,\n IProcessBackgroundTaskState,\n IScheduledBackgroundTaskState,\n IToolInvocationBackgroundTaskState,\n} from '@robota-sdk/agent-interface-execution';\n\n/**\n * #2079: the base members shared by every kind — identical across the four discriminated members,\n * so `IAgentBackgroundTaskState` (any one member would do) is the source for their shape here.\n */\ntype TBaseBackgroundTaskState = Pick<\n IAgentBackgroundTaskState,\n | 'id'\n | 'label'\n | 'status'\n | 'mode'\n | 'parentSessionId'\n | 'parentTaskId'\n | 'depth'\n | 'cwd'\n | 'pid'\n | 'startedAt'\n | 'updatedAt'\n | 'lastActivityAt'\n | 'completedAt'\n | 'currentAction'\n | 'unread'\n | 'error'\n | 'logPath'\n | 'transcriptPath'\n | 'timeoutReason'\n | 'metadata'\n>;\n\n/** Base string members every kind may carry (#2079: `logPath`/`transcriptPath`/`pid` are shared — see contracts). */\nconst BASE_OPTIONAL_STRING_KEYS = ['parentTaskId', 'currentAction', 'logPath', 'transcriptPath'] as const;\n/** #2079: agent-only string members — a value here on a non-agent task is a corrupt cross-kind field. */\nconst AGENT_OPTIONAL_STRING_KEYS = [\n 'agentType',\n 'resumeSessionId',\n 'promptPreview',\n 'worktreePath',\n 'branchName',\n 'worktreeStatus',\n 'worktreeNextAction',\n 'worktreeBaseRevision',\n 'parentWorktreeStatus',\n] as const;\n/** #2079: produced by every runner except the agent one (a process command, an MCP tool summary, a schedule's shell command / wake instruction). */\nconst NON_AGENT_OPTIONAL_STRING_KEYS = ['commandPreview'] as const;\n\nconst OPTIONAL_TIMESTAMP_KEYS = ['startedAt', 'lastActivityAt', 'completedAt'] as const;\n\nconst TASK_STATE_KEYS: readonly string[] = [\n 'id',\n 'kind',\n 'label',\n 'status',\n 'mode',\n 'parentSessionId',\n 'depth',\n 'cwd',\n 'pid',\n 'updatedAt',\n 'isolation',\n 'unread',\n 'result',\n 'error',\n 'timeoutReason',\n 'schedule',\n 'nextFireAt',\n 'metadata',\n ...BASE_OPTIONAL_STRING_KEYS,\n ...AGENT_OPTIONAL_STRING_KEYS,\n ...NON_AGENT_OPTIONAL_STRING_KEYS,\n ...OPTIONAL_TIMESTAMP_KEYS,\n];\n\n/**\n * TRANS-005 (#2081), discriminated by kind (#2079): the persisted state carries base members every\n * kind may set, plus a set of kind-exclusive members — agent-only (`agentType`, `isolation`,\n * `resumeSessionId`, `promptPreview`, the worktree-isolation fields), scheduled-only (`schedule`,\n * `nextFireAt`), or produced by every runner except the agent one (`commandPreview`). A value on a\n * field outside its own kind is reported as corrupt at that field's own path — exactly how\n * `decodeBackgroundTaskResult` (#2079) and the pre-existing `result.kind` check just below already\n * treat a cross-kind field — and the switch below then builds only the fields that belong to the\n * decoded `kind`, so a foreign field never reaches the returned object even though it was read (and\n * flagged) above.\n */\nexport function decodeBackgroundTaskState(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): IBackgroundTaskState | undefined {\n const raw = decodeDeclaredObject(value, path, issues, TASK_STATE_KEYS);\n if (raw === undefined) return undefined;\n const id = decodeString(raw['id'], atKey(path, 'id'), issues);\n const kind = decodeLiteral(raw['kind'], TASK_KINDS, atKey(path, 'kind'), issues);\n const label = decodeString(raw['label'], atKey(path, 'label'), issues);\n const status = decodeLiteral(raw['status'], TASK_STATUSES, atKey(path, 'status'), issues);\n const mode = decodeLiteral(raw['mode'], TASK_MODES, atKey(path, 'mode'), issues);\n const parentSessionId = decodeString(\n raw['parentSessionId'],\n atKey(path, 'parentSessionId'),\n issues,\n );\n const depth = decodeInteger(raw['depth'], atKey(path, 'depth'), issues);\n const cwd = decodeString(raw['cwd'], atKey(path, 'cwd'), issues);\n const updatedAt = decodeTimestampString(raw['updatedAt'], atKey(path, 'updatedAt'), issues);\n const unread = decodeBoolean(raw['unread'], atKey(path, 'unread'), issues);\n if (\n id === undefined ||\n kind === undefined ||\n label === undefined ||\n status === undefined ||\n mode === undefined ||\n parentSessionId === undefined ||\n depth === undefined ||\n cwd === undefined ||\n updatedAt === undefined ||\n unread === undefined\n ) {\n return undefined;\n }\n\n const base: TBaseBackgroundTaskState = {\n id,\n label,\n status,\n mode,\n parentSessionId,\n depth,\n cwd,\n updatedAt,\n unread,\n };\n for (const key of BASE_OPTIONAL_STRING_KEYS) {\n setOptional(base, key, decodeOptional(raw[key], atKey(path, key), issues, decodeString));\n }\n for (const key of OPTIONAL_TIMESTAMP_KEYS) {\n setOptional(base, key, decodeOptional(raw[key], atKey(path, key), issues, decodeTimestampString));\n }\n setOptional(base, 'pid', decodeOptional(raw['pid'], atKey(path, 'pid'), issues, decodeInteger));\n setOptional(\n base,\n 'timeoutReason',\n decodeOptional(\n raw['timeoutReason'],\n atKey(path, 'timeoutReason'),\n issues,\n (member, memberPath, sink) => decodeLiteral(member, TASK_TIMEOUT_REASONS, memberPath, sink),\n ),\n );\n setOptional(\n base,\n 'metadata',\n decodeOptional(raw['metadata'], atKey(path, 'metadata'), issues, decodePrimitiveMap),\n );\n setOptional(\n base,\n 'error',\n decodeOptional(raw['error'], atKey(path, 'error'), issues, decodeBackgroundTaskError),\n );\n\n const result = decodeOptional(raw['result'], atKey(path, 'result'), issues, decodeBackgroundTaskResult);\n if (result !== undefined) {\n if (result.taskId !== id) {\n addIssue(issues, atKey(atKey(path, 'result'), 'taskId'), 'must match the task ID');\n }\n if (result.kind !== kind) {\n addIssue(issues, atKey(atKey(path, 'result'), 'kind'), 'must match the task kind');\n }\n }\n\n const agentType = decodeOptional(raw['agentType'], atKey(path, 'agentType'), issues, decodeString);\n const resumeSessionId = decodeOptional(\n raw['resumeSessionId'],\n atKey(path, 'resumeSessionId'),\n issues,\n decodeString,\n );\n const promptPreview = decodeOptional(\n raw['promptPreview'],\n atKey(path, 'promptPreview'),\n issues,\n decodeString,\n );\n const isolation = decodeOptional(\n raw['isolation'],\n atKey(path, 'isolation'),\n issues,\n (member, memberPath, sink) => decodeLiteral(member, TASK_ISOLATIONS, memberPath, sink),\n );\n const worktreeFields: Record<string, string | undefined> = {};\n for (const key of [\n 'worktreePath',\n 'branchName',\n 'worktreeStatus',\n 'worktreeNextAction',\n 'worktreeBaseRevision',\n 'parentWorktreeStatus',\n ] as const) {\n worktreeFields[key] = decodeOptional(raw[key], atKey(path, key), issues, decodeString);\n }\n for (const key of AGENT_OPTIONAL_STRING_KEYS) {\n const decodedValue =\n key === 'agentType'\n ? agentType\n : key === 'resumeSessionId'\n ? resumeSessionId\n : key === 'promptPreview'\n ? promptPreview\n : worktreeFields[key];\n if (kind !== 'agent' && decodedValue !== undefined) {\n addIssue(issues, atKey(path, key), `must not be set for a '${kind}' task`);\n }\n }\n if (kind !== 'agent' && isolation !== undefined) {\n addIssue(issues, atKey(path, 'isolation'), `must not be set for a '${kind}' task`);\n }\n\n const commandPreview = decodeOptional(\n raw['commandPreview'],\n atKey(path, 'commandPreview'),\n issues,\n decodeString,\n );\n if (kind === 'agent' && commandPreview !== undefined) {\n addIssue(issues, atKey(path, 'commandPreview'), `must not be set for a '${kind}' task`);\n }\n\n const schedule = decodeOptional(\n raw['schedule'],\n atKey(path, 'schedule'),\n issues,\n decodeBackgroundTaskSchedule,\n );\n const nextFireAt = decodeOptional(\n raw['nextFireAt'],\n atKey(path, 'nextFireAt'),\n issues,\n decodeTimestampString,\n );\n if (kind !== 'scheduled' && schedule !== undefined) {\n addIssue(issues, atKey(path, 'schedule'), `must not be set for a '${kind}' task`);\n }\n if (kind !== 'scheduled' && nextFireAt !== undefined) {\n addIssue(issues, atKey(path, 'nextFireAt'), `must not be set for a '${kind}' task`);\n }\n\n switch (kind) {\n case 'agent': {\n const correlatedResult = result?.kind === 'agent' ? result : undefined;\n const task: IAgentBackgroundTaskState = { ...base, kind };\n setOptional(task, 'result', correlatedResult);\n setOptional(task, 'agentType', agentType);\n setOptional(task, 'resumeSessionId', resumeSessionId);\n setOptional(task, 'promptPreview', promptPreview);\n setOptional(task, 'isolation', isolation);\n setOptional(task, 'worktreePath', worktreeFields['worktreePath']);\n setOptional(task, 'branchName', worktreeFields['branchName']);\n setOptional(task, 'worktreeStatus', worktreeFields['worktreeStatus']);\n setOptional(task, 'worktreeNextAction', worktreeFields['worktreeNextAction']);\n setOptional(task, 'worktreeBaseRevision', worktreeFields['worktreeBaseRevision']);\n setOptional(task, 'parentWorktreeStatus', worktreeFields['parentWorktreeStatus']);\n return task;\n }\n case 'process': {\n const correlatedResult = result?.kind === 'process' ? result : undefined;\n const task: IProcessBackgroundTaskState = { ...base, kind };\n setOptional(task, 'result', correlatedResult);\n setOptional(task, 'commandPreview', commandPreview);\n return task;\n }\n case 'tool-invocation': {\n const correlatedResult = result?.kind === 'tool-invocation' ? result : undefined;\n const task: IToolInvocationBackgroundTaskState = { ...base, kind };\n setOptional(task, 'result', correlatedResult);\n setOptional(task, 'commandPreview', commandPreview);\n return task;\n }\n case 'scheduled': {\n const correlatedResult = result?.kind === 'scheduled' ? result : undefined;\n const task: IScheduledBackgroundTaskState = { ...base, kind };\n setOptional(task, 'result', correlatedResult);\n setOptional(task, 'commandPreview', commandPreview);\n setOptional(task, 'schedule', schedule);\n setOptional(task, 'nextFireAt', nextFireAt);\n return task;\n }\n }\n}\n","/**\n * TRANS-005 (#2081) — the twelve-variant persisted background-task event union.\n *\n * The union is discriminated by `type`, and the discriminant is read FIRST: it selects the key set\n * the variant declares, so an event that names the wrong members fails on the members rather than on\n * the union. An unrecognised `type` is reported once, at `type`, instead of as a pile of\n * missing-member issues from whichever variant happened to be tried.\n */\n\nimport { decodeBackgroundTaskState } from './background-task-decoders.js';\nimport { decodePrimitiveMap } from './background-task-members.js';\nimport { atKey, setOptional } from './decode-outcome.js';\nimport {\n decodeBoolean,\n decodeDeclaredObject,\n decodeLiteral,\n decodeOptional,\n decodeString,\n} from './scalars.js';\n\nimport type { TDecodeIssues } from './decode-outcome.js';\nimport type { TBackgroundTaskEvent } from '@robota-sdk/agent-interface-execution';\n\n/** The task-event variants whose entire payload is a task state. */\nconst TASK_CARRYING_EVENTS = [\n 'background_task_created',\n 'background_task_started',\n 'background_task_updated',\n 'background_task_completed',\n 'background_task_failed',\n 'background_task_cancelled',\n] as const;\n\nconst TASK_EVENT_TYPES = [\n ...TASK_CARRYING_EVENTS,\n 'background_task_text_delta',\n 'background_task_tool_start',\n 'background_task_tool_end',\n 'background_task_permission_request',\n 'background_task_closed',\n 'background_task_waking',\n] as const;\n\n/** Every variant but the six task-carrying ones names the task by id rather than by value. */\nfunction taskIdOf(\n raw: Record<string, unknown>,\n path: string,\n issues: TDecodeIssues,\n): string | undefined {\n return decodeString(raw['taskId'], atKey(path, 'taskId'), issues);\n}\n\ntype TTaskEventType = (typeof TASK_EVENT_TYPES)[number];\n\nfunction decodeTextDeltaEvent(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): TBackgroundTaskEvent | undefined {\n const raw = decodeDeclaredObject(value, path, issues, ['type', 'taskId', 'delta']);\n if (raw === undefined) return undefined;\n const taskId = taskIdOf(raw, path, issues);\n const delta = decodeString(raw['delta'], atKey(path, 'delta'), issues);\n if (taskId === undefined || delta === undefined) return undefined;\n return { type: 'background_task_text_delta', taskId, delta };\n}\n\nfunction decodeToolStartEvent(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): TBackgroundTaskEvent | undefined {\n const raw = decodeDeclaredObject(value, path, issues, ['type', 'taskId', 'toolName', 'firstArg']);\n if (raw === undefined) return undefined;\n const taskId = taskIdOf(raw, path, issues);\n const toolName = decodeString(raw['toolName'], atKey(path, 'toolName'), issues);\n if (taskId === undefined || toolName === undefined) return undefined;\n const event: TBackgroundTaskEvent = { type: 'background_task_tool_start', taskId, toolName };\n setOptional(\n event,\n 'firstArg',\n decodeOptional(raw['firstArg'], atKey(path, 'firstArg'), issues, decodeString),\n );\n return event;\n}\n\nfunction decodeToolEndEvent(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): TBackgroundTaskEvent | undefined {\n const raw = decodeDeclaredObject(value, path, issues, [\n 'type',\n 'taskId',\n 'toolName',\n 'success',\n 'error',\n ]);\n if (raw === undefined) return undefined;\n const taskId = taskIdOf(raw, path, issues);\n const toolName = decodeString(raw['toolName'], atKey(path, 'toolName'), issues);\n const success = decodeBoolean(raw['success'], atKey(path, 'success'), issues);\n if (taskId === undefined || toolName === undefined || success === undefined) return undefined;\n const event: TBackgroundTaskEvent = {\n type: 'background_task_tool_end',\n taskId,\n toolName,\n success,\n };\n setOptional(\n event,\n 'error',\n decodeOptional(raw['error'], atKey(path, 'error'), issues, decodeString),\n );\n return event;\n}\n\nfunction decodePermissionRequestEvent(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): TBackgroundTaskEvent | undefined {\n const raw = decodeDeclaredObject(value, path, issues, [\n 'type',\n 'taskId',\n 'requestId',\n 'toolName',\n 'toolArgs',\n ]);\n if (raw === undefined) return undefined;\n const taskId = taskIdOf(raw, path, issues);\n const requestId = decodeString(raw['requestId'], atKey(path, 'requestId'), issues);\n const toolName = decodeString(raw['toolName'], atKey(path, 'toolName'), issues);\n const toolArgs = decodePrimitiveMap(raw['toolArgs'], atKey(path, 'toolArgs'), issues);\n if (\n taskId === undefined ||\n requestId === undefined ||\n toolName === undefined ||\n toolArgs === undefined\n ) {\n return undefined;\n }\n return { type: 'background_task_permission_request', taskId, requestId, toolName, toolArgs };\n}\n\nfunction decodeClosedEvent(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): TBackgroundTaskEvent | undefined {\n const raw = decodeDeclaredObject(value, path, issues, ['type', 'taskId']);\n if (raw === undefined) return undefined;\n const taskId = taskIdOf(raw, path, issues);\n return taskId === undefined ? undefined : { type: 'background_task_closed', taskId };\n}\n\nfunction decodeWakingEvent(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): TBackgroundTaskEvent | undefined {\n const raw = decodeDeclaredObject(value, path, issues, ['type', 'taskId', 'instruction']);\n if (raw === undefined) return undefined;\n const taskId = taskIdOf(raw, path, issues);\n if (taskId === undefined) return undefined;\n const event: TBackgroundTaskEvent = { type: 'background_task_waking', taskId };\n setOptional(\n event,\n 'instruction',\n decodeOptional(raw['instruction'], atKey(path, 'instruction'), issues, decodeString),\n );\n return event;\n}\n\n/** A task-carrying variant: `{ type, task }`, where `task` is a whole task state. */\nfunction decodeTaskCarryingEvent(\n type: TTaskEventType,\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): TBackgroundTaskEvent | undefined {\n const raw = decodeDeclaredObject(value, path, issues, ['type', 'task']);\n if (raw === undefined) return undefined;\n const task = decodeBackgroundTaskState(raw['task'], atKey(path, 'task'), issues);\n if (task === undefined) return undefined;\n return { type, task } as TBackgroundTaskEvent;\n}\n\n/** The variant decoders, keyed by discriminant — the dispatcher stays a lookup, not a ladder. */\nconst VARIANT_DECODERS: Partial<\n Record<\n TTaskEventType,\n (value: unknown, path: string, issues: TDecodeIssues) => TBackgroundTaskEvent | undefined\n >\n> = {\n background_task_text_delta: decodeTextDeltaEvent,\n background_task_tool_start: decodeToolStartEvent,\n background_task_tool_end: decodeToolEndEvent,\n background_task_permission_request: decodePermissionRequestEvent,\n background_task_closed: decodeClosedEvent,\n background_task_waking: decodeWakingEvent,\n};\n\nexport function decodeBackgroundTaskEvent(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): TBackgroundTaskEvent | undefined {\n const type = decodeLiteral(\n (value as { type?: unknown } | null)?.type,\n TASK_EVENT_TYPES,\n atKey(path, 'type'),\n issues,\n );\n if (type === undefined) return undefined;\n const decodeVariant = VARIANT_DECODERS[type];\n return decodeVariant === undefined\n ? decodeTaskCarryingEvent(type, value, path, issues)\n : decodeVariant(value, path, issues);\n}\n","/**\n * TRANS-005 (#2081) — decoders for the session-event payloads a record persists: skill activation,\n * automatic-memory events and their references, and context references.\n *\n * These contracts spell their instants as `string`, not `Date`, so they stay strings here — but they\n * are still checked as instants, because a member that reads as a timestamp and cannot be parsed as\n * one is a defect that only surfaces later, as an ordering that is quietly wrong.\n */\n\nimport { atKey, setOptional } from './decode-outcome.js';\nimport {\n decodeBoolean,\n decodeDeclaredObject,\n decodeInteger,\n decodeLiteral,\n decodeNumber,\n decodeOpenMap,\n decodeOptional,\n decodeString,\n decodeTimestampString,\n decodeUniversalValue,\n} from './scalars.js';\n\nimport type { TDecodeIssues } from './decode-outcome.js';\nimport type {\n IContextReferenceItem,\n IMemoryEvent,\n IMemoryReference,\n ISkillActivationEvent,\n TContextReferenceLoadType,\n TContextReferenceStatus,\n TSkillActivationInvocation,\n TSkillActivationMode,\n TSkillActivationSource,\n TSkillActivationStatus,\n} from '@robota-sdk/agent-interface-session';\n\nconst SKILL_SOURCES = ['skill', 'plugin'] as const satisfies readonly TSkillActivationSource[];\nconst SKILL_INVOCATIONS = [\n 'user-slash',\n 'model-tool',\n] as const satisfies readonly TSkillActivationInvocation[];\nconst SKILL_MODES = ['inject', 'fork'] as const satisfies readonly TSkillActivationMode[];\nconst SKILL_STATUSES = [\n 'started',\n 'completed',\n 'failed',\n] as const satisfies readonly TSkillActivationStatus[];\n\nconst MEMORY_EVENT_TYPES = [\n 'memory_candidate_extracted',\n 'memory_candidate_queued',\n 'memory_candidate_saved',\n 'memory_candidate_skipped',\n 'memory_candidate_approved',\n 'memory_candidate_rejected',\n 'memory_retrieved',\n] as const satisfies readonly IMemoryEvent['type'][];\n\nconst CONTEXT_LOAD_TYPES = [\n 'manual',\n 'prompt-reference',\n 'system',\n] as const satisfies readonly TContextReferenceLoadType[];\n\nconst CONTEXT_STATUSES = [\n 'active',\n 'observed',\n] as const satisfies readonly TContextReferenceStatus[];\n\nexport function decodeSkillActivationEvent(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): ISkillActivationEvent | undefined {\n const raw = decodeDeclaredObject(value, path, issues, [\n 'type',\n 'skillName',\n 'source',\n 'invocation',\n 'mode',\n 'status',\n 'timestamp',\n 'qualifiedName',\n 'error',\n ]);\n if (raw === undefined) return undefined;\n const type = decodeLiteral(raw['type'], ['skill-activation'], atKey(path, 'type'), issues);\n const skillName = decodeString(raw['skillName'], atKey(path, 'skillName'), issues);\n const source = decodeLiteral(raw['source'], SKILL_SOURCES, atKey(path, 'source'), issues);\n const invocation = decodeLiteral(\n raw['invocation'],\n SKILL_INVOCATIONS,\n atKey(path, 'invocation'),\n issues,\n );\n const mode = decodeLiteral(raw['mode'], SKILL_MODES, atKey(path, 'mode'), issues);\n const status = decodeLiteral(raw['status'], SKILL_STATUSES, atKey(path, 'status'), issues);\n const timestamp = decodeTimestampString(raw['timestamp'], atKey(path, 'timestamp'), issues);\n if (\n type === undefined ||\n skillName === undefined ||\n source === undefined ||\n invocation === undefined ||\n mode === undefined ||\n status === undefined ||\n timestamp === undefined\n ) {\n return undefined;\n }\n const qualifiedName = decodeOptional(\n raw['qualifiedName'],\n atKey(path, 'qualifiedName'),\n issues,\n decodeString,\n );\n const error = decodeOptional(raw['error'], atKey(path, 'error'), issues, decodeString);\n // Every member is `readonly`, so the optional ones are spread in rather than assigned after.\n return {\n type,\n skillName,\n source,\n invocation,\n mode,\n status,\n timestamp,\n ...(qualifiedName === undefined ? {} : { qualifiedName }),\n ...(error === undefined ? {} : { error }),\n };\n}\n\nexport function decodeMemoryReference(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): IMemoryReference | undefined {\n const raw = decodeDeclaredObject(value, path, issues, ['topic', 'path', 'score', 'truncated']);\n if (raw === undefined) return undefined;\n const topic = decodeString(raw['topic'], atKey(path, 'topic'), issues);\n const referencePath = decodeString(raw['path'], atKey(path, 'path'), issues);\n const score = decodeNumber(raw['score'], atKey(path, 'score'), issues);\n const truncated = decodeBoolean(raw['truncated'], atKey(path, 'truncated'), issues);\n if (\n topic === undefined ||\n referencePath === undefined ||\n score === undefined ||\n truncated === undefined\n ) {\n return undefined;\n }\n return { topic, path: referencePath, score, truncated };\n}\n\nexport function decodeMemoryEvent(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): IMemoryEvent | undefined {\n const raw = decodeDeclaredObject(value, path, issues, [\n 'type',\n 'at',\n 'candidateId',\n 'topic',\n 'reason',\n 'data',\n ]);\n if (raw === undefined) return undefined;\n const type = decodeLiteral(raw['type'], MEMORY_EVENT_TYPES, atKey(path, 'type'), issues);\n const at = decodeTimestampString(raw['at'], atKey(path, 'at'), issues);\n if (type === undefined || at === undefined) return undefined;\n const event: IMemoryEvent = { type, at };\n for (const key of ['candidateId', 'topic', 'reason'] as const) {\n setOptional(event, key, decodeOptional(raw[key], atKey(path, key), issues, decodeString));\n }\n setOptional(\n event,\n 'data',\n decodeOptional(raw['data'], atKey(path, 'data'), issues, (member, memberPath, sink) =>\n decodeOpenMap(member, memberPath, sink, decodeUniversalValue),\n ),\n );\n return event;\n}\n\nexport function decodeContextReferenceItem(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): IContextReferenceItem | undefined {\n const raw = decodeDeclaredObject(value, path, issues, [\n 'id',\n 'sourcePath',\n 'relativePath',\n 'originalReference',\n 'loadType',\n 'status',\n 'byteLength',\n 'loadedAt',\n 'lastUsedAt',\n ]);\n if (raw === undefined) return undefined;\n const id = decodeString(raw['id'], atKey(path, 'id'), issues);\n const sourcePath = decodeString(raw['sourcePath'], atKey(path, 'sourcePath'), issues);\n const relativePath = decodeString(raw['relativePath'], atKey(path, 'relativePath'), issues);\n const originalReference = decodeString(\n raw['originalReference'],\n atKey(path, 'originalReference'),\n issues,\n );\n const loadType = decodeLiteral(\n raw['loadType'],\n CONTEXT_LOAD_TYPES,\n atKey(path, 'loadType'),\n issues,\n );\n const status = decodeLiteral(raw['status'], CONTEXT_STATUSES, atKey(path, 'status'), issues);\n const byteLength = decodeInteger(raw['byteLength'], atKey(path, 'byteLength'), issues);\n const loadedAt = decodeTimestampString(raw['loadedAt'], atKey(path, 'loadedAt'), issues);\n if (\n id === undefined ||\n sourcePath === undefined ||\n relativePath === undefined ||\n originalReference === undefined ||\n loadType === undefined ||\n status === undefined ||\n byteLength === undefined ||\n loadedAt === undefined\n ) {\n return undefined;\n }\n const item: IContextReferenceItem = {\n id,\n sourcePath,\n relativePath,\n originalReference,\n loadType,\n status,\n byteLength,\n loadedAt,\n };\n setOptional(\n item,\n 'lastUsedAt',\n decodeOptional(raw['lastUsedAt'], atKey(path, 'lastUsedAt'), issues, decodeTimestampString),\n );\n return item;\n}\n","/**\n * TRANS-005 (#2081) — decoders for the three in-flight artifacts a session record persists so they\n * survive a resume: the autonomous goal, the plan artifact, and the active-branch pointer.\n *\n * The branch pointer is pure data by design — it names a `branchId`/`checkpointId` that live in a\n * separate manifest store, and a pointer into a manifest that no longer holds them is a RESUME\n * concern, not a decode one. This decoder therefore validates the pointer's shape and says nothing\n * about whether it resolves.\n */\n\nimport { atKey, setOptional } from './decode-outcome.js';\nimport {\n decodeArray,\n decodeDeclaredObject,\n decodeInteger,\n decodeLiteral,\n decodeOptional,\n decodeString,\n decodeTimestampString,\n} from './scalars.js';\n\nimport type { TDecodeIssues } from './decode-outcome.js';\nimport type {\n IActiveBranchPointer,\n IGoalProgressEntry,\n IGoalState,\n IPlanArtifact,\n IPlanStep,\n TGoalStatus,\n TGoalStopReason,\n TPlanPhase,\n TPlanStepStatus,\n} from '@robota-sdk/agent-interface-session';\n\nconst GOAL_STATUSES = ['active', 'satisfied', 'stopped'] as const satisfies readonly TGoalStatus[];\n\nconst GOAL_STOP_REASONS = [\n 'satisfied',\n 'max-iterations',\n 'cancelled',\n 'no-progress',\n] as const satisfies readonly TGoalStopReason[];\n\nconst GOAL_SIGNALS = [\n 'continue',\n 'satisfied',\n] as const satisfies readonly IGoalProgressEntry['signal'][];\n\nconst PLAN_STEP_STATUSES = [\n 'pending',\n 'in-progress',\n 'done',\n] as const satisfies readonly TPlanStepStatus[];\n\nconst PLAN_PHASES = [\n 'planning',\n 'awaiting-approval',\n 'executing',\n 'completed',\n] as const satisfies readonly TPlanPhase[];\n\nfunction decodeGoalProgressEntry(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): IGoalProgressEntry | undefined {\n const raw = decodeDeclaredObject(value, path, issues, ['iteration', 'signal', 'reason']);\n if (raw === undefined) return undefined;\n const iteration = decodeInteger(raw['iteration'], atKey(path, 'iteration'), issues);\n const signal = decodeLiteral(raw['signal'], GOAL_SIGNALS, atKey(path, 'signal'), issues);\n const reason = decodeString(raw['reason'], atKey(path, 'reason'), issues);\n if (iteration === undefined || signal === undefined || reason === undefined) return undefined;\n return { iteration, signal, reason };\n}\n\nexport function decodeGoalState(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): IGoalState | undefined {\n const raw = decodeDeclaredObject(value, path, issues, [\n 'id',\n 'objective',\n 'status',\n 'stopReason',\n 'iterations',\n 'maxIterations',\n 'startedAt',\n 'progress',\n ]);\n if (raw === undefined) return undefined;\n const id = decodeString(raw['id'], atKey(path, 'id'), issues);\n const objective = decodeString(raw['objective'], atKey(path, 'objective'), issues);\n const status = decodeLiteral(raw['status'], GOAL_STATUSES, atKey(path, 'status'), issues);\n const iterations = decodeInteger(raw['iterations'], atKey(path, 'iterations'), issues);\n const maxIterations = decodeInteger(raw['maxIterations'], atKey(path, 'maxIterations'), issues);\n const startedAt = decodeTimestampString(raw['startedAt'], atKey(path, 'startedAt'), issues);\n const progress = decodeArray(\n raw['progress'],\n atKey(path, 'progress'),\n issues,\n decodeGoalProgressEntry,\n );\n if (\n id === undefined ||\n objective === undefined ||\n status === undefined ||\n iterations === undefined ||\n maxIterations === undefined ||\n startedAt === undefined ||\n progress === undefined\n ) {\n return undefined;\n }\n const goal: IGoalState = {\n id,\n objective,\n status,\n iterations,\n maxIterations,\n startedAt,\n progress,\n };\n setOptional(\n goal,\n 'stopReason',\n decodeOptional(\n raw['stopReason'],\n atKey(path, 'stopReason'),\n issues,\n (member, memberPath, sink) => decodeLiteral(member, GOAL_STOP_REASONS, memberPath, sink),\n ),\n );\n return goal;\n}\n\nfunction decodePlanStep(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): IPlanStep | undefined {\n const raw = decodeDeclaredObject(value, path, issues, ['id', 'description', 'status']);\n if (raw === undefined) return undefined;\n const id = decodeString(raw['id'], atKey(path, 'id'), issues);\n const description = decodeString(raw['description'], atKey(path, 'description'), issues);\n const status = decodeLiteral(raw['status'], PLAN_STEP_STATUSES, atKey(path, 'status'), issues);\n if (id === undefined || description === undefined || status === undefined) return undefined;\n return { id, description, status };\n}\n\nexport function decodePlanArtifact(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): IPlanArtifact | undefined {\n const raw = decodeDeclaredObject(value, path, issues, [\n 'id',\n 'objective',\n 'steps',\n 'phase',\n 'createdAt',\n 'approvedAt',\n ]);\n if (raw === undefined) return undefined;\n const id = decodeString(raw['id'], atKey(path, 'id'), issues);\n const objective = decodeString(raw['objective'], atKey(path, 'objective'), issues);\n const steps = decodeArray(raw['steps'], atKey(path, 'steps'), issues, decodePlanStep);\n const phase = decodeLiteral(raw['phase'], PLAN_PHASES, atKey(path, 'phase'), issues);\n const createdAt = decodeTimestampString(raw['createdAt'], atKey(path, 'createdAt'), issues);\n if (\n id === undefined ||\n objective === undefined ||\n steps === undefined ||\n phase === undefined ||\n createdAt === undefined\n ) {\n return undefined;\n }\n const plan: IPlanArtifact = { id, objective, steps, phase, createdAt };\n setOptional(\n plan,\n 'approvedAt',\n decodeOptional(raw['approvedAt'], atKey(path, 'approvedAt'), issues, decodeTimestampString),\n );\n return plan;\n}\n\nexport function decodeActiveBranchPointer(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): IActiveBranchPointer | undefined {\n const raw = decodeDeclaredObject(value, path, issues, ['branchId', 'checkpointId']);\n if (raw === undefined) return undefined;\n const branchId = decodeString(raw['branchId'], atKey(path, 'branchId'), issues);\n const checkpointId = decodeString(raw['checkpointId'], atKey(path, 'checkpointId'), issues);\n if (branchId === undefined || checkpointId === undefined) return undefined;\n return { branchId, checkpointId };\n}\n","/** Total decoding for session-owned self-paced loops. */\n\nimport { addIssue, atKey, setOptional } from './decode-outcome.js';\nimport {\n decodeBoolean,\n decodeDeclaredObject,\n decodeInteger,\n decodeLiteral,\n decodeOptional,\n decodeString,\n decodeTimestampString,\n} from './scalars.js';\n\nimport type { TDecodeIssues } from './decode-outcome.js';\nimport type { ISessionLoopState, TSessionLoopPhase } from '@robota-sdk/agent-interface-session';\n\nconst PHASES: readonly TSessionLoopPhase[] = [\n 'waiting',\n 'pending',\n 'running',\n 'stopped',\n 'expired',\n];\n\nconst KEYS = [\n 'loopId',\n 'instruction',\n 'useDefaultPrompt',\n 'createdAt',\n 'expiresAt',\n 'revision',\n 'generation',\n 'phase',\n 'nextAllowedAt',\n 'delaySeconds',\n 'reason',\n 'fallbackUsed',\n 'terminalReason',\n] as const;\n\nexport function decodeSessionLoopState(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): ISessionLoopState | undefined {\n const raw = decodeDeclaredObject(value, path, issues, KEYS);\n if (!raw) return undefined;\n const loopId = decodeString(raw['loopId'], atKey(path, 'loopId'), issues);\n const instruction = decodeString(raw['instruction'], atKey(path, 'instruction'), issues);\n const useDefaultPrompt = decodeOptional(\n raw['useDefaultPrompt'], atKey(path, 'useDefaultPrompt'), issues, decodeBoolean,\n );\n const createdAt = decodeTimestampString(raw['createdAt'], atKey(path, 'createdAt'), issues);\n const expiresAt = decodeTimestampString(raw['expiresAt'], atKey(path, 'expiresAt'), issues);\n const revision = decodeInteger(raw['revision'], atKey(path, 'revision'), issues);\n const generation = decodeInteger(raw['generation'], atKey(path, 'generation'), issues);\n const phase = decodeLiteral(raw['phase'], PHASES, atKey(path, 'phase'), issues);\n const fallbackUsed = decodeBoolean(raw['fallbackUsed'], atKey(path, 'fallbackUsed'), issues);\n const nextAllowedAt = decodeOptional(\n raw['nextAllowedAt'],\n atKey(path, 'nextAllowedAt'),\n issues,\n decodeTimestampString,\n );\n const delaySeconds = decodeOptional(\n raw['delaySeconds'],\n atKey(path, 'delaySeconds'),\n issues,\n decodeInteger,\n );\n const reason = decodeOptional(raw['reason'], atKey(path, 'reason'), issues, decodeString);\n const terminalReason = decodeOptional(\n raw['terminalReason'],\n atKey(path, 'terminalReason'),\n issues,\n decodeString,\n );\n\n if (revision !== undefined && (!Number.isSafeInteger(revision) || revision < 0)) {\n addIssue(issues, atKey(path, 'revision'), 'expected a non-negative safe integer');\n }\n if (generation !== undefined && (!Number.isSafeInteger(generation) || generation < 0)) {\n addIssue(issues, atKey(path, 'generation'), 'expected a non-negative safe integer');\n }\n if (delaySeconds !== undefined && (delaySeconds < 60 || delaySeconds > 3600)) {\n addIssue(issues, atKey(path, 'delaySeconds'), 'expected a delay between 60 and 3600 seconds');\n }\n if (loopId !== undefined && !loopId.trim()) {\n addIssue(issues, atKey(path, 'loopId'), 'expected a non-empty loop ID');\n }\n if (instruction !== undefined && !instruction.trim()) {\n addIssue(issues, atKey(path, 'instruction'), 'expected a non-empty instruction');\n }\n if (phase === 'waiting' && nextAllowedAt === undefined && raw['nextAllowedAt'] === undefined) {\n addIssue(issues, atKey(path, 'nextAllowedAt'), 'waiting loop needs a next wake time');\n }\n if (\n loopId === undefined ||\n instruction === undefined ||\n createdAt === undefined ||\n expiresAt === undefined ||\n revision === undefined ||\n generation === undefined ||\n phase === undefined ||\n fallbackUsed === undefined\n ) {\n return undefined;\n }\n const state: ISessionLoopState = {\n loopId,\n instruction,\n createdAt,\n expiresAt,\n revision,\n generation,\n phase,\n fallbackUsed,\n };\n setOptional(state, 'useDefaultPrompt', useDefaultPrompt);\n setOptional(state, 'nextAllowedAt', nextAllowedAt);\n setOptional(state, 'delaySeconds', delaySeconds);\n setOptional(state, 'reason', reason);\n setOptional(state, 'terminalReason', terminalReason);\n return state;\n}\n","/**\n * TRANS-005 (#2081) — decoders for the tool schemas a session record persists.\n *\n * `IParameterSchema` is the universal JSON-schema subset, and it is recursive: `items`, `properties`\n * and `anyOf` all carry the same node type. `properties` is an OPEN map — its keys are the tool\n * author's parameter names, not contract members — while every node's own member set is declared.\n */\n\nimport { addIssue, atKey, describeValue, setOptional } from './decode-outcome.js';\nimport {\n decodeArray,\n decodeDeclaredObject,\n decodeLiteral,\n decodeNumber,\n decodeOpenMap,\n decodeOptional,\n decodeString,\n decodeStringArray,\n} from './scalars.js';\n\nimport type { TDecodeIssues } from './decode-outcome.js';\nimport type {\n IObjectParameterSchema,\n IParameterSchema,\n IToolSchema,\n TJSONSchemaEnum,\n TJSONSchemaKind,\n TParameterDefaultValue,\n} from '@robota-sdk/agent-core';\n\nconst SCHEMA_KINDS = [\n 'string',\n 'number',\n 'integer',\n 'boolean',\n 'array',\n 'object',\n 'null',\n] as const satisfies readonly TJSONSchemaKind[];\n\nconst PARAMETER_SCHEMA_KEYS = [\n 'type',\n 'description',\n 'enum',\n 'items',\n 'properties',\n 'required',\n 'anyOf',\n 'additionalProperties',\n 'minimum',\n 'maximum',\n 'pattern',\n 'format',\n 'default',\n];\n\nfunction decodeEnumMember(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): string | number | boolean | undefined {\n if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {\n return value;\n }\n addIssue(issues, path, `expected a string, number or boolean, received ${describeValue(value)}`);\n return undefined;\n}\n\nfunction decodeDefaultValue(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): TParameterDefaultValue | undefined {\n if (value === null) return null;\n return decodeEnumMember(value, path, issues);\n}\n\n/**\n * `additionalProperties` is either a closure flag or a schema for the properties not named — the\n * two are told apart by JavaScript type, which is how JSON Schema itself spells it.\n */\nfunction decodeAdditionalProperties(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): boolean | IParameterSchema | undefined {\n if (typeof value === 'boolean') return value;\n return decodeParameterSchema(value, path, issues);\n}\n\nfunction decodeParameterSchema(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): IParameterSchema | undefined {\n const raw = decodeDeclaredObject(value, path, issues, PARAMETER_SCHEMA_KEYS);\n if (raw === undefined) return undefined;\n\n const schema: IParameterSchema = {};\n setOptional(\n schema,\n 'type',\n decodeOptional(raw['type'], atKey(path, 'type'), issues, (member, memberPath, sink) =>\n decodeLiteral(member, SCHEMA_KINDS, memberPath, sink),\n ),\n );\n setOptional(\n schema,\n 'description',\n decodeOptional(raw['description'], atKey(path, 'description'), issues, decodeString),\n );\n setOptional(\n schema,\n 'enum',\n decodeOptional(raw['enum'], atKey(path, 'enum'), issues, (member, memberPath, sink) => {\n const decoded = decodeArray(member, memberPath, sink, decodeEnumMember);\n return decoded as TJSONSchemaEnum | undefined;\n }),\n );\n setOptional(\n schema,\n 'items',\n decodeOptional(raw['items'], atKey(path, 'items'), issues, decodeParameterSchema),\n );\n setOptional(\n schema,\n 'properties',\n decodeOptional(\n raw['properties'],\n atKey(path, 'properties'),\n issues,\n (member, memberPath, sink) => decodeOpenMap(member, memberPath, sink, decodeParameterSchema),\n ),\n );\n setOptional(\n schema,\n 'required',\n decodeOptional(raw['required'], atKey(path, 'required'), issues, decodeStringArray),\n );\n setOptional(\n schema,\n 'anyOf',\n decodeOptional(raw['anyOf'], atKey(path, 'anyOf'), issues, (member, memberPath, sink) =>\n decodeArray(member, memberPath, sink, decodeParameterSchema),\n ),\n );\n setOptional(\n schema,\n 'additionalProperties',\n decodeOptional(\n raw['additionalProperties'],\n atKey(path, 'additionalProperties'),\n issues,\n decodeAdditionalProperties,\n ),\n );\n setOptional(\n schema,\n 'minimum',\n decodeOptional(raw['minimum'], atKey(path, 'minimum'), issues, decodeNumber),\n );\n setOptional(\n schema,\n 'maximum',\n decodeOptional(raw['maximum'], atKey(path, 'maximum'), issues, decodeNumber),\n );\n setOptional(\n schema,\n 'pattern',\n decodeOptional(raw['pattern'], atKey(path, 'pattern'), issues, decodeString),\n );\n setOptional(\n schema,\n 'format',\n decodeOptional(raw['format'], atKey(path, 'format'), issues, decodeString),\n );\n setOptional(\n schema,\n 'default',\n decodeOptional(raw['default'], atKey(path, 'default'), issues, decodeDefaultValue),\n );\n return schema;\n}\n\n/** The root of a tool's parameters: an object node that NAMES its properties. */\nfunction decodeObjectParameterSchema(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): IObjectParameterSchema | undefined {\n const schema = decodeParameterSchema(value, path, issues);\n if (schema === undefined) return undefined;\n if (schema.type !== 'object') {\n addIssue(issues, atKey(path, 'type'), \"expected the root parameter schema to be type 'object'\");\n return undefined;\n }\n if (schema.properties === undefined) {\n addIssue(\n issues,\n atKey(path, 'properties'),\n 'expected the root parameter schema to name its properties',\n );\n return undefined;\n }\n return { ...schema, type: 'object', properties: schema.properties };\n}\n\nexport function decodeToolSchema(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): IToolSchema | undefined {\n const raw = decodeDeclaredObject(value, path, issues, [\n 'name',\n 'description',\n 'parameters',\n 'outputSchema',\n ]);\n if (raw === undefined) return undefined;\n const name = decodeString(raw['name'], atKey(path, 'name'), issues);\n const description = decodeString(raw['description'], atKey(path, 'description'), issues);\n const parameters = decodeObjectParameterSchema(\n raw['parameters'],\n atKey(path, 'parameters'),\n issues,\n );\n if (name === undefined || description === undefined || parameters === undefined) return undefined;\n const schema: IToolSchema = { name, description, parameters };\n setOptional(\n schema,\n 'outputSchema',\n decodeOptional(raw['outputSchema'], atKey(path, 'outputSchema'), issues, decodeParameterSchema),\n );\n return schema;\n}\n","/**\n * TRANS-005 (#2081) — the optional members of a persisted session record.\n *\n * Written out one member at a time rather than driven from a key→decoder table. A table would be\n * shorter, but its entries decode to a union of every member type, so the assignment back onto the\n * record needs a cast — and a cast is what this whole codec exists to remove. Each statement below\n * is checked by the compiler against the contract member it fills.\n */\n\nimport { decodeBackgroundJobGroupEvent, decodeJobGroupState } from './background-group-decoders.js';\nimport { decodeBackgroundTaskState } from './background-task-decoders.js';\nimport { decodeBackgroundTaskEvent } from './background-task-event-decoders.js';\nimport { atKey, setOptional } from './decode-outcome.js';\nimport {\n decodeContextReferenceItem,\n decodeMemoryEvent,\n decodeMemoryReference,\n decodeSkillActivationEvent,\n} from './event-decoders.js';\nimport {\n decodeActiveBranchPointer,\n decodeGoalState,\n decodePlanArtifact,\n} from './goal-plan-branch-decoders.js';\nimport { decodeHistoryEntry } from './message-decoders.js';\nimport { decodeSessionLoopState } from './session-loop-decoders.js';\nimport { decodeArray, decodeOptional, decodeString } from './scalars.js';\nimport { decodeToolSchema } from './tool-schema-decoders.js';\n\nimport type { TDecodeIssues } from './decode-outcome.js';\nimport type { IInteractiveSessionRecord } from '@robota-sdk/agent-interface-session';\n\n/** Decode an optional array member: absent stays absent, present is decoded element by element. */\nfunction optionalArray<TItem>(\n raw: Record<string, unknown>,\n key: string,\n path: string,\n issues: TDecodeIssues,\n decodeItem: (value: unknown, path: string, issues: TDecodeIssues) => TItem | undefined,\n): TItem[] | undefined {\n return decodeOptional(raw[key], atKey(path, key), issues, (member, memberPath, sink) =>\n decodeArray(member, memberPath, sink, decodeItem),\n );\n}\n\n/** Fill in every optional member of `record` from `raw`, recording any defect it finds. */\nexport function applyOptionalRecordMembers(\n record: IInteractiveSessionRecord,\n raw: Record<string, unknown>,\n path: string,\n issues: TDecodeIssues,\n): void {\n for (const key of ['name', 'systemPrompt', 'sandboxSnapshotId'] as const) {\n setOptional(record, key, decodeOptional(raw[key], atKey(path, key), issues, decodeString));\n }\n\n setOptional(record, 'history', optionalArray(raw, 'history', path, issues, decodeHistoryEntry));\n setOptional(\n record,\n 'toolSchemas',\n optionalArray(raw, 'toolSchemas', path, issues, decodeToolSchema),\n );\n setOptional(\n record,\n 'backgroundTasks',\n optionalArray(raw, 'backgroundTasks', path, issues, decodeBackgroundTaskState),\n );\n setOptional(\n record,\n 'backgroundTaskEvents',\n optionalArray(raw, 'backgroundTaskEvents', path, issues, decodeBackgroundTaskEvent),\n );\n setOptional(\n record,\n 'backgroundJobGroups',\n optionalArray(raw, 'backgroundJobGroups', path, issues, decodeJobGroupState),\n );\n setOptional(\n record,\n 'backgroundJobGroupEvents',\n optionalArray(raw, 'backgroundJobGroupEvents', path, issues, decodeBackgroundJobGroupEvent),\n );\n setOptional(\n record,\n 'sessionLoops',\n optionalArray(raw, 'sessionLoops', path, issues, decodeSessionLoopState),\n );\n setOptional(\n record,\n 'skillActivationEvents',\n optionalArray(raw, 'skillActivationEvents', path, issues, decodeSkillActivationEvent),\n );\n setOptional(\n record,\n 'memoryEvents',\n optionalArray(raw, 'memoryEvents', path, issues, decodeMemoryEvent),\n );\n setOptional(\n record,\n 'usedMemoryReferences',\n optionalArray(raw, 'usedMemoryReferences', path, issues, decodeMemoryReference),\n );\n setOptional(\n record,\n 'contextReferences',\n optionalArray(raw, 'contextReferences', path, issues, decodeContextReferenceItem),\n );\n\n setOptional(\n record,\n 'goal',\n decodeOptional(raw['goal'], atKey(path, 'goal'), issues, decodeGoalState),\n );\n setOptional(\n record,\n 'plan',\n decodeOptional(raw['plan'], atKey(path, 'plan'), issues, decodePlanArtifact),\n );\n setOptional(\n record,\n 'activeBranch',\n decodeOptional(\n raw['activeBranch'],\n atKey(path, 'activeBranch'),\n issues,\n decodeActiveBranchPointer,\n ),\n );\n}\n","/**\n * TRANS-005 (#2081) — the total decoder for a persisted interactive-session record, and the\n * versioned envelope that carries one.\n *\n * ## Why the version is in an envelope and not in the record\n *\n * `IInteractiveSessionRecord` has no version member, and this codec does not add one. A REQUIRED\n * member would oblige every producer to set it, and migrating producers is another leaf's work; an\n * OPTIONAL one would mean absent-is-acceptable, which is the permissive reader this codec exists to\n * remove. An envelope keeps the version mandatory exactly where it is checked, and leaves every\n * producer untouched until the leaf that migrates it.\n */\n\nimport { atKey } from './decode-outcome.js';\nimport { decodeMessage } from './message-decoders.js';\nimport { applyOptionalRecordMembers } from './record-optional-members.js';\nimport {\n decodeArray,\n decodeDeclaredObject,\n decodeString,\n decodeTimestampString,\n} from './scalars.js';\n\nimport type { TDecodeIssues, TSessionRecordDecodeOutcome } from './decode-outcome.js';\nimport type { IInteractiveSessionRecord } from '@robota-sdk/agent-interface-session';\n\n/**\n * The version of the persisted record envelope this build reads and writes.\n *\n * Bump it when the shape changes in a way an older reader would decode WRONGLY rather than not at\n * all. A reader that meets a version it does not implement reports `unsupported` and stops — it does\n * not decode the members it recognises, because a partially decoded session is the silent\n * field-loss this codec replaces.\n *\n * ONE concept, not two (issue #2185): the envelope `{ schemaVersion, record }` and the record it\n * wraps version together — a change to either shape bumps this number, and every consumer of the\n * envelope reads it: the portable session artifact (`serializeSessionArtifact`) and the session\n * store (`NodeSessionStore.save`, `WorkspaceSessionStore`). Two constants would let an envelope-only\n * change reject records that are fine, and the two shapes have never moved apart. The constant is\n * therefore named for the pair it versions, not for its first consumer: TRANS-006 kept the\n * incumbent `SESSION_ARTIFACT_SCHEMA_VERSION` (published, written by the producing path) over the\n * duplicate TRANS-005 introduced, and #2185 renamed it here — prerelease, so no alias.\n *\n * Its DECLARATION lives here rather than beside the artifact functions because `session-artifact.ts`\n * imports this module; declaring it there and importing it back would be a module cycle. The export\n * from the package barrel is unchanged.\n */\nexport const SESSION_RECORD_ENVELOPE_VERSION = 1;\n\n/** A persisted record with the version of the shape it was written in. */\nexport interface IVersionedInteractiveSessionRecord {\n schemaVersion: number;\n record: IInteractiveSessionRecord;\n}\n\n/**\n * Every key the record contract declares.\n *\n * Exported so a test can compare it against `keyof IInteractiveSessionRecord`: a member added to the\n * contract without a branch below then fails that comparison rather than being silently dropped by\n * a decoder that never heard of it.\n */\nexport const INTERACTIVE_SESSION_RECORD_KEYS: readonly string[] = [\n 'id',\n 'name',\n 'cwd',\n 'createdAt',\n 'updatedAt',\n 'messages',\n 'history',\n 'systemPrompt',\n 'toolSchemas',\n 'backgroundTasks',\n 'backgroundTaskEvents',\n 'backgroundJobGroups',\n 'backgroundJobGroupEvents',\n 'sessionLoops',\n 'skillActivationEvents',\n 'memoryEvents',\n 'usedMemoryReferences',\n 'contextReferences',\n 'sandboxSnapshotId',\n 'goal',\n 'plan',\n 'activeBranch',\n];\n\n/**\n * Decode a bare persisted record.\n *\n * The value is decoded, never cast: what comes back is either a record every member of which was\n * checked, or the list of every place it failed — not the first place.\n */\nexport function decodeInteractiveSessionRecord(value: unknown): TSessionRecordDecodeOutcome {\n const issues: TDecodeIssues = [];\n const record = decodeRecordInto(value, '', issues);\n if (record === undefined || issues.length > 0) {\n return { status: 'corrupt', issues };\n }\n return { status: 'valid', record };\n}\n\n/**\n * Decode a versioned envelope.\n *\n * The version is read BEFORE the record, and a version this build does not implement returns\n * `unsupported` WITHOUT nested issues: reporting field defects against a shape from another version\n * describes the reader's expectations, not the data's condition, and a caller cannot act on it.\n */\nexport function decodeVersionedInteractiveSessionRecord(\n value: unknown,\n): TSessionRecordDecodeOutcome {\n const issues: TDecodeIssues = [];\n const envelope = decodeDeclaredObject(value, '', issues, ['schemaVersion', 'record']);\n if (envelope === undefined) return { status: 'corrupt', issues };\n\n const declaredVersion = envelope['schemaVersion'];\n if (typeof declaredVersion !== 'number' || !Number.isFinite(declaredVersion)) {\n return { status: 'unsupported', schemaVersion: undefined };\n }\n if (declaredVersion !== SESSION_RECORD_ENVELOPE_VERSION) {\n return { status: 'unsupported', schemaVersion: declaredVersion };\n }\n\n const record = decodeRecordInto(envelope['record'], 'record', issues);\n if (record === undefined || issues.length > 0) {\n return { status: 'corrupt', issues };\n }\n return { status: 'valid', record };\n}\n\n/** The record decode itself, at whatever path it sits (the root, or `record` inside an envelope). */\nfunction decodeRecordInto(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): IInteractiveSessionRecord | undefined {\n const raw = decodeDeclaredObject(value, path, issues, INTERACTIVE_SESSION_RECORD_KEYS);\n if (raw === undefined) return undefined;\n\n const id = decodeString(raw['id'], atKey(path, 'id'), issues);\n const cwd = decodeString(raw['cwd'], atKey(path, 'cwd'), issues);\n const createdAt = decodeTimestampString(raw['createdAt'], atKey(path, 'createdAt'), issues);\n const updatedAt = decodeTimestampString(raw['updatedAt'], atKey(path, 'updatedAt'), issues);\n // `messages` is the one required array. An absent one reaches `decodeArray` as `undefined` and is\n // reported there like any other non-array, so it needs no case of its own.\n const messages = decodeArray(raw['messages'], atKey(path, 'messages'), issues, decodeMessage);\n\n if (\n id === undefined ||\n cwd === undefined ||\n createdAt === undefined ||\n updatedAt === undefined ||\n messages === undefined\n ) {\n return undefined;\n }\n\n const record: IInteractiveSessionRecord = { id, cwd, createdAt, updatedAt, messages };\n applyOptionalRecordMembers(record, raw, path, issues);\n return record;\n}\n","/**\n * SELFHOST-014 — the neutral export/import envelope for a session, over `IInteractiveSessionRecord`.\n *\n * This is a RECORD-TRANSPORT sibling of the file-backed `session-store.ts` (SRP: transport vs file persistence).\n * It is the async, durable COMPLEMENT to REMOTE-001's live P2P channel — export a thread to a portable artifact,\n * hand it off, import + resume it later on a second surface, with both peers possibly offline. No transport, no\n * pairing, no wire protocol.\n *\n * Two operations, specified separately and never conflated:\n * 1. Round-trip serialize (fidelity, local): `serializeSessionArtifact(record)` with no transform is\n * full-fidelity — `deserializeSessionArtifact(serializeSessionArtifact(record))` deep-equals the record.\n * (Precondition: `IInteractiveSessionRecord` is JSON-safe, as it is by construction — it is the on-disk file-store\n * shape; a non-JSON value like a `Date`/`Map`/class instance nested in a payload would not round-trip.)\n * 2. Export-for-share: `serializeSessionArtifact(record, { redact })` applies a caller-supplied, policy-free\n * `redact` transform BEFORE writing bytes. The envelope selects NO fields and owns NO field policy — the app\n * builds `redact` (composing the opt-in `scrubSensitiveKeys`). The no-transform form stays full-fidelity.\n *\n * Neutrality (mechanically fenced — TC-05): this module is pure serialize/deserialize + a schema-version header +\n * the app-supplied `redact` seam. It carries NO link/cloud/upload/access-control and NO redaction FIELD policy.\n */\n\nimport {\n SESSION_RECORD_ENVELOPE_VERSION,\n decodeVersionedInteractiveSessionRecord,\n} from './session-record-codec/index.js';\n\nimport type { IVersionedInteractiveSessionRecord } from './session-record-codec/index.js';\nimport type { IInteractiveSessionRecord } from '@robota-sdk/agent-interface-session';\n\n/**\n * TRANS-006: the envelope type and its version constant are the codec's (`session-record-codec/`).\n * They used to be declared here as `ISessionArtifact` and a local constant, which was a second name\n * and a second number for one shape — the envelope the codec decodes and the envelope this module\n * writes were always the same `{ schemaVersion, record }`. Nothing on disk changed when they were\n * unified, because there was nothing to change.\n */\n\nexport interface ISerializeSessionArtifactOptions {\n /**\n * SHARE-PATH ONLY. An app-supplied, policy-free transform applied to the record before serialization — the app\n * decides which trust-boundary fields to strip (composing the opt-in `scrubSensitiveKeys`). Omit for the\n * full-fidelity local round-trip.\n */\n redact?: (record: IInteractiveSessionRecord) => IInteractiveSessionRecord;\n}\n\n/** How many decode issues an error message carries before it elides the rest. */\nconst MAX_REPORTED_ISSUES = 5;\n\n/**\n * Parse the bytes, reporting a non-JSON body the same way a non-record body is reported.\n *\n * `JSON.parse` throwing and the decoder refusing are the same failure for a caller — the bytes are\n * not an artifact — so they are not two different exceptions to catch.\n */\nfunction parseArtifactBytes(bytes: string): unknown {\n try {\n return JSON.parse(bytes) as unknown;\n } catch {\n throw new Error('Invalid session artifact: the bytes are not JSON.');\n }\n}\n\n/**\n * Serialize a session record into a portable, versioned artifact. With no `redact`, this is the full-fidelity\n * round-trip form; with `redact`, the caller's transform is applied first (the share path).\n */\nexport function serializeSessionArtifact(\n record: IInteractiveSessionRecord,\n options: ISerializeSessionArtifactOptions = {},\n): string {\n const payload = options.redact ? options.redact(record) : record;\n const artifact: IVersionedInteractiveSessionRecord = {\n schemaVersion: SESSION_RECORD_ENVELOPE_VERSION,\n record: payload,\n };\n return JSON.stringify(artifact, null, 2);\n}\n\n/**\n * Parse a session artifact back into an `IInteractiveSessionRecord`, rejecting an artifact whose schema version this build\n * does not understand (so an incompatible artifact is never silently mis-imported).\n */\nexport function deserializeSessionArtifact(bytes: string): IInteractiveSessionRecord {\n const outcome = decodeVersionedInteractiveSessionRecord(parseArtifactBytes(bytes));\n if (outcome.status === 'unsupported') {\n throw new Error(\n `Unsupported session artifact schema version ${outcome.schemaVersion ?? '(absent or not a number)'} ` +\n `(this build reads ${SESSION_RECORD_ENVELOPE_VERSION}).`,\n );\n }\n if (outcome.status === 'corrupt') {\n // The paths are the point: an artifact that cannot be imported should tell its holder WHERE it\n // is wrong, not that it is wrong. Bounded, because a wholly unrelated payload produces an issue\n // per member and a thousand-line error informs nobody.\n const shown = outcome.issues.slice(0, MAX_REPORTED_ISSUES);\n const detail = shown\n .map((issue) => `${issue.path === '' ? '(root)' : issue.path}: ${issue.message}`)\n .join('; ');\n const elided =\n outcome.issues.length > shown.length\n ? ` (+${outcome.issues.length - shown.length} more)`\n : '';\n throw new Error(`Invalid session artifact: ${detail}${elided}`);\n }\n return outcome.record;\n}\n","/**\n * SELFHOST-014 — the single source of the sensitive-key redaction (SSOT).\n *\n * The recursive secret-key scrub used to live privately in `session-logger.ts`, logging-coupled and not exported.\n * It is extracted here so exactly ONE definition of \"which keys are sensitive\" exists, consumed by BOTH the file\n * session logger (persistence-time redaction) and the SELFHOST-014 share-artifact `redact` transform (an opt-in\n * the app composes). This utility is a pure, mechanism-level key scrub only: it carries NO field/trust-boundary\n * policy (which of `cwd`/`sandboxSnapshotId`/… to strip is an app decision) and is NEVER forced into the\n * full-fidelity local round-trip.\n */\n\n/** Values a scrub can walk (mirrors the logger's log-value shape; avoids `any`/`unknown`). */\nexport type TScrubbableValue = string | number | boolean | object | null | undefined;\n\nconst DEFAULT_REDACTED_VALUE = '[REDACTED]';\n\n/**\n * Keys whose VALUE is a secret and must be redacted before persistence or sharing:\n * `apiKey`/`authorization`/`accessToken`/`refreshToken`/`secret`/`password`/`xApiKey` (case/`-`/`_`-insensitive).\n */\nexport const SENSITIVE_KEY_PATTERN =\n /^(api[-_]?key|authorization|access[-_]?token|refresh[-_]?token|secret|password|x[-_]?api[-_]?key)$/i;\n\n/** True when a key's value should be redacted. The one predicate both the logger and the artifact scrub use. */\nexport function isSensitiveKey(key: string): boolean {\n return SENSITIVE_KEY_PATTERN.test(key);\n}\n\nfunction scrubValue(\n key: string | undefined,\n value: TScrubbableValue,\n redactedValue: string,\n): TScrubbableValue {\n if (key !== undefined && isSensitiveKey(key)) {\n return redactedValue;\n }\n if (Array.isArray(value)) {\n return value.map((item) => scrubValue(undefined, item as TScrubbableValue, redactedValue));\n }\n // A Date (or any non-plain object without own enumerable keys) is returned as-is by the Object.entries walk.\n if (value !== null && typeof value === 'object' && !(value instanceof Date)) {\n const record = value as Record<string, TScrubbableValue>;\n const out: Record<string, TScrubbableValue> = {};\n for (const [childKey, childValue] of Object.entries(record)) {\n out[childKey] = scrubValue(childKey, childValue, redactedValue);\n }\n return out;\n }\n return value;\n}\n\n/**\n * Deep-copy `value`, replacing any value whose KEY is sensitive with `redactedValue` (default `[REDACTED]`).\n * Pure — does not mutate the input. Returns the same shape (`T`).\n */\nexport function scrubSensitiveKeys<T>(value: T, redactedValue: string = DEFAULT_REDACTED_VALUE): T {\n return scrubValue(undefined, value as TScrubbableValue, redactedValue) as T;\n}\n","/**\n * INFRA-017: typed contract for session-log event names + replay keys (SSOT).\n *\n * `FileSessionLogger` owns the `{ schemaVersion, timestamp, sessionId, event }` JSONL envelope.\n * This module owns its version and event vocabulary; `session-log-codec` validates each payload\n * before replay or completeness checks.\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/** Supported persisted session-log envelope version. */\nexport const SESSION_LOG_SCHEMA_VERSION = 1;\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 sessionShutdownStepError: 'session_shutdown_step_error',\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 providerStreamRawDelta: 'provider_stream_raw_delta',\n providerResponseRaw: 'provider_response_raw',\n providerResponseNormalized: 'provider_response_normalized',\n /**\n * CORE-043: which transport actually carried a structured-output schema on this request, and\n * whether the schema had to be stated in the prompt instead. Diagnostic, not replay substrate — a\n * replay answers a `provider_request` from its recorded response, and this line explains why that\n * request looked the way it did.\n */\n structuredOutputTransport: 'structured_output_transport',\n /**\n * A request moved to another model because the one it was on failed. Diagnostic, not replay\n * substrate: the `provider_request` announced for the new model is what a replay answers.\n */\n providerFallback: 'provider_fallback',\n assistantMessageCommitted: 'assistant_message_committed',\n\n // Tool replay substrate (keyed by executionId + toolCallId).\n toolExecutionRequest: 'tool_execution_request',\n toolExecutionResult: 'tool_execution_result',\n toolBatchStarted: 'tool_batch_started',\n toolMessageCommitted: 'tool_message_committed',\n\n // Runtime state persisted beside the conversation substrate.\n backgroundTaskEvent: 'background_task_event',\n backgroundJobGroupEvent: 'background_job_group_event',\n memoryEvent: 'memory_event',\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","/**\n * Session log payload normalization — scrubbing and size-bounding what goes into a log line.\n *\n * Split out of `session-logger.ts` to keep each file under 300 lines. The split is by\n * responsibility, not by size alone: this module decides WHAT a line may contain (sensitive keys\n * redacted, oversized payloads written beside the log and referenced), while `session-logger.ts`\n * decides WHERE and WHEN the bytes are written.\n */\n\nimport { createHash } from 'node:crypto';\n\nimport { isSensitiveKey } from './scrub-sensitive.js';\n\nimport type { IExternalPayloadSink } from './session-log-sinks.js';\nimport type {\n IFileSessionLoggerOptions,\n TSessionLogData,\n TSessionLogValue,\n} from './session-log-reference-types.js';\n\nexport function normalizeLogData(\n sessionId: string,\n data: TSessionLogData,\n options: Required<IFileSessionLoggerOptions>,\n externalPayloadSink: IExternalPayloadSink | undefined,\n): TSessionLogData {\n const normalized: TSessionLogData = {};\n for (const [key, value] of Object.entries(data)) {\n normalized[key] = normalizeLogValue(sessionId, key, value, options, externalPayloadSink);\n }\n return normalized;\n}\n\nfunction normalizeLogValue(\n sessionId: string,\n key: string,\n value: TSessionLogValue,\n options: Required<IFileSessionLoggerOptions>,\n externalPayloadSink: IExternalPayloadSink | undefined,\n): TSessionLogValue {\n if (isSensitiveKey(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, value, options, externalPayloadSink);\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, key, item as TSessionLogValue, options, externalPayloadSink),\n );\n return maybeExternalizePayload(sessionId, normalizedArray, options, externalPayloadSink);\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 childKey,\n childValue,\n options,\n externalPayloadSink,\n );\n }\n return maybeExternalizePayload(sessionId, normalizedRecord, options, externalPayloadSink);\n }\n return String(value);\n}\n\nfunction maybeExternalizePayload(\n sessionId: string,\n value: TSessionLogValue,\n options: Required<IFileSessionLoggerOptions>,\n externalPayloadSink: IExternalPayloadSink | undefined,\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 || externalPayloadSink === undefined) {\n return value;\n }\n\n const sha256 = createHash('sha256').update(serialized).digest('hex');\n return externalPayloadSink.writeJson(sessionId, sha256, serialized);\n}\n\n/** No-op logger — used when logging is disabled. */\n","/**\n * Session Logger — pluggable logging interface for session events.\n *\n * ISessionLogger defines the contract. FileSessionLogger serializes JSONL through an\n * injected sink and never opens a path itself. Consumers can implement their\n * own (e.g., remote, database, silent) and inject via Session constructor.\n */\n\nimport { createLogger } from '@robota-sdk/agent-core';\n\nimport { isSafeSessionId } from './session-id.js';\nimport { SESSION_LOG_SCHEMA_VERSION } from './session-log-events.js';\nimport { normalizeLogData } from './session-log-payload.js';\n\nimport type { ISessionLogSink } from './session-log-sinks.js';\nimport type {\n IFileSessionLoggerOptions,\n TSessionLogData,\n} from './session-log-reference-types.js';\n\nexport type {\n IExternalPayloadReference,\n IFileSessionLoggerOptions,\n TSessionLogData,\n TSessionLogValue,\n} from './session-log-reference-types.js';\n\nconst logger = createLogger('FileSessionLogger');\n\n/**\n * Events that arrive once per streamed token.\n *\n * CORE-029: `session-run.ts` logs one of these per text delta and this class answered each with a\n * blocking `appendFileSync` — a synchronous disk write per token on the streaming hot path. They are\n * buffered and written in one call; every OTHER event flushes the buffer before writing itself, so\n * ordering in the file is unchanged and no semantic event is ever delayed behind a stream.\n */\nconst HOT_PATH_EVENTS: ReadonlySet<string> = new Set(['text_delta']);\n\n/** Flush the delta buffer once it reaches this size, so a long stream cannot grow without bound. */\nconst HOT_PATH_FLUSH_BYTES = 64 * 1024;\n\n/**\n * Every logger with something buffered, flushed if the process exits mid-stream.\n *\n * Buffering trades a write per token for a write per batch, and the price is a window in which the\n * tail of a stream exists only in memory. A normal shutdown closes that window by itself — the\n * `session_shutdown` event is not a hot-path event, so it flushes before writing — but an abnormal\n * exit would not, and a replay log missing its last exchange is a worse defect than the one being\n * fixed. `exit` handlers may only do synchronous work, which is exactly what `flush` does.\n */\nconst liveLoggers = new Set<FileSessionLogger>();\nlet exitHookInstalled = false;\n\nfunction ensureExitFlush(loggerInstance: FileSessionLogger): void {\n liveLoggers.add(loggerInstance);\n if (exitHookInstalled) return;\n if (typeof process === 'undefined' || typeof process.on !== 'function') return;\n exitHookInstalled = true;\n process.on('exit', () => {\n for (const live of liveLoggers) {\n live.flush();\n }\n });\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]';\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 * Write out anything buffered.\n *\n * Optional because an implementation that never buffers has nothing to do. A caller that needs\n * the log to be complete on disk — session end, or a reader about to parse it — calls this.\n */\n flush?(): void;\n}\n\n/**\n * Sink-driven session logger — writes JSONL through `ISessionLogSink`.\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 options: Required<IFileSessionLoggerOptions>;\n /** Buffered hot-path lines, per session file. Keyed by session id (CORE-029). */\n private readonly pending = new Map<string, string[]>();\n private pendingBytes = 0;\n\n constructor(\n private readonly sink: ISessionLogSink,\n options: IFileSessionLoggerOptions = {},\n ) {\n this.options = {\n externalPayloadThresholdBytes:\n options.externalPayloadThresholdBytes ?? DEFAULT_EXTERNAL_PAYLOAD_THRESHOLD_BYTES,\n redactedValue: options.redactedValue ?? DEFAULT_REDACTED_VALUE,\n };\n }\n\n log(sessionId: string, event: string, data: TSessionLogData): void {\n // SEC-006: `sessionId` becomes a path component below. This is a second sink on the same value the\n // session store guards, and it is reachable with a remote-supplied id via the playground resume\n // path, so it must not rely on the store having been called first. Logging must never break a\n // session, so a rejected id drops the line rather than throwing — the store raises the loud error.\n if (!isSafeSessionId(sessionId)) return;\n try {\n const normalizedData = normalizeLogData(\n sessionId,\n data,\n this.options,\n this.sink.externalPayloadSink,\n );\n const entry =\n JSON.stringify({\n ...normalizedData,\n schemaVersion: SESSION_LOG_SCHEMA_VERSION,\n timestamp: new Date().toISOString(),\n sessionId,\n event,\n }) + '\\n';\n\n if (HOT_PATH_EVENTS.has(event)) {\n this.buffer(sessionId, entry);\n return;\n }\n\n // Any other event flushes first, so the file's order is the order the events happened in.\n this.flush();\n this.write(sessionId, entry);\n } catch (error) {\n // allow-fallback: logging must never break a session (SEC-006 states the same rule for a\n // rejected id). What changed in CORE-029 is that the failure is no longer INVISIBLE — a log\n // that silently stops writing is indistinguishable from a session that produced no events.\n this.report(sessionId, event, error);\n }\n }\n\n /** Write out every buffered hot-path line. Safe to call when nothing is pending. */\n flush(): void {\n if (this.pending.size === 0) {\n liveLoggers.delete(this);\n return;\n }\n const batches = [...this.pending.entries()];\n this.pending.clear();\n this.pendingBytes = 0;\n liveLoggers.delete(this);\n for (const [sessionId, lines] of batches) {\n try {\n this.write(sessionId, lines.join(''));\n } catch (error) {\n this.report(sessionId, 'flush', error);\n }\n }\n }\n\n private buffer(sessionId: string, entry: string): void {\n ensureExitFlush(this);\n const lines = this.pending.get(sessionId) ?? [];\n lines.push(entry);\n this.pending.set(sessionId, lines);\n this.pendingBytes += entry.length;\n // A stream that never ends must not accumulate without bound; flushing on size keeps the\n // write count proportional to bytes rather than to tokens, which is the whole point.\n if (this.pendingBytes >= HOT_PATH_FLUSH_BYTES) {\n this.flush();\n }\n }\n\n private write(sessionId: string, text: string): void {\n this.sink.append(sessionId, text);\n }\n\n private report(sessionId: string, event: string, error: unknown): void {\n logger.warn('session log write failed', {\n sessionId,\n event,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n}\n\nexport class SilentSessionLogger implements ISessionLogger {\n log(): void {\n // intentionally empty\n }\n}\n","import { createHash } from 'node:crypto';\nimport { appendFileSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\nimport { createLogger } from '@robota-sdk/agent-core';\nimport {\n OWNER_ONLY_FILE_MODE,\n ensureOwnerOnlyDirectory,\n tightenExistingFile,\n} from '@robota-sdk/agent-core/node';\n\nimport { assertSafeSessionId } from './session-id.js';\n\nimport type { IExternalPayloadReference } from './session-log-reference-types.js';\n\nconst logger = createLogger('NodeSessionLogSink');\nconst SHA256_PATTERN = /^[0-9a-f]{64}$/;\n\nfunction assertContentAddress(sha256: string, serialized: string): void {\n const actualSha256 = createHash('sha256').update(serialized).digest('hex');\n if (!SHA256_PATTERN.test(sha256) || sha256 !== actualSha256) {\n throw new Error('Invalid sha256: external JSON payloads require their exact content digest.');\n }\n}\n\n/** Canonical validation and construction for every session-log external-payload reference. */\nexport function createSessionLogExternalPayloadReference(\n sessionId: string,\n sha256: string,\n serialized: string,\n): IExternalPayloadReference {\n assertSafeSessionId(sessionId);\n assertContentAddress(sha256, serialized);\n return {\n kind: 'external-payload',\n encoding: 'json',\n sha256,\n byteLength: Buffer.byteLength(serialized),\n relativePath: join(`${sessionId}.payloads`, `${sha256}.json`),\n };\n}\n\n/** Workspace-neutral sink for content-addressed external JSON payloads. */\nexport interface IExternalPayloadSink {\n writeJson(sessionId: string, sha256: string, serialized: string): IExternalPayloadReference;\n}\n\n/** Workspace-neutral append sink for session-log bytes. */\nexport interface ISessionLogSink {\n append(sessionId: string, text: string): void;\n readonly externalPayloadSink?: IExternalPayloadSink;\n}\n\n/** Explicit host-filesystem sink for JSONL logs and their content-addressed sidecars. */\nexport class NodeSessionLogSink implements ISessionLogSink, IExternalPayloadSink {\n readonly externalPayloadSink: IExternalPayloadSink = this;\n private readonly enabled: boolean;\n\n constructor(private readonly logDirectory: string) {\n try {\n // SEC-020: `mkdirSync(dir, { recursive: true, mode })` does NOT set the mode of a directory\n // that already exists — it returns successfully and adopts whatever is there. Measured: a log\n // directory pre-created at 0777 stayed 0777 while its records were written 0600, which means\n // another local account could not read a record but could unlink and replace one, and could\n // enumerate every session id.\n ensureOwnerOnlyDirectory(logDirectory);\n this.enabled = true;\n } catch (error) {\n // allow-fallback: session logging is diagnostic and must not disable the session. It now also\n // disables on a directory that cannot be made owner-only, which is the correct direction: no\n // log is better than a log any account can read or replace.\n this.enabled = false;\n logger.warn('session log directory could not be created — session logging is disabled', {\n logDirectory,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n append(sessionId: string, text: string): void {\n assertSafeSessionId(sessionId);\n if (!this.enabled) return;\n const path = join(this.logDirectory, `${sessionId}.jsonl`);\n // SEC-020: `mode` on a write applies only when the file is CREATED, so a log an older version\n // left at 0644 keeps 0644 for its whole life however many times it is appended to. Tightening\n // first is the repair path for a store written before this change.\n tightenExistingFile(path);\n appendFileSync(path, text, { mode: OWNER_ONLY_FILE_MODE });\n }\n\n writeJson(sessionId: string, sha256: string, serialized: string): IExternalPayloadReference {\n const reference = createSessionLogExternalPayloadReference(sessionId, sha256, serialized);\n const payloadDirectoryName = `${sessionId}.payloads`;\n if (this.enabled) {\n ensureOwnerOnlyDirectory(join(this.logDirectory, payloadDirectoryName));\n const payloadPath = join(this.logDirectory, reference.relativePath);\n try {\n writeFileSync(payloadPath, serialized, {\n encoding: 'utf8',\n mode: OWNER_ONLY_FILE_MODE,\n flag: 'wx',\n });\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;\n // SEC-020: the payload is content-addressed, so an existing file already holds these exact\n // bytes and `wx` correctly declines to rewrite it. Its MODE is a different question — one\n // written by an older version is 0644 and nothing else would ever repair it.\n tightenExistingFile(payloadPath);\n }\n }\n return reference;\n }\n}\n","import { atIndex, atKey, describeValue } from '../session-record-codec/decode-outcome.js';\nimport { decodeMessage } from '../session-record-codec/message-decoders.js';\nimport {\n decodeArray,\n decodeBoolean,\n decodeDeclaredObject,\n decodeInteger,\n decodeLiteral,\n decodeNumber,\n decodeString,\n} from '../session-record-codec/scalars.js';\nimport { decodeToolSchema } from '../session-record-codec/tool-schema-decoders.js';\n\nimport type { TDecodeIssues } from '../session-record-codec/decode-outcome.js';\nimport type { IContextWindowState, TUniversalValue } from '@robota-sdk/agent-core';\n\nexport type TFieldDecoder = (value: unknown, path: string, issues: TDecodeIssues) => unknown;\nexport type TPayloadShape = Readonly<Record<string, { decode: TFieldDecoder; optional?: boolean }>>;\n\nexport const required = (decode: TFieldDecoder): { decode: TFieldDecoder } => ({ decode });\nexport const optional = (decode: TFieldDecoder): { decode: TFieldDecoder; optional: true } => ({\n decode,\n optional: true,\n});\nexport const strings = (value: unknown, path: string, issues: TDecodeIssues): unknown =>\n decodeArray(value, path, issues, decodeString);\nexport const messages = (value: unknown, path: string, issues: TDecodeIssues): unknown =>\n decodeArray(value, path, issues, decodeMessage);\nexport const schemas = (value: unknown, path: string, issues: TDecodeIssues): unknown =>\n decodeArray(value, path, issues, decodeToolSchema);\nexport const committedAssistant = (value: unknown, path: string, issues: TDecodeIssues): unknown =>\n typeof value === 'string' ? value : decodeMessage(value, path, issues);\nexport const nonNegativeInteger = (\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): unknown => {\n const decoded = decodeInteger(value, path, issues);\n if (decoded !== undefined && decoded < 0)\n issues.push({ path, message: 'expected a non-negative integer' });\n return decoded;\n};\n\nexport function isPlainRecord(value: unknown): value is Record<string, unknown> {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return false;\n const prototype: unknown = Object.getPrototypeOf(value);\n return prototype === Object.prototype || prototype === null;\n}\n\nexport function record(value: unknown, path: string, issues: TDecodeIssues): unknown {\n if (!isPlainRecord(value)) {\n issues.push({ path, message: `expected an object, received ${describeValue(value)}` });\n return undefined;\n }\n for (const [key, member] of Object.entries(value)) json(member, atKey(path, key), issues);\n return value;\n}\n\n// IContextWindowState is the producer's four-field snapshot contract.\nconst CONTEXT_STATE_KEYS = [\n 'maxTokens',\n 'usedTokens',\n 'usedPercentage',\n 'remainingPercentage',\n] as const satisfies readonly (keyof IContextWindowState)[];\n\nexport function contextState(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n): IContextWindowState | undefined {\n const raw = decodeDeclaredObject(value, path, issues, CONTEXT_STATE_KEYS);\n if (raw === undefined) return undefined;\n const maxTokens = decodeNumber(raw['maxTokens'], atKey(path, 'maxTokens'), issues);\n const usedTokens = decodeNumber(raw['usedTokens'], atKey(path, 'usedTokens'), issues);\n const usedPercentage = decodeNumber(raw['usedPercentage'], atKey(path, 'usedPercentage'), issues);\n const remainingPercentage = decodeNumber(\n raw['remainingPercentage'],\n atKey(path, 'remainingPercentage'),\n issues,\n );\n if (\n maxTokens === undefined ||\n usedTokens === undefined ||\n usedPercentage === undefined ||\n remainingPercentage === undefined\n )\n return undefined;\n return { maxTokens, usedTokens, usedPercentage, remainingPercentage };\n}\n\n// IOwnerPathSegment is owned by agent-core's event service: exactly { type, id }.\nexport const ownerPath = (value: unknown, path: string, issues: TDecodeIssues): unknown =>\n decodeArray(value, path, issues, (segment, segmentPath, sink) => {\n const raw = decodeDeclaredObject(segment, segmentPath, sink, ['type', 'id']);\n if (raw === undefined) return undefined;\n const type = decodeString(raw['type'], atKey(segmentPath, 'type'), sink);\n const id = decodeString(raw['id'], atKey(segmentPath, 'id'), sink);\n return type === undefined || id === undefined ? undefined : { type, id };\n });\n\n// Provider-native payloads and tool result data have no provider-neutral member schema.\nexport function json(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n seen = new WeakSet<object>(),\n): TUniversalValue | undefined {\n if (value === null || typeof value === 'string' || typeof value === 'boolean') return value;\n if (typeof value === 'number' && Number.isFinite(value)) return value;\n if (typeof value !== 'object' || value instanceof Date) {\n issues.push({\n path,\n message: `expected a JSON-compatible value, received ${describeValue(value)}`,\n });\n return undefined;\n }\n if (!Array.isArray(value) && !isPlainRecord(value)) {\n issues.push({\n path,\n message: `expected a JSON-compatible object, received ${describeValue(value)}`,\n });\n return undefined;\n }\n if (seen.has(value)) {\n issues.push({ path, message: 'expected an acyclic JSON-compatible value' });\n return undefined;\n }\n seen.add(value);\n if (Array.isArray(value))\n for (let index = 0; index < value.length; index++)\n json(value[index], atIndex(path, index), issues, seen);\n else\n for (const [key, member] of Object.entries(value)) json(member, atKey(path, key), issues, seen);\n seen.delete(value);\n return value as TUniversalValue;\n}\n\n/** Structural preflight before calling recursive record decoders. */\nexport function checkContainerIntegrity(\n value: unknown,\n path: string,\n issues: TDecodeIssues,\n seen = new WeakSet<object>(),\n): void {\n if (typeof value !== 'object' || value === null || value instanceof Date) return;\n if (seen.has(value)) {\n issues.push({ path, message: 'expected an acyclic value' });\n return;\n }\n seen.add(value);\n if (Array.isArray(value)) {\n for (let index = 0; index < value.length; index++) {\n if (!Object.hasOwn(value, index))\n issues.push({\n path: atIndex(path, index),\n message: 'expected an array element, received nothing',\n });\n else checkContainerIntegrity(value[index], atIndex(path, index), issues, seen);\n }\n } else {\n for (const [key, member] of Object.entries(value))\n checkContainerIntegrity(member, atKey(path, key), issues, seen);\n }\n seen.delete(value);\n}\n\nexport const literal =\n <T extends string>(...values: readonly T[]): TFieldDecoder =>\n (value, path, issues) =>\n decodeLiteral(value, values, path, issues);\n\nexport const historyStructure = (value: unknown, path: string, issues: TDecodeIssues): unknown =>\n decodeArray(value, path, issues, (item, itemPath, sink) => {\n if (typeof item !== 'object' || item === null || Array.isArray(item)) {\n sink.push({ path: itemPath, message: `expected an object, received ${describeValue(item)}` });\n return undefined;\n }\n const entry = item as Record<string, unknown>;\n decodeLiteral(\n entry['role'],\n ['user', 'assistant', 'system', 'tool'],\n atKey(itemPath, 'role'),\n sink,\n );\n nonNegativeInteger(entry['contentLength'], atKey(itemPath, 'contentLength'), sink);\n decodeBoolean(entry['hasToolCalls'], atKey(itemPath, 'hasToolCalls'), sink);\n strings(entry['toolCallNames'], atKey(itemPath, 'toolCallNames'), sink);\n if (entry['metadata'] !== undefined)\n record(entry['metadata'], atKey(itemPath, 'metadata'), sink);\n for (const key of Object.keys(entry))\n if (!['role', 'contentLength', 'hasToolCalls', 'toolCallNames', 'metadata'].includes(key))\n sink.push({ path: atKey(itemPath, key), message: 'unknown history structure field' });\n return entry;\n });\n","import {\n committedAssistant,\n contextState,\n historyStructure,\n json,\n literal,\n messages,\n nonNegativeInteger,\n optional,\n ownerPath,\n record,\n required,\n schemas,\n strings,\n} from './field-decoders.js';\nimport { decodeBackgroundJobGroupEvent } from '../session-record-codec/background-group-decoders.js';\nimport { decodeBackgroundTaskEvent } from '../session-record-codec/background-task-event-decoders.js';\nimport { decodeMemoryEvent } from '../session-record-codec/event-decoders.js';\nimport { decodeMessage } from '../session-record-codec/message-decoders.js';\nimport { decodeBoolean, decodeNumber, decodeString } from '../session-record-codec/scalars.js';\n\nimport type { TSessionLogEventName } from '../session-log-events.js';\nimport type { TPayloadShape } from './field-decoders.js';\n\n// The required members are drawn from production emitters; optional members include values\n// omitted by the logger when a producer supplies undefined.\nexport const PAYLOADS = {\n session_init: {\n cwd: required(decodeString),\n systemPromptLength: required(nonNegativeInteger),\n systemPrompt: required(decodeString),\n toolSchemas: required(schemas),\n model: required(decodeString),\n provider: required(decodeString),\n },\n session_shutdown: { reason: required(decodeString) },\n session_shutdown_step_error: { step: required(decodeString), error: required(decodeString) },\n context: {\n maxTokens: required(decodeNumber),\n usedTokens: required(decodeNumber),\n usedPercentage: required(decodeNumber),\n remainingPercentage: required(decodeNumber),\n },\n context_compact: {\n trigger: required(decodeString),\n before: required(contextState),\n after: required(contextState),\n },\n error: {\n message: required(decodeString),\n stack: required(decodeString),\n historyLength: required(nonNegativeInteger),\n },\n history_mutation: {\n executionId: optional(decodeString),\n conversationId: optional(decodeString),\n round: optional(nonNegativeInteger),\n batchId: optional(decodeString),\n usageObservationId: optional(decodeString),\n providerId: optional(decodeString),\n modelId: optional(decodeString),\n providerError: optional(decodeBoolean),\n contextOverflow: optional(decodeBoolean),\n mutation: required(literal('append_message')),\n index: required(nonNegativeInteger),\n message: required(decodeMessage),\n },\n provider_request: {\n executionId: required(decodeString),\n conversationId: optional(decodeString),\n round: required(nonNegativeInteger),\n provider: required(decodeString),\n model: required(decodeString),\n effort: optional(decodeString),\n forcedSummary: optional(decodeBoolean),\n messages: required(messages),\n tools: optional(schemas),\n },\n provider_native_raw_payload: {\n executionId: required(decodeString),\n conversationId: optional(decodeString),\n round: required(nonNegativeInteger),\n provider: required(decodeString),\n apiSurface: optional(decodeString),\n payloadKind: required(literal('request', 'response', 'stream_event')),\n sequence: required(nonNegativeInteger),\n payload: optional(json),\n metadata: optional(record),\n },\n provider_stream_raw_delta: {\n executionId: required(decodeString),\n conversationId: optional(decodeString),\n round: required(nonNegativeInteger),\n sequence: required(nonNegativeInteger),\n delta: required(decodeString),\n },\n provider_response_raw: {\n executionId: required(decodeString),\n conversationId: optional(decodeString),\n round: required(nonNegativeInteger),\n effort: optional(decodeString),\n response: required(decodeMessage),\n responseKind: required(decodeString),\n },\n provider_response_normalized: {\n executionId: required(decodeString),\n conversationId: optional(decodeString),\n round: required(nonNegativeInteger),\n response: required(decodeMessage),\n toolCallsCount: optional(nonNegativeInteger),\n },\n structured_output_transport: {\n executionId: required(decodeString),\n conversationId: optional(decodeString),\n round: required(nonNegativeInteger),\n provider: required(decodeString),\n model: required(decodeString),\n mechanism: required(literal('response_schema', 'json_object', 'none')),\n provenance: required(literal('catalog', 'vendor-default', 'undeclared', 'unverified-endpoint')),\n sent: required(decodeBoolean),\n schemaInPrompt: required(decodeBoolean),\n reason: optional(decodeString),\n },\n provider_fallback: {\n executionId: required(decodeString),\n conversationId: optional(decodeString),\n round: required(nonNegativeInteger),\n fromProvider: required(decodeString),\n fromModel: required(decodeString),\n toProvider: required(decodeString),\n toModel: required(decodeString),\n reason: required(decodeString),\n },\n assistant_message_committed: {\n executionId: required(decodeString),\n conversationId: optional(decodeString),\n round: required(nonNegativeInteger),\n message: required(committedAssistant),\n },\n tool_execution_request: {\n executionId: required(decodeString),\n conversationId: optional(decodeString),\n round: required(nonNegativeInteger),\n batchId: optional(decodeString),\n index: optional(nonNegativeInteger),\n toolName: required(decodeString),\n toolCallId: required(decodeString),\n parameters: required(record),\n ownerPath: optional(ownerPath),\n },\n tool_execution_result: {\n executionId: required(decodeString),\n conversationId: optional(decodeString),\n round: required(nonNegativeInteger),\n batchId: optional(decodeString),\n index: optional(nonNegativeInteger),\n toolName: optional(decodeString),\n toolCallId: optional(decodeString),\n success: required(decodeBoolean),\n result: optional(json),\n error: optional(decodeString),\n metadata: optional(record),\n },\n tool_batch_started: {\n executionId: required(decodeString),\n conversationId: optional(decodeString),\n round: required(nonNegativeInteger),\n batchId: optional(decodeString),\n mode: required(literal('parallel', 'sequential')),\n maxConcurrency: required(nonNegativeInteger),\n requestCount: required(nonNegativeInteger),\n tools: required(strings),\n },\n tool_message_committed: {\n executionId: required(decodeString),\n conversationId: optional(decodeString),\n round: required(nonNegativeInteger),\n batchId: optional(decodeString),\n index: optional(nonNegativeInteger),\n message: required(decodeMessage),\n },\n background_task_event: {\n backgroundEventType: optional(decodeString),\n backgroundEvent: optional(decodeBackgroundTaskEvent),\n data: optional(decodeBackgroundTaskEvent),\n taskId: optional(decodeString),\n originToolCallId: optional(decodeString),\n },\n background_job_group_event: {\n backgroundJobGroupEvent: optional(decodeBackgroundJobGroupEvent),\n data: optional(decodeBackgroundJobGroupEvent),\n },\n memory_event: { memoryEvent: optional(decodeMemoryEvent), data: optional(decodeMemoryEvent) },\n user: { content: required(decodeString) },\n pre_run: {\n historyLength: required(nonNegativeInteger),\n historyChars: required(nonNegativeInteger),\n historyEstTokens: required(nonNegativeInteger),\n input: required(decodeString),\n history: required(messages),\n model: required(decodeString),\n provider: required(decodeString),\n maxTokens: required(decodeNumber),\n nativeWebSearchSupported: required(decodeBoolean),\n nativeWebSearchEnabled: required(decodeBoolean),\n nativeWebFetchSupported: required(decodeBoolean),\n nativeWebFetchEnabled: required(decodeBoolean),\n },\n text_delta: { delta: required(decodeString) },\n assistant: {\n content: required(decodeString),\n historyLength: required(nonNegativeInteger),\n estimatedChars: required(nonNegativeInteger),\n history: required(messages),\n historyStructure: required(historyStructure),\n },\n tool_call: { tool: required(decodeString), args: required(record) },\n tool_result: {\n tool: required(decodeString),\n success: required(decodeBoolean),\n dataChars: required(nonNegativeInteger),\n truncated: required(decodeBoolean),\n },\n tool_blocked: { tool: required(decodeString), reason: required(decodeString) },\n tool_denied: { tool: required(decodeString), reason: required(decodeString) },\n server_tool: { tool: required(decodeString) },\n} satisfies Record<TSessionLogEventName, TPayloadShape>;\n","import { SESSION_LOG_EVENT, SESSION_LOG_SCHEMA_VERSION } from '../session-log-events.js';\nimport { checkContainerIntegrity, isPlainRecord, json } from './field-decoders.js';\nimport { PAYLOADS } from './payload-shapes.js';\nimport { atKey, describeValue } from '../session-record-codec/decode-outcome.js';\nimport { decodeString, decodeTimestampString } from '../session-record-codec/scalars.js';\n\nimport type { TSessionLogEventName } from '../session-log-events.js';\nimport type { ISessionLogEntry } from '../session-log-entry-types.js';\nimport type { TPayloadShape } from './field-decoders.js';\nimport type { TDecodeIssues } from '../session-record-codec/decode-outcome.js';\nimport type { TUniversalMessage, TUniversalValue } from '@robota-sdk/agent-core';\nimport type { ISessionRecordDecodeIssue } from '@robota-sdk/agent-interface-session';\n\nexport { SESSION_LOG_SCHEMA_VERSION } from '../session-log-events.js';\n\nexport type TSessionLogDecodeErrorCode = 'INVALID_JSON' | 'INVALID_EVENT' | 'UNSUPPORTED_VERSION';\n\ntype TDecodedEvent<TName extends TSessionLogEventName> = ISessionLogEntry & { event: TName };\nexport type TDecodedSessionLogEntry =\n | (TDecodedEvent<'provider_response_normalized'> & { response: TUniversalMessage })\n | (TDecodedEvent<'history_mutation'> & { mutation: 'append_message'; message: TUniversalMessage })\n | (TDecodedEvent<'assistant_message_committed'> & { message: TUniversalMessage | string })\n | (TDecodedEvent<'tool_message_committed'> & { message: TUniversalMessage })\n | (TDecodedEvent<'background_task_event'> & { backgroundEvent?: object; data?: object })\n | (TDecodedEvent<'background_job_group_event'> & {\n backgroundJobGroupEvent?: object;\n data?: object;\n })\n | (TDecodedEvent<'memory_event'> & { memoryEvent?: object; data?: object })\n | TDecodedEvent<\n Exclude<\n TSessionLogEventName,\n | 'provider_response_normalized'\n | 'history_mutation'\n | 'assistant_message_committed'\n | 'tool_message_committed'\n | 'background_task_event'\n | 'background_job_group_event'\n | 'memory_event'\n >\n >;\n\nexport class SessionLogDecodeError extends Error {\n readonly code: TSessionLogDecodeErrorCode;\n readonly issues: readonly ISessionRecordDecodeIssue[];\n readonly schemaVersion?: number;\n\n constructor(\n code: TSessionLogDecodeErrorCode,\n issues: readonly ISessionRecordDecodeIssue[],\n options: { schemaVersion?: number; cause?: unknown } = {},\n ) {\n super(\n `Session log decode failed: ${code}${issues[0] ? ` at ${issues[0].path}: ${issues[0].message}` : ''}`,\n { cause: options.cause },\n );\n this.name = 'SessionLogDecodeError';\n this.code = code;\n this.issues = issues;\n if (options.schemaVersion !== undefined) this.schemaVersion = options.schemaVersion;\n }\n}\n\ninterface IEntryDecodeResult {\n entry?: TDecodedSessionLogEntry;\n unsupported?: number;\n}\n\ninterface IEnvelopeDecodeResult {\n output: Record<string, TUniversalValue>;\n event?: TSessionLogEventName;\n unsupported?: number;\n}\n\nconst EVENTS = new Set<string>(Object.values(SESSION_LOG_EVENT));\n\n/** Validate the full input before returning any entry to replay consumers. */\nexport function decodeSessionLogEntries(\n entries: unknown,\n options: { lineNumbers?: readonly number[] } = {},\n): TDecodedSessionLogEntry[] {\n if (!Array.isArray(entries))\n throw new SessionLogDecodeError('INVALID_EVENT', [\n { path: 'entries', message: `expected an array, received ${describeValue(entries)}` },\n ]);\n const issues: TDecodeIssues = [];\n const decoded: TDecodedSessionLogEntry[] = [];\n let unsupported: number | undefined;\n for (const [index, value] of entries.entries()) {\n const path =\n options.lineNumbers?.[index] === undefined\n ? `[${index}]`\n : `line ${options.lineNumbers[index]}`;\n const result = decodeEntry(value, path, issues);\n if (result.entry !== undefined) decoded.push(result.entry);\n unsupported ??= result.unsupported;\n }\n if (issues.length > 0)\n throw new SessionLogDecodeError(\n unsupported === undefined ? 'INVALID_EVENT' : 'UNSUPPORTED_VERSION',\n issues,\n { schemaVersion: unsupported },\n );\n return decoded;\n}\n\nfunction decodeEntry(value: unknown, path: string, issues: TDecodeIssues): IEntryDecodeResult {\n if (!isPlainRecord(value)) {\n issues.push({ path, message: `expected an object, received ${describeValue(value)}` });\n return {};\n }\n const issuesBeforeIntegrity = issues.length;\n checkContainerIntegrity(value, path, issues);\n // Recursive record decoders may not carry a cycle guard. Never enter them after this preflight fails.\n if (issues.length !== issuesBeforeIntegrity) return {};\n const envelope = decodeEnvelope(value, path, issues);\n if (envelope.event === undefined) return { unsupported: envelope.unsupported };\n decodePayload(value, envelope.event, path, envelope.output, issues);\n return { entry: envelope.output as TDecodedSessionLogEntry, unsupported: envelope.unsupported };\n}\n\nfunction decodeEnvelope(\n raw: Record<string, unknown>,\n path: string,\n issues: TDecodeIssues,\n): IEnvelopeDecodeResult {\n const output: Record<string, TUniversalValue> = { schemaVersion: SESSION_LOG_SCHEMA_VERSION };\n let unsupported: number | undefined;\n if (raw['schemaVersion'] !== SESSION_LOG_SCHEMA_VERSION) {\n const version = raw['schemaVersion'];\n if (typeof version === 'number' && Number.isSafeInteger(version) && version >= 0) {\n unsupported = version;\n issues.push({ path: atKey(path, 'schemaVersion'), message: 'unsupported schema version' });\n } else {\n issues.push({\n path: atKey(path, 'schemaVersion'),\n message: `expected schema version 1, received ${describeValue(version)}`,\n });\n }\n }\n const timestamp = decodeTimestampString(raw['timestamp'], atKey(path, 'timestamp'), issues);\n if (timestamp !== undefined) output['timestamp'] = timestamp;\n const sessionId = decodeString(raw['sessionId'], atKey(path, 'sessionId'), issues);\n if (sessionId === '')\n issues.push({ path: atKey(path, 'sessionId'), message: 'expected a non-empty session ID' });\n if (sessionId !== undefined) output['sessionId'] = sessionId;\n const event = raw['event'];\n if (typeof event !== 'string' || !EVENTS.has(event)) {\n issues.push({\n path: atKey(path, 'event'),\n message: `expected a declared event name, received ${describeValue(event)}`,\n });\n return { output, unsupported };\n }\n output['event'] = event;\n return { output, event: event as TSessionLogEventName, unsupported };\n}\n\nfunction decodePayload(\n raw: Record<string, unknown>,\n event: TSessionLogEventName,\n path: string,\n output: Record<string, TUniversalValue>,\n issues: TDecodeIssues,\n): void {\n const shape: TPayloadShape = PAYLOADS[event];\n for (const [key, field] of Object.entries(shape)) {\n if (!Object.hasOwn(raw, key) && field.optional) continue;\n const member = field.decode(raw[key], atKey(path, key), issues);\n if (member !== undefined) output[key] = member as TUniversalValue;\n }\n for (const key of Object.keys(raw)) {\n if (['schemaVersion', 'timestamp', 'sessionId', 'event', ...Object.keys(shape)].includes(key))\n continue;\n if (event !== 'server_tool') {\n issues.push({ path: atKey(path, key), message: 'unknown event payload field' });\n continue;\n }\n const member = json(raw[key], atKey(path, key), issues);\n if (member !== undefined) output[key] = member;\n }\n requireAuxiliaryPayload(raw, event, path, issues);\n}\n\nfunction requireAuxiliaryPayload(\n raw: Record<string, unknown>,\n event: TSessionLogEventName,\n path: string,\n issues: TDecodeIssues,\n): void {\n if (\n event === 'background_task_event' &&\n raw['backgroundEvent'] === undefined &&\n raw['data'] === undefined\n )\n issues.push({ path, message: 'expected a background task event payload' });\n if (\n event === 'background_job_group_event' &&\n raw['backgroundJobGroupEvent'] === undefined &&\n raw['data'] === undefined\n )\n issues.push({ path, message: 'expected a background job group event payload' });\n if (event === 'memory_event' && raw['memoryEvent'] === undefined && raw['data'] === undefined)\n issues.push({ path, message: 'expected a memory event payload' });\n}\n","import type { IExternalPayloadSource } from './external-payload-source-types.js';\n\nexport type TSessionLogPayloadResolutionErrorCode =\n | 'INVALID_LIMIT'\n | 'INVALID_REFERENCE'\n | 'UNRESOLVED_REFERENCE'\n | 'OUTSIDE_ROOT'\n | 'PAYLOAD_NOT_FOUND'\n | 'STABLE_PAYLOAD_READ_UNAVAILABLE'\n | 'PAYLOAD_UNREADABLE'\n | 'BYTE_LENGTH_MISMATCH'\n | 'SHA256_MISMATCH'\n | 'INVALID_JSON'\n | 'MAX_DEPTH_EXCEEDED'\n | 'MAX_TOTAL_BYTES_EXCEEDED'\n | 'CIRCULAR_REFERENCE';\n\nexport interface ISessionLogPayloadResolutionOptions {\n readonly source?: IExternalPayloadSource;\n readonly maxDepth?: number;\n readonly maxTotalBytes?: number;\n}\n\nexport interface ISessionLogPayloadResolutionErrorMetadata {\n readonly relativePath?: string;\n readonly resolvedPath?: string;\n readonly depth?: number;\n readonly expected?: string | number;\n readonly actual?: string | number;\n}\n\nexport class SessionLogPayloadResolutionError extends Error {\n readonly code: TSessionLogPayloadResolutionErrorCode;\n readonly metadata: Readonly<ISessionLogPayloadResolutionErrorMetadata>;\n\n constructor(\n code: TSessionLogPayloadResolutionErrorCode,\n message: string,\n metadata: ISessionLogPayloadResolutionErrorMetadata = {},\n cause?: unknown,\n ) {\n super(message, cause === undefined ? undefined : { cause });\n this.name = 'SessionLogPayloadResolutionError';\n this.code = code;\n this.metadata = metadata;\n }\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { dirname, isAbsolute, resolve, win32 } from 'node:path';\n\nimport {\n createStableRootedFileReader,\n StableFileAuthorityError,\n} from '@robota-sdk/agent-file-authority';\n\nimport { SessionLogPayloadResolutionError } from './external-payload-resolution-contracts.js';\n\nimport type {\n IExternalPayloadSource,\n ISessionLogSource,\n} from './external-payload-source-types.js';\n\nexport type {\n IExternalPayloadSource,\n ISessionLogSource,\n} from './external-payload-source-types.js';\n\nfunction payloadPathSegments(relativePath: string): readonly string[] {\n if (\n relativePath.trim().length === 0 ||\n relativePath.includes('\\0') ||\n isAbsolute(relativePath) ||\n win32.isAbsolute(relativePath)\n ) {\n throw outsideRootError(relativePath);\n }\n const segments = relativePath.split(/[\\\\/]+/u);\n if (segments.some((segment) => segment === '.' || segment === '..')) {\n throw outsideRootError(relativePath);\n }\n return segments;\n}\n\nfunction validateMaxBytes(maxBytes: number): void {\n if (!Number.isFinite(maxBytes) || !Number.isSafeInteger(maxBytes) || maxBytes < 0) {\n throw new SessionLogPayloadResolutionError(\n 'INVALID_LIMIT',\n 'External-payload maxBytes must be a finite, non-negative safe integer.',\n { actual: String(maxBytes) },\n );\n }\n}\n\nfunction outsideRootError(relativePath: string, cause?: Error): SessionLogPayloadResolutionError {\n return new SessionLogPayloadResolutionError(\n 'OUTSIDE_ROOT',\n `External payload path escapes its base directory or contains a link: ${relativePath}.`,\n { relativePath },\n cause,\n );\n}\n\nfunction mapStableFileAuthorityError(\n error: StableFileAuthorityError,\n relativePath: string,\n maxBytes: number,\n): SessionLogPayloadResolutionError {\n if (error.code === 'INVALID_PATH' || error.code === 'UNSAFE_ENTRY') {\n return outsideRootError(relativePath, error);\n }\n if (error.code === 'UNSUPPORTED_BACKEND') {\n return new SessionLogPayloadResolutionError(\n 'STABLE_PAYLOAD_READ_UNAVAILABLE',\n 'Stable root-relative external-payload reads are unavailable on this host.',\n { relativePath },\n error,\n );\n }\n if (error.code === 'OVER_BUDGET') {\n return new SessionLogPayloadResolutionError(\n 'MAX_TOTAL_BYTES_EXCEEDED',\n `External payload exceeds the remaining byte budget of ${maxBytes}.`,\n { relativePath, expected: maxBytes },\n error,\n );\n }\n return new SessionLogPayloadResolutionError(\n 'PAYLOAD_UNREADABLE',\n `External payload could not be read: ${relativePath}.`,\n { relativePath },\n error,\n );\n}\n\n/** Explicit host-filesystem adapter. A file path is never accepted by the neutral parser itself. */\nexport class NodeExternalPayloadSource implements IExternalPayloadSource {\n private readonly baseDirectory: string;\n\n constructor(baseDirectory: string) {\n if (baseDirectory.trim().length === 0) {\n throw new Error('External-payload base directory must not be empty.');\n }\n this.baseDirectory = resolve(baseDirectory);\n }\n\n readBytes(relativePath: string, maxBytes: number): Uint8Array | undefined {\n validateMaxBytes(maxBytes);\n const segments = payloadPathSegments(relativePath);\n try {\n const reader = createStableRootedFileReader(this.baseDirectory);\n try {\n return reader.readBytes(segments, maxBytes);\n } finally {\n reader.close();\n }\n } catch (error) {\n if (error instanceof SessionLogPayloadResolutionError) throw error;\n if (error instanceof StableFileAuthorityError) {\n throw mapStableFileAuthorityError(error, relativePath, maxBytes);\n }\n throw new SessionLogPayloadResolutionError(\n 'PAYLOAD_UNREADABLE',\n `External payload could not be read: ${relativePath}.`,\n { relativePath },\n error,\n );\n }\n }\n}\n\n/** Explicit host-filesystem adapter for a JSONL session log. */\nexport class NodeSessionLogSource implements ISessionLogSource {\n readonly externalPayloadSource: IExternalPayloadSource;\n\n constructor(private readonly logFile: string) {\n if (logFile.trim().length === 0) {\n throw new Error('Session log-file path must not be empty.');\n }\n this.externalPayloadSource = new NodeExternalPayloadSource(dirname(logFile));\n }\n\n readText(): string | undefined {\n return existsSync(this.logFile) ? readFileSync(this.logFile, 'utf8') : undefined;\n }\n}\n","import { randomBytes } from 'node:crypto';\nimport {\n chmodSync,\n closeSync,\n constants,\n fsyncSync,\n linkSync,\n lstatSync,\n mkdtempSync,\n openSync,\n readdirSync,\n rmdirSync,\n unlinkSync,\n writeFileSync,\n} from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\n\nimport { NodeExternalPayloadSource } from './session-log-sources.js';\n\nimport type { IToolResultSpillStore } from '@robota-sdk/agent-core';\n\nconst REFERENCE = /^tool-result:([A-Za-z0-9_-]{22,64})$/u;\nconst DEFAULT_RETENTION_MS = 60 * 60 * 1000;\nconst MAX_SPILL_BYTES = 8 * 1024 * 1024;\nconst FILE_FLAGS =\n constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | (constants.O_NOFOLLOW ?? 0);\n\nexport type TToolResultSpillErrorCode =\n | 'invalid-options'\n | 'unsafe-root'\n | 'write-failed'\n | 'missing'\n | 'expired'\n | 'invalid-reference'\n | 'read-failed'\n | 'cleanup-failed'\n | 'closed';\n\nexport class ToolResultSpillError extends Error {\n constructor(readonly code: TToolResultSpillErrorCode) {\n super(`Tool result spill failed (${code})`);\n this.name = 'ToolResultSpillError';\n }\n}\n\nexport interface INodeToolResultSpillStoreOptions {\n /** Host-owned parent; defaults to the operating-system temporary directory. */\n readonly parentDirectory?: string;\n readonly retentionMs?: number;\n readonly now?: () => number;\n /** Receives a fixed, payload-free reason if an idle expiry timer cannot delete a file. */\n readonly onCleanupFailure?: (reason: 'cleanup-failed') => void;\n}\n\ninterface IStoredResult {\n readonly fileName: string;\n readonly expiresAt: number;\n}\n\n/** Node host implementation of core's opaque, session-lifetime spill port. */\nexport class NodeToolResultSpillStore implements IToolResultSpillStore {\n private readonly directory: string;\n private readonly retentionMs: number;\n private readonly now: () => number;\n private readonly onCleanupFailure?: (reason: 'cleanup-failed') => void;\n private readonly entries = new Map<string, IStoredResult>();\n private expiryTimer?: ReturnType<typeof setTimeout>;\n private closed = false;\n\n constructor(options: INodeToolResultSpillStoreOptions = {}) {\n const parent = options.parentDirectory ?? tmpdir();\n this.retentionMs = options.retentionMs ?? DEFAULT_RETENTION_MS;\n this.now = options.now ?? Date.now;\n this.onCleanupFailure = options.onCleanupFailure;\n if (!Number.isSafeInteger(this.retentionMs) || this.retentionMs <= 0) {\n throw new ToolResultSpillError('invalid-options');\n }\n try {\n const stat = lstatSync(parent);\n if (!stat.isDirectory() || stat.isSymbolicLink()) throw new Error('unsafe parent');\n this.directory = mkdtempSync(join(parent, 'agent-tool-results-'));\n if (process.platform !== 'win32') chmodSync(this.directory, 0o700);\n this.assertRoot();\n } catch {\n throw new ToolResultSpillError('unsafe-root');\n }\n }\n\n private assertRoot(): void {\n try {\n const stat = lstatSync(this.directory);\n if (!stat.isDirectory() || stat.isSymbolicLink()) throw new Error('unsafe directory');\n if (process.platform !== 'win32' && (stat.mode & 0o077) !== 0) {\n throw new Error('directory is not owner-only');\n }\n } catch {\n throw new ToolResultSpillError('unsafe-root');\n }\n }\n\n private scheduleExpiry(minimumDelayMs = 1): void {\n if (this.expiryTimer !== undefined) clearTimeout(this.expiryTimer);\n this.expiryTimer = undefined;\n if (this.closed || this.entries.size === 0) return;\n let firstExpiry = Number.POSITIVE_INFINITY;\n for (const entry of this.entries.values()) {\n if (entry.expiresAt < firstExpiry) firstExpiry = entry.expiresAt;\n }\n const delay = Math.min(2_147_483_647, Math.max(minimumDelayMs, firstExpiry - this.now()));\n this.expiryTimer = setTimeout(() => {\n this.expiryTimer = undefined;\n void this.cleanupExpired().catch(() => {\n try {\n if (this.onCleanupFailure) this.onCleanupFailure('cleanup-failed');\n else process.emitWarning('Tool result spill expiry cleanup failed (cleanup-failed)');\n } catch {\n process.emitWarning('Tool result spill expiry cleanup failed (cleanup-failed)');\n }\n this.scheduleExpiry(60_000);\n });\n }, delay);\n this.expiryTimer.unref?.();\n }\n\n async write(content: string): Promise<{ readonly reference: string }> {\n if (this.closed) throw new ToolResultSpillError('closed');\n await this.cleanupExpired();\n this.assertRoot();\n if (Buffer.byteLength(content, 'utf8') > MAX_SPILL_BYTES) {\n throw new ToolResultSpillError('write-failed');\n }\n const token = randomBytes(18).toString('base64url');\n const tempName = `${randomBytes(18).toString('base64url')}.partial`;\n const fileName = `${token}.txt`;\n const tempPath = join(this.directory, tempName);\n const finalPath = join(this.directory, fileName);\n let fd: number | undefined;\n let linked = false;\n try {\n fd = openSync(tempPath, FILE_FLAGS, 0o600);\n writeFileSync(fd, content, 'utf8');\n fsyncSync(fd);\n closeSync(fd);\n fd = undefined;\n linkSync(tempPath, finalPath);\n linked = true;\n unlinkSync(tempPath);\n const reference = `tool-result:${token}`;\n this.entries.set(reference, { fileName, expiresAt: this.now() + this.retentionMs });\n this.scheduleExpiry();\n return { reference };\n } catch {\n let cleanupFailed = false;\n if (fd !== undefined) {\n try {\n closeSync(fd);\n } catch {\n cleanupFailed = true;\n }\n }\n try {\n unlinkSync(tempPath);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') cleanupFailed = true;\n }\n if (linked) {\n try {\n unlinkSync(finalPath);\n } catch {\n cleanupFailed = true;\n }\n }\n throw new ToolResultSpillError(cleanupFailed ? 'cleanup-failed' : 'write-failed');\n }\n }\n\n async read(reference: string): Promise<string> {\n if (this.closed) throw new ToolResultSpillError('closed');\n if (!REFERENCE.test(reference)) throw new ToolResultSpillError('invalid-reference');\n const entry = this.entries.get(reference);\n if (!entry) throw new ToolResultSpillError('missing');\n if (this.now() >= entry.expiresAt) {\n this.removeEntry(reference, entry);\n this.scheduleExpiry();\n throw new ToolResultSpillError('expired');\n }\n this.assertRoot();\n try {\n const bytes = new NodeExternalPayloadSource(this.directory).readBytes(\n entry.fileName,\n MAX_SPILL_BYTES,\n );\n if (bytes === undefined) throw new ToolResultSpillError('missing');\n return Buffer.from(bytes).toString('utf8');\n } catch (error) {\n if (error instanceof ToolResultSpillError) throw error;\n throw new ToolResultSpillError('read-failed');\n }\n }\n\n private removeEntry(reference: string, entry: IStoredResult): void {\n this.assertRoot();\n try {\n unlinkSync(join(this.directory, entry.fileName));\n this.entries.delete(reference);\n } catch {\n throw new ToolResultSpillError('cleanup-failed');\n }\n }\n\n async cleanupExpired(): Promise<void> {\n if (this.closed) throw new ToolResultSpillError('closed');\n for (const [reference, entry] of this.entries) {\n if (this.now() >= entry.expiresAt) this.removeEntry(reference, entry);\n }\n this.scheduleExpiry();\n }\n\n async shutdown(): Promise<void> {\n if (this.closed) return;\n if (this.expiryTimer !== undefined) clearTimeout(this.expiryTimer);\n this.expiryTimer = undefined;\n for (const [reference, entry] of this.entries) this.removeEntry(reference, entry);\n this.assertRoot();\n try {\n if (readdirSync(this.directory).length !== 0) throw new Error('unexpected files');\n rmdirSync(this.directory);\n this.closed = true;\n } catch {\n throw new ToolResultSpillError('cleanup-failed');\n }\n }\n}\n","/**\n * SCREEN-1993 — the prompt-history file at the path the host names (its user-level `history.jsonl`):\n * one JSON object per line, append-only, owner-only. A derived projection of what the session record\n * already holds, kept so the terminal UI can search prompts across sessions and projects without\n * decoding a record.\n *\n * Writes follow the `NodeSessionLogSink` regime (SEC-020): the directory is made owner-only when the\n * file is constructed, and every append tightens the file before writing with the owner-only mode.\n * Reads walk the file BACKWARDS in fixed blocks so the newest prompts are yielded first and the UI\n * can render them before the rest is read; the file is opened no-follow like every other session\n * file this package reads.\n */\nimport { appendFileSync, closeSync, constants, fstatSync, openSync, readSync } from 'node:fs';\nimport { dirname } from 'node:path';\n\nimport {\n OWNER_ONLY_FILE_MODE,\n ensureOwnerOnlyDirectory,\n tightenExistingFile,\n} from '@robota-sdk/agent-core/node';\n\nimport type { TUniversalValue } from '@robota-sdk/agent-core';\nimport type {\n IPromptHistoryBlock,\n IPromptHistoryEntry,\n IPromptHistoryReadOptions,\n IPromptHistorySource,\n IPromptHistoryWriter,\n} from '@robota-sdk/agent-interface-session';\n\nconst KIB = 1024;\nconst DEFAULT_BLOCK_KIB = 64;\n/** 64 KiB: a few hundred prompts per block — enough for the first frame, small enough to yield often. */\nexport const DEFAULT_PROMPT_HISTORY_BLOCK_BYTES = DEFAULT_BLOCK_KIB * KIB;\nconst NEWLINE = 0x0a;\n\nexport interface INodePromptHistoryFileOptions {\n /** An ancestor of the file the host also owns, tightened along with the directory (SEC-020). */\n readonly ownedRoot?: string;\n /** Test seam: the read block size in bytes. */\n readonly blockBytes?: number;\n}\n\nfunction isEntry(value: TUniversalValue): value is IPromptHistoryEntry & TUniversalValue {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return false;\n const record = value as Record<string, TUniversalValue>;\n return (\n typeof record.at === 'string' &&\n typeof record.sessionId === 'string' &&\n typeof record.project === 'string' &&\n typeof record.text === 'string'\n );\n}\n\n/** One line → an entry, or `undefined` when the line is not a well-formed entry. */\nexport function parsePromptHistoryLine(line: string): IPromptHistoryEntry | undefined {\n if (line.trim().length === 0) return undefined;\n let parsed: TUniversalValue;\n try {\n parsed = JSON.parse(line) as TUniversalValue;\n } catch {\n // allow-fallback: a line JSON cannot parse is exactly what `skippedLines` counts for the caller.\n return undefined;\n }\n if (!isEntry(parsed)) return undefined;\n return { at: parsed.at, sessionId: parsed.sessionId, project: parsed.project, text: parsed.text };\n}\n\nfunction blockOf(lines: readonly string[]): IPromptHistoryBlock {\n const entries: IPromptHistoryEntry[] = [];\n let skippedLines = 0;\n for (const line of lines) {\n if (line.length === 0) continue;\n const entry = parsePromptHistoryLine(line);\n if (entry === undefined) skippedLines += 1;\n else entries.push(entry);\n }\n return { entries, skippedLines };\n}\n\nfunction isMissingFile(error: Error): boolean {\n return (error as NodeJS.ErrnoException).code === 'ENOENT';\n}\n\nexport class NodePromptHistoryFile implements IPromptHistoryWriter, IPromptHistorySource {\n private readonly blockBytes: number;\n private readonly ownedRoot: string | undefined;\n\n constructor(\n private readonly path: string,\n options: INodePromptHistoryFileOptions = {},\n ) {\n this.blockBytes = options.blockBytes ?? DEFAULT_PROMPT_HISTORY_BLOCK_BYTES;\n this.ownedRoot = options.ownedRoot;\n }\n\n append(entry: IPromptHistoryEntry): void {\n const directory = dirname(this.path);\n ensureOwnerOnlyDirectory(\n directory,\n this.ownedRoot === undefined ? {} : { withinRoot: this.ownedRoot },\n );\n // SEC-020: `mode` applies only when the file is created; tightening first repairs an older file.\n tightenExistingFile(this.path);\n appendFileSync(this.path, `${JSON.stringify(entry)}\\n`, { mode: OWNER_ONLY_FILE_MODE });\n }\n\n /**\n * Newest-first blocks. Only a missing file is the empty state (a fresh install, or history off);\n * any other open or read failure is thrown so the surface renders it instead of an empty list.\n */\n async *read(options: IPromptHistoryReadOptions): AsyncIterable<IPromptHistoryBlock> {\n let descriptor: number;\n try {\n descriptor = openSync(this.path, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));\n } catch (error) {\n if (error instanceof Error && isMissingFile(error)) return;\n throw error;\n }\n try {\n let position = fstatSync(descriptor).size;\n // The bytes after the last newline seen so far: a line the previous block cut in half.\n let carry = Buffer.alloc(0);\n while (position > 0 && !options.signal.aborted) {\n const length = Math.min(this.blockBytes, position);\n position -= length;\n const chunk = Buffer.alloc(length);\n const bytesRead = readSync(descriptor, chunk, 0, length, position);\n const buffer = Buffer.concat([chunk.subarray(0, bytesRead), carry]);\n const firstNewline = position === 0 ? -1 : buffer.indexOf(NEWLINE);\n // Everything before the first newline belongs to a line that continues in the block before.\n carry = firstNewline === -1 && position > 0 ? buffer : buffer.subarray(0, firstNewline + 1);\n const complete =\n position === 0\n ? buffer\n : firstNewline === -1\n ? Buffer.alloc(0)\n : buffer.subarray(firstNewline + 1);\n if (position > 0 && firstNewline === -1) continue;\n const lines = complete.toString('utf8').split('\\n').reverse();\n yield blockOf(lines);\n }\n } finally {\n closeSync(descriptor);\n }\n }\n}\n","import { SessionLogPayloadResolutionError } from './external-payload-resolution-contracts.js';\n\nimport type { IExternalPayloadReference } from './session-logger.js';\n\nconst SHA256_PATTERN = /^[0-9a-f]{64}$/i;\n\nexport function validateExternalPayloadReference(\n value: Record<string, unknown>,\n): IExternalPayloadReference {\n if (!isValidSessionLogExternalPayloadReference(value)) {\n throw new SessionLogPayloadResolutionError(\n 'INVALID_REFERENCE',\n 'External payload reference has an invalid shape.',\n );\n }\n return {\n kind: 'external-payload',\n encoding: 'json',\n sha256: String(value.sha256).toLowerCase(),\n byteLength: Number(value.byteLength),\n relativePath: String(value.relativePath),\n };\n}\n\n/** Internal SSOT shared by the resolver and raw-log validator; not part of the package barrel. */\nexport function isValidSessionLogExternalPayloadReference(value: Record<string, unknown>): boolean {\n const keys = Object.keys(value).sort();\n const expectedKeys = ['byteLength', 'encoding', 'kind', 'relativePath', 'sha256'];\n return (\n keys.length === expectedKeys.length &&\n keys.every((key, index) => key === expectedKeys[index]) &&\n value.kind === 'external-payload' &&\n value.encoding === 'json' &&\n typeof value.sha256 === 'string' &&\n SHA256_PATTERN.test(value.sha256) &&\n typeof value.byteLength === 'number' &&\n Number.isSafeInteger(value.byteLength) &&\n value.byteLength >= 0 &&\n typeof value.relativePath === 'string' &&\n value.relativePath.trim().length > 0\n );\n}\n","import { createHash } from 'node:crypto';\n\nimport { validateExternalPayloadReference } from './external-payload-file-reader.js';\nimport { SessionLogPayloadResolutionError } from './external-payload-resolution-contracts.js';\n\nimport type { ISessionLogPayloadResolutionOptions } from './external-payload-resolution-contracts.js';\nimport type { IExternalPayloadSource } from './session-log-sources.js';\nimport type { IExternalPayloadReference } from './session-logger.js';\n\nexport { SessionLogPayloadResolutionError } from './external-payload-resolution-contracts.js';\nexport type {\n ISessionLogPayloadResolutionErrorMetadata,\n ISessionLogPayloadResolutionOptions,\n TSessionLogPayloadResolutionErrorCode,\n} from './external-payload-resolution-contracts.js';\n\nconst DEFAULT_MAX_DEPTH = 32;\nconst DEFAULT_MAX_TOTAL_MIB = 64;\nconst BYTES_PER_KIB = 1024;\nconst KIB_PER_MIB = 1024;\nconst DEFAULT_MAX_TOTAL_BYTES = DEFAULT_MAX_TOTAL_MIB * KIB_PER_MIB * BYTES_PER_KIB;\n\ninterface IResolutionState {\n readonly maxDepth: number;\n readonly maxTotalBytes: number;\n readonly source: IExternalPayloadSource | undefined;\n totalBytes: number;\n readonly activePayloadPaths: Set<string>;\n readonly activeObjects: WeakSet<object>;\n}\n\n/**\n * Hydrate every external JSON payload reference in one value using one aggregate budget.\n * The input is treated as untrusted and the returned graph contains only JSON-compatible values.\n */\nexport function resolveSessionLogExternalPayloads(\n value: unknown,\n options: ISessionLogPayloadResolutionOptions,\n): unknown {\n const state = createResolutionState(options);\n return resolveValue(value, state, 0);\n}\n\nfunction createResolutionState(options: ISessionLogPayloadResolutionOptions): IResolutionState {\n const maxDepth = validateLimit('maxDepth', options.maxDepth ?? DEFAULT_MAX_DEPTH);\n const maxTotalBytes = validateLimit(\n 'maxTotalBytes',\n options.maxTotalBytes ?? DEFAULT_MAX_TOTAL_BYTES,\n );\n return {\n source: options.source,\n maxDepth,\n maxTotalBytes,\n totalBytes: 0,\n activePayloadPaths: new Set<string>(),\n activeObjects: new WeakSet<object>(),\n };\n}\n\nfunction validateLimit(name: string, value: number): number {\n if (!Number.isFinite(value) || !Number.isSafeInteger(value) || value < 0) {\n throw new SessionLogPayloadResolutionError(\n 'INVALID_LIMIT',\n `${name} must be a finite, non-negative safe integer.`,\n { actual: String(value) },\n );\n }\n return value;\n}\n\nfunction resolveValue(value: unknown, state: IResolutionState, referenceDepth: number): unknown {\n if (\n value === null ||\n typeof value === 'string' ||\n typeof value === 'boolean' ||\n (typeof value === 'number' && Number.isFinite(value))\n ) {\n return value;\n }\n if (typeof value === 'number') {\n throw invalidJsonValue('Non-finite numbers are not valid session-log JSON values.');\n }\n if (typeof value !== 'object') {\n throw invalidJsonValue(`Unsupported session-log JSON value type: ${typeof value}.`);\n }\n if (state.activeObjects.has(value)) {\n throw new SessionLogPayloadResolutionError(\n 'CIRCULAR_REFERENCE',\n 'Circular in-memory value encountered while resolving external payloads.',\n { depth: referenceDepth },\n );\n }\n if (isPotentialExternalPayloadReference(value)) {\n return resolveReference(value, state, referenceDepth);\n }\n if (Array.isArray(value)) {\n state.activeObjects.add(value);\n try {\n return value.map((item) => resolveValue(item, state, referenceDepth));\n } finally {\n state.activeObjects.delete(value);\n }\n }\n if (!isPlainRecord(value)) {\n throw invalidJsonValue('Session-log payload objects must be plain JSON records.');\n }\n state.activeObjects.add(value);\n try {\n return Object.fromEntries(\n Object.entries(value).map(([key, child]) => [\n key,\n resolveValue(child, state, referenceDepth),\n ]),\n );\n } finally {\n state.activeObjects.delete(value);\n }\n}\n\nfunction resolveReference(\n value: Record<string, unknown>,\n state: IResolutionState,\n referenceDepth: number,\n): unknown {\n const reference = validateExternalPayloadReference(value);\n if (referenceDepth >= state.maxDepth) {\n throw new SessionLogPayloadResolutionError(\n 'MAX_DEPTH_EXCEEDED',\n `External-payload reference depth exceeds the configured maximum of ${state.maxDepth}.`,\n { relativePath: reference.relativePath, depth: referenceDepth },\n );\n }\n if (state.source === undefined) {\n throw new SessionLogPayloadResolutionError(\n 'UNRESOLVED_REFERENCE',\n 'An external session-log payload requires an explicit payload source.',\n { relativePath: reference.relativePath, depth: referenceDepth },\n );\n }\n if (state.activePayloadPaths.has(reference.relativePath)) {\n throw new SessionLogPayloadResolutionError(\n 'CIRCULAR_REFERENCE',\n `External payload ${reference.relativePath} recursively references an active payload.`,\n { relativePath: reference.relativePath, depth: referenceDepth },\n );\n }\n\n const parsed = readExternalPayloadJson(reference, state);\n state.activePayloadPaths.add(reference.relativePath);\n try {\n return resolveValue(parsed, state, referenceDepth + 1);\n } finally {\n state.activePayloadPaths.delete(reference.relativePath);\n }\n}\n\nfunction readExternalPayloadJson(\n reference: IExternalPayloadReference,\n state: IResolutionState,\n): unknown {\n const remainingBytes = state.maxTotalBytes - state.totalBytes;\n const bytes = state.source?.readBytes(reference.relativePath, remainingBytes);\n if (bytes === undefined) {\n throw new SessionLogPayloadResolutionError(\n 'PAYLOAD_NOT_FOUND',\n `External payload was not found: ${reference.relativePath}.`,\n { relativePath: reference.relativePath },\n );\n }\n const nextTotalBytes = state.totalBytes + bytes.byteLength;\n if (!Number.isSafeInteger(nextTotalBytes) || nextTotalBytes > state.maxTotalBytes) {\n throw new SessionLogPayloadResolutionError(\n 'MAX_TOTAL_BYTES_EXCEEDED',\n `External-payload bytes exceed the configured maximum of ${state.maxTotalBytes}.`,\n { expected: state.maxTotalBytes, actual: nextTotalBytes },\n );\n }\n state.totalBytes = nextTotalBytes;\n if (bytes.byteLength !== reference.byteLength) {\n throw new SessionLogPayloadResolutionError(\n 'BYTE_LENGTH_MISMATCH',\n `External payload byte length does not match its reference: ${reference.relativePath}.`,\n {\n relativePath: reference.relativePath,\n expected: reference.byteLength,\n actual: bytes.byteLength,\n },\n );\n }\n const actualSha256 = createHash('sha256').update(bytes).digest('hex');\n if (actualSha256 !== reference.sha256) {\n throw new SessionLogPayloadResolutionError(\n 'SHA256_MISMATCH',\n `External payload sha256 does not match its reference: ${reference.relativePath}.`,\n { relativePath: reference.relativePath, expected: reference.sha256, actual: actualSha256 },\n );\n }\n try {\n return JSON.parse(Buffer.from(bytes).toString('utf8')) as unknown;\n } catch (error) {\n throw new SessionLogPayloadResolutionError(\n 'INVALID_JSON',\n `External payload is not valid JSON: ${reference.relativePath}.`,\n { relativePath: reference.relativePath },\n error,\n );\n }\n}\n\nfunction invalidJsonValue(message: string): SessionLogPayloadResolutionError {\n return new SessionLogPayloadResolutionError('INVALID_JSON', message);\n}\n\nfunction isPotentialExternalPayloadReference(value: object): value is Record<string, unknown> {\n return !Array.isArray(value) && 'kind' in value && value.kind === 'external-payload';\n}\n\nfunction isPlainRecord(value: object): value is Record<string, unknown> {\n const prototype = Object.getPrototypeOf(value);\n return prototype === Object.prototype || prototype === null;\n}\n","import { isValidSessionLogExternalPayloadReference } from './external-payload-file-reader.js';\nimport { decodeSessionLogEntries } from './session-log-codec/index.js';\n\nimport type { ISessionLogEntry } from './session-log-entry-types.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 | 'UNRESOLVED_REPLAY_PAYLOAD';\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 decodeSessionLogEntries(entries).forEach((entry, index) => {\n collectPayloadReferenceIssues(entry, index, issues);\n collectUnresolvedReplayPayloadIssue(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 (\n entry.event === 'provider_response_normalized' &&\n !containsExternalPayloadReference(entry.response)\n ) {\n events.normalizedResponses.add(key.key);\n }\n}\n\nfunction collectUnresolvedReplayPayloadIssue(\n entry: ISessionLogEntry,\n eventIndex: number,\n issues: ISessionReplayValidationIssue[],\n): void {\n const replayValue =\n entry.event === 'history_mutation'\n ? entry.message\n : entry.event === 'provider_response_normalized'\n ? entry.response\n : undefined;\n if (!containsExternalPayloadReference(replayValue)) return;\n issues.push({\n code: 'UNRESOLVED_REPLAY_PAYLOAD',\n message: `Replay substrate ${entry.event} contains an unresolved external payload.`,\n eventIndex,\n executionId: typeof entry.executionId === 'string' ? entry.executionId : undefined,\n round: typeof entry.round === 'number' ? entry.round : undefined,\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 (!isValidSessionLogExternalPayloadReference(value)) {\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 containsExternalPayloadReference(value: TUniversalValue): boolean {\n if (Array.isArray(value)) {\n return value.some((item) => containsExternalPayloadReference(item));\n }\n if (!isRecord(value)) return false;\n if (value.kind === 'external-payload') return true;\n return Object.values(value).some((child) => containsExternalPayloadReference(child));\n}\n\nfunction isRecord(value: TUniversalValue): value is Record<string, TUniversalValue> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n","import { messageToHistoryEntry } from '@robota-sdk/agent-core';\n\nimport { resolveSessionLogExternalPayloads } from './external-payload-resolver.js';\nimport { decodeSessionLogEntries, SessionLogDecodeError } from './session-log-codec/index.js';\n\nimport type { ISessionLogPayloadResolutionOptions } from './external-payload-resolver.js';\nimport type { IExternalPayloadSource, ISessionLogSource } from './session-log-sources.js';\nimport type { IHistoryEntry, TUniversalMessage } from '@robota-sdk/agent-core';\nimport type { ISessionLogEntry } from './session-log-entry-types.js';\n\nexport type { ISessionLogEntry } from './session-log-entry-types.js';\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 type ISessionLogLoadOptions = Omit<ISessionLogPayloadResolutionOptions, 'source'> & {\n readonly externalPayloadSource?: IExternalPayloadSource;\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(\n source: ISessionLogSource,\n options: ISessionLogLoadOptions = {},\n): ISessionLogEntry[] {\n const text = source.readText();\n const parsedLines: { value: unknown; lineNumber: number }[] = [];\n if (text !== undefined) {\n text.split('\\n').forEach((line, index) => {\n if (line.trim().length === 0) return;\n try {\n parsedLines.push({ value: JSON.parse(line) as unknown, lineNumber: index + 1 });\n } catch (cause) {\n throw new SessionLogDecodeError(\n 'INVALID_JSON',\n [{ path: `line ${index + 1}`, message: 'expected valid JSON' }],\n { cause },\n );\n }\n });\n }\n const hydrated = resolveSessionLogExternalPayloads(\n parsedLines.map(({ value }) => value),\n {\n source: options.externalPayloadSource ?? source.externalPayloadSource,\n ...options,\n },\n );\n return decodeSessionLogEntries(hydrated, {\n lineNumbers: parsedLines.map(({ lineNumber }) => lineNumber),\n });\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 decodeSessionLogEntries(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 messages.push(entry.message);\n history.push(messageToHistoryEntry(entry.message));\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 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 * NodeSessionStore — persists conversation sessions as JSON files.\n *\n * The caller explicitly supplies a host-owned base directory.\n * This adapter does not interpret that directory as a trusted project root.\n * The store directory is created on first write if it does not exist.\n */\n\nimport { readFileSync, existsSync, unlinkSync, readdirSync } from 'fs';\nimport { resolve, sep } from 'path';\n\nimport { ensureOwnerOnlyDirectory, writeOwnerOnlyFile } from '@robota-sdk/agent-core/node';\n\nimport { assertSafeSessionId, isSafeSessionId } from './session-id.js';\nimport {\n SESSION_RECORD_ENVELOPE_VERSION,\n decodeVersionedInteractiveSessionRecord,\n} from './session-record-codec/index.js';\n\nimport type {\n IInteractiveSessionRecord,\n IInteractiveSessionStore,\n ISessionListEntry,\n TSessionLoadOutcome,\n} from '@robota-sdk/agent-interface-session';\n\n/** A read failure described without leaking the whole error object into a persisted diagnostic. */\nfunction describeError(error: unknown): string {\n return error instanceof Error ? error.message : 'unknown error';\n}\n\n/** Decode stored bytes into an outcome, keeping \"not JSON\" and \"not a record\" the same answer. */\nfunction decodeStoredSession(raw: string): TSessionLoadOutcome {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw) as unknown;\n } catch {\n return {\n status: 'corrupt',\n issues: [{ path: '', message: 'the session file is not JSON' }],\n };\n }\n const outcome = decodeVersionedInteractiveSessionRecord(parsed);\n if (outcome.status === 'valid') return { status: 'valid', record: outcome.record };\n if (outcome.status === 'unsupported') {\n return { status: 'unsupported', schemaVersion: outcome.schemaVersion };\n }\n return { status: 'corrupt', issues: outcome.issues };\n}\n\n/**\n * Most recent first, and an unreadable entry sorts last rather than being dropped.\n *\n * An entry with no record has no `updatedAt` to sort by; giving it the epoch keeps it present and\n * out of the way, which is the whole point of listing it at all.\n */\nfunction compareListEntriesByRecency(left: ISessionListEntry, right: ISessionListEntry): number {\n const at = (entry: ISessionListEntry): number =>\n entry.outcome.status === 'valid' ? new Date(entry.outcome.record.updatedAt).getTime() : 0;\n return at(right) - at(left);\n}\n\n/**\n * Persistent session store backed by individual JSON files.\n *\n * Construct with a host-owned `baseDir`; framework project composition uses a separate\n * authority-backed adapter over the same neutral port.\n */\nexport class NodeSessionStore implements IInteractiveSessionStore {\n private readonly baseDir: string;\n private readonly ownedRoot: string | undefined;\n\n /**\n * @param baseDir the directory holding the records.\n * @param ownedRoot an ancestor of `baseDir` the HOST also owns, tightened along with it (SEC-020).\n * Optional because this adapter does not interpret its base directory as a trusted root and must\n * not guess that a parent belongs to the product — which of them do is composition's knowledge.\n * Omitting it leaves a store root an older version created at whatever mode it was given, which\n * is what review of PR #2224 found: the leaf was 0700 and the directory above it was not.\n */\n constructor(baseDir: string, ownedRoot?: string) {\n this.baseDir = baseDir;\n this.ownedRoot = ownedRoot;\n }\n\n /**\n * Ensure the storage directory exists AND that only its owner can enter it (SEC-020).\n *\n * The `existsSync` guard this replaces is the whole defect. It skipped the case that matters: a\n * directory some earlier version, a shared CI checkout, or another local user left at a wider\n * mode was adopted as ours with no signal. Measured under umask 022 before this change, a fresh\n * sessions directory came out 0755 and its records 0644 — and a directory pre-created at 0777\n * stayed 0777.\n */\n private ensureDir(): void {\n ensureOwnerOnlyDirectory(\n this.baseDir,\n this.ownedRoot === undefined ? {} : { withinRoot: this.ownedRoot },\n );\n }\n\n /**\n * Absolute path to a session's JSON file.\n *\n * SEC-006: every public method routes through here, so validating the id at this one point covers\n * `save` (write), `load` (read), and `delete` (unlink) at once.\n *\n * Issue #2240: `assertSafeSessionId` is the guard, and it is sound — no separator, no `.`/`..`\n * can pass it. But it is a regex reject behind a helper, which static analysis does not model as\n * a path sanitizer, so `js/path-injection` re-opened on `load` every time this function changed\n * length. The containment check below is the shape such tools DO recognise: the resolved path\n * must stay inside the resolved base directory. It is unreachable after the assertion and costs\n * one `resolve`; it exists so the guard is visible where the sink is, not to replace the guard.\n */\n private filePath(id: string): string {\n assertSafeSessionId(id);\n const base = resolve(this.baseDir);\n const candidate = resolve(base, `${id}.json`);\n if (!candidate.startsWith(base + sep)) {\n throw new Error(\n `Invalid session id: ${JSON.stringify(id)} resolves outside the session store.`,\n );\n }\n return candidate;\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 * SEC-020: the atomic write now comes from `writeOwnerOnlyFile`, which carries the mode from the\n * moment the temp file is created. The hand-rolled version here wrote it at the umask's default\n * and let `rename` carry that mode to the final path, so every record was 0644 — and even setting\n * the mode after the write would leave a window in which the full transcript was world-readable\n * on disk.\n */\n save(session: IInteractiveSessionRecord): void {\n this.ensureDir();\n // TRANS-007: the versioned envelope, not the bare record. Without a version on disk there is\n // nothing to compare, so \"written by a build this one does not read\" cannot be told from\n // \"damaged\" even in principle — and those two need different things from the user.\n // SEC-020's owner-only atomic write carries it: the envelope is WHAT is written, the mode is HOW.\n writeOwnerOnlyFile(\n this.filePath(session.id),\n JSON.stringify({ schemaVersion: SESSION_RECORD_ENVELOPE_VERSION, record: session }, null, 2),\n );\n }\n\n /**\n * Load a session by its ID, saying WHICH of the four things happened.\n *\n * `undefined` used to answer all four — never saved, damaged, written by a build this one cannot\n * read, and the read failed — and a caller that meant to preserve fields it does not own then\n * treated \"damaged\" as \"no prior record\" and overwrote the file. The outcome type is what removes\n * that, by making the caller answer the question it was not asking.\n */\n load(id: string): TSessionLoadOutcome {\n const path = this.filePath(id);\n if (!existsSync(path)) {\n return { status: 'missing' };\n }\n let raw: string;\n try {\n raw = readFileSync(path, 'utf-8');\n } catch (error) {\n // A file that exists and cannot be read is NOT missing. Reporting it as missing is what let a\n // recovery path run over a session that was still there.\n return {\n status: 'corrupt',\n issues: [{ path: '', message: `could not read the session file: ${describeError(error)}` }],\n };\n }\n return decodeStoredSession(raw);\n }\n\n /**\n * Every session this directory holds, each with what the store concluded about it.\n *\n * Unreadable entries are REPORTED rather than skipped. A store that distinguishes four outcomes on\n * `load` and then hides two of them from the surface a person browses has moved the defect rather\n * than removed it: the difference a user experiences is between \"my session vanished\" and \"my\n * session needs a different build\".\n */\n list(): readonly ISessionListEntry[] {\n if (!existsSync(this.baseDir)) {\n return [];\n }\n return readdirSync(this.baseDir)\n .filter((file) => file.endsWith('.json'))\n .map((file) => file.slice(0, -'.json'.length))\n .map((id) => ({ id, outcome: this.outcomeForListedId(id) }))\n .sort(compareListEntriesByRecency);\n }\n\n /**\n * The outcome for one directory entry, without letting a bad NAME throw out of `list`.\n *\n * `load` validates the id, because an id reaching it is a caller's value and a malformed one is a\n * bug or an attack (SEC-006). A name read out of the directory is neither: the store did not\n * choose it, and one file it cannot use as an id must not take the whole listing down with it.\n * Routing `list` through `load` made exactly that happen — a single `my session.json` in the\n * sessions directory threw, and the resume picker went with it.\n *\n * Reporting it is the same answer `list` gives for every other file it cannot read, which is the\n * property this work exists to establish.\n */\n private outcomeForListedId(id: string): TSessionLoadOutcome {\n if (!isSafeSessionId(id)) {\n return {\n status: 'corrupt',\n issues: [{ path: '', message: 'the file name is not a usable session id' }],\n };\n }\n return this.load(id);\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","/**\n * SELFHOST-007 — neutral checkpoint tree (branching time-travel).\n *\n * A pure, I/O-free branch-tree over opaque checkpoint-node ids — git-for-a-session with no file I/O,\n * no persistence, and no retention/prune policy. It lives beside the storage-neutral persistence\n * primitive (`SessionStore`/`IInteractiveSessionRecord`): same neutral-mechanism class (opaque payloads, no\n * product policy). The agent-framework checkpoint store consumes it over the existing one-way\n * `agent-framework → agent-session` edge; the reverse edge would be a cycle and is forbidden.\n *\n * Model: each node is `{ id, parentId }` (root has no `parentId`). Adding a checkpoint appends a child\n * of the active head. Forking moves the active head to a PAST node so the next append DIVERGES — the\n * original descendants stay reachable (a sibling branch). A \"branch\" is a leaf (a node with no\n * children); `switch` moves the active head to any existing node.\n */\n\nexport interface ICheckpointNode {\n id: string;\n /** Parent checkpoint id; absent for the root. */\n parentId?: string;\n}\n\nexport class CheckpointTree {\n private readonly nodes = new Map<string, ICheckpointNode>();\n /** Child adjacency (parentId → child ids in insertion order) for O(1) leaf/branch queries. */\n private readonly children = new Map<string, string[]>();\n private activeId: string | undefined;\n\n /**\n * Build a tree from explicit `{ id, parentId }` edges (e.g. reconstructed from persisted checkpoint\n * manifests). Nodes may arrive in any order — parents are linked by id. The active head is left at\n * the given `activeId` (or undefined). This is the delegation entry point a consumer store uses to\n * answer navigation queries (`listBranches`/`ancestors`) without the tree owning any persistence.\n */\n static fromNodes(nodes: ICheckpointNode[], activeId?: string): CheckpointTree {\n const tree = new CheckpointTree();\n // First pass: register every node so parent links resolve regardless of arrival order.\n for (const node of nodes) {\n if (tree.nodes.has(node.id)) throw new Error(`CheckpointTree: duplicate node \"${node.id}\"`);\n tree.nodes.set(node.id, node.parentId === undefined ? { id: node.id } : { ...node });\n }\n // Second pass: build child adjacency in the provided order.\n for (const node of nodes) {\n if (node.parentId !== undefined) {\n const siblings = tree.children.get(node.parentId) ?? [];\n siblings.push(node.id);\n tree.children.set(node.parentId, siblings);\n }\n }\n tree.activeId = activeId;\n return tree;\n }\n\n /**\n * Append a checkpoint as a child of the current active head and make it the new active head.\n * The first append (no active head) becomes the root. Ids must be unique.\n */\n addCheckpoint(id: string): void {\n if (this.nodes.has(id)) throw new Error(`CheckpointTree: duplicate checkpoint id \"${id}\"`);\n const parentId = this.activeId;\n this.nodes.set(id, parentId === undefined ? { id } : { id, parentId });\n if (parentId !== undefined) {\n const siblings = this.children.get(parentId) ?? [];\n siblings.push(id);\n this.children.set(parentId, siblings);\n }\n this.activeId = id;\n }\n\n /**\n * Fork from a PAST checkpoint: move the active head to `fromId` so the next `addCheckpoint` diverges\n * into a sibling branch, leaving `fromId`'s original descendants reachable. Returns `fromId`.\n */\n fork(fromId: string): string {\n if (!this.nodes.has(fromId)) throw new Error(`CheckpointTree: unknown checkpoint \"${fromId}\"`);\n this.activeId = fromId;\n return fromId;\n }\n\n /** Move the active head to an existing node (typically a branch leaf). */\n switch(nodeId: string): void {\n if (!this.nodes.has(nodeId)) throw new Error(`CheckpointTree: unknown checkpoint \"${nodeId}\"`);\n this.activeId = nodeId;\n }\n\n /** The current active head id (undefined for an empty tree). */\n activeLeaf(): string | undefined {\n return this.activeId;\n }\n\n /** All branch tips (leaf nodes with no children), in insertion order. */\n listBranches(): string[] {\n const leaves: string[] = [];\n for (const id of this.nodes.keys()) {\n if ((this.children.get(id)?.length ?? 0) === 0) leaves.push(id);\n }\n return leaves;\n }\n\n /**\n * The chain from `id` up to (and including) the root — nearest first. Empty if `id` is unknown. Only\n * REGISTERED nodes are included: a dangling `parentId` (edge to a node not in the tree — possible on\n * manifest drift/corruption) terminates the walk rather than emitting a phantom id.\n */\n ancestors(id: string): string[] {\n const chain: string[] = [];\n let cursor: string | undefined = id;\n while (cursor !== undefined && this.nodes.has(cursor)) {\n chain.push(cursor);\n cursor = this.nodes.get(cursor)!.parentId;\n }\n return chain;\n }\n\n /** Whether a checkpoint id exists in the tree. */\n has(id: string): boolean {\n return this.nodes.has(id);\n }\n\n /** The number of checkpoints in the tree. */\n get size(): number {\n return this.nodes.size;\n }\n}\n"],"mappings":"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"}
|