acpx 0.14.0 → 0.15.1

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.
Files changed (32) hide show
  1. package/README.md +3 -3
  2. package/dist/{cli-DSwJ-0g-.js → cli-BHAT_D3V.js} +4 -4
  3. package/dist/{cli-DSwJ-0g-.js.map → cli-BHAT_D3V.js.map} +1 -1
  4. package/dist/cli.d.ts +1 -1
  5. package/dist/cli.js +8 -7
  6. package/dist/cli.js.map +1 -1
  7. package/dist/{client-IfFkZz0S.d.ts → client-Cvz6msGc.d.ts} +6 -2
  8. package/dist/client-Cvz6msGc.d.ts.map +1 -0
  9. package/dist/{flags-ftdtbXWd.js → flags-BHcZWTov.js} +3 -3
  10. package/dist/{flags-ftdtbXWd.js.map → flags-BHcZWTov.js.map} +1 -1
  11. package/dist/{flows-DEook1GU.js → flows-BOI1XkMp.js} +52 -11
  12. package/dist/flows-BOI1XkMp.js.map +1 -0
  13. package/dist/flows.d.ts +4 -2
  14. package/dist/flows.d.ts.map +1 -1
  15. package/dist/flows.js +1 -1
  16. package/dist/{live-checkpoint-BW9JivEG.js → live-checkpoint-sB7dFOYR.js} +272 -86
  17. package/dist/live-checkpoint-sB7dFOYR.js.map +1 -0
  18. package/dist/{output-jb0298L8.js → output-8-D6Gd4Z.js} +41 -9
  19. package/dist/output-8-D6Gd4Z.js.map +1 -0
  20. package/dist/runtime.d.ts +10 -3
  21. package/dist/runtime.d.ts.map +1 -1
  22. package/dist/runtime.js +45 -7
  23. package/dist/runtime.js.map +1 -1
  24. package/dist/{session-options-DwRDODlr.d.ts → session-options-DyIGRNfu.d.ts} +48 -2
  25. package/dist/session-options-DyIGRNfu.d.ts.map +1 -0
  26. package/package.json +2 -2
  27. package/skills/acpx/SKILL.md +2 -0
  28. package/dist/client-IfFkZz0S.d.ts.map +0 -1
  29. package/dist/flows-DEook1GU.js.map +0 -1
  30. package/dist/live-checkpoint-BW9JivEG.js.map +0 -1
  31. package/dist/output-jb0298L8.js.map +0 -1
  32. package/dist/session-options-DwRDODlr.d.ts.map +0 -1
@@ -454,6 +454,7 @@ const AGENT_REGISTRY = {
454
454
  kilocode: "npx -y @kilocode/cli acp",
455
455
  kimi: "kimi acp",
456
456
  kiro: "kiro-cli-chat acp",
457
+ mcode: "mcode acp",
457
458
  mux: `npx -y mux@${ACP_ADAPTER_PACKAGE_RANGES.mux} acp`,
458
459
  opencode: "npx -y opencode-ai acp",
459
460
  pool: "pool acp",
@@ -507,6 +508,7 @@ const AGENT_ARGV_REGISTRY = {
507
508
  ],
508
509
  kimi: ["kimi", "acp"],
509
510
  kiro: ["kiro-cli-chat", "acp"],
511
+ mcode: ["mcode", "acp"],
510
512
  mux: [
511
513
  "npx",
512
514
  "-y",
@@ -3145,14 +3147,18 @@ function promotePrefixedAuthEnvironment(env) {
3145
3147
  }
3146
3148
  return protectedKeys;
3147
3149
  }
3148
- function buildAgentEnvironment(authCredentials, sessionEnv) {
3150
+ function validateAgentProcessEnv(agentProcessEnv) {
3151
+ for (const [key, value] of Object.entries(agentProcessEnv ?? {})) if (typeof value !== "string" || key.includes("=") || key.includes("\0") || value.includes("\0")) throw new Error("Invalid agentProcessEnv: environment entries cannot contain NUL or names with '='");
3152
+ }
3153
+ function buildAgentEnvironment(authCredentials, sessionEnv, agentProcessEnv) {
3154
+ validateAgentProcessEnv(agentProcessEnv);
3149
3155
  const env = { ...process.env };
3150
3156
  const protectedAuthEnvKeys = promotePrefixedAuthEnvironment(env);
3151
3157
  if (authCredentials) for (const [methodId, credential] of Object.entries(authCredentials)) {
3152
3158
  addAuthCredentialEnvKeys(protectedAuthEnvKeys, methodId, credential);
3153
3159
  assignAuthCredentialEnv(env, methodId, credential);
3154
3160
  }
3155
- if (sessionEnv) for (const [key, value] of Object.entries(sessionEnv)) {
3161
+ for (const overlay of [sessionEnv, agentProcessEnv]) for (const [key, value] of Object.entries(overlay ?? {})) {
3156
3162
  if (typeof value !== "string" || protectedAuthEnvKeys.has(protectedEnvKey(key))) continue;
3157
3163
  assignSessionEnv(env, key, value);
3158
3164
  }
@@ -3188,10 +3194,10 @@ function resolveConfiguredAuthCredential(methodId, authCredentials) {
3188
3194
  const configCredentials = authCredentials ?? {};
3189
3195
  return configCredentials[methodId] ?? configCredentials[toEnvToken(methodId)];
3190
3196
  }
3191
- function buildAgentSpawnOptions(cwd, authCredentials, sessionEnv) {
3197
+ function buildAgentSpawnOptions(cwd, authCredentials, sessionEnv, agentProcessEnv) {
3192
3198
  return {
3193
3199
  cwd,
3194
- env: buildAgentEnvironment(authCredentials, sessionEnv),
3200
+ env: buildAgentEnvironment(authCredentials, sessionEnv, agentProcessEnv),
3195
3201
  stdio: [
3196
3202
  "pipe",
3197
3203
  "pipe",
@@ -3320,6 +3326,94 @@ function assertRequestedModelSupported(params) {
3320
3326
  }
3321
3327
  }
3322
3328
  //#endregion
3329
+ //#region src/acp/ndjson-stream.ts
3330
+ const DEFAULT_MAX_ACP_MESSAGE_BYTES = 67108864;
3331
+ var AcpMessageLimitError = class extends AcpxOperationalError {
3332
+ constructor(limit) {
3333
+ super(`ACP message exceeded ACPX_MAX_ACP_MESSAGE_BYTES (${limit} bytes). Increase the limit or set it to 0 for unlimited input.`, {
3334
+ outputCode: "RUNTIME",
3335
+ detailCode: "ACP_MESSAGE_TOO_LARGE",
3336
+ origin: "acp",
3337
+ retryable: false
3338
+ });
3339
+ }
3340
+ };
3341
+ function readMaxAcpMessageBytes(raw = process.env.ACPX_MAX_ACP_MESSAGE_BYTES) {
3342
+ const value = raw?.trim();
3343
+ if (!value) return DEFAULT_MAX_ACP_MESSAGE_BYTES;
3344
+ const bytes = Number(value);
3345
+ if (!/^\d+$/.test(value) || !Number.isSafeInteger(bytes)) throw new Error("ACPX_MAX_ACP_MESSAGE_BYTES must be a non-negative safe integer; zero is unlimited");
3346
+ return bytes === 0 ? void 0 : bytes;
3347
+ }
3348
+ function countLineBytes(chunk, retained, limit) {
3349
+ let start = 0;
3350
+ while (start < chunk.length) {
3351
+ const newline = chunk.indexOf(10, start);
3352
+ const end = newline < 0 ? chunk.length : newline;
3353
+ retained += end - start;
3354
+ if (retained > limit) throw new AcpMessageLimitError(limit);
3355
+ if (newline < 0) return retained;
3356
+ retained = 0;
3357
+ start = end + 1;
3358
+ }
3359
+ return retained;
3360
+ }
3361
+ function parseAcpJsonMessageLine(line) {
3362
+ const message = JSON.parse(line);
3363
+ return isAcpMessageObject(message) ? message : void 0;
3364
+ }
3365
+ function enqueueNdJsonLine(agentCommand, line, controller) {
3366
+ const trimmedLine = line.trim();
3367
+ if (!trimmedLine || shouldIgnoreNonJsonAgentOutputLine(agentCommand, trimmedLine)) return;
3368
+ try {
3369
+ const message = parseAcpJsonMessageLine(trimmedLine);
3370
+ if (message) controller.enqueue(message);
3371
+ } catch (err) {
3372
+ console.error("Failed to parse JSON message:", trimmedLine, err);
3373
+ }
3374
+ }
3375
+ function enqueueNdJsonLines(agentCommand, lines, controller) {
3376
+ for (const line of lines) enqueueNdJsonLine(agentCommand, line, controller);
3377
+ }
3378
+ function createNdJsonMessageStream(agentCommand, output, input, maxMessageBytes, onReadError) {
3379
+ const textEncoder = new TextEncoder();
3380
+ const textDecoder = new TextDecoder();
3381
+ return {
3382
+ readable: new ReadableStream({ async start(controller) {
3383
+ let content = "";
3384
+ let retainedBytes = 0;
3385
+ const reader = input.getReader();
3386
+ try {
3387
+ while (true) {
3388
+ const { value, done } = await reader.read();
3389
+ if (done) break;
3390
+ if (maxMessageBytes !== void 0) retainedBytes = countLineBytes(value, retainedBytes, maxMessageBytes);
3391
+ content += textDecoder.decode(value, { stream: true });
3392
+ const lines = content.split("\n");
3393
+ content = lines.pop() || "";
3394
+ enqueueNdJsonLines(agentCommand, lines, controller);
3395
+ }
3396
+ controller.close();
3397
+ } catch (err) {
3398
+ const error = err instanceof Error ? err : new Error(String(err));
3399
+ controller.error(error);
3400
+ onReadError?.(error);
3401
+ } finally {
3402
+ reader.releaseLock();
3403
+ }
3404
+ } }),
3405
+ writable: new WritableStream({ async write(message) {
3406
+ const content = JSON.stringify(message) + "\n";
3407
+ const writer = output.getWriter();
3408
+ try {
3409
+ await writer.write(textEncoder.encode(content));
3410
+ } finally {
3411
+ writer.releaseLock();
3412
+ }
3413
+ } })
3414
+ };
3415
+ }
3416
+ //#endregion
3323
3417
  //#region src/acp/session-control-errors.ts
3324
3418
  const SESSION_CONTROL_UNSUPPORTED_ACP_CODES = /* @__PURE__ */ new Set([-32601, -32602]);
3325
3419
  function asRecord$1(value) {
@@ -3376,6 +3470,13 @@ function buildTerminalSpawnOptions(command, cwd, env, platform = process.platfor
3376
3470
  windowsHide: true
3377
3471
  }, platform, resolvedEnv ?? process.env);
3378
3472
  }
3473
+ function readTerminalOutputCeiling() {
3474
+ const raw = process.env.ACPX_TERMINAL_MAX_OUTPUT_BYTES?.trim();
3475
+ if (!raw) return;
3476
+ const bytes = Number(raw);
3477
+ if (!/^\d+$/.test(raw) || !Number.isSafeInteger(bytes)) throw new Error("ACPX_TERMINAL_MAX_OUTPUT_BYTES must be a non-negative safe integer; zero disables the host ceiling");
3478
+ return bytes === 0 ? void 0 : bytes;
3479
+ }
3379
3480
  function trimToUtf8Boundary(buffer, limit) {
3380
3481
  if (limit <= 0) return Buffer.alloc(0);
3381
3482
  if (buffer.length <= limit) return buffer;
@@ -3419,8 +3520,10 @@ var TerminalManager = class {
3419
3520
  confirmExecute;
3420
3521
  killGraceMs;
3421
3522
  processHelperTimeoutMs;
3523
+ outputCeilingBytes;
3422
3524
  terminals = /* @__PURE__ */ new Map();
3423
3525
  constructor(options) {
3526
+ this.outputCeilingBytes = readTerminalOutputCeiling();
3424
3527
  this.cwd = options.cwd;
3425
3528
  this.permissionMode = options.permissionMode;
3426
3529
  this.nonInteractivePermissions = options.nonInteractivePermissions ?? "deny";
@@ -3445,7 +3548,8 @@ var TerminalManager = class {
3445
3548
  });
3446
3549
  try {
3447
3550
  if (!await this.isExecuteApproved(commandLine)) throw new PermissionDeniedError("Permission denied for terminal/create");
3448
- const outputByteLimit = Math.max(0, Math.round(params.outputByteLimit ?? DEFAULT_TERMINAL_OUTPUT_LIMIT_BYTES));
3551
+ const requestedLimit = Math.max(0, Math.round(params.outputByteLimit ?? DEFAULT_TERMINAL_OUTPUT_LIMIT_BYTES));
3552
+ const outputByteLimit = Math.min(requestedLimit, this.outputCeilingBytes ?? Number.POSITIVE_INFINITY);
3449
3553
  const { proc, spawnCommand } = await spawnTerminalProcess(params, this.cwd);
3450
3554
  let resolveExit = () => {};
3451
3555
  const exitPromise = new Promise((resolve) => {
@@ -4006,6 +4110,17 @@ function snapshotPermissionPolicy(policy) {
4006
4110
  ...policy.defaultAction ? { defaultAction: policy.defaultAction } : {}
4007
4111
  };
4008
4112
  }
4113
+ function snapshotProcessLaunchScope(scope) {
4114
+ if (!scope || scope.kind === "client") return Object.freeze({ kind: "client" });
4115
+ if (scope.kind === "runtime-session") return Object.freeze({
4116
+ kind: "runtime-session",
4117
+ sessionKey: scope.sessionKey
4118
+ });
4119
+ return Object.freeze({
4120
+ kind: "runtime-probe",
4121
+ agent: scope.agent
4122
+ });
4123
+ }
4009
4124
  function childProcessIsRunning(agent) {
4010
4125
  if (!agent) return false;
4011
4126
  return agent.exitCode == null && agent.signalCode == null && !agent.killed;
@@ -4027,56 +4142,6 @@ function installSdkConsoleErrorSuppression() {
4027
4142
  console.error = originalConsoleError;
4028
4143
  };
4029
4144
  }
4030
- function enqueueNdJsonLine(agentCommand, line, controller) {
4031
- const trimmedLine = line.trim();
4032
- if (!trimmedLine || shouldIgnoreNonJsonAgentOutputLine(agentCommand, trimmedLine)) return;
4033
- try {
4034
- const message = parseAcpJsonMessageLine(trimmedLine);
4035
- if (message) controller.enqueue(message);
4036
- } catch (err) {
4037
- console.error("Failed to parse JSON message:", trimmedLine, err);
4038
- }
4039
- }
4040
- function parseAcpJsonMessageLine(line) {
4041
- const message = JSON.parse(line);
4042
- return isAcpMessageObject(message) ? message : void 0;
4043
- }
4044
- function enqueueNdJsonLines(agentCommand, lines, controller) {
4045
- for (const line of lines) enqueueNdJsonLine(agentCommand, line, controller);
4046
- }
4047
- function createNdJsonMessageStream(agentCommand, output, input) {
4048
- const textEncoder = new TextEncoder();
4049
- const textDecoder = new TextDecoder();
4050
- return {
4051
- readable: new ReadableStream({ async start(controller) {
4052
- let content = "";
4053
- const reader = input.getReader();
4054
- try {
4055
- while (true) {
4056
- const { value, done } = await reader.read();
4057
- if (done) break;
4058
- if (!value) continue;
4059
- content += textDecoder.decode(value, { stream: true });
4060
- const lines = content.split("\n");
4061
- content = lines.pop() || "";
4062
- enqueueNdJsonLines(agentCommand, lines, controller);
4063
- }
4064
- } finally {
4065
- reader.releaseLock();
4066
- controller.close();
4067
- }
4068
- } }),
4069
- writable: new WritableStream({ async write(message) {
4070
- const content = JSON.stringify(message) + "\n";
4071
- const writer = output.getWriter();
4072
- try {
4073
- await writer.write(textEncoder.encode(content));
4074
- } finally {
4075
- writer.releaseLock();
4076
- }
4077
- } })
4078
- };
4079
- }
4080
4145
  var AcpClient = class {
4081
4146
  options;
4082
4147
  connection;
@@ -4113,6 +4178,7 @@ var AcpClient = class {
4113
4178
  this.options = {
4114
4179
  ...options,
4115
4180
  cwd: asAbsoluteCwd(options.cwd),
4181
+ agentProcessEnv: options.agentProcessEnv ? { ...options.agentProcessEnv } : void 0,
4116
4182
  authPolicy: options.authPolicy ?? "skip",
4117
4183
  permissionPolicy: snapshotPermissionPolicy(options.permissionPolicy),
4118
4184
  elicitationModes: normalizeElicitationModes(options.elicitationModes)
@@ -4217,29 +4283,41 @@ var AcpClient = class {
4217
4283
  async start() {
4218
4284
  if (this.connection && this.agent && isChildProcessRunning(this.agent)) return;
4219
4285
  if (this.connection || this.agent) await this.close();
4286
+ const maxMessageBytes = readMaxAcpMessageBytes();
4220
4287
  const launch = await this.resolveAgentLaunchPlan();
4221
4288
  this.logAgentLaunch(launch);
4222
4289
  await this.ensureLaunchSupport(launch);
4223
- const child = await this.spawnAgentProcess(launch);
4290
+ const { child, process: startedProcess } = await this.spawnAgentProcess(launch);
4224
4291
  this.closing = false;
4225
- this.agentStartedAt = isoNow$1();
4292
+ this.agentStartedAt = startedProcess.startedAt;
4226
4293
  this.lastAgentExit = void 0;
4227
- this.lastKnownPid = child.pid ?? void 0;
4228
- this.attachAgentLifecycleObservers(child);
4294
+ this.lastKnownPid = startedProcess.pid;
4229
4295
  const startupStderr = [];
4230
4296
  child.stderr.on("data", (chunk) => {
4231
4297
  this.captureStartupStderr(startupStderr, chunk);
4232
4298
  if (!this.options.verbose) return;
4233
4299
  process.stderr.write(chunk);
4234
4300
  });
4301
+ const startupFailure = this.createStartupFailureWatcher(child, startupStderr);
4302
+ try {
4303
+ await this.admitAndObserveSpawnedProcess(child, startedProcess);
4304
+ const admissionExit = startupFailure.getError();
4305
+ if (admissionExit) throw admissionExit;
4306
+ } catch (error) {
4307
+ startupFailure.dispose();
4308
+ throw error;
4309
+ }
4235
4310
  const input = Writable.toWeb(child.stdin);
4236
4311
  const output = Readable.toWeb(child.stdout);
4237
- const stream = this.createTappedStream(createNdJsonMessageStream(this.options.agentCommand, input, output));
4238
- const connection = this.createConnection(stream, launch);
4312
+ let connection;
4313
+ const stream = this.createTappedStream(createNdJsonMessageStream(this.options.agentCommand, input, output, maxMessageBytes, (error) => {
4314
+ this.rejectPendingConnectionRequests(error);
4315
+ connection?.close?.(error);
4316
+ }));
4317
+ connection = this.createConnection(stream, launch);
4239
4318
  connection.signal.addEventListener("abort", () => {
4240
4319
  this.recordAgentExit("connection_close", child.exitCode ?? null, child.signalCode ?? null);
4241
4320
  }, { once: true });
4242
- const startupFailure = this.createStartupFailureWatcher(child, startupStderr);
4243
4321
  await this.initializeAgentConnection({
4244
4322
  child,
4245
4323
  connection,
@@ -4263,7 +4341,7 @@ var AcpClient = class {
4263
4341
  geminiAcp: isGeminiAcpCommand(spawnCommand, args),
4264
4342
  copilotAcp: isCopilotAcpCommand(spawnCommand, args),
4265
4343
  claudeAcp: isClaudeAcpCommand(spawnCommand, args),
4266
- spawnOptions: buildAgentSpawnOptions(this.options.cwd, this.options.authCredentials, this.options.sessionOptions?.env)
4344
+ spawnOptions: buildAgentSpawnOptions(this.options.cwd, this.options.authCredentials, this.options.sessionOptions?.env, this.options.agentProcessEnv)
4267
4345
  };
4268
4346
  }
4269
4347
  logAgentLaunch(plan) {
@@ -4289,16 +4367,57 @@ var AcpClient = class {
4289
4367
  }
4290
4368
  async spawnAgentProcess(plan) {
4291
4369
  const spawnCommand = buildAgentSpawnCommand(plan.spawnCommand, plan.args, process.platform, plan.spawnOptions.env);
4292
- const spawnedChild = spawn(spawnCommand.command, spawnCommand.args, {
4293
- ...plan.spawnOptions,
4294
- windowsVerbatimArguments: spawnCommand.windowsVerbatimArguments
4370
+ const launch = Object.freeze({
4371
+ launchId: randomUUID(),
4372
+ scope: snapshotProcessLaunchScope(this.options.processLaunchScope),
4373
+ command: spawnCommand.command,
4374
+ args: Object.freeze([...spawnCommand.args]),
4375
+ cwd: this.options.cwd
4295
4376
  });
4377
+ await this.options.processLifecycle?.onBeforeSpawn?.(launch);
4378
+ let spawnedChild;
4296
4379
  try {
4380
+ spawnedChild = spawn(spawnCommand.command, spawnCommand.args, {
4381
+ ...plan.spawnOptions,
4382
+ windowsVerbatimArguments: spawnCommand.windowsVerbatimArguments
4383
+ });
4297
4384
  await waitForSpawn$1(spawnedChild);
4298
4385
  } catch (error) {
4299
- throw new AgentSpawnError(this.options.agentCommand, error);
4386
+ const spawnError = new AgentSpawnError(this.options.agentCommand, error);
4387
+ this.notifyProcessSpawnFailure(launch, spawnError);
4388
+ throw spawnError;
4389
+ }
4390
+ const child = requireAgentStdio(spawnedChild);
4391
+ const pid = child.pid;
4392
+ if (pid === void 0) {
4393
+ const spawnError = new AgentSpawnError(this.options.agentCommand, /* @__PURE__ */ new Error("spawned agent process did not expose a PID"));
4394
+ this.notifyProcessSpawnFailure(launch, spawnError);
4395
+ await this.terminateAgentProcess(child);
4396
+ throw spawnError;
4397
+ }
4398
+ return {
4399
+ child,
4400
+ process: Object.freeze({
4401
+ ...launch,
4402
+ pid,
4403
+ startedAt: isoNow$1()
4404
+ })
4405
+ };
4406
+ }
4407
+ async admitAndObserveSpawnedProcess(child, process) {
4408
+ let releaseExitNotification = () => {};
4409
+ const exitNotificationBarrier = new Promise((resolve) => {
4410
+ releaseExitNotification = resolve;
4411
+ });
4412
+ this.attachAgentLifecycleObservers(child, process, exitNotificationBarrier);
4413
+ try {
4414
+ await this.options.processLifecycle?.onSpawned?.(process);
4415
+ } catch (error) {
4416
+ await this.terminateAgentProcess(child);
4417
+ throw error;
4418
+ } finally {
4419
+ releaseExitNotification();
4300
4420
  }
4301
- return requireAgentStdio(spawnedChild);
4302
4421
  }
4303
4422
  createConnection(stream, launch) {
4304
4423
  const app = client({ name: "acpx" }).onNotification(methods.client.session.update, async ({ params }) => {
@@ -4357,7 +4476,7 @@ var AcpClient = class {
4357
4476
  }
4358
4477
  async handleInitializeFailure(params, error) {
4359
4478
  params.startupFailure.dispose();
4360
- const normalizedError = await this.normalizeInitializeError(error, params.child, params.startupStderr);
4479
+ const normalizedError = error instanceof AcpMessageLimitError ? error : await this.normalizeInitializeError(error, params.child, params.startupStderr);
4361
4480
  try {
4362
4481
  params.child.kill();
4363
4482
  } catch {}
@@ -4822,6 +4941,7 @@ var AcpClient = class {
4822
4941
  }
4823
4942
  createStartupFailureWatcher(child, startupStderr) {
4824
4943
  let settled = false;
4944
+ let failure;
4825
4945
  let rejectPromise;
4826
4946
  const cleanup = () => {
4827
4947
  child.off("error", onError);
@@ -4832,7 +4952,10 @@ var AcpClient = class {
4832
4952
  if (settled) return;
4833
4953
  settled = true;
4834
4954
  cleanup();
4835
- if (error) rejectPromise(error);
4955
+ if (error) {
4956
+ failure = error;
4957
+ rejectPromise(error);
4958
+ }
4836
4959
  };
4837
4960
  const createError = (params) => new AgentStartupError({
4838
4961
  agentCommand: this.options.agentCommand,
@@ -4856,14 +4979,18 @@ var AcpClient = class {
4856
4979
  signal
4857
4980
  }));
4858
4981
  };
4982
+ const promise = new Promise((_resolve, reject) => {
4983
+ rejectPromise = reject;
4984
+ child.once("error", onError);
4985
+ child.once("exit", onExit);
4986
+ child.once("close", onClose);
4987
+ if (child.exitCode !== null || child.signalCode !== null) onExit(child.exitCode, child.signalCode);
4988
+ });
4989
+ promise.catch(() => {});
4859
4990
  return {
4860
- promise: new Promise((_resolve, reject) => {
4861
- rejectPromise = reject;
4862
- child.once("error", onError);
4863
- child.once("exit", onExit);
4864
- child.once("close", onClose);
4865
- }),
4866
- dispose: () => finish()
4991
+ promise,
4992
+ dispose: () => finish(),
4993
+ getError: () => failure
4867
4994
  };
4868
4995
  }
4869
4996
  async normalizeInitializeError(error, child, startupStderr) {
@@ -5043,10 +5170,19 @@ var AcpClient = class {
5043
5170
  recorded: true
5044
5171
  };
5045
5172
  }
5046
- attachAgentLifecycleObservers(child) {
5047
- child.once("exit", (exitCode, signal) => {
5173
+ attachAgentLifecycleObservers(child, startedProcess, exitNotificationBarrier) {
5174
+ const onExit = (exitCode, signal) => {
5175
+ const exitedAt = isoNow$1();
5048
5176
  this.recordAgentExit("process_exit", exitCode, signal);
5049
- });
5177
+ exitNotificationBarrier.then(() => {
5178
+ this.notifyProcessExit(startedProcess, exitCode, signal, exitedAt);
5179
+ });
5180
+ };
5181
+ child.once("exit", onExit);
5182
+ if (child.exitCode !== null || child.signalCode !== null) {
5183
+ child.off("exit", onExit);
5184
+ onExit(child.exitCode, child.signalCode);
5185
+ }
5050
5186
  child.once("close", (exitCode, signal) => {
5051
5187
  this.recordAgentExit("process_close", exitCode, signal);
5052
5188
  });
@@ -5054,6 +5190,43 @@ var AcpClient = class {
5054
5190
  this.recordAgentExit("pipe_close", child.exitCode ?? null, child.signalCode ?? null);
5055
5191
  });
5056
5192
  }
5193
+ notifyProcessSpawnFailure(launch, error) {
5194
+ const handler = this.options.processLifecycle?.onSpawnFailed;
5195
+ if (!handler) return;
5196
+ const event = Object.freeze({
5197
+ ...launch,
5198
+ error,
5199
+ failedAt: isoNow$1()
5200
+ });
5201
+ try {
5202
+ Promise.resolve(handler(event)).catch((observerError) => {
5203
+ this.logProcessLifecycleError("onSpawnFailed", observerError);
5204
+ });
5205
+ } catch (observerError) {
5206
+ this.logProcessLifecycleError("onSpawnFailed", observerError);
5207
+ }
5208
+ }
5209
+ notifyProcessExit(startedProcess, exitCode, signal, exitedAt) {
5210
+ const handler = this.options.processLifecycle?.onExit;
5211
+ if (!handler) return;
5212
+ const event = Object.freeze({
5213
+ ...startedProcess,
5214
+ exitCode,
5215
+ signal,
5216
+ exitedAt
5217
+ });
5218
+ try {
5219
+ Promise.resolve(handler(event)).catch((error) => {
5220
+ this.logProcessLifecycleError("onExit", error);
5221
+ });
5222
+ } catch (error) {
5223
+ this.logProcessLifecycleError("onExit", error);
5224
+ }
5225
+ }
5226
+ logProcessLifecycleError(hook, error) {
5227
+ const message = error instanceof Error ? error.message : String(error);
5228
+ this.log(`process lifecycle ${hook} hook failed: ${message}`);
5229
+ }
5057
5230
  recordAgentExit(reason, exitCode, signal) {
5058
5231
  if (this.lastAgentExit) return;
5059
5232
  this.lastAgentExit = {
@@ -6482,9 +6655,24 @@ async function withConnectedSession(options) {
6482
6655
  //#region src/runtime/engine/prompt-turn.ts
6483
6656
  const SESSION_REPLY_IDLE_MS = 1e3;
6484
6657
  const SESSION_REPLY_DRAIN_TIMEOUT_MS = 5e3;
6658
+ function responseMetaField(meta) {
6659
+ return meta === void 0 ? {} : { _meta: meta };
6660
+ }
6661
+ function recoveredSessionResult(response, conversation, promptMessageId) {
6662
+ recordPromptResponseUsage(conversation, response?.usage, promptMessageId);
6663
+ return {
6664
+ stopReason: "end_turn",
6665
+ source: "session",
6666
+ ...responseMetaField(response?._meta)
6667
+ };
6668
+ }
6485
6669
  async function runPromptTurn(params) {
6670
+ let settledResponse;
6486
6671
  try {
6487
6672
  const promptPromise = params.client.prompt(params.sessionId, params.prompt, params.onPromptRequestWritten, params.onElicitation);
6673
+ promptPromise.then((response) => {
6674
+ settledResponse = response;
6675
+ }, () => {});
6488
6676
  await params.onPromptStarted?.();
6489
6677
  const response = await withTimeout(promptPromise, params.timeoutMs);
6490
6678
  await params.client.waitForSessionUpdatesIdle?.({
@@ -6494,7 +6682,8 @@ async function runPromptTurn(params) {
6494
6682
  recordPromptResponseUsage(params.conversation, response.usage, params.promptMessageId);
6495
6683
  return {
6496
6684
  stopReason: response.stopReason,
6497
- source: "rpc"
6685
+ source: "rpc",
6686
+ ...responseMetaField(response._meta)
6498
6687
  };
6499
6688
  } catch (error) {
6500
6689
  if (!(error instanceof TimeoutError) || !params.promptMessageId) throw error;
@@ -6502,10 +6691,7 @@ async function runPromptTurn(params) {
6502
6691
  idleMs: SESSION_REPLY_IDLE_MS,
6503
6692
  timeoutMs: SESSION_REPLY_DRAIN_TIMEOUT_MS
6504
6693
  }).catch(() => {});
6505
- if (hasAgentReplyAfterPrompt(params.conversation, params.promptMessageId)) return {
6506
- stopReason: "end_turn",
6507
- source: "session"
6508
- };
6694
+ if (hasAgentReplyAfterPrompt(params.conversation, params.promptMessageId)) return recoveredSessionResult(settledResponse, params.conversation, params.promptMessageId);
6509
6695
  throw error;
6510
6696
  }
6511
6697
  }
@@ -6565,4 +6751,4 @@ var LiveSessionCheckpoint = class {
6565
6751
  //#endregion
6566
6752
  export { formatPerfMetric as $, AgentSpawnError as $t, RequestedModelUnsupportedError as A, DEFAULT_AGENT_NAME as At, absolutePath as B, extractAcpError as Bt, sessionOptionsFromRecord as C, parsePromptSource as Ct, AcpClient as D, TimeoutError as Dt, reconcileAgentSessionId as E, InterruptedError as Et, runTimedExecFile as F, resolveCanonicalAgentName as Ft, listSessions as G, NON_INTERACTIVE_PERMISSION_POLICIES as Gt, findSession as H, toAcpErrorPayload as Ht, splitCommandLine as I, exitCodeForOutputErrorCode as It, pruneSessions as J, OUTPUT_FORMATS as Jt, listSessionsForAgent as K, OUTPUT_ERROR_CODES as Kt, getAcpxVersion as L, formatErrorMessage as Lt, modelStateFromConfigOptions as M, normalizeAgentName$1 as Mt, normalizeAgentCommandInput as N, resolveAgentArgv as Nt, REQUESTED_MODEL_UNSUPPORTED_ERROR_CODE as O, withInterrupt as Ot, renderArgvIdentity as P, resolveAgentCommand as Pt, assertPersistedKeyPolicy as Q, AcpxOperationalError as Qt, permissionModeSatisfies as R, isRetryablePromptError as Rt, persistSessionOptions as S, mergePromptSourceWithText as St, applyLifecycleSnapshotToRecord as T, textPrompt as Tt, findSessionByDirectoryWalk as U, AUTH_POLICIES as Ut, findGitRepositoryRoot as V, isAcpResourceNotFoundError as Vt, isoNow$2 as W, EXIT_CODES as Wt, writeSessionRecord as X, PERMISSION_POLICY_ACTIONS as Xt, resolveSessionRecord as Y, PERMISSION_MODES as Yt, createAtomicWriteTempPath as Z, SESSION_RECORD_SCHEMA as Zt, recordPromptSubmission as _, isAcpJsonRpcMessage as _t, applyConfigOptionSelection as a, setPerfGauge as at, advertisedModelState as b, PromptInputValidationError as bt, applyRequestedModelIfAdvertised as c, serializeSessionRecordForDisk as ct, setDesiredModeId as d, defaultSessionEventLog as dt, QueueConnectionError as en, getPerfMetricsSnapshot as et, syncAdvertisedModelState as f, sessionBaseDir$1 as ft, recordClientOperation as g, extractSessionUpdateNotification as gt, createSessionConversation as h, sessionEventSegmentPath as ht, connectAndLoadSession as i, resetPerfMetrics as it, isRequestedModelUnsupportedError as j, listBuiltInAgents as jt, REQUESTED_MODEL_UNSUPPORTED_REASONS as k, withTimeout as kt, currentModelIdFromSetModelResponse as l, normalizeRuntimeSessionId as lt, cloneSessionConversation as m, sessionEventLockPath as mt, runPromptTurn as n, measurePerf as nt, applyConfigOptionsToRecord as o, startPerfTimer as ot, cloneSessionAcpxState as p, sessionEventActivePath as pt, normalizeName as q, OUTPUT_ERROR_ORIGINS as qt, withConnectedSession as r, recordPerfDuration as rt, applyModelSelection as s, parseSessionRecord as st, LiveSessionCheckpoint as t, QueueProtocolError as tn, incrementPerfCounter as tt, setCurrentModelId as u, DEFAULT_EVENT_SEGMENT_MAX_BYTES as ut, recordSessionUpdate as v, parseJsonRpcErrorMessage as vt, applyConversation as w, promptToDisplayText as wt, mergeSessionOptions as x, isPromptInput as xt, trimConversationForRuntime as y, parsePromptStopReason as yt, DEFAULT_HISTORY_LIMIT as z, normalizeOutputError as zt };
6567
6753
 
6568
- //# sourceMappingURL=live-checkpoint-BW9JivEG.js.map
6754
+ //# sourceMappingURL=live-checkpoint-sB7dFOYR.js.map