lody 0.69.0 → 0.70.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,6 +2,7 @@ import { createRequire } from "module";
2
2
  import require$$1, { execFile as execFile$1, spawn } from "node:child_process";
3
3
  import { createRequire as createRequire$1 } from "node:module";
4
4
  import { Writable, Readable } from "node:stream";
5
+ import { StringDecoder } from "node:string_decoder";
5
6
  import fs__default from "node:fs";
6
7
  import path__default from "node:path";
7
8
  import fs__default$1, { readFile, constants } from "node:fs/promises";
@@ -20651,8 +20652,9 @@ function createJSONRPCReader(readable) {
20651
20652
  return {
20652
20653
  listen(callback) {
20653
20654
  let buf = "";
20655
+ const decoder = new StringDecoder("utf8");
20654
20656
  const onData = (chunk) => {
20655
- buf += chunk.toString();
20657
+ buf += decoder.write(chunk);
20656
20658
  for (; ; ) {
20657
20659
  const i = buf.indexOf("\n");
20658
20660
  if (i < 0) break;
@@ -20801,6 +20803,22 @@ var LEGACY_SET_SESSION_MODEL_METHOD = "session/set_model";
20801
20803
  var ACP_EXT_SESSION_USAGE_UPDATE_METHOD = "_acp_ext:session_usage_update";
20802
20804
  var ACP_EXT_SESSION_RATE_LIMITS_METHOD = "_acp_ext:session_rate_limits";
20803
20805
  var ACP_EXT_CODEX_PROPOSED_PLAN_METHOD = "_acp_ext:codex_proposed_plan";
20806
+ var CODEX_STEER_APPLIED_METHOD = "_codex/steerApplied";
20807
+ var CODEX_STEER_CAPABILITY = {
20808
+ version: 1,
20809
+ appliedNotification: CODEX_STEER_APPLIED_METHOD,
20810
+ upstreamTurn: "same",
20811
+ configPolicy: "active"
20812
+ };
20813
+ function getCodexSteerId(meta3) {
20814
+ if (typeof meta3 !== "object" || meta3 === null) return null;
20815
+ const codex = meta3["codex"];
20816
+ if (typeof codex !== "object" || codex === null) return null;
20817
+ const steer = codex["steer"];
20818
+ if (typeof steer !== "object" || steer === null) return null;
20819
+ const id = steer["id"];
20820
+ return typeof id === "string" && id.length > 0 ? id : null;
20821
+ }
20804
20822
  function isExtMethodRequest(request) {
20805
20823
  return request.method === "authentication/status" || request.method === "authentication/logout" || request.method === LEGACY_SET_SESSION_MODEL_METHOD;
20806
20824
  }
@@ -21442,6 +21460,7 @@ function createTerminalOutputMeta(mode, terminalId, data) {
21442
21460
  };
21443
21461
  }
21444
21462
  }
21463
+ var CONTEXT_COMPACTION_META = { contextCompaction: true };
21445
21464
  function toAcpStatus(status) {
21446
21465
  switch (status) {
21447
21466
  case "inProgress":
@@ -21584,6 +21603,35 @@ function createImageGenerationUpdate(item, options) {
21584
21603
  rawOutput: imageGenerationRawOutput(item)
21585
21604
  };
21586
21605
  }
21606
+ function createContextCompactionStartUpdate(item) {
21607
+ return {
21608
+ sessionUpdate: "tool_call",
21609
+ toolCallId: item.id,
21610
+ kind: "other",
21611
+ title: "Context compacting",
21612
+ status: "in_progress",
21613
+ _meta: CONTEXT_COMPACTION_META
21614
+ };
21615
+ }
21616
+ function createContextCompactionCompleteUpdate(item) {
21617
+ return {
21618
+ sessionUpdate: "tool_call_update",
21619
+ toolCallId: item.id,
21620
+ title: "Context compacted",
21621
+ status: "completed",
21622
+ _meta: CONTEXT_COMPACTION_META
21623
+ };
21624
+ }
21625
+ function createCompletedContextCompactionUpdate(item) {
21626
+ return {
21627
+ sessionUpdate: "tool_call",
21628
+ toolCallId: item.id,
21629
+ kind: "other",
21630
+ title: "Context compacted",
21631
+ status: "completed",
21632
+ _meta: CONTEXT_COMPACTION_META
21633
+ };
21634
+ }
21587
21635
  async function createExecuteToolCallUpdate(item, title, rawInput, rawOutput) {
21588
21636
  return {
21589
21637
  sessionUpdate: "tool_call",
@@ -22063,6 +22111,69 @@ async function readFileContent(filePath) {
22063
22111
  function recoverCorruptedDiff(diff) {
22064
22112
  return diff.replace(/\n\nMoved to: .*$/, "");
22065
22113
  }
22114
+ var COMPLETE_EMPTY_COMMENT = /^<!--\s*(?:\*\/\s*)?-->\s*$/u;
22115
+ var PARTIAL_COMMENT_BODY = /^\s*(?:\*\/?\s*)?$/u;
22116
+ var PARTIAL_COMMENT_CLOSE = /^\s*(?:\*\/\s*)?-{1,2}$/u;
22117
+ var isHorizontalWhitespace = (char) => char === " " || char === " ";
22118
+ var canBecomeEmptyComment = (text) => {
22119
+ if (text === "<" || text === "<!" || text === "<!-") {
22120
+ return true;
22121
+ }
22122
+ if (!text.startsWith("<!--")) {
22123
+ return false;
22124
+ }
22125
+ const tail = text.slice(4);
22126
+ return COMPLETE_EMPTY_COMMENT.test(text) || PARTIAL_COMMENT_BODY.test(tail) || PARTIAL_COMMENT_CLOSE.test(tail);
22127
+ };
22128
+ var ReasoningSummaryFilter = class {
22129
+ pending = "";
22130
+ lineHasContent = false;
22131
+ push(delta) {
22132
+ let output = "";
22133
+ const emit = (text) => {
22134
+ output += text;
22135
+ for (const char of text) {
22136
+ if (char === "\n" || char === "\r") {
22137
+ this.lineHasContent = false;
22138
+ } else if (!isHorizontalWhitespace(char)) {
22139
+ this.lineHasContent = true;
22140
+ }
22141
+ }
22142
+ };
22143
+ for (const char of delta) {
22144
+ if (this.pending) {
22145
+ this.pending += char;
22146
+ if (canBecomeEmptyComment(this.pending)) {
22147
+ continue;
22148
+ }
22149
+ emit(this.pending);
22150
+ this.pending = "";
22151
+ continue;
22152
+ }
22153
+ if (char === "<" && !this.lineHasContent) {
22154
+ this.pending = char;
22155
+ } else {
22156
+ emit(char);
22157
+ }
22158
+ }
22159
+ return output;
22160
+ }
22161
+ finish() {
22162
+ const output = COMPLETE_EMPTY_COMMENT.test(this.pending) ? "" : this.pending;
22163
+ this.pending = "";
22164
+ this.lineHasContent = false;
22165
+ return output;
22166
+ }
22167
+ };
22168
+ function stripEmptyReasoningComments(text) {
22169
+ const filter = new ReasoningSummaryFilter();
22170
+ return filter.push(text) + filter.finish();
22171
+ }
22172
+ function sanitizeReasoningParts(summary, content) {
22173
+ const useSummary = summary.length > 0;
22174
+ const parts = useSummary ? summary : content;
22175
+ return parts.map((part) => useSummary ? stripEmptyReasoningComments(part) : part).filter((part) => part.length > 0);
22176
+ }
22066
22177
  function createCodexMessagePhaseMeta(phase) {
22067
22178
  if (!phase) {
22068
22179
  return void 0;
@@ -22129,6 +22240,7 @@ var CodexEventHandler = class {
22129
22240
  activeImageGenerationItems = /* @__PURE__ */ new Set();
22130
22241
  emittedImageViewItems = /* @__PURE__ */ new Set();
22131
22242
  seenReasoningDeltaItemIds = /* @__PURE__ */ new Set();
22243
+ reasoningSummaryFilters = /* @__PURE__ */ new Map();
22132
22244
  terminalCommandIds = /* @__PURE__ */ new Set();
22133
22245
  terminalCommandOutputIds = /* @__PURE__ */ new Set();
22134
22246
  proposedPlanMarkdown = "";
@@ -22142,6 +22254,10 @@ var CodexEventHandler = class {
22142
22254
  return this.failure;
22143
22255
  }
22144
22256
  async handleNotification(notification) {
22257
+ if (notification.method === "account/rateLimits/updated") {
22258
+ await this.handleRateLimitsSnapshot(notification.params.rateLimits, true);
22259
+ return;
22260
+ }
22145
22261
  const session = new ACPSessionConnection(this.connection, this.sessionState.sessionId);
22146
22262
  const updateEvent = await this.createUpdateEvent(notification);
22147
22263
  if (updateEvent) {
@@ -22170,6 +22286,8 @@ var CodexEventHandler = class {
22170
22286
  case "thread/tokenUsage/updated":
22171
22287
  return this.createUsageUpdate(notification.params);
22172
22288
  case "thread/name/updated":
22289
+ this.sessionState.sessionTitle = notification.params.threadName ?? null;
22290
+ this.sessionState.sessionTitleSource = notification.params.threadName == null ? "unset" : "explicit";
22173
22291
  return {
22174
22292
  sessionUpdate: "session_info_update",
22175
22293
  title: notification.params.threadName ?? null
@@ -22195,7 +22313,6 @@ var CodexEventHandler = class {
22195
22313
  case "item/mcpToolCall/progress":
22196
22314
  return this.createMcpToolProgressEvent(notification.params);
22197
22315
  case "account/rateLimits/updated":
22198
- this.handleRateLimitsUpdated(notification.params);
22199
22316
  return null;
22200
22317
  case "configWarning":
22201
22318
  return await this.createConfigWarningEvent(notification.params);
@@ -22210,9 +22327,9 @@ var CodexEventHandler = class {
22210
22327
  case "thread/compacted":
22211
22328
  return this.createContextCompactedEvent();
22212
22329
  case "item/reasoning/summaryTextDelta":
22213
- return this.createReasoningDeltaEvent(notification.params);
22330
+ return this.createReasoningSummaryDeltaEvent(notification.params);
22214
22331
  case "item/reasoning/textDelta":
22215
- return this.createReasoningDeltaEvent(notification.params);
22332
+ return this.createRawReasoningDeltaEvent(notification.params);
22216
22333
  case "item/reasoning/summaryPartAdded":
22217
22334
  return this.createReasoningSectionBreakEvent(notification.params);
22218
22335
  case "model/rerouted":
@@ -22277,12 +22394,6 @@ var CodexEventHandler = class {
22277
22394
  this.createSessionUsageExtNotification(notification.params)
22278
22395
  );
22279
22396
  return;
22280
- case "account/rateLimits/updated":
22281
- await this.notifyExt(
22282
- ACP_EXT_SESSION_RATE_LIMITS_METHOD,
22283
- this.createSessionRateLimitsExtNotification(notification.params)
22284
- );
22285
- return;
22286
22397
  case "item/plan/delta":
22287
22398
  await this.emitCodexProposedPlanDelta(notification.params);
22288
22399
  return;
@@ -22344,16 +22455,26 @@ var CodexEventHandler = class {
22344
22455
  isLatest: true
22345
22456
  };
22346
22457
  }
22347
- createSessionRateLimitsExtNotification(params) {
22348
- const rateLimits = params.rateLimits;
22458
+ createSessionRateLimitsExtNotification(rateLimits) {
22459
+ const windows = [rateLimits.primary, rateLimits.secondary].filter(
22460
+ (window) => window !== null
22461
+ );
22462
+ const fiveHour = windows.find((window) => window.windowDurationMins === 5 * 60) ?? null;
22463
+ const sevenDay = windows.find((window) => window.windowDurationMins === 7 * 24 * 60) ?? null;
22349
22464
  return {
22465
+ schemaVersion: 2,
22350
22466
  planName: rateLimits.planType,
22351
22467
  limitName: rateLimits.limitName,
22352
22468
  limitId: rateLimits.limitId,
22353
- fiveHour: rateLimits.primary?.usedPercent ?? null,
22354
- sevenDay: rateLimits.secondary?.usedPercent ?? null,
22355
- fiveHourResetAt: rateLimits.primary?.resetsAt ?? null,
22356
- sevenDayResetAt: rateLimits.secondary?.resetsAt ?? null
22469
+ windows: windows.map((window) => ({
22470
+ usedPercent: window.usedPercent,
22471
+ windowDurationMins: window.windowDurationMins,
22472
+ resetsAt: window.resetsAt
22473
+ })),
22474
+ fiveHour: fiveHour?.usedPercent ?? null,
22475
+ sevenDay: sevenDay?.usedPercent ?? null,
22476
+ fiveHourResetAt: fiveHour?.resetsAt ?? null,
22477
+ sevenDayResetAt: sevenDay?.resetsAt ?? null
22357
22478
  };
22358
22479
  }
22359
22480
  createCodexSessionInfoUpdate(codexMetadata) {
@@ -22415,13 +22536,34 @@ ${event.details}` : "";
22415
22536
  sameThreadGoalSnapshot(left, right) {
22416
22537
  return left !== null && left !== void 0 && left.objective === right.objective && left.status === right.status && left.tokenBudget === right.tokenBudget;
22417
22538
  }
22418
- createReasoningDeltaEvent(event) {
22539
+ createReasoningSummaryDeltaEvent(event) {
22540
+ this.seenReasoningDeltaItemIds.add(event.itemId);
22541
+ let filter = this.reasoningSummaryFilters.get(event.itemId);
22542
+ if (!filter) {
22543
+ filter = new ReasoningSummaryFilter();
22544
+ this.reasoningSummaryFilters.set(event.itemId, filter);
22545
+ }
22546
+ const text = filter.push(event.delta);
22547
+ return text.length > 0 ? this.createAgentThoughtEvent(text, event.itemId) : null;
22548
+ }
22549
+ createRawReasoningDeltaEvent(event) {
22419
22550
  this.seenReasoningDeltaItemIds.add(event.itemId);
22420
22551
  return this.createAgentThoughtEvent(event.delta, event.itemId);
22421
22552
  }
22422
22553
  createReasoningSectionBreakEvent(event) {
22423
22554
  this.seenReasoningDeltaItemIds.add(event.itemId);
22424
- return this.createAgentThoughtEvent("\n\n", event.itemId);
22555
+ const trailingText = this.finishReasoningSummaryFilter(event.itemId);
22556
+ return this.createAgentThoughtEvent(`${trailingText}
22557
+
22558
+ `, event.itemId);
22559
+ }
22560
+ finishReasoningSummaryFilter(itemId) {
22561
+ const filter = this.reasoningSummaryFilters.get(itemId);
22562
+ if (!filter) {
22563
+ return "";
22564
+ }
22565
+ this.reasoningSummaryFilters.delete(itemId);
22566
+ return filter.finish();
22425
22567
  }
22426
22568
  createAgentThoughtEvent(text, messageId) {
22427
22569
  return createAgentTextThoughtChunk(text, messageId);
@@ -22456,6 +22598,8 @@ ${event.details}` : "";
22456
22598
  case "agentMessage":
22457
22599
  this.rememberAgentMessagePhase(event.item);
22458
22600
  return null;
22601
+ case "contextCompaction":
22602
+ return createContextCompactionStartUpdate(event.item);
22459
22603
  case "subAgentActivity":
22460
22604
  case "sleep":
22461
22605
  case "userMessage":
@@ -22463,7 +22607,6 @@ ${event.details}` : "";
22463
22607
  case "reasoning":
22464
22608
  case "enteredReviewMode":
22465
22609
  case "exitedReviewMode":
22466
- case "contextCompaction":
22467
22610
  case "plan":
22468
22611
  return null;
22469
22612
  }
@@ -22499,7 +22642,8 @@ ${event.details}` : "";
22499
22642
  return createImageGenerationUpdate(event.item, { terminalStatus: true });
22500
22643
  case "reasoning":
22501
22644
  if (this.seenReasoningDeltaItemIds.delete(event.item.id)) {
22502
- return null;
22645
+ const trailingText = this.finishReasoningSummaryFilter(event.item.id);
22646
+ return trailingText.length > 0 ? this.createAgentThoughtEvent(trailingText, event.item.id) : null;
22503
22647
  }
22504
22648
  return this.createCompletedReasoningEvent(event.item);
22505
22649
  case "webSearch":
@@ -22512,7 +22656,7 @@ ${event.details}` : "";
22512
22656
  case "exitedReviewMode":
22513
22657
  return this.createExitedReviewModeEvent(event.item);
22514
22658
  case "contextCompaction":
22515
- return this.createContextCompactedEvent();
22659
+ return createContextCompactionCompleteUpdate(event.item);
22516
22660
  //ignored types
22517
22661
  case "subAgentActivity":
22518
22662
  case "sleep":
@@ -22527,8 +22671,7 @@ ${event.details}` : "";
22527
22671
  this.agentMessagePhases.set(item.id, item.phase);
22528
22672
  }
22529
22673
  createCompletedReasoningEvent(item) {
22530
- const parts = item.summary.length > 0 ? item.summary : item.content;
22531
- const text = parts.filter((part) => part.length > 0).join("\n\n");
22674
+ const text = sanitizeReasoningParts(item.summary, item.content).join("\n\n");
22532
22675
  if (text.length === 0) {
22533
22676
  return null;
22534
22677
  }
@@ -22662,7 +22805,15 @@ ${event.stdin}
22662
22805
  }
22663
22806
  async createErrorEvent(params) {
22664
22807
  const error48 = params.error.codexErrorInfo;
22665
- if (error48 === "usageLimitExceeded") {
22808
+ if (params.willRetry) {
22809
+ return this.createCodexSessionInfoUpdate({
22810
+ error: {
22811
+ ...params.error,
22812
+ turnId: params.turnId,
22813
+ willRetry: true
22814
+ }
22815
+ });
22816
+ } else if (error48 === "usageLimitExceeded") {
22666
22817
  this.failure = RequestError.internalError(
22667
22818
  this.createTurnErrorData(params.error)
22668
22819
  );
@@ -22720,16 +22871,36 @@ ${event.stdin}
22720
22871
  size
22721
22872
  };
22722
22873
  }
22723
- handleRateLimitsUpdated(params) {
22874
+ async handleRateLimitsSnapshot(rateLimits, sparse = false) {
22875
+ const merged = this.storeRateLimitsSnapshot(rateLimits, sparse);
22876
+ await this.notifyExt(
22877
+ ACP_EXT_SESSION_RATE_LIMITS_METHOD,
22878
+ this.createSessionRateLimitsExtNotification(merged)
22879
+ );
22880
+ }
22881
+ storeRateLimitsSnapshot(rateLimits, sparse) {
22724
22882
  if (!this.sessionState.rateLimits) {
22725
22883
  this.sessionState.rateLimits = /* @__PURE__ */ new Map();
22726
22884
  }
22727
- const limitId = params.rateLimits.limitId ?? params.rateLimits.limitName ?? "unknown";
22885
+ const fallbackLimitId = this.sessionState.rateLimits.size === 1 ? this.sessionState.rateLimits.keys().next().value : null;
22886
+ const limitId = rateLimits.limitId ?? rateLimits.limitName ?? fallbackLimitId ?? "unknown";
22887
+ const existing = this.sessionState.rateLimits.get(limitId)?.snapshot;
22888
+ const snapshot = sparse && existing ? {
22889
+ limitId: rateLimits.limitId ?? existing.limitId,
22890
+ limitName: rateLimits.limitName ?? existing.limitName,
22891
+ primary: rateLimits.primary ?? existing.primary,
22892
+ secondary: rateLimits.secondary ?? existing.secondary,
22893
+ credits: rateLimits.credits ?? existing.credits,
22894
+ individualLimit: rateLimits.individualLimit ?? existing.individualLimit,
22895
+ planType: rateLimits.planType ?? existing.planType,
22896
+ rateLimitReachedType: rateLimits.rateLimitReachedType ?? existing.rateLimitReachedType
22897
+ } : rateLimits;
22728
22898
  this.sessionState.rateLimits.set(limitId, {
22729
22899
  limitId,
22730
- limitName: params.rateLimits.limitName ?? limitId,
22731
- snapshot: params.rateLimits
22900
+ limitName: snapshot.limitName ?? limitId,
22901
+ snapshot
22732
22902
  });
22903
+ return snapshot;
22733
22904
  }
22734
22905
  handleFuzzyFileSearchSessionUpdated(params) {
22735
22906
  const toolCallId = fuzzyFileSearchToolCallId(params.sessionId);
@@ -23084,6 +23255,7 @@ var ELICITATION_OPTIONS = [
23084
23255
  { optionId: "accept", name: "Accept", kind: "allow_once" },
23085
23256
  { optionId: "decline", name: "Decline", kind: "reject_once" }
23086
23257
  ];
23258
+ var USER_INPUT_OTHER_FIELD_SUFFIX = "__other";
23087
23259
  function parsePersistOptions(meta3) {
23088
23260
  const result = /* @__PURE__ */ new Set();
23089
23261
  if (!meta3 || typeof meta3 !== "object") return result;
@@ -23219,6 +23391,27 @@ function elicitationResponseMeta(response, context, persist = void 0) {
23219
23391
  }
23220
23392
  return Object.keys(meta3).length === 0 ? null : meta3;
23221
23393
  }
23394
+ function userInputOtherFieldId(questionId, questionIds) {
23395
+ const base = `${questionId}${USER_INPUT_OTHER_FIELD_SUFFIX}`;
23396
+ if (!questionIds.has(base)) {
23397
+ return base;
23398
+ }
23399
+ let index = 1;
23400
+ while (questionIds.has(`${base}${index}`)) {
23401
+ index += 1;
23402
+ }
23403
+ return `${base}${index}`;
23404
+ }
23405
+ function userInputResponseValue(content, fieldId) {
23406
+ const value = content[fieldId];
23407
+ if (typeof value === "string" && value.trim() === "") {
23408
+ return void 0;
23409
+ }
23410
+ if (Array.isArray(value) && value.length === 0) {
23411
+ return void 0;
23412
+ }
23413
+ return value;
23414
+ }
23222
23415
  function buildToolApprovalOptions(persistOptions) {
23223
23416
  const options = [
23224
23417
  { optionId: McpApprovalOptionId.AllowOnce, name: "Allow", kind: "allow_once" }
@@ -23315,8 +23508,68 @@ var CodexElicitationHandler = class {
23315
23508
  return { action: "cancel", content: null, _meta: null };
23316
23509
  }
23317
23510
  }
23318
- requestOptions() {
23319
- return this.cancellationSignal ? { cancellationSignal: this.cancellationSignal } : void 0;
23511
+ async handleUserInput(params) {
23512
+ if (!clientSupportsFormElicitation(this.clientCapabilities)) {
23513
+ return { answers: {} };
23514
+ }
23515
+ try {
23516
+ const response = await this.requestUserInputElicitation(params);
23517
+ if (response === null) {
23518
+ return { answers: {} };
23519
+ }
23520
+ return this.convertUserInputResponse(response, params);
23521
+ } catch (error48) {
23522
+ logger.error("Error handling Codex user input request", error48);
23523
+ return { answers: {} };
23524
+ }
23525
+ }
23526
+ requestOptions(cancellationSignal = this.cancellationSignal) {
23527
+ return cancellationSignal ? { cancellationSignal } : void 0;
23528
+ }
23529
+ async requestUserInputElicitation(params) {
23530
+ const request = this.buildUserInputRequest(params);
23531
+ if (params.autoResolutionMs === null) {
23532
+ return await this.connection.request(
23533
+ methods.client.elicitation.create,
23534
+ request,
23535
+ this.requestOptions()
23536
+ );
23537
+ }
23538
+ const abortController = new AbortController();
23539
+ let timeout;
23540
+ let removeAbortListener;
23541
+ const timeoutPromise = new Promise((resolve) => {
23542
+ const resolveWithoutInput = () => {
23543
+ abortController.abort();
23544
+ resolve(null);
23545
+ };
23546
+ timeout = setTimeout(resolveWithoutInput, Math.max(0, params.autoResolutionMs ?? 0));
23547
+ if (this.cancellationSignal?.aborted) {
23548
+ resolveWithoutInput();
23549
+ return;
23550
+ }
23551
+ if (this.cancellationSignal) {
23552
+ this.cancellationSignal.addEventListener("abort", resolveWithoutInput, { once: true });
23553
+ removeAbortListener = () => {
23554
+ this.cancellationSignal?.removeEventListener("abort", resolveWithoutInput);
23555
+ };
23556
+ }
23557
+ });
23558
+ const requestPromise = Promise.resolve(this.connection.request(
23559
+ methods.client.elicitation.create,
23560
+ request,
23561
+ this.requestOptions(abortController.signal)
23562
+ ));
23563
+ void requestPromise.catch(() => {
23564
+ });
23565
+ try {
23566
+ return await Promise.race([requestPromise, timeoutPromise]);
23567
+ } finally {
23568
+ if (timeout) {
23569
+ clearTimeout(timeout);
23570
+ }
23571
+ removeAbortListener?.();
23572
+ }
23320
23573
  }
23321
23574
  createMcpElicitationContext(params) {
23322
23575
  const isToolApproval = isMcpToolCallApproval(params._meta);
@@ -23362,6 +23615,72 @@ var CodexElicitationHandler = class {
23362
23615
  };
23363
23616
  }
23364
23617
  }
23618
+ buildUserInputRequest(params) {
23619
+ const properties = {};
23620
+ const required2 = [];
23621
+ const questionIds = new Set(params.questions.map((question) => question.id));
23622
+ for (const question of params.questions) {
23623
+ const options = question.options ?? [];
23624
+ const hasOptions = options.length > 0;
23625
+ const hasOtherAnswer = question.isOther && hasOptions;
23626
+ const base = {
23627
+ title: question.header || question.id,
23628
+ description: question.question,
23629
+ _meta: {
23630
+ codex: {
23631
+ isOther: question.isOther,
23632
+ isSecret: question.isSecret
23633
+ }
23634
+ }
23635
+ };
23636
+ if (!hasOtherAnswer) {
23637
+ required2.push(question.id);
23638
+ }
23639
+ properties[question.id] = hasOptions ? {
23640
+ ...base,
23641
+ type: "string",
23642
+ oneOf: options.map((option) => ({
23643
+ const: option.label,
23644
+ title: option.label,
23645
+ description: option.description
23646
+ }))
23647
+ } : {
23648
+ ...base,
23649
+ type: "string"
23650
+ };
23651
+ if (hasOtherAnswer) {
23652
+ properties[userInputOtherFieldId(question.id, questionIds)] = {
23653
+ type: "string",
23654
+ title: "Other",
23655
+ description: "Type your own answer instead of choosing an option above.",
23656
+ _meta: {
23657
+ codex: {
23658
+ questionId: question.id,
23659
+ isOtherAnswer: true,
23660
+ isSecret: question.isSecret
23661
+ }
23662
+ }
23663
+ };
23664
+ }
23665
+ }
23666
+ const firstQuestion = params.questions[0];
23667
+ return {
23668
+ sessionId: this.sessionState.sessionId,
23669
+ toolCallId: params.itemId,
23670
+ mode: "form",
23671
+ message: params.questions.length === 1 && firstQuestion ? firstQuestion.question : "Input requested",
23672
+ requestedSchema: {
23673
+ type: "object",
23674
+ properties,
23675
+ required: required2
23676
+ },
23677
+ _meta: {
23678
+ codex: {
23679
+ autoResolutionMs: params.autoResolutionMs
23680
+ }
23681
+ }
23682
+ };
23683
+ }
23365
23684
  buildPermissionRequest(params, context) {
23366
23685
  const sessionId = this.sessionState.sessionId;
23367
23686
  const messageContent = {
@@ -23459,6 +23778,24 @@ var CodexElicitationHandler = class {
23459
23778
  }
23460
23779
  return { action: "cancel", content: null, _meta: null };
23461
23780
  }
23781
+ convertUserInputResponse(response, params) {
23782
+ if (!CreateElicitationResponse.isAccept(response)) {
23783
+ return { answers: {} };
23784
+ }
23785
+ const answers = {};
23786
+ const content = contentRecord(response.content);
23787
+ const questionIds = new Set(params.questions.map((question) => question.id));
23788
+ for (const question of params.questions) {
23789
+ const value = question.isOther && question.options != null && question.options.length > 0 ? userInputResponseValue(content, userInputOtherFieldId(question.id, questionIds)) ?? userInputResponseValue(content, question.id) : userInputResponseValue(content, question.id);
23790
+ if (value === void 0) {
23791
+ continue;
23792
+ }
23793
+ answers[question.id] = {
23794
+ answers: Array.isArray(value) ? value.map(String) : [String(value)]
23795
+ };
23796
+ }
23797
+ return { answers };
23798
+ }
23462
23799
  async publishAcceptedMcpToolApproval(context, accepted) {
23463
23800
  if (!accepted || context.correlatedCallId === void 0) {
23464
23801
  return;
@@ -24245,7 +24582,7 @@ var package_default = {
24245
24582
  publishConfig: {
24246
24583
  access: "public"
24247
24584
  },
24248
- version: "1.1.2",
24585
+ version: "1.2.1",
24249
24586
  description: "An ACP-compatible coding agent powered by Codex",
24250
24587
  main: "dist/index.js",
24251
24588
  bin: {
@@ -24311,13 +24648,17 @@ var package_default = {
24311
24648
  },
24312
24649
  dependencies: {
24313
24650
  "@agentclientprotocol/sdk": "^1.2.1",
24314
- "@openai/codex": "^0.144.0",
24651
+ "@openai/codex": "^0.144.4",
24315
24652
  diff: "^9.0.0",
24316
24653
  open: "^11.0.0",
24317
24654
  "vscode-jsonrpc": "^9.0.1",
24318
24655
  zod: "^4.0.0"
24319
24656
  }
24320
24657
  };
24658
+ var CUSTOM_GATEWAY_PROVIDER_ID = "custom-gateway";
24659
+ var SUPPORTED_GATEWAY_PROTOCOLS = {
24660
+ openai: "responses"
24661
+ };
24321
24662
  var CodexAcpClient = class {
24322
24663
  codexClient;
24323
24664
  config;
@@ -24355,6 +24696,7 @@ var CodexAcpClient = class {
24355
24696
  if (!isCodexAuthRequest(authRequest)) {
24356
24697
  throw RequestError.invalidRequest();
24357
24698
  }
24699
+ this.gatewayConfig = null;
24358
24700
  switch (authRequest.methodId) {
24359
24701
  case "api-key": {
24360
24702
  const apiKey = authRequest._meta?.["api-key"]?.apiKey ?? this.readApiKeyFromEnv();
@@ -24363,7 +24705,6 @@ var CodexAcpClient = class {
24363
24705
  case "chat-gpt": {
24364
24706
  const accountResponse = await this.codexClient.accountRead({ refreshToken: true });
24365
24707
  if (accountResponse.account?.type === "chatgpt") {
24366
- this.gatewayConfig = null;
24367
24708
  return true;
24368
24709
  }
24369
24710
  const loginCompletedPromise = this.awaitNextLoginCompleted();
@@ -24371,7 +24712,6 @@ var CodexAcpClient = class {
24371
24712
  if (loginResponse.type == "chatgpt") {
24372
24713
  await open_default(loginResponse.authUrl);
24373
24714
  }
24374
- this.gatewayConfig = null;
24375
24715
  const result = await loginCompletedPromise;
24376
24716
  return result.success;
24377
24717
  }
@@ -24379,25 +24719,14 @@ var CodexAcpClient = class {
24379
24719
  if (!authRequest._meta) throw RequestError.invalidRequest();
24380
24720
  const gatewaySettings = authRequest._meta["gateway"];
24381
24721
  if (!gatewaySettings) throw RequestError.invalidRequest();
24382
- const baseUrl = gatewaySettings.baseUrl;
24383
- const providerName = typeof gatewaySettings.providerName === "string" && gatewaySettings.providerName.trim().length > 0 ? gatewaySettings.providerName : "User-provided gateway";
24384
- const headers = {
24385
- "X-Client-Feature-ID": "codex",
24386
- ...gatewaySettings.headers
24387
- };
24388
- this.gatewayConfig = {
24389
- modelProvider: "custom-gateway",
24390
- config: {
24391
- name: providerName,
24392
- base_url: baseUrl,
24393
- http_headers: headers,
24394
- wire_api: "responses"
24395
- }
24396
- };
24722
+ this.applyGatewayConfig({
24723
+ baseUrl: gatewaySettings.baseUrl,
24724
+ apiType: GatewayAuthMethod._meta.gateway.protocol,
24725
+ headers: gatewaySettings.headers,
24726
+ providerName: gatewaySettings.providerName
24727
+ });
24397
24728
  return true;
24398
24729
  }
24399
- this.gatewayConfig = null;
24400
- return false;
24401
24730
  }
24402
24731
  async authenticateWithApiKey(apiKey) {
24403
24732
  const loginCompletedPromise = this.awaitNextLoginCompleted();
@@ -24405,7 +24734,6 @@ var CodexAcpClient = class {
24405
24734
  type: "apiKey",
24406
24735
  apiKey
24407
24736
  });
24408
- this.gatewayConfig = null;
24409
24737
  const result = await loginCompletedPromise;
24410
24738
  return result.success;
24411
24739
  }
@@ -24472,12 +24800,90 @@ var CodexAcpClient = class {
24472
24800
  const response = await this.codexClient.accountRead({ refreshToken: false });
24473
24801
  return response.requiresOpenaiAuth && !response.account;
24474
24802
  }
24475
- hasGatewayAuth() {
24476
- return this.gatewayConfig !== null;
24803
+ /**
24804
+ * Validates and stores custom gateway routing. Shared by the `gateway` auth
24805
+ * method and the ACP `providers/set` method. Throws `invalid_params` for an
24806
+ * unsupported protocol or a malformed base URL.
24807
+ */
24808
+ applyGatewayConfig(params) {
24809
+ const apiType = params.apiType;
24810
+ const wireApi = SUPPORTED_GATEWAY_PROTOCOLS[apiType];
24811
+ if (!wireApi) {
24812
+ throw RequestError.invalidParams(
24813
+ { apiType },
24814
+ `Unsupported provider apiType "${apiType}"; supported: ${Object.keys(SUPPORTED_GATEWAY_PROTOCOLS).join(", ")}`
24815
+ );
24816
+ }
24817
+ if (typeof params.baseUrl !== "string" || params.baseUrl.trim().length === 0) {
24818
+ throw RequestError.invalidParams(void 0, "baseUrl must be a non-empty string");
24819
+ }
24820
+ const providerName = typeof params.providerName === "string" && params.providerName.trim().length > 0 ? params.providerName : "User-provided gateway";
24821
+ const headers = {
24822
+ "X-Client-Feature-ID": "codex",
24823
+ ...params.headers
24824
+ };
24825
+ this.gatewayConfig = {
24826
+ modelProvider: CUSTOM_GATEWAY_PROVIDER_ID,
24827
+ config: {
24828
+ name: providerName,
24829
+ base_url: params.baseUrl,
24830
+ http_headers: headers,
24831
+ wire_api: wireApi
24832
+ }
24833
+ };
24834
+ }
24835
+ /**
24836
+ * `providers/list`: returns the single client-configurable custom gateway
24837
+ * provider. `current` carries only non-secret routing (never headers), and is
24838
+ * `null` when the provider is not configured/disabled.
24839
+ */
24840
+ listProviders() {
24841
+ const gatewayConfig = this.gatewayConfig;
24842
+ const current = gatewayConfig ? {
24843
+ apiType: gatewayApiTypeFromConfig(gatewayConfig),
24844
+ baseUrl: gatewayConfig.config.base_url
24845
+ } : null;
24846
+ return [
24847
+ {
24848
+ providerId: CUSTOM_GATEWAY_PROVIDER_ID,
24849
+ supported: Object.keys(SUPPORTED_GATEWAY_PROTOCOLS),
24850
+ required: false,
24851
+ current
24852
+ }
24853
+ ];
24854
+ }
24855
+ /**
24856
+ * `providers/set`: replaces the full configuration for the custom gateway
24857
+ * provider. Rejects unknown provider ids with `invalid_params`.
24858
+ */
24859
+ setProvider(request) {
24860
+ if (request.providerId !== CUSTOM_GATEWAY_PROVIDER_ID) {
24861
+ throw RequestError.invalidParams(
24862
+ { providerId: request.providerId },
24863
+ `Unknown providerId "${request.providerId}"; only "${CUSTOM_GATEWAY_PROVIDER_ID}" is configurable`
24864
+ );
24865
+ }
24866
+ this.applyGatewayConfig({
24867
+ apiType: request.apiType,
24868
+ baseUrl: request.baseUrl,
24869
+ headers: request.headers
24870
+ });
24871
+ }
24872
+ /**
24873
+ * `providers/disable`: disables the custom gateway provider. Disabling an
24874
+ * unknown provider id is idempotent success (RFD behavior §7).
24875
+ */
24876
+ disableProvider(request) {
24877
+ if (request.providerId === CUSTOM_GATEWAY_PROVIDER_ID) {
24878
+ this.gatewayConfig = null;
24879
+ }
24477
24880
  }
24478
24881
  async getAccount() {
24479
24882
  return this.codexClient.accountRead({ refreshToken: false });
24480
24883
  }
24884
+ async getRateLimits() {
24885
+ return this.codexClient.accountRateLimitsRead();
24886
+ }
24481
24887
  async resumeSession(request, onSubscribed) {
24482
24888
  const additionalDirectories = readAdditionalDirectories(request.cwd, request.additionalDirectories, request._meta);
24483
24889
  await this.refreshSkills(request.cwd, additionalDirectories);
@@ -24715,6 +25121,10 @@ var CodexAcpClient = class {
24715
25121
  handleElicitation: async (params) => {
24716
25122
  await this.waitForSessionNotifications(sessionId);
24717
25123
  return await elicitationHandler.handleElicitation(params);
25124
+ },
25125
+ handleUserInput: async (params) => {
25126
+ await this.waitForSessionNotifications(sessionId);
25127
+ return await elicitationHandler.handleUserInput(params);
24718
25128
  }
24719
25129
  });
24720
25130
  }
@@ -24764,6 +25174,15 @@ var CodexAcpClient = class {
24764
25174
  }
24765
25175
  return await this.codexClient.runTurn(params, onTurnStarted);
24766
25176
  }
25177
+ async sendSteer(request, expectedTurnId, steerId) {
25178
+ const response = await this.codexClient.turnSteer({
25179
+ threadId: request.sessionId,
25180
+ input: buildPromptItems(request.prompt),
25181
+ expectedTurnId,
25182
+ clientUserMessageId: steerId
25183
+ });
25184
+ return response.turnId;
25185
+ }
24767
25186
  resolveTurnInterrupted(params) {
24768
25187
  this.codexClient.resolveTurnInterrupted(params.threadId, params.turnId);
24769
25188
  }
@@ -24881,7 +25300,7 @@ var CodexAcpClient = class {
24881
25300
  const [allProviders, archivedAllProviders, customGateway] = await Promise.all([
24882
25301
  this.codexClient.threadList({}),
24883
25302
  this.codexClient.threadList({ archived: true }),
24884
- this.codexClient.threadList({ modelProviders: ["custom-gateway"] })
25303
+ this.codexClient.threadList({ modelProviders: [CUSTOM_GATEWAY_PROVIDER_ID] })
24885
25304
  ]);
24886
25305
  return {
24887
25306
  allProviders: {
@@ -25034,6 +25453,11 @@ function arraysEqual(left, right) {
25034
25453
  function isJsonObject(value) {
25035
25454
  return value !== null && typeof value === "object" && !Array.isArray(value);
25036
25455
  }
25456
+ function gatewayApiTypeFromConfig(gatewayConfig) {
25457
+ const wireApi = gatewayConfig.config.wire_api;
25458
+ const match = Object.entries(SUPPORTED_GATEWAY_PROTOCOLS).find(([, wire]) => wire === wireApi);
25459
+ return match?.[0] ?? "openai";
25460
+ }
25037
25461
  function mergeGatewayConfig(config2, gatewayConfig) {
25038
25462
  if (gatewayConfig !== null) {
25039
25463
  const newConfig = { ...config2 };
@@ -25050,6 +25474,9 @@ function mergeGatewayConfig(config2, gatewayConfig) {
25050
25474
  }
25051
25475
  var MODEL_CONFIG_ID = "model";
25052
25476
  var REASONING_EFFORT_CONFIG_ID = "reasoning_effort";
25477
+ function capitalize(value) {
25478
+ return value.charAt(0).toUpperCase() + value.slice(1);
25479
+ }
25053
25480
  function findSupportedEffort(options, effort) {
25054
25481
  if (!effort) return void 0;
25055
25482
  return options.find((o) => o.reasoningEffort === effort)?.reasoningEffort;
@@ -25087,7 +25514,7 @@ function createReasoningEffortConfigOption(supportedReasoningEfforts, currentEff
25087
25514
  currentValue: currentEffort,
25088
25515
  options: supportedReasoningEfforts.map((option) => ({
25089
25516
  value: option.reasoningEffort,
25090
- name: option.reasoningEffort,
25517
+ name: capitalize(option.reasoningEffort),
25091
25518
  description: option.description
25092
25519
  }))
25093
25520
  };
@@ -25288,8 +25715,7 @@ var CodexCommands = class {
25288
25715
  return { handled: true };
25289
25716
  }
25290
25717
  default:
25291
- await this.sendUnknownCommandMessage(commandName, sessionId);
25292
- return { handled: true };
25718
+ return { handled: false };
25293
25719
  }
25294
25720
  }
25295
25721
  async runReviewCommand(sessionState, target, options) {
@@ -25371,17 +25797,6 @@ var CodexCommands = class {
25371
25797
  const session = new ACPSessionConnection(this.connection, sessionId);
25372
25798
  await session.update(createAgentTextMessageChunk(text));
25373
25799
  }
25374
- async sendUnknownCommandMessage(name, sessionId) {
25375
- const lines = this.getBuiltinCommands().map((command) => `- /${command.name}: ${command.description}`);
25376
- const text = [
25377
- `Unknown command "/${name}".`,
25378
- "Available commands:"
25379
- ];
25380
- if (lines.length > 0) {
25381
- text.push(...lines);
25382
- }
25383
- await this.sendCommandMessage(text.join("\n"), sessionId);
25384
- }
25385
25800
  buildStatusMessage(sessionState) {
25386
25801
  const agentMode = sessionState.agentMode;
25387
25802
  const accountText = this.formatAccountInfo(sessionState.account);
@@ -25645,6 +26060,7 @@ function toolCallIdFromThreadItem(item) {
25645
26060
  case "webSearch":
25646
26061
  case "imageView":
25647
26062
  case "imageGeneration":
26063
+ case "contextCompaction":
25648
26064
  return item.id;
25649
26065
  case "userMessage":
25650
26066
  case "hookPrompt":
@@ -25654,7 +26070,6 @@ function toolCallIdFromThreadItem(item) {
25654
26070
  case "subAgentActivity":
25655
26071
  case "enteredReviewMode":
25656
26072
  case "exitedReviewMode":
25657
- case "contextCompaction":
25658
26073
  case "sleep":
25659
26074
  return null;
25660
26075
  }
@@ -25737,9 +26152,13 @@ function createAgentReasoningEventUpdates(payload) {
25737
26152
  if (text === null || text.length === 0) {
25738
26153
  return [];
25739
26154
  }
26155
+ const sanitizedText = stripEmptyReasoningComments(text);
26156
+ if (sanitizedText.length === 0) {
26157
+ return [];
26158
+ }
25740
26159
  return [{
25741
26160
  sessionUpdate: "agent_thought_chunk",
25742
- content: { type: "text", text }
26161
+ content: { type: "text", text: sanitizedText }
25743
26162
  }];
25744
26163
  }
25745
26164
  function imageBlocks(images) {
@@ -25776,11 +26195,7 @@ function contentBlocksFromResponseContent(content) {
25776
26195
  });
25777
26196
  }
25778
26197
  function createReasoningUpdates(item) {
25779
- const parts = textParts(item["summary"]);
25780
- if (parts.length === 0) {
25781
- parts.push(...textParts(item["content"]));
25782
- }
25783
- return parts.map((text) => ({
26198
+ return sanitizeReasoningParts(textParts(item["summary"]), textParts(item["content"])).map((text) => ({
25784
26199
  sessionUpdate: "agent_thought_chunk",
25785
26200
  content: { type: "text", text }
25786
26201
  }));
@@ -26571,6 +26986,7 @@ var CodexAcpServer = class _CodexAcpServer {
26571
26986
  pendingMcpStartupSessions;
26572
26987
  pendingTurnStarts;
26573
26988
  activePrompts;
26989
+ pendingSteers;
26574
26990
  closingSessions;
26575
26991
  sessionGenerations;
26576
26992
  sessionOpenGenerations;
@@ -26579,6 +26995,7 @@ var CodexAcpServer = class _CodexAcpServer {
26579
26995
  this.pendingMcpStartupSessions = /* @__PURE__ */ new Map();
26580
26996
  this.pendingTurnStarts = /* @__PURE__ */ new Map();
26581
26997
  this.activePrompts = /* @__PURE__ */ new Map();
26998
+ this.pendingSteers = /* @__PURE__ */ new Map();
26582
26999
  this.closingSessions = /* @__PURE__ */ new Map();
26583
27000
  this.sessionGenerations = /* @__PURE__ */ new Map();
26584
27001
  this.sessionOpenGenerations = /* @__PURE__ */ new Map();
@@ -26616,6 +27033,7 @@ var CodexAcpServer = class _CodexAcpServer {
26616
27033
  auth: {
26617
27034
  logout: {}
26618
27035
  },
27036
+ providers: {},
26619
27037
  loadSession: true,
26620
27038
  promptCapabilities: {
26621
27039
  embeddedContext: true,
@@ -26632,6 +27050,11 @@ var CodexAcpServer = class _CodexAcpServer {
26632
27050
  acp: false,
26633
27051
  http: true,
26634
27052
  sse: false
27053
+ },
27054
+ _meta: {
27055
+ codex: {
27056
+ steer: CODEX_STEER_CAPABILITY
27057
+ }
26635
27058
  }
26636
27059
  },
26637
27060
  authMethods: getCodexAuthMethods(_params.clientCapabilities)
@@ -26807,9 +27230,12 @@ You have been logged out. Please try again.`);
26807
27230
  planModeEnabled: false,
26808
27231
  planModeExplicitlySet: false,
26809
27232
  sessionMcpServers,
26810
- terminalOutputMode: this.terminalOutputMode
27233
+ terminalOutputMode: this.terminalOutputMode,
27234
+ sessionTitle: null,
27235
+ sessionTitleSource: "sessionId" in request ? "unknown" : "unset"
26811
27236
  };
26812
27237
  this.sessions.set(sessionId, sessionState);
27238
+ this.publishRateLimitsAsync(sessionState);
26813
27239
  resumeSubscribed = false;
26814
27240
  if (requestedMcpServers.length > 0 && mcpServerStartupVersion !== null) {
26815
27241
  this.pendingMcpStartupSessions.set(sessionId, {
@@ -26845,6 +27271,27 @@ You have been logged out. Please try again.`);
26845
27271
  }
26846
27272
  return a === b;
26847
27273
  }
27274
+ publishRateLimitsAsync(sessionState) {
27275
+ if (!sessionState.authConfigured || !this.authProviderUsesOpenAiAccount(sessionState.authProvider)) {
27276
+ return;
27277
+ }
27278
+ void this.codexAcpClient.getRateLimits().then(async (response) => {
27279
+ if (this.sessions.get(sessionState.sessionId) !== sessionState) {
27280
+ return;
27281
+ }
27282
+ if (!response?.rateLimits) {
27283
+ return;
27284
+ }
27285
+ const rateLimitsById = Object.values(response.rateLimitsByLimitId ?? {}).filter((snapshot) => snapshot !== void 0);
27286
+ const snapshots = rateLimitsById.length > 0 ? rateLimitsById : [response.rateLimits];
27287
+ const handler = new CodexEventHandler(this.connection, sessionState);
27288
+ for (const snapshot of snapshots) {
27289
+ await handler.handleRateLimitsSnapshot(snapshot);
27290
+ }
27291
+ }).catch((err) => {
27292
+ logger.error(`Failed to read rate limits for session ${sessionState.sessionId}`, err);
27293
+ });
27294
+ }
26848
27295
  getAuthProviderForAuthenticateRequest(request) {
26849
27296
  if (isCodexAuthRequest(request) && request.methodId === "gateway") {
26850
27297
  return "custom-gateway";
@@ -26988,6 +27435,17 @@ You have been logged out. Please try again.`);
26988
27435
  await this.refreshSessionsAuthState(null);
26989
27436
  logger.log("Logout request completed");
26990
27437
  }
27438
+ listProviders(_params) {
27439
+ return { providers: this.codexAcpClient.listProviders() };
27440
+ }
27441
+ setProvider(params) {
27442
+ this.codexAcpClient.setProvider(params);
27443
+ return {};
27444
+ }
27445
+ disableProvider(params) {
27446
+ this.codexAcpClient.disableProvider(params);
27447
+ return {};
27448
+ }
26991
27449
  async refreshSessionsAuthState(authProvider) {
26992
27450
  if (this.sessions.size === 0) return;
26993
27451
  const sessionsToRefresh = [...this.sessions.values()].filter((sessionState) => this.authProvidersMatch(sessionState.authProvider, authProvider));
@@ -26996,6 +27454,7 @@ You have been logged out. Please try again.`);
26996
27454
  for (const sessionState of sessionsToRefresh) {
26997
27455
  sessionState.account = authState.account;
26998
27456
  sessionState.authConfigured = authState.authConfigured;
27457
+ this.publishRateLimitsAsync(sessionState);
26999
27458
  }
27000
27459
  }
27001
27460
  async setSessionMode(_params) {
@@ -27259,9 +27718,12 @@ You have been logged out. Please try again.`);
27259
27718
  planModeEnabled: false,
27260
27719
  planModeExplicitlySet: false,
27261
27720
  sessionMcpServers,
27262
- terminalOutputMode: this.terminalOutputMode
27721
+ terminalOutputMode: this.terminalOutputMode,
27722
+ sessionTitle: null,
27723
+ sessionTitleSource: "unset"
27263
27724
  };
27264
27725
  this.sessions.set(sessionId, sessionState);
27726
+ this.publishRateLimitsAsync(sessionState);
27265
27727
  subscribed = false;
27266
27728
  if (requestedMcpServers.length > 0 && mcpServerStartupVersion !== null) {
27267
27729
  this.pendingMcpStartupSessions.set(sessionId, {
@@ -27283,6 +27745,7 @@ You have been logged out. Please try again.`);
27283
27745
  async streamThreadHistory(sessionId, thread) {
27284
27746
  const session = new ACPSessionConnection(this.connection, sessionId);
27285
27747
  const sessionState = this.getSessionState(sessionId);
27748
+ await this.publishThreadHistoryTitle(session, sessionState, thread);
27286
27749
  const responseItemFallbackUpdates = await createResponseItemHistoryFallbackUpdates(
27287
27750
  thread,
27288
27751
  sessionState.terminalOutputMode
@@ -27299,6 +27762,47 @@ You have been logged out. Please try again.`);
27299
27762
  await session.update(update);
27300
27763
  }
27301
27764
  }
27765
+ async publishThreadHistoryTitle(session, sessionState, thread) {
27766
+ const explicitTitle = this.normalizeSessionTitle(thread.name);
27767
+ if (explicitTitle) {
27768
+ sessionState.sessionTitle = explicitTitle;
27769
+ sessionState.sessionTitleSource = "explicit";
27770
+ await session.update({
27771
+ sessionUpdate: "session_info_update",
27772
+ title: explicitTitle
27773
+ });
27774
+ return;
27775
+ }
27776
+ const historyTitle = this.findFirstUserMessageTitle(thread) ?? this.normalizeSessionTitle(thread.preview);
27777
+ await this.publishFallbackSessionTitle(sessionState, historyTitle);
27778
+ }
27779
+ findFirstUserMessageTitle(thread) {
27780
+ for (const turn of thread.turns) {
27781
+ for (const item of turn.items) {
27782
+ if (item.type !== "userMessage") continue;
27783
+ const title = this.normalizeSessionTitle(item.content.filter((input) => input.type === "text").map((input) => input.text).join(" "));
27784
+ if (title) return title;
27785
+ }
27786
+ }
27787
+ return null;
27788
+ }
27789
+ async publishFallbackSessionTitle(sessionState, title) {
27790
+ if (sessionState.sessionTitleSource !== "unset" || !title) return;
27791
+ sessionState.sessionTitle = title;
27792
+ sessionState.sessionTitleSource = "fallback";
27793
+ const session = new ACPSessionConnection(this.connection, sessionState.sessionId);
27794
+ await session.update({
27795
+ sessionUpdate: "session_info_update",
27796
+ title
27797
+ });
27798
+ }
27799
+ createPromptFallbackTitle(prompt) {
27800
+ return this.normalizeSessionTitle(prompt.filter((block) => block.type === "text").map((block) => block.text).join(" "));
27801
+ }
27802
+ normalizeSessionTitle(title) {
27803
+ const normalized = title?.replace(/\s+/g, " ").trim() ?? "";
27804
+ return normalized.length > 0 ? normalized : null;
27805
+ }
27302
27806
  async createHistoryUpdates(item, sessionState) {
27303
27807
  switch (item.type) {
27304
27808
  case "userMessage":
@@ -27345,7 +27849,7 @@ You have been logged out. Please try again.`);
27345
27849
  case "exitedReviewMode":
27346
27850
  return [this.createReviewModeUpdate(item, false)];
27347
27851
  case "contextCompaction":
27348
- return [this.createContextCompactionUpdate()];
27852
+ return [createCompletedContextCompactionUpdate(item)];
27349
27853
  case "plan":
27350
27854
  return [this.createPlanUpdate(item)];
27351
27855
  }
@@ -27362,9 +27866,8 @@ You have been logged out. Please try again.`);
27362
27866
  return updates;
27363
27867
  }
27364
27868
  createReasoningUpdates(item) {
27365
- const parts = item.summary.length > 0 ? item.summary : item.content;
27366
27869
  const messageId = item.id;
27367
- return parts.map((text) => createAgentTextThoughtChunk(text, messageId));
27870
+ return sanitizeReasoningParts(item.summary, item.content).map((text) => createAgentTextThoughtChunk(text, messageId));
27368
27871
  }
27369
27872
  createWebSearchUpdate(item) {
27370
27873
  return {
@@ -27388,15 +27891,6 @@ You have been logged out. Please try again.`);
27388
27891
  }
27389
27892
  };
27390
27893
  }
27391
- createContextCompactionUpdate() {
27392
- return {
27393
- sessionUpdate: "agent_message_chunk",
27394
- content: {
27395
- type: "text",
27396
- text: "Context compacted."
27397
- }
27398
- };
27399
- }
27400
27894
  createPlanUpdate(item) {
27401
27895
  return {
27402
27896
  sessionUpdate: "agent_message_chunk",
@@ -27516,6 +28010,7 @@ ${item.text}`
27516
28010
  cancelSignal,
27517
28011
  signal: abortController.signal,
27518
28012
  currentTurn: null,
28013
+ outcome: null,
27519
28014
  requestCancel: () => {
27520
28015
  if (abortController.signal.aborted) {
27521
28016
  return;
@@ -27539,12 +28034,78 @@ ${item.text}`
27539
28034
  if (this.activePrompts.get(sessionId) === activePrompt) {
27540
28035
  this.activePrompts.delete(sessionId);
27541
28036
  }
28037
+ this.clearPendingSteers(sessionId, activePrompt);
27542
28038
  resolveCompletion();
27543
28039
  }
27544
28040
  };
27545
28041
  this.activePrompts.set(sessionId, activePrompt);
27546
28042
  return activePrompt;
27547
28043
  }
28044
+ clearPendingSteers(sessionId, activePrompt) {
28045
+ const pending = this.pendingSteers.get(sessionId);
28046
+ if (!pending) return;
28047
+ for (const [steerId, steer] of pending) {
28048
+ if (steer.activePrompt === activePrompt) pending.delete(steerId);
28049
+ }
28050
+ if (pending.size === 0) this.pendingSteers.delete(sessionId);
28051
+ }
28052
+ async handleSteerAppliedNotification(sessionId, event, activePrompt) {
28053
+ if (event.method !== "item/completed" || event.params.item.type !== "userMessage") return;
28054
+ const steerId = event.params.item.clientId;
28055
+ if (!steerId) return;
28056
+ const pending = this.pendingSteers.get(sessionId);
28057
+ if (!pending) return;
28058
+ const steer = pending.get(steerId);
28059
+ if (!steer || steer.activePrompt !== activePrompt || steer.turnId !== event.params.turnId) return;
28060
+ pending.delete(steerId);
28061
+ if (pending.size === 0) this.pendingSteers.delete(sessionId);
28062
+ await this.connection.notify(CODEX_STEER_APPLIED_METHOD, { sessionId, steerId });
28063
+ }
28064
+ async steerPrompt(params) {
28065
+ const steerId = getCodexSteerId(params._meta);
28066
+ if (!steerId) throw RequestError.invalidRequest("Missing Codex steer id");
28067
+ const firstText = params.prompt[0]?.type === "text" ? params.prompt[0].text : "";
28068
+ if (firstText.startsWith("/")) {
28069
+ throw RequestError.invalidRequest("Slash commands cannot steer an active Codex turn");
28070
+ }
28071
+ let activePrompt = this.activePrompts.get(params.sessionId);
28072
+ if (!activePrompt) throw RequestError.invalidRequest("No active Codex turn to steer");
28073
+ if (!activePrompt.currentTurn) {
28074
+ await this.pendingTurnStarts.get(params.sessionId)?.promise;
28075
+ activePrompt = this.activePrompts.get(params.sessionId);
28076
+ }
28077
+ const turn = activePrompt?.currentTurn;
28078
+ if (!activePrompt || !turn || activePrompt.signal.aborted) {
28079
+ throw RequestError.invalidRequest("No active Codex turn to steer");
28080
+ }
28081
+ const pending = this.pendingSteers.get(params.sessionId) ?? /* @__PURE__ */ new Map();
28082
+ if (pending.has(steerId)) {
28083
+ throw RequestError.invalidRequest(`Duplicate Codex steer id: ${steerId}`);
28084
+ }
28085
+ pending.set(steerId, { activePrompt, turnId: turn.turnId });
28086
+ this.pendingSteers.set(params.sessionId, pending);
28087
+ try {
28088
+ const steeredTurnId = await this.runWithProcessCheck(
28089
+ () => this.codexAcpClient.sendSteer(params, turn.turnId, steerId)
28090
+ );
28091
+ if (steeredTurnId !== turn.turnId) {
28092
+ throw RequestError.internalError(
28093
+ void 0,
28094
+ `Codex steered unexpected turn ${steeredTurnId}; expected ${turn.turnId}`
28095
+ );
28096
+ }
28097
+ } catch (error48) {
28098
+ if (pending.get(steerId)?.activePrompt === activePrompt) {
28099
+ pending.delete(steerId);
28100
+ if (pending.size === 0) this.pendingSteers.delete(params.sessionId);
28101
+ }
28102
+ throw error48;
28103
+ }
28104
+ if (!activePrompt.outcome) {
28105
+ throw RequestError.internalError(void 0, "Active Codex prompt has no tracked outcome");
28106
+ }
28107
+ return await activePrompt.outcome;
28108
+ }
27548
28109
  cancelBeforeTurnStarted(activePrompt) {
27549
28110
  return activePrompt.cancelSignal.then(() => {
27550
28111
  if (activePrompt.currentTurn === null) {
@@ -27676,6 +28237,22 @@ ${item.text}`
27676
28237
  return turnId;
27677
28238
  }
27678
28239
  async prompt(params, signal) {
28240
+ if (getCodexSteerId(params._meta)) {
28241
+ return await this.steerPrompt(params);
28242
+ }
28243
+ if (this.activePrompts.has(params.sessionId)) {
28244
+ throw RequestError.invalidRequest(
28245
+ "A Codex prompt is already active; use the advertised steer extension"
28246
+ );
28247
+ }
28248
+ const outcome = this.runPrompt(params, signal);
28249
+ const activePrompt = this.activePrompts.get(params.sessionId);
28250
+ if (activePrompt) {
28251
+ activePrompt.outcome = outcome;
28252
+ }
28253
+ return await outcome;
28254
+ }
28255
+ async runPrompt(params, signal) {
27679
28256
  logger.log("Prompt received", {
27680
28257
  sessionId: params.sessionId,
27681
28258
  prompt: params.prompt
@@ -27684,14 +28261,8 @@ ${item.text}`
27684
28261
  sessionState.currentTurnId = null;
27685
28262
  sessionState.lastTokenUsage = null;
27686
28263
  const activePrompt = this.trackActivePrompt(params.sessionId);
27687
- let pendingTurnStart = null;
27688
- const ensurePendingTurnStart = () => {
27689
- if (pendingTurnStart === null) {
27690
- pendingTurnStart = this.createPendingTurnStart();
27691
- this.pendingTurnStarts.set(params.sessionId, pendingTurnStart);
27692
- }
27693
- return pendingTurnStart;
27694
- };
28264
+ const pendingTurnStart = this.createPendingTurnStart();
28265
+ this.pendingTurnStarts.set(params.sessionId, pendingTurnStart);
27695
28266
  const disposePromptRequestCancellation = this.observePromptRequestCancellation(signal, sessionState, activePrompt);
27696
28267
  try {
27697
28268
  const eventHandler = new CodexEventHandler(this.connection, sessionState);
@@ -27705,6 +28276,7 @@ ${item.text}`
27705
28276
  await this.codexAcpClient.subscribeToSessionEvents(
27706
28277
  params.sessionId,
27707
28278
  async (event) => {
28279
+ await this.handleSteerAppliedNotification(params.sessionId, event, activePrompt);
27708
28280
  await elicitationHandler.handleNotification(event);
27709
28281
  return eventHandler.handleNotification(event);
27710
28282
  },
@@ -27715,9 +28287,6 @@ ${item.text}`
27715
28287
  return this.cancelledPromptResponse(sessionState);
27716
28288
  }
27717
28289
  const commandPromise = this.availableCommands.tryHandleCommand(params.prompt, sessionState, {
27718
- onTurnStartPending: () => {
27719
- ensurePendingTurnStart();
27720
- },
27721
28290
  onTurnStarted: (turnId, threadId) => {
27722
28291
  const turn = { threadId, turnId };
27723
28292
  activePrompt.currentTurn = turn;
@@ -27726,7 +28295,7 @@ ${item.text}`
27726
28295
  return;
27727
28296
  }
27728
28297
  sessionState.currentTurnId = turnId;
27729
- pendingTurnStart?.resolve(turnId);
28298
+ pendingTurnStart.resolve(turnId);
27730
28299
  }
27731
28300
  });
27732
28301
  void commandPromise.catch((err) => {
@@ -27780,7 +28349,6 @@ ${item.text}`
27780
28349
  sessionState.currentModelSupportsFast
27781
28350
  );
27782
28351
  const collaborationMode = this.createPlanModeCollaborationMode(sessionState, modelId);
27783
- ensurePendingTurnStart();
27784
28352
  const sendPromptPromise = this.runWithProcessCheck(
27785
28353
  () => this.codexAcpClient.sendPrompt(
27786
28354
  params,
@@ -27799,7 +28367,7 @@ ${item.text}`
27799
28367
  return;
27800
28368
  }
27801
28369
  sessionState.currentTurnId = turnId;
27802
- pendingTurnStart?.resolve(turnId);
28370
+ pendingTurnStart.resolve(turnId);
27803
28371
  },
27804
28372
  () => this.promptShouldStop(params.sessionId, activePrompt)
27805
28373
  )
@@ -27826,6 +28394,10 @@ ${item.text}`
27826
28394
  if (error48) {
27827
28395
  throw error48;
27828
28396
  }
28397
+ await this.publishFallbackSessionTitle(
28398
+ sessionState,
28399
+ this.createPromptFallbackTitle(params.prompt)
28400
+ );
27829
28401
  return {
27830
28402
  stopReason: "end_turn",
27831
28403
  usage: this.buildPromptUsage(sessionState.lastTokenUsage),
@@ -27978,6 +28550,7 @@ var CommandExecutionApprovalRequest = new import_node2.RequestType("item/command
27978
28550
  var FileChangeApprovalRequest = new import_node2.RequestType("item/fileChange/requestApproval");
27979
28551
  var PermissionsApprovalRequest = new import_node2.RequestType("item/permissions/requestApproval");
27980
28552
  var McpServerElicitationRequest = new import_node2.RequestType("mcpServer/elicitation/request");
28553
+ var ToolRequestUserInputRequest = new import_node2.RequestType("item/tool/requestUserInput");
27981
28554
  var GOAL_RUNTIME_EFFECTS_GRACE_MS = 1e3;
27982
28555
  var CodexAppServerClient = class {
27983
28556
  connection;
@@ -28075,6 +28648,16 @@ var CodexAppServerClient = class {
28075
28648
  }
28076
28649
  return await handler.handleElicitation(params);
28077
28650
  });
28651
+ this.connection.onRequest(ToolRequestUserInputRequest, async (params) => {
28652
+ if (this.isStaleTurn(params.threadId, params.turnId)) {
28653
+ return { answers: {} };
28654
+ }
28655
+ const handler = this.elicitationHandlers.get(params.threadId);
28656
+ if (!handler) {
28657
+ return { answers: {} };
28658
+ }
28659
+ return await handler.handleUserInput(params);
28660
+ });
28078
28661
  }
28079
28662
  onApprovalRequest(threadId, handler) {
28080
28663
  this.approvalHandlers.set(threadId, handler);
@@ -28093,6 +28676,9 @@ var CodexAppServerClient = class {
28093
28676
  async turnStart(params) {
28094
28677
  return await this.sendRequest({ method: "turn/start", params });
28095
28678
  }
28679
+ async turnSteer(params) {
28680
+ return await this.sendRequest({ method: "turn/steer", params });
28681
+ }
28096
28682
  async runTurn(params, onTurnStarted) {
28097
28683
  const capturedCompletions = [];
28098
28684
  const releaseCapture = this.captureTurnCompletions(params.threadId, (event) => {
@@ -28378,6 +28964,9 @@ var CodexAppServerClient = class {
28378
28964
  async accountRead(params) {
28379
28965
  return await this.sendRequest({ method: "account/read", params });
28380
28966
  }
28967
+ async accountRateLimitsRead() {
28968
+ return await this.sendRequest({ method: "account/rateLimits/read", params: void 0 });
28969
+ }
28381
28970
  //TODO create type-safe helper
28382
28971
  async awaitTurnCompleted(threadId, turnId) {
28383
28972
  return await new Promise((resolve) => {
@@ -28932,5 +29521,5 @@ function startAcpServer() {
28932
29521
  codexAcpServer = null;
28933
29522
  }
28934
29523
  });
28935
- }).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);
29524
+ }).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.providers.list, (ctx) => getAgent().listProviders(ctx.params)).onRequest(methods.agent.providers.set, (ctx) => getAgent().setProvider(ctx.params)).onRequest(methods.agent.providers.disable, (ctx) => getAgent().disableProvider(ctx.params)).onRequest(methods.agent.session.prompt, (ctx) => getAgent().prompt(ctx.params, ctx.signal)).onNotification(methods.agent.session.cancel, (ctx) => getAgent().cancel(ctx.params)).onRequest("authentication/status", emptyExtensionParamsParser, (ctx) => getAgent().extMethod("authentication/status", ctx.params)).onRequest("authentication/logout", emptyExtensionParamsParser, (ctx) => getAgent().extMethod("authentication/logout", ctx.params)).onRequest(LEGACY_SET_SESSION_MODEL_METHOD, legacySetSessionModelParamsParser, (ctx) => getAgent().extMethod(LEGACY_SET_SESSION_MODEL_METHOD, ctx.params)).connect(acpJsonStream);
28936
29525
  }