{"version":3,"file":"process-runtime.d.ts","sourceRoot":"","sources":["../../src/components/process-runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import { type ChildProcessWithoutNullStreams, spawn } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\nimport type { LoadedEvoComponentArtifact } from \"./artifact.ts\";\nimport {\n\tcapabilityErrorFrame,\n\tcapabilitySuccessFrame,\n\ttype EvoCapabilityRequestFrame,\n\tparseEvoCapabilityRequestFrame,\n} from \"./capabilities/protocol.ts\";\nimport type { EvoCapabilityComponentIdentity } from \"./capabilities/service.ts\";\nimport type { EvoAbiDefinition } from \"./registry.ts\";\n\nconst DEFAULT_REQUEST_TIMEOUT_MS = 120_000;\nconst MAX_STDERR_BYTES = 64 * 1024;\nconst DOCKER_PROBE_TIMEOUT_MS = 10_000;\nconst DOCKER_PULL_TIMEOUT_MS = 180_000;\nconst MAX_CONCURRENT_CAPABILITY_REQUESTS = 32;\nconst DEFAULT_MAX_STDOUT_FRAME_BYTES = 8 * 1024 * 1024;\nconst DEFAULT_MAX_STDOUT_BYTES = 64 * 1024 * 1024;\nconst DEFAULT_MAX_CAPABILITY_RESULT_BYTES = 8 * 1024 * 1024;\nconst DEFAULT_TERMINATION_GRACE_MS = 1_000;\nconst INITIAL_STDOUT_FRAME_BUFFER_BYTES = 64 * 1024;\nconst DOCKER_CONTROL_TIMEOUT_MS = 10_000;\nconst MAX_DOCKER_CONTROL_OUTPUT_BYTES = 64 * 1024;\n\n/** Major version pinned to the supported host runtime; override for air-gapped hosts. */\nfunction dockerImage(): string {\n\treturn process.env.PI_EVO_DOCKER_IMAGE || \"node:22-slim\";\n}\n\nexport type EvoComponentSandboxKind = \"bwrap\" | \"docker\";\n\ninterface ProcessRequest {\n\tid: number;\n\tmethod: \"initialize\" | \"invoke\" | \"health\" | \"shutdown\";\n\tpayload?: unknown;\n}\n\ninterface ProcessResponse {\n\tid: number;\n\tok: boolean;\n\tresult?: unknown;\n\terror?: string;\n}\n\ninterface PendingRequest {\n\tresolve(value: unknown): void;\n\treject(error: Error): void;\n\ttimer: NodeJS.Timeout;\n\tmethod: ProcessRequest[\"method\"];\n}\n\nexport interface EvoComponentCapabilityBroker {\n\trequest(\n\t\tcomponent: EvoCapabilityComponentIdentity,\n\t\tframe: EvoCapabilityRequestFrame,\n\t\tsignal: AbortSignal,\n\t): Promise<unknown>;\n}\n\nexport interface EvoComponentProcessOptions {\n\trequestTimeoutMs?: number;\n\t/** Maximum bytes in one component JSONL frame. Primarily overridden by focused tests. */\n\tmaxStdoutFrameBytes?: number;\n\t/** Maximum aggregate stdout bytes emitted over the component process lifetime. */\n\tmaxStdoutBytes?: number;\n\t/** Maximum bytes in one host-to-component capability-result JSONL frame. */\n\tmaxCapabilityResultBytes?: number;\n\t/** Grace between TERM and KILL when tearing down an uncooperative component. */\n\tterminationGraceMs?: number;\n\t/** Test/development escape hatch. Production callers should keep this true. */\n\tsandbox?: boolean;\n\tcapabilityBroker?: EvoComponentCapabilityBroker;\n}\n\nfunction parseResponse(parsed: unknown): ProcessResponse {\n\tif (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n\t\tthrow new Error(\"Component process response must be an object\");\n\t}\n\tconst response = parsed as Record<string, unknown>;\n\tfor (const key of Object.keys(response)) {\n\t\tif (![\"id\", \"ok\", \"result\", \"error\"].includes(key)) {\n\t\t\tthrow new Error(`Component process response has unknown key: ${key}`);\n\t\t}\n\t}\n\tif (!Number.isSafeInteger(response.id) || (response.id as number) <= 0 || typeof response.ok !== \"boolean\") {\n\t\tthrow new Error(\"Component process response is invalid\");\n\t}\n\tif (response.ok) {\n\t\tif (Object.hasOwn(response, \"error\")) throw new Error(\"Successful component response cannot include error\");\n\t} else {\n\t\tif (typeof response.error !== \"string\" || response.error.length === 0) {\n\t\t\tthrow new Error(\"Failed component response must include a non-empty error\");\n\t\t}\n\t\tif (Object.hasOwn(response, \"result\")) throw new Error(\"Failed component response cannot include result\");\n\t}\n\treturn response as unknown as ProcessResponse;\n}\n\ntype ComponentOutput =\n\t| { kind: \"response\"; value: ProcessResponse }\n\t| { kind: \"capability\"; value: EvoCapabilityRequestFrame };\n\nfunction parseComponentOutput(line: string): ComponentOutput {\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(line) as unknown;\n\t} catch (error) {\n\t\tthrow new Error(\"Component process returned invalid JSON\", { cause: error });\n\t}\n\tif (typeof parsed === \"object\" && parsed !== null && !Array.isArray(parsed) && Object.hasOwn(parsed, \"type\")) {\n\t\treturn { kind: \"capability\", value: parseEvoCapabilityRequestFrame(parsed) };\n\t}\n\treturn { kind: \"response\", value: parseResponse(parsed) };\n}\n\nfunction probeCommand(command: string, args: string[], timeoutMs: number): Promise<boolean> {\n\treturn new Promise((resolve) => {\n\t\tconst child = spawn(command, args, { stdio: \"ignore\" });\n\t\tconst timer = setTimeout(() => {\n\t\t\tchild.kill(\"SIGTERM\");\n\t\t\tresolve(false);\n\t\t}, timeoutMs);\n\t\ttimer.unref?.();\n\t\tchild.once(\"error\", () => {\n\t\t\tclearTimeout(timer);\n\t\t\tresolve(false);\n\t\t});\n\t\tchild.once(\"close\", (code) => {\n\t\t\tclearTimeout(timer);\n\t\t\tresolve(code === 0);\n\t\t});\n\t});\n}\n\ninterface DockerControlResult {\n\tcode: number | null;\n\tstdout: string;\n\tstderr: string;\n}\n\nfunction runDockerControlCommand(args: string[]): Promise<DockerControlResult> {\n\treturn new Promise((resolvePromise, reject) => {\n\t\tconst child = spawn(\"docker\", args, {\n\t\t\tenv: { ...process.env, LANG: \"C\" },\n\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t});\n\t\tconst stdout: Buffer[] = [];\n\t\tconst stderr: Buffer[] = [];\n\t\tlet outputBytes = 0;\n\t\tlet settled = false;\n\t\tconst timer = setTimeout(() => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tchild.kill(\"SIGKILL\");\n\t\t\treject(new Error(`Docker control command timed out: docker ${args.join(\" \")}`));\n\t\t}, DOCKER_CONTROL_TIMEOUT_MS);\n\t\ttimer.unref();\n\t\tconst capture =\n\t\t\t(target: Buffer[]) =>\n\t\t\t(chunk: Buffer | string): void => {\n\t\t\t\tif (settled) return;\n\t\t\t\tconst buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\n\t\t\t\toutputBytes += buffer.byteLength;\n\t\t\t\tif (outputBytes > MAX_DOCKER_CONTROL_OUTPUT_BYTES) {\n\t\t\t\t\tsettled = true;\n\t\t\t\t\tclearTimeout(timer);\n\t\t\t\t\tchild.kill(\"SIGKILL\");\n\t\t\t\t\treject(new Error(\"Docker control command output exceeded the byte limit\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\ttarget.push(buffer);\n\t\t\t};\n\t\tchild.stdout.on(\"data\", capture(stdout));\n\t\tchild.stderr.on(\"data\", capture(stderr));\n\t\tchild.once(\"error\", (error) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tclearTimeout(timer);\n\t\t\treject(new Error(`Docker control command failed to start: ${error.message}`, { cause: error }));\n\t\t});\n\t\tchild.once(\"close\", (code) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tclearTimeout(timer);\n\t\t\tresolvePromise({\n\t\t\t\tcode,\n\t\t\t\tstdout: Buffer.concat(stdout).toString(\"utf8\"),\n\t\t\t\tstderr: Buffer.concat(stderr).toString(\"utf8\"),\n\t\t\t});\n\t\t});\n\t});\n}\n\nasync function probeBwrapSandbox(): Promise<boolean> {\n\tif (process.platform !== \"linux\") return false;\n\treturn probeCommand(\n\t\t\"bwrap\",\n\t\t[\n\t\t\t\"--die-with-parent\",\n\t\t\t\"--new-session\",\n\t\t\t\"--unshare-net\",\n\t\t\t\"--unshare-pid\",\n\t\t\t\"--unshare-ipc\",\n\t\t\t\"--unshare-uts\",\n\t\t\t\"--proc\",\n\t\t\t\"/proc\",\n\t\t\t\"--dev\",\n\t\t\t\"/dev\",\n\t\t\t\"--ro-bind\",\n\t\t\t\"/usr\",\n\t\t\t\"/usr\",\n\t\t\t\"--ro-bind\",\n\t\t\t\"/bin\",\n\t\t\t\"/bin\",\n\t\t\t\"--\",\n\t\t\t\"/bin/true\",\n\t\t],\n\t\tDOCKER_PROBE_TIMEOUT_MS,\n\t);\n}\n\nasync function probeDockerSandbox(): Promise<boolean> {\n\tconst image = dockerImage();\n\tif (await probeCommand(\"docker\", [\"image\", \"inspect\", image], DOCKER_PROBE_TIMEOUT_MS)) return true;\n\t// A missing image is fetched once; a dead daemon or offline host fails here too.\n\treturn probeCommand(\"docker\", [\"pull\", \"--quiet\", image], DOCKER_PULL_TIMEOUT_MS);\n}\n\nlet sandboxProbe: Promise<EvoComponentSandboxKind | undefined> | undefined;\n\n/**\n * Resolve the strongest available component sandbox: bwrap when unprivileged user\n * namespaces work, otherwise an equally isolated Docker container (hosts that\n * restrict userns via AppArmor typically still run Docker). The probe result is\n * cached per process.\n */\nexport function resolveEvoComponentSandbox(): Promise<EvoComponentSandboxKind | undefined> {\n\tsandboxProbe ??= (async () => {\n\t\tif (await probeBwrapSandbox()) return \"bwrap\";\n\t\tif (await probeDockerSandbox()) return \"docker\";\n\t\treturn undefined;\n\t})();\n\treturn sandboxProbe;\n}\n\nexport async function canUseEvoComponentSandbox(): Promise<boolean> {\n\treturn (await resolveEvoComponentSandbox()) !== undefined;\n}\n\ninterface ComponentLaunch {\n\tcommand: string;\n\targs: string[];\n\tenv: NodeJS.ProcessEnv;\n\tdockerContainerName?: string;\n}\n\nfunction bwrapCommand(artifact: LoadedEvoComponentArtifact): ComponentLaunch {\n\tif (process.platform !== \"linux\") {\n\t\tthrow new Error(`Sandboxed Evo component processes are unsupported on ${process.platform}`);\n\t}\n\tconst runtime = process.execPath;\n\treturn {\n\t\tcommand: \"bwrap\",\n\t\targs: [\n\t\t\t\"--die-with-parent\",\n\t\t\t\"--new-session\",\n\t\t\t\"--unshare-net\",\n\t\t\t\"--unshare-pid\",\n\t\t\t\"--unshare-ipc\",\n\t\t\t\"--unshare-uts\",\n\t\t\t\"--proc\",\n\t\t\t\"/proc\",\n\t\t\t\"--dev\",\n\t\t\t\"/dev\",\n\t\t\t\"--tmpfs\",\n\t\t\t\"/tmp\",\n\t\t\t\"--ro-bind\",\n\t\t\t\"/usr\",\n\t\t\t\"/usr\",\n\t\t\t\"--ro-bind\",\n\t\t\t\"/bin\",\n\t\t\t\"/bin\",\n\t\t\t\"--ro-bind-try\",\n\t\t\t\"/lib\",\n\t\t\t\"/lib\",\n\t\t\t\"--ro-bind-try\",\n\t\t\t\"/lib64\",\n\t\t\t\"/lib64\",\n\t\t\t\"--dir\",\n\t\t\t\"/component\",\n\t\t\t\"--ro-bind\",\n\t\t\tartifact.directory,\n\t\t\t\"/component\",\n\t\t\t\"--dir\",\n\t\t\t\"/runtime\",\n\t\t\t\"--ro-bind\",\n\t\t\truntime,\n\t\t\t\"/runtime/node\",\n\t\t\t\"--dir\",\n\t\t\t\"/home\",\n\t\t\t\"--chdir\",\n\t\t\t\"/component\",\n\t\t\t\"--\",\n\t\t\t\"/runtime/node\",\n\t\t\t`/component/${artifact.manifest.entrypoint}`,\n\t\t],\n\t\tenv: { HOME: \"/home\", PATH: \"/runtime:/usr/bin:/bin\", LANG: \"C.UTF-8\" },\n\t};\n}\n\n/** Isolation equivalent to the bwrap profile: no network, read-only rootfs, no capabilities. */\nexport function dockerSandboxCommand(artifact: LoadedEvoComponentArtifact): ComponentLaunch {\n\tconst containerName = `pi-evo-${process.pid}-${randomUUID().replaceAll(\"-\", \"\")}`;\n\tconst user =\n\t\ttypeof process.getuid === \"function\" && typeof process.getgid === \"function\"\n\t\t\t? [`--user=${process.getuid()}:${process.getgid()}`]\n\t\t\t: [];\n\treturn {\n\t\tcommand: \"docker\",\n\t\targs: [\n\t\t\t\"run\",\n\t\t\t\"--rm\",\n\t\t\t\"--name\",\n\t\t\tcontainerName,\n\t\t\t\"--interactive\",\n\t\t\t\"--init\",\n\t\t\t\"--network=none\",\n\t\t\t\"--read-only\",\n\t\t\t\"--cap-drop=ALL\",\n\t\t\t\"--security-opt=no-new-privileges\",\n\t\t\t\"--pids-limit=256\",\n\t\t\t...user,\n\t\t\t\"--tmpfs\",\n\t\t\t\"/tmp\",\n\t\t\t\"--volume\",\n\t\t\t`${artifact.directory}:/component:ro`,\n\t\t\t\"--workdir\",\n\t\t\t\"/component\",\n\t\t\t\"--env\",\n\t\t\t\"HOME=/tmp\",\n\t\t\t\"--env\",\n\t\t\t\"LANG=C.UTF-8\",\n\t\t\tdockerImage(),\n\t\t\t\"node\",\n\t\t\t`/component/${artifact.manifest.entrypoint}`,\n\t\t],\n\t\t// The docker CLI itself needs the caller's environment (DOCKER_HOST and\n\t\t// friends); the container only sees the --env flags above.\n\t\tenv: process.env,\n\t\tdockerContainerName: containerName,\n\t};\n}\n\nfunction directCommand(artifact: LoadedEvoComponentArtifact): ComponentLaunch {\n\treturn {\n\t\tcommand: process.execPath,\n\t\targs: [artifact.entrypoint],\n\t\tenv: { HOME: artifact.directory, PATH: process.env.PATH, LANG: \"C.UTF-8\" },\n\t};\n}\n\nexport class EvoComponentProcess<TInput = unknown, TOutput = unknown, TConfig = unknown> {\n\tprivate child?: ChildProcessWithoutNullStreams;\n\tprivate nextId = 1;\n\tprivate readonly pending = new Map<number, PendingRequest>();\n\tprivate readonly inFlightCapabilities = new Set<string>();\n\tprivate readonly seenCapabilityIds = new Map<number, Set<string>>();\n\tprivate readonly capabilityAbort = new AbortController();\n\tprivate stderr = \"\";\n\tprivate stdoutFrameBuffer?: Buffer;\n\tprivate stdoutFrameBytes = 0;\n\tprivate stdoutBytes = 0;\n\tprivate stdoutFailed = false;\n\tprivate childClose?: Promise<void>;\n\tprivate resolveChildClose: () => void = () => {};\n\tprivate terminationPromise?: Promise<void>;\n\tprivate terminationError?: Error;\n\tprivate dockerContainerName?: string;\n\tprivate stopped = false;\n\tprivate launchedSandbox?: EvoComponentSandboxKind | \"direct\";\n\tprivate readonly artifact: LoadedEvoComponentArtifact;\n\tprivate readonly abi: EvoAbiDefinition<TInput, TOutput, TConfig>;\n\tprivate readonly config: TConfig;\n\tprivate readonly options: EvoComponentProcessOptions;\n\tprivate readonly maxStdoutFrameBytes: number;\n\tprivate readonly maxStdoutBytes: number;\n\tprivate readonly maxCapabilityResultBytes: number;\n\tprivate readonly terminationGraceMs: number;\n\n\tconstructor(\n\t\tartifact: LoadedEvoComponentArtifact,\n\t\tabi: EvoAbiDefinition<TInput, TOutput, TConfig>,\n\t\tconfig: TConfig,\n\t\toptions: EvoComponentProcessOptions = {},\n\t) {\n\t\tthis.artifact = artifact;\n\t\tthis.abi = abi;\n\t\tthis.config = config;\n\t\tthis.options = options;\n\t\tthis.maxStdoutFrameBytes = this.positiveByteLimit(\n\t\t\toptions.maxStdoutFrameBytes ?? DEFAULT_MAX_STDOUT_FRAME_BYTES,\n\t\t\t\"maxStdoutFrameBytes\",\n\t\t);\n\t\tthis.maxStdoutBytes = this.positiveByteLimit(\n\t\t\toptions.maxStdoutBytes ?? DEFAULT_MAX_STDOUT_BYTES,\n\t\t\t\"maxStdoutBytes\",\n\t\t);\n\t\tthis.maxCapabilityResultBytes = this.positiveByteLimit(\n\t\t\toptions.maxCapabilityResultBytes ?? DEFAULT_MAX_CAPABILITY_RESULT_BYTES,\n\t\t\t\"maxCapabilityResultBytes\",\n\t\t);\n\t\tthis.terminationGraceMs = this.positiveByteLimit(\n\t\t\toptions.terminationGraceMs ?? DEFAULT_TERMINATION_GRACE_MS,\n\t\t\t\"terminationGraceMs\",\n\t\t);\n\t\tif (this.maxStdoutFrameBytes > this.maxStdoutBytes) {\n\t\t\tthrow new Error(\"maxStdoutFrameBytes must not exceed maxStdoutBytes\");\n\t\t}\n\t}\n\n\t/** The execution boundary this process actually launched under. */\n\tget sandboxKind(): EvoComponentSandboxKind | \"direct\" | undefined {\n\t\treturn this.launchedSandbox;\n\t}\n\n\tasync start(): Promise<void> {\n\t\tif (this.stopped) throw new Error(\"Component process cannot be restarted after shutdown\");\n\t\tif (this.child) return;\n\t\tlet launch: ComponentLaunch;\n\t\tif (this.options.sandbox === false) {\n\t\t\tthis.launchedSandbox = \"direct\";\n\t\t\tlaunch = directCommand(this.artifact);\n\t\t} else {\n\t\t\tconst kind = await resolveEvoComponentSandbox();\n\t\t\tif (!kind) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"No component sandbox is available: bwrap cannot create unprivileged namespaces on this host \" +\n\t\t\t\t\t\t\"and Docker is not usable. Fix either, or retry with explicit one-time direct permission.\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.launchedSandbox = kind;\n\t\t\tlaunch = kind === \"bwrap\" ? bwrapCommand(this.artifact) : dockerSandboxCommand(this.artifact);\n\t\t}\n\t\tconst child = spawn(launch.command, launch.args, {\n\t\t\tcwd: this.artifact.directory,\n\t\t\tenv: launch.env,\n\t\t\tstdio: [\"pipe\", \"pipe\", \"pipe\"],\n\t\t\tdetached: process.platform !== \"win32\",\n\t\t});\n\t\tthis.child = child;\n\t\tthis.dockerContainerName = launch.dockerContainerName;\n\t\tthis.childClose = new Promise<void>((resolveClose) => {\n\t\t\tthis.resolveChildClose = resolveClose;\n\t\t});\n\t\tchild.stdout.on(\"data\", (chunk: Buffer | string) => this.handleStdoutData(chunk));\n\t\tchild.stderr.on(\"data\", (chunk: Buffer | string) => {\n\t\t\tif (Buffer.byteLength(this.stderr, \"utf8\") >= MAX_STDERR_BYTES) return;\n\t\t\tthis.stderr += Buffer.isBuffer(chunk) ? chunk.toString(\"utf8\") : chunk;\n\t\t\tif (Buffer.byteLength(this.stderr, \"utf8\") > MAX_STDERR_BYTES) {\n\t\t\t\tthis.stderr = Buffer.from(this.stderr).subarray(0, MAX_STDERR_BYTES).toString(\"utf8\");\n\t\t\t}\n\t\t});\n\t\tchild.once(\"error\", (error) =>\n\t\t\tthis.failProcess(new Error(`Component process failed to start: ${error.message}`)),\n\t\t);\n\t\tchild.once(\"close\", (code, signal) => {\n\t\t\tthis.resolveChildClose();\n\t\t\tvoid this.beginHardTermination();\n\t\t\tthis.child = undefined;\n\t\t\tthis.capabilityAbort.abort(new Error(\"Component process closed\"));\n\t\t\tconst detail = this.stderr.trim();\n\t\t\tconst truncated = !this.stdoutFailed && this.stdoutFrameBytes > 0;\n\t\t\tthis.stdoutFrameBuffer = undefined;\n\t\t\tthis.stdoutFrameBytes = 0;\n\t\t\tthis.failAll(\n\t\t\t\ttruncated\n\t\t\t\t\t? new Error(\"Component process closed with a truncated JSONL frame\")\n\t\t\t\t\t: new Error(`Component process exited (${signal ?? code ?? \"unknown\"})${detail ? `: ${detail}` : \"\"}`),\n\t\t\t);\n\t\t});\n\t\tawait this.request(\"initialize\", {\n\t\t\tabi: this.abi.id,\n\t\t\tcomponent: {\n\t\t\t\tid: this.artifact.manifest.id,\n\t\t\t\tversion: this.artifact.manifest.version,\n\t\t\t\tartifactDigest: this.artifact.manifest.artifactDigest,\n\t\t\t},\n\t\t\tconfig: this.config,\n\t\t});\n\t}\n\n\tasync invoke(input: unknown): Promise<TOutput> {\n\t\tawait this.start();\n\t\tconst validated = this.abi.validateInput(input);\n\t\treturn this.abi.validateOutput(await this.request(\"invoke\", validated));\n\t}\n\n\tasync health(): Promise<unknown> {\n\t\tawait this.start();\n\t\treturn this.request(\"health\");\n\t}\n\n\tasync shutdown(): Promise<void> {\n\t\tif (!this.child) {\n\t\t\tthis.stopped = true;\n\t\t\tawait this.terminationPromise;\n\t\t\tif (this.terminationError) throw this.terminationError;\n\t\t\treturn;\n\t\t}\n\t\tif (!this.stdoutFailed) {\n\t\t\ttry {\n\t\t\t\tawait this.request(\"shutdown\");\n\t\t\t} catch {\n\t\t\t\t// The process may close immediately after acknowledging shutdown.\n\t\t\t}\n\t\t}\n\t\tthis.stopped = true;\n\t\tawait this.beginHardTermination();\n\t\tif (this.terminationError) throw this.terminationError;\n\t}\n\n\t/** Abort active work and immediately begin TERM-to-KILL process teardown. */\n\tasync terminate(reason?: unknown): Promise<void> {\n\t\tconst error =\n\t\t\treason instanceof Error\n\t\t\t\t? reason\n\t\t\t\t: new Error(reason === undefined ? \"Component process terminated\" : String(reason));\n\t\tthis.stopped = true;\n\t\tthis.capabilityAbort.abort(error);\n\t\tthis.failProcess(error);\n\t\tawait this.beginHardTermination();\n\t\tif (this.terminationError) throw this.terminationError;\n\t}\n\n\tprivate request(method: ProcessRequest[\"method\"], payload?: unknown): Promise<unknown> {\n\t\tif (this.stopped) {\n\t\t\tconst reason = this.capabilityAbort.signal.reason;\n\t\t\tthrow reason instanceof Error ? reason : new Error(\"Component process is not running\");\n\t\t}\n\t\tconst child = this.child;\n\t\tif (!child || child.stdin.destroyed) throw new Error(\"Component process is not running\");\n\t\tconst id = this.nextId++;\n\t\tconst timeoutMs = this.options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst timer = setTimeout(() => {\n\t\t\t\tthis.pending.delete(id);\n\t\t\t\tconst error = new Error(`Component ${method} request timed out`);\n\t\t\t\treject(error);\n\t\t\t\tthis.failProcess(error);\n\t\t\t}, timeoutMs);\n\t\t\ttimer.unref?.();\n\t\t\tthis.pending.set(id, { resolve, reject, timer, method });\n\t\t\tchild.stdin.write(\n\t\t\t\t`${JSON.stringify({ id, method, ...(payload === undefined ? {} : { payload }) })}\\n`,\n\t\t\t\t(error) => {\n\t\t\t\t\tif (!error) return;\n\t\t\t\t\tconst pending = this.pending.get(id);\n\t\t\t\t\tif (!pending) return;\n\t\t\t\t\tthis.pending.delete(id);\n\t\t\t\t\tclearTimeout(pending.timer);\n\t\t\t\t\tpending.reject(error);\n\t\t\t\t\tthis.failProcess(error);\n\t\t\t\t},\n\t\t\t);\n\t\t});\n\t}\n\n\tprivate handleLine(line: string): void {\n\t\tlet output: ComponentOutput;\n\t\ttry {\n\t\t\toutput = parseComponentOutput(line);\n\t\t} catch (error) {\n\t\t\tthis.failProcess(error instanceof Error ? error : new Error(String(error)));\n\t\t\treturn;\n\t\t}\n\t\tif (output.kind === \"capability\") {\n\t\t\tthis.handleCapabilityRequest(output.value);\n\t\t\treturn;\n\t\t}\n\t\tconst response = output.value;\n\t\tconst pending = this.pending.get(response.id);\n\t\tif (!pending) {\n\t\t\tthis.failProcess(new Error(`Component process returned an unknown response id: ${response.id}`));\n\t\t\treturn;\n\t\t}\n\t\tif (\n\t\t\tpending.method === \"invoke\" &&\n\t\t\t[...this.inFlightCapabilities].some((key) => key.startsWith(`${response.id}:`))\n\t\t) {\n\t\t\tthis.failProcess(new Error(`Component invoke ${response.id} completed with outstanding capability requests`));\n\t\t\treturn;\n\t\t}\n\t\tthis.pending.delete(response.id);\n\t\tthis.seenCapabilityIds.delete(response.id);\n\t\tclearTimeout(pending.timer);\n\t\tif (response.ok) pending.resolve(response.result);\n\t\telse pending.reject(new Error(response.error || \"Component process request failed\"));\n\t}\n\n\tprivate positiveByteLimit(value: number, label: string): number {\n\t\tif (!Number.isSafeInteger(value) || value <= 0) throw new Error(`${label} must be a positive safe integer`);\n\t\treturn value;\n\t}\n\n\tprivate appendStdoutFrameChunk(chunk: Buffer): boolean {\n\t\tif (chunk.byteLength === 0) return true;\n\t\tconst requiredBytes = this.stdoutFrameBytes + chunk.byteLength;\n\t\tif (requiredBytes > this.maxStdoutFrameBytes) {\n\t\t\tthis.failProcess(new Error(\"Component process stdout frame exceeds the configured byte limit\"));\n\t\t\treturn false;\n\t\t}\n\t\tif (!this.stdoutFrameBuffer || this.stdoutFrameBuffer.byteLength < requiredBytes) {\n\t\t\tlet capacity =\n\t\t\t\tthis.stdoutFrameBuffer?.byteLength ?? Math.min(INITIAL_STDOUT_FRAME_BUFFER_BYTES, this.maxStdoutFrameBytes);\n\t\t\twhile (capacity < requiredBytes) capacity = Math.min(this.maxStdoutFrameBytes, capacity * 2);\n\t\t\tconst expanded = Buffer.allocUnsafe(capacity);\n\t\t\tthis.stdoutFrameBuffer?.copy(expanded, 0, 0, this.stdoutFrameBytes);\n\t\t\tthis.stdoutFrameBuffer = expanded;\n\t\t}\n\t\tchunk.copy(this.stdoutFrameBuffer, this.stdoutFrameBytes);\n\t\tthis.stdoutFrameBytes = requiredBytes;\n\t\treturn true;\n\t}\n\n\tprivate handleStdoutData(value: Buffer | string): void {\n\t\tif (this.stdoutFailed) return;\n\t\tconst chunk = Buffer.isBuffer(value) ? value : Buffer.from(value);\n\t\tthis.stdoutBytes += chunk.byteLength;\n\t\tif (this.stdoutBytes > this.maxStdoutBytes) {\n\t\t\tthis.failProcess(new Error(\"Component process stdout exceeds the configured process byte limit\"));\n\t\t\treturn;\n\t\t}\n\t\tlet start = 0;\n\t\tfor (let index = 0; index < chunk.byteLength; index += 1) {\n\t\t\tif (chunk[index] !== 0x0a) continue;\n\t\t\tif (!this.appendStdoutFrameChunk(chunk.subarray(start, index))) return;\n\t\t\tlet frame = this.stdoutFrameBuffer?.subarray(0, this.stdoutFrameBytes) ?? Buffer.alloc(0);\n\t\t\tthis.stdoutFrameBytes = 0;\n\t\t\tif (frame.at(-1) === 0x0d) frame = frame.subarray(0, -1);\n\t\t\tthis.handleLine(frame.toString(\"utf8\"));\n\t\t\tif (this.stdoutFailed) return;\n\t\t\tstart = index + 1;\n\t\t}\n\t\tthis.appendStdoutFrameChunk(chunk.subarray(start));\n\t}\n\n\tprivate handleCapabilityRequest(frame: EvoCapabilityRequestFrame): void {\n\t\tconst pending = this.pending.get(frame.invokeId);\n\t\tif (!pending || pending.method !== \"invoke\") {\n\t\t\tthis.failProcess(new Error(`Capability request references a non-active invoke: ${frame.invokeId}`));\n\t\t\treturn;\n\t\t}\n\t\tlet seen = this.seenCapabilityIds.get(frame.invokeId);\n\t\tif (!seen) {\n\t\t\tseen = new Set();\n\t\t\tthis.seenCapabilityIds.set(frame.invokeId, seen);\n\t\t}\n\t\tif (seen.has(frame.id)) {\n\t\t\tthis.failProcess(new Error(`Duplicate capability request id for invoke ${frame.invokeId}: ${frame.id}`));\n\t\t\treturn;\n\t\t}\n\t\tif (this.inFlightCapabilities.size >= MAX_CONCURRENT_CAPABILITY_REQUESTS) {\n\t\t\tthis.failProcess(new Error(\"Component exceeded the concurrent capability request limit\"));\n\t\t\treturn;\n\t\t}\n\t\tseen.add(frame.id);\n\t\tconst key = `${frame.invokeId}:${frame.id}`;\n\t\tthis.inFlightCapabilities.add(key);\n\t\tconst identity: EvoCapabilityComponentIdentity = {\n\t\t\tid: this.artifact.manifest.id,\n\t\t\tabi: this.artifact.manifest.abi,\n\t\t\tartifactDigest: this.artifact.manifest.artifactDigest,\n\t\t\tdeclaredCapabilities: this.artifact.manifest.capabilities,\n\t\t\tabiCapabilityCeiling: this.abi.capabilityCeiling,\n\t\t};\n\t\tconst result = this.options.capabilityBroker\n\t\t\t? this.options.capabilityBroker.request(identity, frame, this.capabilityAbort.signal)\n\t\t\t: Promise.reject(Object.assign(new Error(\"Capability broker is unavailable\"), { code: \"unavailable\" }));\n\t\tvoid result.then(\n\t\t\t(value) => this.finishCapabilityRequest(key, frame, capabilitySuccessFrame(frame, value)),\n\t\t\t(error: unknown) => {\n\t\t\t\tconst code =\n\t\t\t\t\ttypeof error === \"object\" &&\n\t\t\t\t\terror !== null &&\n\t\t\t\t\t\"code\" in error &&\n\t\t\t\t\ttypeof error.code === \"string\" &&\n\t\t\t\t\t/^[a-z][a-z0-9_-]{0,63}$/.test(error.code)\n\t\t\t\t\t\t? error.code\n\t\t\t\t\t\t: \"service_error\";\n\t\t\t\tconst message =\n\t\t\t\t\t(error instanceof Error ? error.message : String(error)).slice(0, 1_024) || \"Capability failed\";\n\t\t\t\tthis.finishCapabilityRequest(key, frame, capabilityErrorFrame(frame, code, message));\n\t\t\t},\n\t\t);\n\t}\n\n\tprivate finishCapabilityRequest(key: string, frame: EvoCapabilityRequestFrame, result: unknown): void {\n\t\tif (!this.inFlightCapabilities.delete(key)) return;\n\t\tif (!this.pending.has(frame.invokeId)) {\n\t\t\tthis.failProcess(new Error(`Capability result arrived after invoke ${frame.invokeId} completed`));\n\t\t\treturn;\n\t\t}\n\t\tconst child = this.child;\n\t\tif (!child || child.stdin.destroyed) {\n\t\t\tthis.failProcess(new Error(\"Component process closed before a capability result could be delivered\"));\n\t\t\treturn;\n\t\t}\n\t\tlet serialized: string | undefined;\n\t\ttry {\n\t\t\tserialized = JSON.stringify(result);\n\t\t} catch (error) {\n\t\t\tthis.failProcess(new Error(\"Capability result is not JSON serializable\", { cause: error }));\n\t\t\treturn;\n\t\t}\n\t\tif (serialized === undefined) {\n\t\t\tthis.failProcess(new Error(\"Capability result is not JSON serializable\"));\n\t\t\treturn;\n\t\t}\n\t\tconst line = `${serialized}\\n`;\n\t\tif (Buffer.byteLength(line, \"utf8\") > this.maxCapabilityResultBytes) {\n\t\t\tthis.failProcess(new Error(\"Capability result exceeds the configured byte limit\"));\n\t\t\treturn;\n\t\t}\n\t\tchild.stdin.write(line, (error) => {\n\t\t\tif (error) this.failProcess(error);\n\t\t});\n\t}\n\n\tprivate failProcess(error: Error): void {\n\t\tif (this.stdoutFailed) return;\n\t\tthis.stdoutFailed = true;\n\t\tthis.capabilityAbort.abort(error);\n\t\tvoid this.beginHardTermination();\n\t\tthis.failAll(error);\n\t}\n\n\tprivate signalChild(child: ChildProcessWithoutNullStreams, signal: NodeJS.Signals): void {\n\t\ttry {\n\t\t\tif (process.platform !== \"win32\" && child.pid !== undefined) process.kill(-child.pid, signal);\n\t\t\telse child.kill(signal);\n\t\t} catch (error) {\n\t\t\tif (typeof error === \"object\" && error !== null && \"code\" in error && error.code === \"ESRCH\") return;\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprivate waitForChildClose(closed: Promise<void>): Promise<boolean> {\n\t\treturn new Promise((resolvePromise) => {\n\t\t\tconst timer = setTimeout(() => resolvePromise(false), this.terminationGraceMs);\n\t\t\ttimer.unref();\n\t\t\tvoid closed.then(() => {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\tresolvePromise(true);\n\t\t\t});\n\t\t});\n\t}\n\n\tprivate processGroupExists(pid: number): boolean {\n\t\ttry {\n\t\t\tprocess.kill(-pid, 0);\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\tif (typeof error === \"object\" && error !== null && \"code\" in error && error.code === \"ESRCH\") return false;\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprivate async waitForProcessGroupExit(pid: number): Promise<boolean> {\n\t\tconst deadline = Date.now() + this.terminationGraceMs;\n\t\twhile (this.processGroupExists(pid)) {\n\t\t\tconst remaining = deadline - Date.now();\n\t\t\tif (remaining <= 0) return false;\n\t\t\tawait new Promise<void>((resolvePromise) => setTimeout(resolvePromise, Math.min(remaining, 10)));\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate async dockerContainerState(name: string): Promise<\"running\" | \"stopped\" | \"absent\"> {\n\t\tconst result = await runDockerControlCommand([\"container\", \"inspect\", \"--format\", \"{{.State.Running}}\", name]);\n\t\tif (result.code === 0) {\n\t\t\tconst state = result.stdout.trim();\n\t\t\tif (state === \"true\") return \"running\";\n\t\t\tif (state === \"false\") return \"stopped\";\n\t\t\tthrow new Error(`Docker returned an invalid container state for ${name}`);\n\t\t}\n\t\tif (/No such (object|container)/i.test(result.stderr)) return \"absent\";\n\t\tthrow new Error(`Docker could not inspect container ${name}: ${result.stderr.trim() || `exit ${result.code}`}`);\n\t}\n\n\tprivate async signalDockerContainer(name: string, signal: \"TERM\" | \"KILL\"): Promise<void> {\n\t\tif ((await this.dockerContainerState(name)) !== \"running\") return;\n\t\tconst result = await runDockerControlCommand([\"kill\", \"--signal\", signal, name]);\n\t\tif (result.code === 0) return;\n\t\tif ((await this.dockerContainerState(name)) !== \"running\") return;\n\t\tthrow new Error(\n\t\t\t`Docker could not send ${signal} to container ${name}: ${result.stderr.trim() || `exit ${result.code}`}`,\n\t\t);\n\t}\n\n\tprivate async waitForDockerContainerExit(name: string): Promise<boolean> {\n\t\tconst deadline = Date.now() + this.terminationGraceMs;\n\t\twhile ((await this.dockerContainerState(name)) === \"running\") {\n\t\t\tconst remaining = deadline - Date.now();\n\t\t\tif (remaining <= 0) return false;\n\t\t\tawait new Promise<void>((resolvePromise) => setTimeout(resolvePromise, Math.min(remaining, 50)));\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate async removeDockerContainer(name: string): Promise<void> {\n\t\tif ((await this.dockerContainerState(name)) === \"absent\") return;\n\t\tconst result = await runDockerControlCommand([\"rm\", \"--force\", name]);\n\t\tif (result.code !== 0 && (await this.dockerContainerState(name)) !== \"absent\") {\n\t\t\tthrow new Error(`Docker could not remove container ${name}: ${result.stderr.trim() || `exit ${result.code}`}`);\n\t\t}\n\t\tif ((await this.dockerContainerState(name)) !== \"absent\") {\n\t\t\tthrow new Error(`Docker container ${name} still exists after forced removal`);\n\t\t}\n\t}\n\n\tprivate async terminateDockerContainer(\n\t\tname: string,\n\t\tchild: ChildProcessWithoutNullStreams,\n\t\tclosed: Promise<void>,\n\t): Promise<void> {\n\t\tawait this.signalDockerContainer(name, \"TERM\");\n\t\tif (!(await this.waitForDockerContainerExit(name))) {\n\t\t\tawait this.signalDockerContainer(name, \"KILL\");\n\t\t\tif (!(await this.waitForDockerContainerExit(name))) await this.removeDockerContainer(name);\n\t\t}\n\t\tawait this.removeDockerContainer(name);\n\t\tif (!(await this.waitForChildClose(closed))) {\n\t\t\tthis.signalChild(child, \"SIGKILL\");\n\t\t\tif (!(await this.waitForChildClose(closed))) {\n\t\t\t\tthrow new Error(\"Docker CLI did not close after its container was removed\");\n\t\t\t}\n\t\t}\n\t\t// The CLI may be killed while its create request is still reaching the\n\t\t// daemon. Recheck by the unique name only after the CLI has closed.\n\t\tawait this.removeDockerContainer(name);\n\t}\n\n\tprivate beginHardTermination(): Promise<void> {\n\t\tif (this.terminationPromise) return this.terminationPromise;\n\t\tconst child = this.child;\n\t\tconst closed = this.childClose;\n\t\tif (!child || !closed) return Promise.resolve();\n\t\tthis.terminationPromise = (async () => {\n\t\t\ttry {\n\t\t\t\tif (this.launchedSandbox === \"docker\" && this.dockerContainerName) {\n\t\t\t\t\tawait this.terminateDockerContainer(this.dockerContainerName, child, closed);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis.signalChild(child, \"SIGTERM\");\n\t\t\t\tconst terminated =\n\t\t\t\t\tprocess.platform !== \"win32\" && child.pid !== undefined\n\t\t\t\t\t\t? await this.waitForProcessGroupExit(child.pid)\n\t\t\t\t\t\t: await this.waitForChildClose(closed);\n\t\t\t\tif (terminated) {\n\t\t\t\t\tif (!(await this.waitForChildClose(closed))) {\n\t\t\t\t\t\tthrow new Error(\"Component process group exited without closing its leader\");\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis.signalChild(child, \"SIGKILL\");\n\t\t\t\tif (process.platform !== \"win32\" && child.pid !== undefined) {\n\t\t\t\t\tif (!(await this.waitForProcessGroupExit(child.pid))) {\n\t\t\t\t\t\tthrow new Error(\"Component process group still exists after SIGKILL\");\n\t\t\t\t\t}\n\t\t\t\t\tif (!(await this.waitForChildClose(closed))) {\n\t\t\t\t\t\tthrow new Error(\"Component process group exited without closing its leader after SIGKILL\");\n\t\t\t\t\t}\n\t\t\t\t} else if (!(await this.waitForChildClose(closed))) {\n\t\t\t\t\tthrow new Error(\"Component process did not close after SIGKILL\");\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\ttry {\n\t\t\t\t\tthis.signalChild(child, \"SIGKILL\");\n\t\t\t\t\tif (process.platform !== \"win32\" && child.pid !== undefined) {\n\t\t\t\t\t\tawait this.waitForProcessGroupExit(child.pid);\n\t\t\t\t\t}\n\t\t\t\t\tawait this.waitForChildClose(closed);\n\t\t\t\t} catch {\n\t\t\t\t\t// Preserve the daemon/group teardown error below.\n\t\t\t\t}\n\t\t\t\tthis.terminationError = new Error(\"Component process hard teardown failed\", { cause: error });\n\t\t\t}\n\t\t})();\n\t\treturn this.terminationPromise;\n\t}\n\n\tprivate failAll(error: Error): void {\n\t\tthis.capabilityAbort.abort(error);\n\t\tfor (const pending of this.pending.values()) {\n\t\t\tclearTimeout(pending.timer);\n\t\t\tpending.reject(error);\n\t\t}\n\t\tthis.pending.clear();\n\t\tthis.inFlightCapabilities.clear();\n\t\tthis.seenCapabilityIds.clear();\n\t}\n}\n"]}