@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 +3 -0
- package/README.md +33 -27
- package/dist/index.js +161 -75
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
-
###
|
|
60
|
+
### Claude Code
|
|
61
61
|
|
|
62
62
|
```bash
|
|
63
|
-
|
|
63
|
+
claude mcp add codex-mcp -- npx -y @leo000001/codex-mcp
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
-
Or add to `~/.
|
|
66
|
+
Or add to `~/.claude/settings.json`:
|
|
67
67
|
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
|
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 `"
|
|
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"`,
|
|
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
|
-
| `
|
|
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 `
|
|
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 = [
|
|
168
|
-
|
|
169
|
-
"
|
|
170
|
-
"
|
|
171
|
-
"
|
|
172
|
-
"
|
|
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 ? "
|
|
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?.
|
|
1221
|
+
if (decision === "acceptWithExecpolicyAmendment" && (!extra?.execpolicy_amendment || extra.execpolicy_amendment.length === 0)) {
|
|
1183
1222
|
throw new Error(
|
|
1184
|
-
`Error [${"INVALID_ARGUMENT" /* INVALID_ARGUMENT */}]:
|
|
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?.
|
|
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
|
-
|
|
1307
|
-
|
|
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(
|
|
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, {
|
|
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
|
|
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:
|
|
1406
|
-
threadId:
|
|
1407
|
-
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
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
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,
|
|
2101
|
+
function buildCommandApprovalResponse(decision, execpolicy_amendment) {
|
|
1999
2102
|
if (decision === "acceptWithExecpolicyAmendment") {
|
|
2000
|
-
if (!
|
|
2103
|
+
if (!execpolicy_amendment || execpolicy_amendment.length === 0) {
|
|
2001
2104
|
throw new Error(
|
|
2002
|
-
`Error [${"INVALID_ARGUMENT" /* INVALID_ARGUMENT */}]:
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 ? "
|
|
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
|
|
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
|
|
3113
|
+
"Approval decision for respond_permission. acceptWithExecpolicyAmendment requires execpolicy_amendment."
|
|
3028
3114
|
),
|
|
3029
|
-
|
|
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(
|