@dotobokuri/fleet-console 1.5.5 → 1.6.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 (67) hide show
  1. package/AGENTS.md +13 -11
  2. package/dist/cli-bin.mjs +1566 -495
  3. package/dist/cli.d.ts +6 -1
  4. package/dist/cli.mjs +1566 -495
  5. package/dist/client/assets/{_baseUniq-CpEjig2A.js → _baseUniq-CYDnUUwD.js} +1 -1
  6. package/dist/client/assets/{arc-BmymS_Kz.js → arc-C2kUi1S_.js} +1 -1
  7. package/dist/client/assets/{architectureDiagram-Q4EWVU46-D1d0KVz1.js → architectureDiagram-Q4EWVU46-CsfgL2zC.js} +1 -1
  8. package/dist/client/assets/{blockDiagram-DXYQGD6D-Cwuv0sNk.js → blockDiagram-DXYQGD6D-uqim2Pmo.js} +1 -1
  9. package/dist/client/assets/{c4Diagram-AHTNJAMY-Bsk-GjC_.js → c4Diagram-AHTNJAMY-CRz88GqP.js} +1 -1
  10. package/dist/client/assets/channel-BDggAVtM.js +1 -0
  11. package/dist/client/assets/{chunk-4BX2VUAB-ZM4i9T4D.js → chunk-4BX2VUAB-DNgf85a7.js} +1 -1
  12. package/dist/client/assets/{chunk-4TB4RGXK-Sw5ODKRj.js → chunk-4TB4RGXK-Ds39jFtV.js} +1 -1
  13. package/dist/client/assets/{chunk-55IACEB6-DDDLwVg4.js → chunk-55IACEB6-Bv7hCHgD.js} +1 -1
  14. package/dist/client/assets/{chunk-EDXVE4YY-BNk5Xk1Q.js → chunk-EDXVE4YY-BUjgWLOR.js} +1 -1
  15. package/dist/client/assets/{chunk-FMBD7UC4-C_CC6grw.js → chunk-FMBD7UC4-WVMeE3Je.js} +1 -1
  16. package/dist/client/assets/{chunk-OYMX7WX6-DTbaYVUc.js → chunk-OYMX7WX6-Bd5Z4NNL.js} +1 -1
  17. package/dist/client/assets/{chunk-QZHKN3VN-i7z9jXPR.js → chunk-QZHKN3VN-BfIbGoT4.js} +1 -1
  18. package/dist/client/assets/{chunk-YZCP3GAM-cTSeZOtz.js → chunk-YZCP3GAM-T_CDhFqg.js} +1 -1
  19. package/dist/client/assets/classDiagram-6PBFFD2Q-AVod154E.js +1 -0
  20. package/dist/client/assets/classDiagram-v2-HSJHXN6E-AVod154E.js +1 -0
  21. package/dist/client/assets/clone-DMJcvMWj.js +1 -0
  22. package/dist/client/assets/{cose-bilkent-S5V4N54A-uVW4wTRq.js → cose-bilkent-S5V4N54A-M6EvTH7M.js} +1 -1
  23. package/dist/client/assets/{dagre-KV5264BT-CclQ97X6.js → dagre-KV5264BT-UUCScvPY.js} +1 -1
  24. package/dist/client/assets/{diagram-5BDNPKRD-eZaXK0Xj.js → diagram-5BDNPKRD-BnmEvwtB.js} +1 -1
  25. package/dist/client/assets/{diagram-G4DWMVQ6-DIQ8qOFv.js → diagram-G4DWMVQ6-QHdTQz1G.js} +1 -1
  26. package/dist/client/assets/{diagram-MMDJMWI5-B_EbeR8B.js → diagram-MMDJMWI5-B-4tZ6wC.js} +1 -1
  27. package/dist/client/assets/{diagram-TYMM5635-DI64qX-Y.js → diagram-TYMM5635-D5KVosUo.js} +1 -1
  28. package/dist/client/assets/{erDiagram-SMLLAGMA-DLkwbOsk.js → erDiagram-SMLLAGMA-B-FwpOjI.js} +1 -1
  29. package/dist/client/assets/{flowDiagram-DWJPFMVM-BWvMzejD.js → flowDiagram-DWJPFMVM-CO00mjhm.js} +1 -1
  30. package/dist/client/assets/fraunces-latin-ext-standard-italic-CGbN9UgK.woff2 +0 -0
  31. package/dist/client/assets/fraunces-latin-standard-italic-lSdLDfvT.woff2 +0 -0
  32. package/dist/client/assets/fraunces-vietnamese-standard-italic-DxWqP7Ku.woff2 +0 -0
  33. package/dist/client/assets/{ganttDiagram-T4ZO3ILL-B4qUxmRl.js → ganttDiagram-T4ZO3ILL-ChcRAmXN.js} +1 -1
  34. package/dist/client/assets/{gitGraphDiagram-UUTBAWPF-CnvcLURP.js → gitGraphDiagram-UUTBAWPF-BWSq2uT3.js} +1 -1
  35. package/dist/client/assets/{graph-N4oFFLXe.js → graph-mErvxWG_.js} +1 -1
  36. package/dist/client/assets/index-B4QPttGw.css +1 -0
  37. package/dist/client/assets/index-GN6Lp6Jj.js +734 -0
  38. package/dist/client/assets/{infoDiagram-42DDH7IO-3uxP8aX_.js → infoDiagram-42DDH7IO-CchQFIPi.js} +1 -1
  39. package/dist/client/assets/{ishikawaDiagram-UXIWVN3A-DQomHvSO.js → ishikawaDiagram-UXIWVN3A-BAsF6BnZ.js} +1 -1
  40. package/dist/client/assets/{journeyDiagram-VCZTEJTY-C_Ogwo77.js → journeyDiagram-VCZTEJTY-DE0wiQxC.js} +1 -1
  41. package/dist/client/assets/{kanban-definition-6JOO6SKY-CGOeur5D.js → kanban-definition-6JOO6SKY-BzPJLpKw.js} +1 -1
  42. package/dist/client/assets/{layout-DKlBkSB9.js → layout-CO0unZG0.js} +1 -1
  43. package/dist/client/assets/{linear-2pQUtBdp.js → linear-CkDoVGz5.js} +1 -1
  44. package/dist/client/assets/{mermaid.core-BrseLPLb.js → mermaid.core-BWqsf-WS.js} +4 -4
  45. package/dist/client/assets/{min-OFtHLPyF.js → min-BMb-G34C.js} +1 -1
  46. package/dist/client/assets/{mindmap-definition-QFDTVHPH-DQk9T3wA.js → mindmap-definition-QFDTVHPH-XVxnUTPw.js} +1 -1
  47. package/dist/client/assets/{pieDiagram-DEJITSTG-Btu4aJWj.js → pieDiagram-DEJITSTG-CKM6Cfhz.js} +1 -1
  48. package/dist/client/assets/{quadrantDiagram-34T5L4WZ-BHMDVip9.js → quadrantDiagram-34T5L4WZ-Dn_2AZqA.js} +1 -1
  49. package/dist/client/assets/{requirementDiagram-MS252O5E-CbXi80dX.js → requirementDiagram-MS252O5E-uNgGGE60.js} +1 -1
  50. package/dist/client/assets/{sankeyDiagram-XADWPNL6-D5c7vUw4.js → sankeyDiagram-XADWPNL6-B75Ahck1.js} +1 -1
  51. package/dist/client/assets/{sequenceDiagram-FGHM5R23-CGuRanG_.js → sequenceDiagram-FGHM5R23-Cw5UNG2_.js} +1 -1
  52. package/dist/client/assets/{stateDiagram-FHFEXIEX-CVD3CB69.js → stateDiagram-FHFEXIEX-C6u6py_B.js} +1 -1
  53. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-BMBsbsoy.js +1 -0
  54. package/dist/client/assets/{timeline-definition-GMOUNBTQ-DMTeeglq.js → timeline-definition-GMOUNBTQ-Bi50wA17.js} +1 -1
  55. package/dist/client/assets/{vennDiagram-DHZGUBPP-CQ6TwRhb.js → vennDiagram-DHZGUBPP-CufQGlDN.js} +1 -1
  56. package/dist/client/assets/{wardley-RL74JXVD-DdgusU4o.js → wardley-RL74JXVD-BQLghHpH.js} +1 -1
  57. package/dist/client/assets/{wardleyDiagram-NUSXRM2D-BFsWqK49.js → wardleyDiagram-NUSXRM2D-C-_4o-CK.js} +1 -1
  58. package/dist/client/assets/{xychartDiagram-5P7HB3ND-Hca3jUV6.js → xychartDiagram-5P7HB3ND-e4ekvSc7.js} +1 -1
  59. package/dist/client/index.html +2 -2
  60. package/package.json +1 -1
  61. package/dist/client/assets/channel--qpYuR58.js +0 -1
  62. package/dist/client/assets/classDiagram-6PBFFD2Q-BAI-fNQ3.js +0 -1
  63. package/dist/client/assets/classDiagram-v2-HSJHXN6E-BAI-fNQ3.js +0 -1
  64. package/dist/client/assets/clone-CqNqlAzp.js +0 -1
  65. package/dist/client/assets/index-DfJLxHX8.js +0 -734
  66. package/dist/client/assets/index-uNbiLDxs.css +0 -1
  67. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-BevvDvg0.js +0 -1
package/dist/cli.mjs CHANGED
@@ -4,14 +4,14 @@ import * as fs5 from 'fs';
4
4
  import fs5__default, { readFileSync, existsSync, mkdtempSync, writeFileSync, mkdirSync, rmSync, chmodSync, renameSync, readdirSync, constants, openSync, closeSync, lstatSync, unlinkSync, realpathSync, statSync, symlinkSync, fstatSync } from 'fs';
5
5
  import * as path93 from 'path';
6
6
  import path93__default, { join, resolve, relative, dirname } from 'path';
7
- import process4 from 'process';
7
+ import process5 from 'process';
8
8
  import { fileURLToPath, pathToFileURL } from 'url';
9
9
  import * as os22 from 'os';
10
10
  import os22__default from 'os';
11
11
  import { EventEmitter } from 'events';
12
12
  import { stat, mkdir, readFile, readdir, appendFile, writeFile, rename, access, rm } from 'fs/promises';
13
13
  import { Writable, Readable } from 'stream';
14
- import crypto3, { createHash, randomUUID, timingSafeEqual } from 'crypto';
14
+ import crypto5, { createHash, randomUUID, timingSafeEqual } from 'crypto';
15
15
  import http from 'http';
16
16
  import { createRequire } from 'module';
17
17
  import net2 from 'net';
@@ -969,10 +969,10 @@ function mergeDefs(...defs) {
969
969
  function cloneDef(schema) {
970
970
  return mergeDefs(schema._zod.def);
971
971
  }
972
- function getElementAtPath(obj, path30) {
973
- if (!path30)
972
+ function getElementAtPath(obj, path34) {
973
+ if (!path34)
974
974
  return obj;
975
- return path30.reduce((acc, key) => acc?.[key], obj);
975
+ return path34.reduce((acc, key) => acc?.[key], obj);
976
976
  }
977
977
  function promiseAllObject(promisesObj) {
978
978
  const keys = Object.keys(promisesObj);
@@ -1381,11 +1381,11 @@ function explicitlyAborted(x, startIndex = 0) {
1381
1381
  }
1382
1382
  return false;
1383
1383
  }
1384
- function prefixIssues(path30, issues) {
1384
+ function prefixIssues(path34, issues) {
1385
1385
  return issues.map((iss) => {
1386
1386
  var _a3;
1387
1387
  (_a3 = iss).path ?? (_a3.path = []);
1388
- iss.path.unshift(path30);
1388
+ iss.path.unshift(path34);
1389
1389
  return iss;
1390
1390
  });
1391
1391
  }
@@ -1532,16 +1532,16 @@ function flattenError(error51, mapper = (issue3) => issue3.message) {
1532
1532
  }
1533
1533
  function formatError(error51, mapper = (issue3) => issue3.message) {
1534
1534
  const fieldErrors = { _errors: [] };
1535
- const processError = (error52, path30 = []) => {
1535
+ const processError = (error52, path34 = []) => {
1536
1536
  for (const issue3 of error52.issues) {
1537
1537
  if (issue3.code === "invalid_union" && issue3.errors.length) {
1538
- issue3.errors.map((issues) => processError({ issues }, [...path30, ...issue3.path]));
1538
+ issue3.errors.map((issues) => processError({ issues }, [...path34, ...issue3.path]));
1539
1539
  } else if (issue3.code === "invalid_key") {
1540
- processError({ issues: issue3.issues }, [...path30, ...issue3.path]);
1540
+ processError({ issues: issue3.issues }, [...path34, ...issue3.path]);
1541
1541
  } else if (issue3.code === "invalid_element") {
1542
- processError({ issues: issue3.issues }, [...path30, ...issue3.path]);
1542
+ processError({ issues: issue3.issues }, [...path34, ...issue3.path]);
1543
1543
  } else {
1544
- const fullpath = [...path30, ...issue3.path];
1544
+ const fullpath = [...path34, ...issue3.path];
1545
1545
  if (fullpath.length === 0) {
1546
1546
  fieldErrors._errors.push(mapper(issue3));
1547
1547
  } else {
@@ -1568,17 +1568,17 @@ function formatError(error51, mapper = (issue3) => issue3.message) {
1568
1568
  }
1569
1569
  function treeifyError(error51, mapper = (issue3) => issue3.message) {
1570
1570
  const result = { errors: [] };
1571
- const processError = (error52, path30 = []) => {
1571
+ const processError = (error52, path34 = []) => {
1572
1572
  var _a3, _b;
1573
1573
  for (const issue3 of error52.issues) {
1574
1574
  if (issue3.code === "invalid_union" && issue3.errors.length) {
1575
- issue3.errors.map((issues) => processError({ issues }, [...path30, ...issue3.path]));
1575
+ issue3.errors.map((issues) => processError({ issues }, [...path34, ...issue3.path]));
1576
1576
  } else if (issue3.code === "invalid_key") {
1577
- processError({ issues: issue3.issues }, [...path30, ...issue3.path]);
1577
+ processError({ issues: issue3.issues }, [...path34, ...issue3.path]);
1578
1578
  } else if (issue3.code === "invalid_element") {
1579
- processError({ issues: issue3.issues }, [...path30, ...issue3.path]);
1579
+ processError({ issues: issue3.issues }, [...path34, ...issue3.path]);
1580
1580
  } else {
1581
- const fullpath = [...path30, ...issue3.path];
1581
+ const fullpath = [...path34, ...issue3.path];
1582
1582
  if (fullpath.length === 0) {
1583
1583
  result.errors.push(mapper(issue3));
1584
1584
  continue;
@@ -1610,8 +1610,8 @@ function treeifyError(error51, mapper = (issue3) => issue3.message) {
1610
1610
  }
1611
1611
  function toDotPath(_path) {
1612
1612
  const segs = [];
1613
- const path30 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
1614
- for (const seg of path30) {
1613
+ const path34 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
1614
+ for (const seg of path34) {
1615
1615
  if (typeof seg === "number")
1616
1616
  segs.push(`[${seg}]`);
1617
1617
  else if (typeof seg === "symbol")
@@ -14275,13 +14275,13 @@ function resolveRef(ref, ctx) {
14275
14275
  if (!ref.startsWith("#")) {
14276
14276
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
14277
14277
  }
14278
- const path30 = ref.slice(1).split("/").filter(Boolean);
14279
- if (path30.length === 0) {
14278
+ const path34 = ref.slice(1).split("/").filter(Boolean);
14279
+ if (path34.length === 0) {
14280
14280
  return ctx.rootSchema;
14281
14281
  }
14282
14282
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
14283
- if (path30[0] === defsKey) {
14284
- const key = path30[1];
14283
+ if (path34[0] === defsKey) {
14284
+ const key = path34[1];
14285
14285
  if (!key || !ctx.defs[key]) {
14286
14286
  throw new Error(`Reference not found: ${ref}`);
14287
14287
  }
@@ -17913,7 +17913,7 @@ function inferBinName(packageName) {
17913
17913
  // ../../packages/core-unified-agent/models.json
17914
17914
  var models_default = {
17915
17915
  version: 1,
17916
- updatedAt: "2026-06-13T00:00:00Z",
17916
+ updatedAt: "2026-06-16T00:00:00Z",
17917
17917
  providers: {
17918
17918
  claude: {
17919
17919
  name: "Claude Code with Anthropic",
@@ -17923,7 +17923,9 @@ var models_default = {
17923
17923
  { modelId: "sonnet", name: "Claude Sonnet", effort: { supported: true, levels: ["low", "medium", "high", "xhigh", "max"], default: "medium" } },
17924
17924
  { modelId: "opus", name: "Claude Opus", effort: { supported: true, levels: ["low", "medium", "high", "xhigh", "max"], default: "xhigh" } },
17925
17925
  { modelId: "opus[1m]", name: "Claude Opus [1M]", effort: { supported: true, levels: ["low", "medium", "high", "xhigh", "max"], default: "xhigh" } },
17926
- { modelId: "claude-opus-4-6[1m]", name: "Claude Opus 4.6 [1M]", effort: { supported: true, levels: ["low", "medium", "high", "xhigh", "max"], default: "high" } }
17926
+ { modelId: "claude-opus-4-6[1m]", name: "Claude Opus 4.6 [1M]", effort: { supported: true, levels: ["low", "medium", "high", "xhigh", "max"], default: "high" } },
17927
+ { modelId: "claude-opus-4-7[1m]", name: "Claude Opus 4.7 [1M]", effort: { supported: true, levels: ["low", "medium", "high", "xhigh", "max"], default: "high" } },
17928
+ { modelId: "claude-opus-4-8[1m]", name: "Claude Opus 4.8 [1M]", effort: { supported: true, levels: ["low", "medium", "high", "xhigh", "max"], default: "high" } }
17927
17929
  ]
17928
17930
  },
17929
17931
  "claude-zai": {
@@ -21074,7 +21076,7 @@ function specToMcpTool(spec) {
21074
21076
  }
21075
21077
  function installExecutorToolCallRouter(runtime, sessionToken, ctx) {
21076
21078
  runtime.server.setOnToolCallArrived(sessionToken, (toolName, args) => {
21077
- const toolCallId = crypto3.randomUUID();
21079
+ const toolCallId = crypto5.randomUUID();
21078
21080
  void runtime.registry.invoke(toolName, args, { cwd: ctx.cwd, sessionLabel: ctx.sessionLabel, toolCallId, signal: ctx.signal }).then((result) => runtime.server.resolveNextToolCall(sessionToken, toolCallId, result)).catch((err) => {
21079
21081
  runtime.server.resolveNextToolCall(sessionToken, toolCallId, {
21080
21082
  content: [{ type: "text", text: err instanceof Error ? err.message : String(err) }],
@@ -21465,7 +21467,7 @@ function createInProcessMcpServer(deps = {}) {
21465
21467
  if (activeStopPromise) await activeStopPromise;
21466
21468
  if (activeServer && activeServerUrl) return activeServerUrl;
21467
21469
  if (activeStartPromise) return activeStartPromise;
21468
- activeOpaquePath = `/${crypto3.randomUUID()}`;
21470
+ activeOpaquePath = `/${crypto5.randomUUID()}`;
21469
21471
  activeStartPromise = new Promise((resolve, reject) => {
21470
21472
  const srv = http.createServer(handleRequest);
21471
21473
  srv.timeout = MCP_SERVER_TIMEOUT_MS;
@@ -21722,7 +21724,7 @@ function createExecutorMcpSession(deps, toolTimeoutSeconds, request) {
21722
21724
  throw new Error("Executor session cwd is required");
21723
21725
  }
21724
21726
  assertNonEmptyExecutorTools(request.serverName, request.specs);
21725
- const token = crypto3.randomUUID();
21727
+ const token = crypto5.randomUUID();
21726
21728
  registerExecutorSessionTools(runtime.runtime, token, [...request.specs]);
21727
21729
  installExecutorToolCallRouter(runtime.runtime, token, {
21728
21730
  cwd,
@@ -21768,7 +21770,7 @@ function issueSessionToken(deps, sessionTokensByLabel, request) {
21768
21770
  for (const { name, runtime } of deps.runtimes) {
21769
21771
  const tools = runtime.registry.getAllAgentTools();
21770
21772
  assertNonEmptyExecutorTools(name, tools);
21771
- const token = crypto3.randomUUID();
21773
+ const token = crypto5.randomUUID();
21772
21774
  registerExecutorSessionTools(runtime, token, tools);
21773
21775
  installExecutorToolCallRouter(runtime, token, { cwd, sessionLabel: label, signal: request.signal });
21774
21776
  tokens.push({ name, token });
@@ -23134,7 +23136,8 @@ function sleepSync(ms) {
23134
23136
  var DEFAULT_TEMP_FILE_MIN_AGE_MS = 6e4;
23135
23137
  function createDurableJsonStore(deps) {
23136
23138
  const now = deps.now ?? (() => Date.now());
23137
- const mode = SECURE_FILE_MODE ;
23139
+ const sensitivity = deps.sensitivity;
23140
+ const mode = sensitivity === "sensitive" ? SECURE_FILE_MODE : 420;
23138
23141
  const minAgeMs = deps.tempCleanupMinAgeMs ?? DEFAULT_TEMP_FILE_MIN_AGE_MS;
23139
23142
  const dir = path93.dirname(deps.filePath);
23140
23143
  return {
@@ -23162,10 +23165,10 @@ function createDurableJsonStore(deps) {
23162
23165
  };
23163
23166
  function withLock(operation) {
23164
23167
  if (!deps.lockDir) {
23165
- ensureSafeDirectoryIfSensitive(dir);
23168
+ ensureSafeDirectoryIfSensitive(dir, sensitivity);
23166
23169
  return operation();
23167
23170
  }
23168
- ensureSafeDirectoryIfSensitive(dir);
23171
+ ensureSafeDirectoryIfSensitive(dir, sensitivity);
23169
23172
  return withDirectoryLock(
23170
23173
  {
23171
23174
  lockDir: deps.lockDir,
@@ -23207,8 +23210,10 @@ function readJsonFile(filePath, sanitize) {
23207
23210
  }
23208
23211
  }
23209
23212
  function ensureSafeDirectoryIfSensitive(dir, sensitivity) {
23210
- {
23213
+ if (sensitivity === "sensitive") {
23211
23214
  ensureSafeDirectory(dir);
23215
+ } else {
23216
+ fs5.mkdirSync(dir, { recursive: true });
23212
23217
  }
23213
23218
  }
23214
23219
 
@@ -31414,7 +31419,7 @@ var LOCK_FILE_MODE = 384;
31414
31419
  function createConsoleLock(deps = {}) {
31415
31420
  const fsImpl = deps.fs ?? fs5__default;
31416
31421
  const now = deps.now ?? Date.now;
31417
- const randomToken = deps.randomToken ?? (() => crypto3.randomBytes(32).toString("base64url"));
31422
+ const randomToken = deps.randomToken ?? (() => crypto5.randomBytes(32).toString("base64url"));
31418
31423
  const hostname5 = deps.hostname ?? (() => "127.0.0.1");
31419
31424
  function ensureLockDir(dir) {
31420
31425
  fsImpl.mkdirSync(dir, { recursive: true, mode: LOCK_DIR_MODE });
@@ -31509,6 +31514,389 @@ function createConsoleLock(deps = {}) {
31509
31514
  }
31510
31515
  return { ensureLockDir, readLock, writeLock, removeLock, assertLockModes, assertTrustedLock };
31511
31516
  }
31517
+ var FLEET_DATA_DIR_NAME = ".fleet";
31518
+ function getFleetDataDir() {
31519
+ return path93.join(os22.homedir(), FLEET_DATA_DIR_NAME);
31520
+ }
31521
+ function readAuthStoreFile(filePath) {
31522
+ let fd;
31523
+ try {
31524
+ fd = fs5.openSync(filePath, fs5.constants.O_RDONLY | NOFOLLOW_FLAG);
31525
+ if (!fs5.fstatSync(fd).isFile()) {
31526
+ return {};
31527
+ }
31528
+ return JSON.parse(fs5.readFileSync(fd, "utf-8"));
31529
+ } catch {
31530
+ return {};
31531
+ } finally {
31532
+ if (fd !== void 0) {
31533
+ try {
31534
+ fs5.closeSync(fd);
31535
+ } catch {
31536
+ }
31537
+ }
31538
+ }
31539
+ }
31540
+ var DEFAULT_AUTH_PATH = path93.join(getFleetDataDir(), "auth.json");
31541
+ var AUTH_LOCK_OWNER_FILE_NAME = "owner.json";
31542
+ var AUTH_LOCK_TIMEOUT_MS = 5e3;
31543
+ function createAuthService(deps = {}) {
31544
+ const authPath = deps.authPath ?? DEFAULT_AUTH_PATH;
31545
+ const lockDir = `${authPath}.lock`;
31546
+ return {
31547
+ // [HIGH #1] TOCTOU 수정: existsSync/read/check/delete/write 전체를 락 블록 안으로 이동
31548
+ async deleteApiKey(providerId) {
31549
+ let deleted = false;
31550
+ withAuthLock(authPath, lockDir, () => {
31551
+ if (!fs5.existsSync(authPath)) return;
31552
+ const data = readAuthStoreFile(authPath);
31553
+ if (!Object.prototype.hasOwnProperty.call(data, providerId)) return;
31554
+ delete data[providerId];
31555
+ writeAuthData(authPath, data);
31556
+ deleted = true;
31557
+ });
31558
+ return deleted;
31559
+ },
31560
+ // [HIGH #2] 읽기 경로: fd 기반 O_RDONLY|O_NOFOLLOW+fstatSync isFile
31561
+ async getApiKey(providerId) {
31562
+ let fd;
31563
+ try {
31564
+ if (!fs5.existsSync(authPath)) {
31565
+ return void 0;
31566
+ }
31567
+ fd = fs5.openSync(authPath, fs5.constants.O_RDONLY | NOFOLLOW_FLAG);
31568
+ if (!fs5.fstatSync(fd).isFile()) {
31569
+ return void 0;
31570
+ }
31571
+ const data = JSON.parse(fs5.readFileSync(fd, "utf-8"));
31572
+ return typeof data[providerId]?.key === "string" ? data[providerId].key : void 0;
31573
+ } catch {
31574
+ return void 0;
31575
+ } finally {
31576
+ if (fd !== void 0) {
31577
+ try {
31578
+ fs5.closeSync(fd);
31579
+ } catch {
31580
+ }
31581
+ }
31582
+ }
31583
+ },
31584
+ // [HIGH #2] 읽기 경로: fd 기반 O_RDONLY|O_NOFOLLOW 통일
31585
+ async listProviderIds() {
31586
+ if (!fs5.existsSync(authPath)) {
31587
+ return [];
31588
+ }
31589
+ try {
31590
+ return Object.keys(readAuthStoreFile(authPath)).sort();
31591
+ } catch {
31592
+ return [];
31593
+ }
31594
+ },
31595
+ async setApiKey(providerId, key) {
31596
+ const dir = path93.dirname(authPath);
31597
+ ensureSafeDirectory(dir);
31598
+ withAuthLock(authPath, lockDir, () => {
31599
+ const data = fs5.existsSync(authPath) ? readAuthStoreFile(authPath) : {};
31600
+ data[providerId] = {
31601
+ ...data[providerId] ?? {},
31602
+ key
31603
+ };
31604
+ writeAuthData(authPath, data);
31605
+ });
31606
+ }
31607
+ };
31608
+ }
31609
+ function withAuthLock(authPath, lockDir, operation) {
31610
+ return withDirectoryLock(
31611
+ {
31612
+ lockDir,
31613
+ ownerFileName: AUTH_LOCK_OWNER_FILE_NAME,
31614
+ timeoutMs: AUTH_LOCK_TIMEOUT_MS
31615
+ },
31616
+ operation
31617
+ );
31618
+ }
31619
+ function writeAuthData(authPath, data) {
31620
+ writeAtomicSync(authPath, `${JSON.stringify(data, null, 2)}
31621
+ `, {
31622
+ mode: SECURE_FILE_MODE,
31623
+ fsync: true
31624
+ });
31625
+ }
31626
+ function formatMissingAuthKeyMessage(input) {
31627
+ const cliHint = input.cli ? `cli '${input.cli}'` : "selected CLI";
31628
+ return `Auth token not found for ${cliHint} (providerId: '${input.providerId}'). Run \`fleet auth login\` to register one.`;
31629
+ }
31630
+ function formatAuthValidationFailureMessage(input) {
31631
+ const detail = input.detail ? ` Detail: ${input.detail}` : "";
31632
+ if (input.status === "unauthorized") {
31633
+ return `Auth token was rejected (providerId: '${input.providerId}'). Check the token and try again.${detail}`;
31634
+ }
31635
+ if (input.status === "forbidden") {
31636
+ return `Auth token is not allowed for this provider (providerId: '${input.providerId}'). Check the token permissions.${detail}`;
31637
+ }
31638
+ if (input.status === "timeout") {
31639
+ return `Auth token validation timed out (providerId: '${input.providerId}'). Check the connection and try again.${detail}`;
31640
+ }
31641
+ if (input.status === "network") {
31642
+ return `Auth token validation failed due to a network error (providerId: '${input.providerId}'). Check the connection and try again.${detail}`;
31643
+ }
31644
+ if (input.status === "server") {
31645
+ return `Auth token validation failed because the provider returned an error (providerId: '${input.providerId}'). Try again later.${detail}`;
31646
+ }
31647
+ return `Auth token validation failed (providerId: '${input.providerId}'). Check the token and try again.${detail}`;
31648
+ }
31649
+ var LEGACY_AUTH_PATH = path93.join(getFleetDataDir(), "agent", "auth.json");
31650
+ var CURRENT_AUTH_PATH = path93.join(getFleetDataDir(), "auth.json");
31651
+ function mergeAuthStoresNoOverwrite(legacy, current) {
31652
+ const data = { ...current };
31653
+ const migratedProviderIds = [];
31654
+ const skippedProviderIds = [];
31655
+ for (const [providerId, entry] of Object.entries(legacy)) {
31656
+ if (Object.prototype.hasOwnProperty.call(data, providerId)) {
31657
+ skippedProviderIds.push(providerId);
31658
+ continue;
31659
+ }
31660
+ data[providerId] = entry;
31661
+ migratedProviderIds.push(providerId);
31662
+ }
31663
+ return {
31664
+ data,
31665
+ migratedProviderIds,
31666
+ skippedProviderIds
31667
+ };
31668
+ }
31669
+ async function migrateLegacyAuthStore(options = {}) {
31670
+ const legacyPath = options.legacyPath ?? LEGACY_AUTH_PATH;
31671
+ const currentPath = options.currentPath ?? CURRENT_AUTH_PATH;
31672
+ if (!fs5.existsSync(legacyPath)) {
31673
+ return createMigrationResult({
31674
+ legacyPath,
31675
+ currentPath,
31676
+ status: "legacy-missing",
31677
+ migratedProviderIds: [],
31678
+ skippedProviderIds: [],
31679
+ shouldPrintNotice: false
31680
+ });
31681
+ }
31682
+ const legacy = readAuthStoreFile(legacyPath);
31683
+ const current = fs5.existsSync(currentPath) ? readAuthStoreFile(currentPath) : {};
31684
+ const merged = mergeAuthStoresNoOverwrite(legacy, current);
31685
+ if (merged.migratedProviderIds.length > 0) {
31686
+ const dir = path93.dirname(currentPath);
31687
+ ensureSafeDirectory(dir);
31688
+ writeAtomicSync(currentPath, `${JSON.stringify(merged.data, null, 2)}
31689
+ `, {
31690
+ mode: SECURE_FILE_MODE,
31691
+ fsync: true
31692
+ });
31693
+ }
31694
+ return createMigrationResult({
31695
+ legacyPath,
31696
+ currentPath,
31697
+ status: merged.migratedProviderIds.length > 0 ? "migrated" : "unchanged",
31698
+ migratedProviderIds: merged.migratedProviderIds,
31699
+ skippedProviderIds: merged.skippedProviderIds,
31700
+ shouldPrintNotice: options.notify !== false && merged.migratedProviderIds.length > 0
31701
+ });
31702
+ }
31703
+ function createMigrationResult(input) {
31704
+ return {
31705
+ legacyPath: input.legacyPath,
31706
+ currentPath: input.currentPath,
31707
+ migratedCount: input.migratedProviderIds.length,
31708
+ skippedCount: input.skippedProviderIds.length,
31709
+ migratedProviderIds: input.migratedProviderIds,
31710
+ skippedProviderIds: input.skippedProviderIds,
31711
+ shouldPrintNotice: input.shouldPrintNotice,
31712
+ status: input.status
31713
+ };
31714
+ }
31715
+ var DEFAULT_AUTH_VALIDATION_TIMEOUT_MS = 5e3;
31716
+ async function validateAnthropicCompatibleApiKey(request) {
31717
+ const controller = new AbortController();
31718
+ const timeout = setTimeout(() => {
31719
+ controller.abort();
31720
+ }, request.timeoutMs ?? DEFAULT_AUTH_VALIDATION_TIMEOUT_MS);
31721
+ try {
31722
+ const response = await fetch(buildMessagesUrl(request.baseUrl), {
31723
+ method: "POST",
31724
+ headers: {
31725
+ "anthropic-version": "2023-06-01",
31726
+ "content-type": "application/json",
31727
+ "x-api-key": request.apiKey
31728
+ },
31729
+ body: JSON.stringify({
31730
+ model: "claude-3-5-haiku-20241022",
31731
+ max_tokens: 1,
31732
+ messages: [
31733
+ {
31734
+ role: "user",
31735
+ content: "ping"
31736
+ }
31737
+ ]
31738
+ }),
31739
+ signal: controller.signal
31740
+ });
31741
+ if (response.ok) {
31742
+ return { providerId: request.providerId, status: "success" };
31743
+ }
31744
+ if (response.status === 401) {
31745
+ return { providerId: request.providerId, status: "unauthorized" };
31746
+ }
31747
+ if (response.status === 403) {
31748
+ return { providerId: request.providerId, status: "forbidden" };
31749
+ }
31750
+ if (response.status >= 500) {
31751
+ return {
31752
+ providerId: request.providerId,
31753
+ status: "server",
31754
+ detail: `HTTP ${response.status}`
31755
+ };
31756
+ }
31757
+ return {
31758
+ providerId: request.providerId,
31759
+ status: "unknown",
31760
+ detail: `HTTP ${response.status}`
31761
+ };
31762
+ } catch (error51) {
31763
+ if (isAbortError(error51)) {
31764
+ return { providerId: request.providerId, status: "timeout" };
31765
+ }
31766
+ return {
31767
+ providerId: request.providerId,
31768
+ status: "network",
31769
+ detail: error51 instanceof Error ? error51.message : String(error51)
31770
+ };
31771
+ } finally {
31772
+ clearTimeout(timeout);
31773
+ }
31774
+ }
31775
+ function isAuthValidationSuccess(result) {
31776
+ return result.status === "success";
31777
+ }
31778
+ function createAuthValidationError(result) {
31779
+ return new Error(formatAuthValidationFailureMessage(result));
31780
+ }
31781
+ function buildMessagesUrl(baseUrl) {
31782
+ return `${baseUrl.replace(/\/+$/, "")}/v1/messages`;
31783
+ }
31784
+ function isAbortError(error51) {
31785
+ return error51 instanceof DOMException && error51.name === "AbortError";
31786
+ }
31787
+ var CLAUDE_ZAI_PROVIDER_ID = "Claude Code with Z.AI GLM";
31788
+ var CLAUDE_KIMI_PROVIDER_ID = "Claude Code with Moonshot Kimi";
31789
+ var AUTH_PROVIDER_DEFINITIONS = {
31790
+ "claude-zai": {
31791
+ providerId: CLAUDE_ZAI_PROVIDER_ID,
31792
+ baseUrl: CLI_BACKENDS["claude-zai"].defaultEnv.ANTHROPIC_BASE_URL,
31793
+ env: CLI_BACKENDS["claude-zai"].defaultEnv
31794
+ },
31795
+ "claude-kimi": {
31796
+ providerId: CLAUDE_KIMI_PROVIDER_ID,
31797
+ baseUrl: CLI_BACKENDS["claude-kimi"].defaultEnv.ANTHROPIC_BASE_URL,
31798
+ env: CLI_BACKENDS["claude-kimi"].defaultEnv
31799
+ }
31800
+ };
31801
+ async function resolveAuthEnv(cli, deps) {
31802
+ const provider = AUTH_PROVIDER_DEFINITIONS[cli];
31803
+ if (!provider) return {};
31804
+ await migrateLegacyAuthStore();
31805
+ const auth = deps?.authService ?? createAuthService({ authPath: DEFAULT_AUTH_PATH });
31806
+ const token = await auth.getApiKey(provider.providerId);
31807
+ if (!token) {
31808
+ throw new Error(formatMissingAuthKeyMessage({ cli, providerId: provider.providerId }));
31809
+ }
31810
+ const validation = await validateAnthropicCompatibleApiKey({
31811
+ providerId: provider.providerId,
31812
+ apiKey: token,
31813
+ baseUrl: provider.baseUrl
31814
+ });
31815
+ if (!isAuthValidationSuccess(validation)) {
31816
+ const failure = {
31817
+ providerId: validation.providerId,
31818
+ status: validation.status,
31819
+ detail: validation.detail
31820
+ };
31821
+ throw createAuthValidationError(failure);
31822
+ }
31823
+ return { ...provider.env, ANTHROPIC_AUTH_TOKEN: token };
31824
+ }
31825
+ var GLOBAL_OPTIONS_VERSION = 1;
31826
+ var GLOBAL_OPTIONS_FILE_NAME = "settings.json";
31827
+ var LOCK_DIR_NAME = "settings.json.lock";
31828
+ var LOCK_OWNER_FILE_NAME = "owner";
31829
+ var TEMP_FILE_PREFIX = `.tmp-${GLOBAL_OPTIONS_FILE_NAME}-`;
31830
+ function createGlobalOptionsStore(deps = {}) {
31831
+ const dataDir = deps.dataDir ?? getFleetDataDir();
31832
+ const optionsPath = path93.join(dataDir, GLOBAL_OPTIONS_FILE_NAME);
31833
+ const lockDir = path93.join(dataDir, LOCK_DIR_NAME);
31834
+ const store2 = createDurableJsonStore({
31835
+ filePath: optionsPath,
31836
+ lockDir,
31837
+ lockOwnerFileName: LOCK_OWNER_FILE_NAME,
31838
+ sanitize: (value) => sanitizeGlobalOptionsData(value).data,
31839
+ sensitivity: "sensitive",
31840
+ timeoutMs: deps.timeoutMs,
31841
+ staleLockMs: deps.staleLockMs,
31842
+ tempCleanupPrefix: TEMP_FILE_PREFIX,
31843
+ now: deps.now
31844
+ });
31845
+ return {
31846
+ path: optionsPath,
31847
+ load: () => store2.load(),
31848
+ save: (data) => store2.save(sanitizeGlobalOptionsData(data).data),
31849
+ update: (mutate) => store2.update((current) => sanitizeGlobalOptionsData(mutate(current)).data)
31850
+ };
31851
+ }
31852
+ function createEmptyGlobalOptionsData() {
31853
+ return {
31854
+ version: GLOBAL_OPTIONS_VERSION
31855
+ };
31856
+ }
31857
+ function sanitizeGlobalOptionsData(value) {
31858
+ if (!isRecord3(value)) {
31859
+ return { data: createEmptyGlobalOptionsData(), changed: true };
31860
+ }
31861
+ if (value.version !== GLOBAL_OPTIONS_VERSION) {
31862
+ return { data: createEmptyGlobalOptionsData(), changed: true };
31863
+ }
31864
+ const data = {
31865
+ version: GLOBAL_OPTIONS_VERSION,
31866
+ ...typeof value.replaceSystemPrompt === "boolean" ? { replaceSystemPrompt: value.replaceSystemPrompt } : {},
31867
+ ...typeof value.enableMetaphor === "boolean" ? { enableMetaphor: value.enableMetaphor } : {}
31868
+ };
31869
+ const allowedKeys = /* @__PURE__ */ new Set(["version", "replaceSystemPrompt", "enableMetaphor"]);
31870
+ const changed = Object.keys(value).some((key) => !allowedKeys.has(key)) || "replaceSystemPrompt" in value && typeof value.replaceSystemPrompt !== "boolean" || "enableMetaphor" in value && typeof value.enableMetaphor !== "boolean";
31871
+ return { data, changed };
31872
+ }
31873
+ function isRecord3(value) {
31874
+ return typeof value === "object" && value !== null && !Array.isArray(value);
31875
+ }
31876
+ function createGlobalOptionsService(deps = {}) {
31877
+ const store2 = deps.store ?? createGlobalOptionsStore({ dataDir: deps.dataDir });
31878
+ return {
31879
+ load: () => store2.load(),
31880
+ save: (data) => {
31881
+ store2.save(data);
31882
+ return store2.load();
31883
+ },
31884
+ update: (mutate) => updateGlobalOptions(store2, mutate)
31885
+ };
31886
+ }
31887
+ function updateGlobalOptions(store2, mutate) {
31888
+ return store2.update(mutate);
31889
+ }
31890
+
31891
+ // ../../packages/fleet-infra/dist/index.js
31892
+ function createInfraServices(deps = {}) {
31893
+ const globalOptionsService = createGlobalOptionsService();
31894
+ const authService = createAuthService({ authPath: deps.authPath ?? DEFAULT_AUTH_PATH });
31895
+ return {
31896
+ authService,
31897
+ globalOptionsService
31898
+ };
31899
+ }
31512
31900
  function readFleetConsoleRelease() {
31513
31901
  const requireFromHere = createRequire(import.meta.url);
31514
31902
  const packageJsonPath = requireFromHere.resolve("../package.json");
@@ -31522,17 +31910,34 @@ function readFleetConsoleRelease() {
31522
31910
  }
31523
31911
 
31524
31912
  // src/paths.ts
31525
- var LOCK_DIR_NAME = "fleet-console";
31913
+ var LOCK_DIR_NAME2 = "fleet-console";
31914
+ var CONSOLE_RUNTIME_DIR_NAME = "console";
31526
31915
  var LOCK_FILE_NAME = "console.lock";
31916
+ var CONSOLE_DATA_DIR_NAME = "console";
31917
+ var CONSOLE_STATE_FILE_NAME = "state.json";
31918
+ var CONSOLE_CAPTURES_DIR_NAME = "captures";
31527
31919
  function createConsolePaths(deps = {}) {
31528
31920
  const env = deps.env ?? process.env;
31529
31921
  const base = env.FLEET_CONSOLE_DIR ?? defaultConsoleBaseDir(deps);
31530
31922
  return { dir: base, lockFile: path93__default.join(base, LOCK_FILE_NAME) };
31531
31923
  }
31924
+ function createConsoleDataPaths(deps = {}) {
31925
+ const dir = path93__default.join(deps.fleetDataDir ?? getFleetDataDir(), CONSOLE_DATA_DIR_NAME);
31926
+ return {
31927
+ dir,
31928
+ stateFile: path93__default.join(dir, CONSOLE_STATE_FILE_NAME),
31929
+ capturesDir: path93__default.join(dir, CONSOLE_CAPTURES_DIR_NAME)
31930
+ };
31931
+ }
31532
31932
  function defaultConsoleBaseDir(deps) {
31933
+ let release;
31934
+ const channel = deps.channel ?? (release = readFleetConsoleRelease()).channel;
31935
+ if (channel === "local") {
31936
+ const packageRoot = deps.packageRoot ?? (release ??= readFleetConsoleRelease()).packageRoot;
31937
+ return path93__default.join(path93__default.resolve(packageRoot, "..", ".."), ".fleet", CONSOLE_RUNTIME_DIR_NAME);
31938
+ }
31533
31939
  const uid = deps.uid ?? (typeof process.getuid === "function" ? process.getuid() : 0);
31534
- const channel = deps.channel ?? readFleetConsoleRelease().channel;
31535
- return path93__default.join(os22__default.tmpdir(), `${LOCK_DIR_NAME}-${uid}-${channel}`);
31940
+ return path93__default.join(os22__default.tmpdir(), `${LOCK_DIR_NAME2}-${uid}-${channel}`);
31536
31941
  }
31537
31942
  var FLEET_PROTOCOL_GATE_PROMPT = String.raw`# Protocol Gate
31538
31943
 
@@ -31925,6 +32330,8 @@ function createClaudeFamilyCliDefinition(options) {
31925
32330
  lineTerminator: "\r",
31926
32331
  multilineStrategy: "paste-mode"
31927
32332
  },
32333
+ // Claude Code 계열은 세션 이름 변경 슬래시 명령 `/rename`을 지원한다.
32334
+ renameCommand: "/rename",
31928
32335
  terminalName: "xterm-256color"
31929
32336
  };
31930
32337
  }
@@ -31948,7 +32355,7 @@ var codexCli = {
31948
32355
  async createProfile(options) {
31949
32356
  const { bin, prefixArgs } = resolveBinary("codex", "CODEX_BIN", options.env);
31950
32357
  return {
31951
- args: [...prefixArgs, "--no-alt-screen", ...buildModelArgs2(options.model)],
32358
+ args: [...prefixArgs, ...buildResumeArgs(options.resumeSessionId), "--no-alt-screen", ...buildModelArgs2(options.model)],
31952
32359
  bin,
31953
32360
  binPrefixArgs: prefixArgs,
31954
32361
  cwd: options.cwd,
@@ -31960,10 +32367,15 @@ var codexCli = {
31960
32367
  lineTerminator: "\r",
31961
32368
  multilineStrategy: "paste-mode"
31962
32369
  },
32370
+ // Codex CLI도 세션 이름 변경 슬래시 명령 `/rename`을 지원한다.
32371
+ renameCommand: "/rename",
31963
32372
  terminalName: "xterm-256color"
31964
32373
  };
31965
32374
  }
31966
32375
  };
32376
+ function buildResumeArgs(resumeSessionId) {
32377
+ return resumeSessionId === void 0 ? [] : ["resume", resumeSessionId];
32378
+ }
31967
32379
  function buildModelArgs2(model) {
31968
32380
  return model === void 0 ? [] : ["--model", model];
31969
32381
  }
@@ -31980,7 +32392,8 @@ async function resolveAgentCliProfile(env, cwd, options = {}) {
31980
32392
  authService: options.authService,
31981
32393
  cwd,
31982
32394
  env,
31983
- model: options.model
32395
+ model: options.model,
32396
+ resumeSessionId: options.resumeSessionId
31984
32397
  });
31985
32398
  }
31986
32399
  function resolveAgentCliId(env, options = {}) {
@@ -32024,6 +32437,20 @@ var AGENT_CLI_INJECTION_CAPABILITIES = {
32024
32437
  function getAgentCliInjectionCapability(cliId) {
32025
32438
  return AGENT_CLI_INJECTION_CAPABILITIES[cliId];
32026
32439
  }
32440
+ function createSessionCaptureHookExec(deps) {
32441
+ const args = deps.tsxLoader ? [
32442
+ "--import",
32443
+ pathToFileURL(deps.tsxLoader).href,
32444
+ deps.entryPath,
32445
+ "hook",
32446
+ "capture-session",
32447
+ deps.provider
32448
+ ] : [deps.entryPath, "hook", "capture-session", deps.provider];
32449
+ return {
32450
+ command: deps.execPath ?? process5.execPath,
32451
+ args
32452
+ };
32453
+ }
32027
32454
  function buildHostCarrierModelDefaults2(config2) {
32028
32455
  const cliType = resolveAgentCliType2(config2.id, config2.defaultCliType);
32029
32456
  const cliDefaults = buildCarrierModelDefaults(config2, cliType);
@@ -32053,6 +32480,7 @@ function buildClaudeNativeSubagentPlan2(registry3) {
32053
32480
  function buildClaudeNativeArgs(context) {
32054
32481
  const systemPromptArg = context.replaceSystemPrompt ? "--system-prompt-file" : "--append-system-prompt-file";
32055
32482
  return [
32483
+ ...buildResumeArgs2(context.resumeSessionId),
32056
32484
  systemPromptArg,
32057
32485
  requireSystemPromptFile(context),
32058
32486
  ...context.pluginRoots.flatMap((pluginRoot) => [
@@ -32063,6 +32491,9 @@ function buildClaudeNativeArgs(context) {
32063
32491
  "--dangerously-skip-permissions"
32064
32492
  ];
32065
32493
  }
32494
+ function buildResumeArgs2(resumeSessionId) {
32495
+ return resumeSessionId === void 0 ? [] : ["--resume", resumeSessionId];
32496
+ }
32066
32497
  function buildClaudeMcpConfig(servers) {
32067
32498
  return JSON.stringify({
32068
32499
  mcpServers: Object.fromEntries(
@@ -32120,20 +32551,32 @@ function escapeTomlMultilineString(value) {
32120
32551
  });
32121
32552
  return result;
32122
32553
  }
32554
+ function buildPosixShellCommand(values) {
32555
+ return values.map(posixShellQuote).join(" ");
32556
+ }
32557
+ function posixShellQuote(value) {
32558
+ return `'${value.replaceAll("'", "'\\''")}'`;
32559
+ }
32123
32560
  var CODEX_TOOL_TIMEOUT_SEC = 1800;
32124
32561
  function buildCodexNativeArgs(context) {
32125
32562
  const profileName = requireCodexProfileName(context);
32126
32563
  const args = [
32564
+ ...buildResumeArgs3(context.resumeSessionId),
32127
32565
  "--enable",
32128
32566
  "plugins",
32129
32567
  "--enable",
32130
32568
  "child_agents_md",
32569
+ "--enable",
32570
+ "hooks",
32131
32571
  "--profile",
32132
32572
  profileName,
32133
32573
  "-c",
32134
32574
  'approval_policy="never"',
32135
32575
  "-c",
32136
- 'sandbox_mode="danger-full-access"'
32576
+ 'sandbox_mode="danger-full-access"',
32577
+ // hook 신뢰 프롬프트("Hooks need review")를 fleet 관리 세션에서 건너뛴다. bypass_hook_trust는
32578
+ // config 키가 아니라 전용 CLI 플래그이므로 -c override가 아닌 플래그로 전달해야 한다.
32579
+ "--dangerously-bypass-hook-trust"
32137
32580
  ];
32138
32581
  for (const server of context.mcpServers) {
32139
32582
  const prefix = `mcp_servers.${server.name}`;
@@ -32149,6 +32592,9 @@ function buildCodexNativeArgs(context) {
32149
32592
  }
32150
32593
  return args;
32151
32594
  }
32595
+ function buildResumeArgs3(resumeSessionId) {
32596
+ return resumeSessionId === void 0 ? [] : ["resume", resumeSessionId];
32597
+ }
32152
32598
  function requireCodexProfileName(context) {
32153
32599
  if (context.codexProfileName) return context.codexProfileName;
32154
32600
  throw new Error("Codex profile name is required for native injection");
@@ -32359,20 +32805,32 @@ function claudeHooks(options) {
32359
32805
  if (!hookExec) {
32360
32806
  throw new Error("Fleet Claude session hook command is required");
32361
32807
  }
32808
+ const captureSessionHookExec = options.captureSessionHookExec;
32362
32809
  return {
32363
32810
  hooks: {
32364
32811
  SessionStart: [{
32365
- hooks: [{
32366
- // exec 형식: command는 직접 spawn되는 실행 파일, args는 셸 토크나이징 없이 그대로 전달된다.
32367
- // Windows cmd/powershell의 따옴표 규칙과 무관하게 동작하며 공백 포함 경로도 안전하다.
32368
- args: [...hookExec.args],
32369
- command: hookExec.command,
32370
- type: "command"
32812
+ hooks: [claudeCommandHook(hookExec)]
32813
+ }],
32814
+ ...captureSessionHookExec ? {
32815
+ UserPromptSubmit: [{
32816
+ hooks: [claudeCommandHook(captureSessionHookExec)]
32371
32817
  }]
32372
- }]
32818
+ } : {}
32373
32819
  }
32374
32820
  };
32375
32821
  }
32822
+ function claudeCommandHook(hookExec) {
32823
+ if (!hookExec) {
32824
+ throw new Error("Fleet Claude session hook command is required");
32825
+ }
32826
+ return {
32827
+ // exec 형식: command는 직접 spawn되는 실행 파일, args는 셸 토크나이징 없이 그대로 전달된다.
32828
+ // Windows cmd/powershell의 따옴표 규칙과 무관하게 동작하며 공백 포함 경로도 안전하다.
32829
+ args: [...hookExec.args],
32830
+ command: hookExec.command,
32831
+ type: "command"
32832
+ };
32833
+ }
32376
32834
  function claudeAgentFile(subagent) {
32377
32835
  const frontmatter = [
32378
32836
  "---",
@@ -32882,7 +33340,7 @@ function homeMarketplaceTarget(fleetRoot) {
32882
33340
  }
32883
33341
  function projectMarketplaceTarget(cwd) {
32884
33342
  const projectFleetRoot = resolveProjectFleetRoot(cwd);
32885
- const hash2 = crypto3.createHash("sha256").update(path93__default.resolve(cwd, ".fleet")).digest("hex").slice(0, 12);
33343
+ const hash2 = crypto5.createHash("sha256").update(path93__default.resolve(cwd, ".fleet")).digest("hex").slice(0, 12);
32886
33344
  return {
32887
33345
  flat: true,
32888
33346
  name: `${FLEET_PROJECT_MARKETPLACE_NAME_PREFIX}-${hash2}`,
@@ -32908,7 +33366,6 @@ function renderPluginRoot(pluginRoot, bundle, options, fleetRoot) {
32908
33366
  const stagedPluginRoot = path93__default.join(stageParent, path93__default.basename(pluginRoot));
32909
33367
  try {
32910
33368
  ensurePrivateDir(stagedPluginRoot, stagedPluginRoot);
32911
- writePrivateJson(path93__default.join(stagedPluginRoot, ".codex-plugin", "plugin.json"), codexManifest(bundle), stagedPluginRoot);
32912
33369
  writePrivateJson(path93__default.join(stagedPluginRoot, ".claude-plugin", "plugin.json"), claudeManifest(bundle), stagedPluginRoot);
32913
33370
  switch (bundle.source) {
32914
33371
  case "asset":
@@ -32923,6 +33380,7 @@ function renderPluginRoot(pluginRoot, bundle, options, fleetRoot) {
32923
33380
  renderGlobalPluginRoot(stagedPluginRoot, fleetRoot);
32924
33381
  break;
32925
33382
  }
33383
+ writePrivateJson(path93__default.join(stagedPluginRoot, ".codex-plugin", "plugin.json"), codexManifest(bundle, stagedPluginRoot), stagedPluginRoot);
32926
33384
  removePrivatePath(pluginRoot, parentRoot);
32927
33385
  renameSync(stagedPluginRoot, pluginRoot);
32928
33386
  } finally {
@@ -32951,7 +33409,7 @@ function pruneMarketplaceRoot(target) {
32951
33409
  }
32952
33410
  }
32953
33411
  function buildContentHash(target) {
32954
- const hash2 = crypto3.createHash("sha256");
33412
+ const hash2 = crypto5.createHash("sha256");
32955
33413
  for (const filePath of listRenderableFiles(target)) {
32956
33414
  const relativePath2 = path93__default.relative(target.root, filePath);
32957
33415
  hash2.update(relativePath2);
@@ -33054,14 +33512,36 @@ function marketplacePluginPath(target, bundle) {
33054
33512
  if (target.flat) return `./${FLAT_PLUGIN_DIR_NAME}`;
33055
33513
  return `./${PLUGIN_BUNDLES_DIR_NAME}/${bundle.directoryName}`;
33056
33514
  }
33057
- function codexManifest(bundle) {
33515
+ function codexManifest(bundle, pluginRoot) {
33058
33516
  return {
33059
33517
  name: bundle.name,
33060
- version: "0.0.0",
33518
+ version: codexManifestVersion(bundle, pluginRoot),
33061
33519
  description: bundle.description,
33062
33520
  skills: "./skills/"
33063
33521
  };
33064
33522
  }
33523
+ function codexManifestVersion(bundle, pluginRoot) {
33524
+ const hash2 = crypto5.createHash("sha256");
33525
+ hash2.update(bundle.name);
33526
+ hash2.update("\0");
33527
+ hash2.update(bundle.description);
33528
+ hash2.update("\0");
33529
+ for (const filePath of listCodexEffectivePluginFiles(pluginRoot)) {
33530
+ const relativePath2 = path93__default.relative(pluginRoot, filePath);
33531
+ hash2.update(relativePath2);
33532
+ hash2.update("\0");
33533
+ hash2.update(readFileSync(filePath));
33534
+ hash2.update("\0");
33535
+ }
33536
+ return `0.0.0+${hash2.digest("hex").slice(0, 12)}`;
33537
+ }
33538
+ function listCodexEffectivePluginFiles(pluginRoot) {
33539
+ const skillsPath = path93__default.join(pluginRoot, "skills");
33540
+ if (!existsSync(skillsPath)) return [];
33541
+ const files = [];
33542
+ collectRenderableFiles(pluginRoot, skillsPath, files, /* @__PURE__ */ new Set());
33543
+ return files.sort();
33544
+ }
33065
33545
  function claudeManifest(bundle) {
33066
33546
  return {
33067
33547
  name: bundle.name,
@@ -33081,7 +33561,7 @@ async function injectAgentCliProfile(profile, options) {
33081
33561
  const injectTone = options.enableMetaphor ?? false;
33082
33562
  const endpoint = await options.dedicatedMcpSession.getEndpoint();
33083
33563
  const startupDefinitions = buildStartupNativeDefinitions(profile.id, options.carrierRuntime);
33084
- const tokenLabel = options.mcpSessionLabel ?? `agent:${profile.id}:${crypto3.randomUUID()}`;
33564
+ const tokenLabel = options.mcpSessionLabel ?? `agent:${profile.id}:${crypto5.randomUUID()}`;
33085
33565
  const tokens = await options.dedicatedMcpSession.issueSessionToken({ cwd: profile.cwd, label: tokenLabel });
33086
33566
  const mcpServers = buildAgentCliMcpServerConfigs(endpoint.servers, tokens);
33087
33567
  const doctrine = options.buildSystemPrompt(injectTone);
@@ -33095,11 +33575,12 @@ async function injectAgentCliProfile(profile, options) {
33095
33575
  cwd: profile.cwd,
33096
33576
  dataDir: options.dataDir,
33097
33577
  rootDir: options.pluginRootDir,
33578
+ captureSessionHookExec: options.captureSessionHookExec,
33098
33579
  hookExec: startupDefinitions.host === "claude" ? requireHookExec(options.hookExec) : void 0,
33099
33580
  withMarketplaceLock: options.withMarketplaceLock
33100
33581
  });
33101
33582
  const codexPluginKeys = plugin.codexRegistrations.map((registration) => `${registration.pluginName}@${registration.marketplaceName}`);
33102
- const codexProfile = profile.id === "codex" ? writeCodexFleetProfile(profile.env, doctrine, codexPluginKeys, (cleanup2) => tempCleanups.push(cleanup2)) : void 0;
33583
+ const codexProfile = profile.id === "codex" ? writeCodexFleetProfile(profile.env, doctrine, codexPluginKeys, options.captureSessionHookExec, (cleanup2) => tempCleanups.push(cleanup2)) : void 0;
33103
33584
  const launchWarnings = [];
33104
33585
  for (const registration of plugin.codexRegistrations) {
33105
33586
  const registrationWarning = await ensureCodexPluginRegistered(registration, {
@@ -33126,13 +33607,14 @@ async function injectAgentCliProfile(profile, options) {
33126
33607
  pluginRoot: plugin.pluginRoot,
33127
33608
  pluginRoots: plugin.pluginRoots,
33128
33609
  codexProfileName: codexProfile?.profileName,
33129
- replaceSystemPrompt: options.replaceSystemPrompt ?? true,
33610
+ replaceSystemPrompt: options.replaceSystemPrompt ?? false,
33611
+ resumeSessionId: options.resumeSessionId,
33130
33612
  systemPromptFile
33131
33613
  };
33132
33614
  const injectedArgs = buildAgentCliArgs(capability.builderId, context);
33133
33615
  return {
33134
33616
  ...profile,
33135
- args: [...profile.args, ...injectedArgs],
33617
+ args: mergeAgentCliArgs(profile, capability.builderId, context, injectedArgs),
33136
33618
  cleanup,
33137
33619
  launchWarnings: [...profile.launchWarnings ?? [], ...launchWarnings]
33138
33620
  };
@@ -33174,11 +33656,11 @@ function writeSystemPromptFile(cliId, systemPrompt, onCleanup) {
33174
33656
  chmodBestEffort2(filePath, SYSTEM_PROMPT_FILE_MODE);
33175
33657
  return filePath;
33176
33658
  }
33177
- function writeCodexFleetProfile(env, doctrine, pluginKeys, onCleanup) {
33659
+ function writeCodexFleetProfile(env, doctrine, pluginKeys, captureSessionHookExec, onCleanup) {
33178
33660
  const codexHome = env.CODEX_HOME ?? path93__default.join(env.HOME ?? os22__default.homedir(), ".codex");
33179
33661
  mkdirSync(codexHome, { recursive: true });
33180
33662
  pruneStaleCodexFleetProfiles(codexHome);
33181
- const profileName = `fleet-${crypto3.randomUUID()}`;
33663
+ const profileName = `fleet-${crypto5.randomUUID()}`;
33182
33664
  const profilePath = path93__default.join(codexHome, `${profileName}.config.toml`);
33183
33665
  onCleanup(() => rmBestEffort(profilePath));
33184
33666
  writeFileNoFollow2(profilePath, [
@@ -33193,11 +33675,21 @@ function writeCodexFleetProfile(env, doctrine, pluginKeys, onCleanup) {
33193
33675
  `[plugins."${escapeTomlBasicString2(pluginKey)}"]`,
33194
33676
  "enabled = true",
33195
33677
  ""
33196
- ])
33678
+ ]),
33679
+ ...codexCaptureHookConfig(captureSessionHookExec)
33197
33680
  ].join("\n"));
33198
33681
  chmodBestEffort2(profilePath, SYSTEM_PROMPT_FILE_MODE);
33199
33682
  return { profileName, profilePath };
33200
33683
  }
33684
+ function codexCaptureHookConfig(captureSessionHookExec) {
33685
+ if (captureSessionHookExec === void 0) return [];
33686
+ const command2 = buildPosixShellCommand([captureSessionHookExec.command, ...captureSessionHookExec.args]);
33687
+ return [
33688
+ "[hooks]",
33689
+ `UserPromptSubmit = [{ hooks = [{ type = "command", command = "${escapeTomlBasicString2(command2)}" }] }]`,
33690
+ ""
33691
+ ];
33692
+ }
33201
33693
  function writeFileNoFollow2(filePath, content) {
33202
33694
  const flags = constants.O_WRONLY | constants.O_CREAT | constants.O_TRUNC | constants.O_NOFOLLOW;
33203
33695
  const fd = openSync(filePath, flags, SYSTEM_PROMPT_FILE_MODE);
@@ -33270,6 +33762,24 @@ function buildAgentCliArgs(builderId, context) {
33270
33762
  return buildCodexNativeArgs(context);
33271
33763
  }
33272
33764
  }
33765
+ function mergeAgentCliArgs(profile, builderId, context, injectedArgs) {
33766
+ if (builderId !== "codex-native" || context.resumeSessionId === void 0) {
33767
+ return [...profile.args, ...injectedArgs];
33768
+ }
33769
+ const resumeSessionId = context.resumeSessionId;
33770
+ const prefixLength = profile.binPrefixArgs?.length ?? 0;
33771
+ const codexResumeArgs = ["resume", resumeSessionId];
33772
+ const injectedTail = injectedArgs.slice(codexResumeArgs.length);
33773
+ return [
33774
+ ...profile.args.slice(0, prefixLength),
33775
+ ...codexResumeArgs,
33776
+ ...profile.args.slice(prefixLength).filter((arg, index, args) => !isCodexResumeArgAt(args, index, resumeSessionId)),
33777
+ ...injectedTail
33778
+ ];
33779
+ }
33780
+ function isCodexResumeArgAt(args, index, resumeSessionId) {
33781
+ return args[index] === "resume" && args[index + 1] === resumeSessionId || args[index] === resumeSessionId && args[index - 1] === "resume";
33782
+ }
33273
33783
  function createOnceCleanup(cleanup) {
33274
33784
  let cleaned = false;
33275
33785
  return () => {
@@ -33453,389 +33963,6 @@ function applyMessagePolicy(text, policy) {
33453
33963
  submit: policy.lineTerminator
33454
33964
  };
33455
33965
  }
33456
- var FLEET_DATA_DIR_NAME = ".fleet";
33457
- function getFleetDataDir() {
33458
- return path93.join(os22.homedir(), FLEET_DATA_DIR_NAME);
33459
- }
33460
- function readAuthStoreFile(filePath) {
33461
- let fd;
33462
- try {
33463
- fd = fs5.openSync(filePath, fs5.constants.O_RDONLY | NOFOLLOW_FLAG);
33464
- if (!fs5.fstatSync(fd).isFile()) {
33465
- return {};
33466
- }
33467
- return JSON.parse(fs5.readFileSync(fd, "utf-8"));
33468
- } catch {
33469
- return {};
33470
- } finally {
33471
- if (fd !== void 0) {
33472
- try {
33473
- fs5.closeSync(fd);
33474
- } catch {
33475
- }
33476
- }
33477
- }
33478
- }
33479
- var DEFAULT_AUTH_PATH = path93.join(getFleetDataDir(), "auth.json");
33480
- var AUTH_LOCK_OWNER_FILE_NAME = "owner.json";
33481
- var AUTH_LOCK_TIMEOUT_MS = 5e3;
33482
- function createAuthService(deps = {}) {
33483
- const authPath = deps.authPath ?? DEFAULT_AUTH_PATH;
33484
- const lockDir = `${authPath}.lock`;
33485
- return {
33486
- // [HIGH #1] TOCTOU 수정: existsSync/read/check/delete/write 전체를 락 블록 안으로 이동
33487
- async deleteApiKey(providerId) {
33488
- let deleted = false;
33489
- withAuthLock(authPath, lockDir, () => {
33490
- if (!fs5.existsSync(authPath)) return;
33491
- const data = readAuthStoreFile(authPath);
33492
- if (!Object.prototype.hasOwnProperty.call(data, providerId)) return;
33493
- delete data[providerId];
33494
- writeAuthData(authPath, data);
33495
- deleted = true;
33496
- });
33497
- return deleted;
33498
- },
33499
- // [HIGH #2] 읽기 경로: fd 기반 O_RDONLY|O_NOFOLLOW+fstatSync isFile
33500
- async getApiKey(providerId) {
33501
- let fd;
33502
- try {
33503
- if (!fs5.existsSync(authPath)) {
33504
- return void 0;
33505
- }
33506
- fd = fs5.openSync(authPath, fs5.constants.O_RDONLY | NOFOLLOW_FLAG);
33507
- if (!fs5.fstatSync(fd).isFile()) {
33508
- return void 0;
33509
- }
33510
- const data = JSON.parse(fs5.readFileSync(fd, "utf-8"));
33511
- return typeof data[providerId]?.key === "string" ? data[providerId].key : void 0;
33512
- } catch {
33513
- return void 0;
33514
- } finally {
33515
- if (fd !== void 0) {
33516
- try {
33517
- fs5.closeSync(fd);
33518
- } catch {
33519
- }
33520
- }
33521
- }
33522
- },
33523
- // [HIGH #2] 읽기 경로: fd 기반 O_RDONLY|O_NOFOLLOW 통일
33524
- async listProviderIds() {
33525
- if (!fs5.existsSync(authPath)) {
33526
- return [];
33527
- }
33528
- try {
33529
- return Object.keys(readAuthStoreFile(authPath)).sort();
33530
- } catch {
33531
- return [];
33532
- }
33533
- },
33534
- async setApiKey(providerId, key) {
33535
- const dir = path93.dirname(authPath);
33536
- ensureSafeDirectory(dir);
33537
- withAuthLock(authPath, lockDir, () => {
33538
- const data = fs5.existsSync(authPath) ? readAuthStoreFile(authPath) : {};
33539
- data[providerId] = {
33540
- ...data[providerId] ?? {},
33541
- key
33542
- };
33543
- writeAuthData(authPath, data);
33544
- });
33545
- }
33546
- };
33547
- }
33548
- function withAuthLock(authPath, lockDir, operation) {
33549
- return withDirectoryLock(
33550
- {
33551
- lockDir,
33552
- ownerFileName: AUTH_LOCK_OWNER_FILE_NAME,
33553
- timeoutMs: AUTH_LOCK_TIMEOUT_MS
33554
- },
33555
- operation
33556
- );
33557
- }
33558
- function writeAuthData(authPath, data) {
33559
- writeAtomicSync(authPath, `${JSON.stringify(data, null, 2)}
33560
- `, {
33561
- mode: SECURE_FILE_MODE,
33562
- fsync: true
33563
- });
33564
- }
33565
- function formatMissingAuthKeyMessage(input) {
33566
- const cliHint = input.cli ? `cli '${input.cli}'` : "selected CLI";
33567
- return `Auth token not found for ${cliHint} (providerId: '${input.providerId}'). Run \`fleet auth login\` to register one.`;
33568
- }
33569
- function formatAuthValidationFailureMessage(input) {
33570
- const detail = input.detail ? ` Detail: ${input.detail}` : "";
33571
- if (input.status === "unauthorized") {
33572
- return `Auth token was rejected (providerId: '${input.providerId}'). Check the token and try again.${detail}`;
33573
- }
33574
- if (input.status === "forbidden") {
33575
- return `Auth token is not allowed for this provider (providerId: '${input.providerId}'). Check the token permissions.${detail}`;
33576
- }
33577
- if (input.status === "timeout") {
33578
- return `Auth token validation timed out (providerId: '${input.providerId}'). Check the connection and try again.${detail}`;
33579
- }
33580
- if (input.status === "network") {
33581
- return `Auth token validation failed due to a network error (providerId: '${input.providerId}'). Check the connection and try again.${detail}`;
33582
- }
33583
- if (input.status === "server") {
33584
- return `Auth token validation failed because the provider returned an error (providerId: '${input.providerId}'). Try again later.${detail}`;
33585
- }
33586
- return `Auth token validation failed (providerId: '${input.providerId}'). Check the token and try again.${detail}`;
33587
- }
33588
- var LEGACY_AUTH_PATH = path93.join(getFleetDataDir(), "agent", "auth.json");
33589
- var CURRENT_AUTH_PATH = path93.join(getFleetDataDir(), "auth.json");
33590
- function mergeAuthStoresNoOverwrite(legacy, current) {
33591
- const data = { ...current };
33592
- const migratedProviderIds = [];
33593
- const skippedProviderIds = [];
33594
- for (const [providerId, entry] of Object.entries(legacy)) {
33595
- if (Object.prototype.hasOwnProperty.call(data, providerId)) {
33596
- skippedProviderIds.push(providerId);
33597
- continue;
33598
- }
33599
- data[providerId] = entry;
33600
- migratedProviderIds.push(providerId);
33601
- }
33602
- return {
33603
- data,
33604
- migratedProviderIds,
33605
- skippedProviderIds
33606
- };
33607
- }
33608
- async function migrateLegacyAuthStore(options = {}) {
33609
- const legacyPath = options.legacyPath ?? LEGACY_AUTH_PATH;
33610
- const currentPath = options.currentPath ?? CURRENT_AUTH_PATH;
33611
- if (!fs5.existsSync(legacyPath)) {
33612
- return createMigrationResult({
33613
- legacyPath,
33614
- currentPath,
33615
- status: "legacy-missing",
33616
- migratedProviderIds: [],
33617
- skippedProviderIds: [],
33618
- shouldPrintNotice: false
33619
- });
33620
- }
33621
- const legacy = readAuthStoreFile(legacyPath);
33622
- const current = fs5.existsSync(currentPath) ? readAuthStoreFile(currentPath) : {};
33623
- const merged = mergeAuthStoresNoOverwrite(legacy, current);
33624
- if (merged.migratedProviderIds.length > 0) {
33625
- const dir = path93.dirname(currentPath);
33626
- ensureSafeDirectory(dir);
33627
- writeAtomicSync(currentPath, `${JSON.stringify(merged.data, null, 2)}
33628
- `, {
33629
- mode: SECURE_FILE_MODE,
33630
- fsync: true
33631
- });
33632
- }
33633
- return createMigrationResult({
33634
- legacyPath,
33635
- currentPath,
33636
- status: merged.migratedProviderIds.length > 0 ? "migrated" : "unchanged",
33637
- migratedProviderIds: merged.migratedProviderIds,
33638
- skippedProviderIds: merged.skippedProviderIds,
33639
- shouldPrintNotice: options.notify !== false && merged.migratedProviderIds.length > 0
33640
- });
33641
- }
33642
- function createMigrationResult(input) {
33643
- return {
33644
- legacyPath: input.legacyPath,
33645
- currentPath: input.currentPath,
33646
- migratedCount: input.migratedProviderIds.length,
33647
- skippedCount: input.skippedProviderIds.length,
33648
- migratedProviderIds: input.migratedProviderIds,
33649
- skippedProviderIds: input.skippedProviderIds,
33650
- shouldPrintNotice: input.shouldPrintNotice,
33651
- status: input.status
33652
- };
33653
- }
33654
- var DEFAULT_AUTH_VALIDATION_TIMEOUT_MS = 5e3;
33655
- async function validateAnthropicCompatibleApiKey(request) {
33656
- const controller = new AbortController();
33657
- const timeout = setTimeout(() => {
33658
- controller.abort();
33659
- }, request.timeoutMs ?? DEFAULT_AUTH_VALIDATION_TIMEOUT_MS);
33660
- try {
33661
- const response = await fetch(buildMessagesUrl(request.baseUrl), {
33662
- method: "POST",
33663
- headers: {
33664
- "anthropic-version": "2023-06-01",
33665
- "content-type": "application/json",
33666
- "x-api-key": request.apiKey
33667
- },
33668
- body: JSON.stringify({
33669
- model: "claude-3-5-haiku-20241022",
33670
- max_tokens: 1,
33671
- messages: [
33672
- {
33673
- role: "user",
33674
- content: "ping"
33675
- }
33676
- ]
33677
- }),
33678
- signal: controller.signal
33679
- });
33680
- if (response.ok) {
33681
- return { providerId: request.providerId, status: "success" };
33682
- }
33683
- if (response.status === 401) {
33684
- return { providerId: request.providerId, status: "unauthorized" };
33685
- }
33686
- if (response.status === 403) {
33687
- return { providerId: request.providerId, status: "forbidden" };
33688
- }
33689
- if (response.status >= 500) {
33690
- return {
33691
- providerId: request.providerId,
33692
- status: "server",
33693
- detail: `HTTP ${response.status}`
33694
- };
33695
- }
33696
- return {
33697
- providerId: request.providerId,
33698
- status: "unknown",
33699
- detail: `HTTP ${response.status}`
33700
- };
33701
- } catch (error51) {
33702
- if (isAbortError(error51)) {
33703
- return { providerId: request.providerId, status: "timeout" };
33704
- }
33705
- return {
33706
- providerId: request.providerId,
33707
- status: "network",
33708
- detail: error51 instanceof Error ? error51.message : String(error51)
33709
- };
33710
- } finally {
33711
- clearTimeout(timeout);
33712
- }
33713
- }
33714
- function isAuthValidationSuccess(result) {
33715
- return result.status === "success";
33716
- }
33717
- function createAuthValidationError(result) {
33718
- return new Error(formatAuthValidationFailureMessage(result));
33719
- }
33720
- function buildMessagesUrl(baseUrl) {
33721
- return `${baseUrl.replace(/\/+$/, "")}/v1/messages`;
33722
- }
33723
- function isAbortError(error51) {
33724
- return error51 instanceof DOMException && error51.name === "AbortError";
33725
- }
33726
- var CLAUDE_ZAI_PROVIDER_ID = "Claude Code with Z.AI GLM";
33727
- var CLAUDE_KIMI_PROVIDER_ID = "Claude Code with Moonshot Kimi";
33728
- var AUTH_PROVIDER_DEFINITIONS = {
33729
- "claude-zai": {
33730
- providerId: CLAUDE_ZAI_PROVIDER_ID,
33731
- baseUrl: CLI_BACKENDS["claude-zai"].defaultEnv.ANTHROPIC_BASE_URL,
33732
- env: CLI_BACKENDS["claude-zai"].defaultEnv
33733
- },
33734
- "claude-kimi": {
33735
- providerId: CLAUDE_KIMI_PROVIDER_ID,
33736
- baseUrl: CLI_BACKENDS["claude-kimi"].defaultEnv.ANTHROPIC_BASE_URL,
33737
- env: CLI_BACKENDS["claude-kimi"].defaultEnv
33738
- }
33739
- };
33740
- async function resolveAuthEnv(cli, deps) {
33741
- const provider = AUTH_PROVIDER_DEFINITIONS[cli];
33742
- if (!provider) return {};
33743
- await migrateLegacyAuthStore();
33744
- const auth = deps?.authService ?? createAuthService({ authPath: DEFAULT_AUTH_PATH });
33745
- const token = await auth.getApiKey(provider.providerId);
33746
- if (!token) {
33747
- throw new Error(formatMissingAuthKeyMessage({ cli, providerId: provider.providerId }));
33748
- }
33749
- const validation = await validateAnthropicCompatibleApiKey({
33750
- providerId: provider.providerId,
33751
- apiKey: token,
33752
- baseUrl: provider.baseUrl
33753
- });
33754
- if (!isAuthValidationSuccess(validation)) {
33755
- const failure = {
33756
- providerId: validation.providerId,
33757
- status: validation.status,
33758
- detail: validation.detail
33759
- };
33760
- throw createAuthValidationError(failure);
33761
- }
33762
- return { ...provider.env, ANTHROPIC_AUTH_TOKEN: token };
33763
- }
33764
- var GLOBAL_OPTIONS_VERSION = 1;
33765
- var GLOBAL_OPTIONS_FILE_NAME = "settings.json";
33766
- var LOCK_DIR_NAME2 = "settings.json.lock";
33767
- var LOCK_OWNER_FILE_NAME = "owner";
33768
- var TEMP_FILE_PREFIX = `.tmp-${GLOBAL_OPTIONS_FILE_NAME}-`;
33769
- function createGlobalOptionsStore(deps = {}) {
33770
- const dataDir = deps.dataDir ?? getFleetDataDir();
33771
- const optionsPath = path93.join(dataDir, GLOBAL_OPTIONS_FILE_NAME);
33772
- const lockDir = path93.join(dataDir, LOCK_DIR_NAME2);
33773
- const store2 = createDurableJsonStore({
33774
- filePath: optionsPath,
33775
- lockDir,
33776
- lockOwnerFileName: LOCK_OWNER_FILE_NAME,
33777
- sanitize: (value) => sanitizeGlobalOptionsData(value).data,
33778
- sensitivity: "sensitive",
33779
- timeoutMs: deps.timeoutMs,
33780
- staleLockMs: deps.staleLockMs,
33781
- tempCleanupPrefix: TEMP_FILE_PREFIX,
33782
- now: deps.now
33783
- });
33784
- return {
33785
- path: optionsPath,
33786
- load: () => store2.load(),
33787
- save: (data) => store2.save(sanitizeGlobalOptionsData(data).data),
33788
- update: (mutate) => store2.update((current) => sanitizeGlobalOptionsData(mutate(current)).data)
33789
- };
33790
- }
33791
- function createEmptyGlobalOptionsData() {
33792
- return {
33793
- version: GLOBAL_OPTIONS_VERSION
33794
- };
33795
- }
33796
- function sanitizeGlobalOptionsData(value) {
33797
- if (!isRecord3(value)) {
33798
- return { data: createEmptyGlobalOptionsData(), changed: true };
33799
- }
33800
- if (value.version !== GLOBAL_OPTIONS_VERSION) {
33801
- return { data: createEmptyGlobalOptionsData(), changed: true };
33802
- }
33803
- const data = {
33804
- version: GLOBAL_OPTIONS_VERSION,
33805
- ...typeof value.replaceSystemPrompt === "boolean" ? { replaceSystemPrompt: value.replaceSystemPrompt } : {},
33806
- ...typeof value.enableMetaphor === "boolean" ? { enableMetaphor: value.enableMetaphor } : {}
33807
- };
33808
- const allowedKeys = /* @__PURE__ */ new Set(["version", "replaceSystemPrompt", "enableMetaphor"]);
33809
- const changed = Object.keys(value).some((key) => !allowedKeys.has(key)) || "replaceSystemPrompt" in value && typeof value.replaceSystemPrompt !== "boolean" || "enableMetaphor" in value && typeof value.enableMetaphor !== "boolean";
33810
- return { data, changed };
33811
- }
33812
- function isRecord3(value) {
33813
- return typeof value === "object" && value !== null && !Array.isArray(value);
33814
- }
33815
- function createGlobalOptionsService(deps = {}) {
33816
- const store2 = deps.store ?? createGlobalOptionsStore({ dataDir: deps.dataDir });
33817
- return {
33818
- load: () => store2.load(),
33819
- save: (data) => {
33820
- store2.save(data);
33821
- return store2.load();
33822
- },
33823
- update: (mutate) => updateGlobalOptions(store2, mutate)
33824
- };
33825
- }
33826
- function updateGlobalOptions(store2, mutate) {
33827
- return store2.update(mutate);
33828
- }
33829
-
33830
- // ../../packages/fleet-infra/dist/index.js
33831
- function createInfraServices(deps = {}) {
33832
- const globalOptionsService = createGlobalOptionsService();
33833
- const authService = createAuthService({ authPath: deps.authPath ?? DEFAULT_AUTH_PATH });
33834
- return {
33835
- authService,
33836
- globalOptionsService
33837
- };
33838
- }
33839
33966
  var FLEET_WIKI_ENTRY_BEGIN = "<<<FLEET_WIKI_ENTRY_BEGIN";
33840
33967
  var FLEET_WIKI_ENTRY_END = "<<<FLEET_WIKI_ENTRY_END>>>";
33841
33968
  var FLEET_WIKI_RAW_SOURCE_BEGIN = "<<<FLEET_WIKI_RAW_SOURCE_BEGIN";
@@ -34862,7 +34989,7 @@ function stripLeadingFrontmatter(body) {
34862
34989
  return stripped ? next : body;
34863
34990
  }
34864
34991
  function computeContentHash(content) {
34865
- return crypto3.createHash("sha256").update(content, "utf8").digest("hex").slice(0, 8);
34992
+ return crypto5.createHash("sha256").update(content, "utf8").digest("hex").slice(0, 8);
34866
34993
  }
34867
34994
  function assertWithinRawDir(absolutePath, paths) {
34868
34995
  const relative = path93__default.relative(paths.rawDir, absolutePath);
@@ -35075,7 +35202,7 @@ async function writeAtomic(filePath, content, paths) {
35075
35202
  await ensureMemoryRoot(paths);
35076
35203
  const tempPath = path93__default.join(
35077
35204
  path93__default.dirname(filePath),
35078
- `.tmp-${process.pid}-${Date.now()}-${crypto3.randomUUID()}-${os22__default.hostname()}`
35205
+ `.tmp-${process.pid}-${Date.now()}-${crypto5.randomUUID()}-${os22__default.hostname()}`
35079
35206
  );
35080
35207
  await writeFile(tempPath, content, "utf8");
35081
35208
  await rename(tempPath, filePath);
@@ -37612,7 +37739,7 @@ function parseOptionalIsoDate(value) {
37612
37739
  return Number.isFinite(timestamp) ? timestamp : null;
37613
37740
  }
37614
37741
  function hashBodyForComparison(body) {
37615
- return crypto3.createHash("sha256").update(body.replace(/\s+/g, " ").trim(), "utf8").digest("hex").slice(0, 8);
37742
+ return crypto5.createHash("sha256").update(body.replace(/\s+/g, " ").trim(), "utf8").digest("hex").slice(0, 8);
37616
37743
  }
37617
37744
  function hasConflictingStatuses(entries) {
37618
37745
  const statuses = new Set(entries.map((entry) => entry.status).filter((status) => status !== "unknown"));
@@ -39795,6 +39922,420 @@ function buildWikiToolSpec(config2, usage) {
39795
39922
  };
39796
39923
  }
39797
39924
 
39925
+ // src/carrier-settings-routes.ts
39926
+ var SUBAGENT_CLI_TYPES = /* @__PURE__ */ new Set(["claude", "claude-zai", "claude-kimi"]);
39927
+ var TASKFORCE_MIN_BACKENDS = 2;
39928
+ function createCarrierSettingsRouter(deps) {
39929
+ const controller = createStatusOverlayController(deps.registry);
39930
+ return async function handleCarrierSettingsRoute(context) {
39931
+ const { req, res, pathname } = context;
39932
+ if (pathname === "/carrier-settings/state") {
39933
+ if (req.method !== "GET") {
39934
+ deps.writeJson(res, 405, { error: "Method not allowed" });
39935
+ return true;
39936
+ }
39937
+ deps.writeJson(res, 200, buildCarrierSettingsState(deps.registry));
39938
+ return true;
39939
+ }
39940
+ if (pathname === "/carrier-settings/options") {
39941
+ if (req.method !== "GET") {
39942
+ deps.writeJson(res, 405, { error: "Method not allowed" });
39943
+ return true;
39944
+ }
39945
+ deps.writeJson(res, 200, buildCarrierSettingsOptions());
39946
+ return true;
39947
+ }
39948
+ const mutation = parseCarrierMutation(pathname);
39949
+ if (!mutation) return false;
39950
+ await handleCarrierMutation(req, res, deps, controller, mutation);
39951
+ return true;
39952
+ };
39953
+ }
39954
+ function buildCarrierSettingsState(registry3) {
39955
+ const defaultsByCarrier = buildDefaultsByCarrier(registry3);
39956
+ const snapshot = readCarriersSnapshot(defaultsByCarrier);
39957
+ return {
39958
+ generation: snapshot.generation,
39959
+ carriers: getRegisteredOrder(registry3).map((carrierId) => {
39960
+ const config2 = requireCarrierConfig(registry3, carrierId);
39961
+ const resolved = snapshot.carriers[carrierId] ?? fallbackResolvedState(config2);
39962
+ return toCarrierSettingsCarrier(registry3, config2, resolved);
39963
+ })
39964
+ };
39965
+ }
39966
+ function buildCarrierSettingsOptions() {
39967
+ return {
39968
+ cliTypes: getCliTypes().map(toCliOption),
39969
+ taskForceConstraints: { minBackends: TASKFORCE_MIN_BACKENDS }
39970
+ };
39971
+ }
39972
+ async function handleCarrierMutation(req, res, deps, controller, mutation) {
39973
+ const method = req.method ?? "GET";
39974
+ const expectedMethod = mutation.kind === "display-name" ? "PATCH" : mutation.kind === "taskforce-backend" || mutation.kind === "taskforce-all" ? method : "PUT";
39975
+ if (!isExpectedCarrierMethod(mutation, method, expectedMethod)) {
39976
+ deps.writeJson(res, 405, { error: "Method not allowed" });
39977
+ return;
39978
+ }
39979
+ if (!deps.isAuthorized(req)) {
39980
+ deps.writeJson(res, 401, { error: "unauthorized" });
39981
+ return;
39982
+ }
39983
+ if (!isJsonRequest(req)) {
39984
+ deps.writeJson(res, 415, { error: "unsupported_media_type" });
39985
+ return;
39986
+ }
39987
+ if (!getRegisteredCarrierConfig(deps.registry, mutation.carrierId)) {
39988
+ deps.writeJson(res, 404, { error: "carrier_not_found" });
39989
+ return;
39990
+ }
39991
+ if (mutation.kind === "taskforce-backend" && !isCliType(mutation.cliType)) {
39992
+ deps.writeJson(res, 400, { error: "invalid_cli_type" });
39993
+ return;
39994
+ }
39995
+ try {
39996
+ if (mutation.kind === "cli") {
39997
+ const body2 = await readRequiredJsonBody(req, res, deps);
39998
+ if (!body2.ok) return;
39999
+ await mutateCarrierCli(res, deps, controller, mutation.carrierId, body2.body);
40000
+ return;
40001
+ }
40002
+ if (mutation.kind === "model") {
40003
+ const body2 = await readRequiredJsonBody(req, res, deps);
40004
+ if (!body2.ok) return;
40005
+ await mutateCarrierModel(res, deps, mutation.carrierId, body2.body);
40006
+ return;
40007
+ }
40008
+ if (mutation.kind === "display-name") {
40009
+ const body2 = await readRequiredJsonBody(req, res, deps);
40010
+ if (!body2.ok) return;
40011
+ mutateDisplayName(res, deps, mutation.carrierId, body2.body);
40012
+ return;
40013
+ }
40014
+ if (mutation.kind === "agent-mode") {
40015
+ const body2 = await readRequiredJsonBody(req, res, deps);
40016
+ if (!body2.ok) return;
40017
+ mutateAgentMode(res, deps, mutation.carrierId, body2.body);
40018
+ return;
40019
+ }
40020
+ if (mutation.kind === "taskforce-backend") {
40021
+ if (method === "DELETE") {
40022
+ const body3 = await readRequiredJsonBody(req, res, deps);
40023
+ if (!body3.ok) return;
40024
+ resetTaskForceModelSelection(mutation.carrierId, mutation.cliType);
40025
+ refreshTaskForceConfiguredCarriers(deps.registry);
40026
+ writeMutationState(res, deps);
40027
+ return;
40028
+ }
40029
+ const body2 = await readRequiredJsonBody(req, res, deps);
40030
+ if (!body2.ok) return;
40031
+ mutateTaskForceBackend(res, deps, mutation.carrierId, mutation.cliType, body2.body);
40032
+ return;
40033
+ }
40034
+ const body = await readRequiredJsonBody(req, res, deps);
40035
+ if (!body.ok) return;
40036
+ resetCarrierTaskForceConfig(mutation.carrierId);
40037
+ refreshTaskForceConfiguredCarriers(deps.registry);
40038
+ writeMutationState(res, deps);
40039
+ } catch (error51) {
40040
+ deps.writeJson(res, 400, { error: error51 instanceof Error ? error51.message : "invalid_request" });
40041
+ }
40042
+ }
40043
+ async function mutateCarrierCli(res, deps, controller, carrierId, body) {
40044
+ const cliType = readCliType(body.cliType);
40045
+ if (!cliType) {
40046
+ deps.writeJson(res, 400, { error: "invalid_cli_type" });
40047
+ return;
40048
+ }
40049
+ await controller.changeCliType(carrierId, cliType);
40050
+ if (!SUBAGENT_CLI_TYPES.has(cliType)) {
40051
+ const resolved = readCarriersSnapshot(buildDefaultsByCarrier(deps.registry)).carriers[carrierId];
40052
+ if (resolved?.agentMode === "subagent") {
40053
+ const config2 = requireCarrierConfig(deps.registry, carrierId);
40054
+ updateCarrierAgentModeAtomically(carrierId, "cli", config2.defaultAgentMode ?? "cli");
40055
+ }
40056
+ }
40057
+ writeMutationState(res, deps);
40058
+ }
40059
+ async function mutateCarrierModel(res, deps, carrierId, body) {
40060
+ const config2 = requireCarrierConfig(deps.registry, carrierId);
40061
+ const cliType = resolveAgentCliType2(carrierId, config2.defaultCliType);
40062
+ const selection = readSelection(cliType, body);
40063
+ if (!selection) {
40064
+ deps.writeJson(res, 400, { error: "invalid_model_selection" });
40065
+ return;
40066
+ }
40067
+ await updateAgentCliSelection(carrierId, cliType, selection);
40068
+ notifyStatusUpdate(deps.registry);
40069
+ writeMutationState(res, deps);
40070
+ }
40071
+ function mutateDisplayName(res, deps, carrierId, body) {
40072
+ const displayName = normalizeCarrierDisplayNameInput(body.displayName);
40073
+ if (displayName === null) {
40074
+ deps.writeJson(res, 400, { error: "invalid_display_name" });
40075
+ return;
40076
+ }
40077
+ updateCarrierDisplayName(carrierId, displayName, getCarrierSourceDisplayName(deps.registry, carrierId));
40078
+ notifyStatusUpdate(deps.registry);
40079
+ writeMutationState(res, deps);
40080
+ }
40081
+ function mutateAgentMode(res, deps, carrierId, body) {
40082
+ const config2 = requireCarrierConfig(deps.registry, carrierId);
40083
+ const agentMode = body.agentMode;
40084
+ if (agentMode !== "cli" && agentMode !== "subagent") {
40085
+ deps.writeJson(res, 400, { error: "invalid_agent_mode" });
40086
+ return;
40087
+ }
40088
+ const cliType = resolveAgentCliType2(carrierId, config2.defaultCliType);
40089
+ if (agentMode === "subagent" && !SUBAGENT_CLI_TYPES.has(cliType)) {
40090
+ deps.writeJson(res, 400, { error: "subagent_unsupported" });
40091
+ return;
40092
+ }
40093
+ updateCarrierAgentModeAtomically(carrierId, agentMode, config2.defaultAgentMode ?? "cli");
40094
+ if (agentMode === "subagent") refreshTaskForceConfiguredCarriers(deps.registry);
40095
+ notifyStatusUpdate(deps.registry);
40096
+ writeMutationState(res, deps);
40097
+ }
40098
+ function mutateTaskForceBackend(res, deps, carrierId, cliType, body) {
40099
+ const selection = readSelection(cliType, body);
40100
+ if (!selection) {
40101
+ deps.writeJson(res, 400, { error: "invalid_model_selection" });
40102
+ return;
40103
+ }
40104
+ updateTaskForceBackendAtomically(carrierId, cliType, selection);
40105
+ refreshTaskForceConfiguredCarriers(deps.registry);
40106
+ notifyStatusUpdate(deps.registry);
40107
+ writeMutationState(res, deps);
40108
+ }
40109
+ function writeMutationState(res, deps) {
40110
+ const response = { state: buildCarrierSettingsState(deps.registry) };
40111
+ deps.writeJson(res, 200, response);
40112
+ }
40113
+ async function readRequiredJsonBody(req, res, deps) {
40114
+ const body = await deps.readJsonBody(req);
40115
+ if (!body || typeof body !== "object" || Array.isArray(body)) {
40116
+ deps.writeJson(res, 400, { error: "invalid_json" });
40117
+ return { ok: false };
40118
+ }
40119
+ return { ok: true, body };
40120
+ }
40121
+ function buildDefaultsByCarrier(registry3) {
40122
+ return Object.fromEntries(
40123
+ getRegisteredOrder(registry3).map((carrierId) => {
40124
+ const config2 = requireCarrierConfig(registry3, carrierId);
40125
+ return [carrierId, {
40126
+ cliType: config2.defaultCliType,
40127
+ ...config2.defaultAgentMode ? { defaultAgentMode: config2.defaultAgentMode } : {},
40128
+ ...buildCarrierModelDefaults(config2, config2.defaultCliType)
40129
+ }];
40130
+ })
40131
+ );
40132
+ }
40133
+ function toCarrierSettingsCarrier(registry3, config2, resolved) {
40134
+ const cliType = resolved.agentCliType ?? config2.defaultCliType;
40135
+ const selection = resolved.agentCli[cliType] ?? readDefaultSelection(cliType);
40136
+ const taskForceBackends = toTaskForceBackends(resolved.taskforce);
40137
+ return {
40138
+ carrierId: config2.id,
40139
+ displayName: resolved.displayName ?? getCarrierSourceDisplayName(registry3, config2.id),
40140
+ sourceDisplayName: getCarrierSourceDisplayName(registry3, config2.id),
40141
+ role: config2.carrierMetadata?.title ?? config2.displayName,
40142
+ roleDescription: config2.carrierMetadata?.summary ?? "",
40143
+ ...config2.carrierMetadata?.category ? { category: config2.carrierMetadata.category } : {},
40144
+ slot: config2.slot,
40145
+ cliType,
40146
+ defaultCliType: config2.defaultCliType,
40147
+ model: selection.model,
40148
+ ...selection.effort ? { effort: selection.effort } : {},
40149
+ agentMode: resolved.agentMode,
40150
+ subagentMode: resolved.agentMode === "subagent",
40151
+ taskForceBackendCount: taskForceBackends.length,
40152
+ taskforce: { backends: taskForceBackends }
40153
+ };
40154
+ }
40155
+ function toTaskForceBackends(taskforce) {
40156
+ return TASKFORCE_CLI_TYPES.map((cliType) => {
40157
+ const selection = taskforce[cliType];
40158
+ if (!selection) return null;
40159
+ return {
40160
+ cliType,
40161
+ model: selection.model,
40162
+ ...selection.effort ? { effort: selection.effort } : {}
40163
+ };
40164
+ }).filter((backend) => backend !== null);
40165
+ }
40166
+ function toCliOption(cliType) {
40167
+ const provider = getProviderModels(cliType);
40168
+ return {
40169
+ id: cliType,
40170
+ displayName: CLI_DISPLAY_NAMES[cliType] ?? provider.name,
40171
+ supportsSubagent: SUBAGENT_CLI_TYPES.has(cliType),
40172
+ models: provider.models.map((model) => {
40173
+ const effort = getEffort(cliType, model.modelId);
40174
+ return {
40175
+ modelId: model.modelId,
40176
+ name: model.name,
40177
+ ...effort.supported ? { effort: { levels: effort.levels, default: effort.default } } : {}
40178
+ };
40179
+ }),
40180
+ defaultModel: provider.defaultModel ?? provider.models[0]?.modelId
40181
+ };
40182
+ }
40183
+ function createStatusOverlayController(registry3) {
40184
+ return new StatusOverlayController({
40185
+ getEntries: () => [],
40186
+ getRegisteredOrder: () => getRegisteredOrder(registry3),
40187
+ getCarrierConfig: (carrierId) => getRegisteredCarrierConfig(registry3, carrierId),
40188
+ getResolvedCliType: (carrierId) => {
40189
+ const config2 = getRegisteredCarrierConfig(registry3, carrierId);
40190
+ return config2 ? resolveAgentCliType2(carrierId, config2.defaultCliType) : void 0;
40191
+ },
40192
+ getCurrentModelSelection: (carrierId) => {
40193
+ return readCurrentModelSelection(registry3, carrierId);
40194
+ },
40195
+ getAvailableModels: (cliType) => {
40196
+ const provider = getProviderModels(cliType);
40197
+ return {
40198
+ defaultModel: provider.defaultModel,
40199
+ models: provider.models.map((model) => ({
40200
+ modelId: model.modelId,
40201
+ name: model.name,
40202
+ effort: getEffort(cliType, model.modelId)
40203
+ }))
40204
+ };
40205
+ },
40206
+ getEffort: (cliType, modelId) => getEffort(cliType, modelId),
40207
+ getAgentCliSelection: (carrierId, cliType) => getAgentCliSelection(carrierId, cliType),
40208
+ saveAgentCliSelection,
40209
+ updateCarrierCliType: (carrierId, cliType) => updateCarrierCliType(registry3, carrierId),
40210
+ applyAgentCliTypeSelectionUpdate,
40211
+ refreshAgentPanel: () => void 0,
40212
+ syncModelConfig: () => void 0,
40213
+ notifyStatusUpdate: () => notifyStatusUpdate(registry3)
40214
+ });
40215
+ }
40216
+ function updateCarrierAgentModeAtomically(carrierId, agentMode, defaultAgentMode) {
40217
+ if (agentMode === "cli") {
40218
+ setCarrierAgentMode(carrierId, false, defaultAgentMode);
40219
+ return;
40220
+ }
40221
+ updateCarriers((states) => {
40222
+ const carriers = { ...states.carriers ?? {} };
40223
+ const current = carriers[carrierId] ?? {};
40224
+ const next = { ...current };
40225
+ next.agentMode = "subagent";
40226
+ delete next.taskforce;
40227
+ carriers[carrierId] = next;
40228
+ states.carriers = carriers;
40229
+ });
40230
+ }
40231
+ function updateTaskForceBackendAtomically(carrierId, cliType, selection) {
40232
+ updateCarriers((states) => {
40233
+ const carriers = { ...states.carriers ?? {} };
40234
+ const current = carriers[carrierId] ?? {};
40235
+ carriers[carrierId] = {
40236
+ ...current,
40237
+ agentMode: "cli",
40238
+ taskforce: {
40239
+ ...current.taskforce ?? {},
40240
+ [cliType]: selection
40241
+ }
40242
+ };
40243
+ states.carriers = carriers;
40244
+ });
40245
+ }
40246
+ function refreshTaskForceConfiguredCarriers(registry3) {
40247
+ setTaskForceConfiguredCarriers(registry3, getConfiguredTaskForceCarrierIds(getRegisteredOrder(registry3)));
40248
+ notifyStatusUpdate(registry3);
40249
+ }
40250
+ function readSelection(cliType, body) {
40251
+ if (typeof body.model !== "string") return null;
40252
+ const provider = getProviderModels(cliType);
40253
+ if (!provider.models.some((model) => model.modelId === body.model)) return null;
40254
+ const effort = getEffort(cliType, body.model);
40255
+ if (!effort.supported) {
40256
+ return body.effort === void 0 ? { model: body.model } : null;
40257
+ }
40258
+ if (typeof body.effort !== "string" || !effort.levels.includes(body.effort)) return null;
40259
+ return { model: body.model, effort: body.effort };
40260
+ }
40261
+ function readDefaultSelection(cliType) {
40262
+ const provider = getProviderModels(cliType);
40263
+ const model = provider.defaultModel;
40264
+ const effort = getEffort(cliType, model);
40265
+ return {
40266
+ model,
40267
+ ...effort.supported ? { effort: effort.default } : {}
40268
+ };
40269
+ }
40270
+ function readCurrentModelSelection(registry3, carrierId) {
40271
+ const config2 = getRegisteredCarrierConfig(registry3, carrierId);
40272
+ if (!config2) return void 0;
40273
+ const defaults = {
40274
+ [carrierId]: {
40275
+ cliType: config2.defaultCliType,
40276
+ ...config2.defaultAgentMode ? { defaultAgentMode: config2.defaultAgentMode } : {},
40277
+ ...buildCarrierModelDefaults(config2, config2.defaultCliType)
40278
+ }
40279
+ };
40280
+ const resolved = readCarriersSnapshot(defaults).carriers[carrierId] ?? fallbackResolvedState(config2);
40281
+ const cliType = resolved.agentCliType ?? config2.defaultCliType;
40282
+ return resolved.agentCli[cliType] ?? readDefaultSelection(cliType);
40283
+ }
40284
+ function fallbackResolvedState(config2) {
40285
+ return {
40286
+ agentMode: config2.defaultAgentMode ?? "cli",
40287
+ agentCliType: config2.defaultCliType,
40288
+ agentCli: { [config2.defaultCliType]: readDefaultSelection(config2.defaultCliType) },
40289
+ taskforce: {}
40290
+ };
40291
+ }
40292
+ function parseCarrierMutation(pathname) {
40293
+ const parts = pathname.split("/").filter(Boolean);
40294
+ if (parts[0] !== "carrier-settings" || parts[1] !== "carriers" || !parts[2]) return null;
40295
+ const carrierId = safeDecodeURIComponent(parts[2]);
40296
+ if (!carrierId) return null;
40297
+ if (parts.length === 4 && parts[3] === "cli") return { kind: "cli", carrierId };
40298
+ if (parts.length === 4 && parts[3] === "model") return { kind: "model", carrierId };
40299
+ if (parts.length === 4 && parts[3] === "display-name") return { kind: "display-name", carrierId };
40300
+ if (parts.length === 4 && parts[3] === "agent-mode") return { kind: "agent-mode", carrierId };
40301
+ if (parts.length === 4 && parts[3] === "taskforce") return { kind: "taskforce-all", carrierId };
40302
+ if (parts.length === 5 && parts[3] === "taskforce") {
40303
+ const cliType = safeDecodeURIComponent(parts[4] ?? "");
40304
+ return cliType ? { kind: "taskforce-backend", carrierId, cliType } : null;
40305
+ }
40306
+ return null;
40307
+ }
40308
+ function safeDecodeURIComponent(value) {
40309
+ try {
40310
+ return decodeURIComponent(value);
40311
+ } catch {
40312
+ return null;
40313
+ }
40314
+ }
40315
+ function isExpectedCarrierMethod(mutation, method, expectedMethod) {
40316
+ if (mutation.kind === "taskforce-backend") return method === "PUT" || method === "DELETE";
40317
+ if (mutation.kind === "taskforce-all") return method === "DELETE";
40318
+ return method === expectedMethod;
40319
+ }
40320
+ function isJsonRequest(req) {
40321
+ const contentType = req.headers["content-type"];
40322
+ return typeof contentType === "string" && contentType.toLowerCase().split(";")[0]?.trim() === "application/json";
40323
+ }
40324
+ function readCliType(value) {
40325
+ return typeof value === "string" && isCliType(value) ? value : null;
40326
+ }
40327
+ function isCliType(value) {
40328
+ return Object.prototype.hasOwnProperty.call(CLI_BACKENDS, value);
40329
+ }
40330
+ function getCliTypes() {
40331
+ return Object.keys(CLI_BACKENDS);
40332
+ }
40333
+ function requireCarrierConfig(registry3, carrierId) {
40334
+ const config2 = getRegisteredCarrierConfig(registry3, carrierId);
40335
+ if (!config2) throw new Error(`Carrier not found: ${carrierId}`);
40336
+ return config2;
40337
+ }
40338
+
39798
40339
  // src/codex/security-headers.ts
39799
40340
  var CONTENT_SECURITY_POLICY = "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; object-src 'none'; base-uri 'none'";
39800
40341
  var SECURITY_HEADERS = {
@@ -39822,7 +40363,7 @@ function safeEqual(left, right) {
39822
40363
  return timingSafeEqual(leftBytes, rightBytes);
39823
40364
  }
39824
40365
  function workspaceHash(canonicalCwd) {
39825
- return crypto3.createHash("sha256").update(canonicalCwd).digest("hex").slice(0, 12);
40366
+ return crypto5.createHash("sha256").update(canonicalCwd).digest("hex").slice(0, 12);
39826
40367
  }
39827
40368
  async function canonicalizeTheaterPath(cwd) {
39828
40369
  return canonicalizeTheaterPathSync(cwd);
@@ -40702,7 +41243,7 @@ function createCodexGateway(deps) {
40702
41243
  return true;
40703
41244
  }
40704
41245
  if (selected.kind === "no-workspace") {
40705
- if (isJsonRequest(request)) {
41246
+ if (isJsonRequest2(request)) {
40706
41247
  sendJson2(response, 404, { error: "no_workspace_registered" });
40707
41248
  return true;
40708
41249
  }
@@ -40814,7 +41355,7 @@ function legacyWorkspacePath(url2) {
40814
41355
  }
40815
41356
  return null;
40816
41357
  }
40817
- function isJsonRequest(request) {
41358
+ function isJsonRequest2(request) {
40818
41359
  const accept = Array.isArray(request.headers.accept) ? request.headers.accept.join(",") : request.headers.accept ?? "";
40819
41360
  const requestedWith = Array.isArray(request.headers["x-requested-with"]) ? request.headers["x-requested-with"].join(",") : request.headers["x-requested-with"] ?? "";
40820
41361
  return accept.includes("application/json") || requestedWith.toLowerCase() === "xmlhttprequest";
@@ -40889,6 +41430,155 @@ function readRawHeaderValues(rawHeaders, name) {
40889
41430
  function stripIpv6Brackets2(host) {
40890
41431
  return host.startsWith("[") && host.endsWith("]") ? host.slice(1, -1) : host;
40891
41432
  }
41433
+ var STATE_VERSION = 1;
41434
+ var STATE_LOCK_DIR_NAME = "state.lock";
41435
+ var STATE_LOCK_OWNER_FILE_NAME = "owner.json";
41436
+ var STATE_TEMP_PREFIX = ".state.";
41437
+ function createConsoleDurableStateStore(deps = {}) {
41438
+ const paths = deps.paths ?? createConsoleDataPaths();
41439
+ const createStore = deps.createStore ?? createDurableJsonStore;
41440
+ return createStore({
41441
+ filePath: paths.stateFile,
41442
+ lockDir: path93__default.join(paths.dir, STATE_LOCK_DIR_NAME),
41443
+ lockOwnerFileName: STATE_LOCK_OWNER_FILE_NAME,
41444
+ now: deps.now,
41445
+ sanitize: sanitizeDurableConsoleState,
41446
+ sensitivity: "sensitive",
41447
+ tempCleanupPrefix: STATE_TEMP_PREFIX
41448
+ });
41449
+ }
41450
+ function sanitizeDurableConsoleState(value) {
41451
+ if (!isRecord4(value) || value.version !== STATE_VERSION) return emptyDurableConsoleState();
41452
+ return {
41453
+ version: STATE_VERSION,
41454
+ theaters: readTheaterRegistrations(value.theaters),
41455
+ operations: readDurableOperations(value.operations)
41456
+ };
41457
+ }
41458
+ function emptyDurableConsoleState() {
41459
+ return { version: STATE_VERSION, theaters: [], operations: [] };
41460
+ }
41461
+ function readProviderSessionCapture(fleetSessionId, deps = {}) {
41462
+ if (!isSafeCaptureId(fleetSessionId)) return null;
41463
+ const capturesDir = deps.capturesDir ?? createConsoleDataPaths().capturesDir;
41464
+ const filePath = path93__default.join(capturesDir, `${fleetSessionId}.json`);
41465
+ try {
41466
+ const parsed = JSON.parse(fs5__default.readFileSync(filePath, "utf8"));
41467
+ return sanitizeProviderSession(parsed);
41468
+ } catch {
41469
+ return null;
41470
+ }
41471
+ }
41472
+ function mergeProviderSessionCaptures(state, deps = {}) {
41473
+ let changed = false;
41474
+ const operations = state.operations.map((operation) => {
41475
+ if (operation.providerSession) return operation;
41476
+ const providerSession = readProviderSessionCapture(operation.sessionId, deps);
41477
+ if (!providerSession) return operation;
41478
+ changed = true;
41479
+ return { ...operation, providerSession };
41480
+ });
41481
+ return changed ? { ...state, operations } : state;
41482
+ }
41483
+ function unlinkProviderSessionCapture(fleetSessionId, deps = {}) {
41484
+ if (!isSafeCaptureId(fleetSessionId)) return false;
41485
+ const capturesDir = deps.capturesDir ?? createConsoleDataPaths().capturesDir;
41486
+ try {
41487
+ fs5__default.unlinkSync(path93__default.join(capturesDir, `${fleetSessionId}.json`));
41488
+ return true;
41489
+ } catch {
41490
+ return false;
41491
+ }
41492
+ }
41493
+ function cleanupProviderSessionCaptures(state, deps = {}) {
41494
+ for (const operation of state.operations) {
41495
+ if (operation.providerSession) unlinkProviderSessionCapture(operation.sessionId, deps);
41496
+ }
41497
+ }
41498
+ function readTheaterRegistrations(value) {
41499
+ if (!Array.isArray(value)) return [];
41500
+ const registrations = [];
41501
+ for (const item of value) {
41502
+ const registration = sanitizeTheaterRegistration(item);
41503
+ if (registration) registrations.push(registration);
41504
+ }
41505
+ return registrations;
41506
+ }
41507
+ function readDurableOperations(value) {
41508
+ if (!Array.isArray(value)) return [];
41509
+ const operations = [];
41510
+ for (const item of value) {
41511
+ const operation = sanitizeDurableOperation(item);
41512
+ if (operation) operations.push(operation);
41513
+ }
41514
+ return operations;
41515
+ }
41516
+ function sanitizeTheaterRegistration(value) {
41517
+ if (!isRecord4(value)) return null;
41518
+ const id = readNonEmptyString(value.id);
41519
+ const theaterPath = readNonEmptyString(value.path);
41520
+ const realpath = readNonEmptyString(value.realpath);
41521
+ const label = readNonEmptyString(value.label);
41522
+ const registeredAt = readNonEmptyString(value.registeredAt);
41523
+ const lastOpenedAt = readNonEmptyString(value.lastOpenedAt);
41524
+ if (!id || !theaterPath || !realpath || !label || !registeredAt || !lastOpenedAt) return null;
41525
+ return { id, path: theaterPath, realpath, label, registeredAt, lastOpenedAt };
41526
+ }
41527
+ function sanitizeDurableOperation(value) {
41528
+ if (!isRecord4(value)) return null;
41529
+ const sessionId = readNonEmptyString(value.sessionId);
41530
+ const theaterId = readNonEmptyString(value.theaterId);
41531
+ const cwd = readNonEmptyString(value.cwd);
41532
+ const cwdLabel = readNonEmptyString(value.cwdLabel);
41533
+ const sequence = readPositiveInteger(value.sequence);
41534
+ const createdAt = readFiniteNumber(value.createdAt);
41535
+ if (!sessionId || !theaterId || !cwd || !cwdLabel || sequence === null || createdAt === null) return null;
41536
+ const providerSession = sanitizeProviderSession(value.providerSession);
41537
+ return {
41538
+ sessionId,
41539
+ theaterId,
41540
+ cwd,
41541
+ cwdLabel,
41542
+ sequence,
41543
+ ...readOptionalString(value.label) ? { label: readOptionalString(value.label) } : {},
41544
+ ...readOptionalString(value.cliId) ? { cliId: readOptionalString(value.cliId) } : {},
41545
+ ...readOptionalString(value.cliLabel) ? { cliLabel: readOptionalString(value.cliLabel) } : {},
41546
+ createdAt,
41547
+ ...providerSession ? { providerSession } : {}
41548
+ };
41549
+ }
41550
+ function sanitizeProviderSession(value) {
41551
+ if (!isRecord4(value)) return null;
41552
+ const provider = value.provider === "claude" || value.provider === "codex" ? value.provider : null;
41553
+ const sessionId = readNonEmptyString(value.sessionId);
41554
+ const capturedAt = readNonEmptyString(value.capturedAt);
41555
+ if (!provider || !sessionId || !capturedAt) return null;
41556
+ return {
41557
+ provider,
41558
+ sessionId,
41559
+ ...readOptionalString(value.transcriptPath) ? { transcriptPath: readOptionalString(value.transcriptPath) } : {},
41560
+ ...readOptionalString(value.source) ? { source: readOptionalString(value.source) } : {},
41561
+ capturedAt
41562
+ };
41563
+ }
41564
+ function isRecord4(value) {
41565
+ return typeof value === "object" && value !== null && !Array.isArray(value);
41566
+ }
41567
+ function isSafeCaptureId(value) {
41568
+ return value.length > 0 && path93__default.basename(value) === value && !value.includes(path93__default.sep) && !value.includes(path93__default.posix.sep);
41569
+ }
41570
+ function readNonEmptyString(value) {
41571
+ return typeof value === "string" && value.length > 0 ? value : null;
41572
+ }
41573
+ function readOptionalString(value) {
41574
+ return typeof value === "string" && value.length > 0 ? value : void 0;
41575
+ }
41576
+ function readPositiveInteger(value) {
41577
+ return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : null;
41578
+ }
41579
+ function readFiniteNumber(value) {
41580
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
41581
+ }
40892
41582
 
40893
41583
  // src/observability-routes.ts
40894
41584
  function writeObserverEvents(req, res, workspace, store2) {
@@ -41099,15 +41789,71 @@ function createConsoleObservabilityStore(deps = {}) {
41099
41789
  terminalSessionsById.set(state.sessionId, state);
41100
41790
  return toTerminalSessionInfo(state);
41101
41791
  }
41792
+ function injectDormantOperation(operation) {
41793
+ const currentSequence = terminalSequenceByTheater.get(operation.theaterId) ?? 0;
41794
+ terminalSequenceByTheater.set(operation.theaterId, Math.max(currentSequence, operation.sequence));
41795
+ const state = {
41796
+ sessionId: operation.sessionId,
41797
+ cwd: operation.cwd,
41798
+ canonicalCwd: canonicalizeTheaterPathSync(operation.cwd),
41799
+ cwdLabel: operation.cwdLabel,
41800
+ sequence: operation.sequence,
41801
+ label: operation.label,
41802
+ cliId: operation.cliId,
41803
+ cliLabel: operation.cliLabel,
41804
+ createdAt: operation.createdAt,
41805
+ theaterId: operation.theaterId,
41806
+ terminalSessionId: operation.sessionId,
41807
+ status: "dormant",
41808
+ providerSession: operation.providerSession
41809
+ };
41810
+ terminalSessionsById.set(state.sessionId, state);
41811
+ return toTerminalSessionInfo(state);
41812
+ }
41102
41813
  function listTerminalSessions() {
41103
41814
  return Array.from(terminalSessionsById.values()).map(toTerminalSessionInfo).sort((a, b) => b.createdAt - a.createdAt);
41104
41815
  }
41816
+ function listDurableOperations() {
41817
+ return Array.from(terminalSessionsById.values()).map((session) => ({
41818
+ sessionId: session.sessionId,
41819
+ theaterId: session.theaterId,
41820
+ cwd: session.cwd,
41821
+ cwdLabel: session.cwdLabel,
41822
+ sequence: session.sequence,
41823
+ ...session.label ? { label: session.label } : {},
41824
+ ...session.cliId ? { cliId: session.cliId } : {},
41825
+ ...session.cliLabel ? { cliLabel: session.cliLabel } : {},
41826
+ createdAt: session.createdAt,
41827
+ ...session.providerSession ? { providerSession: session.providerSession } : {}
41828
+ }));
41829
+ }
41830
+ function getDurableOperation(sessionId) {
41831
+ return listDurableOperations().find((operation) => operation.sessionId === sessionId) ?? null;
41832
+ }
41833
+ function updateTerminalSessionProviderSession(sessionId, providerSession) {
41834
+ const session = terminalSessionsById.get(sessionId);
41835
+ if (!session) return null;
41836
+ session.providerSession = providerSession;
41837
+ return toTerminalSessionInfo(session);
41838
+ }
41105
41839
  function updateTerminalSessionStatus(sessionId, status) {
41106
41840
  const session = terminalSessionsById.get(sessionId);
41107
41841
  if (!session) return null;
41108
41842
  session.status = status;
41109
41843
  return toTerminalSessionInfo(session);
41110
41844
  }
41845
+ function transitionTerminalSessionToDormant(sessionId, providerSession) {
41846
+ const session = terminalSessionsById.get(sessionId);
41847
+ if (!session) return null;
41848
+ const workspace = workspacesByCliRunId.get(sessionId);
41849
+ if (workspace?.terminalSessionId === sessionId) {
41850
+ removeWorkspaceIndexes(workspace);
41851
+ workspacesByCliRunId.delete(sessionId);
41852
+ }
41853
+ session.status = "dormant";
41854
+ session.providerSession = providerSession;
41855
+ return toTerminalSessionInfo(session);
41856
+ }
41111
41857
  function renameTerminalSession(sessionId, rawLabel) {
41112
41858
  const session = terminalSessionsById.get(sessionId);
41113
41859
  if (!session) return null;
@@ -41147,18 +41893,23 @@ function createConsoleObservabilityStore(deps = {}) {
41147
41893
  clear,
41148
41894
  getLaunchCwd,
41149
41895
  getTruncation,
41896
+ getDurableOperation,
41150
41897
  getWorkspace,
41151
41898
  listEvents,
41152
41899
  listJobs,
41900
+ listDurableOperations,
41153
41901
  listTerminalSessions,
41154
41902
  listWorkspaces,
41155
41903
  appendTerminalRuntimeEvent,
41156
41904
  createPendingTerminalSession,
41905
+ injectDormantOperation,
41157
41906
  notifySessionUpdated,
41158
41907
  renameTerminalSession,
41159
41908
  subscribe,
41160
41909
  subscribeAll,
41910
+ updateTerminalSessionProviderSession,
41161
41911
  updateTerminalSessionStatus,
41912
+ transitionTerminalSessionToDormant,
41162
41913
  removeTerminalSession,
41163
41914
  registerTerminalRuntimeSession,
41164
41915
  workspaceCount: () => listWorkspaces().length
@@ -41230,7 +41981,8 @@ function toTerminalSessionInfo(state) {
41230
41981
  theaterId: state.theaterId,
41231
41982
  registrationId: state.registrationId,
41232
41983
  cliRunId: state.cliRunId,
41233
- tenantId: state.cliRunId
41984
+ tenantId: state.cliRunId,
41985
+ resumeAvailable: state.providerSession !== void 0
41234
41986
  };
41235
41987
  }
41236
41988
  function pruneTenantJobs(state) {
@@ -41444,7 +42196,7 @@ function resolveConsolePath(pathname) {
41444
42196
  var GRANT_ID_BYTES = 24;
41445
42197
  var DEFAULT_GRANT_TTL_MS = 6e4;
41446
42198
  function createFolderGrantStore(deps = {}) {
41447
- const randomId = deps.randomId ?? (() => crypto3.randomBytes(GRANT_ID_BYTES).toString("base64url"));
42199
+ const randomId = deps.randomId ?? (() => crypto5.randomBytes(GRANT_ID_BYTES).toString("base64url"));
41448
42200
  const statSync5 = deps.statSync ?? fs5__default.statSync;
41449
42201
  const ttlMs = deps.ttlMs ?? DEFAULT_GRANT_TTL_MS;
41450
42202
  const now = deps.now ?? Date.now;
@@ -41659,6 +42411,31 @@ function buildConsoleHookCommand(entry) {
41659
42411
  }
41660
42412
  throw new Error(`Unsupported Fleet Console session hook entry extension: ${extension}`);
41661
42413
  }
42414
+ function buildConsoleCaptureHookCommand(entry, cliId) {
42415
+ const extension = path93__default.extname(entry.entryPath);
42416
+ if (JAVASCRIPT_ENTRY_EXTENSIONS.has(extension)) {
42417
+ return createSessionCaptureHookExec({
42418
+ entryPath: entry.entryPath,
42419
+ execPath: entry.execPath,
42420
+ provider: toCaptureProvider(cliId)
42421
+ });
42422
+ }
42423
+ if (TYPESCRIPT_ENTRY_EXTENSIONS.has(extension)) {
42424
+ if (!entry.tsxLoaderPath) {
42425
+ throw new Error("Fleet Console capture session hook requires a tsx loader path");
42426
+ }
42427
+ return createSessionCaptureHookExec({
42428
+ entryPath: entry.entryPath,
42429
+ execPath: entry.execPath,
42430
+ provider: toCaptureProvider(cliId),
42431
+ tsxLoader: entry.tsxLoaderPath
42432
+ });
42433
+ }
42434
+ throw new Error(`Unsupported Fleet Console session hook entry extension: ${extension}`);
42435
+ }
42436
+ function toCaptureProvider(cliId) {
42437
+ return cliId === "codex" ? "codex" : "claude";
42438
+ }
41662
42439
  function runCodexCommand(command2) {
41663
42440
  const result = spawnSync(command2.bin, command2.args, {
41664
42441
  cwd: command2.cwd,
@@ -41684,12 +42461,12 @@ var CONSOLE_ENTRY_PATH = fileURLToPath(import.meta.url);
41684
42461
  var HOOK_ENTRY_EXTENSIONS = /* @__PURE__ */ new Set([".cjs", ".cts", ".js", ".mjs", ".mts", ".ts", ".tsx"]);
41685
42462
  var require2 = createRequire(import.meta.url);
41686
42463
  function createDefaultTerminalLaunchResolver(deps = {}) {
41687
- const baseCwd = deps.cwd ?? process4.cwd();
41688
- const env = deps.env ?? process4.env;
41689
- const execPath = deps.execPath ?? process4.execPath;
42464
+ const baseCwd = deps.cwd ?? process5.cwd();
42465
+ const env = deps.env ?? process5.env;
42466
+ const execPath = deps.execPath ?? process5.execPath;
41690
42467
  const homedir3 = deps.homedir ?? DEFAULT_TERMINAL_CWD_FALLBACK;
41691
- const platform = deps.platform ?? process4.platform;
41692
- const entryPath = resolveHookEntryPath(deps.entryPath ?? process4.argv[1]);
42468
+ const platform = deps.platform ?? process5.platform;
42469
+ const entryPath = resolveHookEntryPath(deps.entryPath ?? process5.argv[1]);
41693
42470
  const tsxLoaderPath = deps.tsxLoaderPath ?? resolveOptionalPackage("tsx");
41694
42471
  const dataDir = deps.dataDir ?? getFleetDataDir();
41695
42472
  const infraServices = deps.infraServices ?? createInfraServices();
@@ -41729,6 +42506,7 @@ function createDefaultTerminalLaunchResolver(deps = {}) {
41729
42506
  onRuntimeSessionStart: deps.onRuntimeSessionStart,
41730
42507
  resolveProfile,
41731
42508
  cliId: context?.cliId,
42509
+ resumeSessionId: context?.resumeSessionId,
41732
42510
  sessionId
41733
42511
  });
41734
42512
  };
@@ -41749,7 +42527,7 @@ function hasHookEntryExtension(entryPath) {
41749
42527
  }
41750
42528
  function startTerminalShell(launch, size) {
41751
42529
  const { spawn: spawnPty } = require2("node-pty");
41752
- const useConptyDll = resolveUseConptyDll(process4.platform, process4.env);
42530
+ const useConptyDll = resolveUseConptyDll(process5.platform, process5.env);
41753
42531
  return spawnPty(launch.bin, [...launch.args], {
41754
42532
  cols: size.cols,
41755
42533
  rows: size.rows,
@@ -41774,7 +42552,8 @@ async function createAgentCliLaunchSpec(options) {
41774
42552
  const profile = await options.resolveProfile(options.env, options.cwd, {
41775
42553
  authEnvResolver: options.authEnvResolver,
41776
42554
  authService: options.infraServices.authService,
41777
- cliId: options.cliId
42555
+ cliId: options.cliId,
42556
+ resumeSessionId: options.resumeSessionId
41778
42557
  });
41779
42558
  const injectedProfile = await options.injectProfile(profile, {
41780
42559
  buildSystemPrompt: (injectTone) => createSystemPromptBuilder({ carrierRuntime: agentRuntime.carrierRuntime }).build(injectTone),
@@ -41783,9 +42562,11 @@ async function createAgentCliLaunchSpec(options) {
41783
42562
  dataDir: options.dataDir,
41784
42563
  dedicatedMcpSession: agentRuntime.dedicatedMcpSession,
41785
42564
  enableMetaphor: false,
42565
+ captureSessionHookExec: buildConsoleCaptureHookCommand(options.hookEntry, profile.id),
41786
42566
  hookExec: buildConsoleHookCommand(options.hookEntry),
41787
42567
  onCleanup: (cleanup) => cleanupStack.push(cleanup),
41788
- replaceSystemPrompt: true,
42568
+ replaceSystemPrompt: false,
42569
+ resumeSessionId: options.resumeSessionId,
41789
42570
  withMarketplaceLock: withConsoleMarketplaceLock,
41790
42571
  mcpSessionLabel: options.sessionId
41791
42572
  });
@@ -41819,6 +42600,7 @@ function toLaunchSpec(profile, cleanup) {
41819
42600
  cwd: profile.cwd,
41820
42601
  env: { ...profile.env },
41821
42602
  messagePolicy: profile.messagePolicy,
42603
+ renameCommand: profile.renameCommand,
41822
42604
  terminalName: profile.terminalName
41823
42605
  };
41824
42606
  }
@@ -41912,6 +42694,9 @@ function createTerminalSessionManager(deps) {
41912
42694
  function getSessionMessagePolicy(sessionId) {
41913
42695
  return sessions.get(sessionId)?.messagePolicy;
41914
42696
  }
42697
+ function getSessionRenameCommand(sessionId) {
42698
+ return sessions.get(sessionId)?.renameCommand;
42699
+ }
41915
42700
  function writeToSession(sessionId, data) {
41916
42701
  const session = sessions.get(sessionId);
41917
42702
  if (!session || typeof session.pty.write !== "function") return false;
@@ -41949,7 +42734,7 @@ function createTerminalSessionManager(deps) {
41949
42734
  }
41950
42735
  }
41951
42736
  async function launchSession(context) {
41952
- const launch = await deps.launch(context.cwd, { sessionId: context.sessionId, kind: context.kind, cliId: context.cliId });
42737
+ const launch = await deps.launch(context.cwd, { sessionId: context.sessionId, kind: context.kind, cliId: context.cliId, resumeSessionId: context.resumeSessionId });
41953
42738
  let pty;
41954
42739
  try {
41955
42740
  pty = startShell(launch, { cols: DEFAULT_COLS, rows: DEFAULT_ROWS });
@@ -41964,6 +42749,7 @@ function createTerminalSessionManager(deps) {
41964
42749
  scrollback: [],
41965
42750
  cleanup: launch.cleanup,
41966
42751
  messagePolicy: launch.messagePolicy,
42752
+ renameCommand: launch.renameCommand,
41967
42753
  activeSocket: null,
41968
42754
  cols: DEFAULT_COLS,
41969
42755
  rows: DEFAULT_ROWS
@@ -42028,7 +42814,7 @@ function createTerminalSessionManager(deps) {
42028
42814
  await runLaunchCleanup(session.cleanup);
42029
42815
  }
42030
42816
  }
42031
- return { canAttach, createSession, attach, getSessionMessagePolicy, terminate, stop, writeToSession };
42817
+ return { canAttach, createSession, attach, getSessionMessagePolicy, getSessionRenameCommand, terminate, stop, writeToSession };
42032
42818
  }
42033
42819
  async function runLaunchCleanup(cleanup) {
42034
42820
  try {
@@ -42058,7 +42844,7 @@ var DEFAULT_TICKET_TTL_MS = 1e4;
42058
42844
  function createTerminalTicketRegistry(deps = {}) {
42059
42845
  const ttlMs = deps.ttlMs ?? DEFAULT_TICKET_TTL_MS;
42060
42846
  const now = deps.now ?? Date.now;
42061
- const randomTicket = deps.randomTicket ?? (() => crypto3.randomBytes(32).toString("base64url"));
42847
+ const randomTicket = deps.randomTicket ?? (() => crypto5.randomBytes(32).toString("base64url"));
42062
42848
  const tickets = /* @__PURE__ */ new Map();
42063
42849
  function issue3(context) {
42064
42850
  prune();
@@ -42246,6 +43032,26 @@ var TheaterRegistry = class {
42246
43032
  this.#mruId = id;
42247
43033
  return item;
42248
43034
  }
43035
+ load(items) {
43036
+ this.restore(items);
43037
+ }
43038
+ restore(items) {
43039
+ const restored = /* @__PURE__ */ new Map();
43040
+ let mruId = null;
43041
+ for (const item of items) {
43042
+ const existing = restored.get(item.id);
43043
+ if (existing && existing.realpath !== item.realpath) {
43044
+ throw new Error("theater_id_collision");
43045
+ }
43046
+ restored.set(item.id, item);
43047
+ if (!mruId || item.lastOpenedAt.localeCompare(restored.get(mruId)?.lastOpenedAt ?? "") > 0) {
43048
+ mruId = item.id;
43049
+ }
43050
+ }
43051
+ this.#items.clear();
43052
+ for (const [id, item] of restored) this.#items.set(id, item);
43053
+ this.#mruId = mruId;
43054
+ }
42249
43055
  get(id) {
42250
43056
  return this.#items.get(id) ?? null;
42251
43057
  }
@@ -42255,6 +43061,11 @@ var TheaterRegistry = class {
42255
43061
  list() {
42256
43062
  return [...this.#items.values()].sort((left, right) => right.lastOpenedAt.localeCompare(left.lastOpenedAt));
42257
43063
  }
43064
+ remove(id) {
43065
+ const removed = this.#items.delete(id);
43066
+ if (this.#mruId === id) this.#mruId = this.list()[0]?.id ?? null;
43067
+ return removed;
43068
+ }
42258
43069
  };
42259
43070
 
42260
43071
  // src/update-check.ts
@@ -42331,6 +43142,9 @@ function createConsoleServer(deps = {}) {
42331
43142
  const folderGrants = createFolderGrantStore();
42332
43143
  const infraServices = createInfraServices();
42333
43144
  const dataDir = deps.dataDir ?? getFleetDataDir();
43145
+ initStore(dataDir);
43146
+ const durablePaths = createConsoleDataPaths({ fleetDataDir: dataDir });
43147
+ const durableStateStore = createConsoleDurableStateStore({ paths: durablePaths });
42334
43148
  const authEnvResolver = (cli) => resolveAuthEnv(cli, { authService: infraServices.authService });
42335
43149
  const ownsAgentRuntime = deps.agentRuntime === void 0;
42336
43150
  const agentRuntime = deps.agentRuntime ?? createFleetAgentRuntimeLifecycle({
@@ -42369,10 +43183,18 @@ function createConsoleServer(deps = {}) {
42369
43183
  }),
42370
43184
  startShell: deps.terminalStartShell,
42371
43185
  maxSessions: deps.maxTerminalSessions,
42372
- // PTY가 종료되면 콘솔 세션 목록에서도 제거해 잔존/재실행을 막는다.
42373
43186
  onSessionExit: (sessionId) => {
42374
43187
  pendingRuntimeSessions.delete(sessionId);
42375
- observability.removeTerminalSession(sessionId);
43188
+ const operation = observability.getDurableOperation(sessionId);
43189
+ const providerSession = operation?.providerSession ?? readProviderSessionCapture(sessionId, { capturesDir: durablePaths.capturesDir });
43190
+ if (providerSession) {
43191
+ observability.updateTerminalSessionProviderSession(sessionId, providerSession);
43192
+ const dormant = observability.transitionTerminalSessionToDormant(sessionId, providerSession);
43193
+ if (dormant) observability.notifySessionUpdated(dormant);
43194
+ } else {
43195
+ observability.removeTerminalSession(sessionId);
43196
+ }
43197
+ persistDurableState();
42376
43198
  }
42377
43199
  });
42378
43200
  const unsubscribeCarrierReminderRouter = createCarrierResultReminderRouter({
@@ -42404,6 +43226,12 @@ function createConsoleServer(deps = {}) {
42404
43226
  let activeEndpoint = null;
42405
43227
  let agentRuntimeStopped = false;
42406
43228
  let consoleResourcesDisposed = false;
43229
+ const carrierSettingsRouter = createCarrierSettingsRouter({
43230
+ registry: carrierRegistry,
43231
+ isAuthorized: isTerminalAuthorized,
43232
+ readJsonBody,
43233
+ writeJson
43234
+ });
42407
43235
  function handleRequest(req, res) {
42408
43236
  const pathname = getPathname(req);
42409
43237
  if (pathname === "/console/codex" || pathname.startsWith("/console/codex/")) {
@@ -42431,6 +43259,11 @@ function createConsoleServer(deps = {}) {
42431
43259
  runAsyncHandler(handleTerminalSessions(req, res), res);
42432
43260
  return;
42433
43261
  }
43262
+ const terminalSessionResumeMatch = pathname.match(/^\/terminal\/sessions\/([^/]+)\/resume$/);
43263
+ if (terminalSessionResumeMatch) {
43264
+ runAsyncHandler(handleTerminalSessionResume(req, res, decodeURIComponent(terminalSessionResumeMatch[1] ?? "")), res);
43265
+ return;
43266
+ }
42434
43267
  const terminalSessionItemMatch = pathname.match(/^\/terminal\/sessions\/([^/]+)$/);
42435
43268
  if (terminalSessionItemMatch) {
42436
43269
  runAsyncHandler(handleTerminalSessionItem(req, res, decodeURIComponent(terminalSessionItemMatch[1] ?? "")), res);
@@ -42440,6 +43273,11 @@ function createConsoleServer(deps = {}) {
42440
43273
  runAsyncHandler(handleObserverTheaters(req, res), res);
42441
43274
  return;
42442
43275
  }
43276
+ const theaterItemMatch = pathname.match(/^\/observer\/theaters\/([^/]+)$/);
43277
+ if (theaterItemMatch) {
43278
+ runAsyncHandler(handleObserverTheaterItem(req, res, decodeURIComponent(theaterItemMatch[1] ?? "")), res);
43279
+ return;
43280
+ }
42443
43281
  const theaterSessionMatch = pathname.match(/^\/observer\/theaters\/([^/]+)\/sessions$/);
42444
43282
  if (theaterSessionMatch) {
42445
43283
  runAsyncHandler(handleObserverTheaterSessions(req, res, decodeURIComponent(theaterSessionMatch[1] ?? "")), res);
@@ -42453,6 +43291,10 @@ function createConsoleServer(deps = {}) {
42453
43291
  handleObserverCarriers(req, res);
42454
43292
  return;
42455
43293
  }
43294
+ if (pathname === "/carrier-settings" || pathname.startsWith("/carrier-settings/")) {
43295
+ runAsyncBooleanHandler(carrierSettingsRouter({ req, res, pathname }), res);
43296
+ return;
43297
+ }
42456
43298
  if (pathname === "/observer/tenants") {
42457
43299
  handleObserverWorkspaces(req, res);
42458
43300
  return;
@@ -42571,6 +43413,17 @@ function createConsoleServer(deps = {}) {
42571
43413
  }
42572
43414
  await createTerminalSessionForCwd(cwd, res, cliId);
42573
43415
  }
43416
+ function injectRenameCommand(sessionId, label) {
43417
+ if (!label) return;
43418
+ const renameCommand = terminalSessions.getSessionRenameCommand(sessionId);
43419
+ if (!renameCommand) return;
43420
+ const safeLabel = sanitizeCarrierResultReminder(label.replace(/[\r\n\t]+/g, " ")).trim();
43421
+ if (safeLabel.length === 0) return;
43422
+ const policy = terminalSessions.getSessionMessagePolicy(sessionId) ?? {};
43423
+ for (const chunk of formatCarrierResultReminderMessage(policy, `${renameCommand} ${safeLabel}`)) {
43424
+ terminalSessions.writeToSession(sessionId, chunk);
43425
+ }
43426
+ }
42574
43427
  async function handleTerminalSessionItem(req, res, sessionId) {
42575
43428
  if (req.method !== "DELETE" && req.method !== "PATCH") {
42576
43429
  writeJson(res, 405, { error: "Method not allowed" });
@@ -42592,13 +43445,62 @@ function createConsoleServer(deps = {}) {
42592
43445
  return;
42593
43446
  }
42594
43447
  observability.notifySessionUpdated(updated);
43448
+ injectRenameCommand(sessionId, updated.label);
43449
+ persistDurableState();
42595
43450
  writeJson(res, 200, updated);
42596
43451
  return;
42597
43452
  }
42598
- terminalSessions.terminate(sessionId);
42599
- observability.removeTerminalSession(sessionId);
43453
+ forgetTerminalSession(sessionId);
42600
43454
  writeJson(res, 200, { ok: true });
42601
43455
  }
43456
+ async function handleTerminalSessionResume(req, res, sessionId) {
43457
+ if (req.method !== "POST") {
43458
+ writeJson(res, 405, { error: "Method not allowed" });
43459
+ return;
43460
+ }
43461
+ if (!isTerminalAuthorized(req)) {
43462
+ writeJson(res, 401, { error: "unauthorized" });
43463
+ return;
43464
+ }
43465
+ const dormantSession = observability.listTerminalSessions().find((session) => session.sessionId === sessionId && session.status === "dormant");
43466
+ const operation = dormantSession ? observability.getDurableOperation(sessionId) : null;
43467
+ if (!operation) {
43468
+ writeJson(res, 404, { error: "session_not_found" });
43469
+ return;
43470
+ }
43471
+ const cliId = readDurableAgentCliId(operation);
43472
+ if (!cliId) {
43473
+ writeJson(res, 409, { error: "resume_unavailable" });
43474
+ return;
43475
+ }
43476
+ const providerSession = operation.providerSession ?? readProviderSessionCapture(sessionId, { capturesDir: durablePaths.capturesDir });
43477
+ if (!providerSession) {
43478
+ writeJson(res, 409, { error: "resume_unavailable" });
43479
+ return;
43480
+ }
43481
+ observability.updateTerminalSessionProviderSession(sessionId, providerSession);
43482
+ const starting = observability.updateTerminalSessionStatus(sessionId, "starting");
43483
+ if (starting) observability.notifySessionUpdated(starting);
43484
+ try {
43485
+ await terminalSessions.createSession({ sessionId, cwd: operation.cwd, cliId, resumeSessionId: providerSession.sessionId });
43486
+ const runtimeSession = pendingRuntimeSessions.get(sessionId);
43487
+ pendingRuntimeSessions.delete(sessionId);
43488
+ const resumed = runtimeSession ? observability.registerTerminalRuntimeSession(runtimeSession) ?? starting : observability.updateTerminalSessionStatus(sessionId, "terminal-only") ?? starting;
43489
+ if (!resumed) {
43490
+ writeJson(res, 404, { error: "session_not_found" });
43491
+ return;
43492
+ }
43493
+ observability.notifySessionUpdated(resumed);
43494
+ persistDurableState();
43495
+ writeJson(res, 200, resumed);
43496
+ } catch {
43497
+ pendingRuntimeSessions.delete(sessionId);
43498
+ const reverted = observability.updateTerminalSessionStatus(sessionId, "dormant");
43499
+ if (reverted) observability.notifySessionUpdated(reverted);
43500
+ persistDurableState();
43501
+ writeJson(res, 503, { error: "terminal_unavailable" });
43502
+ }
43503
+ }
42602
43504
  async function handleObserverTheaters(req, res) {
42603
43505
  if (req.method === "GET") {
42604
43506
  writeJson(res, 200, { theaters: listTheaterInfos(), agentClis: getAgentCliMetadata() });
@@ -42631,8 +43533,28 @@ function createConsoleServer(deps = {}) {
42631
43533
  console.warn(`[fleet-console] Codex workspace registration skipped for Theater ${theater.id}: ${error51 instanceof Error ? error51.message : String(error51)}`);
42632
43534
  }
42633
43535
  }
43536
+ persistDurableState();
42634
43537
  writeJson(res, 200, toTheaterInfo(theater, hasWiki));
42635
43538
  }
43539
+ async function handleObserverTheaterItem(req, res, theaterId) {
43540
+ if (req.method !== "DELETE") {
43541
+ writeJson(res, 405, { error: "Method not allowed" });
43542
+ return;
43543
+ }
43544
+ if (!isTerminalAuthorized(req)) {
43545
+ writeJson(res, 401, { error: "unauthorized" });
43546
+ return;
43547
+ }
43548
+ const operations = observability.listDurableOperations().filter((operation) => operation.theaterId === theaterId);
43549
+ const removed = theaters.remove(theaterId);
43550
+ if (!removed) {
43551
+ writeJson(res, 404, { error: "theater_not_found" });
43552
+ return;
43553
+ }
43554
+ for (const operation of operations) forgetTerminalSession(operation.sessionId, { persist: false });
43555
+ persistDurableState();
43556
+ writeJson(res, 200, { ok: true });
43557
+ }
42636
43558
  async function handleObserverTheaterSessions(req, res, theaterId) {
42637
43559
  if (req.method !== "POST") {
42638
43560
  writeJson(res, 405, { error: "Method not allowed" });
@@ -42653,7 +43575,7 @@ function createConsoleServer(deps = {}) {
42653
43575
  await createTerminalSessionForCwd(theater.path, res, cliId);
42654
43576
  }
42655
43577
  async function createTerminalSessionForCwd(cwd, res, cliId) {
42656
- const sessionId = crypto3.randomUUID();
43578
+ const sessionId = crypto5.randomUUID();
42657
43579
  const session = observability.createPendingTerminalSession({ sessionId, cwd, cliId });
42658
43580
  try {
42659
43581
  await terminalSessions.createSession({ sessionId, cwd, cliId });
@@ -42661,6 +43583,7 @@ function createConsoleServer(deps = {}) {
42661
43583
  pendingRuntimeSessions.delete(sessionId);
42662
43584
  const created = runtimeSession ? observability.registerTerminalRuntimeSession(runtimeSession) ?? session : observability.updateTerminalSessionStatus(sessionId, "terminal-only") ?? session;
42663
43585
  observability.notifySessionUpdated(created);
43586
+ persistDurableState();
42664
43587
  writeJson(res, 200, created);
42665
43588
  } catch (error51) {
42666
43589
  pendingRuntimeSessions.delete(sessionId);
@@ -42734,6 +43657,13 @@ function createConsoleServer(deps = {}) {
42734
43657
  function listTheaterInfos() {
42735
43658
  return theaters.list().map((theater) => toTheaterInfo(theater, codex.getWorkspace(theater.id) !== null));
42736
43659
  }
43660
+ function forgetTerminalSession(sessionId, options = {}) {
43661
+ terminalSessions.terminate(sessionId);
43662
+ pendingRuntimeSessions.delete(sessionId);
43663
+ observability.removeTerminalSession(sessionId);
43664
+ unlinkProviderSessionCapture(sessionId, { capturesDir: durablePaths.capturesDir });
43665
+ if (options.persist !== false) persistDurableState();
43666
+ }
42737
43667
  function toTheaterInfo(theater, hasWiki) {
42738
43668
  return {
42739
43669
  id: theater.id,
@@ -42796,12 +43726,69 @@ function createConsoleServer(deps = {}) {
42796
43726
  terminalUpgrade.close();
42797
43727
  currentLock?.release();
42798
43728
  }
43729
+ function rehydrateDurableState() {
43730
+ let state;
43731
+ try {
43732
+ state = durableStateStore.load();
43733
+ theaters.restore(state.theaters);
43734
+ } catch (error51) {
43735
+ console.warn(`[fleet-console] Durable state restore skipped: ${error51 instanceof Error ? error51.message : String(error51)}`);
43736
+ state = emptyDurableConsoleState();
43737
+ theaters.restore([]);
43738
+ }
43739
+ const merged = mergeProviderSessionCaptures(state, { capturesDir: durablePaths.capturesDir });
43740
+ syncProviderSessionsToObservability(merged);
43741
+ const restorable = {
43742
+ ...merged,
43743
+ operations: merged.operations.filter((operation) => operation.providerSession)
43744
+ };
43745
+ if (merged !== state || restorable.operations.length !== merged.operations.length) {
43746
+ try {
43747
+ durableStateStore.save(restorable);
43748
+ cleanupProviderSessionCaptures(restorable, { capturesDir: durablePaths.capturesDir });
43749
+ } catch (error51) {
43750
+ console.warn(`[fleet-console] Durable state capture merge was not persisted: ${error51 instanceof Error ? error51.message : String(error51)}`);
43751
+ }
43752
+ } else {
43753
+ cleanupProviderSessionCaptures(restorable, { capturesDir: durablePaths.capturesDir });
43754
+ }
43755
+ for (const operation of restorable.operations) {
43756
+ if (theaters.get(operation.theaterId)) observability.injectDormantOperation(operation);
43757
+ }
43758
+ }
43759
+ function persistDurableState() {
43760
+ try {
43761
+ const state = mergeProviderSessionCaptures({
43762
+ version: 1,
43763
+ theaters: theaters.list(),
43764
+ operations: observability.listDurableOperations()
43765
+ }, { capturesDir: durablePaths.capturesDir });
43766
+ syncProviderSessionsToObservability(state);
43767
+ const operationsWithProviderSession = state.operations.filter((operation) => operation.providerSession);
43768
+ durableStateStore.save({
43769
+ version: state.version,
43770
+ theaters: state.theaters,
43771
+ // 대화 없는 빈 Operation 드롭은 rehydrate에서 capture 머지 뒤에만 수행한다.
43772
+ // create 시점의 메타데이터를 먼저 남겨야 이후 도착한 capture 파일이 재시작 때 병합될 수 있다.
43773
+ operations: state.operations
43774
+ });
43775
+ cleanupProviderSessionCaptures({ ...state, operations: operationsWithProviderSession }, { capturesDir: durablePaths.capturesDir });
43776
+ } catch (error51) {
43777
+ console.warn(`[fleet-console] Durable state save failed: ${error51 instanceof Error ? error51.message : String(error51)}`);
43778
+ }
43779
+ }
43780
+ function syncProviderSessionsToObservability(state) {
43781
+ for (const operation of state.operations) {
43782
+ if (operation.providerSession) observability.updateTerminalSessionProviderSession(operation.sessionId, operation.providerSession);
43783
+ }
43784
+ }
42799
43785
  return {
42800
43786
  host,
42801
43787
  port,
42802
43788
  async start(lockPaths) {
42803
43789
  if (server && lockHandle) return lockHandle.payload.endpoint;
42804
43790
  try {
43791
+ rehydrateDurableState();
42805
43792
  await new Promise((resolve, reject) => {
42806
43793
  const srv = createHttpServer(handleRequest, terminalUpgrade);
42807
43794
  srv.once("error", reject);
@@ -42884,6 +43871,14 @@ function resolveCarrierEventOrigin(event, jobOriginById) {
42884
43871
  function queueOriginCleanup(jobId, jobOriginById) {
42885
43872
  queueMicrotask(() => jobOriginById.delete(jobId));
42886
43873
  }
43874
+ function readDurableAgentCliId(operation) {
43875
+ if (!operation.cliId) return null;
43876
+ try {
43877
+ return parseAgentCliId(operation.cliId) ?? null;
43878
+ } catch {
43879
+ return null;
43880
+ }
43881
+ }
42887
43882
  function createHttpServer(handler, terminalUpgrade) {
42888
43883
  const srv = http.createServer(handler);
42889
43884
  srv.timeout = SERVER_TIMEOUT_MS;
@@ -43001,6 +43996,78 @@ function isAllowedTerminalOrigin(req, expectedPort) {
43001
43996
  if (origin === void 0) return true;
43002
43997
  return origin === `http://127.0.0.1:${expectedPort}`;
43003
43998
  }
43999
+ var CAPTURE_TEMP_PREFIX = ".capture.";
44000
+ async function runCaptureSessionHook(provider, env = process5.env) {
44001
+ return captureSession({
44002
+ diagnostics: process5.stderr,
44003
+ env,
44004
+ input: await readStdin(),
44005
+ provider
44006
+ });
44007
+ }
44008
+ function captureSession(options) {
44009
+ try {
44010
+ return captureSessionStrict(options);
44011
+ } catch (error51) {
44012
+ options.diagnostics?.write(`[fleet-console] capture-session skipped: ${error51 instanceof Error ? error51.message : String(error51)}
44013
+ `);
44014
+ return null;
44015
+ }
44016
+ }
44017
+ function captureSessionStrict(options) {
44018
+ const provider = parseProvider(options.provider);
44019
+ const fleetSessionId = readFleetSessionId(options.env ?? process5.env);
44020
+ const input = parseHookInput(options.input ?? "");
44021
+ const providerSession = toProviderSession(provider, input, options.now ?? (() => /* @__PURE__ */ new Date()));
44022
+ const capturesDir = (options.paths ?? createConsoleDataPaths()).capturesDir;
44023
+ const finalPath = path93__default.join(capturesDir, `${fleetSessionId}.json`);
44024
+ const tempPath = path93__default.join(capturesDir, `${CAPTURE_TEMP_PREFIX}${fleetSessionId}.${process5.pid}.${Date.now()}.tmp`);
44025
+ fs5__default.mkdirSync(capturesDir, { recursive: true, mode: 448 });
44026
+ fs5__default.writeFileSync(tempPath, `${JSON.stringify(providerSession, null, 2)}
44027
+ `, { mode: 384 });
44028
+ fs5__default.renameSync(tempPath, finalPath);
44029
+ return { path: finalPath, providerSession };
44030
+ }
44031
+ function parseProvider(value) {
44032
+ if (value === "claude" || value === "codex") return value;
44033
+ throw new Error("invalid_provider");
44034
+ }
44035
+ function readFleetSessionId(env) {
44036
+ const value = env.FLEET_CONSOLE_SESSION_ID;
44037
+ if (!value) throw new Error("missing_fleet_session_id");
44038
+ if (path93__default.basename(value) !== value || value.includes(path93__default.sep) || value.includes(path93__default.posix.sep)) {
44039
+ throw new Error("invalid_fleet_session_id");
44040
+ }
44041
+ return value;
44042
+ }
44043
+ function parseHookInput(input) {
44044
+ try {
44045
+ const parsed = JSON.parse(input);
44046
+ if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) return parsed;
44047
+ } catch {
44048
+ }
44049
+ throw new Error("invalid_hook_input");
44050
+ }
44051
+ function toProviderSession(provider, input, now) {
44052
+ if (typeof input.session_id !== "string" || input.session_id.length === 0) throw new Error("missing_provider_session_id");
44053
+ return {
44054
+ provider,
44055
+ sessionId: input.session_id,
44056
+ ...typeof input.transcript_path === "string" && input.transcript_path.length > 0 ? { transcriptPath: input.transcript_path } : {},
44057
+ ...typeof input.source === "string" && input.source.length > 0 ? { source: input.source } : {},
44058
+ capturedAt: now().toISOString()
44059
+ };
44060
+ }
44061
+ function readStdin() {
44062
+ return new Promise((resolve, reject) => {
44063
+ const chunks = [];
44064
+ process5.stdin.on("data", (chunk) => {
44065
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
44066
+ });
44067
+ process5.stdin.on("error", reject);
44068
+ process5.stdin.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
44069
+ });
44070
+ }
43004
44071
  function createConsoleStalePolicy(deps = {}) {
43005
44072
  const fsImpl = deps.fs ?? fs5__default;
43006
44073
  function isBuildStale(lock, buildFile) {
@@ -43018,7 +44085,7 @@ function createConsoleStalePolicy(deps = {}) {
43018
44085
  var FIXED_HOST = "127.0.0.1";
43019
44086
  var HELP_BANNER_INDENT = " ";
43020
44087
  var DEFAULT_HELP_RELEASE = "local";
43021
- var CONSOLE_HOOK_COMMANDS = /* @__PURE__ */ new Set(["subagents-context"]);
44088
+ var CONSOLE_HOOK_COMMANDS = /* @__PURE__ */ new Set(["capture-session", "subagents-context"]);
43022
44089
  function parseConsoleCliMode(argv) {
43023
44090
  if (argv.length === 0) return "start";
43024
44091
  const [first, ...rest] = argv;
@@ -43045,10 +44112,12 @@ Run 'fleet console --help' for usage.`);
43045
44112
  }
43046
44113
  function parseConsoleHookCommand(argv) {
43047
44114
  const [commandName, ...rest] = argv;
43048
- if (!commandName || !CONSOLE_HOOK_COMMANDS.has(commandName) || rest.length > 0) {
44115
+ if (!commandName || !CONSOLE_HOOK_COMMANDS.has(commandName)) {
43049
44116
  throw new Error("Unknown fleet-console hook command");
43050
44117
  }
43051
- return "subagents-context";
44118
+ if (commandName === "subagents-context" && rest.length === 0) return { command: "subagents-context" };
44119
+ if (commandName === "capture-session" && rest.length === 1 && rest[0]) return { command: "capture-session", provider: rest[0] };
44120
+ throw new Error("Unknown fleet-console hook command");
43052
44121
  }
43053
44122
  function buildConsoleHelpText(options = {}) {
43054
44123
  const colorEnabled = resolveColorEnabled(options);
@@ -43110,8 +44179,8 @@ function buildWikiHelpText(options = {}) {
43110
44179
  return colorEnabled ? text : stripAnsi(text);
43111
44180
  }
43112
44181
  function createConsoleDaemonLifecycle(deps = {}) {
43113
- const env = deps.env ?? process4.env;
43114
- const execPath = deps.execPath ?? process4.execPath;
44182
+ const env = deps.env ?? process5.env;
44183
+ const execPath = deps.execPath ?? process5.execPath;
43115
44184
  const serverModulePath = deps.serverModulePath ?? resolveDefaultServerModulePath();
43116
44185
  const spawnDetached = deps.spawnDetached ?? ((bin, args, options) => {
43117
44186
  spawn(bin, [...args], options).unref();
@@ -43125,10 +44194,10 @@ function createConsoleDaemonLifecycle(deps = {}) {
43125
44194
  const server = createConsoleServer();
43126
44195
  await server.start(paths);
43127
44196
  await new Promise((resolve) => {
43128
- process4.once("SIGTERM", () => {
44197
+ process5.once("SIGTERM", () => {
43129
44198
  void server.stop().finally(resolve);
43130
44199
  });
43131
- process4.once("SIGINT", () => {
44200
+ process5.once("SIGINT", () => {
43132
44201
  void server.stop().finally(resolve);
43133
44202
  });
43134
44203
  });
@@ -43142,14 +44211,14 @@ function createConsoleDaemonLifecycle(deps = {}) {
43142
44211
  const payload = readTrustedLock();
43143
44212
  if (!payload) return;
43144
44213
  try {
43145
- process4.kill(payload.pid, "SIGTERM");
44214
+ process5.kill(payload.pid, "SIGTERM");
43146
44215
  } catch (err) {
43147
44216
  if (err.code !== "ESRCH") throw err;
43148
44217
  }
43149
44218
  await sleep(200);
43150
44219
  try {
43151
- process4.kill(payload.pid, 0);
43152
- process4.kill(payload.pid, "SIGKILL");
44220
+ process5.kill(payload.pid, 0);
44221
+ process5.kill(payload.pid, "SIGKILL");
43153
44222
  } catch (err) {
43154
44223
  if (err.code !== "ESRCH") throw err;
43155
44224
  }
@@ -43267,49 +44336,51 @@ async function runConsoleRestart(deps = {}) {
43267
44336
  return openFleetConsole({ lifecycle, openBrowser: deps.openBrowser });
43268
44337
  }
43269
44338
  async function main() {
43270
- if (process4.argv[2] === "serve") {
44339
+ if (process5.argv[2] === "serve") {
43271
44340
  await createConsoleDaemonLifecycle().runServer();
43272
44341
  return;
43273
44342
  }
43274
- if (process4.argv[2] === "hook") {
43275
- parseConsoleHookCommand(process4.argv.slice(3));
43276
- {
43277
- process4.stdout.write(`${runSubagentsContextHook(process4.env)}
44343
+ if (process5.argv[2] === "hook") {
44344
+ const hookCommand = parseConsoleHookCommand(process5.argv.slice(3));
44345
+ if (hookCommand.command === "subagents-context") {
44346
+ process5.stdout.write(`${runSubagentsContextHook(process5.env)}
43278
44347
  `);
43279
44348
  return;
43280
44349
  }
44350
+ await runCaptureSessionHook(hookCommand.provider, process5.env);
44351
+ return;
43281
44352
  }
43282
- if (process4.argv[2] === "codex") {
43283
- await mainWiki(process4.argv.slice(3));
44353
+ if (process5.argv[2] === "codex") {
44354
+ await mainWiki(process5.argv.slice(3));
43284
44355
  return;
43285
44356
  }
43286
- const mode = parseConsoleCliMode(process4.argv.slice(2));
44357
+ const mode = parseConsoleCliMode(process5.argv.slice(2));
43287
44358
  if (mode === "help") {
43288
- process4.stdout.write(`${buildConsoleHelpText({ env: process4.env, isTTY: process4.stdout.isTTY })}
44359
+ process5.stdout.write(`${buildConsoleHelpText({ env: process5.env, isTTY: process5.stdout.isTTY })}
43289
44360
  `);
43290
44361
  return;
43291
44362
  }
43292
44363
  if (mode === "status") {
43293
- process4.stdout.write(`${await runConsoleStatus()}
44364
+ process5.stdout.write(`${await runConsoleStatus()}
43294
44365
  `);
43295
44366
  return;
43296
44367
  }
43297
44368
  if (mode === "stop") {
43298
- process4.stdout.write(`${await runConsoleStop()}
44369
+ process5.stdout.write(`${await runConsoleStop()}
43299
44370
  `);
43300
44371
  return;
43301
44372
  }
43302
44373
  if (mode === "restart") {
43303
44374
  await runConsoleRestart();
43304
- process4.stdout.write("Fleet Console restarted.\n");
44375
+ process5.stdout.write("Fleet Console restarted.\n");
43305
44376
  return;
43306
44377
  }
43307
44378
  await openFleetConsole();
43308
- process4.stdout.write("Fleet Console opened.\n");
44379
+ process5.stdout.write("Fleet Console opened.\n");
43309
44380
  }
43310
- async function mainWiki(argv = process4.argv.slice(2)) {
44381
+ async function mainWiki(argv = process5.argv.slice(2)) {
43311
44382
  if (argv.includes("--help") || argv.includes("-h")) {
43312
- process4.stdout.write(`${buildWikiHelpText({ env: process4.env, isTTY: process4.stdout.isTTY })}
44383
+ process5.stdout.write(`${buildWikiHelpText({ env: process5.env, isTTY: process5.stdout.isTTY })}
43313
44384
  `);
43314
44385
  return;
43315
44386
  }
@@ -43321,19 +44392,19 @@ async function mainWiki(argv = process4.argv.slice(2)) {
43321
44392
  if (unsupported.length > 0) {
43322
44393
  throw new Error(`Unknown fleet wiki option: ${unsupported[0]}`);
43323
44394
  }
43324
- await openFleetWikiWorkspace({ cwd: process4.cwd() });
44395
+ await openFleetWikiWorkspace({ cwd: process5.cwd() });
43325
44396
  }
43326
44397
  function resolveDefaultServerModulePath() {
43327
44398
  const builtPath = new URL("../dist/cli.mjs", import.meta.url).pathname;
43328
44399
  if (fs5__default.existsSync(builtPath)) return builtPath;
43329
44400
  return fileURLToPath(import.meta.url);
43330
44401
  }
43331
- var isDirectRun = process4.argv[1] && path93__default.resolve(process4.argv[1]) === fileURLToPath(import.meta.url);
44402
+ var isDirectRun = process5.argv[1] && path93__default.resolve(process5.argv[1]) === fileURLToPath(import.meta.url);
43332
44403
  if (isDirectRun) {
43333
44404
  await main().catch((error51) => {
43334
- process4.stderr.write(`${error51 instanceof Error ? error51.message : String(error51)}
44405
+ process5.stderr.write(`${error51 instanceof Error ? error51.message : String(error51)}
43335
44406
  `);
43336
- process4.exitCode = 1;
44407
+ process5.exitCode = 1;
43337
44408
  });
43338
44409
  }
43339
44410