@shareai-lab/kode 1.0.76 → 1.0.79
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/package.json +1 -1
- package/src/constants/prompts.ts +6 -15
- package/src/services/claude.ts +4 -4
- package/src/services/openai.ts +23 -1
- package/src/utils/debugLogger.ts +106 -0
package/package.json
CHANGED
package/src/constants/prompts.ts
CHANGED
|
@@ -9,25 +9,17 @@ import { PRODUCT_NAME, PROJECT_FILE, PRODUCT_COMMAND } from './product'
|
|
|
9
9
|
import { BashTool } from '../tools/BashTool/BashTool'
|
|
10
10
|
import { MACRO } from './macros'
|
|
11
11
|
|
|
12
|
-
//
|
|
13
|
-
export
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// Security policy constant matching reference implementation (va0)
|
|
18
|
-
export const SECURITY_POLICY =
|
|
19
|
-
'IMPORTANT: Assist with defensive security tasks only. Refuse to create, modify, or improve code that may be used maliciously. Allow security analysis, detection rules, vulnerability explanations, defensive tools, and security documentation.'
|
|
12
|
+
// // Security policy constant matching reference implementation
|
|
13
|
+
// export const SECURITY_POLICY =
|
|
14
|
+
// 'IMPORTANT: Assist with defensive security tasks only. Refuse to create, modify, or improve code that may be used maliciously. Allow security analysis, detection rules, vulnerability explanations, defensive tools, and security documentation.'
|
|
20
15
|
|
|
21
16
|
export function getCLISyspromptPrefix(): string {
|
|
22
|
-
return `You are ${PRODUCT_NAME},
|
|
17
|
+
return `You are ${PRODUCT_NAME}, ShareAI-lab's Agent AI CLI for terminal & coding.`
|
|
23
18
|
}
|
|
24
19
|
|
|
25
20
|
export async function getSystemPrompt(): Promise<string[]> {
|
|
26
21
|
return [
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
${SECURITY_POLICY}
|
|
30
|
-
|
|
22
|
+
`
|
|
31
23
|
You are an interactive CLI tool that helps users with software engineering tasks. Use the instructions below and the tools available to you to assist the user.
|
|
32
24
|
|
|
33
25
|
IMPORTANT: Refuse to write code or explain code that may be used maliciously; even if the user claims it is for educational purposes. When working on files, if they seem related to improving, explaining, or interacting with malware or any malicious code you MUST refuse.
|
|
@@ -164,8 +156,7 @@ Today's date: ${new Date().toLocaleDateString()}
|
|
|
164
156
|
|
|
165
157
|
export async function getAgentPrompt(): Promise<string[]> {
|
|
166
158
|
return [
|
|
167
|
-
|
|
168
|
-
|
|
159
|
+
`
|
|
169
160
|
You are an agent for ${PRODUCT_NAME}. Given the user's prompt, you should use the tools available to you to answer the user's question.
|
|
170
161
|
|
|
171
162
|
Notes:
|
package/src/services/claude.ts
CHANGED
|
@@ -1395,9 +1395,9 @@ async function queryAnthropicNative(
|
|
|
1395
1395
|
tools.map(async tool =>
|
|
1396
1396
|
({
|
|
1397
1397
|
name: tool.name,
|
|
1398
|
-
description:
|
|
1399
|
-
|
|
1400
|
-
|
|
1398
|
+
description: typeof tool.description === 'function'
|
|
1399
|
+
? await tool.description()
|
|
1400
|
+
: tool.description,
|
|
1401
1401
|
input_schema: zodToJsonSchema(tool.inputSchema),
|
|
1402
1402
|
}) as unknown as Anthropic.Beta.Messages.BetaTool,
|
|
1403
1403
|
)
|
|
@@ -1744,7 +1744,7 @@ async function queryOpenAI(
|
|
|
1744
1744
|
: '',
|
|
1745
1745
|
})
|
|
1746
1746
|
|
|
1747
|
-
systemPrompt = [getCLISyspromptPrefix()
|
|
1747
|
+
systemPrompt = [getCLISyspromptPrefix() + systemPrompt] // some openai-like providers need the entire system prompt as a single block
|
|
1748
1748
|
}
|
|
1749
1749
|
|
|
1750
1750
|
const system: TextBlockParam[] = splitSysPromptPrefix(systemPrompt).map(
|
package/src/services/openai.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { getGlobalConfig, GlobalConfig } from '../utils/config'
|
|
|
3
3
|
import { ProxyAgent, fetch, Response } from 'undici'
|
|
4
4
|
import { setSessionState, getSessionState } from '../utils/sessionState'
|
|
5
5
|
import { logEvent } from '../services/statsig'
|
|
6
|
-
import { debug as debugLogger, getCurrentRequest } from '../utils/debugLogger'
|
|
6
|
+
import { debug as debugLogger, getCurrentRequest, logAPIError } from '../utils/debugLogger'
|
|
7
7
|
|
|
8
8
|
// Helper function to calculate retry delay with exponential backoff
|
|
9
9
|
function getRetryDelay(attempt: number, retryAfter?: string | null): number {
|
|
@@ -637,9 +637,31 @@ export async function getCompletionWithProfile(
|
|
|
637
637
|
|
|
638
638
|
// If no specific handler found, log the error for debugging
|
|
639
639
|
console.log(`⚠️ Unhandled API error (${response.status}): ${errorMessage}`)
|
|
640
|
+
|
|
641
|
+
// Log API error using unified logger
|
|
642
|
+
logAPIError({
|
|
643
|
+
model: opts.model,
|
|
644
|
+
endpoint: `${baseURL}${endpoint}`,
|
|
645
|
+
status: response.status,
|
|
646
|
+
error: errorMessage,
|
|
647
|
+
request: opts,
|
|
648
|
+
response: errorData,
|
|
649
|
+
provider: provider
|
|
650
|
+
})
|
|
640
651
|
} catch (parseError) {
|
|
641
652
|
// If we can't parse the error, fall back to generic retry
|
|
642
653
|
console.log(`⚠️ Could not parse error response (${response.status})`)
|
|
654
|
+
|
|
655
|
+
// Log parse error
|
|
656
|
+
logAPIError({
|
|
657
|
+
model: opts.model,
|
|
658
|
+
endpoint: `${baseURL}${endpoint}`,
|
|
659
|
+
status: response.status,
|
|
660
|
+
error: `Could not parse error response: ${parseError.message}`,
|
|
661
|
+
request: opts,
|
|
662
|
+
response: { parseError: parseError.message },
|
|
663
|
+
provider: provider
|
|
664
|
+
})
|
|
643
665
|
}
|
|
644
666
|
|
|
645
667
|
const delayMs = getRetryDelay(attempt)
|
package/src/utils/debugLogger.ts
CHANGED
|
@@ -457,6 +457,112 @@ export function logReminderEvent(
|
|
|
457
457
|
})
|
|
458
458
|
}
|
|
459
459
|
|
|
460
|
+
// API错误日志功能
|
|
461
|
+
export function logAPIError(context: {
|
|
462
|
+
model: string
|
|
463
|
+
endpoint: string
|
|
464
|
+
status: number
|
|
465
|
+
error: any
|
|
466
|
+
request?: any
|
|
467
|
+
response?: any
|
|
468
|
+
provider?: string
|
|
469
|
+
}) {
|
|
470
|
+
const errorDir = join(paths.cache, getProjectDir(process.cwd()), 'logs', 'error', 'api')
|
|
471
|
+
|
|
472
|
+
// 确保目录存在
|
|
473
|
+
if (!existsSync(errorDir)) {
|
|
474
|
+
mkdirSync(errorDir, { recursive: true })
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// 生成文件名
|
|
478
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-')
|
|
479
|
+
const sanitizedModel = context.model.replace(/[^a-zA-Z0-9-_]/g, '_')
|
|
480
|
+
const filename = `${sanitizedModel}_${timestamp}.log`
|
|
481
|
+
const filepath = join(errorDir, filename)
|
|
482
|
+
|
|
483
|
+
// 准备完整的日志内容(文件中保存所有信息)
|
|
484
|
+
const fullLogContent = {
|
|
485
|
+
timestamp: new Date().toISOString(),
|
|
486
|
+
sessionId: SESSION_ID,
|
|
487
|
+
requestId: getCurrentRequest()?.id,
|
|
488
|
+
model: context.model,
|
|
489
|
+
provider: context.provider,
|
|
490
|
+
endpoint: context.endpoint,
|
|
491
|
+
status: context.status,
|
|
492
|
+
error: context.error,
|
|
493
|
+
request: context.request, // 保存完整请求
|
|
494
|
+
response: context.response, // 保存完整响应
|
|
495
|
+
environment: {
|
|
496
|
+
nodeVersion: process.version,
|
|
497
|
+
platform: process.platform,
|
|
498
|
+
cwd: process.cwd(),
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// 写入文件(保存完整信息)
|
|
503
|
+
try {
|
|
504
|
+
appendFileSync(filepath, JSON.stringify(fullLogContent, null, 2) + '\n')
|
|
505
|
+
appendFileSync(filepath, '='.repeat(80) + '\n\n')
|
|
506
|
+
} catch (err) {
|
|
507
|
+
console.error('Failed to write API error log:', err)
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// 在调试模式下记录到系统日志
|
|
511
|
+
if (isDebugMode()) {
|
|
512
|
+
debug.error('API_ERROR', {
|
|
513
|
+
model: context.model,
|
|
514
|
+
status: context.status,
|
|
515
|
+
error: typeof context.error === 'string' ? context.error : context.error?.message || 'Unknown error',
|
|
516
|
+
endpoint: context.endpoint,
|
|
517
|
+
logFile: filename,
|
|
518
|
+
})
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// 优雅的终端显示(仅在verbose模式下)
|
|
522
|
+
if (isVerboseMode() || isDebugVerboseMode()) {
|
|
523
|
+
console.log()
|
|
524
|
+
console.log(chalk.red('━'.repeat(60)))
|
|
525
|
+
console.log(chalk.red.bold('⚠️ API Error'))
|
|
526
|
+
console.log(chalk.red('━'.repeat(60)))
|
|
527
|
+
|
|
528
|
+
// 显示关键信息
|
|
529
|
+
console.log(chalk.white(' Model: ') + chalk.yellow(context.model))
|
|
530
|
+
console.log(chalk.white(' Status: ') + chalk.red(context.status))
|
|
531
|
+
|
|
532
|
+
// 格式化错误消息
|
|
533
|
+
let errorMessage = 'Unknown error'
|
|
534
|
+
if (typeof context.error === 'string') {
|
|
535
|
+
errorMessage = context.error
|
|
536
|
+
} else if (context.error?.message) {
|
|
537
|
+
errorMessage = context.error.message
|
|
538
|
+
} else if (context.error?.error?.message) {
|
|
539
|
+
errorMessage = context.error.error.message
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// 错误消息换行显示
|
|
543
|
+
console.log(chalk.white(' Error: ') + chalk.red(errorMessage))
|
|
544
|
+
|
|
545
|
+
// 如果有响应体,显示格式化的响应
|
|
546
|
+
if (context.response) {
|
|
547
|
+
console.log()
|
|
548
|
+
console.log(chalk.gray(' Response:'))
|
|
549
|
+
const responseStr = typeof context.response === 'string'
|
|
550
|
+
? context.response
|
|
551
|
+
: JSON.stringify(context.response, null, 2)
|
|
552
|
+
|
|
553
|
+
// 缩进显示响应内容
|
|
554
|
+
responseStr.split('\n').forEach(line => {
|
|
555
|
+
console.log(chalk.gray(' ' + line))
|
|
556
|
+
})
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
console.log()
|
|
560
|
+
console.log(chalk.dim(` 📁 Full log: ~/.kode/logs/error/api/${filename}`))
|
|
561
|
+
console.log(chalk.red('━'.repeat(60)))
|
|
562
|
+
console.log()
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
460
566
|
// 新增:LLM 交互核心调试信息
|
|
461
567
|
export function logLLMInteraction(context: {
|
|
462
568
|
systemPrompt: string
|