agentaudit 3.12.5 → 3.12.6
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/cli.mjs +61 -4
- package/package.json +1 -1
package/cli.mjs
CHANGED
|
@@ -2587,10 +2587,43 @@ function loadAuditPrompt() {
|
|
|
2587
2587
|
return null;
|
|
2588
2588
|
}
|
|
2589
2589
|
|
|
2590
|
+
// Known context window sizes (input tokens) for common models
|
|
2591
|
+
const MODEL_CONTEXT_LIMITS = {
|
|
2592
|
+
'claude-sonnet-4': 200000, 'claude-opus-4': 200000, 'claude-haiku-4': 200000,
|
|
2593
|
+
'claude-3.5-sonnet': 200000, 'claude-3-haiku': 200000,
|
|
2594
|
+
'gpt-4o': 128000, 'gpt-4o-mini': 128000, 'gpt-4-turbo': 128000, 'gpt-4': 8192,
|
|
2595
|
+
'gemini-2.5-flash': 1048576, 'gemini-2.5-pro': 1048576, 'gemini-2.0-flash': 1048576,
|
|
2596
|
+
'deepseek-chat': 64000, 'deepseek-reasoner': 64000,
|
|
2597
|
+
'mistral-large': 128000, 'mistral-small': 32000,
|
|
2598
|
+
};
|
|
2599
|
+
|
|
2600
|
+
function estimateTokens(text) { return Math.ceil(text.length / 3.5); }
|
|
2601
|
+
|
|
2602
|
+
function checkContextLimit(model, systemPrompt, userMessage) {
|
|
2603
|
+
const modelKey = Object.keys(MODEL_CONTEXT_LIMITS).find(k => model.toLowerCase().includes(k.toLowerCase()));
|
|
2604
|
+
if (!modelKey) return null; // unknown model, skip check
|
|
2605
|
+
const limit = MODEL_CONTEXT_LIMITS[modelKey];
|
|
2606
|
+
const estimated = estimateTokens(systemPrompt) + estimateTokens(userMessage);
|
|
2607
|
+
if (estimated > limit * 0.9) {
|
|
2608
|
+
return { estimated, limit, pct: Math.round(estimated / limit * 100) };
|
|
2609
|
+
}
|
|
2610
|
+
return null;
|
|
2611
|
+
}
|
|
2612
|
+
|
|
2590
2613
|
async function callLlm(llmConfig, systemPrompt, userMessage) {
|
|
2591
2614
|
const apiKey = process.env[llmConfig.key];
|
|
2592
2615
|
if (!apiKey) return { error: `Missing API key: ${llmConfig.key}` };
|
|
2593
2616
|
const start = Date.now();
|
|
2617
|
+
|
|
2618
|
+
// Context window warning
|
|
2619
|
+
const ctxCheck = checkContextLimit(llmConfig.model, systemPrompt, userMessage);
|
|
2620
|
+
if (ctxCheck) {
|
|
2621
|
+
console.log(` ${c.yellow}⚠ Input ~${Math.round(ctxCheck.estimated/1000)}k tokens (${ctxCheck.pct}% of ${Math.round(ctxCheck.limit/1000)}k context window)${c.reset}`);
|
|
2622
|
+
if (ctxCheck.pct > 100) {
|
|
2623
|
+
return { error: `Input too large (~${Math.round(ctxCheck.estimated/1000)}k tokens) for ${llmConfig.model} (${Math.round(ctxCheck.limit/1000)}k context limit). Try a smaller package or a model with a larger context window.` };
|
|
2624
|
+
}
|
|
2625
|
+
}
|
|
2626
|
+
|
|
2594
2627
|
let _text = '';
|
|
2595
2628
|
try {
|
|
2596
2629
|
let data;
|
|
@@ -2607,14 +2640,19 @@ async function callLlm(llmConfig, systemPrompt, userMessage) {
|
|
|
2607
2640
|
return { error: friendly?.text || data.error.message || JSON.stringify(data.error), hint: friendly?.hint, duration: Date.now() - start };
|
|
2608
2641
|
}
|
|
2609
2642
|
_text = data.content?.[0]?.text || '';
|
|
2643
|
+
if (data.stop_reason === 'max_tokens') {
|
|
2644
|
+
console.log(` ${c.red}✗ Output truncated — model hit max_tokens limit (${data.usage?.output_tokens || '?'} tokens). Results may be incomplete.${c.reset}`);
|
|
2645
|
+
console.log(` ${c.dim} Hint: Try a model with higher output capacity, or scan a smaller package.${c.reset}`);
|
|
2646
|
+
}
|
|
2610
2647
|
const report = extractJSON(_text);
|
|
2611
2648
|
if (report) {
|
|
2612
2649
|
report.audit_model = data.model || llmConfig.model;
|
|
2613
2650
|
report.audit_provider = llmConfig.provider;
|
|
2614
2651
|
if (data.id) report.provider_msg_id = data.id;
|
|
2615
2652
|
if (data.usage) { report.input_tokens = data.usage.input_tokens; report.output_tokens = data.usage.output_tokens; }
|
|
2653
|
+
if (data.stop_reason === 'max_tokens') report.output_truncated = true;
|
|
2616
2654
|
}
|
|
2617
|
-
return { report, text: _text, duration: Date.now() - start };
|
|
2655
|
+
return { report, text: _text, duration: Date.now() - start, truncated: data.stop_reason === 'max_tokens' };
|
|
2618
2656
|
} else if (llmConfig.type === 'gemini') {
|
|
2619
2657
|
const res = await fetch(`${llmConfig.url}/${llmConfig.model}:generateContent?key=${apiKey}`, {
|
|
2620
2658
|
method: 'POST',
|
|
@@ -2632,13 +2670,19 @@ async function callLlm(llmConfig, systemPrompt, userMessage) {
|
|
|
2632
2670
|
return { error: friendly?.text || data.error.message || JSON.stringify(data.error), hint: friendly?.hint, duration: Date.now() - start };
|
|
2633
2671
|
}
|
|
2634
2672
|
_text = data.candidates?.[0]?.content?.parts?.[0]?.text || '';
|
|
2673
|
+
const geminiFinish = data.candidates?.[0]?.finishReason;
|
|
2674
|
+
if (geminiFinish === 'MAX_TOKENS') {
|
|
2675
|
+
console.log(` ${c.red}✗ Output truncated — model hit maxOutputTokens limit (${data.usageMetadata?.candidatesTokenCount || '?'} tokens). Results may be incomplete.${c.reset}`);
|
|
2676
|
+
console.log(` ${c.dim} Hint: Try a model with higher output capacity, or scan a smaller package.${c.reset}`);
|
|
2677
|
+
}
|
|
2635
2678
|
const report = extractJSON(_text);
|
|
2636
2679
|
if (report) {
|
|
2637
2680
|
report.audit_model = data.modelVersion || llmConfig.model;
|
|
2638
2681
|
report.audit_provider = llmConfig.provider;
|
|
2639
2682
|
if (data.usageMetadata) { report.input_tokens = data.usageMetadata.promptTokenCount; report.output_tokens = data.usageMetadata.candidatesTokenCount; }
|
|
2683
|
+
if (geminiFinish === 'MAX_TOKENS') report.output_truncated = true;
|
|
2640
2684
|
}
|
|
2641
|
-
return { report, text: _text, duration: Date.now() - start };
|
|
2685
|
+
return { report, text: _text, duration: Date.now() - start, truncated: geminiFinish === 'MAX_TOKENS' };
|
|
2642
2686
|
} else {
|
|
2643
2687
|
const headers = { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' };
|
|
2644
2688
|
if (llmConfig.provider === 'openrouter') { headers['HTTP-Referer'] = 'https://agentaudit.dev'; headers['X-Title'] = 'AgentAudit CLI'; }
|
|
@@ -2654,6 +2698,11 @@ async function callLlm(llmConfig, systemPrompt, userMessage) {
|
|
|
2654
2698
|
return { error: friendly?.text || data.error.message || JSON.stringify(data.error), hint: friendly?.hint, duration: Date.now() - start };
|
|
2655
2699
|
}
|
|
2656
2700
|
_text = data.choices?.[0]?.message?.content || '';
|
|
2701
|
+
const oaiFinish = data.choices?.[0]?.finish_reason;
|
|
2702
|
+
if (oaiFinish === 'length') {
|
|
2703
|
+
console.log(` ${c.red}✗ Output truncated — model hit max_tokens limit (${data.usage?.completion_tokens || '?'} tokens). Results may be incomplete.${c.reset}`);
|
|
2704
|
+
console.log(` ${c.dim} Hint: Try a model with higher output capacity, or scan a smaller package.${c.reset}`);
|
|
2705
|
+
}
|
|
2657
2706
|
const report = extractJSON(_text);
|
|
2658
2707
|
if (report) {
|
|
2659
2708
|
report.audit_model = data.model || llmConfig.model;
|
|
@@ -2661,12 +2710,13 @@ async function callLlm(llmConfig, systemPrompt, userMessage) {
|
|
|
2661
2710
|
if (data.id) report.provider_msg_id = data.id;
|
|
2662
2711
|
if (data.system_fingerprint) report.provider_fingerprint = data.system_fingerprint;
|
|
2663
2712
|
if (data.usage) { report.input_tokens = data.usage.prompt_tokens; report.output_tokens = data.usage.completion_tokens; }
|
|
2713
|
+
if (oaiFinish === 'length') report.output_truncated = true;
|
|
2664
2714
|
}
|
|
2665
|
-
return { report, text: _text, duration: Date.now() - start };
|
|
2715
|
+
return { report, text: _text, duration: Date.now() - start, truncated: oaiFinish === 'length' };
|
|
2666
2716
|
}
|
|
2667
2717
|
} catch (err) {
|
|
2668
2718
|
const dur = Date.now() - start;
|
|
2669
|
-
if (err.name === 'TimeoutError' || err.message?.includes('timeout')) return { error: 'Request timed out (
|
|
2719
|
+
if (err.name === 'TimeoutError' || err.message?.includes('timeout')) return { error: 'Request timed out (180s)', hint: 'Try again or use a faster model', duration: dur };
|
|
2670
2720
|
if (err.code === 'ENOTFOUND' || err.code === 'ECONNREFUSED' || err.message?.includes('fetch failed')) return { error: `Network error: could not reach ${llmConfig.provider}`, hint: 'Check your internet connection', duration: dur };
|
|
2671
2721
|
return { error: err.message, duration: dur };
|
|
2672
2722
|
}
|
|
@@ -3139,6 +3189,13 @@ async function auditRepo(url) {
|
|
|
3139
3189
|
|
|
3140
3190
|
console.log(` ${c.green}done${c.reset} ${c.dim}(${elapsed(start)})${c.reset}`);
|
|
3141
3191
|
|
|
3192
|
+
if (llmResult.truncated) {
|
|
3193
|
+
console.log();
|
|
3194
|
+
console.log(` ${c.yellow}⚠ WARNING: The model's output was truncated (hit token limit).${c.reset}`);
|
|
3195
|
+
console.log(` ${c.yellow} Some findings may be missing from this scan.${c.reset}`);
|
|
3196
|
+
console.log(` ${c.dim} Tip: Try a model with more output capacity or scan a smaller package.${c.reset}`);
|
|
3197
|
+
}
|
|
3198
|
+
|
|
3142
3199
|
const report = llmResult.report;
|
|
3143
3200
|
if (!report) {
|
|
3144
3201
|
console.log(` ${c.red}Could not parse LLM response as JSON${c.reset}`);
|