diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js index d454fd7c5bf742b469be75eb8c3988b694a0ffaa..5ddcd5cfcb80fd3417172a5e618f1e983e200644 100644 --- a/dist/live-checkpoint-BSIrfgVo.js +++ b/dist/live-checkpoint-BSIrfgVo.js @@ -1661,7 +1661,7 @@ const ZED_TAG_KEYS = /* @__PURE__ */ new Set([ "RedactedThinking", "ToolUse" ]); -const MAP_OBJECT_PATHS = /* @__PURE__ */ new Set(["request_token_usage", "messages.Agent.tool_results"]); +const MAP_OBJECT_PATHS = /* @__PURE__ */ new Set(["request_token_usage", "messages.Agent.tool_results", "acpx.session_options.env"]); const OPAQUE_VALUE_PATHS = /* @__PURE__ */ new Set([ "agent_capabilities", "messages.Agent.content.ToolUse.input", @@ -3135,8 +3135,8 @@ function promotePrefixedAuthEnvironment(env) { } return protectedKeys; } -function buildAgentEnvironment(authCredentials, sessionEnv) { - const env = { ...process.env }; +function buildAgentEnvironment(authCredentials, sessionEnv, spawnEnvironment) { + const env = spawnEnvironment ? { ...spawnEnvironment } : { ...process.env }; const protectedAuthEnvKeys = promotePrefixedAuthEnvironment(env); if (authCredentials) for (const [methodId, credential] of Object.entries(authCredentials)) { addAuthCredentialEnvKeys(protectedAuthEnvKeys, methodId, credential); @@ -3178,10 +3178,10 @@ function resolveConfiguredAuthCredential(methodId, authCredentials) { const configCredentials = authCredentials ?? {}; return configCredentials[methodId] ?? configCredentials[toEnvToken(methodId)]; } -function buildAgentSpawnOptions(cwd, authCredentials, sessionEnv) { +function buildAgentSpawnOptions(cwd, authCredentials, sessionEnv, spawnEnvironment) { return { cwd, - env: buildAgentEnvironment(authCredentials, sessionEnv), + env: buildAgentEnvironment(authCredentials, sessionEnv, spawnEnvironment), stdio: [ "pipe", "pipe", @@ -4216,9 +4216,21 @@ var AcpClient = class { this.lastAgentExit = void 0; this.lastKnownPid = child.pid ?? void 0; this.attachAgentLifecycleObservers(child); + if (this.options.onAgentSpawn) { + try { + if (!child.pid) throw new Error("ACPX agent spawn did not expose a valid process id."); + await this.options.onAgentSpawn({ pid: child.pid, startedAt: this.agentStartedAt }); + } catch (error) { + try { + child.kill("SIGKILL"); + } catch {} + throw error; + } + } const startupStderr = []; child.stderr.on("data", (chunk) => { this.captureStartupStderr(startupStderr, chunk); + this.options.onAgentStderr?.(String(chunk)); if (!this.options.verbose) return; process.stderr.write(chunk); }); @@ -4253,7 +4265,12 @@ var AcpClient = class { geminiAcp: isGeminiAcpCommand(spawnCommand, args), copilotAcp: isCopilotAcpCommand(spawnCommand, args), claudeAcp: isClaudeAcpCommand(spawnCommand, args), - spawnOptions: buildAgentSpawnOptions(this.options.cwd, this.options.authCredentials, this.options.sessionOptions?.env) + spawnOptions: buildAgentSpawnOptions( + this.options.spawnCwd ?? this.options.cwd, + this.options.authCredentials, + this.options.sessionOptions?.env, + this.options.spawnEnvironment?.() + ) }; } logAgentLaunch(plan) { @@ -5028,6 +5045,12 @@ var AcpClient = class { attachAgentLifecycleObservers(child) { child.once("exit", (exitCode, signal) => { this.recordAgentExit("process_exit", exitCode, signal); + this.options.onAgentExit?.({ + pid: child.pid ?? this.lastKnownPid, + exitCode, + signal, + exitedAt: isoNow$1() + }); }); child.once("close", (exitCode, signal) => { this.recordAgentExit("process_close", exitCode, signal); diff --git a/dist/runtime.d.ts b/dist/runtime.d.ts index e8102acb03c4c38830ad5ec22f356125eb0423b7..efea5921f1c5dd5523325bc443469faef45e2233 100644 --- a/dist/runtime.d.ts +++ b/dist/runtime.d.ts @@ -1,7 +1,7 @@ import { _ as SessionRecord, a as AcpElicitationHandler, c as AcpElicitationResponse, f as McpServer$1, h as PermissionPolicy, i as AcpElicitationContext, l as AcpPermissionDecision, m as PermissionMode, n as SystemPromptOption, o as AcpElicitationMode, p as NonInteractivePermissionPolicy, s as AcpElicitationRequest, t as SessionAgentOptions, u as AcpPermissionRequest } from "./session-options-DwRDODlr.js"; import { a as RequestedModelUnsupportedErrorCode, i as RequestedModelUnsupportedError, n as REQUESTED_MODEL_UNSUPPORTED_ERROR_CODE, o as RequestedModelUnsupportedReason, r as REQUESTED_MODEL_UNSUPPORTED_REASONS, s as isRequestedModelUnsupportedError, t as AcpClient } from "./client-CxNllqui.js"; import fs from "node:fs"; -import { ToolCallContent, ToolCallLocation, ToolKind } from "@agentclientprotocol/sdk"; +import { SessionNotification, ToolCallContent, ToolCallLocation, ToolKind } from "@agentclientprotocol/sdk"; //#region src/agent-registry.d.ts declare const DEFAULT_AGENT_NAME = "codex"; //#endregion @@ -313,6 +313,29 @@ type AcpRuntimeOptions = { onPermissionRequest?: (req: AcpPermissionRequest, ctx: { signal: AbortSignal; }) => Promise; + /** Ephemeral allowlisted environment evaluated immediately before child spawn. */ + spawnEnvironment?: () => Record; + /** Host-only spawn cwd; does not change the cwd advertised in session/new. */ + spawnCwd?: string; + onAgentSpawn?: (meta: { pid: number; startedAt: string }) => Promise | void; + onAgentStderr?: (chunk: string) => void; + onAcpMessage?: (direction: "inbound" | "outbound", message: unknown) => void; + onAgentExit?: (meta: { + pid?: number; + exitCode: number | null; + signal: NodeJS.Signals | null; + exitedAt: string; + }) => void; + /** Ephemeral full ACP notification callback; never written to session records. */ + onSessionNotification?: (notification: SessionNotification) => void; + /** Ephemeral normalized ACP client filesystem/terminal operation callback. */ + onClientOperation?: (operation: { + method: string; + status: string; + summary: string; + details?: string; + timestamp: string; + }) => void; }; type AcpFileSessionStoreOptions = { stateDir: string; diff --git a/dist/runtime.js b/dist/runtime.js index a1f4a70a003792c6eacf68b6b038f37bfec1db53..ee3e155db800222aaf2353906dbad93698d17b74 100644 --- a/dist/runtime.js +++ b/dist/runtime.js @@ -812,7 +812,16 @@ var AcpRuntimeManager = class { this.deps = deps; } createClient(options) { - return this.deps.clientFactory?.(options) ?? new AcpClient(options); + const patchedOptions = { + ...options, + spawnCwd: this.options.spawnCwd, + spawnEnvironment: this.options.spawnEnvironment, + onAgentSpawn: this.options.onAgentSpawn, + onAgentStderr: this.options.onAgentStderr, + onAgentExit: this.options.onAgentExit, + onAcpMessage: this.options.onAcpMessage + }; + return this.deps.clientFactory?.(patchedOptions) ?? new AcpClient(patchedOptions); } createSessionOwner(input) { const owner = { @@ -823,10 +832,14 @@ var AcpRuntimeManager = class { input.client.setEventHandlers({ + onAcpMessage: this.options.onAcpMessage, onSessionUpdate: (notification) => this.routeOwnedSessionUpdate(owner, notification), onClientOperation: (operation) => this.routeOwnedClientOperation(owner, operation) }); return owner; } routeOwnedSessionUpdate(owner, notification) { + try { + this.options.onSessionNotification?.(notification); + } catch {} const active = owner.activeTurn; if (active) { const { task, turn } = active; @@ -863,6 +874,9 @@ var AcpRuntimeManager = class { projection.checkpoint.request(); } routeOwnedClientOperation(owner, operation) { + try { + this.options.onClientOperation?.(operation); + } catch {} const active = owner.activeTurn; if (!active) return; const { task, turn } = active; diff --git a/dist/session-options-DwRDODlr.d.ts b/dist/session-options-DwRDODlr.d.ts index c3da1645235bbea22de3f8484149051cd7dca56b..a69d7327b8eaade69ee90a6bc2493b682f724084 100644 --- a/dist/session-options-DwRDODlr.d.ts +++ b/dist/session-options-DwRDODlr.d.ts @@ -116,6 +116,18 @@ type AcpClientOptions = { }; env?: Record; }; + /** Ephemeral child environment factory; its return value is never persisted. */ + spawnEnvironment?: () => Record; + /** Host-only child cwd, separate from the cwd advertised to ACP. */ + spawnCwd?: string; + onAgentSpawn?: (meta: { pid: number; startedAt: string }) => Promise | void; + onAgentStderr?: (chunk: string) => void; + onAgentExit?: (meta: { + pid?: number; + exitCode: number | null; + signal: NodeJS.Signals | null; + exitedAt: string; + }) => void; onAcpMessage?: (direction: AcpMessageDirection, message: AcpJsonRpcMessage) => void; onAcpOutputMessage?: (direction: AcpMessageDirection, message: AcpJsonRpcMessage) => void; onSessionUpdate?: (notification: SessionNotification) => void;