@autohq/cli 0.1.337 → 0.1.338

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.
@@ -23434,7 +23434,7 @@ Object.assign(lookup, {
23434
23434
  // package.json
23435
23435
  var package_default = {
23436
23436
  name: "@autohq/cli",
23437
- version: "0.1.337",
23437
+ version: "0.1.338",
23438
23438
  license: "SEE LICENSE IN README.md",
23439
23439
  publishConfig: {
23440
23440
  access: "public"
@@ -67372,6 +67372,98 @@ function tomlString(value2) {
67372
67372
  return `"${value2.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
67373
67373
  }
67374
67374
 
67375
+ // src/commands/agent-bridge/harness/codex/turn-retry.ts
67376
+ var MAX_CODEX_TURN_RETRY_ATTEMPTS = 3;
67377
+ var MAX_CODEX_TURN_RETRY_TOTAL_DELAY_MS = 3e4;
67378
+ var CODEX_TURN_RETRY_BACKOFF_BASE_MS = 500;
67379
+ var CODEX_TURN_RETRY_MIN_DELAY_MS = 250;
67380
+ var CODEX_TURN_RETRY_MAX_DELAY_MS = 1e4;
67381
+ var CODEX_TURN_RETRY_JITTER_RATIO = 0.2;
67382
+ function classifyRetryableCodexTurnFailure(message) {
67383
+ if (!message) {
67384
+ return null;
67385
+ }
67386
+ const explicitRetryAfterMs = parseExplicitRetryAfterMs(message);
67387
+ const retryAfterMs = explicitRetryAfterMs ?? parseTryAgainDelayMs(message);
67388
+ if (!isRateLimitMessage(message) && explicitRetryAfterMs === null) {
67389
+ return null;
67390
+ }
67391
+ return {
67392
+ cause: "rate_limit",
67393
+ message,
67394
+ retryAfterMs
67395
+ };
67396
+ }
67397
+ function codexTurnRetryDelayMs(input) {
67398
+ if (input.attempt > MAX_CODEX_TURN_RETRY_ATTEMPTS) {
67399
+ return null;
67400
+ }
67401
+ const remainingDelayBudgetMs = MAX_CODEX_TURN_RETRY_TOTAL_DELAY_MS - input.cumulativeDelayMs;
67402
+ if (remainingDelayBudgetMs <= 0) {
67403
+ return null;
67404
+ }
67405
+ const delayMs = input.failure.retryAfterMs !== null ? boundedRetryAfterMs(input.failure.retryAfterMs) : jitteredBackoffMs(input.attempt);
67406
+ return delayMs <= remainingDelayBudgetMs ? delayMs : null;
67407
+ }
67408
+ function isRateLimitMessage(message) {
67409
+ return /\b(?:too many requests|tokens per min|tpm)\b/i.test(message) || /\b(?:http\s*)?(?:status(?:\s*code)?|code)\s*[:=]?\s*429\b/i.test(
67410
+ message
67411
+ ) || /\b429\b[^\n.]{0,80}\btoo many requests\b/i.test(message) || /\brate[-\s]?limit(?:ed| reached)?\b/i.test(message);
67412
+ }
67413
+ function parseExplicitRetryAfterMs(message) {
67414
+ return parseDelayMs(
67415
+ message.match(
67416
+ /\bretry[-\s]?after\b\s*[:=]?\s*(\d+(?:\.\d+)?)\s*(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m)?\b/i
67417
+ )
67418
+ );
67419
+ }
67420
+ function parseTryAgainDelayMs(message) {
67421
+ return parseDelayMs(
67422
+ message.match(
67423
+ /\b(?:try again|retry)\s+(?:in|after)\s+(\d+(?:\.\d+)?)\s*(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m)?\b/i
67424
+ )
67425
+ );
67426
+ }
67427
+ function parseDelayMs(match) {
67428
+ if (!match) {
67429
+ return null;
67430
+ }
67431
+ const value2 = Number.parseFloat(match[1] ?? "");
67432
+ if (!Number.isFinite(value2) || value2 < 0) {
67433
+ return null;
67434
+ }
67435
+ switch ((match[2] ?? "s").toLowerCase()) {
67436
+ case "millisecond":
67437
+ case "milliseconds":
67438
+ case "msec":
67439
+ case "msecs":
67440
+ case "ms":
67441
+ return Math.round(value2);
67442
+ case "minute":
67443
+ case "minutes":
67444
+ case "min":
67445
+ case "mins":
67446
+ case "m":
67447
+ return Math.round(value2 * 6e4);
67448
+ default:
67449
+ return Math.round(value2 * 1e3);
67450
+ }
67451
+ }
67452
+ function boundedRetryAfterMs(delayMs) {
67453
+ return Math.min(
67454
+ Math.max(delayMs, CODEX_TURN_RETRY_MIN_DELAY_MS),
67455
+ CODEX_TURN_RETRY_MAX_DELAY_MS
67456
+ );
67457
+ }
67458
+ function jitteredBackoffMs(attempt) {
67459
+ const baseDelayMs = Math.min(
67460
+ CODEX_TURN_RETRY_BACKOFF_BASE_MS * 2 ** (attempt - 1),
67461
+ CODEX_TURN_RETRY_MAX_DELAY_MS
67462
+ );
67463
+ const jitter = 1 - CODEX_TURN_RETRY_JITTER_RATIO + Math.random() * CODEX_TURN_RETRY_JITTER_RATIO * 2;
67464
+ return boundedRetryAfterMs(Math.round(baseDelayMs * jitter));
67465
+ }
67466
+
67375
67467
  // src/commands/agent-bridge/harness/codex/session.ts
67376
67468
  var CODEX_REQUEST_TIMEOUT_MS = 3e4;
67377
67469
  var CODEX_ITEM_SETTLE_TIMEOUT_MS = 1e4;
@@ -67380,6 +67472,7 @@ var CODEX_TOOL_ITEM_TYPES = /* @__PURE__ */ new Set([
67380
67472
  "fileChange",
67381
67473
  "mcpToolCall"
67382
67474
  ]);
67475
+ var CODEX_RETRYABLE_ERROR_PAIRING_WINDOW_MS = 1e3;
67383
67476
  function startCodexAgentBridgeSession(input) {
67384
67477
  return new CodexAgentBridgeSessionImpl(input);
67385
67478
  }
@@ -67404,6 +67497,8 @@ var CodexAgentBridgeSessionImpl = class {
67404
67497
  // Messages held in "deferred" mode while a turn is in flight; flushed as a
67405
67498
  // fresh turn once the active turn completes.
67406
67499
  deferredMessages = [];
67500
+ turnRetry = null;
67501
+ pendingRetryableError = null;
67407
67502
  constructor(input) {
67408
67503
  this.input = input;
67409
67504
  }
@@ -67414,6 +67509,13 @@ var CodexAgentBridgeSessionImpl = class {
67414
67509
  await this.ensureStarted();
67415
67510
  const threadId = this.requireThreadId();
67416
67511
  const mode = options?.mode ?? "interrupt";
67512
+ if (this.turnRetry !== null) {
67513
+ this.deferredMessages.push(message);
67514
+ this.input.writeOutput?.(
67515
+ "agent_bridge_codex_message_deferred reason=turn_retry"
67516
+ );
67517
+ return;
67518
+ }
67417
67519
  if (this.activeTurnId === null) {
67418
67520
  await this.startTurn(threadId, message);
67419
67521
  return;
@@ -67449,6 +67551,8 @@ var CodexAgentBridgeSessionImpl = class {
67449
67551
  });
67450
67552
  }
67451
67553
  this.activeTurnId = null;
67554
+ this.turnRetry = null;
67555
+ this.clearPendingRetryableError();
67452
67556
  this.pendingToolItemIds.clear();
67453
67557
  this.resolveSettlement();
67454
67558
  this.rejectAllPending(new Error("Codex session is closed"));
@@ -67551,6 +67655,12 @@ var CodexAgentBridgeSessionImpl = class {
67551
67655
  input: [userTextInput(message)]
67552
67656
  });
67553
67657
  }
67658
+ async startContinuationTurn(threadId) {
67659
+ await this.request("turn/start", {
67660
+ threadId,
67661
+ input: []
67662
+ });
67663
+ }
67554
67664
  // Inject a message into the active turn. Prefers `turn/steer` (codex folds the
67555
67665
  // input into the running turn and owns transcript consistency); falls back to a
67556
67666
  // hard `turn/interrupt` + fresh `turn/start` when the turn cannot be steered.
@@ -67671,10 +67781,7 @@ var CodexAgentBridgeSessionImpl = class {
67671
67781
  this.settleResponse(message.id, message.result, message.error);
67672
67782
  return;
67673
67783
  case "notification":
67674
- this.trackNotification(message.notification);
67675
- this.enqueueCallback(
67676
- () => this.input.onNotification(message.notification)
67677
- );
67784
+ this.handleNotification(message.notification);
67678
67785
  return;
67679
67786
  case "serverRequest":
67680
67787
  this.enqueueCallback(() => this.input.onServerRequest(message.request));
@@ -67715,6 +67822,156 @@ var CodexAgentBridgeSessionImpl = class {
67715
67822
  }
67716
67823
  });
67717
67824
  }
67825
+ handleNotification(notification) {
67826
+ if (notification.type === "turnCompleted") {
67827
+ this.handleTurnCompletedNotification(notification);
67828
+ return;
67829
+ }
67830
+ if (notification.type === "error") {
67831
+ this.handleErrorNotification(notification);
67832
+ return;
67833
+ }
67834
+ this.trackNotification(notification);
67835
+ this.enqueueCallback(() => this.input.onNotification(notification));
67836
+ }
67837
+ handleErrorNotification(notification) {
67838
+ const retryableFailure = !notification.willRetry && classifyRetryableCodexTurnFailure(notification.message);
67839
+ if (retryableFailure && (this.activeTurnId !== null || this.turnRetry !== null)) {
67840
+ this.holdRetryableError(notification, {
67841
+ releaseOnTimeout: this.turnRetry === null
67842
+ });
67843
+ return;
67844
+ }
67845
+ this.trackNotification(notification);
67846
+ this.enqueueCallback(() => this.input.onNotification(notification));
67847
+ }
67848
+ handleTurnCompletedNotification(notification) {
67849
+ if (notification.status !== "failed") {
67850
+ this.turnRetry = null;
67851
+ this.clearPendingRetryableError();
67852
+ this.trackNotification(notification);
67853
+ this.enqueueCallback(() => this.input.onNotification(notification));
67854
+ return;
67855
+ }
67856
+ const failure = classifyRetryableCodexTurnFailure(
67857
+ notification.errorMessage
67858
+ );
67859
+ if (!failure) {
67860
+ this.turnRetry = null;
67861
+ this.clearPendingRetryableError();
67862
+ this.trackNotification(notification);
67863
+ this.enqueueCallback(() => this.input.onNotification(notification));
67864
+ return;
67865
+ }
67866
+ this.clearPendingRetryableError();
67867
+ const currentRetry = this.turnRetry ?? {
67868
+ originalFailure: notification,
67869
+ attempts: 0,
67870
+ cumulativeDelayMs: 0
67871
+ };
67872
+ const nextAttempt = currentRetry.attempts + 1;
67873
+ const delayMs = codexTurnRetryDelayMs({
67874
+ attempt: nextAttempt,
67875
+ cumulativeDelayMs: currentRetry.cumulativeDelayMs,
67876
+ failure
67877
+ });
67878
+ if (delayMs === null) {
67879
+ this.turnRetry = null;
67880
+ this.settleCompletedTurn({
67881
+ flushDeferred: true,
67882
+ turnId: notification.turnId
67883
+ });
67884
+ this.input.writeOutput?.(
67885
+ `agent_bridge_codex_turn_retry_exhausted attempts=${currentRetry.attempts} total_delay_ms=${currentRetry.cumulativeDelayMs} cause=${failure.cause} original_error=${singleLine(currentRetry.originalFailure.errorMessage ?? "unknown error")}`
67886
+ );
67887
+ this.enqueueCallback(
67888
+ () => this.input.onNotification(currentRetry.originalFailure)
67889
+ );
67890
+ return;
67891
+ }
67892
+ this.turnRetry = {
67893
+ originalFailure: currentRetry.originalFailure,
67894
+ attempts: nextAttempt,
67895
+ cumulativeDelayMs: currentRetry.cumulativeDelayMs + delayMs
67896
+ };
67897
+ this.settleCompletedTurn({
67898
+ flushDeferred: false,
67899
+ turnId: notification.turnId
67900
+ });
67901
+ this.scheduleFailedTurnRetry({
67902
+ attempt: nextAttempt,
67903
+ delayMs,
67904
+ failure,
67905
+ originalFailure: currentRetry.originalFailure
67906
+ });
67907
+ }
67908
+ holdRetryableError(notification, input) {
67909
+ this.clearPendingRetryableError();
67910
+ this.input.writeOutput?.(
67911
+ `agent_bridge_codex_retryable_error_held message=${singleLine(notification.message)}`
67912
+ );
67913
+ const timer = setTimeout(() => {
67914
+ if (this.pendingRetryableError?.notification !== notification) {
67915
+ return;
67916
+ }
67917
+ this.pendingRetryableError = null;
67918
+ if (!input.releaseOnTimeout) {
67919
+ return;
67920
+ }
67921
+ this.enqueueCallback(() => this.input.onNotification(notification));
67922
+ }, CODEX_RETRYABLE_ERROR_PAIRING_WINDOW_MS);
67923
+ timer.unref?.();
67924
+ this.pendingRetryableError = { notification, timer };
67925
+ }
67926
+ clearPendingRetryableError() {
67927
+ if (!this.pendingRetryableError) {
67928
+ return;
67929
+ }
67930
+ clearTimeout(this.pendingRetryableError.timer);
67931
+ this.pendingRetryableError = null;
67932
+ }
67933
+ scheduleFailedTurnRetry(input) {
67934
+ this.input.writeOutput?.(
67935
+ `agent_bridge_codex_turn_retry_scheduled attempt=${input.attempt} max_attempts=${MAX_CODEX_TURN_RETRY_ATTEMPTS} delay_ms=${input.delayMs} cause=${input.failure.cause} error=${singleLine(input.failure.message)}`
67936
+ );
67937
+ const timer = setTimeout(() => {
67938
+ void this.retryFailedTurn(input).catch((error51) => {
67939
+ this.enqueueCallback(() => this.input.onError(error51));
67940
+ });
67941
+ }, input.delayMs);
67942
+ timer.unref?.();
67943
+ }
67944
+ async retryFailedTurn(input) {
67945
+ if (this.closed) {
67946
+ return;
67947
+ }
67948
+ const threadId = this.threadId;
67949
+ if (threadId === null) {
67950
+ this.turnRetry = null;
67951
+ this.clearPendingRetryableError();
67952
+ this.flushDeferredMessages();
67953
+ this.enqueueCallback(
67954
+ () => this.input.onNotification(input.originalFailure)
67955
+ );
67956
+ return;
67957
+ }
67958
+ try {
67959
+ this.input.writeOutput?.(
67960
+ `agent_bridge_codex_turn_retry_starting attempt=${input.attempt} thread_id=${threadId}`
67961
+ );
67962
+ await this.startContinuationTurn(threadId);
67963
+ } catch (error51) {
67964
+ this.input.writeOutput?.(
67965
+ `agent_bridge_codex_turn_retry_failed attempt=${input.attempt} error=${errorMessage2(error51)}`
67966
+ );
67967
+ this.turnRetry = null;
67968
+ this.clearPendingRetryableError();
67969
+ this.flushDeferredMessages();
67970
+ this.enqueueCallback(
67971
+ () => this.input.onNotification(input.originalFailure)
67972
+ );
67973
+ }
67974
+ }
67718
67975
  // Track turn/item lifecycle so steer/interrupt target the live turn and gate on
67719
67976
  // tool-item settlement.
67720
67977
  trackNotification(notification) {
@@ -67723,12 +67980,10 @@ var CodexAgentBridgeSessionImpl = class {
67723
67980
  this.activeTurnId = notification.turnId;
67724
67981
  return;
67725
67982
  case "turnCompleted":
67726
- if (notification.turnId === this.activeTurnId) {
67727
- this.activeTurnId = null;
67728
- }
67729
- this.pendingToolItemIds.clear();
67730
- this.resolveSettlement();
67731
- this.flushDeferredMessages();
67983
+ this.settleCompletedTurn({
67984
+ flushDeferred: true,
67985
+ turnId: notification.turnId
67986
+ });
67732
67987
  return;
67733
67988
  case "itemStarted":
67734
67989
  if (CODEX_TOOL_ITEM_TYPES.has(notification.item.type)) {
@@ -67745,6 +68000,16 @@ var CodexAgentBridgeSessionImpl = class {
67745
68000
  return;
67746
68001
  }
67747
68002
  }
68003
+ settleCompletedTurn(input) {
68004
+ if (input.turnId === this.activeTurnId) {
68005
+ this.activeTurnId = null;
68006
+ }
68007
+ this.pendingToolItemIds.clear();
68008
+ this.resolveSettlement();
68009
+ if (input.flushDeferred) {
68010
+ this.flushDeferredMessages();
68011
+ }
68012
+ }
67748
68013
  onProcessExit(code) {
67749
68014
  this.rejectAllPending(
67750
68015
  new Error(`Codex app-server exited (code ${code ?? "unknown"})`)
@@ -67841,6 +68106,9 @@ function threadIdFromResult(result) {
67841
68106
  function errorMessage2(error51) {
67842
68107
  return error51 instanceof Error ? error51.message : String(error51);
67843
68108
  }
68109
+ function singleLine(message) {
68110
+ return message.replace(/\s+/g, " ").trim();
68111
+ }
67844
68112
 
67845
68113
  // src/commands/agent-bridge/harness/codex/index.ts
67846
68114
  function createCodexCommandHandler(input) {
package/dist/index.js CHANGED
@@ -30455,7 +30455,7 @@ var init_package = __esm({
30455
30455
  "package.json"() {
30456
30456
  package_default = {
30457
30457
  name: "@autohq/cli",
30458
- version: "0.1.337",
30458
+ version: "0.1.338",
30459
30459
  license: "SEE LICENSE IN README.md",
30460
30460
  publishConfig: {
30461
30461
  access: "public"
@@ -45020,6 +45020,98 @@ function tomlString(value) {
45020
45020
  return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
45021
45021
  }
45022
45022
 
45023
+ // src/commands/agent-bridge/harness/codex/turn-retry.ts
45024
+ var MAX_CODEX_TURN_RETRY_ATTEMPTS = 3;
45025
+ var MAX_CODEX_TURN_RETRY_TOTAL_DELAY_MS = 3e4;
45026
+ var CODEX_TURN_RETRY_BACKOFF_BASE_MS = 500;
45027
+ var CODEX_TURN_RETRY_MIN_DELAY_MS = 250;
45028
+ var CODEX_TURN_RETRY_MAX_DELAY_MS = 1e4;
45029
+ var CODEX_TURN_RETRY_JITTER_RATIO = 0.2;
45030
+ function classifyRetryableCodexTurnFailure(message) {
45031
+ if (!message) {
45032
+ return null;
45033
+ }
45034
+ const explicitRetryAfterMs = parseExplicitRetryAfterMs(message);
45035
+ const retryAfterMs = explicitRetryAfterMs ?? parseTryAgainDelayMs(message);
45036
+ if (!isRateLimitMessage(message) && explicitRetryAfterMs === null) {
45037
+ return null;
45038
+ }
45039
+ return {
45040
+ cause: "rate_limit",
45041
+ message,
45042
+ retryAfterMs
45043
+ };
45044
+ }
45045
+ function codexTurnRetryDelayMs(input) {
45046
+ if (input.attempt > MAX_CODEX_TURN_RETRY_ATTEMPTS) {
45047
+ return null;
45048
+ }
45049
+ const remainingDelayBudgetMs = MAX_CODEX_TURN_RETRY_TOTAL_DELAY_MS - input.cumulativeDelayMs;
45050
+ if (remainingDelayBudgetMs <= 0) {
45051
+ return null;
45052
+ }
45053
+ const delayMs = input.failure.retryAfterMs !== null ? boundedRetryAfterMs(input.failure.retryAfterMs) : jitteredBackoffMs(input.attempt);
45054
+ return delayMs <= remainingDelayBudgetMs ? delayMs : null;
45055
+ }
45056
+ function isRateLimitMessage(message) {
45057
+ return /\b(?:too many requests|tokens per min|tpm)\b/i.test(message) || /\b(?:http\s*)?(?:status(?:\s*code)?|code)\s*[:=]?\s*429\b/i.test(
45058
+ message
45059
+ ) || /\b429\b[^\n.]{0,80}\btoo many requests\b/i.test(message) || /\brate[-\s]?limit(?:ed| reached)?\b/i.test(message);
45060
+ }
45061
+ function parseExplicitRetryAfterMs(message) {
45062
+ return parseDelayMs(
45063
+ message.match(
45064
+ /\bretry[-\s]?after\b\s*[:=]?\s*(\d+(?:\.\d+)?)\s*(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m)?\b/i
45065
+ )
45066
+ );
45067
+ }
45068
+ function parseTryAgainDelayMs(message) {
45069
+ return parseDelayMs(
45070
+ message.match(
45071
+ /\b(?:try again|retry)\s+(?:in|after)\s+(\d+(?:\.\d+)?)\s*(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m)?\b/i
45072
+ )
45073
+ );
45074
+ }
45075
+ function parseDelayMs(match) {
45076
+ if (!match) {
45077
+ return null;
45078
+ }
45079
+ const value = Number.parseFloat(match[1] ?? "");
45080
+ if (!Number.isFinite(value) || value < 0) {
45081
+ return null;
45082
+ }
45083
+ switch ((match[2] ?? "s").toLowerCase()) {
45084
+ case "millisecond":
45085
+ case "milliseconds":
45086
+ case "msec":
45087
+ case "msecs":
45088
+ case "ms":
45089
+ return Math.round(value);
45090
+ case "minute":
45091
+ case "minutes":
45092
+ case "min":
45093
+ case "mins":
45094
+ case "m":
45095
+ return Math.round(value * 6e4);
45096
+ default:
45097
+ return Math.round(value * 1e3);
45098
+ }
45099
+ }
45100
+ function boundedRetryAfterMs(delayMs) {
45101
+ return Math.min(
45102
+ Math.max(delayMs, CODEX_TURN_RETRY_MIN_DELAY_MS),
45103
+ CODEX_TURN_RETRY_MAX_DELAY_MS
45104
+ );
45105
+ }
45106
+ function jitteredBackoffMs(attempt) {
45107
+ const baseDelayMs = Math.min(
45108
+ CODEX_TURN_RETRY_BACKOFF_BASE_MS * 2 ** (attempt - 1),
45109
+ CODEX_TURN_RETRY_MAX_DELAY_MS
45110
+ );
45111
+ const jitter = 1 - CODEX_TURN_RETRY_JITTER_RATIO + Math.random() * CODEX_TURN_RETRY_JITTER_RATIO * 2;
45112
+ return boundedRetryAfterMs(Math.round(baseDelayMs * jitter));
45113
+ }
45114
+
45023
45115
  // src/commands/agent-bridge/harness/codex/session.ts
45024
45116
  var CODEX_REQUEST_TIMEOUT_MS = 3e4;
45025
45117
  var CODEX_ITEM_SETTLE_TIMEOUT_MS = 1e4;
@@ -45028,6 +45120,7 @@ var CODEX_TOOL_ITEM_TYPES = /* @__PURE__ */ new Set([
45028
45120
  "fileChange",
45029
45121
  "mcpToolCall"
45030
45122
  ]);
45123
+ var CODEX_RETRYABLE_ERROR_PAIRING_WINDOW_MS = 1e3;
45031
45124
  function startCodexAgentBridgeSession(input) {
45032
45125
  return new CodexAgentBridgeSessionImpl(input);
45033
45126
  }
@@ -45052,6 +45145,8 @@ var CodexAgentBridgeSessionImpl = class {
45052
45145
  // Messages held in "deferred" mode while a turn is in flight; flushed as a
45053
45146
  // fresh turn once the active turn completes.
45054
45147
  deferredMessages = [];
45148
+ turnRetry = null;
45149
+ pendingRetryableError = null;
45055
45150
  constructor(input) {
45056
45151
  this.input = input;
45057
45152
  }
@@ -45062,6 +45157,13 @@ var CodexAgentBridgeSessionImpl = class {
45062
45157
  await this.ensureStarted();
45063
45158
  const threadId = this.requireThreadId();
45064
45159
  const mode = options?.mode ?? "interrupt";
45160
+ if (this.turnRetry !== null) {
45161
+ this.deferredMessages.push(message);
45162
+ this.input.writeOutput?.(
45163
+ "agent_bridge_codex_message_deferred reason=turn_retry"
45164
+ );
45165
+ return;
45166
+ }
45065
45167
  if (this.activeTurnId === null) {
45066
45168
  await this.startTurn(threadId, message);
45067
45169
  return;
@@ -45097,6 +45199,8 @@ var CodexAgentBridgeSessionImpl = class {
45097
45199
  });
45098
45200
  }
45099
45201
  this.activeTurnId = null;
45202
+ this.turnRetry = null;
45203
+ this.clearPendingRetryableError();
45100
45204
  this.pendingToolItemIds.clear();
45101
45205
  this.resolveSettlement();
45102
45206
  this.rejectAllPending(new Error("Codex session is closed"));
@@ -45199,6 +45303,12 @@ var CodexAgentBridgeSessionImpl = class {
45199
45303
  input: [userTextInput(message)]
45200
45304
  });
45201
45305
  }
45306
+ async startContinuationTurn(threadId) {
45307
+ await this.request("turn/start", {
45308
+ threadId,
45309
+ input: []
45310
+ });
45311
+ }
45202
45312
  // Inject a message into the active turn. Prefers `turn/steer` (codex folds the
45203
45313
  // input into the running turn and owns transcript consistency); falls back to a
45204
45314
  // hard `turn/interrupt` + fresh `turn/start` when the turn cannot be steered.
@@ -45319,10 +45429,7 @@ var CodexAgentBridgeSessionImpl = class {
45319
45429
  this.settleResponse(message.id, message.result, message.error);
45320
45430
  return;
45321
45431
  case "notification":
45322
- this.trackNotification(message.notification);
45323
- this.enqueueCallback(
45324
- () => this.input.onNotification(message.notification)
45325
- );
45432
+ this.handleNotification(message.notification);
45326
45433
  return;
45327
45434
  case "serverRequest":
45328
45435
  this.enqueueCallback(() => this.input.onServerRequest(message.request));
@@ -45363,6 +45470,156 @@ var CodexAgentBridgeSessionImpl = class {
45363
45470
  }
45364
45471
  });
45365
45472
  }
45473
+ handleNotification(notification) {
45474
+ if (notification.type === "turnCompleted") {
45475
+ this.handleTurnCompletedNotification(notification);
45476
+ return;
45477
+ }
45478
+ if (notification.type === "error") {
45479
+ this.handleErrorNotification(notification);
45480
+ return;
45481
+ }
45482
+ this.trackNotification(notification);
45483
+ this.enqueueCallback(() => this.input.onNotification(notification));
45484
+ }
45485
+ handleErrorNotification(notification) {
45486
+ const retryableFailure = !notification.willRetry && classifyRetryableCodexTurnFailure(notification.message);
45487
+ if (retryableFailure && (this.activeTurnId !== null || this.turnRetry !== null)) {
45488
+ this.holdRetryableError(notification, {
45489
+ releaseOnTimeout: this.turnRetry === null
45490
+ });
45491
+ return;
45492
+ }
45493
+ this.trackNotification(notification);
45494
+ this.enqueueCallback(() => this.input.onNotification(notification));
45495
+ }
45496
+ handleTurnCompletedNotification(notification) {
45497
+ if (notification.status !== "failed") {
45498
+ this.turnRetry = null;
45499
+ this.clearPendingRetryableError();
45500
+ this.trackNotification(notification);
45501
+ this.enqueueCallback(() => this.input.onNotification(notification));
45502
+ return;
45503
+ }
45504
+ const failure = classifyRetryableCodexTurnFailure(
45505
+ notification.errorMessage
45506
+ );
45507
+ if (!failure) {
45508
+ this.turnRetry = null;
45509
+ this.clearPendingRetryableError();
45510
+ this.trackNotification(notification);
45511
+ this.enqueueCallback(() => this.input.onNotification(notification));
45512
+ return;
45513
+ }
45514
+ this.clearPendingRetryableError();
45515
+ const currentRetry = this.turnRetry ?? {
45516
+ originalFailure: notification,
45517
+ attempts: 0,
45518
+ cumulativeDelayMs: 0
45519
+ };
45520
+ const nextAttempt = currentRetry.attempts + 1;
45521
+ const delayMs = codexTurnRetryDelayMs({
45522
+ attempt: nextAttempt,
45523
+ cumulativeDelayMs: currentRetry.cumulativeDelayMs,
45524
+ failure
45525
+ });
45526
+ if (delayMs === null) {
45527
+ this.turnRetry = null;
45528
+ this.settleCompletedTurn({
45529
+ flushDeferred: true,
45530
+ turnId: notification.turnId
45531
+ });
45532
+ this.input.writeOutput?.(
45533
+ `agent_bridge_codex_turn_retry_exhausted attempts=${currentRetry.attempts} total_delay_ms=${currentRetry.cumulativeDelayMs} cause=${failure.cause} original_error=${singleLine(currentRetry.originalFailure.errorMessage ?? "unknown error")}`
45534
+ );
45535
+ this.enqueueCallback(
45536
+ () => this.input.onNotification(currentRetry.originalFailure)
45537
+ );
45538
+ return;
45539
+ }
45540
+ this.turnRetry = {
45541
+ originalFailure: currentRetry.originalFailure,
45542
+ attempts: nextAttempt,
45543
+ cumulativeDelayMs: currentRetry.cumulativeDelayMs + delayMs
45544
+ };
45545
+ this.settleCompletedTurn({
45546
+ flushDeferred: false,
45547
+ turnId: notification.turnId
45548
+ });
45549
+ this.scheduleFailedTurnRetry({
45550
+ attempt: nextAttempt,
45551
+ delayMs,
45552
+ failure,
45553
+ originalFailure: currentRetry.originalFailure
45554
+ });
45555
+ }
45556
+ holdRetryableError(notification, input) {
45557
+ this.clearPendingRetryableError();
45558
+ this.input.writeOutput?.(
45559
+ `agent_bridge_codex_retryable_error_held message=${singleLine(notification.message)}`
45560
+ );
45561
+ const timer = setTimeout(() => {
45562
+ if (this.pendingRetryableError?.notification !== notification) {
45563
+ return;
45564
+ }
45565
+ this.pendingRetryableError = null;
45566
+ if (!input.releaseOnTimeout) {
45567
+ return;
45568
+ }
45569
+ this.enqueueCallback(() => this.input.onNotification(notification));
45570
+ }, CODEX_RETRYABLE_ERROR_PAIRING_WINDOW_MS);
45571
+ timer.unref?.();
45572
+ this.pendingRetryableError = { notification, timer };
45573
+ }
45574
+ clearPendingRetryableError() {
45575
+ if (!this.pendingRetryableError) {
45576
+ return;
45577
+ }
45578
+ clearTimeout(this.pendingRetryableError.timer);
45579
+ this.pendingRetryableError = null;
45580
+ }
45581
+ scheduleFailedTurnRetry(input) {
45582
+ this.input.writeOutput?.(
45583
+ `agent_bridge_codex_turn_retry_scheduled attempt=${input.attempt} max_attempts=${MAX_CODEX_TURN_RETRY_ATTEMPTS} delay_ms=${input.delayMs} cause=${input.failure.cause} error=${singleLine(input.failure.message)}`
45584
+ );
45585
+ const timer = setTimeout(() => {
45586
+ void this.retryFailedTurn(input).catch((error51) => {
45587
+ this.enqueueCallback(() => this.input.onError(error51));
45588
+ });
45589
+ }, input.delayMs);
45590
+ timer.unref?.();
45591
+ }
45592
+ async retryFailedTurn(input) {
45593
+ if (this.closed) {
45594
+ return;
45595
+ }
45596
+ const threadId = this.threadId;
45597
+ if (threadId === null) {
45598
+ this.turnRetry = null;
45599
+ this.clearPendingRetryableError();
45600
+ this.flushDeferredMessages();
45601
+ this.enqueueCallback(
45602
+ () => this.input.onNotification(input.originalFailure)
45603
+ );
45604
+ return;
45605
+ }
45606
+ try {
45607
+ this.input.writeOutput?.(
45608
+ `agent_bridge_codex_turn_retry_starting attempt=${input.attempt} thread_id=${threadId}`
45609
+ );
45610
+ await this.startContinuationTurn(threadId);
45611
+ } catch (error51) {
45612
+ this.input.writeOutput?.(
45613
+ `agent_bridge_codex_turn_retry_failed attempt=${input.attempt} error=${errorMessage2(error51)}`
45614
+ );
45615
+ this.turnRetry = null;
45616
+ this.clearPendingRetryableError();
45617
+ this.flushDeferredMessages();
45618
+ this.enqueueCallback(
45619
+ () => this.input.onNotification(input.originalFailure)
45620
+ );
45621
+ }
45622
+ }
45366
45623
  // Track turn/item lifecycle so steer/interrupt target the live turn and gate on
45367
45624
  // tool-item settlement.
45368
45625
  trackNotification(notification) {
@@ -45371,12 +45628,10 @@ var CodexAgentBridgeSessionImpl = class {
45371
45628
  this.activeTurnId = notification.turnId;
45372
45629
  return;
45373
45630
  case "turnCompleted":
45374
- if (notification.turnId === this.activeTurnId) {
45375
- this.activeTurnId = null;
45376
- }
45377
- this.pendingToolItemIds.clear();
45378
- this.resolveSettlement();
45379
- this.flushDeferredMessages();
45631
+ this.settleCompletedTurn({
45632
+ flushDeferred: true,
45633
+ turnId: notification.turnId
45634
+ });
45380
45635
  return;
45381
45636
  case "itemStarted":
45382
45637
  if (CODEX_TOOL_ITEM_TYPES.has(notification.item.type)) {
@@ -45393,6 +45648,16 @@ var CodexAgentBridgeSessionImpl = class {
45393
45648
  return;
45394
45649
  }
45395
45650
  }
45651
+ settleCompletedTurn(input) {
45652
+ if (input.turnId === this.activeTurnId) {
45653
+ this.activeTurnId = null;
45654
+ }
45655
+ this.pendingToolItemIds.clear();
45656
+ this.resolveSettlement();
45657
+ if (input.flushDeferred) {
45658
+ this.flushDeferredMessages();
45659
+ }
45660
+ }
45396
45661
  onProcessExit(code) {
45397
45662
  this.rejectAllPending(
45398
45663
  new Error(`Codex app-server exited (code ${code ?? "unknown"})`)
@@ -45489,6 +45754,9 @@ function threadIdFromResult(result) {
45489
45754
  function errorMessage2(error51) {
45490
45755
  return error51 instanceof Error ? error51.message : String(error51);
45491
45756
  }
45757
+ function singleLine(message) {
45758
+ return message.replace(/\s+/g, " ").trim();
45759
+ }
45492
45760
 
45493
45761
  // src/commands/agent-bridge/harness/codex/index.ts
45494
45762
  function createCodexCommandHandler(input) {
@@ -49557,7 +49825,7 @@ function formatSearchText(result, writeLine, style) {
49557
49825
  writeLine(
49558
49826
  `${style.dim(String(match.sequence))} ${match.kind} ${style.label(
49559
49827
  `[${snippet.query}]`
49560
- )}: ${singleLine(snippet.text)}`
49828
+ )}: ${singleLine2(snippet.text)}`
49561
49829
  );
49562
49830
  }
49563
49831
  }
@@ -49719,16 +49987,16 @@ function entryPreview(event, full) {
49719
49987
  return "";
49720
49988
  }
49721
49989
  });
49722
- const joined = singleLine(texts.join(" "));
49990
+ const joined = singleLine2(texts.join(" "));
49723
49991
  return full ? joined : clip(joined);
49724
49992
  }
49725
49993
  function preview(value) {
49726
- return clip(singleLine(stringify5(value)));
49994
+ return clip(singleLine2(stringify5(value)));
49727
49995
  }
49728
49996
  function stringify5(value) {
49729
49997
  return typeof value === "string" ? value : JSON.stringify(value);
49730
49998
  }
49731
- function singleLine(text) {
49999
+ function singleLine2(text) {
49732
50000
  return text.replace(/\s+/g, " ").trim();
49733
50001
  }
49734
50002
  function clip(text) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autohq/cli",
3
- "version": "0.1.337",
3
+ "version": "0.1.338",
4
4
  "license": "SEE LICENSE IN README.md",
5
5
  "publishConfig": {
6
6
  "access": "public"