@ken-jo/agent-connector 0.1.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 (203) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +6 -0
  3. package/README.md +348 -0
  4. package/dist/amp-QSFH2V7Y.js +217 -0
  5. package/dist/amp-QSFH2V7Y.js.map +1 -0
  6. package/dist/amp-TULUPXXL.js +237 -0
  7. package/dist/amp-TULUPXXL.js.map +1 -0
  8. package/dist/antigravity-CX2YLSY7.js +17 -0
  9. package/dist/antigravity-CX2YLSY7.js.map +1 -0
  10. package/dist/antigravity-STHZWMP3.js +171 -0
  11. package/dist/antigravity-STHZWMP3.js.map +1 -0
  12. package/dist/antigravity-cli-P6HADOAO.js +121 -0
  13. package/dist/antigravity-cli-P6HADOAO.js.map +1 -0
  14. package/dist/antigravity-cli-YQDFVV4U.js +86 -0
  15. package/dist/antigravity-cli-YQDFVV4U.js.map +1 -0
  16. package/dist/chunk-54EMKTTX.js +191 -0
  17. package/dist/chunk-54EMKTTX.js.map +1 -0
  18. package/dist/chunk-7L6ITC4T.js +54 -0
  19. package/dist/chunk-7L6ITC4T.js.map +1 -0
  20. package/dist/chunk-7MSDGHJW.js +316 -0
  21. package/dist/chunk-7MSDGHJW.js.map +1 -0
  22. package/dist/chunk-BCHWFTV4.js +196 -0
  23. package/dist/chunk-BCHWFTV4.js.map +1 -0
  24. package/dist/chunk-DBQMX75U.js +36 -0
  25. package/dist/chunk-DBQMX75U.js.map +1 -0
  26. package/dist/chunk-EXLS7BSI.js +29 -0
  27. package/dist/chunk-EXLS7BSI.js.map +1 -0
  28. package/dist/chunk-JNM3TPLX.js +593 -0
  29. package/dist/chunk-JNM3TPLX.js.map +1 -0
  30. package/dist/chunk-JWNT5KW7.js +197 -0
  31. package/dist/chunk-JWNT5KW7.js.map +1 -0
  32. package/dist/chunk-KHO5WNTP.js +30 -0
  33. package/dist/chunk-KHO5WNTP.js.map +1 -0
  34. package/dist/chunk-LTT56GI3.js +155 -0
  35. package/dist/chunk-LTT56GI3.js.map +1 -0
  36. package/dist/chunk-MEH7GC7R.js +111 -0
  37. package/dist/chunk-MEH7GC7R.js.map +1 -0
  38. package/dist/chunk-MQPNU2CY.js +64 -0
  39. package/dist/chunk-MQPNU2CY.js.map +1 -0
  40. package/dist/chunk-OPXAP3AC.js +12 -0
  41. package/dist/chunk-OPXAP3AC.js.map +1 -0
  42. package/dist/chunk-RK6VBLXP.js +50 -0
  43. package/dist/chunk-RK6VBLXP.js.map +1 -0
  44. package/dist/chunk-TIIP3RG2.js +420 -0
  45. package/dist/chunk-TIIP3RG2.js.map +1 -0
  46. package/dist/chunk-TRKWUNO4.js +702 -0
  47. package/dist/chunk-TRKWUNO4.js.map +1 -0
  48. package/dist/chunk-TRQKHP7D.js +93 -0
  49. package/dist/chunk-TRQKHP7D.js.map +1 -0
  50. package/dist/chunk-WCETQWWK.js +59 -0
  51. package/dist/chunk-WCETQWWK.js.map +1 -0
  52. package/dist/chunk-WV63X7IK.js +357 -0
  53. package/dist/chunk-WV63X7IK.js.map +1 -0
  54. package/dist/chunk-XD7MY6HB.js +343 -0
  55. package/dist/chunk-XD7MY6HB.js.map +1 -0
  56. package/dist/chunk-XNAM7F35.js +276 -0
  57. package/dist/chunk-XNAM7F35.js.map +1 -0
  58. package/dist/chunk-XUCWLJY5.js +238 -0
  59. package/dist/chunk-XUCWLJY5.js.map +1 -0
  60. package/dist/chunk-YNZU6PAV.js +800 -0
  61. package/dist/chunk-YNZU6PAV.js.map +1 -0
  62. package/dist/chunk-YT43MUSJ.js +146 -0
  63. package/dist/chunk-YT43MUSJ.js.map +1 -0
  64. package/dist/claude-code-HKDV7T32.js +637 -0
  65. package/dist/claude-code-HKDV7T32.js.map +1 -0
  66. package/dist/claude-code-W74MNTAC.js +652 -0
  67. package/dist/claude-code-W74MNTAC.js.map +1 -0
  68. package/dist/cli/sdk.d.ts +54 -0
  69. package/dist/cli/sdk.js +82 -0
  70. package/dist/cli/sdk.js.map +1 -0
  71. package/dist/cli.d.ts +2 -0
  72. package/dist/cli.js +15 -0
  73. package/dist/cli.js.map +1 -0
  74. package/dist/codebuff-3PMZJJHP.js +307 -0
  75. package/dist/codebuff-3PMZJJHP.js.map +1 -0
  76. package/dist/codebuff-QX57FJDN.js +220 -0
  77. package/dist/codebuff-QX57FJDN.js.map +1 -0
  78. package/dist/codex-JFQTS6LU.js +593 -0
  79. package/dist/codex-JFQTS6LU.js.map +1 -0
  80. package/dist/codex-Q3ATRRX3.js +364 -0
  81. package/dist/codex-Q3ATRRX3.js.map +1 -0
  82. package/dist/copilot-cli-MTCGHZ2T.js +622 -0
  83. package/dist/copilot-cli-MTCGHZ2T.js.map +1 -0
  84. package/dist/copilot-cli-SJGIXVSD.js +369 -0
  85. package/dist/copilot-cli-SJGIXVSD.js.map +1 -0
  86. package/dist/crush-I7PK3FGI.js +360 -0
  87. package/dist/crush-I7PK3FGI.js.map +1 -0
  88. package/dist/crush-LN5QFFR2.js +183 -0
  89. package/dist/crush-LN5QFFR2.js.map +1 -0
  90. package/dist/cursor-4TMXKJOZ.js +603 -0
  91. package/dist/cursor-4TMXKJOZ.js.map +1 -0
  92. package/dist/cursor-QZ6MTO74.js +194 -0
  93. package/dist/cursor-QZ6MTO74.js.map +1 -0
  94. package/dist/detect-WIAGVYS4.js +68 -0
  95. package/dist/detect-WIAGVYS4.js.map +1 -0
  96. package/dist/doctor-EZW2K4NI.js +372 -0
  97. package/dist/doctor-EZW2K4NI.js.map +1 -0
  98. package/dist/droid-KN6VRMMT.js +424 -0
  99. package/dist/droid-KN6VRMMT.js.map +1 -0
  100. package/dist/droid-W5WF5XEX.js +224 -0
  101. package/dist/droid-W5WF5XEX.js.map +1 -0
  102. package/dist/gemini-cli-CIRW37TU.js +369 -0
  103. package/dist/gemini-cli-CIRW37TU.js.map +1 -0
  104. package/dist/gemini-cli-Y35FFK4M.js +705 -0
  105. package/dist/gemini-cli-Y35FFK4M.js.map +1 -0
  106. package/dist/goose-A3M5GODH.js +212 -0
  107. package/dist/goose-A3M5GODH.js.map +1 -0
  108. package/dist/goose-ZTGTSUFJ.js +480 -0
  109. package/dist/goose-ZTGTSUFJ.js.map +1 -0
  110. package/dist/hermes-H2KV44GK.js +470 -0
  111. package/dist/hermes-H2KV44GK.js.map +1 -0
  112. package/dist/hermes-ZYLMOFLZ.js +196 -0
  113. package/dist/hermes-ZYLMOFLZ.js.map +1 -0
  114. package/dist/hook-YVDILAMZ.js +75 -0
  115. package/dist/hook-YVDILAMZ.js.map +1 -0
  116. package/dist/index.d.ts +22 -0
  117. package/dist/index.js +10 -0
  118. package/dist/index.js.map +1 -0
  119. package/dist/install-HQRYZOQ2.js +60 -0
  120. package/dist/install-HQRYZOQ2.js.map +1 -0
  121. package/dist/jetbrains-copilot-HWA3DMH7.js +633 -0
  122. package/dist/jetbrains-copilot-HWA3DMH7.js.map +1 -0
  123. package/dist/kilo-NSWAVP6P.js +235 -0
  124. package/dist/kilo-NSWAVP6P.js.map +1 -0
  125. package/dist/kilo-QXJRCSLT.js +384 -0
  126. package/dist/kilo-QXJRCSLT.js.map +1 -0
  127. package/dist/kilo-cli-EAQTHXUW.js +587 -0
  128. package/dist/kilo-cli-EAQTHXUW.js.map +1 -0
  129. package/dist/kilo-cli-EVBKUODA.js +160 -0
  130. package/dist/kilo-cli-EVBKUODA.js.map +1 -0
  131. package/dist/kimi-EI3UM3ZV.js +155 -0
  132. package/dist/kimi-EI3UM3ZV.js.map +1 -0
  133. package/dist/kimi-GQ7VXNYT.js +423 -0
  134. package/dist/kimi-GQ7VXNYT.js.map +1 -0
  135. package/dist/kiro-UCLSLBW2.js +217 -0
  136. package/dist/kiro-UCLSLBW2.js.map +1 -0
  137. package/dist/kiro-VQTT6LO2.js +451 -0
  138. package/dist/kiro-VQTT6LO2.js.map +1 -0
  139. package/dist/leaderboard-K6ON3SWW.js +137 -0
  140. package/dist/leaderboard-K6ON3SWW.js.map +1 -0
  141. package/dist/mux-T2TV4CIQ.js +211 -0
  142. package/dist/mux-T2TV4CIQ.js.map +1 -0
  143. package/dist/mux-ZMLVAXOO.js +96 -0
  144. package/dist/mux-ZMLVAXOO.js.map +1 -0
  145. package/dist/omp-PB4VDKS6.js +586 -0
  146. package/dist/omp-PB4VDKS6.js.map +1 -0
  147. package/dist/openclaw-ETN2M63O.js +751 -0
  148. package/dist/openclaw-ETN2M63O.js.map +1 -0
  149. package/dist/openclaw-IYA7ASL6.js +150 -0
  150. package/dist/openclaw-IYA7ASL6.js.map +1 -0
  151. package/dist/opencode-FZ33JKUZ.js +734 -0
  152. package/dist/opencode-FZ33JKUZ.js.map +1 -0
  153. package/dist/opencode-HWT3RSMN.js +216 -0
  154. package/dist/opencode-HWT3RSMN.js.map +1 -0
  155. package/dist/package-DLOVE2E4.js +1132 -0
  156. package/dist/package-DLOVE2E4.js.map +1 -0
  157. package/dist/pi-CIU2QHT4.js +208 -0
  158. package/dist/pi-CIU2QHT4.js.map +1 -0
  159. package/dist/pi-UFX2OYFV.js +139 -0
  160. package/dist/pi-UFX2OYFV.js.map +1 -0
  161. package/dist/qwen-code-5POCNU6P.js +573 -0
  162. package/dist/qwen-code-5POCNU6P.js.map +1 -0
  163. package/dist/qwen-code-W7NUCDEN.js +134 -0
  164. package/dist/qwen-code-W7NUCDEN.js.map +1 -0
  165. package/dist/roo-code-IOIUXRDE.js +242 -0
  166. package/dist/roo-code-IOIUXRDE.js.map +1 -0
  167. package/dist/roo-code-ZLKH4A7N.js +229 -0
  168. package/dist/roo-code-ZLKH4A7N.js.map +1 -0
  169. package/dist/runtime/index.d.ts +157 -0
  170. package/dist/runtime/index.js +20 -0
  171. package/dist/runtime/index.js.map +1 -0
  172. package/dist/serve-HVWWY4B6.js +65 -0
  173. package/dist/serve-HVWWY4B6.js.map +1 -0
  174. package/dist/status-FE7TECZD.js +128 -0
  175. package/dist/status-FE7TECZD.js.map +1 -0
  176. package/dist/synthetic-UKR6SMGS.js +178 -0
  177. package/dist/synthetic-UKR6SMGS.js.map +1 -0
  178. package/dist/telemetry-36GESKQQ.js +349 -0
  179. package/dist/telemetry-36GESKQQ.js.map +1 -0
  180. package/dist/trae-JSNDZFKL.js +204 -0
  181. package/dist/trae-JSNDZFKL.js.map +1 -0
  182. package/dist/trae-PA26DPBW.js +157 -0
  183. package/dist/trae-PA26DPBW.js.map +1 -0
  184. package/dist/types-CYUB1OTr.d.ts +427 -0
  185. package/dist/uninstall-IGCKNOSF.js +72 -0
  186. package/dist/uninstall-IGCKNOSF.js.map +1 -0
  187. package/dist/upgrade-JV2VK242.js +104 -0
  188. package/dist/upgrade-JV2VK242.js.map +1 -0
  189. package/dist/usage-6ER6EMEY.js +324 -0
  190. package/dist/usage-6ER6EMEY.js.map +1 -0
  191. package/dist/usage-event-JKBI6XCF.js +48 -0
  192. package/dist/usage-event-JKBI6XCF.js.map +1 -0
  193. package/dist/vscode-copilot-CK3NO54M.js +715 -0
  194. package/dist/vscode-copilot-CK3NO54M.js.map +1 -0
  195. package/dist/warp-JNQA7PBU.js +126 -0
  196. package/dist/warp-JNQA7PBU.js.map +1 -0
  197. package/dist/warp-QNVDXOG4.js +199 -0
  198. package/dist/warp-QNVDXOG4.js.map +1 -0
  199. package/dist/zed-NNNX5MNT.js +272 -0
  200. package/dist/zed-NNNX5MNT.js.map +1 -0
  201. package/dist/zed-VRJKWYUR.js +209 -0
  202. package/dist/zed-VRJKWYUR.js.map +1 -0
  203. package/package.json +79 -0
@@ -0,0 +1,343 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/usage/normalize.ts
4
+ var REASONING_TIERS = /* @__PURE__ */ new Set([
5
+ "minimal",
6
+ "low",
7
+ "medium",
8
+ "high",
9
+ "xhigh",
10
+ "auto",
11
+ "none"
12
+ ]);
13
+ function stripParenthesizedReasoningTier(modelId) {
14
+ if (!modelId.endsWith(")")) return null;
15
+ const withoutClosing = modelId.slice(0, -1);
16
+ const open = withoutClosing.lastIndexOf("(");
17
+ if (open < 0) return null;
18
+ const base = withoutClosing.slice(0, open);
19
+ const tier = withoutClosing.slice(open + 1);
20
+ if (base === "" || base.trim() !== base) return null;
21
+ if (!REASONING_TIERS.has(tier)) return null;
22
+ return base;
23
+ }
24
+ function normalizeAnthropicPrefixedClaudeModel(modelId) {
25
+ const prefix = "anthropic/claude-";
26
+ if (!modelId.startsWith(prefix)) return null;
27
+ const rest = modelId.slice(prefix.length);
28
+ const parts = rest.split("-");
29
+ const major = parts[0];
30
+ const minor = parts[1];
31
+ const family = parts[2];
32
+ if (major === void 0 || minor === void 0 || family === void 0) return null;
33
+ if (parts.length > 3) return null;
34
+ if (family !== "opus" && family !== "sonnet" && family !== "haiku") return null;
35
+ return `claude-${family}-${major}-${minor}`;
36
+ }
37
+ function normalizeModelForGrouping(modelId) {
38
+ let name = modelId.toLowerCase();
39
+ const base = stripParenthesizedReasoningTier(name);
40
+ if (base !== null) name = base;
41
+ if (name.length > 9) {
42
+ const potentialDate = name.slice(name.length - 8);
43
+ const allDigits = /^[0-9]{8}$/.test(potentialDate);
44
+ if (allDigits && name.charCodeAt(name.length - 9) === 45) {
45
+ name = name.slice(0, name.length - 9);
46
+ }
47
+ }
48
+ if (name.includes("claude")) {
49
+ const chars = [...name];
50
+ let result = "";
51
+ for (let i = 0; i < chars.length; i++) {
52
+ const c = chars[i];
53
+ const prev = chars[i - 1];
54
+ const next = chars[i + 1];
55
+ if (c === "." && i > 0 && i < chars.length - 1 && prev !== void 0 && next !== void 0 && isAsciiDigit(prev) && isAsciiDigit(next)) {
56
+ result += "-";
57
+ } else {
58
+ result += c;
59
+ }
60
+ }
61
+ name = result;
62
+ }
63
+ const canonical = normalizeAnthropicPrefixedClaudeModel(name);
64
+ if (canonical !== null) name = canonical;
65
+ return name;
66
+ }
67
+ function isAsciiDigit(c) {
68
+ return c >= "0" && c <= "9";
69
+ }
70
+ function containsDelimited(haystack, needle) {
71
+ let from = 0;
72
+ for (; ; ) {
73
+ const pos = haystack.indexOf(needle, from);
74
+ if (pos < 0) return false;
75
+ const beforeOk = pos === 0 || !isAlphaNum(haystack.charCodeAt(pos - 1));
76
+ const afterPos = pos + needle.length;
77
+ const afterOk = afterPos === haystack.length || !isAlphaNum(haystack.charCodeAt(afterPos));
78
+ if (beforeOk && afterOk) return true;
79
+ from = pos + 1;
80
+ }
81
+ }
82
+ function isAlphaNum(code) {
83
+ return code >= 48 && code <= 57 || // 0-9
84
+ code >= 65 && code <= 90 || // A-Z
85
+ code >= 97 && code <= 122;
86
+ }
87
+ function inferProvider(model) {
88
+ const lower = model.toLowerCase();
89
+ if (lower.includes("claude") || lower.includes("anthropic") || containsDelimited(lower, "opus") || containsDelimited(lower, "sonnet") || containsDelimited(lower, "haiku")) {
90
+ return "anthropic";
91
+ }
92
+ if (lower.includes("gpt") || lower.includes("openai") || containsDelimited(lower, "o1") || containsDelimited(lower, "o3") || containsDelimited(lower, "o4")) {
93
+ return "openai";
94
+ }
95
+ if (lower.includes("gemini") || lower.includes("google")) return "google";
96
+ if (lower.includes("grok")) return "xai";
97
+ if (lower.includes("deepseek")) return "deepseek";
98
+ if (lower.includes("minimax")) return "minimax";
99
+ if (lower.includes("mistral") || lower.includes("mixtral")) return "mistral";
100
+ if (lower.includes("llama") || containsDelimited(lower, "meta")) return "meta";
101
+ if (lower.includes("qwen")) return "qwen";
102
+ return null;
103
+ }
104
+ var ZERO_WIDTH_RE = /[​‌‍]/g;
105
+ function stripZeroWidthChars(s) {
106
+ ZERO_WIDTH_RE.lastIndex = 0;
107
+ return ZERO_WIDTH_RE.test(s) ? s.replace(ZERO_WIDTH_RE, "") : s;
108
+ }
109
+ var AGENT_PREFIXES = ["astrape:", "oh-my-claudecode:", "oh-my-codex:"];
110
+ function stripAgentPrefix(name) {
111
+ for (const prefix of AGENT_PREFIXES) {
112
+ if (name.length >= prefix.length && name.slice(0, prefix.length).toLowerCase() === prefix) {
113
+ return name.slice(prefix.length);
114
+ }
115
+ }
116
+ return name;
117
+ }
118
+ function canonicalizeAgentName(name) {
119
+ return name.split(/\s+/).filter((w) => w !== "").join(" ");
120
+ }
121
+ function titlecaseWord(word) {
122
+ const lower = word.toLowerCase();
123
+ if (lower === "ui") return "UI";
124
+ if (lower === "ux") return "UX";
125
+ if (lower === "api") return "API";
126
+ if (word === "") return "";
127
+ const chars = [...word];
128
+ const first = chars[0];
129
+ return first.toUpperCase() + chars.slice(1).join("");
130
+ }
131
+ function titlecaseAgent(name) {
132
+ if (name === "") return "";
133
+ return name.split("-").flatMap((part) => part.split(/\s+/)).filter((w) => w !== "").map(titlecaseWord).join(" ");
134
+ }
135
+ function normalizeAgentName(agent) {
136
+ const cleaned = stripZeroWidthChars(agent);
137
+ const trimmed = cleaned.trim();
138
+ const stripped = stripAgentPrefix(trimmed);
139
+ const canonical = canonicalizeAgentName(stripped);
140
+ const agentLower = canonical.toLowerCase();
141
+ if (agentLower.includes("plan")) {
142
+ if (agentLower.includes("omo") || agentLower.includes("sisyphus")) {
143
+ return "Planner-Sisyphus";
144
+ }
145
+ return titlecaseAgent(canonical);
146
+ }
147
+ if (agentLower === "omo" || agentLower === "sisyphus") return "Sisyphus";
148
+ if (agentLower === "orchestrator-sisyphus") return "Atlas";
149
+ return titlecaseAgent(canonical);
150
+ }
151
+ function normalizeOhMyOpencodeAgentName(agentLower) {
152
+ switch (agentLower) {
153
+ case "sisyphus (ultraworker)":
154
+ case "sisyphus - ultraworker":
155
+ case "sisyphus ultraworker":
156
+ case "sisyphus":
157
+ return "Sisyphus";
158
+ case "hephaestus (deep agent)":
159
+ case "hephaestus - deep agent":
160
+ case "hephaestus deep agent":
161
+ case "hephaestus":
162
+ return "Hephaestus";
163
+ case "prometheus (plan builder)":
164
+ case "prometheus - plan builder":
165
+ case "prometheus plan builder":
166
+ case "prometheus (planner)":
167
+ case "prometheus":
168
+ return "Prometheus";
169
+ case "atlas (plan executor)":
170
+ case "atlas - plan executor":
171
+ case "atlas plan executor":
172
+ case "atlas":
173
+ return "Atlas";
174
+ case "metis (plan consultant)":
175
+ case "metis - plan consultant":
176
+ case "metis plan consultant":
177
+ case "metis":
178
+ return "Metis";
179
+ case "momus (plan critic)":
180
+ case "momus - plan critic":
181
+ case "momus plan critic":
182
+ case "momus (plan reviewer)":
183
+ case "momus":
184
+ return "Momus";
185
+ case "orchestrator-sisyphus":
186
+ return "Atlas";
187
+ case "sisyphus-junior":
188
+ return "Sisyphus-Junior";
189
+ case "planner-sisyphus":
190
+ return "Planner-Sisyphus";
191
+ default:
192
+ return void 0;
193
+ }
194
+ }
195
+ function normalizeOpencodeAgentName(agent) {
196
+ const cleaned = stripZeroWidthChars(agent);
197
+ const trimmed = cleaned.trim();
198
+ const stripped = stripAgentPrefix(trimmed);
199
+ const canonical = canonicalizeAgentName(stripped);
200
+ const agentLower = canonical.toLowerCase();
201
+ const ohMy = normalizeOhMyOpencodeAgentName(agentLower);
202
+ if (ohMy !== void 0) return ohMy;
203
+ return normalizeAgentName(canonical);
204
+ }
205
+ function normalizeWorkspaceKey(raw) {
206
+ const trimmed = raw.trim();
207
+ if (trimmed === "") return void 0;
208
+ const preserveUnc = trimmed.startsWith("\\\\") || trimmed.startsWith("//");
209
+ let normalized = trimmed.replace(/\\/g, "/");
210
+ if (preserveUnc) {
211
+ let body = normalized.replace(/^\/+/, "");
212
+ while (body.includes("//")) body = body.replace(/\/\//g, "/");
213
+ normalized = `//${body}`;
214
+ } else {
215
+ while (normalized.includes("//")) normalized = normalized.replace(/\/\//g, "/");
216
+ }
217
+ const minLen = preserveUnc ? 2 : 1;
218
+ if (normalized.length > minLen) {
219
+ normalized = normalized.replace(/\/+$/, "");
220
+ }
221
+ return normalized === "" ? void 0 : normalized;
222
+ }
223
+ function workspaceLabelFromKey(key) {
224
+ const segments = key.split("/");
225
+ for (let i = segments.length - 1; i >= 0; i--) {
226
+ const seg = segments[i];
227
+ if (seg !== void 0 && seg !== "") return seg;
228
+ }
229
+ return void 0;
230
+ }
231
+ function workspaceFromPath(raw) {
232
+ const key = normalizeWorkspaceKey(raw);
233
+ if (key === void 0) return {};
234
+ const label = workspaceLabelFromKey(key);
235
+ return label === void 0 ? { projectKey: key } : { projectKey: key, projectLabel: label };
236
+ }
237
+
238
+ // src/usage/aggregate.ts
239
+ function emptyTokens() {
240
+ return { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, reasoning: 0 };
241
+ }
242
+ function sumTokens(t) {
243
+ return t.input + t.output + t.cacheRead + t.cacheWrite + t.reasoning;
244
+ }
245
+ function addTokensInPlace(a, b) {
246
+ a.input += b.input;
247
+ a.output += b.output;
248
+ a.cacheRead += b.cacheRead;
249
+ a.cacheWrite += b.cacheWrite;
250
+ a.reasoning += b.reasoning;
251
+ }
252
+ var CONFIDENCE_RANK = {
253
+ "host-estimated": 0,
254
+ "host-reported": 1
255
+ };
256
+ function worstConfidence(a, b) {
257
+ return CONFIDENCE_RANK[b] < CONFIDENCE_RANK[a] ? b : a;
258
+ }
259
+ function dedupe(records) {
260
+ const seen = /* @__PURE__ */ new Set();
261
+ const out = [];
262
+ for (const rec of records) {
263
+ const key = rec.dedupKey;
264
+ if (key === void 0 || key === "") {
265
+ out.push(rec);
266
+ continue;
267
+ }
268
+ if (!seen.has(key)) {
269
+ seen.add(key);
270
+ out.push(rec);
271
+ }
272
+ }
273
+ return out;
274
+ }
275
+ function localDay(ts) {
276
+ const d = new Date(ts);
277
+ const y = d.getFullYear();
278
+ const m = `${d.getMonth() + 1}`.padStart(2, "0");
279
+ const day = `${d.getDate()}`.padStart(2, "0");
280
+ return `${y}-${m}-${day}`;
281
+ }
282
+ function groupKey(rec, by) {
283
+ switch (by) {
284
+ case "platform":
285
+ return rec.platformId;
286
+ case "project":
287
+ return rec.projectLabel ?? rec.projectKey ?? "(no project)";
288
+ case "session":
289
+ return rec.sessionId;
290
+ case "model":
291
+ return normalizeModelForGrouping(rec.modelId);
292
+ case "day":
293
+ return localDay(rec.ts);
294
+ }
295
+ }
296
+ function aggregateBy(records, by) {
297
+ const groups = /* @__PURE__ */ new Map();
298
+ for (const rec of records) {
299
+ const key = groupKey(rec, by);
300
+ let g = groups.get(key);
301
+ if (g === void 0) {
302
+ g = {
303
+ key,
304
+ tokens: emptyTokens(),
305
+ total: 0,
306
+ sessions: 0,
307
+ messages: 0,
308
+ confidence: rec.confidence,
309
+ lastTs: rec.ts,
310
+ _sessions: /* @__PURE__ */ new Set()
311
+ };
312
+ groups.set(key, g);
313
+ }
314
+ addTokensInPlace(g.tokens, rec.tokens);
315
+ g.messages += rec.messageCount;
316
+ g._sessions.add(rec.sessionId);
317
+ g.confidence = worstConfidence(g.confidence, rec.confidence);
318
+ if (rec.ts > g.lastTs) g.lastTs = rec.ts;
319
+ if (rec.cost !== void 0) g.cost = (g.cost ?? 0) + rec.cost;
320
+ }
321
+ const rows = [];
322
+ for (const g of groups.values()) {
323
+ const { _sessions, ...summary } = g;
324
+ summary.total = sumTokens(summary.tokens);
325
+ summary.sessions = _sessions.size;
326
+ rows.push(summary);
327
+ }
328
+ rows.sort((a, b) => b.total - a.total || b.lastTs - a.lastTs);
329
+ return rows;
330
+ }
331
+
332
+ export {
333
+ inferProvider,
334
+ normalizeOpencodeAgentName,
335
+ normalizeWorkspaceKey,
336
+ workspaceLabelFromKey,
337
+ workspaceFromPath,
338
+ emptyTokens,
339
+ sumTokens,
340
+ dedupe,
341
+ aggregateBy
342
+ };
343
+ //# sourceMappingURL=chunk-XD7MY6HB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/usage/normalize.ts","../src/usage/aggregate.ts"],"sourcesContent":["/**\n * usage/normalize — model/provider/workspace normalization.\n *\n * `normalizeModelForGrouping` and `inferProvider` are FAITHFUL ports of\n * tokscale's order-dependent logic (tokscale-core/src/lib.rs\n * normalize_model_for_grouping + provider_identity.rs inferred_provider_from_model).\n * The matching order is load-bearing — keep the steps in sequence.\n *\n * `normalizeWorkspaceKey` / `workspaceLabelFromKey` port tokscale\n * sessions/mod.rs (same slash-collapsing + UNC-prefix handling) so a cwd string\n * from a host log maps to the same stable project key tokscale would produce.\n */\n\n// ─────────────────────────────────────────────────────────────────────────\n// Model normalization (port of lib.rs normalize_model_for_grouping)\n// ─────────────────────────────────────────────────────────────────────────\n\nconst REASONING_TIERS: ReadonlySet<string> = new Set([\n \"minimal\",\n \"low\",\n \"medium\",\n \"high\",\n \"xhigh\",\n \"auto\",\n \"none\",\n]);\n\n/**\n * Strip a `(level)` reasoning-effort suffix, returning the base model — or null\n * when the suffix is not one of the recognized tiers. Port of\n * strip_parenthesized_reasoning_tier: the base must be non-empty and already\n * trimmed (no surrounding whitespace).\n */\nfunction stripParenthesizedReasoningTier(modelId: string): string | null {\n if (!modelId.endsWith(\")\")) return null;\n const withoutClosing = modelId.slice(0, -1);\n const open = withoutClosing.lastIndexOf(\"(\");\n if (open < 0) return null;\n const base = withoutClosing.slice(0, open);\n const tier = withoutClosing.slice(open + 1);\n if (base === \"\" || base.trim() !== base) return null;\n if (!REASONING_TIERS.has(tier)) return null;\n return base;\n}\n\n/**\n * Map an `anthropic/claude-<major>-<minor>-<family>` id to the canonical\n * `claude-<family>-<major>-<minor>` form. Returns null when the shape does not\n * match exactly (too many parts, unknown family). Port of\n * normalize_anthropic_prefixed_claude_model.\n */\nfunction normalizeAnthropicPrefixedClaudeModel(modelId: string): string | null {\n const prefix = \"anthropic/claude-\";\n if (!modelId.startsWith(prefix)) return null;\n const rest = modelId.slice(prefix.length);\n const parts = rest.split(\"-\");\n const major = parts[0];\n const minor = parts[1];\n const family = parts[2];\n if (major === undefined || minor === undefined || family === undefined) return null;\n if (parts.length > 3) return null; // too many parts\n if (family !== \"opus\" && family !== \"sonnet\" && family !== \"haiku\") return null;\n return `claude-${family}-${major}-${minor}`;\n}\n\n/**\n * Normalize a raw model id to a stable grouping key. ORDER MATTERS — this is the\n * exact tokscale sequence:\n * 1. lowercase;\n * 2. strip a recognized `(tier)` reasoning suffix;\n * 3. strip a trailing `-YYYYMMDD` 8-digit date (only when len > 9 and the\n * 9th-from-end char is '-');\n * 4. for claude ids, replace dots between two digits with dashes\n * (e.g. \"claude-3.5-sonnet\" → \"claude-3-5-sonnet\");\n * 5. canonicalize an `anthropic/claude-*` prefix.\n */\nexport function normalizeModelForGrouping(modelId: string): string {\n let name = modelId.toLowerCase();\n\n const base = stripParenthesizedReasoningTier(name);\n if (base !== null) name = base;\n\n if (name.length > 9) {\n const potentialDate = name.slice(name.length - 8);\n const allDigits = /^[0-9]{8}$/.test(potentialDate);\n if (allDigits && name.charCodeAt(name.length - 9) === 0x2d /* '-' */) {\n name = name.slice(0, name.length - 9);\n }\n }\n\n if (name.includes(\"claude\")) {\n const chars = [...name];\n let result = \"\";\n for (let i = 0; i < chars.length; i++) {\n const c = chars[i] as string;\n const prev = chars[i - 1];\n const next = chars[i + 1];\n if (\n c === \".\" &&\n i > 0 &&\n i < chars.length - 1 &&\n prev !== undefined &&\n next !== undefined &&\n isAsciiDigit(prev) &&\n isAsciiDigit(next)\n ) {\n result += \"-\";\n } else {\n result += c;\n }\n }\n name = result;\n }\n\n const canonical = normalizeAnthropicPrefixedClaudeModel(name);\n if (canonical !== null) name = canonical;\n\n return name;\n}\n\nfunction isAsciiDigit(c: string): boolean {\n return c >= \"0\" && c <= \"9\";\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Provider inference (port of provider_identity.rs inferred_provider_from_model)\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * Whether `needle` occurs in `haystack` not flanked by alphanumerics on either\n * side (so \"o1\" matches \"o1-preview\" but not \"protocol1\"). Port of\n * contains_delimited.\n */\nfunction containsDelimited(haystack: string, needle: string): boolean {\n let from = 0;\n for (;;) {\n const pos = haystack.indexOf(needle, from);\n if (pos < 0) return false;\n const beforeOk = pos === 0 || !isAlphaNum(haystack.charCodeAt(pos - 1));\n const afterPos = pos + needle.length;\n const afterOk = afterPos === haystack.length || !isAlphaNum(haystack.charCodeAt(afterPos));\n if (beforeOk && afterOk) return true;\n from = pos + 1;\n }\n}\n\nfunction isAlphaNum(code: number): boolean {\n return (\n (code >= 0x30 && code <= 0x39) || // 0-9\n (code >= 0x41 && code <= 0x5a) || // A-Z\n (code >= 0x61 && code <= 0x7a) // a-z\n );\n}\n\n/**\n * Infer a canonical provider id from a model id. ORDER MATTERS — first match\n * wins, exactly as tokscale's inferred_provider_from_model. Returns null when no\n * family matches (caller supplies a platform default).\n */\nexport function inferProvider(model: string): string | null {\n const lower = model.toLowerCase();\n\n if (\n lower.includes(\"claude\") ||\n lower.includes(\"anthropic\") ||\n containsDelimited(lower, \"opus\") ||\n containsDelimited(lower, \"sonnet\") ||\n containsDelimited(lower, \"haiku\")\n ) {\n return \"anthropic\";\n }\n if (\n lower.includes(\"gpt\") ||\n lower.includes(\"openai\") ||\n containsDelimited(lower, \"o1\") ||\n containsDelimited(lower, \"o3\") ||\n containsDelimited(lower, \"o4\")\n ) {\n return \"openai\";\n }\n if (lower.includes(\"gemini\") || lower.includes(\"google\")) return \"google\";\n if (lower.includes(\"grok\")) return \"xai\";\n if (lower.includes(\"deepseek\")) return \"deepseek\";\n if (lower.includes(\"minimax\")) return \"minimax\";\n if (lower.includes(\"mistral\") || lower.includes(\"mixtral\")) return \"mistral\";\n if (lower.includes(\"llama\") || containsDelimited(lower, \"meta\")) return \"meta\";\n if (lower.includes(\"qwen\")) return \"qwen\";\n return null;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Agent-name normalization (port of sessions/mod.rs normalize_agent_name +\n// normalize_opencode_agent_name). The opencode reader MUST normalize the agent\n// before its dedup fingerprint, or fork-copied history (which differs only in a\n// raw agent string) escapes dedup and double-counts.\n// ─────────────────────────────────────────────────────────────────────────\n\n// U+200B ZWSP, U+200C ZWNJ, U+200D ZWJ, U+FEFF BOM/ZWNBSP.\nconst ZERO_WIDTH_RE = /[​‌‍]/g;\n\n/** Strip zero-width chars oh-my-openagent uses as invisible sort prefixes. */\nfunction stripZeroWidthChars(s: string): string {\n ZERO_WIDTH_RE.lastIndex = 0;\n return ZERO_WIDTH_RE.test(s) ? s.replace(ZERO_WIDTH_RE, \"\") : s;\n}\n\nconst AGENT_PREFIXES = [\"astrape:\", \"oh-my-claudecode:\", \"oh-my-codex:\"] as const;\n\n/** Strip a known namespace prefix (case-insensitive), else return as-is. */\nfunction stripAgentPrefix(name: string): string {\n for (const prefix of AGENT_PREFIXES) {\n if (name.length >= prefix.length && name.slice(0, prefix.length).toLowerCase() === prefix) {\n return name.slice(prefix.length);\n }\n }\n return name;\n}\n\n/** Collapse internal whitespace runs to single spaces (port of canonicalize_agent_name). */\nfunction canonicalizeAgentName(name: string): string {\n return name.split(/\\s+/).filter((w) => w !== \"\").join(\" \");\n}\n\n/** Titlecase one word, with UI/UX/API kept uppercase (port of titlecase_word). */\nfunction titlecaseWord(word: string): string {\n const lower = word.toLowerCase();\n if (lower === \"ui\") return \"UI\";\n if (lower === \"ux\") return \"UX\";\n if (lower === \"api\") return \"API\";\n if (word === \"\") return \"\";\n const chars = [...word];\n const first = chars[0] as string;\n return first.toUpperCase() + chars.slice(1).join(\"\");\n}\n\n/** Titlecase an agent name across '-' and whitespace boundaries (port of titlecase_agent). */\nfunction titlecaseAgent(name: string): string {\n if (name === \"\") return \"\";\n return name\n .split(\"-\")\n .flatMap((part) => part.split(/\\s+/))\n .filter((w) => w !== \"\")\n .map(titlecaseWord)\n .join(\" \");\n}\n\n/** Port of normalize_agent_name. */\nexport function normalizeAgentName(agent: string): string {\n const cleaned = stripZeroWidthChars(agent);\n const trimmed = cleaned.trim();\n const stripped = stripAgentPrefix(trimmed);\n const canonical = canonicalizeAgentName(stripped);\n const agentLower = canonical.toLowerCase();\n\n if (agentLower.includes(\"plan\")) {\n if (agentLower.includes(\"omo\") || agentLower.includes(\"sisyphus\")) {\n return \"Planner-Sisyphus\";\n }\n return titlecaseAgent(canonical);\n }\n if (agentLower === \"omo\" || agentLower === \"sisyphus\") return \"Sisyphus\";\n if (agentLower === \"orchestrator-sisyphus\") return \"Atlas\";\n return titlecaseAgent(canonical);\n}\n\n/** Port of normalize_oh_my_opencode_agent_name (returns undefined when no match). */\nfunction normalizeOhMyOpencodeAgentName(agentLower: string): string | undefined {\n switch (agentLower) {\n case \"sisyphus (ultraworker)\":\n case \"sisyphus - ultraworker\":\n case \"sisyphus ultraworker\":\n case \"sisyphus\":\n return \"Sisyphus\";\n case \"hephaestus (deep agent)\":\n case \"hephaestus - deep agent\":\n case \"hephaestus deep agent\":\n case \"hephaestus\":\n return \"Hephaestus\";\n case \"prometheus (plan builder)\":\n case \"prometheus - plan builder\":\n case \"prometheus plan builder\":\n case \"prometheus (planner)\":\n case \"prometheus\":\n return \"Prometheus\";\n case \"atlas (plan executor)\":\n case \"atlas - plan executor\":\n case \"atlas plan executor\":\n case \"atlas\":\n return \"Atlas\";\n case \"metis (plan consultant)\":\n case \"metis - plan consultant\":\n case \"metis plan consultant\":\n case \"metis\":\n return \"Metis\";\n case \"momus (plan critic)\":\n case \"momus - plan critic\":\n case \"momus plan critic\":\n case \"momus (plan reviewer)\":\n case \"momus\":\n return \"Momus\";\n case \"orchestrator-sisyphus\":\n return \"Atlas\";\n case \"sisyphus-junior\":\n return \"Sisyphus-Junior\";\n case \"planner-sisyphus\":\n return \"Planner-Sisyphus\";\n default:\n return undefined;\n }\n}\n\n/**\n * Port of normalize_opencode_agent_name (sessions/mod.rs:88-154). Applies the\n * shared cleaning pipeline, then the oh-my-opencode agent table, then falls back\n * to normalizeAgentName. Used by the opencode reader BEFORE its dedup fingerprint\n * so fork copies collapse (matching opencode.rs:272).\n */\nexport function normalizeOpencodeAgentName(agent: string): string {\n const cleaned = stripZeroWidthChars(agent);\n const trimmed = cleaned.trim();\n const stripped = stripAgentPrefix(trimmed);\n const canonical = canonicalizeAgentName(stripped);\n const agentLower = canonical.toLowerCase();\n\n const ohMy = normalizeOhMyOpencodeAgentName(agentLower);\n if (ohMy !== undefined) return ohMy;\n\n return normalizeAgentName(canonical);\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Workspace key / label (port of sessions/mod.rs)\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * Normalize an arbitrary cwd/dir string to a stable workspace key. Port of\n * normalize_workspace_key: backslashes → forward slashes, collapse repeated\n * slashes, strip a trailing slash (preserving a leading `//` UNC prefix).\n * Returns undefined for an empty input.\n */\nexport function normalizeWorkspaceKey(raw: string): string | undefined {\n const trimmed = raw.trim();\n if (trimmed === \"\") return undefined;\n\n const preserveUnc = trimmed.startsWith(\"\\\\\\\\\") || trimmed.startsWith(\"//\");\n let normalized = trimmed.replace(/\\\\/g, \"/\");\n\n if (preserveUnc) {\n let body = normalized.replace(/^\\/+/, \"\");\n while (body.includes(\"//\")) body = body.replace(/\\/\\//g, \"/\");\n normalized = `//${body}`;\n } else {\n while (normalized.includes(\"//\")) normalized = normalized.replace(/\\/\\//g, \"/\");\n }\n\n const minLen = preserveUnc ? 2 : 1;\n if (normalized.length > minLen) {\n normalized = normalized.replace(/\\/+$/, \"\");\n }\n\n return normalized === \"\" ? undefined : normalized;\n}\n\n/**\n * Human-readable label from a workspace key: the last non-empty path segment.\n * Port of workspace_label_from_key. Returns undefined when no segment remains.\n */\nexport function workspaceLabelFromKey(key: string): string | undefined {\n const segments = key.split(\"/\");\n for (let i = segments.length - 1; i >= 0; i--) {\n const seg = segments[i];\n if (seg !== undefined && seg !== \"\") return seg;\n }\n return undefined;\n}\n\n/**\n * Convert a cwd/dir string to both its normalized key and label in one call.\n * Both fields are undefined when the input does not yield a usable key.\n */\nexport function workspaceFromPath(raw: string): {\n projectKey?: string;\n projectLabel?: string;\n} {\n const key = normalizeWorkspaceKey(raw);\n if (key === undefined) return {};\n const label = workspaceLabelFromKey(key);\n return label === undefined ? { projectKey: key } : { projectKey: key, projectLabel: label };\n}\n","/**\n * usage/aggregate — token math, cross-source de-dup, and group-by rollups.\n *\n * Ports tokscale's shared aggregation model (docs/research/usage-shared-model.md\n * §4–5): a TokenBreakdown total is the sum of all five dimensions; dedup keeps\n * the FIRST occurrence per dedupKey in insertion order (un-keyed records always\n * pass through); group-by folds records into UsageSummary rows summing tokens /\n * cost / messages, counting distinct sessions, tracking the worst confidence and\n * the latest timestamp, sorted by total desc.\n */\n\nimport type {\n TokenBreakdown,\n UsageConfidence,\n UsageGroupBy,\n UsageRecord,\n UsageSummary,\n} from \"./types.js\";\nimport { normalizeModelForGrouping } from \"./normalize.js\";\n\n// ─────────────────────────────────────────────────────────────────────────\n// Token helpers\n// ─────────────────────────────────────────────────────────────────────────\n\n/** A fresh all-zero TokenBreakdown. */\nexport function emptyTokens(): TokenBreakdown {\n return { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, reasoning: 0 };\n}\n\n/** Element-wise sum of two breakdowns into a NEW object (operands untouched). */\nexport function addTokens(a: TokenBreakdown, b: TokenBreakdown): TokenBreakdown {\n return {\n input: a.input + b.input,\n output: a.output + b.output,\n cacheRead: a.cacheRead + b.cacheRead,\n cacheWrite: a.cacheWrite + b.cacheWrite,\n reasoning: a.reasoning + b.reasoning,\n };\n}\n\n/** Scalar total across all five token dimensions. */\nexport function sumTokens(t: TokenBreakdown): number {\n return t.input + t.output + t.cacheRead + t.cacheWrite + t.reasoning;\n}\n\n/** Accumulate `b` into `a` in place (used inside the rollup fold). */\nfunction addTokensInPlace(a: TokenBreakdown, b: TokenBreakdown): void {\n a.input += b.input;\n a.output += b.output;\n a.cacheRead += b.cacheRead;\n a.cacheWrite += b.cacheWrite;\n a.reasoning += b.reasoning;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Confidence\n// ─────────────────────────────────────────────────────────────────────────\n\n/** Least-trustworthy (0) → most-trustworthy. host-estimated is the weaker. */\nconst CONFIDENCE_RANK: Record<UsageConfidence, number> = {\n \"host-estimated\": 0,\n \"host-reported\": 1,\n};\n\n/** The worse (least-confident) of two provenance labels. */\nexport function worstConfidence(a: UsageConfidence, b: UsageConfidence): UsageConfidence {\n return CONFIDENCE_RANK[b] < CONFIDENCE_RANK[a] ? b : a;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// De-duplication\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * Cross-source de-dup. Keep the FIRST record seen per `dedupKey` in insertion\n * order; records without a dedupKey always pass through (the per-reader dedup is\n * the real safeguard — this is the global backstop, per shared-model §5 and\n * design §6). Deterministic: same input order → same output.\n */\nexport function dedupe(records: UsageRecord[]): UsageRecord[] {\n const seen = new Set<string>();\n const out: UsageRecord[] = [];\n for (const rec of records) {\n const key = rec.dedupKey;\n if (key === undefined || key === \"\") {\n out.push(rec); // un-keyed → always include\n continue;\n }\n if (!seen.has(key)) {\n seen.add(key);\n out.push(rec);\n }\n // duplicate key → drop (keep-first)\n }\n return out;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Group-by aggregation\n// ─────────────────────────────────────────────────────────────────────────\n\n/** Local YYYY-MM-DD for an epoch-ms timestamp (matches tokscale's local-tz date). */\nfunction localDay(ts: number): string {\n const d = new Date(ts);\n const y = d.getFullYear();\n const m = `${d.getMonth() + 1}`.padStart(2, \"0\");\n const day = `${d.getDate()}`.padStart(2, \"0\");\n return `${y}-${m}-${day}`;\n}\n\n/** The grouping key value for a record under the requested dimension. */\nfunction groupKey(rec: UsageRecord, by: UsageGroupBy): string {\n switch (by) {\n case \"platform\":\n return rec.platformId;\n case \"project\":\n return rec.projectLabel ?? rec.projectKey ?? \"(no project)\";\n case \"session\":\n return rec.sessionId;\n case \"model\":\n return normalizeModelForGrouping(rec.modelId);\n case \"day\":\n return localDay(rec.ts);\n }\n}\n\ninterface MutableSummary extends UsageSummary {\n /** Distinct session ids folded into this group (counted at finalization). */\n _sessions: Set<string>;\n}\n\n/**\n * Roll records up into {@link UsageSummary} rows by the chosen dimension. Tokens,\n * cost, and message counts sum; `sessions` counts distinct session ids; the row\n * carries the worst confidence and the latest timestamp seen. Sorted by total\n * tokens descending (ties broken by recency).\n */\nexport function aggregateBy(records: UsageRecord[], by: UsageGroupBy): UsageSummary[] {\n const groups = new Map<string, MutableSummary>();\n\n for (const rec of records) {\n const key = groupKey(rec, by);\n let g = groups.get(key);\n if (g === undefined) {\n g = {\n key,\n tokens: emptyTokens(),\n total: 0,\n sessions: 0,\n messages: 0,\n confidence: rec.confidence,\n lastTs: rec.ts,\n _sessions: new Set<string>(),\n };\n groups.set(key, g);\n }\n\n addTokensInPlace(g.tokens, rec.tokens);\n g.messages += rec.messageCount;\n g._sessions.add(rec.sessionId);\n g.confidence = worstConfidence(g.confidence, rec.confidence);\n if (rec.ts > g.lastTs) g.lastTs = rec.ts;\n if (rec.cost !== undefined) g.cost = (g.cost ?? 0) + rec.cost;\n }\n\n const rows: UsageSummary[] = [];\n for (const g of groups.values()) {\n const { _sessions, ...summary } = g;\n summary.total = sumTokens(summary.tokens);\n summary.sessions = _sessions.size;\n rows.push(summary);\n }\n\n rows.sort((a, b) => b.total - a.total || b.lastTs - a.lastTs);\n return rows;\n}\n"],"mappings":"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","names":[]}