@alas-ide/codex-acp 2.5.0-alas.5 → 2.5.0-alas.7

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 +680 -595
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -32121,6 +32121,17 @@ var CODEX_NETWORK_PERMISSION_TITLE = "Allow network access?";
32121
32121
  var CODEX_FILE_CHANGE_PERMISSION_TITLE = "Make edits?";
32122
32122
  var CODEX_ADDITIONAL_PERMISSIONS_TITLE = "Grant permissions?";
32123
32123
  var PERMISSION_PROMPT_META_KEY = "permission_prompt";
32124
+ var NEUTRAL_PERMISSION_PRESENTATION_META_KEY = "permissionPresentation";
32125
+ var NEUTRAL_QUESTION_CUSTOM_ANSWER_META_KEY = "questionCustomAnswer";
32126
+ function neutralPresentationCapability() {
32127
+ return { version: 1 };
32128
+ }
32129
+ function clientRequestsNeutralPresentation(capabilities, key) {
32130
+ const declared = capabilities?._meta?.[key];
32131
+ if (declared === null || typeof declared !== "object" || Array.isArray(declared)) return false;
32132
+ const version2 = declared["version"];
32133
+ return typeof version2 === "number" && Number.isInteger(version2) && version2 >= 1;
32134
+ }
32124
32135
  function requestPermissionMeta(capabilities, title, reason) {
32125
32136
  const description = nonBlank(reason);
32126
32137
  const permission = {
@@ -32130,6 +32141,7 @@ function requestPermissionMeta(capabilities, title, reason) {
32130
32141
  };
32131
32142
  const meta3 = {
32132
32143
  ...airOnlyMeta(capabilities.airClient, AIR_PERMISSION_KEY, permission),
32144
+ ...capabilities.neutralPermissionPresentation ? { [NEUTRAL_PERMISSION_PRESENTATION_META_KEY]: permission } : {},
32133
32145
  ...capabilities.permissionPromptFields ? { [PERMISSION_PROMPT_META_KEY]: permission } : {}
32134
32146
  };
32135
32147
  return Object.keys(meta3).length > 0 ? { _meta: meta3 } : {};
@@ -32141,11 +32153,15 @@ function readPermissionMeta(meta3) {
32141
32153
  if (typeof title !== "string") return void 0;
32142
32154
  return { version: 1, title, ...typeof description === "string" ? { description } : {} };
32143
32155
  }
32144
- function optionPermissionMeta(airClient, description) {
32156
+ function optionPermissionMeta(capabilities, description) {
32145
32157
  const normalized = nonBlank(description);
32146
32158
  if (!normalized) return void 0;
32147
32159
  const permission = { version: 1, description: normalized };
32148
- return airOnlyMeta(airClient, AIR_PERMISSION_KEY, permission);
32160
+ const meta3 = {
32161
+ ...airOnlyMeta(capabilities.airClient, AIR_PERMISSION_KEY, permission),
32162
+ ...capabilities.neutralPermissionPresentation ? { [NEUTRAL_PERMISSION_PRESENTATION_META_KEY]: permission } : {}
32163
+ };
32164
+ return Object.keys(meta3).length > 0 ? meta3 : void 0;
32149
32165
  }
32150
32166
  function nonBlank(value) {
32151
32167
  const normalized = value?.trim();
@@ -34722,195 +34738,553 @@ var NeutralSessionFailureLedger = class {
34722
34738
  }
34723
34739
  };
34724
34740
 
34725
- // src/tool-calls/ClientCapabilities.ts
34726
- var ClientCapabilities = class _ClientCapabilities {
34727
- static DEFAULT = new _ClientCapabilities({
34728
- airClient: false,
34729
- airToolCallContract: false,
34730
- goal: false,
34731
- asyncTasks: false,
34732
- terminalOutput: false,
34733
- terminalOutputDelta: false,
34734
- planUpdates: false,
34735
- air: { rawInputRendering: false, planContentDelta: false, diffPatch: false },
34736
- diffPatchFormat: "none",
34737
- sessionFailureFormat: "none",
34738
- permissionPromptFields: false
34739
- });
34740
- /** The client declares `_meta.jetbrains.air`. */
34741
- airClient;
34742
- /** The client gets the tool call reports of the AIR tool call contract. Only AIR on ACP v1 does. */
34743
- airToolCallContract;
34744
- /** The client is AIR or declares `_meta.goal`. */
34745
- goal;
34746
- /** AIR declares its versioned asyncTasks capability; other clients opt in with literal true. */
34747
- asyncTasks;
34748
- /** The client declares `_meta.terminal_output`, the Zed convention for command output chunks. */
34749
- terminalOutput;
34750
- /** The client declares `_meta.terminal_output_delta` and appends the output chunks. */
34751
- terminalOutputDelta;
34752
- /** The client shows `plan_update`. Other clients get the plan as agent message text. */
34753
- planUpdates;
34754
- air;
34755
- diffPatchFormat;
34756
- sessionFailureFormat;
34757
- /** A permission request has its own `title` and `description` (ACP v2). */
34758
- permissionPromptFields;
34759
- constructor(values) {
34760
- this.airClient = values.airClient;
34761
- this.airToolCallContract = values.airToolCallContract;
34762
- this.goal = values.goal;
34763
- this.asyncTasks = values.asyncTasks;
34764
- this.terminalOutput = values.terminalOutput;
34765
- this.terminalOutputDelta = values.terminalOutputDelta;
34766
- this.planUpdates = values.planUpdates;
34767
- this.air = values.air;
34768
- this.diffPatchFormat = values.diffPatchFormat;
34769
- this.sessionFailureFormat = values.sessionFailureFormat;
34770
- this.permissionPromptFields = values.permissionPromptFields;
34771
- }
34772
- static from(capabilities) {
34773
- const airClient = isAirClient(capabilities);
34774
- const diffPatch = clientSupportsAirCapability(capabilities, AIR_DIFF_PATCH_KEY);
34775
- const goal = capabilities?._meta?.["goal"];
34776
- return new _ClientCapabilities({
34777
- airClient,
34778
- airToolCallContract: airClient,
34779
- goal: airClient || goal !== null && typeof goal === "object" && !Array.isArray(goal),
34780
- asyncTasks: airClient ? clientSupportsAirCapability(capabilities, AIR_ASYNC_TASKS_KEY) : capabilities?._meta?.["async-tasks"] === true,
34781
- terminalOutput: capabilities?._meta?.["terminal_output"] === true,
34782
- terminalOutputDelta: capabilities?._meta?.["terminal_output_delta"] === true,
34783
- planUpdates: capabilities?.plan != null,
34784
- air: {
34785
- rawInputRendering: clientSupportsAirCapability(capabilities, AIR_RAW_INPUT_RENDERING_KEY),
34786
- planContentDelta: clientSupportsAirCapability(capabilities, AIR_PLAN_CONTENT_DELTA_KEY),
34787
- diffPatch
34788
- },
34789
- // A client that declares both forms gets only the client-neutral one.
34790
- diffPatchFormat: clientRequestsNeutralDiffPatch(capabilities) ? "neutral" : diffPatch ? "air" : "none",
34791
- // Likewise, and the client-neutral form selects the whole typed failure lane.
34792
- sessionFailureFormat: clientRequestsNeutralSessionFailure(capabilities) ? "neutral" : clientSupportsAirCapability(capabilities, AIR_SESSION_FAILURE_KEY) ? "air" : "none",
34793
- permissionPromptFields: false
34794
- });
34795
- }
34796
- /**
34797
- * The capabilities of an ACP v2 client, from the v1 view of its capabilities (`toV1ClientCapabilitiesView`).
34798
- *
34799
- * Every v2 client gets the standard tool call reports, also AIR: v2 has its own terminal and diff updates.
34800
- * A shell command streams its output into its terminal. A read, search or list command has no terminal,
34801
- * so its output goes to `content` at the end. A diff carries a Git patch. A permission request has its own title.
34802
- * Plan updates need no capability on v2.
34803
- */
34804
- static fromV2(capabilities) {
34805
- return new _ClientCapabilities({
34806
- airClient: isAirClient(capabilities),
34807
- airToolCallContract: false,
34808
- goal: isAirClient(capabilities),
34809
- asyncTasks: clientSupportsAirCapability(capabilities, AIR_ASYNC_TASKS_KEY),
34810
- terminalOutput: true,
34811
- terminalOutputDelta: false,
34812
- planUpdates: true,
34813
- air: { rawInputRendering: false, planContentDelta: false, diffPatch: false },
34814
- diffPatchFormat: "acpV2",
34815
- // ACP v2 has its own terminal failure state, so it ignores the client-neutral opt-in.
34816
- sessionFailureFormat: clientSupportsAirCapability(capabilities, AIR_SESSION_FAILURE_KEY) ? "air" : "none",
34817
- permissionPromptFields: true
34818
- });
34819
- }
34820
- /**
34821
- * The key of the output chunks of a command, or `null` when the client has no chunk channel for it.
34822
- * A client that declares `terminal_output_delta` gets appends for every command.
34823
- * A client that declares `terminal_output` (Zed) gets `terminal_output` for a command that shows a terminal,
34824
- * and no chunks for a read, search or list command.
34825
- * Every other client that is not AIR gets `terminal_output_delta`, as before the tool call contract.
34826
- * AIR without either capability gets no chunks.
34827
- * A command sends its output once: as chunks when the key is not `null`, and in `rawOutput` otherwise.
34828
- */
34829
- terminalOutputKey(terminal) {
34830
- if (this.terminalOutputDelta) return "terminal_output_delta";
34831
- if (this.terminalOutput) return terminal ? "terminal_output" : null;
34832
- return this.airToolCallContract ? null : "terminal_output_delta";
34833
- }
34834
- };
34835
-
34836
- // src/ServiceErrorMessage.ts
34837
- var SERVICE_ERROR_TYPES = /* @__PURE__ */ new Set([
34838
- "invalid_request_error",
34839
- "server_error",
34840
- "rate_limit_error",
34841
- "insufficient_quota",
34842
- "authentication_error",
34843
- "permission_error",
34844
- "not_found_error",
34845
- "conflict_error",
34846
- "overloaded_error"
34847
- ]);
34848
- var INVALID_ENCRYPTED_CONTENT_CODE = "invalid_encrypted_content";
34849
- var INVALID_ENCRYPTED_CONTENT_MESSAGE = "The current provider can't read this thread's earlier reasoning, which another provider or account encrypted. This happens after the provider changes in a thread. Switch back to the previous provider or start a new thread.";
34850
- function workspaceRoutingUnavailableMessage(text) {
34851
- return `Codex can't reach chatgpt.com to look up your ChatGPT workspace. Check your network connection and any proxy, VPN, or firewall that blocks chatgpt.com, then try again. (${text})`;
34741
+ // src/AgentFileChangeReport.ts
34742
+ import fs2 from "node:fs";
34743
+ import path5 from "node:path";
34744
+ var AGENT_FILE_CHANGE_REPORT_VERSION = 1;
34745
+ var AGENT_FILE_CHANGE_REPORT_MAX_PATHS = 1024;
34746
+ var AGENT_FILE_CHANGE_REPORT_MAX_PATH_LENGTH = 4096;
34747
+ var AGENT_FILE_CHANGE_REPORT_MAX_TOTAL_BYTES = 256 * 1024;
34748
+ var AGENT_FILE_CHANGE_REPORT_MAX_DIFF_BYTES = 8 * 1024 * 1024;
34749
+ var NEUTRAL_AGENT_FILE_CHANGE_REPORT_META_KEY = "agentFileChangeReport";
34750
+ var NEUTRAL_AGENT_FILE_CHANGE_REPORT_REQUEST_META_KEY = "agentFileChangeReportRequest";
34751
+ 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.";
34752
+ function captureAgentFileChangeWorkspace(cwd, additionalDirectories) {
34753
+ const lexicalCwd = parseWorkspaceRoot(cwd);
34754
+ return {
34755
+ cwd,
34756
+ additionalDirectories: [...additionalDirectories],
34757
+ diffRoot: lexicalCwd === null ? cwd : findDiffDisplayRoot(lexicalCwd).value
34758
+ };
34852
34759
  }
34853
- function readableServiceErrorMessage(text) {
34854
- if (isWorkspaceRoutingUnavailableMessage(text)) return workspaceRoutingUnavailableMessage(text);
34855
- const value = parseJson(text);
34856
- if (value === void 0) return text;
34857
- if (isInvalidEncryptedContentError(value)) return INVALID_ENCRYPTED_CONTENT_MESSAGE;
34858
- if (!isObject2(value) || Object.keys(value).some((key) => !["type", "status", "error"].includes(key)) || value["type"] !== "error" || typeof value["status"] !== "number" || !Number.isInteger(value["status"]) || value["status"] < 400 || value["status"] > 599 || !isObject2(value["error"])) return text;
34859
- const error62 = value["error"];
34860
- if (Object.keys(error62).some((key) => !["type", "message", "code", "param"].includes(key)) || typeof error62["type"] !== "string" || !SERVICE_ERROR_TYPES.has(error62["type"]) || typeof error62["message"] !== "string" || error62["message"].trim().length === 0 || ["code", "param"].some((key) => key in error62 && error62[key] !== null && typeof error62[key] !== "string")) return text;
34861
- return error62["message"];
34760
+ function neutralAgentFileChangeReportCapability() {
34761
+ return { version: AGENT_FILE_CHANGE_REPORT_VERSION };
34862
34762
  }
34863
- function isInvalidEncryptedContentError(value) {
34864
- if (!isObject2(value) || !isObject2(value["error"])) return false;
34865
- const error62 = value["error"];
34866
- if (error62["code"] === INVALID_ENCRYPTED_CONTENT_CODE) return true;
34867
- const metadata = error62["metadata"];
34868
- if (!isObject2(metadata)) return false;
34869
- return metadata["provider_error_code"] === INVALID_ENCRYPTED_CONTENT_CODE || typeof metadata["raw"] === "string" && isInvalidEncryptedContentError(parseJson(metadata["raw"]));
34763
+ function clientRequestsNeutralAgentFileChangeReport(capabilities) {
34764
+ const declared = asRecord2(capabilities?._meta?.[NEUTRAL_AGENT_FILE_CHANGE_REPORT_META_KEY]);
34765
+ const version2 = declared?.["version"];
34766
+ return typeof version2 === "number" && Number.isInteger(version2) && version2 >= 1;
34870
34767
  }
34871
- function parseJson(text) {
34872
- try {
34873
- return JSON.parse(text);
34874
- } catch {
34875
- return void 0;
34768
+ function parseAgentFileChangeReportRequest(meta3) {
34769
+ const jetbrains = asRecord2(meta3?.[JETBRAINS_META_KEY]);
34770
+ const air = asRecord2(jetbrains?.[AIR_META_KEY]);
34771
+ return validateAgentFileChangeReportRequest(air?.[AIR_AGENT_FILE_CHANGE_REPORT_REQUEST_KEY]);
34772
+ }
34773
+ function parseNeutralAgentFileChangeReportRequest(meta3) {
34774
+ return validateAgentFileChangeReportRequest(meta3?.[NEUTRAL_AGENT_FILE_CHANGE_REPORT_REQUEST_META_KEY]);
34775
+ }
34776
+ function agentFileChangeReportRequestFor(format, meta3) {
34777
+ switch (format) {
34778
+ case "none":
34779
+ return null;
34780
+ case "air":
34781
+ return parseAgentFileChangeReportRequest(meta3);
34782
+ case "neutral":
34783
+ return parseNeutralAgentFileChangeReportRequest(meta3);
34876
34784
  }
34877
34785
  }
34878
- function isObject2(value) {
34879
- return value !== null && typeof value === "object" && !Array.isArray(value);
34786
+ function agentFileChangeReportMeta(format, report) {
34787
+ if (format === "neutral") {
34788
+ return { [NEUTRAL_AGENT_FILE_CHANGE_REPORT_META_KEY]: report };
34789
+ }
34790
+ return {
34791
+ [JETBRAINS_META_KEY]: {
34792
+ [AIR_META_KEY]: {
34793
+ [AIR_EXTENSION_VERSION_KEY]: AIR_EXTENSION_VERSION,
34794
+ [AIR_AGENT_FILE_CHANGE_REPORT_KEY]: report
34795
+ }
34796
+ }
34797
+ };
34880
34798
  }
34881
-
34882
- // src/app-server-recovery/AppServerExit.ts
34883
- var CODEX_PROCESS_EXITED_ERROR_CODE = 1001;
34884
- function likelyOutOfMemory(exit) {
34885
- return exit.signal === "SIGKILL" || exit.code === 137;
34799
+ function validateAgentFileChangeReportRequest(value) {
34800
+ const request = asRecord2(value);
34801
+ if (request === null || !hasOnlyKeys(request, ["version", "requestId"]) || request["version"] !== AGENT_FILE_CHANGE_REPORT_VERSION) {
34802
+ return null;
34803
+ }
34804
+ const requestId = request["requestId"];
34805
+ if (typeof requestId !== "string" || !/^[A-Za-z0-9._:-]{1,128}$/.test(requestId)) {
34806
+ return null;
34807
+ }
34808
+ return { version: AGENT_FILE_CHANGE_REPORT_VERSION, requestId };
34886
34809
  }
34887
- function describeExit(exit) {
34888
- if (exit.error !== void 0) {
34889
- return exit.pid === void 0 ? `could not be started (${exit.error})` : `stopped responding (${exit.error})`;
34810
+ function createReportedAgentFileChangeReport(requestId, diff, workspace) {
34811
+ if (Buffer.byteLength(diff, "utf8") > AGENT_FILE_CHANGE_REPORT_MAX_DIFF_BYTES) {
34812
+ throw new AgentFileChangeReportError("invalidOutput", "The turn diff exceeds the parser input limit");
34890
34813
  }
34891
- const oom = likelyOutOfMemory(exit) ? ", which usually means it ran out of memory" : "";
34892
- if (exit.signal !== null) return `was killed by ${exit.signal}${oom}`;
34893
- return `exited with code ${exit.code ?? "unknown"}${oom}`;
34814
+ const parsedReport = parseTurnDiff(diff);
34815
+ const normalized = normalizeFileChangeReport(parsedReport, workspace);
34816
+ return fitReportedAgentFileChangeReport({
34817
+ version: AGENT_FILE_CHANGE_REPORT_VERSION,
34818
+ requestId,
34819
+ status: "reported",
34820
+ ...normalized
34821
+ });
34894
34822
  }
34895
- var AppServerUnavailableError = class extends RequestError {
34896
- constructor(message, data) {
34897
- super(CODEX_PROCESS_EXITED_ERROR_CODE, message, data);
34898
- this.name = "AppServerUnavailableError";
34823
+ function createUnavailableAgentFileChangeReport(requestId, reason) {
34824
+ return {
34825
+ version: AGENT_FILE_CHANGE_REPORT_VERSION,
34826
+ requestId,
34827
+ status: "unavailable",
34828
+ reason
34829
+ };
34830
+ }
34831
+ var AgentFileChangeReportError = class extends Error {
34832
+ reason;
34833
+ constructor(reason, message) {
34834
+ super(message);
34835
+ this.name = "AgentFileChangeReportError";
34836
+ this.reason = reason;
34899
34837
  }
34900
34838
  };
34901
- var ThreadRefusedError = class extends AppServerUnavailableError {
34902
- constructor(message, data) {
34903
- super(message, data);
34904
- this.name = "ThreadRefusedError";
34839
+ function parseTurnDiff(diff) {
34840
+ if (diff.trim() === "") {
34841
+ return { paths: [], complete: false, uncertainty: TURN_DIFF_UNCERTAINTY };
34905
34842
  }
34906
- };
34907
- var SessionReplacedError = class extends AppServerUnavailableError {
34908
- constructor(sessionId) {
34909
- super(
34910
- `Session ${sessionId} was closed or opened again while the agent reopened it after an app-server restart.`,
34911
- { exitCode: null, signal: null, restartable: true }
34843
+ let patches;
34844
+ try {
34845
+ patches = parsePatch(diff);
34846
+ } catch (error62) {
34847
+ throw new AgentFileChangeReportError(
34848
+ "invalidOutput",
34849
+ `The turn diff is not valid unified diff: ${error62 instanceof Error ? error62.message : String(error62)}`
34912
34850
  );
34913
- this.name = "SessionReplacedError";
34851
+ }
34852
+ if (patches.length === 0) {
34853
+ throw new AgentFileChangeReportError("invalidOutput", "The turn diff contains no parseable file patches");
34854
+ }
34855
+ const rawFileHeaders = extractRawFileHeaders(diff);
34856
+ const paths = [];
34857
+ for (const [index, patch] of patches.entries()) {
34858
+ const rawHeaders = rawFileHeaders[index];
34859
+ const oldPath = normalizeDiffPath(selectLosslessFileName(rawHeaders?.oldFileName, patch.oldFileName));
34860
+ const newPath = normalizeDiffPath(selectLosslessFileName(rawHeaders?.newFileName, patch.newFileName));
34861
+ if (oldPath === null && newPath === null) {
34862
+ throw new AgentFileChangeReportError("invalidOutput", "The turn diff contains a patch without a file path");
34863
+ }
34864
+ if (oldPath !== null) paths.push(oldPath);
34865
+ if (newPath !== null) paths.push(newPath);
34866
+ }
34867
+ return { paths, complete: false, uncertainty: TURN_DIFF_UNCERTAINTY };
34868
+ }
34869
+ function extractRawFileHeaders(diff) {
34870
+ return diff.split(/(?=^diff --git )/m).filter((section) => section.trim() !== "").map((section) => {
34871
+ const lines = section.split(/\r?\n/);
34872
+ for (let index = 0; index < lines.length; index += 1) {
34873
+ const line = lines[index];
34874
+ if (line?.startsWith("@@ ")) return null;
34875
+ const nextLine = lines[index + 1];
34876
+ if (line?.startsWith("--- ") && nextLine?.startsWith("+++ ")) {
34877
+ return {
34878
+ oldFileName: line.slice(4),
34879
+ newFileName: nextLine.slice(4)
34880
+ };
34881
+ }
34882
+ }
34883
+ return null;
34884
+ });
34885
+ }
34886
+ function selectLosslessFileName(rawFileName, parsedFileName) {
34887
+ return rawFileName === void 0 || rawFileName.startsWith('"') ? parsedFileName : rawFileName;
34888
+ }
34889
+ function normalizeDiffPath(value) {
34890
+ if (value === void 0 || value === "/dev/null") return null;
34891
+ return value.startsWith("a/") || value.startsWith("b/") ? value.slice(2) : value;
34892
+ }
34893
+ function fitReportedAgentFileChangeReport(report) {
34894
+ const paths = [...report.paths];
34895
+ let truncated = report.truncated;
34896
+ let fitted = report;
34897
+ while (Buffer.byteLength(JSON.stringify(fitted), "utf8") > AGENT_FILE_CHANGE_REPORT_MAX_TOTAL_BYTES) {
34898
+ if (paths.length === 0) {
34899
+ throw new AgentFileChangeReportError("invalidOutput", "The audit report exceeds the wire limit");
34900
+ }
34901
+ paths.pop();
34902
+ truncated = true;
34903
+ fitted = {
34904
+ ...report,
34905
+ paths: [...paths],
34906
+ declaredComplete: false,
34907
+ truncated
34908
+ };
34909
+ }
34910
+ return fitted;
34911
+ }
34912
+ function normalizeFileChangeReport(report, workspace) {
34913
+ const lexicalCwd = parseWorkspaceRoot(workspace.cwd);
34914
+ if (lexicalCwd === null) {
34915
+ throw new AgentFileChangeReportError("providerError", "The session working directory is not absolute");
34916
+ }
34917
+ const cwd = canonicalizeWorkspaceRoot(lexicalCwd);
34918
+ const diffRoot = parseWorkspaceRoot(workspace.diffRoot);
34919
+ if (diffRoot === null || diffRoot.flavor !== cwd.flavor) {
34920
+ throw new AgentFileChangeReportError("providerError", "The captured turn-diff root is invalid");
34921
+ }
34922
+ const roots = [cwd, ...workspace.additionalDirectories.flatMap((directory) => {
34923
+ const root = normalizeWorkspaceRoot(directory);
34924
+ return root === null || root.flavor !== cwd.flavor ? [] : [root];
34925
+ })];
34926
+ const paths = [];
34927
+ const seen = /* @__PURE__ */ new Set();
34928
+ let totalBytes = 0;
34929
+ let truncated = false;
34930
+ for (const reportedPath of report.paths) {
34931
+ if (reportedPath.length > AGENT_FILE_CHANGE_REPORT_MAX_PATH_LENGTH) {
34932
+ truncated = true;
34933
+ continue;
34934
+ }
34935
+ const normalized = normalizeReportedPath(reportedPath, diffRoot, roots);
34936
+ if (normalized === null || normalized.value.length > AGENT_FILE_CHANGE_REPORT_MAX_PATH_LENGTH) {
34937
+ truncated = true;
34938
+ continue;
34939
+ }
34940
+ const key = normalized.flavor === "windows" ? normalized.value.toLowerCase() : normalized.value;
34941
+ if (seen.has(key)) {
34942
+ continue;
34943
+ }
34944
+ const bytes = Buffer.byteLength(normalized.value, "utf8");
34945
+ if (paths.length >= AGENT_FILE_CHANGE_REPORT_MAX_PATHS || totalBytes + bytes > AGENT_FILE_CHANGE_REPORT_MAX_TOTAL_BYTES) {
34946
+ truncated = true;
34947
+ continue;
34948
+ }
34949
+ seen.add(key);
34950
+ paths.push(normalized.value);
34951
+ totalBytes += bytes;
34952
+ }
34953
+ return {
34954
+ paths,
34955
+ declaredComplete: report.complete && !truncated,
34956
+ truncated,
34957
+ ...report.uncertainty ? { uncertainty: report.uncertainty } : {}
34958
+ };
34959
+ }
34960
+ function normalizeWorkspaceRoot(value) {
34961
+ const root = parseWorkspaceRoot(value);
34962
+ return root === null ? null : canonicalizeWorkspaceRoot(root);
34963
+ }
34964
+ function parseWorkspaceRoot(value) {
34965
+ const trimmed = value.trim();
34966
+ if (!isValidPathText(trimmed)) {
34967
+ return null;
34968
+ }
34969
+ if (isWindowsAbsolutePath(trimmed)) {
34970
+ return {
34971
+ value: path5.win32.normalize(trimmed.replace(/\//g, "\\")),
34972
+ flavor: "windows"
34973
+ };
34974
+ }
34975
+ if (path5.posix.isAbsolute(trimmed)) {
34976
+ return {
34977
+ value: path5.posix.normalize(trimmed.replace(/\\/g, "/")),
34978
+ flavor: "posix"
34979
+ };
34980
+ }
34981
+ return null;
34982
+ }
34983
+ function normalizeReportedPath(value, relativeRoot, roots) {
34984
+ 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)) {
34985
+ return null;
34986
+ }
34987
+ let candidate;
34988
+ if (isWindowsAbsolutePath(value)) {
34989
+ candidate = { value: path5.win32.normalize(value.replace(/\//g, "\\")), flavor: "windows" };
34990
+ } else if (path5.posix.isAbsolute(value)) {
34991
+ candidate = { value: path5.posix.normalize(value.replace(/\\/g, "/")), flavor: "posix" };
34992
+ } else if (relativeRoot.flavor === "windows") {
34993
+ candidate = {
34994
+ value: path5.win32.resolve(relativeRoot.value, value.replace(/\//g, "\\")),
34995
+ flavor: "windows"
34996
+ };
34997
+ } else {
34998
+ candidate = {
34999
+ value: path5.posix.resolve(relativeRoot.value, value.replace(/\\/g, "/")),
35000
+ flavor: "posix"
35001
+ };
35002
+ }
35003
+ candidate = canonicalizeReportedPath(candidate);
35004
+ return roots.some((root) => pathIsStrictlyInside(root, candidate)) ? candidate : null;
35005
+ }
35006
+ function findDiffDisplayRoot(cwd) {
35007
+ if (!isNativePathFlavor(cwd.flavor)) return cwd;
35008
+ const pathImplementation = cwd.flavor === "windows" ? path5.win32 : path5.posix;
35009
+ let current = cwd.value;
35010
+ while (true) {
35011
+ if (fs2.existsSync(pathImplementation.join(current, ".git"))) {
35012
+ return canonicalizeWorkspaceRoot({ value: current, flavor: cwd.flavor });
35013
+ }
35014
+ const parent = pathImplementation.dirname(current);
35015
+ if (parent === current) return cwd;
35016
+ current = parent;
35017
+ }
35018
+ }
35019
+ function canonicalizeWorkspaceRoot(root) {
35020
+ if (!isNativePathFlavor(root.flavor)) {
35021
+ return root;
35022
+ }
35023
+ return { ...root, value: canonicalizeFromExistingAncestor(root.value) };
35024
+ }
35025
+ function canonicalizeReportedPath(candidate) {
35026
+ if (!isNativePathFlavor(candidate.flavor)) {
35027
+ return candidate;
35028
+ }
35029
+ const parent = canonicalizeFromExistingAncestor(path5.dirname(candidate.value));
35030
+ return { ...candidate, value: path5.resolve(parent, path5.basename(candidate.value)) };
35031
+ }
35032
+ function canonicalizeFromExistingAncestor(value) {
35033
+ const original = path5.resolve(value);
35034
+ let current = original;
35035
+ const missingSegments = [];
35036
+ while (true) {
35037
+ try {
35038
+ const canonical = fs2.realpathSync.native(current);
35039
+ return path5.resolve(canonical, ...missingSegments.reverse());
35040
+ } catch (error62) {
35041
+ if (!isMissingPathError(error62)) return original;
35042
+ }
35043
+ const parent = path5.dirname(current);
35044
+ if (parent === current) return original;
35045
+ missingSegments.push(path5.basename(current));
35046
+ current = parent;
35047
+ }
35048
+ }
35049
+ function isMissingPathError(error62) {
35050
+ if (typeof error62 !== "object" || error62 === null || !("code" in error62)) return false;
35051
+ return error62.code === "ENOENT" || error62.code === "ENOTDIR";
35052
+ }
35053
+ function isNativePathFlavor(flavor) {
35054
+ return process.platform === "win32" ? flavor === "windows" : flavor === "posix";
35055
+ }
35056
+ function pathIsStrictlyInside(root, candidate) {
35057
+ if (root.flavor !== candidate.flavor) {
35058
+ return false;
35059
+ }
35060
+ const pathImplementation = root.flavor === "windows" ? path5.win32 : path5.posix;
35061
+ const relative = pathImplementation.relative(root.value, candidate.value);
35062
+ return relative.length > 0 && !pathImplementation.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${pathImplementation.sep}`);
35063
+ }
35064
+ function isWindowsAbsolutePath(value) {
35065
+ const portable = value.replace(/\\/g, "/");
35066
+ return /^[A-Za-z]:\//.test(portable) || /^\/\/[^/]+\/[^/]+(?:\/|$)/.test(portable);
35067
+ }
35068
+ function isValidPathText(value) {
35069
+ return value.length > 0 && !/[\u0000-\u001F\u007F-\u009F]/.test(value);
35070
+ }
35071
+ function asRecord2(value) {
35072
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
35073
+ }
35074
+ function hasOnlyKeys(value, allowed) {
35075
+ const allowedKeys = new Set(allowed);
35076
+ return Object.keys(value).every((key) => allowedKeys.has(key));
35077
+ }
35078
+
35079
+ // src/tool-calls/ClientCapabilities.ts
35080
+ var ClientCapabilities = class _ClientCapabilities {
35081
+ static DEFAULT = new _ClientCapabilities({
35082
+ airClient: false,
35083
+ airToolCallContract: false,
35084
+ goal: false,
35085
+ asyncTasks: false,
35086
+ terminalOutput: false,
35087
+ terminalOutputDelta: false,
35088
+ planUpdates: false,
35089
+ air: { rawInputRendering: false, planContentDelta: false, diffPatch: false },
35090
+ diffPatchFormat: "none",
35091
+ sessionFailureFormat: "none",
35092
+ agentFileChangeReportFormat: "none",
35093
+ permissionPromptFields: false,
35094
+ neutralPermissionPresentation: false,
35095
+ neutralQuestionCustomAnswer: false
35096
+ });
35097
+ /** The client declares `_meta.jetbrains.air`. */
35098
+ airClient;
35099
+ /** The client gets the tool call reports of the AIR tool call contract. Only AIR on ACP v1 does. */
35100
+ airToolCallContract;
35101
+ /** The client is AIR or declares `_meta.goal`. */
35102
+ goal;
35103
+ /** AIR declares its versioned asyncTasks capability; other clients opt in with literal true. */
35104
+ asyncTasks;
35105
+ /** The client declares `_meta.terminal_output`, the Zed convention for command output chunks. */
35106
+ terminalOutput;
35107
+ /** The client declares `_meta.terminal_output_delta` and appends the output chunks. */
35108
+ terminalOutputDelta;
35109
+ /** The client shows `plan_update`. Other clients get the plan as agent message text. */
35110
+ planUpdates;
35111
+ air;
35112
+ diffPatchFormat;
35113
+ sessionFailureFormat;
35114
+ agentFileChangeReportFormat;
35115
+ /** A permission request has its own `title` and `description` (ACP v2). */
35116
+ permissionPromptFields;
35117
+ /** The client gets the permission presentation in `_meta.permissionPresentation`. Only ACP v1 reads the opt-in. */
35118
+ neutralPermissionPresentation;
35119
+ /** A question's note field carries `_meta.questionCustomAnswer`. Only ACP v1 reads the opt-in. */
35120
+ neutralQuestionCustomAnswer;
35121
+ constructor(values) {
35122
+ this.airClient = values.airClient;
35123
+ this.airToolCallContract = values.airToolCallContract;
35124
+ this.goal = values.goal;
35125
+ this.asyncTasks = values.asyncTasks;
35126
+ this.terminalOutput = values.terminalOutput;
35127
+ this.terminalOutputDelta = values.terminalOutputDelta;
35128
+ this.planUpdates = values.planUpdates;
35129
+ this.air = values.air;
35130
+ this.diffPatchFormat = values.diffPatchFormat;
35131
+ this.sessionFailureFormat = values.sessionFailureFormat;
35132
+ this.agentFileChangeReportFormat = values.agentFileChangeReportFormat;
35133
+ this.permissionPromptFields = values.permissionPromptFields;
35134
+ this.neutralPermissionPresentation = values.neutralPermissionPresentation;
35135
+ this.neutralQuestionCustomAnswer = values.neutralQuestionCustomAnswer;
35136
+ }
35137
+ static from(capabilities) {
35138
+ const airClient = isAirClient(capabilities);
35139
+ const diffPatch = clientSupportsAirCapability(capabilities, AIR_DIFF_PATCH_KEY);
35140
+ const goal = capabilities?._meta?.["goal"];
35141
+ return new _ClientCapabilities({
35142
+ airClient,
35143
+ airToolCallContract: airClient,
35144
+ goal: airClient || goal !== null && typeof goal === "object" && !Array.isArray(goal),
35145
+ asyncTasks: airClient ? clientSupportsAirCapability(capabilities, AIR_ASYNC_TASKS_KEY) : capabilities?._meta?.["async-tasks"] === true,
35146
+ terminalOutput: capabilities?._meta?.["terminal_output"] === true,
35147
+ terminalOutputDelta: capabilities?._meta?.["terminal_output_delta"] === true,
35148
+ planUpdates: capabilities?.plan != null,
35149
+ air: {
35150
+ rawInputRendering: clientSupportsAirCapability(capabilities, AIR_RAW_INPUT_RENDERING_KEY),
35151
+ planContentDelta: clientSupportsAirCapability(capabilities, AIR_PLAN_CONTENT_DELTA_KEY),
35152
+ diffPatch
35153
+ },
35154
+ // A client that declares both forms gets only the client-neutral one.
35155
+ diffPatchFormat: clientRequestsNeutralDiffPatch(capabilities) ? "neutral" : diffPatch ? "air" : "none",
35156
+ // Likewise, and the client-neutral form selects the whole typed failure lane.
35157
+ sessionFailureFormat: clientRequestsNeutralSessionFailure(capabilities) ? "neutral" : clientSupportsAirCapability(capabilities, AIR_SESSION_FAILURE_KEY) ? "air" : "none",
35158
+ // Likewise. The request and the report use the selected form.
35159
+ agentFileChangeReportFormat: clientRequestsNeutralAgentFileChangeReport(capabilities) ? "neutral" : clientSupportsAirCapability(capabilities, AIR_AGENT_FILE_CHANGE_REPORT_KEY) ? "air" : "none",
35160
+ permissionPromptFields: false,
35161
+ neutralPermissionPresentation: clientRequestsNeutralPresentation(capabilities, NEUTRAL_PERMISSION_PRESENTATION_META_KEY),
35162
+ neutralQuestionCustomAnswer: clientRequestsNeutralPresentation(capabilities, NEUTRAL_QUESTION_CUSTOM_ANSWER_META_KEY)
35163
+ });
35164
+ }
35165
+ /**
35166
+ * The capabilities of an ACP v2 client, from the v1 view of its capabilities (`toV1ClientCapabilitiesView`).
35167
+ *
35168
+ * Every v2 client gets the standard tool call reports, also AIR: v2 has its own terminal and diff updates.
35169
+ * A shell command streams its output into its terminal. A read, search or list command has no terminal,
35170
+ * so its output goes to `content` at the end. A diff carries a Git patch. A permission request has its own title.
35171
+ * Plan updates need no capability on v2.
35172
+ */
35173
+ static fromV2(capabilities) {
35174
+ return new _ClientCapabilities({
35175
+ airClient: isAirClient(capabilities),
35176
+ airToolCallContract: false,
35177
+ goal: isAirClient(capabilities),
35178
+ asyncTasks: clientSupportsAirCapability(capabilities, AIR_ASYNC_TASKS_KEY),
35179
+ terminalOutput: true,
35180
+ terminalOutputDelta: false,
35181
+ planUpdates: true,
35182
+ air: { rawInputRendering: false, planContentDelta: false, diffPatch: false },
35183
+ diffPatchFormat: "acpV2",
35184
+ // ACP v2 has its own terminal failure state, so it ignores the client-neutral opt-in.
35185
+ sessionFailureFormat: clientSupportsAirCapability(capabilities, AIR_SESSION_FAILURE_KEY) ? "air" : "none",
35186
+ // The client-neutral report is an ACP v1 extension, so v2 ignores its opt-in.
35187
+ agentFileChangeReportFormat: clientSupportsAirCapability(capabilities, AIR_AGENT_FILE_CHANGE_REPORT_KEY) ? "air" : "none",
35188
+ permissionPromptFields: true,
35189
+ // A v2 request has its own title and description, so v2 ignores both client-neutral opt-ins.
35190
+ neutralPermissionPresentation: false,
35191
+ neutralQuestionCustomAnswer: false
35192
+ });
35193
+ }
35194
+ /**
35195
+ * The key of the output chunks of a command, or `null` when the client has no chunk channel for it.
35196
+ * A client that declares `terminal_output_delta` gets appends for every command.
35197
+ * A client that declares `terminal_output` (Zed) gets `terminal_output` for a command that shows a terminal,
35198
+ * and no chunks for a read, search or list command.
35199
+ * Every other client that is not AIR gets `terminal_output_delta`, as before the tool call contract.
35200
+ * AIR without either capability gets no chunks.
35201
+ * A command sends its output once: as chunks when the key is not `null`, and in `rawOutput` otherwise.
35202
+ */
35203
+ terminalOutputKey(terminal) {
35204
+ if (this.terminalOutputDelta) return "terminal_output_delta";
35205
+ if (this.terminalOutput) return terminal ? "terminal_output" : null;
35206
+ return this.airToolCallContract ? null : "terminal_output_delta";
35207
+ }
35208
+ };
35209
+
35210
+ // src/ServiceErrorMessage.ts
35211
+ var SERVICE_ERROR_TYPES = /* @__PURE__ */ new Set([
35212
+ "invalid_request_error",
35213
+ "server_error",
35214
+ "rate_limit_error",
35215
+ "insufficient_quota",
35216
+ "authentication_error",
35217
+ "permission_error",
35218
+ "not_found_error",
35219
+ "conflict_error",
35220
+ "overloaded_error"
35221
+ ]);
35222
+ var INVALID_ENCRYPTED_CONTENT_CODE = "invalid_encrypted_content";
35223
+ var INVALID_ENCRYPTED_CONTENT_MESSAGE = "The current provider can't read this thread's earlier reasoning, which another provider or account encrypted. This happens after the provider changes in a thread. Switch back to the previous provider or start a new thread.";
35224
+ function workspaceRoutingUnavailableMessage(text) {
35225
+ return `Codex can't reach chatgpt.com to look up your ChatGPT workspace. Check your network connection and any proxy, VPN, or firewall that blocks chatgpt.com, then try again. (${text})`;
35226
+ }
35227
+ function readableServiceErrorMessage(text) {
35228
+ if (isWorkspaceRoutingUnavailableMessage(text)) return workspaceRoutingUnavailableMessage(text);
35229
+ const value = parseJson(text);
35230
+ if (value === void 0) return text;
35231
+ if (isInvalidEncryptedContentError(value)) return INVALID_ENCRYPTED_CONTENT_MESSAGE;
35232
+ if (!isObject2(value) || Object.keys(value).some((key) => !["type", "status", "error"].includes(key)) || value["type"] !== "error" || typeof value["status"] !== "number" || !Number.isInteger(value["status"]) || value["status"] < 400 || value["status"] > 599 || !isObject2(value["error"])) return text;
35233
+ const error62 = value["error"];
35234
+ if (Object.keys(error62).some((key) => !["type", "message", "code", "param"].includes(key)) || typeof error62["type"] !== "string" || !SERVICE_ERROR_TYPES.has(error62["type"]) || typeof error62["message"] !== "string" || error62["message"].trim().length === 0 || ["code", "param"].some((key) => key in error62 && error62[key] !== null && typeof error62[key] !== "string")) return text;
35235
+ return error62["message"];
35236
+ }
35237
+ function isInvalidEncryptedContentError(value) {
35238
+ if (!isObject2(value) || !isObject2(value["error"])) return false;
35239
+ const error62 = value["error"];
35240
+ if (error62["code"] === INVALID_ENCRYPTED_CONTENT_CODE) return true;
35241
+ const metadata = error62["metadata"];
35242
+ if (!isObject2(metadata)) return false;
35243
+ return metadata["provider_error_code"] === INVALID_ENCRYPTED_CONTENT_CODE || typeof metadata["raw"] === "string" && isInvalidEncryptedContentError(parseJson(metadata["raw"]));
35244
+ }
35245
+ function parseJson(text) {
35246
+ try {
35247
+ return JSON.parse(text);
35248
+ } catch {
35249
+ return void 0;
35250
+ }
35251
+ }
35252
+ function isObject2(value) {
35253
+ return value !== null && typeof value === "object" && !Array.isArray(value);
35254
+ }
35255
+
35256
+ // src/app-server-recovery/AppServerExit.ts
35257
+ var CODEX_PROCESS_EXITED_ERROR_CODE = 1001;
35258
+ function likelyOutOfMemory(exit) {
35259
+ return exit.signal === "SIGKILL" || exit.code === 137;
35260
+ }
35261
+ function describeExit(exit) {
35262
+ if (exit.error !== void 0) {
35263
+ return exit.pid === void 0 ? `could not be started (${exit.error})` : `stopped responding (${exit.error})`;
35264
+ }
35265
+ const oom = likelyOutOfMemory(exit) ? ", which usually means it ran out of memory" : "";
35266
+ if (exit.signal !== null) return `was killed by ${exit.signal}${oom}`;
35267
+ return `exited with code ${exit.code ?? "unknown"}${oom}`;
35268
+ }
35269
+ var AppServerUnavailableError = class extends RequestError {
35270
+ constructor(message, data) {
35271
+ super(CODEX_PROCESS_EXITED_ERROR_CODE, message, data);
35272
+ this.name = "AppServerUnavailableError";
35273
+ }
35274
+ };
35275
+ var ThreadRefusedError = class extends AppServerUnavailableError {
35276
+ constructor(message, data) {
35277
+ super(message, data);
35278
+ this.name = "ThreadRefusedError";
35279
+ }
35280
+ };
35281
+ var SessionReplacedError = class extends AppServerUnavailableError {
35282
+ constructor(sessionId) {
35283
+ super(
35284
+ `Session ${sessionId} was closed or opened again while the agent reopened it after an app-server restart.`,
35285
+ { exitCode: null, signal: null, restartable: true }
35286
+ );
35287
+ this.name = "SessionReplacedError";
34914
35288
  }
34915
35289
  };
34916
35290
  var UnpersistedSessionLostError = class extends AppServerUnavailableError {
@@ -36484,413 +36858,114 @@ var StandardSubagentEventRouter = class {
36484
36858
  }
36485
36859
  await this.publishCancellation(child);
36486
36860
  }
36487
- /** Historical state bypasses the current foreground cache and grants no runtime ownership. */
36488
- async replayTurnOutcome(threadId, turn) {
36489
- const child = this.children.get(threadId);
36490
- const state = turnEndState(turn);
36491
- if (!child || state === void 0 || this.retired) return;
36492
- await this.ensureAnnounced(child);
36493
- if (this.retired) return;
36494
- await this.session.updateSubagentState(child.sessionId, child.parentSessionId, state);
36495
- }
36496
- childSessionIds() {
36497
- return [...this.children.values()].map((child) => child.sessionId);
36498
- }
36499
- isNativeSubagentThread(threadId) {
36500
- return this.children.has(threadId);
36501
- }
36502
- shouldIgnore() {
36503
- return false;
36504
- }
36505
- notificationSessionId(notification) {
36506
- const threadId = notification.params.threadId;
36507
- return typeof threadId === "string" ? this.children.get(threadId)?.sessionId ?? this.rootSessionId : this.rootSessionId;
36508
- }
36509
- takeBufferedNotifications() {
36510
- return this.replayQueue.splice(0);
36511
- }
36512
- async waitForMaterializedSession(threadId) {
36513
- if (this.retired) return null;
36514
- await this.materializePending();
36515
- const child = this.children.get(threadId);
36516
- if (child) await this.ensureAnnounced(child);
36517
- return child?.sessionId ?? null;
36518
- }
36519
- closingChildSessions(notification) {
36520
- if (notification.method !== "turn/completed") return [];
36521
- const child = this.children.get(notification.params.threadId);
36522
- const turn = notification.params.turn;
36523
- if (!child || turn.status === "inProgress" || child.endedTurns.has(turn.id) || child.turnId !== null && child.turnId !== turn.id) return [];
36524
- return [{
36525
- threadId: child.threadId,
36526
- sessionId: child.sessionId,
36527
- state: turn.status === "interrupted" ? "cancelled" : turn.status === "failed" ? "failed" : "completed"
36528
- }];
36529
- }
36530
- async wait(_signal) {
36531
- return "settled";
36532
- }
36533
- hasOutstanding() {
36534
- return [...this.children.values()].some((child) => child.turnId !== null);
36535
- }
36536
- legacyActivityStarted(item) {
36537
- this.legacyActivities.add(item.id);
36538
- return SubagentActivityReporter.activity(item, "in_progress", "start");
36539
- }
36540
- legacyActivityCompleted(item) {
36541
- return SubagentActivityReporter.activity(item, "completed", this.legacyActivities.delete(item.id) ? "update" : "start");
36542
- }
36543
- legacyCollaborationStarted(item) {
36544
- return CollabAgentReporter.started(item);
36545
- }
36546
- legacyCollaborationCompleted(item) {
36547
- return CollabAgentReporter.completed(item);
36548
- }
36549
- };
36550
- function turnEndState(turn) {
36551
- switch (turn.status) {
36552
- case "inProgress":
36553
- return void 0;
36554
- case "completed":
36555
- return toV2IdleState({ stopReason: "end_turn" });
36556
- case "interrupted":
36557
- return toV2IdleState({ stopReason: "cancelled" });
36558
- case "failed":
36559
- return toV2IdleState({ stopReason: "end_turn" }, {
36560
- code: -32603,
36561
- message: turn.error ? readableServiceErrorMessage(turn.error.message) : "Subagent work failed"
36562
- });
36563
- }
36564
- }
36565
-
36566
- // src/RateLimitsMap.ts
36567
- function rateLimitId(snapshot, fallback = "codex") {
36568
- return snapshot.limitId ?? fallback;
36569
- }
36570
- function createRateLimitsMap(response) {
36571
- const result = /* @__PURE__ */ new Map();
36572
- const snapshots = Object.entries(response.rateLimitsByLimitId ?? {}).filter((entry) => entry[1] !== void 0);
36573
- if (snapshots.length === 0) {
36574
- snapshots.push([rateLimitId(response.rateLimits), response.rateLimits]);
36575
- }
36576
- for (const [fallbackId, snapshot] of snapshots) {
36577
- const limitId = rateLimitId(snapshot, fallbackId);
36578
- result.set(limitId, {
36579
- limitId,
36580
- limitName: snapshot.limitName ?? limitId,
36581
- snapshot
36582
- });
36583
- }
36584
- return result;
36585
- }
36586
- function mergeRateLimitSnapshot(previous, update) {
36587
- return {
36588
- ...update,
36589
- limitId: update.limitId ?? "codex",
36590
- credits: update.credits ?? previous.credits,
36591
- individualLimit: update.individualLimit ?? previous.individualLimit,
36592
- spendControlReached: update.spendControlReached ?? previous.spendControlReached,
36593
- planType: update.planType ?? previous.planType
36594
- };
36595
- }
36596
-
36597
- // src/AgentFileChangeReport.ts
36598
- import fs2 from "node:fs";
36599
- import path5 from "node:path";
36600
- var AGENT_FILE_CHANGE_REPORT_VERSION = 1;
36601
- var AGENT_FILE_CHANGE_REPORT_MAX_PATHS = 1024;
36602
- var AGENT_FILE_CHANGE_REPORT_MAX_PATH_LENGTH = 4096;
36603
- var AGENT_FILE_CHANGE_REPORT_MAX_TOTAL_BYTES = 256 * 1024;
36604
- var AGENT_FILE_CHANGE_REPORT_MAX_DIFF_BYTES = 8 * 1024 * 1024;
36605
- 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.";
36606
- function captureAgentFileChangeWorkspace(cwd, additionalDirectories) {
36607
- const lexicalCwd = parseWorkspaceRoot(cwd);
36608
- return {
36609
- cwd,
36610
- additionalDirectories: [...additionalDirectories],
36611
- diffRoot: lexicalCwd === null ? cwd : findDiffDisplayRoot(lexicalCwd).value
36612
- };
36613
- }
36614
- function parseAgentFileChangeReportRequest(meta3) {
36615
- const jetbrains = asRecord2(meta3?.[JETBRAINS_META_KEY]);
36616
- const air = asRecord2(jetbrains?.[AIR_META_KEY]);
36617
- const request = asRecord2(air?.[AIR_AGENT_FILE_CHANGE_REPORT_REQUEST_KEY]);
36618
- if (request === null || !hasOnlyKeys(request, ["version", "requestId"]) || request["version"] !== AGENT_FILE_CHANGE_REPORT_VERSION) {
36619
- return null;
36620
- }
36621
- const requestId = request["requestId"];
36622
- if (typeof requestId !== "string" || !/^[A-Za-z0-9._:-]{1,128}$/.test(requestId)) {
36623
- return null;
36624
- }
36625
- return { version: AGENT_FILE_CHANGE_REPORT_VERSION, requestId };
36626
- }
36627
- function createReportedAgentFileChangeReport(requestId, diff, workspace) {
36628
- if (Buffer.byteLength(diff, "utf8") > AGENT_FILE_CHANGE_REPORT_MAX_DIFF_BYTES) {
36629
- throw new AgentFileChangeReportError("invalidOutput", "The turn diff exceeds the parser input limit");
36630
- }
36631
- const parsedReport = parseTurnDiff(diff);
36632
- const normalized = normalizeFileChangeReport(parsedReport, workspace);
36633
- return fitReportedAgentFileChangeReport({
36634
- version: AGENT_FILE_CHANGE_REPORT_VERSION,
36635
- requestId,
36636
- status: "reported",
36637
- ...normalized
36638
- });
36639
- }
36640
- function createUnavailableAgentFileChangeReport(requestId, reason) {
36641
- return {
36642
- version: AGENT_FILE_CHANGE_REPORT_VERSION,
36643
- requestId,
36644
- status: "unavailable",
36645
- reason
36646
- };
36647
- }
36648
- var AgentFileChangeReportError = class extends Error {
36649
- reason;
36650
- constructor(reason, message) {
36651
- super(message);
36652
- this.name = "AgentFileChangeReportError";
36653
- this.reason = reason;
36654
- }
36655
- };
36656
- function parseTurnDiff(diff) {
36657
- if (diff.trim() === "") {
36658
- return { paths: [], complete: false, uncertainty: TURN_DIFF_UNCERTAINTY };
36659
- }
36660
- let patches;
36661
- try {
36662
- patches = parsePatch(diff);
36663
- } catch (error62) {
36664
- throw new AgentFileChangeReportError(
36665
- "invalidOutput",
36666
- `The turn diff is not valid unified diff: ${error62 instanceof Error ? error62.message : String(error62)}`
36667
- );
36668
- }
36669
- if (patches.length === 0) {
36670
- throw new AgentFileChangeReportError("invalidOutput", "The turn diff contains no parseable file patches");
36671
- }
36672
- const rawFileHeaders = extractRawFileHeaders(diff);
36673
- const paths = [];
36674
- for (const [index, patch] of patches.entries()) {
36675
- const rawHeaders = rawFileHeaders[index];
36676
- const oldPath = normalizeDiffPath(selectLosslessFileName(rawHeaders?.oldFileName, patch.oldFileName));
36677
- const newPath = normalizeDiffPath(selectLosslessFileName(rawHeaders?.newFileName, patch.newFileName));
36678
- if (oldPath === null && newPath === null) {
36679
- throw new AgentFileChangeReportError("invalidOutput", "The turn diff contains a patch without a file path");
36680
- }
36681
- if (oldPath !== null) paths.push(oldPath);
36682
- if (newPath !== null) paths.push(newPath);
36683
- }
36684
- return { paths, complete: false, uncertainty: TURN_DIFF_UNCERTAINTY };
36685
- }
36686
- function extractRawFileHeaders(diff) {
36687
- return diff.split(/(?=^diff --git )/m).filter((section) => section.trim() !== "").map((section) => {
36688
- const lines = section.split(/\r?\n/);
36689
- for (let index = 0; index < lines.length; index += 1) {
36690
- const line = lines[index];
36691
- if (line?.startsWith("@@ ")) return null;
36692
- const nextLine = lines[index + 1];
36693
- if (line?.startsWith("--- ") && nextLine?.startsWith("+++ ")) {
36694
- return {
36695
- oldFileName: line.slice(4),
36696
- newFileName: nextLine.slice(4)
36697
- };
36698
- }
36699
- }
36700
- return null;
36701
- });
36702
- }
36703
- function selectLosslessFileName(rawFileName, parsedFileName) {
36704
- return rawFileName === void 0 || rawFileName.startsWith('"') ? parsedFileName : rawFileName;
36705
- }
36706
- function normalizeDiffPath(value) {
36707
- if (value === void 0 || value === "/dev/null") return null;
36708
- return value.startsWith("a/") || value.startsWith("b/") ? value.slice(2) : value;
36709
- }
36710
- function fitReportedAgentFileChangeReport(report) {
36711
- const paths = [...report.paths];
36712
- let truncated = report.truncated;
36713
- let fitted = report;
36714
- while (Buffer.byteLength(JSON.stringify(fitted), "utf8") > AGENT_FILE_CHANGE_REPORT_MAX_TOTAL_BYTES) {
36715
- if (paths.length === 0) {
36716
- throw new AgentFileChangeReportError("invalidOutput", "The audit report exceeds the wire limit");
36717
- }
36718
- paths.pop();
36719
- truncated = true;
36720
- fitted = {
36721
- ...report,
36722
- paths: [...paths],
36723
- declaredComplete: false,
36724
- truncated
36725
- };
36861
+ /** Historical state bypasses the current foreground cache and grants no runtime ownership. */
36862
+ async replayTurnOutcome(threadId, turn) {
36863
+ const child = this.children.get(threadId);
36864
+ const state = turnEndState(turn);
36865
+ if (!child || state === void 0 || this.retired) return;
36866
+ await this.ensureAnnounced(child);
36867
+ if (this.retired) return;
36868
+ await this.session.updateSubagentState(child.sessionId, child.parentSessionId, state);
36726
36869
  }
36727
- return fitted;
36728
- }
36729
- function normalizeFileChangeReport(report, workspace) {
36730
- const lexicalCwd = parseWorkspaceRoot(workspace.cwd);
36731
- if (lexicalCwd === null) {
36732
- throw new AgentFileChangeReportError("providerError", "The session working directory is not absolute");
36870
+ childSessionIds() {
36871
+ return [...this.children.values()].map((child) => child.sessionId);
36733
36872
  }
36734
- const cwd = canonicalizeWorkspaceRoot(lexicalCwd);
36735
- const diffRoot = parseWorkspaceRoot(workspace.diffRoot);
36736
- if (diffRoot === null || diffRoot.flavor !== cwd.flavor) {
36737
- throw new AgentFileChangeReportError("providerError", "The captured turn-diff root is invalid");
36873
+ isNativeSubagentThread(threadId) {
36874
+ return this.children.has(threadId);
36738
36875
  }
36739
- const roots = [cwd, ...workspace.additionalDirectories.flatMap((directory) => {
36740
- const root = normalizeWorkspaceRoot(directory);
36741
- return root === null || root.flavor !== cwd.flavor ? [] : [root];
36742
- })];
36743
- const paths = [];
36744
- const seen = /* @__PURE__ */ new Set();
36745
- let totalBytes = 0;
36746
- let truncated = false;
36747
- for (const reportedPath of report.paths) {
36748
- if (reportedPath.length > AGENT_FILE_CHANGE_REPORT_MAX_PATH_LENGTH) {
36749
- truncated = true;
36750
- continue;
36751
- }
36752
- const normalized = normalizeReportedPath(reportedPath, diffRoot, roots);
36753
- if (normalized === null || normalized.value.length > AGENT_FILE_CHANGE_REPORT_MAX_PATH_LENGTH) {
36754
- truncated = true;
36755
- continue;
36756
- }
36757
- const key = normalized.flavor === "windows" ? normalized.value.toLowerCase() : normalized.value;
36758
- if (seen.has(key)) {
36759
- continue;
36760
- }
36761
- const bytes = Buffer.byteLength(normalized.value, "utf8");
36762
- if (paths.length >= AGENT_FILE_CHANGE_REPORT_MAX_PATHS || totalBytes + bytes > AGENT_FILE_CHANGE_REPORT_MAX_TOTAL_BYTES) {
36763
- truncated = true;
36764
- continue;
36765
- }
36766
- seen.add(key);
36767
- paths.push(normalized.value);
36768
- totalBytes += bytes;
36876
+ shouldIgnore() {
36877
+ return false;
36769
36878
  }
36770
- return {
36771
- paths,
36772
- declaredComplete: report.complete && !truncated,
36773
- truncated,
36774
- ...report.uncertainty ? { uncertainty: report.uncertainty } : {}
36775
- };
36776
- }
36777
- function normalizeWorkspaceRoot(value) {
36778
- const root = parseWorkspaceRoot(value);
36779
- return root === null ? null : canonicalizeWorkspaceRoot(root);
36780
- }
36781
- function parseWorkspaceRoot(value) {
36782
- const trimmed = value.trim();
36783
- if (!isValidPathText(trimmed)) {
36784
- return null;
36879
+ notificationSessionId(notification) {
36880
+ const threadId = notification.params.threadId;
36881
+ return typeof threadId === "string" ? this.children.get(threadId)?.sessionId ?? this.rootSessionId : this.rootSessionId;
36785
36882
  }
36786
- if (isWindowsAbsolutePath(trimmed)) {
36787
- return {
36788
- value: path5.win32.normalize(trimmed.replace(/\//g, "\\")),
36789
- flavor: "windows"
36790
- };
36883
+ takeBufferedNotifications() {
36884
+ return this.replayQueue.splice(0);
36791
36885
  }
36792
- if (path5.posix.isAbsolute(trimmed)) {
36793
- return {
36794
- value: path5.posix.normalize(trimmed.replace(/\\/g, "/")),
36795
- flavor: "posix"
36796
- };
36886
+ async waitForMaterializedSession(threadId) {
36887
+ if (this.retired) return null;
36888
+ await this.materializePending();
36889
+ const child = this.children.get(threadId);
36890
+ if (child) await this.ensureAnnounced(child);
36891
+ return child?.sessionId ?? null;
36797
36892
  }
36798
- return null;
36799
- }
36800
- function normalizeReportedPath(value, relativeRoot, roots) {
36801
- 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)) {
36802
- return null;
36893
+ closingChildSessions(notification) {
36894
+ if (notification.method !== "turn/completed") return [];
36895
+ const child = this.children.get(notification.params.threadId);
36896
+ const turn = notification.params.turn;
36897
+ if (!child || turn.status === "inProgress" || child.endedTurns.has(turn.id) || child.turnId !== null && child.turnId !== turn.id) return [];
36898
+ return [{
36899
+ threadId: child.threadId,
36900
+ sessionId: child.sessionId,
36901
+ state: turn.status === "interrupted" ? "cancelled" : turn.status === "failed" ? "failed" : "completed"
36902
+ }];
36803
36903
  }
36804
- let candidate;
36805
- if (isWindowsAbsolutePath(value)) {
36806
- candidate = { value: path5.win32.normalize(value.replace(/\//g, "\\")), flavor: "windows" };
36807
- } else if (path5.posix.isAbsolute(value)) {
36808
- candidate = { value: path5.posix.normalize(value.replace(/\\/g, "/")), flavor: "posix" };
36809
- } else if (relativeRoot.flavor === "windows") {
36810
- candidate = {
36811
- value: path5.win32.resolve(relativeRoot.value, value.replace(/\//g, "\\")),
36812
- flavor: "windows"
36813
- };
36814
- } else {
36815
- candidate = {
36816
- value: path5.posix.resolve(relativeRoot.value, value.replace(/\\/g, "/")),
36817
- flavor: "posix"
36818
- };
36904
+ async wait(_signal) {
36905
+ return "settled";
36819
36906
  }
36820
- candidate = canonicalizeReportedPath(candidate);
36821
- return roots.some((root) => pathIsStrictlyInside(root, candidate)) ? candidate : null;
36822
- }
36823
- function findDiffDisplayRoot(cwd) {
36824
- if (!isNativePathFlavor(cwd.flavor)) return cwd;
36825
- const pathImplementation = cwd.flavor === "windows" ? path5.win32 : path5.posix;
36826
- let current = cwd.value;
36827
- while (true) {
36828
- if (fs2.existsSync(pathImplementation.join(current, ".git"))) {
36829
- return canonicalizeWorkspaceRoot({ value: current, flavor: cwd.flavor });
36830
- }
36831
- const parent = pathImplementation.dirname(current);
36832
- if (parent === current) return cwd;
36833
- current = parent;
36907
+ hasOutstanding() {
36908
+ return [...this.children.values()].some((child) => child.turnId !== null);
36834
36909
  }
36835
- }
36836
- function canonicalizeWorkspaceRoot(root) {
36837
- if (!isNativePathFlavor(root.flavor)) {
36838
- return root;
36910
+ legacyActivityStarted(item) {
36911
+ this.legacyActivities.add(item.id);
36912
+ return SubagentActivityReporter.activity(item, "in_progress", "start");
36839
36913
  }
36840
- return { ...root, value: canonicalizeFromExistingAncestor(root.value) };
36841
- }
36842
- function canonicalizeReportedPath(candidate) {
36843
- if (!isNativePathFlavor(candidate.flavor)) {
36844
- return candidate;
36914
+ legacyActivityCompleted(item) {
36915
+ return SubagentActivityReporter.activity(item, "completed", this.legacyActivities.delete(item.id) ? "update" : "start");
36845
36916
  }
36846
- const parent = canonicalizeFromExistingAncestor(path5.dirname(candidate.value));
36847
- return { ...candidate, value: path5.resolve(parent, path5.basename(candidate.value)) };
36848
- }
36849
- function canonicalizeFromExistingAncestor(value) {
36850
- const original = path5.resolve(value);
36851
- let current = original;
36852
- const missingSegments = [];
36853
- while (true) {
36854
- try {
36855
- const canonical = fs2.realpathSync.native(current);
36856
- return path5.resolve(canonical, ...missingSegments.reverse());
36857
- } catch (error62) {
36858
- if (!isMissingPathError(error62)) return original;
36859
- }
36860
- const parent = path5.dirname(current);
36861
- if (parent === current) return original;
36862
- missingSegments.push(path5.basename(current));
36863
- current = parent;
36917
+ legacyCollaborationStarted(item) {
36918
+ return CollabAgentReporter.started(item);
36864
36919
  }
36865
- }
36866
- function isMissingPathError(error62) {
36867
- if (typeof error62 !== "object" || error62 === null || !("code" in error62)) return false;
36868
- return error62.code === "ENOENT" || error62.code === "ENOTDIR";
36869
- }
36870
- function isNativePathFlavor(flavor) {
36871
- return process.platform === "win32" ? flavor === "windows" : flavor === "posix";
36872
- }
36873
- function pathIsStrictlyInside(root, candidate) {
36874
- if (root.flavor !== candidate.flavor) {
36875
- return false;
36920
+ legacyCollaborationCompleted(item) {
36921
+ return CollabAgentReporter.completed(item);
36922
+ }
36923
+ };
36924
+ function turnEndState(turn) {
36925
+ switch (turn.status) {
36926
+ case "inProgress":
36927
+ return void 0;
36928
+ case "completed":
36929
+ return toV2IdleState({ stopReason: "end_turn" });
36930
+ case "interrupted":
36931
+ return toV2IdleState({ stopReason: "cancelled" });
36932
+ case "failed":
36933
+ return toV2IdleState({ stopReason: "end_turn" }, {
36934
+ code: -32603,
36935
+ message: turn.error ? readableServiceErrorMessage(turn.error.message) : "Subagent work failed"
36936
+ });
36876
36937
  }
36877
- const pathImplementation = root.flavor === "windows" ? path5.win32 : path5.posix;
36878
- const relative = pathImplementation.relative(root.value, candidate.value);
36879
- return relative.length > 0 && !pathImplementation.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${pathImplementation.sep}`);
36880
- }
36881
- function isWindowsAbsolutePath(value) {
36882
- const portable = value.replace(/\\/g, "/");
36883
- return /^[A-Za-z]:\//.test(portable) || /^\/\/[^/]+\/[^/]+(?:\/|$)/.test(portable);
36884
36938
  }
36885
- function isValidPathText(value) {
36886
- return value.length > 0 && !/[\u0000-\u001F\u007F-\u009F]/.test(value);
36939
+
36940
+ // src/RateLimitsMap.ts
36941
+ function rateLimitId(snapshot, fallback = "codex") {
36942
+ return snapshot.limitId ?? fallback;
36887
36943
  }
36888
- function asRecord2(value) {
36889
- return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
36944
+ function createRateLimitsMap(response) {
36945
+ const result = /* @__PURE__ */ new Map();
36946
+ const snapshots = Object.entries(response.rateLimitsByLimitId ?? {}).filter((entry) => entry[1] !== void 0);
36947
+ if (snapshots.length === 0) {
36948
+ snapshots.push([rateLimitId(response.rateLimits), response.rateLimits]);
36949
+ }
36950
+ for (const [fallbackId, snapshot] of snapshots) {
36951
+ const limitId = rateLimitId(snapshot, fallbackId);
36952
+ result.set(limitId, {
36953
+ limitId,
36954
+ limitName: snapshot.limitName ?? limitId,
36955
+ snapshot
36956
+ });
36957
+ }
36958
+ return result;
36890
36959
  }
36891
- function hasOnlyKeys(value, allowed) {
36892
- const allowedKeys = new Set(allowed);
36893
- return Object.keys(value).every((key) => allowedKeys.has(key));
36960
+ function mergeRateLimitSnapshot(previous, update) {
36961
+ return {
36962
+ ...update,
36963
+ limitId: update.limitId ?? "codex",
36964
+ credits: update.credits ?? previous.credits,
36965
+ individualLimit: update.individualLimit ?? previous.individualLimit,
36966
+ spendControlReached: update.spendControlReached ?? previous.spendControlReached,
36967
+ planType: update.planType ?? previous.planType
36968
+ };
36894
36969
  }
36895
36970
 
36896
36971
  // src/SessionNotice.ts
@@ -38891,9 +38966,9 @@ function parsePersistOptions(meta3) {
38891
38966
  function isMcpToolCallApproval(meta3) {
38892
38967
  return isRecord2(meta3) && meta3["codex_approval_kind"] === "mcp_tool_call";
38893
38968
  }
38894
- function buildMcpPermissionOptions(isToolApproval, persistOptions, airClient) {
38969
+ function buildMcpPermissionOptions(isToolApproval, persistOptions, capabilities) {
38895
38970
  const permissionOption2 = (optionId, name, kind, description) => {
38896
- const meta3 = optionPermissionMeta(airClient, description);
38971
+ const meta3 = optionPermissionMeta(capabilities, description);
38897
38972
  return { optionId, name, kind, ...meta3 ? { _meta: meta3 } : {} };
38898
38973
  };
38899
38974
  const options = [permissionOption2(
@@ -38955,7 +39030,7 @@ function buildMcpPermissionRequest(sessionId, params, context, nextStandaloneToo
38955
39030
  options: buildMcpPermissionOptions(
38956
39031
  context.isToolApproval,
38957
39032
  context.persistOptions,
38958
- renderer.capabilities.airClient
39033
+ renderer.capabilities
38959
39034
  )
38960
39035
  },
38961
39036
  correlatedCallId
@@ -39068,7 +39143,7 @@ function userInputResponseValue(content, fieldId) {
39068
39143
  }
39069
39144
  return value;
39070
39145
  }
39071
- function buildUserInputSchema(questions, clientCapabilities, answersRequired) {
39146
+ function buildUserInputSchema(questions, clientCapabilities, answersRequired, neutralCustomAnswer = false) {
39072
39147
  const properties = {};
39073
39148
  const required2 = [];
39074
39149
  const questionIds = new Set(questions.map((question) => question.id));
@@ -39120,7 +39195,10 @@ function buildUserInputSchema(questions, clientCapabilities, answersRequired) {
39120
39195
  properties[userInputNoteFieldId(question.id, questionIds)] = {
39121
39196
  type: "string",
39122
39197
  title: "Additional answer or note",
39123
- _meta: airClient ? withAirMeta({ ...noteMeta, [LEGACY_AIR_CUSTOM_ANSWER_KEY]: true }, AIR_CUSTOM_ANSWER_KEY, true) : noteMeta
39198
+ _meta: {
39199
+ ...airClient ? withAirMeta({ ...noteMeta, [LEGACY_AIR_CUSTOM_ANSWER_KEY]: true }, AIR_CUSTOM_ANSWER_KEY, true) : noteMeta,
39200
+ ...neutralCustomAnswer ? { [NEUTRAL_QUESTION_CUSTOM_ANSWER_META_KEY]: { version: 1, questionId: question.id } } : {}
39201
+ }
39124
39202
  };
39125
39203
  }
39126
39204
  }
@@ -39130,13 +39208,13 @@ function buildUserInputSchema(questions, clientCapabilities, answersRequired) {
39130
39208
  ...answersRequired ? { required: required2 } : {}
39131
39209
  };
39132
39210
  }
39133
- function buildUserInputRequest(params, clientCapabilities) {
39211
+ function buildUserInputRequest(params, clientCapabilities, neutralCustomAnswer) {
39134
39212
  return {
39135
39213
  sessionId: params.threadId,
39136
39214
  toolCallId: params.itemId,
39137
39215
  mode: "form",
39138
39216
  message: "Codex needs your input to continue.",
39139
- requestedSchema: buildUserInputSchema(params.questions, clientCapabilities, true),
39217
+ requestedSchema: buildUserInputSchema(params.questions, clientCapabilities, true, neutralCustomAnswer),
39140
39218
  _meta: {
39141
39219
  codex: {
39142
39220
  autoResolutionMs: params.autoResolutionMs
@@ -39299,7 +39377,7 @@ var CodexElicitationHandler = class {
39299
39377
  return cancellationSignal ? { cancellationSignal } : void 0;
39300
39378
  }
39301
39379
  async requestUserInputElicitation(params) {
39302
- const request = buildUserInputRequest(params, this.clientCapabilities);
39380
+ const request = buildUserInputRequest(params, this.clientCapabilities, this.renderer.capabilities.neutralQuestionCustomAnswer);
39303
39381
  const signal = subagentInteractionSignal(params.threadId, this.cancellationSignal);
39304
39382
  if (params.autoResolutionMs === null) {
39305
39383
  return await withSubagentUserAction(params.threadId, () => this.connection.request(
@@ -40675,7 +40753,7 @@ var package_default = {
40675
40753
  publishConfig: {
40676
40754
  access: "public"
40677
40755
  },
40678
- version: "2.5.0-alas.5",
40756
+ version: "2.5.0-alas.7",
40679
40757
  description: "",
40680
40758
  main: "dist/index.js",
40681
40759
  bin: {
@@ -40753,7 +40831,7 @@ var package_default = {
40753
40831
  alasDownstream: {
40754
40832
  upstreamVersion: "2.5.0",
40755
40833
  upstreamCommit: "34e08970aa324a2be985f5080674d5aa91d7666f",
40756
- sourceCommit: "d1c53266b4d5d1f308323259b81d01ea687104ef"
40834
+ sourceCommit: "4b88aaf47a9765b7b64a51712cca179fc141fa46"
40757
40835
  }
40758
40836
  };
40759
40837
 
@@ -49211,14 +49289,16 @@ var AsyncQuestionClient = class {
49211
49289
  }
49212
49290
  };
49213
49291
  var AsyncQuestionElicitations = class {
49214
- constructor(sessionId, clientCapabilities, client) {
49292
+ constructor(sessionId, clientCapabilities, client, neutralCustomAnswer = false) {
49215
49293
  this.sessionId = sessionId;
49216
49294
  this.clientCapabilities = clientCapabilities;
49217
49295
  this.client = client;
49296
+ this.neutralCustomAnswer = neutralCustomAnswer;
49218
49297
  }
49219
49298
  sessionId;
49220
49299
  clientCapabilities;
49221
49300
  client;
49301
+ neutralCustomAnswer;
49222
49302
  // The running turns that asked a question, each with the signal that cancels its questions.
49223
49303
  turns = /* @__PURE__ */ new Map();
49224
49304
  /** Asks the questions of a completed async message and cancels them when their turn ends; never waits. */
@@ -49263,7 +49343,7 @@ var AsyncQuestionElicitations = class {
49263
49343
  mode: "form",
49264
49344
  message: "Codex keeps working while you answer. An answer sent after it finishes is not delivered.",
49265
49345
  // Questions are answered independently, as in Codex.
49266
- requestedSchema: buildUserInputSchema(questions, this.clientCapabilities, false),
49346
+ requestedSchema: buildUserInputSchema(questions, this.clientCapabilities, false, this.neutralCustomAnswer),
49267
49347
  _meta: { codex: { delivery: "async" } }
49268
49348
  };
49269
49349
  void this.client.createElicitation(request, turn.signal).then(
@@ -52176,9 +52256,6 @@ var DENY_ALL_ELICITATIONS = {
52176
52256
  handleElicitation: async () => ({ action: "cancel", content: null, _meta: null }),
52177
52257
  handleUserInput: async () => ({ answers: {} })
52178
52258
  };
52179
- function clientSupportsAgentFileChangeReports(capabilities) {
52180
- return clientSupportsAirCapability(capabilities, AIR_AGENT_FILE_CHANGE_REPORT_KEY);
52181
- }
52182
52259
  function isAuthRequiredError(error62) {
52183
52260
  return error62 instanceof RequestError && error62.code === RequestError.authRequired().code;
52184
52261
  }
@@ -52625,6 +52702,19 @@ var CodexAcpServer = class {
52625
52702
  ...protocolVersion === 1 && this.capabilities.sessionFailureFormat === "neutral" ? {
52626
52703
  [NEUTRAL_SESSION_FAILURE_META_KEY]: neutralSessionFailureCapability()
52627
52704
  } : {},
52705
+ // The client-neutral agent file-change report, see `docs/air-extensions.md`. It is an ACP v1 extension,
52706
+ // so `ClientCapabilities.fromV2` never selects it.
52707
+ ...protocolVersion === 1 && this.capabilities.agentFileChangeReportFormat === "neutral" ? {
52708
+ [NEUTRAL_AGENT_FILE_CHANGE_REPORT_META_KEY]: neutralAgentFileChangeReportCapability()
52709
+ } : {},
52710
+ // The client-neutral presentation contracts, see `docs/air-extensions.md`. `ClientCapabilities.fromV2`
52711
+ // never selects them.
52712
+ ...this.capabilities.neutralPermissionPresentation ? {
52713
+ [NEUTRAL_PERMISSION_PRESENTATION_META_KEY]: neutralPresentationCapability()
52714
+ } : {},
52715
+ ...this.capabilities.neutralQuestionCustomAnswer ? {
52716
+ [NEUTRAL_QUESTION_CUSTOM_ANSWER_META_KEY]: neutralPresentationCapability()
52717
+ } : {},
52628
52718
  // The session index for a client that opted in without AIR, see `docs/air-extensions.md`.
52629
52719
  ...neutralSessionIndex ? {
52630
52720
  [NEUTRAL_SESSION_INDEX_META_KEY]: neutralSessionIndex
@@ -55315,7 +55405,7 @@ You have been logged out. Please try again.`);
55315
55405
  title
55316
55406
  });
55317
55407
  }
55318
- async publishAgentFileChangeReport(sessionState, turnId, request, unavailableReason, turnDiff, workspace) {
55408
+ async publishAgentFileChangeReport(sessionState, format, turnId, request, unavailableReason, turnDiff, workspace) {
55319
55409
  let report;
55320
55410
  try {
55321
55411
  report = turnId === null ? createUnavailableAgentFileChangeReport(request.requestId, unavailableReason) : createReportedAgentFileChangeReport(request.requestId, turnDiff, workspace);
@@ -55333,14 +55423,7 @@ You have been logged out. Please try again.`);
55333
55423
  const session = new ACPSessionConnection(this.connection, sessionState.sessionId);
55334
55424
  await session.update({
55335
55425
  sessionUpdate: "session_info_update",
55336
- _meta: {
55337
- [JETBRAINS_META_KEY]: {
55338
- [AIR_META_KEY]: {
55339
- [AIR_EXTENSION_VERSION_KEY]: AIR_EXTENSION_VERSION,
55340
- [AIR_AGENT_FILE_CHANGE_REPORT_KEY]: report
55341
- }
55342
- }
55343
- }
55426
+ _meta: agentFileChangeReportMeta(format, report)
55344
55427
  });
55345
55428
  } catch (error62) {
55346
55429
  logger.error("Failed to publish agent file-change report", error62);
@@ -56013,7 +56096,8 @@ You have been logged out. Please try again.`);
56013
56096
  const sessionState = this.getSessionState(params.sessionId);
56014
56097
  const clearing = sessionState.promptSuggestions?.clear();
56015
56098
  if (clearing) await clearing;
56016
- const agentFileChangeReportRequest = clientSupportsAgentFileChangeReports(this.clientCapabilities) ? parseAgentFileChangeReportRequest(params._meta) : null;
56099
+ const agentFileChangeReportFormat = this.capabilities.agentFileChangeReportFormat;
56100
+ const agentFileChangeReportRequest = agentFileChangeReportRequestFor(agentFileChangeReportFormat, params._meta);
56017
56101
  const agentFileChangeWorkspace = agentFileChangeReportRequest === null ? null : captureAgentFileChangeWorkspace(sessionState.cwd, sessionState.additionalDirectories);
56018
56102
  let agentFileChangeReportTurnId = null;
56019
56103
  let agentFileChangeReportUnavailableReason = "providerError";
@@ -56547,7 +56631,7 @@ You have been logged out. Please try again.`);
56547
56631
  } catch (error62) {
56548
56632
  logger.error("Failed to publish terminal compaction or subagent state during prompt cleanup", error62);
56549
56633
  }
56550
- if (agentFileChangeReportRequest !== null && agentFileChangeWorkspace !== null) {
56634
+ if (agentFileChangeReportFormat !== "none" && agentFileChangeReportRequest !== null && agentFileChangeWorkspace !== null) {
56551
56635
  if (promptWasCancelled || activePrompt.signal.aborted || this.sessionIsClosing(params.sessionId)) {
56552
56636
  agentFileChangeReportTurnId = null;
56553
56637
  agentFileChangeReportUnavailableReason = "cancelled";
@@ -56557,6 +56641,7 @@ You have been logged out. Please try again.`);
56557
56641
  }
56558
56642
  await this.publishAgentFileChangeReport(
56559
56643
  sessionState,
56644
+ agentFileChangeReportFormat,
56560
56645
  agentFileChangeReportTurnId,
56561
56646
  agentFileChangeReportRequest,
56562
56647
  agentFileChangeReportUnavailableReason,
@@ -56616,7 +56701,7 @@ You have been logged out. Please try again.`);
56616
56701
  this.connection,
56617
56702
  this.clientCapabilities,
56618
56703
  () => this.codexAcpClient
56619
- ));
56704
+ ), this.capabilities.neutralQuestionCustomAnswer);
56620
56705
  this.asyncQuestions.set(sessionId, questions);
56621
56706
  }
56622
56707
  return questions;