@lvce-editor/chat-view 3.6.0 → 3.8.0

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.
@@ -1075,6 +1075,7 @@ const create$2 = rpcId => {
1075
1075
  };
1076
1076
 
1077
1077
  const {
1078
+ invoke: invoke$2,
1078
1079
  set: set$3
1079
1080
  } = create$2(6002);
1080
1081
 
@@ -1525,6 +1526,7 @@ const createDefaultState = () => {
1525
1526
  tokensUsed: 0,
1526
1527
  uid: 0,
1527
1528
  usageOverviewEnabled: false,
1529
+ useChatNetworkWorkerForRequests: false,
1528
1530
  useMockApi: false,
1529
1531
  viewMode: 'list',
1530
1532
  warningCount: 0,
@@ -2790,393 +2792,6 @@ const openRouterTooManyRequestsMessage = 'OpenRouter rate limit reached (429). P
2790
2792
  const openRouterRequestFailureReasons = ['ContentSecurityPolicyViolation: Check DevTools for details.', 'OpenRouter server offline: Check DevTools for details.', 'Check your internet connection.'];
2791
2793
  const openRouterTooManyRequestsReasons = ['Wait a short time and retry your request.', 'Reduce request frequency to avoid rate limits.', 'Use a different model if this one is saturated.'];
2792
2794
 
2793
- const delay = async ms => {
2794
- await new Promise(resolve => setTimeout(resolve, ms));
2795
- };
2796
-
2797
- const getMockAiResponse = async (userMessage, delayInMs) => {
2798
- await delay(delayInMs);
2799
- return `Mock AI response: I received "${userMessage}".`;
2800
- };
2801
-
2802
- let queue = [];
2803
- let waiters = [];
2804
- let finished = false;
2805
- let errorResult;
2806
- const reset$1 = () => {
2807
- queue = [];
2808
- waiters = [];
2809
- finished = false;
2810
- errorResult = undefined;
2811
- };
2812
- const setHttpErrorResponse = (statusCode, body) => {
2813
- const rawError = body && typeof body === 'object' ? Reflect.get(body, 'error') : undefined;
2814
- const errorCode = rawError && typeof rawError === 'object' ? Reflect.get(rawError, 'code') : undefined;
2815
- const errorMessage = rawError && typeof rawError === 'object' ? Reflect.get(rawError, 'message') : undefined;
2816
- const errorType = rawError && typeof rawError === 'object' ? Reflect.get(rawError, 'type') : undefined;
2817
- errorResult = {
2818
- details: 'http-error',
2819
- ...(typeof errorCode === 'string' ? {
2820
- errorCode
2821
- } : {}),
2822
- ...(typeof errorMessage === 'string' ? {
2823
- errorMessage
2824
- } : {}),
2825
- ...(typeof errorType === 'string' ? {
2826
- errorType
2827
- } : {}),
2828
- statusCode,
2829
- type: 'error'
2830
- };
2831
- };
2832
- const takeErrorResponse = () => {
2833
- const error = errorResult;
2834
- errorResult = undefined;
2835
- return error;
2836
- };
2837
- const pushChunk = chunk => {
2838
- if (waiters.length > 0) {
2839
- const resolve = waiters.shift();
2840
- resolve?.(chunk);
2841
- return;
2842
- }
2843
- queue.push(chunk);
2844
- };
2845
- const finish = () => {
2846
- finished = true;
2847
- if (waiters.length === 0) {
2848
- return;
2849
- }
2850
- const activeWaiters = waiters;
2851
- waiters = [];
2852
- for (const resolve of activeWaiters) {
2853
- resolve(undefined);
2854
- }
2855
- };
2856
- const readNextChunk = async () => {
2857
- if (queue.length > 0) {
2858
- return queue.shift();
2859
- }
2860
- if (finished) {
2861
- return undefined;
2862
- }
2863
- const {
2864
- promise,
2865
- resolve
2866
- } = Promise.withResolvers();
2867
- waiters.push(resolve);
2868
- return promise;
2869
- };
2870
-
2871
- const parseSseDataLines = eventChunk => {
2872
- const lines = eventChunk.split('\n');
2873
- const dataLines = [];
2874
- for (const line of lines) {
2875
- if (!line.startsWith('data:')) {
2876
- continue;
2877
- }
2878
- dataLines.push(line.slice(5).trimStart());
2879
- }
2880
- return dataLines;
2881
- };
2882
- const emitToolCalls = async (toolCallAccumulator, onToolCallsChunk) => {
2883
- if (!onToolCallsChunk) {
2884
- return;
2885
- }
2886
- const toolCalls = Object.entries(toolCallAccumulator).toSorted((a, b) => Number(a[0]) - Number(b[0])).map(entry => entry[1]).filter(toolCall => !!toolCall.name);
2887
- if (toolCalls.length === 0) {
2888
- return;
2889
- }
2890
- await onToolCallsChunk(toolCalls);
2891
- };
2892
- const getMockOpenApiAssistantText = async (stream, onTextChunk, onToolCallsChunk, onDataEvent, onEventStreamFinished) => {
2893
- const error = takeErrorResponse();
2894
- if (error) {
2895
- return error;
2896
- }
2897
- let text = '';
2898
- let remainder = '';
2899
- let toolCallAccumulator = {};
2900
- let finishedNotified = false;
2901
- const notifyFinished = async () => {
2902
- if (finishedNotified) {
2903
- return;
2904
- }
2905
- finishedNotified = true;
2906
- if (onEventStreamFinished) {
2907
- await onEventStreamFinished();
2908
- }
2909
- };
2910
- const handleParsedSseEvent = async parsed => {
2911
- if (onDataEvent) {
2912
- await onDataEvent(parsed);
2913
- }
2914
- if (!parsed || typeof parsed !== 'object') {
2915
- return;
2916
- }
2917
- const eventType = Reflect.get(parsed, 'type');
2918
- if (eventType === 'response.completed') {
2919
- await notifyFinished();
2920
- return;
2921
- }
2922
- if (eventType === 'response.output_text.delta') {
2923
- const delta = Reflect.get(parsed, 'delta');
2924
- if (typeof delta !== 'string' || !delta) {
2925
- return;
2926
- }
2927
- text += delta;
2928
- if (stream && onTextChunk) {
2929
- await onTextChunk(delta);
2930
- }
2931
- return;
2932
- }
2933
- if (eventType === 'response.output_item.added') {
2934
- const outputIndex = Reflect.get(parsed, 'output_index');
2935
- const item = Reflect.get(parsed, 'item');
2936
- if (typeof outputIndex !== 'number' || !item || typeof item !== 'object') {
2937
- return;
2938
- }
2939
- if (Reflect.get(item, 'type') !== 'function_call') {
2940
- return;
2941
- }
2942
- const name = Reflect.get(item, 'name');
2943
- const argumentsValue = Reflect.get(item, 'arguments');
2944
- const callId = Reflect.get(item, 'call_id');
2945
- toolCallAccumulator = {
2946
- ...toolCallAccumulator,
2947
- [outputIndex]: {
2948
- arguments: typeof argumentsValue === 'string' ? argumentsValue : '',
2949
- ...(typeof callId === 'string' ? {
2950
- id: callId
2951
- } : {}),
2952
- name: typeof name === 'string' ? name : ''
2953
- }
2954
- };
2955
- await emitToolCalls(toolCallAccumulator, onToolCallsChunk);
2956
- return;
2957
- }
2958
- if (eventType === 'response.function_call_arguments.delta' || eventType === 'response.function_call_arguments.done') {
2959
- const outputIndex = Reflect.get(parsed, 'output_index');
2960
- if (typeof outputIndex !== 'number') {
2961
- return;
2962
- }
2963
- const current = toolCallAccumulator[outputIndex] || {
2964
- arguments: '',
2965
- name: ''
2966
- };
2967
- const delta = Reflect.get(parsed, 'delta');
2968
- const argumentsValue = Reflect.get(parsed, 'arguments');
2969
- const name = Reflect.get(parsed, 'name');
2970
- const callId = Reflect.get(parsed, 'call_id');
2971
- const next = {
2972
- arguments: typeof argumentsValue === 'string' ? argumentsValue : typeof delta === 'string' ? `${current.arguments}${delta}` : current.arguments,
2973
- ...(typeof callId === 'string' ? {
2974
- id: callId
2975
- } : current.id ? {
2976
- id: current.id
2977
- } : {}),
2978
- name: typeof name === 'string' && name ? name : current.name
2979
- };
2980
- toolCallAccumulator = {
2981
- ...toolCallAccumulator,
2982
- [outputIndex]: next
2983
- };
2984
- await emitToolCalls(toolCallAccumulator, onToolCallsChunk);
2985
- }
2986
- };
2987
- const consumeSseDataLines = async dataLines => {
2988
- for (const line of dataLines) {
2989
- if (line === '[DONE]') {
2990
- await notifyFinished();
2991
- continue;
2992
- }
2993
- let parsed;
2994
- try {
2995
- parsed = JSON.parse(line);
2996
- } catch {
2997
- continue;
2998
- }
2999
- await handleParsedSseEvent(parsed);
3000
- }
3001
- };
3002
- while (true) {
3003
- const chunk = await readNextChunk();
3004
- if (typeof chunk !== 'string') {
3005
- break;
3006
- }
3007
- if (chunk.startsWith('data:')) {
3008
- remainder += chunk;
3009
- while (true) {
3010
- const separatorIndex = remainder.indexOf('\n\n');
3011
- if (separatorIndex === -1) {
3012
- break;
3013
- }
3014
- const rawEvent = remainder.slice(0, separatorIndex);
3015
- remainder = remainder.slice(separatorIndex + 2);
3016
- const dataLines = parseSseDataLines(rawEvent);
3017
- await consumeSseDataLines(dataLines);
3018
- }
3019
- continue;
3020
- }
3021
- text += chunk;
3022
- if (stream && onTextChunk) {
3023
- await onTextChunk(chunk);
3024
- }
3025
- }
3026
- if (remainder) {
3027
- const dataLines = parseSseDataLines(remainder);
3028
- await consumeSseDataLines(dataLines);
3029
- }
3030
- await notifyFinished();
3031
- return {
3032
- text,
3033
- type: 'success'
3034
- };
3035
- };
3036
-
3037
- const activateByEvent = (event, assetDir, platform) => {
3038
- // @ts-ignore
3039
- return activateByEvent$1(event, assetDir, platform);
3040
- };
3041
-
3042
- const executeProvider = async ({
3043
- assetDir,
3044
- event,
3045
- method,
3046
- noProviderFoundMessage,
3047
- params,
3048
- platform
3049
- }) => {
3050
- await activateByEvent(event, assetDir, platform);
3051
- // @ts-ignore
3052
- const result = invoke$1(method, ...params);
3053
- return result;
3054
- };
3055
-
3056
- const CommandExecute = 'ExtensionHostCommand.executeCommand';
3057
- const FileSystemWriteFile = 'ExtensionHostFileSystem.writeFile';
3058
-
3059
- const normalizeLimitInfo = value => {
3060
- if (!value || typeof value !== 'object') {
3061
- return undefined;
3062
- }
3063
- const limitRemaining = Reflect.get(value, 'limitRemaining');
3064
- const limitReset = Reflect.get(value, 'limitReset');
3065
- const retryAfter = Reflect.get(value, 'retryAfter');
3066
- const usage = Reflect.get(value, 'usage');
3067
- const usageDaily = Reflect.get(value, 'usageDaily');
3068
- const normalized = {
3069
- ...(typeof limitRemaining === 'number' || limitRemaining === null ? {
3070
- limitRemaining
3071
- } : {}),
3072
- ...(typeof limitReset === 'string' || limitReset === null ? {
3073
- limitReset
3074
- } : {}),
3075
- ...(typeof retryAfter === 'string' || retryAfter === null ? {
3076
- retryAfter
3077
- } : {}),
3078
- ...(typeof usage === 'number' ? {
3079
- usage
3080
- } : {}),
3081
- ...(typeof usageDaily === 'number' ? {
3082
- usageDaily
3083
- } : {})
3084
- };
3085
- const hasDetails = typeof limitRemaining === 'number' || limitRemaining === null || typeof limitReset === 'string' || limitReset === null || typeof retryAfter === 'string' || retryAfter === null || typeof usage === 'number' || typeof usageDaily === 'number';
3086
- return hasDetails ? normalized : undefined;
3087
- };
3088
-
3089
- const normalizeMockResult = value => {
3090
- if (typeof value === 'string') {
3091
- return {
3092
- text: value,
3093
- type: 'success'
3094
- };
3095
- }
3096
- if (!value || typeof value !== 'object') {
3097
- return {
3098
- details: 'request-failed',
3099
- type: 'error'
3100
- };
3101
- }
3102
- const type = Reflect.get(value, 'type');
3103
- if (type === 'success') {
3104
- const text = Reflect.get(value, 'text');
3105
- if (typeof text === 'string') {
3106
- return {
3107
- text,
3108
- type: 'success'
3109
- };
3110
- }
3111
- return {
3112
- details: 'request-failed',
3113
- type: 'error'
3114
- };
3115
- }
3116
- if (type === 'error') {
3117
- const details = Reflect.get(value, 'details');
3118
- if (details === 'request-failed' || details === 'too-many-requests' || details === 'http-error') {
3119
- const rawMessage = Reflect.get(value, 'rawMessage');
3120
- const statusCode = Reflect.get(value, 'statusCode');
3121
- const limitInfo = normalizeLimitInfo(Reflect.get(value, 'limitInfo'));
3122
- return {
3123
- details,
3124
- ...(limitInfo ? {
3125
- limitInfo
3126
- } : {}),
3127
- ...(typeof rawMessage === 'string' ? {
3128
- rawMessage
3129
- } : {}),
3130
- ...(typeof statusCode === 'number' ? {
3131
- statusCode
3132
- } : {}),
3133
- type: 'error'
3134
- };
3135
- }
3136
- }
3137
- const text = Reflect.get(value, 'text');
3138
- if (typeof text === 'string') {
3139
- return {
3140
- text,
3141
- type: 'success'
3142
- };
3143
- }
3144
- return {
3145
- details: 'request-failed',
3146
- type: 'error'
3147
- };
3148
- };
3149
-
3150
- const getMockOpenRouterAssistantText = async (messages, modelId, openRouterApiBaseUrl, openRouterApiKey, mockApiCommandId, assetDir, platform) => {
3151
- if (!mockApiCommandId) {
3152
- return {
3153
- details: 'request-failed',
3154
- type: 'error'
3155
- };
3156
- }
3157
- try {
3158
- const result = await executeProvider({
3159
- assetDir,
3160
- event: `onCommand:${mockApiCommandId}`,
3161
- method: CommandExecute,
3162
- noProviderFoundMessage: 'No mock api command found',
3163
- params: [mockApiCommandId, {
3164
- messages,
3165
- modelId,
3166
- openRouterApiBaseUrl,
3167
- openRouterApiKey
3168
- }],
3169
- platform
3170
- });
3171
- return normalizeMockResult(result);
3172
- } catch {
3173
- return {
3174
- details: 'request-failed',
3175
- type: 'error'
3176
- };
3177
- }
3178
- };
3179
-
3180
2795
  const executeGetWorkspaceUriTool = async (_args, _options) => {
3181
2796
  try {
3182
2797
  const workspaceUri = await getWorkspacePath();
@@ -3307,6 +2922,25 @@ const executeRenderHtmlTool = async (args, _options) => {
3307
2922
  });
3308
2923
  };
3309
2924
 
2925
+ const activateByEvent = (event, assetDir, platform) => {
2926
+ // @ts-ignore
2927
+ return activateByEvent$1(event, assetDir, platform);
2928
+ };
2929
+
2930
+ const executeProvider = async ({
2931
+ assetDir,
2932
+ event,
2933
+ method,
2934
+ noProviderFoundMessage,
2935
+ params,
2936
+ platform
2937
+ }) => {
2938
+ await activateByEvent(event, assetDir, platform);
2939
+ // @ts-ignore
2940
+ const result = invoke$1(method, ...params);
2941
+ return result;
2942
+ };
2943
+
3310
2944
  const OnFileSystem = 'onFileSystem';
3311
2945
 
3312
2946
  const executeFileSystemCommand = async (method, params, options) => {
@@ -3320,6 +2954,9 @@ const executeFileSystemCommand = async (method, params, options) => {
3320
2954
  });
3321
2955
  };
3322
2956
 
2957
+ const CommandExecute = 'ExtensionHostCommand.executeCommand';
2958
+ const FileSystemWriteFile = 'ExtensionHostFileSystem.writeFile';
2959
+
3323
2960
  const executeWriteFileTool = async (args, options) => {
3324
2961
  const filePath = typeof args.path === 'string' ? args.path : '';
3325
2962
  const content = typeof args.content === 'string' ? args.content : '';
@@ -3379,17 +3016,61 @@ const executeChatTool = async (name, rawArguments, options) => {
3379
3016
  error: `Unknown tool: ${name}`
3380
3017
  });
3381
3018
  };
3382
-
3383
- const getReadFileTool = () => {
3019
+
3020
+ const getReadFileTool = () => {
3021
+ return {
3022
+ function: {
3023
+ description: 'Read UTF-8 text content from a file inside the currently open workspace folder. Only pass an absolute URI.',
3024
+ name: 'read_file',
3025
+ parameters: {
3026
+ additionalProperties: false,
3027
+ properties: {
3028
+ uri: {
3029
+ description: 'Absolute file URI within the workspace (for example: file:///workspace/src/index.ts).',
3030
+ type: 'string'
3031
+ }
3032
+ },
3033
+ required: ['uri'],
3034
+ type: 'object'
3035
+ }
3036
+ },
3037
+ type: 'function'
3038
+ };
3039
+ };
3040
+ const getWriteFileTool = () => {
3041
+ return {
3042
+ function: {
3043
+ description: 'Write UTF-8 text content to a file inside the currently open workspace folder.',
3044
+ name: 'write_file',
3045
+ parameters: {
3046
+ additionalProperties: false,
3047
+ properties: {
3048
+ content: {
3049
+ description: 'New UTF-8 text content to write to the file.',
3050
+ type: 'string'
3051
+ },
3052
+ path: {
3053
+ description: 'Relative file path within the workspace (for example: src/index.ts).',
3054
+ type: 'string'
3055
+ }
3056
+ },
3057
+ required: ['path', 'content'],
3058
+ type: 'object'
3059
+ }
3060
+ },
3061
+ type: 'function'
3062
+ };
3063
+ };
3064
+ const getListFilesTool = () => {
3384
3065
  return {
3385
3066
  function: {
3386
- description: 'Read UTF-8 text content from a file inside the currently open workspace folder. Only pass an absolute URI.',
3387
- name: 'read_file',
3067
+ description: 'List direct children (files and folders) for a folder URI inside the currently open workspace folder. Only pass an absolute URI.',
3068
+ name: 'list_files',
3388
3069
  parameters: {
3389
3070
  additionalProperties: false,
3390
3071
  properties: {
3391
3072
  uri: {
3392
- description: 'Absolute file URI within the workspace (for example: file:///workspace/src/index.ts).',
3073
+ description: 'Absolute folder URI within the workspace (for example: file:///workspace/src).',
3393
3074
  type: 'string'
3394
3075
  }
3395
3076
  },
@@ -3400,106 +3081,434 @@ const getReadFileTool = () => {
3400
3081
  type: 'function'
3401
3082
  };
3402
3083
  };
3403
- const getWriteFileTool = () => {
3084
+ const getGetWorkspaceUriTool = () => {
3404
3085
  return {
3405
3086
  function: {
3406
- description: 'Write UTF-8 text content to a file inside the currently open workspace folder.',
3407
- name: 'write_file',
3087
+ description: 'Get the URI of the currently open workspace folder.',
3088
+ name: 'getWorkspaceUri',
3089
+ parameters: {
3090
+ additionalProperties: false,
3091
+ properties: {},
3092
+ type: 'object'
3093
+ }
3094
+ },
3095
+ type: 'function'
3096
+ };
3097
+ };
3098
+ const getRenderHtmlTool = () => {
3099
+ return {
3100
+ function: {
3101
+ description: 'Render custom HTML and optional CSS directly in the chat tool call list using native chat UI rendering. Use this for structured cards, tables, and small dashboards. After calling this tool, do not repeat the same HTML, data table, or long content again as plain text unless the user explicitly asks for a text-only version.',
3102
+ name: 'render_html',
3408
3103
  parameters: {
3409
3104
  additionalProperties: false,
3410
3105
  properties: {
3411
- content: {
3412
- description: 'New UTF-8 text content to write to the file.',
3106
+ css: {
3107
+ description: 'Optional CSS string applied inside the preview document.',
3413
3108
  type: 'string'
3414
3109
  },
3415
- path: {
3416
- description: 'Relative file path within the workspace (for example: src/index.ts).',
3110
+ html: {
3111
+ description: 'HTML string to render in the preview document.',
3112
+ type: 'string'
3113
+ },
3114
+ title: {
3115
+ description: 'Optional short title for the preview.',
3417
3116
  type: 'string'
3418
3117
  }
3419
3118
  },
3420
- required: ['path', 'content'],
3119
+ required: ['html'],
3421
3120
  type: 'object'
3422
3121
  }
3423
3122
  },
3424
3123
  type: 'function'
3425
3124
  };
3426
- };
3427
- const getListFilesTool = () => {
3428
- return {
3429
- function: {
3430
- description: 'List direct children (files and folders) for a folder URI inside the currently open workspace folder. Only pass an absolute URI.',
3431
- name: 'list_files',
3432
- parameters: {
3433
- additionalProperties: false,
3434
- properties: {
3435
- uri: {
3436
- description: 'Absolute folder URI within the workspace (for example: file:///workspace/src).',
3437
- type: 'string'
3438
- }
3439
- },
3440
- required: ['uri'],
3441
- type: 'object'
3125
+ };
3126
+ const getBasicChatTools = () => {
3127
+ return [getReadFileTool(), getWriteFileTool(), getListFilesTool(), getGetWorkspaceUriTool(), getRenderHtmlTool()];
3128
+ };
3129
+
3130
+ const getClientRequestIdHeader = () => {
3131
+ return {
3132
+ 'x-client-request-id': crypto.randomUUID()
3133
+ };
3134
+ };
3135
+
3136
+ const delay = async ms => {
3137
+ await new Promise(resolve => setTimeout(resolve, ms));
3138
+ };
3139
+
3140
+ const getMockAiResponse = async (userMessage, delayInMs) => {
3141
+ await delay(delayInMs);
3142
+ return `Mock AI response: I received "${userMessage}".`;
3143
+ };
3144
+
3145
+ let queue = [];
3146
+ let waiters = [];
3147
+ let finished = false;
3148
+ let errorResult;
3149
+ const reset$2 = () => {
3150
+ queue = [];
3151
+ waiters = [];
3152
+ finished = false;
3153
+ errorResult = undefined;
3154
+ };
3155
+ const setHttpErrorResponse = (statusCode, body) => {
3156
+ const rawError = body && typeof body === 'object' ? Reflect.get(body, 'error') : undefined;
3157
+ const errorCode = rawError && typeof rawError === 'object' ? Reflect.get(rawError, 'code') : undefined;
3158
+ const errorMessage = rawError && typeof rawError === 'object' ? Reflect.get(rawError, 'message') : undefined;
3159
+ const errorType = rawError && typeof rawError === 'object' ? Reflect.get(rawError, 'type') : undefined;
3160
+ errorResult = {
3161
+ details: 'http-error',
3162
+ ...(typeof errorCode === 'string' ? {
3163
+ errorCode
3164
+ } : {}),
3165
+ ...(typeof errorMessage === 'string' ? {
3166
+ errorMessage
3167
+ } : {}),
3168
+ ...(typeof errorType === 'string' ? {
3169
+ errorType
3170
+ } : {}),
3171
+ statusCode,
3172
+ type: 'error'
3173
+ };
3174
+ };
3175
+ const takeErrorResponse = () => {
3176
+ const error = errorResult;
3177
+ errorResult = undefined;
3178
+ return error;
3179
+ };
3180
+ const pushChunk = chunk => {
3181
+ if (waiters.length > 0) {
3182
+ const resolve = waiters.shift();
3183
+ resolve?.(chunk);
3184
+ return;
3185
+ }
3186
+ queue.push(chunk);
3187
+ };
3188
+ const finish = () => {
3189
+ finished = true;
3190
+ if (waiters.length === 0) {
3191
+ return;
3192
+ }
3193
+ const activeWaiters = waiters;
3194
+ waiters = [];
3195
+ for (const resolve of activeWaiters) {
3196
+ resolve(undefined);
3197
+ }
3198
+ };
3199
+ const readNextChunk = async () => {
3200
+ if (queue.length > 0) {
3201
+ return queue.shift();
3202
+ }
3203
+ if (finished) {
3204
+ return undefined;
3205
+ }
3206
+ const {
3207
+ promise,
3208
+ resolve
3209
+ } = Promise.withResolvers();
3210
+ waiters.push(resolve);
3211
+ return promise;
3212
+ };
3213
+
3214
+ const parseSseDataLines = eventChunk => {
3215
+ const lines = eventChunk.split('\n');
3216
+ const dataLines = [];
3217
+ for (const line of lines) {
3218
+ if (!line.startsWith('data:')) {
3219
+ continue;
3220
+ }
3221
+ dataLines.push(line.slice(5).trimStart());
3222
+ }
3223
+ return dataLines;
3224
+ };
3225
+ const emitToolCalls = async (toolCallAccumulator, onToolCallsChunk) => {
3226
+ if (!onToolCallsChunk) {
3227
+ return;
3228
+ }
3229
+ const toolCalls = Object.entries(toolCallAccumulator).toSorted((a, b) => Number(a[0]) - Number(b[0])).map(entry => entry[1]).filter(toolCall => !!toolCall.name);
3230
+ if (toolCalls.length === 0) {
3231
+ return;
3232
+ }
3233
+ await onToolCallsChunk(toolCalls);
3234
+ };
3235
+ const getMockOpenApiAssistantText = async (stream, onTextChunk, onToolCallsChunk, onDataEvent, onEventStreamFinished) => {
3236
+ const error = takeErrorResponse();
3237
+ if (error) {
3238
+ return error;
3239
+ }
3240
+ let text = '';
3241
+ let remainder = '';
3242
+ let toolCallAccumulator = {};
3243
+ let finishedNotified = false;
3244
+ const notifyFinished = async () => {
3245
+ if (finishedNotified) {
3246
+ return;
3247
+ }
3248
+ finishedNotified = true;
3249
+ if (onEventStreamFinished) {
3250
+ await onEventStreamFinished();
3251
+ }
3252
+ };
3253
+ const handleParsedSseEvent = async parsed => {
3254
+ if (onDataEvent) {
3255
+ await onDataEvent(parsed);
3256
+ }
3257
+ if (!parsed || typeof parsed !== 'object') {
3258
+ return;
3259
+ }
3260
+ const eventType = Reflect.get(parsed, 'type');
3261
+ if (eventType === 'response.completed') {
3262
+ await notifyFinished();
3263
+ return;
3264
+ }
3265
+ if (eventType === 'response.output_text.delta') {
3266
+ const delta = Reflect.get(parsed, 'delta');
3267
+ if (typeof delta !== 'string' || !delta) {
3268
+ return;
3269
+ }
3270
+ text += delta;
3271
+ if (stream && onTextChunk) {
3272
+ await onTextChunk(delta);
3273
+ }
3274
+ return;
3275
+ }
3276
+ if (eventType === 'response.output_item.added') {
3277
+ const outputIndex = Reflect.get(parsed, 'output_index');
3278
+ const item = Reflect.get(parsed, 'item');
3279
+ if (typeof outputIndex !== 'number' || !item || typeof item !== 'object') {
3280
+ return;
3281
+ }
3282
+ if (Reflect.get(item, 'type') !== 'function_call') {
3283
+ return;
3284
+ }
3285
+ const name = Reflect.get(item, 'name');
3286
+ const argumentsValue = Reflect.get(item, 'arguments');
3287
+ const callId = Reflect.get(item, 'call_id');
3288
+ toolCallAccumulator = {
3289
+ ...toolCallAccumulator,
3290
+ [outputIndex]: {
3291
+ arguments: typeof argumentsValue === 'string' ? argumentsValue : '',
3292
+ ...(typeof callId === 'string' ? {
3293
+ id: callId
3294
+ } : {}),
3295
+ name: typeof name === 'string' ? name : ''
3296
+ }
3297
+ };
3298
+ await emitToolCalls(toolCallAccumulator, onToolCallsChunk);
3299
+ return;
3300
+ }
3301
+ if (eventType === 'response.function_call_arguments.delta' || eventType === 'response.function_call_arguments.done') {
3302
+ const outputIndex = Reflect.get(parsed, 'output_index');
3303
+ if (typeof outputIndex !== 'number') {
3304
+ return;
3305
+ }
3306
+ const current = toolCallAccumulator[outputIndex] || {
3307
+ arguments: '',
3308
+ name: ''
3309
+ };
3310
+ const delta = Reflect.get(parsed, 'delta');
3311
+ const argumentsValue = Reflect.get(parsed, 'arguments');
3312
+ const name = Reflect.get(parsed, 'name');
3313
+ const callId = Reflect.get(parsed, 'call_id');
3314
+ const next = {
3315
+ arguments: typeof argumentsValue === 'string' ? argumentsValue : typeof delta === 'string' ? `${current.arguments}${delta}` : current.arguments,
3316
+ ...(typeof callId === 'string' ? {
3317
+ id: callId
3318
+ } : current.id ? {
3319
+ id: current.id
3320
+ } : {}),
3321
+ name: typeof name === 'string' && name ? name : current.name
3322
+ };
3323
+ toolCallAccumulator = {
3324
+ ...toolCallAccumulator,
3325
+ [outputIndex]: next
3326
+ };
3327
+ await emitToolCalls(toolCallAccumulator, onToolCallsChunk);
3328
+ }
3329
+ };
3330
+ const consumeSseDataLines = async dataLines => {
3331
+ for (const line of dataLines) {
3332
+ if (line === '[DONE]') {
3333
+ await notifyFinished();
3334
+ continue;
3442
3335
  }
3443
- },
3444
- type: 'function'
3445
- };
3446
- };
3447
- const getGetWorkspaceUriTool = () => {
3448
- return {
3449
- function: {
3450
- description: 'Get the URI of the currently open workspace folder.',
3451
- name: 'getWorkspaceUri',
3452
- parameters: {
3453
- additionalProperties: false,
3454
- properties: {},
3455
- type: 'object'
3336
+ let parsed;
3337
+ try {
3338
+ parsed = JSON.parse(line);
3339
+ } catch {
3340
+ continue;
3456
3341
  }
3457
- },
3458
- type: 'function'
3342
+ await handleParsedSseEvent(parsed);
3343
+ }
3459
3344
  };
3460
- };
3461
- const getRenderHtmlTool = () => {
3462
- return {
3463
- function: {
3464
- description: 'Render custom HTML and optional CSS directly in the chat tool call list using native chat UI rendering. Use this for structured cards, tables, and small dashboards. After calling this tool, do not repeat the same HTML, data table, or long content again as plain text unless the user explicitly asks for a text-only version.',
3465
- name: 'render_html',
3466
- parameters: {
3467
- additionalProperties: false,
3468
- properties: {
3469
- css: {
3470
- description: 'Optional CSS string applied inside the preview document.',
3471
- type: 'string'
3472
- },
3473
- html: {
3474
- description: 'HTML string to render in the preview document.',
3475
- type: 'string'
3476
- },
3477
- title: {
3478
- description: 'Optional short title for the preview.',
3479
- type: 'string'
3480
- }
3481
- },
3482
- required: ['html'],
3483
- type: 'object'
3345
+ while (true) {
3346
+ const chunk = await readNextChunk();
3347
+ if (typeof chunk !== 'string') {
3348
+ break;
3349
+ }
3350
+ if (chunk.startsWith('data:')) {
3351
+ remainder += chunk;
3352
+ while (true) {
3353
+ const separatorIndex = remainder.indexOf('\n\n');
3354
+ if (separatorIndex === -1) {
3355
+ break;
3356
+ }
3357
+ const rawEvent = remainder.slice(0, separatorIndex);
3358
+ remainder = remainder.slice(separatorIndex + 2);
3359
+ const dataLines = parseSseDataLines(rawEvent);
3360
+ await consumeSseDataLines(dataLines);
3484
3361
  }
3485
- },
3486
- type: 'function'
3362
+ continue;
3363
+ }
3364
+ text += chunk;
3365
+ if (stream && onTextChunk) {
3366
+ await onTextChunk(chunk);
3367
+ }
3368
+ }
3369
+ if (remainder) {
3370
+ const dataLines = parseSseDataLines(remainder);
3371
+ await consumeSseDataLines(dataLines);
3372
+ }
3373
+ await notifyFinished();
3374
+ return {
3375
+ text,
3376
+ type: 'success'
3487
3377
  };
3488
3378
  };
3489
- const getBasicChatTools = () => {
3490
- return [getReadFileTool(), getWriteFileTool(), getListFilesTool(), getGetWorkspaceUriTool(), getRenderHtmlTool()];
3379
+
3380
+ const normalizeLimitInfo = value => {
3381
+ if (!value || typeof value !== 'object') {
3382
+ return undefined;
3383
+ }
3384
+ const limitRemaining = Reflect.get(value, 'limitRemaining');
3385
+ const limitReset = Reflect.get(value, 'limitReset');
3386
+ const retryAfter = Reflect.get(value, 'retryAfter');
3387
+ const usage = Reflect.get(value, 'usage');
3388
+ const usageDaily = Reflect.get(value, 'usageDaily');
3389
+ const normalized = {
3390
+ ...(typeof limitRemaining === 'number' || limitRemaining === null ? {
3391
+ limitRemaining
3392
+ } : {}),
3393
+ ...(typeof limitReset === 'string' || limitReset === null ? {
3394
+ limitReset
3395
+ } : {}),
3396
+ ...(typeof retryAfter === 'string' || retryAfter === null ? {
3397
+ retryAfter
3398
+ } : {}),
3399
+ ...(typeof usage === 'number' ? {
3400
+ usage
3401
+ } : {}),
3402
+ ...(typeof usageDaily === 'number' ? {
3403
+ usageDaily
3404
+ } : {})
3405
+ };
3406
+ const hasDetails = typeof limitRemaining === 'number' || limitRemaining === null || typeof limitReset === 'string' || limitReset === null || typeof retryAfter === 'string' || retryAfter === null || typeof usage === 'number' || typeof usageDaily === 'number';
3407
+ return hasDetails ? normalized : undefined;
3491
3408
  };
3492
3409
 
3493
- const getClientRequestIdHeader = () => {
3410
+ const normalizeMockResult = value => {
3411
+ if (typeof value === 'string') {
3412
+ return {
3413
+ text: value,
3414
+ type: 'success'
3415
+ };
3416
+ }
3417
+ if (!value || typeof value !== 'object') {
3418
+ return {
3419
+ details: 'request-failed',
3420
+ type: 'error'
3421
+ };
3422
+ }
3423
+ const type = Reflect.get(value, 'type');
3424
+ if (type === 'success') {
3425
+ const text = Reflect.get(value, 'text');
3426
+ if (typeof text === 'string') {
3427
+ return {
3428
+ text,
3429
+ type: 'success'
3430
+ };
3431
+ }
3432
+ return {
3433
+ details: 'request-failed',
3434
+ type: 'error'
3435
+ };
3436
+ }
3437
+ if (type === 'error') {
3438
+ const details = Reflect.get(value, 'details');
3439
+ if (details === 'request-failed' || details === 'too-many-requests' || details === 'http-error') {
3440
+ const rawMessage = Reflect.get(value, 'rawMessage');
3441
+ const statusCode = Reflect.get(value, 'statusCode');
3442
+ const limitInfo = normalizeLimitInfo(Reflect.get(value, 'limitInfo'));
3443
+ return {
3444
+ details,
3445
+ ...(limitInfo ? {
3446
+ limitInfo
3447
+ } : {}),
3448
+ ...(typeof rawMessage === 'string' ? {
3449
+ rawMessage
3450
+ } : {}),
3451
+ ...(typeof statusCode === 'number' ? {
3452
+ statusCode
3453
+ } : {}),
3454
+ type: 'error'
3455
+ };
3456
+ }
3457
+ }
3458
+ const text = Reflect.get(value, 'text');
3459
+ if (typeof text === 'string') {
3460
+ return {
3461
+ text,
3462
+ type: 'success'
3463
+ };
3464
+ }
3494
3465
  return {
3495
- 'x-client-request-id': crypto.randomUUID()
3466
+ details: 'request-failed',
3467
+ type: 'error'
3496
3468
  };
3497
3469
  };
3498
3470
 
3471
+ const getMockOpenRouterAssistantText = async (messages, modelId, openRouterApiBaseUrl, openRouterApiKey, mockApiCommandId, assetDir, platform) => {
3472
+ if (!mockApiCommandId) {
3473
+ return {
3474
+ details: 'request-failed',
3475
+ type: 'error'
3476
+ };
3477
+ }
3478
+ try {
3479
+ const result = await executeProvider({
3480
+ assetDir,
3481
+ event: `onCommand:${mockApiCommandId}`,
3482
+ method: CommandExecute,
3483
+ noProviderFoundMessage: 'No mock api command found',
3484
+ params: [mockApiCommandId, {
3485
+ messages,
3486
+ modelId,
3487
+ openRouterApiBaseUrl,
3488
+ openRouterApiKey
3489
+ }],
3490
+ platform
3491
+ });
3492
+ return normalizeMockResult(result);
3493
+ } catch {
3494
+ return {
3495
+ details: 'request-failed',
3496
+ type: 'error'
3497
+ };
3498
+ }
3499
+ };
3500
+
3499
3501
  const getOpenApiApiEndpoint = openApiApiBaseUrl => {
3500
3502
  return `${openApiApiBaseUrl}/responses`;
3501
3503
  };
3502
3504
 
3505
+ const makeApiRequest = async options => {
3506
+ return invoke$2('ChatNetwork.makeApiRequest', options);
3507
+ };
3508
+ const makeStreamingApiRequest = async options => {
3509
+ return invoke$2('ChatNetwork.makeStreamingApiRequest', options);
3510
+ };
3511
+
3503
3512
  const getTextContent = content => {
3504
3513
  if (typeof content === 'string') {
3505
3514
  return content;
@@ -3521,6 +3530,8 @@ const getTextContent = content => {
3521
3530
  return textParts.join('\n');
3522
3531
  };
3523
3532
 
3533
+ /* eslint-disable @typescript-eslint/prefer-readonly-parameter-types */
3534
+
3524
3535
  const getOpenAiTools = tools => {
3525
3536
  return tools.map(tool => {
3526
3537
  if (!tool || typeof tool !== 'object') {
@@ -4028,15 +4039,44 @@ const parseOpenApiStream = async (response, onTextChunk, onToolCallsChunk, onDat
4028
4039
  ...(responseId ? {
4029
4040
  responseId
4030
4041
  } : {}),
4031
- responseFunctionCalls,
4032
- text,
4033
- type: 'success'
4042
+ responseFunctionCalls,
4043
+ text,
4044
+ type: 'success'
4045
+ };
4046
+ };
4047
+ const getOpenApiErrorDetails = async response => {
4048
+ let parsed;
4049
+ try {
4050
+ parsed = await response.json();
4051
+ } catch {
4052
+ return {};
4053
+ }
4054
+ if (!parsed || typeof parsed !== 'object') {
4055
+ return {};
4056
+ }
4057
+ const error = Reflect.get(parsed, 'error');
4058
+ if (!error || typeof error !== 'object') {
4059
+ return {};
4060
+ }
4061
+ const errorCode = Reflect.get(error, 'code');
4062
+ const errorMessage = Reflect.get(error, 'message');
4063
+ const errorType = Reflect.get(error, 'type');
4064
+ return {
4065
+ ...(typeof errorCode === 'string' ? {
4066
+ errorCode
4067
+ } : {}),
4068
+ ...(typeof errorMessage === 'string' ? {
4069
+ errorMessage
4070
+ } : {}),
4071
+ ...(typeof errorType === 'string' ? {
4072
+ errorType
4073
+ } : {})
4034
4074
  };
4035
4075
  };
4036
- const getOpenApiErrorDetails = async response => {
4076
+ const getOpenApiErrorDetailsFromResponseText = responseText => {
4037
4077
  let parsed;
4038
4078
  try {
4039
- parsed = await response.json();
4079
+ parsed = JSON.parse(responseText);
4040
4080
  } catch {
4041
4081
  return {};
4042
4082
  }
@@ -4062,6 +4102,23 @@ const getOpenApiErrorDetails = async response => {
4062
4102
  } : {})
4063
4103
  };
4064
4104
  };
4105
+ const getResponseFromSseEvents = events => {
4106
+ const chunks = events.map(event => {
4107
+ const data = typeof event === 'string' ? event : JSON.stringify(event);
4108
+ return `data: ${data}\n\n`;
4109
+ });
4110
+ const stream = new ReadableStream({
4111
+ start(controller) {
4112
+ for (const chunk of chunks) {
4113
+ controller.enqueue(new TextEncoder().encode(chunk));
4114
+ }
4115
+ controller.close();
4116
+ }
4117
+ });
4118
+ return {
4119
+ body: stream
4120
+ };
4121
+ };
4065
4122
  const getOpenApiAssistantText = async (messages, modelId, openApiApiKey, openApiApiBaseUrl, assetDir, platform, options) => {
4066
4123
  const {
4067
4124
  includeObfuscation = false,
@@ -4070,6 +4127,7 @@ const getOpenApiAssistantText = async (messages, modelId, openApiApiKey, openApi
4070
4127
  onTextChunk,
4071
4128
  onToolCallsChunk,
4072
4129
  stream,
4130
+ useChatNetworkWorkerForRequests = false,
4073
4131
  webSearchEnabled = false
4074
4132
  } = options ?? {
4075
4133
  stream: false
@@ -4082,46 +4140,89 @@ const getOpenApiAssistantText = async (messages, modelId, openApiApiKey, openApi
4082
4140
  const maxToolIterations = 4;
4083
4141
  let previousResponseId;
4084
4142
  for (let i = 0; i <= maxToolIterations; i++) {
4085
- let response;
4086
- try {
4087
- response = await fetch(getOpenApiApiEndpoint(openApiApiBaseUrl), {
4088
- body: JSON.stringify(getOpenAiParams(openAiInput, modelId, stream, includeObfuscation, tools, webSearchEnabled, previousResponseId)),
4089
- headers: {
4090
- Authorization: `Bearer ${openApiApiKey}`,
4091
- 'Content-Type': 'application/json',
4092
- ...getClientRequestIdHeader()
4093
- },
4094
- method: 'POST'
4095
- });
4096
- } catch {
4097
- return {
4098
- details: 'request-failed',
4099
- type: 'error'
4100
- };
4101
- }
4102
- if (!response.ok) {
4103
- const {
4104
- errorCode,
4105
- errorMessage,
4106
- errorType
4107
- } = await getOpenApiErrorDetails(response);
4108
- return {
4109
- details: 'http-error',
4110
- ...(errorCode ? {
4111
- errorCode
4112
- } : {}),
4113
- ...(errorMessage ? {
4114
- errorMessage
4115
- } : {}),
4116
- ...(errorType ? {
4117
- errorType
4118
- } : {}),
4119
- statusCode: response.status,
4120
- type: 'error'
4121
- };
4122
- }
4143
+ const postBody = getOpenAiParams(openAiInput, modelId, stream, includeObfuscation, tools, webSearchEnabled, previousResponseId);
4123
4144
  if (stream) {
4124
- const streamResult = await parseOpenApiStream(response, onTextChunk, onToolCallsChunk, onDataEvent);
4145
+ const streamResult = useChatNetworkWorkerForRequests ? await (async () => {
4146
+ const requestResult = await makeStreamingApiRequest({
4147
+ headers: {
4148
+ Authorization: `Bearer ${openApiApiKey}`,
4149
+ 'Content-Type': 'application/json',
4150
+ ...getClientRequestIdHeader()
4151
+ },
4152
+ method: 'POST',
4153
+ postBody,
4154
+ url: getOpenApiApiEndpoint(openApiApiBaseUrl)
4155
+ });
4156
+ if (requestResult.type === 'error') {
4157
+ if (requestResult.statusCode === 0) {
4158
+ return {
4159
+ details: 'request-failed',
4160
+ type: 'error'
4161
+ };
4162
+ }
4163
+ const {
4164
+ errorCode,
4165
+ errorMessage,
4166
+ errorType
4167
+ } = getOpenApiErrorDetailsFromResponseText(requestResult.response);
4168
+ return {
4169
+ details: 'http-error',
4170
+ ...(errorCode ? {
4171
+ errorCode
4172
+ } : {}),
4173
+ ...(errorMessage ? {
4174
+ errorMessage
4175
+ } : {}),
4176
+ ...(errorType ? {
4177
+ errorType
4178
+ } : {}),
4179
+ statusCode: requestResult.statusCode,
4180
+ type: 'error'
4181
+ };
4182
+ }
4183
+ const response = getResponseFromSseEvents(requestResult.body);
4184
+ return parseOpenApiStream(response, onTextChunk, onToolCallsChunk, onDataEvent);
4185
+ })() : await (async () => {
4186
+ let response;
4187
+ try {
4188
+ response = await fetch(getOpenApiApiEndpoint(openApiApiBaseUrl), {
4189
+ body: JSON.stringify(postBody),
4190
+ headers: {
4191
+ Authorization: `Bearer ${openApiApiKey}`,
4192
+ 'Content-Type': 'application/json',
4193
+ ...getClientRequestIdHeader()
4194
+ },
4195
+ method: 'POST'
4196
+ });
4197
+ } catch {
4198
+ return {
4199
+ details: 'request-failed',
4200
+ type: 'error'
4201
+ };
4202
+ }
4203
+ if (!response.ok) {
4204
+ const {
4205
+ errorCode,
4206
+ errorMessage,
4207
+ errorType
4208
+ } = await getOpenApiErrorDetails(response);
4209
+ return {
4210
+ details: 'http-error',
4211
+ ...(errorCode ? {
4212
+ errorCode
4213
+ } : {}),
4214
+ ...(errorMessage ? {
4215
+ errorMessage
4216
+ } : {}),
4217
+ ...(errorType ? {
4218
+ errorType
4219
+ } : {}),
4220
+ statusCode: response.status,
4221
+ type: 'error'
4222
+ };
4223
+ }
4224
+ return parseOpenApiStream(response, onTextChunk, onToolCallsChunk, onDataEvent);
4225
+ })();
4125
4226
  if (streamResult.type !== 'success') {
4126
4227
  return streamResult;
4127
4228
  }
@@ -4168,13 +4269,92 @@ const getOpenApiAssistantText = async (messages, modelId, openApiApiKey, openApi
4168
4269
  };
4169
4270
  }
4170
4271
  let parsed;
4171
- try {
4172
- parsed = await response.json();
4173
- } catch {
4174
- return {
4175
- details: 'request-failed',
4176
- type: 'error'
4177
- };
4272
+ if (useChatNetworkWorkerForRequests) {
4273
+ const requestResult = await makeApiRequest({
4274
+ headers: {
4275
+ Authorization: `Bearer ${openApiApiKey}`,
4276
+ 'Content-Type': 'application/json',
4277
+ ...getClientRequestIdHeader()
4278
+ },
4279
+ method: 'POST',
4280
+ postBody,
4281
+ url: getOpenApiApiEndpoint(openApiApiBaseUrl)
4282
+ });
4283
+ if (requestResult.type === 'error') {
4284
+ if (requestResult.statusCode === 0) {
4285
+ return {
4286
+ details: 'request-failed',
4287
+ type: 'error'
4288
+ };
4289
+ }
4290
+ const {
4291
+ errorCode,
4292
+ errorMessage,
4293
+ errorType
4294
+ } = getOpenApiErrorDetailsFromResponseText(requestResult.response);
4295
+ return {
4296
+ details: 'http-error',
4297
+ ...(errorCode ? {
4298
+ errorCode
4299
+ } : {}),
4300
+ ...(errorMessage ? {
4301
+ errorMessage
4302
+ } : {}),
4303
+ ...(errorType ? {
4304
+ errorType
4305
+ } : {}),
4306
+ statusCode: requestResult.statusCode,
4307
+ type: 'error'
4308
+ };
4309
+ }
4310
+ parsed = requestResult.body;
4311
+ } else {
4312
+ let response;
4313
+ try {
4314
+ response = await fetch(getOpenApiApiEndpoint(openApiApiBaseUrl), {
4315
+ body: JSON.stringify(postBody),
4316
+ headers: {
4317
+ Authorization: `Bearer ${openApiApiKey}`,
4318
+ 'Content-Type': 'application/json',
4319
+ ...getClientRequestIdHeader()
4320
+ },
4321
+ method: 'POST'
4322
+ });
4323
+ } catch {
4324
+ return {
4325
+ details: 'request-failed',
4326
+ type: 'error'
4327
+ };
4328
+ }
4329
+ if (!response.ok) {
4330
+ const {
4331
+ errorCode,
4332
+ errorMessage,
4333
+ errorType
4334
+ } = await getOpenApiErrorDetails(response);
4335
+ return {
4336
+ details: 'http-error',
4337
+ ...(errorCode ? {
4338
+ errorCode
4339
+ } : {}),
4340
+ ...(errorMessage ? {
4341
+ errorMessage
4342
+ } : {}),
4343
+ ...(errorType ? {
4344
+ errorType
4345
+ } : {}),
4346
+ statusCode: response.status,
4347
+ type: 'error'
4348
+ };
4349
+ }
4350
+ try {
4351
+ parsed = await response.json();
4352
+ } catch {
4353
+ return {
4354
+ details: 'request-failed',
4355
+ type: 'error'
4356
+ };
4357
+ }
4178
4358
  }
4179
4359
  if (!parsed || typeof parsed !== 'object') {
4180
4360
  return {
@@ -4409,28 +4589,67 @@ const getOpenRouterRaw429Message = async response => {
4409
4589
  }
4410
4590
  return raw;
4411
4591
  };
4412
- const getOpenRouterLimitInfo = async (openRouterApiKey, openRouterApiBaseUrl) => {
4413
- let response;
4592
+ const getOpenRouterRaw429MessageFromText = responseText => {
4593
+ let parsed;
4414
4594
  try {
4415
- response = await fetch(getOpenRouterKeyEndpoint(openRouterApiBaseUrl), {
4416
- headers: {
4417
- Authorization: `Bearer ${openRouterApiKey}`,
4418
- ...getClientRequestIdHeader()
4419
- },
4420
- method: 'GET'
4421
- });
4595
+ parsed = JSON.parse(responseText);
4422
4596
  } catch {
4423
4597
  return undefined;
4424
4598
  }
4425
- if (!response.ok) {
4599
+ if (!parsed || typeof parsed !== 'object') {
4426
4600
  return undefined;
4427
4601
  }
4428
- let parsed;
4429
- try {
4430
- parsed = await response.json();
4431
- } catch {
4602
+ const error = Reflect.get(parsed, 'error');
4603
+ if (!error || typeof error !== 'object') {
4604
+ return undefined;
4605
+ }
4606
+ const metadata = Reflect.get(error, 'metadata');
4607
+ if (!metadata || typeof metadata !== 'object') {
4608
+ return undefined;
4609
+ }
4610
+ const raw = Reflect.get(metadata, 'raw');
4611
+ if (typeof raw !== 'string' || !raw) {
4432
4612
  return undefined;
4433
4613
  }
4614
+ return raw;
4615
+ };
4616
+ const getOpenRouterLimitInfo = async (openRouterApiKey, openRouterApiBaseUrl, useChatNetworkWorkerForRequests) => {
4617
+ let parsed;
4618
+ if (useChatNetworkWorkerForRequests) {
4619
+ const result = await makeApiRequest({
4620
+ headers: {
4621
+ Authorization: `Bearer ${openRouterApiKey}`,
4622
+ ...getClientRequestIdHeader()
4623
+ },
4624
+ method: 'GET',
4625
+ url: getOpenRouterKeyEndpoint(openRouterApiBaseUrl)
4626
+ });
4627
+ if (result.type === 'error') {
4628
+ return undefined;
4629
+ }
4630
+ parsed = result.body;
4631
+ } else {
4632
+ let response;
4633
+ try {
4634
+ response = await fetch(getOpenRouterKeyEndpoint(openRouterApiBaseUrl), {
4635
+ headers: {
4636
+ Authorization: `Bearer ${openRouterApiKey}`,
4637
+ ...getClientRequestIdHeader()
4638
+ },
4639
+ method: 'GET'
4640
+ });
4641
+ } catch {
4642
+ return undefined;
4643
+ }
4644
+ if (!response.ok) {
4645
+ return undefined;
4646
+ }
4647
+ try {
4648
+ parsed = await response.json();
4649
+ } catch {
4650
+ return undefined;
4651
+ }
4652
+ }
4434
4653
  if (!parsed || typeof parsed !== 'object') {
4435
4654
  return undefined;
4436
4655
  }
@@ -4462,7 +4681,7 @@ const getOpenRouterLimitInfo = async (openRouterApiKey, openRouterApiBaseUrl) =>
4462
4681
  }
4463
4682
  return normalizedLimitInfo;
4464
4683
  };
4465
- const getOpenRouterAssistantText = async (messages, modelId, openRouterApiKey, openRouterApiBaseUrl, assetDir, platform) => {
4684
+ const getOpenRouterAssistantText = async (messages, modelId, openRouterApiKey, openRouterApiBaseUrl, assetDir, platform, useChatNetworkWorkerForRequests = false) => {
4466
4685
  const completionMessages = messages.map(message => ({
4467
4686
  content: message.text,
4468
4687
  role: message.role
@@ -4470,64 +4689,111 @@ const getOpenRouterAssistantText = async (messages, modelId, openRouterApiKey, o
4470
4689
  const tools = getBasicChatTools();
4471
4690
  const maxToolIterations = 4;
4472
4691
  for (let i = 0; i <= maxToolIterations; i++) {
4473
- let response;
4474
- try {
4475
- response = await fetch(getOpenRouterApiEndpoint(openRouterApiBaseUrl), {
4476
- body: JSON.stringify({
4477
- messages: completionMessages,
4478
- model: modelId,
4479
- tool_choice: 'auto',
4480
- tools
4481
- }),
4692
+ let parsed;
4693
+ if (useChatNetworkWorkerForRequests) {
4694
+ const requestResult = await makeApiRequest({
4482
4695
  headers: {
4483
4696
  Authorization: `Bearer ${openRouterApiKey}`,
4484
4697
  'Content-Type': 'application/json',
4485
4698
  ...getClientRequestIdHeader()
4486
4699
  },
4487
- method: 'POST'
4700
+ method: 'POST',
4701
+ postBody: {
4702
+ messages: completionMessages,
4703
+ model: modelId,
4704
+ tool_choice: 'auto',
4705
+ tools
4706
+ },
4707
+ url: getOpenRouterApiEndpoint(openRouterApiBaseUrl)
4488
4708
  });
4489
- } catch {
4490
- return {
4491
- details: 'request-failed',
4492
- type: 'error'
4493
- };
4494
- }
4495
- if (!response.ok) {
4496
- if (response.status === 429) {
4497
- const retryAfter = response.headers?.get?.('retry-after') ?? null;
4498
- const rawMessage = await getOpenRouterRaw429Message(response);
4499
- const limitInfo = await getOpenRouterLimitInfo(openRouterApiKey, openRouterApiBaseUrl);
4709
+ if (requestResult.type === 'error') {
4710
+ if (requestResult.statusCode === 429) {
4711
+ const retryAfter = requestResult.headers?.['retry-after'] ?? null;
4712
+ const rawMessage = getOpenRouterRaw429MessageFromText(requestResult.response);
4713
+ const limitInfo = await getOpenRouterLimitInfo(openRouterApiKey, openRouterApiBaseUrl, useChatNetworkWorkerForRequests);
4714
+ return {
4715
+ details: 'too-many-requests',
4716
+ ...(limitInfo || retryAfter ? {
4717
+ limitInfo: {
4718
+ ...limitInfo,
4719
+ ...(retryAfter ? {
4720
+ retryAfter
4721
+ } : {})
4722
+ }
4723
+ } : {}),
4724
+ ...(rawMessage ? {
4725
+ rawMessage
4726
+ } : {}),
4727
+ statusCode: 429,
4728
+ type: 'error'
4729
+ };
4730
+ }
4500
4731
  return {
4501
- details: 'too-many-requests',
4502
- ...(limitInfo || retryAfter ? {
4503
- limitInfo: {
4504
- ...limitInfo,
4505
- ...(retryAfter ? {
4506
- retryAfter
4507
- } : {})
4508
- }
4509
- } : {}),
4510
- ...(rawMessage ? {
4511
- rawMessage
4512
- } : {}),
4513
- statusCode: 429,
4732
+ details: 'http-error',
4733
+ statusCode: requestResult.statusCode,
4734
+ type: 'error'
4735
+ };
4736
+ }
4737
+ parsed = requestResult.body;
4738
+ } else {
4739
+ let response;
4740
+ try {
4741
+ response = await fetch(getOpenRouterApiEndpoint(openRouterApiBaseUrl), {
4742
+ body: JSON.stringify({
4743
+ messages: completionMessages,
4744
+ model: modelId,
4745
+ tool_choice: 'auto',
4746
+ tools
4747
+ }),
4748
+ headers: {
4749
+ Authorization: `Bearer ${openRouterApiKey}`,
4750
+ 'Content-Type': 'application/json',
4751
+ ...getClientRequestIdHeader()
4752
+ },
4753
+ method: 'POST'
4754
+ });
4755
+ } catch {
4756
+ return {
4757
+ details: 'request-failed',
4758
+ type: 'error'
4759
+ };
4760
+ }
4761
+ if (!response.ok) {
4762
+ if (response.status === 429) {
4763
+ const retryAfter = response.headers?.get?.('retry-after') ?? null;
4764
+ const rawMessage = await getOpenRouterRaw429Message(response);
4765
+ const limitInfo = await getOpenRouterLimitInfo(openRouterApiKey, openRouterApiBaseUrl, useChatNetworkWorkerForRequests);
4766
+ return {
4767
+ details: 'too-many-requests',
4768
+ ...(limitInfo || retryAfter ? {
4769
+ limitInfo: {
4770
+ ...limitInfo,
4771
+ ...(retryAfter ? {
4772
+ retryAfter
4773
+ } : {})
4774
+ }
4775
+ } : {}),
4776
+ ...(rawMessage ? {
4777
+ rawMessage
4778
+ } : {}),
4779
+ statusCode: 429,
4780
+ type: 'error'
4781
+ };
4782
+ }
4783
+ return {
4784
+ details: 'http-error',
4785
+ statusCode: response.status,
4786
+ type: 'error'
4787
+ };
4788
+ }
4789
+ try {
4790
+ parsed = await response.json();
4791
+ } catch {
4792
+ return {
4793
+ details: 'request-failed',
4514
4794
  type: 'error'
4515
4795
  };
4516
4796
  }
4517
- return {
4518
- details: 'http-error',
4519
- statusCode: response.status,
4520
- type: 'error'
4521
- };
4522
- }
4523
- let parsed;
4524
- try {
4525
- parsed = await response.json();
4526
- } catch {
4527
- return {
4528
- details: 'request-failed',
4529
- type: 'error'
4530
- };
4531
4797
  }
4532
4798
  if (!parsed || typeof parsed !== 'object') {
4533
4799
  return {
@@ -4667,6 +4933,17 @@ const isOpenRouterModel = (selectedModelId, models) => {
4667
4933
  return selectedModelId.toLowerCase().startsWith('openrouter/');
4668
4934
  };
4669
4935
 
4936
+ let requests = [];
4937
+ const reset$1 = () => {
4938
+ requests = [];
4939
+ };
4940
+ const capture = request => {
4941
+ requests = [...requests, request];
4942
+ };
4943
+ const getAll = () => {
4944
+ return requests;
4945
+ };
4946
+
4670
4947
  const getAiResponse = async ({
4671
4948
  assetDir,
4672
4949
  messageId,
@@ -4686,6 +4963,7 @@ const getAiResponse = async ({
4686
4963
  platform,
4687
4964
  selectedModelId,
4688
4965
  streamingEnabled = true,
4966
+ useChatNetworkWorkerForRequests = false,
4689
4967
  useMockApi,
4690
4968
  userText,
4691
4969
  webSearchEnabled = false
@@ -4695,6 +4973,22 @@ const getAiResponse = async ({
4695
4973
  const usesOpenRouterModel = isOpenRouterModel(selectedModelId, models);
4696
4974
  if (usesOpenApiModel) {
4697
4975
  if (useMockApi) {
4976
+ const openAiInput = messages.map(message => ({
4977
+ content: message.text,
4978
+ role: message.role
4979
+ }));
4980
+ const modelId = getOpenApiModelId(selectedModelId);
4981
+ const headers = {
4982
+ Authorization: `Bearer ${openApiApiKey}`,
4983
+ 'Content-Type': 'application/json',
4984
+ ...getClientRequestIdHeader()
4985
+ };
4986
+ capture({
4987
+ headers,
4988
+ method: 'POST',
4989
+ payload: getOpenAiParams(openAiInput, modelId, streamingEnabled, passIncludeObfuscation, getBasicChatTools(), webSearchEnabled),
4990
+ url: getOpenApiApiEndpoint(openApiApiBaseUrl)
4991
+ });
4698
4992
  const result = await getMockOpenApiAssistantText(streamingEnabled, onTextChunk, onToolCallsChunk, onDataEvent, onEventStreamFinished);
4699
4993
  if (result.type === 'success') {
4700
4994
  const {
@@ -4720,6 +5014,7 @@ const getAiResponse = async ({
4720
5014
  onToolCallsChunk
4721
5015
  } : {}),
4722
5016
  stream: streamingEnabled,
5017
+ useChatNetworkWorkerForRequests,
4723
5018
  webSearchEnabled
4724
5019
  });
4725
5020
  if (result.type === 'success') {
@@ -4746,7 +5041,7 @@ const getAiResponse = async ({
4746
5041
  text = getOpenRouterErrorMessage(result);
4747
5042
  }
4748
5043
  } else if (openRouterApiKey) {
4749
- const result = await getOpenRouterAssistantText(messages, modelId, openRouterApiKey, openRouterApiBaseUrl, assetDir, platform);
5044
+ const result = await getOpenRouterAssistantText(messages, modelId, openRouterApiKey, openRouterApiBaseUrl, assetDir, platform, useChatNetworkWorkerForRequests);
4750
5045
  if (result.type === 'success') {
4751
5046
  const {
4752
5047
  text: assistantText
@@ -4830,6 +5125,7 @@ const handleClickSaveOpenApiApiKey = async state => {
4830
5125
  platform: updatedState.platform,
4831
5126
  selectedModelId: updatedState.selectedModelId,
4832
5127
  streamingEnabled: updatedState.streamingEnabled,
5128
+ useChatNetworkWorkerForRequests: updatedState.useChatNetworkWorkerForRequests,
4833
5129
  useMockApi: updatedState.useMockApi,
4834
5130
  userText: previousUserMessage.text
4835
5131
  });
@@ -4910,6 +5206,7 @@ const handleClickSaveOpenRouterApiKey = async state => {
4910
5206
  openRouterApiKey,
4911
5207
  platform: updatedState.platform,
4912
5208
  selectedModelId: updatedState.selectedModelId,
5209
+ useChatNetworkWorkerForRequests: updatedState.useChatNetworkWorkerForRequests,
4913
5210
  useMockApi: updatedState.useMockApi,
4914
5211
  userText: previousUserMessage.text
4915
5212
  });
@@ -5286,6 +5583,7 @@ Assistant: ${assistantText}`;
5286
5583
  platform: state.platform,
5287
5584
  selectedModelId,
5288
5585
  streamingEnabled: false,
5586
+ useChatNetworkWorkerForRequests: state.useChatNetworkWorkerForRequests,
5289
5587
  useMockApi,
5290
5588
  userText: titlePrompt,
5291
5589
  webSearchEnabled: false
@@ -5313,6 +5611,7 @@ const handleSubmit = async state => {
5313
5611
  selectedSessionId,
5314
5612
  sessions,
5315
5613
  streamingEnabled,
5614
+ useChatNetworkWorkerForRequests,
5316
5615
  useMockApi,
5317
5616
  viewMode,
5318
5617
  webSearchEnabled
@@ -5465,6 +5764,7 @@ const handleSubmit = async state => {
5465
5764
  platform,
5466
5765
  selectedModelId,
5467
5766
  streamingEnabled,
5767
+ useChatNetworkWorkerForRequests,
5468
5768
  useMockApi,
5469
5769
  userText,
5470
5770
  webSearchEnabled
@@ -6105,8 +6405,17 @@ const loadStreamingEnabled = async () => {
6105
6405
  }
6106
6406
  };
6107
6407
 
6408
+ const loadUseChatNetworkWorkerForRequests = async () => {
6409
+ try {
6410
+ const savedUseChatNetworkWorkerForRequests = await get('chatView.useChatNetworkWorkerForRequests');
6411
+ return typeof savedUseChatNetworkWorkerForRequests === 'boolean' ? savedUseChatNetworkWorkerForRequests : false;
6412
+ } catch {
6413
+ return false;
6414
+ }
6415
+ };
6416
+
6108
6417
  const loadPreferences = async () => {
6109
- const [aiSessionTitleGenerationEnabled, composerDropEnabled, openApiApiKey, openRouterApiKey, emitStreamingFunctionCallEvents, streamingEnabled, passIncludeObfuscation] = await Promise.all([loadAiSessionTitleGenerationEnabled(), loadComposerDropEnabled(), loadOpenApiApiKey(), loadOpenRouterApiKey(), loadEmitStreamingFunctionCallEvents(), loadStreamingEnabled(), loadPassIncludeObfuscation()]);
6418
+ const [aiSessionTitleGenerationEnabled, composerDropEnabled, openApiApiKey, openRouterApiKey, emitStreamingFunctionCallEvents, streamingEnabled, passIncludeObfuscation, useChatNetworkWorkerForRequests] = await Promise.all([loadAiSessionTitleGenerationEnabled(), loadComposerDropEnabled(), loadOpenApiApiKey(), loadOpenRouterApiKey(), loadEmitStreamingFunctionCallEvents(), loadStreamingEnabled(), loadPassIncludeObfuscation(), loadUseChatNetworkWorkerForRequests()]);
6110
6419
  return {
6111
6420
  aiSessionTitleGenerationEnabled,
6112
6421
  composerDropEnabled,
@@ -6114,7 +6423,8 @@ const loadPreferences = async () => {
6114
6423
  openApiApiKey,
6115
6424
  openRouterApiKey,
6116
6425
  passIncludeObfuscation,
6117
- streamingEnabled
6426
+ streamingEnabled,
6427
+ useChatNetworkWorkerForRequests
6118
6428
  };
6119
6429
  };
6120
6430
 
@@ -6151,7 +6461,8 @@ const loadContent = async (state, savedState) => {
6151
6461
  openApiApiKey,
6152
6462
  openRouterApiKey,
6153
6463
  passIncludeObfuscation,
6154
- streamingEnabled
6464
+ streamingEnabled,
6465
+ useChatNetworkWorkerForRequests
6155
6466
  } = await loadPreferences();
6156
6467
  const legacySavedSessions = getSavedSessions(savedState);
6157
6468
  const storedSessions = await listChatSessions();
@@ -6195,10 +6506,20 @@ const loadContent = async (state, savedState) => {
6195
6506
  selectedSessionId,
6196
6507
  sessions,
6197
6508
  streamingEnabled,
6509
+ useChatNetworkWorkerForRequests,
6198
6510
  viewMode
6199
6511
  };
6200
6512
  };
6201
6513
 
6514
+ const mockOpenApiRequestGetAll = _state => {
6515
+ return getAll();
6516
+ };
6517
+
6518
+ const mockOpenApiRequestReset = state => {
6519
+ reset$1();
6520
+ return state;
6521
+ };
6522
+
6202
6523
  const mockOpenApiSetHttpErrorResponse = (state, statusCode, body) => {
6203
6524
  setHttpErrorResponse(statusCode, body);
6204
6525
  return state;
@@ -6215,7 +6536,7 @@ const mockOpenApiStreamPushChunk = (state, chunk) => {
6215
6536
  };
6216
6537
 
6217
6538
  const mockOpenApiStreamReset = state => {
6218
- reset$1();
6539
+ reset$2();
6219
6540
  return state;
6220
6541
  };
6221
6542
 
@@ -6254,7 +6575,7 @@ const openMockSession = async (state, mockSessionId, mockChatMessages) => {
6254
6575
  };
6255
6576
 
6256
6577
  const registerMockResponse = (state, mockResponse) => {
6257
- reset$1();
6578
+ reset$2();
6258
6579
  pushChunk(mockResponse.text);
6259
6580
  finish();
6260
6581
  return state;
@@ -7936,6 +8257,18 @@ const setStreamingEnabled = (state, streamingEnabled) => {
7936
8257
  };
7937
8258
  };
7938
8259
 
8260
+ const setUseChatNetworkWorkerForRequests = async (state, useChatNetworkWorkerForRequests, persist = true) => {
8261
+ if (persist) {
8262
+ await update({
8263
+ 'chatView.useChatNetworkWorkerForRequests': useChatNetworkWorkerForRequests
8264
+ });
8265
+ }
8266
+ return {
8267
+ ...state,
8268
+ useChatNetworkWorkerForRequests
8269
+ };
8270
+ };
8271
+
7939
8272
  const defaultMockApiCommandId = 'ChatE2e.mockApi';
7940
8273
  const useMockApi = (state, value, mockApiCommandId = defaultMockApiCommandId) => {
7941
8274
  if (!value) {
@@ -7985,6 +8318,8 @@ const commandMap = {
7985
8318
  'Chat.initialize': initialize,
7986
8319
  'Chat.loadContent': wrapCommand(loadContent),
7987
8320
  'Chat.loadContent2': wrapCommand(loadContent),
8321
+ 'Chat.mockOpenApiRequestGetAll': wrapGetter(mockOpenApiRequestGetAll),
8322
+ 'Chat.mockOpenApiRequestReset': wrapCommand(mockOpenApiRequestReset),
7988
8323
  'Chat.mockOpenApiSetHttpErrorResponse': wrapCommand(mockOpenApiSetHttpErrorResponse),
7989
8324
  'Chat.mockOpenApiStreamFinish': wrapCommand(mockOpenApiStreamFinish),
7990
8325
  'Chat.mockOpenApiStreamPushChunk': wrapCommand(mockOpenApiStreamPushChunk),
@@ -8001,6 +8336,7 @@ const commandMap = {
8001
8336
  'Chat.setEmitStreamingFunctionCallEvents': wrapCommand(setEmitStreamingFunctionCallEvents),
8002
8337
  'Chat.setOpenRouterApiKey': wrapCommand(setOpenRouterApiKey),
8003
8338
  'Chat.setStreamingEnabled': wrapCommand(setStreamingEnabled),
8339
+ 'Chat.setUseChatNetworkWorkerForRequests': wrapCommand(setUseChatNetworkWorkerForRequests),
8004
8340
  'Chat.terminate': terminate,
8005
8341
  'Chat.useMockApi': wrapCommand(useMockApi)
8006
8342
  };