@stdiobus/workers-registry 1.3.19 → 1.3.21

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../../../workers-registry/acp-worker/src/index.ts", "../../../../workers-registry/acp-worker/src/agent.ts", "../../../../workers-registry/acp-worker/src/mcp/manager.ts", "../../../../workers-registry/acp-worker/src/session/session.ts", "../../../../workers-registry/acp-worker/src/session/manager.ts", "../../../../workers-registry/acp-worker/src/registry-launcher/registry/resolver.ts", "../../../../workers-registry/acp-worker/src/registry-launcher/runtime/agent-runtime.ts", "../../../../workers-registry/acp-worker/src/registry-launcher/runtime/manager.ts", "../../../../workers-registry/acp-worker/src/registry-launcher/stream/ndjson-handler.ts", "../../../../workers-registry/acp-worker/src/registry-launcher/registry/index.ts", "../../../../workers-registry/acp-worker/src/registry-launcher/config/api-keys.ts", "../../../../workers-registry/acp-worker/src/registry-launcher/router/message-router.ts", "../../../../workers-registry/acp-worker/src/registry-launcher/config/config.ts", "../../../../workers-registry/acp-worker/src/registry-launcher/config/types.ts", "../../../../workers-registry/acp-worker/src/acp/client-capabilities.ts", "../../../../workers-registry/acp-worker/src/acp/content-mapper.ts", "../../../../workers-registry/acp-worker/src/acp/tools.ts"],
4
- "sourcesContent": ["/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * ACP/MCP Protocol Worker for stdio Bus kernel\n *\n * This worker implements the Agent Client Protocol (ACP) using official SDKs\n * and connects to MCP servers for tool execution.\n *\n * It runs as a child process of stdio Bus kernel, communicating via stdin/stdout NDJSON.\n *\n * @module index\n */\n\nimport { Readable, Writable } from 'node:stream';\nimport { AgentSideConnection, ndJsonStream } from '@agentclientprotocol/sdk';\nimport { ACPAgent } from './agent.js';\n\n// Log startup message to stderr (not stdout - stdout is for protocol messages)\nconsole.error('[worker] Starting ACP/MCP Protocol Worker...');\n\n/**\n * Convert Node.js stdin to a web ReadableStream.\n * The SDK expects web streams for NDJSON communication.\n */\nconst inputStream = Readable.toWeb(process.stdin) as ReadableStream<Uint8Array>;\n\n/**\n * Convert Node.js stdout to a web WritableStream.\n * The SDK expects web streams for NDJSON communication.\n */\nconst outputStream = Writable.toWeb(process.stdout) as WritableStream<Uint8Array>;\n\n/**\n * Create the NDJSON stream for ACP communication.\n * The SDK handles all NDJSON framing and JSON-RPC protocol details automatically.\n */\nconst stream = ndJsonStream(outputStream, inputStream);\n\n/**\n * Create the AgentSideConnection with stdio transport.\n *\n * The SDK pattern uses a factory function that receives the connection\n * and returns an Agent instance. The SDK handles all NDJSON framing\n * and JSON-RPC protocol details automatically.\n */\nconst connection = new AgentSideConnection(\n (conn) => new ACPAgent(conn),\n stream,\n);\n\n// Log that connection is established\nconsole.error('[worker] AgentSideConnection established, ready for messages');\n\n/**\n * Handle graceful shutdown on SIGTERM.\n *\n * When stdio Bus kernel sends SIGTERM, we should wait for the connection to close\n * and allow pending operations to complete.\n */\nprocess.on('SIGTERM', async () => {\n console.error('[worker] Received SIGTERM, shutting down...');\n // Wait for the connection to close gracefully\n await connection.closed;\n process.exit(0);\n});\n\n/**\n * Handle SIGINT for development convenience.\n */\nprocess.on('SIGINT', async () => {\n console.error('[worker] Received SIGINT, shutting down...');\n await connection.closed;\n process.exit(0);\n});\n\n/**\n * Handle uncaught exceptions to prevent silent failures.\n */\nprocess.on('uncaughtException', (error) => {\n console.error('[worker] Uncaught exception:', error);\n process.exit(1);\n});\n\n/**\n * Handle unhandled promise rejections.\n */\nprocess.on('unhandledRejection', (reason, promise) => {\n console.error('[worker] Unhandled rejection at:', promise, 'reason:', reason);\n});\n\n/**\n * Wait for the connection to close (either normally or due to error).\n * This keeps the process running until the connection ends.\n */\nconnection.closed.then(() => {\n console.error('[worker] Connection closed');\n process.exit(0);\n}).catch((error) => {\n console.error('[worker] Connection error:', error);\n process.exit(1);\n});\n\n// Export types for npm package consumers\nexport type {\n Platform,\n BinaryTarget,\n BinaryDistribution,\n NpxDistribution,\n UvxDistribution,\n Distribution,\n RegistryAgent,\n Registry,\n SpawnCommand,\n} from './registry-launcher/registry/types.js';\n\nexport type {\n RegistryIndex,\n IRegistryIndex,\n} from './registry-launcher/registry/index.js';\n\n// Export runtime classes and functions\nexport { ACPAgent } from './agent.js';\n\n// Registry Launcher exports\nexport {\n PlatformNotSupportedError,\n NoDistributionError,\n getCurrentPlatform,\n resolve,\n resolveBinary,\n resolveNpx,\n resolveUvx,\n} from './registry-launcher/registry/resolver.js';\n\n// Runtime exports\nexport { AgentRuntimeManager } from './registry-launcher/runtime/manager.js';\nexport { AgentRuntimeImpl } from './registry-launcher/runtime/agent-runtime.js';\nexport type { RuntimeState, AgentRuntime } from './registry-launcher/runtime/types.js';\n\n// Stream exports\nexport {\n NDJSONHandler, INDJSONHandler, ErrorCallback, MessageCallback,\n} from './registry-launcher/stream/ndjson-handler.js';\n\n// Router exports\nexport {\n MessageRouter,\n createErrorResponse,\n ErrorResponse,\n RoutingErrorCodes,\n transformMessage,\n extractAgentId,\n extractId,\n WriteCallback,\n} from './registry-launcher/router/message-router.js';\n\n// Config exports\nexport { loadConfig } from './registry-launcher/config/config.js';\nexport type {\n DEFAULT_CONFIG,\n LauncherConfig,\n} from './registry-launcher/config/types.js';\n\n// MCP exports\nexport { MCPManager, MCPConnection, MCPFactories } from './mcp/manager.js';\nexport type {\n MCPServerConfig,\n MCPContent,\n MCPImageContent,\n MCPBlobResourceContents,\n MCPEmbeddedResource,\n MCPResource,\n MCPTextContent,\n MCPResourceContents,\n MCPTextResourceContents,\n MCPTool,\n MCPToolCallResult,\n MCPResourceReadResult,\n} from './mcp/types.js';\n\n// ACP utilities exports\nexport {\n canReadFile,\n canWriteFile,\n FileReadResult,\n FileWriteResult,\n readFile,\n canUseTerminal,\n TerminalResult,\n writeFile,\n executeCommand,\n startCommand,\n} from './acp/client-capabilities.js';\nexport {\n createErrorToolCallContent,\n mapMCPContentToACPContentBlock,\n mapMCPResourceContentsToACPContentBlock,\n isResourceLink,\n extractResourceLinkUri,\n mapMCPResultToACPToolCallContent,\n ResourceLink,\n mapToolResultToACPContent,\n} from './acp/content-mapper.js';\nexport {\n determineToolKind,\n executeToolCall,\n generateToolCallId,\n executeToolCallWithPermission,\n requestToolPermission,\n PermissionResult,\n ToolCallStatus,\n ToolKind,\n sendToolCallInitiation,\n sendToolCallUpdate,\n} from './acp/tools.js';\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Agent Implementation\n *\n * This module implements the ACP Agent interface using @agentclientprotocol/sdk.\n * The Agent handles all ACP protocol methods including initialization,\n * session management, and prompt processing.\n *\n * @module agent\n */\n\nimport type {\n Agent,\n AgentSideConnection,\n AuthenticateRequest,\n AuthenticateResponse,\n CancelNotification,\n ClientCapabilities,\n InitializeRequest,\n InitializeResponse,\n LoadSessionRequest,\n LoadSessionResponse,\n NewSessionRequest,\n NewSessionResponse,\n PromptRequest,\n PromptResponse,\n} from '@agentclientprotocol/sdk';\nimport { PROTOCOL_VERSION } from '@agentclientprotocol/sdk';\nimport { SessionManager } from './session/manager.js';\n\n/**\n * ACP Agent implementation for stdio Bus kernel worker.\n *\n * This class implements the Agent interface from the ACP SDK,\n * handling all protocol methods and coordinating with MCP servers\n * for tool execution.\n */\nexport class ACPAgent implements Agent {\n /**\n * Reference to the AgentSideConnection for sending notifications.\n * Used by prompt processing to send session updates.\n */\n private readonly _connection: AgentSideConnection;\n\n /**\n * Session manager for handling session lifecycle.\n * Manages session creation, lookup, cancellation, and cleanup.\n */\n private readonly _sessionManager: SessionManager;\n\n /**\n * Client capabilities received during initialization.\n * Used to determine what features the client supports.\n */\n private _clientCapabilities: ClientCapabilities | null = null;\n\n /**\n * Creates a new ACP Agent instance.\n *\n * @param connection - The AgentSideConnection for communicating with the client\n */\n constructor(connection: AgentSideConnection) {\n this._connection = connection;\n this._sessionManager = new SessionManager();\n }\n\n /**\n * Get the connection for sending notifications.\n * Used by prompt processing to send session updates.\n */\n get connection(): AgentSideConnection {\n return this._connection;\n }\n\n /**\n * Get the session manager for session operations.\n */\n get sessionManager(): SessionManager {\n return this._sessionManager;\n }\n\n /**\n * Get the client capabilities received during initialization.\n * Returns null if initialize() has not been called yet.\n */\n get clientCapabilities(): ClientCapabilities | null {\n return this._clientCapabilities;\n }\n\n /**\n * Handle ACP initialize request.\n * Returns agent capabilities and info.\n *\n * Stores client capabilities for later use and returns InitializeResponse\n * with agent info and capabilities including promptCapabilities.embeddedContext: true.\n *\n * @param params - The initialization request parameters\n * @returns Promise resolving to InitializeResponse with agent capabilities\n */\n async initialize(params: InitializeRequest): Promise<InitializeResponse> {\n // Store client capabilities for later use\n this._clientCapabilities = params.clientCapabilities ?? null;\n\n // Return InitializeResponse with agent info and capabilities\n return {\n protocolVersion: PROTOCOL_VERSION,\n agentInfo: {\n name: 'stdio-bus-worker',\n version: '1.0.0',\n },\n agentCapabilities: {\n promptCapabilities: {\n embeddedContext: true,\n },\n },\n authMethods: [],\n };\n }\n\n /**\n * Handle ACP session/new request.\n * Creates a new session with MCP server connections.\n *\n * Generates a unique sessionId using crypto.randomUUID(), stores session state,\n * initializes MCP connections from the request params, and returns NewSessionResponse.\n *\n * @param params - The new session request parameters containing cwd and optional mcpServers\n * @returns Promise resolving to NewSessionResponse with session ID\n */\n async newSession(params: NewSessionRequest): Promise<NewSessionResponse> {\n // Convert ACP McpServer[] to MCPServerConfig[] for the session manager\n const mcpServers = params.mcpServers?.map((server) => {\n // Handle stdio type servers (most common for local MCP servers)\n if ('command' in server) {\n return {\n id: server.name,\n command: server.command,\n args: server.args,\n env: server.env?.reduce(\n (acc, envVar) => {\n acc[envVar.name] = envVar.value;\n return acc;\n },\n {} as Record<string, string>,\n ),\n };\n }\n // For HTTP/SSE servers, we'll need to handle them differently\n // For now, skip non-stdio servers\n return null;\n }).filter((s): s is NonNullable<typeof s> => s !== null);\n\n // Create a new session with the session manager\n // This generates a UUID for sessionId and stores session state\n const session = await this._sessionManager.createSession(params.cwd, mcpServers);\n\n // Return NewSessionResponse with the sessionId\n return {\n sessionId: session.id,\n };\n }\n\n /**\n * Handle ACP session/load request.\n * Loads an existing session (optional capability).\n *\n * @param params - The load session request parameters\n * @returns Promise resolving to LoadSessionResponse\n */\n async loadSession(_params: LoadSessionRequest): Promise<LoadSessionResponse> {\n // Session loading is an optional capability\n // Return empty response to indicate session not found\n return {} as LoadSessionResponse;\n }\n\n /**\n * Handle ACP authenticate request.\n * Processes authentication (if required).\n *\n * @param params - The authentication request parameters\n * @returns Promise resolving to AuthenticateResponse or void\n */\n async authenticate(_params: AuthenticateRequest): Promise<AuthenticateResponse | void> {\n // Authentication is optional - no auth methods are declared\n // This is a no-op implementation\n }\n\n /**\n * Handle ACP session/prompt request.\n * Processes user prompts and streams responses.\n *\n * Currently implements echo mode for testing - echoes user prompt as agent response.\n *\n * @param params - The prompt request parameters\n * @returns Promise resolving to PromptResponse with stop reason\n */\n async prompt(params: PromptRequest): Promise<PromptResponse> {\n // Validate session exists\n const session = this._sessionManager.getSession(params.sessionId);\n if (!session) {\n throw new Error(`Session not found: ${params.sessionId}`);\n }\n\n // Check for cancellation before processing\n if (session.isCancelled()) {\n return { stopReason: 'cancelled' };\n }\n\n // Process each content block in the prompt\n // Echo mode: echo user prompt as agent response\n for (const block of params.prompt) {\n // Check for cancellation during processing\n if (session.isCancelled()) {\n return { stopReason: 'cancelled' };\n }\n\n // Handle different content block types\n if (block.type === 'text') {\n // Echo text content as agent_message_chunk\n await this._connection.sessionUpdate({\n sessionId: params.sessionId,\n update: {\n sessionUpdate: 'agent_message_chunk',\n content: {\n type: 'text',\n text: block.text,\n },\n },\n });\n } else if (block.type === 'resource_link') {\n // For resource_link, try to resolve and echo the content\n const resourceLink = block as { type: 'resource_link'; uri: string; name: string };\n try {\n // Try to read the resource from MCP servers\n const result = await session.mcpManager.readResource(resourceLink.uri);\n if (result.contents.length > 0) {\n const content = result.contents[0];\n if ('text' in content) {\n await this._connection.sessionUpdate({\n sessionId: params.sessionId,\n update: {\n sessionUpdate: 'agent_message_chunk',\n content: {\n type: 'text',\n text: `[Resource: ${resourceLink.name}]\\n${content.text}`,\n },\n },\n });\n } else if ('blob' in content) {\n await this._connection.sessionUpdate({\n sessionId: params.sessionId,\n update: {\n sessionUpdate: 'agent_message_chunk',\n content: {\n type: 'text',\n text: `[Resource: ${resourceLink.name}] (binary data, ${content.blob.length} bytes)`,\n },\n },\n });\n }\n }\n } catch {\n // If resource resolution fails, just echo the link info\n await this._connection.sessionUpdate({\n sessionId: params.sessionId,\n update: {\n sessionUpdate: 'agent_message_chunk',\n content: {\n type: 'text',\n text: `[Resource link: ${resourceLink.name} (${resourceLink.uri})]`,\n },\n },\n });\n }\n } else if (block.type === 'resource') {\n // For embedded resource, echo the content\n const resource = block as { type: 'resource'; resource: { uri: string; text?: string; blob?: string } };\n if (resource.resource.text !== undefined) {\n await this._connection.sessionUpdate({\n sessionId: params.sessionId,\n update: {\n sessionUpdate: 'agent_message_chunk',\n content: {\n type: 'text',\n text: `[Embedded resource: ${resource.resource.uri}]\\n${resource.resource.text}`,\n },\n },\n });\n } else if (resource.resource.blob !== undefined) {\n await this._connection.sessionUpdate({\n sessionId: params.sessionId,\n update: {\n sessionUpdate: 'agent_message_chunk',\n content: {\n type: 'text',\n text: `[Embedded resource: ${resource.resource.uri}] (binary data)`,\n },\n },\n });\n }\n } else if (block.type === 'image') {\n // For images, echo a description\n const image = block as { type: 'image'; mimeType: string };\n await this._connection.sessionUpdate({\n sessionId: params.sessionId,\n update: {\n sessionUpdate: 'agent_message_chunk',\n content: {\n type: 'text',\n text: `[Image: ${image.mimeType}]`,\n },\n },\n });\n }\n }\n\n // Final cancellation check\n if (session.isCancelled()) {\n return { stopReason: 'cancelled' };\n }\n\n // Return end_turn as stopReason for successful completion\n return { stopReason: 'end_turn' };\n }\n\n /**\n * Handle ACP session/cancel notification.\n * Cancels ongoing operations for a session.\n *\n * Looks up the session by sessionId and calls session.cancel() to set\n * the cancellation flag and abort pending MCP operations.\n *\n * @param params - The cancel notification parameters containing sessionId\n */\n async cancel(params: CancelNotification): Promise<void> {\n // Look up the session by sessionId and cancel it\n // The session's cancel() method sets the cancellation flag\n // and aborts pending MCP operations\n this._sessionManager.cancelSession(params.sessionId);\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * MCP Manager\n *\n * Manages multiple MCP server connections for a session.\n * Handles connection lifecycle, tool discovery, and tool invocation.\n *\n * @module mcp/manager\n */\n\nimport { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';\nimport type { ServerCapabilities } from '@modelcontextprotocol/sdk/types.js';\nimport type {\n MCPBlobResourceContents,\n MCPContent,\n MCPResource,\n MCPResourceContents,\n MCPResourceReadResult,\n MCPServerConfig,\n MCPTextResourceContents,\n MCPTool,\n MCPToolCallResult,\n} from './types.js';\n\n/**\n * Represents an active MCP connection with its client and transport.\n */\nexport interface MCPConnection {\n /** The MCP SDK client instance */\n client: Client;\n /** The stdio transport for the connection */\n transport: StdioClientTransport;\n /** Server configuration */\n config: MCPServerConfig;\n /** Whether the connection is active */\n connected: boolean;\n /** Server capabilities from initialization handshake */\n capabilities?: ServerCapabilities;\n /** Error message if the server crashed */\n crashError?: string;\n}\n\n/**\n * Factory functions for creating MCP SDK instances.\n * Used for dependency injection in tests.\n */\nexport interface MCPFactories {\n /** Factory for creating Client instances */\n createClient: (options: { name: string; version: string }) => Client;\n /** Factory for creating StdioClientTransport instances */\n createTransport: (options: {\n command: string;\n args?: string[];\n env?: Record<string, string>\n }) => StdioClientTransport;\n}\n\n/** Default factories using the real MCP SDK */\nconst defaultFactories: MCPFactories = {\n createClient: (options) => new Client(options),\n createTransport: (options) => new StdioClientTransport(options),\n};\n\n/**\n * Manages MCP server connections for a session.\n */\nexport class MCPManager {\n /** Active connections keyed by server ID */\n private connections: Map<string, MCPConnection> = new Map();\n\n /** Map from tool name to server ID for routing tool calls */\n private toolToServer: Map<string, string> = new Map();\n\n /** Callback for server crash notifications */\n private onServerCrash?: (serverId: string, error: string) => void;\n\n /** Factory functions for creating SDK instances (injectable for testing) */\n private factories: MCPFactories;\n\n /**\n * Create a new MCPManager instance.\n *\n * @param factories - Optional factory functions for dependency injection (used in tests)\n */\n constructor(factories?: Partial<MCPFactories>) {\n this.factories = { ...defaultFactories, ...factories };\n }\n\n /**\n * Set a callback to be notified when a server crashes.\n *\n * @param callback - Function to call when a server crashes\n */\n setOnServerCrash(callback: (serverId: string, error: string) => void): void {\n this.onServerCrash = callback;\n }\n\n /**\n * Connect to MCP servers specified in the configuration.\n *\n * @param servers - Array of MCP server configurations\n */\n async connect(servers: MCPServerConfig[]): Promise<void> {\n for (const serverConfig of servers) {\n try {\n // Create stdio transport for subprocess MCP server\n // Use StdioClientTransport\n const transport = this.factories.createTransport({\n command: serverConfig.command,\n args: serverConfig.args,\n env: serverConfig.env,\n });\n\n // Create MCP client\n // Use Client class\n const client = this.factories.createClient({\n name: 'stdio-bus-worker',\n version: '1.0.0',\n });\n\n // Connect client to transport\n // SDK sends initialize and notifications/initialized\n await client.connect(transport);\n\n // Get and store server capabilities\n // Store server capabilities for feature detection\n const capabilities = client.getServerCapabilities();\n\n // Store the connection\n const connection: MCPConnection = {\n client,\n transport,\n config: serverConfig,\n connected: true,\n capabilities,\n };\n this.connections.set(serverConfig.id, connection);\n\n // Set up crash detection via transport close event\n // Detect server process exit\n this.setupCrashDetection(serverConfig.id, client);\n\n // Log successful connection to stderr\n console.error(`[MCP] Connected to server: ${serverConfig.id}`);\n } catch (error) {\n // Handle connection errors gracefully\n console.error(`[MCP] Failed to connect to server ${serverConfig.id}:`, error);\n // Continue with other servers\n }\n }\n }\n\n /**\n * Set up crash detection for an MCP server connection.\n *\n * @param serverId - The server ID\n * @param client - The MCP client\n */\n private setupCrashDetection(serverId: string, client: Client): void {\n // Listen for client close event which indicates server disconnection\n client.onclose = () => {\n const connection = this.connections.get(serverId);\n if (connection && connection.connected) {\n // Mark server as crashed\n connection.connected = false;\n connection.crashError = 'Server process exited unexpectedly';\n\n // Remove tools from this server from the routing map\n for (const [toolName, toolServerId] of this.toolToServer.entries()) {\n if (toolServerId === serverId) {\n this.toolToServer.delete(toolName);\n }\n }\n\n // Log the crash\n console.error(`[MCP] Server ${serverId} crashed: ${connection.crashError}`);\n\n // Notify callback if set\n if (this.onServerCrash) {\n this.onServerCrash(serverId, connection.crashError);\n }\n }\n };\n }\n\n /**\n * Get all available tools from connected MCP servers.\n *\n * @returns Combined list of tools from all connected servers\n */\n async listTools(): Promise<MCPTool[]> {\n const allTools: MCPTool[] = [];\n\n // Clear the toolToServer map before repopulating\n this.toolToServer.clear();\n\n // Iterate through all connected MCP servers\n for (const [serverId, connection] of this.connections) {\n if (!connection.connected) {\n continue;\n }\n\n try {\n // Handle pagination - keep fetching while there's a nextCursor\n let cursor: string | undefined;\n do {\n // Call client.listTools() on each connection\n // Use client.listTools() to discover available tools\n const result = await connection.client.listTools(cursor ? { cursor } : undefined);\n\n // Map the results to MCPTool[] format with serverId included\n // Store tool definitions (name, description, inputSchema)\n for (const tool of result.tools) {\n allTools.push({\n name: tool.name,\n description: tool.description,\n inputSchema: tool.inputSchema as Record<string, unknown>,\n serverId,\n });\n\n // Track which server provides this tool for routing\n this.toolToServer.set(tool.name, serverId);\n }\n\n // Handle pagination if the server returns nextCursor\n cursor = result.nextCursor;\n } while (cursor);\n } catch (error) {\n // Log error but continue with other servers\n console.error(`[MCP] Failed to list tools from server ${serverId}:`, error);\n }\n }\n\n return allTools;\n }\n\n /**\n * Invoke a tool on the appropriate MCP server.\n * - Finds the server that provides the tool\n * - Calls client.callTool({ name, arguments }) on the appropriate connection\n * - Handles CallToolResult response\n * - Checks isError flag for failures\n * - Returns errors for calls to crashed server\n *\n * @param name - The name of the tool to invoke\n * @param args - The arguments to pass to the tool\n * @param serverId - Optional server ID to call the tool on (if known)\n * @returns The tool call result with content and error status\n * @throws Error if tool is not found or server is not connected\n */\n async callTool(name: string, args: Record<string, unknown>, serverId?: string): Promise<MCPToolCallResult> {\n // Determine which server to call\n const targetServerId = serverId ?? this.toolToServer.get(name);\n\n if (!targetServerId) {\n throw new Error(`Tool \"${name}\" not found. Call listTools() first to discover available tools.`);\n }\n\n // Get the connection for the target server\n const connection = this.connections.get(targetServerId);\n\n if (!connection) {\n throw new Error(`Server \"${targetServerId}\" not found.`);\n }\n\n if (!connection.connected) {\n // Return errors for calls to crashed server\n const crashMessage = connection.crashError || 'Server is not connected';\n throw new Error(`Server \"${targetServerId}\" is unavailable: ${crashMessage}`);\n }\n\n try {\n // Call client.callTool() on the appropriate connection\n // Use client.callTool() to invoke tools\n const result = await connection.client.callTool({\n name,\n arguments: args,\n });\n\n // Map the SDK result to our MCPToolCallResult type\n // Use SDK content types from result\n const content: MCPContent[] = (result.content as Array<{ type: string;[key: string]: unknown }>).map((item) => {\n if (item.type === 'text') {\n return {\n type: 'text' as const,\n text: item.text as string,\n };\n } else if (item.type === 'image') {\n return {\n type: 'image' as const,\n data: item.data as string,\n mimeType: item.mimeType as string,\n };\n } else if (item.type === 'resource') {\n const resource = item.resource as { uri: string; mimeType?: string; text?: string; blob?: string };\n return {\n type: 'resource' as const,\n resource: {\n uri: resource.uri,\n mimeType: resource.mimeType,\n text: resource.text,\n blob: resource.blob,\n },\n };\n }\n // Default to text for unknown types\n return {\n type: 'text' as const,\n text: JSON.stringify(item),\n };\n });\n\n // Check CallToolResult.isError for tool failures\n return {\n content,\n isError: result.isError === true,\n };\n } catch (error) {\n // Log error and re-throw\n console.error(`[MCP] Failed to call tool \"${name}\" on server ${targetServerId}:`, error);\n throw error;\n }\n }\n\n /**\n * Get all available resources from connected MCP servers.\n * - Calls client.listResources() to discover available resources\n * - Stores resource definitions (uri, name, description, mimeType)\n * - Handles pagination via nextCursor if present\n *\n * @returns Combined list of resources from all connected servers\n */\n async listResources(): Promise<MCPResource[]> {\n const allResources: MCPResource[] = [];\n\n // Iterate through all connected MCP servers\n for (const [serverId, connection] of this.connections) {\n if (!connection.connected) {\n continue;\n }\n\n try {\n // Handle pagination - keep fetching while there's a nextCursor\n let cursor: string | undefined;\n do {\n // Call client.listResources() on each connection\n // Use client.listResources() to discover resources\n const result = await connection.client.listResources(cursor ? { cursor } : undefined);\n\n // Map the results to MCPResource[] format with serverId included\n for (const resource of result.resources) {\n allResources.push({\n uri: resource.uri,\n name: resource.name,\n description: resource.description,\n mimeType: resource.mimeType,\n serverId,\n });\n }\n\n // Handle pagination if the server returns nextCursor\n cursor = result.nextCursor;\n } while (cursor);\n } catch (error) {\n // Log error but continue with other servers\n console.error(`[MCP] Failed to list resources from server ${serverId}:`, error);\n }\n }\n\n return allResources;\n }\n\n /**\n * Read a resource from the appropriate MCP server.\n * - Calls client.readResource({ uri }) on the appropriate connection\n * - Handles TextResourceContents and BlobResourceContents\n * - Determines which server handles the URI based on resource list\n *\n * @param uri - The URI of the resource to read\n * @param serverId - Optional server ID to read from (if known)\n * @returns The resource contents\n * @throws Error if resource server is not found or not connected\n */\n async readResource(uri: string, serverId?: string): Promise<MCPResourceReadResult> {\n // Determine which server to use\n let targetServerId = serverId;\n\n // If no server ID provided, try to find the server that provides this resource\n if (!targetServerId) {\n // Search through all connected servers' resources to find the one with this URI\n for (const [, connection] of this.connections) {\n if (!connection.connected) {\n continue;\n }\n\n try {\n // Try to read from this server - if it has the resource, it will succeed\n const result = await connection.client.readResource({ uri });\n\n // Map the SDK result to our MCPResourceReadResult type\n const contents: MCPResourceContents[] = result.contents.map((item) => {\n const resourceItem = item as { uri: string; mimeType?: string; text?: string; blob?: string };\n if ('text' in resourceItem && resourceItem.text !== undefined) {\n return {\n uri: resourceItem.uri,\n mimeType: resourceItem.mimeType,\n text: resourceItem.text,\n } as MCPTextResourceContents;\n } else if ('blob' in resourceItem && resourceItem.blob !== undefined) {\n return {\n uri: resourceItem.uri,\n mimeType: resourceItem.mimeType,\n blob: resourceItem.blob,\n } as MCPBlobResourceContents;\n }\n // Default to text with empty content for unknown types\n return {\n uri: resourceItem.uri,\n mimeType: resourceItem.mimeType,\n text: '',\n } as MCPTextResourceContents;\n });\n\n return { contents };\n } catch {\n // This server doesn't have the resource, try the next one\n continue;\n }\n }\n\n throw new Error(`Resource \"${uri}\" not found on any connected server.`);\n }\n\n // Get the connection for the target server\n const connection = this.connections.get(targetServerId);\n\n if (!connection) {\n throw new Error(`Server \"${targetServerId}\" not found.`);\n }\n\n if (!connection.connected) {\n throw new Error(`Server \"${targetServerId}\" is not connected.`);\n }\n\n try {\n // Call client.readResource() on the appropriate connection\n // Use client.readResource() to read resources\n const result = await connection.client.readResource({ uri });\n\n // Map the SDK result to our MCPResourceReadResult type\n // Handle TextResourceContents and BlobResourceContents\n const contents: MCPResourceContents[] = result.contents.map((item) => {\n const resourceItem = item as { uri: string; mimeType?: string; text?: string; blob?: string };\n if ('text' in resourceItem && resourceItem.text !== undefined) {\n return {\n uri: resourceItem.uri,\n mimeType: resourceItem.mimeType,\n text: resourceItem.text,\n } as MCPTextResourceContents;\n } else if ('blob' in resourceItem && resourceItem.blob !== undefined) {\n return {\n uri: resourceItem.uri,\n mimeType: resourceItem.mimeType,\n blob: resourceItem.blob,\n } as MCPBlobResourceContents;\n }\n // Default to text with empty content for unknown types\n return {\n uri: resourceItem.uri,\n mimeType: resourceItem.mimeType,\n text: '',\n } as MCPTextResourceContents;\n });\n\n return { contents };\n } catch (error) {\n // Log error and re-throw\n console.error(`[MCP] Failed to read resource \"${uri}\" from server ${targetServerId}:`, error);\n throw error;\n }\n }\n\n /**\n * Get a connection by server ID.\n *\n * @param serverId - The server ID to look up\n * @returns The connection or undefined if not found\n */\n getConnection(serverId: string): MCPConnection | undefined {\n return this.connections.get(serverId);\n }\n\n /**\n * Get all active connections.\n *\n * @returns Array of all active connections\n */\n getAllConnections(): MCPConnection[] {\n return Array.from(this.connections.values()).filter((conn) => conn.connected);\n }\n\n /**\n * Get server capabilities for a specific server.\n *\n * @param serverId - The server ID to look up\n * @returns The server capabilities or undefined if not found/connected\n */\n getServerCapabilities(serverId: string): ServerCapabilities | undefined {\n const connection = this.connections.get(serverId);\n return connection?.connected ? connection.capabilities : undefined;\n }\n\n /**\n * Close all MCP server connections.\n */\n async close(): Promise<void> {\n for (const connection of this.connections.values()) {\n try {\n await connection.client.close();\n connection.connected = false;\n } catch (error) {\n console.error(`[MCP] Error closing connection ${connection.config.id}:`, error);\n }\n }\n this.connections.clear();\n this.toolToServer.clear();\n }\n\n /**\n * Abort all pending MCP operations.\n * Called when a session is cancelled to stop in-flight requests.\n */\n abortPendingOperations(): void {\n // Mark all connections as not connected to prevent new operations\n for (const connection of this.connections.values()) {\n connection.connected = false;\n }\n }\n\n /**\n * Check if a server has crashed.\n *\n * @param serverId - The server ID to check\n * @returns True if the server has crashed\n */\n isServerCrashed(serverId: string): boolean {\n const connection = this.connections.get(serverId);\n return connection !== undefined && !connection.connected && connection.crashError !== undefined;\n }\n\n /**\n * Get the crash error for a server.\n *\n * @param serverId - The server ID to check\n * @returns The crash error message or undefined if not crashed\n */\n getServerCrashError(serverId: string): string | undefined {\n const connection = this.connections.get(serverId);\n return connection?.crashError;\n }\n\n /**\n * Get all crashed servers.\n *\n * @returns Array of crashed server IDs with their error messages\n */\n getCrashedServers(): Array<{ serverId: string; error: string }> {\n const crashed: Array<{ serverId: string; error: string }> = [];\n for (const [serverId, connection] of this.connections) {\n if (!connection.connected && connection.crashError) {\n crashed.push({ serverId, error: connection.crashError });\n }\n }\n return crashed;\n }\n}\n", "/**\n * Session\n *\n * Represents a single ACP session with its state and MCP connections.\n * Stores sessionId, cwd, MCP connections, and cancellation flag.\n *\n * @module session/session\n */\n\nimport type { HistoryEntry, SessionState } from './types.js';\nimport { MCPManager } from '../mcp/index.js';\n\n/**\n * Factory function type for creating MCPManager instances.\n * Used for dependency injection in tests.\n */\nexport type MCPManagerFactory = () => MCPManager;\n\n/** Default factory using the real MCPManager */\nconst defaultMcpManagerFactory: MCPManagerFactory = () => new MCPManager();\n\n/**\n * Represents an ACP session.\n *\n * The Session class manages:\n * - Session identification (sessionId per requirements)\n * - Working directory context (cwd)\n * - MCP server connections via MCPManager\n * - Cancellation state for aborting operations\n * - Conversation history\n */\nexport class Session {\n /** Unique session identifier (SessionId type per requirements) */\n readonly id: string;\n\n /** Current working directory for the session */\n readonly cwd: string;\n\n /** Manager for MCP server connections */\n readonly mcpManager: MCPManager;\n\n /** Cancellation flag for aborting pending operations */\n private cancelled: boolean = false;\n\n /** Timestamp when the session was created */\n private createdAt: Date;\n\n /** Conversation history for the session */\n private history: HistoryEntry[] = [];\n\n /**\n * Create a new Session.\n *\n * @param id - Unique session identifier\n * @param cwd - Current working directory for the session\n * @param mcpManagerFactory - Optional factory for creating MCPManager (used in tests)\n */\n constructor(id: string, cwd: string, mcpManagerFactory?: MCPManagerFactory) {\n this.id = id;\n this.cwd = cwd;\n this.mcpManager = (mcpManagerFactory ?? defaultMcpManagerFactory)();\n this.createdAt = new Date();\n }\n\n /**\n * Check if the session has been cancelled.\n *\n * @returns true if the session has been cancelled\n */\n isCancelled(): boolean {\n return this.cancelled;\n }\n\n /**\n * Cancel the session.\n * Sets the cancellation flag and aborts pending MCP operations.\n */\n cancel(): void {\n this.cancelled = true;\n // Abort pending MCP operations by closing all connections\n // This will cause any in-flight requests to fail gracefully\n this.mcpManager.abortPendingOperations();\n }\n\n /**\n * Add an entry to the conversation history.\n *\n * @param role - Role of the message sender ('user' or 'agent')\n * @param content - Content of the message\n */\n addHistoryEntry(role: 'user' | 'agent', content: string): void {\n this.history.push({\n role,\n content,\n timestamp: new Date(),\n });\n }\n\n /**\n * Get the conversation history.\n *\n * @returns Array of history entries\n */\n getHistory(): HistoryEntry[] {\n return [...this.history];\n }\n\n /**\n * Clear the conversation history.\n */\n clearHistory(): void {\n this.history = [];\n }\n\n /**\n * Get the session state.\n *\n * @returns Current session state including id, cwd, cancelled flag, and history\n */\n getState(): SessionState {\n return {\n id: this.id,\n cwd: this.cwd,\n cancelled: this.cancelled,\n createdAt: this.createdAt,\n history: [...this.history],\n };\n }\n\n /**\n * Close the session and cleanup resources.\n * Closes all MCP server connections.\n */\n async close(): Promise<void> {\n await this.mcpManager.close();\n }\n}\n", "/**\n * Session Manager\n *\n * Manages the lifecycle of ACP sessions.\n *\n * @module session/manager\n */\n\nimport { type MCPManagerFactory, Session } from './session.js';\nimport type { MCPServerConfig } from '../mcp/types.js';\n\n/**\n * Manages ACP sessions.\n */\nexport class SessionManager {\n private sessions: Map<string, Session> = new Map();\n private mcpManagerFactory?: MCPManagerFactory;\n\n /**\n * Create a new SessionManager.\n *\n * @param mcpManagerFactory - Optional factory for creating MCPManager instances (used in tests)\n */\n constructor(mcpManagerFactory?: MCPManagerFactory) {\n this.mcpManagerFactory = mcpManagerFactory;\n }\n\n /**\n * Create a new session.\n */\n async createSession(cwd: string, mcpServers?: MCPServerConfig[]): Promise<Session> {\n // TODO: Implement in task 21.2\n const id = this.generateSessionId();\n const session = new Session(id, cwd, this.mcpManagerFactory);\n\n if (mcpServers && mcpServers.length > 0) {\n await session.mcpManager.connect(mcpServers);\n }\n\n this.sessions.set(id, session);\n return session;\n }\n\n /**\n * Get a session by ID.\n */\n getSession(id: string): Session | undefined {\n return this.sessions.get(id);\n }\n\n /**\n * Cancel a session.\n */\n cancelSession(id: string): boolean {\n const session = this.sessions.get(id);\n if (session) {\n session.cancel();\n return true;\n }\n return false;\n }\n\n /**\n * Close and remove a session.\n */\n async closeSession(id: string): Promise<boolean> {\n const session = this.sessions.get(id);\n if (session) {\n await session.close();\n this.sessions.delete(id);\n return true;\n }\n return false;\n }\n\n /**\n * Close all sessions.\n */\n async closeAll(): Promise<void> {\n for (const session of this.sessions.values()) {\n await session.close();\n }\n this.sessions.clear();\n }\n\n /**\n * Get all sessions.\n *\n * @returns Array of all sessions\n */\n getAllSessions(): Session[] {\n return Array.from(this.sessions.values());\n }\n\n /**\n * Remove a session from the manager without closing it.\n *\n * @param id - Session ID to remove\n * @returns true if session was removed, false if not found\n */\n removeSession(id: string): boolean {\n return this.sessions.delete(id);\n }\n\n /**\n * Generate a unique session ID using crypto.randomUUID().\n *\n * Generate unique sessionId using UUID\n *\n * @returns A unique UUID string for the session\n */\n private generateSessionId(): string {\n return crypto.randomUUID();\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Distribution Resolver for the ACP Registry.\n *\n * Resolves distribution metadata to spawn commands based on distribution type.\n * Supports binary (platform-specific), npx (npm), and uvx (Python) distributions.\n *\n * @module registry/resolver\n */\n\nimport {\n BinaryDistribution,\n BinaryTarget,\n Distribution,\n NpxDistribution,\n Platform,\n SpawnCommand,\n UvxDistribution,\n} from './types.js';\n\n/**\n * Error thrown when the current platform is not supported by a binary distribution.\n */\nexport class PlatformNotSupportedError extends Error {\n constructor(\n public readonly agentId: string,\n public readonly platform: Platform,\n ) {\n super(`Platform not supported: ${platform} for agent ${agentId}`);\n this.name = 'PlatformNotSupportedError';\n }\n}\n\n/**\n * Error thrown when no supported distribution type is available.\n */\nexport class NoDistributionError extends Error {\n constructor(public readonly agentId: string) {\n super(`No supported distribution type for agent ${agentId}`);\n this.name = 'NoDistributionError';\n }\n}\n\n/**\n * Get the current platform identifier.\n * Maps Node.js platform/arch to ACP Registry platform format.\n *\n * @returns Platform identifier for the current OS and architecture\n */\nexport function getCurrentPlatform(): Platform {\n const platform = process.platform;\n const arch = process.arch;\n\n if (platform === 'darwin' && arch === 'arm64') return 'darwin-aarch64';\n if (platform === 'darwin' && arch === 'x64') return 'darwin-x86_64';\n if (platform === 'linux' && arch === 'arm64') return 'linux-aarch64';\n if (platform === 'linux' && arch === 'x64') return 'linux-x86_64';\n if (platform === 'win32' && arch === 'arm64') return 'windows-aarch64';\n if (platform === 'win32' && arch === 'x64') return 'windows-x86_64';\n\n // Default to linux-x86_64 for unsupported platforms\n return 'linux-x86_64';\n}\n\n/**\n * Resolve a binary distribution to a spawn command.\n *\n * @param distribution - Binary distribution to resolve\n * @param agentId - Agent ID for error messages\n * @returns Resolved spawn command\n * @throws PlatformNotSupportedError if current platform is not supported\n */\nexport function resolveBinary(\n distribution: BinaryDistribution,\n agentId: string,\n): SpawnCommand {\n const currentPlatform = getCurrentPlatform();\n const target: BinaryTarget | undefined = distribution[currentPlatform];\n\n if (!target) {\n throw new PlatformNotSupportedError(agentId, currentPlatform);\n }\n\n return {\n command: target.cmd,\n args: target.args ?? [],\n env: target.env,\n };\n}\n\n/**\n * Resolve an npx distribution to a spawn command.\n *\n * @param distribution - NPX distribution to resolve\n * @returns Resolved spawn command\n */\nexport function resolveNpx(distribution: NpxDistribution): SpawnCommand {\n return {\n command: 'npx',\n args: [distribution.package, ...(distribution.args ?? [])],\n env: distribution.env,\n };\n}\n\n/**\n * Resolve a uvx distribution to a spawn command.\n *\n * @param distribution - UVX distribution to resolve\n * @returns Resolved spawn command\n */\nexport function resolveUvx(distribution: UvxDistribution): SpawnCommand {\n return {\n command: 'uvx',\n args: [distribution.package, ...(distribution.args ?? [])],\n env: distribution.env,\n };\n}\n\n/**\n * Resolve a distribution to a spawn command.\n *\n * Priority order: npx > uvx > binary\n * (npx/uvx are preferred as they don't require platform-specific handling)\n *\n * @param distribution - Distribution to resolve\n * @param agentId - Agent ID for error messages\n * @returns Resolved spawn command\n * @throws PlatformNotSupportedError if binary distribution doesn't support current platform\n * @throws NoDistributionError if no distribution type is available\n */\nexport function resolve(\n distribution: Distribution,\n agentId: string,\n): SpawnCommand {\n // Prefer npx (most common, cross-platform)\n if (distribution.npx) {\n return resolveNpx(distribution.npx);\n }\n\n // Then uvx (Python packages)\n if (distribution.uvx) {\n return resolveUvx(distribution.uvx);\n }\n\n // Finally binary (platform-specific)\n if (distribution.binary) {\n return resolveBinary(distribution.binary, agentId);\n }\n\n throw new NoDistributionError(agentId);\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { ChildProcess, spawn } from 'child_process';\nimport type { AgentRuntime, RuntimeState } from './types.js';\nimport type { SpawnCommand } from '../registry/types.js';\n\n/**\n * Default timeout in milliseconds for graceful termination before SIGKILL.\n */\nconst DEFAULT_TERMINATE_TIMEOUT_MS = 5000;\n\n/**\n * Implementation of AgentRuntime that manages a spawned agent process.\n *\n * Handles process spawning with non-blocking file descriptors,\n * message writing to stdin, and graceful termination with SIGTERM/SIGKILL.\n */\nexport class AgentRuntimeImpl implements AgentRuntime {\n public readonly agentId: string;\n public state: RuntimeState;\n public readonly process: ChildProcess;\n\n private readonly onExitCallback?: (code: number | null, signal: string | null) => void;\n\n /**\n * Create a new AgentRuntime by spawning a process.\n *\n * @param agentId - The agent identifier\n * @param spawnCommand - The resolved spawn command\n * @param onExit - Optional callback when process exits\n */\n private constructor(\n agentId: string,\n process: ChildProcess,\n onExit?: (code: number | null, signal: string | null) => void,\n ) {\n this.agentId = agentId;\n this.process = process;\n this.state = 'starting';\n this.onExitCallback = onExit;\n\n this.setupProcessHandlers();\n }\n\n /**\n * Spawn a new agent process and create an AgentRuntime instance.\n *\n * @param agentId - The agent identifier\n * @param spawnCommand - The resolved spawn command with command, args, and optional env\n * @param onExit - Optional callback when process exits\n * @returns A new AgentRuntime instance\n */\n public static spawn(\n agentId: string,\n spawnCommand: SpawnCommand,\n onExit?: (code: number | null, signal: string | null) => void,\n ): AgentRuntimeImpl {\n const childProcess = spawn(spawnCommand.command, spawnCommand.args, {\n stdio: ['pipe', 'pipe', 'pipe'],\n env: {\n ...process.env,\n ...spawnCommand.env,\n },\n // Detach from parent's controlling terminal to avoid signal propagation issues\n detached: false,\n });\n\n // Set file descriptors to non-blocking mode\n // Node.js streams are already non-blocking by default when using 'pipe',\n // but we ensure the streams are in flowing mode for proper async handling\n if (childProcess.stdout) {\n childProcess.stdout.setEncoding('utf8');\n }\n if (childProcess.stderr) {\n childProcess.stderr.setEncoding('utf8');\n }\n\n return new AgentRuntimeImpl(agentId, childProcess, onExit);\n }\n\n /**\n * Set up event handlers for the child process.\n */\n private setupProcessHandlers(): void {\n // Handle process spawn success\n this.process.on('spawn', () => {\n if (this.state === 'starting') {\n this.state = 'running';\n }\n });\n\n // Handle process errors (e.g., spawn failure)\n this.process.on('error', (error: Error) => {\n this.state = 'stopped';\n // Log to stderr as per requirements\n process.stderr.write(\n `[${new Date().toISOString()}] ERROR: Agent ${this.agentId} process error: ${error.message}\\n`,\n );\n });\n\n // Handle process exit\n this.process.on('exit', (code: number | null, signal: string | null) => {\n this.state = 'stopped';\n if (this.onExitCallback) {\n this.onExitCallback(code, signal);\n }\n });\n\n // Handle stdin errors (e.g., broken pipe)\n if (this.process.stdin) {\n this.process.stdin.on('error', (error: Error) => {\n // Log stdin write errors but don't change state\n process.stderr.write(\n `[${new Date().toISOString()}] WARN: Agent ${this.agentId} stdin error: ${error.message}\\n`,\n );\n });\n }\n }\n\n /**\n * Write a message to the agent's stdin as NDJSON.\n *\n * @param message - The message object to send\n * @returns true if the write was accepted, false if backpressure or error\n */\n public write(message: object): boolean {\n if (this.state !== 'running' && this.state !== 'starting') {\n return false;\n }\n\n if (!this.process.stdin || this.process.stdin.destroyed) {\n return false;\n }\n\n try {\n const ndjsonLine = JSON.stringify(message) + '\\n';\n return this.process.stdin.write(ndjsonLine);\n } catch {\n return false;\n }\n }\n\n /**\n * Terminate the agent process gracefully.\n *\n * Sends SIGTERM first, then SIGKILL after timeout if process doesn't exit.\n *\n * @param timeout - Timeout in milliseconds before SIGKILL (default: 5000ms)\n * @returns Promise that resolves when process has exited\n */\n public async terminate(timeout: number = DEFAULT_TERMINATE_TIMEOUT_MS): Promise<void> {\n // Already stopped or stopping\n if (this.state === 'stopped') {\n return;\n }\n\n if (this.state === 'stopping') {\n // Wait for existing termination to complete\n return this.waitForExit();\n }\n\n this.state = 'stopping';\n\n // Close stdin to signal no more input\n if (this.process.stdin && !this.process.stdin.destroyed) {\n this.process.stdin.end();\n }\n\n // Send SIGTERM for graceful shutdown\n this.process.kill('SIGTERM');\n\n // Wait for exit with timeout\n const exitPromise = this.waitForExit();\n const timeoutPromise = new Promise<'timeout'>((resolve) => {\n setTimeout(() => resolve('timeout'), timeout);\n });\n\n const result = await Promise.race([exitPromise, timeoutPromise]);\n\n if (result === 'timeout' && !this.process.killed && this.process.exitCode === null) {\n // Force kill if still running after timeout\n this.process.kill('SIGKILL');\n await this.waitForExit();\n }\n }\n\n /**\n * Wait for the process to exit.\n *\n * @returns Promise that resolves when process exits\n */\n private waitForExit(): Promise<void> {\n if (this.state === 'stopped') {\n return Promise.resolve();\n }\n\n return new Promise((resolve) => {\n this.process.once('exit', () => {\n resolve();\n });\n });\n }\n}\n\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { AgentRuntime } from './types.js';\nimport type { SpawnCommand } from '../registry/types.js';\nimport { AgentRuntimeImpl } from './agent-runtime.js';\n\n/**\n * Default timeout in milliseconds for graceful termination before SIGKILL.\n */\nconst DEFAULT_SHUTDOWN_TIMEOUT_MS = 5000;\n\n/**\n * Callback type for agent exit events.\n */\nexport type AgentExitCallback = (agentId: string, code: number | null) => void;\n\n/**\n * Manager for agent runtime lifecycle.\n *\n * Handles spawning, tracking, and terminating agent processes.\n * Stores runtimes in a Map keyed by agentId.\n */\nexport class AgentRuntimeManager {\n /** Map of agentId to AgentRuntime instances */\n private readonly runtimes: Map<string, AgentRuntime> = new Map();\n\n /** Registered callbacks for agent exit events */\n private readonly exitCallbacks: AgentExitCallback[] = [];\n\n /**\n * Get an existing runtime or spawn a new one for the given agentId.\n *\n * If a runtime already exists for the agentId, returns it.\n * Otherwise, spawns a new agent process using the provided spawn command.\n *\n * @param agentId - The agent identifier\n * @param spawnCommand - The resolved spawn command for spawning a new process\n * @returns The existing or newly spawned AgentRuntime\n */\n public async getOrSpawn(agentId: string, spawnCommand: SpawnCommand): Promise<AgentRuntime> {\n // Return existing runtime if available\n const existing = this.runtimes.get(agentId);\n if (existing && existing.state !== 'stopped') {\n return existing;\n }\n\n // Spawn new runtime\n const runtime = AgentRuntimeImpl.spawn(\n agentId,\n spawnCommand,\n (code: number | null, _signal: string | null) => {\n this.handleAgentExit(agentId, code);\n },\n );\n\n // Store in map\n this.runtimes.set(agentId, runtime);\n\n return runtime;\n }\n\n /**\n * Get an existing runtime without spawning.\n *\n * @param agentId - The agent identifier\n * @returns The AgentRuntime if it exists, undefined otherwise\n */\n public get(agentId: string): AgentRuntime | undefined {\n return this.runtimes.get(agentId);\n }\n\n /**\n * Terminate a specific agent runtime.\n *\n * Sends SIGTERM to the agent process and waits for it to exit.\n * If the process doesn't exit within the timeout, sends SIGKILL.\n *\n * @param agentId - The agent identifier\n * @param timeout - Timeout in milliseconds before SIGKILL (default: 5000ms)\n */\n public async terminate(agentId: string, timeout: number = DEFAULT_SHUTDOWN_TIMEOUT_MS): Promise<void> {\n const runtime = this.runtimes.get(agentId);\n if (!runtime) {\n return;\n }\n\n await runtime.terminate(timeout);\n this.runtimes.delete(agentId);\n }\n\n /**\n * Terminate all agent runtimes for graceful shutdown.\n *\n * @param timeout - Timeout in milliseconds before SIGKILL (default: 5000ms)\n */\n public async terminateAll(timeout: number = DEFAULT_SHUTDOWN_TIMEOUT_MS): Promise<void> {\n const terminatePromises: Promise<void>[] = [];\n\n for (const [agentId, runtime] of this.runtimes) {\n if (runtime.state !== 'stopped') {\n terminatePromises.push(\n runtime.terminate(timeout).then(() => {\n this.runtimes.delete(agentId);\n }),\n );\n }\n }\n\n await Promise.all(terminatePromises);\n }\n\n /**\n * Register a callback for agent exit events.\n *\n * The callback is invoked when an agent process exits unexpectedly.\n *\n * @param callback - Function to call when an agent exits\n */\n public onAgentExit(callback: AgentExitCallback): void {\n this.exitCallbacks.push(callback);\n }\n\n /**\n * Handle agent process exit.\n *\n * Removes the runtime from the map and notifies registered callbacks.\n *\n * @param agentId - The agent identifier\n * @param code - The exit code (null if terminated by signal)\n */\n private handleAgentExit(agentId: string, code: number | null): void {\n // Remove from map\n this.runtimes.delete(agentId);\n\n // Notify all registered callbacks\n for (const callback of this.exitCallbacks) {\n try {\n callback(agentId, code);\n } catch {\n // Ignore callback errors to prevent one failing callback from affecting others\n }\n }\n }\n\n /**\n * Get the number of active runtimes.\n *\n * @returns The count of runtimes currently in the map\n */\n public get size(): number {\n return this.runtimes.size;\n }\n\n /**\n * Check if a runtime exists for the given agentId.\n *\n * @param agentId - The agent identifier\n * @returns true if a runtime exists, false otherwise\n */\n public has(agentId: string): boolean {\n return this.runtimes.has(agentId);\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * NDJSON (Newline-Delimited JSON) stream handler for the Registry Launcher.\n *\n * Handles buffering and parsing of NDJSON messages from stdin and writing\n * NDJSON messages to stdout. Supports partial reads across multiple chunks.\n *\n * @module stream/ndjson-handler\n */\n\nimport { Writable } from 'node:stream';\n\n/**\n * Callback type for parsed messages.\n */\nexport type MessageCallback = (message: object) => void;\n\n/**\n * Callback type for parse errors.\n */\nexport type ErrorCallback = (error: Error, line: string) => void;\n\n/**\n * Interface for NDJSON stream handling.\n */\nexport interface INDJSONHandler {\n /**\n * Register a callback for parsed messages.\n */\n onMessage(callback: MessageCallback): void;\n\n /**\n * Register a callback for parse errors.\n */\n onError(callback: ErrorCallback): void;\n\n /**\n * Write a message to the output stream.\n * @returns true if write was successful, false if stream is not writable\n */\n write(message: object): boolean;\n\n /**\n * Process incoming data chunk (call from stdin 'data' event).\n */\n processChunk(chunk: Buffer): void;\n}\n\n/**\n * Log an error message to stderr with ISO 8601 timestamp.\n * @param message - Error message to log\n */\nfunction logError(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`[${timestamp}] [ERROR] [ndjson] ${message}`);\n}\n\n/**\n * NDJSON stream handler implementation.\n *\n * This class handles:\n * - Buffering incoming data until complete newline-delimited messages are received\n * - Handling partial JSON messages that span multiple read operations\n * - Appending newline characters after each JSON message when writing\n * - Splitting on newline boundaries when reading\n * - Logging errors and skipping malformed lines\n */\nexport class NDJSONHandler implements INDJSONHandler {\n /** Buffer for accumulating partial data */\n private buffer: string = '';\n\n /** Output stream for writing messages */\n private readonly output: Writable;\n\n /** Registered message callback */\n private messageCallback: MessageCallback | null = null;\n\n /** Registered error callback */\n private errorCallback: ErrorCallback | null = null;\n\n /**\n * Create a new NDJSONHandler.\n * @param output - Writable stream for output (typically process.stdout)\n */\n constructor(output: Writable) {\n this.output = output;\n }\n\n /**\n * Register a callback for parsed messages.\n * Only one callback can be registered at a time.\n *\n * @param callback - Function to call with each parsed message\n */\n onMessage(callback: MessageCallback): void {\n this.messageCallback = callback;\n }\n\n /**\n * Register a callback for parse errors.\n * Only one callback can be registered at a time.\n *\n * @param callback - Function to call with parse errors\n */\n onError(callback: ErrorCallback): void {\n this.errorCallback = callback;\n }\n\n /**\n * Write a message to the output stream.\n *\n * Serializes the message as JSON and appends a newline character.\n *\n * @param message - Object to serialize and write\n * @returns true if write was successful, false if stream is not writable\n */\n write(message: object): boolean {\n if (!this.output.writable) {\n return false;\n }\n\n try {\n const json = JSON.stringify(message);\n this.output.write(json + '\\n');\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Process incoming data chunk.\n *\n * Buffers data and emits complete messages when newline boundaries are found.\n * Handles partial messages that span multiple chunks.\n *\n * @param chunk - Buffer containing incoming data\n */\n processChunk(chunk: Buffer): void {\n // Append chunk to buffer\n this.buffer += chunk.toString('utf-8');\n\n // Process complete lines\n this.processBuffer();\n }\n\n /**\n * Process the internal buffer, extracting and parsing complete lines.\n *\n * Splits on newline boundaries and parses each complete line as JSON.\n * Incomplete lines remain in the buffer for the next chunk.\n */\n private processBuffer(): void {\n let newlineIndex: number;\n\n // Process all complete lines in the buffer\n while ((newlineIndex = this.buffer.indexOf('\\n')) !== -1) {\n // Extract the line (without the newline)\n const line = this.buffer.slice(0, newlineIndex);\n\n // Remove the processed line and newline from buffer\n this.buffer = this.buffer.slice(newlineIndex + 1);\n\n // Skip empty lines\n if (line.trim().length === 0) {\n continue;\n }\n\n // Parse and emit the message\n this.parseLine(line);\n }\n }\n\n /**\n * Parse a single line as JSON and emit the message.\n *\n * If parsing fails, logs the error and invokes the error callback.\n * Malformed lines are skipped.\n *\n * @param line - Line to parse as JSON\n */\n private parseLine(line: string): void {\n try {\n const message = JSON.parse(line);\n\n // Ensure the parsed value is an object (not a primitive)\n if (message === null || typeof message !== 'object') {\n const error = new Error('Parsed JSON is not an object');\n logError(`Malformed NDJSON line (not an object): ${this.truncateLine(line)}`);\n this.errorCallback?.(error, line);\n return;\n }\n\n // Emit the parsed message\n this.messageCallback?.(message);\n } catch (error) {\n // Log the error and skip the malformed line\n logError(`Failed to parse NDJSON line: ${this.truncateLine(line)}`);\n this.errorCallback?.(error as Error, line);\n }\n }\n\n /**\n * Truncate a line for logging purposes.\n * @param line - Line to truncate\n * @param maxLength - Maximum length (default: 100)\n * @returns Truncated line with ellipsis if needed\n */\n private truncateLine(line: string, maxLength: number = 100): string {\n if (line.length <= maxLength) {\n return line;\n }\n return line.slice(0, maxLength) + '...';\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Registry Index for the ACP Registry.\n *\n * Handles fetching, parsing, and querying the ACP Registry.\n * Supports configurable registry URL via environment variable.\n *\n * @module registry/index\n */\n\nimport { Distribution, Registry, RegistryAgent, SpawnCommand } from './types.js';\nimport { resolve as resolveDistribution } from './resolver.js';\n\n// Re-export types for external use\nexport type {\n Platform,\n BinaryDistribution,\n BinaryTarget,\n NpxDistribution,\n UvxDistribution,\n Distribution,\n RegistryAgent,\n Registry,\n SpawnCommand,\n} from './types.js';\n\n// Re-export resolver functions and errors for external use\nexport { PlatformNotSupportedError, NoDistributionError, getCurrentPlatform, resolve } from './resolver.js';\n\n/**\n * Environment variable name for registry URL override.\n */\nconst ENV_REGISTRY_URL = 'ACP_REGISTRY_URL';\n\n/**\n * Error thrown when registry fetch fails.\n */\nexport class RegistryFetchError extends Error {\n constructor(message: string, public readonly cause?: Error) {\n super(message);\n this.name = 'RegistryFetchError';\n }\n}\n\n/**\n * Error thrown when registry JSON is malformed.\n */\nexport class RegistryParseError extends Error {\n constructor(message: string, public readonly cause?: Error) {\n super(message);\n this.name = 'RegistryParseError';\n }\n}\n\n/**\n * Error thrown when an agent is not found in the registry.\n */\nexport class AgentNotFoundError extends Error {\n constructor(public readonly agentId: string) {\n super(`Agent not found: ${agentId}`);\n this.name = 'AgentNotFoundError';\n }\n}\n\n/**\n * Log an error message to stderr with ISO 8601 timestamp.\n * @param message - Error message to log\n */\nfunction logError(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`[${timestamp}] [ERROR] [registry] ${message}`);\n}\n\n/**\n * Log an info message to stderr with ISO 8601 timestamp.\n * @param message - Info message to log\n */\nfunction logInfo(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`[${timestamp}] [INFO] [registry] ${message}`);\n}\n\n/**\n * Interface for the RegistryIndex.\n */\nexport interface IRegistryIndex {\n /**\n * Fetch and parse the ACP Registry from the configured URL.\n * @throws RegistryFetchError if fetch fails\n * @throws RegistryParseError if JSON is malformed\n */\n fetch(): Promise<void>;\n\n /**\n * Look up an agent by ID.\n * @returns The agent entry or undefined if not found\n */\n lookup(agentId: string): RegistryAgent | undefined;\n\n /**\n * Resolve an agent ID to a spawn command.\n * @throws AgentNotFoundError if agent not in registry\n * @throws PlatformNotSupportedError if binary distribution doesn't support current platform\n */\n resolve(agentId: string): SpawnCommand;\n}\n\n/**\n * Validate that a value is a non-empty string.\n */\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.length > 0;\n}\n\n/**\n * Validate that a value is a valid distribution object.\n * Distribution must have at least one of: binary, npx, uvx\n */\nfunction isValidDistribution(value: unknown): value is Distribution {\n if (value === null || typeof value !== 'object') {\n return false;\n }\n\n const dist = value as Record<string, unknown>;\n\n // Must have at least one distribution type\n const hasBinary = dist.binary !== undefined && typeof dist.binary === 'object';\n const hasNpx = dist.npx !== undefined && typeof dist.npx === 'object';\n const hasUvx = dist.uvx !== undefined && typeof dist.uvx === 'object';\n\n if (!hasBinary && !hasNpx && !hasUvx) {\n return false;\n }\n\n // Validate npx if present\n if (hasNpx) {\n const npx = dist.npx as Record<string, unknown>;\n if (!isNonEmptyString(npx.package)) {\n return false;\n }\n }\n\n // Validate uvx if present\n if (hasUvx) {\n const uvx = dist.uvx as Record<string, unknown>;\n if (!isNonEmptyString(uvx.package)) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Validate and parse a registry agent entry.\n */\nfunction parseAgent(value: unknown, index: number): RegistryAgent {\n if (value === null || typeof value !== 'object') {\n throw new RegistryParseError(`Agent at index ${index} is not an object`);\n }\n\n const raw = value as Record<string, unknown>;\n\n if (!isNonEmptyString(raw.id)) {\n throw new RegistryParseError(`Agent at index ${index} has invalid or missing \"id\" field`);\n }\n\n if (!isNonEmptyString(raw.name)) {\n throw new RegistryParseError(`Agent at index ${index} has invalid or missing \"name\" field`);\n }\n\n if (!isNonEmptyString(raw.version)) {\n throw new RegistryParseError(`Agent at index ${index} has invalid or missing \"version\" field`);\n }\n\n if (!isValidDistribution(raw.distribution)) {\n throw new RegistryParseError(`Agent at index ${index} has invalid or missing \"distribution\" field`);\n }\n\n const agent: RegistryAgent = {\n id: raw.id,\n name: raw.name,\n version: raw.version,\n distribution: raw.distribution as Distribution,\n };\n\n // Optional fields\n if (typeof raw.description === 'string') {\n agent.description = raw.description;\n }\n\n if (typeof raw.repository === 'string') {\n agent.repository = raw.repository;\n }\n\n if (Array.isArray(raw.authors)) {\n agent.authors = raw.authors.filter((a): a is string => typeof a === 'string');\n }\n\n if (typeof raw.license === 'string') {\n agent.license = raw.license;\n }\n\n if (typeof raw.icon === 'string') {\n agent.icon = raw.icon;\n }\n\n return agent;\n}\n\n/**\n * Parse and validate registry JSON data.\n *\n * @param data - Raw JSON data to parse\n * @returns Parsed and validated Registry object\n * @throws RegistryParseError if JSON is malformed or invalid\n */\nexport function parseRegistry(data: unknown): Registry {\n if (data === null || typeof data !== 'object') {\n throw new RegistryParseError('Registry data is not an object');\n }\n\n const raw = data as Record<string, unknown>;\n\n // Validate version field\n if (!isNonEmptyString(raw.version)) {\n throw new RegistryParseError('Registry has invalid or missing \"version\" field');\n }\n\n // Validate agents array\n if (!Array.isArray(raw.agents)) {\n throw new RegistryParseError('Registry has invalid or missing \"agents\" field');\n }\n\n // Parse each agent\n const agents: RegistryAgent[] = [];\n for (let i = 0; i < raw.agents.length; i++) {\n agents.push(parseAgent(raw.agents[i], i));\n }\n\n return {\n version: raw.version,\n agents,\n };\n}\n\n/**\n * Registry Index implementation.\n *\n * Fetches, parses, and provides lookup functionality for the ACP Registry.\n */\nexport class RegistryIndex implements IRegistryIndex {\n /** Configured registry URL */\n private readonly registryUrl: string;\n\n /** Parsed registry data (null until fetch() is called) */\n private registry: Registry | null = null;\n\n /** Map of agent ID to agent entry for fast lookup */\n private agentMap: Map<string, RegistryAgent> = new Map();\n\n /**\n * Create a new RegistryIndex.\n *\n * @param registryUrl - URL to fetch the registry from (can be overridden by ACP_REGISTRY_URL env var)\n */\n constructor(registryUrl: string) {\n // Environment variable takes precedence\n const envUrl = process.env[ENV_REGISTRY_URL];\n this.registryUrl = isNonEmptyString(envUrl) ? envUrl : registryUrl;\n }\n\n /**\n * Fetch and parse the ACP Registry from the configured URL.\n *\n * @throws RegistryFetchError if fetch fails\n * @throws RegistryParseError if JSON is malformed\n */\n async fetch(): Promise<void> {\n logInfo(`Fetching registry from ${this.registryUrl}`);\n\n let response: Response;\n try {\n response = await fetch(this.registryUrl);\n } catch (error) {\n const message = `Failed to fetch registry from ${this.registryUrl}: ${(error as Error).message}`;\n logError(message);\n throw new RegistryFetchError(message, error as Error);\n }\n\n if (!response.ok) {\n const message = `Failed to fetch registry from ${this.registryUrl}: HTTP ${response.status} ${response.statusText}`;\n logError(message);\n throw new RegistryFetchError(message);\n }\n\n let text: string;\n try {\n text = await response.text();\n } catch (error) {\n const message = `Failed to read registry response body: ${(error as Error).message}`;\n logError(message);\n throw new RegistryFetchError(message, error as Error);\n }\n\n let data: unknown;\n try {\n data = JSON.parse(text);\n } catch (error) {\n const message = `Failed to parse registry JSON: ${(error as Error).message}`;\n logError(message);\n throw new RegistryParseError(message, error as Error);\n }\n\n try {\n this.registry = parseRegistry(data);\n } catch (error) {\n if (error instanceof RegistryParseError) {\n logError(error.message);\n throw error;\n }\n const message = `Failed to validate registry data: ${(error as Error).message}`;\n logError(message);\n throw new RegistryParseError(message, error as Error);\n }\n\n // Build the agent lookup map\n this.agentMap.clear();\n for (const agent of this.registry.agents) {\n this.agentMap.set(agent.id, agent);\n }\n\n logInfo(`Registry loaded: version ${this.registry.version}, ${this.registry.agents.length} agents`);\n }\n\n /**\n * Look up an agent by ID.\n *\n * @param agentId - Agent ID to look up\n * @returns The agent entry or undefined if not found\n */\n lookup(agentId: string): RegistryAgent | undefined {\n return this.agentMap.get(agentId);\n }\n\n /**\n * Resolve an agent ID to a spawn command.\n *\n * @param agentId - Agent ID to resolve\n * @returns Resolved spawn command\n * @throws AgentNotFoundError if agent not in registry\n * @throws PlatformNotSupportedError if binary distribution doesn't support current platform\n */\n resolve(agentId: string): SpawnCommand {\n const agent = this.lookup(agentId);\n if (!agent) {\n throw new AgentNotFoundError(agentId);\n }\n\n return resolveDistribution(agent.distribution, agentId);\n }\n\n /**\n * Get the parsed registry data.\n * @returns The parsed registry or null if not yet fetched\n */\n getRegistry(): Registry | null {\n return this.registry;\n }\n}\n\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * API Keys loading for the Registry Launcher.\n *\n * Handles loading API keys from JSON files for agent authentication.\n *\n * @module config/api-keys\n */\n\nimport { readFileSync } from 'node:fs';\n\n/**\n * API keys structure for a single agent.\n */\nexport interface AgentApiKeys {\n /** API key for the agent */\n apiKey: string;\n /** Environment variables to pass to the agent */\n env: Record<string, string>;\n}\n\n/**\n * API keys file structure.\n */\nexport interface ApiKeysFile {\n /** Map of agent ID to API keys */\n agents: Record<string, AgentApiKeys>;\n /** Version of the API keys file format */\n version: string;\n}\n\n/**\n * Log a warning message to stderr with ISO 8601 timestamp.\n * @param message - Warning message to log\n */\nfunction logWarning(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`[${timestamp}] [WARN] [api-keys] ${message}`);\n}\n\n/**\n * Log an info message to stderr with ISO 8601 timestamp.\n * @param message - Info message to log\n */\nfunction logInfo(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`[${timestamp}] [INFO] [api-keys] ${message}`);\n}\n\n/**\n * Load API keys from a JSON file.\n *\n * @param apiKeysPath - Path to the API keys JSON file\n * @returns Loaded API keys or empty object if file is missing/malformed\n */\nexport function loadApiKeys(apiKeysPath: string): Record<string, AgentApiKeys> {\n try {\n const fileContent = readFileSync(apiKeysPath, 'utf-8');\n const parsed = JSON.parse(fileContent) as ApiKeysFile;\n\n if (!parsed.agents || typeof parsed.agents !== 'object') {\n logWarning(`API keys file \"${apiKeysPath}\" does not contain valid \"agents\" object`);\n return {};\n }\n\n // Count agents with non-empty API keys\n const agentsWithKeys = Object.entries(parsed.agents).filter(\n ([_, keys]) => keys.apiKey && keys.apiKey.length > 0,\n );\n\n logInfo(`Loaded API keys for ${agentsWithKeys.length} agents from \"${apiKeysPath}\"`);\n\n return parsed.agents;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n logWarning(`API keys file \"${apiKeysPath}\" not found, agents will not be authenticated`);\n } else if (error instanceof SyntaxError) {\n logWarning(`API keys file \"${apiKeysPath}\" contains malformed JSON`);\n } else {\n logWarning(`Failed to read API keys file \"${apiKeysPath}\": ${(error as Error).message}`);\n }\n return {};\n }\n}\n\n/**\n * Get API key for a specific agent.\n *\n * @param apiKeys - Loaded API keys\n * @param agentId - Agent ID to get key for\n * @returns API key or undefined if not found\n */\nexport function getAgentApiKey(\n apiKeys: Record<string, AgentApiKeys>,\n agentId: string,\n): string | undefined {\n const keys = apiKeys[agentId];\n if (!keys || !keys.apiKey || keys.apiKey.length === 0) {\n return undefined;\n }\n return keys.apiKey;\n}\n\n/**\n * Get environment variables for a specific agent.\n *\n * @param apiKeys - Loaded API keys\n * @param agentId - Agent ID to get env vars for\n * @returns Environment variables or empty object if not found\n */\nexport function getAgentEnv(\n apiKeys: Record<string, AgentApiKeys>,\n agentId: string,\n): Record<string, string> {\n const keys = apiKeys[agentId];\n if (!keys || !keys.env) {\n return {};\n }\n return keys.env;\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Message Router for the Registry Launcher.\n *\n * Routes incoming JSON-RPC messages to the appropriate agent based on agentId.\n * Handles agentId extraction, message transformation, and error response generation.\n *\n * @module router/message-router\n */\n\nimport type { IRegistryIndex } from '../registry/index.js';\nimport { AgentNotFoundError, PlatformNotSupportedError } from '../registry/index.js';\nimport type { AgentRuntimeManager } from '../runtime/manager.js';\nimport type { AgentRuntime } from '../runtime/types.js';\nimport { getAgentApiKey } from '../config/api-keys.js';\n\n/**\n * JSON-RPC error codes for routing errors.\n */\nexport const RoutingErrorCodes = {\n /** Missing agentId in request */\n MISSING_AGENT_ID: -32600,\n /** Agent not found in registry */\n AGENT_NOT_FOUND: -32001,\n /** Platform not supported for binary distribution */\n PLATFORM_NOT_SUPPORTED: -32002,\n /** Agent spawn failed */\n SPAWN_FAILED: -32003,\n} as const;\n\n/**\n * JSON-RPC error response structure.\n */\nexport interface ErrorResponse {\n jsonrpc: '2.0';\n id: string | number | null;\n error: {\n code: number;\n message: string;\n data?: unknown;\n };\n}\n\n/**\n * Pending request tracking structure.\n */\ninterface PendingRequest {\n id: string | number;\n agentId: string;\n timestamp: number;\n}\n\n/**\n * Callback type for writing responses to stdout.\n */\nexport type WriteCallback = (message: object) => boolean;\n\n/**\n * Log an error message to stderr with ISO 8601 timestamp.\n */\nfunction logError(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`[${timestamp}] [ERROR] [router] ${message}`);\n}\n\n/**\n * Log an info message to stderr with ISO 8601 timestamp.\n */\nfunction logInfo(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`[${timestamp}] [INFO] [router] ${message}`);\n}\n\n\n/**\n * Create a JSON-RPC error response.\n *\n * @param id - Request ID (null for notifications or unknown)\n * @param code - Error code\n * @param message - Error message\n * @param data - Optional additional error data\n * @returns Error response object\n */\nexport function createErrorResponse(\n id: string | number | null,\n code: number,\n message: string,\n data?: unknown,\n): ErrorResponse {\n const response: ErrorResponse = {\n jsonrpc: '2.0',\n id,\n error: { code, message },\n };\n\n if (data !== undefined) {\n response.error.data = data;\n }\n\n return response;\n}\n\n/**\n * Extract the agentId field from a message.\n *\n * @param message - The message object to extract from\n * @returns The agentId string or undefined if not present\n */\nexport function extractAgentId(message: object): string | undefined {\n const msg = message as Record<string, unknown>;\n const agentId = msg.agentId;\n\n if (typeof agentId === 'string' && agentId.length > 0) {\n return agentId;\n }\n\n return undefined;\n}\n\n/**\n * Extract the JSON-RPC id field from a message.\n *\n * @param message - The message object to extract from\n * @returns The id (string, number, or null)\n */\nexport function extractId(message: object): string | number | null {\n const msg = message as Record<string, unknown>;\n const id = msg.id;\n\n if (typeof id === 'string' || typeof id === 'number') {\n return id;\n }\n\n return null;\n}\n\n/**\n * Transform a message for forwarding to an agent.\n *\n * Removes the agentId field while preserving all other fields.\n *\n * @param message - The original message\n * @returns A new message object without the agentId field\n */\nexport function transformMessage(message: object): object {\n const { agentId: _, ...rest } = message as Record<string, unknown>;\n return rest;\n}\n\n\n/**\n * Message Router implementation.\n *\n * Routes incoming JSON-RPC messages to the appropriate agent based on agentId.\n * Handles message transformation, error generation, and request correlation.\n */\nexport class MessageRouter {\n /** Registry index for agent lookup and resolution */\n private readonly registry: IRegistryIndex;\n\n /** Runtime manager for agent process lifecycle */\n private readonly runtimeManager: AgentRuntimeManager;\n\n /** Callback for writing responses to stdout */\n private readonly writeCallback: WriteCallback;\n\n /** API keys for agent authentication */\n private readonly apiKeys: Record<string, any>;\n\n /** Map of request ID to pending request info for correlation */\n private readonly pendingRequests: Map<string | number, PendingRequest> = new Map();\n\n /** Map of agent ID to authentication state */\n private readonly authState: Map<string, 'none' | 'pending' | 'authenticated'> = new Map();\n\n /** Map of agent sessionId to client sessionId for notification routing */\n private readonly sessionIdMap: Map<string, string> = new Map();\n\n /**\n * Create a new MessageRouter.\n *\n * @param registry - Registry index for agent lookup\n * @param runtimeManager - Runtime manager for agent processes\n * @param writeCallback - Callback for writing responses to stdout\n * @param apiKeys - API keys for agent authentication (optional)\n */\n constructor(\n registry: IRegistryIndex,\n runtimeManager: AgentRuntimeManager,\n writeCallback: WriteCallback,\n apiKeys: Record<string, any> = {},\n ) {\n this.registry = registry;\n this.runtimeManager = runtimeManager;\n this.writeCallback = writeCallback;\n this.apiKeys = apiKeys;\n }\n\n /**\n * Route an incoming message to the appropriate agent.\n *\n * Extracts agentId, resolves spawn command, and forwards message.\n *\n * @param message - The incoming JSON-RPC message\n * @returns Error response if routing fails, undefined on success\n */\n async route(message: object): Promise<ErrorResponse | undefined> {\n const id = extractId(message);\n const agentId = extractAgentId(message);\n\n // Return error if agentId is missing\n if (agentId === undefined) {\n logError('Missing agentId in request');\n return createErrorResponse(id, RoutingErrorCodes.MISSING_AGENT_ID, 'Missing agentId');\n }\n\n // Resolve agent to spawn command\n let spawnCommand;\n try {\n spawnCommand = this.registry.resolve(agentId);\n } catch (error) {\n if (error instanceof AgentNotFoundError) {\n logError(`Agent not found: ${agentId}`);\n return createErrorResponse(id, RoutingErrorCodes.AGENT_NOT_FOUND, 'Agent not found', {\n agentId,\n });\n }\n if (error instanceof PlatformNotSupportedError) {\n logError(`Platform not supported for agent: ${agentId}`);\n return createErrorResponse(\n id,\n RoutingErrorCodes.PLATFORM_NOT_SUPPORTED,\n 'Platform not supported',\n { agentId, platform: (error as PlatformNotSupportedError).platform },\n );\n }\n throw error;\n }\n\n // Get or spawn agent runtime\n let runtime: AgentRuntime;\n try {\n runtime = await this.runtimeManager.getOrSpawn(agentId, spawnCommand);\n } catch (error) {\n logError(`Failed to spawn agent ${agentId}: ${(error as Error).message}`);\n return createErrorResponse(id, RoutingErrorCodes.SPAWN_FAILED, 'Agent spawn failed', {\n agentId,\n error: (error as Error).message,\n });\n }\n\n // Track pending request for correlation\n if (id !== null) {\n const msg = message as Record<string, unknown>;\n const clientSessionId = typeof msg.sessionId === 'string' ? msg.sessionId : undefined;\n\n this.pendingRequests.set(id, {\n id,\n agentId,\n timestamp: Date.now(),\n clientSessionId,\n } as any);\n }\n\n // Transform message (remove agentId) and forward to agent\n const transformedMessage = transformMessage(message);\n const success = runtime.write(transformedMessage);\n\n if (!success) {\n logError(`Failed to write to agent ${agentId}`);\n // Remove from pending if write failed\n if (id !== null) {\n this.pendingRequests.delete(id);\n }\n } else {\n logInfo(`Routed message to agent ${agentId}`);\n }\n\n return undefined;\n }\n\n /**\n * Handle a response from an agent process.\n *\n * Intercepts initialize responses to trigger automatic authentication.\n * Tracks sessionId mapping for proper notification routing.\n * Forwards all responses to stdout.\n *\n * @param agentId - The agent that sent the response\n * @param response - The response object from the agent\n */\n handleAgentResponse(agentId: string, response: object): void {\n const id = extractId(response);\n const msg = response as Record<string, unknown>;\n\n // Check if this is a response to a tracked request\n if (id !== null) {\n const pending = this.pendingRequests.get(id);\n if (pending && pending.agentId === agentId) {\n const result = msg.result as Record<string, unknown> | undefined;\n\n // Check if this is an initialize response with authMethods\n if (result && Array.isArray(result.authMethods) && result.authMethods.length > 0) {\n logInfo(`Agent ${agentId} requires authentication, attempting auto-auth`);\n this.authState.set(agentId, 'pending');\n void this.attemptAuthentication(agentId, result.authMethods as Array<{ id: string }>);\n }\n\n // Check if this is a session/new response - track sessionId mapping\n // session/new responses have sessionId in result, not in params\n if (result && typeof result.sessionId === 'string') {\n const agentSessionId = result.sessionId;\n const clientSessionId = (pending as any).clientSessionId;\n if (clientSessionId) {\n this.sessionIdMap.set(agentSessionId, clientSessionId);\n logInfo(`Mapped agent sessionId ${agentSessionId} to client sessionId ${clientSessionId}`);\n }\n }\n\n this.pendingRequests.delete(id);\n }\n }\n\n // Handle notifications (no id) - map sessionId from agent to client\n if (id === null && msg.method) {\n const params = msg.params as Record<string, unknown> | undefined;\n if (params && typeof params.sessionId === 'string') {\n const agentSessionId = params.sessionId;\n const clientSessionId = this.sessionIdMap.get(agentSessionId);\n\n if (clientSessionId) {\n // Replace agent sessionId with client sessionId for stdio Bus routing\n const enriched = {\n ...msg,\n sessionId: clientSessionId,\n params: {\n ...params,\n sessionId: agentSessionId, // Keep original in params for agent context\n },\n };\n this.writeCallback(enriched);\n return;\n } else {\n logError(`No sessionId mapping found for agent sessionId: ${agentSessionId}`);\n }\n }\n }\n\n // Forward response unchanged\n this.writeCallback(response);\n }\n\n /**\n * Attempt automatic authentication for an agent.\n *\n * Selects the best authentication method and sends authenticate request.\n *\n * @param agentId - The agent to authenticate\n * @param authMethods - Available authentication methods from initialize response\n */\n private async attemptAuthentication(\n agentId: string,\n authMethods: Array<{ id: string }>,\n ): Promise<void> {\n // Get API key for this agent\n const apiKey = getAgentApiKey(this.apiKeys, agentId);\n\n if (!apiKey) {\n logError(`No API key found for agent ${agentId}, authentication will fail`);\n this.authState.set(agentId, 'none');\n return;\n }\n\n // Select authentication method (prefer openai-api-key, then any api-key method)\n let selectedMethod = authMethods.find(m => m.id === 'openai-api-key');\n if (!selectedMethod) {\n selectedMethod = authMethods.find(m => m.id.includes('api-key') || m.id.includes('apikey'));\n }\n if (!selectedMethod) {\n selectedMethod = authMethods[0];\n }\n\n logInfo(`Authenticating agent ${agentId} with method: ${selectedMethod.id}`);\n\n // Get agent runtime\n let runtime: AgentRuntime;\n try {\n const spawnCommand = this.registry.resolve(agentId);\n runtime = await this.runtimeManager.getOrSpawn(agentId, spawnCommand);\n } catch (error) {\n logError(`Failed to get runtime for authentication: ${(error as Error).message}`);\n this.authState.set(agentId, 'none');\n return;\n }\n\n // Build authenticate request\n const authRequest = {\n jsonrpc: '2.0',\n id: `auth-${agentId}-${Date.now()}`,\n method: 'authenticate',\n params: {\n methodId: selectedMethod.id,\n credentials: {\n apiKey: apiKey,\n },\n },\n };\n\n // Send authenticate request to agent\n const transformed = transformMessage(authRequest);\n const serialized = JSON.stringify(transformed) + '\\n';\n\n if (runtime.process.stdin) {\n runtime.process.stdin.write(serialized, (error) => {\n if (error) {\n logError(`Failed to send authenticate request to ${agentId}: ${error.message}`);\n this.authState.set(agentId, 'none');\n } else {\n logInfo(`Sent authenticate request to agent ${agentId}`);\n // Mark as authenticated (optimistic)\n this.authState.set(agentId, 'authenticated');\n }\n });\n }\n }\n\n /**\n * Get the number of pending requests.\n *\n * @returns The count of pending requests\n */\n get pendingCount(): number {\n return this.pendingRequests.size;\n }\n\n /**\n * Check if a request ID is pending.\n *\n * @param id - The request ID to check\n * @returns true if the request is pending, false otherwise\n */\n isPending(id: string | number): boolean {\n return this.pendingRequests.has(id);\n }\n\n /**\n * Clear all pending requests.\n * Useful for cleanup during shutdown.\n */\n clearPending(): void {\n this.pendingRequests.clear();\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Configuration loading for the Registry Launcher.\n *\n * Handles loading configuration from JSON files with support for:\n * - Environment variable overrides (ACP_REGISTRY_URL)\n * - Default values for missing fields\n * - Warning logs for missing or malformed config files\n *\n * @module config\n */\n\nimport { readFileSync } from 'node:fs';\nimport { DEFAULT_CONFIG, LauncherConfig } from './types.js';\n\n/**\n * Environment variable name for registry URL override.\n */\nconst ENV_REGISTRY_URL = 'ACP_REGISTRY_URL';\n\n/**\n * Environment variable name for API keys path override.\n */\nconst ENV_API_KEYS_PATH = 'ACP_API_KEYS_PATH';\n\n/**\n * Log a warning message to stderr with ISO 8601 timestamp.\n * @param message - Warning message to log\n */\nfunction logWarning(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`[${timestamp}] [WARN] [config] ${message}`);\n}\n\n/**\n * Validate that a value is a non-empty string.\n * @param value - Value to validate\n * @returns True if value is a non-empty string\n */\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.length > 0;\n}\n\n/**\n * Validate that a value is a positive number.\n * @param value - Value to validate\n * @returns True if value is a positive number\n */\nfunction isPositiveNumber(value: unknown): value is number {\n return typeof value === 'number' && value > 0 && Number.isFinite(value);\n}\n\n/**\n * Parse and validate a configuration object.\n * Applies default values for missing or invalid fields.\n *\n * @param obj - Parsed JSON object\n * @returns Validated LauncherConfig with defaults applied\n */\nfunction parseConfigObject(obj: unknown): LauncherConfig {\n const config: LauncherConfig = { ...DEFAULT_CONFIG };\n\n if (obj === null || typeof obj !== 'object') {\n logWarning('Config file does not contain a valid object, using defaults');\n return config;\n }\n\n const rawConfig = obj as Record<string, unknown>;\n\n // Parse registryUrl\n if ('registryUrl' in rawConfig) {\n if (isNonEmptyString(rawConfig.registryUrl)) {\n config.registryUrl = rawConfig.registryUrl;\n } else {\n logWarning('Config field \"registryUrl\" is not a valid string, using default');\n }\n }\n\n // Parse apiKeysPath\n if ('apiKeysPath' in rawConfig) {\n if (isNonEmptyString(rawConfig.apiKeysPath)) {\n config.apiKeysPath = rawConfig.apiKeysPath;\n } else {\n logWarning('Config field \"apiKeysPath\" is not a valid string, using default');\n }\n }\n\n // Parse shutdownTimeoutSec\n if ('shutdownTimeoutSec' in rawConfig) {\n if (isPositiveNumber(rawConfig.shutdownTimeoutSec)) {\n config.shutdownTimeoutSec = rawConfig.shutdownTimeoutSec;\n } else {\n logWarning('Config field \"shutdownTimeoutSec\" is not a valid positive number, using default');\n }\n }\n\n return config;\n}\n\n/**\n * Apply environment variable overrides to the configuration.\n * Environment variables take precedence over config file values.\n *\n * @param config - Configuration to apply overrides to\n * @returns Configuration with environment overrides applied\n */\nfunction applyEnvironmentOverrides(config: LauncherConfig): LauncherConfig {\n const envRegistryUrl = process.env[ENV_REGISTRY_URL];\n const envApiKeysPath = process.env[ENV_API_KEYS_PATH];\n\n const overrides: Partial<LauncherConfig> = {};\n\n if (isNonEmptyString(envRegistryUrl)) {\n overrides.registryUrl = envRegistryUrl;\n }\n\n if (isNonEmptyString(envApiKeysPath)) {\n overrides.apiKeysPath = envApiKeysPath;\n }\n\n return {\n ...config,\n ...overrides,\n };\n}\n\n/**\n * Load configuration from a JSON file.\n *\n * This function:\n * 1. Reads the config file from the specified path\n * 2. Parses it as JSON\n * 3. Validates and applies default values for missing fields\n * 4. Applies environment variable overrides\n *\n * If the config file is missing or malformed, default values are used\n * and a warning is logged to stderr.\n *\n * @param configPath - Path to the JSON config file (optional)\n * @returns Loaded and validated configuration\n */\nexport function loadConfig(configPath?: string): LauncherConfig {\n let config: LauncherConfig = { ...DEFAULT_CONFIG };\n\n if (configPath) {\n try {\n const fileContent = readFileSync(configPath, 'utf-8');\n const parsed = JSON.parse(fileContent);\n config = parseConfigObject(parsed);\n } catch (error) {\n if (error instanceof SyntaxError) {\n logWarning(`Config file \"${configPath}\" contains malformed JSON, using defaults`);\n } else if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n logWarning(`Config file \"${configPath}\" not found, using defaults`);\n } else if ((error as NodeJS.ErrnoException).code === 'EACCES') {\n logWarning(`Config file \"${configPath}\" is not readable, using defaults`);\n } else {\n logWarning(`Failed to read config file \"${configPath}\": ${(error as Error).message}, using defaults`);\n }\n }\n }\n\n // Apply environment variable overrides\n config = applyEnvironmentOverrides(config);\n\n return config;\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Configuration for the Registry Launcher.\n */\nexport interface LauncherConfig {\n /** Registry URL (default: official ACP Registry) */\n registryUrl: string;\n /** Path to API keys JSON file (default: ./api-keys.json) */\n apiKeysPath: string;\n /** Agent shutdown timeout in seconds (default: 5) */\n shutdownTimeoutSec: number;\n}\n\n/**\n * Default configuration values.\n */\nexport const DEFAULT_CONFIG: LauncherConfig = {\n registryUrl: 'https://cdn.agentclientprotocol.com/registry/v1/latest/registry.json',\n apiKeysPath: './api-keys.json',\n shutdownTimeoutSec: 5,\n};\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * ACP Client Capabilities\n *\n * Provides utilities for accessing client capabilities like file system\n * and terminal operations via the ACP SDK.\n *\n * @module acp/client-capabilities\n */\n\nimport type { AgentSideConnection, ClientCapabilities, TerminalHandle } from '@agentclientprotocol/sdk';\n\n/**\n * Result of a file read operation.\n */\nexport interface FileReadResult {\n /** The file content */\n content: string;\n /** Whether the operation was successful */\n success: boolean;\n /** Error message if the operation failed */\n error?: string;\n}\n\n/**\n * Result of a file write operation.\n */\nexport interface FileWriteResult {\n /** Whether the operation was successful */\n success: boolean;\n /** Error message if the operation failed */\n error?: string;\n}\n\n/**\n * Result of a terminal command execution.\n */\nexport interface TerminalResult {\n /** The terminal output */\n output: string;\n /** The exit code (null if terminated by signal) */\n exitCode: number | null;\n /** The signal that terminated the process (null if exited normally) */\n signal: string | null;\n /** Whether the output was truncated */\n truncated: boolean;\n /** Whether the operation was successful */\n success: boolean;\n /** Error message if the operation failed */\n error?: string;\n}\n\n/**\n * Check if the client supports file system read operations.\n *\n * @param capabilities - The client capabilities\n * @returns Whether file read is supported\n */\nexport function canReadFile(capabilities: ClientCapabilities | null): boolean {\n return capabilities?.fs?.readTextFile === true;\n}\n\n/**\n * Check if the client supports file system write operations.\n *\n * @param capabilities - The client capabilities\n * @returns Whether file write is supported\n */\nexport function canWriteFile(capabilities: ClientCapabilities | null): boolean {\n return capabilities?.fs?.writeTextFile === true;\n}\n\n/**\n * Check if the client supports terminal operations.\n *\n * @param capabilities - The client capabilities\n * @returns Whether terminal is supported\n */\nexport function canUseTerminal(capabilities: ClientCapabilities | null): boolean {\n return capabilities?.terminal === true;\n}\n\n/**\n * Read a text file from the client's file system.\n *\n * @param connection - The ACP connection\n * @param sessionId - The session ID\n * @param path - Absolute path to the file\n * @param options - Optional read options (line, limit)\n * @returns The file read result\n */\nexport async function readFile(\n connection: AgentSideConnection,\n sessionId: string,\n path: string,\n options?: { line?: number; limit?: number },\n): Promise<FileReadResult> {\n try {\n const response = await connection.readTextFile({\n sessionId,\n path,\n line: options?.line,\n limit: options?.limit,\n });\n\n return {\n content: response.content,\n success: true,\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.error(`[ACP] Failed to read file \"${path}\":`, error);\n return {\n content: '',\n success: false,\n error: errorMessage,\n };\n }\n}\n\n/**\n * Write content to a text file in the client's file system.\n *\n * @param connection - The ACP connection\n * @param sessionId - The session ID\n * @param path - Absolute path to the file\n * @param content - The content to write\n * @returns The file write result\n */\nexport async function writeFile(\n connection: AgentSideConnection,\n sessionId: string,\n path: string,\n content: string,\n): Promise<FileWriteResult> {\n try {\n await connection.writeTextFile({\n sessionId,\n path,\n content,\n });\n\n return { success: true };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.error(`[ACP] Failed to write file \"${path}\":`, error);\n return {\n success: false,\n error: errorMessage,\n };\n }\n}\n\n/**\n * Execute a command in a terminal and wait for completion.\n * - Use SDK terminal methods (create, output, wait, kill, release)\n * - Track terminal IDs for cleanup\n * - Implement command timeouts\n *\n * @param connection - The ACP connection\n * @param sessionId - The session ID\n * @param command - The command to execute\n * @param options - Optional execution options\n * @returns The terminal execution result\n */\nexport async function executeCommand(\n connection: AgentSideConnection,\n sessionId: string,\n command: string,\n options?: {\n args?: string[];\n cwd?: string;\n env?: Array<{ name: string; value: string }>;\n timeout?: number;\n outputByteLimit?: number;\n },\n): Promise<TerminalResult> {\n let terminal: TerminalHandle | null = null;\n\n try {\n // Create terminal and execute command\n terminal = await connection.createTerminal({\n sessionId,\n command,\n args: options?.args,\n cwd: options?.cwd,\n env: options?.env,\n outputByteLimit: options?.outputByteLimit,\n });\n\n // Set up timeout if specified\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n let timedOut = false;\n\n if (options?.timeout && options.timeout > 0) {\n timeoutId = setTimeout(async () => {\n timedOut = true;\n if (terminal) {\n try {\n await terminal.kill();\n } catch {\n // Ignore kill errors\n }\n }\n }, options.timeout);\n }\n\n try {\n // Wait for command to complete\n const exitResult = await terminal.waitForExit();\n\n // Clear timeout if set\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n\n // Get final output\n const outputResult = await terminal.currentOutput();\n\n return {\n output: outputResult.output,\n exitCode: timedOut ? null : exitResult.exitCode ?? null,\n signal: timedOut ? 'SIGTERM' : exitResult.signal ?? null,\n truncated: outputResult.truncated,\n success: !timedOut && exitResult.exitCode === 0,\n error: timedOut ? 'Command timed out' : undefined,\n };\n } finally {\n // Clear timeout if still pending\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.error(`[ACP] Failed to execute command \"${command}\":`, error);\n return {\n output: '',\n exitCode: null,\n signal: null,\n truncated: false,\n success: false,\n error: errorMessage,\n };\n } finally {\n // Always release the terminal\n if (terminal) {\n try {\n await terminal.release();\n } catch {\n // Ignore release errors\n }\n }\n }\n}\n\n/**\n * Execute a command in a terminal without waiting for completion.\n * Returns a handle for managing the terminal.\n *\n * @param connection - The ACP connection\n * @param sessionId - The session ID\n * @param command - The command to execute\n * @param options - Optional execution options\n * @returns The terminal handle for managing the process\n */\nexport async function startCommand(\n connection: AgentSideConnection,\n sessionId: string,\n command: string,\n options?: {\n args?: string[];\n cwd?: string;\n env?: Array<{ name: string; value: string }>;\n outputByteLimit?: number;\n },\n): Promise<TerminalHandle> {\n return connection.createTerminal({\n sessionId,\n command,\n args: options?.args,\n cwd: options?.cwd,\n env: options?.env,\n outputByteLimit: options?.outputByteLimit,\n });\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Content Mapper\n *\n * Maps MCP tool call results to ACP ToolCallContent format.\n * Handles conversion between MCP content types (TextContent, ImageContent, EmbeddedResource)\n * and ACP ToolCallContent types (Content, Diff, Terminal).\n *\n * Also provides resource_link resolution for ACP prompts.\n *\n * @module acp/content-mapper\n */\n\nimport type {\n ContentBlock,\n EmbeddedResource as ACPEmbeddedResource,\n ImageContent as ACPImageContent,\n TextContent as ACPTextContent,\n ToolCallContent,\n} from '@agentclientprotocol/sdk';\nimport type {\n MCPContent,\n MCPEmbeddedResource,\n MCPImageContent,\n MCPResourceContents,\n MCPTextContent,\n} from '../mcp/types.js';\n\n/**\n * Maps a single MCP content item to an ACP ContentBlock.\n *\n * MCP content types:\n * - TextContent: { type: 'text', text: string }\n * - ImageContent: { type: 'image', data: string, mimeType: string }\n * - EmbeddedResource: { type: 'resource', resource: { uri, mimeType?, text?, blob? } }\n *\n * ACP ContentBlock types:\n * - TextContent: { type: 'text', text: string }\n * - ImageContent: { type: 'image', data: string, mimeType: string }\n * - EmbeddedResource: { type: 'resource', resource: TextResourceContents | BlobResourceContents }\n *\n * @param mcpContent - The MCP content item to convert\n * @returns The corresponding ACP ContentBlock\n */\nexport function mapMCPContentToACPContentBlock(mcpContent: MCPContent): ContentBlock {\n switch (mcpContent.type) {\n case 'text':\n return mapTextContent(mcpContent);\n case 'image':\n return mapImageContent(mcpContent);\n case 'resource':\n return mapEmbeddedResource(mcpContent);\n default:\n // Handle unknown content types by converting to text\n return {\n type: 'text',\n text: JSON.stringify(mcpContent),\n };\n }\n}\n\n/**\n * Maps MCP TextContent to ACP TextContent.\n *\n * @param mcpText - The MCP text content\n * @returns The ACP text content block\n */\nfunction mapTextContent(mcpText: MCPTextContent): ContentBlock {\n const result: ACPTextContent & { type: 'text' } = {\n type: 'text',\n text: mcpText.text,\n };\n return result;\n}\n\n/**\n * Maps MCP ImageContent to ACP ImageContent.\n *\n * @param mcpImage - The MCP image content\n * @returns The ACP image content block\n */\nfunction mapImageContent(mcpImage: MCPImageContent): ContentBlock {\n const result: ACPImageContent & { type: 'image' } = {\n type: 'image',\n data: mcpImage.data,\n mimeType: mcpImage.mimeType,\n };\n return result;\n}\n\n/**\n * Maps MCP EmbeddedResource to ACP EmbeddedResource.\n *\n * MCP EmbeddedResource has:\n * - resource.uri: string\n * - resource.mimeType?: string\n * - resource.text?: string (for text resources)\n * - resource.blob?: string (for binary resources)\n *\n * ACP EmbeddedResource expects:\n * - resource: TextResourceContents | BlobResourceContents\n * - TextResourceContents: { uri, mimeType?, text }\n * - BlobResourceContents: { uri, mimeType?, blob }\n *\n * @param mcpResource - The MCP embedded resource\n * @returns The ACP embedded resource content block\n */\nfunction mapEmbeddedResource(mcpResource: MCPEmbeddedResource): ContentBlock {\n const { resource } = mcpResource;\n\n // Determine if this is a text or blob resource\n if (resource.text !== undefined) {\n // Text resource\n const result: ACPEmbeddedResource & { type: 'resource' } = {\n type: 'resource',\n resource: {\n uri: resource.uri,\n mimeType: resource.mimeType,\n text: resource.text,\n },\n };\n return result;\n } else if (resource.blob !== undefined) {\n // Blob resource\n const result: ACPEmbeddedResource & { type: 'resource' } = {\n type: 'resource',\n resource: {\n uri: resource.uri,\n mimeType: resource.mimeType,\n blob: resource.blob,\n },\n };\n return result;\n } else {\n // Resource without content - create empty text resource\n const result: ACPEmbeddedResource & { type: 'resource' } = {\n type: 'resource',\n resource: {\n uri: resource.uri,\n mimeType: resource.mimeType,\n text: '',\n },\n };\n return result;\n }\n}\n\n/**\n * Maps an array of MCP content items to ACP ToolCallContent array.\n *\n * Each MCP content item is wrapped in an ACP Content structure with type: 'content'.\n *\n * @param mcpContents - Array of MCP content items\n * @returns Array of ACP ToolCallContent items\n */\nexport function mapMCPResultToACPToolCallContent(mcpContents: MCPContent[]): ToolCallContent[] {\n return mcpContents.map((mcpContent) => {\n const contentBlock = mapMCPContentToACPContentBlock(mcpContent);\n // Wrap in ACP Content structure with type: 'content'\n return {\n type: 'content' as const,\n content: contentBlock,\n };\n });\n}\n\n/**\n * Creates an error ToolCallContent from an error message.\n *\n * Used when MCP tool execution fails (isError: true) to create\n * appropriate error content for the ACP client.\n *\n * @param errorMessage - The error message to display\n * @returns A ToolCallContent array with the error message\n */\nexport function createErrorToolCallContent(errorMessage: string): ToolCallContent[] {\n return [\n {\n type: 'content' as const,\n content: {\n type: 'text',\n text: `Error: ${errorMessage}`,\n },\n },\n ];\n}\n\n/**\n * Maps MCP tool call result to ACP ToolCallContent array.\n *\n * Handles both successful results and error results.\n * For errors, creates appropriate error content.\n *\n * @param content - Array of MCP content items from tool result\n * @param isError - Whether the tool execution resulted in an error\n * @returns Array of ACP ToolCallContent items\n */\nexport function mapToolResultToACPContent(\n content: MCPContent[],\n isError: boolean = false,\n): ToolCallContent[] {\n if (isError && content.length > 0) {\n // For error results, extract text content as error message\n const errorText = content\n .filter((c): c is MCPTextContent => c.type === 'text')\n .map((c) => c.text)\n .join('\\n');\n\n if (errorText) {\n return createErrorToolCallContent(errorText);\n }\n }\n\n // Map all content items to ACP format\n return mapMCPResultToACPToolCallContent(content);\n}\n\n\n/**\n * Represents a resource_link from an ACP ContentBlock.\n */\nexport interface ResourceLink {\n type: 'resource_link';\n uri: string;\n name: string;\n title?: string;\n description?: string;\n mimeType?: string;\n size?: number;\n}\n\n/**\n * Type guard to check if a ContentBlock is a resource_link.\n *\n * @param block - The content block to check\n * @returns True if the block is a resource_link\n */\nexport function isResourceLink(block: ContentBlock): block is ResourceLink {\n return block.type === 'resource_link' && 'uri' in block;\n}\n\n/**\n * Maps MCP resource contents to an ACP EmbeddedResource ContentBlock.\n *\n * Converts the result of readResource() to an ACP-compatible format.\n *\n * @param contents - The MCP resource contents from readResource()\n * @returns The ACP embedded resource content block\n */\nexport function mapMCPResourceContentsToACPContentBlock(contents: MCPResourceContents): ContentBlock {\n if ('text' in contents) {\n // Text resource\n const result: ACPEmbeddedResource & { type: 'resource' } = {\n type: 'resource',\n resource: {\n uri: contents.uri,\n mimeType: contents.mimeType,\n text: contents.text,\n },\n };\n return result;\n }\n\n // Blob resource (the only other option in the union type)\n const result: ACPEmbeddedResource & { type: 'resource' } = {\n type: 'resource',\n resource: {\n uri: contents.uri,\n mimeType: contents.mimeType,\n blob: contents.blob,\n },\n };\n return result;\n}\n\n/**\n * Extracts the URI from a resource_link ContentBlock.\n *\n * @param block - The resource_link content block\n * @returns The URI string or null if not a valid resource_link\n */\nexport function extractResourceLinkUri(block: ContentBlock): string | null {\n if (isResourceLink(block)) {\n return block.uri;\n }\n return null;\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Tool Call Utilities\n *\n * Provides utilities for initiating and managing tool calls in ACP.\n * Handles tool call lifecycle including initiation, permission requests,\n * execution, and result reporting.\n *\n * @module acp/tools\n */\n\nimport type { AgentSideConnection, PermissionOption, ToolCallContent, ToolCallUpdate } from '@agentclientprotocol/sdk';\nimport { mapToolResultToACPContent } from './content-mapper.js';\nimport type { MCPManager } from '../mcp/manager.js';\nimport type { MCPContent } from '../mcp/types.js';\n\n/**\n * Permission request result.\n */\nexport interface PermissionResult {\n /** Whether permission was granted */\n granted: boolean;\n /** The option ID that was selected (if granted) */\n optionId?: string;\n /** Whether the request was cancelled */\n cancelled?: boolean;\n}\n\n/**\n * Tool kind categories for ACP tool calls.\n * Helps clients choose appropriate icons and UI treatment.\n */\nexport type ToolKind =\n 'read'\n | 'edit'\n | 'delete'\n | 'move'\n | 'search'\n | 'execute'\n | 'think'\n | 'fetch'\n | 'switch_mode'\n | 'other';\n\n/**\n * Tool call status values.\n */\nexport type ToolCallStatus = 'pending' | 'in_progress' | 'completed' | 'failed';\n\n/**\n * Counter for generating unique tool call IDs within a session.\n */\nlet toolCallCounter = 0;\n\n/**\n * Generate a unique tool call ID.\n *\n * @returns A unique tool call ID string\n */\nexport function generateToolCallId(): string {\n toolCallCounter++;\n return `tool-${Date.now()}-${toolCallCounter}`;\n}\n\n/**\n * Determine the tool kind based on tool name and description.\n *\n * @param toolName - The name of the tool\n * @param description - Optional tool description\n * @returns The appropriate ToolKind\n */\nexport function determineToolKind(toolName: string, description?: string): ToolKind {\n const name = toolName.toLowerCase();\n const desc = (description || '').toLowerCase();\n\n // Check description first for more specific hints\n if (desc.includes('external') || desc.includes('api') || desc.includes('http')) {\n return 'fetch';\n }\n\n // Check for read operations\n if (name.includes('read') || name.includes('get') || name.includes('list') || name.includes('fetch')) {\n return 'read';\n }\n\n // Check for edit/write operations\n if (name.includes('write') || name.includes('edit') || name.includes('update') || name.includes('modify')) {\n return 'edit';\n }\n\n // Check for delete operations\n if (name.includes('delete') || name.includes('remove')) {\n return 'delete';\n }\n\n // Check for move/rename operations\n if (name.includes('move') || name.includes('rename')) {\n return 'move';\n }\n\n // Check for search operations\n if (name.includes('search') || name.includes('find') || name.includes('query')) {\n return 'search';\n }\n\n // Check for execute operations\n if (name.includes('exec') || name.includes('run') || name.includes('shell') || name.includes('command')) {\n return 'execute';\n }\n\n // Check for fetch operations (external data)\n if (name.includes('http') || name.includes('api') || name.includes('request')) {\n return 'fetch';\n }\n\n // Default to 'other' for unknown tools\n return 'other';\n}\n\n/**\n * Send a tool_call session update to initiate a tool call.\n *\n * @param connection - The ACP connection\n * @param sessionId - The session ID\n * @param toolCallId - The unique tool call ID\n * @param title - Human-readable title for the tool call\n * @param kind - The tool kind category\n * @param status - Initial status (usually 'pending')\n */\nexport async function sendToolCallInitiation(\n connection: AgentSideConnection,\n sessionId: string,\n toolCallId: string,\n title: string,\n kind: ToolKind = 'other',\n status: ToolCallStatus = 'pending',\n): Promise<void> {\n await connection.sessionUpdate({\n sessionId,\n update: {\n sessionUpdate: 'tool_call',\n toolCallId,\n title,\n kind,\n status,\n },\n });\n}\n\n/**\n * Send a tool_call_update to report progress or results.\n *\n * @param connection - The ACP connection\n * @param sessionId - The session ID\n * @param toolCallId - The tool call ID to update\n * @param status - The new status\n * @param content - Optional content to include\n * @param title - Optional updated title\n */\nexport async function sendToolCallUpdate(\n connection: AgentSideConnection,\n sessionId: string,\n toolCallId: string,\n status: ToolCallStatus,\n content?: ToolCallContent[],\n title?: string,\n): Promise<void> {\n await connection.sessionUpdate({\n sessionId,\n update: {\n sessionUpdate: 'tool_call_update',\n toolCallId,\n status,\n content,\n title,\n },\n });\n}\n\n/**\n * Request permission from the user before executing a tool.\n *\n * @param connection - The ACP connection\n * @param sessionId - The session ID\n * @param toolCallId - The tool call ID\n * @param title - Human-readable title for the tool call\n * @param kind - The tool kind category\n * @param options - Permission options to present to the user\n * @returns The permission result with granted/cancelled status\n */\nexport async function requestToolPermission(\n connection: AgentSideConnection,\n sessionId: string,\n toolCallId: string,\n title: string,\n kind: ToolKind = 'other',\n options?: PermissionOption[],\n): Promise<PermissionResult> {\n // Build default permission options if not provided\n const permissionOptions: PermissionOption[] = options ?? [\n { optionId: 'allow_once', name: 'Allow once', kind: 'allow_once' },\n { optionId: 'allow_always', name: 'Allow always', kind: 'allow_always' },\n { optionId: 'reject_once', name: 'Reject', kind: 'reject_once' },\n ];\n\n // Build the tool call update for the permission request\n const toolCall: ToolCallUpdate = {\n toolCallId,\n title,\n kind,\n status: 'pending',\n };\n\n try {\n // Use SDK's requestPermission method\n const response = await connection.requestPermission({\n sessionId,\n toolCall,\n options: permissionOptions,\n });\n\n // Handle the response outcome\n if (response.outcome.outcome === 'cancelled') {\n return { granted: false, cancelled: true };\n }\n\n if (response.outcome.outcome === 'selected') {\n const selectedOption = response.outcome.optionId;\n // Check if the selected option is an allow option\n const isAllowed = selectedOption.startsWith('allow');\n return {\n granted: isAllowed,\n optionId: selectedOption,\n cancelled: false,\n };\n }\n\n // Unknown outcome - treat as rejected\n return { granted: false, cancelled: false };\n } catch (error) {\n // If permission request fails, treat as cancelled\n console.error('[ACP] Permission request failed:', error);\n return { granted: false, cancelled: true };\n }\n}\n\n/**\n * Execute a tool call with full lifecycle management.\n *\n * This function handles the complete tool call flow:\n * 1. Sends tool_call initiation with pending status\n * 2. Updates to in_progress when execution starts\n * 3. Executes the tool via MCP\n * 4. Updates with completed/failed status and results\n *\n * @param connection - The ACP connection\n * @param sessionId - The session ID\n * @param mcpManager - The MCP manager for tool execution\n * @param toolName - The name of the tool to execute\n * @param args - Arguments to pass to the tool\n * @param description - Optional tool description for kind detection\n * @returns The tool call result content\n */\nexport async function executeToolCall(\n connection: AgentSideConnection,\n sessionId: string,\n mcpManager: MCPManager,\n toolName: string,\n args: Record<string, unknown>,\n description?: string,\n): Promise<ToolCallContent[]> {\n // Generate unique tool call ID\n const toolCallId = generateToolCallId();\n\n // Determine tool kind\n const kind = determineToolKind(toolName, description);\n\n // Create human-readable title\n const title = `Executing: ${toolName}`;\n\n try {\n // Send tool_call initiation with pending status\n await sendToolCallInitiation(connection, sessionId, toolCallId, title, kind, 'pending');\n\n // Update to in_progress\n await sendToolCallUpdate(connection, sessionId, toolCallId, 'in_progress');\n\n // Execute the tool via MCP\n const result = await mcpManager.callTool(toolName, args);\n\n // Map MCP result to ACP content\n const content = mapToolResultToACPContent(result.content as MCPContent[], result.isError);\n\n // Send final update with completed/failed status\n const finalStatus: ToolCallStatus = result.isError ? 'failed' : 'completed';\n await sendToolCallUpdate(connection, sessionId, toolCallId, finalStatus, content);\n\n return content;\n } catch (error) {\n // Send failed status on error\n const errorMessage = error instanceof Error ? error.message : String(error);\n const errorContent: ToolCallContent[] = [{\n type: 'content',\n content: {\n type: 'text',\n text: `Error: ${errorMessage}`,\n },\n }];\n\n await sendToolCallUpdate(connection, sessionId, toolCallId, 'failed', errorContent);\n\n return errorContent;\n }\n}\n\n/**\n * Execute a tool call with permission request.\n *\n * This function handles the complete tool call flow with permission:\n * 1. Sends tool_call initiation with pending status\n * 2. Requests permission from the user\n * 3. If granted, executes the tool via MCP\n * 4. Updates with completed/failed status and results\n *\n * @param connection - The ACP connection\n * @param sessionId - The session ID\n * @param mcpManager - The MCP manager for tool execution\n * @param toolName - The name of the tool to execute\n * @param args - Arguments to pass to the tool\n * @param description - Optional tool description for kind detection\n * @param requirePermission - Whether to request permission before execution\n * @returns The tool call result content and permission result\n */\nexport async function executeToolCallWithPermission(\n connection: AgentSideConnection,\n sessionId: string,\n mcpManager: MCPManager,\n toolName: string,\n args: Record<string, unknown>,\n description?: string,\n requirePermission: boolean = true,\n): Promise<{ content: ToolCallContent[]; permissionResult?: PermissionResult }> {\n // Generate unique tool call ID\n const toolCallId = generateToolCallId();\n\n // Determine tool kind\n const kind = determineToolKind(toolName, description);\n\n // Create human-readable title\n const title = `Executing: ${toolName}`;\n\n try {\n // Send tool_call initiation with pending status\n await sendToolCallInitiation(connection, sessionId, toolCallId, title, kind, 'pending');\n\n // Request permission if required\n if (requirePermission) {\n const permissionResult = await requestToolPermission(\n connection,\n sessionId,\n toolCallId,\n title,\n kind,\n );\n\n // If permission was cancelled or rejected, return early\n if (!permissionResult.granted) {\n const status: ToolCallStatus = permissionResult.cancelled ? 'failed' : 'failed';\n const message = permissionResult.cancelled ? 'Permission request cancelled' : 'Permission denied';\n const errorContent: ToolCallContent[] = [{\n type: 'content',\n content: {\n type: 'text',\n text: message,\n },\n }];\n\n await sendToolCallUpdate(connection, sessionId, toolCallId, status, errorContent);\n\n return { content: errorContent, permissionResult };\n }\n }\n\n // Update to in_progress\n await sendToolCallUpdate(connection, sessionId, toolCallId, 'in_progress');\n\n // Execute the tool via MCP\n const result = await mcpManager.callTool(toolName, args);\n\n // Map MCP result to ACP content\n const content = mapToolResultToACPContent(result.content as MCPContent[], result.isError);\n\n // Send final update with completed/failed status\n const finalStatus: ToolCallStatus = result.isError ? 'failed' : 'completed';\n await sendToolCallUpdate(connection, sessionId, toolCallId, finalStatus, content);\n\n return { content };\n } catch (error) {\n // Send failed status on error\n const errorMessage = error instanceof Error ? error.message : String(error);\n const errorContent: ToolCallContent[] = [{\n type: 'content',\n content: {\n type: 'text',\n text: `Error: ${errorMessage}`,\n },\n }];\n\n await sendToolCallUpdate(connection, sessionId, toolCallId, 'failed', errorContent);\n\n return { content: errorContent };\n }\n}\n"],
5
- "mappings": "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",
3
+ "sources": ["../../../../workers-registry/acp-worker/src/index.ts", "../../../../workers-registry/acp-worker/src/agent.ts", "../../../../workers-registry/acp-worker/src/mcp/manager.ts", "../../../../workers-registry/acp-worker/src/session/session.ts", "../../../../workers-registry/acp-worker/src/session/manager.ts", "../../../../workers-registry/acp-worker/src/stdio/session-id-router.ts", "../../../../workers-registry/acp-worker/src/registry-launcher/registry/resolver.ts", "../../../../workers-registry/acp-worker/src/registry-launcher/runtime/agent-runtime.ts", "../../../../workers-registry/acp-worker/src/registry-launcher/runtime/manager.ts", "../../../../workers-registry/acp-worker/src/registry-launcher/stream/ndjson-handler.ts", "../../../../workers-registry/acp-worker/src/registry-launcher/registry/index.ts", "../../../../workers-registry/acp-worker/src/registry-launcher/config/api-keys.ts", "../../../../workers-registry/acp-worker/src/registry-launcher/router/message-router.ts", "../../../../workers-registry/acp-worker/src/registry-launcher/config/config.ts", "../../../../workers-registry/acp-worker/src/registry-launcher/config/types.ts", "../../../../workers-registry/acp-worker/src/acp/client-capabilities.ts", "../../../../workers-registry/acp-worker/src/acp/content-mapper.ts", "../../../../workers-registry/acp-worker/src/acp/tools.ts"],
4
+ "sourcesContent": ["/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * ACP/MCP Protocol Worker for stdio Bus kernel\n *\n * This worker implements the Agent Client Protocol (ACP) using official SDKs\n * and connects to MCP servers for tool execution.\n *\n * It runs as a child process of stdio Bus kernel, communicating via stdin/stdout NDJSON.\n *\n * @module index\n */\n\nimport { Readable, Writable, Transform } from 'node:stream';\nimport { AgentSideConnection, ndJsonStream } from '@agentclientprotocol/sdk';\nimport { ACPAgent } from './agent.js';\nimport { SessionIdRouter } from './stdio/session-id-router.js';\n\n// Log startup message to stderr (not stdout - stdout is for protocol messages)\nconsole.error('[worker] Starting ACP/MCP Protocol Worker...');\n\nconst sessionIdRouter = new SessionIdRouter();\n\n/**\n * Transform stream to intercept stdin and save sessionId from requests.\n * Removes sessionId before passing to ACP SDK (SDK doesn't know about it).\n */\nconst stdinTransform = new Transform({\n objectMode: false,\n transform(chunk: Buffer, _encoding, callback) {\n const lines = chunk.toString().split('\\n');\n const processedLines: string[] = [];\n\n for (const line of lines) {\n processedLines.push(sessionIdRouter.processIncomingLine(line));\n }\n\n callback(null, Buffer.from(processedLines.join('\\n')));\n }\n});\n\n/**\n * Transform stream to intercept stdout and restore sessionId in responses.\n * Adds sessionId back for stdio_bus routing.\n */\nconst stdoutTransform = new Transform({\n objectMode: false,\n transform(chunk: Buffer, _encoding, callback) {\n const lines = chunk.toString().split('\\n');\n const processedLines: string[] = [];\n\n for (const line of lines) {\n processedLines.push(sessionIdRouter.processOutgoingLine(line));\n }\n\n callback(null, Buffer.from(processedLines.join('\\n')));\n }\n});\n\n// Pipe stdin through transform before SDK\nprocess.stdin.pipe(stdinTransform);\n\n/**\n * Convert transformed stdin to web ReadableStream for SDK.\n */\nconst inputStream = Readable.toWeb(stdinTransform) as ReadableStream<Uint8Array>;\n\n/**\n * Convert stdout transform to web WritableStream for SDK.\n */\nconst outputStream = Writable.toWeb(stdoutTransform) as WritableStream<Uint8Array>;\n\n// Pipe transform output to actual stdout\nstdoutTransform.pipe(process.stdout);\n\n/**\n * Create the NDJSON stream for ACP communication.\n * The SDK handles all NDJSON framing and JSON-RPC protocol details automatically.\n */\nconst stream = ndJsonStream(outputStream, inputStream);\n\n/**\n * Create the AgentSideConnection with stdio transport.\n *\n * The SDK pattern uses a factory function that receives the connection\n * and returns an Agent instance. The SDK handles all NDJSON framing\n * and JSON-RPC protocol details automatically.\n */\nconst connection = new AgentSideConnection(\n (conn) => new ACPAgent(conn),\n stream,\n);\n\n// Log that connection is established\nconsole.error('[worker] AgentSideConnection established, ready for messages');\n\n/**\n * Handle graceful shutdown on SIGTERM.\n *\n * When stdio Bus kernel sends SIGTERM, we should wait for the connection to close\n * and allow pending operations to complete.\n */\nprocess.on('SIGTERM', async () => {\n console.error('[worker] Received SIGTERM, shutting down...');\n // Wait for the connection to close gracefully\n await connection.closed;\n process.exit(0);\n});\n\n/**\n * Handle SIGINT for development convenience.\n */\nprocess.on('SIGINT', async () => {\n console.error('[worker] Received SIGINT, shutting down...');\n await connection.closed;\n process.exit(0);\n});\n\n/**\n * Handle uncaught exceptions to prevent silent failures.\n */\nprocess.on('uncaughtException', (error) => {\n console.error('[worker] Uncaught exception:', error);\n process.exit(1);\n});\n\n/**\n * Handle unhandled promise rejections.\n */\nprocess.on('unhandledRejection', (reason, promise) => {\n console.error('[worker] Unhandled rejection at:', promise, 'reason:', reason);\n});\n\n/**\n * Wait for the connection to close (either normally or due to error).\n * This keeps the process running until the connection ends.\n */\nconnection.closed.then(() => {\n console.error('[worker] Connection closed');\n process.exit(0);\n}).catch((error) => {\n console.error('[worker] Connection error:', error);\n process.exit(1);\n});\n\n// Export types for npm package consumers\nexport type {\n Platform,\n BinaryTarget,\n BinaryDistribution,\n NpxDistribution,\n UvxDistribution,\n Distribution,\n RegistryAgent,\n Registry,\n SpawnCommand,\n} from './registry-launcher/registry/types.js';\n\nexport type {\n RegistryIndex,\n IRegistryIndex,\n} from './registry-launcher/registry/index.js';\n\n// Export runtime classes and functions\nexport { ACPAgent } from './agent.js';\n\n// Registry Launcher exports\nexport {\n PlatformNotSupportedError,\n NoDistributionError,\n getCurrentPlatform,\n resolve,\n resolveBinary,\n resolveNpx,\n resolveUvx,\n} from './registry-launcher/registry/resolver.js';\n\n// Runtime exports\nexport { AgentRuntimeManager } from './registry-launcher/runtime/manager.js';\nexport { AgentRuntimeImpl } from './registry-launcher/runtime/agent-runtime.js';\nexport type { RuntimeState, AgentRuntime } from './registry-launcher/runtime/types.js';\n\n// Stream exports\nexport {\n NDJSONHandler, INDJSONHandler, ErrorCallback, MessageCallback,\n} from './registry-launcher/stream/ndjson-handler.js';\n\n// Router exports\nexport {\n MessageRouter,\n createErrorResponse,\n ErrorResponse,\n RoutingErrorCodes,\n transformMessage,\n extractAgentId,\n extractId,\n WriteCallback,\n} from './registry-launcher/router/message-router.js';\n\n// Config exports\nexport { loadConfig } from './registry-launcher/config/config.js';\nexport type {\n DEFAULT_CONFIG,\n LauncherConfig,\n} from './registry-launcher/config/types.js';\n\n// MCP exports\nexport { MCPManager, MCPConnection, MCPFactories } from './mcp/manager.js';\nexport type {\n MCPServerConfig,\n MCPContent,\n MCPImageContent,\n MCPBlobResourceContents,\n MCPEmbeddedResource,\n MCPResource,\n MCPTextContent,\n MCPResourceContents,\n MCPTextResourceContents,\n MCPTool,\n MCPToolCallResult,\n MCPResourceReadResult,\n} from './mcp/types.js';\n\n// ACP utilities exports\nexport {\n canReadFile,\n canWriteFile,\n FileReadResult,\n FileWriteResult,\n readFile,\n canUseTerminal,\n TerminalResult,\n writeFile,\n executeCommand,\n startCommand,\n} from './acp/client-capabilities.js';\nexport {\n createErrorToolCallContent,\n mapMCPContentToACPContentBlock,\n mapMCPResourceContentsToACPContentBlock,\n isResourceLink,\n extractResourceLinkUri,\n mapMCPResultToACPToolCallContent,\n ResourceLink,\n mapToolResultToACPContent,\n} from './acp/content-mapper.js';\nexport {\n determineToolKind,\n executeToolCall,\n generateToolCallId,\n executeToolCallWithPermission,\n requestToolPermission,\n PermissionResult,\n ToolCallStatus,\n ToolKind,\n sendToolCallInitiation,\n sendToolCallUpdate,\n} from './acp/tools.js';\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Agent Implementation\n *\n * This module implements the ACP Agent interface using @agentclientprotocol/sdk.\n * The Agent handles all ACP protocol methods including initialization,\n * session management, and prompt processing.\n *\n * @module agent\n */\n\nimport type {\n Agent,\n AgentSideConnection,\n AuthenticateRequest,\n AuthenticateResponse,\n CancelNotification,\n ClientCapabilities,\n InitializeRequest,\n InitializeResponse,\n LoadSessionRequest,\n LoadSessionResponse,\n NewSessionRequest,\n NewSessionResponse,\n PromptRequest,\n PromptResponse,\n} from '@agentclientprotocol/sdk';\nimport { PROTOCOL_VERSION } from '@agentclientprotocol/sdk';\nimport { SessionManager } from './session/manager.js';\n\n/**\n * ACP Agent implementation for stdio Bus kernel worker.\n *\n * This class implements the Agent interface from the ACP SDK,\n * handling all protocol methods and coordinating with MCP servers\n * for tool execution.\n */\nexport class ACPAgent implements Agent {\n /**\n * Reference to the AgentSideConnection for sending notifications.\n * Used by prompt processing to send session updates.\n */\n private readonly _connection: AgentSideConnection;\n\n /**\n * Session manager for handling session lifecycle.\n * Manages session creation, lookup, cancellation, and cleanup.\n */\n private readonly _sessionManager: SessionManager;\n\n /**\n * Client capabilities received during initialization.\n * Used to determine what features the client supports.\n */\n private _clientCapabilities: ClientCapabilities | null = null;\n\n /**\n * Creates a new ACP Agent instance.\n *\n * @param connection - The AgentSideConnection for communicating with the client\n */\n constructor(connection: AgentSideConnection) {\n this._connection = connection;\n this._sessionManager = new SessionManager();\n }\n\n /**\n * Get the connection for sending notifications.\n * Used by prompt processing to send session updates.\n */\n get connection(): AgentSideConnection {\n return this._connection;\n }\n\n /**\n * Get the session manager for session operations.\n */\n get sessionManager(): SessionManager {\n return this._sessionManager;\n }\n\n /**\n * Get the client capabilities received during initialization.\n * Returns null if initialize() has not been called yet.\n */\n get clientCapabilities(): ClientCapabilities | null {\n return this._clientCapabilities;\n }\n\n /**\n * Handle ACP initialize request.\n * Returns agent capabilities and info.\n *\n * Stores client capabilities for later use and returns InitializeResponse\n * with agent info and capabilities including promptCapabilities.embeddedContext: true.\n *\n * @param params - The initialization request parameters\n * @returns Promise resolving to InitializeResponse with agent capabilities\n */\n async initialize(params: InitializeRequest): Promise<InitializeResponse> {\n // Store client capabilities for later use\n this._clientCapabilities = params.clientCapabilities ?? null;\n\n // Return InitializeResponse with agent info and capabilities\n return {\n protocolVersion: PROTOCOL_VERSION,\n agentInfo: {\n name: 'stdio-bus-worker',\n version: '1.0.0',\n },\n agentCapabilities: {\n promptCapabilities: {\n embeddedContext: true,\n },\n },\n authMethods: [],\n };\n }\n\n /**\n * Handle ACP session/new request.\n * Creates a new session with MCP server connections.\n *\n * Generates a unique sessionId using crypto.randomUUID(), stores session state,\n * initializes MCP connections from the request params, and returns NewSessionResponse.\n *\n * @param params - The new session request parameters containing cwd and optional mcpServers\n * @returns Promise resolving to NewSessionResponse with session ID\n */\n async newSession(params: NewSessionRequest): Promise<NewSessionResponse> {\n // Convert ACP McpServer[] to MCPServerConfig[] for the session manager\n const mcpServers = params.mcpServers?.map((server) => {\n // Handle stdio type servers (most common for local MCP servers)\n if ('command' in server) {\n return {\n id: server.name,\n command: server.command,\n args: server.args,\n env: server.env?.reduce(\n (acc, envVar) => {\n acc[envVar.name] = envVar.value;\n return acc;\n },\n {} as Record<string, string>,\n ),\n };\n }\n // For HTTP/SSE servers, we'll need to handle them differently\n // For now, skip non-stdio servers\n return null;\n }).filter((s): s is NonNullable<typeof s> => s !== null);\n\n // Create a new session with the session manager\n // This generates a UUID for sessionId and stores session state\n const session = await this._sessionManager.createSession(params.cwd, mcpServers);\n\n // Return NewSessionResponse with the sessionId\n return {\n sessionId: session.id,\n };\n }\n\n /**\n * Handle ACP session/load request.\n * Loads an existing session (optional capability).\n *\n * @param params - The load session request parameters\n * @returns Promise resolving to LoadSessionResponse\n */\n async loadSession(_params: LoadSessionRequest): Promise<LoadSessionResponse> {\n // Session loading is an optional capability\n // Return empty response to indicate session not found\n return {} as LoadSessionResponse;\n }\n\n /**\n * Handle ACP authenticate request.\n * Processes authentication (if required).\n *\n * @param params - The authentication request parameters\n * @returns Promise resolving to AuthenticateResponse or void\n */\n async authenticate(_params: AuthenticateRequest): Promise<AuthenticateResponse | void> {\n // Authentication is optional - no auth methods are declared\n // This is a no-op implementation\n }\n\n /**\n * Handle ACP session/prompt request.\n * Processes user prompts and streams responses.\n *\n * Currently implements echo mode for testing - echoes user prompt as agent response.\n *\n * @param params - The prompt request parameters\n * @returns Promise resolving to PromptResponse with stop reason\n */\n async prompt(params: PromptRequest): Promise<PromptResponse> {\n // Validate session exists\n const session = this._sessionManager.getSession(params.sessionId);\n if (!session) {\n throw new Error(`Session not found: ${params.sessionId}`);\n }\n\n // Check for cancellation before processing\n if (session.isCancelled()) {\n return { stopReason: 'cancelled' };\n }\n\n // Process each content block in the prompt\n // Echo mode: echo user prompt as agent response\n for (const block of params.prompt) {\n // Check for cancellation during processing\n if (session.isCancelled()) {\n return { stopReason: 'cancelled' };\n }\n\n // Handle different content block types\n if (block.type === 'text') {\n // Echo text content as agent_message_chunk\n await this._connection.sessionUpdate({\n sessionId: params.sessionId,\n update: {\n sessionUpdate: 'agent_message_chunk',\n content: {\n type: 'text',\n text: block.text,\n },\n },\n });\n } else if (block.type === 'resource_link') {\n // For resource_link, try to resolve and echo the content\n const resourceLink = block as { type: 'resource_link'; uri: string; name: string };\n try {\n // Try to read the resource from MCP servers\n const result = await session.mcpManager.readResource(resourceLink.uri);\n if (result.contents.length > 0) {\n const content = result.contents[0];\n if ('text' in content) {\n await this._connection.sessionUpdate({\n sessionId: params.sessionId,\n update: {\n sessionUpdate: 'agent_message_chunk',\n content: {\n type: 'text',\n text: `[Resource: ${resourceLink.name}]\\n${content.text}`,\n },\n },\n });\n } else if ('blob' in content) {\n await this._connection.sessionUpdate({\n sessionId: params.sessionId,\n update: {\n sessionUpdate: 'agent_message_chunk',\n content: {\n type: 'text',\n text: `[Resource: ${resourceLink.name}] (binary data, ${content.blob.length} bytes)`,\n },\n },\n });\n }\n }\n } catch {\n // If resource resolution fails, just echo the link info\n await this._connection.sessionUpdate({\n sessionId: params.sessionId,\n update: {\n sessionUpdate: 'agent_message_chunk',\n content: {\n type: 'text',\n text: `[Resource link: ${resourceLink.name} (${resourceLink.uri})]`,\n },\n },\n });\n }\n } else if (block.type === 'resource') {\n // For embedded resource, echo the content\n const resource = block as { type: 'resource'; resource: { uri: string; text?: string; blob?: string } };\n if (resource.resource.text !== undefined) {\n await this._connection.sessionUpdate({\n sessionId: params.sessionId,\n update: {\n sessionUpdate: 'agent_message_chunk',\n content: {\n type: 'text',\n text: `[Embedded resource: ${resource.resource.uri}]\\n${resource.resource.text}`,\n },\n },\n });\n } else if (resource.resource.blob !== undefined) {\n await this._connection.sessionUpdate({\n sessionId: params.sessionId,\n update: {\n sessionUpdate: 'agent_message_chunk',\n content: {\n type: 'text',\n text: `[Embedded resource: ${resource.resource.uri}] (binary data)`,\n },\n },\n });\n }\n } else if (block.type === 'image') {\n // For images, echo a description\n const image = block as { type: 'image'; mimeType: string };\n await this._connection.sessionUpdate({\n sessionId: params.sessionId,\n update: {\n sessionUpdate: 'agent_message_chunk',\n content: {\n type: 'text',\n text: `[Image: ${image.mimeType}]`,\n },\n },\n });\n }\n }\n\n // Final cancellation check\n if (session.isCancelled()) {\n return { stopReason: 'cancelled' };\n }\n\n // Return end_turn as stopReason for successful completion\n return { stopReason: 'end_turn' };\n }\n\n /**\n * Handle ACP session/cancel notification.\n * Cancels ongoing operations for a session.\n *\n * Looks up the session by sessionId and calls session.cancel() to set\n * the cancellation flag and abort pending MCP operations.\n *\n * @param params - The cancel notification parameters containing sessionId\n */\n async cancel(params: CancelNotification): Promise<void> {\n // Look up the session by sessionId and cancel it\n // The session's cancel() method sets the cancellation flag\n // and aborts pending MCP operations\n this._sessionManager.cancelSession(params.sessionId);\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * MCP Manager\n *\n * Manages multiple MCP server connections for a session.\n * Handles connection lifecycle, tool discovery, and tool invocation.\n *\n * @module mcp/manager\n */\n\nimport { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';\nimport type { ServerCapabilities } from '@modelcontextprotocol/sdk/types.js';\nimport type {\n MCPBlobResourceContents,\n MCPContent,\n MCPResource,\n MCPResourceContents,\n MCPResourceReadResult,\n MCPServerConfig,\n MCPTextResourceContents,\n MCPTool,\n MCPToolCallResult,\n} from './types.js';\n\n/**\n * Represents an active MCP connection with its client and transport.\n */\nexport interface MCPConnection {\n /** The MCP SDK client instance */\n client: Client;\n /** The stdio transport for the connection */\n transport: StdioClientTransport;\n /** Server configuration */\n config: MCPServerConfig;\n /** Whether the connection is active */\n connected: boolean;\n /** Server capabilities from initialization handshake */\n capabilities?: ServerCapabilities;\n /** Error message if the server crashed */\n crashError?: string;\n}\n\n/**\n * Factory functions for creating MCP SDK instances.\n * Used for dependency injection in tests.\n */\nexport interface MCPFactories {\n /** Factory for creating Client instances */\n createClient: (options: { name: string; version: string }) => Client;\n /** Factory for creating StdioClientTransport instances */\n createTransport: (options: {\n command: string;\n args?: string[];\n env?: Record<string, string>\n }) => StdioClientTransport;\n}\n\n/** Default factories using the real MCP SDK */\nconst defaultFactories: MCPFactories = {\n createClient: (options) => new Client(options),\n createTransport: (options) => new StdioClientTransport(options),\n};\n\n/**\n * Manages MCP server connections for a session.\n */\nexport class MCPManager {\n /** Active connections keyed by server ID */\n private connections: Map<string, MCPConnection> = new Map();\n\n /** Map from tool name to server ID for routing tool calls */\n private toolToServer: Map<string, string> = new Map();\n\n /** Callback for server crash notifications */\n private onServerCrash?: (serverId: string, error: string) => void;\n\n /** Factory functions for creating SDK instances (injectable for testing) */\n private factories: MCPFactories;\n\n /**\n * Create a new MCPManager instance.\n *\n * @param factories - Optional factory functions for dependency injection (used in tests)\n */\n constructor(factories?: Partial<MCPFactories>) {\n this.factories = { ...defaultFactories, ...factories };\n }\n\n /**\n * Set a callback to be notified when a server crashes.\n *\n * @param callback - Function to call when a server crashes\n */\n setOnServerCrash(callback: (serverId: string, error: string) => void): void {\n this.onServerCrash = callback;\n }\n\n /**\n * Connect to MCP servers specified in the configuration.\n *\n * @param servers - Array of MCP server configurations\n */\n async connect(servers: MCPServerConfig[]): Promise<void> {\n for (const serverConfig of servers) {\n try {\n // Create stdio transport for subprocess MCP server\n // Use StdioClientTransport\n const transport = this.factories.createTransport({\n command: serverConfig.command,\n args: serverConfig.args,\n env: serverConfig.env,\n });\n\n // Create MCP client\n // Use Client class\n const client = this.factories.createClient({\n name: 'stdio-bus-worker',\n version: '1.0.0',\n });\n\n // Connect client to transport\n // SDK sends initialize and notifications/initialized\n await client.connect(transport);\n\n // Get and store server capabilities\n // Store server capabilities for feature detection\n const capabilities = client.getServerCapabilities();\n\n // Store the connection\n const connection: MCPConnection = {\n client,\n transport,\n config: serverConfig,\n connected: true,\n capabilities,\n };\n this.connections.set(serverConfig.id, connection);\n\n // Set up crash detection via transport close event\n // Detect server process exit\n this.setupCrashDetection(serverConfig.id, client);\n\n // Log successful connection to stderr\n console.error(`[MCP] Connected to server: ${serverConfig.id}`);\n } catch (error) {\n // Handle connection errors gracefully\n console.error(`[MCP] Failed to connect to server ${serverConfig.id}:`, error);\n // Continue with other servers\n }\n }\n }\n\n /**\n * Set up crash detection for an MCP server connection.\n *\n * @param serverId - The server ID\n * @param client - The MCP client\n */\n private setupCrashDetection(serverId: string, client: Client): void {\n // Listen for client close event which indicates server disconnection\n client.onclose = () => {\n const connection = this.connections.get(serverId);\n if (connection && connection.connected) {\n // Mark server as crashed\n connection.connected = false;\n connection.crashError = 'Server process exited unexpectedly';\n\n // Remove tools from this server from the routing map\n for (const [toolName, toolServerId] of this.toolToServer.entries()) {\n if (toolServerId === serverId) {\n this.toolToServer.delete(toolName);\n }\n }\n\n // Log the crash\n console.error(`[MCP] Server ${serverId} crashed: ${connection.crashError}`);\n\n // Notify callback if set\n if (this.onServerCrash) {\n this.onServerCrash(serverId, connection.crashError);\n }\n }\n };\n }\n\n /**\n * Get all available tools from connected MCP servers.\n *\n * @returns Combined list of tools from all connected servers\n */\n async listTools(): Promise<MCPTool[]> {\n const allTools: MCPTool[] = [];\n\n // Clear the toolToServer map before repopulating\n this.toolToServer.clear();\n\n // Iterate through all connected MCP servers\n for (const [serverId, connection] of this.connections) {\n if (!connection.connected) {\n continue;\n }\n\n try {\n // Handle pagination - keep fetching while there's a nextCursor\n let cursor: string | undefined;\n do {\n // Call client.listTools() on each connection\n // Use client.listTools() to discover available tools\n const result = await connection.client.listTools(cursor ? { cursor } : undefined);\n\n // Map the results to MCPTool[] format with serverId included\n // Store tool definitions (name, description, inputSchema)\n for (const tool of result.tools) {\n allTools.push({\n name: tool.name,\n description: tool.description,\n inputSchema: tool.inputSchema as Record<string, unknown>,\n serverId,\n });\n\n // Track which server provides this tool for routing\n this.toolToServer.set(tool.name, serverId);\n }\n\n // Handle pagination if the server returns nextCursor\n cursor = result.nextCursor;\n } while (cursor);\n } catch (error) {\n // Log error but continue with other servers\n console.error(`[MCP] Failed to list tools from server ${serverId}:`, error);\n }\n }\n\n return allTools;\n }\n\n /**\n * Invoke a tool on the appropriate MCP server.\n * - Finds the server that provides the tool\n * - Calls client.callTool({ name, arguments }) on the appropriate connection\n * - Handles CallToolResult response\n * - Checks isError flag for failures\n * - Returns errors for calls to crashed server\n *\n * @param name - The name of the tool to invoke\n * @param args - The arguments to pass to the tool\n * @param serverId - Optional server ID to call the tool on (if known)\n * @returns The tool call result with content and error status\n * @throws Error if tool is not found or server is not connected\n */\n async callTool(name: string, args: Record<string, unknown>, serverId?: string): Promise<MCPToolCallResult> {\n // Determine which server to call\n const targetServerId = serverId ?? this.toolToServer.get(name);\n\n if (!targetServerId) {\n throw new Error(`Tool \"${name}\" not found. Call listTools() first to discover available tools.`);\n }\n\n // Get the connection for the target server\n const connection = this.connections.get(targetServerId);\n\n if (!connection) {\n throw new Error(`Server \"${targetServerId}\" not found.`);\n }\n\n if (!connection.connected) {\n // Return errors for calls to crashed server\n const crashMessage = connection.crashError || 'Server is not connected';\n throw new Error(`Server \"${targetServerId}\" is unavailable: ${crashMessage}`);\n }\n\n try {\n // Call client.callTool() on the appropriate connection\n // Use client.callTool() to invoke tools\n const result = await connection.client.callTool({\n name,\n arguments: args,\n });\n\n // Map the SDK result to our MCPToolCallResult type\n // Use SDK content types from result\n const content: MCPContent[] = (result.content as Array<{ type: string;[key: string]: unknown }>).map((item) => {\n if (item.type === 'text') {\n return {\n type: 'text' as const,\n text: item.text as string,\n };\n } else if (item.type === 'image') {\n return {\n type: 'image' as const,\n data: item.data as string,\n mimeType: item.mimeType as string,\n };\n } else if (item.type === 'resource') {\n const resource = item.resource as { uri: string; mimeType?: string; text?: string; blob?: string };\n return {\n type: 'resource' as const,\n resource: {\n uri: resource.uri,\n mimeType: resource.mimeType,\n text: resource.text,\n blob: resource.blob,\n },\n };\n }\n // Default to text for unknown types\n return {\n type: 'text' as const,\n text: JSON.stringify(item),\n };\n });\n\n // Check CallToolResult.isError for tool failures\n return {\n content,\n isError: result.isError === true,\n };\n } catch (error) {\n // Log error and re-throw\n console.error(`[MCP] Failed to call tool \"${name}\" on server ${targetServerId}:`, error);\n throw error;\n }\n }\n\n /**\n * Get all available resources from connected MCP servers.\n * - Calls client.listResources() to discover available resources\n * - Stores resource definitions (uri, name, description, mimeType)\n * - Handles pagination via nextCursor if present\n *\n * @returns Combined list of resources from all connected servers\n */\n async listResources(): Promise<MCPResource[]> {\n const allResources: MCPResource[] = [];\n\n // Iterate through all connected MCP servers\n for (const [serverId, connection] of this.connections) {\n if (!connection.connected) {\n continue;\n }\n\n try {\n // Handle pagination - keep fetching while there's a nextCursor\n let cursor: string | undefined;\n do {\n // Call client.listResources() on each connection\n // Use client.listResources() to discover resources\n const result = await connection.client.listResources(cursor ? { cursor } : undefined);\n\n // Map the results to MCPResource[] format with serverId included\n for (const resource of result.resources) {\n allResources.push({\n uri: resource.uri,\n name: resource.name,\n description: resource.description,\n mimeType: resource.mimeType,\n serverId,\n });\n }\n\n // Handle pagination if the server returns nextCursor\n cursor = result.nextCursor;\n } while (cursor);\n } catch (error) {\n // Log error but continue with other servers\n console.error(`[MCP] Failed to list resources from server ${serverId}:`, error);\n }\n }\n\n return allResources;\n }\n\n /**\n * Read a resource from the appropriate MCP server.\n * - Calls client.readResource({ uri }) on the appropriate connection\n * - Handles TextResourceContents and BlobResourceContents\n * - Determines which server handles the URI based on resource list\n *\n * @param uri - The URI of the resource to read\n * @param serverId - Optional server ID to read from (if known)\n * @returns The resource contents\n * @throws Error if resource server is not found or not connected\n */\n async readResource(uri: string, serverId?: string): Promise<MCPResourceReadResult> {\n // Determine which server to use\n let targetServerId = serverId;\n\n // If no server ID provided, try to find the server that provides this resource\n if (!targetServerId) {\n // Search through all connected servers' resources to find the one with this URI\n for (const [, connection] of this.connections) {\n if (!connection.connected) {\n continue;\n }\n\n try {\n // Try to read from this server - if it has the resource, it will succeed\n const result = await connection.client.readResource({ uri });\n\n // Map the SDK result to our MCPResourceReadResult type\n const contents: MCPResourceContents[] = result.contents.map((item) => {\n const resourceItem = item as { uri: string; mimeType?: string; text?: string; blob?: string };\n if ('text' in resourceItem && resourceItem.text !== undefined) {\n return {\n uri: resourceItem.uri,\n mimeType: resourceItem.mimeType,\n text: resourceItem.text,\n } as MCPTextResourceContents;\n } else if ('blob' in resourceItem && resourceItem.blob !== undefined) {\n return {\n uri: resourceItem.uri,\n mimeType: resourceItem.mimeType,\n blob: resourceItem.blob,\n } as MCPBlobResourceContents;\n }\n // Default to text with empty content for unknown types\n return {\n uri: resourceItem.uri,\n mimeType: resourceItem.mimeType,\n text: '',\n } as MCPTextResourceContents;\n });\n\n return { contents };\n } catch {\n // This server doesn't have the resource, try the next one\n continue;\n }\n }\n\n throw new Error(`Resource \"${uri}\" not found on any connected server.`);\n }\n\n // Get the connection for the target server\n const connection = this.connections.get(targetServerId);\n\n if (!connection) {\n throw new Error(`Server \"${targetServerId}\" not found.`);\n }\n\n if (!connection.connected) {\n throw new Error(`Server \"${targetServerId}\" is not connected.`);\n }\n\n try {\n // Call client.readResource() on the appropriate connection\n // Use client.readResource() to read resources\n const result = await connection.client.readResource({ uri });\n\n // Map the SDK result to our MCPResourceReadResult type\n // Handle TextResourceContents and BlobResourceContents\n const contents: MCPResourceContents[] = result.contents.map((item) => {\n const resourceItem = item as { uri: string; mimeType?: string; text?: string; blob?: string };\n if ('text' in resourceItem && resourceItem.text !== undefined) {\n return {\n uri: resourceItem.uri,\n mimeType: resourceItem.mimeType,\n text: resourceItem.text,\n } as MCPTextResourceContents;\n } else if ('blob' in resourceItem && resourceItem.blob !== undefined) {\n return {\n uri: resourceItem.uri,\n mimeType: resourceItem.mimeType,\n blob: resourceItem.blob,\n } as MCPBlobResourceContents;\n }\n // Default to text with empty content for unknown types\n return {\n uri: resourceItem.uri,\n mimeType: resourceItem.mimeType,\n text: '',\n } as MCPTextResourceContents;\n });\n\n return { contents };\n } catch (error) {\n // Log error and re-throw\n console.error(`[MCP] Failed to read resource \"${uri}\" from server ${targetServerId}:`, error);\n throw error;\n }\n }\n\n /**\n * Get a connection by server ID.\n *\n * @param serverId - The server ID to look up\n * @returns The connection or undefined if not found\n */\n getConnection(serverId: string): MCPConnection | undefined {\n return this.connections.get(serverId);\n }\n\n /**\n * Get all active connections.\n *\n * @returns Array of all active connections\n */\n getAllConnections(): MCPConnection[] {\n return Array.from(this.connections.values()).filter((conn) => conn.connected);\n }\n\n /**\n * Get server capabilities for a specific server.\n *\n * @param serverId - The server ID to look up\n * @returns The server capabilities or undefined if not found/connected\n */\n getServerCapabilities(serverId: string): ServerCapabilities | undefined {\n const connection = this.connections.get(serverId);\n return connection?.connected ? connection.capabilities : undefined;\n }\n\n /**\n * Close all MCP server connections.\n */\n async close(): Promise<void> {\n for (const connection of this.connections.values()) {\n try {\n await connection.client.close();\n connection.connected = false;\n } catch (error) {\n console.error(`[MCP] Error closing connection ${connection.config.id}:`, error);\n }\n }\n this.connections.clear();\n this.toolToServer.clear();\n }\n\n /**\n * Abort all pending MCP operations.\n * Called when a session is cancelled to stop in-flight requests.\n */\n abortPendingOperations(): void {\n // Mark all connections as not connected to prevent new operations\n for (const connection of this.connections.values()) {\n connection.connected = false;\n }\n }\n\n /**\n * Check if a server has crashed.\n *\n * @param serverId - The server ID to check\n * @returns True if the server has crashed\n */\n isServerCrashed(serverId: string): boolean {\n const connection = this.connections.get(serverId);\n return connection !== undefined && !connection.connected && connection.crashError !== undefined;\n }\n\n /**\n * Get the crash error for a server.\n *\n * @param serverId - The server ID to check\n * @returns The crash error message or undefined if not crashed\n */\n getServerCrashError(serverId: string): string | undefined {\n const connection = this.connections.get(serverId);\n return connection?.crashError;\n }\n\n /**\n * Get all crashed servers.\n *\n * @returns Array of crashed server IDs with their error messages\n */\n getCrashedServers(): Array<{ serverId: string; error: string }> {\n const crashed: Array<{ serverId: string; error: string }> = [];\n for (const [serverId, connection] of this.connections) {\n if (!connection.connected && connection.crashError) {\n crashed.push({ serverId, error: connection.crashError });\n }\n }\n return crashed;\n }\n}\n", "/**\n * Session\n *\n * Represents a single ACP session with its state and MCP connections.\n * Stores sessionId, cwd, MCP connections, and cancellation flag.\n *\n * @module session/session\n */\n\nimport type { HistoryEntry, SessionState } from './types.js';\nimport { MCPManager } from '../mcp/index.js';\n\n/**\n * Factory function type for creating MCPManager instances.\n * Used for dependency injection in tests.\n */\nexport type MCPManagerFactory = () => MCPManager;\n\n/** Default factory using the real MCPManager */\nconst defaultMcpManagerFactory: MCPManagerFactory = () => new MCPManager();\n\n/**\n * Represents an ACP session.\n *\n * The Session class manages:\n * - Session identification (sessionId per requirements)\n * - Working directory context (cwd)\n * - MCP server connections via MCPManager\n * - Cancellation state for aborting operations\n * - Conversation history\n */\nexport class Session {\n /** Unique session identifier (SessionId type per requirements) */\n readonly id: string;\n\n /** Current working directory for the session */\n readonly cwd: string;\n\n /** Manager for MCP server connections */\n readonly mcpManager: MCPManager;\n\n /** Cancellation flag for aborting pending operations */\n private cancelled: boolean = false;\n\n /** Timestamp when the session was created */\n private createdAt: Date;\n\n /** Conversation history for the session */\n private history: HistoryEntry[] = [];\n\n /**\n * Create a new Session.\n *\n * @param id - Unique session identifier\n * @param cwd - Current working directory for the session\n * @param mcpManagerFactory - Optional factory for creating MCPManager (used in tests)\n */\n constructor(id: string, cwd: string, mcpManagerFactory?: MCPManagerFactory) {\n this.id = id;\n this.cwd = cwd;\n this.mcpManager = (mcpManagerFactory ?? defaultMcpManagerFactory)();\n this.createdAt = new Date();\n }\n\n /**\n * Check if the session has been cancelled.\n *\n * @returns true if the session has been cancelled\n */\n isCancelled(): boolean {\n return this.cancelled;\n }\n\n /**\n * Cancel the session.\n * Sets the cancellation flag and aborts pending MCP operations.\n */\n cancel(): void {\n this.cancelled = true;\n // Abort pending MCP operations by closing all connections\n // This will cause any in-flight requests to fail gracefully\n this.mcpManager.abortPendingOperations();\n }\n\n /**\n * Add an entry to the conversation history.\n *\n * @param role - Role of the message sender ('user' or 'agent')\n * @param content - Content of the message\n */\n addHistoryEntry(role: 'user' | 'agent', content: string): void {\n this.history.push({\n role,\n content,\n timestamp: new Date(),\n });\n }\n\n /**\n * Get the conversation history.\n *\n * @returns Array of history entries\n */\n getHistory(): HistoryEntry[] {\n return [...this.history];\n }\n\n /**\n * Clear the conversation history.\n */\n clearHistory(): void {\n this.history = [];\n }\n\n /**\n * Get the session state.\n *\n * @returns Current session state including id, cwd, cancelled flag, and history\n */\n getState(): SessionState {\n return {\n id: this.id,\n cwd: this.cwd,\n cancelled: this.cancelled,\n createdAt: this.createdAt,\n history: [...this.history],\n };\n }\n\n /**\n * Close the session and cleanup resources.\n * Closes all MCP server connections.\n */\n async close(): Promise<void> {\n await this.mcpManager.close();\n }\n}\n", "/**\n * Session Manager\n *\n * Manages the lifecycle of ACP sessions.\n *\n * @module session/manager\n */\n\nimport { type MCPManagerFactory, Session } from './session.js';\nimport type { MCPServerConfig } from '../mcp/types.js';\n\n/**\n * Manages ACP sessions.\n */\nexport class SessionManager {\n private sessions: Map<string, Session> = new Map();\n private mcpManagerFactory?: MCPManagerFactory;\n\n /**\n * Create a new SessionManager.\n *\n * @param mcpManagerFactory - Optional factory for creating MCPManager instances (used in tests)\n */\n constructor(mcpManagerFactory?: MCPManagerFactory) {\n this.mcpManagerFactory = mcpManagerFactory;\n }\n\n /**\n * Create a new session.\n */\n async createSession(cwd: string, mcpServers?: MCPServerConfig[]): Promise<Session> {\n // TODO: Implement in task 21.2\n const id = this.generateSessionId();\n const session = new Session(id, cwd, this.mcpManagerFactory);\n\n if (mcpServers && mcpServers.length > 0) {\n await session.mcpManager.connect(mcpServers);\n }\n\n this.sessions.set(id, session);\n return session;\n }\n\n /**\n * Get a session by ID.\n */\n getSession(id: string): Session | undefined {\n return this.sessions.get(id);\n }\n\n /**\n * Cancel a session.\n */\n cancelSession(id: string): boolean {\n const session = this.sessions.get(id);\n if (session) {\n session.cancel();\n return true;\n }\n return false;\n }\n\n /**\n * Close and remove a session.\n */\n async closeSession(id: string): Promise<boolean> {\n const session = this.sessions.get(id);\n if (session) {\n await session.close();\n this.sessions.delete(id);\n return true;\n }\n return false;\n }\n\n /**\n * Close all sessions.\n */\n async closeAll(): Promise<void> {\n for (const session of this.sessions.values()) {\n await session.close();\n }\n this.sessions.clear();\n }\n\n /**\n * Get all sessions.\n *\n * @returns Array of all sessions\n */\n getAllSessions(): Session[] {\n return Array.from(this.sessions.values());\n }\n\n /**\n * Remove a session from the manager without closing it.\n *\n * @param id - Session ID to remove\n * @returns true if session was removed, false if not found\n */\n removeSession(id: string): boolean {\n return this.sessions.delete(id);\n }\n\n /**\n * Generate a unique session ID using crypto.randomUUID().\n *\n * Generate unique sessionId using UUID\n *\n * @returns A unique UUID string for the session\n */\n private generateSessionId(): string {\n return crypto.randomUUID();\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Session ID routing helper for stdio Bus \u2194 ACP messages.\n *\n * Tracks request IDs to restore stdio Bus sessionId on responses,\n * and maps ACP sessionId to stdio Bus sessionId for notifications.\n */\n\ntype JsonRecord = Record<string, unknown>;\n\nexport class SessionIdRouter {\n private readonly requestSessionIdMap = new Map<string | number, string>();\n private readonly acpSessionIdMap = new Map<string, string>();\n\n /**\n * Process a single inbound (stdin) line.\n *\n * - Saves stdio Bus sessionId for request/response correlation\n * - Tracks ACP sessionId \u2194 stdio Bus sessionId mapping when available\n * - Strips stdio Bus sessionId before passing to ACP SDK\n */\n processIncomingLine(line: string): string {\n if (!line.trim()) {\n return line;\n }\n\n try {\n const msg = JSON.parse(line) as JsonRecord;\n const routingSessionId = this.readSessionId(msg.sessionId);\n const hasId = msg.id !== undefined && msg.id !== null;\n\n if (hasId && routingSessionId) {\n this.requestSessionIdMap.set(msg.id as string | number, routingSessionId);\n console.error(`[worker] Saved sessionId=\"${routingSessionId}\" for request id=${msg.id}`);\n }\n\n const paramsSessionId = this.readSessionId((msg.params as JsonRecord | undefined)?.sessionId);\n if (routingSessionId && paramsSessionId) {\n this.setAcpSessionMapping(paramsSessionId, routingSessionId, 'request');\n }\n\n if (hasId && routingSessionId) {\n const { sessionId, ...msgWithoutSession } = msg;\n return JSON.stringify(msgWithoutSession);\n }\n\n return line;\n } catch {\n return line;\n }\n }\n\n /**\n * Process a single outbound (stdout) line.\n *\n * - Restores stdio Bus sessionId on responses using request mapping\n * - Maps ACP sessionId to stdio Bus sessionId for notifications\n */\n processOutgoingLine(line: string): string {\n if (!line.trim()) {\n return line;\n }\n\n try {\n const msg = JSON.parse(line) as JsonRecord;\n const hasId = msg.id !== undefined && msg.id !== null;\n\n if (hasId && this.requestSessionIdMap.has(msg.id as string | number)) {\n const routingSessionId = this.requestSessionIdMap.get(msg.id as string | number);\n this.requestSessionIdMap.delete(msg.id as string | number);\n\n if (routingSessionId) {\n const resultSessionId = this.readSessionId((msg.result as JsonRecord | undefined)?.sessionId);\n if (resultSessionId) {\n this.setAcpSessionMapping(resultSessionId, routingSessionId, 'response');\n }\n\n const msgWithSession = { ...msg, sessionId: routingSessionId };\n console.error(`[worker] Restored sessionId=\"${routingSessionId}\" for response id=${msg.id}`);\n return JSON.stringify(msgWithSession);\n }\n }\n\n if (!hasId && !this.readSessionId(msg.sessionId)) {\n const paramsSessionId = this.readSessionId((msg.params as JsonRecord | undefined)?.sessionId);\n if (paramsSessionId) {\n const routingSessionId = this.acpSessionIdMap.get(paramsSessionId);\n if (routingSessionId) {\n const msgWithSession = { ...msg, sessionId: routingSessionId };\n return JSON.stringify(msgWithSession);\n }\n }\n }\n\n return line;\n } catch {\n return line;\n }\n }\n\n private readSessionId(value: unknown): string | null {\n return typeof value === 'string' && value.length > 0 ? value : null;\n }\n\n private setAcpSessionMapping(\n acpSessionId: string,\n routingSessionId: string,\n source: 'request' | 'response',\n ): void {\n const existing = this.acpSessionIdMap.get(acpSessionId);\n if (existing === routingSessionId) {\n return;\n }\n\n this.acpSessionIdMap.set(acpSessionId, routingSessionId);\n console.error(\n `[worker] Mapped ACP sessionId=\"${acpSessionId}\" to routing sessionId=\"${routingSessionId}\" (${source})`,\n );\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Distribution Resolver for the ACP Registry.\n *\n * Resolves distribution metadata to spawn commands based on distribution type.\n * Supports binary (platform-specific), npx (npm), and uvx (Python) distributions.\n *\n * @module registry/resolver\n */\n\nimport {\n BinaryDistribution,\n BinaryTarget,\n Distribution,\n NpxDistribution,\n Platform,\n SpawnCommand,\n UvxDistribution,\n} from './types.js';\n\n/**\n * Error thrown when the current platform is not supported by a binary distribution.\n */\nexport class PlatformNotSupportedError extends Error {\n constructor(\n public readonly agentId: string,\n public readonly platform: Platform,\n ) {\n super(`Platform not supported: ${platform} for agent ${agentId}`);\n this.name = 'PlatformNotSupportedError';\n }\n}\n\n/**\n * Error thrown when no supported distribution type is available.\n */\nexport class NoDistributionError extends Error {\n constructor(public readonly agentId: string) {\n super(`No supported distribution type for agent ${agentId}`);\n this.name = 'NoDistributionError';\n }\n}\n\n/**\n * Get the current platform identifier.\n * Maps Node.js platform/arch to ACP Registry platform format.\n *\n * @returns Platform identifier for the current OS and architecture\n */\nexport function getCurrentPlatform(): Platform {\n const platform = process.platform;\n const arch = process.arch;\n\n if (platform === 'darwin' && arch === 'arm64') return 'darwin-aarch64';\n if (platform === 'darwin' && arch === 'x64') return 'darwin-x86_64';\n if (platform === 'linux' && arch === 'arm64') return 'linux-aarch64';\n if (platform === 'linux' && arch === 'x64') return 'linux-x86_64';\n if (platform === 'win32' && arch === 'arm64') return 'windows-aarch64';\n if (platform === 'win32' && arch === 'x64') return 'windows-x86_64';\n\n // Default to linux-x86_64 for unsupported platforms\n return 'linux-x86_64';\n}\n\n/**\n * Resolve a binary distribution to a spawn command.\n *\n * @param distribution - Binary distribution to resolve\n * @param agentId - Agent ID for error messages\n * @returns Resolved spawn command\n * @throws PlatformNotSupportedError if current platform is not supported\n */\nexport function resolveBinary(\n distribution: BinaryDistribution,\n agentId: string,\n): SpawnCommand {\n const currentPlatform = getCurrentPlatform();\n const target: BinaryTarget | undefined = distribution[currentPlatform];\n\n if (!target) {\n throw new PlatformNotSupportedError(agentId, currentPlatform);\n }\n\n return {\n command: target.cmd,\n args: target.args ?? [],\n env: target.env,\n };\n}\n\n/**\n * Resolve an npx distribution to a spawn command.\n *\n * @param distribution - NPX distribution to resolve\n * @returns Resolved spawn command\n */\nexport function resolveNpx(distribution: NpxDistribution): SpawnCommand {\n return {\n command: 'npx',\n args: [distribution.package, ...(distribution.args ?? [])],\n env: distribution.env,\n };\n}\n\n/**\n * Resolve a uvx distribution to a spawn command.\n *\n * @param distribution - UVX distribution to resolve\n * @returns Resolved spawn command\n */\nexport function resolveUvx(distribution: UvxDistribution): SpawnCommand {\n return {\n command: 'uvx',\n args: [distribution.package, ...(distribution.args ?? [])],\n env: distribution.env,\n };\n}\n\n/**\n * Resolve a distribution to a spawn command.\n *\n * Priority order: npx > uvx > binary\n * (npx/uvx are preferred as they don't require platform-specific handling)\n *\n * @param distribution - Distribution to resolve\n * @param agentId - Agent ID for error messages\n * @returns Resolved spawn command\n * @throws PlatformNotSupportedError if binary distribution doesn't support current platform\n * @throws NoDistributionError if no distribution type is available\n */\nexport function resolve(\n distribution: Distribution,\n agentId: string,\n): SpawnCommand {\n // Prefer npx (most common, cross-platform)\n if (distribution.npx) {\n return resolveNpx(distribution.npx);\n }\n\n // Then uvx (Python packages)\n if (distribution.uvx) {\n return resolveUvx(distribution.uvx);\n }\n\n // Finally binary (platform-specific)\n if (distribution.binary) {\n return resolveBinary(distribution.binary, agentId);\n }\n\n throw new NoDistributionError(agentId);\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { ChildProcess, spawn } from 'child_process';\nimport type { AgentRuntime, RuntimeState } from './types.js';\nimport type { SpawnCommand } from '../registry/types.js';\n\n/**\n * Default timeout in milliseconds for graceful termination before SIGKILL.\n */\nconst DEFAULT_TERMINATE_TIMEOUT_MS = 5000;\n\n/**\n * Implementation of AgentRuntime that manages a spawned agent process.\n *\n * Handles process spawning with non-blocking file descriptors,\n * message writing to stdin, and graceful termination with SIGTERM/SIGKILL.\n */\nexport class AgentRuntimeImpl implements AgentRuntime {\n public readonly agentId: string;\n public state: RuntimeState;\n public readonly process: ChildProcess;\n\n private readonly onExitCallback?: (code: number | null, signal: string | null) => void;\n\n /**\n * Create a new AgentRuntime by spawning a process.\n *\n * @param agentId - The agent identifier\n * @param spawnCommand - The resolved spawn command\n * @param onExit - Optional callback when process exits\n */\n private constructor(\n agentId: string,\n process: ChildProcess,\n onExit?: (code: number | null, signal: string | null) => void,\n ) {\n this.agentId = agentId;\n this.process = process;\n this.state = 'starting';\n this.onExitCallback = onExit;\n\n this.setupProcessHandlers();\n }\n\n /**\n * Spawn a new agent process and create an AgentRuntime instance.\n *\n * @param agentId - The agent identifier\n * @param spawnCommand - The resolved spawn command with command, args, and optional env\n * @param onExit - Optional callback when process exits\n * @returns A new AgentRuntime instance\n */\n public static spawn(\n agentId: string,\n spawnCommand: SpawnCommand,\n onExit?: (code: number | null, signal: string | null) => void,\n ): AgentRuntimeImpl {\n const childProcess = spawn(spawnCommand.command, spawnCommand.args, {\n stdio: ['pipe', 'pipe', 'pipe'],\n env: {\n ...process.env,\n ...spawnCommand.env,\n },\n // Detach from parent's controlling terminal to avoid signal propagation issues\n detached: false,\n });\n\n // Set file descriptors to non-blocking mode\n // Node.js streams are already non-blocking by default when using 'pipe',\n // but we ensure the streams are in flowing mode for proper async handling\n if (childProcess.stdout) {\n childProcess.stdout.setEncoding('utf8');\n }\n if (childProcess.stderr) {\n childProcess.stderr.setEncoding('utf8');\n }\n\n return new AgentRuntimeImpl(agentId, childProcess, onExit);\n }\n\n /**\n * Set up event handlers for the child process.\n */\n private setupProcessHandlers(): void {\n // Handle process spawn success\n this.process.on('spawn', () => {\n if (this.state === 'starting') {\n this.state = 'running';\n }\n });\n\n // Handle process errors (e.g., spawn failure)\n this.process.on('error', (error: Error) => {\n this.state = 'stopped';\n // Log to stderr as per requirements\n process.stderr.write(\n `[${new Date().toISOString()}] ERROR: Agent ${this.agentId} process error: ${error.message}\\n`,\n );\n });\n\n // Handle process exit\n this.process.on('exit', (code: number | null, signal: string | null) => {\n this.state = 'stopped';\n if (this.onExitCallback) {\n this.onExitCallback(code, signal);\n }\n });\n\n // Handle stdin errors (e.g., broken pipe)\n if (this.process.stdin) {\n this.process.stdin.on('error', (error: Error) => {\n // Log stdin write errors but don't change state\n process.stderr.write(\n `[${new Date().toISOString()}] WARN: Agent ${this.agentId} stdin error: ${error.message}\\n`,\n );\n });\n }\n }\n\n /**\n * Write a message to the agent's stdin as NDJSON.\n *\n * @param message - The message object to send\n * @returns true if the write was accepted, false if backpressure or error\n */\n public write(message: object): boolean {\n if (this.state !== 'running' && this.state !== 'starting') {\n return false;\n }\n\n if (!this.process.stdin || this.process.stdin.destroyed) {\n return false;\n }\n\n try {\n const ndjsonLine = JSON.stringify(message) + '\\n';\n return this.process.stdin.write(ndjsonLine);\n } catch {\n return false;\n }\n }\n\n /**\n * Terminate the agent process gracefully.\n *\n * Sends SIGTERM first, then SIGKILL after timeout if process doesn't exit.\n *\n * @param timeout - Timeout in milliseconds before SIGKILL (default: 5000ms)\n * @returns Promise that resolves when process has exited\n */\n public async terminate(timeout: number = DEFAULT_TERMINATE_TIMEOUT_MS): Promise<void> {\n // Already stopped or stopping\n if (this.state === 'stopped') {\n return;\n }\n\n if (this.state === 'stopping') {\n // Wait for existing termination to complete\n return this.waitForExit();\n }\n\n this.state = 'stopping';\n\n // Close stdin to signal no more input\n if (this.process.stdin && !this.process.stdin.destroyed) {\n this.process.stdin.end();\n }\n\n // Send SIGTERM for graceful shutdown\n this.process.kill('SIGTERM');\n\n // Wait for exit with timeout\n const exitPromise = this.waitForExit();\n const timeoutPromise = new Promise<'timeout'>((resolve) => {\n setTimeout(() => resolve('timeout'), timeout);\n });\n\n const result = await Promise.race([exitPromise, timeoutPromise]);\n\n if (result === 'timeout' && !this.process.killed && this.process.exitCode === null) {\n // Force kill if still running after timeout\n this.process.kill('SIGKILL');\n await this.waitForExit();\n }\n }\n\n /**\n * Wait for the process to exit.\n *\n * @returns Promise that resolves when process exits\n */\n private waitForExit(): Promise<void> {\n if (this.state === 'stopped') {\n return Promise.resolve();\n }\n\n return new Promise((resolve) => {\n this.process.once('exit', () => {\n resolve();\n });\n });\n }\n}\n\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { AgentRuntime } from './types.js';\nimport type { SpawnCommand } from '../registry/types.js';\nimport { AgentRuntimeImpl } from './agent-runtime.js';\n\n/**\n * Default timeout in milliseconds for graceful termination before SIGKILL.\n */\nconst DEFAULT_SHUTDOWN_TIMEOUT_MS = 5000;\n\n/**\n * Callback type for agent exit events.\n */\nexport type AgentExitCallback = (agentId: string, code: number | null) => void;\n\n/**\n * Manager for agent runtime lifecycle.\n *\n * Handles spawning, tracking, and terminating agent processes.\n * Stores runtimes in a Map keyed by agentId.\n */\nexport class AgentRuntimeManager {\n /** Map of agentId to AgentRuntime instances */\n private readonly runtimes: Map<string, AgentRuntime> = new Map();\n\n /** Registered callbacks for agent exit events */\n private readonly exitCallbacks: AgentExitCallback[] = [];\n\n /**\n * Get an existing runtime or spawn a new one for the given agentId.\n *\n * If a runtime already exists for the agentId, returns it.\n * Otherwise, spawns a new agent process using the provided spawn command.\n *\n * @param agentId - The agent identifier\n * @param spawnCommand - The resolved spawn command for spawning a new process\n * @returns The existing or newly spawned AgentRuntime\n */\n public async getOrSpawn(agentId: string, spawnCommand: SpawnCommand): Promise<AgentRuntime> {\n // Return existing runtime if available\n const existing = this.runtimes.get(agentId);\n if (existing && existing.state !== 'stopped') {\n return existing;\n }\n\n // Spawn new runtime\n const runtime = AgentRuntimeImpl.spawn(\n agentId,\n spawnCommand,\n (code: number | null, _signal: string | null) => {\n this.handleAgentExit(agentId, code);\n },\n );\n\n // Store in map\n this.runtimes.set(agentId, runtime);\n\n return runtime;\n }\n\n /**\n * Get an existing runtime without spawning.\n *\n * @param agentId - The agent identifier\n * @returns The AgentRuntime if it exists, undefined otherwise\n */\n public get(agentId: string): AgentRuntime | undefined {\n return this.runtimes.get(agentId);\n }\n\n /**\n * Terminate a specific agent runtime.\n *\n * Sends SIGTERM to the agent process and waits for it to exit.\n * If the process doesn't exit within the timeout, sends SIGKILL.\n *\n * @param agentId - The agent identifier\n * @param timeout - Timeout in milliseconds before SIGKILL (default: 5000ms)\n */\n public async terminate(agentId: string, timeout: number = DEFAULT_SHUTDOWN_TIMEOUT_MS): Promise<void> {\n const runtime = this.runtimes.get(agentId);\n if (!runtime) {\n return;\n }\n\n await runtime.terminate(timeout);\n this.runtimes.delete(agentId);\n }\n\n /**\n * Terminate all agent runtimes for graceful shutdown.\n *\n * @param timeout - Timeout in milliseconds before SIGKILL (default: 5000ms)\n */\n public async terminateAll(timeout: number = DEFAULT_SHUTDOWN_TIMEOUT_MS): Promise<void> {\n const terminatePromises: Promise<void>[] = [];\n\n for (const [agentId, runtime] of this.runtimes) {\n if (runtime.state !== 'stopped') {\n terminatePromises.push(\n runtime.terminate(timeout).then(() => {\n this.runtimes.delete(agentId);\n }),\n );\n }\n }\n\n await Promise.all(terminatePromises);\n }\n\n /**\n * Register a callback for agent exit events.\n *\n * The callback is invoked when an agent process exits unexpectedly.\n *\n * @param callback - Function to call when an agent exits\n */\n public onAgentExit(callback: AgentExitCallback): void {\n this.exitCallbacks.push(callback);\n }\n\n /**\n * Handle agent process exit.\n *\n * Removes the runtime from the map and notifies registered callbacks.\n *\n * @param agentId - The agent identifier\n * @param code - The exit code (null if terminated by signal)\n */\n private handleAgentExit(agentId: string, code: number | null): void {\n // Remove from map\n this.runtimes.delete(agentId);\n\n // Notify all registered callbacks\n for (const callback of this.exitCallbacks) {\n try {\n callback(agentId, code);\n } catch {\n // Ignore callback errors to prevent one failing callback from affecting others\n }\n }\n }\n\n /**\n * Get the number of active runtimes.\n *\n * @returns The count of runtimes currently in the map\n */\n public get size(): number {\n return this.runtimes.size;\n }\n\n /**\n * Check if a runtime exists for the given agentId.\n *\n * @param agentId - The agent identifier\n * @returns true if a runtime exists, false otherwise\n */\n public has(agentId: string): boolean {\n return this.runtimes.has(agentId);\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * NDJSON (Newline-Delimited JSON) stream handler for the Registry Launcher.\n *\n * Handles buffering and parsing of NDJSON messages from stdin and writing\n * NDJSON messages to stdout. Supports partial reads across multiple chunks.\n *\n * @module stream/ndjson-handler\n */\n\nimport { Writable } from 'node:stream';\n\n/**\n * Callback type for parsed messages.\n */\nexport type MessageCallback = (message: object) => void;\n\n/**\n * Callback type for parse errors.\n */\nexport type ErrorCallback = (error: Error, line: string) => void;\n\n/**\n * Interface for NDJSON stream handling.\n */\nexport interface INDJSONHandler {\n /**\n * Register a callback for parsed messages.\n */\n onMessage(callback: MessageCallback): void;\n\n /**\n * Register a callback for parse errors.\n */\n onError(callback: ErrorCallback): void;\n\n /**\n * Write a message to the output stream.\n * @returns true if write was successful, false if stream is not writable\n */\n write(message: object): boolean;\n\n /**\n * Process incoming data chunk (call from stdin 'data' event).\n */\n processChunk(chunk: Buffer): void;\n}\n\n/**\n * Log an error message to stderr with ISO 8601 timestamp.\n * @param message - Error message to log\n */\nfunction logError(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`[${timestamp}] [ERROR] [ndjson] ${message}`);\n}\n\n/**\n * NDJSON stream handler implementation.\n *\n * This class handles:\n * - Buffering incoming data until complete newline-delimited messages are received\n * - Handling partial JSON messages that span multiple read operations\n * - Appending newline characters after each JSON message when writing\n * - Splitting on newline boundaries when reading\n * - Logging errors and skipping malformed lines\n */\nexport class NDJSONHandler implements INDJSONHandler {\n /** Buffer for accumulating partial data */\n private buffer: string = '';\n\n /** Output stream for writing messages */\n private readonly output: Writable;\n\n /** Registered message callback */\n private messageCallback: MessageCallback | null = null;\n\n /** Registered error callback */\n private errorCallback: ErrorCallback | null = null;\n\n /**\n * Create a new NDJSONHandler.\n * @param output - Writable stream for output (typically process.stdout)\n */\n constructor(output: Writable) {\n this.output = output;\n }\n\n /**\n * Register a callback for parsed messages.\n * Only one callback can be registered at a time.\n *\n * @param callback - Function to call with each parsed message\n */\n onMessage(callback: MessageCallback): void {\n this.messageCallback = callback;\n }\n\n /**\n * Register a callback for parse errors.\n * Only one callback can be registered at a time.\n *\n * @param callback - Function to call with parse errors\n */\n onError(callback: ErrorCallback): void {\n this.errorCallback = callback;\n }\n\n /**\n * Write a message to the output stream.\n *\n * Serializes the message as JSON and appends a newline character.\n *\n * @param message - Object to serialize and write\n * @returns true if write was successful, false if stream is not writable\n */\n write(message: object): boolean {\n if (!this.output.writable) {\n return false;\n }\n\n try {\n const json = JSON.stringify(message);\n this.output.write(json + '\\n');\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Process incoming data chunk.\n *\n * Buffers data and emits complete messages when newline boundaries are found.\n * Handles partial messages that span multiple chunks.\n *\n * @param chunk - Buffer containing incoming data\n */\n processChunk(chunk: Buffer): void {\n // Append chunk to buffer\n this.buffer += chunk.toString('utf-8');\n\n // Process complete lines\n this.processBuffer();\n }\n\n /**\n * Process the internal buffer, extracting and parsing complete lines.\n *\n * Splits on newline boundaries and parses each complete line as JSON.\n * Incomplete lines remain in the buffer for the next chunk.\n */\n private processBuffer(): void {\n let newlineIndex: number;\n\n // Process all complete lines in the buffer\n while ((newlineIndex = this.buffer.indexOf('\\n')) !== -1) {\n // Extract the line (without the newline)\n const line = this.buffer.slice(0, newlineIndex);\n\n // Remove the processed line and newline from buffer\n this.buffer = this.buffer.slice(newlineIndex + 1);\n\n // Skip empty lines\n if (line.trim().length === 0) {\n continue;\n }\n\n // Parse and emit the message\n this.parseLine(line);\n }\n }\n\n /**\n * Parse a single line as JSON and emit the message.\n *\n * If parsing fails, logs the error and invokes the error callback.\n * Malformed lines are skipped.\n *\n * @param line - Line to parse as JSON\n */\n private parseLine(line: string): void {\n try {\n const message = JSON.parse(line);\n\n // Ensure the parsed value is an object (not a primitive)\n if (message === null || typeof message !== 'object') {\n const error = new Error('Parsed JSON is not an object');\n logError(`Malformed NDJSON line (not an object): ${this.truncateLine(line)}`);\n this.errorCallback?.(error, line);\n return;\n }\n\n // Emit the parsed message\n this.messageCallback?.(message);\n } catch (error) {\n // Log the error and skip the malformed line\n logError(`Failed to parse NDJSON line: ${this.truncateLine(line)}`);\n this.errorCallback?.(error as Error, line);\n }\n }\n\n /**\n * Truncate a line for logging purposes.\n * @param line - Line to truncate\n * @param maxLength - Maximum length (default: 100)\n * @returns Truncated line with ellipsis if needed\n */\n private truncateLine(line: string, maxLength: number = 100): string {\n if (line.length <= maxLength) {\n return line;\n }\n return line.slice(0, maxLength) + '...';\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Registry Index for the ACP Registry.\n *\n * Handles fetching, parsing, and querying the ACP Registry.\n * Supports configurable registry URL via environment variable.\n *\n * @module registry/index\n */\n\nimport { Distribution, Registry, RegistryAgent, SpawnCommand } from './types.js';\nimport { resolve as resolveDistribution } from './resolver.js';\n\n// Re-export types for external use\nexport type {\n Platform,\n BinaryDistribution,\n BinaryTarget,\n NpxDistribution,\n UvxDistribution,\n Distribution,\n RegistryAgent,\n Registry,\n SpawnCommand,\n} from './types.js';\n\n// Re-export resolver functions and errors for external use\nexport { PlatformNotSupportedError, NoDistributionError, getCurrentPlatform, resolve } from './resolver.js';\n\n/**\n * Environment variable name for registry URL override.\n */\nconst ENV_REGISTRY_URL = 'ACP_REGISTRY_URL';\n\n/**\n * Error thrown when registry fetch fails.\n */\nexport class RegistryFetchError extends Error {\n constructor(message: string, public readonly cause?: Error) {\n super(message);\n this.name = 'RegistryFetchError';\n }\n}\n\n/**\n * Error thrown when registry JSON is malformed.\n */\nexport class RegistryParseError extends Error {\n constructor(message: string, public readonly cause?: Error) {\n super(message);\n this.name = 'RegistryParseError';\n }\n}\n\n/**\n * Error thrown when an agent is not found in the registry.\n */\nexport class AgentNotFoundError extends Error {\n constructor(public readonly agentId: string) {\n super(`Agent not found: ${agentId}`);\n this.name = 'AgentNotFoundError';\n }\n}\n\n/**\n * Log an error message to stderr with ISO 8601 timestamp.\n * @param message - Error message to log\n */\nfunction logError(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`[${timestamp}] [ERROR] [registry] ${message}`);\n}\n\n/**\n * Log an info message to stderr with ISO 8601 timestamp.\n * @param message - Info message to log\n */\nfunction logInfo(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`[${timestamp}] [INFO] [registry] ${message}`);\n}\n\n/**\n * Interface for the RegistryIndex.\n */\nexport interface IRegistryIndex {\n /**\n * Fetch and parse the ACP Registry from the configured URL.\n * @throws RegistryFetchError if fetch fails\n * @throws RegistryParseError if JSON is malformed\n */\n fetch(): Promise<void>;\n\n /**\n * Look up an agent by ID.\n * @returns The agent entry or undefined if not found\n */\n lookup(agentId: string): RegistryAgent | undefined;\n\n /**\n * Resolve an agent ID to a spawn command.\n * @throws AgentNotFoundError if agent not in registry\n * @throws PlatformNotSupportedError if binary distribution doesn't support current platform\n */\n resolve(agentId: string): SpawnCommand;\n}\n\n/**\n * Validate that a value is a non-empty string.\n */\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.length > 0;\n}\n\n/**\n * Validate that a value is a valid distribution object.\n * Distribution must have at least one of: binary, npx, uvx\n */\nfunction isValidDistribution(value: unknown): value is Distribution {\n if (value === null || typeof value !== 'object') {\n return false;\n }\n\n const dist = value as Record<string, unknown>;\n\n // Must have at least one distribution type\n const hasBinary = dist.binary !== undefined && typeof dist.binary === 'object';\n const hasNpx = dist.npx !== undefined && typeof dist.npx === 'object';\n const hasUvx = dist.uvx !== undefined && typeof dist.uvx === 'object';\n\n if (!hasBinary && !hasNpx && !hasUvx) {\n return false;\n }\n\n // Validate npx if present\n if (hasNpx) {\n const npx = dist.npx as Record<string, unknown>;\n if (!isNonEmptyString(npx.package)) {\n return false;\n }\n }\n\n // Validate uvx if present\n if (hasUvx) {\n const uvx = dist.uvx as Record<string, unknown>;\n if (!isNonEmptyString(uvx.package)) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Validate and parse a registry agent entry.\n */\nfunction parseAgent(value: unknown, index: number): RegistryAgent {\n if (value === null || typeof value !== 'object') {\n throw new RegistryParseError(`Agent at index ${index} is not an object`);\n }\n\n const raw = value as Record<string, unknown>;\n\n if (!isNonEmptyString(raw.id)) {\n throw new RegistryParseError(`Agent at index ${index} has invalid or missing \"id\" field`);\n }\n\n if (!isNonEmptyString(raw.name)) {\n throw new RegistryParseError(`Agent at index ${index} has invalid or missing \"name\" field`);\n }\n\n if (!isNonEmptyString(raw.version)) {\n throw new RegistryParseError(`Agent at index ${index} has invalid or missing \"version\" field`);\n }\n\n if (!isValidDistribution(raw.distribution)) {\n throw new RegistryParseError(`Agent at index ${index} has invalid or missing \"distribution\" field`);\n }\n\n const agent: RegistryAgent = {\n id: raw.id,\n name: raw.name,\n version: raw.version,\n distribution: raw.distribution as Distribution,\n };\n\n // Optional fields\n if (typeof raw.description === 'string') {\n agent.description = raw.description;\n }\n\n if (typeof raw.repository === 'string') {\n agent.repository = raw.repository;\n }\n\n if (Array.isArray(raw.authors)) {\n agent.authors = raw.authors.filter((a): a is string => typeof a === 'string');\n }\n\n if (typeof raw.license === 'string') {\n agent.license = raw.license;\n }\n\n if (typeof raw.icon === 'string') {\n agent.icon = raw.icon;\n }\n\n return agent;\n}\n\n/**\n * Parse and validate registry JSON data.\n *\n * @param data - Raw JSON data to parse\n * @returns Parsed and validated Registry object\n * @throws RegistryParseError if JSON is malformed or invalid\n */\nexport function parseRegistry(data: unknown): Registry {\n if (data === null || typeof data !== 'object') {\n throw new RegistryParseError('Registry data is not an object');\n }\n\n const raw = data as Record<string, unknown>;\n\n // Validate version field\n if (!isNonEmptyString(raw.version)) {\n throw new RegistryParseError('Registry has invalid or missing \"version\" field');\n }\n\n // Validate agents array\n if (!Array.isArray(raw.agents)) {\n throw new RegistryParseError('Registry has invalid or missing \"agents\" field');\n }\n\n // Parse each agent\n const agents: RegistryAgent[] = [];\n for (let i = 0; i < raw.agents.length; i++) {\n agents.push(parseAgent(raw.agents[i], i));\n }\n\n return {\n version: raw.version,\n agents,\n };\n}\n\n/**\n * Registry Index implementation.\n *\n * Fetches, parses, and provides lookup functionality for the ACP Registry.\n */\nexport class RegistryIndex implements IRegistryIndex {\n /** Configured registry URL */\n private readonly registryUrl: string;\n\n /** Parsed registry data (null until fetch() is called) */\n private registry: Registry | null = null;\n\n /** Map of agent ID to agent entry for fast lookup */\n private agentMap: Map<string, RegistryAgent> = new Map();\n\n /**\n * Create a new RegistryIndex.\n *\n * @param registryUrl - URL to fetch the registry from (can be overridden by ACP_REGISTRY_URL env var)\n */\n constructor(registryUrl: string) {\n // Environment variable takes precedence\n const envUrl = process.env[ENV_REGISTRY_URL];\n this.registryUrl = isNonEmptyString(envUrl) ? envUrl : registryUrl;\n }\n\n /**\n * Fetch and parse the ACP Registry from the configured URL.\n *\n * @throws RegistryFetchError if fetch fails\n * @throws RegistryParseError if JSON is malformed\n */\n async fetch(): Promise<void> {\n logInfo(`Fetching registry from ${this.registryUrl}`);\n\n let response: Response;\n try {\n response = await fetch(this.registryUrl);\n } catch (error) {\n const message = `Failed to fetch registry from ${this.registryUrl}: ${(error as Error).message}`;\n logError(message);\n throw new RegistryFetchError(message, error as Error);\n }\n\n if (!response.ok) {\n const message = `Failed to fetch registry from ${this.registryUrl}: HTTP ${response.status} ${response.statusText}`;\n logError(message);\n throw new RegistryFetchError(message);\n }\n\n let text: string;\n try {\n text = await response.text();\n } catch (error) {\n const message = `Failed to read registry response body: ${(error as Error).message}`;\n logError(message);\n throw new RegistryFetchError(message, error as Error);\n }\n\n let data: unknown;\n try {\n data = JSON.parse(text);\n } catch (error) {\n const message = `Failed to parse registry JSON: ${(error as Error).message}`;\n logError(message);\n throw new RegistryParseError(message, error as Error);\n }\n\n try {\n this.registry = parseRegistry(data);\n } catch (error) {\n if (error instanceof RegistryParseError) {\n logError(error.message);\n throw error;\n }\n const message = `Failed to validate registry data: ${(error as Error).message}`;\n logError(message);\n throw new RegistryParseError(message, error as Error);\n }\n\n // Build the agent lookup map\n this.agentMap.clear();\n for (const agent of this.registry.agents) {\n this.agentMap.set(agent.id, agent);\n }\n\n logInfo(`Registry loaded: version ${this.registry.version}, ${this.registry.agents.length} agents`);\n }\n\n /**\n * Look up an agent by ID.\n *\n * @param agentId - Agent ID to look up\n * @returns The agent entry or undefined if not found\n */\n lookup(agentId: string): RegistryAgent | undefined {\n return this.agentMap.get(agentId);\n }\n\n /**\n * Resolve an agent ID to a spawn command.\n *\n * @param agentId - Agent ID to resolve\n * @returns Resolved spawn command\n * @throws AgentNotFoundError if agent not in registry\n * @throws PlatformNotSupportedError if binary distribution doesn't support current platform\n */\n resolve(agentId: string): SpawnCommand {\n const agent = this.lookup(agentId);\n if (!agent) {\n throw new AgentNotFoundError(agentId);\n }\n\n return resolveDistribution(agent.distribution, agentId);\n }\n\n /**\n * Get the parsed registry data.\n * @returns The parsed registry or null if not yet fetched\n */\n getRegistry(): Registry | null {\n return this.registry;\n }\n}\n\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * API Keys loading for the Registry Launcher.\n *\n * Handles loading API keys from JSON files for agent authentication.\n *\n * @module config/api-keys\n */\n\nimport { readFileSync } from 'node:fs';\n\n/**\n * API keys structure for a single agent.\n */\nexport interface AgentApiKeys {\n /** API key for the agent */\n apiKey: string;\n /** Environment variables to pass to the agent */\n env: Record<string, string>;\n}\n\n/**\n * API keys file structure.\n */\nexport interface ApiKeysFile {\n /** Map of agent ID to API keys */\n agents: Record<string, AgentApiKeys>;\n /** Version of the API keys file format */\n version: string;\n}\n\n/**\n * Log a warning message to stderr with ISO 8601 timestamp.\n * @param message - Warning message to log\n */\nfunction logWarning(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`[${timestamp}] [WARN] [api-keys] ${message}`);\n}\n\n/**\n * Log an info message to stderr with ISO 8601 timestamp.\n * @param message - Info message to log\n */\nfunction logInfo(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`[${timestamp}] [INFO] [api-keys] ${message}`);\n}\n\n/**\n * Load API keys from a JSON file.\n *\n * @param apiKeysPath - Path to the API keys JSON file\n * @returns Loaded API keys or empty object if file is missing/malformed\n */\nexport function loadApiKeys(apiKeysPath: string): Record<string, AgentApiKeys> {\n try {\n const fileContent = readFileSync(apiKeysPath, 'utf-8');\n const parsed = JSON.parse(fileContent) as ApiKeysFile;\n\n if (!parsed.agents || typeof parsed.agents !== 'object') {\n logWarning(`API keys file \"${apiKeysPath}\" does not contain valid \"agents\" object`);\n return {};\n }\n\n // Count agents with non-empty API keys\n const agentsWithKeys = Object.entries(parsed.agents).filter(\n ([_, keys]) => keys.apiKey && keys.apiKey.length > 0,\n );\n\n logInfo(`Loaded API keys for ${agentsWithKeys.length} agents from \"${apiKeysPath}\"`);\n\n return parsed.agents;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n logWarning(`API keys file \"${apiKeysPath}\" not found, agents will not be authenticated`);\n } else if (error instanceof SyntaxError) {\n logWarning(`API keys file \"${apiKeysPath}\" contains malformed JSON`);\n } else {\n logWarning(`Failed to read API keys file \"${apiKeysPath}\": ${(error as Error).message}`);\n }\n return {};\n }\n}\n\n/**\n * Get API key for a specific agent.\n *\n * @param apiKeys - Loaded API keys\n * @param agentId - Agent ID to get key for\n * @returns API key or undefined if not found\n */\nexport function getAgentApiKey(\n apiKeys: Record<string, AgentApiKeys>,\n agentId: string,\n): string | undefined {\n const keys = apiKeys[agentId];\n if (!keys || !keys.apiKey || keys.apiKey.length === 0) {\n return undefined;\n }\n return keys.apiKey;\n}\n\n/**\n * Get environment variables for a specific agent.\n *\n * @param apiKeys - Loaded API keys\n * @param agentId - Agent ID to get env vars for\n * @returns Environment variables or empty object if not found\n */\nexport function getAgentEnv(\n apiKeys: Record<string, AgentApiKeys>,\n agentId: string,\n): Record<string, string> {\n const keys = apiKeys[agentId];\n if (!keys || !keys.env) {\n return {};\n }\n return keys.env;\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Message Router for the Registry Launcher.\n *\n * Routes incoming JSON-RPC messages to the appropriate agent based on agentId.\n * Handles agentId extraction, message transformation, and error response generation.\n *\n * @module router/message-router\n */\n\nimport type { IRegistryIndex } from '../registry/index.js';\nimport { AgentNotFoundError, PlatformNotSupportedError } from '../registry/index.js';\nimport type { AgentRuntimeManager } from '../runtime/manager.js';\nimport type { AgentRuntime } from '../runtime/types.js';\nimport { getAgentApiKey } from '../config/api-keys.js';\n\n/**\n * JSON-RPC error codes for routing errors.\n */\nexport const RoutingErrorCodes = {\n /** Missing agentId in request */\n MISSING_AGENT_ID: -32600,\n /** Agent not found in registry */\n AGENT_NOT_FOUND: -32001,\n /** Platform not supported for binary distribution */\n PLATFORM_NOT_SUPPORTED: -32002,\n /** Agent spawn failed */\n SPAWN_FAILED: -32003,\n} as const;\n\n/**\n * JSON-RPC error response structure.\n */\nexport interface ErrorResponse {\n jsonrpc: '2.0';\n id: string | number | null;\n error: {\n code: number;\n message: string;\n data?: unknown;\n };\n}\n\n/**\n * Pending request tracking structure.\n */\ninterface PendingRequest {\n id: string | number;\n agentId: string;\n timestamp: number;\n}\n\n/**\n * Callback type for writing responses to stdout.\n */\nexport type WriteCallback = (message: object) => boolean;\n\n/**\n * Log an error message to stderr with ISO 8601 timestamp.\n */\nfunction logError(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`[${timestamp}] [ERROR] [router] ${message}`);\n}\n\n/**\n * Log an info message to stderr with ISO 8601 timestamp.\n */\nfunction logInfo(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`[${timestamp}] [INFO] [router] ${message}`);\n}\n\n\n/**\n * Create a JSON-RPC error response.\n *\n * @param id - Request ID (null for notifications or unknown)\n * @param code - Error code\n * @param message - Error message\n * @param data - Optional additional error data\n * @returns Error response object\n */\nexport function createErrorResponse(\n id: string | number | null,\n code: number,\n message: string,\n data?: unknown,\n): ErrorResponse {\n const response: ErrorResponse = {\n jsonrpc: '2.0',\n id,\n error: { code, message },\n };\n\n if (data !== undefined) {\n response.error.data = data;\n }\n\n return response;\n}\n\n/**\n * Extract the agentId field from a message.\n *\n * @param message - The message object to extract from\n * @returns The agentId string or undefined if not present\n */\nexport function extractAgentId(message: object): string | undefined {\n const msg = message as Record<string, unknown>;\n const agentId = msg.agentId;\n\n if (typeof agentId === 'string' && agentId.length > 0) {\n return agentId;\n }\n\n return undefined;\n}\n\n/**\n * Extract the JSON-RPC id field from a message.\n *\n * @param message - The message object to extract from\n * @returns The id (string, number, or null)\n */\nexport function extractId(message: object): string | number | null {\n const msg = message as Record<string, unknown>;\n const id = msg.id;\n\n if (typeof id === 'string' || typeof id === 'number') {\n return id;\n }\n\n return null;\n}\n\n/**\n * Transform a message for forwarding to an agent.\n *\n * Removes the agentId field while preserving all other fields.\n *\n * @param message - The original message\n * @returns A new message object without the agentId field\n */\nexport function transformMessage(message: object): object {\n const { agentId: _, ...rest } = message as Record<string, unknown>;\n return rest;\n}\n\n\n/**\n * Message Router implementation.\n *\n * Routes incoming JSON-RPC messages to the appropriate agent based on agentId.\n * Handles message transformation, error generation, and request correlation.\n */\nexport class MessageRouter {\n /** Registry index for agent lookup and resolution */\n private readonly registry: IRegistryIndex;\n\n /** Runtime manager for agent process lifecycle */\n private readonly runtimeManager: AgentRuntimeManager;\n\n /** Callback for writing responses to stdout */\n private readonly writeCallback: WriteCallback;\n\n /** API keys for agent authentication */\n private readonly apiKeys: Record<string, any>;\n\n /** Map of request ID to pending request info for correlation */\n private readonly pendingRequests: Map<string | number, PendingRequest> = new Map();\n\n /** Map of agent ID to authentication state */\n private readonly authState: Map<string, 'none' | 'pending' | 'authenticated'> = new Map();\n\n /** Map of agent sessionId to client sessionId for notification routing */\n private readonly sessionIdMap: Map<string, string> = new Map();\n\n /**\n * Create a new MessageRouter.\n *\n * @param registry - Registry index for agent lookup\n * @param runtimeManager - Runtime manager for agent processes\n * @param writeCallback - Callback for writing responses to stdout\n * @param apiKeys - API keys for agent authentication (optional)\n */\n constructor(\n registry: IRegistryIndex,\n runtimeManager: AgentRuntimeManager,\n writeCallback: WriteCallback,\n apiKeys: Record<string, any> = {},\n ) {\n this.registry = registry;\n this.runtimeManager = runtimeManager;\n this.writeCallback = writeCallback;\n this.apiKeys = apiKeys;\n }\n\n /**\n * Route an incoming message to the appropriate agent.\n *\n * Extracts agentId, resolves spawn command, and forwards message.\n *\n * @param message - The incoming JSON-RPC message\n * @returns Error response if routing fails, undefined on success\n */\n async route(message: object): Promise<ErrorResponse | undefined> {\n const id = extractId(message);\n const agentId = extractAgentId(message);\n\n // Return error if agentId is missing\n if (agentId === undefined) {\n logError('Missing agentId in request');\n return createErrorResponse(id, RoutingErrorCodes.MISSING_AGENT_ID, 'Missing agentId');\n }\n\n // Resolve agent to spawn command\n let spawnCommand;\n try {\n spawnCommand = this.registry.resolve(agentId);\n } catch (error) {\n if (error instanceof AgentNotFoundError) {\n logError(`Agent not found: ${agentId}`);\n return createErrorResponse(id, RoutingErrorCodes.AGENT_NOT_FOUND, 'Agent not found', {\n agentId,\n });\n }\n if (error instanceof PlatformNotSupportedError) {\n logError(`Platform not supported for agent: ${agentId}`);\n return createErrorResponse(\n id,\n RoutingErrorCodes.PLATFORM_NOT_SUPPORTED,\n 'Platform not supported',\n { agentId, platform: (error as PlatformNotSupportedError).platform },\n );\n }\n throw error;\n }\n\n // Get or spawn agent runtime\n let runtime: AgentRuntime;\n try {\n runtime = await this.runtimeManager.getOrSpawn(agentId, spawnCommand);\n } catch (error) {\n logError(`Failed to spawn agent ${agentId}: ${(error as Error).message}`);\n return createErrorResponse(id, RoutingErrorCodes.SPAWN_FAILED, 'Agent spawn failed', {\n agentId,\n error: (error as Error).message,\n });\n }\n\n // Track pending request for correlation\n if (id !== null) {\n const msg = message as Record<string, unknown>;\n const clientSessionId = typeof msg.sessionId === 'string' ? msg.sessionId : undefined;\n\n this.pendingRequests.set(id, {\n id,\n agentId,\n timestamp: Date.now(),\n clientSessionId,\n } as any);\n }\n\n // Transform message (remove agentId) and forward to agent\n const transformedMessage = transformMessage(message);\n const success = runtime.write(transformedMessage);\n\n if (!success) {\n logError(`Failed to write to agent ${agentId}`);\n // Remove from pending if write failed\n if (id !== null) {\n this.pendingRequests.delete(id);\n }\n } else {\n logInfo(`Routed message to agent ${agentId}`);\n }\n\n return undefined;\n }\n\n /**\n * Handle a response from an agent process.\n *\n * Intercepts initialize responses to trigger automatic authentication.\n * Tracks sessionId mapping for proper notification routing.\n * Forwards all responses to stdout.\n *\n * @param agentId - The agent that sent the response\n * @param response - The response object from the agent\n */\n handleAgentResponse(agentId: string, response: object): void {\n const id = extractId(response);\n const msg = response as Record<string, unknown>;\n\n // Check if this is a response to a tracked request\n if (id !== null) {\n const pending = this.pendingRequests.get(id);\n if (pending && pending.agentId === agentId) {\n const result = msg.result as Record<string, unknown> | undefined;\n\n // Check if this is an initialize response with authMethods\n if (result && Array.isArray(result.authMethods) && result.authMethods.length > 0) {\n logInfo(`Agent ${agentId} requires authentication, attempting auto-auth`);\n this.authState.set(agentId, 'pending');\n void this.attemptAuthentication(agentId, result.authMethods as Array<{ id: string }>);\n }\n\n // Check if this is a session/new response - track sessionId mapping\n // session/new responses have sessionId in result, not in params\n if (result && typeof result.sessionId === 'string') {\n const agentSessionId = result.sessionId;\n const clientSessionId = (pending as any).clientSessionId;\n if (clientSessionId) {\n this.sessionIdMap.set(agentSessionId, clientSessionId);\n logInfo(`Mapped agent sessionId ${agentSessionId} to client sessionId ${clientSessionId}`);\n }\n }\n\n this.pendingRequests.delete(id);\n }\n }\n\n // Handle notifications (no id) - map sessionId from agent to client\n if (id === null && msg.method) {\n logInfo(`Received notification: ${msg.method}`);\n const params = msg.params as Record<string, unknown> | undefined;\n if (params && typeof params.sessionId === 'string') {\n const agentSessionId = params.sessionId;\n const clientSessionId = this.sessionIdMap.get(agentSessionId);\n\n if (clientSessionId) {\n // Replace agent sessionId with client sessionId for stdio Bus routing\n const enriched = {\n ...msg,\n sessionId: clientSessionId,\n params: {\n ...params,\n sessionId: agentSessionId, // Keep original in params for agent context\n },\n };\n logInfo(`Forwarding notification with mapped sessionId: ${clientSessionId}`);\n this.writeCallback(enriched);\n return;\n } else {\n // FIX: Forward notification even without mapping - use agentSessionId\n logInfo(`Forwarding notification with unmapped agentSessionId: ${agentSessionId}`);\n this.writeCallback(response);\n return;\n }\n } else {\n // CRITICAL FIX: Handle notifications without sessionId in params\n // Check if sessionId is at top level or if this is a global notification\n const topLevelSessionId = msg.sessionId as string | undefined;\n if (topLevelSessionId) {\n // sessionId already at top level, forward as-is\n this.writeCallback(response);\n return;\n } else {\n // Global notification without sessionId - add a default sessionId for routing\n logError(`Notification without sessionId: ${msg.method}, adding default sessionId for routing`);\n const enriched = {\n ...msg,\n sessionId: 'global-notifications', // Default sessionId for stdio_bus routing\n };\n this.writeCallback(enriched);\n return;\n }\n }\n }\n\n // Forward response unchanged\n this.writeCallback(response);\n }\n\n /**\n * Attempt automatic authentication for an agent.\n *\n * Selects the best authentication method and sends authenticate request.\n *\n * @param agentId - The agent to authenticate\n * @param authMethods - Available authentication methods from initialize response\n */\n private async attemptAuthentication(\n agentId: string,\n authMethods: Array<{ id: string }>,\n ): Promise<void> {\n // Get API key for this agent\n const apiKey = getAgentApiKey(this.apiKeys, agentId);\n\n if (!apiKey) {\n logError(`No API key found for agent ${agentId}, authentication will fail`);\n this.authState.set(agentId, 'none');\n return;\n }\n\n // Select authentication method (prefer openai-api-key, then any api-key method)\n let selectedMethod = authMethods.find(m => m.id === 'openai-api-key');\n if (!selectedMethod) {\n selectedMethod = authMethods.find(m => m.id.includes('api-key') || m.id.includes('apikey'));\n }\n if (!selectedMethod) {\n selectedMethod = authMethods[0];\n }\n\n logInfo(`Authenticating agent ${agentId} with method: ${selectedMethod.id}`);\n\n // Get agent runtime\n let runtime: AgentRuntime;\n try {\n const spawnCommand = this.registry.resolve(agentId);\n runtime = await this.runtimeManager.getOrSpawn(agentId, spawnCommand);\n } catch (error) {\n logError(`Failed to get runtime for authentication: ${(error as Error).message}`);\n this.authState.set(agentId, 'none');\n return;\n }\n\n // Build authenticate request\n const authRequest = {\n jsonrpc: '2.0',\n id: `auth-${agentId}-${Date.now()}`,\n method: 'authenticate',\n params: {\n methodId: selectedMethod.id,\n credentials: {\n apiKey: apiKey,\n },\n },\n };\n\n // Send authenticate request to agent\n const transformed = transformMessage(authRequest);\n const serialized = JSON.stringify(transformed) + '\\n';\n\n if (runtime.process.stdin) {\n runtime.process.stdin.write(serialized, (error) => {\n if (error) {\n logError(`Failed to send authenticate request to ${agentId}: ${error.message}`);\n this.authState.set(agentId, 'none');\n } else {\n logInfo(`Sent authenticate request to agent ${agentId}`);\n // Mark as authenticated (optimistic)\n this.authState.set(agentId, 'authenticated');\n }\n });\n }\n }\n\n /**\n * Get the number of pending requests.\n *\n * @returns The count of pending requests\n */\n get pendingCount(): number {\n return this.pendingRequests.size;\n }\n\n /**\n * Check if a request ID is pending.\n *\n * @param id - The request ID to check\n * @returns true if the request is pending, false otherwise\n */\n isPending(id: string | number): boolean {\n return this.pendingRequests.has(id);\n }\n\n /**\n * Clear all pending requests.\n * Useful for cleanup during shutdown.\n */\n clearPending(): void {\n this.pendingRequests.clear();\n }\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Configuration loading for the Registry Launcher.\n *\n * Handles loading configuration from JSON files with support for:\n * - Environment variable overrides (ACP_REGISTRY_URL)\n * - Default values for missing fields\n * - Warning logs for missing or malformed config files\n *\n * @module config\n */\n\nimport { readFileSync } from 'node:fs';\nimport { DEFAULT_CONFIG, LauncherConfig } from './types.js';\n\n/**\n * Environment variable name for registry URL override.\n */\nconst ENV_REGISTRY_URL = 'ACP_REGISTRY_URL';\n\n/**\n * Environment variable name for API keys path override.\n */\nconst ENV_API_KEYS_PATH = 'ACP_API_KEYS_PATH';\n\n/**\n * Log a warning message to stderr with ISO 8601 timestamp.\n * @param message - Warning message to log\n */\nfunction logWarning(message: string): void {\n const timestamp = new Date().toISOString();\n console.error(`[${timestamp}] [WARN] [config] ${message}`);\n}\n\n/**\n * Validate that a value is a non-empty string.\n * @param value - Value to validate\n * @returns True if value is a non-empty string\n */\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.length > 0;\n}\n\n/**\n * Validate that a value is a positive number.\n * @param value - Value to validate\n * @returns True if value is a positive number\n */\nfunction isPositiveNumber(value: unknown): value is number {\n return typeof value === 'number' && value > 0 && Number.isFinite(value);\n}\n\n/**\n * Parse and validate a configuration object.\n * Applies default values for missing or invalid fields.\n *\n * @param obj - Parsed JSON object\n * @returns Validated LauncherConfig with defaults applied\n */\nfunction parseConfigObject(obj: unknown): LauncherConfig {\n const config: LauncherConfig = { ...DEFAULT_CONFIG };\n\n if (obj === null || typeof obj !== 'object') {\n logWarning('Config file does not contain a valid object, using defaults');\n return config;\n }\n\n const rawConfig = obj as Record<string, unknown>;\n\n // Parse registryUrl\n if ('registryUrl' in rawConfig) {\n if (isNonEmptyString(rawConfig.registryUrl)) {\n config.registryUrl = rawConfig.registryUrl;\n } else {\n logWarning('Config field \"registryUrl\" is not a valid string, using default');\n }\n }\n\n // Parse apiKeysPath\n if ('apiKeysPath' in rawConfig) {\n if (isNonEmptyString(rawConfig.apiKeysPath)) {\n config.apiKeysPath = rawConfig.apiKeysPath;\n } else {\n logWarning('Config field \"apiKeysPath\" is not a valid string, using default');\n }\n }\n\n // Parse shutdownTimeoutSec\n if ('shutdownTimeoutSec' in rawConfig) {\n if (isPositiveNumber(rawConfig.shutdownTimeoutSec)) {\n config.shutdownTimeoutSec = rawConfig.shutdownTimeoutSec;\n } else {\n logWarning('Config field \"shutdownTimeoutSec\" is not a valid positive number, using default');\n }\n }\n\n return config;\n}\n\n/**\n * Apply environment variable overrides to the configuration.\n * Environment variables take precedence over config file values.\n *\n * @param config - Configuration to apply overrides to\n * @returns Configuration with environment overrides applied\n */\nfunction applyEnvironmentOverrides(config: LauncherConfig): LauncherConfig {\n const envRegistryUrl = process.env[ENV_REGISTRY_URL];\n const envApiKeysPath = process.env[ENV_API_KEYS_PATH];\n\n const overrides: Partial<LauncherConfig> = {};\n\n if (isNonEmptyString(envRegistryUrl)) {\n overrides.registryUrl = envRegistryUrl;\n }\n\n if (isNonEmptyString(envApiKeysPath)) {\n overrides.apiKeysPath = envApiKeysPath;\n }\n\n return {\n ...config,\n ...overrides,\n };\n}\n\n/**\n * Load configuration from a JSON file.\n *\n * This function:\n * 1. Reads the config file from the specified path\n * 2. Parses it as JSON\n * 3. Validates and applies default values for missing fields\n * 4. Applies environment variable overrides\n *\n * If the config file is missing or malformed, default values are used\n * and a warning is logged to stderr.\n *\n * @param configPath - Path to the JSON config file (optional)\n * @returns Loaded and validated configuration\n */\nexport function loadConfig(configPath?: string): LauncherConfig {\n let config: LauncherConfig = { ...DEFAULT_CONFIG };\n\n if (configPath) {\n try {\n const fileContent = readFileSync(configPath, 'utf-8');\n const parsed = JSON.parse(fileContent);\n config = parseConfigObject(parsed);\n } catch (error) {\n if (error instanceof SyntaxError) {\n logWarning(`Config file \"${configPath}\" contains malformed JSON, using defaults`);\n } else if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n logWarning(`Config file \"${configPath}\" not found, using defaults`);\n } else if ((error as NodeJS.ErrnoException).code === 'EACCES') {\n logWarning(`Config file \"${configPath}\" is not readable, using defaults`);\n } else {\n logWarning(`Failed to read config file \"${configPath}\": ${(error as Error).message}, using defaults`);\n }\n }\n }\n\n // Apply environment variable overrides\n config = applyEnvironmentOverrides(config);\n\n return config;\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Configuration for the Registry Launcher.\n */\nexport interface LauncherConfig {\n /** Registry URL (default: official ACP Registry) */\n registryUrl: string;\n /** Path to API keys JSON file (default: ./api-keys.json) */\n apiKeysPath: string;\n /** Agent shutdown timeout in seconds (default: 5) */\n shutdownTimeoutSec: number;\n}\n\n/**\n * Default configuration values.\n */\nexport const DEFAULT_CONFIG: LauncherConfig = {\n registryUrl: 'https://cdn.agentclientprotocol.com/registry/v1/latest/registry.json',\n apiKeysPath: './api-keys.json',\n shutdownTimeoutSec: 5,\n};\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * ACP Client Capabilities\n *\n * Provides utilities for accessing client capabilities like file system\n * and terminal operations via the ACP SDK.\n *\n * @module acp/client-capabilities\n */\n\nimport type { AgentSideConnection, ClientCapabilities, TerminalHandle } from '@agentclientprotocol/sdk';\n\n/**\n * Result of a file read operation.\n */\nexport interface FileReadResult {\n /** The file content */\n content: string;\n /** Whether the operation was successful */\n success: boolean;\n /** Error message if the operation failed */\n error?: string;\n}\n\n/**\n * Result of a file write operation.\n */\nexport interface FileWriteResult {\n /** Whether the operation was successful */\n success: boolean;\n /** Error message if the operation failed */\n error?: string;\n}\n\n/**\n * Result of a terminal command execution.\n */\nexport interface TerminalResult {\n /** The terminal output */\n output: string;\n /** The exit code (null if terminated by signal) */\n exitCode: number | null;\n /** The signal that terminated the process (null if exited normally) */\n signal: string | null;\n /** Whether the output was truncated */\n truncated: boolean;\n /** Whether the operation was successful */\n success: boolean;\n /** Error message if the operation failed */\n error?: string;\n}\n\n/**\n * Check if the client supports file system read operations.\n *\n * @param capabilities - The client capabilities\n * @returns Whether file read is supported\n */\nexport function canReadFile(capabilities: ClientCapabilities | null): boolean {\n return capabilities?.fs?.readTextFile === true;\n}\n\n/**\n * Check if the client supports file system write operations.\n *\n * @param capabilities - The client capabilities\n * @returns Whether file write is supported\n */\nexport function canWriteFile(capabilities: ClientCapabilities | null): boolean {\n return capabilities?.fs?.writeTextFile === true;\n}\n\n/**\n * Check if the client supports terminal operations.\n *\n * @param capabilities - The client capabilities\n * @returns Whether terminal is supported\n */\nexport function canUseTerminal(capabilities: ClientCapabilities | null): boolean {\n return capabilities?.terminal === true;\n}\n\n/**\n * Read a text file from the client's file system.\n *\n * @param connection - The ACP connection\n * @param sessionId - The session ID\n * @param path - Absolute path to the file\n * @param options - Optional read options (line, limit)\n * @returns The file read result\n */\nexport async function readFile(\n connection: AgentSideConnection,\n sessionId: string,\n path: string,\n options?: { line?: number; limit?: number },\n): Promise<FileReadResult> {\n try {\n const response = await connection.readTextFile({\n sessionId,\n path,\n line: options?.line,\n limit: options?.limit,\n });\n\n return {\n content: response.content,\n success: true,\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.error(`[ACP] Failed to read file \"${path}\":`, error);\n return {\n content: '',\n success: false,\n error: errorMessage,\n };\n }\n}\n\n/**\n * Write content to a text file in the client's file system.\n *\n * @param connection - The ACP connection\n * @param sessionId - The session ID\n * @param path - Absolute path to the file\n * @param content - The content to write\n * @returns The file write result\n */\nexport async function writeFile(\n connection: AgentSideConnection,\n sessionId: string,\n path: string,\n content: string,\n): Promise<FileWriteResult> {\n try {\n await connection.writeTextFile({\n sessionId,\n path,\n content,\n });\n\n return { success: true };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.error(`[ACP] Failed to write file \"${path}\":`, error);\n return {\n success: false,\n error: errorMessage,\n };\n }\n}\n\n/**\n * Execute a command in a terminal and wait for completion.\n * - Use SDK terminal methods (create, output, wait, kill, release)\n * - Track terminal IDs for cleanup\n * - Implement command timeouts\n *\n * @param connection - The ACP connection\n * @param sessionId - The session ID\n * @param command - The command to execute\n * @param options - Optional execution options\n * @returns The terminal execution result\n */\nexport async function executeCommand(\n connection: AgentSideConnection,\n sessionId: string,\n command: string,\n options?: {\n args?: string[];\n cwd?: string;\n env?: Array<{ name: string; value: string }>;\n timeout?: number;\n outputByteLimit?: number;\n },\n): Promise<TerminalResult> {\n let terminal: TerminalHandle | null = null;\n\n try {\n // Create terminal and execute command\n terminal = await connection.createTerminal({\n sessionId,\n command,\n args: options?.args,\n cwd: options?.cwd,\n env: options?.env,\n outputByteLimit: options?.outputByteLimit,\n });\n\n // Set up timeout if specified\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n let timedOut = false;\n\n if (options?.timeout && options.timeout > 0) {\n timeoutId = setTimeout(async () => {\n timedOut = true;\n if (terminal) {\n try {\n await terminal.kill();\n } catch {\n // Ignore kill errors\n }\n }\n }, options.timeout);\n }\n\n try {\n // Wait for command to complete\n const exitResult = await terminal.waitForExit();\n\n // Clear timeout if set\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n\n // Get final output\n const outputResult = await terminal.currentOutput();\n\n return {\n output: outputResult.output,\n exitCode: timedOut ? null : exitResult.exitCode ?? null,\n signal: timedOut ? 'SIGTERM' : exitResult.signal ?? null,\n truncated: outputResult.truncated,\n success: !timedOut && exitResult.exitCode === 0,\n error: timedOut ? 'Command timed out' : undefined,\n };\n } finally {\n // Clear timeout if still pending\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.error(`[ACP] Failed to execute command \"${command}\":`, error);\n return {\n output: '',\n exitCode: null,\n signal: null,\n truncated: false,\n success: false,\n error: errorMessage,\n };\n } finally {\n // Always release the terminal\n if (terminal) {\n try {\n await terminal.release();\n } catch {\n // Ignore release errors\n }\n }\n }\n}\n\n/**\n * Execute a command in a terminal without waiting for completion.\n * Returns a handle for managing the terminal.\n *\n * @param connection - The ACP connection\n * @param sessionId - The session ID\n * @param command - The command to execute\n * @param options - Optional execution options\n * @returns The terminal handle for managing the process\n */\nexport async function startCommand(\n connection: AgentSideConnection,\n sessionId: string,\n command: string,\n options?: {\n args?: string[];\n cwd?: string;\n env?: Array<{ name: string; value: string }>;\n outputByteLimit?: number;\n },\n): Promise<TerminalHandle> {\n return connection.createTerminal({\n sessionId,\n command,\n args: options?.args,\n cwd: options?.cwd,\n env: options?.env,\n outputByteLimit: options?.outputByteLimit,\n });\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Content Mapper\n *\n * Maps MCP tool call results to ACP ToolCallContent format.\n * Handles conversion between MCP content types (TextContent, ImageContent, EmbeddedResource)\n * and ACP ToolCallContent types (Content, Diff, Terminal).\n *\n * Also provides resource_link resolution for ACP prompts.\n *\n * @module acp/content-mapper\n */\n\nimport type {\n ContentBlock,\n EmbeddedResource as ACPEmbeddedResource,\n ImageContent as ACPImageContent,\n TextContent as ACPTextContent,\n ToolCallContent,\n} from '@agentclientprotocol/sdk';\nimport type {\n MCPContent,\n MCPEmbeddedResource,\n MCPImageContent,\n MCPResourceContents,\n MCPTextContent,\n} from '../mcp/types.js';\n\n/**\n * Maps a single MCP content item to an ACP ContentBlock.\n *\n * MCP content types:\n * - TextContent: { type: 'text', text: string }\n * - ImageContent: { type: 'image', data: string, mimeType: string }\n * - EmbeddedResource: { type: 'resource', resource: { uri, mimeType?, text?, blob? } }\n *\n * ACP ContentBlock types:\n * - TextContent: { type: 'text', text: string }\n * - ImageContent: { type: 'image', data: string, mimeType: string }\n * - EmbeddedResource: { type: 'resource', resource: TextResourceContents | BlobResourceContents }\n *\n * @param mcpContent - The MCP content item to convert\n * @returns The corresponding ACP ContentBlock\n */\nexport function mapMCPContentToACPContentBlock(mcpContent: MCPContent): ContentBlock {\n switch (mcpContent.type) {\n case 'text':\n return mapTextContent(mcpContent);\n case 'image':\n return mapImageContent(mcpContent);\n case 'resource':\n return mapEmbeddedResource(mcpContent);\n default:\n // Handle unknown content types by converting to text\n return {\n type: 'text',\n text: JSON.stringify(mcpContent),\n };\n }\n}\n\n/**\n * Maps MCP TextContent to ACP TextContent.\n *\n * @param mcpText - The MCP text content\n * @returns The ACP text content block\n */\nfunction mapTextContent(mcpText: MCPTextContent): ContentBlock {\n const result: ACPTextContent & { type: 'text' } = {\n type: 'text',\n text: mcpText.text,\n };\n return result;\n}\n\n/**\n * Maps MCP ImageContent to ACP ImageContent.\n *\n * @param mcpImage - The MCP image content\n * @returns The ACP image content block\n */\nfunction mapImageContent(mcpImage: MCPImageContent): ContentBlock {\n const result: ACPImageContent & { type: 'image' } = {\n type: 'image',\n data: mcpImage.data,\n mimeType: mcpImage.mimeType,\n };\n return result;\n}\n\n/**\n * Maps MCP EmbeddedResource to ACP EmbeddedResource.\n *\n * MCP EmbeddedResource has:\n * - resource.uri: string\n * - resource.mimeType?: string\n * - resource.text?: string (for text resources)\n * - resource.blob?: string (for binary resources)\n *\n * ACP EmbeddedResource expects:\n * - resource: TextResourceContents | BlobResourceContents\n * - TextResourceContents: { uri, mimeType?, text }\n * - BlobResourceContents: { uri, mimeType?, blob }\n *\n * @param mcpResource - The MCP embedded resource\n * @returns The ACP embedded resource content block\n */\nfunction mapEmbeddedResource(mcpResource: MCPEmbeddedResource): ContentBlock {\n const { resource } = mcpResource;\n\n // Determine if this is a text or blob resource\n if (resource.text !== undefined) {\n // Text resource\n const result: ACPEmbeddedResource & { type: 'resource' } = {\n type: 'resource',\n resource: {\n uri: resource.uri,\n mimeType: resource.mimeType,\n text: resource.text,\n },\n };\n return result;\n } else if (resource.blob !== undefined) {\n // Blob resource\n const result: ACPEmbeddedResource & { type: 'resource' } = {\n type: 'resource',\n resource: {\n uri: resource.uri,\n mimeType: resource.mimeType,\n blob: resource.blob,\n },\n };\n return result;\n } else {\n // Resource without content - create empty text resource\n const result: ACPEmbeddedResource & { type: 'resource' } = {\n type: 'resource',\n resource: {\n uri: resource.uri,\n mimeType: resource.mimeType,\n text: '',\n },\n };\n return result;\n }\n}\n\n/**\n * Maps an array of MCP content items to ACP ToolCallContent array.\n *\n * Each MCP content item is wrapped in an ACP Content structure with type: 'content'.\n *\n * @param mcpContents - Array of MCP content items\n * @returns Array of ACP ToolCallContent items\n */\nexport function mapMCPResultToACPToolCallContent(mcpContents: MCPContent[]): ToolCallContent[] {\n return mcpContents.map((mcpContent) => {\n const contentBlock = mapMCPContentToACPContentBlock(mcpContent);\n // Wrap in ACP Content structure with type: 'content'\n return {\n type: 'content' as const,\n content: contentBlock,\n };\n });\n}\n\n/**\n * Creates an error ToolCallContent from an error message.\n *\n * Used when MCP tool execution fails (isError: true) to create\n * appropriate error content for the ACP client.\n *\n * @param errorMessage - The error message to display\n * @returns A ToolCallContent array with the error message\n */\nexport function createErrorToolCallContent(errorMessage: string): ToolCallContent[] {\n return [\n {\n type: 'content' as const,\n content: {\n type: 'text',\n text: `Error: ${errorMessage}`,\n },\n },\n ];\n}\n\n/**\n * Maps MCP tool call result to ACP ToolCallContent array.\n *\n * Handles both successful results and error results.\n * For errors, creates appropriate error content.\n *\n * @param content - Array of MCP content items from tool result\n * @param isError - Whether the tool execution resulted in an error\n * @returns Array of ACP ToolCallContent items\n */\nexport function mapToolResultToACPContent(\n content: MCPContent[],\n isError: boolean = false,\n): ToolCallContent[] {\n if (isError && content.length > 0) {\n // For error results, extract text content as error message\n const errorText = content\n .filter((c): c is MCPTextContent => c.type === 'text')\n .map((c) => c.text)\n .join('\\n');\n\n if (errorText) {\n return createErrorToolCallContent(errorText);\n }\n }\n\n // Map all content items to ACP format\n return mapMCPResultToACPToolCallContent(content);\n}\n\n\n/**\n * Represents a resource_link from an ACP ContentBlock.\n */\nexport interface ResourceLink {\n type: 'resource_link';\n uri: string;\n name: string;\n title?: string;\n description?: string;\n mimeType?: string;\n size?: number;\n}\n\n/**\n * Type guard to check if a ContentBlock is a resource_link.\n *\n * @param block - The content block to check\n * @returns True if the block is a resource_link\n */\nexport function isResourceLink(block: ContentBlock): block is ResourceLink {\n return block.type === 'resource_link' && 'uri' in block;\n}\n\n/**\n * Maps MCP resource contents to an ACP EmbeddedResource ContentBlock.\n *\n * Converts the result of readResource() to an ACP-compatible format.\n *\n * @param contents - The MCP resource contents from readResource()\n * @returns The ACP embedded resource content block\n */\nexport function mapMCPResourceContentsToACPContentBlock(contents: MCPResourceContents): ContentBlock {\n if ('text' in contents) {\n // Text resource\n const result: ACPEmbeddedResource & { type: 'resource' } = {\n type: 'resource',\n resource: {\n uri: contents.uri,\n mimeType: contents.mimeType,\n text: contents.text,\n },\n };\n return result;\n }\n\n // Blob resource (the only other option in the union type)\n const result: ACPEmbeddedResource & { type: 'resource' } = {\n type: 'resource',\n resource: {\n uri: contents.uri,\n mimeType: contents.mimeType,\n blob: contents.blob,\n },\n };\n return result;\n}\n\n/**\n * Extracts the URI from a resource_link ContentBlock.\n *\n * @param block - The resource_link content block\n * @returns The URI string or null if not a valid resource_link\n */\nexport function extractResourceLinkUri(block: ContentBlock): string | null {\n if (isResourceLink(block)) {\n return block.uri;\n }\n return null;\n}\n", "/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Tool Call Utilities\n *\n * Provides utilities for initiating and managing tool calls in ACP.\n * Handles tool call lifecycle including initiation, permission requests,\n * execution, and result reporting.\n *\n * @module acp/tools\n */\n\nimport type { AgentSideConnection, PermissionOption, ToolCallContent, ToolCallUpdate } from '@agentclientprotocol/sdk';\nimport { mapToolResultToACPContent } from './content-mapper.js';\nimport type { MCPManager } from '../mcp/manager.js';\nimport type { MCPContent } from '../mcp/types.js';\n\n/**\n * Permission request result.\n */\nexport interface PermissionResult {\n /** Whether permission was granted */\n granted: boolean;\n /** The option ID that was selected (if granted) */\n optionId?: string;\n /** Whether the request was cancelled */\n cancelled?: boolean;\n}\n\n/**\n * Tool kind categories for ACP tool calls.\n * Helps clients choose appropriate icons and UI treatment.\n */\nexport type ToolKind =\n 'read'\n | 'edit'\n | 'delete'\n | 'move'\n | 'search'\n | 'execute'\n | 'think'\n | 'fetch'\n | 'switch_mode'\n | 'other';\n\n/**\n * Tool call status values.\n */\nexport type ToolCallStatus = 'pending' | 'in_progress' | 'completed' | 'failed';\n\n/**\n * Counter for generating unique tool call IDs within a session.\n */\nlet toolCallCounter = 0;\n\n/**\n * Generate a unique tool call ID.\n *\n * @returns A unique tool call ID string\n */\nexport function generateToolCallId(): string {\n toolCallCounter++;\n return `tool-${Date.now()}-${toolCallCounter}`;\n}\n\n/**\n * Determine the tool kind based on tool name and description.\n *\n * @param toolName - The name of the tool\n * @param description - Optional tool description\n * @returns The appropriate ToolKind\n */\nexport function determineToolKind(toolName: string, description?: string): ToolKind {\n const name = toolName.toLowerCase();\n const desc = (description || '').toLowerCase();\n\n // Check description first for more specific hints\n if (desc.includes('external') || desc.includes('api') || desc.includes('http')) {\n return 'fetch';\n }\n\n // Check for read operations\n if (name.includes('read') || name.includes('get') || name.includes('list') || name.includes('fetch')) {\n return 'read';\n }\n\n // Check for edit/write operations\n if (name.includes('write') || name.includes('edit') || name.includes('update') || name.includes('modify')) {\n return 'edit';\n }\n\n // Check for delete operations\n if (name.includes('delete') || name.includes('remove')) {\n return 'delete';\n }\n\n // Check for move/rename operations\n if (name.includes('move') || name.includes('rename')) {\n return 'move';\n }\n\n // Check for search operations\n if (name.includes('search') || name.includes('find') || name.includes('query')) {\n return 'search';\n }\n\n // Check for execute operations\n if (name.includes('exec') || name.includes('run') || name.includes('shell') || name.includes('command')) {\n return 'execute';\n }\n\n // Check for fetch operations (external data)\n if (name.includes('http') || name.includes('api') || name.includes('request')) {\n return 'fetch';\n }\n\n // Default to 'other' for unknown tools\n return 'other';\n}\n\n/**\n * Send a tool_call session update to initiate a tool call.\n *\n * @param connection - The ACP connection\n * @param sessionId - The session ID\n * @param toolCallId - The unique tool call ID\n * @param title - Human-readable title for the tool call\n * @param kind - The tool kind category\n * @param status - Initial status (usually 'pending')\n */\nexport async function sendToolCallInitiation(\n connection: AgentSideConnection,\n sessionId: string,\n toolCallId: string,\n title: string,\n kind: ToolKind = 'other',\n status: ToolCallStatus = 'pending',\n): Promise<void> {\n await connection.sessionUpdate({\n sessionId,\n update: {\n sessionUpdate: 'tool_call',\n toolCallId,\n title,\n kind,\n status,\n },\n });\n}\n\n/**\n * Send a tool_call_update to report progress or results.\n *\n * @param connection - The ACP connection\n * @param sessionId - The session ID\n * @param toolCallId - The tool call ID to update\n * @param status - The new status\n * @param content - Optional content to include\n * @param title - Optional updated title\n */\nexport async function sendToolCallUpdate(\n connection: AgentSideConnection,\n sessionId: string,\n toolCallId: string,\n status: ToolCallStatus,\n content?: ToolCallContent[],\n title?: string,\n): Promise<void> {\n await connection.sessionUpdate({\n sessionId,\n update: {\n sessionUpdate: 'tool_call_update',\n toolCallId,\n status,\n content,\n title,\n },\n });\n}\n\n/**\n * Request permission from the user before executing a tool.\n *\n * @param connection - The ACP connection\n * @param sessionId - The session ID\n * @param toolCallId - The tool call ID\n * @param title - Human-readable title for the tool call\n * @param kind - The tool kind category\n * @param options - Permission options to present to the user\n * @returns The permission result with granted/cancelled status\n */\nexport async function requestToolPermission(\n connection: AgentSideConnection,\n sessionId: string,\n toolCallId: string,\n title: string,\n kind: ToolKind = 'other',\n options?: PermissionOption[],\n): Promise<PermissionResult> {\n // Build default permission options if not provided\n const permissionOptions: PermissionOption[] = options ?? [\n { optionId: 'allow_once', name: 'Allow once', kind: 'allow_once' },\n { optionId: 'allow_always', name: 'Allow always', kind: 'allow_always' },\n { optionId: 'reject_once', name: 'Reject', kind: 'reject_once' },\n ];\n\n // Build the tool call update for the permission request\n const toolCall: ToolCallUpdate = {\n toolCallId,\n title,\n kind,\n status: 'pending',\n };\n\n try {\n // Use SDK's requestPermission method\n const response = await connection.requestPermission({\n sessionId,\n toolCall,\n options: permissionOptions,\n });\n\n // Handle the response outcome\n if (response.outcome.outcome === 'cancelled') {\n return { granted: false, cancelled: true };\n }\n\n if (response.outcome.outcome === 'selected') {\n const selectedOption = response.outcome.optionId;\n // Check if the selected option is an allow option\n const isAllowed = selectedOption.startsWith('allow');\n return {\n granted: isAllowed,\n optionId: selectedOption,\n cancelled: false,\n };\n }\n\n // Unknown outcome - treat as rejected\n return { granted: false, cancelled: false };\n } catch (error) {\n // If permission request fails, treat as cancelled\n console.error('[ACP] Permission request failed:', error);\n return { granted: false, cancelled: true };\n }\n}\n\n/**\n * Execute a tool call with full lifecycle management.\n *\n * This function handles the complete tool call flow:\n * 1. Sends tool_call initiation with pending status\n * 2. Updates to in_progress when execution starts\n * 3. Executes the tool via MCP\n * 4. Updates with completed/failed status and results\n *\n * @param connection - The ACP connection\n * @param sessionId - The session ID\n * @param mcpManager - The MCP manager for tool execution\n * @param toolName - The name of the tool to execute\n * @param args - Arguments to pass to the tool\n * @param description - Optional tool description for kind detection\n * @returns The tool call result content\n */\nexport async function executeToolCall(\n connection: AgentSideConnection,\n sessionId: string,\n mcpManager: MCPManager,\n toolName: string,\n args: Record<string, unknown>,\n description?: string,\n): Promise<ToolCallContent[]> {\n // Generate unique tool call ID\n const toolCallId = generateToolCallId();\n\n // Determine tool kind\n const kind = determineToolKind(toolName, description);\n\n // Create human-readable title\n const title = `Executing: ${toolName}`;\n\n try {\n // Send tool_call initiation with pending status\n await sendToolCallInitiation(connection, sessionId, toolCallId, title, kind, 'pending');\n\n // Update to in_progress\n await sendToolCallUpdate(connection, sessionId, toolCallId, 'in_progress');\n\n // Execute the tool via MCP\n const result = await mcpManager.callTool(toolName, args);\n\n // Map MCP result to ACP content\n const content = mapToolResultToACPContent(result.content as MCPContent[], result.isError);\n\n // Send final update with completed/failed status\n const finalStatus: ToolCallStatus = result.isError ? 'failed' : 'completed';\n await sendToolCallUpdate(connection, sessionId, toolCallId, finalStatus, content);\n\n return content;\n } catch (error) {\n // Send failed status on error\n const errorMessage = error instanceof Error ? error.message : String(error);\n const errorContent: ToolCallContent[] = [{\n type: 'content',\n content: {\n type: 'text',\n text: `Error: ${errorMessage}`,\n },\n }];\n\n await sendToolCallUpdate(connection, sessionId, toolCallId, 'failed', errorContent);\n\n return errorContent;\n }\n}\n\n/**\n * Execute a tool call with permission request.\n *\n * This function handles the complete tool call flow with permission:\n * 1. Sends tool_call initiation with pending status\n * 2. Requests permission from the user\n * 3. If granted, executes the tool via MCP\n * 4. Updates with completed/failed status and results\n *\n * @param connection - The ACP connection\n * @param sessionId - The session ID\n * @param mcpManager - The MCP manager for tool execution\n * @param toolName - The name of the tool to execute\n * @param args - Arguments to pass to the tool\n * @param description - Optional tool description for kind detection\n * @param requirePermission - Whether to request permission before execution\n * @returns The tool call result content and permission result\n */\nexport async function executeToolCallWithPermission(\n connection: AgentSideConnection,\n sessionId: string,\n mcpManager: MCPManager,\n toolName: string,\n args: Record<string, unknown>,\n description?: string,\n requirePermission: boolean = true,\n): Promise<{ content: ToolCallContent[]; permissionResult?: PermissionResult }> {\n // Generate unique tool call ID\n const toolCallId = generateToolCallId();\n\n // Determine tool kind\n const kind = determineToolKind(toolName, description);\n\n // Create human-readable title\n const title = `Executing: ${toolName}`;\n\n try {\n // Send tool_call initiation with pending status\n await sendToolCallInitiation(connection, sessionId, toolCallId, title, kind, 'pending');\n\n // Request permission if required\n if (requirePermission) {\n const permissionResult = await requestToolPermission(\n connection,\n sessionId,\n toolCallId,\n title,\n kind,\n );\n\n // If permission was cancelled or rejected, return early\n if (!permissionResult.granted) {\n const status: ToolCallStatus = permissionResult.cancelled ? 'failed' : 'failed';\n const message = permissionResult.cancelled ? 'Permission request cancelled' : 'Permission denied';\n const errorContent: ToolCallContent[] = [{\n type: 'content',\n content: {\n type: 'text',\n text: message,\n },\n }];\n\n await sendToolCallUpdate(connection, sessionId, toolCallId, status, errorContent);\n\n return { content: errorContent, permissionResult };\n }\n }\n\n // Update to in_progress\n await sendToolCallUpdate(connection, sessionId, toolCallId, 'in_progress');\n\n // Execute the tool via MCP\n const result = await mcpManager.callTool(toolName, args);\n\n // Map MCP result to ACP content\n const content = mapToolResultToACPContent(result.content as MCPContent[], result.isError);\n\n // Send final update with completed/failed status\n const finalStatus: ToolCallStatus = result.isError ? 'failed' : 'completed';\n await sendToolCallUpdate(connection, sessionId, toolCallId, finalStatus, content);\n\n return { content };\n } catch (error) {\n // Send failed status on error\n const errorMessage = error instanceof Error ? error.message : String(error);\n const errorContent: ToolCallContent[] = [{\n type: 'content',\n content: {\n type: 'text',\n text: `Error: ${errorMessage}`,\n },\n }];\n\n await sendToolCallUpdate(connection, sessionId, toolCallId, 'failed', errorContent);\n\n return { content: errorContent };\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["connection", "connection", "process", "resolve", "logError", "connection", "result", "connection"]
7
7
  }