@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.
- package/dist/cli.mjs +137 -4
- package/dist/client/assets/{_baseUniq-DyzqDsbR.js → _baseUniq-DG32CEpb.js} +1 -1
- package/dist/client/assets/{arc-D9PB38OE.js → arc-CoHkSCMF.js} +1 -1
- package/dist/client/assets/{architectureDiagram-Q4EWVU46-0L3Tlker.js → architectureDiagram-Q4EWVU46-f0RKHqJg.js} +1 -1
- package/dist/client/assets/{blockDiagram-DXYQGD6D-BCAdqHmo.js → blockDiagram-DXYQGD6D-Dxb3hLAV.js} +1 -1
- package/dist/client/assets/{c4Diagram-AHTNJAMY-oK3f-N6i.js → c4Diagram-AHTNJAMY-C5q_ahSd.js} +1 -1
- package/dist/client/assets/channel-C-Qx-Oi-.js +1 -0
- package/dist/client/assets/{chunk-4BX2VUAB-CIYxtKO7.js → chunk-4BX2VUAB-BS0W4Fpt.js} +1 -1
- package/dist/client/assets/{chunk-4TB4RGXK-CKA_-tPR.js → chunk-4TB4RGXK-p03eWKTO.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-ArIbE5m1.js → chunk-55IACEB6-BB1ALVX2.js} +1 -1
- package/dist/client/assets/{chunk-EDXVE4YY-bGzMBuDL.js → chunk-EDXVE4YY-DV4N-Npm.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-CZUkX3ry.js → chunk-FMBD7UC4-Da8VspnD.js} +1 -1
- package/dist/client/assets/{chunk-OYMX7WX6-CNXmIVXX.js → chunk-OYMX7WX6-CWTmAQhy.js} +1 -1
- package/dist/client/assets/{chunk-QZHKN3VN-B3t4h1MD.js → chunk-QZHKN3VN-DE9xUsy3.js} +1 -1
- package/dist/client/assets/{chunk-YZCP3GAM-CdGF0f8H.js → chunk-YZCP3GAM-BxriNNdB.js} +1 -1
- package/dist/client/assets/classDiagram-6PBFFD2Q-DA7e8Mft.js +1 -0
- package/dist/client/assets/classDiagram-v2-HSJHXN6E-DA7e8Mft.js +1 -0
- package/dist/client/assets/clone-Rpb38n4i.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-BESjfcni.js → cose-bilkent-S5V4N54A-B4sjskIn.js} +1 -1
- package/dist/client/assets/{dagre-KV5264BT-WpQhPw2a.js → dagre-KV5264BT-oJms5aS3.js} +1 -1
- package/dist/client/assets/{diagram-5BDNPKRD-BfAkutIX.js → diagram-5BDNPKRD-BAbLGddM.js} +1 -1
- package/dist/client/assets/{diagram-G4DWMVQ6-C9I16SLL.js → diagram-G4DWMVQ6-BrhRQwe-.js} +1 -1
- package/dist/client/assets/{diagram-MMDJMWI5-CVJMM2zJ.js → diagram-MMDJMWI5-Buxu7Ng3.js} +1 -1
- package/dist/client/assets/{diagram-TYMM5635-DTqr7jJ3.js → diagram-TYMM5635-Cnp__7LJ.js} +1 -1
- package/dist/client/assets/{erDiagram-SMLLAGMA-B09ElCdB.js → erDiagram-SMLLAGMA-ctmZHrVO.js} +1 -1
- package/dist/client/assets/{flowDiagram-DWJPFMVM-C93jg-t6.js → flowDiagram-DWJPFMVM-DsQXJqF_.js} +1 -1
- package/dist/client/assets/{ganttDiagram-T4ZO3ILL-5Js2lpdY.js → ganttDiagram-T4ZO3ILL-SAFJNY9M.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-UUTBAWPF-BxGzg1Rc.js → gitGraphDiagram-UUTBAWPF-Bd5Dq0vQ.js} +1 -1
- package/dist/client/assets/{graph-BkRyh4eN.js → graph-DzMOPhFp.js} +1 -1
- package/dist/client/assets/index-BLA_qnPk.css +1 -0
- package/dist/client/assets/index-CGE8u6Es.js +465 -0
- package/dist/client/assets/{infoDiagram-42DDH7IO-DZPp1lR6.js → infoDiagram-42DDH7IO-BrEfG9P4.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-UXIWVN3A-CXTuQ2Bb.js → ishikawaDiagram-UXIWVN3A-Dg22orvH.js} +1 -1
- package/dist/client/assets/{journeyDiagram-VCZTEJTY--u6tYqhF.js → journeyDiagram-VCZTEJTY-DEcjEc6c.js} +1 -1
- package/dist/client/assets/{kanban-definition-6JOO6SKY-CGMEohiQ.js → kanban-definition-6JOO6SKY-C2Xa4znN.js} +1 -1
- package/dist/client/assets/{layout-Bzu8ET7F.js → layout-B7E_-0ev.js} +1 -1
- package/dist/client/assets/{linear-B6htRUTP.js → linear-BXJsh3FL.js} +1 -1
- package/dist/client/assets/{mermaid.core-CtpLAI-c.js → mermaid.core-CdZpLnyY.js} +4 -4
- package/dist/client/assets/{min-CEURoL5k.js → min-CEAFFcJY.js} +1 -1
- package/dist/client/assets/{mindmap-definition-QFDTVHPH-B-HiXlEN.js → mindmap-definition-QFDTVHPH-iXzcnu1C.js} +1 -1
- package/dist/client/assets/{pieDiagram-DEJITSTG-DQgiLkTU.js → pieDiagram-DEJITSTG-CdRN75ad.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-34T5L4WZ-xLh7CKRW.js → quadrantDiagram-34T5L4WZ-Dscl0EFH.js} +1 -1
- package/dist/client/assets/{requirementDiagram-MS252O5E-O1SOxLMB.js → requirementDiagram-MS252O5E-CLmSCaQs.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-XADWPNL6-D-mh4ZYc.js → sankeyDiagram-XADWPNL6-BfPk87jf.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-FGHM5R23-Dp2XhLFP.js → sequenceDiagram-FGHM5R23-DE8sU8ej.js} +1 -1
- package/dist/client/assets/{stateDiagram-FHFEXIEX-UQkaQPQt.js → stateDiagram-FHFEXIEX-BmaQc1Nm.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-TcoFpvuT.js +1 -0
- package/dist/client/assets/{timeline-definition-GMOUNBTQ-DDMfKpma.js → timeline-definition-GMOUNBTQ-C4Z0umzx.js} +1 -1
- package/dist/client/assets/{vennDiagram-DHZGUBPP-DaGxBzx2.js → vennDiagram-DHZGUBPP-BKlcRXlE.js} +1 -1
- package/dist/client/assets/{wardley-RL74JXVD-DBNvUuF6.js → wardley-RL74JXVD-B0RT7bAW.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-NUSXRM2D-IbDED74Y.js → wardleyDiagram-NUSXRM2D-mx28kvam.js} +1 -1
- package/dist/client/assets/{xychartDiagram-5P7HB3ND-0RafK-TZ.js → xychartDiagram-5P7HB3ND-DU6qIPI0.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/fleet-plugins/ledger/routes.mjs +130 -1
- package/dist/fleet-plugins/quota/routes.mjs +130 -1
- package/dist/fleet-plugins/scuttlebutt/routes.mjs +130 -1
- package/dist/fleet-plugins/terminal/routes.mjs +747 -115
- package/dist/fleet.mjs +370 -57
- package/package.json +1 -1
- package/dist/client/assets/channel-CzEHIBah.js +0 -1
- package/dist/client/assets/classDiagram-6PBFFD2Q-jNK4g1rr.js +0 -1
- package/dist/client/assets/classDiagram-v2-HSJHXN6E-jNK4g1rr.js +0 -1
- package/dist/client/assets/clone-DggrGKv8.js +0 -1
- package/dist/client/assets/index-BSVaW3Ov.js +0 -465
- package/dist/client/assets/index-DxjgOu3C.css +0 -1
- 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
|
-
|
|
20591
|
-
|
|
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
|
|
20594
|
-
return choice?.type === "tool" &&
|
|
20614
|
+
function matchesToolChoice(choice, identifiers) {
|
|
20615
|
+
return choice?.type === "tool" && typeof choice.name === "string" && identifiers.has(choice.name);
|
|
20595
20616
|
}
|
|
20596
|
-
function
|
|
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) => !
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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
|
|
26175
|
-
const
|
|
26176
|
-
|
|
26177
|
-
|
|
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((
|
|
26441
|
+
new Promise((resolve3) => {
|
|
26188
26442
|
timeout = setTimeout(() => {
|
|
26189
|
-
|
|
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
|
|
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
|
-
//
|
|
26286
|
-
//
|
|
26287
|
-
//
|
|
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
|
|
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,
|
|
27254
|
+
for (const [value, candidates] of optionMappings) {
|
|
26984
27255
|
if (value === void 0) continue;
|
|
26985
|
-
|
|
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 (
|
|
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
|
-
|
|
31223
|
-
|
|
31224
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
46056
|
+
workUnknown = true;
|
|
46057
|
+
listIncomplete = true;
|
|
45730
46058
|
continue;
|
|
45731
46059
|
}
|
|
45732
46060
|
if (typeof entry.id === "string") listedIds.add(entry.id);
|
|
45733
|
-
|
|
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 &&
|
|
46066
|
+
if (!live && workUnknown) return NO_OPINION;
|
|
45736
46067
|
return {
|
|
45737
46068
|
pending: live,
|
|
45738
|
-
settledAgentIds: nextSettledAgentIds({ settledAgentIds, selfAgentId, listedIds, listComplete: !
|
|
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
|
|
45746
|
-
if (typeof entry.id === "string" && (entry.id === selfAgentId || settledAgentIds.has(entry.id))) return
|
|
45747
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
*
|
|
48818
|
-
*
|
|
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
|
-
|
|
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((
|
|
48831
|
-
|
|
48832
|
-
}
|
|
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
|
-
|
|
48853
|
-
|
|
48854
|
-
|
|
48855
|
-
|
|
48856
|
-
|
|
48857
|
-
...
|
|
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")
|
|
49037
|
-
|
|
49038
|
-
|
|
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)
|
|
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.
|
|
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: [...
|
|
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
|
-
|
|
49422
|
-
|
|
49423
|
-
|
|
49424
|
-
|
|
49425
|
-
|
|
49426
|
-
|
|
49427
|
-
|
|
49428
|
-
|
|
49429
|
-
|
|
49430
|
-
|
|
49431
|
-
effortLevels
|
|
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 (!
|
|
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
|
|
50055
|
+
return isRecord15(value) && hasExactKeys(value, ["text"]) && typeof value.text === "string" && value.text.trim().length > 0;
|
|
49459
50056
|
}
|
|
49460
|
-
function
|
|
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
|
};
|