dominds 1.28.0 → 1.28.1
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/tools/txt.js +115 -70
- package/package.json +1 -1
package/dist/tools/txt.js
CHANGED
|
@@ -14,11 +14,40 @@ const driver_messages_1 = require("../runtime/driver-messages");
|
|
|
14
14
|
const work_language_1 = require("../runtime/work-language");
|
|
15
15
|
const tool_1 = require("../tool");
|
|
16
16
|
const output_limit_1 = require("./output-limit");
|
|
17
|
-
function
|
|
18
|
-
return
|
|
17
|
+
function limitTxtToolOutputContent(content, toolName) {
|
|
18
|
+
return (0, output_limit_1.truncateToolOutputText)(content, { toolName }).text;
|
|
19
19
|
}
|
|
20
|
-
function
|
|
21
|
-
|
|
20
|
+
function limitTxtToolMessages(messages, originalContent, limitedContent) {
|
|
21
|
+
if (messages === undefined || originalContent === limitedContent)
|
|
22
|
+
return messages;
|
|
23
|
+
return messages.map((message) => {
|
|
24
|
+
if ('content' in message && message.content === originalContent) {
|
|
25
|
+
return { ...message, content: limitedContent };
|
|
26
|
+
}
|
|
27
|
+
return message;
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
function limitedToolSuccess(content, toolName) {
|
|
31
|
+
return (0, tool_1.toolSuccess)(limitTxtToolOutputContent(content, toolName));
|
|
32
|
+
}
|
|
33
|
+
function limitedToolFailure(content, toolName) {
|
|
34
|
+
return (0, tool_1.toolFailure)(limitTxtToolOutputContent(content, toolName));
|
|
35
|
+
}
|
|
36
|
+
function ok(result, messages, toolName = 'ws_mod') {
|
|
37
|
+
const limitedResult = limitTxtToolOutputContent(result, toolName);
|
|
38
|
+
return {
|
|
39
|
+
status: 'completed',
|
|
40
|
+
result: (0, tool_1.toolSuccess)(limitedResult),
|
|
41
|
+
messages: limitTxtToolMessages(messages, result, limitedResult),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function failed(result, messages, toolName = 'ws_mod') {
|
|
45
|
+
const limitedResult = limitTxtToolOutputContent(result, toolName);
|
|
46
|
+
return {
|
|
47
|
+
status: 'failed',
|
|
48
|
+
result: (0, tool_1.toolFailure)(limitedResult),
|
|
49
|
+
messages: limitTxtToolMessages(messages, result, limitedResult),
|
|
50
|
+
};
|
|
22
51
|
}
|
|
23
52
|
function ensureInsideWorkspace(rel) {
|
|
24
53
|
const cwd = (0, rtws_1.domindsRtwsRootAbs)();
|
|
@@ -554,11 +583,11 @@ exports.wsModPadReminderOwner = {
|
|
|
554
583
|
};
|
|
555
584
|
},
|
|
556
585
|
};
|
|
557
|
-
function okYaml(yaml) {
|
|
558
|
-
return (
|
|
586
|
+
function okYaml(yaml, toolName = 'ws_mod') {
|
|
587
|
+
return limitedToolSuccess(formatYamlCodeBlock(yaml), toolName);
|
|
559
588
|
}
|
|
560
|
-
function failYaml(yaml) {
|
|
561
|
-
return (
|
|
589
|
+
function failYaml(yaml, toolName = 'ws_mod') {
|
|
590
|
+
return limitedToolFailure(formatYamlCodeBlock(yaml), toolName);
|
|
562
591
|
}
|
|
563
592
|
function splitFileTextToLines(fileText) {
|
|
564
593
|
const parts = fileText.split('\n');
|
|
@@ -732,6 +761,17 @@ function yamlFlowNumberArray(values) {
|
|
|
732
761
|
return '[]';
|
|
733
762
|
return `[${values.join(', ')}]`;
|
|
734
763
|
}
|
|
764
|
+
const OCCURRENCE_INDEX_PREVIEW_LIMIT = 200;
|
|
765
|
+
function pushOccurrenceIndexListYaml(lines, key, values) {
|
|
766
|
+
const shown = values.slice(0, OCCURRENCE_INDEX_PREVIEW_LIMIT);
|
|
767
|
+
lines.push(`${key}: ${yamlFlowNumberArray(shown)}`);
|
|
768
|
+
if (shown.length < values.length) {
|
|
769
|
+
lines.push(`${key}_truncated: true`, `${key}_shown_count: ${shown.length}`, `${key}_omitted_count: ${values.length - shown.length}`, `${key}_last: ${values[values.length - 1] ?? 0}`);
|
|
770
|
+
}
|
|
771
|
+
else {
|
|
772
|
+
lines.push(`${key}_truncated: false`);
|
|
773
|
+
}
|
|
774
|
+
}
|
|
735
775
|
function findLiteralOccurrenceIndexes(text, needle) {
|
|
736
776
|
const matches = [];
|
|
737
777
|
let fromIndex = 0;
|
|
@@ -2382,6 +2422,7 @@ exports.fileRangeEditTool = {
|
|
|
2382
2422
|
argsValidation: 'dominds',
|
|
2383
2423
|
call: async (dlg, caller, args) => {
|
|
2384
2424
|
const language = (0, work_language_1.getWorkLanguage)();
|
|
2425
|
+
const mode = 'file_range_edit';
|
|
2385
2426
|
try {
|
|
2386
2427
|
const filePath = requireNonEmptyStringArg(args, 'path');
|
|
2387
2428
|
const rangeSpec = requireNonEmptyStringArg(args, 'range');
|
|
@@ -2390,7 +2431,7 @@ exports.fileRangeEditTool = {
|
|
|
2390
2431
|
const source = resolveFileRangeEditSource(dlg, args);
|
|
2391
2432
|
const replacementText = fileRangeEditSourceText(source);
|
|
2392
2433
|
if (!(0, access_control_1.hasWriteAccess)(caller, filePath)) {
|
|
2393
|
-
return (
|
|
2434
|
+
return limitedToolFailure((0, access_control_1.getAccessDeniedMessage)('write', filePath, language), mode);
|
|
2394
2435
|
}
|
|
2395
2436
|
const absPath = ensureInsideWorkspace(filePath);
|
|
2396
2437
|
const absKey = path_1.default.resolve(absPath);
|
|
@@ -2410,7 +2451,7 @@ exports.fileRangeEditTool = {
|
|
|
2410
2451
|
`path: ${yamlQuote(filePath)}`,
|
|
2411
2452
|
`error: FILE_NOT_FOUND`,
|
|
2412
2453
|
`summary: ${yamlQuote(language === 'zh' ? '文件不存在。' : 'File not found.')}`,
|
|
2413
|
-
].join('\n'));
|
|
2454
|
+
].join('\n'), mode);
|
|
2414
2455
|
}
|
|
2415
2456
|
throw error;
|
|
2416
2457
|
}
|
|
@@ -2421,7 +2462,7 @@ exports.fileRangeEditTool = {
|
|
|
2421
2462
|
`path: ${yamlQuote(filePath)}`,
|
|
2422
2463
|
`error: NOT_A_FILE`,
|
|
2423
2464
|
`summary: ${yamlQuote(language === 'zh' ? '路径不是文件。' : 'Path is not a file.')}`,
|
|
2424
|
-
].join('\n'));
|
|
2465
|
+
].join('\n'), mode);
|
|
2425
2466
|
}
|
|
2426
2467
|
const currentContent = fs_1.default.readFileSync(absPath, 'utf8');
|
|
2427
2468
|
const currentLines = splitFileTextToLines(currentContent);
|
|
@@ -2434,7 +2475,7 @@ exports.fileRangeEditTool = {
|
|
|
2434
2475
|
`range: ${yamlQuote(rangeSpec)}`,
|
|
2435
2476
|
`error: INVALID_RANGE`,
|
|
2436
2477
|
`summary: ${yamlQuote(parsed.error)}`,
|
|
2437
|
-
].join('\n'));
|
|
2478
|
+
].join('\n'), mode);
|
|
2438
2479
|
}
|
|
2439
2480
|
const range = parsed.range;
|
|
2440
2481
|
const startIndex0 = range.kind === 'append' ? rangeTotalLines(currentLines) : range.startLine - 1;
|
|
@@ -2473,10 +2514,10 @@ exports.fileRangeEditTool = {
|
|
|
2473
2514
|
: `${preview ? 'Previewed' : 'Wrote'}: ${action} ${filePath}:${rangeSpec}; old=${deleteCount}, new=${replacementLines.length}.`)}`);
|
|
2474
2515
|
const yaml = yamlLines.join('\n');
|
|
2475
2516
|
if (!showDiff) {
|
|
2476
|
-
return okYaml(yaml);
|
|
2517
|
+
return okYaml(yaml, mode);
|
|
2477
2518
|
}
|
|
2478
2519
|
const unifiedDiff = buildUnifiedSingleHunkDiff(filePath, currentLines, startIndex0, deleteCount, replacementLines);
|
|
2479
|
-
return (
|
|
2520
|
+
return limitedToolSuccess(`${formatYamlCodeBlock(yaml)}\n\n\`\`\`diff\n${unifiedDiff}\`\`\``, mode);
|
|
2480
2521
|
};
|
|
2481
2522
|
return await new Promise((resolve) => {
|
|
2482
2523
|
enqueueFileApply(absKey, {
|
|
@@ -2492,7 +2533,7 @@ exports.fileRangeEditTool = {
|
|
|
2492
2533
|
`mode: file_range_edit`,
|
|
2493
2534
|
`error: WRITE_FAILED`,
|
|
2494
2535
|
`summary: ${yamlQuote(error instanceof Error ? error.message : String(error))}`,
|
|
2495
|
-
].join('\n')));
|
|
2536
|
+
].join('\n'), mode));
|
|
2496
2537
|
}
|
|
2497
2538
|
},
|
|
2498
2539
|
});
|
|
@@ -2505,7 +2546,7 @@ exports.fileRangeEditTool = {
|
|
|
2505
2546
|
`mode: file_range_edit`,
|
|
2506
2547
|
`error: INVALID_ARGS`,
|
|
2507
2548
|
`summary: ${yamlQuote(error instanceof Error ? error.message : String(error))}`,
|
|
2508
|
-
].join('\n'));
|
|
2549
|
+
].join('\n'), mode);
|
|
2509
2550
|
}
|
|
2510
2551
|
},
|
|
2511
2552
|
};
|
|
@@ -2563,12 +2604,12 @@ exports.prepareOccurrenceReplaceTool = {
|
|
|
2563
2604
|
`path: ${yamlQuote(filePath)}`,
|
|
2564
2605
|
`error: INVALID_OCCURRENCE_INDEXES`,
|
|
2565
2606
|
`summary: ${yamlQuote(selectedParsed.error)}`,
|
|
2566
|
-
].join('\n'));
|
|
2607
|
+
].join('\n'), mode);
|
|
2567
2608
|
}
|
|
2568
2609
|
const source = resolveFileRangeEditSource(dlg, args);
|
|
2569
2610
|
const replacementText = fileRangeEditSourceText(source);
|
|
2570
2611
|
if (!(0, access_control_1.hasWriteAccess)(caller, filePath)) {
|
|
2571
|
-
return (
|
|
2612
|
+
return limitedToolFailure((0, access_control_1.getAccessDeniedMessage)('write', filePath, language), mode);
|
|
2572
2613
|
}
|
|
2573
2614
|
const absPath = ensureInsideWorkspace(filePath);
|
|
2574
2615
|
if (!fs_1.default.existsSync(absPath)) {
|
|
@@ -2578,7 +2619,7 @@ exports.prepareOccurrenceReplaceTool = {
|
|
|
2578
2619
|
`path: ${yamlQuote(filePath)}`,
|
|
2579
2620
|
`error: FILE_NOT_FOUND`,
|
|
2580
2621
|
`summary: ${yamlQuote(language === 'zh' ? '文件不存在。' : 'File not found.')}`,
|
|
2581
|
-
].join('\n'));
|
|
2622
|
+
].join('\n'), mode);
|
|
2582
2623
|
}
|
|
2583
2624
|
if (!fs_1.default.statSync(absPath).isFile()) {
|
|
2584
2625
|
return failYaml([
|
|
@@ -2587,7 +2628,7 @@ exports.prepareOccurrenceReplaceTool = {
|
|
|
2587
2628
|
`path: ${yamlQuote(filePath)}`,
|
|
2588
2629
|
`error: NOT_A_FILE`,
|
|
2589
2630
|
`summary: ${yamlQuote(language === 'zh' ? '路径不是文件。' : 'Path is not a file.')}`,
|
|
2590
|
-
].join('\n'));
|
|
2631
|
+
].join('\n'), mode);
|
|
2591
2632
|
}
|
|
2592
2633
|
const currentContent = fs_1.default.readFileSync(absPath, 'utf8');
|
|
2593
2634
|
const occurrenceOffsets = findLiteralOccurrenceIndexes(currentContent, findText);
|
|
@@ -2601,7 +2642,7 @@ exports.prepareOccurrenceReplaceTool = {
|
|
|
2601
2642
|
`summary: ${yamlQuote(language === 'zh'
|
|
2602
2643
|
? '没有找到要替换的 occurrence。'
|
|
2603
2644
|
: 'No occurrence was found for the requested literal text.')}`,
|
|
2604
|
-
].join('\n'));
|
|
2645
|
+
].join('\n'), mode);
|
|
2605
2646
|
}
|
|
2606
2647
|
const selectedOccurrences = selectedParsed.indexes ?? occurrenceOffsets.map((_offset, index0) => index0 + 1);
|
|
2607
2648
|
const outOfRange = selectedOccurrences.find((index1) => index1 > totalOccurrences);
|
|
@@ -2615,7 +2656,7 @@ exports.prepareOccurrenceReplaceTool = {
|
|
|
2615
2656
|
`summary: ${yamlQuote(language === 'zh'
|
|
2616
2657
|
? `occurrence_indexes 包含超范围值:${outOfRange}。`
|
|
2617
2658
|
: `occurrence_indexes contains an out-of-range value: ${outOfRange}.`)}`,
|
|
2618
|
-
].join('\n'));
|
|
2659
|
+
].join('\n'), mode);
|
|
2619
2660
|
}
|
|
2620
2661
|
const nextContent = replaceSelectedLiteralOccurrences(currentContent, occurrenceOffsets, selectedOccurrences, findText, replacementText);
|
|
2621
2662
|
const currentLines = splitTextToLinesForEditing(currentContent);
|
|
@@ -2662,7 +2703,9 @@ exports.prepareOccurrenceReplaceTool = {
|
|
|
2662
2703
|
`plan_id: ${yamlQuote(planId)}`,
|
|
2663
2704
|
];
|
|
2664
2705
|
pushOccurrenceReplaceSourceYaml(yamlLines, plan.source, showDiff);
|
|
2665
|
-
yamlLines.push(`expires_at_ms: ${plan.expiresAtMs}`, `find: ${yamlQuote(findText)}`, `occurrences_found: ${totalOccurrences}
|
|
2706
|
+
yamlLines.push(`expires_at_ms: ${plan.expiresAtMs}`, `find: ${yamlQuote(findText)}`, `occurrences_found: ${totalOccurrences}`);
|
|
2707
|
+
pushOccurrenceIndexListYaml(yamlLines, 'selected_occurrences', selectedOccurrences);
|
|
2708
|
+
yamlLines.push(`selected_count: ${selectedOccurrences.length}`, `file:`, ` old_total_lines: ${plan.oldTotalLines}`, ` old_total_bytes: ${plan.oldTotalBytes}`, ` old_hash: ${yamlQuote(plan.oldFileHash)}`, ` new_total_lines: ${plan.newTotalLines}`, ` new_total_bytes: ${plan.newTotalBytes}`, ` new_hash: ${yamlQuote(plan.newFileHash)}`, `match_preview: ${yamlBlockScalarLines(previewRows, ' ')}`, `next: ${yamlQuote(`apply_occurrence_replace({ "plan_id": "${planId}" })`)}`, `summary: ${yamlQuote(language === 'zh'
|
|
2666
2709
|
? `已规划 ${selectedOccurrences.length}/${totalOccurrences} 个 occurrence 的替换;plan_id=${planId}.`
|
|
2667
2710
|
: `Planned replacement for ${selectedOccurrences.length}/${totalOccurrences} occurrence(s); plan_id=${planId}.`)}`);
|
|
2668
2711
|
if (selectedOccurrences.length < 2) {
|
|
@@ -2672,9 +2715,9 @@ exports.prepareOccurrenceReplaceTool = {
|
|
|
2672
2715
|
}
|
|
2673
2716
|
const yaml = yamlLines.join('\n');
|
|
2674
2717
|
if (!showDiff)
|
|
2675
|
-
return okYaml(yaml);
|
|
2718
|
+
return okYaml(yaml, mode);
|
|
2676
2719
|
const diff = buildUnifiedSingleHunkDiff(filePath, currentLines, 0, currentLines.length, nextLines);
|
|
2677
|
-
return (
|
|
2720
|
+
return limitedToolSuccess(`${formatYamlCodeBlock(yaml)}\n\n\`\`\`diff\n${diff}\`\`\``, mode);
|
|
2678
2721
|
}
|
|
2679
2722
|
catch (error) {
|
|
2680
2723
|
return failYaml([
|
|
@@ -2682,7 +2725,7 @@ exports.prepareOccurrenceReplaceTool = {
|
|
|
2682
2725
|
`mode: ${mode}`,
|
|
2683
2726
|
`error: INVALID_ARGS`,
|
|
2684
2727
|
`summary: ${yamlQuote(error instanceof Error ? error.message : String(error))}`,
|
|
2685
|
-
].join('\n'));
|
|
2728
|
+
].join('\n'), mode);
|
|
2686
2729
|
}
|
|
2687
2730
|
},
|
|
2688
2731
|
};
|
|
@@ -2720,7 +2763,7 @@ exports.applyOccurrenceReplaceTool = {
|
|
|
2720
2763
|
`plan_id: ${yamlQuote(planId)}`,
|
|
2721
2764
|
`error: INVALID_PLAN_ID`,
|
|
2722
2765
|
`summary: ${yamlQuote('Invalid plan_id format.')}`,
|
|
2723
|
-
].join('\n'));
|
|
2766
|
+
].join('\n'), mode);
|
|
2724
2767
|
}
|
|
2725
2768
|
pruneExpiredOccurrenceReplacePlans(Date.now());
|
|
2726
2769
|
const plan = occurrenceReplacePlansById.get(planId);
|
|
@@ -2733,7 +2776,7 @@ exports.applyOccurrenceReplaceTool = {
|
|
|
2733
2776
|
`summary: ${yamlQuote(language === 'zh'
|
|
2734
2777
|
? 'plan_id 不存在,可能已过期、已应用或进程已重启。请重新 prepare。'
|
|
2735
2778
|
: 'plan_id was not found; it may have expired, been applied, or been lost after restart. Re-run prepare.')}`,
|
|
2736
|
-
].join('\n'));
|
|
2779
|
+
].join('\n'), mode);
|
|
2737
2780
|
}
|
|
2738
2781
|
if (plan.plannedBy !== caller.id) {
|
|
2739
2782
|
return failYaml([
|
|
@@ -2744,10 +2787,10 @@ exports.applyOccurrenceReplaceTool = {
|
|
|
2744
2787
|
`summary: ${yamlQuote(language === 'zh'
|
|
2745
2788
|
? '该 plan 不是由当前成员创建,不能应用。'
|
|
2746
2789
|
: 'This plan was created by a different member and cannot be applied by this caller.')}`,
|
|
2747
|
-
].join('\n'));
|
|
2790
|
+
].join('\n'), mode);
|
|
2748
2791
|
}
|
|
2749
2792
|
if (!(0, access_control_1.hasWriteAccess)(caller, plan.relPath)) {
|
|
2750
|
-
return (
|
|
2793
|
+
return limitedToolFailure((0, access_control_1.getAccessDeniedMessage)('write', plan.relPath, language), mode);
|
|
2751
2794
|
}
|
|
2752
2795
|
const res = await new Promise((resolve) => {
|
|
2753
2796
|
enqueueFileApply(plan.absPath, {
|
|
@@ -2766,7 +2809,7 @@ exports.applyOccurrenceReplaceTool = {
|
|
|
2766
2809
|
`summary: ${yamlQuote(language === 'zh'
|
|
2767
2810
|
? 'plan_id 不存在,可能已过期或已应用。请重新 prepare。'
|
|
2768
2811
|
: 'plan_id was not found; it may have expired or been applied. Re-run prepare.')}`,
|
|
2769
|
-
].join('\n')));
|
|
2812
|
+
].join('\n'), mode));
|
|
2770
2813
|
return;
|
|
2771
2814
|
}
|
|
2772
2815
|
if (!fs_1.default.existsSync(currentPlan.absPath)) {
|
|
@@ -2777,7 +2820,7 @@ exports.applyOccurrenceReplaceTool = {
|
|
|
2777
2820
|
`plan_id: ${yamlQuote(planId)}`,
|
|
2778
2821
|
`error: FILE_NOT_FOUND`,
|
|
2779
2822
|
`summary: ${yamlQuote(language === 'zh' ? '文件不存在。' : 'File not found.')}`,
|
|
2780
|
-
].join('\n')));
|
|
2823
|
+
].join('\n'), mode));
|
|
2781
2824
|
return;
|
|
2782
2825
|
}
|
|
2783
2826
|
if (!fs_1.default.statSync(currentPlan.absPath).isFile()) {
|
|
@@ -2788,7 +2831,7 @@ exports.applyOccurrenceReplaceTool = {
|
|
|
2788
2831
|
`plan_id: ${yamlQuote(planId)}`,
|
|
2789
2832
|
`error: NOT_A_FILE`,
|
|
2790
2833
|
`summary: ${yamlQuote(language === 'zh' ? '路径不是文件。' : 'Path is not a file.')}`,
|
|
2791
|
-
].join('\n')));
|
|
2834
|
+
].join('\n'), mode));
|
|
2792
2835
|
return;
|
|
2793
2836
|
}
|
|
2794
2837
|
const currentContent = fs_1.default.readFileSync(currentPlan.absPath, 'utf8');
|
|
@@ -2805,7 +2848,7 @@ exports.applyOccurrenceReplaceTool = {
|
|
|
2805
2848
|
`summary: ${yamlQuote(language === 'zh'
|
|
2806
2849
|
? '文件在 prepare 后发生变化,拒绝应用;请重新 prepare。'
|
|
2807
2850
|
: 'File changed since prepare; refusing to apply. Re-run prepare.')}`,
|
|
2808
|
-
].join('\n')));
|
|
2851
|
+
].join('\n'), mode));
|
|
2809
2852
|
return;
|
|
2810
2853
|
}
|
|
2811
2854
|
const occurrenceOffsets = findLiteralOccurrenceIndexes(currentContent, currentPlan.findText);
|
|
@@ -2819,18 +2862,20 @@ exports.applyOccurrenceReplaceTool = {
|
|
|
2819
2862
|
`plan_id: ${yamlQuote(planId)}`,
|
|
2820
2863
|
];
|
|
2821
2864
|
pushOccurrenceReplaceSourceYaml(yamlLines, currentPlan.source, showDiff);
|
|
2822
|
-
yamlLines.push(`find: ${yamlQuote(currentPlan.findText)}
|
|
2865
|
+
yamlLines.push(`find: ${yamlQuote(currentPlan.findText)}`);
|
|
2866
|
+
pushOccurrenceIndexListYaml(yamlLines, 'selected_occurrences', currentPlan.selectedOccurrences);
|
|
2867
|
+
yamlLines.push(`selected_count: ${currentPlan.selectedOccurrences.length}`, `file:`, ` old_total_lines: ${currentPlan.oldTotalLines}`, ` old_total_bytes: ${currentPlan.oldTotalBytes}`, ` old_hash: ${yamlQuote(currentPlan.oldFileHash)}`, ` new_total_lines: ${currentPlan.newTotalLines}`, ` new_total_bytes: ${currentPlan.newTotalBytes}`, ` new_hash: ${yamlQuote(currentPlan.newFileHash)}`, `summary: ${yamlQuote(language === 'zh'
|
|
2823
2868
|
? `已应用 ${currentPlan.selectedOccurrences.length} 个 occurrence 的替换。`
|
|
2824
2869
|
: `Applied replacement for ${currentPlan.selectedOccurrences.length} occurrence(s).`)}`);
|
|
2825
2870
|
const yaml = yamlLines.join('\n');
|
|
2826
2871
|
if (!showDiff) {
|
|
2827
|
-
resolve(okYaml(yaml));
|
|
2872
|
+
resolve(okYaml(yaml, mode));
|
|
2828
2873
|
return;
|
|
2829
2874
|
}
|
|
2830
2875
|
const oldLines = splitTextToLinesForEditing(currentContent);
|
|
2831
2876
|
const newLines = splitTextToLinesForEditing(nextContent);
|
|
2832
2877
|
const diff = buildUnifiedSingleHunkDiff(currentPlan.relPath, oldLines, 0, oldLines.length, newLines);
|
|
2833
|
-
resolve((
|
|
2878
|
+
resolve(limitedToolSuccess(`${formatYamlCodeBlock(yaml)}\n\n\`\`\`diff\n${diff}\`\`\``, mode));
|
|
2834
2879
|
}
|
|
2835
2880
|
catch (error) {
|
|
2836
2881
|
resolve(failYaml([
|
|
@@ -2839,7 +2884,7 @@ exports.applyOccurrenceReplaceTool = {
|
|
|
2839
2884
|
`plan_id: ${yamlQuote(planId)}`,
|
|
2840
2885
|
`error: FAILED`,
|
|
2841
2886
|
`summary: ${yamlQuote(error instanceof Error ? error.message : String(error))}`,
|
|
2842
|
-
].join('\n')));
|
|
2887
|
+
].join('\n'), mode));
|
|
2843
2888
|
}
|
|
2844
2889
|
},
|
|
2845
2890
|
});
|
|
@@ -2853,7 +2898,7 @@ exports.applyOccurrenceReplaceTool = {
|
|
|
2853
2898
|
`mode: ${mode}`,
|
|
2854
2899
|
`error: INVALID_ARGS`,
|
|
2855
2900
|
`summary: ${yamlQuote(error instanceof Error ? error.message : String(error))}`,
|
|
2856
|
-
].join('\n'));
|
|
2901
|
+
].join('\n'), mode);
|
|
2857
2902
|
}
|
|
2858
2903
|
},
|
|
2859
2904
|
};
|
|
@@ -3045,11 +3090,11 @@ async function runFileAppend(caller, filePath, source, options) {
|
|
|
3045
3090
|
`error: PATH_REQUIRED`,
|
|
3046
3091
|
`summary: ${yamlQuote(language === 'zh' ? '需要提供文件路径。' : 'File path is required.')}`,
|
|
3047
3092
|
].join('\n'));
|
|
3048
|
-
return failed(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
3093
|
+
return failed(content, [{ type: 'environment_msg', role: 'user', content }], mode);
|
|
3049
3094
|
}
|
|
3050
3095
|
if (!(0, access_control_1.hasWriteAccess)(caller, filePath)) {
|
|
3051
3096
|
const content = (0, access_control_1.getAccessDeniedMessage)('write', filePath, language);
|
|
3052
|
-
return failed(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
3097
|
+
return failed(content, [{ type: 'environment_msg', role: 'user', content }], mode);
|
|
3053
3098
|
}
|
|
3054
3099
|
if (inputBody === '') {
|
|
3055
3100
|
const content = formatYamlCodeBlock([
|
|
@@ -3059,7 +3104,7 @@ async function runFileAppend(caller, filePath, source, options) {
|
|
|
3059
3104
|
`error: CONTENT_REQUIRED`,
|
|
3060
3105
|
`summary: ${yamlQuote(language === 'zh' ? '正文不能为空(需要提供要追加的内容)。' : 'Content is required.')}`,
|
|
3061
3106
|
].join('\n'));
|
|
3062
|
-
return failed(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
3107
|
+
return failed(content, [{ type: 'environment_msg', role: 'user', content }], mode);
|
|
3063
3108
|
}
|
|
3064
3109
|
const create = options.create;
|
|
3065
3110
|
const preview = options.preview;
|
|
@@ -3084,7 +3129,7 @@ async function runFileAppend(caller, filePath, source, options) {
|
|
|
3084
3129
|
? '文件不存在(create=false),无法追加。'
|
|
3085
3130
|
: 'File does not exist (create=false); cannot append.')}`,
|
|
3086
3131
|
].join('\n'));
|
|
3087
|
-
resolve(failed(content, [{ type: 'environment_msg', role: 'user', content }]));
|
|
3132
|
+
resolve(failed(content, [{ type: 'environment_msg', role: 'user', content }], mode));
|
|
3088
3133
|
return;
|
|
3089
3134
|
}
|
|
3090
3135
|
if (fileExists && !fs_1.default.statSync(fullPath).isFile()) {
|
|
@@ -3095,7 +3140,7 @@ async function runFileAppend(caller, filePath, source, options) {
|
|
|
3095
3140
|
`error: NOT_A_FILE`,
|
|
3096
3141
|
`summary: ${yamlQuote(language === 'zh' ? '路径不是文件。' : 'Path is not a file.')}`,
|
|
3097
3142
|
].join('\n'));
|
|
3098
|
-
resolve(failed(content, [{ type: 'environment_msg', role: 'user', content }]));
|
|
3143
|
+
resolve(failed(content, [{ type: 'environment_msg', role: 'user', content }], mode));
|
|
3099
3144
|
return;
|
|
3100
3145
|
}
|
|
3101
3146
|
const existingContent = fileExists ? fs_1.default.readFileSync(fullPath, 'utf8') : '';
|
|
@@ -3160,7 +3205,7 @@ async function runFileAppend(caller, filePath, source, options) {
|
|
|
3160
3205
|
const content = showDiff
|
|
3161
3206
|
? `${formatYamlCodeBlock(yaml)}\n\n\`\`\`diff\n${unifiedDiff}\`\`\``
|
|
3162
3207
|
: formatYamlCodeBlock(yaml);
|
|
3163
|
-
resolve(ok(content, [{ type: 'environment_msg', role: 'user', content }]));
|
|
3208
|
+
resolve(ok(content, [{ type: 'environment_msg', role: 'user', content }], mode));
|
|
3164
3209
|
}
|
|
3165
3210
|
catch (error) {
|
|
3166
3211
|
const content = formatYamlCodeBlock([
|
|
@@ -3170,7 +3215,7 @@ async function runFileAppend(caller, filePath, source, options) {
|
|
|
3170
3215
|
`error: FAILED`,
|
|
3171
3216
|
`summary: ${yamlQuote(error instanceof Error ? error.message : String(error))}`,
|
|
3172
3217
|
].join('\n'));
|
|
3173
|
-
resolve(failed(content, [{ type: 'environment_msg', role: 'user', content }]));
|
|
3218
|
+
resolve(failed(content, [{ type: 'environment_msg', role: 'user', content }], mode));
|
|
3174
3219
|
}
|
|
3175
3220
|
},
|
|
3176
3221
|
});
|
|
@@ -3186,7 +3231,7 @@ async function runFileAppend(caller, filePath, source, options) {
|
|
|
3186
3231
|
`error: FAILED`,
|
|
3187
3232
|
`summary: ${yamlQuote(error instanceof Error ? error.message : String(error))}`,
|
|
3188
3233
|
].join('\n'));
|
|
3189
|
-
return failed(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
3234
|
+
return failed(content, [{ type: 'environment_msg', role: 'user', content }], mode);
|
|
3190
3235
|
}
|
|
3191
3236
|
}
|
|
3192
3237
|
async function runFileInsertionCommon(position, caller, options) {
|
|
@@ -3209,11 +3254,11 @@ async function runFileInsertionCommon(position, caller, options) {
|
|
|
3209
3254
|
? `需要提供 path 与 anchor。请调用函数工具:${mode}({ path, anchor, content, ...options })`
|
|
3210
3255
|
: `path and anchor are required. Call the function tool: ${mode}({ path, anchor, content, ...options })`)}`,
|
|
3211
3256
|
].join('\n'));
|
|
3212
|
-
return failed(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
3257
|
+
return failed(content, [{ type: 'environment_msg', role: 'user', content }], mode);
|
|
3213
3258
|
}
|
|
3214
3259
|
if (!(0, access_control_1.hasWriteAccess)(caller, filePath)) {
|
|
3215
3260
|
const content = (0, access_control_1.getAccessDeniedMessage)('write', filePath, language);
|
|
3216
|
-
return failed(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
3261
|
+
return failed(content, [{ type: 'environment_msg', role: 'user', content }], mode);
|
|
3217
3262
|
}
|
|
3218
3263
|
if (inputBody === '') {
|
|
3219
3264
|
const content = formatYamlCodeBlock([
|
|
@@ -3225,7 +3270,7 @@ async function runFileInsertionCommon(position, caller, options) {
|
|
|
3225
3270
|
? '正文不能为空(需要提供要插入的内容)。'
|
|
3226
3271
|
: 'Content is required in the body.')}`,
|
|
3227
3272
|
].join('\n'));
|
|
3228
|
-
return failed(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
3273
|
+
return failed(content, [{ type: 'environment_msg', role: 'user', content }], mode);
|
|
3229
3274
|
}
|
|
3230
3275
|
try {
|
|
3231
3276
|
const fullPath = ensureInsideWorkspace(filePath);
|
|
@@ -3244,7 +3289,7 @@ async function runFileInsertionCommon(position, caller, options) {
|
|
|
3244
3289
|
`error: FILE_NOT_FOUND`,
|
|
3245
3290
|
`summary: ${yamlQuote(language === 'zh' ? '文件不存在,无法插入。' : 'File does not exist.')}`,
|
|
3246
3291
|
].join('\n'));
|
|
3247
|
-
resolve(failed(content, [{ type: 'environment_msg', role: 'user', content }]));
|
|
3292
|
+
resolve(failed(content, [{ type: 'environment_msg', role: 'user', content }], mode));
|
|
3248
3293
|
return;
|
|
3249
3294
|
}
|
|
3250
3295
|
if (!fs_1.default.statSync(fullPath).isFile()) {
|
|
@@ -3255,7 +3300,7 @@ async function runFileInsertionCommon(position, caller, options) {
|
|
|
3255
3300
|
`error: NOT_A_FILE`,
|
|
3256
3301
|
`summary: ${yamlQuote(language === 'zh' ? '路径不是文件。' : 'Path is not a file.')}`,
|
|
3257
3302
|
].join('\n'));
|
|
3258
|
-
resolve(failed(content, [{ type: 'environment_msg', role: 'user', content }]));
|
|
3303
|
+
resolve(failed(content, [{ type: 'environment_msg', role: 'user', content }], mode));
|
|
3259
3304
|
return;
|
|
3260
3305
|
}
|
|
3261
3306
|
const existingContent = fs_1.default.readFileSync(fullPath, 'utf8');
|
|
@@ -3281,7 +3326,7 @@ async function runFileInsertionCommon(position, caller, options) {
|
|
|
3281
3326
|
? '锚点出现多次且未指定 occurrence;拒绝写入。请指定 occurrence 或改用 file_range_edit。'
|
|
3282
3327
|
: 'Anchor appears multiple times and occurrence is not specified; refusing to write. Specify occurrence or use file_range_edit.')}`,
|
|
3283
3328
|
].join('\n'));
|
|
3284
|
-
resolve(failed(content, [{ type: 'environment_msg', role: 'user', content }]));
|
|
3329
|
+
resolve(failed(content, [{ type: 'environment_msg', role: 'user', content }], mode));
|
|
3285
3330
|
return;
|
|
3286
3331
|
}
|
|
3287
3332
|
if (matchLines.length === 0) {
|
|
@@ -3295,7 +3340,7 @@ async function runFileInsertionCommon(position, caller, options) {
|
|
|
3295
3340
|
? '锚点未找到;请改用 file_range_edit 或选择更可靠的 anchor。'
|
|
3296
3341
|
: 'Anchor not found; use file_range_edit or choose a different anchor.')}`,
|
|
3297
3342
|
].join('\n'));
|
|
3298
|
-
resolve(failed(content, [{ type: 'environment_msg', role: 'user', content }]));
|
|
3343
|
+
resolve(failed(content, [{ type: 'environment_msg', role: 'user', content }], mode));
|
|
3299
3344
|
return;
|
|
3300
3345
|
}
|
|
3301
3346
|
const anchorIndex0 = occurrence.kind === 'last'
|
|
@@ -3311,7 +3356,7 @@ async function runFileInsertionCommon(position, caller, options) {
|
|
|
3311
3356
|
`candidates_count: ${matchLines.length}`,
|
|
3312
3357
|
`summary: ${yamlQuote(language === 'zh' ? 'occurrence 超出范围。' : 'Occurrence out of range.')}`,
|
|
3313
3358
|
].join('\n'));
|
|
3314
|
-
resolve(failed(content, [{ type: 'environment_msg', role: 'user', content }]));
|
|
3359
|
+
resolve(failed(content, [{ type: 'environment_msg', role: 'user', content }], mode));
|
|
3315
3360
|
return;
|
|
3316
3361
|
}
|
|
3317
3362
|
const occurrenceResolved = matchLines.length === 1
|
|
@@ -3379,7 +3424,7 @@ async function runFileInsertionCommon(position, caller, options) {
|
|
|
3379
3424
|
const content = showDiff
|
|
3380
3425
|
? `${formatYamlCodeBlock(yaml)}\n\n\`\`\`diff\n${unifiedDiff}\`\`\``
|
|
3381
3426
|
: formatYamlCodeBlock(yaml);
|
|
3382
|
-
resolve(ok(content, [{ type: 'environment_msg', role: 'user', content }]));
|
|
3427
|
+
resolve(ok(content, [{ type: 'environment_msg', role: 'user', content }], mode));
|
|
3383
3428
|
}
|
|
3384
3429
|
catch (error) {
|
|
3385
3430
|
const content = formatYamlCodeBlock([
|
|
@@ -3389,7 +3434,7 @@ async function runFileInsertionCommon(position, caller, options) {
|
|
|
3389
3434
|
`error: FAILED`,
|
|
3390
3435
|
`summary: ${yamlQuote(error instanceof Error ? error.message : String(error))}`,
|
|
3391
3436
|
].join('\n'));
|
|
3392
|
-
resolve(failed(content, [{ type: 'environment_msg', role: 'user', content }]));
|
|
3437
|
+
resolve(failed(content, [{ type: 'environment_msg', role: 'user', content }], mode));
|
|
3393
3438
|
}
|
|
3394
3439
|
},
|
|
3395
3440
|
});
|
|
@@ -3405,7 +3450,7 @@ async function runFileInsertionCommon(position, caller, options) {
|
|
|
3405
3450
|
`error: FAILED`,
|
|
3406
3451
|
`summary: ${yamlQuote(error instanceof Error ? error.message : String(error))}`,
|
|
3407
3452
|
].join('\n'));
|
|
3408
|
-
return failed(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
3453
|
+
return failed(content, [{ type: 'environment_msg', role: 'user', content }], mode);
|
|
3409
3454
|
}
|
|
3410
3455
|
}
|
|
3411
3456
|
exports.fileInsertAfterTool = {
|
|
@@ -3610,11 +3655,11 @@ async function runFileBlockReplace(caller, options) {
|
|
|
3610
3655
|
? '需要提供 path、start_anchor、end_anchor。'
|
|
3611
3656
|
: 'path, start_anchor, and end_anchor are required.')}`,
|
|
3612
3657
|
].join('\n'));
|
|
3613
|
-
return failed(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
3658
|
+
return failed(content, [{ type: 'environment_msg', role: 'user', content }], mode);
|
|
3614
3659
|
}
|
|
3615
3660
|
if (!(0, access_control_1.hasWriteAccess)(caller, filePath)) {
|
|
3616
3661
|
const content = (0, access_control_1.getAccessDeniedMessage)('write', filePath, language);
|
|
3617
|
-
return failed(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
3662
|
+
return failed(content, [{ type: 'environment_msg', role: 'user', content }], mode);
|
|
3618
3663
|
}
|
|
3619
3664
|
if (inputBody === '') {
|
|
3620
3665
|
const content = formatYamlCodeBlock([
|
|
@@ -3626,7 +3671,7 @@ async function runFileBlockReplace(caller, options) {
|
|
|
3626
3671
|
? '正文不能为空(需要提供要写入块内的新内容)。'
|
|
3627
3672
|
: 'Content is required in the body (new block content).')}`,
|
|
3628
3673
|
].join('\n'));
|
|
3629
|
-
return failed(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
3674
|
+
return failed(content, [{ type: 'environment_msg', role: 'user', content }], mode);
|
|
3630
3675
|
}
|
|
3631
3676
|
try {
|
|
3632
3677
|
const fullPath = ensureInsideWorkspace(filePath);
|
|
@@ -3645,7 +3690,7 @@ async function runFileBlockReplace(caller, options) {
|
|
|
3645
3690
|
`error: FILE_NOT_FOUND`,
|
|
3646
3691
|
`summary: ${yamlQuote(language === 'zh' ? '文件不存在。' : 'File does not exist.')}`,
|
|
3647
3692
|
].join('\n'));
|
|
3648
|
-
resolve(failed(content, [{ type: 'environment_msg', role: 'user', content }]));
|
|
3693
|
+
resolve(failed(content, [{ type: 'environment_msg', role: 'user', content }], mode));
|
|
3649
3694
|
return;
|
|
3650
3695
|
}
|
|
3651
3696
|
if (!fs_1.default.statSync(fullPath).isFile()) {
|
|
@@ -3656,7 +3701,7 @@ async function runFileBlockReplace(caller, options) {
|
|
|
3656
3701
|
`error: NOT_A_FILE`,
|
|
3657
3702
|
`summary: ${yamlQuote(language === 'zh' ? '路径不是文件。' : 'Path is not a file.')}`,
|
|
3658
3703
|
].join('\n'));
|
|
3659
|
-
resolve(failed(content, [{ type: 'environment_msg', role: 'user', content }]));
|
|
3704
|
+
resolve(failed(content, [{ type: 'environment_msg', role: 'user', content }], mode));
|
|
3660
3705
|
return;
|
|
3661
3706
|
}
|
|
3662
3707
|
const existing = fs_1.default.readFileSync(fullPath, 'utf8');
|
|
@@ -3695,7 +3740,7 @@ async function runFileBlockReplace(caller, options) {
|
|
|
3695
3740
|
? '锚点未找到或无法配对。请改用 file_range_edit(行号范围精确编辑)。'
|
|
3696
3741
|
: 'Anchors not found or not paired. Use file_range_edit (line-range precise edits).')}`,
|
|
3697
3742
|
].join('\n'));
|
|
3698
|
-
resolve(failed(content, [{ type: 'environment_msg', role: 'user', content }]));
|
|
3743
|
+
resolve(failed(content, [{ type: 'environment_msg', role: 'user', content }], mode));
|
|
3699
3744
|
return;
|
|
3700
3745
|
}
|
|
3701
3746
|
if (!occurrenceSpecified && requireUnique && candidatesCount !== 1 && strict) {
|
|
@@ -3711,7 +3756,7 @@ async function runFileBlockReplace(caller, options) {
|
|
|
3711
3756
|
? `锚点歧义:存在 ${candidatesCount} 个候选块。请指定 occurrence=<n|last>,或改用 file_range_edit(行号范围)。`
|
|
3712
3757
|
: `Ambiguous anchors: ${candidatesCount} candidate block(s). Specify occurrence=<n|last>, or use file_range_edit (line range).`)}`,
|
|
3713
3758
|
].join('\n'));
|
|
3714
|
-
resolve(failed(content, [{ type: 'environment_msg', role: 'user', content }]));
|
|
3759
|
+
resolve(failed(content, [{ type: 'environment_msg', role: 'user', content }], mode));
|
|
3715
3760
|
return;
|
|
3716
3761
|
}
|
|
3717
3762
|
const selected = (() => {
|
|
@@ -3732,7 +3777,7 @@ async function runFileBlockReplace(caller, options) {
|
|
|
3732
3777
|
`error: OCCURRENCE_OUT_OF_RANGE`,
|
|
3733
3778
|
`summary: ${yamlQuote(language === 'zh' ? 'occurrence 超出范围。' : 'occurrence is out of range.')}`,
|
|
3734
3779
|
].join('\n'));
|
|
3735
|
-
resolve(failed(content, [{ type: 'environment_msg', role: 'user', content }]));
|
|
3780
|
+
resolve(failed(content, [{ type: 'environment_msg', role: 'user', content }], mode));
|
|
3736
3781
|
return;
|
|
3737
3782
|
}
|
|
3738
3783
|
const nestedStart = startMatches.some((s) => s > selected.start0 && s < selected.end0);
|
|
@@ -3750,7 +3795,7 @@ async function runFileBlockReplace(caller, options) {
|
|
|
3750
3795
|
? '检测到嵌套/歧义锚点,拒绝写入。请先规范 anchors,或改用 file_range_edit(行号范围)。'
|
|
3751
3796
|
: 'Nested/ambiguous anchors detected. Refusing to write; normalize anchors or use file_range_edit (line range).')}`,
|
|
3752
3797
|
].join('\n'));
|
|
3753
|
-
resolve(failed(content, [{ type: 'environment_msg', role: 'user', content }]));
|
|
3798
|
+
resolve(failed(content, [{ type: 'environment_msg', role: 'user', content }], mode));
|
|
3754
3799
|
return;
|
|
3755
3800
|
}
|
|
3756
3801
|
const occurrenceResolved = candidatesCount === 1
|
|
@@ -3798,7 +3843,7 @@ async function runFileBlockReplace(caller, options) {
|
|
|
3798
3843
|
const content = showDiff
|
|
3799
3844
|
? `${formatYamlCodeBlock(yaml)}\n\n\`\`\`diff\n${unifiedDiff}\`\`\``
|
|
3800
3845
|
: formatYamlCodeBlock(yaml);
|
|
3801
|
-
resolve(ok(content, [{ type: 'environment_msg', role: 'user', content }]));
|
|
3846
|
+
resolve(ok(content, [{ type: 'environment_msg', role: 'user', content }], mode));
|
|
3802
3847
|
}
|
|
3803
3848
|
catch (error) {
|
|
3804
3849
|
const content = formatYamlCodeBlock([
|
|
@@ -3808,7 +3853,7 @@ async function runFileBlockReplace(caller, options) {
|
|
|
3808
3853
|
`error: FAILED`,
|
|
3809
3854
|
`summary: ${yamlQuote(error instanceof Error ? error.message : String(error))}`,
|
|
3810
3855
|
].join('\n'));
|
|
3811
|
-
resolve(failed(content, [{ type: 'environment_msg', role: 'user', content }]));
|
|
3856
|
+
resolve(failed(content, [{ type: 'environment_msg', role: 'user', content }], mode));
|
|
3812
3857
|
}
|
|
3813
3858
|
},
|
|
3814
3859
|
});
|
|
@@ -3824,7 +3869,7 @@ async function runFileBlockReplace(caller, options) {
|
|
|
3824
3869
|
`error: FAILED`,
|
|
3825
3870
|
`summary: ${yamlQuote(error instanceof Error ? error.message : String(error))}`,
|
|
3826
3871
|
].join('\n'));
|
|
3827
|
-
return failed(content, [{ type: 'environment_msg', role: 'user', content }]);
|
|
3872
|
+
return failed(content, [{ type: 'environment_msg', role: 'user', content }], mode);
|
|
3828
3873
|
}
|
|
3829
3874
|
}
|
|
3830
3875
|
exports.fileBlockReplaceTool = {
|