olympus-ai 3.3.0 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/README.md +2 -0
- package/dist/__tests__/learning/aggregation.test.d.ts +2 -0
- package/dist/__tests__/learning/aggregation.test.d.ts.map +1 -0
- package/dist/__tests__/learning/aggregation.test.js +282 -0
- package/dist/__tests__/learning/aggregation.test.js.map +1 -0
- package/dist/__tests__/learning/anomaly.test.d.ts +2 -0
- package/dist/__tests__/learning/anomaly.test.d.ts.map +1 -0
- package/dist/__tests__/learning/anomaly.test.js +107 -0
- package/dist/__tests__/learning/anomaly.test.js.map +1 -0
- package/dist/__tests__/learning/baselines.test.d.ts +2 -0
- package/dist/__tests__/learning/baselines.test.d.ts.map +1 -0
- package/dist/__tests__/learning/baselines.test.js +155 -0
- package/dist/__tests__/learning/baselines.test.js.map +1 -0
- package/dist/__tests__/learning/efficiency.test.d.ts +2 -0
- package/dist/__tests__/learning/efficiency.test.d.ts.map +1 -0
- package/dist/__tests__/learning/efficiency.test.js +94 -0
- package/dist/__tests__/learning/efficiency.test.js.map +1 -0
- package/dist/__tests__/learning/feedback-loop-injection.test.d.ts +6 -0
- package/dist/__tests__/learning/feedback-loop-injection.test.d.ts.map +1 -0
- package/dist/__tests__/learning/feedback-loop-injection.test.js +288 -0
- package/dist/__tests__/learning/feedback-loop-injection.test.js.map +1 -0
- package/dist/__tests__/learning/learning-capture-integration.test.d.ts +6 -0
- package/dist/__tests__/learning/learning-capture-integration.test.d.ts.map +1 -0
- package/dist/__tests__/learning/learning-capture-integration.test.js +151 -0
- package/dist/__tests__/learning/learning-capture-integration.test.js.map +1 -0
- package/dist/__tests__/learning/token-metrics.test.d.ts +2 -0
- package/dist/__tests__/learning/token-metrics.test.d.ts.map +1 -0
- package/dist/__tests__/learning/token-metrics.test.js +308 -0
- package/dist/__tests__/learning/token-metrics.test.js.map +1 -0
- package/dist/__tests__/token-tracking-integration.test.d.ts +8 -0
- package/dist/__tests__/token-tracking-integration.test.d.ts.map +1 -0
- package/dist/__tests__/token-tracking-integration.test.js +669 -0
- package/dist/__tests__/token-tracking-integration.test.js.map +1 -0
- package/dist/cli/commands/metrics.d.ts +10 -2
- package/dist/cli/commands/metrics.d.ts.map +1 -1
- package/dist/cli/commands/metrics.js +25 -239
- package/dist/cli/commands/metrics.js.map +1 -1
- package/dist/cli/index.js +196 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +14 -0
- package/dist/config/loader.js.map +1 -1
- package/dist/hooks/registrations/budget-warning.d.ts +8 -0
- package/dist/hooks/registrations/budget-warning.d.ts.map +1 -0
- package/dist/hooks/registrations/budget-warning.js +63 -0
- package/dist/hooks/registrations/budget-warning.js.map +1 -0
- package/dist/hooks/registrations/index.d.ts +3 -2
- package/dist/hooks/registrations/index.d.ts.map +1 -1
- package/dist/hooks/registrations/index.js +5 -3
- package/dist/hooks/registrations/index.js.map +1 -1
- package/dist/hooks/registrations/learning-capture.d.ts +19 -0
- package/dist/hooks/registrations/learning-capture.d.ts.map +1 -0
- package/dist/hooks/registrations/learning-capture.js +220 -0
- package/dist/hooks/registrations/learning-capture.js.map +1 -0
- package/dist/hooks/registrations/session-start.d.ts.map +1 -1
- package/dist/hooks/registrations/session-start.js +13 -0
- package/dist/hooks/registrations/session-start.js.map +1 -1
- package/dist/hooks/registrations/token-metrics.d.ts +10 -2
- package/dist/hooks/registrations/token-metrics.d.ts.map +1 -1
- package/dist/hooks/registrations/token-metrics.js +18 -4
- package/dist/hooks/registrations/token-metrics.js.map +1 -1
- package/dist/installer/index.d.ts +1 -1
- package/dist/installer/index.d.ts.map +1 -1
- package/dist/installer/index.js +56 -0
- package/dist/installer/index.js.map +1 -1
- package/dist/learning/aggregation.d.ts +39 -0
- package/dist/learning/aggregation.d.ts.map +1 -0
- package/dist/learning/aggregation.js +101 -0
- package/dist/learning/aggregation.js.map +1 -0
- package/dist/learning/anomaly.d.ts +30 -0
- package/dist/learning/anomaly.d.ts.map +1 -0
- package/dist/learning/anomaly.js +102 -0
- package/dist/learning/anomaly.js.map +1 -0
- package/dist/learning/baselines.d.ts +44 -0
- package/dist/learning/baselines.d.ts.map +1 -0
- package/dist/learning/baselines.js +126 -0
- package/dist/learning/baselines.js.map +1 -0
- package/dist/learning/efficiency.d.ts +23 -0
- package/dist/learning/efficiency.d.ts.map +1 -0
- package/dist/learning/efficiency.js +67 -0
- package/dist/learning/efficiency.js.map +1 -0
- package/dist/learning/hooks/learned-context.d.ts.map +1 -1
- package/dist/learning/hooks/learned-context.js +46 -0
- package/dist/learning/hooks/learned-context.js.map +1 -1
- package/dist/learning/pricing.d.ts +30 -0
- package/dist/learning/pricing.d.ts.map +1 -0
- package/dist/learning/pricing.js +98 -0
- package/dist/learning/pricing.js.map +1 -0
- package/dist/learning/session-state.d.ts +12 -2
- package/dist/learning/session-state.d.ts.map +1 -1
- package/dist/learning/session-state.js +72 -3
- package/dist/learning/session-state.js.map +1 -1
- package/dist/learning/storage.d.ts +21 -1
- package/dist/learning/storage.d.ts.map +1 -1
- package/dist/learning/storage.js +84 -0
- package/dist/learning/storage.js.map +1 -1
- package/dist/learning/token-estimator.d.ts +41 -0
- package/dist/learning/token-estimator.d.ts.map +1 -0
- package/dist/learning/token-estimator.js +111 -0
- package/dist/learning/token-estimator.js.map +1 -0
- package/dist/learning/types.d.ts +32 -0
- package/dist/learning/types.d.ts.map +1 -1
- package/dist/learning/utils.d.ts +42 -0
- package/dist/learning/utils.d.ts.map +1 -0
- package/dist/learning/utils.js +76 -0
- package/dist/learning/utils.js.map +1 -0
- package/dist/shared/types.d.ts +29 -0
- package/dist/shared/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/scripts/dist/hooks/olympus-hooks.cjs +86 -84
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { calculateEfficiencyScore, calculateTrend } from '../../learning/efficiency.js';
|
|
3
|
+
describe('calculateEfficiencyScore', () => {
|
|
4
|
+
it('should calculate efficiency score correctly', () => {
|
|
5
|
+
const score = calculateEfficiencyScore(0.9, 5000, 10000);
|
|
6
|
+
// successRate * (baseline / avgTokens)
|
|
7
|
+
// 0.9 * (10000 / 5000) = 0.9 * 2 = 1.8
|
|
8
|
+
expect(score).toBe(1.8);
|
|
9
|
+
});
|
|
10
|
+
it('should cap token factor at 2x', () => {
|
|
11
|
+
// Very low tokens should cap at 2x
|
|
12
|
+
const score = calculateEfficiencyScore(0.9, 1000, 10000);
|
|
13
|
+
// 0.9 * min(10, 2) = 0.9 * 2 = 1.8
|
|
14
|
+
expect(score).toBe(1.8);
|
|
15
|
+
});
|
|
16
|
+
it('should handle zero success rate', () => {
|
|
17
|
+
const score = calculateEfficiencyScore(0, 5000, 10000);
|
|
18
|
+
expect(score).toBe(0);
|
|
19
|
+
});
|
|
20
|
+
it('should handle zero tokens (return 0)', () => {
|
|
21
|
+
const score = calculateEfficiencyScore(0.9, 0, 10000);
|
|
22
|
+
expect(score).toBe(0);
|
|
23
|
+
});
|
|
24
|
+
it('should handle high token usage (low efficiency)', () => {
|
|
25
|
+
const score = calculateEfficiencyScore(0.9, 20000, 10000);
|
|
26
|
+
// 0.9 * (10000 / 20000) = 0.9 * 0.5 = 0.45
|
|
27
|
+
expect(score).toBe(0.45);
|
|
28
|
+
});
|
|
29
|
+
it('should throw on invalid success rate (negative)', () => {
|
|
30
|
+
expect(() => calculateEfficiencyScore(-0.1, 5000, 10000)).toThrow('Invalid success rate');
|
|
31
|
+
});
|
|
32
|
+
it('should throw on invalid success rate (> 1)', () => {
|
|
33
|
+
expect(() => calculateEfficiencyScore(1.5, 5000, 10000)).toThrow('Invalid success rate');
|
|
34
|
+
});
|
|
35
|
+
it('should throw on negative token counts', () => {
|
|
36
|
+
expect(() => calculateEfficiencyScore(0.9, -5000, 10000)).toThrow('Token counts cannot be negative');
|
|
37
|
+
expect(() => calculateEfficiencyScore(0.9, 5000, -10000)).toThrow('Token counts cannot be negative');
|
|
38
|
+
});
|
|
39
|
+
it('should handle perfect efficiency (100% success, half the tokens)', () => {
|
|
40
|
+
const score = calculateEfficiencyScore(1.0, 5000, 10000);
|
|
41
|
+
// 1.0 * (10000 / 5000) = 1.0 * 2 = 2.0
|
|
42
|
+
expect(score).toBe(2.0);
|
|
43
|
+
});
|
|
44
|
+
it('should handle baseline equal to average', () => {
|
|
45
|
+
const score = calculateEfficiencyScore(0.8, 10000, 10000);
|
|
46
|
+
// 0.8 * (10000 / 10000) = 0.8 * 1 = 0.8
|
|
47
|
+
expect(score).toBe(0.8);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
describe('calculateTrend', () => {
|
|
51
|
+
it('should return insufficient_data when samples < 5', () => {
|
|
52
|
+
expect(calculateTrend(5000, 6000, 4)).toBe('insufficient_data');
|
|
53
|
+
expect(calculateTrend(5000, 6000, 0)).toBe('insufficient_data');
|
|
54
|
+
});
|
|
55
|
+
it('should detect improving trend (tokens decreasing)', () => {
|
|
56
|
+
// recentAvg is 10% lower than historical
|
|
57
|
+
const trend = calculateTrend(9000, 10000, 10);
|
|
58
|
+
expect(trend).toBe('improving');
|
|
59
|
+
});
|
|
60
|
+
it('should detect declining trend (tokens increasing)', () => {
|
|
61
|
+
// recentAvg is 10% higher than historical
|
|
62
|
+
const trend = calculateTrend(11000, 10000, 10);
|
|
63
|
+
expect(trend).toBe('declining');
|
|
64
|
+
});
|
|
65
|
+
it('should detect stable trend (within 10% threshold)', () => {
|
|
66
|
+
const trend = calculateTrend(10050, 10000, 10);
|
|
67
|
+
expect(trend).toBe('stable');
|
|
68
|
+
});
|
|
69
|
+
it('should handle edge case: historical avg is zero', () => {
|
|
70
|
+
const trend = calculateTrend(5000, 0, 10);
|
|
71
|
+
expect(trend).toBe('insufficient_data');
|
|
72
|
+
});
|
|
73
|
+
it('should throw on negative averages', () => {
|
|
74
|
+
expect(() => calculateTrend(-5000, 10000, 10)).toThrow('Average token counts cannot be negative');
|
|
75
|
+
expect(() => calculateTrend(5000, -10000, 10)).toThrow('Average token counts cannot be negative');
|
|
76
|
+
});
|
|
77
|
+
it('should detect improving trend at exactly -10%', () => {
|
|
78
|
+
const trend = calculateTrend(9000, 10000, 10);
|
|
79
|
+
expect(trend).toBe('improving');
|
|
80
|
+
});
|
|
81
|
+
it('should detect declining trend at exactly +10%', () => {
|
|
82
|
+
const trend = calculateTrend(11000, 10000, 10);
|
|
83
|
+
expect(trend).toBe('declining');
|
|
84
|
+
});
|
|
85
|
+
it('should detect stable at -9% (just under threshold)', () => {
|
|
86
|
+
const trend = calculateTrend(9100, 10000, 10);
|
|
87
|
+
expect(trend).toBe('stable');
|
|
88
|
+
});
|
|
89
|
+
it('should detect stable at +9% (just under threshold)', () => {
|
|
90
|
+
const trend = calculateTrend(10900, 10000, 10);
|
|
91
|
+
expect(trend).toBe('stable');
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
//# sourceMappingURL=efficiency.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"efficiency.test.js","sourceRoot":"","sources":["../../../src/__tests__/learning/efficiency.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,wBAAwB,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAExF,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;IACxC,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,KAAK,GAAG,wBAAwB,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;QACzD,uCAAuC;QACvC,uCAAuC;QACvC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,mCAAmC;QACnC,MAAM,KAAK,GAAG,wBAAwB,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;QACzD,mCAAmC;QACnC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,MAAM,KAAK,GAAG,wBAAwB,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;QACvD,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACxB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,MAAM,KAAK,GAAG,wBAAwB,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC;QACtD,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACxB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,KAAK,GAAG,wBAAwB,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;QAC1D,2CAA2C;QAC3C,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,CAAC,GAAG,EAAE,CAAC,wBAAwB,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAC5F,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,CAAC,GAAG,EAAE,CAAC,wBAAwB,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAC3F,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,CAAC,GAAG,EAAE,CAAC,wBAAwB,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,iCAAiC,CAAC,CAAC;QACrG,MAAM,CAAC,GAAG,EAAE,CAAC,wBAAwB,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,iCAAiC,CAAC,CAAC;IACvG,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;QAC1E,MAAM,KAAK,GAAG,wBAAwB,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;QACzD,uCAAuC;QACvC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACjD,MAAM,KAAK,GAAG,wBAAwB,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;QAC1D,wCAAwC;QACxC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC1D,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QAChE,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,yCAAyC;QACzC,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QAC9C,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,0CAA0C;QAC1C,MAAM,KAAK,GAAG,cAAc,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QAC/C,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,MAAM,KAAK,GAAG,cAAc,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QAC/C,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QAC1C,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,yCAAyC,CAAC,CAAC;QAClG,MAAM,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,yCAAyC,CAAC,CAAC;IACpG,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QAC9C,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,KAAK,GAAG,cAAc,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QAC/C,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC5D,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QAC9C,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC5D,MAAM,KAAK,GAAG,cAAc,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QAC/C,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feedback-loop-injection.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/learning/feedback-loop-injection.test.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for Phase 2.4 Feedback Loop (Injection)
|
|
3
|
+
* Verifies token metrics integration into session start and budget warnings
|
|
4
|
+
*/
|
|
5
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
6
|
+
import { join } from 'path';
|
|
7
|
+
import { mkdirSync, rmSync, writeFileSync, existsSync } from 'fs';
|
|
8
|
+
import { generateLearnedContext } from '../../learning/hooks/learned-context.js';
|
|
9
|
+
import { loadSessionState, saveSessionState, updateTokenBudget, shouldIssueWarning, markWarningIssued, } from '../../learning/session-state.js';
|
|
10
|
+
import { getLearningDir } from '../../learning/storage.js';
|
|
11
|
+
const TEST_DIR = join(process.cwd(), '.test-injection-' + Date.now());
|
|
12
|
+
const GLOBAL_LEARNING_DIR = getLearningDir();
|
|
13
|
+
let agentPerfBackup = null;
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
mkdirSync(TEST_DIR, { recursive: true });
|
|
16
|
+
mkdirSync(join(TEST_DIR, '.olympus'), { recursive: true });
|
|
17
|
+
// Ensure global learning directory exists
|
|
18
|
+
mkdirSync(GLOBAL_LEARNING_DIR, { recursive: true });
|
|
19
|
+
// Backup existing agent-performance.json if it exists
|
|
20
|
+
const agentPerfPath = join(GLOBAL_LEARNING_DIR, 'agent-performance.json');
|
|
21
|
+
if (existsSync(agentPerfPath)) {
|
|
22
|
+
agentPerfBackup = agentPerfPath + '.backup.' + Date.now();
|
|
23
|
+
writeFileSync(agentPerfBackup, require('fs').readFileSync(agentPerfPath));
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
afterEach(() => {
|
|
27
|
+
// Restore backup if it exists
|
|
28
|
+
if (agentPerfBackup && existsSync(agentPerfBackup)) {
|
|
29
|
+
const agentPerfPath = join(GLOBAL_LEARNING_DIR, 'agent-performance.json');
|
|
30
|
+
writeFileSync(agentPerfPath, require('fs').readFileSync(agentPerfBackup));
|
|
31
|
+
rmSync(agentPerfBackup, { force: true });
|
|
32
|
+
agentPerfBackup = null;
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
// Clean up test file if created
|
|
36
|
+
const agentPerfPath = join(GLOBAL_LEARNING_DIR, 'agent-performance.json');
|
|
37
|
+
if (existsSync(agentPerfPath)) {
|
|
38
|
+
rmSync(agentPerfPath, { force: true });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (TEST_DIR.includes('.test-injection-')) {
|
|
42
|
+
rmSync(TEST_DIR, { recursive: true, force: true });
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
describe('Token Efficiency Injection', () => {
|
|
46
|
+
it('should include token efficiency in learned context when agent data exists', () => {
|
|
47
|
+
const globalLearningDir = GLOBAL_LEARNING_DIR;
|
|
48
|
+
// Create mock agent performance data with token efficiency
|
|
49
|
+
const agentPerformance = {
|
|
50
|
+
'olympian': {
|
|
51
|
+
agent_name: 'olympian',
|
|
52
|
+
total_invocations: 10,
|
|
53
|
+
success_count: 9,
|
|
54
|
+
revision_count: 1,
|
|
55
|
+
cancellation_count: 0,
|
|
56
|
+
success_rate: 0.9,
|
|
57
|
+
failure_patterns: [],
|
|
58
|
+
strong_areas: [],
|
|
59
|
+
weak_areas: [],
|
|
60
|
+
last_updated: new Date().toISOString(),
|
|
61
|
+
token_efficiency: {
|
|
62
|
+
avg_tokens_per_success: 4200,
|
|
63
|
+
avg_tokens_per_failure: 5000,
|
|
64
|
+
total_tokens: 42000,
|
|
65
|
+
invocation_count: 10,
|
|
66
|
+
efficiency_score: 0.9,
|
|
67
|
+
trend: 'stable'
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
'oracle-low': {
|
|
71
|
+
agent_name: 'oracle-low',
|
|
72
|
+
total_invocations: 8,
|
|
73
|
+
success_count: 8,
|
|
74
|
+
revision_count: 0,
|
|
75
|
+
cancellation_count: 0,
|
|
76
|
+
success_rate: 1.0,
|
|
77
|
+
failure_patterns: [],
|
|
78
|
+
strong_areas: [],
|
|
79
|
+
weak_areas: [],
|
|
80
|
+
last_updated: new Date().toISOString(),
|
|
81
|
+
token_efficiency: {
|
|
82
|
+
avg_tokens_per_success: 2100,
|
|
83
|
+
avg_tokens_per_failure: 0,
|
|
84
|
+
total_tokens: 16800,
|
|
85
|
+
invocation_count: 8,
|
|
86
|
+
efficiency_score: 1.2,
|
|
87
|
+
trend: 'improving'
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
writeFileSync(join(globalLearningDir, 'agent-performance.json'), JSON.stringify(agentPerformance, null, 2));
|
|
92
|
+
const context = generateLearnedContext(TEST_DIR);
|
|
93
|
+
// Should include efficiency section
|
|
94
|
+
expect(context).toContain('<olympus-efficiency>');
|
|
95
|
+
expect(context).toContain('AGENT EFFICIENCY');
|
|
96
|
+
expect(context).toContain('SESSION BUDGET');
|
|
97
|
+
expect(context).toContain('Quality remains priority');
|
|
98
|
+
// Should show agents in order of efficiency
|
|
99
|
+
expect(context).toMatch(/oracle-low.*\[PREFERRED\]/);
|
|
100
|
+
expect(context).toContain('olympian');
|
|
101
|
+
});
|
|
102
|
+
it('should gracefully handle missing token data', () => {
|
|
103
|
+
const globalLearningDir = GLOBAL_LEARNING_DIR;
|
|
104
|
+
// Create agent performance without token efficiency
|
|
105
|
+
const agentPerformance = {
|
|
106
|
+
'olympian': {
|
|
107
|
+
agent_name: 'olympian',
|
|
108
|
+
total_invocations: 10,
|
|
109
|
+
success_count: 9,
|
|
110
|
+
revision_count: 1,
|
|
111
|
+
cancellation_count: 0,
|
|
112
|
+
success_rate: 0.9,
|
|
113
|
+
failure_patterns: [],
|
|
114
|
+
strong_areas: [],
|
|
115
|
+
weak_areas: [],
|
|
116
|
+
last_updated: new Date().toISOString()
|
|
117
|
+
// No token_efficiency
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
writeFileSync(join(globalLearningDir, 'agent-performance.json'), JSON.stringify(agentPerformance, null, 2));
|
|
121
|
+
const context = generateLearnedContext(TEST_DIR);
|
|
122
|
+
// Should not include efficiency section when no token data
|
|
123
|
+
expect(context).not.toContain('<olympus-efficiency>');
|
|
124
|
+
});
|
|
125
|
+
it('should respect 500 token total cap', () => {
|
|
126
|
+
const context = generateLearnedContext(TEST_DIR);
|
|
127
|
+
// Rough token estimate: 1 token ≈ 4 chars
|
|
128
|
+
const estimatedTokens = context.length / 4;
|
|
129
|
+
expect(estimatedTokens).toBeLessThanOrEqual(500);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
describe('Session Budget Tracking', () => {
|
|
133
|
+
it('should initialize token budget at session start', () => {
|
|
134
|
+
const state = loadSessionState(TEST_DIR, 'test-session-1');
|
|
135
|
+
expect(state.token_budget).toBeDefined();
|
|
136
|
+
expect(state.token_budget.session_baseline).toBe(10000); // Default baseline
|
|
137
|
+
expect(state.token_budget.current_usage).toBe(0);
|
|
138
|
+
expect(state.token_budget.warning_threshold).toBe(1.5);
|
|
139
|
+
expect(state.token_budget.warning_issued).toBe(false);
|
|
140
|
+
});
|
|
141
|
+
it('should update current_usage after feedback entry', () => {
|
|
142
|
+
let state = loadSessionState(TEST_DIR, 'test-session-2');
|
|
143
|
+
// Simulate token usage
|
|
144
|
+
state = updateTokenBudget(state, 5000);
|
|
145
|
+
expect(state.token_budget.current_usage).toBe(5000);
|
|
146
|
+
state = updateTokenBudget(state, 3000);
|
|
147
|
+
expect(state.token_budget.current_usage).toBe(8000);
|
|
148
|
+
});
|
|
149
|
+
it('should track warning_issued to prevent spam', () => {
|
|
150
|
+
let state = loadSessionState(TEST_DIR, 'test-session-3');
|
|
151
|
+
expect(state.token_budget.warning_issued).toBe(false);
|
|
152
|
+
state = markWarningIssued(state);
|
|
153
|
+
expect(state.token_budget.warning_issued).toBe(true);
|
|
154
|
+
});
|
|
155
|
+
it('should persist state across hook invocations', () => {
|
|
156
|
+
let state = loadSessionState(TEST_DIR, 'test-session-4');
|
|
157
|
+
state = updateTokenBudget(state, 7000);
|
|
158
|
+
saveSessionState(TEST_DIR, state);
|
|
159
|
+
// Load state again (simulating new hook invocation)
|
|
160
|
+
const newState = loadSessionState(TEST_DIR, 'test-session-4');
|
|
161
|
+
expect(newState.token_budget.current_usage).toBe(7000);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
describe('Budget Warning Logic', () => {
|
|
165
|
+
it('should detect when usage exceeds warning threshold', () => {
|
|
166
|
+
let state = loadSessionState(TEST_DIR, 'test-session-5');
|
|
167
|
+
// Baseline is 10k, threshold is 1.5x = 15k
|
|
168
|
+
expect(shouldIssueWarning(state)).toBe(false);
|
|
169
|
+
state = updateTokenBudget(state, 14000);
|
|
170
|
+
expect(shouldIssueWarning(state)).toBe(false);
|
|
171
|
+
state = updateTokenBudget(state, 2000); // Total: 16k > 15k
|
|
172
|
+
expect(shouldIssueWarning(state)).toBe(true);
|
|
173
|
+
});
|
|
174
|
+
it('should not issue warning if already warned', () => {
|
|
175
|
+
let state = loadSessionState(TEST_DIR, 'test-session-6');
|
|
176
|
+
state = updateTokenBudget(state, 16000); // Exceeds threshold
|
|
177
|
+
expect(shouldIssueWarning(state)).toBe(true);
|
|
178
|
+
state = markWarningIssued(state);
|
|
179
|
+
expect(shouldIssueWarning(state)).toBe(false); // Should not warn again
|
|
180
|
+
state = updateTokenBudget(state, 5000); // Even with more usage
|
|
181
|
+
expect(shouldIssueWarning(state)).toBe(false);
|
|
182
|
+
});
|
|
183
|
+
it('should be non-blocking (continue: true always)', () => {
|
|
184
|
+
// Warning logic never blocks - this is verified by the hook implementation
|
|
185
|
+
// The hook always returns { continue: true } regardless of budget status
|
|
186
|
+
expect(true).toBe(true); // Placeholder - hook behavior verified in integration tests
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
describe('Integration with Existing Learning Injection', () => {
|
|
190
|
+
it('should merge token guidance into existing SessionStart flow', () => {
|
|
191
|
+
const globalLearningDir = GLOBAL_LEARNING_DIR;
|
|
192
|
+
// Create mock data for both learning and token efficiency
|
|
193
|
+
const agentPerformance = {
|
|
194
|
+
'olympian': {
|
|
195
|
+
agent_name: 'olympian',
|
|
196
|
+
total_invocations: 10,
|
|
197
|
+
success_count: 9,
|
|
198
|
+
revision_count: 1,
|
|
199
|
+
cancellation_count: 0,
|
|
200
|
+
success_rate: 0.9,
|
|
201
|
+
failure_patterns: [],
|
|
202
|
+
strong_areas: ['multi-file edits'],
|
|
203
|
+
weak_areas: ['complex debugging'],
|
|
204
|
+
last_updated: new Date().toISOString(),
|
|
205
|
+
token_efficiency: {
|
|
206
|
+
avg_tokens_per_success: 4200,
|
|
207
|
+
avg_tokens_per_failure: 5000,
|
|
208
|
+
total_tokens: 42000,
|
|
209
|
+
invocation_count: 10,
|
|
210
|
+
efficiency_score: 0.9,
|
|
211
|
+
trend: 'stable'
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
writeFileSync(join(globalLearningDir, 'agent-performance.json'), JSON.stringify(agentPerformance, null, 2));
|
|
216
|
+
const context = generateLearnedContext(TEST_DIR);
|
|
217
|
+
// Should include both learning context and token efficiency
|
|
218
|
+
expect(context).toContain('Agent Notes'); // Learning context
|
|
219
|
+
expect(context).toContain('<olympus-efficiency>'); // Token guidance
|
|
220
|
+
// Should be under 500 tokens total
|
|
221
|
+
const estimatedTokens = context.length / 4;
|
|
222
|
+
expect(estimatedTokens).toBeLessThanOrEqual(500);
|
|
223
|
+
});
|
|
224
|
+
it('should handle missing token data gracefully', () => {
|
|
225
|
+
const globalLearningDir = GLOBAL_LEARNING_DIR;
|
|
226
|
+
// Create agent performance without token metrics
|
|
227
|
+
const agentPerformance = {
|
|
228
|
+
'olympian': {
|
|
229
|
+
agent_name: 'olympian',
|
|
230
|
+
total_invocations: 3,
|
|
231
|
+
success_count: 2,
|
|
232
|
+
revision_count: 1,
|
|
233
|
+
cancellation_count: 0,
|
|
234
|
+
success_rate: 0.67,
|
|
235
|
+
failure_patterns: [],
|
|
236
|
+
strong_areas: [],
|
|
237
|
+
weak_areas: ['debugging'],
|
|
238
|
+
last_updated: new Date().toISOString()
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
writeFileSync(join(globalLearningDir, 'agent-performance.json'), JSON.stringify(agentPerformance, null, 2));
|
|
242
|
+
const context = generateLearnedContext(TEST_DIR);
|
|
243
|
+
// Should include learning context but not token efficiency
|
|
244
|
+
expect(context).toContain('Agent Notes');
|
|
245
|
+
expect(context).not.toContain('<olympus-efficiency>');
|
|
246
|
+
});
|
|
247
|
+
it('should not produce duplicate injections', () => {
|
|
248
|
+
const context = generateLearnedContext(TEST_DIR);
|
|
249
|
+
// Count occurrences of key markers
|
|
250
|
+
const efficiencyCount = (context.match(/<olympus-efficiency>/g) || []).length;
|
|
251
|
+
expect(efficiencyCount).toBeLessThanOrEqual(1); // At most one efficiency section
|
|
252
|
+
});
|
|
253
|
+
it('should have consistent ordering (learning first, then token efficiency)', () => {
|
|
254
|
+
const globalLearningDir = GLOBAL_LEARNING_DIR;
|
|
255
|
+
const agentPerformance = {
|
|
256
|
+
'olympian': {
|
|
257
|
+
agent_name: 'olympian',
|
|
258
|
+
total_invocations: 10,
|
|
259
|
+
success_count: 9,
|
|
260
|
+
revision_count: 1,
|
|
261
|
+
cancellation_count: 0,
|
|
262
|
+
success_rate: 0.9,
|
|
263
|
+
failure_patterns: [],
|
|
264
|
+
strong_areas: [],
|
|
265
|
+
weak_areas: ['debugging'],
|
|
266
|
+
last_updated: new Date().toISOString(),
|
|
267
|
+
token_efficiency: {
|
|
268
|
+
avg_tokens_per_success: 4200,
|
|
269
|
+
avg_tokens_per_failure: 5000,
|
|
270
|
+
total_tokens: 42000,
|
|
271
|
+
invocation_count: 10,
|
|
272
|
+
efficiency_score: 0.9,
|
|
273
|
+
trend: 'stable'
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
writeFileSync(join(globalLearningDir, 'agent-performance.json'), JSON.stringify(agentPerformance, null, 2));
|
|
278
|
+
const context = generateLearnedContext(TEST_DIR);
|
|
279
|
+
// Find positions of sections
|
|
280
|
+
const agentNotesPos = context.indexOf('Agent Notes');
|
|
281
|
+
const efficiencyPos = context.indexOf('<olympus-efficiency>');
|
|
282
|
+
if (agentNotesPos !== -1 && efficiencyPos !== -1) {
|
|
283
|
+
// Learning context should come before token efficiency
|
|
284
|
+
expect(agentNotesPos).toBeLessThan(efficiencyPos);
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
//# sourceMappingURL=feedback-loop-injection.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feedback-loop-injection.test.js","sourceRoot":"","sources":["../../../src/__tests__/learning/feedback-loop-injection.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"learning-capture-integration.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/learning/learning-capture-integration.test.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration tests for learning-capture hooks
|
|
3
|
+
* Tests the full flow: UserPromptSubmit → PostToolUse → Stop
|
|
4
|
+
*/
|
|
5
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
6
|
+
import { mkdirSync, rmSync, existsSync } from 'fs';
|
|
7
|
+
import { join } from 'path';
|
|
8
|
+
import { registerLearningCaptureHooks } from '../../hooks/registrations/learning-capture.js';
|
|
9
|
+
import { routeHook } from '../../hooks/router.js';
|
|
10
|
+
import { loadSessionState } from '../../learning/session-state.js';
|
|
11
|
+
import { loadFeedback } from '../../learning/storage.js';
|
|
12
|
+
const TEST_DIR = join(process.cwd(), '.test-learning-capture');
|
|
13
|
+
describe('Learning Capture Integration', () => {
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
// Create test directory
|
|
16
|
+
if (!existsSync(TEST_DIR)) {
|
|
17
|
+
mkdirSync(TEST_DIR, { recursive: true });
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
// Clean up test directory
|
|
22
|
+
if (existsSync(TEST_DIR)) {
|
|
23
|
+
rmSync(TEST_DIR, { recursive: true, force: true });
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
it('should accumulate tokens across UserPromptSubmit and PostToolUse', async () => {
|
|
27
|
+
// Register hooks
|
|
28
|
+
registerLearningCaptureHooks();
|
|
29
|
+
const sessionId = 'test-session-123';
|
|
30
|
+
// 1. UserPromptSubmit - should estimate input tokens
|
|
31
|
+
const promptCtx = {
|
|
32
|
+
sessionId,
|
|
33
|
+
directory: TEST_DIR,
|
|
34
|
+
prompt: 'Write a function that adds two numbers',
|
|
35
|
+
};
|
|
36
|
+
await routeHook('UserPromptSubmit', promptCtx);
|
|
37
|
+
// Verify session state was updated with input tokens
|
|
38
|
+
let state = loadSessionState(TEST_DIR, sessionId);
|
|
39
|
+
expect(state.token_budget).toBeDefined();
|
|
40
|
+
expect(state.token_budget.current_usage).toBeGreaterThan(0);
|
|
41
|
+
const afterPrompt = state.token_budget.current_usage;
|
|
42
|
+
// 2. PostToolUse - should estimate output tokens
|
|
43
|
+
const toolCtx = {
|
|
44
|
+
sessionId,
|
|
45
|
+
directory: TEST_DIR,
|
|
46
|
+
toolName: 'Write',
|
|
47
|
+
toolOutput: { content: 'function add(a, b) { return a + b; }' },
|
|
48
|
+
};
|
|
49
|
+
await routeHook('PostToolUse', toolCtx);
|
|
50
|
+
// Verify tokens accumulated
|
|
51
|
+
state = loadSessionState(TEST_DIR, sessionId);
|
|
52
|
+
expect(state.token_budget.current_usage).toBeGreaterThan(afterPrompt);
|
|
53
|
+
const totalTokens = state.token_budget.current_usage;
|
|
54
|
+
// 3. Stop - should create FeedbackEntry and reset budget
|
|
55
|
+
const stopCtx = {
|
|
56
|
+
sessionId,
|
|
57
|
+
directory: TEST_DIR,
|
|
58
|
+
};
|
|
59
|
+
await routeHook('Stop', stopCtx);
|
|
60
|
+
// Verify feedback was created
|
|
61
|
+
const feedback = loadFeedback();
|
|
62
|
+
const sessionFeedback = feedback.filter(f => f.session_id === sessionId);
|
|
63
|
+
expect(sessionFeedback.length).toBeGreaterThan(0);
|
|
64
|
+
const entry = sessionFeedback[0];
|
|
65
|
+
expect(entry.token_usage).toBeDefined();
|
|
66
|
+
expect(entry.token_usage.total_tokens).toBe(totalTokens);
|
|
67
|
+
expect(entry.token_usage.estimated).toBe(true);
|
|
68
|
+
// Verify budget was reset
|
|
69
|
+
state = loadSessionState(TEST_DIR, sessionId);
|
|
70
|
+
expect(state.token_budget.current_usage).toBe(0);
|
|
71
|
+
});
|
|
72
|
+
it('should handle missing directory gracefully', async () => {
|
|
73
|
+
registerLearningCaptureHooks();
|
|
74
|
+
const ctx = {
|
|
75
|
+
sessionId: 'test-session-456',
|
|
76
|
+
// directory is missing
|
|
77
|
+
prompt: 'Test prompt',
|
|
78
|
+
};
|
|
79
|
+
// Should not throw
|
|
80
|
+
const result = await routeHook('UserPromptSubmit', ctx);
|
|
81
|
+
expect(result.continue).toBe(true);
|
|
82
|
+
});
|
|
83
|
+
it('should initialize token_budget if not present (backward compatibility)', async () => {
|
|
84
|
+
registerLearningCaptureHooks();
|
|
85
|
+
const sessionId = 'test-session-789';
|
|
86
|
+
// Create session state without token_budget
|
|
87
|
+
const olympusDir = join(TEST_DIR, '.olympus');
|
|
88
|
+
if (!existsSync(olympusDir)) {
|
|
89
|
+
mkdirSync(olympusDir, { recursive: true });
|
|
90
|
+
}
|
|
91
|
+
const ctx = {
|
|
92
|
+
sessionId,
|
|
93
|
+
directory: TEST_DIR,
|
|
94
|
+
prompt: 'Test prompt for backward compatibility',
|
|
95
|
+
};
|
|
96
|
+
await routeHook('UserPromptSubmit', ctx);
|
|
97
|
+
// Verify token_budget was created
|
|
98
|
+
const state = loadSessionState(TEST_DIR, sessionId);
|
|
99
|
+
expect(state.token_budget).toBeDefined();
|
|
100
|
+
expect(state.token_budget.session_baseline).toBe(10000);
|
|
101
|
+
expect(state.token_budget.warning_threshold).toBe(1.5);
|
|
102
|
+
});
|
|
103
|
+
it('should handle Stop without any prior token accumulation', async () => {
|
|
104
|
+
registerLearningCaptureHooks();
|
|
105
|
+
const sessionId = 'test-session-empty';
|
|
106
|
+
const stopCtx = {
|
|
107
|
+
sessionId,
|
|
108
|
+
directory: TEST_DIR,
|
|
109
|
+
};
|
|
110
|
+
// Stop without any prior events
|
|
111
|
+
const result = await routeHook('Stop', stopCtx);
|
|
112
|
+
expect(result.continue).toBe(true);
|
|
113
|
+
// Should not create feedback entry when tokens are 0
|
|
114
|
+
const feedback = loadFeedback();
|
|
115
|
+
const sessionFeedback = feedback.filter(f => f.session_id === sessionId);
|
|
116
|
+
expect(sessionFeedback.length).toBe(0);
|
|
117
|
+
});
|
|
118
|
+
it('should handle multiple tool uses correctly', async () => {
|
|
119
|
+
registerLearningCaptureHooks();
|
|
120
|
+
const sessionId = 'test-session-multi';
|
|
121
|
+
// Initial prompt
|
|
122
|
+
await routeHook('UserPromptSubmit', {
|
|
123
|
+
sessionId,
|
|
124
|
+
directory: TEST_DIR,
|
|
125
|
+
prompt: 'Create multiple files',
|
|
126
|
+
});
|
|
127
|
+
// Multiple tool uses
|
|
128
|
+
const tools = ['Read', 'Write', 'Edit', 'Bash'];
|
|
129
|
+
for (const tool of tools) {
|
|
130
|
+
await routeHook('PostToolUse', {
|
|
131
|
+
sessionId,
|
|
132
|
+
directory: TEST_DIR,
|
|
133
|
+
toolName: tool,
|
|
134
|
+
toolOutput: { content: `Output from ${tool} tool with some content` },
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
// Verify cumulative token count
|
|
138
|
+
const state = loadSessionState(TEST_DIR, sessionId);
|
|
139
|
+
expect(state.token_budget.current_usage).toBeGreaterThan(0);
|
|
140
|
+
// Stop and verify
|
|
141
|
+
await routeHook('Stop', {
|
|
142
|
+
sessionId,
|
|
143
|
+
directory: TEST_DIR,
|
|
144
|
+
});
|
|
145
|
+
const feedback = loadFeedback();
|
|
146
|
+
const entry = feedback.find(f => f.session_id === sessionId);
|
|
147
|
+
expect(entry).toBeDefined();
|
|
148
|
+
expect(entry.token_usage.total_tokens).toBeGreaterThan(0);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
//# sourceMappingURL=learning-capture-integration.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"learning-capture-integration.test.js","sourceRoot":"","sources":["../../../src/__tests__/learning/learning-capture-integration.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-metrics.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/learning/token-metrics.test.ts"],"names":[],"mappings":""}
|