@agentclientprotocol/codex-acp 1.0.1 → 1.0.2

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.
Files changed (2) hide show
  1. package/dist/index.js +471 -32
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -22544,15 +22544,24 @@ ${event.details}` : "";
22544
22544
  };
22545
22545
  }
22546
22546
  createThreadGoalUpdatedEvent(event) {
22547
+ const goalSnapshot = this.createThreadGoalSnapshot(event);
22548
+ if (this.sameThreadGoalSnapshot(this.sessionState.currentGoal, goalSnapshot)) {
22549
+ return null;
22550
+ }
22551
+ this.sessionState.currentGoal = goalSnapshot;
22547
22552
  const status = this.formatThreadGoalStatus(event.goal.status);
22548
- const objective = event.goal.objective.trim();
22553
+ const objective = goalSnapshot.objective;
22549
22554
  const text = objective.includes("\n") ? `Goal updated (${status}):
22550
22555
  ${objective}` : `Goal updated (${status}): ${objective}`;
22551
22556
  return {
22552
22557
  sessionUpdate: "agent_message_chunk",
22553
22558
  content: {
22554
22559
  type: "text",
22555
- text
22560
+ text: `
22561
+
22562
+ ${text}
22563
+
22564
+ `
22556
22565
  }
22557
22566
  };
22558
22567
  }
@@ -22573,14 +22582,28 @@ ${objective}` : `Goal updated (${status}): ${objective}`;
22573
22582
  }
22574
22583
  }
22575
22584
  createThreadGoalClearedEvent(_event) {
22585
+ if (this.sessionState.currentGoal === null) {
22586
+ return null;
22587
+ }
22588
+ this.sessionState.currentGoal = null;
22576
22589
  return {
22577
22590
  sessionUpdate: "agent_message_chunk",
22578
22591
  content: {
22579
22592
  type: "text",
22580
- text: "Goal cleared."
22593
+ text: "\n\nGoal cleared.\n\n"
22581
22594
  }
22582
22595
  };
22583
22596
  }
22597
+ createThreadGoalSnapshot(event) {
22598
+ return {
22599
+ objective: event.goal.objective.trim(),
22600
+ status: event.goal.status,
22601
+ tokenBudget: event.goal.tokenBudget
22602
+ };
22603
+ }
22604
+ sameThreadGoalSnapshot(left, right) {
22605
+ return left !== null && left !== void 0 && left.objective === right.objective && left.status === right.status && left.tokenBudget === right.tokenBudget;
22606
+ }
22584
22607
  createReasoningDeltaEvent(event) {
22585
22608
  this.seenReasoningDeltaItemIds.add(event.itemId);
22586
22609
  return this.createAgentThoughtEvent(event.delta);
@@ -24259,7 +24282,7 @@ var package_default = {
24259
24282
  publishConfig: {
24260
24283
  access: "public"
24261
24284
  },
24262
- version: "1.0.1",
24285
+ version: "1.0.2",
24263
24286
  description: "",
24264
24287
  main: "dist/index.js",
24265
24288
  bin: {
@@ -24318,7 +24341,7 @@ var package_default = {
24318
24341
  },
24319
24342
  dependencies: {
24320
24343
  "@agentclientprotocol/sdk": "^1.0.0",
24321
- "@openai/codex": "^0.142.2",
24344
+ "@openai/codex": "^0.142.4",
24322
24345
  diff: "^8.0.3",
24323
24346
  open: "^11.0.0",
24324
24347
  "vscode-jsonrpc": "^8.2.1",
@@ -24573,6 +24596,28 @@ var CodexAcpClient = class {
24573
24596
  async runCompact(sessionId) {
24574
24597
  await this.codexClient.runCompact({ threadId: sessionId });
24575
24598
  }
24599
+ async setGoal(sessionId, objective, onTurnStarted) {
24600
+ return await this.codexClient.runGoalSet({
24601
+ threadId: sessionId,
24602
+ objective,
24603
+ status: "active"
24604
+ }, onTurnStarted);
24605
+ }
24606
+ async setGoalStatus(sessionId, status) {
24607
+ await this.codexClient.runGoalSet({
24608
+ threadId: sessionId,
24609
+ status
24610
+ });
24611
+ }
24612
+ async resumeGoal(sessionId, onTurnStarted) {
24613
+ return await this.codexClient.runGoalSet({
24614
+ threadId: sessionId,
24615
+ status: "active"
24616
+ }, onTurnStarted);
24617
+ }
24618
+ async clearGoal(sessionId) {
24619
+ await this.codexClient.runGoalClear({ threadId: sessionId });
24620
+ }
24576
24621
  async awaitMcpServerStartup(serverNames, afterVersion) {
24577
24622
  return await this.codexClient.awaitMcpServerStartup(serverNames, afterVersion);
24578
24623
  }
@@ -25088,22 +25133,27 @@ var CodexCommands = class {
25088
25133
  this.runWithProcessCheck = runWithProcessCheck;
25089
25134
  this.onLogout = onLogout;
25090
25135
  }
25091
- async publish(sessionId) {
25136
+ async publish(sessionState) {
25092
25137
  try {
25093
- const skillsResponse = await this.runWithProcessCheck(() => this.codexAcpClient.listSkills());
25138
+ const skillsResponse = await this.runWithProcessCheck(() => this.codexAcpClient.listSkills(this.createSkillsListParams(sessionState)));
25094
25139
  const availableCommands = this.buildAvailableCommands(skillsResponse?.data ?? []);
25095
25140
  if (availableCommands.length === 0) {
25096
25141
  return;
25097
25142
  }
25098
- const session = new ACPSessionConnection(this.connection, sessionId);
25143
+ const session = new ACPSessionConnection(this.connection, sessionState.sessionId);
25099
25144
  await session.update({
25100
25145
  sessionUpdate: "available_commands_update",
25101
25146
  availableCommands
25102
25147
  });
25103
25148
  } catch (err) {
25104
- logger.error(`Failed to publish available commands for session ${sessionId}`, err);
25149
+ logger.error(`Failed to publish available commands for session ${sessionState.sessionId}`, err);
25105
25150
  }
25106
25151
  }
25152
+ createSkillsListParams(sessionState) {
25153
+ return {
25154
+ cwds: [sessionState.cwd, ...sessionState.additionalDirectories]
25155
+ };
25156
+ }
25107
25157
  buildAvailableCommands(skillsEntries) {
25108
25158
  const commands = /* @__PURE__ */ new Map();
25109
25159
  for (const builtin of this.getBuiltinCommands()) {
@@ -25163,6 +25213,11 @@ var CodexCommands = class {
25163
25213
  description: "Summarize conversation to avoid hitting the context limit.",
25164
25214
  input: null
25165
25215
  },
25216
+ {
25217
+ name: "goal",
25218
+ description: "Set, pause, resume, or clear a task goal.",
25219
+ input: { hint: "[<objective>|clear|pause|resume]" }
25220
+ },
25166
25221
  {
25167
25222
  name: "logout",
25168
25223
  description: "Sign out of Codex. This option is available when you are logged in via ChatGPT.",
@@ -25195,6 +25250,9 @@ var CodexCommands = class {
25195
25250
  await this.runWithProcessCheck(() => this.codexAcpClient.runCompact(sessionId));
25196
25251
  return { handled: true };
25197
25252
  }
25253
+ case "goal": {
25254
+ return await this.runGoalCommand(sessionState, command.rest, options);
25255
+ }
25198
25256
  case "review": {
25199
25257
  const target = this.buildReviewTarget(command.rest);
25200
25258
  const turnCompleted = await this.runReviewCommand(sessionState, target, options);
@@ -25243,7 +25301,7 @@ var CodexCommands = class {
25243
25301
  return { handled: true };
25244
25302
  }
25245
25303
  case "skills": {
25246
- const response = await this.runWithProcessCheck(() => this.codexAcpClient.listSkills());
25304
+ const response = await this.runWithProcessCheck(() => this.codexAcpClient.listSkills(this.createSkillsListParams(sessionState)));
25247
25305
  const skills = (response?.data ?? []).flatMap((entry) => entry.skills);
25248
25306
  const lines = skills.map((skill) => {
25249
25307
  const description = skill.shortDescription ?? skill.description ?? "";
@@ -25280,18 +25338,74 @@ var CodexCommands = class {
25280
25338
  }
25281
25339
  }
25282
25340
  async runReviewCommand(sessionState, target, options) {
25341
+ options.onTurnStartPending?.();
25283
25342
  return await this.runWithProcessCheck(() => this.codexAcpClient.runReview(
25284
25343
  sessionState.sessionId,
25285
25344
  target,
25286
25345
  (turnId, threadId) => {
25287
- if (options.onTurnStarted) {
25288
- options.onTurnStarted(turnId, threadId);
25289
- } else {
25290
- sessionState.currentTurnId = turnId;
25291
- }
25346
+ this.handleCommandTurnStarted(sessionState, options, turnId, threadId);
25292
25347
  }
25293
25348
  ));
25294
25349
  }
25350
+ async runGoalCommand(sessionState, rest, options) {
25351
+ const sessionId = sessionState.sessionId;
25352
+ const argument = rest.trim();
25353
+ if (argument.length === 0) {
25354
+ await this.sendCommandUsageMessage("goal", "[<objective>|clear|pause|resume]", sessionId);
25355
+ return { handled: true };
25356
+ }
25357
+ switch (argument.toLowerCase()) {
25358
+ case "pause":
25359
+ await this.runWithProcessCheck(() => this.codexAcpClient.setGoalStatus(sessionId, "paused"));
25360
+ return { handled: true };
25361
+ case "resume":
25362
+ options.onTurnStartPending?.();
25363
+ return this.createGoalCommandResult(await this.runWithProcessCheck(() => this.codexAcpClient.resumeGoal(
25364
+ sessionId,
25365
+ (turnId) => {
25366
+ this.handleCommandTurnStarted(sessionState, options, turnId, sessionId);
25367
+ }
25368
+ )));
25369
+ case "clear":
25370
+ await this.runWithProcessCheck(() => this.codexAcpClient.clearGoal(sessionId));
25371
+ return { handled: true };
25372
+ }
25373
+ if (argument.length > 4e3) {
25374
+ const session = new ACPSessionConnection(this.connection, sessionId);
25375
+ await session.update({
25376
+ sessionUpdate: "agent_message_chunk",
25377
+ content: {
25378
+ type: "text",
25379
+ text: 'Command "/goal" requires goal text of at most 4000 characters.'
25380
+ }
25381
+ });
25382
+ return { handled: true };
25383
+ }
25384
+ options.onTurnStartPending?.();
25385
+ return this.createGoalCommandResult(await this.runWithProcessCheck(() => this.codexAcpClient.setGoal(
25386
+ sessionId,
25387
+ argument,
25388
+ (turnId) => {
25389
+ this.handleCommandTurnStarted(sessionState, options, turnId, sessionId);
25390
+ }
25391
+ )));
25392
+ }
25393
+ handleCommandTurnStarted(sessionState, options, turnId, threadId) {
25394
+ if (options.onTurnStarted) {
25395
+ options.onTurnStarted(turnId, threadId);
25396
+ } else {
25397
+ sessionState.currentTurnId = turnId;
25398
+ }
25399
+ }
25400
+ createGoalCommandResult(turnCompleted) {
25401
+ if (turnCompleted === null) {
25402
+ return { handled: true };
25403
+ }
25404
+ return {
25405
+ handled: true,
25406
+ turnCompleted
25407
+ };
25408
+ }
25295
25409
  buildReviewTarget(instructions) {
25296
25410
  if (instructions.length === 0) {
25297
25411
  return { type: "uncommittedChanges" };
@@ -26734,7 +26848,7 @@ You have been logged out. Please try again.`);
26734
26848
  });
26735
26849
  this.publishMcpStartupStatusAsync(sessionId);
26736
26850
  }
26737
- this.publishAvailableCommandsAsync(sessionId);
26851
+ this.publishAvailableCommandsAsync(sessionState);
26738
26852
  const sessionModelState = this.createModelState(models, currentModelId);
26739
26853
  const sessionModeState = sessionState.agentMode.toSessionModeState();
26740
26854
  return [sessionId, sessionModelState, sessionModeState];
@@ -27042,7 +27156,9 @@ You have been logged out. Please try again.`);
27042
27156
  createReasoningEffortConfigOption(sessionState.supportedReasoningEfforts, currentModelId.effort)
27043
27157
  );
27044
27158
  }
27045
- configOptions.push(createFastModeConfigOption(sessionState.fastModeEnabled));
27159
+ if (sessionState.currentModelSupportsFast) {
27160
+ configOptions.push(createFastModeConfigOption(sessionState.fastModeEnabled));
27161
+ }
27046
27162
  return configOptions;
27047
27163
  }
27048
27164
  createSessionConfigOptionsResponse(sessionState) {
@@ -27056,8 +27172,8 @@ You have been logged out. Please try again.`);
27056
27172
  isSessionConfigEnabled() {
27057
27173
  return !isJetBrains2026_1Client(this.clientInfo);
27058
27174
  }
27059
- publishAvailableCommandsAsync(sessionId) {
27060
- void this.availableCommands.publish(sessionId);
27175
+ publishAvailableCommandsAsync(sessionState) {
27176
+ void this.availableCommands.publish(sessionState);
27061
27177
  }
27062
27178
  findCurrentModel(models, currentModelId) {
27063
27179
  const modelId = ModelId.fromString(currentModelId);
@@ -27148,7 +27264,7 @@ You have been logged out. Please try again.`);
27148
27264
  });
27149
27265
  this.publishMcpStartupStatusAsync(sessionId);
27150
27266
  }
27151
- await this.availableCommands.publish(sessionId);
27267
+ await this.availableCommands.publish(sessionState);
27152
27268
  const sessionModelState = this.createModelState(models, currentModelId);
27153
27269
  const sessionModeState = sessionState.agentMode.toSessionModeState();
27154
27270
  return {
@@ -27563,6 +27679,13 @@ ${item.text}`
27563
27679
  sessionState.lastTokenUsage = null;
27564
27680
  const activePrompt = this.trackActivePrompt(params.sessionId);
27565
27681
  let pendingTurnStart = null;
27682
+ const ensurePendingTurnStart = () => {
27683
+ if (pendingTurnStart === null) {
27684
+ pendingTurnStart = this.createPendingTurnStart();
27685
+ this.pendingTurnStarts.set(params.sessionId, pendingTurnStart);
27686
+ }
27687
+ return pendingTurnStart;
27688
+ };
27566
27689
  const disposePromptRequestCancellation = this.observePromptRequestCancellation(signal, sessionState, activePrompt);
27567
27690
  try {
27568
27691
  const eventHandler = new CodexEventHandler(this.connection, sessionState);
@@ -27581,6 +27704,9 @@ ${item.text}`
27581
27704
  return this.cancelledPromptResponse(sessionState);
27582
27705
  }
27583
27706
  const commandPromise = this.availableCommands.tryHandleCommand(params.prompt, sessionState, {
27707
+ onTurnStartPending: () => {
27708
+ ensurePendingTurnStart();
27709
+ },
27584
27710
  onTurnStarted: (turnId, threadId) => {
27585
27711
  const turn = { threadId, turnId };
27586
27712
  activePrompt.currentTurn = turn;
@@ -27589,6 +27715,7 @@ ${item.text}`
27589
27715
  return;
27590
27716
  }
27591
27717
  sessionState.currentTurnId = turnId;
27718
+ pendingTurnStart?.resolve(turnId);
27592
27719
  }
27593
27720
  });
27594
27721
  void commandPromise.catch((err) => {
@@ -27641,8 +27768,7 @@ ${item.text}`
27641
27768
  sessionState.fastModeEnabled,
27642
27769
  sessionState.currentModelSupportsFast
27643
27770
  );
27644
- pendingTurnStart = this.createPendingTurnStart();
27645
- this.pendingTurnStarts.set(params.sessionId, pendingTurnStart);
27771
+ ensurePendingTurnStart();
27646
27772
  const sendPromptPromise = this.runWithProcessCheck(
27647
27773
  () => this.codexAcpClient.sendPrompt(
27648
27774
  params,
@@ -27699,10 +27825,11 @@ ${item.text}`
27699
27825
  logger.log("Prompt completed", { sessionId: params.sessionId });
27700
27826
  disposePromptRequestCancellation();
27701
27827
  sessionState.currentTurnId = null;
27702
- if (pendingTurnStart !== null && this.pendingTurnStarts.get(params.sessionId) === pendingTurnStart) {
27828
+ const registeredPendingTurnStart = this.pendingTurnStarts.get(params.sessionId);
27829
+ if (registeredPendingTurnStart !== void 0) {
27703
27830
  this.pendingTurnStarts.delete(params.sessionId);
27831
+ registeredPendingTurnStart.resolve(null);
27704
27832
  }
27705
- pendingTurnStart?.resolve(null);
27706
27833
  activePrompt.complete();
27707
27834
  }
27708
27835
  }
@@ -27834,6 +27961,7 @@ var CommandExecutionApprovalRequest = new import_node2.RequestType("item/command
27834
27961
  var FileChangeApprovalRequest = new import_node2.RequestType("item/fileChange/requestApproval");
27835
27962
  var PermissionsApprovalRequest = new import_node2.RequestType("item/permissions/requestApproval");
27836
27963
  var McpServerElicitationRequest = new import_node2.RequestType("mcpServer/elicitation/request");
27964
+ var GOAL_RUNTIME_EFFECTS_GRACE_MS = 1e3;
27837
27965
  var CodexAppServerClient = class {
27838
27966
  connection;
27839
27967
  approvalHandlers = /* @__PURE__ */ new Map();
@@ -27844,6 +27972,10 @@ var CodexAppServerClient = class {
27844
27972
  pendingTurnCompletionResolvers = /* @__PURE__ */ new Map();
27845
27973
  pendingCompactionCompletionResolvers = /* @__PURE__ */ new Map();
27846
27974
  turnCompletionCaptures = /* @__PURE__ */ new Map();
27975
+ turnRoutingCaptures = /* @__PURE__ */ new Map();
27976
+ threadStatusCaptures = /* @__PURE__ */ new Map();
27977
+ threadGoalUpdateCaptures = /* @__PURE__ */ new Map();
27978
+ threadGoalClearedCaptures = /* @__PURE__ */ new Map();
27847
27979
  staleTurnIds = /* @__PURE__ */ new Map();
27848
27980
  constructor(connection) {
27849
27981
  this.connection = connection;
@@ -27864,15 +27996,21 @@ var CodexAppServerClient = class {
27864
27996
  if (isCompactionCompletedNotification(serverNotification)) {
27865
27997
  this.recordCompactionCompleted(serverNotification);
27866
27998
  }
27999
+ if (isThreadStatusChangedNotification(serverNotification)) {
28000
+ this.recordThreadStatusChanged(serverNotification.params);
28001
+ }
28002
+ if (isThreadGoalUpdatedNotification(serverNotification)) {
28003
+ this.recordThreadGoalUpdated(serverNotification.params);
28004
+ }
28005
+ if (isThreadGoalClearedNotification(serverNotification)) {
28006
+ this.recordThreadGoalCleared(serverNotification.params);
28007
+ }
27867
28008
  const routing = extractTurnRouting(serverNotification);
27868
- const staleTurnNotification = this.isStaleTurn(routing.threadId, routing.turnId);
27869
- if (staleTurnNotification) {
27870
- if (isTurnCompletedNotification(serverNotification) && routing.threadId !== null && routing.turnId !== null) {
27871
- this.clearStaleTurn(routing.threadId, routing.turnId);
27872
- }
27873
- for (const callback of this.codexEventHandlers) {
27874
- callback({ eventType: "notification", ...serverNotification });
27875
- }
28009
+ if (this.handleStaleTurnNotification(serverNotification, routing)) {
28010
+ return;
28011
+ }
28012
+ this.recordTurnRouting(routing);
28013
+ if (this.handleStaleTurnNotification(serverNotification, routing)) {
27876
28014
  return;
27877
28015
  }
27878
28016
  this.notify(serverNotification);
@@ -27974,6 +28112,174 @@ var CodexAppServerClient = class {
27974
28112
  releaseCapture();
27975
28113
  }
27976
28114
  }
28115
+ async runGoalSet(params, onTurnStarted, runtimeEffectsGraceMs = GOAL_RUNTIME_EFFECTS_GRACE_MS) {
28116
+ let goalTurnId = null;
28117
+ const capturedCompletions = [];
28118
+ let resolveGoalTurnCompleted = () => {
28119
+ };
28120
+ const goalTurnCompleted = new Promise((resolve) => {
28121
+ resolveGoalTurnCompleted = resolve;
28122
+ });
28123
+ const releaseCompletionCapture = this.captureTurnCompletions(params.threadId, (event) => {
28124
+ capturedCompletions.push(event);
28125
+ if (goalTurnId === event.turn.id) {
28126
+ resolveGoalTurnCompleted(event);
28127
+ }
28128
+ });
28129
+ let resolveGoalTurnStarted = () => {
28130
+ };
28131
+ const goalTurnStarted = new Promise((resolve) => {
28132
+ resolveGoalTurnStarted = resolve;
28133
+ });
28134
+ let resolveGoalUpdateHandled = () => {
28135
+ };
28136
+ const matchingGoalUpdateHandled = new Promise((resolve) => {
28137
+ resolveGoalUpdateHandled = () => resolve(null);
28138
+ });
28139
+ let goalUpdateHandled = false;
28140
+ let expectedGoal = null;
28141
+ const noGoalTurnStarted = this.createNoGoalTurnStartedPromise(runtimeEffectsGraceMs);
28142
+ const capturedGoalUpdates = [];
28143
+ const releaseRoutingCapture = this.captureTurnRoutings(params.threadId, (turnId) => {
28144
+ if (!goalUpdateHandled || goalTurnId !== null) {
28145
+ return;
28146
+ }
28147
+ goalTurnId = turnId;
28148
+ onTurnStarted?.(turnId);
28149
+ resolveGoalTurnStarted(turnId);
28150
+ });
28151
+ const releaseGoalUpdateCapture = this.captureThreadGoalUpdates(params.threadId, (event) => {
28152
+ capturedGoalUpdates.push(event);
28153
+ if (expectedGoal !== null && goalsMatch(event.goal, expectedGoal)) {
28154
+ goalUpdateHandled = true;
28155
+ resolveGoalUpdateHandled();
28156
+ noGoalTurnStarted.goalUpdated();
28157
+ }
28158
+ });
28159
+ const releaseStatusCapture = this.captureThreadStatuses(params.threadId, (status) => {
28160
+ if (!goalUpdateHandled || goalTurnId !== null) {
28161
+ return;
28162
+ }
28163
+ noGoalTurnStarted.threadStatusChanged(status);
28164
+ });
28165
+ try {
28166
+ const goalSetResponse = await this.threadGoalSet(params);
28167
+ expectedGoal = goalSetResponse.goal;
28168
+ if (capturedGoalUpdates.some((event) => goalsMatch(event.goal, expectedGoal))) {
28169
+ goalUpdateHandled = true;
28170
+ resolveGoalUpdateHandled();
28171
+ noGoalTurnStarted.goalUpdated();
28172
+ }
28173
+ if (expectedGoal.status !== "active") {
28174
+ await matchingGoalUpdateHandled;
28175
+ return null;
28176
+ }
28177
+ const turnId = goalTurnId ?? await Promise.race([goalTurnStarted, noGoalTurnStarted.promise]);
28178
+ noGoalTurnStarted.release();
28179
+ releaseRoutingCapture();
28180
+ releaseStatusCapture();
28181
+ releaseGoalUpdateCapture();
28182
+ if (turnId === null) {
28183
+ return null;
28184
+ }
28185
+ const earlyCompletion = capturedCompletions.find((event) => event.turn.id === turnId);
28186
+ if (earlyCompletion) {
28187
+ return earlyCompletion;
28188
+ }
28189
+ return await goalTurnCompleted;
28190
+ } finally {
28191
+ noGoalTurnStarted.release();
28192
+ releaseCompletionCapture();
28193
+ releaseRoutingCapture();
28194
+ releaseStatusCapture();
28195
+ releaseGoalUpdateCapture();
28196
+ }
28197
+ }
28198
+ async runGoalClear(params) {
28199
+ let goalClearedHandled = false;
28200
+ let resolveGoalClearedHandled = () => {
28201
+ };
28202
+ const matchingGoalClearedHandled = new Promise((resolve) => {
28203
+ resolveGoalClearedHandled = () => resolve();
28204
+ });
28205
+ const releaseGoalClearedCapture = this.captureThreadGoalClears(params.threadId, () => {
28206
+ goalClearedHandled = true;
28207
+ resolveGoalClearedHandled();
28208
+ });
28209
+ try {
28210
+ const response = await this.threadGoalClear(params);
28211
+ if (!response.cleared || goalClearedHandled) {
28212
+ return;
28213
+ }
28214
+ await matchingGoalClearedHandled;
28215
+ } finally {
28216
+ releaseGoalClearedCapture();
28217
+ }
28218
+ }
28219
+ createNoGoalTurnStartedPromise(runtimeEffectsGraceMs) {
28220
+ let released = false;
28221
+ let resolved = false;
28222
+ let goalUpdated = false;
28223
+ let activeAfterGoalUpdate = false;
28224
+ let timeout = null;
28225
+ let resolveNoGoalTurnStarted = () => {
28226
+ };
28227
+ const clearTimer = () => {
28228
+ if (timeout !== null) {
28229
+ clearTimeout(timeout);
28230
+ timeout = null;
28231
+ }
28232
+ };
28233
+ const resolveNoTurn = () => {
28234
+ if (released || resolved) {
28235
+ return;
28236
+ }
28237
+ resolved = true;
28238
+ clearTimer();
28239
+ resolveNoGoalTurnStarted();
28240
+ };
28241
+ const scheduleNoTurnTimer = () => {
28242
+ if (released || resolved || !goalUpdated || activeAfterGoalUpdate || timeout !== null) {
28243
+ return;
28244
+ }
28245
+ timeout = setTimeout(resolveNoTurn, runtimeEffectsGraceMs);
28246
+ };
28247
+ const release = () => {
28248
+ if (released) {
28249
+ return;
28250
+ }
28251
+ released = true;
28252
+ clearTimer();
28253
+ };
28254
+ const promise2 = new Promise((resolve) => {
28255
+ resolveNoGoalTurnStarted = () => {
28256
+ resolve(null);
28257
+ };
28258
+ });
28259
+ const handleGoalUpdated = () => {
28260
+ goalUpdated = true;
28261
+ scheduleNoTurnTimer();
28262
+ };
28263
+ const handleThreadStatusChanged = (status) => {
28264
+ if (!goalUpdated || released || resolved) {
28265
+ return;
28266
+ }
28267
+ if (status.type === "active") {
28268
+ activeAfterGoalUpdate = true;
28269
+ clearTimer();
28270
+ return;
28271
+ }
28272
+ if (activeAfterGoalUpdate) {
28273
+ resolveNoTurn();
28274
+ }
28275
+ };
28276
+ return {
28277
+ promise: promise2,
28278
+ release,
28279
+ goalUpdated: handleGoalUpdated,
28280
+ threadStatusChanged: handleThreadStatusChanged
28281
+ };
28282
+ }
27977
28283
  async runCompact(params) {
27978
28284
  const compactionCompleted = this.awaitCompactionCompleted(params.threadId);
27979
28285
  await this.threadCompactStart(params);
@@ -28014,6 +28320,12 @@ var CodexAppServerClient = class {
28014
28320
  async threadCompactStart(params) {
28015
28321
  return await this.sendRequest({ method: "thread/compact/start", params });
28016
28322
  }
28323
+ async threadGoalSet(params) {
28324
+ return await this.sendRequest({ method: "thread/goal/set", params });
28325
+ }
28326
+ async threadGoalClear(params) {
28327
+ return await this.sendRequest({ method: "thread/goal/clear", params });
28328
+ }
28017
28329
  async listMcpServerStatus(params) {
28018
28330
  return await this.sendRequest({ method: "mcpServerStatus/list", params });
28019
28331
  }
@@ -28145,6 +28457,57 @@ var CodexAppServerClient = class {
28145
28457
  resolve(event);
28146
28458
  }
28147
28459
  }
28460
+ recordThreadStatusChanged(event) {
28461
+ const captures = this.threadStatusCaptures.get(event.threadId);
28462
+ if (!captures) {
28463
+ return;
28464
+ }
28465
+ for (const capture of captures) {
28466
+ capture(event.status);
28467
+ }
28468
+ }
28469
+ recordThreadGoalUpdated(event) {
28470
+ const captures = this.threadGoalUpdateCaptures.get(event.threadId);
28471
+ if (!captures) {
28472
+ return;
28473
+ }
28474
+ for (const capture of captures) {
28475
+ capture(event);
28476
+ }
28477
+ }
28478
+ recordThreadGoalCleared(event) {
28479
+ const captures = this.threadGoalClearedCaptures.get(event.threadId);
28480
+ if (!captures) {
28481
+ return;
28482
+ }
28483
+ for (const capture of captures) {
28484
+ capture();
28485
+ }
28486
+ }
28487
+ recordTurnRouting(routing) {
28488
+ if (routing.threadId === null || routing.turnId === null) {
28489
+ return;
28490
+ }
28491
+ const captures = this.turnRoutingCaptures.get(routing.threadId);
28492
+ if (!captures) {
28493
+ return;
28494
+ }
28495
+ for (const capture of captures) {
28496
+ capture(routing.turnId);
28497
+ }
28498
+ }
28499
+ handleStaleTurnNotification(notification, routing) {
28500
+ if (!this.isStaleTurn(routing.threadId, routing.turnId)) {
28501
+ return false;
28502
+ }
28503
+ if (isTurnCompletedNotification(notification) && routing.threadId !== null && routing.turnId !== null) {
28504
+ this.clearStaleTurn(routing.threadId, routing.turnId);
28505
+ }
28506
+ for (const callback of this.codexEventHandlers) {
28507
+ callback({ eventType: "notification", ...notification });
28508
+ }
28509
+ return true;
28510
+ }
28148
28511
  isStaleTurn(threadId, turnId) {
28149
28512
  if (threadId === null || turnId === null) {
28150
28513
  return false;
@@ -28186,6 +28549,70 @@ var CodexAppServerClient = class {
28186
28549
  }
28187
28550
  };
28188
28551
  }
28552
+ captureTurnRoutings(threadId, capture) {
28553
+ const captures = this.turnRoutingCaptures.get(threadId) ?? /* @__PURE__ */ new Set();
28554
+ captures.add(capture);
28555
+ this.turnRoutingCaptures.set(threadId, captures);
28556
+ let released = false;
28557
+ return () => {
28558
+ if (released) {
28559
+ return;
28560
+ }
28561
+ released = true;
28562
+ captures.delete(capture);
28563
+ if (captures.size === 0) {
28564
+ this.turnRoutingCaptures.delete(threadId);
28565
+ }
28566
+ };
28567
+ }
28568
+ captureThreadStatuses(threadId, capture) {
28569
+ const captures = this.threadStatusCaptures.get(threadId) ?? /* @__PURE__ */ new Set();
28570
+ captures.add(capture);
28571
+ this.threadStatusCaptures.set(threadId, captures);
28572
+ let released = false;
28573
+ return () => {
28574
+ if (released) {
28575
+ return;
28576
+ }
28577
+ released = true;
28578
+ captures.delete(capture);
28579
+ if (captures.size === 0) {
28580
+ this.threadStatusCaptures.delete(threadId);
28581
+ }
28582
+ };
28583
+ }
28584
+ captureThreadGoalUpdates(threadId, capture) {
28585
+ const captures = this.threadGoalUpdateCaptures.get(threadId) ?? /* @__PURE__ */ new Set();
28586
+ captures.add(capture);
28587
+ this.threadGoalUpdateCaptures.set(threadId, captures);
28588
+ let released = false;
28589
+ return () => {
28590
+ if (released) {
28591
+ return;
28592
+ }
28593
+ released = true;
28594
+ captures.delete(capture);
28595
+ if (captures.size === 0) {
28596
+ this.threadGoalUpdateCaptures.delete(threadId);
28597
+ }
28598
+ };
28599
+ }
28600
+ captureThreadGoalClears(threadId, capture) {
28601
+ const captures = this.threadGoalClearedCaptures.get(threadId) ?? /* @__PURE__ */ new Set();
28602
+ captures.add(capture);
28603
+ this.threadGoalClearedCaptures.set(threadId, captures);
28604
+ let released = false;
28605
+ return () => {
28606
+ if (released) {
28607
+ return;
28608
+ }
28609
+ released = true;
28610
+ captures.delete(capture);
28611
+ if (captures.size === 0) {
28612
+ this.threadGoalClearedCaptures.delete(threadId);
28613
+ }
28614
+ };
28615
+ }
28189
28616
  resolveMcpServerStartupResolvers() {
28190
28617
  const pendingResolvers = [];
28191
28618
  for (const resolver of this.mcpServerStartupResolvers) {
@@ -28248,12 +28675,24 @@ function isMcpServerStatusUpdatedNotification(notification) {
28248
28675
  function isTurnCompletedNotification(notification) {
28249
28676
  return notification.method === "turn/completed";
28250
28677
  }
28678
+ function isThreadStatusChangedNotification(notification) {
28679
+ return notification.method === "thread/status/changed";
28680
+ }
28681
+ function isThreadGoalUpdatedNotification(notification) {
28682
+ return notification.method === "thread/goal/updated";
28683
+ }
28684
+ function isThreadGoalClearedNotification(notification) {
28685
+ return notification.method === "thread/goal/cleared";
28686
+ }
28251
28687
  function isCompactionCompletedNotification(notification) {
28252
28688
  if (notification.method === "thread/compacted") {
28253
28689
  return true;
28254
28690
  }
28255
28691
  return notification.method === "item/completed" && notification.params.item.type === "contextCompaction";
28256
28692
  }
28693
+ function goalsMatch(left, right) {
28694
+ return left.threadId === right.threadId && left.objective === right.objective && left.status === right.status && left.tokenBudget === right.tokenBudget && left.updatedAt === right.updatedAt;
28695
+ }
28257
28696
  function extractThreadId(notification) {
28258
28697
  const params = notification.params;
28259
28698
  if (params && typeof params.threadId === "string") {
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.0.1",
6
+ "version": "1.0.2",
7
7
  "description": "",
8
8
  "main": "dist/index.js",
9
9
  "bin": {
@@ -62,7 +62,7 @@
62
62
  },
63
63
  "dependencies": {
64
64
  "@agentclientprotocol/sdk": "^1.0.0",
65
- "@openai/codex": "^0.142.2",
65
+ "@openai/codex": "^0.142.4",
66
66
  "diff": "^8.0.3",
67
67
  "open": "^11.0.0",
68
68
  "vscode-jsonrpc": "^8.2.1",