@agentclientprotocol/codex-acp 1.1.7 → 1.1.9
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 +390 -22
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -23582,34 +23582,68 @@ function sameThreadGoalSnapshot(left, right) {
|
|
|
23582
23582
|
}
|
|
23583
23583
|
|
|
23584
23584
|
// src/CodexEventHandler.ts
|
|
23585
|
-
var CodexEventHandler = class {
|
|
23586
|
-
|
|
23585
|
+
var CodexEventHandler = class _CodexEventHandler {
|
|
23586
|
+
static PLAN_UPDATE_INTERVAL_MS = 150;
|
|
23587
23587
|
sessionState;
|
|
23588
|
+
supportsPlanUpdates;
|
|
23588
23589
|
failure = null;
|
|
23590
|
+
completedPlan = null;
|
|
23589
23591
|
activeFuzzyFileSearchSessions = /* @__PURE__ */ new Set();
|
|
23590
23592
|
activeGuardianApprovalReviews = /* @__PURE__ */ new Set();
|
|
23591
23593
|
activeImageGenerationItems = /* @__PURE__ */ new Set();
|
|
23592
23594
|
emittedImageViewItems = /* @__PURE__ */ new Set();
|
|
23593
23595
|
planDeltaTextByItemId = /* @__PURE__ */ new Map();
|
|
23596
|
+
pendingPlanItemIds = /* @__PURE__ */ new Set();
|
|
23597
|
+
lastEmittedPlanTextByItemId = /* @__PURE__ */ new Map();
|
|
23598
|
+
session;
|
|
23599
|
+
planUpdateTimer = null;
|
|
23600
|
+
planUpdateChain = Promise.resolve();
|
|
23601
|
+
disposed = false;
|
|
23594
23602
|
seenReasoningDeltaItemIds = /* @__PURE__ */ new Set();
|
|
23595
23603
|
terminalCommandIds = /* @__PURE__ */ new Set();
|
|
23596
23604
|
terminalCommandOutputIds = /* @__PURE__ */ new Set();
|
|
23597
23605
|
agentMessagePhases = /* @__PURE__ */ new Map();
|
|
23598
23606
|
activeSubAgentActivities = /* @__PURE__ */ new Set();
|
|
23599
|
-
constructor(connection, sessionState) {
|
|
23600
|
-
this.connection = connection;
|
|
23607
|
+
constructor(connection, sessionState, supportsPlanUpdates = false) {
|
|
23601
23608
|
this.sessionState = sessionState;
|
|
23609
|
+
this.supportsPlanUpdates = supportsPlanUpdates;
|
|
23610
|
+
this.session = new ACPSessionConnection(connection, sessionState.sessionId);
|
|
23602
23611
|
}
|
|
23603
23612
|
getFailure() {
|
|
23604
23613
|
return this.failure;
|
|
23605
23614
|
}
|
|
23615
|
+
takeCompletedPlan() {
|
|
23616
|
+
const plan = this.completedPlan;
|
|
23617
|
+
this.completedPlan = null;
|
|
23618
|
+
return plan;
|
|
23619
|
+
}
|
|
23606
23620
|
async handleNotification(notification) {
|
|
23607
|
-
const session = new ACPSessionConnection(this.connection, this.sessionState.sessionId);
|
|
23608
23621
|
const updateEvent = await this.createUpdateEvent(notification);
|
|
23609
23622
|
if (updateEvent) {
|
|
23610
|
-
await session.update(updateEvent);
|
|
23623
|
+
await this.session.update(updateEvent);
|
|
23611
23624
|
}
|
|
23612
23625
|
}
|
|
23626
|
+
async flushPendingPlanUpdates() {
|
|
23627
|
+
this.cancelPlanUpdateTimer();
|
|
23628
|
+
do {
|
|
23629
|
+
const itemIds = [...this.pendingPlanItemIds];
|
|
23630
|
+
this.pendingPlanItemIds.clear();
|
|
23631
|
+
await Promise.all(itemIds.map((itemId) => {
|
|
23632
|
+
const text = this.planDeltaTextByItemId.get(itemId) ?? "";
|
|
23633
|
+
return text.length > 0 ? this.enqueuePlanSnapshot(itemId, text) : Promise.resolve();
|
|
23634
|
+
}));
|
|
23635
|
+
await this.planUpdateChain;
|
|
23636
|
+
} while (this.pendingPlanItemIds.size > 0);
|
|
23637
|
+
}
|
|
23638
|
+
async dispose() {
|
|
23639
|
+
if (this.disposed) return;
|
|
23640
|
+
await this.flushPendingPlanUpdates();
|
|
23641
|
+
this.disposed = true;
|
|
23642
|
+
this.cancelPlanUpdateTimer();
|
|
23643
|
+
this.pendingPlanItemIds.clear();
|
|
23644
|
+
this.planDeltaTextByItemId.clear();
|
|
23645
|
+
this.lastEmittedPlanTextByItemId.clear();
|
|
23646
|
+
}
|
|
23613
23647
|
async createUpdateEvent(notification) {
|
|
23614
23648
|
switch (notification.method) {
|
|
23615
23649
|
case "item/agentMessage/delta":
|
|
@@ -23628,6 +23662,8 @@ var CodexEventHandler = class {
|
|
|
23628
23662
|
this.sessionState.currentTurnId = notification.params.turn.id;
|
|
23629
23663
|
return null;
|
|
23630
23664
|
case "turn/completed":
|
|
23665
|
+
await this.flushPendingPlanUpdates();
|
|
23666
|
+
this.clearPlanTurnState();
|
|
23631
23667
|
this.sessionState.currentTurnId = null;
|
|
23632
23668
|
return null;
|
|
23633
23669
|
case "thread/tokenUsage/updated":
|
|
@@ -23796,7 +23832,12 @@ ${event.details}` : "";
|
|
|
23796
23832
|
return null;
|
|
23797
23833
|
}
|
|
23798
23834
|
const text = this.planDeltaTextByItemId.get(event.itemId) ?? "";
|
|
23799
|
-
|
|
23835
|
+
const updatedText = text + event.delta;
|
|
23836
|
+
this.planDeltaTextByItemId.set(event.itemId, updatedText);
|
|
23837
|
+
if (this.supportsPlanUpdates) {
|
|
23838
|
+
this.pendingPlanItemIds.add(event.itemId);
|
|
23839
|
+
this.schedulePlanUpdate();
|
|
23840
|
+
}
|
|
23800
23841
|
return null;
|
|
23801
23842
|
}
|
|
23802
23843
|
createReasoningSectionBreakEvent(event) {
|
|
@@ -23894,8 +23935,7 @@ ${event.details}` : "";
|
|
|
23894
23935
|
return null;
|
|
23895
23936
|
case "plan": {
|
|
23896
23937
|
const deltaText = this.planDeltaTextByItemId.get(event.item.id) ?? "";
|
|
23897
|
-
this.
|
|
23898
|
-
return this.createCompletedPlanEvent(event.item, deltaText);
|
|
23938
|
+
return await this.createCompletedPlanEvent(event.item, deltaText);
|
|
23899
23939
|
}
|
|
23900
23940
|
case "exitedReviewMode":
|
|
23901
23941
|
return this.createExitedReviewModeEvent(event.item);
|
|
@@ -23924,13 +23964,64 @@ ${event.details}` : "";
|
|
|
23924
23964
|
}
|
|
23925
23965
|
return this.createAgentThoughtEvent(text, item.id);
|
|
23926
23966
|
}
|
|
23927
|
-
createCompletedPlanEvent(item, deltaText) {
|
|
23967
|
+
async createCompletedPlanEvent(item, deltaText) {
|
|
23928
23968
|
const text = item.text.length > 0 ? item.text : deltaText;
|
|
23969
|
+
this.pendingPlanItemIds.delete(item.id);
|
|
23970
|
+
if (this.pendingPlanItemIds.size === 0) {
|
|
23971
|
+
this.cancelPlanUpdateTimer();
|
|
23972
|
+
}
|
|
23973
|
+
this.planDeltaTextByItemId.delete(item.id);
|
|
23929
23974
|
if (text.length === 0) {
|
|
23930
23975
|
return null;
|
|
23931
23976
|
}
|
|
23977
|
+
this.completedPlan = { itemId: item.id, text };
|
|
23978
|
+
if (this.supportsPlanUpdates) {
|
|
23979
|
+
await this.enqueuePlanSnapshot(item.id, text);
|
|
23980
|
+
return null;
|
|
23981
|
+
}
|
|
23932
23982
|
return this.createPlanTextEvent(text, item.id);
|
|
23933
23983
|
}
|
|
23984
|
+
schedulePlanUpdate() {
|
|
23985
|
+
if (this.disposed || this.planUpdateTimer !== null) return;
|
|
23986
|
+
this.planUpdateTimer = setTimeout(() => {
|
|
23987
|
+
this.planUpdateTimer = null;
|
|
23988
|
+
void this.flushPendingPlanUpdates().catch((error51) => {
|
|
23989
|
+
logger.error("Failed to flush throttled plan updates", error51);
|
|
23990
|
+
});
|
|
23991
|
+
}, _CodexEventHandler.PLAN_UPDATE_INTERVAL_MS);
|
|
23992
|
+
}
|
|
23993
|
+
cancelPlanUpdateTimer() {
|
|
23994
|
+
if (this.planUpdateTimer === null) return;
|
|
23995
|
+
clearTimeout(this.planUpdateTimer);
|
|
23996
|
+
this.planUpdateTimer = null;
|
|
23997
|
+
}
|
|
23998
|
+
enqueuePlanSnapshot(itemId, text) {
|
|
23999
|
+
const send = async () => {
|
|
24000
|
+
if (this.lastEmittedPlanTextByItemId.get(itemId) === text) return;
|
|
24001
|
+
await this.session.update(this.createPlanUpdateEvent(text, itemId));
|
|
24002
|
+
this.lastEmittedPlanTextByItemId.set(itemId, text);
|
|
24003
|
+
};
|
|
24004
|
+
const result = this.planUpdateChain.then(send);
|
|
24005
|
+
this.planUpdateChain = result.catch(() => {
|
|
24006
|
+
});
|
|
24007
|
+
return result;
|
|
24008
|
+
}
|
|
24009
|
+
clearPlanTurnState() {
|
|
24010
|
+
this.cancelPlanUpdateTimer();
|
|
24011
|
+
this.pendingPlanItemIds.clear();
|
|
24012
|
+
this.planDeltaTextByItemId.clear();
|
|
24013
|
+
this.lastEmittedPlanTextByItemId.clear();
|
|
24014
|
+
}
|
|
24015
|
+
createPlanUpdateEvent(text, planId) {
|
|
24016
|
+
return {
|
|
24017
|
+
sessionUpdate: "plan_update",
|
|
24018
|
+
plan: {
|
|
24019
|
+
type: "markdown",
|
|
24020
|
+
planId,
|
|
24021
|
+
content: text
|
|
24022
|
+
}
|
|
24023
|
+
};
|
|
24024
|
+
}
|
|
23934
24025
|
createPlanTextEvent(text, messageId) {
|
|
23935
24026
|
return createAgentTextMessageChunk(
|
|
23936
24027
|
text,
|
|
@@ -24182,12 +24273,17 @@ var ApprovalOptionId = {
|
|
|
24182
24273
|
};
|
|
24183
24274
|
|
|
24184
24275
|
// src/CodexApprovalHandler.ts
|
|
24185
|
-
function permissionOption(optionId, name, kind, codexMeta) {
|
|
24276
|
+
function permissionOption(optionId, name, kind, codexMeta, permission) {
|
|
24186
24277
|
return {
|
|
24187
24278
|
optionId,
|
|
24188
24279
|
name,
|
|
24189
24280
|
kind,
|
|
24190
|
-
...codexMeta
|
|
24281
|
+
...codexMeta || permission ? {
|
|
24282
|
+
_meta: {
|
|
24283
|
+
...permission ? { permission } : {},
|
|
24284
|
+
...codexMeta ? { codex: codexMeta } : {}
|
|
24285
|
+
}
|
|
24286
|
+
} : {}
|
|
24191
24287
|
};
|
|
24192
24288
|
}
|
|
24193
24289
|
var CodexApprovalHandler = class {
|
|
@@ -24294,13 +24390,15 @@ var CodexApprovalHandler = class {
|
|
|
24294
24390
|
ApprovalOptionId.AllowPermissionsForSession,
|
|
24295
24391
|
"Allow for Session",
|
|
24296
24392
|
"allow_always",
|
|
24297
|
-
{ decision: "allowPermissionsForSession", permissions: params.permissions }
|
|
24393
|
+
{ decision: "allowPermissionsForSession", permissions: params.permissions },
|
|
24394
|
+
this.permissionGrantMetadata(params.permissions, "session")
|
|
24298
24395
|
),
|
|
24299
24396
|
permissionOption(
|
|
24300
24397
|
ApprovalOptionId.AllowPermissionsForTurn,
|
|
24301
24398
|
"Allow Once",
|
|
24302
24399
|
"allow_once",
|
|
24303
|
-
{ decision: "allowPermissionsForTurn", permissions: params.permissions }
|
|
24400
|
+
{ decision: "allowPermissionsForTurn", permissions: params.permissions },
|
|
24401
|
+
this.permissionGrantMetadata(params.permissions, "turn")
|
|
24304
24402
|
),
|
|
24305
24403
|
permissionOption(
|
|
24306
24404
|
ApprovalOptionId.RejectPermissions,
|
|
@@ -24362,7 +24460,24 @@ var CodexApprovalHandler = class {
|
|
|
24362
24460
|
ApprovalOptionId.AllowAlways,
|
|
24363
24461
|
params.networkApprovalContext ? "Allow Host for Session" : "Allow for Session",
|
|
24364
24462
|
"allow_always",
|
|
24365
|
-
{ decision: "acceptForSession" }
|
|
24463
|
+
{ decision: "acceptForSession" },
|
|
24464
|
+
params.networkApprovalContext ? {
|
|
24465
|
+
version: 1,
|
|
24466
|
+
changes: [{
|
|
24467
|
+
type: "grant",
|
|
24468
|
+
operation: "grant",
|
|
24469
|
+
description: `Allow access to ${params.networkApprovalContext.host} for this session`,
|
|
24470
|
+
lifetime: { scope: "session" },
|
|
24471
|
+
targets: [{
|
|
24472
|
+
type: "network",
|
|
24473
|
+
matcher: {
|
|
24474
|
+
type: "host",
|
|
24475
|
+
host: params.networkApprovalContext.host,
|
|
24476
|
+
protocol: params.networkApprovalContext.protocol
|
|
24477
|
+
}
|
|
24478
|
+
}]
|
|
24479
|
+
}]
|
|
24480
|
+
} : void 0
|
|
24366
24481
|
),
|
|
24367
24482
|
decision: "acceptForSession"
|
|
24368
24483
|
}
|
|
@@ -24376,6 +24491,22 @@ var CodexApprovalHandler = class {
|
|
|
24376
24491
|
{
|
|
24377
24492
|
decision: "acceptWithExecpolicyAmendment",
|
|
24378
24493
|
execpolicyAmendment: params.proposedExecpolicyAmendment
|
|
24494
|
+
},
|
|
24495
|
+
{
|
|
24496
|
+
version: 1,
|
|
24497
|
+
changes: [{
|
|
24498
|
+
type: "policy_rule",
|
|
24499
|
+
operation: "add",
|
|
24500
|
+
ruleBehavior: "allow",
|
|
24501
|
+
description: `Allow commands starting with ${params.proposedExecpolicyAmendment.join(" ")}`,
|
|
24502
|
+
targets: [{
|
|
24503
|
+
type: "command",
|
|
24504
|
+
matcher: {
|
|
24505
|
+
type: "argv_prefix",
|
|
24506
|
+
argv: params.proposedExecpolicyAmendment
|
|
24507
|
+
}
|
|
24508
|
+
}]
|
|
24509
|
+
}]
|
|
24379
24510
|
}
|
|
24380
24511
|
),
|
|
24381
24512
|
decision: {
|
|
@@ -24394,6 +24525,22 @@ var CodexApprovalHandler = class {
|
|
|
24394
24525
|
{
|
|
24395
24526
|
decision: "applyNetworkPolicyAmendment",
|
|
24396
24527
|
networkPolicyAmendment: amendment
|
|
24528
|
+
},
|
|
24529
|
+
{
|
|
24530
|
+
version: 1,
|
|
24531
|
+
changes: [{
|
|
24532
|
+
type: "policy_rule",
|
|
24533
|
+
operation: "add",
|
|
24534
|
+
ruleBehavior: amendment.action,
|
|
24535
|
+
description: amendment.action === "allow" ? `Allow access to ${amendment.host}` : `Block access to ${amendment.host}`,
|
|
24536
|
+
targets: [{
|
|
24537
|
+
type: "network",
|
|
24538
|
+
matcher: {
|
|
24539
|
+
type: "host",
|
|
24540
|
+
host: amendment.host
|
|
24541
|
+
}
|
|
24542
|
+
}]
|
|
24543
|
+
}]
|
|
24397
24544
|
}
|
|
24398
24545
|
),
|
|
24399
24546
|
decision: {
|
|
@@ -24420,7 +24567,21 @@ var CodexApprovalHandler = class {
|
|
|
24420
24567
|
ApprovalOptionId.AllowAlways,
|
|
24421
24568
|
params.grantRoot ? "Allow Root for Session" : "Allow for Session",
|
|
24422
24569
|
"allow_always",
|
|
24423
|
-
{ decision: "acceptForSession", grantRoot: params.grantRoot ?? null }
|
|
24570
|
+
{ decision: "acceptForSession", grantRoot: params.grantRoot ?? null },
|
|
24571
|
+
params.grantRoot ? {
|
|
24572
|
+
version: 1,
|
|
24573
|
+
changes: [{
|
|
24574
|
+
type: "grant",
|
|
24575
|
+
operation: "grant",
|
|
24576
|
+
description: `Allow writes under ${params.grantRoot} for this session`,
|
|
24577
|
+
lifetime: { scope: "session" },
|
|
24578
|
+
targets: [{
|
|
24579
|
+
type: "filesystem",
|
|
24580
|
+
access: ["write"],
|
|
24581
|
+
matcher: { type: "directory", path: params.grantRoot }
|
|
24582
|
+
}]
|
|
24583
|
+
}]
|
|
24584
|
+
} : void 0
|
|
24424
24585
|
),
|
|
24425
24586
|
decision: "acceptForSession"
|
|
24426
24587
|
},
|
|
@@ -24443,6 +24604,68 @@ var CodexApprovalHandler = class {
|
|
|
24443
24604
|
...permissions.fileSystem ? { fileSystem: permissions.fileSystem } : {}
|
|
24444
24605
|
};
|
|
24445
24606
|
}
|
|
24607
|
+
permissionGrantMetadata(permissions, scope) {
|
|
24608
|
+
const changes = [];
|
|
24609
|
+
const lifetime = { scope };
|
|
24610
|
+
const suffix = scope === "session" ? " for this session" : " for this turn";
|
|
24611
|
+
if (permissions.network?.enabled !== null && permissions.network?.enabled !== void 0) {
|
|
24612
|
+
const allowed = permissions.network.enabled;
|
|
24613
|
+
changes.push({
|
|
24614
|
+
type: allowed ? "grant" : "policy_rule",
|
|
24615
|
+
operation: allowed ? "grant" : "add",
|
|
24616
|
+
...allowed ? {} : { ruleBehavior: "deny" },
|
|
24617
|
+
description: `${allowed ? "Allow" : "Deny"} network access${suffix}`,
|
|
24618
|
+
lifetime,
|
|
24619
|
+
targets: [{ type: "network", matcher: { type: "any" } }]
|
|
24620
|
+
});
|
|
24621
|
+
}
|
|
24622
|
+
const fileSystem = permissions.fileSystem;
|
|
24623
|
+
for (const path6 of fileSystem?.read ?? []) {
|
|
24624
|
+
changes.push(this.fileSystemGrantChange(path6, "read", lifetime, suffix));
|
|
24625
|
+
}
|
|
24626
|
+
for (const path6 of fileSystem?.write ?? []) {
|
|
24627
|
+
changes.push(this.fileSystemGrantChange(path6, "write", lifetime, suffix));
|
|
24628
|
+
}
|
|
24629
|
+
for (const entry of fileSystem?.entries ?? []) {
|
|
24630
|
+
const matcher = (() => {
|
|
24631
|
+
switch (entry.path.type) {
|
|
24632
|
+
case "path":
|
|
24633
|
+
return { type: "exact_path", path: entry.path.path };
|
|
24634
|
+
case "glob_pattern":
|
|
24635
|
+
return { type: "glob", pattern: entry.path.pattern };
|
|
24636
|
+
case "special":
|
|
24637
|
+
return { type: "special", provider: "codex", value: entry.path.value };
|
|
24638
|
+
}
|
|
24639
|
+
})();
|
|
24640
|
+
const pathDescription = entry.path.type === "path" ? entry.path.path : entry.path.type === "glob_pattern" ? entry.path.pattern : JSON.stringify(entry.path.value);
|
|
24641
|
+
changes.push({
|
|
24642
|
+
type: entry.access === "deny" ? "policy_rule" : "grant",
|
|
24643
|
+
operation: entry.access === "deny" ? "add" : "grant",
|
|
24644
|
+
...entry.access === "deny" ? { ruleBehavior: "deny" } : {},
|
|
24645
|
+
description: entry.access === "deny" ? `Deny filesystem access to ${pathDescription}${suffix}` : `Allow ${entry.access} access to ${pathDescription}${suffix}`,
|
|
24646
|
+
lifetime,
|
|
24647
|
+
targets: [{
|
|
24648
|
+
type: "filesystem",
|
|
24649
|
+
...entry.access === "deny" ? {} : { access: [entry.access] },
|
|
24650
|
+
matcher
|
|
24651
|
+
}]
|
|
24652
|
+
});
|
|
24653
|
+
}
|
|
24654
|
+
return changes.length > 0 ? { version: 1, changes } : void 0;
|
|
24655
|
+
}
|
|
24656
|
+
fileSystemGrantChange(path6, access, lifetime, suffix) {
|
|
24657
|
+
return {
|
|
24658
|
+
type: "grant",
|
|
24659
|
+
operation: "grant",
|
|
24660
|
+
description: `Allow ${access} access to ${path6}${suffix}`,
|
|
24661
|
+
lifetime,
|
|
24662
|
+
targets: [{
|
|
24663
|
+
type: "filesystem",
|
|
24664
|
+
access: [access],
|
|
24665
|
+
matcher: { type: "exact_path", path: path6 }
|
|
24666
|
+
}]
|
|
24667
|
+
};
|
|
24668
|
+
}
|
|
24446
24669
|
networkPolicyAmendmentOptionId(index) {
|
|
24447
24670
|
return `${ApprovalOptionId.ApplyNetworkPolicyAmendment}:${index}`;
|
|
24448
24671
|
}
|
|
@@ -25913,7 +26136,7 @@ var package_default = {
|
|
|
25913
26136
|
publishConfig: {
|
|
25914
26137
|
access: "public"
|
|
25915
26138
|
},
|
|
25916
|
-
version: "1.1.
|
|
26139
|
+
version: "1.1.9",
|
|
25917
26140
|
description: "",
|
|
25918
26141
|
main: "dist/index.js",
|
|
25919
26142
|
bin: {
|
|
@@ -28413,7 +28636,14 @@ function isJetBrains2026_1Client(clientInfo) {
|
|
|
28413
28636
|
return (isIntelliJPlatform || isJetBrainsClient) && clientInfo.version.startsWith("2026.1");
|
|
28414
28637
|
}
|
|
28415
28638
|
|
|
28639
|
+
// src/PlanCapabilities.ts
|
|
28640
|
+
function clientSupportsPlanUpdates(clientCapabilities) {
|
|
28641
|
+
return clientCapabilities?.plan != null;
|
|
28642
|
+
}
|
|
28643
|
+
|
|
28416
28644
|
// src/CodexAcpServer.ts
|
|
28645
|
+
var IMPLEMENT_PLAN_OPTION_ID = "implement_plan";
|
|
28646
|
+
var REVISE_PLAN_OPTION_ID = "revise_plan";
|
|
28417
28647
|
var CodexAcpServer = class _CodexAcpServer {
|
|
28418
28648
|
static MODEL_NAME_TOKEN_OVERRIDES = {
|
|
28419
28649
|
gpt: "GPT",
|
|
@@ -29532,7 +29762,7 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
29532
29762
|
case "contextCompaction":
|
|
29533
29763
|
return [createCompletedContextCompactionUpdate(item)];
|
|
29534
29764
|
case "plan":
|
|
29535
|
-
return [this.
|
|
29765
|
+
return item.text.length > 0 ? [this.createPlanHistoryUpdate(item)] : [];
|
|
29536
29766
|
}
|
|
29537
29767
|
}
|
|
29538
29768
|
createUserMessageUpdates(item) {
|
|
@@ -29573,7 +29803,17 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
29573
29803
|
}
|
|
29574
29804
|
};
|
|
29575
29805
|
}
|
|
29576
|
-
|
|
29806
|
+
createPlanHistoryUpdate(item) {
|
|
29807
|
+
if (clientSupportsPlanUpdates(this.clientCapabilities)) {
|
|
29808
|
+
return {
|
|
29809
|
+
sessionUpdate: "plan_update",
|
|
29810
|
+
plan: {
|
|
29811
|
+
type: "markdown",
|
|
29812
|
+
planId: item.id,
|
|
29813
|
+
content: item.text
|
|
29814
|
+
}
|
|
29815
|
+
};
|
|
29816
|
+
}
|
|
29577
29817
|
return createAgentTextMessageChunk(
|
|
29578
29818
|
item.text,
|
|
29579
29819
|
item.id,
|
|
@@ -29866,8 +30106,14 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
29866
30106
|
return pendingTurnStart;
|
|
29867
30107
|
};
|
|
29868
30108
|
const disposePromptRequestCancellation = this.observePromptRequestCancellation(signal, sessionState, activePrompt);
|
|
30109
|
+
let eventHandler = null;
|
|
29869
30110
|
try {
|
|
29870
|
-
const
|
|
30111
|
+
const promptEventHandler = new CodexEventHandler(
|
|
30112
|
+
this.connection,
|
|
30113
|
+
sessionState,
|
|
30114
|
+
clientSupportsPlanUpdates(this.clientCapabilities)
|
|
30115
|
+
);
|
|
30116
|
+
eventHandler = promptEventHandler;
|
|
29871
30117
|
const approvalHandler = new CodexApprovalHandler(this.connection, sessionState, activePrompt.signal);
|
|
29872
30118
|
const elicitationHandler = new CodexElicitationHandler(
|
|
29873
30119
|
this.connection,
|
|
@@ -29879,7 +30125,7 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
29879
30125
|
params.sessionId,
|
|
29880
30126
|
async (event) => {
|
|
29881
30127
|
await elicitationHandler.handleNotification(event);
|
|
29882
|
-
return
|
|
30128
|
+
return promptEventHandler.handleNotification(event);
|
|
29883
30129
|
},
|
|
29884
30130
|
approvalHandler,
|
|
29885
30131
|
elicitationHandler
|
|
@@ -29994,7 +30240,7 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
29994
30240
|
logger.error(`Prompt for cancelled session ${params.sessionId} failed after prompt returned`, err);
|
|
29995
30241
|
}
|
|
29996
30242
|
});
|
|
29997
|
-
|
|
30243
|
+
let turnCompleted = await Promise.race([
|
|
29998
30244
|
sendPromptPromise,
|
|
29999
30245
|
activePrompt.closeSignal,
|
|
30000
30246
|
this.cancelBeforeTurnStarted(activePrompt)
|
|
@@ -30004,6 +30250,7 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
30004
30250
|
}
|
|
30005
30251
|
await this.codexAcpClient.waitForSessionNotifications(params.sessionId);
|
|
30006
30252
|
if (turnCompleted.turn.status === "interrupted") {
|
|
30253
|
+
await eventHandler.flushPendingPlanUpdates();
|
|
30007
30254
|
await this.notifyConversationInterrupted(params.sessionId);
|
|
30008
30255
|
return this.cancelledPromptResponse(sessionState);
|
|
30009
30256
|
}
|
|
@@ -30011,6 +30258,75 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
30011
30258
|
if (error51) {
|
|
30012
30259
|
throw error51;
|
|
30013
30260
|
}
|
|
30261
|
+
await eventHandler.flushPendingPlanUpdates();
|
|
30262
|
+
const completedPlan = eventHandler.takeCompletedPlan();
|
|
30263
|
+
if (completedPlan !== null && sessionState.collaborationMode === PLAN_COLLABORATION_MODE && !this.promptShouldStop(params.sessionId, activePrompt)) {
|
|
30264
|
+
const approved = await this.requestPlanImplementationPermission(
|
|
30265
|
+
sessionState,
|
|
30266
|
+
completedPlan,
|
|
30267
|
+
activePrompt.signal
|
|
30268
|
+
);
|
|
30269
|
+
if (this.promptShouldStop(params.sessionId, activePrompt)) {
|
|
30270
|
+
return this.cancelledPromptResponse(sessionState);
|
|
30271
|
+
}
|
|
30272
|
+
if (approved && !this.promptShouldStop(params.sessionId, activePrompt)) {
|
|
30273
|
+
await this.applyCollaborationModeChange(sessionState, DEFAULT_COLLABORATION_MODE);
|
|
30274
|
+
const session = new ACPSessionConnection(this.connection, sessionState.sessionId);
|
|
30275
|
+
await session.update({
|
|
30276
|
+
sessionUpdate: "config_option_update",
|
|
30277
|
+
configOptions: this.createSessionConfigOptions(sessionState)
|
|
30278
|
+
});
|
|
30279
|
+
const implementationRequest = {
|
|
30280
|
+
sessionId: params.sessionId,
|
|
30281
|
+
prompt: [{ type: "text", text: "Implement the approved plan." }]
|
|
30282
|
+
};
|
|
30283
|
+
activePrompt.currentTurn = null;
|
|
30284
|
+
const implementationPromise = this.runWithProcessCheck(
|
|
30285
|
+
() => this.codexAcpClient.sendPrompt(
|
|
30286
|
+
implementationRequest,
|
|
30287
|
+
agentMode,
|
|
30288
|
+
modelId,
|
|
30289
|
+
serviceTier,
|
|
30290
|
+
disableSummary,
|
|
30291
|
+
sessionState.cwd,
|
|
30292
|
+
sessionState.additionalDirectories,
|
|
30293
|
+
(turnId) => {
|
|
30294
|
+
const turn = { threadId: params.sessionId, turnId };
|
|
30295
|
+
activePrompt.currentTurn = turn;
|
|
30296
|
+
if (this.promptShouldStop(params.sessionId, activePrompt)) {
|
|
30297
|
+
this.interruptLateStartedTurn(turn);
|
|
30298
|
+
return;
|
|
30299
|
+
}
|
|
30300
|
+
sessionState.currentTurnId = turnId;
|
|
30301
|
+
},
|
|
30302
|
+
() => this.promptShouldStop(params.sessionId, activePrompt)
|
|
30303
|
+
)
|
|
30304
|
+
);
|
|
30305
|
+
void implementationPromise.catch((err) => {
|
|
30306
|
+
if (this.activePrompts.get(params.sessionId) !== activePrompt) {
|
|
30307
|
+
logger.error(`Implementation turn for cancelled prompt ${params.sessionId} failed after prompt returned`, err);
|
|
30308
|
+
}
|
|
30309
|
+
});
|
|
30310
|
+
turnCompleted = await Promise.race([
|
|
30311
|
+
implementationPromise,
|
|
30312
|
+
activePrompt.closeSignal,
|
|
30313
|
+
this.cancelBeforeTurnStarted(activePrompt)
|
|
30314
|
+
]);
|
|
30315
|
+
if (turnCompleted === null) {
|
|
30316
|
+
return this.cancelledPromptResponse(sessionState);
|
|
30317
|
+
}
|
|
30318
|
+
await this.codexAcpClient.waitForSessionNotifications(params.sessionId);
|
|
30319
|
+
if (turnCompleted.turn.status === "interrupted") {
|
|
30320
|
+
await eventHandler.flushPendingPlanUpdates();
|
|
30321
|
+
await this.notifyConversationInterrupted(params.sessionId);
|
|
30322
|
+
return this.cancelledPromptResponse(sessionState);
|
|
30323
|
+
}
|
|
30324
|
+
const implementationError = eventHandler.getFailure();
|
|
30325
|
+
if (implementationError) {
|
|
30326
|
+
throw implementationError;
|
|
30327
|
+
}
|
|
30328
|
+
}
|
|
30329
|
+
}
|
|
30014
30330
|
await this.publishFallbackSessionTitle(
|
|
30015
30331
|
sessionState,
|
|
30016
30332
|
this.createPromptFallbackTitle(params.prompt)
|
|
@@ -30025,6 +30341,7 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
30025
30341
|
throw err;
|
|
30026
30342
|
} finally {
|
|
30027
30343
|
logger.log("Prompt completed", { sessionId: params.sessionId });
|
|
30344
|
+
await eventHandler?.dispose();
|
|
30028
30345
|
disposePromptRequestCancellation();
|
|
30029
30346
|
sessionState.currentTurnId = null;
|
|
30030
30347
|
const registeredPendingTurnStart = this.pendingTurnStarts.get(params.sessionId);
|
|
@@ -30035,6 +30352,57 @@ Check ${configPath} and project .codex directories, especially their config.toml
|
|
|
30035
30352
|
activePrompt.complete();
|
|
30036
30353
|
}
|
|
30037
30354
|
}
|
|
30355
|
+
async requestPlanImplementationPermission(sessionState, plan, cancellationSignal) {
|
|
30356
|
+
const toolCallId = `plan-review:${plan.itemId}`;
|
|
30357
|
+
try {
|
|
30358
|
+
const response = await this.connection.request(
|
|
30359
|
+
methods.client.session.requestPermission,
|
|
30360
|
+
{
|
|
30361
|
+
sessionId: sessionState.sessionId,
|
|
30362
|
+
toolCall: {
|
|
30363
|
+
toolCallId,
|
|
30364
|
+
title: "Implement this plan?",
|
|
30365
|
+
kind: "switch_mode",
|
|
30366
|
+
status: "pending",
|
|
30367
|
+
rawInput: { plan: plan.text }
|
|
30368
|
+
},
|
|
30369
|
+
options: [
|
|
30370
|
+
{
|
|
30371
|
+
optionId: IMPLEMENT_PLAN_OPTION_ID,
|
|
30372
|
+
name: "Yes, implement this plan",
|
|
30373
|
+
kind: "allow_once"
|
|
30374
|
+
},
|
|
30375
|
+
{
|
|
30376
|
+
optionId: REVISE_PLAN_OPTION_ID,
|
|
30377
|
+
name: "No, and tell Codex what to do differently",
|
|
30378
|
+
kind: "reject_once"
|
|
30379
|
+
}
|
|
30380
|
+
],
|
|
30381
|
+
_meta: {
|
|
30382
|
+
codex: {
|
|
30383
|
+
kind: "plan_review",
|
|
30384
|
+
planItemId: plan.itemId
|
|
30385
|
+
}
|
|
30386
|
+
}
|
|
30387
|
+
},
|
|
30388
|
+
{ cancellationSignal }
|
|
30389
|
+
);
|
|
30390
|
+
const approved = response.outcome.outcome === "selected" && response.outcome.optionId === IMPLEMENT_PLAN_OPTION_ID;
|
|
30391
|
+
await this.connection.notify(methods.client.session.update, {
|
|
30392
|
+
sessionId: sessionState.sessionId,
|
|
30393
|
+
update: {
|
|
30394
|
+
sessionUpdate: "tool_call_update",
|
|
30395
|
+
toolCallId,
|
|
30396
|
+
status: "completed",
|
|
30397
|
+
rawOutput: approved ? "User approved the plan." : "User kept the session in plan mode."
|
|
30398
|
+
}
|
|
30399
|
+
});
|
|
30400
|
+
return approved;
|
|
30401
|
+
} catch (error51) {
|
|
30402
|
+
logger.error("Error requesting plan implementation permission", error51);
|
|
30403
|
+
return false;
|
|
30404
|
+
}
|
|
30405
|
+
}
|
|
30038
30406
|
cancelledPromptResponse(sessionState) {
|
|
30039
30407
|
return {
|
|
30040
30408
|
stopReason: "cancelled",
|