diff --git a/dist/live-checkpoint-ClPCSdrW.js b/dist/live-checkpoint-ClPCSdrW.js index 243c9d13bcba520923b63adfddad75cf2d94362d..e2a254d4e57fb2e246ccb742f2e9de5a8a461d20 100644 --- a/dist/live-checkpoint-ClPCSdrW.js +++ b/dist/live-checkpoint-ClPCSdrW.js @@ -1532,7 +1532,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", @@ -2557,7 +2557,7 @@ function readCommandLineChar(state) { escaping: false, hasPart: true }; - if (state.ch === "\\" && state.quote !== "'") return { + if (process.platform !== "win32" && state.ch === "\\" && state.quote !== "'") return { current: state.current, quote: state.quote, escaping: true, @@ -2912,8 +2912,8 @@ function promotePrefixedAuthEnvironment(env) { } return protectedKeys; } -function buildAgentEnvironment(authCredentials, sessionEnv) { - const env = { ...process.env }; +function buildAgentEnvironment(authCredentials, sessionEnv, inheritProcessEnv = true) { + const env = inheritProcessEnv ? { ...process.env } : {}; const protectedAuthEnvKeys = promotePrefixedAuthEnvironment(env); if (authCredentials) for (const [methodId, credential] of Object.entries(authCredentials)) { addAuthCredentialEnvKeys(protectedAuthEnvKeys, methodId, credential); @@ -2955,10 +2955,10 @@ function resolveConfiguredAuthCredential(methodId, authCredentials) { const configCredentials = authCredentials ?? {}; return configCredentials[methodId] ?? configCredentials[toEnvToken(methodId)]; } -function buildAgentSpawnOptions(cwd, authCredentials, sessionEnv) { +function buildAgentSpawnOptions(cwd, authCredentials, sessionEnv, inheritProcessEnv) { return { cwd, - env: buildAgentEnvironment(authCredentials, sessionEnv), + env: buildAgentEnvironment(authCredentials, sessionEnv, inheritProcessEnv), stdio: [ "pipe", "pipe", @@ -3724,10 +3724,13 @@ function resolveClientCapabilities(params) { }, terminal: params.terminal }; - if (!params.devinAcp) return baseCapabilities; + const typedSessionFailureMeta = { + jetbrains: { air: { version: 1, capabilities: ["sessionFailure"] } } + }; + if (!params.devinAcp) return { ...baseCapabilities, _meta: typedSessionFailureMeta }; return { ...baseCapabilities, - _meta: DEVIN_COMPATIBILITY_CLIENT_CAPABILITIES_META + _meta: { ...typedSessionFailureMeta, ...DEVIN_COMPATIBILITY_CLIENT_CAPABILITIES_META } }; } function isDevinRequestDiagnosticsMethod(method) { @@ -3957,9 +3960,26 @@ var AcpClient = class { this.lastAgentExit = void 0; this.lastKnownPid = child.pid ?? void 0; this.attachAgentLifecycleObservers(child); + if (this.options.onAgentSpawn) { + try { + if (typeof child.pid !== "number" || child.pid <= 0) { + 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); + if (this.options.onAgentStderr) { + this.options.onAgentStderr(chunk.toString()); + return; + } if (!this.options.verbose) return; process.stderr.write(chunk); }); @@ -3994,7 +4014,7 @@ 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.inheritProcessEnv) }; } logAgentLaunch(plan) { @@ -6070,6 +6090,27 @@ async function withConnectedSession(options) { //#region src/runtime/engine/prompt-turn.ts const SESSION_REPLY_IDLE_MS = 1e3; const SESSION_REPLY_DRAIN_TIMEOUT_MS = 5e3; +const TYPED_SESSION_FAILURE_CATEGORIES = /* @__PURE__ */ new Set([ + "connection", + "access", + "limit", + "service", + "request", + "unknown" +]); +function typedTerminalSessionFailureCategory(response) { + if (response === null || typeof response !== "object" || Array.isArray(response)) return null; + const meta = response._meta; + if (meta === null || typeof meta !== "object" || Array.isArray(meta)) return null; + const jetbrains = meta.jetbrains; + if (jetbrains === null || typeof jetbrains !== "object" || Array.isArray(jetbrains)) return null; + const air = jetbrains.air; + if (air === null || typeof air !== "object" || Array.isArray(air)) return null; + if (!Number.isInteger(air.version) || air.version < 1) return null; + const failure = air.sessionFailure; + if (failure === null || typeof failure !== "object" || Array.isArray(failure) || failure.severity !== "error") return null; + return typeof failure.category === "string" && TYPED_SESSION_FAILURE_CATEGORIES.has(failure.category) ? failure.category : "unknown"; +} async function runPromptTurn(params) { try { const promptPromise = params.client.prompt(params.sessionId, params.prompt); @@ -6079,6 +6120,20 @@ async function runPromptTurn(params) { idleMs: SESSION_REPLY_IDLE_MS, timeoutMs: SESSION_REPLY_DRAIN_TIMEOUT_MS }).catch(() => {}); + const terminalFailureCategory = typedTerminalSessionFailureCategory(response); + if (terminalFailureCategory !== null) { + // Pass complete provider text to the diagnostic callback. Consumers redact + // before bounding it; truncating here can split and expose a credential. + const failure = response._meta.jetbrains.air.sessionFailure; + try { + params.onTerminalSessionFailure?.({ + category: terminalFailureCategory, + ...(typeof failure.title === "string" ? { title: failure.title } : {}), + ...(typeof failure.details === "string" ? { details: failure.details } : {}) + }); + } catch {} + throw new Error(`ACP agent reported a terminal ${terminalFailureCategory} failure.`); + } recordPromptResponseUsage(params.conversation, response.usage, params.promptMessageId); return { stopReason: response.stopReason, diff --git a/dist/runtime.d.ts b/dist/runtime.d.ts index ccdbe5b032521518022223733049b8b38793473b..416bdaae1450c6e85b34afd154349770f9f7b0a9 100644 --- a/dist/runtime.d.ts +++ b/dist/runtime.d.ts @@ -45,6 +45,7 @@ type AcpRuntimeTurnAttachment = { data: string; }; type AcpRuntimeTurnInput = { + onTerminalSessionFailure?: (failure: { category: string; title?: string; details?: string }) => void; handle: AcpRuntimeHandle; text: string; attachments?: AcpRuntimeTurnAttachment[]; @@ -266,6 +267,10 @@ type AcpRuntimeOptions = { timeoutMs?: number; probeAgent?: string; verbose?: boolean; + onAgentStderr?: (chunk: string) => void; + onAgentSpawn?: (meta: { pid: number; startedAt: string }) => Promise; + spawnCwd?: string; + inheritProcessEnv?: boolean; onPermissionRequest?: (req: AcpPermissionRequest, ctx: { signal: AbortSignal; }) => Promise; @@ -302,6 +307,7 @@ declare class AcpRuntimeManager { private createAndSaveRuntimeRecord; private keepPersistentClient; startTurn(input: { + onTerminalSessionFailure?: (failure: { category: string; title?: string; details?: string }) => void; handle: AcpRuntimeHandle; text: string; attachments?: AcpRuntimeTurnAttachment[]; @@ -334,6 +340,7 @@ declare class AcpRuntimeManager { private finalizeRuntimeTurn; private finalizeRuntimeTurnRecord; runTurn(input: { + onTerminalSessionFailure?: (failure: { category: string; title?: string; details?: string }) => void; handle: AcpRuntimeHandle; text: string; attachments?: AcpRuntimeTurnAttachment[]; diff --git a/dist/runtime.js b/dist/runtime.js index 6c9cc999e50a11c399c68b3a0f1b7af4bc2317c0..e06bf4fdee534f3ef56be0387a2f5f8626e697ad 100644 --- a/dist/runtime.js +++ b/dist/runtime.js @@ -744,7 +744,8 @@ var AcpRuntimeManager = class { this.deps = deps; } createClient(options) { - return this.deps.clientFactory?.(options) ?? new AcpClient(options); + const clientOptions = { ...options, onAgentStderr: this.options.onAgentStderr, onAgentSpawn: this.options.onAgentSpawn, spawnCwd: this.options.spawnCwd, inheritProcessEnv: this.options.inheritProcessEnv }; + return this.deps.clientFactory?.(clientOptions) ?? new AcpClient(clientOptions); } async readPendingPersistentClient(record, options) { const pendingClient = this.pendingPersistentClients.get(record.acpxRecordId); @@ -982,6 +983,7 @@ var AcpRuntimeManager = class { client: turn.client, sessionId, prompt: task.promptInput, + onTerminalSessionFailure: task.input.onTerminalSessionFailure, timeoutMs: task.input.timeoutMs ?? this.options.timeoutMs, conversation: turn.conversation, promptMessageId: turn.promptMessageId @@ -1586,6 +1588,7 @@ var AcpxRuntime = class { const turnPromise = this.getManager().then((manager) => manager.startTurn({ handle, text: input.text, + onTerminalSessionFailure: input.onTerminalSessionFailure, attachments: input.attachments, mode: input.mode, sessionMode: state.mode, @@ -1614,6 +1617,7 @@ var AcpxRuntime = class { yield* (await this.getManager()).runTurn({ handle, text: input.text, + onTerminalSessionFailure: input.onTerminalSessionFailure, attachments: input.attachments, mode: input.mode, sessionMode: state.mode, diff --git a/dist/session-options-jkYbBxGE.d.ts b/dist/session-options-jkYbBxGE.d.ts index 9d37f377fb6a0828e0d2bc5a48754f3aa71509a4..22dda59e0b7616a20ad45218913e5d8f884bae49 100644 --- a/dist/session-options-jkYbBxGE.d.ts +++ b/dist/session-options-jkYbBxGE.d.ts @@ -84,6 +84,10 @@ type AcpClientOptions = { terminal?: boolean; suppressSdkConsoleErrors?: boolean; verbose?: boolean; + onAgentStderr?: (chunk: string) => void; + onAgentSpawn?: (meta: { pid: number; startedAt: string }) => Promise; + spawnCwd?: string; + inheritProcessEnv?: boolean; sessionOptions?: { model?: string; allowedTools?: string[];