@junctionpanel/server 0.1.29 → 0.1.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/server/client/daemon-client.d.ts +4 -1
  2. package/dist/server/client/daemon-client.d.ts.map +1 -1
  3. package/dist/server/client/daemon-client.js +29 -0
  4. package/dist/server/client/daemon-client.js.map +1 -1
  5. package/dist/server/server/agent/agent-manager.d.ts +2 -0
  6. package/dist/server/server/agent/agent-manager.d.ts.map +1 -1
  7. package/dist/server/server/agent/agent-manager.js +63 -4
  8. package/dist/server/server/agent/agent-manager.js.map +1 -1
  9. package/dist/server/server/agent/agent-projections.d.ts.map +1 -1
  10. package/dist/server/server/agent/agent-projections.js +9 -2
  11. package/dist/server/server/agent/agent-projections.js.map +1 -1
  12. package/dist/server/server/agent/agent-sdk-types.d.ts +19 -2
  13. package/dist/server/server/agent/agent-sdk-types.d.ts.map +1 -1
  14. package/dist/server/server/agent/agent-sdk-types.js.map +1 -1
  15. package/dist/server/server/agent/agent-storage.d.ts +30 -30
  16. package/dist/server/server/agent/agent-storage.d.ts.map +1 -1
  17. package/dist/server/server/agent/agent-storage.js +33 -1
  18. package/dist/server/server/agent/agent-storage.js.map +1 -1
  19. package/dist/server/server/agent/codex-config.d.ts +12 -0
  20. package/dist/server/server/agent/codex-config.d.ts.map +1 -0
  21. package/dist/server/server/agent/codex-config.js +42 -0
  22. package/dist/server/server/agent/codex-config.js.map +1 -0
  23. package/dist/server/server/agent/mcp-server.js +8 -8
  24. package/dist/server/server/agent/mcp-server.js.map +1 -1
  25. package/dist/server/server/agent/provider-launch-config.d.ts +2 -2
  26. package/dist/server/server/agent/provider-launch-config.d.ts.map +1 -1
  27. package/dist/server/server/agent/provider-launch-config.js +32 -5
  28. package/dist/server/server/agent/provider-launch-config.js.map +1 -1
  29. package/dist/server/server/agent/provider-manifest.js +10 -10
  30. package/dist/server/server/agent/provider-manifest.js.map +1 -1
  31. package/dist/server/server/agent/providers/claude/model-catalog.d.ts +17 -25
  32. package/dist/server/server/agent/providers/claude/model-catalog.d.ts.map +1 -1
  33. package/dist/server/server/agent/providers/claude/model-catalog.js +228 -40
  34. package/dist/server/server/agent/providers/claude/model-catalog.js.map +1 -1
  35. package/dist/server/server/agent/providers/claude-agent.d.ts +2 -1
  36. package/dist/server/server/agent/providers/claude-agent.d.ts.map +1 -1
  37. package/dist/server/server/agent/providers/claude-agent.js +201 -36
  38. package/dist/server/server/agent/providers/claude-agent.js.map +1 -1
  39. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +38 -1
  40. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts.map +1 -1
  41. package/dist/server/server/agent/providers/codex-app-server-agent.js +540 -133
  42. package/dist/server/server/agent/providers/codex-app-server-agent.js.map +1 -1
  43. package/dist/server/server/agent/providers/gemini-agent.d.ts +17 -5
  44. package/dist/server/server/agent/providers/gemini-agent.d.ts.map +1 -1
  45. package/dist/server/server/agent/providers/gemini-agent.js +1040 -482
  46. package/dist/server/server/agent/providers/gemini-agent.js.map +1 -1
  47. package/dist/server/server/agent/providers/opencode-agent.d.ts.map +1 -1
  48. package/dist/server/server/agent/providers/opencode-agent.js +1 -1
  49. package/dist/server/server/agent/providers/opencode-agent.js.map +1 -1
  50. package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts.map +1 -1
  51. package/dist/server/server/session.d.ts +1 -0
  52. package/dist/server/server/session.d.ts.map +1 -1
  53. package/dist/server/server/session.js +54 -3
  54. package/dist/server/server/session.js.map +1 -1
  55. package/dist/server/shared/messages.d.ts +2165 -1353
  56. package/dist/server/shared/messages.d.ts.map +1 -1
  57. package/dist/server/shared/messages.js +35 -0
  58. package/dist/server/shared/messages.js.map +1 -1
  59. package/package.json +3 -2
@@ -7,6 +7,7 @@ import { z } from "zod";
7
7
  import { loadCodexPersistedTimeline } from "./codex-rollout-timeline.js";
8
8
  import { mapCodexRolloutToolCall, mapCodexToolCallFromThreadItem, } from "./codex/tool-call-mapper.js";
9
9
  import { applyProviderEnv, isProviderCommandAvailable, resolveProviderCommandPrefix, } from "../provider-launch-config.js";
10
+ import { buildCodexRuntimeExtra, DEFAULT_CODEX_MODE_ID, isCodexPlanModeEnabled, normalizeCodexModeId, setCodexPlanModeEnabled, } from "../codex-config.js";
10
11
  import { writeImageAttachment } from "./image-attachments.js";
11
12
  const DEFAULT_TIMEOUT_MS = 14 * 24 * 60 * 60 * 1000;
12
13
  const TURN_START_TIMEOUT_MS = 90 * 1000;
@@ -21,39 +22,39 @@ const CODEX_APP_SERVER_CAPABILITIES = {
21
22
  };
22
23
  const CODEX_MODES = [
23
24
  {
24
- id: "read-only",
25
- label: "Read Only",
26
- description: "Read files and answer questions. Manual approval required for edits, commands, or network ops.",
25
+ id: "default",
26
+ label: "Ask",
27
+ description: "Trusted commands run automatically; untrusted commands and edits require approval.",
27
28
  },
28
29
  {
29
- id: "auto",
30
- label: "Auto",
31
- description: "Edit files and run commands but still request approval before escalating scope.",
30
+ id: "acceptEdits",
31
+ label: "Auto Edit",
32
+ description: "Workspace edits and commands run automatically without approval.",
32
33
  },
33
34
  {
34
- id: "full-access",
35
- label: "Full Access",
36
- description: "Edit files, run commands, and access the network without additional prompts.",
35
+ id: "bypassPermissions",
36
+ label: "Bypass",
37
+ description: "Full disk and network access with no approval prompts.",
37
38
  },
38
39
  ];
39
- const DEFAULT_CODEX_MODE_ID = "auto";
40
40
  const MODE_PRESETS = {
41
- "read-only": {
42
- approvalPolicy: "on-request",
43
- sandbox: "read-only",
41
+ default: {
42
+ approvalPolicy: "untrusted",
43
+ sandbox: "workspace-write",
44
44
  },
45
- auto: {
46
- approvalPolicy: "on-request",
45
+ acceptEdits: {
46
+ approvalPolicy: "never",
47
47
  sandbox: "workspace-write",
48
48
  },
49
- "full-access": {
49
+ bypassPermissions: {
50
50
  approvalPolicy: "never",
51
51
  sandbox: "danger-full-access",
52
52
  networkAccess: true,
53
53
  },
54
54
  };
55
55
  function validateCodexMode(modeId) {
56
- if (!(modeId in MODE_PRESETS)) {
56
+ const normalizedModeId = normalizeCodexModeId(modeId);
57
+ if (typeof normalizedModeId !== "string" || !(normalizedModeId in MODE_PRESETS)) {
57
58
  const validModes = Object.keys(MODE_PRESETS).join(", ");
58
59
  throw new Error(`Invalid Codex mode "${modeId}". Valid modes are: ${validModes}`);
59
60
  }
@@ -184,6 +185,13 @@ function toRecord(value) {
184
185
  }
185
186
  return value;
186
187
  }
188
+ function toNonEmptyString(value) {
189
+ if (typeof value !== "string") {
190
+ return null;
191
+ }
192
+ const trimmed = value.trim();
193
+ return trimmed.length > 0 ? trimmed : null;
194
+ }
187
195
  function parseUpdatedQuestionAnswers(updatedInput) {
188
196
  const parsed = {};
189
197
  const root = toRecord(updatedInput);
@@ -218,6 +226,98 @@ function parseUpdatedQuestionAnswers(updatedInput) {
218
226
  }
219
227
  return parsed;
220
228
  }
229
+ function normalizeCodexQuestionOption(value) {
230
+ if (typeof value === "string") {
231
+ const label = value.trim();
232
+ return label ? { label, value: label } : null;
233
+ }
234
+ const record = toRecord(value);
235
+ if (!record) {
236
+ return null;
237
+ }
238
+ const label = toNonEmptyString(record.label) ?? toNonEmptyString(record.value);
239
+ if (!label) {
240
+ return null;
241
+ }
242
+ return {
243
+ label,
244
+ value: toNonEmptyString(record.value) ?? label,
245
+ description: toNonEmptyString(record.description) ?? undefined,
246
+ };
247
+ }
248
+ function normalizeCodexQuestionDescriptor(value, fallbackIndex) {
249
+ const record = toRecord(value);
250
+ if (!record) {
251
+ return null;
252
+ }
253
+ const id = toNonEmptyString(record.id) ??
254
+ toNonEmptyString(record.key) ??
255
+ `question_${fallbackIndex + 1}`;
256
+ const options = Array.isArray(record.options)
257
+ ? record.options
258
+ .map((option) => normalizeCodexQuestionOption(option))
259
+ .filter((option) => option !== null)
260
+ : [];
261
+ return {
262
+ id,
263
+ header: toNonEmptyString(record.header) ?? undefined,
264
+ question: toNonEmptyString(record.question) ??
265
+ toNonEmptyString(record.prompt) ??
266
+ toNonEmptyString(record.text) ??
267
+ undefined,
268
+ isOther: Boolean(record.isOther ?? record.is_other),
269
+ isSecret: Boolean(record.isSecret ?? record.is_secret),
270
+ options,
271
+ };
272
+ }
273
+ function normalizeCodexQuestionDescriptors(value) {
274
+ if (!Array.isArray(value)) {
275
+ return [];
276
+ }
277
+ return value
278
+ .map((question, index) => normalizeCodexQuestionDescriptor(question, index))
279
+ .filter((question) => question !== null);
280
+ }
281
+ function extractExplicitDecision(updatedInput) {
282
+ const root = toRecord(updatedInput);
283
+ if (!root || !Object.prototype.hasOwnProperty.call(root, "decision")) {
284
+ return null;
285
+ }
286
+ return root.decision ?? null;
287
+ }
288
+ function buildCodexPermissionsResponse(response, requestedPermissions) {
289
+ const root = toRecord(response.updatedInput);
290
+ const permissions = toRecord(root?.permissions);
291
+ const scope = root?.scope === "session" ? "session" : "turn";
292
+ if (permissions) {
293
+ return { permissions, scope };
294
+ }
295
+ if (response.behavior === "allow") {
296
+ return { permissions: toRecord(requestedPermissions) ?? {}, scope };
297
+ }
298
+ return { permissions: {}, scope };
299
+ }
300
+ function buildCodexElicitationResponse(response) {
301
+ const root = toRecord(response.updatedInput);
302
+ const explicitAction = root?.action === "accept" || root?.action === "decline" || root?.action === "cancel"
303
+ ? root.action
304
+ : null;
305
+ if (response.behavior === "allow") {
306
+ return {
307
+ action: explicitAction ?? "accept",
308
+ content: root?.content ?? null,
309
+ _meta: root?._meta ?? null,
310
+ };
311
+ }
312
+ return {
313
+ action: explicitAction ?? (response.interrupt ? "cancel" : "decline"),
314
+ content: null,
315
+ _meta: root?._meta ?? null,
316
+ };
317
+ }
318
+ function toPendingPermissionId(requestId) {
319
+ return `permission-${String(requestId)}`;
320
+ }
221
321
  async function listCodexCustomPrompts() {
222
322
  const codexHome = resolveCodexHomeDir();
223
323
  const promptsDir = path.join(codexHome, "prompts");
@@ -524,7 +624,7 @@ class CodexAppServerClient {
524
624
  const request = msg;
525
625
  const handler = this.requestHandlers.get(request.method);
526
626
  try {
527
- const result = handler ? await handler(request.params) : {};
627
+ const result = handler ? await handler(request.params, request.id) : {};
528
628
  const response = { id: request.id, result };
529
629
  this.child.stdin.write(`${JSON.stringify(response)}\n`);
530
630
  }
@@ -581,12 +681,73 @@ function parsePlanTextToTodoItems(text) {
581
681
  completed: false,
582
682
  }));
583
683
  }
684
+ function formatProposedPlanBlock(text) {
685
+ return `<proposed_plan>\n${text}\n</proposed_plan>`;
686
+ }
687
+ function formatProposedPlanChunk(text, options) {
688
+ const parts = [];
689
+ if (options?.open) {
690
+ parts.push("<proposed_plan>\n");
691
+ }
692
+ parts.push(text);
693
+ if (options?.close) {
694
+ parts.push("\n</proposed_plan>");
695
+ }
696
+ return parts.join("");
697
+ }
584
698
  function planStepsToTodoItems(steps) {
585
699
  return steps.map((entry) => ({
586
700
  text: entry.step,
587
701
  completed: entry.status === "completed",
588
702
  }));
589
703
  }
704
+ function supportsPlanCollaborationMode(response) {
705
+ const candidateArrays = [
706
+ Array.isArray(response) ? response : null,
707
+ Array.isArray(response?.data)
708
+ ? (response.data)
709
+ : null,
710
+ Array.isArray(response?.modes)
711
+ ? (response.modes)
712
+ : null,
713
+ Array.isArray(response?.collaborationModes)
714
+ ? (response.collaborationModes)
715
+ : null,
716
+ Array.isArray(response?.items)
717
+ ? (response.items)
718
+ : null,
719
+ ];
720
+ for (const entries of candidateArrays) {
721
+ if (!entries)
722
+ continue;
723
+ for (const entry of entries) {
724
+ const record = toRecord(entry);
725
+ const modeName = (typeof record?.mode === "string" ? record.mode : null) ??
726
+ (typeof record?.name === "string" ? record.name : null) ??
727
+ (typeof record?.id === "string" ? record.id : null) ??
728
+ (typeof entry === "string" ? entry : null);
729
+ if (modeName?.trim().toLowerCase() === "plan") {
730
+ return true;
731
+ }
732
+ }
733
+ }
734
+ return false;
735
+ }
736
+ function shouldRetryInitializeWithoutExperimentalApi(error) {
737
+ const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
738
+ return (message.includes("experimentalapi") ||
739
+ message.includes("experimental api") ||
740
+ message.includes("capabilities") ||
741
+ message.includes("unknown field") ||
742
+ message.includes("invalid params"));
743
+ }
744
+ function shouldRetryTurnStartWithoutCollaborationMode(error) {
745
+ const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
746
+ return (message.includes("collaborationmode") ||
747
+ message.includes("collaboration mode") ||
748
+ message.includes("experimentalapi") ||
749
+ message.includes("experimental api"));
750
+ }
590
751
  function extractPatchLikeText(value) {
591
752
  if (!value || typeof value !== "object") {
592
753
  return undefined;
@@ -896,8 +1057,11 @@ function threadItemToTimeline(item, options) {
896
1057
  }
897
1058
  case "plan": {
898
1059
  const text = normalizedItem.text ?? "";
1060
+ if (typeof text === "string" && text.trim().length > 0) {
1061
+ return { type: "assistant_message", text: formatProposedPlanBlock(text) };
1062
+ }
899
1063
  const items = parsePlanTextToTodoItems(text);
900
- return { type: "todo", items };
1064
+ return items.length > 0 ? { type: "todo", items } : null;
901
1065
  }
902
1066
  case "reasoning": {
903
1067
  const summary = Array.isArray(normalizedItem.summary)
@@ -958,6 +1122,13 @@ const TurnPlanUpdatedNotificationSchema = z.object({
958
1122
  })
959
1123
  .passthrough()),
960
1124
  }).passthrough();
1125
+ const ItemPlanDeltaNotificationSchema = z.object({
1126
+ itemId: z.string(),
1127
+ delta: z.string(),
1128
+ }).passthrough();
1129
+ const ServerRequestResolvedNotificationSchema = z.object({
1130
+ requestId: z.union([z.string(), z.number()]),
1131
+ }).passthrough();
961
1132
  const TurnDiffUpdatedNotificationSchema = z.object({
962
1133
  diff: z.string(),
963
1134
  }).passthrough();
@@ -1097,6 +1268,12 @@ const CodexNotificationSchema = z.union([
1097
1268
  })),
1098
1269
  })),
1099
1270
  z.object({ method: z.literal("turn/plan/updated"), params: z.unknown() }).transform(({ method, params }) => ({ kind: "invalid_payload", method, params })),
1271
+ z.object({ method: z.literal("item/plan/delta"), params: ItemPlanDeltaNotificationSchema }).transform(({ params }) => ({
1272
+ kind: "plan_delta",
1273
+ itemId: params.itemId,
1274
+ delta: params.delta,
1275
+ })),
1276
+ z.object({ method: z.literal("item/plan/delta"), params: z.unknown() }).transform(({ method, params }) => ({ kind: "invalid_payload", method, params })),
1100
1277
  z.object({ method: z.literal("turn/diff/updated"), params: TurnDiffUpdatedNotificationSchema }).transform(({ params }) => ({ kind: "diff_updated", diff: params.diff })),
1101
1278
  z.object({ method: z.literal("turn/diff/updated"), params: z.unknown() }).transform(({ method, params }) => ({ kind: "invalid_payload", method, params })),
1102
1279
  z.object({
@@ -1104,6 +1281,14 @@ const CodexNotificationSchema = z.union([
1104
1281
  params: ThreadTokenUsageUpdatedNotificationSchema,
1105
1282
  }).transform(({ params }) => ({ kind: "token_usage_updated", tokenUsage: params.tokenUsage })),
1106
1283
  z.object({ method: z.literal("thread/tokenUsage/updated"), params: z.unknown() }).transform(({ method, params }) => ({ kind: "invalid_payload", method, params })),
1284
+ z.object({
1285
+ method: z.literal("serverRequest/resolved"),
1286
+ params: ServerRequestResolvedNotificationSchema,
1287
+ }).transform(({ params }) => ({
1288
+ kind: "server_request_resolved",
1289
+ requestId: String(params.requestId),
1290
+ })),
1291
+ z.object({ method: z.literal("serverRequest/resolved"), params: z.unknown() }).transform(({ method, params }) => ({ kind: "invalid_payload", method, params })),
1107
1292
  z.object({ method: z.literal("item/agentMessage/delta"), params: ItemTextDeltaNotificationSchema }).transform(({ params }) => ({
1108
1293
  kind: "agent_message_delta",
1109
1294
  itemId: params.itemId,
@@ -1303,6 +1488,22 @@ export async function codexAppServerTurnInputFromPrompt(prompt, logger) {
1303
1488
  }
1304
1489
  export const __codexAppServerInternals = {
1305
1490
  mapCodexPatchNotificationToToolCall,
1491
+ supportsPlanCollaborationMode,
1492
+ shouldRetryInitializeWithoutExperimentalApi,
1493
+ shouldRetryTurnStartWithoutCollaborationMode,
1494
+ formatProposedPlanBlock,
1495
+ normalizeCodexQuestionDescriptors,
1496
+ parseUpdatedQuestionAnswers,
1497
+ buildCodexPermissionsResponse,
1498
+ buildCodexElicitationResponse,
1499
+ supportedRequestMethods: [
1500
+ "item/commandExecution/requestApproval",
1501
+ "item/fileChange/requestApproval",
1502
+ "tool/requestUserInput",
1503
+ "item/tool/requestUserInput",
1504
+ "item/permissions/requestApproval",
1505
+ "mcpServer/elicitation/request",
1506
+ ],
1306
1507
  };
1307
1508
  class CodexAppServerAgentSession {
1308
1509
  constructor(config, resumeHandle, logger, spawnAppServer) {
@@ -1332,16 +1533,21 @@ class CodexAppServerAgentSession {
1332
1533
  this.warnedInvalidNotificationPayloads = new Set();
1333
1534
  this.warnedIncompleteEditToolCallIds = new Set();
1334
1535
  this.connected = false;
1335
- this.collaborationModes = [];
1336
- this.resolvedCollaborationMode = null;
1536
+ this.nativePlanModeSupported = null;
1537
+ this.pendingPlanTexts = new Map();
1337
1538
  this.cachedSkills = [];
1338
1539
  this.logger = logger.child({ module: "agent", provider: CODEX_PROVIDER });
1339
1540
  if (config.modeId === undefined) {
1340
1541
  throw new Error("Codex agent requires modeId to be specified");
1341
1542
  }
1342
- validateCodexMode(config.modeId);
1343
- this.currentMode = config.modeId;
1344
1543
  this.config = config;
1544
+ const normalizedModeId = normalizeCodexModeId(config.modeId);
1545
+ if (typeof normalizedModeId !== "string") {
1546
+ throw new Error("Codex agent requires a valid modeId");
1547
+ }
1548
+ validateCodexMode(normalizedModeId);
1549
+ this.currentMode = normalizedModeId;
1550
+ this.config.modeId = normalizedModeId;
1345
1551
  this.config.thinkingOptionId = normalizeCodexThinkingOptionId(this.config.thinkingOptionId);
1346
1552
  if (this.resumeHandle?.sessionId) {
1347
1553
  this.currentThreadId = this.resumeHandle.sessionId;
@@ -1358,13 +1564,27 @@ class CodexAppServerAgentSession {
1358
1564
  this.client = new CodexAppServerClient(child, this.logger);
1359
1565
  this.client.setNotificationHandler((method, params) => this.handleNotification(method, params));
1360
1566
  this.registerRequestHandlers();
1361
- await this.client.request("initialize", {
1362
- clientInfo: {
1363
- name: "junction",
1364
- title: "Junction",
1365
- version: "0.0.0",
1366
- },
1367
- });
1567
+ const clientInfo = {
1568
+ name: "junction",
1569
+ title: "Junction",
1570
+ version: "0.0.0",
1571
+ };
1572
+ try {
1573
+ await this.client.request("initialize", {
1574
+ clientInfo,
1575
+ capabilities: {
1576
+ experimentalApi: true,
1577
+ },
1578
+ });
1579
+ }
1580
+ catch (error) {
1581
+ if (!shouldRetryInitializeWithoutExperimentalApi(error)) {
1582
+ throw error;
1583
+ }
1584
+ await this.client.request("initialize", { clientInfo });
1585
+ this.nativePlanModeSupported = false;
1586
+ setCodexPlanModeEnabled(this.config, false);
1587
+ }
1368
1588
  this.client.notify("initialized", {});
1369
1589
  await this.loadCollaborationModes();
1370
1590
  await this.loadSkills();
@@ -1377,22 +1597,22 @@ class CodexAppServerAgentSession {
1377
1597
  async loadCollaborationModes() {
1378
1598
  if (!this.client)
1379
1599
  return;
1600
+ if (this.nativePlanModeSupported === false) {
1601
+ setCodexPlanModeEnabled(this.config, false);
1602
+ return;
1603
+ }
1380
1604
  try {
1381
- const response = (await this.client.request("collaborationMode/list", {}));
1382
- const data = Array.isArray(response?.data) ? response.data : [];
1383
- this.collaborationModes = data.map((entry) => ({
1384
- name: String(entry.name ?? ""),
1385
- mode: entry.mode ?? null,
1386
- model: entry.model ?? null,
1387
- reasoning_effort: entry.reasoning_effort ?? null,
1388
- developer_instructions: entry.developer_instructions ?? null,
1389
- }));
1605
+ const response = await this.client.request("collaborationMode/list", {});
1606
+ this.nativePlanModeSupported = supportsPlanCollaborationMode(response);
1607
+ if (this.nativePlanModeSupported === false) {
1608
+ setCodexPlanModeEnabled(this.config, false);
1609
+ }
1390
1610
  }
1391
1611
  catch (error) {
1392
1612
  this.logger.trace({ error }, "Failed to load collaboration modes");
1393
- this.collaborationModes = [];
1613
+ this.nativePlanModeSupported = false;
1614
+ setCodexPlanModeEnabled(this.config, false);
1394
1615
  }
1395
- this.resolvedCollaborationMode = this.resolveCollaborationMode(this.currentMode);
1396
1616
  }
1397
1617
  async loadSkills() {
1398
1618
  if (!this.client)
@@ -1422,59 +1642,15 @@ class CodexAppServerAgentSession {
1422
1642
  this.cachedSkills = [];
1423
1643
  }
1424
1644
  }
1425
- resolveCollaborationMode(modeId) {
1426
- if (this.collaborationModes.length === 0)
1427
- return null;
1428
- const normalized = modeId.toLowerCase();
1429
- const findByName = (predicate) => this.collaborationModes.find((entry) => predicate(entry.name.toLowerCase()));
1430
- let match;
1431
- if (normalized === "read-only") {
1432
- // Prefer explicit plan collaboration modes over generic read-only modes.
1433
- match =
1434
- findByName((name) => name.includes("plan")) ??
1435
- findByName((name) => name.includes("read"));
1436
- }
1437
- else if (normalized === "full-access") {
1438
- match = findByName((name) => name.includes("full") || name.includes("exec"));
1439
- }
1440
- else {
1441
- match = findByName((name) => name.includes("auto") || name.includes("code"));
1442
- }
1443
- if (!match) {
1444
- match = this.collaborationModes[0] ?? null;
1445
- }
1446
- if (!match)
1447
- return null;
1448
- const settings = {};
1449
- if (match.model)
1450
- settings.model = match.model;
1451
- if (match.reasoning_effort)
1452
- settings.reasoning_effort = match.reasoning_effort;
1453
- const modeSpecificInstruction = normalized === "read-only"
1454
- ? "Plan mode is enabled. Do not execute commands, edit files, or perform write operations. Provide analysis and a step-by-step plan only."
1455
- : "";
1456
- const developerInstructions = [
1457
- modeSpecificInstruction,
1458
- match.developer_instructions?.trim(),
1459
- this.config.systemPrompt?.trim(),
1460
- ]
1461
- .filter((entry) => typeof entry === "string" && entry.length > 0)
1462
- .join("\n\n");
1463
- if (developerInstructions)
1464
- settings.developer_instructions = developerInstructions;
1465
- if (this.config.model)
1466
- settings.model = this.config.model;
1467
- const thinkingOptionId = normalizeCodexThinkingOptionId(this.config.thinkingOptionId);
1468
- if (thinkingOptionId)
1469
- settings.reasoning_effort = thinkingOptionId;
1470
- return { mode: match.mode ?? "code", settings, name: match.name };
1471
- }
1472
1645
  registerRequestHandlers() {
1473
1646
  if (!this.client)
1474
1647
  return;
1475
- this.client.setRequestHandler("item/commandExecution/requestApproval", (params) => this.handleCommandApprovalRequest(params));
1476
- this.client.setRequestHandler("item/fileChange/requestApproval", (params) => this.handleFileChangeApprovalRequest(params));
1477
- this.client.setRequestHandler("tool/requestUserInput", (params) => this.handleToolApprovalRequest(params));
1648
+ this.client.setRequestHandler("item/commandExecution/requestApproval", (params, requestId) => this.handleCommandApprovalRequest("item/commandExecution/requestApproval", params, requestId));
1649
+ this.client.setRequestHandler("item/fileChange/requestApproval", (params, requestId) => this.handleFileChangeApprovalRequest("item/fileChange/requestApproval", params, requestId));
1650
+ this.client.setRequestHandler("tool/requestUserInput", (params, requestId) => this.handleToolApprovalRequest("tool/requestUserInput", params, requestId));
1651
+ this.client.setRequestHandler("item/tool/requestUserInput", (params, requestId) => this.handleToolApprovalRequest("item/tool/requestUserInput", params, requestId));
1652
+ this.client.setRequestHandler("item/permissions/requestApproval", (params, requestId) => this.handlePermissionsApprovalRequest("item/permissions/requestApproval", params, requestId));
1653
+ this.client.setRequestHandler("mcpServer/elicitation/request", (params, requestId) => this.handleMcpElicitationRequest("mcpServer/elicitation/request", params, requestId));
1478
1654
  }
1479
1655
  async loadPersistedHistory() {
1480
1656
  if (!this.client || !this.currentThreadId)
@@ -1672,6 +1848,11 @@ class CodexAppServerAgentSession {
1672
1848
  const preset = MODE_PRESETS[this.currentMode] ?? MODE_PRESETS[DEFAULT_CODEX_MODE_ID];
1673
1849
  const approvalPolicy = this.config.approvalPolicy ?? preset.approvalPolicy;
1674
1850
  const sandboxPolicyType = this.config.sandboxMode ?? preset.sandbox;
1851
+ const explicitPlanMode = options?.extra?.codex?.planMode;
1852
+ const planModeRequested = explicitPlanMode === undefined
1853
+ ? isCodexPlanModeEnabled(this.config)
1854
+ : explicitPlanMode === true;
1855
+ const thinkingOptionId = normalizeCodexThinkingOptionId(this.config.thinkingOptionId);
1675
1856
  const params = {
1676
1857
  threadId: this.currentThreadId,
1677
1858
  input,
@@ -1683,16 +1864,9 @@ class CodexAppServerAgentSession {
1683
1864
  if (this.config.model) {
1684
1865
  params.model = this.config.model;
1685
1866
  }
1686
- const thinkingOptionId = normalizeCodexThinkingOptionId(this.config.thinkingOptionId);
1687
1867
  if (thinkingOptionId) {
1688
1868
  params.effort = thinkingOptionId;
1689
1869
  }
1690
- if (this.resolvedCollaborationMode) {
1691
- params.collaborationMode = {
1692
- mode: this.resolvedCollaborationMode.mode,
1693
- settings: this.resolvedCollaborationMode.settings,
1694
- };
1695
- }
1696
1870
  if (this.config.cwd) {
1697
1871
  params.cwd = this.config.cwd;
1698
1872
  }
@@ -1706,7 +1880,39 @@ class CodexAppServerAgentSession {
1706
1880
  if (codexConfig) {
1707
1881
  params.config = codexConfig;
1708
1882
  }
1709
- await this.client.request("turn/start", params, TURN_START_TIMEOUT_MS);
1883
+ let downgradedFromPlanMode = false;
1884
+ if (this.nativePlanModeSupported !== false) {
1885
+ const collaborationMode = this.buildCollaborationModePayload(planModeRequested ? "plan" : "default");
1886
+ if (collaborationMode) {
1887
+ params.collaborationMode = collaborationMode;
1888
+ }
1889
+ }
1890
+ try {
1891
+ await this.client.request("turn/start", params, TURN_START_TIMEOUT_MS);
1892
+ }
1893
+ catch (error) {
1894
+ const canRetryWithoutPlanMode = Object.prototype.hasOwnProperty.call(params, "collaborationMode") &&
1895
+ shouldRetryTurnStartWithoutCollaborationMode(error);
1896
+ if (!canRetryWithoutPlanMode) {
1897
+ throw error;
1898
+ }
1899
+ delete params.collaborationMode;
1900
+ this.nativePlanModeSupported = false;
1901
+ setCodexPlanModeEnabled(this.config, false);
1902
+ this.cachedRuntimeInfo = null;
1903
+ downgradedFromPlanMode = planModeRequested;
1904
+ await this.client.request("turn/start", params, TURN_START_TIMEOUT_MS);
1905
+ }
1906
+ if (downgradedFromPlanMode) {
1907
+ this.emitEvent({
1908
+ type: "timeline",
1909
+ provider: CODEX_PROVIDER,
1910
+ item: {
1911
+ type: "assistant_message",
1912
+ text: "Plan mode is not supported by this Codex runtime. Sent as a normal turn instead.",
1913
+ },
1914
+ });
1915
+ }
1710
1916
  let sawTurnStarted = false;
1711
1917
  for await (const event of queue) {
1712
1918
  // Drop pre-start timeline noise that can leak from the previous turn.
@@ -1763,9 +1969,10 @@ class CodexAppServerAgentSession {
1763
1969
  model: this.config.model ?? null,
1764
1970
  thinkingOptionId: normalizeCodexThinkingOptionId(this.config.thinkingOptionId) ?? null,
1765
1971
  modeId: this.currentMode ?? null,
1766
- extra: this.resolvedCollaborationMode
1767
- ? { collaborationMode: this.resolvedCollaborationMode.name }
1768
- : undefined,
1972
+ extra: buildCodexRuntimeExtra({
1973
+ planModeSupported: this.nativePlanModeSupported,
1974
+ planModeEnabled: isCodexPlanModeEnabled(this.config),
1975
+ }),
1769
1976
  };
1770
1977
  this.cachedRuntimeInfo = info;
1771
1978
  return { ...info };
@@ -1777,19 +1984,21 @@ class CodexAppServerAgentSession {
1777
1984
  return this.currentMode ?? null;
1778
1985
  }
1779
1986
  async setMode(modeId) {
1780
- validateCodexMode(modeId);
1781
- this.currentMode = modeId;
1782
- this.resolvedCollaborationMode = this.resolveCollaborationMode(modeId);
1987
+ const normalizedModeId = normalizeCodexModeId(modeId);
1988
+ if (typeof normalizedModeId !== "string") {
1989
+ throw new Error(`Invalid Codex mode "${modeId}"`);
1990
+ }
1991
+ validateCodexMode(normalizedModeId);
1992
+ this.currentMode = normalizedModeId;
1993
+ this.config.modeId = normalizedModeId;
1783
1994
  this.cachedRuntimeInfo = null;
1784
1995
  }
1785
1996
  async setModel(modelId) {
1786
1997
  this.config.model = modelId ?? undefined;
1787
- this.resolvedCollaborationMode = this.resolveCollaborationMode(this.currentMode);
1788
1998
  this.cachedRuntimeInfo = null;
1789
1999
  }
1790
2000
  async setThinkingOption(thinkingOptionId) {
1791
2001
  this.config.thinkingOptionId = normalizeCodexThinkingOptionId(thinkingOptionId);
1792
- this.resolvedCollaborationMode = this.resolveCollaborationMode(this.currentMode);
1793
2002
  this.cachedRuntimeInfo = null;
1794
2003
  }
1795
2004
  getPendingPermissions() {
@@ -1838,23 +2047,33 @@ class CodexAppServerAgentSession {
1838
2047
  resolution: response,
1839
2048
  });
1840
2049
  if (pending.kind === "command") {
1841
- const decision = response.behavior === "allow"
1842
- ? "accept"
1843
- : response.interrupt
1844
- ? "cancel"
1845
- : "decline";
2050
+ const decision = extractExplicitDecision(response.updatedInput) ??
2051
+ (response.behavior === "allow"
2052
+ ? "accept"
2053
+ : response.interrupt
2054
+ ? "cancel"
2055
+ : "decline");
1846
2056
  pending.resolve({ decision });
1847
2057
  return;
1848
2058
  }
1849
2059
  if (pending.kind === "file") {
1850
- const decision = response.behavior === "allow"
1851
- ? "accept"
1852
- : response.interrupt
1853
- ? "cancel"
1854
- : "decline";
2060
+ const decision = extractExplicitDecision(response.updatedInput) ??
2061
+ (response.behavior === "allow"
2062
+ ? "accept"
2063
+ : response.interrupt
2064
+ ? "cancel"
2065
+ : "decline");
1855
2066
  pending.resolve({ decision });
1856
2067
  return;
1857
2068
  }
2069
+ if (pending.kind === "permissions") {
2070
+ pending.resolve(buildCodexPermissionsResponse(response, pending.permissions));
2071
+ return;
2072
+ }
2073
+ if (pending.kind === "elicitation") {
2074
+ pending.resolve(buildCodexElicitationResponse(response));
2075
+ return;
2076
+ }
1858
2077
  // tool/requestUserInput
1859
2078
  const answers = {};
1860
2079
  const questions = pending.questions ?? [];
@@ -2015,10 +2234,29 @@ class CodexAppServerAgentSession {
2015
2234
  innerConfig.mcp_servers = mcpServers;
2016
2235
  }
2017
2236
  if (this.config.extra?.codex) {
2018
- Object.assign(innerConfig, this.config.extra.codex);
2237
+ const { planModeEnabled: _planModeEnabled, ...codexExtra } = this.config.extra.codex;
2238
+ Object.assign(innerConfig, codexExtra);
2019
2239
  }
2020
2240
  return Object.keys(innerConfig).length > 0 ? innerConfig : null;
2021
2241
  }
2242
+ buildCollaborationModePayload(mode) {
2243
+ if (this.nativePlanModeSupported !== true) {
2244
+ return null;
2245
+ }
2246
+ const model = normalizeCodexModelId(this.config.model);
2247
+ if (!model) {
2248
+ return null;
2249
+ }
2250
+ const thinkingOptionId = normalizeCodexThinkingOptionId(this.config.thinkingOptionId) ?? null;
2251
+ return {
2252
+ mode,
2253
+ settings: {
2254
+ model,
2255
+ reasoning_effort: thinkingOptionId,
2256
+ developer_instructions: null,
2257
+ },
2258
+ };
2259
+ }
2022
2260
  async buildUserInput(prompt) {
2023
2261
  if (typeof prompt === "string") {
2024
2262
  return [{ type: "text", text: prompt }];
@@ -2073,6 +2311,7 @@ class CodexAppServerAgentSession {
2073
2311
  this.emittedExecCommandCompletedCallIds.clear();
2074
2312
  this.pendingCommandOutputDeltas.clear();
2075
2313
  this.pendingFileChangeOutputDeltas.clear();
2314
+ this.pendingPlanTexts.clear();
2076
2315
  this.warnedIncompleteEditToolCallIds.clear();
2077
2316
  this.eventQueue?.end();
2078
2317
  return;
@@ -2089,6 +2328,22 @@ class CodexAppServerAgentSession {
2089
2328
  });
2090
2329
  return;
2091
2330
  }
2331
+ if (parsed.kind === "plan_delta") {
2332
+ const previous = this.pendingPlanTexts.get(parsed.itemId) ?? "";
2333
+ const next = previous + parsed.delta;
2334
+ this.pendingPlanTexts.set(parsed.itemId, next);
2335
+ this.emitEvent({
2336
+ type: "timeline",
2337
+ provider: CODEX_PROVIDER,
2338
+ item: {
2339
+ type: "assistant_message",
2340
+ text: formatProposedPlanChunk(parsed.delta, {
2341
+ open: previous.length === 0,
2342
+ }),
2343
+ },
2344
+ });
2345
+ return;
2346
+ }
2092
2347
  if (parsed.kind === "diff_updated") {
2093
2348
  // NOTE: Codex app-server emits frequent `turn/diff/updated` notifications
2094
2349
  // containing a full accumulated unified diff for the *entire turn*.
@@ -2096,6 +2351,20 @@ class CodexAppServerAgentSession {
2096
2351
  // We intentionally do NOT store every diff update in the timeline.
2097
2352
  return;
2098
2353
  }
2354
+ if (parsed.kind === "server_request_resolved") {
2355
+ const pendingRequestId = toPendingPermissionId(parsed.requestId);
2356
+ if (this.resolvedPermissionRequests.has(pendingRequestId)) {
2357
+ return;
2358
+ }
2359
+ this.pendingPermissions.delete(pendingRequestId);
2360
+ this.emitEvent({
2361
+ type: "permission_resolved",
2362
+ provider: CODEX_PROVIDER,
2363
+ requestId: pendingRequestId,
2364
+ resolution: { behavior: "allow" },
2365
+ });
2366
+ return;
2367
+ }
2099
2368
  if (parsed.kind === "token_usage_updated") {
2100
2369
  this.latestUsage = toAgentUsage(parsed.tokenUsage);
2101
2370
  return;
@@ -2225,6 +2494,24 @@ class CodexAppServerAgentSession {
2225
2494
  timelineItem.text = buffered;
2226
2495
  }
2227
2496
  }
2497
+ if (timelineItem.type === "assistant_message" &&
2498
+ normalizedItemType === "plan" &&
2499
+ itemId) {
2500
+ const bufferedPlanText = this.pendingPlanTexts.get(itemId) ?? "";
2501
+ const finalPlanText = typeof parsed.item.text === "string" ? parsed.item.text : "";
2502
+ if (bufferedPlanText.length > 0) {
2503
+ const trailingText = finalPlanText.startsWith(bufferedPlanText)
2504
+ ? finalPlanText.slice(bufferedPlanText.length)
2505
+ : "";
2506
+ timelineItem.text = formatProposedPlanChunk(trailingText, {
2507
+ close: true,
2508
+ });
2509
+ this.pendingPlanTexts.delete(itemId);
2510
+ }
2511
+ else if (finalPlanText.trim().length > 0) {
2512
+ timelineItem.text = formatProposedPlanBlock(finalPlanText);
2513
+ }
2514
+ }
2228
2515
  if (timelineItem.type === "reasoning" && itemId) {
2229
2516
  const buffered = this.pendingReasoning.get(itemId);
2230
2517
  if (buffered && buffered.length > 0) {
@@ -2240,6 +2527,7 @@ class CodexAppServerAgentSession {
2240
2527
  this.emittedItemStartedIds.delete(itemId);
2241
2528
  this.pendingCommandOutputDeltas.delete(itemId);
2242
2529
  this.pendingFileChangeOutputDeltas.delete(itemId);
2530
+ this.pendingPlanTexts.delete(itemId);
2243
2531
  }
2244
2532
  }
2245
2533
  return;
@@ -2336,7 +2624,7 @@ class CodexAppServerAgentSession {
2336
2624
  payload,
2337
2625
  }, "Codex edit tool call is missing diff/content fields");
2338
2626
  }
2339
- handleCommandApprovalRequest(params) {
2627
+ handleCommandApprovalRequest(requestMethod, params, rawRequestId) {
2340
2628
  const parsed = params;
2341
2629
  const commandPreview = mapCodexExecNotificationToToolCall({
2342
2630
  callId: parsed.itemId,
@@ -2344,8 +2632,12 @@ class CodexAppServerAgentSession {
2344
2632
  cwd: parsed.cwd ?? this.config.cwd ?? null,
2345
2633
  running: true,
2346
2634
  });
2347
- const requestId = `permission-${parsed.itemId}`;
2348
- const title = parsed.command ? `Run command: ${parsed.command}` : "Run command";
2635
+ const requestId = toPendingPermissionId(rawRequestId ?? parsed.approvalId ?? parsed.itemId);
2636
+ const title = parsed.command
2637
+ ? `Run command: ${parsed.command}`
2638
+ : parsed.networkApprovalContext
2639
+ ? "Allow network access"
2640
+ : "Run command";
2349
2641
  const request = {
2350
2642
  id: requestId,
2351
2643
  provider: CODEX_PROVIDER,
@@ -2366,9 +2658,18 @@ class CodexAppServerAgentSession {
2366
2658
  output: null,
2367
2659
  },
2368
2660
  metadata: {
2661
+ requestMethod,
2369
2662
  itemId: parsed.itemId,
2663
+ approvalId: parsed.approvalId ?? null,
2370
2664
  threadId: parsed.threadId,
2371
2665
  turnId: parsed.turnId,
2666
+ commandActions: parsed.commandActions ?? null,
2667
+ availableDecisions: parsed.availableDecisions ?? null,
2668
+ additionalPermissions: parsed.additionalPermissions ?? null,
2669
+ proposedExecpolicyAmendment: parsed.proposedExecpolicyAmendment ?? null,
2670
+ proposedNetworkPolicyAmendments: parsed.proposedNetworkPolicyAmendments ?? null,
2671
+ networkApprovalContext: parsed.networkApprovalContext ?? null,
2672
+ skillMetadata: parsed.skillMetadata ?? null,
2372
2673
  },
2373
2674
  };
2374
2675
  this.pendingPermissions.set(requestId, request);
@@ -2377,9 +2678,9 @@ class CodexAppServerAgentSession {
2377
2678
  this.pendingPermissionHandlers.set(requestId, { resolve, kind: "command" });
2378
2679
  });
2379
2680
  }
2380
- handleFileChangeApprovalRequest(params) {
2681
+ handleFileChangeApprovalRequest(requestMethod, params, rawRequestId) {
2381
2682
  const parsed = params;
2382
- const requestId = `permission-${parsed.itemId}`;
2683
+ const requestId = toPendingPermissionId(rawRequestId ?? parsed.itemId);
2383
2684
  const request = {
2384
2685
  id: requestId,
2385
2686
  provider: CODEX_PROVIDER,
@@ -2391,13 +2692,16 @@ class CodexAppServerAgentSession {
2391
2692
  type: "unknown",
2392
2693
  input: {
2393
2694
  reason: parsed.reason ?? null,
2695
+ grantRoot: parsed.grantRoot ?? null,
2394
2696
  },
2395
2697
  output: null,
2396
2698
  },
2397
2699
  metadata: {
2700
+ requestMethod,
2398
2701
  itemId: parsed.itemId,
2399
2702
  threadId: parsed.threadId,
2400
2703
  turnId: parsed.turnId,
2704
+ grantRoot: parsed.grantRoot ?? null,
2401
2705
  },
2402
2706
  };
2403
2707
  this.pendingPermissions.set(requestId, request);
@@ -2406,28 +2710,132 @@ class CodexAppServerAgentSession {
2406
2710
  this.pendingPermissionHandlers.set(requestId, { resolve, kind: "file" });
2407
2711
  });
2408
2712
  }
2409
- handleToolApprovalRequest(params) {
2713
+ handleToolApprovalRequest(requestMethod, params, rawRequestId) {
2410
2714
  const parsed = params;
2411
- const requestId = `permission-${parsed.itemId}`;
2715
+ const requestId = toPendingPermissionId(rawRequestId ?? parsed.itemId);
2716
+ const questions = normalizeCodexQuestionDescriptors(parsed.questions);
2412
2717
  const request = {
2413
2718
  id: requestId,
2414
2719
  provider: CODEX_PROVIDER,
2415
- name: "CodexTool",
2416
- kind: "tool",
2417
- title: "Tool action requires approval",
2720
+ name: "CodexQuestion",
2721
+ kind: "question",
2722
+ title: "Answer question",
2418
2723
  description: undefined,
2724
+ input: {
2725
+ questions,
2726
+ },
2419
2727
  detail: {
2420
2728
  type: "unknown",
2421
2729
  input: {
2422
- questions: Array.isArray(parsed.questions) ? parsed.questions : [],
2730
+ questions,
2423
2731
  },
2424
2732
  output: null,
2425
2733
  },
2426
2734
  metadata: {
2735
+ requestMethod,
2427
2736
  itemId: parsed.itemId,
2428
2737
  threadId: parsed.threadId,
2429
2738
  turnId: parsed.turnId,
2430
- questions: parsed.questions,
2739
+ questions,
2740
+ },
2741
+ };
2742
+ this.pendingPermissions.set(requestId, request);
2743
+ this.emitEvent({ type: "permission_requested", provider: CODEX_PROVIDER, request });
2744
+ return new Promise((resolve) => {
2745
+ this.pendingPermissionHandlers.set(requestId, {
2746
+ resolve,
2747
+ kind: "question",
2748
+ questions,
2749
+ });
2750
+ });
2751
+ }
2752
+ handlePermissionsApprovalRequest(requestMethod, params, rawRequestId) {
2753
+ const parsed = params;
2754
+ const requestId = toPendingPermissionId(rawRequestId ?? parsed.itemId);
2755
+ const request = {
2756
+ id: requestId,
2757
+ provider: CODEX_PROVIDER,
2758
+ name: "CodexPermissionProfile",
2759
+ kind: "tool",
2760
+ title: "Grant additional permissions",
2761
+ description: parsed.reason ?? undefined,
2762
+ input: {
2763
+ permissions: parsed.permissions ?? null,
2764
+ },
2765
+ detail: {
2766
+ type: "unknown",
2767
+ input: {
2768
+ reason: parsed.reason ?? null,
2769
+ permissions: parsed.permissions ?? null,
2770
+ },
2771
+ output: null,
2772
+ },
2773
+ metadata: {
2774
+ requestMethod,
2775
+ itemId: parsed.itemId,
2776
+ threadId: parsed.threadId,
2777
+ turnId: parsed.turnId,
2778
+ permissions: parsed.permissions ?? null,
2779
+ },
2780
+ };
2781
+ this.pendingPermissions.set(requestId, request);
2782
+ this.emitEvent({ type: "permission_requested", provider: CODEX_PROVIDER, request });
2783
+ return new Promise((resolve) => {
2784
+ this.pendingPermissionHandlers.set(requestId, {
2785
+ resolve,
2786
+ kind: "permissions",
2787
+ permissions: parsed.permissions ?? null,
2788
+ });
2789
+ });
2790
+ }
2791
+ handleMcpElicitationRequest(requestMethod, params, rawRequestId) {
2792
+ const parsed = params;
2793
+ const baseRequestId = rawRequestId ??
2794
+ parsed.elicitationId ??
2795
+ `${parsed.threadId}:${parsed.turnId ?? "none"}:${parsed.serverName ?? "mcp"}`;
2796
+ const requestId = toPendingPermissionId(baseRequestId);
2797
+ const title = parsed.mode === "url"
2798
+ ? "Open MCP authorization link"
2799
+ : parsed.serverName
2800
+ ? `Respond to ${parsed.serverName}`
2801
+ : "Respond to MCP server";
2802
+ const request = {
2803
+ id: requestId,
2804
+ provider: CODEX_PROVIDER,
2805
+ name: "CodexMcpElicitation",
2806
+ kind: "other",
2807
+ title,
2808
+ description: parsed.message ?? undefined,
2809
+ input: {
2810
+ mode: parsed.mode ?? null,
2811
+ requestedSchema: parsed.requestedSchema ?? null,
2812
+ url: parsed.url ?? null,
2813
+ },
2814
+ detail: {
2815
+ type: "unknown",
2816
+ input: {
2817
+ requestMethod,
2818
+ serverName: parsed.serverName ?? null,
2819
+ mode: parsed.mode ?? null,
2820
+ message: parsed.message ?? null,
2821
+ requestedSchema: parsed.requestedSchema ?? null,
2822
+ url: parsed.url ?? null,
2823
+ elicitationId: parsed.elicitationId ?? null,
2824
+ _meta: parsed._meta ?? null,
2825
+ },
2826
+ output: null,
2827
+ },
2828
+ metadata: {
2829
+ requestMethod,
2830
+ threadId: parsed.threadId,
2831
+ turnId: parsed.turnId ?? null,
2832
+ serverName: parsed.serverName ?? null,
2833
+ mode: parsed.mode ?? null,
2834
+ message: parsed.message ?? null,
2835
+ requestedSchema: parsed.requestedSchema ?? null,
2836
+ url: parsed.url ?? null,
2837
+ elicitationId: parsed.elicitationId ?? null,
2838
+ _meta: parsed._meta ?? null,
2431
2839
  },
2432
2840
  };
2433
2841
  this.pendingPermissions.set(requestId, request);
@@ -2435,8 +2843,7 @@ class CodexAppServerAgentSession {
2435
2843
  return new Promise((resolve) => {
2436
2844
  this.pendingPermissionHandlers.set(requestId, {
2437
2845
  resolve,
2438
- kind: "tool",
2439
- questions: Array.isArray(parsed.questions) ? parsed.questions : [],
2846
+ kind: "elicitation",
2440
2847
  });
2441
2848
  });
2442
2849
  }
@@ -2616,7 +3023,7 @@ export class CodexAppServerAgentClient {
2616
3023
  }
2617
3024
  }
2618
3025
  async isAvailable() {
2619
- return isProviderCommandAvailable(this.runtimeSettings?.command, resolveCodexBinary);
3026
+ return isProviderCommandAvailable(this.runtimeSettings?.command, resolveCodexBinary, applyProviderEnv(process.env, this.runtimeSettings));
2620
3027
  }
2621
3028
  }
2622
3029
  //# sourceMappingURL=codex-app-server-agent.js.map