@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.
- package/dist/agent-bridge.js +279 -11
- package/dist/index.js +283 -15
- package/package.json +1 -1
package/dist/agent-bridge.js
CHANGED
|
@@ -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.
|
|
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.
|
|
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
|
-
|
|
67727
|
-
|
|
67728
|
-
|
|
67729
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
45375
|
-
|
|
45376
|
-
|
|
45377
|
-
|
|
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
|
-
)}: ${
|
|
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 =
|
|
49990
|
+
const joined = singleLine2(texts.join(" "));
|
|
49723
49991
|
return full ? joined : clip(joined);
|
|
49724
49992
|
}
|
|
49725
49993
|
function preview(value) {
|
|
49726
|
-
return clip(
|
|
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
|
|
49999
|
+
function singleLine2(text) {
|
|
49732
50000
|
return text.replace(/\s+/g, " ").trim();
|
|
49733
50001
|
}
|
|
49734
50002
|
function clip(text) {
|