@agentclientprotocol/codex-acp 1.1.2 → 1.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +1 -0
  2. package/dist/index.js +722 -92
  3. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -22592,6 +22592,7 @@ function createTerminalOutputMeta(mode, terminalId, data) {
22592
22592
  }
22593
22593
 
22594
22594
  // src/CodexToolCallMapper.ts
22595
+ var CONTEXT_COMPACTION_META = { contextCompaction: true };
22595
22596
  function toAcpStatus(status) {
22596
22597
  switch (status) {
22597
22598
  case "inProgress":
@@ -22734,6 +22735,35 @@ function createImageGenerationUpdate(item, options) {
22734
22735
  rawOutput: imageGenerationRawOutput(item)
22735
22736
  };
22736
22737
  }
22738
+ function createContextCompactionStartUpdate(item) {
22739
+ return {
22740
+ sessionUpdate: "tool_call",
22741
+ toolCallId: item.id,
22742
+ kind: "other",
22743
+ title: "Context compacting",
22744
+ status: "in_progress",
22745
+ _meta: CONTEXT_COMPACTION_META
22746
+ };
22747
+ }
22748
+ function createContextCompactionCompleteUpdate(item) {
22749
+ return {
22750
+ sessionUpdate: "tool_call_update",
22751
+ toolCallId: item.id,
22752
+ title: "Context compacted",
22753
+ status: "completed",
22754
+ _meta: CONTEXT_COMPACTION_META
22755
+ };
22756
+ }
22757
+ function createCompletedContextCompactionUpdate(item) {
22758
+ return {
22759
+ sessionUpdate: "tool_call",
22760
+ toolCallId: item.id,
22761
+ kind: "other",
22762
+ title: "Context compacted",
22763
+ status: "completed",
22764
+ _meta: CONTEXT_COMPACTION_META
22765
+ };
22766
+ }
22737
22767
  async function createExecuteToolCallUpdate(item, title, rawInput, rawOutput) {
22738
22768
  return {
22739
22769
  sessionUpdate: "tool_call",
@@ -22847,7 +22877,8 @@ function createCollabAgentToolCallUpdate(item) {
22847
22877
  kind: "other",
22848
22878
  title: item.tool,
22849
22879
  status: toAcpStatus(item.status),
22850
- rawInput: createCollabAgentToolCallRawInput(item)
22880
+ rawInput: createCollabAgentToolCallRawInput(item),
22881
+ _meta: createCollabAgentToolCallMeta(item)
22851
22882
  };
22852
22883
  }
22853
22884
  function createCollabAgentToolCallCompleteUpdate(item) {
@@ -22856,7 +22887,8 @@ function createCollabAgentToolCallCompleteUpdate(item) {
22856
22887
  toolCallId: item.id,
22857
22888
  title: item.tool,
22858
22889
  status: toAcpStatus(item.status),
22859
- rawInput: createCollabAgentToolCallRawInput(item)
22890
+ rawInput: createCollabAgentToolCallRawInput(item),
22891
+ _meta: createCollabAgentToolCallMeta(item)
22860
22892
  };
22861
22893
  }
22862
22894
  function createCollabAgentToolCallRawInput(item) {
@@ -22865,9 +22897,66 @@ function createCollabAgentToolCallRawInput(item) {
22865
22897
  senderThreadId: item.senderThreadId,
22866
22898
  receiverThreadIds: item.receiverThreadIds,
22867
22899
  agentsStates: item.agentsStates,
22900
+ model: item.model,
22901
+ reasoningEffort: item.reasoningEffort,
22868
22902
  status: item.status
22869
22903
  };
22870
22904
  }
22905
+ function createCollabAgentToolCallMeta(item) {
22906
+ return {
22907
+ codex: {
22908
+ collaboration: {
22909
+ tool: item.tool,
22910
+ senderThreadId: item.senderThreadId,
22911
+ receiverThreadIds: item.receiverThreadIds
22912
+ }
22913
+ }
22914
+ };
22915
+ }
22916
+ function createSubAgentActivityUpdate(item, status, sessionUpdate) {
22917
+ const name = item.agentPath.split("/").filter(Boolean).at(-1) ?? "subagent";
22918
+ const title = formatSubAgentActivityTitle(item.kind, name);
22919
+ const common = {
22920
+ toolCallId: item.id,
22921
+ status,
22922
+ rawInput: {
22923
+ agentThreadId: item.agentThreadId,
22924
+ agentPath: item.agentPath,
22925
+ activityKind: item.kind
22926
+ },
22927
+ _meta: {
22928
+ codex: {
22929
+ subagent: {
22930
+ threadId: item.agentThreadId,
22931
+ path: item.agentPath,
22932
+ activity: item.kind
22933
+ }
22934
+ }
22935
+ }
22936
+ };
22937
+ if (sessionUpdate === "tool_call") {
22938
+ return {
22939
+ sessionUpdate,
22940
+ title,
22941
+ kind: "other",
22942
+ ...common
22943
+ };
22944
+ }
22945
+ return {
22946
+ sessionUpdate,
22947
+ ...common
22948
+ };
22949
+ }
22950
+ function formatSubAgentActivityTitle(kind, name) {
22951
+ switch (kind) {
22952
+ case "started":
22953
+ return `Start subagent ${name}`;
22954
+ case "interacted":
22955
+ return `Interact with subagent ${name}`;
22956
+ case "interrupted":
22957
+ return `Interrupt subagent ${name}`;
22958
+ }
22959
+ }
22871
22960
  function formatWebSearchTitle(item) {
22872
22961
  const action = item.action;
22873
22962
  if (!action) {
@@ -23273,6 +23362,30 @@ function createAgentTextThoughtChunk(text, messageId, meta3) {
23273
23362
  return createAgentThoughtChunk({ type: "text", text }, messageId, meta3);
23274
23363
  }
23275
23364
 
23365
+ // src/AcpExtensions.ts
23366
+ var LEGACY_SET_SESSION_MODEL_METHOD = "session/set_model";
23367
+ var GOAL_CONTROL_METHOD = "_codex/session/goal_control";
23368
+ function isExtMethodRequest(request) {
23369
+ return request.method === "authentication/status" || request.method === "authentication/logout" || request.method === LEGACY_SET_SESSION_MODEL_METHOD || request.method === GOAL_CONTROL_METHOD;
23370
+ }
23371
+
23372
+ // src/ThreadGoalSnapshot.ts
23373
+ function toThreadGoalSnapshot(goal) {
23374
+ return {
23375
+ objective: goal.objective.trim(),
23376
+ status: goal.status,
23377
+ tokenBudget: goal.tokenBudget,
23378
+ timeUsedSeconds: goal.timeUsedSeconds,
23379
+ createdAt: goal.createdAt,
23380
+ controlMethod: GOAL_CONTROL_METHOD
23381
+ };
23382
+ }
23383
+ function sameThreadGoalSnapshot(left, right) {
23384
+ if (left === void 0) return false;
23385
+ if (left === null || right === null) return left === right;
23386
+ return left.objective === right.objective && left.status === right.status && left.tokenBudget === right.tokenBudget && left.createdAt === right.createdAt;
23387
+ }
23388
+
23276
23389
  // src/CodexEventHandler.ts
23277
23390
  var CodexEventHandler = class {
23278
23391
  connection;
@@ -23286,6 +23399,7 @@ var CodexEventHandler = class {
23286
23399
  terminalCommandIds = /* @__PURE__ */ new Set();
23287
23400
  terminalCommandOutputIds = /* @__PURE__ */ new Set();
23288
23401
  agentMessagePhases = /* @__PURE__ */ new Map();
23402
+ activeSubAgentActivities = /* @__PURE__ */ new Set();
23289
23403
  constructor(connection, sessionState) {
23290
23404
  this.connection = connection;
23291
23405
  this.sessionState = sessionState;
@@ -23321,6 +23435,8 @@ var CodexEventHandler = class {
23321
23435
  case "thread/tokenUsage/updated":
23322
23436
  return this.createUsageUpdate(notification.params);
23323
23437
  case "thread/name/updated":
23438
+ this.sessionState.sessionTitle = notification.params.threadName ?? null;
23439
+ this.sessionState.sessionTitleSource = notification.params.threadName == null ? "unset" : "explicit";
23324
23440
  return {
23325
23441
  sessionUpdate: "session_info_update",
23326
23442
  title: notification.params.threadName ?? null
@@ -23451,8 +23567,9 @@ ${event.details}` : "";
23451
23567
  `);
23452
23568
  }
23453
23569
  createThreadGoalUpdatedEvent(event) {
23454
- const goalSnapshot = this.createThreadGoalSnapshot(event);
23455
- if (this.sameThreadGoalSnapshot(this.sessionState.currentGoal, goalSnapshot)) {
23570
+ this.sessionState.goalRevision += 1;
23571
+ const goalSnapshot = toThreadGoalSnapshot(event.goal);
23572
+ if (sameThreadGoalSnapshot(this.sessionState.currentGoal, goalSnapshot)) {
23456
23573
  return null;
23457
23574
  }
23458
23575
  this.sessionState.currentGoal = goalSnapshot;
@@ -23461,6 +23578,7 @@ ${event.details}` : "";
23461
23578
  });
23462
23579
  }
23463
23580
  createThreadGoalClearedEvent(_event) {
23581
+ this.sessionState.goalRevision += 1;
23464
23582
  if (this.sessionState.currentGoal === null) {
23465
23583
  return null;
23466
23584
  }
@@ -23469,16 +23587,6 @@ ${event.details}` : "";
23469
23587
  goal: null
23470
23588
  });
23471
23589
  }
23472
- createThreadGoalSnapshot(event) {
23473
- return {
23474
- objective: event.goal.objective.trim(),
23475
- status: event.goal.status,
23476
- tokenBudget: event.goal.tokenBudget
23477
- };
23478
- }
23479
- sameThreadGoalSnapshot(left, right) {
23480
- return left !== null && left !== void 0 && left.objective === right.objective && left.status === right.status && left.tokenBudget === right.tokenBudget;
23481
- }
23482
23590
  createReasoningDeltaEvent(event) {
23483
23591
  this.seenReasoningDeltaItemIds.add(event.itemId);
23484
23592
  return this.createAgentThoughtEvent(event.delta, event.itemId);
@@ -23520,14 +23628,17 @@ ${event.details}` : "";
23520
23628
  case "agentMessage":
23521
23629
  this.rememberAgentMessagePhase(event.item);
23522
23630
  return null;
23631
+ case "contextCompaction":
23632
+ return createContextCompactionStartUpdate(event.item);
23523
23633
  case "subAgentActivity":
23634
+ this.activeSubAgentActivities.add(event.item.id);
23635
+ return createSubAgentActivityUpdate(event.item, "in_progress", "tool_call");
23524
23636
  case "sleep":
23525
23637
  case "userMessage":
23526
23638
  case "hookPrompt":
23527
23639
  case "reasoning":
23528
23640
  case "enteredReviewMode":
23529
23641
  case "exitedReviewMode":
23530
- case "contextCompaction":
23531
23642
  case "plan":
23532
23643
  return null;
23533
23644
  }
@@ -23576,9 +23687,12 @@ ${event.details}` : "";
23576
23687
  case "exitedReviewMode":
23577
23688
  return this.createExitedReviewModeEvent(event.item);
23578
23689
  case "contextCompaction":
23579
- return this.createContextCompactedEvent();
23690
+ return createContextCompactionCompleteUpdate(event.item);
23580
23691
  //ignored types
23581
- case "subAgentActivity":
23692
+ case "subAgentActivity": {
23693
+ const sessionUpdate = this.activeSubAgentActivities.delete(event.item.id) ? "tool_call_update" : "tool_call";
23694
+ return createSubAgentActivityUpdate(event.item, "completed", sessionUpdate);
23695
+ }
23582
23696
  case "sleep":
23583
23697
  case "userMessage":
23584
23698
  case "hookPrompt":
@@ -23726,7 +23840,15 @@ ${event.stdin}
23726
23840
  }
23727
23841
  async createErrorEvent(params) {
23728
23842
  const error51 = params.error.codexErrorInfo;
23729
- if (error51 === "usageLimitExceeded") {
23843
+ if (params.willRetry) {
23844
+ return this.createCodexSessionInfoUpdate({
23845
+ error: {
23846
+ ...params.error,
23847
+ turnId: params.turnId,
23848
+ willRetry: true
23849
+ }
23850
+ });
23851
+ } else if (error51 === "usageLimitExceeded") {
23730
23852
  this.failure = RequestError.internalError(
23731
23853
  this.createTurnErrorData(params.error)
23732
23854
  );
@@ -24158,6 +24280,7 @@ var ELICITATION_OPTIONS = [
24158
24280
  { optionId: "accept", name: "Accept", kind: "allow_once" },
24159
24281
  { optionId: "decline", name: "Decline", kind: "reject_once" }
24160
24282
  ];
24283
+ var USER_INPUT_OTHER_FIELD_SUFFIX = "__other";
24161
24284
  function parsePersistOptions(meta3) {
24162
24285
  const result = /* @__PURE__ */ new Set();
24163
24286
  if (!meta3 || typeof meta3 !== "object") return result;
@@ -24293,6 +24416,27 @@ function elicitationResponseMeta(response, context, persist = void 0) {
24293
24416
  }
24294
24417
  return Object.keys(meta3).length === 0 ? null : meta3;
24295
24418
  }
24419
+ function userInputOtherFieldId(questionId, questionIds) {
24420
+ const base = `${questionId}${USER_INPUT_OTHER_FIELD_SUFFIX}`;
24421
+ if (!questionIds.has(base)) {
24422
+ return base;
24423
+ }
24424
+ let index = 1;
24425
+ while (questionIds.has(`${base}${index}`)) {
24426
+ index += 1;
24427
+ }
24428
+ return `${base}${index}`;
24429
+ }
24430
+ function userInputResponseValue(content, fieldId) {
24431
+ const value = content[fieldId];
24432
+ if (typeof value === "string" && value.trim() === "") {
24433
+ return void 0;
24434
+ }
24435
+ if (Array.isArray(value) && value.length === 0) {
24436
+ return void 0;
24437
+ }
24438
+ return value;
24439
+ }
24296
24440
  function buildToolApprovalOptions(persistOptions) {
24297
24441
  const options = [
24298
24442
  { optionId: McpApprovalOptionId.AllowOnce, name: "Allow", kind: "allow_once" }
@@ -24389,8 +24533,68 @@ var CodexElicitationHandler = class {
24389
24533
  return { action: "cancel", content: null, _meta: null };
24390
24534
  }
24391
24535
  }
24392
- requestOptions() {
24393
- return this.cancellationSignal ? { cancellationSignal: this.cancellationSignal } : void 0;
24536
+ async handleUserInput(params) {
24537
+ if (!clientSupportsFormElicitation(this.clientCapabilities)) {
24538
+ return { answers: {} };
24539
+ }
24540
+ try {
24541
+ const response = await this.requestUserInputElicitation(params);
24542
+ if (response === null) {
24543
+ return { answers: {} };
24544
+ }
24545
+ return this.convertUserInputResponse(response, params);
24546
+ } catch (error51) {
24547
+ logger.error("Error handling Codex user input request", error51);
24548
+ return { answers: {} };
24549
+ }
24550
+ }
24551
+ requestOptions(cancellationSignal = this.cancellationSignal) {
24552
+ return cancellationSignal ? { cancellationSignal } : void 0;
24553
+ }
24554
+ async requestUserInputElicitation(params) {
24555
+ const request = this.buildUserInputRequest(params);
24556
+ if (params.autoResolutionMs === null) {
24557
+ return await this.connection.request(
24558
+ methods.client.elicitation.create,
24559
+ request,
24560
+ this.requestOptions()
24561
+ );
24562
+ }
24563
+ const abortController = new AbortController();
24564
+ let timeout;
24565
+ let removeAbortListener;
24566
+ const timeoutPromise = new Promise((resolve) => {
24567
+ const resolveWithoutInput = () => {
24568
+ abortController.abort();
24569
+ resolve(null);
24570
+ };
24571
+ timeout = setTimeout(resolveWithoutInput, Math.max(0, params.autoResolutionMs ?? 0));
24572
+ if (this.cancellationSignal?.aborted) {
24573
+ resolveWithoutInput();
24574
+ return;
24575
+ }
24576
+ if (this.cancellationSignal) {
24577
+ this.cancellationSignal.addEventListener("abort", resolveWithoutInput, { once: true });
24578
+ removeAbortListener = () => {
24579
+ this.cancellationSignal?.removeEventListener("abort", resolveWithoutInput);
24580
+ };
24581
+ }
24582
+ });
24583
+ const requestPromise = Promise.resolve(this.connection.request(
24584
+ methods.client.elicitation.create,
24585
+ request,
24586
+ this.requestOptions(abortController.signal)
24587
+ ));
24588
+ void requestPromise.catch(() => {
24589
+ });
24590
+ try {
24591
+ return await Promise.race([requestPromise, timeoutPromise]);
24592
+ } finally {
24593
+ if (timeout) {
24594
+ clearTimeout(timeout);
24595
+ }
24596
+ removeAbortListener?.();
24597
+ }
24394
24598
  }
24395
24599
  createMcpElicitationContext(params) {
24396
24600
  const isToolApproval = isMcpToolCallApproval(params._meta);
@@ -24436,6 +24640,72 @@ var CodexElicitationHandler = class {
24436
24640
  };
24437
24641
  }
24438
24642
  }
24643
+ buildUserInputRequest(params) {
24644
+ const properties = {};
24645
+ const required2 = [];
24646
+ const questionIds = new Set(params.questions.map((question) => question.id));
24647
+ for (const question of params.questions) {
24648
+ const options = question.options ?? [];
24649
+ const hasOptions = options.length > 0;
24650
+ const hasOtherAnswer = question.isOther && hasOptions;
24651
+ const base = {
24652
+ title: question.header || question.id,
24653
+ description: question.question,
24654
+ _meta: {
24655
+ codex: {
24656
+ isOther: question.isOther,
24657
+ isSecret: question.isSecret
24658
+ }
24659
+ }
24660
+ };
24661
+ if (!hasOtherAnswer) {
24662
+ required2.push(question.id);
24663
+ }
24664
+ properties[question.id] = hasOptions ? {
24665
+ ...base,
24666
+ type: "string",
24667
+ oneOf: options.map((option) => ({
24668
+ const: option.label,
24669
+ title: option.label,
24670
+ description: option.description
24671
+ }))
24672
+ } : {
24673
+ ...base,
24674
+ type: "string"
24675
+ };
24676
+ if (hasOtherAnswer) {
24677
+ properties[userInputOtherFieldId(question.id, questionIds)] = {
24678
+ type: "string",
24679
+ title: "Other",
24680
+ description: "Type your own answer instead of choosing an option above.",
24681
+ _meta: {
24682
+ codex: {
24683
+ questionId: question.id,
24684
+ isOtherAnswer: true,
24685
+ isSecret: question.isSecret
24686
+ }
24687
+ }
24688
+ };
24689
+ }
24690
+ }
24691
+ const firstQuestion = params.questions[0];
24692
+ return {
24693
+ sessionId: this.sessionState.sessionId,
24694
+ toolCallId: params.itemId,
24695
+ mode: "form",
24696
+ message: params.questions.length === 1 && firstQuestion ? firstQuestion.question : "Input requested",
24697
+ requestedSchema: {
24698
+ type: "object",
24699
+ properties,
24700
+ required: required2
24701
+ },
24702
+ _meta: {
24703
+ codex: {
24704
+ autoResolutionMs: params.autoResolutionMs
24705
+ }
24706
+ }
24707
+ };
24708
+ }
24439
24709
  buildPermissionRequest(params, context) {
24440
24710
  const sessionId = this.sessionState.sessionId;
24441
24711
  const messageContent = {
@@ -24533,6 +24803,24 @@ var CodexElicitationHandler = class {
24533
24803
  }
24534
24804
  return { action: "cancel", content: null, _meta: null };
24535
24805
  }
24806
+ convertUserInputResponse(response, params) {
24807
+ if (!CreateElicitationResponse.isAccept(response)) {
24808
+ return { answers: {} };
24809
+ }
24810
+ const answers = {};
24811
+ const content = contentRecord(response.content);
24812
+ const questionIds = new Set(params.questions.map((question) => question.id));
24813
+ for (const question of params.questions) {
24814
+ 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);
24815
+ if (value === void 0) {
24816
+ continue;
24817
+ }
24818
+ answers[question.id] = {
24819
+ answers: Array.isArray(value) ? value.map(String) : [String(value)]
24820
+ };
24821
+ }
24822
+ return { answers };
24823
+ }
24536
24824
  async publishAcceptedMcpToolApproval(context, accepted) {
24537
24825
  if (!accepted || context.correlatedCallId === void 0) {
24538
24826
  return;
@@ -25399,7 +25687,7 @@ var package_default = {
25399
25687
  publishConfig: {
25400
25688
  access: "public"
25401
25689
  },
25402
- version: "1.1.2",
25690
+ version: "1.1.5",
25403
25691
  description: "",
25404
25692
  main: "dist/index.js",
25405
25693
  bin: {
@@ -25458,7 +25746,7 @@ var package_default = {
25458
25746
  },
25459
25747
  dependencies: {
25460
25748
  "@agentclientprotocol/sdk": "^1.2.1",
25461
- "@openai/codex": "^0.144.0",
25749
+ "@openai/codex": "^0.144.6",
25462
25750
  diff: "^9.0.0",
25463
25751
  open: "^11.0.0",
25464
25752
  "vscode-jsonrpc": "^9.0.1",
@@ -25466,7 +25754,46 @@ var package_default = {
25466
25754
  }
25467
25755
  };
25468
25756
 
25757
+ // src/CollaborationModeConfig.ts
25758
+ var COLLABORATION_MODE_CONFIG_ID = "collaboration_mode";
25759
+ var DEFAULT_COLLABORATION_MODE = "default";
25760
+ var PLAN_COLLABORATION_MODE = "plan";
25761
+ function createCollaborationModeConfigOption(currentValue) {
25762
+ return {
25763
+ id: COLLABORATION_MODE_CONFIG_ID,
25764
+ name: "Collaboration mode",
25765
+ description: "How Codex collaborates for subsequent turns",
25766
+ category: "collaboration_mode",
25767
+ type: "select",
25768
+ currentValue,
25769
+ options: [
25770
+ { value: DEFAULT_COLLABORATION_MODE, name: "Default" },
25771
+ { value: PLAN_COLLABORATION_MODE, name: "Plan", description: "Plan before making changes" }
25772
+ ]
25773
+ };
25774
+ }
25775
+ function parseCollaborationMode(value) {
25776
+ if (value === DEFAULT_COLLABORATION_MODE) return DEFAULT_COLLABORATION_MODE;
25777
+ if (value === PLAN_COLLABORATION_MODE) return PLAN_COLLABORATION_MODE;
25778
+ return null;
25779
+ }
25780
+ function createCodexCollaborationMode(mode, currentModelId) {
25781
+ const modelId = ModelId.fromString(currentModelId);
25782
+ return {
25783
+ mode,
25784
+ settings: {
25785
+ model: modelId.model,
25786
+ reasoning_effort: modelId.effort,
25787
+ developer_instructions: null
25788
+ }
25789
+ };
25790
+ }
25791
+
25469
25792
  // src/CodexAcpClient.ts
25793
+ var CUSTOM_GATEWAY_PROVIDER_ID = "custom-gateway";
25794
+ var SUPPORTED_GATEWAY_PROTOCOLS = {
25795
+ openai: "responses"
25796
+ };
25470
25797
  var CodexAcpClient = class {
25471
25798
  codexClient;
25472
25799
  config;
@@ -25476,6 +25803,7 @@ var CodexAcpClient = class {
25476
25803
  pendingAccountUpdated = null;
25477
25804
  sessionNotificationQueues = /* @__PURE__ */ new Map();
25478
25805
  skillExtraRoots = [];
25806
+ configPath = null;
25479
25807
  constructor(codexClient, codexConfig, modelProvider) {
25480
25808
  this.codexClient = codexClient;
25481
25809
  this.config = codexConfig ?? {};
@@ -25488,19 +25816,27 @@ var CodexAcpClient = class {
25488
25816
  version: `${package_default.version}`
25489
25817
  };
25490
25818
  async initialize(request) {
25491
- await this.codexClient.initialize({
25492
- capabilities: null,
25819
+ const response = await this.codexClient.initialize({
25820
+ capabilities: {
25821
+ experimentalApi: true,
25822
+ requestAttestation: false
25823
+ },
25493
25824
  clientInfo: {
25494
25825
  name: request.clientInfo?.name ?? this.defaultClientInfo.name,
25495
25826
  version: request.clientInfo?.version ?? this.defaultClientInfo.version,
25496
25827
  title: request.clientInfo?.title ?? this.defaultClientInfo.title
25497
25828
  }
25498
25829
  });
25830
+ this.configPath = response?.codexHome ?? null;
25831
+ }
25832
+ getHomePath() {
25833
+ return this.configPath;
25499
25834
  }
25500
25835
  async authenticate(authRequest) {
25501
25836
  if (!isCodexAuthRequest(authRequest)) {
25502
25837
  throw RequestError.invalidRequest();
25503
25838
  }
25839
+ this.gatewayConfig = null;
25504
25840
  switch (authRequest.methodId) {
25505
25841
  case "api-key": {
25506
25842
  const apiKey = authRequest._meta?.["api-key"]?.apiKey ?? this.readApiKeyFromEnv();
@@ -25509,7 +25845,6 @@ var CodexAcpClient = class {
25509
25845
  case "chat-gpt": {
25510
25846
  const accountResponse = await this.codexClient.accountRead({ refreshToken: true });
25511
25847
  if (accountResponse.account?.type === "chatgpt") {
25512
- this.gatewayConfig = null;
25513
25848
  return true;
25514
25849
  }
25515
25850
  const loginCompletedPromise = this.awaitNextLoginCompleted();
@@ -25517,7 +25852,6 @@ var CodexAcpClient = class {
25517
25852
  if (loginResponse.type == "chatgpt") {
25518
25853
  await open_default(loginResponse.authUrl);
25519
25854
  }
25520
- this.gatewayConfig = null;
25521
25855
  const result = await loginCompletedPromise;
25522
25856
  return result.success;
25523
25857
  }
@@ -25525,25 +25859,14 @@ var CodexAcpClient = class {
25525
25859
  if (!authRequest._meta) throw RequestError.invalidRequest();
25526
25860
  const gatewaySettings = authRequest._meta["gateway"];
25527
25861
  if (!gatewaySettings) throw RequestError.invalidRequest();
25528
- const baseUrl = gatewaySettings.baseUrl;
25529
- const providerName = typeof gatewaySettings.providerName === "string" && gatewaySettings.providerName.trim().length > 0 ? gatewaySettings.providerName : "User-provided gateway";
25530
- const headers = {
25531
- "X-Client-Feature-ID": "codex",
25532
- ...gatewaySettings.headers
25533
- };
25534
- this.gatewayConfig = {
25535
- modelProvider: "custom-gateway",
25536
- config: {
25537
- name: providerName,
25538
- base_url: baseUrl,
25539
- http_headers: headers,
25540
- wire_api: "responses"
25541
- }
25542
- };
25862
+ this.applyGatewayConfig({
25863
+ baseUrl: gatewaySettings.baseUrl,
25864
+ apiType: GatewayAuthMethod._meta.gateway.protocol,
25865
+ headers: gatewaySettings.headers,
25866
+ providerName: gatewaySettings.providerName
25867
+ });
25543
25868
  return true;
25544
25869
  }
25545
- this.gatewayConfig = null;
25546
- return false;
25547
25870
  }
25548
25871
  async authenticateWithApiKey(apiKey) {
25549
25872
  const loginCompletedPromise = this.awaitNextLoginCompleted();
@@ -25551,7 +25874,6 @@ var CodexAcpClient = class {
25551
25874
  type: "apiKey",
25552
25875
  apiKey
25553
25876
  });
25554
- this.gatewayConfig = null;
25555
25877
  const result = await loginCompletedPromise;
25556
25878
  return result.success;
25557
25879
  }
@@ -25618,8 +25940,83 @@ var CodexAcpClient = class {
25618
25940
  const response = await this.codexClient.accountRead({ refreshToken: false });
25619
25941
  return response.requiresOpenaiAuth && !response.account;
25620
25942
  }
25621
- hasGatewayAuth() {
25622
- return this.gatewayConfig !== null;
25943
+ /**
25944
+ * Validates and stores custom gateway routing. Shared by the `gateway` auth
25945
+ * method and the ACP `providers/set` method. Throws `invalid_params` for an
25946
+ * unsupported protocol or a malformed base URL.
25947
+ */
25948
+ applyGatewayConfig(params) {
25949
+ const apiType = params.apiType;
25950
+ const wireApi = SUPPORTED_GATEWAY_PROTOCOLS[apiType];
25951
+ if (!wireApi) {
25952
+ throw RequestError.invalidParams(
25953
+ { apiType },
25954
+ `Unsupported provider apiType "${apiType}"; supported: ${Object.keys(SUPPORTED_GATEWAY_PROTOCOLS).join(", ")}`
25955
+ );
25956
+ }
25957
+ if (typeof params.baseUrl !== "string" || params.baseUrl.trim().length === 0) {
25958
+ throw RequestError.invalidParams(void 0, "baseUrl must be a non-empty string");
25959
+ }
25960
+ const providerName = typeof params.providerName === "string" && params.providerName.trim().length > 0 ? params.providerName : "User-provided gateway";
25961
+ const headers = {
25962
+ "X-Client-Feature-ID": "codex",
25963
+ ...params.headers
25964
+ };
25965
+ this.gatewayConfig = {
25966
+ modelProvider: CUSTOM_GATEWAY_PROVIDER_ID,
25967
+ config: {
25968
+ name: providerName,
25969
+ base_url: params.baseUrl,
25970
+ http_headers: headers,
25971
+ wire_api: wireApi
25972
+ }
25973
+ };
25974
+ }
25975
+ /**
25976
+ * `providers/list`: returns the single client-configurable custom gateway
25977
+ * provider. `current` carries only non-secret routing (never headers), and is
25978
+ * `null` when the provider is not configured/disabled.
25979
+ */
25980
+ listProviders() {
25981
+ const gatewayConfig = this.gatewayConfig;
25982
+ const current = gatewayConfig ? {
25983
+ apiType: gatewayApiTypeFromConfig(gatewayConfig),
25984
+ baseUrl: gatewayConfig.config.base_url
25985
+ } : null;
25986
+ return [
25987
+ {
25988
+ providerId: CUSTOM_GATEWAY_PROVIDER_ID,
25989
+ supported: Object.keys(SUPPORTED_GATEWAY_PROTOCOLS),
25990
+ required: false,
25991
+ current
25992
+ }
25993
+ ];
25994
+ }
25995
+ /**
25996
+ * `providers/set`: replaces the full configuration for the custom gateway
25997
+ * provider. Rejects unknown provider ids with `invalid_params`.
25998
+ */
25999
+ setProvider(request) {
26000
+ if (request.providerId !== CUSTOM_GATEWAY_PROVIDER_ID) {
26001
+ throw RequestError.invalidParams(
26002
+ { providerId: request.providerId },
26003
+ `Unknown providerId "${request.providerId}"; only "${CUSTOM_GATEWAY_PROVIDER_ID}" is configurable`
26004
+ );
26005
+ }
26006
+ this.applyGatewayConfig({
26007
+ apiType: request.apiType,
26008
+ baseUrl: request.baseUrl,
26009
+ headers: request.headers
26010
+ });
26011
+ }
26012
+ /**
26013
+ * `providers/disable`: disables the custom gateway provider. Disabling an
26014
+ * unknown provider id is idempotent success (RFD behavior §7).
26015
+ */
26016
+ disableProvider(request) {
26017
+ if (request.providerId === CUSTOM_GATEWAY_PROVIDER_ID) {
26018
+ this.gatewayConfig = null;
26019
+ }
25623
26020
  }
25624
26021
  async getAccount() {
25625
26022
  return this.codexClient.accountRead({ refreshToken: false });
@@ -25640,6 +26037,7 @@ var CodexAcpClient = class {
25640
26037
  sessionId: request.sessionId,
25641
26038
  currentModelId,
25642
26039
  models: codexModels,
26040
+ collaborationMode: this.getCollaborationMode(response.thread.id),
25643
26041
  modelProvider: response.modelProvider,
25644
26042
  currentServiceTier: response.serviceTier ?? null,
25645
26043
  additionalDirectories
@@ -25665,6 +26063,7 @@ var CodexAcpClient = class {
25665
26063
  sessionId: request.sessionId,
25666
26064
  currentModelId,
25667
26065
  models: codexModels,
26066
+ collaborationMode: this.getCollaborationMode(response.thread.id),
25668
26067
  modelProvider: response.modelProvider,
25669
26068
  currentServiceTier: response.serviceTier ?? null,
25670
26069
  thread: historyResponse.thread,
@@ -25688,6 +26087,7 @@ var CodexAcpClient = class {
25688
26087
  sessionId: response.thread.id,
25689
26088
  currentModelId,
25690
26089
  models: codexModels,
26090
+ collaborationMode: this.getCollaborationMode(response.thread.id),
25691
26091
  modelProvider: response.modelProvider,
25692
26092
  currentServiceTier: response.serviceTier ?? null,
25693
26093
  additionalDirectories
@@ -25713,6 +26113,10 @@ var CodexAcpClient = class {
25713
26113
  async runCompact(sessionId) {
25714
26114
  await this.codexClient.runCompact({ threadId: sessionId });
25715
26115
  }
26116
+ async getGoal(sessionId) {
26117
+ const response = await this.codexClient.threadGoalGet({ threadId: sessionId });
26118
+ return response?.goal ?? null;
26119
+ }
25716
26120
  async setGoal(sessionId, objective, onTurnStarted) {
25717
26121
  return await this.codexClient.runGoalSet({
25718
26122
  threadId: sessionId,
@@ -25721,10 +26125,17 @@ var CodexAcpClient = class {
25721
26125
  }, onTurnStarted);
25722
26126
  }
25723
26127
  async setGoalStatus(sessionId, status) {
26128
+ let updatedGoal = null;
25724
26129
  await this.codexClient.runGoalSet({
25725
26130
  threadId: sessionId,
25726
26131
  status
26132
+ }, void 0, void 0, (goal) => {
26133
+ updatedGoal = goal;
25727
26134
  });
26135
+ if (updatedGoal === null) {
26136
+ throw new Error(`Goal update for session ${sessionId} returned no goal`);
26137
+ }
26138
+ return updatedGoal;
25728
26139
  }
25729
26140
  async resumeGoal(sessionId, onTurnStarted) {
25730
26141
  return await this.codexClient.runGoalSet({
@@ -25772,11 +26183,16 @@ var CodexAcpClient = class {
25772
26183
  }
25773
26184
  async getConfigMcpServerNames(projectPath) {
25774
26185
  const response = await this.codexClient.configRead({ includeLayers: true, cwd: projectPath });
25775
- const mcpServers = response?.config?.["mcp_servers"];
25776
- if (!mcpServers || typeof mcpServers !== "object" || Array.isArray(mcpServers)) {
26186
+ const effectiveMcpServers = response?.config?.["mcp_servers"];
26187
+ const configLayers = response?.layers ?? [];
26188
+ const layerMcpServers = configLayers.map((layer) => {
26189
+ return isJsonObject(layer.config) ? layer.config["mcp_servers"] : void 0;
26190
+ });
26191
+ const configuredMcpServers = [effectiveMcpServers, ...layerMcpServers].filter(isJsonObject);
26192
+ if (configuredMcpServers.length === 0) {
25777
26193
  return /* @__PURE__ */ new Set();
25778
26194
  }
25779
- return new Set(Object.keys(mcpServers));
26195
+ return new Set(configuredMcpServers.flatMap((server) => Object.keys(server)));
25780
26196
  }
25781
26197
  getModelProvider() {
25782
26198
  return this.gatewayConfig?.modelProvider ?? this.modelProvider;
@@ -25861,6 +26277,10 @@ var CodexAcpClient = class {
25861
26277
  handleElicitation: async (params) => {
25862
26278
  await this.waitForSessionNotifications(sessionId);
25863
26279
  return await elicitationHandler.handleElicitation(params);
26280
+ },
26281
+ handleUserInput: async (params) => {
26282
+ await this.waitForSessionNotifications(sessionId);
26283
+ return await elicitationHandler.handleUserInput(params);
25864
26284
  }
25865
26285
  });
25866
26286
  }
@@ -25906,6 +26326,15 @@ var CodexAcpClient = class {
25906
26326
  serviceTier
25907
26327
  }, onTurnStarted);
25908
26328
  }
26329
+ async setCollaborationMode(sessionId, mode, currentModelId) {
26330
+ await this.codexClient.threadSettingsUpdate({
26331
+ threadId: sessionId,
26332
+ collaborationMode: createCodexCollaborationMode(mode, currentModelId)
26333
+ });
26334
+ }
26335
+ getCollaborationMode(sessionId) {
26336
+ return this.codexClient.getThreadSettings(sessionId)?.collaborationMode.mode ?? "default";
26337
+ }
25909
26338
  resolveTurnInterrupted(params) {
25910
26339
  this.codexClient.resolveTurnInterrupted(params.threadId, params.turnId);
25911
26340
  }
@@ -26023,7 +26452,7 @@ var CodexAcpClient = class {
26023
26452
  const [allProviders, archivedAllProviders, customGateway] = await Promise.all([
26024
26453
  this.codexClient.threadList({}),
26025
26454
  this.codexClient.threadList({ archived: true }),
26026
- this.codexClient.threadList({ modelProviders: ["custom-gateway"] })
26455
+ this.codexClient.threadList({ modelProviders: [CUSTOM_GATEWAY_PROVIDER_ID] })
26027
26456
  ]);
26028
26457
  return {
26029
26458
  allProviders: {
@@ -26176,6 +26605,11 @@ function arraysEqual(left, right) {
26176
26605
  function isJsonObject(value) {
26177
26606
  return value !== null && typeof value === "object" && !Array.isArray(value);
26178
26607
  }
26608
+ function gatewayApiTypeFromConfig(gatewayConfig) {
26609
+ const wireApi = gatewayConfig.config.wire_api;
26610
+ const match = Object.entries(SUPPORTED_GATEWAY_PROTOCOLS).find(([, wire]) => wire === wireApi);
26611
+ return match?.[0] ?? "openai";
26612
+ }
26179
26613
  function mergeGatewayConfig(config2, gatewayConfig) {
26180
26614
  if (gatewayConfig !== null) {
26181
26615
  const newConfig = { ...config2 };
@@ -26194,6 +26628,9 @@ function mergeGatewayConfig(config2, gatewayConfig) {
26194
26628
  // src/ModelConfigOption.ts
26195
26629
  var MODEL_CONFIG_ID = "model";
26196
26630
  var REASONING_EFFORT_CONFIG_ID = "reasoning_effort";
26631
+ function capitalize(value) {
26632
+ return value.charAt(0).toUpperCase() + value.slice(1);
26633
+ }
26197
26634
  function findSupportedEffort(options, effort) {
26198
26635
  if (!effort) return void 0;
26199
26636
  return options.find((o) => o.reasoningEffort === effort)?.reasoningEffort;
@@ -26231,7 +26668,7 @@ function createReasoningEffortConfigOption(supportedReasoningEfforts, currentEff
26231
26668
  currentValue: currentEffort,
26232
26669
  options: supportedReasoningEfforts.map((option) => ({
26233
26670
  value: option.reasoningEffort,
26234
- name: option.reasoningEffort,
26671
+ name: capitalize(option.reasoningEffort),
26235
26672
  description: option.description
26236
26673
  }))
26237
26674
  };
@@ -26295,6 +26732,20 @@ var CodexCommands = class {
26295
26732
  */
26296
26733
  getBuiltinCommands() {
26297
26734
  return [
26735
+ {
26736
+ name: "plan",
26737
+ description: "Turn plan mode on.",
26738
+ input: null,
26739
+ _meta: {
26740
+ commandAction: {
26741
+ kind: "setConfigOption",
26742
+ configId: COLLABORATION_MODE_CONFIG_ID,
26743
+ value: PLAN_COLLABORATION_MODE,
26744
+ resetValue: DEFAULT_COLLABORATION_MODE,
26745
+ presentation: "state"
26746
+ }
26747
+ }
26748
+ },
26298
26749
  {
26299
26750
  name: "mcp",
26300
26751
  description: "List configured Model Context Protocol (MCP) tools.",
@@ -26332,8 +26783,14 @@ var CodexCommands = class {
26332
26783
  },
26333
26784
  {
26334
26785
  name: "goal",
26335
- description: "Set, pause, resume, or clear a task goal.",
26336
- input: { hint: "[<objective>|clear|pause|resume]" }
26786
+ description: "Set a goal to keep pursuing.",
26787
+ input: { hint: "[<objective>|clear|pause|resume]" },
26788
+ _meta: {
26789
+ commandAction: {
26790
+ kind: "prefixPrompt",
26791
+ presentation: "state"
26792
+ }
26793
+ }
26337
26794
  },
26338
26795
  {
26339
26796
  name: "logout",
@@ -26363,6 +26820,15 @@ var CodexCommands = class {
26363
26820
  if (commandName.startsWith("$")) return { handled: false };
26364
26821
  const sessionId = sessionState.sessionId;
26365
26822
  switch (commandName) {
26823
+ case "plan": {
26824
+ if (command.rest.length > 0) {
26825
+ await this.sendCommandUsageMessage(commandName, "no arguments", sessionId);
26826
+ return { handled: true };
26827
+ }
26828
+ const mode = sessionState.collaborationMode === PLAN_COLLABORATION_MODE ? DEFAULT_COLLABORATION_MODE : PLAN_COLLABORATION_MODE;
26829
+ await options.setConfigOption?.(COLLABORATION_MODE_CONFIG_ID, mode);
26830
+ return { handled: options.setConfigOption !== void 0 };
26831
+ }
26366
26832
  case "compact": {
26367
26833
  await this.runWithProcessCheck(() => this.codexAcpClient.runCompact(sessionId));
26368
26834
  return { handled: true };
@@ -26438,8 +26904,7 @@ var CodexCommands = class {
26438
26904
  return { handled: true };
26439
26905
  }
26440
26906
  default:
26441
- await this.sendUnknownCommandMessage(commandName, sessionId);
26442
- return { handled: true };
26907
+ return { handled: false };
26443
26908
  }
26444
26909
  }
26445
26910
  async runReviewCommand(sessionState, target, options) {
@@ -26518,18 +26983,6 @@ var CodexCommands = class {
26518
26983
  const session = new ACPSessionConnection(this.connection, sessionId);
26519
26984
  await session.update(createAgentTextMessageChunk(`Command "/${name}" requires ${inputHint}.`));
26520
26985
  }
26521
- async sendUnknownCommandMessage(name, sessionId) {
26522
- const lines = this.getBuiltinCommands().map((command) => `- /${command.name}: ${command.description}`);
26523
- const text = [
26524
- `Unknown command "/${name}".`,
26525
- "Available commands:"
26526
- ];
26527
- if (lines.length > 0) {
26528
- text.push(...lines);
26529
- }
26530
- const session = new ACPSessionConnection(this.connection, sessionId);
26531
- await session.update(createAgentTextMessageChunk(text.join("\n")));
26532
- }
26533
26986
  buildStatusMessage(sessionState) {
26534
26987
  const agentMode = sessionState.agentMode;
26535
26988
  const accountText = this.formatAccountInfo(sessionState.account);
@@ -26799,6 +27252,7 @@ function toolCallIdFromThreadItem(item) {
26799
27252
  case "webSearch":
26800
27253
  case "imageView":
26801
27254
  case "imageGeneration":
27255
+ case "contextCompaction":
26802
27256
  return item.id;
26803
27257
  case "userMessage":
26804
27258
  case "hookPrompt":
@@ -26808,7 +27262,6 @@ function toolCallIdFromThreadItem(item) {
26808
27262
  case "subAgentActivity":
26809
27263
  case "enteredReviewMode":
26810
27264
  case "exitedReviewMode":
26811
- case "contextCompaction":
26812
27265
  case "sleep":
26813
27266
  return null;
26814
27267
  }
@@ -27625,12 +28078,6 @@ function numberValue(value) {
27625
28078
  return typeof value === "number" && Number.isFinite(value) ? value : null;
27626
28079
  }
27627
28080
 
27628
- // src/AcpExtensions.ts
27629
- var LEGACY_SET_SESSION_MODEL_METHOD = "session/set_model";
27630
- function isExtMethodRequest(request) {
27631
- return request.method === "authentication/status" || request.method === "authentication/logout" || request.method === LEGACY_SET_SESSION_MODEL_METHOD;
27632
- }
27633
-
27634
28081
  // src/FastModeConfig.ts
27635
28082
  var FAST_MODE_CONFIG_ID = "fast-mode";
27636
28083
  var FAST_MODE_CATEGORY = "model_config";
@@ -27756,6 +28203,7 @@ var CodexAcpServer = class _CodexAcpServer {
27756
28203
  auth: {
27757
28204
  logout: {}
27758
28205
  },
28206
+ providers: {},
27759
28207
  loadSession: true,
27760
28208
  promptCapabilities: {
27761
28209
  embeddedContext: true,
@@ -27791,6 +28239,25 @@ var CodexAcpServer = class _CodexAcpServer {
27791
28239
  }
27792
28240
  case LEGACY_SET_SESSION_MODEL_METHOD:
27793
28241
  return await this.unstable_setSessionModel(this.parseLegacySetSessionModelParams(methodRequest.params));
28242
+ case GOAL_CONTROL_METHOD: {
28243
+ const sessionState = this.sessions.get(methodRequest.params.sessionId);
28244
+ if (!sessionState) {
28245
+ throw RequestError.invalidParams(void 0, `Unknown session: ${methodRequest.params.sessionId}`);
28246
+ }
28247
+ const sessionGeneration = this.getSessionGeneration(sessionState.sessionId);
28248
+ if (methodRequest.params.action === "pause") {
28249
+ const goal = await this.runWithProcessCheck(() => this.codexAcpClient.setGoalStatus(sessionState.sessionId, "paused"));
28250
+ if (this.goalPublishIsCurrent(sessionState, sessionGeneration)) {
28251
+ await this.publishGoalSnapshot(sessionState, toThreadGoalSnapshot(goal), false);
28252
+ }
28253
+ } else if (methodRequest.params.action === "clear") {
28254
+ await this.runWithProcessCheck(() => this.codexAcpClient.clearGoal(sessionState.sessionId));
28255
+ if (this.goalPublishIsCurrent(sessionState, sessionGeneration)) {
28256
+ await this.publishGoalSnapshot(sessionState, null, false);
28257
+ }
28258
+ }
28259
+ return {};
28260
+ }
27794
28261
  }
27795
28262
  }
27796
28263
  async checkAuthorization() {
@@ -27826,6 +28293,12 @@ var CodexAcpServer = class _CodexAcpServer {
27826
28293
 
27827
28294
  You have been logged out. Please try again.`);
27828
28295
  }
28296
+ const configPath = this.codexAcpClient.getHomePath() ?? "global";
28297
+ if (e.message.includes("load config")) {
28298
+ throw RequestError.internalError(`${e.message}
28299
+
28300
+ Check ${configPath} and project .codex directories, especially their config.toml files, or any CODEX_CONFIG override.`);
28301
+ }
27829
28302
  }
27830
28303
  beginSessionOpen(sessionId) {
27831
28304
  const generation = this.getSessionGeneration(sessionId);
@@ -27932,6 +28405,7 @@ You have been logged out. Please try again.`);
27932
28405
  supportedReasoningEfforts: currentModel?.supportedReasoningEfforts ?? [],
27933
28406
  supportedInputModalities: currentModel?.inputModalities ?? ["text", "image"],
27934
28407
  agentMode: AgentMode.getInitialAgentMode(),
28408
+ collaborationMode: sessionMetadata.collaborationMode,
27935
28409
  currentTurnId: null,
27936
28410
  lastTokenUsage: null,
27937
28411
  totalTokenUsage: null,
@@ -27945,7 +28419,10 @@ You have been logged out. Please try again.`);
27945
28419
  fastModeEnabled: sessionMetadata.currentServiceTier === "fast",
27946
28420
  currentModelSupportsFast,
27947
28421
  sessionMcpServers,
27948
- terminalOutputMode: this.terminalOutputMode
28422
+ terminalOutputMode: this.terminalOutputMode,
28423
+ goalRevision: 0,
28424
+ sessionTitle: null,
28425
+ sessionTitleSource: "sessionId" in request ? "unknown" : "unset"
27949
28426
  };
27950
28427
  this.sessions.set(sessionId, sessionState);
27951
28428
  resumeSubscribed = false;
@@ -27957,6 +28434,9 @@ You have been logged out. Please try again.`);
27957
28434
  this.publishMcpStartupStatusAsync(sessionId);
27958
28435
  }
27959
28436
  this.publishAvailableCommandsAsync(sessionState);
28437
+ if ("sessionId" in request) {
28438
+ this.publishCurrentGoalAsync(sessionState, sessionGeneration);
28439
+ }
27960
28440
  const sessionModelState = this.createModelState(models, currentModelId);
27961
28441
  const sessionModeState = sessionState.agentMode.toSessionModeState();
27962
28442
  return [sessionId, sessionModelState, sessionModeState];
@@ -28126,6 +28606,17 @@ You have been logged out. Please try again.`);
28126
28606
  await this.refreshSessionsAuthState(null);
28127
28607
  logger.log("Logout request completed");
28128
28608
  }
28609
+ listProviders(_params) {
28610
+ return { providers: this.codexAcpClient.listProviders() };
28611
+ }
28612
+ setProvider(params) {
28613
+ this.codexAcpClient.setProvider(params);
28614
+ return {};
28615
+ }
28616
+ disableProvider(params) {
28617
+ this.codexAcpClient.disableProvider(params);
28618
+ return {};
28619
+ }
28129
28620
  async refreshSessionsAuthState(authProvider) {
28130
28621
  if (this.sessions.size === 0) return;
28131
28622
  const sessionsToRefresh = [...this.sessions.values()].filter((sessionState) => this.authProvidersMatch(sessionState.authProvider, authProvider));
@@ -28153,6 +28644,12 @@ You have been logged out. Please try again.`);
28153
28644
  });
28154
28645
  const sessionState = this.sessions.get(params.sessionId);
28155
28646
  if (!sessionState) throw new Error(`Session ${params.sessionId} not found`);
28647
+ await this.applySessionConfigOption(sessionState, params);
28648
+ return {
28649
+ configOptions: this.createSessionConfigOptions(sessionState)
28650
+ };
28651
+ }
28652
+ async applySessionConfigOption(sessionState, params) {
28156
28653
  switch (params.configId) {
28157
28654
  case FAST_MODE_CONFIG_ID:
28158
28655
  this.applyFastModeChange(sessionState, params);
@@ -28160,6 +28657,9 @@ You have been logged out. Please try again.`);
28160
28657
  case MODE_CONFIG_ID:
28161
28658
  this.applyModeChange(sessionState, this.stringConfigValue(params));
28162
28659
  break;
28660
+ case COLLABORATION_MODE_CONFIG_ID:
28661
+ await this.applyCollaborationModeChange(sessionState, this.stringConfigValue(params));
28662
+ break;
28163
28663
  case MODEL_CONFIG_ID:
28164
28664
  this.applyModelChange(sessionState, this.stringConfigValue(params));
28165
28665
  break;
@@ -28169,9 +28669,6 @@ You have been logged out. Please try again.`);
28169
28669
  default:
28170
28670
  throw RequestError.invalidParams();
28171
28671
  }
28172
- return {
28173
- configOptions: this.createSessionConfigOptions(sessionState)
28174
- };
28175
28672
  }
28176
28673
  applyFastModeChange(sessionState, params) {
28177
28674
  const value = params.value;
@@ -28197,6 +28694,14 @@ You have been logged out. Please try again.`);
28197
28694
  }
28198
28695
  sessionState.agentMode = newMode;
28199
28696
  }
28697
+ async applyCollaborationModeChange(sessionState, value) {
28698
+ const mode = parseCollaborationMode(value);
28699
+ if (mode === null) {
28700
+ throw RequestError.invalidParams();
28701
+ }
28702
+ await this.codexAcpClient.setCollaborationMode(sessionState.sessionId, mode, sessionState.currentModelId);
28703
+ sessionState.collaborationMode = mode;
28704
+ }
28200
28705
  applyModelChange(sessionState, value) {
28201
28706
  const model = sessionState.availableModels.find((m) => m.id === value);
28202
28707
  if (!model) {
@@ -28264,6 +28769,7 @@ You have been logged out. Please try again.`);
28264
28769
  const currentModelId = ModelId.fromString(sessionState.currentModelId);
28265
28770
  const configOptions = [
28266
28771
  sessionState.agentMode.toConfigOption(),
28772
+ createCollaborationModeConfigOption(sessionState.collaborationMode),
28267
28773
  createModelConfigOption(sessionState.availableModels, currentModelId.model)
28268
28774
  ];
28269
28775
  if (sessionState.supportedReasoningEfforts.length > 0) {
@@ -28293,6 +28799,46 @@ You have been logged out. Please try again.`);
28293
28799
  publishAvailableCommandsAsync(sessionState) {
28294
28800
  void this.availableCommands.publish(sessionState);
28295
28801
  }
28802
+ publishCurrentGoalAsync(sessionState, sessionGeneration) {
28803
+ void this.publishCurrentGoalBestEffort(sessionState, sessionGeneration, true);
28804
+ }
28805
+ async publishCurrentGoalBestEffort(sessionState, sessionGeneration, force) {
28806
+ try {
28807
+ await this.publishCurrentGoal(sessionState, sessionGeneration, force);
28808
+ } catch (err) {
28809
+ logger.error(`Failed to publish current goal for session ${sessionState.sessionId}`, err);
28810
+ }
28811
+ }
28812
+ async publishCurrentGoal(sessionState, sessionGeneration, force) {
28813
+ const requestRevision = ++sessionState.goalRevision;
28814
+ const goal = await this.runWithProcessCheck(() => this.codexAcpClient.getGoal(sessionState.sessionId));
28815
+ const snapshot = goal === null ? null : toThreadGoalSnapshot(goal);
28816
+ if (!this.goalPublishIsCurrent(sessionState, sessionGeneration) || sessionState.goalRevision !== requestRevision) {
28817
+ return;
28818
+ }
28819
+ await this.publishGoalSnapshot(sessionState, snapshot, force, false);
28820
+ }
28821
+ goalPublishIsCurrent(sessionState, sessionGeneration) {
28822
+ return this.sessions.get(sessionState.sessionId) === sessionState && this.getSessionGeneration(sessionState.sessionId) === sessionGeneration && !this.sessionIsClosing(sessionState.sessionId);
28823
+ }
28824
+ async publishGoalSnapshot(sessionState, snapshot, force, incrementRevision = true) {
28825
+ if (incrementRevision) {
28826
+ sessionState.goalRevision += 1;
28827
+ }
28828
+ if (!force && sameThreadGoalSnapshot(sessionState.currentGoal, snapshot)) {
28829
+ return;
28830
+ }
28831
+ sessionState.currentGoal = snapshot;
28832
+ const session = new ACPSessionConnection(this.connection, sessionState.sessionId);
28833
+ await session.update({
28834
+ sessionUpdate: "session_info_update",
28835
+ _meta: {
28836
+ codex: {
28837
+ goal: snapshot
28838
+ }
28839
+ }
28840
+ });
28841
+ }
28296
28842
  findCurrentModel(models, currentModelId) {
28297
28843
  const modelId = ModelId.fromString(currentModelId);
28298
28844
  return models.find((m) => m.id === modelId.model);
@@ -28358,6 +28904,7 @@ You have been logged out. Please try again.`);
28358
28904
  supportedReasoningEfforts: currentModel?.supportedReasoningEfforts ?? [],
28359
28905
  supportedInputModalities: currentModel?.inputModalities ?? ["text", "image"],
28360
28906
  agentMode: AgentMode.getInitialAgentMode(),
28907
+ collaborationMode: sessionMetadata.collaborationMode,
28361
28908
  currentTurnId: null,
28362
28909
  lastTokenUsage: null,
28363
28910
  totalTokenUsage: null,
@@ -28371,7 +28918,10 @@ You have been logged out. Please try again.`);
28371
28918
  fastModeEnabled: sessionMetadata.currentServiceTier === "fast",
28372
28919
  currentModelSupportsFast,
28373
28920
  sessionMcpServers,
28374
- terminalOutputMode: this.terminalOutputMode
28921
+ terminalOutputMode: this.terminalOutputMode,
28922
+ goalRevision: 0,
28923
+ sessionTitle: null,
28924
+ sessionTitleSource: "unset"
28375
28925
  };
28376
28926
  this.sessions.set(sessionId, sessionState);
28377
28927
  subscribed = false;
@@ -28383,6 +28933,7 @@ You have been logged out. Please try again.`);
28383
28933
  this.publishMcpStartupStatusAsync(sessionId);
28384
28934
  }
28385
28935
  await this.availableCommands.publish(sessionState);
28936
+ await this.publishCurrentGoalBestEffort(sessionState, requestedSessionGeneration, true);
28386
28937
  const sessionModelState = this.createModelState(models, currentModelId);
28387
28938
  const sessionModeState = sessionState.agentMode.toSessionModeState();
28388
28939
  return {
@@ -28395,6 +28946,7 @@ You have been logged out. Please try again.`);
28395
28946
  async streamThreadHistory(sessionId, thread) {
28396
28947
  const session = new ACPSessionConnection(this.connection, sessionId);
28397
28948
  const sessionState = this.getSessionState(sessionId);
28949
+ await this.publishThreadHistoryTitle(session, sessionState, thread);
28398
28950
  const responseItemFallbackUpdates = await createResponseItemHistoryFallbackUpdates(
28399
28951
  thread,
28400
28952
  sessionState.terminalOutputMode
@@ -28411,14 +28963,56 @@ You have been logged out. Please try again.`);
28411
28963
  await session.update(update);
28412
28964
  }
28413
28965
  }
28966
+ async publishThreadHistoryTitle(session, sessionState, thread) {
28967
+ const explicitTitle = this.normalizeSessionTitle(thread.name);
28968
+ if (explicitTitle) {
28969
+ sessionState.sessionTitle = explicitTitle;
28970
+ sessionState.sessionTitleSource = "explicit";
28971
+ await session.update({
28972
+ sessionUpdate: "session_info_update",
28973
+ title: explicitTitle
28974
+ });
28975
+ return;
28976
+ }
28977
+ const historyTitle = this.findFirstUserMessageTitle(thread) ?? this.normalizeSessionTitle(thread.preview);
28978
+ await this.publishFallbackSessionTitle(sessionState, historyTitle);
28979
+ }
28980
+ findFirstUserMessageTitle(thread) {
28981
+ for (const turn of thread.turns) {
28982
+ for (const item of turn.items) {
28983
+ if (item.type !== "userMessage") continue;
28984
+ const title = this.normalizeSessionTitle(item.content.filter((input) => input.type === "text").map((input) => input.text).join(" "));
28985
+ if (title) return title;
28986
+ }
28987
+ }
28988
+ return null;
28989
+ }
28990
+ async publishFallbackSessionTitle(sessionState, title) {
28991
+ if (sessionState.sessionTitleSource !== "unset" || !title) return;
28992
+ sessionState.sessionTitle = title;
28993
+ sessionState.sessionTitleSource = "fallback";
28994
+ const session = new ACPSessionConnection(this.connection, sessionState.sessionId);
28995
+ await session.update({
28996
+ sessionUpdate: "session_info_update",
28997
+ title
28998
+ });
28999
+ }
29000
+ createPromptFallbackTitle(prompt) {
29001
+ return this.normalizeSessionTitle(prompt.filter((block) => block.type === "text").map((block) => block.text).join(" "));
29002
+ }
29003
+ normalizeSessionTitle(title) {
29004
+ const normalized = title?.replace(/\s+/g, " ").trim() ?? "";
29005
+ return normalized.length > 0 ? normalized : null;
29006
+ }
28414
29007
  async createHistoryUpdates(item, sessionState) {
28415
29008
  switch (item.type) {
28416
29009
  case "userMessage":
28417
29010
  return this.createUserMessageUpdates(item);
28418
29011
  case "hookPrompt":
28419
- case "subAgentActivity":
28420
29012
  case "sleep":
28421
29013
  return [];
29014
+ case "subAgentActivity":
29015
+ return [createSubAgentActivityUpdate(item, "completed", "tool_call")];
28422
29016
  case "agentMessage": {
28423
29017
  const meta3 = createCodexMessagePhaseMeta(item.phase);
28424
29018
  return [{
@@ -28457,7 +29051,7 @@ You have been logged out. Please try again.`);
28457
29051
  case "exitedReviewMode":
28458
29052
  return [this.createReviewModeUpdate(item, false)];
28459
29053
  case "contextCompaction":
28460
- return [this.createContextCompactionUpdate()];
29054
+ return [createCompletedContextCompactionUpdate(item)];
28461
29055
  case "plan":
28462
29056
  return [this.createPlanUpdate(item)];
28463
29057
  }
@@ -28500,15 +29094,6 @@ You have been logged out. Please try again.`);
28500
29094
  }
28501
29095
  };
28502
29096
  }
28503
- createContextCompactionUpdate() {
28504
- return {
28505
- sessionUpdate: "agent_message_chunk",
28506
- content: {
28507
- type: "text",
28508
- text: "Context compacted."
28509
- }
28510
- };
28511
- }
28512
29097
  createPlanUpdate(item) {
28513
29098
  return {
28514
29099
  sessionUpdate: "agent_message_chunk",
@@ -28839,6 +29424,18 @@ ${item.text}`
28839
29424
  }
28840
29425
  sessionState.currentTurnId = turnId;
28841
29426
  pendingTurnStart?.resolve(turnId);
29427
+ },
29428
+ setConfigOption: async (configId, value) => {
29429
+ await this.applySessionConfigOption(sessionState, {
29430
+ sessionId: sessionState.sessionId,
29431
+ configId,
29432
+ value
29433
+ });
29434
+ const session = new ACPSessionConnection(this.connection, sessionState.sessionId);
29435
+ await session.update({
29436
+ sessionUpdate: "config_option_update",
29437
+ configOptions: this.createSessionConfigOptions(sessionState)
29438
+ });
28842
29439
  }
28843
29440
  });
28844
29441
  void commandPromise.catch((err) => {
@@ -28936,6 +29533,10 @@ ${item.text}`
28936
29533
  if (error51) {
28937
29534
  throw error51;
28938
29535
  }
29536
+ await this.publishFallbackSessionTitle(
29537
+ sessionState,
29538
+ this.createPromptFallbackTitle(params.prompt)
29539
+ );
28939
29540
  return {
28940
29541
  stopReason: "end_turn",
28941
29542
  usage: this.buildPromptUsage(sessionState.lastTokenUsage),
@@ -29090,6 +29691,7 @@ var CommandExecutionApprovalRequest = new import_node2.RequestType("item/command
29090
29691
  var FileChangeApprovalRequest = new import_node2.RequestType("item/fileChange/requestApproval");
29091
29692
  var PermissionsApprovalRequest = new import_node2.RequestType("item/permissions/requestApproval");
29092
29693
  var McpServerElicitationRequest = new import_node2.RequestType("mcpServer/elicitation/request");
29694
+ var ToolRequestUserInputRequest = new import_node2.RequestType("item/tool/requestUserInput");
29093
29695
  var GOAL_RUNTIME_EFFECTS_GRACE_MS = 1e3;
29094
29696
  var CodexAppServerClient = class {
29095
29697
  connection;
@@ -29105,6 +29707,7 @@ var CodexAppServerClient = class {
29105
29707
  threadStatusCaptures = /* @__PURE__ */ new Map();
29106
29708
  threadGoalUpdateCaptures = /* @__PURE__ */ new Map();
29107
29709
  threadGoalClearedCaptures = /* @__PURE__ */ new Map();
29710
+ threadSettings = /* @__PURE__ */ new Map();
29108
29711
  staleTurnIds = /* @__PURE__ */ new Map();
29109
29712
  constructor(connection) {
29110
29713
  this.connection = connection;
@@ -29134,6 +29737,9 @@ var CodexAppServerClient = class {
29134
29737
  if (isThreadGoalClearedNotification(serverNotification)) {
29135
29738
  this.recordThreadGoalCleared(serverNotification.params);
29136
29739
  }
29740
+ if (serverNotification.method === "thread/settings/updated") {
29741
+ this.threadSettings.set(serverNotification.params.threadId, serverNotification.params.threadSettings);
29742
+ }
29137
29743
  const routing = extractTurnRouting(serverNotification);
29138
29744
  if (this.handleStaleTurnNotification(serverNotification, routing)) {
29139
29745
  return;
@@ -29187,6 +29793,16 @@ var CodexAppServerClient = class {
29187
29793
  }
29188
29794
  return await handler.handleElicitation(params);
29189
29795
  });
29796
+ this.connection.onRequest(ToolRequestUserInputRequest, async (params) => {
29797
+ if (this.isStaleTurn(params.threadId, params.turnId)) {
29798
+ return { answers: {} };
29799
+ }
29800
+ const handler = this.elicitationHandlers.get(params.threadId);
29801
+ if (!handler) {
29802
+ return { answers: {} };
29803
+ }
29804
+ return await handler.handleUserInput(params);
29805
+ });
29190
29806
  }
29191
29807
  onApprovalRequest(threadId, handler) {
29192
29808
  this.approvalHandlers.set(threadId, handler);
@@ -29241,7 +29857,7 @@ var CodexAppServerClient = class {
29241
29857
  releaseCapture();
29242
29858
  }
29243
29859
  }
29244
- async runGoalSet(params, onTurnStarted, runtimeEffectsGraceMs = GOAL_RUNTIME_EFFECTS_GRACE_MS) {
29860
+ async runGoalSet(params, onTurnStarted, runtimeEffectsGraceMs = GOAL_RUNTIME_EFFECTS_GRACE_MS, onGoalSet) {
29245
29861
  let goalTurnId = null;
29246
29862
  const capturedCompletions = [];
29247
29863
  let resolveGoalTurnCompleted = () => {
@@ -29294,6 +29910,7 @@ var CodexAppServerClient = class {
29294
29910
  try {
29295
29911
  const goalSetResponse = await this.threadGoalSet(params);
29296
29912
  expectedGoal = goalSetResponse.goal;
29913
+ onGoalSet?.(expectedGoal);
29297
29914
  if (capturedGoalUpdates.some((event) => goalsMatch(event.goal, expectedGoal))) {
29298
29915
  goalUpdateHandled = true;
29299
29916
  resolveGoalUpdateHandled();
@@ -29431,6 +30048,12 @@ var CodexAppServerClient = class {
29431
30048
  async threadResume(params) {
29432
30049
  return await this.sendRequest({ method: "thread/resume", params });
29433
30050
  }
30051
+ getThreadSettings(threadId) {
30052
+ return this.threadSettings.get(threadId);
30053
+ }
30054
+ async threadSettingsUpdate(params) {
30055
+ await this.connection.sendRequest("thread/settings/update", params);
30056
+ }
29434
30057
  async threadList(params) {
29435
30058
  return await this.sendRequest({ method: "thread/list", params });
29436
30059
  }
@@ -29452,6 +30075,9 @@ var CodexAppServerClient = class {
29452
30075
  async threadGoalSet(params) {
29453
30076
  return await this.sendRequest({ method: "thread/goal/set", params });
29454
30077
  }
30078
+ async threadGoalGet(params) {
30079
+ return await this.sendRequest({ method: "thread/goal/get", params });
30080
+ }
29455
30081
  async threadGoalClear(params) {
29456
30082
  return await this.sendRequest({ method: "thread/goal/clear", params });
29457
30083
  }
@@ -29981,6 +30607,10 @@ var legacySetSessionModelParamsParser = external_exports.object({
29981
30607
  sessionId: external_exports.string(),
29982
30608
  modelId: external_exports.string()
29983
30609
  }).passthrough();
30610
+ var goalControlParamsParser = external_exports.object({
30611
+ sessionId: external_exports.string(),
30612
+ action: external_exports.enum(["pause", "clear"])
30613
+ }).passthrough();
29984
30614
  if (process.argv.includes("--version")) {
29985
30615
  console.log(`${package_default.name} ${package_default.version}`);
29986
30616
  process.exit(0);
@@ -30053,5 +30683,5 @@ function startAcpServer() {
30053
30683
  codexAcpServer = null;
30054
30684
  }
30055
30685
  });
30056
- }).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);
30686
+ }).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)).onRequest(GOAL_CONTROL_METHOD, goalControlParamsParser, (ctx) => getAgent().extMethod(GOAL_CONTROL_METHOD, ctx.params)).connect(acpJsonStream);
30057
30687
  }