@lvce-editor/chat-view 2.7.0 → 2.9.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.
- package/dist/chatViewWorkerMain.js +376 -145
- package/package.json +1 -1
|
@@ -1078,6 +1078,7 @@ const {
|
|
|
1078
1078
|
set: set$3
|
|
1079
1079
|
} = create$2(6002);
|
|
1080
1080
|
|
|
1081
|
+
const Div$1 = 4;
|
|
1081
1082
|
const Ol$1 = 49;
|
|
1082
1083
|
|
|
1083
1084
|
const ClientX = 'event.clientX';
|
|
@@ -1512,6 +1513,7 @@ const createDefaultState = () => {
|
|
|
1512
1513
|
useMockApi: false,
|
|
1513
1514
|
viewMode: 'list',
|
|
1514
1515
|
warningCount: 0,
|
|
1516
|
+
webSearchEnabled: true,
|
|
1515
1517
|
width: 0,
|
|
1516
1518
|
x: 0,
|
|
1517
1519
|
y: 0
|
|
@@ -2647,7 +2649,8 @@ const deleteSession = async (state, id) => {
|
|
|
2647
2649
|
};
|
|
2648
2650
|
|
|
2649
2651
|
const handleClickOpenApiApiKeySettings = async state => {
|
|
2650
|
-
|
|
2652
|
+
// Open the built-in settings editor so the user can inspect or edit their OpenAI API key.
|
|
2653
|
+
await invoke('Main.openUri', 'app://settings.json');
|
|
2651
2654
|
return state;
|
|
2652
2655
|
};
|
|
2653
2656
|
|
|
@@ -3051,8 +3054,6 @@ const getMockOpenRouterAssistantText = async (messages, modelId, openRouterApiBa
|
|
|
3051
3054
|
}
|
|
3052
3055
|
};
|
|
3053
3056
|
|
|
3054
|
-
const OnFileSystem = 'onFileSystem';
|
|
3055
|
-
|
|
3056
3057
|
const isPathTraversalAttempt = path => {
|
|
3057
3058
|
if (!path) {
|
|
3058
3059
|
return false;
|
|
@@ -3069,6 +3070,7 @@ const isPathTraversalAttempt = path => {
|
|
|
3069
3070
|
const segments = path.split(/[\\/]/);
|
|
3070
3071
|
return segments.includes('..');
|
|
3071
3072
|
};
|
|
3073
|
+
|
|
3072
3074
|
const normalizeRelativePath = path => {
|
|
3073
3075
|
const segments = path.split(/[\\/]/).filter(segment => segment && segment !== '.');
|
|
3074
3076
|
if (segments.length === 0) {
|
|
@@ -3076,6 +3078,87 @@ const normalizeRelativePath = path => {
|
|
|
3076
3078
|
}
|
|
3077
3079
|
return segments.join('/');
|
|
3078
3080
|
};
|
|
3081
|
+
|
|
3082
|
+
const executeListFilesTool = async (args, _options) => {
|
|
3083
|
+
const folderPath = typeof args.path === 'string' && args.path ? args.path : '.';
|
|
3084
|
+
if (isPathTraversalAttempt(folderPath)) {
|
|
3085
|
+
return JSON.stringify({
|
|
3086
|
+
error: 'Access denied: path must be relative and stay within the open workspace folder.'
|
|
3087
|
+
});
|
|
3088
|
+
}
|
|
3089
|
+
const normalizedPath = normalizeRelativePath(folderPath);
|
|
3090
|
+
try {
|
|
3091
|
+
const entries = await invoke('FileSystem.readDirWithFileTypes', normalizedPath);
|
|
3092
|
+
return JSON.stringify({
|
|
3093
|
+
entries,
|
|
3094
|
+
path: normalizedPath
|
|
3095
|
+
});
|
|
3096
|
+
} catch (error) {
|
|
3097
|
+
return JSON.stringify({
|
|
3098
|
+
error: String(error),
|
|
3099
|
+
path: normalizedPath
|
|
3100
|
+
});
|
|
3101
|
+
}
|
|
3102
|
+
};
|
|
3103
|
+
|
|
3104
|
+
const executeReadFileTool = async (args, _options) => {
|
|
3105
|
+
const filePath = typeof args.path === 'string' ? args.path : '';
|
|
3106
|
+
if (!filePath || isPathTraversalAttempt(filePath)) {
|
|
3107
|
+
return JSON.stringify({
|
|
3108
|
+
error: 'Access denied: path must be relative and stay within the open workspace folder.'
|
|
3109
|
+
});
|
|
3110
|
+
}
|
|
3111
|
+
const normalizedPath = normalizeRelativePath(filePath);
|
|
3112
|
+
try {
|
|
3113
|
+
const content = await readFile(normalizedPath);
|
|
3114
|
+
return JSON.stringify({
|
|
3115
|
+
content,
|
|
3116
|
+
path: normalizedPath
|
|
3117
|
+
});
|
|
3118
|
+
} catch (error) {
|
|
3119
|
+
return JSON.stringify({
|
|
3120
|
+
error: String(error),
|
|
3121
|
+
path: normalizedPath
|
|
3122
|
+
});
|
|
3123
|
+
}
|
|
3124
|
+
};
|
|
3125
|
+
|
|
3126
|
+
const OnFileSystem = 'onFileSystem';
|
|
3127
|
+
|
|
3128
|
+
const executeFileSystemCommand = async (method, params, options) => {
|
|
3129
|
+
return executeProvider({
|
|
3130
|
+
assetDir: options.assetDir,
|
|
3131
|
+
event: OnFileSystem,
|
|
3132
|
+
method,
|
|
3133
|
+
noProviderFoundMessage: 'No file system provider found',
|
|
3134
|
+
params,
|
|
3135
|
+
platform: options.platform
|
|
3136
|
+
});
|
|
3137
|
+
};
|
|
3138
|
+
|
|
3139
|
+
const executeWriteFileTool = async (args, options) => {
|
|
3140
|
+
const filePath = typeof args.path === 'string' ? args.path : '';
|
|
3141
|
+
const content = typeof args.content === 'string' ? args.content : '';
|
|
3142
|
+
if (!filePath || isPathTraversalAttempt(filePath)) {
|
|
3143
|
+
return JSON.stringify({
|
|
3144
|
+
error: 'Access denied: path must be relative and stay within the open workspace folder.'
|
|
3145
|
+
});
|
|
3146
|
+
}
|
|
3147
|
+
const normalizedPath = normalizeRelativePath(filePath);
|
|
3148
|
+
try {
|
|
3149
|
+
await executeFileSystemCommand(FileSystemWriteFile, ['file', normalizedPath, content], options);
|
|
3150
|
+
return JSON.stringify({
|
|
3151
|
+
ok: true,
|
|
3152
|
+
path: normalizedPath
|
|
3153
|
+
});
|
|
3154
|
+
} catch (error) {
|
|
3155
|
+
return JSON.stringify({
|
|
3156
|
+
error: String(error),
|
|
3157
|
+
path: normalizedPath
|
|
3158
|
+
});
|
|
3159
|
+
}
|
|
3160
|
+
};
|
|
3161
|
+
|
|
3079
3162
|
const parseToolArguments = rawArguments => {
|
|
3080
3163
|
if (typeof rawArguments !== 'string') {
|
|
3081
3164
|
return {};
|
|
@@ -3090,16 +3173,23 @@ const parseToolArguments = rawArguments => {
|
|
|
3090
3173
|
return {};
|
|
3091
3174
|
}
|
|
3092
3175
|
};
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3176
|
+
|
|
3177
|
+
const executeChatTool = async (name, rawArguments, options) => {
|
|
3178
|
+
const args = parseToolArguments(rawArguments);
|
|
3179
|
+
if (name === 'read_file') {
|
|
3180
|
+
return executeReadFileTool(args);
|
|
3181
|
+
}
|
|
3182
|
+
if (name === 'write_file') {
|
|
3183
|
+
return executeWriteFileTool(args, options);
|
|
3184
|
+
}
|
|
3185
|
+
if (name === 'list_files') {
|
|
3186
|
+
return executeListFilesTool(args);
|
|
3187
|
+
}
|
|
3188
|
+
return JSON.stringify({
|
|
3189
|
+
error: `Unknown tool: ${name}`
|
|
3101
3190
|
});
|
|
3102
3191
|
};
|
|
3192
|
+
|
|
3103
3193
|
const getBasicChatTools = () => {
|
|
3104
3194
|
return [{
|
|
3105
3195
|
function: {
|
|
@@ -3157,76 +3247,6 @@ const getBasicChatTools = () => {
|
|
|
3157
3247
|
type: 'function'
|
|
3158
3248
|
}];
|
|
3159
3249
|
};
|
|
3160
|
-
const executeChatTool = async (name, rawArguments, options) => {
|
|
3161
|
-
const args = parseToolArguments(rawArguments);
|
|
3162
|
-
if (name === 'read_file') {
|
|
3163
|
-
const filePath = typeof args.path === 'string' ? args.path : '';
|
|
3164
|
-
if (!filePath || isPathTraversalAttempt(filePath)) {
|
|
3165
|
-
return JSON.stringify({
|
|
3166
|
-
error: 'Access denied: path must be relative and stay within the open workspace folder.'
|
|
3167
|
-
});
|
|
3168
|
-
}
|
|
3169
|
-
const normalizedPath = normalizeRelativePath(filePath);
|
|
3170
|
-
try {
|
|
3171
|
-
const content = await readFile(normalizedPath);
|
|
3172
|
-
return JSON.stringify({
|
|
3173
|
-
content,
|
|
3174
|
-
path: normalizedPath
|
|
3175
|
-
});
|
|
3176
|
-
} catch (error) {
|
|
3177
|
-
return JSON.stringify({
|
|
3178
|
-
error: String(error),
|
|
3179
|
-
path: normalizedPath
|
|
3180
|
-
});
|
|
3181
|
-
}
|
|
3182
|
-
}
|
|
3183
|
-
if (name === 'write_file') {
|
|
3184
|
-
const filePath = typeof args.path === 'string' ? args.path : '';
|
|
3185
|
-
const content = typeof args.content === 'string' ? args.content : '';
|
|
3186
|
-
if (!filePath || isPathTraversalAttempt(filePath)) {
|
|
3187
|
-
return JSON.stringify({
|
|
3188
|
-
error: 'Access denied: path must be relative and stay within the open workspace folder.'
|
|
3189
|
-
});
|
|
3190
|
-
}
|
|
3191
|
-
const normalizedPath = normalizeRelativePath(filePath);
|
|
3192
|
-
try {
|
|
3193
|
-
await executeFileSystemCommand(FileSystemWriteFile, ['file', normalizedPath, content], options);
|
|
3194
|
-
return JSON.stringify({
|
|
3195
|
-
ok: true,
|
|
3196
|
-
path: normalizedPath
|
|
3197
|
-
});
|
|
3198
|
-
} catch (error) {
|
|
3199
|
-
return JSON.stringify({
|
|
3200
|
-
error: String(error),
|
|
3201
|
-
path: normalizedPath
|
|
3202
|
-
});
|
|
3203
|
-
}
|
|
3204
|
-
}
|
|
3205
|
-
if (name === 'list_files') {
|
|
3206
|
-
const folderPath = typeof args.path === 'string' && args.path ? args.path : '.';
|
|
3207
|
-
if (isPathTraversalAttempt(folderPath)) {
|
|
3208
|
-
return JSON.stringify({
|
|
3209
|
-
error: 'Access denied: path must be relative and stay within the open workspace folder.'
|
|
3210
|
-
});
|
|
3211
|
-
}
|
|
3212
|
-
const normalizedPath = normalizeRelativePath(folderPath);
|
|
3213
|
-
try {
|
|
3214
|
-
const entries = await invoke('FileSystem.readDirWithFileTypes', normalizedPath);
|
|
3215
|
-
return JSON.stringify({
|
|
3216
|
-
entries,
|
|
3217
|
-
path: normalizedPath
|
|
3218
|
-
});
|
|
3219
|
-
} catch (error) {
|
|
3220
|
-
return JSON.stringify({
|
|
3221
|
-
error: String(error),
|
|
3222
|
-
path: normalizedPath
|
|
3223
|
-
});
|
|
3224
|
-
}
|
|
3225
|
-
}
|
|
3226
|
-
return JSON.stringify({
|
|
3227
|
-
error: `Unknown tool: ${name}`
|
|
3228
|
-
});
|
|
3229
|
-
};
|
|
3230
3250
|
|
|
3231
3251
|
const getClientRequestIdHeader = () => {
|
|
3232
3252
|
return {
|
|
@@ -3286,7 +3306,8 @@ const getOpenAiTools = tools => {
|
|
|
3286
3306
|
};
|
|
3287
3307
|
});
|
|
3288
3308
|
};
|
|
3289
|
-
const getOpenAiParams = (input, modelId, stream, includeObfuscation, tools, previousResponseId) => {
|
|
3309
|
+
const getOpenAiParams = (input, modelId, stream, includeObfuscation, tools, webSearchEnabled, previousResponseId) => {
|
|
3310
|
+
const openAiTools = getOpenAiTools(tools);
|
|
3290
3311
|
return {
|
|
3291
3312
|
input,
|
|
3292
3313
|
model: modelId,
|
|
@@ -3302,7 +3323,9 @@ const getOpenAiParams = (input, modelId, stream, includeObfuscation, tools, prev
|
|
|
3302
3323
|
previous_response_id: previousResponseId
|
|
3303
3324
|
} : {}),
|
|
3304
3325
|
tool_choice: 'auto',
|
|
3305
|
-
tools:
|
|
3326
|
+
tools: webSearchEnabled ? [...openAiTools, {
|
|
3327
|
+
type: 'web_search'
|
|
3328
|
+
}] : openAiTools
|
|
3306
3329
|
};
|
|
3307
3330
|
};
|
|
3308
3331
|
const getStreamChunkText = content => {
|
|
@@ -3320,6 +3343,47 @@ const getStreamChunkText = content => {
|
|
|
3320
3343
|
return typeof text === 'string' ? text : '';
|
|
3321
3344
|
}).join('');
|
|
3322
3345
|
};
|
|
3346
|
+
const getShortToolErrorMessage = error => {
|
|
3347
|
+
const trimmed = error.trim().replace(/^Error:\s*/, '');
|
|
3348
|
+
const firstLine = trimmed.split('\n')[0];
|
|
3349
|
+
if (firstLine.length <= 80) {
|
|
3350
|
+
return firstLine;
|
|
3351
|
+
}
|
|
3352
|
+
return `${firstLine.slice(0, 77)}...`;
|
|
3353
|
+
};
|
|
3354
|
+
const getToolCallExecutionStatus = content => {
|
|
3355
|
+
let parsed;
|
|
3356
|
+
try {
|
|
3357
|
+
parsed = JSON.parse(content);
|
|
3358
|
+
} catch {
|
|
3359
|
+
return {
|
|
3360
|
+
errorMessage: 'Invalid tool output',
|
|
3361
|
+
status: 'error'
|
|
3362
|
+
};
|
|
3363
|
+
}
|
|
3364
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
3365
|
+
return {
|
|
3366
|
+
errorMessage: 'Invalid tool output',
|
|
3367
|
+
status: 'error'
|
|
3368
|
+
};
|
|
3369
|
+
}
|
|
3370
|
+
const rawError = Reflect.get(parsed, 'error');
|
|
3371
|
+
if (typeof rawError !== 'string' || !rawError.trim()) {
|
|
3372
|
+
return {
|
|
3373
|
+
status: 'success'
|
|
3374
|
+
};
|
|
3375
|
+
}
|
|
3376
|
+
const errorMessage = getShortToolErrorMessage(rawError);
|
|
3377
|
+
if (/not[\s_-]?found|enoent/i.test(errorMessage)) {
|
|
3378
|
+
return {
|
|
3379
|
+
status: 'not-found'
|
|
3380
|
+
};
|
|
3381
|
+
}
|
|
3382
|
+
return {
|
|
3383
|
+
errorMessage,
|
|
3384
|
+
status: 'error'
|
|
3385
|
+
};
|
|
3386
|
+
};
|
|
3323
3387
|
const getResponseOutputText = parsed => {
|
|
3324
3388
|
if (!parsed || typeof parsed !== 'object') {
|
|
3325
3389
|
return '';
|
|
@@ -3457,12 +3521,29 @@ const updateToolCallAccumulator = (accumulator, chunk) => {
|
|
|
3457
3521
|
};
|
|
3458
3522
|
};
|
|
3459
3523
|
const getResponseFunctionCallsFromStreamingAccumulator = toolCallAccumulator => {
|
|
3460
|
-
return Object.entries(toolCallAccumulator).toSorted((a, b) =>
|
|
3524
|
+
return Object.entries(toolCallAccumulator).toSorted((a, b) => a[0].localeCompare(b[0])).map(entry => entry[1]).filter(toolCall => typeof toolCall.id === 'string' && !!toolCall.id && !!toolCall.name).map(toolCall => ({
|
|
3461
3525
|
arguments: toolCall.arguments,
|
|
3462
3526
|
callId: toolCall.id,
|
|
3463
3527
|
name: toolCall.name
|
|
3464
3528
|
}));
|
|
3465
3529
|
};
|
|
3530
|
+
const getStreamingToolCallKey = value => {
|
|
3531
|
+
if (!value || typeof value !== 'object') {
|
|
3532
|
+
return undefined;
|
|
3533
|
+
}
|
|
3534
|
+
const outputIndex = Reflect.get(value, 'output_index');
|
|
3535
|
+
if (typeof outputIndex === 'number') {
|
|
3536
|
+
return String(outputIndex);
|
|
3537
|
+
}
|
|
3538
|
+
if (typeof outputIndex === 'string' && outputIndex) {
|
|
3539
|
+
return outputIndex;
|
|
3540
|
+
}
|
|
3541
|
+
const itemId = Reflect.get(value, 'item_id');
|
|
3542
|
+
if (typeof itemId === 'string' && itemId) {
|
|
3543
|
+
return itemId;
|
|
3544
|
+
}
|
|
3545
|
+
return undefined;
|
|
3546
|
+
};
|
|
3466
3547
|
const parseOpenApiStream = async (response, onTextChunk, onToolCallsChunk, onDataEvent) => {
|
|
3467
3548
|
if (!response.body) {
|
|
3468
3549
|
return {
|
|
@@ -3482,7 +3563,7 @@ const parseOpenApiStream = async (response, onTextChunk, onToolCallsChunk, onDat
|
|
|
3482
3563
|
if (!onToolCallsChunk) {
|
|
3483
3564
|
return;
|
|
3484
3565
|
}
|
|
3485
|
-
const toolCalls = Object.entries(toolCallAccumulator).toSorted((a, b) =>
|
|
3566
|
+
const toolCalls = Object.entries(toolCallAccumulator).toSorted((a, b) => a[0].localeCompare(b[0])).map(entry => entry[1]).filter(toolCall => !!toolCall.name);
|
|
3486
3567
|
if (toolCalls.length === 0) {
|
|
3487
3568
|
return;
|
|
3488
3569
|
}
|
|
@@ -3530,9 +3611,9 @@ const parseOpenApiStream = async (response, onTextChunk, onToolCallsChunk, onDat
|
|
|
3530
3611
|
return;
|
|
3531
3612
|
}
|
|
3532
3613
|
if (eventType === 'response.output_item.added') {
|
|
3533
|
-
const
|
|
3614
|
+
const toolCallKey = getStreamingToolCallKey(parsed);
|
|
3534
3615
|
const item = Reflect.get(parsed, 'item');
|
|
3535
|
-
if (
|
|
3616
|
+
if (!toolCallKey || !item || typeof item !== 'object') {
|
|
3536
3617
|
return;
|
|
3537
3618
|
}
|
|
3538
3619
|
const itemType = Reflect.get(item, 'type');
|
|
@@ -3551,15 +3632,15 @@ const parseOpenApiStream = async (response, onTextChunk, onToolCallsChunk, onDat
|
|
|
3551
3632
|
};
|
|
3552
3633
|
toolCallAccumulator = {
|
|
3553
3634
|
...toolCallAccumulator,
|
|
3554
|
-
[
|
|
3635
|
+
[toolCallKey]: next
|
|
3555
3636
|
};
|
|
3556
3637
|
await emitToolCallAccumulator();
|
|
3557
3638
|
return;
|
|
3558
3639
|
}
|
|
3559
3640
|
if (eventType === 'response.output_item.done') {
|
|
3560
|
-
const
|
|
3641
|
+
const toolCallKey = getStreamingToolCallKey(parsed);
|
|
3561
3642
|
const item = Reflect.get(parsed, 'item');
|
|
3562
|
-
if (
|
|
3643
|
+
if (!toolCallKey || !item || typeof item !== 'object') {
|
|
3563
3644
|
return;
|
|
3564
3645
|
}
|
|
3565
3646
|
const itemType = Reflect.get(item, 'type');
|
|
@@ -3569,13 +3650,13 @@ const parseOpenApiStream = async (response, onTextChunk, onToolCallsChunk, onDat
|
|
|
3569
3650
|
const callId = Reflect.get(item, 'call_id');
|
|
3570
3651
|
const name = Reflect.get(item, 'name');
|
|
3571
3652
|
const rawArguments = Reflect.get(item, 'arguments');
|
|
3572
|
-
const current = toolCallAccumulator[
|
|
3653
|
+
const current = toolCallAccumulator[toolCallKey] || {
|
|
3573
3654
|
arguments: '',
|
|
3574
3655
|
name: ''
|
|
3575
3656
|
};
|
|
3576
3657
|
toolCallAccumulator = {
|
|
3577
3658
|
...toolCallAccumulator,
|
|
3578
|
-
[
|
|
3659
|
+
[toolCallKey]: {
|
|
3579
3660
|
arguments: typeof rawArguments === 'string' ? rawArguments : current.arguments,
|
|
3580
3661
|
...(typeof callId === 'string' ? {
|
|
3581
3662
|
id: callId
|
|
@@ -3589,11 +3670,11 @@ const parseOpenApiStream = async (response, onTextChunk, onToolCallsChunk, onDat
|
|
|
3589
3670
|
return;
|
|
3590
3671
|
}
|
|
3591
3672
|
if (eventType === 'response.function_call_arguments.delta' || eventType === 'response.function_call_arguments.done') {
|
|
3592
|
-
const
|
|
3593
|
-
if (
|
|
3673
|
+
const toolCallKey = getStreamingToolCallKey(parsed);
|
|
3674
|
+
if (!toolCallKey) {
|
|
3594
3675
|
return;
|
|
3595
3676
|
}
|
|
3596
|
-
const current = toolCallAccumulator[
|
|
3677
|
+
const current = toolCallAccumulator[toolCallKey] || {
|
|
3597
3678
|
arguments: '',
|
|
3598
3679
|
name: ''
|
|
3599
3680
|
};
|
|
@@ -3612,7 +3693,7 @@ const parseOpenApiStream = async (response, onTextChunk, onToolCallsChunk, onDat
|
|
|
3612
3693
|
};
|
|
3613
3694
|
toolCallAccumulator = {
|
|
3614
3695
|
...toolCallAccumulator,
|
|
3615
|
-
[
|
|
3696
|
+
[toolCallKey]: next
|
|
3616
3697
|
};
|
|
3617
3698
|
await emitToolCallAccumulator();
|
|
3618
3699
|
return;
|
|
@@ -3746,7 +3827,8 @@ const getOpenApiAssistantText = async (messages, modelId, openApiApiKey, openApi
|
|
|
3746
3827
|
onEventStreamFinished,
|
|
3747
3828
|
onTextChunk,
|
|
3748
3829
|
onToolCallsChunk,
|
|
3749
|
-
stream
|
|
3830
|
+
stream,
|
|
3831
|
+
webSearchEnabled = false
|
|
3750
3832
|
} = options ?? {
|
|
3751
3833
|
stream: false
|
|
3752
3834
|
};
|
|
@@ -3761,7 +3843,7 @@ const getOpenApiAssistantText = async (messages, modelId, openApiApiKey, openApi
|
|
|
3761
3843
|
let response;
|
|
3762
3844
|
try {
|
|
3763
3845
|
response = await fetch(getOpenApiApiEndpoint(openApiApiBaseUrl), {
|
|
3764
|
-
body: JSON.stringify(getOpenAiParams(openAiInput, modelId, stream, includeObfuscation, tools, previousResponseId)),
|
|
3846
|
+
body: JSON.stringify(getOpenAiParams(openAiInput, modelId, stream, includeObfuscation, tools, webSearchEnabled, previousResponseId)),
|
|
3765
3847
|
headers: {
|
|
3766
3848
|
Authorization: `Bearer ${openApiApiKey}`,
|
|
3767
3849
|
'Content-Type': 'application/json',
|
|
@@ -3804,6 +3886,37 @@ const getOpenApiAssistantText = async (messages, modelId, openApiApiKey, openApi
|
|
|
3804
3886
|
if (streamResult.responseId) {
|
|
3805
3887
|
previousResponseId = streamResult.responseId;
|
|
3806
3888
|
}
|
|
3889
|
+
if (streamResult.responseFunctionCalls.length > 0) {
|
|
3890
|
+
openAiInput.length = 0;
|
|
3891
|
+
const executedToolCalls = [];
|
|
3892
|
+
for (const toolCall of streamResult.responseFunctionCalls) {
|
|
3893
|
+
const content = await executeChatTool(toolCall.name, toolCall.arguments, {
|
|
3894
|
+
assetDir,
|
|
3895
|
+
platform
|
|
3896
|
+
});
|
|
3897
|
+
const executionStatus = getToolCallExecutionStatus(content);
|
|
3898
|
+
executedToolCalls.push({
|
|
3899
|
+
arguments: toolCall.arguments,
|
|
3900
|
+
...(executionStatus.errorMessage ? {
|
|
3901
|
+
errorMessage: executionStatus.errorMessage
|
|
3902
|
+
} : {}),
|
|
3903
|
+
id: toolCall.callId,
|
|
3904
|
+
name: toolCall.name,
|
|
3905
|
+
...(executionStatus.status ? {
|
|
3906
|
+
status: executionStatus.status
|
|
3907
|
+
} : {})
|
|
3908
|
+
});
|
|
3909
|
+
openAiInput.push({
|
|
3910
|
+
call_id: toolCall.callId,
|
|
3911
|
+
output: content,
|
|
3912
|
+
type: 'function_call_output'
|
|
3913
|
+
});
|
|
3914
|
+
}
|
|
3915
|
+
if (onToolCallsChunk && executedToolCalls.length > 0) {
|
|
3916
|
+
await onToolCallsChunk(executedToolCalls);
|
|
3917
|
+
}
|
|
3918
|
+
continue;
|
|
3919
|
+
}
|
|
3807
3920
|
if (onEventStreamFinished) {
|
|
3808
3921
|
await onEventStreamFinished();
|
|
3809
3922
|
}
|
|
@@ -3834,17 +3947,33 @@ const getOpenApiAssistantText = async (messages, modelId, openApiApiKey, openApi
|
|
|
3834
3947
|
const responseFunctionCalls = getResponseFunctionCalls(parsed);
|
|
3835
3948
|
if (responseFunctionCalls.length > 0) {
|
|
3836
3949
|
openAiInput.length = 0;
|
|
3950
|
+
const executedToolCalls = [];
|
|
3837
3951
|
for (const toolCall of responseFunctionCalls) {
|
|
3838
3952
|
const content = await executeChatTool(toolCall.name, toolCall.arguments, {
|
|
3839
3953
|
assetDir,
|
|
3840
3954
|
platform
|
|
3841
3955
|
});
|
|
3956
|
+
const executionStatus = getToolCallExecutionStatus(content);
|
|
3957
|
+
executedToolCalls.push({
|
|
3958
|
+
arguments: toolCall.arguments,
|
|
3959
|
+
...(executionStatus.errorMessage ? {
|
|
3960
|
+
errorMessage: executionStatus.errorMessage
|
|
3961
|
+
} : {}),
|
|
3962
|
+
id: toolCall.callId,
|
|
3963
|
+
name: toolCall.name,
|
|
3964
|
+
...(executionStatus.status ? {
|
|
3965
|
+
status: executionStatus.status
|
|
3966
|
+
} : {})
|
|
3967
|
+
});
|
|
3842
3968
|
openAiInput.push({
|
|
3843
3969
|
call_id: toolCall.callId,
|
|
3844
3970
|
output: content,
|
|
3845
3971
|
type: 'function_call_output'
|
|
3846
3972
|
});
|
|
3847
3973
|
}
|
|
3974
|
+
if (onToolCallsChunk && executedToolCalls.length > 0) {
|
|
3975
|
+
await onToolCallsChunk(executedToolCalls);
|
|
3976
|
+
}
|
|
3848
3977
|
continue;
|
|
3849
3978
|
}
|
|
3850
3979
|
const outputText = getResponseOutputText(parsed);
|
|
@@ -3873,6 +4002,7 @@ const getOpenApiAssistantText = async (messages, modelId, openApiApiKey, openApi
|
|
|
3873
4002
|
const toolCalls = Reflect.get(message, 'tool_calls');
|
|
3874
4003
|
if (Array.isArray(toolCalls) && toolCalls.length > 0) {
|
|
3875
4004
|
openAiInput.length = 0;
|
|
4005
|
+
const executedToolCalls = [];
|
|
3876
4006
|
for (const toolCall of toolCalls) {
|
|
3877
4007
|
if (!toolCall || typeof toolCall !== 'object') {
|
|
3878
4008
|
continue;
|
|
@@ -3888,12 +4018,29 @@ const getOpenApiAssistantText = async (messages, modelId, openApiApiKey, openApi
|
|
|
3888
4018
|
assetDir,
|
|
3889
4019
|
platform
|
|
3890
4020
|
}) : '{}';
|
|
4021
|
+
if (typeof name === 'string') {
|
|
4022
|
+
const executionStatus = getToolCallExecutionStatus(content);
|
|
4023
|
+
executedToolCalls.push({
|
|
4024
|
+
arguments: typeof rawArguments === 'string' ? rawArguments : '',
|
|
4025
|
+
...(executionStatus.errorMessage ? {
|
|
4026
|
+
errorMessage: executionStatus.errorMessage
|
|
4027
|
+
} : {}),
|
|
4028
|
+
id,
|
|
4029
|
+
name,
|
|
4030
|
+
...(executionStatus.status ? {
|
|
4031
|
+
status: executionStatus.status
|
|
4032
|
+
} : {})
|
|
4033
|
+
});
|
|
4034
|
+
}
|
|
3891
4035
|
openAiInput.push({
|
|
3892
4036
|
call_id: id,
|
|
3893
4037
|
output: content,
|
|
3894
4038
|
type: 'function_call_output'
|
|
3895
4039
|
});
|
|
3896
4040
|
}
|
|
4041
|
+
if (onToolCallsChunk && executedToolCalls.length > 0) {
|
|
4042
|
+
await onToolCallsChunk(executedToolCalls);
|
|
4043
|
+
}
|
|
3897
4044
|
continue;
|
|
3898
4045
|
}
|
|
3899
4046
|
const content = Reflect.get(message, 'content');
|
|
@@ -3920,6 +4067,12 @@ const getOpenApiErrorMessage = errorResult => {
|
|
|
3920
4067
|
const errorMessage = errorResult.errorMessage?.trim();
|
|
3921
4068
|
const hasErrorCode = typeof errorResult.errorCode === 'string' && errorResult.errorCode.length > 0;
|
|
3922
4069
|
const hasErrorType = typeof errorResult.errorType === 'string' && errorResult.errorType.length > 0;
|
|
4070
|
+
|
|
4071
|
+
// Provide a concise, user-friendly message when OpenAI reports an invalid API key.
|
|
4072
|
+
if (errorResult.errorCode === 'invalid_api_key') {
|
|
4073
|
+
const status = typeof errorResult.statusCode === 'number' ? errorResult.statusCode : 401;
|
|
4074
|
+
return `OpenAI request failed (status ${status}): Invalid API key. Please verify your OpenAI API key in Chat settings.`;
|
|
4075
|
+
}
|
|
3923
4076
|
if (errorResult.statusCode === 429) {
|
|
3924
4077
|
let prefix = 'OpenAI rate limit exceeded (429)';
|
|
3925
4078
|
if (hasErrorCode) {
|
|
@@ -4283,7 +4436,8 @@ const getAiResponse = async ({
|
|
|
4283
4436
|
selectedModelId,
|
|
4284
4437
|
streamingEnabled = false,
|
|
4285
4438
|
useMockApi,
|
|
4286
|
-
userText
|
|
4439
|
+
userText,
|
|
4440
|
+
webSearchEnabled = false
|
|
4287
4441
|
}) => {
|
|
4288
4442
|
let text = '';
|
|
4289
4443
|
const usesOpenApiModel = isOpenApiModel(selectedModelId, models);
|
|
@@ -4314,7 +4468,8 @@ const getAiResponse = async ({
|
|
|
4314
4468
|
...(onToolCallsChunk ? {
|
|
4315
4469
|
onToolCallsChunk
|
|
4316
4470
|
} : {}),
|
|
4317
|
-
stream: streamingEnabled
|
|
4471
|
+
stream: streamingEnabled,
|
|
4472
|
+
webSearchEnabled
|
|
4318
4473
|
});
|
|
4319
4474
|
if (result.type === 'success') {
|
|
4320
4475
|
const {
|
|
@@ -4703,7 +4858,8 @@ const handleSubmit = async state => {
|
|
|
4703
4858
|
sessions,
|
|
4704
4859
|
streamingEnabled,
|
|
4705
4860
|
useMockApi,
|
|
4706
|
-
viewMode
|
|
4861
|
+
viewMode,
|
|
4862
|
+
webSearchEnabled
|
|
4707
4863
|
} = state;
|
|
4708
4864
|
const userText = composerValue.trim();
|
|
4709
4865
|
if (!userText) {
|
|
@@ -4847,7 +5003,8 @@ const handleSubmit = async state => {
|
|
|
4847
5003
|
selectedModelId,
|
|
4848
5004
|
streamingEnabled,
|
|
4849
5005
|
useMockApi,
|
|
4850
|
-
userText
|
|
5006
|
+
userText,
|
|
5007
|
+
webSearchEnabled
|
|
4851
5008
|
});
|
|
4852
5009
|
const {
|
|
4853
5010
|
latestState
|
|
@@ -5168,6 +5325,11 @@ const handleKeyDown = async (state, key, shiftKey) => {
|
|
|
5168
5325
|
return handleSubmit(submitState);
|
|
5169
5326
|
};
|
|
5170
5327
|
|
|
5328
|
+
const handleMessagesContextMenu = async state => {
|
|
5329
|
+
await invoke('ContextMenu.show', 1234);
|
|
5330
|
+
return state;
|
|
5331
|
+
};
|
|
5332
|
+
|
|
5171
5333
|
const handleModelChange = async (state, value) => {
|
|
5172
5334
|
return {
|
|
5173
5335
|
...state,
|
|
@@ -5510,6 +5672,34 @@ const getCss = (composerHeight, listItemHeight, chatMessageFontSize, chatMessage
|
|
|
5510
5672
|
--ChatMessageFontSize: ${chatMessageFontSize}px;
|
|
5511
5673
|
--ChatMessageLineHeight: ${chatMessageLineHeight}px;
|
|
5512
5674
|
--ChatMessageFontFamily: ${chatMessageFontFamily};
|
|
5675
|
+
}
|
|
5676
|
+
|
|
5677
|
+
.ChatToolCalls {
|
|
5678
|
+
position: relative;
|
|
5679
|
+
border: 1px solid var(--vscode-editorWidget-border);
|
|
5680
|
+
border-radius: 4px;
|
|
5681
|
+
margin-bottom: 8px;
|
|
5682
|
+
padding: 10px 8px 6px;
|
|
5683
|
+
background: var(--vscode-editorWidget-background);
|
|
5684
|
+
}
|
|
5685
|
+
|
|
5686
|
+
.ChatToolCallsLabel {
|
|
5687
|
+
position: absolute;
|
|
5688
|
+
top: -8px;
|
|
5689
|
+
left: 8px;
|
|
5690
|
+
padding: 0 4px;
|
|
5691
|
+
border-radius: 3px;
|
|
5692
|
+
background: var(--vscode-editor-background);
|
|
5693
|
+
color: var(--vscode-descriptionForeground);
|
|
5694
|
+
font-size: 10px;
|
|
5695
|
+
line-height: 14px;
|
|
5696
|
+
text-transform: lowercase;
|
|
5697
|
+
letter-spacing: 0.02em;
|
|
5698
|
+
}
|
|
5699
|
+
|
|
5700
|
+
.ChatToolCallReadFileLink {
|
|
5701
|
+
color: var(--vscode-textLink-foreground);
|
|
5702
|
+
text-decoration: underline;
|
|
5513
5703
|
}`;
|
|
5514
5704
|
};
|
|
5515
5705
|
|
|
@@ -5577,6 +5767,9 @@ const ChatListItemLabel = 'ChatListItemLabel';
|
|
|
5577
5767
|
const Markdown = 'Markdown';
|
|
5578
5768
|
const Message = 'Message';
|
|
5579
5769
|
const ChatMessageContent = 'ChatMessageContent';
|
|
5770
|
+
const ChatToolCalls = 'ChatToolCalls';
|
|
5771
|
+
const ChatToolCallsLabel = 'ChatToolCallsLabel';
|
|
5772
|
+
const ChatToolCallReadFileLink = 'ChatToolCallReadFileLink';
|
|
5580
5773
|
const ChatOrderedList = 'ChatOrderedList';
|
|
5581
5774
|
const ChatOrderedListItem = 'ChatOrderedListItem';
|
|
5582
5775
|
const MessageUser = 'MessageUser';
|
|
@@ -5607,6 +5800,7 @@ const HandleChatListScroll = 21;
|
|
|
5607
5800
|
const HandleMessagesScroll = 22;
|
|
5608
5801
|
const HandleClickSessionDebug = 23;
|
|
5609
5802
|
const HandleClickReadFile = 24;
|
|
5803
|
+
const HandleMessagesContextMenu = 25;
|
|
5610
5804
|
|
|
5611
5805
|
const getModelLabel = model => {
|
|
5612
5806
|
if (model.provider === 'openRouter') {
|
|
@@ -5800,6 +5994,31 @@ const getChatHeaderDomDetailMode = selectedSessionTitle => {
|
|
|
5800
5994
|
}, text(selectedSessionTitle), ...getChatHeaderActionsDom()];
|
|
5801
5995
|
};
|
|
5802
5996
|
|
|
5997
|
+
const getMessageNodeDom = node => {
|
|
5998
|
+
if (node.type === 'text') {
|
|
5999
|
+
return [{
|
|
6000
|
+
childCount: 1,
|
|
6001
|
+
className: Markdown,
|
|
6002
|
+
type: P
|
|
6003
|
+
}, text(node.text)];
|
|
6004
|
+
}
|
|
6005
|
+
return [{
|
|
6006
|
+
childCount: node.items.length,
|
|
6007
|
+
className: ChatOrderedList,
|
|
6008
|
+
type: Ol
|
|
6009
|
+
}, ...node.items.flatMap(item => {
|
|
6010
|
+
return [{
|
|
6011
|
+
childCount: 1,
|
|
6012
|
+
className: ChatOrderedListItem,
|
|
6013
|
+
type: Li
|
|
6014
|
+
}, text(item.text)];
|
|
6015
|
+
})];
|
|
6016
|
+
};
|
|
6017
|
+
|
|
6018
|
+
const getMessageContentDom = nodes => {
|
|
6019
|
+
return nodes.flatMap(getMessageNodeDom);
|
|
6020
|
+
};
|
|
6021
|
+
|
|
5803
6022
|
const getMissingApiKeyDom = ({
|
|
5804
6023
|
getApiKeyText,
|
|
5805
6024
|
inputName,
|
|
@@ -5921,8 +6140,10 @@ const getToolCallArgumentPreview = rawArguments => {
|
|
|
5921
6140
|
return rawArguments;
|
|
5922
6141
|
};
|
|
5923
6142
|
|
|
6143
|
+
const RE_QUERY_OR_HASH = /[?#].*$/;
|
|
6144
|
+
const RE_TRAILING_SLASH = /\/$/;
|
|
5924
6145
|
const getFileNameFromUri = uri => {
|
|
5925
|
-
const stripped = uri.replace(
|
|
6146
|
+
const stripped = uri.replace(RE_QUERY_OR_HASH, '').replace(RE_TRAILING_SLASH, '');
|
|
5926
6147
|
const slashIndex = Math.max(stripped.lastIndexOf('/'), stripped.lastIndexOf('\\\\'));
|
|
5927
6148
|
const fileName = slashIndex === -1 ? stripped : stripped.slice(slashIndex + 1);
|
|
5928
6149
|
return fileName || uri;
|
|
@@ -5946,42 +6167,68 @@ const getReadFileTarget = rawArguments => {
|
|
|
5946
6167
|
if (!title) {
|
|
5947
6168
|
return undefined;
|
|
5948
6169
|
}
|
|
5949
|
-
|
|
6170
|
+
// `read_file` tool calls usually provide a relative `path`; pass it through so UI clicks can open the file.
|
|
6171
|
+
const clickableUri = uriValue || pathValue;
|
|
5950
6172
|
return {
|
|
5951
6173
|
clickableUri,
|
|
5952
6174
|
title
|
|
5953
6175
|
};
|
|
5954
6176
|
};
|
|
5955
6177
|
|
|
6178
|
+
const getToolCallStatusLabel$1 = toolCall => {
|
|
6179
|
+
if (toolCall.status === 'not-found') {
|
|
6180
|
+
return ' (not-found)';
|
|
6181
|
+
}
|
|
6182
|
+
if (toolCall.status === 'error') {
|
|
6183
|
+
if (toolCall.errorMessage) {
|
|
6184
|
+
return ` (error: ${toolCall.errorMessage})`;
|
|
6185
|
+
}
|
|
6186
|
+
return ' (error)';
|
|
6187
|
+
}
|
|
6188
|
+
return '';
|
|
6189
|
+
};
|
|
6190
|
+
|
|
5956
6191
|
const getToolCallReadFileVirtualDom = toolCall => {
|
|
5957
6192
|
const target = getReadFileTarget(toolCall.arguments);
|
|
5958
6193
|
if (!target) {
|
|
5959
6194
|
return [];
|
|
5960
6195
|
}
|
|
5961
6196
|
const fileName = getFileNameFromUri(target.title);
|
|
5962
|
-
const
|
|
6197
|
+
const toolNameLabel = `${toolCall.name} `;
|
|
6198
|
+
const statusLabel = getToolCallStatusLabel$1(toolCall);
|
|
6199
|
+
const fileNameClickableProps = target.clickableUri ? {
|
|
5963
6200
|
'data-uri': target.clickableUri,
|
|
5964
6201
|
onClick: HandleClickReadFile
|
|
5965
6202
|
} : {};
|
|
5966
6203
|
return [{
|
|
5967
|
-
childCount:
|
|
6204
|
+
childCount: statusLabel ? 4 : 3,
|
|
5968
6205
|
className: ChatOrderedListItem,
|
|
5969
|
-
...clickableProps,
|
|
5970
6206
|
title: target.title,
|
|
5971
6207
|
type: Li
|
|
5972
6208
|
}, {
|
|
5973
6209
|
childCount: 0,
|
|
5974
6210
|
className: FileIcon,
|
|
5975
|
-
...clickableProps,
|
|
5976
6211
|
type: Div
|
|
5977
|
-
}, {
|
|
6212
|
+
}, text(toolNameLabel), {
|
|
5978
6213
|
childCount: 1,
|
|
5979
|
-
|
|
5980
|
-
|
|
6214
|
+
className: ChatToolCallReadFileLink,
|
|
6215
|
+
...fileNameClickableProps,
|
|
5981
6216
|
type: Span
|
|
5982
|
-
}, text(fileName)];
|
|
6217
|
+
}, text(fileName), ...(statusLabel ? [text(statusLabel)] : [])];
|
|
5983
6218
|
};
|
|
5984
6219
|
|
|
6220
|
+
const getToolCallStatusLabel = toolCall => {
|
|
6221
|
+
if (toolCall.status === 'not-found') {
|
|
6222
|
+
return ' (not-found)';
|
|
6223
|
+
}
|
|
6224
|
+
if (toolCall.status === 'error') {
|
|
6225
|
+
if (toolCall.errorMessage) {
|
|
6226
|
+
return ` (error: ${toolCall.errorMessage})`;
|
|
6227
|
+
}
|
|
6228
|
+
return ' (error)';
|
|
6229
|
+
}
|
|
6230
|
+
return '';
|
|
6231
|
+
};
|
|
5985
6232
|
const getToolCallDom = toolCall => {
|
|
5986
6233
|
if (toolCall.name === 'read_file') {
|
|
5987
6234
|
const virtualDom = getToolCallReadFileVirtualDom(toolCall);
|
|
@@ -5990,7 +6237,7 @@ const getToolCallDom = toolCall => {
|
|
|
5990
6237
|
}
|
|
5991
6238
|
}
|
|
5992
6239
|
const argumentPreview = getToolCallArgumentPreview(toolCall.arguments);
|
|
5993
|
-
const label = `${toolCall.name} ${argumentPreview}`;
|
|
6240
|
+
const label = `${toolCall.name} ${argumentPreview}${getToolCallStatusLabel(toolCall)}`;
|
|
5994
6241
|
return [{
|
|
5995
6242
|
childCount: 1,
|
|
5996
6243
|
className: ChatOrderedListItem,
|
|
@@ -6003,6 +6250,14 @@ const getToolCallsDom = message => {
|
|
|
6003
6250
|
return [];
|
|
6004
6251
|
}
|
|
6005
6252
|
return [{
|
|
6253
|
+
childCount: 2,
|
|
6254
|
+
className: ChatToolCalls,
|
|
6255
|
+
type: Div$1
|
|
6256
|
+
}, {
|
|
6257
|
+
childCount: 1,
|
|
6258
|
+
className: ChatToolCallsLabel,
|
|
6259
|
+
type: Div$1
|
|
6260
|
+
}, text('tools'), {
|
|
6006
6261
|
childCount: message.toolCalls.length,
|
|
6007
6262
|
className: ChatOrderedList,
|
|
6008
6263
|
type: Ol$1
|
|
@@ -6066,28 +6321,6 @@ const parseMessageContent = rawMessage => {
|
|
|
6066
6321
|
type: 'text'
|
|
6067
6322
|
}] : nodes;
|
|
6068
6323
|
};
|
|
6069
|
-
const getMessageContentDom = nodes => {
|
|
6070
|
-
return nodes.flatMap(node => {
|
|
6071
|
-
if (node.type === 'text') {
|
|
6072
|
-
return [{
|
|
6073
|
-
childCount: 1,
|
|
6074
|
-
className: Markdown,
|
|
6075
|
-
type: P
|
|
6076
|
-
}, text(node.text)];
|
|
6077
|
-
}
|
|
6078
|
-
return [{
|
|
6079
|
-
childCount: node.items.length,
|
|
6080
|
-
className: ChatOrderedList,
|
|
6081
|
-
type: Ol
|
|
6082
|
-
}, ...node.items.flatMap(item => {
|
|
6083
|
-
return [{
|
|
6084
|
-
childCount: 1,
|
|
6085
|
-
className: ChatOrderedListItem,
|
|
6086
|
-
type: Li
|
|
6087
|
-
}, text(item.text)];
|
|
6088
|
-
})];
|
|
6089
|
-
});
|
|
6090
|
-
};
|
|
6091
6324
|
|
|
6092
6325
|
const getChatMessageDom = (message, openRouterApiKeyInput, openApiApiKeyInput = '', openRouterApiKeyState = 'idle') => {
|
|
6093
6326
|
const roleClassName = message.role === 'user' ? MessageUser : MessageAssistant;
|
|
@@ -6126,6 +6359,7 @@ const getMessagesDom = (messages, openRouterApiKeyInput, openApiApiKeyInput = ''
|
|
|
6126
6359
|
return [{
|
|
6127
6360
|
childCount: messages.length,
|
|
6128
6361
|
className: 'ChatMessages',
|
|
6362
|
+
onContextMenu: HandleMessagesContextMenu,
|
|
6129
6363
|
onScroll: HandleMessagesScroll,
|
|
6130
6364
|
scrollTop: messagesScrollTop,
|
|
6131
6365
|
type: Div
|
|
@@ -6376,6 +6610,10 @@ const renderEventListeners = () => {
|
|
|
6376
6610
|
}, {
|
|
6377
6611
|
name: HandleMessagesScroll,
|
|
6378
6612
|
params: ['handleMessagesScroll', 'event.target.scrollTop']
|
|
6613
|
+
}, {
|
|
6614
|
+
name: HandleMessagesContextMenu,
|
|
6615
|
+
params: ['handleMessagesContextMenu'],
|
|
6616
|
+
preventDefault: true
|
|
6379
6617
|
}, {
|
|
6380
6618
|
name: HandleFocus,
|
|
6381
6619
|
params: ['handleInputFocus', TargetName]
|
|
@@ -6419,30 +6657,22 @@ const saveState = state => {
|
|
|
6419
6657
|
const {
|
|
6420
6658
|
chatListScrollTop,
|
|
6421
6659
|
composerValue,
|
|
6422
|
-
height,
|
|
6423
6660
|
messagesScrollTop,
|
|
6424
6661
|
nextMessageId,
|
|
6425
6662
|
renamingSessionId,
|
|
6426
6663
|
selectedModelId,
|
|
6427
6664
|
selectedSessionId,
|
|
6428
|
-
viewMode
|
|
6429
|
-
width,
|
|
6430
|
-
x,
|
|
6431
|
-
y
|
|
6665
|
+
viewMode
|
|
6432
6666
|
} = state;
|
|
6433
6667
|
return {
|
|
6434
6668
|
chatListScrollTop,
|
|
6435
6669
|
composerValue,
|
|
6436
|
-
height,
|
|
6437
6670
|
messagesScrollTop,
|
|
6438
6671
|
nextMessageId,
|
|
6439
6672
|
renamingSessionId,
|
|
6440
6673
|
selectedModelId,
|
|
6441
6674
|
selectedSessionId,
|
|
6442
|
-
viewMode
|
|
6443
|
-
width,
|
|
6444
|
-
x,
|
|
6445
|
-
y
|
|
6675
|
+
viewMode
|
|
6446
6676
|
};
|
|
6447
6677
|
};
|
|
6448
6678
|
|
|
@@ -6519,6 +6749,7 @@ const commandMap = {
|
|
|
6519
6749
|
'Chat.handleInput': wrapCommand(handleInput),
|
|
6520
6750
|
'Chat.handleInputFocus': wrapCommand(handleInputFocus),
|
|
6521
6751
|
'Chat.handleKeyDown': wrapCommand(handleKeyDown),
|
|
6752
|
+
'Chat.handleMessagesContextMenu': wrapCommand(handleMessagesContextMenu),
|
|
6522
6753
|
'Chat.handleMessagesScroll': wrapCommand(handleMessagesScroll),
|
|
6523
6754
|
'Chat.handleModelChange': wrapCommand(handleModelChange),
|
|
6524
6755
|
'Chat.handleSubmit': wrapCommand(handleSubmit),
|