For AI agents: the complete documentation index is available at https://scriptedalchemy.github.io/agent-bundle/zh/llms.txt, the full documentation bundle is available at https://scriptedalchemy.github.io/agent-bundle/zh/llms-full.txt, and this page is available as Markdown at https://scriptedalchemy.github.io/agent-bundle/zh/guide/distribution/validation.md.
跳至主要内容
  • 简体中文
  • 产物校验

    校验用同一条命令回答两个不同的问题。针对源码,它问的是项目是否描述了一个合法的捆绑包;针对 产物,它问的是输出的字节是否就是清单所承诺的字节。

    npx agent-bundle validate --root .                        # 项目源码
    npx agent-bundle validate --artifact artifact --strict    # 已构建字节,无需源码

    validate --artifact、mcp 与 hooks 都能在项目源码已被删除的情况下针对已构建产物工作。这是刻意 如此:只有在源码树存在时才能通过校验的产物,其实并不算自包含;测试中的 packed-deleted-source 证明级别存在的理由与此相同。

    内容寻址,而不是路径存在性

    agent-bundle.manifest.json 为每个输出文件记录一份 SHA-256 摘要,包括被复制的脚本与资源。产物校验 把真实字节与这些摘要比对,因此被手工改过的生成文件会失败,而不会因为路径还在就通过。被引用的文件同样 会被检查,清单声明的 logo 若在产物中缺失或逃逸出部署树,会报告 AB6025。已列入清单的编译证据记录若 与清单文件表不一致,会报告 AB6039。

    自包含性由编译过程的 externals 证据证明(在编译期报告 AB6005)。编译器服务会降低每个宿主包表面、 每个包构建入口(dist/bin/*.js、渲染式路由的 Flight worker .mjs 与 lib 入口),以及每个 MCP App 视图。框架自有的 ArtifactDependencyAuditPlugin 会接入 thisCompilation,记录 Rspack 保持外部化的每个模块,服务则在 信任任何产物之前读取这份证据。只有 Node 内建模块(node:fs、fs)、pnpapi 以及同一产物已输出的同级 文件可以保持外部化;其他请求无论被 Rspack 输出成 import、require 还是其 createRequire shim,都会 失败。表达式请求(import(expr)、require(expr))不在编译器视野之内:Rslib 的配置不解析它,而是原样保留, 因此它既不会被打包,也不算外部模块。

    已输出模块的遍历仍留在这份编译器证据之后,只负责编译器看不见的部分。它把每个已输出的 .js/.mjs 模块按 ES 模块读取,并在其中任何一个,包括编译产物,里出现表达式 import() 时失败 (AB6005 has a non-literal dynamic import)。被编译证据记录证明的模块,记录以相同字节覆盖的清单 bundle 文件,且构建未使用 tools 逃生舱,只做词法 分析而不完整解析,但它仍带有的每个字面量导入都要对照记录核对:Node 内建模块或该文件记录在案的 外部依赖通过;其他任何请求都是构建被要求忽略的导入(rspackIgnore/webpackIgnore,Rspack 原样保留它,没有模块、外部依赖或警告),并以 AB6005 loads "<request>", which the compiler neither bundled nor recorded as an external 失败。摘要匹配只证明字节是编译器输出的,不证明其中每个导入都 已被解析。其他所有模块都会被完整解析,导入也会对照清单解析,生成的 install.mjs、被复制的消费者脚本、 没有记录的产物中的每个模块,以及逃生舱可能改写了输出资源的构建(记录中的 coverage.rewritable) 里的每个模块。这些发现就是 AB6005 的残余形态:不受支持或无效的说明符、缺失于或逃逸出输出树的目标、 无效语法;dist 的发现以 dist/<path> 命名文件。预构建负载(kind: 'prebuilt')保持不透明、仅做哈希 锁定,声明文件(.d.ts)不会被遍历。

    编译时,AB4837 拒绝导入携带编译器的框架入口:agent-bundle、 agent-bundle/api、agent-bundle/config、agent-bundle/eval、agent-bundle/rstest、 agent-bundle/test 和 agent-bundle/test/browser。检查使用转换后的依赖请求和解析后的 包导出身份(包括别名),并指出导入模块。被配置的转换器擦除的类型导入仍然合法。 仅分析源码的 inspect 和 validate 不证明此依赖边界。agent-bundle/app 是浏览器安全的叶入口。 已安装产物通过 <plugin> web 提供 App。

    编译证据记录

    agent-bundle build 在产物根目录写出 agent-bundle.compile-evidence.json,并在 agent-bundle.manifest.json 中把它列为 generated 文件。这份记录就是编译器服务对它输出的每个文件 所报告的内容,并与那些字节绑定。assets[] 中的每一项对应一个已编译文件(bundle kind: bin/*.mjs、scripts/*.mjs、hooks/*.mjs、mcp/*.mjs、Flight worker、mcp-apps/*.html), 并保存 path、sha256、被保留的 externals(kind 为 artifact-relative 或 builtin,以及 externalType、issuers、request、userRequest,同级文件还有 target)和被内联的 packages。记录级字段是策略 closed-world-externals@1、producer { name: 'agent-bundle', rspack, version }、coverage.rewritable(当 tools.rspack 或 tools.rsbuild 逃生口参与了这次构建时为 true,此时输出字节可能与模块图不一致),以及 coverage.unobserved。

    agent-bundle validate --artifact 把已列入清单的记录对照清单文件表复核,且不读取 JavaScript (AB6039)。包构建在进程内返回同一份记录(PackageBuildResult.evidence,路径为 dist/bin/…),不写到磁盘。 coverage.unobserved 列出 Rslib 在捆绑中原样保留的加载形式,因此编译器既不会打包它们,也不会记录 它们:import(<expression>)、require(<expression>)、require.resolve(…)、 createRequire(…)(…)、import.meta.resolve(…)。因此「没有记录任何 externals」并不能证明不存在 这类加载。

    AB6039 是 error。恢复方式:重新构建产物,使其编译证据记录描述已输出的文件。消息形式:

    • Compile evidence record <reason>.(严格解析器自身的消息,例如 is not valid JSON)
    • Compile evidence record cannot be read.
    • Compile evidence was judged under policy <name>@<revision>; this validator applies closed-world-externals@1.
    • Compile evidence does not cover compiled file "<path>".
    • Compile evidence for "<path>" describes different bytes.
    • Compile evidence names "<path>", which the manifest does not list as a compiled file.
    • Compile evidence for "<path>" records "<request>" as a built-in; it is not one.
    • Compile evidence for "<path>" records sibling "<request>", which the artifact does not contain.
    • Compile evidence for "<path>" records "<request>" as an external; a view inlines every module it loads.

    每条诊断都是一份结构化记录:稳定的 AB 代码、一个严重级别、一条消息,通常还有 sourcePath 与一条 recovery 提示。由诊断把关的命令,build、prepack、validate、doctor、install 与 dev,只有 存在 error 级诊断时才以非零退出;warning 与 info 绝不会为构建、校验或 dev 重建把关。eval 与 inspect 在试验失败或无定论、或模型无效时也会以 1 退出,这与错误诊断无关,见 命令行退出码。

    Claude Code 严格校验

    宿主校验只为所选投影运行,且作用于组合根目录。当 Claude Code 位于 PATH 上、且所选投影中包含 claude 时,产物校验会对根目录运行已安装的 claude plugin validate。agent-bundle build 会对刚写出的根目录运行 同样的检查(--no-host-validation 跳过;--strict 把宿主 warning 提升为构建 error),而 agent-bundle doctor --host claude --from <root> 会对该根目录以及 Claude 列出的每一份已安装副本运行 这些检查。Claude Code 根据被指向的清单决定检查什么:一个同时包含 .claude-plugin/plugin.json 与 .claude-plugin/marketplace.json 的根目录会被当作 marketplace, 而 marketplace 校验从不打开插件的 hook、skill、agent 或 command 文件。因此 Agent Bundle 运行两条命令:

    # plugin.json、hooks/hooks.json、skills/、agents/、commands/
    claude plugin validate <root>/.claude-plugin/plugin.json --strict
    # marketplace schema、插件名、source 路径
    claude plugin validate <root>/.claude-plugin/marketplace.json --strict
    • 宿主的 error 会变成 Agent Bundle 的 error(AB6021)。
    • 宿主的 warning 保持为 warning(AB6020),除非设置了 agent-bundle validate --strict。
    • 每条发现都会点名 Claude Code 检查的文件,例如 (hooks hooks/hooks.json): hooks: hooks.postToolUse: unknown hook event。Agent Bundle 会先请求 CLI 的 --json 报告(Claude Code 2.1.259 及更高版本;固定的 2.1.260 会输出它),只有当 CLI 拒绝该标志或探测到的 版本早于 2.1.259 时才解析文本报告。marketplace 那次 运行中对插件运行已报告过的清单发现的 plugins[0] plugin.json → 副本会被丢弃。
    • 二进制文件缺失会被报告为一次显式的信息性跳过(AB6019),绝不会伪造成功;探测或运行无法完成 时为 AB6022。PATH 上没有 claude 时,build 只会启动一次进程并只报告一次跳过。

    加载检查

    校验不是加载结论。Claude Code 2.1.250 到 2.1.260 在 --strict 下会接受一些随后会被会话拒绝加载的清单与 组件文件,例如无效的 monitors/monitors.json,或指向自动加载的 hooks/hooks.json 的 hooks 字段。拒绝 唯一显现的地方是 claude --plugin-dir <root> plugin list --json("Plugins reference" → "plugin list") 中该插件所在的行:被拒绝的行带有 errors 数组,内含宿主的消息;健康的行没有 errors 键。因此 build 与 validate --artifact 在两次校验运行之后正好执行这条列表命令,并读取 bundle 的 <name>@inline 行:

    load.status含义代码
    loaded该行没有 errors。无
    refused该行带有 errors;逐字引用。AB7325(error;当每一项都是 Dependency "…" is not installed 时为 warning,因为它描述的是执行校验的机器而非产物,--strict 会提升它)
    unregistered没有该 bundle 的行:Claude Code 没有把该目录注册为插件。AB7311
    failed列表无法读取(非零退出、超时、不是 JSON 数组)。AB6022

    这条列表命令是只读的,也不需要登录。Doctor 不会重复它:其 --plugin-dir 注册证明与清单行中的 errors 已经承载了同样的结论。

    CI 应当使用严格校验:

    npx agent-bundle validate --artifact artifact --strict

    当需要确定性的、只看 schema 的检查时使用 --no-host-validation,没有安装这些宿主的机器也必须能运行 同一道门禁,并得到同样的 schema 结论。

    在开发期,先在不安装的情况下为单个会话加载已构建的根目录,然后迭代:

    claude --plugin-dir artifact

    在 agent-bundle build 重写了 hooks/、.mcp.json 或 agents/ 之后,在该会话中运行 /reload-plugins;SKILL.md 的修改无需它即可生效。--plugin-dir 插件对不带参数的 claude plugin list 不可见;把该标志放在子命令之前即可查看:

    claude --plugin-dir artifact plugin list --json

    这份列表就是上文描述的加载结论,build 与 validate --artifact 会替你运行它。agent-bundle doctor --host claude --from <root> 会在 --plugin-dir 注册证明与已安装副本两处读取同一个 errors 数组,并报告为 AB7325(load-failed);当已安装副本带有该数组时,agent-bundle install claude 以 AB7006 失败。被固定的 Claude plugin schema 也会预先拒绝 hooks: "./hooks/hooks.json"(AB6012),因为清单中的该字段只用于额外的 hook 文件。

    Doctor 还会读取每一行的 enabled 标志。被 claude plugin disable 关闭的副本("Plugins reference" → "plugin disable")仍然处于已安装状态,甚至可能与 bundle 字节一致,但它的任何内容都不会进入会话;Doctor 把它列为 disabled,保留其内容结论,并追加一条 AB7327 warning,其修复建议就是精确的 claude plugin enable <name>@<marketplace> [--scope <scope>] 命令,重新安装不会启用一个被禁用的插件。

    Codex 与 Cursor:被固定的 schema

    Codex 与 Cursor 目前都没有发布插件校验的开发者动词,因此 Agent Bundle 用自己 vendored 的、被固定的 schema 校验它们输出的 JSON 文档,并把宿主的 schema 生成器视为一个漂移信号,绝不当作替代的插件契约。

    代码严重级别含义
    AB6026info每份 Cursor 报告都会声明 Cursor 未发布 plugin-validate 动词,并指明本地校验所用的 vendored schema 固定版本。
    AB6027error某个必需的生成式 Cursor 文档缺失,或者已存在的 plugin、市场、MCP 或 hooks 文档不可读、不是合法 JSON,或被其固定 schema 拒绝。hooks 文档是 .cursor-plugin/plugin.json 的 hooks 字段所指定的那个(紧挨着清单的 .cursor-plugin/hooks.json,或内联对象),仅在该字段缺失时才回退到 hooks/hooks.json;指定的文件缺失或逃逸出插件根目录时为 error。
    AB6028error生成的字节违反了被固定的 Cursor 加载器证据:清单候选优先级选中了回退清单、符号链接解析到捆绑包之外,或者 CURSOR_PLUGIN_ROOT 出现在加载器替换字段之外。
    AB6029info / warningCursor Agent 版本探测不可用(ENOENT,info),或未能完成(warning)。本地的固定 schema 校验仍会运行。
    AB6030infoCodex CLI 不可用,或已安装的 Codex 版本未发布插件校验命令。
    AB6031info / warning(--strict 下为 error)Codex 的 app-server schema 生成动词不可用,或其实时输出缺失、与被固定的生成式钩子 schema 不一致。
    AB6032error某个必需的 Codex 捆绑包文档缺失、不可读、不是合法 JSON,或未通过其 vendored 固定 schema。
    AB6033error一条有界的 Codex 版本或 schema 生成命令无法启动、执行失败、超时、输出超过 1 MiB,或产生了不可读的输出。

    AB6031 是其中最值得注意的一条:当 Codex 生成的 schema 与被固定的版本不一致时,只有在 Codex 发布了 匹配的契约之后才会更新这个固定版本。自动跟随宿主的实时输出,会把一次上游变更变成这里一次无声的契约变更。

    Agent Skills 规范 lint

    AB6034 是针对输出字节的 error:某份 SKILL.md 拥有合法的 YAML frontmatter,但其后没有 Markdown 指令正文。被固定的 Agent Skills 规范要求 frontmatter 之后必须跟随 Markdown 内容,而这项检查针对的是 产物实际包含的内容,而不是编写时的源码。

    哈希固定与仓库自有的表格

    哈希固定覆盖那些真实来源位于本仓库之外、且可能漂移的 vendored 外部内容:src/adapters/schemas/* 下的 宿主文档 schema(其上游 URL、commit 与 SHA-256 记录在 PROVENANCE.json 中)、清单 agentSkills 块中 由 Agent Skills 规范派生的 schema 固定值,以及为完整性而记录的输出产物文件与源输入。

    仓库自有的能力表格与证据不做哈希。能力证据记录被观察到的宿主版本(observedVersion)与 target, 适配器则携带一个单调递增的 adapterRevision。Git 已经为仓库自有内容做了版本管理;在仓库内部再哈希一遍 是自我指涉的,并且会让每次编辑表格都产生变动噪声。

    npm prepack 门禁

    npx agent-bundle prepack --root . --output artifact --json

    prepack 运行发布构建,在 dist/ 生成规范 npm 根目录,再通过当前 Node 可执行文件 (process.execPath)启动 npm 的 npm-cli.js,从该根目录运行 npm pack --dry-run --json --ignore-scripts,因此在 npm 不是 PATH 上可直接 spawn 的命令时(pnpm 管理的 Node,或 Windows 上 PATH 没有 npm.cmd)门禁仍可用。 当 npm_execpath 指向 pnpm 时,它不会把该变量当作 npm。生成的包会原样复制已校验的组合产物,让路由式 package.json bin 指向清单声明的 bin/<name>.mjs,并且只额外加入单独编写的 bin 与库。 门禁会检查这份精确清单、清单哈希、包 bin 目标、发布版本一致性、包专属编译证据以及 package.json 中的安装期依赖字段。作者写入的 package.json.files 与 .npmignore 规则不会缩窄 生成根目录:每个所选隐藏宿主目录与每个清单文件都必须进入包,否则 prepack 会在发布前失败。构建会把每个依赖内联进手写包入口与各宿主包,因此已发布的插件不应安装任何东西:请把框架、@agent-bundle/runtime、react、 zod 以及其余技术栈都声明在 devDependencies 下。已编译的 bundle,无论是宿主包模块还是 dist bundle,都不能把裸包保持外部化:编译器会在 prepack 触及清单之前报告 AB6005。因此 仅仅把包内联进 bundle 并不能证明消费者需要安装它。--output 是相对 --root 的产物路径,会覆盖配置中的 output.distPath,默认值为 artifact。请直接运行门禁(生成项目提供 pack:check),再用 npm publish ./dist --ignore-scripts 发布生成根目录。任何 npm 生命周期都绝不会执行宿主安装。

    消费者的 preinstall、install 与 postinstall 脚本仅在不直接调用 Node,或恰好是一个 node <generated-package-path> 命令时保留。源 dist/ 下已知的手写输出路径会改写到生成根目录,并对照 已暂存文件校验。不支持的 Node 命令或缺失的源码资源会报告 AB4768;包构建器绝不会为了满足生命周期路径 而复制整棵源码树。

    这种内联是编译器的契约,而不是一次工具链升级就可能翻转的打包器默认值。生成的可执行文件,产物脚本、路由式 CLI、MCP 入口、钩子包装层以及包构建的 JavaScript bundle,都以禁用 Rslib 的 autoExternal、bundle: true 与 splitChunks: false 的配置编译,框架自身也不添加任何 externals;Rslib 的 node target 只把 Node 内建模块(node:fs、path,以及 Yarn PnP 的 pnpapi)保持外部化。MCP App 视图则把每个脚本与样式都 内联进同一个 HTML 文件,同一个审计插件也位于它们的 Rsbuild 编译器中:浏览器文档不允许任何 external, 因此视图编译保持外部化的任何模块,比如通过逃生舱映射到全局变量的 react,都会让该视图以 AB6005 失败(Compiled MCP App view "mcp-apps/<name>.html" keeps … external …)。AB4725 会拒绝不为 false 的 tools.rsbuild.output.autoExternal,以及静态可见、 点名某个包的字符串或对象 externals 条目。RegExp、函数形式、相对路径与 mutator 添加的 externals 则由编译过程 自身的证据判断:若 Rspack 在宿主包或 dist 的生成可执行文件中保持了内建模块、pnpapi 或已输出同级文件 之外的任何 external,AB6005 就会失败。通过 tools 逃生舱保持外部化并不能绕过契约,也永远到不了 prepack 的清单检查。

    AB7014 只接受三类消费者运行时证据:

    1. 通过 definePrebuilt 声明的预构建 payload 上的 runtimeDependencies;
    2. TypeScript 的 preProcessFile 从打包后声明文件读出的引用:import、export、import-equals require、import("…") 类型、外部模块文件中的模块扩充,以及 /// <reference types>(连同其 @types 对应包);没有 import 的文件中的环境式 declare module 不算证据,而 #子路径 会经由包的 imports 映射解析;
    3. 消费者侧 preinstall、install 或 postinstall 脚本(包括其委托的脚本)点名该依赖,或运行其 bin 命令。

    框架自身的运行时模块在运行时不加载任何包,因此不存在可作为第四类证据读取的框架进程依赖记录。

    仅仅内联进已编译 bundle 的包不算已使用。当这是唯一证据时,AB7014 会点名内联它的 dist bundle, 例如:The build inlined "a" into dist/bin/x.js; every consumer installs them for nothing. 请把仅构建期使用的包移到 devDependencies;编译后的 bundle 会内联其 import(AB6005)。仅当打包后的 声明文件引用、消费者安装脚本点名或运行,或预构建 payload 在 runtimeDependencies (definePrebuilt)中声明某个包时,才把它保留为运行时依赖。

    代码含义
    AB7010dry-run 的 npm 清单遗漏了某个预期 npm 根目录文件。请打包生成根目录,且不要排除其中的文件。
    AB7011npm 根目录中复制的某个产物文件与其清单 SHA-256 不再匹配。请重新构建,且不要修改生成根目录。
    AB7012生成 npm 根目录或实际发布的 package.json 中,某个 bin 指向 src/,或指名了不在该包打包文件集中的文件。每个 bin 都必须指向该包实际包含的文件;路由式 CLI 使用清单声明的 bin/<name>.mjs,手写 bin 使用生成的 bin/*.js。
    AB7013package.json、规范化后的插件元数据、某份宿主清单或产物 provenance 报告了不同的发布版本。请让每处发布标识一致。
    AB7014dependencies、optionalDependencies 或 peerDependencies 字段点名了消费者无需安装的包。必须有上列三类证据之一才算使用;已编译 bundle 内联该包不算。诊断会点名内联了这个未使用包的所有 dist bundle。对 dependencies 与 optionalDependencies 是 error,对 peerDependencies 是 warning;可选 peer 会跳过,optionalDependencies 中的同名项优先于 dependencies。
    AB7015dependencies、optionalDependencies 或 peerDependencies 中的某一项无法被消费者的 npm 经由注册表解析。包名与说明符一并交给 npm 自己的解析器 npm-package-arg 读取,结果分为三类:注册表类(版本、范围、dist-tag,或指向它们的 npm: 别名)、抓取类(git、GitHub 简写、远程 tarball 或路径来源,npm 12 默认拒绝 git 与远程抓取(allow-git、allow-remote),而路径在消费者磁盘上并不存在),以及不可解析类(npm 不接受的包名、它不支持的协议如 link:、portal: 或拼写错误、既非范围也非 URL 安全 dist-tag 的选择器、指向非注册表目标的别名,或非法 URL,清单本身就读不下去,因此即使出现在可选 peer 上也会被报告)。抓取类的 optionalDependencies 项只发出警告,因为 npm 可以在没有它时继续;但消费者安装脚本需要该包时会升级为错误:其 bin 命令位于命令位置、运行了 node_modules/<name>/ 文件,或通过 -r/--require/--import/--loader(包括 NODE_OPTIONS)预加载了裸包。若 dependencies 或 optionalDependencies 也声明了同名 peer,则只按该具体条目判断。规范根目录按 npm publish ./dist 校验,因此 workspace: 与 catalog: 一律拒绝;已打包的 bundleDependencies 与包内可安装的 file: 来源不会被报告。请依赖已发布的版本,或把该包打包进产物并声明在 devDependencies 下。

    发布构建同样会拒绝 package.json 中没有有效发布版本的项目(AB4013),因此已发布的产物绝不会携带 0.0.0-dev.<short-revision> 这个开发期回退值。

    下一步

    • 宿主安装:把已校验的捆绑包注册到宿主。
    • 预览包:这些输出目前如何抵达消费者。