@junctionpanel/server 0.1.29 → 0.1.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/server/client/daemon-client.d.ts +3 -1
- package/dist/server/client/daemon-client.d.ts.map +1 -1
- package/dist/server/client/daemon-client.js +2 -0
- package/dist/server/client/daemon-client.js.map +1 -1
- package/dist/server/server/agent/agent-sdk-types.d.ts +6 -0
- package/dist/server/server/agent/agent-sdk-types.d.ts.map +1 -1
- package/dist/server/server/agent/agent-sdk-types.js.map +1 -1
- package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +8 -0
- package/dist/server/server/agent/providers/codex-app-server-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/codex-app-server-agent.js +243 -96
- package/dist/server/server/agent/providers/codex-app-server-agent.js.map +1 -1
- package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts.map +1 -1
- package/dist/server/server/session.d.ts.map +1 -1
- package/dist/server/server/session.js +19 -3
- package/dist/server/server/session.js.map +1 -1
- package/dist/server/shared/messages.d.ts +572 -12
- package/dist/server/shared/messages.d.ts.map +1 -1
- package/dist/server/shared/messages.js +16 -0
- package/dist/server/shared/messages.js.map +1 -1
- package/package.json +2 -2
|
@@ -218,6 +218,9 @@ function parseUpdatedQuestionAnswers(updatedInput) {
|
|
|
218
218
|
}
|
|
219
219
|
return parsed;
|
|
220
220
|
}
|
|
221
|
+
function toPendingPermissionId(requestId) {
|
|
222
|
+
return `permission-${String(requestId)}`;
|
|
223
|
+
}
|
|
221
224
|
async function listCodexCustomPrompts() {
|
|
222
225
|
const codexHome = resolveCodexHomeDir();
|
|
223
226
|
const promptsDir = path.join(codexHome, "prompts");
|
|
@@ -524,7 +527,7 @@ class CodexAppServerClient {
|
|
|
524
527
|
const request = msg;
|
|
525
528
|
const handler = this.requestHandlers.get(request.method);
|
|
526
529
|
try {
|
|
527
|
-
const result = handler ? await handler(request.params) : {};
|
|
530
|
+
const result = handler ? await handler(request.params, request.id) : {};
|
|
528
531
|
const response = { id: request.id, result };
|
|
529
532
|
this.child.stdin.write(`${JSON.stringify(response)}\n`);
|
|
530
533
|
}
|
|
@@ -581,12 +584,73 @@ function parsePlanTextToTodoItems(text) {
|
|
|
581
584
|
completed: false,
|
|
582
585
|
}));
|
|
583
586
|
}
|
|
587
|
+
function formatProposedPlanBlock(text) {
|
|
588
|
+
return `<proposed_plan>\n${text}\n</proposed_plan>`;
|
|
589
|
+
}
|
|
590
|
+
function formatProposedPlanChunk(text, options) {
|
|
591
|
+
const parts = [];
|
|
592
|
+
if (options?.open) {
|
|
593
|
+
parts.push("<proposed_plan>\n");
|
|
594
|
+
}
|
|
595
|
+
parts.push(text);
|
|
596
|
+
if (options?.close) {
|
|
597
|
+
parts.push("\n</proposed_plan>");
|
|
598
|
+
}
|
|
599
|
+
return parts.join("");
|
|
600
|
+
}
|
|
584
601
|
function planStepsToTodoItems(steps) {
|
|
585
602
|
return steps.map((entry) => ({
|
|
586
603
|
text: entry.step,
|
|
587
604
|
completed: entry.status === "completed",
|
|
588
605
|
}));
|
|
589
606
|
}
|
|
607
|
+
function supportsPlanCollaborationMode(response) {
|
|
608
|
+
const candidateArrays = [
|
|
609
|
+
Array.isArray(response) ? response : null,
|
|
610
|
+
Array.isArray(response?.data)
|
|
611
|
+
? (response.data)
|
|
612
|
+
: null,
|
|
613
|
+
Array.isArray(response?.modes)
|
|
614
|
+
? (response.modes)
|
|
615
|
+
: null,
|
|
616
|
+
Array.isArray(response?.collaborationModes)
|
|
617
|
+
? (response.collaborationModes)
|
|
618
|
+
: null,
|
|
619
|
+
Array.isArray(response?.items)
|
|
620
|
+
? (response.items)
|
|
621
|
+
: null,
|
|
622
|
+
];
|
|
623
|
+
for (const entries of candidateArrays) {
|
|
624
|
+
if (!entries)
|
|
625
|
+
continue;
|
|
626
|
+
for (const entry of entries) {
|
|
627
|
+
const record = toRecord(entry);
|
|
628
|
+
const modeName = (typeof record?.mode === "string" ? record.mode : null) ??
|
|
629
|
+
(typeof record?.name === "string" ? record.name : null) ??
|
|
630
|
+
(typeof record?.id === "string" ? record.id : null) ??
|
|
631
|
+
(typeof entry === "string" ? entry : null);
|
|
632
|
+
if (modeName?.trim().toLowerCase() === "plan") {
|
|
633
|
+
return true;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
return false;
|
|
638
|
+
}
|
|
639
|
+
function shouldRetryInitializeWithoutExperimentalApi(error) {
|
|
640
|
+
const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
|
|
641
|
+
return (message.includes("experimentalapi") ||
|
|
642
|
+
message.includes("experimental api") ||
|
|
643
|
+
message.includes("capabilities") ||
|
|
644
|
+
message.includes("unknown field") ||
|
|
645
|
+
message.includes("invalid params"));
|
|
646
|
+
}
|
|
647
|
+
function shouldRetryTurnStartWithoutCollaborationMode(error) {
|
|
648
|
+
const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
|
|
649
|
+
return (message.includes("collaborationmode") ||
|
|
650
|
+
message.includes("collaboration mode") ||
|
|
651
|
+
message.includes("experimentalapi") ||
|
|
652
|
+
message.includes("experimental api"));
|
|
653
|
+
}
|
|
590
654
|
function extractPatchLikeText(value) {
|
|
591
655
|
if (!value || typeof value !== "object") {
|
|
592
656
|
return undefined;
|
|
@@ -896,8 +960,11 @@ function threadItemToTimeline(item, options) {
|
|
|
896
960
|
}
|
|
897
961
|
case "plan": {
|
|
898
962
|
const text = normalizedItem.text ?? "";
|
|
963
|
+
if (typeof text === "string" && text.trim().length > 0) {
|
|
964
|
+
return { type: "assistant_message", text: formatProposedPlanBlock(text) };
|
|
965
|
+
}
|
|
899
966
|
const items = parsePlanTextToTodoItems(text);
|
|
900
|
-
return { type: "todo", items };
|
|
967
|
+
return items.length > 0 ? { type: "todo", items } : null;
|
|
901
968
|
}
|
|
902
969
|
case "reasoning": {
|
|
903
970
|
const summary = Array.isArray(normalizedItem.summary)
|
|
@@ -958,6 +1025,13 @@ const TurnPlanUpdatedNotificationSchema = z.object({
|
|
|
958
1025
|
})
|
|
959
1026
|
.passthrough()),
|
|
960
1027
|
}).passthrough();
|
|
1028
|
+
const ItemPlanDeltaNotificationSchema = z.object({
|
|
1029
|
+
itemId: z.string(),
|
|
1030
|
+
delta: z.string(),
|
|
1031
|
+
}).passthrough();
|
|
1032
|
+
const ServerRequestResolvedNotificationSchema = z.object({
|
|
1033
|
+
requestId: z.union([z.string(), z.number()]),
|
|
1034
|
+
}).passthrough();
|
|
961
1035
|
const TurnDiffUpdatedNotificationSchema = z.object({
|
|
962
1036
|
diff: z.string(),
|
|
963
1037
|
}).passthrough();
|
|
@@ -1097,6 +1171,12 @@ const CodexNotificationSchema = z.union([
|
|
|
1097
1171
|
})),
|
|
1098
1172
|
})),
|
|
1099
1173
|
z.object({ method: z.literal("turn/plan/updated"), params: z.unknown() }).transform(({ method, params }) => ({ kind: "invalid_payload", method, params })),
|
|
1174
|
+
z.object({ method: z.literal("item/plan/delta"), params: ItemPlanDeltaNotificationSchema }).transform(({ params }) => ({
|
|
1175
|
+
kind: "plan_delta",
|
|
1176
|
+
itemId: params.itemId,
|
|
1177
|
+
delta: params.delta,
|
|
1178
|
+
})),
|
|
1179
|
+
z.object({ method: z.literal("item/plan/delta"), params: z.unknown() }).transform(({ method, params }) => ({ kind: "invalid_payload", method, params })),
|
|
1100
1180
|
z.object({ method: z.literal("turn/diff/updated"), params: TurnDiffUpdatedNotificationSchema }).transform(({ params }) => ({ kind: "diff_updated", diff: params.diff })),
|
|
1101
1181
|
z.object({ method: z.literal("turn/diff/updated"), params: z.unknown() }).transform(({ method, params }) => ({ kind: "invalid_payload", method, params })),
|
|
1102
1182
|
z.object({
|
|
@@ -1104,6 +1184,14 @@ const CodexNotificationSchema = z.union([
|
|
|
1104
1184
|
params: ThreadTokenUsageUpdatedNotificationSchema,
|
|
1105
1185
|
}).transform(({ params }) => ({ kind: "token_usage_updated", tokenUsage: params.tokenUsage })),
|
|
1106
1186
|
z.object({ method: z.literal("thread/tokenUsage/updated"), params: z.unknown() }).transform(({ method, params }) => ({ kind: "invalid_payload", method, params })),
|
|
1187
|
+
z.object({
|
|
1188
|
+
method: z.literal("serverRequest/resolved"),
|
|
1189
|
+
params: ServerRequestResolvedNotificationSchema,
|
|
1190
|
+
}).transform(({ params }) => ({
|
|
1191
|
+
kind: "server_request_resolved",
|
|
1192
|
+
requestId: String(params.requestId),
|
|
1193
|
+
})),
|
|
1194
|
+
z.object({ method: z.literal("serverRequest/resolved"), params: z.unknown() }).transform(({ method, params }) => ({ kind: "invalid_payload", method, params })),
|
|
1107
1195
|
z.object({ method: z.literal("item/agentMessage/delta"), params: ItemTextDeltaNotificationSchema }).transform(({ params }) => ({
|
|
1108
1196
|
kind: "agent_message_delta",
|
|
1109
1197
|
itemId: params.itemId,
|
|
@@ -1303,6 +1391,10 @@ export async function codexAppServerTurnInputFromPrompt(prompt, logger) {
|
|
|
1303
1391
|
}
|
|
1304
1392
|
export const __codexAppServerInternals = {
|
|
1305
1393
|
mapCodexPatchNotificationToToolCall,
|
|
1394
|
+
supportsPlanCollaborationMode,
|
|
1395
|
+
shouldRetryInitializeWithoutExperimentalApi,
|
|
1396
|
+
shouldRetryTurnStartWithoutCollaborationMode,
|
|
1397
|
+
formatProposedPlanBlock,
|
|
1306
1398
|
};
|
|
1307
1399
|
class CodexAppServerAgentSession {
|
|
1308
1400
|
constructor(config, resumeHandle, logger, spawnAppServer) {
|
|
@@ -1332,8 +1424,8 @@ class CodexAppServerAgentSession {
|
|
|
1332
1424
|
this.warnedInvalidNotificationPayloads = new Set();
|
|
1333
1425
|
this.warnedIncompleteEditToolCallIds = new Set();
|
|
1334
1426
|
this.connected = false;
|
|
1335
|
-
this.
|
|
1336
|
-
this.
|
|
1427
|
+
this.nativePlanModeSupported = null;
|
|
1428
|
+
this.pendingPlanTexts = new Map();
|
|
1337
1429
|
this.cachedSkills = [];
|
|
1338
1430
|
this.logger = logger.child({ module: "agent", provider: CODEX_PROVIDER });
|
|
1339
1431
|
if (config.modeId === undefined) {
|
|
@@ -1358,13 +1450,26 @@ class CodexAppServerAgentSession {
|
|
|
1358
1450
|
this.client = new CodexAppServerClient(child, this.logger);
|
|
1359
1451
|
this.client.setNotificationHandler((method, params) => this.handleNotification(method, params));
|
|
1360
1452
|
this.registerRequestHandlers();
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1453
|
+
const clientInfo = {
|
|
1454
|
+
name: "junction",
|
|
1455
|
+
title: "Junction",
|
|
1456
|
+
version: "0.0.0",
|
|
1457
|
+
};
|
|
1458
|
+
try {
|
|
1459
|
+
await this.client.request("initialize", {
|
|
1460
|
+
clientInfo,
|
|
1461
|
+
capabilities: {
|
|
1462
|
+
experimentalApi: true,
|
|
1463
|
+
},
|
|
1464
|
+
});
|
|
1465
|
+
}
|
|
1466
|
+
catch (error) {
|
|
1467
|
+
if (!shouldRetryInitializeWithoutExperimentalApi(error)) {
|
|
1468
|
+
throw error;
|
|
1469
|
+
}
|
|
1470
|
+
await this.client.request("initialize", { clientInfo });
|
|
1471
|
+
this.nativePlanModeSupported = false;
|
|
1472
|
+
}
|
|
1368
1473
|
this.client.notify("initialized", {});
|
|
1369
1474
|
await this.loadCollaborationModes();
|
|
1370
1475
|
await this.loadSkills();
|
|
@@ -1377,22 +1482,17 @@ class CodexAppServerAgentSession {
|
|
|
1377
1482
|
async loadCollaborationModes() {
|
|
1378
1483
|
if (!this.client)
|
|
1379
1484
|
return;
|
|
1485
|
+
if (this.nativePlanModeSupported === false) {
|
|
1486
|
+
return;
|
|
1487
|
+
}
|
|
1380
1488
|
try {
|
|
1381
|
-
const response =
|
|
1382
|
-
|
|
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
|
-
}));
|
|
1489
|
+
const response = await this.client.request("collaborationMode/list", {});
|
|
1490
|
+
this.nativePlanModeSupported = supportsPlanCollaborationMode(response);
|
|
1390
1491
|
}
|
|
1391
1492
|
catch (error) {
|
|
1392
1493
|
this.logger.trace({ error }, "Failed to load collaboration modes");
|
|
1393
|
-
this.
|
|
1494
|
+
this.nativePlanModeSupported = false;
|
|
1394
1495
|
}
|
|
1395
|
-
this.resolvedCollaborationMode = this.resolveCollaborationMode(this.currentMode);
|
|
1396
1496
|
}
|
|
1397
1497
|
async loadSkills() {
|
|
1398
1498
|
if (!this.client)
|
|
@@ -1422,59 +1522,12 @@ class CodexAppServerAgentSession {
|
|
|
1422
1522
|
this.cachedSkills = [];
|
|
1423
1523
|
}
|
|
1424
1524
|
}
|
|
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
1525
|
registerRequestHandlers() {
|
|
1473
1526
|
if (!this.client)
|
|
1474
1527
|
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));
|
|
1528
|
+
this.client.setRequestHandler("item/commandExecution/requestApproval", (params, requestId) => this.handleCommandApprovalRequest(params, requestId));
|
|
1529
|
+
this.client.setRequestHandler("item/fileChange/requestApproval", (params, requestId) => this.handleFileChangeApprovalRequest(params, requestId));
|
|
1530
|
+
this.client.setRequestHandler("tool/requestUserInput", (params, requestId) => this.handleToolApprovalRequest(params, requestId));
|
|
1478
1531
|
}
|
|
1479
1532
|
async loadPersistedHistory() {
|
|
1480
1533
|
if (!this.client || !this.currentThreadId)
|
|
@@ -1672,6 +1725,8 @@ class CodexAppServerAgentSession {
|
|
|
1672
1725
|
const preset = MODE_PRESETS[this.currentMode] ?? MODE_PRESETS[DEFAULT_CODEX_MODE_ID];
|
|
1673
1726
|
const approvalPolicy = this.config.approvalPolicy ?? preset.approvalPolicy;
|
|
1674
1727
|
const sandboxPolicyType = this.config.sandboxMode ?? preset.sandbox;
|
|
1728
|
+
const planModeRequested = options?.extra?.codex?.planMode === true;
|
|
1729
|
+
const thinkingOptionId = normalizeCodexThinkingOptionId(this.config.thinkingOptionId);
|
|
1675
1730
|
const params = {
|
|
1676
1731
|
threadId: this.currentThreadId,
|
|
1677
1732
|
input,
|
|
@@ -1683,16 +1738,9 @@ class CodexAppServerAgentSession {
|
|
|
1683
1738
|
if (this.config.model) {
|
|
1684
1739
|
params.model = this.config.model;
|
|
1685
1740
|
}
|
|
1686
|
-
const thinkingOptionId = normalizeCodexThinkingOptionId(this.config.thinkingOptionId);
|
|
1687
1741
|
if (thinkingOptionId) {
|
|
1688
1742
|
params.effort = thinkingOptionId;
|
|
1689
1743
|
}
|
|
1690
|
-
if (this.resolvedCollaborationMode) {
|
|
1691
|
-
params.collaborationMode = {
|
|
1692
|
-
mode: this.resolvedCollaborationMode.mode,
|
|
1693
|
-
settings: this.resolvedCollaborationMode.settings,
|
|
1694
|
-
};
|
|
1695
|
-
}
|
|
1696
1744
|
if (this.config.cwd) {
|
|
1697
1745
|
params.cwd = this.config.cwd;
|
|
1698
1746
|
}
|
|
@@ -1706,7 +1754,38 @@ class CodexAppServerAgentSession {
|
|
|
1706
1754
|
if (codexConfig) {
|
|
1707
1755
|
params.config = codexConfig;
|
|
1708
1756
|
}
|
|
1709
|
-
|
|
1757
|
+
let downgradedFromPlanMode = false;
|
|
1758
|
+
if (this.nativePlanModeSupported !== false) {
|
|
1759
|
+
const collaborationMode = this.buildCollaborationModePayload(planModeRequested ? "plan" : "default");
|
|
1760
|
+
if (collaborationMode) {
|
|
1761
|
+
params.collaborationMode = collaborationMode;
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
try {
|
|
1765
|
+
await this.client.request("turn/start", params, TURN_START_TIMEOUT_MS);
|
|
1766
|
+
}
|
|
1767
|
+
catch (error) {
|
|
1768
|
+
const canRetryWithoutPlanMode = Object.prototype.hasOwnProperty.call(params, "collaborationMode") &&
|
|
1769
|
+
shouldRetryTurnStartWithoutCollaborationMode(error);
|
|
1770
|
+
if (!canRetryWithoutPlanMode) {
|
|
1771
|
+
throw error;
|
|
1772
|
+
}
|
|
1773
|
+
delete params.collaborationMode;
|
|
1774
|
+
this.nativePlanModeSupported = false;
|
|
1775
|
+
this.cachedRuntimeInfo = null;
|
|
1776
|
+
downgradedFromPlanMode = planModeRequested;
|
|
1777
|
+
await this.client.request("turn/start", params, TURN_START_TIMEOUT_MS);
|
|
1778
|
+
}
|
|
1779
|
+
if (downgradedFromPlanMode) {
|
|
1780
|
+
this.emitEvent({
|
|
1781
|
+
type: "timeline",
|
|
1782
|
+
provider: CODEX_PROVIDER,
|
|
1783
|
+
item: {
|
|
1784
|
+
type: "assistant_message",
|
|
1785
|
+
text: "Plan mode is not supported by this Codex runtime. Sent as a normal turn instead.",
|
|
1786
|
+
},
|
|
1787
|
+
});
|
|
1788
|
+
}
|
|
1710
1789
|
let sawTurnStarted = false;
|
|
1711
1790
|
for await (const event of queue) {
|
|
1712
1791
|
// Drop pre-start timeline noise that can leak from the previous turn.
|
|
@@ -1763,9 +1842,9 @@ class CodexAppServerAgentSession {
|
|
|
1763
1842
|
model: this.config.model ?? null,
|
|
1764
1843
|
thinkingOptionId: normalizeCodexThinkingOptionId(this.config.thinkingOptionId) ?? null,
|
|
1765
1844
|
modeId: this.currentMode ?? null,
|
|
1766
|
-
extra: this.
|
|
1767
|
-
?
|
|
1768
|
-
:
|
|
1845
|
+
extra: this.nativePlanModeSupported === null
|
|
1846
|
+
? undefined
|
|
1847
|
+
: { planModeSupported: this.nativePlanModeSupported },
|
|
1769
1848
|
};
|
|
1770
1849
|
this.cachedRuntimeInfo = info;
|
|
1771
1850
|
return { ...info };
|
|
@@ -1779,17 +1858,14 @@ class CodexAppServerAgentSession {
|
|
|
1779
1858
|
async setMode(modeId) {
|
|
1780
1859
|
validateCodexMode(modeId);
|
|
1781
1860
|
this.currentMode = modeId;
|
|
1782
|
-
this.resolvedCollaborationMode = this.resolveCollaborationMode(modeId);
|
|
1783
1861
|
this.cachedRuntimeInfo = null;
|
|
1784
1862
|
}
|
|
1785
1863
|
async setModel(modelId) {
|
|
1786
1864
|
this.config.model = modelId ?? undefined;
|
|
1787
|
-
this.resolvedCollaborationMode = this.resolveCollaborationMode(this.currentMode);
|
|
1788
1865
|
this.cachedRuntimeInfo = null;
|
|
1789
1866
|
}
|
|
1790
1867
|
async setThinkingOption(thinkingOptionId) {
|
|
1791
1868
|
this.config.thinkingOptionId = normalizeCodexThinkingOptionId(thinkingOptionId);
|
|
1792
|
-
this.resolvedCollaborationMode = this.resolveCollaborationMode(this.currentMode);
|
|
1793
1869
|
this.cachedRuntimeInfo = null;
|
|
1794
1870
|
}
|
|
1795
1871
|
getPendingPermissions() {
|
|
@@ -2019,6 +2095,24 @@ class CodexAppServerAgentSession {
|
|
|
2019
2095
|
}
|
|
2020
2096
|
return Object.keys(innerConfig).length > 0 ? innerConfig : null;
|
|
2021
2097
|
}
|
|
2098
|
+
buildCollaborationModePayload(mode) {
|
|
2099
|
+
if (this.nativePlanModeSupported !== true) {
|
|
2100
|
+
return null;
|
|
2101
|
+
}
|
|
2102
|
+
const model = normalizeCodexModelId(this.config.model);
|
|
2103
|
+
if (!model) {
|
|
2104
|
+
return null;
|
|
2105
|
+
}
|
|
2106
|
+
const thinkingOptionId = normalizeCodexThinkingOptionId(this.config.thinkingOptionId) ?? null;
|
|
2107
|
+
return {
|
|
2108
|
+
mode,
|
|
2109
|
+
settings: {
|
|
2110
|
+
model,
|
|
2111
|
+
reasoning_effort: thinkingOptionId,
|
|
2112
|
+
developer_instructions: null,
|
|
2113
|
+
},
|
|
2114
|
+
};
|
|
2115
|
+
}
|
|
2022
2116
|
async buildUserInput(prompt) {
|
|
2023
2117
|
if (typeof prompt === "string") {
|
|
2024
2118
|
return [{ type: "text", text: prompt }];
|
|
@@ -2073,6 +2167,7 @@ class CodexAppServerAgentSession {
|
|
|
2073
2167
|
this.emittedExecCommandCompletedCallIds.clear();
|
|
2074
2168
|
this.pendingCommandOutputDeltas.clear();
|
|
2075
2169
|
this.pendingFileChangeOutputDeltas.clear();
|
|
2170
|
+
this.pendingPlanTexts.clear();
|
|
2076
2171
|
this.warnedIncompleteEditToolCallIds.clear();
|
|
2077
2172
|
this.eventQueue?.end();
|
|
2078
2173
|
return;
|
|
@@ -2089,6 +2184,22 @@ class CodexAppServerAgentSession {
|
|
|
2089
2184
|
});
|
|
2090
2185
|
return;
|
|
2091
2186
|
}
|
|
2187
|
+
if (parsed.kind === "plan_delta") {
|
|
2188
|
+
const previous = this.pendingPlanTexts.get(parsed.itemId) ?? "";
|
|
2189
|
+
const next = previous + parsed.delta;
|
|
2190
|
+
this.pendingPlanTexts.set(parsed.itemId, next);
|
|
2191
|
+
this.emitEvent({
|
|
2192
|
+
type: "timeline",
|
|
2193
|
+
provider: CODEX_PROVIDER,
|
|
2194
|
+
item: {
|
|
2195
|
+
type: "assistant_message",
|
|
2196
|
+
text: formatProposedPlanChunk(parsed.delta, {
|
|
2197
|
+
open: previous.length === 0,
|
|
2198
|
+
}),
|
|
2199
|
+
},
|
|
2200
|
+
});
|
|
2201
|
+
return;
|
|
2202
|
+
}
|
|
2092
2203
|
if (parsed.kind === "diff_updated") {
|
|
2093
2204
|
// NOTE: Codex app-server emits frequent `turn/diff/updated` notifications
|
|
2094
2205
|
// containing a full accumulated unified diff for the *entire turn*.
|
|
@@ -2096,6 +2207,20 @@ class CodexAppServerAgentSession {
|
|
|
2096
2207
|
// We intentionally do NOT store every diff update in the timeline.
|
|
2097
2208
|
return;
|
|
2098
2209
|
}
|
|
2210
|
+
if (parsed.kind === "server_request_resolved") {
|
|
2211
|
+
const pendingRequestId = toPendingPermissionId(parsed.requestId);
|
|
2212
|
+
if (this.resolvedPermissionRequests.has(pendingRequestId)) {
|
|
2213
|
+
return;
|
|
2214
|
+
}
|
|
2215
|
+
this.pendingPermissions.delete(pendingRequestId);
|
|
2216
|
+
this.emitEvent({
|
|
2217
|
+
type: "permission_resolved",
|
|
2218
|
+
provider: CODEX_PROVIDER,
|
|
2219
|
+
requestId: pendingRequestId,
|
|
2220
|
+
resolution: { behavior: "allow" },
|
|
2221
|
+
});
|
|
2222
|
+
return;
|
|
2223
|
+
}
|
|
2099
2224
|
if (parsed.kind === "token_usage_updated") {
|
|
2100
2225
|
this.latestUsage = toAgentUsage(parsed.tokenUsage);
|
|
2101
2226
|
return;
|
|
@@ -2225,6 +2350,24 @@ class CodexAppServerAgentSession {
|
|
|
2225
2350
|
timelineItem.text = buffered;
|
|
2226
2351
|
}
|
|
2227
2352
|
}
|
|
2353
|
+
if (timelineItem.type === "assistant_message" &&
|
|
2354
|
+
normalizedItemType === "plan" &&
|
|
2355
|
+
itemId) {
|
|
2356
|
+
const bufferedPlanText = this.pendingPlanTexts.get(itemId) ?? "";
|
|
2357
|
+
const finalPlanText = typeof parsed.item.text === "string" ? parsed.item.text : "";
|
|
2358
|
+
if (bufferedPlanText.length > 0) {
|
|
2359
|
+
const trailingText = finalPlanText.startsWith(bufferedPlanText)
|
|
2360
|
+
? finalPlanText.slice(bufferedPlanText.length)
|
|
2361
|
+
: "";
|
|
2362
|
+
timelineItem.text = formatProposedPlanChunk(trailingText, {
|
|
2363
|
+
close: true,
|
|
2364
|
+
});
|
|
2365
|
+
this.pendingPlanTexts.delete(itemId);
|
|
2366
|
+
}
|
|
2367
|
+
else if (finalPlanText.trim().length > 0) {
|
|
2368
|
+
timelineItem.text = formatProposedPlanBlock(finalPlanText);
|
|
2369
|
+
}
|
|
2370
|
+
}
|
|
2228
2371
|
if (timelineItem.type === "reasoning" && itemId) {
|
|
2229
2372
|
const buffered = this.pendingReasoning.get(itemId);
|
|
2230
2373
|
if (buffered && buffered.length > 0) {
|
|
@@ -2240,6 +2383,7 @@ class CodexAppServerAgentSession {
|
|
|
2240
2383
|
this.emittedItemStartedIds.delete(itemId);
|
|
2241
2384
|
this.pendingCommandOutputDeltas.delete(itemId);
|
|
2242
2385
|
this.pendingFileChangeOutputDeltas.delete(itemId);
|
|
2386
|
+
this.pendingPlanTexts.delete(itemId);
|
|
2243
2387
|
}
|
|
2244
2388
|
}
|
|
2245
2389
|
return;
|
|
@@ -2336,7 +2480,7 @@ class CodexAppServerAgentSession {
|
|
|
2336
2480
|
payload,
|
|
2337
2481
|
}, "Codex edit tool call is missing diff/content fields");
|
|
2338
2482
|
}
|
|
2339
|
-
handleCommandApprovalRequest(params) {
|
|
2483
|
+
handleCommandApprovalRequest(params, rawRequestId) {
|
|
2340
2484
|
const parsed = params;
|
|
2341
2485
|
const commandPreview = mapCodexExecNotificationToToolCall({
|
|
2342
2486
|
callId: parsed.itemId,
|
|
@@ -2344,7 +2488,7 @@ class CodexAppServerAgentSession {
|
|
|
2344
2488
|
cwd: parsed.cwd ?? this.config.cwd ?? null,
|
|
2345
2489
|
running: true,
|
|
2346
2490
|
});
|
|
2347
|
-
const requestId =
|
|
2491
|
+
const requestId = toPendingPermissionId(rawRequestId ?? parsed.itemId);
|
|
2348
2492
|
const title = parsed.command ? `Run command: ${parsed.command}` : "Run command";
|
|
2349
2493
|
const request = {
|
|
2350
2494
|
id: requestId,
|
|
@@ -2377,9 +2521,9 @@ class CodexAppServerAgentSession {
|
|
|
2377
2521
|
this.pendingPermissionHandlers.set(requestId, { resolve, kind: "command" });
|
|
2378
2522
|
});
|
|
2379
2523
|
}
|
|
2380
|
-
handleFileChangeApprovalRequest(params) {
|
|
2524
|
+
handleFileChangeApprovalRequest(params, rawRequestId) {
|
|
2381
2525
|
const parsed = params;
|
|
2382
|
-
const requestId =
|
|
2526
|
+
const requestId = toPendingPermissionId(rawRequestId ?? parsed.itemId);
|
|
2383
2527
|
const request = {
|
|
2384
2528
|
id: requestId,
|
|
2385
2529
|
provider: CODEX_PROVIDER,
|
|
@@ -2406,16 +2550,19 @@ class CodexAppServerAgentSession {
|
|
|
2406
2550
|
this.pendingPermissionHandlers.set(requestId, { resolve, kind: "file" });
|
|
2407
2551
|
});
|
|
2408
2552
|
}
|
|
2409
|
-
handleToolApprovalRequest(params) {
|
|
2553
|
+
handleToolApprovalRequest(params, rawRequestId) {
|
|
2410
2554
|
const parsed = params;
|
|
2411
|
-
const requestId =
|
|
2555
|
+
const requestId = toPendingPermissionId(rawRequestId ?? parsed.itemId);
|
|
2412
2556
|
const request = {
|
|
2413
2557
|
id: requestId,
|
|
2414
2558
|
provider: CODEX_PROVIDER,
|
|
2415
|
-
name: "
|
|
2416
|
-
kind: "
|
|
2417
|
-
title: "
|
|
2559
|
+
name: "CodexQuestion",
|
|
2560
|
+
kind: "question",
|
|
2561
|
+
title: "Answer question",
|
|
2418
2562
|
description: undefined,
|
|
2563
|
+
input: {
|
|
2564
|
+
questions: Array.isArray(parsed.questions) ? parsed.questions : [],
|
|
2565
|
+
},
|
|
2419
2566
|
detail: {
|
|
2420
2567
|
type: "unknown",
|
|
2421
2568
|
input: {
|
|
@@ -2435,7 +2582,7 @@ class CodexAppServerAgentSession {
|
|
|
2435
2582
|
return new Promise((resolve) => {
|
|
2436
2583
|
this.pendingPermissionHandlers.set(requestId, {
|
|
2437
2584
|
resolve,
|
|
2438
|
-
kind: "
|
|
2585
|
+
kind: "question",
|
|
2439
2586
|
questions: Array.isArray(parsed.questions) ? parsed.questions : [],
|
|
2440
2587
|
});
|
|
2441
2588
|
});
|