@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
@@ -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 } from '../utils/debugLogger'
6
+ import { debug as debugLogger, getCurrentRequest } 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 {
@@ -53,6 +53,7 @@ function abortableDelay(delayMs: number, signal?: AbortSignal): Promise<void> {
53
53
  enum ModelErrorType {
54
54
  MaxLength = '1024',
55
55
  MaxCompletionTokens = 'max_completion_tokens',
56
+ TemperatureRestriction = 'temperature_restriction',
56
57
  StreamOptions = 'stream_options',
57
58
  Citations = 'citations',
58
59
  RateLimit = 'rate_limit',
@@ -98,6 +99,49 @@ interface ErrorHandler {
98
99
  fix: ErrorFixer
99
100
  }
100
101
 
102
+ // GPT-5 specific error handlers with enhanced detection patterns
103
+ const GPT5_ERROR_HANDLERS: ErrorHandler[] = [
104
+ {
105
+ type: ModelErrorType.MaxCompletionTokens,
106
+ detect: errMsg => {
107
+ const lowerMsg = errMsg.toLowerCase()
108
+ return (
109
+ // Exact OpenAI GPT-5 error message
110
+ (lowerMsg.includes("unsupported parameter: 'max_tokens'") && lowerMsg.includes("'max_completion_tokens'")) ||
111
+ // Generic max_tokens error patterns
112
+ (lowerMsg.includes("max_tokens") && lowerMsg.includes("max_completion_tokens")) ||
113
+ (lowerMsg.includes("max_tokens") && lowerMsg.includes("not supported")) ||
114
+ (lowerMsg.includes("max_tokens") && lowerMsg.includes("use max_completion_tokens")) ||
115
+ // Additional patterns for various providers
116
+ (lowerMsg.includes("invalid parameter") && lowerMsg.includes("max_tokens")) ||
117
+ (lowerMsg.includes("parameter error") && lowerMsg.includes("max_tokens"))
118
+ )
119
+ },
120
+ fix: async opts => {
121
+ console.log(`🔧 GPT-5 Fix: Converting max_tokens (${opts.max_tokens}) to max_completion_tokens`)
122
+ if ('max_tokens' in opts) {
123
+ opts.max_completion_tokens = opts.max_tokens
124
+ delete opts.max_tokens
125
+ }
126
+ },
127
+ },
128
+ {
129
+ type: ModelErrorType.TemperatureRestriction,
130
+ detect: errMsg => {
131
+ const lowerMsg = errMsg.toLowerCase()
132
+ return (
133
+ lowerMsg.includes("temperature") &&
134
+ (lowerMsg.includes("only supports") || lowerMsg.includes("must be 1") || lowerMsg.includes("invalid temperature"))
135
+ )
136
+ },
137
+ fix: async opts => {
138
+ console.log(`🔧 GPT-5 Fix: Adjusting temperature from ${opts.temperature} to 1`)
139
+ opts.temperature = 1
140
+ },
141
+ },
142
+ // Add more GPT-5 specific handlers as needed
143
+ ]
144
+
101
145
  // Standard error handlers
102
146
  const ERROR_HANDLERS: ErrorHandler[] = [
103
147
  {
@@ -210,6 +254,11 @@ function isRateLimitError(errMsg: string): boolean {
210
254
  // Model-specific feature flags - can be extended with more properties as needed
211
255
  interface ModelFeatures {
212
256
  usesMaxCompletionTokens: boolean
257
+ supportsResponsesAPI?: boolean
258
+ requiresTemperatureOne?: boolean
259
+ supportsVerbosityControl?: boolean
260
+ supportsCustomTools?: boolean
261
+ supportsAllowedTools?: boolean
213
262
  }
214
263
 
215
264
  // Map of model identifiers to their specific features
@@ -220,16 +269,63 @@ const MODEL_FEATURES: Record<string, ModelFeatures> = {
220
269
  'o1-mini': { usesMaxCompletionTokens: true },
221
270
  'o1-pro': { usesMaxCompletionTokens: true },
222
271
  'o3-mini': { usesMaxCompletionTokens: true },
272
+ // GPT-5 models
273
+ 'gpt-5': {
274
+ usesMaxCompletionTokens: true,
275
+ supportsResponsesAPI: true,
276
+ requiresTemperatureOne: true,
277
+ supportsVerbosityControl: true,
278
+ supportsCustomTools: true,
279
+ supportsAllowedTools: true,
280
+ },
281
+ 'gpt-5-mini': {
282
+ usesMaxCompletionTokens: true,
283
+ supportsResponsesAPI: true,
284
+ requiresTemperatureOne: true,
285
+ supportsVerbosityControl: true,
286
+ supportsCustomTools: true,
287
+ supportsAllowedTools: true,
288
+ },
289
+ 'gpt-5-nano': {
290
+ usesMaxCompletionTokens: true,
291
+ supportsResponsesAPI: true,
292
+ requiresTemperatureOne: true,
293
+ supportsVerbosityControl: true,
294
+ supportsCustomTools: true,
295
+ supportsAllowedTools: true,
296
+ },
297
+ 'gpt-5-chat-latest': {
298
+ usesMaxCompletionTokens: true,
299
+ supportsResponsesAPI: false, // Uses Chat Completions only
300
+ requiresTemperatureOne: true,
301
+ supportsVerbosityControl: true,
302
+ },
223
303
  }
224
304
 
225
305
  // Helper to get model features based on model ID/name
226
306
  function getModelFeatures(modelName: string): ModelFeatures {
227
- // Check for exact matches first
307
+ if (!modelName || typeof modelName !== 'string') {
308
+ return { usesMaxCompletionTokens: false }
309
+ }
310
+
311
+ // Check for exact matches first (highest priority)
228
312
  if (MODEL_FEATURES[modelName]) {
229
313
  return MODEL_FEATURES[modelName]
230
314
  }
231
315
 
232
- // Check for partial matches (e.g., if modelName contains a known model ID)
316
+ // Simple GPT-5 detection: any model name containing 'gpt-5'
317
+ if (modelName.toLowerCase().includes('gpt-5')) {
318
+ return {
319
+ usesMaxCompletionTokens: true,
320
+ supportsResponsesAPI: true,
321
+ requiresTemperatureOne: true,
322
+ supportsVerbosityControl: true,
323
+ supportsCustomTools: true,
324
+ supportsAllowedTools: true,
325
+ }
326
+ }
327
+
328
+ // Check for partial matches (e.g., other reasoning models)
233
329
  for (const [key, features] of Object.entries(MODEL_FEATURES)) {
234
330
  if (modelName.includes(key)) {
235
331
  return features
@@ -249,15 +345,53 @@ function applyModelSpecificTransformations(
249
345
  }
250
346
 
251
347
  const features = getModelFeatures(opts.model)
348
+ const isGPT5 = opts.model.toLowerCase().includes('gpt-5')
252
349
 
253
- // Apply transformations based on features
254
- if (
255
- features.usesMaxCompletionTokens &&
256
- 'max_tokens' in opts &&
257
- !('max_completion_tokens' in opts)
258
- ) {
259
- opts.max_completion_tokens = opts.max_tokens
260
- delete opts.max_tokens
350
+ // 🔥 Enhanced GPT-5 Detection and Transformation
351
+ if (isGPT5 || features.usesMaxCompletionTokens) {
352
+ // Force max_completion_tokens for all GPT-5 models
353
+ if ('max_tokens' in opts && !('max_completion_tokens' in opts)) {
354
+ console.log(`🔧 Transforming max_tokens (${opts.max_tokens}) to max_completion_tokens for ${opts.model}`)
355
+ opts.max_completion_tokens = opts.max_tokens
356
+ delete opts.max_tokens
357
+ }
358
+
359
+ // Force temperature = 1 for GPT-5 models
360
+ if (features.requiresTemperatureOne && 'temperature' in opts) {
361
+ if (opts.temperature !== 1 && opts.temperature !== undefined) {
362
+ console.log(
363
+ `🔧 GPT-5 temperature constraint: Adjusting temperature from ${opts.temperature} to 1 for ${opts.model}`
364
+ )
365
+ opts.temperature = 1
366
+ }
367
+ }
368
+
369
+ // Remove unsupported parameters for GPT-5
370
+ if (isGPT5) {
371
+ // Remove parameters that may not be supported by GPT-5
372
+ delete opts.frequency_penalty
373
+ delete opts.presence_penalty
374
+ delete opts.logit_bias
375
+ delete opts.user
376
+
377
+ // Add reasoning_effort if not present and model supports it
378
+ if (!opts.reasoning_effort && features.supportsVerbosityControl) {
379
+ opts.reasoning_effort = 'medium' // Default reasoning effort for coding tasks
380
+ }
381
+ }
382
+ }
383
+
384
+ // Apply transformations for non-GPT-5 models
385
+ else {
386
+ // Standard max_tokens to max_completion_tokens conversion for other reasoning models
387
+ if (
388
+ features.usesMaxCompletionTokens &&
389
+ 'max_tokens' in opts &&
390
+ !('max_completion_tokens' in opts)
391
+ ) {
392
+ opts.max_completion_tokens = opts.max_tokens
393
+ delete opts.max_tokens
394
+ }
261
395
  }
262
396
 
263
397
  // Add more transformations here as needed
@@ -267,7 +401,10 @@ async function applyModelErrorFixes(
267
401
  opts: OpenAI.ChatCompletionCreateParams,
268
402
  baseURL: string,
269
403
  ) {
270
- for (const handler of ERROR_HANDLERS) {
404
+ const isGPT5 = opts.model.startsWith('gpt-5')
405
+ const handlers = isGPT5 ? [...GPT5_ERROR_HANDLERS, ...ERROR_HANDLERS] : ERROR_HANDLERS
406
+
407
+ for (const handler of handlers) {
271
408
  if (hasModelError(baseURL, opts.model, handler.type)) {
272
409
  await handler.fix(opts)
273
410
  return
@@ -333,6 +470,9 @@ async function tryWithEndpointFallback(
333
470
  throw lastError || new Error('All endpoints failed')
334
471
  }
335
472
 
473
+ // Export shared utilities for GPT-5 compatibility
474
+ export { getGPT5CompletionWithProfile, getModelFeatures, applyModelSpecificTransformations }
475
+
336
476
  export async function getCompletionWithProfile(
337
477
  modelProfile: any,
338
478
  opts: OpenAI.ChatCompletionCreateParams,
@@ -465,6 +605,43 @@ export async function getCompletionWithProfile(
465
605
  throw new Error('Request cancelled by user')
466
606
  }
467
607
 
608
+ // 🔥 NEW: Parse error message to detect and handle specific API errors
609
+ try {
610
+ const errorData = await response.json()
611
+ const errorMessage = errorData?.error?.message || errorData?.message || `HTTP ${response.status}`
612
+
613
+ // Check if this is a parameter error that we can fix
614
+ const isGPT5 = opts.model.startsWith('gpt-5')
615
+ const handlers = isGPT5 ? [...GPT5_ERROR_HANDLERS, ...ERROR_HANDLERS] : ERROR_HANDLERS
616
+
617
+ for (const handler of handlers) {
618
+ if (handler.detect(errorMessage)) {
619
+ console.log(`🔧 Detected ${handler.type} error for ${opts.model}: ${errorMessage}`)
620
+
621
+ // Store this error for future requests
622
+ setModelError(baseURL || '', opts.model, handler.type, errorMessage)
623
+
624
+ // Apply the fix and retry immediately
625
+ await handler.fix(opts)
626
+ console.log(`🔧 Applied fix for ${handler.type}, retrying...`)
627
+
628
+ return getCompletionWithProfile(
629
+ modelProfile,
630
+ opts,
631
+ attempt + 1,
632
+ maxAttempts,
633
+ signal,
634
+ )
635
+ }
636
+ }
637
+
638
+ // If no specific handler found, log the error for debugging
639
+ console.log(`⚠️ Unhandled API error (${response.status}): ${errorMessage}`)
640
+ } catch (parseError) {
641
+ // If we can't parse the error, fall back to generic retry
642
+ console.log(`⚠️ Could not parse error response (${response.status})`)
643
+ }
644
+
468
645
  const delayMs = getRetryDelay(attempt)
469
646
  console.log(
470
647
  ` ⎿ API error (${response.status}), retrying in ${Math.round(delayMs / 1000)}s... (attempt ${attempt + 1}/${maxAttempts})`,
@@ -538,6 +715,43 @@ export async function getCompletionWithProfile(
538
715
  throw new Error('Request cancelled by user')
539
716
  }
540
717
 
718
+ // 🔥 NEW: Parse error message to detect and handle specific API errors
719
+ try {
720
+ const errorData = await response.json()
721
+ const errorMessage = errorData?.error?.message || errorData?.message || `HTTP ${response.status}`
722
+
723
+ // Check if this is a parameter error that we can fix
724
+ const isGPT5 = opts.model.startsWith('gpt-5')
725
+ const handlers = isGPT5 ? [...GPT5_ERROR_HANDLERS, ...ERROR_HANDLERS] : ERROR_HANDLERS
726
+
727
+ for (const handler of handlers) {
728
+ if (handler.detect(errorMessage)) {
729
+ console.log(`🔧 Detected ${handler.type} error for ${opts.model}: ${errorMessage}`)
730
+
731
+ // Store this error for future requests
732
+ setModelError(baseURL || '', opts.model, handler.type, errorMessage)
733
+
734
+ // Apply the fix and retry immediately
735
+ await handler.fix(opts)
736
+ console.log(`🔧 Applied fix for ${handler.type}, retrying...`)
737
+
738
+ return getCompletionWithProfile(
739
+ modelProfile,
740
+ opts,
741
+ attempt + 1,
742
+ maxAttempts,
743
+ signal,
744
+ )
745
+ }
746
+ }
747
+
748
+ // If no specific handler found, log the error for debugging
749
+ console.log(`⚠️ Unhandled API error (${response.status}): ${errorMessage}`)
750
+ } catch (parseError) {
751
+ // If we can't parse the error, fall back to generic retry
752
+ console.log(`⚠️ Could not parse error response (${response.status})`)
753
+ }
754
+
541
755
  const delayMs = getRetryDelay(attempt)
542
756
  console.log(
543
757
  ` ⎿ API error (${response.status}), retrying in ${Math.round(delayMs / 1000)}s... (attempt ${attempt + 1}/${maxAttempts})`,
@@ -689,6 +903,301 @@ export function streamCompletion(
689
903
  return createStreamProcessor(stream)
690
904
  }
691
905
 
906
+ /**
907
+ * Call GPT-5 Responses API with proper parameter handling
908
+ */
909
+ export async function callGPT5ResponsesAPI(
910
+ modelProfile: any,
911
+ opts: any, // Using 'any' for Responses API params which differ from ChatCompletionCreateParams
912
+ signal?: AbortSignal,
913
+ ): Promise<any> {
914
+ const baseURL = modelProfile?.baseURL || 'https://api.openai.com/v1'
915
+ const apiKey = modelProfile?.apiKey
916
+ const proxy = getGlobalConfig().proxy
917
+ ? new ProxyAgent(getGlobalConfig().proxy)
918
+ : undefined
919
+
920
+ const headers: Record<string, string> = {
921
+ 'Content-Type': 'application/json',
922
+ Authorization: `Bearer ${apiKey}`,
923
+ }
924
+
925
+ // 🔥 Enhanced Responses API Parameter Mapping for GPT-5
926
+ const responsesParams: any = {
927
+ model: opts.model,
928
+ input: opts.messages, // Responses API uses 'input' instead of 'messages'
929
+ }
930
+
931
+ // 🔧 GPT-5 Token Configuration
932
+ if (opts.max_completion_tokens) {
933
+ responsesParams.max_completion_tokens = opts.max_completion_tokens
934
+ } else if (opts.max_tokens) {
935
+ // Fallback conversion if max_tokens is still present
936
+ responsesParams.max_completion_tokens = opts.max_tokens
937
+ }
938
+
939
+ // 🔧 GPT-5 Temperature Handling (only 1 or undefined)
940
+ if (opts.temperature === 1) {
941
+ responsesParams.temperature = 1
942
+ }
943
+ // Note: Do not pass temperature if it's not 1, GPT-5 will use default
944
+
945
+ // 🔧 GPT-5 Reasoning Configuration
946
+ const reasoningEffort = opts.reasoning_effort || 'medium'
947
+ responsesParams.reasoning = {
948
+ effort: reasoningEffort,
949
+ // 🚀 Enable reasoning summaries for transparency in coding tasks
950
+ generate_summary: true,
951
+ }
952
+
953
+ // 🔧 GPT-5 Tools Support
954
+ if (opts.tools && opts.tools.length > 0) {
955
+ responsesParams.tools = opts.tools
956
+
957
+ // 🚀 GPT-5 Tool Choice Configuration
958
+ if (opts.tool_choice) {
959
+ responsesParams.tool_choice = opts.tool_choice
960
+ }
961
+ }
962
+
963
+ // 🔧 GPT-5 System Instructions (separate from messages)
964
+ const systemMessages = opts.messages.filter(msg => msg.role === 'system')
965
+ const nonSystemMessages = opts.messages.filter(msg => msg.role !== 'system')
966
+
967
+ if (systemMessages.length > 0) {
968
+ responsesParams.instructions = systemMessages.map(msg => msg.content).join('\n\n')
969
+ responsesParams.input = nonSystemMessages
970
+ }
971
+
972
+ // Handle verbosity (if supported) - optimized for coding tasks
973
+ const features = getModelFeatures(opts.model)
974
+ if (features.supportsVerbosityControl) {
975
+ // High verbosity for coding tasks to get detailed explanations and structured code
976
+ // Based on GPT-5 best practices for agent-like coding environments
977
+ responsesParams.text = {
978
+ verbosity: 'high',
979
+ }
980
+ }
981
+
982
+ // Apply GPT-5 coding optimizations
983
+ if (opts.model.startsWith('gpt-5')) {
984
+ // Set reasoning effort based on task complexity
985
+ if (!responsesParams.reasoning) {
986
+ responsesParams.reasoning = {
987
+ effort: 'medium', // Balanced for most coding tasks
988
+ }
989
+ }
990
+
991
+ // Add instructions parameter for coding-specific guidance
992
+ if (!responsesParams.instructions) {
993
+ responsesParams.instructions = `You are an expert programmer working in a terminal-based coding environment. Follow these guidelines:
994
+ - Provide clear, concise code solutions
995
+ - Use proper error handling and validation
996
+ - Follow coding best practices and patterns
997
+ - Explain complex logic when necessary
998
+ - Focus on maintainable, readable code`
999
+ }
1000
+ }
1001
+
1002
+ try {
1003
+ const response = await fetch(`${baseURL}/responses`, {
1004
+ method: 'POST',
1005
+ headers,
1006
+ body: JSON.stringify(responsesParams),
1007
+ dispatcher: proxy,
1008
+ signal: signal,
1009
+ })
1010
+
1011
+ if (!response.ok) {
1012
+ throw new Error(`GPT-5 Responses API error: ${response.status} ${response.statusText}`)
1013
+ }
1014
+
1015
+ const responseData = await response.json()
1016
+
1017
+ // Convert Responses API response back to Chat Completion format for compatibility
1018
+ return convertResponsesAPIToChatCompletion(responseData)
1019
+ } catch (error) {
1020
+ if (signal?.aborted) {
1021
+ throw new Error('Request cancelled by user')
1022
+ }
1023
+ throw error
1024
+ }
1025
+ }
1026
+
1027
+ /**
1028
+ * Convert Responses API response to Chat Completion format for compatibility
1029
+ * 🔥 Enhanced for GPT-5 with reasoning summary support
1030
+ */
1031
+ function convertResponsesAPIToChatCompletion(responsesData: any): any {
1032
+ // Extract content from Responses API format
1033
+ let outputText = responsesData.output_text || ''
1034
+ const usage = responsesData.usage || {}
1035
+
1036
+ // 🚀 GPT-5 Reasoning Summary Integration
1037
+ // If reasoning summary is available, prepend it to the output for transparency
1038
+ if (responsesData.output && Array.isArray(responsesData.output)) {
1039
+ const reasoningItems = responsesData.output.filter(item => item.type === 'reasoning' && item.summary)
1040
+ const messageItems = responsesData.output.filter(item => item.type === 'message')
1041
+
1042
+ if (reasoningItems.length > 0 && messageItems.length > 0) {
1043
+ const reasoningSummary = reasoningItems
1044
+ .map(item => item.summary?.map(s => s.text).join('\n'))
1045
+ .filter(Boolean)
1046
+ .join('\n\n')
1047
+
1048
+ const mainContent = messageItems
1049
+ .map(item => item.content?.map(c => c.text).join('\n'))
1050
+ .filter(Boolean)
1051
+ .join('\n\n')
1052
+
1053
+ if (reasoningSummary) {
1054
+ outputText = `**🧠 Reasoning Process:**\n${reasoningSummary}\n\n**📝 Response:**\n${mainContent}`
1055
+ } else {
1056
+ outputText = mainContent
1057
+ }
1058
+ }
1059
+ }
1060
+
1061
+ return {
1062
+ id: responsesData.id || `chatcmpl-${Date.now()}`,
1063
+ object: 'chat.completion',
1064
+ created: Math.floor(Date.now() / 1000),
1065
+ model: responsesData.model || '',
1066
+ choices: [
1067
+ {
1068
+ index: 0,
1069
+ message: {
1070
+ role: 'assistant',
1071
+ content: outputText,
1072
+ // 🚀 Include reasoning metadata if available
1073
+ ...(responsesData.reasoning && {
1074
+ reasoning: {
1075
+ effort: responsesData.reasoning.effort,
1076
+ summary: responsesData.reasoning.summary,
1077
+ },
1078
+ }),
1079
+ },
1080
+ finish_reason: responsesData.status === 'completed' ? 'stop' : 'length',
1081
+ },
1082
+ ],
1083
+ usage: {
1084
+ prompt_tokens: usage.input_tokens || 0,
1085
+ completion_tokens: usage.output_tokens || 0,
1086
+ total_tokens: (usage.input_tokens || 0) + (usage.output_tokens || 0),
1087
+ // 🔧 GPT-5 Enhanced Usage Details
1088
+ prompt_tokens_details: {
1089
+ cached_tokens: usage.input_tokens_details?.cached_tokens || 0,
1090
+ },
1091
+ completion_tokens_details: {
1092
+ reasoning_tokens: usage.output_tokens_details?.reasoning_tokens || 0,
1093
+ },
1094
+ },
1095
+ }
1096
+ }
1097
+
1098
+ /**
1099
+ * Enhanced getCompletionWithProfile that supports GPT-5 Responses API
1100
+ * 🔥 Optimized for both official OpenAI and third-party GPT-5 providers
1101
+ */
1102
+ async function getGPT5CompletionWithProfile(
1103
+ modelProfile: any,
1104
+ opts: OpenAI.ChatCompletionCreateParams,
1105
+ attempt: number = 0,
1106
+ maxAttempts: number = 10,
1107
+ signal?: AbortSignal,
1108
+ ): Promise<OpenAI.ChatCompletion | AsyncIterable<OpenAI.ChatCompletionChunk>> {
1109
+ const features = getModelFeatures(opts.model)
1110
+ const isOfficialOpenAI = !modelProfile.baseURL ||
1111
+ modelProfile.baseURL.includes('api.openai.com')
1112
+
1113
+ // 🚀 Try Responses API for official OpenAI non-streaming requests
1114
+ if (features.supportsResponsesAPI && !opts.stream && isOfficialOpenAI) {
1115
+ try {
1116
+ debugLogger.api('ATTEMPTING_GPT5_RESPONSES_API', {
1117
+ model: opts.model,
1118
+ baseURL: modelProfile.baseURL || 'official',
1119
+ provider: modelProfile.provider,
1120
+ stream: opts.stream,
1121
+ requestId: getCurrentRequest()?.id,
1122
+ })
1123
+
1124
+ const result = await callGPT5ResponsesAPI(modelProfile, opts, signal)
1125
+
1126
+ debugLogger.api('GPT5_RESPONSES_API_SUCCESS', {
1127
+ model: opts.model,
1128
+ baseURL: modelProfile.baseURL || 'official',
1129
+ requestId: getCurrentRequest()?.id,
1130
+ })
1131
+
1132
+ return result
1133
+ } catch (error) {
1134
+ debugLogger.api('GPT5_RESPONSES_API_FALLBACK', {
1135
+ model: opts.model,
1136
+ error: error.message,
1137
+ baseURL: modelProfile.baseURL || 'official',
1138
+ requestId: getCurrentRequest()?.id,
1139
+ })
1140
+
1141
+ console.warn(
1142
+ `🔄 GPT-5 Responses API failed, falling back to Chat Completions: ${error.message}`
1143
+ )
1144
+ // Fall through to Chat Completions API
1145
+ }
1146
+ }
1147
+
1148
+ // 🌐 Handle third-party GPT-5 providers with enhanced compatibility
1149
+ else if (!isOfficialOpenAI) {
1150
+ debugLogger.api('GPT5_THIRD_PARTY_PROVIDER', {
1151
+ model: opts.model,
1152
+ baseURL: modelProfile.baseURL,
1153
+ provider: modelProfile.provider,
1154
+ supportsResponsesAPI: features.supportsResponsesAPI,
1155
+ requestId: getCurrentRequest()?.id,
1156
+ })
1157
+
1158
+ // 🔧 Apply enhanced parameter optimization for third-party providers
1159
+ console.log(`🌐 Using GPT-5 via third-party provider: ${modelProfile.provider} (${modelProfile.baseURL})`)
1160
+
1161
+ // Some third-party providers may need additional parameter adjustments
1162
+ if (modelProfile.provider === 'azure') {
1163
+ // Azure OpenAI specific adjustments
1164
+ delete opts.reasoning_effort // Azure may not support this yet
1165
+ } else if (modelProfile.provider === 'custom-openai') {
1166
+ // Generic OpenAI-compatible provider optimizations
1167
+ console.log(`🔧 Applying OpenAI-compatible optimizations for custom provider`)
1168
+ }
1169
+ }
1170
+
1171
+ // 📡 Handle streaming requests (Responses API doesn't support streaming yet)
1172
+ else if (opts.stream) {
1173
+ debugLogger.api('GPT5_STREAMING_MODE', {
1174
+ model: opts.model,
1175
+ baseURL: modelProfile.baseURL || 'official',
1176
+ reason: 'responses_api_no_streaming',
1177
+ requestId: getCurrentRequest()?.id,
1178
+ })
1179
+
1180
+ console.log(`🔄 Using Chat Completions for streaming (Responses API streaming not available)`)
1181
+ }
1182
+
1183
+ // 🔧 Enhanced Chat Completions fallback with GPT-5 optimizations
1184
+ debugLogger.api('USING_CHAT_COMPLETIONS_FOR_GPT5', {
1185
+ model: opts.model,
1186
+ baseURL: modelProfile.baseURL || 'official',
1187
+ provider: modelProfile.provider,
1188
+ reason: isOfficialOpenAI ? 'streaming_or_fallback' : 'third_party_provider',
1189
+ requestId: getCurrentRequest()?.id,
1190
+ })
1191
+
1192
+ return await getCompletionWithProfile(
1193
+ modelProfile,
1194
+ opts,
1195
+ attempt,
1196
+ maxAttempts,
1197
+ signal,
1198
+ )
1199
+ }
1200
+
692
1201
  /**
693
1202
  * Fetch available models from custom OpenAI-compatible API
694
1203
  */
@@ -0,0 +1,90 @@
1
+ /**
2
+ * GPT-5 Responses API state management
3
+ * Manages previous_response_id for conversation continuity and reasoning context reuse
4
+ */
5
+
6
+ interface ConversationState {
7
+ previousResponseId?: string
8
+ lastUpdate: number
9
+ }
10
+
11
+ class ResponseStateManager {
12
+ private conversationStates = new Map<string, ConversationState>()
13
+
14
+ // Cache cleanup after 1 hour of inactivity
15
+ private readonly CLEANUP_INTERVAL = 60 * 60 * 1000
16
+
17
+ constructor() {
18
+ // Periodic cleanup of stale conversations
19
+ setInterval(() => {
20
+ this.cleanup()
21
+ }, this.CLEANUP_INTERVAL)
22
+ }
23
+
24
+ /**
25
+ * Set the previous response ID for a conversation
26
+ */
27
+ setPreviousResponseId(conversationId: string, responseId: string): void {
28
+ this.conversationStates.set(conversationId, {
29
+ previousResponseId: responseId,
30
+ lastUpdate: Date.now()
31
+ })
32
+ }
33
+
34
+ /**
35
+ * Get the previous response ID for a conversation
36
+ */
37
+ getPreviousResponseId(conversationId: string): string | undefined {
38
+ const state = this.conversationStates.get(conversationId)
39
+ if (state) {
40
+ // Update last access time
41
+ state.lastUpdate = Date.now()
42
+ return state.previousResponseId
43
+ }
44
+ return undefined
45
+ }
46
+
47
+ /**
48
+ * Clear state for a conversation
49
+ */
50
+ clearConversation(conversationId: string): void {
51
+ this.conversationStates.delete(conversationId)
52
+ }
53
+
54
+ /**
55
+ * Clear all conversation states
56
+ */
57
+ clearAll(): void {
58
+ this.conversationStates.clear()
59
+ }
60
+
61
+ /**
62
+ * Clean up stale conversations
63
+ */
64
+ private cleanup(): void {
65
+ const now = Date.now()
66
+ for (const [conversationId, state] of this.conversationStates.entries()) {
67
+ if (now - state.lastUpdate > this.CLEANUP_INTERVAL) {
68
+ this.conversationStates.delete(conversationId)
69
+ }
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Get current state size (for debugging/monitoring)
75
+ */
76
+ getStateSize(): number {
77
+ return this.conversationStates.size
78
+ }
79
+ }
80
+
81
+ // Singleton instance
82
+ export const responseStateManager = new ResponseStateManager()
83
+
84
+ /**
85
+ * Helper to generate conversation ID from context
86
+ */
87
+ export function getConversationId(agentId?: string, messageId?: string): string {
88
+ // Use agentId as primary identifier, fallback to messageId or timestamp
89
+ return agentId || messageId || `conv_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
90
+ }