mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-06-30 03:12:11 +00:00
🦜 refactor: Use path for Read/Write/Edit/Create File Tools (#13834)
Some checks are pending
Docker Dev Branch Images Build / build (Dockerfile, lc-dev, node) (push) Waiting to run
Docker Dev Branch Images Build / build (Dockerfile.multi, lc-dev-api, api-build) (push) Waiting to run
GitNexus Index / index (push) Waiting to run
GitNexus Index / post-index (push) Blocked by required conditions
Some checks are pending
Docker Dev Branch Images Build / build (Dockerfile, lc-dev, node) (push) Waiting to run
Docker Dev Branch Images Build / build (Dockerfile.multi, lc-dev-api, api-build) (push) Waiting to run
GitNexus Index / index (push) Waiting to run
GitNexus Index / post-index (push) Blocked by required conditions
* fix(agents): use `path` for read/write/edit/create file tools Pairs with @librechat/agents renaming the read_file/write_file/edit_file tool parameter from `file_path` to `path` (models — esp. Kimi K2 — emit `path` far more reliably, and it matches grep/glob/list_directory which already use `path`). - tools.ts: LibreChat's own code/skill file-tool schemas use `path` (the skill read_file tool inherits the SDK definition, which is already renamed) - handlers.ts: read `args.path` for the model-facing tool arg + error messages - the internal host `readSandboxFile`/`writeSandboxFile` contract is unchanged - tests updated Requires @librechat/agents with the param rename (danny-avila/agents#250). All agents unit suites green (175). * chore: update @librechat/agents to v3.2.41 and bump related dependencies in package-lock.json and package.json files * fix(api): Refactor header merging in MCPConnection to use Object.assign for clarity * test(e2e): mock emits `path` for create/edit file-authoring tools The mock LLM still sent `file_path` for the create_file/edit_file calls, which the renamed handlers no longer read -> the skill-file-authoring e2e failed with 'Expected skill to be persisted'. Switch the fixture to `path` to match the tools. (The internal readSandboxFile/writeSandboxFile contract stays on `file_path`, so api/server/services/Files/Code/process.js and its spec are unchanged.)
This commit is contained in:
parent
2fcba914f7
commit
68d142d0e9
10 changed files with 156 additions and 152 deletions
|
|
@ -46,7 +46,7 @@
|
|||
"@azure/storage-blob": "^12.30.0",
|
||||
"@google/genai": "^2.8.0",
|
||||
"@keyv/redis": "^4.3.3",
|
||||
"@librechat/agents": "^3.2.38",
|
||||
"@librechat/agents": "^3.2.41",
|
||||
"@librechat/api": "*",
|
||||
"@librechat/data-schemas": "*",
|
||||
"@microsoft/microsoft-graph-client": "^3.0.7",
|
||||
|
|
|
|||
|
|
@ -427,7 +427,7 @@ Created by the Playwright mock e2e suite to verify host file authoring without c
|
|||
|
||||
function buildCreateSkillArgs(skillName) {
|
||||
return {
|
||||
file_path: `skills/${skillName}/SKILL.md`,
|
||||
path: `skills/${skillName}/SKILL.md`,
|
||||
content: buildSkillBody(skillName),
|
||||
overwrite: false,
|
||||
};
|
||||
|
|
@ -435,7 +435,7 @@ function buildCreateSkillArgs(skillName) {
|
|||
|
||||
function buildEditSkillArgs(skillName) {
|
||||
return {
|
||||
file_path: `skills/${skillName}/SKILL.md`,
|
||||
path: `skills/${skillName}/SKILL.md`,
|
||||
old_text: `description: ${SKILL_DESCRIPTION}`,
|
||||
new_text: `description: ${EDITED_SKILL_DESCRIPTION}`,
|
||||
};
|
||||
|
|
|
|||
45
package-lock.json
generated
45
package-lock.json
generated
|
|
@ -61,7 +61,7 @@
|
|||
"@azure/storage-blob": "^12.30.0",
|
||||
"@google/genai": "^2.8.0",
|
||||
"@keyv/redis": "^4.3.3",
|
||||
"@librechat/agents": "^3.2.38",
|
||||
"@librechat/agents": "^3.2.41",
|
||||
"@librechat/api": "*",
|
||||
"@librechat/data-schemas": "*",
|
||||
"@microsoft/microsoft-graph-client": "^3.0.7",
|
||||
|
|
@ -9834,9 +9834,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@librechat/agents": {
|
||||
"version": "3.2.38",
|
||||
"resolved": "https://registry.npmjs.org/@librechat/agents/-/agents-3.2.38.tgz",
|
||||
"integrity": "sha512-jbD+H9T/Bcz2ot50X2J/Ffsq5McFHriS66AhFHMIzUfsbDVbLFSYLj/6K61JRjxegIZ/z/gV6tmVAmZCHWh5kg==",
|
||||
"version": "3.2.41",
|
||||
"resolved": "https://registry.npmjs.org/@librechat/agents/-/agents-3.2.41.tgz",
|
||||
"integrity": "sha512-AjzSrurK/ihORn4NIftlS5CCviBWcT2kkLvYoCuV/aRT2E4HQea6aUfC+g0FIK71pBN9RThQv7Gh3I/7/aMxIw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/sdk": "^0.92.0",
|
||||
|
|
@ -11538,7 +11538,8 @@
|
|||
"node_modules/@protobufjs/aspromise": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
|
||||
"integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="
|
||||
"integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/base64": {
|
||||
"version": "1.1.2",
|
||||
|
|
@ -11557,12 +11558,12 @@
|
|||
"integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="
|
||||
},
|
||||
"node_modules/@protobufjs/fetch": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
|
||||
"integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz",
|
||||
"integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@protobufjs/aspromise": "^1.1.1",
|
||||
"@protobufjs/inquire": "^1.1.0"
|
||||
"@protobufjs/aspromise": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@protobufjs/float": {
|
||||
|
|
@ -11571,9 +11572,9 @@
|
|||
"integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="
|
||||
},
|
||||
"node_modules/@protobufjs/inquire": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.1.tgz",
|
||||
"integrity": "sha512-mnzgDV26ueAvk7rsbt9L7bE0SuAoqyuys/sMMrmVcN5x9VsxpcG3rqAUSgDyLp0UZlmNfIbQ4fHfCtreVBk8Ew==",
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.2.tgz",
|
||||
"integrity": "sha512-pa0vFRuws4wkvaXKK1uXZMAwAX4/t8ANaJo45iw/oQHNQ9q5xUzwgFmVJGXiga2BeN+zpX7Vf9vmsiIa2J+MUw==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/path": {
|
||||
|
|
@ -35755,9 +35756,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/protobufjs": {
|
||||
"version": "7.5.8",
|
||||
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.8.tgz",
|
||||
"integrity": "sha512-dvpCIeLPbXZS/Ete7yLaO7RenOdken2NHKykBXbsaGxZT0UTltcarBciw+A78SRQs9iMAAVpsYA+l8b1hTePIA==",
|
||||
"version": "7.5.9",
|
||||
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.9.tgz",
|
||||
"integrity": "sha512-Od4muIm3HW1AouyHF5lONOf1FWo3hY1NbFDoy191X9GzhpgW1clCoaFjfVs2rKJNFYpTNJbje4cbAIDBZJ63ZA==",
|
||||
"hasInstallScript": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
|
|
@ -35765,9 +35766,9 @@
|
|||
"@protobufjs/base64": "^1.1.2",
|
||||
"@protobufjs/codegen": "^2.0.5",
|
||||
"@protobufjs/eventemitter": "^1.1.0",
|
||||
"@protobufjs/fetch": "^1.1.0",
|
||||
"@protobufjs/fetch": "^1.1.1",
|
||||
"@protobufjs/float": "^1.0.2",
|
||||
"@protobufjs/inquire": "^1.1.1",
|
||||
"@protobufjs/inquire": "^1.1.2",
|
||||
"@protobufjs/path": "^1.1.2",
|
||||
"@protobufjs/pool": "^1.1.0",
|
||||
"@protobufjs/utf8": "^1.1.1",
|
||||
|
|
@ -40365,9 +40366,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/undici": {
|
||||
"version": "7.24.1",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-7.24.1.tgz",
|
||||
"integrity": "sha512-5xoBibbmnjlcR3jdqtY2Lnx7WbrD/tHlT01TmvqZUFVc9Q1w4+j5hbnapTqbcXITMH1ovjq/W7BkqBilHiVAaA==",
|
||||
"version": "7.28.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-7.28.0.tgz",
|
||||
"integrity": "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=20.18.1"
|
||||
|
|
@ -42389,7 +42390,7 @@
|
|||
"@azure/storage-blob": "^12.30.0",
|
||||
"@google/genai": "^2.8.0",
|
||||
"@keyv/redis": "^4.3.3",
|
||||
"@librechat/agents": "^3.2.38",
|
||||
"@librechat/agents": "^3.2.41",
|
||||
"@librechat/data-schemas": "*",
|
||||
"@modelcontextprotocol/sdk": "^1.29.0",
|
||||
"@opentelemetry/api": "^1.9.0",
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@
|
|||
"@azure/storage-blob": "^12.30.0",
|
||||
"@google/genai": "^2.8.0",
|
||||
"@keyv/redis": "^4.3.3",
|
||||
"@librechat/agents": "^3.2.38",
|
||||
"@librechat/agents": "^3.2.41",
|
||||
"@librechat/data-schemas": "*",
|
||||
"@modelcontextprotocol/sdk": "^1.29.0",
|
||||
"@opentelemetry/api": "^1.9.0",
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ jest.mock('@librechat/agents', () => ({
|
|||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
file_path: {
|
||||
path: {
|
||||
type: 'string',
|
||||
description: 'For skill files: "{skillName}/{path}".',
|
||||
},
|
||||
|
|
|
|||
|
|
@ -673,7 +673,7 @@ describe('createToolExecuteHandler', () => {
|
|||
{
|
||||
id: 'call_read_5',
|
||||
name: Constants.READ_FILE,
|
||||
args: { file_path: 'maybe-disabled-read/SKILL.md' },
|
||||
args: { path: 'maybe-disabled-read/SKILL.md' },
|
||||
},
|
||||
]);
|
||||
|
||||
|
|
@ -717,7 +717,7 @@ describe('createToolExecuteHandler', () => {
|
|||
{
|
||||
id: 'call_read_6',
|
||||
name: Constants.READ_FILE,
|
||||
args: { file_path: 'manually-primed/references/foo.md' },
|
||||
args: { path: 'manually-primed/references/foo.md' },
|
||||
},
|
||||
]);
|
||||
|
||||
|
|
@ -766,7 +766,7 @@ describe('createToolExecuteHandler', () => {
|
|||
{
|
||||
id: 'call_read_1',
|
||||
name: Constants.READ_FILE,
|
||||
args: { file_path: 'pii-redactor/SKILL.md' },
|
||||
args: { path: 'pii-redactor/SKILL.md' },
|
||||
},
|
||||
]);
|
||||
|
||||
|
|
@ -795,7 +795,7 @@ describe('createToolExecuteHandler', () => {
|
|||
{
|
||||
id: 'call_read_2',
|
||||
name: Constants.READ_FILE,
|
||||
args: { file_path: 'normal-skill/SKILL.md' },
|
||||
args: { path: 'normal-skill/SKILL.md' },
|
||||
},
|
||||
]);
|
||||
|
||||
|
|
@ -833,7 +833,7 @@ describe('createToolExecuteHandler', () => {
|
|||
{
|
||||
id: 'call_read_3',
|
||||
name: Constants.READ_FILE,
|
||||
args: { file_path: 'manual-only-skill/SKILL.md' },
|
||||
args: { path: 'manual-only-skill/SKILL.md' },
|
||||
},
|
||||
]);
|
||||
|
||||
|
|
@ -869,7 +869,7 @@ describe('createToolExecuteHandler', () => {
|
|||
{
|
||||
id: 'call_read_4',
|
||||
name: Constants.READ_FILE,
|
||||
args: { file_path: 'other-disabled-skill/SKILL.md' },
|
||||
args: { path: 'other-disabled-skill/SKILL.md' },
|
||||
},
|
||||
]);
|
||||
|
||||
|
|
@ -911,7 +911,7 @@ describe('createToolExecuteHandler', () => {
|
|||
{
|
||||
id: 'call_read_always',
|
||||
name: Constants.READ_FILE,
|
||||
args: { file_path: 'always-applied-legal/SKILL.md' },
|
||||
args: { path: 'always-applied-legal/SKILL.md' },
|
||||
},
|
||||
]);
|
||||
|
||||
|
|
@ -947,7 +947,7 @@ describe('createToolExecuteHandler', () => {
|
|||
{
|
||||
id: 'call_read_pin',
|
||||
name: Constants.READ_FILE,
|
||||
args: { file_path: 'collides/SKILL.md' },
|
||||
args: { path: 'collides/SKILL.md' },
|
||||
},
|
||||
]);
|
||||
|
||||
|
|
@ -1093,7 +1093,7 @@ describe('createToolExecuteHandler', () => {
|
|||
id: 'call_create_skill',
|
||||
name: 'create_file',
|
||||
args: {
|
||||
file_path: 'skills/new-skill/SKILL.md',
|
||||
path: 'skills/new-skill/SKILL.md',
|
||||
content:
|
||||
'---\nname: new-skill\ndescription: Use for tests\ndisable-model-invocation: true\nallowed-tools:\n - execute_code\n---\n# New skill\n',
|
||||
},
|
||||
|
|
@ -1140,7 +1140,7 @@ describe('createToolExecuteHandler', () => {
|
|||
id: 'call_create_auto_frontmatter',
|
||||
name: 'create_file',
|
||||
args: {
|
||||
file_path: 'skills/auto-skill/SKILL.md',
|
||||
path: 'skills/auto-skill/SKILL.md',
|
||||
content: '# Auto skill\nUse this skill when testing generated frontmatter.\n',
|
||||
},
|
||||
},
|
||||
|
|
@ -1179,7 +1179,7 @@ describe('createToolExecuteHandler', () => {
|
|||
id: 'call_create_block_description',
|
||||
name: 'create_file',
|
||||
args: {
|
||||
file_path: 'skills/block-description-skill/SKILL.md',
|
||||
path: 'skills/block-description-skill/SKILL.md',
|
||||
content:
|
||||
'---\nname: block-description-skill\ndescription: |-\n Use this skill for long descriptions.\n Keep both lines searchable.\n---\n# Block description skill\n',
|
||||
},
|
||||
|
|
@ -1233,7 +1233,7 @@ describe('createToolExecuteHandler', () => {
|
|||
id: 'call_create_skill',
|
||||
name: 'create_file',
|
||||
args: {
|
||||
file_path: 'skills/new-skill/SKILL.md',
|
||||
path: 'skills/new-skill/SKILL.md',
|
||||
content: '---\nname: new-skill\ndescription: Use for tests\n---\n# New skill\n',
|
||||
},
|
||||
},
|
||||
|
|
@ -1241,7 +1241,7 @@ describe('createToolExecuteHandler', () => {
|
|||
id: 'call_create_reference',
|
||||
name: 'create_file',
|
||||
args: {
|
||||
file_path: 'skills/new-skill/references/a.md',
|
||||
path: 'skills/new-skill/references/a.md',
|
||||
content: 'reference text',
|
||||
},
|
||||
},
|
||||
|
|
@ -1307,7 +1307,7 @@ describe('createToolExecuteHandler', () => {
|
|||
id: 'call_create_round_skill',
|
||||
name: 'create_file',
|
||||
args: {
|
||||
file_path: 'skills/round-skill/SKILL.md',
|
||||
path: 'skills/round-skill/SKILL.md',
|
||||
content: createdSkill.body,
|
||||
},
|
||||
},
|
||||
|
|
@ -1320,7 +1320,7 @@ describe('createToolExecuteHandler', () => {
|
|||
{
|
||||
id: 'call_read_round_skill',
|
||||
name: Constants.READ_FILE,
|
||||
args: { file_path: 'skills/round-skill/SKILL.md' },
|
||||
args: { path: 'skills/round-skill/SKILL.md' },
|
||||
},
|
||||
],
|
||||
runtimeConfigurable,
|
||||
|
|
@ -1357,7 +1357,7 @@ describe('createToolExecuteHandler', () => {
|
|||
id: 'call_create_existing',
|
||||
name: 'create_file',
|
||||
args: {
|
||||
file_path: 'skills/existing-skill/SKILL.md',
|
||||
path: 'skills/existing-skill/SKILL.md',
|
||||
content: '---\nname: existing-skill\ndescription: Use for tests\n---\n# Updated\n',
|
||||
},
|
||||
},
|
||||
|
|
@ -1396,7 +1396,7 @@ describe('createToolExecuteHandler', () => {
|
|||
id: 'call_duplicate_stale_skill',
|
||||
name: 'create_file',
|
||||
args: {
|
||||
file_path: 'skills/stale-skill/SKILL.md',
|
||||
path: 'skills/stale-skill/SKILL.md',
|
||||
content: '---\nname: stale-skill\ndescription: Replacement\n---\n# Replacement\n',
|
||||
},
|
||||
},
|
||||
|
|
@ -1437,7 +1437,7 @@ describe('createToolExecuteHandler', () => {
|
|||
id: 'call_edit_excluded_skill',
|
||||
name: 'edit_file',
|
||||
args: {
|
||||
file_path: 'skills/excluded-skill/SKILL.md',
|
||||
path: 'skills/excluded-skill/SKILL.md',
|
||||
old_text: '# Excluded',
|
||||
new_text: '# Changed',
|
||||
},
|
||||
|
|
@ -1477,7 +1477,7 @@ describe('createToolExecuteHandler', () => {
|
|||
id: 'call_recovered_hidden_skill',
|
||||
name: 'edit_file',
|
||||
args: {
|
||||
file_path: 'skills/hidden-recovered-skill/SKILL.md',
|
||||
path: 'skills/hidden-recovered-skill/SKILL.md',
|
||||
old_text: '# Hidden',
|
||||
new_text: '# Changed',
|
||||
},
|
||||
|
|
@ -1519,7 +1519,7 @@ describe('createToolExecuteHandler', () => {
|
|||
id: 'call_edit_file',
|
||||
name: 'edit_file',
|
||||
args: {
|
||||
file_path: 'skills/edit-skill/references/a.md',
|
||||
path: 'skills/edit-skill/references/a.md',
|
||||
old_text: 'hello old',
|
||||
new_text: 'hello new',
|
||||
},
|
||||
|
|
@ -1581,7 +1581,7 @@ describe('createToolExecuteHandler', () => {
|
|||
id: 'call_edit_stale_bundled_file',
|
||||
name: 'edit_file',
|
||||
args: {
|
||||
file_path: 'skills/edit-skill/references/a.md',
|
||||
path: 'skills/edit-skill/references/a.md',
|
||||
old_text: 'hello old',
|
||||
new_text: 'hello new',
|
||||
},
|
||||
|
|
@ -1624,7 +1624,7 @@ describe('createToolExecuteHandler', () => {
|
|||
id: 'call_edit_skill_md_frontmatter',
|
||||
name: 'edit_file',
|
||||
args: {
|
||||
file_path: 'skills/runtime-skill/SKILL.md',
|
||||
path: 'skills/runtime-skill/SKILL.md',
|
||||
old_text: 'description: Use before\naction: ignored',
|
||||
new_text:
|
||||
'description: Use before\nuser-invocable: false\ndisable-model-invocation: true\nallowed-tools:\n - execute_code\nalways-apply: true',
|
||||
|
|
@ -1677,7 +1677,7 @@ describe('createToolExecuteHandler', () => {
|
|||
id: 'call_edit_skill_md_block_description',
|
||||
name: 'edit_file',
|
||||
args: {
|
||||
file_path: 'skills/runtime-skill/SKILL.md',
|
||||
path: 'skills/runtime-skill/SKILL.md',
|
||||
old_text: 'description: Use before',
|
||||
new_text:
|
||||
'description: |-\n Use this skill for long descriptions.\n Keep both lines searchable.',
|
||||
|
|
@ -1717,7 +1717,7 @@ describe('createToolExecuteHandler', () => {
|
|||
id: 'call_edit_skill_md_name',
|
||||
name: 'edit_file',
|
||||
args: {
|
||||
file_path: 'skills/runtime-skill/SKILL.md',
|
||||
path: 'skills/runtime-skill/SKILL.md',
|
||||
old_text: 'name: runtime-skill',
|
||||
new_text: 'name: dev-toolkit',
|
||||
},
|
||||
|
|
@ -1759,7 +1759,7 @@ describe('createToolExecuteHandler', () => {
|
|||
id: 'call_edit_ambiguous',
|
||||
name: 'edit_file',
|
||||
args: {
|
||||
file_path: 'skills/ambiguous-skill/references/a.md',
|
||||
path: 'skills/ambiguous-skill/references/a.md',
|
||||
old_text: 'same',
|
||||
new_text: 'different',
|
||||
},
|
||||
|
|
@ -1796,7 +1796,7 @@ describe('createToolExecuteHandler', () => {
|
|||
id: 'call_edit_hidden_skill',
|
||||
name: 'edit_file',
|
||||
args: {
|
||||
file_path: 'skills/hidden-skill/SKILL.md',
|
||||
path: 'skills/hidden-skill/SKILL.md',
|
||||
old_text: '# Hidden',
|
||||
new_text: '# Changed',
|
||||
},
|
||||
|
|
@ -1841,7 +1841,7 @@ describe('createToolExecuteHandler', () => {
|
|||
id: 'call_edit_primed_hidden_skill',
|
||||
name: 'edit_file',
|
||||
args: {
|
||||
file_path: 'skills/primed-hidden-skill/SKILL.md',
|
||||
path: 'skills/primed-hidden-skill/SKILL.md',
|
||||
old_text: '# Hidden',
|
||||
new_text: '# Changed',
|
||||
},
|
||||
|
|
@ -1883,7 +1883,7 @@ describe('createToolExecuteHandler', () => {
|
|||
id: 'call_overwrite_large',
|
||||
name: 'create_file',
|
||||
args: {
|
||||
file_path: 'skills/large-skill/references/large.md',
|
||||
path: 'skills/large-skill/references/large.md',
|
||||
content: 'replacement',
|
||||
overwrite: true,
|
||||
},
|
||||
|
|
@ -1935,7 +1935,7 @@ describe('createToolExecuteHandler', () => {
|
|||
id: 'call_edit_one',
|
||||
name: 'edit_file',
|
||||
args: {
|
||||
file_path: 'skills/serial-skill/references/a.md',
|
||||
path: 'skills/serial-skill/references/a.md',
|
||||
old_text: 'one',
|
||||
new_text: 'two',
|
||||
},
|
||||
|
|
@ -1944,7 +1944,7 @@ describe('createToolExecuteHandler', () => {
|
|||
id: 'call_edit_two',
|
||||
name: 'edit_file',
|
||||
args: {
|
||||
file_path: 'skills/serial-skill/references/a.md',
|
||||
path: 'skills/serial-skill/references/a.md',
|
||||
old_text: 'two',
|
||||
new_text: 'three',
|
||||
},
|
||||
|
|
@ -1999,7 +1999,7 @@ describe('createToolExecuteHandler', () => {
|
|||
id: 'call_create_sandbox',
|
||||
name: 'create_file',
|
||||
args: {
|
||||
file_path: '/mnt/data/new.txt',
|
||||
path: '/mnt/data/new.txt',
|
||||
content: 'hello world',
|
||||
},
|
||||
codeSessionContext: {
|
||||
|
|
@ -2038,7 +2038,7 @@ describe('createToolExecuteHandler', () => {
|
|||
id: 'call_create_existing_sandbox',
|
||||
name: 'create_file',
|
||||
args: {
|
||||
file_path: '/mnt/data/existing.txt',
|
||||
path: '/mnt/data/existing.txt',
|
||||
content: 'new text\n',
|
||||
},
|
||||
},
|
||||
|
|
@ -2065,7 +2065,7 @@ describe('createToolExecuteHandler', () => {
|
|||
id: 'call_edit_sandbox',
|
||||
name: 'edit_file',
|
||||
args: {
|
||||
file_path: '/mnt/data/edit.txt',
|
||||
path: '/mnt/data/edit.txt',
|
||||
old_text: 'alpha old',
|
||||
new_text: 'alpha new',
|
||||
},
|
||||
|
|
@ -2151,7 +2151,7 @@ describe('createToolExecuteHandler', () => {
|
|||
id: 'call_create_queued_sandbox',
|
||||
name: 'create_file',
|
||||
args: {
|
||||
file_path: '/mnt/data/queued.txt',
|
||||
path: '/mnt/data/queued.txt',
|
||||
content: 'hello world\n',
|
||||
},
|
||||
},
|
||||
|
|
@ -2159,7 +2159,7 @@ describe('createToolExecuteHandler', () => {
|
|||
id: 'call_edit_queued_sandbox',
|
||||
name: 'edit_file',
|
||||
args: {
|
||||
file_path: '/mnt/data/queued.txt',
|
||||
path: '/mnt/data/queued.txt',
|
||||
old_text: 'hello world',
|
||||
new_text: 'goodbye world',
|
||||
},
|
||||
|
|
@ -2193,7 +2193,7 @@ describe('createToolExecuteHandler', () => {
|
|||
id: 'call_no_code_env_authoring',
|
||||
name: 'create_file',
|
||||
args: {
|
||||
file_path: '/mnt/data/nope.txt',
|
||||
path: '/mnt/data/nope.txt',
|
||||
content: 'nope',
|
||||
},
|
||||
},
|
||||
|
|
@ -2218,7 +2218,7 @@ describe('createToolExecuteHandler', () => {
|
|||
id: 'call_code_only_skill_path',
|
||||
name: 'create_file',
|
||||
args: {
|
||||
file_path: 'skills/nope/SKILL.md',
|
||||
path: 'skills/nope/SKILL.md',
|
||||
content: '---\nname: nope\ndescription: Nope\n---\n# Nope\n',
|
||||
},
|
||||
},
|
||||
|
|
@ -2274,7 +2274,7 @@ describe('createToolExecuteHandler', () => {
|
|||
{
|
||||
id: 'call_mnt_1',
|
||||
name: Constants.READ_FILE,
|
||||
args: { file_path: '/mnt/data/sentinel.txt' },
|
||||
args: { path: '/mnt/data/sentinel.txt' },
|
||||
codeSessionContext: {
|
||||
session_id: 'sess-X',
|
||||
files: [{ id: 'f1', name: 'sentinel.txt', session_id: 'sess-X' }],
|
||||
|
|
@ -2303,7 +2303,7 @@ describe('createToolExecuteHandler', () => {
|
|||
{
|
||||
id: 'call_mnt_2',
|
||||
name: Constants.READ_FILE,
|
||||
args: { file_path: '/mnt/data/sentinel.txt' },
|
||||
args: { path: '/mnt/data/sentinel.txt' },
|
||||
},
|
||||
]);
|
||||
|
||||
|
|
@ -2325,7 +2325,7 @@ describe('createToolExecuteHandler', () => {
|
|||
{
|
||||
id: 'call_unknown_skill',
|
||||
name: Constants.READ_FILE,
|
||||
args: { file_path: 'not-a-skill/foo.md' },
|
||||
args: { path: 'not-a-skill/foo.md' },
|
||||
codeSessionContext: { session_id: 'sess-Y' },
|
||||
} as unknown as ToolCallRequest,
|
||||
]);
|
||||
|
|
@ -2391,7 +2391,7 @@ describe('createToolExecuteHandler', () => {
|
|||
{
|
||||
id: 'call_primed_outside_catalog',
|
||||
name: Constants.READ_FILE,
|
||||
args: { file_path: 'primed-only-skill/references/foo.md' },
|
||||
args: { path: 'primed-only-skill/references/foo.md' },
|
||||
},
|
||||
]);
|
||||
|
||||
|
|
@ -2440,7 +2440,7 @@ describe('createToolExecuteHandler', () => {
|
|||
{
|
||||
id: 'call_stale_catalog_skill',
|
||||
name: Constants.READ_FILE,
|
||||
args: { file_path: 'stale-catalog-skill/SKILL.md' },
|
||||
args: { path: 'stale-catalog-skill/SKILL.md' },
|
||||
},
|
||||
]);
|
||||
|
||||
|
|
@ -2475,7 +2475,7 @@ describe('createToolExecuteHandler', () => {
|
|||
{
|
||||
id: 'call_skills_off',
|
||||
name: Constants.READ_FILE,
|
||||
args: { file_path: 'whatever/path.md' },
|
||||
args: { path: 'whatever/path.md' },
|
||||
},
|
||||
]);
|
||||
|
||||
|
|
@ -2495,7 +2495,7 @@ describe('createToolExecuteHandler', () => {
|
|||
{
|
||||
id: 'call_no_route',
|
||||
name: Constants.READ_FILE,
|
||||
args: { file_path: 'whatever/path.md' },
|
||||
args: { path: 'whatever/path.md' },
|
||||
},
|
||||
]);
|
||||
|
||||
|
|
@ -2524,7 +2524,7 @@ describe('createToolExecuteHandler', () => {
|
|||
{
|
||||
id: 'call_real_skill',
|
||||
name: Constants.READ_FILE,
|
||||
args: { file_path: 'real-skill/SKILL.md' },
|
||||
args: { path: 'real-skill/SKILL.md' },
|
||||
},
|
||||
]);
|
||||
|
||||
|
|
@ -2555,7 +2555,7 @@ describe('createToolExecuteHandler', () => {
|
|||
{
|
||||
id: 'call_real_skill_namespace',
|
||||
name: Constants.READ_FILE,
|
||||
args: { file_path: 'skills/real-skill/SKILL.md' },
|
||||
args: { path: 'skills/real-skill/SKILL.md' },
|
||||
},
|
||||
]);
|
||||
|
||||
|
|
@ -2584,7 +2584,7 @@ describe('createToolExecuteHandler', () => {
|
|||
{
|
||||
id: 'call_explicit_skill_namespace_off',
|
||||
name: Constants.READ_FILE,
|
||||
args: { file_path: 'skills/whatever/SKILL.md' },
|
||||
args: { path: 'skills/whatever/SKILL.md' },
|
||||
},
|
||||
]);
|
||||
|
||||
|
|
@ -2613,7 +2613,7 @@ describe('createToolExecuteHandler', () => {
|
|||
{
|
||||
id: 'call_trailing_slash',
|
||||
name: Constants.READ_FILE,
|
||||
args: { file_path: 'output/' },
|
||||
args: { path: 'output/' },
|
||||
},
|
||||
]);
|
||||
|
||||
|
|
@ -2635,7 +2635,7 @@ describe('createToolExecuteHandler', () => {
|
|||
{
|
||||
id: 'call_trailing_slash_no_env',
|
||||
name: Constants.READ_FILE,
|
||||
args: { file_path: 'output/' },
|
||||
args: { path: 'output/' },
|
||||
},
|
||||
]);
|
||||
|
||||
|
|
@ -2655,7 +2655,7 @@ describe('createToolExecuteHandler', () => {
|
|||
{
|
||||
id: 'call_no_callback',
|
||||
name: Constants.READ_FILE,
|
||||
args: { file_path: '/mnt/data/x.txt' },
|
||||
args: { path: '/mnt/data/x.txt' },
|
||||
},
|
||||
]);
|
||||
|
||||
|
|
@ -2684,7 +2684,7 @@ describe('createToolExecuteHandler', () => {
|
|||
{
|
||||
id: 'call_huge_file',
|
||||
name: Constants.READ_FILE,
|
||||
args: { file_path: '/mnt/data/huge.log' },
|
||||
args: { path: '/mnt/data/huge.log' },
|
||||
},
|
||||
]);
|
||||
|
||||
|
|
@ -2712,7 +2712,7 @@ describe('createToolExecuteHandler', () => {
|
|||
{
|
||||
id: 'call_small_file',
|
||||
name: Constants.READ_FILE,
|
||||
args: { file_path: '/mnt/data/sentinel.txt' },
|
||||
args: { path: '/mnt/data/sentinel.txt' },
|
||||
},
|
||||
]);
|
||||
|
||||
|
|
@ -2733,7 +2733,7 @@ describe('createToolExecuteHandler', () => {
|
|||
{
|
||||
id: 'call_null_result',
|
||||
name: Constants.READ_FILE,
|
||||
args: { file_path: '/mnt/data/missing.txt' },
|
||||
args: { path: '/mnt/data/missing.txt' },
|
||||
},
|
||||
]);
|
||||
|
||||
|
|
@ -2764,7 +2764,7 @@ describe('createToolExecuteHandler', () => {
|
|||
{
|
||||
id: 'call_png',
|
||||
name: Constants.READ_FILE,
|
||||
args: { file_path: '/mnt/data/simple_graph.png' },
|
||||
args: { path: '/mnt/data/simple_graph.png' },
|
||||
codeSessionContext: { session_id: 'sess-Z' },
|
||||
} as unknown as ToolCallRequest,
|
||||
]);
|
||||
|
|
@ -2789,7 +2789,7 @@ describe('createToolExecuteHandler', () => {
|
|||
{
|
||||
id: 'call_zip',
|
||||
name: Constants.READ_FILE,
|
||||
args: { file_path: '/mnt/data/archive.zip' },
|
||||
args: { path: '/mnt/data/archive.zip' },
|
||||
},
|
||||
]);
|
||||
|
||||
|
|
@ -2813,7 +2813,7 @@ describe('createToolExecuteHandler', () => {
|
|||
{
|
||||
id: 'call_uppercase',
|
||||
name: Constants.READ_FILE,
|
||||
args: { file_path: '/mnt/data/CHART.PNG' },
|
||||
args: { path: '/mnt/data/CHART.PNG' },
|
||||
},
|
||||
]);
|
||||
|
||||
|
|
@ -2840,7 +2840,7 @@ describe('createToolExecuteHandler', () => {
|
|||
{
|
||||
id: 'call_nul_sniff',
|
||||
name: Constants.READ_FILE,
|
||||
args: { file_path: '/mnt/data/mystery_file' },
|
||||
args: { path: '/mnt/data/mystery_file' },
|
||||
},
|
||||
]);
|
||||
|
||||
|
|
@ -2864,7 +2864,7 @@ describe('createToolExecuteHandler', () => {
|
|||
{
|
||||
id: 'call_text',
|
||||
name: Constants.READ_FILE,
|
||||
args: { file_path: '/mnt/data/notes.txt' },
|
||||
args: { path: '/mnt/data/notes.txt' },
|
||||
},
|
||||
]);
|
||||
|
||||
|
|
@ -2888,7 +2888,7 @@ describe('createToolExecuteHandler', () => {
|
|||
{
|
||||
id: 'call_dotted_dir',
|
||||
name: Constants.READ_FILE,
|
||||
args: { file_path: '/mnt/data/proj.v1/notes' },
|
||||
args: { path: '/mnt/data/proj.v1/notes' },
|
||||
},
|
||||
]);
|
||||
|
||||
|
|
@ -2916,7 +2916,7 @@ describe('createToolExecuteHandler', () => {
|
|||
{
|
||||
id: 'call_svg',
|
||||
name: Constants.READ_FILE,
|
||||
args: { file_path: '/mnt/data/icon.svg' },
|
||||
args: { path: '/mnt/data/icon.svg' },
|
||||
},
|
||||
]);
|
||||
|
||||
|
|
|
|||
|
|
@ -1374,10 +1374,10 @@ function isSandboxMissingFileError(error: unknown): boolean {
|
|||
|
||||
function invalidSandboxAuthoringPath(filePath: string): string | null {
|
||||
if (filePath.length === 0) {
|
||||
return 'file_path is required';
|
||||
return 'path is required';
|
||||
}
|
||||
if (filePath.includes('\0')) {
|
||||
return 'file_path cannot contain NUL bytes';
|
||||
return 'path cannot contain NUL bytes';
|
||||
}
|
||||
if (filePath.endsWith('/')) {
|
||||
return `File path "${filePath}" points to a directory. Provide a file path.`;
|
||||
|
|
@ -2270,9 +2270,9 @@ async function handleCreateFileCall(
|
|||
sourceConfigurable?: Record<string, unknown>,
|
||||
sandboxContext?: SandboxSessionContext,
|
||||
): AuthoringResult {
|
||||
const args = tc.args as { file_path?: unknown; content?: unknown; overwrite?: unknown };
|
||||
if (typeof args.file_path !== 'string' || args.file_path.length === 0) {
|
||||
return errorResult(tc, 'file_path is required');
|
||||
const args = tc.args as { path?: unknown; content?: unknown; overwrite?: unknown };
|
||||
if (typeof args.path !== 'string' || args.path.length === 0) {
|
||||
return errorResult(tc, 'path is required');
|
||||
}
|
||||
if (typeof args.content !== 'string') {
|
||||
return errorResult(tc, 'content is required');
|
||||
|
|
@ -2282,25 +2282,25 @@ async function handleCreateFileCall(
|
|||
}
|
||||
|
||||
const overwrite = args.overwrite === true;
|
||||
if (!args.file_path.startsWith(SKILL_FILE_PREFIX)) {
|
||||
if (!args.path.startsWith(SKILL_FILE_PREFIX)) {
|
||||
if (mergedConfigurable?.codeEnvAvailable !== true) {
|
||||
return errorResult(
|
||||
tc,
|
||||
`Path "${args.file_path}" is not a skill file, and this agent does not have code execution enabled.`,
|
||||
`Path "${args.path}" is not a skill file, and this agent does not have code execution enabled.`,
|
||||
);
|
||||
}
|
||||
return await handleSandboxCreateFileCall({
|
||||
tc,
|
||||
options,
|
||||
req,
|
||||
filePath: args.file_path,
|
||||
filePath: args.path,
|
||||
content: args.content,
|
||||
overwrite,
|
||||
sandboxContext,
|
||||
});
|
||||
}
|
||||
|
||||
const parsed = parseSkillAuthoringPath(args.file_path);
|
||||
const parsed = parseSkillAuthoringPath(args.path);
|
||||
if (typeof parsed === 'string') {
|
||||
return errorResult(tc, parsed);
|
||||
}
|
||||
|
|
@ -2378,13 +2378,13 @@ async function handleEditFileCall(
|
|||
sandboxContext?: SandboxSessionContext,
|
||||
): AuthoringResult {
|
||||
const args = tc.args as {
|
||||
file_path?: unknown;
|
||||
path?: unknown;
|
||||
old_text?: unknown;
|
||||
new_text?: unknown;
|
||||
edits?: unknown;
|
||||
};
|
||||
if (typeof args.file_path !== 'string' || args.file_path.length === 0) {
|
||||
return errorResult(tc, 'file_path is required');
|
||||
if (typeof args.path !== 'string' || args.path.length === 0) {
|
||||
return errorResult(tc, 'path is required');
|
||||
}
|
||||
|
||||
const edits = normalizeEditArgs(args);
|
||||
|
|
@ -2392,24 +2392,24 @@ async function handleEditFileCall(
|
|||
return errorResult(tc, edits);
|
||||
}
|
||||
|
||||
if (!args.file_path.startsWith(SKILL_FILE_PREFIX)) {
|
||||
if (!args.path.startsWith(SKILL_FILE_PREFIX)) {
|
||||
if (mergedConfigurable?.codeEnvAvailable !== true) {
|
||||
return errorResult(
|
||||
tc,
|
||||
`Path "${args.file_path}" is not a skill file, and this agent does not have code execution enabled.`,
|
||||
`Path "${args.path}" is not a skill file, and this agent does not have code execution enabled.`,
|
||||
);
|
||||
}
|
||||
return await handleSandboxEditFileCall({
|
||||
tc,
|
||||
options,
|
||||
req,
|
||||
filePath: args.file_path,
|
||||
filePath: args.path,
|
||||
edits,
|
||||
sandboxContext,
|
||||
});
|
||||
}
|
||||
|
||||
const parsed = parseSkillAuthoringPath(args.file_path);
|
||||
const parsed = parseSkillAuthoringPath(args.path);
|
||||
if (typeof parsed === 'string') {
|
||||
return errorResult(tc, parsed);
|
||||
}
|
||||
|
|
@ -2511,13 +2511,13 @@ async function handleReadFileCall(
|
|||
): Promise<ToolExecuteResult> {
|
||||
const { getSkillByName, getSkillFileByPath, getStrategyFunctions, updateSkillFileContent } =
|
||||
options;
|
||||
const args = tc.args as { file_path?: string };
|
||||
if (!args.file_path) {
|
||||
const args = tc.args as { path?: string };
|
||||
if (!args.path) {
|
||||
return {
|
||||
toolCallId: tc.id,
|
||||
status: 'error',
|
||||
content: '',
|
||||
errorMessage: 'file_path is required',
|
||||
errorMessage: 'path is required',
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -2529,23 +2529,23 @@ async function handleReadFileCall(
|
|||
* reference (skill paths are relative `{skillName}/...`), and consulting
|
||||
* `getSkillByName` would just burn a DB round-trip on a guaranteed miss.
|
||||
*/
|
||||
if (args.file_path.startsWith('/mnt/data/')) {
|
||||
if (args.path.startsWith('/mnt/data/')) {
|
||||
if (codeEnvAvailable) {
|
||||
return handleSandboxFileFallback(tc, args.file_path, options, req);
|
||||
return handleSandboxFileFallback(tc, args.path, options, req);
|
||||
}
|
||||
return {
|
||||
toolCallId: tc.id,
|
||||
status: 'error',
|
||||
content: '',
|
||||
errorMessage: `Path "${args.file_path}" is a code-execution sandbox path, but this agent does not have code execution enabled.`,
|
||||
errorMessage: `Path "${args.path}" is a code-execution sandbox path, but this agent does not have code execution enabled.`,
|
||||
};
|
||||
}
|
||||
|
||||
let skillName: string;
|
||||
let relativePath: string;
|
||||
const explicitSkillNamespace = args.file_path.startsWith(SKILL_FILE_PREFIX);
|
||||
const explicitSkillNamespace = args.path.startsWith(SKILL_FILE_PREFIX);
|
||||
if (explicitSkillNamespace) {
|
||||
const parsed = parseSkillAuthoringPath(args.file_path);
|
||||
const parsed = parseSkillAuthoringPath(args.path);
|
||||
if (typeof parsed === 'string') {
|
||||
return {
|
||||
toolCallId: tc.id,
|
||||
|
|
@ -2557,21 +2557,21 @@ async function handleReadFileCall(
|
|||
skillName = parsed.skillName;
|
||||
relativePath = parsed.relativePath;
|
||||
} else {
|
||||
const slashIdx = args.file_path.indexOf('/');
|
||||
const slashIdx = args.path.indexOf('/');
|
||||
if (slashIdx < 1) {
|
||||
if (codeEnvAvailable) {
|
||||
return handleSandboxFileFallback(tc, args.file_path, options, req);
|
||||
return handleSandboxFileFallback(tc, args.path, options, req);
|
||||
}
|
||||
return {
|
||||
toolCallId: tc.id,
|
||||
status: 'error',
|
||||
content: '',
|
||||
errorMessage: `Invalid file path "${args.file_path}". Use format: {skillName}/{path}`,
|
||||
errorMessage: `Invalid file path "${args.path}". Use format: {skillName}/{path}`,
|
||||
};
|
||||
}
|
||||
|
||||
skillName = args.file_path.slice(0, slashIdx);
|
||||
relativePath = args.file_path.slice(slashIdx + 1);
|
||||
skillName = args.path.slice(0, slashIdx);
|
||||
relativePath = args.path.slice(slashIdx + 1);
|
||||
if (!relativePath) {
|
||||
/**
|
||||
* `read_file("output/")`: a malformed-but-unambiguously-not-a-skill
|
||||
|
|
@ -2580,7 +2580,7 @@ async function handleReadFileCall(
|
|||
* dead-ending with a skill-centric error message.
|
||||
*/
|
||||
if (codeEnvAvailable) {
|
||||
return handleSandboxFileFallback(tc, args.file_path, options, req);
|
||||
return handleSandboxFileFallback(tc, args.path, options, req);
|
||||
}
|
||||
return {
|
||||
toolCallId: tc.id,
|
||||
|
|
@ -2642,7 +2642,7 @@ async function handleReadFileCall(
|
|||
*/
|
||||
if (!skillsEffectivelyEnabled) {
|
||||
if (codeEnvAvailable && !explicitSkillNamespace) {
|
||||
return handleSandboxFileFallback(tc, args.file_path, options, req);
|
||||
return handleSandboxFileFallback(tc, args.path, options, req);
|
||||
}
|
||||
return {
|
||||
toolCallId: tc.id,
|
||||
|
|
@ -2681,7 +2681,7 @@ async function handleReadFileCall(
|
|||
const recovered = await recoverAuthorSkill();
|
||||
if (!recovered) {
|
||||
if (codeEnvAvailable && !explicitSkillNamespace) {
|
||||
return handleSandboxFileFallback(tc, args.file_path, options, req);
|
||||
return handleSandboxFileFallback(tc, args.path, options, req);
|
||||
}
|
||||
return {
|
||||
toolCallId: tc.id,
|
||||
|
|
@ -2763,7 +2763,7 @@ async function handleReadFileCall(
|
|||
return {
|
||||
toolCallId: tc.id,
|
||||
status: 'success',
|
||||
content: `File: ${args.file_path}\n\n${addLineNumbers(skill.body)}`,
|
||||
content: `File: ${args.path}\n\n${addLineNumbers(skill.body)}`,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -2794,7 +2794,7 @@ async function handleReadFileCall(
|
|||
return {
|
||||
toolCallId: tc.id,
|
||||
status: 'success',
|
||||
content: `Binary file (${file.mimeType}, ${file.bytes} bytes). Use bash to process: /mnt/data/${args.file_path}`,
|
||||
content: `Binary file (${file.mimeType}, ${file.bytes} bytes). Use bash to process: /mnt/data/${args.path}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -2804,7 +2804,7 @@ async function handleReadFileCall(
|
|||
return {
|
||||
toolCallId: tc.id,
|
||||
status: 'success',
|
||||
content: `File: ${args.file_path} (${file.bytes} bytes)\n\n${addLineNumbers(file.content)}`,
|
||||
content: `File: ${args.path} (${file.bytes} bytes)\n\n${addLineNumbers(file.content)}`,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -2814,14 +2814,14 @@ async function handleReadFileCall(
|
|||
return {
|
||||
toolCallId: tc.id,
|
||||
status: 'success',
|
||||
content: `File "${args.file_path}" is too large to read directly (${file.bytes} bytes, limit: ${MAX_READABLE_BYTES}). Invoke the skill first, then use bash to read it at /mnt/data/${args.file_path}.`,
|
||||
content: `File "${args.path}" is too large to read directly (${file.bytes} bytes, limit: ${MAX_READABLE_BYTES}). Invoke the skill first, then use bash to read it at /mnt/data/${args.path}.`,
|
||||
};
|
||||
}
|
||||
if (isImage && file.bytes > MAX_BINARY_BYTES) {
|
||||
return {
|
||||
toolCallId: tc.id,
|
||||
status: 'success',
|
||||
content: `File too large (${file.bytes} bytes, limit: ${MAX_BINARY_BYTES}). Use bash to process: /mnt/data/${args.file_path}`,
|
||||
content: `File too large (${file.bytes} bytes, limit: ${MAX_BINARY_BYTES}). Use bash to process: /mnt/data/${args.path}`,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -2865,7 +2865,7 @@ async function handleReadFileCall(
|
|||
return {
|
||||
toolCallId: tc.id,
|
||||
status: 'success',
|
||||
content: `File "${args.file_path}" exceeded streaming limit (${streamLimit} bytes). Invoke the skill first, then use bash to read it at /mnt/data/${args.file_path}.`,
|
||||
content: `File "${args.path}" exceeded streaming limit (${streamLimit} bytes). Invoke the skill first, then use bash to read it at /mnt/data/${args.path}.`,
|
||||
};
|
||||
}
|
||||
chunks.push(chunk);
|
||||
|
|
@ -2903,7 +2903,7 @@ async function handleReadFileCall(
|
|||
return {
|
||||
toolCallId: tc.id,
|
||||
status: 'success',
|
||||
content: `Image: ${args.file_path} (${buffer.length} bytes, ${file.mimeType})`,
|
||||
content: `Image: ${args.path} (${buffer.length} bytes, ${file.mimeType})`,
|
||||
artifact: {
|
||||
content: [
|
||||
{ type: 'image_url', image_url: { url: `data:${file.mimeType};base64,${base64}` } },
|
||||
|
|
@ -2919,7 +2919,7 @@ async function handleReadFileCall(
|
|||
return {
|
||||
toolCallId: tc.id,
|
||||
status: 'success',
|
||||
content: `Binary file (${file.mimeType}, ${buffer.length} bytes). Use bash to process: /mnt/data/${args.file_path}`,
|
||||
content: `Binary file (${file.mimeType}, ${buffer.length} bytes). Use bash to process: /mnt/data/${args.path}`,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -2941,14 +2941,14 @@ async function handleReadFileCall(
|
|||
return {
|
||||
toolCallId: tc.id,
|
||||
status: 'success',
|
||||
content: `File too large (${buffer.length} bytes, limit: ${MAX_READABLE_BYTES}). Use bash: cat /mnt/data/${args.file_path}`,
|
||||
content: `File too large (${buffer.length} bytes, limit: ${MAX_READABLE_BYTES}). Use bash: cat /mnt/data/${args.path}`,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
toolCallId: tc.id,
|
||||
status: 'success',
|
||||
content: `File: ${args.file_path} (${buffer.length} bytes)\n\n${addLineNumbers(text)}`,
|
||||
content: `File: ${args.path} (${buffer.length} bytes)\n\n${addLineNumbers(text)}`,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
|
|
@ -3126,16 +3126,16 @@ function getFileAuthoringQueueKey(
|
|||
if (!isHostFileAuthoringToolCall(tc.name, mergedConfigurable)) {
|
||||
return undefined;
|
||||
}
|
||||
const args = tc.args as { file_path?: unknown };
|
||||
if (typeof args.file_path !== 'string' || args.file_path.length === 0) {
|
||||
const args = tc.args as { path?: unknown };
|
||||
if (typeof args.path !== 'string' || args.path.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
if (!args.file_path.startsWith(SKILL_FILE_PREFIX)) {
|
||||
return `sandbox:${args.file_path}`;
|
||||
if (!args.path.startsWith(SKILL_FILE_PREFIX)) {
|
||||
return `sandbox:${args.path}`;
|
||||
}
|
||||
const parsed = parseSkillAuthoringPath(args.file_path);
|
||||
const parsed = parseSkillAuthoringPath(args.path);
|
||||
if (typeof parsed === 'string') {
|
||||
return `skill:${args.file_path}`;
|
||||
return `skill:${args.path}`;
|
||||
}
|
||||
return `skill:${parsed.skillName}`;
|
||||
}
|
||||
|
|
@ -3199,8 +3199,8 @@ export function createToolExecuteHandler(options: ToolExecuteOptions): EventHand
|
|||
);
|
||||
const isSandboxFileAuthoringCall =
|
||||
isFileAuthoringCall &&
|
||||
typeof (tc.args as { file_path?: unknown }).file_path === 'string' &&
|
||||
!(tc.args as { file_path: string }).file_path.startsWith(SKILL_FILE_PREFIX);
|
||||
typeof (tc.args as { path?: unknown }).path === 'string' &&
|
||||
!(tc.args as { path: string }).path.startsWith(SKILL_FILE_PREFIX);
|
||||
if (
|
||||
tc.name === Constants.SKILL_TOOL ||
|
||||
tc.name === Constants.READ_FILE ||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ jest.mock('@librechat/agents', () => ({
|
|||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
file_path: {
|
||||
path: {
|
||||
type: 'string',
|
||||
description: 'For skill files: "{skillName}/{path}".',
|
||||
},
|
||||
|
|
@ -55,9 +55,9 @@ const TOOL_DESCRIPTION_ADVISORY_MAX_LENGTH = 1024;
|
|||
|
||||
function filePathDescription(tool?: LCTool): string {
|
||||
const parameters = tool?.parameters as
|
||||
| { properties?: { file_path?: { description?: string } } }
|
||||
| { properties?: { path?: { description?: string } } }
|
||||
| undefined;
|
||||
return parameters?.properties?.file_path?.description ?? '';
|
||||
return parameters?.properties?.path?.description ?? '';
|
||||
}
|
||||
|
||||
function maxToolDescriptionLength(definitions: LCTool[]): number {
|
||||
|
|
|
|||
|
|
@ -140,13 +140,13 @@ Use for text, CSV, JSON, Markdown, logs, and small source files at paths returne
|
|||
const CODE_READ_FILE_PARAMETERS: LCTool['parameters'] = Object.freeze({
|
||||
type: 'object',
|
||||
properties: {
|
||||
file_path: {
|
||||
path: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Path to a file from code execution output, such as "/mnt/data/result.csv" or another path returned by the execution tool.',
|
||||
},
|
||||
},
|
||||
required: ['file_path'],
|
||||
required: ['path'],
|
||||
}) as LCTool['parameters'];
|
||||
|
||||
const CODE_READ_FILE_DEF: LCTool = Object.freeze({
|
||||
|
|
@ -159,7 +159,7 @@ const CODE_READ_FILE_DEF: LCTool = Object.freeze({
|
|||
const SKILL_CREATE_FILE_PARAMETERS: LCTool['parameters'] = Object.freeze({
|
||||
type: 'object',
|
||||
properties: {
|
||||
file_path: {
|
||||
path: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Path to write. Use "skills/{skillName}/..." for skill files when available, or a code-execution sandbox path such as "/mnt/data/result.txt" when code execution is enabled. For SKILL.md, the YAML frontmatter name must match {skillName}.',
|
||||
|
|
@ -174,13 +174,13 @@ const SKILL_CREATE_FILE_PARAMETERS: LCTool['parameters'] = Object.freeze({
|
|||
default: false,
|
||||
},
|
||||
},
|
||||
required: ['file_path', 'content'],
|
||||
required: ['path', 'content'],
|
||||
}) as LCTool['parameters'];
|
||||
|
||||
const CODE_CREATE_FILE_PARAMETERS: LCTool['parameters'] = Object.freeze({
|
||||
type: 'object',
|
||||
properties: {
|
||||
file_path: {
|
||||
path: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Path to write in the code-execution sandbox, such as "/mnt/data/result.txt". Prefer /mnt/data/{file} for files that should remain available to later sandbox calls.',
|
||||
|
|
@ -195,13 +195,13 @@ const CODE_CREATE_FILE_PARAMETERS: LCTool['parameters'] = Object.freeze({
|
|||
default: false,
|
||||
},
|
||||
},
|
||||
required: ['file_path', 'content'],
|
||||
required: ['path', 'content'],
|
||||
}) as LCTool['parameters'];
|
||||
|
||||
const SKILL_EDIT_FILE_PARAMETERS: LCTool['parameters'] = Object.freeze({
|
||||
type: 'object',
|
||||
properties: {
|
||||
file_path: {
|
||||
path: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Path to edit. Use "skills/{skillName}/..." for skill files when available, or a code-execution sandbox path such as "/mnt/data/result.txt" when code execution is enabled. edit_file cannot rename skills; keep SKILL.md frontmatter name equal to {skillName}.',
|
||||
|
|
@ -227,13 +227,13 @@ const SKILL_EDIT_FILE_PARAMETERS: LCTool['parameters'] = Object.freeze({
|
|||
},
|
||||
},
|
||||
},
|
||||
required: ['file_path'],
|
||||
required: ['path'],
|
||||
}) as LCTool['parameters'];
|
||||
|
||||
const CODE_EDIT_FILE_PARAMETERS: LCTool['parameters'] = Object.freeze({
|
||||
type: 'object',
|
||||
properties: {
|
||||
file_path: {
|
||||
path: {
|
||||
type: 'string',
|
||||
description: 'Path to edit in the code-execution sandbox, such as "/mnt/data/result.txt".',
|
||||
},
|
||||
|
|
@ -258,7 +258,7 @@ const CODE_EDIT_FILE_PARAMETERS: LCTool['parameters'] = Object.freeze({
|
|||
},
|
||||
},
|
||||
},
|
||||
required: ['file_path'],
|
||||
required: ['path'],
|
||||
}) as LCTool['parameters'];
|
||||
|
||||
const SKILL_CREATE_FILE_DESCRIPTION = `Create a new file, or overwrite an existing file with explicit intent.
|
||||
|
|
|
|||
|
|
@ -1630,8 +1630,11 @@ export class MCPConnection extends EventEmitter {
|
|||
this.allowedAddresses,
|
||||
);
|
||||
/** Merge headers: SSE defaults < init headers < user headers (user wins) */
|
||||
const fetchHeaders = new Headers(
|
||||
Object.assign({}, SSE_REQUEST_HEADERS, resolvedInit?.headers, headers),
|
||||
const fetchHeaders = Object.assign(
|
||||
{},
|
||||
SSE_REQUEST_HEADERS,
|
||||
resolvedInit?.headers,
|
||||
headers,
|
||||
);
|
||||
return undiciFetch(urlString, {
|
||||
...resolvedInit,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue