@planu/cli 0.68.0 → 0.70.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 (63) hide show
  1. package/dist/config/discovery-queries.json +267 -0
  2. package/dist/config/license-plans.json +1 -0
  3. package/dist/engine/discovery/registry-search.d.ts +4 -0
  4. package/dist/engine/discovery/registry-search.d.ts.map +1 -0
  5. package/dist/engine/discovery/registry-search.js +82 -0
  6. package/dist/engine/discovery/registry-search.js.map +1 -0
  7. package/dist/engine/discovery/result-ranker.d.ts +8 -0
  8. package/dist/engine/discovery/result-ranker.d.ts.map +1 -0
  9. package/dist/engine/discovery/result-ranker.js +46 -0
  10. package/dist/engine/discovery/result-ranker.js.map +1 -0
  11. package/dist/engine/discovery/tech-discovery.d.ts +4 -0
  12. package/dist/engine/discovery/tech-discovery.d.ts.map +1 -0
  13. package/dist/engine/discovery/tech-discovery.js +83 -0
  14. package/dist/engine/discovery/tech-discovery.js.map +1 -0
  15. package/dist/engine/token-optimizer/analytics.d.ts +10 -0
  16. package/dist/engine/token-optimizer/analytics.d.ts.map +1 -0
  17. package/dist/engine/token-optimizer/analytics.js +362 -0
  18. package/dist/engine/token-optimizer/analytics.js.map +1 -0
  19. package/dist/engine/token-optimizer/index.d.ts +2 -0
  20. package/dist/engine/token-optimizer/index.d.ts.map +1 -1
  21. package/dist/engine/token-optimizer/index.js +1 -0
  22. package/dist/engine/token-optimizer/index.js.map +1 -1
  23. package/dist/engine/token-optimizer/reconciler.d.ts +22 -0
  24. package/dist/engine/token-optimizer/reconciler.d.ts.map +1 -0
  25. package/dist/engine/token-optimizer/reconciler.js +215 -0
  26. package/dist/engine/token-optimizer/reconciler.js.map +1 -0
  27. package/dist/storage/token-ledger-store.d.ts +26 -0
  28. package/dist/storage/token-ledger-store.d.ts.map +1 -0
  29. package/dist/storage/token-ledger-store.js +354 -0
  30. package/dist/storage/token-ledger-store.js.map +1 -0
  31. package/dist/tools/register-token-tools.d.ts.map +1 -1
  32. package/dist/tools/register-token-tools.js +21 -0
  33. package/dist/tools/register-token-tools.js.map +1 -1
  34. package/dist/tools/schemas/index.d.ts +1 -0
  35. package/dist/tools/schemas/index.d.ts.map +1 -1
  36. package/dist/tools/schemas/index.js +1 -0
  37. package/dist/tools/schemas/index.js.map +1 -1
  38. package/dist/tools/schemas/token-intelligence.d.ts +21 -0
  39. package/dist/tools/schemas/token-intelligence.d.ts.map +1 -0
  40. package/dist/tools/schemas/token-intelligence.js +18 -0
  41. package/dist/tools/schemas/token-intelligence.js.map +1 -0
  42. package/dist/tools/suggest-tooling/handler.d.ts.map +1 -1
  43. package/dist/tools/suggest-tooling/handler.js +39 -1
  44. package/dist/tools/suggest-tooling/handler.js.map +1 -1
  45. package/dist/tools/token-intelligence-handler.d.ts +8 -0
  46. package/dist/tools/token-intelligence-handler.d.ts.map +1 -0
  47. package/dist/tools/token-intelligence-handler.js +413 -0
  48. package/dist/tools/token-intelligence-handler.js.map +1 -0
  49. package/dist/types/discovery.d.ts +22 -0
  50. package/dist/types/discovery.d.ts.map +1 -0
  51. package/dist/types/discovery.js +3 -0
  52. package/dist/types/discovery.js.map +1 -0
  53. package/dist/types/index.d.ts +2 -0
  54. package/dist/types/index.d.ts.map +1 -1
  55. package/dist/types/index.js +2 -0
  56. package/dist/types/index.js.map +1 -1
  57. package/dist/types/token-ledger.d.ts +195 -0
  58. package/dist/types/token-ledger.d.ts.map +1 -0
  59. package/dist/types/token-ledger.js +3 -0
  60. package/dist/types/token-ledger.js.map +1 -0
  61. package/package.json +1 -1
  62. package/src/config/discovery-queries.json +267 -0
  63. package/src/config/license-plans.json +1 -0
@@ -0,0 +1,362 @@
1
+ // Cost Analytics Engine (SPEC-182)
2
+ // Aggregations, trends, anomaly detection, and projections from token ledger data
3
+ // ---------------------------------------------------------------------------
4
+ // Constants
5
+ // ---------------------------------------------------------------------------
6
+ const TREND_WINDOW_DAYS = 7;
7
+ const TREND_STABLE_THRESHOLD = 10; // percent
8
+ const SPIKE_MULTIPLIER = 2;
9
+ const SPIKE_CRITICAL_MULTIPLIER = 3;
10
+ const SPIKE_WINDOW_DAYS = 14;
11
+ const HIGH_COST_TOOL_PERCENT = 0.5;
12
+ const DEFAULT_TOP_CONSUMERS_LIMIT = 10;
13
+ // ---------------------------------------------------------------------------
14
+ // Helpers
15
+ // ---------------------------------------------------------------------------
16
+ function isoToDate(timestamp) {
17
+ return timestamp.slice(0, 10); // YYYY-MM-DD
18
+ }
19
+ function modeOf(values) {
20
+ if (values.length === 0) {
21
+ return '';
22
+ }
23
+ const freq = new Map();
24
+ for (const v of values) {
25
+ freq.set(v, (freq.get(v) ?? 0) + 1);
26
+ }
27
+ let topKey = '';
28
+ let topCount = 0;
29
+ for (const [k, c] of freq) {
30
+ if (c > topCount) {
31
+ topCount = c;
32
+ topKey = k;
33
+ }
34
+ }
35
+ return topKey;
36
+ }
37
+ function average(nums) {
38
+ if (nums.length === 0) {
39
+ return 0;
40
+ }
41
+ return nums.reduce((s, n) => s + n, 0) / nums.length;
42
+ }
43
+ function daysInCalendarMonth(year, month) {
44
+ return new Date(year, month, 0).getDate(); // month is 1-based here
45
+ }
46
+ // ---------------------------------------------------------------------------
47
+ // aggregateEntries
48
+ // ---------------------------------------------------------------------------
49
+ function computeEstimationAccuracy(estimatedCount, actualForEstimated, estimatedSum) {
50
+ if (estimatedCount === 0 || actualForEstimated === 0) {
51
+ return { estimationAccuracy: 100, overEstimationPercent: 0 };
52
+ }
53
+ return {
54
+ estimationAccuracy: Math.min(100, (actualForEstimated / estimatedSum) * 100),
55
+ overEstimationPercent: ((estimatedSum - actualForEstimated) / actualForEstimated) * 100,
56
+ };
57
+ }
58
+ function buildEmptyAggregation() {
59
+ return {
60
+ totalTokens: 0,
61
+ totalInputTokens: 0,
62
+ totalOutputTokens: 0,
63
+ totalCostUsd: 0,
64
+ totalEstimatedCostUsd: 0,
65
+ entryCount: 0,
66
+ uniqueTools: 0,
67
+ uniqueModels: 0,
68
+ uniqueSessions: 0,
69
+ avgTokensPerCall: 0,
70
+ avgCostPerCall: 0,
71
+ tokensByTool: {},
72
+ costByTool: {},
73
+ tokensByModel: {},
74
+ costByModel: {},
75
+ estimationAccuracy: 100,
76
+ overEstimationPercent: 0,
77
+ };
78
+ }
79
+ function incrementRecord(rec, key, val) {
80
+ return { ...rec, [key]: (rec[key] ?? 0) + val };
81
+ }
82
+ export function aggregateEntries(entries) {
83
+ if (entries.length === 0) {
84
+ return buildEmptyAggregation();
85
+ }
86
+ const result = entries.reduce((a, e) => {
87
+ const hasEst = e.estimatedTokens !== undefined;
88
+ return {
89
+ tT: a.tT + e.totalTokens,
90
+ tI: a.tI + e.inputTokens,
91
+ tO: a.tO + e.outputTokens,
92
+ tC: a.tC + e.costUsd,
93
+ tE: a.tE + (e.estimatedCostUsd ?? 0),
94
+ eS: a.eS + (hasEst ? (e.estimatedTokens ?? 0) : 0),
95
+ aE: a.aE + (hasEst ? e.totalTokens : 0),
96
+ eC: a.eC + (hasEst ? 1 : 0),
97
+ byTool: incrementRecord(a.byTool, e.toolName, e.totalTokens),
98
+ cByTool: incrementRecord(a.cByTool, e.toolName, e.costUsd),
99
+ byModel: incrementRecord(a.byModel, e.model, e.totalTokens),
100
+ cByModel: incrementRecord(a.cByModel, e.model, e.costUsd),
101
+ tools: new Set([...a.tools, e.toolName]),
102
+ models: new Set([...a.models, e.model]),
103
+ sessions: new Set([...a.sessions, e.sessionId]),
104
+ };
105
+ }, {
106
+ tT: 0,
107
+ tI: 0,
108
+ tO: 0,
109
+ tC: 0,
110
+ tE: 0,
111
+ eS: 0,
112
+ aE: 0,
113
+ eC: 0,
114
+ byTool: {},
115
+ cByTool: {},
116
+ byModel: {},
117
+ cByModel: {},
118
+ tools: new Set(),
119
+ models: new Set(),
120
+ sessions: new Set(),
121
+ });
122
+ const { estimationAccuracy, overEstimationPercent } = computeEstimationAccuracy(result.eC, result.aE, result.eS);
123
+ const count = entries.length;
124
+ return {
125
+ totalTokens: result.tT,
126
+ totalInputTokens: result.tI,
127
+ totalOutputTokens: result.tO,
128
+ totalCostUsd: result.tC,
129
+ totalEstimatedCostUsd: result.tE,
130
+ entryCount: count,
131
+ uniqueTools: result.tools.size,
132
+ uniqueModels: result.models.size,
133
+ uniqueSessions: result.sessions.size,
134
+ avgTokensPerCall: result.tT / count,
135
+ avgCostPerCall: result.tC / count,
136
+ tokensByTool: result.byTool,
137
+ costByTool: result.cByTool,
138
+ tokensByModel: result.byModel,
139
+ costByModel: result.cByModel,
140
+ estimationAccuracy,
141
+ overEstimationPercent,
142
+ };
143
+ }
144
+ // ---------------------------------------------------------------------------
145
+ // computeDailyBreakdown
146
+ // ---------------------------------------------------------------------------
147
+ export function computeDailyBreakdown(entries) {
148
+ const byDate = new Map();
149
+ for (const e of entries) {
150
+ const date = isoToDate(e.timestamp);
151
+ const existing = byDate.get(date) ?? { tokens: 0, cost: 0, tools: [], models: [], count: 0 };
152
+ existing.tokens += e.totalTokens;
153
+ existing.cost += e.costUsd;
154
+ existing.tools.push(e.toolName);
155
+ existing.models.push(e.model);
156
+ existing.count += 1;
157
+ byDate.set(date, existing);
158
+ }
159
+ return Array.from(byDate.entries())
160
+ .sort(([a], [b]) => a.localeCompare(b))
161
+ .map(([date, data]) => ({
162
+ date,
163
+ totalTokens: data.tokens,
164
+ totalCostUsd: data.cost,
165
+ callCount: data.count,
166
+ topTool: modeOf(data.tools),
167
+ topModel: modeOf(data.models),
168
+ }));
169
+ }
170
+ // ---------------------------------------------------------------------------
171
+ // detectTrend
172
+ // ---------------------------------------------------------------------------
173
+ export function detectTrend(dailyBreakdown) {
174
+ if (dailyBreakdown.length < TREND_WINDOW_DAYS * 2) {
175
+ return {
176
+ direction: 'stable',
177
+ changePercent: 0,
178
+ description: 'Insufficient data to determine trend (need 14+ days).',
179
+ };
180
+ }
181
+ const sorted = [...dailyBreakdown].sort((a, b) => a.date.localeCompare(b.date));
182
+ const last14 = sorted.slice(-14);
183
+ const prev7 = last14.slice(0, 7).map((d) => d.totalTokens);
184
+ const curr7 = last14.slice(7).map((d) => d.totalTokens);
185
+ const prevAvg = average(prev7);
186
+ const currAvg = average(curr7);
187
+ if (prevAvg === 0) {
188
+ return { direction: 'stable', changePercent: 0, description: 'No previous data to compare.' };
189
+ }
190
+ const changePercent = ((currAvg - prevAvg) / prevAvg) * 100;
191
+ let direction;
192
+ if (changePercent > TREND_STABLE_THRESHOLD) {
193
+ direction = 'increasing';
194
+ }
195
+ else if (changePercent < -TREND_STABLE_THRESHOLD) {
196
+ direction = 'decreasing';
197
+ }
198
+ else {
199
+ direction = 'stable';
200
+ }
201
+ const absPercent = Math.abs(changePercent).toFixed(0);
202
+ const description = direction === 'stable'
203
+ ? `Token usage is stable (${absPercent}% change week over week).`
204
+ : `Token usage is ${direction} ${absPercent}% week over week.`;
205
+ return { direction, changePercent: parseFloat(changePercent.toFixed(2)), description };
206
+ }
207
+ // ---------------------------------------------------------------------------
208
+ // detectAnomalies
209
+ // ---------------------------------------------------------------------------
210
+ export function detectAnomalies(entries, dailyBreakdown) {
211
+ const anomalies = [];
212
+ if (dailyBreakdown.length === 0) {
213
+ return anomalies;
214
+ }
215
+ const sorted = [...dailyBreakdown].sort((a, b) => a.date.localeCompare(b.date));
216
+ for (let i = 0; i < sorted.length; i++) {
217
+ const day = sorted[i];
218
+ if (day === undefined) {
219
+ continue;
220
+ }
221
+ // --- Spike detection ---
222
+ const windowStart = Math.max(0, i - SPIKE_WINDOW_DAYS);
223
+ const windowDays = sorted.slice(windowStart, i);
224
+ if (windowDays.length > 0) {
225
+ const windowAvg = average(windowDays.map((d) => d.totalTokens));
226
+ if (windowAvg > 0 && day.totalTokens > windowAvg * SPIKE_MULTIPLIER) {
227
+ const severity = day.totalTokens > windowAvg * SPIKE_CRITICAL_MULTIPLIER ? 'critical' : 'warning';
228
+ anomalies.push({
229
+ date: day.date,
230
+ type: 'spike',
231
+ severity,
232
+ description: `Token spike on ${day.date}: ${day.totalTokens.toLocaleString()} tokens vs ${Math.round(windowAvg).toLocaleString()} avg.`,
233
+ value: day.totalTokens,
234
+ threshold: windowAvg * SPIKE_MULTIPLIER,
235
+ });
236
+ }
237
+ }
238
+ // --- High cost tool detection ---
239
+ const dayEntries = entries.filter((e) => isoToDate(e.timestamp) === day.date);
240
+ if (day.totalCostUsd > 0 && dayEntries.length > 0) {
241
+ const costByTool = new Map();
242
+ for (const e of dayEntries) {
243
+ costByTool.set(e.toolName, (costByTool.get(e.toolName) ?? 0) + e.costUsd);
244
+ }
245
+ for (const [tool, cost] of costByTool) {
246
+ const fraction = cost / day.totalCostUsd;
247
+ if (fraction > HIGH_COST_TOOL_PERCENT) {
248
+ anomalies.push({
249
+ date: day.date,
250
+ type: 'high_cost_tool',
251
+ severity: 'warning',
252
+ description: `Tool "${tool}" consumed ${(fraction * 100).toFixed(0)}% of daily cost on ${day.date}.`,
253
+ value: cost,
254
+ threshold: day.totalCostUsd * HIGH_COST_TOOL_PERCENT,
255
+ });
256
+ }
257
+ }
258
+ }
259
+ }
260
+ return anomalies;
261
+ }
262
+ // ---------------------------------------------------------------------------
263
+ // computeBudgetStatus
264
+ // ---------------------------------------------------------------------------
265
+ export function computeBudgetStatus(entries, config) {
266
+ const now = new Date();
267
+ const year = now.getFullYear();
268
+ const month = now.getMonth() + 1; // 1-based
269
+ const daysInMonth = daysInCalendarMonth(year, month);
270
+ const daysElapsed = now.getDate();
271
+ // Current month filter
272
+ const monthPrefix = `${year}-${String(month).padStart(2, '0')}`;
273
+ const monthEntries = entries.filter((e) => e.timestamp.startsWith(monthPrefix));
274
+ const spentUsd = monthEntries.reduce((sum, e) => sum + e.costUsd, 0);
275
+ const dailyAvgUsd = daysElapsed > 0 ? spentUsd / daysElapsed : 0;
276
+ const projectedMonthlyUsd = dailyAvgUsd * daysInMonth;
277
+ const limit = config.monthlyLimitUsd ?? null;
278
+ const remainingUsd = limit !== null ? Math.max(0, limit - spentUsd) : null;
279
+ const percentUsed = limit !== null && limit > 0 ? (spentUsd / limit) * 100 : null;
280
+ const warningFraction = config.warningThresholdPercent / 100;
281
+ const criticalFraction = config.criticalThresholdPercent / 100;
282
+ const isWarning = limit !== null && percentUsed !== null && percentUsed / 100 >= warningFraction;
283
+ const isCritical = limit !== null && percentUsed !== null && percentUsed / 100 >= criticalFraction;
284
+ const isOverBudget = limit !== null && spentUsd > limit;
285
+ // Projected exhaustion day
286
+ let projectedExhaustionDay = null;
287
+ if (limit !== null && dailyAvgUsd > 0 && projectedMonthlyUsd > limit) {
288
+ const daysUntilExhaustion = limit / dailyAvgUsd;
289
+ projectedExhaustionDay = Math.min(daysInMonth, Math.ceil(daysUntilExhaustion));
290
+ }
291
+ return {
292
+ monthlyLimitUsd: limit,
293
+ spentUsd,
294
+ remainingUsd,
295
+ percentUsed: percentUsed !== null ? parseFloat(percentUsed.toFixed(2)) : null,
296
+ projectedMonthlyUsd: parseFloat(projectedMonthlyUsd.toFixed(4)),
297
+ projectedExhaustionDay,
298
+ isWarning,
299
+ isCritical,
300
+ isOverBudget,
301
+ daysInMonth,
302
+ daysElapsed,
303
+ dailyAvgUsd: parseFloat(dailyAvgUsd.toFixed(6)),
304
+ };
305
+ }
306
+ // ---------------------------------------------------------------------------
307
+ // getTopConsumers
308
+ // ---------------------------------------------------------------------------
309
+ export function getTopConsumers(entries, limit = DEFAULT_TOP_CONSUMERS_LIMIT) {
310
+ const totalTokens = entries.reduce((s, e) => s + e.totalTokens, 0);
311
+ const byTool = new Map();
312
+ for (const e of entries) {
313
+ const existing = byTool.get(e.toolName) ?? { tokens: 0, cost: 0, count: 0 };
314
+ existing.tokens += e.totalTokens;
315
+ existing.cost += e.costUsd;
316
+ existing.count += 1;
317
+ byTool.set(e.toolName, existing);
318
+ }
319
+ return Array.from(byTool.entries())
320
+ .map(([name, data]) => ({
321
+ name,
322
+ tokens: data.tokens,
323
+ costUsd: data.cost,
324
+ percentage: totalTokens > 0 ? parseFloat(((data.tokens / totalTokens) * 100).toFixed(2)) : 0,
325
+ callCount: data.count,
326
+ }))
327
+ .sort((a, b) => b.tokens - a.tokens)
328
+ .slice(0, limit);
329
+ }
330
+ // ---------------------------------------------------------------------------
331
+ // computeOptimizationSavings
332
+ // ---------------------------------------------------------------------------
333
+ export function computeOptimizationSavings(entries) {
334
+ let tokensSavedByCache = 0;
335
+ let tokensSavedByOptimization = 0;
336
+ let totalCost = 0;
337
+ for (const e of entries) {
338
+ if (e.cached) {
339
+ tokensSavedByCache += e.totalTokens;
340
+ }
341
+ if (e.optimizationSavings !== undefined && e.optimizationSavings > 0) {
342
+ tokensSavedByOptimization += e.optimizationSavings;
343
+ }
344
+ totalCost += e.costUsd;
345
+ }
346
+ const totalTokensSaved = tokensSavedByCache + tokensSavedByOptimization;
347
+ // Estimate cost saved: use average cost per token across all entries
348
+ const totalActualTokens = entries.reduce((s, e) => s + e.totalTokens, 0);
349
+ const avgCostPerToken = totalActualTokens > 0 ? totalCost / totalActualTokens : 0;
350
+ const costSavedUsd = totalTokensSaved * avgCostPerToken;
351
+ // savingsPercent relative to (total consumed + what was saved)
352
+ const totalWithSavings = totalActualTokens + totalTokensSaved;
353
+ const savingsPercent = totalWithSavings > 0 ? parseFloat(((totalTokensSaved / totalWithSavings) * 100).toFixed(2)) : 0;
354
+ return {
355
+ tokensSavedByCache,
356
+ tokensSavedByOptimization,
357
+ totalTokensSaved,
358
+ costSavedUsd: parseFloat(costSavedUsd.toFixed(6)),
359
+ savingsPercent,
360
+ };
361
+ }
362
+ //# sourceMappingURL=analytics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analytics.js","sourceRoot":"","sources":["../../../src/engine/token-optimizer/analytics.ts"],"names":[],"mappings":"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"}
@@ -7,4 +7,6 @@ export { IncrementalTracker } from './incremental.js';
7
7
  export { BudgetManager } from './budget.js';
8
8
  export { OptimizationReporter } from './reporter.js';
9
9
  export { TokenOptimizer } from './optimizer.js';
10
+ export { aggregateEntries, computeDailyBreakdown, detectTrend, detectAnomalies, computeBudgetStatus, getTopConsumers, computeOptimizationSavings, } from './analytics.js';
11
+ export type { DailyBreakdown, TrendResult, Anomaly, TopConsumer, OptimizationSavings, } from './analytics.js';
10
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/engine/token-optimizer/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAC7E,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAClF,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/engine/token-optimizer/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAC7E,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAClF,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EACrB,WAAW,EACX,eAAe,EACf,mBAAmB,EACnB,eAAe,EACf,0BAA0B,GAC3B,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,cAAc,EACd,WAAW,EACX,OAAO,EACP,WAAW,EACX,mBAAmB,GACpB,MAAM,gBAAgB,CAAC"}
@@ -8,4 +8,5 @@ export { IncrementalTracker } from './incremental.js';
8
8
  export { BudgetManager } from './budget.js';
9
9
  export { OptimizationReporter } from './reporter.js';
10
10
  export { TokenOptimizer } from './optimizer.js';
11
+ export { aggregateEntries, computeDailyBreakdown, detectTrend, detectAnomalies, computeBudgetStatus, getTopConsumers, computeOptimizationSavings, } from './analytics.js';
11
12
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/engine/token-optimizer/index.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAC7E,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAClF,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/engine/token-optimizer/index.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAC7E,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAClF,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EACrB,WAAW,EACX,eAAe,EACf,mBAAmB,EACnB,eAAe,EACf,0BAA0B,GAC3B,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { ReconciliationInput, ReconciliationResult, CalibrationData } from '../../types/index.js';
2
+ export type { ReconciliationInput, ReconciliationResult, CalibrationData };
3
+ /**
4
+ * Pure reconciliation: compares a pre-implementation estimate against actual
5
+ * ledger entries to produce deviation, accuracy, and calibration data.
6
+ */
7
+ export declare function reconcile(input: ReconciliationInput): ReconciliationResult;
8
+ /**
9
+ * Formats a ReconciliationResult as a markdown report with visual indicators.
10
+ */
11
+ export declare function formatReconciliation(result: ReconciliationResult): string;
12
+ /**
13
+ * Extracts calibration data points from multiple reconciliation results.
14
+ */
15
+ export declare function buildCalibrationData(results: ReconciliationResult[]): CalibrationData[];
16
+ /**
17
+ * Computes a weighted average calibration factor from historical data.
18
+ * Recent entries (second half of the array) are weighted 2x.
19
+ * Returns the factor to multiply future estimates by (>1 = need to increase estimates).
20
+ */
21
+ export declare function suggestCalibrationFactor(calibrations: CalibrationData[]): number;
22
+ //# sourceMappingURL=reconciler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconciler.d.ts","sourceRoot":"","sources":["../../../src/engine/token-optimizer/reconciler.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAEV,mBAAmB,EACnB,oBAAoB,EACpB,eAAe,EAChB,MAAM,kBAAkB,CAAC;AAE1B,YAAY,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,eAAe,EAAE,CAAC;AAsI3E;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,mBAAmB,GAAG,oBAAoB,CAmC1E;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,CA4CzE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,oBAAoB,EAAE,GAAG,eAAe,EAAE,CAQvF;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,YAAY,EAAE,eAAe,EAAE,GAAG,MAAM,CAgBhF"}
@@ -0,0 +1,215 @@
1
+ // Estimation Reconciler (SPEC-182)
2
+ // Closes the loop: estimate_ai_cost prediction vs actual token usage from ledger
3
+ // ---------------------------------------------------------------------------
4
+ // Constants
5
+ // ---------------------------------------------------------------------------
6
+ const ACCURATE_THRESHOLD_PCT = 15;
7
+ const GRADE_EXCELLENT = 90;
8
+ const GRADE_GOOD = 75;
9
+ const GRADE_FAIR = 50;
10
+ const CALIBRATION_RECENT_WEIGHT = 2;
11
+ // ---------------------------------------------------------------------------
12
+ // Core functions
13
+ // ---------------------------------------------------------------------------
14
+ /**
15
+ * Aggregates ledger entries into actual token/cost totals.
16
+ */
17
+ function aggregateEntries(entries) {
18
+ if (entries.length === 0) {
19
+ return {
20
+ inputTokens: 0,
21
+ outputTokens: 0,
22
+ totalTokens: 0,
23
+ costUsd: 0,
24
+ models: [],
25
+ callCount: 0,
26
+ };
27
+ }
28
+ let inputTokens = 0;
29
+ let outputTokens = 0;
30
+ let costUsd = 0;
31
+ const modelSet = new Set();
32
+ for (const entry of entries) {
33
+ inputTokens += entry.inputTokens;
34
+ outputTokens += entry.outputTokens;
35
+ costUsd += entry.costUsd;
36
+ modelSet.add(entry.model);
37
+ }
38
+ return {
39
+ inputTokens,
40
+ outputTokens,
41
+ totalTokens: inputTokens + outputTokens,
42
+ costUsd,
43
+ models: [...modelSet],
44
+ callCount: entries.length,
45
+ };
46
+ }
47
+ /**
48
+ * Computes deviation percent: (estimated - actual) / actual * 100.
49
+ * Returns 0 when actual is 0 to avoid division by zero.
50
+ */
51
+ function computeDeviationPercent(estimated, actual) {
52
+ if (actual === 0) {
53
+ return 0;
54
+ }
55
+ return ((estimated - actual) / actual) * 100;
56
+ }
57
+ /**
58
+ * Converts deviation percent to accuracy: max(0, 100 - abs(deviation)).
59
+ */
60
+ function deviationToAccuracy(deviationPct) {
61
+ return Math.max(0, 100 - Math.abs(deviationPct));
62
+ }
63
+ /**
64
+ * Maps accuracy value to a grade label.
65
+ */
66
+ function gradeAccuracy(accuracy) {
67
+ if (accuracy >= GRADE_EXCELLENT) {
68
+ return 'excellent';
69
+ }
70
+ if (accuracy >= GRADE_GOOD) {
71
+ return 'good';
72
+ }
73
+ if (accuracy >= GRADE_FAIR) {
74
+ return 'fair';
75
+ }
76
+ return 'poor';
77
+ }
78
+ /**
79
+ * Maps deviation percent to a direction label.
80
+ */
81
+ function computeDirection(deviationPct) {
82
+ if (Math.abs(deviationPct) <= ACCURATE_THRESHOLD_PCT) {
83
+ return 'accurate';
84
+ }
85
+ return deviationPct > 0 ? 'over' : 'under';
86
+ }
87
+ /**
88
+ * Generates a human-readable calibration suggestion from deviation info.
89
+ */
90
+ function buildCalibrationSuggestion(direction, tokenDeviationPct) {
91
+ if (direction === 'accurate') {
92
+ return undefined;
93
+ }
94
+ const absPct = Math.abs(tokenDeviationPct).toFixed(1);
95
+ if (direction === 'over') {
96
+ return (`Estimates are running ${absPct}% high. ` +
97
+ `Consider reducing future token estimates by ~${absPct}% for specs of similar size.`);
98
+ }
99
+ return (`Estimates are running ${absPct}% low. ` +
100
+ `Consider increasing future token estimates by ~${absPct}% for specs of similar size.`);
101
+ }
102
+ // ---------------------------------------------------------------------------
103
+ // Exported functions
104
+ // ---------------------------------------------------------------------------
105
+ /**
106
+ * Pure reconciliation: compares a pre-implementation estimate against actual
107
+ * ledger entries to produce deviation, accuracy, and calibration data.
108
+ */
109
+ export function reconcile(input) {
110
+ const estimatedTotal = input.estimatedInputTokens + input.estimatedOutputTokens;
111
+ const actual = aggregateEntries(input.actualEntries);
112
+ const tokenDeviationPct = computeDeviationPercent(estimatedTotal, actual.totalTokens);
113
+ const costDeviationPct = computeDeviationPercent(input.estimatedCostUsd, actual.costUsd);
114
+ const tokenAccuracy = deviationToAccuracy(tokenDeviationPct);
115
+ const costAccuracy = deviationToAccuracy(costDeviationPct);
116
+ const direction = computeDirection(tokenDeviationPct);
117
+ return {
118
+ specId: input.specId,
119
+ estimated: {
120
+ inputTokens: input.estimatedInputTokens,
121
+ outputTokens: input.estimatedOutputTokens,
122
+ totalTokens: estimatedTotal,
123
+ costUsd: input.estimatedCostUsd,
124
+ model: input.estimatedModel,
125
+ },
126
+ actual,
127
+ deviation: {
128
+ tokenPercent: tokenDeviationPct,
129
+ costPercent: costDeviationPct,
130
+ absoluteTokenDiff: estimatedTotal - actual.totalTokens,
131
+ absoluteCostDiff: input.estimatedCostUsd - actual.costUsd,
132
+ direction,
133
+ },
134
+ accuracy: {
135
+ tokenAccuracy,
136
+ costAccuracy,
137
+ grade: gradeAccuracy(tokenAccuracy),
138
+ },
139
+ calibrationSuggestion: buildCalibrationSuggestion(direction, tokenDeviationPct),
140
+ };
141
+ }
142
+ /**
143
+ * Formats a ReconciliationResult as a markdown report with visual indicators.
144
+ */
145
+ export function formatReconciliation(result) {
146
+ const gradeIcon = result.accuracy.grade === 'excellent'
147
+ ? '✅'
148
+ : result.accuracy.grade === 'good'
149
+ ? '✅'
150
+ : result.accuracy.grade === 'fair'
151
+ ? '⚠️'
152
+ : '❌';
153
+ const directionLabel = result.deviation.direction === 'accurate'
154
+ ? '✅ Within 15% (accurate)'
155
+ : result.deviation.direction === 'over'
156
+ ? `⚠️ Over-estimated by ${result.deviation.tokenPercent.toFixed(1)}%`
157
+ : `❌ Under-estimated by ${Math.abs(result.deviation.tokenPercent).toFixed(1)}%`;
158
+ const lines = [
159
+ `## Estimation Reconciliation — ${result.specId}`,
160
+ '',
161
+ '### Token Summary',
162
+ `| Metric | Estimated | Actual | Deviation |`,
163
+ `|--------|-----------|--------|-----------|`,
164
+ `| Input tokens | ${String(result.estimated.inputTokens)} | ${String(result.actual.inputTokens)} | — |`,
165
+ `| Output tokens | ${String(result.estimated.outputTokens)} | ${String(result.actual.outputTokens)} | — |`,
166
+ `| Total tokens | ${String(result.estimated.totalTokens)} | ${String(result.actual.totalTokens)} | ${result.deviation.tokenPercent.toFixed(1)}% |`,
167
+ `| Cost (USD) | $${result.estimated.costUsd.toFixed(6)} | $${result.actual.costUsd.toFixed(6)} | ${result.deviation.costPercent.toFixed(1)}% |`,
168
+ '',
169
+ '### Accuracy',
170
+ `${gradeIcon} Grade: **${result.accuracy.grade.toUpperCase()}**`,
171
+ `- Token accuracy: ${result.accuracy.tokenAccuracy.toFixed(1)}%`,
172
+ `- Cost accuracy: ${result.accuracy.costAccuracy.toFixed(1)}%`,
173
+ `- Direction: ${directionLabel}`,
174
+ '',
175
+ '### Actual Usage Details',
176
+ `- API calls: ${String(result.actual.callCount)}`,
177
+ `- Models used: ${result.actual.models.join(', ') || '—'}`,
178
+ ];
179
+ if (result.calibrationSuggestion) {
180
+ lines.push('', '### Calibration Suggestion', result.calibrationSuggestion);
181
+ }
182
+ return lines.join('\n');
183
+ }
184
+ /**
185
+ * Extracts calibration data points from multiple reconciliation results.
186
+ */
187
+ export function buildCalibrationData(results) {
188
+ return results.map((r) => ({
189
+ specId: r.specId,
190
+ timestamp: new Date().toISOString(),
191
+ estimatedTokens: r.estimated.totalTokens,
192
+ actualTokens: r.actual.totalTokens,
193
+ ratio: r.estimated.totalTokens > 0 ? r.actual.totalTokens / r.estimated.totalTokens : 1,
194
+ }));
195
+ }
196
+ /**
197
+ * Computes a weighted average calibration factor from historical data.
198
+ * Recent entries (second half of the array) are weighted 2x.
199
+ * Returns the factor to multiply future estimates by (>1 = need to increase estimates).
200
+ */
201
+ export function suggestCalibrationFactor(calibrations) {
202
+ if (calibrations.length === 0) {
203
+ return 1;
204
+ }
205
+ const midpoint = Math.floor(calibrations.length / 2);
206
+ let weightedSum = 0;
207
+ let totalWeight = 0;
208
+ for (let i = 0; i < calibrations.length; i++) {
209
+ const weight = i >= midpoint ? CALIBRATION_RECENT_WEIGHT : 1;
210
+ weightedSum += (calibrations[i]?.ratio ?? 1) * weight;
211
+ totalWeight += weight;
212
+ }
213
+ return weightedSum / totalWeight;
214
+ }
215
+ //# sourceMappingURL=reconciler.js.map