@leo000001/codex-mcp 0.2.1 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -34,6 +34,7 @@ var Methods = {
34
34
  THREAD_START: "thread/start",
35
35
  THREAD_RESUME: "thread/resume",
36
36
  THREAD_FORK: "thread/fork",
37
+ THREAD_BACKGROUND_TERMINALS_CLEAN: "thread/backgroundTerminals/clean",
37
38
  TURN_START: "turn/start",
38
39
  TURN_INTERRUPT: "turn/interrupt",
39
40
  TURN_STEER: "turn/steer",
@@ -48,19 +49,31 @@ var Methods = {
48
49
  // Server → Client notifications
49
50
  ERROR: "error",
50
51
  THREAD_STARTED: "thread/started",
52
+ THREAD_ARCHIVED: "thread/archived",
53
+ THREAD_UNARCHIVED: "thread/unarchived",
54
+ THREAD_NAME_UPDATED: "thread/name/updated",
55
+ THREAD_TOKEN_USAGE_UPDATED: "thread/tokenUsage/updated",
51
56
  TURN_STARTED: "turn/started",
52
57
  TURN_COMPLETED: "turn/completed",
53
58
  TURN_DIFF_UPDATED: "turn/diff/updated",
54
59
  TURN_PLAN_UPDATED: "turn/plan/updated",
55
60
  ITEM_STARTED: "item/started",
56
61
  ITEM_COMPLETED: "item/completed",
62
+ RAW_RESPONSE_ITEM_COMPLETED: "rawResponseItem/completed",
57
63
  AGENT_MESSAGE_DELTA: "item/agentMessage/delta",
58
64
  COMMAND_OUTPUT_DELTA: "item/commandExecution/outputDelta",
65
+ COMMAND_TERMINAL_INTERACTION: "item/commandExecution/terminalInteraction",
59
66
  FILE_CHANGE_OUTPUT_DELTA: "item/fileChange/outputDelta",
60
67
  REASONING_TEXT_DELTA: "item/reasoning/textDelta",
61
68
  REASONING_SUMMARY_DELTA: "item/reasoning/summaryTextDelta",
69
+ REASONING_SUMMARY_PART_ADDED: "item/reasoning/summaryPartAdded",
62
70
  PLAN_DELTA: "item/plan/delta",
63
71
  MCP_TOOL_PROGRESS: "item/mcpToolCall/progress",
72
+ MODEL_REROUTED: "model/rerouted",
73
+ FUZZY_FILE_SEARCH_SESSION_UPDATED: "fuzzyFileSearch/sessionUpdated",
74
+ FUZZY_FILE_SEARCH_SESSION_COMPLETED: "fuzzyFileSearch/sessionCompleted",
75
+ WINDOWS_WORLD_WRITABLE_WARNING: "windows/worldWritableWarning",
76
+ ACCOUNT_LOGIN_COMPLETED: "account/login/completed",
64
77
  SESSION_CONFIGURED: "sessionConfigured"
65
78
  };
66
79
 
@@ -164,7 +177,14 @@ var SANDBOX_MODES = ["read-only", "workspace-write", "danger-full-access"];
164
177
  var PERSONALITIES = ["none", "friendly", "pragmatic"];
165
178
  var EFFORT_LEVELS = ["none", "minimal", "low", "medium", "high", "xhigh"];
166
179
  var SUMMARY_MODES = ["auto", "concise", "detailed", "none"];
167
- var SESSION_ACTIONS = ["list", "get", "cancel", "interrupt", "fork"];
180
+ var SESSION_ACTIONS = [
181
+ "list",
182
+ "get",
183
+ "cancel",
184
+ "interrupt",
185
+ "fork",
186
+ "clean_background_terminals"
187
+ ];
168
188
  var CHECK_ACTIONS = ["poll", "respond_permission", "respond_user_input"];
169
189
  var RESPONSE_MODES = ["minimal", "delta_compact", "full"];
170
190
  var COMMAND_DECISIONS = [
@@ -213,7 +233,7 @@ var DEFAULT_TERMINAL_CLEANUP_MS = 5 * 60 * 1e3;
213
233
  var CLEANUP_INTERVAL_MS = 6e4;
214
234
 
215
235
  // src/app-server/client.ts
216
- var CLIENT_VERSION = true ? "0.2.1" : "0.0.0-dev";
236
+ var CLIENT_VERSION = true ? "2.0.2" : "0.0.0-dev";
217
237
  var DEFAULT_REQUEST_TIMEOUT = 3e4;
218
238
  var STARTUP_REQUEST_TIMEOUT = 9e4;
219
239
  var MAX_WRITE_QUEUE_BYTES = 5 * 1024 * 1024;
@@ -330,6 +350,9 @@ var AppServerClient = class extends EventEmitter {
330
350
  async threadResume(params) {
331
351
  return this.request(Methods.THREAD_RESUME, params);
332
352
  }
353
+ async threadBackgroundTerminalsClean(params) {
354
+ return this.request(Methods.THREAD_BACKGROUND_TERMINALS_CLEAN, params);
355
+ }
333
356
  async turnStart(params, timeout = STARTUP_REQUEST_TIMEOUT) {
334
357
  return this.request(Methods.TURN_START, params, timeout);
335
358
  }
@@ -659,6 +682,9 @@ var COALESCED_PROGRESS_DELTA_METHODS = /* @__PURE__ */ new Set([
659
682
  Methods.REASONING_SUMMARY_DELTA
660
683
  ]);
661
684
  var MAX_COALESCED_DELTA_CHARS = 16384;
685
+ var AUTH_REFRESH_UNSUPPORTED_CODE = -32e3;
686
+ var AUTH_REFRESH_UNSUPPORTED_MESSAGE = "account/chatgptAuthTokens/refresh unsupported: codex-mcp does not manage external ChatGPT auth tokens";
687
+ var AUTH_REFRESH_TERMINAL_MESSAGE = "account/chatgptAuthTokens/refresh unsupported: session is terminal";
662
688
  var NOISE_FILTER_ENABLED = process.env.CODEX_MCP_DISABLE_NOISE_FILTER !== "1";
663
689
  var WINDOWS_TERMINAL_INTEGRATION_PREFIX = `${String.fromCharCode(27)}]633;`;
664
690
  var SHELL_NOISE_LINE_PATTERNS = [
@@ -959,6 +985,32 @@ var SessionManager = class {
959
985
  turnId: session.activeTurnId
960
986
  });
961
987
  }
988
+ async cleanBackgroundTerminals(sessionId) {
989
+ const session = this.getSessionOrThrow(sessionId);
990
+ const client = this.getClientOrThrow(sessionId);
991
+ if (session.status === "cancelled") {
992
+ throw new Error(
993
+ `Error [${"CANCELLED" /* CANCELLED */}]: Session '${sessionId}' has been cancelled and cannot be cleaned`
994
+ );
995
+ }
996
+ if (!session.threadId) {
997
+ throw new Error(
998
+ `Error [${"INTERNAL" /* INTERNAL */}]: Session '${sessionId}' has no threadId, cannot clean background terminals`
999
+ );
1000
+ }
1001
+ await client.threadBackgroundTerminalsClean({ threadId: session.threadId });
1002
+ session.lastActiveAt = (/* @__PURE__ */ new Date()).toISOString();
1003
+ pushEvent(
1004
+ session.eventBuffer,
1005
+ "progress",
1006
+ {
1007
+ method: Methods.THREAD_BACKGROUND_TERMINALS_CLEAN,
1008
+ threadId: session.threadId,
1009
+ status: "requested"
1010
+ },
1011
+ true
1012
+ );
1013
+ }
962
1014
  async forkSession(sessionId) {
963
1015
  const session = this.getSessionOrThrow(sessionId);
964
1016
  const originalClient = this.getClientOrThrow(sessionId);
@@ -1064,6 +1116,9 @@ var SessionManager = class {
1064
1116
  params: req.params,
1065
1117
  itemId: req.itemId,
1066
1118
  reason: req.reason,
1119
+ approvalId: req.approvalId,
1120
+ commandActions: req.commandActions,
1121
+ proposedExecpolicyAmendment: req.proposedExecpolicyAmendment,
1067
1122
  createdAt: req.createdAt
1068
1123
  });
1069
1124
  }
@@ -1105,6 +1160,9 @@ var SessionManager = class {
1105
1160
  while (result.actions.length > 1 && payloadByteSize(result) > normalizedMaxBytes) {
1106
1161
  result.actions.pop();
1107
1162
  }
1163
+ if (payloadByteSize(result) > normalizedMaxBytes) {
1164
+ result.actions = compactActionsToMinimum(result.actions);
1165
+ }
1108
1166
  truncatedFields.push("actions");
1109
1167
  }
1110
1168
  if (typeof result.actions !== "undefined" && payloadByteSize(result) > normalizedMaxBytes) {
@@ -1167,9 +1225,9 @@ var SessionManager = class {
1167
1225
  `Error [${"INVALID_ARGUMENT" /* INVALID_ARGUMENT */}]: Invalid command decision '${decision}'`
1168
1226
  );
1169
1227
  }
1170
- if (decision === "acceptWithExecpolicyAmendment" && (!extra?.execpolicyAmendment || extra.execpolicyAmendment.length === 0)) {
1228
+ if (decision === "acceptWithExecpolicyAmendment" && (!extra?.execpolicy_amendment || extra.execpolicy_amendment.length === 0)) {
1171
1229
  throw new Error(
1172
- `Error [${"INVALID_ARGUMENT" /* INVALID_ARGUMENT */}]: execpolicyAmendment required for acceptWithExecpolicyAmendment`
1230
+ `Error [${"INVALID_ARGUMENT" /* INVALID_ARGUMENT */}]: execpolicy_amendment required for acceptWithExecpolicyAmendment`
1173
1231
  );
1174
1232
  }
1175
1233
  } else if (req.kind === "fileChange") {
@@ -1185,7 +1243,7 @@ var SessionManager = class {
1185
1243
  }
1186
1244
  let response;
1187
1245
  if (req.kind === "command") {
1188
- response = buildCommandApprovalResponse(decision, extra?.execpolicyAmendment);
1246
+ response = buildCommandApprovalResponse(decision, extra?.execpolicy_amendment);
1189
1247
  } else if (req.kind === "fileChange") {
1190
1248
  response = { decision };
1191
1249
  }
@@ -1204,6 +1262,7 @@ var SessionManager = class {
1204
1262
  {
1205
1263
  requestId,
1206
1264
  kind: req.kind,
1265
+ approvalId: req.approvalId,
1207
1266
  decision,
1208
1267
  denyMessage: extra?.denyMessage
1209
1268
  },
@@ -1237,6 +1296,7 @@ var SessionManager = class {
1237
1296
  {
1238
1297
  requestId,
1239
1298
  kind: "user_input",
1299
+ approvalId: req.approvalId,
1240
1300
  answers
1241
1301
  },
1242
1302
  true
@@ -1289,15 +1349,44 @@ var SessionManager = class {
1289
1349
  session.lastActiveAt = (/* @__PURE__ */ new Date()).toISOString();
1290
1350
  const p = params;
1291
1351
  switch (method) {
1352
+ case Methods.THREAD_STARTED: {
1353
+ const thread = isRecord(p.thread) ? p.thread : void 0;
1354
+ pushEvent(session.eventBuffer, "progress", {
1355
+ method,
1356
+ ...p,
1357
+ threadId: normalizeOptionalString(p.threadId) ?? normalizeOptionalString(thread?.id),
1358
+ status: normalizeOptionalString(thread?.status)
1359
+ });
1360
+ break;
1361
+ }
1362
+ case Methods.THREAD_ARCHIVED:
1363
+ case Methods.THREAD_UNARCHIVED:
1364
+ case Methods.THREAD_NAME_UPDATED:
1365
+ case Methods.THREAD_TOKEN_USAGE_UPDATED:
1366
+ case Methods.FUZZY_FILE_SEARCH_SESSION_UPDATED:
1367
+ case Methods.FUZZY_FILE_SEARCH_SESSION_COMPLETED:
1368
+ case Methods.WINDOWS_WORLD_WRITABLE_WARNING:
1369
+ case Methods.ACCOUNT_LOGIN_COMPLETED:
1370
+ pushEvent(session.eventBuffer, "progress", { method, ...p });
1371
+ break;
1292
1372
  case Methods.TURN_STARTED:
1293
1373
  if (session.status === "cancelled") break;
1294
- session.activeTurnId = p.turn?.id ?? (typeof p.turnId === "string" ? p.turnId : void 0);
1295
- pushEvent(session.eventBuffer, "progress", { method, ...p });
1374
+ {
1375
+ const turnObj = p.turn;
1376
+ const status = normalizeOptionalString(turnObj?.status);
1377
+ session.activeTurnId = normalizeOptionalString(turnObj?.id);
1378
+ pushEvent(session.eventBuffer, "progress", {
1379
+ method,
1380
+ ...p,
1381
+ turnId: session.activeTurnId,
1382
+ status
1383
+ });
1384
+ }
1296
1385
  break;
1297
1386
  case Methods.TURN_COMPLETED: {
1298
1387
  if (session.status === "cancelled") break;
1299
1388
  const turnObj = p.turn;
1300
- const completedTurnId = (typeof p.turnId === "string" ? p.turnId : void 0) ?? turnObj?.id ?? session.activeTurnId ?? "";
1389
+ const completedTurnId = turnObj?.id ?? session.activeTurnId ?? "";
1301
1390
  session.status = "idle";
1302
1391
  session.activeTurnId = void 0;
1303
1392
  session.lastResult = {
@@ -1309,7 +1398,17 @@ var SessionManager = class {
1309
1398
  turnError: turnObj?.error,
1310
1399
  completedAt: (/* @__PURE__ */ new Date()).toISOString()
1311
1400
  };
1312
- pushEvent(session.eventBuffer, "result", { method, ...p }, true);
1401
+ pushEvent(
1402
+ session.eventBuffer,
1403
+ "result",
1404
+ {
1405
+ method,
1406
+ ...p,
1407
+ turnId: completedTurnId,
1408
+ status: normalizeOptionalString(turnObj?.status)
1409
+ },
1410
+ true
1411
+ );
1313
1412
  break;
1314
1413
  }
1315
1414
  case Methods.ERROR: {
@@ -1343,12 +1442,20 @@ var SessionManager = class {
1343
1442
  case Methods.AGENT_MESSAGE_DELTA:
1344
1443
  pushEvent(session.eventBuffer, "output", { method, delta: p.delta, itemId: p.itemId });
1345
1444
  break;
1445
+ case Methods.ITEM_STARTED:
1346
1446
  case Methods.ITEM_COMPLETED:
1447
+ case Methods.RAW_RESPONSE_ITEM_COMPLETED:
1347
1448
  {
1348
1449
  const item = p.item;
1349
1450
  const itemType = item && typeof item.type === "string" ? item.type : void 0;
1451
+ const status = normalizeOptionalString(item?.status);
1350
1452
  const eventType = itemType === "agentMessage" || itemType === "userMessage" ? "output" : "progress";
1351
- pushEvent(session.eventBuffer, eventType, { method, item: p.item });
1453
+ pushEvent(session.eventBuffer, eventType, {
1454
+ method,
1455
+ ...p,
1456
+ item: p.item,
1457
+ status
1458
+ });
1352
1459
  }
1353
1460
  break;
1354
1461
  case Methods.COMMAND_OUTPUT_DELTA: {
@@ -1361,14 +1468,16 @@ var SessionManager = class {
1361
1468
  }
1362
1469
  break;
1363
1470
  }
1471
+ case Methods.COMMAND_TERMINAL_INTERACTION:
1364
1472
  case Methods.FILE_CHANGE_OUTPUT_DELTA:
1365
1473
  case Methods.REASONING_TEXT_DELTA:
1366
1474
  case Methods.REASONING_SUMMARY_DELTA:
1475
+ case Methods.REASONING_SUMMARY_PART_ADDED:
1367
1476
  case Methods.PLAN_DELTA:
1368
1477
  case Methods.MCP_TOOL_PROGRESS:
1369
- case Methods.ITEM_STARTED:
1370
1478
  case Methods.TURN_DIFF_UPDATED:
1371
1479
  case Methods.TURN_PLAN_UPDATED:
1480
+ case Methods.MODEL_REROUTED:
1372
1481
  pushEvent(session.eventBuffer, "progress", { method, ...p });
1373
1482
  break;
1374
1483
  default:
@@ -1385,15 +1494,24 @@ var SessionManager = class {
1385
1494
  switch (method) {
1386
1495
  case Methods.COMMAND_APPROVAL: {
1387
1496
  const requestId = `req_${randomUUID().slice(0, 8)}`;
1388
- const reason = normalizeOptionalString(p.reason);
1497
+ const approvalParams = params;
1498
+ const reason = normalizeOptionalString(approvalParams.reason);
1499
+ const approvalId = normalizeOptionalString(approvalParams.approvalId);
1500
+ const commandActions = Array.isArray(approvalParams.commandActions) ? approvalParams.commandActions : null;
1501
+ const proposedExecpolicyAmendment = normalizeStringArrayOrNull(
1502
+ approvalParams.proposedExecpolicyAmendment
1503
+ );
1389
1504
  const pending = {
1390
1505
  requestId,
1391
1506
  kind: "command",
1392
1507
  params,
1393
- itemId: p.itemId,
1394
- threadId: p.threadId,
1395
- turnId: p.turnId,
1508
+ itemId: normalizeOptionalString(approvalParams.itemId) ?? "",
1509
+ threadId: normalizeOptionalString(approvalParams.threadId) ?? "",
1510
+ turnId: normalizeOptionalString(approvalParams.turnId) ?? "",
1396
1511
  reason,
1512
+ approvalId,
1513
+ commandActions,
1514
+ proposedExecpolicyAmendment,
1397
1515
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1398
1516
  resolved: false,
1399
1517
  respond: (result) => client.respondToServer(id, result)
@@ -1415,6 +1533,7 @@ var SessionManager = class {
1415
1533
  {
1416
1534
  requestId,
1417
1535
  kind: "command",
1536
+ approvalId,
1418
1537
  decision: "decline",
1419
1538
  timeout: true
1420
1539
  },
@@ -1434,9 +1553,13 @@ var SessionManager = class {
1434
1553
  {
1435
1554
  requestId,
1436
1555
  kind: "command",
1437
- command: p.command,
1438
- cwd: p.cwd,
1439
- reason
1556
+ itemId: approvalParams.itemId,
1557
+ approvalId,
1558
+ command: approvalParams.command,
1559
+ cwd: approvalParams.cwd,
1560
+ reason,
1561
+ commandActions,
1562
+ proposedExecpolicyAmendment
1440
1563
  },
1441
1564
  true
1442
1565
  );
@@ -1560,7 +1683,11 @@ var SessionManager = class {
1560
1683
  });
1561
1684
  break;
1562
1685
  case Methods.AUTH_TOKEN_REFRESH:
1563
- client.respondErrorToServer(id, -32601, "Auth token refresh not supported by codex-mcp");
1686
+ client.respondErrorToServer(
1687
+ id,
1688
+ AUTH_REFRESH_UNSUPPORTED_CODE,
1689
+ AUTH_REFRESH_UNSUPPORTED_MESSAGE
1690
+ );
1564
1691
  break;
1565
1692
  case Methods.LEGACY_PATCH_APPROVAL:
1566
1693
  case Methods.LEGACY_EXEC_APPROVAL:
@@ -1709,7 +1836,7 @@ function respondToTerminalSessionRequest(client, id, method) {
1709
1836
  });
1710
1837
  break;
1711
1838
  case Methods.AUTH_TOKEN_REFRESH:
1712
- client.respondErrorToServer(id, -32601, "Session is terminal");
1839
+ client.respondErrorToServer(id, AUTH_REFRESH_UNSUPPORTED_CODE, AUTH_REFRESH_TERMINAL_MESSAGE);
1713
1840
  break;
1714
1841
  case Methods.LEGACY_PATCH_APPROVAL:
1715
1842
  case Methods.LEGACY_EXEC_APPROVAL:
@@ -1723,6 +1850,11 @@ function respondToTerminalSessionRequest(client, id, method) {
1723
1850
  function normalizeOptionalString(value) {
1724
1851
  return typeof value === "string" ? value : void 0;
1725
1852
  }
1853
+ function normalizeStringArrayOrNull(value) {
1854
+ if (!Array.isArray(value)) return null;
1855
+ const normalized = value.filter((entry) => typeof entry === "string");
1856
+ return normalized;
1857
+ }
1726
1858
  function sendPendingRequestResponseOrThrow(req, response, sessionId, requestId) {
1727
1859
  if (!req.respond) {
1728
1860
  throw new Error(
@@ -1744,7 +1876,9 @@ function compactActionsForBudget(actions) {
1744
1876
  kind: action.kind,
1745
1877
  params: compactActionParamsForBudget(action),
1746
1878
  itemId: action.itemId,
1747
- createdAt: action.createdAt
1879
+ createdAt: action.createdAt,
1880
+ commandActions: action.commandActions,
1881
+ proposedExecpolicyAmendment: action.proposedExecpolicyAmendment
1748
1882
  }));
1749
1883
  }
1750
1884
  function compactActionParamsForBudget(action) {
@@ -1757,12 +1891,30 @@ function compactActionParamsForBudget(action) {
1757
1891
  }
1758
1892
  const compactQuestions = [];
1759
1893
  for (const entry of rawQuestions) {
1760
- if (isRecord(entry) && typeof entry.questionId === "string") {
1761
- compactQuestions.push({ questionId: entry.questionId });
1894
+ if (!isRecord(entry)) continue;
1895
+ const id = typeof entry.id === "string" ? entry.id : void 0;
1896
+ if (id) {
1897
+ compactQuestions.push({ id });
1762
1898
  }
1763
1899
  }
1764
1900
  return compactQuestions.length > 0 ? { questions: compactQuestions } : void 0;
1765
1901
  }
1902
+ function compactActionsToMinimum(actions) {
1903
+ if (actions.length === 0) return actions;
1904
+ const first = actions[0];
1905
+ return [
1906
+ {
1907
+ type: first.type,
1908
+ requestId: first.requestId,
1909
+ kind: first.kind,
1910
+ params: void 0,
1911
+ itemId: first.itemId,
1912
+ createdAt: first.createdAt,
1913
+ commandActions: first.commandActions,
1914
+ proposedExecpolicyAmendment: first.proposedExecpolicyAmendment
1915
+ }
1916
+ ];
1917
+ }
1766
1918
  function clampCursorToLatest(cursor, latestCursor) {
1767
1919
  return Math.max(0, Math.min(cursor, latestCursor));
1768
1920
  }
@@ -1983,17 +2135,17 @@ function toSensitiveInfo(session) {
1983
2135
  config: session.config
1984
2136
  };
1985
2137
  }
1986
- function buildCommandApprovalResponse(decision, execpolicyAmendment) {
2138
+ function buildCommandApprovalResponse(decision, execpolicy_amendment) {
1987
2139
  if (decision === "acceptWithExecpolicyAmendment") {
1988
- if (!execpolicyAmendment || execpolicyAmendment.length === 0) {
2140
+ if (!execpolicy_amendment || execpolicy_amendment.length === 0) {
1989
2141
  throw new Error(
1990
- `Error [${"INVALID_ARGUMENT" /* INVALID_ARGUMENT */}]: execpolicyAmendment required for acceptWithExecpolicyAmendment`
2142
+ `Error [${"INVALID_ARGUMENT" /* INVALID_ARGUMENT */}]: execpolicy_amendment required for acceptWithExecpolicyAmendment`
1991
2143
  );
1992
2144
  }
1993
2145
  return {
1994
2146
  decision: {
1995
2147
  acceptWithExecpolicyAmendment: {
1996
- execpolicy_amendment: execpolicyAmendment
2148
+ execpolicy_amendment
1997
2149
  }
1998
2150
  }
1999
2151
  };
@@ -2094,6 +2246,18 @@ async function executeCodexSession(args, sessionManager) {
2094
2246
  };
2095
2247
  }
2096
2248
  return await sessionManager.forkSession(args.sessionId);
2249
+ case "clean_background_terminals":
2250
+ if (!args.sessionId) {
2251
+ return {
2252
+ error: `Error [${"INVALID_ARGUMENT" /* INVALID_ARGUMENT */}]: sessionId required for 'clean_background_terminals'`,
2253
+ isError: true
2254
+ };
2255
+ }
2256
+ await sessionManager.cleanBackgroundTerminals(args.sessionId);
2257
+ return {
2258
+ success: true,
2259
+ message: `Background terminals cleaned for session ${args.sessionId}`
2260
+ };
2097
2261
  default:
2098
2262
  return {
2099
2263
  error: `Error [${"INVALID_ARGUMENT" /* INVALID_ARGUMENT */}]: Unknown action '${args.action}'`,
@@ -2108,7 +2272,13 @@ function executeCodexCheck(args, sessionManager) {
2108
2272
  const pollOptions = args.pollOptions;
2109
2273
  switch (args.action) {
2110
2274
  case "poll": {
2111
- const maxEvents = typeof args.maxEvents === "number" ? Math.max(POLL_MIN_MAX_EVENTS, args.maxEvents) : POLL_DEFAULT_MAX_EVENTS;
2275
+ if (args.requestId !== void 0 || args.decision !== void 0 || args.execpolicy_amendment !== void 0 || args.denyMessage !== void 0 || args.answers !== void 0) {
2276
+ return {
2277
+ error: `Error [${"INVALID_ARGUMENT" /* INVALID_ARGUMENT */}]: requestId/decision/execpolicy_amendment/denyMessage/answers are only valid for respond_* actions`,
2278
+ isError: true
2279
+ };
2280
+ }
2281
+ const maxEvents = typeof args.maxEvents === "number" ? Math.max(POLL_MIN_MAX_EVENTS, Math.floor(args.maxEvents)) : POLL_DEFAULT_MAX_EVENTS;
2112
2282
  return sessionManager.pollEvents(args.sessionId, args.cursor, maxEvents, {
2113
2283
  responseMode,
2114
2284
  pollOptions
@@ -2121,16 +2291,41 @@ function executeCodexCheck(args, sessionManager) {
2121
2291
  isError: true
2122
2292
  };
2123
2293
  }
2294
+ if (args.answers !== void 0) {
2295
+ return {
2296
+ error: `Error [${"INVALID_ARGUMENT" /* INVALID_ARGUMENT */}]: answers is only valid for respond_user_input`,
2297
+ isError: true
2298
+ };
2299
+ }
2300
+ if (args.decision === "acceptWithExecpolicyAmendment") {
2301
+ if (!args.execpolicy_amendment || args.execpolicy_amendment.length === 0) {
2302
+ return {
2303
+ error: `Error [${"INVALID_ARGUMENT" /* INVALID_ARGUMENT */}]: execpolicy_amendment required for acceptWithExecpolicyAmendment`,
2304
+ isError: true
2305
+ };
2306
+ }
2307
+ } else if (args.execpolicy_amendment !== void 0) {
2308
+ return {
2309
+ error: `Error [${"INVALID_ARGUMENT" /* INVALID_ARGUMENT */}]: execpolicy_amendment is only valid with decision='acceptWithExecpolicyAmendment'`,
2310
+ isError: true
2311
+ };
2312
+ }
2313
+ if (!ALL_DECISIONS.includes(args.decision)) {
2314
+ return {
2315
+ error: `Error [${"INVALID_ARGUMENT" /* INVALID_ARGUMENT */}]: Unknown decision '${args.decision}'`,
2316
+ isError: true
2317
+ };
2318
+ }
2124
2319
  try {
2125
2320
  sessionManager.resolveApproval(args.sessionId, args.requestId, args.decision, {
2126
- execpolicyAmendment: args.execpolicyAmendment,
2321
+ execpolicy_amendment: args.execpolicy_amendment,
2127
2322
  denyMessage: args.denyMessage
2128
2323
  });
2129
2324
  } catch (err) {
2130
2325
  const message = err instanceof Error ? err.message : String(err);
2131
2326
  return { error: message, isError: true };
2132
2327
  }
2133
- const maxEvents = args.maxEvents ?? RESPOND_DEFAULT_MAX_EVENTS;
2328
+ const maxEvents = typeof args.maxEvents === "number" ? Math.max(0, Math.floor(args.maxEvents)) : RESPOND_DEFAULT_MAX_EVENTS;
2134
2329
  return sessionManager.pollEventsMonotonic(args.sessionId, args.cursor, maxEvents, {
2135
2330
  responseMode,
2136
2331
  pollOptions
@@ -2143,13 +2338,19 @@ function executeCodexCheck(args, sessionManager) {
2143
2338
  isError: true
2144
2339
  };
2145
2340
  }
2341
+ if (args.decision !== void 0 || args.execpolicy_amendment !== void 0 || args.denyMessage !== void 0) {
2342
+ return {
2343
+ error: `Error [${"INVALID_ARGUMENT" /* INVALID_ARGUMENT */}]: decision/execpolicy_amendment/denyMessage are only valid for respond_permission`,
2344
+ isError: true
2345
+ };
2346
+ }
2146
2347
  try {
2147
2348
  sessionManager.resolveUserInput(args.sessionId, args.requestId, args.answers);
2148
2349
  } catch (err) {
2149
2350
  const message = err instanceof Error ? err.message : String(err);
2150
2351
  return { error: message, isError: true };
2151
2352
  }
2152
- const maxEvents = args.maxEvents ?? RESPOND_DEFAULT_MAX_EVENTS;
2353
+ const maxEvents = typeof args.maxEvents === "number" ? Math.max(0, Math.floor(args.maxEvents)) : RESPOND_DEFAULT_MAX_EVENTS;
2153
2354
  return sessionManager.pollEventsMonotonic(args.sessionId, args.cursor, maxEvents, {
2154
2355
  responseMode,
2155
2356
  pollOptions
@@ -2436,6 +2637,7 @@ function buildGotchasText() {
2436
2637
  `- Pending approvals/user-input auto-decline after \`approvalTimeoutMs\` (default ${DEFAULT_APPROVAL_TIMEOUT_MS} ms).`,
2437
2638
  "- `untrusted` behavior is enforced by Codex CLI backend and may auto-allow some low-risk commands.",
2438
2639
  "- Do not assume every read-only command will always require approval across CLI versions.",
2640
+ `- **Timeout vs polling conflict**: The recommended polling interval for \`running\` status is >=120 seconds, but the default approval timeout is ${DEFAULT_APPROVAL_TIMEOUT_MS / 1e3} seconds. If a session transitions to \`waiting_approval\` between polls, the approval will auto-decline before the client can respond. Set \`advanced.approvalTimeoutMs\` to at least 300000 (5 minutes) when using \`untrusted\` or \`on-request\` policies.`,
2439
2641
  "",
2440
2642
  "## Event model",
2441
2643
  "",
@@ -2505,6 +2707,7 @@ function buildQuickstartText() {
2505
2707
  "",
2506
2708
  "- Use `pollInterval` as a minimum delay: `running` >=120000ms (and usually longer for big tasks).",
2507
2709
  "- `waiting_approval` is the exception: poll/answer around 1000ms to avoid timeout.",
2710
+ `- When using \`untrusted\` or \`on-request\` policies, set \`advanced.approvalTimeoutMs\` to at least 300000 to prevent approvals from expiring between polling intervals.`,
2508
2711
  "",
2509
2712
  "3. If `actions[]` contains an approval request, respond:",
2510
2713
  "",
@@ -2576,7 +2779,7 @@ function buildCompatReport(deps, codexCliVersion) {
2576
2779
  schemaVersion: "1.0.0",
2577
2780
  features: {
2578
2781
  respondPermission: true,
2579
- respondApprovalAlias: true,
2782
+ respondApprovalAlias: false,
2580
2783
  respondUserInput: true,
2581
2784
  sessionInterrupt: true,
2582
2785
  responseModeMinimal: true,
@@ -2732,7 +2935,7 @@ function registerResources(server, deps) {
2732
2935
  }
2733
2936
 
2734
2937
  // src/server.ts
2735
- var SERVER_VERSION = true ? "0.2.1" : "0.0.0-dev";
2938
+ var SERVER_VERSION = true ? "2.0.2" : "0.0.0-dev";
2736
2939
  function formatErrorMessage(err) {
2737
2940
  const message = err instanceof Error ? err.message : String(err);
2738
2941
  const m = /^Error \[([A-Z_]+)\]:\s*(.*)$/.exec(message);
@@ -2783,6 +2986,119 @@ function createServer(serverCwd) {
2783
2986
  ),
2784
2987
  ...errorOutputShape
2785
2988
  };
2989
+ const codexCheckPollOptionsSchema = z.object({
2990
+ includeEvents: z.boolean().optional().describe("Default: true. Include events[] in response."),
2991
+ includeActions: z.boolean().optional().describe("Default: true. Include actions[] in response."),
2992
+ includeResult: z.boolean().optional().describe("Default: true. Include result in response."),
2993
+ maxBytes: z.number().int().positive().optional().describe("Default: unlimited. Best-effort response payload cap in bytes.")
2994
+ }).optional().describe("Optional poll shaping controls.");
2995
+ const codexCheckInputSchema = z.object({
2996
+ action: z.enum(CHECK_ACTIONS),
2997
+ sessionId: z.string().describe("Target session ID"),
2998
+ cursor: z.number().int().nonnegative().optional().describe("Event cursor (default: session last consumed cursor)."),
2999
+ maxEvents: z.number().int().nonnegative().optional().describe(
3000
+ `Max events. Default: poll=${POLL_DEFAULT_MAX_EVENTS} (min ${POLL_MIN_MAX_EVENTS}), respond_*=${RESPOND_DEFAULT_MAX_EVENTS}.`
3001
+ ),
3002
+ responseMode: z.enum(RESPONSE_MODES).optional().describe("Response mode. Default: minimal. Options: minimal/delta_compact/full."),
3003
+ pollOptions: codexCheckPollOptionsSchema,
3004
+ // respond_permission
3005
+ requestId: z.string().optional().describe("Request ID from actions[]"),
3006
+ decision: z.enum(ALL_DECISIONS).optional().describe(
3007
+ "Approval decision for respond_permission. acceptWithExecpolicyAmendment requires execpolicy_amendment."
3008
+ ),
3009
+ execpolicy_amendment: z.array(z.string()).optional().describe("For acceptWithExecpolicyAmendment only"),
3010
+ denyMessage: z.string().optional().describe("Deny reason (not sent to agent)"),
3011
+ // respond_user_input
3012
+ answers: z.record(
3013
+ z.string(),
3014
+ z.object({
3015
+ answers: z.array(z.string())
3016
+ })
3017
+ ).optional().describe("question-id -> answers map (id from actions[] user_input request).")
3018
+ }).superRefine((value, ctx) => {
3019
+ const addIssue = (path4, message) => {
3020
+ ctx.addIssue({
3021
+ code: z.ZodIssueCode.custom,
3022
+ path: [path4],
3023
+ message
3024
+ });
3025
+ };
3026
+ switch (value.action) {
3027
+ case "poll": {
3028
+ if (value.maxEvents !== void 0 && value.maxEvents < POLL_MIN_MAX_EVENTS) {
3029
+ addIssue(
3030
+ "maxEvents",
3031
+ `poll requires maxEvents >= ${POLL_MIN_MAX_EVENTS} to avoid no-op loops.`
3032
+ );
3033
+ }
3034
+ if (value.requestId !== void 0) {
3035
+ addIssue("requestId", "requestId is only allowed for respond_* actions.");
3036
+ }
3037
+ if (value.decision !== void 0) {
3038
+ addIssue("decision", "decision is only allowed for action='respond_permission'.");
3039
+ }
3040
+ if (value.execpolicy_amendment !== void 0) {
3041
+ addIssue(
3042
+ "execpolicy_amendment",
3043
+ "execpolicy_amendment is only allowed for action='respond_permission'."
3044
+ );
3045
+ }
3046
+ if (value.denyMessage !== void 0) {
3047
+ addIssue("denyMessage", "denyMessage is only allowed for action='respond_permission'.");
3048
+ }
3049
+ if (value.answers !== void 0) {
3050
+ addIssue("answers", "answers is only allowed for action='respond_user_input'.");
3051
+ }
3052
+ break;
3053
+ }
3054
+ case "respond_permission": {
3055
+ if (!value.requestId) {
3056
+ addIssue("requestId", "requestId is required for action='respond_permission'.");
3057
+ }
3058
+ if (!value.decision) {
3059
+ addIssue("decision", "decision is required for action='respond_permission'.");
3060
+ }
3061
+ if (value.answers !== void 0) {
3062
+ addIssue("answers", "answers is only allowed for action='respond_user_input'.");
3063
+ }
3064
+ const needsExecpolicy = value.decision === "acceptWithExecpolicyAmendment";
3065
+ if (needsExecpolicy && (!value.execpolicy_amendment || value.execpolicy_amendment.length === 0)) {
3066
+ addIssue(
3067
+ "execpolicy_amendment",
3068
+ "execpolicy_amendment is required and must be non-empty when decision='acceptWithExecpolicyAmendment'."
3069
+ );
3070
+ }
3071
+ if (!needsExecpolicy && value.execpolicy_amendment !== void 0) {
3072
+ addIssue(
3073
+ "execpolicy_amendment",
3074
+ "execpolicy_amendment is only allowed when decision='acceptWithExecpolicyAmendment'."
3075
+ );
3076
+ }
3077
+ break;
3078
+ }
3079
+ case "respond_user_input": {
3080
+ if (!value.requestId) {
3081
+ addIssue("requestId", "requestId is required for action='respond_user_input'.");
3082
+ }
3083
+ if (!value.answers) {
3084
+ addIssue("answers", "answers is required for action='respond_user_input'.");
3085
+ }
3086
+ if (value.decision !== void 0) {
3087
+ addIssue("decision", "decision is only allowed for action='respond_permission'.");
3088
+ }
3089
+ if (value.execpolicy_amendment !== void 0) {
3090
+ addIssue(
3091
+ "execpolicy_amendment",
3092
+ "execpolicy_amendment is only allowed for action='respond_permission'."
3093
+ );
3094
+ }
3095
+ if (value.denyMessage !== void 0) {
3096
+ addIssue("denyMessage", "denyMessage is only allowed for action='respond_permission'.");
3097
+ }
3098
+ break;
3099
+ }
3100
+ }
3101
+ });
2786
3102
  server.registerTool(
2787
3103
  "codex",
2788
3104
  {
@@ -2883,16 +3199,17 @@ function createServer(serverCwd) {
2883
3199
  "codex_session",
2884
3200
  {
2885
3201
  title: "Manage Sessions",
2886
- description: `Session actions: list, get, cancel, interrupt, fork.
3202
+ description: `Session actions: list, get, cancel, interrupt, fork, clean_background_terminals.
2887
3203
 
2888
3204
  - list: sessions in memory.
2889
3205
  - get: details. includeSensitive defaults to false; true adds threadId/cwd/profile/config.
2890
3206
  - cancel: terminal.
2891
3207
  - interrupt: stop current turn.
2892
- - fork: clone current thread into a new session; source remains unchanged.`,
3208
+ - fork: clone current thread into a new session; source remains unchanged.
3209
+ - clean_background_terminals: ask app-server to clean stale background terminals for this thread.`,
2893
3210
  inputSchema: {
2894
3211
  action: z.enum(SESSION_ACTIONS),
2895
- sessionId: z.string().optional().describe("Required for get/cancel/interrupt/fork"),
3212
+ sessionId: z.string().optional().describe("Required for get/cancel/interrupt/fork/clean_background_terminals"),
2896
3213
  includeSensitive: z.boolean().default(false).optional().describe("Include cwd/config/threadId/profile in get (default: false)")
2897
3214
  },
2898
3215
  outputSchema: {
@@ -2965,35 +3282,7 @@ respond_user_input: user-input answers. Default maxEvents=${RESPOND_DEFAULT_MAX_
2965
3282
 
2966
3283
  events[].type is coarse-grained; details are in events[].data.method.
2967
3284
  cursor omitted => use session last cursor. cursorResetTo => reset and continue.`,
2968
- inputSchema: {
2969
- action: z.enum(CHECK_ACTIONS),
2970
- sessionId: z.string().describe("Target session ID"),
2971
- cursor: z.number().int().nonnegative().optional().describe("Event cursor (default: session last consumed cursor)."),
2972
- maxEvents: z.number().int().nonnegative().optional().describe(
2973
- `Max events. Default: poll=${POLL_DEFAULT_MAX_EVENTS} (min ${POLL_MIN_MAX_EVENTS}), respond_*=${RESPOND_DEFAULT_MAX_EVENTS}.`
2974
- ),
2975
- responseMode: z.enum(RESPONSE_MODES).optional().describe("Response mode. Default: minimal. Options: minimal/delta_compact/full."),
2976
- pollOptions: z.object({
2977
- includeEvents: z.boolean().optional().describe("Default: true. Include events[] in response."),
2978
- includeActions: z.boolean().optional().describe("Default: true. Include actions[] in response."),
2979
- includeResult: z.boolean().optional().describe("Default: true. Include result in response."),
2980
- maxBytes: z.number().int().positive().optional().describe("Default: unlimited. Best-effort response payload cap in bytes.")
2981
- }).optional().describe("Optional poll shaping controls."),
2982
- // respond_permission
2983
- requestId: z.string().optional().describe("Request ID from actions[]"),
2984
- decision: z.enum(ALL_DECISIONS).optional().describe(
2985
- "Approval decision for respond_permission. acceptWithExecpolicyAmendment requires execpolicyAmendment."
2986
- ),
2987
- execpolicyAmendment: z.array(z.string()).optional().describe("For acceptWithExecpolicyAmendment only"),
2988
- denyMessage: z.string().optional().describe("Deny reason (not sent to agent)"),
2989
- // respond_user_input
2990
- answers: z.record(
2991
- z.string(),
2992
- z.object({
2993
- answers: z.array(z.string())
2994
- })
2995
- ).optional().describe("questionId -> answers map (questionId from actions[] user_input request).")
2996
- },
3285
+ inputSchema: codexCheckInputSchema,
2997
3286
  outputSchema: {
2998
3287
  sessionId: z.string().optional(),
2999
3288
  status: z.enum(["running", "idle", "waiting_approval", "error", "cancelled"]).optional(),
@@ -3021,10 +3310,13 @@ cursor omitted => use session last cursor. cursorResetTo => reset and continue.`
3021
3310
  z.object({
3022
3311
  type: z.enum(["approval", "user_input"]),
3023
3312
  requestId: z.string(),
3024
- kind: z.string(),
3313
+ kind: z.enum(["command", "fileChange", "user_input"]),
3025
3314
  params: z.unknown(),
3026
3315
  itemId: z.string(),
3027
3316
  reason: z.string().optional(),
3317
+ approvalId: z.string().optional(),
3318
+ commandActions: z.array(z.unknown()).nullable().optional(),
3319
+ proposedExecpolicyAmendment: z.array(z.string()).nullable().optional(),
3028
3320
  createdAt: z.string()
3029
3321
  })
3030
3322
  ).optional(),