@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.
Files changed (28) hide show
  1. package/dist/cjs/hooks/createWorkspacePolicyHook.cjs +4 -3
  2. package/dist/cjs/hooks/createWorkspacePolicyHook.cjs.map +1 -1
  3. package/dist/cjs/tools/ReadFile.cjs +2 -2
  4. package/dist/cjs/tools/ReadFile.cjs.map +1 -1
  5. package/dist/cjs/tools/cloudflare/CloudflareProgrammaticToolCalling.cjs +11 -11
  6. package/dist/cjs/tools/cloudflare/CloudflareProgrammaticToolCalling.cjs.map +1 -1
  7. package/dist/cjs/tools/local/LocalCodingTools.cjs +11 -11
  8. package/dist/cjs/tools/local/LocalCodingTools.cjs.map +1 -1
  9. package/dist/esm/hooks/createWorkspacePolicyHook.mjs +4 -3
  10. package/dist/esm/hooks/createWorkspacePolicyHook.mjs.map +1 -1
  11. package/dist/esm/tools/ReadFile.mjs +2 -2
  12. package/dist/esm/tools/ReadFile.mjs.map +1 -1
  13. package/dist/esm/tools/cloudflare/CloudflareProgrammaticToolCalling.mjs +11 -11
  14. package/dist/esm/tools/cloudflare/CloudflareProgrammaticToolCalling.mjs.map +1 -1
  15. package/dist/esm/tools/local/LocalCodingTools.mjs +11 -11
  16. package/dist/esm/tools/local/LocalCodingTools.mjs.map +1 -1
  17. package/dist/types/tools/ReadFile.d.ts +4 -4
  18. package/package.json +1 -1
  19. package/src/hooks/__tests__/createWorkspacePolicyHook.test.ts +12 -12
  20. package/src/hooks/createWorkspacePolicyHook.ts +7 -6
  21. package/src/tools/ReadFile.ts +2 -2
  22. package/src/tools/__tests__/LocalExecutionTools.test.ts +25 -25
  23. package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +5 -5
  24. package/src/tools/__tests__/ReadFile.test.ts +3 -3
  25. package/src/tools/__tests__/ToolNode.session.test.ts +2 -2
  26. package/src/tools/__tests__/workspaceSeam.test.ts +2 -2
  27. package/src/tools/cloudflare/CloudflareProgrammaticToolCalling.ts +11 -11
  28. 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(file_path="ptc.txt", content="from local ptc")',
179
- 'contents = await read_file(file_path="ptc.txt")',
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 \'{"file_path":"bash-ptc.txt","content":"from bash ptc"}\'',
209
- 'read_file \'{"file_path":"bash-ptc.txt"}\'',
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({ file_path: 'cp.txt', content: 'first' });
339
- await writeTool!.invoke({ file_path: 'cp.txt', content: 'second' });
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({ file_path: 'binary.bin' });
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({ file_path: 'big.txt' });
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({ file_path: 'escape/secret.txt' })
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":{"file_path":"x"}}).encode("utf-8")',
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
- file_path: 'a.ts',
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
- file_path: 'a.ts',
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
- file_path: 'a.txt',
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
- file_path: 'a.txt',
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({ file_path: 'a.txt', content: 'goodbye\n' });
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({ file_path: 'tiny.png' });
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: { file_path: 'tiny.png' },
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({ file_path: 'big.png' });
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({ file_path: 'a.txt' });
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: { file_path: 'broken.js', content: 'function (\n' },
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: { file_path: 'broken.js', content: 'function (\n' },
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: { file_path: join(parent, 'shared/lib.ts') },
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: { file_path: file, content: 'function broken( {\n' },
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: { file_path: file, content: 'function broken( {\n' },
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
- file_path: file,
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
- { file_path: '/tmp/x' }
1506
+ { path: '/tmp/x' }
1507
1507
  );
1508
1508
  expect(gate.denyReason).toBeUndefined();
1509
- expect(gate.input).toEqual({ file_path: '/tmp/x' });
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: { file_path: '/tmp/rewritten' },
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
- { file_path: '/tmp/original' }
1532
+ { path: '/tmp/original' }
1533
1533
  );
1534
1534
  expect(gate.denyReason).toBeUndefined();
1535
- expect(gate.input).toEqual({ file_path: '/tmp/rewritten' });
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 file_path as required string property', () => {
13
- expect(ReadFileToolSchema.properties.file_path.type).toBe('string');
14
- expect(ReadFileToolSchema.required).toContain('file_path');
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: { file_path: '/mnt/data/data.csv' },
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: { file_path: 'some-skill/notes.md' },
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: { file_path: 'note.md', content: 'hi\n' },
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: { file_path: 'note.md' },
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(file_path, offset=None, limit=None):
482
- resolved = _resolve(file_path)
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(file_path, content):
486
+ async def write_file(path, content):
487
487
  _assert_writable("write_file")
488
- resolved = _resolve(file_path)
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(file_path, old_text=None, new_text=None, edits=None):
495
+ async def edit_file(path, old_text=None, new_text=None, edits=None):
496
496
  _assert_writable("edit_file")
497
- resolved = _resolve(file_path)
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 {file_path}")
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.file_path);
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.file_path);
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.file_path);
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.file_path);
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
- file_path: {
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: ['file_path'],
66
+ required: ['path'],
67
67
  };
68
68
 
69
69
  export const LocalWriteFileToolSchema: t.JsonSchemaType = {
70
70
  type: 'object',
71
71
  properties: {
72
- file_path: {
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: ['file_path', 'content'],
82
+ required: ['path', 'content'],
83
83
  };
84
84
 
85
85
  export const LocalEditFileToolSchema: t.JsonSchemaType = {
86
86
  type: 'object',
87
87
  properties: {
88
- file_path: {
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: ['file_path'],
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
- file_path: string;
394
+ path: string;
395
395
  offset?: number;
396
396
  limit?: number;
397
397
  };
398
398
  const path = await resolveWorkspacePathSafe(
399
- input.file_path,
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.file_path}`);
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 { file_path: string; content: string };
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.file_path,
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
- file_path: string;
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.file_path,
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.file_path}. ` +
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
  );