@dotobokuri/fleet-console 1.33.0 → 1.34.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 +553 -433
  2. package/dist/client/assets/{_baseUniq-DGGrBWAb.js → _baseUniq-kpxXKqaz.js} +1 -1
  3. package/dist/client/assets/{arc-BNCMUJG_.js → arc-Cf0soLes.js} +1 -1
  4. package/dist/client/assets/{architectureDiagram-Q4EWVU46-C7qQq1FU.js → architectureDiagram-Q4EWVU46-DiRqbCQ7.js} +1 -1
  5. package/dist/client/assets/{blockDiagram-DXYQGD6D-Cd8j3SNd.js → blockDiagram-DXYQGD6D-DC25h3LU.js} +1 -1
  6. package/dist/client/assets/{c4Diagram-AHTNJAMY-fFAORcJP.js → c4Diagram-AHTNJAMY-BJLnKoD4.js} +1 -1
  7. package/dist/client/assets/channel-Deg6stWj.js +1 -0
  8. package/dist/client/assets/{chunk-4BX2VUAB-peomhKWw.js → chunk-4BX2VUAB-DWUk4gCy.js} +1 -1
  9. package/dist/client/assets/{chunk-4TB4RGXK-TEb8s6Dl.js → chunk-4TB4RGXK-DW4Ee9nw.js} +1 -1
  10. package/dist/client/assets/{chunk-55IACEB6-11aKxGwZ.js → chunk-55IACEB6-8z_Na955.js} +1 -1
  11. package/dist/client/assets/{chunk-EDXVE4YY-CPeOY-w0.js → chunk-EDXVE4YY-C3Z4nalr.js} +1 -1
  12. package/dist/client/assets/{chunk-FMBD7UC4-DpbKBDUG.js → chunk-FMBD7UC4-UIVDEAE7.js} +1 -1
  13. package/dist/client/assets/{chunk-OYMX7WX6-DQiIenR8.js → chunk-OYMX7WX6-DpFWa4G9.js} +1 -1
  14. package/dist/client/assets/{chunk-QZHKN3VN-ByEBhAst.js → chunk-QZHKN3VN-BJndYOsb.js} +1 -1
  15. package/dist/client/assets/{chunk-YZCP3GAM-Ca8uNdzu.js → chunk-YZCP3GAM-DJcSuWBd.js} +1 -1
  16. package/dist/client/assets/classDiagram-6PBFFD2Q-CpidLq53.js +1 -0
  17. package/dist/client/assets/classDiagram-v2-HSJHXN6E-CpidLq53.js +1 -0
  18. package/dist/client/assets/clone-CPyCexwP.js +1 -0
  19. package/dist/client/assets/{cose-bilkent-S5V4N54A-B9r4jGi9.js → cose-bilkent-S5V4N54A-C3m_quy1.js} +1 -1
  20. package/dist/client/assets/{dagre-KV5264BT-Csnn3Aws.js → dagre-KV5264BT-DzDFWezD.js} +1 -1
  21. package/dist/client/assets/{diagram-5BDNPKRD-xgvBmNve.js → diagram-5BDNPKRD-D3OFrOF_.js} +1 -1
  22. package/dist/client/assets/{diagram-G4DWMVQ6-eDo2cgdI.js → diagram-G4DWMVQ6-CgCqSaI7.js} +1 -1
  23. package/dist/client/assets/{diagram-MMDJMWI5-C3GDlrcv.js → diagram-MMDJMWI5-CnJzOV_w.js} +1 -1
  24. package/dist/client/assets/{diagram-TYMM5635-BDyVzMHP.js → diagram-TYMM5635-BrUz8pH4.js} +1 -1
  25. package/dist/client/assets/{erDiagram-SMLLAGMA-YegVvYsw.js → erDiagram-SMLLAGMA-C1IvCMgN.js} +1 -1
  26. package/dist/client/assets/{flowDiagram-DWJPFMVM-zcVenDmh.js → flowDiagram-DWJPFMVM-DCrfhs19.js} +1 -1
  27. package/dist/client/assets/{ganttDiagram-T4ZO3ILL-CkMabQtg.js → ganttDiagram-T4ZO3ILL-ChQcD1Li.js} +1 -1
  28. package/dist/client/assets/{gitGraphDiagram-UUTBAWPF-CuD1D5LQ.js → gitGraphDiagram-UUTBAWPF-C5aEYdTT.js} +1 -1
  29. package/dist/client/assets/{graph-DvVj819O.js → graph-CpGaCv1x.js} +1 -1
  30. package/dist/client/assets/index-DKrNZlfG.css +1 -0
  31. package/dist/client/assets/index-aW5PuGmI.js +452 -0
  32. package/dist/client/assets/{infoDiagram-42DDH7IO-DPoWXM0E.js → infoDiagram-42DDH7IO-BMhiz3U-.js} +1 -1
  33. package/dist/client/assets/{ishikawaDiagram-UXIWVN3A-BBoXoRxo.js → ishikawaDiagram-UXIWVN3A-5_f4rhFk.js} +1 -1
  34. package/dist/client/assets/{journeyDiagram-VCZTEJTY-B-IoiUw0.js → journeyDiagram-VCZTEJTY-BHP2UyTZ.js} +1 -1
  35. package/dist/client/assets/{kanban-definition-6JOO6SKY-BcjjEa8G.js → kanban-definition-6JOO6SKY-edTTflKa.js} +1 -1
  36. package/dist/client/assets/{layout-BjaFRuOv.js → layout-B7z9q0n7.js} +1 -1
  37. package/dist/client/assets/{linear-DLVG-hZ_.js → linear-BDNnEnhj.js} +1 -1
  38. package/dist/client/assets/{mermaid.core-6fYEjApv.js → mermaid.core-DDcMYg9W.js} +4 -4
  39. package/dist/client/assets/{min-BfgBrJLR.js → min-D6WC647e.js} +1 -1
  40. package/dist/client/assets/{mindmap-definition-QFDTVHPH-BjelYLWH.js → mindmap-definition-QFDTVHPH-lvrJFtla.js} +1 -1
  41. package/dist/client/assets/{pieDiagram-DEJITSTG-BznY8oUt.js → pieDiagram-DEJITSTG-D1h_ZTUL.js} +1 -1
  42. package/dist/client/assets/{quadrantDiagram-34T5L4WZ-jEnKUROj.js → quadrantDiagram-34T5L4WZ-DSAYqOT0.js} +1 -1
  43. package/dist/client/assets/{requirementDiagram-MS252O5E-BuQqQSjX.js → requirementDiagram-MS252O5E-BtNZUJPU.js} +1 -1
  44. package/dist/client/assets/{sankeyDiagram-XADWPNL6-B9g8trZW.js → sankeyDiagram-XADWPNL6-Ci2wj876.js} +1 -1
  45. package/dist/client/assets/{sequenceDiagram-FGHM5R23-V6Zwufo5.js → sequenceDiagram-FGHM5R23-ES3fQi8m.js} +1 -1
  46. package/dist/client/assets/{stateDiagram-FHFEXIEX-8E2cIHyh.js → stateDiagram-FHFEXIEX-BAJV8Z8I.js} +1 -1
  47. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-CiX1nm_r.js +1 -0
  48. package/dist/client/assets/{timeline-definition-GMOUNBTQ-DpiM_Ig9.js → timeline-definition-GMOUNBTQ-Bl7xwJpF.js} +1 -1
  49. package/dist/client/assets/{vennDiagram-DHZGUBPP-D975VPJg.js → vennDiagram-DHZGUBPP-Csv3w_qu.js} +1 -1
  50. package/dist/client/assets/{wardley-RL74JXVD-BiM6uI8Z.js → wardley-RL74JXVD-CaXvusTS.js} +1 -1
  51. package/dist/client/assets/{wardleyDiagram-NUSXRM2D-IoXxaljw.js → wardleyDiagram-NUSXRM2D-DfKsNCHq.js} +1 -1
  52. package/dist/client/assets/{xychartDiagram-5P7HB3ND-BqNE-iqW.js → xychartDiagram-5P7HB3ND-BmDHSI31.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 +106 -4
  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;
@@ -31774,35 +31780,23 @@ var AcpConnection = class extends BaseConnection {
31774
31780
  }
31775
31781
  case "tool_call": {
31776
31782
  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
31783
  this.emit(
31784
31784
  "toolCall",
31785
- cleanTcTitle,
31785
+ toolCallData.title ?? "",
31786
31786
  toolCallData.status ?? "",
31787
31787
  sessionId,
31788
- { ...toolCallData, ...titleTcOverride }
31788
+ toolCallData
31789
31789
  );
31790
31790
  break;
31791
31791
  }
31792
31792
  case "tool_call_update": {
31793
31793
  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
31794
  this.emit(
31801
31795
  "toolCallUpdate",
31802
- cleanTitle,
31796
+ toolCallUpdateData.title ?? "",
31803
31797
  toolCallUpdateData.status ?? "",
31804
31798
  sessionId,
31805
- { ...toolCallUpdateData, ...titleOverride }
31799
+ toolCallUpdateData
31806
31800
  );
31807
31801
  break;
31808
31802
  }
@@ -31967,7 +31961,7 @@ function inferBinName(packageName) {
31967
31961
  // ../../packages/core-unified-agent/models.json
31968
31962
  var models_default = {
31969
31963
  version: 1,
31970
- updatedAt: "2026-07-21T00:00:00Z",
31964
+ updatedAt: "2026-07-25T00:00:00Z",
31971
31965
  providers: {
31972
31966
  claude: {
31973
31967
  name: "Claude Code",
@@ -32003,11 +31997,13 @@ var models_default = {
32003
31997
  defaultModel: "gpt-5.6-sol",
32004
31998
  models: [
32005
31999
  { modelId: "gpt-5.6-sol", name: "GPT-5.6-Sol", effort: { supported: true, levels: ["low", "medium", "high", "xhigh", "max", "ultra"], default: "low" } },
32000
+ { 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
32001
  { modelId: "gpt-5.6-terra", name: "GPT-5.6-Terra", effort: { supported: true, levels: ["low", "medium", "high", "xhigh", "max", "ultra"], default: "medium" } },
32002
+ { 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
32003
  { modelId: "gpt-5.6-luna", name: "GPT-5.6-Luna", effort: { supported: true, levels: ["low", "medium", "high", "xhigh", "max"], default: "medium" } },
32004
+ { 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
32005
  { 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" } }
32006
+ { 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
32007
  ]
32012
32008
  },
32013
32009
  "opencode-go": {
@@ -32093,6 +32089,10 @@ var ModelEntrySchema = external_exports.object({
32093
32089
  description: external_exports.string().optional(),
32094
32090
  /** spawn 시 실제 CLI 모델 ID를 조립해야 하는 경우의 템플릿 */
32095
32091
  spawnModelTemplate: external_exports.string().optional(),
32092
+ /** 카탈로그 ID와 실제 provider 모델 ID가 다른 경우의 원본 모델 ID */
32093
+ providerModelId: external_exports.string().optional(),
32094
+ /** provider가 모델과 별도로 받는 서비스 티어 */
32095
+ serviceTier: external_exports.string().optional(),
32096
32096
  /** 모델의 컨텍스트 윈도우 크기 (토큰) */
32097
32097
  contextWindow: external_exports.number().int().positive().optional(),
32098
32098
  /** 모델별 effort 설정 */
@@ -32115,6 +32115,14 @@ var ModelEntrySchema = external_exports.object({
32115
32115
  path: ["spawnModelTemplate"]
32116
32116
  });
32117
32117
  }
32118
+ if (ctx.value.serviceTier && !ctx.value.providerModelId) {
32119
+ ctx.issues.push({
32120
+ code: "custom",
32121
+ input: ctx.value.serviceTier,
32122
+ message: "serviceTier\uB97C \uC9C0\uC815\uD55C \uBAA8\uB378\uC740 providerModelId\uB3C4 \uC9C0\uC815\uD574\uC57C \uD569\uB2C8\uB2E4",
32123
+ path: ["serviceTier"]
32124
+ });
32125
+ }
32118
32126
  });
32119
32127
  var ModelsMappingSchema = external_exports.record(external_exports.string(), external_exports.string());
32120
32128
  var ProviderSchema = external_exports.object({
@@ -32264,8 +32272,6 @@ var CLI_BACKENDS = {
32264
32272
  cliCommand: "codex",
32265
32273
  protocol: "codex-app-server",
32266
32274
  authRequired: true,
32267
- npxPackage: "@agentclientprotocol/codex-acp@1.1.2",
32268
- acpArgs: [],
32269
32275
  appServerArgs: ["app-server", "--listen", "stdio://"],
32270
32276
  modes: [
32271
32277
  { id: "default", label: "Plan" },
@@ -32275,7 +32281,7 @@ var CLI_BACKENDS = {
32275
32281
  supportsSessionClose: true,
32276
32282
  supportsSessionLoad: true,
32277
32283
  requiresModelAtSpawn: false,
32278
- usesNpxBridge: true,
32284
+ usesNpxBridge: false,
32279
32285
  defaultMaxTokens: 1e5
32280
32286
  },
32281
32287
  "opencode-go": {
@@ -32366,22 +32372,6 @@ function getYoloModeId(cli) {
32366
32372
  function getAllBackendConfigs() {
32367
32373
  return Object.values(CLI_BACKENDS);
32368
32374
  }
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
32375
  function buildConfigOverrideArgs(overrides) {
32386
32376
  return overrides.flatMap((override) => ["-c", override]);
32387
32377
  }
@@ -32408,45 +32398,6 @@ function mcpServerConfigsToAcp(servers) {
32408
32398
  headers: server.headers ?? []
32409
32399
  }));
32410
32400
  }
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
32401
 
32451
32402
  // ../../packages/core-unified-agent/src/detector/CliDetector.ts
32452
32403
  var CLI_DETECT_LIST = Object.entries(CLI_BACKENDS).map(([id, backend]) => ({
@@ -33002,6 +32953,7 @@ var CODEX_METHODS = {
33002
32953
  THREAD_RESUME: "thread/resume",
33003
32954
  THREAD_READ: "thread/read",
33004
32955
  THREAD_ARCHIVE: "thread/archive",
32956
+ THREAD_UNARCHIVE: "thread/unarchive",
33005
32957
  TURN_START: "turn/start",
33006
32958
  TURN_INTERRUPT: "turn/interrupt",
33007
32959
  TURN_STEER: "turn/steer"
@@ -33030,6 +32982,7 @@ var CODEX_MCP_READY_STATUS = "ready";
33030
32982
  var CODEX_MCP_FAILED_STATUSES = /* @__PURE__ */ new Set(["failed", "error"]);
33031
32983
  var DEFAULT_MCP_STARTUP_TIMEOUT = 6e4;
33032
32984
  var STDERR_TAIL_LIMIT = 20;
32985
+ var SESSION_TEARDOWN_TIMEOUT_MS = 5e3;
33033
32986
  var CodexAppServerConnection = class extends BaseConnection {
33034
32987
  clientInfo;
33035
32988
  autoApprove;
@@ -33042,6 +32995,7 @@ var CodexAppServerConnection = class extends BaseConnection {
33042
32995
  pendingMcpReadyWaiters = /* @__PURE__ */ new Set();
33043
32996
  stdoutBuffer = "";
33044
32997
  pendingModel = null;
32998
+ pendingServiceTier = void 0;
33045
32999
  pendingEffort = null;
33046
33000
  codexHome = null;
33047
33001
  isDisconnecting = false;
@@ -33066,6 +33020,9 @@ var CodexAppServerConnection = class extends BaseConnection {
33066
33020
  setPendingModel(model) {
33067
33021
  this.pendingModel = model;
33068
33022
  }
33023
+ setPendingServiceTier(serviceTier) {
33024
+ this.pendingServiceTier = serviceTier;
33025
+ }
33069
33026
  setPendingEffort(effort) {
33070
33027
  this.pendingEffort = effort;
33071
33028
  }
@@ -33108,6 +33065,7 @@ var CodexAppServerConnection = class extends BaseConnection {
33108
33065
  cwd: options?.cwd ?? this.cwd,
33109
33066
  developerInstructions: options?.developerInstructions ?? null,
33110
33067
  model: options?.model ?? null,
33068
+ serviceTier: options?.serviceTier ?? null,
33111
33069
  approvalPolicy: options?.approvalPolicy ?? null,
33112
33070
  sandbox: options?.sandbox ?? null,
33113
33071
  config: options?.config ?? null
@@ -33118,10 +33076,11 @@ var CodexAppServerConnection = class extends BaseConnection {
33118
33076
  this.setState("ready");
33119
33077
  return response;
33120
33078
  } catch (error51) {
33079
+ const diagnosticError = this.withStderrDiagnostics(error51, "Codex initialize");
33121
33080
  this.setState("error");
33122
33081
  this.isDisconnecting = true;
33123
33082
  await this.disconnect();
33124
- throw error51;
33083
+ throw diagnosticError;
33125
33084
  }
33126
33085
  }
33127
33086
  async loadSession(threadId, options) {
@@ -33129,11 +33088,37 @@ var CodexAppServerConnection = class extends BaseConnection {
33129
33088
  try {
33130
33089
  response = await this.sendThreadResumeRequest(threadId, options);
33131
33090
  } catch (error51) {
33132
- const rolloutPath = this.findRolloutPathForThreadId(threadId);
33133
- if (!rolloutPath || !isMissingRolloutError(error51, threadId)) {
33134
- throw error51;
33091
+ if (isArchivedThreadError(error51, threadId)) {
33092
+ try {
33093
+ await this.sendRequest(
33094
+ CODEX_METHODS.THREAD_UNARCHIVE,
33095
+ { threadId }
33096
+ );
33097
+ } catch (unarchiveError) {
33098
+ const message = unarchiveError instanceof Error ? unarchiveError.message : String(unarchiveError);
33099
+ throw new Error(
33100
+ `thread/unarchive\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4(\uC6D0\uC778: archived \uC0C1\uD0DC\uB85C resume \uAC70\uBD80\uB428): ${message}`,
33101
+ { cause: unarchiveError }
33102
+ );
33103
+ }
33104
+ try {
33105
+ response = await this.sendThreadResumeRequest(threadId, options);
33106
+ } catch (resumeError) {
33107
+ await this.sendRequest(
33108
+ CODEX_METHODS.THREAD_ARCHIVE,
33109
+ { threadId },
33110
+ SESSION_TEARDOWN_TIMEOUT_MS
33111
+ ).catch(() => {
33112
+ });
33113
+ throw resumeError;
33114
+ }
33115
+ } else {
33116
+ const rolloutPath = this.findRolloutPathForThreadId(threadId);
33117
+ if (!rolloutPath || !isMissingRolloutError(error51, threadId)) {
33118
+ throw error51;
33119
+ }
33120
+ response = await this.sendThreadResumeRequest(threadId, options, rolloutPath);
33135
33121
  }
33136
- response = await this.sendThreadResumeRequest(threadId, options, rolloutPath);
33137
33122
  }
33138
33123
  this.threadId = response.thread.id;
33139
33124
  this.turnId = null;
@@ -33195,16 +33180,19 @@ var CodexAppServerConnection = class extends BaseConnection {
33195
33180
  this.on("exit", onExit);
33196
33181
  });
33197
33182
  try {
33183
+ const serviceTier = options?.serviceTier !== void 0 ? options.serviceTier : this.pendingServiceTier;
33198
33184
  const response = await this.sendRequest(
33199
33185
  CODEX_METHODS.TURN_START,
33200
33186
  {
33201
33187
  threadId: sessionId,
33202
33188
  input,
33203
33189
  model: options?.model ?? this.pendingModel ?? null,
33190
+ ...serviceTier !== void 0 ? { serviceTier } : {},
33204
33191
  effort: options?.effort ?? this.pendingEffort ?? null
33205
33192
  }
33206
33193
  );
33207
33194
  this.pendingModel = null;
33195
+ this.pendingServiceTier = void 0;
33208
33196
  this.pendingEffort = null;
33209
33197
  this.turnId = response.turn.id;
33210
33198
  await turnCompleted;
@@ -33231,14 +33219,16 @@ var CodexAppServerConnection = class extends BaseConnection {
33231
33219
  if (threadId && activeTurnId) {
33232
33220
  await this.sendRequest(
33233
33221
  CODEX_METHODS.TURN_INTERRUPT,
33234
- { threadId, turnId: activeTurnId }
33222
+ { threadId, turnId: activeTurnId },
33223
+ SESSION_TEARDOWN_TIMEOUT_MS
33235
33224
  ).catch(() => {
33236
33225
  });
33237
33226
  }
33238
33227
  if (threadId) {
33239
33228
  await this.sendRequest(
33240
33229
  CODEX_METHODS.THREAD_ARCHIVE,
33241
- { threadId }
33230
+ { threadId },
33231
+ SESSION_TEARDOWN_TIMEOUT_MS
33242
33232
  ).catch(() => {
33243
33233
  });
33244
33234
  }
@@ -33256,6 +33246,7 @@ var CodexAppServerConnection = class extends BaseConnection {
33256
33246
  cwd: options?.cwd ?? this.cwd,
33257
33247
  developerInstructions: options?.developerInstructions ?? null,
33258
33248
  model: options?.model ?? null,
33249
+ serviceTier: options?.serviceTier ?? null,
33259
33250
  approvalPolicy: options?.approvalPolicy ?? null,
33260
33251
  sandbox: options?.sandbox ?? null,
33261
33252
  config: options?.config ?? null
@@ -33273,6 +33264,9 @@ var CodexAppServerConnection = class extends BaseConnection {
33273
33264
  this.rejectPendingRequests(new Error("Codex \uC5F0\uACB0\uC774 \uC885\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4."));
33274
33265
  this.rejectPendingMcpReadyWaiters(new Error("Codex \uC5F0\uACB0\uC774 \uC885\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4."));
33275
33266
  this.stdoutBuffer = "";
33267
+ this.pendingModel = null;
33268
+ this.pendingServiceTier = void 0;
33269
+ this.pendingEffort = null;
33276
33270
  this.codexHome = null;
33277
33271
  try {
33278
33272
  await super.disconnect();
@@ -33721,6 +33715,7 @@ var CodexAppServerConnection = class extends BaseConnection {
33721
33715
  path: rolloutPath ?? null,
33722
33716
  cwd: options?.cwd ?? this.cwd,
33723
33717
  model: options?.model ?? null,
33718
+ serviceTier: options?.serviceTier ?? null,
33724
33719
  approvalPolicy: options?.approvalPolicy ?? null,
33725
33720
  sandbox: options?.sandbox ?? null,
33726
33721
  developerInstructions: options?.developerInstructions ?? null,
@@ -33840,6 +33835,9 @@ var CodexAppServerConnection = class extends BaseConnection {
33840
33835
  function isMissingRolloutError(error51, threadId) {
33841
33836
  return error51 instanceof Error && error51.message.includes(`no rollout found for thread id ${threadId}`);
33842
33837
  }
33838
+ function isArchivedThreadError(error51, threadId) {
33839
+ return error51 instanceof Error && error51.message.includes(`session ${threadId} is archived`);
33840
+ }
33843
33841
  function findRolloutPath(root, threadId) {
33844
33842
  if (!fs10.existsSync(root)) {
33845
33843
  return null;
@@ -33861,7 +33859,7 @@ function findRolloutPath(root, threadId) {
33861
33859
  }
33862
33860
 
33863
33861
  // ../../packages/core-unified-agent/src/client/UnifiedCodexAgentClient.ts
33864
- var CODEX_TURN_LEVEL_CONFIG_KEYS = /* @__PURE__ */ new Set(["effort", "model"]);
33862
+ var CODEX_TURN_LEVEL_CONFIG_KEYS = /* @__PURE__ */ new Set(["effort"]);
33865
33863
  var CODEX_THREAD_POLICY_CONFIG_KEYS = /* @__PURE__ */ new Set(["approvalPolicy", "sandbox"]);
33866
33864
  var UnifiedCodexAgentClient = class extends EventEmitter {
33867
33865
  connection = null;
@@ -33870,6 +33868,8 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
33870
33868
  currentSystemPrompt = null;
33871
33869
  firstPromptPending = null;
33872
33870
  pendingOverrides = null;
33871
+ currentModelSelection = {};
33872
+ archiveSessionOnDisconnect = false;
33873
33873
  detector = new CliDetector();
33874
33874
  on(event, listener) {
33875
33875
  return super.on(event, listener);
@@ -33889,9 +33889,6 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
33889
33889
  throw new Error("UnifiedCodexAgentClient\uB294 codex CLI\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4.");
33890
33890
  }
33891
33891
  this.validateModelEffort(options.model, options.effort);
33892
- if (shouldUseCodexAcp(options)) {
33893
- return this.connectAcp(options);
33894
- }
33895
33892
  return this.connectAppServer(options);
33896
33893
  }
33897
33894
  async connectAppServer(options) {
@@ -33900,6 +33897,7 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
33900
33897
  const command2 = options.cliPath ?? backend.cliCommand;
33901
33898
  const baseArgs = backend.appServerArgs ?? ["app-server", "--listen", "stdio://"];
33902
33899
  const modeMapping = this.resolveMode(options.yoloMode === false ? "default" : "yolo");
33900
+ const modelSelection = this.resolveModelSelection(options.model);
33903
33901
  const args = [
33904
33902
  ...baseArgs,
33905
33903
  ...this.buildStartupConfigArgs(options.configOverrides, options.mcpServers, modeMapping)
@@ -33923,13 +33921,14 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
33923
33921
  if (options.sessionId) {
33924
33922
  await connection.connect({
33925
33923
  skipThreadStart: true,
33926
- model: options.model
33924
+ model: modelSelection.providerModelId,
33925
+ serviceTier: modelSelection.serviceTier
33927
33926
  });
33928
33927
  await connection.loadSession(
33929
33928
  options.sessionId,
33930
33929
  this.buildCodexThreadDefaultsForResume(
33931
33930
  options.cwd,
33932
- options.model,
33931
+ modelSelection,
33933
33932
  null,
33934
33933
  modeMapping
33935
33934
  )
@@ -33937,7 +33936,8 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
33937
33936
  } else {
33938
33937
  await connection.connect({
33939
33938
  developerInstructions: void 0,
33940
- model: options.model,
33939
+ model: modelSelection.providerModelId,
33940
+ serviceTier: modelSelection.serviceTier,
33941
33941
  approvalPolicy: modeMapping.approvalPolicy,
33942
33942
  sandbox: modeMapping.sandbox
33943
33943
  });
@@ -33951,6 +33951,8 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
33951
33951
  this.sessionCwd = options.cwd;
33952
33952
  this.currentSystemPrompt = options.systemPrompt ?? null;
33953
33953
  this.firstPromptPending = options.sessionId ? null : this.currentSystemPrompt;
33954
+ this.currentModelSelection = modelSelection;
33955
+ this.archiveSessionOnDisconnect = options.archiveSessionOnDisconnect === true;
33954
33956
  this.pendingOverrides = {
33955
33957
  turnConfig: options.effort ? { effort: options.effort } : {},
33956
33958
  threadConfig: {
@@ -33964,71 +33966,17 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
33964
33966
  session: { sessionId }
33965
33967
  };
33966
33968
  }
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
33969
  async disconnect() {
34019
33970
  if (!this.connection) {
34020
33971
  this.clearSessionState();
34021
33972
  return;
34022
33973
  }
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
- }
33974
+ if (this.archiveSessionOnDisconnect && this.sessionId) {
33975
+ await this.connection.endSession().catch(() => {
33976
+ });
34029
33977
  }
34030
- await conn.disconnect();
34031
- conn.removeAllListeners();
33978
+ await this.connection.disconnect();
33979
+ this.connection.removeAllListeners();
34032
33980
  this.connection = null;
34033
33981
  this.clearSessionState();
34034
33982
  }
@@ -34036,14 +33984,7 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
34036
33984
  if (!this.connection) {
34037
33985
  throw new Error("\uC5F0\uACB0\uB418\uC5B4 \uC788\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4");
34038
33986
  }
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
- }
33987
+ await this.connection.endSession();
34047
33988
  this.sessionId = null;
34048
33989
  this.sessionCwd = null;
34049
33990
  }
@@ -34058,7 +33999,7 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
34058
33999
  }
34059
34000
  return {
34060
34001
  cli: "codex",
34061
- protocol: this.connection instanceof AcpConnection ? "acp" : "codex-app-server",
34002
+ protocol: "codex-app-server",
34062
34003
  sessionId: this.sessionId,
34063
34004
  state: this.connection.connectionState
34064
34005
  };
@@ -34070,12 +34011,6 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
34070
34011
  if (!this.connection) {
34071
34012
  throw new Error("\uC5F0\uACB0\uB418\uC5B4 \uC788\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4");
34072
34013
  }
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
34014
  this.applyPendingOverrides();
34080
34015
  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
34016
  const systemPrompt = this.firstPromptPending;
@@ -34086,53 +34021,20 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
34086
34021
  }
34087
34022
  return { stopReason: "end_turn" };
34088
34023
  }
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
34024
  async cancelPrompt() {
34106
34025
  if (!this.connection) {
34107
34026
  throw new Error("\uC5F0\uACB0\uB418\uC5B4 \uC788\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4");
34108
34027
  }
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
34028
  await this.connection.cancelPrompt();
34117
34029
  }
34118
34030
  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;
34031
+ const selection = this.resolveModelSelection(model);
34032
+ this.currentModelSelection = selection;
34033
+ this.ensurePendingOverrides().model = selection;
34124
34034
  }
34125
34035
  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
- );
34036
+ if (configId === "model") {
34037
+ await this.setModel(value);
34136
34038
  return;
34137
34039
  }
34138
34040
  const pending = this.ensurePendingOverrides();
@@ -34144,10 +34046,6 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
34144
34046
  this.emitTyped("log", `[codex] config '${configId}' will apply on next thread/start or thread/resume`);
34145
34047
  }
34146
34048
  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
34049
  const resolved = this.resolveMode(mode);
34152
34050
  const pending = this.ensurePendingOverrides();
34153
34051
  pending.threadConfig.approvalPolicy = resolved.approvalPolicy;
@@ -34170,17 +34068,6 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
34170
34068
  if (!this.connection) {
34171
34069
  throw new Error("\uC5F0\uACB0\uB418\uC5B4 \uC788\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4");
34172
34070
  }
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
34071
  if (mcpServers?.length) {
34185
34072
  this.emitTyped(
34186
34073
  "log",
@@ -34192,7 +34079,7 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
34192
34079
  sessionId,
34193
34080
  this.buildCodexThreadDefaultsForResume(
34194
34081
  targetCwd,
34195
- void 0,
34082
+ this.currentModelSelection,
34196
34083
  null,
34197
34084
  this.pendingOverrides?.threadConfig ?? {}
34198
34085
  )
@@ -34206,24 +34093,6 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
34206
34093
  throw new Error("\uC5F0\uACB0\uB418\uC5B4 \uC788\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4");
34207
34094
  }
34208
34095
  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
34096
  const result = await this.connection.resetSession(
34228
34097
  this.buildCodexThreadDefaultsForReset(targetCwd)
34229
34098
  );
@@ -34239,33 +34108,14 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
34239
34108
  protocol: "codex-app-server"
34240
34109
  };
34241
34110
  }
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
34111
  clearSessionState() {
34264
34112
  this.sessionId = null;
34265
34113
  this.sessionCwd = null;
34266
34114
  this.currentSystemPrompt = null;
34267
34115
  this.firstPromptPending = null;
34268
34116
  this.pendingOverrides = null;
34117
+ this.currentModelSelection = {};
34118
+ this.archiveSessionOnDisconnect = false;
34269
34119
  }
34270
34120
  validateModelEffort(modelId, effort) {
34271
34121
  if (!effort) {
@@ -34288,69 +34138,7 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
34288
34138
  }
34289
34139
  setupEventForwarding() {
34290
34140
  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
- });
34141
+ this.setupAppServerEventForwarding(this.connection);
34354
34142
  }
34355
34143
  setupAppServerEventForwarding(connection) {
34356
34144
  connection.on("stateChange", (state) => {
@@ -34413,15 +34201,15 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
34413
34201
  if (!this.pendingOverrides || !this.connection) {
34414
34202
  return;
34415
34203
  }
34416
- if (this.connection instanceof AcpConnection) {
34417
- return;
34418
- }
34419
34204
  if (this.pendingOverrides.model) {
34420
- this.connection.setPendingModel(this.pendingOverrides.model);
34205
+ this.connection.setPendingModel(this.pendingOverrides.model.providerModelId ?? null);
34206
+ this.connection.setPendingServiceTier(this.pendingOverrides.model.serviceTier ?? null);
34421
34207
  this.pendingOverrides.model = void 0;
34422
34208
  }
34423
34209
  if (this.pendingOverrides.turnConfig.model) {
34424
- this.connection.setPendingModel(this.pendingOverrides.turnConfig.model);
34210
+ const selection = this.resolveModelSelection(this.pendingOverrides.turnConfig.model);
34211
+ this.connection.setPendingModel(selection.providerModelId ?? null);
34212
+ this.connection.setPendingServiceTier(selection.serviceTier ?? null);
34425
34213
  delete this.pendingOverrides.turnConfig.model;
34426
34214
  }
34427
34215
  if (this.pendingOverrides.turnConfig.effort) {
@@ -34436,19 +34224,22 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
34436
34224
  const config2 = configEntries.length > 0 ? Object.fromEntries(configEntries) : void 0;
34437
34225
  return {
34438
34226
  cwd,
34227
+ model: this.currentModelSelection.providerModelId,
34228
+ serviceTier: this.currentModelSelection.serviceTier,
34439
34229
  approvalPolicy,
34440
34230
  sandbox,
34441
34231
  developerInstructions: void 0,
34442
34232
  config: config2
34443
34233
  };
34444
34234
  }
34445
- buildCodexThreadDefaultsForResume(cwd, model, systemPrompt, threadConfig) {
34235
+ buildCodexThreadDefaultsForResume(cwd, modelSelection, systemPrompt, threadConfig) {
34446
34236
  const { approvalPolicy, sandbox } = threadConfig;
34447
34237
  const configEntries = Object.entries(threadConfig).filter(([key]) => !CODEX_THREAD_POLICY_CONFIG_KEYS.has(key));
34448
34238
  const config2 = configEntries.length > 0 ? Object.fromEntries(configEntries) : void 0;
34449
34239
  return {
34450
34240
  cwd,
34451
- ...model ? { model } : {},
34241
+ ...modelSelection.providerModelId ? { model: modelSelection.providerModelId } : {},
34242
+ ...modelSelection.serviceTier ? { serviceTier: modelSelection.serviceTier } : {},
34452
34243
  approvalPolicy,
34453
34244
  sandbox,
34454
34245
  developerInstructions: systemPrompt ?? void 0,
@@ -34465,16 +34256,6 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
34465
34256
  return { approvalPolicy: "on-request", sandbox: "read-only" };
34466
34257
  }
34467
34258
  }
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
34259
  buildStartupConfigArgs(overrides, servers, modeMapping) {
34479
34260
  const configArgs = [
34480
34261
  ...modeMapping ? [
@@ -34504,6 +34285,10 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
34504
34285
  return;
34505
34286
  }
34506
34287
  try {
34288
+ if (this.archiveSessionOnDisconnect && this.sessionId) {
34289
+ await this.connection.endSession().catch(() => {
34290
+ });
34291
+ }
34507
34292
  await this.connection.disconnect();
34508
34293
  } catch {
34509
34294
  }
@@ -34511,65 +34296,17 @@ var UnifiedCodexAgentClient = class extends EventEmitter {
34511
34296
  this.connection = null;
34512
34297
  this.clearSessionState();
34513
34298
  }
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;
34299
+ resolveModelSelection(modelId) {
34300
+ if (!modelId) {
34301
+ return {};
34561
34302
  }
34562
- return String(error51);
34563
- }
34564
- matchAnyPattern(text, patterns) {
34565
- return patterns.some((pattern) => pattern.test(text));
34303
+ const model = getProviderModels("codex").models.find((entry) => entry.modelId === modelId);
34304
+ return {
34305
+ providerModelId: model?.providerModelId ?? modelId,
34306
+ serviceTier: model?.serviceTier
34307
+ };
34566
34308
  }
34567
34309
  };
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
34310
  var UnifiedCursorAgentClient = class extends EventEmitter {
34574
34311
  connection = null;
34575
34312
  sessionId = null;
@@ -38086,9 +37823,243 @@ function stripIpv6Brackets2(host) {
38086
37823
 
38087
37824
  // core/host/operations/types.ts
38088
37825
  var MAX_GROUP_NAME_LENGTH = 64;
37826
+ var DELETION_GRACE_MS = 8e3;
38089
37827
 
38090
- // core/host/durable-state.ts
38091
- var STATE_VERSION = 2;
37828
+ // core/host/deferred-deletion.ts
37829
+ var DeferredDeletionError = class extends Error {
37830
+ status;
37831
+ constructor(status, message) {
37832
+ super(message);
37833
+ this.name = "DeferredDeletionError";
37834
+ this.status = status;
37835
+ }
37836
+ };
37837
+ var OPERATION_DELETED_EVENT_CHANNEL = "operation:deleted";
37838
+ var OPERATION_RESTORED_EVENT_CHANNEL = "operation:restored";
37839
+ var OPERATION_PURGED_EVENT_CHANNEL = "operation:purged";
37840
+ var PURGE_RETRY_MS = 1e3;
37841
+ function createDeferredDeletionCoordinator(deps) {
37842
+ const now = deps.now ?? Date.now;
37843
+ const randomId = deps.randomId ?? crypto2.randomUUID;
37844
+ const setTimer = deps.setTimer ?? setTimeout;
37845
+ const clearTimer = deps.clearTimer ?? clearTimeout;
37846
+ let tombstones = [];
37847
+ let timer = null;
37848
+ function list() {
37849
+ return tombstones;
37850
+ }
37851
+ function load(nextTombstones) {
37852
+ tombstones = [...nextTombstones];
37853
+ schedule();
37854
+ }
37855
+ function deleteOperation(operationId) {
37856
+ sweepExpired();
37857
+ const existing = tombstones.find((item) => item.kind === "operation" && item.targetId === operationId);
37858
+ if (existing) return toReceipt(existing);
37859
+ const operation = deps.operations.get(operationId);
37860
+ if (!operation) return null;
37861
+ const previousOperations = deps.operations.list();
37862
+ const deletedAt = now();
37863
+ const tombstone = {
37864
+ deletionId: randomId(),
37865
+ targetId: operation.id,
37866
+ deletedAt,
37867
+ expiresAt: deletedAt + DELETION_GRACE_MS,
37868
+ kind: "operation",
37869
+ operation
37870
+ };
37871
+ const nextTombstones = [...tombstones, tombstone];
37872
+ deps.operations.delete(operationId);
37873
+ try {
37874
+ deps.save(nextTombstones);
37875
+ } catch (error51) {
37876
+ deps.operations.replace(previousOperations);
37877
+ throw error51;
37878
+ }
37879
+ tombstones = nextTombstones;
37880
+ publishOperation(OPERATION_DELETED_EVENT_CHANNEL, operation);
37881
+ schedule();
37882
+ return toReceipt(tombstone);
37883
+ }
37884
+ function deleteTheater(theaterId) {
37885
+ sweepExpired();
37886
+ const existing = tombstones.find((item) => item.kind === "theater" && item.targetId === theaterId);
37887
+ if (existing) return toReceipt(existing);
37888
+ const theater = deps.theaters.get(theaterId);
37889
+ if (!theater) return null;
37890
+ const previousTheaters = deps.theaters.list();
37891
+ const previousOperations = deps.operations.list();
37892
+ const previousGroups = deps.operations.listAllGroups();
37893
+ const deletedOperations = deps.operations.listByTheater(theaterId);
37894
+ const deletedGroups = deps.operations.listGroups(theaterId);
37895
+ const deletedAt = now();
37896
+ const tombstone = {
37897
+ deletionId: randomId(),
37898
+ targetId: theater.id,
37899
+ deletedAt,
37900
+ expiresAt: deletedAt + DELETION_GRACE_MS,
37901
+ kind: "theater",
37902
+ theater,
37903
+ operations: deletedOperations,
37904
+ groups: deletedGroups
37905
+ };
37906
+ const nextTombstones = [...tombstones, tombstone];
37907
+ deps.theaters.remove(theaterId);
37908
+ deps.operations.deleteByTheater(theaterId);
37909
+ try {
37910
+ deps.save(nextTombstones);
37911
+ } catch (error51) {
37912
+ deps.theaters.restore(previousTheaters);
37913
+ deps.operations.replace(previousOperations);
37914
+ deps.operations.replaceGroups(previousGroups);
37915
+ throw error51;
37916
+ }
37917
+ tombstones = nextTombstones;
37918
+ deps.unregisterTheaterWorkspaces(theaterId);
37919
+ for (const operation of deletedOperations) publishOperation(OPERATION_DELETED_EVENT_CHANNEL, operation);
37920
+ schedule();
37921
+ return toReceipt(tombstone);
37922
+ }
37923
+ async function restore(deletionId) {
37924
+ const restoredAt = now();
37925
+ const tombstone = tombstones.find((item) => item.deletionId === deletionId);
37926
+ if (!tombstone || tombstone.expiresAt <= restoredAt) {
37927
+ if (tombstone) sweepExpired();
37928
+ throw new DeferredDeletionError(404, "deletion_not_found");
37929
+ }
37930
+ if (tombstone.kind === "operation") {
37931
+ if (deps.operations.get(tombstone.targetId) || !deps.theaters.get(tombstone.operation.theaterId)) {
37932
+ throw new DeferredDeletionError(409, "restore_conflict");
37933
+ }
37934
+ const previousOperations2 = deps.operations.list();
37935
+ const nextTombstones2 = tombstones.filter((item) => item.deletionId !== deletionId);
37936
+ deps.operations.replace([...previousOperations2, tombstone.operation]);
37937
+ try {
37938
+ deps.save(nextTombstones2);
37939
+ } catch (error51) {
37940
+ deps.operations.replace(previousOperations2);
37941
+ throw error51;
37942
+ }
37943
+ tombstones = nextTombstones2;
37944
+ publishOperation(OPERATION_RESTORED_EVENT_CHANNEL, tombstone.operation);
37945
+ schedule();
37946
+ return { ok: true, kind: tombstone.kind, targetId: tombstone.targetId };
37947
+ }
37948
+ await deps.validateTheaterRestore(tombstone.theater);
37949
+ if (deps.theaters.get(tombstone.targetId) || tombstone.operations.some((operation) => deps.operations.get(operation.id)) || hasGroupConflict(tombstone.groups, deps.operations.listAllGroups())) {
37950
+ throw new DeferredDeletionError(409, "restore_conflict");
37951
+ }
37952
+ const previousTheaters = deps.theaters.list();
37953
+ const previousOperations = deps.operations.list();
37954
+ const previousGroups = deps.operations.listAllGroups();
37955
+ const nextTombstones = tombstones.filter((item) => item.deletionId !== deletionId);
37956
+ deps.theaters.restore([...previousTheaters, tombstone.theater]);
37957
+ deps.operations.replace([...previousOperations, ...tombstone.operations]);
37958
+ deps.operations.replaceGroups([...previousGroups, ...tombstone.groups]);
37959
+ try {
37960
+ deps.save(nextTombstones);
37961
+ } catch (error51) {
37962
+ deps.theaters.restore(previousTheaters);
37963
+ deps.operations.replace(previousOperations);
37964
+ deps.operations.replaceGroups(previousGroups);
37965
+ throw error51;
37966
+ }
37967
+ tombstones = nextTombstones;
37968
+ await deps.registerTheaterWorkspace(tombstone.theater);
37969
+ for (const operation of tombstone.operations) publishOperation(OPERATION_RESTORED_EVENT_CHANNEL, operation);
37970
+ schedule();
37971
+ return { ok: true, kind: tombstone.kind, targetId: tombstone.targetId };
37972
+ }
37973
+ function sweepExpired() {
37974
+ const cutoff = now();
37975
+ const expired = tombstones.filter((item) => item.expiresAt <= cutoff);
37976
+ if (expired.length === 0) {
37977
+ schedule();
37978
+ return;
37979
+ }
37980
+ const expiredIds = new Set(expired.map((item) => item.deletionId));
37981
+ const nextTombstones = tombstones.filter((item) => !expiredIds.has(item.deletionId));
37982
+ try {
37983
+ deps.save(nextTombstones);
37984
+ } catch (error51) {
37985
+ schedule(PURGE_RETRY_MS);
37986
+ throw error51;
37987
+ }
37988
+ tombstones = nextTombstones;
37989
+ for (const tombstone of expired) {
37990
+ if (tombstone.kind === "operation") {
37991
+ publishOperation(OPERATION_PURGED_EVENT_CHANNEL, tombstone.operation);
37992
+ } else {
37993
+ for (const operation of tombstone.operations) publishOperation(OPERATION_PURGED_EVENT_CHANNEL, operation);
37994
+ }
37995
+ }
37996
+ schedule();
37997
+ }
37998
+ function hasPendingOperation(operationId) {
37999
+ sweepExpired();
38000
+ return tombstones.some((item) => item.kind === "operation" ? item.targetId === operationId : item.operations.some((operation) => operation.id === operationId));
38001
+ }
38002
+ function hasPendingTheater(theaterId) {
38003
+ sweepExpired();
38004
+ return tombstones.some((item) => item.kind === "theater" && item.targetId === theaterId);
38005
+ }
38006
+ function schedule(forcedDelay) {
38007
+ if (timer) clearTimer(timer);
38008
+ timer = null;
38009
+ if (forcedDelay !== void 0) {
38010
+ timer = setTimer(runScheduledSweep, forcedDelay);
38011
+ timer.unref?.();
38012
+ return;
38013
+ }
38014
+ const nearest = tombstones.reduce((minimum, item) => minimum === null ? item.expiresAt : Math.min(minimum, item.expiresAt), null);
38015
+ if (nearest === null) return;
38016
+ timer = setTimer(runScheduledSweep, Math.max(0, nearest - now()));
38017
+ timer.unref?.();
38018
+ }
38019
+ function runScheduledSweep() {
38020
+ timer = null;
38021
+ try {
38022
+ sweepExpired();
38023
+ } catch {
38024
+ }
38025
+ }
38026
+ function dispose() {
38027
+ if (timer) clearTimer(timer);
38028
+ timer = null;
38029
+ }
38030
+ function publishOperation(channel, operation) {
38031
+ deps.publish(channel, {
38032
+ operationId: operation.id,
38033
+ pluginId: operation.pluginId,
38034
+ type: operation.type,
38035
+ ...channel === OPERATION_RESTORED_EVENT_CHANNEL ? { operation } : {}
38036
+ });
38037
+ }
38038
+ return {
38039
+ list,
38040
+ load,
38041
+ deleteOperation,
38042
+ deleteTheater,
38043
+ restore,
38044
+ sweepExpired,
38045
+ hasPendingOperation,
38046
+ hasPendingTheater,
38047
+ dispose
38048
+ };
38049
+ }
38050
+ function toReceipt(tombstone) {
38051
+ return {
38052
+ deletionId: tombstone.deletionId,
38053
+ kind: tombstone.kind,
38054
+ targetId: tombstone.targetId,
38055
+ expiresAt: tombstone.expiresAt
38056
+ };
38057
+ }
38058
+ function hasGroupConflict(groups, existing) {
38059
+ const existingIds = new Set(existing.map((group) => group.id));
38060
+ return groups.some((group) => existingIds.has(group.id));
38061
+ }
38062
+ var STATE_VERSION = 3;
38092
38063
  var STATE_LOCK_DIR_NAME = "state.lock";
38093
38064
  var STATE_LOCK_OWNER_FILE_NAME = "owner.json";
38094
38065
  var STATE_TEMP_PREFIX = ".state.";
@@ -38135,25 +38106,37 @@ function sanitizeDurableConsoleState(value) {
38135
38106
  version: STATE_VERSION,
38136
38107
  theaters: readTheaterRegistrations(upgraded.theaters),
38137
38108
  operations: readOperations(upgraded.operations),
38138
- groups: readOperationGroups(upgraded.groups)
38109
+ groups: readOperationGroups(upgraded.groups),
38110
+ deletionTombstones: readDeletionTombstones(upgraded.deletionTombstones)
38139
38111
  };
38140
38112
  }
38141
38113
  function emptyDurableConsoleState() {
38142
- return { version: STATE_VERSION, theaters: [], operations: [], groups: [] };
38114
+ return { version: STATE_VERSION, theaters: [], operations: [], groups: [], deletionTombstones: [] };
38143
38115
  }
38144
38116
  function migrateToCurrentVersion(value) {
38145
- if (value.version === 1) return migrateV1ToV2(value);
38146
- return value;
38117
+ let current = value;
38118
+ if (current.version === 1) current = migrateV1ToV2(current);
38119
+ if (current.version === 2) current = migrateV2ToV3(current);
38120
+ return current;
38147
38121
  }
38148
38122
  function migrateV1ToV2(v1) {
38149
38123
  const operations = Array.isArray(v1.operations) ? v1.operations.map(migrateV1Operation) : [];
38150
38124
  return {
38151
- version: STATE_VERSION,
38125
+ version: 2,
38152
38126
  theaters: v1.theaters ?? [],
38153
38127
  operations,
38154
38128
  groups: []
38155
38129
  };
38156
38130
  }
38131
+ function migrateV2ToV3(v2) {
38132
+ return {
38133
+ version: STATE_VERSION,
38134
+ theaters: v2.theaters ?? [],
38135
+ operations: v2.operations ?? [],
38136
+ groups: v2.groups ?? [],
38137
+ deletionTombstones: []
38138
+ };
38139
+ }
38157
38140
  function migrateV1Operation(value) {
38158
38141
  if (!isRecord5(value)) return null;
38159
38142
  const sessionId = readNonEmptyString(value.sessionId);
@@ -38317,6 +38300,37 @@ function sanitizeOperationGroup(value) {
38317
38300
  if (!VALID_GROUP_COLOR_KEYS.has(color)) return null;
38318
38301
  return { id, theaterId, name: rawName, color, order, createdAt };
38319
38302
  }
38303
+ function readDeletionTombstones(value) {
38304
+ if (!Array.isArray(value)) return [];
38305
+ const tombstones = [];
38306
+ for (const item of value) {
38307
+ const tombstone = sanitizeDeletionTombstone(item);
38308
+ if (tombstone) tombstones.push(tombstone);
38309
+ }
38310
+ return tombstones;
38311
+ }
38312
+ function sanitizeDeletionTombstone(value) {
38313
+ if (!isRecord5(value)) return null;
38314
+ const deletionId = readNonEmptyString(value.deletionId);
38315
+ const targetId = readNonEmptyString(value.targetId);
38316
+ const deletedAt = readFiniteNumber(value.deletedAt);
38317
+ const expiresAt = readFiniteNumber(value.expiresAt);
38318
+ if (!deletionId || !targetId || deletedAt === null || expiresAt === null) return null;
38319
+ if (value.kind === "operation") {
38320
+ const operation = sanitizeOperationNode(value.operation);
38321
+ if (!operation || operation.id !== targetId) return null;
38322
+ return { deletionId, targetId, deletedAt, expiresAt, kind: "operation", operation };
38323
+ }
38324
+ if (value.kind === "theater") {
38325
+ const theater = sanitizeTheaterRegistration(value.theater);
38326
+ if (!theater || theater.id !== targetId || !Array.isArray(value.operations) || !Array.isArray(value.groups)) return null;
38327
+ const operations = value.operations.map(sanitizeOperationNode).filter((operation) => operation !== null);
38328
+ const groups = value.groups.map(sanitizeOperationGroup).filter((group) => group !== null);
38329
+ 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;
38330
+ return { deletionId, targetId, deletedAt, expiresAt, kind: "theater", theater, operations, groups };
38331
+ }
38332
+ return null;
38333
+ }
38320
38334
 
38321
38335
  // core/host/operations/sanitize.ts
38322
38336
  var FIXED_SENSITIVE_OPERATION_FIELDS = /* @__PURE__ */ new Set([
@@ -38332,11 +38346,20 @@ var FIXED_SENSITIVE_OPERATION_FIELDS = /* @__PURE__ */ new Set([
38332
38346
  ]);
38333
38347
  function createSanitizedOpDto(node, options = {}) {
38334
38348
  const sensitiveFields = /* @__PURE__ */ new Set([...FIXED_SENSITIVE_OPERATION_FIELDS, ...options.sensitiveFields ?? []]);
38349
+ const payload = sanitizeRecord(node.payload, sensitiveFields);
38350
+ delete payload.resumeAvailable;
38351
+ if (isResumableProviderSession(node.payload?.providerSession)) payload.resumeAvailable = true;
38335
38352
  return {
38336
38353
  ...node,
38337
- payload: sanitizeRecord(node.payload, sensitiveFields)
38354
+ payload
38338
38355
  };
38339
38356
  }
38357
+ function isResumableProviderSession(value) {
38358
+ if (!isRecord6(value)) return false;
38359
+ const provider = value.provider;
38360
+ const sessionId = value.sessionId;
38361
+ return typeof provider === "string" && provider.length > 0 && typeof sessionId === "string" && sessionId.length > 0;
38362
+ }
38340
38363
  function sanitizeRecord(value, sensitiveFields) {
38341
38364
  const output = {};
38342
38365
  for (const [key, item] of Object.entries(value)) {
@@ -38418,6 +38441,10 @@ async function handleCollection(req, res, deps) {
38418
38441
  deps.writeJson(res, 400, { error: "invalid_operation_accent" });
38419
38442
  return;
38420
38443
  }
38444
+ if (typeof body2.id === "string" && deps.isPendingDeletion?.(body2.id)) {
38445
+ deps.writeJson(res, 409, { error: "pending_deletion" });
38446
+ return;
38447
+ }
38421
38448
  try {
38422
38449
  const node = deps.store.create({
38423
38450
  id: typeof body2.id === "string" ? body2.id : void 0,
@@ -38450,8 +38477,8 @@ async function handleItem(req, res, id, deps) {
38450
38477
  return;
38451
38478
  }
38452
38479
  if (req.method === "DELETE") {
38453
- const deleted = deps.deleteOperation(id);
38454
- deps.writeJson(res, deleted ? 200 : 404, deleted ? { ok: true } : { error: "operation_not_found" });
38480
+ const deletion = deps.deleteOperation(id);
38481
+ deps.writeJson(res, 200, { ok: true, deletion });
38455
38482
  return;
38456
38483
  }
38457
38484
  const body2 = await deps.readJsonBody(req);
@@ -39042,6 +39069,10 @@ function createPlansRouter(deps) {
39042
39069
  await handlePlansRead(req, res, deps);
39043
39070
  return true;
39044
39071
  }
39072
+ if (pathname === "/api/v1/plans/search") {
39073
+ await handlePlansSearch(req, res, deps);
39074
+ return true;
39075
+ }
39045
39076
  if (pathname === "/api/v1/plans/events") {
39046
39077
  await handlePlansEvents(req, res, deps);
39047
39078
  return true;
@@ -39049,6 +39080,38 @@ function createPlansRouter(deps) {
39049
39080
  return false;
39050
39081
  };
39051
39082
  }
39083
+ async function handlePlansSearch(req, res, deps) {
39084
+ if (req.method !== "POST") {
39085
+ deps.writeJson(res, 405, { error: "Method not allowed" });
39086
+ return;
39087
+ }
39088
+ if (!deps.isAuthorized(req)) {
39089
+ deps.writeJson(res, 401, { error: "unauthorized" });
39090
+ return;
39091
+ }
39092
+ const body2 = await deps.readJsonBody(req);
39093
+ 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) {
39094
+ deps.writeJson(res, 400, { error: "invalid_request" });
39095
+ return;
39096
+ }
39097
+ const theaterPath = deps.resolveTheaterPath(body2.theaterId);
39098
+ if (!theaterPath) {
39099
+ deps.writeJson(res, 404, { error: "theater_not_found" });
39100
+ return;
39101
+ }
39102
+ try {
39103
+ const context = await resolveTheaterRoot(theaterPath);
39104
+ const tokens = body2.query.trim().toLocaleLowerCase().split(/\s+/).filter(Boolean);
39105
+ const plans = (await listPlansForWorkspace(deps.dataDir, context.realRoot)).filter((plan) => {
39106
+ const text = `${plan.name}
39107
+ ${plan.title}`.toLocaleLowerCase();
39108
+ return tokens.every((token) => text.includes(token));
39109
+ }).slice(0, body2.limit).map((plan) => ({ name: plan.name, title: plan.title }));
39110
+ deps.writeJson(res, 200, { plans });
39111
+ } catch (error51) {
39112
+ writePlanStoreError(res, deps, error51);
39113
+ }
39114
+ }
39052
39115
  async function handlePlansEvents(req, res, deps) {
39053
39116
  if (req.method !== "GET") {
39054
39117
  deps.writeJson(res, 405, { error: "Method not allowed" });
@@ -40864,7 +40927,6 @@ var SERVER_TIMEOUT_MS = 30 * 60 * 1e3;
40864
40927
  var MAX_BODY_BYTES = 1024 * 1024;
40865
40928
  var UPDATE_APPLY_FORBIDDEN_BODY_KEYS = /* @__PURE__ */ new Set(["channel", "package", "packageName", "packageVersion", "packages", "targetVersion", "version"]);
40866
40929
  var OPERATION_RENAMED_EVENT_CHANNEL = "operation:renamed";
40867
- var OPERATION_DELETED_EVENT_CHANNEL = "operation:deleted";
40868
40930
  var PAIRING_IDENTITY_PATH = "/api/v1/pairing-identity";
40869
40931
  var PAIRING_IDENTITY = { product: "fleet-console", schemaVersion: 1, pairingProtocolVersion: 1 };
40870
40932
  var SERVER_API_CATALOG = [
@@ -40924,6 +40986,13 @@ var SERVER_API_CATALOG = [
40924
40986
  category: "Observer",
40925
40987
  gate: "origin-write"
40926
40988
  },
40989
+ {
40990
+ method: "POST",
40991
+ path: "/api/v1/deletions/:deletionId/restore",
40992
+ summary: "\uC720\uC608 \uC911\uC778 Operation \uB610\uB294 Theater \uC0AD\uC81C\uB97C \uBCF5\uAD6C\uD569\uB2C8\uB2E4.",
40993
+ category: "Observer",
40994
+ gate: "origin-write"
40995
+ },
40927
40996
  {
40928
40997
  method: "POST",
40929
40998
  path: "/api/v1/theaters/:theaterId/codex-workspace",
@@ -41051,16 +41120,29 @@ function createConsoleServer(deps = {}) {
41051
41120
  function publishPluginEvent(channel2, payload) {
41052
41121
  for (const listener of pluginEventListeners.get(channel2) ?? []) listener(payload);
41053
41122
  }
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;
41123
+ const deletionCoordinator = createDeferredDeletionCoordinator({
41124
+ operations,
41125
+ theaters,
41126
+ save: saveDurableState,
41127
+ publish: publishPluginEvent,
41128
+ unregisterTheaterWorkspaces: (theaterId) => codex.unregisterTheaterWorkspaces(theaterId),
41129
+ validateTheaterRestore: async (theater) => {
41130
+ try {
41131
+ const restoredRealpath = await fs10__default.promises.realpath(theater.path);
41132
+ const stat4 = await fs10__default.promises.stat(restoredRealpath);
41133
+ if (!stat4.isDirectory() || restoredRealpath !== theater.realpath || workspaceHash(restoredRealpath) !== theater.id) {
41134
+ throw new Error("restore_conflict");
41135
+ }
41136
+ } catch {
41137
+ throw new DeferredDeletionError(409, "restore_parent_missing");
41138
+ }
41139
+ },
41140
+ registerTheaterWorkspace: async (theater) => {
41141
+ await codex.registerWorkspace(theater.realpath, theater.lastOpenedAt, theater.id);
41142
+ }
41143
+ });
41144
+ function deleteOperationForPlugin(operationId) {
41145
+ return deletionCoordinator.deleteOperation(operationId) !== null;
41064
41146
  }
41065
41147
  let desktopFullscreen = false;
41066
41148
  let unsubscribeUpdateCheckChanges = updateCheck.onChange?.(() => {
@@ -41071,6 +41153,7 @@ function createConsoleServer(deps = {}) {
41071
41153
  list: () => operations.list(),
41072
41154
  get: (id) => operations.get(id),
41073
41155
  create: (input) => {
41156
+ if (input.id && deletionCoordinator.hasPendingOperation(input.id)) throw new Error("pending_deletion");
41074
41157
  const operation = operations.create(input);
41075
41158
  persistDurableState();
41076
41159
  return operation;
@@ -41086,7 +41169,7 @@ function createConsoleServer(deps = {}) {
41086
41169
  }
41087
41170
  return operation;
41088
41171
  },
41089
- delete: deleteOperation,
41172
+ delete: deleteOperationForPlugin,
41090
41173
  registerOperationType: (type) => {
41091
41174
  pluginOperationTypes.add(type);
41092
41175
  return () => {
@@ -41300,7 +41383,8 @@ function createConsoleServer(deps = {}) {
41300
41383
  readJsonBody,
41301
41384
  writeJson,
41302
41385
  persist: persistDurableState,
41303
- deleteOperation,
41386
+ deleteOperation: (operationId) => deletionCoordinator.deleteOperation(operationId),
41387
+ isPendingDeletion: (operationId) => deletionCoordinator.hasPendingOperation(operationId),
41304
41388
  getPluginSensitiveFields: (pluginId) => [
41305
41389
  ...pluginHost.sensitiveFieldsByPluginId.get(pluginId) ?? [],
41306
41390
  ...pluginPayloadSanitizers.get(pluginId) ?? []
@@ -41390,6 +41474,11 @@ function createConsoleServer(deps = {}) {
41390
41474
  runAsyncBooleanHandler(plansRouter({ req, res, pathname }), res, () => false);
41391
41475
  return;
41392
41476
  }
41477
+ const restoreMatch = pathname.match(/^\/api\/v1\/deletions\/([^/]+)\/restore$/);
41478
+ if (restoreMatch) {
41479
+ runAsyncHandler(handleDeferredDeletionRestore(req, res, decodeURIComponent(restoreMatch[1] ?? "")), res);
41480
+ return;
41481
+ }
41393
41482
  const theaterItemMatch = pathname.match(/^\/api\/v1\/theaters\/([^/]+)$/);
41394
41483
  if (theaterItemMatch) {
41395
41484
  runAsyncHandler(handleObserverTheaterItem(req, res, decodeURIComponent(theaterItemMatch[1] ?? "")), res);
@@ -41537,6 +41626,11 @@ function createConsoleServer(deps = {}) {
41537
41626
  writeJson(res, 400, { error: "invalid_folder_grant" });
41538
41627
  return;
41539
41628
  }
41629
+ const canonicalCwd = canonicalizeTheaterPathSync(cwd);
41630
+ if (deletionCoordinator.hasPendingTheater(workspaceHash(canonicalCwd))) {
41631
+ writeJson(res, 409, { error: "pending_deletion" });
41632
+ return;
41633
+ }
41540
41634
  const theater = await theaters.register(cwd);
41541
41635
  await codex.registerWorkspace(theater.realpath, void 0, theater.id);
41542
41636
  persistDurableState();
@@ -41566,12 +41660,27 @@ function createConsoleServer(deps = {}) {
41566
41660
  writeJson(res, 200, toTheaterInfo(theater, true));
41567
41661
  return;
41568
41662
  }
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 });
41663
+ const deletion = deletionCoordinator.deleteTheater(theaterId);
41664
+ writeJson(res, 200, { ok: true, deletion });
41665
+ }
41666
+ async function handleDeferredDeletionRestore(req, res, deletionId) {
41667
+ if (req.method !== "POST") {
41668
+ writeJson(res, 405, { error: "Method not allowed" });
41669
+ return;
41670
+ }
41671
+ if (!isTerminalAuthorized(req)) {
41672
+ writeJson(res, 401, { error: "unauthorized" });
41673
+ return;
41674
+ }
41675
+ try {
41676
+ writeJson(res, 200, await deletionCoordinator.restore(deletionId));
41677
+ } catch (error51) {
41678
+ if (error51 instanceof DeferredDeletionError) {
41679
+ writeJson(res, error51.status, { error: error51.message });
41680
+ return;
41681
+ }
41682
+ throw error51;
41683
+ }
41575
41684
  }
41576
41685
  function handleStatus(req, res) {
41577
41686
  const theaterId = readUrl(req).searchParams.get("theaterId");
@@ -41788,6 +41897,12 @@ function createConsoleServer(deps = {}) {
41788
41897
  operations.replace([]);
41789
41898
  operations.replaceGroups([]);
41790
41899
  }
41900
+ deletionCoordinator.load(state.deletionTombstones ?? []);
41901
+ try {
41902
+ deletionCoordinator.sweepExpired();
41903
+ } catch (error51) {
41904
+ console.warn(`[fleet-console] Expired deletion sweep deferred: ${error51 instanceof Error ? error51.message : String(error51)}`);
41905
+ }
41791
41906
  await restoreCodexWorkspaces();
41792
41907
  }
41793
41908
  async function restoreCodexWorkspaces() {
@@ -41829,16 +41944,20 @@ function createConsoleServer(deps = {}) {
41829
41944
  }
41830
41945
  function persistDurableState() {
41831
41946
  try {
41832
- durableStateStore.save({
41833
- version: 2,
41834
- theaters: theaters.list(),
41835
- operations: operations.list(),
41836
- groups: operations.listAllGroups()
41837
- });
41947
+ saveDurableState(deletionCoordinator.list());
41838
41948
  } catch (error51) {
41839
41949
  console.warn(`[fleet-console] Durable state save failed: ${error51 instanceof Error ? error51.message : String(error51)}`);
41840
41950
  }
41841
41951
  }
41952
+ function saveDurableState(deletionTombstones) {
41953
+ durableStateStore.save({
41954
+ version: STATE_VERSION,
41955
+ theaters: theaters.list(),
41956
+ operations: operations.list(),
41957
+ groups: operations.listAllGroups(),
41958
+ deletionTombstones
41959
+ });
41960
+ }
41842
41961
  async function stopServer() {
41843
41962
  const current = server;
41844
41963
  const currentLoopback = loopbackServer;
@@ -41848,6 +41967,7 @@ function createConsoleServer(deps = {}) {
41848
41967
  lockHandle = null;
41849
41968
  activeLockFile = null;
41850
41969
  activeEndpoint = null;
41970
+ deletionCoordinator.dispose();
41851
41971
  try {
41852
41972
  await Promise.all([
41853
41973
  closeHttpServer(current),