@dotobokuri/fleet-console 1.33.0 → 1.35.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.
Files changed (65) hide show
  1. package/dist/cli.mjs +576 -433
  2. package/dist/client/assets/{_baseUniq-DGGrBWAb.js → _baseUniq--isZn0gt.js} +1 -1
  3. package/dist/client/assets/{arc-BNCMUJG_.js → arc-CbzUc80R.js} +1 -1
  4. package/dist/client/assets/{architectureDiagram-Q4EWVU46-C7qQq1FU.js → architectureDiagram-Q4EWVU46-KMk7A7ht.js} +1 -1
  5. package/dist/client/assets/{blockDiagram-DXYQGD6D-Cd8j3SNd.js → blockDiagram-DXYQGD6D-BDIXylVL.js} +1 -1
  6. package/dist/client/assets/{c4Diagram-AHTNJAMY-fFAORcJP.js → c4Diagram-AHTNJAMY-4BtEjWxc.js} +1 -1
  7. package/dist/client/assets/channel-DNy689Yu.js +1 -0
  8. package/dist/client/assets/{chunk-4BX2VUAB-peomhKWw.js → chunk-4BX2VUAB-B6P7vZc2.js} +1 -1
  9. package/dist/client/assets/{chunk-4TB4RGXK-TEb8s6Dl.js → chunk-4TB4RGXK-w5tBo1dz.js} +1 -1
  10. package/dist/client/assets/{chunk-55IACEB6-11aKxGwZ.js → chunk-55IACEB6-C_Bcd4uB.js} +1 -1
  11. package/dist/client/assets/{chunk-EDXVE4YY-CPeOY-w0.js → chunk-EDXVE4YY-BikIv-lO.js} +1 -1
  12. package/dist/client/assets/{chunk-FMBD7UC4-DpbKBDUG.js → chunk-FMBD7UC4-C785J70Z.js} +1 -1
  13. package/dist/client/assets/{chunk-OYMX7WX6-DQiIenR8.js → chunk-OYMX7WX6-qhvy0Mwx.js} +1 -1
  14. package/dist/client/assets/{chunk-QZHKN3VN-ByEBhAst.js → chunk-QZHKN3VN-28VsIAWd.js} +1 -1
  15. package/dist/client/assets/{chunk-YZCP3GAM-Ca8uNdzu.js → chunk-YZCP3GAM-Dj_CFH0t.js} +1 -1
  16. package/dist/client/assets/classDiagram-6PBFFD2Q-BN9_O5GO.js +1 -0
  17. package/dist/client/assets/classDiagram-v2-HSJHXN6E-BN9_O5GO.js +1 -0
  18. package/dist/client/assets/clone-CYXyhbsy.js +1 -0
  19. package/dist/client/assets/{cose-bilkent-S5V4N54A-B9r4jGi9.js → cose-bilkent-S5V4N54A-CbwRTcWp.js} +1 -1
  20. package/dist/client/assets/{dagre-KV5264BT-Csnn3Aws.js → dagre-KV5264BT-DmTFlNmc.js} +1 -1
  21. package/dist/client/assets/{diagram-5BDNPKRD-xgvBmNve.js → diagram-5BDNPKRD-D5filFx0.js} +1 -1
  22. package/dist/client/assets/{diagram-G4DWMVQ6-eDo2cgdI.js → diagram-G4DWMVQ6-BstKtz5F.js} +1 -1
  23. package/dist/client/assets/{diagram-MMDJMWI5-C3GDlrcv.js → diagram-MMDJMWI5-BubZFKBf.js} +1 -1
  24. package/dist/client/assets/{diagram-TYMM5635-BDyVzMHP.js → diagram-TYMM5635-Dkt0qt66.js} +1 -1
  25. package/dist/client/assets/{erDiagram-SMLLAGMA-YegVvYsw.js → erDiagram-SMLLAGMA-D32vcceA.js} +1 -1
  26. package/dist/client/assets/{flowDiagram-DWJPFMVM-zcVenDmh.js → flowDiagram-DWJPFMVM-8iX8d0Mm.js} +1 -1
  27. package/dist/client/assets/{ganttDiagram-T4ZO3ILL-CkMabQtg.js → ganttDiagram-T4ZO3ILL-DVdts0bT.js} +1 -1
  28. package/dist/client/assets/{gitGraphDiagram-UUTBAWPF-CuD1D5LQ.js → gitGraphDiagram-UUTBAWPF-C36cV6di.js} +1 -1
  29. package/dist/client/assets/{graph-DvVj819O.js → graph-BfCG4izN.js} +1 -1
  30. package/dist/client/assets/index-0s24Vlsc.css +1 -0
  31. package/dist/client/assets/index-o0x3lJ2C.js +452 -0
  32. package/dist/client/assets/{infoDiagram-42DDH7IO-DPoWXM0E.js → infoDiagram-42DDH7IO-7hCUS4JK.js} +1 -1
  33. package/dist/client/assets/{ishikawaDiagram-UXIWVN3A-BBoXoRxo.js → ishikawaDiagram-UXIWVN3A-DYOGN_tm.js} +1 -1
  34. package/dist/client/assets/{journeyDiagram-VCZTEJTY-B-IoiUw0.js → journeyDiagram-VCZTEJTY-C-RaUFGz.js} +1 -1
  35. package/dist/client/assets/{kanban-definition-6JOO6SKY-BcjjEa8G.js → kanban-definition-6JOO6SKY-7GR2tX78.js} +1 -1
  36. package/dist/client/assets/{layout-BjaFRuOv.js → layout-C9u6a9pL.js} +1 -1
  37. package/dist/client/assets/{linear-DLVG-hZ_.js → linear-CoxISMQN.js} +1 -1
  38. package/dist/client/assets/{mermaid.core-6fYEjApv.js → mermaid.core-Clx-Pww4.js} +4 -4
  39. package/dist/client/assets/{min-BfgBrJLR.js → min-B7iH_omx.js} +1 -1
  40. package/dist/client/assets/{mindmap-definition-QFDTVHPH-BjelYLWH.js → mindmap-definition-QFDTVHPH-BEg4jpMk.js} +1 -1
  41. package/dist/client/assets/{pieDiagram-DEJITSTG-BznY8oUt.js → pieDiagram-DEJITSTG-Cu-NdmIs.js} +1 -1
  42. package/dist/client/assets/{quadrantDiagram-34T5L4WZ-jEnKUROj.js → quadrantDiagram-34T5L4WZ-BW_7SRcp.js} +1 -1
  43. package/dist/client/assets/{requirementDiagram-MS252O5E-BuQqQSjX.js → requirementDiagram-MS252O5E-C_F2SkO4.js} +1 -1
  44. package/dist/client/assets/{sankeyDiagram-XADWPNL6-B9g8trZW.js → sankeyDiagram-XADWPNL6-B7f43JV1.js} +1 -1
  45. package/dist/client/assets/{sequenceDiagram-FGHM5R23-V6Zwufo5.js → sequenceDiagram-FGHM5R23-oARdfmhl.js} +1 -1
  46. package/dist/client/assets/{stateDiagram-FHFEXIEX-8E2cIHyh.js → stateDiagram-FHFEXIEX-VKGucJY7.js} +1 -1
  47. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-DfRePizj.js +1 -0
  48. package/dist/client/assets/{timeline-definition-GMOUNBTQ-DpiM_Ig9.js → timeline-definition-GMOUNBTQ-DsImBBQe.js} +1 -1
  49. package/dist/client/assets/{vennDiagram-DHZGUBPP-D975VPJg.js → vennDiagram-DHZGUBPP-DdJLtnOA.js} +1 -1
  50. package/dist/client/assets/{wardley-RL74JXVD-BiM6uI8Z.js → wardley-RL74JXVD-Cn3hq_Uh.js} +1 -1
  51. package/dist/client/assets/{wardleyDiagram-NUSXRM2D-IoXxaljw.js → wardleyDiagram-NUSXRM2D-VZCV5eGr.js} +1 -1
  52. package/dist/client/assets/{xychartDiagram-5P7HB3ND-BqNE-iqW.js → xychartDiagram-5P7HB3ND-BglQI2vQ.js} +1 -1
  53. package/dist/client/index.html +2 -2
  54. package/dist/fleet-plugins/file-explorer/routes.mjs +102 -5
  55. package/dist/fleet-plugins/repository/routes.mjs +110 -6
  56. package/dist/fleet-plugins/skills/routes.mjs +93 -42
  57. package/dist/fleet-plugins/terminal/routes.mjs +901 -799
  58. package/package.json +1 -1
  59. package/dist/client/assets/channel-BDofRchL.js +0 -1
  60. package/dist/client/assets/classDiagram-6PBFFD2Q-BrRbVOSf.js +0 -1
  61. package/dist/client/assets/classDiagram-v2-HSJHXN6E-BrRbVOSf.js +0 -1
  62. package/dist/client/assets/clone-CEWs7Jhj.js +0 -1
  63. package/dist/client/assets/index-DeA0aGZd.css +0 -1
  64. package/dist/client/assets/index-Dk_jqw8o.js +0 -451
  65. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-0HROefWC.js +0 -1
package/dist/cli.mjs CHANGED
@@ -903,16 +903,22 @@ function sanitizeGlobalOptionsData(value) {
903
903
  return { data: createEmptyGlobalOptionsData(), changed: true };
904
904
  }
905
905
  const kimiModel = sanitizeKimiModel(value.kimiModel);
906
+ const agentIdleDormantMinutes = sanitizeAgentIdleDormantMinutes(value.agentIdleDormantMinutes);
906
907
  const data = {
907
908
  version: GLOBAL_OPTIONS_VERSION,
908
909
  ...typeof value.enableMetaphor === "boolean" ? { enableMetaphor: value.enableMetaphor } : {},
909
- ...value.codexLaunchMode === "acp" || value.codexLaunchMode === "app-server" ? { codexLaunchMode: value.codexLaunchMode } : {},
910
- ...kimiModel ? { kimiModel } : {}
910
+ ...kimiModel ? { kimiModel } : {},
911
+ ...agentIdleDormantMinutes !== void 0 ? { agentIdleDormantMinutes } : {}
911
912
  };
912
- const allowedKeys = /* @__PURE__ */ new Set(["version", "enableMetaphor", "codexLaunchMode", "kimiModel"]);
913
- const changed = Object.keys(value).some((key) => !allowedKeys.has(key)) || "enableMetaphor" in value && typeof value.enableMetaphor !== "boolean" || "codexLaunchMode" in value && value.codexLaunchMode !== "acp" && value.codexLaunchMode !== "app-server" || "kimiModel" in value && kimiModel === void 0;
913
+ const allowedKeys = /* @__PURE__ */ new Set(["version", "enableMetaphor", "kimiModel", "agentIdleDormantMinutes"]);
914
+ const changed = Object.keys(value).some((key) => !allowedKeys.has(key)) || "enableMetaphor" in value && typeof value.enableMetaphor !== "boolean" || "kimiModel" in value && kimiModel === void 0 || "agentIdleDormantMinutes" in value && agentIdleDormantMinutes === void 0;
914
915
  return { data, changed };
915
916
  }
917
+ function sanitizeAgentIdleDormantMinutes(value) {
918
+ if (value === null) return null;
919
+ if (typeof value === "number" && Number.isFinite(value) && Number.isInteger(value) && value > 0) return value;
920
+ return void 0;
921
+ }
916
922
  function sanitizeKimiModel(value) {
917
923
  if (!isRecord(value)) {
918
924
  return void 0;
@@ -12209,6 +12215,8 @@ var SETTINGS_LOCK_OWNER_FILE_NAME = "owner.json";
12209
12215
  var SETTINGS_TEMP_PREFIX = ".settings.";
12210
12216
  var MIN_CONSOLE_STATIC_PORT = 1024;
12211
12217
  var MAX_CONSOLE_STATIC_PORT = 65535;
12218
+ var MAX_SEEN_FEATURE_TOURS = 64;
12219
+ var MAX_FEATURE_TOUR_KEY_LENGTH = 64;
12212
12220
  function createConsoleSettingsStore(deps = {}) {
12213
12221
  const paths = deps.paths ?? createConsoleDataPaths();
12214
12222
  const createStore = deps.createStore ?? createDurableJsonStore;
@@ -12248,6 +12256,16 @@ function sanitizeUiFontSettings(value) {
12248
12256
  }
12249
12257
  return void 0;
12250
12258
  }
12259
+ function sanitizeSeenFeatureTours(value) {
12260
+ if (!Array.isArray(value)) return void 0;
12261
+ const seen = /* @__PURE__ */ new Set();
12262
+ for (const item of value) {
12263
+ if (typeof item !== "string" || item.length > MAX_FEATURE_TOUR_KEY_LENGTH) continue;
12264
+ seen.add(item);
12265
+ if (seen.size === MAX_SEEN_FEATURE_TOURS) break;
12266
+ }
12267
+ return [...seen];
12268
+ }
12251
12269
  function isUiFontSettings(value) {
12252
12270
  if (!isRecord2(value) || !isValidUiFontSize(value.size)) return false;
12253
12271
  if (value.source === "builtin") return isConsoleUiFontId(value.id);
@@ -12259,6 +12277,7 @@ function readConsoleGeneralSettings(value) {
12259
12277
  const consoleStaticPort = isValidConsoleStaticPort(value.consoleStaticPort) ? value.consoleStaticPort : void 0;
12260
12278
  const language = value.language === "auto" || value.language === "en" || value.language === "ko" ? value.language : void 0;
12261
12279
  const reducePanelMotion = typeof value.reducePanelMotion === "boolean" ? value.reducePanelMotion : void 0;
12280
+ const seenFeatureTours = sanitizeSeenFeatureTours(value.seenFeatureTours);
12262
12281
  const theme = value.theme === "instrument" || value.theme === "maritime" || value.theme === "carbon" ? value.theme : void 0;
12263
12282
  const uiFont = sanitizeUiFontSettings(value.uiFont);
12264
12283
  return {
@@ -12266,6 +12285,7 @@ function readConsoleGeneralSettings(value) {
12266
12285
  ...consoleStaticPort !== void 0 ? { consoleStaticPort } : {},
12267
12286
  ...language !== void 0 ? { language } : {},
12268
12287
  ...reducePanelMotion !== void 0 ? { reducePanelMotion } : {},
12288
+ ...seenFeatureTours !== void 0 ? { seenFeatureTours } : {},
12269
12289
  ...theme !== void 0 ? { theme } : {},
12270
12290
  ...uiFont !== void 0 ? { uiFont } : {}
12271
12291
  };
@@ -12366,6 +12386,10 @@ async function mutateGlobalSettings(req, res, deps) {
12366
12386
  deps.writeJson(res, 400, { error: "invalid_reduce_panel_motion" });
12367
12387
  return;
12368
12388
  }
12389
+ if (body2.seenFeatureTours !== void 0 && !isSeenFeatureToursInput(body2.seenFeatureTours)) {
12390
+ deps.writeJson(res, 400, { error: "invalid_seen_feature_tours" });
12391
+ return;
12392
+ }
12369
12393
  if (body2.theme !== void 0 && body2.theme !== "instrument" && body2.theme !== "maritime" && body2.theme !== "carbon") {
12370
12394
  deps.writeJson(res, 400, { error: "invalid_theme" });
12371
12395
  return;
@@ -12384,6 +12408,7 @@ async function mutateGlobalSettings(req, res, deps) {
12384
12408
  ...isValidConsoleStaticPort2(body2.consoleStaticPort) ? { consoleStaticPort: body2.consoleStaticPort } : {},
12385
12409
  ...body2.language === "auto" || body2.language === "en" || body2.language === "ko" ? { language: body2.language } : {},
12386
12410
  ...typeof body2.reducePanelMotion === "boolean" ? { reducePanelMotion: body2.reducePanelMotion } : {},
12411
+ ...body2.seenFeatureTours !== void 0 ? { seenFeatureTours: sanitizeSeenFeatureTours(body2.seenFeatureTours) ?? [] } : {},
12387
12412
  ...theme !== void 0 ? { theme } : {},
12388
12413
  ...isUiFontSettings(body2.uiFont) ? { uiFont: body2.uiFont } : {}
12389
12414
  },
@@ -12400,6 +12425,7 @@ function toGlobalSettingsState(data) {
12400
12425
  consoleStaticPort: general.consoleStaticPort ?? null,
12401
12426
  language: general.language ?? "auto",
12402
12427
  reducePanelMotion: general.reducePanelMotion ?? false,
12428
+ seenFeatureTours: general.seenFeatureTours ?? [],
12403
12429
  theme: general.theme ?? "instrument",
12404
12430
  uiFont: general.uiFont ?? DEFAULT_UI_FONT_SETTINGS
12405
12431
  };
@@ -12414,6 +12440,9 @@ function isValidConsoleStaticPort2(value) {
12414
12440
  function isUiFontSettingsOrUndefined(value) {
12415
12441
  return value === void 0 || isUiFontSettings(value);
12416
12442
  }
12443
+ function isSeenFeatureToursInput(value) {
12444
+ return Array.isArray(value) && value.length <= 64 && value.every((item) => typeof item === "string" && item.length <= 64);
12445
+ }
12417
12446
 
12418
12447
  // core/host/plugin-settings-routes.ts
12419
12448
  var PLUGIN_SETTINGS_PREFIX = "/api/v1/settings/plugins/";
@@ -31774,35 +31803,23 @@ var AcpConnection = class extends BaseConnection {
31774
31803
  }
31775
31804
  case "tool_call": {
31776
31805
  const { sessionUpdate: _tc, ...toolCallData } = update;
31777
- const rawTcTitle = toolCallData.title ?? "";
31778
- const cleanTcTitle = rawTcTitle.replace(/\s*\(\s*[\d.,\s-]*\b(?:NaN|undefined)\b[\d.,\s-]*\)/g, "");
31779
- if (cleanTcTitle !== rawTcTitle) {
31780
- console.debug("[AcpConnection] sanitized tool_call payload", { rawTitle: rawTcTitle, cleanTitle: cleanTcTitle });
31781
- }
31782
- const titleTcOverride = "title" in toolCallData ? { title: cleanTcTitle } : {};
31783
31806
  this.emit(
31784
31807
  "toolCall",
31785
- cleanTcTitle,
31808
+ toolCallData.title ?? "",
31786
31809
  toolCallData.status ?? "",
31787
31810
  sessionId,
31788
- { ...toolCallData, ...titleTcOverride }
31811
+ toolCallData
31789
31812
  );
31790
31813
  break;
31791
31814
  }
31792
31815
  case "tool_call_update": {
31793
31816
  const { sessionUpdate: _tcu, ...toolCallUpdateData } = update;
31794
- const rawTitle = toolCallUpdateData.title ?? "";
31795
- const cleanTitle = rawTitle.replace(/\s*\(\s*[\d.,\s-]*\b(?:NaN|undefined)\b[\d.,\s-]*\)/g, "");
31796
- if (cleanTitle !== rawTitle) {
31797
- console.debug("[AcpConnection] sanitized tool_call_update payload", { rawTitle, cleanTitle });
31798
- }
31799
- const titleOverride = "title" in toolCallUpdateData ? { title: cleanTitle } : {};
31800
31817
  this.emit(
31801
31818
  "toolCallUpdate",
31802
- cleanTitle,
31819
+ toolCallUpdateData.title ?? "",
31803
31820
  toolCallUpdateData.status ?? "",
31804
31821
  sessionId,
31805
- { ...toolCallUpdateData, ...titleOverride }
31822
+ toolCallUpdateData
31806
31823
  );
31807
31824
  break;
31808
31825
  }
@@ -31967,7 +31984,7 @@ function inferBinName(packageName) {
31967
31984
  // ../../packages/core-unified-agent/models.json
31968
31985
  var models_default = {
31969
31986
  version: 1,
31970
- updatedAt: "2026-07-21T00:00:00Z",
31987
+ updatedAt: "2026-07-25T00:00:00Z",
31971
31988
  providers: {
31972
31989
  claude: {
31973
31990
  name: "Claude Code",
@@ -32003,11 +32020,13 @@ var models_default = {
32003
32020
  defaultModel: "gpt-5.6-sol",
32004
32021
  models: [
32005
32022
  { modelId: "gpt-5.6-sol", name: "GPT-5.6-Sol", effort: { supported: true, levels: ["low", "medium", "high", "xhigh", "max", "ultra"], default: "low" } },
32023
+ { modelId: "gpt-5.6-sol-fast", name: "GPT-5.6-Sol Fast", providerModelId: "gpt-5.6-sol", serviceTier: "priority", effort: { supported: true, levels: ["low", "medium", "high", "xhigh", "max", "ultra"], default: "low" } },
32006
32024
  { modelId: "gpt-5.6-terra", name: "GPT-5.6-Terra", effort: { supported: true, levels: ["low", "medium", "high", "xhigh", "max", "ultra"], default: "medium" } },
32025
+ { modelId: "gpt-5.6-terra-fast", name: "GPT-5.6-Terra Fast", providerModelId: "gpt-5.6-terra", serviceTier: "priority", effort: { supported: true, levels: ["low", "medium", "high", "xhigh", "max", "ultra"], default: "medium" } },
32007
32026
  { modelId: "gpt-5.6-luna", name: "GPT-5.6-Luna", effort: { supported: true, levels: ["low", "medium", "high", "xhigh", "max"], default: "medium" } },
32027
+ { modelId: "gpt-5.6-luna-fast", name: "GPT-5.6-Luna Fast", providerModelId: "gpt-5.6-luna", serviceTier: "priority", effort: { supported: true, levels: ["low", "medium", "high", "xhigh", "max"], default: "medium" } },
32008
32028
  { modelId: "gpt-5.5", name: "GPT-5.5", effort: { supported: true, levels: ["low", "medium", "high", "xhigh"], default: "high" } },
32009
- { modelId: "gpt-5.4", name: "GPT-5.4", effort: { supported: true, levels: ["low", "medium", "high", "xhigh"], default: "high" } },
32010
- { modelId: "gpt-5.4-mini", name: "GPT-5.4 Mini", effort: { supported: true, levels: ["low", "medium", "high", "xhigh"], default: "high" } }
32029
+ { modelId: "gpt-5.5-fast", name: "GPT-5.5 Fast", providerModelId: "gpt-5.5", serviceTier: "priority", effort: { supported: true, levels: ["low", "medium", "high", "xhigh"], default: "high" } }
32011
32030
  ]
32012
32031
  },
32013
32032
  "opencode-go": {
@@ -32093,6 +32112,10 @@ var ModelEntrySchema = external_exports.object({
32093
32112
  description: external_exports.string().optional(),
32094
32113
  /** spawn 시 실제 CLI 모델 ID를 조립해야 하는 경우의 템플릿 */
32095
32114
  spawnModelTemplate: external_exports.string().optional(),
32115
+ /** 카탈로그 ID와 실제 provider 모델 ID가 다른 경우의 원본 모델 ID */
32116
+ providerModelId: external_exports.string().optional(),
32117
+ /** provider가 모델과 별도로 받는 서비스 티어 */
32118
+ serviceTier: external_exports.string().optional(),
32096
32119
  /** 모델의 컨텍스트 윈도우 크기 (토큰) */
32097
32120
  contextWindow: external_exports.number().int().positive().optional(),
32098
32121
  /** 모델별 effort 설정 */
@@ -32115,6 +32138,14 @@ var ModelEntrySchema = external_exports.object({
32115
32138
  path: ["spawnModelTemplate"]
32116
32139
  });
32117
32140
  }
32141
+ if (ctx.value.serviceTier && !ctx.value.providerModelId) {
32142
+ ctx.issues.push({
32143
+ code: "custom",
32144
+ input: ctx.value.serviceTier,
32145
+ message: "serviceTier\uB97C \uC9C0\uC815\uD55C \uBAA8\uB378\uC740 providerModelId\uB3C4 \uC9C0\uC815\uD574\uC57C \uD569\uB2C8\uB2E4",
32146
+ path: ["serviceTier"]
32147
+ });
32148
+ }
32118
32149
  });
32119
32150
  var ModelsMappingSchema = external_exports.record(external_exports.string(), external_exports.string());
32120
32151
  var ProviderSchema = external_exports.object({
@@ -32264,8 +32295,6 @@ var CLI_BACKENDS = {
32264
32295
  cliCommand: "codex",
32265
32296
  protocol: "codex-app-server",
32266
32297
  authRequired: true,
32267
- npxPackage: "@agentclientprotocol/codex-acp@1.1.2",
32268
- acpArgs: [],
32269
32298
  appServerArgs: ["app-server", "--listen", "stdio://"],
32270
32299
  modes: [
32271
32300
  { id: "default", label: "Plan" },
@@ -32275,7 +32304,7 @@ var CLI_BACKENDS = {
32275
32304
  supportsSessionClose: true,
32276
32305
  supportsSessionLoad: true,
32277
32306
  requiresModelAtSpawn: false,
32278
- usesNpxBridge: true,
32307
+ usesNpxBridge: false,
32279
32308
  defaultMaxTokens: 1e5
32280
32309
  },
32281
32310
  "opencode-go": {
@@ -32366,22 +32395,6 @@ function getYoloModeId(cli) {
32366
32395
  function getAllBackendConfigs() {
32367
32396
  return Object.values(CLI_BACKENDS);
32368
32397
  }
32369
- function buildCodexConfigEnv(configOverrides, baseConfigJson) {
32370
- const map2 = parseBaseConfigMap(baseConfigJson);
32371
- for (const override of configOverrides ?? []) {
32372
- const eqIndex = override.indexOf("=");
32373
- if (eqIndex < 0) {
32374
- continue;
32375
- }
32376
- const key = override.slice(0, eqIndex);
32377
- const rawValue = override.slice(eqIndex + 1);
32378
- assignNestedKey(map2, key, parseOverrideValue(rawValue));
32379
- }
32380
- if (Object.keys(map2).length === 0) {
32381
- return void 0;
32382
- }
32383
- return JSON.stringify(map2);
32384
- }
32385
32398
  function buildConfigOverrideArgs(overrides) {
32386
32399
  return overrides.flatMap((override) => ["-c", override]);
32387
32400
  }
@@ -32408,45 +32421,6 @@ function mcpServerConfigsToAcp(servers) {
32408
32421
  headers: server.headers ?? []
32409
32422
  }));
32410
32423
  }
32411
- function parseBaseConfigMap(baseConfigJson) {
32412
- if (!baseConfigJson) {
32413
- return {};
32414
- }
32415
- try {
32416
- const parsed = JSON.parse(baseConfigJson);
32417
- if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
32418
- return parsed;
32419
- }
32420
- } catch {
32421
- }
32422
- return {};
32423
- }
32424
- function parseOverrideValue(raw) {
32425
- try {
32426
- return JSON.parse(raw);
32427
- } catch {
32428
- if (raw.length >= 2 && raw.startsWith('"') && raw.endsWith('"')) {
32429
- return raw.slice(1, -1);
32430
- }
32431
- return raw;
32432
- }
32433
- }
32434
- function assignNestedKey(target, dottedKey, value) {
32435
- const segments = dottedKey.split(".");
32436
- let cursor = target;
32437
- for (let i = 0; i < segments.length - 1; i += 1) {
32438
- const segment = segments[i];
32439
- const existing = cursor[segment];
32440
- if (typeof existing === "object" && existing !== null && !Array.isArray(existing)) {
32441
- cursor = existing;
32442
- } else {
32443
- const nested = {};
32444
- cursor[segment] = nested;
32445
- cursor = nested;
32446
- }
32447
- }
32448
- cursor[segments[segments.length - 1]] = value;
32449
- }
32450
32424
 
32451
32425
  // ../../packages/core-unified-agent/src/detector/CliDetector.ts
32452
32426
  var CLI_DETECT_LIST = Object.entries(CLI_BACKENDS).map(([id, backend]) => ({
@@ -33002,6 +32976,7 @@ var CODEX_METHODS = {
33002
32976
  THREAD_RESUME: "thread/resume",
33003
32977
  THREAD_READ: "thread/read",
33004
32978
  THREAD_ARCHIVE: "thread/archive",
32979
+ THREAD_UNARCHIVE: "thread/unarchive",
33005
32980
  TURN_START: "turn/start",
33006
32981
  TURN_INTERRUPT: "turn/interrupt",
33007
32982
  TURN_STEER: "turn/steer"
@@ -33030,6 +33005,7 @@ var CODEX_MCP_READY_STATUS = "ready";
33030
33005
  var CODEX_MCP_FAILED_STATUSES = /* @__PURE__ */ new Set(["failed", "error"]);
33031
33006
  var DEFAULT_MCP_STARTUP_TIMEOUT = 6e4;
33032
33007
  var STDERR_TAIL_LIMIT = 20;
33008
+ var SESSION_TEARDOWN_TIMEOUT_MS = 5e3;
33033
33009
  var CodexAppServerConnection = class extends BaseConnection {
33034
33010
  clientInfo;
33035
33011
  autoApprove;
@@ -33042,6 +33018,7 @@ var CodexAppServerConnection = class extends BaseConnection {
33042
33018
  pendingMcpReadyWaiters = /* @__PURE__ */ new Set();
33043
33019
  stdoutBuffer = "";
33044
33020
  pendingModel = null;
33021
+ pendingServiceTier = void 0;
33045
33022
  pendingEffort = null;
33046
33023
  codexHome = null;
33047
33024
  isDisconnecting = false;
@@ -33066,6 +33043,9 @@ var CodexAppServerConnection = class extends BaseConnection {
33066
33043
  setPendingModel(model) {
33067
33044
  this.pendingModel = model;
33068
33045
  }
33046
+ setPendingServiceTier(serviceTier) {
33047
+ this.pendingServiceTier = serviceTier;
33048
+ }
33069
33049
  setPendingEffort(effort) {
33070
33050
  this.pendingEffort = effort;
33071
33051
  }
@@ -33108,6 +33088,7 @@ var CodexAppServerConnection = class extends BaseConnection {
33108
33088
  cwd: options?.cwd ?? this.cwd,
33109
33089
  developerInstructions: options?.developerInstructions ?? null,
33110
33090
  model: options?.model ?? null,
33091
+ serviceTier: options?.serviceTier ?? null,
33111
33092
  approvalPolicy: options?.approvalPolicy ?? null,
33112
33093
  sandbox: options?.sandbox ?? null,
33113
33094
  config: options?.config ?? null
@@ -33118,10 +33099,11 @@ var CodexAppServerConnection = class extends BaseConnection {
33118
33099
  this.setState("ready");
33119
33100
  return response;
33120
33101
  } catch (error51) {
33102
+ const diagnosticError = this.withStderrDiagnostics(error51, "Codex initialize");
33121
33103
  this.setState("error");
33122
33104
  this.isDisconnecting = true;
33123
33105
  await this.disconnect();
33124
- throw error51;
33106
+ throw diagnosticError;
33125
33107
  }
33126
33108
  }
33127
33109
  async loadSession(threadId, options) {
@@ -33129,11 +33111,37 @@ var CodexAppServerConnection = class extends BaseConnection {
33129
33111
  try {
33130
33112
  response = await this.sendThreadResumeRequest(threadId, options);
33131
33113
  } catch (error51) {
33132
- const rolloutPath = this.findRolloutPathForThreadId(threadId);
33133
- if (!rolloutPath || !isMissingRolloutError(error51, threadId)) {
33134
- throw error51;
33114
+ if (isArchivedThreadError(error51, threadId)) {
33115
+ try {
33116
+ await this.sendRequest(
33117
+ CODEX_METHODS.THREAD_UNARCHIVE,
33118
+ { threadId }
33119
+ );
33120
+ } catch (unarchiveError) {
33121
+ const message = unarchiveError instanceof Error ? unarchiveError.message : String(unarchiveError);
33122
+ throw new Error(
33123
+ `thread/unarchive\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4(\uC6D0\uC778: archived \uC0C1\uD0DC\uB85C resume \uAC70\uBD80\uB428): ${message}`,
33124
+ { cause: unarchiveError }
33125
+ );
33126
+ }
33127
+ try {
33128
+ response = await this.sendThreadResumeRequest(threadId, options);
33129
+ } catch (resumeError) {
33130
+ await this.sendRequest(
33131
+ CODEX_METHODS.THREAD_ARCHIVE,
33132
+ { threadId },
33133
+ SESSION_TEARDOWN_TIMEOUT_MS
33134
+ ).catch(() => {
33135
+ });
33136
+ throw resumeError;
33137
+ }
33138
+ } else {
33139
+ const rolloutPath = this.findRolloutPathForThreadId(threadId);
33140
+ if (!rolloutPath || !isMissingRolloutError(error51, threadId)) {
33141
+ throw error51;
33142
+ }
33143
+ response = await this.sendThreadResumeRequest(threadId, options, rolloutPath);
33135
33144
  }
33136
- response = await this.sendThreadResumeRequest(threadId, options, rolloutPath);
33137
33145
  }
33138
33146
  this.threadId = response.thread.id;
33139
33147
  this.turnId = null;
@@ -33195,16 +33203,19 @@ var CodexAppServerConnection = class extends BaseConnection {
33195
33203
  this.on("exit", onExit);
33196
33204
  });
33197
33205
  try {
33206
+ const serviceTier = options?.serviceTier !== void 0 ? options.serviceTier : this.pendingServiceTier;
33198
33207
  const response = await this.sendRequest(
33199
33208
  CODEX_METHODS.TURN_START,
33200
33209
  {
33201
33210
  threadId: sessionId,
33202
33211
  input,
33203
33212
  model: options?.model ?? this.pendingModel ?? null,
33213
+ ...serviceTier !== void 0 ? { serviceTier } : {},
33204
33214
  effort: options?.effort ?? this.pendingEffort ?? null
33205
33215
  }
33206
33216
  );
33207
33217
  this.pendingModel = null;
33218
+ this.pendingServiceTier = void 0;
33208
33219
  this.pendingEffort = null;
33209
33220
  this.turnId = response.turn.id;
33210
33221
  await turnCompleted;
@@ -33231,14 +33242,16 @@ var CodexAppServerConnection = class extends BaseConnection {
33231
33242
  if (threadId && activeTurnId) {
33232
33243
  await this.sendRequest(
33233
33244
  CODEX_METHODS.TURN_INTERRUPT,
33234
- { threadId, turnId: activeTurnId }
33245
+ { threadId, turnId: activeTurnId },
33246
+ SESSION_TEARDOWN_TIMEOUT_MS
33235
33247
  ).catch(() => {
33236
33248
  });
33237
33249
  }
33238
33250
  if (threadId) {
33239
33251
  await this.sendRequest(
33240
33252
  CODEX_METHODS.THREAD_ARCHIVE,
33241
- { threadId }
33253
+ { threadId },
33254
+ SESSION_TEARDOWN_TIMEOUT_MS
33242
33255
  ).catch(() => {
33243
33256
  });
33244
33257
  }
@@ -33256,6 +33269,7 @@ var CodexAppServerConnection = class extends BaseConnection {
33256
33269
  cwd: options?.cwd ?? this.cwd,
33257
33270
  developerInstructions: options?.developerInstructions ?? null,
33258
33271
  model: options?.model ?? null,
33272
+ serviceTier: options?.serviceTier ?? null,
33259
33273
  approvalPolicy: options?.approvalPolicy ?? null,
33260
33274
  sandbox: options?.sandbox ?? null,
33261
33275
  config: options?.config ?? null
@@ -33273,6 +33287,9 @@ var CodexAppServerConnection = class extends BaseConnection {
33273
33287
  this.rejectPendingRequests(new Error("Codex \uC5F0\uACB0\uC774 \uC885\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4."));
33274
33288
  this.rejectPendingMcpReadyWaiters(new Error("Codex \uC5F0\uACB0\uC774 \uC885\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4."));
33275
33289
  this.stdoutBuffer = "";
33290
+ this.pendingModel = null;
33291
+ this.pendingServiceTier = void 0;
33292
+ this.pendingEffort = null;
33276
33293
  this.codexHome = null;
33277
33294
  try {
33278
33295
  await super.disconnect();
@@ -33721,6 +33738,7 @@ var CodexAppServerConnection = class extends BaseConnection {
33721
33738
  path: rolloutPath ?? null,
33722
33739
  cwd: options?.cwd ?? this.cwd,
33723
33740
  model: options?.model ?? null,
33741
+ serviceTier: options?.serviceTier ?? null,
33724
33742
  approvalPolicy: options?.approvalPolicy ?? null,
33725
33743
  sandbox: options?.sandbox ?? null,
33726
33744
  developerInstructions: options?.developerInstructions ?? null,
@@ -33840,6 +33858,9 @@ var CodexAppServerConnection = class extends BaseConnection {
33840
33858
  function isMissingRolloutError(error51, threadId) {
33841
33859
  return error51 instanceof Error && error51.message.includes(`no rollout found for thread id ${threadId}`);
33842
33860
  }
33861
+ function isArchivedThreadError(error51, threadId) {
33862
+ return error51 instanceof Error && error51.message.includes(`session ${threadId} is archived`);
33863
+ }
33843
33864
  function findRolloutPath(root, threadId) {
33844
33865
  if (!fs10.existsSync(root)) {
33845
33866
  return null;
@@ -33861,7 +33882,7 @@ function findRolloutPath(root, threadId) {
33861
33882
  }
33862
33883
 
33863
33884
  // ../../packages/core-unified-agent/src/client/UnifiedCodexAgentClient.ts
33864
- var CODEX_TURN_LEVEL_CONFIG_KEYS = /* @__PURE__ */ new Set(["effort", "model"]);
33885
+ var CODEX_TURN_LEVEL_CONFIG_KEYS = /* @__PURE__ */ new Set(["effort"]);
33865
33886
  var CODEX_THREAD_POLICY_CONFIG_KEYS = /* @__PURE__ */ new Set(["approvalPolicy", "sandbox"]);
33866
33887
  var UnifiedCodexAgentClient = class extends EventEmitter {
33867
33888
  connection = null;
@@ -33870,6 +33891,8 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
33870
33891
  currentSystemPrompt = null;
33871
33892
  firstPromptPending = null;
33872
33893
  pendingOverrides = null;
33894
+ currentModelSelection = {};
33895
+ archiveSessionOnDisconnect = false;
33873
33896
  detector = new CliDetector();
33874
33897
  on(event, listener) {
33875
33898
  return super.on(event, listener);
@@ -33889,9 +33912,6 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
33889
33912
  throw new Error("UnifiedCodexAgentClient\uB294 codex CLI\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4.");
33890
33913
  }
33891
33914
  this.validateModelEffort(options.model, options.effort);
33892
- if (shouldUseCodexAcp(options)) {
33893
- return this.connectAcp(options);
33894
- }
33895
33915
  return this.connectAppServer(options);
33896
33916
  }
33897
33917
  async connectAppServer(options) {
@@ -33900,6 +33920,7 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
33900
33920
  const command2 = options.cliPath ?? backend.cliCommand;
33901
33921
  const baseArgs = backend.appServerArgs ?? ["app-server", "--listen", "stdio://"];
33902
33922
  const modeMapping = this.resolveMode(options.yoloMode === false ? "default" : "yolo");
33923
+ const modelSelection = this.resolveModelSelection(options.model);
33903
33924
  const args = [
33904
33925
  ...baseArgs,
33905
33926
  ...this.buildStartupConfigArgs(options.configOverrides, options.mcpServers, modeMapping)
@@ -33923,13 +33944,14 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
33923
33944
  if (options.sessionId) {
33924
33945
  await connection.connect({
33925
33946
  skipThreadStart: true,
33926
- model: options.model
33947
+ model: modelSelection.providerModelId,
33948
+ serviceTier: modelSelection.serviceTier
33927
33949
  });
33928
33950
  await connection.loadSession(
33929
33951
  options.sessionId,
33930
33952
  this.buildCodexThreadDefaultsForResume(
33931
33953
  options.cwd,
33932
- options.model,
33954
+ modelSelection,
33933
33955
  null,
33934
33956
  modeMapping
33935
33957
  )
@@ -33937,7 +33959,8 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
33937
33959
  } else {
33938
33960
  await connection.connect({
33939
33961
  developerInstructions: void 0,
33940
- model: options.model,
33962
+ model: modelSelection.providerModelId,
33963
+ serviceTier: modelSelection.serviceTier,
33941
33964
  approvalPolicy: modeMapping.approvalPolicy,
33942
33965
  sandbox: modeMapping.sandbox
33943
33966
  });
@@ -33951,6 +33974,8 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
33951
33974
  this.sessionCwd = options.cwd;
33952
33975
  this.currentSystemPrompt = options.systemPrompt ?? null;
33953
33976
  this.firstPromptPending = options.sessionId ? null : this.currentSystemPrompt;
33977
+ this.currentModelSelection = modelSelection;
33978
+ this.archiveSessionOnDisconnect = options.archiveSessionOnDisconnect === true;
33954
33979
  this.pendingOverrides = {
33955
33980
  turnConfig: options.effort ? { effort: options.effort } : {},
33956
33981
  threadConfig: {
@@ -33964,71 +33989,17 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
33964
33989
  session: { sessionId }
33965
33990
  };
33966
33991
  }
33967
- async connectAcp(options) {
33968
- const cleanEnv = cleanEnvironment(process.env, options.env);
33969
- const spawnConfig = createSpawnConfig("codex", options);
33970
- const codexConfigEnv = buildCodexConfigEnv(
33971
- options.configOverrides,
33972
- cleanEnv.CODEX_CONFIG
33973
- );
33974
- const connection = new AcpConnection({
33975
- command: spawnConfig.command,
33976
- args: spawnConfig.args,
33977
- cliType: "codex",
33978
- cwd: options.cwd,
33979
- env: { ...cleanEnv, ...codexConfigEnv ? { CODEX_CONFIG: codexConfigEnv } : {} },
33980
- requestTimeout: options.timeout ?? 6e5,
33981
- initTimeout: options.timeout ?? 6e4,
33982
- promptIdleTimeout: options.promptIdleTimeout ?? 6e5,
33983
- clientInfo: options.clientInfo,
33984
- autoApprove: options.autoApprove,
33985
- fsAccess: options.fsAccess
33986
- });
33987
- this.connection = connection;
33988
- this.setupEventForwarding();
33989
- const recentLogs = [];
33990
- const collectLog = (message) => {
33991
- recentLogs.push(message);
33992
- if (recentLogs.length > 30) {
33993
- recentLogs.shift();
33994
- }
33995
- };
33996
- connection.on("log", collectLog);
33997
- let session;
33998
- try {
33999
- session = await connection.connect(
34000
- options.cwd,
34001
- options.sessionId,
34002
- this.resolveAcpMcpServers(options.mcpServers)
34003
- );
34004
- } catch (error51) {
34005
- const connectionError = this.buildConnectionError(error51, recentLogs);
34006
- await this.cleanupFailedConnection();
34007
- throw connectionError;
34008
- } finally {
34009
- connection.off("log", collectLog);
34010
- }
34011
- await this.finalizeAcpConnect(options, session);
34012
- return {
34013
- cli: "codex",
34014
- protocol: "acp",
34015
- session
34016
- };
34017
- }
34018
33992
  async disconnect() {
34019
33993
  if (!this.connection) {
34020
33994
  this.clearSessionState();
34021
33995
  return;
34022
33996
  }
34023
- const conn = this.connection;
34024
- if (this.sessionId && conn instanceof AcpConnection && conn.canResetSession) {
34025
- try {
34026
- await conn.endSession(this.sessionId);
34027
- } catch {
34028
- }
33997
+ if (this.archiveSessionOnDisconnect && this.sessionId) {
33998
+ await this.connection.endSession().catch(() => {
33999
+ });
34029
34000
  }
34030
- await conn.disconnect();
34031
- conn.removeAllListeners();
34001
+ await this.connection.disconnect();
34002
+ this.connection.removeAllListeners();
34032
34003
  this.connection = null;
34033
34004
  this.clearSessionState();
34034
34005
  }
@@ -34036,14 +34007,7 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
34036
34007
  if (!this.connection) {
34037
34008
  throw new Error("\uC5F0\uACB0\uB418\uC5B4 \uC788\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4");
34038
34009
  }
34039
- if (this.connection instanceof AcpConnection) {
34040
- if (!this.sessionId) {
34041
- throw new Error("\uC5F0\uACB0\uB418\uC5B4 \uC788\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4");
34042
- }
34043
- await this.connection.endSession(this.sessionId);
34044
- } else {
34045
- await this.connection.endSession();
34046
- }
34010
+ await this.connection.endSession();
34047
34011
  this.sessionId = null;
34048
34012
  this.sessionCwd = null;
34049
34013
  }
@@ -34058,7 +34022,7 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
34058
34022
  }
34059
34023
  return {
34060
34024
  cli: "codex",
34061
- protocol: this.connection instanceof AcpConnection ? "acp" : "codex-app-server",
34025
+ protocol: "codex-app-server",
34062
34026
  sessionId: this.sessionId,
34063
34027
  state: this.connection.connectionState
34064
34028
  };
@@ -34070,12 +34034,6 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
34070
34034
  if (!this.connection) {
34071
34035
  throw new Error("\uC5F0\uACB0\uB418\uC5B4 \uC788\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4");
34072
34036
  }
34073
- if (this.connection instanceof AcpConnection) {
34074
- if (!this.sessionId) {
34075
- throw new Error("\uC5F0\uACB0\uB418\uC5B4 \uC788\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4");
34076
- }
34077
- return this.sendPromptWithPendingSystemPrompt(content);
34078
- }
34079
34037
  this.applyPendingOverrides();
34080
34038
  const input = typeof content === "string" ? [{ type: "text", text: content, text_elements: [] }] : content.map((block) => "text" in block ? { type: "text", text: block.text, text_elements: [] } : { type: "text", text: JSON.stringify(block), text_elements: [] });
34081
34039
  const systemPrompt = this.firstPromptPending;
@@ -34086,53 +34044,20 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
34086
34044
  }
34087
34045
  return { stopReason: "end_turn" };
34088
34046
  }
34089
- async sendPromptWithPendingSystemPrompt(content) {
34090
- if (!(this.connection instanceof AcpConnection) || !this.sessionId) {
34091
- throw new Error("\uC5F0\uACB0\uB418\uC5B4 \uC788\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4");
34092
- }
34093
- const systemPrompt = this.firstPromptPending;
34094
- if (!systemPrompt) {
34095
- return this.connection.sendPrompt(this.sessionId, content);
34096
- }
34097
- const userBlocks = typeof content === "string" ? [{ type: "text", text: content }] : content;
34098
- const response = await this.connection.sendPrompt(this.sessionId, [
34099
- { type: "text", text: systemPrompt },
34100
- ...userBlocks
34101
- ]);
34102
- this.firstPromptPending = null;
34103
- return response;
34104
- }
34105
34047
  async cancelPrompt() {
34106
34048
  if (!this.connection) {
34107
34049
  throw new Error("\uC5F0\uACB0\uB418\uC5B4 \uC788\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4");
34108
34050
  }
34109
- if (this.connection instanceof AcpConnection) {
34110
- if (!this.sessionId) {
34111
- throw new Error("\uC5F0\uACB0\uB418\uC5B4 \uC788\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4");
34112
- }
34113
- await this.connection.cancelSession(this.sessionId);
34114
- return;
34115
- }
34116
34051
  await this.connection.cancelPrompt();
34117
34052
  }
34118
34053
  async setModel(model) {
34119
- if (this.connection instanceof AcpConnection && this.sessionId) {
34120
- await this.connection.setModel(this.sessionId, model);
34121
- return;
34122
- }
34123
- this.ensurePendingOverrides().model = model;
34054
+ const selection = this.resolveModelSelection(model);
34055
+ this.currentModelSelection = selection;
34056
+ this.ensurePendingOverrides().model = selection;
34124
34057
  }
34125
34058
  async setConfigOption(configId, value) {
34126
- if (this.connection instanceof AcpConnection && this.sessionId) {
34127
- if (configId === "model") {
34128
- await this.connection.setModel(this.sessionId, value);
34129
- return;
34130
- }
34131
- await this.connection.setConfigOption(
34132
- this.sessionId,
34133
- configId === "effort" ? "reasoning_effort" : configId,
34134
- value
34135
- );
34059
+ if (configId === "model") {
34060
+ await this.setModel(value);
34136
34061
  return;
34137
34062
  }
34138
34063
  const pending = this.ensurePendingOverrides();
@@ -34144,10 +34069,6 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
34144
34069
  this.emitTyped("log", `[codex] config '${configId}' will apply on next thread/start or thread/resume`);
34145
34070
  }
34146
34071
  async setMode(mode) {
34147
- if (this.connection instanceof AcpConnection && this.sessionId) {
34148
- await this.connection.setMode(this.sessionId, this.resolveAcpMode(mode));
34149
- return;
34150
- }
34151
34072
  const resolved = this.resolveMode(mode);
34152
34073
  const pending = this.ensurePendingOverrides();
34153
34074
  pending.threadConfig.approvalPolicy = resolved.approvalPolicy;
@@ -34170,17 +34091,6 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
34170
34091
  if (!this.connection) {
34171
34092
  throw new Error("\uC5F0\uACB0\uB418\uC5B4 \uC788\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4");
34172
34093
  }
34173
- if (this.connection instanceof AcpConnection) {
34174
- await this.connection.loadSession({
34175
- sessionId,
34176
- cwd: this.sessionCwd ?? process.cwd(),
34177
- mcpServers: this.resolveAcpMcpServers(mcpServers)
34178
- });
34179
- this.sessionId = sessionId;
34180
- this.currentSystemPrompt = null;
34181
- this.firstPromptPending = null;
34182
- return;
34183
- }
34184
34094
  if (mcpServers?.length) {
34185
34095
  this.emitTyped(
34186
34096
  "log",
@@ -34192,7 +34102,7 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
34192
34102
  sessionId,
34193
34103
  this.buildCodexThreadDefaultsForResume(
34194
34104
  targetCwd,
34195
- void 0,
34105
+ this.currentModelSelection,
34196
34106
  null,
34197
34107
  this.pendingOverrides?.threadConfig ?? {}
34198
34108
  )
@@ -34206,24 +34116,6 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
34206
34116
  throw new Error("\uC5F0\uACB0\uB418\uC5B4 \uC788\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4");
34207
34117
  }
34208
34118
  const targetCwd = cwd ?? this.sessionCwd ?? process.cwd();
34209
- if (this.connection instanceof AcpConnection) {
34210
- if (!this.sessionId) {
34211
- throw new Error("\uC5F0\uACB0\uB418\uC5B4 \uC788\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4");
34212
- }
34213
- if (!this.connection.canResetSession) {
34214
- throw new Error("[codex] \uC138\uC158 \uB9AC\uC14B\uC744 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. disconnect() \uD6C4 \uC7AC\uC5F0\uACB0\uD558\uC138\uC694.");
34215
- }
34216
- await this.connection.endSession(this.sessionId);
34217
- const session = await this.connection.reconnectSession(targetCwd);
34218
- this.sessionId = session.sessionId;
34219
- this.sessionCwd = targetCwd;
34220
- this.firstPromptPending = this.currentSystemPrompt;
34221
- return {
34222
- cli: "codex",
34223
- protocol: "acp",
34224
- session
34225
- };
34226
- }
34227
34119
  const result = await this.connection.resetSession(
34228
34120
  this.buildCodexThreadDefaultsForReset(targetCwd)
34229
34121
  );
@@ -34239,33 +34131,14 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
34239
34131
  protocol: "codex-app-server"
34240
34132
  };
34241
34133
  }
34242
- resolveAcpMcpServers(servers) {
34243
- return servers?.length ? mcpServerConfigsToAcp(servers) : [];
34244
- }
34245
- async finalizeAcpConnect(options, session) {
34246
- this.sessionId = session.sessionId;
34247
- this.sessionCwd = options.cwd;
34248
- this.currentSystemPrompt = options.systemPrompt ?? null;
34249
- this.firstPromptPending = options.sessionId ? null : this.currentSystemPrompt;
34250
- this.pendingOverrides = {
34251
- turnConfig: {},
34252
- threadConfig: {}
34253
- };
34254
- const mode = options.yoloMode === false ? "default" : "yolo";
34255
- await this.setMode(mode);
34256
- if (options.model) {
34257
- await this.setModel(options.model);
34258
- }
34259
- if (options.effort) {
34260
- await this.setConfigOption("effort", options.effort);
34261
- }
34262
- }
34263
34134
  clearSessionState() {
34264
34135
  this.sessionId = null;
34265
34136
  this.sessionCwd = null;
34266
34137
  this.currentSystemPrompt = null;
34267
34138
  this.firstPromptPending = null;
34268
34139
  this.pendingOverrides = null;
34140
+ this.currentModelSelection = {};
34141
+ this.archiveSessionOnDisconnect = false;
34269
34142
  }
34270
34143
  validateModelEffort(modelId, effort) {
34271
34144
  if (!effort) {
@@ -34288,69 +34161,7 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
34288
34161
  }
34289
34162
  setupEventForwarding() {
34290
34163
  if (!this.connection) return;
34291
- const connection = this.connection;
34292
- if (connection instanceof AcpConnection) {
34293
- this.setupAcpEventForwarding(connection);
34294
- return;
34295
- }
34296
- this.setupAppServerEventForwarding(connection);
34297
- }
34298
- setupAcpEventForwarding(connection) {
34299
- connection.on("stateChange", (state) => {
34300
- this.emitTyped("stateChange", state);
34301
- });
34302
- connection.on("userMessageChunk", (text, sessionId) => {
34303
- this.emitTyped("userMessageChunk", text, sessionId);
34304
- });
34305
- connection.on("messageChunk", (text, sessionId) => {
34306
- this.emitTyped("messageChunk", text, sessionId);
34307
- });
34308
- connection.on("thoughtChunk", (text, sessionId) => {
34309
- this.emitTyped("thoughtChunk", text, sessionId);
34310
- });
34311
- connection.on("toolCall", (title, status, sessionId, data) => {
34312
- this.emitTyped("toolCall", title, status, sessionId, data);
34313
- });
34314
- connection.on("toolCallUpdate", (title, status, sessionId, data) => {
34315
- this.emitTyped("toolCallUpdate", title, status, sessionId, data);
34316
- });
34317
- connection.on("plan", (plan, sessionId) => {
34318
- this.emitTyped("plan", plan, sessionId);
34319
- });
34320
- connection.on("availableCommandsUpdate", (commands, sessionId) => {
34321
- this.emitTyped("availableCommandsUpdate", commands, sessionId);
34322
- });
34323
- connection.on("promptComplete", (sessionId) => {
34324
- this.emitTyped("promptComplete", sessionId);
34325
- });
34326
- connection.on("permissionRequest", (params, resolve3) => {
34327
- this.emitTyped(
34328
- "permissionRequest",
34329
- params,
34330
- resolve3
34331
- );
34332
- });
34333
- connection.on("sessionUpdate", (update) => {
34334
- this.emitTyped("sessionUpdate", update);
34335
- });
34336
- connection.on("fileRead", (params, resolve3) => {
34337
- this.emitTyped("fileRead", params, resolve3);
34338
- });
34339
- connection.on("fileWrite", (params, resolve3) => {
34340
- this.emitTyped("fileWrite", params, resolve3);
34341
- });
34342
- connection.on("error", (err) => {
34343
- this.emitTyped("error", err);
34344
- });
34345
- connection.on("exit", (code, signal) => {
34346
- this.emitTyped("exit", code, signal);
34347
- });
34348
- connection.on("log", (msg) => {
34349
- this.emitTyped("log", msg);
34350
- });
34351
- connection.on("logEntry", (entry) => {
34352
- this.emitTyped("logEntry", entry);
34353
- });
34164
+ this.setupAppServerEventForwarding(this.connection);
34354
34165
  }
34355
34166
  setupAppServerEventForwarding(connection) {
34356
34167
  connection.on("stateChange", (state) => {
@@ -34413,15 +34224,15 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
34413
34224
  if (!this.pendingOverrides || !this.connection) {
34414
34225
  return;
34415
34226
  }
34416
- if (this.connection instanceof AcpConnection) {
34417
- return;
34418
- }
34419
34227
  if (this.pendingOverrides.model) {
34420
- this.connection.setPendingModel(this.pendingOverrides.model);
34228
+ this.connection.setPendingModel(this.pendingOverrides.model.providerModelId ?? null);
34229
+ this.connection.setPendingServiceTier(this.pendingOverrides.model.serviceTier ?? null);
34421
34230
  this.pendingOverrides.model = void 0;
34422
34231
  }
34423
34232
  if (this.pendingOverrides.turnConfig.model) {
34424
- this.connection.setPendingModel(this.pendingOverrides.turnConfig.model);
34233
+ const selection = this.resolveModelSelection(this.pendingOverrides.turnConfig.model);
34234
+ this.connection.setPendingModel(selection.providerModelId ?? null);
34235
+ this.connection.setPendingServiceTier(selection.serviceTier ?? null);
34425
34236
  delete this.pendingOverrides.turnConfig.model;
34426
34237
  }
34427
34238
  if (this.pendingOverrides.turnConfig.effort) {
@@ -34436,19 +34247,22 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
34436
34247
  const config2 = configEntries.length > 0 ? Object.fromEntries(configEntries) : void 0;
34437
34248
  return {
34438
34249
  cwd,
34250
+ model: this.currentModelSelection.providerModelId,
34251
+ serviceTier: this.currentModelSelection.serviceTier,
34439
34252
  approvalPolicy,
34440
34253
  sandbox,
34441
34254
  developerInstructions: void 0,
34442
34255
  config: config2
34443
34256
  };
34444
34257
  }
34445
- buildCodexThreadDefaultsForResume(cwd, model, systemPrompt, threadConfig) {
34258
+ buildCodexThreadDefaultsForResume(cwd, modelSelection, systemPrompt, threadConfig) {
34446
34259
  const { approvalPolicy, sandbox } = threadConfig;
34447
34260
  const configEntries = Object.entries(threadConfig).filter(([key]) => !CODEX_THREAD_POLICY_CONFIG_KEYS.has(key));
34448
34261
  const config2 = configEntries.length > 0 ? Object.fromEntries(configEntries) : void 0;
34449
34262
  return {
34450
34263
  cwd,
34451
- ...model ? { model } : {},
34264
+ ...modelSelection.providerModelId ? { model: modelSelection.providerModelId } : {},
34265
+ ...modelSelection.serviceTier ? { serviceTier: modelSelection.serviceTier } : {},
34452
34266
  approvalPolicy,
34453
34267
  sandbox,
34454
34268
  developerInstructions: systemPrompt ?? void 0,
@@ -34465,16 +34279,6 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
34465
34279
  return { approvalPolicy: "on-request", sandbox: "read-only" };
34466
34280
  }
34467
34281
  }
34468
- resolveAcpMode(modeId) {
34469
- switch (modeId) {
34470
- case "autoEdit":
34471
- return "agent";
34472
- case "yolo":
34473
- return "agent-full-access";
34474
- default:
34475
- return "read-only";
34476
- }
34477
- }
34478
34282
  buildStartupConfigArgs(overrides, servers, modeMapping) {
34479
34283
  const configArgs = [
34480
34284
  ...modeMapping ? [
@@ -34504,6 +34308,10 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
34504
34308
  return;
34505
34309
  }
34506
34310
  try {
34311
+ if (this.archiveSessionOnDisconnect && this.sessionId) {
34312
+ await this.connection.endSession().catch(() => {
34313
+ });
34314
+ }
34507
34315
  await this.connection.disconnect();
34508
34316
  } catch {
34509
34317
  }
@@ -34511,65 +34319,17 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
34511
34319
  this.connection = null;
34512
34320
  this.clearSessionState();
34513
34321
  }
34514
- buildConnectionError(error51, recentLogs) {
34515
- if (getBackendConfig("codex").authRequired && this.isAuthenticationError(error51, recentLogs)) {
34516
- return new Error(
34517
- "[codex] \uC778\uC99D\uC774 \uD544\uC694\uD558\uAC70\uB098 \uC778\uC99D\uC774 \uB9CC\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 \uD574\uB2F9 CLI\uC5D0\uC11C \uB85C\uADF8\uC778/\uC778\uC99D\uC744 \uC644\uB8CC\uD55C \uB4A4 \uB2E4\uC2DC \uC2DC\uB3C4\uD574\uC8FC\uC138\uC694."
34518
- );
34519
- }
34520
- if (error51 instanceof Error) {
34521
- return error51;
34522
- }
34523
- if (typeof error51 === "object" && error51 !== null) {
34524
- const obj = error51;
34525
- if (typeof obj.message === "string") {
34526
- const code = typeof obj.code === "number" ? ` (code: ${obj.code})` : "";
34527
- const data = obj.data ? ` \u2014 ${JSON.stringify(obj.data)}` : "";
34528
- return new Error(`${obj.message}${code}${data}`);
34529
- }
34530
- return new Error(JSON.stringify(error51));
34531
- }
34532
- return new Error(String(error51));
34533
- }
34534
- isAuthenticationError(error51, recentLogs) {
34535
- const authPatterns = [
34536
- /auth_required/i,
34537
- /authentication required/i,
34538
- /not authenticated/i,
34539
- /please login/i,
34540
- /please log in/i,
34541
- /sign in/i,
34542
- /reauth/i,
34543
- /unauthorized/i,
34544
- /invalid api key/i
34545
- ];
34546
- if (this.matchAnyPattern(this.extractErrorText(error51), authPatterns)) {
34547
- return true;
34548
- }
34549
- return recentLogs.some((log) => this.matchAnyPattern(log, authPatterns));
34550
- }
34551
- extractErrorText(error51) {
34552
- if (error51 instanceof Error) {
34553
- const code = error51.code;
34554
- if (code === -32e3) {
34555
- return `auth_required ${error51.message}`;
34556
- }
34557
- return error51.message;
34558
- }
34559
- if (typeof error51 === "string") {
34560
- return error51;
34322
+ resolveModelSelection(modelId) {
34323
+ if (!modelId) {
34324
+ return {};
34561
34325
  }
34562
- return String(error51);
34563
- }
34564
- matchAnyPattern(text, patterns) {
34565
- return patterns.some((pattern) => pattern.test(text));
34326
+ const model = getProviderModels("codex").models.find((entry) => entry.modelId === modelId);
34327
+ return {
34328
+ providerModelId: model?.providerModelId ?? modelId,
34329
+ serviceTier: model?.serviceTier
34330
+ };
34566
34331
  }
34567
34332
  };
34568
- function shouldUseCodexAcp(options) {
34569
- const value = options.env?.CODEX_USE_ACP ?? process.env.CODEX_USE_ACP;
34570
- const normalized = value?.trim().toLowerCase();
34571
- return normalized !== void 0 && normalized !== "false" && normalized !== "0";
34572
- }
34573
34333
  var UnifiedCursorAgentClient = class extends EventEmitter {
34574
34334
  connection = null;
34575
34335
  sessionId = null;
@@ -38086,9 +37846,243 @@ function stripIpv6Brackets2(host) {
38086
37846
 
38087
37847
  // core/host/operations/types.ts
38088
37848
  var MAX_GROUP_NAME_LENGTH = 64;
37849
+ var DELETION_GRACE_MS = 8e3;
38089
37850
 
38090
- // core/host/durable-state.ts
38091
- var STATE_VERSION = 2;
37851
+ // core/host/deferred-deletion.ts
37852
+ var DeferredDeletionError = class extends Error {
37853
+ status;
37854
+ constructor(status, message) {
37855
+ super(message);
37856
+ this.name = "DeferredDeletionError";
37857
+ this.status = status;
37858
+ }
37859
+ };
37860
+ var OPERATION_DELETED_EVENT_CHANNEL = "operation:deleted";
37861
+ var OPERATION_RESTORED_EVENT_CHANNEL = "operation:restored";
37862
+ var OPERATION_PURGED_EVENT_CHANNEL = "operation:purged";
37863
+ var PURGE_RETRY_MS = 1e3;
37864
+ function createDeferredDeletionCoordinator(deps) {
37865
+ const now = deps.now ?? Date.now;
37866
+ const randomId = deps.randomId ?? crypto2.randomUUID;
37867
+ const setTimer = deps.setTimer ?? setTimeout;
37868
+ const clearTimer = deps.clearTimer ?? clearTimeout;
37869
+ let tombstones = [];
37870
+ let timer = null;
37871
+ function list() {
37872
+ return tombstones;
37873
+ }
37874
+ function load(nextTombstones) {
37875
+ tombstones = [...nextTombstones];
37876
+ schedule();
37877
+ }
37878
+ function deleteOperation(operationId) {
37879
+ sweepExpired();
37880
+ const existing = tombstones.find((item) => item.kind === "operation" && item.targetId === operationId);
37881
+ if (existing) return toReceipt(existing);
37882
+ const operation = deps.operations.get(operationId);
37883
+ if (!operation) return null;
37884
+ const previousOperations = deps.operations.list();
37885
+ const deletedAt = now();
37886
+ const tombstone = {
37887
+ deletionId: randomId(),
37888
+ targetId: operation.id,
37889
+ deletedAt,
37890
+ expiresAt: deletedAt + DELETION_GRACE_MS,
37891
+ kind: "operation",
37892
+ operation
37893
+ };
37894
+ const nextTombstones = [...tombstones, tombstone];
37895
+ deps.operations.delete(operationId);
37896
+ try {
37897
+ deps.save(nextTombstones);
37898
+ } catch (error51) {
37899
+ deps.operations.replace(previousOperations);
37900
+ throw error51;
37901
+ }
37902
+ tombstones = nextTombstones;
37903
+ publishOperation(OPERATION_DELETED_EVENT_CHANNEL, operation);
37904
+ schedule();
37905
+ return toReceipt(tombstone);
37906
+ }
37907
+ function deleteTheater(theaterId) {
37908
+ sweepExpired();
37909
+ const existing = tombstones.find((item) => item.kind === "theater" && item.targetId === theaterId);
37910
+ if (existing) return toReceipt(existing);
37911
+ const theater = deps.theaters.get(theaterId);
37912
+ if (!theater) return null;
37913
+ const previousTheaters = deps.theaters.list();
37914
+ const previousOperations = deps.operations.list();
37915
+ const previousGroups = deps.operations.listAllGroups();
37916
+ const deletedOperations = deps.operations.listByTheater(theaterId);
37917
+ const deletedGroups = deps.operations.listGroups(theaterId);
37918
+ const deletedAt = now();
37919
+ const tombstone = {
37920
+ deletionId: randomId(),
37921
+ targetId: theater.id,
37922
+ deletedAt,
37923
+ expiresAt: deletedAt + DELETION_GRACE_MS,
37924
+ kind: "theater",
37925
+ theater,
37926
+ operations: deletedOperations,
37927
+ groups: deletedGroups
37928
+ };
37929
+ const nextTombstones = [...tombstones, tombstone];
37930
+ deps.theaters.remove(theaterId);
37931
+ deps.operations.deleteByTheater(theaterId);
37932
+ try {
37933
+ deps.save(nextTombstones);
37934
+ } catch (error51) {
37935
+ deps.theaters.restore(previousTheaters);
37936
+ deps.operations.replace(previousOperations);
37937
+ deps.operations.replaceGroups(previousGroups);
37938
+ throw error51;
37939
+ }
37940
+ tombstones = nextTombstones;
37941
+ deps.unregisterTheaterWorkspaces(theaterId);
37942
+ for (const operation of deletedOperations) publishOperation(OPERATION_DELETED_EVENT_CHANNEL, operation);
37943
+ schedule();
37944
+ return toReceipt(tombstone);
37945
+ }
37946
+ async function restore(deletionId) {
37947
+ const restoredAt = now();
37948
+ const tombstone = tombstones.find((item) => item.deletionId === deletionId);
37949
+ if (!tombstone || tombstone.expiresAt <= restoredAt) {
37950
+ if (tombstone) sweepExpired();
37951
+ throw new DeferredDeletionError(404, "deletion_not_found");
37952
+ }
37953
+ if (tombstone.kind === "operation") {
37954
+ if (deps.operations.get(tombstone.targetId) || !deps.theaters.get(tombstone.operation.theaterId)) {
37955
+ throw new DeferredDeletionError(409, "restore_conflict");
37956
+ }
37957
+ const previousOperations2 = deps.operations.list();
37958
+ const nextTombstones2 = tombstones.filter((item) => item.deletionId !== deletionId);
37959
+ deps.operations.replace([...previousOperations2, tombstone.operation]);
37960
+ try {
37961
+ deps.save(nextTombstones2);
37962
+ } catch (error51) {
37963
+ deps.operations.replace(previousOperations2);
37964
+ throw error51;
37965
+ }
37966
+ tombstones = nextTombstones2;
37967
+ publishOperation(OPERATION_RESTORED_EVENT_CHANNEL, tombstone.operation);
37968
+ schedule();
37969
+ return { ok: true, kind: tombstone.kind, targetId: tombstone.targetId };
37970
+ }
37971
+ await deps.validateTheaterRestore(tombstone.theater);
37972
+ if (deps.theaters.get(tombstone.targetId) || tombstone.operations.some((operation) => deps.operations.get(operation.id)) || hasGroupConflict(tombstone.groups, deps.operations.listAllGroups())) {
37973
+ throw new DeferredDeletionError(409, "restore_conflict");
37974
+ }
37975
+ const previousTheaters = deps.theaters.list();
37976
+ const previousOperations = deps.operations.list();
37977
+ const previousGroups = deps.operations.listAllGroups();
37978
+ const nextTombstones = tombstones.filter((item) => item.deletionId !== deletionId);
37979
+ deps.theaters.restore([...previousTheaters, tombstone.theater]);
37980
+ deps.operations.replace([...previousOperations, ...tombstone.operations]);
37981
+ deps.operations.replaceGroups([...previousGroups, ...tombstone.groups]);
37982
+ try {
37983
+ deps.save(nextTombstones);
37984
+ } catch (error51) {
37985
+ deps.theaters.restore(previousTheaters);
37986
+ deps.operations.replace(previousOperations);
37987
+ deps.operations.replaceGroups(previousGroups);
37988
+ throw error51;
37989
+ }
37990
+ tombstones = nextTombstones;
37991
+ await deps.registerTheaterWorkspace(tombstone.theater);
37992
+ for (const operation of tombstone.operations) publishOperation(OPERATION_RESTORED_EVENT_CHANNEL, operation);
37993
+ schedule();
37994
+ return { ok: true, kind: tombstone.kind, targetId: tombstone.targetId };
37995
+ }
37996
+ function sweepExpired() {
37997
+ const cutoff = now();
37998
+ const expired = tombstones.filter((item) => item.expiresAt <= cutoff);
37999
+ if (expired.length === 0) {
38000
+ schedule();
38001
+ return;
38002
+ }
38003
+ const expiredIds = new Set(expired.map((item) => item.deletionId));
38004
+ const nextTombstones = tombstones.filter((item) => !expiredIds.has(item.deletionId));
38005
+ try {
38006
+ deps.save(nextTombstones);
38007
+ } catch (error51) {
38008
+ schedule(PURGE_RETRY_MS);
38009
+ throw error51;
38010
+ }
38011
+ tombstones = nextTombstones;
38012
+ for (const tombstone of expired) {
38013
+ if (tombstone.kind === "operation") {
38014
+ publishOperation(OPERATION_PURGED_EVENT_CHANNEL, tombstone.operation);
38015
+ } else {
38016
+ for (const operation of tombstone.operations) publishOperation(OPERATION_PURGED_EVENT_CHANNEL, operation);
38017
+ }
38018
+ }
38019
+ schedule();
38020
+ }
38021
+ function hasPendingOperation(operationId) {
38022
+ sweepExpired();
38023
+ return tombstones.some((item) => item.kind === "operation" ? item.targetId === operationId : item.operations.some((operation) => operation.id === operationId));
38024
+ }
38025
+ function hasPendingTheater(theaterId) {
38026
+ sweepExpired();
38027
+ return tombstones.some((item) => item.kind === "theater" && item.targetId === theaterId);
38028
+ }
38029
+ function schedule(forcedDelay) {
38030
+ if (timer) clearTimer(timer);
38031
+ timer = null;
38032
+ if (forcedDelay !== void 0) {
38033
+ timer = setTimer(runScheduledSweep, forcedDelay);
38034
+ timer.unref?.();
38035
+ return;
38036
+ }
38037
+ const nearest = tombstones.reduce((minimum, item) => minimum === null ? item.expiresAt : Math.min(minimum, item.expiresAt), null);
38038
+ if (nearest === null) return;
38039
+ timer = setTimer(runScheduledSweep, Math.max(0, nearest - now()));
38040
+ timer.unref?.();
38041
+ }
38042
+ function runScheduledSweep() {
38043
+ timer = null;
38044
+ try {
38045
+ sweepExpired();
38046
+ } catch {
38047
+ }
38048
+ }
38049
+ function dispose() {
38050
+ if (timer) clearTimer(timer);
38051
+ timer = null;
38052
+ }
38053
+ function publishOperation(channel, operation) {
38054
+ deps.publish(channel, {
38055
+ operationId: operation.id,
38056
+ pluginId: operation.pluginId,
38057
+ type: operation.type,
38058
+ ...channel === OPERATION_RESTORED_EVENT_CHANNEL ? { operation } : {}
38059
+ });
38060
+ }
38061
+ return {
38062
+ list,
38063
+ load,
38064
+ deleteOperation,
38065
+ deleteTheater,
38066
+ restore,
38067
+ sweepExpired,
38068
+ hasPendingOperation,
38069
+ hasPendingTheater,
38070
+ dispose
38071
+ };
38072
+ }
38073
+ function toReceipt(tombstone) {
38074
+ return {
38075
+ deletionId: tombstone.deletionId,
38076
+ kind: tombstone.kind,
38077
+ targetId: tombstone.targetId,
38078
+ expiresAt: tombstone.expiresAt
38079
+ };
38080
+ }
38081
+ function hasGroupConflict(groups, existing) {
38082
+ const existingIds = new Set(existing.map((group) => group.id));
38083
+ return groups.some((group) => existingIds.has(group.id));
38084
+ }
38085
+ var STATE_VERSION = 3;
38092
38086
  var STATE_LOCK_DIR_NAME = "state.lock";
38093
38087
  var STATE_LOCK_OWNER_FILE_NAME = "owner.json";
38094
38088
  var STATE_TEMP_PREFIX = ".state.";
@@ -38135,25 +38129,37 @@ function sanitizeDurableConsoleState(value) {
38135
38129
  version: STATE_VERSION,
38136
38130
  theaters: readTheaterRegistrations(upgraded.theaters),
38137
38131
  operations: readOperations(upgraded.operations),
38138
- groups: readOperationGroups(upgraded.groups)
38132
+ groups: readOperationGroups(upgraded.groups),
38133
+ deletionTombstones: readDeletionTombstones(upgraded.deletionTombstones)
38139
38134
  };
38140
38135
  }
38141
38136
  function emptyDurableConsoleState() {
38142
- return { version: STATE_VERSION, theaters: [], operations: [], groups: [] };
38137
+ return { version: STATE_VERSION, theaters: [], operations: [], groups: [], deletionTombstones: [] };
38143
38138
  }
38144
38139
  function migrateToCurrentVersion(value) {
38145
- if (value.version === 1) return migrateV1ToV2(value);
38146
- return value;
38140
+ let current = value;
38141
+ if (current.version === 1) current = migrateV1ToV2(current);
38142
+ if (current.version === 2) current = migrateV2ToV3(current);
38143
+ return current;
38147
38144
  }
38148
38145
  function migrateV1ToV2(v1) {
38149
38146
  const operations = Array.isArray(v1.operations) ? v1.operations.map(migrateV1Operation) : [];
38150
38147
  return {
38151
- version: STATE_VERSION,
38148
+ version: 2,
38152
38149
  theaters: v1.theaters ?? [],
38153
38150
  operations,
38154
38151
  groups: []
38155
38152
  };
38156
38153
  }
38154
+ function migrateV2ToV3(v2) {
38155
+ return {
38156
+ version: STATE_VERSION,
38157
+ theaters: v2.theaters ?? [],
38158
+ operations: v2.operations ?? [],
38159
+ groups: v2.groups ?? [],
38160
+ deletionTombstones: []
38161
+ };
38162
+ }
38157
38163
  function migrateV1Operation(value) {
38158
38164
  if (!isRecord5(value)) return null;
38159
38165
  const sessionId = readNonEmptyString(value.sessionId);
@@ -38317,6 +38323,37 @@ function sanitizeOperationGroup(value) {
38317
38323
  if (!VALID_GROUP_COLOR_KEYS.has(color)) return null;
38318
38324
  return { id, theaterId, name: rawName, color, order, createdAt };
38319
38325
  }
38326
+ function readDeletionTombstones(value) {
38327
+ if (!Array.isArray(value)) return [];
38328
+ const tombstones = [];
38329
+ for (const item of value) {
38330
+ const tombstone = sanitizeDeletionTombstone(item);
38331
+ if (tombstone) tombstones.push(tombstone);
38332
+ }
38333
+ return tombstones;
38334
+ }
38335
+ function sanitizeDeletionTombstone(value) {
38336
+ if (!isRecord5(value)) return null;
38337
+ const deletionId = readNonEmptyString(value.deletionId);
38338
+ const targetId = readNonEmptyString(value.targetId);
38339
+ const deletedAt = readFiniteNumber(value.deletedAt);
38340
+ const expiresAt = readFiniteNumber(value.expiresAt);
38341
+ if (!deletionId || !targetId || deletedAt === null || expiresAt === null) return null;
38342
+ if (value.kind === "operation") {
38343
+ const operation = sanitizeOperationNode(value.operation);
38344
+ if (!operation || operation.id !== targetId) return null;
38345
+ return { deletionId, targetId, deletedAt, expiresAt, kind: "operation", operation };
38346
+ }
38347
+ if (value.kind === "theater") {
38348
+ const theater = sanitizeTheaterRegistration(value.theater);
38349
+ if (!theater || theater.id !== targetId || !Array.isArray(value.operations) || !Array.isArray(value.groups)) return null;
38350
+ const operations = value.operations.map(sanitizeOperationNode).filter((operation) => operation !== null);
38351
+ const groups = value.groups.map(sanitizeOperationGroup).filter((group) => group !== null);
38352
+ if (operations.length !== value.operations.length || groups.length !== value.groups.length || operations.some((operation) => operation.theaterId !== targetId) || groups.some((group) => group.theaterId !== targetId)) return null;
38353
+ return { deletionId, targetId, deletedAt, expiresAt, kind: "theater", theater, operations, groups };
38354
+ }
38355
+ return null;
38356
+ }
38320
38357
 
38321
38358
  // core/host/operations/sanitize.ts
38322
38359
  var FIXED_SENSITIVE_OPERATION_FIELDS = /* @__PURE__ */ new Set([
@@ -38332,11 +38369,20 @@ var FIXED_SENSITIVE_OPERATION_FIELDS = /* @__PURE__ */ new Set([
38332
38369
  ]);
38333
38370
  function createSanitizedOpDto(node, options = {}) {
38334
38371
  const sensitiveFields = /* @__PURE__ */ new Set([...FIXED_SENSITIVE_OPERATION_FIELDS, ...options.sensitiveFields ?? []]);
38372
+ const payload = sanitizeRecord(node.payload, sensitiveFields);
38373
+ delete payload.resumeAvailable;
38374
+ if (isResumableProviderSession(node.payload?.providerSession)) payload.resumeAvailable = true;
38335
38375
  return {
38336
38376
  ...node,
38337
- payload: sanitizeRecord(node.payload, sensitiveFields)
38377
+ payload
38338
38378
  };
38339
38379
  }
38380
+ function isResumableProviderSession(value) {
38381
+ if (!isRecord6(value)) return false;
38382
+ const provider = value.provider;
38383
+ const sessionId = value.sessionId;
38384
+ return typeof provider === "string" && provider.length > 0 && typeof sessionId === "string" && sessionId.length > 0;
38385
+ }
38340
38386
  function sanitizeRecord(value, sensitiveFields) {
38341
38387
  const output = {};
38342
38388
  for (const [key, item] of Object.entries(value)) {
@@ -38418,6 +38464,10 @@ async function handleCollection(req, res, deps) {
38418
38464
  deps.writeJson(res, 400, { error: "invalid_operation_accent" });
38419
38465
  return;
38420
38466
  }
38467
+ if (typeof body2.id === "string" && deps.isPendingDeletion?.(body2.id)) {
38468
+ deps.writeJson(res, 409, { error: "pending_deletion" });
38469
+ return;
38470
+ }
38421
38471
  try {
38422
38472
  const node = deps.store.create({
38423
38473
  id: typeof body2.id === "string" ? body2.id : void 0,
@@ -38450,8 +38500,8 @@ async function handleItem(req, res, id, deps) {
38450
38500
  return;
38451
38501
  }
38452
38502
  if (req.method === "DELETE") {
38453
- const deleted = deps.deleteOperation(id);
38454
- deps.writeJson(res, deleted ? 200 : 404, deleted ? { ok: true } : { error: "operation_not_found" });
38503
+ const deletion = deps.deleteOperation(id);
38504
+ deps.writeJson(res, 200, { ok: true, deletion });
38455
38505
  return;
38456
38506
  }
38457
38507
  const body2 = await deps.readJsonBody(req);
@@ -39042,6 +39092,10 @@ function createPlansRouter(deps) {
39042
39092
  await handlePlansRead(req, res, deps);
39043
39093
  return true;
39044
39094
  }
39095
+ if (pathname === "/api/v1/plans/search") {
39096
+ await handlePlansSearch(req, res, deps);
39097
+ return true;
39098
+ }
39045
39099
  if (pathname === "/api/v1/plans/events") {
39046
39100
  await handlePlansEvents(req, res, deps);
39047
39101
  return true;
@@ -39049,6 +39103,38 @@ function createPlansRouter(deps) {
39049
39103
  return false;
39050
39104
  };
39051
39105
  }
39106
+ async function handlePlansSearch(req, res, deps) {
39107
+ if (req.method !== "POST") {
39108
+ deps.writeJson(res, 405, { error: "Method not allowed" });
39109
+ return;
39110
+ }
39111
+ if (!deps.isAuthorized(req)) {
39112
+ deps.writeJson(res, 401, { error: "unauthorized" });
39113
+ return;
39114
+ }
39115
+ const body2 = await deps.readJsonBody(req);
39116
+ if (!isPlainObject2(body2) || typeof body2.theaterId !== "string" || typeof body2.query !== "string" || body2.query.trim() === "" || !Number.isInteger(body2.limit) || body2.limit < 1 || body2.limit > 8 || "relPath" in body2) {
39117
+ deps.writeJson(res, 400, { error: "invalid_request" });
39118
+ return;
39119
+ }
39120
+ const theaterPath = deps.resolveTheaterPath(body2.theaterId);
39121
+ if (!theaterPath) {
39122
+ deps.writeJson(res, 404, { error: "theater_not_found" });
39123
+ return;
39124
+ }
39125
+ try {
39126
+ const context = await resolveTheaterRoot(theaterPath);
39127
+ const tokens = body2.query.trim().toLocaleLowerCase().split(/\s+/).filter(Boolean);
39128
+ const plans = (await listPlansForWorkspace(deps.dataDir, context.realRoot)).filter((plan) => {
39129
+ const text = `${plan.name}
39130
+ ${plan.title}`.toLocaleLowerCase();
39131
+ return tokens.every((token) => text.includes(token));
39132
+ }).slice(0, body2.limit).map((plan) => ({ name: plan.name, title: plan.title }));
39133
+ deps.writeJson(res, 200, { plans });
39134
+ } catch (error51) {
39135
+ writePlanStoreError(res, deps, error51);
39136
+ }
39137
+ }
39052
39138
  async function handlePlansEvents(req, res, deps) {
39053
39139
  if (req.method !== "GET") {
39054
39140
  deps.writeJson(res, 405, { error: "Method not allowed" });
@@ -40864,7 +40950,6 @@ var SERVER_TIMEOUT_MS = 30 * 60 * 1e3;
40864
40950
  var MAX_BODY_BYTES = 1024 * 1024;
40865
40951
  var UPDATE_APPLY_FORBIDDEN_BODY_KEYS = /* @__PURE__ */ new Set(["channel", "package", "packageName", "packageVersion", "packages", "targetVersion", "version"]);
40866
40952
  var OPERATION_RENAMED_EVENT_CHANNEL = "operation:renamed";
40867
- var OPERATION_DELETED_EVENT_CHANNEL = "operation:deleted";
40868
40953
  var PAIRING_IDENTITY_PATH = "/api/v1/pairing-identity";
40869
40954
  var PAIRING_IDENTITY = { product: "fleet-console", schemaVersion: 1, pairingProtocolVersion: 1 };
40870
40955
  var SERVER_API_CATALOG = [
@@ -40924,6 +41009,13 @@ var SERVER_API_CATALOG = [
40924
41009
  category: "Observer",
40925
41010
  gate: "origin-write"
40926
41011
  },
41012
+ {
41013
+ method: "POST",
41014
+ path: "/api/v1/deletions/:deletionId/restore",
41015
+ summary: "\uC720\uC608 \uC911\uC778 Operation \uB610\uB294 Theater \uC0AD\uC81C\uB97C \uBCF5\uAD6C\uD569\uB2C8\uB2E4.",
41016
+ category: "Observer",
41017
+ gate: "origin-write"
41018
+ },
40927
41019
  {
40928
41020
  method: "POST",
40929
41021
  path: "/api/v1/theaters/:theaterId/codex-workspace",
@@ -41051,16 +41143,29 @@ function createConsoleServer(deps = {}) {
41051
41143
  function publishPluginEvent(channel2, payload) {
41052
41144
  for (const listener of pluginEventListeners.get(channel2) ?? []) listener(payload);
41053
41145
  }
41054
- function deleteOperation(operationId) {
41055
- const operation = operations.get(operationId);
41056
- if (!operation || !operations.delete(operationId)) return false;
41057
- persistDurableState();
41058
- publishPluginEvent(OPERATION_DELETED_EVENT_CHANNEL, {
41059
- operationId: operation.id,
41060
- pluginId: operation.pluginId,
41061
- type: operation.type
41062
- });
41063
- return true;
41146
+ const deletionCoordinator = createDeferredDeletionCoordinator({
41147
+ operations,
41148
+ theaters,
41149
+ save: saveDurableState,
41150
+ publish: publishPluginEvent,
41151
+ unregisterTheaterWorkspaces: (theaterId) => codex.unregisterTheaterWorkspaces(theaterId),
41152
+ validateTheaterRestore: async (theater) => {
41153
+ try {
41154
+ const restoredRealpath = await fs10__default.promises.realpath(theater.path);
41155
+ const stat4 = await fs10__default.promises.stat(restoredRealpath);
41156
+ if (!stat4.isDirectory() || restoredRealpath !== theater.realpath || workspaceHash(restoredRealpath) !== theater.id) {
41157
+ throw new Error("restore_conflict");
41158
+ }
41159
+ } catch {
41160
+ throw new DeferredDeletionError(409, "restore_parent_missing");
41161
+ }
41162
+ },
41163
+ registerTheaterWorkspace: async (theater) => {
41164
+ await codex.registerWorkspace(theater.realpath, theater.lastOpenedAt, theater.id);
41165
+ }
41166
+ });
41167
+ function deleteOperationForPlugin(operationId) {
41168
+ return deletionCoordinator.deleteOperation(operationId) !== null;
41064
41169
  }
41065
41170
  let desktopFullscreen = false;
41066
41171
  let unsubscribeUpdateCheckChanges = updateCheck.onChange?.(() => {
@@ -41071,6 +41176,7 @@ function createConsoleServer(deps = {}) {
41071
41176
  list: () => operations.list(),
41072
41177
  get: (id) => operations.get(id),
41073
41178
  create: (input) => {
41179
+ if (input.id && deletionCoordinator.hasPendingOperation(input.id)) throw new Error("pending_deletion");
41074
41180
  const operation = operations.create(input);
41075
41181
  persistDurableState();
41076
41182
  return operation;
@@ -41086,7 +41192,7 @@ function createConsoleServer(deps = {}) {
41086
41192
  }
41087
41193
  return operation;
41088
41194
  },
41089
- delete: deleteOperation,
41195
+ delete: deleteOperationForPlugin,
41090
41196
  registerOperationType: (type) => {
41091
41197
  pluginOperationTypes.add(type);
41092
41198
  return () => {
@@ -41300,7 +41406,8 @@ function createConsoleServer(deps = {}) {
41300
41406
  readJsonBody,
41301
41407
  writeJson,
41302
41408
  persist: persistDurableState,
41303
- deleteOperation,
41409
+ deleteOperation: (operationId) => deletionCoordinator.deleteOperation(operationId),
41410
+ isPendingDeletion: (operationId) => deletionCoordinator.hasPendingOperation(operationId),
41304
41411
  getPluginSensitiveFields: (pluginId) => [
41305
41412
  ...pluginHost.sensitiveFieldsByPluginId.get(pluginId) ?? [],
41306
41413
  ...pluginPayloadSanitizers.get(pluginId) ?? []
@@ -41390,6 +41497,11 @@ function createConsoleServer(deps = {}) {
41390
41497
  runAsyncBooleanHandler(plansRouter({ req, res, pathname }), res, () => false);
41391
41498
  return;
41392
41499
  }
41500
+ const restoreMatch = pathname.match(/^\/api\/v1\/deletions\/([^/]+)\/restore$/);
41501
+ if (restoreMatch) {
41502
+ runAsyncHandler(handleDeferredDeletionRestore(req, res, decodeURIComponent(restoreMatch[1] ?? "")), res);
41503
+ return;
41504
+ }
41393
41505
  const theaterItemMatch = pathname.match(/^\/api\/v1\/theaters\/([^/]+)$/);
41394
41506
  if (theaterItemMatch) {
41395
41507
  runAsyncHandler(handleObserverTheaterItem(req, res, decodeURIComponent(theaterItemMatch[1] ?? "")), res);
@@ -41537,6 +41649,11 @@ function createConsoleServer(deps = {}) {
41537
41649
  writeJson(res, 400, { error: "invalid_folder_grant" });
41538
41650
  return;
41539
41651
  }
41652
+ const canonicalCwd = canonicalizeTheaterPathSync(cwd);
41653
+ if (deletionCoordinator.hasPendingTheater(workspaceHash(canonicalCwd))) {
41654
+ writeJson(res, 409, { error: "pending_deletion" });
41655
+ return;
41656
+ }
41540
41657
  const theater = await theaters.register(cwd);
41541
41658
  await codex.registerWorkspace(theater.realpath, void 0, theater.id);
41542
41659
  persistDurableState();
@@ -41566,12 +41683,27 @@ function createConsoleServer(deps = {}) {
41566
41683
  writeJson(res, 200, toTheaterInfo(theater, true));
41567
41684
  return;
41568
41685
  }
41569
- theaters.remove(theaterId);
41570
- codex.unregisterTheaterWorkspaces(theaterId);
41571
- for (const operation of operations.listByTheater(theaterId)) deleteOperation(operation.id);
41572
- operations.deleteGroupsByTheater(theaterId);
41573
- persistDurableState();
41574
- writeJson(res, 200, { ok: true });
41686
+ const deletion = deletionCoordinator.deleteTheater(theaterId);
41687
+ writeJson(res, 200, { ok: true, deletion });
41688
+ }
41689
+ async function handleDeferredDeletionRestore(req, res, deletionId) {
41690
+ if (req.method !== "POST") {
41691
+ writeJson(res, 405, { error: "Method not allowed" });
41692
+ return;
41693
+ }
41694
+ if (!isTerminalAuthorized(req)) {
41695
+ writeJson(res, 401, { error: "unauthorized" });
41696
+ return;
41697
+ }
41698
+ try {
41699
+ writeJson(res, 200, await deletionCoordinator.restore(deletionId));
41700
+ } catch (error51) {
41701
+ if (error51 instanceof DeferredDeletionError) {
41702
+ writeJson(res, error51.status, { error: error51.message });
41703
+ return;
41704
+ }
41705
+ throw error51;
41706
+ }
41575
41707
  }
41576
41708
  function handleStatus(req, res) {
41577
41709
  const theaterId = readUrl(req).searchParams.get("theaterId");
@@ -41788,6 +41920,12 @@ function createConsoleServer(deps = {}) {
41788
41920
  operations.replace([]);
41789
41921
  operations.replaceGroups([]);
41790
41922
  }
41923
+ deletionCoordinator.load(state.deletionTombstones ?? []);
41924
+ try {
41925
+ deletionCoordinator.sweepExpired();
41926
+ } catch (error51) {
41927
+ console.warn(`[fleet-console] Expired deletion sweep deferred: ${error51 instanceof Error ? error51.message : String(error51)}`);
41928
+ }
41791
41929
  await restoreCodexWorkspaces();
41792
41930
  }
41793
41931
  async function restoreCodexWorkspaces() {
@@ -41829,16 +41967,20 @@ function createConsoleServer(deps = {}) {
41829
41967
  }
41830
41968
  function persistDurableState() {
41831
41969
  try {
41832
- durableStateStore.save({
41833
- version: 2,
41834
- theaters: theaters.list(),
41835
- operations: operations.list(),
41836
- groups: operations.listAllGroups()
41837
- });
41970
+ saveDurableState(deletionCoordinator.list());
41838
41971
  } catch (error51) {
41839
41972
  console.warn(`[fleet-console] Durable state save failed: ${error51 instanceof Error ? error51.message : String(error51)}`);
41840
41973
  }
41841
41974
  }
41975
+ function saveDurableState(deletionTombstones) {
41976
+ durableStateStore.save({
41977
+ version: STATE_VERSION,
41978
+ theaters: theaters.list(),
41979
+ operations: operations.list(),
41980
+ groups: operations.listAllGroups(),
41981
+ deletionTombstones
41982
+ });
41983
+ }
41842
41984
  async function stopServer() {
41843
41985
  const current = server;
41844
41986
  const currentLoopback = loopbackServer;
@@ -41848,6 +41990,7 @@ function createConsoleServer(deps = {}) {
41848
41990
  lockHandle = null;
41849
41991
  activeLockFile = null;
41850
41992
  activeEndpoint = null;
41993
+ deletionCoordinator.dispose();
41851
41994
  try {
41852
41995
  await Promise.all([
41853
41996
  closeHttpServer(current),