@raolin2025/claude-code-node 2.2.0 → 2.2.2

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raolin2025/claude-code-node",
3
- "version": "2.2.0",
3
+ "version": "2.2.2",
4
4
  "description": "Node.js AI Code Agent CLI - Zero dependencies, pure JavaScript, security hardened, multi-channel notifications",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -114,12 +114,12 @@ export class QueryEngine {
114
114
  // 没有工具调用 → 最终回复
115
115
  if (!response.toolCalls || response.toolCalls.length === 0) {
116
116
  finalResponse = response.content
117
- this.state.messages.push(new AssistantMessage(response.content))
117
+ this.state.messages.push(new AssistantMessage(response.content, [], response.reasoningContent))
118
118
  break
119
119
  }
120
120
 
121
121
  // 有工具调用 → 记录 assistant 消息(含 tool_calls)
122
- this.state.messages.push(new AssistantMessage(response.content, response.toolCalls))
122
+ this.state.messages.push(new AssistantMessage(response.content, response.toolCalls, response.reasoningContent))
123
123
 
124
124
  // 执行工具
125
125
  const toolResults = await this._executeToolCalls(response.toolCalls)
@@ -168,21 +168,27 @@ export class QueryEngine {
168
168
  } else if (msg.role === 'user') {
169
169
  request.push({ role: 'user', content: this._formatContent(msg.content) })
170
170
  } else if (msg.role === 'assistant') {
171
- // tool_calls 的 assistant 消息:OpenAI 格式
171
+ // 构建 assistant 消息基础
172
+ const asstMsg = {
173
+ role: 'assistant',
174
+ content: msg.content || null,
175
+ }
176
+
177
+ // DeepSeek thinking mode: 必须传回 reasoning_content (tool call 场景)
178
+ if (msg.reasoningContent) {
179
+ asstMsg.reasoning_content = msg.reasoningContent
180
+ }
181
+
182
+ // tool_calls
172
183
  if (msg.toolCalls && msg.toolCalls.length > 0) {
173
- request.push({
174
- role: 'assistant',
175
- content: msg.content || null,
176
- tool_calls: msg.toolCalls.map(tc => ({
177
- id: tc.id,
178
- type: 'function',
179
- function: { name: tc.name, arguments: typeof tc.input === 'string' ? tc.input : JSON.stringify(tc.input) },
180
- })),
181
- })
182
- } else {
183
- // 纯文本 assistant 消息
184
- request.push({ role: 'assistant', content: this._formatContent(msg.content) })
184
+ asstMsg.tool_calls = msg.toolCalls.map(tc => ({
185
+ id: tc.id,
186
+ type: 'function',
187
+ function: { name: tc.name, arguments: typeof tc.input === 'string' ? tc.input : JSON.stringify(tc.input) },
188
+ }))
185
189
  }
190
+
191
+ request.push(asstMsg)
186
192
  } else if (msg.role === 'tool') {
187
193
  // tool 结果消息 — 直接透传
188
194
  request.push({
@@ -287,6 +293,8 @@ export class QueryEngine {
287
293
  }))
288
294
 
289
295
  const useStream = !this.config.noStream
296
+ const modelLower = this.config.model.toLowerCase()
297
+
290
298
  const body = {
291
299
  model: this.config.model,
292
300
  messages,
@@ -295,6 +303,13 @@ export class QueryEngine {
295
303
  ...(useStream && { stream: true }),
296
304
  }
297
305
 
306
+ // DeepSeek V4: 默认启用 thinking mode → 要求回传 reasoning_content
307
+ // 这里显式关闭,避免 tool call 场景下的 400 错误
308
+ // thinking 参数是 DeepSeek 私有扩展,直接放在 body 顶层
309
+ if (modelLower.startsWith('deepseek-v')) {
310
+ body.thinking = { type: 'disabled' }
311
+ }
312
+
298
313
  const url = apiBase.replace(/\/+$/, '') + '/chat/completions'
299
314
 
300
315
  // H3 修复:SSRF 防护 — 在 fetch 之前检查 API 主机名
@@ -16,6 +16,7 @@ export async function* parseStream(response) {
16
16
 
17
17
  const result = {
18
18
  content: '',
19
+ reasoningContent: '',
19
20
  toolCalls: [],
20
21
  usage: { input_tokens: 0, output_tokens: 0 },
21
22
  }
@@ -40,6 +41,11 @@ export async function* parseStream(response) {
40
41
 
41
42
  if (!delta) continue
42
43
 
44
+ // reasoning_content (DeepSeek thinking mode)
45
+ if (delta.reasoning_content) {
46
+ result.reasoningContent += delta.reasoning_content
47
+ }
48
+
43
49
  // 文本
44
50
  if (delta.content) {
45
51
  result.content += delta.content
@@ -95,13 +101,17 @@ export async function* parseStream(response) {
95
101
  * 解析非流式响应
96
102
  */
97
103
  export function parseNonStreamResponse(data) {
98
- const result = { content: '', toolCalls: [], usage: {} }
104
+ const result = { content: '', reasoningContent: '', toolCalls: [], usage: {} }
99
105
  const choice = data.choices?.[0]
100
106
 
101
107
  if (choice?.message?.content) {
102
108
  result.content = choice.message.content
103
109
  }
104
110
 
111
+ if (choice?.message?.reasoning_content) {
112
+ result.reasoningContent = choice.message.reasoning_content
113
+ }
114
+
105
115
  for (const tc of (choice?.message?.tool_calls || [])) {
106
116
  let input = {}
107
117
  try { input = JSON.parse(tc.function.arguments) } catch {}
@@ -31,9 +31,11 @@ export class UserMessage extends Message {
31
31
 
32
32
  export class AssistantMessage extends Message {
33
33
  toolCalls
34
- constructor(content, toolCalls = []) {
34
+ reasoningContent
35
+ constructor(content, toolCalls = [], reasoningContent = '') {
35
36
  super(Role.ASSISTANT, content)
36
37
  this.toolCalls = toolCalls
38
+ this.reasoningContent = reasoningContent
37
39
  }
38
40
  }
39
41