@alas-ide/codex-acp 2.5.0-alas.4 → 2.5.0-alas.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +526 -425
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -34696,6 +34696,370 @@ function goalSessionInfoUpdate(goal, capabilities) {
34696
34696
  };
34697
34697
  }
34698
34698
 
34699
+ // src/SessionFailureExtension.ts
34700
+ var NEUTRAL_SESSION_FAILURE_META_KEY = "sessionFailure";
34701
+ var SESSION_FAILURE_VERSION = 1;
34702
+ function neutralSessionFailureCapability() {
34703
+ return { version: SESSION_FAILURE_VERSION };
34704
+ }
34705
+ function clientRequestsNeutralSessionFailure(capabilities) {
34706
+ const declared = capabilities?._meta?.[NEUTRAL_SESSION_FAILURE_META_KEY];
34707
+ if (declared === null || typeof declared !== "object" || Array.isArray(declared)) return false;
34708
+ const version2 = declared["version"];
34709
+ return typeof version2 === "number" && Number.isInteger(version2) && version2 >= 1;
34710
+ }
34711
+ var NeutralSessionFailureLedger = class {
34712
+ revisions = /* @__PURE__ */ new Map();
34713
+ nextIncident = 1;
34714
+ incidentNumber() {
34715
+ return this.nextIncident++;
34716
+ }
34717
+ /** The lowest revision of `id` that is at least `proposed` and above every revision already sent. */
34718
+ revision(id, proposed) {
34719
+ const revision = Math.max(proposed, (this.revisions.get(id) ?? 0) + 1);
34720
+ this.revisions.set(id, revision);
34721
+ return revision;
34722
+ }
34723
+ };
34724
+
34725
+ // src/AgentFileChangeReport.ts
34726
+ import fs2 from "node:fs";
34727
+ import path5 from "node:path";
34728
+ var AGENT_FILE_CHANGE_REPORT_VERSION = 1;
34729
+ var AGENT_FILE_CHANGE_REPORT_MAX_PATHS = 1024;
34730
+ var AGENT_FILE_CHANGE_REPORT_MAX_PATH_LENGTH = 4096;
34731
+ var AGENT_FILE_CHANGE_REPORT_MAX_TOTAL_BYTES = 256 * 1024;
34732
+ var AGENT_FILE_CHANGE_REPORT_MAX_DIFF_BYTES = 8 * 1024 * 1024;
34733
+ var NEUTRAL_AGENT_FILE_CHANGE_REPORT_META_KEY = "agentFileChangeReport";
34734
+ var NEUTRAL_AGENT_FILE_CHANGE_REPORT_REQUEST_META_KEY = "agentFileChangeReportRequest";
34735
+ var TURN_DIFF_UNCERTAINTY = "Codex turn diffs may omit same-content renames and changes made outside apply_patch, including shell commands, version-control commands, generators, and child processes.";
34736
+ function captureAgentFileChangeWorkspace(cwd, additionalDirectories) {
34737
+ const lexicalCwd = parseWorkspaceRoot(cwd);
34738
+ return {
34739
+ cwd,
34740
+ additionalDirectories: [...additionalDirectories],
34741
+ diffRoot: lexicalCwd === null ? cwd : findDiffDisplayRoot(lexicalCwd).value
34742
+ };
34743
+ }
34744
+ function neutralAgentFileChangeReportCapability() {
34745
+ return { version: AGENT_FILE_CHANGE_REPORT_VERSION };
34746
+ }
34747
+ function clientRequestsNeutralAgentFileChangeReport(capabilities) {
34748
+ const declared = asRecord2(capabilities?._meta?.[NEUTRAL_AGENT_FILE_CHANGE_REPORT_META_KEY]);
34749
+ const version2 = declared?.["version"];
34750
+ return typeof version2 === "number" && Number.isInteger(version2) && version2 >= 1;
34751
+ }
34752
+ function parseAgentFileChangeReportRequest(meta3) {
34753
+ const jetbrains = asRecord2(meta3?.[JETBRAINS_META_KEY]);
34754
+ const air = asRecord2(jetbrains?.[AIR_META_KEY]);
34755
+ return validateAgentFileChangeReportRequest(air?.[AIR_AGENT_FILE_CHANGE_REPORT_REQUEST_KEY]);
34756
+ }
34757
+ function parseNeutralAgentFileChangeReportRequest(meta3) {
34758
+ return validateAgentFileChangeReportRequest(meta3?.[NEUTRAL_AGENT_FILE_CHANGE_REPORT_REQUEST_META_KEY]);
34759
+ }
34760
+ function agentFileChangeReportRequestFor(format, meta3) {
34761
+ switch (format) {
34762
+ case "none":
34763
+ return null;
34764
+ case "air":
34765
+ return parseAgentFileChangeReportRequest(meta3);
34766
+ case "neutral":
34767
+ return parseNeutralAgentFileChangeReportRequest(meta3);
34768
+ }
34769
+ }
34770
+ function agentFileChangeReportMeta(format, report) {
34771
+ if (format === "neutral") {
34772
+ return { [NEUTRAL_AGENT_FILE_CHANGE_REPORT_META_KEY]: report };
34773
+ }
34774
+ return {
34775
+ [JETBRAINS_META_KEY]: {
34776
+ [AIR_META_KEY]: {
34777
+ [AIR_EXTENSION_VERSION_KEY]: AIR_EXTENSION_VERSION,
34778
+ [AIR_AGENT_FILE_CHANGE_REPORT_KEY]: report
34779
+ }
34780
+ }
34781
+ };
34782
+ }
34783
+ function validateAgentFileChangeReportRequest(value) {
34784
+ const request = asRecord2(value);
34785
+ if (request === null || !hasOnlyKeys(request, ["version", "requestId"]) || request["version"] !== AGENT_FILE_CHANGE_REPORT_VERSION) {
34786
+ return null;
34787
+ }
34788
+ const requestId = request["requestId"];
34789
+ if (typeof requestId !== "string" || !/^[A-Za-z0-9._:-]{1,128}$/.test(requestId)) {
34790
+ return null;
34791
+ }
34792
+ return { version: AGENT_FILE_CHANGE_REPORT_VERSION, requestId };
34793
+ }
34794
+ function createReportedAgentFileChangeReport(requestId, diff, workspace) {
34795
+ if (Buffer.byteLength(diff, "utf8") > AGENT_FILE_CHANGE_REPORT_MAX_DIFF_BYTES) {
34796
+ throw new AgentFileChangeReportError("invalidOutput", "The turn diff exceeds the parser input limit");
34797
+ }
34798
+ const parsedReport = parseTurnDiff(diff);
34799
+ const normalized = normalizeFileChangeReport(parsedReport, workspace);
34800
+ return fitReportedAgentFileChangeReport({
34801
+ version: AGENT_FILE_CHANGE_REPORT_VERSION,
34802
+ requestId,
34803
+ status: "reported",
34804
+ ...normalized
34805
+ });
34806
+ }
34807
+ function createUnavailableAgentFileChangeReport(requestId, reason) {
34808
+ return {
34809
+ version: AGENT_FILE_CHANGE_REPORT_VERSION,
34810
+ requestId,
34811
+ status: "unavailable",
34812
+ reason
34813
+ };
34814
+ }
34815
+ var AgentFileChangeReportError = class extends Error {
34816
+ reason;
34817
+ constructor(reason, message) {
34818
+ super(message);
34819
+ this.name = "AgentFileChangeReportError";
34820
+ this.reason = reason;
34821
+ }
34822
+ };
34823
+ function parseTurnDiff(diff) {
34824
+ if (diff.trim() === "") {
34825
+ return { paths: [], complete: false, uncertainty: TURN_DIFF_UNCERTAINTY };
34826
+ }
34827
+ let patches;
34828
+ try {
34829
+ patches = parsePatch(diff);
34830
+ } catch (error62) {
34831
+ throw new AgentFileChangeReportError(
34832
+ "invalidOutput",
34833
+ `The turn diff is not valid unified diff: ${error62 instanceof Error ? error62.message : String(error62)}`
34834
+ );
34835
+ }
34836
+ if (patches.length === 0) {
34837
+ throw new AgentFileChangeReportError("invalidOutput", "The turn diff contains no parseable file patches");
34838
+ }
34839
+ const rawFileHeaders = extractRawFileHeaders(diff);
34840
+ const paths = [];
34841
+ for (const [index, patch] of patches.entries()) {
34842
+ const rawHeaders = rawFileHeaders[index];
34843
+ const oldPath = normalizeDiffPath(selectLosslessFileName(rawHeaders?.oldFileName, patch.oldFileName));
34844
+ const newPath = normalizeDiffPath(selectLosslessFileName(rawHeaders?.newFileName, patch.newFileName));
34845
+ if (oldPath === null && newPath === null) {
34846
+ throw new AgentFileChangeReportError("invalidOutput", "The turn diff contains a patch without a file path");
34847
+ }
34848
+ if (oldPath !== null) paths.push(oldPath);
34849
+ if (newPath !== null) paths.push(newPath);
34850
+ }
34851
+ return { paths, complete: false, uncertainty: TURN_DIFF_UNCERTAINTY };
34852
+ }
34853
+ function extractRawFileHeaders(diff) {
34854
+ return diff.split(/(?=^diff --git )/m).filter((section) => section.trim() !== "").map((section) => {
34855
+ const lines = section.split(/\r?\n/);
34856
+ for (let index = 0; index < lines.length; index += 1) {
34857
+ const line = lines[index];
34858
+ if (line?.startsWith("@@ ")) return null;
34859
+ const nextLine = lines[index + 1];
34860
+ if (line?.startsWith("--- ") && nextLine?.startsWith("+++ ")) {
34861
+ return {
34862
+ oldFileName: line.slice(4),
34863
+ newFileName: nextLine.slice(4)
34864
+ };
34865
+ }
34866
+ }
34867
+ return null;
34868
+ });
34869
+ }
34870
+ function selectLosslessFileName(rawFileName, parsedFileName) {
34871
+ return rawFileName === void 0 || rawFileName.startsWith('"') ? parsedFileName : rawFileName;
34872
+ }
34873
+ function normalizeDiffPath(value) {
34874
+ if (value === void 0 || value === "/dev/null") return null;
34875
+ return value.startsWith("a/") || value.startsWith("b/") ? value.slice(2) : value;
34876
+ }
34877
+ function fitReportedAgentFileChangeReport(report) {
34878
+ const paths = [...report.paths];
34879
+ let truncated = report.truncated;
34880
+ let fitted = report;
34881
+ while (Buffer.byteLength(JSON.stringify(fitted), "utf8") > AGENT_FILE_CHANGE_REPORT_MAX_TOTAL_BYTES) {
34882
+ if (paths.length === 0) {
34883
+ throw new AgentFileChangeReportError("invalidOutput", "The audit report exceeds the wire limit");
34884
+ }
34885
+ paths.pop();
34886
+ truncated = true;
34887
+ fitted = {
34888
+ ...report,
34889
+ paths: [...paths],
34890
+ declaredComplete: false,
34891
+ truncated
34892
+ };
34893
+ }
34894
+ return fitted;
34895
+ }
34896
+ function normalizeFileChangeReport(report, workspace) {
34897
+ const lexicalCwd = parseWorkspaceRoot(workspace.cwd);
34898
+ if (lexicalCwd === null) {
34899
+ throw new AgentFileChangeReportError("providerError", "The session working directory is not absolute");
34900
+ }
34901
+ const cwd = canonicalizeWorkspaceRoot(lexicalCwd);
34902
+ const diffRoot = parseWorkspaceRoot(workspace.diffRoot);
34903
+ if (diffRoot === null || diffRoot.flavor !== cwd.flavor) {
34904
+ throw new AgentFileChangeReportError("providerError", "The captured turn-diff root is invalid");
34905
+ }
34906
+ const roots = [cwd, ...workspace.additionalDirectories.flatMap((directory) => {
34907
+ const root = normalizeWorkspaceRoot(directory);
34908
+ return root === null || root.flavor !== cwd.flavor ? [] : [root];
34909
+ })];
34910
+ const paths = [];
34911
+ const seen = /* @__PURE__ */ new Set();
34912
+ let totalBytes = 0;
34913
+ let truncated = false;
34914
+ for (const reportedPath of report.paths) {
34915
+ if (reportedPath.length > AGENT_FILE_CHANGE_REPORT_MAX_PATH_LENGTH) {
34916
+ truncated = true;
34917
+ continue;
34918
+ }
34919
+ const normalized = normalizeReportedPath(reportedPath, diffRoot, roots);
34920
+ if (normalized === null || normalized.value.length > AGENT_FILE_CHANGE_REPORT_MAX_PATH_LENGTH) {
34921
+ truncated = true;
34922
+ continue;
34923
+ }
34924
+ const key = normalized.flavor === "windows" ? normalized.value.toLowerCase() : normalized.value;
34925
+ if (seen.has(key)) {
34926
+ continue;
34927
+ }
34928
+ const bytes = Buffer.byteLength(normalized.value, "utf8");
34929
+ if (paths.length >= AGENT_FILE_CHANGE_REPORT_MAX_PATHS || totalBytes + bytes > AGENT_FILE_CHANGE_REPORT_MAX_TOTAL_BYTES) {
34930
+ truncated = true;
34931
+ continue;
34932
+ }
34933
+ seen.add(key);
34934
+ paths.push(normalized.value);
34935
+ totalBytes += bytes;
34936
+ }
34937
+ return {
34938
+ paths,
34939
+ declaredComplete: report.complete && !truncated,
34940
+ truncated,
34941
+ ...report.uncertainty ? { uncertainty: report.uncertainty } : {}
34942
+ };
34943
+ }
34944
+ function normalizeWorkspaceRoot(value) {
34945
+ const root = parseWorkspaceRoot(value);
34946
+ return root === null ? null : canonicalizeWorkspaceRoot(root);
34947
+ }
34948
+ function parseWorkspaceRoot(value) {
34949
+ const trimmed = value.trim();
34950
+ if (!isValidPathText(trimmed)) {
34951
+ return null;
34952
+ }
34953
+ if (isWindowsAbsolutePath(trimmed)) {
34954
+ return {
34955
+ value: path5.win32.normalize(trimmed.replace(/\//g, "\\")),
34956
+ flavor: "windows"
34957
+ };
34958
+ }
34959
+ if (path5.posix.isAbsolute(trimmed)) {
34960
+ return {
34961
+ value: path5.posix.normalize(trimmed.replace(/\\/g, "/")),
34962
+ flavor: "posix"
34963
+ };
34964
+ }
34965
+ return null;
34966
+ }
34967
+ function normalizeReportedPath(value, relativeRoot, roots) {
34968
+ if (!isValidPathText(value) || /^[A-Za-z]:[^\\/]/.test(value) || /^\\\\[?.]\\/.test(value) || /^(?:\\\\|\/\/)/.test(value) && !isWindowsAbsolutePath(value) || relativeRoot.flavor === "windows" && /^\\(?!\\)/.test(value) || /^[A-Za-z][A-Za-z0-9+.-]*:\/\//.test(value)) {
34969
+ return null;
34970
+ }
34971
+ let candidate;
34972
+ if (isWindowsAbsolutePath(value)) {
34973
+ candidate = { value: path5.win32.normalize(value.replace(/\//g, "\\")), flavor: "windows" };
34974
+ } else if (path5.posix.isAbsolute(value)) {
34975
+ candidate = { value: path5.posix.normalize(value.replace(/\\/g, "/")), flavor: "posix" };
34976
+ } else if (relativeRoot.flavor === "windows") {
34977
+ candidate = {
34978
+ value: path5.win32.resolve(relativeRoot.value, value.replace(/\//g, "\\")),
34979
+ flavor: "windows"
34980
+ };
34981
+ } else {
34982
+ candidate = {
34983
+ value: path5.posix.resolve(relativeRoot.value, value.replace(/\\/g, "/")),
34984
+ flavor: "posix"
34985
+ };
34986
+ }
34987
+ candidate = canonicalizeReportedPath(candidate);
34988
+ return roots.some((root) => pathIsStrictlyInside(root, candidate)) ? candidate : null;
34989
+ }
34990
+ function findDiffDisplayRoot(cwd) {
34991
+ if (!isNativePathFlavor(cwd.flavor)) return cwd;
34992
+ const pathImplementation = cwd.flavor === "windows" ? path5.win32 : path5.posix;
34993
+ let current = cwd.value;
34994
+ while (true) {
34995
+ if (fs2.existsSync(pathImplementation.join(current, ".git"))) {
34996
+ return canonicalizeWorkspaceRoot({ value: current, flavor: cwd.flavor });
34997
+ }
34998
+ const parent = pathImplementation.dirname(current);
34999
+ if (parent === current) return cwd;
35000
+ current = parent;
35001
+ }
35002
+ }
35003
+ function canonicalizeWorkspaceRoot(root) {
35004
+ if (!isNativePathFlavor(root.flavor)) {
35005
+ return root;
35006
+ }
35007
+ return { ...root, value: canonicalizeFromExistingAncestor(root.value) };
35008
+ }
35009
+ function canonicalizeReportedPath(candidate) {
35010
+ if (!isNativePathFlavor(candidate.flavor)) {
35011
+ return candidate;
35012
+ }
35013
+ const parent = canonicalizeFromExistingAncestor(path5.dirname(candidate.value));
35014
+ return { ...candidate, value: path5.resolve(parent, path5.basename(candidate.value)) };
35015
+ }
35016
+ function canonicalizeFromExistingAncestor(value) {
35017
+ const original = path5.resolve(value);
35018
+ let current = original;
35019
+ const missingSegments = [];
35020
+ while (true) {
35021
+ try {
35022
+ const canonical = fs2.realpathSync.native(current);
35023
+ return path5.resolve(canonical, ...missingSegments.reverse());
35024
+ } catch (error62) {
35025
+ if (!isMissingPathError(error62)) return original;
35026
+ }
35027
+ const parent = path5.dirname(current);
35028
+ if (parent === current) return original;
35029
+ missingSegments.push(path5.basename(current));
35030
+ current = parent;
35031
+ }
35032
+ }
35033
+ function isMissingPathError(error62) {
35034
+ if (typeof error62 !== "object" || error62 === null || !("code" in error62)) return false;
35035
+ return error62.code === "ENOENT" || error62.code === "ENOTDIR";
35036
+ }
35037
+ function isNativePathFlavor(flavor) {
35038
+ return process.platform === "win32" ? flavor === "windows" : flavor === "posix";
35039
+ }
35040
+ function pathIsStrictlyInside(root, candidate) {
35041
+ if (root.flavor !== candidate.flavor) {
35042
+ return false;
35043
+ }
35044
+ const pathImplementation = root.flavor === "windows" ? path5.win32 : path5.posix;
35045
+ const relative = pathImplementation.relative(root.value, candidate.value);
35046
+ return relative.length > 0 && !pathImplementation.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${pathImplementation.sep}`);
35047
+ }
35048
+ function isWindowsAbsolutePath(value) {
35049
+ const portable = value.replace(/\\/g, "/");
35050
+ return /^[A-Za-z]:\//.test(portable) || /^\/\/[^/]+\/[^/]+(?:\/|$)/.test(portable);
35051
+ }
35052
+ function isValidPathText(value) {
35053
+ return value.length > 0 && !/[\u0000-\u001F\u007F-\u009F]/.test(value);
35054
+ }
35055
+ function asRecord2(value) {
35056
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
35057
+ }
35058
+ function hasOnlyKeys(value, allowed) {
35059
+ const allowedKeys = new Set(allowed);
35060
+ return Object.keys(value).every((key) => allowedKeys.has(key));
35061
+ }
35062
+
34699
35063
  // src/tool-calls/ClientCapabilities.ts
34700
35064
  var ClientCapabilities = class _ClientCapabilities {
34701
35065
  static DEFAULT = new _ClientCapabilities({
@@ -34708,6 +35072,8 @@ var ClientCapabilities = class _ClientCapabilities {
34708
35072
  planUpdates: false,
34709
35073
  air: { rawInputRendering: false, planContentDelta: false, diffPatch: false },
34710
35074
  diffPatchFormat: "none",
35075
+ sessionFailureFormat: "none",
35076
+ agentFileChangeReportFormat: "none",
34711
35077
  permissionPromptFields: false
34712
35078
  });
34713
35079
  /** The client declares `_meta.jetbrains.air`. */
@@ -34726,6 +35092,8 @@ var ClientCapabilities = class _ClientCapabilities {
34726
35092
  planUpdates;
34727
35093
  air;
34728
35094
  diffPatchFormat;
35095
+ sessionFailureFormat;
35096
+ agentFileChangeReportFormat;
34729
35097
  /** A permission request has its own `title` and `description` (ACP v2). */
34730
35098
  permissionPromptFields;
34731
35099
  constructor(values) {
@@ -34738,6 +35106,8 @@ var ClientCapabilities = class _ClientCapabilities {
34738
35106
  this.planUpdates = values.planUpdates;
34739
35107
  this.air = values.air;
34740
35108
  this.diffPatchFormat = values.diffPatchFormat;
35109
+ this.sessionFailureFormat = values.sessionFailureFormat;
35110
+ this.agentFileChangeReportFormat = values.agentFileChangeReportFormat;
34741
35111
  this.permissionPromptFields = values.permissionPromptFields;
34742
35112
  }
34743
35113
  static from(capabilities) {
@@ -34759,6 +35129,10 @@ var ClientCapabilities = class _ClientCapabilities {
34759
35129
  },
34760
35130
  // A client that declares both forms gets only the client-neutral one.
34761
35131
  diffPatchFormat: clientRequestsNeutralDiffPatch(capabilities) ? "neutral" : diffPatch ? "air" : "none",
35132
+ // Likewise, and the client-neutral form selects the whole typed failure lane.
35133
+ sessionFailureFormat: clientRequestsNeutralSessionFailure(capabilities) ? "neutral" : clientSupportsAirCapability(capabilities, AIR_SESSION_FAILURE_KEY) ? "air" : "none",
35134
+ // Likewise. The request and the report use the selected form.
35135
+ agentFileChangeReportFormat: clientRequestsNeutralAgentFileChangeReport(capabilities) ? "neutral" : clientSupportsAirCapability(capabilities, AIR_AGENT_FILE_CHANGE_REPORT_KEY) ? "air" : "none",
34762
35136
  permissionPromptFields: false
34763
35137
  });
34764
35138
  }
@@ -34781,6 +35155,10 @@ var ClientCapabilities = class _ClientCapabilities {
34781
35155
  planUpdates: true,
34782
35156
  air: { rawInputRendering: false, planContentDelta: false, diffPatch: false },
34783
35157
  diffPatchFormat: "acpV2",
35158
+ // ACP v2 has its own terminal failure state, so it ignores the client-neutral opt-in.
35159
+ sessionFailureFormat: clientSupportsAirCapability(capabilities, AIR_SESSION_FAILURE_KEY) ? "air" : "none",
35160
+ // The client-neutral report is an ACP v1 extension, so v2 ignores its opt-in.
35161
+ agentFileChangeReportFormat: clientSupportsAirCapability(capabilities, AIR_AGENT_FILE_CHANGE_REPORT_KEY) ? "air" : "none",
34784
35162
  permissionPromptFields: true
34785
35163
  });
34786
35164
  }
@@ -36451,413 +36829,114 @@ var StandardSubagentEventRouter = class {
36451
36829
  }
36452
36830
  await this.publishCancellation(child);
36453
36831
  }
36454
- /** Historical state bypasses the current foreground cache and grants no runtime ownership. */
36455
- async replayTurnOutcome(threadId, turn) {
36456
- const child = this.children.get(threadId);
36457
- const state = turnEndState(turn);
36458
- if (!child || state === void 0 || this.retired) return;
36459
- await this.ensureAnnounced(child);
36460
- if (this.retired) return;
36461
- await this.session.updateSubagentState(child.sessionId, child.parentSessionId, state);
36462
- }
36463
- childSessionIds() {
36464
- return [...this.children.values()].map((child) => child.sessionId);
36465
- }
36466
- isNativeSubagentThread(threadId) {
36467
- return this.children.has(threadId);
36468
- }
36469
- shouldIgnore() {
36470
- return false;
36471
- }
36472
- notificationSessionId(notification) {
36473
- const threadId = notification.params.threadId;
36474
- return typeof threadId === "string" ? this.children.get(threadId)?.sessionId ?? this.rootSessionId : this.rootSessionId;
36475
- }
36476
- takeBufferedNotifications() {
36477
- return this.replayQueue.splice(0);
36478
- }
36479
- async waitForMaterializedSession(threadId) {
36480
- if (this.retired) return null;
36481
- await this.materializePending();
36482
- const child = this.children.get(threadId);
36483
- if (child) await this.ensureAnnounced(child);
36484
- return child?.sessionId ?? null;
36485
- }
36486
- closingChildSessions(notification) {
36487
- if (notification.method !== "turn/completed") return [];
36488
- const child = this.children.get(notification.params.threadId);
36489
- const turn = notification.params.turn;
36490
- if (!child || turn.status === "inProgress" || child.endedTurns.has(turn.id) || child.turnId !== null && child.turnId !== turn.id) return [];
36491
- return [{
36492
- threadId: child.threadId,
36493
- sessionId: child.sessionId,
36494
- state: turn.status === "interrupted" ? "cancelled" : turn.status === "failed" ? "failed" : "completed"
36495
- }];
36496
- }
36497
- async wait(_signal) {
36498
- return "settled";
36499
- }
36500
- hasOutstanding() {
36501
- return [...this.children.values()].some((child) => child.turnId !== null);
36502
- }
36503
- legacyActivityStarted(item) {
36504
- this.legacyActivities.add(item.id);
36505
- return SubagentActivityReporter.activity(item, "in_progress", "start");
36506
- }
36507
- legacyActivityCompleted(item) {
36508
- return SubagentActivityReporter.activity(item, "completed", this.legacyActivities.delete(item.id) ? "update" : "start");
36509
- }
36510
- legacyCollaborationStarted(item) {
36511
- return CollabAgentReporter.started(item);
36512
- }
36513
- legacyCollaborationCompleted(item) {
36514
- return CollabAgentReporter.completed(item);
36515
- }
36516
- };
36517
- function turnEndState(turn) {
36518
- switch (turn.status) {
36519
- case "inProgress":
36520
- return void 0;
36521
- case "completed":
36522
- return toV2IdleState({ stopReason: "end_turn" });
36523
- case "interrupted":
36524
- return toV2IdleState({ stopReason: "cancelled" });
36525
- case "failed":
36526
- return toV2IdleState({ stopReason: "end_turn" }, {
36527
- code: -32603,
36528
- message: turn.error ? readableServiceErrorMessage(turn.error.message) : "Subagent work failed"
36529
- });
36530
- }
36531
- }
36532
-
36533
- // src/RateLimitsMap.ts
36534
- function rateLimitId(snapshot, fallback = "codex") {
36535
- return snapshot.limitId ?? fallback;
36536
- }
36537
- function createRateLimitsMap(response) {
36538
- const result = /* @__PURE__ */ new Map();
36539
- const snapshots = Object.entries(response.rateLimitsByLimitId ?? {}).filter((entry) => entry[1] !== void 0);
36540
- if (snapshots.length === 0) {
36541
- snapshots.push([rateLimitId(response.rateLimits), response.rateLimits]);
36542
- }
36543
- for (const [fallbackId, snapshot] of snapshots) {
36544
- const limitId = rateLimitId(snapshot, fallbackId);
36545
- result.set(limitId, {
36546
- limitId,
36547
- limitName: snapshot.limitName ?? limitId,
36548
- snapshot
36549
- });
36550
- }
36551
- return result;
36552
- }
36553
- function mergeRateLimitSnapshot(previous, update) {
36554
- return {
36555
- ...update,
36556
- limitId: update.limitId ?? "codex",
36557
- credits: update.credits ?? previous.credits,
36558
- individualLimit: update.individualLimit ?? previous.individualLimit,
36559
- spendControlReached: update.spendControlReached ?? previous.spendControlReached,
36560
- planType: update.planType ?? previous.planType
36561
- };
36562
- }
36563
-
36564
- // src/AgentFileChangeReport.ts
36565
- import fs2 from "node:fs";
36566
- import path5 from "node:path";
36567
- var AGENT_FILE_CHANGE_REPORT_VERSION = 1;
36568
- var AGENT_FILE_CHANGE_REPORT_MAX_PATHS = 1024;
36569
- var AGENT_FILE_CHANGE_REPORT_MAX_PATH_LENGTH = 4096;
36570
- var AGENT_FILE_CHANGE_REPORT_MAX_TOTAL_BYTES = 256 * 1024;
36571
- var AGENT_FILE_CHANGE_REPORT_MAX_DIFF_BYTES = 8 * 1024 * 1024;
36572
- var TURN_DIFF_UNCERTAINTY = "Codex turn diffs may omit same-content renames and changes made outside apply_patch, including shell commands, version-control commands, generators, and child processes.";
36573
- function captureAgentFileChangeWorkspace(cwd, additionalDirectories) {
36574
- const lexicalCwd = parseWorkspaceRoot(cwd);
36575
- return {
36576
- cwd,
36577
- additionalDirectories: [...additionalDirectories],
36578
- diffRoot: lexicalCwd === null ? cwd : findDiffDisplayRoot(lexicalCwd).value
36579
- };
36580
- }
36581
- function parseAgentFileChangeReportRequest(meta3) {
36582
- const jetbrains = asRecord2(meta3?.[JETBRAINS_META_KEY]);
36583
- const air = asRecord2(jetbrains?.[AIR_META_KEY]);
36584
- const request = asRecord2(air?.[AIR_AGENT_FILE_CHANGE_REPORT_REQUEST_KEY]);
36585
- if (request === null || !hasOnlyKeys(request, ["version", "requestId"]) || request["version"] !== AGENT_FILE_CHANGE_REPORT_VERSION) {
36586
- return null;
36587
- }
36588
- const requestId = request["requestId"];
36589
- if (typeof requestId !== "string" || !/^[A-Za-z0-9._:-]{1,128}$/.test(requestId)) {
36590
- return null;
36591
- }
36592
- return { version: AGENT_FILE_CHANGE_REPORT_VERSION, requestId };
36593
- }
36594
- function createReportedAgentFileChangeReport(requestId, diff, workspace) {
36595
- if (Buffer.byteLength(diff, "utf8") > AGENT_FILE_CHANGE_REPORT_MAX_DIFF_BYTES) {
36596
- throw new AgentFileChangeReportError("invalidOutput", "The turn diff exceeds the parser input limit");
36597
- }
36598
- const parsedReport = parseTurnDiff(diff);
36599
- const normalized = normalizeFileChangeReport(parsedReport, workspace);
36600
- return fitReportedAgentFileChangeReport({
36601
- version: AGENT_FILE_CHANGE_REPORT_VERSION,
36602
- requestId,
36603
- status: "reported",
36604
- ...normalized
36605
- });
36606
- }
36607
- function createUnavailableAgentFileChangeReport(requestId, reason) {
36608
- return {
36609
- version: AGENT_FILE_CHANGE_REPORT_VERSION,
36610
- requestId,
36611
- status: "unavailable",
36612
- reason
36613
- };
36614
- }
36615
- var AgentFileChangeReportError = class extends Error {
36616
- reason;
36617
- constructor(reason, message) {
36618
- super(message);
36619
- this.name = "AgentFileChangeReportError";
36620
- this.reason = reason;
36621
- }
36622
- };
36623
- function parseTurnDiff(diff) {
36624
- if (diff.trim() === "") {
36625
- return { paths: [], complete: false, uncertainty: TURN_DIFF_UNCERTAINTY };
36626
- }
36627
- let patches;
36628
- try {
36629
- patches = parsePatch(diff);
36630
- } catch (error62) {
36631
- throw new AgentFileChangeReportError(
36632
- "invalidOutput",
36633
- `The turn diff is not valid unified diff: ${error62 instanceof Error ? error62.message : String(error62)}`
36634
- );
36635
- }
36636
- if (patches.length === 0) {
36637
- throw new AgentFileChangeReportError("invalidOutput", "The turn diff contains no parseable file patches");
36638
- }
36639
- const rawFileHeaders = extractRawFileHeaders(diff);
36640
- const paths = [];
36641
- for (const [index, patch] of patches.entries()) {
36642
- const rawHeaders = rawFileHeaders[index];
36643
- const oldPath = normalizeDiffPath(selectLosslessFileName(rawHeaders?.oldFileName, patch.oldFileName));
36644
- const newPath = normalizeDiffPath(selectLosslessFileName(rawHeaders?.newFileName, patch.newFileName));
36645
- if (oldPath === null && newPath === null) {
36646
- throw new AgentFileChangeReportError("invalidOutput", "The turn diff contains a patch without a file path");
36647
- }
36648
- if (oldPath !== null) paths.push(oldPath);
36649
- if (newPath !== null) paths.push(newPath);
36650
- }
36651
- return { paths, complete: false, uncertainty: TURN_DIFF_UNCERTAINTY };
36652
- }
36653
- function extractRawFileHeaders(diff) {
36654
- return diff.split(/(?=^diff --git )/m).filter((section) => section.trim() !== "").map((section) => {
36655
- const lines = section.split(/\r?\n/);
36656
- for (let index = 0; index < lines.length; index += 1) {
36657
- const line = lines[index];
36658
- if (line?.startsWith("@@ ")) return null;
36659
- const nextLine = lines[index + 1];
36660
- if (line?.startsWith("--- ") && nextLine?.startsWith("+++ ")) {
36661
- return {
36662
- oldFileName: line.slice(4),
36663
- newFileName: nextLine.slice(4)
36664
- };
36665
- }
36666
- }
36667
- return null;
36668
- });
36669
- }
36670
- function selectLosslessFileName(rawFileName, parsedFileName) {
36671
- return rawFileName === void 0 || rawFileName.startsWith('"') ? parsedFileName : rawFileName;
36672
- }
36673
- function normalizeDiffPath(value) {
36674
- if (value === void 0 || value === "/dev/null") return null;
36675
- return value.startsWith("a/") || value.startsWith("b/") ? value.slice(2) : value;
36676
- }
36677
- function fitReportedAgentFileChangeReport(report) {
36678
- const paths = [...report.paths];
36679
- let truncated = report.truncated;
36680
- let fitted = report;
36681
- while (Buffer.byteLength(JSON.stringify(fitted), "utf8") > AGENT_FILE_CHANGE_REPORT_MAX_TOTAL_BYTES) {
36682
- if (paths.length === 0) {
36683
- throw new AgentFileChangeReportError("invalidOutput", "The audit report exceeds the wire limit");
36684
- }
36685
- paths.pop();
36686
- truncated = true;
36687
- fitted = {
36688
- ...report,
36689
- paths: [...paths],
36690
- declaredComplete: false,
36691
- truncated
36692
- };
36832
+ /** Historical state bypasses the current foreground cache and grants no runtime ownership. */
36833
+ async replayTurnOutcome(threadId, turn) {
36834
+ const child = this.children.get(threadId);
36835
+ const state = turnEndState(turn);
36836
+ if (!child || state === void 0 || this.retired) return;
36837
+ await this.ensureAnnounced(child);
36838
+ if (this.retired) return;
36839
+ await this.session.updateSubagentState(child.sessionId, child.parentSessionId, state);
36693
36840
  }
36694
- return fitted;
36695
- }
36696
- function normalizeFileChangeReport(report, workspace) {
36697
- const lexicalCwd = parseWorkspaceRoot(workspace.cwd);
36698
- if (lexicalCwd === null) {
36699
- throw new AgentFileChangeReportError("providerError", "The session working directory is not absolute");
36841
+ childSessionIds() {
36842
+ return [...this.children.values()].map((child) => child.sessionId);
36700
36843
  }
36701
- const cwd = canonicalizeWorkspaceRoot(lexicalCwd);
36702
- const diffRoot = parseWorkspaceRoot(workspace.diffRoot);
36703
- if (diffRoot === null || diffRoot.flavor !== cwd.flavor) {
36704
- throw new AgentFileChangeReportError("providerError", "The captured turn-diff root is invalid");
36844
+ isNativeSubagentThread(threadId) {
36845
+ return this.children.has(threadId);
36705
36846
  }
36706
- const roots = [cwd, ...workspace.additionalDirectories.flatMap((directory) => {
36707
- const root = normalizeWorkspaceRoot(directory);
36708
- return root === null || root.flavor !== cwd.flavor ? [] : [root];
36709
- })];
36710
- const paths = [];
36711
- const seen = /* @__PURE__ */ new Set();
36712
- let totalBytes = 0;
36713
- let truncated = false;
36714
- for (const reportedPath of report.paths) {
36715
- if (reportedPath.length > AGENT_FILE_CHANGE_REPORT_MAX_PATH_LENGTH) {
36716
- truncated = true;
36717
- continue;
36718
- }
36719
- const normalized = normalizeReportedPath(reportedPath, diffRoot, roots);
36720
- if (normalized === null || normalized.value.length > AGENT_FILE_CHANGE_REPORT_MAX_PATH_LENGTH) {
36721
- truncated = true;
36722
- continue;
36723
- }
36724
- const key = normalized.flavor === "windows" ? normalized.value.toLowerCase() : normalized.value;
36725
- if (seen.has(key)) {
36726
- continue;
36727
- }
36728
- const bytes = Buffer.byteLength(normalized.value, "utf8");
36729
- if (paths.length >= AGENT_FILE_CHANGE_REPORT_MAX_PATHS || totalBytes + bytes > AGENT_FILE_CHANGE_REPORT_MAX_TOTAL_BYTES) {
36730
- truncated = true;
36731
- continue;
36732
- }
36733
- seen.add(key);
36734
- paths.push(normalized.value);
36735
- totalBytes += bytes;
36847
+ shouldIgnore() {
36848
+ return false;
36736
36849
  }
36737
- return {
36738
- paths,
36739
- declaredComplete: report.complete && !truncated,
36740
- truncated,
36741
- ...report.uncertainty ? { uncertainty: report.uncertainty } : {}
36742
- };
36743
- }
36744
- function normalizeWorkspaceRoot(value) {
36745
- const root = parseWorkspaceRoot(value);
36746
- return root === null ? null : canonicalizeWorkspaceRoot(root);
36747
- }
36748
- function parseWorkspaceRoot(value) {
36749
- const trimmed = value.trim();
36750
- if (!isValidPathText(trimmed)) {
36751
- return null;
36850
+ notificationSessionId(notification) {
36851
+ const threadId = notification.params.threadId;
36852
+ return typeof threadId === "string" ? this.children.get(threadId)?.sessionId ?? this.rootSessionId : this.rootSessionId;
36752
36853
  }
36753
- if (isWindowsAbsolutePath(trimmed)) {
36754
- return {
36755
- value: path5.win32.normalize(trimmed.replace(/\//g, "\\")),
36756
- flavor: "windows"
36757
- };
36854
+ takeBufferedNotifications() {
36855
+ return this.replayQueue.splice(0);
36758
36856
  }
36759
- if (path5.posix.isAbsolute(trimmed)) {
36760
- return {
36761
- value: path5.posix.normalize(trimmed.replace(/\\/g, "/")),
36762
- flavor: "posix"
36763
- };
36857
+ async waitForMaterializedSession(threadId) {
36858
+ if (this.retired) return null;
36859
+ await this.materializePending();
36860
+ const child = this.children.get(threadId);
36861
+ if (child) await this.ensureAnnounced(child);
36862
+ return child?.sessionId ?? null;
36764
36863
  }
36765
- return null;
36766
- }
36767
- function normalizeReportedPath(value, relativeRoot, roots) {
36768
- if (!isValidPathText(value) || /^[A-Za-z]:[^\\/]/.test(value) || /^\\\\[?.]\\/.test(value) || /^(?:\\\\|\/\/)/.test(value) && !isWindowsAbsolutePath(value) || relativeRoot.flavor === "windows" && /^\\(?!\\)/.test(value) || /^[A-Za-z][A-Za-z0-9+.-]*:\/\//.test(value)) {
36769
- return null;
36864
+ closingChildSessions(notification) {
36865
+ if (notification.method !== "turn/completed") return [];
36866
+ const child = this.children.get(notification.params.threadId);
36867
+ const turn = notification.params.turn;
36868
+ if (!child || turn.status === "inProgress" || child.endedTurns.has(turn.id) || child.turnId !== null && child.turnId !== turn.id) return [];
36869
+ return [{
36870
+ threadId: child.threadId,
36871
+ sessionId: child.sessionId,
36872
+ state: turn.status === "interrupted" ? "cancelled" : turn.status === "failed" ? "failed" : "completed"
36873
+ }];
36770
36874
  }
36771
- let candidate;
36772
- if (isWindowsAbsolutePath(value)) {
36773
- candidate = { value: path5.win32.normalize(value.replace(/\//g, "\\")), flavor: "windows" };
36774
- } else if (path5.posix.isAbsolute(value)) {
36775
- candidate = { value: path5.posix.normalize(value.replace(/\\/g, "/")), flavor: "posix" };
36776
- } else if (relativeRoot.flavor === "windows") {
36777
- candidate = {
36778
- value: path5.win32.resolve(relativeRoot.value, value.replace(/\//g, "\\")),
36779
- flavor: "windows"
36780
- };
36781
- } else {
36782
- candidate = {
36783
- value: path5.posix.resolve(relativeRoot.value, value.replace(/\\/g, "/")),
36784
- flavor: "posix"
36785
- };
36875
+ async wait(_signal) {
36876
+ return "settled";
36786
36877
  }
36787
- candidate = canonicalizeReportedPath(candidate);
36788
- return roots.some((root) => pathIsStrictlyInside(root, candidate)) ? candidate : null;
36789
- }
36790
- function findDiffDisplayRoot(cwd) {
36791
- if (!isNativePathFlavor(cwd.flavor)) return cwd;
36792
- const pathImplementation = cwd.flavor === "windows" ? path5.win32 : path5.posix;
36793
- let current = cwd.value;
36794
- while (true) {
36795
- if (fs2.existsSync(pathImplementation.join(current, ".git"))) {
36796
- return canonicalizeWorkspaceRoot({ value: current, flavor: cwd.flavor });
36797
- }
36798
- const parent = pathImplementation.dirname(current);
36799
- if (parent === current) return cwd;
36800
- current = parent;
36878
+ hasOutstanding() {
36879
+ return [...this.children.values()].some((child) => child.turnId !== null);
36801
36880
  }
36802
- }
36803
- function canonicalizeWorkspaceRoot(root) {
36804
- if (!isNativePathFlavor(root.flavor)) {
36805
- return root;
36881
+ legacyActivityStarted(item) {
36882
+ this.legacyActivities.add(item.id);
36883
+ return SubagentActivityReporter.activity(item, "in_progress", "start");
36806
36884
  }
36807
- return { ...root, value: canonicalizeFromExistingAncestor(root.value) };
36808
- }
36809
- function canonicalizeReportedPath(candidate) {
36810
- if (!isNativePathFlavor(candidate.flavor)) {
36811
- return candidate;
36885
+ legacyActivityCompleted(item) {
36886
+ return SubagentActivityReporter.activity(item, "completed", this.legacyActivities.delete(item.id) ? "update" : "start");
36812
36887
  }
36813
- const parent = canonicalizeFromExistingAncestor(path5.dirname(candidate.value));
36814
- return { ...candidate, value: path5.resolve(parent, path5.basename(candidate.value)) };
36815
- }
36816
- function canonicalizeFromExistingAncestor(value) {
36817
- const original = path5.resolve(value);
36818
- let current = original;
36819
- const missingSegments = [];
36820
- while (true) {
36821
- try {
36822
- const canonical = fs2.realpathSync.native(current);
36823
- return path5.resolve(canonical, ...missingSegments.reverse());
36824
- } catch (error62) {
36825
- if (!isMissingPathError(error62)) return original;
36826
- }
36827
- const parent = path5.dirname(current);
36828
- if (parent === current) return original;
36829
- missingSegments.push(path5.basename(current));
36830
- current = parent;
36888
+ legacyCollaborationStarted(item) {
36889
+ return CollabAgentReporter.started(item);
36831
36890
  }
36832
- }
36833
- function isMissingPathError(error62) {
36834
- if (typeof error62 !== "object" || error62 === null || !("code" in error62)) return false;
36835
- return error62.code === "ENOENT" || error62.code === "ENOTDIR";
36836
- }
36837
- function isNativePathFlavor(flavor) {
36838
- return process.platform === "win32" ? flavor === "windows" : flavor === "posix";
36839
- }
36840
- function pathIsStrictlyInside(root, candidate) {
36841
- if (root.flavor !== candidate.flavor) {
36842
- return false;
36891
+ legacyCollaborationCompleted(item) {
36892
+ return CollabAgentReporter.completed(item);
36893
+ }
36894
+ };
36895
+ function turnEndState(turn) {
36896
+ switch (turn.status) {
36897
+ case "inProgress":
36898
+ return void 0;
36899
+ case "completed":
36900
+ return toV2IdleState({ stopReason: "end_turn" });
36901
+ case "interrupted":
36902
+ return toV2IdleState({ stopReason: "cancelled" });
36903
+ case "failed":
36904
+ return toV2IdleState({ stopReason: "end_turn" }, {
36905
+ code: -32603,
36906
+ message: turn.error ? readableServiceErrorMessage(turn.error.message) : "Subagent work failed"
36907
+ });
36843
36908
  }
36844
- const pathImplementation = root.flavor === "windows" ? path5.win32 : path5.posix;
36845
- const relative = pathImplementation.relative(root.value, candidate.value);
36846
- return relative.length > 0 && !pathImplementation.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${pathImplementation.sep}`);
36847
- }
36848
- function isWindowsAbsolutePath(value) {
36849
- const portable = value.replace(/\\/g, "/");
36850
- return /^[A-Za-z]:\//.test(portable) || /^\/\/[^/]+\/[^/]+(?:\/|$)/.test(portable);
36851
36909
  }
36852
- function isValidPathText(value) {
36853
- return value.length > 0 && !/[\u0000-\u001F\u007F-\u009F]/.test(value);
36910
+
36911
+ // src/RateLimitsMap.ts
36912
+ function rateLimitId(snapshot, fallback = "codex") {
36913
+ return snapshot.limitId ?? fallback;
36854
36914
  }
36855
- function asRecord2(value) {
36856
- return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
36915
+ function createRateLimitsMap(response) {
36916
+ const result = /* @__PURE__ */ new Map();
36917
+ const snapshots = Object.entries(response.rateLimitsByLimitId ?? {}).filter((entry) => entry[1] !== void 0);
36918
+ if (snapshots.length === 0) {
36919
+ snapshots.push([rateLimitId(response.rateLimits), response.rateLimits]);
36920
+ }
36921
+ for (const [fallbackId, snapshot] of snapshots) {
36922
+ const limitId = rateLimitId(snapshot, fallbackId);
36923
+ result.set(limitId, {
36924
+ limitId,
36925
+ limitName: snapshot.limitName ?? limitId,
36926
+ snapshot
36927
+ });
36928
+ }
36929
+ return result;
36857
36930
  }
36858
- function hasOnlyKeys(value, allowed) {
36859
- const allowedKeys = new Set(allowed);
36860
- return Object.keys(value).every((key) => allowedKeys.has(key));
36931
+ function mergeRateLimitSnapshot(previous, update) {
36932
+ return {
36933
+ ...update,
36934
+ limitId: update.limitId ?? "codex",
36935
+ credits: update.credits ?? previous.credits,
36936
+ individualLimit: update.individualLimit ?? previous.individualLimit,
36937
+ spendControlReached: update.spendControlReached ?? previous.spendControlReached,
36938
+ planType: update.planType ?? previous.planType
36939
+ };
36861
36940
  }
36862
36941
 
36863
36942
  // src/SessionNotice.ts
@@ -37059,6 +37138,7 @@ var SessionIndexTitles = class {
37059
37138
 
37060
37139
  // src/CodexEventHandler.ts
37061
37140
  var MAX_SESSION_FAILURE_TITLE_LENGTH = 240;
37141
+ var SESSION_NOTICE_KIND = "notice";
37062
37142
  var SESSION_FAILURE_POLICY = {
37063
37143
  transport_lost: {
37064
37144
  category: "connection",
@@ -37141,12 +37221,14 @@ function failureWasShownAsMessage(error62) {
37141
37221
  return error62 instanceof RequestError && failuresShownAsMessages.has(error62);
37142
37222
  }
37143
37223
  var CodexEventHandler = class {
37144
- constructor(connection, sessionState, supportsTypedSessionFailures, sessionFailureEpoch, subagents, onAccountUpdated, collectTurnDiffs, supportsCompaction, supportsNotices) {
37224
+ constructor(connection, sessionState, sessionFailureFormat, sessionFailureEpoch, subagents, onAccountUpdated, collectTurnDiffs, supportsCompaction, supportsNotices, neutralFailures) {
37225
+ this.sessionFailureFormat = sessionFailureFormat;
37145
37226
  this.supportsCompaction = supportsCompaction;
37146
37227
  this.supportsNotices = supportsNotices;
37228
+ this.neutralFailures = neutralFailures;
37147
37229
  this.onAccountUpdated = onAccountUpdated;
37148
37230
  this.sessionState = sessionState;
37149
- this.supportsTypedSessionFailures = supportsTypedSessionFailures;
37231
+ this.supportsTypedSessionFailures = sessionFailureFormat !== "none";
37150
37232
  this.sessionFailureEpoch = sessionFailureEpoch;
37151
37233
  this.session = new ACPSessionConnection(connection, sessionState.sessionId);
37152
37234
  this.renderer = new AcpToolCallRenderer(sessionState.clientCapabilities);
@@ -37157,9 +37239,12 @@ var CodexEventHandler = class {
37157
37239
  this.failuresById.set(sessionState.sessionFailure.id, sessionState.sessionFailure);
37158
37240
  }
37159
37241
  }
37242
+ sessionFailureFormat;
37160
37243
  supportsCompaction;
37161
37244
  supportsNotices;
37245
+ neutralFailures;
37162
37246
  sessionState;
37247
+ /** The typed failure lane, in either form. Only {@link createSessionFailureMeta} reads the form itself. */
37163
37248
  supportsTypedSessionFailures;
37164
37249
  sessionFailureEpoch;
37165
37250
  pendingErrors = [];
@@ -37991,11 +38076,12 @@ ${event.details}` : event.summary;
37991
38076
  const previous = this.failuresById.get(id);
37992
38077
  const failure3 = {
37993
38078
  id,
37994
- revision: nextSessionFailureRevision(previous, id),
38079
+ revision: this.sessionFailureRevision(previous, id),
37995
38080
  category: policy.category,
37996
38081
  severity,
37997
38082
  title: readableServiceErrorMessage(title),
37998
- actions: actionsOverride ?? policy.actions
38083
+ actions: actionsOverride ?? policy.actions,
38084
+ kind
37999
38085
  };
38000
38086
  this.failuresById.set(id, failure3);
38001
38087
  this.failureTurnIdById.set(id, attributeToTurn ? turnId : void 0);
@@ -38010,7 +38096,14 @@ ${event.details}` : event.summary;
38010
38096
  return `${turnId}:error`;
38011
38097
  }
38012
38098
  this.allocatedFailureScopes.add(scope);
38013
- return `${scope}:error:${this.sessionFailureEpoch}:${this.nextNoticeId++}`;
38099
+ return `${scope}:error:${this.sessionFailureEpoch}:${this.nextIncidentNumber()}`;
38100
+ }
38101
+ nextIncidentNumber() {
38102
+ return this.sessionFailureFormat === "neutral" ? this.neutralFailures.incidentNumber() : this.nextNoticeId++;
38103
+ }
38104
+ sessionFailureRevision(previous, id) {
38105
+ const revision = nextSessionFailureRevision(previous, id);
38106
+ return this.sessionFailureFormat === "neutral" ? this.neutralFailures.revision(id, revision) : revision;
38014
38107
  }
38015
38108
  /**
38016
38109
  * A retry warning remains the active incident until Codex produces turn content again. That content is
@@ -38043,15 +38136,16 @@ ${event.details}` : event.summary;
38043
38136
  recordSessionNotice(title, details) {
38044
38137
  const key = `${title}\0${details ?? ""}`;
38045
38138
  const previous = this.lastSessionNotice?.key === key ? this.lastSessionNotice.failure : void 0;
38046
- const id = previous?.id ?? `${this.sessionState.sessionId}:notice:${this.sessionFailureEpoch}:${this.nextNoticeId++}`;
38139
+ const id = previous?.id ?? `${this.sessionState.sessionId}:notice:${this.sessionFailureEpoch}:${this.nextIncidentNumber()}`;
38047
38140
  const notice = {
38048
38141
  id,
38049
- revision: nextSessionFailureRevision(previous, id),
38142
+ revision: this.sessionFailureRevision(previous, id),
38050
38143
  category: "unknown",
38051
38144
  severity: "warning",
38052
38145
  title,
38053
38146
  ...details === void 0 ? {} : { details },
38054
- actions: []
38147
+ actions: [],
38148
+ kind: SESSION_NOTICE_KIND
38055
38149
  };
38056
38150
  this.lastSessionNotice = { key, failure: notice };
38057
38151
  return notice;
@@ -38062,11 +38156,15 @@ ${event.details}` : event.summary;
38062
38156
  return combinedTitle.length <= MAX_SESSION_FAILURE_TITLE_LENGTH ? [combinedTitle] : [summary, details];
38063
38157
  }
38064
38158
  createSessionFailureMeta(failure3) {
38159
+ if (this.sessionFailureFormat === "neutral") {
38160
+ return { [NEUTRAL_SESSION_FAILURE_META_KEY]: { version: SESSION_FAILURE_VERSION, ...failure3 } };
38161
+ }
38162
+ const { kind: _kind, ...airFailure } = failure3;
38065
38163
  return {
38066
38164
  [JETBRAINS_META_KEY]: {
38067
38165
  [AIR_META_KEY]: {
38068
38166
  [AIR_EXTENSION_VERSION_KEY]: AIR_EXTENSION_VERSION,
38069
- [AIR_SESSION_FAILURE_KEY]: failure3
38167
+ [AIR_SESSION_FAILURE_KEY]: airFailure
38070
38168
  }
38071
38169
  }
38072
38170
  };
@@ -40623,7 +40721,7 @@ var package_default = {
40623
40721
  publishConfig: {
40624
40722
  access: "public"
40625
40723
  },
40626
- version: "2.5.0-alas.4",
40724
+ version: "2.5.0-alas.6",
40627
40725
  description: "",
40628
40726
  main: "dist/index.js",
40629
40727
  bin: {
@@ -40701,7 +40799,7 @@ var package_default = {
40701
40799
  alasDownstream: {
40702
40800
  upstreamVersion: "2.5.0",
40703
40801
  upstreamCommit: "34e08970aa324a2be985f5080674d5aa91d7666f",
40704
- sourceCommit: "8af13bd4134b9a24acfd06b85418642f3c2742b6"
40802
+ sourceCommit: "7c99c083270255f2960fa4cd2da93f72bdf402ad"
40705
40803
  }
40706
40804
  };
40707
40805
 
@@ -52124,12 +52222,6 @@ var DENY_ALL_ELICITATIONS = {
52124
52222
  handleElicitation: async () => ({ action: "cancel", content: null, _meta: null }),
52125
52223
  handleUserInput: async () => ({ answers: {} })
52126
52224
  };
52127
- function clientSupportsTypedSessionFailures(capabilities) {
52128
- return clientSupportsAirCapability(capabilities, AIR_SESSION_FAILURE_KEY);
52129
- }
52130
- function clientSupportsAgentFileChangeReports(capabilities) {
52131
- return clientSupportsAirCapability(capabilities, AIR_AGENT_FILE_CHANGE_REPORT_KEY);
52132
- }
52133
52225
  function isAuthRequiredError(error62) {
52134
52226
  return error62 instanceof RequestError && error62.code === RequestError.authRequired().code;
52135
52227
  }
@@ -52146,6 +52238,7 @@ var CodexAcpServer = class {
52146
52238
  getExitCode;
52147
52239
  getRecentStderr;
52148
52240
  sessionFailureEpoch;
52241
+ neutralSessionFailures = new NeutralSessionFailureLedger();
52149
52242
  availableCommands;
52150
52243
  clientInfo;
52151
52244
  clientCapabilities;
@@ -52570,6 +52663,16 @@ var CodexAcpServer = class {
52570
52663
  ...protocolVersion === 1 && this.capabilities.diffPatchFormat === "neutral" ? {
52571
52664
  [NEUTRAL_DIFF_PATCH_META_KEY]: neutralDiffPatchCapability()
52572
52665
  } : {},
52666
+ // The client-neutral session failure, see `docs/air-extensions.md`. ACP v2 has its own terminal
52667
+ // failure state, so `ClientCapabilities.fromV2` never selects it.
52668
+ ...protocolVersion === 1 && this.capabilities.sessionFailureFormat === "neutral" ? {
52669
+ [NEUTRAL_SESSION_FAILURE_META_KEY]: neutralSessionFailureCapability()
52670
+ } : {},
52671
+ // The client-neutral agent file-change report, see `docs/air-extensions.md`. It is an ACP v1 extension,
52672
+ // so `ClientCapabilities.fromV2` never selects it.
52673
+ ...protocolVersion === 1 && this.capabilities.agentFileChangeReportFormat === "neutral" ? {
52674
+ [NEUTRAL_AGENT_FILE_CHANGE_REPORT_META_KEY]: neutralAgentFileChangeReportCapability()
52675
+ } : {},
52573
52676
  // The session index for a client that opted in without AIR, see `docs/air-extensions.md`.
52574
52677
  ...neutralSessionIndex ? {
52575
52678
  [NEUTRAL_SESSION_INDEX_META_KEY]: neutralSessionIndex
@@ -53192,13 +53295,14 @@ You have been logged out. Please try again.`);
53192
53295
  baselineEventHandler = new CodexEventHandler(
53193
53296
  this.connection,
53194
53297
  sessionState,
53195
- clientSupportsTypedSessionFailures(this.clientCapabilities),
53298
+ this.capabilities.sessionFailureFormat,
53196
53299
  this.sessionFailureEpoch,
53197
53300
  sessionState.subagents,
53198
53301
  (accountUpdated) => this.handleAccountUpdated(accountUpdated),
53199
53302
  false,
53200
53303
  clientSupportsCompaction(this.clientCapabilities),
53201
- clientSupportsNotices(this.clientCapabilities)
53304
+ clientSupportsNotices(this.clientCapabilities),
53305
+ this.neutralSessionFailures
53202
53306
  );
53203
53307
  }
53204
53308
  await this.trackCodexForegroundWork(sessionState, event);
@@ -54475,13 +54579,14 @@ You have been logged out. Please try again.`);
54475
54579
  const eventHandler = new CodexEventHandler(
54476
54580
  this.connection,
54477
54581
  sessionState,
54478
- clientSupportsTypedSessionFailures(this.clientCapabilities),
54582
+ this.capabilities.sessionFailureFormat,
54479
54583
  this.sessionFailureEpoch,
54480
54584
  sessionState.subagents,
54481
54585
  (accountUpdated) => this.handleAccountUpdated(accountUpdated),
54482
54586
  false,
54483
54587
  clientSupportsCompaction(this.clientCapabilities),
54484
- clientSupportsNotices(this.clientCapabilities)
54588
+ clientSupportsNotices(this.clientCapabilities),
54589
+ this.neutralSessionFailures
54485
54590
  );
54486
54591
  const permissionContext = this.permissionLifecycleContext(sessionState).beginPrompt();
54487
54592
  const toolCallRenderer = new AcpToolCallRenderer(this.capabilities);
@@ -55258,7 +55363,7 @@ You have been logged out. Please try again.`);
55258
55363
  title
55259
55364
  });
55260
55365
  }
55261
- async publishAgentFileChangeReport(sessionState, turnId, request, unavailableReason, turnDiff, workspace) {
55366
+ async publishAgentFileChangeReport(sessionState, format, turnId, request, unavailableReason, turnDiff, workspace) {
55262
55367
  let report;
55263
55368
  try {
55264
55369
  report = turnId === null ? createUnavailableAgentFileChangeReport(request.requestId, unavailableReason) : createReportedAgentFileChangeReport(request.requestId, turnDiff, workspace);
@@ -55276,14 +55381,7 @@ You have been logged out. Please try again.`);
55276
55381
  const session = new ACPSessionConnection(this.connection, sessionState.sessionId);
55277
55382
  await session.update({
55278
55383
  sessionUpdate: "session_info_update",
55279
- _meta: {
55280
- [JETBRAINS_META_KEY]: {
55281
- [AIR_META_KEY]: {
55282
- [AIR_EXTENSION_VERSION_KEY]: AIR_EXTENSION_VERSION,
55283
- [AIR_AGENT_FILE_CHANGE_REPORT_KEY]: report
55284
- }
55285
- }
55286
- }
55384
+ _meta: agentFileChangeReportMeta(format, report)
55287
55385
  });
55288
55386
  } catch (error62) {
55289
55387
  logger.error("Failed to publish agent file-change report", error62);
@@ -55956,7 +56054,8 @@ You have been logged out. Please try again.`);
55956
56054
  const sessionState = this.getSessionState(params.sessionId);
55957
56055
  const clearing = sessionState.promptSuggestions?.clear();
55958
56056
  if (clearing) await clearing;
55959
- const agentFileChangeReportRequest = clientSupportsAgentFileChangeReports(this.clientCapabilities) ? parseAgentFileChangeReportRequest(params._meta) : null;
56057
+ const agentFileChangeReportFormat = this.capabilities.agentFileChangeReportFormat;
56058
+ const agentFileChangeReportRequest = agentFileChangeReportRequestFor(agentFileChangeReportFormat, params._meta);
55960
56059
  const agentFileChangeWorkspace = agentFileChangeReportRequest === null ? null : captureAgentFileChangeWorkspace(sessionState.cwd, sessionState.additionalDirectories);
55961
56060
  let agentFileChangeReportTurnId = null;
55962
56061
  let agentFileChangeReportUnavailableReason = "providerError";
@@ -56038,13 +56137,14 @@ You have been logged out. Please try again.`);
56038
56137
  const promptEventHandler = new CodexEventHandler(
56039
56138
  this.connection,
56040
56139
  sessionState,
56041
- clientSupportsTypedSessionFailures(this.clientCapabilities),
56140
+ this.capabilities.sessionFailureFormat,
56042
56141
  this.sessionFailureEpoch,
56043
56142
  sessionState.subagents,
56044
56143
  (accountUpdated) => this.handleAccountUpdated(accountUpdated),
56045
56144
  agentFileChangeReportRequest !== null,
56046
56145
  clientSupportsCompaction(this.clientCapabilities),
56047
- clientSupportsNotices(this.clientCapabilities)
56146
+ clientSupportsNotices(this.clientCapabilities),
56147
+ this.neutralSessionFailures
56048
56148
  );
56049
56149
  eventHandler = promptEventHandler;
56050
56150
  const permissionLifecycle = this.permissionLifecycleContext(sessionState);
@@ -56463,7 +56563,7 @@ You have been logged out. Please try again.`);
56463
56563
  }
56464
56564
  insertion?.onFailure?.(toV2Failure(err));
56465
56565
  const isUnexpectedFailure = !(err instanceof RequestError);
56466
- if (eventHandler !== null && clientSupportsTypedSessionFailures(this.clientCapabilities) && (isProcessExit || isUnexpectedFailure)) {
56566
+ if (eventHandler !== null && this.capabilities.sessionFailureFormat !== "none" && (isProcessExit || isUnexpectedFailure)) {
56467
56567
  eventHandler.recordSyntheticTerminalFailure(
56468
56568
  isProcessExit ? "transport_lost" : "internal_error",
56469
56569
  sessionState.currentTurnId
@@ -56489,7 +56589,7 @@ You have been logged out. Please try again.`);
56489
56589
  } catch (error62) {
56490
56590
  logger.error("Failed to publish terminal compaction or subagent state during prompt cleanup", error62);
56491
56591
  }
56492
- if (agentFileChangeReportRequest !== null && agentFileChangeWorkspace !== null) {
56592
+ if (agentFileChangeReportFormat !== "none" && agentFileChangeReportRequest !== null && agentFileChangeWorkspace !== null) {
56493
56593
  if (promptWasCancelled || activePrompt.signal.aborted || this.sessionIsClosing(params.sessionId)) {
56494
56594
  agentFileChangeReportTurnId = null;
56495
56595
  agentFileChangeReportUnavailableReason = "cancelled";
@@ -56499,6 +56599,7 @@ You have been logged out. Please try again.`);
56499
56599
  }
56500
56600
  await this.publishAgentFileChangeReport(
56501
56601
  sessionState,
56602
+ agentFileChangeReportFormat,
56502
56603
  agentFileChangeReportTurnId,
56503
56604
  agentFileChangeReportRequest,
56504
56605
  agentFileChangeReportUnavailableReason,
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "2.5.0-alas.4",
6
+ "version": "2.5.0-alas.6",
7
7
  "description": "",
8
8
  "main": "dist/index.js",
9
9
  "bin": {
@@ -81,6 +81,6 @@
81
81
  "alasDownstream": {
82
82
  "upstreamVersion": "2.5.0",
83
83
  "upstreamCommit": "34e08970aa324a2be985f5080674d5aa91d7666f",
84
- "sourceCommit": "8af13bd4134b9a24acfd06b85418642f3c2742b6"
84
+ "sourceCommit": "7c99c083270255f2960fa4cd2da93f72bdf402ad"
85
85
  }
86
86
  }