@leo000001/codex-mcp 0.2.0 → 2.0.0

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/CHANGELOG.md CHANGED
@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
12
12
  - `approvalPolicy`, `sandbox`, and `effort` are now **required** parameters in the `codex` tool — callers must explicitly set based on their own permission level and task complexity
13
13
  - `effort` parameter promoted from `advanced.effort` to top-level parameter in the `codex` tool
14
14
  - `codex_reply` parameter `sandboxPolicy` renamed to `sandbox`
15
+ - `codex_check` parameter `execpolicyAmendment` renamed to `execpolicy_amendment` to match app-server protocol field naming
15
16
 
16
17
  ### Changed
17
18
 
@@ -23,6 +24,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
23
24
  - Approval and user-input timeout timers now call `.unref()` to avoid blocking process exit
24
25
  - Documentation aligned with implementation details for event eviction and e2e guidance
25
26
  - Tool input defaults are now defined in schema (`cursor`, `maxEvents`, `includeSensitive`, `advanced.approvalTimeoutMs`) and client-facing text avoids duplicated default descriptions
27
+ - `codex_session` adds `clean_background_terminals` action to call `thread/background_terminals/clean`
28
+ - Approval action payloads now expose `approvalId` and `networkApprovalContext` when provided by app-server
26
29
 
27
30
  ## [0.1.0] - 2026-02-15
28
31
 
package/README.md CHANGED
@@ -57,18 +57,23 @@ Add to your MCP client config (e.g. Claude Desktop, Cursor, etc.):
57
57
  }
58
58
  ```
59
59
 
60
- ### OpenAI Codex CLI
60
+ ### Claude Code
61
61
 
62
62
  ```bash
63
- codex mcp add codex-mcp -- npx -y @leo000001/codex-mcp
63
+ claude mcp add codex-mcp -- npx -y @leo000001/codex-mcp
64
64
  ```
65
65
 
66
- Or add to `~/.codex/config.toml`:
66
+ Or add to `~/.claude/settings.json`:
67
67
 
68
- ```toml
69
- [mcp_servers.codex-mcp]
70
- command = "npx"
71
- args = ["-y", "@leo000001/codex-mcp"]
68
+ ```json
69
+ {
70
+ "mcpServers": {
71
+ "codex-mcp": {
72
+ "command": "npx",
73
+ "args": ["-y", "@leo000001/codex-mcp"]
74
+ }
75
+ }
76
+ }
72
77
  ```
73
78
 
74
79
  ## STDIO Guard Modes
@@ -95,16 +100,16 @@ $env:CODEX_MCP_STDIO_MODE = "strict"; npx -y @leo000001/codex-mcp
95
100
 
96
101
  Start a Codex agent session asynchronously. Returns immediately with `sessionId`.
97
102
 
98
- | Parameter | Type | Required | Description |
99
- | ---------------- | ------ | -------- | ---------------------------------------------------------------------------------------------------------------------- |
100
- | `prompt` | string | Yes | Task or question for the Codex agent |
101
- | `approvalPolicy` | string | Yes | Approval policy: `untrusted`, `on-failure`, `on-request`, `never` — caller must set based on its own permission level |
102
- | `sandbox` | string | Yes | Sandbox mode: `read-only`, `workspace-write`, `danger-full-access` — caller must set based on its own permission level |
103
+ | Parameter | Type | Required | Description |
104
+ | ---------------- | ------ | -------- | --------------------------------------------------------------------------------------------------------------------------------- |
105
+ | `prompt` | string | Yes | Task or question for the Codex agent |
106
+ | `approvalPolicy` | string | Yes | Approval policy: `untrusted`, `on-failure`, `on-request`, `never` — caller must set based on its own permission level |
107
+ | `sandbox` | string | Yes | Sandbox mode: `read-only`, `workspace-write`, `danger-full-access` — caller must set based on its own permission level |
103
108
  | `effort` | string | No | Reasoning effort: `none`, `minimal`, `low`, `medium`, `high`, `xhigh`. Default: `low`; increase/decrease based on task complexity |
104
- | `cwd` | string | No | Working directory. Default: server cwd |
105
- | `model` | string | No | Model override. Default: from `~/.codex/config.toml` |
106
- | `profile` | string | No | `config.toml` profile name (passed as `codex app-server -p`) |
107
- | `advanced` | object | No | Low-frequency options (see below) |
109
+ | `cwd` | string | No | Working directory. Default: server cwd |
110
+ | `model` | string | No | Model override. Default: from `~/.codex/config.toml` |
111
+ | `profile` | string | No | `config.toml` profile name (passed as `codex app-server -p`) |
112
+ | `advanced` | object | No | Low-frequency options (see below) |
108
113
 
109
114
  <details>
110
115
  <summary><code>advanced</code> object parameters (9 low-frequency parameters)</summary>
@@ -175,12 +180,12 @@ Send a follow-up message to an existing session.
175
180
 
176
181
  ### `codex_session` — Manage sessions
177
182
 
178
- List, inspect, cancel, interrupt, or fork sessions.
183
+ List, inspect, cancel, interrupt, fork sessions, or clean background terminals.
179
184
 
180
185
  | Parameter | Type | Required | Description |
181
186
  | ------------------ | ------- | ----------------------------- | ---------------------------------------------------------------------- |
182
- | `action` | string | Yes | `"list"`, `"get"`, `"cancel"`, `"interrupt"`, or `"fork"` |
183
- | `sessionId` | string | For get/cancel/interrupt/fork | Target session ID |
187
+ | `action` | string | Yes | `"list"`, `"get"`, `"cancel"`, `"interrupt"`, `"fork"`, or `"clean_background_terminals"` |
188
+ | `sessionId` | string | For get/cancel/interrupt/fork/clean_background_terminals | Target session ID |
184
189
  | `includeSensitive` | boolean | No | Include `cwd`/`profile`/`config`/`threadId` in `get`. Default: `false` |
185
190
 
186
191
  **Returns:**
@@ -188,6 +193,7 @@ List, inspect, cancel, interrupt, or fork sessions.
188
193
  - `action="get"` → `PublicSessionInfo` (or `SensitiveSessionInfo` when `includeSensitive=true`)
189
194
  - `action="cancel"|"interrupt"` → `{ success: true, message }`
190
195
  - `action="fork"` → `{ sessionId, threadId, status: "idle", pollInterval }`
196
+ - `action="clean_background_terminals"` → `{ success: true, message }`
191
197
 
192
198
  ```json
193
199
  { "action": "list" }
@@ -195,6 +201,7 @@ List, inspect, cancel, interrupt, or fork sessions.
195
201
  { "action": "cancel", "sessionId": "sess_abc123" }
196
202
  { "action": "interrupt", "sessionId": "sess_abc123" }
197
203
  { "action": "fork", "sessionId": "sess_abc123" }
204
+ { "action": "clean_background_terminals", "sessionId": "sess_abc123" }
198
205
  ```
199
206
 
200
207
  ### `codex_check` — Poll events & respond
@@ -203,15 +210,15 @@ Query a running session for events, respond to approval requests, or answer user
203
210
 
204
211
  | Parameter | Type | Required | Description |
205
212
  | --------------------- | -------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
206
- | `action` | string | Yes | `"poll"`, `"respond_permission"`, `"respond_approval"` (deprecated alias), or `"respond_user_input"` |
213
+ | `action` | string | Yes | `"poll"`, `"respond_permission"`, or `"respond_user_input"` |
207
214
  | `sessionId` | string | Yes | Target session ID |
208
- | `cursor` | number | No | Event cursor for incremental polling (`action="poll"`). For `respond_*`, codex-mcp applies monotonic cursor progression: `max(cursor, sessionLastCursor)`. |
209
- | `maxEvents` | number | No | Keep this small. `poll` default: `1` (minimum `1`; increase only for catch-up). `respond_*` default: `0` (recommended; compact ACK, no event replay). |
210
- | `responseMode` | string | No | Response shaping mode: `minimal` (default), `delta_compact`, `full` |
211
- | `pollOptions` | object | No | Optional controls: `includeEvents` (default `true`), `includeActions` (default `true`), `includeResult` (default `true`), `maxBytes` (default unlimited) |
215
+ | `cursor` | number | No | Event cursor for incremental polling (`action="poll"`). For `respond_*`, codex-mcp applies monotonic cursor progression: `max(cursor, sessionLastCursor)`. |
216
+ | `maxEvents` | number | No | Keep this small. `poll` default: `1` (minimum `1`; increase only for catch-up). `respond_*` default: `0` (recommended; compact ACK, no event replay). |
217
+ | `responseMode` | string | No | Response shaping mode: `minimal` (default), `delta_compact`, `full` |
218
+ | `pollOptions` | object | No | Optional controls: `includeEvents` (default `true`), `includeActions` (default `true`), `includeResult` (default `true`), `maxBytes` (default unlimited) |
212
219
  | `requestId` | string | For respond_permission/user_input | Request ID from `actions[]` |
213
220
  | `decision` | string | For respond_permission | For command approvals: `"accept"`, `"acceptForSession"`, `"acceptWithExecpolicyAmendment"`, `"decline"`, `"cancel"`; for file changes: `"accept"`, `"acceptForSession"`, `"decline"`, `"cancel"` |
214
- | `execpolicyAmendment` | string[] | For acceptWithExecpolicyAmendment | Exec policy amendment list (required when `decision="acceptWithExecpolicyAmendment"`) |
221
+ | `execpolicy_amendment` | string[] | For acceptWithExecpolicyAmendment | Exec policy amendment list (required when `decision="acceptWithExecpolicyAmendment"`) |
215
222
  | `denyMessage` | string | No | Internal note on deny (not sent to app-server) |
216
223
  | `answers` | object | For respond_user_input | For `respond_user_input`: `questionId -> { answers: string[] }` |
217
224
 
@@ -259,8 +266,7 @@ Approvals/results/errors are pinned to reduce eviction risk.
259
266
  When the agent requests approval or user input, `poll` includes an `actions[]` list. Respond with:
260
267
 
261
268
  - `respond_permission`: `decision` is one of `accept`, `acceptForSession`, `decline`, `cancel`.
262
- - For command approvals, `acceptWithExecpolicyAmendment` is supported and requires `execpolicyAmendment`.
263
- - `respond_approval` is still accepted as a deprecated alias for `respond_permission`.
269
+ - For command approvals, `acceptWithExecpolicyAmendment` is supported and requires `execpolicy_amendment`.
264
270
  - `respond_user_input`: send `answers` keyed by `questionId`.
265
271
 
266
272
  Pending approvals auto-decline after `advanced.approvalTimeoutMs`.
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,13 +177,15 @@ 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"];
168
- var CHECK_ACTIONS = [
169
- "poll",
170
- "respond_permission",
171
- "respond_approval",
172
- "respond_user_input"
180
+ var SESSION_ACTIONS = [
181
+ "list",
182
+ "get",
183
+ "cancel",
184
+ "interrupt",
185
+ "fork",
186
+ "clean_background_terminals"
173
187
  ];
188
+ var CHECK_ACTIONS = ["poll", "respond_permission", "respond_user_input"];
174
189
  var RESPONSE_MODES = ["minimal", "delta_compact", "full"];
175
190
  var COMMAND_DECISIONS = [
176
191
  "accept",
@@ -218,7 +233,7 @@ var DEFAULT_TERMINAL_CLEANUP_MS = 5 * 60 * 1e3;
218
233
  var CLEANUP_INTERVAL_MS = 6e4;
219
234
 
220
235
  // src/app-server/client.ts
221
- var CLIENT_VERSION = true ? "0.2.0" : "0.0.0-dev";
236
+ var CLIENT_VERSION = true ? "2.0.0" : "0.0.0-dev";
222
237
  var DEFAULT_REQUEST_TIMEOUT = 3e4;
223
238
  var STARTUP_REQUEST_TIMEOUT = 9e4;
224
239
  var MAX_WRITE_QUEUE_BYTES = 5 * 1024 * 1024;
@@ -335,6 +350,9 @@ var AppServerClient = class extends EventEmitter {
335
350
  async threadResume(params) {
336
351
  return this.request(Methods.THREAD_RESUME, params);
337
352
  }
353
+ async threadBackgroundTerminalsClean(params) {
354
+ return this.request(Methods.THREAD_BACKGROUND_TERMINALS_CLEAN, params);
355
+ }
338
356
  async turnStart(params, timeout = STARTUP_REQUEST_TIMEOUT) {
339
357
  return this.request(Methods.TURN_START, params, timeout);
340
358
  }
@@ -964,6 +982,32 @@ var SessionManager = class {
964
982
  turnId: session.activeTurnId
965
983
  });
966
984
  }
985
+ async cleanBackgroundTerminals(sessionId) {
986
+ const session = this.getSessionOrThrow(sessionId);
987
+ const client = this.getClientOrThrow(sessionId);
988
+ if (session.status === "cancelled") {
989
+ throw new Error(
990
+ `Error [${"CANCELLED" /* CANCELLED */}]: Session '${sessionId}' has been cancelled and cannot be cleaned`
991
+ );
992
+ }
993
+ if (!session.threadId) {
994
+ throw new Error(
995
+ `Error [${"INTERNAL" /* INTERNAL */}]: Session '${sessionId}' has no threadId, cannot clean background terminals`
996
+ );
997
+ }
998
+ await client.threadBackgroundTerminalsClean({ threadId: session.threadId });
999
+ session.lastActiveAt = (/* @__PURE__ */ new Date()).toISOString();
1000
+ pushEvent(
1001
+ session.eventBuffer,
1002
+ "progress",
1003
+ {
1004
+ method: Methods.THREAD_BACKGROUND_TERMINALS_CLEAN,
1005
+ threadId: session.threadId,
1006
+ status: "requested"
1007
+ },
1008
+ true
1009
+ );
1010
+ }
967
1011
  async forkSession(sessionId) {
968
1012
  const session = this.getSessionOrThrow(sessionId);
969
1013
  const originalClient = this.getClientOrThrow(sessionId);
@@ -1069,6 +1113,8 @@ var SessionManager = class {
1069
1113
  params: req.params,
1070
1114
  itemId: req.itemId,
1071
1115
  reason: req.reason,
1116
+ approvalId: req.approvalId,
1117
+ networkApprovalContext: req.networkApprovalContext,
1072
1118
  createdAt: req.createdAt
1073
1119
  });
1074
1120
  }
@@ -1084,13 +1130,6 @@ var SessionManager = class {
1084
1130
  actions: actions.length > 0 ? actions : void 0,
1085
1131
  result: includeResult && (session.status === "idle" || session.status === "error" || session.status === "cancelled") ? session.lastResult : void 0
1086
1132
  };
1087
- if (pollOptions?.includeTools === true) {
1088
- addCompatWarningWithinBudget(
1089
- result,
1090
- "pollOptions.includeTools is not yet supported by codex-mcp; returning no tool metadata.",
1091
- maxBytes
1092
- );
1093
- }
1094
1133
  if (typeof maxBytes === "number") {
1095
1134
  const normalizedMaxBytes = Math.max(1, Math.floor(maxBytes));
1096
1135
  const hasAnyPayload = result.events.length > 0 || typeof result.actions !== "undefined" || typeof result.result !== "undefined";
@@ -1179,9 +1218,9 @@ var SessionManager = class {
1179
1218
  `Error [${"INVALID_ARGUMENT" /* INVALID_ARGUMENT */}]: Invalid command decision '${decision}'`
1180
1219
  );
1181
1220
  }
1182
- if (decision === "acceptWithExecpolicyAmendment" && (!extra?.execpolicyAmendment || extra.execpolicyAmendment.length === 0)) {
1221
+ if (decision === "acceptWithExecpolicyAmendment" && (!extra?.execpolicy_amendment || extra.execpolicy_amendment.length === 0)) {
1183
1222
  throw new Error(
1184
- `Error [${"INVALID_ARGUMENT" /* INVALID_ARGUMENT */}]: execpolicyAmendment required for acceptWithExecpolicyAmendment`
1223
+ `Error [${"INVALID_ARGUMENT" /* INVALID_ARGUMENT */}]: execpolicy_amendment required for acceptWithExecpolicyAmendment`
1185
1224
  );
1186
1225
  }
1187
1226
  } else if (req.kind === "fileChange") {
@@ -1197,7 +1236,7 @@ var SessionManager = class {
1197
1236
  }
1198
1237
  let response;
1199
1238
  if (req.kind === "command") {
1200
- response = buildCommandApprovalResponse(decision, extra?.execpolicyAmendment);
1239
+ response = buildCommandApprovalResponse(decision, extra?.execpolicy_amendment);
1201
1240
  } else if (req.kind === "fileChange") {
1202
1241
  response = { decision };
1203
1242
  }
@@ -1216,6 +1255,8 @@ var SessionManager = class {
1216
1255
  {
1217
1256
  requestId,
1218
1257
  kind: req.kind,
1258
+ approvalId: req.approvalId,
1259
+ networkApprovalContext: req.networkApprovalContext,
1219
1260
  decision,
1220
1261
  denyMessage: extra?.denyMessage
1221
1262
  },
@@ -1249,6 +1290,8 @@ var SessionManager = class {
1249
1290
  {
1250
1291
  requestId,
1251
1292
  kind: "user_input",
1293
+ approvalId: req.approvalId,
1294
+ networkApprovalContext: req.networkApprovalContext,
1252
1295
  answers
1253
1296
  },
1254
1297
  true
@@ -1301,10 +1344,39 @@ var SessionManager = class {
1301
1344
  session.lastActiveAt = (/* @__PURE__ */ new Date()).toISOString();
1302
1345
  const p = params;
1303
1346
  switch (method) {
1347
+ case Methods.THREAD_STARTED: {
1348
+ const thread = isRecord(p.thread) ? p.thread : void 0;
1349
+ pushEvent(session.eventBuffer, "progress", {
1350
+ method,
1351
+ ...p,
1352
+ threadId: normalizeOptionalString(p.threadId) ?? normalizeOptionalString(thread?.id),
1353
+ status: normalizeOptionalString(thread?.status)
1354
+ });
1355
+ break;
1356
+ }
1357
+ case Methods.THREAD_ARCHIVED:
1358
+ case Methods.THREAD_UNARCHIVED:
1359
+ case Methods.THREAD_NAME_UPDATED:
1360
+ case Methods.THREAD_TOKEN_USAGE_UPDATED:
1361
+ case Methods.FUZZY_FILE_SEARCH_SESSION_UPDATED:
1362
+ case Methods.FUZZY_FILE_SEARCH_SESSION_COMPLETED:
1363
+ case Methods.WINDOWS_WORLD_WRITABLE_WARNING:
1364
+ case Methods.ACCOUNT_LOGIN_COMPLETED:
1365
+ pushEvent(session.eventBuffer, "progress", { method, ...p });
1366
+ break;
1304
1367
  case Methods.TURN_STARTED:
1305
1368
  if (session.status === "cancelled") break;
1306
- session.activeTurnId = p.turn?.id ?? (typeof p.turnId === "string" ? p.turnId : void 0);
1307
- pushEvent(session.eventBuffer, "progress", { method, ...p });
1369
+ {
1370
+ const turnObj = p.turn;
1371
+ const status = normalizeOptionalString(turnObj?.status);
1372
+ session.activeTurnId = normalizeOptionalString(turnObj?.id) ?? (typeof p.turnId === "string" ? p.turnId : void 0);
1373
+ pushEvent(session.eventBuffer, "progress", {
1374
+ method,
1375
+ ...p,
1376
+ turnId: session.activeTurnId,
1377
+ status
1378
+ });
1379
+ }
1308
1380
  break;
1309
1381
  case Methods.TURN_COMPLETED: {
1310
1382
  if (session.status === "cancelled") break;
@@ -1321,7 +1393,17 @@ var SessionManager = class {
1321
1393
  turnError: turnObj?.error,
1322
1394
  completedAt: (/* @__PURE__ */ new Date()).toISOString()
1323
1395
  };
1324
- pushEvent(session.eventBuffer, "result", { method, ...p }, true);
1396
+ pushEvent(
1397
+ session.eventBuffer,
1398
+ "result",
1399
+ {
1400
+ method,
1401
+ ...p,
1402
+ turnId: completedTurnId,
1403
+ status: normalizeOptionalString(turnObj?.status)
1404
+ },
1405
+ true
1406
+ );
1325
1407
  break;
1326
1408
  }
1327
1409
  case Methods.ERROR: {
@@ -1355,12 +1437,20 @@ var SessionManager = class {
1355
1437
  case Methods.AGENT_MESSAGE_DELTA:
1356
1438
  pushEvent(session.eventBuffer, "output", { method, delta: p.delta, itemId: p.itemId });
1357
1439
  break;
1440
+ case Methods.ITEM_STARTED:
1358
1441
  case Methods.ITEM_COMPLETED:
1442
+ case Methods.RAW_RESPONSE_ITEM_COMPLETED:
1359
1443
  {
1360
1444
  const item = p.item;
1361
1445
  const itemType = item && typeof item.type === "string" ? item.type : void 0;
1446
+ const status = normalizeOptionalString(item?.status);
1362
1447
  const eventType = itemType === "agentMessage" || itemType === "userMessage" ? "output" : "progress";
1363
- pushEvent(session.eventBuffer, eventType, { method, item: p.item });
1448
+ pushEvent(session.eventBuffer, eventType, {
1449
+ method,
1450
+ ...p,
1451
+ item: p.item,
1452
+ status
1453
+ });
1364
1454
  }
1365
1455
  break;
1366
1456
  case Methods.COMMAND_OUTPUT_DELTA: {
@@ -1373,14 +1463,16 @@ var SessionManager = class {
1373
1463
  }
1374
1464
  break;
1375
1465
  }
1466
+ case Methods.COMMAND_TERMINAL_INTERACTION:
1376
1467
  case Methods.FILE_CHANGE_OUTPUT_DELTA:
1377
1468
  case Methods.REASONING_TEXT_DELTA:
1378
1469
  case Methods.REASONING_SUMMARY_DELTA:
1470
+ case Methods.REASONING_SUMMARY_PART_ADDED:
1379
1471
  case Methods.PLAN_DELTA:
1380
1472
  case Methods.MCP_TOOL_PROGRESS:
1381
- case Methods.ITEM_STARTED:
1382
1473
  case Methods.TURN_DIFF_UPDATED:
1383
1474
  case Methods.TURN_PLAN_UPDATED:
1475
+ case Methods.MODEL_REROUTED:
1384
1476
  pushEvent(session.eventBuffer, "progress", { method, ...p });
1385
1477
  break;
1386
1478
  default:
@@ -1397,15 +1489,22 @@ var SessionManager = class {
1397
1489
  switch (method) {
1398
1490
  case Methods.COMMAND_APPROVAL: {
1399
1491
  const requestId = `req_${randomUUID().slice(0, 8)}`;
1400
- const reason = normalizeOptionalString(p.reason);
1492
+ const approvalParams = params;
1493
+ const reason = normalizeOptionalString(approvalParams.reason);
1494
+ const approvalId = normalizeOptionalString(
1495
+ approvalParams.approvalId ?? approvalParams.approval_id
1496
+ );
1497
+ const networkApprovalContext = approvalParams.networkApprovalContext ?? approvalParams.network_approval_context;
1401
1498
  const pending = {
1402
1499
  requestId,
1403
1500
  kind: "command",
1404
1501
  params,
1405
- itemId: p.itemId,
1406
- threadId: p.threadId,
1407
- turnId: p.turnId,
1502
+ itemId: normalizeOptionalString(approvalParams.itemId) ?? "",
1503
+ threadId: normalizeOptionalString(approvalParams.threadId) ?? "",
1504
+ turnId: normalizeOptionalString(approvalParams.turnId) ?? "",
1408
1505
  reason,
1506
+ approvalId,
1507
+ networkApprovalContext,
1409
1508
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1410
1509
  resolved: false,
1411
1510
  respond: (result) => client.respondToServer(id, result)
@@ -1427,6 +1526,7 @@ var SessionManager = class {
1427
1526
  {
1428
1527
  requestId,
1429
1528
  kind: "command",
1529
+ approvalId,
1430
1530
  decision: "decline",
1431
1531
  timeout: true
1432
1532
  },
@@ -1446,9 +1546,12 @@ var SessionManager = class {
1446
1546
  {
1447
1547
  requestId,
1448
1548
  kind: "command",
1449
- command: p.command,
1450
- cwd: p.cwd,
1451
- reason
1549
+ itemId: approvalParams.itemId,
1550
+ approvalId,
1551
+ command: approvalParams.command,
1552
+ cwd: approvalParams.cwd,
1553
+ reason,
1554
+ networkApprovalContext
1452
1555
  },
1453
1556
  true
1454
1557
  );
@@ -1995,17 +2098,17 @@ function toSensitiveInfo(session) {
1995
2098
  config: session.config
1996
2099
  };
1997
2100
  }
1998
- function buildCommandApprovalResponse(decision, execpolicyAmendment) {
2101
+ function buildCommandApprovalResponse(decision, execpolicy_amendment) {
1999
2102
  if (decision === "acceptWithExecpolicyAmendment") {
2000
- if (!execpolicyAmendment || execpolicyAmendment.length === 0) {
2103
+ if (!execpolicy_amendment || execpolicy_amendment.length === 0) {
2001
2104
  throw new Error(
2002
- `Error [${"INVALID_ARGUMENT" /* INVALID_ARGUMENT */}]: execpolicyAmendment required for acceptWithExecpolicyAmendment`
2105
+ `Error [${"INVALID_ARGUMENT" /* INVALID_ARGUMENT */}]: execpolicy_amendment required for acceptWithExecpolicyAmendment`
2003
2106
  );
2004
2107
  }
2005
2108
  return {
2006
2109
  decision: {
2007
2110
  acceptWithExecpolicyAmendment: {
2008
- execpolicy_amendment: execpolicyAmendment
2111
+ execpolicy_amendment
2009
2112
  }
2010
2113
  }
2011
2114
  };
@@ -2106,6 +2209,18 @@ async function executeCodexSession(args, sessionManager) {
2106
2209
  };
2107
2210
  }
2108
2211
  return await sessionManager.forkSession(args.sessionId);
2212
+ case "clean_background_terminals":
2213
+ if (!args.sessionId) {
2214
+ return {
2215
+ error: `Error [${"INVALID_ARGUMENT" /* INVALID_ARGUMENT */}]: sessionId required for 'clean_background_terminals'`,
2216
+ isError: true
2217
+ };
2218
+ }
2219
+ await sessionManager.cleanBackgroundTerminals(args.sessionId);
2220
+ return {
2221
+ success: true,
2222
+ message: `Background terminals cleaned for session ${args.sessionId}`
2223
+ };
2109
2224
  default:
2110
2225
  return {
2111
2226
  error: `Error [${"INVALID_ARGUMENT" /* INVALID_ARGUMENT */}]: Unknown action '${args.action}'`,
@@ -2126,17 +2241,16 @@ function executeCodexCheck(args, sessionManager) {
2126
2241
  pollOptions
2127
2242
  });
2128
2243
  }
2129
- case "respond_permission":
2130
- case "respond_approval": {
2244
+ case "respond_permission": {
2131
2245
  if (!args.requestId || !args.decision) {
2132
2246
  return {
2133
- error: `Error [${"INVALID_ARGUMENT" /* INVALID_ARGUMENT */}]: requestId and decision required for respond_permission/respond_approval`,
2247
+ error: `Error [${"INVALID_ARGUMENT" /* INVALID_ARGUMENT */}]: requestId and decision required for respond_permission`,
2134
2248
  isError: true
2135
2249
  };
2136
2250
  }
2137
2251
  try {
2138
2252
  sessionManager.resolveApproval(args.sessionId, args.requestId, args.decision, {
2139
- execpolicyAmendment: args.execpolicyAmendment,
2253
+ execpolicy_amendment: args.execpolicy_amendment,
2140
2254
  denyMessage: args.denyMessage
2141
2255
  });
2142
2256
  } catch (err) {
@@ -2144,18 +2258,10 @@ function executeCodexCheck(args, sessionManager) {
2144
2258
  return { error: message, isError: true };
2145
2259
  }
2146
2260
  const maxEvents = args.maxEvents ?? RESPOND_DEFAULT_MAX_EVENTS;
2147
- const result = sessionManager.pollEventsMonotonic(args.sessionId, args.cursor, maxEvents, {
2261
+ return sessionManager.pollEventsMonotonic(args.sessionId, args.cursor, maxEvents, {
2148
2262
  responseMode,
2149
2263
  pollOptions
2150
2264
  });
2151
- if (args.action === "respond_approval") {
2152
- return addWarning(
2153
- result,
2154
- "Action 'respond_approval' is deprecated, use 'respond_permission'.",
2155
- pollOptions?.maxBytes
2156
- );
2157
- }
2158
- return result;
2159
2265
  }
2160
2266
  case "respond_user_input": {
2161
2267
  if (!args.requestId || !args.answers) {
@@ -2183,23 +2289,6 @@ function executeCodexCheck(args, sessionManager) {
2183
2289
  };
2184
2290
  }
2185
2291
  }
2186
- function addWarning(result, warning, maxBytes) {
2187
- if (!result.compatWarnings) result.compatWarnings = [];
2188
- result.compatWarnings.push(warning);
2189
- if (typeof maxBytes !== "number") {
2190
- return result;
2191
- }
2192
- const normalizedMaxBytes = Math.max(1, Math.floor(maxBytes));
2193
- if (Buffer.byteLength(JSON.stringify(result), "utf8") <= normalizedMaxBytes) {
2194
- return result;
2195
- }
2196
- if (result.compatWarnings.length > 1) {
2197
- result.compatWarnings.pop();
2198
- } else {
2199
- result.compatWarnings = void 0;
2200
- }
2201
- return result;
2202
- }
2203
2292
 
2204
2293
  // src/resources/register-resources.ts
2205
2294
  import { spawnSync } from "child_process";
@@ -2465,7 +2554,6 @@ function buildGotchasText() {
2465
2554
  `- Poll default is \`maxEvents=${POLL_DEFAULT_MAX_EVENTS}\` (authoritative: tool schema / constants).`,
2466
2555
  `- Poll enforces minimum \`maxEvents=${POLL_MIN_MAX_EVENTS}\`; sending \`0\` is normalized to \`${POLL_MIN_MAX_EVENTS}\`.`,
2467
2556
  `- \`respond_permission\` and \`respond_user_input\` default to compact ACK with \`maxEvents=${RESPOND_DEFAULT_MAX_EVENTS}\`.`,
2468
- "- `respond_approval` is a deprecated alias for `respond_permission`.",
2469
2557
  "- Default response mode is `minimal`; use `full` if you need full raw event payloads.",
2470
2558
  "- respond_* uses monotonic cursor handling: `max(cursor, sessionLastCursor)`.",
2471
2559
  "- If `cursorResetTo` is present, your cursor is stale (old events were evicted); restart from that value.",
@@ -2771,7 +2859,7 @@ function registerResources(server, deps) {
2771
2859
  }
2772
2860
 
2773
2861
  // src/server.ts
2774
- var SERVER_VERSION = true ? "0.2.0" : "0.0.0-dev";
2862
+ var SERVER_VERSION = true ? "2.0.0" : "0.0.0-dev";
2775
2863
  function formatErrorMessage(err) {
2776
2864
  const message = err instanceof Error ? err.message : String(err);
2777
2865
  const m = /^Error \[([A-Z_]+)\]:\s*(.*)$/.exec(message);
@@ -2922,16 +3010,17 @@ function createServer(serverCwd) {
2922
3010
  "codex_session",
2923
3011
  {
2924
3012
  title: "Manage Sessions",
2925
- description: `Session actions: list, get, cancel, interrupt, fork.
3013
+ description: `Session actions: list, get, cancel, interrupt, fork, clean_background_terminals.
2926
3014
 
2927
3015
  - list: sessions in memory.
2928
3016
  - get: details. includeSensitive defaults to false; true adds threadId/cwd/profile/config.
2929
3017
  - cancel: terminal.
2930
3018
  - interrupt: stop current turn.
2931
- - fork: clone current thread into a new session; source remains unchanged.`,
3019
+ - fork: clone current thread into a new session; source remains unchanged.
3020
+ - clean_background_terminals: ask app-server to clean stale background terminals for this thread.`,
2932
3021
  inputSchema: {
2933
3022
  action: z.enum(SESSION_ACTIONS),
2934
- sessionId: z.string().optional().describe("Required for get/cancel/interrupt/fork"),
3023
+ sessionId: z.string().optional().describe("Required for get/cancel/interrupt/fork/clean_background_terminals"),
2935
3024
  includeSensitive: z.boolean().default(false).optional().describe("Include cwd/config/threadId/profile in get (default: false)")
2936
3025
  },
2937
3026
  outputSchema: {
@@ -3000,8 +3089,6 @@ poll: events since cursor. Default maxEvents=${POLL_DEFAULT_MAX_EVENTS}.
3000
3089
 
3001
3090
  respond_permission: approval decision. Default maxEvents=${RESPOND_DEFAULT_MAX_EVENTS} (compact ACK).
3002
3091
 
3003
- respond_approval: deprecated alias for respond_permission.
3004
-
3005
3092
  respond_user_input: user-input answers. Default maxEvents=${RESPOND_DEFAULT_MAX_EVENTS} (compact ACK).
3006
3093
 
3007
3094
  events[].type is coarse-grained; details are in events[].data.method.
@@ -3018,15 +3105,14 @@ cursor omitted => use session last cursor. cursorResetTo => reset and continue.`
3018
3105
  includeEvents: z.boolean().optional().describe("Default: true. Include events[] in response."),
3019
3106
  includeActions: z.boolean().optional().describe("Default: true. Include actions[] in response."),
3020
3107
  includeResult: z.boolean().optional().describe("Default: true. Include result in response."),
3021
- maxBytes: z.number().int().positive().optional().describe("Default: unlimited. Best-effort response payload cap in bytes."),
3022
- includeTools: z.boolean().optional().describe("Default: false. Reserved for future dynamic tool metadata support.")
3108
+ maxBytes: z.number().int().positive().optional().describe("Default: unlimited. Best-effort response payload cap in bytes.")
3023
3109
  }).optional().describe("Optional poll shaping controls."),
3024
- // respond_permission/respond_approval
3110
+ // respond_permission
3025
3111
  requestId: z.string().optional().describe("Request ID from actions[]"),
3026
3112
  decision: z.enum(ALL_DECISIONS).optional().describe(
3027
- "Approval decision for respond_permission/respond_approval. acceptWithExecpolicyAmendment requires execpolicyAmendment."
3113
+ "Approval decision for respond_permission. acceptWithExecpolicyAmendment requires execpolicy_amendment."
3028
3114
  ),
3029
- execpolicyAmendment: z.array(z.string()).optional().describe("For acceptWithExecpolicyAmendment only"),
3115
+ execpolicy_amendment: z.array(z.string()).optional().describe("For acceptWithExecpolicyAmendment only"),
3030
3116
  denyMessage: z.string().optional().describe("Deny reason (not sent to agent)"),
3031
3117
  // respond_user_input
3032
3118
  answers: z.record(