@agentclientprotocol/codex-acp 1.11.1-preview.4 → 1.11.1-preview.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 +582 -633
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -22772,6 +22772,111 @@ function reversePatch(structuredPatch) {
22772
22772
  return reversed;
22773
22773
  }
22774
22774
 
22775
+ // src/DiffStats.ts
22776
+ var DiffStatsCalculator = class {
22777
+ addedFile(text) {
22778
+ return { version: 1, added: this.lineCount(text), removed: 0 };
22779
+ }
22780
+ deletedFile(text) {
22781
+ return { version: 1, added: 0, removed: this.lineCount(text) };
22782
+ }
22783
+ update(patch) {
22784
+ if (patch.isBinary || patch.hunks.length === 0) return null;
22785
+ let added = 0;
22786
+ let removed = 0;
22787
+ let previousOldEnd = 1;
22788
+ let previousNewEnd = 1;
22789
+ for (const hunk of patch.hunks) {
22790
+ const { oldStart, oldLines, newStart, newLines } = hunk;
22791
+ if (![oldStart, oldLines, newStart, newLines].every(Number.isSafeInteger) || oldStart < previousOldEnd || newStart < previousNewEnd || oldLines < 0 || newLines < 0 || newStart - oldStart !== added - removed) return null;
22792
+ let oldConsumed = 0;
22793
+ let newConsumed = 0;
22794
+ let previousWasContent = false;
22795
+ for (const line of hunk.lines) {
22796
+ switch (line[0]) {
22797
+ case "+":
22798
+ added++;
22799
+ newConsumed++;
22800
+ previousWasContent = true;
22801
+ break;
22802
+ case "-":
22803
+ removed++;
22804
+ oldConsumed++;
22805
+ previousWasContent = true;
22806
+ break;
22807
+ case " ":
22808
+ case void 0:
22809
+ oldConsumed++;
22810
+ newConsumed++;
22811
+ previousWasContent = true;
22812
+ break;
22813
+ case "\\":
22814
+ if (!previousWasContent || line.replace(/\r$/, "") !== "\") return null;
22815
+ previousWasContent = false;
22816
+ break;
22817
+ default:
22818
+ return null;
22819
+ }
22820
+ }
22821
+ if (oldConsumed !== oldLines || newConsumed !== newLines) return null;
22822
+ previousOldEnd = oldStart + oldLines;
22823
+ previousNewEnd = newStart + newLines;
22824
+ }
22825
+ return { version: 1, added, removed };
22826
+ }
22827
+ lineCount(text) {
22828
+ let count = 0;
22829
+ for (let offset = text.indexOf("\n"); offset >= 0; offset = text.indexOf("\n", offset + 1)) count++;
22830
+ for (let offset = text.indexOf("\r"); offset >= 0; offset = text.indexOf("\r", offset + 1)) {
22831
+ if (text[offset + 1] !== "\n") count++;
22832
+ }
22833
+ if (text.length > 0 && !text.endsWith("\n") && !text.endsWith("\r")) count++;
22834
+ return count;
22835
+ }
22836
+ };
22837
+
22838
+ // src/AirExtension.ts
22839
+ var JETBRAINS_META_KEY = "jetbrains";
22840
+ var AIR_META_KEY = "air";
22841
+ var AIR_EXTENSION_VERSION_KEY = "version";
22842
+ var AIR_EXTENSION_CAPABILITIES_KEY = "capabilities";
22843
+ var AIR_DIFF_STATS_KEY = "diffStats";
22844
+ var AIR_SESSION_FAILURE_KEY = "sessionFailure";
22845
+ var AIR_AGENT_FILE_CHANGE_REPORT_KEY = "agentFileChangeReport";
22846
+ var AIR_NATIVE_SUBAGENT_SESSIONS_KEY = "nativeSubagentSessions";
22847
+ var AIR_ASYNC_TASKS_KEY = "asyncTasks";
22848
+ var AIR_RECOMMENDED_CONFIG_VALUE_KEY = "recommendedValue";
22849
+ var AIR_ASYNC_TASKS_BACKGROUNDED_KEY = "backgrounded";
22850
+ var AIR_AGENT_FILE_CHANGE_REPORT_REQUEST_KEY = "agentFileChangeReportRequest";
22851
+ var AIR_EXTENSION_VERSION = 1;
22852
+ function withAirMeta(meta3, key, value) {
22853
+ const root = asRecord(meta3);
22854
+ const jetbrains = asRecord(root[JETBRAINS_META_KEY]);
22855
+ const air = asRecord(jetbrains[AIR_META_KEY]);
22856
+ return {
22857
+ ...root,
22858
+ [JETBRAINS_META_KEY]: {
22859
+ ...jetbrains,
22860
+ [AIR_META_KEY]: {
22861
+ ...air,
22862
+ [AIR_EXTENSION_VERSION_KEY]: AIR_EXTENSION_VERSION,
22863
+ [key]: value
22864
+ }
22865
+ }
22866
+ };
22867
+ }
22868
+ function clientSupportsAirCapability(capabilities, capability) {
22869
+ const meta3 = asRecord(capabilities?._meta);
22870
+ const jetbrains = asRecord(meta3[JETBRAINS_META_KEY]);
22871
+ const air = asRecord(jetbrains[AIR_META_KEY]);
22872
+ const version2 = air[AIR_EXTENSION_VERSION_KEY];
22873
+ const supported = air[AIR_EXTENSION_CAPABILITIES_KEY];
22874
+ return typeof version2 === "number" && Number.isInteger(version2) && version2 >= AIR_EXTENSION_VERSION && Array.isArray(supported) && supported.includes(capability);
22875
+ }
22876
+ function asRecord(value) {
22877
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
22878
+ }
22879
+
22775
22880
  // src/CodexToolCallMapper.ts
22776
22881
  import { readFile } from "node:fs/promises";
22777
22882
  import path2 from "node:path";
@@ -22843,6 +22948,7 @@ function commandToolName(source) {
22843
22948
 
22844
22949
  // src/CodexToolCallMapper.ts
22845
22950
  var CONTEXT_COMPACTION_META = createContextCompactionMeta();
22951
+ var DIFF_STATS = new DiffStatsCalculator();
22846
22952
  function toAcpStatus(status) {
22847
22953
  switch (status) {
22848
22954
  case "inProgress":
@@ -23507,53 +23613,43 @@ async function createAddFileContent(change) {
23507
23613
  newText: change.diff,
23508
23614
  // app-server always returns file content instead of diff
23509
23615
  path: change.path,
23510
- _meta: {
23511
- kind: "add"
23512
- }
23616
+ _meta: withAirMeta({ kind: "add" }, AIR_DIFF_STATS_KEY, DIFF_STATS.addedFile(change.diff))
23513
23617
  };
23514
23618
  }
23515
23619
  async function createUpdateFileContent(change) {
23516
23620
  if (change.kind.type !== "update") return null;
23517
23621
  const unifiedDiff = recoverCorruptedDiff(change.diff);
23622
+ const patches = parsePatch(unifiedDiff);
23623
+ if (patches.length !== 1) return null;
23624
+ const patch = patches[0];
23518
23625
  const movePath = change.kind.move_path;
23519
23626
  const oldContent = await readFileContent(change.path);
23520
23627
  if (oldContent !== null) {
23521
- const patchedContent = applyPatch(oldContent, unifiedDiff);
23628
+ const patchedContent = applyPatch(oldContent, patch);
23522
23629
  if (patchedContent === false) {
23523
- const revertedPatch2 = revertPatch(unifiedDiff);
23524
- if (revertedPatch2) {
23525
- const revertedContent2 = applyPatch(oldContent, revertedPatch2);
23526
- if (revertedContent2 !== false) {
23527
- return createUpdateDiffContent(change.path, revertedContent2, oldContent);
23528
- }
23630
+ const revertedContent2 = applyPatch(oldContent, reversePatch(patch));
23631
+ if (revertedContent2 !== false) {
23632
+ return createUpdateDiffContent(change.path, revertedContent2, oldContent, patch);
23529
23633
  }
23530
23634
  return null;
23531
23635
  }
23532
- return createUpdateDiffContent(movePath ?? change.path, oldContent, patchedContent);
23636
+ return createUpdateDiffContent(movePath ?? change.path, oldContent, patchedContent, patch);
23533
23637
  }
23534
23638
  if (!movePath) return null;
23535
23639
  const newContent = await readFileContent(movePath);
23536
23640
  if (newContent === null) return null;
23537
- const revertedPatch = revertPatch(unifiedDiff);
23538
- if (!revertedPatch) return null;
23539
- const revertedContent = applyPatch(newContent, revertedPatch);
23641
+ const revertedContent = applyPatch(newContent, reversePatch(patch));
23540
23642
  if (revertedContent === false) return null;
23541
- return createUpdateDiffContent(movePath, revertedContent, newContent);
23643
+ return createUpdateDiffContent(movePath, revertedContent, newContent, patch);
23542
23644
  }
23543
- function revertPatch(unifiedDiff) {
23544
- const [patch] = parsePatch(unifiedDiff);
23545
- if (!patch) return null;
23546
- return reversePatch(patch);
23547
- }
23548
- function createUpdateDiffContent(path9, oldText, newText) {
23645
+ function createUpdateDiffContent(path9, oldText, newText, patch) {
23646
+ const stats = DIFF_STATS.update(patch);
23549
23647
  return {
23550
23648
  type: "diff",
23551
23649
  oldText,
23552
23650
  newText,
23553
23651
  path: path9,
23554
- _meta: {
23555
- kind: "update"
23556
- }
23652
+ _meta: stats ? withAirMeta({ kind: "update" }, AIR_DIFF_STATS_KEY, stats) : { kind: "update" }
23557
23653
  };
23558
23654
  }
23559
23655
  async function createDeleteFileContent(change) {
@@ -23563,9 +23659,7 @@ async function createDeleteFileContent(change) {
23563
23659
  // app-server always returns file content instead of diff
23564
23660
  newText: "",
23565
23661
  path: change.path,
23566
- _meta: {
23567
- kind: "delete"
23568
- }
23662
+ _meta: withAirMeta({ kind: "delete" }, AIR_DIFF_STATS_KEY, DIFF_STATS.deletedFile(change.diff))
23569
23663
  };
23570
23664
  }
23571
23665
  async function readFileContent(filePath) {
@@ -23677,47 +23771,6 @@ function sameThreadGoalSnapshot(left, right) {
23677
23771
  // src/CodexEventHandler.ts
23678
23772
  import { randomUUID } from "node:crypto";
23679
23773
 
23680
- // src/AirExtension.ts
23681
- var JETBRAINS_META_KEY = "jetbrains";
23682
- var AIR_META_KEY = "air";
23683
- var AIR_EXTENSION_VERSION_KEY = "version";
23684
- var AIR_EXTENSION_CAPABILITIES_KEY = "capabilities";
23685
- var AIR_SESSION_FAILURE_KEY = "sessionFailure";
23686
- var AIR_AGENT_FILE_CHANGE_REPORT_KEY = "agentFileChangeReport";
23687
- var AIR_NATIVE_SUBAGENT_SESSIONS_KEY = "nativeSubagentSessions";
23688
- var AIR_ASYNC_TASKS_KEY = "asyncTasks";
23689
- var AIR_RECOMMENDED_CONFIG_VALUE_KEY = "recommendedValue";
23690
- var AIR_ASYNC_TASKS_BACKGROUNDED_KEY = "backgrounded";
23691
- var AIR_AGENT_FILE_CHANGE_REPORT_REQUEST_KEY = "agentFileChangeReportRequest";
23692
- var AIR_EXTENSION_VERSION = 1;
23693
- function withAirMeta(meta3, key, value) {
23694
- const root = asRecord(meta3);
23695
- const jetbrains = asRecord(root[JETBRAINS_META_KEY]);
23696
- const air = asRecord(jetbrains[AIR_META_KEY]);
23697
- return {
23698
- ...root,
23699
- [JETBRAINS_META_KEY]: {
23700
- ...jetbrains,
23701
- [AIR_META_KEY]: {
23702
- ...air,
23703
- [AIR_EXTENSION_VERSION_KEY]: AIR_EXTENSION_VERSION,
23704
- [key]: value
23705
- }
23706
- }
23707
- };
23708
- }
23709
- function clientSupportsAirCapability(capabilities, capability) {
23710
- const meta3 = asRecord(capabilities?._meta);
23711
- const jetbrains = asRecord(meta3[JETBRAINS_META_KEY]);
23712
- const air = asRecord(jetbrains[AIR_META_KEY]);
23713
- const version2 = air[AIR_EXTENSION_VERSION_KEY];
23714
- const supported = air[AIR_EXTENSION_CAPABILITIES_KEY];
23715
- return typeof version2 === "number" && Number.isInteger(version2) && version2 >= AIR_EXTENSION_VERSION && Array.isArray(supported) && supported.includes(capability);
23716
- }
23717
- function asRecord(value) {
23718
- return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
23719
- }
23720
-
23721
23774
  // src/subagents/CodexAgentPath.ts
23722
23775
  function normalizeAgentPath(path9) {
23723
23776
  const normalized = path9.trim().replace(/\/+$/, "");
@@ -24147,6 +24200,305 @@ function mergeRateLimitSnapshot(previous, update) {
24147
24200
  };
24148
24201
  }
24149
24202
 
24203
+ // src/AgentFileChangeReport.ts
24204
+ import fs2 from "node:fs";
24205
+ import path3 from "node:path";
24206
+ var AGENT_FILE_CHANGE_REPORT_VERSION = 1;
24207
+ var AGENT_FILE_CHANGE_REPORT_MAX_PATHS = 1024;
24208
+ var AGENT_FILE_CHANGE_REPORT_MAX_PATH_LENGTH = 4096;
24209
+ var AGENT_FILE_CHANGE_REPORT_MAX_TOTAL_BYTES = 256 * 1024;
24210
+ var AGENT_FILE_CHANGE_REPORT_MAX_DIFF_BYTES = 8 * 1024 * 1024;
24211
+ 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.";
24212
+ function captureAgentFileChangeWorkspace(cwd, additionalDirectories) {
24213
+ const lexicalCwd = parseWorkspaceRoot(cwd);
24214
+ return {
24215
+ cwd,
24216
+ additionalDirectories: [...additionalDirectories],
24217
+ diffRoot: lexicalCwd === null ? cwd : findDiffDisplayRoot(lexicalCwd).value
24218
+ };
24219
+ }
24220
+ function parseAgentFileChangeReportRequest(meta3) {
24221
+ const jetbrains = asRecord2(meta3?.[JETBRAINS_META_KEY]);
24222
+ const air = asRecord2(jetbrains?.[AIR_META_KEY]);
24223
+ const request = asRecord2(air?.[AIR_AGENT_FILE_CHANGE_REPORT_REQUEST_KEY]);
24224
+ if (request === null || !hasOnlyKeys(request, ["version", "requestId"]) || request["version"] !== AGENT_FILE_CHANGE_REPORT_VERSION) {
24225
+ return null;
24226
+ }
24227
+ const requestId = request["requestId"];
24228
+ if (typeof requestId !== "string" || !/^[A-Za-z0-9._:-]{1,128}$/.test(requestId)) {
24229
+ return null;
24230
+ }
24231
+ return { version: AGENT_FILE_CHANGE_REPORT_VERSION, requestId };
24232
+ }
24233
+ function createReportedAgentFileChangeReport(requestId, diff, workspace) {
24234
+ if (Buffer.byteLength(diff, "utf8") > AGENT_FILE_CHANGE_REPORT_MAX_DIFF_BYTES) {
24235
+ throw new AgentFileChangeReportError("invalidOutput", "The turn diff exceeds the parser input limit");
24236
+ }
24237
+ const parsedReport = parseTurnDiff(diff);
24238
+ const normalized = normalizeFileChangeReport(parsedReport, workspace);
24239
+ return fitReportedAgentFileChangeReport({
24240
+ version: AGENT_FILE_CHANGE_REPORT_VERSION,
24241
+ requestId,
24242
+ status: "reported",
24243
+ ...normalized
24244
+ });
24245
+ }
24246
+ function createUnavailableAgentFileChangeReport(requestId, reason) {
24247
+ return {
24248
+ version: AGENT_FILE_CHANGE_REPORT_VERSION,
24249
+ requestId,
24250
+ status: "unavailable",
24251
+ reason
24252
+ };
24253
+ }
24254
+ var AgentFileChangeReportError = class extends Error {
24255
+ reason;
24256
+ constructor(reason, message) {
24257
+ super(message);
24258
+ this.name = "AgentFileChangeReportError";
24259
+ this.reason = reason;
24260
+ }
24261
+ };
24262
+ function parseTurnDiff(diff) {
24263
+ if (diff.trim() === "") {
24264
+ return { paths: [], complete: false, uncertainty: TURN_DIFF_UNCERTAINTY };
24265
+ }
24266
+ let patches;
24267
+ try {
24268
+ patches = parsePatch(diff);
24269
+ } catch (error51) {
24270
+ throw new AgentFileChangeReportError(
24271
+ "invalidOutput",
24272
+ `The turn diff is not valid unified diff: ${error51 instanceof Error ? error51.message : String(error51)}`
24273
+ );
24274
+ }
24275
+ if (patches.length === 0) {
24276
+ throw new AgentFileChangeReportError("invalidOutput", "The turn diff contains no parseable file patches");
24277
+ }
24278
+ const rawFileHeaders = extractRawFileHeaders(diff);
24279
+ const paths = [];
24280
+ for (const [index, patch] of patches.entries()) {
24281
+ const rawHeaders = rawFileHeaders[index];
24282
+ const oldPath = normalizeDiffPath(selectLosslessFileName(rawHeaders?.oldFileName, patch.oldFileName));
24283
+ const newPath = normalizeDiffPath(selectLosslessFileName(rawHeaders?.newFileName, patch.newFileName));
24284
+ if (oldPath === null && newPath === null) {
24285
+ throw new AgentFileChangeReportError("invalidOutput", "The turn diff contains a patch without a file path");
24286
+ }
24287
+ if (oldPath !== null) paths.push(oldPath);
24288
+ if (newPath !== null) paths.push(newPath);
24289
+ }
24290
+ return { paths, complete: false, uncertainty: TURN_DIFF_UNCERTAINTY };
24291
+ }
24292
+ function extractRawFileHeaders(diff) {
24293
+ return diff.split(/(?=^diff --git )/m).filter((section) => section.trim() !== "").map((section) => {
24294
+ const lines = section.split(/\r?\n/);
24295
+ for (let index = 0; index < lines.length; index += 1) {
24296
+ const line = lines[index];
24297
+ if (line?.startsWith("@@ ")) return null;
24298
+ const nextLine = lines[index + 1];
24299
+ if (line?.startsWith("--- ") && nextLine?.startsWith("+++ ")) {
24300
+ return {
24301
+ oldFileName: line.slice(4),
24302
+ newFileName: nextLine.slice(4)
24303
+ };
24304
+ }
24305
+ }
24306
+ return null;
24307
+ });
24308
+ }
24309
+ function selectLosslessFileName(rawFileName, parsedFileName) {
24310
+ return rawFileName === void 0 || rawFileName.startsWith('"') ? parsedFileName : rawFileName;
24311
+ }
24312
+ function normalizeDiffPath(value) {
24313
+ if (value === void 0 || value === "/dev/null") return null;
24314
+ return value.startsWith("a/") || value.startsWith("b/") ? value.slice(2) : value;
24315
+ }
24316
+ function fitReportedAgentFileChangeReport(report) {
24317
+ const paths = [...report.paths];
24318
+ let truncated = report.truncated;
24319
+ let fitted = report;
24320
+ while (Buffer.byteLength(JSON.stringify(fitted), "utf8") > AGENT_FILE_CHANGE_REPORT_MAX_TOTAL_BYTES) {
24321
+ if (paths.length === 0) {
24322
+ throw new AgentFileChangeReportError("invalidOutput", "The audit report exceeds the wire limit");
24323
+ }
24324
+ paths.pop();
24325
+ truncated = true;
24326
+ fitted = {
24327
+ ...report,
24328
+ paths: [...paths],
24329
+ declaredComplete: false,
24330
+ truncated
24331
+ };
24332
+ }
24333
+ return fitted;
24334
+ }
24335
+ function normalizeFileChangeReport(report, workspace) {
24336
+ const lexicalCwd = parseWorkspaceRoot(workspace.cwd);
24337
+ if (lexicalCwd === null) {
24338
+ throw new AgentFileChangeReportError("providerError", "The session working directory is not absolute");
24339
+ }
24340
+ const cwd = canonicalizeWorkspaceRoot(lexicalCwd);
24341
+ const diffRoot = parseWorkspaceRoot(workspace.diffRoot);
24342
+ if (diffRoot === null || diffRoot.flavor !== cwd.flavor) {
24343
+ throw new AgentFileChangeReportError("providerError", "The captured turn-diff root is invalid");
24344
+ }
24345
+ const roots = [cwd, ...workspace.additionalDirectories.flatMap((directory) => {
24346
+ const root = normalizeWorkspaceRoot(directory);
24347
+ return root === null || root.flavor !== cwd.flavor ? [] : [root];
24348
+ })];
24349
+ const paths = [];
24350
+ const seen = /* @__PURE__ */ new Set();
24351
+ let totalBytes = 0;
24352
+ let truncated = false;
24353
+ for (const reportedPath of report.paths) {
24354
+ if (reportedPath.length > AGENT_FILE_CHANGE_REPORT_MAX_PATH_LENGTH) {
24355
+ truncated = true;
24356
+ continue;
24357
+ }
24358
+ const normalized = normalizeReportedPath(reportedPath, diffRoot, roots);
24359
+ if (normalized === null || normalized.value.length > AGENT_FILE_CHANGE_REPORT_MAX_PATH_LENGTH) {
24360
+ truncated = true;
24361
+ continue;
24362
+ }
24363
+ const key = normalized.flavor === "windows" ? normalized.value.toLowerCase() : normalized.value;
24364
+ if (seen.has(key)) {
24365
+ continue;
24366
+ }
24367
+ const bytes = Buffer.byteLength(normalized.value, "utf8");
24368
+ if (paths.length >= AGENT_FILE_CHANGE_REPORT_MAX_PATHS || totalBytes + bytes > AGENT_FILE_CHANGE_REPORT_MAX_TOTAL_BYTES) {
24369
+ truncated = true;
24370
+ continue;
24371
+ }
24372
+ seen.add(key);
24373
+ paths.push(normalized.value);
24374
+ totalBytes += bytes;
24375
+ }
24376
+ return {
24377
+ paths,
24378
+ declaredComplete: report.complete && !truncated,
24379
+ truncated,
24380
+ ...report.uncertainty ? { uncertainty: report.uncertainty } : {}
24381
+ };
24382
+ }
24383
+ function normalizeWorkspaceRoot(value) {
24384
+ const root = parseWorkspaceRoot(value);
24385
+ return root === null ? null : canonicalizeWorkspaceRoot(root);
24386
+ }
24387
+ function parseWorkspaceRoot(value) {
24388
+ const trimmed = value.trim();
24389
+ if (!isValidPathText(trimmed)) {
24390
+ return null;
24391
+ }
24392
+ if (isWindowsAbsolutePath(trimmed)) {
24393
+ return {
24394
+ value: path3.win32.normalize(trimmed.replace(/\//g, "\\")),
24395
+ flavor: "windows"
24396
+ };
24397
+ }
24398
+ if (path3.posix.isAbsolute(trimmed)) {
24399
+ return {
24400
+ value: path3.posix.normalize(trimmed.replace(/\\/g, "/")),
24401
+ flavor: "posix"
24402
+ };
24403
+ }
24404
+ return null;
24405
+ }
24406
+ function normalizeReportedPath(value, relativeRoot, roots) {
24407
+ 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)) {
24408
+ return null;
24409
+ }
24410
+ let candidate;
24411
+ if (isWindowsAbsolutePath(value)) {
24412
+ candidate = { value: path3.win32.normalize(value.replace(/\//g, "\\")), flavor: "windows" };
24413
+ } else if (path3.posix.isAbsolute(value)) {
24414
+ candidate = { value: path3.posix.normalize(value.replace(/\\/g, "/")), flavor: "posix" };
24415
+ } else if (relativeRoot.flavor === "windows") {
24416
+ candidate = {
24417
+ value: path3.win32.resolve(relativeRoot.value, value.replace(/\//g, "\\")),
24418
+ flavor: "windows"
24419
+ };
24420
+ } else {
24421
+ candidate = {
24422
+ value: path3.posix.resolve(relativeRoot.value, value.replace(/\\/g, "/")),
24423
+ flavor: "posix"
24424
+ };
24425
+ }
24426
+ candidate = canonicalizeReportedPath(candidate);
24427
+ return roots.some((root) => pathIsStrictlyInside(root, candidate)) ? candidate : null;
24428
+ }
24429
+ function findDiffDisplayRoot(cwd) {
24430
+ if (!isNativePathFlavor(cwd.flavor)) return cwd;
24431
+ const pathImplementation = cwd.flavor === "windows" ? path3.win32 : path3.posix;
24432
+ let current = cwd.value;
24433
+ while (true) {
24434
+ if (fs2.existsSync(pathImplementation.join(current, ".git"))) {
24435
+ return canonicalizeWorkspaceRoot({ value: current, flavor: cwd.flavor });
24436
+ }
24437
+ const parent = pathImplementation.dirname(current);
24438
+ if (parent === current) return cwd;
24439
+ current = parent;
24440
+ }
24441
+ }
24442
+ function canonicalizeWorkspaceRoot(root) {
24443
+ if (!isNativePathFlavor(root.flavor)) {
24444
+ return root;
24445
+ }
24446
+ return { ...root, value: canonicalizeFromExistingAncestor(root.value) };
24447
+ }
24448
+ function canonicalizeReportedPath(candidate) {
24449
+ if (!isNativePathFlavor(candidate.flavor)) {
24450
+ return candidate;
24451
+ }
24452
+ const parent = canonicalizeFromExistingAncestor(path3.dirname(candidate.value));
24453
+ return { ...candidate, value: path3.resolve(parent, path3.basename(candidate.value)) };
24454
+ }
24455
+ function canonicalizeFromExistingAncestor(value) {
24456
+ const original = path3.resolve(value);
24457
+ let current = original;
24458
+ const missingSegments = [];
24459
+ while (true) {
24460
+ try {
24461
+ const canonical = fs2.realpathSync.native(current);
24462
+ return path3.resolve(canonical, ...missingSegments.reverse());
24463
+ } catch (error51) {
24464
+ if (!isMissingPathError(error51)) return original;
24465
+ }
24466
+ const parent = path3.dirname(current);
24467
+ if (parent === current) return original;
24468
+ missingSegments.push(path3.basename(current));
24469
+ current = parent;
24470
+ }
24471
+ }
24472
+ function isMissingPathError(error51) {
24473
+ if (typeof error51 !== "object" || error51 === null || !("code" in error51)) return false;
24474
+ return error51.code === "ENOENT" || error51.code === "ENOTDIR";
24475
+ }
24476
+ function isNativePathFlavor(flavor) {
24477
+ return process.platform === "win32" ? flavor === "windows" : flavor === "posix";
24478
+ }
24479
+ function pathIsStrictlyInside(root, candidate) {
24480
+ if (root.flavor !== candidate.flavor) {
24481
+ return false;
24482
+ }
24483
+ const pathImplementation = root.flavor === "windows" ? path3.win32 : path3.posix;
24484
+ const relative = pathImplementation.relative(root.value, candidate.value);
24485
+ return relative.length > 0 && !pathImplementation.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${pathImplementation.sep}`);
24486
+ }
24487
+ function isWindowsAbsolutePath(value) {
24488
+ const portable = value.replace(/\\/g, "/");
24489
+ return /^[A-Za-z]:\//.test(portable) || /^\/\/[^/]+\/[^/]+(?:\/|$)/.test(portable);
24490
+ }
24491
+ function isValidPathText(value) {
24492
+ return value.length > 0 && !/[\u0000-\u001F\u007F-\u009F]/.test(value);
24493
+ }
24494
+ function asRecord2(value) {
24495
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
24496
+ }
24497
+ function hasOnlyKeys(value, allowed) {
24498
+ const allowedKeys = new Set(allowed);
24499
+ return Object.keys(value).every((key) => allowedKeys.has(key));
24500
+ }
24501
+
24150
24502
  // src/CodexEventHandler.ts
24151
24503
  var MAX_SESSION_FAILURE_TITLE_LENGTH = 240;
24152
24504
  var SESSION_FAILURE_POLICY = {
@@ -24254,6 +24606,9 @@ var CodexEventHandler = class _CodexEventHandler {
24254
24606
  terminalCommandIds = /* @__PURE__ */ new Set();
24255
24607
  terminalCommandOutputIds = /* @__PURE__ */ new Set();
24256
24608
  agentMessagePhases = /* @__PURE__ */ new Map();
24609
+ turnDiffs = /* @__PURE__ */ new Map();
24610
+ oversizedTurnDiffs = /* @__PURE__ */ new Set();
24611
+ collectTurnDiffs;
24257
24612
  subagents;
24258
24613
  /** Connection-level `authStatus` sink; the app-server account push feeds it. */
24259
24614
  onAccountUpdated;
@@ -24261,7 +24616,7 @@ var CodexEventHandler = class _CodexEventHandler {
24261
24616
  sessionState.sessionId,
24262
24617
  false,
24263
24618
  new ACPSessionConnection(connection, sessionState.sessionId)
24264
- ), onAccountUpdated) {
24619
+ ), onAccountUpdated, collectTurnDiffs = false) {
24265
24620
  this.onAccountUpdated = onAccountUpdated;
24266
24621
  this.sessionState = sessionState;
24267
24622
  this.supportsPlanUpdates = supportsPlanUpdates;
@@ -24269,6 +24624,7 @@ var CodexEventHandler = class _CodexEventHandler {
24269
24624
  this.sessionFailureEpoch = sessionFailureEpoch;
24270
24625
  this.session = new ACPSessionConnection(connection, sessionState.sessionId);
24271
24626
  this.subagents = subagents;
24627
+ this.collectTurnDiffs = collectTurnDiffs;
24272
24628
  if (sessionState.sessionFailure !== void 0) {
24273
24629
  this.failuresById.set(sessionState.sessionFailure.id, sessionState.sessionFailure);
24274
24630
  }
@@ -24276,6 +24632,12 @@ var CodexEventHandler = class _CodexEventHandler {
24276
24632
  getFailure() {
24277
24633
  return this.failure;
24278
24634
  }
24635
+ getTurnDiff(turnId) {
24636
+ return this.turnDiffs.get(turnId) ?? "";
24637
+ }
24638
+ isTurnDiffOversized(turnId) {
24639
+ return this.oversizedTurnDiffs.has(turnId);
24640
+ }
24279
24641
  getTerminalSessionFailureMeta(turnId, allowUnattributed = false) {
24280
24642
  const failure = this.sessionState.sessionFailure;
24281
24643
  if (!this.supportsTypedSessionFailures || failure === void 0 || (this.failureTurnIdById.get(failure.id) === void 0 ? !allowUnattributed : turnId === null || this.failureTurnIdById.get(failure.id) !== turnId)) {
@@ -24435,6 +24797,8 @@ var CodexEventHandler = class _CodexEventHandler {
24435
24797
  this.pendingPlanItemIds.clear();
24436
24798
  this.planDeltaTextByItemId.clear();
24437
24799
  this.lastEmittedPlanTextByItemId.clear();
24800
+ this.turnDiffs.clear();
24801
+ this.oversizedTurnDiffs.clear();
24438
24802
  }
24439
24803
  async createUpdateEvent(notification) {
24440
24804
  switch (notification.method) {
@@ -24453,6 +24817,20 @@ var CodexEventHandler = class _CodexEventHandler {
24453
24817
  case "turn/plan/updated":
24454
24818
  this.completeRetryIncidentOnTurnProgress();
24455
24819
  return await this.updatePlan(notification.params);
24820
+ case "turn/diff/updated":
24821
+ if (notification.params.threadId === this.sessionState.sessionId) {
24822
+ this.completeRetryIncidentOnTurnProgress();
24823
+ if (!this.disposed && this.collectTurnDiffs) {
24824
+ if (Buffer.byteLength(notification.params.diff, "utf8") > AGENT_FILE_CHANGE_REPORT_MAX_DIFF_BYTES) {
24825
+ this.turnDiffs.delete(notification.params.turnId);
24826
+ this.oversizedTurnDiffs.add(notification.params.turnId);
24827
+ } else {
24828
+ this.oversizedTurnDiffs.delete(notification.params.turnId);
24829
+ this.turnDiffs.set(notification.params.turnId, notification.params.diff);
24830
+ }
24831
+ }
24832
+ }
24833
+ return null;
24456
24834
  case "error":
24457
24835
  return await this.createErrorEvent(notification.params);
24458
24836
  case "turn/started":
@@ -24547,7 +24925,6 @@ var CodexEventHandler = class _CodexEventHandler {
24547
24925
  case "command/exec/outputDelta":
24548
24926
  case "hook/started":
24549
24927
  case "hook/completed":
24550
- case "turn/diff/updated":
24551
24928
  case "turn/moderationMetadata":
24552
24929
  case "item/fileChange/outputDelta":
24553
24930
  case "item/fileChange/patchUpdated":
@@ -26599,31 +26976,31 @@ function isCodexAuthRequest(request) {
26599
26976
 
26600
26977
  // node_modules/open/index.js
26601
26978
  import process11 from "node:process";
26602
- import path4 from "node:path";
26979
+ import path5 from "node:path";
26603
26980
  import { fileURLToPath } from "node:url";
26604
26981
  import childProcess4 from "node:child_process";
26605
- import fs6, { constants as fsConstants2 } from "node:fs/promises";
26982
+ import fs7, { constants as fsConstants2 } from "node:fs/promises";
26606
26983
 
26607
26984
  // node_modules/wsl-utils/index.js
26608
- import path3 from "node:path";
26985
+ import path4 from "node:path";
26609
26986
  import { promisify as promisify2 } from "node:util";
26610
26987
  import childProcess2 from "node:child_process";
26611
- import fs5, { constants as fsConstants } from "node:fs/promises";
26988
+ import fs6, { constants as fsConstants } from "node:fs/promises";
26612
26989
 
26613
26990
  // node_modules/is-wsl/index.js
26614
26991
  import process3 from "node:process";
26615
26992
  import os from "node:os";
26616
- import fs4 from "node:fs";
26993
+ import fs5 from "node:fs";
26617
26994
 
26618
26995
  // node_modules/is-inside-container/index.js
26619
- import fs3 from "node:fs";
26996
+ import fs4 from "node:fs";
26620
26997
 
26621
26998
  // node_modules/is-docker/index.js
26622
- import fs2 from "node:fs";
26999
+ import fs3 from "node:fs";
26623
27000
  var isDockerCached;
26624
27001
  function hasDockerEnv() {
26625
27002
  try {
26626
- fs2.statSync("/.dockerenv");
27003
+ fs3.statSync("/.dockerenv");
26627
27004
  return true;
26628
27005
  } catch {
26629
27006
  return false;
@@ -26631,7 +27008,7 @@ function hasDockerEnv() {
26631
27008
  }
26632
27009
  function hasDockerCGroup() {
26633
27010
  try {
26634
- return fs2.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
27011
+ return fs3.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
26635
27012
  } catch {
26636
27013
  return false;
26637
27014
  }
@@ -26647,7 +27024,7 @@ function isDocker() {
26647
27024
  var cachedResult;
26648
27025
  var hasContainerEnv = () => {
26649
27026
  try {
26650
- fs3.statSync("/run/.containerenv");
27027
+ fs4.statSync("/run/.containerenv");
26651
27028
  return true;
26652
27029
  } catch {
26653
27030
  return false;
@@ -26672,12 +27049,12 @@ var isWsl = () => {
26672
27049
  return true;
26673
27050
  }
26674
27051
  try {
26675
- if (fs4.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft")) {
27052
+ if (fs5.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft")) {
26676
27053
  return !isInsideContainer();
26677
27054
  }
26678
27055
  } catch {
26679
27056
  }
26680
- if (fs4.existsSync("/proc/sys/fs/binfmt_misc/WSLInterop") || fs4.existsSync("/run/WSL")) {
27057
+ if (fs5.existsSync("/proc/sys/fs/binfmt_misc/WSLInterop") || fs5.existsSync("/run/WSL")) {
26681
27058
  return !isInsideContainer();
26682
27059
  }
26683
27060
  return false;
@@ -26745,14 +27122,14 @@ var wslDrivesMountPoint = /* @__PURE__ */ (() => {
26745
27122
  const configFilePath = "/etc/wsl.conf";
26746
27123
  let isConfigFileExists = false;
26747
27124
  try {
26748
- await fs5.access(configFilePath, fsConstants.F_OK);
27125
+ await fs6.access(configFilePath, fsConstants.F_OK);
26749
27126
  isConfigFileExists = true;
26750
27127
  } catch {
26751
27128
  }
26752
27129
  if (!isConfigFileExists) {
26753
27130
  return defaultMountPoint;
26754
27131
  }
26755
- const configContent = await fs5.readFile(configFilePath, { encoding: "utf8" });
27132
+ const configContent = await fs6.readFile(configFilePath, { encoding: "utf8" });
26756
27133
  const parsedMountPoint = parseMountPointFromConfig(configContent);
26757
27134
  if (parsedMountPoint === void 0) {
26758
27135
  return defaultMountPoint;
@@ -26772,7 +27149,7 @@ var canAccessPowerShell = async () => {
26772
27149
  canAccessPowerShellPromise ??= (async () => {
26773
27150
  try {
26774
27151
  const psPath = await powerShellPath2();
26775
- await fs5.access(psPath, fsConstants.X_OK);
27152
+ await fs6.access(psPath, fsConstants.X_OK);
26776
27153
  return true;
26777
27154
  } catch {
26778
27155
  return false;
@@ -26785,7 +27162,7 @@ var wslDefaultBrowser = async () => {
26785
27162
  const command = String.raw`(Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice").ProgId`;
26786
27163
  const { stdout } = await executePowerShell(command, {
26787
27164
  powerShellPath: psPath,
26788
- cwd: path3.dirname(psPath)
27165
+ cwd: path4.dirname(psPath)
26789
27166
  });
26790
27167
  return stdout.trim();
26791
27168
  };
@@ -26991,8 +27368,8 @@ var is_in_ssh_default = isInSsh;
26991
27368
 
26992
27369
  // node_modules/open/index.js
26993
27370
  var fallbackAttemptSymbol = /* @__PURE__ */ Symbol("fallbackAttempt");
26994
- var __dirname = import.meta.url ? path4.dirname(fileURLToPath(import.meta.url)) : "";
26995
- var localXdgOpenPath = path4.join(__dirname, "xdg-open");
27371
+ var __dirname = import.meta.url ? path5.dirname(fileURLToPath(import.meta.url)) : "";
27372
+ var localXdgOpenPath = path5.join(__dirname, "xdg-open");
26996
27373
  var { platform, arch } = process11;
26997
27374
  var tryEachApp = async (apps2, opener) => {
26998
27375
  if (apps2.length === 0) {
@@ -27134,7 +27511,7 @@ var baseOpen = async (options) => {
27134
27511
  childProcessOptions.stdio = "ignore";
27135
27512
  }
27136
27513
  if (is_wsl_default) {
27137
- childProcessOptions.cwd = path4.dirname(command);
27514
+ childProcessOptions.cwd = path5.dirname(command);
27138
27515
  }
27139
27516
  } else {
27140
27517
  if (app) {
@@ -27143,7 +27520,7 @@ var baseOpen = async (options) => {
27143
27520
  const isBundled = !__dirname || __dirname === "/";
27144
27521
  let exeLocalXdgOpen = false;
27145
27522
  try {
27146
- await fs6.access(localXdgOpenPath, fsConstants2.X_OK);
27523
+ await fs7.access(localXdgOpenPath, fsConstants2.X_OK);
27147
27524
  exeLocalXdgOpen = true;
27148
27525
  } catch {
27149
27526
  }
@@ -27431,7 +27808,7 @@ var package_default = {
27431
27808
  publishConfig: {
27432
27809
  access: "public"
27433
27810
  },
27434
- version: "1.11.1-preview.4",
27811
+ version: "1.11.1-preview.6",
27435
27812
  description: "",
27436
27813
  main: "dist/index.js",
27437
27814
  bin: {
@@ -27483,402 +27860,106 @@ var package_default = {
27483
27860
  keywords: [],
27484
27861
  author: "Agent Client Protocol",
27485
27862
  license: "Apache-2.0",
27486
- type: "module",
27487
- devDependencies: {
27488
- "@types/node": "^26.1.0",
27489
- esbuild: "^0.28.2",
27490
- "mcp-hello-world": "^1.1.2",
27491
- tsx: "^4.23.12",
27492
- typescript: "^7.0.2",
27493
- vitest: "^4.1.11"
27494
- },
27495
- dependencies: {
27496
- "@agentclientprotocol/sdk": "^1.4.0",
27497
- "@openai/codex": "^0.154.0",
27498
- diff: "^9.0.0",
27499
- open: "^11.0.1",
27500
- "vscode-jsonrpc": "^9.0.1",
27501
- zod: "^4.0.0"
27502
- }
27503
- };
27504
-
27505
- // src/CollaborationModeConfig.ts
27506
- var COLLABORATION_MODE_CONFIG_ID = "collaboration_mode";
27507
- var DEFAULT_COLLABORATION_MODE = "default";
27508
- var PLAN_COLLABORATION_MODE = "plan";
27509
- function createCollaborationModeConfigOption(currentValue) {
27510
- return {
27511
- id: COLLABORATION_MODE_CONFIG_ID,
27512
- name: "Collaboration mode",
27513
- description: "How Codex collaborates for subsequent turns",
27514
- category: "collaboration_mode",
27515
- type: "select",
27516
- currentValue,
27517
- options: [
27518
- { value: DEFAULT_COLLABORATION_MODE, name: "Default" },
27519
- { value: PLAN_COLLABORATION_MODE, name: "Plan", description: "Plan before making changes" }
27520
- ]
27521
- };
27522
- }
27523
- function parseCollaborationMode(value) {
27524
- if (value === DEFAULT_COLLABORATION_MODE) return DEFAULT_COLLABORATION_MODE;
27525
- if (value === PLAN_COLLABORATION_MODE) return PLAN_COLLABORATION_MODE;
27526
- return null;
27527
- }
27528
- function createCodexCollaborationMode(mode, currentModelId) {
27529
- const modelId = ModelId.fromString(currentModelId);
27530
- return {
27531
- mode,
27532
- settings: {
27533
- model: modelId.model,
27534
- reasoning_effort: modelId.effort,
27535
- developer_instructions: null
27536
- }
27537
- };
27538
- }
27539
-
27540
- // src/PathUtils.ts
27541
- import path5 from "node:path";
27542
- function isAbsolutePathLike(value) {
27543
- const trimmed = value.trim();
27544
- return path5.isAbsolute(trimmed) || isWindowsAbsolutePath(trimmed);
27545
- }
27546
- function arePathsEqual(left, right) {
27547
- return normalizePathForComparison(left) === normalizePathForComparison(right);
27548
- }
27549
- function arePathBasenamesEqual(left, right) {
27550
- const leftBase = path5.posix.basename(normalizePathForComparison(left));
27551
- const rightBase = path5.posix.basename(normalizePathForComparison(right));
27552
- if (shouldComparePathCaseInsensitive(left) || shouldComparePathCaseInsensitive(right)) {
27553
- return leftBase.toLowerCase() === rightBase.toLowerCase();
27554
- }
27555
- return leftBase === rightBase;
27556
- }
27557
- function normalizePathForComparison(value) {
27558
- const trimmed = value.trim();
27559
- if (trimmed.length === 0) {
27560
- return "";
27561
- }
27562
- if (isWindowsAbsolutePath(trimmed)) {
27563
- const normalized2 = path5.win32.normalize(trimmed).replace(/\\/g, "/");
27564
- return trimTrailingPathSeparators(normalized2).toLowerCase();
27565
- }
27566
- const pathForComparison = path5.isAbsolute(trimmed) ? trimmed : trimmed.replace(/\\/g, "/");
27567
- const normalized = path5.posix.normalize(pathForComparison);
27568
- return trimTrailingPathSeparators(normalized);
27569
- }
27570
- function isWindowsAbsolutePath(value) {
27571
- const portableValue = value.replace(/\\/g, "/");
27572
- return /^[A-Za-z]:\//.test(portableValue) || /^\/\/[^/]+\/[^/]+/.test(portableValue);
27573
- }
27574
- function shouldComparePathCaseInsensitive(value) {
27575
- return isWindowsAbsolutePath(value) || /^[A-Za-z]:/.test(value) || value.includes("\\");
27576
- }
27577
- function trimTrailingPathSeparators(value) {
27578
- let trimmed = value;
27579
- while (trimmed.endsWith("/") && !isRootPath(trimmed)) {
27580
- trimmed = trimmed.slice(0, -1);
27581
- }
27582
- return trimmed;
27583
- }
27584
- function isRootPath(value) {
27585
- return value === "/" || /^[A-Za-z]:\/$/.test(value) || /^\/\/[^/]+\/[^/]+\/$/.test(value);
27586
- }
27587
-
27588
- // src/AgentFileChangeReport.ts
27589
- import fs7 from "node:fs";
27590
- import path6 from "node:path";
27591
- var AGENT_FILE_CHANGE_REPORT_VERSION = 1;
27592
- var AGENT_FILE_CHANGE_REPORT_TIMEOUT_MS = 3e4;
27593
- var AGENT_FILE_CHANGE_REPORT_MAX_PATHS = 1024;
27594
- var AGENT_FILE_CHANGE_REPORT_MAX_PATH_LENGTH = 4096;
27595
- var AGENT_FILE_CHANGE_REPORT_MAX_TOTAL_BYTES = 256 * 1024;
27596
- var AGENT_FILE_CHANGE_REPORT_MAX_UNCERTAINTY_LENGTH = 2e3;
27597
- var AGENT_FILE_CHANGE_REPORT_OUTPUT_SCHEMA = {
27598
- type: "object",
27599
- additionalProperties: false,
27600
- required: ["paths", "complete", "uncertainty"],
27601
- properties: {
27602
- paths: {
27603
- type: "array",
27604
- items: { type: "string" }
27605
- },
27606
- complete: { type: "boolean" },
27607
- uncertainty: {
27608
- anyOf: [{
27609
- type: "string",
27610
- maxLength: AGENT_FILE_CHANGE_REPORT_MAX_UNCERTAINTY_LENGTH
27611
- }, {
27612
- type: "null"
27613
- }]
27614
- }
27615
- }
27616
- };
27617
- var AGENT_FILE_CHANGE_REPORT_DEVELOPER_INSTRUCTIONS = `You are running an internal, read-only file-change audit for the immediately preceding turn.
27618
- Do not modify files, run commands that can modify files, or ask the user questions.
27619
- Report files that the preceding turn causally created, modified, deleted, or moved, including changes made by shell commands, version-control commands, generators, and child processes.
27620
- Do not report files that were only read or inspected.
27621
- You may use read-only inspection when needed. If the list may be incomplete, set complete to false and briefly explain why in uncertainty.`;
27622
- function createAgentFileChangeReportPrompt(workspace) {
27623
- return `List the paths changed by the immediately preceding turn.
27624
- Return only the structured result required by the output schema.
27625
- Relative paths are resolved against the working directory.
27626
- Working directory: ${JSON.stringify(workspace.cwd)}
27627
- Additional allowed directories: ${JSON.stringify(workspace.additionalDirectories)}`;
27628
- }
27629
- function parseAgentFileChangeReportRequest(meta3) {
27630
- const jetbrains = asRecord2(meta3?.[JETBRAINS_META_KEY]);
27631
- const air = asRecord2(jetbrains?.[AIR_META_KEY]);
27632
- const request = asRecord2(air?.[AIR_AGENT_FILE_CHANGE_REPORT_REQUEST_KEY]);
27633
- if (request === null || !hasOnlyKeys(request, ["version", "requestId"]) || request["version"] !== AGENT_FILE_CHANGE_REPORT_VERSION) {
27634
- return null;
27635
- }
27636
- const requestId = request["requestId"];
27637
- if (typeof requestId !== "string" || !/^[A-Za-z0-9._:-]{1,128}$/.test(requestId)) {
27638
- return null;
27639
- }
27640
- return { version: AGENT_FILE_CHANGE_REPORT_VERSION, requestId };
27641
- }
27642
- function createReportedAgentFileChangeReport(requestId, turn, workspace) {
27643
- const modelReport = parseModelFileChangeReport(turn);
27644
- const normalized = normalizeModelFileChangeReport(modelReport, workspace);
27645
- return fitReportedAgentFileChangeReport({
27646
- version: AGENT_FILE_CHANGE_REPORT_VERSION,
27647
- requestId,
27648
- status: "reported",
27649
- ...normalized
27650
- });
27651
- }
27652
- function createUnavailableAgentFileChangeReport(requestId, reason) {
27653
- return {
27654
- version: AGENT_FILE_CHANGE_REPORT_VERSION,
27655
- requestId,
27656
- status: "unavailable",
27657
- reason
27658
- };
27659
- }
27660
- var AgentFileChangeReportError = class extends Error {
27661
- reason;
27662
- constructor(reason, message) {
27663
- super(message);
27664
- this.name = "AgentFileChangeReportError";
27665
- this.reason = reason;
27666
- }
27667
- };
27668
- function parseModelFileChangeReport(turn) {
27669
- switch (turn.status) {
27670
- case "interrupted":
27671
- throw new AgentFileChangeReportError("cancelled", "The audit turn was interrupted");
27672
- case "failed":
27673
- throw new AgentFileChangeReportError(
27674
- "providerError",
27675
- `The audit turn failed${turn.error?.message ? `: ${turn.error.message}` : ""}`
27676
- );
27677
- case "inProgress":
27678
- throw new AgentFileChangeReportError("notReported", "The audit turn did not complete");
27679
- case "completed":
27680
- break;
27681
- }
27682
- let text = null;
27683
- for (let index = turn.items.length - 1; index >= 0; index -= 1) {
27684
- const item = turn.items[index];
27685
- if (item?.type === "agentMessage") {
27686
- text = item.text;
27687
- break;
27688
- }
27689
- }
27690
- if (text === null) {
27691
- throw new AgentFileChangeReportError("notReported", "The audit turn returned no agent message");
27692
- }
27693
- let value;
27694
- try {
27695
- value = JSON.parse(text);
27696
- } catch {
27697
- throw new AgentFileChangeReportError("invalidOutput", "The audit turn returned invalid JSON");
27698
- }
27699
- const report = asRecord2(value);
27700
- if (report === null || !hasOnlyKeys(report, ["paths", "complete", "uncertainty"])) {
27701
- throw new AgentFileChangeReportError("invalidOutput", "The audit turn returned an invalid object");
27702
- }
27703
- const paths = report["paths"];
27704
- const complete = report["complete"];
27705
- const uncertainty = report["uncertainty"];
27706
- if (!Array.isArray(paths) || !paths.every((item) => typeof item === "string") || typeof complete !== "boolean" || uncertainty !== void 0 && uncertainty !== null && typeof uncertainty !== "string") {
27707
- throw new AgentFileChangeReportError("invalidOutput", "The audit turn returned invalid fields");
27708
- }
27709
- const normalizedUncertainty = typeof uncertainty === "string" ? uncertainty.trim() : void 0;
27710
- if (normalizedUncertainty !== void 0 && normalizedUncertainty.length > AGENT_FILE_CHANGE_REPORT_MAX_UNCERTAINTY_LENGTH) {
27711
- throw new AgentFileChangeReportError("invalidOutput", "The audit turn returned oversized uncertainty");
27863
+ type: "module",
27864
+ devDependencies: {
27865
+ "@types/node": "^26.1.0",
27866
+ esbuild: "^0.28.2",
27867
+ "mcp-hello-world": "^1.1.2",
27868
+ tsx: "^4.23.12",
27869
+ typescript: "^7.0.2",
27870
+ vitest: "^4.1.11"
27871
+ },
27872
+ dependencies: {
27873
+ "@agentclientprotocol/sdk": "^1.4.0",
27874
+ "@openai/codex": "^0.154.0",
27875
+ diff: "^9.0.0",
27876
+ open: "^11.0.1",
27877
+ "vscode-jsonrpc": "^9.0.1",
27878
+ zod: "^4.0.0"
27712
27879
  }
27880
+ };
27881
+
27882
+ // src/CollaborationModeConfig.ts
27883
+ var COLLABORATION_MODE_CONFIG_ID = "collaboration_mode";
27884
+ var DEFAULT_COLLABORATION_MODE = "default";
27885
+ var PLAN_COLLABORATION_MODE = "plan";
27886
+ function createCollaborationModeConfigOption(currentValue) {
27713
27887
  return {
27714
- paths,
27715
- complete,
27716
- ...normalizedUncertainty ? { uncertainty: normalizedUncertainty } : {}
27888
+ id: COLLABORATION_MODE_CONFIG_ID,
27889
+ name: "Collaboration mode",
27890
+ description: "How Codex collaborates for subsequent turns",
27891
+ category: "collaboration_mode",
27892
+ type: "select",
27893
+ currentValue,
27894
+ options: [
27895
+ { value: DEFAULT_COLLABORATION_MODE, name: "Default" },
27896
+ { value: PLAN_COLLABORATION_MODE, name: "Plan", description: "Plan before making changes" }
27897
+ ]
27717
27898
  };
27718
27899
  }
27719
- function fitReportedAgentFileChangeReport(report) {
27720
- const paths = [...report.paths];
27721
- let truncated = report.truncated;
27722
- let fitted = report;
27723
- while (Buffer.byteLength(JSON.stringify(fitted), "utf8") > AGENT_FILE_CHANGE_REPORT_MAX_TOTAL_BYTES) {
27724
- if (paths.length === 0) {
27725
- throw new AgentFileChangeReportError("invalidOutput", "The audit report exceeds the wire limit");
27726
- }
27727
- paths.pop();
27728
- truncated = true;
27729
- fitted = {
27730
- ...report,
27731
- paths: [...paths],
27732
- declaredComplete: false,
27733
- truncated
27734
- };
27735
- }
27736
- return fitted;
27900
+ function parseCollaborationMode(value) {
27901
+ if (value === DEFAULT_COLLABORATION_MODE) return DEFAULT_COLLABORATION_MODE;
27902
+ if (value === PLAN_COLLABORATION_MODE) return PLAN_COLLABORATION_MODE;
27903
+ return null;
27737
27904
  }
27738
- function normalizeModelFileChangeReport(report, workspace) {
27739
- const cwd = normalizeWorkspaceRoot(workspace.cwd);
27740
- if (cwd === null) {
27741
- throw new AgentFileChangeReportError("providerError", "The session working directory is not absolute");
27742
- }
27743
- const roots = [cwd, ...workspace.additionalDirectories.flatMap((directory) => {
27744
- const root = normalizeWorkspaceRoot(directory);
27745
- return root === null || root.flavor !== cwd.flavor ? [] : [root];
27746
- })];
27747
- const paths = [];
27748
- const seen = /* @__PURE__ */ new Set();
27749
- let totalBytes = 0;
27750
- let truncated = false;
27751
- for (const reportedPath of report.paths) {
27752
- if (reportedPath.length > AGENT_FILE_CHANGE_REPORT_MAX_PATH_LENGTH) {
27753
- truncated = true;
27754
- continue;
27755
- }
27756
- const normalized = normalizeReportedPath(reportedPath, cwd, roots);
27757
- if (normalized === null || normalized.value.length > AGENT_FILE_CHANGE_REPORT_MAX_PATH_LENGTH) {
27758
- truncated = true;
27759
- continue;
27760
- }
27761
- const key = normalized.flavor === "windows" ? normalized.value.toLowerCase() : normalized.value;
27762
- if (seen.has(key)) {
27763
- continue;
27764
- }
27765
- const bytes = Buffer.byteLength(normalized.value, "utf8");
27766
- if (paths.length >= AGENT_FILE_CHANGE_REPORT_MAX_PATHS || totalBytes + bytes > AGENT_FILE_CHANGE_REPORT_MAX_TOTAL_BYTES) {
27767
- truncated = true;
27768
- continue;
27769
- }
27770
- seen.add(key);
27771
- paths.push(normalized.value);
27772
- totalBytes += bytes;
27773
- }
27905
+ function createCodexCollaborationMode(mode, currentModelId) {
27906
+ const modelId = ModelId.fromString(currentModelId);
27774
27907
  return {
27775
- paths,
27776
- declaredComplete: report.complete && !truncated,
27777
- truncated,
27778
- ...report.uncertainty ? { uncertainty: report.uncertainty } : {}
27908
+ mode,
27909
+ settings: {
27910
+ model: modelId.model,
27911
+ reasoning_effort: modelId.effort,
27912
+ developer_instructions: null
27913
+ }
27779
27914
  };
27780
27915
  }
27781
- function normalizeWorkspaceRoot(value) {
27782
- const trimmed = value.trim();
27783
- if (!isValidPathText(trimmed)) {
27784
- return null;
27785
- }
27786
- if (isWindowsAbsolutePath2(trimmed)) {
27787
- return canonicalizeWorkspaceRoot({
27788
- value: path6.win32.normalize(trimmed.replace(/\//g, "\\")),
27789
- flavor: "windows"
27790
- });
27791
- }
27792
- if (path6.posix.isAbsolute(trimmed)) {
27793
- return canonicalizeWorkspaceRoot({
27794
- value: path6.posix.normalize(trimmed.replace(/\\/g, "/")),
27795
- flavor: "posix"
27796
- });
27797
- }
27798
- return null;
27799
- }
27800
- function normalizeReportedPath(value, cwd, roots) {
27916
+
27917
+ // src/PathUtils.ts
27918
+ import path6 from "node:path";
27919
+ function isAbsolutePathLike(value) {
27801
27920
  const trimmed = value.trim();
27802
- if (!isValidPathText(trimmed) || /^[A-Za-z]:[^\\/]/.test(trimmed) || /^\\\\[?.]\\/.test(trimmed) || /^(?:\\\\|\/\/)/.test(trimmed) && !isWindowsAbsolutePath2(trimmed) || cwd.flavor === "windows" && /^\\(?!\\)/.test(trimmed) || /^[A-Za-z][A-Za-z0-9+.-]*:\/\//.test(trimmed)) {
27803
- return null;
27804
- }
27805
- let candidate;
27806
- if (isWindowsAbsolutePath2(trimmed)) {
27807
- candidate = { value: path6.win32.normalize(trimmed.replace(/\//g, "\\")), flavor: "windows" };
27808
- } else if (path6.posix.isAbsolute(trimmed)) {
27809
- candidate = { value: path6.posix.normalize(trimmed.replace(/\\/g, "/")), flavor: "posix" };
27810
- } else if (cwd.flavor === "windows") {
27811
- candidate = {
27812
- value: path6.win32.resolve(cwd.value, trimmed.replace(/\//g, "\\")),
27813
- flavor: "windows"
27814
- };
27815
- } else {
27816
- candidate = {
27817
- value: path6.posix.resolve(cwd.value, trimmed.replace(/\\/g, "/")),
27818
- flavor: "posix"
27819
- };
27820
- }
27821
- candidate = canonicalizeReportedPath(candidate);
27822
- return roots.some((root) => pathIsStrictlyInside(root, candidate)) ? candidate : null;
27921
+ return path6.isAbsolute(trimmed) || isWindowsAbsolutePath2(trimmed);
27823
27922
  }
27824
- function canonicalizeWorkspaceRoot(root) {
27825
- if (!isNativePathFlavor(root.flavor)) {
27826
- return root;
27827
- }
27828
- return { ...root, value: canonicalizeFromExistingAncestor(root.value) };
27923
+ function arePathsEqual(left, right) {
27924
+ return normalizePathForComparison(left) === normalizePathForComparison(right);
27829
27925
  }
27830
- function canonicalizeReportedPath(candidate) {
27831
- if (!isNativePathFlavor(candidate.flavor)) {
27832
- return candidate;
27926
+ function arePathBasenamesEqual(left, right) {
27927
+ const leftBase = path6.posix.basename(normalizePathForComparison(left));
27928
+ const rightBase = path6.posix.basename(normalizePathForComparison(right));
27929
+ if (shouldComparePathCaseInsensitive(left) || shouldComparePathCaseInsensitive(right)) {
27930
+ return leftBase.toLowerCase() === rightBase.toLowerCase();
27833
27931
  }
27834
- const parent = canonicalizeFromExistingAncestor(path6.dirname(candidate.value));
27835
- return { ...candidate, value: path6.resolve(parent, path6.basename(candidate.value)) };
27932
+ return leftBase === rightBase;
27836
27933
  }
27837
- function canonicalizeFromExistingAncestor(value) {
27838
- const original = path6.resolve(value);
27839
- let current = original;
27840
- const missingSegments = [];
27841
- while (true) {
27842
- try {
27843
- const canonical = fs7.realpathSync.native(current);
27844
- return path6.resolve(canonical, ...missingSegments.reverse());
27845
- } catch (error51) {
27846
- if (!isMissingPathError(error51)) return original;
27847
- }
27848
- const parent = path6.dirname(current);
27849
- if (parent === current) return original;
27850
- missingSegments.push(path6.basename(current));
27851
- current = parent;
27934
+ function normalizePathForComparison(value) {
27935
+ const trimmed = value.trim();
27936
+ if (trimmed.length === 0) {
27937
+ return "";
27852
27938
  }
27853
- }
27854
- function isMissingPathError(error51) {
27855
- if (typeof error51 !== "object" || error51 === null || !("code" in error51)) return false;
27856
- return error51.code === "ENOENT" || error51.code === "ENOTDIR";
27857
- }
27858
- function isNativePathFlavor(flavor) {
27859
- return process.platform === "win32" ? flavor === "windows" : flavor === "posix";
27860
- }
27861
- function pathIsStrictlyInside(root, candidate) {
27862
- if (root.flavor !== candidate.flavor) {
27863
- return false;
27939
+ if (isWindowsAbsolutePath2(trimmed)) {
27940
+ const normalized2 = path6.win32.normalize(trimmed).replace(/\\/g, "/");
27941
+ return trimTrailingPathSeparators(normalized2).toLowerCase();
27864
27942
  }
27865
- const pathImplementation = root.flavor === "windows" ? path6.win32 : path6.posix;
27866
- const relative = pathImplementation.relative(root.value, candidate.value);
27867
- return relative.length > 0 && !pathImplementation.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${pathImplementation.sep}`);
27943
+ const pathForComparison = path6.isAbsolute(trimmed) ? trimmed : trimmed.replace(/\\/g, "/");
27944
+ const normalized = path6.posix.normalize(pathForComparison);
27945
+ return trimTrailingPathSeparators(normalized);
27868
27946
  }
27869
27947
  function isWindowsAbsolutePath2(value) {
27870
- const portable = value.replace(/\\/g, "/");
27871
- return /^[A-Za-z]:\//.test(portable) || /^\/\/[^/]+\/[^/]+(?:\/|$)/.test(portable);
27948
+ const portableValue = value.replace(/\\/g, "/");
27949
+ return /^[A-Za-z]:\//.test(portableValue) || /^\/\/[^/]+\/[^/]+/.test(portableValue);
27872
27950
  }
27873
- function isValidPathText(value) {
27874
- return value.length > 0 && !/[\u0000-\u001F\u007F-\u009F]/.test(value);
27951
+ function shouldComparePathCaseInsensitive(value) {
27952
+ return isWindowsAbsolutePath2(value) || /^[A-Za-z]:/.test(value) || value.includes("\\");
27875
27953
  }
27876
- function asRecord2(value) {
27877
- return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
27954
+ function trimTrailingPathSeparators(value) {
27955
+ let trimmed = value;
27956
+ while (trimmed.endsWith("/") && !isRootPath(trimmed)) {
27957
+ trimmed = trimmed.slice(0, -1);
27958
+ }
27959
+ return trimmed;
27878
27960
  }
27879
- function hasOnlyKeys(value, allowed) {
27880
- const allowedKeys = new Set(allowed);
27881
- return Object.keys(value).every((key) => allowedKeys.has(key));
27961
+ function isRootPath(value) {
27962
+ return value === "/" || /^[A-Za-z]:\/$/.test(value) || /^\/\/[^/]+\/[^/]+\/$/.test(value);
27882
27963
  }
27883
27964
 
27884
27965
  // src/subagents/CodexSubagentSubscriptions.ts
@@ -28620,7 +28701,7 @@ var CodexAcpClient = class {
28620
28701
  baseUrl: activeProvider.baseUrl
28621
28702
  });
28622
28703
  const mergedConfig = {
28623
- ...mergeGatewayConfig(this.config, this.gatewayConfig),
28704
+ ...forceGitRootTurnDiffPaths(mergeGatewayConfig(this.config, this.gatewayConfig)),
28624
28705
  projects: Object.fromEntries(sessionRoots.map((root) => [root, {
28625
28706
  trust_level: "trusted"
28626
28707
  }]))
@@ -28780,101 +28861,6 @@ var CodexAcpClient = class {
28780
28861
  serviceTier
28781
28862
  }, onTurnStarted);
28782
28863
  }
28783
- async runAgentFileChangeReport(params) {
28784
- if (params.signal?.aborted) {
28785
- return createUnavailableAgentFileChangeReport(params.requestId, "cancelled");
28786
- }
28787
- const budget = new AgentFileChangeReportBudget(params.signal);
28788
- let forkThreadId = null;
28789
- let auditTurnId = null;
28790
- let auditTurnCompleted = false;
28791
- let lateStopReason = null;
28792
- try {
28793
- const forkPromise = this.codexClient.threadFork({
28794
- excludeTurns: true,
28795
- threadId: params.sessionId,
28796
- lastTurnId: params.turnId,
28797
- cwd: params.workspace.cwd,
28798
- approvalPolicy: "never",
28799
- sandbox: "read-only",
28800
- developerInstructions: AGENT_FILE_CHANGE_REPORT_DEVELOPER_INSTRUCTIONS,
28801
- ephemeral: true
28802
- });
28803
- void forkPromise.then((fork2) => {
28804
- if (lateStopReason !== null && forkThreadId === null) {
28805
- void this.unsubscribeAgentFileChangeReportThread(fork2.thread.id, budget);
28806
- }
28807
- }, () => {
28808
- });
28809
- const fork = await budget.wait(forkPromise);
28810
- forkThreadId = fork.thread.id;
28811
- const turnPromise = this.codexClient.runTurn({
28812
- threadId: forkThreadId,
28813
- input: [{
28814
- type: "text",
28815
- text: createAgentFileChangeReportPrompt(params.workspace),
28816
- text_elements: []
28817
- }],
28818
- cwd: params.workspace.cwd,
28819
- approvalPolicy: "never",
28820
- sandboxPolicy: { type: "readOnly", networkAccess: false },
28821
- summary: "none",
28822
- outputSchema: AGENT_FILE_CHANGE_REPORT_OUTPUT_SCHEMA
28823
- }, (turnId) => {
28824
- auditTurnId = turnId;
28825
- if (lateStopReason !== null && forkThreadId !== null) {
28826
- void this.interruptAgentFileChangeReport(forkThreadId, turnId, lateStopReason, budget);
28827
- }
28828
- });
28829
- const outcome = await budget.wait(turnPromise);
28830
- auditTurnCompleted = true;
28831
- return createReportedAgentFileChangeReport(
28832
- params.requestId,
28833
- outcome.turn,
28834
- params.workspace
28835
- );
28836
- } catch (error51) {
28837
- if (error51 instanceof AgentFileChangeReportBudgetError) {
28838
- lateStopReason = error51.reason;
28839
- if (!auditTurnCompleted && forkThreadId !== null && auditTurnId !== null) {
28840
- await this.interruptAgentFileChangeReport(
28841
- forkThreadId,
28842
- auditTurnId,
28843
- error51.reason,
28844
- budget
28845
- );
28846
- }
28847
- return createUnavailableAgentFileChangeReport(params.requestId, error51.reason);
28848
- }
28849
- if (error51 instanceof AgentFileChangeReportError) {
28850
- logger.error("Agent file-change report unavailable", error51);
28851
- return createUnavailableAgentFileChangeReport(params.requestId, error51.reason);
28852
- }
28853
- logger.error("Agent file-change report failed", error51);
28854
- return createUnavailableAgentFileChangeReport(params.requestId, "providerError");
28855
- } finally {
28856
- if (forkThreadId !== null) {
28857
- await this.unsubscribeAgentFileChangeReportThread(forkThreadId, budget);
28858
- }
28859
- }
28860
- }
28861
- async interruptAgentFileChangeReport(threadId, turnId, reason, budget) {
28862
- this.codexClient.markTurnStale(threadId, turnId);
28863
- try {
28864
- await budget.wait(this.codexClient.turnInterrupt({ threadId, turnId }));
28865
- } catch (error51) {
28866
- logger.error(`Failed to interrupt ${reason} agent file-change report`, error51);
28867
- } finally {
28868
- this.codexClient.resolveTurnInterrupted(threadId, turnId);
28869
- }
28870
- }
28871
- async unsubscribeAgentFileChangeReportThread(threadId, budget) {
28872
- try {
28873
- await budget.wait(this.codexClient.threadUnsubscribe({ threadId }));
28874
- } catch (error51) {
28875
- logger.error("Failed to unsubscribe the agent file-change report thread", error51);
28876
- }
28877
- }
28878
28864
  async setCollaborationMode(sessionId, mode, currentModelId) {
28879
28865
  await this.codexClient.threadSettingsUpdate({
28880
28866
  threadId: sessionId,
@@ -29031,58 +29017,6 @@ var CodexAcpClient = class {
29031
29017
  };
29032
29018
  }
29033
29019
  };
29034
- var AgentFileChangeReportBudgetError = class extends Error {
29035
- constructor(reason) {
29036
- super(`Agent file-change report ${reason}`);
29037
- this.reason = reason;
29038
- this.name = "AgentFileChangeReportBudgetError";
29039
- }
29040
- reason;
29041
- };
29042
- var AgentFileChangeReportBudget = class {
29043
- constructor(signal) {
29044
- this.signal = signal;
29045
- }
29046
- signal;
29047
- deadline = Date.now() + AGENT_FILE_CHANGE_REPORT_TIMEOUT_MS;
29048
- async wait(operation) {
29049
- void operation.catch(() => {
29050
- });
29051
- const immediateReason = this.stopReason();
29052
- if (immediateReason !== null) {
29053
- throw new AgentFileChangeReportBudgetError(immediateReason);
29054
- }
29055
- return await new Promise((resolve, reject) => {
29056
- let settled = false;
29057
- const finish = (action) => {
29058
- if (settled) return;
29059
- settled = true;
29060
- clearTimeout(timeout);
29061
- this.signal?.removeEventListener("abort", onAbort);
29062
- action();
29063
- };
29064
- const onAbort = () => finish(() => reject(new AgentFileChangeReportBudgetError("cancelled")));
29065
- const timeout = setTimeout(
29066
- () => finish(() => reject(new AgentFileChangeReportBudgetError("timeout"))),
29067
- Math.max(1, this.deadline - Date.now())
29068
- );
29069
- timeout.unref();
29070
- this.signal?.addEventListener("abort", onAbort, { once: true });
29071
- if (this.signal?.aborted) {
29072
- onAbort();
29073
- }
29074
- void operation.then(
29075
- (value) => finish(() => resolve(value)),
29076
- (error51) => finish(() => reject(error51))
29077
- );
29078
- });
29079
- }
29080
- stopReason() {
29081
- if (this.signal?.aborted) return "cancelled";
29082
- if (Date.now() >= this.deadline) return "timeout";
29083
- return null;
29084
- }
29085
- };
29086
29020
  function buildPromptItems(prompt) {
29087
29021
  return prompt.map((block) => {
29088
29022
  switch (block.type) {
@@ -29197,6 +29131,16 @@ function mergeSandboxWorkspaceWriteRoots(config2, roots) {
29197
29131
  }
29198
29132
  };
29199
29133
  }
29134
+ function forceGitRootTurnDiffPaths(config2) {
29135
+ const features = isJsonObject(config2["features"]) ? config2["features"] : {};
29136
+ return {
29137
+ ...config2,
29138
+ features: {
29139
+ ...features,
29140
+ cwd_relative_turn_diffs: false
29141
+ }
29142
+ };
29143
+ }
29200
29144
  function addAdditionalDirectoriesToSandboxPolicy(sandboxPolicy, additionalDirectories) {
29201
29145
  if (additionalDirectories.length === 0 || sandboxPolicy.type !== "workspaceWrite") {
29202
29146
  return sandboxPolicy;
@@ -33951,24 +33895,19 @@ Check ${configPath} and project .codex directories, especially their config.toml
33951
33895
  title
33952
33896
  });
33953
33897
  }
33954
- async publishAgentFileChangeReport(sessionState, turnId, request, unavailableReason, signal) {
33898
+ async publishAgentFileChangeReport(sessionState, turnId, request, unavailableReason, turnDiff, workspace) {
33955
33899
  let report;
33956
33900
  try {
33957
- report = turnId === null ? createUnavailableAgentFileChangeReport(request.requestId, unavailableReason) : await this.codexAcpClient.runAgentFileChangeReport({
33958
- sessionId: sessionState.sessionId,
33959
- turnId,
33960
- // The client owns request-id correlation and duplicate suppression. The wrapper
33961
- // stays stateless so a retried ACP prompt still receives a terminal report.
33962
- requestId: request.requestId,
33963
- workspace: {
33964
- cwd: sessionState.cwd,
33965
- additionalDirectories: sessionState.additionalDirectories
33966
- },
33967
- signal
33968
- });
33901
+ report = turnId === null ? createUnavailableAgentFileChangeReport(request.requestId, unavailableReason) : createReportedAgentFileChangeReport(request.requestId, turnDiff, workspace);
33969
33902
  } catch (error51) {
33970
- logger.error("Agent file-change report failed unexpectedly", error51);
33971
- report = createUnavailableAgentFileChangeReport(request.requestId, "providerError");
33903
+ logger.error(
33904
+ error51 instanceof AgentFileChangeReportError ? "Agent file-change report unavailable" : "Agent file-change report failed unexpectedly",
33905
+ error51
33906
+ );
33907
+ report = createUnavailableAgentFileChangeReport(
33908
+ request.requestId,
33909
+ error51 instanceof AgentFileChangeReportError ? error51.reason : "providerError"
33910
+ );
33972
33911
  }
33973
33912
  try {
33974
33913
  const session = new ACPSessionConnection(this.connection, sessionState.sessionId);
@@ -34438,6 +34377,7 @@ Check ${configPath} and project .codex directories, especially their config.toml
34438
34377
  });
34439
34378
  const sessionState = this.getSessionState(params.sessionId);
34440
34379
  const agentFileChangeReportRequest = clientSupportsAgentFileChangeReports(this.clientCapabilities) ? parseAgentFileChangeReportRequest(params._meta) : null;
34380
+ const agentFileChangeWorkspace = agentFileChangeReportRequest === null ? null : captureAgentFileChangeWorkspace(sessionState.cwd, sessionState.additionalDirectories);
34441
34381
  let agentFileChangeReportTurnId = null;
34442
34382
  let agentFileChangeReportUnavailableReason = "providerError";
34443
34383
  let promptWasCancelled = false;
@@ -34476,7 +34416,8 @@ Check ${configPath} and project .codex directories, especially their config.toml
34476
34416
  clientSupportsTypedSessionFailures(this.clientCapabilities),
34477
34417
  this.sessionFailureEpoch,
34478
34418
  sessionState.subagents,
34479
- (accountUpdated) => this.handleAccountUpdated(accountUpdated)
34419
+ (accountUpdated) => this.handleAccountUpdated(accountUpdated),
34420
+ agentFileChangeReportRequest !== null
34480
34421
  );
34481
34422
  eventHandler = promptEventHandler;
34482
34423
  const permissionLifecycle = this.permissionLifecycleContext(sessionState);
@@ -34828,13 +34769,21 @@ Check ${configPath} and project .codex directories, especially their config.toml
34828
34769
  } catch (error51) {
34829
34770
  logger.error("Failed to publish terminal subagent state during prompt cleanup", error51);
34830
34771
  }
34831
- if (agentFileChangeReportRequest !== null) {
34772
+ if (agentFileChangeReportRequest !== null && agentFileChangeWorkspace !== null) {
34773
+ if (promptWasCancelled || activePrompt.signal.aborted || this.sessionIsClosing(params.sessionId)) {
34774
+ agentFileChangeReportTurnId = null;
34775
+ agentFileChangeReportUnavailableReason = "cancelled";
34776
+ } else if (agentFileChangeReportTurnId !== null && eventHandler?.isTurnDiffOversized(agentFileChangeReportTurnId)) {
34777
+ agentFileChangeReportTurnId = null;
34778
+ agentFileChangeReportUnavailableReason = "invalidOutput";
34779
+ }
34832
34780
  await this.publishAgentFileChangeReport(
34833
34781
  sessionState,
34834
34782
  agentFileChangeReportTurnId,
34835
34783
  agentFileChangeReportRequest,
34836
34784
  agentFileChangeReportUnavailableReason,
34837
- activePrompt.signal
34785
+ agentFileChangeReportTurnId === null || eventHandler === null ? "" : eventHandler.getTurnDiff(agentFileChangeReportTurnId),
34786
+ agentFileChangeWorkspace
34838
34787
  );
34839
34788
  }
34840
34789
  logger.log("Prompt completed", { sessionId: params.sessionId });