lody 0.69.0 → 0.70.0

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