@dotobokuri/fleet-console 1.59.0 → 1.61.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 (69) hide show
  1. package/AGENTS.md +1 -1
  2. package/dist/cli.d.ts +13 -2
  3. package/dist/cli.mjs +126 -30
  4. package/dist/client/assets/{_baseUniq-CKkDoWU8.js → _baseUniq-9W447fNf.js} +1 -1
  5. package/dist/client/assets/{arc-CcRdX8oF.js → arc-hoitnVWi.js} +1 -1
  6. package/dist/client/assets/{architectureDiagram-Q4EWVU46-gaFzE1W7.js → architectureDiagram-Q4EWVU46-DYd58Vfc.js} +1 -1
  7. package/dist/client/assets/{blockDiagram-DXYQGD6D-Bzybih30.js → blockDiagram-DXYQGD6D-kuFqPdP_.js} +1 -1
  8. package/dist/client/assets/{c4Diagram-AHTNJAMY-Dz8bwV9j.js → c4Diagram-AHTNJAMY-C7n41zCn.js} +1 -1
  9. package/dist/client/assets/channel-D2TsEFjE.js +1 -0
  10. package/dist/client/assets/{chunk-4BX2VUAB-DmgZtGyV.js → chunk-4BX2VUAB-DrHeb54-.js} +1 -1
  11. package/dist/client/assets/{chunk-4TB4RGXK-DX1WxV3U.js → chunk-4TB4RGXK-DFjT6kwT.js} +1 -1
  12. package/dist/client/assets/{chunk-55IACEB6-Bk4b3T9u.js → chunk-55IACEB6-DxEzuA7W.js} +1 -1
  13. package/dist/client/assets/{chunk-EDXVE4YY-BzGadfvT.js → chunk-EDXVE4YY-aAlF0YEo.js} +1 -1
  14. package/dist/client/assets/{chunk-FMBD7UC4-44f7YyvW.js → chunk-FMBD7UC4-DwqRMFL1.js} +1 -1
  15. package/dist/client/assets/{chunk-OYMX7WX6-CyUAky9W.js → chunk-OYMX7WX6-DCkUlaRi.js} +1 -1
  16. package/dist/client/assets/{chunk-QZHKN3VN-C11OUntR.js → chunk-QZHKN3VN-B1Dx8Zhl.js} +1 -1
  17. package/dist/client/assets/{chunk-YZCP3GAM-DVTmWawU.js → chunk-YZCP3GAM-J2bgJmn-.js} +1 -1
  18. package/dist/client/assets/classDiagram-6PBFFD2Q-DhjJiPic.js +1 -0
  19. package/dist/client/assets/classDiagram-v2-HSJHXN6E-DhjJiPic.js +1 -0
  20. package/dist/client/assets/clone-DwVC2-Mi.js +1 -0
  21. package/dist/client/assets/{cose-bilkent-S5V4N54A-CEatGsZv.js → cose-bilkent-S5V4N54A-BxzLzyJp.js} +1 -1
  22. package/dist/client/assets/{dagre-KV5264BT-DlrgN02H.js → dagre-KV5264BT-D1ixDJxZ.js} +1 -1
  23. package/dist/client/assets/{diagram-5BDNPKRD-BZ3hRO5t.js → diagram-5BDNPKRD-DZPIz1Pa.js} +1 -1
  24. package/dist/client/assets/{diagram-G4DWMVQ6-B-izS46x.js → diagram-G4DWMVQ6-Pbcr-a5H.js} +1 -1
  25. package/dist/client/assets/{diagram-MMDJMWI5-Cw-wnJGs.js → diagram-MMDJMWI5-CBssBmPY.js} +1 -1
  26. package/dist/client/assets/{diagram-TYMM5635-BoWnyd4_.js → diagram-TYMM5635-cVQUs6jp.js} +1 -1
  27. package/dist/client/assets/{erDiagram-SMLLAGMA-BUmyXZbP.js → erDiagram-SMLLAGMA-ChMSAXos.js} +1 -1
  28. package/dist/client/assets/{flowDiagram-DWJPFMVM-COS3kjS0.js → flowDiagram-DWJPFMVM-FEIrU5mu.js} +1 -1
  29. package/dist/client/assets/{ganttDiagram-T4ZO3ILL-BTr1fdio.js → ganttDiagram-T4ZO3ILL-D6ELHTJY.js} +1 -1
  30. package/dist/client/assets/{gitGraphDiagram-UUTBAWPF-BkGkaosN.js → gitGraphDiagram-UUTBAWPF-DTJ6HgV4.js} +1 -1
  31. package/dist/client/assets/{graph-DmbgK-mH.js → graph-uscjDdHm.js} +1 -1
  32. package/dist/client/assets/index-DMjY1OQ0.js +465 -0
  33. package/dist/client/assets/index-Ec4RaE0g.css +1 -0
  34. package/dist/client/assets/{infoDiagram-42DDH7IO-B8zI6k1l.js → infoDiagram-42DDH7IO-C2AlA___.js} +1 -1
  35. package/dist/client/assets/{ishikawaDiagram-UXIWVN3A-cIcEzrcK.js → ishikawaDiagram-UXIWVN3A-BZGfIhBp.js} +1 -1
  36. package/dist/client/assets/{journeyDiagram-VCZTEJTY-DhuabHCk.js → journeyDiagram-VCZTEJTY-DnGNMdvj.js} +1 -1
  37. package/dist/client/assets/{kanban-definition-6JOO6SKY-uHTkqxhX.js → kanban-definition-6JOO6SKY-BkuplZGR.js} +1 -1
  38. package/dist/client/assets/{layout-Cfgk_duA.js → layout-BBayH8iU.js} +1 -1
  39. package/dist/client/assets/{linear-BKvV0sr5.js → linear-BQvaHed0.js} +1 -1
  40. package/dist/client/assets/{mermaid.core-DozmrCuG.js → mermaid.core-CO0f3d6Y.js} +4 -4
  41. package/dist/client/assets/{min-ke46otlW.js → min-B610GdeY.js} +1 -1
  42. package/dist/client/assets/{mindmap-definition-QFDTVHPH-d7xzxhzf.js → mindmap-definition-QFDTVHPH-gwkhgCyj.js} +1 -1
  43. package/dist/client/assets/{pieDiagram-DEJITSTG-C4S9Jy5A.js → pieDiagram-DEJITSTG-DYU2zgk8.js} +1 -1
  44. package/dist/client/assets/{quadrantDiagram-34T5L4WZ-CoQnj3kv.js → quadrantDiagram-34T5L4WZ-B5Vkk65i.js} +1 -1
  45. package/dist/client/assets/{requirementDiagram-MS252O5E-CdopMDtg.js → requirementDiagram-MS252O5E-D4EcF7kq.js} +1 -1
  46. package/dist/client/assets/{sankeyDiagram-XADWPNL6-DdR4zdh4.js → sankeyDiagram-XADWPNL6-DFk-RMV4.js} +1 -1
  47. package/dist/client/assets/{sequenceDiagram-FGHM5R23-NeYZWVQm.js → sequenceDiagram-FGHM5R23-B-beo9e0.js} +1 -1
  48. package/dist/client/assets/{stateDiagram-FHFEXIEX-D0CQGKV_.js → stateDiagram-FHFEXIEX-VDT0TOd7.js} +1 -1
  49. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-wHPBl_p1.js +1 -0
  50. package/dist/client/assets/{timeline-definition-GMOUNBTQ-Bz2b3nfQ.js → timeline-definition-GMOUNBTQ-By4DMNqG.js} +1 -1
  51. package/dist/client/assets/{vennDiagram-DHZGUBPP-CVO2Ja80.js → vennDiagram-DHZGUBPP-DzlBrijd.js} +1 -1
  52. package/dist/client/assets/{wardley-RL74JXVD-Ba_GKs5u.js → wardley-RL74JXVD-BHSHTMU6.js} +1 -1
  53. package/dist/client/assets/{wardleyDiagram-NUSXRM2D-yP3xvrPy.js → wardleyDiagram-NUSXRM2D-COB-cWHQ.js} +1 -1
  54. package/dist/client/assets/{xychartDiagram-5P7HB3ND-BLSs6w4C.js → xychartDiagram-5P7HB3ND-C-GVSBHd.js} +1 -1
  55. package/dist/client/index.html +2 -2
  56. package/dist/fleet-plugins/file-explorer/routes.mjs +132 -13
  57. package/dist/fleet-plugins/ledger/routes.mjs +223 -16
  58. package/dist/fleet-plugins/quota/routes.mjs +33 -2
  59. package/dist/fleet-plugins/scuttlebutt/routes.mjs +5 -5
  60. package/dist/fleet-plugins/terminal/routes.mjs +653 -79
  61. package/dist/fleet.mjs +383 -66
  62. package/package.json +1 -1
  63. package/dist/client/assets/channel-CAhLkt5l.js +0 -1
  64. package/dist/client/assets/classDiagram-6PBFFD2Q-BSvLoE2t.js +0 -1
  65. package/dist/client/assets/classDiagram-v2-HSJHXN6E-BSvLoE2t.js +0 -1
  66. package/dist/client/assets/clone-o26VS_sk.js +0 -1
  67. package/dist/client/assets/index-C-3spb_b.js +0 -465
  68. package/dist/client/assets/index-FU1PGEfG.css +0 -1
  69. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-hM1GV37r.js +0 -1
@@ -20195,6 +20195,10 @@ var CLAUDE_COMPAT_CONTEXT_WINDOW = 1e6;
20195
20195
  var CLAUDE_DEFAULT_CONTEXT_WINDOW = 2e5;
20196
20196
  var CLAUDE_COMPACT_RESERVE = 32e3;
20197
20197
  var PROVIDER_COMPACT_RESERVE = 16e3;
20198
+ var COMPACT_CEILING_EARLY_PERCENT = 88;
20199
+ var COMPACT_CEILING_LATE_PERCENT = 97;
20200
+ var COMPACT_CEILING_CUSTOM_MIN = 70;
20201
+ var COMPACT_CEILING_CUSTOM_MAX = 99;
20198
20202
  var DEFAULT_MAX_JSON_BYTES = 16 * 1024 * 1024;
20199
20203
  var DEFAULT_MAX_SSE_FRAME_BYTES = 1024 * 1024;
20200
20204
  var MAX_SSE_SEPARATOR_BYTES = 4;
@@ -20212,20 +20216,41 @@ function hasClaudeOneMillionMarker(modelId) {
20212
20216
  function stripClaudeOneMillionMarker(modelId) {
20213
20217
  return modelId.replace(/\[1m\]$/i, "");
20214
20218
  }
20215
- function projectClaudeContextInputTokens(inputTokens, advertisedContextWindow, upstreamContextWindow = advertisedContextWindow) {
20219
+ function projectClaudeContextInputTokens(inputTokens, advertisedContextWindow, upstreamContextWindow = advertisedContextWindow, compactCeiling) {
20216
20220
  if (!Number.isFinite(inputTokens) || inputTokens < 0) return inputTokens;
20217
20221
  const advertisedWindow = positiveContextWindow(advertisedContextWindow);
20218
20222
  if (advertisedWindow === void 0) return inputTokens;
20219
20223
  const projectionWindow = positiveContextWindow(upstreamContextWindow) ?? advertisedWindow;
20220
20224
  const coordinate = advertisedWindow >= CLAUDE_COMPAT_CONTEXT_WINDOW ? CLAUDE_COMPAT_CONTEXT_WINDOW : CLAUDE_DEFAULT_CONTEXT_WINDOW;
20221
20225
  const claudeCompactThreshold = coordinate - CLAUDE_COMPACT_RESERVE;
20222
- const providerCompactThreshold = projectionWindow - PROVIDER_COMPACT_RESERVE;
20226
+ const providerCompactThreshold = compactThresholdTokens(projectionWindow, compactCeiling);
20223
20227
  if (providerCompactThreshold <= 0) return Math.min(coordinate, inputTokens);
20224
20228
  return Math.min(
20225
20229
  coordinate,
20226
20230
  Math.ceil(inputTokens * claudeCompactThreshold / providerCompactThreshold)
20227
20231
  );
20228
20232
  }
20233
+ function compactThresholdTokens(window, compactCeiling) {
20234
+ if (!Number.isFinite(window) || window <= 0) return window;
20235
+ const percent2 = compactCeilingPercent(compactCeiling);
20236
+ if (percent2 === void 0) return window - PROVIDER_COMPACT_RESERVE;
20237
+ return Math.floor(window * percent2 / 100);
20238
+ }
20239
+ function compactCeilingPercent(compactCeiling) {
20240
+ if (compactCeiling === "early") return COMPACT_CEILING_EARLY_PERCENT;
20241
+ if (compactCeiling === "late") return COMPACT_CEILING_LATE_PERCENT;
20242
+ if (typeof compactCeiling === "number" && Number.isInteger(compactCeiling) && compactCeiling >= COMPACT_CEILING_CUSTOM_MIN && compactCeiling <= COMPACT_CEILING_CUSTOM_MAX) {
20243
+ return compactCeiling;
20244
+ }
20245
+ return void 0;
20246
+ }
20247
+ function normalizeCompactCeiling(value) {
20248
+ if (value === "early" || value === "late") return value;
20249
+ if (typeof value === "number" && Number.isInteger(value) && value >= COMPACT_CEILING_CUSTOM_MIN && value <= COMPACT_CEILING_CUSTOM_MAX) {
20250
+ return value;
20251
+ }
20252
+ return void 0;
20253
+ }
20229
20254
  async function* projectAnthropicResponseUsage(chunks, options) {
20230
20255
  const contextWindow = positiveContextWindow(options.contextWindow);
20231
20256
  const responseModel = typeof options.responseModel === "string" && options.responseModel.length > 0 ? options.responseModel : void 0;
@@ -20239,7 +20264,8 @@ async function* projectAnthropicResponseUsage(chunks, options) {
20239
20264
  chunks,
20240
20265
  contextWindow,
20241
20266
  responseModel,
20242
- positiveLimit(options.maxSseFrameBytes, DEFAULT_MAX_SSE_FRAME_BYTES)
20267
+ positiveLimit(options.maxSseFrameBytes, DEFAULT_MAX_SSE_FRAME_BYTES),
20268
+ options.compactCeiling
20243
20269
  );
20244
20270
  return;
20245
20271
  }
@@ -20248,13 +20274,14 @@ async function* projectAnthropicResponseUsage(chunks, options) {
20248
20274
  chunks,
20249
20275
  contextWindow,
20250
20276
  responseModel,
20251
- positiveLimit(options.maxJsonBytes, DEFAULT_MAX_JSON_BYTES)
20277
+ positiveLimit(options.maxJsonBytes, DEFAULT_MAX_JSON_BYTES),
20278
+ options.compactCeiling
20252
20279
  );
20253
20280
  return;
20254
20281
  }
20255
20282
  yield* chunks;
20256
20283
  }
20257
- async function* projectJsonUsage(chunks, contextWindow, responseModel, maxBytes) {
20284
+ async function* projectJsonUsage(chunks, contextWindow, responseModel, maxBytes, compactCeiling) {
20258
20285
  const buffered = [];
20259
20286
  let bufferedBytes = 0;
20260
20287
  let passthrough = false;
@@ -20274,10 +20301,10 @@ async function* projectJsonUsage(chunks, contextWindow, responseModel, maxBytes)
20274
20301
  }
20275
20302
  if (passthrough || bufferedBytes === 0) return;
20276
20303
  const original = concatChunks(buffered, bufferedBytes);
20277
- const projected = projectJsonBytes(original, contextWindow, responseModel);
20304
+ const projected = projectJsonBytes(original, contextWindow, responseModel, compactCeiling);
20278
20305
  yield projected ?? original;
20279
20306
  }
20280
- async function* projectSseUsage(chunks, contextWindow, responseModel, maxFrameBytes) {
20307
+ async function* projectSseUsage(chunks, contextWindow, responseModel, maxFrameBytes, compactCeiling) {
20281
20308
  let buffered = new Uint8Array(0);
20282
20309
  let oversizedFrame = false;
20283
20310
  for await (const chunk of chunks) {
@@ -20304,7 +20331,7 @@ async function* projectSseUsage(chunks, contextWindow, responseModel, maxFrameBy
20304
20331
  const frame = buffered.slice(0, separator.index);
20305
20332
  const separatorBytes = buffered.slice(separator.index, separator.index + separator.length);
20306
20333
  const originalFrame = buffered.slice(0, separator.index + separator.length);
20307
- const projectedFrame = frame.byteLength > maxFrameBytes ? void 0 : projectSseFrame(frame, contextWindow, responseModel);
20334
+ const projectedFrame = frame.byteLength > maxFrameBytes ? void 0 : projectSseFrame(frame, contextWindow, responseModel, compactCeiling);
20308
20335
  yield projectedFrame === void 0 ? originalFrame : concatBytes(projectedFrame, separatorBytes);
20309
20336
  buffered = buffered.slice(separator.index + separator.length);
20310
20337
  }
@@ -20318,17 +20345,17 @@ async function* projectSseUsage(chunks, contextWindow, responseModel, maxFrameBy
20318
20345
  }
20319
20346
  if (buffered.byteLength > 0) yield buffered;
20320
20347
  }
20321
- function projectJsonBytes(bytes, contextWindow, responseModel) {
20348
+ function projectJsonBytes(bytes, contextWindow, responseModel, compactCeiling) {
20322
20349
  let parsed;
20323
20350
  try {
20324
20351
  parsed = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
20325
20352
  } catch {
20326
20353
  return void 0;
20327
20354
  }
20328
- const projected = projectAnthropicUsageEnvelope(parsed, contextWindow, responseModel);
20355
+ const projected = projectAnthropicUsageEnvelope(parsed, contextWindow, responseModel, compactCeiling);
20329
20356
  return projected.changed ? new TextEncoder().encode(JSON.stringify(projected.value)) : void 0;
20330
20357
  }
20331
- function projectSseFrame(frame, contextWindow, responseModel) {
20358
+ function projectSseFrame(frame, contextWindow, responseModel, compactCeiling) {
20332
20359
  let text2;
20333
20360
  try {
20334
20361
  text2 = new TextDecoder("utf-8", { fatal: true }).decode(frame);
@@ -20354,7 +20381,7 @@ function projectSseFrame(frame, contextWindow, responseModel) {
20354
20381
  } catch {
20355
20382
  return void 0;
20356
20383
  }
20357
- const projected = projectAnthropicUsageEnvelope(parsed, contextWindow, responseModel);
20384
+ const projected = projectAnthropicUsageEnvelope(parsed, contextWindow, responseModel, compactCeiling);
20358
20385
  if (!projected.changed) return void 0;
20359
20386
  const firstDataIndex = dataIndexes[0];
20360
20387
  const dataIndexSet = new Set(dataIndexes);
@@ -20368,19 +20395,19 @@ function projectSseFrame(frame, contextWindow, responseModel) {
20368
20395
  }
20369
20396
  return new TextEncoder().encode(outputLines.join(newline));
20370
20397
  }
20371
- function projectAnthropicUsageEnvelope(value, contextWindow, responseModel) {
20398
+ function projectAnthropicUsageEnvelope(value, contextWindow, responseModel, compactCeiling) {
20372
20399
  if (!isRecord2(value)) return { changed: false, value };
20373
20400
  let projectedValue = value;
20374
20401
  let changed = false;
20375
20402
  if (contextWindow !== void 0) {
20376
- const topLevelUsage = projectUsageProperty(projectedValue, "usage", contextWindow);
20403
+ const topLevelUsage = projectUsageProperty(projectedValue, "usage", contextWindow, compactCeiling);
20377
20404
  if (topLevelUsage.changed) {
20378
20405
  projectedValue = topLevelUsage.value;
20379
20406
  changed = true;
20380
20407
  }
20381
20408
  const message = projectedValue.message;
20382
20409
  if (isRecord2(message)) {
20383
- const messageUsage = projectUsageProperty(message, "usage", contextWindow);
20410
+ const messageUsage = projectUsageProperty(message, "usage", contextWindow, compactCeiling);
20384
20411
  if (messageUsage.changed) {
20385
20412
  projectedValue = { ...projectedValue, message: messageUsage.value };
20386
20413
  changed = true;
@@ -20400,7 +20427,7 @@ function projectAnthropicUsageEnvelope(value, contextWindow, responseModel) {
20400
20427
  }
20401
20428
  return { changed, value: projectedValue };
20402
20429
  }
20403
- function projectUsageProperty(container, property, contextWindow) {
20430
+ function projectUsageProperty(container, property, contextWindow, compactCeiling) {
20404
20431
  const usage4 = container[property];
20405
20432
  if (!isRecord2(usage4)) return { changed: false, value: container };
20406
20433
  const coordinates = ANTHROPIC_INPUT_USAGE_FIELDS.map((field) => {
@@ -20409,7 +20436,7 @@ function projectUsageProperty(container, property, contextWindow) {
20409
20436
  }).filter((entry) => entry !== void 0);
20410
20437
  if (coordinates.length === 0) return { changed: false, value: container };
20411
20438
  const total = coordinates.reduce((sum, entry) => sum + entry.tokens, 0);
20412
- const projectedTotal = projectClaudeContextInputTokens(total, contextWindow);
20439
+ const projectedTotal = projectClaudeContextInputTokens(total, contextWindow, contextWindow, compactCeiling);
20413
20440
  if (projectedTotal === total) return { changed: false, value: container };
20414
20441
  const projectedUsage = { ...usage4 };
20415
20442
  const remainderField = coordinates.some((entry) => entry.field === "input_tokens") ? "input_tokens" : coordinates[0].field;
@@ -22073,12 +22100,13 @@ function stringMetadata(metadata) {
22073
22100
  );
22074
22101
  return entries.length === 0 ? void 0 : Object.fromEntries(entries);
22075
22102
  }
22076
- function toAnthropicCacheAwareUsage(rawInputTokens, cachedInputTokens, cacheWriteInputTokens, outputTokens, advertisedContextWindow, upstreamContextWindow) {
22103
+ function toAnthropicCacheAwareUsage(rawInputTokens, cachedInputTokens, cacheWriteInputTokens, outputTokens, advertisedContextWindow, upstreamContextWindow, compactCeiling) {
22077
22104
  const safeInputTokens = Number.isFinite(rawInputTokens) && rawInputTokens > 0 ? rawInputTokens : 0;
22078
22105
  const projectedTotal = projectClaudeContextInputTokens(
22079
22106
  safeInputTokens,
22080
22107
  advertisedContextWindow,
22081
- upstreamContextWindow
22108
+ upstreamContextWindow,
22109
+ compactCeiling
22082
22110
  );
22083
22111
  if (safeInputTokens === 0) {
22084
22112
  return {
@@ -22128,7 +22156,8 @@ function anthropicUsageFromCanonical(usage4, advertisedContextWindow, options =
22128
22156
  usage4?.cache_write_input_tokens,
22129
22157
  options.forceOutputZero ? 0 : usage4?.output_tokens ?? 0,
22130
22158
  advertisedContextWindow,
22131
- usage4?.context_window
22159
+ usage4?.context_window,
22160
+ options.compactCeiling
22132
22161
  );
22133
22162
  }
22134
22163
  async function collectAnthropicMessage(events, fallbackModel, options = {}) {
@@ -22231,7 +22260,8 @@ async function collectAnthropicMessage(events, fallbackModel, options = {}) {
22231
22260
  cacheWriteInputTokens,
22232
22261
  outputTokens,
22233
22262
  options.contextWindow,
22234
- upstreamContextWindow
22263
+ upstreamContextWindow,
22264
+ options.compactCeiling
22235
22265
  )
22236
22266
  };
22237
22267
  }
@@ -22309,7 +22339,8 @@ data: ${JSON.stringify(data)}
22309
22339
  stop_reason: null,
22310
22340
  stop_sequence: null,
22311
22341
  usage: anthropicUsageFromCanonical(event.response.usage, options.contextWindow, {
22312
- forceOutputZero: true
22342
+ forceOutputZero: true,
22343
+ compactCeiling: options.compactCeiling
22313
22344
  })
22314
22345
  }
22315
22346
  });
@@ -22475,7 +22506,9 @@ data: ${JSON.stringify(data)}
22475
22506
  // Claude Code updates context occupancy from the terminal cumulative
22476
22507
  // usage frame. Sending output-only leaves Responses-backed models at
22477
22508
  // zero input usage even though Kimi's native Anthropic stream works.
22478
- usage: anthropicUsageFromCanonical(event.response.usage, options.contextWindow)
22509
+ usage: anthropicUsageFromCanonical(event.response.usage, options.contextWindow, {
22510
+ compactCeiling: options.compactCeiling
22511
+ })
22479
22512
  });
22480
22513
  yield encode3("message_stop", { type: "message_stop" });
22481
22514
  messageStopped = true;
@@ -22720,6 +22753,20 @@ function nextEventBoundary(buffer) {
22720
22753
  const match = /\r\n\r\n|\n\n|\r\r/.exec(buffer);
22721
22754
  return match === null ? void 0 : { index: match.index, length: match[0].length };
22722
22755
  }
22756
+ function nextEventBoundaryBytes(buffer) {
22757
+ for (let index = 0; index < buffer.byteLength; index += 1) {
22758
+ const first = buffer[index];
22759
+ if (first === 10) {
22760
+ if (buffer[index + 1] === 10) return { index, length: 2 };
22761
+ continue;
22762
+ }
22763
+ if (first !== 13) continue;
22764
+ if (buffer[index + 1] === 13) return { index, length: 2 };
22765
+ if (buffer[index + 1] !== 10) continue;
22766
+ if (buffer[index + 2] === 13 && buffer[index + 3] === 10) return { index, length: 4 };
22767
+ }
22768
+ return void 0;
22769
+ }
22723
22770
  function parseSseFrameFields(frame) {
22724
22771
  let eventName;
22725
22772
  const data = [];
@@ -22863,6 +22910,104 @@ async function* logCanonicalEvents(events, event) {
22863
22910
  yield item;
22864
22911
  }
22865
22912
  }
22913
+ function logRawWireEvent(label, eventName, data) {
22914
+ if (!wireLogEnabled()) return;
22915
+ wireLog(label, {
22916
+ ...eventName === void 0 ? {} : { event: eventName },
22917
+ data
22918
+ });
22919
+ }
22920
+ var RAW_EVENT_MAX_FRAME_BYTES = 1024 * 1024;
22921
+ var RAW_EVENT_MAX_JSON_BYTES = 16 * 1024 * 1024;
22922
+ async function* logRawPassthroughBody(chunks, options) {
22923
+ if (!wireLogEnabled()) {
22924
+ yield* chunks;
22925
+ return;
22926
+ }
22927
+ const mediaType = options.contentType?.split(";", 1)[0]?.trim().toLowerCase();
22928
+ if (mediaType === "text/event-stream") {
22929
+ yield* tapPassthroughSse(chunks, options.label);
22930
+ return;
22931
+ }
22932
+ if (mediaType === "application/json" || mediaType?.endsWith("+json")) {
22933
+ yield* tapPassthroughJson(chunks, options.label);
22934
+ return;
22935
+ }
22936
+ yield* chunks;
22937
+ }
22938
+ async function* tapPassthroughSse(chunks, label) {
22939
+ let pending = new Uint8Array(0);
22940
+ let oversizedFrame = false;
22941
+ for await (const chunk of chunks) {
22942
+ pending = concatBytes2(pending, chunk);
22943
+ let boundary = nextEventBoundaryBytes(pending);
22944
+ while (boundary !== void 0) {
22945
+ const frame = pending.slice(0, boundary.index);
22946
+ pending = pending.slice(boundary.index + boundary.length);
22947
+ if (!oversizedFrame && frame.byteLength <= RAW_EVENT_MAX_FRAME_BYTES) {
22948
+ tapPassthroughFrameBytes(label, frame);
22949
+ }
22950
+ oversizedFrame = false;
22951
+ boundary = nextEventBoundaryBytes(pending);
22952
+ }
22953
+ if (pending.byteLength > RAW_EVENT_MAX_FRAME_BYTES) {
22954
+ pending = pending.slice(Math.max(0, pending.byteLength - 3));
22955
+ oversizedFrame = true;
22956
+ }
22957
+ yield chunk;
22958
+ }
22959
+ if (!oversizedFrame && pending.byteLength > 0) {
22960
+ tapPassthroughFrameBytes(label, pending);
22961
+ }
22962
+ }
22963
+ function tapPassthroughFrameBytes(label, frameBytes) {
22964
+ let frame;
22965
+ try {
22966
+ frame = new TextDecoder("utf-8", { fatal: true }).decode(frameBytes);
22967
+ } catch {
22968
+ return;
22969
+ }
22970
+ const { event: eventName, data } = parseSseFrameFields(frame);
22971
+ if (data.length === 0 || data === "[DONE]") return;
22972
+ let parsed;
22973
+ try {
22974
+ parsed = JSON.parse(data);
22975
+ } catch {
22976
+ return;
22977
+ }
22978
+ logRawWireEvent(label, eventName, parsed);
22979
+ }
22980
+ async function* tapPassthroughJson(chunks, label) {
22981
+ let pending = new Uint8Array(0);
22982
+ let oversized = false;
22983
+ for await (const chunk of chunks) {
22984
+ if (!oversized) {
22985
+ if (pending.byteLength + chunk.byteLength > RAW_EVENT_MAX_JSON_BYTES) {
22986
+ oversized = true;
22987
+ pending = new Uint8Array(0);
22988
+ } else {
22989
+ pending = concatBytes2(pending, chunk);
22990
+ }
22991
+ }
22992
+ yield chunk;
22993
+ }
22994
+ if (oversized || pending.byteLength === 0) return;
22995
+ let parsed;
22996
+ try {
22997
+ parsed = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(pending));
22998
+ } catch {
22999
+ return;
23000
+ }
23001
+ logRawWireEvent(label, void 0, parsed);
23002
+ }
23003
+ function concatBytes2(left, right) {
23004
+ if (left.byteLength === 0) return right;
23005
+ if (right.byteLength === 0) return left;
23006
+ const combined = new Uint8Array(left.byteLength + right.byteLength);
23007
+ combined.set(left, 0);
23008
+ combined.set(right, left.byteLength);
23009
+ return combined;
23010
+ }
22866
23011
  var OPENAI_RESPONSES_URL = "https://api.openai.com/v1/responses";
22867
23012
  var CHATGPT_CODEX_RESPONSES_URL = "https://chatgpt.com/backend-api/codex/responses";
22868
23013
  var DEFAULT_MAX_UPSTREAM_BODY_BYTES = 64 * 1024 * 1024;
@@ -23293,6 +23438,7 @@ function parseEventFrame(frame) {
23293
23438
  `OpenAI SSE contained invalid JSON: ${error51 instanceof Error ? error51.message : String(error51)}`
23294
23439
  );
23295
23440
  }
23441
+ logRawWireEvent("openai.wire.event", eventName, parsed);
23296
23442
  if (isRecord3(parsed) && typeof parsed.type !== "string" && eventName !== void 0) {
23297
23443
  parsed = { ...parsed, type: eventName };
23298
23444
  }
@@ -23790,12 +23936,14 @@ var AnthropicMessagesGateway = class {
23790
23936
  }),
23791
23937
  body: withSseKeepAlive(encodeAnthropicSse(events, {
23792
23938
  contextWindow: options.contextWindow,
23939
+ compactCeiling: options.compactCeiling,
23793
23940
  model: request.model
23794
23941
  }))
23795
23942
  };
23796
23943
  }
23797
23944
  const message = await collectAnthropicMessage(events, request.model, {
23798
23945
  contextWindow: options.contextWindow,
23946
+ compactCeiling: options.compactCeiling,
23799
23947
  model: request.model
23800
23948
  });
23801
23949
  return {
@@ -24521,6 +24669,7 @@ function parseEventFrame2(frame) {
24521
24669
  `Grok CLI SSE contained invalid JSON: ${error51 instanceof Error ? error51.message : String(error51)}`
24522
24670
  );
24523
24671
  }
24672
+ logRawWireEvent("xai-responses.wire.event", eventName, parsed);
24524
24673
  if (isRecord5(parsed) && typeof parsed.type !== "string" && eventName !== void 0) {
24525
24674
  parsed = { ...parsed, type: eventName };
24526
24675
  }
@@ -28524,6 +28673,7 @@ ${argumentsText}`;
28524
28673
  continue;
28525
28674
  }
28526
28675
  const decodedFrame = decodeCursorServerMessage(frame.payload);
28676
+ logRawWireEvent("cursor.wire.event", void 0, decodedFrame);
28527
28677
  if (!isCursorHeartbeatFrame(decodedFrame)) noteSemanticProgress();
28528
28678
  handleFrame(decodedFrame);
28529
28679
  }
@@ -29146,12 +29296,14 @@ function normalizeAiGatewaySettings(value) {
29146
29296
  }];
29147
29297
  }) : [];
29148
29298
  const providerPriority = sanitizeProviderPriority(value.providerPriority);
29299
+ const compactCeiling = normalizeCompactCeiling(value.compactCeiling);
29149
29300
  return {
29150
29301
  version: 1,
29151
29302
  ...models.length > 0 ? { models } : {},
29152
29303
  ...value.cursorDiagnosticsEnabled === true ? { cursorDiagnosticsEnabled: true } : {},
29153
29304
  ...typeof value.wireLogEnabled === "boolean" ? { wireLogEnabled: value.wireLogEnabled } : {},
29154
- ...providerPriority ? { providerPriority: [...providerPriority] } : {}
29305
+ ...providerPriority ? { providerPriority: [...providerPriority] } : {},
29306
+ ...compactCeiling !== void 0 ? { compactCeiling } : {}
29155
29307
  };
29156
29308
  }
29157
29309
  function sanitizeProviderPriority(value) {
@@ -29757,17 +29909,19 @@ async function* translateChatCompletionsStream(body, options, toolSchemas) {
29757
29909
  while (boundary !== void 0) {
29758
29910
  const frame = buffer.slice(0, boundary.index);
29759
29911
  buffer = buffer.slice(boundary.index + boundary.length);
29760
- const data = chatFrameData(frame);
29761
- if (data !== void 0) {
29762
- yield* consumeChunk(data);
29912
+ const frameData = chatFrameData(frame);
29913
+ if (frameData !== void 0) {
29914
+ logRawWireEvent("openai-chat.wire.event", frameData.event, frameData.data);
29915
+ yield* consumeChunk(frameData.data);
29763
29916
  }
29764
29917
  boundary = nextEventBoundary(buffer);
29765
29918
  }
29766
29919
  }
29767
29920
  if (buffer.trim().length > 0) {
29768
- const data = chatFrameData(buffer);
29769
- if (data !== void 0) {
29770
- yield* consumeChunk(data);
29921
+ const frameData = chatFrameData(buffer);
29922
+ if (frameData !== void 0) {
29923
+ logRawWireEvent("openai-chat.wire.event", frameData.event, frameData.data);
29924
+ yield* consumeChunk(frameData.data);
29771
29925
  }
29772
29926
  }
29773
29927
  yield* finish();
@@ -29777,12 +29931,15 @@ async function* translateChatCompletionsStream(body, options, toolSchemas) {
29777
29931
  }
29778
29932
  }
29779
29933
  function chatFrameData(frame) {
29780
- const { data } = parseSseFrameFields(frame);
29934
+ const { event: eventName, data } = parseSseFrameFields(frame);
29781
29935
  if (data.length === 0 || data === "[DONE]") {
29782
29936
  return void 0;
29783
29937
  }
29784
29938
  try {
29785
- return JSON.parse(data);
29939
+ return {
29940
+ ...eventName === void 0 ? {} : { event: eventName },
29941
+ data: JSON.parse(data)
29942
+ };
29786
29943
  } catch (error51) {
29787
29944
  throw new UpstreamProtocolError(
29788
29945
  `Chat Completions SSE contained invalid JSON: ${error51 instanceof Error ? error51.message : String(error51)}`
@@ -29980,6 +30137,7 @@ function parseEventFrame3(frame) {
29980
30137
  `OpenCode Go SSE contained invalid JSON: ${error51 instanceof Error ? error51.message : String(error51)}`
29981
30138
  );
29982
30139
  }
30140
+ logRawWireEvent("opencode-go-responses.wire.event", eventName, parsed);
29983
30141
  if (isRecord11(parsed) && typeof parsed.type !== "string" && eventName !== void 0) {
29984
30142
  parsed = { ...parsed, type: eventName };
29985
30143
  }
@@ -30426,9 +30584,14 @@ async function proxyAnthropicMessages(res, body, options) {
30426
30584
  }
30427
30585
  const rawBody = readResponseBody(upstream.body);
30428
30586
  const contentType = upstream.headers.get("content-type");
30429
- const projectedBody = options.contextWindow === void 0 && options.responseModel === void 0 ? rawBody : projectAnthropicResponseUsage(rawBody, {
30587
+ const observedBody = options.wireEventLabel === void 0 ? rawBody : logRawPassthroughBody(rawBody, {
30588
+ label: options.wireEventLabel,
30589
+ contentType
30590
+ });
30591
+ const projectedBody = options.contextWindow === void 0 && options.responseModel === void 0 ? observedBody : projectAnthropicResponseUsage(observedBody, {
30430
30592
  contentType,
30431
30593
  contextWindow: options.contextWindow,
30594
+ compactCeiling: options.compactCeiling,
30432
30595
  responseModel: options.responseModel
30433
30596
  });
30434
30597
  const responseBody = options.keepAlive === true && contentType?.split(";", 1)[0]?.trim().toLowerCase() === "text/event-stream" ? withSseKeepAlive(projectedBody) : projectedBody;
@@ -30479,17 +30642,19 @@ function isOpencodeAnthropicPassthrough(model) {
30479
30642
  function createOpencodeGateway(wire) {
30480
30643
  return new AnthropicMessagesGateway(createOpencodeGoAdapter(wire));
30481
30644
  }
30482
- async function proxyToOpencode(requestHeaders, res, body, model, contextWindow, apiKey, fetchImpl, signal) {
30645
+ async function proxyToOpencode(requestHeaders, res, body, model, contextWindow, compactCeiling, apiKey, fetchImpl, signal) {
30483
30646
  const headers = opencodeAnthropicHeaders(requestHeaders, apiKey);
30484
30647
  const responseModel = typeof body.model === "string" ? body.model : void 0;
30485
30648
  await proxyAnthropicMessages(res, opencodeRequestBody(body, model), {
30486
30649
  contextWindow,
30650
+ compactCeiling,
30487
30651
  responseModel,
30488
30652
  keepAlive: true,
30489
30653
  fetchImpl,
30490
30654
  headers,
30491
30655
  signal,
30492
- url: OPENCODE_GO_MESSAGES_URL
30656
+ url: OPENCODE_GO_MESSAGES_URL,
30657
+ wireEventLabel: "opencode-go-anthropic.wire.event"
30493
30658
  });
30494
30659
  }
30495
30660
  var AI_GATEWAY_ROUTE_SEGMENT = "ai-gateway";
@@ -30542,6 +30707,13 @@ function createAiGatewayRouter(deps) {
30542
30707
  return false;
30543
30708
  }
30544
30709
  };
30710
+ const compactCeiling = () => {
30711
+ try {
30712
+ return gatewaySettings()?.compactCeiling;
30713
+ } catch {
30714
+ return void 0;
30715
+ }
30716
+ };
30545
30717
  const handle = async ({ req, res, pathname }) => {
30546
30718
  if (pathname.endsWith("/api/hello")) {
30547
30719
  res.writeHead(200, { "content-type": "application/json" });
@@ -30661,6 +30833,7 @@ function createAiGatewayRouter(deps) {
30661
30833
  return true;
30662
30834
  }
30663
30835
  const claudeContextWindow = target2.contextWindow;
30836
+ const ceiling = compactCeiling();
30664
30837
  if (target2.provider === "kimi") {
30665
30838
  await proxyToKimi(
30666
30839
  req.headers,
@@ -30668,6 +30841,7 @@ function createAiGatewayRouter(deps) {
30668
30841
  body,
30669
30842
  upstreamModelId(target2),
30670
30843
  claudeContextWindow,
30844
+ ceiling,
30671
30845
  credential,
30672
30846
  fetchImpl,
30673
30847
  controller.signal
@@ -30681,6 +30855,7 @@ function createAiGatewayRouter(deps) {
30681
30855
  body,
30682
30856
  upstreamModelId(target2),
30683
30857
  claudeContextWindow,
30858
+ ceiling,
30684
30859
  credential,
30685
30860
  fetchImpl,
30686
30861
  controller.signal
@@ -30693,6 +30868,7 @@ function createAiGatewayRouter(deps) {
30693
30868
  const upstream = await gateway.stream(body, {
30694
30869
  apiKey: credential,
30695
30870
  ...claudeContextWindow ? { contextWindow: claudeContextWindow } : {},
30871
+ ...ceiling === void 0 ? {} : { compactCeiling: ceiling },
30696
30872
  ...modelContextWindow === void 0 ? {} : { modelContextWindow },
30697
30873
  ...diagnosticsEnabled === void 0 ? {} : { diagnosticsEnabled },
30698
30874
  signal: controller.signal,
@@ -30736,20 +30912,23 @@ async function proxyToAnthropic(requestHeaders, res, body, fetchImpl, signal) {
30736
30912
  fetchImpl,
30737
30913
  headers,
30738
30914
  signal,
30739
- url: ANTHROPIC_MESSAGES_URL
30915
+ url: ANTHROPIC_MESSAGES_URL,
30916
+ wireEventLabel: "anthropic.wire.event"
30740
30917
  });
30741
30918
  }
30742
- async function proxyToKimi(requestHeaders, res, body, model, contextWindow, apiKey, fetchImpl, signal) {
30919
+ async function proxyToKimi(requestHeaders, res, body, model, contextWindow, compactCeiling, apiKey, fetchImpl, signal) {
30743
30920
  const headers = kimiAnthropicHeaders(requestHeaders, apiKey);
30744
30921
  const responseModel = typeof body.model === "string" ? body.model : void 0;
30745
30922
  await proxyAnthropicMessages(res, kimiRequestBody(body, model), {
30746
30923
  contextWindow,
30924
+ compactCeiling,
30747
30925
  responseModel,
30748
30926
  keepAlive: true,
30749
30927
  fetchImpl,
30750
30928
  headers,
30751
30929
  signal,
30752
- url: KIMI_MESSAGES_URL
30930
+ url: KIMI_MESSAGES_URL,
30931
+ wireEventLabel: "kimi-anthropic.wire.event"
30753
30932
  });
30754
30933
  }
30755
30934
  function createGatewayFor(model, chatgptAccountId, originator) {
@@ -30844,6 +31023,7 @@ function createAiGatewaySettingsStore(deps = {}) {
30844
31023
  // 우선순위는 이 update 계약이 나르지 않는 별도 표면의 설정이다. 이월하지 않으면
30845
31024
  // 무관한 모델 노출 저장 한 번이 사용자의 소진 순서를 지운다.
30846
31025
  ...current.providerPriority ? { providerPriority: current.providerPriority } : {},
31026
+ ...current.compactCeiling !== void 0 ? { compactCeiling: current.compactCeiling } : {},
30847
31027
  ...value ?? {}
30848
31028
  })),
30849
31029
  writeCursorDiagnosticsEnabled: (enabled) => update((current) => normalizeAiGatewaySettings({
@@ -30859,6 +31039,16 @@ function createAiGatewaySettingsStore(deps = {}) {
30859
31039
  const withoutWireLog = { ...next };
30860
31040
  delete withoutWireLog.wireLogEnabled;
30861
31041
  return withoutWireLog;
31042
+ }),
31043
+ writeCompactCeiling: (ceiling) => update((current) => {
31044
+ const next = normalizeAiGatewaySettings({
31045
+ ...current,
31046
+ ...ceiling === void 0 ? {} : { compactCeiling: ceiling }
31047
+ });
31048
+ if (ceiling !== void 0) return next;
31049
+ const withoutCeiling = { ...next };
31050
+ delete withoutCeiling.compactCeiling;
31051
+ return withoutCeiling;
30862
31052
  })
30863
31053
  };
30864
31054
  }
@@ -30887,7 +31077,7 @@ function readLegacySettings(legacyPath) {
30887
31077
  return hasStoredValue(settings2) ? { kind: "adopt", settings: settings2 } : { kind: "nothing" };
30888
31078
  }
30889
31079
  function hasStoredValue(settings2) {
30890
- return (settings2.models?.length ?? 0) > 0 || settings2.cursorDiagnosticsEnabled !== void 0 || settings2.wireLogEnabled !== void 0;
31080
+ return (settings2.models?.length ?? 0) > 0 || settings2.cursorDiagnosticsEnabled !== void 0 || settings2.wireLogEnabled !== void 0 || settings2.providerPriority !== void 0 || settings2.compactCeiling !== void 0;
30891
31081
  }
30892
31082
 
30893
31083
  // ../../packages/fleet-admiral/src/ai-gateway/auth.ts
@@ -44830,7 +45020,13 @@ function buildAgentCliLaunchKinds(metadata, operationType, gatewaySelection) {
44830
45020
  id: cli.id,
44831
45021
  type: operationType,
44832
45022
  title: cli.label,
44833
- ...disabledReason ? { disabled: true, disabledReason } : cli.id === "claude-gateway" ? { variants: buildClaudeGatewayLaunchVariants(gatewaySelection) } : {}
45023
+ ...disabledReason ? { disabled: true, disabledReason } : cli.id === "claude-gateway" ? {
45024
+ variants: buildClaudeGatewayLaunchVariants(gatewaySelection),
45025
+ // 채팅으로 태어나는 길은 SDK 인수 계약 위에 서므로 Claude Gateway 종류에서만 열린다
45026
+ // (전환 경로의 `chat_unsupported`와 같은 판정). 다른 종류는 선언하지 않으므로
45027
+ // 컴포저의 시작 뷰 선택 자체가 서지 않는다.
45028
+ launchViews: ["terminal", "chat"]
45029
+ } : {}
44834
45030
  };
44835
45031
  });
44836
45032
  }
@@ -46291,6 +46487,13 @@ function agentLaunchProviderFromModel(model) {
46291
46487
  function isAgentLaunchProvider(value) {
46292
46488
  return value === "claude" || GATEWAY_PROVIDERS.includes(value);
46293
46489
  }
46490
+ function agentLaunchProviderFromCliId(cliId) {
46491
+ if (!cliId) return void 0;
46492
+ if (cliId === "claude-gateway" || cliId === "claude") return "claude";
46493
+ const separator = cliId.indexOf(GATEWAY_MODEL_SEPARATOR);
46494
+ const candidate = separator > 0 ? cliId.slice(0, separator) : cliId;
46495
+ return GATEWAY_PROVIDERS.includes(candidate) ? candidate : void 0;
46496
+ }
46294
46497
  var BACKGROUND_PENDING_TTL_MS = 30 * 6e4;
46295
46498
  var EMPTY_SETTLED_AGENT_IDS = /* @__PURE__ */ new Set();
46296
46499
  function createConsoleObservabilityStore(deps = {}) {
@@ -46497,6 +46700,25 @@ function createConsoleObservabilityStore(deps = {}) {
46497
46700
  clearTerminalSessionBackgroundPending(session);
46498
46701
  return toTerminalSessionInfo(session);
46499
46702
  }
46703
+ function setTerminalSessionChatActive(sessionId, active) {
46704
+ const session = terminalSessionsById.get(sessionId);
46705
+ if (!session) return null;
46706
+ if (session.chatActive === true === active) return null;
46707
+ if (active) session.chatActive = true;
46708
+ else {
46709
+ delete session.chatActive;
46710
+ delete session.chatWorking;
46711
+ }
46712
+ return toTerminalSessionInfo(session);
46713
+ }
46714
+ function setTerminalSessionChatWorking(sessionId, working) {
46715
+ const session = terminalSessionsById.get(sessionId);
46716
+ if (!session || session.chatActive !== true) return null;
46717
+ if (session.chatWorking === true === working) return toTerminalSessionInfo(session);
46718
+ if (working) session.chatWorking = true;
46719
+ else delete session.chatWorking;
46720
+ return toTerminalSessionInfo(session);
46721
+ }
46500
46722
  function renameTerminalSession(sessionId, rawLabel) {
46501
46723
  const session = terminalSessionsById.get(sessionId);
46502
46724
  if (!session) return null;
@@ -46609,6 +46831,8 @@ function createConsoleObservabilityStore(deps = {}) {
46609
46831
  setTerminalSessionBackgroundPending,
46610
46832
  getTerminalSessionSettledAgentIds,
46611
46833
  setTerminalSessionModelActivity,
46834
+ setTerminalSessionChatActive,
46835
+ setTerminalSessionChatWorking,
46612
46836
  transitionTerminalSessionToDormant,
46613
46837
  removeTerminalSession,
46614
46838
  registerTerminalRuntimeSession,
@@ -46632,6 +46856,8 @@ function toTerminalSessionInfo(state) {
46632
46856
  ...state.modelActivity ? { modelActivity: state.modelActivity } : {},
46633
46857
  ...state.attentionPending === true ? { attentionPending: true } : {},
46634
46858
  ...state.backgroundPending === true ? { backgroundPending: true } : {},
46859
+ ...state.chatActive === true ? { chatActive: true } : {},
46860
+ ...state.chatWorking === true ? { chatWorking: true } : {},
46635
46861
  createdAt: state.createdAt,
46636
46862
  theaterId: state.theaterId,
46637
46863
  registrationId: state.registrationId,
@@ -46765,18 +46991,38 @@ function readAnalysisProviderSession(value) {
46765
46991
  };
46766
46992
  }
46767
46993
  var MAX_TOOL_DETAIL_CHARS = 160;
46994
+ var MAX_TOOL_RESULT_CHARS = 120;
46768
46995
  var MAX_TEXT_CHARS = 6e4;
46769
- function chatEventsFromTranscriptLine(raw, options = {}) {
46996
+ var WRITE_TOOLS = /* @__PURE__ */ new Set(["Write", "Edit", "MultiEdit", "NotebookEdit"]);
46997
+ var CONTENT_RESULT_TOOLS = /* @__PURE__ */ new Set([
46998
+ "Read",
46999
+ "NotebookRead",
47000
+ "Glob",
47001
+ "Grep",
47002
+ "WebFetch",
47003
+ "WebSearch",
47004
+ "Task",
47005
+ "Agent"
47006
+ ]);
47007
+ var PATH_KEYS = ["file_path", "path", "notebook_path"];
47008
+ function chatReplayFromTranscriptLine(raw, options = {}) {
46770
47009
  let line;
46771
47010
  try {
46772
47011
  line = JSON.parse(raw);
46773
47012
  } catch {
46774
- return [];
47013
+ return { events: [] };
46775
47014
  }
47015
+ const parsedAt = typeof line.timestamp === "string" ? Date.parse(line.timestamp) : Number.NaN;
47016
+ const events = eventsFromTranscriptLine(line, options);
47017
+ return Number.isFinite(parsedAt) ? { at: parsedAt, events } : { events };
47018
+ }
47019
+ function eventsFromTranscriptLine(line, options) {
46776
47020
  if (line.isMeta === true || line.isSidechain === true || line.isCompactSummary === true) return [];
46777
47021
  const at = typeof line.timestamp === "string" ? Date.parse(line.timestamp) : Number.NaN;
46778
47022
  const atField = Number.isFinite(at) ? { at } : {};
46779
47023
  if (line.type === "user") {
47024
+ const results = toolResultsFrom(line.message?.content, options);
47025
+ if (results.length > 0) return results;
46780
47026
  const text2 = readUserText(line.message?.content);
46781
47027
  return text2 ? [{ kind: "dispatch", text: text2, ...atField }] : [];
46782
47028
  }
@@ -46789,7 +47035,17 @@ function chatEventsFromSdkMessage(message, options = {}) {
46789
47035
  if (message.type === "stream_event") {
46790
47036
  const inner = message.event;
46791
47037
  if (!inner || typeof inner !== "object") return [];
46792
- if (inner.type !== "content_block_delta") return [];
47038
+ const innerType = inner.type;
47039
+ if (innerType === "content_block_start") {
47040
+ const block = inner.content_block;
47041
+ if (!block || typeof block !== "object") return [];
47042
+ const start = block;
47043
+ if (start.type !== "tool_use") return [];
47044
+ if (typeof start.id !== "string" || start.id.length === 0) return [];
47045
+ if (typeof start.name !== "string" || start.name.length === 0) return [];
47046
+ return [{ kind: "tool-start", id: start.id, name: start.name }];
47047
+ }
47048
+ if (innerType !== "content_block_delta") return [];
46793
47049
  const delta = inner.delta;
46794
47050
  if (!delta || typeof delta !== "object") return [];
46795
47051
  const text2 = delta;
@@ -46802,6 +47058,10 @@ function chatEventsFromSdkMessage(message, options = {}) {
46802
47058
  const body = message.message;
46803
47059
  return eventsFromAssistantContent(body?.content, options);
46804
47060
  }
47061
+ if (message.type === "user") {
47062
+ const body = message.message;
47063
+ return toolResultsFrom(body?.content, options);
47064
+ }
46805
47065
  if (message.type === "result") {
46806
47066
  const durationMs = message.duration_ms;
46807
47067
  const ok = message.is_error !== true;
@@ -46825,11 +47085,37 @@ function eventsFromAssistantContent(content, options) {
46825
47085
  continue;
46826
47086
  }
46827
47087
  if (block.type === "tool_use" && typeof block.name === "string" && block.name.length > 0) {
46828
- events.push({ kind: "tool", name: block.name, detail: summarizeToolInput(block.input, options) });
47088
+ const change = changeFromToolInput(block.name, block.input, options);
47089
+ events.push({
47090
+ kind: "tool",
47091
+ name: block.name,
47092
+ detail: summarizeToolInput(block.input, options),
47093
+ ...typeof block.id === "string" && block.id.length > 0 ? { id: block.id } : {},
47094
+ ...pathIsOutsideCwd(block.input, options.cwd) ? { outside: true } : {},
47095
+ ...change ? { change } : {}
47096
+ });
46829
47097
  }
46830
47098
  }
46831
47099
  return events;
46832
47100
  }
47101
+ function toolResultsFrom(content, options) {
47102
+ if (!Array.isArray(content)) return [];
47103
+ const events = [];
47104
+ for (const block of content) {
47105
+ if (!block || typeof block !== "object" || block.type !== "tool_result") continue;
47106
+ if (typeof block.tool_use_id !== "string" || block.tool_use_id.length === 0) continue;
47107
+ const ok = block.is_error !== true;
47108
+ const tool = options.toolNames?.get(block.tool_use_id);
47109
+ const quiet = ok && tool !== void 0 && (CONTENT_RESULT_TOOLS.has(tool) || WRITE_TOOLS.has(tool));
47110
+ events.push({
47111
+ kind: "tool-result",
47112
+ id: block.tool_use_id,
47113
+ ok,
47114
+ summary: quiet ? "" : summarizeToolResult(block.content, options)
47115
+ });
47116
+ }
47117
+ return events;
47118
+ }
46833
47119
  function readUserText(content) {
46834
47120
  if (typeof content === "string") return normalizeDispatchText(content);
46835
47121
  if (!Array.isArray(content)) return null;
@@ -46849,16 +47135,107 @@ function normalizeDispatchText(text2) {
46849
47135
  function summarizeToolInput(input, options = {}) {
46850
47136
  if (!input || typeof input !== "object" || Array.isArray(input)) return "";
46851
47137
  const record7 = input;
46852
- for (const key of ["file_path", "path", "command", "pattern", "url", "query", "description", "prompt", "subject"]) {
47138
+ for (const key of PATH_KEYS.concat(["command", "pattern", "url", "query", "description", "prompt", "subject"])) {
46853
47139
  const value = record7[key];
46854
47140
  if (typeof value === "string" && value.trim().length > 0) {
46855
47141
  const flat = value.replace(/\s+/g, " ").trim();
46856
- const shown = key === "file_path" || key === "path" ? displayPath(flat, options.cwd) : normalizePathTokens(flat, options.cwd);
47142
+ const shown = PATH_KEYS.includes(key) ? displayPath(flat, options.cwd) : normalizePathTokens(flat, options.cwd);
46857
47143
  return shown.length > MAX_TOOL_DETAIL_CHARS ? `${shown.slice(0, MAX_TOOL_DETAIL_CHARS - 1)}\u2026` : shown;
46858
47144
  }
46859
47145
  }
46860
47146
  return "";
46861
47147
  }
47148
+ function summarizeToolResult(content, options = {}) {
47149
+ const text2 = readResultText(content);
47150
+ if (text2 === null) return "";
47151
+ const first = text2.split("\n").map((line) => line.trim()).find((line) => line.length > 0);
47152
+ if (first === void 0) return "";
47153
+ const flat = normalizePathTokens(first.replace(/\s+/g, " ").trim(), options.cwd);
47154
+ const masked = maskSecrets(abbreviateAbsolutePaths(flat));
47155
+ return masked.length > MAX_TOOL_RESULT_CHARS ? `${masked.slice(0, MAX_TOOL_RESULT_CHARS - 1)}\u2026` : masked;
47156
+ }
47157
+ var QUOTED_ABSOLUTE_PATH = /(['"`])((?:[A-Za-z]:[\\/]|\\\\|\/)[^'"`\n]*)\1/g;
47158
+ var BARE_ABSOLUTE_PATH = /(?<![^\s'"`([{=<])(?:[A-Za-z]:[\\/]|\\\\|\/)[^\s'"`,;:()[\]<>]+/g;
47159
+ function abbreviateAbsolutePaths(value) {
47160
+ return value.replace(QUOTED_ABSOLUTE_PATH, (_match, quote, path46) => `${quote}${foldPath(path46)}${quote}`).replace(BARE_ABSOLUTE_PATH, (match) => foldPath(match));
47161
+ }
47162
+ function foldPath(path46) {
47163
+ const segments = path46.split(/[\\/]/).filter((segment) => segment.length > 0 && !/^[A-Za-z]:$/.test(segment));
47164
+ if (segments.length === 0) return path46;
47165
+ return `\u2026/${segments.slice(-2).join("/")}`;
47166
+ }
47167
+ function readResultText(content) {
47168
+ if (typeof content === "string") return content;
47169
+ if (!Array.isArray(content)) return null;
47170
+ const parts = [];
47171
+ for (const block of content) {
47172
+ if (block?.type === "text" && typeof block.text === "string") parts.push(block.text);
47173
+ }
47174
+ return parts.length > 0 ? parts.join("\n") : null;
47175
+ }
47176
+ function maskSecrets(value) {
47177
+ return value.replace(/\b(sk|pk|rk)-[A-Za-z0-9_-]{16,}/g, "$1-\u2026").replace(/\b(gh[pousr]|xox[baprs])_[A-Za-z0-9_-]{16,}/g, "$1_\u2026").replace(/\bAKIA[0-9A-Z]{12,}/g, "AKIA\u2026").replace(/\b(?:Bearer|Authorization:)\s+[A-Za-z0-9._~+/=-]{16,}/gi, "Bearer \u2026").replace(/\beyJ[A-Za-z0-9._-]{24,}/g, "eyJ\u2026");
47178
+ }
47179
+ function changeFromToolInput(name, input, options = {}) {
47180
+ if (!WRITE_TOOLS.has(name)) return null;
47181
+ if (!input || typeof input !== "object" || Array.isArray(input)) return null;
47182
+ const record7 = input;
47183
+ const raw = PATH_KEYS.map((key) => record7[key]).find((value) => typeof value === "string" && value.trim().length > 0) ?? null;
47184
+ if (raw === null) return null;
47185
+ const file2 = displayPath(raw.trim(), options.cwd);
47186
+ if (name === "Write") {
47187
+ return { file: file2, added: lineCount(record7.content), removed: 0 };
47188
+ }
47189
+ if (name === "Edit") {
47190
+ return { file: file2, added: lineCount(record7.new_string), removed: lineCount(record7.old_string) };
47191
+ }
47192
+ if (name === "MultiEdit") {
47193
+ let added = 0;
47194
+ let removed = 0;
47195
+ const edits = Array.isArray(record7.edits) ? record7.edits : [];
47196
+ for (const edit of edits) {
47197
+ if (!edit || typeof edit !== "object") continue;
47198
+ const entry = edit;
47199
+ added += lineCount(entry.new_string);
47200
+ removed += lineCount(entry.old_string);
47201
+ }
47202
+ return { file: file2, added, removed };
47203
+ }
47204
+ return { file: file2, added: 0, removed: 0 };
47205
+ }
47206
+ function lineCount(value) {
47207
+ if (typeof value !== "string" || value.length === 0) return 0;
47208
+ return value.replace(/\n$/, "").split("\n").length;
47209
+ }
47210
+ function pathIsOutsideCwd(input, cwd) {
47211
+ if (!cwd || !input || typeof input !== "object" || Array.isArray(input)) return false;
47212
+ const record7 = input;
47213
+ for (const key of PATH_KEYS) {
47214
+ const value = record7[key];
47215
+ if (typeof value !== "string" || value.trim().length === 0) continue;
47216
+ const normalized = resolveDotSegments(value.trim().replace(/\\/g, "/"));
47217
+ if (!normalized.startsWith("/") && !/^[A-Za-z]:\//.test(normalized)) return false;
47218
+ const root = resolveDotSegments(cwd.replace(/\\/g, "/")).replace(/\/+$/, "");
47219
+ if (root.length <= 1) return false;
47220
+ return normalized !== root && !normalized.startsWith(`${root}/`);
47221
+ }
47222
+ return false;
47223
+ }
47224
+ function resolveDotSegments(path46) {
47225
+ if (!path46.includes("./") && !path46.endsWith("/.") && !path46.endsWith("/..")) return path46;
47226
+ const parts = path46.split("/");
47227
+ const out = [];
47228
+ for (const part of parts) {
47229
+ if (part === ".") continue;
47230
+ if (part === "..") {
47231
+ if (out.length > 1 || out.length === 1 && out[0] !== "") out.pop();
47232
+ continue;
47233
+ }
47234
+ out.push(part);
47235
+ }
47236
+ const joined = out.join("/");
47237
+ return joined.length > 0 ? joined : "/";
47238
+ }
46862
47239
  function normalizePathTokens(value, cwd) {
46863
47240
  let result = value;
46864
47241
  if (cwd) {
@@ -46870,10 +47247,10 @@ function normalizePathTokens(value, cwd) {
46870
47247
  return result;
46871
47248
  }
46872
47249
  function displayPath(value, cwd) {
46873
- const normalized = value.replace(/\\/g, "/");
47250
+ const normalized = resolveDotSegments(value.replace(/\\/g, "/"));
46874
47251
  if (!normalized.startsWith("/") && !/^[A-Za-z]:\//.test(normalized)) return value;
46875
47252
  if (cwd) {
46876
- const root = cwd.replace(/\\/g, "/").replace(/\/+$/, "");
47253
+ const root = resolveDotSegments(cwd.replace(/\\/g, "/")).replace(/\/+$/, "");
46877
47254
  if (normalized === root) return ".";
46878
47255
  if (normalized.startsWith(`${root}/`)) return normalized.slice(root.length + 1);
46879
47256
  }
@@ -46886,13 +47263,17 @@ function capText(text2) {
46886
47263
 
46887
47264
  // ../fleet-plugins/terminal/server/agent-api/chat-session.ts
46888
47265
  var JOURNAL_CAP = 2e3;
47266
+ var TOOL_NAME_CAP = 500;
46889
47267
  var AgentChatSession = class {
46890
47268
  operationId;
46891
47269
  seed;
46892
47270
  createSdk;
46893
47271
  sdk = null;
46894
47272
  sdkFlight = null;
46895
- /** resume 좌표는 트랜스크립트 파일명이 말하는 세션 id다 — 캡처 id는 드리프트할 수 있다. */
47273
+ /**
47274
+ * resume 좌표는 트랜스크립트 파일명이 말하는 세션 id다 — 캡처 id는 드리프트할 수 있다.
47275
+ * `fresh`는 아직 세션이 없으므로 null로 출발하고, 첫 SDK 메시지의 session_id가 이 자리를 채운다.
47276
+ */
46896
47277
  latestSessionId;
46897
47278
  journal = [];
46898
47279
  seq = 0;
@@ -46900,27 +47281,52 @@ var AgentChatSession = class {
46900
47281
  turnFlight = Promise.resolve();
46901
47282
  pendingTurns = 0;
46902
47283
  disposed = false;
47284
+ /**
47285
+ * tool_use id → 도구 이름. 결과 블록은 자기가 어떤 도구의 결말인지 모르는데, 무엇을 요약해도
47286
+ * 되는지는 도구가 정한다(읽기·쓰기 계열의 성공 결과는 내용이라 싣지 않는다). 긴 세션에서
47287
+ * 무한히 자라지 않게 상한을 두고 오래된 것부터 버린다 — 결과는 호출 직후에 오므로 잃을 게 없다.
47288
+ */
47289
+ toolNames = /* @__PURE__ */ new Map();
46903
47290
  constructor(operationId, seed, createSdk) {
46904
47291
  this.operationId = operationId;
46905
47292
  this.seed = seed;
46906
47293
  this.createSdk = createSdk;
46907
- this.latestSessionId = path19__default.basename(seed.transcriptPath, ".jsonl");
47294
+ this.latestSessionId = seed.origin.kind === "resume" ? path19__default.basename(seed.origin.transcriptPath, ".jsonl") : null;
46908
47295
  }
46909
47296
  get busy() {
46910
47297
  return this.pendingTurns > 0;
46911
47298
  }
46912
47299
  async replayTranscript() {
47300
+ if (this.seed.origin.kind === "fresh") return;
47301
+ const transcriptPath = this.seed.origin.transcriptPath;
46913
47302
  this.push({ kind: "replay-start" });
46914
47303
  let turns = 0;
47304
+ let turnAt = null;
47305
+ let lastAt = null;
47306
+ const closeReplayedTurn = () => {
47307
+ if (turnAt !== null && lastAt !== null && lastAt > turnAt) {
47308
+ this.push({ kind: "turn-end", ok: true, durationMs: lastAt - turnAt });
47309
+ }
47310
+ turnAt = null;
47311
+ lastAt = null;
47312
+ };
46915
47313
  try {
46916
- const raw = await promises.readFile(this.seed.transcriptPath, "utf8");
47314
+ const raw = await promises.readFile(transcriptPath, "utf8");
46917
47315
  for (const line of raw.split("\n")) {
46918
47316
  if (line.trim().length === 0) continue;
46919
- for (const event of chatEventsFromTranscriptLine(line, { cwd: this.seed.cwd })) {
46920
- if (event.kind === "dispatch") turns += 1;
47317
+ const mapped = chatReplayFromTranscriptLine(line, { cwd: this.seed.cwd, toolNames: this.toolNames });
47318
+ for (const event of mapped.events) {
47319
+ if (event.kind === "dispatch") {
47320
+ closeReplayedTurn();
47321
+ turns += 1;
47322
+ turnAt = mapped.at ?? null;
47323
+ }
47324
+ this.rememberTool(event);
46921
47325
  this.push(event);
46922
47326
  }
47327
+ if (mapped.events.length > 0 && mapped.at !== void 0) lastAt = mapped.at;
46923
47328
  }
47329
+ closeReplayedTurn();
46924
47330
  } catch {
46925
47331
  this.push({ kind: "error", code: "chat_replay_unavailable" });
46926
47332
  }
@@ -46938,6 +47344,13 @@ var AgentChatSession = class {
46938
47344
  * 턴을 직렬화해 실행한다. 반환은 큐 등록 시점이다 — 진행·실패는 저널 이벤트로 전달된다.
46939
47345
  * 실패한 턴은 반드시 run.close()로 슬롯을 반납한다(스파이크에서 실증한 함정).
46940
47346
  */
47347
+ /**
47348
+ * 활동축이 이 세션의 턴을 받을 수 있는지 미리 확인한다. 큐에 넣은 뒤에는 HTTP 응답이 이미
47349
+ * 떠났으므로, 거절은 요청 경계에서만 시끄러울 수 있다.
47350
+ */
47351
+ canReportActivity() {
47352
+ return this.seed.canReportActivity();
47353
+ }
46941
47354
  send(text2) {
46942
47355
  if (this.disposed) return;
46943
47356
  this.pendingTurns += 1;
@@ -46954,6 +47367,16 @@ var AgentChatSession = class {
46954
47367
  await this.turnFlight.catch(() => void 0);
46955
47368
  this.listeners.clear();
46956
47369
  }
47370
+ /** 도구 이벤트가 지나갈 때 id→이름을 적어 둔다. 결과 요약 정책이 이 축 위에서 결정된다. */
47371
+ rememberTool(event) {
47372
+ if (event.kind !== "tool" && event.kind !== "tool-start") return;
47373
+ if (event.id === void 0) return;
47374
+ this.toolNames.set(event.id, event.name);
47375
+ if (this.toolNames.size > TOOL_NAME_CAP) {
47376
+ const oldest = this.toolNames.keys().next();
47377
+ if (!oldest.done) this.toolNames.delete(oldest.value);
47378
+ }
47379
+ }
46957
47380
  push(event) {
46958
47381
  const entry = { seq: ++this.seq, event };
46959
47382
  this.journal.push(entry);
@@ -46995,18 +47418,24 @@ var AgentChatSession = class {
46995
47418
  /**
46996
47419
  * 원 트랜스크립트를 격리 config dir의 같은 projects/<인코딩된 cwd>/ 아래로 복사한다.
46997
47420
  * 인코딩 규칙을 재구현하지 않는다 — 원 경로의 부모 디렉터리 이름이 곧 그 인코딩이다.
47421
+ * 채팅으로 태어난 세션에는 복사할 원본이 없다 — SDK가 자기 dir 안에 스스로 만든다.
46998
47422
  */
46999
47423
  async copyTranscriptIntoConfigDir(configDir) {
47000
- const projectDirName = path19__default.basename(path19__default.dirname(this.seed.transcriptPath));
47424
+ if (this.seed.origin.kind === "fresh") return;
47425
+ const projectDirName = path19__default.basename(path19__default.dirname(this.seed.origin.transcriptPath));
47001
47426
  const dest = path19__default.join(configDir, "projects", projectDirName, `${this.latestSessionId}.jsonl`);
47002
47427
  await promises.mkdir(path19__default.dirname(dest), { recursive: true, mode: 448 });
47003
- await promises.copyFile(this.seed.transcriptPath, dest);
47428
+ await promises.copyFile(this.seed.origin.transcriptPath, dest);
47004
47429
  }
47005
47430
  async runTurn(text2) {
47006
47431
  if (this.disposed) return;
47007
47432
  this.push({ kind: "dispatch", text: text2, at: Date.now() });
47008
47433
  this.push({ kind: "turn-start", at: Date.now() });
47009
- this.push({ kind: "status", working: true });
47434
+ if (!this.seed.reportActivity(true)) {
47435
+ this.push({ kind: "error", code: "chat_activity_unavailable" });
47436
+ this.push({ kind: "turn-end", ok: false });
47437
+ return;
47438
+ }
47010
47439
  let sawResult = false;
47011
47440
  try {
47012
47441
  const sdk = await this.ensureSdk();
@@ -47015,7 +47444,9 @@ var AgentChatSession = class {
47015
47444
  model: this.seed.model,
47016
47445
  ...this.seed.effort ? { effort: this.seed.effort } : {},
47017
47446
  cwd: this.seed.cwd,
47018
- resume: this.latestSessionId,
47447
+ // 채팅으로 태어난 세션의 첫 턴에는 이어붙일 좌표가 없다 — resume 없이 시작하고,
47448
+ // SDK가 돌려주는 session_id가 그 자리를 채운다(그다음 턴부터는 resume이 선다).
47449
+ ...this.latestSessionId ? { resume: this.latestSessionId } : {},
47019
47450
  permissionMode: "bypassPermissions",
47020
47451
  // 스트리밍 감각의 근거 — 글자 단위 text_delta를 받으려면 부분 메시지가 필요하다.
47021
47452
  includePartialMessages: true
@@ -47025,9 +47456,10 @@ var AgentChatSession = class {
47025
47456
  if (typeof message.session_id === "string" && message.session_id.length > 0) {
47026
47457
  this.latestSessionId = message.session_id;
47027
47458
  }
47028
- for (const event of chatEventsFromSdkMessage(message, { cwd: this.seed.cwd })) {
47459
+ for (const event of chatEventsFromSdkMessage(message, { cwd: this.seed.cwd, toolNames: this.toolNames })) {
47029
47460
  if (event.kind === "turn-end") sawResult = true;
47030
- if (event.kind === "text-delta") this.pushEphemeral(event);
47461
+ this.rememberTool(event);
47462
+ if (event.kind === "text-delta" || event.kind === "tool-start") this.pushEphemeral(event);
47031
47463
  else this.push(event);
47032
47464
  }
47033
47465
  }
@@ -47040,7 +47472,7 @@ var AgentChatSession = class {
47040
47472
  this.push({ kind: "error", code: "chat_turn_failed" });
47041
47473
  if (!sawResult) this.push({ kind: "turn-end", ok: false });
47042
47474
  } finally {
47043
- this.push({ kind: "status", working: false });
47475
+ this.seed.reportActivity(false);
47044
47476
  }
47045
47477
  }
47046
47478
  /**
@@ -47049,15 +47481,19 @@ var AgentChatSession = class {
47049
47481
  */
47050
47482
  async writeBackTranscript() {
47051
47483
  const sdk = this.sdk;
47052
- if (!sdk) return;
47053
- const projectDirName = path19__default.basename(path19__default.dirname(this.seed.transcriptPath));
47054
- const source = path19__default.join(sdk.configDir, "projects", projectDirName, `${this.latestSessionId}.jsonl`);
47484
+ if (!sdk || !this.latestSessionId) return;
47485
+ const sessionId = this.latestSessionId;
47486
+ const projectDirName = await this.resolveProjectDirName(sdk.configDir);
47487
+ if (!projectDirName) return;
47488
+ const source = path19__default.join(sdk.configDir, "projects", projectDirName, `${sessionId}.jsonl`);
47055
47489
  const sourceStat = await promises.stat(source).catch(() => null);
47056
47490
  if (!sourceStat?.isFile()) return;
47057
- const dest = path19__default.join(path19__default.dirname(this.seed.transcriptPath), `${this.latestSessionId}.jsonl`);
47491
+ const destDir = this.seed.origin.kind === "resume" ? path19__default.dirname(this.seed.origin.transcriptPath) : path19__default.join(this.seed.origin.transcriptRoot, projectDirName);
47492
+ const dest = path19__default.join(destDir, `${sessionId}.jsonl`);
47058
47493
  const destStat = await promises.stat(dest).catch(() => null);
47059
47494
  if (destStat?.isFile() && destStat.size > sourceStat.size) return;
47060
47495
  try {
47496
+ if (this.seed.origin.kind === "fresh") await promises.mkdir(destDir, { recursive: true, mode: 448 });
47061
47497
  await promises.copyFile(source, dest);
47062
47498
  } catch {
47063
47499
  this.push({ kind: "error", code: "chat_writeback_failed" });
@@ -47065,12 +47501,25 @@ var AgentChatSession = class {
47065
47501
  }
47066
47502
  this.seed.onProviderSessionUpdate({
47067
47503
  provider: "claude",
47068
- sessionId: this.latestSessionId,
47504
+ sessionId,
47069
47505
  transcriptPath: dest,
47070
47506
  source: "chat-mode",
47071
47507
  capturedAt: (/* @__PURE__ */ new Date()).toISOString()
47072
47508
  });
47073
47509
  }
47510
+ /**
47511
+ * 이 세션의 트랜스크립트가 앉은 projects 디렉터리 이름. resume은 원 경로가 이미 말해 주고,
47512
+ * fresh는 SDK가 자기 dir 안에 만든 이름을 읽는다 — cwd 인코딩 규칙을 재구현하지 않기 위해서다.
47513
+ * 격리 dir은 이 세션 전용이라 그 아래 projects 항목은 하나뿐이며, 여럿이면 무엇이 우리 것인지
47514
+ * 말할 수 없으므로 되쓰지 않는다(잘못된 뿌리에 남의 세션을 심는 것보다 낫다).
47515
+ */
47516
+ async resolveProjectDirName(configDir) {
47517
+ if (this.seed.origin.kind === "resume") return path19__default.basename(path19__default.dirname(this.seed.origin.transcriptPath));
47518
+ const entries = await promises.readdir(path19__default.join(configDir, "projects"), { withFileTypes: true }).catch(() => null);
47519
+ if (!entries) return null;
47520
+ const dirs = entries.filter((entry) => entry.isDirectory());
47521
+ return dirs.length === 1 ? dirs[0]?.name ?? null : null;
47522
+ }
47074
47523
  };
47075
47524
  var AgentChatRegistry = class {
47076
47525
  sessions = /* @__PURE__ */ new Map();
@@ -47300,6 +47749,15 @@ function registerTerminalSettingsRoutes(ctx, deps) {
47300
47749
  ));
47301
47750
  return true;
47302
47751
  }
47752
+ if ("compactCeiling" in update) {
47753
+ const stored = deps.aiGatewayStore.writeCompactCeiling(update.compactCeiling);
47754
+ ctx.host.http.writeJson(res, 200, toTerminalSettingsState(
47755
+ deps.globalOptionsService.load(),
47756
+ stored,
47757
+ deps.wireLogRuntime.enabled()
47758
+ ));
47759
+ return true;
47760
+ }
47303
47761
  const updated = deps.globalOptionsService.update((current) => ({ ...current, ...update }));
47304
47762
  ctx.host.http.writeJson(res, 200, toTerminalSettingsState(
47305
47763
  updated,
@@ -47326,7 +47784,8 @@ function toTerminalSettingsState(data, aiGateway, wireLogEnabled2) {
47326
47784
  } : null,
47327
47785
  aiGatewayCatalog: buildAiGatewayCatalog(),
47328
47786
  cursorDiagnosticsEnabled: aiGateway.cursorDiagnosticsEnabled === true,
47329
- wireLogEnabled: wireLogEnabled2
47787
+ wireLogEnabled: wireLogEnabled2,
47788
+ compactCeiling: aiGateway.compactCeiling ?? null
47330
47789
  };
47331
47790
  }
47332
47791
  function resolveAgentIdleDormantMinutes(data) {
@@ -47353,6 +47812,11 @@ function parseTerminalSettingsBody(value) {
47353
47812
  if (keys[0] === "wireLogEnabled") {
47354
47813
  return typeof body.wireLogEnabled === "boolean" ? { wireLogEnabled: body.wireLogEnabled } : null;
47355
47814
  }
47815
+ if (keys[0] === "compactCeiling") {
47816
+ if (body.compactCeiling === null) return { compactCeiling: void 0 };
47817
+ const ceiling = normalizeCompactCeiling(body.compactCeiling);
47818
+ return ceiling === void 0 ? null : { compactCeiling: ceiling };
47819
+ }
47356
47820
  return null;
47357
47821
  }
47358
47822
  function isAgentIdleDormantMinutes(value) {
@@ -47407,7 +47871,13 @@ var OPERATION_DELETED_EVENT_CHANNEL = "operation:deleted";
47407
47871
  var OPERATION_PURGED_EVENT_CHANNEL = "operation:purged";
47408
47872
  var TERMINAL_PLUGIN_ID = "terminal";
47409
47873
  var CHAT_MODE_PAYLOAD_KEY = "chatMode";
47874
+ var CHAT_BORN_PAYLOAD_KEY = "chatBorn";
47410
47875
  var CHAT_SUPPORTED_CLI_ID = "claude-gateway";
47876
+ function resolveClaudeProjectsRoot() {
47877
+ const configured = process4.env.CLAUDE_CONFIG_DIR;
47878
+ const configDir = configured && configured.length > 0 ? configured : path19__default.join(os3__default.homedir(), ".claude");
47879
+ return path19__default.join(configDir, "projects");
47880
+ }
47411
47881
  async function registerAgentRoutes(ctx, terminalRuntime, deps) {
47412
47882
  const api = await createAgentApi(ctx, terminalRuntime, deps);
47413
47883
  terminalRuntime.registerLaunchResolver(AGENT_OPERATION_TYPE, api.launch);
@@ -47450,7 +47920,10 @@ function buildAgentLaunchKindBackfillPatch(operation) {
47450
47920
  function buildAgentLaunchProviderBackfillPatch(operation) {
47451
47921
  if (operation.pluginId !== TERMINAL_PLUGIN_ID || operation.type !== AGENT_OPERATION_TYPE) return null;
47452
47922
  if (isAgentLaunchProvider(operation.payload[AGENT_LAUNCH_PROVIDER_PAYLOAD_KEY])) return null;
47453
- return { payload: { ...operation.payload, [AGENT_LAUNCH_PROVIDER_PAYLOAD_KEY]: "claude" } };
47923
+ const cliId = typeof operation.payload.cliId === "string" ? operation.payload.cliId : void 0;
47924
+ const provider = agentLaunchProviderFromCliId(cliId);
47925
+ if (!provider) return null;
47926
+ return { payload: { ...operation.payload, [AGENT_LAUNCH_PROVIDER_PAYLOAD_KEY]: provider } };
47454
47927
  }
47455
47928
  function buildGatewayLoadoutTools(deps) {
47456
47929
  const readAiGatewaySettings = deps.readAiGatewaySettings;
@@ -47713,6 +48186,15 @@ async function createAgentApi(ctx, terminalRuntime, deps) {
47713
48186
  const prompt = typeof body?.prompt === "string" ? sanitizeLaunchPrompt(body.prompt) : void 0;
47714
48187
  const attachmentIds = readAttachmentIds(body?.attachmentIds, res);
47715
48188
  if (attachmentIds === false) return true;
48189
+ if (body?.viewMode !== void 0 && body.viewMode !== "terminal" && body.viewMode !== "chat") {
48190
+ ctx.host.http.writeJson(res, 400, { error: "invalid_view_mode" });
48191
+ return true;
48192
+ }
48193
+ const chatBorn = body?.viewMode === "chat";
48194
+ if (chatBorn && cliId !== CHAT_SUPPORTED_CLI_ID) {
48195
+ ctx.host.http.writeJson(res, 409, { error: "chat_unsupported" });
48196
+ return true;
48197
+ }
47716
48198
  const cwd = ctx.host.paths.resolveTheaterPath(theaterId);
47717
48199
  if (!cwd) {
47718
48200
  ctx.host.http.writeJson(res, 404, { error: "theater_not_found" });
@@ -47732,7 +48214,8 @@ async function createAgentApi(ctx, terminalRuntime, deps) {
47732
48214
  await createSession(cwd, theaterId, cliId, res, {
47733
48215
  ...launchOptions,
47734
48216
  ...composedPrompt ? { prompt: composedPrompt } : {},
47735
- ...attachmentIds.length > 0 ? { attachmentIds } : {}
48217
+ ...attachmentIds.length > 0 ? { attachmentIds } : {},
48218
+ ...chatBorn ? { chatBorn: true } : {}
47736
48219
  });
47737
48220
  } catch (error51) {
47738
48221
  if (attachmentIds.length > 0) launchAttachments.unreserve(attachmentIds);
@@ -47805,13 +48288,44 @@ async function createAgentApi(ctx, terminalRuntime, deps) {
47805
48288
  }
47806
48289
  return methodNotAllowed2(res);
47807
48290
  }
48291
+ async function startChatBornSession(sessionId, res, launchOptions) {
48292
+ const rollback = (status, error51) => {
48293
+ if (launchOptions.attachmentIds && launchOptions.attachmentIds.length > 0) {
48294
+ launchAttachments.unreserve(launchOptions.attachmentIds);
48295
+ }
48296
+ ctx.host.operations.delete(sessionId);
48297
+ observability.removeTerminalSession(sessionId);
48298
+ ctx.host.http.writeJson(res, status, { error: error51 });
48299
+ };
48300
+ const node = ctx.host.operations.get(sessionId);
48301
+ if (!node) return rollback(500, "session_not_found");
48302
+ observability.updateTerminalSessionStatus(sessionId, "dormant");
48303
+ const activated = observability.setTerminalSessionChatActive(sessionId, true);
48304
+ if (activated) observability.notifySessionUpdated(activated);
48305
+ const seed = await resolveChatSeed(node);
48306
+ if (!seed.ok) return rollback(seed.status, seed.error);
48307
+ let chat;
48308
+ try {
48309
+ chat = await chatRegistry.ensure(sessionId, () => seed.seed);
48310
+ } catch {
48311
+ return rollback(503, "chat_unavailable");
48312
+ }
48313
+ if (launchOptions.prompt) chat.send(launchOptions.prompt);
48314
+ if (launchOptions.attachmentIds && launchOptions.attachmentIds.length > 0) {
48315
+ launchAttachments.bind(sessionId, launchOptions.attachmentIds);
48316
+ if (!ctx.host.operations.get(sessionId)) launchAttachments.releaseSession(sessionId);
48317
+ }
48318
+ const created = observability.getTerminalSessionInfo(sessionId);
48319
+ if (created) observability.notifySessionUpdated(created);
48320
+ ctx.host.http.writeJson(res, 200, created ?? { sessionId });
48321
+ }
47808
48322
  async function createSession(cwd, theaterId, cliId, res, launchOptions = {}) {
47809
48323
  const meta3 = (await buildAgentCliLaunchMetadata()).find((entry) => entry.id === cliId);
47810
48324
  if (!meta3 || !meta3.available || !meta3.signedIn) {
47811
48325
  if (launchOptions.attachmentIds && launchOptions.attachmentIds.length > 0) {
47812
48326
  launchAttachments.unreserve(launchOptions.attachmentIds);
47813
48327
  }
47814
- ctx.host.http.writeJson(res, 409, { error: "agent_cli_unavailable" });
48328
+ ctx.host.http.writeJson(res, 409, { error: meta3 && !meta3.available ? "agent_cli_not_installed" : "agent_cli_signed_out" });
47815
48329
  return;
47816
48330
  }
47817
48331
  const sessionId = crypto5.randomUUID();
@@ -47832,10 +48346,17 @@ async function createAgentApi(ctx, terminalRuntime, deps) {
47832
48346
  // Claude Code의 resume가 launch 좌표를 자체 복원한다고 가정하지 않는다 — 정규화된
47833
48347
  // 최초 선택을 Operation에 남겨 dormant·Console 재시작 뒤에도 같은 인자로 재개한다.
47834
48348
  ...launchOptions.model ? { launchModel: launchOptions.model } : {},
47835
- ...launchOptions.effort ? { launchEffort: launchOptions.effort } : {}
48349
+ ...launchOptions.effort ? { launchEffort: launchOptions.effort } : {},
48350
+ // 채팅으로 태어난 Operation은 두 마커를 함께 진다. chatMode가 뷰를 가르고, chatBorn이
48351
+ // "transcript 부재는 상실이 아니라 아직 첫 턴 전"이라는 뜻을 durable하게 남긴다.
48352
+ ...launchOptions.chatBorn ? { [CHAT_MODE_PAYLOAD_KEY]: true, [CHAT_BORN_PAYLOAD_KEY]: true } : {}
47836
48353
  },
47837
48354
  createdAt: session.createdAt
47838
48355
  });
48356
+ if (launchOptions.chatBorn) {
48357
+ await startChatBornSession(sessionId, res, launchOptions);
48358
+ return;
48359
+ }
47839
48360
  try {
47840
48361
  await terminalRuntime.attach({
47841
48362
  cwd,
@@ -47880,7 +48401,7 @@ async function createAgentApi(ctx, terminalRuntime, deps) {
47880
48401
  }
47881
48402
  pendingRuntimeSessions.delete(sessionId);
47882
48403
  observability.updateTerminalSessionStatus(sessionId, "error");
47883
- ctx.host.http.writeJson(res, 503, { error: "terminal_unavailable" });
48404
+ ctx.host.http.writeJson(res, 503, { error: classifyLaunchSpawnFailure(error51) });
47884
48405
  }
47885
48406
  }
47886
48407
  async function handleResume(req, res, sessionId) {
@@ -47911,6 +48432,8 @@ async function createAgentApi(ctx, terminalRuntime, deps) {
47911
48432
  const cleared = { ...ctx.host.operations.get(sessionId)?.payload ?? node.payload };
47912
48433
  delete cleared[CHAT_MODE_PAYLOAD_KEY];
47913
48434
  ctx.host.operations.patch(sessionId, { payload: cleared });
48435
+ const releasedOnResume = observability.setTerminalSessionChatActive(sessionId, false);
48436
+ if (releasedOnResume) observability.notifySessionUpdated(releasedOnResume);
47914
48437
  resumeNode = ctx.host.operations.get(sessionId) ?? node;
47915
48438
  if (!fresh) resumeProviderSession = readProviderSession(resumeNode.payload) ?? providerSession;
47916
48439
  }
@@ -48038,6 +48561,11 @@ async function createAgentApi(ctx, terminalRuntime, deps) {
48038
48561
  }
48039
48562
  try {
48040
48563
  const chat = await chatRegistry.ensure(sessionId, () => seed.seed);
48564
+ if (!chat.canReportActivity()) {
48565
+ settleAttachments(false);
48566
+ ctx.host.http.writeJson(res, 503, { error: "chat_activity_unavailable" });
48567
+ return true;
48568
+ }
48041
48569
  chat.send(composeLaunchPromptWithAttachments(text2.trim(), attachmentPaths));
48042
48570
  } catch {
48043
48571
  settleAttachments(false);
@@ -48115,6 +48643,8 @@ async function createAgentApi(ctx, terminalRuntime, deps) {
48115
48643
  const cleared = { ...ctx.host.operations.get(sessionId)?.payload ?? node.payload };
48116
48644
  delete cleared[CHAT_MODE_PAYLOAD_KEY];
48117
48645
  ctx.host.operations.patch(sessionId, { payload: cleared });
48646
+ const released = observability.setTerminalSessionChatActive(sessionId, false);
48647
+ if (released) observability.notifySessionUpdated(released);
48118
48648
  ctx.host.http.writeJson(res, 200, { ok: true });
48119
48649
  return true;
48120
48650
  }
@@ -48140,6 +48670,8 @@ async function createAgentApi(ctx, terminalRuntime, deps) {
48140
48670
  }
48141
48671
  }
48142
48672
  ctx.host.operations.patch(sessionId, { payload: { ...node.payload, [CHAT_MODE_PAYLOAD_KEY]: true } });
48673
+ const activated = observability.setTerminalSessionChatActive(sessionId, true);
48674
+ if (activated) observability.notifySessionUpdated(activated);
48143
48675
  if (live) {
48144
48676
  terminalRuntime.invalidateTicketsForSession(sessionId);
48145
48677
  terminalRuntime.terminate(sessionId);
@@ -48215,9 +48747,11 @@ async function createAgentApi(ctx, terminalRuntime, deps) {
48215
48747
  return { ok: false, status: 409, error: "chat_unsupported" };
48216
48748
  }
48217
48749
  const providerSession = readProviderSession(node.payload);
48218
- if (!providerSession?.transcriptPath) return { ok: false, status: 409, error: "chat_transcript_missing" };
48219
- const transcriptPath = await resolveTranscriptPath(providerSession.transcriptPath, node.ts.createdAt);
48220
- if (!transcriptPath) return { ok: false, status: 409, error: "chat_transcript_missing" };
48750
+ const transcriptPath = providerSession?.transcriptPath ? await resolveTranscriptPath(providerSession.transcriptPath, node.ts.createdAt) : null;
48751
+ const chatBorn = node.payload[CHAT_BORN_PAYLOAD_KEY] === true;
48752
+ const neverStarted = chatBorn && !providerSession;
48753
+ if (!transcriptPath && !neverStarted) return { ok: false, status: 409, error: "chat_transcript_missing" };
48754
+ const sessionOrigin = transcriptPath ? { kind: "resume", transcriptPath } : { kind: "fresh", transcriptRoot: resolveClaudeProjectsRoot() };
48221
48755
  const origin = ctx.host.server.origin();
48222
48756
  if (!origin) return { ok: false, status: 503, error: "chat_gateway_unavailable" };
48223
48757
  const cwd = readPayloadString(node.payload, "cwd") || ctx.host.paths.resolveTheaterPath(node.theaterId);
@@ -48231,11 +48765,18 @@ async function createAgentApi(ctx, terminalRuntime, deps) {
48231
48765
  model,
48232
48766
  ...effort ? { effort } : {},
48233
48767
  cwd,
48234
- transcriptPath,
48768
+ origin: sessionOrigin,
48235
48769
  onProviderSessionUpdate: (updated) => {
48236
48770
  const operation = ctx.host.operations.get(node.id);
48237
48771
  if (operation) ctx.host.operations.patch(node.id, { payload: { ...operation.payload, providerSession: updated } });
48238
48772
  observability.updateTerminalSessionProviderSession(node.id, updated);
48773
+ },
48774
+ canReportActivity: () => observability.getTerminalSessionInfo(node.id)?.chatActive === true,
48775
+ reportActivity: (working) => {
48776
+ const updated = observability.setTerminalSessionChatWorking(node.id, working);
48777
+ if (!updated) return false;
48778
+ observability.notifySessionUpdated(updated);
48779
+ return true;
48239
48780
  }
48240
48781
  }
48241
48782
  };
@@ -48385,7 +48926,12 @@ async function createAgentApi(ctx, terminalRuntime, deps) {
48385
48926
  terminalRuntime.invalidateTicketsForSession(operationId);
48386
48927
  observability.notifySessionUpdated(dormant);
48387
48928
  const exitCwd = readPayloadString(ctx.host.operations.get(operationId)?.payload ?? {}, "cwd") ?? "";
48388
- ctx.host.operations.patch(operationId, { payload: toOperationPayload(ctx.host.operations.get(operationId)?.payload, exitCwd, dormant, providerSession, observability.getDurableOperation(operationId)?.providerTitle) });
48929
+ ctx.host.operations.patch(operationId, {
48930
+ payload: {
48931
+ ...toOperationPayload(ctx.host.operations.get(operationId)?.payload, exitCwd, dormant, providerSession, observability.getDurableOperation(operationId)?.providerTitle),
48932
+ restoredDormant: true
48933
+ }
48934
+ });
48389
48935
  }
48390
48936
  } else {
48391
48937
  observability.removeTerminalSession(operationId);
@@ -48492,10 +49038,24 @@ async function createAgentApi(ctx, terminalRuntime, deps) {
48492
49038
  for (const operation of ctx.host.operations.list()) {
48493
49039
  if (operation.pluginId !== ctx.pluginId || operation.type !== AGENT_OPERATION_TYPE) continue;
48494
49040
  const providerSession = readProviderSession(operation.payload);
48495
- if (!providerSession || observability.getTerminalSessionInfo(operation.id)) continue;
49041
+ const chatBorn = operation.payload[CHAT_BORN_PAYLOAD_KEY] === true;
49042
+ if (observability.getTerminalSessionInfo(operation.id)) continue;
49043
+ if (!providerSession && !chatBorn) {
49044
+ ctx.host.operations.patch(operation.id, { payload: { ...operation.payload, restoredDormant: true } });
49045
+ continue;
49046
+ }
48496
49047
  const dormant = injectOperation(operation);
49048
+ if (operation.payload[CHAT_MODE_PAYLOAD_KEY] === true) {
49049
+ const adopted = observability.setTerminalSessionChatActive(operation.id, true);
49050
+ if (adopted) observability.notifySessionUpdated(adopted);
49051
+ }
48497
49052
  const cwd = readPayloadString(operation.payload, "cwd") || (ctx.host.paths.resolveTheaterPath(operation.theaterId) ?? "");
48498
- ctx.host.operations.patch(operation.id, { payload: toOperationPayload(operation.payload, cwd, dormant, providerSession, observability.getDurableOperation(operation.id)?.providerTitle) });
49053
+ ctx.host.operations.patch(operation.id, {
49054
+ payload: {
49055
+ ...toOperationPayload(operation.payload, cwd, dormant, providerSession, observability.getDurableOperation(operation.id)?.providerTitle),
49056
+ restoredDormant: true
49057
+ }
49058
+ });
48499
49059
  }
48500
49060
  }
48501
49061
  function backfillAgentOperationLaunchAxes() {
@@ -48544,7 +49104,7 @@ function createTerminalWikiToolSpecs(fleetDataDir) {
48544
49104
  }
48545
49105
  function toOperationPayload(existing, cwd, session, providerSession, providerTitle) {
48546
49106
  const payload = { ...existing ?? {} };
48547
- for (const key of ["cwd", "cliId", "launchKindId", "cliLabel", "providerSession", "labelSource", "providerTitle"]) {
49107
+ for (const key of ["cwd", "cliId", "launchKindId", "cliLabel", "providerSession", "labelSource", "providerTitle", "restoredDormant"]) {
48548
49108
  delete payload[key];
48549
49109
  }
48550
49110
  return {
@@ -48630,6 +49190,12 @@ function readHookPrompt(value) {
48630
49190
  return void 0;
48631
49191
  }
48632
49192
  }
49193
+ function classifyLaunchSpawnFailure(error51) {
49194
+ const code = error51?.code;
49195
+ if (code === "ENOENT") return "agent_cli_binary_missing";
49196
+ if (code === "EACCES" || code === "EPERM") return "agent_cli_not_executable";
49197
+ return "terminal_unavailable";
49198
+ }
48633
49199
  var DEFAULT_MAX_READ = 4 * 1024 * 1024;
48634
49200
  var DEFAULT_MAX_REFRESH = 32 * 1024 * 1024;
48635
49201
  var CONTINUITY_BYTES = 4 * 1024;
@@ -50129,6 +50695,7 @@ function formatSignInFailureMessage(displayName, status) {
50129
50695
  var OPERATION_RESTORED_EVENT_CHANNEL2 = "operation:restored";
50130
50696
  var RESTORED_DORMANT_PAYLOAD_KEY = "restoredDormant";
50131
50697
  function registerShellRoutes(ctx, runtime) {
50698
+ markRestoredShellOperations(ctx);
50132
50699
  const unsubscribeRestore = ctx.host.events.subscribe(OPERATION_RESTORED_EVENT_CHANNEL2, (payload) => {
50133
50700
  if (!isRestoredShellEvent(payload, ctx.pluginId)) return;
50134
50701
  const operation = ctx.host.operations.get(payload.operationId);
@@ -50229,6 +50796,13 @@ function registerShellRoutes(ctx, runtime) {
50229
50796
  { method: "POST", path: "/:operationId/relaunch", summary: "Relaunch a dormant Shell session.", category: "Terminal Plugin", gate: "origin-write", transport: "http" }
50230
50797
  ]);
50231
50798
  }
50799
+ function markRestoredShellOperations(ctx) {
50800
+ for (const operation of ctx.host.operations.list()) {
50801
+ if (operation.pluginId !== ctx.pluginId || operation.type !== "shell") continue;
50802
+ if (operation.payload[RESTORED_DORMANT_PAYLOAD_KEY] === true) continue;
50803
+ ctx.host.operations.patch(operation.id, { payload: { ...operation.payload, [RESTORED_DORMANT_PAYLOAD_KEY]: true } });
50804
+ }
50805
+ }
50232
50806
  function isRestoredShellEvent(value, pluginId) {
50233
50807
  if (!value || typeof value !== "object") return false;
50234
50808
  const event = value;