@agentconnect.md/codex-acp 1.11.0-agentconnect.1 → 1.12.0-agentconnect.1

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 +721 -696
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -22782,6 +22782,111 @@ function reversePatch(structuredPatch) {
22782
22782
  return reversed;
22783
22783
  }
22784
22784
 
22785
+ // src/DiffStats.ts
22786
+ var DiffStatsCalculator = class {
22787
+ addedFile(text) {
22788
+ return { version: 1, added: this.lineCount(text), removed: 0 };
22789
+ }
22790
+ deletedFile(text) {
22791
+ return { version: 1, added: 0, removed: this.lineCount(text) };
22792
+ }
22793
+ update(patch) {
22794
+ if (patch.isBinary || patch.hunks.length === 0) return null;
22795
+ let added = 0;
22796
+ let removed = 0;
22797
+ let previousOldEnd = 1;
22798
+ let previousNewEnd = 1;
22799
+ for (const hunk of patch.hunks) {
22800
+ const { oldStart, oldLines, newStart, newLines } = hunk;
22801
+ if (![oldStart, oldLines, newStart, newLines].every(Number.isSafeInteger) || oldStart < previousOldEnd || newStart < previousNewEnd || oldLines < 0 || newLines < 0 || newStart - oldStart !== added - removed) return null;
22802
+ let oldConsumed = 0;
22803
+ let newConsumed = 0;
22804
+ let previousWasContent = false;
22805
+ for (const line of hunk.lines) {
22806
+ switch (line[0]) {
22807
+ case "+":
22808
+ added++;
22809
+ newConsumed++;
22810
+ previousWasContent = true;
22811
+ break;
22812
+ case "-":
22813
+ removed++;
22814
+ oldConsumed++;
22815
+ previousWasContent = true;
22816
+ break;
22817
+ case " ":
22818
+ case void 0:
22819
+ oldConsumed++;
22820
+ newConsumed++;
22821
+ previousWasContent = true;
22822
+ break;
22823
+ case "\\":
22824
+ if (!previousWasContent || line.replace(/\r$/, "") !== "\") return null;
22825
+ previousWasContent = false;
22826
+ break;
22827
+ default:
22828
+ return null;
22829
+ }
22830
+ }
22831
+ if (oldConsumed !== oldLines || newConsumed !== newLines) return null;
22832
+ previousOldEnd = oldStart + oldLines;
22833
+ previousNewEnd = newStart + newLines;
22834
+ }
22835
+ return { version: 1, added, removed };
22836
+ }
22837
+ lineCount(text) {
22838
+ let count = 0;
22839
+ for (let offset = text.indexOf("\n"); offset >= 0; offset = text.indexOf("\n", offset + 1)) count++;
22840
+ for (let offset = text.indexOf("\r"); offset >= 0; offset = text.indexOf("\r", offset + 1)) {
22841
+ if (text[offset + 1] !== "\n") count++;
22842
+ }
22843
+ if (text.length > 0 && !text.endsWith("\n") && !text.endsWith("\r")) count++;
22844
+ return count;
22845
+ }
22846
+ };
22847
+
22848
+ // src/AirExtension.ts
22849
+ var JETBRAINS_META_KEY = "jetbrains";
22850
+ var AIR_META_KEY = "air";
22851
+ var AIR_EXTENSION_VERSION_KEY = "version";
22852
+ var AIR_EXTENSION_CAPABILITIES_KEY = "capabilities";
22853
+ var AIR_DIFF_STATS_KEY = "diffStats";
22854
+ var AIR_SESSION_FAILURE_KEY = "sessionFailure";
22855
+ var AIR_AGENT_FILE_CHANGE_REPORT_KEY = "agentFileChangeReport";
22856
+ var AIR_NATIVE_SUBAGENT_SESSIONS_KEY = "nativeSubagentSessions";
22857
+ var AIR_ASYNC_TASKS_KEY = "asyncTasks";
22858
+ var AIR_RECOMMENDED_CONFIG_VALUE_KEY = "recommendedValue";
22859
+ var AIR_ASYNC_TASKS_BACKGROUNDED_KEY = "backgrounded";
22860
+ var AIR_AGENT_FILE_CHANGE_REPORT_REQUEST_KEY = "agentFileChangeReportRequest";
22861
+ var AIR_EXTENSION_VERSION = 1;
22862
+ function withAirMeta(meta3, key, value) {
22863
+ const root = asRecord(meta3);
22864
+ const jetbrains = asRecord(root[JETBRAINS_META_KEY]);
22865
+ const air = asRecord(jetbrains[AIR_META_KEY]);
22866
+ return {
22867
+ ...root,
22868
+ [JETBRAINS_META_KEY]: {
22869
+ ...jetbrains,
22870
+ [AIR_META_KEY]: {
22871
+ ...air,
22872
+ [AIR_EXTENSION_VERSION_KEY]: AIR_EXTENSION_VERSION,
22873
+ [key]: value
22874
+ }
22875
+ }
22876
+ };
22877
+ }
22878
+ function clientSupportsAirCapability(capabilities, capability) {
22879
+ const meta3 = asRecord(capabilities?._meta);
22880
+ const jetbrains = asRecord(meta3[JETBRAINS_META_KEY]);
22881
+ const air = asRecord(jetbrains[AIR_META_KEY]);
22882
+ const version2 = air[AIR_EXTENSION_VERSION_KEY];
22883
+ const supported = air[AIR_EXTENSION_CAPABILITIES_KEY];
22884
+ return typeof version2 === "number" && Number.isInteger(version2) && version2 >= AIR_EXTENSION_VERSION && Array.isArray(supported) && supported.includes(capability);
22885
+ }
22886
+ function asRecord(value) {
22887
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
22888
+ }
22889
+
22785
22890
  // src/CodexToolCallMapper.ts
22786
22891
  import { readFile } from "node:fs/promises";
22787
22892
  import path2 from "node:path";
@@ -22834,8 +22939,26 @@ function createContextCompactionMeta(metadata = {}) {
22834
22939
  };
22835
22940
  }
22836
22941
 
22942
+ // src/ToolCallName.ts
22943
+ function functionToolName(name, namespace) {
22944
+ return `${namespace ?? ""}${name}`;
22945
+ }
22946
+ function commandToolName(source) {
22947
+ switch (source) {
22948
+ case "unifiedExecStartup":
22949
+ return "exec_command";
22950
+ case "unifiedExecInteraction":
22951
+ return "write_stdin";
22952
+ // These sources do not identify a unique model tool.
22953
+ case "agent":
22954
+ case "userShell":
22955
+ return void 0;
22956
+ }
22957
+ }
22958
+
22837
22959
  // src/CodexToolCallMapper.ts
22838
22960
  var CONTEXT_COMPACTION_META = createContextCompactionMeta();
22961
+ var DIFF_STATS = new DiffStatsCalculator();
22839
22962
  function toAcpStatus(status) {
22840
22963
  switch (status) {
22841
22964
  case "inProgress":
@@ -22864,14 +22987,19 @@ async function createFileChangeUpdate(item) {
22864
22987
  };
22865
22988
  }
22866
22989
  async function createCommandExecutionUpdate(item) {
22990
+ const name = commandToolName(item.source);
22867
22991
  const commandAction = item.commandActions.length === 1 ? item.commandActions[0] : void 0;
22868
22992
  if (commandAction) {
22869
- return createCommandActionEvent(item.id, item.status, item.cwd, commandAction);
22993
+ return {
22994
+ ...createCommandActionEvent(item.id, item.status, item.cwd, commandAction),
22995
+ ...name === void 0 ? {} : { name }
22996
+ };
22870
22997
  }
22871
22998
  const command = stripShellPrefix(item.command);
22872
22999
  return createTerminalCommandEvent({
22873
23000
  sessionUpdate: "tool_call",
22874
23001
  toolCallId: item.id,
23002
+ ...name === void 0 ? {} : { name },
22875
23003
  kind: "execute",
22876
23004
  title: command,
22877
23005
  status: toAcpStatus(item.status),
@@ -22926,7 +23054,10 @@ async function createMcpToolCallUpdate(item) {
22926
23054
  };
22927
23055
  }
22928
23056
  async function createDynamicToolCallUpdate(item) {
22929
- return createExecuteToolCallUpdate(item, item.tool, { arguments: item.arguments });
23057
+ return {
23058
+ ...await createExecuteToolCallUpdate(item, item.tool, { arguments: item.arguments }),
23059
+ name: functionToolName(item.tool, item.namespace)
23060
+ };
22930
23061
  }
22931
23062
  function createImageViewUpdate(item) {
22932
23063
  const displayPath = item.path;
@@ -22934,6 +23065,7 @@ function createImageViewUpdate(item) {
22934
23065
  sessionUpdate: "tool_call",
22935
23066
  toolCallId: item.id,
22936
23067
  kind: "read",
23068
+ name: "view_image",
22937
23069
  title: `View Image ${displayPath}`,
22938
23070
  status: "completed",
22939
23071
  content: [createContent({
@@ -23491,53 +23623,43 @@ async function createAddFileContent(change) {
23491
23623
  newText: change.diff,
23492
23624
  // app-server always returns file content instead of diff
23493
23625
  path: change.path,
23494
- _meta: {
23495
- kind: "add"
23496
- }
23626
+ _meta: withAirMeta({ kind: "add" }, AIR_DIFF_STATS_KEY, DIFF_STATS.addedFile(change.diff))
23497
23627
  };
23498
23628
  }
23499
23629
  async function createUpdateFileContent(change) {
23500
23630
  if (change.kind.type !== "update") return null;
23501
23631
  const unifiedDiff = recoverCorruptedDiff(change.diff);
23632
+ const patches = parsePatch(unifiedDiff);
23633
+ if (patches.length !== 1) return null;
23634
+ const patch = patches[0];
23502
23635
  const movePath = change.kind.move_path;
23503
23636
  const oldContent = await readFileContent(change.path);
23504
23637
  if (oldContent !== null) {
23505
- const patchedContent = applyPatch(oldContent, unifiedDiff);
23638
+ const patchedContent = applyPatch(oldContent, patch);
23506
23639
  if (patchedContent === false) {
23507
- const revertedPatch2 = revertPatch(unifiedDiff);
23508
- if (revertedPatch2) {
23509
- const revertedContent2 = applyPatch(oldContent, revertedPatch2);
23510
- if (revertedContent2 !== false) {
23511
- return createUpdateDiffContent(change.path, revertedContent2, oldContent);
23512
- }
23640
+ const revertedContent2 = applyPatch(oldContent, reversePatch(patch));
23641
+ if (revertedContent2 !== false) {
23642
+ return createUpdateDiffContent(change.path, revertedContent2, oldContent, patch);
23513
23643
  }
23514
23644
  return null;
23515
23645
  }
23516
- return createUpdateDiffContent(movePath ?? change.path, oldContent, patchedContent);
23646
+ return createUpdateDiffContent(movePath ?? change.path, oldContent, patchedContent, patch);
23517
23647
  }
23518
23648
  if (!movePath) return null;
23519
23649
  const newContent = await readFileContent(movePath);
23520
23650
  if (newContent === null) return null;
23521
- const revertedPatch = revertPatch(unifiedDiff);
23522
- if (!revertedPatch) return null;
23523
- const revertedContent = applyPatch(newContent, revertedPatch);
23651
+ const revertedContent = applyPatch(newContent, reversePatch(patch));
23524
23652
  if (revertedContent === false) return null;
23525
- return createUpdateDiffContent(movePath, revertedContent, newContent);
23526
- }
23527
- function revertPatch(unifiedDiff) {
23528
- const [patch] = parsePatch(unifiedDiff);
23529
- if (!patch) return null;
23530
- return reversePatch(patch);
23653
+ return createUpdateDiffContent(movePath, revertedContent, newContent, patch);
23531
23654
  }
23532
- function createUpdateDiffContent(path9, oldText, newText) {
23655
+ function createUpdateDiffContent(path9, oldText, newText, patch) {
23656
+ const stats = DIFF_STATS.update(patch);
23533
23657
  return {
23534
23658
  type: "diff",
23535
23659
  oldText,
23536
23660
  newText,
23537
23661
  path: path9,
23538
- _meta: {
23539
- kind: "update"
23540
- }
23662
+ _meta: stats ? withAirMeta({ kind: "update" }, AIR_DIFF_STATS_KEY, stats) : { kind: "update" }
23541
23663
  };
23542
23664
  }
23543
23665
  async function createDeleteFileContent(change) {
@@ -23547,9 +23669,7 @@ async function createDeleteFileContent(change) {
23547
23669
  // app-server always returns file content instead of diff
23548
23670
  newText: "",
23549
23671
  path: change.path,
23550
- _meta: {
23551
- kind: "delete"
23552
- }
23672
+ _meta: withAirMeta({ kind: "delete" }, AIR_DIFF_STATS_KEY, DIFF_STATS.deletedFile(change.diff))
23553
23673
  };
23554
23674
  }
23555
23675
  async function readFileContent(filePath) {
@@ -23661,47 +23781,6 @@ function sameThreadGoalSnapshot(left, right) {
23661
23781
  // src/CodexEventHandler.ts
23662
23782
  import { randomUUID } from "node:crypto";
23663
23783
 
23664
- // src/AirExtension.ts
23665
- var JETBRAINS_META_KEY = "jetbrains";
23666
- var AIR_META_KEY = "air";
23667
- var AIR_EXTENSION_VERSION_KEY = "version";
23668
- var AIR_EXTENSION_CAPABILITIES_KEY = "capabilities";
23669
- var AIR_SESSION_FAILURE_KEY = "sessionFailure";
23670
- var AIR_AGENT_FILE_CHANGE_REPORT_KEY = "agentFileChangeReport";
23671
- var AIR_NATIVE_SUBAGENT_SESSIONS_KEY = "nativeSubagentSessions";
23672
- var AIR_ASYNC_TASKS_KEY = "asyncTasks";
23673
- var AIR_RECOMMENDED_CONFIG_VALUE_KEY = "recommendedValue";
23674
- var AIR_ASYNC_TASKS_BACKGROUNDED_KEY = "backgrounded";
23675
- var AIR_AGENT_FILE_CHANGE_REPORT_REQUEST_KEY = "agentFileChangeReportRequest";
23676
- var AIR_EXTENSION_VERSION = 1;
23677
- function withAirMeta(meta3, key, value) {
23678
- const root = asRecord(meta3);
23679
- const jetbrains = asRecord(root[JETBRAINS_META_KEY]);
23680
- const air = asRecord(jetbrains[AIR_META_KEY]);
23681
- return {
23682
- ...root,
23683
- [JETBRAINS_META_KEY]: {
23684
- ...jetbrains,
23685
- [AIR_META_KEY]: {
23686
- ...air,
23687
- [AIR_EXTENSION_VERSION_KEY]: AIR_EXTENSION_VERSION,
23688
- [key]: value
23689
- }
23690
- }
23691
- };
23692
- }
23693
- function clientSupportsAirCapability(capabilities, capability) {
23694
- const meta3 = asRecord(capabilities?._meta);
23695
- const jetbrains = asRecord(meta3[JETBRAINS_META_KEY]);
23696
- const air = asRecord(jetbrains[AIR_META_KEY]);
23697
- const version2 = air[AIR_EXTENSION_VERSION_KEY];
23698
- const supported = air[AIR_EXTENSION_CAPABILITIES_KEY];
23699
- return typeof version2 === "number" && Number.isInteger(version2) && version2 >= AIR_EXTENSION_VERSION && Array.isArray(supported) && supported.includes(capability);
23700
- }
23701
- function asRecord(value) {
23702
- return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
23703
- }
23704
-
23705
23784
  // src/subagents/CodexAgentPath.ts
23706
23785
  function normalizeAgentPath(path9) {
23707
23786
  const normalized = path9.trim().replace(/\/+$/, "");
@@ -24095,40 +24174,339 @@ function terminalStateFromTurn(status) {
24095
24174
  return void 0;
24096
24175
  }
24097
24176
  }
24098
- function fallbackName(sessionId) {
24099
- const suffix = sessionId.length > 8 ? sessionId.slice(-8) : sessionId;
24100
- return `Agent ${suffix}`;
24177
+ function fallbackName(sessionId) {
24178
+ const suffix = sessionId.length > 8 ? sessionId.slice(-8) : sessionId;
24179
+ return `Agent ${suffix}`;
24180
+ }
24181
+
24182
+ // src/RateLimitsMap.ts
24183
+ function rateLimitId(snapshot, fallback = "codex") {
24184
+ return snapshot.limitId ?? fallback;
24185
+ }
24186
+ function createRateLimitsMap(response) {
24187
+ const result = /* @__PURE__ */ new Map();
24188
+ const snapshots = Object.entries(response.rateLimitsByLimitId ?? {}).filter((entry) => entry[1] !== void 0);
24189
+ if (snapshots.length === 0) {
24190
+ snapshots.push([rateLimitId(response.rateLimits), response.rateLimits]);
24191
+ }
24192
+ for (const [fallbackId, snapshot] of snapshots) {
24193
+ const limitId = rateLimitId(snapshot, fallbackId);
24194
+ result.set(limitId, {
24195
+ limitId,
24196
+ limitName: snapshot.limitName ?? limitId,
24197
+ snapshot
24198
+ });
24199
+ }
24200
+ return result;
24201
+ }
24202
+ function mergeRateLimitSnapshot(previous, update) {
24203
+ return {
24204
+ ...update,
24205
+ limitId: update.limitId ?? "codex",
24206
+ credits: update.credits ?? previous.credits,
24207
+ individualLimit: update.individualLimit ?? previous.individualLimit,
24208
+ spendControlReached: update.spendControlReached ?? previous.spendControlReached,
24209
+ planType: update.planType ?? previous.planType
24210
+ };
24211
+ }
24212
+
24213
+ // src/AgentFileChangeReport.ts
24214
+ import fs2 from "node:fs";
24215
+ import path3 from "node:path";
24216
+ var AGENT_FILE_CHANGE_REPORT_VERSION = 1;
24217
+ var AGENT_FILE_CHANGE_REPORT_MAX_PATHS = 1024;
24218
+ var AGENT_FILE_CHANGE_REPORT_MAX_PATH_LENGTH = 4096;
24219
+ var AGENT_FILE_CHANGE_REPORT_MAX_TOTAL_BYTES = 256 * 1024;
24220
+ var AGENT_FILE_CHANGE_REPORT_MAX_DIFF_BYTES = 8 * 1024 * 1024;
24221
+ 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.";
24222
+ function captureAgentFileChangeWorkspace(cwd, additionalDirectories) {
24223
+ const lexicalCwd = parseWorkspaceRoot(cwd);
24224
+ return {
24225
+ cwd,
24226
+ additionalDirectories: [...additionalDirectories],
24227
+ diffRoot: lexicalCwd === null ? cwd : findDiffDisplayRoot(lexicalCwd).value
24228
+ };
24229
+ }
24230
+ function parseAgentFileChangeReportRequest(meta3) {
24231
+ const jetbrains = asRecord2(meta3?.[JETBRAINS_META_KEY]);
24232
+ const air = asRecord2(jetbrains?.[AIR_META_KEY]);
24233
+ const request = asRecord2(air?.[AIR_AGENT_FILE_CHANGE_REPORT_REQUEST_KEY]);
24234
+ if (request === null || !hasOnlyKeys(request, ["version", "requestId"]) || request["version"] !== AGENT_FILE_CHANGE_REPORT_VERSION) {
24235
+ return null;
24236
+ }
24237
+ const requestId = request["requestId"];
24238
+ if (typeof requestId !== "string" || !/^[A-Za-z0-9._:-]{1,128}$/.test(requestId)) {
24239
+ return null;
24240
+ }
24241
+ return { version: AGENT_FILE_CHANGE_REPORT_VERSION, requestId };
24242
+ }
24243
+ function createReportedAgentFileChangeReport(requestId, diff, workspace) {
24244
+ if (Buffer.byteLength(diff, "utf8") > AGENT_FILE_CHANGE_REPORT_MAX_DIFF_BYTES) {
24245
+ throw new AgentFileChangeReportError("invalidOutput", "The turn diff exceeds the parser input limit");
24246
+ }
24247
+ const parsedReport = parseTurnDiff(diff);
24248
+ const normalized = normalizeFileChangeReport(parsedReport, workspace);
24249
+ return fitReportedAgentFileChangeReport({
24250
+ version: AGENT_FILE_CHANGE_REPORT_VERSION,
24251
+ requestId,
24252
+ status: "reported",
24253
+ ...normalized
24254
+ });
24255
+ }
24256
+ function createUnavailableAgentFileChangeReport(requestId, reason) {
24257
+ return {
24258
+ version: AGENT_FILE_CHANGE_REPORT_VERSION,
24259
+ requestId,
24260
+ status: "unavailable",
24261
+ reason
24262
+ };
24263
+ }
24264
+ var AgentFileChangeReportError = class extends Error {
24265
+ reason;
24266
+ constructor(reason, message) {
24267
+ super(message);
24268
+ this.name = "AgentFileChangeReportError";
24269
+ this.reason = reason;
24270
+ }
24271
+ };
24272
+ function parseTurnDiff(diff) {
24273
+ if (diff.trim() === "") {
24274
+ return { paths: [], complete: false, uncertainty: TURN_DIFF_UNCERTAINTY };
24275
+ }
24276
+ let patches;
24277
+ try {
24278
+ patches = parsePatch(diff);
24279
+ } catch (error51) {
24280
+ throw new AgentFileChangeReportError(
24281
+ "invalidOutput",
24282
+ `The turn diff is not valid unified diff: ${error51 instanceof Error ? error51.message : String(error51)}`
24283
+ );
24284
+ }
24285
+ if (patches.length === 0) {
24286
+ throw new AgentFileChangeReportError("invalidOutput", "The turn diff contains no parseable file patches");
24287
+ }
24288
+ const rawFileHeaders = extractRawFileHeaders(diff);
24289
+ const paths = [];
24290
+ for (const [index, patch] of patches.entries()) {
24291
+ const rawHeaders = rawFileHeaders[index];
24292
+ const oldPath = normalizeDiffPath(selectLosslessFileName(rawHeaders?.oldFileName, patch.oldFileName));
24293
+ const newPath = normalizeDiffPath(selectLosslessFileName(rawHeaders?.newFileName, patch.newFileName));
24294
+ if (oldPath === null && newPath === null) {
24295
+ throw new AgentFileChangeReportError("invalidOutput", "The turn diff contains a patch without a file path");
24296
+ }
24297
+ if (oldPath !== null) paths.push(oldPath);
24298
+ if (newPath !== null) paths.push(newPath);
24299
+ }
24300
+ return { paths, complete: false, uncertainty: TURN_DIFF_UNCERTAINTY };
24301
+ }
24302
+ function extractRawFileHeaders(diff) {
24303
+ return diff.split(/(?=^diff --git )/m).filter((section) => section.trim() !== "").map((section) => {
24304
+ const lines = section.split(/\r?\n/);
24305
+ for (let index = 0; index < lines.length; index += 1) {
24306
+ const line = lines[index];
24307
+ if (line?.startsWith("@@ ")) return null;
24308
+ const nextLine = lines[index + 1];
24309
+ if (line?.startsWith("--- ") && nextLine?.startsWith("+++ ")) {
24310
+ return {
24311
+ oldFileName: line.slice(4),
24312
+ newFileName: nextLine.slice(4)
24313
+ };
24314
+ }
24315
+ }
24316
+ return null;
24317
+ });
24318
+ }
24319
+ function selectLosslessFileName(rawFileName, parsedFileName) {
24320
+ return rawFileName === void 0 || rawFileName.startsWith('"') ? parsedFileName : rawFileName;
24321
+ }
24322
+ function normalizeDiffPath(value) {
24323
+ if (value === void 0 || value === "/dev/null") return null;
24324
+ return value.startsWith("a/") || value.startsWith("b/") ? value.slice(2) : value;
24325
+ }
24326
+ function fitReportedAgentFileChangeReport(report) {
24327
+ const paths = [...report.paths];
24328
+ let truncated = report.truncated;
24329
+ let fitted = report;
24330
+ while (Buffer.byteLength(JSON.stringify(fitted), "utf8") > AGENT_FILE_CHANGE_REPORT_MAX_TOTAL_BYTES) {
24331
+ if (paths.length === 0) {
24332
+ throw new AgentFileChangeReportError("invalidOutput", "The audit report exceeds the wire limit");
24333
+ }
24334
+ paths.pop();
24335
+ truncated = true;
24336
+ fitted = {
24337
+ ...report,
24338
+ paths: [...paths],
24339
+ declaredComplete: false,
24340
+ truncated
24341
+ };
24342
+ }
24343
+ return fitted;
24344
+ }
24345
+ function normalizeFileChangeReport(report, workspace) {
24346
+ const lexicalCwd = parseWorkspaceRoot(workspace.cwd);
24347
+ if (lexicalCwd === null) {
24348
+ throw new AgentFileChangeReportError("providerError", "The session working directory is not absolute");
24349
+ }
24350
+ const cwd = canonicalizeWorkspaceRoot(lexicalCwd);
24351
+ const diffRoot = parseWorkspaceRoot(workspace.diffRoot);
24352
+ if (diffRoot === null || diffRoot.flavor !== cwd.flavor) {
24353
+ throw new AgentFileChangeReportError("providerError", "The captured turn-diff root is invalid");
24354
+ }
24355
+ const roots = [cwd, ...workspace.additionalDirectories.flatMap((directory) => {
24356
+ const root = normalizeWorkspaceRoot(directory);
24357
+ return root === null || root.flavor !== cwd.flavor ? [] : [root];
24358
+ })];
24359
+ const paths = [];
24360
+ const seen = /* @__PURE__ */ new Set();
24361
+ let totalBytes = 0;
24362
+ let truncated = false;
24363
+ for (const reportedPath of report.paths) {
24364
+ if (reportedPath.length > AGENT_FILE_CHANGE_REPORT_MAX_PATH_LENGTH) {
24365
+ truncated = true;
24366
+ continue;
24367
+ }
24368
+ const normalized = normalizeReportedPath(reportedPath, diffRoot, roots);
24369
+ if (normalized === null || normalized.value.length > AGENT_FILE_CHANGE_REPORT_MAX_PATH_LENGTH) {
24370
+ truncated = true;
24371
+ continue;
24372
+ }
24373
+ const key = normalized.flavor === "windows" ? normalized.value.toLowerCase() : normalized.value;
24374
+ if (seen.has(key)) {
24375
+ continue;
24376
+ }
24377
+ const bytes = Buffer.byteLength(normalized.value, "utf8");
24378
+ if (paths.length >= AGENT_FILE_CHANGE_REPORT_MAX_PATHS || totalBytes + bytes > AGENT_FILE_CHANGE_REPORT_MAX_TOTAL_BYTES) {
24379
+ truncated = true;
24380
+ continue;
24381
+ }
24382
+ seen.add(key);
24383
+ paths.push(normalized.value);
24384
+ totalBytes += bytes;
24385
+ }
24386
+ return {
24387
+ paths,
24388
+ declaredComplete: report.complete && !truncated,
24389
+ truncated,
24390
+ ...report.uncertainty ? { uncertainty: report.uncertainty } : {}
24391
+ };
24392
+ }
24393
+ function normalizeWorkspaceRoot(value) {
24394
+ const root = parseWorkspaceRoot(value);
24395
+ return root === null ? null : canonicalizeWorkspaceRoot(root);
24396
+ }
24397
+ function parseWorkspaceRoot(value) {
24398
+ const trimmed = value.trim();
24399
+ if (!isValidPathText(trimmed)) {
24400
+ return null;
24401
+ }
24402
+ if (isWindowsAbsolutePath(trimmed)) {
24403
+ return {
24404
+ value: path3.win32.normalize(trimmed.replace(/\//g, "\\")),
24405
+ flavor: "windows"
24406
+ };
24407
+ }
24408
+ if (path3.posix.isAbsolute(trimmed)) {
24409
+ return {
24410
+ value: path3.posix.normalize(trimmed.replace(/\\/g, "/")),
24411
+ flavor: "posix"
24412
+ };
24413
+ }
24414
+ return null;
24415
+ }
24416
+ function normalizeReportedPath(value, relativeRoot, roots) {
24417
+ 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)) {
24418
+ return null;
24419
+ }
24420
+ let candidate;
24421
+ if (isWindowsAbsolutePath(value)) {
24422
+ candidate = { value: path3.win32.normalize(value.replace(/\//g, "\\")), flavor: "windows" };
24423
+ } else if (path3.posix.isAbsolute(value)) {
24424
+ candidate = { value: path3.posix.normalize(value.replace(/\\/g, "/")), flavor: "posix" };
24425
+ } else if (relativeRoot.flavor === "windows") {
24426
+ candidate = {
24427
+ value: path3.win32.resolve(relativeRoot.value, value.replace(/\//g, "\\")),
24428
+ flavor: "windows"
24429
+ };
24430
+ } else {
24431
+ candidate = {
24432
+ value: path3.posix.resolve(relativeRoot.value, value.replace(/\\/g, "/")),
24433
+ flavor: "posix"
24434
+ };
24435
+ }
24436
+ candidate = canonicalizeReportedPath(candidate);
24437
+ return roots.some((root) => pathIsStrictlyInside(root, candidate)) ? candidate : null;
24438
+ }
24439
+ function findDiffDisplayRoot(cwd) {
24440
+ if (!isNativePathFlavor(cwd.flavor)) return cwd;
24441
+ const pathImplementation = cwd.flavor === "windows" ? path3.win32 : path3.posix;
24442
+ let current = cwd.value;
24443
+ while (true) {
24444
+ if (fs2.existsSync(pathImplementation.join(current, ".git"))) {
24445
+ return canonicalizeWorkspaceRoot({ value: current, flavor: cwd.flavor });
24446
+ }
24447
+ const parent = pathImplementation.dirname(current);
24448
+ if (parent === current) return cwd;
24449
+ current = parent;
24450
+ }
24451
+ }
24452
+ function canonicalizeWorkspaceRoot(root) {
24453
+ if (!isNativePathFlavor(root.flavor)) {
24454
+ return root;
24455
+ }
24456
+ return { ...root, value: canonicalizeFromExistingAncestor(root.value) };
24457
+ }
24458
+ function canonicalizeReportedPath(candidate) {
24459
+ if (!isNativePathFlavor(candidate.flavor)) {
24460
+ return candidate;
24461
+ }
24462
+ const parent = canonicalizeFromExistingAncestor(path3.dirname(candidate.value));
24463
+ return { ...candidate, value: path3.resolve(parent, path3.basename(candidate.value)) };
24464
+ }
24465
+ function canonicalizeFromExistingAncestor(value) {
24466
+ const original = path3.resolve(value);
24467
+ let current = original;
24468
+ const missingSegments = [];
24469
+ while (true) {
24470
+ try {
24471
+ const canonical = fs2.realpathSync.native(current);
24472
+ return path3.resolve(canonical, ...missingSegments.reverse());
24473
+ } catch (error51) {
24474
+ if (!isMissingPathError(error51)) return original;
24475
+ }
24476
+ const parent = path3.dirname(current);
24477
+ if (parent === current) return original;
24478
+ missingSegments.push(path3.basename(current));
24479
+ current = parent;
24480
+ }
24481
+ }
24482
+ function isMissingPathError(error51) {
24483
+ if (typeof error51 !== "object" || error51 === null || !("code" in error51)) return false;
24484
+ return error51.code === "ENOENT" || error51.code === "ENOTDIR";
24101
24485
  }
24102
-
24103
- // src/RateLimitsMap.ts
24104
- function rateLimitId(snapshot, fallback = "codex") {
24105
- return snapshot.limitId ?? fallback;
24486
+ function isNativePathFlavor(flavor) {
24487
+ return process.platform === "win32" ? flavor === "windows" : flavor === "posix";
24106
24488
  }
24107
- function createRateLimitsMap(response) {
24108
- const result = /* @__PURE__ */ new Map();
24109
- const snapshots = Object.entries(response.rateLimitsByLimitId ?? {}).filter((entry) => entry[1] !== void 0);
24110
- if (snapshots.length === 0) {
24111
- snapshots.push([rateLimitId(response.rateLimits), response.rateLimits]);
24112
- }
24113
- for (const [fallbackId, snapshot] of snapshots) {
24114
- const limitId = rateLimitId(snapshot, fallbackId);
24115
- result.set(limitId, {
24116
- limitId,
24117
- limitName: snapshot.limitName ?? limitId,
24118
- snapshot
24119
- });
24489
+ function pathIsStrictlyInside(root, candidate) {
24490
+ if (root.flavor !== candidate.flavor) {
24491
+ return false;
24120
24492
  }
24121
- return result;
24493
+ const pathImplementation = root.flavor === "windows" ? path3.win32 : path3.posix;
24494
+ const relative = pathImplementation.relative(root.value, candidate.value);
24495
+ return relative.length > 0 && !pathImplementation.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${pathImplementation.sep}`);
24122
24496
  }
24123
- function mergeRateLimitSnapshot(previous, update) {
24124
- return {
24125
- ...update,
24126
- limitId: update.limitId ?? "codex",
24127
- credits: update.credits ?? previous.credits,
24128
- individualLimit: update.individualLimit ?? previous.individualLimit,
24129
- spendControlReached: update.spendControlReached ?? previous.spendControlReached,
24130
- planType: update.planType ?? previous.planType
24131
- };
24497
+ function isWindowsAbsolutePath(value) {
24498
+ const portable = value.replace(/\\/g, "/");
24499
+ return /^[A-Za-z]:\//.test(portable) || /^\/\/[^/]+\/[^/]+(?:\/|$)/.test(portable);
24500
+ }
24501
+ function isValidPathText(value) {
24502
+ return value.length > 0 && !/[\u0000-\u001F\u007F-\u009F]/.test(value);
24503
+ }
24504
+ function asRecord2(value) {
24505
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
24506
+ }
24507
+ function hasOnlyKeys(value, allowed) {
24508
+ const allowedKeys = new Set(allowed);
24509
+ return Object.keys(value).every((key) => allowedKeys.has(key));
24132
24510
  }
24133
24511
 
24134
24512
  // src/CodexEventHandler.ts
@@ -24238,6 +24616,9 @@ var CodexEventHandler = class _CodexEventHandler {
24238
24616
  terminalCommandIds = /* @__PURE__ */ new Set();
24239
24617
  terminalCommandOutputIds = /* @__PURE__ */ new Set();
24240
24618
  agentMessagePhases = /* @__PURE__ */ new Map();
24619
+ turnDiffs = /* @__PURE__ */ new Map();
24620
+ oversizedTurnDiffs = /* @__PURE__ */ new Set();
24621
+ collectTurnDiffs;
24241
24622
  subagents;
24242
24623
  /** Connection-level `authStatus` sink; the app-server account push feeds it. */
24243
24624
  onAccountUpdated;
@@ -24245,7 +24626,7 @@ var CodexEventHandler = class _CodexEventHandler {
24245
24626
  sessionState.sessionId,
24246
24627
  false,
24247
24628
  new ACPSessionConnection(connection, sessionState.sessionId)
24248
- ), onAccountUpdated) {
24629
+ ), onAccountUpdated, collectTurnDiffs = false) {
24249
24630
  this.onAccountUpdated = onAccountUpdated;
24250
24631
  this.sessionState = sessionState;
24251
24632
  this.supportsPlanUpdates = supportsPlanUpdates;
@@ -24253,6 +24634,7 @@ var CodexEventHandler = class _CodexEventHandler {
24253
24634
  this.sessionFailureEpoch = sessionFailureEpoch;
24254
24635
  this.session = new ACPSessionConnection(connection, sessionState.sessionId);
24255
24636
  this.subagents = subagents;
24637
+ this.collectTurnDiffs = collectTurnDiffs;
24256
24638
  if (sessionState.sessionFailure !== void 0) {
24257
24639
  this.failuresById.set(sessionState.sessionFailure.id, sessionState.sessionFailure);
24258
24640
  }
@@ -24260,6 +24642,12 @@ var CodexEventHandler = class _CodexEventHandler {
24260
24642
  getFailure() {
24261
24643
  return this.failure;
24262
24644
  }
24645
+ getTurnDiff(turnId) {
24646
+ return this.turnDiffs.get(turnId) ?? "";
24647
+ }
24648
+ isTurnDiffOversized(turnId) {
24649
+ return this.oversizedTurnDiffs.has(turnId);
24650
+ }
24263
24651
  getTerminalSessionFailureMeta(turnId, allowUnattributed = false) {
24264
24652
  const failure = this.sessionState.sessionFailure;
24265
24653
  if (!this.supportsTypedSessionFailures || failure === void 0 || (this.failureTurnIdById.get(failure.id) === void 0 ? !allowUnattributed : turnId === null || this.failureTurnIdById.get(failure.id) !== turnId)) {
@@ -24419,6 +24807,8 @@ var CodexEventHandler = class _CodexEventHandler {
24419
24807
  this.pendingPlanItemIds.clear();
24420
24808
  this.planDeltaTextByItemId.clear();
24421
24809
  this.lastEmittedPlanTextByItemId.clear();
24810
+ this.turnDiffs.clear();
24811
+ this.oversizedTurnDiffs.clear();
24422
24812
  }
24423
24813
  async createUpdateEvent(notification) {
24424
24814
  switch (notification.method) {
@@ -24437,6 +24827,20 @@ var CodexEventHandler = class _CodexEventHandler {
24437
24827
  case "turn/plan/updated":
24438
24828
  this.completeRetryIncidentOnTurnProgress();
24439
24829
  return await this.updatePlan(notification.params);
24830
+ case "turn/diff/updated":
24831
+ if (notification.params.threadId === this.sessionState.sessionId) {
24832
+ this.completeRetryIncidentOnTurnProgress();
24833
+ if (!this.disposed && this.collectTurnDiffs) {
24834
+ if (Buffer.byteLength(notification.params.diff, "utf8") > AGENT_FILE_CHANGE_REPORT_MAX_DIFF_BYTES) {
24835
+ this.turnDiffs.delete(notification.params.turnId);
24836
+ this.oversizedTurnDiffs.add(notification.params.turnId);
24837
+ } else {
24838
+ this.oversizedTurnDiffs.delete(notification.params.turnId);
24839
+ this.turnDiffs.set(notification.params.turnId, notification.params.diff);
24840
+ }
24841
+ }
24842
+ }
24843
+ return null;
24440
24844
  case "error":
24441
24845
  return await this.createErrorEvent(notification.params);
24442
24846
  case "turn/started":
@@ -24531,7 +24935,6 @@ var CodexEventHandler = class _CodexEventHandler {
24531
24935
  case "command/exec/outputDelta":
24532
24936
  case "hook/started":
24533
24937
  case "hook/completed":
24534
- case "turn/diff/updated":
24535
24938
  case "turn/moderationMetadata":
24536
24939
  case "item/fileChange/outputDelta":
24537
24940
  case "item/fileChange/patchUpdated":
@@ -24715,10 +25118,16 @@ ${event.details}` : event.summary;
24715
25118
  async completeItemEvent(event) {
24716
25119
  switch (event.item.type) {
24717
25120
  case "fileChange":
25121
+ return {
25122
+ sessionUpdate: "tool_call_update",
25123
+ toolCallId: event.item.id,
25124
+ status: event.item.status === "completed" ? "completed" : "failed"
25125
+ };
24718
25126
  case "dynamicToolCall":
24719
25127
  return {
24720
25128
  sessionUpdate: "tool_call_update",
24721
25129
  toolCallId: event.item.id,
25130
+ name: functionToolName(event.item.tool, event.item.namespace),
24722
25131
  status: event.item.status === "completed" ? "completed" : "failed"
24723
25132
  };
24724
25133
  case "mcpToolCall":
@@ -24930,9 +25339,11 @@ ${event.stdin}
24930
25339
  return `mcp_startup.${encodeURIComponent(serverName)}`;
24931
25340
  }
24932
25341
  completeCommandExecutionEvent(item) {
25342
+ const name = commandToolName(item.source);
24933
25343
  const update = {
24934
25344
  sessionUpdate: "tool_call_update",
24935
25345
  toolCallId: item.id,
25346
+ ...name === void 0 ? {} : { name },
24936
25347
  status: item.status === "completed" ? "completed" : "failed",
24937
25348
  rawOutput: {
24938
25349
  formatted_output: item.aggregatedOutput ?? "",
@@ -25548,7 +25959,8 @@ function nonBlank(value) {
25548
25959
  }
25549
25960
 
25550
25961
  // src/permissions/presentation.ts
25551
- function commandToolCall(params) {
25962
+ function commandToolCall(params, permissionContext) {
25963
+ const name = permissionContext.commandName(params.threadId, params.itemId);
25552
25964
  const network = params.networkApprovalContext;
25553
25965
  const rawInput = {
25554
25966
  ...params.command ? { command: stripShellPrefix(params.command) } : {},
@@ -25559,6 +25971,7 @@ function commandToolCall(params) {
25559
25971
  const additionalPermissionContent = params.additionalPermissions ? permissionProfileContent(params.additionalPermissions) : [];
25560
25972
  return {
25561
25973
  toolCallId: params.itemId,
25974
+ ...name !== void 0 ? { name } : {},
25562
25975
  kind: "execute",
25563
25976
  status: "pending",
25564
25977
  title: network ? `${network.protocol} network access to ${network.host}` : commandTitle(params.commandActions),
@@ -25584,6 +25997,7 @@ function additionalPermissionsToolCall(itemId, cwd, environmentId, permissions)
25584
25997
  const content = permissionProfileContent(permissions);
25585
25998
  return {
25586
25999
  toolCallId: itemId,
26000
+ name: "request_permissions",
25587
26001
  kind: "other",
25588
26002
  status: "pending",
25589
26003
  title: "Additional sandbox permissions",
@@ -25682,7 +26096,7 @@ var CodexApprovalHandler = class {
25682
26096
  try {
25683
26097
  const response = await this.requestPermission({
25684
26098
  sessionId: params.threadId,
25685
- toolCall: commandToolCall(authoritativeParams),
26099
+ toolCall: commandToolCall(authoritativeParams, this.permissionContext),
25686
26100
  options: decisions.map(({ option }) => option),
25687
26101
  _meta: requestPermissionMeta(
25688
26102
  params.networkApprovalContext ? CODEX_NETWORK_PERMISSION_TITLE : CODEX_COMMAND_PERMISSION_TITLE,
@@ -25790,6 +26204,7 @@ var PermissionPromptContext = class {
25790
26204
  this.nextStandaloneId = nextStandaloneId;
25791
26205
  }
25792
26206
  nextStandaloneId;
26207
+ commandNames = /* @__PURE__ */ new Map();
25793
26208
  fileChanges = /* @__PURE__ */ new Map();
25794
26209
  pendingMcpApprovals = /* @__PURE__ */ new Map();
25795
26210
  handleNotification(notification) {
@@ -25813,6 +26228,9 @@ var PermissionPromptContext = class {
25813
26228
  fileChange(threadId, itemId) {
25814
26229
  return this.fileChanges.get(threadId)?.get(itemId);
25815
26230
  }
26231
+ commandName(threadId, itemId) {
26232
+ return this.commandNames.get(threadId)?.get(itemId);
26233
+ }
25816
26234
  popPendingMcpApproval(threadId, serverName) {
25817
26235
  const byServer = this.pendingMcpApprovals.get(threadId);
25818
26236
  if (!byServer) return void 0;
@@ -25827,6 +26245,15 @@ var PermissionPromptContext = class {
25827
26245
  return this.nextStandaloneId(serverName);
25828
26246
  }
25829
26247
  handleItemStarted(threadId, item) {
26248
+ if (item.type === "commandExecution") {
26249
+ const name = commandToolName(item.source);
26250
+ if (name !== void 0) {
26251
+ const byItem = this.commandNames.get(threadId) ?? /* @__PURE__ */ new Map();
26252
+ byItem.set(item.id, name);
26253
+ this.commandNames.set(threadId, byItem);
26254
+ }
26255
+ return;
26256
+ }
25830
26257
  if (item.type === "fileChange") {
25831
26258
  const byItem = this.fileChanges.get(threadId) ?? /* @__PURE__ */ new Map();
25832
26259
  byItem.set(item.id, item);
@@ -25841,6 +26268,12 @@ var PermissionPromptContext = class {
25841
26268
  this.pendingMcpApprovals.set(threadId, byServer);
25842
26269
  }
25843
26270
  handleItemCompleted(threadId, item) {
26271
+ if (item.type === "commandExecution") {
26272
+ const byItem = this.commandNames.get(threadId);
26273
+ byItem?.delete(item.id);
26274
+ if (byItem?.size === 0) this.commandNames.delete(threadId);
26275
+ return;
26276
+ }
25844
26277
  if (item.type === "fileChange") {
25845
26278
  const byItem = this.fileChanges.get(threadId);
25846
26279
  byItem?.delete(item.id);
@@ -25858,6 +26291,7 @@ var PermissionPromptContext = class {
25858
26291
  if (byServer.size === 0) this.pendingMcpApprovals.delete(threadId);
25859
26292
  }
25860
26293
  clearTransientState(threadId) {
26294
+ this.commandNames.delete(threadId);
25861
26295
  this.fileChanges.delete(threadId);
25862
26296
  this.pendingMcpApprovals.delete(threadId);
25863
26297
  }
@@ -26046,7 +26480,9 @@ function cancelledResponse() {
26046
26480
  }
26047
26481
 
26048
26482
  // src/CodexElicitationHandler.ts
26049
- var USER_INPUT_OTHER_FIELD_SUFFIX = "__other";
26483
+ var USER_INPUT_NOTE_FIELD_SUFFIX = "_note";
26484
+ var USER_INPUT_OTHER_OPTION = "None of the above";
26485
+ var USER_INPUT_NOTE_PREFIX = "user_note: ";
26050
26486
  function normalizeElicitationSchema(value) {
26051
26487
  const normalized = normalizeElicitationSchemaValue(value);
26052
26488
  if (!isRecord2(normalized)) {
@@ -26103,16 +26539,14 @@ function elicitationResponseMeta(response, context, persist = void 0) {
26103
26539
  }
26104
26540
  return Object.keys(meta3).length === 0 ? null : meta3;
26105
26541
  }
26106
- function userInputOtherFieldId(questionId, questionIds) {
26107
- const base = `${questionId}${USER_INPUT_OTHER_FIELD_SUFFIX}`;
26108
- if (!questionIds.has(base)) {
26109
- return base;
26110
- }
26542
+ function userInputNoteFieldId(questionId, questionIds) {
26543
+ const base = `${questionId}${USER_INPUT_NOTE_FIELD_SUFFIX}`;
26544
+ let fieldId = base;
26111
26545
  let index = 1;
26112
- while (questionIds.has(`${base}${index}`)) {
26113
- index += 1;
26546
+ while (questionIds.has(fieldId)) {
26547
+ fieldId = `${base}${index++}`;
26114
26548
  }
26115
- return `${base}${index}`;
26549
+ return fieldId;
26116
26550
  }
26117
26551
  function userInputResponseValue(content, fieldId) {
26118
26552
  const value = content[fieldId];
@@ -26349,8 +26783,8 @@ var CodexElicitationHandler = class {
26349
26783
  const hasOptions = options.length > 0;
26350
26784
  const hasOtherAnswer = question.isOther && hasOptions;
26351
26785
  const base = {
26352
- title: question.header || question.id,
26353
- description: question.question,
26786
+ title: question.question || question.header || question.id,
26787
+ ...question.header ? { description: question.header } : {},
26354
26788
  _meta: {
26355
26789
  codex: {
26356
26790
  isOther: question.isOther,
@@ -26358,42 +26792,45 @@ var CodexElicitationHandler = class {
26358
26792
  }
26359
26793
  }
26360
26794
  };
26361
- if (!hasOtherAnswer) {
26362
- required2.push(question.id);
26363
- }
26795
+ required2.push(question.id);
26364
26796
  properties[question.id] = hasOptions ? {
26365
26797
  ...base,
26366
26798
  type: "string",
26367
- oneOf: options.map((option) => ({
26368
- const: option.label,
26369
- title: option.label,
26370
- description: option.description
26371
- }))
26799
+ oneOf: [
26800
+ ...options.map((option) => ({
26801
+ const: option.label,
26802
+ title: option.label,
26803
+ ...option.description ? { description: option.description } : {}
26804
+ })),
26805
+ ...hasOtherAnswer && !options.some((option) => option.label === USER_INPUT_OTHER_OPTION) ? [{
26806
+ const: USER_INPUT_OTHER_OPTION,
26807
+ title: USER_INPUT_OTHER_OPTION,
26808
+ description: "Provide a different answer in the note field."
26809
+ }] : []
26810
+ ]
26372
26811
  } : {
26373
26812
  ...base,
26374
26813
  type: "string"
26375
26814
  };
26376
26815
  if (hasOtherAnswer) {
26377
- properties[userInputOtherFieldId(question.id, questionIds)] = {
26816
+ properties[userInputNoteFieldId(question.id, questionIds)] = {
26378
26817
  type: "string",
26379
- title: "Other",
26380
- description: "Type your own answer instead of choosing an option above.",
26818
+ title: "Additional answer or note",
26381
26819
  _meta: {
26382
26820
  codex: {
26383
26821
  questionId: question.id,
26384
- isOtherAnswer: true,
26822
+ role: "user_note",
26385
26823
  isSecret: question.isSecret
26386
26824
  }
26387
26825
  }
26388
26826
  };
26389
26827
  }
26390
26828
  }
26391
- const firstQuestion = params.questions[0];
26392
26829
  return {
26393
26830
  sessionId: params.threadId,
26394
26831
  toolCallId: params.itemId,
26395
26832
  mode: "form",
26396
- message: params.questions.length === 1 && firstQuestion ? firstQuestion.question : "Input requested",
26833
+ message: "Codex needs your input to continue.",
26397
26834
  requestedSchema: {
26398
26835
  type: "object",
26399
26836
  properties,
@@ -26444,12 +26881,23 @@ var CodexElicitationHandler = class {
26444
26881
  const content = contentRecord(response.content);
26445
26882
  const questionIds = new Set(params.questions.map((question) => question.id));
26446
26883
  for (const question of params.questions) {
26447
- const value = question.isOther && question.options != null && question.options.length > 0 ? userInputResponseValue(content, userInputOtherFieldId(question.id, questionIds)) ?? userInputResponseValue(content, question.id) : userInputResponseValue(content, question.id);
26448
- if (value === void 0) {
26884
+ const answerValues = [];
26885
+ const value = userInputResponseValue(content, question.id);
26886
+ if (value !== void 0) {
26887
+ answerValues.push(...Array.isArray(value) ? value.map(String) : [String(value)]);
26888
+ }
26889
+ if (question.isOther && question.options != null && question.options.length > 0) {
26890
+ const note = userInputResponseValue(content, userInputNoteFieldId(question.id, questionIds));
26891
+ if (note !== void 0) {
26892
+ const notes = Array.isArray(note) ? note : [note];
26893
+ answerValues.push(...notes.map((item) => `${USER_INPUT_NOTE_PREFIX}${String(item).trim()}`));
26894
+ }
26895
+ }
26896
+ if (answerValues.length === 0) {
26449
26897
  continue;
26450
26898
  }
26451
26899
  answers[question.id] = {
26452
- answers: Array.isArray(value) ? value.map(String) : [String(value)]
26900
+ answers: answerValues
26453
26901
  };
26454
26902
  }
26455
26903
  return { answers };
@@ -26539,31 +26987,31 @@ function isCodexAuthRequest(request) {
26539
26987
 
26540
26988
  // node_modules/open/index.js
26541
26989
  import process11 from "node:process";
26542
- import path4 from "node:path";
26990
+ import path5 from "node:path";
26543
26991
  import { fileURLToPath } from "node:url";
26544
26992
  import childProcess4 from "node:child_process";
26545
- import fs6, { constants as fsConstants2 } from "node:fs/promises";
26993
+ import fs7, { constants as fsConstants2 } from "node:fs/promises";
26546
26994
 
26547
26995
  // node_modules/wsl-utils/index.js
26548
- import path3 from "node:path";
26996
+ import path4 from "node:path";
26549
26997
  import { promisify as promisify2 } from "node:util";
26550
26998
  import childProcess2 from "node:child_process";
26551
- import fs5, { constants as fsConstants } from "node:fs/promises";
26999
+ import fs6, { constants as fsConstants } from "node:fs/promises";
26552
27000
 
26553
27001
  // node_modules/is-wsl/index.js
26554
27002
  import process3 from "node:process";
26555
27003
  import os from "node:os";
26556
- import fs4 from "node:fs";
27004
+ import fs5 from "node:fs";
26557
27005
 
26558
27006
  // node_modules/is-inside-container/index.js
26559
- import fs3 from "node:fs";
27007
+ import fs4 from "node:fs";
26560
27008
 
26561
27009
  // node_modules/is-docker/index.js
26562
- import fs2 from "node:fs";
27010
+ import fs3 from "node:fs";
26563
27011
  var isDockerCached;
26564
27012
  function hasDockerEnv() {
26565
27013
  try {
26566
- fs2.statSync("/.dockerenv");
27014
+ fs3.statSync("/.dockerenv");
26567
27015
  return true;
26568
27016
  } catch {
26569
27017
  return false;
@@ -26571,7 +27019,7 @@ function hasDockerEnv() {
26571
27019
  }
26572
27020
  function hasDockerCGroup() {
26573
27021
  try {
26574
- return fs2.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
27022
+ return fs3.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
26575
27023
  } catch {
26576
27024
  return false;
26577
27025
  }
@@ -26587,7 +27035,7 @@ function isDocker() {
26587
27035
  var cachedResult;
26588
27036
  var hasContainerEnv = () => {
26589
27037
  try {
26590
- fs3.statSync("/run/.containerenv");
27038
+ fs4.statSync("/run/.containerenv");
26591
27039
  return true;
26592
27040
  } catch {
26593
27041
  return false;
@@ -26612,12 +27060,12 @@ var isWsl = () => {
26612
27060
  return true;
26613
27061
  }
26614
27062
  try {
26615
- if (fs4.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft")) {
27063
+ if (fs5.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft")) {
26616
27064
  return !isInsideContainer();
26617
27065
  }
26618
27066
  } catch {
26619
27067
  }
26620
- if (fs4.existsSync("/proc/sys/fs/binfmt_misc/WSLInterop") || fs4.existsSync("/run/WSL")) {
27068
+ if (fs5.existsSync("/proc/sys/fs/binfmt_misc/WSLInterop") || fs5.existsSync("/run/WSL")) {
26621
27069
  return !isInsideContainer();
26622
27070
  }
26623
27071
  return false;
@@ -26685,14 +27133,14 @@ var wslDrivesMountPoint = /* @__PURE__ */ (() => {
26685
27133
  const configFilePath = "/etc/wsl.conf";
26686
27134
  let isConfigFileExists = false;
26687
27135
  try {
26688
- await fs5.access(configFilePath, fsConstants.F_OK);
27136
+ await fs6.access(configFilePath, fsConstants.F_OK);
26689
27137
  isConfigFileExists = true;
26690
27138
  } catch {
26691
27139
  }
26692
27140
  if (!isConfigFileExists) {
26693
27141
  return defaultMountPoint;
26694
27142
  }
26695
- const configContent = await fs5.readFile(configFilePath, { encoding: "utf8" });
27143
+ const configContent = await fs6.readFile(configFilePath, { encoding: "utf8" });
26696
27144
  const parsedMountPoint = parseMountPointFromConfig(configContent);
26697
27145
  if (parsedMountPoint === void 0) {
26698
27146
  return defaultMountPoint;
@@ -26712,7 +27160,7 @@ var canAccessPowerShell = async () => {
26712
27160
  canAccessPowerShellPromise ??= (async () => {
26713
27161
  try {
26714
27162
  const psPath = await powerShellPath2();
26715
- await fs5.access(psPath, fsConstants.X_OK);
27163
+ await fs6.access(psPath, fsConstants.X_OK);
26716
27164
  return true;
26717
27165
  } catch {
26718
27166
  return false;
@@ -26725,7 +27173,7 @@ var wslDefaultBrowser = async () => {
26725
27173
  const command = String.raw`(Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice").ProgId`;
26726
27174
  const { stdout } = await executePowerShell(command, {
26727
27175
  powerShellPath: psPath,
26728
- cwd: path3.dirname(psPath)
27176
+ cwd: path4.dirname(psPath)
26729
27177
  });
26730
27178
  return stdout.trim();
26731
27179
  };
@@ -26931,8 +27379,8 @@ var is_in_ssh_default = isInSsh;
26931
27379
 
26932
27380
  // node_modules/open/index.js
26933
27381
  var fallbackAttemptSymbol = /* @__PURE__ */ Symbol("fallbackAttempt");
26934
- var __dirname = import.meta.url ? path4.dirname(fileURLToPath(import.meta.url)) : "";
26935
- var localXdgOpenPath = path4.join(__dirname, "xdg-open");
27382
+ var __dirname = import.meta.url ? path5.dirname(fileURLToPath(import.meta.url)) : "";
27383
+ var localXdgOpenPath = path5.join(__dirname, "xdg-open");
26936
27384
  var { platform, arch } = process11;
26937
27385
  var tryEachApp = async (apps2, opener) => {
26938
27386
  if (apps2.length === 0) {
@@ -27074,7 +27522,7 @@ var baseOpen = async (options) => {
27074
27522
  childProcessOptions.stdio = "ignore";
27075
27523
  }
27076
27524
  if (is_wsl_default) {
27077
- childProcessOptions.cwd = path4.dirname(command);
27525
+ childProcessOptions.cwd = path5.dirname(command);
27078
27526
  }
27079
27527
  } else {
27080
27528
  if (app) {
@@ -27083,7 +27531,7 @@ var baseOpen = async (options) => {
27083
27531
  const isBundled = !__dirname || __dirname === "/";
27084
27532
  let exeLocalXdgOpen = false;
27085
27533
  try {
27086
- await fs6.access(localXdgOpenPath, fsConstants2.X_OK);
27534
+ await fs7.access(localXdgOpenPath, fsConstants2.X_OK);
27087
27535
  exeLocalXdgOpen = true;
27088
27536
  } catch {
27089
27537
  }
@@ -27371,7 +27819,7 @@ var package_default = {
27371
27819
  publishConfig: {
27372
27820
  access: "public"
27373
27821
  },
27374
- version: "1.11.0-agentconnect.1",
27822
+ version: "1.12.0-agentconnect.1",
27375
27823
  description: "",
27376
27824
  main: "dist/index.js",
27377
27825
  bin: {
@@ -27422,402 +27870,106 @@ var package_default = {
27422
27870
  keywords: [],
27423
27871
  author: "Agent Client Protocol",
27424
27872
  license: "Apache-2.0",
27425
- type: "module",
27426
- devDependencies: {
27427
- "@types/node": "^26.1.0",
27428
- esbuild: "^0.28.2",
27429
- "mcp-hello-world": "^1.1.2",
27430
- tsx: "^4.23.12",
27431
- typescript: "^7.0.2",
27432
- vitest: "^4.1.11"
27433
- },
27434
- dependencies: {
27435
- "@agentclientprotocol/sdk": "^1.4.0",
27436
- "@openai/codex": "^0.153.4",
27437
- diff: "^9.0.0",
27438
- open: "^11.0.1",
27439
- "vscode-jsonrpc": "^9.0.1",
27440
- zod: "^4.0.0"
27441
- }
27442
- };
27443
-
27444
- // src/CollaborationModeConfig.ts
27445
- var COLLABORATION_MODE_CONFIG_ID = "collaboration_mode";
27446
- var DEFAULT_COLLABORATION_MODE = "default";
27447
- var PLAN_COLLABORATION_MODE = "plan";
27448
- function createCollaborationModeConfigOption(currentValue) {
27449
- return {
27450
- id: COLLABORATION_MODE_CONFIG_ID,
27451
- name: "Collaboration mode",
27452
- description: "How Codex collaborates for subsequent turns",
27453
- category: "collaboration_mode",
27454
- type: "select",
27455
- currentValue,
27456
- options: [
27457
- { value: DEFAULT_COLLABORATION_MODE, name: "Default" },
27458
- { value: PLAN_COLLABORATION_MODE, name: "Plan", description: "Plan before making changes" }
27459
- ]
27460
- };
27461
- }
27462
- function parseCollaborationMode(value) {
27463
- if (value === DEFAULT_COLLABORATION_MODE) return DEFAULT_COLLABORATION_MODE;
27464
- if (value === PLAN_COLLABORATION_MODE) return PLAN_COLLABORATION_MODE;
27465
- return null;
27466
- }
27467
- function createCodexCollaborationMode(mode, currentModelId) {
27468
- const modelId = ModelId.fromString(currentModelId);
27469
- return {
27470
- mode,
27471
- settings: {
27472
- model: modelId.model,
27473
- reasoning_effort: modelId.effort,
27474
- developer_instructions: null
27475
- }
27476
- };
27477
- }
27478
-
27479
- // src/PathUtils.ts
27480
- import path5 from "node:path";
27481
- function isAbsolutePathLike(value) {
27482
- const trimmed = value.trim();
27483
- return path5.isAbsolute(trimmed) || isWindowsAbsolutePath(trimmed);
27484
- }
27485
- function arePathsEqual(left, right) {
27486
- return normalizePathForComparison(left) === normalizePathForComparison(right);
27487
- }
27488
- function arePathBasenamesEqual(left, right) {
27489
- const leftBase = path5.posix.basename(normalizePathForComparison(left));
27490
- const rightBase = path5.posix.basename(normalizePathForComparison(right));
27491
- if (shouldComparePathCaseInsensitive(left) || shouldComparePathCaseInsensitive(right)) {
27492
- return leftBase.toLowerCase() === rightBase.toLowerCase();
27493
- }
27494
- return leftBase === rightBase;
27495
- }
27496
- function normalizePathForComparison(value) {
27497
- const trimmed = value.trim();
27498
- if (trimmed.length === 0) {
27499
- return "";
27500
- }
27501
- if (isWindowsAbsolutePath(trimmed)) {
27502
- const normalized2 = path5.win32.normalize(trimmed).replace(/\\/g, "/");
27503
- return trimTrailingPathSeparators(normalized2).toLowerCase();
27504
- }
27505
- const pathForComparison = path5.isAbsolute(trimmed) ? trimmed : trimmed.replace(/\\/g, "/");
27506
- const normalized = path5.posix.normalize(pathForComparison);
27507
- return trimTrailingPathSeparators(normalized);
27508
- }
27509
- function isWindowsAbsolutePath(value) {
27510
- const portableValue = value.replace(/\\/g, "/");
27511
- return /^[A-Za-z]:\//.test(portableValue) || /^\/\/[^/]+\/[^/]+/.test(portableValue);
27512
- }
27513
- function shouldComparePathCaseInsensitive(value) {
27514
- return isWindowsAbsolutePath(value) || /^[A-Za-z]:/.test(value) || value.includes("\\");
27515
- }
27516
- function trimTrailingPathSeparators(value) {
27517
- let trimmed = value;
27518
- while (trimmed.endsWith("/") && !isRootPath(trimmed)) {
27519
- trimmed = trimmed.slice(0, -1);
27520
- }
27521
- return trimmed;
27522
- }
27523
- function isRootPath(value) {
27524
- return value === "/" || /^[A-Za-z]:\/$/.test(value) || /^\/\/[^/]+\/[^/]+\/$/.test(value);
27525
- }
27526
-
27527
- // src/AgentFileChangeReport.ts
27528
- import fs7 from "node:fs";
27529
- import path6 from "node:path";
27530
- var AGENT_FILE_CHANGE_REPORT_VERSION = 1;
27531
- var AGENT_FILE_CHANGE_REPORT_TIMEOUT_MS = 3e4;
27532
- var AGENT_FILE_CHANGE_REPORT_MAX_PATHS = 1024;
27533
- var AGENT_FILE_CHANGE_REPORT_MAX_PATH_LENGTH = 4096;
27534
- var AGENT_FILE_CHANGE_REPORT_MAX_TOTAL_BYTES = 256 * 1024;
27535
- var AGENT_FILE_CHANGE_REPORT_MAX_UNCERTAINTY_LENGTH = 2e3;
27536
- var AGENT_FILE_CHANGE_REPORT_OUTPUT_SCHEMA = {
27537
- type: "object",
27538
- additionalProperties: false,
27539
- required: ["paths", "complete", "uncertainty"],
27540
- properties: {
27541
- paths: {
27542
- type: "array",
27543
- items: { type: "string" }
27544
- },
27545
- complete: { type: "boolean" },
27546
- uncertainty: {
27547
- anyOf: [{
27548
- type: "string",
27549
- maxLength: AGENT_FILE_CHANGE_REPORT_MAX_UNCERTAINTY_LENGTH
27550
- }, {
27551
- type: "null"
27552
- }]
27553
- }
27554
- }
27555
- };
27556
- var AGENT_FILE_CHANGE_REPORT_DEVELOPER_INSTRUCTIONS = `You are running an internal, read-only file-change audit for the immediately preceding turn.
27557
- Do not modify files, run commands that can modify files, or ask the user questions.
27558
- 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.
27559
- Do not report files that were only read or inspected.
27560
- You may use read-only inspection when needed. If the list may be incomplete, set complete to false and briefly explain why in uncertainty.`;
27561
- function createAgentFileChangeReportPrompt(workspace) {
27562
- return `List the paths changed by the immediately preceding turn.
27563
- Return only the structured result required by the output schema.
27564
- Relative paths are resolved against the working directory.
27565
- Working directory: ${JSON.stringify(workspace.cwd)}
27566
- Additional allowed directories: ${JSON.stringify(workspace.additionalDirectories)}`;
27567
- }
27568
- function parseAgentFileChangeReportRequest(meta3) {
27569
- const jetbrains = asRecord2(meta3?.[JETBRAINS_META_KEY]);
27570
- const air = asRecord2(jetbrains?.[AIR_META_KEY]);
27571
- const request = asRecord2(air?.[AIR_AGENT_FILE_CHANGE_REPORT_REQUEST_KEY]);
27572
- if (request === null || !hasOnlyKeys(request, ["version", "requestId"]) || request["version"] !== AGENT_FILE_CHANGE_REPORT_VERSION) {
27573
- return null;
27574
- }
27575
- const requestId = request["requestId"];
27576
- if (typeof requestId !== "string" || !/^[A-Za-z0-9._:-]{1,128}$/.test(requestId)) {
27577
- return null;
27578
- }
27579
- return { version: AGENT_FILE_CHANGE_REPORT_VERSION, requestId };
27580
- }
27581
- function createReportedAgentFileChangeReport(requestId, turn, workspace) {
27582
- const modelReport = parseModelFileChangeReport(turn);
27583
- const normalized = normalizeModelFileChangeReport(modelReport, workspace);
27584
- return fitReportedAgentFileChangeReport({
27585
- version: AGENT_FILE_CHANGE_REPORT_VERSION,
27586
- requestId,
27587
- status: "reported",
27588
- ...normalized
27589
- });
27590
- }
27591
- function createUnavailableAgentFileChangeReport(requestId, reason) {
27592
- return {
27593
- version: AGENT_FILE_CHANGE_REPORT_VERSION,
27594
- requestId,
27595
- status: "unavailable",
27596
- reason
27597
- };
27598
- }
27599
- var AgentFileChangeReportError = class extends Error {
27600
- reason;
27601
- constructor(reason, message) {
27602
- super(message);
27603
- this.name = "AgentFileChangeReportError";
27604
- this.reason = reason;
27605
- }
27606
- };
27607
- function parseModelFileChangeReport(turn) {
27608
- switch (turn.status) {
27609
- case "interrupted":
27610
- throw new AgentFileChangeReportError("cancelled", "The audit turn was interrupted");
27611
- case "failed":
27612
- throw new AgentFileChangeReportError(
27613
- "providerError",
27614
- `The audit turn failed${turn.error?.message ? `: ${turn.error.message}` : ""}`
27615
- );
27616
- case "inProgress":
27617
- throw new AgentFileChangeReportError("notReported", "The audit turn did not complete");
27618
- case "completed":
27619
- break;
27620
- }
27621
- let text = null;
27622
- for (let index = turn.items.length - 1; index >= 0; index -= 1) {
27623
- const item = turn.items[index];
27624
- if (item?.type === "agentMessage") {
27625
- text = item.text;
27626
- break;
27627
- }
27628
- }
27629
- if (text === null) {
27630
- throw new AgentFileChangeReportError("notReported", "The audit turn returned no agent message");
27631
- }
27632
- let value;
27633
- try {
27634
- value = JSON.parse(text);
27635
- } catch {
27636
- throw new AgentFileChangeReportError("invalidOutput", "The audit turn returned invalid JSON");
27637
- }
27638
- const report = asRecord2(value);
27639
- if (report === null || !hasOnlyKeys(report, ["paths", "complete", "uncertainty"])) {
27640
- throw new AgentFileChangeReportError("invalidOutput", "The audit turn returned an invalid object");
27641
- }
27642
- const paths = report["paths"];
27643
- const complete = report["complete"];
27644
- const uncertainty = report["uncertainty"];
27645
- if (!Array.isArray(paths) || !paths.every((item) => typeof item === "string") || typeof complete !== "boolean" || uncertainty !== void 0 && uncertainty !== null && typeof uncertainty !== "string") {
27646
- throw new AgentFileChangeReportError("invalidOutput", "The audit turn returned invalid fields");
27647
- }
27648
- const normalizedUncertainty = typeof uncertainty === "string" ? uncertainty.trim() : void 0;
27649
- if (normalizedUncertainty !== void 0 && normalizedUncertainty.length > AGENT_FILE_CHANGE_REPORT_MAX_UNCERTAINTY_LENGTH) {
27650
- throw new AgentFileChangeReportError("invalidOutput", "The audit turn returned oversized uncertainty");
27873
+ type: "module",
27874
+ devDependencies: {
27875
+ "@types/node": "^26.1.0",
27876
+ esbuild: "^0.28.2",
27877
+ "mcp-hello-world": "^1.1.2",
27878
+ tsx: "^4.23.12",
27879
+ typescript: "^7.0.2",
27880
+ vitest: "^4.1.11"
27881
+ },
27882
+ dependencies: {
27883
+ "@agentclientprotocol/sdk": "^1.4.0",
27884
+ "@openai/codex": "^0.154.0",
27885
+ diff: "^9.0.0",
27886
+ open: "^11.0.1",
27887
+ "vscode-jsonrpc": "^9.0.1",
27888
+ zod: "^4.0.0"
27651
27889
  }
27890
+ };
27891
+
27892
+ // src/CollaborationModeConfig.ts
27893
+ var COLLABORATION_MODE_CONFIG_ID = "collaboration_mode";
27894
+ var DEFAULT_COLLABORATION_MODE = "default";
27895
+ var PLAN_COLLABORATION_MODE = "plan";
27896
+ function createCollaborationModeConfigOption(currentValue) {
27652
27897
  return {
27653
- paths,
27654
- complete,
27655
- ...normalizedUncertainty ? { uncertainty: normalizedUncertainty } : {}
27898
+ id: COLLABORATION_MODE_CONFIG_ID,
27899
+ name: "Collaboration mode",
27900
+ description: "How Codex collaborates for subsequent turns",
27901
+ category: "collaboration_mode",
27902
+ type: "select",
27903
+ currentValue,
27904
+ options: [
27905
+ { value: DEFAULT_COLLABORATION_MODE, name: "Default" },
27906
+ { value: PLAN_COLLABORATION_MODE, name: "Plan", description: "Plan before making changes" }
27907
+ ]
27656
27908
  };
27657
27909
  }
27658
- function fitReportedAgentFileChangeReport(report) {
27659
- const paths = [...report.paths];
27660
- let truncated = report.truncated;
27661
- let fitted = report;
27662
- while (Buffer.byteLength(JSON.stringify(fitted), "utf8") > AGENT_FILE_CHANGE_REPORT_MAX_TOTAL_BYTES) {
27663
- if (paths.length === 0) {
27664
- throw new AgentFileChangeReportError("invalidOutput", "The audit report exceeds the wire limit");
27665
- }
27666
- paths.pop();
27667
- truncated = true;
27668
- fitted = {
27669
- ...report,
27670
- paths: [...paths],
27671
- declaredComplete: false,
27672
- truncated
27673
- };
27674
- }
27675
- return fitted;
27910
+ function parseCollaborationMode(value) {
27911
+ if (value === DEFAULT_COLLABORATION_MODE) return DEFAULT_COLLABORATION_MODE;
27912
+ if (value === PLAN_COLLABORATION_MODE) return PLAN_COLLABORATION_MODE;
27913
+ return null;
27676
27914
  }
27677
- function normalizeModelFileChangeReport(report, workspace) {
27678
- const cwd = normalizeWorkspaceRoot(workspace.cwd);
27679
- if (cwd === null) {
27680
- throw new AgentFileChangeReportError("providerError", "The session working directory is not absolute");
27681
- }
27682
- const roots = [cwd, ...workspace.additionalDirectories.flatMap((directory) => {
27683
- const root = normalizeWorkspaceRoot(directory);
27684
- return root === null || root.flavor !== cwd.flavor ? [] : [root];
27685
- })];
27686
- const paths = [];
27687
- const seen = /* @__PURE__ */ new Set();
27688
- let totalBytes = 0;
27689
- let truncated = false;
27690
- for (const reportedPath of report.paths) {
27691
- if (reportedPath.length > AGENT_FILE_CHANGE_REPORT_MAX_PATH_LENGTH) {
27692
- truncated = true;
27693
- continue;
27694
- }
27695
- const normalized = normalizeReportedPath(reportedPath, cwd, roots);
27696
- if (normalized === null || normalized.value.length > AGENT_FILE_CHANGE_REPORT_MAX_PATH_LENGTH) {
27697
- truncated = true;
27698
- continue;
27699
- }
27700
- const key = normalized.flavor === "windows" ? normalized.value.toLowerCase() : normalized.value;
27701
- if (seen.has(key)) {
27702
- continue;
27703
- }
27704
- const bytes = Buffer.byteLength(normalized.value, "utf8");
27705
- if (paths.length >= AGENT_FILE_CHANGE_REPORT_MAX_PATHS || totalBytes + bytes > AGENT_FILE_CHANGE_REPORT_MAX_TOTAL_BYTES) {
27706
- truncated = true;
27707
- continue;
27708
- }
27709
- seen.add(key);
27710
- paths.push(normalized.value);
27711
- totalBytes += bytes;
27712
- }
27915
+ function createCodexCollaborationMode(mode, currentModelId) {
27916
+ const modelId = ModelId.fromString(currentModelId);
27713
27917
  return {
27714
- paths,
27715
- declaredComplete: report.complete && !truncated,
27716
- truncated,
27717
- ...report.uncertainty ? { uncertainty: report.uncertainty } : {}
27918
+ mode,
27919
+ settings: {
27920
+ model: modelId.model,
27921
+ reasoning_effort: modelId.effort,
27922
+ developer_instructions: null
27923
+ }
27718
27924
  };
27719
27925
  }
27720
- function normalizeWorkspaceRoot(value) {
27721
- const trimmed = value.trim();
27722
- if (!isValidPathText(trimmed)) {
27723
- return null;
27724
- }
27725
- if (isWindowsAbsolutePath2(trimmed)) {
27726
- return canonicalizeWorkspaceRoot({
27727
- value: path6.win32.normalize(trimmed.replace(/\//g, "\\")),
27728
- flavor: "windows"
27729
- });
27730
- }
27731
- if (path6.posix.isAbsolute(trimmed)) {
27732
- return canonicalizeWorkspaceRoot({
27733
- value: path6.posix.normalize(trimmed.replace(/\\/g, "/")),
27734
- flavor: "posix"
27735
- });
27736
- }
27737
- return null;
27738
- }
27739
- function normalizeReportedPath(value, cwd, roots) {
27926
+
27927
+ // src/PathUtils.ts
27928
+ import path6 from "node:path";
27929
+ function isAbsolutePathLike(value) {
27740
27930
  const trimmed = value.trim();
27741
- 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)) {
27742
- return null;
27743
- }
27744
- let candidate;
27745
- if (isWindowsAbsolutePath2(trimmed)) {
27746
- candidate = { value: path6.win32.normalize(trimmed.replace(/\//g, "\\")), flavor: "windows" };
27747
- } else if (path6.posix.isAbsolute(trimmed)) {
27748
- candidate = { value: path6.posix.normalize(trimmed.replace(/\\/g, "/")), flavor: "posix" };
27749
- } else if (cwd.flavor === "windows") {
27750
- candidate = {
27751
- value: path6.win32.resolve(cwd.value, trimmed.replace(/\//g, "\\")),
27752
- flavor: "windows"
27753
- };
27754
- } else {
27755
- candidate = {
27756
- value: path6.posix.resolve(cwd.value, trimmed.replace(/\\/g, "/")),
27757
- flavor: "posix"
27758
- };
27759
- }
27760
- candidate = canonicalizeReportedPath(candidate);
27761
- return roots.some((root) => pathIsStrictlyInside(root, candidate)) ? candidate : null;
27931
+ return path6.isAbsolute(trimmed) || isWindowsAbsolutePath2(trimmed);
27762
27932
  }
27763
- function canonicalizeWorkspaceRoot(root) {
27764
- if (!isNativePathFlavor(root.flavor)) {
27765
- return root;
27766
- }
27767
- return { ...root, value: canonicalizeFromExistingAncestor(root.value) };
27933
+ function arePathsEqual(left, right) {
27934
+ return normalizePathForComparison(left) === normalizePathForComparison(right);
27768
27935
  }
27769
- function canonicalizeReportedPath(candidate) {
27770
- if (!isNativePathFlavor(candidate.flavor)) {
27771
- return candidate;
27936
+ function arePathBasenamesEqual(left, right) {
27937
+ const leftBase = path6.posix.basename(normalizePathForComparison(left));
27938
+ const rightBase = path6.posix.basename(normalizePathForComparison(right));
27939
+ if (shouldComparePathCaseInsensitive(left) || shouldComparePathCaseInsensitive(right)) {
27940
+ return leftBase.toLowerCase() === rightBase.toLowerCase();
27772
27941
  }
27773
- const parent = canonicalizeFromExistingAncestor(path6.dirname(candidate.value));
27774
- return { ...candidate, value: path6.resolve(parent, path6.basename(candidate.value)) };
27942
+ return leftBase === rightBase;
27775
27943
  }
27776
- function canonicalizeFromExistingAncestor(value) {
27777
- const original = path6.resolve(value);
27778
- let current = original;
27779
- const missingSegments = [];
27780
- while (true) {
27781
- try {
27782
- const canonical = fs7.realpathSync.native(current);
27783
- return path6.resolve(canonical, ...missingSegments.reverse());
27784
- } catch (error51) {
27785
- if (!isMissingPathError(error51)) return original;
27786
- }
27787
- const parent = path6.dirname(current);
27788
- if (parent === current) return original;
27789
- missingSegments.push(path6.basename(current));
27790
- current = parent;
27944
+ function normalizePathForComparison(value) {
27945
+ const trimmed = value.trim();
27946
+ if (trimmed.length === 0) {
27947
+ return "";
27791
27948
  }
27792
- }
27793
- function isMissingPathError(error51) {
27794
- if (typeof error51 !== "object" || error51 === null || !("code" in error51)) return false;
27795
- return error51.code === "ENOENT" || error51.code === "ENOTDIR";
27796
- }
27797
- function isNativePathFlavor(flavor) {
27798
- return process.platform === "win32" ? flavor === "windows" : flavor === "posix";
27799
- }
27800
- function pathIsStrictlyInside(root, candidate) {
27801
- if (root.flavor !== candidate.flavor) {
27802
- return false;
27949
+ if (isWindowsAbsolutePath2(trimmed)) {
27950
+ const normalized2 = path6.win32.normalize(trimmed).replace(/\\/g, "/");
27951
+ return trimTrailingPathSeparators(normalized2).toLowerCase();
27803
27952
  }
27804
- const pathImplementation = root.flavor === "windows" ? path6.win32 : path6.posix;
27805
- const relative = pathImplementation.relative(root.value, candidate.value);
27806
- return relative.length > 0 && !pathImplementation.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${pathImplementation.sep}`);
27953
+ const pathForComparison = path6.isAbsolute(trimmed) ? trimmed : trimmed.replace(/\\/g, "/");
27954
+ const normalized = path6.posix.normalize(pathForComparison);
27955
+ return trimTrailingPathSeparators(normalized);
27807
27956
  }
27808
27957
  function isWindowsAbsolutePath2(value) {
27809
- const portable = value.replace(/\\/g, "/");
27810
- return /^[A-Za-z]:\//.test(portable) || /^\/\/[^/]+\/[^/]+(?:\/|$)/.test(portable);
27958
+ const portableValue = value.replace(/\\/g, "/");
27959
+ return /^[A-Za-z]:\//.test(portableValue) || /^\/\/[^/]+\/[^/]+/.test(portableValue);
27811
27960
  }
27812
- function isValidPathText(value) {
27813
- return value.length > 0 && !/[\u0000-\u001F\u007F-\u009F]/.test(value);
27961
+ function shouldComparePathCaseInsensitive(value) {
27962
+ return isWindowsAbsolutePath2(value) || /^[A-Za-z]:/.test(value) || value.includes("\\");
27814
27963
  }
27815
- function asRecord2(value) {
27816
- return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
27964
+ function trimTrailingPathSeparators(value) {
27965
+ let trimmed = value;
27966
+ while (trimmed.endsWith("/") && !isRootPath(trimmed)) {
27967
+ trimmed = trimmed.slice(0, -1);
27968
+ }
27969
+ return trimmed;
27817
27970
  }
27818
- function hasOnlyKeys(value, allowed) {
27819
- const allowedKeys = new Set(allowed);
27820
- return Object.keys(value).every((key) => allowedKeys.has(key));
27971
+ function isRootPath(value) {
27972
+ return value === "/" || /^[A-Za-z]:\/$/.test(value) || /^\/\/[^/]+\/[^/]+\/$/.test(value);
27821
27973
  }
27822
27974
 
27823
27975
  // src/subagents/CodexSubagentSubscriptions.ts
@@ -28621,7 +28773,7 @@ var CodexAcpClient = class {
28621
28773
  baseUrl: activeProvider.baseUrl
28622
28774
  });
28623
28775
  const mergedConfig = {
28624
- ...mergeGatewayConfig(this.config, this.gatewayConfig),
28776
+ ...forceGitRootTurnDiffPaths(mergeGatewayConfig(this.config, this.gatewayConfig)),
28625
28777
  projects: Object.fromEntries(sessionRoots2.map((root) => [root, {
28626
28778
  trust_level: "trusted"
28627
28779
  }]))
@@ -28788,101 +28940,6 @@ var CodexAcpClient = class {
28788
28940
  serviceTier
28789
28941
  }, onTurnStarted);
28790
28942
  }
28791
- async runAgentFileChangeReport(params) {
28792
- if (params.signal?.aborted) {
28793
- return createUnavailableAgentFileChangeReport(params.requestId, "cancelled");
28794
- }
28795
- const budget = new AgentFileChangeReportBudget(params.signal);
28796
- let forkThreadId = null;
28797
- let auditTurnId = null;
28798
- let auditTurnCompleted = false;
28799
- let lateStopReason = null;
28800
- try {
28801
- const forkPromise = this.codexClient.threadFork({
28802
- excludeTurns: true,
28803
- threadId: params.sessionId,
28804
- lastTurnId: params.turnId,
28805
- cwd: params.workspace.cwd,
28806
- approvalPolicy: "never",
28807
- sandbox: "read-only",
28808
- developerInstructions: AGENT_FILE_CHANGE_REPORT_DEVELOPER_INSTRUCTIONS,
28809
- ephemeral: true
28810
- });
28811
- void forkPromise.then((fork2) => {
28812
- if (lateStopReason !== null && forkThreadId === null) {
28813
- void this.unsubscribeAgentFileChangeReportThread(fork2.thread.id, budget);
28814
- }
28815
- }, () => {
28816
- });
28817
- const fork = await budget.wait(forkPromise);
28818
- forkThreadId = fork.thread.id;
28819
- const turnPromise = this.codexClient.runTurn({
28820
- threadId: forkThreadId,
28821
- input: [{
28822
- type: "text",
28823
- text: createAgentFileChangeReportPrompt(params.workspace),
28824
- text_elements: []
28825
- }],
28826
- cwd: params.workspace.cwd,
28827
- approvalPolicy: "never",
28828
- sandboxPolicy: { type: "readOnly", networkAccess: false },
28829
- summary: "none",
28830
- outputSchema: AGENT_FILE_CHANGE_REPORT_OUTPUT_SCHEMA
28831
- }, (turnId) => {
28832
- auditTurnId = turnId;
28833
- if (lateStopReason !== null && forkThreadId !== null) {
28834
- void this.interruptAgentFileChangeReport(forkThreadId, turnId, lateStopReason, budget);
28835
- }
28836
- });
28837
- const outcome = await budget.wait(turnPromise);
28838
- auditTurnCompleted = true;
28839
- return createReportedAgentFileChangeReport(
28840
- params.requestId,
28841
- outcome.turn,
28842
- params.workspace
28843
- );
28844
- } catch (error51) {
28845
- if (error51 instanceof AgentFileChangeReportBudgetError) {
28846
- lateStopReason = error51.reason;
28847
- if (!auditTurnCompleted && forkThreadId !== null && auditTurnId !== null) {
28848
- await this.interruptAgentFileChangeReport(
28849
- forkThreadId,
28850
- auditTurnId,
28851
- error51.reason,
28852
- budget
28853
- );
28854
- }
28855
- return createUnavailableAgentFileChangeReport(params.requestId, error51.reason);
28856
- }
28857
- if (error51 instanceof AgentFileChangeReportError) {
28858
- logger.error("Agent file-change report unavailable", error51);
28859
- return createUnavailableAgentFileChangeReport(params.requestId, error51.reason);
28860
- }
28861
- logger.error("Agent file-change report failed", error51);
28862
- return createUnavailableAgentFileChangeReport(params.requestId, "providerError");
28863
- } finally {
28864
- if (forkThreadId !== null) {
28865
- await this.unsubscribeAgentFileChangeReportThread(forkThreadId, budget);
28866
- }
28867
- }
28868
- }
28869
- async interruptAgentFileChangeReport(threadId, turnId, reason, budget) {
28870
- this.codexClient.markTurnStale(threadId, turnId);
28871
- try {
28872
- await budget.wait(this.codexClient.turnInterrupt({ threadId, turnId }));
28873
- } catch (error51) {
28874
- logger.error(`Failed to interrupt ${reason} agent file-change report`, error51);
28875
- } finally {
28876
- this.codexClient.resolveTurnInterrupted(threadId, turnId);
28877
- }
28878
- }
28879
- async unsubscribeAgentFileChangeReportThread(threadId, budget) {
28880
- try {
28881
- await budget.wait(this.codexClient.threadUnsubscribe({ threadId }));
28882
- } catch (error51) {
28883
- logger.error("Failed to unsubscribe the agent file-change report thread", error51);
28884
- }
28885
- }
28886
28943
  async setAgentMode(sessionId, agentMode) {
28887
28944
  const config2 = this.permissionProfileConfig;
28888
28945
  if (!config2) return;
@@ -29048,58 +29105,6 @@ var CodexAcpClient = class {
29048
29105
  };
29049
29106
  }
29050
29107
  };
29051
- var AgentFileChangeReportBudgetError = class extends Error {
29052
- constructor(reason) {
29053
- super(`Agent file-change report ${reason}`);
29054
- this.reason = reason;
29055
- this.name = "AgentFileChangeReportBudgetError";
29056
- }
29057
- reason;
29058
- };
29059
- var AgentFileChangeReportBudget = class {
29060
- constructor(signal) {
29061
- this.signal = signal;
29062
- }
29063
- signal;
29064
- deadline = Date.now() + AGENT_FILE_CHANGE_REPORT_TIMEOUT_MS;
29065
- async wait(operation) {
29066
- void operation.catch(() => {
29067
- });
29068
- const immediateReason = this.stopReason();
29069
- if (immediateReason !== null) {
29070
- throw new AgentFileChangeReportBudgetError(immediateReason);
29071
- }
29072
- return await new Promise((resolve, reject) => {
29073
- let settled = false;
29074
- const finish = (action) => {
29075
- if (settled) return;
29076
- settled = true;
29077
- clearTimeout(timeout);
29078
- this.signal?.removeEventListener("abort", onAbort);
29079
- action();
29080
- };
29081
- const onAbort = () => finish(() => reject(new AgentFileChangeReportBudgetError("cancelled")));
29082
- const timeout = setTimeout(
29083
- () => finish(() => reject(new AgentFileChangeReportBudgetError("timeout"))),
29084
- Math.max(1, this.deadline - Date.now())
29085
- );
29086
- timeout.unref();
29087
- this.signal?.addEventListener("abort", onAbort, { once: true });
29088
- if (this.signal?.aborted) {
29089
- onAbort();
29090
- }
29091
- void operation.then(
29092
- (value) => finish(() => resolve(value)),
29093
- (error51) => finish(() => reject(error51))
29094
- );
29095
- });
29096
- }
29097
- stopReason() {
29098
- if (this.signal?.aborted) return "cancelled";
29099
- if (Date.now() >= this.deadline) return "timeout";
29100
- return null;
29101
- }
29102
- };
29103
29108
  function buildPromptItems(prompt) {
29104
29109
  return prompt.map((block) => {
29105
29110
  switch (block.type) {
@@ -29214,6 +29219,16 @@ function mergeSandboxWorkspaceWriteRoots(config2, roots) {
29214
29219
  }
29215
29220
  };
29216
29221
  }
29222
+ function forceGitRootTurnDiffPaths(config2) {
29223
+ const features = isJsonObject(config2["features"]) ? config2["features"] : {};
29224
+ return {
29225
+ ...config2,
29226
+ features: {
29227
+ ...features,
29228
+ cwd_relative_turn_diffs: false
29229
+ }
29230
+ };
29231
+ }
29217
29232
  function addAdditionalDirectoriesToSandboxPolicy(sandboxPolicy, additionalDirectories) {
29218
29233
  if (additionalDirectories.length === 0 || sandboxPolicy.type !== "workspaceWrite") {
29219
29234
  return sandboxPolicy;
@@ -31011,6 +31026,7 @@ function createFunctionCallUpdate(item) {
31011
31026
  if (!toolCallId || !name) {
31012
31027
  return null;
31013
31028
  }
31029
+ const toolName = functionToolName(name, stringValue(item["namespace"]));
31014
31030
  const isExecCommand = name === "exec_command";
31015
31031
  const args = parseFunctionArguments(item["arguments"]);
31016
31032
  const command = isExecCommand ? commandFromFunctionArguments(args) : null;
@@ -31018,7 +31034,10 @@ function createFunctionCallUpdate(item) {
31018
31034
  const commandAction = command ? inferCommandAction(command, cwd) : null;
31019
31035
  if (commandAction) {
31020
31036
  return {
31021
- update: createCommandActionEvent(toolCallId, "inProgress", cwd, commandAction),
31037
+ update: {
31038
+ ...createCommandActionEvent(toolCallId, "inProgress", cwd, commandAction),
31039
+ name: toolName
31040
+ },
31022
31041
  usesTerminal: false,
31023
31042
  isExecCommand
31024
31043
  };
@@ -31026,6 +31045,7 @@ function createFunctionCallUpdate(item) {
31026
31045
  const update = {
31027
31046
  sessionUpdate: "tool_call",
31028
31047
  toolCallId,
31048
+ name: toolName,
31029
31049
  kind: toolKindForFunctionCall(name),
31030
31050
  title: titleForFunctionCall(name, args),
31031
31051
  status: "in_progress",
@@ -33979,24 +33999,19 @@ Check ${configPath} and project .codex directories, especially their config.toml
33979
33999
  title
33980
34000
  });
33981
34001
  }
33982
- async publishAgentFileChangeReport(sessionState, turnId, request, unavailableReason, signal) {
34002
+ async publishAgentFileChangeReport(sessionState, turnId, request, unavailableReason, turnDiff, workspace) {
33983
34003
  let report;
33984
34004
  try {
33985
- report = turnId === null ? createUnavailableAgentFileChangeReport(request.requestId, unavailableReason) : await this.codexAcpClient.runAgentFileChangeReport({
33986
- sessionId: sessionState.sessionId,
33987
- turnId,
33988
- // The client owns request-id correlation and duplicate suppression. The wrapper
33989
- // stays stateless so a retried ACP prompt still receives a terminal report.
33990
- requestId: request.requestId,
33991
- workspace: {
33992
- cwd: sessionState.cwd,
33993
- additionalDirectories: sessionState.additionalDirectories
33994
- },
33995
- signal
33996
- });
34005
+ report = turnId === null ? createUnavailableAgentFileChangeReport(request.requestId, unavailableReason) : createReportedAgentFileChangeReport(request.requestId, turnDiff, workspace);
33997
34006
  } catch (error51) {
33998
- logger.error("Agent file-change report failed unexpectedly", error51);
33999
- report = createUnavailableAgentFileChangeReport(request.requestId, "providerError");
34007
+ logger.error(
34008
+ error51 instanceof AgentFileChangeReportError ? "Agent file-change report unavailable" : "Agent file-change report failed unexpectedly",
34009
+ error51
34010
+ );
34011
+ report = createUnavailableAgentFileChangeReport(
34012
+ request.requestId,
34013
+ error51 instanceof AgentFileChangeReportError ? error51.reason : "providerError"
34014
+ );
34000
34015
  }
34001
34016
  try {
34002
34017
  const session = new ACPSessionConnection(this.connection, sessionState.sessionId);
@@ -34466,6 +34481,7 @@ Check ${configPath} and project .codex directories, especially their config.toml
34466
34481
  });
34467
34482
  const sessionState = this.getSessionState(params.sessionId);
34468
34483
  const agentFileChangeReportRequest = clientSupportsAgentFileChangeReports(this.clientCapabilities) ? parseAgentFileChangeReportRequest(params._meta) : null;
34484
+ const agentFileChangeWorkspace = agentFileChangeReportRequest === null ? null : captureAgentFileChangeWorkspace(sessionState.cwd, sessionState.additionalDirectories);
34469
34485
  let agentFileChangeReportTurnId = null;
34470
34486
  let agentFileChangeReportUnavailableReason = "providerError";
34471
34487
  let promptWasCancelled = false;
@@ -34505,7 +34521,8 @@ Check ${configPath} and project .codex directories, especially their config.toml
34505
34521
  clientSupportsTypedSessionFailures(this.clientCapabilities),
34506
34522
  this.sessionFailureEpoch,
34507
34523
  sessionState.subagents,
34508
- (accountUpdated) => this.handleAccountUpdated(accountUpdated)
34524
+ (accountUpdated) => this.handleAccountUpdated(accountUpdated),
34525
+ agentFileChangeReportRequest !== null
34509
34526
  );
34510
34527
  eventHandler = promptEventHandler;
34511
34528
  const permissionLifecycle = this.permissionLifecycleContext(sessionState);
@@ -34876,13 +34893,21 @@ Check ${configPath} and project .codex directories, especially their config.toml
34876
34893
  } catch (error51) {
34877
34894
  logger.error("Failed to publish terminal subagent state during prompt cleanup", error51);
34878
34895
  }
34879
- if (agentFileChangeReportRequest !== null) {
34896
+ if (agentFileChangeReportRequest !== null && agentFileChangeWorkspace !== null) {
34897
+ if (promptWasCancelled || activePrompt.signal.aborted || this.sessionIsClosing(params.sessionId)) {
34898
+ agentFileChangeReportTurnId = null;
34899
+ agentFileChangeReportUnavailableReason = "cancelled";
34900
+ } else if (agentFileChangeReportTurnId !== null && eventHandler?.isTurnDiffOversized(agentFileChangeReportTurnId)) {
34901
+ agentFileChangeReportTurnId = null;
34902
+ agentFileChangeReportUnavailableReason = "invalidOutput";
34903
+ }
34880
34904
  await this.publishAgentFileChangeReport(
34881
34905
  sessionState,
34882
34906
  agentFileChangeReportTurnId,
34883
34907
  agentFileChangeReportRequest,
34884
34908
  agentFileChangeReportUnavailableReason,
34885
- activePrompt.signal
34909
+ agentFileChangeReportTurnId === null || eventHandler === null ? "" : eventHandler.getTurnDiff(agentFileChangeReportTurnId),
34910
+ agentFileChangeWorkspace
34886
34911
  );
34887
34912
  }
34888
34913
  logger.log("Prompt completed", { sessionId: params.sessionId });