feat: upgrade claude-agent-sdk to 0.2.38 and adapt to new SDK types (#113)

* feat: upgrade claude-agent-sdk to 0.2.38 and adapt to new SDK types

- Bump @anthropic-ai/claude-agent-sdk from 0.1.x to 0.2.38 (both root and mcp-server)
- Bump zod from 3.x to 4.x (SDK peer dependency)
- Add allowDangerouslySkipPermissions to query options (required for bypassPermissions)
- Suppress new SDK message types (tool_progress, tool_use_summary, auth_status)
- Use structured error field on assistant messages instead of text-sniffing
- Add stop_reason to result message handling for diagnostics
- Add SDKAssistantMessageError type matching SDK's string literal union

* chore: remove CLAUDE_CODE_MAX_OUTPUT_TOKENS from all config and docs
This commit is contained in:
ezl-keygraph
2026-02-11 00:19:59 +05:30
committed by GitHub
parent 24bcd29d97
commit 3c13a9a7e6
11 changed files with 278 additions and 33 deletions
+1
View File
@@ -223,6 +223,7 @@ export async function runClaudePrompt(
maxTurns: 10_000,
cwd: sourceDir,
permissionMode: 'bypassPermissions' as const,
allowDangerouslySkipPermissions: true,
mcpServers,
};
+86 -1
View File
@@ -22,6 +22,7 @@ import type { AuditLogger } from './audit-logger.js';
import type { ProgressManager } from './progress-manager.js';
import type {
AssistantMessage,
SDKAssistantMessageError,
ResultMessage,
ToolUseMessage,
ToolResultMessage,
@@ -100,13 +101,86 @@ export function detectApiError(content: string): ApiErrorDetection {
return { detected: false };
}
// Maps SDK structured error types to our error handling.
function handleStructuredError(
errorType: SDKAssistantMessageError,
content: string
): ApiErrorDetection {
switch (errorType) {
case 'billing_error':
return {
detected: true,
shouldThrow: new PentestError(
`Billing error (structured): ${content.slice(0, 100)}`,
'billing',
true // Retryable with backoff
),
};
case 'rate_limit':
return {
detected: true,
shouldThrow: new PentestError(
`Rate limit hit (structured): ${content.slice(0, 100)}`,
'network',
true // Retryable with backoff
),
};
case 'authentication_failed':
return {
detected: true,
shouldThrow: new PentestError(
`Authentication failed: ${content.slice(0, 100)}`,
'config',
false // Not retryable - needs API key fix
),
};
case 'server_error':
return {
detected: true,
shouldThrow: new PentestError(
`Server error (structured): ${content.slice(0, 100)}`,
'network',
true // Retryable
),
};
case 'invalid_request':
return {
detected: true,
shouldThrow: new PentestError(
`Invalid request: ${content.slice(0, 100)}`,
'config',
false // Not retryable - needs code fix
),
};
case 'max_output_tokens':
return {
detected: true,
shouldThrow: new PentestError(
`Max output tokens reached: ${content.slice(0, 100)}`,
'billing',
true // Retryable - may succeed with different content
),
};
case 'unknown':
default:
return { detected: true };
}
}
export function handleAssistantMessage(
message: AssistantMessage,
turnCount: number
): AssistantResult {
const content = extractMessageContent(message);
const cleanedContent = filterJsonToolCalls(content);
const errorDetection = detectApiError(content);
// Prefer structured error field from SDK, fall back to text-sniffing
let errorDetection: ApiErrorDetection;
if (message.error) {
errorDetection = handleStructuredError(message.error, content);
} else {
errorDetection = detectApiError(content);
}
const result: AssistantResult = {
content,
@@ -141,6 +215,14 @@ export function handleResultMessage(message: ResultMessage): ResultData {
result.subtype = message.subtype;
}
// Capture stop_reason for diagnostics (helps debug early stops, budget exceeded, etc.)
if (message.stop_reason !== undefined) {
result.stop_reason = message.stop_reason;
if (message.stop_reason && message.stop_reason !== 'end_turn') {
console.log(chalk.yellow(` Stop reason: ${message.stop_reason}`));
}
}
return result;
}
@@ -247,6 +329,9 @@ export async function dispatchMessage(
}
case 'user':
case 'tool_progress':
case 'tool_use_summary':
case 'auth_status':
return { type: 'continue' };
case 'tool_use': {
+12
View File
@@ -46,6 +46,7 @@ export interface ResultData {
cost: number;
duration_ms: number;
subtype?: string;
stop_reason?: string | null;
permissionDenials: number;
}
@@ -66,8 +67,18 @@ export interface ContentBlock {
text?: string;
}
export type SDKAssistantMessageError =
| 'authentication_failed'
| 'billing_error'
| 'rate_limit'
| 'invalid_request'
| 'server_error'
| 'max_output_tokens'
| 'unknown';
export interface AssistantMessage {
type: 'assistant';
error?: SDKAssistantMessageError;
message: {
content: ContentBlock[] | string;
};
@@ -79,6 +90,7 @@ export interface ResultMessage {
total_cost_usd?: number;
duration_ms?: number;
subtype?: string;
stop_reason?: string | null;
permission_denials?: unknown[];
}