@agentclientprotocol/codex-acp 1.0.0 → 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 (3) hide show
  1. package/README.md +5 -2
  2. package/dist/index.js +1056 -237
  3. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -3343,6 +3343,9 @@ var CLIENT_METHODS = {
3343
3343
  elicitation_create: "elicitation/create",
3344
3344
  elicitation_complete: "elicitation/complete"
3345
3345
  };
3346
+ var PROTOCOL_METHODS = {
3347
+ cancel_request: "$/cancel_request"
3348
+ };
3346
3349
  var PROTOCOL_VERSION = 1;
3347
3350
 
3348
3351
  // node_modules/zod/v4/classic/external.js
@@ -18935,6 +18938,17 @@ var zFileSystemCapabilities = object({
18935
18938
  writeTextFile: boolean2().optional().default(false),
18936
18939
  _meta: record(string2(), unknown()).nullish()
18937
18940
  });
18941
+ var zBooleanConfigOptionCapabilities = object({
18942
+ _meta: record(string2(), unknown()).nullish()
18943
+ });
18944
+ var zSessionConfigOptionsCapabilities = object({
18945
+ boolean: defaultOnError(zBooleanConfigOptionCapabilities.nullish(), () => void 0),
18946
+ _meta: record(string2(), unknown()).nullish()
18947
+ });
18948
+ var zClientSessionCapabilities = object({
18949
+ configOptions: defaultOnError(zSessionConfigOptionsCapabilities.nullish(), () => void 0),
18950
+ _meta: record(string2(), unknown()).nullish()
18951
+ });
18938
18952
  var zPlanCapabilities = object({
18939
18953
  _meta: record(string2(), unknown()).nullish()
18940
18954
  });
@@ -18971,6 +18985,7 @@ var zClientNesCapabilities = object({
18971
18985
  var zClientCapabilities = object({
18972
18986
  fs: zFileSystemCapabilities.optional().default({ readTextFile: false, writeTextFile: false }),
18973
18987
  terminal: boolean2().optional().default(false),
18988
+ session: defaultOnError(zClientSessionCapabilities.nullish(), () => void 0),
18974
18989
  plan: defaultOnError(zPlanCapabilities.nullish(), () => void 0),
18975
18990
  auth: zAuthCapabilities.optional().default({ terminal: false }),
18976
18991
  elicitation: defaultOnError(zElicitationCapabilities.nullish(), () => void 0),
@@ -19515,6 +19530,19 @@ function ndJsonStream(output, input) {
19515
19530
  }
19516
19531
 
19517
19532
  // node_modules/@agentclientprotocol/sdk/dist/jsonrpc.js
19533
+ var CANCEL_REQUEST_METHOD = "$/cancel_request";
19534
+ function isRecord(value) {
19535
+ return typeof value === "object" && value !== null;
19536
+ }
19537
+ function isJsonRpcId(value) {
19538
+ return value === null || typeof value === "string" || typeof value === "number" && Number.isFinite(value);
19539
+ }
19540
+ function cancelRequestId(params) {
19541
+ if (!isRecord(params) || !isJsonRpcId(params["requestId"])) {
19542
+ return void 0;
19543
+ }
19544
+ return params["requestId"];
19545
+ }
19518
19546
  var Handled = {
19519
19547
  /**
19520
19548
  * Marks a message as handled.
@@ -19561,13 +19589,40 @@ function errorToResult(error51) {
19561
19589
  return RequestError.internalError({ details }).toResult();
19562
19590
  }
19563
19591
  }
19592
+ function requestCancelledError(reason) {
19593
+ if (reason instanceof RequestError && reason.code === -32800) {
19594
+ return reason;
19595
+ }
19596
+ return RequestError.requestCancelled(reason);
19597
+ }
19598
+ function errorToRequestResult(error51, signal) {
19599
+ const requestCancelled = abortErrorToRequestCancelled(error51, signal);
19600
+ return requestCancelled ? requestCancelled.toResult() : errorToResult(error51);
19601
+ }
19602
+ function abortErrorToRequestCancelled(error51, signal) {
19603
+ if (!signal.aborted || !isAbortError(error51)) {
19604
+ return void 0;
19605
+ }
19606
+ return requestCancelledError(signal.reason);
19607
+ }
19608
+ function isAbortError(error51) {
19609
+ if (typeof error51 !== "object" || error51 === null) {
19610
+ return false;
19611
+ }
19612
+ const maybeAbortError = error51;
19613
+ return maybeAbortError.name === "AbortError" || maybeAbortError.code === "ABORT_ERR";
19614
+ }
19564
19615
  var RequestResponder = class {
19565
19616
  id;
19566
19617
  sendResult;
19618
+ signal;
19619
+ finishRequest;
19567
19620
  didRespond = false;
19568
- constructor(id, sendResult) {
19621
+ constructor(id, sendResult, signal = new AbortController().signal, finishRequest) {
19569
19622
  this.id = id;
19570
19623
  this.sendResult = sendResult;
19624
+ this.signal = signal;
19625
+ this.finishRequest = finishRequest;
19571
19626
  }
19572
19627
  /**
19573
19628
  * Whether this request has already received a response.
@@ -19596,7 +19651,9 @@ var RequestResponder = class {
19596
19651
  return rejectedPromise(new Error("JSON-RPC request already responded"));
19597
19652
  }
19598
19653
  this.didRespond = true;
19599
- return this.sendResult(result);
19654
+ return this.sendResult(result).finally(() => {
19655
+ this.finishRequest?.();
19656
+ });
19600
19657
  }
19601
19658
  };
19602
19659
  var HandlerRegistration = class {
@@ -19636,8 +19693,8 @@ var ConnectionContext = class {
19636
19693
  /**
19637
19694
  * Sends a request over the connection.
19638
19695
  */
19639
- sendRequest(method, params, mapResponse) {
19640
- return this.connection.sendRequest(method, params, mapResponse);
19696
+ sendRequest(method, params, mapResponse, options) {
19697
+ return this.connection.sendRequest(method, params, mapResponse, options);
19641
19698
  }
19642
19699
  /**
19643
19700
  * Sends a notification over the connection.
@@ -19645,6 +19702,12 @@ var ConnectionContext = class {
19645
19702
  sendNotification(method, params) {
19646
19703
  return this.connection.sendNotification(method, params);
19647
19704
  }
19705
+ /**
19706
+ * Sends a protocol-level request cancellation notification.
19707
+ */
19708
+ sendCancelRequest(requestId) {
19709
+ return this.connection.sendCancelRequest(requestId);
19710
+ }
19648
19711
  /**
19649
19712
  * Registers a handler that can be disposed independently.
19650
19713
  */
@@ -19666,6 +19729,7 @@ var ConnectionContext = class {
19666
19729
  };
19667
19730
  var Connection = class {
19668
19731
  pendingResponses = /* @__PURE__ */ new Map();
19732
+ incomingRequests = /* @__PURE__ */ new Map();
19669
19733
  nextRequestId = 0;
19670
19734
  staticHandlers = [];
19671
19735
  dynamicHandlers = /* @__PURE__ */ new Set();
@@ -19763,13 +19827,15 @@ var Connection = class {
19763
19827
  * `mapResponse` can convert the raw result before the returned promise
19764
19828
  * resolves.
19765
19829
  */
19766
- sendRequest(method, params, mapResponse) {
19830
+ sendRequest(method, params, mapResponse, options = {}) {
19767
19831
  if (this.abortController.signal.aborted) {
19768
19832
  return rejectedPromise(this.closedReason());
19769
19833
  }
19770
19834
  const id = this.nextRequestId++;
19835
+ let cancel = () => {
19836
+ };
19771
19837
  const responsePromise = new Promise((resolve, reject) => {
19772
- this.pendingResponses.set(id, {
19838
+ const pendingResponse = {
19773
19839
  resolve: (response) => {
19774
19840
  try {
19775
19841
  const value = mapResponse ? mapResponse(response) : response;
@@ -19779,14 +19845,45 @@ var Connection = class {
19779
19845
  }
19780
19846
  },
19781
19847
  reject
19848
+ };
19849
+ cancel = () => {
19850
+ if (pendingResponse.cancellationSent) {
19851
+ return;
19852
+ }
19853
+ pendingResponse.cancellationSent = true;
19854
+ pendingResponse.cleanup?.();
19855
+ void this.sendCancelRequest(id).catch(() => {
19856
+ });
19857
+ };
19858
+ options.cancellationSignal?.addEventListener("abort", cancel, {
19859
+ once: true
19782
19860
  });
19861
+ pendingResponse.cleanup = () => {
19862
+ options.cancellationSignal?.removeEventListener("abort", cancel);
19863
+ };
19864
+ this.pendingResponses.set(id, pendingResponse);
19783
19865
  });
19784
19866
  responsePromise.catch(() => {
19785
19867
  });
19786
- void this.sendMessage({ jsonrpc: "2.0", id, method, params }).catch(() => {
19868
+ const requestSent = this.sendMessage({
19869
+ jsonrpc: "2.0",
19870
+ id,
19871
+ method,
19872
+ params
19787
19873
  });
19874
+ void requestSent.catch(() => {
19875
+ });
19876
+ if (options.cancellationSignal?.aborted) {
19877
+ cancel();
19878
+ }
19788
19879
  return responsePromise;
19789
19880
  }
19881
+ /**
19882
+ * Sends a protocol-level request cancellation notification.
19883
+ */
19884
+ sendCancelRequest(requestId) {
19885
+ return this.sendNotification(CANCEL_REQUEST_METHOD, { requestId });
19886
+ }
19790
19887
  /**
19791
19888
  * Sends a JSON-RPC notification.
19792
19889
  */
@@ -19804,11 +19901,16 @@ var Connection = class {
19804
19901
  return;
19805
19902
  }
19806
19903
  const closeError = error51 ?? new Error("ACP connection closed");
19904
+ this.abortController.abort(closeError);
19807
19905
  for (const pendingResponse of this.pendingResponses.values()) {
19906
+ pendingResponse.cleanup?.();
19808
19907
  pendingResponse.reject(closeError);
19809
19908
  }
19810
19909
  this.pendingResponses.clear();
19811
- this.abortController.abort(closeError);
19910
+ for (const controller of this.incomingRequests.values()) {
19911
+ controller.abort(closeError);
19912
+ }
19913
+ this.incomingRequests.clear();
19812
19914
  void this.receiveReader?.cancel(closeError).catch(() => {
19813
19915
  });
19814
19916
  }
@@ -19869,6 +19971,9 @@ var Connection = class {
19869
19971
  return;
19870
19972
  }
19871
19973
  if ("method" in message) {
19974
+ if (!("id" in message)) {
19975
+ this.handleProtocolNotification(message);
19976
+ }
19872
19977
  void this.processIncomingMessage(this.toIncomingMessage(message)).catch((error51) => this.close(error51));
19873
19978
  } else if ("id" in message) {
19874
19979
  this.handleResponse(message);
@@ -19909,7 +20014,7 @@ var Connection = class {
19909
20014
  return;
19910
20015
  }
19911
20016
  if (current.kind === "request" && !current.responder.responded) {
19912
- await current.responder.respondWithResult(errorToResult(error51));
20017
+ await current.responder.respondWithResult(errorToRequestResult(error51, current.responder.signal));
19913
20018
  } else {
19914
20019
  const response = errorToResult(error51);
19915
20020
  if ("error" in response) {
@@ -19920,16 +20025,24 @@ var Connection = class {
19920
20025
  }
19921
20026
  toIncomingMessage(message) {
19922
20027
  if ("id" in message) {
20028
+ const abortController = new AbortController();
20029
+ this.incomingRequests.set(message.id, abortController);
20030
+ const finishRequest = () => {
20031
+ if (this.incomingRequests.get(message.id) === abortController) {
20032
+ this.incomingRequests.delete(message.id);
20033
+ }
20034
+ };
19923
20035
  return {
19924
20036
  kind: "request",
19925
20037
  method: message.method,
19926
20038
  params: message.params,
19927
20039
  raw: message,
20040
+ signal: abortController.signal,
19928
20041
  responder: new RequestResponder(message.id, (result) => this.sendMessage({
19929
20042
  jsonrpc: "2.0",
19930
20043
  id: message.id,
19931
20044
  ...result
19932
- }))
20045
+ }), abortController.signal, finishRequest)
19933
20046
  };
19934
20047
  }
19935
20048
  return {
@@ -19942,6 +20055,8 @@ var Connection = class {
19942
20055
  handleResponse(response) {
19943
20056
  const pendingResponse = this.pendingResponses.get(response.id);
19944
20057
  if (pendingResponse) {
20058
+ this.pendingResponses.delete(response.id);
20059
+ pendingResponse.cleanup?.();
19945
20060
  if ("result" in response) {
19946
20061
  pendingResponse.resolve(response.result);
19947
20062
  } else if ("error" in response) {
@@ -19950,11 +20065,24 @@ var Connection = class {
19950
20065
  } else {
19951
20066
  pendingResponse.reject(RequestError.invalidRequest(response));
19952
20067
  }
19953
- this.pendingResponses.delete(response.id);
19954
20068
  } else {
19955
20069
  console.error("Got response to unknown request", response.id);
19956
20070
  }
19957
20071
  }
20072
+ handleProtocolNotification(message) {
20073
+ if (message.method !== CANCEL_REQUEST_METHOD) {
20074
+ return;
20075
+ }
20076
+ const requestId = cancelRequestId(message.params);
20077
+ if (requestId === void 0) {
20078
+ return;
20079
+ }
20080
+ const controller = this.incomingRequests.get(requestId);
20081
+ if (!controller || controller.signal.aborted) {
20082
+ return;
20083
+ }
20084
+ controller.abort(RequestError.requestCancelled({ requestId }));
20085
+ }
19958
20086
  closedReason() {
19959
20087
  return this.abortController.signal.reason ?? new Error("ACP connection closed");
19960
20088
  }
@@ -20095,6 +20223,12 @@ var RequestError = class _RequestError extends Error {
20095
20223
  static internalError(data, additionalMessage) {
20096
20224
  return new _RequestError(-32603, `Internal error${additionalMessage ? `: ${additionalMessage}` : ""}`, data);
20097
20225
  }
20226
+ /**
20227
+ * Execution of the request was aborted.
20228
+ */
20229
+ static requestCancelled(data, additionalMessage) {
20230
+ return new _RequestError(-32800, `Request cancelled${additionalMessage ? `: ${additionalMessage}` : ""}`, data);
20231
+ }
20098
20232
  /**
20099
20233
  * Authentication required.
20100
20234
  */
@@ -20210,6 +20344,9 @@ var methods = {
20210
20344
  create: CLIENT_METHODS.elicitation_create,
20211
20345
  complete: CLIENT_METHODS.elicitation_complete
20212
20346
  }
20347
+ },
20348
+ protocol: {
20349
+ cancelRequest: PROTOCOL_METHODS.cancel_request
20213
20350
  }
20214
20351
  };
20215
20352
  var startActiveSession = /* @__PURE__ */ Symbol("startActiveSession");
@@ -20224,8 +20361,8 @@ var AcpContext = class {
20224
20361
  return this.cx;
20225
20362
  }
20226
20363
  /** @internal */
20227
- sendRequest(method, params, mapResponse) {
20228
- return this.cx.sendRequest(method, params, mapResponse);
20364
+ sendRequest(method, params, mapResponse, options) {
20365
+ return this.cx.sendRequest(method, params, mapResponse, options);
20229
20366
  }
20230
20367
  /** @internal */
20231
20368
  sendNotification(method, params) {
@@ -20244,9 +20381,9 @@ var AgentContext = class _AgentContext extends AcpContext {
20244
20381
  static create(cx) {
20245
20382
  return new _AgentContext(cx);
20246
20383
  }
20247
- request(method, params) {
20384
+ request(method, params, options) {
20248
20385
  const spec = clientRequestSpecsByMethod[method];
20249
- return this.sendRequest(method, params, spec?.mapResponse);
20386
+ return this.sendRequest(method, params, spec?.mapResponse, options);
20250
20387
  }
20251
20388
  notify(method, params) {
20252
20389
  return this.sendNotification(method, params);
@@ -20261,8 +20398,8 @@ var ClientContext = class _ClientContext extends AcpContext {
20261
20398
  return new _ClientContext(cx);
20262
20399
  }
20263
20400
  /** @internal */
20264
- [startActiveSession](params) {
20265
- return this.sendRequest(AGENT_METHODS.session_new, params, (response) => this.attachSession(response));
20401
+ [startActiveSession](params, options) {
20402
+ return this.sendRequest(AGENT_METHODS.session_new, params, (response) => this.attachSession(response), options);
20266
20403
  }
20267
20404
  buildSession(cwdOrRequest) {
20268
20405
  if (typeof cwdOrRequest === "string") {
@@ -20296,9 +20433,9 @@ var ClientContext = class _ClientContext extends AcpContext {
20296
20433
  closeRegistration
20297
20434
  ]);
20298
20435
  }
20299
- request(method, params) {
20436
+ request(method, params, options) {
20300
20437
  const spec = agentRequestSpecsByMethod[method];
20301
- return this.sendRequest(method, params, spec?.mapResponse);
20438
+ return this.sendRequest(method, params, spec?.mapResponse, options);
20302
20439
  }
20303
20440
  notify(method, params) {
20304
20441
  return this.sendNotification(method, params);
@@ -20475,8 +20612,8 @@ var SessionBuilder = class _SessionBuilder {
20475
20612
  * Call `dispose()` on the returned session when you no longer need update
20476
20613
  * routing, or use `withSession(...)` to scope disposal automatically.
20477
20614
  */
20478
- async start() {
20479
- return this.cx[startActiveSession](this.toRequest());
20615
+ async start(options) {
20616
+ return this.cx[startActiveSession](this.toRequest(), options);
20480
20617
  }
20481
20618
  /**
20482
20619
  * Starts the session, runs `op`, and disposes the active-session update
@@ -20538,12 +20675,12 @@ var ActiveSession = class _ActiveSession {
20538
20675
  * `PromptResponse`, and the same completion is also queued as a `stop`
20539
20676
  * message for `nextUpdate()`.
20540
20677
  */
20541
- prompt(prompt) {
20678
+ prompt(prompt, options) {
20542
20679
  this.updates.clearErrors();
20543
20680
  const response = this.cx.request(AGENT_METHODS.session_prompt, {
20544
20681
  sessionId: this.sessionId,
20545
20682
  prompt: this.promptBlocks(prompt)
20546
- });
20683
+ }, options);
20547
20684
  void response.then((value) => {
20548
20685
  this.updates.enqueue({
20549
20686
  kind: "stop",
@@ -20627,12 +20764,12 @@ function notificationSpec(method, params) {
20627
20764
  }
20628
20765
  function registerAppRequest(builder, spec, context, handler) {
20629
20766
  builder.onReceiveRequest(spec.method, (params) => parseParams(spec.params, params), async (params, responder, cx) => {
20630
- const response = await handler(context(params, cx));
20767
+ const response = await handler(context(params, cx, responder.signal));
20631
20768
  await responder.respond(spec.mapResponse ? spec.mapResponse(response) : response);
20632
20769
  });
20633
20770
  }
20634
20771
  function registerAppNotification(builder, spec, context, handler) {
20635
- builder.onReceiveNotification(spec.method, (params) => parseParams(spec.params, params), (params, cx) => handler(context(params, cx)));
20772
+ builder.onReceiveNotification(spec.method, (params) => parseParams(spec.params, params), (params, cx) => handler(context(params, cx, cx.signal)));
20636
20773
  }
20637
20774
  function specsByMethod(specs) {
20638
20775
  const byMethod = {};
@@ -20691,15 +20828,17 @@ var agentRequestSpecsByMethod = specsByMethod(agentRequestSpecs);
20691
20828
  var agentNotificationSpecsByMethod = specsByMethod(agentNotificationSpecs);
20692
20829
  var clientRequestSpecsByMethod = specsByMethod(clientRequestSpecs);
20693
20830
  var clientNotificationSpecsByMethod = specsByMethod(clientNotificationSpecs);
20694
- function agentHandlerContext(params, client) {
20831
+ function agentHandlerContext(params, client, signal) {
20695
20832
  return {
20696
20833
  params,
20834
+ signal,
20697
20835
  client
20698
20836
  };
20699
20837
  }
20700
- function clientHandlerContext(params, agent2) {
20838
+ function clientHandlerContext(params, agent2, signal) {
20701
20839
  return {
20702
20840
  params,
20841
+ signal,
20703
20842
  agent: agent2
20704
20843
  };
20705
20844
  }
@@ -20818,11 +20957,11 @@ var AgentApp = class {
20818
20957
  return this.notification(spec, handlerOrParams);
20819
20958
  }
20820
20959
  request(spec, handler) {
20821
- registerAppRequest(this.builder, spec, (params, cx) => agentHandlerContext(params, AgentContext.create(cx)), handler);
20960
+ registerAppRequest(this.builder, spec, (params, cx, signal) => agentHandlerContext(params, AgentContext.create(cx), signal), handler);
20822
20961
  return this;
20823
20962
  }
20824
20963
  notification(spec, handler) {
20825
- registerAppNotification(this.builder, spec, (params, cx) => agentHandlerContext(params, AgentContext.create(cx)), handler);
20964
+ registerAppNotification(this.builder, spec, (params, cx, signal) => agentHandlerContext(params, AgentContext.create(cx), signal), handler);
20826
20965
  return this;
20827
20966
  }
20828
20967
  connectConnection(target, options = {}) {
@@ -20915,11 +21054,11 @@ var ClientApp = class {
20915
21054
  return this.notification(spec, handlerOrParams);
20916
21055
  }
20917
21056
  request(spec, handler) {
20918
- registerAppRequest(this.builder, spec, (params, cx) => clientHandlerContext(params, ClientContext.create(cx)), handler);
21057
+ registerAppRequest(this.builder, spec, (params, cx, signal) => clientHandlerContext(params, ClientContext.create(cx), signal), handler);
20919
21058
  return this;
20920
21059
  }
20921
21060
  notification(spec, handler) {
20922
- registerAppNotification(this.builder, spec, (params, cx) => clientHandlerContext(params, ClientContext.create(cx)), handler);
21061
+ registerAppNotification(this.builder, spec, (params, cx, signal) => clientHandlerContext(params, ClientContext.create(cx), signal), handler);
20923
21062
  return this;
20924
21063
  }
20925
21064
  connectConnection(target) {
@@ -21844,6 +21983,34 @@ function createWebSearchCompleteUpdate(item) {
21844
21983
  rawInput: item
21845
21984
  };
21846
21985
  }
21986
+ function createCollabAgentToolCallUpdate(item) {
21987
+ return {
21988
+ sessionUpdate: "tool_call",
21989
+ toolCallId: item.id,
21990
+ kind: "other",
21991
+ title: item.tool,
21992
+ status: toAcpStatus(item.status),
21993
+ rawInput: createCollabAgentToolCallRawInput(item)
21994
+ };
21995
+ }
21996
+ function createCollabAgentToolCallCompleteUpdate(item) {
21997
+ return {
21998
+ sessionUpdate: "tool_call_update",
21999
+ toolCallId: item.id,
22000
+ title: item.tool,
22001
+ status: toAcpStatus(item.status),
22002
+ rawInput: createCollabAgentToolCallRawInput(item)
22003
+ };
22004
+ }
22005
+ function createCollabAgentToolCallRawInput(item) {
22006
+ return {
22007
+ prompt: item.prompt,
22008
+ senderThreadId: item.senderThreadId,
22009
+ receiverThreadIds: item.receiverThreadIds,
22010
+ agentsStates: item.agentsStates,
22011
+ status: item.status
22012
+ };
22013
+ }
21847
22014
  function formatWebSearchTitle(item) {
21848
22015
  const action = item.action;
21849
22016
  if (!action) {
@@ -22295,6 +22462,7 @@ var CodexEventHandler = class {
22295
22462
  case "mcpServer/startupStatus/updated":
22296
22463
  case "serverRequest/resolved":
22297
22464
  case "model/verification":
22465
+ case "model/safetyBuffering/updated":
22298
22466
  case "windows/worldWritableWarning":
22299
22467
  case "thread/realtime/started":
22300
22468
  case "thread/realtime/itemAdded":
@@ -22316,6 +22484,7 @@ var CodexEventHandler = class {
22316
22484
  case "remoteControl/status/changed":
22317
22485
  case "app/list/updated":
22318
22486
  case "thread/settings/updated":
22487
+ case "externalAgentConfig/import/progress":
22319
22488
  case "process/outputDelta":
22320
22489
  case "process/exited":
22321
22490
  return null;
@@ -22375,15 +22544,24 @@ ${event.details}` : "";
22375
22544
  };
22376
22545
  }
22377
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;
22378
22552
  const status = this.formatThreadGoalStatus(event.goal.status);
22379
- const objective = event.goal.objective.trim();
22553
+ const objective = goalSnapshot.objective;
22380
22554
  const text = objective.includes("\n") ? `Goal updated (${status}):
22381
22555
  ${objective}` : `Goal updated (${status}): ${objective}`;
22382
22556
  return {
22383
22557
  sessionUpdate: "agent_message_chunk",
22384
22558
  content: {
22385
22559
  type: "text",
22386
- text
22560
+ text: `
22561
+
22562
+ ${text}
22563
+
22564
+ `
22387
22565
  }
22388
22566
  };
22389
22567
  }
@@ -22404,14 +22582,28 @@ ${objective}` : `Goal updated (${status}): ${objective}`;
22404
22582
  }
22405
22583
  }
22406
22584
  createThreadGoalClearedEvent(_event) {
22585
+ if (this.sessionState.currentGoal === null) {
22586
+ return null;
22587
+ }
22588
+ this.sessionState.currentGoal = null;
22407
22589
  return {
22408
22590
  sessionUpdate: "agent_message_chunk",
22409
22591
  content: {
22410
22592
  type: "text",
22411
- text: "Goal cleared."
22593
+ text: "\n\nGoal cleared.\n\n"
22412
22594
  }
22413
22595
  };
22414
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
+ }
22415
22607
  createReasoningDeltaEvent(event) {
22416
22608
  this.seenReasoningDeltaItemIds.add(event.itemId);
22417
22609
  return this.createAgentThoughtEvent(event.delta);
@@ -22455,6 +22647,7 @@ ${objective}` : `Goal updated (${status}): ${objective}`;
22455
22647
  this.activeImageGenerationItems.add(event.item.id);
22456
22648
  return createImageGenerationStartUpdate(event.item);
22457
22649
  case "collabAgentToolCall":
22650
+ return createCollabAgentToolCallUpdate(event.item);
22458
22651
  case "subAgentActivity":
22459
22652
  case "sleep":
22460
22653
  case "userMessage":
@@ -22504,12 +22697,13 @@ ${objective}` : `Goal updated (${status}): ${objective}`;
22504
22697
  return this.createCompletedReasoningEvent(event.item);
22505
22698
  case "webSearch":
22506
22699
  return createWebSearchCompleteUpdate(event.item);
22700
+ case "collabAgentToolCall":
22701
+ return createCollabAgentToolCallCompleteUpdate(event.item);
22507
22702
  case "exitedReviewMode":
22508
22703
  return this.createExitedReviewModeEvent(event.item);
22509
22704
  case "contextCompaction":
22510
22705
  return this.createContextCompactedEvent();
22511
22706
  //ignored types
22512
- case "collabAgentToolCall":
22513
22707
  case "subAgentActivity":
22514
22708
  case "sleep":
22515
22709
  case "userMessage":
@@ -22668,8 +22862,12 @@ ${event.stdin}
22668
22862
  }
22669
22863
  async createErrorEvent(params) {
22670
22864
  const error51 = params.error.codexErrorInfo;
22671
- if (error51 == "unauthorized" || error51 == "usageLimitExceeded" || this.getHttpStatusCode(error51) == 401) {
22672
- this.failure = RequestError.authRequired();
22865
+ if (error51 === "usageLimitExceeded") {
22866
+ this.failure = RequestError.internalError(
22867
+ this.createTurnErrorData(params.error)
22868
+ );
22869
+ } else if (this.isAuthenticationRequiredError(error51)) {
22870
+ this.failure = this.sessionState.authConfigured ? RequestError.internalError(this.createTurnErrorData(params.error)) : RequestError.authRequired(this.createTurnErrorData(params.error), params.error.message);
22673
22871
  }
22674
22872
  return {
22675
22873
  sessionUpdate: "agent_message_chunk",
@@ -22681,6 +22879,9 @@ ${event.stdin}
22681
22879
  }
22682
22880
  };
22683
22881
  }
22882
+ isAuthenticationRequiredError(error51) {
22883
+ return error51 === "unauthorized" || this.getHttpStatusCode(error51) === 401;
22884
+ }
22684
22885
  getHttpStatusCode(error51) {
22685
22886
  if (error51 !== null && typeof error51 === "object") {
22686
22887
  if ("httpConnectionFailed" in error51) {
@@ -22695,6 +22896,18 @@ ${event.stdin}
22695
22896
  }
22696
22897
  return null;
22697
22898
  }
22899
+ createTurnErrorData(error51) {
22900
+ const data = {
22901
+ message: error51.additionalDetails ?? error51.message
22902
+ };
22903
+ if (error51.codexErrorInfo !== null) {
22904
+ data.codexErrorInfo = error51.codexErrorInfo;
22905
+ }
22906
+ if (error51.additionalDetails !== null) {
22907
+ data.additionalDetails = error51.additionalDetails;
22908
+ }
22909
+ return data;
22910
+ }
22698
22911
  handleTokenUsageUpdated(params) {
22699
22912
  this.sessionState.lastTokenUsage = toTokenCount(params.tokenUsage.last);
22700
22913
  this.sessionState.totalTokenUsage = toTokenCount(params.tokenUsage.total);
@@ -22774,15 +22987,21 @@ function permissionOption(optionId, name, kind, codexMeta) {
22774
22987
  var CodexApprovalHandler = class {
22775
22988
  connection;
22776
22989
  sessionState;
22777
- constructor(connection, sessionState) {
22990
+ cancellationSignal;
22991
+ constructor(connection, sessionState, cancellationSignal) {
22778
22992
  this.connection = connection;
22779
22993
  this.sessionState = sessionState;
22994
+ this.cancellationSignal = cancellationSignal;
22780
22995
  }
22781
22996
  async handleCommandExecution(params) {
22782
22997
  try {
22783
22998
  const sessionId = this.sessionState.sessionId;
22784
22999
  const acpRequest = this.buildCommandPermissionRequest(sessionId, params);
22785
- const response = await this.connection.request(methods.client.session.requestPermission, acpRequest);
23000
+ const response = await this.connection.request(
23001
+ methods.client.session.requestPermission,
23002
+ acpRequest,
23003
+ this.requestOptions()
23004
+ );
22786
23005
  return this.convertCommandResponse(params, response);
22787
23006
  } catch (error51) {
22788
23007
  logger.error("Error requesting command execution permission", error51);
@@ -22793,7 +23012,11 @@ var CodexApprovalHandler = class {
22793
23012
  try {
22794
23013
  const sessionId = this.sessionState.sessionId;
22795
23014
  const acpRequest = this.buildFileChangePermissionRequest(sessionId, params);
22796
- const response = await this.connection.request(methods.client.session.requestPermission, acpRequest);
23015
+ const response = await this.connection.request(
23016
+ methods.client.session.requestPermission,
23017
+ acpRequest,
23018
+ this.requestOptions()
23019
+ );
22797
23020
  return this.convertFileChangeResponse(params, response);
22798
23021
  } catch (error51) {
22799
23022
  logger.error("Error requesting file change permission", error51);
@@ -22804,13 +23027,20 @@ var CodexApprovalHandler = class {
22804
23027
  try {
22805
23028
  const sessionId = this.sessionState.sessionId;
22806
23029
  const acpRequest = this.buildPermissionsRequest(sessionId, params);
22807
- const response = await this.connection.request(methods.client.session.requestPermission, acpRequest);
23030
+ const response = await this.connection.request(
23031
+ methods.client.session.requestPermission,
23032
+ acpRequest,
23033
+ this.requestOptions()
23034
+ );
22808
23035
  return this.convertPermissionsResponse(params, response);
22809
23036
  } catch (error51) {
22810
23037
  logger.error("Error requesting permissions", error51);
22811
23038
  return this.rejectPermissionsResponse();
22812
23039
  }
22813
23040
  }
23041
+ requestOptions() {
23042
+ return this.cancellationSignal ? { cancellationSignal: this.cancellationSignal } : void 0;
23043
+ }
22814
23044
  buildCommandPermissionRequest(sessionId, params) {
22815
23045
  const options = this.buildCommandOptions(params).map(({ option }) => option);
22816
23046
  return {
@@ -23095,6 +23325,7 @@ function buildToolApprovalOptions(persistOptions) {
23095
23325
  var CodexElicitationHandler = class {
23096
23326
  connection;
23097
23327
  sessionState;
23328
+ cancellationSignal;
23098
23329
  // In Rust, the MCP elicitation handler receives ElicitationRequestEvent directly from the MCP
23099
23330
  // protocol layer, where id is set to "mcp_tool_call_approval_<call_id>" — the call ID is extracted
23100
23331
  // by stripping that prefix.
@@ -23111,9 +23342,10 @@ var CodexElicitationHandler = class {
23111
23342
  // call's elicitation before starting the next, so there is at most one pending approval per
23112
23343
  // (threadId, serverName).
23113
23344
  pendingMcpApprovals = /* @__PURE__ */ new Map();
23114
- constructor(connection, sessionState) {
23345
+ constructor(connection, sessionState, cancellationSignal) {
23115
23346
  this.connection = connection;
23116
23347
  this.sessionState = sessionState;
23348
+ this.cancellationSignal = cancellationSignal;
23117
23349
  }
23118
23350
  handleNotification(notification) {
23119
23351
  switch (notification.method) {
@@ -23133,7 +23365,11 @@ var CodexElicitationHandler = class {
23133
23365
  async handleElicitation(params) {
23134
23366
  try {
23135
23367
  const { request, correlatedCallId } = this.buildPermissionRequest(params);
23136
- const response = await this.connection.request(methods.client.session.requestPermission, request);
23368
+ const response = await this.connection.request(
23369
+ methods.client.session.requestPermission,
23370
+ request,
23371
+ this.requestOptions()
23372
+ );
23137
23373
  if (correlatedCallId !== void 0 && response.outcome.outcome !== "cancelled") {
23138
23374
  const optionId = response.outcome.optionId;
23139
23375
  if (optionId !== McpApprovalOptionId.Decline) {
@@ -23149,6 +23385,9 @@ var CodexElicitationHandler = class {
23149
23385
  return { action: "cancel", content: null, _meta: null };
23150
23386
  }
23151
23387
  }
23388
+ requestOptions() {
23389
+ return this.cancellationSignal ? { cancellationSignal: this.cancellationSignal } : void 0;
23390
+ }
23152
23391
  buildPermissionRequest(params) {
23153
23392
  const sessionId = this.sessionState.sessionId;
23154
23393
  const messageContent = {
@@ -23158,7 +23397,7 @@ var CodexElicitationHandler = class {
23158
23397
  const meta3 = params._meta;
23159
23398
  const isToolApproval = isMcpToolCallApproval(meta3);
23160
23399
  const options = isToolApproval ? buildToolApprovalOptions(parsePersistOptions(meta3)) : ELICITATION_OPTIONS;
23161
- if (params.mode === "form") {
23400
+ if (params.mode === "form" || params.mode === "openai/form") {
23162
23401
  const correlatedCallId = isToolApproval ? this.popPendingApproval(params.threadId, params.serverName) : void 0;
23163
23402
  if (correlatedCallId !== void 0) {
23164
23403
  return {
@@ -23259,6 +23498,8 @@ var CodexElicitationHandler = class {
23259
23498
  };
23260
23499
 
23261
23500
  // src/CodexAuthMethod.ts
23501
+ var CODEX_API_KEY_ENV_VAR = "CODEX_API_KEY";
23502
+ var OPENAI_API_KEY_ENV_VAR = "OPENAI_API_KEY";
23262
23503
  var ApiKeyAuthMethod = {
23263
23504
  id: "api-key",
23264
23505
  name: "API Key",
@@ -23285,8 +23526,11 @@ var GatewayAuthMethod = {
23285
23526
  }
23286
23527
  }
23287
23528
  };
23288
- function getCodexAuthMethods(clientCapabilities) {
23289
- const authMethods = [ApiKeyAuthMethod, ChatGptAuthMethod];
23529
+ function getCodexAuthMethods(clientCapabilities, env = process.env) {
23530
+ const authMethods = [ApiKeyAuthMethod];
23531
+ if (!env["NO_BROWSER"]) {
23532
+ authMethods.push(ChatGptAuthMethod);
23533
+ }
23290
23534
  const supportsGatewayAuth = clientCapabilities?.auth?._meta?.["gateway"] === true;
23291
23535
  if (supportsGatewayAuth) {
23292
23536
  authMethods.push(GatewayAuthMethod);
@@ -24038,7 +24282,7 @@ var package_default = {
24038
24282
  publishConfig: {
24039
24283
  access: "public"
24040
24284
  },
24041
- version: "1.0.0",
24285
+ version: "1.0.2",
24042
24286
  description: "",
24043
24287
  main: "dist/index.js",
24044
24288
  bin: {
@@ -24096,8 +24340,8 @@ var package_default = {
24096
24340
  vitest: "^4.0.10"
24097
24341
  },
24098
24342
  dependencies: {
24099
- "@agentclientprotocol/sdk": "^0.28.1",
24100
- "@openai/codex": "^0.141.0",
24343
+ "@agentclientprotocol/sdk": "^1.0.0",
24344
+ "@openai/codex": "^0.142.4",
24101
24345
  diff: "^8.0.3",
24102
24346
  open: "^11.0.0",
24103
24347
  "vscode-jsonrpc": "^8.2.1",
@@ -24142,17 +24386,15 @@ var CodexAcpClient = class {
24142
24386
  }
24143
24387
  switch (authRequest.methodId) {
24144
24388
  case "api-key": {
24145
- if (!authRequest._meta || !authRequest._meta["api-key"]) throw RequestError.invalidRequest();
24146
- const loginCompletedPromise = this.awaitNextLoginCompleted();
24147
- await this.codexClient.accountLogin({
24148
- type: "apiKey",
24149
- apiKey: authRequest._meta["api-key"].apiKey
24150
- });
24151
- this.gatewayConfig = null;
24152
- const result = await loginCompletedPromise;
24153
- return result.success;
24389
+ const apiKey = authRequest._meta?.["api-key"]?.apiKey ?? this.readApiKeyFromEnv();
24390
+ return await this.authenticateWithApiKey(apiKey);
24154
24391
  }
24155
24392
  case "chat-gpt": {
24393
+ const accountResponse = await this.codexClient.accountRead({ refreshToken: true });
24394
+ if (accountResponse.account?.type === "chatgpt") {
24395
+ this.gatewayConfig = null;
24396
+ return true;
24397
+ }
24156
24398
  const loginCompletedPromise = this.awaitNextLoginCompleted();
24157
24399
  const loginResponse = await this.codexClient.accountLogin({ type: "chatgpt" });
24158
24400
  if (loginResponse.type == "chatgpt") {
@@ -24186,6 +24428,28 @@ var CodexAcpClient = class {
24186
24428
  this.gatewayConfig = null;
24187
24429
  return false;
24188
24430
  }
24431
+ async authenticateWithApiKey(apiKey) {
24432
+ const loginCompletedPromise = this.awaitNextLoginCompleted();
24433
+ await this.codexClient.accountLogin({
24434
+ type: "apiKey",
24435
+ apiKey
24436
+ });
24437
+ this.gatewayConfig = null;
24438
+ const result = await loginCompletedPromise;
24439
+ return result.success;
24440
+ }
24441
+ readApiKeyFromEnv() {
24442
+ for (const envVar of [CODEX_API_KEY_ENV_VAR, OPENAI_API_KEY_ENV_VAR]) {
24443
+ const value = process.env[envVar]?.trim();
24444
+ if (value) {
24445
+ return value;
24446
+ }
24447
+ }
24448
+ throw RequestError.internalError(
24449
+ { envVars: [CODEX_API_KEY_ENV_VAR, OPENAI_API_KEY_ENV_VAR] },
24450
+ `${CODEX_API_KEY_ENV_VAR} or ${OPENAI_API_KEY_ENV_VAR} is not set`
24451
+ );
24452
+ }
24189
24453
  async getAuthenticationStatus() {
24190
24454
  const modelProvider = await this.getCurrentModelProvider();
24191
24455
  if (modelProvider) {
@@ -24208,7 +24472,7 @@ var CodexAcpClient = class {
24208
24472
  case "chatgpt":
24209
24473
  return {
24210
24474
  type: "chat-gpt",
24211
- email: account.email
24475
+ email: account.email ?? ""
24212
24476
  };
24213
24477
  case "amazonBedrock":
24214
24478
  return {
@@ -24223,7 +24487,7 @@ var CodexAcpClient = class {
24223
24487
  return sessionModelProvider;
24224
24488
  }
24225
24489
  const settingsModelProvider = await this.codexClient.configRead({ includeLayers: false });
24226
- return settingsModelProvider.config.model_provider;
24490
+ return settingsModelProvider?.config?.model_provider ?? null;
24227
24491
  }
24228
24492
  async logout() {
24229
24493
  const accountUpdatedPromise = this.awaitNextAccountUpdated();
@@ -24237,6 +24501,9 @@ var CodexAcpClient = class {
24237
24501
  const response = await this.codexClient.accountRead({ refreshToken: false });
24238
24502
  return response.requiresOpenaiAuth && !response.account;
24239
24503
  }
24504
+ hasGatewayAuth() {
24505
+ return this.gatewayConfig !== null;
24506
+ }
24240
24507
  async getAccount() {
24241
24508
  return this.codexClient.accountRead({ refreshToken: false });
24242
24509
  }
@@ -24246,7 +24513,7 @@ var CodexAcpClient = class {
24246
24513
  const response = await this.codexClient.threadResume({
24247
24514
  config: await this.createSessionConfig(request.cwd, additionalDirectories, request.mcpServers ?? []),
24248
24515
  cwd: request.cwd,
24249
- modelProvider: this.getResumeModelProvider(),
24516
+ modelProvider: await this.getResumeModelProvider(),
24250
24517
  threadId: request.sessionId
24251
24518
  });
24252
24519
  onSubscribed?.();
@@ -24256,6 +24523,7 @@ var CodexAcpClient = class {
24256
24523
  sessionId: request.sessionId,
24257
24524
  currentModelId,
24258
24525
  models: codexModels,
24526
+ modelProvider: response.modelProvider,
24259
24527
  currentServiceTier: response.serviceTier ?? null,
24260
24528
  additionalDirectories
24261
24529
  };
@@ -24266,7 +24534,7 @@ var CodexAcpClient = class {
24266
24534
  const response = await this.codexClient.threadResume({
24267
24535
  config: await this.createSessionConfig(request.cwd, additionalDirectories, request.mcpServers ?? []),
24268
24536
  cwd: request.cwd,
24269
- modelProvider: this.getResumeModelProvider(),
24537
+ modelProvider: await this.getResumeModelProvider(),
24270
24538
  threadId: request.sessionId
24271
24539
  });
24272
24540
  onSubscribed?.();
@@ -24280,6 +24548,7 @@ var CodexAcpClient = class {
24280
24548
  sessionId: request.sessionId,
24281
24549
  currentModelId,
24282
24550
  models: codexModels,
24551
+ modelProvider: response.modelProvider,
24283
24552
  currentServiceTier: response.serviceTier ?? null,
24284
24553
  thread: historyResponse.thread,
24285
24554
  additionalDirectories
@@ -24302,6 +24571,7 @@ var CodexAcpClient = class {
24302
24571
  sessionId: response.thread.id,
24303
24572
  currentModelId,
24304
24573
  models: codexModels,
24574
+ modelProvider: response.modelProvider,
24305
24575
  currentServiceTier: response.serviceTier ?? null,
24306
24576
  additionalDirectories
24307
24577
  };
@@ -24326,6 +24596,28 @@ var CodexAcpClient = class {
24326
24596
  async runCompact(sessionId) {
24327
24597
  await this.codexClient.runCompact({ threadId: sessionId });
24328
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
+ }
24329
24621
  async awaitMcpServerStartup(serverNames, afterVersion) {
24330
24622
  return await this.codexClient.awaitMcpServerStartup(serverNames, afterVersion);
24331
24623
  }
@@ -24372,8 +24664,8 @@ var CodexAcpClient = class {
24372
24664
  getModelProvider() {
24373
24665
  return this.gatewayConfig?.modelProvider ?? this.modelProvider;
24374
24666
  }
24375
- getResumeModelProvider() {
24376
- return this.getModelProvider() ?? "openai";
24667
+ async getResumeModelProvider() {
24668
+ return await this.getCurrentModelProvider() ?? "openai";
24377
24669
  }
24378
24670
  async refreshSkills(cwd, additionalRoots) {
24379
24671
  if (!cwd) {
@@ -24417,11 +24709,18 @@ var CodexAcpClient = class {
24417
24709
  * Falls back to model defaults if parameters are missing or unsupported.
24418
24710
  */
24419
24711
  createModelId(availableModels, modelId, reasoningEffort) {
24420
- const selectedModel = availableModels.find((m) => m.id === modelId) ?? availableModels.find((m) => m.isDefault);
24421
- if (!selectedModel) {
24712
+ const selectedModel = availableModels.find((m) => m.id === modelId);
24713
+ if (selectedModel) {
24714
+ return ModelId.create(selectedModel.id, reasoningEffort ?? selectedModel.defaultReasoningEffort);
24715
+ }
24716
+ if (modelId) {
24717
+ return ModelId.create(modelId, reasoningEffort ?? "medium");
24718
+ }
24719
+ const defaultModel = availableModels.find((m) => m.isDefault);
24720
+ if (!defaultModel) {
24422
24721
  throw new Error(`Model selection failed: No model found for ID "${modelId}" and no default model is defined.`);
24423
24722
  }
24424
- return ModelId.create(selectedModel.id, reasoningEffort ?? selectedModel.defaultReasoningEffort);
24723
+ return ModelId.create(defaultModel.id, reasoningEffort ?? defaultModel.defaultReasoningEffort);
24425
24724
  }
24426
24725
  async subscribeToSessionEvents(sessionId, eventHandler, approvalHandler, elicitationHandler) {
24427
24726
  this.codexClient.onServerNotification(sessionId, (event) => {
@@ -24545,11 +24844,6 @@ var CodexAcpClient = class {
24545
24844
  "vscode",
24546
24845
  "exec",
24547
24846
  "appServer",
24548
- "subAgent",
24549
- "subAgentReview",
24550
- "subAgentCompact",
24551
- "subAgentThreadSpawn",
24552
- "subAgentOther",
24553
24847
  "unknown"
24554
24848
  ];
24555
24849
  const requestedCwd = request.cwd?.trim() ?? null;
@@ -24568,23 +24862,19 @@ var CodexAcpClient = class {
24568
24862
  modelProviders,
24569
24863
  sourceKinds
24570
24864
  });
24571
- if (listResponse.data.length === 0) {
24572
- const diagnostics = await this.runSessionListDiagnostics();
24573
- logger.log("Session list diagnostics", diagnostics);
24574
- }
24575
- let sessions = listResponse.data.map((thread) => ({
24865
+ const mapThreadToSession = (thread) => ({
24576
24866
  sessionId: thread.id,
24577
24867
  cwd: thread.cwd,
24578
24868
  title: (thread.name ?? thread.preview) || null,
24579
24869
  updatedAt: new Date(thread.updatedAt * 1e3).toISOString()
24580
- }));
24870
+ });
24871
+ if (listResponse.data.length === 0) {
24872
+ const diagnostics = await this.runSessionListDiagnostics();
24873
+ logger.log("Session list diagnostics", diagnostics);
24874
+ }
24875
+ let sessions = listResponse.data.map(mapThreadToSession);
24581
24876
  if (requestedCwd) {
24582
- const filtered = listResponse.data.filter(filterByCwd).map((thread) => ({
24583
- sessionId: thread.id,
24584
- cwd: thread.cwd,
24585
- title: (thread.name ?? thread.preview) || null,
24586
- updatedAt: new Date(thread.updatedAt * 1e3).toISOString()
24587
- }));
24877
+ const filtered = listResponse.data.filter(filterByCwd).map(mapThreadToSession);
24588
24878
  if (filtered.length > 0 || path4.isAbsolute(requestedCwd)) {
24589
24879
  sessions = filtered;
24590
24880
  } else {
@@ -24640,7 +24930,7 @@ function buildPromptItems(prompt) {
24640
24930
  case "text":
24641
24931
  return { type: "text", text: block.text, text_elements: [] };
24642
24932
  case "image": {
24643
- const url2 = block.uri ?? `data:${block.mimeType};base64,${block.data}`;
24933
+ const url2 = isSupportedImageUrl(block.uri) ? block.uri : imageDataUrl(block);
24644
24934
  return { type: "image", url: url2 };
24645
24935
  }
24646
24936
  case "resource_link":
@@ -24671,9 +24961,23 @@ ${context}`, text_elements: [] };
24671
24961
  }
24672
24962
  }).filter((block) => block !== null);
24673
24963
  }
24964
+ function imageDataUrl(block) {
24965
+ return `data:${block.mimeType};base64,${block.data}`;
24966
+ }
24674
24967
  function isImageMimeType(mimeType) {
24675
24968
  return mimeType?.startsWith("image/") ?? false;
24676
24969
  }
24970
+ function isSupportedImageUrl(uri) {
24971
+ if (!uri) {
24972
+ return false;
24973
+ }
24974
+ try {
24975
+ const protocol = new URL(uri).protocol;
24976
+ return protocol === "http:" || protocol === "https:" || protocol === "data:";
24977
+ } catch {
24978
+ return false;
24979
+ }
24980
+ }
24677
24981
  function formatUriAsLink(name, uri) {
24678
24982
  if (name && name.length > 0) {
24679
24983
  return `[@${name}](${uri})`;
@@ -24778,6 +25082,18 @@ function findSupportedEffort(options, effort) {
24778
25082
  return options.find((o) => o.reasoningEffort === effort)?.reasoningEffort;
24779
25083
  }
24780
25084
  function createModelConfigOption(availableModels, currentBaseModelId) {
25085
+ const options = availableModels.map((model) => ({
25086
+ value: model.id,
25087
+ name: model.displayName,
25088
+ description: model.description
25089
+ }));
25090
+ if (!availableModels.some((model) => model.id === currentBaseModelId)) {
25091
+ options.unshift({
25092
+ value: currentBaseModelId,
25093
+ name: currentBaseModelId,
25094
+ description: null
25095
+ });
25096
+ }
24781
25097
  return {
24782
25098
  id: MODEL_CONFIG_ID,
24783
25099
  name: "Model",
@@ -24785,11 +25101,7 @@ function createModelConfigOption(availableModels, currentBaseModelId) {
24785
25101
  category: "model",
24786
25102
  type: "select",
24787
25103
  currentValue: currentBaseModelId,
24788
- options: availableModels.map((model) => ({
24789
- value: model.id,
24790
- name: model.displayName,
24791
- description: model.description
24792
- }))
25104
+ options
24793
25105
  };
24794
25106
  }
24795
25107
  function createReasoningEffortConfigOption(supportedReasoningEfforts, currentEffort) {
@@ -24813,27 +25125,35 @@ var CodexCommands = class {
24813
25125
  connection;
24814
25126
  codexAcpClient;
24815
25127
  runWithProcessCheck;
24816
- constructor(connection, codexAcpClient, runWithProcessCheck) {
25128
+ onLogout;
25129
+ constructor(connection, codexAcpClient, runWithProcessCheck, onLogout = () => {
25130
+ }) {
24817
25131
  this.connection = connection;
24818
25132
  this.codexAcpClient = codexAcpClient;
24819
25133
  this.runWithProcessCheck = runWithProcessCheck;
25134
+ this.onLogout = onLogout;
24820
25135
  }
24821
- async publish(sessionId) {
25136
+ async publish(sessionState) {
24822
25137
  try {
24823
- const skillsResponse = await this.runWithProcessCheck(() => this.codexAcpClient.listSkills());
25138
+ const skillsResponse = await this.runWithProcessCheck(() => this.codexAcpClient.listSkills(this.createSkillsListParams(sessionState)));
24824
25139
  const availableCommands = this.buildAvailableCommands(skillsResponse?.data ?? []);
24825
25140
  if (availableCommands.length === 0) {
24826
25141
  return;
24827
25142
  }
24828
- const session = new ACPSessionConnection(this.connection, sessionId);
25143
+ const session = new ACPSessionConnection(this.connection, sessionState.sessionId);
24829
25144
  await session.update({
24830
25145
  sessionUpdate: "available_commands_update",
24831
25146
  availableCommands
24832
25147
  });
24833
25148
  } catch (err) {
24834
- logger.error(`Failed to publish available commands for session ${sessionId}`, err);
25149
+ logger.error(`Failed to publish available commands for session ${sessionState.sessionId}`, err);
24835
25150
  }
24836
25151
  }
25152
+ createSkillsListParams(sessionState) {
25153
+ return {
25154
+ cwds: [sessionState.cwd, ...sessionState.additionalDirectories]
25155
+ };
25156
+ }
24837
25157
  buildAvailableCommands(skillsEntries) {
24838
25158
  const commands = /* @__PURE__ */ new Map();
24839
25159
  for (const builtin of this.getBuiltinCommands()) {
@@ -24893,6 +25213,11 @@ var CodexCommands = class {
24893
25213
  description: "Summarize conversation to avoid hitting the context limit.",
24894
25214
  input: null
24895
25215
  },
25216
+ {
25217
+ name: "goal",
25218
+ description: "Set, pause, resume, or clear a task goal.",
25219
+ input: { hint: "[<objective>|clear|pause|resume]" }
25220
+ },
24896
25221
  {
24897
25222
  name: "logout",
24898
25223
  description: "Sign out of Codex. This option is available when you are logged in via ChatGPT.",
@@ -24914,7 +25239,7 @@ var CodexCommands = class {
24914
25239
  rest: commandText.slice(name.length).trim()
24915
25240
  };
24916
25241
  }
24917
- async tryHandleCommand(prompt, sessionState) {
25242
+ async tryHandleCommand(prompt, sessionState, options = {}) {
24918
25243
  const command = this.parseCommand(prompt);
24919
25244
  if (command === null) return { handled: false };
24920
25245
  const commandName = command.name;
@@ -24925,9 +25250,12 @@ var CodexCommands = class {
24925
25250
  await this.runWithProcessCheck(() => this.codexAcpClient.runCompact(sessionId));
24926
25251
  return { handled: true };
24927
25252
  }
25253
+ case "goal": {
25254
+ return await this.runGoalCommand(sessionState, command.rest, options);
25255
+ }
24928
25256
  case "review": {
24929
25257
  const target = this.buildReviewTarget(command.rest);
24930
- const turnCompleted = await this.runReviewCommand(sessionState, target);
25258
+ const turnCompleted = await this.runReviewCommand(sessionState, target, options);
24931
25259
  return { handled: true, turnCompleted };
24932
25260
  }
24933
25261
  case "review-branch": {
@@ -24938,7 +25266,7 @@ var CodexCommands = class {
24938
25266
  const turnCompleted = await this.runReviewCommand(sessionState, {
24939
25267
  type: "baseBranch",
24940
25268
  branch: command.rest
24941
- });
25269
+ }, options);
24942
25270
  return { handled: true, turnCompleted };
24943
25271
  }
24944
25272
  case "review-commit": {
@@ -24950,7 +25278,7 @@ var CodexCommands = class {
24950
25278
  type: "commit",
24951
25279
  sha: command.rest,
24952
25280
  title: null
24953
- });
25281
+ }, options);
24954
25282
  return { handled: true, turnCompleted };
24955
25283
  }
24956
25284
  case "status": {
@@ -24964,6 +25292,7 @@ var CodexCommands = class {
24964
25292
  }
24965
25293
  case "logout": {
24966
25294
  await this.runWithProcessCheck(() => this.codexAcpClient.logout());
25295
+ await this.onLogout();
24967
25296
  const session = new ACPSessionConnection(this.connection, sessionId);
24968
25297
  await session.update({
24969
25298
  sessionUpdate: "agent_message_chunk",
@@ -24972,7 +25301,7 @@ var CodexCommands = class {
24972
25301
  return { handled: true };
24973
25302
  }
24974
25303
  case "skills": {
24975
- const response = await this.runWithProcessCheck(() => this.codexAcpClient.listSkills());
25304
+ const response = await this.runWithProcessCheck(() => this.codexAcpClient.listSkills(this.createSkillsListParams(sessionState)));
24976
25305
  const skills = (response?.data ?? []).flatMap((entry) => entry.skills);
24977
25306
  const lines = skills.map((skill) => {
24978
25307
  const description = skill.shortDescription ?? skill.description ?? "";
@@ -25008,15 +25337,75 @@ var CodexCommands = class {
25008
25337
  return { handled: true };
25009
25338
  }
25010
25339
  }
25011
- async runReviewCommand(sessionState, target) {
25340
+ async runReviewCommand(sessionState, target, options) {
25341
+ options.onTurnStartPending?.();
25012
25342
  return await this.runWithProcessCheck(() => this.codexAcpClient.runReview(
25013
25343
  sessionState.sessionId,
25014
25344
  target,
25015
- (turnId) => {
25016
- sessionState.currentTurnId = turnId;
25345
+ (turnId, threadId) => {
25346
+ this.handleCommandTurnStarted(sessionState, options, turnId, threadId);
25017
25347
  }
25018
25348
  ));
25019
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
+ }
25020
25409
  buildReviewTarget(instructions) {
25021
25410
  if (instructions.length === 0) {
25022
25411
  return { type: "uncommittedChanges" };
@@ -25484,15 +25873,16 @@ function createFunctionCallUpdate(item) {
25484
25873
  if (!toolCallId || !name) {
25485
25874
  return null;
25486
25875
  }
25876
+ const isExecCommand = name === "exec_command";
25487
25877
  const args = parseFunctionArguments(item["arguments"]);
25488
- const command = name === "exec_command" ? commandFromFunctionArguments(args) : null;
25489
- const cwd = name === "exec_command" ? cwdFromFunctionArguments(args) : "";
25878
+ const command = isExecCommand ? commandFromFunctionArguments(args) : null;
25879
+ const cwd = isExecCommand ? cwdFromFunctionArguments(args) : "";
25490
25880
  const commandAction = command ? inferCommandAction(command, cwd) : null;
25491
25881
  if (commandAction) {
25492
25882
  return {
25493
25883
  update: createCommandActionEvent(toolCallId, "inProgress", cwd, commandAction),
25494
25884
  usesTerminal: false,
25495
- isExecCommand: true
25885
+ isExecCommand
25496
25886
  };
25497
25887
  }
25498
25888
  const update = {
@@ -25504,12 +25894,12 @@ function createFunctionCallUpdate(item) {
25504
25894
  rawInput: rawInputForFunctionCall(name, args)
25505
25895
  };
25506
25896
  if (!functionCallUsesTerminal(item)) {
25507
- return { update, usesTerminal: false, isExecCommand: false };
25897
+ return { update, usesTerminal: false, isExecCommand };
25508
25898
  }
25509
25899
  return {
25510
25900
  update: withTerminalContent(update, toolCallId, cwd),
25511
25901
  usesTerminal: true,
25512
- isExecCommand: true
25902
+ isExecCommand
25513
25903
  };
25514
25904
  }
25515
25905
  function createFunctionCallOutputUpdate(item, terminalOutputMode, terminalToolCallIds, execToolCallIds) {
@@ -26023,7 +26413,7 @@ function sedFileArguments(args) {
26023
26413
  return files;
26024
26414
  }
26025
26415
  function looksLikeSedRangeScript(arg) {
26026
- return /^(\d+|\$)?(,(\d+|\$))?[pd]$/.test(arg) || /^(\d+|\$)?(,(\d+|\$))?p$/.test(arg);
26416
+ return /^(\d+|\$)?(,(\d+|\$))?[pd]$/.test(arg);
26027
26417
  }
26028
26418
  function headTailFileArguments(args) {
26029
26419
  const files = [];
@@ -26238,7 +26628,8 @@ var CodexAcpServer = class _CodexAcpServer {
26238
26628
  this.availableCommands = new CodexCommands(
26239
26629
  connection,
26240
26630
  codexAcpClient,
26241
- (operation) => this.runWithProcessCheck(operation)
26631
+ (operation) => this.runWithProcessCheck(operation),
26632
+ () => this.refreshSessionsAuthState(null)
26242
26633
  );
26243
26634
  }
26244
26635
  async initialize(_params) {
@@ -26322,6 +26713,7 @@ var CodexAcpServer = class _CodexAcpServer {
26322
26713
  async handleError(e) {
26323
26714
  if (e.message.includes("log out") || e.message.includes("cloud requirements")) {
26324
26715
  await this.runWithProcessCheck(() => this.codexAcpClient.logout());
26716
+ await this.refreshSessionsAuthState(null);
26325
26717
  throw RequestError.internalError(`${e.message}
26326
26718
 
26327
26719
  You have been logged out. Please try again.`);
@@ -26407,9 +26799,10 @@ You have been logged out. Please try again.`);
26407
26799
  sessionMetadata = await this.runWithProcessCheck(() => this.codexAcpClient.newSession(request));
26408
26800
  }
26409
26801
  const { sessionId, currentModelId, models } = sessionMetadata;
26410
- let account;
26802
+ const authProvider = sessionMetadata.modelProvider ?? this.codexAcpClient.getModelProvider();
26803
+ let authState;
26411
26804
  try {
26412
- account = await this.getActiveAccount();
26805
+ authState = await this.getAuthStateForProvider(authProvider);
26413
26806
  } catch (err) {
26414
26807
  if (resumeSubscribed && requestedSessionGeneration !== null) {
26415
26808
  await this.cleanupStaleSessionOpen(sessionId, requestedSessionGeneration);
@@ -26436,7 +26829,9 @@ You have been logged out. Please try again.`);
26436
26829
  totalTokenUsage: null,
26437
26830
  modelContextWindow: null,
26438
26831
  rateLimits: null,
26439
- account,
26832
+ account: authState.account,
26833
+ authConfigured: authState.authConfigured,
26834
+ authProvider,
26440
26835
  cwd: request.cwd,
26441
26836
  additionalDirectories: sessionMetadata.additionalDirectories,
26442
26837
  fastModeEnabled: sessionMetadata.currentServiceTier === "fast",
@@ -26453,17 +26848,38 @@ You have been logged out. Please try again.`);
26453
26848
  });
26454
26849
  this.publishMcpStartupStatusAsync(sessionId);
26455
26850
  }
26456
- this.publishAvailableCommandsAsync(sessionId);
26851
+ this.publishAvailableCommandsAsync(sessionState);
26457
26852
  const sessionModelState = this.createModelState(models, currentModelId);
26458
26853
  const sessionModeState = sessionState.agentMode.toSessionModeState();
26459
26854
  return [sessionId, sessionModelState, sessionModeState];
26460
26855
  }
26461
- async getActiveAccount() {
26462
- if (this.codexAcpClient.getModelProvider()) {
26463
- return null;
26856
+ async getAuthStateForProvider(authProvider) {
26857
+ if (!this.authProviderUsesOpenAiAccount(authProvider)) {
26858
+ return {
26859
+ account: null,
26860
+ authConfigured: true
26861
+ };
26464
26862
  }
26465
26863
  const accountResponse = await this.runWithProcessCheck(() => this.codexAcpClient.getAccount());
26466
- return accountResponse.account;
26864
+ return {
26865
+ account: accountResponse.account,
26866
+ authConfigured: accountResponse.account !== null || !accountResponse.requiresOpenaiAuth
26867
+ };
26868
+ }
26869
+ authProviderUsesOpenAiAccount(authProvider) {
26870
+ return authProvider === null || authProvider === "openai";
26871
+ }
26872
+ authProvidersMatch(a, b) {
26873
+ if (this.authProviderUsesOpenAiAccount(a) && this.authProviderUsesOpenAiAccount(b)) {
26874
+ return true;
26875
+ }
26876
+ return a === b;
26877
+ }
26878
+ getAuthProviderForAuthenticateRequest(request) {
26879
+ if (isCodexAuthRequest(request) && request.methodId === "gateway") {
26880
+ return "custom-gateway";
26881
+ }
26882
+ return null;
26467
26883
  }
26468
26884
  async loadSession(params) {
26469
26885
  logger.log("Loading session...", { sessionId: params.sessionId });
@@ -26592,14 +27008,26 @@ You have been logged out. Please try again.`);
26592
27008
  logger.log("Authenticate request failed");
26593
27009
  throw RequestError.invalidParams();
26594
27010
  }
27011
+ await this.refreshSessionsAuthState(this.getAuthProviderForAuthenticateRequest(_params));
26595
27012
  logger.log("Authenticate request completed");
26596
27013
  return {};
26597
27014
  }
26598
27015
  async logout(_params) {
26599
27016
  logger.log("Logout request received");
26600
27017
  await this.runWithProcessCheck(() => this.codexAcpClient.logout());
27018
+ await this.refreshSessionsAuthState(null);
26601
27019
  logger.log("Logout request completed");
26602
27020
  }
27021
+ async refreshSessionsAuthState(authProvider) {
27022
+ if (this.sessions.size === 0) return;
27023
+ const sessionsToRefresh = [...this.sessions.values()].filter((sessionState) => this.authProvidersMatch(sessionState.authProvider, authProvider));
27024
+ if (sessionsToRefresh.length === 0) return;
27025
+ const authState = await this.getAuthStateForProvider(authProvider);
27026
+ for (const sessionState of sessionsToRefresh) {
27027
+ sessionState.account = authState.account;
27028
+ sessionState.authConfigured = authState.authConfigured;
27029
+ }
27030
+ }
26603
27031
  async setSessionMode(_params) {
26604
27032
  logger.log("Set session mode requested", {
26605
27033
  sessionId: _params.sessionId,
@@ -26657,6 +27085,10 @@ You have been logged out. Please try again.`);
26657
27085
  applyModelChange(sessionState, value) {
26658
27086
  const model = sessionState.availableModels.find((m) => m.id === value);
26659
27087
  if (!model) {
27088
+ const currentModel = ModelId.fromString(sessionState.currentModelId).model;
27089
+ if (value === currentModel) {
27090
+ return;
27091
+ }
26660
27092
  throw RequestError.invalidParams();
26661
27093
  }
26662
27094
  const currentEffort = ModelId.fromString(sessionState.currentModelId).effort;
@@ -26715,12 +27147,19 @@ You have been logged out. Please try again.`);
26715
27147
  }
26716
27148
  createSessionConfigOptions(sessionState) {
26717
27149
  const currentModelId = ModelId.fromString(sessionState.currentModelId);
26718
- return [
27150
+ const configOptions = [
26719
27151
  sessionState.agentMode.toConfigOption(),
26720
- createModelConfigOption(sessionState.availableModels, currentModelId.model),
26721
- createReasoningEffortConfigOption(sessionState.supportedReasoningEfforts, currentModelId.effort),
26722
- createFastModeConfigOption(sessionState.fastModeEnabled)
27152
+ createModelConfigOption(sessionState.availableModels, currentModelId.model)
26723
27153
  ];
27154
+ if (sessionState.supportedReasoningEfforts.length > 0) {
27155
+ configOptions.push(
27156
+ createReasoningEffortConfigOption(sessionState.supportedReasoningEfforts, currentModelId.effort)
27157
+ );
27158
+ }
27159
+ if (sessionState.currentModelSupportsFast) {
27160
+ configOptions.push(createFastModeConfigOption(sessionState.fastModeEnabled));
27161
+ }
27162
+ return configOptions;
26724
27163
  }
26725
27164
  createSessionConfigOptionsResponse(sessionState) {
26726
27165
  if (!this.isSessionConfigEnabled()) {
@@ -26733,8 +27172,8 @@ You have been logged out. Please try again.`);
26733
27172
  isSessionConfigEnabled() {
26734
27173
  return !isJetBrains2026_1Client(this.clientInfo);
26735
27174
  }
26736
- publishAvailableCommandsAsync(sessionId) {
26737
- void this.availableCommands.publish(sessionId);
27175
+ publishAvailableCommandsAsync(sessionState) {
27176
+ void this.availableCommands.publish(sessionState);
26738
27177
  }
26739
27178
  findCurrentModel(models, currentModelId) {
26740
27179
  const modelId = ModelId.fromString(currentModelId);
@@ -26777,9 +27216,10 @@ You have been logged out. Please try again.`);
26777
27216
  throw err;
26778
27217
  }
26779
27218
  const { sessionId, currentModelId, models, thread } = sessionMetadata;
26780
- let account;
27219
+ const authProvider = sessionMetadata.modelProvider ?? this.codexAcpClient.getModelProvider();
27220
+ let authState;
26781
27221
  try {
26782
- account = await this.getActiveAccount();
27222
+ authState = await this.getAuthStateForProvider(authProvider);
26783
27223
  } catch (err) {
26784
27224
  if (subscribed) {
26785
27225
  await this.cleanupStaleSessionOpen(request.sessionId, requestedSessionGeneration);
@@ -26805,7 +27245,9 @@ You have been logged out. Please try again.`);
26805
27245
  totalTokenUsage: null,
26806
27246
  modelContextWindow: null,
26807
27247
  rateLimits: null,
26808
- account,
27248
+ account: authState.account,
27249
+ authConfigured: authState.authConfigured,
27250
+ authProvider,
26809
27251
  cwd: request.cwd,
26810
27252
  additionalDirectories: sessionMetadata.additionalDirectories,
26811
27253
  fastModeEnabled: sessionMetadata.currentServiceTier === "fast",
@@ -26822,7 +27264,7 @@ You have been logged out. Please try again.`);
26822
27264
  });
26823
27265
  this.publishMcpStartupStatusAsync(sessionId);
26824
27266
  }
26825
- await this.availableCommands.publish(sessionId);
27267
+ await this.availableCommands.publish(sessionState);
26826
27268
  const sessionModelState = this.createModelState(models, currentModelId);
26827
27269
  const sessionModeState = sessionState.agentMode.toSessionModeState();
26828
27270
  return {
@@ -26881,7 +27323,7 @@ You have been logged out. Please try again.`);
26881
27323
  case "dynamicToolCall":
26882
27324
  return [await createDynamicToolCallUpdate(item)];
26883
27325
  case "collabAgentToolCall":
26884
- return [this.createCollabAgentToolCallUpdate(item)];
27326
+ return [createCollabAgentToolCallUpdate(item)];
26885
27327
  case "webSearch":
26886
27328
  return [this.createWebSearchUpdate(item)];
26887
27329
  case "imageView":
@@ -26918,22 +27360,6 @@ You have been logged out. Please try again.`);
26918
27360
  content: { type: "text", text }
26919
27361
  }));
26920
27362
  }
26921
- createCollabAgentToolCallUpdate(item) {
26922
- return {
26923
- sessionUpdate: "tool_call",
26924
- toolCallId: item.id,
26925
- kind: "other",
26926
- title: `collab.${item.tool}`,
26927
- status: this.toAcpToolCallStatus(item.status),
26928
- rawInput: {
26929
- prompt: item.prompt,
26930
- senderThreadId: item.senderThreadId,
26931
- receiverThreadIds: item.receiverThreadIds,
26932
- agentsStates: item.agentsStates,
26933
- status: item.status
26934
- }
26935
- };
26936
- }
26937
27363
  createWebSearchUpdate(item) {
26938
27364
  return {
26939
27365
  sessionUpdate: "tool_call",
@@ -26975,16 +27401,6 @@ ${item.text}`
26975
27401
  }
26976
27402
  };
26977
27403
  }
26978
- toAcpToolCallStatus(status) {
26979
- switch (status) {
26980
- case "inProgress":
26981
- return "in_progress";
26982
- case "completed":
26983
- return "completed";
26984
- case "failed":
26985
- return "failed";
26986
- }
26987
- }
26988
27404
  userInputToContentBlocks(input) {
26989
27405
  switch (input.type) {
26990
27406
  case "text":
@@ -27080,16 +27496,33 @@ ${item.text}`
27080
27496
  const closeSignal = new Promise((resolve) => {
27081
27497
  resolveCloseSignal = resolve;
27082
27498
  });
27499
+ let resolveCancelSignal = () => {
27500
+ };
27501
+ const cancelSignal = new Promise((resolve) => {
27502
+ resolveCancelSignal = resolve;
27503
+ });
27504
+ const abortController = new AbortController();
27083
27505
  let completed = false;
27084
27506
  let closeRequested = false;
27085
27507
  const activePrompt = {
27086
27508
  completion,
27087
27509
  closeSignal,
27510
+ cancelSignal,
27511
+ signal: abortController.signal,
27512
+ currentTurn: null,
27513
+ requestCancel: () => {
27514
+ if (abortController.signal.aborted) {
27515
+ return;
27516
+ }
27517
+ abortController.abort();
27518
+ resolveCancelSignal(null);
27519
+ },
27088
27520
  requestClose: () => {
27089
27521
  if (closeRequested) {
27090
27522
  return;
27091
27523
  }
27092
27524
  closeRequested = true;
27525
+ activePrompt.requestCancel();
27093
27526
  resolveCloseSignal(null);
27094
27527
  },
27095
27528
  complete: () => {
@@ -27106,6 +27539,40 @@ ${item.text}`
27106
27539
  this.activePrompts.set(sessionId, activePrompt);
27107
27540
  return activePrompt;
27108
27541
  }
27542
+ cancelBeforeTurnStarted(activePrompt) {
27543
+ return activePrompt.cancelSignal.then(() => {
27544
+ if (activePrompt.currentTurn === null) {
27545
+ return null;
27546
+ }
27547
+ return new Promise(() => {
27548
+ });
27549
+ });
27550
+ }
27551
+ observePromptRequestCancellation(signal, sessionState, activePrompt) {
27552
+ if (!signal) {
27553
+ return () => {
27554
+ };
27555
+ }
27556
+ const onAbort = () => {
27557
+ if (this.activePrompts.get(sessionState.sessionId) !== activePrompt) {
27558
+ return;
27559
+ }
27560
+ logger.log("Prompt request cancelled", { sessionId: sessionState.sessionId });
27561
+ activePrompt.requestCancel();
27562
+ const turn = activePrompt.currentTurn;
27563
+ if (!turn) {
27564
+ return;
27565
+ }
27566
+ void this.requestTurnInterrupt(turn, "Cancel");
27567
+ };
27568
+ if (signal.aborted) {
27569
+ onAbort();
27570
+ return () => {
27571
+ };
27572
+ }
27573
+ signal.addEventListener("abort", onAbort, { once: true });
27574
+ return () => signal.removeEventListener("abort", onAbort);
27575
+ }
27109
27576
  createPendingTurnStart() {
27110
27577
  let resolve = () => {
27111
27578
  };
@@ -27114,25 +27581,39 @@ ${item.text}`
27114
27581
  });
27115
27582
  return { promise: promise2, resolve };
27116
27583
  }
27117
- interruptLateStartedTurn(sessionId, turnId) {
27584
+ async interruptPromptTurn(turn, requestName) {
27118
27585
  this.codexAcpClient.markTurnStale({
27119
- threadId: sessionId,
27120
- turnId
27586
+ threadId: turn.threadId,
27587
+ turnId: turn.turnId
27121
27588
  });
27122
- void this.runWithProcessCheck(() => this.codexAcpClient.turnInterrupt({
27123
- threadId: sessionId,
27124
- turnId
27125
- })).catch((err) => {
27126
- logger.error(`Close - late turnInterrupt failed`, err);
27127
- }).finally(() => {
27589
+ try {
27590
+ await this.requestTurnInterrupt(turn, requestName);
27591
+ } finally {
27128
27592
  this.codexAcpClient.resolveTurnInterrupted({
27129
- threadId: sessionId,
27130
- turnId
27593
+ threadId: turn.threadId,
27594
+ turnId: turn.turnId
27131
27595
  });
27132
- });
27596
+ }
27597
+ }
27598
+ async requestTurnInterrupt(turn, requestName) {
27599
+ try {
27600
+ await this.runWithProcessCheck(() => this.codexAcpClient.turnInterrupt({
27601
+ threadId: turn.threadId,
27602
+ turnId: turn.turnId
27603
+ }));
27604
+ logger.log(`${requestName} - turnInterrupt succeeded`, {
27605
+ sessionId: turn.threadId,
27606
+ currentTurnId: turn.turnId
27607
+ });
27608
+ } catch (err) {
27609
+ logger.error(`${requestName} - turnInterrupt failed`, err);
27610
+ }
27133
27611
  }
27134
- promptIsClosedOrStale(sessionId, activePrompt) {
27135
- return this.activePrompts.get(sessionId) !== activePrompt || this.sessionIsClosing(sessionId);
27612
+ interruptLateStartedTurn(turn) {
27613
+ void this.interruptPromptTurn(turn, "Close");
27614
+ }
27615
+ promptShouldStop(sessionId, activePrompt) {
27616
+ return activePrompt.signal.aborted || this.activePrompts.get(sessionId) !== activePrompt || this.sessionIsClosing(sessionId);
27136
27617
  }
27137
27618
  async interruptSessionTurn(sessionState, requestName, resolveInterruptedTurn) {
27138
27619
  const turnId = await this.getInterruptibleTurnId(sessionState, requestName);
@@ -27188,7 +27669,7 @@ ${item.text}`
27188
27669
  }
27189
27670
  return turnId;
27190
27671
  }
27191
- async prompt(params) {
27672
+ async prompt(params, signal) {
27192
27673
  logger.log("Prompt received", {
27193
27674
  sessionId: params.sessionId,
27194
27675
  prompt: params.prompt
@@ -27198,10 +27679,18 @@ ${item.text}`
27198
27679
  sessionState.lastTokenUsage = null;
27199
27680
  const activePrompt = this.trackActivePrompt(params.sessionId);
27200
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
+ };
27689
+ const disposePromptRequestCancellation = this.observePromptRequestCancellation(signal, sessionState, activePrompt);
27201
27690
  try {
27202
27691
  const eventHandler = new CodexEventHandler(this.connection, sessionState);
27203
- const approvalHandler = new CodexApprovalHandler(this.connection, sessionState);
27204
- const elicitationHandler = new CodexElicitationHandler(this.connection, sessionState);
27692
+ const approvalHandler = new CodexApprovalHandler(this.connection, sessionState, activePrompt.signal);
27693
+ const elicitationHandler = new CodexElicitationHandler(this.connection, sessionState, activePrompt.signal);
27205
27694
  await this.codexAcpClient.subscribeToSessionEvents(
27206
27695
  params.sessionId,
27207
27696
  (event) => {
@@ -27211,28 +27700,43 @@ ${item.text}`
27211
27700
  approvalHandler,
27212
27701
  elicitationHandler
27213
27702
  );
27214
- const commandResult = await this.availableCommands.tryHandleCommand(params.prompt, sessionState);
27703
+ if (activePrompt.signal.aborted) {
27704
+ return this.cancelledPromptResponse(sessionState);
27705
+ }
27706
+ const commandPromise = this.availableCommands.tryHandleCommand(params.prompt, sessionState, {
27707
+ onTurnStartPending: () => {
27708
+ ensurePendingTurnStart();
27709
+ },
27710
+ onTurnStarted: (turnId, threadId) => {
27711
+ const turn = { threadId, turnId };
27712
+ activePrompt.currentTurn = turn;
27713
+ if (this.promptShouldStop(params.sessionId, activePrompt)) {
27714
+ this.interruptLateStartedTurn(turn);
27715
+ return;
27716
+ }
27717
+ sessionState.currentTurnId = turnId;
27718
+ pendingTurnStart?.resolve(turnId);
27719
+ }
27720
+ });
27721
+ void commandPromise.catch((err) => {
27722
+ if (this.activePrompts.get(params.sessionId) !== activePrompt) {
27723
+ logger.error(`Command for cancelled prompt ${params.sessionId} failed after prompt returned`, err);
27724
+ }
27725
+ });
27726
+ const commandResult = await Promise.race([
27727
+ commandPromise,
27728
+ activePrompt.closeSignal,
27729
+ this.cancelBeforeTurnStarted(activePrompt)
27730
+ ]);
27731
+ if (commandResult === null) {
27732
+ return this.cancelledPromptResponse(sessionState);
27733
+ }
27215
27734
  if (commandResult.handled) {
27216
27735
  logger.log("Prompt handled by a command");
27217
27736
  await this.codexAcpClient.waitForSessionNotifications(params.sessionId);
27218
27737
  if (commandResult.turnCompleted?.turn.status === "interrupted") {
27219
- if (!this.sessionIsClosing(params.sessionId) && this.sessions.has(params.sessionId)) {
27220
- await this.connection.notify(methods.client.session.update, {
27221
- sessionId: params.sessionId,
27222
- update: {
27223
- sessionUpdate: "agent_message_chunk",
27224
- content: {
27225
- type: "text",
27226
- text: "*Conversation interrupted*"
27227
- }
27228
- }
27229
- });
27230
- }
27231
- return {
27232
- stopReason: "cancelled",
27233
- usage: this.buildPromptUsage(sessionState.lastTokenUsage),
27234
- _meta: this.buildQuotaMeta(sessionState)
27235
- };
27738
+ await this.notifyConversationInterrupted(params.sessionId);
27739
+ return this.cancelledPromptResponse(sessionState);
27236
27740
  }
27237
27741
  const error52 = eventHandler.getFailure();
27238
27742
  if (error52) {
@@ -27245,11 +27749,7 @@ ${item.text}`
27245
27749
  };
27246
27750
  }
27247
27751
  if (this.sessionIsClosing(params.sessionId)) {
27248
- return {
27249
- stopReason: "cancelled",
27250
- usage: this.buildPromptUsage(sessionState.lastTokenUsage),
27251
- _meta: this.buildQuotaMeta(sessionState)
27252
- };
27752
+ return this.cancelledPromptResponse(sessionState);
27253
27753
  }
27254
27754
  const modelId = ModelId.fromString(sessionState.currentModelId);
27255
27755
  const modelLacksReasoning = sessionState.supportedReasoningEfforts.length > 0 && sessionState.supportedReasoningEfforts.every((e) => e.reasoningEffort === "none");
@@ -27268,8 +27768,7 @@ ${item.text}`
27268
27768
  sessionState.fastModeEnabled,
27269
27769
  sessionState.currentModelSupportsFast
27270
27770
  );
27271
- pendingTurnStart = this.createPendingTurnStart();
27272
- this.pendingTurnStarts.set(params.sessionId, pendingTurnStart);
27771
+ ensurePendingTurnStart();
27273
27772
  const sendPromptPromise = this.runWithProcessCheck(
27274
27773
  () => this.codexAcpClient.sendPrompt(
27275
27774
  params,
@@ -27280,51 +27779,35 @@ ${item.text}`
27280
27779
  sessionState.cwd,
27281
27780
  sessionState.additionalDirectories,
27282
27781
  (turnId) => {
27283
- if (this.promptIsClosedOrStale(params.sessionId, activePrompt)) {
27284
- this.interruptLateStartedTurn(params.sessionId, turnId);
27782
+ const turn = { threadId: params.sessionId, turnId };
27783
+ activePrompt.currentTurn = turn;
27784
+ if (this.promptShouldStop(params.sessionId, activePrompt)) {
27785
+ this.interruptLateStartedTurn(turn);
27285
27786
  return;
27286
27787
  }
27287
27788
  sessionState.currentTurnId = turnId;
27288
27789
  pendingTurnStart?.resolve(turnId);
27289
27790
  },
27290
- () => this.promptIsClosedOrStale(params.sessionId, activePrompt)
27791
+ () => this.promptShouldStop(params.sessionId, activePrompt)
27291
27792
  )
27292
27793
  );
27293
27794
  void sendPromptPromise.catch((err) => {
27294
27795
  if (this.activePrompts.get(params.sessionId) !== activePrompt) {
27295
- logger.error(`Prompt for closed session ${params.sessionId} failed after close`, err);
27796
+ logger.error(`Prompt for cancelled session ${params.sessionId} failed after prompt returned`, err);
27296
27797
  }
27297
27798
  });
27298
27799
  const turnCompleted = await Promise.race([
27299
27800
  sendPromptPromise,
27300
- activePrompt.closeSignal
27801
+ activePrompt.closeSignal,
27802
+ this.cancelBeforeTurnStarted(activePrompt)
27301
27803
  ]);
27302
27804
  if (turnCompleted === null) {
27303
- return {
27304
- stopReason: "cancelled",
27305
- usage: this.buildPromptUsage(sessionState.lastTokenUsage),
27306
- _meta: this.buildQuotaMeta(sessionState)
27307
- };
27805
+ return this.cancelledPromptResponse(sessionState);
27308
27806
  }
27309
27807
  await this.codexAcpClient.waitForSessionNotifications(params.sessionId);
27310
27808
  if (turnCompleted.turn.status === "interrupted") {
27311
- if (!this.sessionIsClosing(params.sessionId) && this.sessions.has(params.sessionId)) {
27312
- await this.connection.notify(methods.client.session.update, {
27313
- sessionId: params.sessionId,
27314
- update: {
27315
- sessionUpdate: "agent_message_chunk",
27316
- content: {
27317
- type: "text",
27318
- text: "*Conversation interrupted*"
27319
- }
27320
- }
27321
- });
27322
- }
27323
- return {
27324
- stopReason: "cancelled",
27325
- usage: this.buildPromptUsage(sessionState.lastTokenUsage),
27326
- _meta: this.buildQuotaMeta(sessionState)
27327
- };
27809
+ await this.notifyConversationInterrupted(params.sessionId);
27810
+ return this.cancelledPromptResponse(sessionState);
27328
27811
  }
27329
27812
  const error51 = eventHandler.getFailure();
27330
27813
  if (error51) {
@@ -27340,14 +27823,38 @@ ${item.text}`
27340
27823
  throw err;
27341
27824
  } finally {
27342
27825
  logger.log("Prompt completed", { sessionId: params.sessionId });
27826
+ disposePromptRequestCancellation();
27343
27827
  sessionState.currentTurnId = null;
27344
- if (pendingTurnStart !== null && this.pendingTurnStarts.get(params.sessionId) === pendingTurnStart) {
27828
+ const registeredPendingTurnStart = this.pendingTurnStarts.get(params.sessionId);
27829
+ if (registeredPendingTurnStart !== void 0) {
27345
27830
  this.pendingTurnStarts.delete(params.sessionId);
27831
+ registeredPendingTurnStart.resolve(null);
27346
27832
  }
27347
- pendingTurnStart?.resolve(null);
27348
27833
  activePrompt.complete();
27349
27834
  }
27350
27835
  }
27836
+ cancelledPromptResponse(sessionState) {
27837
+ return {
27838
+ stopReason: "cancelled",
27839
+ usage: this.buildPromptUsage(sessionState.lastTokenUsage),
27840
+ _meta: this.buildQuotaMeta(sessionState)
27841
+ };
27842
+ }
27843
+ async notifyConversationInterrupted(sessionId) {
27844
+ if (this.sessionIsClosing(sessionId) || !this.sessions.has(sessionId)) {
27845
+ return;
27846
+ }
27847
+ await this.connection.notify(methods.client.session.update, {
27848
+ sessionId,
27849
+ update: {
27850
+ sessionUpdate: "agent_message_chunk",
27851
+ content: {
27852
+ type: "text",
27853
+ text: "*Conversation interrupted*"
27854
+ }
27855
+ }
27856
+ });
27857
+ }
27351
27858
  buildQuotaMeta(sessionState) {
27352
27859
  const lastTokenUsage = sessionState.lastTokenUsage;
27353
27860
  const modelName = sessionState.currentModelId.replace(/\[.*?]$/, "");
@@ -27454,6 +27961,7 @@ var CommandExecutionApprovalRequest = new import_node2.RequestType("item/command
27454
27961
  var FileChangeApprovalRequest = new import_node2.RequestType("item/fileChange/requestApproval");
27455
27962
  var PermissionsApprovalRequest = new import_node2.RequestType("item/permissions/requestApproval");
27456
27963
  var McpServerElicitationRequest = new import_node2.RequestType("mcpServer/elicitation/request");
27964
+ var GOAL_RUNTIME_EFFECTS_GRACE_MS = 1e3;
27457
27965
  var CodexAppServerClient = class {
27458
27966
  connection;
27459
27967
  approvalHandlers = /* @__PURE__ */ new Map();
@@ -27464,6 +27972,10 @@ var CodexAppServerClient = class {
27464
27972
  pendingTurnCompletionResolvers = /* @__PURE__ */ new Map();
27465
27973
  pendingCompactionCompletionResolvers = /* @__PURE__ */ new Map();
27466
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();
27467
27979
  staleTurnIds = /* @__PURE__ */ new Map();
27468
27980
  constructor(connection) {
27469
27981
  this.connection = connection;
@@ -27484,15 +27996,21 @@ var CodexAppServerClient = class {
27484
27996
  if (isCompactionCompletedNotification(serverNotification)) {
27485
27997
  this.recordCompactionCompleted(serverNotification);
27486
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
+ }
27487
28008
  const routing = extractTurnRouting(serverNotification);
27488
- const staleTurnNotification = this.isStaleTurn(routing.threadId, routing.turnId);
27489
- if (staleTurnNotification) {
27490
- if (isTurnCompletedNotification(serverNotification) && routing.threadId !== null && routing.turnId !== null) {
27491
- this.clearStaleTurn(routing.threadId, routing.turnId);
27492
- }
27493
- for (const callback of this.codexEventHandlers) {
27494
- callback({ eventType: "notification", ...serverNotification });
27495
- }
28009
+ if (this.handleStaleTurnNotification(serverNotification, routing)) {
28010
+ return;
28011
+ }
28012
+ this.recordTurnRouting(routing);
28013
+ if (this.handleStaleTurnNotification(serverNotification, routing)) {
27496
28014
  return;
27497
28015
  }
27498
28016
  this.notify(serverNotification);
@@ -27583,7 +28101,7 @@ var CodexAppServerClient = class {
27583
28101
  });
27584
28102
  try {
27585
28103
  const reviewStarted = await this.reviewStart(params);
27586
- onTurnStarted?.(reviewStarted.turn.id);
28104
+ onTurnStarted?.(reviewStarted.turn.id, reviewStarted.reviewThreadId);
27587
28105
  const earlyCompletion = capturedCompletions.find((event) => event.turn.id === reviewStarted.turn.id);
27588
28106
  releaseCapture();
27589
28107
  if (earlyCompletion) {
@@ -27594,6 +28112,174 @@ var CodexAppServerClient = class {
27594
28112
  releaseCapture();
27595
28113
  }
27596
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
+ }
27597
28283
  async runCompact(params) {
27598
28284
  const compactionCompleted = this.awaitCompactionCompleted(params.threadId);
27599
28285
  await this.threadCompactStart(params);
@@ -27634,6 +28320,12 @@ var CodexAppServerClient = class {
27634
28320
  async threadCompactStart(params) {
27635
28321
  return await this.sendRequest({ method: "thread/compact/start", params });
27636
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
+ }
27637
28329
  async listMcpServerStatus(params) {
27638
28330
  return await this.sendRequest({ method: "mcpServerStatus/list", params });
27639
28331
  }
@@ -27765,6 +28457,57 @@ var CodexAppServerClient = class {
27765
28457
  resolve(event);
27766
28458
  }
27767
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
+ }
27768
28511
  isStaleTurn(threadId, turnId) {
27769
28512
  if (threadId === null || turnId === null) {
27770
28513
  return false;
@@ -27806,6 +28549,70 @@ var CodexAppServerClient = class {
27806
28549
  }
27807
28550
  };
27808
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
+ }
27809
28616
  resolveMcpServerStartupResolvers() {
27810
28617
  const pendingResolvers = [];
27811
28618
  for (const resolver of this.mcpServerStartupResolvers) {
@@ -27868,12 +28675,24 @@ function isMcpServerStatusUpdatedNotification(notification) {
27868
28675
  function isTurnCompletedNotification(notification) {
27869
28676
  return notification.method === "turn/completed";
27870
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
+ }
27871
28687
  function isCompactionCompletedNotification(notification) {
27872
28688
  if (notification.method === "thread/compacted") {
27873
28689
  return true;
27874
28690
  }
27875
28691
  return notification.method === "item/completed" && notification.params.item.type === "contextCompaction";
27876
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
+ }
27877
28696
  function extractThreadId(notification) {
27878
28697
  const params = notification.params;
27879
28698
  if (params && typeof params.threadId === "string") {
@@ -28075,7 +28894,7 @@ function startAcpServer() {
28075
28894
  codexConnection.process.stderr.addListener("data", (data) => {
28076
28895
  stderr = (stderr + data.toString()).slice(-maxStderrTailChars);
28077
28896
  });
28078
- process.stdin.on("close", (chunk) => {
28897
+ process.stdin.on("close", () => {
28079
28898
  codexConnection.process.stdin.end();
28080
28899
  setTimeout(() => {
28081
28900
  if (!codexConnection.process.killed) {
@@ -28105,5 +28924,5 @@ function startAcpServer() {
28105
28924
  codexAcpServer = null;
28106
28925
  }
28107
28926
  });
28108
- }).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.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)).onRequest(methods.agent.logout, (ctx) => getAgent().logout(ctx.params)).onRequest(methods.agent.session.prompt, (ctx) => getAgent().prompt(ctx.params)).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)).connect(acpJsonStream);
28927
+ }).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.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)).onRequest(methods.agent.logout, (ctx) => getAgent().logout(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)).connect(acpJsonStream);
28109
28928
  }