@ganglion/xacpx 0.17.0-beta.4 → 0.17.0-beta.6

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 (28) hide show
  1. package/README.md +1 -1
  2. package/dist/bridge/bridge-main.js +211 -107
  3. package/dist/cli.js +5911 -4891
  4. package/dist/config/types.d.ts +8 -0
  5. package/dist/control/control-service.d.ts +20 -18
  6. package/dist/control/session-turn-runner.d.ts +35 -0
  7. package/dist/control/turn-queue.d.ts +57 -0
  8. package/dist/control/turn-support.d.ts +28 -0
  9. package/dist/orchestration/orchestration-service.d.ts +14 -101
  10. package/dist/orchestration/orchestration-types.d.ts +2 -0
  11. package/dist/orchestration/service/coordinator-registry-service.d.ts +25 -0
  12. package/dist/orchestration/service/group-service.d.ts +25 -0
  13. package/dist/orchestration/service/human-delegation-service.d.ts +16 -0
  14. package/dist/orchestration/service/human-question-service.d.ts +57 -0
  15. package/dist/orchestration/service/notice-delivery-service.d.ts +25 -0
  16. package/dist/orchestration/service/orchestration-state-kernel.d.ts +58 -0
  17. package/dist/orchestration/service/question-flow-core.d.ts +52 -0
  18. package/dist/orchestration/service/rpc-delegation-service.d.ts +18 -0
  19. package/dist/orchestration/service/task-approval-service.d.ts +17 -0
  20. package/dist/orchestration/service/task-cancellation-service.d.ts +26 -0
  21. package/dist/orchestration/service/task-lifecycle-service.d.ts +21 -0
  22. package/dist/orchestration/service/worker-session-manager.d.ts +65 -0
  23. package/dist/orchestration/task-watch-timeouts.d.ts +2 -0
  24. package/dist/plugin-api.js +49 -14
  25. package/dist/transport/command-timeouts.d.ts +47 -0
  26. package/dist/weixin/util/weixin-log.d.ts +14 -0
  27. package/package.json +2 -2
  28. package/dist/weixin/util/logger.d.ts +0 -14
package/README.md CHANGED
@@ -182,7 +182,7 @@ xacpx-relay add token # prints the access token once
182
182
  xacpx-relay start # defaults: --host 0.0.0.0 --http-port 8787
183
183
 
184
184
  # On each instance host:
185
- xacpx plugin add @ganglion/xacpx-channel-relay # requires xacpx >= 0.11.0
185
+ xacpx plugin add @ganglion/xacpx-channel-relay # requires xacpx >= 0.17.0-beta.6
186
186
  xacpx channel add relay --url wss://relay.example.com --token <access-token> --name my-box
187
187
  xacpx restart
188
188
  ```
@@ -3426,18 +3426,6 @@ var init_acpx_queue_owner_launcher = __esm(() => {
3426
3426
  init_i18n();
3427
3427
  });
3428
3428
 
3429
- // src/transport/permission-mode-flag.ts
3430
- function permissionModeToFlag(permissionMode) {
3431
- switch (permissionMode) {
3432
- case "approve-reads":
3433
- return "--approve-reads";
3434
- case "deny-all":
3435
- return "--deny-all";
3436
- case "approve-all":
3437
- return "--approve-all";
3438
- }
3439
- }
3440
-
3441
3429
  // src/util/path.ts
3442
3430
  import path2 from "node:path";
3443
3431
  import { homedir as homedir3 } from "node:os";
@@ -3698,6 +3686,133 @@ async function deleteAcpxSessionFiles(options) {
3698
3686
  }
3699
3687
  var init_acpx_session_files = () => {};
3700
3688
 
3689
+ // src/transport/command-timeouts.ts
3690
+ function formatTimeout(timeoutMs) {
3691
+ return timeoutMs >= 1000 ? `${timeoutMs / 1000}s` : `${timeoutMs}ms`;
3692
+ }
3693
+ function captureOutputTail(value) {
3694
+ const normalized = value?.trim();
3695
+ if (!normalized)
3696
+ return;
3697
+ return normalized.slice(-COMMAND_OUTPUT_TAIL_CHARS);
3698
+ }
3699
+ function isCommandTimeoutError(error) {
3700
+ return error instanceof CommandTimeoutError;
3701
+ }
3702
+ var DEFAULT_MANAGEMENT_COMMAND_TIMEOUT_MS = 30000, DEFAULT_SESSION_INIT_TIMEOUT_MS = 120000, BRIDGE_REQUEST_TIMEOUT_GRACE_MS = 15000, COMMAND_OUTPUT_TAIL_CHARS = 2000, CommandTimeoutError;
3703
+ var init_command_timeouts = __esm(() => {
3704
+ CommandTimeoutError = class CommandTimeoutError extends Error {
3705
+ timeoutMs;
3706
+ command;
3707
+ stage;
3708
+ stdoutTail;
3709
+ stderrTail;
3710
+ constructor(timeoutMs, command, detail = {}) {
3711
+ const stdoutTail = captureOutputTail(detail.stdout);
3712
+ const stderrTail = captureOutputTail(detail.stderr);
3713
+ const output = [
3714
+ stdoutTail ? `stdout tail: ${stdoutTail}` : "",
3715
+ stderrTail ? `stderr tail: ${stderrTail}` : ""
3716
+ ].filter(Boolean).join("; ");
3717
+ super(`acpx command timed out${detail.stage ? ` during ${detail.stage}` : ""} after ${formatTimeout(timeoutMs)}: ${command}` + (output ? `; ${output}` : ""));
3718
+ this.timeoutMs = timeoutMs;
3719
+ this.command = command;
3720
+ this.name = "CommandTimeoutError";
3721
+ this.stage = detail.stage;
3722
+ this.stdoutTail = stdoutTail;
3723
+ this.stderrTail = stderrTail;
3724
+ }
3725
+ };
3726
+ });
3727
+
3728
+ // src/transport/permission-mode-flag.ts
3729
+ function permissionModeToFlag(permissionMode) {
3730
+ switch (permissionMode) {
3731
+ case "approve-reads":
3732
+ return "--approve-reads";
3733
+ case "deny-all":
3734
+ return "--deny-all";
3735
+ case "approve-all":
3736
+ return "--approve-all";
3737
+ }
3738
+ }
3739
+
3740
+ // src/transport/acpx-command-builder.ts
3741
+ function buildPermissionArgs(input) {
3742
+ const args = [permissionModeToFlag(input.permissionMode), "--non-interactive-permissions", input.nonInteractivePermissions];
3743
+ if (typeof input.permissionPolicy === "string" && input.permissionPolicy.trim().length > 0) {
3744
+ args.push("--permission-policy", input.permissionPolicy);
3745
+ }
3746
+ return args;
3747
+ }
3748
+ function buildQueueOwnerTtlArgs(queueOwnerTtlSeconds) {
3749
+ if (typeof queueOwnerTtlSeconds !== "number" || !Number.isFinite(queueOwnerTtlSeconds))
3750
+ return [];
3751
+ return ["--ttl", String(queueOwnerTtlSeconds)];
3752
+ }
3753
+ function buildModelArgs(model) {
3754
+ const trimmed = model?.trim();
3755
+ return trimmed ? ["--model", trimmed] : [];
3756
+ }
3757
+ function appendAgentAndTail(prefix, input, tail) {
3758
+ if (input.agentCommand)
3759
+ return [...prefix, "--agent", input.agentCommand, ...tail];
3760
+ return [...prefix, input.agent, ...tail];
3761
+ }
3762
+ function buildSessionArgs(input, tail, options = {}) {
3763
+ const prefix = [
3764
+ "--format",
3765
+ options.format ?? "quiet",
3766
+ "--cwd",
3767
+ input.cwd,
3768
+ ...buildPermissionArgs(input.permission),
3769
+ ...buildModelArgs(input.model)
3770
+ ];
3771
+ if (options.verbose)
3772
+ prefix.push("--verbose");
3773
+ return appendAgentAndTail(prefix, input, tail);
3774
+ }
3775
+ function buildPromptArgs(input, tail) {
3776
+ const prefix = [
3777
+ "--format",
3778
+ "json",
3779
+ "--json-strict",
3780
+ "--cwd",
3781
+ input.cwd,
3782
+ ...buildPermissionArgs(input.permission),
3783
+ ...buildModelArgs(input.model),
3784
+ ...buildQueueOwnerTtlArgs(input.queueOwnerTtlSeconds)
3785
+ ];
3786
+ return appendAgentAndTail(prefix, input, tail);
3787
+ }
3788
+ function buildAgentQueryArgs(input, format, tail) {
3789
+ const prefix = ["--format", format, "--cwd", input.cwd, ...buildPermissionArgs(input.permission)];
3790
+ return appendAgentAndTail(prefix, input, tail);
3791
+ }
3792
+ function isMissingAcpxSessionError(stderr, stdout) {
3793
+ const combined = `${stderr}
3794
+ ${stdout}`.toLowerCase();
3795
+ return combined.includes("no named session") || combined.includes("no cwd session") || combined.includes("session not found") || combined.includes("unknown session") || combined.includes("no acpx session found");
3796
+ }
3797
+ function parseAcpxSessionRecordId(stdout) {
3798
+ try {
3799
+ const parsed = JSON.parse(stdout);
3800
+ const acpxRecordId = typeof parsed.acpxRecordId === "string" ? parsed.acpxRecordId : typeof parsed.id === "string" ? parsed.id : undefined;
3801
+ const agentSessionId = typeof parsed.agentSessionId === "string" ? parsed.agentSessionId : undefined;
3802
+ if (acpxRecordId && /^[\w.:-]+$/.test(acpxRecordId) && acpxRecordId.length >= 8) {
3803
+ return { acpxRecordId, agentSessionId };
3804
+ }
3805
+ } catch {
3806
+ const firstLine = stdout.trim().split(/\r?\n/, 1)[0];
3807
+ if (firstLine && /^[\w.:-]+$/.test(firstLine) && firstLine.length >= 8) {
3808
+ return { acpxRecordId: firstLine };
3809
+ }
3810
+ }
3811
+ return;
3812
+ }
3813
+ var DEFAULT_PERMISSION_MODE = "approve-all", DEFAULT_NON_INTERACTIVE = "deny";
3814
+ var init_acpx_command_builder = () => {};
3815
+
3701
3816
  // src/bridge/bridge-main.ts
3702
3817
  import { createInterface } from "node:readline";
3703
3818
 
@@ -3794,6 +3909,9 @@ init_acpx_queue_owner_launcher();
3794
3909
  init_agent_session_list();
3795
3910
  init_codex_subagent_filter();
3796
3911
  init_acpx_session_files();
3912
+ init_command_timeouts();
3913
+ init_command_timeouts();
3914
+ init_acpx_command_builder();
3797
3915
  class EnsureSessionFailedError extends Error {
3798
3916
  kind;
3799
3917
  data;
@@ -3804,20 +3922,6 @@ class EnsureSessionFailedError extends Error {
3804
3922
  this.data = data;
3805
3923
  }
3806
3924
  }
3807
- var DEFAULT_SESSION_INIT_TIMEOUT_MS = 120000;
3808
-
3809
- class CommandTimeoutError extends Error {
3810
- timeoutMs;
3811
- constructor(timeoutMs, command) {
3812
- super(`acpx command timed out after ${timeoutMs / 1000}s: ${command}`);
3813
- this.timeoutMs = timeoutMs;
3814
- this.name = "CommandTimeoutError";
3815
- }
3816
- }
3817
- function modelArgs(model) {
3818
- const trimmed = model?.trim();
3819
- return trimmed ? ["--model", trimmed] : [];
3820
- }
3821
3925
 
3822
3926
  class BridgeRuntime {
3823
3927
  command;
@@ -3856,7 +3960,7 @@ class BridgeRuntime {
3856
3960
  ...includeFilterCwd && input.filterCwd ? ["--filter-cwd", input.filterCwd] : [],
3857
3961
  ...input.cursor ? ["--cursor", input.cursor] : []
3858
3962
  ], { format: "json" }));
3859
- return await this.run(spec.command, spec.args);
3963
+ return await this.run(spec.command, spec.args, { timeoutMs: this.sessionInitTimeoutMs() });
3860
3964
  },
3861
3965
  formatError: (result) => result.stderr || result.stdout || `sessions list failed with exit code ${result.code}`,
3862
3966
  isSubagentSession: (input.driver ?? input.agent) === CODEX_AGENT_NAME ? codexSubagentPredicate() : undefined
@@ -3890,27 +3994,39 @@ class BridgeRuntime {
3890
3994
  const value = this.options.sessionInitTimeoutMs;
3891
3995
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : DEFAULT_SESSION_INIT_TIMEOUT_MS;
3892
3996
  }
3997
+ managementCommandTimeoutMs() {
3998
+ const value = this.options.managementCommandTimeoutMs;
3999
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : DEFAULT_MANAGEMENT_COMMAND_TIMEOUT_MS;
4000
+ }
3893
4001
  async hasSession(input) {
3894
4002
  const spawnSpec = resolveSpawnCommand(this.command, this.buildSessionArgs(input, [
3895
4003
  "sessions",
3896
4004
  "show",
3897
4005
  input.name
3898
4006
  ]));
3899
- const result = await this.run(spawnSpec.command, spawnSpec.args);
4007
+ const result = await this.run(spawnSpec.command, spawnSpec.args, {
4008
+ timeoutMs: this.managementCommandTimeoutMs(),
4009
+ stage: "has-session"
4010
+ });
3900
4011
  return { exists: result.code === 0 };
3901
4012
  }
3902
4013
  async tailSessionHistory(input) {
3903
4014
  const candidates = [
4015
+ ["sessions", "history", input.name, "--limit", String(input.lines)],
3904
4016
  ["sessions", "history", "quiet", "-s", input.name, String(input.lines)],
3905
4017
  ["sessions", "history", "quiet", input.name, String(input.lines)],
3906
4018
  ["sessions", "history", "-s", input.name, "--tail", String(input.lines)],
3907
4019
  ["sessions", "history", input.name, "--tail", String(input.lines)],
3908
4020
  ["sessions", "history", "--name", input.name, "--tail", String(input.lines)]
3909
4021
  ];
4022
+ const deadline = Date.now() + this.managementCommandTimeoutMs();
3910
4023
  let lastResult;
3911
4024
  for (const tailArgs of candidates) {
3912
4025
  const spawnSpec = resolveSpawnCommand(this.command, this.buildSessionArgs(input, tailArgs));
3913
- const result = await this.run(spawnSpec.command, spawnSpec.args);
4026
+ const result = await this.run(spawnSpec.command, spawnSpec.args, {
4027
+ timeoutMs: Math.max(deadline - Date.now(), 1),
4028
+ stage: "session-history"
4029
+ });
3914
4030
  if (result.code === 0) {
3915
4031
  return { text: result.stdout.trimEnd() };
3916
4032
  }
@@ -4077,28 +4193,16 @@ class BridgeRuntime {
4077
4193
  "show",
4078
4194
  input.name
4079
4195
  ]));
4080
- const result = await this.run(spawnSpec.command, spawnSpec.args);
4196
+ const result = await this.run(spawnSpec.command, spawnSpec.args, {
4197
+ timeoutMs: this.managementCommandTimeoutMs(),
4198
+ stage: "read-session-record"
4199
+ });
4081
4200
  if (result.code !== 0) {
4082
4201
  throw new Error(result.stderr || result.stdout || "sessions show failed");
4083
4202
  }
4084
- try {
4085
- const parsed = JSON.parse(result.stdout);
4086
- let acpxRecordId;
4087
- if (typeof parsed.acpxRecordId === "string") {
4088
- acpxRecordId = parsed.acpxRecordId;
4089
- } else if (typeof parsed.id === "string") {
4090
- acpxRecordId = parsed.id;
4091
- }
4092
- const agentSessionId = typeof parsed.agentSessionId === "string" ? parsed.agentSessionId : undefined;
4093
- if (acpxRecordId && /^[\w.:-]+$/.test(acpxRecordId) && acpxRecordId.length >= 8) {
4094
- return { acpxRecordId, agentSessionId };
4095
- }
4096
- } catch {
4097
- const firstLine = result.stdout.trim().split(/\r?\n/, 1)[0];
4098
- if (firstLine && /^[\w.:-]+$/.test(firstLine) && firstLine.length >= 8) {
4099
- return { acpxRecordId: firstLine };
4100
- }
4101
- }
4203
+ const record = parseAcpxSessionRecordId(result.stdout);
4204
+ if (record)
4205
+ return record;
4102
4206
  throw new Error("failed to resolve acpx session record id");
4103
4207
  }
4104
4208
  async getAgentSessionId(input) {
@@ -4112,7 +4216,10 @@ class BridgeRuntime {
4112
4216
  input.name,
4113
4217
  input.modeId
4114
4218
  ]));
4115
- const result = await this.run(spawnSpec.command, spawnSpec.args);
4219
+ const result = await this.run(spawnSpec.command, spawnSpec.args, {
4220
+ timeoutMs: this.managementCommandTimeoutMs(),
4221
+ stage: "set-mode"
4222
+ });
4116
4223
  if (result.code !== 0) {
4117
4224
  throw new Error(result.stderr || result.stdout || "set-mode failed");
4118
4225
  }
@@ -4126,7 +4233,10 @@ class BridgeRuntime {
4126
4233
  "model",
4127
4234
  input.modelId
4128
4235
  ]));
4129
- const result = await this.run(spawnSpec.command, spawnSpec.args);
4236
+ const result = await this.run(spawnSpec.command, spawnSpec.args, {
4237
+ timeoutMs: this.managementCommandTimeoutMs(),
4238
+ stage: "set-model"
4239
+ });
4130
4240
  if (result.code !== 0) {
4131
4241
  throw new Error(result.stderr || result.stdout || "set-model failed");
4132
4242
  }
@@ -4138,7 +4248,10 @@ class BridgeRuntime {
4138
4248
  "-s",
4139
4249
  input.name
4140
4250
  ], { format: "json" }));
4141
- const result = await this.run(spawnSpec.command, spawnSpec.args);
4251
+ const result = await this.run(spawnSpec.command, spawnSpec.args, {
4252
+ timeoutMs: this.managementCommandTimeoutMs(),
4253
+ stage: "get-session-model"
4254
+ });
4142
4255
  if (result.code !== 0) {
4143
4256
  throw new Error(result.stderr || result.stdout || "status failed");
4144
4257
  }
@@ -4158,7 +4271,10 @@ class BridgeRuntime {
4158
4271
  "-s",
4159
4272
  input.name
4160
4273
  ]));
4161
- const result = await this.run(spawnSpec.command, spawnSpec.args);
4274
+ const result = await this.run(spawnSpec.command, spawnSpec.args, {
4275
+ timeoutMs: this.managementCommandTimeoutMs(),
4276
+ stage: "cancel"
4277
+ });
4162
4278
  if (result.code !== 0) {
4163
4279
  throw new Error(result.stderr || result.stdout || "cancel failed");
4164
4280
  }
@@ -4173,11 +4289,14 @@ class BridgeRuntime {
4173
4289
  "close",
4174
4290
  input.name
4175
4291
  ]));
4176
- const result = await this.run(spawnSpec.command, spawnSpec.args);
4292
+ const result = await this.run(spawnSpec.command, spawnSpec.args, {
4293
+ timeoutMs: this.managementCommandTimeoutMs(),
4294
+ stage: "remove-session"
4295
+ });
4177
4296
  if (result.code === 0) {
4178
4297
  return {};
4179
4298
  }
4180
- if (isMissingBridgeSessionError(result.stderr, result.stdout)) {
4299
+ if (isMissingAcpxSessionError(result.stderr, result.stdout)) {
4181
4300
  return {};
4182
4301
  }
4183
4302
  throw new Error(result.stderr || result.stdout || "sessions close failed");
@@ -4207,54 +4326,30 @@ class BridgeRuntime {
4207
4326
  return {};
4208
4327
  }
4209
4328
  buildSessionArgs(input, tail, options = {}) {
4210
- const prefix = [
4211
- "--format",
4212
- options.format ?? "quiet",
4213
- "--cwd",
4214
- input.cwd,
4215
- ...this.buildPermissionArgs(),
4216
- ...modelArgs(input.model)
4217
- ];
4218
- if (options.verbose) {
4219
- prefix.push("--verbose");
4220
- }
4221
- if (input.agentCommand) {
4222
- return [...prefix, "--agent", input.agentCommand, ...tail];
4223
- }
4224
- return [...prefix, input.agent, ...tail];
4329
+ return buildSessionArgs({
4330
+ agent: input.agent,
4331
+ agentCommand: input.agentCommand,
4332
+ cwd: input.cwd,
4333
+ model: input.model,
4334
+ permission: this.permissionInput()
4335
+ }, tail, options);
4225
4336
  }
4226
4337
  buildPromptArgs(input, tail) {
4227
- const prefix = [
4228
- "--format",
4229
- "json",
4230
- "--json-strict",
4231
- "--cwd",
4232
- input.cwd,
4233
- ...this.buildPermissionArgs(),
4234
- ...modelArgs(input.model),
4235
- ...this.buildQueueOwnerTtlArgs()
4236
- ];
4237
- if (input.agentCommand) {
4238
- return [...prefix, "--agent", input.agentCommand, ...tail];
4239
- }
4240
- return [...prefix, input.agent, ...tail];
4241
- }
4242
- buildQueueOwnerTtlArgs() {
4243
- const ttl = this.options.queueOwnerTtlSeconds;
4244
- if (typeof ttl !== "number" || !Number.isFinite(ttl)) {
4245
- return [];
4246
- }
4247
- return ["--ttl", String(ttl)];
4248
- }
4249
- buildPermissionArgs() {
4250
- const permissionMode = this.options.permissionMode ?? "approve-all";
4251
- const nonInteractivePermissions = this.options.nonInteractivePermissions ?? "deny";
4252
- const modeFlag = permissionModeToFlag(permissionMode);
4253
- const args = [modeFlag, "--non-interactive-permissions", nonInteractivePermissions];
4254
- if (typeof this.options.permissionPolicy === "string" && this.options.permissionPolicy.trim().length > 0) {
4255
- args.push("--permission-policy", this.options.permissionPolicy);
4256
- }
4257
- return args;
4338
+ return buildPromptArgs({
4339
+ agent: input.agent,
4340
+ agentCommand: input.agentCommand,
4341
+ cwd: input.cwd,
4342
+ model: input.model,
4343
+ permission: this.permissionInput(),
4344
+ queueOwnerTtlSeconds: this.options.queueOwnerTtlSeconds
4345
+ }, tail);
4346
+ }
4347
+ permissionInput() {
4348
+ return {
4349
+ permissionMode: this.options.permissionMode ?? DEFAULT_PERMISSION_MODE,
4350
+ nonInteractivePermissions: this.options.nonInteractivePermissions ?? DEFAULT_NON_INTERACTIVE,
4351
+ permissionPolicy: this.options.permissionPolicy
4352
+ };
4258
4353
  }
4259
4354
  }
4260
4355
  function spawnCapture(command, args, options) {
@@ -4269,7 +4364,11 @@ function spawnCapture(command, args, options) {
4269
4364
  const timeoutId = typeof options?.timeoutMs === "number" && Number.isFinite(options.timeoutMs) && options.timeoutMs > 0 ? setTimeout(() => {
4270
4365
  const kill = options.killProcessTreeFn ?? ((pid) => terminateProcessTree(pid, { detachedProcessGroup: false }));
4271
4366
  kill(child.pid ?? 0);
4272
- reject(new CommandTimeoutError(options.timeoutMs, [command, ...args].join(" ")));
4367
+ reject(new CommandTimeoutError(options.timeoutMs, [command, ...args].join(" "), {
4368
+ stage: options.stage,
4369
+ stdout,
4370
+ stderr
4371
+ }));
4273
4372
  }, options.timeoutMs) : undefined;
4274
4373
  child.stdout.on("data", (chunk) => {
4275
4374
  stdout += String(chunk);
@@ -4425,11 +4524,6 @@ function isUnknownVerboseOption(stderr, stdout) {
4425
4524
  ${stdout}`;
4426
4525
  return /(unknown|unrecognized)\b[^\n]*--verbose/i.test(combined);
4427
4526
  }
4428
- function isMissingBridgeSessionError(stderr, stdout) {
4429
- const combined = `${stderr}
4430
- ${stdout}`.toLowerCase();
4431
- return combined.includes("no named session") || combined.includes("no cwd session") || combined.includes("session not found") || combined.includes("unknown session") || combined.includes("no acpx session found");
4432
- }
4433
4527
 
4434
4528
  // src/bridge/bridge-server.ts
4435
4529
  class BridgeInvalidRequestError extends Error {
@@ -4491,6 +4585,15 @@ class BridgeServer {
4491
4585
  const message = error instanceof Error ? error.message : String(error);
4492
4586
  const ensureSessionFields = error instanceof EnsureSessionFailedError ? { kind: error.kind, ...error.data ? { data: error.data } : {} } : {};
4493
4587
  const promptDetails = error instanceof PromptCommandError ? { details: { exitCode: error.exitCode, stdout: error.stdout, stderr: error.stderr } } : {};
4588
+ const timeoutDetails = error instanceof CommandTimeoutError ? {
4589
+ timeout: {
4590
+ timeoutMs: error.timeoutMs,
4591
+ command: error.command,
4592
+ ...error.stage ? { stage: error.stage } : {},
4593
+ ...error.stdoutTail ? { stdoutTail: error.stdoutTail } : {},
4594
+ ...error.stderrTail ? { stderrTail: error.stderrTail } : {}
4595
+ }
4596
+ } : {};
4494
4597
  return `${JSON.stringify({
4495
4598
  id: requestId,
4496
4599
  ok: false,
@@ -4498,7 +4601,8 @@ class BridgeServer {
4498
4601
  code: error instanceof BridgeInvalidRequestError ? "BRIDGE_INVALID_REQUEST" : "BRIDGE_INTERNAL_ERROR",
4499
4602
  message,
4500
4603
  ...ensureSessionFields,
4501
- ...promptDetails
4604
+ ...promptDetails,
4605
+ ...timeoutDetails
4502
4606
  }
4503
4607
  })}
4504
4608
  `;