@dotobokuri/fleet-console 1.65.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 +137 -4
  2. package/dist/client/assets/{_baseUniq-DyzqDsbR.js → _baseUniq-rDf4uMQX.js} +1 -1
  3. package/dist/client/assets/{arc-D9PB38OE.js → arc-btPQ2GHn.js} +1 -1
  4. package/dist/client/assets/{architectureDiagram-Q4EWVU46-0L3Tlker.js → architectureDiagram-Q4EWVU46-DG6KDx34.js} +1 -1
  5. package/dist/client/assets/{blockDiagram-DXYQGD6D-BCAdqHmo.js → blockDiagram-DXYQGD6D-XUrLsQey.js} +1 -1
  6. package/dist/client/assets/{c4Diagram-AHTNJAMY-oK3f-N6i.js → c4Diagram-AHTNJAMY-DuXkZ8b1.js} +1 -1
  7. package/dist/client/assets/channel-CXR6clX_.js +1 -0
  8. package/dist/client/assets/{chunk-4BX2VUAB-CIYxtKO7.js → chunk-4BX2VUAB-C2tTKvHv.js} +1 -1
  9. package/dist/client/assets/{chunk-4TB4RGXK-CKA_-tPR.js → chunk-4TB4RGXK-C5bwM8_h.js} +1 -1
  10. package/dist/client/assets/{chunk-55IACEB6-ArIbE5m1.js → chunk-55IACEB6-BhcOy6C-.js} +1 -1
  11. package/dist/client/assets/{chunk-EDXVE4YY-bGzMBuDL.js → chunk-EDXVE4YY-TOZwStuB.js} +1 -1
  12. package/dist/client/assets/{chunk-FMBD7UC4-CZUkX3ry.js → chunk-FMBD7UC4-B8JSJHVV.js} +1 -1
  13. package/dist/client/assets/{chunk-OYMX7WX6-CNXmIVXX.js → chunk-OYMX7WX6-BsNJ9iub.js} +1 -1
  14. package/dist/client/assets/{chunk-QZHKN3VN-B3t4h1MD.js → chunk-QZHKN3VN-BhHSM1D6.js} +1 -1
  15. package/dist/client/assets/{chunk-YZCP3GAM-CdGF0f8H.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-BESjfcni.js → cose-bilkent-S5V4N54A-D5mwO2NV.js} +1 -1
  20. package/dist/client/assets/{dagre-KV5264BT-WpQhPw2a.js → dagre-KV5264BT-5fSh4g2E.js} +1 -1
  21. package/dist/client/assets/{diagram-5BDNPKRD-BfAkutIX.js → diagram-5BDNPKRD-CtCGKgkm.js} +1 -1
  22. package/dist/client/assets/{diagram-G4DWMVQ6-C9I16SLL.js → diagram-G4DWMVQ6-CmGVsepF.js} +1 -1
  23. package/dist/client/assets/{diagram-MMDJMWI5-CVJMM2zJ.js → diagram-MMDJMWI5-BR9Rfnad.js} +1 -1
  24. package/dist/client/assets/{diagram-TYMM5635-DTqr7jJ3.js → diagram-TYMM5635-C9kuBFny.js} +1 -1
  25. package/dist/client/assets/{erDiagram-SMLLAGMA-B09ElCdB.js → erDiagram-SMLLAGMA-BlL5tH_h.js} +1 -1
  26. package/dist/client/assets/{flowDiagram-DWJPFMVM-C93jg-t6.js → flowDiagram-DWJPFMVM-IUe6EFlH.js} +1 -1
  27. package/dist/client/assets/{ganttDiagram-T4ZO3ILL-5Js2lpdY.js → ganttDiagram-T4ZO3ILL-G-CTHnXi.js} +1 -1
  28. package/dist/client/assets/{gitGraphDiagram-UUTBAWPF-BxGzg1Rc.js → gitGraphDiagram-UUTBAWPF-COUsDQ0n.js} +1 -1
  29. package/dist/client/assets/{graph-BkRyh4eN.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-DZPp1lR6.js → infoDiagram-42DDH7IO-DbNkXGyd.js} +1 -1
  33. package/dist/client/assets/{ishikawaDiagram-UXIWVN3A-CXTuQ2Bb.js → ishikawaDiagram-UXIWVN3A-Iqt-Zs6v.js} +1 -1
  34. package/dist/client/assets/{journeyDiagram-VCZTEJTY--u6tYqhF.js → journeyDiagram-VCZTEJTY-m7TrTD61.js} +1 -1
  35. package/dist/client/assets/{kanban-definition-6JOO6SKY-CGMEohiQ.js → kanban-definition-6JOO6SKY-Df5KWqsi.js} +1 -1
  36. package/dist/client/assets/{layout-Bzu8ET7F.js → layout-BtA7fX7y.js} +1 -1
  37. package/dist/client/assets/{linear-B6htRUTP.js → linear-DVL8mywl.js} +1 -1
  38. package/dist/client/assets/{mermaid.core-CtpLAI-c.js → mermaid.core-CmjUZw2x.js} +4 -4
  39. package/dist/client/assets/{min-CEURoL5k.js → min-BTu70auM.js} +1 -1
  40. package/dist/client/assets/{mindmap-definition-QFDTVHPH-B-HiXlEN.js → mindmap-definition-QFDTVHPH-BYpo-Mnc.js} +1 -1
  41. package/dist/client/assets/{pieDiagram-DEJITSTG-DQgiLkTU.js → pieDiagram-DEJITSTG-B8zFmcim.js} +1 -1
  42. package/dist/client/assets/{quadrantDiagram-34T5L4WZ-xLh7CKRW.js → quadrantDiagram-34T5L4WZ-CMYVBi7F.js} +1 -1
  43. package/dist/client/assets/{requirementDiagram-MS252O5E-O1SOxLMB.js → requirementDiagram-MS252O5E-BFeVMKvJ.js} +1 -1
  44. package/dist/client/assets/{sankeyDiagram-XADWPNL6-D-mh4ZYc.js → sankeyDiagram-XADWPNL6-D2I70l9E.js} +1 -1
  45. package/dist/client/assets/{sequenceDiagram-FGHM5R23-Dp2XhLFP.js → sequenceDiagram-FGHM5R23-zawVvGJJ.js} +1 -1
  46. package/dist/client/assets/{stateDiagram-FHFEXIEX-UQkaQPQt.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-DDMfKpma.js → timeline-definition-GMOUNBTQ-0gfoVpmY.js} +1 -1
  49. package/dist/client/assets/{vennDiagram-DHZGUBPP-DaGxBzx2.js → vennDiagram-DHZGUBPP-CuaEOGH2.js} +1 -1
  50. package/dist/client/assets/{wardley-RL74JXVD-DBNvUuF6.js → wardley-RL74JXVD-C-H0Svof.js} +1 -1
  51. package/dist/client/assets/{wardleyDiagram-NUSXRM2D-IbDED74Y.js → wardleyDiagram-NUSXRM2D-MQjjZFwh.js} +1 -1
  52. package/dist/client/assets/{xychartDiagram-5P7HB3ND-0RafK-TZ.js → xychartDiagram-5P7HB3ND-BhTAVqtf.js} +1 -1
  53. package/dist/client/index.html +2 -2
  54. package/dist/fleet-plugins/ledger/routes.mjs +130 -1
  55. package/dist/fleet-plugins/quota/routes.mjs +130 -1
  56. package/dist/fleet-plugins/scuttlebutt/routes.mjs +130 -1
  57. package/dist/fleet-plugins/terminal/routes.mjs +344 -39
  58. package/dist/fleet.mjs +137 -4
  59. package/package.json +1 -1
  60. package/dist/client/assets/channel-CzEHIBah.js +0 -1
  61. package/dist/client/assets/classDiagram-6PBFFD2Q-jNK4g1rr.js +0 -1
  62. package/dist/client/assets/classDiagram-v2-HSJHXN6E-jNK4g1rr.js +0 -1
  63. package/dist/client/assets/clone-DggrGKv8.js +0 -1
  64. package/dist/client/assets/index-BSVaW3Ov.js +0 -465
  65. package/dist/client/assets/index-DxjgOu3C.css +0 -1
  66. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-CRw5Ot78.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 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",
@@ -20967,6 +20983,135 @@ var models_default = {
20967
20983
  ],
20968
20984
  benchmarkKey: "gpt-5.6-luna"
20969
20985
  },
20986
+ {
20987
+ modelId: "gpt-5.6-sol-524k",
20988
+ name: "GPT-5.6-Sol-524K",
20989
+ capabilityClass: "flagship",
20990
+ providerModelId: "gpt-5.6-sol",
20991
+ contextWindow: 524288,
20992
+ effort: {
20993
+ supported: true,
20994
+ levels: [
20995
+ "low",
20996
+ "medium",
20997
+ "high",
20998
+ "xhigh",
20999
+ "max"
21000
+ ]
21001
+ },
21002
+ aliases: [
21003
+ "gpt-5.6-sol-524k"
21004
+ ],
21005
+ benchmarkKey: "gpt-5.6-sol"
21006
+ },
21007
+ {
21008
+ modelId: "gpt-5.6-sol-524k-fast",
21009
+ name: "GPT-5.6-Sol-524K-Fast",
21010
+ capabilityClass: "flagship",
21011
+ providerModelId: "gpt-5.6-sol",
21012
+ serviceTier: "priority",
21013
+ contextWindow: 524288,
21014
+ effort: {
21015
+ supported: true,
21016
+ levels: [
21017
+ "low",
21018
+ "medium",
21019
+ "high",
21020
+ "xhigh",
21021
+ "max"
21022
+ ]
21023
+ },
21024
+ aliases: [
21025
+ "gpt-5.6-sol-524k-fast"
21026
+ ],
21027
+ benchmarkKey: "gpt-5.6-sol"
21028
+ },
21029
+ {
21030
+ modelId: "gpt-5.6-luna-524k",
21031
+ name: "GPT-5.6-Luna-524K",
21032
+ capabilityClass: "light",
21033
+ providerModelId: "gpt-5.6-luna",
21034
+ contextWindow: 524288,
21035
+ effort: {
21036
+ supported: true,
21037
+ levels: [
21038
+ "low",
21039
+ "medium",
21040
+ "high",
21041
+ "xhigh",
21042
+ "max"
21043
+ ]
21044
+ },
21045
+ aliases: [
21046
+ "gpt-5.6-luna-524k"
21047
+ ],
21048
+ benchmarkKey: "gpt-5.6-luna"
21049
+ },
21050
+ {
21051
+ modelId: "gpt-5.6-luna-524k-fast",
21052
+ name: "GPT-5.6-Luna-524K-Fast",
21053
+ capabilityClass: "light",
21054
+ providerModelId: "gpt-5.6-luna",
21055
+ serviceTier: "priority",
21056
+ contextWindow: 524288,
21057
+ effort: {
21058
+ supported: true,
21059
+ levels: [
21060
+ "low",
21061
+ "medium",
21062
+ "high",
21063
+ "xhigh",
21064
+ "max"
21065
+ ]
21066
+ },
21067
+ aliases: [
21068
+ "gpt-5.6-luna-524k-fast"
21069
+ ],
21070
+ benchmarkKey: "gpt-5.6-luna"
21071
+ },
21072
+ {
21073
+ modelId: "gpt-5.6-terra-524k",
21074
+ name: "GPT-5.6-Terra-524K",
21075
+ capabilityClass: "standard",
21076
+ providerModelId: "gpt-5.6-terra",
21077
+ contextWindow: 524288,
21078
+ effort: {
21079
+ supported: true,
21080
+ levels: [
21081
+ "low",
21082
+ "medium",
21083
+ "high",
21084
+ "xhigh",
21085
+ "max"
21086
+ ]
21087
+ },
21088
+ aliases: [
21089
+ "gpt-5.6-terra-524k"
21090
+ ],
21091
+ benchmarkKey: "gpt-5.6-terra"
21092
+ },
21093
+ {
21094
+ modelId: "gpt-5.6-terra-524k-fast",
21095
+ name: "GPT-5.6-Terra-524K-Fast",
21096
+ capabilityClass: "standard",
21097
+ providerModelId: "gpt-5.6-terra",
21098
+ serviceTier: "priority",
21099
+ contextWindow: 524288,
21100
+ effort: {
21101
+ supported: true,
21102
+ levels: [
21103
+ "low",
21104
+ "medium",
21105
+ "high",
21106
+ "xhigh",
21107
+ "max"
21108
+ ]
21109
+ },
21110
+ aliases: [
21111
+ "gpt-5.6-terra-524k-fast"
21112
+ ],
21113
+ benchmarkKey: "gpt-5.6-terra"
21114
+ },
20970
21115
  {
20971
21116
  modelId: "gpt-5.6-sol-1m",
20972
21117
  name: "GPT-5.6-Sol-1M",
@@ -48476,6 +48621,18 @@ var AgentChatSession = class {
48476
48621
  * 답은 턴이 끝난 뒤에 오므로(실측), 그 사이 새 턴이 시작했다면 그 값은 남의 턴 자리에 앉는다.
48477
48622
  */
48478
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;
48479
48636
  constructor(operationId, seed, createSdk) {
48480
48637
  this.operationId = operationId;
48481
48638
  this.seed = seed;
@@ -48812,25 +48969,33 @@ var AgentChatSession = class {
48812
48969
  });
48813
48970
  }
48814
48971
  /**
48815
- * 턴이 시작하는 순간 자식에게 문맥 내역을 묻고, 답이 오면 그대로 저널에 싣는다.
48972
+ * 자식에게 문맥 **내역**을 묻고, 답이 오면 저널에 싣는다. 두 경계에서 불린다 — 턴을 보내기
48973
+ * 직전(`"start"`)과 턴이 닫힌 직후(`"end"`).
48816
48974
  *
48817
- * **이 번이 유일한 기회다**(실측). 자식은 턴이 시작되면 control 채널을 닫아, +0ms의 요청만
48818
- * 답을 받고 뒤의 요청은 턴이 10초를 돌든 전부 "Query closed before response received"로
48819
- * 끝난다. 주기적으로 물어 최신값을 좇는 설계는 이 자식에서 성립하지 않는다.
48975
+ * 왕복이 느리다는 것이 배선의 전제다(실측 21~88초, 요청 하나에 30초, 직렬 큐). 그래서
48976
+ * 총량은 채널에 맡기지 않고 `message_delta` usage가 진다. 여기서 얻는 것은 카테고리 분해
48977
+ * 그것만은 control 채널이 유일한 출처다.
48820
48978
  *
48821
- * 그래서 값은 **이 턴이 시작될 때까지의** 문맥이다 방금 끝난 턴이 더한 몫은 다음 턴이
48822
- * 시작될 비로소 드러난다. 화면은 지연을 감추지 않는다.
48979
+ * 중에는 아예 묻지 않는다. 자식이 턴이 시작되면 control을 닫아 +0ms의 요청만 답을 받고
48980
+ * 뒤는 전부 "Query closed before response received"로 끝난다(실측). 유휴 폴링도 하지 않는다 —
48981
+ * 답은 오지만 요청마다 30초를 태우고 큐만 밀린다.
48823
48982
  *
48824
48983
  * 응답을 기다리지 않는 이유는 스트림 때문이다: 여기서 await하면 첫 메시지 소비가 그만큼 늦다.
48825
- * 늦게 도착한 답도 같은 턴의 시작 시점 값이므로, 그때 실어도 뜻이 달라지지 않는다.
48826
48984
  */
48827
- snapshotContextAtTurnStart(session) {
48985
+ requestContextSnapshot(session, asOf) {
48986
+ if (this.contextInFlight) return;
48828
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
+ };
48829
48993
  try {
48830
- void session.getContextUsage().then((usage4) => {
48831
- if (usage4 && generation === this.contextGeneration) this.pushContext(usage4);
48832
- }, () => void 0);
48994
+ void session.getContextUsage().then(settle, () => {
48995
+ this.contextInFlight = false;
48996
+ });
48833
48997
  } catch {
48998
+ this.contextInFlight = false;
48834
48999
  }
48835
49000
  }
48836
49001
  /**
@@ -48844,25 +49009,100 @@ var AgentChatSession = class {
48844
49009
  * 예약분(자동 압축 여유)과 남은 자리는 **쓴 것이 아니다**. 같은 목록에 두면 합이 언제나 창
48845
49010
  * 전체가 되어 미터가 항상 100%를 가리킨다.
48846
49011
  */
48847
- pushContext(usage4) {
49012
+ pushContext(usage4, asOf) {
49013
+ this.observedClaudeCoordinate = usage4.max;
48848
49014
  const spent = usage4.categories.filter((category) => !category.deferred && category.tokens > 0 && !CONTEXT_UNSPENT.has(category.name));
48849
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
+ }));
48850
49021
  this.push({
48851
49022
  kind: "context",
48852
- total: spent.reduce((sum, category) => sum + category.tokens, 0),
48853
- max: usage4.max,
48854
- ...reserved && reserved.tokens > 0 ? { reserved: reserved.tokens } : {},
48855
- ...usage4.compactAt === null ? {} : { compactAt: usage4.compactAt },
48856
- slices: spent.map((category) => ({ name: category.name, tokens: category.tokens })),
48857
- ...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
+ } : {},
48858
49037
  ...usage4.mcpTools.length > 0 ? {
48859
49038
  mcpTools: usage4.mcpTools.map((tool) => ({
48860
49039
  name: tool.server ? `${tool.server} \xB7 ${tool.name}` : tool.name,
48861
- tokens: tool.tokens
49040
+ tokens: window ? window.unproject(tool.tokens) : tool.tokens
48862
49041
  }))
48863
49042
  } : {}
48864
49043
  });
48865
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
+ }
48866
49106
  push(event) {
48867
49107
  const entry = { seq: ++this.seq, event };
48868
49108
  if (event.kind === "job-progress") {
@@ -48996,6 +49236,7 @@ var AgentChatSession = class {
48996
49236
  if (this.latestSessionId !== this.reportedSessionId) this.syncProviderSessionOnce();
48997
49237
  }
48998
49238
  this.rememberJobOutput(message);
49239
+ this.trackLiveContext(message);
48999
49240
  for (const event of chatEventsFromSdkMessage(message, { cwd: this.seed.cwd, toolNames: this.toolNames })) {
49000
49241
  this.ingest(event);
49001
49242
  }
@@ -49073,10 +49314,13 @@ var AgentChatSession = class {
49073
49314
  });
49074
49315
  this.abandonAsks("The turn ended before the question was answered.");
49075
49316
  this.seed.reportActivity(false);
49317
+ this.liveContextTotal = null;
49076
49318
  const awaiting = this.awaitingTurn;
49077
49319
  this.awaitingTurn = null;
49078
49320
  awaiting?.();
49079
49321
  this.releaseTurnCloseWaiters();
49322
+ const session = this.session;
49323
+ if (session) this.requestContextSnapshot(session, "end");
49080
49324
  }
49081
49325
  /** 자리가 비었음을 줄 서 있던 디스패치들에게 알린다. 결말 하나가 전부를 깨운다. */
49082
49326
  releaseTurnCloseWaiters() {
@@ -49150,7 +49394,7 @@ var AgentChatSession = class {
49150
49394
  const settled = new Promise((resolve3) => {
49151
49395
  this.awaitingTurn = resolve3;
49152
49396
  });
49153
- this.snapshotContextAtTurnStart(session);
49397
+ this.requestContextSnapshot(session, "start");
49154
49398
  session.send(text2);
49155
49399
  this.turnReachedChild = true;
49156
49400
  await settled;
@@ -49289,6 +49533,21 @@ var AgentChatRegistry = class {
49289
49533
  await Promise.all([...operationIds].map((operationId) => this.dispose(operationId)));
49290
49534
  }
49291
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
+ }
49292
49551
 
49293
49552
  // ../fleet-plugins/terminal/server/agent-api/chat-ws.ts
49294
49553
  var MAX_CHAT_ANSWER_MESSAGE_CHARS = 2e3;
@@ -49401,6 +49660,15 @@ var ANALYSIS_ERROR_CODES = {
49401
49660
  var ANALYST_GATEWAY_CLI_ID = "claude-gateway";
49402
49661
  var ANALYST_DEFAULT_MODEL = "sonnet";
49403
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
+ }
49404
49672
  var NATIVE_CLAUDE_LABELS = {
49405
49673
  "fable[1m]": "Claude Fable",
49406
49674
  sonnet: "Claude Sonnet",
@@ -49410,26 +49678,33 @@ function nativeClaudeAnalystModels() {
49410
49678
  return NATIVE_CLAUDE_MODEL_ALIASES.map((modelId) => ({
49411
49679
  modelId,
49412
49680
  name: NATIVE_CLAUDE_LABELS[modelId] ?? modelId,
49413
- effort: { supported: true, levels: [...NATIVE_CLAUDE_EFFORTS] }
49681
+ effort: { supported: true, levels: [...ANALYST_EFFORT_LEVELS] }
49414
49682
  }));
49415
49683
  }
49416
49684
  function resolveAnalysisGatewayBaseUrl(origin) {
49417
49685
  return `${origin.replace(/\/+$/u, "")}/plugins/terminal/${AI_GATEWAY_ROUTE_SEGMENT}`;
49418
49686
  }
49419
49687
  function buildAnalysisCatalog(nativeModels, gatewayModels, available) {
49420
- const native = nativeModels.map((model) => ({
49421
- id: model.modelId,
49422
- label: model.name,
49423
- effortLevels: model.effort.supported ? [...model.effort.levels ?? []] : [],
49424
- ...model.modelId === ANALYST_DEFAULT_MODEL ? { defaultEffort: ANALYST_DEFAULT_EFFORT } : model.effort.supported && model.effort.default ? { defaultEffort: model.effort.default } : {}
49425
- }));
49426
- const gateway = gatewayModels.map((model) => ({
49427
- id: toClaudeGatewayModelId(model),
49428
- label: model.displayName,
49429
- // 게이트웨이 모델 스키마에는 기본 강도가 없다. 없는 값을 지어내면 사용자가 고르지 않은
49430
- // 강도로 돈다.
49431
- effortLevels: model.effort.supported ? [...model.effort.levels] : []
49432
- }));
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
+ });
49433
49708
  const models = [...native, ...gateway];
49434
49709
  return {
49435
49710
  clis: [{
@@ -49446,7 +49721,7 @@ function analysisError(code, message) {
49446
49721
  return { error: { code, message } };
49447
49722
  }
49448
49723
  function isAnalysisSelection(catalog, value) {
49449
- 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;
49450
49725
  const cli = catalog.clis.find((candidate) => candidate.cliId === value.cliId);
49451
49726
  if (!cli?.available) return false;
49452
49727
  const model = cli.models.find((candidate) => candidate.id === value.model);
@@ -49455,9 +49730,9 @@ function isAnalysisSelection(catalog, value) {
49455
49730
  return typeof value.effort === "string" && value.effort.length > 0 && model.effortLevels.includes(value.effort);
49456
49731
  }
49457
49732
  function isMessageBody(value) {
49458
- 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;
49459
49734
  }
49460
- function isRecord14(value) {
49735
+ function isRecord15(value) {
49461
49736
  return typeof value === "object" && value !== null && !Array.isArray(value);
49462
49737
  }
49463
49738
  function hasExactKeys(value, keys) {
@@ -50024,6 +50299,8 @@ async function createAgentApi(ctx, terminalRuntime, deps) {
50024
50299
  }
50025
50300
  const launchOptions = readLaunchOptions(body, cliId, res);
50026
50301
  if (launchOptions === false) return true;
50302
+ const geometry = readOptionalGeometry(body?.geometry, res);
50303
+ if (geometry === false) return true;
50027
50304
  if (body?.prompt !== void 0 && typeof body.prompt !== "string") {
50028
50305
  ctx.host.http.writeJson(res, 400, { error: "invalid_prompt" });
50029
50306
  return true;
@@ -50064,7 +50341,8 @@ async function createAgentApi(ctx, terminalRuntime, deps) {
50064
50341
  ...launchOptions,
50065
50342
  ...composedPrompt ? { prompt: composedPrompt } : {},
50066
50343
  ...attachmentIds.length > 0 ? { attachmentIds } : {},
50067
- ...chatBorn ? { chatBorn: true } : {}
50344
+ ...chatBorn ? { chatBorn: true } : {},
50345
+ ...geometry ? { geometry } : {}
50068
50346
  });
50069
50347
  } catch (error51) {
50070
50348
  if (attachmentIds.length > 0) launchAttachments.unreserve(attachmentIds);
@@ -50080,6 +50358,24 @@ async function createAgentApi(ctx, terminalRuntime, deps) {
50080
50358
  }
50081
50359
  return value;
50082
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
+ }
50083
50379
  function readLaunchOptions(body, cliId, res) {
50084
50380
  if (body?.model !== void 0 && typeof body.model !== "string" || body?.effort !== void 0 && typeof body.effort !== "string") {
50085
50381
  ctx.host.http.writeJson(res, 400, { error: "invalid_launch_option" });
@@ -50204,6 +50500,7 @@ async function createAgentApi(ctx, terminalRuntime, deps) {
50204
50500
  // "transcript 부재는 상실이 아니라 아직 첫 턴 전"이라는 뜻을 durable하게 남긴다.
50205
50501
  ...launchOptions.chatBorn ? { [CHAT_MODE_PAYLOAD_KEY]: true, [CHAT_BORN_PAYLOAD_KEY]: true } : {}
50206
50502
  },
50503
+ ...launchOptions.geometry ? { geometry: launchOptions.geometry } : {},
50207
50504
  createdAt: session.createdAt
50208
50505
  });
50209
50506
  if (launchOptions.chatBorn) {
@@ -50684,6 +50981,9 @@ async function createAgentApi(ctx, terminalRuntime, deps) {
50684
50981
  });
50685
50982
  } catch {
50686
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;
50687
50987
  const chatSkillOverrides = buildDisabledSkillOverrides(GATEWAY_DISABLED_CLAUDE_SKILLS);
50688
50988
  const systemPromptMode = deps.globalOptionsService.load().claudeGatewaySystemPromptMode ?? "append";
50689
50989
  const systemPrompt = systemPromptMode === "off" ? void 0 : { mode: systemPromptMode, text: createSystemPromptBuilder().build() };
@@ -50693,6 +50993,11 @@ async function createAgentApi(ctx, terminalRuntime, deps) {
50693
50993
  seed: {
50694
50994
  baseUrl: gatewayBaseUrl,
50695
50995
  model,
50996
+ // 자식은 창이 500k인 모델도 자기 200k 좌표로 재고, 그 좌표를 물어보는 모든 표면에 그대로
50997
+ // 말한다. 실제 창과 투영에 쓰인 압축 정책을 함께 실어야 세션이 그것을 되돌릴 수 있다.
50998
+ // 네이티브 Claude 모델은 카탈로그에 없으므로 두 값 없이 지나가고 오늘의 숫자를 유지한다.
50999
+ ...gatewayContextWindow === void 0 ? {} : { contextWindow: gatewayContextWindow },
51000
+ ...gatewayCompactCeiling === void 0 ? {} : { compactCeiling: gatewayCompactCeiling },
50696
51001
  ...launchEffort ? { effort: launchEffort.effort } : {},
50697
51002
  cwd,
50698
51003
  claudeConfigDir,