@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.
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../workers-registry/acp-worker/src/registry-launcher/config/types.ts", "../../../../workers-registry/acp-worker/src/registry-launcher/config/config.ts", "../../../../workers-registry/acp-worker/src/registry-launcher/config/api-keys.ts", "../../../../workers-registry/acp-worker/src/registry-launcher/registry/resolver.ts", "../../../../workers-registry/acp-worker/src/registry-launcher/registry/index.ts", "../../../../workers-registry/acp-worker/src/registry-launcher/stream/ndjson-handler.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/router/message-router.ts", "../../../../workers-registry/acp-worker/src/registry-launcher/log.ts", "../../../../workers-registry/acp-worker/src/registry-launcher/index.ts", "../../../../workers-registry/acp-registry/index.js"],
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 * 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 * 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 * 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 * 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\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 * 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\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 * 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 * Logging utilities for the Registry Launcher.\n *\n * Provides structured logging to stderr with ISO 8601 timestamps and severity levels.\n * All logs go to stderr only - stdout is reserved for NDJSON protocol messages.\n *\n * @module log\n */\n\n/**\n * Log severity levels.\n */\nexport type LogLevel = 'DEBUG' | 'INFO' | 'WARN' | 'ERROR';\n\n/**\n * Log context identifier for the registry launcher.\n */\nconst LOG_CONTEXT = 'registry-launcher';\n\n/**\n * Format a log message with ISO 8601 timestamp and severity level.\n *\n * @param level - Severity level\n * @param message - Log message\n * @param context - Optional context identifier (defaults to 'registry-launcher')\n * @returns Formatted log line\n */\nfunction formatLogMessage(level: LogLevel, message: string, context: string = LOG_CONTEXT): string {\n const timestamp = new Date().toISOString();\n return `[${timestamp}] [${level}] [${context}] ${message}`;\n}\n\n/**\n * Log a message to stderr with ISO 8601 timestamp and severity level.\n *\n * @param level - Severity level (DEBUG, INFO, WARN, ERROR)\n * @param message - Log message\n * @param context - Optional context identifier (defaults to 'registry-launcher')\n */\nexport function log(level: LogLevel, message: string, context?: string): void {\n const formatted = formatLogMessage(level, message, context);\n console.error(formatted);\n}\n\n/**\n * Log an agent spawn event.\n *\n * @param agentId - The agent identifier\n * @param command - The spawn command\n * @param args - The spawn command arguments\n */\nexport function logSpawn(agentId: string, command: string, args: string[]): void {\n const fullCommand = [command, ...args].join(' ');\n log('INFO', `Spawning agent \"${agentId}\": ${fullCommand}`);\n}\n\n/**\n * Log an agent exit event.\n *\n * @param agentId - The agent identifier\n * @param exitCode - The exit code (null if terminated by signal)\n * @param signal - The signal that terminated the process (null if exited normally)\n */\nexport function logExit(agentId: string, exitCode: number | null, signal?: string | null): void {\n if (signal) {\n log('INFO', `Agent \"${agentId}\" exited with signal ${signal}`);\n } else if (exitCode !== null) {\n log('INFO', `Agent \"${agentId}\" exited with code ${exitCode}`);\n } else {\n log('INFO', `Agent \"${agentId}\" exited`);\n }\n}\n\n/**\n * Log a backpressure warning when writing to an agent process fails.\n *\n * @param agentId - The agent identifier\n */\nexport function logBackpressure(agentId: string): void {\n log('WARN', `Write to agent \"${agentId}\" failed due to backpressure`);\n}\n\n/**\n * Log an error when an agent's stdin closes unexpectedly.\n *\n * @param agentId - The agent identifier\n * @param error - The error that occurred\n */\nexport function logStdinClosed(agentId: string, error?: Error): void {\n const errorMessage = error ? `: ${error.message}` : '';\n log('ERROR', `Agent \"${agentId}\" stdin closed unexpectedly${errorMessage}`);\n}\n\n/**\n * Convenience function for logging debug messages.\n *\n * @param message - Debug message\n * @param context - Optional context identifier\n */\nexport function logDebug(message: string, context?: string): void {\n log('DEBUG', message, context);\n}\n\n/**\n * Convenience function for logging info messages.\n *\n * @param message - Info message\n * @param context - Optional context identifier\n */\nexport function logInfo(message: string, context?: string): void {\n log('INFO', message, context);\n}\n\n/**\n * Convenience function for logging warning messages.\n *\n * @param message - Warning message\n * @param context - Optional context identifier\n */\nexport function logWarn(message: string, context?: string): void {\n log('WARN', message, context);\n}\n\n/**\n * Convenience function for logging error messages.\n *\n * @param message - Error message\n * @param context - Optional context identifier\n */\nexport function logError(message: string, context?: string): void {\n log('ERROR', message, context);\n}\n", "#!/usr/bin/env node\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 * Main entry point for the Registry Launcher Worker.\n *\n * The Registry Launcher is a stdio Bus worker that:\n * 1. Fetches and parses the ACP Registry on startup\n * 2. Receives NDJSON messages from stdio Bus via stdin\n * 3. Routes messages to appropriate agent processes based on agentId\n * 4. Forwards agent responses back to stdout unchanged\n *\n * @module registry-launcher\n */\n\nimport { loadConfig } from './config/config.js';\nimport { loadApiKeys } from './config/api-keys.js';\nimport { RegistryFetchError, RegistryIndex, RegistryParseError } from './registry/index.js';\nimport { NDJSONHandler } from './stream/ndjson-handler.js';\nimport { AgentRuntimeManager } from './runtime/manager.js';\nimport { MessageRouter } from './router/message-router.js';\nimport { logError, logExit, logInfo } from './log.js';\n\n/**\n * Exit codes for the Registry Launcher.\n */\nconst ExitCodes = {\n /** Successful graceful shutdown */\n SUCCESS: 0,\n /** Fatal error during startup or operation */\n FATAL_ERROR: 1,\n} as const;\n\n/**\n * Flag to track if shutdown is in progress.\n */\nlet isShuttingDown = false;\n\n/**\n * Parse command-line arguments to get the config file path.\n *\n * Usage: node index.js [config-path]\n *\n * @returns The config file path or undefined if not provided\n */\nfunction parseArgs(): string | undefined {\n // argv[0] is node, argv[1] is the script path\n // argv[2] is the first argument (config path)\n const args = process.argv.slice(2);\n\n if (args.length > 0 && args[0] && !args[0].startsWith('-')) {\n return args[0];\n }\n\n return undefined;\n}\n\n/**\n * Set up SIGTERM handler for graceful shutdown.\n *\n * @param runtimeManager - The runtime manager to terminate agents\n * @param shutdownTimeoutMs - Timeout in milliseconds for graceful shutdown\n * @returns A function to trigger shutdown programmatically\n */\nfunction setupSignalHandlers(\n runtimeManager: AgentRuntimeManager,\n shutdownTimeoutMs: number,\n): () => Promise<void> {\n const shutdown = async (): Promise<void> => {\n if (isShuttingDown) {\n return;\n }\n isShuttingDown = true;\n\n logInfo('Received shutdown signal, initiating graceful shutdown');\n\n try {\n // Terminate all agent processes\n await runtimeManager.terminateAll(shutdownTimeoutMs);\n logInfo('All agent processes terminated');\n\n // Exit with success code\n process.exit(ExitCodes.SUCCESS);\n } catch (error) {\n logError(`Error during shutdown: ${(error as Error).message}`);\n process.exit(ExitCodes.FATAL_ERROR);\n }\n };\n\n // Handle SIGTERM\n process.on('SIGTERM', () => {\n void shutdown();\n });\n\n // Also handle SIGINT for development convenience\n process.on('SIGINT', () => {\n void shutdown();\n });\n\n return shutdown;\n}\n\n/**\n * Set up stdin NDJSON handler and wire up message routing.\n *\n * @param router - The message router for handling incoming messages\n * @param ndjsonHandler - The NDJSON handler for stdin/stdout\n */\nfunction setupStdinHandler(router: MessageRouter, ndjsonHandler: NDJSONHandler): void {\n // Handle parsed messages from stdin\n ndjsonHandler.onMessage(async (message: object) => {\n try {\n const errorResponse = await router.route(message);\n\n // If routing returned an error, write it to stdout\n if (errorResponse) {\n ndjsonHandler.write(errorResponse);\n }\n } catch (error) {\n logError(`Unexpected error routing message: ${(error as Error).message}`);\n }\n });\n\n // Handle parse errors\n ndjsonHandler.onError((error: Error, line: string) => {\n logError(`Failed to parse NDJSON: ${error.message} - Line: ${line.slice(0, 100)}`);\n });\n\n // Process stdin data\n process.stdin.setEncoding('utf8');\n process.stdin.on('data', (chunk: Buffer | string) => {\n const buffer = typeof chunk === 'string' ? Buffer.from(chunk) : chunk;\n ndjsonHandler.processChunk(buffer);\n });\n\n // Handle stdin close\n process.stdin.on('end', () => {\n logInfo('stdin closed');\n });\n\n // Handle stdin errors\n process.stdin.on('error', (error: Error) => {\n logError(`stdin error: ${error.message}`);\n });\n}\n\n/**\n * Set up agent response handling.\n *\n * Wires up agent stdout to the message router for forwarding responses.\n *\n * @param runtimeManager - The runtime manager\n * @param router - The message router\n */\nfunction setupAgentResponseHandling(\n runtimeManager: AgentRuntimeManager,\n router: MessageRouter,\n): void {\n // Handle agent exit events\n runtimeManager.onAgentExit((agentId: string, code: number | null) => {\n logExit(agentId, code);\n });\n\n // Hook into runtime manager to set up stdout handling for new agents\n const originalGetOrSpawn = runtimeManager.getOrSpawn.bind(runtimeManager);\n runtimeManager.getOrSpawn = async function (agentId: string, spawnCommand: import('./registry/types.js').SpawnCommand) {\n const runtime = await originalGetOrSpawn(agentId, spawnCommand);\n\n // Set up stdout handler for this agent if not already done\n const proc = runtime.process;\n if (proc.stdout && !proc.stdout.listenerCount('data')) {\n let buffer = '';\n\n proc.stdout.on('data', (chunk: string) => {\n buffer += chunk;\n\n // Process complete NDJSON lines\n let newlineIndex;\n while ((newlineIndex = buffer.indexOf('\\n')) !== -1) {\n const line = buffer.slice(0, newlineIndex);\n buffer = buffer.slice(newlineIndex + 1);\n\n if (line.trim()) {\n try {\n const response = JSON.parse(line);\n router.handleAgentResponse(agentId, response);\n } catch (err) {\n logError(`Failed to parse agent ${agentId} response: ${(err as Error).message}`);\n }\n }\n }\n });\n }\n\n // Log stderr from agent\n if (proc.stderr && !proc.stderr.listenerCount('data')) {\n proc.stderr.on('data', (chunk: string) => {\n // Log agent stderr to our stderr\n process.stderr.write(`[agent:${agentId}] ${chunk}`);\n });\n }\n\n return runtime;\n };\n}\n\n/**\n * Main entry point for the Registry Launcher.\n *\n * 1. Load configuration from command-line argument\n * 2. Fetch and parse registry on startup\n * 3. Set up stdin NDJSON handler\n * 4. Wire up message router with registry and runtime manager\n * 5. Handle SIGTERM for graceful shutdown\n * 6. Exit with appropriate codes\n */\nasync function main(): Promise<void> {\n logInfo('Registry Launcher starting');\n\n // Parse command-line arguments\n const configPath = parseArgs();\n if (configPath) {\n logInfo(`Loading configuration from: ${configPath}`);\n }\n\n // Load configuration\n const config = loadConfig(configPath);\n logInfo(`Configuration loaded: registryUrl=${config.registryUrl}, apiKeysPath=${config.apiKeysPath}, shutdownTimeoutSec=${config.shutdownTimeoutSec}`);\n\n // Load API keys\n const apiKeys = loadApiKeys(config.apiKeysPath);\n\n // Create registry index\n const registry = new RegistryIndex(config.registryUrl);\n\n // Fetch and parse registry on startup\n try {\n await registry.fetch();\n } catch (error) {\n if (error instanceof RegistryFetchError) {\n logError(`Failed to fetch registry: ${error.message}`);\n process.exit(ExitCodes.FATAL_ERROR);\n }\n if (error instanceof RegistryParseError) {\n logError(`Failed to parse registry: ${error.message}`);\n process.exit(ExitCodes.FATAL_ERROR);\n }\n logError(`Unexpected error fetching registry: ${(error as Error).message}`);\n process.exit(ExitCodes.FATAL_ERROR);\n }\n\n // Create runtime manager\n const runtimeManager = new AgentRuntimeManager();\n\n // Create NDJSON handler for stdin/stdout\n const ndjsonHandler = new NDJSONHandler(process.stdout);\n\n // Create message router\n const router = new MessageRouter(\n registry,\n runtimeManager,\n (message: object) => ndjsonHandler.write(message),\n apiKeys,\n );\n\n // Set up signal handlers for graceful shutdown\n const shutdownTimeoutMs = config.shutdownTimeoutSec * 1000;\n setupSignalHandlers(runtimeManager, shutdownTimeoutMs);\n\n // Set up agent response handling\n setupAgentResponseHandling(runtimeManager, router);\n\n // Set up stdin handler and wire up routing\n setupStdinHandler(router, ndjsonHandler);\n\n logInfo('Registry Launcher ready, waiting for messages');\n}\n\n// Run main and handle any uncaught errors\nmain().catch((error: Error) => {\n logError(`Fatal error: ${error.message}`);\n process.exit(ExitCodes.FATAL_ERROR);\n});\n", "#!/usr/bin/env node\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 Launcher worker entrypoint.\n *\n * Ensures a default config file is passed when none is provided,\n * then loads the Registry Launcher implementation.\n */\n\nimport { fileURLToPath } from 'url';\nimport { dirname, join } from 'path';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\nconst DEFAULT_CONFIG_FILE = 'acp-registry-config.json';\n\nfunction ensureDefaultConfigArg() {\n const hasExplicitConfig = process.argv.length > 2 && process.argv[2] && !process.argv[2].startsWith('-');\n if (hasExplicitConfig) {\n return;\n }\n\n const defaultConfigPath = join(__dirname, DEFAULT_CONFIG_FILE);\n process.argv.splice(2, 0, defaultConfigPath);\n}\n\nensureDefaultConfigArg();\n\n// Load the real Registry Launcher implementation\nawait import('../acp-worker/src/registry-launcher/index.ts');\n"],
5
+ "mappings": "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",
6
+ "names": ["readFileSync", "logWarning", "logInfo", "isNonEmptyString", "ENV_REGISTRY_URL", "logError", "process", "resolve", "logError", "logInfo", "logInfo", "logError", "logInfo", "logError"]
7
+ }
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import{Readable,Writable}from"node:stream";import{AgentSideConnection,ndJsonStream}from"@agentclientprotocol/sdk";import{PROTOCOL_VERSION}from"@agentclientprotocol/sdk";import{Client}from"@modelcontextprotocol/sdk/client/index.js";import{StdioClientTransport}from"@modelcontextprotocol/sdk/client/stdio.js";var defaultFactories={createClient:options=>new Client(options),createTransport:options=>new StdioClientTransport(options)};var MCPManager=class{connections=new Map;toolToServer=new Map;onServerCrash;factories;constructor(factories){this.factories={...defaultFactories,...factories}}setOnServerCrash(callback){this.onServerCrash=callback}async connect(servers){for(const serverConfig of servers){try{const transport=this.factories.createTransport({command:serverConfig.command,args:serverConfig.args,env:serverConfig.env});const client=this.factories.createClient({name:"stdio-bus-worker",version:"1.0.0"});await client.connect(transport);const capabilities=client.getServerCapabilities();const connection2={client,transport,config:serverConfig,connected:true,capabilities};this.connections.set(serverConfig.id,connection2);this.setupCrashDetection(serverConfig.id,client);console.error(`[MCP] Connected to server: ${serverConfig.id}`)}catch(error){console.error(`[MCP] Failed to connect to server ${serverConfig.id}:`,error)}}}setupCrashDetection(serverId,client){client.onclose=()=>{const connection2=this.connections.get(serverId);if(connection2&&connection2.connected){connection2.connected=false;connection2.crashError="Server process exited unexpectedly";for(const[toolName,toolServerId]of this.toolToServer.entries()){if(toolServerId===serverId){this.toolToServer.delete(toolName)}}console.error(`[MCP] Server ${serverId} crashed: ${connection2.crashError}`);if(this.onServerCrash){this.onServerCrash(serverId,connection2.crashError)}}}}async listTools(){const allTools=[];this.toolToServer.clear();for(const[serverId,connection2]of this.connections){if(!connection2.connected){continue}try{let cursor;do{const result=await connection2.client.listTools(cursor?{cursor}:void 0);for(const tool of result.tools){allTools.push({name:tool.name,description:tool.description,inputSchema:tool.inputSchema,serverId});this.toolToServer.set(tool.name,serverId)}cursor=result.nextCursor}while(cursor)}catch(error){console.error(`[MCP] Failed to list tools from server ${serverId}:`,error)}}return allTools}async callTool(name,args,serverId){const targetServerId=serverId??this.toolToServer.get(name);if(!targetServerId){throw new Error(`Tool "${name}" not found. Call listTools() first to discover available tools.`)}const connection2=this.connections.get(targetServerId);if(!connection2){throw new Error(`Server "${targetServerId}" not found.`)}if(!connection2.connected){const crashMessage=connection2.crashError||"Server is not connected";throw new Error(`Server "${targetServerId}" is unavailable: ${crashMessage}`)}try{const result=await connection2.client.callTool({name,arguments:args});const content=result.content.map(item=>{if(item.type==="text"){return{type:"text",text:item.text}}else if(item.type==="image"){return{type:"image",data:item.data,mimeType:item.mimeType}}else if(item.type==="resource"){const resource=item.resource;return{type:"resource",resource:{uri:resource.uri,mimeType:resource.mimeType,text:resource.text,blob:resource.blob}}}return{type:"text",text:JSON.stringify(item)}});return{content,isError:result.isError===true}}catch(error){console.error(`[MCP] Failed to call tool "${name}" on server ${targetServerId}:`,error);throw error}}async listResources(){const allResources=[];for(const[serverId,connection2]of this.connections){if(!connection2.connected){continue}try{let cursor;do{const result=await connection2.client.listResources(cursor?{cursor}:void 0);for(const resource of result.resources){allResources.push({uri:resource.uri,name:resource.name,description:resource.description,mimeType:resource.mimeType,serverId})}cursor=result.nextCursor}while(cursor)}catch(error){console.error(`[MCP] Failed to list resources from server ${serverId}:`,error)}}return allResources}async readResource(uri,serverId){let targetServerId=serverId;if(!targetServerId){for(const[,connection3]of this.connections){if(!connection3.connected){continue}try{const result=await connection3.client.readResource({uri});const contents=result.contents.map(item=>{const resourceItem=item;if("text"in resourceItem&&resourceItem.text!==void 0){return{uri:resourceItem.uri,mimeType:resourceItem.mimeType,text:resourceItem.text}}else if("blob"in resourceItem&&resourceItem.blob!==void 0){return{uri:resourceItem.uri,mimeType:resourceItem.mimeType,blob:resourceItem.blob}}return{uri:resourceItem.uri,mimeType:resourceItem.mimeType,text:""}});return{contents}}catch{continue}}throw new Error(`Resource "${uri}" not found on any connected server.`)}const connection2=this.connections.get(targetServerId);if(!connection2){throw new Error(`Server "${targetServerId}" not found.`)}if(!connection2.connected){throw new Error(`Server "${targetServerId}" is not connected.`)}try{const result=await connection2.client.readResource({uri});const contents=result.contents.map(item=>{const resourceItem=item;if("text"in resourceItem&&resourceItem.text!==void 0){return{uri:resourceItem.uri,mimeType:resourceItem.mimeType,text:resourceItem.text}}else if("blob"in resourceItem&&resourceItem.blob!==void 0){return{uri:resourceItem.uri,mimeType:resourceItem.mimeType,blob:resourceItem.blob}}return{uri:resourceItem.uri,mimeType:resourceItem.mimeType,text:""}});return{contents}}catch(error){console.error(`[MCP] Failed to read resource "${uri}" from server ${targetServerId}:`,error);throw error}}getConnection(serverId){return this.connections.get(serverId)}getAllConnections(){return Array.from(this.connections.values()).filter(conn=>conn.connected)}getServerCapabilities(serverId){const connection2=this.connections.get(serverId);return connection2?.connected?connection2.capabilities:void 0}async close(){for(const connection2 of this.connections.values()){try{await connection2.client.close();connection2.connected=false}catch(error){console.error(`[MCP] Error closing connection ${connection2.config.id}:`,error)}}this.connections.clear();this.toolToServer.clear()}abortPendingOperations(){for(const connection2 of this.connections.values()){connection2.connected=false}}isServerCrashed(serverId){const connection2=this.connections.get(serverId);return connection2!==void 0&&!connection2.connected&&connection2.crashError!==void 0}getServerCrashError(serverId){const connection2=this.connections.get(serverId);return connection2?.crashError}getCrashedServers(){const crashed=[];for(const[serverId,connection2]of this.connections){if(!connection2.connected&&connection2.crashError){crashed.push({serverId,error:connection2.crashError})}}return crashed}};var defaultMcpManagerFactory=()=>new MCPManager;var Session=class{id;cwd;mcpManager;cancelled=false;createdAt;history=[];constructor(id,cwd,mcpManagerFactory){this.id=id;this.cwd=cwd;this.mcpManager=(mcpManagerFactory??defaultMcpManagerFactory)();this.createdAt=new Date}isCancelled(){return this.cancelled}cancel(){this.cancelled=true;this.mcpManager.abortPendingOperations()}addHistoryEntry(role,content){this.history.push({role,content,timestamp:new Date})}getHistory(){return[...this.history]}clearHistory(){this.history=[]}getState(){return{id:this.id,cwd:this.cwd,cancelled:this.cancelled,createdAt:this.createdAt,history:[...this.history]}}async close(){await this.mcpManager.close()}};var SessionManager=class{sessions=new Map;mcpManagerFactory;constructor(mcpManagerFactory){this.mcpManagerFactory=mcpManagerFactory}async createSession(cwd,mcpServers){const id=this.generateSessionId();const session=new Session(id,cwd,this.mcpManagerFactory);if(mcpServers&&mcpServers.length>0){await session.mcpManager.connect(mcpServers)}this.sessions.set(id,session);return session}getSession(id){return this.sessions.get(id)}cancelSession(id){const session=this.sessions.get(id);if(session){session.cancel();return true}return false}async closeSession(id){const session=this.sessions.get(id);if(session){await session.close();this.sessions.delete(id);return true}return false}async closeAll(){for(const session of this.sessions.values()){await session.close()}this.sessions.clear()}getAllSessions(){return Array.from(this.sessions.values())}removeSession(id){return this.sessions.delete(id)}generateSessionId(){return crypto.randomUUID()}};var ACPAgent=class{_connection;_sessionManager;_clientCapabilities=null;constructor(connection2){this._connection=connection2;this._sessionManager=new SessionManager}get connection(){return this._connection}get sessionManager(){return this._sessionManager}get clientCapabilities(){return this._clientCapabilities}async initialize(params){this._clientCapabilities=params.clientCapabilities??null;return{protocolVersion:PROTOCOL_VERSION,agentInfo:{name:"stdio-bus-worker",version:"1.0.0"},agentCapabilities:{promptCapabilities:{embeddedContext:true}},authMethods:[]}}async newSession(params){const mcpServers=params.mcpServers?.map(server=>{if("command"in server){return{id:server.name,command:server.command,args:server.args,env:server.env?.reduce((acc,envVar)=>{acc[envVar.name]=envVar.value;return acc},{})}}return null}).filter(s=>s!==null);const session=await this._sessionManager.createSession(params.cwd,mcpServers);return{sessionId:session.id}}async loadSession(_params){return{}}async authenticate(_params){}async prompt(params){const session=this._sessionManager.getSession(params.sessionId);if(!session){throw new Error(`Session not found: ${params.sessionId}`)}if(session.isCancelled()){return{stopReason:"cancelled"}}for(const block of params.prompt){if(session.isCancelled()){return{stopReason:"cancelled"}}if(block.type==="text"){await this._connection.sessionUpdate({sessionId:params.sessionId,update:{sessionUpdate:"agent_message_chunk",content:{type:"text",text:block.text}}})}else if(block.type==="resource_link"){const resourceLink=block;try{const result=await session.mcpManager.readResource(resourceLink.uri);if(result.contents.length>0){const content=result.contents[0];if("text"in content){await this._connection.sessionUpdate({sessionId:params.sessionId,update:{sessionUpdate:"agent_message_chunk",content:{type:"text",text:`[Resource: ${resourceLink.name}]
2
+ import{Readable,Writable,Transform}from"node:stream";import{AgentSideConnection,ndJsonStream}from"@agentclientprotocol/sdk";import{PROTOCOL_VERSION}from"@agentclientprotocol/sdk";import{Client}from"@modelcontextprotocol/sdk/client/index.js";import{StdioClientTransport}from"@modelcontextprotocol/sdk/client/stdio.js";var defaultFactories={createClient:options=>new Client(options),createTransport:options=>new StdioClientTransport(options)};var MCPManager=class{connections=new Map;toolToServer=new Map;onServerCrash;factories;constructor(factories){this.factories={...defaultFactories,...factories}}setOnServerCrash(callback){this.onServerCrash=callback}async connect(servers){for(const serverConfig of servers){try{const transport=this.factories.createTransport({command:serverConfig.command,args:serverConfig.args,env:serverConfig.env});const client=this.factories.createClient({name:"stdio-bus-worker",version:"1.0.0"});await client.connect(transport);const capabilities=client.getServerCapabilities();const connection2={client,transport,config:serverConfig,connected:true,capabilities};this.connections.set(serverConfig.id,connection2);this.setupCrashDetection(serverConfig.id,client);console.error(`[MCP] Connected to server: ${serverConfig.id}`)}catch(error){console.error(`[MCP] Failed to connect to server ${serverConfig.id}:`,error)}}}setupCrashDetection(serverId,client){client.onclose=()=>{const connection2=this.connections.get(serverId);if(connection2&&connection2.connected){connection2.connected=false;connection2.crashError="Server process exited unexpectedly";for(const[toolName,toolServerId]of this.toolToServer.entries()){if(toolServerId===serverId){this.toolToServer.delete(toolName)}}console.error(`[MCP] Server ${serverId} crashed: ${connection2.crashError}`);if(this.onServerCrash){this.onServerCrash(serverId,connection2.crashError)}}}}async listTools(){const allTools=[];this.toolToServer.clear();for(const[serverId,connection2]of this.connections){if(!connection2.connected){continue}try{let cursor;do{const result=await connection2.client.listTools(cursor?{cursor}:void 0);for(const tool of result.tools){allTools.push({name:tool.name,description:tool.description,inputSchema:tool.inputSchema,serverId});this.toolToServer.set(tool.name,serverId)}cursor=result.nextCursor}while(cursor)}catch(error){console.error(`[MCP] Failed to list tools from server ${serverId}:`,error)}}return allTools}async callTool(name,args,serverId){const targetServerId=serverId??this.toolToServer.get(name);if(!targetServerId){throw new Error(`Tool "${name}" not found. Call listTools() first to discover available tools.`)}const connection2=this.connections.get(targetServerId);if(!connection2){throw new Error(`Server "${targetServerId}" not found.`)}if(!connection2.connected){const crashMessage=connection2.crashError||"Server is not connected";throw new Error(`Server "${targetServerId}" is unavailable: ${crashMessage}`)}try{const result=await connection2.client.callTool({name,arguments:args});const content=result.content.map(item=>{if(item.type==="text"){return{type:"text",text:item.text}}else if(item.type==="image"){return{type:"image",data:item.data,mimeType:item.mimeType}}else if(item.type==="resource"){const resource=item.resource;return{type:"resource",resource:{uri:resource.uri,mimeType:resource.mimeType,text:resource.text,blob:resource.blob}}}return{type:"text",text:JSON.stringify(item)}});return{content,isError:result.isError===true}}catch(error){console.error(`[MCP] Failed to call tool "${name}" on server ${targetServerId}:`,error);throw error}}async listResources(){const allResources=[];for(const[serverId,connection2]of this.connections){if(!connection2.connected){continue}try{let cursor;do{const result=await connection2.client.listResources(cursor?{cursor}:void 0);for(const resource of result.resources){allResources.push({uri:resource.uri,name:resource.name,description:resource.description,mimeType:resource.mimeType,serverId})}cursor=result.nextCursor}while(cursor)}catch(error){console.error(`[MCP] Failed to list resources from server ${serverId}:`,error)}}return allResources}async readResource(uri,serverId){let targetServerId=serverId;if(!targetServerId){for(const[,connection3]of this.connections){if(!connection3.connected){continue}try{const result=await connection3.client.readResource({uri});const contents=result.contents.map(item=>{const resourceItem=item;if("text"in resourceItem&&resourceItem.text!==void 0){return{uri:resourceItem.uri,mimeType:resourceItem.mimeType,text:resourceItem.text}}else if("blob"in resourceItem&&resourceItem.blob!==void 0){return{uri:resourceItem.uri,mimeType:resourceItem.mimeType,blob:resourceItem.blob}}return{uri:resourceItem.uri,mimeType:resourceItem.mimeType,text:""}});return{contents}}catch{continue}}throw new Error(`Resource "${uri}" not found on any connected server.`)}const connection2=this.connections.get(targetServerId);if(!connection2){throw new Error(`Server "${targetServerId}" not found.`)}if(!connection2.connected){throw new Error(`Server "${targetServerId}" is not connected.`)}try{const result=await connection2.client.readResource({uri});const contents=result.contents.map(item=>{const resourceItem=item;if("text"in resourceItem&&resourceItem.text!==void 0){return{uri:resourceItem.uri,mimeType:resourceItem.mimeType,text:resourceItem.text}}else if("blob"in resourceItem&&resourceItem.blob!==void 0){return{uri:resourceItem.uri,mimeType:resourceItem.mimeType,blob:resourceItem.blob}}return{uri:resourceItem.uri,mimeType:resourceItem.mimeType,text:""}});return{contents}}catch(error){console.error(`[MCP] Failed to read resource "${uri}" from server ${targetServerId}:`,error);throw error}}getConnection(serverId){return this.connections.get(serverId)}getAllConnections(){return Array.from(this.connections.values()).filter(conn=>conn.connected)}getServerCapabilities(serverId){const connection2=this.connections.get(serverId);return connection2?.connected?connection2.capabilities:void 0}async close(){for(const connection2 of this.connections.values()){try{await connection2.client.close();connection2.connected=false}catch(error){console.error(`[MCP] Error closing connection ${connection2.config.id}:`,error)}}this.connections.clear();this.toolToServer.clear()}abortPendingOperations(){for(const connection2 of this.connections.values()){connection2.connected=false}}isServerCrashed(serverId){const connection2=this.connections.get(serverId);return connection2!==void 0&&!connection2.connected&&connection2.crashError!==void 0}getServerCrashError(serverId){const connection2=this.connections.get(serverId);return connection2?.crashError}getCrashedServers(){const crashed=[];for(const[serverId,connection2]of this.connections){if(!connection2.connected&&connection2.crashError){crashed.push({serverId,error:connection2.crashError})}}return crashed}};var defaultMcpManagerFactory=()=>new MCPManager;var Session=class{id;cwd;mcpManager;cancelled=false;createdAt;history=[];constructor(id,cwd,mcpManagerFactory){this.id=id;this.cwd=cwd;this.mcpManager=(mcpManagerFactory??defaultMcpManagerFactory)();this.createdAt=new Date}isCancelled(){return this.cancelled}cancel(){this.cancelled=true;this.mcpManager.abortPendingOperations()}addHistoryEntry(role,content){this.history.push({role,content,timestamp:new Date})}getHistory(){return[...this.history]}clearHistory(){this.history=[]}getState(){return{id:this.id,cwd:this.cwd,cancelled:this.cancelled,createdAt:this.createdAt,history:[...this.history]}}async close(){await this.mcpManager.close()}};var SessionManager=class{sessions=new Map;mcpManagerFactory;constructor(mcpManagerFactory){this.mcpManagerFactory=mcpManagerFactory}async createSession(cwd,mcpServers){const id=this.generateSessionId();const session=new Session(id,cwd,this.mcpManagerFactory);if(mcpServers&&mcpServers.length>0){await session.mcpManager.connect(mcpServers)}this.sessions.set(id,session);return session}getSession(id){return this.sessions.get(id)}cancelSession(id){const session=this.sessions.get(id);if(session){session.cancel();return true}return false}async closeSession(id){const session=this.sessions.get(id);if(session){await session.close();this.sessions.delete(id);return true}return false}async closeAll(){for(const session of this.sessions.values()){await session.close()}this.sessions.clear()}getAllSessions(){return Array.from(this.sessions.values())}removeSession(id){return this.sessions.delete(id)}generateSessionId(){return crypto.randomUUID()}};var ACPAgent=class{_connection;_sessionManager;_clientCapabilities=null;constructor(connection2){this._connection=connection2;this._sessionManager=new SessionManager}get connection(){return this._connection}get sessionManager(){return this._sessionManager}get clientCapabilities(){return this._clientCapabilities}async initialize(params){this._clientCapabilities=params.clientCapabilities??null;return{protocolVersion:PROTOCOL_VERSION,agentInfo:{name:"stdio-bus-worker",version:"1.0.0"},agentCapabilities:{promptCapabilities:{embeddedContext:true}},authMethods:[]}}async newSession(params){const mcpServers=params.mcpServers?.map(server=>{if("command"in server){return{id:server.name,command:server.command,args:server.args,env:server.env?.reduce((acc,envVar)=>{acc[envVar.name]=envVar.value;return acc},{})}}return null}).filter(s=>s!==null);const session=await this._sessionManager.createSession(params.cwd,mcpServers);return{sessionId:session.id}}async loadSession(_params){return{}}async authenticate(_params){}async prompt(params){const session=this._sessionManager.getSession(params.sessionId);if(!session){throw new Error(`Session not found: ${params.sessionId}`)}if(session.isCancelled()){return{stopReason:"cancelled"}}for(const block of params.prompt){if(session.isCancelled()){return{stopReason:"cancelled"}}if(block.type==="text"){await this._connection.sessionUpdate({sessionId:params.sessionId,update:{sessionUpdate:"agent_message_chunk",content:{type:"text",text:block.text}}})}else if(block.type==="resource_link"){const resourceLink=block;try{const result=await session.mcpManager.readResource(resourceLink.uri);if(result.contents.length>0){const content=result.contents[0];if("text"in content){await this._connection.sessionUpdate({sessionId:params.sessionId,update:{sessionUpdate:"agent_message_chunk",content:{type:"text",text:`[Resource: ${resourceLink.name}]
3
3
  ${content.text}`}}})}else if("blob"in content){await this._connection.sessionUpdate({sessionId:params.sessionId,update:{sessionUpdate:"agent_message_chunk",content:{type:"text",text:`[Resource: ${resourceLink.name}] (binary data, ${content.blob.length} bytes)`}}})}}}catch{await this._connection.sessionUpdate({sessionId:params.sessionId,update:{sessionUpdate:"agent_message_chunk",content:{type:"text",text:`[Resource link: ${resourceLink.name} (${resourceLink.uri})]`}}})}}else if(block.type==="resource"){const resource=block;if(resource.resource.text!==void 0){await this._connection.sessionUpdate({sessionId:params.sessionId,update:{sessionUpdate:"agent_message_chunk",content:{type:"text",text:`[Embedded resource: ${resource.resource.uri}]
4
- ${resource.resource.text}`}}})}else if(resource.resource.blob!==void 0){await this._connection.sessionUpdate({sessionId:params.sessionId,update:{sessionUpdate:"agent_message_chunk",content:{type:"text",text:`[Embedded resource: ${resource.resource.uri}] (binary data)`}}})}}else if(block.type==="image"){const image=block;await this._connection.sessionUpdate({sessionId:params.sessionId,update:{sessionUpdate:"agent_message_chunk",content:{type:"text",text:`[Image: ${image.mimeType}]`}}})}}if(session.isCancelled()){return{stopReason:"cancelled"}}return{stopReason:"end_turn"}}async cancel(params){this._sessionManager.cancelSession(params.sessionId)}};var PlatformNotSupportedError=class extends Error{constructor(agentId,platform){super(`Platform not supported: ${platform} for agent ${agentId}`);this.agentId=agentId;this.platform=platform;this.name="PlatformNotSupportedError"}};var NoDistributionError=class extends Error{constructor(agentId){super(`No supported distribution type for agent ${agentId}`);this.agentId=agentId;this.name="NoDistributionError"}};function getCurrentPlatform(){const platform=process.platform;const arch=process.arch;if(platform==="darwin"&&arch==="arm64")return"darwin-aarch64";if(platform==="darwin"&&arch==="x64")return"darwin-x86_64";if(platform==="linux"&&arch==="arm64")return"linux-aarch64";if(platform==="linux"&&arch==="x64")return"linux-x86_64";if(platform==="win32"&&arch==="arm64")return"windows-aarch64";if(platform==="win32"&&arch==="x64")return"windows-x86_64";return"linux-x86_64"}function resolveBinary(distribution,agentId){const currentPlatform=getCurrentPlatform();const target=distribution[currentPlatform];if(!target){throw new PlatformNotSupportedError(agentId,currentPlatform)}return{command:target.cmd,args:target.args??[],env:target.env}}function resolveNpx(distribution){return{command:"npx",args:[distribution.package,...distribution.args??[]],env:distribution.env}}function resolveUvx(distribution){return{command:"uvx",args:[distribution.package,...distribution.args??[]],env:distribution.env}}function resolve(distribution,agentId){if(distribution.npx){return resolveNpx(distribution.npx)}if(distribution.uvx){return resolveUvx(distribution.uvx)}if(distribution.binary){return resolveBinary(distribution.binary,agentId)}throw new NoDistributionError(agentId)}import{spawn}from"child_process";var DEFAULT_TERMINATE_TIMEOUT_MS=5e3;var AgentRuntimeImpl=class _AgentRuntimeImpl{agentId;state;process;onExitCallback;constructor(agentId,process2,onExit){this.agentId=agentId;this.process=process2;this.state="starting";this.onExitCallback=onExit;this.setupProcessHandlers()}static spawn(agentId,spawnCommand,onExit){const childProcess=spawn(spawnCommand.command,spawnCommand.args,{stdio:["pipe","pipe","pipe"],env:{...process.env,...spawnCommand.env},detached:false});if(childProcess.stdout){childProcess.stdout.setEncoding("utf8")}if(childProcess.stderr){childProcess.stderr.setEncoding("utf8")}return new _AgentRuntimeImpl(agentId,childProcess,onExit)}setupProcessHandlers(){this.process.on("spawn",()=>{if(this.state==="starting"){this.state="running"}});this.process.on("error",error=>{this.state="stopped";process.stderr.write(`[${new Date().toISOString()}] ERROR: Agent ${this.agentId} process error: ${error.message}
4
+ ${resource.resource.text}`}}})}else if(resource.resource.blob!==void 0){await this._connection.sessionUpdate({sessionId:params.sessionId,update:{sessionUpdate:"agent_message_chunk",content:{type:"text",text:`[Embedded resource: ${resource.resource.uri}] (binary data)`}}})}}else if(block.type==="image"){const image=block;await this._connection.sessionUpdate({sessionId:params.sessionId,update:{sessionUpdate:"agent_message_chunk",content:{type:"text",text:`[Image: ${image.mimeType}]`}}})}}if(session.isCancelled()){return{stopReason:"cancelled"}}return{stopReason:"end_turn"}}async cancel(params){this._sessionManager.cancelSession(params.sessionId)}};var SessionIdRouter=class{requestSessionIdMap=new Map;acpSessionIdMap=new Map;processIncomingLine(line){if(!line.trim()){return line}try{const msg=JSON.parse(line);const routingSessionId=this.readSessionId(msg.sessionId);const hasId=msg.id!==void 0&&msg.id!==null;if(hasId&&routingSessionId){this.requestSessionIdMap.set(msg.id,routingSessionId);console.error(`[worker] Saved sessionId="${routingSessionId}" for request id=${msg.id}`)}const paramsSessionId=this.readSessionId(msg.params?.sessionId);if(routingSessionId&&paramsSessionId){this.setAcpSessionMapping(paramsSessionId,routingSessionId,"request")}if(hasId&&routingSessionId){const{sessionId,...msgWithoutSession}=msg;return JSON.stringify(msgWithoutSession)}return line}catch{return line}}processOutgoingLine(line){if(!line.trim()){return line}try{const msg=JSON.parse(line);const hasId=msg.id!==void 0&&msg.id!==null;if(hasId&&this.requestSessionIdMap.has(msg.id)){const routingSessionId=this.requestSessionIdMap.get(msg.id);this.requestSessionIdMap.delete(msg.id);if(routingSessionId){const resultSessionId=this.readSessionId(msg.result?.sessionId);if(resultSessionId){this.setAcpSessionMapping(resultSessionId,routingSessionId,"response")}const msgWithSession={...msg,sessionId:routingSessionId};console.error(`[worker] Restored sessionId="${routingSessionId}" for response id=${msg.id}`);return JSON.stringify(msgWithSession)}}if(!hasId&&!this.readSessionId(msg.sessionId)){const paramsSessionId=this.readSessionId(msg.params?.sessionId);if(paramsSessionId){const routingSessionId=this.acpSessionIdMap.get(paramsSessionId);if(routingSessionId){const msgWithSession={...msg,sessionId:routingSessionId};return JSON.stringify(msgWithSession)}}}return line}catch{return line}}readSessionId(value){return typeof value==="string"&&value.length>0?value:null}setAcpSessionMapping(acpSessionId,routingSessionId,source){const existing=this.acpSessionIdMap.get(acpSessionId);if(existing===routingSessionId){return}this.acpSessionIdMap.set(acpSessionId,routingSessionId);console.error(`[worker] Mapped ACP sessionId="${acpSessionId}" to routing sessionId="${routingSessionId}" (${source})`)}};var PlatformNotSupportedError=class extends Error{constructor(agentId,platform){super(`Platform not supported: ${platform} for agent ${agentId}`);this.agentId=agentId;this.platform=platform;this.name="PlatformNotSupportedError"}};var NoDistributionError=class extends Error{constructor(agentId){super(`No supported distribution type for agent ${agentId}`);this.agentId=agentId;this.name="NoDistributionError"}};function getCurrentPlatform(){const platform=process.platform;const arch=process.arch;if(platform==="darwin"&&arch==="arm64")return"darwin-aarch64";if(platform==="darwin"&&arch==="x64")return"darwin-x86_64";if(platform==="linux"&&arch==="arm64")return"linux-aarch64";if(platform==="linux"&&arch==="x64")return"linux-x86_64";if(platform==="win32"&&arch==="arm64")return"windows-aarch64";if(platform==="win32"&&arch==="x64")return"windows-x86_64";return"linux-x86_64"}function resolveBinary(distribution,agentId){const currentPlatform=getCurrentPlatform();const target=distribution[currentPlatform];if(!target){throw new PlatformNotSupportedError(agentId,currentPlatform)}return{command:target.cmd,args:target.args??[],env:target.env}}function resolveNpx(distribution){return{command:"npx",args:[distribution.package,...distribution.args??[]],env:distribution.env}}function resolveUvx(distribution){return{command:"uvx",args:[distribution.package,...distribution.args??[]],env:distribution.env}}function resolve(distribution,agentId){if(distribution.npx){return resolveNpx(distribution.npx)}if(distribution.uvx){return resolveUvx(distribution.uvx)}if(distribution.binary){return resolveBinary(distribution.binary,agentId)}throw new NoDistributionError(agentId)}import{spawn}from"child_process";var DEFAULT_TERMINATE_TIMEOUT_MS=5e3;var AgentRuntimeImpl=class _AgentRuntimeImpl{agentId;state;process;onExitCallback;constructor(agentId,process2,onExit){this.agentId=agentId;this.process=process2;this.state="starting";this.onExitCallback=onExit;this.setupProcessHandlers()}static spawn(agentId,spawnCommand,onExit){const childProcess=spawn(spawnCommand.command,spawnCommand.args,{stdio:["pipe","pipe","pipe"],env:{...process.env,...spawnCommand.env},detached:false});if(childProcess.stdout){childProcess.stdout.setEncoding("utf8")}if(childProcess.stderr){childProcess.stderr.setEncoding("utf8")}return new _AgentRuntimeImpl(agentId,childProcess,onExit)}setupProcessHandlers(){this.process.on("spawn",()=>{if(this.state==="starting"){this.state="running"}});this.process.on("error",error=>{this.state="stopped";process.stderr.write(`[${new Date().toISOString()}] ERROR: Agent ${this.agentId} process error: ${error.message}
5
5
  `)});this.process.on("exit",(code,signal)=>{this.state="stopped";if(this.onExitCallback){this.onExitCallback(code,signal)}});if(this.process.stdin){this.process.stdin.on("error",error=>{process.stderr.write(`[${new Date().toISOString()}] WARN: Agent ${this.agentId} stdin error: ${error.message}
6
- `)})}}write(message){if(this.state!=="running"&&this.state!=="starting"){return false}if(!this.process.stdin||this.process.stdin.destroyed){return false}try{const ndjsonLine=JSON.stringify(message)+"\n";return this.process.stdin.write(ndjsonLine)}catch{return false}}async terminate(timeout=DEFAULT_TERMINATE_TIMEOUT_MS){if(this.state==="stopped"){return}if(this.state==="stopping"){return this.waitForExit()}this.state="stopping";if(this.process.stdin&&!this.process.stdin.destroyed){this.process.stdin.end()}this.process.kill("SIGTERM");const exitPromise=this.waitForExit();const timeoutPromise=new Promise(resolve2=>{setTimeout(()=>resolve2("timeout"),timeout)});const result=await Promise.race([exitPromise,timeoutPromise]);if(result==="timeout"&&!this.process.killed&&this.process.exitCode===null){this.process.kill("SIGKILL");await this.waitForExit()}}waitForExit(){if(this.state==="stopped"){return Promise.resolve()}return new Promise(resolve2=>{this.process.once("exit",()=>{resolve2()})})}};var DEFAULT_SHUTDOWN_TIMEOUT_MS=5e3;var AgentRuntimeManager=class{runtimes=new Map;exitCallbacks=[];async getOrSpawn(agentId,spawnCommand){const existing=this.runtimes.get(agentId);if(existing&&existing.state!=="stopped"){return existing}const runtime=AgentRuntimeImpl.spawn(agentId,spawnCommand,(code,_signal)=>{this.handleAgentExit(agentId,code)});this.runtimes.set(agentId,runtime);return runtime}get(agentId){return this.runtimes.get(agentId)}async terminate(agentId,timeout=DEFAULT_SHUTDOWN_TIMEOUT_MS){const runtime=this.runtimes.get(agentId);if(!runtime){return}await runtime.terminate(timeout);this.runtimes.delete(agentId)}async terminateAll(timeout=DEFAULT_SHUTDOWN_TIMEOUT_MS){const terminatePromises=[];for(const[agentId,runtime]of this.runtimes){if(runtime.state!=="stopped"){terminatePromises.push(runtime.terminate(timeout).then(()=>{this.runtimes.delete(agentId)}))}}await Promise.all(terminatePromises)}onAgentExit(callback){this.exitCallbacks.push(callback)}handleAgentExit(agentId,code){this.runtimes.delete(agentId);for(const callback of this.exitCallbacks){try{callback(agentId,code)}catch{}}}get size(){return this.runtimes.size}has(agentId){return this.runtimes.has(agentId)}};function logError(message){const timestamp=new Date().toISOString();console.error(`[${timestamp}] [ERROR] [ndjson] ${message}`)}var NDJSONHandler=class{buffer="";output;messageCallback=null;errorCallback=null;constructor(output){this.output=output}onMessage(callback){this.messageCallback=callback}onError(callback){this.errorCallback=callback}write(message){if(!this.output.writable){return false}try{const json=JSON.stringify(message);this.output.write(json+"\n");return true}catch{return false}}processChunk(chunk){this.buffer+=chunk.toString("utf-8");this.processBuffer()}processBuffer(){let newlineIndex;while((newlineIndex=this.buffer.indexOf("\n"))!==-1){const line=this.buffer.slice(0,newlineIndex);this.buffer=this.buffer.slice(newlineIndex+1);if(line.trim().length===0){continue}this.parseLine(line)}}parseLine(line){try{const message=JSON.parse(line);if(message===null||typeof message!=="object"){const error=new Error("Parsed JSON is not an object");logError(`Malformed NDJSON line (not an object): ${this.truncateLine(line)}`);this.errorCallback?.(error,line);return}this.messageCallback?.(message)}catch(error){logError(`Failed to parse NDJSON line: ${this.truncateLine(line)}`);this.errorCallback?.(error,line)}}truncateLine(line,maxLength=100){if(line.length<=maxLength){return line}return line.slice(0,maxLength)+"..."}};var AgentNotFoundError=class extends Error{constructor(agentId){super(`Agent not found: ${agentId}`);this.agentId=agentId;this.name="AgentNotFoundError"}};function getAgentApiKey(apiKeys,agentId){const keys=apiKeys[agentId];if(!keys||!keys.apiKey||keys.apiKey.length===0){return void 0}return keys.apiKey}var RoutingErrorCodes={MISSING_AGENT_ID:-32600,AGENT_NOT_FOUND:-32001,PLATFORM_NOT_SUPPORTED:-32002,SPAWN_FAILED:-32003};function logError2(message){const timestamp=new Date().toISOString();console.error(`[${timestamp}] [ERROR] [router] ${message}`)}function logInfo(message){const timestamp=new Date().toISOString();console.error(`[${timestamp}] [INFO] [router] ${message}`)}function createErrorResponse(id,code,message,data){const response={jsonrpc:"2.0",id,error:{code,message}};if(data!==void 0){response.error.data=data}return response}function extractAgentId(message){const msg=message;const agentId=msg.agentId;if(typeof agentId==="string"&&agentId.length>0){return agentId}return void 0}function extractId(message){const msg=message;const id=msg.id;if(typeof id==="string"||typeof id==="number"){return id}return null}function transformMessage(message){const{agentId:_,...rest}=message;return rest}var MessageRouter=class{registry;runtimeManager;writeCallback;apiKeys;pendingRequests=new Map;authState=new Map;sessionIdMap=new Map;constructor(registry,runtimeManager,writeCallback,apiKeys={}){this.registry=registry;this.runtimeManager=runtimeManager;this.writeCallback=writeCallback;this.apiKeys=apiKeys}async route(message){const id=extractId(message);const agentId=extractAgentId(message);if(agentId===void 0){logError2("Missing agentId in request");return createErrorResponse(id,RoutingErrorCodes.MISSING_AGENT_ID,"Missing agentId")}let spawnCommand;try{spawnCommand=this.registry.resolve(agentId)}catch(error){if(error instanceof AgentNotFoundError){logError2(`Agent not found: ${agentId}`);return createErrorResponse(id,RoutingErrorCodes.AGENT_NOT_FOUND,"Agent not found",{agentId})}if(error instanceof PlatformNotSupportedError){logError2(`Platform not supported for agent: ${agentId}`);return createErrorResponse(id,RoutingErrorCodes.PLATFORM_NOT_SUPPORTED,"Platform not supported",{agentId,platform:error.platform})}throw error}let runtime;try{runtime=await this.runtimeManager.getOrSpawn(agentId,spawnCommand)}catch(error){logError2(`Failed to spawn agent ${agentId}: ${error.message}`);return createErrorResponse(id,RoutingErrorCodes.SPAWN_FAILED,"Agent spawn failed",{agentId,error:error.message})}if(id!==null){const msg=message;const clientSessionId=typeof msg.sessionId==="string"?msg.sessionId:void 0;this.pendingRequests.set(id,{id,agentId,timestamp:Date.now(),clientSessionId})}const transformedMessage=transformMessage(message);const success=runtime.write(transformedMessage);if(!success){logError2(`Failed to write to agent ${agentId}`);if(id!==null){this.pendingRequests.delete(id)}}else{logInfo(`Routed message to agent ${agentId}`)}return void 0}handleAgentResponse(agentId,response){const id=extractId(response);const msg=response;if(id!==null){const pending=this.pendingRequests.get(id);if(pending&&pending.agentId===agentId){const result=msg.result;if(result&&Array.isArray(result.authMethods)&&result.authMethods.length>0){logInfo(`Agent ${agentId} requires authentication, attempting auto-auth`);this.authState.set(agentId,"pending");void this.attemptAuthentication(agentId,result.authMethods)}if(result&&typeof result.sessionId==="string"){const agentSessionId=result.sessionId;const clientSessionId=pending.clientSessionId;if(clientSessionId){this.sessionIdMap.set(agentSessionId,clientSessionId);logInfo(`Mapped agent sessionId ${agentSessionId} to client sessionId ${clientSessionId}`)}}this.pendingRequests.delete(id)}}if(id===null&&msg.method){const params=msg.params;if(params&&typeof params.sessionId==="string"){const agentSessionId=params.sessionId;const clientSessionId=this.sessionIdMap.get(agentSessionId);if(clientSessionId){const enriched={...msg,sessionId:clientSessionId,params:{...params,sessionId:agentSessionId}};this.writeCallback(enriched);return}else{logError2(`No sessionId mapping found for agent sessionId: ${agentSessionId}`)}}}this.writeCallback(response)}async attemptAuthentication(agentId,authMethods){const apiKey=getAgentApiKey(this.apiKeys,agentId);if(!apiKey){logError2(`No API key found for agent ${agentId}, authentication will fail`);this.authState.set(agentId,"none");return}let selectedMethod=authMethods.find(m=>m.id==="openai-api-key");if(!selectedMethod){selectedMethod=authMethods.find(m=>m.id.includes("api-key")||m.id.includes("apikey"))}if(!selectedMethod){selectedMethod=authMethods[0]}logInfo(`Authenticating agent ${agentId} with method: ${selectedMethod.id}`);let runtime;try{const spawnCommand=this.registry.resolve(agentId);runtime=await this.runtimeManager.getOrSpawn(agentId,spawnCommand)}catch(error){logError2(`Failed to get runtime for authentication: ${error.message}`);this.authState.set(agentId,"none");return}const authRequest={jsonrpc:"2.0",id:`auth-${agentId}-${Date.now()}`,method:"authenticate",params:{methodId:selectedMethod.id,credentials:{apiKey}}};const transformed=transformMessage(authRequest);const serialized=JSON.stringify(transformed)+"\n";if(runtime.process.stdin){runtime.process.stdin.write(serialized,error=>{if(error){logError2(`Failed to send authenticate request to ${agentId}: ${error.message}`);this.authState.set(agentId,"none")}else{logInfo(`Sent authenticate request to agent ${agentId}`);this.authState.set(agentId,"authenticated")}})}}get pendingCount(){return this.pendingRequests.size}isPending(id){return this.pendingRequests.has(id)}clearPending(){this.pendingRequests.clear()}};import{readFileSync}from"node:fs";var DEFAULT_CONFIG={registryUrl:"https://cdn.agentclientprotocol.com/registry/v1/latest/registry.json",apiKeysPath:"./api-keys.json",shutdownTimeoutSec:5};var ENV_REGISTRY_URL="ACP_REGISTRY_URL";var ENV_API_KEYS_PATH="ACP_API_KEYS_PATH";function logWarning(message){const timestamp=new Date().toISOString();console.error(`[${timestamp}] [WARN] [config] ${message}`)}function isNonEmptyString(value){return typeof value==="string"&&value.length>0}function isPositiveNumber(value){return typeof value==="number"&&value>0&&Number.isFinite(value)}function parseConfigObject(obj){const config={...DEFAULT_CONFIG};if(obj===null||typeof obj!=="object"){logWarning("Config file does not contain a valid object, using defaults");return config}const rawConfig=obj;if("registryUrl"in rawConfig){if(isNonEmptyString(rawConfig.registryUrl)){config.registryUrl=rawConfig.registryUrl}else{logWarning('Config field "registryUrl" is not a valid string, using default')}}if("apiKeysPath"in rawConfig){if(isNonEmptyString(rawConfig.apiKeysPath)){config.apiKeysPath=rawConfig.apiKeysPath}else{logWarning('Config field "apiKeysPath" is not a valid string, using default')}}if("shutdownTimeoutSec"in rawConfig){if(isPositiveNumber(rawConfig.shutdownTimeoutSec)){config.shutdownTimeoutSec=rawConfig.shutdownTimeoutSec}else{logWarning('Config field "shutdownTimeoutSec" is not a valid positive number, using default')}}return config}function applyEnvironmentOverrides(config){const envRegistryUrl=process.env[ENV_REGISTRY_URL];const envApiKeysPath=process.env[ENV_API_KEYS_PATH];const overrides={};if(isNonEmptyString(envRegistryUrl)){overrides.registryUrl=envRegistryUrl}if(isNonEmptyString(envApiKeysPath)){overrides.apiKeysPath=envApiKeysPath}return{...config,...overrides}}function loadConfig(configPath){let config={...DEFAULT_CONFIG};if(configPath){try{const fileContent=readFileSync(configPath,"utf-8");const parsed=JSON.parse(fileContent);config=parseConfigObject(parsed)}catch(error){if(error instanceof SyntaxError){logWarning(`Config file "${configPath}" contains malformed JSON, using defaults`)}else if(error.code==="ENOENT"){logWarning(`Config file "${configPath}" not found, using defaults`)}else if(error.code==="EACCES"){logWarning(`Config file "${configPath}" is not readable, using defaults`)}else{logWarning(`Failed to read config file "${configPath}": ${error.message}, using defaults`)}}}config=applyEnvironmentOverrides(config);return config}function canReadFile(capabilities){return capabilities?.fs?.readTextFile===true}function canWriteFile(capabilities){return capabilities?.fs?.writeTextFile===true}function canUseTerminal(capabilities){return capabilities?.terminal===true}async function readFile(connection2,sessionId,path,options){try{const response=await connection2.readTextFile({sessionId,path,line:options?.line,limit:options?.limit});return{content:response.content,success:true}}catch(error){const errorMessage=error instanceof Error?error.message:String(error);console.error(`[ACP] Failed to read file "${path}":`,error);return{content:"",success:false,error:errorMessage}}}async function writeFile(connection2,sessionId,path,content){try{await connection2.writeTextFile({sessionId,path,content});return{success:true}}catch(error){const errorMessage=error instanceof Error?error.message:String(error);console.error(`[ACP] Failed to write file "${path}":`,error);return{success:false,error:errorMessage}}}async function executeCommand(connection2,sessionId,command,options){let terminal=null;try{terminal=await connection2.createTerminal({sessionId,command,args:options?.args,cwd:options?.cwd,env:options?.env,outputByteLimit:options?.outputByteLimit});let timeoutId=null;let timedOut=false;if(options?.timeout&&options.timeout>0){timeoutId=setTimeout(async()=>{timedOut=true;if(terminal){try{await terminal.kill()}catch{}}},options.timeout)}try{const exitResult=await terminal.waitForExit();if(timeoutId){clearTimeout(timeoutId)}const outputResult=await terminal.currentOutput();return{output:outputResult.output,exitCode:timedOut?null:exitResult.exitCode??null,signal:timedOut?"SIGTERM":exitResult.signal??null,truncated:outputResult.truncated,success:!timedOut&&exitResult.exitCode===0,error:timedOut?"Command timed out":void 0}}finally{if(timeoutId){clearTimeout(timeoutId)}}}catch(error){const errorMessage=error instanceof Error?error.message:String(error);console.error(`[ACP] Failed to execute command "${command}":`,error);return{output:"",exitCode:null,signal:null,truncated:false,success:false,error:errorMessage}}finally{if(terminal){try{await terminal.release()}catch{}}}}async function startCommand(connection2,sessionId,command,options){return connection2.createTerminal({sessionId,command,args:options?.args,cwd:options?.cwd,env:options?.env,outputByteLimit:options?.outputByteLimit})}function mapMCPContentToACPContentBlock(mcpContent){switch(mcpContent.type){case"text":return mapTextContent(mcpContent);case"image":return mapImageContent(mcpContent);case"resource":return mapEmbeddedResource(mcpContent);default:return{type:"text",text:JSON.stringify(mcpContent)}}}function mapTextContent(mcpText){const result={type:"text",text:mcpText.text};return result}function mapImageContent(mcpImage){const result={type:"image",data:mcpImage.data,mimeType:mcpImage.mimeType};return result}function mapEmbeddedResource(mcpResource){const{resource}=mcpResource;if(resource.text!==void 0){const result={type:"resource",resource:{uri:resource.uri,mimeType:resource.mimeType,text:resource.text}};return result}else if(resource.blob!==void 0){const result={type:"resource",resource:{uri:resource.uri,mimeType:resource.mimeType,blob:resource.blob}};return result}else{const result={type:"resource",resource:{uri:resource.uri,mimeType:resource.mimeType,text:""}};return result}}function mapMCPResultToACPToolCallContent(mcpContents){return mcpContents.map(mcpContent=>{const contentBlock=mapMCPContentToACPContentBlock(mcpContent);return{type:"content",content:contentBlock}})}function createErrorToolCallContent(errorMessage){return[{type:"content",content:{type:"text",text:`Error: ${errorMessage}`}}]}function mapToolResultToACPContent(content,isError=false){if(isError&&content.length>0){const errorText=content.filter(c=>c.type==="text").map(c=>c.text).join("\n");if(errorText){return createErrorToolCallContent(errorText)}}return mapMCPResultToACPToolCallContent(content)}function isResourceLink(block){return block.type==="resource_link"&&"uri"in block}function mapMCPResourceContentsToACPContentBlock(contents){if("text"in contents){const result2={type:"resource",resource:{uri:contents.uri,mimeType:contents.mimeType,text:contents.text}};return result2}const result={type:"resource",resource:{uri:contents.uri,mimeType:contents.mimeType,blob:contents.blob}};return result}function extractResourceLinkUri(block){if(isResourceLink(block)){return block.uri}return null}var toolCallCounter=0;function generateToolCallId(){toolCallCounter++;return`tool-${Date.now()}-${toolCallCounter}`}function determineToolKind(toolName,description){const name=toolName.toLowerCase();const desc=(description||"").toLowerCase();if(desc.includes("external")||desc.includes("api")||desc.includes("http")){return"fetch"}if(name.includes("read")||name.includes("get")||name.includes("list")||name.includes("fetch")){return"read"}if(name.includes("write")||name.includes("edit")||name.includes("update")||name.includes("modify")){return"edit"}if(name.includes("delete")||name.includes("remove")){return"delete"}if(name.includes("move")||name.includes("rename")){return"move"}if(name.includes("search")||name.includes("find")||name.includes("query")){return"search"}if(name.includes("exec")||name.includes("run")||name.includes("shell")||name.includes("command")){return"execute"}if(name.includes("http")||name.includes("api")||name.includes("request")){return"fetch"}return"other"}async function sendToolCallInitiation(connection2,sessionId,toolCallId,title,kind="other",status="pending"){await connection2.sessionUpdate({sessionId,update:{sessionUpdate:"tool_call",toolCallId,title,kind,status}})}async function sendToolCallUpdate(connection2,sessionId,toolCallId,status,content,title){await connection2.sessionUpdate({sessionId,update:{sessionUpdate:"tool_call_update",toolCallId,status,content,title}})}async function requestToolPermission(connection2,sessionId,toolCallId,title,kind="other",options){const permissionOptions=options??[{optionId:"allow_once",name:"Allow once",kind:"allow_once"},{optionId:"allow_always",name:"Allow always",kind:"allow_always"},{optionId:"reject_once",name:"Reject",kind:"reject_once"}];const toolCall={toolCallId,title,kind,status:"pending"};try{const response=await connection2.requestPermission({sessionId,toolCall,options:permissionOptions});if(response.outcome.outcome==="cancelled"){return{granted:false,cancelled:true}}if(response.outcome.outcome==="selected"){const selectedOption=response.outcome.optionId;const isAllowed=selectedOption.startsWith("allow");return{granted:isAllowed,optionId:selectedOption,cancelled:false}}return{granted:false,cancelled:false}}catch(error){console.error("[ACP] Permission request failed:",error);return{granted:false,cancelled:true}}}async function executeToolCall(connection2,sessionId,mcpManager,toolName,args,description){const toolCallId=generateToolCallId();const kind=determineToolKind(toolName,description);const title=`Executing: ${toolName}`;try{await sendToolCallInitiation(connection2,sessionId,toolCallId,title,kind,"pending");await sendToolCallUpdate(connection2,sessionId,toolCallId,"in_progress");const result=await mcpManager.callTool(toolName,args);const content=mapToolResultToACPContent(result.content,result.isError);const finalStatus=result.isError?"failed":"completed";await sendToolCallUpdate(connection2,sessionId,toolCallId,finalStatus,content);return content}catch(error){const errorMessage=error instanceof Error?error.message:String(error);const errorContent=[{type:"content",content:{type:"text",text:`Error: ${errorMessage}`}}];await sendToolCallUpdate(connection2,sessionId,toolCallId,"failed",errorContent);return errorContent}}async function executeToolCallWithPermission(connection2,sessionId,mcpManager,toolName,args,description,requirePermission=true){const toolCallId=generateToolCallId();const kind=determineToolKind(toolName,description);const title=`Executing: ${toolName}`;try{await sendToolCallInitiation(connection2,sessionId,toolCallId,title,kind,"pending");if(requirePermission){const permissionResult=await requestToolPermission(connection2,sessionId,toolCallId,title,kind);if(!permissionResult.granted){const status=permissionResult.cancelled?"failed":"failed";const message=permissionResult.cancelled?"Permission request cancelled":"Permission denied";const errorContent=[{type:"content",content:{type:"text",text:message}}];await sendToolCallUpdate(connection2,sessionId,toolCallId,status,errorContent);return{content:errorContent,permissionResult}}}await sendToolCallUpdate(connection2,sessionId,toolCallId,"in_progress");const result=await mcpManager.callTool(toolName,args);const content=mapToolResultToACPContent(result.content,result.isError);const finalStatus=result.isError?"failed":"completed";await sendToolCallUpdate(connection2,sessionId,toolCallId,finalStatus,content);return{content}}catch(error){const errorMessage=error instanceof Error?error.message:String(error);const errorContent=[{type:"content",content:{type:"text",text:`Error: ${errorMessage}`}}];await sendToolCallUpdate(connection2,sessionId,toolCallId,"failed",errorContent);return{content:errorContent}}}console.error("[worker] Starting ACP/MCP Protocol Worker...");var inputStream=Readable.toWeb(process.stdin);var outputStream=Writable.toWeb(process.stdout);var stream=ndJsonStream(outputStream,inputStream);var connection=new AgentSideConnection(conn=>new ACPAgent(conn),stream);console.error("[worker] AgentSideConnection established, ready for messages");process.on("SIGTERM",async()=>{console.error("[worker] Received SIGTERM, shutting down...");await connection.closed;process.exit(0)});process.on("SIGINT",async()=>{console.error("[worker] Received SIGINT, shutting down...");await connection.closed;process.exit(0)});process.on("uncaughtException",error=>{console.error("[worker] Uncaught exception:",error);process.exit(1)});process.on("unhandledRejection",(reason,promise)=>{console.error("[worker] Unhandled rejection at:",promise,"reason:",reason)});connection.closed.then(()=>{console.error("[worker] Connection closed");process.exit(0)}).catch(error=>{console.error("[worker] Connection error:",error);process.exit(1)});export{ACPAgent,AgentRuntimeImpl,AgentRuntimeManager,MCPManager,MessageRouter,NDJSONHandler,NoDistributionError,PlatformNotSupportedError,RoutingErrorCodes,canReadFile,canUseTerminal,canWriteFile,createErrorResponse,createErrorToolCallContent,determineToolKind,executeCommand,executeToolCall,executeToolCallWithPermission,extractAgentId,extractId,extractResourceLinkUri,generateToolCallId,getCurrentPlatform,isResourceLink,loadConfig,mapMCPContentToACPContentBlock,mapMCPResourceContentsToACPContentBlock,mapMCPResultToACPToolCallContent,mapToolResultToACPContent,readFile,requestToolPermission,resolve,resolveBinary,resolveNpx,resolveUvx,sendToolCallInitiation,sendToolCallUpdate,startCommand,transformMessage,writeFile};
6
+ `)})}}write(message){if(this.state!=="running"&&this.state!=="starting"){return false}if(!this.process.stdin||this.process.stdin.destroyed){return false}try{const ndjsonLine=JSON.stringify(message)+"\n";return this.process.stdin.write(ndjsonLine)}catch{return false}}async terminate(timeout=DEFAULT_TERMINATE_TIMEOUT_MS){if(this.state==="stopped"){return}if(this.state==="stopping"){return this.waitForExit()}this.state="stopping";if(this.process.stdin&&!this.process.stdin.destroyed){this.process.stdin.end()}this.process.kill("SIGTERM");const exitPromise=this.waitForExit();const timeoutPromise=new Promise(resolve2=>{setTimeout(()=>resolve2("timeout"),timeout)});const result=await Promise.race([exitPromise,timeoutPromise]);if(result==="timeout"&&!this.process.killed&&this.process.exitCode===null){this.process.kill("SIGKILL");await this.waitForExit()}}waitForExit(){if(this.state==="stopped"){return Promise.resolve()}return new Promise(resolve2=>{this.process.once("exit",()=>{resolve2()})})}};var DEFAULT_SHUTDOWN_TIMEOUT_MS=5e3;var AgentRuntimeManager=class{runtimes=new Map;exitCallbacks=[];async getOrSpawn(agentId,spawnCommand){const existing=this.runtimes.get(agentId);if(existing&&existing.state!=="stopped"){return existing}const runtime=AgentRuntimeImpl.spawn(agentId,spawnCommand,(code,_signal)=>{this.handleAgentExit(agentId,code)});this.runtimes.set(agentId,runtime);return runtime}get(agentId){return this.runtimes.get(agentId)}async terminate(agentId,timeout=DEFAULT_SHUTDOWN_TIMEOUT_MS){const runtime=this.runtimes.get(agentId);if(!runtime){return}await runtime.terminate(timeout);this.runtimes.delete(agentId)}async terminateAll(timeout=DEFAULT_SHUTDOWN_TIMEOUT_MS){const terminatePromises=[];for(const[agentId,runtime]of this.runtimes){if(runtime.state!=="stopped"){terminatePromises.push(runtime.terminate(timeout).then(()=>{this.runtimes.delete(agentId)}))}}await Promise.all(terminatePromises)}onAgentExit(callback){this.exitCallbacks.push(callback)}handleAgentExit(agentId,code){this.runtimes.delete(agentId);for(const callback of this.exitCallbacks){try{callback(agentId,code)}catch{}}}get size(){return this.runtimes.size}has(agentId){return this.runtimes.has(agentId)}};function logError(message){const timestamp=new Date().toISOString();console.error(`[${timestamp}] [ERROR] [ndjson] ${message}`)}var NDJSONHandler=class{buffer="";output;messageCallback=null;errorCallback=null;constructor(output){this.output=output}onMessage(callback){this.messageCallback=callback}onError(callback){this.errorCallback=callback}write(message){if(!this.output.writable){return false}try{const json=JSON.stringify(message);this.output.write(json+"\n");return true}catch{return false}}processChunk(chunk){this.buffer+=chunk.toString("utf-8");this.processBuffer()}processBuffer(){let newlineIndex;while((newlineIndex=this.buffer.indexOf("\n"))!==-1){const line=this.buffer.slice(0,newlineIndex);this.buffer=this.buffer.slice(newlineIndex+1);if(line.trim().length===0){continue}this.parseLine(line)}}parseLine(line){try{const message=JSON.parse(line);if(message===null||typeof message!=="object"){const error=new Error("Parsed JSON is not an object");logError(`Malformed NDJSON line (not an object): ${this.truncateLine(line)}`);this.errorCallback?.(error,line);return}this.messageCallback?.(message)}catch(error){logError(`Failed to parse NDJSON line: ${this.truncateLine(line)}`);this.errorCallback?.(error,line)}}truncateLine(line,maxLength=100){if(line.length<=maxLength){return line}return line.slice(0,maxLength)+"..."}};var AgentNotFoundError=class extends Error{constructor(agentId){super(`Agent not found: ${agentId}`);this.agentId=agentId;this.name="AgentNotFoundError"}};function getAgentApiKey(apiKeys,agentId){const keys=apiKeys[agentId];if(!keys||!keys.apiKey||keys.apiKey.length===0){return void 0}return keys.apiKey}var RoutingErrorCodes={MISSING_AGENT_ID:-32600,AGENT_NOT_FOUND:-32001,PLATFORM_NOT_SUPPORTED:-32002,SPAWN_FAILED:-32003};function logError2(message){const timestamp=new Date().toISOString();console.error(`[${timestamp}] [ERROR] [router] ${message}`)}function logInfo(message){const timestamp=new Date().toISOString();console.error(`[${timestamp}] [INFO] [router] ${message}`)}function createErrorResponse(id,code,message,data){const response={jsonrpc:"2.0",id,error:{code,message}};if(data!==void 0){response.error.data=data}return response}function extractAgentId(message){const msg=message;const agentId=msg.agentId;if(typeof agentId==="string"&&agentId.length>0){return agentId}return void 0}function extractId(message){const msg=message;const id=msg.id;if(typeof id==="string"||typeof id==="number"){return id}return null}function transformMessage(message){const{agentId:_,...rest}=message;return rest}var MessageRouter=class{registry;runtimeManager;writeCallback;apiKeys;pendingRequests=new Map;authState=new Map;sessionIdMap=new Map;constructor(registry,runtimeManager,writeCallback,apiKeys={}){this.registry=registry;this.runtimeManager=runtimeManager;this.writeCallback=writeCallback;this.apiKeys=apiKeys}async route(message){const id=extractId(message);const agentId=extractAgentId(message);if(agentId===void 0){logError2("Missing agentId in request");return createErrorResponse(id,RoutingErrorCodes.MISSING_AGENT_ID,"Missing agentId")}let spawnCommand;try{spawnCommand=this.registry.resolve(agentId)}catch(error){if(error instanceof AgentNotFoundError){logError2(`Agent not found: ${agentId}`);return createErrorResponse(id,RoutingErrorCodes.AGENT_NOT_FOUND,"Agent not found",{agentId})}if(error instanceof PlatformNotSupportedError){logError2(`Platform not supported for agent: ${agentId}`);return createErrorResponse(id,RoutingErrorCodes.PLATFORM_NOT_SUPPORTED,"Platform not supported",{agentId,platform:error.platform})}throw error}let runtime;try{runtime=await this.runtimeManager.getOrSpawn(agentId,spawnCommand)}catch(error){logError2(`Failed to spawn agent ${agentId}: ${error.message}`);return createErrorResponse(id,RoutingErrorCodes.SPAWN_FAILED,"Agent spawn failed",{agentId,error:error.message})}if(id!==null){const msg=message;const clientSessionId=typeof msg.sessionId==="string"?msg.sessionId:void 0;this.pendingRequests.set(id,{id,agentId,timestamp:Date.now(),clientSessionId})}const transformedMessage=transformMessage(message);const success=runtime.write(transformedMessage);if(!success){logError2(`Failed to write to agent ${agentId}`);if(id!==null){this.pendingRequests.delete(id)}}else{logInfo(`Routed message to agent ${agentId}`)}return void 0}handleAgentResponse(agentId,response){const id=extractId(response);const msg=response;if(id!==null){const pending=this.pendingRequests.get(id);if(pending&&pending.agentId===agentId){const result=msg.result;if(result&&Array.isArray(result.authMethods)&&result.authMethods.length>0){logInfo(`Agent ${agentId} requires authentication, attempting auto-auth`);this.authState.set(agentId,"pending");void this.attemptAuthentication(agentId,result.authMethods)}if(result&&typeof result.sessionId==="string"){const agentSessionId=result.sessionId;const clientSessionId=pending.clientSessionId;if(clientSessionId){this.sessionIdMap.set(agentSessionId,clientSessionId);logInfo(`Mapped agent sessionId ${agentSessionId} to client sessionId ${clientSessionId}`)}}this.pendingRequests.delete(id)}}if(id===null&&msg.method){logInfo(`Received notification: ${msg.method}`);const params=msg.params;if(params&&typeof params.sessionId==="string"){const agentSessionId=params.sessionId;const clientSessionId=this.sessionIdMap.get(agentSessionId);if(clientSessionId){const enriched={...msg,sessionId:clientSessionId,params:{...params,sessionId:agentSessionId}};logInfo(`Forwarding notification with mapped sessionId: ${clientSessionId}`);this.writeCallback(enriched);return}else{logInfo(`Forwarding notification with unmapped agentSessionId: ${agentSessionId}`);this.writeCallback(response);return}}else{const topLevelSessionId=msg.sessionId;if(topLevelSessionId){this.writeCallback(response);return}else{logError2(`Notification without sessionId: ${msg.method}, adding default sessionId for routing`);const enriched={...msg,sessionId:"global-notifications"};this.writeCallback(enriched);return}}}this.writeCallback(response)}async attemptAuthentication(agentId,authMethods){const apiKey=getAgentApiKey(this.apiKeys,agentId);if(!apiKey){logError2(`No API key found for agent ${agentId}, authentication will fail`);this.authState.set(agentId,"none");return}let selectedMethod=authMethods.find(m=>m.id==="openai-api-key");if(!selectedMethod){selectedMethod=authMethods.find(m=>m.id.includes("api-key")||m.id.includes("apikey"))}if(!selectedMethod){selectedMethod=authMethods[0]}logInfo(`Authenticating agent ${agentId} with method: ${selectedMethod.id}`);let runtime;try{const spawnCommand=this.registry.resolve(agentId);runtime=await this.runtimeManager.getOrSpawn(agentId,spawnCommand)}catch(error){logError2(`Failed to get runtime for authentication: ${error.message}`);this.authState.set(agentId,"none");return}const authRequest={jsonrpc:"2.0",id:`auth-${agentId}-${Date.now()}`,method:"authenticate",params:{methodId:selectedMethod.id,credentials:{apiKey}}};const transformed=transformMessage(authRequest);const serialized=JSON.stringify(transformed)+"\n";if(runtime.process.stdin){runtime.process.stdin.write(serialized,error=>{if(error){logError2(`Failed to send authenticate request to ${agentId}: ${error.message}`);this.authState.set(agentId,"none")}else{logInfo(`Sent authenticate request to agent ${agentId}`);this.authState.set(agentId,"authenticated")}})}}get pendingCount(){return this.pendingRequests.size}isPending(id){return this.pendingRequests.has(id)}clearPending(){this.pendingRequests.clear()}};import{readFileSync}from"node:fs";var DEFAULT_CONFIG={registryUrl:"https://cdn.agentclientprotocol.com/registry/v1/latest/registry.json",apiKeysPath:"./api-keys.json",shutdownTimeoutSec:5};var ENV_REGISTRY_URL="ACP_REGISTRY_URL";var ENV_API_KEYS_PATH="ACP_API_KEYS_PATH";function logWarning(message){const timestamp=new Date().toISOString();console.error(`[${timestamp}] [WARN] [config] ${message}`)}function isNonEmptyString(value){return typeof value==="string"&&value.length>0}function isPositiveNumber(value){return typeof value==="number"&&value>0&&Number.isFinite(value)}function parseConfigObject(obj){const config={...DEFAULT_CONFIG};if(obj===null||typeof obj!=="object"){logWarning("Config file does not contain a valid object, using defaults");return config}const rawConfig=obj;if("registryUrl"in rawConfig){if(isNonEmptyString(rawConfig.registryUrl)){config.registryUrl=rawConfig.registryUrl}else{logWarning('Config field "registryUrl" is not a valid string, using default')}}if("apiKeysPath"in rawConfig){if(isNonEmptyString(rawConfig.apiKeysPath)){config.apiKeysPath=rawConfig.apiKeysPath}else{logWarning('Config field "apiKeysPath" is not a valid string, using default')}}if("shutdownTimeoutSec"in rawConfig){if(isPositiveNumber(rawConfig.shutdownTimeoutSec)){config.shutdownTimeoutSec=rawConfig.shutdownTimeoutSec}else{logWarning('Config field "shutdownTimeoutSec" is not a valid positive number, using default')}}return config}function applyEnvironmentOverrides(config){const envRegistryUrl=process.env[ENV_REGISTRY_URL];const envApiKeysPath=process.env[ENV_API_KEYS_PATH];const overrides={};if(isNonEmptyString(envRegistryUrl)){overrides.registryUrl=envRegistryUrl}if(isNonEmptyString(envApiKeysPath)){overrides.apiKeysPath=envApiKeysPath}return{...config,...overrides}}function loadConfig(configPath){let config={...DEFAULT_CONFIG};if(configPath){try{const fileContent=readFileSync(configPath,"utf-8");const parsed=JSON.parse(fileContent);config=parseConfigObject(parsed)}catch(error){if(error instanceof SyntaxError){logWarning(`Config file "${configPath}" contains malformed JSON, using defaults`)}else if(error.code==="ENOENT"){logWarning(`Config file "${configPath}" not found, using defaults`)}else if(error.code==="EACCES"){logWarning(`Config file "${configPath}" is not readable, using defaults`)}else{logWarning(`Failed to read config file "${configPath}": ${error.message}, using defaults`)}}}config=applyEnvironmentOverrides(config);return config}function canReadFile(capabilities){return capabilities?.fs?.readTextFile===true}function canWriteFile(capabilities){return capabilities?.fs?.writeTextFile===true}function canUseTerminal(capabilities){return capabilities?.terminal===true}async function readFile(connection2,sessionId,path,options){try{const response=await connection2.readTextFile({sessionId,path,line:options?.line,limit:options?.limit});return{content:response.content,success:true}}catch(error){const errorMessage=error instanceof Error?error.message:String(error);console.error(`[ACP] Failed to read file "${path}":`,error);return{content:"",success:false,error:errorMessage}}}async function writeFile(connection2,sessionId,path,content){try{await connection2.writeTextFile({sessionId,path,content});return{success:true}}catch(error){const errorMessage=error instanceof Error?error.message:String(error);console.error(`[ACP] Failed to write file "${path}":`,error);return{success:false,error:errorMessage}}}async function executeCommand(connection2,sessionId,command,options){let terminal=null;try{terminal=await connection2.createTerminal({sessionId,command,args:options?.args,cwd:options?.cwd,env:options?.env,outputByteLimit:options?.outputByteLimit});let timeoutId=null;let timedOut=false;if(options?.timeout&&options.timeout>0){timeoutId=setTimeout(async()=>{timedOut=true;if(terminal){try{await terminal.kill()}catch{}}},options.timeout)}try{const exitResult=await terminal.waitForExit();if(timeoutId){clearTimeout(timeoutId)}const outputResult=await terminal.currentOutput();return{output:outputResult.output,exitCode:timedOut?null:exitResult.exitCode??null,signal:timedOut?"SIGTERM":exitResult.signal??null,truncated:outputResult.truncated,success:!timedOut&&exitResult.exitCode===0,error:timedOut?"Command timed out":void 0}}finally{if(timeoutId){clearTimeout(timeoutId)}}}catch(error){const errorMessage=error instanceof Error?error.message:String(error);console.error(`[ACP] Failed to execute command "${command}":`,error);return{output:"",exitCode:null,signal:null,truncated:false,success:false,error:errorMessage}}finally{if(terminal){try{await terminal.release()}catch{}}}}async function startCommand(connection2,sessionId,command,options){return connection2.createTerminal({sessionId,command,args:options?.args,cwd:options?.cwd,env:options?.env,outputByteLimit:options?.outputByteLimit})}function mapMCPContentToACPContentBlock(mcpContent){switch(mcpContent.type){case"text":return mapTextContent(mcpContent);case"image":return mapImageContent(mcpContent);case"resource":return mapEmbeddedResource(mcpContent);default:return{type:"text",text:JSON.stringify(mcpContent)}}}function mapTextContent(mcpText){const result={type:"text",text:mcpText.text};return result}function mapImageContent(mcpImage){const result={type:"image",data:mcpImage.data,mimeType:mcpImage.mimeType};return result}function mapEmbeddedResource(mcpResource){const{resource}=mcpResource;if(resource.text!==void 0){const result={type:"resource",resource:{uri:resource.uri,mimeType:resource.mimeType,text:resource.text}};return result}else if(resource.blob!==void 0){const result={type:"resource",resource:{uri:resource.uri,mimeType:resource.mimeType,blob:resource.blob}};return result}else{const result={type:"resource",resource:{uri:resource.uri,mimeType:resource.mimeType,text:""}};return result}}function mapMCPResultToACPToolCallContent(mcpContents){return mcpContents.map(mcpContent=>{const contentBlock=mapMCPContentToACPContentBlock(mcpContent);return{type:"content",content:contentBlock}})}function createErrorToolCallContent(errorMessage){return[{type:"content",content:{type:"text",text:`Error: ${errorMessage}`}}]}function mapToolResultToACPContent(content,isError=false){if(isError&&content.length>0){const errorText=content.filter(c=>c.type==="text").map(c=>c.text).join("\n");if(errorText){return createErrorToolCallContent(errorText)}}return mapMCPResultToACPToolCallContent(content)}function isResourceLink(block){return block.type==="resource_link"&&"uri"in block}function mapMCPResourceContentsToACPContentBlock(contents){if("text"in contents){const result2={type:"resource",resource:{uri:contents.uri,mimeType:contents.mimeType,text:contents.text}};return result2}const result={type:"resource",resource:{uri:contents.uri,mimeType:contents.mimeType,blob:contents.blob}};return result}function extractResourceLinkUri(block){if(isResourceLink(block)){return block.uri}return null}var toolCallCounter=0;function generateToolCallId(){toolCallCounter++;return`tool-${Date.now()}-${toolCallCounter}`}function determineToolKind(toolName,description){const name=toolName.toLowerCase();const desc=(description||"").toLowerCase();if(desc.includes("external")||desc.includes("api")||desc.includes("http")){return"fetch"}if(name.includes("read")||name.includes("get")||name.includes("list")||name.includes("fetch")){return"read"}if(name.includes("write")||name.includes("edit")||name.includes("update")||name.includes("modify")){return"edit"}if(name.includes("delete")||name.includes("remove")){return"delete"}if(name.includes("move")||name.includes("rename")){return"move"}if(name.includes("search")||name.includes("find")||name.includes("query")){return"search"}if(name.includes("exec")||name.includes("run")||name.includes("shell")||name.includes("command")){return"execute"}if(name.includes("http")||name.includes("api")||name.includes("request")){return"fetch"}return"other"}async function sendToolCallInitiation(connection2,sessionId,toolCallId,title,kind="other",status="pending"){await connection2.sessionUpdate({sessionId,update:{sessionUpdate:"tool_call",toolCallId,title,kind,status}})}async function sendToolCallUpdate(connection2,sessionId,toolCallId,status,content,title){await connection2.sessionUpdate({sessionId,update:{sessionUpdate:"tool_call_update",toolCallId,status,content,title}})}async function requestToolPermission(connection2,sessionId,toolCallId,title,kind="other",options){const permissionOptions=options??[{optionId:"allow_once",name:"Allow once",kind:"allow_once"},{optionId:"allow_always",name:"Allow always",kind:"allow_always"},{optionId:"reject_once",name:"Reject",kind:"reject_once"}];const toolCall={toolCallId,title,kind,status:"pending"};try{const response=await connection2.requestPermission({sessionId,toolCall,options:permissionOptions});if(response.outcome.outcome==="cancelled"){return{granted:false,cancelled:true}}if(response.outcome.outcome==="selected"){const selectedOption=response.outcome.optionId;const isAllowed=selectedOption.startsWith("allow");return{granted:isAllowed,optionId:selectedOption,cancelled:false}}return{granted:false,cancelled:false}}catch(error){console.error("[ACP] Permission request failed:",error);return{granted:false,cancelled:true}}}async function executeToolCall(connection2,sessionId,mcpManager,toolName,args,description){const toolCallId=generateToolCallId();const kind=determineToolKind(toolName,description);const title=`Executing: ${toolName}`;try{await sendToolCallInitiation(connection2,sessionId,toolCallId,title,kind,"pending");await sendToolCallUpdate(connection2,sessionId,toolCallId,"in_progress");const result=await mcpManager.callTool(toolName,args);const content=mapToolResultToACPContent(result.content,result.isError);const finalStatus=result.isError?"failed":"completed";await sendToolCallUpdate(connection2,sessionId,toolCallId,finalStatus,content);return content}catch(error){const errorMessage=error instanceof Error?error.message:String(error);const errorContent=[{type:"content",content:{type:"text",text:`Error: ${errorMessage}`}}];await sendToolCallUpdate(connection2,sessionId,toolCallId,"failed",errorContent);return errorContent}}async function executeToolCallWithPermission(connection2,sessionId,mcpManager,toolName,args,description,requirePermission=true){const toolCallId=generateToolCallId();const kind=determineToolKind(toolName,description);const title=`Executing: ${toolName}`;try{await sendToolCallInitiation(connection2,sessionId,toolCallId,title,kind,"pending");if(requirePermission){const permissionResult=await requestToolPermission(connection2,sessionId,toolCallId,title,kind);if(!permissionResult.granted){const status=permissionResult.cancelled?"failed":"failed";const message=permissionResult.cancelled?"Permission request cancelled":"Permission denied";const errorContent=[{type:"content",content:{type:"text",text:message}}];await sendToolCallUpdate(connection2,sessionId,toolCallId,status,errorContent);return{content:errorContent,permissionResult}}}await sendToolCallUpdate(connection2,sessionId,toolCallId,"in_progress");const result=await mcpManager.callTool(toolName,args);const content=mapToolResultToACPContent(result.content,result.isError);const finalStatus=result.isError?"failed":"completed";await sendToolCallUpdate(connection2,sessionId,toolCallId,finalStatus,content);return{content}}catch(error){const errorMessage=error instanceof Error?error.message:String(error);const errorContent=[{type:"content",content:{type:"text",text:`Error: ${errorMessage}`}}];await sendToolCallUpdate(connection2,sessionId,toolCallId,"failed",errorContent);return{content:errorContent}}}console.error("[worker] Starting ACP/MCP Protocol Worker...");var sessionIdRouter=new SessionIdRouter;var stdinTransform=new Transform({objectMode:false,transform(chunk,_encoding,callback){const lines=chunk.toString().split("\n");const processedLines=[];for(const line of lines){processedLines.push(sessionIdRouter.processIncomingLine(line))}callback(null,Buffer.from(processedLines.join("\n")))}});var stdoutTransform=new Transform({objectMode:false,transform(chunk,_encoding,callback){const lines=chunk.toString().split("\n");const processedLines=[];for(const line of lines){processedLines.push(sessionIdRouter.processOutgoingLine(line))}callback(null,Buffer.from(processedLines.join("\n")))}});process.stdin.pipe(stdinTransform);var inputStream=Readable.toWeb(stdinTransform);var outputStream=Writable.toWeb(stdoutTransform);stdoutTransform.pipe(process.stdout);var stream=ndJsonStream(outputStream,inputStream);var connection=new AgentSideConnection(conn=>new ACPAgent(conn),stream);console.error("[worker] AgentSideConnection established, ready for messages");process.on("SIGTERM",async()=>{console.error("[worker] Received SIGTERM, shutting down...");await connection.closed;process.exit(0)});process.on("SIGINT",async()=>{console.error("[worker] Received SIGINT, shutting down...");await connection.closed;process.exit(0)});process.on("uncaughtException",error=>{console.error("[worker] Uncaught exception:",error);process.exit(1)});process.on("unhandledRejection",(reason,promise)=>{console.error("[worker] Unhandled rejection at:",promise,"reason:",reason)});connection.closed.then(()=>{console.error("[worker] Connection closed");process.exit(0)}).catch(error=>{console.error("[worker] Connection error:",error);process.exit(1)});export{ACPAgent,AgentRuntimeImpl,AgentRuntimeManager,MCPManager,MessageRouter,NDJSONHandler,NoDistributionError,PlatformNotSupportedError,RoutingErrorCodes,canReadFile,canUseTerminal,canWriteFile,createErrorResponse,createErrorToolCallContent,determineToolKind,executeCommand,executeToolCall,executeToolCallWithPermission,extractAgentId,extractId,extractResourceLinkUri,generateToolCallId,getCurrentPlatform,isResourceLink,loadConfig,mapMCPContentToACPContentBlock,mapMCPResourceContentsToACPContentBlock,mapMCPResultToACPToolCallContent,mapToolResultToACPContent,readFile,requestToolPermission,resolve,resolveBinary,resolveNpx,resolveUvx,sendToolCallInitiation,sendToolCallUpdate,startCommand,transformMessage,writeFile};
7
7
  //# sourceMappingURL=index.js.map