@dotobokuri/fleet-console 1.65.0 → 1.67.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-DG32CEpb.js} +1 -1
  3. package/dist/client/assets/{arc-D9PB38OE.js → arc-CoHkSCMF.js} +1 -1
  4. package/dist/client/assets/{architectureDiagram-Q4EWVU46-0L3Tlker.js → architectureDiagram-Q4EWVU46-f0RKHqJg.js} +1 -1
  5. package/dist/client/assets/{blockDiagram-DXYQGD6D-BCAdqHmo.js → blockDiagram-DXYQGD6D-Dxb3hLAV.js} +1 -1
  6. package/dist/client/assets/{c4Diagram-AHTNJAMY-oK3f-N6i.js → c4Diagram-AHTNJAMY-C5q_ahSd.js} +1 -1
  7. package/dist/client/assets/channel-C-Qx-Oi-.js +1 -0
  8. package/dist/client/assets/{chunk-4BX2VUAB-CIYxtKO7.js → chunk-4BX2VUAB-BS0W4Fpt.js} +1 -1
  9. package/dist/client/assets/{chunk-4TB4RGXK-CKA_-tPR.js → chunk-4TB4RGXK-p03eWKTO.js} +1 -1
  10. package/dist/client/assets/{chunk-55IACEB6-ArIbE5m1.js → chunk-55IACEB6-BB1ALVX2.js} +1 -1
  11. package/dist/client/assets/{chunk-EDXVE4YY-bGzMBuDL.js → chunk-EDXVE4YY-DV4N-Npm.js} +1 -1
  12. package/dist/client/assets/{chunk-FMBD7UC4-CZUkX3ry.js → chunk-FMBD7UC4-Da8VspnD.js} +1 -1
  13. package/dist/client/assets/{chunk-OYMX7WX6-CNXmIVXX.js → chunk-OYMX7WX6-CWTmAQhy.js} +1 -1
  14. package/dist/client/assets/{chunk-QZHKN3VN-B3t4h1MD.js → chunk-QZHKN3VN-DE9xUsy3.js} +1 -1
  15. package/dist/client/assets/{chunk-YZCP3GAM-CdGF0f8H.js → chunk-YZCP3GAM-BxriNNdB.js} +1 -1
  16. package/dist/client/assets/classDiagram-6PBFFD2Q-DA7e8Mft.js +1 -0
  17. package/dist/client/assets/classDiagram-v2-HSJHXN6E-DA7e8Mft.js +1 -0
  18. package/dist/client/assets/clone-Rpb38n4i.js +1 -0
  19. package/dist/client/assets/{cose-bilkent-S5V4N54A-BESjfcni.js → cose-bilkent-S5V4N54A-B4sjskIn.js} +1 -1
  20. package/dist/client/assets/{dagre-KV5264BT-WpQhPw2a.js → dagre-KV5264BT-oJms5aS3.js} +1 -1
  21. package/dist/client/assets/{diagram-5BDNPKRD-BfAkutIX.js → diagram-5BDNPKRD-BAbLGddM.js} +1 -1
  22. package/dist/client/assets/{diagram-G4DWMVQ6-C9I16SLL.js → diagram-G4DWMVQ6-BrhRQwe-.js} +1 -1
  23. package/dist/client/assets/{diagram-MMDJMWI5-CVJMM2zJ.js → diagram-MMDJMWI5-Buxu7Ng3.js} +1 -1
  24. package/dist/client/assets/{diagram-TYMM5635-DTqr7jJ3.js → diagram-TYMM5635-Cnp__7LJ.js} +1 -1
  25. package/dist/client/assets/{erDiagram-SMLLAGMA-B09ElCdB.js → erDiagram-SMLLAGMA-ctmZHrVO.js} +1 -1
  26. package/dist/client/assets/{flowDiagram-DWJPFMVM-C93jg-t6.js → flowDiagram-DWJPFMVM-DsQXJqF_.js} +1 -1
  27. package/dist/client/assets/{ganttDiagram-T4ZO3ILL-5Js2lpdY.js → ganttDiagram-T4ZO3ILL-SAFJNY9M.js} +1 -1
  28. package/dist/client/assets/{gitGraphDiagram-UUTBAWPF-BxGzg1Rc.js → gitGraphDiagram-UUTBAWPF-Bd5Dq0vQ.js} +1 -1
  29. package/dist/client/assets/{graph-BkRyh4eN.js → graph-DzMOPhFp.js} +1 -1
  30. package/dist/client/assets/index-BLA_qnPk.css +1 -0
  31. package/dist/client/assets/index-CGE8u6Es.js +465 -0
  32. package/dist/client/assets/{infoDiagram-42DDH7IO-DZPp1lR6.js → infoDiagram-42DDH7IO-BrEfG9P4.js} +1 -1
  33. package/dist/client/assets/{ishikawaDiagram-UXIWVN3A-CXTuQ2Bb.js → ishikawaDiagram-UXIWVN3A-Dg22orvH.js} +1 -1
  34. package/dist/client/assets/{journeyDiagram-VCZTEJTY--u6tYqhF.js → journeyDiagram-VCZTEJTY-DEcjEc6c.js} +1 -1
  35. package/dist/client/assets/{kanban-definition-6JOO6SKY-CGMEohiQ.js → kanban-definition-6JOO6SKY-C2Xa4znN.js} +1 -1
  36. package/dist/client/assets/{layout-Bzu8ET7F.js → layout-B7E_-0ev.js} +1 -1
  37. package/dist/client/assets/{linear-B6htRUTP.js → linear-BXJsh3FL.js} +1 -1
  38. package/dist/client/assets/{mermaid.core-CtpLAI-c.js → mermaid.core-CdZpLnyY.js} +4 -4
  39. package/dist/client/assets/{min-CEURoL5k.js → min-CEAFFcJY.js} +1 -1
  40. package/dist/client/assets/{mindmap-definition-QFDTVHPH-B-HiXlEN.js → mindmap-definition-QFDTVHPH-iXzcnu1C.js} +1 -1
  41. package/dist/client/assets/{pieDiagram-DEJITSTG-DQgiLkTU.js → pieDiagram-DEJITSTG-CdRN75ad.js} +1 -1
  42. package/dist/client/assets/{quadrantDiagram-34T5L4WZ-xLh7CKRW.js → quadrantDiagram-34T5L4WZ-Dscl0EFH.js} +1 -1
  43. package/dist/client/assets/{requirementDiagram-MS252O5E-O1SOxLMB.js → requirementDiagram-MS252O5E-CLmSCaQs.js} +1 -1
  44. package/dist/client/assets/{sankeyDiagram-XADWPNL6-D-mh4ZYc.js → sankeyDiagram-XADWPNL6-BfPk87jf.js} +1 -1
  45. package/dist/client/assets/{sequenceDiagram-FGHM5R23-Dp2XhLFP.js → sequenceDiagram-FGHM5R23-DE8sU8ej.js} +1 -1
  46. package/dist/client/assets/{stateDiagram-FHFEXIEX-UQkaQPQt.js → stateDiagram-FHFEXIEX-BmaQc1Nm.js} +1 -1
  47. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-TcoFpvuT.js +1 -0
  48. package/dist/client/assets/{timeline-definition-GMOUNBTQ-DDMfKpma.js → timeline-definition-GMOUNBTQ-C4Z0umzx.js} +1 -1
  49. package/dist/client/assets/{vennDiagram-DHZGUBPP-DaGxBzx2.js → vennDiagram-DHZGUBPP-BKlcRXlE.js} +1 -1
  50. package/dist/client/assets/{wardley-RL74JXVD-DBNvUuF6.js → wardley-RL74JXVD-B0RT7bAW.js} +1 -1
  51. package/dist/client/assets/{wardleyDiagram-NUSXRM2D-IbDED74Y.js → wardleyDiagram-NUSXRM2D-mx28kvam.js} +1 -1
  52. package/dist/client/assets/{xychartDiagram-5P7HB3ND-0RafK-TZ.js → xychartDiagram-5P7HB3ND-DU6qIPI0.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 +747 -115
  58. package/dist/fleet.mjs +370 -57
  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);
@@ -20587,18 +20603,25 @@ function withheldClaudeSkillStub(name, tokens, budget) {
20587
20603
  var CLAUDE_WEB_SEARCH_TOOL_NAME = "WebSearch";
20588
20604
  var ANTHROPIC_WEB_SEARCH_TOOL_NAME = "web_search";
20589
20605
  var ANTHROPIC_WEB_SEARCH_TOOL_TYPE = "web_search_20250305";
20590
- function isClaudeWebSearchTool(tool) {
20591
- return tool.type === ANTHROPIC_WEB_SEARCH_TOOL_TYPE || tool.name === CLAUDE_WEB_SEARCH_TOOL_NAME || tool.name === ANTHROPIC_WEB_SEARCH_TOOL_NAME;
20606
+ var CLAUDE_WEB_SEARCH_TOOL_IDENTIFIERS = [
20607
+ CLAUDE_WEB_SEARCH_TOOL_NAME,
20608
+ ANTHROPIC_WEB_SEARCH_TOOL_NAME,
20609
+ ANTHROPIC_WEB_SEARCH_TOOL_TYPE
20610
+ ];
20611
+ function matchesToolIdentifier(tool, identifiers) {
20612
+ return typeof tool.name === "string" && identifiers.has(tool.name) || typeof tool.type === "string" && identifiers.has(tool.type);
20592
20613
  }
20593
- function isClaudeWebSearchToolChoice(choice) {
20594
- return choice?.type === "tool" && (choice.name === CLAUDE_WEB_SEARCH_TOOL_NAME || choice.name === ANTHROPIC_WEB_SEARCH_TOOL_NAME);
20614
+ function matchesToolChoice(choice, identifiers) {
20615
+ return choice?.type === "tool" && typeof choice.name === "string" && identifiers.has(choice.name);
20595
20616
  }
20596
- function omitClaudeWebSearchTools(request) {
20617
+ function omitClaudeClientTools(request, names) {
20618
+ const identifiers = new Set(names);
20619
+ if (identifiers.size === 0) return { request, changed: false };
20597
20620
  const tools = request.tools;
20598
- const kept = tools?.filter((tool) => !isClaudeWebSearchTool(tool));
20621
+ const kept = tools?.filter((tool) => !matchesToolIdentifier(tool, identifiers));
20599
20622
  const toolsChanged = tools !== void 0 && kept !== void 0 && kept.length !== tools.length;
20600
20623
  const choice = request.tool_choice;
20601
- const choiceChanged = isClaudeWebSearchToolChoice(choice);
20624
+ const choiceChanged = matchesToolChoice(choice, identifiers);
20602
20625
  if (!toolsChanged && !choiceChanged) {
20603
20626
  return { request, changed: false };
20604
20627
  }
@@ -20617,6 +20640,42 @@ function omitClaudeWebSearchTools(request) {
20617
20640
  changed: true
20618
20641
  };
20619
20642
  }
20643
+ var CLAUDE_USAGE_LIMIT_DIRECTIVE = /^\s*\[Usage limit [^\]]*\]\s*$/;
20644
+ function stripClaudeUsageLimitDirectives(messages) {
20645
+ let removed = 0;
20646
+ const kept = [];
20647
+ for (const message of messages) {
20648
+ const stripped = stripUsageLimitBlocks(message);
20649
+ removed += stripped.removed;
20650
+ if (stripped.message !== null) kept.push(stripped.message);
20651
+ }
20652
+ if (removed === 0) return { messages, changed: false, removed: 0 };
20653
+ const last = kept[kept.length - 1];
20654
+ if (last === void 0 || last.role === "assistant") {
20655
+ return { messages, changed: false, removed: 0 };
20656
+ }
20657
+ return { messages: kept, changed: true, removed };
20658
+ }
20659
+ function stripUsageLimitBlocks(message) {
20660
+ const content = message.content;
20661
+ if (typeof content === "string") {
20662
+ return isClaudeUsageLimitDirective(content) ? { message: null, removed: 1 } : { message, removed: 0 };
20663
+ }
20664
+ if (!Array.isArray(content)) return { message, removed: 0 };
20665
+ const kept = content.filter(
20666
+ (block) => !(isClaudeTextBlock(block) && isClaudeUsageLimitDirective(block.text))
20667
+ );
20668
+ const removed = content.length - kept.length;
20669
+ if (removed === 0) return { message, removed: 0 };
20670
+ if (kept.length === 0) return { message: null, removed };
20671
+ return { message: { ...message, content: kept }, removed };
20672
+ }
20673
+ function isClaudeUsageLimitDirective(text2) {
20674
+ return CLAUDE_USAGE_LIMIT_DIRECTIVE.test(text2);
20675
+ }
20676
+ function omitClaudeWebSearchTools(request) {
20677
+ return omitClaudeClientTools(request, CLAUDE_WEB_SEARCH_TOOL_IDENTIFIERS);
20678
+ }
20620
20679
  var benchmarks_default = {
20621
20680
  version: 2,
20622
20681
  sources: {
@@ -20839,7 +20898,7 @@ var models_default = {
20839
20898
  codex: {
20840
20899
  name: "Codex",
20841
20900
  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",
20901
+ 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
20902
  models: [
20844
20903
  {
20845
20904
  modelId: "gpt-5.6-sol",
@@ -20967,6 +21026,135 @@ var models_default = {
20967
21026
  ],
20968
21027
  benchmarkKey: "gpt-5.6-luna"
20969
21028
  },
21029
+ {
21030
+ modelId: "gpt-5.6-sol-524k",
21031
+ name: "GPT-5.6-Sol-524K",
21032
+ capabilityClass: "flagship",
21033
+ providerModelId: "gpt-5.6-sol",
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-sol-524k"
21047
+ ],
21048
+ benchmarkKey: "gpt-5.6-sol"
21049
+ },
21050
+ {
21051
+ modelId: "gpt-5.6-sol-524k-fast",
21052
+ name: "GPT-5.6-Sol-524K-Fast",
21053
+ capabilityClass: "flagship",
21054
+ providerModelId: "gpt-5.6-sol",
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-sol-524k-fast"
21069
+ ],
21070
+ benchmarkKey: "gpt-5.6-sol"
21071
+ },
21072
+ {
21073
+ modelId: "gpt-5.6-luna-524k",
21074
+ name: "GPT-5.6-Luna-524K",
21075
+ capabilityClass: "light",
21076
+ providerModelId: "gpt-5.6-luna",
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-luna-524k"
21090
+ ],
21091
+ benchmarkKey: "gpt-5.6-luna"
21092
+ },
21093
+ {
21094
+ modelId: "gpt-5.6-luna-524k-fast",
21095
+ name: "GPT-5.6-Luna-524K-Fast",
21096
+ capabilityClass: "light",
21097
+ providerModelId: "gpt-5.6-luna",
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-luna-524k-fast"
21112
+ ],
21113
+ benchmarkKey: "gpt-5.6-luna"
21114
+ },
21115
+ {
21116
+ modelId: "gpt-5.6-terra-524k",
21117
+ name: "GPT-5.6-Terra-524K",
21118
+ capabilityClass: "standard",
21119
+ providerModelId: "gpt-5.6-terra",
21120
+ contextWindow: 524288,
21121
+ effort: {
21122
+ supported: true,
21123
+ levels: [
21124
+ "low",
21125
+ "medium",
21126
+ "high",
21127
+ "xhigh",
21128
+ "max"
21129
+ ]
21130
+ },
21131
+ aliases: [
21132
+ "gpt-5.6-terra-524k"
21133
+ ],
21134
+ benchmarkKey: "gpt-5.6-terra"
21135
+ },
21136
+ {
21137
+ modelId: "gpt-5.6-terra-524k-fast",
21138
+ name: "GPT-5.6-Terra-524K-Fast",
21139
+ capabilityClass: "standard",
21140
+ providerModelId: "gpt-5.6-terra",
21141
+ serviceTier: "priority",
21142
+ contextWindow: 524288,
21143
+ effort: {
21144
+ supported: true,
21145
+ levels: [
21146
+ "low",
21147
+ "medium",
21148
+ "high",
21149
+ "xhigh",
21150
+ "max"
21151
+ ]
21152
+ },
21153
+ aliases: [
21154
+ "gpt-5.6-terra-524k-fast"
21155
+ ],
21156
+ benchmarkKey: "gpt-5.6-terra"
21157
+ },
20970
21158
  {
20971
21159
  modelId: "gpt-5.6-sol-1m",
20972
21160
  name: "GPT-5.6-Sol-1M",
@@ -26096,22 +26284,42 @@ function parseXaiEventStream(body, options, functionCallTimeoutMs) {
26096
26284
  functionCallTimeoutMs
26097
26285
  );
26098
26286
  }
26287
+ var XAI_ASSEMBLY_DEADLINE = /* @__PURE__ */ Symbol("xai-assembly-deadline");
26099
26288
  async function* assembleXaiFunctionCalls(source, controller, timeoutMs) {
26100
26289
  const iterator = source[Symbol.asyncIterator]();
26101
26290
  const pending = /* @__PURE__ */ new Map();
26291
+ let snapshot;
26102
26292
  let completedNormally = false;
26103
26293
  try {
26104
26294
  while (true) {
26105
26295
  const deadlineAt = earliestXaiFunctionCallDeadline(pending);
26106
- const result = deadlineAt === void 0 ? await iterator.next() : await nextXaiEventBeforeDeadline(iterator, deadlineAt, controller, timeoutMs);
26296
+ const result = deadlineAt === void 0 ? await iterator.next() : await nextXaiEventBeforeDeadline(iterator, deadlineAt);
26297
+ if (result === XAI_ASSEMBLY_DEADLINE) {
26298
+ yield* salvageXaiPendingCalls(
26299
+ pending,
26300
+ snapshot,
26301
+ `function call assembly exceeded ${timeoutMs}ms`,
26302
+ controller
26303
+ );
26304
+ completedNormally = true;
26305
+ return;
26306
+ }
26107
26307
  if (result.done) {
26108
26308
  if (pending.size > 0) {
26109
- throw incompleteXaiFunctionCall("stream ended before function call output_item.done");
26309
+ yield* salvageXaiPendingCalls(
26310
+ pending,
26311
+ snapshot,
26312
+ "stream ended before function call output_item.done",
26313
+ controller
26314
+ );
26110
26315
  }
26111
26316
  completedNormally = true;
26112
26317
  return;
26113
26318
  }
26114
26319
  const event = result.value;
26320
+ if (event.type === "response.created") {
26321
+ snapshot = event.response;
26322
+ }
26115
26323
  if (event.type === "response.completed" && pending.size > 0) {
26116
26324
  throw incompleteXaiFunctionCall("response.completed arrived before function call output_item.done");
26117
26325
  }
@@ -26122,10 +26330,18 @@ async function* assembleXaiFunctionCalls(source, controller, timeoutMs) {
26122
26330
  // Each call gets a full timeout from its own added event. Waiting uses the
26123
26331
  // earliest active deadline, so a later parallel call is never shortened by
26124
26332
  // an earlier call's age.
26125
- deadlineAt: Date.now() + timeoutMs
26333
+ deadlineAt: Date.now() + timeoutMs,
26334
+ argumentsBuffer: ""
26126
26335
  });
26127
26336
  continue;
26128
26337
  }
26338
+ if (event.type === "response.function_call_arguments.delta") {
26339
+ const call = pending.get(xaiFunctionCallKey(event.output_index, event.item_id));
26340
+ if (call !== void 0) {
26341
+ call.argumentsBuffer += event.delta;
26342
+ }
26343
+ continue;
26344
+ }
26129
26345
  if (event.type === "response.function_call_arguments.done") {
26130
26346
  const call = pending.get(xaiFunctionCallKey(event.output_index, event.item_id));
26131
26347
  if (call !== void 0) {
@@ -26171,24 +26387,60 @@ function earliestXaiFunctionCallDeadline(pending) {
26171
26387
  }
26172
26388
  return earliest;
26173
26389
  }
26174
- async function nextXaiEventBeforeDeadline(iterator, deadlineAt, controller, timeoutMs) {
26175
- const remainingMs = deadlineAt - Date.now();
26176
- if (remainingMs <= 0) {
26177
- const error51 = incompleteXaiFunctionCall(`function call assembly exceeded ${timeoutMs}ms`);
26390
+ async function* salvageXaiPendingCalls(pending, snapshot, reason, controller) {
26391
+ const salvageable = [...pending.values()].every(
26392
+ (call) => completeXaiArguments(call) !== void 0
26393
+ );
26394
+ if (snapshot === void 0 || !salvageable) {
26395
+ const error51 = incompleteXaiFunctionCall(reason);
26178
26396
  controller.abort(error51);
26179
26397
  throw error51;
26180
26398
  }
26399
+ wireLog("xai-responses.function_call.salvaged", {
26400
+ reason,
26401
+ calls: pending.size
26402
+ });
26403
+ controller.abort(incompleteXaiFunctionCall(reason));
26404
+ for (const [key, call] of pending) {
26405
+ const args = completeXaiArguments(call) ?? "";
26406
+ const item = { ...call.added.item, arguments: args };
26407
+ yield call.added;
26408
+ yield {
26409
+ type: "response.function_call_arguments.done",
26410
+ item_id: call.added.item.id,
26411
+ output_index: call.added.output_index,
26412
+ arguments: args
26413
+ };
26414
+ yield { type: "response.output_item.done", output_index: call.added.output_index, item };
26415
+ pending.delete(key);
26416
+ }
26417
+ yield { type: "response.completed", response: { ...snapshot, usage: null } };
26418
+ }
26419
+ function completeXaiArguments(call) {
26420
+ const candidate = call.argumentsDone?.arguments ?? call.argumentsBuffer;
26421
+ if (candidate.length === 0) return void 0;
26422
+ let parsed;
26423
+ try {
26424
+ parsed = JSON.parse(candidate);
26425
+ } catch {
26426
+ return void 0;
26427
+ }
26428
+ return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? candidate : void 0;
26429
+ }
26430
+ async function nextXaiEventBeforeDeadline(iterator, deadlineAt) {
26431
+ const remainingMs = deadlineAt - Date.now();
26432
+ if (remainingMs <= 0) {
26433
+ return XAI_ASSEMBLY_DEADLINE;
26434
+ }
26181
26435
  let timeout;
26182
26436
  const next = iterator.next();
26183
26437
  void next.catch(() => void 0);
26184
26438
  try {
26185
26439
  return await Promise.race([
26186
26440
  next,
26187
- new Promise((_, reject) => {
26441
+ new Promise((resolve3) => {
26188
26442
  timeout = setTimeout(() => {
26189
- const error51 = incompleteXaiFunctionCall(`function call assembly exceeded ${timeoutMs}ms`);
26190
- controller.abort(error51);
26191
- reject(error51);
26443
+ resolve3(XAI_ASSEMBLY_DEADLINE);
26192
26444
  }, remainingMs);
26193
26445
  })
26194
26446
  ]);
@@ -26223,6 +26475,10 @@ function parseEventFrame3(frame) {
26223
26475
  }
26224
26476
  return canonicalEvent3(parsed);
26225
26477
  }
26478
+ var XAI_EOS_SENTINEL = "<|eos|>";
26479
+ function withoutXaiSentinel(text2) {
26480
+ return text2.includes(XAI_EOS_SENTINEL) ? text2.split(XAI_EOS_SENTINEL).join("") : text2;
26481
+ }
26226
26482
  function canonicalEvent3(value) {
26227
26483
  if (!isRecord7(value) || typeof value.type !== "string") {
26228
26484
  throw new UpstreamProtocolError("Grok CLI SSE event was not an object with a type");
@@ -26242,25 +26498,30 @@ function canonicalEvent3(value) {
26242
26498
  content_index: number32(value.content_index, "content_index"),
26243
26499
  part: {
26244
26500
  type: "output_text",
26245
- text: typeof part.text === "string" ? part.text : ""
26501
+ text: typeof part.text === "string" ? withoutXaiSentinel(part.text) : ""
26246
26502
  }
26247
26503
  };
26248
26504
  }
26249
- case "response.output_text.delta":
26505
+ case "response.output_text.delta": {
26506
+ const delta = withoutXaiSentinel(string32(value.delta, "delta"));
26507
+ if (delta.length === 0) {
26508
+ return void 0;
26509
+ }
26250
26510
  return {
26251
26511
  type: value.type,
26252
26512
  item_id: string32(value.item_id, "item_id"),
26253
26513
  output_index: number32(value.output_index, "output_index"),
26254
26514
  content_index: number32(value.content_index, "content_index"),
26255
- delta: string32(value.delta, "delta")
26515
+ delta
26256
26516
  };
26517
+ }
26257
26518
  case "response.output_text.done":
26258
26519
  return {
26259
26520
  type: value.type,
26260
26521
  item_id: string32(value.item_id, "item_id"),
26261
26522
  output_index: number32(value.output_index, "output_index"),
26262
26523
  content_index: number32(value.content_index, "content_index"),
26263
- text: string32(value.text, "text")
26524
+ text: withoutXaiSentinel(string32(value.text, "text"))
26264
26525
  };
26265
26526
  case "response.reasoning_summary_text.delta":
26266
26527
  case "response.reasoning_text.delta":
@@ -26282,11 +26543,17 @@ function canonicalEvent3(value) {
26282
26543
  item
26283
26544
  };
26284
26545
  }
26285
- // Hold argument fragments until the Grok CLI proxy's partial-JSON behavior is observed.
26286
- // The `.done` event carries the complete argument object, which downstream treats as the
26287
- // remainder when nothing was streamed ahead of it.
26546
+ // Fragments still never reach the client the assembler absorbs them and the `.done`
26547
+ // event carries the complete argument object. They are decoded rather than discarded
26548
+ // because this proxy has been measured ending a stream with the whole argument object
26549
+ // delivered as fragments and no terminal frame; the buffer is that turn's only copy.
26288
26550
  case "response.function_call_arguments.delta":
26289
- return void 0;
26551
+ return {
26552
+ type: value.type,
26553
+ item_id: string32(value.item_id, "item_id"),
26554
+ output_index: number32(value.output_index, "output_index"),
26555
+ delta: string32(value.delta, "delta")
26556
+ };
26290
26557
  case "response.function_call_arguments.done":
26291
26558
  return {
26292
26559
  type: value.type,
@@ -26756,6 +27023,10 @@ function isCursorNativeRedirectToolName(name) {
26756
27023
  const leaf = toolLeafName3(name).replace(/[_-]/g, "").toLowerCase();
26757
27024
  return ["grep", "bash", "shellcommand", "execcommand"].includes(leaf);
26758
27025
  }
27026
+ function isCursorWithheldToolName(name) {
27027
+ const leaf = toolLeafName3(name).replace(/[_-]/g, "").toLowerCase();
27028
+ return ["bash", "shellcommand", "execcommand"].includes(leaf);
27029
+ }
26759
27030
  function cursorNativeExecRedirect(exec2, tools, providerIdentifier) {
26760
27031
  const messageId = numberValue2(exec2.id ?? 0);
26761
27032
  const execId = stringValue2(exec2.execId) || `redirect-${messageId}`;
@@ -26967,22 +27238,23 @@ function grepArguments(tool, input) {
26967
27238
  args.path = input.path;
26968
27239
  }
26969
27240
  const optionMappings = [
26970
- [input.glob || void 0, "glob"],
26971
- [input.outputMode, "output_mode"],
26972
- [input.caseInsensitive ? true : void 0, "case_insensitive"],
26973
- [input.contextBefore, "context_before"],
26974
- [input.contextAfter, "context_after"],
26975
- [input.context, "context"],
26976
- [input.type, "type"],
26977
- [input.headLimit, "head_limit"],
26978
- [input.multiline ? true : void 0, "multiline"],
26979
- [input.sort, "sort"],
26980
- [input.sortAscending, "sort_ascending"],
26981
- [input.offset, "offset"]
27241
+ [input.glob || void 0, ["glob"]],
27242
+ [input.outputMode, ["output_mode"]],
27243
+ [input.caseInsensitive ? true : void 0, ["-i", "case_insensitive"]],
27244
+ [input.contextBefore, ["-B", "context_before"]],
27245
+ [input.contextAfter, ["-A", "context_after"]],
27246
+ [input.context, ["context", "-C"]],
27247
+ [input.type, ["type"]],
27248
+ [input.headLimit, ["head_limit"]],
27249
+ [input.multiline ? true : void 0, ["multiline"]],
27250
+ [input.sort, ["sort"]],
27251
+ [input.sortAscending, ["sort_ascending"]],
27252
+ [input.offset, ["offset"]]
26982
27253
  ];
26983
- for (const [value, key] of optionMappings) {
27254
+ for (const [value, candidates] of optionMappings) {
26984
27255
  if (value === void 0) continue;
26985
- if (!schemaHasProperty(tool.inputSchemaValue, key)) return null;
27256
+ const key = firstSchemaProperty(tool.inputSchemaValue, candidates);
27257
+ if (key === void 0) return null;
26986
27258
  args[key] = value;
26987
27259
  }
26988
27260
  return args;
@@ -28601,7 +28873,7 @@ function applyCursorToolBudget(request) {
28601
28873
  const supportsDeferredLoading = declaredTools.some((tool) => isCursorToolSearchName(tool.name));
28602
28874
  const sourceTools = declaredTools.filter((tool) => {
28603
28875
  const explicitlySelected = referencedNames.has(tool.name) || cursorToolMatches(tool.name, selectedName);
28604
- if (isCursorNativeRedirectToolName(tool.name) && !explicitlySelected) return false;
28876
+ if (isCursorWithheldToolName(tool.name) && !explicitlySelected) return false;
28605
28877
  return !supportsDeferredLoading || tool.defer_loading !== true || isCursorToolSearchName(tool.name) || explicitlySelected;
28606
28878
  });
28607
28879
  const wireTools = sourceTools.map(toCursorWireTool);
@@ -31001,6 +31273,63 @@ function bareModelName(model) {
31001
31273
  const prefix = `${GATEWAY_PROVIDER_NAMES[model.provider]}-`;
31002
31274
  return model.displayName.startsWith(prefix) ? model.displayName.slice(prefix.length) : model.displayName;
31003
31275
  }
31276
+ var codexRequestPolicy = {
31277
+ provider: "codex",
31278
+ shapeRequest: (request, steps) => steps.withholdWebSearchTools(steps.pruneSkillPayloads(request))
31279
+ };
31280
+ var cursorRequestPolicy = {
31281
+ provider: "cursor",
31282
+ shapeRequest: (request, steps) => steps.withholdWebSearchTools(steps.pruneSkillPayloads(request))
31283
+ };
31284
+ var kimiRequestPolicy = {
31285
+ provider: "kimi",
31286
+ shapeRequest: (request, steps) => steps.pruneSkillPayloads(request)
31287
+ };
31288
+ var opencodeGoRequestPolicy = {
31289
+ provider: "opencode",
31290
+ shapeRequest: (request, steps) => steps.withholdWebSearchTools(steps.pruneSkillPayloads(request))
31291
+ };
31292
+ var xaiRequestPolicy = {
31293
+ provider: "xai",
31294
+ shapeRequest: (request, steps) => steps.withholdWebSearchTools(steps.pruneSkillPayloads(request))
31295
+ };
31296
+ var POLICIES = {
31297
+ codex: codexRequestPolicy,
31298
+ cursor: cursorRequestPolicy,
31299
+ kimi: kimiRequestPolicy,
31300
+ // The provider id is `opencode`; its folder carries the `-go` subscription name.
31301
+ opencode: opencodeGoRequestPolicy,
31302
+ xai: xaiRequestPolicy
31303
+ };
31304
+ function resolveGatewayRequestPolicy(provider) {
31305
+ return POLICIES[provider];
31306
+ }
31307
+ function applyGatewayRequestPolicy(request, target2, withheldSkills) {
31308
+ const context = {
31309
+ target: target2,
31310
+ upstreamModel: upstreamModelId(target2),
31311
+ withheldSkills
31312
+ };
31313
+ const policy = resolveGatewayRequestPolicy(target2.provider);
31314
+ return policy.shapeRequest(request, buildPolicySteps(context), context);
31315
+ }
31316
+ function buildPolicySteps(context) {
31317
+ return {
31318
+ pruneSkillPayloads: (request) => {
31319
+ const pruned = pruneClaudeSkillPayloads(request.messages, {
31320
+ ...typeof context.target.contextWindow === "number" ? { contextWindow: context.target.contextWindow } : {},
31321
+ model: context.upstreamModel,
31322
+ withheld: context.withheldSkills
31323
+ });
31324
+ for (const skill of pruned.withheld) context.withheldSkills.add(skill.name);
31325
+ return pruned.changed ? { ...request, messages: [...pruned.messages] } : request;
31326
+ },
31327
+ withholdWebSearchTools: (request) => {
31328
+ const omitted = omitClaudeWebSearchTools(request);
31329
+ return omitted.changed ? omitted.request : request;
31330
+ }
31331
+ };
31332
+ }
31004
31333
  var HOP_BY_HOP_HEADERS = /* @__PURE__ */ new Set([
31005
31334
  "connection",
31006
31335
  "content-encoding",
@@ -31219,19 +31548,11 @@ function createAiGatewayRouter(deps) {
31219
31548
  writeAnthropicError(res, 403, "permission_error", `AI gateway model is not enabled: ${requested}`);
31220
31549
  return true;
31221
31550
  }
31222
- if (target2) {
31223
- const pruned = pruneClaudeSkillPayloads(body.messages, {
31224
- ...typeof target2.contextWindow === "number" ? { contextWindow: target2.contextWindow } : {},
31225
- model: upstreamModelId(target2),
31226
- withheld: withheldSkills
31227
- });
31228
- for (const skill of pruned.withheld) withheldSkills.add(skill.name);
31229
- if (pruned.changed) body = { ...body, messages: [...pruned.messages] };
31230
- }
31231
- if (target2 && target2.provider !== "kimi") {
31232
- const omitted = omitClaudeWebSearchTools(body);
31233
- if (omitted.changed) body = omitted.request;
31551
+ const withoutUsageLimitDirectives = stripClaudeUsageLimitDirectives(body.messages);
31552
+ if (withoutUsageLimitDirectives.changed) {
31553
+ body = { ...body, messages: [...withoutUsageLimitDirectives.messages] };
31234
31554
  }
31555
+ if (target2) body = applyGatewayRequestPolicy(body, target2, withheldSkills);
31235
31556
  let credential = "";
31236
31557
  let chatgptAccountId = "";
31237
31558
  if (target2?.provider === "cursor") {
@@ -32193,9 +32514,13 @@ function buildClaudeGatewayArgs(context) {
32193
32514
  ]),
32194
32515
  ...context.mcpServers.length > 0 ? ["--mcp-config", buildClaudeMcpConfig(context.mcpServers)] : [],
32195
32516
  ...buildSettingsArgs(context.skillOverrides),
32517
+ ...buildSearchToolArgs(),
32196
32518
  "--dangerously-skip-permissions"
32197
32519
  ];
32198
32520
  }
32521
+ function buildSearchToolArgs() {
32522
+ return ["--allowedTools", "Grep,Glob"];
32523
+ }
32199
32524
  function buildSettingsArgs(skillOverrides) {
32200
32525
  if (skillOverrides === void 0 || Object.keys(skillOverrides).length === 0) return [];
32201
32526
  return ["--settings", JSON.stringify({ skillOverrides })];
@@ -45705,7 +46030,8 @@ function normalizeAttentionReason(value) {
45705
46030
  }
45706
46031
 
45707
46032
  // ../fleet-plugins/terminal/server/agent-api/background-report.ts
45708
- var NON_AGENT_BACKGROUND_TASK_TYPES = /* @__PURE__ */ new Set(["shell"]);
46033
+ var AGENT_BACKGROUND_TASK_TYPES = /* @__PURE__ */ new Set(["subagent", "teammate", "workflow"]);
46034
+ var NON_AGENT_BACKGROUND_TASK_TYPES = /* @__PURE__ */ new Set(["shell", "monitor"]);
45709
46035
  var NO_SETTLED_AGENT_IDS = /* @__PURE__ */ new Set();
45710
46036
  var NO_OPINION = { pending: void 0, settledAgentIds: void 0 };
45711
46037
  function readBackgroundHookReport(input, settledAgentIds = NO_SETTLED_AGENT_IDS) {
@@ -45722,29 +46048,36 @@ function readBackgroundHookReport(input, settledAgentIds = NO_SETTLED_AGENT_IDS)
45722
46048
  const selfAgentId = typeof payload.agent_id === "string" ? payload.agent_id : void 0;
45723
46049
  const listedIds = /* @__PURE__ */ new Set();
45724
46050
  let live = false;
45725
- let unreadable = false;
46051
+ let workUnknown = false;
46052
+ let listIncomplete = false;
45726
46053
  for (const task of tasks) {
45727
46054
  const entry = readTaskEntry(task);
45728
46055
  if (!entry) {
45729
- unreadable = true;
46056
+ workUnknown = true;
46057
+ listIncomplete = true;
45730
46058
  continue;
45731
46059
  }
45732
46060
  if (typeof entry.id === "string") listedIds.add(entry.id);
45733
- if (isLiveAgentWork(entry, selfAgentId, settledAgentIds)) live = true;
46061
+ else listIncomplete = true;
46062
+ const verdict = classifyBackgroundTask(entry, selfAgentId, settledAgentIds);
46063
+ if (verdict === "live") live = true;
46064
+ else if (verdict === "unreadable") workUnknown = true;
45734
46065
  }
45735
- if (!live && unreadable) return NO_OPINION;
46066
+ if (!live && workUnknown) return NO_OPINION;
45736
46067
  return {
45737
46068
  pending: live,
45738
- settledAgentIds: nextSettledAgentIds({ settledAgentIds, selfAgentId, listedIds, listComplete: !unreadable })
46069
+ settledAgentIds: nextSettledAgentIds({ settledAgentIds, selfAgentId, listedIds, listComplete: !listIncomplete })
45739
46070
  };
45740
46071
  }
45741
46072
  function readTaskEntry(task) {
45742
46073
  if (typeof task !== "object" || task === null || Array.isArray(task)) return null;
45743
46074
  return task;
45744
46075
  }
45745
- function isLiveAgentWork(entry, selfAgentId, settledAgentIds) {
45746
- if (typeof entry.id === "string" && (entry.id === selfAgentId || settledAgentIds.has(entry.id))) return false;
45747
- return !(typeof entry.type === "string" && NON_AGENT_BACKGROUND_TASK_TYPES.has(entry.type));
46076
+ function classifyBackgroundTask(entry, selfAgentId, settledAgentIds) {
46077
+ if (typeof entry.id === "string" && (entry.id === selfAgentId || settledAgentIds.has(entry.id))) return "not-agent-work";
46078
+ if (typeof entry.type !== "string") return "unreadable";
46079
+ if (AGENT_BACKGROUND_TASK_TYPES.has(entry.type)) return "live";
46080
+ return NON_AGENT_BACKGROUND_TASK_TYPES.has(entry.type) ? "not-agent-work" : "unreadable";
45748
46081
  }
45749
46082
  function nextSettledAgentIds({
45750
46083
  settledAgentIds,
@@ -47498,6 +47831,16 @@ function createConsoleObservabilityStore(deps = {}) {
47498
47831
  if (clearsAttention) delete session.attentionPending;
47499
47832
  return toTerminalSessionInfo(session);
47500
47833
  }
47834
+ function setTerminalSessionChatBackgroundPending(sessionId, pending) {
47835
+ const session = terminalSessionsById.get(sessionId);
47836
+ if (!session || session.chatActive !== true) return null;
47837
+ if (session.backgroundPendingExpiry) {
47838
+ clearTimeout(session.backgroundPendingExpiry);
47839
+ delete session.backgroundPendingExpiry;
47840
+ }
47841
+ session.backgroundPending = pending;
47842
+ return toTerminalSessionInfo(session);
47843
+ }
47501
47844
  function setTerminalSessionChatAwaiting(sessionId, awaiting) {
47502
47845
  const session = terminalSessionsById.get(sessionId);
47503
47846
  if (!session || session.chatActive !== true) return null;
@@ -47619,6 +47962,7 @@ function createConsoleObservabilityStore(deps = {}) {
47619
47962
  getTerminalSessionSettledAgentIds,
47620
47963
  setTerminalSessionModelActivity,
47621
47964
  setTerminalSessionChatActive,
47965
+ setTerminalSessionChatBackgroundPending,
47622
47966
  setTerminalSessionChatWorking,
47623
47967
  setTerminalSessionChatAwaiting,
47624
47968
  transitionTerminalSessionToDormant,
@@ -47826,7 +48170,9 @@ function eventsFromTranscriptLine(line, options) {
47826
48170
  const results = toolResultsFrom(line.message?.content, options);
47827
48171
  if (results.length > 0) return results;
47828
48172
  const text2 = readUserText(line.message?.content);
47829
- return text2 ? [{ kind: "dispatch", text: text2, ...atField }] : [];
48173
+ if (text2 === null) return [];
48174
+ if (isInjectedCarrier(line, text2)) return [{ kind: "turn-start", ...atField }];
48175
+ return [{ kind: "dispatch", text: text2, ...atField }];
47830
48176
  }
47831
48177
  if (line.type === "assistant") {
47832
48178
  return eventsFromAssistantContent(line.message?.content, options);
@@ -48142,11 +48488,29 @@ function readUserText(content) {
48142
48488
  }
48143
48489
  return normalizeDispatchText(parts.join("\n"));
48144
48490
  }
48491
+ var INJECTED_TRANSCRIPT_TAGS = /* @__PURE__ */ new Set([
48492
+ "task-notification",
48493
+ "local-command-stdout",
48494
+ "local-command-caveat",
48495
+ "command-name",
48496
+ "command-message",
48497
+ "system-reminder",
48498
+ "bash-input",
48499
+ "bash-stdout"
48500
+ ]);
48501
+ var LEADING_TAG_NAME = /^<([a-zA-Z][a-zA-Z0-9-]*)(?=[\s>])/;
48502
+ function isInjectedOrigin(origin) {
48503
+ const kind = origin?.kind;
48504
+ return typeof kind === "string" && kind.length > 0 && kind !== "human";
48505
+ }
48506
+ function isInjectedCarrier(line, text2) {
48507
+ if (isInjectedOrigin(line.origin)) return true;
48508
+ const tag = LEADING_TAG_NAME.exec(text2.trimStart())?.[1];
48509
+ return tag !== void 0 && INJECTED_TRANSCRIPT_TAGS.has(tag);
48510
+ }
48145
48511
  function normalizeDispatchText(text2) {
48146
48512
  const trimmed = text2.trim();
48147
- if (trimmed.length === 0) return null;
48148
- if (trimmed.startsWith("<system-reminder>") || trimmed.startsWith("<command-name>")) return null;
48149
- return capText(trimmed);
48513
+ return trimmed.length === 0 ? null : capText(trimmed);
48150
48514
  }
48151
48515
  function agentChatAskFromToolInput(name, id, input) {
48152
48516
  if (!input || typeof input !== "object" || Array.isArray(input)) return null;
@@ -48443,6 +48807,14 @@ var AgentChatSession = class {
48443
48807
  * 으로 남겨 두지 않고 거둬진 것으로 닫는다 — 화면이 없는 작업을 기다리게 두지 않기 위해서다.
48444
48808
  */
48445
48809
  liveJobs = /* @__PURE__ */ new Set();
48810
+ /**
48811
+ * 이미 결말을 낸 잡. 이름 붙은 에이전트는 다음 지시를 기다리며 세션에 상주하므로, 자기 결말을 낸
48812
+ * 뒤에도 살아 있는 작업 목록에 계속 실려 온다(PTY 쪽 hook payload에서 실측된 것과 같은 registry다).
48813
+ * 그 항목을 목록에 있다는 이유로 다시 살아 있는 작업으로 읽으면, 이 축에는 시한이 없으므로 세션이
48814
+ * 끝날 때까지 유휴·입력 대기 전이가 통째로 막힌다. 기억은 목록이 지워 준다 — 더 이상 실리지 않는
48815
+ * id는 registry에서 사라진 것이므로 버린다.
48816
+ */
48817
+ settledJobs = /* @__PURE__ */ new Set();
48446
48818
  /**
48447
48819
  * 중지 세대. 중지는 도는 턴 하나가 아니라 **그 시점의 큐 전체**를 끊어야 한다 — 큐에 밀려
48448
48820
  * 있던 턴이 중지 직후 태연히 시작하면, 사용자가 멈춘 것은 화면에서 멈추지 않는다. 각 턴은
@@ -48476,6 +48848,18 @@ var AgentChatSession = class {
48476
48848
  * 답은 턴이 끝난 뒤에 오므로(실측), 그 사이 새 턴이 시작했다면 그 값은 남의 턴 자리에 앉는다.
48477
48849
  */
48478
48850
  contextGeneration = 0;
48851
+ /**
48852
+ * control 요청이 하나 떠 있는가.
48853
+ *
48854
+ * 겹쳐 던지면 자식이 직렬로 처리한다 — 실측하면 요청 하나에 약 30초가 들고, 셋을 던진 마지막
48855
+ * 답은 100초 뒤에 왔다. 그래서 앞선 요청이 살아 있는 동안에는 새로 던지지 않는다. 놓친 창은
48856
+ * 다음 경계(턴 시작·턴 종료)가 다시 얻는다.
48857
+ */
48858
+ contextInFlight = false;
48859
+ /** 턴이 도는 동안 흘러간 라이브 총량. 턴 경계에서 비운다. */
48860
+ liveContextTotal = null;
48861
+ /** 자식이 스냅숏에서 직접 말한 좌표. 도착하기 전에는 모델 id에서 유도한다. */
48862
+ observedClaudeCoordinate = null;
48479
48863
  constructor(operationId, seed, createSdk) {
48480
48864
  this.operationId = operationId;
48481
48865
  this.seed = seed;
@@ -48499,21 +48883,38 @@ var AgentChatSession = class {
48499
48883
  turnAt = null;
48500
48884
  lastAt = null;
48501
48885
  };
48886
+ let pendingOpenAt;
48887
+ const openPendingTurn = () => {
48888
+ if (pendingOpenAt === void 0) return;
48889
+ const at = pendingOpenAt;
48890
+ pendingOpenAt = void 0;
48891
+ closeReplayedTurn();
48892
+ turns += 1;
48893
+ turnAt = at;
48894
+ this.push(at === null ? { kind: "turn-start" } : { kind: "turn-start", at });
48895
+ };
48502
48896
  try {
48503
48897
  const raw = await promises.readFile(transcriptPath, "utf8");
48504
48898
  for (const line of raw.split("\n")) {
48505
48899
  if (line.trim().length === 0) continue;
48506
48900
  const mapped = chatReplayFromTranscriptLine(line, { cwd: this.seed.cwd, toolNames: this.toolNames });
48507
48901
  for (const event of mapped.events) {
48902
+ if (event.kind === "turn-start") {
48903
+ if (pendingOpenAt === void 0) pendingOpenAt = mapped.at ?? null;
48904
+ continue;
48905
+ }
48508
48906
  if (event.kind === "dispatch") {
48907
+ pendingOpenAt = void 0;
48509
48908
  closeReplayedTurn();
48510
48909
  turns += 1;
48511
48910
  turnAt = mapped.at ?? null;
48911
+ } else {
48912
+ openPendingTurn();
48512
48913
  }
48513
48914
  this.rememberTool(event);
48514
48915
  this.push(event);
48515
48916
  }
48516
- if (mapped.events.length > 0 && mapped.at !== void 0) lastAt = mapped.at;
48917
+ if (mapped.events.length > 0 && mapped.at !== void 0 && pendingOpenAt === void 0) lastAt = mapped.at;
48517
48918
  }
48518
48919
  closeReplayedTurn();
48519
48920
  } catch {
@@ -48682,11 +49083,7 @@ var AgentChatSession = class {
48682
49083
  /** 도구 이벤트가 지나갈 때 id→이름을 적어 둔다. 결과 요약 정책이 이 축 위에서 결정된다. */
48683
49084
  rememberTool(event) {
48684
49085
  if (event.kind === "job") {
48685
- this.jobKinds.set(event.id, event.jobKind);
48686
- if (this.jobKinds.size > JOB_KIND_CAP) {
48687
- const oldest = this.jobKinds.keys().next();
48688
- if (!oldest.done) this.jobKinds.delete(oldest.value);
48689
- }
49086
+ this.rememberJobKind(event.id, event.jobKind);
48690
49087
  return;
48691
49088
  }
48692
49089
  if (event.kind !== "tool" && event.kind !== "tool-start") return;
@@ -48697,6 +49094,46 @@ var AgentChatSession = class {
48697
49094
  if (!oldest.done) this.toolNames.delete(oldest.value);
48698
49095
  }
48699
49096
  }
49097
+ /**
49098
+ * 잡 하나의 종류를 적는다. 긴 세션에서 무한히 자라지 않게 상한을 두고 오래된 것부터 버리되, 아직
49099
+ * 살아 있는 잡은 건너뛴다 — 그 종류는 활동축이 지금 읽고 있고, 잃으면 축이 무의견으로 굳는다.
49100
+ */
49101
+ rememberJobKind(jobId, kind, incoming) {
49102
+ this.jobKinds.set(jobId, kind);
49103
+ if (this.jobKinds.size <= JOB_KIND_CAP) return;
49104
+ for (const oldest of this.jobKinds.keys()) {
49105
+ if (this.liveJobs.has(oldest) || incoming?.has(oldest) === true) continue;
49106
+ this.jobKinds.delete(oldest);
49107
+ return;
49108
+ }
49109
+ }
49110
+ /**
49111
+ * 살아 있는 작업 목록이 알려 준 종류를 적어 둔다.
49112
+ *
49113
+ * `background_tasks_changed`는 `task_type`을 싣고 오지만 스트림 이벤트(`jobs`)는 id만 나른다 —
49114
+ * 화면은 종류를 이미 첫 등장(`job`)에서 받았기 때문이다. 활동축은 사정이 다르다: 셸을 세지
49115
+ * 않으려면 이 목록에서 처음 본 잡의 종류도 알아야 하고, 그 값은 원본 메시지에만 있다.
49116
+ */
49117
+ rememberJobKinds(message) {
49118
+ if (message.type !== "system" || message.subtype !== "background_tasks_changed") return;
49119
+ const tasks = message.tasks;
49120
+ if (!Array.isArray(tasks)) return;
49121
+ const incoming = /* @__PURE__ */ new Set();
49122
+ for (const task of tasks) {
49123
+ if (!task || typeof task !== "object") continue;
49124
+ const id = task.task_id;
49125
+ if (typeof id !== "string" || id.length === 0) continue;
49126
+ incoming.add(id);
49127
+ }
49128
+ for (const task of tasks) {
49129
+ if (!task || typeof task !== "object") continue;
49130
+ const id = task.task_id;
49131
+ if (typeof id !== "string" || id.length === 0) continue;
49132
+ const rawKind = task.task_type;
49133
+ if (typeof rawKind !== "string") continue;
49134
+ this.rememberJobKind(id, readJobKind(rawKind), incoming);
49135
+ }
49136
+ }
48700
49137
  /**
48701
49138
  * 결말 알림이 알려 준 출력 파일 경로를 적어 둔다.
48702
49139
  *
@@ -48812,25 +49249,33 @@ var AgentChatSession = class {
48812
49249
  });
48813
49250
  }
48814
49251
  /**
48815
- * 턴이 시작하는 순간 자식에게 문맥 내역을 묻고, 답이 오면 그대로 저널에 싣는다.
49252
+ * 자식에게 문맥 **내역**을 묻고, 답이 오면 저널에 싣는다. 두 경계에서 불린다 — 턴을 보내기
49253
+ * 직전(`"start"`)과 턴이 닫힌 직후(`"end"`).
48816
49254
  *
48817
- * **이 번이 유일한 기회다**(실측). 자식은 턴이 시작되면 control 채널을 닫아, +0ms의 요청만
48818
- * 답을 받고 뒤의 요청은 턴이 10초를 돌든 전부 "Query closed before response received"로
48819
- * 끝난다. 주기적으로 물어 최신값을 좇는 설계는 이 자식에서 성립하지 않는다.
49255
+ * 왕복이 느리다는 것이 배선의 전제다(실측 21~88초, 요청 하나에 30초, 직렬 큐). 그래서
49256
+ * 총량은 채널에 맡기지 않고 `message_delta` usage가 진다. 여기서 얻는 것은 카테고리 분해
49257
+ * 그것만은 control 채널이 유일한 출처다.
48820
49258
  *
48821
- * 그래서 값은 **이 턴이 시작될 때까지의** 문맥이다 방금 끝난 턴이 더한 몫은 다음 턴이
48822
- * 시작될 비로소 드러난다. 화면은 지연을 감추지 않는다.
49259
+ * 중에는 아예 묻지 않는다. 자식이 턴이 시작되면 control을 닫아 +0ms의 요청만 답을 받고
49260
+ * 뒤는 전부 "Query closed before response received"로 끝난다(실측). 유휴 폴링도 하지 않는다 —
49261
+ * 답은 오지만 요청마다 30초를 태우고 큐만 밀린다.
48823
49262
  *
48824
49263
  * 응답을 기다리지 않는 이유는 스트림 때문이다: 여기서 await하면 첫 메시지 소비가 그만큼 늦다.
48825
- * 늦게 도착한 답도 같은 턴의 시작 시점 값이므로, 그때 실어도 뜻이 달라지지 않는다.
48826
49264
  */
48827
- snapshotContextAtTurnStart(session) {
49265
+ requestContextSnapshot(session, asOf) {
49266
+ if (this.contextInFlight) return;
48828
49267
  const generation = ++this.contextGeneration;
49268
+ this.contextInFlight = true;
49269
+ const settle = (usage4) => {
49270
+ this.contextInFlight = false;
49271
+ if (usage4 && generation === this.contextGeneration) this.pushContext(usage4, asOf);
49272
+ };
48829
49273
  try {
48830
- void session.getContextUsage().then((usage4) => {
48831
- if (usage4 && generation === this.contextGeneration) this.pushContext(usage4);
48832
- }, () => void 0);
49274
+ void session.getContextUsage().then(settle, () => {
49275
+ this.contextInFlight = false;
49276
+ });
48833
49277
  } catch {
49278
+ this.contextInFlight = false;
48834
49279
  }
48835
49280
  }
48836
49281
  /**
@@ -48844,25 +49289,100 @@ var AgentChatSession = class {
48844
49289
  * 예약분(자동 압축 여유)과 남은 자리는 **쓴 것이 아니다**. 같은 목록에 두면 합이 언제나 창
48845
49290
  * 전체가 되어 미터가 항상 100%를 가리킨다.
48846
49291
  */
48847
- pushContext(usage4) {
49292
+ pushContext(usage4, asOf) {
49293
+ this.observedClaudeCoordinate = usage4.max;
48848
49294
  const spent = usage4.categories.filter((category) => !category.deferred && category.tokens > 0 && !CONTEXT_UNSPENT.has(category.name));
48849
49295
  const reserved = usage4.categories.find((category) => category.name === CONTEXT_RESERVED_NAME);
49296
+ const window = this.realWindow(usage4.max);
49297
+ const slices = spent.map((category) => ({
49298
+ name: category.name,
49299
+ tokens: window ? window.unproject(category.tokens) : category.tokens
49300
+ }));
48850
49301
  this.push({
48851
49302
  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 })) } : {},
49303
+ asOf,
49304
+ total: slices.reduce((sum, slice) => sum + slice.tokens, 0),
49305
+ max: window ? window.max : usage4.max,
49306
+ // 예약분은 되돌리지 않고 **갈아 끼운다**. 자식의 값은 자기 좌표의 여유(max − compactAt)이고
49307
+ // 실제 여유는 정책이 정하는 다른 수다 같은 비율로 늘리면 있지도 않은 자리를 예약해 둔다.
49308
+ ...window ? window.max > window.compactAt ? { reserved: window.max - window.compactAt } : {} : reserved && reserved.tokens > 0 ? { reserved: reserved.tokens } : {},
49309
+ ...window ? usage4.compactAt === null ? {} : { compactAt: window.compactAt } : usage4.compactAt === null ? {} : { compactAt: usage4.compactAt },
49310
+ slices,
49311
+ ...usage4.memoryFiles.length > 0 ? {
49312
+ memoryFiles: usage4.memoryFiles.map((file2) => ({
49313
+ name: file2.path,
49314
+ tokens: window ? window.unproject(file2.tokens) : file2.tokens
49315
+ }))
49316
+ } : {},
48858
49317
  ...usage4.mcpTools.length > 0 ? {
48859
49318
  mcpTools: usage4.mcpTools.map((tool) => ({
48860
49319
  name: tool.server ? `${tool.server} \xB7 ${tool.name}` : tool.name,
48861
- tokens: tool.tokens
49320
+ tokens: window ? window.unproject(tool.tokens) : tool.tokens
48862
49321
  }))
48863
49322
  } : {}
48864
49323
  });
48865
49324
  }
49325
+ /**
49326
+ * 턴이 도는 동안 총량을 흘린다. 미터가 이 턴을 따라 움직이는 유일한 경로다.
49327
+ *
49328
+ * 읽는 것은 `message_delta`의 usage **하나뿐**이며, 그것은 소거로 남은 결과다(실측):
49329
+ * `message_start`와 완성 `assistant`의 usage는 언제나 0으로 오고, `result.usage`는 그 턴의 모든
49330
+ * 모델 호출을 **합산한** 값이어서 창 점유가 아니다 — 도구를 다섯 번 돈 턴에서 61,630이었고 같은
49331
+ * 순간의 실제 점유는 15,785였다. 그것을 총량으로 쓰면 미터가 네 배로 뛴다.
49332
+ *
49333
+ * 서브에이전트의 usage도 제외한다. 그 창은 부모의 창이 아니다.
49334
+ */
49335
+ trackLiveContext(message) {
49336
+ if (message.type !== "stream_event") return;
49337
+ if (typeof message.parent_tool_use_id === "string" && message.parent_tool_use_id.length > 0) return;
49338
+ const event = message.event;
49339
+ if (!isRecord14(event) || event.type !== "message_delta") return;
49340
+ const total = readClaudeOccupiedInputTokens(event.usage);
49341
+ if (total === null) return;
49342
+ const claudeCoordinate = this.claudeCoordinate();
49343
+ const window = this.realWindow(claudeCoordinate);
49344
+ const live = window ? window.unproject(total) : total;
49345
+ if (this.liveContextTotal === live) return;
49346
+ this.liveContextTotal = live;
49347
+ this.pushEphemeral({
49348
+ kind: "context-live",
49349
+ total: live,
49350
+ max: window ? window.max : claudeCoordinate
49351
+ });
49352
+ }
49353
+ /**
49354
+ * 자식이 이 세션을 재는 좌표.
49355
+ *
49356
+ * 스냅숏이 한 번이라도 왔으면 자식이 직접 말한 값이 권위다. 그 전에는 유도한다 — 좌표를 정하는
49357
+ * 것은 모델 id의 `[1m]` 마커이고, 그 마커를 붙이는 규칙이 곧 카탈로그 창이 1M에 닿는지 여부다.
49358
+ * 유도가 필요한 이유는 라이브 총량이 첫 스냅숏(왕복 30초)을 기다릴 수 없기 때문이다.
49359
+ */
49360
+ claudeCoordinate() {
49361
+ if (this.observedClaudeCoordinate !== null) return this.observedClaudeCoordinate;
49362
+ const window = this.seed.contextWindow;
49363
+ const oneMillion = typeof window === "number" && Number.isFinite(window) && window > 0 ? window >= CLAUDE_COMPAT_CONTEXT_WINDOW : hasClaudeOneMillionMarker(this.seed.model);
49364
+ return oneMillion ? CLAUDE_COMPAT_CONTEXT_WINDOW : CLAUDE_DEFAULT_CONTEXT_WINDOW;
49365
+ }
49366
+ /**
49367
+ * 자식이 보고한 좌표를 이 모델의 실제 창으로 되돌리는 자.
49368
+ *
49369
+ * `null`은 되돌릴 것이 없다는 뜻이며 세 경우가 여기로 온다: 시드에 카탈로그 창이 없는 세션
49370
+ * (네이티브 Claude 모델·카탈로그 밖 id), 그리고 자식이 자기 두 좌표가 아닌 값을 말한 세션 —
49371
+ * 후자는 역함수가 스스로 항등으로 접는다. 어느 경우든 오늘의 숫자가 그대로 나간다.
49372
+ */
49373
+ realWindow(claudeCoordinate) {
49374
+ const max = this.seed.contextWindow;
49375
+ if (typeof max !== "number" || !Number.isFinite(max) || max <= 0) return null;
49376
+ if (claudeCoordinate !== CLAUDE_DEFAULT_CONTEXT_WINDOW && claudeCoordinate !== CLAUDE_COMPAT_CONTEXT_WINDOW) {
49377
+ return null;
49378
+ }
49379
+ const ceiling = this.seed.compactCeiling ?? null;
49380
+ return {
49381
+ max,
49382
+ compactAt: compactThresholdTokens(max, ceiling),
49383
+ unproject: (tokens) => unprojectClaudeContextInputTokens(tokens, max, claudeCoordinate, ceiling)
49384
+ };
49385
+ }
48866
49386
  push(event) {
48867
49387
  const entry = { seq: ++this.seq, event };
48868
49388
  if (event.kind === "job-progress") {
@@ -48996,6 +49516,8 @@ var AgentChatSession = class {
48996
49516
  if (this.latestSessionId !== this.reportedSessionId) this.syncProviderSessionOnce();
48997
49517
  }
48998
49518
  this.rememberJobOutput(message);
49519
+ this.rememberJobKinds(message);
49520
+ this.trackLiveContext(message);
48999
49521
  for (const event of chatEventsFromSdkMessage(message, { cwd: this.seed.cwd, toolNames: this.toolNames })) {
49000
49522
  this.ingest(event);
49001
49523
  }
@@ -49033,12 +49555,50 @@ var AgentChatSession = class {
49033
49555
  }
49034
49556
  /** 살아 있는 잡의 집합을 이벤트대로 따라간다. `jobs`는 세는 것이 아니라 통째로 갈아 끼운다. */
49035
49557
  trackJob(event) {
49036
- if (event.kind === "job") this.liveJobs.add(event.id);
49037
- else if (event.kind === "job-end") this.liveJobs.delete(event.id);
49038
- else if (event.kind === "jobs") {
49558
+ if (event.kind === "job") {
49559
+ this.liveJobs.add(event.id);
49560
+ this.settledJobs.delete(event.id);
49561
+ } else if (event.kind === "job-end") {
49562
+ this.liveJobs.delete(event.id);
49563
+ this.settledJobs.add(event.id);
49564
+ } else if (event.kind === "jobs") {
49039
49565
  this.liveJobs.clear();
49040
- for (const id of event.ids) this.liveJobs.add(id);
49566
+ for (const id of event.ids) {
49567
+ if (!this.settledJobs.has(id)) this.liveJobs.add(id);
49568
+ }
49569
+ const listed = new Set(event.ids);
49570
+ for (const id of [...this.settledJobs]) {
49571
+ if (!listed.has(id)) this.settledJobs.delete(id);
49572
+ }
49573
+ } else return;
49574
+ this.syncBackgroundPending();
49575
+ }
49576
+ /**
49577
+ * 살아 있는 잡 목록을 활동축의 백그라운드 대기값으로 접는다.
49578
+ *
49579
+ * 세는 것은 에이전트 작업뿐이다 — 서브에이전트(이름 붙은 팀메이트 포함)와 워크플로우. PTY 어댑터가
49580
+ * hook 목록에서 셸과 MCP 감시 작업을 빼는 것과 같은 계약이며, 이유도 같다: 사람이 기다리는 것은
49581
+ * 자기 일을 대신 하는 에이전트이지 그 에이전트가 남긴 명령이 아니고, 그것까지 세면 긴 백그라운드
49582
+ * 명령 하나가 세션을 유휴 밖에 세워 둔 채 유휴 휴면까지 막는다.
49583
+ *
49584
+ * 처음 보는 종류는 세지 않는다. PTY 쪽은 같은 자리에서 무의견으로 물러나지만 그 축에는 시한이
49585
+ * 있어 무의견이 영원하지 않다 — 이 축에는 시한이 없으므로, 여기서 물러나면 상주하는 미지 잡 하나가
49586
+ * 에이전트 작업이 다 끝난 뒤에도 배지를 영영 켜 둔다. 새 종류를 놓치는 쪽은 이 기능이 없던 상태로
49587
+ * 돌아갈 뿐이고, 그 종류는 화면의 잡 목록에 `other`로 서서 눈에 띈다.
49588
+ *
49589
+ * 턴 경계는 여기서 보지 않는다. 턴이 도는 동안에도 값은 서지만, 읽는 쪽이 작업 중을 앞세우므로
49590
+ * 화면에 나타나는 것은 턴이 닫힌 뒤부터다 — 그 순간이 곧 "남은 것은 백그라운드뿐"인 순간이다.
49591
+ */
49592
+ syncBackgroundPending() {
49593
+ let pending = false;
49594
+ for (const id of this.liveJobs) {
49595
+ const kind = this.jobKinds.get(id);
49596
+ if (kind === "agent" || kind === "workflow") {
49597
+ pending = true;
49598
+ break;
49599
+ }
49041
49600
  }
49601
+ this.seed.reportBackgroundPending(pending);
49042
49602
  }
49043
49603
  /**
49044
49604
  * 턴 하나를 연다. 화면의 스피너와 활동축이 같은 순간에 켜지는 자리다.
@@ -49072,11 +49632,15 @@ var AgentChatSession = class {
49072
49632
  ...end.answer === void 0 ? {} : { answer: end.answer }
49073
49633
  });
49074
49634
  this.abandonAsks("The turn ended before the question was answered.");
49635
+ this.syncBackgroundPending();
49075
49636
  this.seed.reportActivity(false);
49637
+ this.liveContextTotal = null;
49076
49638
  const awaiting = this.awaitingTurn;
49077
49639
  this.awaitingTurn = null;
49078
49640
  awaiting?.();
49079
49641
  this.releaseTurnCloseWaiters();
49642
+ const session = this.session;
49643
+ if (session) this.requestContextSnapshot(session, "end");
49080
49644
  }
49081
49645
  /** 자리가 비었음을 줄 서 있던 디스패치들에게 알린다. 결말 하나가 전부를 깨운다. */
49082
49646
  releaseTurnCloseWaiters() {
@@ -49114,6 +49678,8 @@ var AgentChatSession = class {
49114
49678
  }
49115
49679
  for (const id of [...this.liveJobs]) this.push({ kind: "job-end", id, status: "stopped" });
49116
49680
  this.liveJobs.clear();
49681
+ this.settledJobs.clear();
49682
+ this.syncBackgroundPending();
49117
49683
  this.settlingStoppedTurn = false;
49118
49684
  this.closeTurn({ ok: false });
49119
49685
  this.releaseTurnCloseWaiters();
@@ -49150,7 +49716,7 @@ var AgentChatSession = class {
49150
49716
  const settled = new Promise((resolve3) => {
49151
49717
  this.awaitingTurn = resolve3;
49152
49718
  });
49153
- this.snapshotContextAtTurnStart(session);
49719
+ this.requestContextSnapshot(session, "start");
49154
49720
  session.send(text2);
49155
49721
  this.turnReachedChild = true;
49156
49722
  await settled;
@@ -49289,6 +49855,21 @@ var AgentChatRegistry = class {
49289
49855
  await Promise.all([...operationIds].map((operationId) => this.dispose(operationId)));
49290
49856
  }
49291
49857
  };
49858
+ function isRecord14(value) {
49859
+ return typeof value === "object" && value !== null && !Array.isArray(value);
49860
+ }
49861
+ function readClaudeOccupiedInputTokens(usage4) {
49862
+ if (!isRecord14(usage4)) return null;
49863
+ let total = 0;
49864
+ let found = false;
49865
+ for (const field of ["input_tokens", "cache_read_input_tokens", "cache_creation_input_tokens"]) {
49866
+ const tokens = usage4[field];
49867
+ if (typeof tokens !== "number" || !Number.isFinite(tokens) || tokens < 0) continue;
49868
+ total += tokens;
49869
+ found = true;
49870
+ }
49871
+ return found ? total : null;
49872
+ }
49292
49873
 
49293
49874
  // ../fleet-plugins/terminal/server/agent-api/chat-ws.ts
49294
49875
  var MAX_CHAT_ANSWER_MESSAGE_CHARS = 2e3;
@@ -49401,6 +49982,15 @@ var ANALYSIS_ERROR_CODES = {
49401
49982
  var ANALYST_GATEWAY_CLI_ID = "claude-gateway";
49402
49983
  var ANALYST_DEFAULT_MODEL = "sonnet";
49403
49984
  var ANALYST_DEFAULT_EFFORT = "low";
49985
+ var ANALYST_EFFORT_LEVELS = ["low", "medium", "high"];
49986
+ function clampAnalystEffortLevels(levels) {
49987
+ return ANALYST_EFFORT_LEVELS.filter((level) => levels.includes(level));
49988
+ }
49989
+ function clampAnalystDefaultEffort(levels, fallback) {
49990
+ if (fallback && levels.includes(fallback)) return fallback;
49991
+ if (levels.includes(ANALYST_DEFAULT_EFFORT)) return ANALYST_DEFAULT_EFFORT;
49992
+ return levels[0];
49993
+ }
49404
49994
  var NATIVE_CLAUDE_LABELS = {
49405
49995
  "fable[1m]": "Claude Fable",
49406
49996
  sonnet: "Claude Sonnet",
@@ -49410,26 +50000,33 @@ function nativeClaudeAnalystModels() {
49410
50000
  return NATIVE_CLAUDE_MODEL_ALIASES.map((modelId) => ({
49411
50001
  modelId,
49412
50002
  name: NATIVE_CLAUDE_LABELS[modelId] ?? modelId,
49413
- effort: { supported: true, levels: [...NATIVE_CLAUDE_EFFORTS] }
50003
+ effort: { supported: true, levels: [...ANALYST_EFFORT_LEVELS] }
49414
50004
  }));
49415
50005
  }
49416
50006
  function resolveAnalysisGatewayBaseUrl(origin) {
49417
50007
  return `${origin.replace(/\/+$/u, "")}/plugins/terminal/${AI_GATEWAY_ROUTE_SEGMENT}`;
49418
50008
  }
49419
50009
  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
- }));
50010
+ const native = nativeModels.map((model) => {
50011
+ const effortLevels = model.effort.supported ? clampAnalystEffortLevels(model.effort.levels ?? []) : [];
50012
+ const defaultEffort = model.modelId === ANALYST_DEFAULT_MODEL ? clampAnalystDefaultEffort(effortLevels, ANALYST_DEFAULT_EFFORT) : model.effort.supported ? clampAnalystDefaultEffort(effortLevels, model.effort.default) : void 0;
50013
+ return {
50014
+ id: model.modelId,
50015
+ label: model.name,
50016
+ effortLevels,
50017
+ ...defaultEffort ? { defaultEffort } : {}
50018
+ };
50019
+ });
50020
+ const gateway = gatewayModels.map((model) => {
50021
+ const effortLevels = model.effort.supported ? clampAnalystEffortLevels(model.effort.levels) : [];
50022
+ return {
50023
+ id: toClaudeGatewayModelId(model),
50024
+ label: model.displayName,
50025
+ // 게이트웨이 모델 스키마에는 기본 강도가 없다. 없는 값을 지어내면 사용자가 고르지 않은
50026
+ // 강도로 돈다. 클램프 후 단이 비면 강도 없는 모델과 같다.
50027
+ effortLevels
50028
+ };
50029
+ });
49433
50030
  const models = [...native, ...gateway];
49434
50031
  return {
49435
50032
  clis: [{
@@ -49446,7 +50043,7 @@ function analysisError(code, message) {
49446
50043
  return { error: { code, message } };
49447
50044
  }
49448
50045
  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;
50046
+ 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
50047
  const cli = catalog.clis.find((candidate) => candidate.cliId === value.cliId);
49451
50048
  if (!cli?.available) return false;
49452
50049
  const model = cli.models.find((candidate) => candidate.id === value.model);
@@ -49455,9 +50052,9 @@ function isAnalysisSelection(catalog, value) {
49455
50052
  return typeof value.effort === "string" && value.effort.length > 0 && model.effortLevels.includes(value.effort);
49456
50053
  }
49457
50054
  function isMessageBody(value) {
49458
- return isRecord14(value) && hasExactKeys(value, ["text"]) && typeof value.text === "string" && value.text.trim().length > 0;
50055
+ return isRecord15(value) && hasExactKeys(value, ["text"]) && typeof value.text === "string" && value.text.trim().length > 0;
49459
50056
  }
49460
- function isRecord14(value) {
50057
+ function isRecord15(value) {
49461
50058
  return typeof value === "object" && value !== null && !Array.isArray(value);
49462
50059
  }
49463
50060
  function hasExactKeys(value, keys) {
@@ -50024,6 +50621,8 @@ async function createAgentApi(ctx, terminalRuntime, deps) {
50024
50621
  }
50025
50622
  const launchOptions = readLaunchOptions(body, cliId, res);
50026
50623
  if (launchOptions === false) return true;
50624
+ const geometry = readOptionalGeometry(body?.geometry, res);
50625
+ if (geometry === false) return true;
50027
50626
  if (body?.prompt !== void 0 && typeof body.prompt !== "string") {
50028
50627
  ctx.host.http.writeJson(res, 400, { error: "invalid_prompt" });
50029
50628
  return true;
@@ -50064,7 +50663,8 @@ async function createAgentApi(ctx, terminalRuntime, deps) {
50064
50663
  ...launchOptions,
50065
50664
  ...composedPrompt ? { prompt: composedPrompt } : {},
50066
50665
  ...attachmentIds.length > 0 ? { attachmentIds } : {},
50067
- ...chatBorn ? { chatBorn: true } : {}
50666
+ ...chatBorn ? { chatBorn: true } : {},
50667
+ ...geometry ? { geometry } : {}
50068
50668
  });
50069
50669
  } catch (error51) {
50070
50670
  if (attachmentIds.length > 0) launchAttachments.unreserve(attachmentIds);
@@ -50080,6 +50680,24 @@ async function createAgentApi(ctx, terminalRuntime, deps) {
50080
50680
  }
50081
50681
  return value;
50082
50682
  }
50683
+ function readOptionalGeometry(value, res) {
50684
+ if (value === void 0) return void 0;
50685
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
50686
+ ctx.host.http.writeJson(res, 400, { error: "invalid_geometry" });
50687
+ return false;
50688
+ }
50689
+ const candidate = value;
50690
+ const x = candidate.x;
50691
+ const y = candidate.y;
50692
+ const width = candidate.width;
50693
+ const height = candidate.height;
50694
+ const zIndex = candidate.zIndex;
50695
+ 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)) {
50696
+ ctx.host.http.writeJson(res, 400, { error: "invalid_geometry" });
50697
+ return false;
50698
+ }
50699
+ return { x, y, width, height, zIndex };
50700
+ }
50083
50701
  function readLaunchOptions(body, cliId, res) {
50084
50702
  if (body?.model !== void 0 && typeof body.model !== "string" || body?.effort !== void 0 && typeof body.effort !== "string") {
50085
50703
  ctx.host.http.writeJson(res, 400, { error: "invalid_launch_option" });
@@ -50204,6 +50822,7 @@ async function createAgentApi(ctx, terminalRuntime, deps) {
50204
50822
  // "transcript 부재는 상실이 아니라 아직 첫 턴 전"이라는 뜻을 durable하게 남긴다.
50205
50823
  ...launchOptions.chatBorn ? { [CHAT_MODE_PAYLOAD_KEY]: true, [CHAT_BORN_PAYLOAD_KEY]: true } : {}
50206
50824
  },
50825
+ ...launchOptions.geometry ? { geometry: launchOptions.geometry } : {},
50207
50826
  createdAt: session.createdAt
50208
50827
  });
50209
50828
  if (launchOptions.chatBorn) {
@@ -50684,6 +51303,9 @@ async function createAgentApi(ctx, terminalRuntime, deps) {
50684
51303
  });
50685
51304
  } catch {
50686
51305
  }
51306
+ const gatewayModel = findGatewayModel(model);
51307
+ const gatewayContextWindow = typeof gatewayModel?.contextWindow === "number" && Number.isFinite(gatewayModel.contextWindow) && gatewayModel.contextWindow > 0 ? gatewayModel.contextWindow : void 0;
51308
+ const gatewayCompactCeiling = gatewayContextWindow === void 0 ? void 0 : deps.readAiGatewaySettings?.().compactCeiling ?? null;
50687
51309
  const chatSkillOverrides = buildDisabledSkillOverrides(GATEWAY_DISABLED_CLAUDE_SKILLS);
50688
51310
  const systemPromptMode = deps.globalOptionsService.load().claudeGatewaySystemPromptMode ?? "append";
50689
51311
  const systemPrompt = systemPromptMode === "off" ? void 0 : { mode: systemPromptMode, text: createSystemPromptBuilder().build() };
@@ -50693,6 +51315,11 @@ async function createAgentApi(ctx, terminalRuntime, deps) {
50693
51315
  seed: {
50694
51316
  baseUrl: gatewayBaseUrl,
50695
51317
  model,
51318
+ // 자식은 창이 500k인 모델도 자기 200k 좌표로 재고, 그 좌표를 물어보는 모든 표면에 그대로
51319
+ // 말한다. 실제 창과 투영에 쓰인 압축 정책을 함께 실어야 세션이 그것을 되돌릴 수 있다.
51320
+ // 네이티브 Claude 모델은 카탈로그에 없으므로 두 값 없이 지나가고 오늘의 숫자를 유지한다.
51321
+ ...gatewayContextWindow === void 0 ? {} : { contextWindow: gatewayContextWindow },
51322
+ ...gatewayCompactCeiling === void 0 ? {} : { compactCeiling: gatewayCompactCeiling },
50696
51323
  ...launchEffort ? { effort: launchEffort.effort } : {},
50697
51324
  cwd,
50698
51325
  claudeConfigDir,
@@ -50742,6 +51369,11 @@ async function createAgentApi(ctx, terminalRuntime, deps) {
50742
51369
  reportAwaiting: (awaiting) => {
50743
51370
  const updated = observability.setTerminalSessionChatAwaiting(node.id, awaiting);
50744
51371
  if (updated) observability.notifySessionUpdated(updated);
51372
+ },
51373
+ reportBackgroundPending: (pending) => {
51374
+ const before = observability.getTerminalSessionInfo(node.id)?.backgroundPending === true;
51375
+ const updated = observability.setTerminalSessionChatBackgroundPending(node.id, pending);
51376
+ if (updated && before !== pending) observability.notifySessionUpdated(updated);
50745
51377
  }
50746
51378
  }
50747
51379
  };