@leo000001/codex-mcp 2.0.0 → 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/CHANGELOG.md +10 -1
- package/README.md +5 -2
- package/dist/index.js +215 -51
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -25,7 +25,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
25
25
|
- Documentation aligned with implementation details for event eviction and e2e guidance
|
|
26
26
|
- Tool input defaults are now defined in schema (`cursor`, `maxEvents`, `includeSensitive`, `advanced.approvalTimeoutMs`) and client-facing text avoids duplicated default descriptions
|
|
27
27
|
- `codex_session` adds `clean_background_terminals` action to call `thread/background_terminals/clean`
|
|
28
|
-
- Approval action payloads now expose `approvalId`
|
|
28
|
+
- Approval action payloads now expose `approvalId` when provided by app-server
|
|
29
|
+
- Documentation was de-duplicated by splitting responsibilities between `AGENTS.md` (execution handbook) and `docs/DESIGN.md` (single source upgrade playbook), and a one-shot schema refresh runbook/record was added (`2026-02-21`, no schema diff)
|
|
30
|
+
- `src/app-server/protocol.ts` is now aligned to current v2 schema coverage for thread/turn params (`dynamicTools`, `persistExtendedHistory`, `collaborationMode`, richer `SandboxPolicy`, strict `UserInput` union, `turn/steer` params)
|
|
31
|
+
- `codex_check` input validation is now action-aware at schema level (`poll` vs `respond_permission` vs `respond_user_input`), including conditional `execpolicy_amendment` rules and forbidden-field checks
|
|
32
|
+
- Auth refresh request handling now uses explicit unsupported semantics (`-32000`) instead of `-32601` for `account/chatgptAuthTokens/refresh`
|
|
33
|
+
- Compatibility policy is now explicitly strict: removed non-essential alias compatibility (`approval_id`, `network_approval_context`, `questionId`) and documented a single necessary-compatibility whitelist (v1/v2 thread/turn id extraction)
|
|
34
|
+
- Command approval context is now surfaced directly in `actions[]` / `approval_request` payloads (`commandActions`, `proposedExecpolicyAmendment`) for richer client-side approval UX
|
|
35
|
+
- `turn/started` and `turn/completed` notification handling now only uses canonical `turn.id` shape (plus runtime `activeTurnId` fallback), and corresponding v1 top-level `turnId` compatibility tests were removed
|
|
36
|
+
- `compat-report` now correctly advertises `respondApprovalAlias: false` to match strict no-alias behavior
|
|
37
|
+
- Upgrade-policy docs were further de-duplicated: `docs/DESIGN.md` remains the single detailed compatibility source, and `AGENTS.md` now stays as a concise execution gate
|
|
29
38
|
|
|
30
39
|
## [0.1.0] - 2026-02-15
|
|
31
40
|
|
package/README.md
CHANGED
|
@@ -220,7 +220,7 @@ Query a running session for events, respond to approval requests, or answer user
|
|
|
220
220
|
| `decision` | string | For respond_permission | For command approvals: `"accept"`, `"acceptForSession"`, `"acceptWithExecpolicyAmendment"`, `"decline"`, `"cancel"`; for file changes: `"accept"`, `"acceptForSession"`, `"decline"`, `"cancel"` |
|
|
221
221
|
| `execpolicy_amendment` | string[] | For acceptWithExecpolicyAmendment | Exec policy amendment list (required when `decision="acceptWithExecpolicyAmendment"`) |
|
|
222
222
|
| `denyMessage` | string | No | Internal note on deny (not sent to app-server) |
|
|
223
|
-
| `answers` | object | For respond_user_input | For `respond_user_input`: `
|
|
223
|
+
| `answers` | object | For respond_user_input | For `respond_user_input`: `question-id -> { answers: string[] }` |
|
|
224
224
|
|
|
225
225
|
**Returns (poll and respond_*):** `{ sessionId, status, pollInterval?, cursorResetTo?, events, nextCursor, actions?, result? }`
|
|
226
226
|
|
|
@@ -267,10 +267,13 @@ When the agent requests approval or user input, `poll` includes an `actions[]` l
|
|
|
267
267
|
|
|
268
268
|
- `respond_permission`: `decision` is one of `accept`, `acceptForSession`, `decline`, `cancel`.
|
|
269
269
|
- For command approvals, `acceptWithExecpolicyAmendment` is supported and requires `execpolicy_amendment`.
|
|
270
|
-
- `respond_user_input`: send `answers` keyed by `
|
|
270
|
+
- `respond_user_input`: send `answers` keyed by the question `id`.
|
|
271
|
+
- For command approvals, `actions[]` may include `commandActions` and `proposedExecpolicyAmendment` for richer review UI.
|
|
271
272
|
|
|
272
273
|
Pending approvals auto-decline after `advanced.approvalTimeoutMs`.
|
|
273
274
|
|
|
275
|
+
Auth callback note: if app-server sends `account/chatgptAuthTokens/refresh`, codex-mcp returns JSON-RPC error `-32000` because external ChatGPT token refresh is out of scope for this server.
|
|
276
|
+
|
|
274
277
|
## Session Lifecycle & Cleanup
|
|
275
278
|
|
|
276
279
|
Sessions auto-clean up in the background:
|
package/dist/index.js
CHANGED
|
@@ -233,7 +233,7 @@ var DEFAULT_TERMINAL_CLEANUP_MS = 5 * 60 * 1e3;
|
|
|
233
233
|
var CLEANUP_INTERVAL_MS = 6e4;
|
|
234
234
|
|
|
235
235
|
// src/app-server/client.ts
|
|
236
|
-
var CLIENT_VERSION = true ? "2.0.
|
|
236
|
+
var CLIENT_VERSION = true ? "2.0.2" : "0.0.0-dev";
|
|
237
237
|
var DEFAULT_REQUEST_TIMEOUT = 3e4;
|
|
238
238
|
var STARTUP_REQUEST_TIMEOUT = 9e4;
|
|
239
239
|
var MAX_WRITE_QUEUE_BYTES = 5 * 1024 * 1024;
|
|
@@ -682,6 +682,9 @@ var COALESCED_PROGRESS_DELTA_METHODS = /* @__PURE__ */ new Set([
|
|
|
682
682
|
Methods.REASONING_SUMMARY_DELTA
|
|
683
683
|
]);
|
|
684
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";
|
|
685
688
|
var NOISE_FILTER_ENABLED = process.env.CODEX_MCP_DISABLE_NOISE_FILTER !== "1";
|
|
686
689
|
var WINDOWS_TERMINAL_INTEGRATION_PREFIX = `${String.fromCharCode(27)}]633;`;
|
|
687
690
|
var SHELL_NOISE_LINE_PATTERNS = [
|
|
@@ -1114,7 +1117,8 @@ var SessionManager = class {
|
|
|
1114
1117
|
itemId: req.itemId,
|
|
1115
1118
|
reason: req.reason,
|
|
1116
1119
|
approvalId: req.approvalId,
|
|
1117
|
-
|
|
1120
|
+
commandActions: req.commandActions,
|
|
1121
|
+
proposedExecpolicyAmendment: req.proposedExecpolicyAmendment,
|
|
1118
1122
|
createdAt: req.createdAt
|
|
1119
1123
|
});
|
|
1120
1124
|
}
|
|
@@ -1156,6 +1160,9 @@ var SessionManager = class {
|
|
|
1156
1160
|
while (result.actions.length > 1 && payloadByteSize(result) > normalizedMaxBytes) {
|
|
1157
1161
|
result.actions.pop();
|
|
1158
1162
|
}
|
|
1163
|
+
if (payloadByteSize(result) > normalizedMaxBytes) {
|
|
1164
|
+
result.actions = compactActionsToMinimum(result.actions);
|
|
1165
|
+
}
|
|
1159
1166
|
truncatedFields.push("actions");
|
|
1160
1167
|
}
|
|
1161
1168
|
if (typeof result.actions !== "undefined" && payloadByteSize(result) > normalizedMaxBytes) {
|
|
@@ -1256,7 +1263,6 @@ var SessionManager = class {
|
|
|
1256
1263
|
requestId,
|
|
1257
1264
|
kind: req.kind,
|
|
1258
1265
|
approvalId: req.approvalId,
|
|
1259
|
-
networkApprovalContext: req.networkApprovalContext,
|
|
1260
1266
|
decision,
|
|
1261
1267
|
denyMessage: extra?.denyMessage
|
|
1262
1268
|
},
|
|
@@ -1291,7 +1297,6 @@ var SessionManager = class {
|
|
|
1291
1297
|
requestId,
|
|
1292
1298
|
kind: "user_input",
|
|
1293
1299
|
approvalId: req.approvalId,
|
|
1294
|
-
networkApprovalContext: req.networkApprovalContext,
|
|
1295
1300
|
answers
|
|
1296
1301
|
},
|
|
1297
1302
|
true
|
|
@@ -1369,7 +1374,7 @@ var SessionManager = class {
|
|
|
1369
1374
|
{
|
|
1370
1375
|
const turnObj = p.turn;
|
|
1371
1376
|
const status = normalizeOptionalString(turnObj?.status);
|
|
1372
|
-
session.activeTurnId = normalizeOptionalString(turnObj?.id)
|
|
1377
|
+
session.activeTurnId = normalizeOptionalString(turnObj?.id);
|
|
1373
1378
|
pushEvent(session.eventBuffer, "progress", {
|
|
1374
1379
|
method,
|
|
1375
1380
|
...p,
|
|
@@ -1381,7 +1386,7 @@ var SessionManager = class {
|
|
|
1381
1386
|
case Methods.TURN_COMPLETED: {
|
|
1382
1387
|
if (session.status === "cancelled") break;
|
|
1383
1388
|
const turnObj = p.turn;
|
|
1384
|
-
const completedTurnId =
|
|
1389
|
+
const completedTurnId = turnObj?.id ?? session.activeTurnId ?? "";
|
|
1385
1390
|
session.status = "idle";
|
|
1386
1391
|
session.activeTurnId = void 0;
|
|
1387
1392
|
session.lastResult = {
|
|
@@ -1491,10 +1496,11 @@ var SessionManager = class {
|
|
|
1491
1496
|
const requestId = `req_${randomUUID().slice(0, 8)}`;
|
|
1492
1497
|
const approvalParams = params;
|
|
1493
1498
|
const reason = normalizeOptionalString(approvalParams.reason);
|
|
1494
|
-
const approvalId = normalizeOptionalString(
|
|
1495
|
-
|
|
1499
|
+
const approvalId = normalizeOptionalString(approvalParams.approvalId);
|
|
1500
|
+
const commandActions = Array.isArray(approvalParams.commandActions) ? approvalParams.commandActions : null;
|
|
1501
|
+
const proposedExecpolicyAmendment = normalizeStringArrayOrNull(
|
|
1502
|
+
approvalParams.proposedExecpolicyAmendment
|
|
1496
1503
|
);
|
|
1497
|
-
const networkApprovalContext = approvalParams.networkApprovalContext ?? approvalParams.network_approval_context;
|
|
1498
1504
|
const pending = {
|
|
1499
1505
|
requestId,
|
|
1500
1506
|
kind: "command",
|
|
@@ -1504,7 +1510,8 @@ var SessionManager = class {
|
|
|
1504
1510
|
turnId: normalizeOptionalString(approvalParams.turnId) ?? "",
|
|
1505
1511
|
reason,
|
|
1506
1512
|
approvalId,
|
|
1507
|
-
|
|
1513
|
+
commandActions,
|
|
1514
|
+
proposedExecpolicyAmendment,
|
|
1508
1515
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1509
1516
|
resolved: false,
|
|
1510
1517
|
respond: (result) => client.respondToServer(id, result)
|
|
@@ -1551,7 +1558,8 @@ var SessionManager = class {
|
|
|
1551
1558
|
command: approvalParams.command,
|
|
1552
1559
|
cwd: approvalParams.cwd,
|
|
1553
1560
|
reason,
|
|
1554
|
-
|
|
1561
|
+
commandActions,
|
|
1562
|
+
proposedExecpolicyAmendment
|
|
1555
1563
|
},
|
|
1556
1564
|
true
|
|
1557
1565
|
);
|
|
@@ -1675,7 +1683,11 @@ var SessionManager = class {
|
|
|
1675
1683
|
});
|
|
1676
1684
|
break;
|
|
1677
1685
|
case Methods.AUTH_TOKEN_REFRESH:
|
|
1678
|
-
client.respondErrorToServer(
|
|
1686
|
+
client.respondErrorToServer(
|
|
1687
|
+
id,
|
|
1688
|
+
AUTH_REFRESH_UNSUPPORTED_CODE,
|
|
1689
|
+
AUTH_REFRESH_UNSUPPORTED_MESSAGE
|
|
1690
|
+
);
|
|
1679
1691
|
break;
|
|
1680
1692
|
case Methods.LEGACY_PATCH_APPROVAL:
|
|
1681
1693
|
case Methods.LEGACY_EXEC_APPROVAL:
|
|
@@ -1824,7 +1836,7 @@ function respondToTerminalSessionRequest(client, id, method) {
|
|
|
1824
1836
|
});
|
|
1825
1837
|
break;
|
|
1826
1838
|
case Methods.AUTH_TOKEN_REFRESH:
|
|
1827
|
-
client.respondErrorToServer(id,
|
|
1839
|
+
client.respondErrorToServer(id, AUTH_REFRESH_UNSUPPORTED_CODE, AUTH_REFRESH_TERMINAL_MESSAGE);
|
|
1828
1840
|
break;
|
|
1829
1841
|
case Methods.LEGACY_PATCH_APPROVAL:
|
|
1830
1842
|
case Methods.LEGACY_EXEC_APPROVAL:
|
|
@@ -1838,6 +1850,11 @@ function respondToTerminalSessionRequest(client, id, method) {
|
|
|
1838
1850
|
function normalizeOptionalString(value) {
|
|
1839
1851
|
return typeof value === "string" ? value : void 0;
|
|
1840
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
|
+
}
|
|
1841
1858
|
function sendPendingRequestResponseOrThrow(req, response, sessionId, requestId) {
|
|
1842
1859
|
if (!req.respond) {
|
|
1843
1860
|
throw new Error(
|
|
@@ -1859,7 +1876,9 @@ function compactActionsForBudget(actions) {
|
|
|
1859
1876
|
kind: action.kind,
|
|
1860
1877
|
params: compactActionParamsForBudget(action),
|
|
1861
1878
|
itemId: action.itemId,
|
|
1862
|
-
createdAt: action.createdAt
|
|
1879
|
+
createdAt: action.createdAt,
|
|
1880
|
+
commandActions: action.commandActions,
|
|
1881
|
+
proposedExecpolicyAmendment: action.proposedExecpolicyAmendment
|
|
1863
1882
|
}));
|
|
1864
1883
|
}
|
|
1865
1884
|
function compactActionParamsForBudget(action) {
|
|
@@ -1872,12 +1891,30 @@ function compactActionParamsForBudget(action) {
|
|
|
1872
1891
|
}
|
|
1873
1892
|
const compactQuestions = [];
|
|
1874
1893
|
for (const entry of rawQuestions) {
|
|
1875
|
-
if (isRecord(entry)
|
|
1876
|
-
|
|
1894
|
+
if (!isRecord(entry)) continue;
|
|
1895
|
+
const id = typeof entry.id === "string" ? entry.id : void 0;
|
|
1896
|
+
if (id) {
|
|
1897
|
+
compactQuestions.push({ id });
|
|
1877
1898
|
}
|
|
1878
1899
|
}
|
|
1879
1900
|
return compactQuestions.length > 0 ? { questions: compactQuestions } : void 0;
|
|
1880
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
|
+
}
|
|
1881
1918
|
function clampCursorToLatest(cursor, latestCursor) {
|
|
1882
1919
|
return Math.max(0, Math.min(cursor, latestCursor));
|
|
1883
1920
|
}
|
|
@@ -2235,7 +2272,13 @@ function executeCodexCheck(args, sessionManager) {
|
|
|
2235
2272
|
const pollOptions = args.pollOptions;
|
|
2236
2273
|
switch (args.action) {
|
|
2237
2274
|
case "poll": {
|
|
2238
|
-
|
|
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;
|
|
2239
2282
|
return sessionManager.pollEvents(args.sessionId, args.cursor, maxEvents, {
|
|
2240
2283
|
responseMode,
|
|
2241
2284
|
pollOptions
|
|
@@ -2248,6 +2291,31 @@ function executeCodexCheck(args, sessionManager) {
|
|
|
2248
2291
|
isError: true
|
|
2249
2292
|
};
|
|
2250
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
|
+
}
|
|
2251
2319
|
try {
|
|
2252
2320
|
sessionManager.resolveApproval(args.sessionId, args.requestId, args.decision, {
|
|
2253
2321
|
execpolicy_amendment: args.execpolicy_amendment,
|
|
@@ -2257,7 +2325,7 @@ function executeCodexCheck(args, sessionManager) {
|
|
|
2257
2325
|
const message = err instanceof Error ? err.message : String(err);
|
|
2258
2326
|
return { error: message, isError: true };
|
|
2259
2327
|
}
|
|
2260
|
-
const maxEvents = args.maxEvents
|
|
2328
|
+
const maxEvents = typeof args.maxEvents === "number" ? Math.max(0, Math.floor(args.maxEvents)) : RESPOND_DEFAULT_MAX_EVENTS;
|
|
2261
2329
|
return sessionManager.pollEventsMonotonic(args.sessionId, args.cursor, maxEvents, {
|
|
2262
2330
|
responseMode,
|
|
2263
2331
|
pollOptions
|
|
@@ -2270,13 +2338,19 @@ function executeCodexCheck(args, sessionManager) {
|
|
|
2270
2338
|
isError: true
|
|
2271
2339
|
};
|
|
2272
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
|
+
}
|
|
2273
2347
|
try {
|
|
2274
2348
|
sessionManager.resolveUserInput(args.sessionId, args.requestId, args.answers);
|
|
2275
2349
|
} catch (err) {
|
|
2276
2350
|
const message = err instanceof Error ? err.message : String(err);
|
|
2277
2351
|
return { error: message, isError: true };
|
|
2278
2352
|
}
|
|
2279
|
-
const maxEvents = args.maxEvents
|
|
2353
|
+
const maxEvents = typeof args.maxEvents === "number" ? Math.max(0, Math.floor(args.maxEvents)) : RESPOND_DEFAULT_MAX_EVENTS;
|
|
2280
2354
|
return sessionManager.pollEventsMonotonic(args.sessionId, args.cursor, maxEvents, {
|
|
2281
2355
|
responseMode,
|
|
2282
2356
|
pollOptions
|
|
@@ -2563,6 +2637,7 @@ function buildGotchasText() {
|
|
|
2563
2637
|
`- Pending approvals/user-input auto-decline after \`approvalTimeoutMs\` (default ${DEFAULT_APPROVAL_TIMEOUT_MS} ms).`,
|
|
2564
2638
|
"- `untrusted` behavior is enforced by Codex CLI backend and may auto-allow some low-risk commands.",
|
|
2565
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.`,
|
|
2566
2641
|
"",
|
|
2567
2642
|
"## Event model",
|
|
2568
2643
|
"",
|
|
@@ -2632,6 +2707,7 @@ function buildQuickstartText() {
|
|
|
2632
2707
|
"",
|
|
2633
2708
|
"- Use `pollInterval` as a minimum delay: `running` >=120000ms (and usually longer for big tasks).",
|
|
2634
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.`,
|
|
2635
2711
|
"",
|
|
2636
2712
|
"3. If `actions[]` contains an approval request, respond:",
|
|
2637
2713
|
"",
|
|
@@ -2703,7 +2779,7 @@ function buildCompatReport(deps, codexCliVersion) {
|
|
|
2703
2779
|
schemaVersion: "1.0.0",
|
|
2704
2780
|
features: {
|
|
2705
2781
|
respondPermission: true,
|
|
2706
|
-
respondApprovalAlias:
|
|
2782
|
+
respondApprovalAlias: false,
|
|
2707
2783
|
respondUserInput: true,
|
|
2708
2784
|
sessionInterrupt: true,
|
|
2709
2785
|
responseModeMinimal: true,
|
|
@@ -2859,7 +2935,7 @@ function registerResources(server, deps) {
|
|
|
2859
2935
|
}
|
|
2860
2936
|
|
|
2861
2937
|
// src/server.ts
|
|
2862
|
-
var SERVER_VERSION = true ? "2.0.
|
|
2938
|
+
var SERVER_VERSION = true ? "2.0.2" : "0.0.0-dev";
|
|
2863
2939
|
function formatErrorMessage(err) {
|
|
2864
2940
|
const message = err instanceof Error ? err.message : String(err);
|
|
2865
2941
|
const m = /^Error \[([A-Z_]+)\]:\s*(.*)$/.exec(message);
|
|
@@ -2910,6 +2986,119 @@ function createServer(serverCwd) {
|
|
|
2910
2986
|
),
|
|
2911
2987
|
...errorOutputShape
|
|
2912
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
|
+
});
|
|
2913
3102
|
server.registerTool(
|
|
2914
3103
|
"codex",
|
|
2915
3104
|
{
|
|
@@ -3093,35 +3282,7 @@ respond_user_input: user-input answers. Default maxEvents=${RESPOND_DEFAULT_MAX_
|
|
|
3093
3282
|
|
|
3094
3283
|
events[].type is coarse-grained; details are in events[].data.method.
|
|
3095
3284
|
cursor omitted => use session last cursor. cursorResetTo => reset and continue.`,
|
|
3096
|
-
inputSchema:
|
|
3097
|
-
action: z.enum(CHECK_ACTIONS),
|
|
3098
|
-
sessionId: z.string().describe("Target session ID"),
|
|
3099
|
-
cursor: z.number().int().nonnegative().optional().describe("Event cursor (default: session last consumed cursor)."),
|
|
3100
|
-
maxEvents: z.number().int().nonnegative().optional().describe(
|
|
3101
|
-
`Max events. Default: poll=${POLL_DEFAULT_MAX_EVENTS} (min ${POLL_MIN_MAX_EVENTS}), respond_*=${RESPOND_DEFAULT_MAX_EVENTS}.`
|
|
3102
|
-
),
|
|
3103
|
-
responseMode: z.enum(RESPONSE_MODES).optional().describe("Response mode. Default: minimal. Options: minimal/delta_compact/full."),
|
|
3104
|
-
pollOptions: z.object({
|
|
3105
|
-
includeEvents: z.boolean().optional().describe("Default: true. Include events[] in response."),
|
|
3106
|
-
includeActions: z.boolean().optional().describe("Default: true. Include actions[] in response."),
|
|
3107
|
-
includeResult: z.boolean().optional().describe("Default: true. Include result in response."),
|
|
3108
|
-
maxBytes: z.number().int().positive().optional().describe("Default: unlimited. Best-effort response payload cap in bytes.")
|
|
3109
|
-
}).optional().describe("Optional poll shaping controls."),
|
|
3110
|
-
// respond_permission
|
|
3111
|
-
requestId: z.string().optional().describe("Request ID from actions[]"),
|
|
3112
|
-
decision: z.enum(ALL_DECISIONS).optional().describe(
|
|
3113
|
-
"Approval decision for respond_permission. acceptWithExecpolicyAmendment requires execpolicy_amendment."
|
|
3114
|
-
),
|
|
3115
|
-
execpolicy_amendment: z.array(z.string()).optional().describe("For acceptWithExecpolicyAmendment only"),
|
|
3116
|
-
denyMessage: z.string().optional().describe("Deny reason (not sent to agent)"),
|
|
3117
|
-
// respond_user_input
|
|
3118
|
-
answers: z.record(
|
|
3119
|
-
z.string(),
|
|
3120
|
-
z.object({
|
|
3121
|
-
answers: z.array(z.string())
|
|
3122
|
-
})
|
|
3123
|
-
).optional().describe("questionId -> answers map (questionId from actions[] user_input request).")
|
|
3124
|
-
},
|
|
3285
|
+
inputSchema: codexCheckInputSchema,
|
|
3125
3286
|
outputSchema: {
|
|
3126
3287
|
sessionId: z.string().optional(),
|
|
3127
3288
|
status: z.enum(["running", "idle", "waiting_approval", "error", "cancelled"]).optional(),
|
|
@@ -3149,10 +3310,13 @@ cursor omitted => use session last cursor. cursorResetTo => reset and continue.`
|
|
|
3149
3310
|
z.object({
|
|
3150
3311
|
type: z.enum(["approval", "user_input"]),
|
|
3151
3312
|
requestId: z.string(),
|
|
3152
|
-
kind: z.
|
|
3313
|
+
kind: z.enum(["command", "fileChange", "user_input"]),
|
|
3153
3314
|
params: z.unknown(),
|
|
3154
3315
|
itemId: z.string(),
|
|
3155
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(),
|
|
3156
3320
|
createdAt: z.string()
|
|
3157
3321
|
})
|
|
3158
3322
|
).optional(),
|