内容
# wiff
**与运行环境无关、可恢复、多智能体工作流 —— 用纯 JavaScript 编写。就像 `wf`,但这是 wiff。**
[](https://www.npmjs.com/package/@xxxoooxoxo/wiff) [](https://registry.modelcontextprotocol.io/v0/servers?search=io.github.xxxoooxoxo/wiff)  
使用小型脚本将任务扩展到一组智能体,而不是靠祈祷。你可以使用 `agent()`、`parallel()` 和 `pipeline()` 编写普通 JavaScript 代码;运行时在后台执行它,记录每个步骤,当运行一半时发生故障 —— 不需要重新支付任何已完成的智能体费用即可恢复。引擎是一个普通的 MCP 服务器,具有持久的磁盘状态,因此同一个运行可以从 **任何** MCP 客户端启动、查看、恢复或取消 —— Codex、Claude Code、Cursor 或 cron 作业。(今天,*工作器* 在 [Codex](https://github.com/openai/codex) 上运行;[与模型无关的后端](https://github.com/xxxoooxoxo/wiff/issues/1) 正在路线图中。)
<picture>
<source media="(prefers-color-scheme: light)" srcset="docs/screenshots/run-light.png">
<img alt="在查看器中实时运行的 wiff:一个三阶段的无障碍审计,其中一个库存智能体已完成,三个审计智能体并行运行,每个智能体都有一个实时的状态行、甘特时间线和每个智能体的模型/工作量/沙盒/令牌徽章。" src="docs/screenshots/run-dark.png">
</picture>
```javascript
export const meta = {
name: "audit",
description: "并行审计文件,隔离修复确认的问题",
phases: [{ title: "审计" }, { title: "修复" }],
};
phase("Audit");
const findings = await parallel(
args.files.map((file) => () =>
agent(`审计 ${file} 以查找身份验证错误`, {
key: `audit:${file}`, // 稳定的密钥 → 恢复时免费重放
sandbox: "read-only",
schema: findingSchema, // 结构化的 JSON 输出
}),
),
);
phase("修复");
return await parallel(
findings.filter((f) => f.real).map((f) => () =>
agent(`修复:${f.summary}`, {
key: `fix:${f.file}`,
agentType: "surgeon", // .codex/agents/surgeon.md 中的角色
isolation: "worktree", // 独有的 git 工作树 —— 并行写入不能冲突
sandbox: "workspace-write",
}),
),
);
```
## 为什么
**没有与运行环境无关的工作流编排系统。** 每个编码环境都有一些多智能体故事 —— Claude Code 有其 Workflow 工具,Codex 有子智能体,Cursor 有自己的智能体 —— 但每个都是与其环境绑定的:它们的运行在该应用程序中生存和死亡,其状态对其他所有内容都是不可见的,并且它们都不能从其自己的聊天窗口以外的任何地方驱动。wiff 将编排从环境中分离出来:引擎是一个普通的 MCP 服务器,具有持久的磁盘状态,因此 **任何** MCP 客户端都可以启动、查看、恢复或取消相同的运行,并且编排本身是一个脚本而不是对话。
临时多智能体编排(“为此派生一些子智能体”)也很棒,直到运行有 40 个智能体深度并且某些东西死亡。代码工作流为您提供:
- **确定性** —— 编排是一个脚本,而不是氛围。工作流代码中没有时间、随机性、文件系统或网络;智能体执行外部工作。
- **恢复,而不是重试** —— 每个智能体调用都使用稳定的密钥和输入哈希记录在案。当主机被终止时,不需要重新支付已完成的智能体的费用即可恢复运行。中途被中断的智能体会重新运行,并注入其先前尝试的转录摘要(“这是你已经完成的内容 —— 继续”),而工作树智能体会继承其部分签出。
- **失败时严格的语义** —— 被拒绝的智能体会大声地使工作流失败(`parallelSettled()` 是明确的光谱退出)。没有沉默的 `null` 冒充成功。
- **需要时隔离** —— `isolation: "worktree"` 为每个写入智能体提供一个新鲜的独立 git 工作树。干净的工作树会消失;脏的工作树会被保留并在运行中列出,供您检查或合并。
- **角色** —— `agentType: "reviewer"` 将 markdown 角色作为子节点的开发人员说明,带有 frontmatter 默认值用于模型/工作量/沙盒。
## 安装
**Codex** (插件:MCP 工具 + `$workflow` 作者技能):
```sh
codex plugin marketplace add https://github.com/xxxoooxoxo/wiff.git
codex plugin add wiff@wiff
```
**Claude Code** (插件:MCP 工具 + 技能):
```sh
claude plugin marketplace add xxxoooxoxo/wiff
claude plugin install wiff@wiff
```
**其他** —— 服务器在 npm 上([`@xxxoooxoxo/wiff`](https://www.npmjs.com/package/@xxxoooxoxo/wiff))和官方 MCP 注册表(`io.github.xxxoooxoxo/wiff`)中,因此注册表感知的客户端可以按名称安装它,而其他所有客户端都使用 npx 运行:
```sh
npx -y @xxxoooxoxo/wiff # stdio MCP 服务器
```
或从本地checkout:
```sh
git clone https://github.com/xxxoooxoxo/wiff.git
codex plugin marketplace add ./wiff
codex plugin add wiff@wiff
```
然后启动一个新的 Codex 会话,并调用捆绑的技能与 `$workflow` 或询问:*“以可恢复的工作流运行此操作。”*
安装插件会自动批准其四个工作流控制器工具,以便 headless 和桌面运行不会在 MCP 批准提示处停止。智能体的文件系统访问仍然由 `sandbox` 按调用控制。
## 从其他环境(Claude Code、Cursor、任何 MCP 客户端)使用
Codex *插件* 只是包装。底层的引擎是一个纯粹的 stdio MCP 服务器,因此任何支持 MCP 的环境都可以编排 wiff 工作流。思维模型:**编排器和工作器都是可插拔的** —— 谁来驱动,每个 `agent()` 子节点都在一个从其模型名称选择的后端上运行:`gpt-*`/`o*` 模型作为本地 `codex app-server` 的原生 Codex 线程运行,`claude-*`/`opus`/`sonnet`/`haiku`/`fable` 模型作为无头 `claude` 智能体运行,`composer-*` 模型通过官方 Cursor SDK(`@cursor/sdk`)在进程中运行,工作流可以自由混合它们(`provider: "codex" | "claude" | "cursor"` 覆盖推理,`WIFF_BACKEND` 设置未识别模型的后备)。在 Claude 和 Cursor 后端,`sandbox` 由权限策略而不是操作系统强制执行,因此 `workspace-write` 需要 `isolation: "worktree"`。
机器上的要求,无论环境如何:Node >= 22,如果使用 `isolation: "worktree"`,则需要 git,以及智能体使用的运行时 —— `codex` 和/或 `claude` CLI 已安装并经过身份验证,或者用于 Cursor 智能体的 `CURSOR_API_KEY`。
**Claude Code** —— 上面的插件安装是简单路径。要手动连接服务器:
```sh
claude mcp add wiff -- npx -y @xxxoooxoxo/wiff
```
工具调用通过 Claude Code 的权限系统进行;要跳过每次调用的提示,请在 `.claude/settings.json` 中允许四个工具:
```json
{ "permissions": { "allow": [
"mcp__wiff__workflow_start", "mcp__wiff__workflow_status",
"mcp__wiff__workflow_wait", "mcp__wiff__workflow_cancel"
] } }
```
**Cursor / Windsurf / Claude Desktop** —— 将服务器添加到客户端的 `mcp.json` 中:
```json
{
"mcpServers": {
"wiff": { "command": "npx", "args": ["-y", "@xxxoooxoxo/wiff"] }
}
}
```
非 Codex 主机的注意事项:
- **状态是共享的。** 每个环境都读取和写入相同的 `~/.wiff/runs/`,因此从 Codex 启动的运行可以从 Claude Code(反之亦然)观察、取消或恢复,而实时查看器看到所有内容。
- **将脚本契约引入上下文。** `$workflow` 技能仅在 Codex 中自动加载。从其他环境,指向模型到 [`plugins/wiff/skills/workflow/references/api.md`](plugins/wiff/skills/workflow/references/api.md)(或将技能复制到您的环境的技能/规则目录,例如 `.claude/skills/` 或 Cursor 规则),以便它编写有效的脚本。
- **角色** 从 `<cwd>/.codex/agents/` 然后 `~/.codex/agents/` 在每个环境中解析;设置 `CODEX_WORKFLOW_AGENTS_DIR` 在服务器的环境中指向其他位置(例如,共享的 `~/.claude/agents`)。
- `workflow_start` 需要一个明确的绝对 `cwd`,因此服务器自己的工作目录对结果无关紧要。
## 智能体
如果您是编码智能体 —— 通过 MCP 驱动 wiff 或修改此仓库,请阅读 [AGENTS.md](AGENTS.md)。它涵盖了四个工作流工具,脚本作者规则实际上会让智能体出错(稳定的 `key`s,thunks 而不是 promises,工作流代码中没有 I/O,工作树隔离用于并发写入),磁盘上运行状态的位置,以及如何验证对运行时的更改。完整的脚本契约在 [API 参考](plugins/wiff/skills/workflow/references/api.md) 中。
## 工作原理
插件是一个 MCP 服务器,暴露四个工具:`workflow_start`、`workflow_status`、`workflow_wait`、`workflow_cancel`。启动的工作流在其受限的 Node `vm` 中运行脚本(无导入、文件系统、shell、网络、时间或随机性 —— 所有这些都会抛出)。每个 `agent()` 调用都被分派到共享的本地 `codex app-server`,它使用您的模型/工作量/沙盒设置运行子线程;递归编排在子节点内禁用。
运行的每个方面都持久存储在 `~/.wiff/runs/<runId>/` 下:
```
run.json 状态、阶段、计数器、失败、保留的工作树
script.js 确切的源代码(恢复时重新读取)
journal.jsonl 每个阶段/日志/智能体事件,带有输入哈希和令牌使用情况
agents/*.jsonl 每个子节点的完整转录记录
worktrees/ 请求隔离的智能体的独立签出
```
状态、等待、取消和恢复都在主机重启后工作 —— 第二个 Codex 会话可以观察、取消或恢复它没有启动的运行。
参见 [API 参考](plugins/wiff/skills/workflow/references/api.md) 以获取完整的脚本契约,并参见 [`examples/verify-and-fix.js`](plugins/wiff/examples/verify-and-fix.js) 以获取分阶段的示例。
## 实时查看器
在本地 Web UI 中查看每个运行 —— 以及运行中的每个智能体:
```sh
npx -p @xxxoooxoxo/wiff wiff-viewer # http://127.0.0.1:4979 (--port / --root 以覆盖)
# 或从checkout:cd plugins/wiff && npm run viewer
```
零依赖,只读运行文件,因此它可以查看任何进程拥有的运行。顶部实时条显示 **每个运行中的每个智能体** 正在执行的操作(其最新的命令、文件编辑或思想,从转录中尾随)。以下:每个阶段的智能体卡片,实时状态行,甘特时间线,令牌计数,保留的工作树,以及单击即打开的实时尾随转录抽屉。浅色和深色主题。
<img alt="转录抽屉打开,显示已完成的运行,实时尾随一个智能体的转录:其最终的发现报告,然后是原始令牌使用和转折完成事件。" src="docs/screenshots/transcript-dark.png">
## 路线图
- [与模型无关的后端](https://github.com/xxxoooxoxo/wiff/issues/1) —— 在 Codex 之外使用模型运行单个智能体,通过模型名称路由。
## 相关项目
- [robzilla1738/Codex-Workflows](https://github.com/robzilla1738/Codex-Workflows) —— 用于 Codex 的面向代码的工作流运行时,专注于审查扇出。收敛设计,独立实现。
- [scasella/claude-dynamic-workflows-codex](https://github.com/scasella/claude-dynamic-workflows-codex) —— Claude Code 的动态工作流 DSL 重新托管在 GPT 智能体上,具有会话工作者和浏览器运行查看器。
- Codex 的原生子智能体 —— 适用于交互式、人工监督的扇出;此插件适用于自动、可恢复、有日志记录的种类。
## 开发
```sh
cd plugins/wiff
npm test # 单元测试(假后端,无令牌消耗)
npm run check # 语法检查
npm run smoke # 一个真实的 Codex 子节点,端到端
```
将已完成的 smoke 运行 ID 传给以验证跨进程恢复而无需另一次模型调用:
```sh
npm run smoke -- wf_<run-id>
```
Codex 运行已安装的插件来自版本缓存 —— 编辑源代码后,更新 `.codex-plugin/plugin.json` 中的版本并重新运行 `codex plugin add wiff@wiff` 以获取更改。
### 发布
将版本更新到 `main` 会自动将软件包发布到 npm,然后使用相同版本注册到 MCP 注册表。保持版本号在以下位置一致:
- `plugins/wiff/package.json`
- `plugins/wiff/.codex-plugin/plugin.json`
- `plugins/wiff/.claude-plugin/plugin.json`
- `server.json` 及其 npm 软件包条目
发布工作流如果这些值或 npm/MCP 包名称不匹配,则会失败。它可以安全地重新运行;已经存在于任一注册表中的版本将被跳过。
npm 发布使用受信任的 GitHub Actions 发布者而不是长期令牌。一次性 npm 配置为 `@xxxoooxoxo/wiff`,工作流 `release.yml`,允许 `npm publish`。MCP 注册表还使用 GitHub OIDC 进行身份验证,无需存储库秘密。
## 许可证
[MIT](LICENSE)