Skip to content

Agent skill dev#6668

Merged
c121914yu merged 12 commits intomainfrom
agent-skill-dev
Apr 7, 2026
Merged

Agent skill dev#6668
c121914yu merged 12 commits intomainfrom
agent-skill-dev

Conversation

@ctlaltlaltc
Copy link
Copy Markdown
Collaborator

No description provided.

@ctlaltlaltc ctlaltlaltc force-pushed the agent-skill-dev branch 2 times, most recently from 55e6482 to 3c7c0ba Compare March 27, 2026 08:33
@ctlaltlaltc ctlaltlaltc marked this pull request as ready for review March 27, 2026 14:05
@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 31, 2026

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 50.57% 22188 / 43872
🔵 Statements 50.57% 22188 / 43872
🔵 Functions 54.86% 716 / 1305
🔵 Branches 87.29% 4129 / 4730
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
packages/global/common/error/errorCode.ts 100% 100% 100% 100%
packages/global/common/error/code/agentSkill.ts 100% 100% 100% 100%
packages/global/core/app/utils.ts 100% 100% 100% 100%
packages/global/core/workflow/node/constant.ts 97.31% 100% 0% 97.31% 245-252
packages/global/core/workflow/node/agent/skillTools.ts 100% 100% 100% 100%
packages/global/support/permission/constant.ts 100% 100% 100% 100%
packages/global/support/permission/agentSkill/constant.ts 100% 100% 100% 100%
packages/global/support/permission/agentSkill/controller.ts 93.33% 50% 100% 93.33% 7
packages/global/support/permission/user/constant.ts 100% 100% 100% 100%
packages/global/support/permission/user/controller.ts 100% 100% 100% 100%
packages/service/env.ts 94.54% 100% 0% 94.54% 67-69
packages/service/common/logger/categories.ts 100% 100% 100% 100%
packages/service/common/mongo/index.ts 74.6% 69.69% 83.33% 74.6% 56-76, 79, 81-82, 146-148, 153-157
packages/service/common/s3/mq.ts 28.78% 100% 0% 28.78% 29-43, 46-80
packages/service/core/agentSkills/archiveUtils.ts 43.05% 100% 57.14% 43.05% 20-41, 83-112
packages/service/core/agentSkills/controller.ts 60.2% 82.14% 61.53% 60.2% 122-125, 149-150, 160-163, 263-264, 293-294, 305-427, 433-452
packages/service/core/agentSkills/sandboxConfig.ts 47.52% 44.44% 60% 47.52% 57-59, 82-88, 99, 102, 110-121, 140-141, 144-145, 179-182, 187-194, 197, 245-252, 272-317, 329-340, 346-355
packages/service/core/agentSkills/sandboxController.ts 4.82% 33.33% 20% 4.82% 80-455, 471-589
packages/service/core/agentSkills/sandboxSchema.ts 100% 100% 100% 100%
packages/service/core/agentSkills/skillMdBuilder.ts 97.24% 89.39% 100% 97.24% 94-98, 101-105
packages/service/core/agentSkills/storage.ts 48.8% 57.14% 46.15% 48.8% 48-58, 118-121, 153-165, 182-188, 194-197, 213-291
packages/service/core/agentSkills/utils.ts 91.95% 87.2% 100% 91.95% 35-40, 82, 84, 90, 108-109, 159-160, 169, 181-182
packages/service/core/agentSkills/zipBuilder.ts 61.04% 78.78% 66.66% 61.04% 76-79, 99, 130-136, 164-170, 199-203, 225-229, 235-258, 275-296
packages/service/core/agentSkills/version/controller.ts 80% 80% 80% 80% 167-170, 209-242
packages/service/core/ai/sandbox/controller.ts 51.35% 54.16% 60% 51.35% 66-88, 105-110, 123-146, 149-154, 157-162, 179-180, 186-187, 203-206, 217-219, 226-242
packages/service/core/app/controller.ts 66.31% 90% 71.42% 66.31% 38-90, 123-124, 130-143
packages/service/core/app/delete/index.ts 72.41% 100% 50% 72.41% 11-18
packages/service/core/workflow/dispatch/ai/agent/index.ts 4.08% 100% 0% 4.08% 80-638
packages/service/core/workflow/dispatch/ai/agent/utils.ts 10.9% 100% 0% 10.9% 12-83
packages/service/core/workflow/dispatch/ai/agent/capability/sandboxSkills.ts 18.13% 95.45% 33.33% 18.13% 62-120, 192-485
packages/service/core/workflow/dispatch/ai/agent/master/call.ts 4.26% 100% 0% 4.26% 60-776
packages/service/core/workflow/dispatch/ai/agent/master/prompt.ts 6.52% 100% 0% 6.52% 5-302
packages/service/core/workflow/dispatch/ai/agent/sub/file/utils.ts 22.62% 100% 0% 22.62% 35-180, 183-196
packages/service/core/workflow/dispatch/ai/agent/sub/sandbox/index.ts 12.94% 100% 0% 12.94% 29-54, 57-86, 89-116
packages/service/core/workflow/dispatch/ai/agent/sub/sandbox/lifecycle.ts 3.14% 100% 0% 3.14% 59-496
packages/service/core/workflow/dispatch/ai/agent/sub/sandbox/prompt.ts 1.96% 100% 0% 1.96% 11-18, 31-77
packages/service/core/workflow/dispatch/ai/agent/sub/sandbox/skill.ts 1.96% 100% 0% 1.96% 30-222
packages/service/support/permission/agentSkill/auth.ts 82.65% 85.71% 100% 82.65% 69-77, 89-94, 108-109
packages/service/support/user/audit/util.ts 27.69% 63.63% 50% 27.69% 17-25, 28-36, 41-47, 52-53, 56-60, 86-104
projects/app/src/pageComponents/app/detail/Edit/ChatAgent/utils.ts 17.2% 45.45% 40% 17.2% 68-73, 92-93, 95-100, 111-296, 299-318, 321-377
projects/app/src/pages/api/core/agentSkills/debugChat.ts 80.67% 75% 100% 80.67% 227-228, 247-248, 300-359
projects/app/src/pages/api/core/agentSkills/export.ts 100% 100% 100% 100%
projects/app/src/pages/api/core/agentSkills/debugSession/delete.ts 100% 100% 100% 100%
projects/app/src/pages/api/core/agentSkills/debugSession/list.ts 100% 100% 100% 100%
projects/app/src/pages/api/core/agentSkills/debugSession/records.ts 100% 100% 100% 100%
projects/app/src/pages/api/core/chat/feedback/updateUserFeedback.ts 100% 100% 100% 100%
projects/app/src/pages/api/core/chat/record/getRecords_v2.ts 35.44% 100% 50% 35.44% 91-207
projects/app/src/pages/api/support/outLink/playground/config.ts 100% 57.14% 100% 100%
projects/app/src/pages/api/support/outLink/playground/update.ts 100% 100% 100% 100%
projects/app/src/pages/api/support/user/account/loginout.ts 100% 100% 100% 100%
projects/app/src/service/common/system/index.ts 27.51% 100% 15.38% 27.51% 31-51, 57-59, 62-63, 66-68, 71-73, 75-77, 79-81, 92-112, 136-234
projects/app/src/service/core/sandbox/proxyUtils.ts 42.85% 92.85% 40% 42.85% 33-36, 41-47, 51-59, 82-103, 107-115, 118-119, 126-127
projects/app/src/service/support/permission/auth/chat.ts 91.81% 77.46% 66.66% 91.81% 204-212, 284-296
projects/app/src/service/support/permission/auth/outLink.ts 14% 100% 0% 14% 15-17, 20-70
Generated in workflow #319 for commit f8e37ce by the Vitest Coverage Report Action

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 3, 2026

Build Successful - Preview code-sandbox Image for this PR:

registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-pr:code-sandbox_f8e37ce6f689cc22fc0f3ea6d993e3a3aefa2ee8

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 3, 2026

Build Successful - Preview fastgpt Image for this PR:

registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-pr:fastgpt_f8e37ce6f689cc22fc0f3ea6d993e3a3aefa2ee8

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 3, 2026

Build Successful - Preview mcp_server Image for this PR:

registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-pr:mcp_server_f8e37ce6f689cc22fc0f3ea6d993e3a3aefa2ee8

@c121914yu c121914yu force-pushed the agent-skill-dev branch 3 times, most recently from 342e62f to 0a4ad20 Compare April 7, 2026 06:20
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 7, 2026

Docs Preview Deployed!

🔗 👀 Click here to visit preview

registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-docs-pr:f8e37ce6f689cc22fc0f3ea6d993e3a3aefa2ee8

c121914yu
c121914yu previously approved these changes Apr 7, 2026
Copy link
Copy Markdown
Collaborator

@c121914yu c121914yu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agent Skill Dev — Code Review

感谢提交这个相当完整的 Agent Skill 系统!整体架构清晰:Skill CRUD + 版本管理 + 沙箱编辑/运行时 + 权限继承 + 审计 五个层次都有覆盖,API 合约用 zod schema 定义并接入了 OpenAPI 文档,符合项目规范。

这是一个 +29,704 / -6,255 行 的大 PR,我重点审查了核心服务层(packages/service/core/agentSkills/)、沙箱生命周期(workflow/dispatch/ai/agent/sub/sandbox/)、权限校验和关键 API 路由。受 GitHub diff 行数上限影响,前端 UI、i18n、Helm/Docker 部署相关变更未逐行审查。


⚠️ 必须修复 (🔴 阻塞合并)

1. parseYamlFrontmatter 嵌套对象解析逻辑有 Bug (packages/service/core/agentSkills/utils.ts:47-98)

手写的 YAML parser 使用 stack 但只 push 不 pop,也不跟踪缩进层级。一旦遇到 metadata: 这样的嵌套键,后续所有字段都会被错误地写入嵌套对象,即使它们应该在根级别。

例如:

name: my-skill
metadata:
  category: tools
description: My skill   # ❌ 会被错误写入 metadata.description

SKILL.md frontmatter 解析是导入/部署技能的关键路径,这个 bug 会导致数据静默丢失。强烈建议改用 js-yaml (项目已在其他模块用过)。

2. createSkill 缺少 name 字符集校验 → 沙箱内 shell 命令注入风险 (projects/app/src/pages/api/core/agentSkills/create.ts:67-71)

创建技能时只校验 name 长度 ≤50,没有限制字符集。但是 lifecycle.ts:152,158skill.name 直接拼到 shell 命令里:

const zipPath = `${workDirectory}/package_${skill.name}.zip`;
await sandbox.execute(
  `cd ${workDirectory} && unzip -o package_${skill.name}.zip && rm package_${skill.name}.zip`
);

如果用户创建一个名字含 ; rm -rf /; # 的技能,部署到沙箱时会执行任意命令。即使沙箱隔离能限制爆炸半径,这仍然是一个真实的注入漏洞。

建议:在 create/update 入口校验 name 必须匹配 /^[a-z0-9][a-z0-9-]{0,49}$/,与 extractSkillFromMarkdown 中的校验保持一致。或者在 lifecycle 中改用 base64/hash 命名 zip 文件。

3. canModifySkill 是死代码,且逻辑过时 (packages/service/core/agentSkills/controller.ts:256-273)

这个函数已经被 authSkill 取代,全仓库只有它自己的定义和一行注释引用。但它的逻辑只检查 tmbId === skill.tmbId(创建者),完全忽略了协作者权限和文件夹继承权限。应该删除,避免后续被人误用。

4. 沙箱数量限制存在竞态条件 (packages/service/core/agentSkills/sandboxController.ts:237-247, lifecycle.ts:272-285)

const activeCount = await MongoSandboxInstance.countDocuments({...});
if (activeCount >= maxEditDebug) throw new Error(...);
// ⚠️ 中间窗口: 多个并发请求都会通过
await newSandbox.create();

两个并发请求都会通过 count 检查,然后都创建沙箱,突破上限。应该用 MongoSandboxInstance.findOneAndUpdate 加原子锁,或者用 Redis 分布式锁。在多副本部署下这个问题会更严重。


🟡 建议改进

5. 路径穿越校验不完整 (packages/service/core/agentSkills/archiveUtils.ts:31)

if (!normalized || normalized.includes('../')) continue;

这个校验漏掉了:

  • .. 单独作为路径(没有 /)
  • /.. 结尾的路径(foo/..)
  • 大小写或 URL 编码绕过(取决于 decompress 库的解析)

建议:用 path.posix.normalize() 后判断 result.startsWith('..')path.isAbsolute。这些恶意路径最终会被 repackFileMapAsZip 重新打包,然后被沙箱内的 unzip -o 解压(unzip 默认允许 .. 组件,需要 -: 才禁用)。

6. findSkillAndAllChildren / getParents 没有循环检测 (controller.ts:305-344, 411-427)

两个递归函数都没有 visited set,如果数据库里 parentId 形成环(理论上 schema 没有约束),会导致无限递归 → 栈溢出。updateParentFoldersUpdateTime(同文件 432 行)正确地用了 existsId set,这两个函数应该参照实现。

另外 findSkillAndAllChildren 是 N+1 查询模式 — 每层文件夹一个查询。建议改用 MongoDB $graphLookup aggregation。

7. 'edit-debug' 魔法字符串散落在 5+ 处

搜索结果(部分):

  • sandboxController.ts:127 定义了 EDIT_DEBUG_CHAT_ID = 'edit-debug' 但只在本文件内使用
  • lifecycle.ts:445debugChat.ts:233save-deploy.ts:63 都重复硬编码了 'edit-debug'

应该提到 @fastgpt/global/core/agentSkills/constants 导出共享。

8. fetchSkillsMetaForPrompt 每次 dispatch 都从 S3 下载 ZIP (capability/sandboxSkills.ts:62-120)

仅仅是为了构造 prompt 中的 SKILL.md 路径,就要并发下载所有技能 ZIP 包并解压。这是 hot path(每次 agent 调用都走),应该把 SKILL.md 的相对路径在 import/save-deploy 时存到 version metadata 里,运行时直接用。

9. 项目日志规范违反: 4 处 console.* (schema.ts:113, version/schema.ts:83, save-deploy.ts:187, edit.ts:82)

根据 .claude/skills/system-pr_review/style/logger.md:

服务端统一使用 @fastgpt/service/common/logger,避免 console.log

虽然 schema 索引创建在模块加载时,但仍然应该走统一 logger。save-deploy.tsedit.ts 的 catch 块里 console.error 也丢失了上下文(skillId、tmbId、teamId),不利于事后排查。

10. save-deploy.ts / edit.ts / import.ts 没用 NextAPI 包装器

项目里的 API 路由约定是 export default NextAPI(handler)(参见 create.ts)。这三个文件直接 export default async function handler 绕过了统一中间件 — 失去了请求日志、错误处理、链路上下文等能力。SSE 路由可能有特殊处理需求(edit.ts),但 save-deploy.ts 返回 JSON,理论上应该用 NextAPI

11. existingInstance.metadata!.endpoint! 非空断言不安全 (sandboxController.ts:143,191)

这两个非空断言没有任何前置校验,如果数据库中存在历史数据(metadata 缺 endpoint 字段),会运行时崩溃。建议加显式检查并降级到重新创建沙箱。

12. buildSessionHandlerbuildEditDebugHandler ~99% 重复 (sandboxSkills.ts:369-485)

两个 handler 几乎完全相同(只有错误处理细节差异),违反 DRY。建议抽取共享的 toolId → schema 映射表,然后用单个 handler factory。

13. isSandboxExpiredError 启发式过宽松 (sandboxSkills.ts:122-136)

return (
  msg.includes('not found') ||
  msg.includes('not exist') ||
  msg.includes('connection') ||  // ⚠️ 非常宽松
  msg.includes('sandbox_not_found') ||
  msg.includes('econnrefused') ||
  msg.includes('econnreset')
);

'connection' 这种字符串可能匹配很多无关错误(比如下游 API 连接错误),会导致不必要的沙箱重建。建议改用错误类型/code 判定,或者从 SDK 暴露专用的 SandboxNotFoundError

14. 沙箱查询缺少 teamId 隔离 (sandboxController.ts:130-134, lifecycle.ts:193-196,443-447)

const existingInstance = await MongoSandboxInstance.findOne({
  appId: skillId,
  chatId: EDIT_DEBUG_CHAT_ID,
  'metadata.sandboxType': SandboxTypeEnum.editDebug
});

虽然外层 API 已经做了 authSkill 校验,但 service 层方法本身没有 teamId scope,纵深防御缺失。如果以后有内部调用绕过 API 层,可能产生跨团队数据访问。

15. updateParentFoldersUpdateTime 用事务做 fire-and-forget (controller.ts:432-452)

这个函数是 fire-and-forget(.catch 吃掉错误),却用了 mongoSessionRun — 事务的开销没有必要。改用 bulkWrite 一次性更新所有父文件夹的 updateTime 即可。

16. import.ts:174 错误响应可能泄漏内部信息

jsonRes(res, { code: 500, error: err });

直接传 err 对象,会被序列化包括堆栈、内部路径等。应该用 err?.message || 'Import failed'


🟢 可选优化

  • packageSkillInSandbox 接受 workDirectory 参数并直接拼到 find 命令里。当前所有 caller 都用 defaults.workDirectory,但函数签名暴露了风险。建议要么去掉参数,要么校验值是合法的绝对路径。
  • buildVolumeConfig (sandboxConfig.ts:328-340) 把 sessionId 转成 DNS label 时,如果 sessionId 全是特殊字符可能产生空字符串,违反 K8s naming。加一个 fallback。
  • buildBaseContainerEnvFASTGPT_SESSION_ID 暴露到沙箱容器。当 sessionId === chatId 时,等同于把 chatId 透给用户代码。值得评估隐私影响。
  • existingInstance.metadata?.skillIds.map(String) (lifecycle.ts:221-223) 用了可选链但 .map 没保护,如果 metadata 存在但 skillIds 是 undefined 会崩溃。
  • YAML parser 还有其他问题:不支持多行字符串、转义引号、嵌套数组、注释末尾;Number('1e5') 会通过数值检测但语义错误。综合看,确实应该换 js-yaml
  • parseSkillMarkdownerror 字段返回了但部分调用方没检查(如 lifecycle.ts:116sandboxSkills.ts:90)— 解析失败时会用空 frontmatter 继续运行,可能掩盖问题。

🧪 建议补充的测试

  1. parseYamlFrontmatter 嵌套对象:验证 root-level 字段不会被错误归到 nested 对象
  2. extractToFileMap 路径穿越:针对 ..foo/..、绝对路径(TAR)、Windows \ 路径的边界用例
  3. createEditDebugSandbox 并发限流:并发 N+1 个请求,断言只有 N 个成功
  4. findSkillAndAllChildren 循环 parent:构造环形数据,断言不会栈溢出
  5. 技能名特殊字符:含 ;$、空格、引号的技能名能否走通整个 import → deploy → unzip → run 链路
  6. authSkill 权限继承:协作者通过文件夹继承权限的场景

📊 总体评价

维度 评分
架构设计 ⭐⭐⭐⭐☆ — 分层清晰,capability 抽象合理
安全性 ⭐⭐⭐☆☆ — 权限继承做得好,但有 shell 注入和路径穿越隐患
健壮性 ⭐⭐⭐☆☆ — YAML parser bug + 多个非空断言 + 缺少循环检测
性能 ⭐⭐⭐☆☆ — 热路径上重复下载 ZIP 是显著浪费
代码风格一致性 ⭐⭐⭐☆☆ — 4 处 console、3 个路由绕过 NextAPI、魔法字符串散落

🚀 审查结论

Request changes — 主要是 YAML parser bug(#1)、技能名注入(#2)、死代码(#3)和竞态条件(#4)需要在合并前解决。其他 🟡 项可以分批修复但建议在合并前至少处理 #5、#6#9

这是一个体量大、业务复杂的功能,能做到当前完整度已经很不容易。期待看到修复后的版本! 🙏

}

return result;
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 严重 Bug:嵌套对象解析逻辑错误

这个手写 YAML parser 用 stack 但只 push 不 pop,也不跟踪缩进。一旦遇到 metadata: 这样的嵌套键,后续所有字段都会被错误写入 nested 对象,即使是 root-level 字段。

复现:

name: my-skill
metadata:
  category: tools
description: My skill

实际解析结果:description 会被错误归到 metadata.description,而不是 root。

SKILL.md frontmatter 解析是导入/部署技能的核心路径,这个 bug 会导致数据静默丢失

强烈建议改用 js-yaml(npm 上最成熟的 YAML 库),手写 parser 还有其他若干局限(多行字符串、转义、注释末尾、1e5 数值误判等)。

import yaml from 'js-yaml';

function parseYamlFrontmatter(text: string): Record<string, any> {
  const result = yaml.load(text);
  return typeof result === 'object' && result !== null ? (result as Record<string, any>) : {};
}

onProgress?.({ sandboxId, phase: 'extractingPackage', skillName: skill.name });
const extractResult = await sandbox.execute(
`cd ${workDirectory} && unzip -o package_${skill.name}.zip && rm package_${skill.name}.zip`
);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 沙箱内 shell 命令注入风险

skill.name 被直接拼接到 shell 命令里:

`cd ${workDirectory} && unzip -o package_${skill.name}.zip && rm package_${skill.name}.zip`

projects/app/src/pages/api/core/agentSkills/create.ts:67-71 在创建技能时没有校验 name 字符集 — 只校验长度 ≤50。这意味着用户可以创建一个名为 ; rm -rf /; # 的技能,部署时会在沙箱内执行任意命令。

虽然沙箱隔离能限制爆炸半径,但这是一个真实的注入漏洞,而且也违反纵深防御原则。

两种修复方式(任选其一):

  1. create.tsupdate.ts 的入口校验 name 字符集,与 extractSkillFromMarkdown 中的正则保持一致:
if (!/^[a-z0-9][a-z0-9-]{0,49}$/.test(name.trim())) {
  throw new Error('Skill name must be lowercase alphanumeric and hyphens only');
}
  1. 在 lifecycle 中改用 hash 或 skillId 命名 zip 文件,完全避开用户输入:
const zipPath = `${workDirectory}/package_${skill._id}.zip`;


// Only the creator can modify
return skill.tmbId?.toString() === tmbId;
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 死代码 + 逻辑过时

全仓库搜索 canModifySkill:只有这里的定义,以及 save-deploy.ts:51 的一行注释引用。它已经被 authSkill 取代。

更糟的是它的逻辑只检查 tmbId === skill.tmbId(只允许创建者),完全忽略了:

  • 团队所有者
  • 协作者写权限
  • 文件夹继承权限

如果未来有人误用这个函数,会绕过整个权限系统。建议直接删除

onProgress?.({ sandboxId: skillId, phase: 'failed', message });
throw new Error(message);
}
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 竞态条件:沙箱数量限制非原子

const activeCount = await MongoSandboxInstance.countDocuments({...});
if (activeCount >= maxEditDebug) throw new Error(...);
// ⚠️ 中间窗口: N 个并发请求都会通过
await newSandbox.create();

并发场景:N 个用户同时点 "打开调试沙箱",N 个请求都看到 activeCount = limit - 1,N 个请求都通过检查,N 个沙箱被创建 → 突破上限。在多副本部署下问题更严重。

修复方案:

  1. 用 Redis 分布式锁 (SETNX + TTL)
  2. 或者先 findOneAndUpdate 一个 "counter" 文档,失败则拒绝
  3. 或者直接在 MongoSandboxInstance.create 后立刻 countDocuments,如果超过 limit 就回滚刚创建的实例

lifecycle.ts:272-285 有同样的问题,需要一并修。

fileMap[normalized] = file.data;
}
return fileMap;
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 路径穿越校验不完整

if (!normalized || normalized.includes('../')) continue;

漏掉的情况:

  • .. 单独(没有 /)
  • /.. 结尾(foo/..)
  • TAR 文件中的绝对路径(经过 replace(/^\/+/, '') 后变成相对路径,但仍然能把文件写到 workspace 根目录)

这些恶意路径最终会被 repackFileMapAsZip 重新打包,然后被沙箱内的 unzip -o 解压。unzip 默认允许 .. 路径组件(需要 -: 才禁用)。

建议改用 path.posix.normalize 验证:

import path from 'path';

const normalized = path.posix.normalize(
  file.path.replace(/\\/g, '/').replace(/^\/+/, '')
);
if (!normalized || normalized === '..' || normalized.startsWith('../') || path.posix.isAbsolute(normalized)) {
  continue;
}

directory: ''
}
);
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 热路径上重复下载 ZIP,显著的性能问题

这个函数在每次 agent dispatch 时都被调用,会并发下载所有技能 ZIP 包(从 S3),解压,只为了构造 prompt 中的 SKILL.md 路径。

如果一个 agent 配置了 5 个技能,每次对话都要 5 次 S3 GET + 5 次 ZIP 解压。

建议:在 import / save-deploy 时把 SKILL.md 的相对路径(info.relativePath)和 frontmatter 解析结果直接存到 MongoAgentSkillsVersion.metadata 中,运行时直接读 DB。

// version schema 增加字段
skillMdRelativePath: { type: String },
frontmatter: { type: Object }  // 缓存 name/description/avatar

这样 fetchSkillsMetaForPrompt 就可以缩减成一次 MongoDB 查询。

);
}
return false;
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 错误识别启发式过于宽松

return (
  msg.includes('not found') ||
  msg.includes('not exist') ||
  msg.includes('connection') ||  // 太宽松
  msg.includes('sandbox_not_found') ||
  msg.includes('econnrefused') ||
  msg.includes('econnreset')
);

'connection' 这种通用词可能匹配下游 API、数据库甚至 LLM 提供商的连接错误,会触发不必要的沙箱重建,放大故障。

建议:从 @fastgpt-sdk/sandbox-adapter 暴露专用 error class(如 SandboxNotFoundError / SandboxUnreachableError),用 instanceof 判定,而不是字符串匹配。

sandboxId: existingInstance.sandboxId
});

const endpointInfo = existingInstance.metadata!.endpoint!;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 非空断言不安全

const endpointInfo = existingInstance.metadata!.endpoint!;

这两个非空断言没有任何前置校验。如果数据库里有历史数据(metadata 缺 endpoint 字段,或者旧版字段名不一致),会直接抛 Cannot read properties of undefined

建议显式检查并降级到重新创建沙箱:

const endpointInfo = existingInstance.metadata?.endpoint;
if (!endpointInfo) {
  addLog.warn('[Sandbox] Running instance missing endpoint, recreating', { instanceId: existingInstance._id });
  // fall through to creation path
} else {
  // reuse path
}


res.end();
} catch (err: any) {
console.error('[API] Create edit-debug sandbox error:', err);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 违反项目日志规范

根据 .claude/skills/system-pr_review/style/logger.md:服务端统一使用 @fastgpt/service/common/logger,避免 console.log/console.error

这里 console.error 还丢失了关键上下文(skillId、tmbId、teamId),不利于事后排查。

建议:

import { getLogger, LogCategories } from '@fastgpt/service/common/logger';
const logger = getLogger(LogCategories.MODULE.AGENT_SKILLS.SANDBOX);

// catch 中:
logger.error('Create edit-debug sandbox failed', { error: err, skillId, tmbId, teamId });

同样问题: save-deploy.ts:187schema.ts:113version/schema.ts:83

const handler = handlers[toolId];
if (!handler) return { response: 'Unknown sandbox tool', usages: [] };
return handler();
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 buildEditDebugHandlerbuildSessionHandler 99% 重复

两个 handler 几乎一模一样,只在 dispose/renew 细节上不同。违反 DRY,后续维护要同步改两处容易出错。

建议:抽取一个 toolId → schema 映射表,然后统一一个 handler factory:

const SANDBOX_TOOL_DISPATCH = {
  [SandboxToolIds.readFile]: { schema: SandboxReadFileSchema, dispatch: dispatchSandboxReadFile, withSkillRef: true },
  [SandboxToolIds.writeFile]: { schema: SandboxWriteFileSchema, dispatch: dispatchSandboxWriteFile },
  // ...
} as const;

async function dispatchSandboxTool(toolId: string, args: string, ctx, ...) {
  const def = SANDBOX_TOOL_DISPATCH[toolId];
  if (!def) return null;
  const parsed = def.schema.safeParse(parseJsonArgs(args));
  if (!parsed.success) return { response: parsed.error.message, usages: [] };
  // ...
}

ctlaltlaltc and others added 12 commits April 7, 2026 22:50
…figs (#6710)

* chore: Rename container names for consistency in Docker configs

* chore: Rename service names for consistency in Docker configs

chore: Update OpenSandbox versions and image repositories (#6709)

* chore: Update OpenSandbox versions and image repositories

* yml version

* images

* init yml

* port

---------

Co-authored-by: archer <545436317@qq.com>

refactor(chat): optimize sandbox status logic and decouple UI/Status hooks (#6713)

* refactor(chat): optimize sandbox status logic and decouple UI/Status hooks

* fix: useRef, rename onClose to afterClose

Update .env.template (#6720)

aiproxy默认的请求地址改成http协议

feat: comprehensive agent skill management and sandbox infrastructure optimization

- Skill System: Implemented a full skill management module including CRUD operations, folder organization, AI-driven skill generation, and versioning (switch/update).
- Sandbox Infrastructure: Introduced 'volume-manager' for PVC and Docker volume lifecycle management, replacing the MinIO sync-agent for better data persistence.
- Workflow Integration: Enhanced the Agent node to support skill selection and configuration, including new UI components and data normalization.
- Permission Management: Added granular permission controls for skills, supporting collaborators, owner transfers, and permission inheritance.
- UI/UX: Added a dedicated Skill dashboard, sandbox debug interface (terminal, logs, and iframe proxy), and comprehensive i18n support.
- Maintenance: Migrated Docker services to named volumes, optimized sandbox instance limits, and improved error handling for sandbox providers.

Co-authored-by: chanzhi82020 <chenzhi@sangfor.com.cn>
Co-authored-by: lavine77
Signed-off-by: Jon <ljp@sangfor.com.cn>

feat: hide skill

prettier
@c121914yu c121914yu merged commit 57a505f into main Apr 7, 2026
5 of 6 checks passed
@c121914yu c121914yu deleted the agent-skill-dev branch April 7, 2026 14:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants