@midscene/core 1.10.8-beta-20260723115348.0 → 1.10.8

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 (86) hide show
  1. package/dist/es/agent/agent.mjs +66 -26
  2. package/dist/es/agent/agent.mjs.map +1 -1
  3. package/dist/es/agent/file-chooser.mjs +82 -0
  4. package/dist/es/agent/file-chooser.mjs.map +1 -0
  5. package/dist/es/agent/prompt-context.mjs +12 -1
  6. package/dist/es/agent/prompt-context.mjs.map +1 -1
  7. package/dist/es/agent/task-builder.mjs +11 -2
  8. package/dist/es/agent/task-builder.mjs.map +1 -1
  9. package/dist/es/agent/tasks.mjs +4 -16
  10. package/dist/es/agent/tasks.mjs.map +1 -1
  11. package/dist/es/agent/ui-utils.mjs +1 -0
  12. package/dist/es/agent/ui-utils.mjs.map +1 -1
  13. package/dist/es/agent/utils.mjs +1 -1
  14. package/dist/es/ai-model/inspect.mjs +1 -1
  15. package/dist/es/ai-model/inspect.mjs.map +1 -1
  16. package/dist/es/ai-model/llm-planning.mjs +2 -1
  17. package/dist/es/ai-model/llm-planning.mjs.map +1 -1
  18. package/dist/es/ai-model/model-adapter/chat-completion.mjs +3 -1
  19. package/dist/es/ai-model/model-adapter/chat-completion.mjs.map +1 -1
  20. package/dist/es/ai-model/models/kimi.mjs +35 -0
  21. package/dist/es/ai-model/models/kimi.mjs.map +1 -1
  22. package/dist/es/ai-model/prompt/extraction.mjs +4 -2
  23. package/dist/es/ai-model/prompt/extraction.mjs.map +1 -1
  24. package/dist/es/ai-model/service-caller/codex-app-server.mjs +22 -14
  25. package/dist/es/ai-model/service-caller/codex-app-server.mjs.map +1 -1
  26. package/dist/es/ai-model/service-caller/index.mjs +15 -14
  27. package/dist/es/ai-model/service-caller/index.mjs.map +1 -1
  28. package/dist/es/ai-model/workflows/planning/custom-planning.mjs.map +1 -1
  29. package/dist/es/device/index.mjs +21 -1
  30. package/dist/es/device/index.mjs.map +1 -1
  31. package/dist/es/report.mjs +1 -0
  32. package/dist/es/report.mjs.map +1 -1
  33. package/dist/es/types.mjs.map +1 -1
  34. package/dist/es/utils.mjs +2 -2
  35. package/dist/es/yaml/utils.mjs +9 -1
  36. package/dist/es/yaml/utils.mjs.map +1 -1
  37. package/dist/lib/agent/agent.js +63 -23
  38. package/dist/lib/agent/agent.js.map +1 -1
  39. package/dist/lib/agent/file-chooser.js +125 -0
  40. package/dist/lib/agent/file-chooser.js.map +1 -0
  41. package/dist/lib/agent/prompt-context.js +14 -0
  42. package/dist/lib/agent/prompt-context.js.map +1 -1
  43. package/dist/lib/agent/task-builder.js +11 -2
  44. package/dist/lib/agent/task-builder.js.map +1 -1
  45. package/dist/lib/agent/tasks.js +6 -18
  46. package/dist/lib/agent/tasks.js.map +1 -1
  47. package/dist/lib/agent/ui-utils.js +1 -0
  48. package/dist/lib/agent/ui-utils.js.map +1 -1
  49. package/dist/lib/agent/utils.js +1 -1
  50. package/dist/lib/ai-model/inspect.js +1 -1
  51. package/dist/lib/ai-model/inspect.js.map +1 -1
  52. package/dist/lib/ai-model/llm-planning.js +2 -1
  53. package/dist/lib/ai-model/llm-planning.js.map +1 -1
  54. package/dist/lib/ai-model/model-adapter/chat-completion.js +3 -1
  55. package/dist/lib/ai-model/model-adapter/chat-completion.js.map +1 -1
  56. package/dist/lib/ai-model/models/kimi.js +35 -0
  57. package/dist/lib/ai-model/models/kimi.js.map +1 -1
  58. package/dist/lib/ai-model/prompt/extraction.js +4 -2
  59. package/dist/lib/ai-model/prompt/extraction.js.map +1 -1
  60. package/dist/lib/ai-model/service-caller/codex-app-server.js +22 -14
  61. package/dist/lib/ai-model/service-caller/codex-app-server.js.map +1 -1
  62. package/dist/lib/ai-model/service-caller/index.js +15 -14
  63. package/dist/lib/ai-model/service-caller/index.js.map +1 -1
  64. package/dist/lib/ai-model/workflows/planning/custom-planning.js.map +1 -1
  65. package/dist/lib/device/index.js +27 -1
  66. package/dist/lib/device/index.js.map +1 -1
  67. package/dist/lib/report.js +1 -0
  68. package/dist/lib/report.js.map +1 -1
  69. package/dist/lib/types.js.map +1 -1
  70. package/dist/lib/utils.js +2 -2
  71. package/dist/lib/yaml/utils.js +9 -1
  72. package/dist/lib/yaml/utils.js.map +1 -1
  73. package/dist/types/agent/agent.d.ts +5 -2
  74. package/dist/types/agent/file-chooser.d.ts +19 -0
  75. package/dist/types/agent/prompt-context.d.ts +1 -0
  76. package/dist/types/agent/tasks.d.ts +1 -1
  77. package/dist/types/ai-model/model-adapter/types.d.ts +9 -0
  78. package/dist/types/ai-model/models/kimi.d.ts +18 -0
  79. package/dist/types/ai-model/models/registry.d.ts +18 -0
  80. package/dist/types/ai-model/prompt/extraction.d.ts +1 -1
  81. package/dist/types/ai-model/service-caller/codex-app-server.d.ts +5 -1
  82. package/dist/types/device/device-options.d.ts +7 -0
  83. package/dist/types/device/index.d.ts +11 -0
  84. package/dist/types/types.d.ts +6 -4
  85. package/dist/types/yaml.d.ts +9 -1
  86. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"ai-model/service-caller/codex-app-server.mjs","sources":["../../../../src/ai-model/service-caller/codex-app-server.ts"],"sourcesContent":["import type {\n AIUsageInfo,\n CodeGenerationChunk,\n StreamingCallback,\n} from '@/types';\nimport type {\n IModelConfig,\n TModelReasoningEnabled,\n} from '@midscene/shared/env';\nimport { getDebug } from '@midscene/shared/logger';\nimport { ifInBrowser } from '@midscene/shared/utils';\nimport type { ChatCompletionMessageParam } from 'openai/resources/index';\n\nconst CODEX_PROVIDER_SCHEME = 'codex://';\nconst CODEX_DEFAULT_TIMEOUT_MS = 10 * 60 * 1000;\nconst CODEX_DEFAULT_PROCESS_START_TIMEOUT_MS = 15 * 1000;\nconst CODEX_DEFAULT_CLEANUP_TIMEOUT_MS = 8 * 1000;\nconst CODEX_TEXT_INPUT_MAX_LENGTH = 256 * 1024;\n\nconst debugCodex = getDebug('ai:call:codex');\nconst warnCodex = getDebug('ai:call:codex', { console: true });\n\ntype CodexReasoningEffort =\n | 'none'\n | 'minimal'\n | 'low'\n | 'medium'\n | 'high'\n | 'xhigh';\n\ntype JsonRpcRequest = {\n id: string | number;\n method: string;\n params?: unknown;\n};\n\ntype JsonRpcResponse = {\n id: string | number;\n result?: unknown;\n error?: {\n code?: number;\n message?: string;\n data?: unknown;\n };\n};\n\ntype JsonRpcNotification = {\n method: string;\n params?: Record<string, any>;\n};\n\ntype JsonRpcMessage = JsonRpcRequest | JsonRpcResponse | JsonRpcNotification;\n\ntype CodexTextInput = {\n type: 'text';\n text: string;\n text_elements: any[];\n};\n\ntype CodexImageInput = {\n type: 'image';\n url: string;\n};\n\ntype CodexLocalImageInput = {\n type: 'localImage';\n path: string;\n};\n\ntype CodexTurnInput = CodexTextInput | CodexImageInput | CodexLocalImageInput;\n\ntype CodexTurnResult = {\n content: string;\n reasoning_content?: string;\n usage?: AIUsageInfo;\n isStreamed: boolean;\n};\n\ntype CodexTurnStartResponse = {\n turn?: {\n id?: string;\n };\n};\n\ntype CodexThreadStartResponse = {\n thread?: {\n id?: string;\n };\n};\n\ntype CodexUsageNotification = {\n threadId?: string;\n turnId?: string;\n tokenUsage?: {\n total?: {\n totalTokens?: number;\n inputTokens?: number;\n cachedInputTokens?: number;\n outputTokens?: number;\n reasoningOutputTokens?: number;\n };\n last?: {\n totalTokens?: number;\n inputTokens?: number;\n cachedInputTokens?: number;\n outputTokens?: number;\n reasoningOutputTokens?: number;\n };\n };\n};\n\nclass SerializedRunner {\n private tail: Promise<void> = Promise.resolve();\n\n async run<T>(work: () => Promise<T>): Promise<T> {\n const previous = this.tail;\n let release!: () => void;\n this.tail = new Promise<void>((resolve) => {\n release = resolve;\n });\n\n await previous;\n try {\n return await work();\n } finally {\n release();\n }\n }\n}\n\nexport const isCodexAppServerProvider = (baseURL?: string): boolean => {\n if (!baseURL) return false;\n return baseURL.trim().toLowerCase().startsWith(CODEX_PROVIDER_SCHEME);\n};\n\nconst isAbortError = (error: unknown): boolean => {\n if (!error) return false;\n if (error instanceof Error && error.name === 'AbortError') return true;\n const message =\n error instanceof Error ? error.message : String(error ?? 'unknown error');\n return /aborted|abort/i.test(message);\n};\n\nconst toNonEmptyString = (value: unknown): string | undefined => {\n if (typeof value !== 'string') return undefined;\n const trimmed = value.trim();\n return trimmed || undefined;\n};\n\nexport const normalizeCodexLocalImagePath = (\n imageUrl: string,\n platform: NodeJS.Platform = process.platform,\n): string => {\n if (!imageUrl.startsWith('file://')) {\n return imageUrl;\n }\n\n try {\n const parsed = new URL(imageUrl);\n const pathname = decodeURIComponent(parsed.pathname);\n const host = parsed.hostname.toLowerCase();\n\n if (platform === 'win32') {\n const windowsPath = pathname\n .replace(/\\//g, '\\\\')\n .replace(/^\\\\([A-Za-z]:)/, '$1');\n\n if (host && host !== 'localhost') {\n return `\\\\\\\\${parsed.hostname}${windowsPath}`;\n }\n\n return windowsPath;\n }\n\n if (host && host !== 'localhost') {\n return `//${parsed.hostname}${pathname}`;\n }\n\n return pathname;\n } catch {\n return decodeURIComponent(imageUrl.slice('file://'.length));\n }\n};\n\nconst extractTextFromMessage = (\n message: ChatCompletionMessageParam,\n): string => {\n const content = (message as any).content;\n if (typeof content === 'string') {\n return content;\n }\n\n if (Array.isArray(content)) {\n return content\n .map((part) => {\n if (!part || typeof part !== 'object') return '';\n\n if (part.type === 'text' && typeof part.text === 'string') {\n return part.text;\n }\n\n if (part.type === 'input_text' && typeof part.text === 'string') {\n return part.text;\n }\n\n return '';\n })\n .filter(Boolean)\n .join('\\n');\n }\n\n return '';\n};\n\nconst extractImageInputs = (\n message: ChatCompletionMessageParam,\n): Array<CodexImageInput | CodexLocalImageInput> => {\n const content = (message as any).content;\n if (!Array.isArray(content)) return [];\n\n const inputs: Array<CodexImageInput | CodexLocalImageInput> = [];\n for (const part of content) {\n if (!part || typeof part !== 'object') continue;\n\n const partType = String(part.type || '');\n const imageUrl =\n partType === 'image_url'\n ? toNonEmptyString(part.image_url?.url)\n : partType === 'input_image'\n ? toNonEmptyString(part.image_url || part.url)\n : undefined;\n\n if (!imageUrl) continue;\n\n if (\n imageUrl.startsWith('/') ||\n imageUrl.startsWith('./') ||\n imageUrl.startsWith('../') ||\n imageUrl.startsWith('file://')\n ) {\n const path = imageUrl.startsWith('file://')\n ? normalizeCodexLocalImagePath(imageUrl)\n : imageUrl;\n\n inputs.push({\n type: 'localImage',\n path,\n });\n continue;\n }\n\n inputs.push({\n type: 'image',\n url: imageUrl,\n });\n }\n\n return inputs;\n};\n\nexport const resolveCodexReasoningEffort = ({\n reasoningEnabled,\n modelConfig,\n}: {\n reasoningEnabled?: TModelReasoningEnabled;\n modelConfig: IModelConfig;\n}): CodexReasoningEffort | undefined => {\n if (reasoningEnabled === true) return 'high';\n if (reasoningEnabled === false) return 'none';\n if (reasoningEnabled === 'default') return undefined;\n\n const normalized = modelConfig.reasoningEffort?.trim().toLowerCase();\n if (\n normalized === 'none' ||\n normalized === 'minimal' ||\n normalized === 'low' ||\n normalized === 'medium' ||\n normalized === 'high' ||\n normalized === 'xhigh'\n ) {\n return normalized;\n }\n\n return 'none';\n};\n\nexport const buildCodexTurnPayloadFromMessages = (\n messages: ChatCompletionMessageParam[],\n): {\n developerInstructions?: string;\n input: CodexTurnInput[];\n} => {\n const developerInstructionParts: string[] = [];\n const transcriptParts: string[] = [];\n const imageInputs: Array<CodexImageInput | CodexLocalImageInput> = [];\n\n for (const message of messages) {\n const role = String((message as any).role || 'user');\n const text = extractTextFromMessage(message);\n\n if (role === 'system') {\n if (text.trim()) developerInstructionParts.push(text.trim());\n continue;\n }\n\n const roleTag = role.toUpperCase();\n if (text.trim()) {\n transcriptParts.push(`[${roleTag}]\\n${text.trim()}`);\n } else {\n transcriptParts.push(`[${roleTag}]\\n(no text content)`);\n }\n\n if (role === 'user') {\n imageInputs.push(...extractImageInputs(message));\n }\n }\n\n const fullTranscript = transcriptParts.join('\\n\\n');\n const transcriptText =\n (fullTranscript.length > CODEX_TEXT_INPUT_MAX_LENGTH\n ? fullTranscript.slice(-CODEX_TEXT_INPUT_MAX_LENGTH)\n : fullTranscript) || 'Please answer the latest user request.';\n\n const input: CodexTurnInput[] = [\n {\n type: 'text',\n text: transcriptText,\n text_elements: [],\n },\n ...imageInputs,\n ];\n\n const developerInstructions = developerInstructionParts.length\n ? developerInstructionParts.join('\\n\\n')\n : undefined;\n\n return {\n developerInstructions,\n input,\n };\n};\n\nclass CodexAppServerConnection {\n private child: any;\n private lineReader: any;\n private pendingMessages: JsonRpcMessage[] = [];\n private lineBuffer: string[] = [];\n private nextRequestId = 1;\n private closed = false;\n private lastExitCode: number | null = null;\n private processErrorMessage: string | null = null;\n private stderrBuffer = '';\n\n private constructor(child: any, lineReader: any) {\n this.child = child;\n this.lineReader = lineReader;\n }\n\n static async create(): Promise<CodexAppServerConnection> {\n if (ifInBrowser) {\n throw new Error(\n 'codex app-server provider is not supported in browser runtime',\n );\n }\n\n const childProcessModuleName = 'node:child_process';\n const readlineModuleName = 'node:readline';\n const { spawn } = await import(childProcessModuleName);\n const readline = await import(readlineModuleName);\n\n const child = spawn('codex', ['app-server'], {\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n\n if (!child.stdin || !child.stdout || !child.stderr) {\n throw new Error('failed to start codex app-server: stdio unavailable');\n }\n\n const lineReader = readline.createInterface({\n input: child.stdout,\n crlfDelay: Number.POSITIVE_INFINITY,\n });\n const connection = new CodexAppServerConnection(child, lineReader);\n connection.detachFromEventLoop();\n connection.attachProcessListeners();\n await connection.initializeHandshake();\n\n return connection;\n }\n\n isClosed(): boolean {\n return this.closed;\n }\n\n async runTurn({\n messages,\n modelConfig,\n stream,\n onChunk,\n reasoningEnabled,\n abortSignal,\n }: {\n messages: ChatCompletionMessageParam[];\n modelConfig: IModelConfig;\n stream?: boolean;\n onChunk?: StreamingCallback;\n reasoningEnabled?: TModelReasoningEnabled;\n abortSignal?: AbortSignal;\n }): Promise<CodexTurnResult> {\n const startTime = Date.now();\n const timeoutMs = modelConfig.timeout || CODEX_DEFAULT_TIMEOUT_MS;\n const deadlineAt = Date.now() + timeoutMs;\n const isStreaming = !!(stream && onChunk);\n\n const { developerInstructions, input } =\n buildCodexTurnPayloadFromMessages(messages);\n const effort = resolveCodexReasoningEffort({\n reasoningEnabled,\n modelConfig,\n });\n\n let threadId: string | undefined;\n let turnId: string | undefined;\n let latestErrorMessage: string | undefined;\n let accumulatedText = '';\n let accumulatedReasoning = '';\n let latestUsage: AIUsageInfo | undefined;\n\n const emitChunk = ({\n content,\n reasoning,\n isComplete,\n usage,\n }: {\n content: string;\n reasoning: string;\n isComplete: boolean;\n usage?: AIUsageInfo;\n }) => {\n if (!isStreaming || !onChunk) return;\n const chunk: CodeGenerationChunk = {\n content,\n reasoning_content: reasoning,\n accumulated: accumulatedText,\n isComplete,\n usage,\n };\n onChunk(chunk);\n };\n\n try {\n const threadStartResponse = await this.request<CodexThreadStartResponse>({\n method: 'thread/start',\n params: {\n model: modelConfig.modelName,\n cwd: process.cwd(),\n approvalPolicy: 'never',\n sandbox: 'read-only',\n ephemeral: true,\n experimentalRawEvents: false,\n persistExtendedHistory: false,\n developerInstructions: developerInstructions || null,\n },\n deadlineAt,\n abortSignal,\n });\n\n threadId = threadStartResponse?.thread?.id;\n if (!threadId) {\n throw new Error('thread/start did not return a thread id');\n }\n\n const turnStartResponse = await this.request<CodexTurnStartResponse>({\n method: 'turn/start',\n params: {\n threadId,\n input,\n effort,\n },\n deadlineAt,\n abortSignal,\n });\n\n turnId = turnStartResponse?.turn?.id;\n if (!turnId) {\n throw new Error('turn/start did not return a turn id');\n }\n\n let turnStatus: string | undefined;\n while (!turnStatus) {\n const message = await this.nextMessage({ deadlineAt, abortSignal });\n\n if (this.isResponseMessage(message)) {\n // No concurrent requests in adapter runtime.\n continue;\n }\n\n if (this.isRequestMessage(message)) {\n await this.respondToServerRequest(message);\n continue;\n }\n\n const notification = message as JsonRpcNotification;\n const method = notification.method;\n const params = notification.params || {};\n\n if (method === 'error') {\n const messageText =\n params.error?.message ||\n params.message ||\n 'codex app-server reported turn error';\n latestErrorMessage = String(messageText);\n continue;\n }\n\n if (\n method === 'item/agentMessage/delta' &&\n params.threadId === threadId &&\n params.turnId === turnId\n ) {\n const delta = String(params.delta || '');\n if (delta) {\n accumulatedText += delta;\n emitChunk({\n content: delta,\n reasoning: '',\n isComplete: false,\n });\n }\n continue;\n }\n\n if (\n (method === 'item/reasoning/summaryTextDelta' ||\n method === 'item/reasoning/textDelta') &&\n params.threadId === threadId &&\n params.turnId === turnId\n ) {\n const delta = String(params.delta || '');\n if (delta) {\n accumulatedReasoning += delta;\n emitChunk({\n content: '',\n reasoning: delta,\n isComplete: false,\n });\n }\n continue;\n }\n\n if (\n method === 'item/completed' &&\n params.threadId === threadId &&\n params.turnId === turnId &&\n params.item?.type === 'agentMessage' &&\n typeof params.item?.text === 'string' &&\n !accumulatedText\n ) {\n accumulatedText = params.item.text;\n continue;\n }\n\n if (\n method === 'thread/tokenUsage/updated' &&\n params.threadId === threadId &&\n params.turnId === turnId\n ) {\n latestUsage = this.mapUsage({\n usage: params as CodexUsageNotification,\n modelConfig,\n turnId,\n startTime,\n });\n continue;\n }\n\n if (\n method === 'turn/completed' &&\n params.threadId === threadId &&\n params.turn?.id === turnId\n ) {\n turnStatus = String(params.turn.status || '');\n latestErrorMessage =\n params.turn?.error?.message || latestErrorMessage || undefined;\n break;\n }\n }\n\n if (turnStatus !== 'completed') {\n throw new Error(\n latestErrorMessage ||\n `codex turn finished with status \"${turnStatus || 'unknown'}\"`,\n );\n }\n\n if (isStreaming) {\n emitChunk({\n content: '',\n reasoning: '',\n isComplete: true,\n usage: latestUsage,\n });\n }\n\n return {\n content: accumulatedText,\n reasoning_content: accumulatedReasoning || undefined,\n usage: latestUsage,\n isStreamed: isStreaming,\n };\n } catch (error) {\n if (isAbortError(error) && threadId && turnId) {\n await this.request({\n method: 'turn/interrupt',\n params: {\n threadId,\n turnId,\n },\n deadlineAt: Date.now() + 5_000,\n }).catch(() => {});\n }\n throw error;\n } finally {\n if (threadId) {\n await this.request({\n method: 'thread/unsubscribe',\n params: { threadId },\n deadlineAt: Date.now() + CODEX_DEFAULT_CLEANUP_TIMEOUT_MS,\n }).catch((error) => {\n warnCodex(\n `failed to unsubscribe codex thread ${threadId}: ${String(error)}`,\n );\n });\n }\n }\n }\n\n async dispose(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n\n try {\n this.lineReader?.close?.();\n } catch {}\n\n try {\n this.child?.stdin?.end?.();\n } catch {}\n\n try {\n this.child?.kill?.();\n } catch {}\n }\n\n private attachProcessListeners() {\n this.lineReader.on('line', (line: string) => {\n this.lineBuffer.push(line);\n });\n\n this.child.stderr.on('data', (chunk: Buffer | string) => {\n const text = Buffer.isBuffer(chunk)\n ? chunk.toString('utf8')\n : String(chunk);\n this.stderrBuffer += text;\n if (this.stderrBuffer.length > 8192) {\n this.stderrBuffer = this.stderrBuffer.slice(-8192);\n }\n });\n\n this.child.on('exit', (code: number | null) => {\n this.closed = true;\n this.lastExitCode = code;\n });\n\n this.child.on('error', (error: Error) => {\n this.closed = true;\n this.processErrorMessage = error.message;\n });\n }\n\n /**\n * Keep codex process reusable but let short-lived callers exit naturally.\n * Without unref, one-shot scripts/tests that call AI once can hang.\n */\n private detachFromEventLoop() {\n this.child.unref?.();\n this.child.stdin?.unref?.();\n this.child.stdout?.unref?.();\n this.child.stderr?.unref?.();\n }\n\n private async initializeHandshake() {\n const deadlineAt = Date.now() + CODEX_DEFAULT_PROCESS_START_TIMEOUT_MS;\n await this.request({\n method: 'initialize',\n params: {\n clientInfo: {\n name: 'midscene_codex_provider',\n title: 'Midscene Codex Provider',\n version: '1.0.0',\n },\n capabilities: {\n experimentalApi: false,\n },\n },\n deadlineAt,\n });\n await this.sendMessage({\n method: 'initialized',\n });\n }\n\n private mapUsage({\n usage,\n modelConfig,\n turnId,\n startTime,\n }: {\n usage: CodexUsageNotification;\n modelConfig: IModelConfig;\n turnId: string;\n startTime: number;\n }): AIUsageInfo | undefined {\n const tokenUsage = usage.tokenUsage;\n const picked = tokenUsage?.last || tokenUsage?.total;\n if (!picked) return undefined;\n\n return {\n ...picked,\n prompt_tokens: picked.inputTokens ?? 0,\n completion_tokens: picked.outputTokens ?? 0,\n total_tokens: picked.totalTokens ?? 0,\n cached_input: picked.cachedInputTokens ?? 0,\n time_cost: Date.now() - startTime,\n model_name: modelConfig.modelName,\n model_description: modelConfig.modelDescription,\n response_model_name: undefined,\n slot: modelConfig.slot,\n intent: undefined,\n request_id: turnId,\n } satisfies AIUsageInfo;\n }\n\n private isRequestMessage(message: JsonRpcMessage): message is JsonRpcRequest {\n return (\n typeof (message as any)?.method === 'string' &&\n (message as any)?.id !== undefined\n );\n }\n\n private isResponseMessage(\n message: JsonRpcMessage,\n ): message is JsonRpcResponse {\n return (\n (message as any)?.id !== undefined &&\n ((message as any)?.result !== undefined ||\n (message as any)?.error !== undefined) &&\n typeof (message as any)?.method !== 'string'\n );\n }\n\n private async request<T = unknown>({\n method,\n params,\n deadlineAt,\n abortSignal,\n }: {\n method: string;\n params: unknown;\n deadlineAt?: number;\n abortSignal?: AbortSignal;\n }): Promise<T> {\n const requestId = this.nextRequestId++;\n\n await this.sendMessage({\n id: requestId,\n method,\n params,\n });\n\n while (true) {\n const message = await this.nextMessage({\n deadlineAt,\n abortSignal,\n includePending: false,\n });\n\n if (this.isResponseMessage(message) && message.id === requestId) {\n if (message.error) {\n throw new Error(\n `codex app-server ${method} failed: ${\n message.error.message || 'unknown error'\n }`,\n );\n }\n return (message.result || {}) as T;\n }\n\n if (this.isRequestMessage(message)) {\n await this.respondToServerRequest(message);\n continue;\n }\n\n // Keep unmatched notifications/other responses for later stream handling.\n this.pendingMessages.push(message);\n }\n }\n\n private async respondToServerRequest(request: JsonRpcRequest): Promise<void> {\n const requestId = request.id;\n const method = request.method;\n\n let result: unknown = {};\n if (method === 'item/commandExecution/requestApproval') {\n result = { decision: 'decline' };\n } else if (method === 'item/fileChange/requestApproval') {\n result = { decision: 'decline' };\n } else if (method === 'mcpServer/elicitation/request') {\n result = { action: 'cancel', content: null };\n } else if (method === 'item/tool/requestUserInput') {\n result = { answers: [] };\n } else {\n await this.sendMessage({\n id: requestId,\n error: {\n code: -32601,\n message: `unsupported server request: ${method}`,\n },\n });\n return;\n }\n\n await this.sendMessage({\n id: requestId,\n result,\n });\n }\n\n private async nextMessage({\n deadlineAt,\n abortSignal,\n includePending = true,\n }: {\n deadlineAt?: number;\n abortSignal?: AbortSignal;\n includePending?: boolean;\n }): Promise<JsonRpcMessage> {\n if (includePending && this.pendingMessages.length) {\n return this.pendingMessages.shift() as JsonRpcMessage;\n }\n\n while (true) {\n if (abortSignal?.aborted) {\n throw new Error('codex app-server request aborted');\n }\n\n if (deadlineAt && Date.now() > deadlineAt) {\n throw new Error('codex app-server request timed out');\n }\n\n if (this.lineBuffer.length) {\n const line = this.lineBuffer.shift()!;\n const trimmed = line.trim();\n if (!trimmed) continue;\n\n let parsed: JsonRpcMessage;\n try {\n parsed = JSON.parse(trimmed);\n } catch (error) {\n warnCodex(\n `ignored non-JSON message from codex app-server: ${trimmed}`,\n );\n continue;\n }\n\n return parsed;\n }\n\n if (this.closed) {\n throw this.createClosedConnectionError();\n }\n\n await new Promise((resolve) => setTimeout(resolve, 20));\n }\n }\n\n private async sendMessage(payload: Record<string, unknown>): Promise<void> {\n if (this.closed) {\n throw this.createClosedConnectionError();\n }\n\n const line = JSON.stringify(payload);\n await new Promise<void>((resolve, reject) => {\n this.child.stdin.write(`${line}\\n`, (error: Error | null | undefined) => {\n if (error) {\n reject(\n new Error(\n `failed writing to codex app-server stdin: ${error.message}`,\n ),\n );\n return;\n }\n resolve();\n });\n });\n }\n\n private createClosedConnectionError(): Error {\n const stderr = this.stderrBuffer.trim();\n if (this.processErrorMessage) {\n return new Error(\n stderr\n ? `codex app-server process error: ${this.processErrorMessage}. stderr=${stderr}`\n : `codex app-server process error: ${this.processErrorMessage}`,\n );\n }\n\n return new Error(\n stderr\n ? `codex app-server connection closed (exitCode=${this.lastExitCode}). stderr=${stderr}`\n : `codex app-server connection closed (exitCode=${this.lastExitCode})`,\n );\n }\n}\n\nclass CodexAppServerConnectionManager {\n private connection: CodexAppServerConnection | null = null;\n private runner = new SerializedRunner();\n\n async runTurn({\n messages,\n modelConfig,\n stream,\n onChunk,\n reasoningEnabled,\n abortSignal,\n }: {\n messages: ChatCompletionMessageParam[];\n modelConfig: IModelConfig;\n stream?: boolean;\n onChunk?: StreamingCallback;\n reasoningEnabled?: TModelReasoningEnabled;\n abortSignal?: AbortSignal;\n }): Promise<CodexTurnResult> {\n return this.runner.run(async () => {\n const connection = await this.getConnection();\n try {\n return await connection.runTurn({\n messages,\n modelConfig,\n stream,\n onChunk,\n reasoningEnabled,\n abortSignal,\n });\n } catch (error) {\n if (connection.isClosed() || !isAbortError(error)) {\n await this.resetConnection();\n }\n throw error;\n }\n });\n }\n\n async shutdownForTests(): Promise<void> {\n await this.resetConnection();\n }\n\n private async getConnection(): Promise<CodexAppServerConnection> {\n if (!this.connection || this.connection.isClosed()) {\n this.connection = await CodexAppServerConnection.create();\n debugCodex('started long-lived codex app-server connection');\n }\n return this.connection;\n }\n\n private async resetConnection(): Promise<void> {\n if (!this.connection) return;\n const staleConnection = this.connection;\n this.connection = null;\n await staleConnection.dispose();\n debugCodex('reset codex app-server connection');\n }\n}\n\nconst codexConnectionManager = new CodexAppServerConnectionManager();\n\nexport async function callAIWithCodexAppServer(\n messages: ChatCompletionMessageParam[],\n modelConfig: IModelConfig,\n options?: {\n stream?: boolean;\n onChunk?: StreamingCallback;\n reasoningEnabled?: TModelReasoningEnabled;\n abortSignal?: AbortSignal;\n },\n): Promise<CodexTurnResult> {\n if (ifInBrowser) {\n throw new Error(\n 'codex app-server provider is not supported in browser runtime',\n );\n }\n\n return codexConnectionManager.runTurn({\n messages,\n modelConfig,\n stream: options?.stream,\n onChunk: options?.onChunk,\n reasoningEnabled: options?.reasoningEnabled,\n abortSignal: options?.abortSignal,\n });\n}\n\nexport async function __shutdownCodexAppServerForTests() {\n await codexConnectionManager.shutdownForTests();\n}\n"],"names":["CODEX_PROVIDER_SCHEME","CODEX_DEFAULT_TIMEOUT_MS","CODEX_DEFAULT_PROCESS_START_TIMEOUT_MS","CODEX_DEFAULT_CLEANUP_TIMEOUT_MS","CODEX_TEXT_INPUT_MAX_LENGTH","debugCodex","getDebug","warnCodex","SerializedRunner","work","previous","release","Promise","resolve","isCodexAppServerProvider","baseURL","isAbortError","error","Error","message","String","toNonEmptyString","value","trimmed","undefined","normalizeCodexLocalImagePath","imageUrl","platform","process","parsed","URL","pathname","decodeURIComponent","host","windowsPath","extractTextFromMessage","content","Array","part","Boolean","extractImageInputs","inputs","partType","path","resolveCodexReasoningEffort","reasoningEnabled","modelConfig","normalized","buildCodexTurnPayloadFromMessages","messages","developerInstructionParts","transcriptParts","imageInputs","role","text","roleTag","fullTranscript","transcriptText","input","developerInstructions","CodexAppServerConnection","ifInBrowser","childProcessModuleName","readlineModuleName","spawn","readline","child","lineReader","Number","connection","stream","onChunk","abortSignal","startTime","Date","timeoutMs","deadlineAt","isStreaming","effort","threadId","turnId","latestErrorMessage","accumulatedText","accumulatedReasoning","latestUsage","emitChunk","reasoning","isComplete","usage","chunk","threadStartResponse","turnStartResponse","turnStatus","notification","method","params","messageText","delta","line","Buffer","code","tokenUsage","picked","requestId","request","result","includePending","JSON","setTimeout","payload","reject","stderr","CodexAppServerConnectionManager","staleConnection","codexConnectionManager","callAIWithCodexAppServer","options","__shutdownCodexAppServerForTests"],"mappings":"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"}
1
+ {"version":3,"file":"ai-model/service-caller/codex-app-server.mjs","sources":["../../../../src/ai-model/service-caller/codex-app-server.ts"],"sourcesContent":["import type {\n AIUsageInfo,\n CodeGenerationChunk,\n StreamingCallback,\n} from '@/types';\nimport type {\n IModelConfig,\n TModelReasoningEnabled,\n} from '@midscene/shared/env';\nimport { getDebug } from '@midscene/shared/logger';\nimport { ifInBrowser } from '@midscene/shared/utils';\nimport type { ChatCompletionMessageParam } from 'openai/resources/index';\n\nconst CODEX_PROVIDER_SCHEME = 'codex://';\nconst CODEX_DEFAULT_TIMEOUT_MS = 10 * 60 * 1000;\nconst CODEX_DEFAULT_PROCESS_START_TIMEOUT_MS = 15 * 1000;\nconst CODEX_DEFAULT_CLEANUP_TIMEOUT_MS = 8 * 1000;\nconst CODEX_TEXT_INPUT_MAX_LENGTH = 256 * 1024;\n\nconst debugCodex = getDebug('ai:call:codex');\nconst warnCodex = getDebug('ai:call:codex', { console: true });\n\ntype CodexReasoningEffort =\n | 'none'\n | 'minimal'\n | 'low'\n | 'medium'\n | 'high'\n | 'xhigh';\n\ntype JsonRpcRequest = {\n id: string | number;\n method: string;\n params?: unknown;\n};\n\ntype JsonRpcResponse = {\n id: string | number;\n result?: unknown;\n error?: {\n code?: number;\n message?: string;\n data?: unknown;\n };\n};\n\ntype JsonRpcNotification = {\n method: string;\n params?: Record<string, any>;\n};\n\ntype JsonRpcMessage = JsonRpcRequest | JsonRpcResponse | JsonRpcNotification;\n\ntype CodexTextInput = {\n type: 'text';\n text: string;\n text_elements: any[];\n};\n\n// Codex app-server v2 accepts `detail` on both image input variants:\n// https://github.com/openai/codex/blob/main/codex-rs/app-server-protocol/src/protocol/v2/turn.rs#L276-L329\n// Codex has also reported `original` as a supported value in production:\n// https://github.com/openai/codex/issues/24797\ntype CodexImageInput = {\n type: 'image';\n url: string;\n detail?: CodexImageDetail;\n};\n\ntype CodexLocalImageInput = {\n type: 'localImage';\n path: string;\n detail?: CodexImageDetail;\n};\n\ntype CodexTurnInput = CodexTextInput | CodexImageInput | CodexLocalImageInput;\n\ntype CodexImageDetail = 'auto' | 'low' | 'high' | 'original';\n\ntype CodexTurnResult = {\n content: string;\n reasoning_content?: string;\n usage?: AIUsageInfo;\n isStreamed: boolean;\n};\n\ntype CodexTurnStartResponse = {\n turn?: {\n id?: string;\n };\n};\n\ntype CodexThreadStartResponse = {\n thread?: {\n id?: string;\n };\n};\n\ntype CodexUsageNotification = {\n threadId?: string;\n turnId?: string;\n tokenUsage?: {\n total?: {\n totalTokens?: number;\n inputTokens?: number;\n cachedInputTokens?: number;\n outputTokens?: number;\n reasoningOutputTokens?: number;\n };\n last?: {\n totalTokens?: number;\n inputTokens?: number;\n cachedInputTokens?: number;\n outputTokens?: number;\n reasoningOutputTokens?: number;\n };\n };\n};\n\nclass SerializedRunner {\n private tail: Promise<void> = Promise.resolve();\n\n async run<T>(work: () => Promise<T>): Promise<T> {\n const previous = this.tail;\n let release!: () => void;\n this.tail = new Promise<void>((resolve) => {\n release = resolve;\n });\n\n await previous;\n try {\n return await work();\n } finally {\n release();\n }\n }\n}\n\nexport const isCodexAppServerProvider = (baseURL?: string): boolean => {\n if (!baseURL) return false;\n return baseURL.trim().toLowerCase().startsWith(CODEX_PROVIDER_SCHEME);\n};\n\nconst isAbortError = (error: unknown): boolean => {\n if (!error) return false;\n if (error instanceof Error && error.name === 'AbortError') return true;\n const message =\n error instanceof Error ? error.message : String(error ?? 'unknown error');\n return /aborted|abort/i.test(message);\n};\n\nconst toNonEmptyString = (value: unknown): string | undefined => {\n if (typeof value !== 'string') return undefined;\n const trimmed = value.trim();\n return trimmed || undefined;\n};\n\nconst toCodexImageDetail = (value: unknown): CodexImageDetail | undefined =>\n value === 'auto' ||\n value === 'low' ||\n value === 'high' ||\n value === 'original'\n ? value\n : undefined;\n\nexport const normalizeCodexLocalImagePath = (\n imageUrl: string,\n platform: NodeJS.Platform = process.platform,\n): string => {\n if (!imageUrl.startsWith('file://')) {\n return imageUrl;\n }\n\n try {\n const parsed = new URL(imageUrl);\n const pathname = decodeURIComponent(parsed.pathname);\n const host = parsed.hostname.toLowerCase();\n\n if (platform === 'win32') {\n const windowsPath = pathname\n .replace(/\\//g, '\\\\')\n .replace(/^\\\\([A-Za-z]:)/, '$1');\n\n if (host && host !== 'localhost') {\n return `\\\\\\\\${parsed.hostname}${windowsPath}`;\n }\n\n return windowsPath;\n }\n\n if (host && host !== 'localhost') {\n return `//${parsed.hostname}${pathname}`;\n }\n\n return pathname;\n } catch {\n return decodeURIComponent(imageUrl.slice('file://'.length));\n }\n};\n\nconst extractTextFromMessage = (\n message: ChatCompletionMessageParam,\n): string => {\n const content = (message as any).content;\n if (typeof content === 'string') {\n return content;\n }\n\n if (Array.isArray(content)) {\n return content\n .map((part) => {\n if (!part || typeof part !== 'object') return '';\n\n if (part.type === 'text' && typeof part.text === 'string') {\n return part.text;\n }\n\n if (part.type === 'input_text' && typeof part.text === 'string') {\n return part.text;\n }\n\n return '';\n })\n .filter(Boolean)\n .join('\\n');\n }\n\n return '';\n};\n\nconst extractImageInputs = (\n message: ChatCompletionMessageParam,\n imageDetailOverride?: CodexImageDetail,\n): Array<CodexImageInput | CodexLocalImageInput> => {\n const content = (message as any).content;\n if (!Array.isArray(content)) return [];\n\n const inputs: Array<CodexImageInput | CodexLocalImageInput> = [];\n for (const part of content) {\n if (!part || typeof part !== 'object') continue;\n\n const partType = String(part.type || '');\n const imageUrl =\n partType === 'image_url'\n ? toNonEmptyString(part.image_url?.url)\n : partType === 'input_image'\n ? toNonEmptyString(part.image_url || part.url)\n : undefined;\n const imageDetail =\n imageDetailOverride ??\n toCodexImageDetail(\n partType === 'image_url' ? part.image_url?.detail : part.detail,\n );\n\n if (!imageUrl) continue;\n\n if (\n imageUrl.startsWith('/') ||\n imageUrl.startsWith('./') ||\n imageUrl.startsWith('../') ||\n imageUrl.startsWith('file://')\n ) {\n const path = imageUrl.startsWith('file://')\n ? normalizeCodexLocalImagePath(imageUrl)\n : imageUrl;\n\n inputs.push({\n type: 'localImage',\n path,\n ...(imageDetail ? { detail: imageDetail } : {}),\n });\n continue;\n }\n\n inputs.push({\n type: 'image',\n url: imageUrl,\n ...(imageDetail ? { detail: imageDetail } : {}),\n });\n }\n\n return inputs;\n};\n\nexport const resolveCodexReasoningEffort = ({\n reasoningEnabled,\n modelConfig,\n}: {\n reasoningEnabled?: TModelReasoningEnabled;\n modelConfig: IModelConfig;\n}): CodexReasoningEffort | undefined => {\n if (reasoningEnabled !== true) return 'none';\n\n const normalized = modelConfig.reasoningEffort?.trim().toLowerCase();\n if (\n normalized === 'none' ||\n normalized === 'minimal' ||\n normalized === 'low' ||\n normalized === 'medium' ||\n normalized === 'high' ||\n normalized === 'xhigh'\n ) {\n return normalized;\n }\n\n return 'medium';\n};\n\nexport const buildCodexTurnPayloadFromMessages = (\n messages: ChatCompletionMessageParam[],\n imageDetailOverride?: CodexImageDetail,\n): {\n developerInstructions?: string;\n input: CodexTurnInput[];\n} => {\n const developerInstructionParts: string[] = [];\n const transcriptParts: string[] = [];\n const imageInputs: Array<CodexImageInput | CodexLocalImageInput> = [];\n\n for (const message of messages) {\n const role = String((message as any).role || 'user');\n const text = extractTextFromMessage(message);\n\n if (role === 'system') {\n if (text.trim()) developerInstructionParts.push(text.trim());\n continue;\n }\n\n const roleTag = role.toUpperCase();\n if (text.trim()) {\n transcriptParts.push(`[${roleTag}]\\n${text.trim()}`);\n } else {\n transcriptParts.push(`[${roleTag}]\\n(no text content)`);\n }\n\n if (role === 'user') {\n imageInputs.push(...extractImageInputs(message, imageDetailOverride));\n }\n }\n\n const fullTranscript = transcriptParts.join('\\n\\n');\n const transcriptText =\n (fullTranscript.length > CODEX_TEXT_INPUT_MAX_LENGTH\n ? fullTranscript.slice(-CODEX_TEXT_INPUT_MAX_LENGTH)\n : fullTranscript) || 'Please answer the latest user request.';\n\n const input: CodexTurnInput[] = [\n {\n type: 'text',\n text: transcriptText,\n text_elements: [],\n },\n ...imageInputs,\n ];\n\n const developerInstructions = developerInstructionParts.length\n ? developerInstructionParts.join('\\n\\n')\n : undefined;\n\n return {\n developerInstructions,\n input,\n };\n};\n\nclass CodexAppServerConnection {\n private child: any;\n private lineReader: any;\n private pendingMessages: JsonRpcMessage[] = [];\n private lineBuffer: string[] = [];\n private nextRequestId = 1;\n private closed = false;\n private lastExitCode: number | null = null;\n private processErrorMessage: string | null = null;\n private stderrBuffer = '';\n\n private constructor(child: any, lineReader: any) {\n this.child = child;\n this.lineReader = lineReader;\n }\n\n static async create(): Promise<CodexAppServerConnection> {\n if (ifInBrowser) {\n throw new Error(\n 'codex app-server provider is not supported in browser runtime',\n );\n }\n\n const childProcessModuleName = 'node:child_process';\n const readlineModuleName = 'node:readline';\n const { spawn } = await import(childProcessModuleName);\n const readline = await import(readlineModuleName);\n\n const child = spawn('codex', ['app-server'], {\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n\n if (!child.stdin || !child.stdout || !child.stderr) {\n throw new Error('failed to start codex app-server: stdio unavailable');\n }\n\n const lineReader = readline.createInterface({\n input: child.stdout,\n crlfDelay: Number.POSITIVE_INFINITY,\n });\n const connection = new CodexAppServerConnection(child, lineReader);\n connection.detachFromEventLoop();\n connection.attachProcessListeners();\n await connection.initializeHandshake();\n\n return connection;\n }\n\n isClosed(): boolean {\n return this.closed;\n }\n\n async runTurn({\n messages,\n modelConfig,\n stream,\n onChunk,\n reasoningEnabled,\n abortSignal,\n imageDetail,\n }: {\n messages: ChatCompletionMessageParam[];\n modelConfig: IModelConfig;\n stream?: boolean;\n onChunk?: StreamingCallback;\n reasoningEnabled?: TModelReasoningEnabled;\n abortSignal?: AbortSignal;\n imageDetail?: CodexImageDetail;\n }): Promise<CodexTurnResult> {\n const startTime = Date.now();\n const timeoutMs = modelConfig.timeout || CODEX_DEFAULT_TIMEOUT_MS;\n const deadlineAt = Date.now() + timeoutMs;\n const isStreaming = !!(stream && onChunk);\n\n const { developerInstructions, input } = buildCodexTurnPayloadFromMessages(\n messages,\n imageDetail,\n );\n const effort = resolveCodexReasoningEffort({\n reasoningEnabled,\n modelConfig,\n });\n\n let threadId: string | undefined;\n let turnId: string | undefined;\n let latestErrorMessage: string | undefined;\n let accumulatedText = '';\n let accumulatedReasoning = '';\n let latestUsage: AIUsageInfo | undefined;\n\n const emitChunk = ({\n content,\n reasoning,\n isComplete,\n usage,\n }: {\n content: string;\n reasoning: string;\n isComplete: boolean;\n usage?: AIUsageInfo;\n }) => {\n if (!isStreaming || !onChunk) return;\n const chunk: CodeGenerationChunk = {\n content,\n reasoning_content: reasoning,\n accumulated: accumulatedText,\n isComplete,\n usage,\n };\n onChunk(chunk);\n };\n\n try {\n const threadStartResponse = await this.request<CodexThreadStartResponse>({\n method: 'thread/start',\n params: {\n model: modelConfig.modelName,\n cwd: process.cwd(),\n approvalPolicy: 'never',\n sandbox: 'read-only',\n ephemeral: true,\n experimentalRawEvents: false,\n persistExtendedHistory: false,\n developerInstructions: developerInstructions || null,\n },\n deadlineAt,\n abortSignal,\n });\n\n threadId = threadStartResponse?.thread?.id;\n if (!threadId) {\n throw new Error('thread/start did not return a thread id');\n }\n\n const turnStartResponse = await this.request<CodexTurnStartResponse>({\n method: 'turn/start',\n params: {\n threadId,\n input,\n effort,\n },\n deadlineAt,\n abortSignal,\n });\n\n turnId = turnStartResponse?.turn?.id;\n if (!turnId) {\n throw new Error('turn/start did not return a turn id');\n }\n\n let turnStatus: string | undefined;\n while (!turnStatus) {\n const message = await this.nextMessage({ deadlineAt, abortSignal });\n\n if (this.isResponseMessage(message)) {\n // No concurrent requests in adapter runtime.\n continue;\n }\n\n if (this.isRequestMessage(message)) {\n await this.respondToServerRequest(message);\n continue;\n }\n\n const notification = message as JsonRpcNotification;\n const method = notification.method;\n const params = notification.params || {};\n\n if (method === 'error') {\n const messageText =\n params.error?.message ||\n params.message ||\n 'codex app-server reported turn error';\n latestErrorMessage = String(messageText);\n continue;\n }\n\n if (\n method === 'item/agentMessage/delta' &&\n params.threadId === threadId &&\n params.turnId === turnId\n ) {\n const delta = String(params.delta || '');\n if (delta) {\n accumulatedText += delta;\n emitChunk({\n content: delta,\n reasoning: '',\n isComplete: false,\n });\n }\n continue;\n }\n\n if (\n (method === 'item/reasoning/summaryTextDelta' ||\n method === 'item/reasoning/textDelta') &&\n params.threadId === threadId &&\n params.turnId === turnId\n ) {\n const delta = String(params.delta || '');\n if (delta) {\n accumulatedReasoning += delta;\n emitChunk({\n content: '',\n reasoning: delta,\n isComplete: false,\n });\n }\n continue;\n }\n\n if (\n method === 'item/completed' &&\n params.threadId === threadId &&\n params.turnId === turnId &&\n params.item?.type === 'agentMessage' &&\n typeof params.item?.text === 'string' &&\n !accumulatedText\n ) {\n accumulatedText = params.item.text;\n continue;\n }\n\n if (\n method === 'thread/tokenUsage/updated' &&\n params.threadId === threadId &&\n params.turnId === turnId\n ) {\n latestUsage = this.mapUsage({\n usage: params as CodexUsageNotification,\n modelConfig,\n turnId,\n startTime,\n });\n continue;\n }\n\n if (\n method === 'turn/completed' &&\n params.threadId === threadId &&\n params.turn?.id === turnId\n ) {\n turnStatus = String(params.turn.status || '');\n latestErrorMessage =\n params.turn?.error?.message || latestErrorMessage || undefined;\n break;\n }\n }\n\n if (turnStatus !== 'completed') {\n throw new Error(\n latestErrorMessage ||\n `codex turn finished with status \"${turnStatus || 'unknown'}\"`,\n );\n }\n\n if (isStreaming) {\n emitChunk({\n content: '',\n reasoning: '',\n isComplete: true,\n usage: latestUsage,\n });\n }\n\n return {\n content: accumulatedText,\n reasoning_content: accumulatedReasoning || undefined,\n usage: latestUsage,\n isStreamed: isStreaming,\n };\n } catch (error) {\n if (isAbortError(error) && threadId && turnId) {\n await this.request({\n method: 'turn/interrupt',\n params: {\n threadId,\n turnId,\n },\n deadlineAt: Date.now() + 5_000,\n }).catch(() => {});\n }\n throw error;\n } finally {\n if (threadId) {\n await this.request({\n method: 'thread/unsubscribe',\n params: { threadId },\n deadlineAt: Date.now() + CODEX_DEFAULT_CLEANUP_TIMEOUT_MS,\n }).catch((error) => {\n warnCodex(\n `failed to unsubscribe codex thread ${threadId}: ${String(error)}`,\n );\n });\n }\n }\n }\n\n async dispose(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n\n try {\n this.lineReader?.close?.();\n } catch {}\n\n try {\n this.child?.stdin?.end?.();\n } catch {}\n\n try {\n this.child?.kill?.();\n } catch {}\n }\n\n private attachProcessListeners() {\n this.lineReader.on('line', (line: string) => {\n this.lineBuffer.push(line);\n });\n\n this.child.stderr.on('data', (chunk: Buffer | string) => {\n const text = Buffer.isBuffer(chunk)\n ? chunk.toString('utf8')\n : String(chunk);\n this.stderrBuffer += text;\n if (this.stderrBuffer.length > 8192) {\n this.stderrBuffer = this.stderrBuffer.slice(-8192);\n }\n });\n\n this.child.on('exit', (code: number | null) => {\n this.closed = true;\n this.lastExitCode = code;\n });\n\n this.child.on('error', (error: Error) => {\n this.closed = true;\n this.processErrorMessage = error.message;\n });\n }\n\n /**\n * Keep codex process reusable but let short-lived callers exit naturally.\n * Without unref, one-shot scripts/tests that call AI once can hang.\n */\n private detachFromEventLoop() {\n this.child.unref?.();\n this.child.stdin?.unref?.();\n this.child.stdout?.unref?.();\n this.child.stderr?.unref?.();\n }\n\n private async initializeHandshake() {\n const deadlineAt = Date.now() + CODEX_DEFAULT_PROCESS_START_TIMEOUT_MS;\n await this.request({\n method: 'initialize',\n params: {\n clientInfo: {\n name: 'midscene_codex_provider',\n title: 'Midscene Codex Provider',\n version: '1.0.0',\n },\n capabilities: {\n experimentalApi: false,\n },\n },\n deadlineAt,\n });\n await this.sendMessage({\n method: 'initialized',\n });\n }\n\n private mapUsage({\n usage,\n modelConfig,\n turnId,\n startTime,\n }: {\n usage: CodexUsageNotification;\n modelConfig: IModelConfig;\n turnId: string;\n startTime: number;\n }): AIUsageInfo | undefined {\n const tokenUsage = usage.tokenUsage;\n const picked = tokenUsage?.last || tokenUsage?.total;\n if (!picked) return undefined;\n\n return {\n ...picked,\n prompt_tokens: picked.inputTokens ?? 0,\n completion_tokens: picked.outputTokens ?? 0,\n total_tokens: picked.totalTokens ?? 0,\n cached_input: picked.cachedInputTokens ?? 0,\n time_cost: Date.now() - startTime,\n model_name: modelConfig.modelName,\n model_description: modelConfig.modelDescription,\n response_model_name: undefined,\n slot: modelConfig.slot,\n intent: undefined,\n request_id: turnId,\n } satisfies AIUsageInfo;\n }\n\n private isRequestMessage(message: JsonRpcMessage): message is JsonRpcRequest {\n return (\n typeof (message as any)?.method === 'string' &&\n (message as any)?.id !== undefined\n );\n }\n\n private isResponseMessage(\n message: JsonRpcMessage,\n ): message is JsonRpcResponse {\n return (\n (message as any)?.id !== undefined &&\n ((message as any)?.result !== undefined ||\n (message as any)?.error !== undefined) &&\n typeof (message as any)?.method !== 'string'\n );\n }\n\n private async request<T = unknown>({\n method,\n params,\n deadlineAt,\n abortSignal,\n }: {\n method: string;\n params: unknown;\n deadlineAt?: number;\n abortSignal?: AbortSignal;\n }): Promise<T> {\n const requestId = this.nextRequestId++;\n\n await this.sendMessage({\n id: requestId,\n method,\n params,\n });\n\n while (true) {\n const message = await this.nextMessage({\n deadlineAt,\n abortSignal,\n includePending: false,\n });\n\n if (this.isResponseMessage(message) && message.id === requestId) {\n if (message.error) {\n throw new Error(\n `codex app-server ${method} failed: ${\n message.error.message || 'unknown error'\n }`,\n );\n }\n return (message.result || {}) as T;\n }\n\n if (this.isRequestMessage(message)) {\n await this.respondToServerRequest(message);\n continue;\n }\n\n // Keep unmatched notifications/other responses for later stream handling.\n this.pendingMessages.push(message);\n }\n }\n\n private async respondToServerRequest(request: JsonRpcRequest): Promise<void> {\n const requestId = request.id;\n const method = request.method;\n\n let result: unknown = {};\n if (method === 'item/commandExecution/requestApproval') {\n result = { decision: 'decline' };\n } else if (method === 'item/fileChange/requestApproval') {\n result = { decision: 'decline' };\n } else if (method === 'mcpServer/elicitation/request') {\n result = { action: 'cancel', content: null };\n } else if (method === 'item/tool/requestUserInput') {\n result = { answers: [] };\n } else {\n await this.sendMessage({\n id: requestId,\n error: {\n code: -32601,\n message: `unsupported server request: ${method}`,\n },\n });\n return;\n }\n\n await this.sendMessage({\n id: requestId,\n result,\n });\n }\n\n private async nextMessage({\n deadlineAt,\n abortSignal,\n includePending = true,\n }: {\n deadlineAt?: number;\n abortSignal?: AbortSignal;\n includePending?: boolean;\n }): Promise<JsonRpcMessage> {\n if (includePending && this.pendingMessages.length) {\n return this.pendingMessages.shift() as JsonRpcMessage;\n }\n\n while (true) {\n if (abortSignal?.aborted) {\n throw new Error('codex app-server request aborted');\n }\n\n if (deadlineAt && Date.now() > deadlineAt) {\n throw new Error('codex app-server request timed out');\n }\n\n if (this.lineBuffer.length) {\n const line = this.lineBuffer.shift()!;\n const trimmed = line.trim();\n if (!trimmed) continue;\n\n let parsed: JsonRpcMessage;\n try {\n parsed = JSON.parse(trimmed);\n } catch (error) {\n warnCodex(\n `ignored non-JSON message from codex app-server: ${trimmed}`,\n );\n continue;\n }\n\n return parsed;\n }\n\n if (this.closed) {\n throw this.createClosedConnectionError();\n }\n\n await new Promise((resolve) => setTimeout(resolve, 20));\n }\n }\n\n private async sendMessage(payload: Record<string, unknown>): Promise<void> {\n if (this.closed) {\n throw this.createClosedConnectionError();\n }\n\n const line = JSON.stringify(payload);\n await new Promise<void>((resolve, reject) => {\n this.child.stdin.write(`${line}\\n`, (error: Error | null | undefined) => {\n if (error) {\n reject(\n new Error(\n `failed writing to codex app-server stdin: ${error.message}`,\n ),\n );\n return;\n }\n resolve();\n });\n });\n }\n\n private createClosedConnectionError(): Error {\n const stderr = this.stderrBuffer.trim();\n if (this.processErrorMessage) {\n return new Error(\n stderr\n ? `codex app-server process error: ${this.processErrorMessage}. stderr=${stderr}`\n : `codex app-server process error: ${this.processErrorMessage}`,\n );\n }\n\n return new Error(\n stderr\n ? `codex app-server connection closed (exitCode=${this.lastExitCode}). stderr=${stderr}`\n : `codex app-server connection closed (exitCode=${this.lastExitCode})`,\n );\n }\n}\n\nclass CodexAppServerConnectionManager {\n private connection: CodexAppServerConnection | null = null;\n private runner = new SerializedRunner();\n\n async runTurn({\n messages,\n modelConfig,\n stream,\n onChunk,\n reasoningEnabled,\n abortSignal,\n imageDetail,\n }: {\n messages: ChatCompletionMessageParam[];\n modelConfig: IModelConfig;\n stream?: boolean;\n onChunk?: StreamingCallback;\n reasoningEnabled?: TModelReasoningEnabled;\n abortSignal?: AbortSignal;\n imageDetail?: CodexImageDetail;\n }): Promise<CodexTurnResult> {\n return this.runner.run(async () => {\n const connection = await this.getConnection();\n try {\n return await connection.runTurn({\n messages,\n modelConfig,\n stream,\n onChunk,\n reasoningEnabled,\n abortSignal,\n imageDetail,\n });\n } catch (error) {\n if (connection.isClosed() || !isAbortError(error)) {\n await this.resetConnection();\n }\n throw error;\n }\n });\n }\n\n async shutdownForTests(): Promise<void> {\n await this.resetConnection();\n }\n\n private async getConnection(): Promise<CodexAppServerConnection> {\n if (!this.connection || this.connection.isClosed()) {\n this.connection = await CodexAppServerConnection.create();\n debugCodex('started long-lived codex app-server connection');\n }\n return this.connection;\n }\n\n private async resetConnection(): Promise<void> {\n if (!this.connection) return;\n const staleConnection = this.connection;\n this.connection = null;\n await staleConnection.dispose();\n debugCodex('reset codex app-server connection');\n }\n}\n\nconst codexConnectionManager = new CodexAppServerConnectionManager();\n\nexport async function callAIWithCodexAppServer(\n messages: ChatCompletionMessageParam[],\n modelConfig: IModelConfig,\n options?: {\n stream?: boolean;\n onChunk?: StreamingCallback;\n reasoningEnabled?: TModelReasoningEnabled;\n abortSignal?: AbortSignal;\n imageDetail?: CodexImageDetail;\n },\n): Promise<CodexTurnResult> {\n if (ifInBrowser) {\n throw new Error(\n 'codex app-server provider is not supported in browser runtime',\n );\n }\n\n return codexConnectionManager.runTurn({\n messages,\n modelConfig,\n stream: options?.stream,\n onChunk: options?.onChunk,\n reasoningEnabled: options?.reasoningEnabled,\n abortSignal: options?.abortSignal,\n imageDetail: options?.imageDetail,\n });\n}\n\nexport async function __shutdownCodexAppServerForTests() {\n await codexConnectionManager.shutdownForTests();\n}\n"],"names":["CODEX_PROVIDER_SCHEME","CODEX_DEFAULT_TIMEOUT_MS","CODEX_DEFAULT_PROCESS_START_TIMEOUT_MS","CODEX_DEFAULT_CLEANUP_TIMEOUT_MS","CODEX_TEXT_INPUT_MAX_LENGTH","debugCodex","getDebug","warnCodex","SerializedRunner","work","previous","release","Promise","resolve","isCodexAppServerProvider","baseURL","isAbortError","error","Error","message","String","toNonEmptyString","value","trimmed","undefined","toCodexImageDetail","normalizeCodexLocalImagePath","imageUrl","platform","process","parsed","URL","pathname","decodeURIComponent","host","windowsPath","extractTextFromMessage","content","Array","part","Boolean","extractImageInputs","imageDetailOverride","inputs","partType","imageDetail","path","resolveCodexReasoningEffort","reasoningEnabled","modelConfig","normalized","buildCodexTurnPayloadFromMessages","messages","developerInstructionParts","transcriptParts","imageInputs","role","text","roleTag","fullTranscript","transcriptText","input","developerInstructions","CodexAppServerConnection","ifInBrowser","childProcessModuleName","readlineModuleName","spawn","readline","child","lineReader","Number","connection","stream","onChunk","abortSignal","startTime","Date","timeoutMs","deadlineAt","isStreaming","effort","threadId","turnId","latestErrorMessage","accumulatedText","accumulatedReasoning","latestUsage","emitChunk","reasoning","isComplete","usage","chunk","threadStartResponse","turnStartResponse","turnStatus","notification","method","params","messageText","delta","line","Buffer","code","tokenUsage","picked","requestId","request","result","includePending","JSON","setTimeout","payload","reject","stderr","CodexAppServerConnectionManager","staleConnection","codexConnectionManager","callAIWithCodexAppServer","options","__shutdownCodexAppServerForTests"],"mappings":"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"}
@@ -179,12 +179,26 @@ async function createChatClient({ modelConfig }) {
179
179
  async function callAI(messages, modelRuntime, options) {
180
180
  const { config: modelConfig, adapter } = modelRuntime;
181
181
  const internalCallId = nextInternalCallId();
182
+ const chatCompletionInput = {
183
+ intent: modelConfig.intent,
184
+ userConfig: {
185
+ temperature: modelConfig.temperature,
186
+ reasoningEnabled: modelConfig.reasoningEnabled,
187
+ reasoningEffort: modelConfig.reasoningEffort,
188
+ reasoningBudget: modelConfig.reasoningBudget,
189
+ responseFormat: modelConfig.responseFormat
190
+ },
191
+ requiresOriginalImageDetail: options?.requiresOriginalImageDetail,
192
+ expectedJsonObjectResponse: options?.expectedJsonObjectResponse
193
+ };
194
+ const imageDetail = adapter.chatCompletion.resolveImageDetail(chatCompletionInput);
182
195
  if (isCodexAppServerProvider(modelConfig.openaiBaseURL)) {
183
196
  const codexResult = await callAIWithCodexAppServer(messages, modelConfig, {
184
197
  stream: options?.stream,
185
198
  onChunk: options?.onChunk,
186
199
  reasoningEnabled: modelConfig.reasoningEnabled,
187
- abortSignal: options?.abortSignal
200
+ abortSignal: options?.abortSignal,
201
+ imageDetail
188
202
  });
189
203
  if (codexResult.usage) {
190
204
  codexResult.usage[INTERNAL_CALL_ID_FIELD] = internalCallId;
@@ -205,18 +219,6 @@ async function callAI(messages, modelRuntime, options) {
205
219
  const debugProfileDetail = getDebug('ai:profile:detail');
206
220
  const startTime = Date.now();
207
221
  const isStreaming = options?.stream && options?.onChunk;
208
- const chatCompletionInput = {
209
- intent: modelConfig.intent,
210
- userConfig: {
211
- temperature: modelConfig.temperature,
212
- reasoningEnabled: modelConfig.reasoningEnabled,
213
- reasoningEffort: modelConfig.reasoningEffort,
214
- reasoningBudget: modelConfig.reasoningBudget,
215
- responseFormat: modelConfig.responseFormat
216
- },
217
- requiresOriginalImageDetail: options?.requiresOriginalImageDetail,
218
- expectedJsonObjectResponse: options?.expectedJsonObjectResponse
219
- };
220
222
  const { config: adapterChatCompletionParams } = adapter.chatCompletion.buildChatCompletionParams(chatCompletionInput);
221
223
  debugCall(`adapter chat completion params: ${stringifyForDebug({
222
224
  config: adapterChatCompletionParams
@@ -262,7 +264,6 @@ async function callAI(messages, modelRuntime, options) {
262
264
  ...extraBody ?? {}
263
265
  };
264
266
  const temperature = requestConfig.temperature;
265
- const imageDetail = adapter.chatCompletion.resolveImageDetail(chatCompletionInput);
266
267
  const messagesWithImageDetail = (()=>{
267
268
  if (!imageDetail) return messages;
268
269
  return messages.map((msg)=>{
@@ -1 +1 @@
1
- {"version":3,"file":"ai-model/service-caller/index.mjs","sources":["../../../../src/ai-model/service-caller/index.ts"],"sourcesContent":["import type { AIUsageInfo } from '@/types';\nimport type { CodeGenerationChunk, StreamingCallback } from '@/types';\n\n// Error class that preserves usage and rawResponse when AI call parsing fails\nexport class AIResponseParseError extends Error {\n usage?: AIUsageInfo;\n /**\n * Adapter-extracted content used by Midscene for parsing. This is not the\n * full provider response or choices[0].message.\n */\n rawResponse: string;\n rawChoiceMessage?: unknown;\n reasoningContent?: string;\n\n constructor(\n message: string,\n rawResponse: string,\n usage?: AIUsageInfo,\n rawChoiceMessage?: unknown,\n reasoningContent?: string,\n ) {\n super(message);\n this.name = 'AIResponseParseError';\n this.rawResponse = rawResponse;\n this.usage = usage;\n this.rawChoiceMessage = rawChoiceMessage;\n this.reasoningContent = reasoningContent;\n }\n}\nimport {\n type IModelConfig,\n MIDSCENE_LANGFUSE_DEBUG,\n MIDSCENE_LANGSMITH_DEBUG,\n type TModelFamily,\n globalConfigManager,\n} from '@midscene/shared/env';\n\nimport { getDebug } from '@midscene/shared/logger';\nimport { assert, ifInBrowser } from '@midscene/shared/utils';\nimport OpenAI from 'openai';\nimport type { ChatCompletionMessageParam } from 'openai/resources/index';\nimport type { Stream } from 'openai/streaming';\nimport type { ModelRuntime } from '../models';\nimport type { AIArgs } from '../types';\nimport {\n callAIWithCodexAppServer,\n isCodexAppServerProvider,\n} from './codex-app-server';\nimport type { JsonParserSource } from './json';\nimport {\n type OpenAIErrorResponseContext,\n formatOpenAIAPIErrorDetails,\n wrapOpenAICompatibleFetch,\n} from './openai-error';\nimport {\n buildRequestAbortSignal,\n isHardTimeoutError,\n resolveEffectiveTimeoutMs,\n restoreHardTimeoutError,\n} from './request-timeout';\nimport { callAiAndParseWithRetry } from './semantic-retry';\nexport {\n extractJSONFromCodeBlock,\n parseModelResponseJson,\n} from './json';\nexport type { JsonParser } from './json';\n\n/**\n * Internal field name stamped onto every AIUsageInfo shaped by callAI().\n * Used for cross-path dedup when the provider does not return a request_id.\n */\nexport const INTERNAL_CALL_ID_FIELD = '_midscene_call_id';\n\nlet internalCallIdCounter = 0;\nfunction nextInternalCallId(): string {\n internalCallIdCounter += 1;\n return `call_${internalCallIdCounter}`;\n}\n\nfunction stringifyForDebug(value: unknown): string {\n try {\n return JSON.stringify(value);\n } catch (_error) {\n return String(value);\n }\n}\n\nfunction getLatestSuccessfulResponseRequestId(\n context: OpenAIErrorResponseContext,\n): string | undefined {\n return context.responseRequestIds?.reduce<string | undefined>(\n (latestRequestId, response) =>\n response.ok ? response.requestId : latestRequestId,\n undefined,\n );\n}\n\nfunction getErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction toError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error));\n}\n\nfunction normalizeRetryCount(retryCount: unknown): number {\n if (typeof retryCount !== 'number' || !Number.isFinite(retryCount)) {\n return 1;\n }\n\n return Math.max(0, Math.floor(retryCount));\n}\n\nfunction appendAIRequestFailureSummary<T extends Error>(\n error: T,\n attemptErrors: Array<{ attempt: number; error: unknown }>,\n maxAttempts: number,\n): T {\n const failedAttempts = attemptErrors.length;\n const retries = Math.max(0, failedAttempts - 1);\n const retryLabel = retries === 1 ? 'retry' : 'retries';\n const originalMessage = error.message;\n const previousAttemptErrors = attemptErrors.slice(0, -1);\n\n error.message = `AI model request failed after ${retries} ${retryLabel} (${failedAttempts}/${maxAttempts} attempts). Last error: ${originalMessage}`;\n\n if (previousAttemptErrors.length === 0) {\n return error;\n }\n\n const details = previousAttemptErrors\n .map(\n ({ attempt, error }) => `Attempt ${attempt}: ${getErrorMessage(error)}`,\n )\n .join('\\n');\n\n error.message = `${error.message}\\nPrevious AI call attempt errors:\\n${details}`;\n return error;\n}\n\nexport async function createChatClient({\n modelConfig,\n}: {\n modelConfig: IModelConfig;\n}): Promise<{\n completion: OpenAI.Chat.Completions;\n modelName: string;\n modelDescription: string;\n modelFamily: TModelFamily | undefined;\n openAIErrorResponseContext: OpenAIErrorResponseContext;\n}> {\n const {\n socksProxy,\n httpProxy,\n modelName,\n openaiBaseURL,\n openaiApiKey,\n openaiExtraConfig,\n modelDescription,\n modelFamily,\n createOpenAIClient,\n timeout,\n } = modelConfig;\n\n let proxyAgent: any = undefined;\n const warnClient = getDebug('ai:call', { console: true });\n const debugProxy = getDebug('ai:call:proxy');\n const warnProxy = getDebug('ai:call:proxy', { console: true });\n\n // Helper function to sanitize proxy URL for logging (remove credentials)\n // Uses URL API instead of regex to avoid ReDoS vulnerabilities\n const sanitizeProxyUrl = (url: string): string => {\n try {\n const parsed = new URL(url);\n if (parsed.username) {\n // Keep username for debugging, hide password for security\n parsed.password = '****';\n return parsed.href;\n }\n return url;\n } catch {\n // If URL parsing fails, return original URL (will be caught later)\n return url;\n }\n };\n\n if (httpProxy) {\n debugProxy('using http proxy', sanitizeProxyUrl(httpProxy));\n if (ifInBrowser) {\n warnProxy(\n 'HTTP proxy is configured but not supported in browser environment',\n );\n } else {\n // Dynamic import with variable to avoid bundler static analysis\n const moduleName = 'undici';\n const { ProxyAgent } = await import(moduleName);\n proxyAgent = new ProxyAgent({\n uri: httpProxy,\n // Note: authentication is handled via the URI (e.g., http://user:pass@proxy.com:8080)\n });\n }\n } else if (socksProxy) {\n debugProxy('using socks proxy', sanitizeProxyUrl(socksProxy));\n if (ifInBrowser) {\n warnProxy(\n 'SOCKS proxy is configured but not supported in browser environment',\n );\n } else {\n try {\n // Dynamic import with variable to avoid bundler static analysis\n const moduleName = 'fetch-socks';\n const { socksDispatcher } = await import(moduleName);\n // Parse SOCKS proxy URL (e.g., socks5://127.0.0.1:1080)\n const proxyUrl = new URL(socksProxy);\n\n // Validate hostname\n if (!proxyUrl.hostname) {\n throw new Error('SOCKS proxy URL must include a valid hostname');\n }\n\n // Validate and parse port\n const port = Number.parseInt(proxyUrl.port, 10);\n if (!proxyUrl.port || Number.isNaN(port)) {\n throw new Error('SOCKS proxy URL must include a valid port');\n }\n\n // Parse SOCKS version from protocol\n const protocol = proxyUrl.protocol.replace(':', '');\n const socksType =\n protocol === 'socks4' ? 4 : protocol === 'socks5' ? 5 : 5;\n\n proxyAgent = socksDispatcher({\n type: socksType,\n host: proxyUrl.hostname,\n port,\n ...(proxyUrl.username\n ? {\n userId: decodeURIComponent(proxyUrl.username),\n password: decodeURIComponent(proxyUrl.password || ''),\n }\n : {}),\n });\n debugProxy('socks proxy configured successfully', {\n type: socksType,\n host: proxyUrl.hostname,\n port: port,\n });\n } catch (error) {\n warnProxy('Failed to configure SOCKS proxy:', error);\n throw new Error(\n `Invalid SOCKS proxy URL: ${socksProxy}. Expected format: socks4://host:port, socks5://host:port, or with authentication: socks5://user:pass@host:port`,\n );\n }\n }\n }\n\n const effectiveTimeoutMs = resolveEffectiveTimeoutMs({ timeout });\n const openAIErrorResponseContext: OpenAIErrorResponseContext = {};\n const openAIOptions = {\n baseURL: openaiBaseURL,\n apiKey: openaiApiKey,\n // Use fetchOptions.dispatcher for fetch-based SDK instead of httpAgent\n // Note: Type assertion needed due to undici version mismatch between dependencies\n ...(proxyAgent ? { fetchOptions: { dispatcher: proxyAgent as any } } : {}),\n ...openaiExtraConfig,\n fetch: wrapOpenAICompatibleFetch(openAIErrorResponseContext),\n // Midscene already handles retries in callAI(), so disable SDK-level retries\n // to avoid duplicate attempts and duplicated backoff latency.\n maxRetries: 0,\n // When disabled (timeoutMs === null) fall through to the SDK default so\n // only the caller-provided abortSignal can cancel the request.\n ...(effectiveTimeoutMs !== null ? { timeout: effectiveTimeoutMs } : {}),\n dangerouslyAllowBrowser: true,\n };\n\n const baseOpenAI = new OpenAI(openAIOptions);\n\n let openai: OpenAI = baseOpenAI;\n\n // LangSmith wrapper\n if (\n openai &&\n globalConfigManager.getEnvConfigInBoolean(MIDSCENE_LANGSMITH_DEBUG)\n ) {\n if (ifInBrowser) {\n throw new Error('langsmith is not supported in browser');\n }\n warnClient('DEBUGGING MODE: langsmith wrapper enabled');\n // Use variable to prevent static analysis by bundlers\n const langsmithModule = 'langsmith/wrappers';\n const { wrapOpenAI } = await import(langsmithModule);\n openai = wrapOpenAI(openai);\n }\n\n // Langfuse wrapper\n if (\n openai &&\n globalConfigManager.getEnvConfigInBoolean(MIDSCENE_LANGFUSE_DEBUG)\n ) {\n if (ifInBrowser) {\n throw new Error('langfuse is not supported in browser');\n }\n warnClient('DEBUGGING MODE: langfuse wrapper enabled');\n // Use variable to prevent static analysis by bundlers\n const langfuseModule = '@langfuse/openai';\n const { observeOpenAI } = await import(langfuseModule);\n openai = observeOpenAI(openai);\n }\n\n if (createOpenAIClient) {\n const wrappedClient = await createOpenAIClient(baseOpenAI, openAIOptions);\n\n if (wrappedClient) {\n openai = wrappedClient as OpenAI;\n }\n }\n\n return {\n completion: openai.chat.completions,\n modelName,\n modelDescription,\n modelFamily,\n openAIErrorResponseContext,\n };\n}\n\ninterface CallAIOptions {\n stream?: boolean;\n onChunk?: StreamingCallback;\n abortSignal?: AbortSignal;\n requiresOriginalImageDetail?: boolean;\n expectedJsonObjectResponse?: boolean;\n}\n\nexport async function callAI(\n messages: ChatCompletionMessageParam[],\n modelRuntime: ModelRuntime,\n options?: CallAIOptions,\n): Promise<{\n content: string;\n reasoning_content?: string;\n rawChoiceMessage?: unknown;\n usage?: AIUsageInfo;\n isStreamed: boolean;\n}> {\n const { config: modelConfig, adapter } = modelRuntime;\n\n // Stable internal ID for this call, used by the agent to deduplicate usage\n // across the onUsage callback and the task-dump-based collectUsageMetrics()\n // path when the provider does not return a request_id.\n const internalCallId = nextInternalCallId();\n\n if (isCodexAppServerProvider(modelConfig.openaiBaseURL)) {\n const codexResult = await callAIWithCodexAppServer(messages, modelConfig, {\n stream: options?.stream,\n onChunk: options?.onChunk,\n reasoningEnabled: modelConfig.reasoningEnabled,\n abortSignal: options?.abortSignal,\n });\n if (codexResult.usage) {\n (codexResult.usage as any)[INTERNAL_CALL_ID_FIELD] = internalCallId;\n if (modelRuntime.onUsage) {\n modelRuntime.onUsage(codexResult.usage);\n }\n }\n return codexResult;\n }\n\n const {\n completion,\n modelName,\n modelDescription,\n modelFamily,\n openAIErrorResponseContext,\n } = await createChatClient({\n modelConfig,\n });\n const effectiveTimeoutMs = resolveEffectiveTimeoutMs(modelConfig);\n\n const extraBody = modelConfig.extraBody;\n\n const debugCall = getDebug('ai:call');\n const warnCall = getDebug('ai:call', { console: true });\n const debugProfileStats = getDebug('ai:profile:stats');\n const debugProfileDetail = getDebug('ai:profile:detail');\n\n const startTime = Date.now();\n\n const isStreaming = options?.stream && options?.onChunk;\n const chatCompletionInput = {\n intent: modelConfig.intent,\n userConfig: {\n temperature: modelConfig.temperature,\n reasoningEnabled: modelConfig.reasoningEnabled,\n reasoningEffort: modelConfig.reasoningEffort,\n reasoningBudget: modelConfig.reasoningBudget,\n responseFormat: modelConfig.responseFormat,\n },\n requiresOriginalImageDetail: options?.requiresOriginalImageDetail,\n expectedJsonObjectResponse: options?.expectedJsonObjectResponse,\n };\n const { config: adapterChatCompletionParams } =\n adapter.chatCompletion.buildChatCompletionParams(chatCompletionInput);\n debugCall(\n `adapter chat completion params: ${stringifyForDebug({\n config: adapterChatCompletionParams,\n })}`,\n );\n let content: string | undefined;\n let accumulated = '';\n let accumulatedReasoning = '';\n let rawChoiceMessage: unknown;\n let usage: OpenAI.CompletionUsage | undefined;\n let timeCost: number | undefined;\n let requestId: string | null | undefined;\n let responseModelName: string | undefined;\n // Tracks whether onUsage has already been fired for this call (e.g. from\n // the streaming final-chunk handler), so the final return does not double-fire.\n let usageReported = false;\n\n const hasUsableText = (value: string | null | undefined): value is string =>\n typeof value === 'string' && value.trim().length > 0;\n\n const resolveContentWithReasoningFallback = (\n contentValue: string | undefined,\n reasoningContent: string,\n ) => {\n if (\n !hasUsableText(contentValue) &&\n adapter.chatCompletion.useReasoningAsContentFallback &&\n hasUsableText(reasoningContent)\n ) {\n warnCall('empty content from AI model, using reasoning content');\n return reasoningContent;\n }\n\n return contentValue;\n };\n\n const buildUsageInfo = (\n usageData?: OpenAI.CompletionUsage,\n requestId?: string | null,\n ) => {\n if (!usageData) return undefined;\n\n const cachedInputTokens = (\n usageData as { prompt_tokens_details?: { cached_tokens?: number } }\n )?.prompt_tokens_details?.cached_tokens;\n\n return {\n ...usageData,\n prompt_tokens: usageData.prompt_tokens ?? 0,\n completion_tokens: usageData.completion_tokens ?? 0,\n total_tokens: usageData.total_tokens ?? 0,\n cached_input: cachedInputTokens ?? 0,\n time_cost: timeCost ?? 0,\n model_name: modelName,\n model_description: modelDescription,\n response_model_name: responseModelName,\n slot: modelConfig.slot,\n // Left undefined at the raw call layer. The agent's onUsage callback\n // fills it from modelConfig.slot for metrics collection, and task\n // layers use withUsageIntent() to stamp a more specific semantic\n // intent (e.g. 'planning', 'insight') when attaching usage to tasks.\n intent: undefined,\n request_id: requestId ?? undefined,\n // Internal stable ID for cross-path dedup when request_id is absent.\n [INTERNAL_CALL_ID_FIELD]: internalCallId,\n } satisfies AIUsageInfo;\n };\n\n const requestConfig = {\n ...adapterChatCompletionParams,\n ...(extraBody ?? {}),\n };\n const temperature = requestConfig.temperature;\n\n const imageDetail =\n adapter.chatCompletion.resolveImageDetail(chatCompletionInput);\n\n // Some adapters request original image detail to preserve screenshot\n // resolution for localization-sensitive tasks.\n const messagesWithImageDetail: ChatCompletionMessageParam[] = (() => {\n if (!imageDetail) {\n return messages;\n }\n\n return messages.map((msg) => {\n if (!Array.isArray(msg.content)) {\n return msg;\n }\n\n const content = msg.content.map((part) => {\n if (part && part.type === 'image_url' && part.image_url?.url) {\n return {\n ...part,\n image_url: {\n ...part.image_url,\n detail: imageDetail,\n },\n };\n }\n return part;\n });\n\n return {\n ...msg,\n content,\n } as ChatCompletionMessageParam;\n });\n })();\n\n try {\n debugCall(\n `sending ${isStreaming ? 'streaming ' : ''}request to ${modelName}`,\n );\n\n if (isStreaming) {\n const { signal: streamSignal, cleanup: cleanupStreamSignal } =\n buildRequestAbortSignal(effectiveTimeoutMs, options?.abortSignal);\n try {\n const stream = (await completion.create(\n {\n model: modelName,\n messages: messagesWithImageDetail,\n ...requestConfig,\n stream: true,\n },\n {\n stream: true,\n signal: streamSignal,\n },\n )) as Stream<OpenAI.Chat.Completions.ChatCompletionChunk> & {\n _request_id?: string | null;\n };\n\n requestId =\n getLatestSuccessfulResponseRequestId(openAIErrorResponseContext) ??\n stream._request_id;\n\n for await (const chunk of stream) {\n const parsedChunk = adapter.chatCompletion.extractContentAndReasoning(\n chunk.choices?.[0]?.delta,\n );\n const content = parsedChunk.content || '';\n const reasoning_content = parsedChunk.reasoning_content || '';\n\n // Check for usage info in any chunk (OpenAI provides usage in separate chunks)\n if (chunk.usage) {\n usage = chunk.usage;\n }\n if (chunk.model) {\n responseModelName = chunk.model;\n }\n\n if (content || reasoning_content) {\n accumulated += content;\n accumulatedReasoning += reasoning_content;\n const chunkData: CodeGenerationChunk = {\n content,\n reasoning_content,\n accumulated,\n isComplete: false,\n usage: undefined,\n };\n options.onChunk!(chunkData);\n }\n\n // Check if stream is complete\n if (chunk.choices?.[0]?.finish_reason) {\n timeCost = Date.now() - startTime;\n\n // If usage is not available from the stream, provide a basic usage info\n if (!usage) {\n // Estimate token counts based on content length (rough approximation)\n const estimatedTokens = Math.max(\n 1,\n Math.floor(accumulated.length / 4),\n );\n usage = {\n prompt_tokens: estimatedTokens,\n completion_tokens: estimatedTokens,\n total_tokens: estimatedTokens * 2,\n };\n }\n\n const finalAccumulated = resolveContentWithReasoningFallback(\n accumulated,\n accumulatedReasoning,\n );\n accumulated = finalAccumulated || '';\n\n // Send final chunk\n const finalUsage = buildUsageInfo(usage, requestId);\n if (finalUsage && modelRuntime.onUsage) {\n modelRuntime.onUsage(finalUsage);\n usageReported = true;\n }\n const finalChunk: CodeGenerationChunk = {\n content: '',\n accumulated,\n reasoning_content: '',\n isComplete: true,\n usage: finalUsage,\n };\n options.onChunk!(finalChunk);\n break;\n }\n }\n } catch (error) {\n throw restoreHardTimeoutError(toError(error), streamSignal);\n } finally {\n cleanupStreamSignal();\n }\n content = accumulated;\n debugProfileStats(\n `streaming model, ${modelName}, mode, ${modelFamily || 'default'}, cost-ms, ${timeCost}, temperature, ${temperature ?? ''}`,\n );\n } else {\n // Non-streaming with retry logic\n const retryCount = normalizeRetryCount(modelConfig.retryCount);\n const retryInterval = modelConfig.retryInterval ?? 2000;\n const maxAttempts = retryCount + 1; // retryCount=1 means 2 total attempts (1 initial + 1 retry)\n\n let lastError: Error | undefined;\n const attemptErrors: Array<{ attempt: number; error: unknown }> = [];\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n const { signal: attemptSignal, cleanup: cleanupAttemptSignal } =\n buildRequestAbortSignal(effectiveTimeoutMs, options?.abortSignal);\n try {\n const result = await completion.create(\n {\n model: modelName,\n messages: messagesWithImageDetail,\n ...requestConfig,\n stream: false,\n } as any,\n { signal: attemptSignal },\n );\n\n timeCost = Date.now() - startTime;\n requestId =\n getLatestSuccessfulResponseRequestId(openAIErrorResponseContext) ??\n result._request_id;\n\n debugProfileStats(\n `model, ${modelName}, mode, ${modelFamily || 'default'}, prompt-tokens, ${result.usage?.prompt_tokens || ''}, completion-tokens, ${result.usage?.completion_tokens || ''}, total-tokens, ${result.usage?.total_tokens || ''}, cost-ms, ${timeCost}, requestId, ${requestId || ''}, temperature, ${temperature ?? ''}`,\n );\n\n debugProfileDetail(\n `model usage detail: ${JSON.stringify(result.usage)}`,\n );\n\n if (!result.choices) {\n throw new Error(\n `invalid response from LLM service: ${JSON.stringify(result)}`,\n );\n }\n\n rawChoiceMessage = result.choices[0].message;\n const parsedMessage =\n adapter.chatCompletion.extractContentAndReasoning(\n result.choices[0].message,\n );\n content = parsedMessage.content;\n accumulatedReasoning = parsedMessage.reasoning_content;\n usage = result.usage;\n responseModelName = result.model;\n\n content = resolveContentWithReasoningFallback(\n content,\n accumulatedReasoning,\n );\n\n if (!hasUsableText(content)) {\n const errorUsage = buildUsageInfo(usage, requestId);\n if (errorUsage && modelRuntime.onUsage) {\n modelRuntime.onUsage(errorUsage);\n }\n throw new AIResponseParseError(\n 'empty content from AI model',\n content || '',\n errorUsage,\n rawChoiceMessage,\n );\n }\n\n break; // Success, exit retry loop\n } catch (error) {\n lastError = restoreHardTimeoutError(toError(error), attemptSignal);\n attemptErrors.push({ attempt, error: lastError });\n const wasHardTimeout = isHardTimeoutError(lastError);\n if (wasHardTimeout) {\n warnCall(\n `AI call hit hard timeout (${effectiveTimeoutMs}ms, attempt ${attempt}/${maxAttempts}, model ${modelName}, slot ${modelConfig.slot})`,\n );\n }\n // Do not retry if the request was aborted by the caller\n if (options?.abortSignal?.aborted) {\n break;\n }\n if (attempt < maxAttempts) {\n warnCall(\n `AI call failed (attempt ${attempt}/${maxAttempts}), retrying in ${retryInterval}ms... Error: ${lastError.message}`,\n );\n await new Promise((resolve) => setTimeout(resolve, retryInterval));\n }\n } finally {\n cleanupAttemptSignal();\n }\n }\n\n if (!content) {\n assert(\n lastError,\n 'AI model request failed without recording an attempt error',\n );\n throw appendAIRequestFailureSummary(\n lastError,\n attemptErrors,\n maxAttempts,\n );\n }\n }\n\n debugCall(`response reasoning content: ${accumulatedReasoning}`);\n debugCall(`response content: ${content}`);\n\n // Ensure we always have usage info for streaming responses\n if (isStreaming && !usage) {\n // Estimate token counts based on content length (rough approximation)\n const estimatedTokens = Math.max(\n 1,\n Math.floor((content || '').length / 4),\n );\n usage = {\n prompt_tokens: estimatedTokens,\n completion_tokens: estimatedTokens,\n total_tokens: estimatedTokens * 2,\n } as OpenAI.CompletionUsage;\n }\n\n const finalUsage = buildUsageInfo(usage, requestId);\n // Report usage to the runtime-level collector if not already reported\n // (e.g. from the streaming final-chunk handler).\n if (!usageReported && finalUsage && modelRuntime.onUsage) {\n modelRuntime.onUsage(finalUsage);\n }\n\n return {\n content: content || '',\n reasoning_content: accumulatedReasoning || undefined,\n rawChoiceMessage,\n usage: finalUsage,\n isStreamed: !!isStreaming,\n };\n } catch (e: any) {\n warnCall('call AI error', e);\n\n if (e instanceof AIResponseParseError) {\n throw e;\n }\n\n const newError = new Error(\n `failed to call ${isStreaming ? 'streaming ' : ''}AI model service (${modelName}): ${e.message}${formatOpenAIAPIErrorDetails(e, openAIErrorResponseContext)}\\nTrouble shooting: https://midscenejs.com/model-provider.html`,\n {\n cause: e,\n },\n );\n throw newError;\n }\n}\n\nexport async function callAIWithObjectResponse<T>(\n messages: ChatCompletionMessageParam[],\n modelRuntime: ModelRuntime,\n options?: {\n abortSignal?: AbortSignal;\n jsonParserSource?: JsonParserSource;\n retryTimes?: number;\n retryInterval?: number;\n },\n): Promise<{\n // TODO: `content` is a misleading name here because this is already the parsed object response. Consider renaming it to `object` or `data`.\n content: T;\n contentString: string;\n usage?: AIUsageInfo;\n reasoning_content?: string;\n rawChoiceMessage?: unknown;\n}> {\n const { config: modelConfig, adapter } = modelRuntime;\n return callAiAndParseWithRetry({\n callAi: () =>\n callAI(messages, modelRuntime, {\n abortSignal: options?.abortSignal,\n expectedJsonObjectResponse: true,\n }),\n parseResponse: (response) => {\n assert(response, 'empty response');\n const jsonContent = adapter.jsonParser(response.content, {\n source: options?.jsonParserSource ?? 'generic-object',\n });\n // This API expects a JSON object. Bare JSON primitives are valid JSON,\n // but do not satisfy object-response callers.\n if (!jsonContent || typeof jsonContent !== 'object') {\n throw new Error(\n `failed to parse json response from model (${modelConfig.modelName}): ${response.content}`,\n );\n }\n return {\n content: jsonContent as T,\n contentString: response.content,\n usage: response.usage,\n reasoning_content: response.reasoning_content,\n rawChoiceMessage: response.rawChoiceMessage,\n };\n },\n toParseError: (error, response) => {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n return new AIResponseParseError(\n errorMessage,\n response.content,\n response.usage,\n response.rawChoiceMessage,\n response.reasoning_content,\n );\n },\n parseRetryTimes: options?.retryTimes ?? modelConfig.retryCount,\n parseRetryInterval: options?.retryInterval ?? modelConfig.retryInterval,\n abortSignal: options?.abortSignal,\n });\n}\n\nexport async function callAIWithStringResponse(\n msgs: AIArgs,\n modelRuntime: ModelRuntime,\n options?: Pick<CallAIOptions, 'abortSignal' | 'requiresOriginalImageDetail'>,\n): Promise<{\n content: string;\n usage?: AIUsageInfo;\n rawChoiceMessage?: unknown;\n}> {\n const { content, usage, rawChoiceMessage } = await callAI(\n msgs,\n modelRuntime,\n options,\n );\n return { content, usage, rawChoiceMessage };\n}\n"],"names":["AIResponseParseError","Error","message","rawResponse","usage","rawChoiceMessage","reasoningContent","INTERNAL_CALL_ID_FIELD","internalCallIdCounter","nextInternalCallId","stringifyForDebug","value","JSON","_error","String","getLatestSuccessfulResponseRequestId","context","latestRequestId","response","undefined","getErrorMessage","error","toError","normalizeRetryCount","retryCount","Number","Math","appendAIRequestFailureSummary","attemptErrors","maxAttempts","failedAttempts","retries","retryLabel","originalMessage","previousAttemptErrors","details","attempt","createChatClient","modelConfig","socksProxy","httpProxy","modelName","openaiBaseURL","openaiApiKey","openaiExtraConfig","modelDescription","modelFamily","createOpenAIClient","timeout","proxyAgent","warnClient","getDebug","debugProxy","warnProxy","sanitizeProxyUrl","url","parsed","URL","ifInBrowser","moduleName","ProxyAgent","socksDispatcher","proxyUrl","port","protocol","socksType","decodeURIComponent","effectiveTimeoutMs","resolveEffectiveTimeoutMs","openAIErrorResponseContext","openAIOptions","wrapOpenAICompatibleFetch","baseOpenAI","OpenAI","openai","globalConfigManager","MIDSCENE_LANGSMITH_DEBUG","langsmithModule","wrapOpenAI","MIDSCENE_LANGFUSE_DEBUG","langfuseModule","observeOpenAI","wrappedClient","callAI","messages","modelRuntime","options","adapter","internalCallId","isCodexAppServerProvider","codexResult","callAIWithCodexAppServer","completion","extraBody","debugCall","warnCall","debugProfileStats","debugProfileDetail","startTime","Date","isStreaming","chatCompletionInput","adapterChatCompletionParams","content","accumulated","accumulatedReasoning","timeCost","requestId","responseModelName","usageReported","hasUsableText","resolveContentWithReasoningFallback","contentValue","buildUsageInfo","usageData","cachedInputTokens","requestConfig","temperature","imageDetail","messagesWithImageDetail","msg","Array","part","streamSignal","cleanupStreamSignal","buildRequestAbortSignal","stream","chunk","parsedChunk","reasoning_content","chunkData","estimatedTokens","finalAccumulated","finalUsage","finalChunk","restoreHardTimeoutError","retryInterval","lastError","attemptSignal","cleanupAttemptSignal","result","parsedMessage","errorUsage","wasHardTimeout","isHardTimeoutError","Promise","resolve","setTimeout","assert","e","newError","formatOpenAIAPIErrorDetails","callAIWithObjectResponse","callAiAndParseWithRetry","jsonContent","errorMessage","callAIWithStringResponse","msgs"],"mappings":"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"}
1
+ {"version":3,"file":"ai-model/service-caller/index.mjs","sources":["../../../../src/ai-model/service-caller/index.ts"],"sourcesContent":["import type { AIUsageInfo } from '@/types';\nimport type { CodeGenerationChunk, StreamingCallback } from '@/types';\n\n// Error class that preserves usage and rawResponse when AI call parsing fails\nexport class AIResponseParseError extends Error {\n usage?: AIUsageInfo;\n /**\n * Adapter-extracted content used by Midscene for parsing. This is not the\n * full provider response or choices[0].message.\n */\n rawResponse: string;\n rawChoiceMessage?: unknown;\n reasoningContent?: string;\n\n constructor(\n message: string,\n rawResponse: string,\n usage?: AIUsageInfo,\n rawChoiceMessage?: unknown,\n reasoningContent?: string,\n ) {\n super(message);\n this.name = 'AIResponseParseError';\n this.rawResponse = rawResponse;\n this.usage = usage;\n this.rawChoiceMessage = rawChoiceMessage;\n this.reasoningContent = reasoningContent;\n }\n}\nimport {\n type IModelConfig,\n MIDSCENE_LANGFUSE_DEBUG,\n MIDSCENE_LANGSMITH_DEBUG,\n type TModelFamily,\n globalConfigManager,\n} from '@midscene/shared/env';\n\nimport { getDebug } from '@midscene/shared/logger';\nimport { assert, ifInBrowser } from '@midscene/shared/utils';\nimport OpenAI from 'openai';\nimport type { ChatCompletionMessageParam } from 'openai/resources/index';\nimport type { Stream } from 'openai/streaming';\nimport type { ModelRuntime } from '../models';\nimport type { AIArgs } from '../types';\nimport {\n callAIWithCodexAppServer,\n isCodexAppServerProvider,\n} from './codex-app-server';\nimport type { JsonParserSource } from './json';\nimport {\n type OpenAIErrorResponseContext,\n formatOpenAIAPIErrorDetails,\n wrapOpenAICompatibleFetch,\n} from './openai-error';\nimport {\n buildRequestAbortSignal,\n isHardTimeoutError,\n resolveEffectiveTimeoutMs,\n restoreHardTimeoutError,\n} from './request-timeout';\nimport { callAiAndParseWithRetry } from './semantic-retry';\nexport {\n extractJSONFromCodeBlock,\n parseModelResponseJson,\n} from './json';\nexport type { JsonParser } from './json';\n\n/**\n * Internal field name stamped onto every AIUsageInfo shaped by callAI().\n * Used for cross-path dedup when the provider does not return a request_id.\n */\nexport const INTERNAL_CALL_ID_FIELD = '_midscene_call_id';\n\nlet internalCallIdCounter = 0;\nfunction nextInternalCallId(): string {\n internalCallIdCounter += 1;\n return `call_${internalCallIdCounter}`;\n}\n\nfunction stringifyForDebug(value: unknown): string {\n try {\n return JSON.stringify(value);\n } catch (_error) {\n return String(value);\n }\n}\n\nfunction getLatestSuccessfulResponseRequestId(\n context: OpenAIErrorResponseContext,\n): string | undefined {\n return context.responseRequestIds?.reduce<string | undefined>(\n (latestRequestId, response) =>\n response.ok ? response.requestId : latestRequestId,\n undefined,\n );\n}\n\nfunction getErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction toError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error));\n}\n\nfunction normalizeRetryCount(retryCount: unknown): number {\n if (typeof retryCount !== 'number' || !Number.isFinite(retryCount)) {\n return 1;\n }\n\n return Math.max(0, Math.floor(retryCount));\n}\n\nfunction appendAIRequestFailureSummary<T extends Error>(\n error: T,\n attemptErrors: Array<{ attempt: number; error: unknown }>,\n maxAttempts: number,\n): T {\n const failedAttempts = attemptErrors.length;\n const retries = Math.max(0, failedAttempts - 1);\n const retryLabel = retries === 1 ? 'retry' : 'retries';\n const originalMessage = error.message;\n const previousAttemptErrors = attemptErrors.slice(0, -1);\n\n error.message = `AI model request failed after ${retries} ${retryLabel} (${failedAttempts}/${maxAttempts} attempts). Last error: ${originalMessage}`;\n\n if (previousAttemptErrors.length === 0) {\n return error;\n }\n\n const details = previousAttemptErrors\n .map(\n ({ attempt, error }) => `Attempt ${attempt}: ${getErrorMessage(error)}`,\n )\n .join('\\n');\n\n error.message = `${error.message}\\nPrevious AI call attempt errors:\\n${details}`;\n return error;\n}\n\nexport async function createChatClient({\n modelConfig,\n}: {\n modelConfig: IModelConfig;\n}): Promise<{\n completion: OpenAI.Chat.Completions;\n modelName: string;\n modelDescription: string;\n modelFamily: TModelFamily | undefined;\n openAIErrorResponseContext: OpenAIErrorResponseContext;\n}> {\n const {\n socksProxy,\n httpProxy,\n modelName,\n openaiBaseURL,\n openaiApiKey,\n openaiExtraConfig,\n modelDescription,\n modelFamily,\n createOpenAIClient,\n timeout,\n } = modelConfig;\n\n let proxyAgent: any = undefined;\n const warnClient = getDebug('ai:call', { console: true });\n const debugProxy = getDebug('ai:call:proxy');\n const warnProxy = getDebug('ai:call:proxy', { console: true });\n\n // Helper function to sanitize proxy URL for logging (remove credentials)\n // Uses URL API instead of regex to avoid ReDoS vulnerabilities\n const sanitizeProxyUrl = (url: string): string => {\n try {\n const parsed = new URL(url);\n if (parsed.username) {\n // Keep username for debugging, hide password for security\n parsed.password = '****';\n return parsed.href;\n }\n return url;\n } catch {\n // If URL parsing fails, return original URL (will be caught later)\n return url;\n }\n };\n\n if (httpProxy) {\n debugProxy('using http proxy', sanitizeProxyUrl(httpProxy));\n if (ifInBrowser) {\n warnProxy(\n 'HTTP proxy is configured but not supported in browser environment',\n );\n } else {\n // Dynamic import with variable to avoid bundler static analysis\n const moduleName = 'undici';\n const { ProxyAgent } = await import(moduleName);\n proxyAgent = new ProxyAgent({\n uri: httpProxy,\n // Note: authentication is handled via the URI (e.g., http://user:pass@proxy.com:8080)\n });\n }\n } else if (socksProxy) {\n debugProxy('using socks proxy', sanitizeProxyUrl(socksProxy));\n if (ifInBrowser) {\n warnProxy(\n 'SOCKS proxy is configured but not supported in browser environment',\n );\n } else {\n try {\n // Dynamic import with variable to avoid bundler static analysis\n const moduleName = 'fetch-socks';\n const { socksDispatcher } = await import(moduleName);\n // Parse SOCKS proxy URL (e.g., socks5://127.0.0.1:1080)\n const proxyUrl = new URL(socksProxy);\n\n // Validate hostname\n if (!proxyUrl.hostname) {\n throw new Error('SOCKS proxy URL must include a valid hostname');\n }\n\n // Validate and parse port\n const port = Number.parseInt(proxyUrl.port, 10);\n if (!proxyUrl.port || Number.isNaN(port)) {\n throw new Error('SOCKS proxy URL must include a valid port');\n }\n\n // Parse SOCKS version from protocol\n const protocol = proxyUrl.protocol.replace(':', '');\n const socksType =\n protocol === 'socks4' ? 4 : protocol === 'socks5' ? 5 : 5;\n\n proxyAgent = socksDispatcher({\n type: socksType,\n host: proxyUrl.hostname,\n port,\n ...(proxyUrl.username\n ? {\n userId: decodeURIComponent(proxyUrl.username),\n password: decodeURIComponent(proxyUrl.password || ''),\n }\n : {}),\n });\n debugProxy('socks proxy configured successfully', {\n type: socksType,\n host: proxyUrl.hostname,\n port: port,\n });\n } catch (error) {\n warnProxy('Failed to configure SOCKS proxy:', error);\n throw new Error(\n `Invalid SOCKS proxy URL: ${socksProxy}. Expected format: socks4://host:port, socks5://host:port, or with authentication: socks5://user:pass@host:port`,\n );\n }\n }\n }\n\n const effectiveTimeoutMs = resolveEffectiveTimeoutMs({ timeout });\n const openAIErrorResponseContext: OpenAIErrorResponseContext = {};\n const openAIOptions = {\n baseURL: openaiBaseURL,\n apiKey: openaiApiKey,\n // Use fetchOptions.dispatcher for fetch-based SDK instead of httpAgent\n // Note: Type assertion needed due to undici version mismatch between dependencies\n ...(proxyAgent ? { fetchOptions: { dispatcher: proxyAgent as any } } : {}),\n ...openaiExtraConfig,\n fetch: wrapOpenAICompatibleFetch(openAIErrorResponseContext),\n // Midscene already handles retries in callAI(), so disable SDK-level retries\n // to avoid duplicate attempts and duplicated backoff latency.\n maxRetries: 0,\n // When disabled (timeoutMs === null) fall through to the SDK default so\n // only the caller-provided abortSignal can cancel the request.\n ...(effectiveTimeoutMs !== null ? { timeout: effectiveTimeoutMs } : {}),\n dangerouslyAllowBrowser: true,\n };\n\n const baseOpenAI = new OpenAI(openAIOptions);\n\n let openai: OpenAI = baseOpenAI;\n\n // LangSmith wrapper\n if (\n openai &&\n globalConfigManager.getEnvConfigInBoolean(MIDSCENE_LANGSMITH_DEBUG)\n ) {\n if (ifInBrowser) {\n throw new Error('langsmith is not supported in browser');\n }\n warnClient('DEBUGGING MODE: langsmith wrapper enabled');\n // Use variable to prevent static analysis by bundlers\n const langsmithModule = 'langsmith/wrappers';\n const { wrapOpenAI } = await import(langsmithModule);\n openai = wrapOpenAI(openai);\n }\n\n // Langfuse wrapper\n if (\n openai &&\n globalConfigManager.getEnvConfigInBoolean(MIDSCENE_LANGFUSE_DEBUG)\n ) {\n if (ifInBrowser) {\n throw new Error('langfuse is not supported in browser');\n }\n warnClient('DEBUGGING MODE: langfuse wrapper enabled');\n // Use variable to prevent static analysis by bundlers\n const langfuseModule = '@langfuse/openai';\n const { observeOpenAI } = await import(langfuseModule);\n openai = observeOpenAI(openai);\n }\n\n if (createOpenAIClient) {\n const wrappedClient = await createOpenAIClient(baseOpenAI, openAIOptions);\n\n if (wrappedClient) {\n openai = wrappedClient as OpenAI;\n }\n }\n\n return {\n completion: openai.chat.completions,\n modelName,\n modelDescription,\n modelFamily,\n openAIErrorResponseContext,\n };\n}\n\ninterface CallAIOptions {\n stream?: boolean;\n onChunk?: StreamingCallback;\n abortSignal?: AbortSignal;\n requiresOriginalImageDetail?: boolean;\n expectedJsonObjectResponse?: boolean;\n}\n\nexport async function callAI(\n messages: ChatCompletionMessageParam[],\n modelRuntime: ModelRuntime,\n options?: CallAIOptions,\n): Promise<{\n content: string;\n reasoning_content?: string;\n rawChoiceMessage?: unknown;\n usage?: AIUsageInfo;\n isStreamed: boolean;\n}> {\n const { config: modelConfig, adapter } = modelRuntime;\n\n // Stable internal ID for this call, used by the agent to deduplicate usage\n // across the onUsage callback and the task-dump-based collectUsageMetrics()\n // path when the provider does not return a request_id.\n const internalCallId = nextInternalCallId();\n const chatCompletionInput = {\n intent: modelConfig.intent,\n userConfig: {\n temperature: modelConfig.temperature,\n reasoningEnabled: modelConfig.reasoningEnabled,\n reasoningEffort: modelConfig.reasoningEffort,\n reasoningBudget: modelConfig.reasoningBudget,\n responseFormat: modelConfig.responseFormat,\n },\n requiresOriginalImageDetail: options?.requiresOriginalImageDetail,\n expectedJsonObjectResponse: options?.expectedJsonObjectResponse,\n };\n const imageDetail =\n adapter.chatCompletion.resolveImageDetail(chatCompletionInput);\n\n if (isCodexAppServerProvider(modelConfig.openaiBaseURL)) {\n const codexResult = await callAIWithCodexAppServer(messages, modelConfig, {\n stream: options?.stream,\n onChunk: options?.onChunk,\n reasoningEnabled: modelConfig.reasoningEnabled,\n abortSignal: options?.abortSignal,\n imageDetail,\n });\n if (codexResult.usage) {\n (codexResult.usage as any)[INTERNAL_CALL_ID_FIELD] = internalCallId;\n if (modelRuntime.onUsage) {\n modelRuntime.onUsage(codexResult.usage);\n }\n }\n return codexResult;\n }\n\n const {\n completion,\n modelName,\n modelDescription,\n modelFamily,\n openAIErrorResponseContext,\n } = await createChatClient({\n modelConfig,\n });\n const effectiveTimeoutMs = resolveEffectiveTimeoutMs(modelConfig);\n\n const extraBody = modelConfig.extraBody;\n\n const debugCall = getDebug('ai:call');\n const warnCall = getDebug('ai:call', { console: true });\n const debugProfileStats = getDebug('ai:profile:stats');\n const debugProfileDetail = getDebug('ai:profile:detail');\n\n const startTime = Date.now();\n\n const isStreaming = options?.stream && options?.onChunk;\n const { config: adapterChatCompletionParams } =\n adapter.chatCompletion.buildChatCompletionParams(chatCompletionInput);\n debugCall(\n `adapter chat completion params: ${stringifyForDebug({\n config: adapterChatCompletionParams,\n })}`,\n );\n let content: string | undefined;\n let accumulated = '';\n let accumulatedReasoning = '';\n let rawChoiceMessage: unknown;\n let usage: OpenAI.CompletionUsage | undefined;\n let timeCost: number | undefined;\n let requestId: string | null | undefined;\n let responseModelName: string | undefined;\n // Tracks whether onUsage has already been fired for this call (e.g. from\n // the streaming final-chunk handler), so the final return does not double-fire.\n let usageReported = false;\n\n const hasUsableText = (value: string | null | undefined): value is string =>\n typeof value === 'string' && value.trim().length > 0;\n\n const resolveContentWithReasoningFallback = (\n contentValue: string | undefined,\n reasoningContent: string,\n ) => {\n if (\n !hasUsableText(contentValue) &&\n adapter.chatCompletion.useReasoningAsContentFallback &&\n hasUsableText(reasoningContent)\n ) {\n warnCall('empty content from AI model, using reasoning content');\n return reasoningContent;\n }\n\n return contentValue;\n };\n\n const buildUsageInfo = (\n usageData?: OpenAI.CompletionUsage,\n requestId?: string | null,\n ) => {\n if (!usageData) return undefined;\n\n const cachedInputTokens = (\n usageData as { prompt_tokens_details?: { cached_tokens?: number } }\n )?.prompt_tokens_details?.cached_tokens;\n\n return {\n ...usageData,\n prompt_tokens: usageData.prompt_tokens ?? 0,\n completion_tokens: usageData.completion_tokens ?? 0,\n total_tokens: usageData.total_tokens ?? 0,\n cached_input: cachedInputTokens ?? 0,\n time_cost: timeCost ?? 0,\n model_name: modelName,\n model_description: modelDescription,\n response_model_name: responseModelName,\n slot: modelConfig.slot,\n // Left undefined at the raw call layer. The agent's onUsage callback\n // fills it from modelConfig.slot for metrics collection, and task\n // layers use withUsageIntent() to stamp a more specific semantic\n // intent (e.g. 'planning', 'insight') when attaching usage to tasks.\n intent: undefined,\n request_id: requestId ?? undefined,\n // Internal stable ID for cross-path dedup when request_id is absent.\n [INTERNAL_CALL_ID_FIELD]: internalCallId,\n } satisfies AIUsageInfo;\n };\n\n const requestConfig = {\n ...adapterChatCompletionParams,\n ...(extraBody ?? {}),\n };\n const temperature = requestConfig.temperature;\n\n // Some adapters request original image detail to preserve screenshot\n // resolution for localization-sensitive tasks.\n const messagesWithImageDetail: ChatCompletionMessageParam[] = (() => {\n if (!imageDetail) {\n return messages;\n }\n\n return messages.map((msg) => {\n if (!Array.isArray(msg.content)) {\n return msg;\n }\n\n const content = msg.content.map((part) => {\n if (part && part.type === 'image_url' && part.image_url?.url) {\n return {\n ...part,\n image_url: {\n ...part.image_url,\n detail: imageDetail,\n },\n };\n }\n return part;\n });\n\n return {\n ...msg,\n content,\n } as ChatCompletionMessageParam;\n });\n })();\n\n try {\n debugCall(\n `sending ${isStreaming ? 'streaming ' : ''}request to ${modelName}`,\n );\n\n if (isStreaming) {\n const { signal: streamSignal, cleanup: cleanupStreamSignal } =\n buildRequestAbortSignal(effectiveTimeoutMs, options?.abortSignal);\n try {\n const stream = (await completion.create(\n {\n model: modelName,\n messages: messagesWithImageDetail,\n ...requestConfig,\n stream: true,\n },\n {\n stream: true,\n signal: streamSignal,\n },\n )) as Stream<OpenAI.Chat.Completions.ChatCompletionChunk> & {\n _request_id?: string | null;\n };\n\n requestId =\n getLatestSuccessfulResponseRequestId(openAIErrorResponseContext) ??\n stream._request_id;\n\n for await (const chunk of stream) {\n const parsedChunk = adapter.chatCompletion.extractContentAndReasoning(\n chunk.choices?.[0]?.delta,\n );\n const content = parsedChunk.content || '';\n const reasoning_content = parsedChunk.reasoning_content || '';\n\n // Check for usage info in any chunk (OpenAI provides usage in separate chunks)\n if (chunk.usage) {\n usage = chunk.usage;\n }\n if (chunk.model) {\n responseModelName = chunk.model;\n }\n\n if (content || reasoning_content) {\n accumulated += content;\n accumulatedReasoning += reasoning_content;\n const chunkData: CodeGenerationChunk = {\n content,\n reasoning_content,\n accumulated,\n isComplete: false,\n usage: undefined,\n };\n options.onChunk!(chunkData);\n }\n\n // Check if stream is complete\n if (chunk.choices?.[0]?.finish_reason) {\n timeCost = Date.now() - startTime;\n\n // If usage is not available from the stream, provide a basic usage info\n if (!usage) {\n // Estimate token counts based on content length (rough approximation)\n const estimatedTokens = Math.max(\n 1,\n Math.floor(accumulated.length / 4),\n );\n usage = {\n prompt_tokens: estimatedTokens,\n completion_tokens: estimatedTokens,\n total_tokens: estimatedTokens * 2,\n };\n }\n\n const finalAccumulated = resolveContentWithReasoningFallback(\n accumulated,\n accumulatedReasoning,\n );\n accumulated = finalAccumulated || '';\n\n // Send final chunk\n const finalUsage = buildUsageInfo(usage, requestId);\n if (finalUsage && modelRuntime.onUsage) {\n modelRuntime.onUsage(finalUsage);\n usageReported = true;\n }\n const finalChunk: CodeGenerationChunk = {\n content: '',\n accumulated,\n reasoning_content: '',\n isComplete: true,\n usage: finalUsage,\n };\n options.onChunk!(finalChunk);\n break;\n }\n }\n } catch (error) {\n throw restoreHardTimeoutError(toError(error), streamSignal);\n } finally {\n cleanupStreamSignal();\n }\n content = accumulated;\n debugProfileStats(\n `streaming model, ${modelName}, mode, ${modelFamily || 'default'}, cost-ms, ${timeCost}, temperature, ${temperature ?? ''}`,\n );\n } else {\n // Non-streaming with retry logic\n const retryCount = normalizeRetryCount(modelConfig.retryCount);\n const retryInterval = modelConfig.retryInterval ?? 2000;\n const maxAttempts = retryCount + 1; // retryCount=1 means 2 total attempts (1 initial + 1 retry)\n\n let lastError: Error | undefined;\n const attemptErrors: Array<{ attempt: number; error: unknown }> = [];\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n const { signal: attemptSignal, cleanup: cleanupAttemptSignal } =\n buildRequestAbortSignal(effectiveTimeoutMs, options?.abortSignal);\n try {\n const result = await completion.create(\n {\n model: modelName,\n messages: messagesWithImageDetail,\n ...requestConfig,\n stream: false,\n } as any,\n { signal: attemptSignal },\n );\n\n timeCost = Date.now() - startTime;\n requestId =\n getLatestSuccessfulResponseRequestId(openAIErrorResponseContext) ??\n result._request_id;\n\n debugProfileStats(\n `model, ${modelName}, mode, ${modelFamily || 'default'}, prompt-tokens, ${result.usage?.prompt_tokens || ''}, completion-tokens, ${result.usage?.completion_tokens || ''}, total-tokens, ${result.usage?.total_tokens || ''}, cost-ms, ${timeCost}, requestId, ${requestId || ''}, temperature, ${temperature ?? ''}`,\n );\n\n debugProfileDetail(\n `model usage detail: ${JSON.stringify(result.usage)}`,\n );\n\n if (!result.choices) {\n throw new Error(\n `invalid response from LLM service: ${JSON.stringify(result)}`,\n );\n }\n\n rawChoiceMessage = result.choices[0].message;\n const parsedMessage =\n adapter.chatCompletion.extractContentAndReasoning(\n result.choices[0].message,\n );\n content = parsedMessage.content;\n accumulatedReasoning = parsedMessage.reasoning_content;\n usage = result.usage;\n responseModelName = result.model;\n\n content = resolveContentWithReasoningFallback(\n content,\n accumulatedReasoning,\n );\n\n if (!hasUsableText(content)) {\n const errorUsage = buildUsageInfo(usage, requestId);\n if (errorUsage && modelRuntime.onUsage) {\n modelRuntime.onUsage(errorUsage);\n }\n throw new AIResponseParseError(\n 'empty content from AI model',\n content || '',\n errorUsage,\n rawChoiceMessage,\n );\n }\n\n break; // Success, exit retry loop\n } catch (error) {\n lastError = restoreHardTimeoutError(toError(error), attemptSignal);\n attemptErrors.push({ attempt, error: lastError });\n const wasHardTimeout = isHardTimeoutError(lastError);\n if (wasHardTimeout) {\n warnCall(\n `AI call hit hard timeout (${effectiveTimeoutMs}ms, attempt ${attempt}/${maxAttempts}, model ${modelName}, slot ${modelConfig.slot})`,\n );\n }\n // Do not retry if the request was aborted by the caller\n if (options?.abortSignal?.aborted) {\n break;\n }\n if (attempt < maxAttempts) {\n warnCall(\n `AI call failed (attempt ${attempt}/${maxAttempts}), retrying in ${retryInterval}ms... Error: ${lastError.message}`,\n );\n await new Promise((resolve) => setTimeout(resolve, retryInterval));\n }\n } finally {\n cleanupAttemptSignal();\n }\n }\n\n if (!content) {\n assert(\n lastError,\n 'AI model request failed without recording an attempt error',\n );\n throw appendAIRequestFailureSummary(\n lastError,\n attemptErrors,\n maxAttempts,\n );\n }\n }\n\n debugCall(`response reasoning content: ${accumulatedReasoning}`);\n debugCall(`response content: ${content}`);\n\n // Ensure we always have usage info for streaming responses\n if (isStreaming && !usage) {\n // Estimate token counts based on content length (rough approximation)\n const estimatedTokens = Math.max(\n 1,\n Math.floor((content || '').length / 4),\n );\n usage = {\n prompt_tokens: estimatedTokens,\n completion_tokens: estimatedTokens,\n total_tokens: estimatedTokens * 2,\n } as OpenAI.CompletionUsage;\n }\n\n const finalUsage = buildUsageInfo(usage, requestId);\n // Report usage to the runtime-level collector if not already reported\n // (e.g. from the streaming final-chunk handler).\n if (!usageReported && finalUsage && modelRuntime.onUsage) {\n modelRuntime.onUsage(finalUsage);\n }\n\n return {\n content: content || '',\n reasoning_content: accumulatedReasoning || undefined,\n rawChoiceMessage,\n usage: finalUsage,\n isStreamed: !!isStreaming,\n };\n } catch (e: any) {\n warnCall('call AI error', e);\n\n if (e instanceof AIResponseParseError) {\n throw e;\n }\n\n const newError = new Error(\n `failed to call ${isStreaming ? 'streaming ' : ''}AI model service (${modelName}): ${e.message}${formatOpenAIAPIErrorDetails(e, openAIErrorResponseContext)}\\nTrouble shooting: https://midscenejs.com/model-provider.html`,\n {\n cause: e,\n },\n );\n throw newError;\n }\n}\n\nexport async function callAIWithObjectResponse<T>(\n messages: ChatCompletionMessageParam[],\n modelRuntime: ModelRuntime,\n options?: {\n abortSignal?: AbortSignal;\n jsonParserSource?: JsonParserSource;\n retryTimes?: number;\n retryInterval?: number;\n },\n): Promise<{\n // TODO: `content` is a misleading name here because this is already the parsed object response. Consider renaming it to `object` or `data`.\n content: T;\n contentString: string;\n usage?: AIUsageInfo;\n reasoning_content?: string;\n rawChoiceMessage?: unknown;\n}> {\n const { config: modelConfig, adapter } = modelRuntime;\n return callAiAndParseWithRetry({\n callAi: () =>\n callAI(messages, modelRuntime, {\n abortSignal: options?.abortSignal,\n expectedJsonObjectResponse: true,\n }),\n parseResponse: (response) => {\n assert(response, 'empty response');\n const jsonContent = adapter.jsonParser(response.content, {\n source: options?.jsonParserSource ?? 'generic-object',\n });\n // This API expects a JSON object. Bare JSON primitives are valid JSON,\n // but do not satisfy object-response callers.\n if (!jsonContent || typeof jsonContent !== 'object') {\n throw new Error(\n `failed to parse json response from model (${modelConfig.modelName}): ${response.content}`,\n );\n }\n return {\n content: jsonContent as T,\n contentString: response.content,\n usage: response.usage,\n reasoning_content: response.reasoning_content,\n rawChoiceMessage: response.rawChoiceMessage,\n };\n },\n toParseError: (error, response) => {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n return new AIResponseParseError(\n errorMessage,\n response.content,\n response.usage,\n response.rawChoiceMessage,\n response.reasoning_content,\n );\n },\n parseRetryTimes: options?.retryTimes ?? modelConfig.retryCount,\n parseRetryInterval: options?.retryInterval ?? modelConfig.retryInterval,\n abortSignal: options?.abortSignal,\n });\n}\n\nexport async function callAIWithStringResponse(\n msgs: AIArgs,\n modelRuntime: ModelRuntime,\n options?: Pick<CallAIOptions, 'abortSignal' | 'requiresOriginalImageDetail'>,\n): Promise<{\n content: string;\n usage?: AIUsageInfo;\n rawChoiceMessage?: unknown;\n}> {\n const { content, usage, rawChoiceMessage } = await callAI(\n msgs,\n modelRuntime,\n options,\n );\n return { content, usage, rawChoiceMessage };\n}\n"],"names":["AIResponseParseError","Error","message","rawResponse","usage","rawChoiceMessage","reasoningContent","INTERNAL_CALL_ID_FIELD","internalCallIdCounter","nextInternalCallId","stringifyForDebug","value","JSON","_error","String","getLatestSuccessfulResponseRequestId","context","latestRequestId","response","undefined","getErrorMessage","error","toError","normalizeRetryCount","retryCount","Number","Math","appendAIRequestFailureSummary","attemptErrors","maxAttempts","failedAttempts","retries","retryLabel","originalMessage","previousAttemptErrors","details","attempt","createChatClient","modelConfig","socksProxy","httpProxy","modelName","openaiBaseURL","openaiApiKey","openaiExtraConfig","modelDescription","modelFamily","createOpenAIClient","timeout","proxyAgent","warnClient","getDebug","debugProxy","warnProxy","sanitizeProxyUrl","url","parsed","URL","ifInBrowser","moduleName","ProxyAgent","socksDispatcher","proxyUrl","port","protocol","socksType","decodeURIComponent","effectiveTimeoutMs","resolveEffectiveTimeoutMs","openAIErrorResponseContext","openAIOptions","wrapOpenAICompatibleFetch","baseOpenAI","OpenAI","openai","globalConfigManager","MIDSCENE_LANGSMITH_DEBUG","langsmithModule","wrapOpenAI","MIDSCENE_LANGFUSE_DEBUG","langfuseModule","observeOpenAI","wrappedClient","callAI","messages","modelRuntime","options","adapter","internalCallId","chatCompletionInput","imageDetail","isCodexAppServerProvider","codexResult","callAIWithCodexAppServer","completion","extraBody","debugCall","warnCall","debugProfileStats","debugProfileDetail","startTime","Date","isStreaming","adapterChatCompletionParams","content","accumulated","accumulatedReasoning","timeCost","requestId","responseModelName","usageReported","hasUsableText","resolveContentWithReasoningFallback","contentValue","buildUsageInfo","usageData","cachedInputTokens","requestConfig","temperature","messagesWithImageDetail","msg","Array","part","streamSignal","cleanupStreamSignal","buildRequestAbortSignal","stream","chunk","parsedChunk","reasoning_content","chunkData","estimatedTokens","finalAccumulated","finalUsage","finalChunk","restoreHardTimeoutError","retryInterval","lastError","attemptSignal","cleanupAttemptSignal","result","parsedMessage","errorUsage","wasHardTimeout","isHardTimeoutError","Promise","resolve","setTimeout","assert","e","newError","formatOpenAIAPIErrorDetails","callAIWithObjectResponse","callAiAndParseWithRetry","jsonContent","errorMessage","callAIWithStringResponse","msgs"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ai-model/workflows/planning/custom-planning.mjs","sources":["../../../../../src/ai-model/workflows/planning/custom-planning.ts"],"sourcesContent":["import { type TUserPrompt, userPromptToString } from '@/common';\nimport type { PlanningAIResponse, PlanningAction } from '@/types';\nimport type { ChatCompletionMessageParam } from 'openai/resources/index';\nimport { ScreenshotItem } from '../../../screenshot-item';\nimport type {\n CustomPlanningInput,\n CustomPlanningMessageConfig,\n ResolvedCustomPlanningDefinition,\n} from '../../model-adapter/custom-planning-types';\nimport {\n AIResponseParseError,\n callAIWithStringResponse,\n} from '../../service-caller/index';\nimport { prepareModelImage } from '../image-preprocess';\nimport { normalizePlanningActionLocateFields } from './locate-normalization';\nimport type { PlanOptions } from './types';\n\nfunction appendHighPriorityKnowledge(\n systemPrompt: string,\n actionContext?: string,\n): string {\n return (\n systemPrompt +\n (actionContext\n ? `<high_priority_knowledge>${actionContext}</high_priority_knowledge>\\n`\n : '')\n );\n}\n\nexport function buildCustomPlanningMessages<TParsed>(\n input: CustomPlanningInput,\n config: CustomPlanningMessageConfig<TParsed>,\n): ChatCompletionMessageParam[] {\n const { options, userInstructionText } = input;\n const { conversationHistory, context, actionContext } = options;\n const systemPrompt = appendHighPriorityKnowledge(\n config.buildSystemPrompt(),\n actionContext,\n );\n const userInstruction = config.buildUserInstruction\n ? config.buildUserInstruction(userInstructionText)\n : userInstructionText;\n\n if (conversationHistory.pendingFeedbackMessage) {\n conversationHistory.append({\n role: 'user',\n content: [\n {\n type: 'text',\n text: `${conversationHistory.pendingFeedbackMessage}. The previous action has been executed, here is the latest screenshot. Please continue according to the instruction.`,\n },\n ],\n });\n conversationHistory.resetPendingFeedbackMessageIfExists();\n }\n\n conversationHistory.append({\n role: 'user',\n content: [\n {\n type: 'image_url',\n image_url: { url: context.screenshot.base64 },\n },\n ],\n });\n\n if (config.systemPromptPlacement === 'system-message') {\n return [\n { role: 'system', content: systemPrompt },\n {\n role: 'user',\n content: [{ type: 'text', text: userInstruction }],\n },\n ...(options.referenceImageMessages ?? []),\n ...conversationHistory.snapshot(config.historyImageLimit),\n ];\n }\n\n return [\n {\n role: 'user',\n content: `${systemPrompt}${userInstruction}`,\n },\n ...(options.referenceImageMessages ?? []),\n ...conversationHistory.snapshot(config.historyImageLimit),\n ];\n}\n\nexport async function runCustomPlanning<TParsed>(\n userInstruction: TUserPrompt,\n options: PlanOptions,\n config: ResolvedCustomPlanningDefinition<TParsed>,\n): Promise<PlanningAIResponse> {\n const { context } = options;\n const preparedImage = await prepareModelImage({\n imageBase64: context.screenshot.base64,\n width: context.shotSize.width,\n height: context.shotSize.height,\n policy: options.modelRuntime.adapter.imagePreprocess,\n });\n const preparedOptions: PlanOptions = {\n ...options,\n context: {\n ...context,\n screenshot: ScreenshotItem.create(\n preparedImage.imageBase64,\n context.screenshot.capturedAt,\n ),\n shotSize: preparedImage.preparedSize,\n },\n };\n const input: CustomPlanningInput = {\n userInstruction,\n userInstructionText: userPromptToString(userInstruction),\n options: preparedOptions,\n coordinateSystem: config.coordinateSystem,\n };\n\n const messages = buildCustomPlanningMessages(input, config.messages);\n const { content, usage, rawChoiceMessage } = await callAIWithStringResponse(\n messages,\n preparedOptions.modelRuntime,\n {\n abortSignal: preparedOptions.abortSignal,\n requiresOriginalImageDetail: preparedOptions.includeLocateInPlanning,\n },\n );\n\n let parsed: TParsed;\n let actions: PlanningAction[];\n let shouldContinuePlanning: boolean;\n\n try {\n parsed = config.parseResponse(content, input);\n actions = config.transformActions(parsed, input);\n normalizePlanningActionLocateFields(actions, {\n actionSpace: preparedOptions.actionSpace,\n includeLocateInPlanning: preparedOptions.includeLocateInPlanning,\n locateResultAdapter: config.coordinateNormalizer,\n locateResultContext: {\n preparedSize: preparedImage.preparedSize,\n contentSize: preparedImage.contentSize,\n },\n });\n shouldContinuePlanning = config.shouldContinuePlanning(parsed, actions);\n } catch (parseError) {\n const errorMessage = `Parse error: ${\n parseError instanceof Error ? parseError.message : String(parseError)\n }`;\n throw new AIResponseParseError(\n errorMessage,\n JSON.stringify(content, undefined, 2),\n usage,\n rawChoiceMessage,\n );\n }\n\n const assistantContent = config.messages.buildAssistantContent?.(\n parsed,\n content,\n input,\n );\n if (assistantContent) {\n options.conversationHistory.append({\n role: 'assistant',\n content: assistantContent,\n });\n }\n\n return {\n actions,\n log: config.buildResponseLog(parsed, content),\n usage,\n shouldContinuePlanning,\n rawResponse: JSON.stringify(content, undefined, 2),\n rawChoiceMessage,\n };\n}\n"],"names":["appendHighPriorityKnowledge","systemPrompt","actionContext","buildCustomPlanningMessages","input","config","options","userInstructionText","conversationHistory","context","userInstruction","runCustomPlanning","preparedImage","prepareModelImage","preparedOptions","ScreenshotItem","userPromptToString","messages","content","usage","rawChoiceMessage","callAIWithStringResponse","parsed","actions","shouldContinuePlanning","normalizePlanningActionLocateFields","parseError","errorMessage","Error","String","AIResponseParseError","JSON","undefined","assistantContent"],"mappings":"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"}
1
+ {"version":3,"file":"ai-model/workflows/planning/custom-planning.mjs","sources":["../../../../../src/ai-model/workflows/planning/custom-planning.ts"],"sourcesContent":["import { type TUserPrompt, userPromptToString } from '@/common';\nimport type { PlanningAIResponse, PlanningAction } from '@/types';\nimport type { ChatCompletionMessageParam } from 'openai/resources/index';\nimport { ScreenshotItem } from '../../../screenshot-item';\nimport type {\n CustomPlanningInput,\n CustomPlanningMessageConfig,\n ResolvedCustomPlanningDefinition,\n} from '../../model-adapter/custom-planning-types';\nimport {\n AIResponseParseError,\n callAIWithStringResponse,\n} from '../../service-caller/index';\nimport { prepareModelImage } from '../image-preprocess';\nimport { normalizePlanningActionLocateFields } from './locate-normalization';\nimport type { PlanOptions } from './types';\n\nfunction appendHighPriorityKnowledge(\n systemPrompt: string,\n actionContext?: string,\n): string {\n return (\n systemPrompt +\n (actionContext\n ? `<high_priority_knowledge>${actionContext}</high_priority_knowledge>\\n`\n : '')\n );\n}\n\nexport function buildCustomPlanningMessages<TParsed>(\n input: CustomPlanningInput,\n config: CustomPlanningMessageConfig<TParsed>,\n): ChatCompletionMessageParam[] {\n const { options, userInstructionText } = input;\n const { conversationHistory, context, actionContext } = options;\n const systemPrompt = appendHighPriorityKnowledge(\n config.buildSystemPrompt(),\n actionContext,\n );\n const userInstruction = config.buildUserInstruction\n ? config.buildUserInstruction(userInstructionText)\n : userInstructionText;\n\n if (conversationHistory.pendingFeedbackMessage) {\n conversationHistory.append({\n role: 'user',\n content: [\n {\n type: 'text',\n text: `${conversationHistory.pendingFeedbackMessage}. The previous action has been executed, here is the latest screenshot. Please continue according to the instruction.`,\n },\n ],\n });\n conversationHistory.resetPendingFeedbackMessageIfExists();\n }\n\n conversationHistory.append({\n role: 'user',\n content: [\n {\n type: 'image_url',\n image_url: { url: context.screenshot.base64 },\n },\n ],\n });\n\n if (config.systemPromptPlacement === 'system-message') {\n return [\n { role: 'system', content: systemPrompt },\n {\n role: 'user',\n content: [{ type: 'text', text: userInstruction }],\n },\n ...(options.referenceImageMessages ?? []),\n ...conversationHistory.snapshot(config.historyImageLimit),\n ];\n }\n\n return [\n {\n role: 'user',\n content: `${systemPrompt}${userInstruction}`,\n },\n ...(options.referenceImageMessages ?? []),\n ...conversationHistory.snapshot(config.historyImageLimit),\n ];\n}\n\nexport async function runCustomPlanning<TParsed>(\n userInstruction: TUserPrompt,\n options: PlanOptions,\n config: ResolvedCustomPlanningDefinition<TParsed>,\n): Promise<PlanningAIResponse> {\n const { context } = options;\n const preparedImage = await prepareModelImage({\n imageBase64: context.screenshot.base64,\n width: context.shotSize.width,\n height: context.shotSize.height,\n policy: options.modelRuntime.adapter.imagePreprocess,\n });\n const preparedOptions: PlanOptions = {\n ...options,\n context: {\n ...context,\n screenshot: ScreenshotItem.create(\n preparedImage.imageBase64,\n context.screenshot.capturedAt,\n ),\n shotSize: preparedImage.preparedSize,\n },\n };\n const input: CustomPlanningInput = {\n userInstruction,\n userInstructionText: userPromptToString(userInstruction),\n options: preparedOptions,\n coordinateSystem: config.coordinateSystem,\n };\n\n const messages = buildCustomPlanningMessages(input, config.messages);\n const { content, usage, rawChoiceMessage } = await callAIWithStringResponse(\n messages,\n preparedOptions.modelRuntime,\n {\n abortSignal: preparedOptions.abortSignal,\n requiresOriginalImageDetail: preparedOptions.includeLocateInPlanning,\n },\n );\n\n let parsed: TParsed;\n let actions: PlanningAction[];\n let shouldContinuePlanning: boolean;\n\n try {\n parsed = config.parseResponse(content, input);\n actions = config.transformActions(parsed, input);\n normalizePlanningActionLocateFields(actions, {\n actionSpace: preparedOptions.actionSpace,\n includeLocateInPlanning: preparedOptions.includeLocateInPlanning,\n locateResultAdapter: config.coordinateNormalizer,\n locateResultContext: {\n preparedSize: preparedImage.preparedSize,\n contentSize: preparedImage.contentSize,\n },\n });\n shouldContinuePlanning = config.shouldContinuePlanning(parsed, actions);\n } catch (parseError) {\n const errorMessage = `Parse error: ${\n parseError instanceof Error ? parseError.message : String(parseError)\n }`;\n throw new AIResponseParseError(\n errorMessage,\n JSON.stringify(content, undefined, 2),\n usage,\n rawChoiceMessage,\n );\n }\n\n const assistantContent = config.messages.buildAssistantContent?.(\n parsed,\n content,\n input,\n );\n // UI-TARS and Auto-GLM define their own assistant history format.\n if (assistantContent) {\n options.conversationHistory.append({\n role: 'assistant',\n content: assistantContent,\n });\n }\n\n return {\n actions,\n log: config.buildResponseLog(parsed, content),\n usage,\n shouldContinuePlanning,\n rawResponse: JSON.stringify(content, undefined, 2),\n rawChoiceMessage,\n };\n}\n"],"names":["appendHighPriorityKnowledge","systemPrompt","actionContext","buildCustomPlanningMessages","input","config","options","userInstructionText","conversationHistory","context","userInstruction","runCustomPlanning","preparedImage","prepareModelImage","preparedOptions","ScreenshotItem","userPromptToString","messages","content","usage","rawChoiceMessage","callAIWithStringResponse","parsed","actions","shouldContinuePlanning","normalizePlanningActionLocateFields","parseError","errorMessage","Error","String","AIResponseParseError","JSON","undefined","assistantContent"],"mappings":"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"}
@@ -55,6 +55,26 @@ const defineActionTap = (tap)=>defineLocatedPointAction({
55
55
  await tap(point);
56
56
  }
57
57
  });
58
+ const registerFileChooserAcceptParamSchema = z.object({
59
+ files: z.union([
60
+ z.string(),
61
+ z.array(z.string())
62
+ ]).describe("File path(s) within the current aiAct call's fileChooserAllowedDir to use whenever a later action triggers a file chooser. fileChooserAllowedDir must be provided for this action. This setting replaces any previously registered file path(s).")
63
+ });
64
+ const defineActionRegisterFileChooserAccept = (register)=>defineAction({
65
+ name: 'RegisterFileChooserAccept',
66
+ description: 'Configure files for file chooser dialogs triggered by later actions in this aiAct',
67
+ interfaceAlias: 'registerFileChooserAccept',
68
+ paramSchema: registerFileChooserAcceptParamSchema,
69
+ sample: {
70
+ files: [
71
+ 'fixtures/document.pdf'
72
+ ]
73
+ },
74
+ call: async (param)=>{
75
+ await register(param.files);
76
+ }
77
+ });
58
78
  const actionRightClickParamSchema = z.object({
59
79
  locate: getMidsceneLocationSchema().describe('The element to be right clicked')
60
80
  });
@@ -479,6 +499,6 @@ const defineActionSleep = ()=>defineAction({
479
499
  await new Promise((resolve)=>setTimeout(resolve, duration));
480
500
  }
481
501
  });
482
- export { AbstractInterface, ActionLongPressParamSchema, ActionPinchParamSchema, ActionSleepParamSchema, ActionSwipeParamSchema, actionClearInputParamSchema, actionCursorMoveParamSchema, actionDoubleClickParamSchema, actionDragAndDropParamSchema, actionHoverParamSchema, actionInputParamSchema, actionKeyboardPressParamSchema, actionRightClickParamSchema, actionScrollParamSchema, actionTapParamSchema, createDefaultMobileActions, defineAction, defineActionClearInput, defineActionCursorMove, defineActionDoubleClick, defineActionDragAndDrop, defineActionHover, defineActionInput, defineActionKeyboardPress, defineActionLongPress, defineActionPinch, defineActionRightClick, defineActionScroll, defineActionSleep, defineActionSwipe, defineActionTap, defineActionsFromInputPrimitives, normalizeMobileSwipeParam, normalizePinchParam };
502
+ export { AbstractInterface, ActionLongPressParamSchema, ActionPinchParamSchema, ActionSleepParamSchema, ActionSwipeParamSchema, actionClearInputParamSchema, actionCursorMoveParamSchema, actionDoubleClickParamSchema, actionDragAndDropParamSchema, actionHoverParamSchema, actionInputParamSchema, actionKeyboardPressParamSchema, actionRightClickParamSchema, actionScrollParamSchema, actionTapParamSchema, createDefaultMobileActions, defineAction, defineActionClearInput, defineActionCursorMove, defineActionDoubleClick, defineActionDragAndDrop, defineActionHover, defineActionInput, defineActionKeyboardPress, defineActionLongPress, defineActionPinch, defineActionRegisterFileChooserAccept, defineActionRightClick, defineActionScroll, defineActionSleep, defineActionSwipe, defineActionTap, defineActionsFromInputPrimitives, normalizeMobileSwipeParam, normalizePinchParam, registerFileChooserAcceptParamSchema };
483
503
 
484
504
  //# sourceMappingURL=index.mjs.map