@agentclientprotocol/codex-acp 1.9.0 → 1.10.1-preview.1
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/README.md +17 -0
- package/dist/index.js +574 -64
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -13,6 +13,7 @@ Use [OpenAI Codex](https://github.com/openai/codex) from [Agent Client Protocol]
|
|
|
13
13
|
- Text prompts, embedded context, images, resource links, and additional workspace directories.
|
|
14
14
|
- Shell command, file change, [permission request](docs/permission-extension.md), MCP tool call, terminal output, reasoning, plan, web search, image generation, image view, token usage, and review events.
|
|
15
15
|
- [Native ACP subagent sessions](docs/subagent-sessions.md) (after capability negotiation) with separate child histories and root-routed permissions; a legacy tool-call fallback otherwise.
|
|
16
|
+
- [Background terminal tasks](docs/async-tasks.md) in AIR, with task status and targeted stop support after capability negotiation.
|
|
16
17
|
- Session-scoped long-running goals through the provider-neutral [goal extension](docs/goal-extension.md).
|
|
17
18
|
- A per-turn [agent file-change report](docs/agent-file-change-report.md) after capability negotiation.
|
|
18
19
|
- Client-provided MCP servers over command-based stdio config and HTTP transport.
|
|
@@ -39,6 +40,16 @@ The npm package includes a compatible `@openai/codex` dependency. Set `CODEX_PAT
|
|
|
39
40
|
CODEX_PATH=/path/to/codex npx -y @agentclientprotocol/codex-acp
|
|
40
41
|
```
|
|
41
42
|
|
|
43
|
+
To try changes that have landed on `main` but are not released yet, install from the
|
|
44
|
+
`preview` channel. Pushes to `main` trigger preview publishing without waiting
|
|
45
|
+
for CI or release-please; release commits are excluded, and newer pushes can
|
|
46
|
+
replace queued previews. See
|
|
47
|
+
[docs/RELEASES.md](docs/RELEASES.md#preview-releases).
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npx -y @agentclientprotocol/codex-acp@preview
|
|
51
|
+
```
|
|
52
|
+
|
|
42
53
|
## Authentication
|
|
43
54
|
|
|
44
55
|
The adapter advertises ACP auth methods during initialization. Clients can authenticate with:
|
|
@@ -82,6 +93,12 @@ Subagent sessions follow the draft [ACP subagent RFD](https://github.com/agentcl
|
|
|
82
93
|
|
|
83
94
|
See [docs/subagent-sessions.md](docs/subagent-sessions.md) for the negotiation, lifecycle events, `session/load` reconstruction, and legacy fallback details.
|
|
84
95
|
|
|
96
|
+
### Background terminal tasks
|
|
97
|
+
|
|
98
|
+
Codex can keep a shell command running after a turn continues. AIR clients can show this work in the Async Tasks panel and stop one command.
|
|
99
|
+
|
|
100
|
+
See [docs/async-tasks.md](docs/async-tasks.md) for the capability, lifecycle events, and stop request.
|
|
101
|
+
|
|
85
102
|
## License
|
|
86
103
|
|
|
87
104
|
By contributing, you agree that your contributions will be licensed under the Apache 2.0 License.
|
package/dist/index.js
CHANGED
|
@@ -22131,32 +22131,7 @@ function attachLogs(proc) {
|
|
|
22131
22131
|
});
|
|
22132
22132
|
}
|
|
22133
22133
|
|
|
22134
|
-
// src/
|
|
22135
|
-
var JETBRAINS_META_KEY = "jetbrains";
|
|
22136
|
-
var AIR_META_KEY = "air";
|
|
22137
|
-
var AIR_EXTENSION_VERSION_KEY = "version";
|
|
22138
|
-
var AIR_EXTENSION_CAPABILITIES_KEY = "capabilities";
|
|
22139
|
-
var AIR_SESSION_FAILURE_KEY = "sessionFailure";
|
|
22140
|
-
var AIR_AGENT_FILE_CHANGE_REPORT_KEY = "agentFileChangeReport";
|
|
22141
|
-
var AIR_NATIVE_SUBAGENT_SESSIONS_KEY = "nativeSubagentSessions";
|
|
22142
|
-
var AIR_AGENT_FILE_CHANGE_REPORT_REQUEST_KEY = "agentFileChangeReportRequest";
|
|
22143
|
-
var AIR_EXTENSION_VERSION = 1;
|
|
22144
|
-
function clientSupportsAirCapability(capabilities, capability) {
|
|
22145
|
-
const jetbrains = capabilities?._meta?.[JETBRAINS_META_KEY];
|
|
22146
|
-
const air = jetbrains?.[AIR_META_KEY];
|
|
22147
|
-
const version2 = air?.[AIR_EXTENSION_VERSION_KEY];
|
|
22148
|
-
const supported = air?.[AIR_EXTENSION_CAPABILITIES_KEY];
|
|
22149
|
-
return typeof version2 === "number" && Number.isInteger(version2) && version2 >= AIR_EXTENSION_VERSION && Array.isArray(supported) && supported.includes(capability);
|
|
22150
|
-
}
|
|
22151
|
-
|
|
22152
|
-
// src/subagents/AcpSubagents.ts
|
|
22153
|
-
function clientSupportsSubagents(capabilities) {
|
|
22154
|
-
const subagents = capabilities?.subagents;
|
|
22155
|
-
if (typeof subagents === "object" && subagents !== null && !Array.isArray(subagents)) {
|
|
22156
|
-
return true;
|
|
22157
|
-
}
|
|
22158
|
-
return clientSupportsAirCapability(capabilities, AIR_NATIVE_SUBAGENT_SESSIONS_KEY);
|
|
22159
|
-
}
|
|
22134
|
+
// src/AcpSessionExtensions.ts
|
|
22160
22135
|
function asSdkSessionNotification(notification) {
|
|
22161
22136
|
return notification;
|
|
22162
22137
|
}
|
|
@@ -23676,6 +23651,26 @@ function sameThreadGoalSnapshot(left, right) {
|
|
|
23676
23651
|
// src/CodexEventHandler.ts
|
|
23677
23652
|
import { randomUUID } from "node:crypto";
|
|
23678
23653
|
|
|
23654
|
+
// src/AirExtension.ts
|
|
23655
|
+
var JETBRAINS_META_KEY = "jetbrains";
|
|
23656
|
+
var AIR_META_KEY = "air";
|
|
23657
|
+
var AIR_EXTENSION_VERSION_KEY = "version";
|
|
23658
|
+
var AIR_EXTENSION_CAPABILITIES_KEY = "capabilities";
|
|
23659
|
+
var AIR_SESSION_FAILURE_KEY = "sessionFailure";
|
|
23660
|
+
var AIR_AGENT_FILE_CHANGE_REPORT_KEY = "agentFileChangeReport";
|
|
23661
|
+
var AIR_NATIVE_SUBAGENT_SESSIONS_KEY = "nativeSubagentSessions";
|
|
23662
|
+
var AIR_ASYNC_TASKS_KEY = "asyncTasks";
|
|
23663
|
+
var AIR_ASYNC_TASKS_BACKGROUNDED_KEY = "backgrounded";
|
|
23664
|
+
var AIR_AGENT_FILE_CHANGE_REPORT_REQUEST_KEY = "agentFileChangeReportRequest";
|
|
23665
|
+
var AIR_EXTENSION_VERSION = 1;
|
|
23666
|
+
function clientSupportsAirCapability(capabilities, capability) {
|
|
23667
|
+
const jetbrains = capabilities?._meta?.[JETBRAINS_META_KEY];
|
|
23668
|
+
const air = jetbrains?.[AIR_META_KEY];
|
|
23669
|
+
const version2 = air?.[AIR_EXTENSION_VERSION_KEY];
|
|
23670
|
+
const supported = air?.[AIR_EXTENSION_CAPABILITIES_KEY];
|
|
23671
|
+
return typeof version2 === "number" && Number.isInteger(version2) && version2 >= AIR_EXTENSION_VERSION && Array.isArray(supported) && supported.includes(capability);
|
|
23672
|
+
}
|
|
23673
|
+
|
|
23679
23674
|
// src/subagents/CodexAgentPath.ts
|
|
23680
23675
|
function normalizeAgentPath(path9) {
|
|
23681
23676
|
const normalized = path9.trim().replace(/\/+$/, "");
|
|
@@ -23731,14 +23726,14 @@ var CodexSubagentEventRouter = class _CodexSubagentEventRouter {
|
|
|
23731
23726
|
}
|
|
23732
23727
|
return childTurn;
|
|
23733
23728
|
}
|
|
23734
|
-
const
|
|
23735
|
-
if (typeof
|
|
23736
|
-
const pending = this.pendingSpawns.get(
|
|
23729
|
+
const notificationThreadId2 = notification.params.threadId;
|
|
23730
|
+
if (typeof notificationThreadId2 === "string" && this.pendingSpawns.has(notificationThreadId2)) {
|
|
23731
|
+
const pending = this.pendingSpawns.get(notificationThreadId2);
|
|
23737
23732
|
if (pending.buffered.length === _CodexSubagentEventRouter.MAX_PENDING_NOTIFICATIONS) {
|
|
23738
23733
|
pending.buffered.shift();
|
|
23739
23734
|
pending.droppedBufferedNotifications += 1;
|
|
23740
23735
|
if (pending.droppedBufferedNotifications === 1) {
|
|
23741
|
-
logger.log(`Pending subagent ${
|
|
23736
|
+
logger.log(`Pending subagent ${notificationThreadId2} exceeded the notification buffer; dropping oldest updates`);
|
|
23742
23737
|
}
|
|
23743
23738
|
}
|
|
23744
23739
|
pending.buffered.push(notification);
|
|
@@ -23823,6 +23818,25 @@ var CodexSubagentEventRouter = class _CodexSubagentEventRouter {
|
|
|
23823
23818
|
const threadId = notification.params.threadId;
|
|
23824
23819
|
return typeof threadId === "string" && this.children.has(threadId) ? this.children.get(threadId).sessionId : this.rootSessionId;
|
|
23825
23820
|
}
|
|
23821
|
+
closingChildSessions(notification) {
|
|
23822
|
+
if (!this.supported) return [];
|
|
23823
|
+
if (notification.method === "turn/completed") {
|
|
23824
|
+
if (terminalStateFromTurn(notification.params.turn.status) === void 0) return [];
|
|
23825
|
+
return this.closingChildSession(notification.params.threadId);
|
|
23826
|
+
}
|
|
23827
|
+
if (notification.method !== "item/started" && notification.method !== "item/completed") return [];
|
|
23828
|
+
const item = notification.params.item;
|
|
23829
|
+
if (item.type === "subAgentActivity") {
|
|
23830
|
+
return item.kind === "interrupted" ? this.closingChildSession(item.agentThreadId) : [];
|
|
23831
|
+
}
|
|
23832
|
+
if (item.type !== "collabAgentToolCall") return [];
|
|
23833
|
+
const closing = /* @__PURE__ */ new Map();
|
|
23834
|
+
for (const [threadId, state] of Object.entries(item.agentsStates)) {
|
|
23835
|
+
if (!state || terminalStateOf(state.status) === void 0) continue;
|
|
23836
|
+
for (const child of this.closingChildSession(threadId)) closing.set(threadId, child);
|
|
23837
|
+
}
|
|
23838
|
+
return [...closing.values()];
|
|
23839
|
+
}
|
|
23826
23840
|
takeBufferedNotifications() {
|
|
23827
23841
|
return this.replayQueue.splice(0);
|
|
23828
23842
|
}
|
|
@@ -23898,6 +23912,10 @@ var CodexSubagentEventRouter = class _CodexSubagentEventRouter {
|
|
|
23898
23912
|
isKnownChild(threadId) {
|
|
23899
23913
|
return threadId !== this.rootSessionId && (this.children.has(threadId) || this.pendingSpawns.has(threadId) || this.terminalPendingSpawns.has(threadId));
|
|
23900
23914
|
}
|
|
23915
|
+
closingChildSession(threadId) {
|
|
23916
|
+
const child = this.children.get(threadId);
|
|
23917
|
+
return child && child.terminalState === void 0 ? [{ threadId, sessionId: child.sessionId }] : [];
|
|
23918
|
+
}
|
|
23901
23919
|
async materialize(childSessionId, path9) {
|
|
23902
23920
|
if (this.children.has(childSessionId)) return;
|
|
23903
23921
|
const pending = this.pendingSpawns.get(childSessionId);
|
|
@@ -24306,17 +24324,29 @@ var CodexEventHandler = class _CodexEventHandler {
|
|
|
24306
24324
|
}
|
|
24307
24325
|
async handleNotification(notification) {
|
|
24308
24326
|
await this.flushPendingErrors();
|
|
24327
|
+
const closingChildren = this.subagents.closingChildSessions(notification);
|
|
24328
|
+
for (const child of closingChildren) {
|
|
24329
|
+
await this.sessionState.asyncTasks.reconcile(child.threadId, child.sessionId);
|
|
24330
|
+
}
|
|
24309
24331
|
const handledBySubagents = await this.subagents.handle(notification);
|
|
24310
24332
|
for (const buffered of this.subagents.takeBufferedNotifications()) {
|
|
24311
24333
|
await this.handleNotification(buffered);
|
|
24312
24334
|
}
|
|
24313
|
-
|
|
24314
|
-
|
|
24335
|
+
const ignoredBySubagents = !handledBySubagents && this.subagents.shouldIgnore(notification);
|
|
24336
|
+
let updateEvent;
|
|
24337
|
+
if (!handledBySubagents && !ignoredBySubagents && notification.method === "item/started" && notification.params.item.type === "commandExecution") {
|
|
24338
|
+
updateEvent = await this.createUpdateEvent(notification);
|
|
24315
24339
|
}
|
|
24316
|
-
if (
|
|
24317
|
-
|
|
24340
|
+
if (!handledBySubagents) {
|
|
24341
|
+
await this.sessionState.asyncTasks.handleNotification(
|
|
24342
|
+
notification,
|
|
24343
|
+
this.subagents.notificationSessionId(notification),
|
|
24344
|
+
toolCallTitle(updateEvent)
|
|
24345
|
+
);
|
|
24318
24346
|
}
|
|
24319
|
-
|
|
24347
|
+
if (handledBySubagents) return;
|
|
24348
|
+
if (ignoredBySubagents) return;
|
|
24349
|
+
if (updateEvent === void 0) updateEvent = await this.createUpdateEvent(notification);
|
|
24320
24350
|
if (updateEvent) {
|
|
24321
24351
|
await this.session.update(updateEvent, this.subagents.notificationSessionId(notification));
|
|
24322
24352
|
}
|
|
@@ -25153,6 +25183,10 @@ ${event.stdin}
|
|
|
25153
25183
|
return createGuardianApprovalReviewToolCall(params);
|
|
25154
25184
|
}
|
|
25155
25185
|
};
|
|
25186
|
+
function toolCallTitle(update) {
|
|
25187
|
+
if (update?.sessionUpdate !== "tool_call") return void 0;
|
|
25188
|
+
return update.title;
|
|
25189
|
+
}
|
|
25156
25190
|
|
|
25157
25191
|
// src/permissions/option-ids.ts
|
|
25158
25192
|
var ApprovalOptionId = {
|
|
@@ -25922,8 +25956,9 @@ function buildMcpPermissionRequest(sessionId, params, context, nextStandaloneToo
|
|
|
25922
25956
|
toolCallId: nextStandaloneToolCallId(),
|
|
25923
25957
|
kind: context.isToolApproval ? "execute" : "other",
|
|
25924
25958
|
status: "pending",
|
|
25959
|
+
title: context.isToolApproval ? "MCP tool call approval" : "Question from MCP server",
|
|
25925
25960
|
content: [messageContent],
|
|
25926
|
-
rawInput: { serverName: params.serverName, schema: params.requestedSchema }
|
|
25961
|
+
rawInput: { serverName: params.serverName, description: params.message, schema: params.requestedSchema }
|
|
25927
25962
|
},
|
|
25928
25963
|
...context.isToolApproval ? { _meta: { is_mcp_tool_approval: true } } : {},
|
|
25929
25964
|
options
|
|
@@ -25941,8 +25976,9 @@ function buildMcpPermissionRequest(sessionId, params, context, nextStandaloneToo
|
|
|
25941
25976
|
toolCallId: `elicitation-${params.elicitationId}`,
|
|
25942
25977
|
kind: "fetch",
|
|
25943
25978
|
status: "pending",
|
|
25979
|
+
title: "MCP server requests to open a URL",
|
|
25944
25980
|
content: [messageContent],
|
|
25945
|
-
rawInput: { serverName: params.serverName, url: params.url }
|
|
25981
|
+
rawInput: { serverName: params.serverName, description: params.message, url: params.url }
|
|
25946
25982
|
},
|
|
25947
25983
|
options
|
|
25948
25984
|
},
|
|
@@ -26124,11 +26160,29 @@ var CodexElicitationHandler = class {
|
|
|
26124
26160
|
context.isToolApproval,
|
|
26125
26161
|
context.persistOptions
|
|
26126
26162
|
);
|
|
26127
|
-
if (correlatedCallId !== void 0
|
|
26128
|
-
|
|
26129
|
-
|
|
26130
|
-
|
|
26131
|
-
|
|
26163
|
+
if (correlatedCallId !== void 0) {
|
|
26164
|
+
if (result.action === "accept") {
|
|
26165
|
+
await this.connection.notify(methods.client.session.update, {
|
|
26166
|
+
sessionId: params.threadId,
|
|
26167
|
+
update: { sessionUpdate: "tool_call_update", toolCallId: correlatedCallId, status: "in_progress" }
|
|
26168
|
+
});
|
|
26169
|
+
}
|
|
26170
|
+
} else {
|
|
26171
|
+
try {
|
|
26172
|
+
await this.connection.notify(methods.client.session.update, {
|
|
26173
|
+
sessionId: params.threadId,
|
|
26174
|
+
update: {
|
|
26175
|
+
sessionUpdate: "tool_call_update",
|
|
26176
|
+
toolCallId: request.toolCall.toolCallId,
|
|
26177
|
+
status: "completed",
|
|
26178
|
+
title: request.toolCall.title,
|
|
26179
|
+
content: request.toolCall.content,
|
|
26180
|
+
rawOutput: { action: result.action }
|
|
26181
|
+
}
|
|
26182
|
+
});
|
|
26183
|
+
} catch (error51) {
|
|
26184
|
+
logger.error("Failed to finalize standalone MCP elicitation tool call", error51);
|
|
26185
|
+
}
|
|
26132
26186
|
}
|
|
26133
26187
|
return result;
|
|
26134
26188
|
} catch (error51) {
|
|
@@ -27285,7 +27339,7 @@ var package_default = {
|
|
|
27285
27339
|
publishConfig: {
|
|
27286
27340
|
access: "public"
|
|
27287
27341
|
},
|
|
27288
|
-
version: "1.
|
|
27342
|
+
version: "1.10.1-preview.1",
|
|
27289
27343
|
description: "",
|
|
27290
27344
|
main: "dist/index.js",
|
|
27291
27345
|
bin: {
|
|
@@ -27348,7 +27402,7 @@ var package_default = {
|
|
|
27348
27402
|
},
|
|
27349
27403
|
dependencies: {
|
|
27350
27404
|
"@agentclientprotocol/sdk": "^1.4.0",
|
|
27351
|
-
"@openai/codex": "^0.153.
|
|
27405
|
+
"@openai/codex": "^0.153.3",
|
|
27352
27406
|
diff: "^9.0.0",
|
|
27353
27407
|
open: "^11.0.1",
|
|
27354
27408
|
"vscode-jsonrpc": "^9.0.1",
|
|
@@ -27855,6 +27909,7 @@ async function forkSession(request, additionalDirectories, dependencies) {
|
|
|
27855
27909
|
await dependencies.refreshSkills(request.cwd, additionalDirectories);
|
|
27856
27910
|
const lastTurnId = await resolveForkTurnId(request, dependencies.codexClient);
|
|
27857
27911
|
const response = await dependencies.codexClient.threadFork({
|
|
27912
|
+
excludeTurns: true,
|
|
27858
27913
|
config: await dependencies.createSessionConfig(
|
|
27859
27914
|
request.cwd,
|
|
27860
27915
|
additionalDirectories,
|
|
@@ -27880,10 +27935,7 @@ async function forkSession(request, additionalDirectories, dependencies) {
|
|
|
27880
27935
|
async function resolveForkTurnId(request, codexClient) {
|
|
27881
27936
|
const forkPoint = readAirForkPoint(request._meta);
|
|
27882
27937
|
if (!forkPoint) return void 0;
|
|
27883
|
-
const history = await codexClient.
|
|
27884
|
-
threadId: request.sessionId,
|
|
27885
|
-
includeTurns: true
|
|
27886
|
-
});
|
|
27938
|
+
const history = await codexClient.threadReadWithHistory(request.sessionId);
|
|
27887
27939
|
const candidateIds = airForkMessageIdCandidates(forkPoint.messageId);
|
|
27888
27940
|
const itemTurnId = candidateIds.map((candidateId) => history.thread.turns.find((turn) => turn.items.some((item) => item.id === candidateId))?.id).find((turnId) => turnId !== void 0);
|
|
27889
27941
|
if (itemTurnId) return itemTurnId;
|
|
@@ -28306,6 +28358,7 @@ var CodexAcpClient = class {
|
|
|
28306
28358
|
const additionalDirectories = readAdditionalDirectories(request.cwd, request.additionalDirectories, request._meta);
|
|
28307
28359
|
await this.refreshSkills(request.cwd, additionalDirectories);
|
|
28308
28360
|
const response = await this.codexClient.threadResume({
|
|
28361
|
+
excludeTurns: true,
|
|
28309
28362
|
config: await this.createSessionConfig(request.cwd, additionalDirectories, request.mcpServers ?? []),
|
|
28310
28363
|
cwd: request.cwd,
|
|
28311
28364
|
modelProvider: await this.getResumeModelProvider(),
|
|
@@ -28340,16 +28393,17 @@ var CodexAcpClient = class {
|
|
|
28340
28393
|
const additionalDirectories = readAdditionalDirectories(request.cwd, request.additionalDirectories, request._meta);
|
|
28341
28394
|
await this.refreshSkills(request.cwd, additionalDirectories);
|
|
28342
28395
|
const response = await this.codexClient.threadResume({
|
|
28396
|
+
excludeTurns: true,
|
|
28343
28397
|
config: await this.createSessionConfig(request.cwd, additionalDirectories, request.mcpServers ?? []),
|
|
28344
28398
|
cwd: request.cwd,
|
|
28345
28399
|
modelProvider: await this.getResumeModelProvider(),
|
|
28346
28400
|
threadId: request.sessionId
|
|
28347
28401
|
});
|
|
28348
28402
|
onSubscribed?.();
|
|
28349
|
-
const
|
|
28350
|
-
|
|
28351
|
-
|
|
28352
|
-
});
|
|
28403
|
+
const thread = response.thread.historyMode === "paginated" ? {
|
|
28404
|
+
...response.thread,
|
|
28405
|
+
turns: response.turnsBackwardsCursor === null ? [] : await this.codexClient.threadReadHistory(response.thread.id, response.turnsBackwardsCursor)
|
|
28406
|
+
} : (await this.codexClient.threadReadWithHistory(response.thread.id)).thread;
|
|
28353
28407
|
const codexModels = await this.fetchAvailableModels();
|
|
28354
28408
|
const currentModelId = this.createModelId(codexModels, response.model, response.reasoningEffort).toString();
|
|
28355
28409
|
return {
|
|
@@ -28359,15 +28413,12 @@ var CodexAcpClient = class {
|
|
|
28359
28413
|
collaborationMode: this.getCollaborationMode(response.thread.id),
|
|
28360
28414
|
modelProvider: response.modelProvider,
|
|
28361
28415
|
currentServiceTier: response.serviceTier ?? null,
|
|
28362
|
-
thread
|
|
28416
|
+
thread,
|
|
28363
28417
|
additionalDirectories
|
|
28364
28418
|
};
|
|
28365
28419
|
}
|
|
28366
28420
|
async readSessionThread(sessionId) {
|
|
28367
|
-
return (await this.codexClient.
|
|
28368
|
-
threadId: sessionId,
|
|
28369
|
-
includeTurns: true
|
|
28370
|
-
})).thread;
|
|
28421
|
+
return (await this.codexClient.threadReadWithHistory(sessionId)).thread;
|
|
28371
28422
|
}
|
|
28372
28423
|
async newSession(request) {
|
|
28373
28424
|
const additionalDirectories = readAdditionalDirectories(request.cwd, request.additionalDirectories, request._meta);
|
|
@@ -28648,6 +28699,7 @@ var CodexAcpClient = class {
|
|
|
28648
28699
|
let lateStopReason = null;
|
|
28649
28700
|
try {
|
|
28650
28701
|
const forkPromise = this.codexClient.threadFork({
|
|
28702
|
+
excludeTurns: true,
|
|
28651
28703
|
threadId: params.sessionId,
|
|
28652
28704
|
lastTurnId: params.turnId,
|
|
28653
28705
|
cwd: params.workspace.cwd,
|
|
@@ -29482,6 +29534,41 @@ var CodexAppServerClient = class {
|
|
|
29482
29534
|
async threadRead(params) {
|
|
29483
29535
|
return await this.sendRequest({ method: "thread/read", params });
|
|
29484
29536
|
}
|
|
29537
|
+
async threadTurnsList(params) {
|
|
29538
|
+
return await this.sendRequest({ method: "thread/turns/list", params });
|
|
29539
|
+
}
|
|
29540
|
+
async threadReadWithHistory(threadId) {
|
|
29541
|
+
const response = await this.threadRead({ threadId });
|
|
29542
|
+
if (response.thread.historyMode === "legacy") {
|
|
29543
|
+
return await this.threadRead({ threadId, includeTurns: true });
|
|
29544
|
+
}
|
|
29545
|
+
const turns = await this.threadReadHistory(threadId);
|
|
29546
|
+
return { ...response, thread: { ...response.thread, turns } };
|
|
29547
|
+
}
|
|
29548
|
+
async threadReadHistory(threadId, initialCursor = null) {
|
|
29549
|
+
const turns = [];
|
|
29550
|
+
const seenCursors = /* @__PURE__ */ new Set();
|
|
29551
|
+
if (initialCursor !== null) seenCursors.add(initialCursor);
|
|
29552
|
+
let cursor = initialCursor;
|
|
29553
|
+
do {
|
|
29554
|
+
const page = await this.threadTurnsList({
|
|
29555
|
+
threadId,
|
|
29556
|
+
cursor,
|
|
29557
|
+
limit: 50,
|
|
29558
|
+
sortDirection: "desc",
|
|
29559
|
+
itemsView: "full"
|
|
29560
|
+
});
|
|
29561
|
+
turns.push(...page.data);
|
|
29562
|
+
cursor = page.nextCursor;
|
|
29563
|
+
if (cursor !== null) {
|
|
29564
|
+
if (seenCursors.has(cursor)) {
|
|
29565
|
+
throw new Error("Codex returned a repeated thread history cursor");
|
|
29566
|
+
}
|
|
29567
|
+
seenCursors.add(cursor);
|
|
29568
|
+
}
|
|
29569
|
+
} while (cursor !== null);
|
|
29570
|
+
return turns.reverse();
|
|
29571
|
+
}
|
|
29485
29572
|
async threadArchive(params) {
|
|
29486
29573
|
return await this.sendRequest({ method: "thread/archive", params });
|
|
29487
29574
|
}
|
|
@@ -29491,6 +29578,12 @@ var CodexAppServerClient = class {
|
|
|
29491
29578
|
async threadCompactStart(params) {
|
|
29492
29579
|
return await this.sendRequest({ method: "thread/compact/start", params });
|
|
29493
29580
|
}
|
|
29581
|
+
async threadBackgroundTerminalsList(params) {
|
|
29582
|
+
return await this.sendRequest({ method: "thread/backgroundTerminals/list", params });
|
|
29583
|
+
}
|
|
29584
|
+
async threadBackgroundTerminalsTerminate(params) {
|
|
29585
|
+
return await this.sendRequest({ method: "thread/backgroundTerminals/terminate", params });
|
|
29586
|
+
}
|
|
29494
29587
|
async threadGoalSet(params) {
|
|
29495
29588
|
return await this.sendRequest({ method: "thread/goal/set", params });
|
|
29496
29589
|
}
|
|
@@ -31462,6 +31555,9 @@ function numberValue(value) {
|
|
|
31462
31555
|
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
31463
31556
|
}
|
|
31464
31557
|
|
|
31558
|
+
// src/async-tasks/AsyncTaskExtension.ts
|
|
31559
|
+
var ASYNC_TASK_STOP_METHOD = "_session/async_task/stop";
|
|
31560
|
+
|
|
31465
31561
|
// src/AuthStatusMeta.ts
|
|
31466
31562
|
var AUTH_STATUS_UPDATE_METHOD = "_auth/status_update";
|
|
31467
31563
|
var AUTH_STATUS_META_KEY = "authStatus";
|
|
@@ -31599,7 +31695,7 @@ function sameAuthStatus(previous, next) {
|
|
|
31599
31695
|
var LEGACY_SET_SESSION_MODEL_METHOD = "session/set_model";
|
|
31600
31696
|
var SESSION_STEERING_METHOD = "_session/steering";
|
|
31601
31697
|
function isExtMethodRequest(request) {
|
|
31602
|
-
return request.method === "authentication/status" || request.method === "authentication/logout" || request.method === LEGACY_SET_SESSION_MODEL_METHOD || request.method === GOAL_CONTROL_METHOD || request.method === LEGACY_GOAL_CONTROL_METHOD || request.method === SESSION_STEERING_METHOD;
|
|
31698
|
+
return request.method === "authentication/status" || request.method === "authentication/logout" || request.method === LEGACY_SET_SESSION_MODEL_METHOD || request.method === GOAL_CONTROL_METHOD || request.method === LEGACY_GOAL_CONTROL_METHOD || request.method === SESSION_STEERING_METHOD || request.method === ASYNC_TASK_STOP_METHOD;
|
|
31603
31699
|
}
|
|
31604
31700
|
|
|
31605
31701
|
// src/FastModeConfig.ts
|
|
@@ -31666,6 +31762,15 @@ function clientSupportsPlanUpdates(clientCapabilities) {
|
|
|
31666
31762
|
return clientCapabilities?.plan != null;
|
|
31667
31763
|
}
|
|
31668
31764
|
|
|
31765
|
+
// src/subagents/AcpSubagents.ts
|
|
31766
|
+
function clientSupportsSubagents(capabilities) {
|
|
31767
|
+
const subagents = capabilities?.subagents;
|
|
31768
|
+
if (typeof subagents === "object" && subagents !== null && !Array.isArray(subagents)) {
|
|
31769
|
+
return true;
|
|
31770
|
+
}
|
|
31771
|
+
return clientSupportsAirCapability(capabilities, AIR_NATIVE_SUBAGENT_SESSIONS_KEY);
|
|
31772
|
+
}
|
|
31773
|
+
|
|
31669
31774
|
// src/CodexAcpServer.ts
|
|
31670
31775
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
31671
31776
|
|
|
@@ -31754,6 +31859,332 @@ function extractTitle(turn) {
|
|
|
31754
31859
|
|
|
31755
31860
|
// src/CodexAcpServer.ts
|
|
31756
31861
|
import { once } from "node:events";
|
|
31862
|
+
|
|
31863
|
+
// src/async-tasks/CodexBackgroundTerminalTasks.ts
|
|
31864
|
+
var CodexBackgroundTerminalTasks = class {
|
|
31865
|
+
constructor(enabled, rootSessionId, appServer, session) {
|
|
31866
|
+
this.enabled = enabled;
|
|
31867
|
+
this.rootSessionId = rootSessionId;
|
|
31868
|
+
this.appServer = appServer;
|
|
31869
|
+
this.session = session;
|
|
31870
|
+
}
|
|
31871
|
+
enabled;
|
|
31872
|
+
rootSessionId;
|
|
31873
|
+
appServer;
|
|
31874
|
+
session;
|
|
31875
|
+
tasks = /* @__PURE__ */ new Map();
|
|
31876
|
+
syncs = /* @__PURE__ */ new Map();
|
|
31877
|
+
appServerGeneration = 0;
|
|
31878
|
+
appServerQueriesEnabled = true;
|
|
31879
|
+
disposed = false;
|
|
31880
|
+
async handleNotification(notification, sessionId, commandTitle2) {
|
|
31881
|
+
if (!this.isActive()) return;
|
|
31882
|
+
const threadId = notificationThreadId(notification);
|
|
31883
|
+
if (threadId === null) return;
|
|
31884
|
+
if (notification.method === "item/started" && notification.params.item.type === "commandExecution") {
|
|
31885
|
+
this.observeCommandStarted(notification.params.item, threadId, sessionId, commandTitle2);
|
|
31886
|
+
return;
|
|
31887
|
+
}
|
|
31888
|
+
if (notification.method === "item/completed" && notification.params.item.type === "commandExecution") {
|
|
31889
|
+
await this.observeCommandCompleted(notification.params.item, threadId);
|
|
31890
|
+
return;
|
|
31891
|
+
}
|
|
31892
|
+
if (notification.method === "turn/completed") {
|
|
31893
|
+
await this.reconcile(threadId, sessionId);
|
|
31894
|
+
return;
|
|
31895
|
+
}
|
|
31896
|
+
if (notification.method === "item/started") {
|
|
31897
|
+
this.refresh(threadId, sessionId);
|
|
31898
|
+
}
|
|
31899
|
+
}
|
|
31900
|
+
observeCommandStarted(item, threadId, sessionId, commandTitle2) {
|
|
31901
|
+
if (!this.isActive() || item.processId === null) return;
|
|
31902
|
+
this.remember(threadId, sessionId, {
|
|
31903
|
+
itemId: item.id,
|
|
31904
|
+
processId: item.processId,
|
|
31905
|
+
command: item.command
|
|
31906
|
+
}, commandTitle2);
|
|
31907
|
+
}
|
|
31908
|
+
async observeCommandCompleted(item, threadId) {
|
|
31909
|
+
if (!this.isActive()) return;
|
|
31910
|
+
const task = this.tasks.get(wireTaskId(this.rootSessionId, threadId, item.id));
|
|
31911
|
+
if (task) await this.finish(task, item.status === "completed" ? "completed" : "failed");
|
|
31912
|
+
}
|
|
31913
|
+
refresh(threadId = this.rootSessionId, sessionId = this.rootSessionId) {
|
|
31914
|
+
void this.reconcile(threadId, sessionId);
|
|
31915
|
+
}
|
|
31916
|
+
async reconcile(threadId = this.rootSessionId, sessionId = this.rootSessionId) {
|
|
31917
|
+
try {
|
|
31918
|
+
await this.sync(threadId, sessionId);
|
|
31919
|
+
} catch (error51) {
|
|
31920
|
+
if (this.isActive()) logger.error(`Failed to list background terminals for ${threadId}`, error51);
|
|
31921
|
+
}
|
|
31922
|
+
}
|
|
31923
|
+
setAppServer(appServer) {
|
|
31924
|
+
this.appServer = appServer;
|
|
31925
|
+
this.appServerGeneration += 1;
|
|
31926
|
+
this.appServerQueriesEnabled = true;
|
|
31927
|
+
}
|
|
31928
|
+
prepareForAppServerReplacement() {
|
|
31929
|
+
this.appServerGeneration += 1;
|
|
31930
|
+
this.appServerQueriesEnabled = false;
|
|
31931
|
+
}
|
|
31932
|
+
async recover(threadId, sessionId, itemIds) {
|
|
31933
|
+
if (!this.canQueryAppServer() || itemIds.size === 0) return;
|
|
31934
|
+
const snapshot = await this.listAll(threadId);
|
|
31935
|
+
if (snapshot === null) return;
|
|
31936
|
+
for (const terminal of snapshot.terminals) {
|
|
31937
|
+
if (!this.queryIsCurrent(snapshot.generation)) return;
|
|
31938
|
+
if (!itemIds.has(terminal.itemId)) continue;
|
|
31939
|
+
const task = this.remember(threadId, sessionId, terminal);
|
|
31940
|
+
if (task.publication === "unpublished" && task.state === "running") await this.announce(task);
|
|
31941
|
+
}
|
|
31942
|
+
}
|
|
31943
|
+
async finishAll(state) {
|
|
31944
|
+
if (!this.isActive()) return;
|
|
31945
|
+
const errors = [];
|
|
31946
|
+
for (const task of this.tasks.values()) {
|
|
31947
|
+
try {
|
|
31948
|
+
await this.finish(task, state);
|
|
31949
|
+
} catch (error51) {
|
|
31950
|
+
errors.push(error51);
|
|
31951
|
+
}
|
|
31952
|
+
}
|
|
31953
|
+
if (errors.length > 0) {
|
|
31954
|
+
throw new AggregateError(errors, `Failed to finish ${errors.length} background terminal task(s)`);
|
|
31955
|
+
}
|
|
31956
|
+
}
|
|
31957
|
+
async sync(threadId = this.rootSessionId, sessionId = this.rootSessionId) {
|
|
31958
|
+
if (!this.canQueryAppServer()) return;
|
|
31959
|
+
const current = this.syncs.get(threadId);
|
|
31960
|
+
if (current) {
|
|
31961
|
+
current.requested = true;
|
|
31962
|
+
current.sessionId = sessionId;
|
|
31963
|
+
return await current.promise;
|
|
31964
|
+
}
|
|
31965
|
+
const pending = {
|
|
31966
|
+
requested: false,
|
|
31967
|
+
sessionId,
|
|
31968
|
+
promise: Promise.resolve()
|
|
31969
|
+
};
|
|
31970
|
+
pending.promise = this.syncUntilCurrent(threadId, pending).finally(() => {
|
|
31971
|
+
if (this.syncs.get(threadId) === pending) this.syncs.delete(threadId);
|
|
31972
|
+
});
|
|
31973
|
+
this.syncs.set(threadId, pending);
|
|
31974
|
+
await pending.promise;
|
|
31975
|
+
}
|
|
31976
|
+
async stop(taskId) {
|
|
31977
|
+
if (!this.isActive()) return false;
|
|
31978
|
+
const task = this.tasks.get(taskId);
|
|
31979
|
+
if (!task || task.publication !== "published") return false;
|
|
31980
|
+
if (task.state === "stopped" && !task.terminalPublished) {
|
|
31981
|
+
await this.publishTerminalState(task);
|
|
31982
|
+
return true;
|
|
31983
|
+
}
|
|
31984
|
+
if (task.state !== "running") return false;
|
|
31985
|
+
task.state = "stopping";
|
|
31986
|
+
try {
|
|
31987
|
+
const response = await this.appServer.threadBackgroundTerminalsTerminate({
|
|
31988
|
+
threadId: task.threadId,
|
|
31989
|
+
processId: task.processId
|
|
31990
|
+
});
|
|
31991
|
+
if (!response.terminated) {
|
|
31992
|
+
if (task.state === "stopping") task.state = "running";
|
|
31993
|
+
return false;
|
|
31994
|
+
}
|
|
31995
|
+
await this.finish(task, "stopped");
|
|
31996
|
+
return true;
|
|
31997
|
+
} catch (error51) {
|
|
31998
|
+
if (task.state === "stopping") task.state = "running";
|
|
31999
|
+
throw error51;
|
|
32000
|
+
}
|
|
32001
|
+
}
|
|
32002
|
+
clear() {
|
|
32003
|
+
this.disposed = true;
|
|
32004
|
+
this.appServerGeneration += 1;
|
|
32005
|
+
this.appServerQueriesEnabled = false;
|
|
32006
|
+
this.tasks.clear();
|
|
32007
|
+
this.syncs.clear();
|
|
32008
|
+
}
|
|
32009
|
+
async syncThread(threadId, sessionId) {
|
|
32010
|
+
const snapshot = await this.listAll(threadId);
|
|
32011
|
+
if (snapshot === null || !this.queryIsCurrent(snapshot.generation)) return;
|
|
32012
|
+
const liveTaskIds = /* @__PURE__ */ new Set();
|
|
32013
|
+
for (const terminal of snapshot.terminals) {
|
|
32014
|
+
if (!this.queryIsCurrent(snapshot.generation)) return;
|
|
32015
|
+
liveTaskIds.add(terminal.itemId);
|
|
32016
|
+
const task = this.remember(threadId, sessionId, terminal);
|
|
32017
|
+
if (task.publication === "unpublished" && task.state === "running") await this.announce(task);
|
|
32018
|
+
}
|
|
32019
|
+
for (const task of this.tasks.values()) {
|
|
32020
|
+
if (!this.queryIsCurrent(snapshot.generation)) return;
|
|
32021
|
+
if (task.threadId === threadId && task.publication === "published" && (task.state === "running" || task.state === "stopping") && !liveTaskIds.has(task.itemId)) {
|
|
32022
|
+
await this.finish(task, "stopped");
|
|
32023
|
+
}
|
|
32024
|
+
}
|
|
32025
|
+
}
|
|
32026
|
+
async syncUntilCurrent(threadId, pending) {
|
|
32027
|
+
let hasFailure = false;
|
|
32028
|
+
let failure;
|
|
32029
|
+
do {
|
|
32030
|
+
pending.requested = false;
|
|
32031
|
+
try {
|
|
32032
|
+
await this.syncThread(threadId, pending.sessionId);
|
|
32033
|
+
hasFailure = false;
|
|
32034
|
+
} catch (error51) {
|
|
32035
|
+
hasFailure = true;
|
|
32036
|
+
failure = error51;
|
|
32037
|
+
}
|
|
32038
|
+
} while (pending.requested && this.canQueryAppServer());
|
|
32039
|
+
if (hasFailure) throw failure;
|
|
32040
|
+
}
|
|
32041
|
+
async announce(task) {
|
|
32042
|
+
if (task.publication === "published") return;
|
|
32043
|
+
if (task.announcement !== null) {
|
|
32044
|
+
await task.announcement;
|
|
32045
|
+
return;
|
|
32046
|
+
}
|
|
32047
|
+
task.publication = "publishing";
|
|
32048
|
+
const announcement = this.publishAnnouncement(task);
|
|
32049
|
+
task.announcement = announcement;
|
|
32050
|
+
try {
|
|
32051
|
+
await announcement;
|
|
32052
|
+
} finally {
|
|
32053
|
+
if (task.announcement === announcement) task.announcement = null;
|
|
32054
|
+
}
|
|
32055
|
+
if (isTerminalState(task.state)) await this.publishTerminalState(task);
|
|
32056
|
+
}
|
|
32057
|
+
async publishAnnouncement(task) {
|
|
32058
|
+
try {
|
|
32059
|
+
await this.publishSpawn(task);
|
|
32060
|
+
task.publication = "published";
|
|
32061
|
+
} catch (error51) {
|
|
32062
|
+
task.publication = "unpublished";
|
|
32063
|
+
throw error51;
|
|
32064
|
+
}
|
|
32065
|
+
}
|
|
32066
|
+
async publishSpawn(task) {
|
|
32067
|
+
await this.session.update({
|
|
32068
|
+
sessionUpdate: "tool_call_update",
|
|
32069
|
+
toolCallId: task.itemId,
|
|
32070
|
+
_meta: {
|
|
32071
|
+
[JETBRAINS_META_KEY]: {
|
|
32072
|
+
[AIR_META_KEY]: {
|
|
32073
|
+
[AIR_ASYNC_TASKS_KEY]: {
|
|
32074
|
+
[AIR_ASYNC_TASKS_BACKGROUNDED_KEY]: true
|
|
32075
|
+
}
|
|
32076
|
+
}
|
|
32077
|
+
}
|
|
32078
|
+
}
|
|
32079
|
+
}, task.sessionId);
|
|
32080
|
+
await this.session.update({
|
|
32081
|
+
sessionUpdate: "async_task_spawned",
|
|
32082
|
+
asyncTaskId: task.asyncTaskId,
|
|
32083
|
+
name: task.name,
|
|
32084
|
+
taskType: "shell",
|
|
32085
|
+
showInTranscript: false,
|
|
32086
|
+
canStop: true,
|
|
32087
|
+
toolCallId: task.itemId
|
|
32088
|
+
}, task.sessionId);
|
|
32089
|
+
}
|
|
32090
|
+
remember(threadId, sessionId, terminal, commandTitle2) {
|
|
32091
|
+
const asyncTaskId = wireTaskId(this.rootSessionId, threadId, terminal.itemId);
|
|
32092
|
+
const existing = this.tasks.get(asyncTaskId);
|
|
32093
|
+
if (existing) {
|
|
32094
|
+
existing.processId = terminal.processId;
|
|
32095
|
+
if (commandTitle2 !== void 0) existing.name = commandTitle2;
|
|
32096
|
+
return existing;
|
|
32097
|
+
}
|
|
32098
|
+
const task = {
|
|
32099
|
+
threadId,
|
|
32100
|
+
sessionId,
|
|
32101
|
+
asyncTaskId,
|
|
32102
|
+
processId: terminal.processId,
|
|
32103
|
+
itemId: terminal.itemId,
|
|
32104
|
+
name: commandTitle2 ?? terminal.command,
|
|
32105
|
+
publication: "unpublished",
|
|
32106
|
+
announcement: null,
|
|
32107
|
+
terminalUpdate: null,
|
|
32108
|
+
terminalPublished: false,
|
|
32109
|
+
state: "running"
|
|
32110
|
+
};
|
|
32111
|
+
this.tasks.set(asyncTaskId, task);
|
|
32112
|
+
return task;
|
|
32113
|
+
}
|
|
32114
|
+
async finish(task, state) {
|
|
32115
|
+
if (task.state === "running" || task.state === "stopping") {
|
|
32116
|
+
task.state = state;
|
|
32117
|
+
} else if (task.state !== state) {
|
|
32118
|
+
return;
|
|
32119
|
+
}
|
|
32120
|
+
if (task.announcement !== null) await task.announcement;
|
|
32121
|
+
if (task.publication === "published") await this.publishTerminalState(task);
|
|
32122
|
+
}
|
|
32123
|
+
async publishTerminalState(task) {
|
|
32124
|
+
if (!isTerminalState(task.state) || task.terminalPublished) return;
|
|
32125
|
+
if (task.terminalUpdate !== null) {
|
|
32126
|
+
await task.terminalUpdate;
|
|
32127
|
+
return;
|
|
32128
|
+
}
|
|
32129
|
+
const terminalUpdate = this.session.update({
|
|
32130
|
+
sessionUpdate: "async_task_state_update",
|
|
32131
|
+
asyncTaskId: task.asyncTaskId,
|
|
32132
|
+
state: task.state,
|
|
32133
|
+
toolCallId: task.itemId
|
|
32134
|
+
}, task.sessionId);
|
|
32135
|
+
task.terminalUpdate = terminalUpdate;
|
|
32136
|
+
try {
|
|
32137
|
+
await terminalUpdate;
|
|
32138
|
+
task.terminalPublished = true;
|
|
32139
|
+
} finally {
|
|
32140
|
+
if (task.terminalUpdate === terminalUpdate) task.terminalUpdate = null;
|
|
32141
|
+
}
|
|
32142
|
+
}
|
|
32143
|
+
async listAll(threadId) {
|
|
32144
|
+
const appServer = this.appServer;
|
|
32145
|
+
const generation = this.appServerGeneration;
|
|
32146
|
+
const terminals = [];
|
|
32147
|
+
const seenCursors = /* @__PURE__ */ new Set();
|
|
32148
|
+
let cursor = null;
|
|
32149
|
+
do {
|
|
32150
|
+
const response = await appServer.threadBackgroundTerminalsList({
|
|
32151
|
+
threadId,
|
|
32152
|
+
cursor
|
|
32153
|
+
});
|
|
32154
|
+
if (!this.queryIsCurrent(generation)) return null;
|
|
32155
|
+
terminals.push(...response.data);
|
|
32156
|
+
cursor = response.nextCursor;
|
|
32157
|
+
if (cursor !== null) {
|
|
32158
|
+
if (seenCursors.has(cursor)) {
|
|
32159
|
+
throw new Error("Codex returned a repeated background terminal cursor");
|
|
32160
|
+
}
|
|
32161
|
+
seenCursors.add(cursor);
|
|
32162
|
+
}
|
|
32163
|
+
} while (cursor !== null);
|
|
32164
|
+
return { generation, terminals };
|
|
32165
|
+
}
|
|
32166
|
+
isActive() {
|
|
32167
|
+
return this.enabled && !this.disposed;
|
|
32168
|
+
}
|
|
32169
|
+
canQueryAppServer() {
|
|
32170
|
+
return this.isActive() && this.appServerQueriesEnabled;
|
|
32171
|
+
}
|
|
32172
|
+
queryIsCurrent(generation) {
|
|
32173
|
+
return this.canQueryAppServer() && generation === this.appServerGeneration;
|
|
32174
|
+
}
|
|
32175
|
+
};
|
|
32176
|
+
function isTerminalState(state) {
|
|
32177
|
+
return state === "completed" || state === "failed" || state === "stopped";
|
|
32178
|
+
}
|
|
32179
|
+
function wireTaskId(rootSessionId, threadId, itemId) {
|
|
32180
|
+
return threadId === rootSessionId ? itemId : `${threadId}:${itemId}`;
|
|
32181
|
+
}
|
|
32182
|
+
function notificationThreadId(notification) {
|
|
32183
|
+
const threadId = notification.params.threadId;
|
|
32184
|
+
return typeof threadId === "string" ? threadId : null;
|
|
32185
|
+
}
|
|
32186
|
+
|
|
32187
|
+
// src/CodexAcpServer.ts
|
|
31757
32188
|
var CODEX_PROCESS_EXITED_ERROR_CODE = 1001;
|
|
31758
32189
|
function clientSupportsTypedSessionFailures(capabilities) {
|
|
31759
32190
|
return clientSupportsAirCapability(capabilities, AIR_SESSION_FAILURE_KEY);
|
|
@@ -31791,6 +32222,7 @@ var CodexAcpServer = class _CodexAcpServer {
|
|
|
31791
32222
|
goalControlGenerations;
|
|
31792
32223
|
permissionLifecycleContexts;
|
|
31793
32224
|
codexProcessState;
|
|
32225
|
+
codexProcessGeneration = 0;
|
|
31794
32226
|
initializeRequest = null;
|
|
31795
32227
|
providerUpdate = null;
|
|
31796
32228
|
constructor(connection, codexAcpClient, defaultAuthRequest, getExitCode, getRecentStderr, codexProcessState) {
|
|
@@ -31818,6 +32250,7 @@ var CodexAcpServer = class _CodexAcpServer {
|
|
|
31818
32250
|
this.booleanConfigOptionsSupported = false;
|
|
31819
32251
|
this.currentAuthStatus = null;
|
|
31820
32252
|
this.availableCommands = this.createAvailableCommands(codexAcpClient);
|
|
32253
|
+
this.observeCodexProcess();
|
|
31821
32254
|
}
|
|
31822
32255
|
createAvailableCommands(client) {
|
|
31823
32256
|
return new CodexCommands(
|
|
@@ -31890,7 +32323,8 @@ var CodexAcpServer = class _CodexAcpServer {
|
|
|
31890
32323
|
[AIR_EXTENSION_CAPABILITIES_KEY]: [
|
|
31891
32324
|
AIR_SESSION_FAILURE_KEY,
|
|
31892
32325
|
AIR_AGENT_FILE_CHANGE_REPORT_KEY,
|
|
31893
|
-
AIR_NATIVE_SUBAGENT_SESSIONS_KEY
|
|
32326
|
+
AIR_NATIVE_SUBAGENT_SESSIONS_KEY,
|
|
32327
|
+
AIR_ASYNC_TASKS_KEY
|
|
31894
32328
|
]
|
|
31895
32329
|
}
|
|
31896
32330
|
}
|
|
@@ -31913,6 +32347,18 @@ var CodexAcpServer = class _CodexAcpServer {
|
|
|
31913
32347
|
return await this.unstable_setSessionModel(this.parseLegacySetSessionModelParams(methodRequest.params));
|
|
31914
32348
|
case SESSION_STEERING_METHOD:
|
|
31915
32349
|
return await this.executeOrQueueSteeringRequest(this.parseSessionSteerParams(methodRequest.params));
|
|
32350
|
+
case ASYNC_TASK_STOP_METHOD: {
|
|
32351
|
+
if (this.providerUpdate !== null) {
|
|
32352
|
+
await this.providerUpdate;
|
|
32353
|
+
}
|
|
32354
|
+
const sessionState = this.sessions.get(methodRequest.params.sessionId);
|
|
32355
|
+
if (!sessionState) return { stopped: false };
|
|
32356
|
+
return {
|
|
32357
|
+
stopped: await this.runWithProcessCheck(
|
|
32358
|
+
() => sessionState.asyncTasks.stop(methodRequest.params.asyncTaskId)
|
|
32359
|
+
)
|
|
32360
|
+
};
|
|
32361
|
+
}
|
|
31916
32362
|
case GOAL_CONTROL_METHOD:
|
|
31917
32363
|
case LEGACY_GOAL_CONTROL_METHOD: {
|
|
31918
32364
|
const sessionState = this.sessions.get(methodRequest.params.sessionId);
|
|
@@ -32154,7 +32600,8 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
32154
32600
|
sessionId,
|
|
32155
32601
|
clientSupportsSubagents(this.clientCapabilities),
|
|
32156
32602
|
new ACPSessionConnection(this.connection, sessionId)
|
|
32157
|
-
)
|
|
32603
|
+
),
|
|
32604
|
+
asyncTasks: this.createAsyncTasks(sessionId)
|
|
32158
32605
|
};
|
|
32159
32606
|
sessionState.titleGen = new TitleGenerator(
|
|
32160
32607
|
this.codexAcpClient.appServerClient,
|
|
@@ -32162,7 +32609,7 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
32162
32609
|
sessionState.cwd,
|
|
32163
32610
|
() => sessionState.sessionTitleSource
|
|
32164
32611
|
);
|
|
32165
|
-
this.
|
|
32612
|
+
this.installSessionState(sessionState);
|
|
32166
32613
|
resumeSubscribed = false;
|
|
32167
32614
|
const canPublishSessionUpdates = operation !== "fork";
|
|
32168
32615
|
if (canPublishSessionUpdates && requestedMcpServers.length > 0 && mcpServerStartupVersion !== null) {
|
|
@@ -32177,6 +32624,7 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
32177
32624
|
}
|
|
32178
32625
|
if (operation === "resume") {
|
|
32179
32626
|
this.publishCurrentGoalAsync(sessionState, sessionGeneration);
|
|
32627
|
+
this.publishAsyncTasksAsync(sessionState, sessionGeneration);
|
|
32180
32628
|
}
|
|
32181
32629
|
const sessionModelState = this.createModelState(models, currentModelId);
|
|
32182
32630
|
const sessionModeState = sessionState.agentMode.toSessionModeState();
|
|
@@ -32206,6 +32654,18 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
32206
32654
|
}
|
|
32207
32655
|
return a === b;
|
|
32208
32656
|
}
|
|
32657
|
+
createAsyncTasks(sessionId) {
|
|
32658
|
+
return new CodexBackgroundTerminalTasks(
|
|
32659
|
+
clientSupportsAirCapability(this.clientCapabilities, AIR_ASYNC_TASKS_KEY),
|
|
32660
|
+
sessionId,
|
|
32661
|
+
this.codexAcpClient.appServerClient,
|
|
32662
|
+
new ACPSessionConnection(this.connection, sessionId)
|
|
32663
|
+
);
|
|
32664
|
+
}
|
|
32665
|
+
installSessionState(sessionState) {
|
|
32666
|
+
this.sessions.get(sessionState.sessionId)?.asyncTasks.clear();
|
|
32667
|
+
this.sessions.set(sessionState.sessionId, sessionState);
|
|
32668
|
+
}
|
|
32209
32669
|
getAuthProviderForAuthenticateRequest(request) {
|
|
32210
32670
|
if (isCodexAuthRequest(request) && request.methodId === "gateway") {
|
|
32211
32671
|
return "custom-gateway";
|
|
@@ -32224,6 +32684,7 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
32224
32684
|
thread
|
|
32225
32685
|
} = await this.getOrCreateSessionWithHistory(params);
|
|
32226
32686
|
await this.streamThreadHistory(sessionId, thread);
|
|
32687
|
+
await this.getSessionState(sessionId).asyncTasks.reconcile();
|
|
32227
32688
|
logger.log("Session loaded", {
|
|
32228
32689
|
sessionId,
|
|
32229
32690
|
modelId: modelState.currentModelId,
|
|
@@ -32297,6 +32758,7 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
32297
32758
|
try {
|
|
32298
32759
|
if (sessionState) {
|
|
32299
32760
|
await this.interruptSessionTurn(sessionState, "Close", true);
|
|
32761
|
+
sessionState.asyncTasks.clear();
|
|
32300
32762
|
} else {
|
|
32301
32763
|
logger.log("Close request received for unknown local session", { sessionId: params.sessionId });
|
|
32302
32764
|
}
|
|
@@ -32432,6 +32894,10 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
32432
32894
|
await Promise.all(activePrompts);
|
|
32433
32895
|
}
|
|
32434
32896
|
logger.log("Restarting Codex app-server for provider update", { sessionCount: this.sessions.size });
|
|
32897
|
+
for (const session of this.sessions.values()) {
|
|
32898
|
+
session.asyncTasks.prepareForAppServerReplacement();
|
|
32899
|
+
}
|
|
32900
|
+
await this.finishAllAsyncTasks("stopped", "before the provider restart");
|
|
32435
32901
|
const replacement = await this.restartCodexClient();
|
|
32436
32902
|
apply(replacement);
|
|
32437
32903
|
if (this.initializeRequest === null) {
|
|
@@ -32442,6 +32908,7 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
32442
32908
|
this.availableCommands = this.createAvailableCommands(replacement);
|
|
32443
32909
|
const resumeErrors = [];
|
|
32444
32910
|
for (const session of this.sessions.values()) {
|
|
32911
|
+
session.asyncTasks.setAppServer(replacement.appServerClient);
|
|
32445
32912
|
try {
|
|
32446
32913
|
await replacement.resumeSession({
|
|
32447
32914
|
sessionId: session.sessionId,
|
|
@@ -32450,6 +32917,7 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
32450
32917
|
mcpServers: session.mcpServers ?? []
|
|
32451
32918
|
});
|
|
32452
32919
|
session.authProvider = replacement.getModelProvider();
|
|
32920
|
+
session.asyncTasks.refresh();
|
|
32453
32921
|
logger.log("Resumed session after provider restart", { sessionId: session.sessionId });
|
|
32454
32922
|
} catch (error51) {
|
|
32455
32923
|
resumeErrors.push(error51);
|
|
@@ -32479,12 +32947,22 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
32479
32947
|
state.stderr = (state.stderr + data.toString()).slice(-2 * 1024);
|
|
32480
32948
|
});
|
|
32481
32949
|
}
|
|
32950
|
+
observeCodexProcess() {
|
|
32951
|
+
const process12 = this.codexProcessState?.connection.process;
|
|
32952
|
+
if (!process12) return;
|
|
32953
|
+
const generation = ++this.codexProcessGeneration;
|
|
32954
|
+
process12.once("exit", () => {
|
|
32955
|
+
if (generation !== this.codexProcessGeneration) return;
|
|
32956
|
+
void this.finishAllAsyncTasks("failed", "after the Codex process exited");
|
|
32957
|
+
});
|
|
32958
|
+
}
|
|
32482
32959
|
async restartCodexClient() {
|
|
32483
32960
|
const state = this.codexProcessState;
|
|
32484
32961
|
if (state === null) {
|
|
32485
32962
|
throw new Error("Codex process state is unavailable");
|
|
32486
32963
|
}
|
|
32487
32964
|
const previous = state.connection;
|
|
32965
|
+
this.codexProcessGeneration += 1;
|
|
32488
32966
|
const exited = previous.process.exitCode === null ? once(previous.process, "exit") : Promise.resolve();
|
|
32489
32967
|
previous.process.stdin.end();
|
|
32490
32968
|
const forceKill = setTimeout(() => {
|
|
@@ -32498,6 +32976,7 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
32498
32976
|
state.stderr = "";
|
|
32499
32977
|
state.connection = startCodexConnection(state.codexPath);
|
|
32500
32978
|
this.captureStderr();
|
|
32979
|
+
this.observeCodexProcess();
|
|
32501
32980
|
return new CodexAcpClient(
|
|
32502
32981
|
new CodexAppServerClient(state.connection.connection),
|
|
32503
32982
|
state.config,
|
|
@@ -33047,6 +33526,10 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
33047
33526
|
publishCurrentGoalAsync(sessionState, sessionGeneration) {
|
|
33048
33527
|
void this.publishCurrentGoalBestEffort(sessionState, sessionGeneration, true);
|
|
33049
33528
|
}
|
|
33529
|
+
publishAsyncTasksAsync(sessionState, sessionGeneration) {
|
|
33530
|
+
if (!this.sessionPublishIsCurrent(sessionState, sessionGeneration)) return;
|
|
33531
|
+
sessionState.asyncTasks.refresh();
|
|
33532
|
+
}
|
|
33050
33533
|
async publishCurrentGoalBestEffort(sessionState, sessionGeneration, force) {
|
|
33051
33534
|
try {
|
|
33052
33535
|
await this.publishCurrentGoal(sessionState, sessionGeneration, force);
|
|
@@ -33170,7 +33653,8 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
33170
33653
|
sessionId,
|
|
33171
33654
|
clientSupportsSubagents(this.clientCapabilities),
|
|
33172
33655
|
new ACPSessionConnection(this.connection, sessionId)
|
|
33173
|
-
)
|
|
33656
|
+
),
|
|
33657
|
+
asyncTasks: this.createAsyncTasks(sessionId)
|
|
33174
33658
|
};
|
|
33175
33659
|
sessionState.titleGen = new TitleGenerator(
|
|
33176
33660
|
this.codexAcpClient.appServerClient,
|
|
@@ -33178,7 +33662,7 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
33178
33662
|
sessionState.cwd,
|
|
33179
33663
|
() => sessionState.sessionTitleSource
|
|
33180
33664
|
);
|
|
33181
|
-
this.
|
|
33665
|
+
this.installSessionState(sessionState);
|
|
33182
33666
|
subscribed = false;
|
|
33183
33667
|
if (requestedMcpServers.length > 0 && mcpServerStartupVersion !== null) {
|
|
33184
33668
|
this.pendingMcpStartupSessions.set(sessionId, {
|
|
@@ -33270,6 +33754,15 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
33270
33754
|
/* @__PURE__ */ new Set([...ancestry, item.agentThreadId]),
|
|
33271
33755
|
threadCache
|
|
33272
33756
|
);
|
|
33757
|
+
try {
|
|
33758
|
+
await sessionState.asyncTasks.recover(
|
|
33759
|
+
item.agentThreadId,
|
|
33760
|
+
childSessionId,
|
|
33761
|
+
commandItemIds(childTurn.items)
|
|
33762
|
+
);
|
|
33763
|
+
} catch (error51) {
|
|
33764
|
+
logger.error(`Failed to restore background terminals for ${item.agentThreadId}`, error51);
|
|
33765
|
+
}
|
|
33273
33766
|
}
|
|
33274
33767
|
}
|
|
33275
33768
|
} else if (activityKind === "completed" || activityKind === "interrupted") {
|
|
@@ -34320,6 +34813,7 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
34320
34813
|
throw new RequestError(requestErrorCode, `VC++ redistributable should be installed`);
|
|
34321
34814
|
}
|
|
34322
34815
|
if (exitCode !== null) {
|
|
34816
|
+
await this.finishAllAsyncTasks("failed", "after the Codex process exited");
|
|
34323
34817
|
const stderr = this.getRecentStderr().trim();
|
|
34324
34818
|
const detail = stderr ? `:
|
|
34325
34819
|
${stderr}` : "";
|
|
@@ -34328,6 +34822,15 @@ ${stderr}` : "";
|
|
|
34328
34822
|
throw err;
|
|
34329
34823
|
}
|
|
34330
34824
|
}
|
|
34825
|
+
async finishAllAsyncTasks(state, reason) {
|
|
34826
|
+
for (const session of this.sessions.values()) {
|
|
34827
|
+
try {
|
|
34828
|
+
await session.asyncTasks.finishAll(state);
|
|
34829
|
+
} catch (error51) {
|
|
34830
|
+
logger.error(`Failed to finish background terminal tasks ${reason}`, error51);
|
|
34831
|
+
}
|
|
34832
|
+
}
|
|
34833
|
+
}
|
|
34331
34834
|
async cancel(params) {
|
|
34332
34835
|
const sessionState = this.sessions.get(params.sessionId);
|
|
34333
34836
|
if (!sessionState) {
|
|
@@ -34377,6 +34880,9 @@ function mergeHistoryUpdates(responseItemFallbackUpdates, threadUpdates) {
|
|
|
34377
34880
|
}
|
|
34378
34881
|
return merged;
|
|
34379
34882
|
}
|
|
34883
|
+
function commandItemIds(items) {
|
|
34884
|
+
return new Set(items.filter((item) => item.type === "commandExecution").map((item) => item.id));
|
|
34885
|
+
}
|
|
34380
34886
|
function historyUpdateKey(update) {
|
|
34381
34887
|
switch (update.sessionUpdate) {
|
|
34382
34888
|
case "user_message_chunk":
|
|
@@ -34560,6 +35066,10 @@ var goalControlParamsParser = external_exports.discriminatedUnion("action", [
|
|
|
34560
35066
|
action: external_exports.enum(["pause", "resume", "clear"])
|
|
34561
35067
|
}).passthrough()
|
|
34562
35068
|
]);
|
|
35069
|
+
var asyncTaskStopParamsParser = external_exports.object({
|
|
35070
|
+
sessionId: external_exports.string().trim().min(1),
|
|
35071
|
+
asyncTaskId: external_exports.string().trim().min(1)
|
|
35072
|
+
}).passthrough();
|
|
34563
35073
|
if (process.argv.includes("--version")) {
|
|
34564
35074
|
console.log(`${package_default.name} ${package_default.version}`);
|
|
34565
35075
|
process.exit(0);
|
|
@@ -34640,5 +35150,5 @@ function startAcpServer() {
|
|
|
34640
35150
|
codexAcpServer = null;
|
|
34641
35151
|
}
|
|
34642
35152
|
});
|
|
34643
|
-
}).onRequest(methods.agent.initialize, (ctx) => getAgent().initialize(ctx.params)).onRequest(methods.agent.session.new, (ctx) => getAgent().newSession(ctx.params)).onRequest(methods.agent.session.load, (ctx) => getAgent().loadSession(ctx.params)).onRequest(methods.agent.session.fork, (ctx) => getAgent().forkSession(ctx.params)).onRequest(methods.agent.session.list, (ctx) => getAgent().listSessions(ctx.params)).onRequest(methods.agent.session.delete, (ctx) => getAgent().deleteSession(ctx.params)).onRequest(methods.agent.session.resume, (ctx) => getAgent().resumeSession(ctx.params)).onRequest(methods.agent.session.close, (ctx) => getAgent().closeSession(ctx.params)).onRequest(methods.agent.session.setMode, (ctx) => getAgent().setSessionMode(ctx.params)).onRequest(methods.agent.session.setConfigOption, (ctx) => getAgent().setSessionConfigOption(ctx.params)).onRequest(methods.agent.authenticate, (ctx) => getAgent().authenticate(ctx.params, ctx.requestId)).onRequest(methods.agent.logout, (ctx) => getAgent().logout(ctx.params)).onRequest(methods.agent.providers.list, (ctx) => getAgent().listProviders(ctx.params)).onRequest(methods.agent.providers.set, (ctx) => getAgent().setProvider(ctx.params)).onRequest(methods.agent.providers.disable, (ctx) => getAgent().disableProvider(ctx.params)).onRequest(methods.agent.session.prompt, (ctx) => getAgent().prompt(ctx.params, ctx.signal)).onNotification(methods.agent.session.cancel, (ctx) => getAgent().cancel(ctx.params)).onRequest("authentication/status", emptyExtensionParamsParser, (ctx) => getAgent().extMethod("authentication/status", ctx.params)).onRequest("authentication/logout", emptyExtensionParamsParser, (ctx) => getAgent().extMethod("authentication/logout", ctx.params)).onRequest(LEGACY_SET_SESSION_MODEL_METHOD, legacySetSessionModelParamsParser, (ctx) => getAgent().extMethod(LEGACY_SET_SESSION_MODEL_METHOD, ctx.params)).onRequest(SESSION_STEERING_METHOD, sessionSteerParamsParser, (ctx) => getAgent().extMethod(SESSION_STEERING_METHOD, ctx.params)).onRequest(GOAL_CONTROL_METHOD, goalControlParamsParser, (ctx) => getAgent().extMethod(GOAL_CONTROL_METHOD, ctx.params)).connect(acpJsonStream);
|
|
35153
|
+
}).onRequest(methods.agent.initialize, (ctx) => getAgent().initialize(ctx.params)).onRequest(methods.agent.session.new, (ctx) => getAgent().newSession(ctx.params)).onRequest(methods.agent.session.load, (ctx) => getAgent().loadSession(ctx.params)).onRequest(methods.agent.session.fork, (ctx) => getAgent().forkSession(ctx.params)).onRequest(methods.agent.session.list, (ctx) => getAgent().listSessions(ctx.params)).onRequest(methods.agent.session.delete, (ctx) => getAgent().deleteSession(ctx.params)).onRequest(methods.agent.session.resume, (ctx) => getAgent().resumeSession(ctx.params)).onRequest(methods.agent.session.close, (ctx) => getAgent().closeSession(ctx.params)).onRequest(methods.agent.session.setMode, (ctx) => getAgent().setSessionMode(ctx.params)).onRequest(methods.agent.session.setConfigOption, (ctx) => getAgent().setSessionConfigOption(ctx.params)).onRequest(methods.agent.authenticate, (ctx) => getAgent().authenticate(ctx.params, ctx.requestId)).onRequest(methods.agent.logout, (ctx) => getAgent().logout(ctx.params)).onRequest(methods.agent.providers.list, (ctx) => getAgent().listProviders(ctx.params)).onRequest(methods.agent.providers.set, (ctx) => getAgent().setProvider(ctx.params)).onRequest(methods.agent.providers.disable, (ctx) => getAgent().disableProvider(ctx.params)).onRequest(methods.agent.session.prompt, (ctx) => getAgent().prompt(ctx.params, ctx.signal)).onNotification(methods.agent.session.cancel, (ctx) => getAgent().cancel(ctx.params)).onRequest("authentication/status", emptyExtensionParamsParser, (ctx) => getAgent().extMethod("authentication/status", ctx.params)).onRequest("authentication/logout", emptyExtensionParamsParser, (ctx) => getAgent().extMethod("authentication/logout", ctx.params)).onRequest(LEGACY_SET_SESSION_MODEL_METHOD, legacySetSessionModelParamsParser, (ctx) => getAgent().extMethod(LEGACY_SET_SESSION_MODEL_METHOD, ctx.params)).onRequest(SESSION_STEERING_METHOD, sessionSteerParamsParser, (ctx) => getAgent().extMethod(SESSION_STEERING_METHOD, ctx.params)).onRequest(ASYNC_TASK_STOP_METHOD, asyncTaskStopParamsParser, (ctx) => getAgent().extMethod(ASYNC_TASK_STOP_METHOD, ctx.params)).onRequest(GOAL_CONTROL_METHOD, goalControlParamsParser, (ctx) => getAgent().extMethod(GOAL_CONTROL_METHOD, ctx.params)).connect(acpJsonStream);
|
|
34644
35154
|
}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "1.
|
|
6
|
+
"version": "1.10.1-preview.1",
|
|
7
7
|
"description": "",
|
|
8
8
|
"main": "dist/index.js",
|
|
9
9
|
"bin": {
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
68
|
"@agentclientprotocol/sdk": "^1.4.0",
|
|
69
|
-
"@openai/codex": "^0.153.
|
|
69
|
+
"@openai/codex": "^0.153.3",
|
|
70
70
|
"diff": "^9.0.0",
|
|
71
71
|
"open": "^11.0.1",
|
|
72
72
|
"vscode-jsonrpc": "^9.0.1",
|