@dotobokuri/fleet-console 1.64.0 → 1.66.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 (66) hide show
  1. package/dist/cli.mjs +26 -22
  2. package/dist/client/assets/{_baseUniq-DurhRkUz.js → _baseUniq-rDf4uMQX.js} +1 -1
  3. package/dist/client/assets/{arc-CRs5BdYr.js → arc-btPQ2GHn.js} +1 -1
  4. package/dist/client/assets/{architectureDiagram-Q4EWVU46-Djo8uCUV.js → architectureDiagram-Q4EWVU46-DG6KDx34.js} +1 -1
  5. package/dist/client/assets/{blockDiagram-DXYQGD6D-hQdc3yPb.js → blockDiagram-DXYQGD6D-XUrLsQey.js} +1 -1
  6. package/dist/client/assets/{c4Diagram-AHTNJAMY-Czv6wUeb.js → c4Diagram-AHTNJAMY-DuXkZ8b1.js} +1 -1
  7. package/dist/client/assets/channel-CXR6clX_.js +1 -0
  8. package/dist/client/assets/{chunk-4BX2VUAB-DXjqdOUQ.js → chunk-4BX2VUAB-C2tTKvHv.js} +1 -1
  9. package/dist/client/assets/{chunk-4TB4RGXK-vvWT5R7G.js → chunk-4TB4RGXK-C5bwM8_h.js} +1 -1
  10. package/dist/client/assets/{chunk-55IACEB6-DMDBDn4n.js → chunk-55IACEB6-BhcOy6C-.js} +1 -1
  11. package/dist/client/assets/{chunk-EDXVE4YY-CvZM4UpD.js → chunk-EDXVE4YY-TOZwStuB.js} +1 -1
  12. package/dist/client/assets/{chunk-FMBD7UC4-BArng_V9.js → chunk-FMBD7UC4-B8JSJHVV.js} +1 -1
  13. package/dist/client/assets/{chunk-OYMX7WX6-DwNWyoK7.js → chunk-OYMX7WX6-BsNJ9iub.js} +1 -1
  14. package/dist/client/assets/{chunk-QZHKN3VN--guvcrYz.js → chunk-QZHKN3VN-BhHSM1D6.js} +1 -1
  15. package/dist/client/assets/{chunk-YZCP3GAM-BqxZQZyX.js → chunk-YZCP3GAM-B9d83zwz.js} +1 -1
  16. package/dist/client/assets/classDiagram-6PBFFD2Q-4x7ZfFx0.js +1 -0
  17. package/dist/client/assets/classDiagram-v2-HSJHXN6E-4x7ZfFx0.js +1 -0
  18. package/dist/client/assets/clone-lkppJrH0.js +1 -0
  19. package/dist/client/assets/{cose-bilkent-S5V4N54A-BMg_LOnu.js → cose-bilkent-S5V4N54A-D5mwO2NV.js} +1 -1
  20. package/dist/client/assets/{dagre-KV5264BT-Dr7w5bQR.js → dagre-KV5264BT-5fSh4g2E.js} +1 -1
  21. package/dist/client/assets/{diagram-5BDNPKRD-B3mTbXqP.js → diagram-5BDNPKRD-CtCGKgkm.js} +1 -1
  22. package/dist/client/assets/{diagram-G4DWMVQ6-qb3u5_YF.js → diagram-G4DWMVQ6-CmGVsepF.js} +1 -1
  23. package/dist/client/assets/{diagram-MMDJMWI5-CBBgto41.js → diagram-MMDJMWI5-BR9Rfnad.js} +1 -1
  24. package/dist/client/assets/{diagram-TYMM5635-Cmo3cjOQ.js → diagram-TYMM5635-C9kuBFny.js} +1 -1
  25. package/dist/client/assets/{erDiagram-SMLLAGMA-HeqZPhtI.js → erDiagram-SMLLAGMA-BlL5tH_h.js} +1 -1
  26. package/dist/client/assets/{flowDiagram-DWJPFMVM-C34ZouQN.js → flowDiagram-DWJPFMVM-IUe6EFlH.js} +1 -1
  27. package/dist/client/assets/{ganttDiagram-T4ZO3ILL-BL2_YoJe.js → ganttDiagram-T4ZO3ILL-G-CTHnXi.js} +1 -1
  28. package/dist/client/assets/{gitGraphDiagram-UUTBAWPF-DSC8Gr0E.js → gitGraphDiagram-UUTBAWPF-COUsDQ0n.js} +1 -1
  29. package/dist/client/assets/{graph-C4jsVwYI.js → graph-BCiainCT.js} +1 -1
  30. package/dist/client/assets/index-B1tQI7A5.js +465 -0
  31. package/dist/client/assets/index-D3b63Dhe.css +1 -0
  32. package/dist/client/assets/{infoDiagram-42DDH7IO-BX_lYqHx.js → infoDiagram-42DDH7IO-DbNkXGyd.js} +1 -1
  33. package/dist/client/assets/{ishikawaDiagram-UXIWVN3A-CsXWVwyY.js → ishikawaDiagram-UXIWVN3A-Iqt-Zs6v.js} +1 -1
  34. package/dist/client/assets/{journeyDiagram-VCZTEJTY-ETPWVMNg.js → journeyDiagram-VCZTEJTY-m7TrTD61.js} +1 -1
  35. package/dist/client/assets/{kanban-definition-6JOO6SKY-m7hLIKgZ.js → kanban-definition-6JOO6SKY-Df5KWqsi.js} +1 -1
  36. package/dist/client/assets/{layout-DrRXjJzL.js → layout-BtA7fX7y.js} +1 -1
  37. package/dist/client/assets/{linear-DzDd2m9j.js → linear-DVL8mywl.js} +1 -1
  38. package/dist/client/assets/{mermaid.core-BHkit2JY.js → mermaid.core-CmjUZw2x.js} +4 -4
  39. package/dist/client/assets/{min-DtwEer1D.js → min-BTu70auM.js} +1 -1
  40. package/dist/client/assets/{mindmap-definition-QFDTVHPH-BO-uheb7.js → mindmap-definition-QFDTVHPH-BYpo-Mnc.js} +1 -1
  41. package/dist/client/assets/{pieDiagram-DEJITSTG-B_4VqNKt.js → pieDiagram-DEJITSTG-B8zFmcim.js} +1 -1
  42. package/dist/client/assets/{quadrantDiagram-34T5L4WZ-HhtqXdkD.js → quadrantDiagram-34T5L4WZ-CMYVBi7F.js} +1 -1
  43. package/dist/client/assets/{requirementDiagram-MS252O5E-CSotIpLH.js → requirementDiagram-MS252O5E-BFeVMKvJ.js} +1 -1
  44. package/dist/client/assets/{sankeyDiagram-XADWPNL6-a8krBcEy.js → sankeyDiagram-XADWPNL6-D2I70l9E.js} +1 -1
  45. package/dist/client/assets/{sequenceDiagram-FGHM5R23-D9Tlp8oT.js → sequenceDiagram-FGHM5R23-zawVvGJJ.js} +1 -1
  46. package/dist/client/assets/{stateDiagram-FHFEXIEX-CbEP_Jmm.js → stateDiagram-FHFEXIEX-DnBtIbuv.js} +1 -1
  47. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-D7rH7_-p.js +1 -0
  48. package/dist/client/assets/{timeline-definition-GMOUNBTQ-CxAJtY22.js → timeline-definition-GMOUNBTQ-0gfoVpmY.js} +1 -1
  49. package/dist/client/assets/{vennDiagram-DHZGUBPP-BSTXi411.js → vennDiagram-DHZGUBPP-CuaEOGH2.js} +1 -1
  50. package/dist/client/assets/{wardley-RL74JXVD-BipCMOcx.js → wardley-RL74JXVD-C-H0Svof.js} +1 -1
  51. package/dist/client/assets/{wardleyDiagram-NUSXRM2D-BudrJ8a1.js → wardleyDiagram-NUSXRM2D-MQjjZFwh.js} +1 -1
  52. package/dist/client/assets/{xychartDiagram-5P7HB3ND-BYlMZf2g.js → xychartDiagram-5P7HB3ND-BhTAVqtf.js} +1 -1
  53. package/dist/client/index.html +2 -2
  54. package/dist/fleet-plugins/ledger/routes.mjs +19 -19
  55. package/dist/fleet-plugins/quota/routes.mjs +19 -19
  56. package/dist/fleet-plugins/scuttlebutt/routes.mjs +19 -19
  57. package/dist/fleet-plugins/terminal/routes.mjs +233 -57
  58. package/dist/fleet.mjs +26 -22
  59. package/package.json +1 -1
  60. package/dist/client/assets/channel-DluRfcgW.js +0 -1
  61. package/dist/client/assets/classDiagram-6PBFFD2Q-CwNh44Lk.js +0 -1
  62. package/dist/client/assets/classDiagram-v2-HSJHXN6E-CwNh44Lk.js +0 -1
  63. package/dist/client/assets/clone-ClWdaAWG.js +0 -1
  64. package/dist/client/assets/index-BuMY4qU-.js +0 -465
  65. package/dist/client/assets/index-DxjgOu3C.css +0 -1
  66. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-B9iCjiGH.js +0 -1
@@ -20229,6 +20229,22 @@ function projectClaudeContextInputTokens(inputTokens, advertisedContextWindow, u
20229
20229
  Math.ceil(inputTokens * claudeCompactThreshold / providerCompactThreshold)
20230
20230
  );
20231
20231
  }
20232
+ function unprojectClaudeContextInputTokens(projectedTokens, advertisedContextWindow, claudeCoordinate, compactCeiling) {
20233
+ if (!Number.isFinite(projectedTokens) || projectedTokens < 0) return projectedTokens;
20234
+ const advertisedWindow = positiveContextWindow(advertisedContextWindow);
20235
+ if (advertisedWindow === void 0) return projectedTokens;
20236
+ if (claudeCoordinate !== CLAUDE_DEFAULT_CONTEXT_WINDOW && claudeCoordinate !== CLAUDE_COMPAT_CONTEXT_WINDOW) {
20237
+ return projectedTokens;
20238
+ }
20239
+ const claudeCompactThreshold = claudeCoordinate - CLAUDE_COMPACT_RESERVE;
20240
+ if (claudeCompactThreshold <= 0) return projectedTokens;
20241
+ const providerCompactThreshold = compactThresholdTokens(advertisedWindow, compactCeiling);
20242
+ if (providerCompactThreshold <= 0) return projectedTokens;
20243
+ return Math.min(
20244
+ advertisedWindow,
20245
+ Math.round(projectedTokens * providerCompactThreshold / claudeCompactThreshold)
20246
+ );
20247
+ }
20232
20248
  function compactThresholdTokens(window, compactCeiling) {
20233
20249
  if (!Number.isFinite(window) || window <= 0) return window;
20234
20250
  const percent2 = compactCeilingPercent(compactCeiling);
@@ -20839,7 +20855,7 @@ var models_default = {
20839
20855
  codex: {
20840
20856
  name: "Codex",
20841
20857
  defaultModel: "gpt-5.6-sol",
20842
- source: "codex app-server model/list (codex-cli 0.147.0, observed 2026-08-11) for model/effort/service-tier and the unsuffixed 272000 defaults; Fleet catalog effort ladders are capped at max because harness launch capabilities are not model rungs; 2026-08-17 production Codex live observations added explicit 512K (524288) and explicit 1M (1000000) variants on the same upstream bases",
20858
+ source: "codex app-server model/list (codex-cli 0.147.0, observed 2026-08-11) for model/effort/service-tier and the unsuffixed 272000 defaults; Fleet catalog effort ladders are capped at max because harness launch capabilities are not model rungs; 2026-08-17 production Codex live observations added explicit 524K (524288) and explicit 1M (1000000) variants on the same upstream bases",
20843
20859
  models: [
20844
20860
  {
20845
20861
  modelId: "gpt-5.6-sol",
@@ -20968,8 +20984,8 @@ var models_default = {
20968
20984
  benchmarkKey: "gpt-5.6-luna"
20969
20985
  },
20970
20986
  {
20971
- modelId: "gpt-5.6-sol-512k",
20972
- name: "GPT-5.6-Sol-512K",
20987
+ modelId: "gpt-5.6-sol-524k",
20988
+ name: "GPT-5.6-Sol-524K",
20973
20989
  capabilityClass: "flagship",
20974
20990
  providerModelId: "gpt-5.6-sol",
20975
20991
  contextWindow: 524288,
@@ -20984,13 +21000,13 @@ var models_default = {
20984
21000
  ]
20985
21001
  },
20986
21002
  aliases: [
20987
- "gpt-5.6-sol-512k"
21003
+ "gpt-5.6-sol-524k"
20988
21004
  ],
20989
21005
  benchmarkKey: "gpt-5.6-sol"
20990
21006
  },
20991
21007
  {
20992
- modelId: "gpt-5.6-sol-512k-fast",
20993
- name: "GPT-5.6-Sol-512K-Fast",
21008
+ modelId: "gpt-5.6-sol-524k-fast",
21009
+ name: "GPT-5.6-Sol-524K-Fast",
20994
21010
  capabilityClass: "flagship",
20995
21011
  providerModelId: "gpt-5.6-sol",
20996
21012
  serviceTier: "priority",
@@ -21006,13 +21022,13 @@ var models_default = {
21006
21022
  ]
21007
21023
  },
21008
21024
  aliases: [
21009
- "gpt-5.6-sol-512k-fast"
21025
+ "gpt-5.6-sol-524k-fast"
21010
21026
  ],
21011
21027
  benchmarkKey: "gpt-5.6-sol"
21012
21028
  },
21013
21029
  {
21014
- modelId: "gpt-5.6-luna-512k",
21015
- name: "GPT-5.6-Luna-512K",
21030
+ modelId: "gpt-5.6-luna-524k",
21031
+ name: "GPT-5.6-Luna-524K",
21016
21032
  capabilityClass: "light",
21017
21033
  providerModelId: "gpt-5.6-luna",
21018
21034
  contextWindow: 524288,
@@ -21027,13 +21043,13 @@ var models_default = {
21027
21043
  ]
21028
21044
  },
21029
21045
  aliases: [
21030
- "gpt-5.6-luna-512k"
21046
+ "gpt-5.6-luna-524k"
21031
21047
  ],
21032
21048
  benchmarkKey: "gpt-5.6-luna"
21033
21049
  },
21034
21050
  {
21035
- modelId: "gpt-5.6-luna-512k-fast",
21036
- name: "GPT-5.6-Luna-512K-Fast",
21051
+ modelId: "gpt-5.6-luna-524k-fast",
21052
+ name: "GPT-5.6-Luna-524K-Fast",
21037
21053
  capabilityClass: "light",
21038
21054
  providerModelId: "gpt-5.6-luna",
21039
21055
  serviceTier: "priority",
@@ -21049,13 +21065,13 @@ var models_default = {
21049
21065
  ]
21050
21066
  },
21051
21067
  aliases: [
21052
- "gpt-5.6-luna-512k-fast"
21068
+ "gpt-5.6-luna-524k-fast"
21053
21069
  ],
21054
21070
  benchmarkKey: "gpt-5.6-luna"
21055
21071
  },
21056
21072
  {
21057
- modelId: "gpt-5.6-terra-512k",
21058
- name: "GPT-5.6-Terra-512K",
21073
+ modelId: "gpt-5.6-terra-524k",
21074
+ name: "GPT-5.6-Terra-524K",
21059
21075
  capabilityClass: "standard",
21060
21076
  providerModelId: "gpt-5.6-terra",
21061
21077
  contextWindow: 524288,
@@ -21070,13 +21086,13 @@ var models_default = {
21070
21086
  ]
21071
21087
  },
21072
21088
  aliases: [
21073
- "gpt-5.6-terra-512k"
21089
+ "gpt-5.6-terra-524k"
21074
21090
  ],
21075
21091
  benchmarkKey: "gpt-5.6-terra"
21076
21092
  },
21077
21093
  {
21078
- modelId: "gpt-5.6-terra-512k-fast",
21079
- name: "GPT-5.6-Terra-512K-Fast",
21094
+ modelId: "gpt-5.6-terra-524k-fast",
21095
+ name: "GPT-5.6-Terra-524K-Fast",
21080
21096
  capabilityClass: "standard",
21081
21097
  providerModelId: "gpt-5.6-terra",
21082
21098
  serviceTier: "priority",
@@ -21092,7 +21108,7 @@ var models_default = {
21092
21108
  ]
21093
21109
  },
21094
21110
  aliases: [
21095
- "gpt-5.6-terra-512k-fast"
21111
+ "gpt-5.6-terra-524k-fast"
21096
21112
  ],
21097
21113
  benchmarkKey: "gpt-5.6-terra"
21098
21114
  },
@@ -48605,6 +48621,18 @@ var AgentChatSession = class {
48605
48621
  * 답은 턴이 끝난 뒤에 오므로(실측), 그 사이 새 턴이 시작했다면 그 값은 남의 턴 자리에 앉는다.
48606
48622
  */
48607
48623
  contextGeneration = 0;
48624
+ /**
48625
+ * control 요청이 하나 떠 있는가.
48626
+ *
48627
+ * 겹쳐 던지면 자식이 직렬로 처리한다 — 실측하면 요청 하나에 약 30초가 들고, 셋을 던진 마지막
48628
+ * 답은 100초 뒤에 왔다. 그래서 앞선 요청이 살아 있는 동안에는 새로 던지지 않는다. 놓친 창은
48629
+ * 다음 경계(턴 시작·턴 종료)가 다시 얻는다.
48630
+ */
48631
+ contextInFlight = false;
48632
+ /** 턴이 도는 동안 흘러간 라이브 총량. 턴 경계에서 비운다. */
48633
+ liveContextTotal = null;
48634
+ /** 자식이 스냅숏에서 직접 말한 좌표. 도착하기 전에는 모델 id에서 유도한다. */
48635
+ observedClaudeCoordinate = null;
48608
48636
  constructor(operationId, seed, createSdk) {
48609
48637
  this.operationId = operationId;
48610
48638
  this.seed = seed;
@@ -48941,25 +48969,33 @@ var AgentChatSession = class {
48941
48969
  });
48942
48970
  }
48943
48971
  /**
48944
- * 턴이 시작하는 순간 자식에게 문맥 내역을 묻고, 답이 오면 그대로 저널에 싣는다.
48972
+ * 자식에게 문맥 **내역**을 묻고, 답이 오면 저널에 싣는다. 두 경계에서 불린다 — 턴을 보내기
48973
+ * 직전(`"start"`)과 턴이 닫힌 직후(`"end"`).
48945
48974
  *
48946
- * **이 번이 유일한 기회다**(실측). 자식은 턴이 시작되면 control 채널을 닫아, +0ms의 요청만
48947
- * 답을 받고 뒤의 요청은 턴이 10초를 돌든 전부 "Query closed before response received"로
48948
- * 끝난다. 주기적으로 물어 최신값을 좇는 설계는 이 자식에서 성립하지 않는다.
48975
+ * 왕복이 느리다는 것이 배선의 전제다(실측 21~88초, 요청 하나에 30초, 직렬 큐). 그래서
48976
+ * 총량은 채널에 맡기지 않고 `message_delta` usage가 진다. 여기서 얻는 것은 카테고리 분해
48977
+ * 그것만은 control 채널이 유일한 출처다.
48949
48978
  *
48950
- * 그래서 값은 **이 턴이 시작될 때까지의** 문맥이다 방금 끝난 턴이 더한 몫은 다음 턴이
48951
- * 시작될 비로소 드러난다. 화면은 지연을 감추지 않는다.
48979
+ * 중에는 아예 묻지 않는다. 자식이 턴이 시작되면 control을 닫아 +0ms의 요청만 답을 받고
48980
+ * 뒤는 전부 "Query closed before response received"로 끝난다(실측). 유휴 폴링도 하지 않는다 —
48981
+ * 답은 오지만 요청마다 30초를 태우고 큐만 밀린다.
48952
48982
  *
48953
48983
  * 응답을 기다리지 않는 이유는 스트림 때문이다: 여기서 await하면 첫 메시지 소비가 그만큼 늦다.
48954
- * 늦게 도착한 답도 같은 턴의 시작 시점 값이므로, 그때 실어도 뜻이 달라지지 않는다.
48955
48984
  */
48956
- snapshotContextAtTurnStart(session) {
48985
+ requestContextSnapshot(session, asOf) {
48986
+ if (this.contextInFlight) return;
48957
48987
  const generation = ++this.contextGeneration;
48988
+ this.contextInFlight = true;
48989
+ const settle = (usage4) => {
48990
+ this.contextInFlight = false;
48991
+ if (usage4 && generation === this.contextGeneration) this.pushContext(usage4, asOf);
48992
+ };
48958
48993
  try {
48959
- void session.getContextUsage().then((usage4) => {
48960
- if (usage4 && generation === this.contextGeneration) this.pushContext(usage4);
48961
- }, () => void 0);
48994
+ void session.getContextUsage().then(settle, () => {
48995
+ this.contextInFlight = false;
48996
+ });
48962
48997
  } catch {
48998
+ this.contextInFlight = false;
48963
48999
  }
48964
49000
  }
48965
49001
  /**
@@ -48973,25 +49009,100 @@ var AgentChatSession = class {
48973
49009
  * 예약분(자동 압축 여유)과 남은 자리는 **쓴 것이 아니다**. 같은 목록에 두면 합이 언제나 창
48974
49010
  * 전체가 되어 미터가 항상 100%를 가리킨다.
48975
49011
  */
48976
- pushContext(usage4) {
49012
+ pushContext(usage4, asOf) {
49013
+ this.observedClaudeCoordinate = usage4.max;
48977
49014
  const spent = usage4.categories.filter((category) => !category.deferred && category.tokens > 0 && !CONTEXT_UNSPENT.has(category.name));
48978
49015
  const reserved = usage4.categories.find((category) => category.name === CONTEXT_RESERVED_NAME);
49016
+ const window = this.realWindow(usage4.max);
49017
+ const slices = spent.map((category) => ({
49018
+ name: category.name,
49019
+ tokens: window ? window.unproject(category.tokens) : category.tokens
49020
+ }));
48979
49021
  this.push({
48980
49022
  kind: "context",
48981
- total: spent.reduce((sum, category) => sum + category.tokens, 0),
48982
- max: usage4.max,
48983
- ...reserved && reserved.tokens > 0 ? { reserved: reserved.tokens } : {},
48984
- ...usage4.compactAt === null ? {} : { compactAt: usage4.compactAt },
48985
- slices: spent.map((category) => ({ name: category.name, tokens: category.tokens })),
48986
- ...usage4.memoryFiles.length > 0 ? { memoryFiles: usage4.memoryFiles.map((file2) => ({ name: file2.path, tokens: file2.tokens })) } : {},
49023
+ asOf,
49024
+ total: slices.reduce((sum, slice) => sum + slice.tokens, 0),
49025
+ max: window ? window.max : usage4.max,
49026
+ // 예약분은 되돌리지 않고 **갈아 끼운다**. 자식의 값은 자기 좌표의 여유(max − compactAt)이고
49027
+ // 실제 여유는 정책이 정하는 다른 수다 같은 비율로 늘리면 있지도 않은 자리를 예약해 둔다.
49028
+ ...window ? window.max > window.compactAt ? { reserved: window.max - window.compactAt } : {} : reserved && reserved.tokens > 0 ? { reserved: reserved.tokens } : {},
49029
+ ...window ? usage4.compactAt === null ? {} : { compactAt: window.compactAt } : usage4.compactAt === null ? {} : { compactAt: usage4.compactAt },
49030
+ slices,
49031
+ ...usage4.memoryFiles.length > 0 ? {
49032
+ memoryFiles: usage4.memoryFiles.map((file2) => ({
49033
+ name: file2.path,
49034
+ tokens: window ? window.unproject(file2.tokens) : file2.tokens
49035
+ }))
49036
+ } : {},
48987
49037
  ...usage4.mcpTools.length > 0 ? {
48988
49038
  mcpTools: usage4.mcpTools.map((tool) => ({
48989
49039
  name: tool.server ? `${tool.server} \xB7 ${tool.name}` : tool.name,
48990
- tokens: tool.tokens
49040
+ tokens: window ? window.unproject(tool.tokens) : tool.tokens
48991
49041
  }))
48992
49042
  } : {}
48993
49043
  });
48994
49044
  }
49045
+ /**
49046
+ * 턴이 도는 동안 총량을 흘린다. 미터가 이 턴을 따라 움직이는 유일한 경로다.
49047
+ *
49048
+ * 읽는 것은 `message_delta`의 usage **하나뿐**이며, 그것은 소거로 남은 결과다(실측):
49049
+ * `message_start`와 완성 `assistant`의 usage는 언제나 0으로 오고, `result.usage`는 그 턴의 모든
49050
+ * 모델 호출을 **합산한** 값이어서 창 점유가 아니다 — 도구를 다섯 번 돈 턴에서 61,630이었고 같은
49051
+ * 순간의 실제 점유는 15,785였다. 그것을 총량으로 쓰면 미터가 네 배로 뛴다.
49052
+ *
49053
+ * 서브에이전트의 usage도 제외한다. 그 창은 부모의 창이 아니다.
49054
+ */
49055
+ trackLiveContext(message) {
49056
+ if (message.type !== "stream_event") return;
49057
+ if (typeof message.parent_tool_use_id === "string" && message.parent_tool_use_id.length > 0) return;
49058
+ const event = message.event;
49059
+ if (!isRecord14(event) || event.type !== "message_delta") return;
49060
+ const total = readClaudeOccupiedInputTokens(event.usage);
49061
+ if (total === null) return;
49062
+ const claudeCoordinate = this.claudeCoordinate();
49063
+ const window = this.realWindow(claudeCoordinate);
49064
+ const live = window ? window.unproject(total) : total;
49065
+ if (this.liveContextTotal === live) return;
49066
+ this.liveContextTotal = live;
49067
+ this.pushEphemeral({
49068
+ kind: "context-live",
49069
+ total: live,
49070
+ max: window ? window.max : claudeCoordinate
49071
+ });
49072
+ }
49073
+ /**
49074
+ * 자식이 이 세션을 재는 좌표.
49075
+ *
49076
+ * 스냅숏이 한 번이라도 왔으면 자식이 직접 말한 값이 권위다. 그 전에는 유도한다 — 좌표를 정하는
49077
+ * 것은 모델 id의 `[1m]` 마커이고, 그 마커를 붙이는 규칙이 곧 카탈로그 창이 1M에 닿는지 여부다.
49078
+ * 유도가 필요한 이유는 라이브 총량이 첫 스냅숏(왕복 30초)을 기다릴 수 없기 때문이다.
49079
+ */
49080
+ claudeCoordinate() {
49081
+ if (this.observedClaudeCoordinate !== null) return this.observedClaudeCoordinate;
49082
+ const window = this.seed.contextWindow;
49083
+ const oneMillion = typeof window === "number" && Number.isFinite(window) && window > 0 ? window >= CLAUDE_COMPAT_CONTEXT_WINDOW : hasClaudeOneMillionMarker(this.seed.model);
49084
+ return oneMillion ? CLAUDE_COMPAT_CONTEXT_WINDOW : CLAUDE_DEFAULT_CONTEXT_WINDOW;
49085
+ }
49086
+ /**
49087
+ * 자식이 보고한 좌표를 이 모델의 실제 창으로 되돌리는 자.
49088
+ *
49089
+ * `null`은 되돌릴 것이 없다는 뜻이며 세 경우가 여기로 온다: 시드에 카탈로그 창이 없는 세션
49090
+ * (네이티브 Claude 모델·카탈로그 밖 id), 그리고 자식이 자기 두 좌표가 아닌 값을 말한 세션 —
49091
+ * 후자는 역함수가 스스로 항등으로 접는다. 어느 경우든 오늘의 숫자가 그대로 나간다.
49092
+ */
49093
+ realWindow(claudeCoordinate) {
49094
+ const max = this.seed.contextWindow;
49095
+ if (typeof max !== "number" || !Number.isFinite(max) || max <= 0) return null;
49096
+ if (claudeCoordinate !== CLAUDE_DEFAULT_CONTEXT_WINDOW && claudeCoordinate !== CLAUDE_COMPAT_CONTEXT_WINDOW) {
49097
+ return null;
49098
+ }
49099
+ const ceiling = this.seed.compactCeiling ?? null;
49100
+ return {
49101
+ max,
49102
+ compactAt: compactThresholdTokens(max, ceiling),
49103
+ unproject: (tokens) => unprojectClaudeContextInputTokens(tokens, max, claudeCoordinate, ceiling)
49104
+ };
49105
+ }
48995
49106
  push(event) {
48996
49107
  const entry = { seq: ++this.seq, event };
48997
49108
  if (event.kind === "job-progress") {
@@ -49125,6 +49236,7 @@ var AgentChatSession = class {
49125
49236
  if (this.latestSessionId !== this.reportedSessionId) this.syncProviderSessionOnce();
49126
49237
  }
49127
49238
  this.rememberJobOutput(message);
49239
+ this.trackLiveContext(message);
49128
49240
  for (const event of chatEventsFromSdkMessage(message, { cwd: this.seed.cwd, toolNames: this.toolNames })) {
49129
49241
  this.ingest(event);
49130
49242
  }
@@ -49202,10 +49314,13 @@ var AgentChatSession = class {
49202
49314
  });
49203
49315
  this.abandonAsks("The turn ended before the question was answered.");
49204
49316
  this.seed.reportActivity(false);
49317
+ this.liveContextTotal = null;
49205
49318
  const awaiting = this.awaitingTurn;
49206
49319
  this.awaitingTurn = null;
49207
49320
  awaiting?.();
49208
49321
  this.releaseTurnCloseWaiters();
49322
+ const session = this.session;
49323
+ if (session) this.requestContextSnapshot(session, "end");
49209
49324
  }
49210
49325
  /** 자리가 비었음을 줄 서 있던 디스패치들에게 알린다. 결말 하나가 전부를 깨운다. */
49211
49326
  releaseTurnCloseWaiters() {
@@ -49279,7 +49394,7 @@ var AgentChatSession = class {
49279
49394
  const settled = new Promise((resolve3) => {
49280
49395
  this.awaitingTurn = resolve3;
49281
49396
  });
49282
- this.snapshotContextAtTurnStart(session);
49397
+ this.requestContextSnapshot(session, "start");
49283
49398
  session.send(text2);
49284
49399
  this.turnReachedChild = true;
49285
49400
  await settled;
@@ -49418,6 +49533,21 @@ var AgentChatRegistry = class {
49418
49533
  await Promise.all([...operationIds].map((operationId) => this.dispose(operationId)));
49419
49534
  }
49420
49535
  };
49536
+ function isRecord14(value) {
49537
+ return typeof value === "object" && value !== null && !Array.isArray(value);
49538
+ }
49539
+ function readClaudeOccupiedInputTokens(usage4) {
49540
+ if (!isRecord14(usage4)) return null;
49541
+ let total = 0;
49542
+ let found = false;
49543
+ for (const field of ["input_tokens", "cache_read_input_tokens", "cache_creation_input_tokens"]) {
49544
+ const tokens = usage4[field];
49545
+ if (typeof tokens !== "number" || !Number.isFinite(tokens) || tokens < 0) continue;
49546
+ total += tokens;
49547
+ found = true;
49548
+ }
49549
+ return found ? total : null;
49550
+ }
49421
49551
 
49422
49552
  // ../fleet-plugins/terminal/server/agent-api/chat-ws.ts
49423
49553
  var MAX_CHAT_ANSWER_MESSAGE_CHARS = 2e3;
@@ -49530,6 +49660,15 @@ var ANALYSIS_ERROR_CODES = {
49530
49660
  var ANALYST_GATEWAY_CLI_ID = "claude-gateway";
49531
49661
  var ANALYST_DEFAULT_MODEL = "sonnet";
49532
49662
  var ANALYST_DEFAULT_EFFORT = "low";
49663
+ var ANALYST_EFFORT_LEVELS = ["low", "medium", "high"];
49664
+ function clampAnalystEffortLevels(levels) {
49665
+ return ANALYST_EFFORT_LEVELS.filter((level) => levels.includes(level));
49666
+ }
49667
+ function clampAnalystDefaultEffort(levels, fallback) {
49668
+ if (fallback && levels.includes(fallback)) return fallback;
49669
+ if (levels.includes(ANALYST_DEFAULT_EFFORT)) return ANALYST_DEFAULT_EFFORT;
49670
+ return levels[0];
49671
+ }
49533
49672
  var NATIVE_CLAUDE_LABELS = {
49534
49673
  "fable[1m]": "Claude Fable",
49535
49674
  sonnet: "Claude Sonnet",
@@ -49539,26 +49678,33 @@ function nativeClaudeAnalystModels() {
49539
49678
  return NATIVE_CLAUDE_MODEL_ALIASES.map((modelId) => ({
49540
49679
  modelId,
49541
49680
  name: NATIVE_CLAUDE_LABELS[modelId] ?? modelId,
49542
- effort: { supported: true, levels: [...NATIVE_CLAUDE_EFFORTS] }
49681
+ effort: { supported: true, levels: [...ANALYST_EFFORT_LEVELS] }
49543
49682
  }));
49544
49683
  }
49545
49684
  function resolveAnalysisGatewayBaseUrl(origin) {
49546
49685
  return `${origin.replace(/\/+$/u, "")}/plugins/terminal/${AI_GATEWAY_ROUTE_SEGMENT}`;
49547
49686
  }
49548
49687
  function buildAnalysisCatalog(nativeModels, gatewayModels, available) {
49549
- const native = nativeModels.map((model) => ({
49550
- id: model.modelId,
49551
- label: model.name,
49552
- effortLevels: model.effort.supported ? [...model.effort.levels ?? []] : [],
49553
- ...model.modelId === ANALYST_DEFAULT_MODEL ? { defaultEffort: ANALYST_DEFAULT_EFFORT } : model.effort.supported && model.effort.default ? { defaultEffort: model.effort.default } : {}
49554
- }));
49555
- const gateway = gatewayModels.map((model) => ({
49556
- id: toClaudeGatewayModelId(model),
49557
- label: model.displayName,
49558
- // 게이트웨이 모델 스키마에는 기본 강도가 없다. 없는 값을 지어내면 사용자가 고르지 않은
49559
- // 강도로 돈다.
49560
- effortLevels: model.effort.supported ? [...model.effort.levels] : []
49561
- }));
49688
+ const native = nativeModels.map((model) => {
49689
+ const effortLevels = model.effort.supported ? clampAnalystEffortLevels(model.effort.levels ?? []) : [];
49690
+ const defaultEffort = model.modelId === ANALYST_DEFAULT_MODEL ? clampAnalystDefaultEffort(effortLevels, ANALYST_DEFAULT_EFFORT) : model.effort.supported ? clampAnalystDefaultEffort(effortLevels, model.effort.default) : void 0;
49691
+ return {
49692
+ id: model.modelId,
49693
+ label: model.name,
49694
+ effortLevels,
49695
+ ...defaultEffort ? { defaultEffort } : {}
49696
+ };
49697
+ });
49698
+ const gateway = gatewayModels.map((model) => {
49699
+ const effortLevels = model.effort.supported ? clampAnalystEffortLevels(model.effort.levels) : [];
49700
+ return {
49701
+ id: toClaudeGatewayModelId(model),
49702
+ label: model.displayName,
49703
+ // 게이트웨이 모델 스키마에는 기본 강도가 없다. 없는 값을 지어내면 사용자가 고르지 않은
49704
+ // 강도로 돈다. 클램프 후 단이 비면 강도 없는 모델과 같다.
49705
+ effortLevels
49706
+ };
49707
+ });
49562
49708
  const models = [...native, ...gateway];
49563
49709
  return {
49564
49710
  clis: [{
@@ -49575,7 +49721,7 @@ function analysisError(code, message) {
49575
49721
  return { error: { code, message } };
49576
49722
  }
49577
49723
  function isAnalysisSelection(catalog, value) {
49578
- if (!isRecord14(value) || !hasExactKeys(value, ["cliId", "model", "effort", "language"]) || typeof value.cliId !== "string" || typeof value.model !== "string" || value.effort !== void 0 && typeof value.effort !== "string" || value.language !== void 0 && value.language !== "en" && value.language !== "ko") return false;
49724
+ if (!isRecord15(value) || !hasExactKeys(value, ["cliId", "model", "effort", "language"]) || typeof value.cliId !== "string" || typeof value.model !== "string" || value.effort !== void 0 && typeof value.effort !== "string" || value.language !== void 0 && value.language !== "en" && value.language !== "ko") return false;
49579
49725
  const cli = catalog.clis.find((candidate) => candidate.cliId === value.cliId);
49580
49726
  if (!cli?.available) return false;
49581
49727
  const model = cli.models.find((candidate) => candidate.id === value.model);
@@ -49584,9 +49730,9 @@ function isAnalysisSelection(catalog, value) {
49584
49730
  return typeof value.effort === "string" && value.effort.length > 0 && model.effortLevels.includes(value.effort);
49585
49731
  }
49586
49732
  function isMessageBody(value) {
49587
- return isRecord14(value) && hasExactKeys(value, ["text"]) && typeof value.text === "string" && value.text.trim().length > 0;
49733
+ return isRecord15(value) && hasExactKeys(value, ["text"]) && typeof value.text === "string" && value.text.trim().length > 0;
49588
49734
  }
49589
- function isRecord14(value) {
49735
+ function isRecord15(value) {
49590
49736
  return typeof value === "object" && value !== null && !Array.isArray(value);
49591
49737
  }
49592
49738
  function hasExactKeys(value, keys) {
@@ -50153,6 +50299,8 @@ async function createAgentApi(ctx, terminalRuntime, deps) {
50153
50299
  }
50154
50300
  const launchOptions = readLaunchOptions(body, cliId, res);
50155
50301
  if (launchOptions === false) return true;
50302
+ const geometry = readOptionalGeometry(body?.geometry, res);
50303
+ if (geometry === false) return true;
50156
50304
  if (body?.prompt !== void 0 && typeof body.prompt !== "string") {
50157
50305
  ctx.host.http.writeJson(res, 400, { error: "invalid_prompt" });
50158
50306
  return true;
@@ -50193,7 +50341,8 @@ async function createAgentApi(ctx, terminalRuntime, deps) {
50193
50341
  ...launchOptions,
50194
50342
  ...composedPrompt ? { prompt: composedPrompt } : {},
50195
50343
  ...attachmentIds.length > 0 ? { attachmentIds } : {},
50196
- ...chatBorn ? { chatBorn: true } : {}
50344
+ ...chatBorn ? { chatBorn: true } : {},
50345
+ ...geometry ? { geometry } : {}
50197
50346
  });
50198
50347
  } catch (error51) {
50199
50348
  if (attachmentIds.length > 0) launchAttachments.unreserve(attachmentIds);
@@ -50209,6 +50358,24 @@ async function createAgentApi(ctx, terminalRuntime, deps) {
50209
50358
  }
50210
50359
  return value;
50211
50360
  }
50361
+ function readOptionalGeometry(value, res) {
50362
+ if (value === void 0) return void 0;
50363
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
50364
+ ctx.host.http.writeJson(res, 400, { error: "invalid_geometry" });
50365
+ return false;
50366
+ }
50367
+ const candidate = value;
50368
+ const x = candidate.x;
50369
+ const y = candidate.y;
50370
+ const width = candidate.width;
50371
+ const height = candidate.height;
50372
+ const zIndex = candidate.zIndex;
50373
+ if (typeof x !== "number" || !Number.isFinite(x) || typeof y !== "number" || !Number.isFinite(y) || typeof width !== "number" || !Number.isFinite(width) || typeof height !== "number" || !Number.isFinite(height) || typeof zIndex !== "number" || !Number.isFinite(zIndex)) {
50374
+ ctx.host.http.writeJson(res, 400, { error: "invalid_geometry" });
50375
+ return false;
50376
+ }
50377
+ return { x, y, width, height, zIndex };
50378
+ }
50212
50379
  function readLaunchOptions(body, cliId, res) {
50213
50380
  if (body?.model !== void 0 && typeof body.model !== "string" || body?.effort !== void 0 && typeof body.effort !== "string") {
50214
50381
  ctx.host.http.writeJson(res, 400, { error: "invalid_launch_option" });
@@ -50333,6 +50500,7 @@ async function createAgentApi(ctx, terminalRuntime, deps) {
50333
50500
  // "transcript 부재는 상실이 아니라 아직 첫 턴 전"이라는 뜻을 durable하게 남긴다.
50334
50501
  ...launchOptions.chatBorn ? { [CHAT_MODE_PAYLOAD_KEY]: true, [CHAT_BORN_PAYLOAD_KEY]: true } : {}
50335
50502
  },
50503
+ ...launchOptions.geometry ? { geometry: launchOptions.geometry } : {},
50336
50504
  createdAt: session.createdAt
50337
50505
  });
50338
50506
  if (launchOptions.chatBorn) {
@@ -50813,6 +50981,9 @@ async function createAgentApi(ctx, terminalRuntime, deps) {
50813
50981
  });
50814
50982
  } catch {
50815
50983
  }
50984
+ const gatewayModel = findGatewayModel(model);
50985
+ const gatewayContextWindow = typeof gatewayModel?.contextWindow === "number" && Number.isFinite(gatewayModel.contextWindow) && gatewayModel.contextWindow > 0 ? gatewayModel.contextWindow : void 0;
50986
+ const gatewayCompactCeiling = gatewayContextWindow === void 0 ? void 0 : deps.readAiGatewaySettings?.().compactCeiling ?? null;
50816
50987
  const chatSkillOverrides = buildDisabledSkillOverrides(GATEWAY_DISABLED_CLAUDE_SKILLS);
50817
50988
  const systemPromptMode = deps.globalOptionsService.load().claudeGatewaySystemPromptMode ?? "append";
50818
50989
  const systemPrompt = systemPromptMode === "off" ? void 0 : { mode: systemPromptMode, text: createSystemPromptBuilder().build() };
@@ -50822,6 +50993,11 @@ async function createAgentApi(ctx, terminalRuntime, deps) {
50822
50993
  seed: {
50823
50994
  baseUrl: gatewayBaseUrl,
50824
50995
  model,
50996
+ // 자식은 창이 500k인 모델도 자기 200k 좌표로 재고, 그 좌표를 물어보는 모든 표면에 그대로
50997
+ // 말한다. 실제 창과 투영에 쓰인 압축 정책을 함께 실어야 세션이 그것을 되돌릴 수 있다.
50998
+ // 네이티브 Claude 모델은 카탈로그에 없으므로 두 값 없이 지나가고 오늘의 숫자를 유지한다.
50999
+ ...gatewayContextWindow === void 0 ? {} : { contextWindow: gatewayContextWindow },
51000
+ ...gatewayCompactCeiling === void 0 ? {} : { compactCeiling: gatewayCompactCeiling },
50825
51001
  ...launchEffort ? { effort: launchEffort.effort } : {},
50826
51002
  cwd,
50827
51003
  claudeConfigDir,