@librechat/agents 3.2.39 → 3.2.41
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/cjs/hooks/createWorkspacePolicyHook.cjs +4 -3
- package/dist/cjs/hooks/createWorkspacePolicyHook.cjs.map +1 -1
- package/dist/cjs/tools/ReadFile.cjs +2 -2
- package/dist/cjs/tools/ReadFile.cjs.map +1 -1
- package/dist/cjs/tools/cloudflare/CloudflareProgrammaticToolCalling.cjs +11 -11
- package/dist/cjs/tools/cloudflare/CloudflareProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/local/LocalCodingTools.cjs +11 -11
- package/dist/cjs/tools/local/LocalCodingTools.cjs.map +1 -1
- package/dist/esm/hooks/createWorkspacePolicyHook.mjs +4 -3
- package/dist/esm/hooks/createWorkspacePolicyHook.mjs.map +1 -1
- package/dist/esm/tools/ReadFile.mjs +2 -2
- package/dist/esm/tools/ReadFile.mjs.map +1 -1
- package/dist/esm/tools/cloudflare/CloudflareProgrammaticToolCalling.mjs +11 -11
- package/dist/esm/tools/cloudflare/CloudflareProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/local/LocalCodingTools.mjs +11 -11
- package/dist/esm/tools/local/LocalCodingTools.mjs.map +1 -1
- package/dist/types/tools/ReadFile.d.ts +4 -4
- package/package.json +1 -1
- package/src/hooks/__tests__/createWorkspacePolicyHook.test.ts +12 -12
- package/src/hooks/createWorkspacePolicyHook.ts +7 -6
- package/src/tools/ReadFile.ts +2 -2
- package/src/tools/__tests__/LocalExecutionTools.test.ts +25 -25
- package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +5 -5
- package/src/tools/__tests__/ReadFile.test.ts +3 -3
- package/src/tools/__tests__/ToolNode.session.test.ts +2 -2
- package/src/tools/__tests__/workspaceSeam.test.ts +2 -2
- package/src/tools/cloudflare/CloudflareProgrammaticToolCalling.ts +11 -11
- package/src/tools/local/LocalCodingTools.ts +14 -14
|
@@ -175,8 +175,8 @@ describe('local execution tools', () => {
|
|
|
175
175
|
aiMessageWithToolCall(Constants.PROGRAMMATIC_TOOL_CALLING, {
|
|
176
176
|
lang: 'py',
|
|
177
177
|
code: [
|
|
178
|
-
'await write_file(
|
|
179
|
-
'contents = await read_file(
|
|
178
|
+
'await write_file(path="ptc.txt", content="from local ptc")',
|
|
179
|
+
'contents = await read_file(path="ptc.txt")',
|
|
180
180
|
'print(contents)',
|
|
181
181
|
].join('\n'),
|
|
182
182
|
}),
|
|
@@ -205,8 +205,8 @@ describe('local execution tools', () => {
|
|
|
205
205
|
messages: [
|
|
206
206
|
aiMessageWithToolCall(Constants.PROGRAMMATIC_TOOL_CALLING, {
|
|
207
207
|
code: [
|
|
208
|
-
'write_file \'{"
|
|
209
|
-
'read_file \'{"
|
|
208
|
+
'write_file \'{"path":"bash-ptc.txt","content":"from bash ptc"}\'',
|
|
209
|
+
'read_file \'{"path":"bash-ptc.txt"}\'',
|
|
210
210
|
].join('\n'),
|
|
211
211
|
}),
|
|
212
212
|
],
|
|
@@ -335,8 +335,8 @@ describe('LocalFileCheckpointer', () => {
|
|
|
335
335
|
|
|
336
336
|
const writeTool = bundle.tools.find((tool_) => tool_.name === 'write_file');
|
|
337
337
|
expect(writeTool).toBeDefined();
|
|
338
|
-
await writeTool!.invoke({
|
|
339
|
-
await writeTool!.invoke({
|
|
338
|
+
await writeTool!.invoke({ path: 'cp.txt', content: 'first' });
|
|
339
|
+
await writeTool!.invoke({ path: 'cp.txt', content: 'second' });
|
|
340
340
|
|
|
341
341
|
const restored = await bundle.checkpointer!.rewind();
|
|
342
342
|
expect(restored).toBe(1);
|
|
@@ -352,7 +352,7 @@ describe('local read tool guards', () => {
|
|
|
352
352
|
|
|
353
353
|
const bundle = createLocalCodingToolBundle({ cwd });
|
|
354
354
|
const readTool = bundle.tools.find((t_) => t_.name === Constants.READ_FILE);
|
|
355
|
-
const result = await readTool!.invoke({
|
|
355
|
+
const result = await readTool!.invoke({ path: 'binary.bin' });
|
|
356
356
|
expect(String(result)).toContain('binary file');
|
|
357
357
|
});
|
|
358
358
|
|
|
@@ -366,7 +366,7 @@ describe('local read tool guards', () => {
|
|
|
366
366
|
maxReadBytes: 1024,
|
|
367
367
|
});
|
|
368
368
|
const readTool = bundle.tools.find((t_) => t_.name === Constants.READ_FILE);
|
|
369
|
-
const result = await readTool!.invoke({
|
|
369
|
+
const result = await readTool!.invoke({ path: 'big.txt' });
|
|
370
370
|
expect(String(result)).toContain('exceeds the 1024-byte read cap');
|
|
371
371
|
});
|
|
372
372
|
|
|
@@ -380,7 +380,7 @@ describe('local read tool guards', () => {
|
|
|
380
380
|
const bundle = createLocalCodingToolBundle({ cwd });
|
|
381
381
|
const readTool = bundle.tools.find((t_) => t_.name === Constants.READ_FILE);
|
|
382
382
|
await expect(
|
|
383
|
-
readTool!.invoke({
|
|
383
|
+
readTool!.invoke({ path: 'escape/secret.txt' })
|
|
384
384
|
).rejects.toThrow(/symlink escape/);
|
|
385
385
|
});
|
|
386
386
|
});
|
|
@@ -406,7 +406,7 @@ describe('local programmatic bridge auth', () => {
|
|
|
406
406
|
code: [
|
|
407
407
|
'import os, json, urllib.request, urllib.error',
|
|
408
408
|
'url = os.environ["BRIDGE_PROBE_URL"] if "BRIDGE_PROBE_URL" in os.environ else __LIBRECHAT_TOOL_BRIDGE',
|
|
409
|
-
'body = json.dumps({"name":"read_file","input":{"
|
|
409
|
+
'body = json.dumps({"name":"read_file","input":{"path":"x"}}).encode("utf-8")',
|
|
410
410
|
'try:',
|
|
411
411
|
' req = urllib.request.Request(url, data=body, headers={"Content-Type":"application/json"}, method="POST")',
|
|
412
412
|
' urllib.request.urlopen(req, timeout=5)',
|
|
@@ -438,7 +438,7 @@ describe('local edit fuzzy matching', () => {
|
|
|
438
438
|
const bundle = createLocalCodingToolBundle({ cwd });
|
|
439
439
|
const editTool = bundle.tools.find((tt) => tt.name === 'edit_file');
|
|
440
440
|
const result = await editTool!.invoke({
|
|
441
|
-
|
|
441
|
+
path: 'a.ts',
|
|
442
442
|
// LLM emits a trailing-whitespace-stripped version.
|
|
443
443
|
old_text:
|
|
444
444
|
'function greet(name: string) {\n return `Hello, ${name}!`;\n}',
|
|
@@ -461,7 +461,7 @@ describe('local edit fuzzy matching', () => {
|
|
|
461
461
|
const bundle = createLocalCodingToolBundle({ cwd });
|
|
462
462
|
const editTool = bundle.tools.find((tt) => tt.name === 'edit_file');
|
|
463
463
|
const result = await editTool!.invoke({
|
|
464
|
-
|
|
464
|
+
path: 'a.ts',
|
|
465
465
|
// LLM stripped the 4-space indent
|
|
466
466
|
old_text: 'method() {\n return 1;\n}',
|
|
467
467
|
new_text: 'method() {\n return 42;\n}',
|
|
@@ -480,7 +480,7 @@ describe('local edit fuzzy matching', () => {
|
|
|
480
480
|
const bundle = createLocalCodingToolBundle({ cwd });
|
|
481
481
|
const editTool = bundle.tools.find((tt) => tt.name === 'edit_file');
|
|
482
482
|
const result = await editTool!.invoke({
|
|
483
|
-
|
|
483
|
+
path: 'a.txt',
|
|
484
484
|
old_text: 'second',
|
|
485
485
|
new_text: 'SECOND',
|
|
486
486
|
});
|
|
@@ -497,7 +497,7 @@ describe('local edit fuzzy matching', () => {
|
|
|
497
497
|
const bundle = createLocalCodingToolBundle({ cwd });
|
|
498
498
|
const editTool = bundle.tools.find((tt) => tt.name === 'edit_file');
|
|
499
499
|
await editTool!.invoke({
|
|
500
|
-
|
|
500
|
+
path: 'a.txt',
|
|
501
501
|
old_text: 'two',
|
|
502
502
|
new_text: 'TWO',
|
|
503
503
|
});
|
|
@@ -512,7 +512,7 @@ describe('local edit fuzzy matching', () => {
|
|
|
512
512
|
await fsWriteFile(file, BOM + 'hello\n', 'utf8');
|
|
513
513
|
const bundle = createLocalCodingToolBundle({ cwd });
|
|
514
514
|
const writeTool = bundle.tools.find((tt) => tt.name === 'write_file');
|
|
515
|
-
await writeTool!.invoke({
|
|
515
|
+
await writeTool!.invoke({ path: 'a.txt', content: 'goodbye\n' });
|
|
516
516
|
const raw = await fsReadFile(file, 'utf8');
|
|
517
517
|
expect(raw.startsWith(BOM)).toBe(true);
|
|
518
518
|
expect(raw.slice(1)).toBe('goodbye\n');
|
|
@@ -532,7 +532,7 @@ describe('local read attachments', () => {
|
|
|
532
532
|
await fsWriteFile(file, tinyPng);
|
|
533
533
|
const bundle = createLocalCodingToolBundle({ cwd });
|
|
534
534
|
const readTool = bundle.tools.find((tt) => tt.name === Constants.READ_FILE);
|
|
535
|
-
const result = await readTool!.invoke({
|
|
535
|
+
const result = await readTool!.invoke({ path: 'tiny.png' });
|
|
536
536
|
expect(String(result)).toContain('binary file');
|
|
537
537
|
});
|
|
538
538
|
|
|
@@ -552,7 +552,7 @@ describe('local read attachments', () => {
|
|
|
552
552
|
const message = (await readTool!.invoke({
|
|
553
553
|
id: 'call_image',
|
|
554
554
|
name: Constants.READ_FILE,
|
|
555
|
-
args: {
|
|
555
|
+
args: { path: 'tiny.png' },
|
|
556
556
|
type: 'tool_call',
|
|
557
557
|
})) as { content: unknown; artifact: unknown };
|
|
558
558
|
expect(Array.isArray(message.content)).toBe(true);
|
|
@@ -589,7 +589,7 @@ describe('local read attachments', () => {
|
|
|
589
589
|
maxAttachmentBytes: 100,
|
|
590
590
|
});
|
|
591
591
|
const readTool = bundle.tools.find((tt) => tt.name === Constants.READ_FILE);
|
|
592
|
-
const result = await readTool!.invoke({
|
|
592
|
+
const result = await readTool!.invoke({ path: 'big.png' });
|
|
593
593
|
expect(String(result)).toMatch(/Refusing to embed/);
|
|
594
594
|
});
|
|
595
595
|
|
|
@@ -602,7 +602,7 @@ describe('local read attachments', () => {
|
|
|
602
602
|
attachReadAttachments: 'images-only',
|
|
603
603
|
});
|
|
604
604
|
const readTool = bundle.tools.find((tt) => tt.name === Constants.READ_FILE);
|
|
605
|
-
const result = await readTool!.invoke({
|
|
605
|
+
const result = await readTool!.invoke({ path: 'a.txt' });
|
|
606
606
|
expect(String(result)).toContain('hello world');
|
|
607
607
|
});
|
|
608
608
|
});
|
|
@@ -662,7 +662,7 @@ describe('post-edit syntax check', () => {
|
|
|
662
662
|
const message = (await writeTool!.invoke({
|
|
663
663
|
id: 'call_w',
|
|
664
664
|
name: 'write_file',
|
|
665
|
-
args: {
|
|
665
|
+
args: { path: 'broken.js', content: 'function (\n' },
|
|
666
666
|
type: 'tool_call',
|
|
667
667
|
})) as { content: string; artifact: { syntax_error?: string } };
|
|
668
668
|
expect(message.content).toContain('[syntax-check warning');
|
|
@@ -680,7 +680,7 @@ describe('post-edit syntax check', () => {
|
|
|
680
680
|
writeTool!.invoke({
|
|
681
681
|
id: 'call_w',
|
|
682
682
|
name: 'write_file',
|
|
683
|
-
args: {
|
|
683
|
+
args: { path: 'broken.js', content: 'function (\n' },
|
|
684
684
|
type: 'tool_call',
|
|
685
685
|
})
|
|
686
686
|
).rejects.toThrow(/syntax check failed/);
|
|
@@ -850,7 +850,7 @@ describe('codex review fixes', () => {
|
|
|
850
850
|
const result = await readTool!.invoke({
|
|
851
851
|
id: 'c',
|
|
852
852
|
name: Constants.READ_FILE,
|
|
853
|
-
args: {
|
|
853
|
+
args: { path: join(parent, 'shared/lib.ts') },
|
|
854
854
|
type: 'tool_call',
|
|
855
855
|
});
|
|
856
856
|
expect(JSON.stringify(result)).toContain('X');
|
|
@@ -2253,7 +2253,7 @@ describe('comprehensive review (round 14) — Codex P1 #37 + P2 #38/#40/#41', ()
|
|
|
2253
2253
|
writeTool!.invoke({
|
|
2254
2254
|
id: 'wf-strict',
|
|
2255
2255
|
name: Constants.WRITE_FILE,
|
|
2256
|
-
args: {
|
|
2256
|
+
args: { path: file, content: 'function broken( {\n' },
|
|
2257
2257
|
type: 'tool_call',
|
|
2258
2258
|
})
|
|
2259
2259
|
).rejects.toThrow(/syntax check failed.*reverted/i);
|
|
@@ -2281,7 +2281,7 @@ describe('comprehensive review (round 14) — Codex P1 #37 + P2 #38/#40/#41', ()
|
|
|
2281
2281
|
writeTool!.invoke({
|
|
2282
2282
|
id: 'wf-strict-new',
|
|
2283
2283
|
name: Constants.WRITE_FILE,
|
|
2284
|
-
args: {
|
|
2284
|
+
args: { path: file, content: 'function broken( {\n' },
|
|
2285
2285
|
type: 'tool_call',
|
|
2286
2286
|
})
|
|
2287
2287
|
).rejects.toThrow(/syntax check failed.*reverted/i);
|
|
@@ -2308,7 +2308,7 @@ describe('comprehensive review (round 14) — Codex P1 #37 + P2 #38/#40/#41', ()
|
|
|
2308
2308
|
id: 'ef-strict',
|
|
2309
2309
|
name: Constants.EDIT_FILE,
|
|
2310
2310
|
args: {
|
|
2311
|
-
|
|
2311
|
+
path: file,
|
|
2312
2312
|
old_text: 'return 1;',
|
|
2313
2313
|
new_text: 'return broken(',
|
|
2314
2314
|
},
|
|
@@ -1503,10 +1503,10 @@ for member in team:
|
|
|
1503
1503
|
{ registry, runId: 'r1' },
|
|
1504
1504
|
'read_file',
|
|
1505
1505
|
'call_1',
|
|
1506
|
-
{
|
|
1506
|
+
{ path: '/tmp/x' }
|
|
1507
1507
|
);
|
|
1508
1508
|
expect(gate.denyReason).toBeUndefined();
|
|
1509
|
-
expect(gate.input).toEqual({
|
|
1509
|
+
expect(gate.input).toEqual({ path: '/tmp/x' });
|
|
1510
1510
|
});
|
|
1511
1511
|
|
|
1512
1512
|
it('applies updatedInput to the inner tool args', async () => {
|
|
@@ -1520,7 +1520,7 @@ for member in team:
|
|
|
1520
1520
|
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
1521
1521
|
async () => ({
|
|
1522
1522
|
decision: 'allow',
|
|
1523
|
-
updatedInput: {
|
|
1523
|
+
updatedInput: { path: '/tmp/rewritten' },
|
|
1524
1524
|
}),
|
|
1525
1525
|
],
|
|
1526
1526
|
});
|
|
@@ -1529,10 +1529,10 @@ for member in team:
|
|
|
1529
1529
|
{ registry, runId: 'r1' },
|
|
1530
1530
|
'read_file',
|
|
1531
1531
|
'call_1',
|
|
1532
|
-
{
|
|
1532
|
+
{ path: '/tmp/original' }
|
|
1533
1533
|
);
|
|
1534
1534
|
expect(gate.denyReason).toBeUndefined();
|
|
1535
|
-
expect(gate.input).toEqual({
|
|
1535
|
+
expect(gate.input).toEqual({ path: '/tmp/rewritten' });
|
|
1536
1536
|
});
|
|
1537
1537
|
|
|
1538
1538
|
it('treats `ask` as fail-closed deny (HITL not reachable from bridge)', async () => {
|
|
@@ -9,9 +9,9 @@ import { Constants } from '@/common';
|
|
|
9
9
|
|
|
10
10
|
describe('ReadFile', () => {
|
|
11
11
|
describe('schema structure', () => {
|
|
12
|
-
it('has
|
|
13
|
-
expect(ReadFileToolSchema.properties.
|
|
14
|
-
expect(ReadFileToolSchema.required).toContain('
|
|
12
|
+
it('has path as required string property', () => {
|
|
13
|
+
expect(ReadFileToolSchema.properties.path.type).toBe('string');
|
|
14
|
+
expect(ReadFileToolSchema.required).toContain('path');
|
|
15
15
|
});
|
|
16
16
|
|
|
17
17
|
it('is an object type schema', () => {
|
|
@@ -1021,7 +1021,7 @@ describe('ToolNode code execution session management', () => {
|
|
|
1021
1021
|
{
|
|
1022
1022
|
id: 'call_rf',
|
|
1023
1023
|
name: Constants.READ_FILE,
|
|
1024
|
-
args: {
|
|
1024
|
+
args: { path: '/mnt/data/data.csv' },
|
|
1025
1025
|
},
|
|
1026
1026
|
],
|
|
1027
1027
|
});
|
|
@@ -1062,7 +1062,7 @@ describe('ToolNode code execution session management', () => {
|
|
|
1062
1062
|
{
|
|
1063
1063
|
id: 'call_rf2',
|
|
1064
1064
|
name: Constants.READ_FILE,
|
|
1065
|
-
args: {
|
|
1065
|
+
args: { path: 'some-skill/notes.md' },
|
|
1066
1066
|
},
|
|
1067
1067
|
],
|
|
1068
1068
|
});
|
|
@@ -125,7 +125,7 @@ describe('workspace seam', () => {
|
|
|
125
125
|
await writeTool.invoke({
|
|
126
126
|
id: 'c1',
|
|
127
127
|
name: 'write_file',
|
|
128
|
-
args: {
|
|
128
|
+
args: { path: 'note.md', content: 'hi\n' },
|
|
129
129
|
type: 'tool_call',
|
|
130
130
|
});
|
|
131
131
|
expect(tracked.writeFile).toHaveBeenCalled();
|
|
@@ -137,7 +137,7 @@ describe('workspace seam', () => {
|
|
|
137
137
|
await readTool.invoke({
|
|
138
138
|
id: 'c2',
|
|
139
139
|
name: Constants.READ_FILE,
|
|
140
|
-
args: {
|
|
140
|
+
args: { path: 'note.md' },
|
|
141
141
|
type: 'tool_call',
|
|
142
142
|
});
|
|
143
143
|
expect(tracked.stat).toHaveBeenCalled();
|
|
@@ -478,30 +478,30 @@ async def execute_code(lang, code, args=None):
|
|
|
478
478
|
finally:
|
|
479
479
|
shutil.rmtree(temp_dir, ignore_errors=True)
|
|
480
480
|
|
|
481
|
-
async def read_file(
|
|
482
|
-
resolved = _resolve(
|
|
481
|
+
async def read_file(path, offset=None, limit=None):
|
|
482
|
+
resolved = _resolve(path)
|
|
483
483
|
with open(resolved, encoding="utf-8") as handle:
|
|
484
484
|
return _line_window(handle.read(), offset, limit)
|
|
485
485
|
|
|
486
|
-
async def write_file(
|
|
486
|
+
async def write_file(path, content):
|
|
487
487
|
_assert_writable("write_file")
|
|
488
|
-
resolved = _resolve(
|
|
488
|
+
resolved = _resolve(path)
|
|
489
489
|
os.makedirs(os.path.dirname(resolved), exist_ok=True)
|
|
490
490
|
existed = os.path.exists(resolved)
|
|
491
491
|
with open(resolved, "w", encoding="utf-8") as handle:
|
|
492
492
|
handle.write(content)
|
|
493
493
|
return f"{'Overwrote' if existed else 'Created'} {resolved} ({len(content)} chars)."
|
|
494
494
|
|
|
495
|
-
async def edit_file(
|
|
495
|
+
async def edit_file(path, old_text=None, new_text=None, edits=None):
|
|
496
496
|
_assert_writable("edit_file")
|
|
497
|
-
resolved = _resolve(
|
|
497
|
+
resolved = _resolve(path)
|
|
498
498
|
edits = edits or [{"old_text": old_text, "new_text": new_text}]
|
|
499
499
|
content = open(resolved, encoding="utf-8").read()
|
|
500
500
|
for edit in edits:
|
|
501
501
|
old = edit.get("old_text") or ""
|
|
502
502
|
new = edit.get("new_text") or ""
|
|
503
503
|
if content.count(old) != 1:
|
|
504
|
-
raise ValueError(f"Could not locate old_text exactly once in {
|
|
504
|
+
raise ValueError(f"Could not locate old_text exactly once in {path}")
|
|
505
505
|
content = content.replace(old, new, 1)
|
|
506
506
|
open(resolved, "w", encoding="utf-8").write(content)
|
|
507
507
|
return f"Applied {len(edits)} edit(s) to {resolved}."
|
|
@@ -881,13 +881,13 @@ async function execute_code(payload) {
|
|
|
881
881
|
}
|
|
882
882
|
|
|
883
883
|
async function read_file(payload) {
|
|
884
|
-
const resolved = resolvePath(payload.
|
|
884
|
+
const resolved = resolvePath(payload.path);
|
|
885
885
|
return lineWindow(await fsp.readFile(resolved, "utf8"), payload.offset, payload.limit);
|
|
886
886
|
}
|
|
887
887
|
|
|
888
888
|
async function write_file(payload) {
|
|
889
889
|
assertWritable("write_file");
|
|
890
|
-
const resolved = resolvePath(payload.
|
|
890
|
+
const resolved = resolvePath(payload.path);
|
|
891
891
|
await fsp.mkdir(path.dirname(resolved), { recursive: true });
|
|
892
892
|
const existed = fs.existsSync(resolved);
|
|
893
893
|
await fsp.writeFile(resolved, payload.content, "utf8");
|
|
@@ -896,14 +896,14 @@ async function write_file(payload) {
|
|
|
896
896
|
|
|
897
897
|
async function edit_file(payload) {
|
|
898
898
|
assertWritable("edit_file");
|
|
899
|
-
const resolved = resolvePath(payload.
|
|
899
|
+
const resolved = resolvePath(payload.path);
|
|
900
900
|
const edits = payload.edits || [{ old_text: payload.old_text, new_text: payload.new_text }];
|
|
901
901
|
let content = await fsp.readFile(resolved, "utf8");
|
|
902
902
|
for (const edit of edits) {
|
|
903
903
|
const oldText = edit.old_text || "";
|
|
904
904
|
const newText = edit.new_text || "";
|
|
905
905
|
if (oldText === "" || content.split(oldText).length - 1 !== 1) {
|
|
906
|
-
throw new Error("Could not locate old_text exactly once in " + payload.
|
|
906
|
+
throw new Error("Could not locate old_text exactly once in " + payload.path);
|
|
907
907
|
}
|
|
908
908
|
content = content.replace(oldText, newText);
|
|
909
909
|
}
|
|
@@ -49,7 +49,7 @@ export const LocalListDirectoryToolName = Constants.LIST_DIRECTORY;
|
|
|
49
49
|
export const LocalReadFileToolSchema: t.JsonSchemaType = {
|
|
50
50
|
type: 'object',
|
|
51
51
|
properties: {
|
|
52
|
-
|
|
52
|
+
path: {
|
|
53
53
|
type: 'string',
|
|
54
54
|
description:
|
|
55
55
|
'Path to a local file, relative to the configured cwd unless absolute paths are allowed.',
|
|
@@ -63,13 +63,13 @@ export const LocalReadFileToolSchema: t.JsonSchemaType = {
|
|
|
63
63
|
description: 'Optional maximum number of lines to return.',
|
|
64
64
|
},
|
|
65
65
|
},
|
|
66
|
-
required: ['
|
|
66
|
+
required: ['path'],
|
|
67
67
|
};
|
|
68
68
|
|
|
69
69
|
export const LocalWriteFileToolSchema: t.JsonSchemaType = {
|
|
70
70
|
type: 'object',
|
|
71
71
|
properties: {
|
|
72
|
-
|
|
72
|
+
path: {
|
|
73
73
|
type: 'string',
|
|
74
74
|
description:
|
|
75
75
|
'Path to write, relative to the configured cwd unless absolute paths are allowed.',
|
|
@@ -79,13 +79,13 @@ export const LocalWriteFileToolSchema: t.JsonSchemaType = {
|
|
|
79
79
|
description: 'Complete file contents to write.',
|
|
80
80
|
},
|
|
81
81
|
},
|
|
82
|
-
required: ['
|
|
82
|
+
required: ['path', 'content'],
|
|
83
83
|
};
|
|
84
84
|
|
|
85
85
|
export const LocalEditFileToolSchema: t.JsonSchemaType = {
|
|
86
86
|
type: 'object',
|
|
87
87
|
properties: {
|
|
88
|
-
|
|
88
|
+
path: {
|
|
89
89
|
type: 'string',
|
|
90
90
|
description:
|
|
91
91
|
'Path to edit, relative to the configured cwd unless absolute paths are allowed.',
|
|
@@ -112,7 +112,7 @@ export const LocalEditFileToolSchema: t.JsonSchemaType = {
|
|
|
112
112
|
},
|
|
113
113
|
},
|
|
114
114
|
},
|
|
115
|
-
required: ['
|
|
115
|
+
required: ['path'],
|
|
116
116
|
};
|
|
117
117
|
|
|
118
118
|
export const LocalGrepSearchToolSchema: t.JsonSchemaType = {
|
|
@@ -391,18 +391,18 @@ export function createLocalReadFileTool(
|
|
|
391
391
|
return tool(
|
|
392
392
|
async (rawInput) => {
|
|
393
393
|
const input = rawInput as {
|
|
394
|
-
|
|
394
|
+
path: string;
|
|
395
395
|
offset?: number;
|
|
396
396
|
limit?: number;
|
|
397
397
|
};
|
|
398
398
|
const path = await resolveWorkspacePathSafe(
|
|
399
|
-
input.
|
|
399
|
+
input.path,
|
|
400
400
|
config,
|
|
401
401
|
'read'
|
|
402
402
|
);
|
|
403
403
|
const fileStat = await fs.stat(path);
|
|
404
404
|
if (!fileStat.isFile()) {
|
|
405
|
-
throw new Error(`Path is not a file: ${input.
|
|
405
|
+
throw new Error(`Path is not a file: ${input.path}`);
|
|
406
406
|
}
|
|
407
407
|
const maxBytes = Math.max(
|
|
408
408
|
config.maxReadBytes ?? DEFAULT_MAX_READ_BYTES,
|
|
@@ -514,12 +514,12 @@ export function createLocalWriteFileTool(
|
|
|
514
514
|
const fs = getWorkspaceFS(config);
|
|
515
515
|
return tool(
|
|
516
516
|
async (rawInput) => {
|
|
517
|
-
const input = rawInput as {
|
|
517
|
+
const input = rawInput as { path: string; content: string };
|
|
518
518
|
if (config.readOnly === true) {
|
|
519
519
|
throw new Error('write_file is blocked in read-only local mode.');
|
|
520
520
|
}
|
|
521
521
|
const path = await resolveWorkspacePathSafe(
|
|
522
|
-
input.
|
|
522
|
+
input.path,
|
|
523
523
|
config,
|
|
524
524
|
'write'
|
|
525
525
|
);
|
|
@@ -598,7 +598,7 @@ export function createLocalEditFileTool(
|
|
|
598
598
|
return tool(
|
|
599
599
|
async (rawInput) => {
|
|
600
600
|
const input = rawInput as {
|
|
601
|
-
|
|
601
|
+
path: string;
|
|
602
602
|
old_text?: string;
|
|
603
603
|
new_text?: string;
|
|
604
604
|
edits?: Array<{ old_text?: string; new_text?: string }>;
|
|
@@ -612,7 +612,7 @@ export function createLocalEditFileTool(
|
|
|
612
612
|
}
|
|
613
613
|
|
|
614
614
|
const path = await resolveWorkspacePathSafe(
|
|
615
|
-
input.
|
|
615
|
+
input.path,
|
|
616
616
|
config,
|
|
617
617
|
'write'
|
|
618
618
|
);
|
|
@@ -627,7 +627,7 @@ export function createLocalEditFileTool(
|
|
|
627
627
|
const match = locateEdit(next, edit.oldText);
|
|
628
628
|
if (match == null) {
|
|
629
629
|
throw new Error(
|
|
630
|
-
`Edit ${i + 1}/${edits.length}: could not locate old_text in ${input.
|
|
630
|
+
`Edit ${i + 1}/${edits.length}: could not locate old_text in ${input.path}. ` +
|
|
631
631
|
'Tried exact, line-trimmed, whitespace-normalized, and indentation-flexible matching. ' +
|
|
632
632
|
'Re-read the file and copy the literal lines.'
|
|
633
633
|
);
|