@qilin-agent/token-meter 3.1.3 → 3.1.4
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/README.i18n.yaml +21 -21
- package/lib/index.js +216 -1
- package/lib/types/index.d.ts +1 -0
- package/lib/types/index.js +3 -0
- package/package.json +16 -16
package/README.i18n.yaml
CHANGED
|
@@ -5,60 +5,60 @@
|
|
|
5
5
|
/:
|
|
6
6
|
en: 6eda56ef696001df
|
|
7
7
|
zh: 13f961071e8609a7
|
|
8
|
-
/qilin-token-meter:
|
|
9
|
-
en:
|
|
10
|
-
zh:
|
|
11
|
-
/qilin-token-meter/summary:
|
|
8
|
+
/qilin-agent-token-meter:
|
|
9
|
+
en: 8ae25b1d039ca515
|
|
10
|
+
zh: b278cba8dfc29d0e
|
|
11
|
+
/qilin-agent-token-meter/summary:
|
|
12
12
|
en: 106e3eda9a85bdd1
|
|
13
13
|
zh: 051bc764bf7a1912
|
|
14
|
-
/qilin-token-meter/table-of-contents:
|
|
14
|
+
/qilin-agent-token-meter/table-of-contents:
|
|
15
15
|
en: d152484eb41ac6b4
|
|
16
16
|
zh: 09388d293f9be9cb
|
|
17
|
-
/qilin-token-meter/use-this-package:
|
|
17
|
+
/qilin-agent-token-meter/use-this-package:
|
|
18
18
|
en: 5907ef1472773a47
|
|
19
19
|
zh: 06f1e988290a32c6
|
|
20
|
-
/qilin-token-meter/use-this-package/when-to-choose-it:
|
|
20
|
+
/qilin-agent-token-meter/use-this-package/when-to-choose-it:
|
|
21
21
|
en: e8c4cd1e945f86ba
|
|
22
22
|
zh: 133c1f1a85179d30
|
|
23
|
-
/qilin-token-meter/use-this-package/measuring-pressure:
|
|
23
|
+
/qilin-agent-token-meter/use-this-package/measuring-pressure:
|
|
24
24
|
en: 58de3b364ab5af6b
|
|
25
25
|
zh: c5440af43d321e56
|
|
26
|
-
/qilin-token-meter/use-this-package/session-projections:
|
|
26
|
+
/qilin-agent-token-meter/use-this-package/session-projections:
|
|
27
27
|
en: 0a4c3ae2763be227
|
|
28
28
|
zh: 0e2c5a77f9b4b94f
|
|
29
|
-
/qilin-token-meter/use-this-package/composition:
|
|
29
|
+
/qilin-agent-token-meter/use-this-package/composition:
|
|
30
30
|
en: 27b823c34f4fd96c
|
|
31
31
|
zh: 904373f93056da3e
|
|
32
|
-
/qilin-token-meter/use-this-package/reading-the-numbers:
|
|
32
|
+
/qilin-agent-token-meter/use-this-package/reading-the-numbers:
|
|
33
33
|
en: 7950203e0c146123
|
|
34
34
|
zh: 4fe421b2268f12a9
|
|
35
|
-
/qilin-token-meter/understand-the-implementation:
|
|
35
|
+
/qilin-agent-token-meter/understand-the-implementation:
|
|
36
36
|
en: 9630d7e26193a512
|
|
37
37
|
zh: 94222c2a487c4b8c
|
|
38
|
-
/qilin-token-meter/understand-the-implementation/design-philosophy:
|
|
38
|
+
/qilin-agent-token-meter/understand-the-implementation/design-philosophy:
|
|
39
39
|
en: 921a2c40433abc06
|
|
40
40
|
zh: 5172de58e8724be8
|
|
41
|
-
/qilin-token-meter/understand-the-implementation/source-map:
|
|
41
|
+
/qilin-agent-token-meter/understand-the-implementation/source-map:
|
|
42
42
|
en: 0bea6321416c49d4
|
|
43
43
|
zh: 5446b39910df9829
|
|
44
|
-
/qilin-token-meter/understand-the-implementation/fold-flow:
|
|
44
|
+
/qilin-agent-token-meter/understand-the-implementation/fold-flow:
|
|
45
45
|
en: c97959d84cf8a1a4
|
|
46
46
|
zh: f17570caa42a1a8e
|
|
47
|
-
/qilin-token-meter/understand-the-implementation/projection-semantics:
|
|
47
|
+
/qilin-agent-token-meter/understand-the-implementation/projection-semantics:
|
|
48
48
|
en: 0269213216881c60
|
|
49
49
|
zh: 5c1c4c46c0e6d109
|
|
50
|
-
/qilin-token-meter/further-exploration:
|
|
50
|
+
/qilin-agent-token-meter/further-exploration:
|
|
51
51
|
en: 0e57783e04ab4bae
|
|
52
52
|
zh: df3e56f0117e125e
|
|
53
|
-
/qilin-token-meter/model-experience:
|
|
53
|
+
/qilin-agent-token-meter/model-experience:
|
|
54
54
|
en: 17231a6190c084ce
|
|
55
55
|
zh: 7485c8e03f5a6726
|
|
56
|
-
/qilin-token-meter/model-experience/kv-cache-effect:
|
|
56
|
+
/qilin-agent-token-meter/model-experience/kv-cache-effect:
|
|
57
57
|
en: 40294c1c31b74824
|
|
58
58
|
zh: c2284876b56d2dbc
|
|
59
|
-
/qilin-token-meter/known-limitations-and-deferred-work:
|
|
59
|
+
/qilin-agent-token-meter/known-limitations-and-deferred-work:
|
|
60
60
|
en: a8c57e6f8c5009a2
|
|
61
61
|
zh: 5975018d14bfbdbb
|
|
62
|
-
/qilin-token-meter/known-limitations-and-deferred-work/dev-note:
|
|
62
|
+
/qilin-agent-token-meter/known-limitations-and-deferred-work/dev-note:
|
|
63
63
|
en: 86d686346e2ee1d8
|
|
64
64
|
zh: d660b9884e4764a4
|
package/lib/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import { assembleAssistantStream, lastAssistantStreamChunk } from "@qilin-agent/
|
|
|
4
4
|
import { deepFreeze } from "@qilin-agent/util-values";
|
|
5
5
|
import { SessionLogOffset, SessionSeq, canonicalHeader, deriveEventMessage, headerEquals, isSurfaceEvent } from "@qilin-agent/session";
|
|
6
6
|
import { z as z$1 } from "zod";
|
|
7
|
+
import { lastAssistantStreamChunk as lastAssistantStreamChunk$1 } from "@qilin-agent/llm/assistant-stream";
|
|
7
8
|
//#region lib/types/estimate.js
|
|
8
9
|
/**
|
|
9
10
|
* Fixed-density heuristic token pricing shared by the meter service and the
|
|
@@ -583,6 +584,220 @@ function priceSurface(nodes, pricing, fileText) {
|
|
|
583
584
|
};
|
|
584
585
|
}
|
|
585
586
|
//#endregion
|
|
587
|
+
//#region lib/types/turn-usage.js
|
|
588
|
+
function isCount(value) {
|
|
589
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
590
|
+
}
|
|
591
|
+
function safeSum(values) {
|
|
592
|
+
let total = 0;
|
|
593
|
+
for (const value of values) {
|
|
594
|
+
total += value;
|
|
595
|
+
if (!Number.isSafeInteger(total)) return void 0;
|
|
596
|
+
}
|
|
597
|
+
return total;
|
|
598
|
+
}
|
|
599
|
+
function messageRoute(message) {
|
|
600
|
+
const { provider, model } = message.source;
|
|
601
|
+
return provider.length > 0 && model.length > 0 ? {
|
|
602
|
+
provider,
|
|
603
|
+
model
|
|
604
|
+
} : void 0;
|
|
605
|
+
}
|
|
606
|
+
function streamUsage(stream) {
|
|
607
|
+
return lastAssistantStreamChunk$1(stream, "usage")?.usage;
|
|
608
|
+
}
|
|
609
|
+
function normalizeUsage(usage, route) {
|
|
610
|
+
const { inputTokens, outputTokens, cacheReadTokens, cacheWriteTokens, reasoningTokens, totalTokens } = usage;
|
|
611
|
+
if (!isCount(inputTokens) || !isCount(outputTokens)) return void 0;
|
|
612
|
+
if (cacheReadTokens !== void 0 && !isCount(cacheReadTokens)) return void 0;
|
|
613
|
+
if (cacheWriteTokens !== void 0 && !isCount(cacheWriteTokens)) return void 0;
|
|
614
|
+
if (reasoningTokens !== void 0 && (!isCount(reasoningTokens) || reasoningTokens > outputTokens)) return;
|
|
615
|
+
const knownPrompt = safeSum([
|
|
616
|
+
inputTokens,
|
|
617
|
+
...cacheReadTokens === void 0 ? [] : [cacheReadTokens],
|
|
618
|
+
...cacheWriteTokens === void 0 ? [] : [cacheWriteTokens]
|
|
619
|
+
]);
|
|
620
|
+
if (knownPrompt === void 0) return void 0;
|
|
621
|
+
let exactTotal;
|
|
622
|
+
if (totalTokens !== void 0) {
|
|
623
|
+
if (!isCount(totalTokens)) return void 0;
|
|
624
|
+
const exactPrompt = totalTokens - outputTokens;
|
|
625
|
+
if (!isCount(exactPrompt) || exactPrompt < knownPrompt) return void 0;
|
|
626
|
+
if (cacheReadTokens !== void 0 && cacheWriteTokens !== void 0 && exactPrompt !== knownPrompt) return;
|
|
627
|
+
exactTotal = totalTokens;
|
|
628
|
+
} else {
|
|
629
|
+
if (cacheReadTokens === void 0 || cacheWriteTokens === void 0) return void 0;
|
|
630
|
+
const derivedTotal = safeSum([knownPrompt, outputTokens]);
|
|
631
|
+
if (derivedTotal === void 0) return void 0;
|
|
632
|
+
exactTotal = derivedTotal;
|
|
633
|
+
}
|
|
634
|
+
return {
|
|
635
|
+
inputTokens,
|
|
636
|
+
outputTokens,
|
|
637
|
+
totalTokens: exactTotal,
|
|
638
|
+
...cacheReadTokens === void 0 ? {} : { cacheReadTokens },
|
|
639
|
+
...cacheWriteTokens === void 0 ? {} : { cacheWriteTokens },
|
|
640
|
+
...reasoningTokens === void 0 ? {} : { reasoningTokens },
|
|
641
|
+
...route === void 0 ? {} : { route }
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
function aggregateAttempts(attempts) {
|
|
645
|
+
if (attempts.length === 0) return void 0;
|
|
646
|
+
const inputTokens = safeSum(attempts.map((attempt) => attempt.inputTokens));
|
|
647
|
+
const outputTokens = safeSum(attempts.map((attempt) => attempt.outputTokens));
|
|
648
|
+
const totalTokens = safeSum(attempts.map((attempt) => attempt.totalTokens));
|
|
649
|
+
if (inputTokens === void 0 || outputTokens === void 0 || totalTokens === void 0) return void 0;
|
|
650
|
+
const cacheRead = attempts.map((attempt) => attempt.cacheReadTokens);
|
|
651
|
+
const cacheWrite = attempts.map((attempt) => attempt.cacheWriteTokens);
|
|
652
|
+
const reasoning = attempts.map((attempt) => attempt.reasoningTokens);
|
|
653
|
+
const cacheReadTokens = cacheRead.every(isCount) ? safeSum(cacheRead) : void 0;
|
|
654
|
+
const cacheWriteTokens = cacheWrite.every(isCount) ? safeSum(cacheWrite) : void 0;
|
|
655
|
+
const reasoningTokens = reasoning.every(isCount) ? safeSum(reasoning) : void 0;
|
|
656
|
+
let routes;
|
|
657
|
+
const attributed = attempts.map((attempt) => attempt.route);
|
|
658
|
+
if (attributed.every((route) => route !== void 0)) {
|
|
659
|
+
const unique = /* @__PURE__ */ new Map();
|
|
660
|
+
for (const route of attributed) unique.set(`${route.provider}\0${route.model}`, route);
|
|
661
|
+
routes = [...unique.values()];
|
|
662
|
+
}
|
|
663
|
+
return {
|
|
664
|
+
uncachedInputTokens: inputTokens,
|
|
665
|
+
outputTokens,
|
|
666
|
+
totalTokens,
|
|
667
|
+
...cacheReadTokens === void 0 ? {} : { cacheReadTokens },
|
|
668
|
+
...cacheWriteTokens === void 0 ? {} : { cacheWriteTokens },
|
|
669
|
+
...reasoningTokens === void 0 ? {} : { reasoningTokens },
|
|
670
|
+
...routes === void 0 ? {} : { routes }
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
function sameAttempt(state, turn, step) {
|
|
674
|
+
return state.turn === turn && state.step === step;
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Fold one complete Turn's durable attempt lifecycle into exact token accounting.
|
|
678
|
+
*
|
|
679
|
+
* No attempt is inferred from a usage sample. Any missing lifecycle boundary,
|
|
680
|
+
* incomplete attempt usage, unsafe count, or contradictory exact total makes
|
|
681
|
+
* the whole disclosure unavailable.
|
|
682
|
+
* @param events - Turn-local durable events from `turn/start` through `turn/end`.
|
|
683
|
+
* @returns exact aggregate usage, or undefined when it cannot be proven.
|
|
684
|
+
*/
|
|
685
|
+
function deriveTurnTokenUsage(events) {
|
|
686
|
+
let state = { kind: "idle" };
|
|
687
|
+
const attempts = [];
|
|
688
|
+
let turn;
|
|
689
|
+
let sawEnd = false;
|
|
690
|
+
let invalid = false;
|
|
691
|
+
const closeOpen = (route) => {
|
|
692
|
+
if (state.kind !== "open" || state.sample === void 0) return false;
|
|
693
|
+
const normalized = normalizeUsage(state.sample, route);
|
|
694
|
+
if (normalized === void 0) return false;
|
|
695
|
+
attempts.push(normalized);
|
|
696
|
+
return true;
|
|
697
|
+
};
|
|
698
|
+
for (const event of events) {
|
|
699
|
+
if (invalid) break;
|
|
700
|
+
if (event.type === "turn/start") {
|
|
701
|
+
if (turn !== void 0 || state.kind !== "idle") invalid = true;
|
|
702
|
+
else turn = event.data.turn;
|
|
703
|
+
continue;
|
|
704
|
+
}
|
|
705
|
+
if (turn === void 0) {
|
|
706
|
+
invalid = true;
|
|
707
|
+
break;
|
|
708
|
+
}
|
|
709
|
+
if (event.type === "turn/end") {
|
|
710
|
+
if (event.data.turn !== turn || state.kind !== "idle" || sawEnd) invalid = true;
|
|
711
|
+
else sawEnd = true;
|
|
712
|
+
continue;
|
|
713
|
+
}
|
|
714
|
+
if (sawEnd) {
|
|
715
|
+
invalid = true;
|
|
716
|
+
break;
|
|
717
|
+
}
|
|
718
|
+
if (event.type === "step/start") {
|
|
719
|
+
if (event.data.turn !== turn || state.kind !== "idle") invalid = true;
|
|
720
|
+
else state = {
|
|
721
|
+
kind: "open",
|
|
722
|
+
turn,
|
|
723
|
+
step: event.data.step
|
|
724
|
+
};
|
|
725
|
+
continue;
|
|
726
|
+
}
|
|
727
|
+
if (event.type === "llm/retry-started") {
|
|
728
|
+
if (event.data.turn !== turn || state.kind !== "settled" || state.by !== "retry" || !sameAttempt(state, event.data.turn, event.data.step)) invalid = true;
|
|
729
|
+
else state = {
|
|
730
|
+
kind: "open",
|
|
731
|
+
turn,
|
|
732
|
+
step: event.data.step
|
|
733
|
+
};
|
|
734
|
+
continue;
|
|
735
|
+
}
|
|
736
|
+
if (event.type === "assistant/attempt") {
|
|
737
|
+
if (event.data.turn !== turn || state.kind !== "open" || !sameAttempt(state, event.data.turn, event.data.step)) {
|
|
738
|
+
invalid = true;
|
|
739
|
+
continue;
|
|
740
|
+
}
|
|
741
|
+
const sample = streamUsage(event.data.stream) ?? state.sample;
|
|
742
|
+
state = {
|
|
743
|
+
kind: "open",
|
|
744
|
+
turn,
|
|
745
|
+
step: event.data.step,
|
|
746
|
+
...sample === void 0 ? {} : { sample }
|
|
747
|
+
};
|
|
748
|
+
if (!closeOpen()) invalid = true;
|
|
749
|
+
else state = {
|
|
750
|
+
kind: "finishClosed",
|
|
751
|
+
turn,
|
|
752
|
+
step: event.data.step
|
|
753
|
+
};
|
|
754
|
+
continue;
|
|
755
|
+
}
|
|
756
|
+
if (event.type === "assistant/message") {
|
|
757
|
+
if (event.data.turn !== turn || state.kind !== "open" || !sameAttempt(state, event.data.turn, event.data.step)) {
|
|
758
|
+
invalid = true;
|
|
759
|
+
continue;
|
|
760
|
+
}
|
|
761
|
+
const sample = event.data.usage ?? streamUsage(event.data.stream);
|
|
762
|
+
if (sample !== void 0) state = {
|
|
763
|
+
...state,
|
|
764
|
+
sample
|
|
765
|
+
};
|
|
766
|
+
if (!closeOpen(messageRoute(event.data.message))) invalid = true;
|
|
767
|
+
else state = {
|
|
768
|
+
kind: "settled",
|
|
769
|
+
turn,
|
|
770
|
+
step: event.data.step,
|
|
771
|
+
by: "message"
|
|
772
|
+
};
|
|
773
|
+
continue;
|
|
774
|
+
}
|
|
775
|
+
if (event.type === "llm/retry") {
|
|
776
|
+
if (event.data.turn !== turn || state.kind === "idle" || !sameAttempt(state, event.data.turn, event.data.step)) {
|
|
777
|
+
invalid = true;
|
|
778
|
+
continue;
|
|
779
|
+
}
|
|
780
|
+
if (state.kind === "settled" || state.kind === "open" && !closeOpen()) invalid = true;
|
|
781
|
+
if (!invalid) state = {
|
|
782
|
+
kind: "settled",
|
|
783
|
+
turn,
|
|
784
|
+
step: event.data.step,
|
|
785
|
+
by: "retry"
|
|
786
|
+
};
|
|
787
|
+
continue;
|
|
788
|
+
}
|
|
789
|
+
if (event.type === "step/end") {
|
|
790
|
+
if (event.data.turn !== turn || state.kind === "idle" || !sameAttempt(state, event.data.turn, event.data.step)) {
|
|
791
|
+
invalid = true;
|
|
792
|
+
continue;
|
|
793
|
+
}
|
|
794
|
+
if (state.kind === "open" && !closeOpen()) invalid = true;
|
|
795
|
+
if (!invalid) state = { kind: "idle" };
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
return invalid || !sawEnd || state.kind !== "idle" ? void 0 : aggregateAttempts(attempts);
|
|
799
|
+
}
|
|
800
|
+
//#endregion
|
|
586
801
|
//#region lib/types/index.js
|
|
587
802
|
/**
|
|
588
803
|
* Single replay-aware token-meter service for request and surface pressure.
|
|
@@ -792,4 +1007,4 @@ var TokenMeter = class extends Service {
|
|
|
792
1007
|
}
|
|
793
1008
|
};
|
|
794
1009
|
//#endregion
|
|
795
|
-
export { TokenMeter, TokenMeter as default };
|
|
1010
|
+
export { TokenMeter, TokenMeter as default, deriveTurnTokenUsage };
|
package/lib/types/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ import type { Message } from '@qilin-agent/llm';
|
|
|
9
9
|
import type { EpochHeader, Session } from '@qilin-agent/session';
|
|
10
10
|
import type { TokenMeasurement, TokenMeterConfig } from './types.ts';
|
|
11
11
|
export type * from './types.ts';
|
|
12
|
+
export { deriveTurnTokenUsage } from './turn-usage.ts';
|
|
12
13
|
export type * from './usage-projection.ts';
|
|
13
14
|
export type * from './breakdown-projection.ts';
|
|
14
15
|
declare module '@qilin-agent/kylin' {
|
package/lib/types/index.js
CHANGED
|
@@ -13,6 +13,9 @@ import { contextPressureProjectionDefinition, tokenUsageProjectionDefinition } f
|
|
|
13
13
|
import { estimateContent, estimateMessage, estimateToolsTokens, ROLE_OVERHEAD } from "./estimate.js";
|
|
14
14
|
import { commitSurfaceTokens, planSurfaceTokens } from "./surface-fold.js";
|
|
15
15
|
import { priceSurface } from "./route-pricing.js";
|
|
16
|
+
// The exact per-Turn fold, for consumers that account for a Turn outside the
|
|
17
|
+
// browser transcript; a deployment's usage reporter is the current one.
|
|
18
|
+
export { deriveTurnTokenUsage } from "./turn-usage.js";
|
|
16
19
|
/** Sum disjoint provider usage buckets without double-counting reasoning output. */
|
|
17
20
|
function usageTokens(usage) {
|
|
18
21
|
return usage.inputTokens
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qilin-agent/token-meter",
|
|
3
3
|
"description": "Replay-aware token measurement service (ctx.tokenMeter) for the QiLin",
|
|
4
|
-
"version": "3.1.
|
|
4
|
+
"version": "3.1.4",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -39,26 +39,26 @@
|
|
|
39
39
|
],
|
|
40
40
|
"license": "MIT",
|
|
41
41
|
"peerDependencies": {
|
|
42
|
-
"@qilin-agent/
|
|
43
|
-
"@qilin-agent/
|
|
44
|
-
"@qilin-agent/
|
|
45
|
-
"@qilin-agent/
|
|
46
|
-
"@qilin-agent/session": "3.1.
|
|
47
|
-
"@qilin-agent/compaction-image-offload": "3.1.
|
|
48
|
-
"@qilin-agent/
|
|
42
|
+
"@qilin-agent/llm": "3.1.4",
|
|
43
|
+
"@qilin-agent/compaction": "3.1.4",
|
|
44
|
+
"@qilin-agent/llm-retry": "3.1.4",
|
|
45
|
+
"@qilin-agent/session-projection": "3.1.4",
|
|
46
|
+
"@qilin-agent/session": "3.1.4",
|
|
47
|
+
"@qilin-agent/compaction-image-offload": "3.1.4",
|
|
48
|
+
"@qilin-agent/kylin": "~4.0.3"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"zod": "^4.4.3",
|
|
52
|
-
"@qilin-agent/
|
|
53
|
-
"@qilin-agent/
|
|
52
|
+
"@qilin-agent/util-values": "3.1.4",
|
|
53
|
+
"@qilin-agent/schemastery": "~3.18.3"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
|
-
"@qilin-agent/compaction": "3.1.
|
|
56
|
+
"@qilin-agent/compaction": "3.1.4",
|
|
57
|
+
"@qilin-agent/llm": "3.1.4",
|
|
57
58
|
"@qilin-agent/kylin": "~4.0.3",
|
|
58
|
-
"@qilin-agent/llm-retry": "3.1.
|
|
59
|
-
"@qilin-agent/
|
|
60
|
-
"@qilin-agent/session": "3.1.
|
|
61
|
-
"@qilin-agent/compaction-image-offload": "3.1.
|
|
62
|
-
"@qilin-agent/session-projection": "3.1.3"
|
|
59
|
+
"@qilin-agent/llm-retry": "3.1.4",
|
|
60
|
+
"@qilin-agent/session": "3.1.4",
|
|
61
|
+
"@qilin-agent/session-projection": "3.1.4",
|
|
62
|
+
"@qilin-agent/compaction-image-offload": "3.1.4"
|
|
63
63
|
}
|
|
64
64
|
}
|