@agentclientprotocol/codex-acp 0.0.45 → 0.0.46
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/index.js +765 -165
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -17319,6 +17319,30 @@ function applyStructuredPatch(source, patch, options = {}) {
|
|
|
17319
17319
|
return resultLines.join("\n");
|
|
17320
17320
|
}
|
|
17321
17321
|
|
|
17322
|
+
// node_modules/diff/libesm/patch/reverse.js
|
|
17323
|
+
function reversePatch(structuredPatch) {
|
|
17324
|
+
if (Array.isArray(structuredPatch)) {
|
|
17325
|
+
return structuredPatch.map((patch) => reversePatch(patch)).reverse();
|
|
17326
|
+
}
|
|
17327
|
+
return Object.assign(Object.assign({}, structuredPatch), { oldFileName: structuredPatch.newFileName, oldHeader: structuredPatch.newHeader, newFileName: structuredPatch.oldFileName, newHeader: structuredPatch.oldHeader, hunks: structuredPatch.hunks.map((hunk) => {
|
|
17328
|
+
return {
|
|
17329
|
+
oldLines: hunk.newLines,
|
|
17330
|
+
oldStart: hunk.newStart,
|
|
17331
|
+
newLines: hunk.oldLines,
|
|
17332
|
+
newStart: hunk.oldStart,
|
|
17333
|
+
lines: hunk.lines.map((l) => {
|
|
17334
|
+
if (l.startsWith("-")) {
|
|
17335
|
+
return `+${l.slice(1)}`;
|
|
17336
|
+
}
|
|
17337
|
+
if (l.startsWith("+")) {
|
|
17338
|
+
return `-${l.slice(1)}`;
|
|
17339
|
+
}
|
|
17340
|
+
return l;
|
|
17341
|
+
})
|
|
17342
|
+
};
|
|
17343
|
+
}) });
|
|
17344
|
+
}
|
|
17345
|
+
|
|
17322
17346
|
// src/CodexToolCallMapper.ts
|
|
17323
17347
|
import { readFile } from "node:fs/promises";
|
|
17324
17348
|
import path2 from "node:path";
|
|
@@ -17521,80 +17545,93 @@ function createSearchTitle(query, path5) {
|
|
|
17521
17545
|
return "Search";
|
|
17522
17546
|
}
|
|
17523
17547
|
async function createPatchContent(change) {
|
|
17524
|
-
|
|
17525
|
-
|
|
17526
|
-
|
|
17527
|
-
|
|
17528
|
-
|
|
17529
|
-
|
|
17530
|
-
|
|
17531
|
-
|
|
17532
|
-
|
|
17533
|
-
|
|
17534
|
-
|
|
17535
|
-
if (change.kind.type === "delete") {
|
|
17536
|
-
const oldContent2 = await readFile(change.path, { encoding: "utf8" }).catch(
|
|
17537
|
-
() => isUnifiedDiff(change.diff) ? patchToDeletedContent(change.diff) : change.diff
|
|
17538
|
-
);
|
|
17539
|
-
return {
|
|
17540
|
-
type: "diff",
|
|
17541
|
-
oldText: oldContent2,
|
|
17542
|
-
newText: "",
|
|
17543
|
-
path: change.path,
|
|
17544
|
-
_meta: {
|
|
17545
|
-
kind: "delete"
|
|
17546
|
-
}
|
|
17547
|
-
};
|
|
17548
|
-
}
|
|
17549
|
-
const oldContent = change.kind.type === "add" ? "" : await readFile(change.path, { encoding: "utf8" }).catch(() => null);
|
|
17550
|
-
if (oldContent === null) {
|
|
17551
|
-
return null;
|
|
17552
|
-
}
|
|
17553
|
-
const newContent = applyPatch(oldContent, change.diff);
|
|
17554
|
-
if (newContent === false) {
|
|
17548
|
+
try {
|
|
17549
|
+
switch (change.kind.type) {
|
|
17550
|
+
case "add":
|
|
17551
|
+
return await createAddFileContent(change);
|
|
17552
|
+
case "delete":
|
|
17553
|
+
return await createDeleteFileContent(change);
|
|
17554
|
+
case "update":
|
|
17555
|
+
return await createUpdateFileContent(change);
|
|
17556
|
+
}
|
|
17557
|
+
} catch (error40) {
|
|
17558
|
+
logger.log(`Error processing file update change: ${error40}`);
|
|
17555
17559
|
return null;
|
|
17556
17560
|
}
|
|
17561
|
+
}
|
|
17562
|
+
async function createAddFileContent(change) {
|
|
17557
17563
|
return {
|
|
17558
17564
|
type: "diff",
|
|
17559
|
-
oldText:
|
|
17560
|
-
newText:
|
|
17565
|
+
oldText: null,
|
|
17566
|
+
newText: change.diff,
|
|
17567
|
+
// app-server always returns file content instead of diff
|
|
17561
17568
|
path: change.path,
|
|
17562
17569
|
_meta: {
|
|
17563
|
-
kind:
|
|
17570
|
+
kind: "add"
|
|
17564
17571
|
}
|
|
17565
17572
|
};
|
|
17566
17573
|
}
|
|
17567
|
-
function
|
|
17568
|
-
|
|
17569
|
-
|
|
17570
|
-
|
|
17571
|
-
|
|
17572
|
-
|
|
17573
|
-
|
|
17574
|
-
|
|
17575
|
-
|
|
17576
|
-
|
|
17577
|
-
|
|
17578
|
-
|
|
17579
|
-
|
|
17580
|
-
if (line === "\") {
|
|
17581
|
-
hasNoTrailingNewlineMarker = true;
|
|
17582
|
-
continue;
|
|
17583
|
-
}
|
|
17584
|
-
if (line.startsWith("-") || line.startsWith(" ")) {
|
|
17585
|
-
oldLines.push(line.slice(1));
|
|
17574
|
+
async function createUpdateFileContent(change) {
|
|
17575
|
+
if (change.kind.type !== "update") return null;
|
|
17576
|
+
const unifiedDiff = recoverCorruptedDiff(change.diff);
|
|
17577
|
+
const movePath = change.kind.move_path;
|
|
17578
|
+
const oldContent = await readFileContent(change.path);
|
|
17579
|
+
if (oldContent !== null) {
|
|
17580
|
+
const patchedContent = applyPatch(oldContent, unifiedDiff);
|
|
17581
|
+
if (patchedContent === false) {
|
|
17582
|
+
const revertedPatch2 = revertPatch(unifiedDiff);
|
|
17583
|
+
if (revertedPatch2) {
|
|
17584
|
+
const revertedContent2 = applyPatch(oldContent, revertedPatch2);
|
|
17585
|
+
if (revertedContent2 !== false) {
|
|
17586
|
+
return createUpdateDiffContent(change.path, revertedContent2, oldContent);
|
|
17586
17587
|
}
|
|
17587
17588
|
}
|
|
17589
|
+
return null;
|
|
17588
17590
|
}
|
|
17589
|
-
|
|
17590
|
-
return "";
|
|
17591
|
-
}
|
|
17592
|
-
const oldText = oldLines.join("\n");
|
|
17593
|
-
return hasNoTrailingNewlineMarker || !unifiedDiff.endsWith("\n") ? oldText : `${oldText}
|
|
17594
|
-
`;
|
|
17595
|
-
} catch {
|
|
17596
|
-
return null;
|
|
17591
|
+
return createUpdateDiffContent(movePath ?? change.path, oldContent, patchedContent);
|
|
17597
17592
|
}
|
|
17593
|
+
if (!movePath) return null;
|
|
17594
|
+
const newContent = await readFileContent(movePath);
|
|
17595
|
+
if (newContent === null) return null;
|
|
17596
|
+
const revertedPatch = revertPatch(unifiedDiff);
|
|
17597
|
+
if (!revertedPatch) return null;
|
|
17598
|
+
const revertedContent = applyPatch(newContent, revertedPatch);
|
|
17599
|
+
if (revertedContent === false) return null;
|
|
17600
|
+
return createUpdateDiffContent(movePath, revertedContent, newContent);
|
|
17601
|
+
}
|
|
17602
|
+
function revertPatch(unifiedDiff) {
|
|
17603
|
+
const [patch] = parsePatch(unifiedDiff);
|
|
17604
|
+
if (!patch) return null;
|
|
17605
|
+
return reversePatch(patch);
|
|
17606
|
+
}
|
|
17607
|
+
function createUpdateDiffContent(path5, oldText, newText) {
|
|
17608
|
+
return {
|
|
17609
|
+
type: "diff",
|
|
17610
|
+
oldText,
|
|
17611
|
+
newText,
|
|
17612
|
+
path: path5,
|
|
17613
|
+
_meta: {
|
|
17614
|
+
kind: "update"
|
|
17615
|
+
}
|
|
17616
|
+
};
|
|
17617
|
+
}
|
|
17618
|
+
async function createDeleteFileContent(change) {
|
|
17619
|
+
return {
|
|
17620
|
+
type: "diff",
|
|
17621
|
+
oldText: change.diff,
|
|
17622
|
+
// app-server always returns file content instead of diff
|
|
17623
|
+
newText: "",
|
|
17624
|
+
path: change.path,
|
|
17625
|
+
_meta: {
|
|
17626
|
+
kind: "delete"
|
|
17627
|
+
}
|
|
17628
|
+
};
|
|
17629
|
+
}
|
|
17630
|
+
async function readFileContent(filePath) {
|
|
17631
|
+
return await readFile(filePath, { encoding: "utf8" }).catch(() => null);
|
|
17632
|
+
}
|
|
17633
|
+
function recoverCorruptedDiff(diff) {
|
|
17634
|
+
return diff.replace(/\n\nMoved to: .*$/, "");
|
|
17598
17635
|
}
|
|
17599
17636
|
|
|
17600
17637
|
// src/CodexEventHandler.ts
|
|
@@ -17725,6 +17762,9 @@ var CodexEventHandler = class {
|
|
|
17725
17762
|
case "thread/goal/cleared":
|
|
17726
17763
|
case "remoteControl/status/changed":
|
|
17727
17764
|
case "app/list/updated":
|
|
17765
|
+
case "thread/settings/updated":
|
|
17766
|
+
case "process/outputDelta":
|
|
17767
|
+
case "process/exited":
|
|
17728
17768
|
return null;
|
|
17729
17769
|
}
|
|
17730
17770
|
}
|
|
@@ -18059,42 +18099,28 @@ var CodexApprovalHandler = class {
|
|
|
18059
18099
|
}
|
|
18060
18100
|
}
|
|
18061
18101
|
buildCommandPermissionRequest(sessionId, params) {
|
|
18062
|
-
const reasonContent = this.createContentFromReason(params.reason ?? null);
|
|
18063
18102
|
return {
|
|
18064
18103
|
sessionId,
|
|
18065
18104
|
toolCall: {
|
|
18066
18105
|
toolCallId: params.itemId,
|
|
18067
18106
|
kind: "execute",
|
|
18068
18107
|
status: "pending",
|
|
18069
|
-
content: reasonContent ? [reasonContent] : null,
|
|
18070
18108
|
rawInput: params.command ? { command: stripShellPrefix(params.command), cwd: params.cwd } : null
|
|
18071
18109
|
},
|
|
18072
|
-
options: APPROVAL_OPTIONS
|
|
18073
|
-
|
|
18074
|
-
}
|
|
18075
|
-
createContentFromReason(reason) {
|
|
18076
|
-
if (reason === null || reason === "") {
|
|
18077
|
-
return null;
|
|
18078
|
-
}
|
|
18079
|
-
return {
|
|
18080
|
-
type: "content",
|
|
18081
|
-
content: {
|
|
18082
|
-
type: "text",
|
|
18083
|
-
text: reason
|
|
18084
|
-
}
|
|
18110
|
+
options: APPROVAL_OPTIONS,
|
|
18111
|
+
_meta: { codex: { params } }
|
|
18085
18112
|
};
|
|
18086
18113
|
}
|
|
18087
18114
|
buildFileChangePermissionRequest(sessionId, params) {
|
|
18088
|
-
const reasonContent = this.createContentFromReason(params.reason ?? null);
|
|
18089
18115
|
return {
|
|
18090
18116
|
sessionId,
|
|
18091
18117
|
toolCall: {
|
|
18092
18118
|
toolCallId: params.itemId,
|
|
18093
18119
|
kind: "edit",
|
|
18094
|
-
status: "pending"
|
|
18095
|
-
content: reasonContent ? [reasonContent] : null
|
|
18120
|
+
status: "pending"
|
|
18096
18121
|
},
|
|
18097
|
-
options: APPROVAL_OPTIONS
|
|
18122
|
+
options: APPROVAL_OPTIONS,
|
|
18123
|
+
_meta: { codex: { params } }
|
|
18098
18124
|
};
|
|
18099
18125
|
}
|
|
18100
18126
|
convertCommandResponse(response) {
|
|
@@ -19002,6 +19028,7 @@ var ModelId = class _ModelId {
|
|
|
19002
19028
|
};
|
|
19003
19029
|
|
|
19004
19030
|
// src/AgentMode.ts
|
|
19031
|
+
var MODE_CONFIG_ID = "mode";
|
|
19005
19032
|
var AgentMode = class _AgentMode {
|
|
19006
19033
|
id;
|
|
19007
19034
|
name;
|
|
@@ -19064,6 +19091,21 @@ var AgentMode = class _AgentMode {
|
|
|
19064
19091
|
currentModeId: this.id
|
|
19065
19092
|
};
|
|
19066
19093
|
}
|
|
19094
|
+
toConfigOption() {
|
|
19095
|
+
return {
|
|
19096
|
+
id: MODE_CONFIG_ID,
|
|
19097
|
+
name: "Mode",
|
|
19098
|
+
description: "Approval and sandboxing preset for the session",
|
|
19099
|
+
category: "mode",
|
|
19100
|
+
type: "select",
|
|
19101
|
+
currentValue: this.id,
|
|
19102
|
+
options: _AgentMode.all().map((mode) => ({
|
|
19103
|
+
value: mode.id,
|
|
19104
|
+
name: mode.name,
|
|
19105
|
+
description: mode.description
|
|
19106
|
+
}))
|
|
19107
|
+
};
|
|
19108
|
+
}
|
|
19067
19109
|
static all() {
|
|
19068
19110
|
return [_AgentMode.ReadOnly, _AgentMode.Agent, _AgentMode.AgentFullAccess];
|
|
19069
19111
|
}
|
|
@@ -19084,13 +19126,19 @@ var AgentMode = class _AgentMode {
|
|
|
19084
19126
|
// src/CodexAcpClient.ts
|
|
19085
19127
|
import path4 from "node:path";
|
|
19086
19128
|
|
|
19129
|
+
// src/McpServerName.ts
|
|
19130
|
+
var MCP_SERVER_NAME_WHITESPACE = /\p{White_Space}/gu;
|
|
19131
|
+
function sanitizeMcpServerName(name) {
|
|
19132
|
+
return name.replace(MCP_SERVER_NAME_WHITESPACE, "_");
|
|
19133
|
+
}
|
|
19134
|
+
|
|
19087
19135
|
// package.json
|
|
19088
19136
|
var package_default = {
|
|
19089
19137
|
name: "@agentclientprotocol/codex-acp",
|
|
19090
19138
|
publishConfig: {
|
|
19091
19139
|
access: "public"
|
|
19092
19140
|
},
|
|
19093
|
-
version: "0.0.
|
|
19141
|
+
version: "0.0.46",
|
|
19094
19142
|
description: "",
|
|
19095
19143
|
main: "dist/index.js",
|
|
19096
19144
|
bin: {
|
|
@@ -19149,7 +19197,7 @@ var package_default = {
|
|
|
19149
19197
|
},
|
|
19150
19198
|
dependencies: {
|
|
19151
19199
|
"@agentclientprotocol/sdk": "^0.22.1",
|
|
19152
|
-
"@openai/codex": "^0.
|
|
19200
|
+
"@openai/codex": "^0.137.0",
|
|
19153
19201
|
diff: "^8.0.3",
|
|
19154
19202
|
open: "^11.0.0",
|
|
19155
19203
|
"vscode-jsonrpc": "^8.2.1"
|
|
@@ -19164,6 +19212,7 @@ var CodexAcpClient = class {
|
|
|
19164
19212
|
gatewayConfig;
|
|
19165
19213
|
pendingLoginCompleted = null;
|
|
19166
19214
|
pendingAccountUpdated = null;
|
|
19215
|
+
sessionNotificationQueues = /* @__PURE__ */ new Map();
|
|
19167
19216
|
constructor(codexClient, codexConfig, modelProvider) {
|
|
19168
19217
|
this.codexClient = codexClient;
|
|
19169
19218
|
this.config = codexConfig ?? {};
|
|
@@ -19289,7 +19338,7 @@ var CodexAcpClient = class {
|
|
|
19289
19338
|
async getAccount() {
|
|
19290
19339
|
return this.codexClient.accountRead({ refreshToken: false });
|
|
19291
19340
|
}
|
|
19292
|
-
async resumeSession(request) {
|
|
19341
|
+
async resumeSession(request, onSubscribed) {
|
|
19293
19342
|
await this.refreshSkills(request.cwd, request._meta);
|
|
19294
19343
|
const response = await this.codexClient.threadResume({
|
|
19295
19344
|
config: await this.createSessionConfig(request.cwd, request.mcpServers ?? []),
|
|
@@ -19297,6 +19346,7 @@ var CodexAcpClient = class {
|
|
|
19297
19346
|
modelProvider: this.getResumeModelProvider(),
|
|
19298
19347
|
threadId: request.sessionId
|
|
19299
19348
|
});
|
|
19349
|
+
onSubscribed?.();
|
|
19300
19350
|
const codexModels = await this.fetchAvailableModels();
|
|
19301
19351
|
const currentModelId = this.createModelId(codexModels, response.model, response.reasoningEffort).toString();
|
|
19302
19352
|
return {
|
|
@@ -19306,13 +19356,14 @@ var CodexAcpClient = class {
|
|
|
19306
19356
|
currentServiceTier: response.serviceTier ?? null
|
|
19307
19357
|
};
|
|
19308
19358
|
}
|
|
19309
|
-
async loadSession(request) {
|
|
19359
|
+
async loadSession(request, onSubscribed) {
|
|
19310
19360
|
const response = await this.codexClient.threadResume({
|
|
19311
19361
|
config: await this.createSessionConfig(request.cwd, request.mcpServers ?? []),
|
|
19312
19362
|
cwd: request.cwd,
|
|
19313
19363
|
modelProvider: this.getResumeModelProvider(),
|
|
19314
19364
|
threadId: request.sessionId
|
|
19315
19365
|
});
|
|
19366
|
+
onSubscribed?.();
|
|
19316
19367
|
const codexModels = await this.fetchAvailableModels();
|
|
19317
19368
|
const currentModelId = this.createModelId(codexModels, response.model, response.reasoningEffort).toString();
|
|
19318
19369
|
return {
|
|
@@ -19342,6 +19393,13 @@ var CodexAcpClient = class {
|
|
|
19342
19393
|
currentServiceTier: response.serviceTier ?? null
|
|
19343
19394
|
};
|
|
19344
19395
|
}
|
|
19396
|
+
async closeSession(sessionId) {
|
|
19397
|
+
try {
|
|
19398
|
+
await this.codexClient.threadUnsubscribe({ threadId: sessionId });
|
|
19399
|
+
} finally {
|
|
19400
|
+
this.codexClient.clearThreadHandlers(sessionId);
|
|
19401
|
+
}
|
|
19402
|
+
}
|
|
19345
19403
|
async awaitMcpServerStartup(serverNames, afterVersion) {
|
|
19346
19404
|
return await this.codexClient.awaitMcpServerStartup(serverNames, afterVersion);
|
|
19347
19405
|
}
|
|
@@ -19361,13 +19419,17 @@ var CodexAcpClient = class {
|
|
|
19361
19419
|
return mergedConfig;
|
|
19362
19420
|
}
|
|
19363
19421
|
const existingNames = await this.getConfigMcpServerNames(projectPath);
|
|
19364
|
-
const
|
|
19422
|
+
const requestedServers = mcpServers.map((mcp) => ({
|
|
19423
|
+
name: sanitizeMcpServerName(mcp.name),
|
|
19424
|
+
server: mcp
|
|
19425
|
+
}));
|
|
19426
|
+
const uniqueServers = requestedServers.filter((mcp) => !existingNames.has(mcp.name));
|
|
19365
19427
|
if (uniqueServers.length === 0) {
|
|
19366
19428
|
return mergedConfig;
|
|
19367
19429
|
}
|
|
19368
19430
|
return {
|
|
19369
19431
|
...mergedConfig,
|
|
19370
|
-
"mcp_servers": Object.fromEntries(uniqueServers.map((mcp) => [mcp.name, this.createMcpSeverConfig(mcp)]))
|
|
19432
|
+
"mcp_servers": Object.fromEntries(uniqueServers.map((mcp) => [mcp.name, this.createMcpSeverConfig(mcp.server)]))
|
|
19371
19433
|
};
|
|
19372
19434
|
}
|
|
19373
19435
|
async getConfigMcpServerNames(projectPath) {
|
|
@@ -19388,14 +19450,13 @@ var CodexAcpClient = class {
|
|
|
19388
19450
|
if (!cwd) {
|
|
19389
19451
|
return;
|
|
19390
19452
|
}
|
|
19391
|
-
const additionalRoots = readAdditionalRoots(meta);
|
|
19453
|
+
const additionalRoots = readAdditionalRoots(meta).map((root) => path4.join(root, ".agents", "skills"));
|
|
19454
|
+
if (additionalRoots.length > 0) {
|
|
19455
|
+
await this.codexClient.skillsExtraRootsSet({ extraRoots: additionalRoots });
|
|
19456
|
+
}
|
|
19392
19457
|
await this.codexClient.listSkills({
|
|
19393
19458
|
cwds: [cwd],
|
|
19394
|
-
forceReload: true
|
|
19395
|
-
perCwdExtraUserRoots: [{
|
|
19396
|
-
cwd,
|
|
19397
|
-
extraUserRoots: additionalRoots
|
|
19398
|
-
}]
|
|
19459
|
+
forceReload: true
|
|
19399
19460
|
});
|
|
19400
19461
|
}
|
|
19401
19462
|
/**
|
|
@@ -19433,14 +19494,57 @@ var CodexAcpClient = class {
|
|
|
19433
19494
|
return ModelId.create(selectedModel.id, reasoningEffort ?? selectedModel.defaultReasoningEffort);
|
|
19434
19495
|
}
|
|
19435
19496
|
async subscribeToSessionEvents(sessionId, eventHandler, approvalHandler, elicitationHandler) {
|
|
19436
|
-
this.codexClient.onServerNotification(sessionId,
|
|
19437
|
-
|
|
19438
|
-
|
|
19497
|
+
this.codexClient.onServerNotification(sessionId, (event) => {
|
|
19498
|
+
this.enqueueSessionNotification(sessionId, () => eventHandler(event));
|
|
19499
|
+
});
|
|
19500
|
+
this.codexClient.onApprovalRequest(sessionId, {
|
|
19501
|
+
handleCommandExecution: async (params) => {
|
|
19502
|
+
await this.waitForSessionNotifications(sessionId);
|
|
19503
|
+
return await approvalHandler.handleCommandExecution(params);
|
|
19504
|
+
},
|
|
19505
|
+
handleFileChange: async (params) => {
|
|
19506
|
+
await this.waitForSessionNotifications(sessionId);
|
|
19507
|
+
return await approvalHandler.handleFileChange(params);
|
|
19508
|
+
}
|
|
19509
|
+
});
|
|
19510
|
+
this.codexClient.onElicitationRequest(sessionId, {
|
|
19511
|
+
handleElicitation: async (params) => {
|
|
19512
|
+
await this.waitForSessionNotifications(sessionId);
|
|
19513
|
+
return await elicitationHandler.handleElicitation(params);
|
|
19514
|
+
}
|
|
19515
|
+
});
|
|
19516
|
+
}
|
|
19517
|
+
async waitForSessionNotifications(sessionId) {
|
|
19518
|
+
while (true) {
|
|
19519
|
+
const queue = this.sessionNotificationQueues.get(sessionId);
|
|
19520
|
+
if (!queue) return;
|
|
19521
|
+
await queue;
|
|
19522
|
+
}
|
|
19523
|
+
}
|
|
19524
|
+
enqueueSessionNotification(sessionId, operation) {
|
|
19525
|
+
const run = async () => {
|
|
19526
|
+
try {
|
|
19527
|
+
await operation();
|
|
19528
|
+
} catch (error40) {
|
|
19529
|
+
logger.error("Error handling Codex session notification", error40);
|
|
19530
|
+
}
|
|
19531
|
+
};
|
|
19532
|
+
const previous = this.sessionNotificationQueues.get(sessionId);
|
|
19533
|
+
const next = previous ? previous.then(run, run) : run();
|
|
19534
|
+
this.sessionNotificationQueues.set(sessionId, next);
|
|
19535
|
+
void next.finally(() => {
|
|
19536
|
+
if (this.sessionNotificationQueues.get(sessionId) === next) {
|
|
19537
|
+
this.sessionNotificationQueues.delete(sessionId);
|
|
19538
|
+
}
|
|
19539
|
+
});
|
|
19439
19540
|
}
|
|
19440
|
-
async sendPrompt(request, agentMode, modelId, serviceTier, disableSummary, cwd) {
|
|
19541
|
+
async sendPrompt(request, agentMode, modelId, serviceTier, disableSummary, cwd, onTurnStarted, shouldCancel) {
|
|
19441
19542
|
const input = buildPromptItems(request.prompt);
|
|
19442
19543
|
const effort = modelId.effort;
|
|
19443
19544
|
await this.refreshSkills(cwd, request._meta);
|
|
19545
|
+
if (shouldCancel?.()) {
|
|
19546
|
+
return null;
|
|
19547
|
+
}
|
|
19444
19548
|
return await this.codexClient.runTurn({
|
|
19445
19549
|
threadId: request.sessionId,
|
|
19446
19550
|
input,
|
|
@@ -19450,7 +19554,13 @@ var CodexAcpClient = class {
|
|
|
19450
19554
|
effort,
|
|
19451
19555
|
model: modelId.model,
|
|
19452
19556
|
serviceTier
|
|
19453
|
-
});
|
|
19557
|
+
}, onTurnStarted);
|
|
19558
|
+
}
|
|
19559
|
+
resolveTurnInterrupted(params) {
|
|
19560
|
+
this.codexClient.resolveTurnInterrupted(params.threadId, params.turnId);
|
|
19561
|
+
}
|
|
19562
|
+
markTurnStale(params) {
|
|
19563
|
+
this.codexClient.markTurnStale(params.threadId, params.turnId);
|
|
19454
19564
|
}
|
|
19455
19565
|
async listSkills(params) {
|
|
19456
19566
|
return this.codexClient.listSkills(params ?? {});
|
|
@@ -19651,6 +19761,44 @@ function mergeGatewayConfig(config2, gatewayConfig) {
|
|
|
19651
19761
|
}
|
|
19652
19762
|
}
|
|
19653
19763
|
|
|
19764
|
+
// src/ModelConfigOption.ts
|
|
19765
|
+
var MODEL_CONFIG_ID = "model";
|
|
19766
|
+
var REASONING_EFFORT_CONFIG_ID = "reasoning_effort";
|
|
19767
|
+
function findSupportedEffort(options, effort) {
|
|
19768
|
+
if (!effort) return void 0;
|
|
19769
|
+
return options.find((o) => o.reasoningEffort === effort)?.reasoningEffort;
|
|
19770
|
+
}
|
|
19771
|
+
function createModelConfigOption(availableModels, currentBaseModelId) {
|
|
19772
|
+
return {
|
|
19773
|
+
id: MODEL_CONFIG_ID,
|
|
19774
|
+
name: "Model",
|
|
19775
|
+
description: "Model Codex uses for the session",
|
|
19776
|
+
category: "model",
|
|
19777
|
+
type: "select",
|
|
19778
|
+
currentValue: currentBaseModelId,
|
|
19779
|
+
options: availableModels.map((model) => ({
|
|
19780
|
+
value: model.id,
|
|
19781
|
+
name: model.displayName,
|
|
19782
|
+
description: model.description
|
|
19783
|
+
}))
|
|
19784
|
+
};
|
|
19785
|
+
}
|
|
19786
|
+
function createReasoningEffortConfigOption(supportedReasoningEfforts, currentEffort) {
|
|
19787
|
+
return {
|
|
19788
|
+
id: REASONING_EFFORT_CONFIG_ID,
|
|
19789
|
+
name: "Reasoning effort",
|
|
19790
|
+
description: "How much reasoning effort the model should use",
|
|
19791
|
+
category: "thought_level",
|
|
19792
|
+
type: "select",
|
|
19793
|
+
currentValue: currentEffort,
|
|
19794
|
+
options: supportedReasoningEfforts.map((option) => ({
|
|
19795
|
+
value: option.reasoningEffort,
|
|
19796
|
+
name: option.reasoningEffort,
|
|
19797
|
+
description: option.description
|
|
19798
|
+
}))
|
|
19799
|
+
};
|
|
19800
|
+
}
|
|
19801
|
+
|
|
19654
19802
|
// src/CodexCommands.ts
|
|
19655
19803
|
var CodexCommands = class {
|
|
19656
19804
|
connection;
|
|
@@ -19736,6 +19884,7 @@ var CodexCommands = class {
|
|
|
19736
19884
|
async tryHandleCommand(prompt, sessionState) {
|
|
19737
19885
|
const commandName = this.getCommandName(prompt);
|
|
19738
19886
|
if (commandName === null) return false;
|
|
19887
|
+
if (commandName.startsWith("$")) return false;
|
|
19739
19888
|
const sessionId = sessionState.sessionId;
|
|
19740
19889
|
switch (commandName) {
|
|
19741
19890
|
case "status": {
|
|
@@ -19980,6 +20129,17 @@ function createFastModeConfigOption(fastModeEnabled) {
|
|
|
19980
20129
|
};
|
|
19981
20130
|
}
|
|
19982
20131
|
|
|
20132
|
+
// src/JBUtils.ts
|
|
20133
|
+
function isJetBrains2026_1Client(clientInfo) {
|
|
20134
|
+
if (!clientInfo) {
|
|
20135
|
+
return false;
|
|
20136
|
+
}
|
|
20137
|
+
const platform2 = clientInfo._meta?.["platform"];
|
|
20138
|
+
const isIntelliJPlatform = platform2 === "intellij";
|
|
20139
|
+
const isJetBrainsClient = clientInfo.name.startsWith("JetBrains");
|
|
20140
|
+
return (isIntelliJPlatform || isJetBrainsClient) && clientInfo.version.startsWith("2026.1");
|
|
20141
|
+
}
|
|
20142
|
+
|
|
19983
20143
|
// src/CodexAcpServer.ts
|
|
19984
20144
|
var CodexAcpServer = class _CodexAcpServer {
|
|
19985
20145
|
static MODEL_NAME_TOKEN_OVERRIDES = {
|
|
@@ -19992,15 +20152,27 @@ var CodexAcpServer = class _CodexAcpServer {
|
|
|
19992
20152
|
defaultAuthRequest;
|
|
19993
20153
|
getExitCode;
|
|
19994
20154
|
availableCommands;
|
|
20155
|
+
clientInfo;
|
|
19995
20156
|
sessions;
|
|
19996
20157
|
pendingMcpStartupSessions;
|
|
20158
|
+
pendingTurnStarts;
|
|
20159
|
+
activePrompts;
|
|
20160
|
+
closingSessions;
|
|
20161
|
+
sessionGenerations;
|
|
20162
|
+
sessionOpenGenerations;
|
|
19997
20163
|
constructor(connection, codexAcpClient, defaultAuthRequest, getExitCode) {
|
|
19998
20164
|
this.sessions = /* @__PURE__ */ new Map();
|
|
19999
20165
|
this.pendingMcpStartupSessions = /* @__PURE__ */ new Map();
|
|
20166
|
+
this.pendingTurnStarts = /* @__PURE__ */ new Map();
|
|
20167
|
+
this.activePrompts = /* @__PURE__ */ new Map();
|
|
20168
|
+
this.closingSessions = /* @__PURE__ */ new Map();
|
|
20169
|
+
this.sessionGenerations = /* @__PURE__ */ new Map();
|
|
20170
|
+
this.sessionOpenGenerations = /* @__PURE__ */ new Map();
|
|
20000
20171
|
this.connection = connection;
|
|
20001
20172
|
this.codexAcpClient = codexAcpClient;
|
|
20002
20173
|
this.defaultAuthRequest = defaultAuthRequest ?? null;
|
|
20003
20174
|
this.getExitCode = getExitCode ?? (() => null);
|
|
20175
|
+
this.clientInfo = null;
|
|
20004
20176
|
this.availableCommands = new CodexCommands(
|
|
20005
20177
|
connection,
|
|
20006
20178
|
codexAcpClient,
|
|
@@ -20009,6 +20181,7 @@ var CodexAcpServer = class _CodexAcpServer {
|
|
|
20009
20181
|
}
|
|
20010
20182
|
async initialize(_params) {
|
|
20011
20183
|
logger.log("Initialize request received");
|
|
20184
|
+
this.clientInfo = _params.clientInfo ?? null;
|
|
20012
20185
|
await this.runWithProcessCheck(() => this.codexAcpClient.initialize(_params));
|
|
20013
20186
|
return {
|
|
20014
20187
|
protocolVersion: PROTOCOL_VERSION,
|
|
@@ -20028,7 +20201,8 @@ var CodexAcpServer = class _CodexAcpServer {
|
|
|
20028
20201
|
},
|
|
20029
20202
|
sessionCapabilities: {
|
|
20030
20203
|
resume: {},
|
|
20031
|
-
list: {}
|
|
20204
|
+
list: {},
|
|
20205
|
+
close: {}
|
|
20032
20206
|
},
|
|
20033
20207
|
mcpCapabilities: {
|
|
20034
20208
|
acp: false,
|
|
@@ -20086,26 +20260,107 @@ var CodexAcpServer = class _CodexAcpServer {
|
|
|
20086
20260
|
You have been logged out. Please try again.`);
|
|
20087
20261
|
}
|
|
20088
20262
|
}
|
|
20263
|
+
beginSessionOpen(sessionId) {
|
|
20264
|
+
const generation = this.getSessionGeneration(sessionId);
|
|
20265
|
+
if (this.sessionIsClosing(sessionId)) {
|
|
20266
|
+
throw RequestError.invalidRequest(`Session ${sessionId} is closing`);
|
|
20267
|
+
}
|
|
20268
|
+
this.sessionOpenGenerations.set(sessionId, generation);
|
|
20269
|
+
return generation;
|
|
20270
|
+
}
|
|
20271
|
+
sessionOpenCanInstall(sessionId, generation) {
|
|
20272
|
+
return !this.sessionIsClosing(sessionId) && this.getSessionGeneration(sessionId) === generation;
|
|
20273
|
+
}
|
|
20274
|
+
async cleanupStaleSessionOpen(sessionId, generation) {
|
|
20275
|
+
if (this.sessionOpenGenerations.get(sessionId) === generation) {
|
|
20276
|
+
if (!this.sessionIsClosing(sessionId)) {
|
|
20277
|
+
this.bumpSessionGeneration(sessionId);
|
|
20278
|
+
}
|
|
20279
|
+
this.beginSessionCloseFence(sessionId);
|
|
20280
|
+
try {
|
|
20281
|
+
await this.runWithProcessCheck(() => this.codexAcpClient.closeSession(sessionId));
|
|
20282
|
+
} catch (err) {
|
|
20283
|
+
logger.error(`Failed to close stale session open for ${sessionId}`, err);
|
|
20284
|
+
} finally {
|
|
20285
|
+
this.endSessionCloseFence(sessionId);
|
|
20286
|
+
}
|
|
20287
|
+
return true;
|
|
20288
|
+
}
|
|
20289
|
+
return false;
|
|
20290
|
+
}
|
|
20291
|
+
async closeStaleSessionOpen(sessionId, generation) {
|
|
20292
|
+
await this.cleanupStaleSessionOpen(sessionId, generation);
|
|
20293
|
+
throw RequestError.invalidRequest(`Session ${sessionId} is closing`);
|
|
20294
|
+
}
|
|
20295
|
+
sessionIsClosing(sessionId) {
|
|
20296
|
+
return (this.closingSessions.get(sessionId) ?? 0) > 0;
|
|
20297
|
+
}
|
|
20298
|
+
beginSessionCloseFence(sessionId) {
|
|
20299
|
+
this.closingSessions.set(sessionId, (this.closingSessions.get(sessionId) ?? 0) + 1);
|
|
20300
|
+
}
|
|
20301
|
+
endSessionCloseFence(sessionId) {
|
|
20302
|
+
const count = this.closingSessions.get(sessionId) ?? 0;
|
|
20303
|
+
if (count <= 1) {
|
|
20304
|
+
this.closingSessions.delete(sessionId);
|
|
20305
|
+
return;
|
|
20306
|
+
}
|
|
20307
|
+
this.closingSessions.set(sessionId, count - 1);
|
|
20308
|
+
}
|
|
20309
|
+
getSessionGeneration(sessionId) {
|
|
20310
|
+
return this.sessionGenerations.get(sessionId) ?? 0;
|
|
20311
|
+
}
|
|
20312
|
+
bumpSessionGeneration(sessionId) {
|
|
20313
|
+
const generation = this.getSessionGeneration(sessionId) + 1;
|
|
20314
|
+
this.sessionGenerations.set(sessionId, generation);
|
|
20315
|
+
return generation;
|
|
20316
|
+
}
|
|
20089
20317
|
async tryCreateSession(request) {
|
|
20318
|
+
const requestedSessionGeneration = "sessionId" in request ? this.beginSessionOpen(request.sessionId) : null;
|
|
20090
20319
|
await this.checkAuthorization();
|
|
20091
20320
|
const requestedMcpServers = request.mcpServers ?? [];
|
|
20092
20321
|
const mcpServerStartupVersion = requestedMcpServers.length > 0 ? this.codexAcpClient.getMcpServerStartupVersion() : null;
|
|
20093
20322
|
let sessionMetadata;
|
|
20323
|
+
let resumeSubscribed = false;
|
|
20094
20324
|
if ("sessionId" in request) {
|
|
20095
20325
|
logger.log(`Resume existing session: ${request.sessionId}...`);
|
|
20096
|
-
|
|
20326
|
+
try {
|
|
20327
|
+
sessionMetadata = await this.runWithProcessCheck(
|
|
20328
|
+
() => this.codexAcpClient.resumeSession(request, () => {
|
|
20329
|
+
resumeSubscribed = true;
|
|
20330
|
+
})
|
|
20331
|
+
);
|
|
20332
|
+
} catch (err) {
|
|
20333
|
+
if (resumeSubscribed && requestedSessionGeneration !== null) {
|
|
20334
|
+
await this.cleanupStaleSessionOpen(request.sessionId, requestedSessionGeneration);
|
|
20335
|
+
}
|
|
20336
|
+
throw err;
|
|
20337
|
+
}
|
|
20097
20338
|
} else {
|
|
20098
20339
|
logger.log(`Create new session...`);
|
|
20099
20340
|
sessionMetadata = await this.runWithProcessCheck(() => this.codexAcpClient.newSession(request));
|
|
20100
20341
|
}
|
|
20101
|
-
const account = await this.getActiveAccount();
|
|
20102
20342
|
const { sessionId, currentModelId, models } = sessionMetadata;
|
|
20343
|
+
let account;
|
|
20344
|
+
try {
|
|
20345
|
+
account = await this.getActiveAccount();
|
|
20346
|
+
} catch (err) {
|
|
20347
|
+
if (resumeSubscribed && requestedSessionGeneration !== null) {
|
|
20348
|
+
await this.cleanupStaleSessionOpen(sessionId, requestedSessionGeneration);
|
|
20349
|
+
}
|
|
20350
|
+
throw err;
|
|
20351
|
+
}
|
|
20352
|
+
const sessionGeneration = requestedSessionGeneration ?? this.beginSessionOpen(sessionId);
|
|
20353
|
+
if (!this.sessionOpenCanInstall(sessionId, sessionGeneration)) {
|
|
20354
|
+
resumeSubscribed = false;
|
|
20355
|
+
await this.closeStaleSessionOpen(sessionId, sessionGeneration);
|
|
20356
|
+
}
|
|
20103
20357
|
const sessionMcpServers = this.resolveSessionMcpServers(requestedMcpServers, "sessionId" in request);
|
|
20104
20358
|
const currentModel = this.findCurrentModel(models, currentModelId);
|
|
20105
20359
|
const currentModelSupportsFast = modelSupportsFast(currentModel);
|
|
20106
20360
|
const sessionState = {
|
|
20107
20361
|
sessionId,
|
|
20108
20362
|
currentModelId,
|
|
20363
|
+
availableModels: models,
|
|
20109
20364
|
supportedReasoningEfforts: currentModel?.supportedReasoningEfforts ?? [],
|
|
20110
20365
|
supportedInputModalities: currentModel?.inputModalities ?? ["text", "image"],
|
|
20111
20366
|
agentMode: AgentMode.getInitialAgentMode(),
|
|
@@ -20121,9 +20376,10 @@ You have been logged out. Please try again.`);
|
|
|
20121
20376
|
sessionMcpServers
|
|
20122
20377
|
};
|
|
20123
20378
|
this.sessions.set(sessionId, sessionState);
|
|
20379
|
+
resumeSubscribed = false;
|
|
20124
20380
|
if (requestedMcpServers.length > 0 && mcpServerStartupVersion !== null) {
|
|
20125
20381
|
this.pendingMcpStartupSessions.set(sessionId, {
|
|
20126
|
-
requestedServers: new Set(requestedMcpServers
|
|
20382
|
+
requestedServers: new Set(getRequestedMcpServerNames(requestedMcpServers)),
|
|
20127
20383
|
afterVersion: mcpServerStartupVersion
|
|
20128
20384
|
});
|
|
20129
20385
|
this.publishMcpStartupStatusAsync(sessionId);
|
|
@@ -20157,7 +20413,7 @@ You have been logged out. Please try again.`);
|
|
|
20157
20413
|
return {
|
|
20158
20414
|
models: modelState,
|
|
20159
20415
|
modes: modeState,
|
|
20160
|
-
|
|
20416
|
+
...this.createSessionConfigOptionsResponse(this.getSessionState(sessionId))
|
|
20161
20417
|
};
|
|
20162
20418
|
}
|
|
20163
20419
|
async resumeSession(params) {
|
|
@@ -20171,7 +20427,7 @@ You have been logged out. Please try again.`);
|
|
|
20171
20427
|
return {
|
|
20172
20428
|
models: modelState,
|
|
20173
20429
|
modes: modeState,
|
|
20174
|
-
|
|
20430
|
+
...this.createSessionConfigOptionsResponse(this.getSessionState(sessionId))
|
|
20175
20431
|
};
|
|
20176
20432
|
}
|
|
20177
20433
|
async listSessions(params) {
|
|
@@ -20179,6 +20435,35 @@ You have been logged out. Please try again.`);
|
|
|
20179
20435
|
await this.checkAuthorization();
|
|
20180
20436
|
return await this.runWithProcessCheck(() => this.codexAcpClient.listSessions(params));
|
|
20181
20437
|
}
|
|
20438
|
+
async closeSession(params) {
|
|
20439
|
+
logger.log("Closing session...", { sessionId: params.sessionId });
|
|
20440
|
+
const closeGeneration = this.bumpSessionGeneration(params.sessionId);
|
|
20441
|
+
const sessionState = this.sessions.get(params.sessionId);
|
|
20442
|
+
this.beginSessionCloseFence(params.sessionId);
|
|
20443
|
+
try {
|
|
20444
|
+
if (sessionState) {
|
|
20445
|
+
await this.interruptSessionTurn(sessionState, "Close", true);
|
|
20446
|
+
} else {
|
|
20447
|
+
logger.log("Close request received for unknown local session", { sessionId: params.sessionId });
|
|
20448
|
+
}
|
|
20449
|
+
const activePrompt = this.activePrompts.get(params.sessionId);
|
|
20450
|
+
if (activePrompt) {
|
|
20451
|
+
activePrompt.requestClose();
|
|
20452
|
+
await activePrompt.completion;
|
|
20453
|
+
}
|
|
20454
|
+
await this.runWithProcessCheck(() => this.codexAcpClient.closeSession(params.sessionId));
|
|
20455
|
+
logger.log("Session closed", { sessionId: params.sessionId });
|
|
20456
|
+
} finally {
|
|
20457
|
+
if (this.getSessionGeneration(params.sessionId) === closeGeneration) {
|
|
20458
|
+
this.sessions.delete(params.sessionId);
|
|
20459
|
+
this.pendingMcpStartupSessions.delete(params.sessionId);
|
|
20460
|
+
this.pendingTurnStarts.delete(params.sessionId);
|
|
20461
|
+
this.activePrompts.delete(params.sessionId);
|
|
20462
|
+
}
|
|
20463
|
+
this.endSessionCloseFence(params.sessionId);
|
|
20464
|
+
}
|
|
20465
|
+
return {};
|
|
20466
|
+
}
|
|
20182
20467
|
async newSession(params) {
|
|
20183
20468
|
logger.log("Starting new session...");
|
|
20184
20469
|
const [sessionId, modelState, modeState] = await this.getOrCreateSession(params);
|
|
@@ -20191,7 +20476,7 @@ You have been logged out. Please try again.`);
|
|
|
20191
20476
|
sessionId,
|
|
20192
20477
|
models: modelState,
|
|
20193
20478
|
modes: modeState,
|
|
20194
|
-
|
|
20479
|
+
...this.createSessionConfigOptionsResponse(this.getSessionState(sessionId))
|
|
20195
20480
|
};
|
|
20196
20481
|
}
|
|
20197
20482
|
async authenticate(_params) {
|
|
@@ -20216,11 +20501,7 @@ You have been logged out. Please try again.`);
|
|
|
20216
20501
|
});
|
|
20217
20502
|
const sessionState = this.sessions.get(_params.sessionId);
|
|
20218
20503
|
if (!sessionState) throw new Error(`Session ${_params.sessionId} not found`);
|
|
20219
|
-
|
|
20220
|
-
if (!newMode) {
|
|
20221
|
-
throw RequestError.invalidParams();
|
|
20222
|
-
}
|
|
20223
|
-
sessionState.agentMode = newMode;
|
|
20504
|
+
this.applyModeChange(sessionState, _params.modeId);
|
|
20224
20505
|
return {};
|
|
20225
20506
|
}
|
|
20226
20507
|
async setSessionConfigOption(params) {
|
|
@@ -20230,17 +20511,66 @@ You have been logged out. Please try again.`);
|
|
|
20230
20511
|
});
|
|
20231
20512
|
const sessionState = this.sessions.get(params.sessionId);
|
|
20232
20513
|
if (!sessionState) throw new Error(`Session ${params.sessionId} not found`);
|
|
20233
|
-
if (params.
|
|
20514
|
+
if (typeof params.value !== "string") {
|
|
20234
20515
|
throw RequestError.invalidParams();
|
|
20235
20516
|
}
|
|
20236
|
-
|
|
20237
|
-
|
|
20517
|
+
const value = params.value;
|
|
20518
|
+
switch (params.configId) {
|
|
20519
|
+
case FAST_MODE_CONFIG_ID:
|
|
20520
|
+
this.applyFastModeChange(sessionState, value);
|
|
20521
|
+
break;
|
|
20522
|
+
case MODE_CONFIG_ID:
|
|
20523
|
+
this.applyModeChange(sessionState, value);
|
|
20524
|
+
break;
|
|
20525
|
+
case MODEL_CONFIG_ID:
|
|
20526
|
+
this.applyModelChange(sessionState, value);
|
|
20527
|
+
break;
|
|
20528
|
+
case REASONING_EFFORT_CONFIG_ID:
|
|
20529
|
+
this.applyReasoningEffortChange(sessionState, value);
|
|
20530
|
+
break;
|
|
20531
|
+
default:
|
|
20532
|
+
throw RequestError.invalidParams();
|
|
20238
20533
|
}
|
|
20239
|
-
sessionState.fastModeEnabled = params.value === FAST_MODE_ON;
|
|
20240
20534
|
return {
|
|
20241
20535
|
configOptions: this.createSessionConfigOptions(sessionState)
|
|
20242
20536
|
};
|
|
20243
20537
|
}
|
|
20538
|
+
applyFastModeChange(sessionState, value) {
|
|
20539
|
+
if (value !== FAST_MODE_ON && value !== FAST_MODE_OFF) {
|
|
20540
|
+
throw RequestError.invalidParams();
|
|
20541
|
+
}
|
|
20542
|
+
sessionState.fastModeEnabled = value === FAST_MODE_ON;
|
|
20543
|
+
}
|
|
20544
|
+
applyModeChange(sessionState, value) {
|
|
20545
|
+
const newMode = AgentMode.find(value);
|
|
20546
|
+
if (!newMode) {
|
|
20547
|
+
throw RequestError.invalidParams();
|
|
20548
|
+
}
|
|
20549
|
+
sessionState.agentMode = newMode;
|
|
20550
|
+
}
|
|
20551
|
+
applyModelChange(sessionState, value) {
|
|
20552
|
+
const model = sessionState.availableModels.find((m) => m.id === value);
|
|
20553
|
+
if (!model) {
|
|
20554
|
+
throw RequestError.invalidParams();
|
|
20555
|
+
}
|
|
20556
|
+
const currentEffort = ModelId.fromString(sessionState.currentModelId).effort;
|
|
20557
|
+
const effort = findSupportedEffort(model.supportedReasoningEfforts, currentEffort) ?? model.defaultReasoningEffort;
|
|
20558
|
+
this.applyModelAndEffort(sessionState, model, effort);
|
|
20559
|
+
}
|
|
20560
|
+
applyReasoningEffortChange(sessionState, value) {
|
|
20561
|
+
const effort = findSupportedEffort(sessionState.supportedReasoningEfforts, value);
|
|
20562
|
+
if (!effort) {
|
|
20563
|
+
throw RequestError.invalidParams();
|
|
20564
|
+
}
|
|
20565
|
+
const { model } = ModelId.fromString(sessionState.currentModelId);
|
|
20566
|
+
sessionState.currentModelId = ModelId.create(model, effort).toString();
|
|
20567
|
+
}
|
|
20568
|
+
applyModelAndEffort(sessionState, model, effort) {
|
|
20569
|
+
sessionState.currentModelId = ModelId.fromComponents(model, effort).toString();
|
|
20570
|
+
sessionState.supportedReasoningEfforts = model.supportedReasoningEfforts;
|
|
20571
|
+
sessionState.supportedInputModalities = model.inputModalities;
|
|
20572
|
+
sessionState.currentModelSupportsFast = modelSupportsFast(model);
|
|
20573
|
+
}
|
|
20244
20574
|
async unstable_setSessionModel(params) {
|
|
20245
20575
|
logger.log("Set session model requested", {
|
|
20246
20576
|
sessionId: params.sessionId,
|
|
@@ -20248,36 +20578,44 @@ You have been logged out. Please try again.`);
|
|
|
20248
20578
|
});
|
|
20249
20579
|
const sessionState = this.sessions.get(params.sessionId);
|
|
20250
20580
|
if (!sessionState) throw new Error(`Session ${params.sessionId} not found`);
|
|
20251
|
-
const
|
|
20252
|
-
const requestedModelName = requestedModelId.model;
|
|
20253
|
-
const requestedEffort = requestedModelId.effort;
|
|
20581
|
+
const { model: requestedModelName, effort: requestedEffort } = ModelId.fromString(params.modelId);
|
|
20254
20582
|
const models = await this.codexAcpClient.fetchAvailableModels();
|
|
20255
20583
|
const model = models.find((m) => m.id === requestedModelName);
|
|
20256
20584
|
if (!model) throw new Error(`Unknown model ${params.modelId}`);
|
|
20257
|
-
const requestedEffortValue = requestedEffort;
|
|
20258
20585
|
let reasoningEffort;
|
|
20259
|
-
if (
|
|
20260
|
-
const matchedEffort = model.supportedReasoningEfforts
|
|
20261
|
-
(option) => option.reasoningEffort === requestedEffortValue
|
|
20262
|
-
)?.reasoningEffort;
|
|
20586
|
+
if (requestedEffort) {
|
|
20587
|
+
const matchedEffort = findSupportedEffort(model.supportedReasoningEfforts, requestedEffort);
|
|
20263
20588
|
if (!matchedEffort) {
|
|
20264
|
-
throw new Error(`Unsupported reasoning effort ${
|
|
20589
|
+
throw new Error(`Unsupported reasoning effort ${requestedEffort} for model ${requestedModelName}`);
|
|
20265
20590
|
}
|
|
20266
20591
|
reasoningEffort = matchedEffort;
|
|
20267
20592
|
} else {
|
|
20268
20593
|
reasoningEffort = model.defaultReasoningEffort;
|
|
20269
20594
|
}
|
|
20270
|
-
sessionState.
|
|
20271
|
-
sessionState
|
|
20272
|
-
sessionState.supportedInputModalities = model.inputModalities;
|
|
20273
|
-
sessionState.currentModelSupportsFast = modelSupportsFast(model);
|
|
20595
|
+
sessionState.availableModels = models;
|
|
20596
|
+
this.applyModelAndEffort(sessionState, model, reasoningEffort);
|
|
20274
20597
|
return {};
|
|
20275
20598
|
}
|
|
20276
20599
|
createSessionConfigOptions(sessionState) {
|
|
20600
|
+
const currentModelId = ModelId.fromString(sessionState.currentModelId);
|
|
20277
20601
|
return [
|
|
20602
|
+
sessionState.agentMode.toConfigOption(),
|
|
20603
|
+
createModelConfigOption(sessionState.availableModels, currentModelId.model),
|
|
20604
|
+
createReasoningEffortConfigOption(sessionState.supportedReasoningEfforts, currentModelId.effort),
|
|
20278
20605
|
createFastModeConfigOption(sessionState.fastModeEnabled)
|
|
20279
20606
|
];
|
|
20280
20607
|
}
|
|
20608
|
+
createSessionConfigOptionsResponse(sessionState) {
|
|
20609
|
+
if (!this.isSessionConfigEnabled()) {
|
|
20610
|
+
return {};
|
|
20611
|
+
}
|
|
20612
|
+
return {
|
|
20613
|
+
configOptions: this.createSessionConfigOptions(sessionState)
|
|
20614
|
+
};
|
|
20615
|
+
}
|
|
20616
|
+
isSessionConfigEnabled() {
|
|
20617
|
+
return !isJetBrains2026_1Client(this.clientInfo);
|
|
20618
|
+
}
|
|
20281
20619
|
publishAvailableCommandsAsync(sessionId) {
|
|
20282
20620
|
void this.availableCommands.publish(sessionId);
|
|
20283
20621
|
}
|
|
@@ -20302,21 +20640,46 @@ You have been logged out. Please try again.`);
|
|
|
20302
20640
|
};
|
|
20303
20641
|
}
|
|
20304
20642
|
async getOrCreateSessionWithHistory(request) {
|
|
20643
|
+
const requestedSessionGeneration = this.beginSessionOpen(request.sessionId);
|
|
20305
20644
|
await this.checkAuthorization();
|
|
20306
20645
|
const requestedMcpServers = request.mcpServers ?? [];
|
|
20307
20646
|
const mcpServerStartupVersion = requestedMcpServers.length > 0 ? this.codexAcpClient.getMcpServerStartupVersion() : null;
|
|
20308
20647
|
logger.log(`Load existing session: ${request.sessionId}...`);
|
|
20309
|
-
|
|
20310
|
-
|
|
20311
|
-
|
|
20312
|
-
|
|
20648
|
+
let subscribed = false;
|
|
20649
|
+
let sessionMetadata;
|
|
20650
|
+
try {
|
|
20651
|
+
sessionMetadata = await this.runWithProcessCheck(
|
|
20652
|
+
() => this.codexAcpClient.loadSession(request, () => {
|
|
20653
|
+
subscribed = true;
|
|
20654
|
+
})
|
|
20655
|
+
);
|
|
20656
|
+
} catch (err) {
|
|
20657
|
+
if (subscribed) {
|
|
20658
|
+
await this.cleanupStaleSessionOpen(request.sessionId, requestedSessionGeneration);
|
|
20659
|
+
}
|
|
20660
|
+
throw err;
|
|
20661
|
+
}
|
|
20313
20662
|
const { sessionId, currentModelId, models, thread } = sessionMetadata;
|
|
20663
|
+
let account;
|
|
20664
|
+
try {
|
|
20665
|
+
account = await this.getActiveAccount();
|
|
20666
|
+
} catch (err) {
|
|
20667
|
+
if (subscribed) {
|
|
20668
|
+
await this.cleanupStaleSessionOpen(request.sessionId, requestedSessionGeneration);
|
|
20669
|
+
}
|
|
20670
|
+
throw err;
|
|
20671
|
+
}
|
|
20672
|
+
if (!this.sessionOpenCanInstall(sessionId, requestedSessionGeneration)) {
|
|
20673
|
+
subscribed = false;
|
|
20674
|
+
await this.closeStaleSessionOpen(sessionId, requestedSessionGeneration);
|
|
20675
|
+
}
|
|
20314
20676
|
const sessionMcpServers = this.resolveSessionMcpServers(requestedMcpServers, true);
|
|
20315
20677
|
const currentModel = this.findCurrentModel(models, currentModelId);
|
|
20316
20678
|
const currentModelSupportsFast = modelSupportsFast(currentModel);
|
|
20317
20679
|
const sessionState = {
|
|
20318
20680
|
sessionId,
|
|
20319
20681
|
currentModelId,
|
|
20682
|
+
availableModels: models,
|
|
20320
20683
|
supportedReasoningEfforts: currentModel?.supportedReasoningEfforts ?? [],
|
|
20321
20684
|
supportedInputModalities: currentModel?.inputModalities ?? ["text", "image"],
|
|
20322
20685
|
agentMode: AgentMode.getInitialAgentMode(),
|
|
@@ -20332,9 +20695,10 @@ You have been logged out. Please try again.`);
|
|
|
20332
20695
|
sessionMcpServers
|
|
20333
20696
|
};
|
|
20334
20697
|
this.sessions.set(sessionId, sessionState);
|
|
20698
|
+
subscribed = false;
|
|
20335
20699
|
if (requestedMcpServers.length > 0 && mcpServerStartupVersion !== null) {
|
|
20336
20700
|
this.pendingMcpStartupSessions.set(sessionId, {
|
|
20337
|
-
requestedServers: new Set(requestedMcpServers
|
|
20701
|
+
requestedServers: new Set(getRequestedMcpServerNames(requestedMcpServers)),
|
|
20338
20702
|
afterVersion: mcpServerStartupVersion
|
|
20339
20703
|
});
|
|
20340
20704
|
this.publishMcpStartupStatusAsync(sessionId);
|
|
@@ -20580,11 +20944,16 @@ ${item.text}`
|
|
|
20580
20944
|
pendingStartup.afterVersion
|
|
20581
20945
|
)
|
|
20582
20946
|
);
|
|
20947
|
+
if (!this.sessions.has(sessionId) || this.sessionIsClosing(sessionId) || this.pendingMcpStartupSessions.get(sessionId) !== pendingStartup) {
|
|
20948
|
+
return;
|
|
20949
|
+
}
|
|
20583
20950
|
await this.publishMcpStartupStatus(sessionId, mcpStartup, pendingStartup.requestedServers);
|
|
20584
20951
|
} catch (err) {
|
|
20585
20952
|
logger.error(`Failed to publish MCP startup status for session ${sessionId}`, err);
|
|
20586
20953
|
} finally {
|
|
20587
|
-
this.pendingMcpStartupSessions.
|
|
20954
|
+
if (this.pendingMcpStartupSessions.get(sessionId) === pendingStartup) {
|
|
20955
|
+
this.pendingMcpStartupSessions.delete(sessionId);
|
|
20956
|
+
}
|
|
20588
20957
|
}
|
|
20589
20958
|
}
|
|
20590
20959
|
async publishMcpStartupStatus(sessionId, mcpStartup, requestedServers) {
|
|
@@ -20600,6 +20969,125 @@ ${item.text}`
|
|
|
20600
20969
|
});
|
|
20601
20970
|
}
|
|
20602
20971
|
}
|
|
20972
|
+
trackActivePrompt(sessionId) {
|
|
20973
|
+
let resolveCompletion = () => {
|
|
20974
|
+
};
|
|
20975
|
+
const completion = new Promise((resolve) => {
|
|
20976
|
+
resolveCompletion = resolve;
|
|
20977
|
+
});
|
|
20978
|
+
let resolveCloseSignal = () => {
|
|
20979
|
+
};
|
|
20980
|
+
const closeSignal = new Promise((resolve) => {
|
|
20981
|
+
resolveCloseSignal = resolve;
|
|
20982
|
+
});
|
|
20983
|
+
let completed = false;
|
|
20984
|
+
let closeRequested = false;
|
|
20985
|
+
const activePrompt = {
|
|
20986
|
+
completion,
|
|
20987
|
+
closeSignal,
|
|
20988
|
+
requestClose: () => {
|
|
20989
|
+
if (closeRequested) {
|
|
20990
|
+
return;
|
|
20991
|
+
}
|
|
20992
|
+
closeRequested = true;
|
|
20993
|
+
resolveCloseSignal(null);
|
|
20994
|
+
},
|
|
20995
|
+
complete: () => {
|
|
20996
|
+
if (completed) {
|
|
20997
|
+
return;
|
|
20998
|
+
}
|
|
20999
|
+
completed = true;
|
|
21000
|
+
if (this.activePrompts.get(sessionId) === activePrompt) {
|
|
21001
|
+
this.activePrompts.delete(sessionId);
|
|
21002
|
+
}
|
|
21003
|
+
resolveCompletion();
|
|
21004
|
+
}
|
|
21005
|
+
};
|
|
21006
|
+
this.activePrompts.set(sessionId, activePrompt);
|
|
21007
|
+
return activePrompt;
|
|
21008
|
+
}
|
|
21009
|
+
createPendingTurnStart() {
|
|
21010
|
+
let resolve = () => {
|
|
21011
|
+
};
|
|
21012
|
+
const promise2 = new Promise((innerResolve) => {
|
|
21013
|
+
resolve = innerResolve;
|
|
21014
|
+
});
|
|
21015
|
+
return { promise: promise2, resolve };
|
|
21016
|
+
}
|
|
21017
|
+
interruptLateStartedTurn(sessionId, turnId) {
|
|
21018
|
+
this.codexAcpClient.markTurnStale({
|
|
21019
|
+
threadId: sessionId,
|
|
21020
|
+
turnId
|
|
21021
|
+
});
|
|
21022
|
+
void this.runWithProcessCheck(() => this.codexAcpClient.turnInterrupt({
|
|
21023
|
+
threadId: sessionId,
|
|
21024
|
+
turnId
|
|
21025
|
+
})).catch((err) => {
|
|
21026
|
+
logger.error(`Close - late turnInterrupt failed`, err);
|
|
21027
|
+
}).finally(() => {
|
|
21028
|
+
this.codexAcpClient.resolveTurnInterrupted({
|
|
21029
|
+
threadId: sessionId,
|
|
21030
|
+
turnId
|
|
21031
|
+
});
|
|
21032
|
+
});
|
|
21033
|
+
}
|
|
21034
|
+
promptIsClosedOrStale(sessionId, activePrompt) {
|
|
21035
|
+
return this.activePrompts.get(sessionId) !== activePrompt || this.sessionIsClosing(sessionId);
|
|
21036
|
+
}
|
|
21037
|
+
async interruptSessionTurn(sessionState, requestName, resolveInterruptedTurn) {
|
|
21038
|
+
const turnId = await this.getInterruptibleTurnId(sessionState, requestName);
|
|
21039
|
+
if (!turnId) {
|
|
21040
|
+
return;
|
|
21041
|
+
}
|
|
21042
|
+
logger.log(`${requestName} session requested`, {
|
|
21043
|
+
sessionId: sessionState.sessionId,
|
|
21044
|
+
currentTurnId: turnId
|
|
21045
|
+
});
|
|
21046
|
+
if (resolveInterruptedTurn) {
|
|
21047
|
+
this.codexAcpClient.markTurnStale({
|
|
21048
|
+
threadId: sessionState.sessionId,
|
|
21049
|
+
turnId
|
|
21050
|
+
});
|
|
21051
|
+
}
|
|
21052
|
+
try {
|
|
21053
|
+
await this.runWithProcessCheck(() => this.codexAcpClient.turnInterrupt({
|
|
21054
|
+
threadId: sessionState.sessionId,
|
|
21055
|
+
turnId
|
|
21056
|
+
}));
|
|
21057
|
+
logger.log(`${requestName} - turnInterrupt succeeded`, {
|
|
21058
|
+
sessionId: sessionState.sessionId,
|
|
21059
|
+
currentTurnId: turnId
|
|
21060
|
+
});
|
|
21061
|
+
} catch (err) {
|
|
21062
|
+
logger.error(`${requestName} - turnInterrupt failed`, err);
|
|
21063
|
+
} finally {
|
|
21064
|
+
if (resolveInterruptedTurn) {
|
|
21065
|
+
this.codexAcpClient.resolveTurnInterrupted({
|
|
21066
|
+
threadId: sessionState.sessionId,
|
|
21067
|
+
turnId
|
|
21068
|
+
});
|
|
21069
|
+
}
|
|
21070
|
+
}
|
|
21071
|
+
}
|
|
21072
|
+
async getInterruptibleTurnId(sessionState, requestName) {
|
|
21073
|
+
if (sessionState.currentTurnId) {
|
|
21074
|
+
return sessionState.currentTurnId;
|
|
21075
|
+
}
|
|
21076
|
+
const pendingTurnStart = this.pendingTurnStarts.get(sessionState.sessionId);
|
|
21077
|
+
if (!pendingTurnStart) {
|
|
21078
|
+
logger.log(`${requestName} request rejected: no current turn`, { sessionId: sessionState.sessionId });
|
|
21079
|
+
return null;
|
|
21080
|
+
}
|
|
21081
|
+
if (requestName === "Close") {
|
|
21082
|
+
pendingTurnStart.resolve(null);
|
|
21083
|
+
return null;
|
|
21084
|
+
}
|
|
21085
|
+
const turnId = await pendingTurnStart.promise;
|
|
21086
|
+
if (!turnId) {
|
|
21087
|
+
logger.log(`${requestName} request rejected: no current turn`, { sessionId: sessionState.sessionId });
|
|
21088
|
+
}
|
|
21089
|
+
return turnId;
|
|
21090
|
+
}
|
|
20603
21091
|
async prompt(params) {
|
|
20604
21092
|
logger.log("Prompt received", {
|
|
20605
21093
|
sessionId: params.sessionId,
|
|
@@ -20608,6 +21096,8 @@ ${item.text}`
|
|
|
20608
21096
|
const sessionState = this.getSessionState(params.sessionId);
|
|
20609
21097
|
sessionState.currentTurnId = null;
|
|
20610
21098
|
sessionState.lastTokenUsage = null;
|
|
21099
|
+
const activePrompt = this.trackActivePrompt(params.sessionId);
|
|
21100
|
+
let pendingTurnStart = null;
|
|
20611
21101
|
try {
|
|
20612
21102
|
const eventHandler = new CodexEventHandler(this.connection, sessionState);
|
|
20613
21103
|
const approvalHandler = new CodexApprovalHandler(this.connection, sessionState);
|
|
@@ -20629,6 +21119,13 @@ ${item.text}`
|
|
|
20629
21119
|
_meta: this.buildQuotaMeta(sessionState)
|
|
20630
21120
|
};
|
|
20631
21121
|
}
|
|
21122
|
+
if (this.sessionIsClosing(params.sessionId)) {
|
|
21123
|
+
return {
|
|
21124
|
+
stopReason: "cancelled",
|
|
21125
|
+
usage: this.buildPromptUsage(sessionState.lastTokenUsage),
|
|
21126
|
+
_meta: this.buildQuotaMeta(sessionState)
|
|
21127
|
+
};
|
|
21128
|
+
}
|
|
20632
21129
|
const modelId = ModelId.fromString(sessionState.currentModelId);
|
|
20633
21130
|
const modelLacksReasoning = sessionState.supportedReasoningEfforts.length > 0 && sessionState.supportedReasoningEfforts.every((e) => e.reasoningEffort === "none");
|
|
20634
21131
|
const disableSummary = sessionState.account?.type === "apiKey" || modelLacksReasoning;
|
|
@@ -20646,20 +21143,57 @@ ${item.text}`
|
|
|
20646
21143
|
sessionState.fastModeEnabled,
|
|
20647
21144
|
sessionState.currentModelSupportsFast
|
|
20648
21145
|
);
|
|
20649
|
-
|
|
20650
|
-
|
|
21146
|
+
pendingTurnStart = this.createPendingTurnStart();
|
|
21147
|
+
this.pendingTurnStarts.set(params.sessionId, pendingTurnStart);
|
|
21148
|
+
const sendPromptPromise = this.runWithProcessCheck(
|
|
21149
|
+
() => this.codexAcpClient.sendPrompt(
|
|
21150
|
+
params,
|
|
21151
|
+
agentMode,
|
|
21152
|
+
modelId,
|
|
21153
|
+
serviceTier,
|
|
21154
|
+
disableSummary,
|
|
21155
|
+
sessionState.cwd,
|
|
21156
|
+
(turnId) => {
|
|
21157
|
+
if (this.promptIsClosedOrStale(params.sessionId, activePrompt)) {
|
|
21158
|
+
this.interruptLateStartedTurn(params.sessionId, turnId);
|
|
21159
|
+
return;
|
|
21160
|
+
}
|
|
21161
|
+
sessionState.currentTurnId = turnId;
|
|
21162
|
+
pendingTurnStart?.resolve(turnId);
|
|
21163
|
+
},
|
|
21164
|
+
() => this.promptIsClosedOrStale(params.sessionId, activePrompt)
|
|
21165
|
+
)
|
|
20651
21166
|
);
|
|
21167
|
+
void sendPromptPromise.catch((err) => {
|
|
21168
|
+
if (this.activePrompts.get(params.sessionId) !== activePrompt) {
|
|
21169
|
+
logger.error(`Prompt for closed session ${params.sessionId} failed after close`, err);
|
|
21170
|
+
}
|
|
21171
|
+
});
|
|
21172
|
+
const turnCompleted = await Promise.race([
|
|
21173
|
+
sendPromptPromise,
|
|
21174
|
+
activePrompt.closeSignal
|
|
21175
|
+
]);
|
|
21176
|
+
if (turnCompleted === null) {
|
|
21177
|
+
return {
|
|
21178
|
+
stopReason: "cancelled",
|
|
21179
|
+
usage: this.buildPromptUsage(sessionState.lastTokenUsage),
|
|
21180
|
+
_meta: this.buildQuotaMeta(sessionState)
|
|
21181
|
+
};
|
|
21182
|
+
}
|
|
21183
|
+
await this.codexAcpClient.waitForSessionNotifications(params.sessionId);
|
|
20652
21184
|
if (turnCompleted.turn.status === "interrupted") {
|
|
20653
|
-
|
|
20654
|
-
|
|
20655
|
-
|
|
20656
|
-
|
|
20657
|
-
|
|
20658
|
-
|
|
20659
|
-
|
|
21185
|
+
if (!this.sessionIsClosing(params.sessionId) && this.sessions.has(params.sessionId)) {
|
|
21186
|
+
await this.connection.sessionUpdate({
|
|
21187
|
+
sessionId: params.sessionId,
|
|
21188
|
+
update: {
|
|
21189
|
+
sessionUpdate: "agent_message_chunk",
|
|
21190
|
+
content: {
|
|
21191
|
+
type: "text",
|
|
21192
|
+
text: "*Conversation interrupted*"
|
|
21193
|
+
}
|
|
20660
21194
|
}
|
|
20661
|
-
}
|
|
20662
|
-
}
|
|
21195
|
+
});
|
|
21196
|
+
}
|
|
20663
21197
|
return {
|
|
20664
21198
|
stopReason: "cancelled",
|
|
20665
21199
|
usage: this.buildPromptUsage(sessionState.lastTokenUsage),
|
|
@@ -20681,6 +21215,11 @@ ${item.text}`
|
|
|
20681
21215
|
} finally {
|
|
20682
21216
|
logger.log("Prompt completed", { sessionId: params.sessionId });
|
|
20683
21217
|
sessionState.currentTurnId = null;
|
|
21218
|
+
if (pendingTurnStart !== null && this.pendingTurnStarts.get(params.sessionId) === pendingTurnStart) {
|
|
21219
|
+
this.pendingTurnStarts.delete(params.sessionId);
|
|
21220
|
+
}
|
|
21221
|
+
pendingTurnStart?.resolve(null);
|
|
21222
|
+
activePrompt.complete();
|
|
20684
21223
|
}
|
|
20685
21224
|
}
|
|
20686
21225
|
buildQuotaMeta(sessionState) {
|
|
@@ -20721,30 +21260,11 @@ ${item.text}`
|
|
|
20721
21260
|
logger.log("Cancel request rejected: session not found", { sessionId: params.sessionId });
|
|
20722
21261
|
return;
|
|
20723
21262
|
}
|
|
20724
|
-
|
|
20725
|
-
logger.log("Cancel request rejected: no current turn", { sessionId: params.sessionId });
|
|
20726
|
-
return;
|
|
20727
|
-
}
|
|
20728
|
-
logger.log("Cancel session requested", {
|
|
20729
|
-
sessionId: params.sessionId,
|
|
20730
|
-
currentTurnId: sessionState.currentTurnId
|
|
20731
|
-
});
|
|
20732
|
-
try {
|
|
20733
|
-
await this.codexAcpClient.turnInterrupt({
|
|
20734
|
-
threadId: params.sessionId,
|
|
20735
|
-
turnId: sessionState.currentTurnId
|
|
20736
|
-
});
|
|
20737
|
-
logger.log("Cancel - turnInterrupt succeeded", {
|
|
20738
|
-
sessionId: params.sessionId,
|
|
20739
|
-
currentTurnId: sessionState.currentTurnId
|
|
20740
|
-
});
|
|
20741
|
-
} catch (err) {
|
|
20742
|
-
logger.error(`Cancel - turnInterrupt failed`, err);
|
|
20743
|
-
}
|
|
21263
|
+
await this.interruptSessionTurn(sessionState, "Cancel", false);
|
|
20744
21264
|
}
|
|
20745
21265
|
};
|
|
20746
21266
|
function getRequestedMcpServerNames(mcpServers) {
|
|
20747
|
-
return Array.from(new Set(mcpServers.map((server) => server.name)));
|
|
21267
|
+
return Array.from(new Set(mcpServers.map((server) => sanitizeMcpServerName(server.name))));
|
|
20748
21268
|
}
|
|
20749
21269
|
|
|
20750
21270
|
// src/CodexAppServerClient.ts
|
|
@@ -20761,6 +21281,7 @@ var CodexAppServerClient = class {
|
|
|
20761
21281
|
mcpServerStartupResolvers = [];
|
|
20762
21282
|
pendingTurnCompletionResolvers = /* @__PURE__ */ new Map();
|
|
20763
21283
|
turnCompletionCaptures = /* @__PURE__ */ new Map();
|
|
21284
|
+
staleTurnIds = /* @__PURE__ */ new Map();
|
|
20764
21285
|
constructor(connection) {
|
|
20765
21286
|
this.connection = connection;
|
|
20766
21287
|
this.connection.onUnhandledNotification((data) => {
|
|
@@ -20777,12 +21298,26 @@ var CodexAppServerClient = class {
|
|
|
20777
21298
|
if (isTurnCompletedNotification(serverNotification)) {
|
|
20778
21299
|
this.recordTurnCompleted(serverNotification.params);
|
|
20779
21300
|
}
|
|
21301
|
+
const routing = extractTurnRouting(serverNotification);
|
|
21302
|
+
const staleTurnNotification = this.isStaleTurn(routing.threadId, routing.turnId);
|
|
21303
|
+
if (staleTurnNotification) {
|
|
21304
|
+
if (isTurnCompletedNotification(serverNotification) && routing.threadId !== null && routing.turnId !== null) {
|
|
21305
|
+
this.clearStaleTurn(routing.threadId, routing.turnId);
|
|
21306
|
+
}
|
|
21307
|
+
for (const callback of this.codexEventHandlers) {
|
|
21308
|
+
callback({ eventType: "notification", ...serverNotification });
|
|
21309
|
+
}
|
|
21310
|
+
return;
|
|
21311
|
+
}
|
|
20780
21312
|
this.notify(serverNotification);
|
|
20781
21313
|
for (const callback of this.codexEventHandlers) {
|
|
20782
21314
|
callback({ eventType: "notification", ...serverNotification });
|
|
20783
21315
|
}
|
|
20784
21316
|
});
|
|
20785
21317
|
this.connection.onRequest(CommandExecutionApprovalRequest, async (params) => {
|
|
21318
|
+
if (this.isStaleTurn(params.threadId, params.turnId)) {
|
|
21319
|
+
return { decision: "cancel" };
|
|
21320
|
+
}
|
|
20786
21321
|
const handler = this.approvalHandlers.get(params.threadId);
|
|
20787
21322
|
if (!handler) {
|
|
20788
21323
|
return { decision: "cancel" };
|
|
@@ -20790,6 +21325,9 @@ var CodexAppServerClient = class {
|
|
|
20790
21325
|
return await handler.handleCommandExecution(params);
|
|
20791
21326
|
});
|
|
20792
21327
|
this.connection.onRequest(FileChangeApprovalRequest, async (params) => {
|
|
21328
|
+
if (this.isStaleTurn(params.threadId, params.turnId)) {
|
|
21329
|
+
return { decision: "cancel" };
|
|
21330
|
+
}
|
|
20793
21331
|
const handler = this.approvalHandlers.get(params.threadId);
|
|
20794
21332
|
if (!handler) {
|
|
20795
21333
|
return { decision: "cancel" };
|
|
@@ -20797,6 +21335,9 @@ var CodexAppServerClient = class {
|
|
|
20797
21335
|
return await handler.handleFileChange(params);
|
|
20798
21336
|
});
|
|
20799
21337
|
this.connection.onRequest(McpServerElicitationRequest, async (params) => {
|
|
21338
|
+
if (this.isStaleTurn(params.threadId, params.turnId)) {
|
|
21339
|
+
return { action: "cancel", content: null, _meta: null };
|
|
21340
|
+
}
|
|
20800
21341
|
const handler = this.elicitationHandlers.get(params.threadId);
|
|
20801
21342
|
if (!handler) {
|
|
20802
21343
|
return { action: "cancel", content: null, _meta: null };
|
|
@@ -20810,19 +21351,25 @@ var CodexAppServerClient = class {
|
|
|
20810
21351
|
onElicitationRequest(threadId, handler) {
|
|
20811
21352
|
this.elicitationHandlers.set(threadId, handler);
|
|
20812
21353
|
}
|
|
21354
|
+
clearThreadHandlers(threadId) {
|
|
21355
|
+
this.notificationHandlers.delete(threadId);
|
|
21356
|
+
this.approvalHandlers.delete(threadId);
|
|
21357
|
+
this.elicitationHandlers.delete(threadId);
|
|
21358
|
+
}
|
|
20813
21359
|
async initialize(params) {
|
|
20814
21360
|
return await this.sendRequest({ method: "initialize", params });
|
|
20815
21361
|
}
|
|
20816
21362
|
async turnStart(params) {
|
|
20817
21363
|
return await this.sendRequest({ method: "turn/start", params });
|
|
20818
21364
|
}
|
|
20819
|
-
async runTurn(params) {
|
|
21365
|
+
async runTurn(params, onTurnStarted) {
|
|
20820
21366
|
const capturedCompletions = [];
|
|
20821
21367
|
const releaseCapture = this.captureTurnCompletions(params.threadId, (event) => {
|
|
20822
21368
|
capturedCompletions.push(event);
|
|
20823
21369
|
});
|
|
20824
21370
|
try {
|
|
20825
21371
|
const turnStarted = await this.turnStart(params);
|
|
21372
|
+
onTurnStarted?.(turnStarted.turn.id);
|
|
20826
21373
|
const earlyCompletion = capturedCompletions.find((event) => event.turn.id === turnStarted.turn.id);
|
|
20827
21374
|
releaseCapture();
|
|
20828
21375
|
if (earlyCompletion) {
|
|
@@ -20836,6 +21383,11 @@ var CodexAppServerClient = class {
|
|
|
20836
21383
|
async turnInterrupt(params) {
|
|
20837
21384
|
return await this.sendRequest({ method: "turn/interrupt", params });
|
|
20838
21385
|
}
|
|
21386
|
+
markTurnStale(threadId, turnId) {
|
|
21387
|
+
const threadStaleTurns = this.staleTurnIds.get(threadId) ?? /* @__PURE__ */ new Set();
|
|
21388
|
+
threadStaleTurns.add(turnId);
|
|
21389
|
+
this.staleTurnIds.set(threadId, threadStaleTurns);
|
|
21390
|
+
}
|
|
20839
21391
|
async threadStart(params) {
|
|
20840
21392
|
return await this.sendRequest({ method: "thread/start", params });
|
|
20841
21393
|
}
|
|
@@ -20851,6 +21403,9 @@ var CodexAppServerClient = class {
|
|
|
20851
21403
|
async threadRead(params) {
|
|
20852
21404
|
return await this.sendRequest({ method: "thread/read", params });
|
|
20853
21405
|
}
|
|
21406
|
+
async threadUnsubscribe(params) {
|
|
21407
|
+
return await this.sendRequest({ method: "thread/unsubscribe", params });
|
|
21408
|
+
}
|
|
20854
21409
|
async listMcpServerStatus(params) {
|
|
20855
21410
|
return await this.sendRequest({ method: "mcpServerStatus/list", params });
|
|
20856
21411
|
}
|
|
@@ -20893,9 +21448,27 @@ var CodexAppServerClient = class {
|
|
|
20893
21448
|
threadResolvers.set(turnId, resolve);
|
|
20894
21449
|
});
|
|
20895
21450
|
}
|
|
21451
|
+
resolveTurnInterrupted(threadId, turnId) {
|
|
21452
|
+
this.recordTurnCompleted({
|
|
21453
|
+
threadId,
|
|
21454
|
+
turn: {
|
|
21455
|
+
id: turnId,
|
|
21456
|
+
items: [],
|
|
21457
|
+
itemsView: "notLoaded",
|
|
21458
|
+
status: "interrupted",
|
|
21459
|
+
error: null,
|
|
21460
|
+
startedAt: null,
|
|
21461
|
+
completedAt: null,
|
|
21462
|
+
durationMs: null
|
|
21463
|
+
}
|
|
21464
|
+
});
|
|
21465
|
+
}
|
|
20896
21466
|
async listModels(params) {
|
|
20897
21467
|
return await this.sendRequest({ method: "model/list", params });
|
|
20898
21468
|
}
|
|
21469
|
+
async skillsExtraRootsSet(params) {
|
|
21470
|
+
return await this.sendRequest({ method: "skills/extraRoots/set", params });
|
|
21471
|
+
}
|
|
20899
21472
|
async listSkills(params) {
|
|
20900
21473
|
return await this.sendRequest({ method: "skills/list", params });
|
|
20901
21474
|
}
|
|
@@ -20943,6 +21516,22 @@ var CodexAppServerClient = class {
|
|
|
20943
21516
|
capture(event);
|
|
20944
21517
|
}
|
|
20945
21518
|
}
|
|
21519
|
+
isStaleTurn(threadId, turnId) {
|
|
21520
|
+
if (threadId === null || turnId === null) {
|
|
21521
|
+
return false;
|
|
21522
|
+
}
|
|
21523
|
+
return this.staleTurnIds.get(threadId)?.has(turnId) ?? false;
|
|
21524
|
+
}
|
|
21525
|
+
clearStaleTurn(threadId, turnId) {
|
|
21526
|
+
const threadStaleTurns = this.staleTurnIds.get(threadId);
|
|
21527
|
+
if (!threadStaleTurns) {
|
|
21528
|
+
return;
|
|
21529
|
+
}
|
|
21530
|
+
threadStaleTurns.delete(turnId);
|
|
21531
|
+
if (threadStaleTurns.size === 0) {
|
|
21532
|
+
this.staleTurnIds.delete(threadId);
|
|
21533
|
+
}
|
|
21534
|
+
}
|
|
20946
21535
|
getOrCreatePendingTurnCompletionResolvers(threadId) {
|
|
20947
21536
|
const existing = this.pendingTurnCompletionResolvers.get(threadId);
|
|
20948
21537
|
if (existing) {
|
|
@@ -21037,6 +21626,17 @@ function extractThreadId(notification) {
|
|
|
21037
21626
|
}
|
|
21038
21627
|
return null;
|
|
21039
21628
|
}
|
|
21629
|
+
function extractTurnRouting(notification) {
|
|
21630
|
+
const params = notification.params;
|
|
21631
|
+
const threadId = extractThreadId(notification);
|
|
21632
|
+
if (params && typeof params.turnId === "string") {
|
|
21633
|
+
return { threadId, turnId: params.turnId };
|
|
21634
|
+
}
|
|
21635
|
+
if (params && typeof params.turn?.id === "string") {
|
|
21636
|
+
return { threadId, turnId: params.turn.id };
|
|
21637
|
+
}
|
|
21638
|
+
return { threadId, turnId: null };
|
|
21639
|
+
}
|
|
21040
21640
|
|
|
21041
21641
|
// src/login.ts
|
|
21042
21642
|
function parseArgs(args) {
|