pumuki-ast-hooks 6.1.10 β 6.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -9
- package/docs/INSTALLATION.md +3 -2
- package/hooks/__tests__/pre-tool-use-evidence-validator.spec.js +16 -0
- package/hooks/pre-tool-use-evidence-validator.ts +57 -2
- package/package.json +19 -19
- package/scripts/hooks-system/.audit-reports/auto-recovery.log +7 -0
- package/scripts/hooks-system/.audit-reports/install-wizard.log +28 -0
- package/scripts/hooks-system/.audit_tmp/hook-metrics.jsonl +208 -0
- package/scripts/hooks-system/infrastructure/ast/ios/__tests__/ios-srp-helpers.spec.js +62 -0
- package/scripts/hooks-system/infrastructure/ast/ios/ast-ios.js +25 -11
- package/scripts/hooks-system/infrastructure/ast/ios/detectors/ios-ast-intelligent-strategies.js +21 -9
- package/scripts/hooks-system/infrastructure/ast/ios/utils/ios-srp-helpers.js +162 -0
- package/scripts/hooks-system/infrastructure/ast/text/__tests__/text-scanner.spec.js +60 -0
- package/scripts/hooks-system/infrastructure/ast/text/text-scanner.js +60 -0
- package/scripts/hooks-system/infrastructure/orchestration/__tests__/intelligent-audit.spec.js +40 -0
- package/scripts/hooks-system/infrastructure/orchestration/intelligent-audit.js +4 -5
- package/scripts/hooks-system/infrastructure/shell/gitflow/__tests__/gitflow-enforcer.spec.js +17 -0
- package/scripts/hooks-system/infrastructure/shell/gitflow/gitflow-enforcer.sh +27 -3
- package/scripts/hooks-system/infrastructure/watchdog/__tests__/.audit-reports/token-monitor.log +21 -0
|
@@ -65,5 +65,65 @@ describe('Text Scanner Module', () => {
|
|
|
65
65
|
const typeSafety = findings.find(f => f.ruleId === 'ios.optionals.type_safety');
|
|
66
66
|
expect(typeSafety).toBeUndefined();
|
|
67
67
|
});
|
|
68
|
+
|
|
69
|
+
it('flags switch-based message provider mapping to string literals', () => {
|
|
70
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ast-text-'));
|
|
71
|
+
const filePath = path.join(root, 'SupportMessageProvider.swift');
|
|
72
|
+
const content = [
|
|
73
|
+
'enum SupportStatus {',
|
|
74
|
+
' case active',
|
|
75
|
+
' case inactive',
|
|
76
|
+
'}',
|
|
77
|
+
'',
|
|
78
|
+
'struct SupportMessageProvider {',
|
|
79
|
+
' func message(for status: SupportStatus) -> String {',
|
|
80
|
+
' switch status {',
|
|
81
|
+
' case .active:',
|
|
82
|
+
' return "support.active"',
|
|
83
|
+
' case .inactive:',
|
|
84
|
+
' return "support.inactive"',
|
|
85
|
+
' }',
|
|
86
|
+
' }',
|
|
87
|
+
'}'
|
|
88
|
+
].join('\n');
|
|
89
|
+
|
|
90
|
+
fs.writeFileSync(filePath, content, 'utf8');
|
|
91
|
+
|
|
92
|
+
const findings = [];
|
|
93
|
+
runTextScanner(root, findings);
|
|
94
|
+
|
|
95
|
+
const ocpFinding = findings.find(f => f.ruleId === 'ios.solid.ocp_switch_to_string');
|
|
96
|
+
expect(ocpFinding).toBeDefined();
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
describe('android rules', () => {
|
|
101
|
+
it('flags when-based message provider mapping to string literals', () => {
|
|
102
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ast-text-'));
|
|
103
|
+
const filePath = path.join(root, 'SupportMessageProvider.kt');
|
|
104
|
+
const content = [
|
|
105
|
+
'enum class SupportStatus {',
|
|
106
|
+
' ACTIVE,',
|
|
107
|
+
' INACTIVE',
|
|
108
|
+
'}',
|
|
109
|
+
'',
|
|
110
|
+
'class SupportMessageProvider {',
|
|
111
|
+
' fun message(status: SupportStatus): String {',
|
|
112
|
+
' return when (status) {',
|
|
113
|
+
' SupportStatus.ACTIVE -> "support.active"',
|
|
114
|
+
' SupportStatus.INACTIVE -> "support.inactive"',
|
|
115
|
+
' }',
|
|
116
|
+
' }',
|
|
117
|
+
'}'
|
|
118
|
+
].join('\n');
|
|
119
|
+
|
|
120
|
+
fs.writeFileSync(filePath, content, 'utf8');
|
|
121
|
+
|
|
122
|
+
const findings = [];
|
|
123
|
+
runTextScanner(root, findings);
|
|
124
|
+
|
|
125
|
+
const ocpFinding = findings.find(f => f.ruleId === 'android.solid.ocp_switch_to_string');
|
|
126
|
+
expect(ocpFinding).toBeDefined();
|
|
127
|
+
});
|
|
68
128
|
});
|
|
69
129
|
});
|
|
@@ -15,6 +15,58 @@ function walk(dir, acc) {
|
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
function hasProviderNameHint(content, filePath) {
|
|
19
|
+
const providerNamePattern = /\b(class|struct|object)\s+[A-Za-z_][A-Za-z0-9_]*(Message|Notice|Localization|Provider|Presenter|Mapper)[A-Za-z0-9_]*\b/i;
|
|
20
|
+
if (providerNamePattern.test(content)) return true;
|
|
21
|
+
const baseName = path.basename(filePath, path.extname(filePath));
|
|
22
|
+
return /Message|Notice|Localization|Provider|Presenter|Mapper/i.test(baseName);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function hasSwiftMappingHint(content) {
|
|
26
|
+
return /\brawValue\b/.test(content) || /\bDictionary<[^>]*String\b/.test(content) || /\[[A-Za-z_][A-Za-z0-9_]*\s*:\s*String\]/.test(content);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function hasKotlinMappingHint(content) {
|
|
30
|
+
return /\bmapOf\s*\(|\bmutableMapOf\s*\(|\bMap<[^>]*String\b/.test(content);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function hasSwiftSwitchToString(content) {
|
|
34
|
+
const switchPattern = /switch\s+[A-Za-z_][A-Za-z0-9_]*\s*\{[^}]*\}/g;
|
|
35
|
+
let match;
|
|
36
|
+
while ((match = switchPattern.exec(content)) !== null) {
|
|
37
|
+
const block = match[0];
|
|
38
|
+
if (!/\bcase\b/.test(block)) continue;
|
|
39
|
+
if (!/\"[^\"]+\"/.test(block)) continue;
|
|
40
|
+
if (!/case\s+(\.|[A-Za-z_][A-Za-z0-9_]*\.)[A-Za-z_][A-Za-z0-9_]*\s*:/.test(block)) continue;
|
|
41
|
+
if (/\breturn\b/.test(block) || /case\s+[^\n:]+:\s*\"[^\"]+\"/.test(block)) return true;
|
|
42
|
+
}
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function hasKotlinWhenToString(content) {
|
|
47
|
+
const whenPattern = /when\s*\([^)]*\)\s*\{[^}]*\}/g;
|
|
48
|
+
let match;
|
|
49
|
+
while ((match = whenPattern.exec(content)) !== null) {
|
|
50
|
+
const block = match[0];
|
|
51
|
+
if (!/[A-Za-z_][A-Za-z0-9_]*\.[A-Za-z_][A-Za-z0-9_]*\s*->\s*\"[^\"]+\"/.test(block)) continue;
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function shouldFlagMessageProviderSwitch(content, filePath, language) {
|
|
58
|
+
if (!hasProviderNameHint(content, filePath)) return false;
|
|
59
|
+
if (language === 'swift') {
|
|
60
|
+
if (hasSwiftMappingHint(content)) return false;
|
|
61
|
+
return hasSwiftSwitchToString(content);
|
|
62
|
+
}
|
|
63
|
+
if (language === 'kotlin') {
|
|
64
|
+
if (hasKotlinMappingHint(content)) return false;
|
|
65
|
+
return hasKotlinWhenToString(content);
|
|
66
|
+
}
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
|
|
18
70
|
function runTextScanner(root, findings) {
|
|
19
71
|
const files = [];
|
|
20
72
|
let iosAnchorFile = null;
|
|
@@ -541,6 +593,11 @@ function runTextScanner(root, findings) {
|
|
|
541
593
|
if (/Handler\(\)\.post/.test(content) && !/@SuppressLint\("HandlerLeak"\)/.test(content)) {
|
|
542
594
|
pushFileFinding('android.antipattern.handler_leak', 'high', file, 1, 1, 'Handler without weak reference - potential memory leak', findings);
|
|
543
595
|
}
|
|
596
|
+
const isAnalyzer = /infrastructure\/ast\/|analyzers\/|detectors\/|scanner|analyzer|detector/i.test(file);
|
|
597
|
+
const isTestFile = /\.(spec|test)\.(js|ts|kt|kts)$/i.test(file);
|
|
598
|
+
if ((ext === '.kt' || ext === '.kts') && !isAnalyzer && !isTestFile && shouldFlagMessageProviderSwitch(content, file, 'kotlin')) {
|
|
599
|
+
pushFileFinding('android.solid.ocp_switch_to_string', 'medium', file, 1, 1, 'OCP warning: switch-to-string mapping in message/localization provider - prefer injected map or enum raw values', findings);
|
|
600
|
+
}
|
|
544
601
|
}
|
|
545
602
|
|
|
546
603
|
if (plat === 'ios') {
|
|
@@ -636,6 +693,9 @@ function runTextScanner(root, findings) {
|
|
|
636
693
|
if (/Text\(\s*\"[^\"]+\"\s*\)/.test(content) && !/NSLocalizedString|\.localized/.test(content)) {
|
|
637
694
|
pushFileFinding('ios.i18n.hardcoded_strings', 'medium', file, 1, 1, 'Hardcoded string in Text() without localization', findings);
|
|
638
695
|
}
|
|
696
|
+
if (!isAnalyzer && !isTestFile && shouldFlagMessageProviderSwitch(content, file, 'swift')) {
|
|
697
|
+
pushFileFinding('ios.solid.ocp_switch_to_string', 'medium', file, 1, 1, 'OCP warning: switch-to-string mapping in message/localization provider - prefer RawRepresentable or injected map', findings);
|
|
698
|
+
}
|
|
639
699
|
if (/Date\(/.test(content) && !/DateFormatter/.test(content)) {
|
|
640
700
|
pushFileFinding('ios.i18n.missing_date_formatter', 'medium', file, 1, 1, 'Date rendered without DateFormatter', findings);
|
|
641
701
|
}
|
package/scripts/hooks-system/infrastructure/orchestration/__tests__/intelligent-audit.spec.js
CHANGED
|
@@ -129,6 +129,46 @@ describe('intelligent-audit', () => {
|
|
|
129
129
|
expect(updated.timestamp).toBeDefined();
|
|
130
130
|
expect(updated.timestamp).not.toBe(previous.timestamp);
|
|
131
131
|
});
|
|
132
|
+
|
|
133
|
+
it('should include medium and low violations in ai_gate output when critical or high exist', async () => {
|
|
134
|
+
const evidencePath = path.join(process.cwd(), '.AI_EVIDENCE.json');
|
|
135
|
+
const original = fs.existsSync(evidencePath) ? fs.readFileSync(evidencePath, 'utf8') : null;
|
|
136
|
+
const minimal = {
|
|
137
|
+
timestamp: new Date().toISOString(),
|
|
138
|
+
severity_metrics: {
|
|
139
|
+
last_updated: new Date().toISOString(),
|
|
140
|
+
total_violations: 0,
|
|
141
|
+
by_severity: { CRITICAL: 0, HIGH: 0, MEDIUM: 0, LOW: 0 }
|
|
142
|
+
},
|
|
143
|
+
ai_gate: { status: 'ALLOWED', scope: 'staging', last_check: new Date().toISOString(), violations: [], instruction: 'x', mandatory: true }
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
try {
|
|
147
|
+
fs.writeFileSync(evidencePath, JSON.stringify(minimal, null, 2));
|
|
148
|
+
|
|
149
|
+
const mod = require('../intelligent-audit');
|
|
150
|
+
const violations = [
|
|
151
|
+
{ severity: 'CRITICAL', ruleId: 'rule.critical', filePath: 'apps/a.ts', line: 1, message: 'c' },
|
|
152
|
+
{ severity: 'HIGH', ruleId: 'rule.high', filePath: 'apps/b.ts', line: 2, message: 'h' },
|
|
153
|
+
{ severity: 'MEDIUM', ruleId: 'rule.medium', filePath: 'apps/c.ts', line: 3, message: 'm' },
|
|
154
|
+
{ severity: 'LOW', ruleId: 'rule.low', filePath: 'apps/d.ts', line: 4, message: 'l' }
|
|
155
|
+
];
|
|
156
|
+
|
|
157
|
+
await mod.updateAIEvidence(violations, { passed: false, blockedBy: 'critical' }, { estimated: 1, percentUsed: 0, remaining: 1 });
|
|
158
|
+
|
|
159
|
+
const updated = JSON.parse(fs.readFileSync(evidencePath, 'utf8'));
|
|
160
|
+
const severities = updated.ai_gate.violations.map(v => v.severity);
|
|
161
|
+
expect(severities).toEqual(expect.arrayContaining(['CRITICAL', 'HIGH', 'MEDIUM', 'LOW']));
|
|
162
|
+
} finally {
|
|
163
|
+
if (original === null) {
|
|
164
|
+
if (fs.existsSync(evidencePath)) {
|
|
165
|
+
fs.unlinkSync(evidencePath);
|
|
166
|
+
}
|
|
167
|
+
} else {
|
|
168
|
+
fs.writeFileSync(evidencePath, original);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
});
|
|
132
172
|
});
|
|
133
173
|
|
|
134
174
|
describe('AI_EVIDENCE.json structure validation', () => {
|
|
@@ -736,11 +736,10 @@ async function updateAIEvidence(violations, gateResult, tokenUsage) {
|
|
|
736
736
|
const mediumViolations = violations.filter(v => v.severity === 'MEDIUM');
|
|
737
737
|
const lowViolations = violations.filter(v => v.severity === 'LOW');
|
|
738
738
|
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
const blockingViolations = gateViolations.slice(0, 50);
|
|
739
|
+
const maxGateViolations = env.getNumber('AI_GATE_MAX_VIOLATIONS', 200);
|
|
740
|
+
const gateViolationsLimit = Number.isFinite(maxGateViolations) && maxGateViolations > 0 ? maxGateViolations : 200;
|
|
741
|
+
const gateViolations = [...criticalViolations, ...highViolations, ...mediumViolations, ...lowViolations];
|
|
742
|
+
const blockingViolations = gateViolations.slice(0, gateViolationsLimit);
|
|
744
743
|
|
|
745
744
|
const gateScope = String(env.get('AI_GATE_SCOPE', 'staging') || 'staging').trim().toLowerCase();
|
|
746
745
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
describe('gitflow-enforcer', () => {
|
|
5
|
+
const scriptPath = path.join(__dirname, '..', 'gitflow-enforcer.sh');
|
|
6
|
+
|
|
7
|
+
test('debe ignorar xcuserdata en atomicidad', () => {
|
|
8
|
+
const script = fs.readFileSync(scriptPath, 'utf8');
|
|
9
|
+
expect(script).toMatch(/xcuserdata/);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test('debe omitir lint:hooks si no esta configurado', () => {
|
|
13
|
+
const script = fs.readFileSync(scriptPath, 'utf8');
|
|
14
|
+
expect(script).toMatch(/lint:hooks/);
|
|
15
|
+
expect(script).toMatch(/no configurado/);
|
|
16
|
+
});
|
|
17
|
+
});
|
|
@@ -93,11 +93,24 @@ refresh_evidence() {
|
|
|
93
93
|
printf "${GREEN}β
Evidencia renovada (${reason}).${NC}\n"
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
has_lint_hooks_script() {
|
|
97
|
+
local pkg="$1"
|
|
98
|
+
if [[ ! -f "$pkg" ]]; then
|
|
99
|
+
return 1
|
|
100
|
+
fi
|
|
101
|
+
if node -e "const fs=require('fs');const p=JSON.parse(fs.readFileSync(process.argv[1],'utf8'));process.exit(p&&p.scripts&&p.scripts['lint:hooks']?0:1);" "$pkg" >/dev/null 2>&1; then
|
|
102
|
+
return 0
|
|
103
|
+
fi
|
|
104
|
+
return 1
|
|
105
|
+
}
|
|
106
|
+
|
|
96
107
|
lint_hooks_system() {
|
|
97
108
|
local repo_pkg="${REPO_ROOT}/package.json"
|
|
98
109
|
local hooks_pkg="${REPO_ROOT}/scripts/hooks-system/package.json"
|
|
110
|
+
local ran=0
|
|
99
111
|
|
|
100
|
-
if
|
|
112
|
+
if has_lint_hooks_script "${repo_pkg}"; then
|
|
113
|
+
ran=1
|
|
101
114
|
printf "${CYAN}π Ejecutando lint (repo root)...${NC}\n"
|
|
102
115
|
if npm --prefix "${REPO_ROOT}" run lint:hooks; then
|
|
103
116
|
printf "${GREEN}β
Lint hooks-system OK.${NC}\n"
|
|
@@ -105,7 +118,8 @@ lint_hooks_system() {
|
|
|
105
118
|
fi
|
|
106
119
|
fi
|
|
107
120
|
|
|
108
|
-
if
|
|
121
|
+
if has_lint_hooks_script "${hooks_pkg}"; then
|
|
122
|
+
ran=1
|
|
109
123
|
printf "${CYAN}π Ejecutando lint (scripts/hooks-system)...${NC}\n"
|
|
110
124
|
if npm --prefix "${REPO_ROOT}/scripts/hooks-system" run lint:hooks; then
|
|
111
125
|
printf "${GREEN}β
Lint hooks-system OK.${NC}\n"
|
|
@@ -113,6 +127,11 @@ lint_hooks_system() {
|
|
|
113
127
|
fi
|
|
114
128
|
fi
|
|
115
129
|
|
|
130
|
+
if [[ "$ran" -eq 0 ]]; then
|
|
131
|
+
printf "${YELLOW}βΉοΈ lint:hooks no configurado; se omite.${NC}\n"
|
|
132
|
+
return 0
|
|
133
|
+
fi
|
|
134
|
+
|
|
116
135
|
printf "${RED}β Lint hooks-system fallΓ³.${NC}\n"
|
|
117
136
|
return 1
|
|
118
137
|
}
|
|
@@ -178,12 +197,17 @@ verify_atomic_commit() {
|
|
|
178
197
|
local roots_list=""
|
|
179
198
|
local root_count=0
|
|
180
199
|
for file in "${files[@]}"; do
|
|
200
|
+
case "$file" in
|
|
201
|
+
*/xcuserdata/*|*/xcuserdatad/*)
|
|
202
|
+
continue
|
|
203
|
+
;;
|
|
204
|
+
esac
|
|
181
205
|
local root="${file%%/*}"
|
|
182
206
|
if [[ "$root" == "$file" ]]; then
|
|
183
207
|
root="(root)"
|
|
184
208
|
fi
|
|
185
209
|
case "$root" in
|
|
186
|
-
.AI_EVIDENCE.json|README.md|CHANGELOG.md|docs )
|
|
210
|
+
.AI_EVIDENCE.json|README.md|CHANGELOG.md|docs|.audit_tmp|.audit-reports|.cursor|.windsurf|xcuserdata|xcuserdatad )
|
|
187
211
|
continue
|
|
188
212
|
;;
|
|
189
213
|
"" )
|
package/scripts/hooks-system/infrastructure/watchdog/__tests__/.audit-reports/token-monitor.log
CHANGED
|
@@ -127,3 +127,24 @@
|
|
|
127
127
|
{"timestamp":"2026-01-15T14:53:25.770Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"ok","percentUsed":10,"tokensUsed":100000,"maxTokens":1000000,"source":"realtime","stale":false},"context":{"message":"Result level=ok percent=10% used=100000/1000000 source=realtime"}}
|
|
128
128
|
{"timestamp":"2026-01-15T14:53:25.772Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"warning","percentUsed":91,"tokensUsed":910000,"maxTokens":1000000,"source":"fallback","stale":false},"context":{"message":"Result level=warning percent=91% used=910000/1000000 source=fallback"}}
|
|
129
129
|
{"timestamp":"2026-01-15T14:53:25.772Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"critical","percentUsed":98,"tokensUsed":980000,"maxTokens":1000000,"source":"realtime","stale":true},"context":{"message":"Result level=critical percent=98% used=980000/1000000 source=realtime (stale)"}}
|
|
130
|
+
{"timestamp":"2026-01-21T12:12:08.680Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"ok","percentUsed":10,"tokensUsed":100000,"maxTokens":1000000,"source":"realtime","stale":false},"context":{"message":"Result level=ok percent=10% used=100000/1000000 source=realtime"}}
|
|
131
|
+
{"timestamp":"2026-01-21T12:12:08.681Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"warning","percentUsed":91,"tokensUsed":910000,"maxTokens":1000000,"source":"fallback","stale":false},"context":{"message":"Result level=warning percent=91% used=910000/1000000 source=fallback"}}
|
|
132
|
+
{"timestamp":"2026-01-21T12:12:08.681Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"critical","percentUsed":98,"tokensUsed":980000,"maxTokens":1000000,"source":"realtime","stale":true},"context":{"message":"Result level=critical percent=98% used=980000/1000000 source=realtime (stale)"}}
|
|
133
|
+
{"timestamp":"2026-01-21T13:01:16.348Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"ok","percentUsed":10,"tokensUsed":100000,"maxTokens":1000000,"source":"realtime","stale":false},"context":{"message":"Result level=ok percent=10% used=100000/1000000 source=realtime"}}
|
|
134
|
+
{"timestamp":"2026-01-21T13:01:16.349Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"warning","percentUsed":91,"tokensUsed":910000,"maxTokens":1000000,"source":"fallback","stale":false},"context":{"message":"Result level=warning percent=91% used=910000/1000000 source=fallback"}}
|
|
135
|
+
{"timestamp":"2026-01-21T13:01:16.350Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"critical","percentUsed":98,"tokensUsed":980000,"maxTokens":1000000,"source":"realtime","stale":true},"context":{"message":"Result level=critical percent=98% used=980000/1000000 source=realtime (stale)"}}
|
|
136
|
+
{"timestamp":"2026-01-21T13:17:08.086Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"ok","percentUsed":10,"tokensUsed":100000,"maxTokens":1000000,"source":"realtime","stale":false},"context":{"message":"Result level=ok percent=10% used=100000/1000000 source=realtime"}}
|
|
137
|
+
{"timestamp":"2026-01-21T13:17:08.088Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"warning","percentUsed":91,"tokensUsed":910000,"maxTokens":1000000,"source":"fallback","stale":false},"context":{"message":"Result level=warning percent=91% used=910000/1000000 source=fallback"}}
|
|
138
|
+
{"timestamp":"2026-01-21T13:17:08.089Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"critical","percentUsed":98,"tokensUsed":980000,"maxTokens":1000000,"source":"realtime","stale":true},"context":{"message":"Result level=critical percent=98% used=980000/1000000 source=realtime (stale)"}}
|
|
139
|
+
{"timestamp":"2026-01-21T14:08:15.939Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"ok","percentUsed":10,"tokensUsed":100000,"maxTokens":1000000,"source":"realtime","stale":false},"context":{"message":"Result level=ok percent=10% used=100000/1000000 source=realtime"}}
|
|
140
|
+
{"timestamp":"2026-01-21T14:08:15.943Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"warning","percentUsed":91,"tokensUsed":910000,"maxTokens":1000000,"source":"fallback","stale":false},"context":{"message":"Result level=warning percent=91% used=910000/1000000 source=fallback"}}
|
|
141
|
+
{"timestamp":"2026-01-21T14:08:15.945Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"critical","percentUsed":98,"tokensUsed":980000,"maxTokens":1000000,"source":"realtime","stale":true},"context":{"message":"Result level=critical percent=98% used=980000/1000000 source=realtime (stale)"}}
|
|
142
|
+
{"timestamp":"2026-01-21T14:14:45.047Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"ok","percentUsed":10,"tokensUsed":100000,"maxTokens":1000000,"source":"realtime","stale":false},"context":{"message":"Result level=ok percent=10% used=100000/1000000 source=realtime"}}
|
|
143
|
+
{"timestamp":"2026-01-21T14:14:45.048Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"warning","percentUsed":91,"tokensUsed":910000,"maxTokens":1000000,"source":"fallback","stale":false},"context":{"message":"Result level=warning percent=91% used=910000/1000000 source=fallback"}}
|
|
144
|
+
{"timestamp":"2026-01-21T14:14:45.048Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"critical","percentUsed":98,"tokensUsed":980000,"maxTokens":1000000,"source":"realtime","stale":true},"context":{"message":"Result level=critical percent=98% used=980000/1000000 source=realtime (stale)"}}
|
|
145
|
+
{"timestamp":"2026-01-21T14:19:16.416Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"ok","percentUsed":10,"tokensUsed":100000,"maxTokens":1000000,"source":"realtime","stale":false},"context":{"message":"Result level=ok percent=10% used=100000/1000000 source=realtime"}}
|
|
146
|
+
{"timestamp":"2026-01-21T14:19:16.417Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"warning","percentUsed":91,"tokensUsed":910000,"maxTokens":1000000,"source":"fallback","stale":false},"context":{"message":"Result level=warning percent=91% used=910000/1000000 source=fallback"}}
|
|
147
|
+
{"timestamp":"2026-01-21T14:19:16.418Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"critical","percentUsed":98,"tokensUsed":980000,"maxTokens":1000000,"source":"realtime","stale":true},"context":{"message":"Result level=critical percent=98% used=980000/1000000 source=realtime (stale)"}}
|
|
148
|
+
{"timestamp":"2026-01-21T14:43:17.747Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"ok","percentUsed":10,"tokensUsed":100000,"maxTokens":1000000,"source":"realtime","stale":false},"context":{"message":"Result level=ok percent=10% used=100000/1000000 source=realtime"}}
|
|
149
|
+
{"timestamp":"2026-01-21T14:43:17.748Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"warning","percentUsed":91,"tokensUsed":910000,"maxTokens":1000000,"source":"fallback","stale":false},"context":{"message":"Result level=warning percent=91% used=910000/1000000 source=fallback"}}
|
|
150
|
+
{"timestamp":"2026-01-21T14:43:17.749Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"critical","percentUsed":98,"tokensUsed":980000,"maxTokens":1000000,"source":"realtime","stale":true},"context":{"message":"Result level=critical percent=98% used=980000/1000000 source=realtime (stale)"}}
|