面向 omp(@oh-my-pi/pi-coding-agent)的脚本化子智能体编排插件。一条工作流就是一段纯
JavaScript —— agent()、parallel()、pipeline()、阶段、重试、质量门禁 —— 在后台运行,实时投射到 TUI
或 React 控制台,并可保存为自己的斜杠命令。
由 pi-dynamic-workflows 迁移而来,完全对齐当前 @oh-my-pi/* 扩展 API。
| omp | @oh-my-pi/pi-coding-agent ≥ 17.2.4 且 < 17.3(peer 依赖,另含 pi-ai / pi-tui / pi-utils) |
| bun | 插件安装与控制台构建 |
| git | 仅 isolation: "worktree" 需要 |
peer 范围为 >=17.2.4 <17.3。17.2.9 已实测兼容:其 typebox shim 不认
Type.Optional(Type.Unsafe(...)) 组合(asRuntime(schema).or is not a function,扩展加载即崩),
workflow 工具已改用 Type.Object 构造 args schema 规避,见 src/workflow-tool.ts 注释。
omp plugin install github:ExLei/omp-dynamic-workflows装完不需要再构建:React 控制台的产物已预构建并提交在 web/dist。omp plugin install 底层调用
bun install <git spec>,而 bun 只有在消费方 package.json 的 trustedDependencies 里列出该依赖时,
才会执行 git 依赖的生命周期脚本 —— omp 的插件根目录并没有列,所以安装期的 prepare 构建会静默不执行。
.github/workflows/web-dist.yml 会在 web/** 变更时重新构建并提交 web/dist,因此发布的产物不会与源码漂移。
bun install
omp plugin link .清单通过 omp.extensions 暴露 extensions/workflow.ts,通过 omp.skills 附带 workflow-authoring 与
workflow-patterns 两个 skill。
安装命令与 macOS/Linux 完全一致(bun 原生支持 Windows;git 仅 isolation: "worktree" 需要):
# 首次安装 bun(如尚未安装)
powershell -c "irm bun.sh/install.ps1 | iex"
# 安装插件
omp plugin install github:ExLei/omp-dynamic-workflows所有状态路径都基于 os.homedir()(Windows 上为 %USERPROFILE%),无需手工迁移:
| 文档里的路径 | Windows 实际位置 |
|---|---|
~/.omp/workflows/settings.json |
%USERPROFILE%\.omp\workflows\settings.json |
~/.omp/workflows/model-tiers.json |
%USERPROFILE%\.omp\workflows\model-tiers.json |
~/.omp/workflows/saved/ |
%USERPROFILE%\.omp\workflows\saved\ |
Web 控制台在 Windows 上用默认浏览器打开(rundll32 url.dll,FileProtocolHandler,不走 shell,URL token 不会被 & 截断);SSH 会话同样只打印 URL。环境变量写法为 PowerShell 语法:
$env:OMP_WORKFLOW_WEB = "8787" # 固定控制台端口
$env:OMP_WORKFLOW_WEB_TOKEN = "…" # 跨重启复用 tokengit worktree 隔离、运行持久化(tmp+rename 原子写、wx 租约锁)均为跨平台实现。
四种入口,从隐式到显式:
# 1. 关键词触发 —— 交互消息中出现独立词 `workflow`/`workflows` 时,
# 提交瞬间改写这条消息,强制本回合走工作流。
> 对比这两种设计方案,run a workflow
# 2. 常驻 effort —— 任何“实质性”消息(≥16 字符且不是斜杠命令)都自动挂载工作流。
# 仅本会话有效,永不落盘。
/effort ultra # 或 /ultracode
# 3. 直接用一条精选模式
/deep-research X 和 Y 的取舍分别是什么?
# 4. 只为这一条提示词强制走一次工作流
/workflows run 审计 src/ 下所有重试路径“挂载”只做授权与引导:本回合保留全部常规工具(模型往往需要先读代码),再额外加入 workflow 工具,
并在 turn_end 恢复原工具集。随后模型写出脚本并调用 workflow。运行默认在后台(headless 会话默认同步执行,
background 参数与 syncMode 设置可覆盖,见工具接口)—— /workflows 列出运行、
底部进度面板实时跟踪,运行结束后结果投递回对话。
flowchart LR
U["用户消息"] -->|关键词 / effort / 斜杠| A["已挂载回合"]
A --> M["模型编写脚本"]
M --> T["workflow 工具"]
T --> R["WorkflowManager"]
R --> S["子智能体执行: 阶段/并行/流水线"]
S --> P["journal + 持久化"]
R --> N["TUI 导航器 + 进度面板"]
R --> W["Web 控制台"]
S --> D["结果投递回对话"]
| 命令 | 参数 | 作用 |
|---|---|---|
/workflows |
(无) → 导航器 UI | 交互式运行列表与控制 |
list · status <id> · watch <id> |
查看运行 | |
pause <id> · resume <id> · stop <id> · rm <id> |
控制运行 | |
run <prompt> |
为 <prompt> 强制一次工作流回合 |
|
save <name> [runId] [project|user] |
把某次运行的脚本保存为斜杠命令 | |
web [url] |
打开控制台,或仅打印 URL | |
/workflows-models |
(无) | 交互式模型分层编辑器(small / medium / big) |
/workflows-trigger |
on · off · set <word> · reset · status |
关键词触发偏好(持久化) |
/workflows-progress |
compact · detailed · status · max <N> |
底部进度面板(持久化) |
/effort |
off · high · ultra |
常驻自动挂载等级(仅本会话) |
/ultracode |
(无) = 开启 · off |
/effort ultra 的别名 |
/deep-research、/adversarial-review、/code-review、/multi-perspective、/codebase-audit |
见内置模式 | 五个精选内置工作流 |
/<savedName> |
key=value 与位置参数 → args |
每个已保存工作流一条命令 |
/code-review 会自己取 diff:无参数 → git diff HEAD;纯数字 → gh pr diff <n>;a..b → 该区间;
其他 → 该路径在 HEAD 下的 diff。
已保存工作流在扩展加载时注册为命令,/workflows save 之后立即再注册一次。同名已保存工作流会遮蔽内置模式
(斜杠命令与工具 name 入参都遵循此优先级)。删除保存文件后,命令会残留到会话重载为止(宿主没有
unregisterCommand)。
五条经过测试的精选工作流。每条既是斜杠命令,也可通过工具的 name + args 直接调用 —— 凡是能套进这些形状的
需求,都优先用它们,而不是重写一份等价脚本。
name |
适用场景 | args |
|---|---|---|
deep-research |
扇出网络搜索、抓取来源,3 票对抗验证主张后输出带引用的报告 | { question, angles?=4, minSupport?=2 }(minSupport = 否决票数,截 1..2;每主张 3 个独立怀疑者、默认怀疑) |
adversarial-review |
先调查,再由怀疑派审阅者逐条交叉核验 | { task, reviewers?, threshold? } |
code-review |
分级多角度 diff 评审(正确性、复用、简化、效率、抽象层次),按位置分组验证 | { diff, diffSource?, level? }(level:high/xhigh/max,默认 high)—— diff 需自行获取 |
multi-perspective |
多个独立视角并行分析后综合 | { topic, perspectives? }(默认:技术、产品、安全、用户体验、可维护性) |
codebase-audit |
针对某范围并行检查后交叉验证 | { scope, checks[] } |
这五个名字只在工具顶层 name 生效。脚本内的 workflow(name, args) 只解析已保存工作流,不解析内置模式。
两个工具在扩展加载时注册,并在 session_start 激活。子智能体永远看不到它们(workflow 与
workflow_control 始终从子会话工具集中排除),因此工作流无法再嵌套派生工作流。
| 字段 | 类型 | 默认值 | 含义 |
|---|---|---|---|
script |
string | — | 工作流 JS。未提供 name 时必填 |
name |
string | — | 按名字运行已保存或内置工作流(已保存优先)。不能与 resumeFromRunId 同用 |
args |
object | — | 绑定到脚本的 args 全局 |
background |
boolean | 随上下文 | 默认:TUI 会话(有 UI)为 true,后台运行返回 run id;headless 会话为 false,阻塞并内联返回结果。优先级:显式 background > options.syncMode > 设置里的 syncMode > 上下文默认 |
maxAgents |
number | 上限 1000 | 本次运行的智能体硬上限 |
concurrency |
number | 8(最大 16) | 同时在飞的智能体数 |
agentRetries |
number | 0(最大 3) | 可恢复失败后的重试次数 |
agentTimeoutMs |
number | 无 | 单个智能体超时 |
tokenBudget |
number | 无 | 软性花费闸门 —— 必须显式指定,绝不推断 |
resumeFromRunId |
string | — | 恢复此前运行(可换用改过的脚本);始终后台 |
maxAgents / concurrency / agentRetries / agentTimeoutMs / tokenBudget 在运行开始(以及恢复)时
被冻结到该运行上,因此恢复的运行仍沿用起始时的限额。
{ action, runId? } —— 除 list 外都必须给 runId。
action |
返回 |
|---|---|
list |
{ runs: [{ runId, workflowName, status, phase, counts{total,done,running,queued,error,skipped}, activeLabels, tokenTotal }] } |
status |
单个运行的同结构摘要 |
pause / resume / stop |
{ result: "paused" | "resumed" | "stopped", run } |
reply { runId, script? } |
答复 waiting_consult 运行:script 为修改后的完整脚本(省略=维持原脚本继续;apply:"confirm" 时省略=采纳审阅建议) |
intervene { runId } |
随时介入:把运行(running/paused/waiting_consult 均可)挂起为 waiting_consult 并投递咨询消息给主代理(含 runId 与回复指引),等待 reply;对已挂起的运行改投主代理并使在飞自动审阅链失效 |
出错时返回 allowedActions:running → status/pause/stop · paused → status/resume/stop ·
waiting_consult → status/stop/reply/intervene · failed/pending → status/resume ·
completed/aborted → status。手动停止落为 stopped/aborted,不会记成错误。reply/intervene
按 runId 全局寻址(跨会话可回复);reply 的 script 必须通过脚本解析校验,否则返回错误并保持等待。
改动工作流 JS 前请先加载随包的 workflow-authoring skill —— 那里有完整契约、配方与评审清单。核心如下:
export const meta = {
name: "fan_out_and_synthesize",
description: "Run bounded independent work, retain a complete coverage ledger, then synthesize",
phases: [{ title: "Fan out" }, { title: "Synthesize" }],
};
// ADAPT: validate and bound args.work for the task before invoking this workflow.
const work = args && Array.isArray(args.work) ? args.work : [];
phase("Fan out");
const fanOutResults = await parallel(
work.map((unit, index) => () =>
agent(
`Complete this independent work unit. Return only evidence relevant to it.\n\n${JSON.stringify(unit)}`,
// INVARIANT: index plus a stable task-owned id keeps labels unique.
{ label: `fanout:${index}:${String(unit.id)}` },
),
),
);
// INVARIANT: preserve every intended identity before filtering or synthesis.
const ledger = work.map((unit, index) => ({
id: String(unit.id),
status: fanOutResults[index] === null ? "failed" : "complete",
result: fanOutResults[index],
}));
phase("Synthesize");
const synthesis = await agent(
`Synthesize the complete fan-out ledger below. Distinguish covered work from failed/missing coverage; do not invent results.\n\n${JSON.stringify(ledger)}`,
{
label: "synthesize-complete-set",
// ADAPT: keep the schema small and aligned with downstream field access.
schema: {
type: "object",
properties: {
summary: { type: "string" },
coveredIds: { type: "array", items: { type: "string" } },
failedIds: { type: "array", items: { type: "string" } },
},
required: ["summary", "coveredIds", "failedIds"],
},
},
);
// INVARIANT: return plain serializable data, including missing-coverage identities.
return { ledger, synthesis };(逐字取自 skills/workflow-authoring/examples/fan-out-and-synthesize.js)
- 首条语句必须是字面量
export const meta = { name, description, phases?, model? }。 - 至少调用一次
agent();每次调用给一个简短唯一的label;显式return可 JSON 序列化的纯数据 —— 不能含函数、Promise、循环引用、BigInt 或句柄。 - 纯 JavaScript:不能
import/require,不能碰文件系统。不确定性只能经args传入;Date.now()、Math.random()、无参new Date()已被屏蔽。 - 可恢复的智能体失败会解析为
null,而不是抛错。先把结果与稳定的工作 ID 配对再过滤,并把缺失覆盖如实报出, 不要悄悄丢掉。
| 全局 | 签名 |
|---|---|
agent |
agent(prompt, options?) → Promise<string | 结构化 | null> |
parallel |
parallel(thunks) → Promise<Array<unknown | null>> —— 传函数不是 Promise;保持顺序 |
pipeline |
pipeline(items, ...stages) → Promise<Array<unknown | null>> —— items 并发、stages 串行;stage 参数 (prev, item, index) |
workflow |
workflow(savedName, childArgs?) → Promise<unknown> —— 只允许一层嵌套;共享限流器、计数、预算与 store |
verify |
verify(item, { reviewers?=2, threshold?=0.5, lens? }) → { real, realCount, total, votes } |
judgePanel |
judgePanel(attempts, { judges?=3, rubric? }) → { index, attempt, score, judgments } | undefined |
loopUntilDry |
loopUntilDry({ round, key?, consecutiveEmpty?=2, maxRounds?=50 }) → unknown[] |
completenessCheck |
completenessCheck(taskArgs, results) → { complete, missing? } | null |
retry |
retry(thunk, { attempts?=3, until? }) → unknown —— until 必须同步 |
gate |
gate(thunk, validator, { attempts?=3 }) → { ok, value, attempts } —— validator 需返回 { ok } |
checkpoint |
checkpoint(prompt, { default?, headless?, kind?, choices?, timeoutMs? }) → unknown |
consult |
consult(prompt, { to?="agent", agent?, apply?="auto", timeoutMs? }) → { applied, revisedScript?, summary }(同步函数,await 兼容)—— live 执行抛 CONSULT_PENDING 中断脚本并挂起运行,重放命中返回 journaled 结果;同步路径(headless)报工具错误含回复指引;agent/timeoutMs 仅声明未接通 |
phase |
phase(title, { budget? }) —— 声明当前阶段,可带软性子预算 |
log |
log(message) —— 新代码用它;console.* 仅为兼容保留 |
args、cwd、process、budget |
调用参数 · 运行 cwd · { cwd() } · { total, spent(), remaining() }(软性记账) |
checkpoint 的 confirm 与 headless 路径已实现;kind: "input" \| "select" 和 timeoutMs 仅在契约中声明、
尚未接通 —— 不要依赖。
consult 是运行中干预点:脚本执行到该行即中断(抛 CONSULT_PENDING),运行挂起为
waiting_consult,由审阅者(默认运行内子代理,to: "main" 则投递主代理)给出修改后的脚本,
经重放式续跑应用(journal 重放已完成调用,只执行变化部分)。恢复运行时重放命中 journal 结果,
consult() 返回 { applied, revisedScript?, summary }。默认 apply: "auto"(子代理建议直接
应用,累计自动应用超过 5 次后回落人工答复);apply: "confirm" 先出建议再经主代理确认,建议
就绪时投递第二条消息(摘要 + 落盘路径,reply 不附 script 即采纳),建议未就绪时省略 script 的
reply 回落「维持原脚本继续」。审阅失败(auto 模式)置 failed,可带脚本 resume 重新咨询;confirm
模式失败保持 waiting_consult 等待人工答复。parallel() 内的 consult:CONSULT_PENDING 是
不可恢复错误,穿透扇出中止在飞的兄弟智能体,运行照常挂起。同步执行(headless)下 consult 表现
为工具报错,文案含回复指引;to:"agent" 的自动审阅链照常后台执行。agent 与 timeoutMs 选项
仅在契约中声明、尚未接通 —— 不要依赖。
运行级共享存储不是脚本全局。智能体拿到的是 store_put / store_get 工具,跨智能体状态经由智能体写入
的 store 流转,脚本侧读不到。
| 选项 | 类型 | 含义 |
|---|---|---|
label |
string | 展示/journal 标签;简短且唯一 |
phase |
string | 覆盖当前阶段 |
schema |
JSON Schema | 结构化输出;顶层必须是 type: "object",返回已解析对象 |
model |
string | 精确 provider/modelId(或裸 id)—— 优先级最高 |
tier |
string | 已配置的分层名(small / medium / big) |
agentType |
string | 具名智能体定义(.omp/agents 或用户目录):工具、模型、提示词 |
isolation |
"worktree" |
尽力创建 git worktree;失败会记录日志并继续(不隔离) |
timeoutMs |
number | null | 单智能体超时;null 关闭 |
retries |
number | 单智能体可恢复重试(0–3);覆盖本次调用的 agentRetries |
模型选择优先级从高到低:model → agentType 的模型 → tier → 阶段模型 → meta.model → 隐式 medium
→ 会话默认。显式指定却不可用的模型会抛 MODEL_NOT_FOUND,不做静默降级;只有隐式默认档会退回会话模型,
并给一次性告警。结构化输出走 structured_output 工具,带 2 次修复重试;最终仍不合规视为不可恢复。
每次 agent() 调用都是一个受限的 omp 子会话:默认编码工具集,加上运行级的 store_put / store_get,
并且 workflow 与 workflow_control 始终禁用(excludeSubagentTools 里列出的也一并禁用)。
子代理会话默认挂载主代理的全部 MCP 服务器(.mcp.json 项目 + 用户配置发现源,
enableMCP: true)——每个子代理独立拉起服务器连接/子进程,不共享主会话连接;
并发 N 个子代理 × M 个服务器对应 N×M 份启动与资源开销。
运行也可以带一个
具名 toolset —— /deep-research 用的是 web-research,它额外提供真实的 web_search / web_fetch
(在扩展宿主进程内执行网络请求);该 tag 会随运行持久化,因此恢复的运行仍保有这些工具,不会静默退化。
agentType 会按其定义的 tools / disallowedTools 进一步收窄。子会话记录默认不落盘,除非开启
persistAgentSessions。
/workflows save <name> [runId] [project|user],或控制台的 Save 按钮,会把 <name>.js 写入所选作用域并
立刻注册斜杠命令:
| 作用域 | 路径 | 说明 |
|---|---|---|
project(默认) |
<cwd>/.omp/workflows/saved/ |
可提交,团队共享 |
user |
~/.omp/workflows/saved/ |
个人,全项目可用 |
加载优先级:项目内 → 历史遗留的 home 项目目录 → 用户级。
一个 tier 就是一个具名槽位,只放一个模型规格(允许 :thinking 后缀),存于
~/.omp/workflows/model-tiers.json:
{ "tiers": { "small": "…", "medium": "…", "big": "…" } }/workflows-models 打开该文件的编辑器;磁盘上没有文件时,它会基于你可用的模型(按价格、再按名称线索排序)
给出建议映射,并标记为未保存。在真正保存之前,opts.tier 会退回会话模型,并给出告警:
[workflow] An agent requested opts.tier but no model-tiers.json is configured, so tiers currently fall back to <model>. Run /workflows-models to configure them
配置有效后,未标注分层的智能体默认走 medium。
一套 React/Vite UI,直接架在活的 WorkflowManager 之上 —— 与 TUI 导航器、任务面板同一个实例,
因此运行、快照、暂停/恢复/停止是共享的,不是镜像同步。
产物随插件发布,安装无需构建。改完 web/src 用 bun run web:build 刷新(CI 在 main 上做同样的事);
OMP_WORKFLOW_WEB_ROOT 可覆盖被托管的目录。
/workflows web |
用默认浏览器打开控制台 |
/workflows web url |
只打印 URL —— 适合 SSH 或粘到另一台机器 |
| 会话启动 | 打印 Workflow web console: http://127.0.0.1:<port>/?token=… (/workflows web to open) |
"web": { "open": true } |
每次会话启动自动开浏览器 |
URL 携带进程级 token,每次启动都变,且无法从磁盘恢复 —— 启动提示滚走之后,/workflows web 是唯一的回路。
自动打开默认关闭:在每个仓库的每次 omp 都弹浏览器是骚扰,在 SSH 下更是错的(opener 会检测
SSH_CONNECTION/无 DISPLAY,并提示你改用 URL)。
在本仓库实测,控制台给启动增加约 3ms:模块求值 ~2.5–3.2ms 加环回绑定 ~0.5ms,对比 omp 约 2.9s 的启动。
omp -p "" --no-session 的 A/B(各 7 次)给出的差值是 −3ms/+22ms —— 落在抖动区间内。靠三条规则守住,
tests/web-startup-budget.test.ts 一旦被破坏就构建失败:
- 入口通过动态且从不 await 的
import()拿到服务器,并推迟到session_start之后一个宏任务 —— 首屏之前不挡任何东西。 - 服务器的模块图被钉在插件本来就会加载的模块上。新增一个
@oh-my-pi/pi-coding-agent的值导入要付 ~1.4s(见src/omp-host.ts)。 - React 资源根目录在首个浏览器请求时才 stat,而不是绑定时;没人打开控制台的会话不会多碰任何文件。
889KB 的 bundle 故意不做代码分割:环回传输 5.4ms(FCP 164ms),而编辑器和两张图在首屏即可见, 懒加载分块只会多闪一下。
实时运行列表;一张运行时拓扑图 —— 阶段是容器,装着它实际产生的智能体(由真实 agentStart/agentEnd
事件构建);日志;SSE 事件流;以及运行的返回值。点任意智能体(图上或表里)会从右侧抽屉展开它未裁剪的
完整记录,运行期间由 /api/runs/:id/agents/:id 实时刷新;快照推送保持裁剪,避免大扇出在每个 tick 重播全部记录。
编写区是 CodeMirror JS 编辑器,编辑的就是工具与斜杠命令实际运行的那份脚本 —— 没有降级的图形 DSL ——
旁边配一张尽力而为的静态结构图:包含关系画成嵌套(phase 拥有其后的调用,parallel 块拥有其分支),
执行顺序画成兄弟节点间的箭头,扇出只能在运行时确定的部分用虚线。点节点会在编辑器里定位到对应调用。
每个区域都是可拖拽调整的窗格,布局按分组持久化在 localStorage。保存时会询问项目级还是个人级作用域 ,
并立即注册斜杠命令。waiting_consult 运行可一键介入(intervene 按钮),并在编辑器里回复(「回复并继续」把
编辑器脚本作为答复提交,经 resolveConsult 带脚本恢复;确认模式建议就绪时编辑器预载审阅建议脚本)。
只绑环回,且所有 /api/* 都必须带 token(?token=、x-workflow-token: 或
Authorization: Bearer)—— 因为启动一次运行就等于在 omp 进程内执行任意 JS。
| 路由 | 用途 |
|---|---|
GET /api/state |
cwd、运行摘要、已保存工作流、内置模式 |
GET /api/events |
SSE 快照/事件流 |
POST /api/parse |
解析脚本 → meta + 静态大纲 |
POST /api/runs |
启动运行 |
GET /api/runs/:id |
状态、脚本、args、快照、结果 |
DELETE /api/runs/:id |
删除运行 |
POST /api/runs/:id/{pause,resume,stop} |
控制 |
GET /api/runs/:id/agents/:agentId |
单个智能体的未裁剪记录 |
GET /api/save-locations?name= |
可用保存作用域,以及哪些已占用该名字 |
POST /api/saved |
保存工作流(与工具路径同样校验) |
从浏览器启动的运行会写入 transcript,但以 triggerTurn: false 投递 —— 在 Web UI 点 Run 只记录上下文,
不会把 TUI 里的助手唤起成一次未经请求(且要计费)的回合。
bun run spike:web # 独立 manager + 模拟智能体,端口 7788
bun run web:dev # Vite 跑 :5178,把 /api 代理到 :7788~/.omp/workflows/settings.json,再被 ~/.omp/workflows/projects/<key>/settings.json 浅覆盖
(项目设置放在机器本地的 workflow home,按 cwd 派生 key —— 不在仓库里)。项目级的 web 对象会整体替换
全局的,不做深合并。未知键被丢弃;文件损坏视为 {}。
| 键 | 类型 | 默认值 |
|---|---|---|
keywordTriggerEnabled |
boolean | true |
keywordTriggerWord |
string(不能以 / 开头,不能含空白) |
"workflow"(默认词同时匹配 workflows) |
defaultAgentTimeoutMs |
number | null | null(无硬超时) |
defaultTokenBudget |
整数 ≥1 | null | null(不限) |
defaultConcurrency |
1–16 | 8 |
defaultAgentRetries |
0–3 | 0 |
progressPanelMode |
"compact" | "detailed" |
"compact" |
progressPanelMaxAgents |
≥1(上限 1000) | 8 |
persistAgentSessions |
boolean | false |
deliveredResultMaxChars |
1–1000000 | 400 |
excludeSubagentTools |
string[] | [](叠加在始终排除的编排工具之上) |
syncMode |
"auto" | "always" | "never" |
"auto" |
phaseNotify |
"off" | "phase" |
"phase" |
web.enabled |
boolean | true(只有显式 false 才关闭) |
web.port |
1–65535 | 0(临时端口) |
web.announce |
boolean | true |
web.open |
boolean | false |
优先级:全局文件 → 项目文件 → OMP_WORKFLOW_WEB(仅 web 相关键)→ 单次运行的工具入参
(concurrency、agentTimeoutMs 等)。
{
"keywordTriggerWord": "workflow",
"defaultConcurrency": 8,
"defaultAgentRetries": 1,
"syncMode": "auto",
"phaseNotify": "phase",
"progressPanelMode": "detailed",
"web": { "enabled": true, "port": 0, "announce": true, "open": false }
}| 变量 | 取值 | 效果 |
|---|---|---|
OMP_WORKFLOW_WEB |
0/false/off 关闭;2–65535 的整数固定该端口;其他非空值 → 启用并用临时端口 |
单次启动覆盖 web.enabled / web.port |
OMP_WORKFLOW_WEB_TOKEN |
任意字符串 | 跨重启复用控制台 token(否则每进程随机 24 字节) |
OMP_WORKFLOW_WEB_ROOT |
含 index.html 的目录 |
托管该 UI 根目录,替代内置 web/dist |
SSH_CONNECTION、SSH_TTY、DISPLAY、WAYLAND_DISPLAY |
只读 | 判断能否打开浏览器 |
| 路径 | 内容 |
|---|---|
~/.omp/workflows/settings.json |
用户设置 |
~/.omp/workflows/model-tiers.json |
模型分层 |
~/.omp/workflows/saved/ |
个人已保存工作流(<name>.js) |
~/.omp/workflows/projects/<basename>-<sha256[0:12]>/ |
机器本地、按 cwd 划分的根目录 |
…/settings.json |
项目设置覆盖 |
…/runs/<runId>.json(及 .bak、.lock、.log) |
运行状态(内嵌恢复 journal)、原子写备份、跨进程租约锁、可选文件日志 |
<cwd>/.omp/workflows/saved/ |
可提交的项目级已保存工作流 |
<repoRoot>/.omp/worktrees/<slug> |
智能体隔离用 worktree,分支 omp/wf/<slug> |
.omp/agents |
供 agentType 使用的项目级具名智能体定义 |
磁盘上的终态运行默认保留 300 条,超出按最旧优先清理。
bun run check # tsc --noEmit
bun test # tests/
bun run build # tsc -p tsconfig.build.json
bun run web:build # 构建控制台 → web/dist| 测试 | 守护的契约 |
|---|---|
omp-integration.test.ts |
清单/扩展发现、工具与命令面、.omp/workflows 命名空间、智能体身份隔离 |
run-persistence.test.ts |
机器本地 runs 目录的存/取/列/删、租约,且运行数据不写入仓库 .omp |
save-location.test.ts |
项目级 vs 用户级保存目录、遗留 home 目录仍可读、展示路径 |
web-server.test.ts |
环回 API 鉴权、运行控制、保存位置 |
web-startup-budget.test.ts |
服务器只能经动态 import 到达;OMP_WORKFLOW_WEB 语义 |
stop-semantics.test.ts |
手动停止落为 stopped/aborted 而非 error |
panel-ui.test.ts |
紧凑面板的 spinner/耗时、导航器与保存位置文案 |
schema-coercion.test.ts |
结构化输出的 TypeBox convert/check 强制转换 |
live-usage.test.ts |
经 workflow 与 manager 的 token 用量聚合 |
js-save.test.ts |
已保存工作流(.js)的作用域存/取与脚本解析校验 |
subagent-sync.test.ts |
子智能体会话同步:syncHostTools 三选项、MCP 默认全量继承 |
workflow-tool-sync.test.ts |
workflow 工具 background 默认值随会话形态,syncMode 优先级链 |
workflow-watch.test.ts |
/workflows watch 输出通道:headless 流式消息、UI 会话状态栏 |
builtin-preview.test.ts |
内置模式预览脚本可解析且与元数据一致 |
acp-bridge.test.ts |
ACP/无 UI 会话检测与进度帧渲染 |
consult-vm.test.ts |
consult() 运行时:live 抛 CONSULT_PENDING、重放命中、settled:false 重新挂起、hashConsult 身份 |
consult-manager-state.test.ts |
waiting_consult 状态机:catch 尾分支、stop/resume 触点、冷启动恢复 |
consult-resolve.test.ts |
resolveConsult/markConsultFailed 收口:原子性、代际校验、持久化先于恢复 |
consult-review-chain.test.ts |
自动审阅链:子代理落盘、token 预算、上限 5 次回落、陈旧链丢弃 |
consult-control-tool.test.ts |
workflow_control reply/intervene:applied/resumed 标签、跨会话寻址、非法输入 |
consult-delivery.test.ts |
投递出口:consult-pending/review-ready/limit 消息与唤醒判定 |
capability-publications.test.ts |
能力契约生成文档零漂移 |
zh-copy.test.ts |
源码与 Web 控制台的全部工具描述、提示词与 UI 文案为中文 |