@agentclientprotocol/codex-acp 1.1.14 → 1.2.0
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/index.js +459 -77
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -3453,7 +3453,7 @@ var require_main = __commonJS({
|
|
|
3453
3453
|
exports.createMessageConnection = createMessageConnection2;
|
|
3454
3454
|
var ril_1 = __importDefault(require_ril());
|
|
3455
3455
|
ril_1.default.install();
|
|
3456
|
-
var
|
|
3456
|
+
var path7 = __importStar(__require("path"));
|
|
3457
3457
|
var os2 = __importStar(__require("os"));
|
|
3458
3458
|
var fs7 = __importStar(__require("fs"));
|
|
3459
3459
|
var crypto_1 = __require("crypto");
|
|
@@ -3605,7 +3605,7 @@ var require_main = __commonJS({
|
|
|
3605
3605
|
throw new Error(`Unable to generate a random pipe name with ${randomLength} characters.`);
|
|
3606
3606
|
}
|
|
3607
3607
|
const randomSuffix = (0, crypto_1.randomBytes)(Math.floor(randomLength / 2)).toString("hex");
|
|
3608
|
-
return
|
|
3608
|
+
return path7.join(tmpDir, `lsp-${randomSuffix}.sock`);
|
|
3609
3609
|
}
|
|
3610
3610
|
function createClientPipeTransport(pipeName, encoding = "utf-8") {
|
|
3611
3611
|
let connectResolve;
|
|
@@ -4509,10 +4509,10 @@ function mergeDefs(...defs) {
|
|
|
4509
4509
|
function cloneDef(schema) {
|
|
4510
4510
|
return mergeDefs(schema._zod.def);
|
|
4511
4511
|
}
|
|
4512
|
-
function getElementAtPath(obj,
|
|
4513
|
-
if (!
|
|
4512
|
+
function getElementAtPath(obj, path7) {
|
|
4513
|
+
if (!path7)
|
|
4514
4514
|
return obj;
|
|
4515
|
-
return
|
|
4515
|
+
return path7.reduce((acc, key) => acc?.[key], obj);
|
|
4516
4516
|
}
|
|
4517
4517
|
function promiseAllObject(promisesObj) {
|
|
4518
4518
|
const keys = Object.keys(promisesObj);
|
|
@@ -4921,11 +4921,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
4921
4921
|
}
|
|
4922
4922
|
return false;
|
|
4923
4923
|
}
|
|
4924
|
-
function prefixIssues(
|
|
4924
|
+
function prefixIssues(path7, issues) {
|
|
4925
4925
|
return issues.map((iss) => {
|
|
4926
4926
|
var _a3;
|
|
4927
4927
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
4928
|
-
iss.path.unshift(
|
|
4928
|
+
iss.path.unshift(path7);
|
|
4929
4929
|
return iss;
|
|
4930
4930
|
});
|
|
4931
4931
|
}
|
|
@@ -5072,16 +5072,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
|
|
|
5072
5072
|
}
|
|
5073
5073
|
function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
5074
5074
|
const fieldErrors = { _errors: [] };
|
|
5075
|
-
const processError = (error52,
|
|
5075
|
+
const processError = (error52, path7 = []) => {
|
|
5076
5076
|
for (const issue2 of error52.issues) {
|
|
5077
5077
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
5078
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
5078
|
+
issue2.errors.map((issues) => processError({ issues }, [...path7, ...issue2.path]));
|
|
5079
5079
|
} else if (issue2.code === "invalid_key") {
|
|
5080
|
-
processError({ issues: issue2.issues }, [...
|
|
5080
|
+
processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
|
|
5081
5081
|
} else if (issue2.code === "invalid_element") {
|
|
5082
|
-
processError({ issues: issue2.issues }, [...
|
|
5082
|
+
processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
|
|
5083
5083
|
} else {
|
|
5084
|
-
const fullpath = [...
|
|
5084
|
+
const fullpath = [...path7, ...issue2.path];
|
|
5085
5085
|
if (fullpath.length === 0) {
|
|
5086
5086
|
fieldErrors._errors.push(mapper(issue2));
|
|
5087
5087
|
} else {
|
|
@@ -5108,17 +5108,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
|
5108
5108
|
}
|
|
5109
5109
|
function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
5110
5110
|
const result = { errors: [] };
|
|
5111
|
-
const processError = (error52,
|
|
5111
|
+
const processError = (error52, path7 = []) => {
|
|
5112
5112
|
var _a3, _b;
|
|
5113
5113
|
for (const issue2 of error52.issues) {
|
|
5114
5114
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
5115
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
5115
|
+
issue2.errors.map((issues) => processError({ issues }, [...path7, ...issue2.path]));
|
|
5116
5116
|
} else if (issue2.code === "invalid_key") {
|
|
5117
|
-
processError({ issues: issue2.issues }, [...
|
|
5117
|
+
processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
|
|
5118
5118
|
} else if (issue2.code === "invalid_element") {
|
|
5119
|
-
processError({ issues: issue2.issues }, [...
|
|
5119
|
+
processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
|
|
5120
5120
|
} else {
|
|
5121
|
-
const fullpath = [...
|
|
5121
|
+
const fullpath = [...path7, ...issue2.path];
|
|
5122
5122
|
if (fullpath.length === 0) {
|
|
5123
5123
|
result.errors.push(mapper(issue2));
|
|
5124
5124
|
continue;
|
|
@@ -5150,8 +5150,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
|
5150
5150
|
}
|
|
5151
5151
|
function toDotPath(_path) {
|
|
5152
5152
|
const segs = [];
|
|
5153
|
-
const
|
|
5154
|
-
for (const seg of
|
|
5153
|
+
const path7 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
5154
|
+
for (const seg of path7) {
|
|
5155
5155
|
if (typeof seg === "number")
|
|
5156
5156
|
segs.push(`[${seg}]`);
|
|
5157
5157
|
else if (typeof seg === "symbol")
|
|
@@ -17843,13 +17843,13 @@ function resolveRef(ref, ctx) {
|
|
|
17843
17843
|
if (!ref.startsWith("#")) {
|
|
17844
17844
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
17845
17845
|
}
|
|
17846
|
-
const
|
|
17847
|
-
if (
|
|
17846
|
+
const path7 = ref.slice(1).split("/").filter(Boolean);
|
|
17847
|
+
if (path7.length === 0) {
|
|
17848
17848
|
return ctx.rootSchema;
|
|
17849
17849
|
}
|
|
17850
17850
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
17851
|
-
if (
|
|
17852
|
-
const key =
|
|
17851
|
+
if (path7[0] === defsKey) {
|
|
17852
|
+
const key = path7[1];
|
|
17853
17853
|
if (!key || !ctx.defs[key]) {
|
|
17854
17854
|
throw new Error(`Reference not found: ${ref}`);
|
|
17855
17855
|
}
|
|
@@ -23235,13 +23235,13 @@ function createTerminalCommandEvent(event, terminalId, cwd) {
|
|
|
23235
23235
|
}
|
|
23236
23236
|
};
|
|
23237
23237
|
}
|
|
23238
|
-
function createSearchTitle(query,
|
|
23239
|
-
if (query &&
|
|
23240
|
-
return `Search for '${query}' in ${
|
|
23238
|
+
function createSearchTitle(query, path7) {
|
|
23239
|
+
if (query && path7) {
|
|
23240
|
+
return `Search for '${query}' in ${path7}`;
|
|
23241
23241
|
} else if (query) {
|
|
23242
23242
|
return `Search for '${query}'`;
|
|
23243
|
-
} else if (
|
|
23244
|
-
return `Search in '${
|
|
23243
|
+
} else if (path7) {
|
|
23244
|
+
return `Search in '${path7}'`;
|
|
23245
23245
|
}
|
|
23246
23246
|
return "Search";
|
|
23247
23247
|
}
|
|
@@ -23467,12 +23467,12 @@ function revertPatch(unifiedDiff) {
|
|
|
23467
23467
|
if (!patch) return null;
|
|
23468
23468
|
return reversePatch(patch);
|
|
23469
23469
|
}
|
|
23470
|
-
function createUpdateDiffContent(
|
|
23470
|
+
function createUpdateDiffContent(path7, oldText, newText) {
|
|
23471
23471
|
return {
|
|
23472
23472
|
type: "diff",
|
|
23473
23473
|
oldText,
|
|
23474
23474
|
newText,
|
|
23475
|
-
path:
|
|
23475
|
+
path: path7,
|
|
23476
23476
|
_meta: {
|
|
23477
23477
|
kind: "update"
|
|
23478
23478
|
}
|
|
@@ -23597,10 +23597,57 @@ function sameThreadGoalSnapshot(left, right) {
|
|
|
23597
23597
|
}
|
|
23598
23598
|
|
|
23599
23599
|
// src/CodexEventHandler.ts
|
|
23600
|
+
import { randomUUID } from "node:crypto";
|
|
23601
|
+
|
|
23602
|
+
// src/AirExtension.ts
|
|
23603
|
+
var JETBRAINS_META_KEY = "jetbrains";
|
|
23604
|
+
var AIR_META_KEY = "air";
|
|
23605
|
+
var AIR_EXTENSION_VERSION_KEY = "version";
|
|
23606
|
+
var AIR_EXTENSION_CAPABILITIES_KEY = "capabilities";
|
|
23607
|
+
var AIR_SESSION_FAILURE_KEY = "sessionFailure";
|
|
23608
|
+
var AIR_EXTENSION_VERSION = 1;
|
|
23609
|
+
|
|
23610
|
+
// src/CodexEventHandler.ts
|
|
23611
|
+
var SESSION_FAILURE_PRESENTATION = {
|
|
23612
|
+
transport_lost: { message: "Connection to Codex was lost.", retryable: true, actions: ["reconnect", "retry"] },
|
|
23613
|
+
auth_required: { message: "Sign in to continue using Codex.", retryable: false, actions: ["login"] },
|
|
23614
|
+
rate_limited: { message: "The Codex rate limit was reached.", retryable: true, actions: ["retry"] },
|
|
23615
|
+
quota_exhausted: { message: "The Codex usage quota is exhausted.", retryable: false, actions: ["new_session"] },
|
|
23616
|
+
overloaded: { message: "Codex is temporarily overloaded.", retryable: true, actions: ["retry"] },
|
|
23617
|
+
context_exhausted: { message: "This conversation has reached its context limit.", retryable: false, actions: ["new_turn"] },
|
|
23618
|
+
budget_exhausted: { message: "This session has reached its usage budget.", retryable: false, actions: ["new_session"] },
|
|
23619
|
+
policy_denied: { message: "The request was blocked by provider policy.", retryable: false, actions: [] },
|
|
23620
|
+
bad_request: { message: "Codex could not process this request.", retryable: false, actions: ["new_turn"] },
|
|
23621
|
+
provider_error: { message: "The model provider reported an error.", retryable: true, actions: ["retry"] },
|
|
23622
|
+
internal_error: { message: "Codex encountered an internal error.", retryable: true, actions: ["retry"] }
|
|
23623
|
+
};
|
|
23624
|
+
var STRING_CODEX_ERROR_CATEGORIES = {
|
|
23625
|
+
contextWindowExceeded: "context_exhausted",
|
|
23626
|
+
sessionBudgetExceeded: "budget_exhausted",
|
|
23627
|
+
usageLimitExceeded: "quota_exhausted",
|
|
23628
|
+
serverOverloaded: "overloaded",
|
|
23629
|
+
cyberPolicy: "policy_denied",
|
|
23630
|
+
internalServerError: "internal_error",
|
|
23631
|
+
unauthorized: "auth_required",
|
|
23632
|
+
badRequest: "bad_request",
|
|
23633
|
+
threadRollbackFailed: "provider_error",
|
|
23634
|
+
sandboxError: "provider_error",
|
|
23635
|
+
other: "provider_error"
|
|
23636
|
+
};
|
|
23637
|
+
var STRUCTURED_CODEX_ERROR_CATEGORIES = {
|
|
23638
|
+
httpConnectionFailed: "transport_lost",
|
|
23639
|
+
responseStreamConnectionFailed: "transport_lost",
|
|
23640
|
+
responseStreamDisconnected: "transport_lost",
|
|
23641
|
+
responseTooManyFailedAttempts: "transport_lost",
|
|
23642
|
+
activeTurnNotSteerable: "provider_error"
|
|
23643
|
+
};
|
|
23600
23644
|
var CodexEventHandler = class _CodexEventHandler {
|
|
23601
23645
|
static PLAN_UPDATE_INTERVAL_MS = 150;
|
|
23602
23646
|
sessionState;
|
|
23603
23647
|
supportsPlanUpdates;
|
|
23648
|
+
supportsTypedSessionFailures;
|
|
23649
|
+
sessionFailureEpoch;
|
|
23650
|
+
pendingErrors = [];
|
|
23604
23651
|
failure = null;
|
|
23605
23652
|
completedPlan = null;
|
|
23606
23653
|
activeFuzzyFileSearchSessions = /* @__PURE__ */ new Set();
|
|
@@ -23619,20 +23666,108 @@ var CodexEventHandler = class _CodexEventHandler {
|
|
|
23619
23666
|
terminalCommandOutputIds = /* @__PURE__ */ new Set();
|
|
23620
23667
|
agentMessagePhases = /* @__PURE__ */ new Map();
|
|
23621
23668
|
activeSubAgentActivities = /* @__PURE__ */ new Set();
|
|
23622
|
-
constructor(connection, sessionState, supportsPlanUpdates = false) {
|
|
23669
|
+
constructor(connection, sessionState, supportsPlanUpdates = false, supportsTypedSessionFailures = false, sessionFailureEpoch = randomUUID()) {
|
|
23623
23670
|
this.sessionState = sessionState;
|
|
23624
23671
|
this.supportsPlanUpdates = supportsPlanUpdates;
|
|
23672
|
+
this.supportsTypedSessionFailures = supportsTypedSessionFailures;
|
|
23673
|
+
this.sessionFailureEpoch = sessionFailureEpoch;
|
|
23625
23674
|
this.session = new ACPSessionConnection(connection, sessionState.sessionId);
|
|
23626
23675
|
}
|
|
23627
23676
|
getFailure() {
|
|
23628
23677
|
return this.failure;
|
|
23629
23678
|
}
|
|
23679
|
+
getTerminalSessionFailureMeta(turnId, allowUnattributed = false) {
|
|
23680
|
+
const failure = this.sessionState.sessionFailure;
|
|
23681
|
+
if (!this.supportsTypedSessionFailures || failure?.phase !== "active" || (failure.turnId === void 0 ? !allowUnattributed : turnId === null || failure.turnId !== turnId)) {
|
|
23682
|
+
return null;
|
|
23683
|
+
}
|
|
23684
|
+
return this.createSessionFailureMeta(failure);
|
|
23685
|
+
}
|
|
23686
|
+
recordSyntheticTerminalFailure(category, turnId) {
|
|
23687
|
+
this.recordSessionFailure(category, turnId ?? void 0);
|
|
23688
|
+
}
|
|
23689
|
+
/**
|
|
23690
|
+
* Handles notifications after the prompt-local handler has been disposed. The app-server subscription
|
|
23691
|
+
* remains installed until the ACP session closes, so terminal errors need a durable session-level path
|
|
23692
|
+
* instead of entering a turn buffer that will never be flushed.
|
|
23693
|
+
*/
|
|
23694
|
+
async handleSessionScopedNotification(notification) {
|
|
23695
|
+
if (notification.method !== "error") {
|
|
23696
|
+
await this.handleNotification(notification);
|
|
23697
|
+
return;
|
|
23698
|
+
}
|
|
23699
|
+
if (!this.supportsTypedSessionFailures) {
|
|
23700
|
+
await this.handleNotification(notification);
|
|
23701
|
+
return;
|
|
23702
|
+
}
|
|
23703
|
+
if (notification.params.willRetry) {
|
|
23704
|
+
await this.session.update(this.createSafeErrorDiagnostic(notification.params));
|
|
23705
|
+
return;
|
|
23706
|
+
}
|
|
23707
|
+
const failure = this.recordSessionFailure(
|
|
23708
|
+
this.sessionFailureCategory(notification.params.error.codexErrorInfo),
|
|
23709
|
+
void 0
|
|
23710
|
+
);
|
|
23711
|
+
await this.session.update(this.createSessionFailureUpdate(failure));
|
|
23712
|
+
}
|
|
23713
|
+
async flushPendingErrors() {
|
|
23714
|
+
if (this.sessionState.currentTurnId === null || this.pendingErrors.length === 0) {
|
|
23715
|
+
return;
|
|
23716
|
+
}
|
|
23717
|
+
const errors = this.pendingErrors.splice(0);
|
|
23718
|
+
for (const error51 of errors) {
|
|
23719
|
+
const update = await this.createErrorEvent(error51);
|
|
23720
|
+
if (update) {
|
|
23721
|
+
await this.session.update(update);
|
|
23722
|
+
}
|
|
23723
|
+
}
|
|
23724
|
+
}
|
|
23725
|
+
async flushPendingErrorsAsSessionScoped() {
|
|
23726
|
+
if (!this.supportsTypedSessionFailures || this.pendingErrors.length === 0) {
|
|
23727
|
+
return;
|
|
23728
|
+
}
|
|
23729
|
+
const errors = this.pendingErrors.splice(0);
|
|
23730
|
+
for (const error51 of errors) {
|
|
23731
|
+
await this.handleSessionScopedNotification({ method: "error", params: error51 });
|
|
23732
|
+
}
|
|
23733
|
+
}
|
|
23734
|
+
async clearSessionFailure() {
|
|
23735
|
+
const active = this.sessionState.sessionFailure;
|
|
23736
|
+
if (!this.supportsTypedSessionFailures || active?.phase !== "active") {
|
|
23737
|
+
return;
|
|
23738
|
+
}
|
|
23739
|
+
const cleared = {
|
|
23740
|
+
...active,
|
|
23741
|
+
revision: active.revision + 1,
|
|
23742
|
+
phase: "cleared"
|
|
23743
|
+
};
|
|
23744
|
+
await this.session.update(this.createSessionFailureUpdate(cleared));
|
|
23745
|
+
this.sessionState.sessionFailure = cleared;
|
|
23746
|
+
}
|
|
23747
|
+
async handleFailedTurn(turn) {
|
|
23748
|
+
const activeFailure = this.sessionState.sessionFailure;
|
|
23749
|
+
if (!this.supportsTypedSessionFailures || turn.status !== "failed" || this.failure !== null || activeFailure?.phase === "active" && activeFailure.turnId === turn.id) {
|
|
23750
|
+
return;
|
|
23751
|
+
}
|
|
23752
|
+
const error51 = turn.error ?? {
|
|
23753
|
+
message: "Turn failed",
|
|
23754
|
+
codexErrorInfo: null,
|
|
23755
|
+
additionalDetails: null
|
|
23756
|
+
};
|
|
23757
|
+
this.recordTypedSessionFailure({
|
|
23758
|
+
threadId: this.sessionState.sessionId,
|
|
23759
|
+
turnId: turn.id,
|
|
23760
|
+
willRetry: false,
|
|
23761
|
+
error: error51
|
|
23762
|
+
});
|
|
23763
|
+
}
|
|
23630
23764
|
takeCompletedPlan() {
|
|
23631
23765
|
const plan = this.completedPlan;
|
|
23632
23766
|
this.completedPlan = null;
|
|
23633
23767
|
return plan;
|
|
23634
23768
|
}
|
|
23635
23769
|
async handleNotification(notification) {
|
|
23770
|
+
await this.flushPendingErrors();
|
|
23636
23771
|
const updateEvent = await this.createUpdateEvent(notification);
|
|
23637
23772
|
if (updateEvent) {
|
|
23638
23773
|
await this.session.update(updateEvent);
|
|
@@ -23653,6 +23788,14 @@ var CodexEventHandler = class _CodexEventHandler {
|
|
|
23653
23788
|
async dispose() {
|
|
23654
23789
|
if (this.disposed) return;
|
|
23655
23790
|
await this.flushPendingPlanUpdates();
|
|
23791
|
+
if (this.pendingErrors.length > 0) {
|
|
23792
|
+
logger.log("Discarding app-server errors that arrived before a turn started", {
|
|
23793
|
+
sessionId: this.sessionState.sessionId,
|
|
23794
|
+
count: this.pendingErrors.length,
|
|
23795
|
+
turnIds: this.pendingErrors.map((error51) => error51.turnId)
|
|
23796
|
+
});
|
|
23797
|
+
this.pendingErrors.splice(0);
|
|
23798
|
+
}
|
|
23656
23799
|
this.disposed = true;
|
|
23657
23800
|
this.cancelPlanUpdateTimer();
|
|
23658
23801
|
this.pendingPlanItemIds.clear();
|
|
@@ -23675,6 +23818,7 @@ var CodexEventHandler = class _CodexEventHandler {
|
|
|
23675
23818
|
return await this.createErrorEvent(notification.params);
|
|
23676
23819
|
case "turn/started":
|
|
23677
23820
|
this.sessionState.currentTurnId = notification.params.turn.id;
|
|
23821
|
+
await this.flushPendingErrors();
|
|
23678
23822
|
return null;
|
|
23679
23823
|
case "turn/completed":
|
|
23680
23824
|
await this.flushPendingPlanUpdates();
|
|
@@ -24174,7 +24318,27 @@ ${event.stdin}
|
|
|
24174
24318
|
}
|
|
24175
24319
|
async createErrorEvent(params) {
|
|
24176
24320
|
const error51 = params.error.codexErrorInfo;
|
|
24321
|
+
if (this.sessionState.currentTurnId === null) {
|
|
24322
|
+
this.pendingErrors.push(params);
|
|
24323
|
+
logger.log("Buffered app-server error until the active turn is known", {
|
|
24324
|
+
sessionId: this.sessionState.sessionId,
|
|
24325
|
+
turnId: params.turnId,
|
|
24326
|
+
willRetry: params.willRetry
|
|
24327
|
+
});
|
|
24328
|
+
return null;
|
|
24329
|
+
}
|
|
24330
|
+
if (params.turnId !== this.sessionState.currentTurnId) {
|
|
24331
|
+
if (this.supportsTypedSessionFailures) {
|
|
24332
|
+
return this.createSafeErrorDiagnostic(params);
|
|
24333
|
+
}
|
|
24334
|
+
return this.createCodexSessionInfoUpdate({
|
|
24335
|
+
error: { ...params.error, turnId: params.turnId, willRetry: params.willRetry }
|
|
24336
|
+
});
|
|
24337
|
+
}
|
|
24177
24338
|
if (params.willRetry) {
|
|
24339
|
+
if (this.supportsTypedSessionFailures) {
|
|
24340
|
+
return this.createSafeErrorDiagnostic(params);
|
|
24341
|
+
}
|
|
24178
24342
|
return this.createCodexSessionInfoUpdate({
|
|
24179
24343
|
error: {
|
|
24180
24344
|
...params.error,
|
|
@@ -24182,7 +24346,12 @@ ${event.stdin}
|
|
|
24182
24346
|
willRetry: true
|
|
24183
24347
|
}
|
|
24184
24348
|
});
|
|
24185
|
-
}
|
|
24349
|
+
}
|
|
24350
|
+
if (this.supportsTypedSessionFailures) {
|
|
24351
|
+
this.recordTypedSessionFailure(params);
|
|
24352
|
+
return null;
|
|
24353
|
+
}
|
|
24354
|
+
if (error51 === "usageLimitExceeded") {
|
|
24186
24355
|
this.failure = RequestError.internalError(
|
|
24187
24356
|
this.createTurnErrorData(params.error)
|
|
24188
24357
|
);
|
|
@@ -24193,22 +24362,78 @@ ${event.stdin}
|
|
|
24193
24362
|
|
|
24194
24363
|
`);
|
|
24195
24364
|
}
|
|
24365
|
+
createSafeErrorDiagnostic(params) {
|
|
24366
|
+
const category = this.sessionFailureCategory(params.error.codexErrorInfo);
|
|
24367
|
+
return this.createCodexSessionInfoUpdate({
|
|
24368
|
+
error: {
|
|
24369
|
+
category,
|
|
24370
|
+
message: SESSION_FAILURE_PRESENTATION[category].message,
|
|
24371
|
+
turnId: params.turnId,
|
|
24372
|
+
willRetry: params.willRetry
|
|
24373
|
+
}
|
|
24374
|
+
});
|
|
24375
|
+
}
|
|
24376
|
+
recordTypedSessionFailure(params) {
|
|
24377
|
+
const category = this.sessionFailureCategory(params.error.codexErrorInfo);
|
|
24378
|
+
this.recordSessionFailure(category, params.turnId);
|
|
24379
|
+
}
|
|
24380
|
+
recordSessionFailure(category, turnId) {
|
|
24381
|
+
const presentation = SESSION_FAILURE_PRESENTATION[category];
|
|
24382
|
+
const previous = this.sessionState.sessionFailure;
|
|
24383
|
+
const id = previous?.phase === "active" ? previous.id : turnId === void 0 ? `${this.sessionState.sessionId}:error:${this.sessionFailureEpoch}` : `${turnId}:error`;
|
|
24384
|
+
const failure = {
|
|
24385
|
+
id,
|
|
24386
|
+
revision: previous?.id === id ? previous.revision + 1 : 1,
|
|
24387
|
+
phase: "active",
|
|
24388
|
+
category,
|
|
24389
|
+
source: "codex",
|
|
24390
|
+
safeMessage: presentation.message,
|
|
24391
|
+
retryable: presentation.retryable,
|
|
24392
|
+
actions: presentation.actions,
|
|
24393
|
+
...turnId === void 0 ? {} : { turnId }
|
|
24394
|
+
};
|
|
24395
|
+
this.sessionState.sessionFailure = failure;
|
|
24396
|
+
return failure;
|
|
24397
|
+
}
|
|
24398
|
+
createSessionFailureMeta(failure) {
|
|
24399
|
+
return {
|
|
24400
|
+
[JETBRAINS_META_KEY]: {
|
|
24401
|
+
[AIR_META_KEY]: {
|
|
24402
|
+
[AIR_EXTENSION_VERSION_KEY]: AIR_EXTENSION_VERSION,
|
|
24403
|
+
[AIR_SESSION_FAILURE_KEY]: failure
|
|
24404
|
+
}
|
|
24405
|
+
}
|
|
24406
|
+
};
|
|
24407
|
+
}
|
|
24408
|
+
createSessionFailureUpdate(failure) {
|
|
24409
|
+
return {
|
|
24410
|
+
sessionUpdate: "session_info_update",
|
|
24411
|
+
_meta: this.createSessionFailureMeta(failure)
|
|
24412
|
+
};
|
|
24413
|
+
}
|
|
24414
|
+
sessionFailureCategory(error51) {
|
|
24415
|
+
if (this.isAuthenticationRequiredError(error51)) return "auth_required";
|
|
24416
|
+
if (this.getHttpStatusCode(error51) === 429) return "rate_limited";
|
|
24417
|
+
if (typeof error51 === "string") {
|
|
24418
|
+
return STRING_CODEX_ERROR_CATEGORIES[error51] ?? "provider_error";
|
|
24419
|
+
}
|
|
24420
|
+
if (error51 !== null) {
|
|
24421
|
+
for (const kind of Object.keys(STRUCTURED_CODEX_ERROR_CATEGORIES)) {
|
|
24422
|
+
if (kind in error51) {
|
|
24423
|
+
return STRUCTURED_CODEX_ERROR_CATEGORIES[kind] ?? "provider_error";
|
|
24424
|
+
}
|
|
24425
|
+
}
|
|
24426
|
+
}
|
|
24427
|
+
return "provider_error";
|
|
24428
|
+
}
|
|
24196
24429
|
isAuthenticationRequiredError(error51) {
|
|
24197
24430
|
return error51 === "unauthorized" || this.getHttpStatusCode(error51) === 401;
|
|
24198
24431
|
}
|
|
24199
24432
|
getHttpStatusCode(error51) {
|
|
24200
|
-
if (error51
|
|
24201
|
-
|
|
24202
|
-
|
|
24203
|
-
|
|
24204
|
-
return error51.responseStreamConnectionFailed.httpStatusCode;
|
|
24205
|
-
} else if ("responseStreamDisconnected" in error51) {
|
|
24206
|
-
return error51.responseStreamDisconnected.httpStatusCode;
|
|
24207
|
-
} else if ("responseTooManyFailedAttempts" in error51) {
|
|
24208
|
-
return error51.responseTooManyFailedAttempts.httpStatusCode;
|
|
24209
|
-
}
|
|
24210
|
-
}
|
|
24211
|
-
return null;
|
|
24433
|
+
if (error51 === null || typeof error51 !== "object") return null;
|
|
24434
|
+
const details = Object.values(error51)[0];
|
|
24435
|
+
if (details === null || typeof details !== "object" || !("httpStatusCode" in details)) return null;
|
|
24436
|
+
return typeof details.httpStatusCode === "number" ? details.httpStatusCode : null;
|
|
24212
24437
|
}
|
|
24213
24438
|
createTurnErrorData(error51) {
|
|
24214
24439
|
const data = {
|
|
@@ -24637,11 +24862,11 @@ var CodexApprovalHandler = class {
|
|
|
24637
24862
|
});
|
|
24638
24863
|
}
|
|
24639
24864
|
const fileSystem = permissions.fileSystem;
|
|
24640
|
-
for (const
|
|
24641
|
-
changes.push(this.fileSystemGrantChange(
|
|
24865
|
+
for (const path7 of fileSystem?.read ?? []) {
|
|
24866
|
+
changes.push(this.fileSystemGrantChange(path7, "read", lifetime, suffix));
|
|
24642
24867
|
}
|
|
24643
|
-
for (const
|
|
24644
|
-
changes.push(this.fileSystemGrantChange(
|
|
24868
|
+
for (const path7 of fileSystem?.write ?? []) {
|
|
24869
|
+
changes.push(this.fileSystemGrantChange(path7, "write", lifetime, suffix));
|
|
24645
24870
|
}
|
|
24646
24871
|
for (const entry of fileSystem?.entries ?? []) {
|
|
24647
24872
|
const matcher = (() => {
|
|
@@ -24670,16 +24895,16 @@ var CodexApprovalHandler = class {
|
|
|
24670
24895
|
}
|
|
24671
24896
|
return changes.length > 0 ? { version: 1, changes } : void 0;
|
|
24672
24897
|
}
|
|
24673
|
-
fileSystemGrantChange(
|
|
24898
|
+
fileSystemGrantChange(path7, access, lifetime, suffix) {
|
|
24674
24899
|
return {
|
|
24675
24900
|
type: "grant",
|
|
24676
24901
|
operation: "grant",
|
|
24677
|
-
description: `Allow ${access} access to ${
|
|
24902
|
+
description: `Allow ${access} access to ${path7}${suffix}`,
|
|
24678
24903
|
lifetime,
|
|
24679
24904
|
targets: [{
|
|
24680
24905
|
type: "filesystem",
|
|
24681
24906
|
access: [access],
|
|
24682
|
-
matcher: { type: "exact_path", path:
|
|
24907
|
+
matcher: { type: "exact_path", path: path7 }
|
|
24683
24908
|
}]
|
|
24684
24909
|
};
|
|
24685
24910
|
}
|
|
@@ -25589,15 +25814,15 @@ var wslDefaultBrowser = async () => {
|
|
|
25589
25814
|
const { stdout } = await executePowerShell(command, { powerShellPath: psPath });
|
|
25590
25815
|
return stdout.trim();
|
|
25591
25816
|
};
|
|
25592
|
-
var convertWslPathToWindows = async (
|
|
25593
|
-
if (/^[a-z]+:\/\//i.test(
|
|
25594
|
-
return
|
|
25817
|
+
var convertWslPathToWindows = async (path7) => {
|
|
25818
|
+
if (/^[a-z]+:\/\//i.test(path7)) {
|
|
25819
|
+
return path7;
|
|
25595
25820
|
}
|
|
25596
25821
|
try {
|
|
25597
|
-
const { stdout } = await execFile2("wslpath", ["-aw",
|
|
25822
|
+
const { stdout } = await execFile2("wslpath", ["-aw", path7], { encoding: "utf8" });
|
|
25598
25823
|
return stdout.trim();
|
|
25599
25824
|
} catch {
|
|
25600
|
-
return
|
|
25825
|
+
return path7;
|
|
25601
25826
|
}
|
|
25602
25827
|
};
|
|
25603
25828
|
|
|
@@ -26147,7 +26372,7 @@ var AgentMode = class _AgentMode {
|
|
|
26147
26372
|
};
|
|
26148
26373
|
|
|
26149
26374
|
// src/CodexAcpClient.ts
|
|
26150
|
-
import
|
|
26375
|
+
import path5 from "node:path";
|
|
26151
26376
|
|
|
26152
26377
|
// src/McpServerName.ts
|
|
26153
26378
|
var MCP_SERVER_NAME_WHITESPACE = /\p{White_Space}/gu;
|
|
@@ -26161,7 +26386,7 @@ var package_default = {
|
|
|
26161
26386
|
publishConfig: {
|
|
26162
26387
|
access: "public"
|
|
26163
26388
|
},
|
|
26164
|
-
version: "1.
|
|
26389
|
+
version: "1.2.0",
|
|
26165
26390
|
description: "",
|
|
26166
26391
|
main: "dist/index.js",
|
|
26167
26392
|
bin: {
|
|
@@ -26194,6 +26419,7 @@ var package_default = {
|
|
|
26194
26419
|
"example:steering": "node --import tsx examples/steering.ts",
|
|
26195
26420
|
"example:steering:multistep": "node --import tsx examples/steering.ts",
|
|
26196
26421
|
"generate-types": "./node_modules/.bin/codex app-server generate-ts --out src/app-server",
|
|
26422
|
+
"release:preflight": "bash scripts/release-preflight.sh",
|
|
26197
26423
|
test: "vitest run",
|
|
26198
26424
|
"test:e2e": "npm run build && RUN_E2E_TESTS=true vitest run src/__tests__/CodexACPAgent/e2e",
|
|
26199
26425
|
"test:watch": "vitest",
|
|
@@ -26265,6 +26491,54 @@ function createCodexCollaborationMode(mode, currentModelId) {
|
|
|
26265
26491
|
};
|
|
26266
26492
|
}
|
|
26267
26493
|
|
|
26494
|
+
// src/PathUtils.ts
|
|
26495
|
+
import path4 from "node:path";
|
|
26496
|
+
function isAbsolutePathLike(value) {
|
|
26497
|
+
const trimmed = value.trim();
|
|
26498
|
+
return path4.isAbsolute(trimmed) || isWindowsAbsolutePath(trimmed);
|
|
26499
|
+
}
|
|
26500
|
+
function arePathsEqual(left, right) {
|
|
26501
|
+
return normalizePathForComparison(left) === normalizePathForComparison(right);
|
|
26502
|
+
}
|
|
26503
|
+
function arePathBasenamesEqual(left, right) {
|
|
26504
|
+
const leftBase = path4.posix.basename(normalizePathForComparison(left));
|
|
26505
|
+
const rightBase = path4.posix.basename(normalizePathForComparison(right));
|
|
26506
|
+
if (shouldComparePathCaseInsensitive(left) || shouldComparePathCaseInsensitive(right)) {
|
|
26507
|
+
return leftBase.toLowerCase() === rightBase.toLowerCase();
|
|
26508
|
+
}
|
|
26509
|
+
return leftBase === rightBase;
|
|
26510
|
+
}
|
|
26511
|
+
function normalizePathForComparison(value) {
|
|
26512
|
+
const trimmed = value.trim();
|
|
26513
|
+
if (trimmed.length === 0) {
|
|
26514
|
+
return "";
|
|
26515
|
+
}
|
|
26516
|
+
if (isWindowsAbsolutePath(trimmed)) {
|
|
26517
|
+
const normalized2 = path4.win32.normalize(trimmed).replace(/\\/g, "/");
|
|
26518
|
+
return trimTrailingPathSeparators(normalized2).toLowerCase();
|
|
26519
|
+
}
|
|
26520
|
+
const pathForComparison = path4.isAbsolute(trimmed) ? trimmed : trimmed.replace(/\\/g, "/");
|
|
26521
|
+
const normalized = path4.posix.normalize(pathForComparison);
|
|
26522
|
+
return trimTrailingPathSeparators(normalized);
|
|
26523
|
+
}
|
|
26524
|
+
function isWindowsAbsolutePath(value) {
|
|
26525
|
+
const portableValue = value.replace(/\\/g, "/");
|
|
26526
|
+
return /^[A-Za-z]:\//.test(portableValue) || /^\/\/[^/]+\/[^/]+/.test(portableValue);
|
|
26527
|
+
}
|
|
26528
|
+
function shouldComparePathCaseInsensitive(value) {
|
|
26529
|
+
return isWindowsAbsolutePath(value) || /^[A-Za-z]:/.test(value) || value.includes("\\");
|
|
26530
|
+
}
|
|
26531
|
+
function trimTrailingPathSeparators(value) {
|
|
26532
|
+
let trimmed = value;
|
|
26533
|
+
while (trimmed.endsWith("/") && !isRootPath(trimmed)) {
|
|
26534
|
+
trimmed = trimmed.slice(0, -1);
|
|
26535
|
+
}
|
|
26536
|
+
return trimmed;
|
|
26537
|
+
}
|
|
26538
|
+
function isRootPath(value) {
|
|
26539
|
+
return value === "/" || /^[A-Za-z]:\/$/.test(value) || /^\/\/[^/]+\/[^/]+\/$/.test(value);
|
|
26540
|
+
}
|
|
26541
|
+
|
|
26268
26542
|
// src/CodexAcpClient.ts
|
|
26269
26543
|
var CUSTOM_GATEWAY_PROVIDER_ID = "custom-gateway";
|
|
26270
26544
|
var SUPPORTED_GATEWAY_PROTOCOLS = {
|
|
@@ -26719,7 +26993,7 @@ var CodexAcpClient = class {
|
|
|
26719
26993
|
if (!cwd) {
|
|
26720
26994
|
return;
|
|
26721
26995
|
}
|
|
26722
|
-
const skillExtraRoots = additionalRoots.map((root) =>
|
|
26996
|
+
const skillExtraRoots = additionalRoots.map((root) => path5.join(root, ".agents", "skills"));
|
|
26723
26997
|
if (!arraysEqual(this.skillExtraRoots, skillExtraRoots)) {
|
|
26724
26998
|
await this.codexClient.skillsExtraRootsSet({ extraRoots: skillExtraRoots });
|
|
26725
26999
|
this.skillExtraRoots = skillExtraRoots;
|
|
@@ -26910,11 +27184,10 @@ var CodexAcpClient = class {
|
|
|
26910
27184
|
const requestedCwd = request.cwd?.trim() ?? null;
|
|
26911
27185
|
const filterByCwd = (thread) => {
|
|
26912
27186
|
if (!requestedCwd) return true;
|
|
26913
|
-
if (
|
|
26914
|
-
return thread.cwd
|
|
27187
|
+
if (isAbsolutePathLike(requestedCwd)) {
|
|
27188
|
+
return arePathsEqual(thread.cwd, requestedCwd);
|
|
26915
27189
|
}
|
|
26916
|
-
|
|
26917
|
-
return path4.basename(thread.cwd) === requestedBase;
|
|
27190
|
+
return arePathBasenamesEqual(thread.cwd, requestedCwd);
|
|
26918
27191
|
};
|
|
26919
27192
|
const preferredProvider = this.getModelProvider();
|
|
26920
27193
|
const modelProviders = preferredProvider ? [preferredProvider] : [];
|
|
@@ -26936,7 +27209,7 @@ var CodexAcpClient = class {
|
|
|
26936
27209
|
let sessions = listResponse.data.map(mapThreadToSession);
|
|
26937
27210
|
if (requestedCwd) {
|
|
26938
27211
|
const filtered = listResponse.data.filter(filterByCwd).map(mapThreadToSession);
|
|
26939
|
-
if (filtered.length > 0 ||
|
|
27212
|
+
if (filtered.length > 0 || isAbsolutePathLike(requestedCwd)) {
|
|
26940
27213
|
sessions = filtered;
|
|
26941
27214
|
} else {
|
|
26942
27215
|
logger.log("Ignoring non-absolute cwd filter for session/list", { cwd: requestedCwd });
|
|
@@ -27051,8 +27324,8 @@ function formatUriAsLink(name, uri) {
|
|
|
27051
27324
|
return `[@${name}](${uri})`;
|
|
27052
27325
|
}
|
|
27053
27326
|
if (uri.startsWith("file://")) {
|
|
27054
|
-
const
|
|
27055
|
-
const fileName =
|
|
27327
|
+
const path7 = uri.replace("file://", "");
|
|
27328
|
+
const fileName = path7.split("/").pop() ?? path7;
|
|
27056
27329
|
return `[@${fileName}](${uri})`;
|
|
27057
27330
|
}
|
|
27058
27331
|
return uri;
|
|
@@ -27082,7 +27355,7 @@ function readAdditionalDirectories(cwd, additionalDirectories, meta3) {
|
|
|
27082
27355
|
if (directory.length === 0) {
|
|
27083
27356
|
throw RequestError.invalidParams(void 0, "additionalDirectories entries must not be empty");
|
|
27084
27357
|
}
|
|
27085
|
-
if (!
|
|
27358
|
+
if (!path5.isAbsolute(directory)) {
|
|
27086
27359
|
throw RequestError.invalidParams(void 0, "additionalDirectories entries must be absolute paths");
|
|
27087
27360
|
}
|
|
27088
27361
|
if (!seen.has(directory)) {
|
|
@@ -27685,7 +27958,7 @@ var SteeringQueue = class {
|
|
|
27685
27958
|
|
|
27686
27959
|
// src/ResponseItemHistoryFallback.ts
|
|
27687
27960
|
import { readFile as readFile2 } from "node:fs/promises";
|
|
27688
|
-
import
|
|
27961
|
+
import path6 from "node:path";
|
|
27689
27962
|
function historyFallbackUpdateKey(update) {
|
|
27690
27963
|
switch (update.sessionUpdate) {
|
|
27691
27964
|
case "user_message_chunk":
|
|
@@ -27925,8 +28198,8 @@ function imageBlocks(images) {
|
|
|
27925
28198
|
return [{ type: "text", text: `[@image](${image})` }];
|
|
27926
28199
|
}
|
|
27927
28200
|
const record2 = asRecord(image);
|
|
27928
|
-
const
|
|
27929
|
-
return
|
|
28201
|
+
const path7 = record2 ? stringValue(record2["path"]) ?? stringValue(record2["url"]) : null;
|
|
28202
|
+
return path7 ? [{ type: "text", text: `[@image](${path7})` }] : [];
|
|
27930
28203
|
});
|
|
27931
28204
|
}
|
|
27932
28205
|
function contentBlocksFromResponseContent(content) {
|
|
@@ -28558,10 +28831,10 @@ function baseCommandName(commandName) {
|
|
|
28558
28831
|
return commandName.split(/[\\/]/).pop() ?? commandName;
|
|
28559
28832
|
}
|
|
28560
28833
|
function absolutizePath(cwd, targetPath) {
|
|
28561
|
-
if (
|
|
28834
|
+
if (path6.isAbsolute(targetPath) || cwd.length === 0) {
|
|
28562
28835
|
return targetPath;
|
|
28563
28836
|
}
|
|
28564
|
-
return
|
|
28837
|
+
return path6.join(cwd, targetPath);
|
|
28565
28838
|
}
|
|
28566
28839
|
function withTerminalContent(event, terminalId, cwd) {
|
|
28567
28840
|
const { rawInput, ...eventWithoutRawInput } = event;
|
|
@@ -28717,8 +28990,17 @@ function clientSupportsPlanUpdates(clientCapabilities) {
|
|
|
28717
28990
|
}
|
|
28718
28991
|
|
|
28719
28992
|
// src/CodexAcpServer.ts
|
|
28993
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
28720
28994
|
var IMPLEMENT_PLAN_OPTION_ID = "implement_plan";
|
|
28721
28995
|
var REVISE_PLAN_OPTION_ID = "revise_plan";
|
|
28996
|
+
var CODEX_PROCESS_EXITED_ERROR_CODE = 1001;
|
|
28997
|
+
function clientSupportsTypedSessionFailures(capabilities) {
|
|
28998
|
+
const jetbrains = capabilities?._meta?.[JETBRAINS_META_KEY];
|
|
28999
|
+
const air = jetbrains?.[AIR_META_KEY];
|
|
29000
|
+
const version2 = air?.[AIR_EXTENSION_VERSION_KEY];
|
|
29001
|
+
const supported = air?.[AIR_EXTENSION_CAPABILITIES_KEY];
|
|
29002
|
+
return typeof version2 === "number" && Number.isInteger(version2) && version2 >= AIR_EXTENSION_VERSION && Array.isArray(supported) && supported.includes(AIR_SESSION_FAILURE_KEY);
|
|
29003
|
+
}
|
|
28722
29004
|
var CodexAcpServer = class _CodexAcpServer {
|
|
28723
29005
|
static MODEL_NAME_TOKEN_OVERRIDES = {
|
|
28724
29006
|
gpt: "GPT",
|
|
@@ -28730,6 +29012,7 @@ var CodexAcpServer = class _CodexAcpServer {
|
|
|
28730
29012
|
defaultAuthRequest;
|
|
28731
29013
|
getExitCode;
|
|
28732
29014
|
getRecentStderr;
|
|
29015
|
+
sessionFailureEpoch;
|
|
28733
29016
|
availableCommands;
|
|
28734
29017
|
clientInfo;
|
|
28735
29018
|
clientCapabilities;
|
|
@@ -28759,6 +29042,7 @@ var CodexAcpServer = class _CodexAcpServer {
|
|
|
28759
29042
|
this.defaultAuthRequest = defaultAuthRequest ?? null;
|
|
28760
29043
|
this.getExitCode = getExitCode ?? (() => null);
|
|
28761
29044
|
this.getRecentStderr = getRecentStderr ?? (() => "");
|
|
29045
|
+
this.sessionFailureEpoch = randomUUID2();
|
|
28762
29046
|
this.clientInfo = null;
|
|
28763
29047
|
this.clientCapabilities = null;
|
|
28764
29048
|
this.terminalOutputMode = "terminal_output_delta";
|
|
@@ -28816,6 +29100,12 @@ var CodexAcpServer = class _CodexAcpServer {
|
|
|
28816
29100
|
version: GOAL_EXTENSION_VERSION,
|
|
28817
29101
|
controlMethod: GOAL_CONTROL_METHOD,
|
|
28818
29102
|
actions: [...GOAL_CONTROL_ACTIONS]
|
|
29103
|
+
},
|
|
29104
|
+
[JETBRAINS_META_KEY]: {
|
|
29105
|
+
[AIR_META_KEY]: {
|
|
29106
|
+
[AIR_EXTENSION_VERSION_KEY]: AIR_EXTENSION_VERSION,
|
|
29107
|
+
[AIR_EXTENSION_CAPABILITIES_KEY]: [AIR_SESSION_FAILURE_KEY]
|
|
29108
|
+
}
|
|
28819
29109
|
}
|
|
28820
29110
|
}
|
|
28821
29111
|
};
|
|
@@ -29999,8 +30289,8 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
29999
30289
|
return `[@${name}](${uri})`;
|
|
30000
30290
|
}
|
|
30001
30291
|
if (uri.startsWith("file://")) {
|
|
30002
|
-
const
|
|
30003
|
-
const fileName =
|
|
30292
|
+
const path7 = uri.replace("file://", "");
|
|
30293
|
+
const fileName = path7.split("/").pop() ?? path7;
|
|
30004
30294
|
return `[@${fileName}](${uri})`;
|
|
30005
30295
|
}
|
|
30006
30296
|
return uri;
|
|
@@ -30253,6 +30543,7 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
30253
30543
|
prompt: params.prompt
|
|
30254
30544
|
});
|
|
30255
30545
|
const sessionState = this.getSessionState(params.sessionId);
|
|
30546
|
+
let recoverableSessionFailure = sessionState.sessionFailure;
|
|
30256
30547
|
sessionState.currentTurnId = null;
|
|
30257
30548
|
sessionState.lastTokenUsage = null;
|
|
30258
30549
|
const activePrompt = this.trackActivePrompt(params.sessionId);
|
|
@@ -30266,11 +30557,20 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
30266
30557
|
};
|
|
30267
30558
|
const disposePromptRequestCancellation = this.observePromptRequestCancellation(signal, sessionState, activePrompt);
|
|
30268
30559
|
let eventHandler = null;
|
|
30560
|
+
let promptNotificationsActive = true;
|
|
30561
|
+
const clearRecoveredSessionFailure = async (handler) => {
|
|
30562
|
+
const current = sessionState.sessionFailure;
|
|
30563
|
+
if (recoverableSessionFailure?.phase === "active" && current?.phase === "active" && current.id === recoverableSessionFailure.id && current.revision === recoverableSessionFailure.revision) {
|
|
30564
|
+
await handler.clearSessionFailure();
|
|
30565
|
+
}
|
|
30566
|
+
};
|
|
30269
30567
|
try {
|
|
30270
30568
|
const promptEventHandler = new CodexEventHandler(
|
|
30271
30569
|
this.connection,
|
|
30272
30570
|
sessionState,
|
|
30273
|
-
clientSupportsPlanUpdates(this.clientCapabilities)
|
|
30571
|
+
clientSupportsPlanUpdates(this.clientCapabilities),
|
|
30572
|
+
clientSupportsTypedSessionFailures(this.clientCapabilities),
|
|
30573
|
+
this.sessionFailureEpoch
|
|
30274
30574
|
);
|
|
30275
30575
|
eventHandler = promptEventHandler;
|
|
30276
30576
|
const approvalHandler = new CodexApprovalHandler(this.connection, sessionState, activePrompt.signal);
|
|
@@ -30283,8 +30583,16 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
30283
30583
|
await this.codexAcpClient.subscribeToSessionEvents(
|
|
30284
30584
|
params.sessionId,
|
|
30285
30585
|
async (event) => {
|
|
30586
|
+
if (!promptNotificationsActive) {
|
|
30587
|
+
await promptEventHandler.handleSessionScopedNotification(event);
|
|
30588
|
+
return;
|
|
30589
|
+
}
|
|
30590
|
+
const completesActiveTurn = event.method === "turn/completed" && event.params.turn.id === sessionState.currentTurnId;
|
|
30286
30591
|
await elicitationHandler.handleNotification(event);
|
|
30287
|
-
|
|
30592
|
+
await promptEventHandler.handleNotification(event);
|
|
30593
|
+
if (completesActiveTurn) {
|
|
30594
|
+
promptNotificationsActive = false;
|
|
30595
|
+
}
|
|
30288
30596
|
},
|
|
30289
30597
|
approvalHandler,
|
|
30290
30598
|
elicitationHandler
|
|
@@ -30334,8 +30642,14 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
30334
30642
|
return this.cancelledPromptResponse(sessionState);
|
|
30335
30643
|
}
|
|
30336
30644
|
if (commandResult.handled) {
|
|
30645
|
+
promptNotificationsActive = false;
|
|
30337
30646
|
logger.log("Prompt handled by a command");
|
|
30338
30647
|
await this.codexAcpClient.waitForSessionNotifications(params.sessionId);
|
|
30648
|
+
await eventHandler.flushPendingErrors();
|
|
30649
|
+
await eventHandler.flushPendingErrorsAsSessionScoped();
|
|
30650
|
+
if (commandResult.turnCompleted) {
|
|
30651
|
+
await eventHandler.handleFailedTurn(commandResult.turnCompleted.turn);
|
|
30652
|
+
}
|
|
30339
30653
|
if (commandResult.turnCompleted?.turn.status === "interrupted") {
|
|
30340
30654
|
return this.cancelledPromptResponse(sessionState);
|
|
30341
30655
|
}
|
|
@@ -30343,6 +30657,15 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
30343
30657
|
if (error52) {
|
|
30344
30658
|
throw error52;
|
|
30345
30659
|
}
|
|
30660
|
+
const terminalFailure2 = this.terminalFailurePromptResponse(
|
|
30661
|
+
sessionState,
|
|
30662
|
+
eventHandler,
|
|
30663
|
+
commandResult.turnCompleted?.turn.id ?? sessionState.currentTurnId
|
|
30664
|
+
);
|
|
30665
|
+
if (terminalFailure2) {
|
|
30666
|
+
return terminalFailure2;
|
|
30667
|
+
}
|
|
30668
|
+
await clearRecoveredSessionFailure(eventHandler);
|
|
30346
30669
|
return {
|
|
30347
30670
|
stopReason: "end_turn",
|
|
30348
30671
|
usage: this.buildPromptUsage(sessionState.lastTokenUsage),
|
|
@@ -30408,6 +30731,9 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
30408
30731
|
return this.cancelledPromptResponse(sessionState);
|
|
30409
30732
|
}
|
|
30410
30733
|
await this.codexAcpClient.waitForSessionNotifications(params.sessionId);
|
|
30734
|
+
await eventHandler.flushPendingErrors();
|
|
30735
|
+
await eventHandler.handleFailedTurn(turnCompleted.turn);
|
|
30736
|
+
promptNotificationsActive = false;
|
|
30411
30737
|
if (turnCompleted.turn.status === "interrupted") {
|
|
30412
30738
|
await eventHandler.flushPendingPlanUpdates();
|
|
30413
30739
|
return this.cancelledPromptResponse(sessionState);
|
|
@@ -30416,6 +30742,14 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
30416
30742
|
if (error51) {
|
|
30417
30743
|
throw error51;
|
|
30418
30744
|
}
|
|
30745
|
+
const terminalFailure = this.terminalFailurePromptResponse(
|
|
30746
|
+
sessionState,
|
|
30747
|
+
eventHandler,
|
|
30748
|
+
turnCompleted.turn.id
|
|
30749
|
+
);
|
|
30750
|
+
if (terminalFailure) {
|
|
30751
|
+
return terminalFailure;
|
|
30752
|
+
}
|
|
30419
30753
|
await eventHandler.flushPendingPlanUpdates();
|
|
30420
30754
|
const completedPlan = eventHandler.takeCompletedPlan();
|
|
30421
30755
|
if (completedPlan !== null && sessionState.collaborationMode === PLAN_COLLABORATION_MODE && !this.promptShouldStop(params.sessionId, activePrompt)) {
|
|
@@ -30439,6 +30773,7 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
30439
30773
|
prompt: [{ type: "text", text: "Implement the approved plan." }]
|
|
30440
30774
|
};
|
|
30441
30775
|
activePrompt.currentTurn = null;
|
|
30776
|
+
sessionState.currentTurnId = null;
|
|
30442
30777
|
const implementationPromise = this.runWithProcessCheck(
|
|
30443
30778
|
() => this.codexAcpClient.sendPrompt(
|
|
30444
30779
|
implementationRequest,
|
|
@@ -30456,6 +30791,8 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
30456
30791
|
return;
|
|
30457
30792
|
}
|
|
30458
30793
|
sessionState.currentTurnId = turnId;
|
|
30794
|
+
recoverableSessionFailure = sessionState.sessionFailure;
|
|
30795
|
+
promptNotificationsActive = true;
|
|
30459
30796
|
},
|
|
30460
30797
|
() => this.promptShouldStop(params.sessionId, activePrompt)
|
|
30461
30798
|
)
|
|
@@ -30474,6 +30811,9 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
30474
30811
|
return this.cancelledPromptResponse(sessionState);
|
|
30475
30812
|
}
|
|
30476
30813
|
await this.codexAcpClient.waitForSessionNotifications(params.sessionId);
|
|
30814
|
+
await eventHandler.flushPendingErrors();
|
|
30815
|
+
await eventHandler.handleFailedTurn(turnCompleted.turn);
|
|
30816
|
+
promptNotificationsActive = false;
|
|
30477
30817
|
if (turnCompleted.turn.status === "interrupted") {
|
|
30478
30818
|
await eventHandler.flushPendingPlanUpdates();
|
|
30479
30819
|
return this.cancelledPromptResponse(sessionState);
|
|
@@ -30482,8 +30822,17 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
30482
30822
|
if (implementationError) {
|
|
30483
30823
|
throw implementationError;
|
|
30484
30824
|
}
|
|
30825
|
+
const implementationFailure = this.terminalFailurePromptResponse(
|
|
30826
|
+
sessionState,
|
|
30827
|
+
eventHandler,
|
|
30828
|
+
turnCompleted.turn.id
|
|
30829
|
+
);
|
|
30830
|
+
if (implementationFailure) {
|
|
30831
|
+
return implementationFailure;
|
|
30832
|
+
}
|
|
30485
30833
|
}
|
|
30486
30834
|
}
|
|
30835
|
+
await clearRecoveredSessionFailure(eventHandler);
|
|
30487
30836
|
await this.publishFallbackSessionTitle(
|
|
30488
30837
|
sessionState,
|
|
30489
30838
|
this.createPromptFallbackTitle(params.prompt)
|
|
@@ -30495,8 +30844,27 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
30495
30844
|
};
|
|
30496
30845
|
} catch (err) {
|
|
30497
30846
|
logger.error(`Prompt for session ${params.sessionId} failed`, err);
|
|
30847
|
+
if (activePrompt.signal.aborted || this.sessionIsClosing(params.sessionId)) {
|
|
30848
|
+
return this.cancelledPromptResponse(sessionState);
|
|
30849
|
+
}
|
|
30850
|
+
const isProcessExit = err instanceof RequestError && err.code === CODEX_PROCESS_EXITED_ERROR_CODE;
|
|
30851
|
+
const isUnexpectedFailure = !(err instanceof RequestError);
|
|
30852
|
+
if (eventHandler !== null && clientSupportsTypedSessionFailures(this.clientCapabilities) && (isProcessExit || isUnexpectedFailure)) {
|
|
30853
|
+
const category = isProcessExit ? "transport_lost" : "internal_error";
|
|
30854
|
+
eventHandler.recordSyntheticTerminalFailure(category, sessionState.currentTurnId);
|
|
30855
|
+
const failureResponse = this.terminalFailurePromptResponse(
|
|
30856
|
+
sessionState,
|
|
30857
|
+
eventHandler,
|
|
30858
|
+
sessionState.currentTurnId,
|
|
30859
|
+
true
|
|
30860
|
+
);
|
|
30861
|
+
if (failureResponse !== null) {
|
|
30862
|
+
return failureResponse;
|
|
30863
|
+
}
|
|
30864
|
+
}
|
|
30498
30865
|
throw err;
|
|
30499
30866
|
} finally {
|
|
30867
|
+
promptNotificationsActive = false;
|
|
30500
30868
|
logger.log("Prompt completed", { sessionId: params.sessionId });
|
|
30501
30869
|
await eventHandler?.dispose();
|
|
30502
30870
|
disposePromptRequestCancellation();
|
|
@@ -30567,6 +30935,20 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
30567
30935
|
_meta: this.buildQuotaMeta(sessionState)
|
|
30568
30936
|
};
|
|
30569
30937
|
}
|
|
30938
|
+
terminalFailurePromptResponse(sessionState, eventHandler, turnId, allowUnattributed = false) {
|
|
30939
|
+
const failureMeta = eventHandler.getTerminalSessionFailureMeta(turnId, allowUnattributed);
|
|
30940
|
+
if (failureMeta === null) {
|
|
30941
|
+
return null;
|
|
30942
|
+
}
|
|
30943
|
+
return {
|
|
30944
|
+
stopReason: "end_turn",
|
|
30945
|
+
usage: this.buildPromptUsage(sessionState.lastTokenUsage),
|
|
30946
|
+
_meta: {
|
|
30947
|
+
...this.buildQuotaMeta(sessionState),
|
|
30948
|
+
...failureMeta
|
|
30949
|
+
}
|
|
30950
|
+
};
|
|
30951
|
+
}
|
|
30570
30952
|
buildQuotaMeta(sessionState) {
|
|
30571
30953
|
const lastTokenUsage = sessionState.lastTokenUsage;
|
|
30572
30954
|
const modelName = sessionState.currentModelId.replace(/\[.*?]$/, "");
|
|
@@ -30589,7 +30971,7 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
30589
30971
|
return await operation();
|
|
30590
30972
|
} catch (err) {
|
|
30591
30973
|
const exitCode = this.getExitCode();
|
|
30592
|
-
const requestErrorCode =
|
|
30974
|
+
const requestErrorCode = CODEX_PROCESS_EXITED_ERROR_CODE;
|
|
30593
30975
|
if (exitCode == 3221225781) {
|
|
30594
30976
|
throw new RequestError(requestErrorCode, `VC++ redistributable should be installed`);
|
|
30595
30977
|
}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "1.
|
|
6
|
+
"version": "1.2.0",
|
|
7
7
|
"description": "",
|
|
8
8
|
"main": "dist/index.js",
|
|
9
9
|
"bin": {
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"example:steering": "node --import tsx examples/steering.ts",
|
|
37
37
|
"example:steering:multistep": "node --import tsx examples/steering.ts",
|
|
38
38
|
"generate-types": "./node_modules/.bin/codex app-server generate-ts --out src/app-server",
|
|
39
|
+
"release:preflight": "bash scripts/release-preflight.sh",
|
|
39
40
|
"test": "vitest run",
|
|
40
41
|
"test:e2e": "npm run build && RUN_E2E_TESTS=true vitest run src/__tests__/CodexACPAgent/e2e",
|
|
41
42
|
"test:watch": "vitest",
|