第 9 章:扩展机制:Skills、Plugins 与 MCP
定位:本章展示 octos 当前源码中的三种扩展机制:Skills(Markdown 声明式)、Plugins(本地可执行工具 / skill package extras)、MCP(标准化协议集成)。前置依赖:第 6 章。适用场景:想为 octos 编写自定义扩展的贡献者,以及想理解 Agent 扩展架构设计的开发者。
Agent 的价值来自适配不同场景的能力。法律文书审查需要法律提示,研究 Agent 需要长时后台任务,远程服务集成又需要标准协议。把所有扩展都塞进同一种机制,会让简单需求过度工程化,也会让复杂需求被迫挤进不合适的抽象。
octos 当前的答案不是“一种万能插件”,而是三条互补轨道:
- Skills:改变 Agent 的提示与上下文
- Plugins:把本地可执行程序包装成 Tool,并承载 skill package extras
- MCP:通过标准协议连接外部工具服务器
9.1 Skills 轨道:Markdown 声明式扩展
Skills 是最轻量的扩展机制。一个 skill 的核心就是一个 SKILL.md,外加可选的 manifest.json。
9.1.1 SKILL.md 格式
---
name: code-review
description: Review code changes for bugs, security issues, and style
version: 1.0.0
requires_bins: rg,git
requires_env: GITHUB_TOKEN
---
When reviewing code, focus on:
1. Security vulnerabilities
2. Error handling completeness
3. Behavior regressions
SkillsLoader 并没有实现完整 YAML 解析器。它做的是两步简化处理:
- 用
split_frontmatter()找到首尾---之间的 frontmatter 块(../octos/crates/octos-agent/src/skills.rs:235-252) - 用
fm_value()从简单的key: value行里读取name、description、requires_bins、requires_env、always等字段(../octos/crates/octos-agent/src/skills.rs:178-232,255-276)
这意味着 skill 元数据的设计目标不是“表达力最大”,而是“足够稳定、足够便宜”。available 的判断也来自这里:requires_bins 里的命令都能找到、requires_env 里的环境变量都存在,skill 才算可用(../octos/crates/octos-agent/src/skills.rs:196-212)。
9.1.2 SkillsLoader 与分层覆盖
SkillsLoader 本身只维护一个“技能目录列表”,真正的优先级是在 runtime 里组装出来的(../octos/crates/octos-agent/src/skills.rs:31-176)。当前 gateway 路径的优先级是:
data_dir/skillsproject_dir/skillsproject_dir/bundled-app-skillsOCTOS_SKILLS_PATH指定的额外目录- 编译进二进制的 built-in skills
这套层次来自 ../octos/crates/octos-cli/src/commands/gateway/gateway_runtime.rs:526-527,646-667 和 ../octos/crates/octos-cli/src/commands/gateway/profile_factory.rs:538-617。实现方式也很有意思:loader 先放入 builtins,再按“低优先级目录先扫描,高优先级目录后覆盖”的顺序遍历,并通过 retain 删掉同名旧 skill(../octos/crates/octos-agent/src/skills.rs:68-108)。
这里需要特别区分配置继承和本地 skill 继承:子账号可以继承父 profile 的 LLM/search/apps/email 等结构化配置,但 customer-installed skills 不从父账号继承。skills_scope.rs 明确把 account skills 限定为当前账号自己的 data_dir/skills,plugin dirs 也只返回当前 account 的 skills 目录(../octos/crates/octos-cli/src/skills_scope.rs:1-38)。这避免父账号安装的本地可执行扩展在子账号中被静默启用。
所以这不是简单的“工作区覆盖全局”三层模型,而是一个更细的 layered view。读者如果只记住“当前账号 skills 最高优先级;project/bundled/env/builtin 提供共享基线;父账号 customer skills 不进入子账号”,就已经抓住当前实现的主线了。
9.1.3 XML 技能索引
build_skills_summary() 会把当前可见的 skill 集合转成 XML,注入系统提示(../octos/crates/octos-agent/src/skills.rs:137-154):
<skills>
<skill available="true" tools="true">
<name>deep-search</name>
<description>Deep web research...</description>
<location>/.../SKILL.md</location>
</skill>
</skills>
这里有三个容易写错的点:
- 当前 XML 里没有
name="..."属性,而是<name>子节点 tools="true"的含义是“该 skill 目录包含manifest.json”,不是“这个 skill 正在执行工具”location会把 skill 的真实来源路径暴露给模型,帮助它区分 builtin 与外部 skill
因此 XML 摘要不是单纯的“可用技能列表”,它还是模型可见的 技能目录索引。
9.1.4 spawn_only:自动后台化,而不是隐藏工具
spawn_only 标记定义在 plugin/skill manifest 的工具项上(../octos/crates/octos-agent/src/plugins/manifest.rs:98-116),但它的运行时语义并不在 manifest 里,而在 registry 和 agent 执行循环里:
PluginLoader会把这些工具名登记为spawn_only(../octos/crates/octos-agent/src/plugins/loader.rs:93-113)ToolRegistry为它们维护自定义提示文案和任务跟踪状态(../octos/crates/octos-agent/src/tools/registry.rs:123-178)- 主 agent 发现某次 tool call 命中
spawn_only时,不同步执行,而是直接后台tokio::spawn一个任务,立刻向模型返回spawn_only_message(../octos/crates/octos-agent/src/agent/execution.rs:105-245)
这意味着 spawn_only 不是“从 ToolSpec 里隐藏掉”。按当前实现,它们仍然注册在工具系统里并对模型可见;差别只是调用时会被自动后台化。
更进一步,resolve_extras() 还会在 skill package 含有 spawn_only 工具时自动把 SKILL.md 本身注入 prompt fragments(../octos/crates/octos-agent/src/plugins/extras.rs:52-61)。这样模型既能看到工具,也能同时拿到“什么时候该用这个后台工具”的提示上下文。
而到了 subagent 场景,registry 会调用 clear_spawn_only() 清空这些标记,因为“subagent 本身就是后台上下文”,此时工具会像普通工具一样直接执行(../octos/crates/octos-agent/src/tools/registry.rs:136-143)。
9.2 Plugins 轨道:本地可执行工具与 skill package extras
如果说 Skills 负责改变 Agent 的“思维方式”,Plugins 负责的就是让 Agent 真正调用外部程序完成工作。
9.2.1 runtime manifest:不只是工具声明
当前 runtime 热路径使用的是 ../octos/crates/octos-agent/src/plugins/manifest.rs 中的 manifest 结构:
{
"name": "weather",
"version": "1.0.0",
"tools": [
{
"name": "get_weather",
"description": "Get current weather for a location",
"input_schema": {
"type": "object",
"properties": {
"city": { "type": "string" }
}
},
"env": ["WEATHER_API_KEY"],
"risk": "medium",
"concurrency_class": "safe"
}
],
"sha256": "a1b2c3...",
"timeout_secs": 600,
"requires_network": true
}
但把它理解为“纯工具 manifest”已经不够了。当前这个结构还支持:
mcp_servershooksprompts.includebinariesspawn_onlyspawn_only_messageenv/env_allowlistriskconcurrency_class
因此它更接近一个 skill package runtime manifest。如果 manifest.tools 为空,但声明了 MCP、hooks 或 prompt fragments,PluginLoader 会跳过可执行文件搜索,照样把 extras 装进系统(../octos/crates/octos-agent/src/plugins/loader.rs:167-179)。
9.2.2 Plugin 二进制协议
sequenceDiagram
participant Agent
participant Plugin as Verified Executable
Agent->>Plugin: exec(".weather_verified", argv[1]="get_weather")
Agent->>Plugin: stdin: {"city":"Beijing"}
Plugin->>Agent: stderr: line-oriented progress
Plugin->>Agent: stdout: {"output":"Beijing: 25°C, sunny","success":true}
Agent->>Plugin: process exits
图 9-1:Plugin 二进制协议时序图。
这里的实现细节比“stdin JSON / stdout JSON”稍复杂(../octos/crates/octos-agent/src/plugins/tool.rs:124-419):
- runtime 实际执行的是经过 hash 校验后写出的
._verified副本 - argv 第一个参数是 tool name
- stdin 发送 JSON 参数
- stderr 逐行读出并转成
ToolProgress事件 - stdout 优先按结构化 JSON 解析
- 如果 stdout 不是合法 JSON,runtime 会退回到“原样拼接 stdout + stderr 文本”
结构化 stdout 还支持比 output/success 更丰富的语义:
file_modifiedfiles_to_send
此外 runtime 还会尝试从 out 参数或输出文本里自动探测生成文件,并触发自动回传(../octos/crates/octos-agent/src/plugins/tool.rs:321-403)。所以 Plugin 协议的真实价值是:把“外部进程”包装成“可流式报告进度、可自动回传文件的 Tool”。
9.2.3 安全与运行时约束
Plugin 这一层的安全措施有几道是必须写清楚的。
第一道:可执行发现是保守的。
PluginLoader 只把“子目录 + manifest.json”当成候选项。真正找二进制时,会依次尝试:
manifest.name- 目录名
main- 目录中任意可执行且非隐藏、非
.json/.md/.toml/.tar.gz的文件
逻辑在 ../octos/crates/octos-agent/src/plugins/loader.rs:181-211。
第二道:SHA-256 校验不是“验完原文件再直接运行”。
Loader 先把原始字节读进内存,再对内存字节做 hash 校验,然后把同一份已验证字节写到 ._verified 文件,后续真正执行的是这份副本(../octos/crates/octos-agent/src/plugins/loader.rs:226-271)。这一步的目的是封住典型 TOCTOU 窗口。
第三道:资源与环境约束。
- 100MB 可执行文件上限(
../octos/crates/octos-agent/src/plugins/loader.rs:213-224) - 继承
BLOCKED_ENV_VARS黑名单(../octos/crates/octos-agent/src/plugins/loader.rs:273-275、../octos/crates/octos-agent/src/plugins/tool.rs:140-148) - tool 级
env/env_allowlist采用严格语义:manifest 显式列出 env 时,只允许这些变量进入 plugin;未显式列出时保留 legacy 兼容路径,但 secret-like 额外环境变量必须走 allowlist(../octos/crates/octos-agent/src/plugins/tool.rs:859-893) - 运行时注入
OCTOS_WORK_DIR给 plugin 放输出文件(../octos/crates/octos-agent/src/plugins/tool.rs:150-164) - 默认超时其实是 600 秒,不是 30 秒(
../octos/crates/octos-agent/src/plugins/tool.rs:35-48);manifest 的timeout_secs只是覆盖默认值(../octos/crates/octos-agent/src/plugins/loader.rs:276-279)
第四道:风险与并发类别不是装饰字段。
risk 会进入运行时审批路径:high / critical 风险工具强制请求交互式 approval;如果当前环境没有 approval bridge,runtime 会安全拒绝,而不是绕过审批继续执行。low 风险默认不触发审批,medium / unknown 当前主要用于显式呈现风险(../octos/crates/octos-agent/src/plugins/manifest.rs:136-144, ../octos/crates/octos-agent/src/plugins/tool.rs:772-820)。
concurrency_class 当前识别 safe 和 exclusive。未知值不会被乐观当作 safe,而是记录告警并在执行侧 fail closed 到 Exclusive,避免一个声明错误的 plugin 被并行执行到破坏共享状态(../octos/crates/octos-agent/src/plugins/manifest.rs:220-263, ../octos/crates/octos-agent/src/plugins/tool.rs:711-730)。
第五道:Unix 上的符号链接拒绝。
is_executable() 用 symlink_metadata() 检查文件类型,只接受普通文件,不接受符号链接(../octos/crates/octos-agent/src/plugins/loader.rs:332-340)。这不是全部安全边界,但能减少 link-swap 这类替换攻击面。
9.2.4 runtime PluginLoader 与 octos-plugin SDK 的边界
这一章最容易写错的地方,是把仓库里的两层代码混成一层。
当前 runtime 热路径 是 ../octos/crates/octos-agent/src/plugins/loader.rs:
- 扫描调用方传入的目录
- 逐个加载子目录 manifest
- 解析 extras
- 查找并校验可执行文件
- 生成 verified copy
- 注册工具到
ToolRegistry - 单个 plugin 失败时
warn!并跳过,不影响其他插件加载(../octos/crates/octos-agent/src/plugins/loader.rs:73-140)
../octos/crates/octos-plugin 则是 SDK / tooling crate,提供的是另一层抽象:
discover_plugins():按来源优先级扫描目录并去重(../octos/crates/octos-plugin/src/discovery.rs:20-56)check_requirements():做bins/env/os三类 gating(../octos/crates/octos-plugin/src/gating.rs:37-123)- richer manifest:
id/type/requires/install/...(../octos/crates/octos-plugin/src/manifest.rs:76-202)
两层有关联,但不能混为一谈。当前主 agent runtime 并不是“每次都先跑 octos-plugin::discover_plugins() 再加载”,而是直接走 octos-agent 自己的 PluginLoader。如果你写的是 runtime tool,要看 octos-agent/src/plugins/*;如果你写的是校验器、市场、安装器、离线发现逻辑,要看 octos-plugin crate。
octos-plugin 的 gating 模型仍然值得理解,因为它定义了生态层的约束语义:
| 检查 | 方法 | 失败处理 |
|---|---|---|
| Binary | which 检查依赖程序是否在 PATH 中 | 标记 unavailable / 跳过 |
| Env | 检查必要环境变量是否存在 | 标记 unavailable / 跳过 |
| OS | 检查当前平台是否在允许列表中 | 标记 unavailable / 跳过 |
还有一个很小但真实的细节:gating 把 darwin 和 macos 当成等价别名,避免 manifest 和 Rust 平台字符串不一致时误伤(../octos/crates/octos-plugin/src/gating.rs:73-104)。
9.3 MCP 集成:标准协议,不等于“远程插件”
MCP(Model Context Protocol)是标准化的工具与上下文集成协议。octos 的 MCP client 位于 ../octos/crates/octos-agent/src/mcp.rs,支持两条接入路径。
9.3.1 Stdio vs HTTP POST(可选 SSE 响应)
| 特性 | Stdio 传输 | HTTP 传输 |
|---|---|---|
| 连接方式 | 本地子进程 + stdin/stdout JSON-RPC | HTTP POST JSON-RPC |
| 响应格式 | 单行 JSON | 普通 JSON 或 text/event-stream |
| 延迟 | 极低(本地 IPC) | 受网络与远端服务影响 |
| 主要安全面 | 子进程环境清理 | SSRF 检查 + DNS pinning |
| 适用场景 | 本地 MCP server | 远程 MCP 服务 |
把第二条路径直接叫成“HTTP-SSE transport”会误导读者。当前实现其实是:
- 请求通过 HTTP POST 发送 JSON-RPC(
../octos/crates/octos-agent/src/mcp.rs:179-198) - client 用
Accept: application/json, text/event-stream同时接受 JSON 或 SSE(../octos/crates/octos-agent/src/mcp.rs:182-183) - 如果响应
content-type包含text/event-stream,再从 SSE body 中提取最后一个data:事件作为 JSON-RPC 结果(../octos/crates/octos-agent/src/mcp.rs:225-255)
因此更准确的说法是:HTTP POST,SSE 只是可选响应封装。
另一个容易被漏掉的点是会话亲和:如果服务端返回 mcp-session-id,client 会保存它,并在后续请求中回放为 Mcp-Session-Id header(../octos/crates/octos-agent/src/mcp.rs:189-205)。
9.3.2 启动与安全约束
MCP client 在两个不同阶段做不同的安全控制。
发现阶段:schema 约束。
启动 server 后,client 会先跑 initialize,再调用 tools/list,并对每个 tool 的 input_schema 做验证(../octos/crates/octos-agent/src/mcp.rs:308-361,500-524):
| 约束 | 值 | 作用点 |
|---|---|---|
| 最大嵌套深度 | 10 | validate_schema() |
| 最大序列化大小 | 64KB | validate_schema() |
超出限制的 tool 不会让整个 server 启动失败,而是 跳过该 tool 并记录 warning。
执行阶段:transport 约束。
| 约束 | 值 | 适用面 |
|---|---|---|
| 单行响应上限 | 1MB | 仅 stdio read_line_limited() |
| tool call 超时 | 60 秒 | McpTool::execute() |
| env 清理 | BLOCKED_ENV_VARS | 仅 stdio transport |
| SSRF + DNS pinning | 开启 | 仅 HTTP transport |
这里最需要纠正的误解是:1MB 不是所有 MCP 响应的统一全局上限。它只作用在 stdio transport 的单行 JSON-RPC 响应上(../octos/crates/octos-agent/src/mcp.rs:20-21,118-143)。HTTP 路径当前没有对整个响应体加同样的总字节限制;它依赖的是 SSRF 检查、DNS pinning、状态码检查和 60 秒请求超时。
9.3.3 名称保护与注册
MCP client 发现到的 tool 不会直接无条件塞进 registry。注册前还有一道保护:PROTECTED_NAMES(../octos/crates/octos-agent/src/mcp.rs:454-497)列出了 19 个内置工具名,MCP tool 如果发生同名碰撞会被直接跳过。
这一步的意义不是美观,而是防止远端 MCP server 静默替换核心能力。例如,如果没有这层保护,一个外部 server 理论上可以注册一个同名 shell 或 browser,把模型对“内置工具”的调用流量劫持过去。
工程决策侧栏:为什么需要三种扩展机制
维度 Skills Plugins MCP 核心作用 改提示与上下文 跑本地可执行工具 接外部协议化工具服务器 主要载体 SKILL.mdmanifest.json+ executableJSON-RPC server 运行边界 无独立执行边界 外部进程 + verified copy 本地/远程连接 典型增值点 低成本行为定制 进度流、文件回传、后台任务 跨 Agent 平台复用 安全面 可用性检查而非隔离 hash 校验 + env 清理 + work dir SSRF + schema 验证 + 名称保护 为什么不能统一成一种?
因为它们解决的不是同一类问题。Skills 让模型学会“怎么想”,Plugin 让系统学会“怎么做”,MCP 让系统学会“怎么接别人的能力”。
如果把 Skills 也做成 Plugin,会让纯提示定制被迫带上二进制、协议和运行时安全成本。反过来,如果把 Plugin 做成纯 Skill,又无法提供真实执行、进度流和文件产出。MCP 看起来和 Plugin 都像“工具扩展”,但它追求的是协议互操作,而不是本地 runtime 集成的最低摩擦。
9.4 Harness 工程契约:ABI、事件与验证器
Skills、Plugins 和 MCP 解决的是“能力如何进入 octos”。Harness 解决的是另一个问题:这些能力运行之后,如何把结果变成可验证、可观测、可升级的工程契约。
9.4.1 ABI versioning:schema_version 不是 manifest version
abi_schema.rs 集中维护 runtime payload 的 schema version,包括 WorkspacePolicy、CompactionPolicy、HookPayload、ProgressEvent、TaskResult、SessionSummary、swarm dispatch、cost attribution、routing decision、credential pool config 和 harness error event(../octos/crates/octos-agent/src/abi_schema.rs:1-142)。这些版本描述的是 octos runtime 能理解的序列化形状,而不是 plugin / skill 自身的发布版本。
关键规则是 fail closed:check_supported(kind, found, supported) 遇到未来版本会返回 typed error,而不是静默忽略新字段(../octos/crates/octos-agent/src/abi_schema.rs:144-186)。这让外部 skill 可以随 runtime 演进,同时避免旧 runtime 错读新 payload。
9.4.2 OCTOS_EVENT_SINK:结构化 side-channel
外部 app skill 不一定需要链接 octos runtime,但它需要一种方式报告 progress、error、validator、cost 或 swarm 事件。Harness 通过环境变量提供这条 side-channel:OCTOS_EVENT_SINK 指向本地 JSONL sink,OCTOS_SESSION_ID / OCTOS_TASK_ID 和 OCTOS_HARNESS_SESSION_ID / OCTOS_HARNESS_TASK_ID 提供关联上下文(../octos/crates/octos-agent/src/harness_events.rs:1-38)。
这不是普通日志文件。stdout 仍是工具结果协议;event sink 是受 HarnessEvent schema 校验的结构化事件 ABI,单行事件有大小上限,写入前会 validate,再 append 到 JSONL(../octos/crates/octos-agent/src/harness_events.rs:116-132)。
sequenceDiagram
participant Skill as app skill process
participant Sink as OCTOS_EVENT_SINK jsonl
participant Runtime as octos-agent runtime
participant API as /api/events/harness
Skill->>Sink: HarnessEvent(progress/error/validator)
Runtime->>Sink: tail + validate + fold into task snapshot
Runtime->>API: broadcast typed frame
9.4.3 Validator runner:不是 shell hook
validator runner 是 workspace contract 的安全执行器。命令 validator 会复用 SafePolicy,清理 BLOCKED_ENV_VARS,超时后终止子进程,并把 evidence 写到 <workspace_root>/.octos/validator-evidence/(../octos/crates/octos-agent/src/validators.rs:1-24)。outcome 以带 schema_version 的 JSONL ledger 持久化,required validator 失败会阻止 terminal success,optional validator 失败只产生 warning。
这和普通 hook 的区别在于:hook 主要改变执行前后的控制流;validator 负责给 artifact 和 workspace contract 提供可回放证据。它是 Ch8 的 contract-gated compaction 和 Ch12 的 workflow artifact gate 的共同基础。
9.4.4 Starter app skills:reference implementation
harness-starter-* 不是玩具 demo,而是 manifest、concurrency、artifact、validator 和 lifecycle smoke test 的 reference implementation。harness-starter-audio 的 smoke test 不只检查 manifest 能解析,还检查 synthesize_clip 必须声明 concurrency_class = "exclusive",因为它写 audio/<slug>.wav;同一个测试还验证 primary_audio artifact 和 file_size_min:$primary_audio:4096 validator(../octos/crates/app-skills/harness-starter-audio/tests/harness_smoke.rs:23-79)。
harness-starter-report 则展示了更小的 report contract:reports/*.md、completion file_exists、verify file_size_min、failure notification(../octos/crates/app-skills/harness-starter-report/workspace-policy.toml:1-29)。读者写新 app skill 时,应把这些 starter 当成工程清单,而不是只复制业务代码。
| 文件 | 作用 | 需要检查什么 |
|---|---|---|
manifest.json | 工具声明 | name、input schema、risk、concurrency class |
workspace-policy.toml | artifact contract | primary artifact、completion validator、failure action |
tests/harness_smoke.rs | 工程质量门 | manifest 可解析、artifact 匹配、validator 可满足、lifecycle 可投射 |
9.5 本章回顾
-
Skills:通过
SKILL.md和少量 frontmatter 元数据改变模型上下文。runtime 会把多层目录压成一个去重后的 skill 视图,再生成 XML 摘要注入系统提示。 -
Plugins:把本地可执行程序包装成 Tool,同时承载 skill package extras。runtime
PluginLoader负责发现、hash 校验、verified copy、env allowlist、risk、concurrency class、工作目录注入和非致命跳过。 -
spawn_only:不是隐藏工具,而是把工具调用自动后台化。主 agent 返回即时消息,后台任务继续跑;subagent 上下文里则把它恢复成普通工具。 -
MCP:不是“远程插件”,而是标准化协议接入。octos 当前支持 stdio 和 HTTP POST(可选 SSE 响应),并用 schema 验证、名称保护、SSRF 与 DNS pinning 约束风险。
-
架构边界:
../octos/crates/octos-agent/src/plugins/*是当前 runtime 热路径;../octos/crates/octos-plugin是 SDK / tooling crate。把这两层分清,读源码时就不会迷路。 -
Harness:ABI versioning、event sink、validator runner 和 starter app skills 共同定义扩展工程契约,让外部能力可验证、可观测、可升级。
Part 2 到此结束。下一章开始 Part 3,从单机会话推进到消息总线与多会话编排。
延伸阅读
- Model Context Protocol:https://modelcontextprotocol.io/
- JSON-RPC 2.0:https://www.jsonrpc.org/specification
- Bubblewrap / sandbox-exec:理解本地可执行扩展为什么必须配合进程级安全边界
思考题
-
Skills 的边界:如果一个需求同时需要提示注入和真实执行能力,你会把逻辑拆成
SKILL.md + Plugin,还是尽量压缩成单一 package?为什么? -
Plugin 信任链:当前 verified copy 解决了 TOCTOU,但如果 manifest 与原始二进制一起被替换,hash 仍然会“自洽”。你会如何把信任链再往前推进一层?
-
HTTP MCP 的响应体:stdio 路径有 1MB 单行上限,HTTP 路径当前没有等价的全局 body cap。你会不会补这一层?如果补,应该放在哪一层最合适?
版本演化说明 本章按当前
octos主分支源码更新。后续阅读时,优先核对../octos/crates/octos-agent/src/skills.rs、../octos/crates/octos-agent/src/plugins/、../octos/crates/octos-agent/src/mcp.rs、../octos/crates/octos-agent/src/abi_schema.rs、../octos/crates/octos-agent/src/harness_events.rs、../octos/crates/octos-agent/src/validators.rs、../octos/crates/octos-plugin/src/和../octos/crates/app-skills/harness-starter-*。