@shareai-lab/kode 1.0.71 → 1.0.73

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.
Files changed (106) hide show
  1. package/README.md +142 -1
  2. package/README.zh-CN.md +47 -1
  3. package/package.json +5 -1
  4. package/src/ProjectOnboarding.tsx +47 -29
  5. package/src/Tool.ts +33 -4
  6. package/src/commands/agents.tsx +3401 -0
  7. package/src/commands/help.tsx +2 -2
  8. package/src/commands/resume.tsx +2 -1
  9. package/src/commands/terminalSetup.ts +4 -4
  10. package/src/commands.ts +3 -0
  11. package/src/components/ApproveApiKey.tsx +1 -1
  12. package/src/components/Config.tsx +10 -6
  13. package/src/components/ConsoleOAuthFlow.tsx +5 -4
  14. package/src/components/CustomSelect/select-option.tsx +28 -2
  15. package/src/components/CustomSelect/select.tsx +14 -5
  16. package/src/components/CustomSelect/theme.ts +45 -0
  17. package/src/components/Help.tsx +4 -4
  18. package/src/components/InvalidConfigDialog.tsx +1 -1
  19. package/src/components/LogSelector.tsx +1 -1
  20. package/src/components/MCPServerApprovalDialog.tsx +1 -1
  21. package/src/components/Message.tsx +2 -0
  22. package/src/components/ModelListManager.tsx +10 -6
  23. package/src/components/ModelSelector.tsx +201 -23
  24. package/src/components/ModelStatusDisplay.tsx +7 -5
  25. package/src/components/PromptInput.tsx +117 -87
  26. package/src/components/SentryErrorBoundary.ts +3 -3
  27. package/src/components/StickerRequestForm.tsx +16 -0
  28. package/src/components/StructuredDiff.tsx +36 -29
  29. package/src/components/TextInput.tsx +13 -0
  30. package/src/components/TodoItem.tsx +11 -0
  31. package/src/components/TrustDialog.tsx +1 -1
  32. package/src/components/messages/AssistantLocalCommandOutputMessage.tsx +5 -1
  33. package/src/components/messages/AssistantToolUseMessage.tsx +14 -4
  34. package/src/components/messages/TaskProgressMessage.tsx +32 -0
  35. package/src/components/messages/TaskToolMessage.tsx +58 -0
  36. package/src/components/permissions/FallbackPermissionRequest.tsx +2 -4
  37. package/src/components/permissions/FileEditPermissionRequest/FileEditPermissionRequest.tsx +1 -1
  38. package/src/components/permissions/FileEditPermissionRequest/FileEditToolDiff.tsx +5 -3
  39. package/src/components/permissions/FileWritePermissionRequest/FileWritePermissionRequest.tsx +1 -1
  40. package/src/components/permissions/FileWritePermissionRequest/FileWriteToolDiff.tsx +5 -3
  41. package/src/components/permissions/FilesystemPermissionRequest/FilesystemPermissionRequest.tsx +2 -4
  42. package/src/components/permissions/PermissionRequest.tsx +3 -5
  43. package/src/constants/macros.ts +2 -0
  44. package/src/constants/modelCapabilities.ts +179 -0
  45. package/src/constants/models.ts +90 -0
  46. package/src/constants/product.ts +1 -1
  47. package/src/context.ts +7 -7
  48. package/src/entrypoints/cli.tsx +23 -3
  49. package/src/entrypoints/mcp.ts +10 -10
  50. package/src/hooks/useCanUseTool.ts +1 -1
  51. package/src/hooks/useTextInput.ts +5 -2
  52. package/src/hooks/useUnifiedCompletion.ts +1404 -0
  53. package/src/messages.ts +1 -0
  54. package/src/query.ts +3 -0
  55. package/src/screens/ConfigureNpmPrefix.tsx +1 -1
  56. package/src/screens/Doctor.tsx +1 -1
  57. package/src/screens/REPL.tsx +15 -9
  58. package/src/services/adapters/base.ts +38 -0
  59. package/src/services/adapters/chatCompletions.ts +90 -0
  60. package/src/services/adapters/responsesAPI.ts +170 -0
  61. package/src/services/claude.ts +198 -62
  62. package/src/services/customCommands.ts +43 -22
  63. package/src/services/gpt5ConnectionTest.ts +340 -0
  64. package/src/services/mcpClient.ts +1 -1
  65. package/src/services/mentionProcessor.ts +273 -0
  66. package/src/services/modelAdapterFactory.ts +69 -0
  67. package/src/services/openai.ts +521 -12
  68. package/src/services/responseStateManager.ts +90 -0
  69. package/src/services/systemReminder.ts +113 -12
  70. package/src/test/testAdapters.ts +96 -0
  71. package/src/tools/AskExpertModelTool/AskExpertModelTool.tsx +120 -56
  72. package/src/tools/BashTool/BashTool.tsx +4 -31
  73. package/src/tools/BashTool/BashToolResultMessage.tsx +1 -1
  74. package/src/tools/BashTool/OutputLine.tsx +1 -0
  75. package/src/tools/FileEditTool/FileEditTool.tsx +4 -5
  76. package/src/tools/FileReadTool/FileReadTool.tsx +43 -10
  77. package/src/tools/MCPTool/MCPTool.tsx +2 -1
  78. package/src/tools/MultiEditTool/MultiEditTool.tsx +2 -2
  79. package/src/tools/NotebookReadTool/NotebookReadTool.tsx +15 -23
  80. package/src/tools/StickerRequestTool/StickerRequestTool.tsx +1 -1
  81. package/src/tools/TaskTool/TaskTool.tsx +170 -86
  82. package/src/tools/TaskTool/prompt.ts +61 -25
  83. package/src/tools/ThinkTool/ThinkTool.tsx +1 -3
  84. package/src/tools/TodoWriteTool/TodoWriteTool.tsx +11 -10
  85. package/src/tools/lsTool/lsTool.tsx +5 -2
  86. package/src/tools.ts +16 -16
  87. package/src/types/conversation.ts +51 -0
  88. package/src/types/logs.ts +58 -0
  89. package/src/types/modelCapabilities.ts +64 -0
  90. package/src/types/notebook.ts +87 -0
  91. package/src/utils/advancedFuzzyMatcher.ts +290 -0
  92. package/src/utils/agentLoader.ts +284 -0
  93. package/src/utils/ask.tsx +1 -0
  94. package/src/utils/commands.ts +1 -1
  95. package/src/utils/commonUnixCommands.ts +161 -0
  96. package/src/utils/config.ts +173 -2
  97. package/src/utils/conversationRecovery.ts +1 -0
  98. package/src/utils/debugLogger.ts +13 -13
  99. package/src/utils/exampleCommands.ts +1 -0
  100. package/src/utils/fuzzyMatcher.ts +328 -0
  101. package/src/utils/messages.tsx +6 -5
  102. package/src/utils/responseState.ts +23 -0
  103. package/src/utils/secureFile.ts +559 -0
  104. package/src/utils/terminal.ts +1 -0
  105. package/src/utils/theme.ts +11 -0
  106. package/src/hooks/useSlashCommandTypeahead.ts +0 -137
@@ -4,7 +4,7 @@ import { AnthropicBedrock } from '@anthropic-ai/bedrock-sdk'
4
4
  import { AnthropicVertex } from '@anthropic-ai/vertex-sdk'
5
5
  import type { BetaUsage } from '@anthropic-ai/sdk/resources/beta/messages/messages.mjs'
6
6
  import chalk from 'chalk'
7
- import { createHash, randomUUID } from 'crypto'
7
+ import { createHash, randomUUID, UUID } from 'crypto'
8
8
  import 'dotenv/config'
9
9
 
10
10
  import { addToTotalCost } from '../cost-tracker'
@@ -15,6 +15,7 @@ import {
15
15
  getAnthropicApiKey,
16
16
  getOrCreateUserID,
17
17
  getGlobalConfig,
18
+ ModelProfile,
18
19
  } from '../utils/config'
19
20
  import { getProjectDocs } from '../context'
20
21
  import { logError, SESSION_ID } from '../utils/log'
@@ -41,6 +42,10 @@ import {
41
42
  import { getModelManager } from '../utils/model'
42
43
  import { zodToJsonSchema } from 'zod-to-json-schema'
43
44
  import type { BetaMessageStream } from '@anthropic-ai/sdk/lib/BetaMessageStream.mjs'
45
+ import { ModelAdapterFactory } from './modelAdapterFactory'
46
+ import { UnifiedRequestParams } from '../types/modelCapabilities'
47
+ import { responseStateManager, getConversationId } from './responseStateManager'
48
+ import type { ToolUseContext } from '../Tool'
44
49
  import type {
45
50
  Message as APIMessage,
46
51
  MessageParam,
@@ -53,10 +58,15 @@ import OpenAI from 'openai'
53
58
  import type { ChatCompletionStream } from 'openai/lib/ChatCompletionStream'
54
59
  import { ContentBlock } from '@anthropic-ai/sdk/resources/messages/messages'
55
60
  import { nanoid } from 'nanoid'
56
- import { getCompletion, getCompletionWithProfile } from './openai'
61
+ import { getCompletionWithProfile, getGPT5CompletionWithProfile } from './openai'
57
62
  import { getReasoningEffort } from '../utils/thinking'
58
63
  import { generateSystemReminders } from './systemReminder'
59
64
 
65
+ // Helper function to check if a model is GPT-5
66
+ function isGPT5Model(modelName: string): boolean {
67
+ return modelName.startsWith('gpt-5')
68
+ }
69
+
60
70
  // Helper function to extract model configuration for debug logging
61
71
  function getModelConfigForDebug(model: string): {
62
72
  modelName: string
@@ -71,7 +81,7 @@ function getModelConfigForDebug(model: string): {
71
81
  const config = getGlobalConfig()
72
82
  const modelManager = getModelManager()
73
83
 
74
- // 🔧 Fix: Use ModelManager to get the actual current model profile
84
+
75
85
  const modelProfile = modelManager.getModel('main')
76
86
 
77
87
  let apiKeyStatus: 'configured' | 'missing' | 'invalid' = 'missing'
@@ -79,7 +89,7 @@ function getModelConfigForDebug(model: string): {
79
89
  let maxTokens: number | undefined
80
90
  let reasoningEffort: string | undefined
81
91
 
82
- // 🔧 Fix: Use ModelProfile configuration exclusively
92
+
83
93
  if (modelProfile) {
84
94
  apiKeyStatus = modelProfile.apiKey ? 'configured' : 'missing'
85
95
  baseURL = modelProfile.baseURL
@@ -310,7 +320,7 @@ async function withRetry<T>(
310
320
  ) {
311
321
  throw error
312
322
  }
313
- // 🔧 CRITICAL FIX: Check abort signal BEFORE showing retry message
323
+
314
324
  if (options.signal?.aborted) {
315
325
  throw new Error('Request cancelled by user')
316
326
  }
@@ -429,7 +439,7 @@ export async function verifyApiKey(
429
439
  'Content-Type': 'application/json',
430
440
  }
431
441
 
432
- // 🔧 Fix: Proper URL construction for verification
442
+
433
443
  if (!baseURL) {
434
444
  console.warn(
435
445
  'No baseURL provided for non-Anthropic provider verification',
@@ -637,7 +647,7 @@ function messageReducer(
637
647
  }
638
648
  async function handleMessageStream(
639
649
  stream: ChatCompletionStream,
640
- signal?: AbortSignal, // 🔧 Add AbortSignal support to stream handler
650
+ signal?: AbortSignal,
641
651
  ): Promise<OpenAI.ChatCompletion> {
642
652
  const streamStartTime = Date.now()
643
653
  let ttftMs: number | undefined
@@ -653,7 +663,7 @@ async function handleMessageStream(
653
663
  let id, model, created, object, usage
654
664
  try {
655
665
  for await (const chunk of stream) {
656
- // 🔧 CRITICAL FIX: Check abort signal in OpenAI streaming loop
666
+
657
667
  if (signal?.aborted) {
658
668
  debugLogger.flow('OPENAI_STREAM_ABORTED', {
659
669
  chunkCount,
@@ -756,7 +766,7 @@ async function handleMessageStream(
756
766
  }
757
767
  }
758
768
 
759
- function convertOpenAIResponseToAnthropic(response: OpenAI.ChatCompletion) {
769
+ function convertOpenAIResponseToAnthropic(response: OpenAI.ChatCompletion, tools?: Tool[]) {
760
770
  let contentBlocks: ContentBlock[] = []
761
771
  const message = response.choices?.[0]?.message
762
772
  if (!message) {
@@ -775,12 +785,12 @@ function convertOpenAIResponseToAnthropic(response: OpenAI.ChatCompletion) {
775
785
  if (message?.tool_calls) {
776
786
  for (const toolCall of message.tool_calls) {
777
787
  const tool = toolCall.function
778
- const toolName = tool.name
788
+ const toolName = tool?.name
779
789
  let toolArgs = {}
780
790
  try {
781
- toolArgs = JSON.parse(tool.arguments)
791
+ toolArgs = tool?.arguments ? JSON.parse(tool.arguments) : {}
782
792
  } catch (e) {
783
- // console.log(e)
793
+ // Invalid JSON in tool arguments
784
794
  }
785
795
 
786
796
  contentBlocks.push({
@@ -825,6 +835,7 @@ function convertOpenAIResponseToAnthropic(response: OpenAI.ChatCompletion) {
825
835
  usage: response.usage,
826
836
  }
827
837
 
838
+
828
839
  return finalMessage
829
840
  }
830
841
 
@@ -1047,9 +1058,10 @@ export async function queryLLM(
1047
1058
  safeMode: boolean
1048
1059
  model: string | import('../utils/config').ModelPointerType
1049
1060
  prependCLISysprompt: boolean
1061
+ toolUseContext?: ToolUseContext
1050
1062
  },
1051
1063
  ): Promise<AssistantMessage> {
1052
- // 🔧 统一的模型解析:支持指针、model ID 和真实模型名称
1064
+
1053
1065
  const modelManager = getModelManager()
1054
1066
  const modelResolution = modelManager.resolveModelWithInfo(options.model)
1055
1067
 
@@ -1062,12 +1074,25 @@ export async function queryLLM(
1062
1074
  const modelProfile = modelResolution.profile
1063
1075
  const resolvedModel = modelProfile.modelName
1064
1076
 
1077
+ // Initialize response state if toolUseContext is provided
1078
+ const toolUseContext = options.toolUseContext
1079
+ if (toolUseContext && !toolUseContext.responseState) {
1080
+ const conversationId = getConversationId(toolUseContext.agentId, toolUseContext.messageId)
1081
+ const previousResponseId = responseStateManager.getPreviousResponseId(conversationId)
1082
+
1083
+ toolUseContext.responseState = {
1084
+ previousResponseId,
1085
+ conversationId
1086
+ }
1087
+ }
1088
+
1065
1089
  debugLogger.api('MODEL_RESOLVED', {
1066
1090
  inputParam: options.model,
1067
- resolvedModelName: modelProfile.modelName,
1068
1091
  resolvedModelName: resolvedModel,
1069
1092
  provider: modelProfile.provider,
1070
1093
  isPointer: ['main', 'task', 'reasoning', 'quick'].includes(options.model),
1094
+ hasResponseState: !!toolUseContext?.responseState,
1095
+ conversationId: toolUseContext?.responseState?.conversationId,
1071
1096
  requestId: getCurrentRequest()?.id,
1072
1097
  })
1073
1098
 
@@ -1078,7 +1103,7 @@ export async function queryLLM(
1078
1103
  toolCount: tools.length,
1079
1104
  model: resolvedModel,
1080
1105
  originalModelParam: options.model,
1081
- requestId: currentRequest?.id,
1106
+ requestId: getCurrentRequest()?.id,
1082
1107
  })
1083
1108
 
1084
1109
  markPhase('LLM_CALL')
@@ -1091,7 +1116,7 @@ export async function queryLLM(
1091
1116
  maxThinkingTokens,
1092
1117
  tools,
1093
1118
  signal,
1094
- { ...options, model: resolvedModel, modelProfile }, // Pass resolved ModelProfile
1119
+ { ...options, model: resolvedModel, modelProfile, toolUseContext }, // Pass resolved ModelProfile and toolUseContext
1095
1120
  ),
1096
1121
  )
1097
1122
 
@@ -1099,9 +1124,23 @@ export async function queryLLM(
1099
1124
  costUSD: result.costUSD,
1100
1125
  durationMs: result.durationMs,
1101
1126
  responseLength: result.message.content?.length || 0,
1102
- requestId: currentRequest?.id,
1127
+ requestId: getCurrentRequest()?.id,
1103
1128
  })
1104
1129
 
1130
+ // Update response state for GPT-5 Responses API continuation
1131
+ if (toolUseContext?.responseState?.conversationId && result.responseId) {
1132
+ responseStateManager.setPreviousResponseId(
1133
+ toolUseContext.responseState.conversationId,
1134
+ result.responseId
1135
+ )
1136
+
1137
+ debugLogger.api('RESPONSE_STATE_UPDATED', {
1138
+ conversationId: toolUseContext.responseState.conversationId,
1139
+ responseId: result.responseId,
1140
+ requestId: getCurrentRequest()?.id,
1141
+ })
1142
+ }
1143
+
1105
1144
  return result
1106
1145
  } catch (error) {
1107
1146
  // 使用错误诊断系统记录 LLM 相关错误
@@ -1131,6 +1170,24 @@ export function formatSystemPromptWithContext(
1131
1170
  const enhancedPrompt = [...systemPrompt]
1132
1171
  let reminders = ''
1133
1172
 
1173
+ // Step 0: Add GPT-5 Agent persistence support for coding tasks
1174
+ const modelManager = getModelManager()
1175
+ const modelProfile = modelManager.getModel('main')
1176
+ if (modelProfile && isGPT5Model(modelProfile.modelName)) {
1177
+ // Add coding-specific persistence instructions based on GPT-5 documentation
1178
+ const persistencePrompts = [
1179
+ "\n# Agent Persistence for Long-Running Coding Tasks",
1180
+ "You are working on a coding project that may involve multiple steps and iterations. Please maintain context and continuity throughout the session:",
1181
+ "- Remember architectural decisions and design patterns established earlier",
1182
+ "- Keep track of file modifications and their relationships",
1183
+ "- Maintain awareness of the overall project structure and goals",
1184
+ "- Reference previous implementations when making related changes",
1185
+ "- Ensure consistency with existing code style and conventions",
1186
+ "- Build incrementally on previous work rather than starting from scratch"
1187
+ ]
1188
+ enhancedPrompt.push(...persistencePrompts)
1189
+ }
1190
+
1134
1191
  // 只有当上下文存在时才处理
1135
1192
  const hasContext = Object.entries(context).length > 0
1136
1193
 
@@ -1185,12 +1242,14 @@ async function queryLLMWithPromptCaching(
1185
1242
  model: string
1186
1243
  prependCLISysprompt: boolean
1187
1244
  modelProfile?: ModelProfile | null
1245
+ toolUseContext?: ToolUseContext
1188
1246
  },
1189
1247
  ): Promise<AssistantMessage> {
1190
1248
  const config = getGlobalConfig()
1191
1249
  const modelManager = getModelManager()
1250
+ const toolUseContext = options.toolUseContext
1251
+
1192
1252
 
1193
- // 🔧 Fix: 使用传入的ModelProfile,而不是硬编码的'main'指针
1194
1253
  const modelProfile = options.modelProfile || modelManager.getModel('main')
1195
1254
  let provider: string
1196
1255
 
@@ -1212,7 +1271,7 @@ async function queryLLMWithPromptCaching(
1212
1271
  maxThinkingTokens,
1213
1272
  tools,
1214
1273
  signal,
1215
- { ...options, modelProfile },
1274
+ { ...options, modelProfile, toolUseContext },
1216
1275
  )
1217
1276
  }
1218
1277
 
@@ -1220,6 +1279,7 @@ async function queryLLMWithPromptCaching(
1220
1279
  return queryOpenAI(messages, systemPrompt, maxThinkingTokens, tools, signal, {
1221
1280
  ...options,
1222
1281
  modelProfile,
1282
+ toolUseContext,
1223
1283
  })
1224
1284
  }
1225
1285
 
@@ -1234,12 +1294,14 @@ async function queryAnthropicNative(
1234
1294
  model: string
1235
1295
  prependCLISysprompt: boolean
1236
1296
  modelProfile?: ModelProfile | null
1297
+ toolUseContext?: ToolUseContext
1237
1298
  },
1238
1299
  ): Promise<AssistantMessage> {
1239
1300
  const config = getGlobalConfig()
1240
1301
  const modelManager = getModelManager()
1302
+ const toolUseContext = options?.toolUseContext
1303
+
1241
1304
 
1242
- // 🔧 Fix: 使用传入的ModelProfile,而不是硬编码的'main'指针
1243
1305
  const modelProfile = options?.modelProfile || modelManager.getModel('main')
1244
1306
  let anthropic: Anthropic | AnthropicBedrock | AnthropicVertex
1245
1307
  let model: string
@@ -1255,7 +1317,7 @@ async function queryAnthropicNative(
1255
1317
  modelProfileBaseURL: modelProfile?.baseURL,
1256
1318
  modelProfileApiKeyExists: !!modelProfile?.apiKey,
1257
1319
  optionsModel: options?.model,
1258
- requestId: currentRequest?.id,
1320
+ requestId: getCurrentRequest()?.id,
1259
1321
  })
1260
1322
 
1261
1323
  if (modelProfile) {
@@ -1296,7 +1358,7 @@ async function queryAnthropicNative(
1296
1358
  modelProfileExists: !!modelProfile,
1297
1359
  modelProfileModelName: modelProfile?.modelName,
1298
1360
  requestedModel: options?.model,
1299
- requestId: currentRequest?.id,
1361
+ requestId: getCurrentRequest()?.id,
1300
1362
  }
1301
1363
  debugLogger.error('ANTHROPIC_FALLBACK_ERROR', errorDetails)
1302
1364
  throw new Error(
@@ -1329,13 +1391,16 @@ async function queryAnthropicNative(
1329
1391
  }),
1330
1392
  )
1331
1393
 
1332
- const toolSchemas = tools.map(
1333
- tool =>
1394
+ const toolSchemas = await Promise.all(
1395
+ tools.map(async tool =>
1334
1396
  ({
1335
1397
  name: tool.name,
1336
- description: tool.description,
1398
+ description: typeof tool.description === 'function'
1399
+ ? await tool.description()
1400
+ : tool.description,
1337
1401
  input_schema: zodToJsonSchema(tool.inputSchema),
1338
- }) as Anthropic.Beta.Tools.Tool,
1402
+ }) as unknown as Anthropic.Beta.Messages.BetaTool,
1403
+ )
1339
1404
  )
1340
1405
 
1341
1406
  const anthropicMessages = addCacheBreakpoints(messages)
@@ -1368,7 +1433,7 @@ async function queryAnthropicNative(
1368
1433
  }
1369
1434
 
1370
1435
  if (maxThinkingTokens > 0) {
1371
- params.extra_headers = {
1436
+ ;(params as any).extra_headers = {
1372
1437
  'anthropic-beta': 'max-tokens-3-5-sonnet-2024-07-15',
1373
1438
  }
1374
1439
  ;(params as any).thinking = { max_tokens: maxThinkingTokens }
@@ -1395,7 +1460,7 @@ async function queryAnthropicNative(
1395
1460
  })
1396
1461
 
1397
1462
  if (config.stream) {
1398
- // 🔧 CRITICAL FIX: Connect AbortSignal to Anthropic API call
1463
+
1399
1464
  const stream = await anthropic.beta.messages.create({
1400
1465
  ...params,
1401
1466
  stream: true,
@@ -1403,7 +1468,7 @@ async function queryAnthropicNative(
1403
1468
  signal: signal // ← CRITICAL: Connect the AbortSignal to API call
1404
1469
  })
1405
1470
 
1406
- let finalResponse: Anthropic.Beta.Messages.Message | null = null
1471
+ let finalResponse: any | null = null
1407
1472
  let messageStartEvent: any = null
1408
1473
  const contentBlocks: any[] = []
1409
1474
  let usage: any = null
@@ -1411,7 +1476,7 @@ async function queryAnthropicNative(
1411
1476
  let stopSequence: string | null = null
1412
1477
 
1413
1478
  for await (const event of stream) {
1414
- // 🔧 CRITICAL FIX: Check abort signal in streaming loop
1479
+
1415
1480
  if (signal.aborted) {
1416
1481
  debugLogger.flow('STREAM_ABORTED', {
1417
1482
  eventType: event.type,
@@ -1485,12 +1550,12 @@ async function queryAnthropicNative(
1485
1550
  modelProfileName: modelProfile?.name,
1486
1551
  })
1487
1552
 
1488
- // 🔧 CRITICAL FIX: Connect AbortSignal to non-streaming API call
1553
+
1489
1554
  return await anthropic.beta.messages.create(params, {
1490
1555
  signal: signal // ← CRITICAL: Connect the AbortSignal to API call
1491
1556
  })
1492
1557
  }
1493
- }, { signal }) // 🔧 CRITICAL FIX: Pass AbortSignal to withRetry
1558
+ }, { signal })
1494
1559
 
1495
1560
  const ttftMs = start - Date.now()
1496
1561
  const durationMs = Date.now() - startIncludingRetries
@@ -1525,7 +1590,6 @@ async function queryAnthropicNative(
1525
1590
  },
1526
1591
  type: 'assistant',
1527
1592
  uuid: nanoid() as UUID,
1528
- ttftMs,
1529
1593
  durationMs,
1530
1594
  costUSD: 0, // Will be calculated below
1531
1595
  }
@@ -1552,7 +1616,6 @@ async function queryAnthropicNative(
1552
1616
  end: Date.now(),
1553
1617
  },
1554
1618
  apiFormat: 'anthropic',
1555
- modelConfig: getModelConfigForDebug(model),
1556
1619
  })
1557
1620
 
1558
1621
  // Calculate cost using native Anthropic usage data
@@ -1571,18 +1634,18 @@ async function queryAnthropicNative(
1571
1634
  (getModelInputTokenCostUSD(model) * 0.1) // Cache reads are 10% of input cost
1572
1635
 
1573
1636
  assistantMessage.costUSD = costUSD
1574
- addToTotalCost(costUSD)
1637
+ addToTotalCost(costUSD, durationMs)
1575
1638
 
1576
1639
  logEvent('api_response_anthropic_native', {
1577
1640
  model,
1578
- input_tokens: inputTokens,
1579
- output_tokens: outputTokens,
1580
- cache_creation_input_tokens: cacheCreationInputTokens,
1581
- cache_read_input_tokens: cacheReadInputTokens,
1582
- cost_usd: costUSD,
1583
- duration_ms: durationMs,
1584
- ttft_ms: ttftMs,
1585
- attempt_number: attemptNumber,
1641
+ input_tokens: String(inputTokens),
1642
+ output_tokens: String(outputTokens),
1643
+ cache_creation_input_tokens: String(cacheCreationInputTokens),
1644
+ cache_read_input_tokens: String(cacheReadInputTokens),
1645
+ cost_usd: String(costUSD),
1646
+ duration_ms: String(durationMs),
1647
+ ttft_ms: String(ttftMs),
1648
+ attempt_number: String(attemptNumber),
1586
1649
  })
1587
1650
 
1588
1651
  return assistantMessage
@@ -1639,12 +1702,14 @@ async function queryOpenAI(
1639
1702
  model: string
1640
1703
  prependCLISysprompt: boolean
1641
1704
  modelProfile?: ModelProfile | null
1705
+ toolUseContext?: ToolUseContext
1642
1706
  },
1643
1707
  ): Promise<AssistantMessage> {
1644
1708
  const config = getGlobalConfig()
1645
1709
  const modelManager = getModelManager()
1710
+ const toolUseContext = options?.toolUseContext
1711
+
1646
1712
 
1647
- // 🔧 Fix: 使用传入的ModelProfile,而不是硬编码的'main'指针
1648
1713
  const modelProfile = options?.modelProfile || modelManager.getModel('main')
1649
1714
  let model: string
1650
1715
 
@@ -1659,7 +1724,7 @@ async function queryOpenAI(
1659
1724
  modelProfileBaseURL: modelProfile?.baseURL,
1660
1725
  modelProfileApiKeyExists: !!modelProfile?.apiKey,
1661
1726
  optionsModel: options?.model,
1662
- requestId: currentRequest?.id,
1727
+ requestId: getCurrentRequest()?.id,
1663
1728
  })
1664
1729
 
1665
1730
  if (modelProfile) {
@@ -1739,11 +1804,17 @@ async function queryOpenAI(
1739
1804
  response = await withRetry(async attempt => {
1740
1805
  attemptNumber = attempt
1741
1806
  start = Date.now()
1807
+ // 🔥 GPT-5 Enhanced Parameter Construction
1808
+ const maxTokens = getMaxTokensFromProfile(modelProfile)
1809
+ const isGPT5 = isGPT5Model(model)
1810
+
1742
1811
  const opts: OpenAI.ChatCompletionCreateParams = {
1743
1812
  model,
1744
- max_tokens: getMaxTokensFromProfile(modelProfile),
1813
+
1814
+ ...(isGPT5 ? { max_completion_tokens: maxTokens } : { max_tokens: maxTokens }),
1745
1815
  messages: [...openaiSystem, ...openaiMessages],
1746
- temperature: MAIN_QUERY_TEMPERATURE,
1816
+
1817
+ temperature: isGPT5 ? 1 : MAIN_QUERY_TEMPERATURE,
1747
1818
  }
1748
1819
  if (config.stream) {
1749
1820
  ;(opts as OpenAI.ChatCompletionCreateParams).stream = true
@@ -1764,7 +1835,7 @@ async function queryOpenAI(
1764
1835
  opts.reasoning_effort = reasoningEffort
1765
1836
  }
1766
1837
 
1767
- // 🔧 Fix: 如果有ModelProfile配置,直接使用它 (更宽松的条件)
1838
+
1768
1839
  if (modelProfile && modelProfile.modelName) {
1769
1840
  debugLogger.api('USING_MODEL_PROFILE_PATH', {
1770
1841
  modelProfileName: modelProfile.modelName,
@@ -1772,19 +1843,85 @@ async function queryOpenAI(
1772
1843
  provider: modelProfile.provider,
1773
1844
  baseURL: modelProfile.baseURL,
1774
1845
  apiKeyExists: !!modelProfile.apiKey,
1775
- requestId: currentRequest?.id,
1846
+ requestId: getCurrentRequest()?.id,
1776
1847
  })
1777
1848
 
1778
- const s = await getCompletionWithProfile(modelProfile, opts, 0, 10, signal) // 🔧 CRITICAL FIX: Pass AbortSignal to OpenAI calls
1779
- let finalResponse
1780
- if (opts.stream) {
1781
- finalResponse = await handleMessageStream(s as ChatCompletionStream, signal) // 🔧 Pass AbortSignal to stream handler
1849
+ // Enable new adapter system with environment variable
1850
+ const USE_NEW_ADAPTER_SYSTEM = process.env.USE_NEW_ADAPTERS !== 'false'
1851
+
1852
+ if (USE_NEW_ADAPTER_SYSTEM) {
1853
+ // New adapter system
1854
+ const adapter = ModelAdapterFactory.createAdapter(modelProfile)
1855
+
1856
+ // Build unified request parameters
1857
+ const unifiedParams: UnifiedRequestParams = {
1858
+ messages: openaiMessages,
1859
+ systemPrompt: openaiSystem.map(s => s.content as string),
1860
+ tools: tools,
1861
+ maxTokens: getMaxTokensFromProfile(modelProfile),
1862
+ stream: config.stream,
1863
+ reasoningEffort: reasoningEffort as any,
1864
+ temperature: isGPT5Model(model) ? 1 : MAIN_QUERY_TEMPERATURE,
1865
+ previousResponseId: toolUseContext?.responseState?.previousResponseId,
1866
+ verbosity: 'high' // High verbosity for coding tasks
1867
+ }
1868
+
1869
+ // Create request using adapter
1870
+ const request = adapter.createRequest(unifiedParams)
1871
+
1872
+ // Determine which API to use
1873
+ if (ModelAdapterFactory.shouldUseResponsesAPI(modelProfile)) {
1874
+ // Use Responses API for GPT-5 and similar models
1875
+ const { callGPT5ResponsesAPI } = await import('./openai')
1876
+ const response = await callGPT5ResponsesAPI(modelProfile, request, signal)
1877
+ const unifiedResponse = adapter.parseResponse(response)
1878
+
1879
+ // Convert unified response back to Anthropic format
1880
+ const apiMessage = {
1881
+ role: 'assistant' as const,
1882
+ content: unifiedResponse.content,
1883
+ tool_calls: unifiedResponse.toolCalls,
1884
+ usage: {
1885
+ prompt_tokens: unifiedResponse.usage.promptTokens,
1886
+ completion_tokens: unifiedResponse.usage.completionTokens,
1887
+ }
1888
+ }
1889
+ const assistantMsg: AssistantMessage = {
1890
+ type: 'assistant',
1891
+ message: apiMessage as any,
1892
+ costUSD: 0, // Will be calculated later
1893
+ durationMs: Date.now() - start,
1894
+ uuid: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}` as any,
1895
+ responseId: unifiedResponse.responseId // For state management
1896
+ }
1897
+ return assistantMsg
1898
+ } else {
1899
+ // Use existing Chat Completions flow
1900
+ const s = await getCompletionWithProfile(modelProfile, request, 0, 10, signal)
1901
+ let finalResponse
1902
+ if (config.stream) {
1903
+ finalResponse = await handleMessageStream(s as ChatCompletionStream, signal)
1904
+ } else {
1905
+ finalResponse = s
1906
+ }
1907
+ const r = convertOpenAIResponseToAnthropic(finalResponse, tools)
1908
+ return r
1909
+ }
1782
1910
  } else {
1783
- finalResponse = s
1911
+ // Legacy system (preserved for fallback)
1912
+ const completionFunction = isGPT5Model(modelProfile.modelName)
1913
+ ? getGPT5CompletionWithProfile
1914
+ : getCompletionWithProfile
1915
+ const s = await completionFunction(modelProfile, opts, 0, 10, signal)
1916
+ let finalResponse
1917
+ if (opts.stream) {
1918
+ finalResponse = await handleMessageStream(s as ChatCompletionStream, signal)
1919
+ } else {
1920
+ finalResponse = s
1921
+ }
1922
+ const r = convertOpenAIResponseToAnthropic(finalResponse, tools)
1923
+ return r
1784
1924
  }
1785
-
1786
- const r = convertOpenAIResponseToAnthropic(finalResponse)
1787
- return r
1788
1925
  } else {
1789
1926
  // 🚨 警告:ModelProfile不可用,使用旧逻辑路径
1790
1927
  debugLogger.api('USING_LEGACY_PATH', {
@@ -1793,7 +1930,7 @@ async function queryOpenAI(
1793
1930
  modelNameExists: !!modelProfile?.modelName,
1794
1931
  fallbackModel: 'main',
1795
1932
  actualModel: model,
1796
- requestId: currentRequest?.id,
1933
+ requestId: getCurrentRequest()?.id,
1797
1934
  })
1798
1935
 
1799
1936
  // 🚨 FALLBACK: 没有有效的ModelProfile时,应该抛出错误而不是使用遗留系统
@@ -1802,14 +1939,14 @@ async function queryOpenAI(
1802
1939
  modelProfileId: modelProfile?.modelName,
1803
1940
  modelNameExists: !!modelProfile?.modelName,
1804
1941
  requestedModel: model,
1805
- requestId: currentRequest?.id,
1942
+ requestId: getCurrentRequest()?.id,
1806
1943
  }
1807
1944
  debugLogger.error('NO_VALID_MODEL_PROFILE', errorDetails)
1808
1945
  throw new Error(
1809
1946
  `No valid ModelProfile available for model: ${model}. Please configure model through /model command. Debug: ${JSON.stringify(errorDetails)}`,
1810
1947
  )
1811
1948
  }
1812
- }, { signal }) // 🔧 CRITICAL FIX: Pass AbortSignal to withRetry
1949
+ }, { signal })
1813
1950
  } catch (error) {
1814
1951
  logError(error)
1815
1952
  return getAssistantMessageFromError(error)
@@ -1847,7 +1984,6 @@ async function queryOpenAI(
1847
1984
  end: Date.now(),
1848
1985
  },
1849
1986
  apiFormat: 'openai',
1850
- modelConfig: getModelConfigForDebug(model),
1851
1987
  })
1852
1988
 
1853
1989
  return {
@@ -1943,5 +2079,5 @@ export async function queryQuick({
1943
2079
  },
1944
2080
  ] as (UserMessage | AssistantMessage)[]
1945
2081
 
1946
- return queryModel('quick', messages, systemPrompt, 0, [], signal)
2082
+ return queryModel('quick', messages, systemPrompt, signal)
1947
2083
  }