@nyssance/codex-acp-v2 0.7.13 → 0.7.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/README.md +1 -1
- package/dist/index.js +40 -128
- package/docs/protocol.md +5 -7
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.7.14 — 2026-10-16
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Sub-agent activity carries `model` and `reasoningEffort` in `rawInput` and `_meta.codex.subagent`.
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Generated against Codex 0.163.0 (`@openai/codex` ^0.163.0); the version floor follows.
|
|
12
|
+
`generate-types` clears `src/app-server` first, so types Codex removed no longer linger.
|
|
13
|
+
- Command approvals offer exactly the `availableDecisions` Codex sends; a missing list, or one naming
|
|
14
|
+
an amendment Codex did not propose, is cancelled. The adapter no longer builds its own decision set.
|
|
15
|
+
- Review exits produce no update: Codex follows every exit with an assistant message carrying the
|
|
16
|
+
conclusion, and the adapter maps that message. Exit buffering and deduplication are removed.
|
|
17
|
+
- Fast mode availability reads `serviceTiers` only; the deprecated `additionalSpeedTiers` is ignored.
|
|
18
|
+
- `codexErrorInfo` values Codex has no name for classify as `unknown`.
|
|
19
|
+
|
|
3
20
|
## 0.7.13 — 2026-10-13
|
|
4
21
|
|
|
5
22
|
### Fixed
|
package/README.md
CHANGED
|
@@ -59,7 +59,7 @@ See [`docs/protocol.md`](docs/protocol.md) for the exact wire contract.
|
|
|
59
59
|
|
|
60
60
|
## Requirements
|
|
61
61
|
|
|
62
|
-
- Codex CLI **0.
|
|
62
|
+
- Codex CLI **0.163.0 or newer** — the app-server types under `src/app-server` are generated
|
|
63
63
|
from the `@openai/codex` release named in `optionalDependencies`, and the agent checks
|
|
64
64
|
`codex --version` before starting the server; an older Codex fails at startup with an
|
|
65
65
|
upgrade hint.
|
package/dist/index.js
CHANGED
|
@@ -22168,7 +22168,7 @@ var package_default = {
|
|
|
22168
22168
|
publishConfig: {
|
|
22169
22169
|
access: "public"
|
|
22170
22170
|
},
|
|
22171
|
-
version: "0.7.
|
|
22171
|
+
version: "0.7.14",
|
|
22172
22172
|
description: "Native ACP v2 agent for the OpenAI Codex app-server",
|
|
22173
22173
|
packageManager: "bun@1.4.0",
|
|
22174
22174
|
engines: {
|
|
@@ -22203,7 +22203,7 @@ var package_default = {
|
|
|
22203
22203
|
"package:win-x64": "cd dist/bin && zip codex-acp-v2-x64-windows.zip codex-acp-v2-x64-windows.exe",
|
|
22204
22204
|
"package:win-arm64": "cd dist/bin && zip codex-acp-v2-arm64-windows.zip codex-acp-v2-arm64-windows.exe",
|
|
22205
22205
|
start: "bun src/index.ts",
|
|
22206
|
-
"generate-types": "codex app-server generate-ts --experimental --out src/app-server",
|
|
22206
|
+
"generate-types": "rm -rf src/app-server && codex app-server generate-ts --experimental --out src/app-server",
|
|
22207
22207
|
test: "vitest run --exclude src/__tests__/e2e.test.ts",
|
|
22208
22208
|
"test:watch": "vitest",
|
|
22209
22209
|
typecheck: "tsc --noEmit",
|
|
@@ -22241,7 +22241,7 @@ var package_default = {
|
|
|
22241
22241
|
"vscode-jsonrpc": "^9.0.2"
|
|
22242
22242
|
},
|
|
22243
22243
|
optionalDependencies: {
|
|
22244
|
-
"@openai/codex": "^0.
|
|
22244
|
+
"@openai/codex": "^0.163.0"
|
|
22245
22245
|
}
|
|
22246
22246
|
};
|
|
22247
22247
|
|
|
@@ -22385,6 +22385,7 @@ class ClientTools {
|
|
|
22385
22385
|
}
|
|
22386
22386
|
|
|
22387
22387
|
// src/agent/turnErrors.ts
|
|
22388
|
+
var CONNECTION_FAILURES = ["httpConnectionFailed", "responseStreamConnectionFailed", "responseStreamDisconnected", "responseTooManyFailedAttempts"];
|
|
22388
22389
|
function classifyTurnError(info) {
|
|
22389
22390
|
const failure2 = (category, retryable = false, stopReason = "error") => ({ category, retryable, stopReason });
|
|
22390
22391
|
if (info === null)
|
|
@@ -22392,7 +22393,9 @@ function classifyTurnError(info) {
|
|
|
22392
22393
|
if (typeof info === "object") {
|
|
22393
22394
|
if ("activeTurnNotSteerable" in info)
|
|
22394
22395
|
return failure2("turn_not_steerable");
|
|
22395
|
-
const status =
|
|
22396
|
+
const status = connectionStatus(info);
|
|
22397
|
+
if (status === undefined)
|
|
22398
|
+
return failure2("unknown");
|
|
22396
22399
|
return failure2("connection", status === null || status === 408 || status === 429 || status >= 500);
|
|
22397
22400
|
}
|
|
22398
22401
|
switch (info) {
|
|
@@ -22426,6 +22429,14 @@ function classifyTurnError(info) {
|
|
|
22426
22429
|
case "other":
|
|
22427
22430
|
return failure2("unknown");
|
|
22428
22431
|
}
|
|
22432
|
+
return failure2("unknown");
|
|
22433
|
+
}
|
|
22434
|
+
function connectionStatus(info) {
|
|
22435
|
+
for (const key of CONNECTION_FAILURES) {
|
|
22436
|
+
if (key in info)
|
|
22437
|
+
return info[key].httpStatusCode;
|
|
22438
|
+
}
|
|
22439
|
+
return;
|
|
22429
22440
|
}
|
|
22430
22441
|
|
|
22431
22442
|
// src/agent/CodexAgent.ts
|
|
@@ -23672,8 +23683,8 @@ function subAgentActivity(item, status, create) {
|
|
|
23672
23683
|
toolCallId: item.id,
|
|
23673
23684
|
...create ? { name: ToolName.SubAgent, title: titles[item.kind], kind: "other" } : {},
|
|
23674
23685
|
status,
|
|
23675
|
-
rawInput: { agentThreadId: item.agentThreadId, agentPath: item.agentPath, activity: item.kind },
|
|
23676
|
-
_meta: { codex: { subagent: { threadId: item.agentThreadId, path: item.agentPath, activity: item.kind } } }
|
|
23686
|
+
rawInput: { model: item.model, reasoningEffort: item.reasoningEffort, agentThreadId: item.agentThreadId, agentPath: item.agentPath, activity: item.kind },
|
|
23687
|
+
_meta: { codex: { subagent: { model: item.model, reasoningEffort: item.reasoningEffort, threadId: item.agentThreadId, path: item.agentPath, activity: item.kind } } }
|
|
23677
23688
|
});
|
|
23678
23689
|
}
|
|
23679
23690
|
function collabToolCall(item, create) {
|
|
@@ -24263,23 +24274,14 @@ function itemSnapshot(item, command) {
|
|
|
24263
24274
|
case "contextCompaction":
|
|
24264
24275
|
return [compactionUpdate(item.id, "completed")];
|
|
24265
24276
|
case "enteredReviewMode":
|
|
24277
|
+
case "exitedReviewMode":
|
|
24266
24278
|
return [];
|
|
24267
|
-
case "exitedReviewMode": {
|
|
24268
|
-
const text = item.review.trim();
|
|
24269
|
-
return text.length > 0 ? [{ sessionUpdate: "agent_message", messageId: item.id, content: [{ type: "text", text }] }] : [];
|
|
24270
|
-
}
|
|
24271
24279
|
case "hookPrompt":
|
|
24272
24280
|
case "functionCallOutput":
|
|
24273
24281
|
case "sleep":
|
|
24274
24282
|
return [];
|
|
24275
24283
|
}
|
|
24276
24284
|
}
|
|
24277
|
-
function isDuplicateReview(item, items) {
|
|
24278
|
-
if (item.type !== "exitedReviewMode")
|
|
24279
|
-
return false;
|
|
24280
|
-
const text = item.review.trim();
|
|
24281
|
-
return text.length > 0 && Array.from(items).some((candidate) => candidate.type === "agentMessage" && candidate.phase !== "commentary" && candidate.text.trim() === text);
|
|
24282
|
-
}
|
|
24283
24285
|
|
|
24284
24286
|
// src/bridge/turnMetadata.ts
|
|
24285
24287
|
function withTurnId(update, turnId, startedAt) {
|
|
@@ -24321,8 +24323,6 @@ class EventBridge {
|
|
|
24321
24323
|
turnPrompt = null;
|
|
24322
24324
|
openToolCalls = new Set;
|
|
24323
24325
|
announcedTools = new Set;
|
|
24324
|
-
pendingReviews = new Map;
|
|
24325
|
-
reviewMessages = new Map;
|
|
24326
24326
|
messagePhases = new Map;
|
|
24327
24327
|
promptMeta = new Map;
|
|
24328
24328
|
reasoningWithDeltas = new Set;
|
|
@@ -24386,8 +24386,6 @@ class EventBridge {
|
|
|
24386
24386
|
this.openToolCalls.clear();
|
|
24387
24387
|
this.announcedTools.clear();
|
|
24388
24388
|
this.messagePhases.clear();
|
|
24389
|
-
this.reviewMessages.clear();
|
|
24390
|
-
this.pendingReviews.clear();
|
|
24391
24389
|
this.reasoningWithDeltas.clear();
|
|
24392
24390
|
this.terminalItems.clear();
|
|
24393
24391
|
this.imageGenerations.clear();
|
|
@@ -24410,8 +24408,6 @@ class EventBridge {
|
|
|
24410
24408
|
for (const item of items) {
|
|
24411
24409
|
if (item.type === "userMessage")
|
|
24412
24410
|
this.unresolvedUserItems.add(item.id);
|
|
24413
|
-
if (item.type === "agentMessage")
|
|
24414
|
-
this.reviewMessages.set(item.id, item);
|
|
24415
24411
|
if (!replay && itemSnapshot(item).some((update) => update.sessionUpdate === "tool_call_update")) {
|
|
24416
24412
|
this.unannouncedItems.set(item.id, { item, turnId });
|
|
24417
24413
|
}
|
|
@@ -24424,12 +24420,8 @@ class EventBridge {
|
|
|
24424
24420
|
if (receipt)
|
|
24425
24421
|
this.restoredCommands.set(item.id, receipt);
|
|
24426
24422
|
this.unresolvedUserItems.delete(item.id);
|
|
24427
|
-
if (!replay
|
|
24428
|
-
continue;
|
|
24429
|
-
if (item.type === "exitedReviewMode") {
|
|
24430
|
-
this.pendingReviews.set(item.id, { item, turnId });
|
|
24423
|
+
if (!replay)
|
|
24431
24424
|
continue;
|
|
24432
|
-
}
|
|
24433
24425
|
if (item.type === "commandExecution" && item.status === "inProgress" && usesTerminal(item))
|
|
24434
24426
|
this.terminalItems.add(item.id);
|
|
24435
24427
|
for (const update of itemSnapshot(item, receipt ?? undefined)) {
|
|
@@ -24549,7 +24541,6 @@ class EventBridge {
|
|
|
24549
24541
|
}
|
|
24550
24542
|
}
|
|
24551
24543
|
async finishOpenToolCalls(status) {
|
|
24552
|
-
await this.flushReviews();
|
|
24553
24544
|
this.unannouncedItems.clear();
|
|
24554
24545
|
const ids = [...this.openToolCalls];
|
|
24555
24546
|
const terminals = [...this.terminalItems];
|
|
@@ -24574,18 +24565,6 @@ class EventBridge {
|
|
|
24574
24565
|
}
|
|
24575
24566
|
async flush() {
|
|
24576
24567
|
await this.flushPlans();
|
|
24577
|
-
await this.flushReviews();
|
|
24578
|
-
}
|
|
24579
|
-
async flushReviews() {
|
|
24580
|
-
const pending = [...this.pendingReviews.values()];
|
|
24581
|
-
this.pendingReviews.clear();
|
|
24582
|
-
for (const { item, turnId } of pending) {
|
|
24583
|
-
if (isDuplicateReview(item, this.reviewMessages.values()))
|
|
24584
|
-
continue;
|
|
24585
|
-
for (const update of itemSnapshot(item)) {
|
|
24586
|
-
await this.client.update(this.withTiming(withTurnId(update, turnId, this.turnTime?.startedAt), turnId));
|
|
24587
|
-
}
|
|
24588
|
-
}
|
|
24589
24568
|
}
|
|
24590
24569
|
dispose() {
|
|
24591
24570
|
this.beginTurn();
|
|
@@ -24709,6 +24688,8 @@ ${details}` : summary)];
|
|
|
24709
24688
|
case "thread/settings/updated":
|
|
24710
24689
|
case "thread/goal/updated":
|
|
24711
24690
|
case "thread/goal/cleared":
|
|
24691
|
+
case "thread/readState/changed":
|
|
24692
|
+
case "thread/prediction/updated":
|
|
24712
24693
|
case "thread/project/updated":
|
|
24713
24694
|
case "thread/environment/connected":
|
|
24714
24695
|
case "thread/environment/disconnected":
|
|
@@ -24825,7 +24806,6 @@ ${details}` : summary)];
|
|
|
24825
24806
|
async itemCompleted(item, turnId) {
|
|
24826
24807
|
switch (item.type) {
|
|
24827
24808
|
case "agentMessage":
|
|
24828
|
-
this.reviewMessages.set(item.id, item);
|
|
24829
24809
|
this.messagePhases.delete(item.id);
|
|
24830
24810
|
return itemSnapshot(item);
|
|
24831
24811
|
case "reasoning": {
|
|
@@ -24864,15 +24844,12 @@ ${details}` : summary)];
|
|
|
24864
24844
|
return [compactionUpdate(item.id, "completed")];
|
|
24865
24845
|
case "plan":
|
|
24866
24846
|
return await this.planCompleted(item);
|
|
24867
|
-
case "exitedReviewMode": {
|
|
24868
|
-
this.pendingReviews.set(item.id, { item, turnId });
|
|
24869
|
-
return [];
|
|
24870
|
-
}
|
|
24871
24847
|
case "userMessage":
|
|
24872
24848
|
case "hookPrompt":
|
|
24873
24849
|
case "functionCallOutput":
|
|
24874
24850
|
case "sleep":
|
|
24875
24851
|
case "enteredReviewMode":
|
|
24852
|
+
case "exitedReviewMode":
|
|
24876
24853
|
return [];
|
|
24877
24854
|
}
|
|
24878
24855
|
}
|
|
@@ -25224,6 +25201,7 @@ class AppServerClient {
|
|
|
25224
25201
|
threadId,
|
|
25225
25202
|
turn: {
|
|
25226
25203
|
id: turnId,
|
|
25204
|
+
rootTurnId: null,
|
|
25227
25205
|
items: [],
|
|
25228
25206
|
itemsView: "notLoaded",
|
|
25229
25207
|
status: "interrupted",
|
|
@@ -25237,7 +25215,7 @@ class AppServerClient {
|
|
|
25237
25215
|
failTurn(threadId, turnId, error40) {
|
|
25238
25216
|
this.recordTurnCompleted({
|
|
25239
25217
|
threadId,
|
|
25240
|
-
turn: { id: turnId, items: [], itemsView: "notLoaded", status: "failed", error: error40, startedAt: null, completedAt: null, durationMs: null }
|
|
25218
|
+
turn: { id: turnId, rootTurnId: null, items: [], itemsView: "notLoaded", status: "failed", error: error40, startedAt: null, completedAt: null, durationMs: null }
|
|
25241
25219
|
});
|
|
25242
25220
|
}
|
|
25243
25221
|
recordTurnCompleted(event) {
|
|
@@ -25975,9 +25953,7 @@ function coerceEffort(model, effort) {
|
|
|
25975
25953
|
return model?.defaultReasoningEffort ?? options[0].reasoningEffort;
|
|
25976
25954
|
}
|
|
25977
25955
|
function modelSupportsFast(model) {
|
|
25978
|
-
|
|
25979
|
-
return false;
|
|
25980
|
-
return model.serviceTiers.some((tier) => tier.id === FAST_SERVICE_TIER) || model.additionalSpeedTiers.includes(FAST_SERVICE_TIER);
|
|
25956
|
+
return model?.serviceTiers.some((tier) => tier.id === FAST_SERVICE_TIER) ?? false;
|
|
25981
25957
|
}
|
|
25982
25958
|
function modelSupportsImages(model) {
|
|
25983
25959
|
return model?.inputModalities.includes("image") ?? true;
|
|
@@ -26077,81 +26053,20 @@ function capitalize(value) {
|
|
|
26077
26053
|
|
|
26078
26054
|
// src/permissions/commandDecisions.ts
|
|
26079
26055
|
function availableCommandDecisions(params) {
|
|
26080
|
-
|
|
26081
|
-
|
|
26082
|
-
}
|
|
26083
|
-
if (!Array.isArray(params.availableDecisions) || params.availableDecisions.length === 0)
|
|
26056
|
+
const decisions = params.availableDecisions;
|
|
26057
|
+
if (!decisions || decisions.length === 0)
|
|
26084
26058
|
return;
|
|
26085
|
-
|
|
26086
|
-
for (const candidate of params.availableDecisions) {
|
|
26087
|
-
const decision = parseDecision(candidate, params);
|
|
26088
|
-
if (!decision)
|
|
26089
|
-
return;
|
|
26090
|
-
decisions.push(decision);
|
|
26091
|
-
}
|
|
26092
|
-
return decisions;
|
|
26093
|
-
}
|
|
26094
|
-
function defaultDecisions(params) {
|
|
26095
|
-
if (params.networkApprovalContext) {
|
|
26096
|
-
const decisions2 = ["accept", "acceptForSession"];
|
|
26097
|
-
for (const amendment of params.proposedNetworkPolicyAmendments ?? []) {
|
|
26098
|
-
decisions2.push({ applyNetworkPolicyAmendment: { network_policy_amendment: amendment } });
|
|
26099
|
-
}
|
|
26100
|
-
decisions2.push("decline", "cancel");
|
|
26101
|
-
return decisions2;
|
|
26102
|
-
}
|
|
26103
|
-
if (params.additionalPermissions)
|
|
26104
|
-
return ["accept", "cancel"];
|
|
26105
|
-
const decisions = ["accept", "acceptForSession"];
|
|
26106
|
-
if (params.proposedExecpolicyAmendment && params.proposedExecpolicyAmendment.length > 0) {
|
|
26107
|
-
decisions.push({ acceptWithExecpolicyAmendment: { execpolicy_amendment: params.proposedExecpolicyAmendment } });
|
|
26108
|
-
}
|
|
26109
|
-
decisions.push("decline", "cancel");
|
|
26110
|
-
return decisions;
|
|
26059
|
+
return decisions.every((decision) => proposed(decision, params)) ? decisions : undefined;
|
|
26111
26060
|
}
|
|
26112
|
-
function
|
|
26113
|
-
if (
|
|
26114
|
-
return
|
|
26115
|
-
|
|
26116
|
-
|
|
26117
|
-
return;
|
|
26118
|
-
if ("acceptWithExecpolicyAmendment" in candidate) {
|
|
26119
|
-
const value = candidate["acceptWithExecpolicyAmendment"];
|
|
26120
|
-
if (!isRecord2(value))
|
|
26121
|
-
return;
|
|
26122
|
-
const amendment = value["execpolicy_amendment"];
|
|
26123
|
-
if (!isStringArray(amendment) || amendment.length === 0)
|
|
26124
|
-
return;
|
|
26125
|
-
if (!sameStrings(amendment, params.proposedExecpolicyAmendment))
|
|
26126
|
-
return;
|
|
26127
|
-
return { acceptWithExecpolicyAmendment: { execpolicy_amendment: [...amendment] } };
|
|
26128
|
-
}
|
|
26129
|
-
if ("applyNetworkPolicyAmendment" in candidate) {
|
|
26130
|
-
const value = candidate["applyNetworkPolicyAmendment"];
|
|
26131
|
-
if (!isRecord2(value))
|
|
26132
|
-
return;
|
|
26133
|
-
const amendment = parseNetworkAmendment(value["network_policy_amendment"]);
|
|
26134
|
-
if (!amendment || !params.networkApprovalContext)
|
|
26135
|
-
return;
|
|
26136
|
-
if (amendment.host !== params.networkApprovalContext.host)
|
|
26137
|
-
return;
|
|
26138
|
-
const proposed = params.proposedNetworkPolicyAmendments ?? [];
|
|
26139
|
-
if (!proposed.some((entry) => entry.host === amendment.host && entry.action === amendment.action))
|
|
26140
|
-
return;
|
|
26141
|
-
return { applyNetworkPolicyAmendment: { network_policy_amendment: amendment } };
|
|
26061
|
+
function proposed(decision, params) {
|
|
26062
|
+
if (typeof decision === "string")
|
|
26063
|
+
return true;
|
|
26064
|
+
if ("acceptWithExecpolicyAmendment" in decision) {
|
|
26065
|
+
const amendment2 = decision.acceptWithExecpolicyAmendment.execpolicy_amendment;
|
|
26066
|
+
return amendment2.length > 0 && sameStrings(amendment2, params.proposedExecpolicyAmendment);
|
|
26142
26067
|
}
|
|
26143
|
-
|
|
26144
|
-
|
|
26145
|
-
function parseNetworkAmendment(value) {
|
|
26146
|
-
if (!isRecord2(value) || typeof value["host"] !== "string")
|
|
26147
|
-
return;
|
|
26148
|
-
const action = value["action"];
|
|
26149
|
-
if (action !== "allow" && action !== "deny")
|
|
26150
|
-
return;
|
|
26151
|
-
return { host: value["host"], action };
|
|
26152
|
-
}
|
|
26153
|
-
function isStringArray(value) {
|
|
26154
|
-
return Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
26068
|
+
const amendment = decision.applyNetworkPolicyAmendment.network_policy_amendment;
|
|
26069
|
+
return amendment.host === params.networkApprovalContext?.host && (params.proposedNetworkPolicyAmendments ?? []).some((entry) => entry.host === amendment.host && entry.action === amendment.action);
|
|
26155
26070
|
}
|
|
26156
26071
|
function sameStrings(left, right) {
|
|
26157
26072
|
return !!right && left.length === right.length && left.every((value, index) => value === right[index]);
|
|
@@ -26394,8 +26309,7 @@ class CodexApprovalHandler {
|
|
|
26394
26309
|
this.signal = signal;
|
|
26395
26310
|
}
|
|
26396
26311
|
async handleCommandExecution(params) {
|
|
26397
|
-
const
|
|
26398
|
-
const decisions = commandDecisionOptions(approvalParams);
|
|
26312
|
+
const decisions = commandDecisionOptions(params);
|
|
26399
26313
|
if (!decisions) {
|
|
26400
26314
|
logger.log("cancelling command approval: no complete decision set", { itemId: params.itemId });
|
|
26401
26315
|
return { decision: "cancel" };
|
|
@@ -26404,7 +26318,7 @@ class CodexApprovalHandler {
|
|
|
26404
26318
|
const response = await this.client.requestPermission({
|
|
26405
26319
|
title: params.networkApprovalContext ? NETWORK_TITLE : COMMAND_TITLE,
|
|
26406
26320
|
description: nonBlank(params.reason),
|
|
26407
|
-
subject: { type: "tool_call", toolCall: commandSubject(
|
|
26321
|
+
subject: { type: "tool_call", toolCall: commandSubject(params) },
|
|
26408
26322
|
options: decisions.map(({ option }) => option)
|
|
26409
26323
|
}, this.signal());
|
|
26410
26324
|
return { decision: selectedDecision(response, decisions) ?? "cancel" };
|
|
@@ -28037,8 +27951,6 @@ function historyUpdates(turns, commandReceipts = new Map) {
|
|
|
28037
27951
|
for (const turn of turns) {
|
|
28038
27952
|
const openingUser = turn.items.find((item) => item.type === "userMessage");
|
|
28039
27953
|
for (const item of turn.items) {
|
|
28040
|
-
if (isDuplicateReview(item, turn.items))
|
|
28041
|
-
continue;
|
|
28042
27954
|
const command = item.type === "userMessage" ? commandReceipts.get(item.id) : undefined;
|
|
28043
27955
|
updates.push(...itemSnapshot(item, command).map((update) => {
|
|
28044
27956
|
const replay = withTurnId(update, turn.id, turn.startedAt);
|
|
@@ -28302,7 +28214,7 @@ async function readSessionHistory(codex, raw, signal, commandReceipts) {
|
|
|
28302
28214
|
if (view === "full")
|
|
28303
28215
|
await read.call(() => assertCommandHistory(codex, start.thread, commandReceipts, signal));
|
|
28304
28216
|
const turns = await read.call(() => Promise.all(page.data.map(async (turn) => {
|
|
28305
|
-
const projections = view === "full" ? await Promise.all(turn.items.
|
|
28217
|
+
const projections = view === "full" ? await Promise.all(turn.items.map(async (item) => projectHistoryItem(item, turn.id, turn.startedAt, item.type === "userMessage" ? await commandReceipts?.read(item.id) : null))) : [];
|
|
28306
28218
|
return {
|
|
28307
28219
|
turnId: turn.id,
|
|
28308
28220
|
status: turn.status,
|
|
@@ -30091,7 +30003,7 @@ function usageOf(session) {
|
|
|
30091
30003
|
function interruptedTurn(threadId) {
|
|
30092
30004
|
return {
|
|
30093
30005
|
threadId,
|
|
30094
|
-
turn: { id: "", items: [], itemsView: "notLoaded", status: "interrupted", error: null, startedAt: null, completedAt: null, durationMs: null }
|
|
30006
|
+
turn: { id: "", rootTurnId: null, items: [], itemsView: "notLoaded", status: "interrupted", error: null, startedAt: null, completedAt: null, durationMs: null }
|
|
30095
30007
|
};
|
|
30096
30008
|
}
|
|
30097
30009
|
function parseSessionListParams(raw) {
|
package/docs/protocol.md
CHANGED
|
@@ -490,7 +490,7 @@ to status `cancelled`, and a cancelled turn marks every still-open tool call
|
|
|
490
490
|
| `web_search` | `fetch` | web search |
|
|
491
491
|
| `view_image` | `read` | image view |
|
|
492
492
|
| `image_generation` | `other` | image generation; result as image content |
|
|
493
|
-
| `subagent`, `collab` | `other` | sub-agent activity and collaboration calls; `_meta.codex.subagent` / `_meta.codex.collaboration` |
|
|
493
|
+
| `subagent`, `collab` | `other` | sub-agent activity and collaboration calls; `_meta.codex.subagent` (`model`, `reasoningEffort`, `threadId`, `path`, `activity`) / `_meta.codex.collaboration` |
|
|
494
494
|
| `fuzzy_file_search` | `search` | Codex fuzzy file search sessions |
|
|
495
495
|
| `guardian_review` | `think` | auto-approval reviews |
|
|
496
496
|
| `mcp_startup` | `other` | failed MCP server startups (status `failed`) |
|
|
@@ -505,7 +505,7 @@ unknown ids, and transport errors fail closed.
|
|
|
505
505
|
|
|
506
506
|
| Title | Options (`optionId` → Codex decision) |
|
|
507
507
|
| --- | --- |
|
|
508
|
-
| `Run command?` / `Allow network access?` | `allow_once` → accept, `allow_for_session` → acceptForSession, `accept_execpolicy_amendment` → acceptWithExecpolicyAmendment, `apply_network_policy_amendment:<n>` → applyNetworkPolicyAmendment, `decline`, `cancel`.
|
|
508
|
+
| `Run command?` / `Allow network access?` | `allow_once` → accept, `allow_for_session` → acceptForSession, `accept_execpolicy_amendment` → acceptWithExecpolicyAmendment, `apply_network_policy_amendment:<n>` → applyNetworkPolicyAmendment, `decline`, `cancel`. Codex always sends `availableDecisions` and that list is authoritative; a missing list, or one naming an amendment Codex did not propose, fails closed with `cancel`. |
|
|
509
509
|
| `Make edits?` | `allow_once` → accept, `allow_for_session` → acceptForSession, `cancel` |
|
|
510
510
|
| `Grant permissions?` | `allow_permissions_turn`, `allow_permissions_turn_strict_auto_review`, `allow_permissions_session`, `reject_permissions` |
|
|
511
511
|
| MCP approvals | `allow_once` / `accept`, `allow_session`, `allow_always` (only when Codex advertises `persist`), `decline`, `cancel` |
|
|
@@ -777,11 +777,9 @@ also contains ordered `{text, question?}` display parts. Receipt-adopting client
|
|
|
777
777
|
that retain optimistic prompt content use these parts for display and copying;
|
|
778
778
|
this does not alter the original input sent to Codex.
|
|
779
779
|
|
|
780
|
-
Review results:
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
A standalone or different review result remains visible. Item-level history retains
|
|
784
|
-
its per-item projection because the matching answer may be outside the requested page.
|
|
780
|
+
Review results: Codex follows every `exitedReviewMode` item with an assistant message
|
|
781
|
+
carrying the same conclusion, including the notice for an interrupted review. The adapter
|
|
782
|
+
maps only that message; review entry and exit markers produce no updates, live or in history.
|
|
785
783
|
|
|
786
784
|
### Plugin enablement
|
|
787
785
|
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.7.
|
|
6
|
+
"version": "0.7.14",
|
|
7
7
|
"description": "Native ACP v2 agent for the OpenAI Codex app-server",
|
|
8
8
|
"packageManager": "bun@1.4.0",
|
|
9
9
|
"engines": {
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"package:win-x64": "cd dist/bin && zip codex-acp-v2-x64-windows.zip codex-acp-v2-x64-windows.exe",
|
|
39
39
|
"package:win-arm64": "cd dist/bin && zip codex-acp-v2-arm64-windows.zip codex-acp-v2-arm64-windows.exe",
|
|
40
40
|
"start": "bun src/index.ts",
|
|
41
|
-
"generate-types": "codex app-server generate-ts --experimental --out src/app-server",
|
|
41
|
+
"generate-types": "rm -rf src/app-server && codex app-server generate-ts --experimental --out src/app-server",
|
|
42
42
|
"test": "vitest run --exclude src/__tests__/e2e.test.ts",
|
|
43
43
|
"test:watch": "vitest",
|
|
44
44
|
"typecheck": "tsc --noEmit",
|
|
@@ -76,6 +76,6 @@
|
|
|
76
76
|
"vscode-jsonrpc": "^9.0.2"
|
|
77
77
|
},
|
|
78
78
|
"optionalDependencies": {
|
|
79
|
-
"@openai/codex": "^0.
|
|
79
|
+
"@openai/codex": "^0.163.0"
|
|
80
80
|
}
|
|
81
81
|
}
|