Merge branch 'worktree-llm-dynamic-config' into worktree-llm-web-config
# Conflicts: # apps/cli/cordis.yml # apps/web/tests/snapshots/code-mode-round/session.jsonl # apps/web/tests/snapshots/cordis-tool-round/session.jsonl # apps/web/tests/snapshots/fresh-round-trip/session.jsonl # apps/web/tests/snapshots/lifecycle-chrome/session.jsonl # apps/web/tests/snapshots/live-interactions/session.jsonl # apps/web/tests/snapshots/navigation-panes/seed.jsonl # apps/web/tests/snapshots/question-composer/session.jsonl # apps/web/tests/snapshots/seeded-history/seed.jsonl # apps/web/tests/snapshots/steering/session.jsonl # docs/cordis-catalog/events.md # docs/cordis-catalog/services.md # docs/core-data-structures/core.i18n.yaml # docs/core-data-structures/settings.i18n.yaml # docs/event-producer-consumer.md # docs/module-graph.md # examples/acp-agent/tests/snapshots/workspace-context/session.jsonl # packages/client/connection/README.i18n.yaml # packages/client/connection/src/index.ts # packages/client/connection/tests/node-half.spec.ts # packages/client/runtime/README.i18n.yaml # packages/client/runtime/README.md # packages/client/runtime/README.zh.md # packages/client/runtime/src/client/index.ts # packages/client/runtime/tests/fake-api.ts # packages/client/ui-models/README.i18n.yaml # packages/examples/tui-demo/README.i18n.yaml # packages/host/apiproxy/README.i18n.yaml # packages/host/apiproxy/package.json # packages/host/apiproxy/src/api-proxy.ts # packages/host/apiproxy/src/api/rpc.schema.ts # packages/host/apiproxy/src/api/rpc.ts # packages/llm/llm-deepseek/README.i18n.yaml # packages/llm/llm-deepseek/README.zh.md # packages/llm/llm-pi-ai/README.i18n.yaml # packages/llm/llm/README.i18n.yaml # packages/llm/llm/README.zh.md # packages/sdk/sdk-client/README.i18n.yaml # packages/settings/settings/README.i18n.yaml # packages/settings/settings/README.md # packages/settings/settings/README.zh.md # packages/subagent/subagent-dsh-sdk/README.i18n.yaml # packages/subagent/subagent-dsh-sdk/README.zh.md # packages/support/llm-replay/README.i18n.yaml # packages/ui/jsonrpc/README.i18n.yaml # packages/ui/jsonrpc/README.zh.md # packages/ui/tui/tests/snapshots/model-selector.expected.txt # packages/ui/tui/tests/snapshots/model-switching.expected.txt # packages/ui/tui/tests/snapshots/resume-sessions.expected.txt # packages/ui/tui/tests/snapshots/status-diagnostics-narrow.expected.txt # packages/ui/tui/tests/snapshots/status-diagnostics.expected.txt # packages/ui/tui/tests/tui.snapshot.ts # pnpm-lock.yaml # python/sdk/README.i18n.yaml # scripts/snapshots/translation-prompt-v4/request-response.expected.json
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write
|
||||
# pnpm run verify-translation-pairing --write packages/workflow/README.md
|
||||
README.md: 17da3397e4fa64837b271d89e9062e3621434338
|
||||
README.zh.md: 056d005de69a493a24b360583651bf313548b3fb
|
||||
README.zh.md: 1e424778781ba4f41c321af7fd4cb8b5159a49cb
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
|
||||
[English](README.md) | 中文
|
||||
|
||||
workflow seam:由模型编写 JavaScript 编排脚本,大规模扇出 subagent(分阶段、每个 agent(智能体)的结构化结果、并发上限),其设计参考 Claude Code 动态工作流。这是 bash 形态的能力 seam(见[能力 seam](../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md)):每个上下文只有一个引擎实现注册为 `ctx.workflows`;面向模型的工具消费它。
|
||||
工作流 seam:由模型编写 JavaScript 编排脚本,大规模扇出 subagent(分阶段、每个 agent(智能体)的结构化结果、并发上限),其设计参考 Claude Code 动态工作流。这是 bash 形态的能力 seam(见[能力 seam](../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md)):每个上下文只有一个引擎实现注册为 `ctx.workflows`;面向模型的工具使用它。
|
||||
|
||||
| 包 | 角色 | ctx 键 |
|
||||
| 包(package) | 角色 | ctx 键 |
|
||||
|---|---|---|
|
||||
| `workflow/` | 抽象 workflow seam:服务基类、运行词汇和 `workflow/*` 事件 | `ctx.workflows` |
|
||||
| `workflow/` | 抽象工作流 seam:服务基类、运行词汇和 `workflow/*` 事件 | `ctx.workflows` |
|
||||
| `workflow-workerthread/` | `node:worker_threads` 引擎:每次运行使用一个 worker;脚本的 vm 上下文位于 worker 内,`agent()` 通过消息端口桥接到 `ctx.subagents` | (提供 `ctx.workflows`) |
|
||||
| `tool-workflow/` | 面向模型的 `workflow` 工具,基于 `ctx.workflows` | (注册到 `ctx.tools`) |
|
||||
| `tool-ralph/` | 基于 `ctx.workflows` 和全新结构化输出 subagent 提供方的固定全新 agent Ralph 策略 | (注册到 `ctx.tools`) |
|
||||
|
||||
接口位于 `workflow/workflow/`。引擎的 `agent()` 钩子使用 [subagent seam](../subagent/README.md)(任何已注册提供方;随产品交付的示例使用 `spawn`),`agent({ schema })` 则使用进程内后端实现的结构化输出支持。worker thread 隔离的是脚本:宿主绝不会被它阻塞,已取消运行经过宽限时间后的终止也会实际生效;但它不是安全边界。如果将来确有需要,可以在同一接口后替换为 isolated-vm/独立进程引擎,以实现真正的沙箱隔离。
|
||||
接口位于 `workflow/workflow/`。引擎的 `agent()` 钩子使用 [subagent seam](../subagent/README.md)(任何已注册提供方;随产品交付的示例使用 `spawn`),`agent({ schema })` 则使用进程内后端实现的结构化输出支持。worker thread 隔离的是脚本:宿主绝不会被它阻塞,已取消运行经过宽限时间后的终止也会实际生效;但它不是安全边界。如果将来确有需要,可以在同一接口背后换用 isolated-vm/独立进程引擎,以实现真正的沙箱隔离。
|
||||
|
||||
通用脚本引擎的决策和延期工作见[动态工作流 Agent Note](../../.agents/notes/implemented/feature/2026-07-05-dynamic-workflows.md)。独立的 [Ralph 消费方](../../.agents/notes/implemented/feature/2026-07-19-fresh-agent-ralph-workflow-tool.md)会固定脚本和全新提供方策略,而不是再添加一个引擎或 agent loop(智能体循环)模式。
|
||||
通用脚本引擎的决策和暂缓事项见[动态工作流 Agent Note(agent 决策记录)](../../.agents/notes/implemented/feature/2026-07-05-dynamic-workflows.md)。独立的 [Ralph 消费方](../../.agents/notes/implemented/feature/2026-07-19-fresh-agent-ralph-workflow-tool.md)会固定脚本和全新提供方策略,而不是再添加一个引擎或 agent loop(智能体循环)模式。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write
|
||||
# pnpm run verify-translation-pairing --write packages/workflow/tool-ralph/README.md
|
||||
README.md: daf242364d1093cff30cd1dc95823e1ecb89a9c7
|
||||
README.zh.md: cc71630c097157182823342a873d673f6420f44e
|
||||
README.zh.md: db9564da1d1b7d8eabc67ba14c9d873201ac2807
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
[English](README.md) | 中文
|
||||
|
||||
面向模型的 `ralph` 工具运行固定的前台工作流,把一个不可变目标依次交给多个全新子 agent(智能体)。它展示如何把专用编排策略实现为基于 [`ctx.workflows`](../workflow/README.md) 和 [`ctx.subagents`](../../subagent/subagent/README.md) 的普通插件:不会向 `agent-loop` 添加 Ralph 模式或全新 agent 循环,同会话的[目标领域](../../goal/goal/README.md)也保持独立。政策和延期工作由 [Ralph Agent Note](../../../.agents/notes/implemented/feature/2026-07-19-fresh-agent-ralph-workflow-tool.md)负责。
|
||||
面向模型的 `ralph` 工具运行固定的前台工作流,把一个不可变目标依次交给多个全新子 agent(智能体)。它展示如何把专用编排策略实现为基于 [`ctx.workflows`](../workflow/README.md) 和 [`ctx.subagents`](../../subagent/subagent/README.md) 的普通插件:不会向 `agent-loop` 添加 Ralph 模式或全新 agent loop(智能体循环),同会话的[目标领域](../../goal/goal/README.md)也保持独立。策略和暂缓事项由 [Ralph Agent Note(agent 决策记录)](../../../.agents/notes/implemented/feature/2026-07-19-fresh-agent-ralph-workflow-tool.md)负责。
|
||||
|
||||
## 契约
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
## 生命周期与取消
|
||||
|
||||
调用方 agent 是每个全新子 agent 的父级,因此会保留 cwd 和谱系,但不会复制其对话。`exec.signal` 进入工作流引擎,同时也桥接到 `run.cancel()`,确保实现相互独立。工具等待 `run.result` 并调用 `run.dispose()`,后一个调用位于 `finally` 中,因此取消的父级步骤会等到引擎完成有界终止且子 agent 完全停稳后才返回。
|
||||
调用方 agent 是每个全新子 agent 的父级,因此会保留 cwd 和谱系,但不会复制其对话。`exec.signal` 进入工作流引擎,同时也桥接到 `run.cancel()`,以便不依赖具体实现。工具等待 `run.result` 并调用 `run.dispose()`,后一个调用位于 `finally` 中,因此取消的父级步骤会等到引擎完成有界终止且子 agent 完全停稳后才返回。
|
||||
|
||||
## 渲染意图
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
| `maxHandoffChars` | `16384` | 一份 Round 报告序列化后的最大字符数。 |
|
||||
| `maxResultChars` | `16384` | 返回给父级的完整成功结果最大字符数。 |
|
||||
|
||||
插件应用时会规范化并校验所有配置值;直接应用、未经过 Loader schema 规范化的情况也包括在内。每次调用前都会立即解析提供方能力,因为提供方注册可能随插件生命周期和 HMR(热模块替换)变化。
|
||||
插件应用时会规范化并校验所有配置值,也包括绕过 Loader schema 规范化而直接应用的情况。每次调用前都会立即解析提供方能力,因为提供方注册可能随插件生命周期和热模块替换(HMR)变化。
|
||||
|
||||
## 模型体验
|
||||
|
||||
@@ -49,21 +49,21 @@ Use the ralph tool ONLY when the direct human explicitly asks for a Ralph loop o
|
||||
|
||||
#### Token 影响
|
||||
|
||||
插件启用期间,每个请求支付少量固定指导成本。
|
||||
插件启用期间,每个请求都会产生少量固定的指导 token 开销。
|
||||
|
||||
#### KV Cache 影响
|
||||
|
||||
只要插件作用域和指导文本不变,前缀就保持稳定。启用或 dispose(资源释放)可能从该提示词段开始使复用失效。
|
||||
只要插件作用域和指导文本不变,前缀就保持稳定。启用或 dispose(资源释放)可能会使从该提示词段起的缓存复用失效。
|
||||
|
||||
### 工具 schema
|
||||
|
||||
#### 模型看到的内容
|
||||
|
||||
已生成的 [`ralph` schema](../../../docs/tool-catalog.md#deepseek-aidsh-tool-ralph)公开一个必填 `objective` 字符串和一个可选 `maxRounds` 数字。提供方选择、交接大小、报告 schema、工作流脚本和编排行为均由部署拥有,不在调用接口中。
|
||||
已生成的 [`ralph` schema](../../../docs/tool-catalog.md#deepseek-aidsh-tool-ralph)公开一个必填 `objective` 字符串和一个可选 `maxRounds` 数字。提供方选择、交接大小、报告 schema、工作流脚本和编排行为均由部署侧控制,不在调用接口中。
|
||||
|
||||
#### Token 影响
|
||||
|
||||
工具可见的每个请求都会支付少量固定 schema 成本。
|
||||
工具可见时,每个请求都会产生少量固定的 schema token 开销。
|
||||
|
||||
#### KV Cache 影响
|
||||
|
||||
@@ -83,11 +83,11 @@ Use the ralph tool ONLY when the direct human explicitly asks for a Ralph loop o
|
||||
|
||||
每个全新子 agent 都有独立的请求缓存。父级结果追加在可复用请求前缀之后。
|
||||
|
||||
## 已知限制与延期工作
|
||||
## 已知限制与暂缓事项
|
||||
|
||||
- **完成由 worker 自行声明**:没有独立的评估器或验证器判断目标是否实际完成;评估器政策及评估器驱动的延续均延期处理。
|
||||
- **仅支持前台**:没有 task id、后台收集、进程恢复检查点、调度器或基于墙上时钟的启动政策。
|
||||
- **完成由 worker 自行声明**:没有独立的评估器或验证器判断目标是否实际完成;评估器策略及评估器驱动的延续均暂缓处理。
|
||||
- **仅支持前台**:没有 task id、后台收集、进程恢复检查点、调度器或基于挂钟时间的启动策略。
|
||||
- **工作区是唯一的跨 Round 长期记忆**:一份有界报告作为显式交接内容,每个子 agent 结束后,未提交的对话推理都会消失。
|
||||
- **一个 Round 对应一个全新子 agent**:Round 内没有扇出、模型/提供方切换、fork 上下文或由模型调用选择的提供方。
|
||||
- **普通子 agent 失败会终止运行**:固定脚本报告失败的 Round 和上一次成功交接,但不会重试;致命的工作流基础设施失败可能在该状态返回前结束。
|
||||
- **聚合工作量仅受 Round 数量限制**:token、价格和已用时间预算均延期处理。
|
||||
- **聚合工作量仅受 Round 数量限制**:token、价格和耗时预算均暂缓处理。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write
|
||||
# pnpm run verify-translation-pairing --write packages/workflow/tool-workflow/README.md
|
||||
README.md: 5afa68764bfe339377954f8912b6f9b795435be1
|
||||
README.zh.md: d24442f2dcf12dae11980ab6ab08d1a63c2b1432
|
||||
README.zh.md: a322fa363803a10b06c66d1aae9b769fad8e5c22
|
||||
|
||||
@@ -2,26 +2,26 @@
|
||||
|
||||
[English](README.md) | 中文
|
||||
|
||||
面向模型的 **`workflow` 工具**:运行一段扇出 subagent 的 JavaScript 编排脚本,并返回脚本的最终值。本包负责基于 [`ctx.workflows`](../workflow/README.md) 塑造 schema 和生命周期;脚本解析、执行、上限与取消位于 seam 之后,消费方继续拥有面向父级的 schema 和结果包络。
|
||||
面向模型的 **`workflow` 工具**:运行一段扇出 subagent 的 JavaScript 编排脚本,并返回脚本的最终值。本包(package)负责基于 [`ctx.workflows`](../workflow/README.md) 塑造 schema 和生命周期;脚本解析、执行、上限与取消位于 seam 之后,消费方仍负责面向父级的 schema 和结果包络。
|
||||
|
||||
## 模型看到的内容
|
||||
|
||||
工具有三个参数:`meta`(必需的身份数据:`name`、`description` 和可选的进度注解)、`script`(必需的纯 JavaScript 函数体,不含 `export const meta` 语句;工具描述包含完整的编写契约)以及 `args`(可选 JSON 对象,作为全局变量 `args` 向脚本公开;裸列表应包装到字段中,使协议 schema 如实表达形态)。插件还会贡献一个 `tool:<toolName>` 系统提示词段,其中包含使用政策:只有用户明确要求工作流/大型编排时才使用该工具;一两项委派优先使用普通 subagent 调用。这遵循工具指导随工具插件交付、绝不放入部署 persona 的约定。
|
||||
工具有三个参数:`meta`(必需的身份数据:`name`、`description` 和可选的进度注解)、`script`(必需的纯 JavaScript 脚本体,不含 `export const meta` 语句;工具描述包含完整的编写契约)以及 `args`(可选 JSON 对象,作为全局变量 `args` 向脚本公开;裸列表应包装到字段中,使协议 schema 如实表达形态)。插件还会贡献一个 `tool:<toolName>` 系统提示词段,其中包含使用策略:只有用户明确要求工作流/大型编排时才使用该工具;一两项委派优先使用普通 subagent 调用。这遵循工具指导随工具插件交付、绝不放入部署 persona 的约定。
|
||||
|
||||
## 生命周期
|
||||
|
||||
当前版本采用同步收集(类似 [`dsh-tool-subagent`](../../subagent/tool-subagent/README.md)):`execute` 启动运行并等待 `run.result`;这些操作位于 `try/finally` 中,该结构总会 dispose 运行,使脚本及其子 agent(智能体)在每条路径上完全停稳。`exec.signal` 会桥接到 `run.cancel()`,包括启动前已经中止的情况。非 `completed` 结束原因会映射为报告原因的 `isError` 结果,绝不会把局部输出当作成功;`start()` 同步抛出的解析/meta 失败会变成模型可据以修正的 `isError`。完成时返回规范值 `{ runId, agentsStarted, result }`;Native 渲染器保留 meta 名称、agent 数量和 JSON 值,只会在 `maxResultChars` 处截断该投影。
|
||||
当前版本采用同步收集(类似 [`dsh-tool-subagent`](../../subagent/tool-subagent/README.md)):`execute` 启动运行并等待 `run.result`;这些操作位于 `try/finally` 中,该结构总会 dispose(资源释放)运行,使脚本及其子 agent(智能体)在每条路径上完全停稳。`exec.signal` 会桥接到 `run.cancel()`,包括启动前已经中止的情况。非 `completed` 结束原因会映射为报告原因的 `isError` 结果,绝不会把局部输出当作成功;`start()` 同步抛出的解析/meta 失败会变成模型可据以修正的 `isError`。完成时返回规范值 `{ runId, agentsStarted, result }`;Native 渲染器保留 meta 名称、agent 数量和 JSON 值,只会在 `maxResultChars` 处截断该投影。
|
||||
|
||||
## 渲染意图
|
||||
|
||||
渲染意图预先确定(见[渲染意图 Agent Note](../../../.agents/notes/implemented/architecture/2026-07-02-tool-render-intent-union.md)):使用一个 `generic` 卡片,标题为 `workflow: <meta.name>`,直接从 `args.meta.name` 读取(呈现是参数的纯函数,不要求引擎解析);脚本文本作为 `rawInput` 携带。结果继续使用 generic 卡片。
|
||||
渲染意图预先确定(见[渲染意图 Agent Note(agent 决策记录)](../../../.agents/notes/implemented/architecture/2026-07-02-tool-render-intent-union.md)):使用一个 `generic` 卡片,标题为 `workflow: <meta.name>`,直接从 `args.meta.name` 读取(呈现是参数的纯函数,不要求引擎解析);脚本文本作为 `rawInput` 携带。结果继续使用 generic 卡片。
|
||||
|
||||
## 配置
|
||||
|
||||
| 键 | 默认值 | 含义 |
|
||||
|---|---|---|
|
||||
| `toolName` | `workflow` | 要注册的面向模型工具名称。 |
|
||||
| `maxResultChars` | `50000` | 渲染结果上限;更长的 JSON 会连同提示一起截断。 |
|
||||
| `maxResultChars` | `50000` | 渲染结果上限;更长的 JSON 会被截断并附上提示。 |
|
||||
|
||||
## 模型体验
|
||||
|
||||
@@ -29,9 +29,9 @@
|
||||
|
||||
#### 模型看到的内容
|
||||
|
||||
在该插件的注册作用域内,每个父级请求都会收到下方的 workflow 指导。作用域工具限制可以隐藏 schema,而不移除这段独立注册的指导。
|
||||
在该插件的注册作用域内,每个父级请求都会收到下方的工作流指导。作用域工具限制可以隐藏 schema,而不移除这段独立注册的指导。
|
||||
|
||||
##### Workflow 指导
|
||||
##### 工作流指导
|
||||
|
||||
```markdown
|
||||
Use the <toolName> tool ONLY when the user explicitly asks for a workflow or for large multi-agent orchestration: you write a JavaScript script (the tool description documents the exact format) that fans work out across many subagents with phases and structured results. For one or two delegations, prefer plain subagent calls.
|
||||
@@ -39,25 +39,25 @@ Use the <toolName> tool ONLY when the user explicitly asks for a workflow or for
|
||||
|
||||
#### Token 影响
|
||||
|
||||
插件启用期间,每个请求支付少量固定指导成本。
|
||||
插件启用期间,每个请求都会产生少量固定的指导 token 开销。
|
||||
|
||||
#### KV Cache 影响
|
||||
|
||||
只要插件作用域和指导文本不变,前缀就保持稳定。启用或 dispose(资源释放)可能从该提示词段开始使复用失效。
|
||||
只要插件作用域和指导文本不变,前缀就保持稳定。启用或 dispose 可能会使从该提示词段起的缓存复用失效。
|
||||
|
||||
### 工具 schema
|
||||
|
||||
#### 模型看到的内容
|
||||
|
||||
工具可见时,已生成的默认 [`workflow` schema](../../../docs/tool-catalog.md#deepseek-aidsh-tool-workflow)包含完整的 JavaScript 钩子与元数据契约;`toolName` 可以重命名该定义,模型会提交脚本、元数据和可选 args。
|
||||
工具可见时,已生成的默认 [`workflow` schema](../../../docs/tool-catalog.md#deepseek-aidsh-tool-workflow) 包含完整的 JavaScript 钩子与元数据契约;`toolName` 可以重命名该定义,模型会提交脚本、元数据和可选 args。
|
||||
|
||||
#### Token 影响
|
||||
|
||||
工具可见的每个请求都会支付较大的固定 schema 成本。
|
||||
工具可见时,每个请求都会产生较大的固定 schema token 开销。
|
||||
|
||||
#### KV Cache 影响
|
||||
|
||||
只要 `toolName`、定义和可见性不变,前缀就保持稳定。重命名、插件生命周期或作用域限制可能从该 schema 开始使复用失效。
|
||||
只要 `toolName`、定义和可见性不变,前缀就保持稳定。重命名、插件生命周期或作用域限制可能会使从该 schema 起的缓存复用失效。
|
||||
|
||||
### 工具调用历史与结果
|
||||
|
||||
@@ -67,14 +67,14 @@ Use the <toolName> tool ONLY when the user explicitly asks for a workflow or for
|
||||
|
||||
#### Token 影响
|
||||
|
||||
调用 token 可能很多,并会保留到上下文压缩(compaction)为止。结果渲染受 `maxResultChars` 限制;子模型 token 与父级保留的上下文相互独立。
|
||||
调用 token 可能很多,并会保留到压缩(compaction)为止。结果渲染受 `maxResultChars` 限制;子模型 token 与父级保留的上下文相互独立。
|
||||
|
||||
#### KV Cache 影响
|
||||
|
||||
仅追加;新增可见内容位于可复用请求前缀之后,不会使现有 KV-cache 条目失效。
|
||||
|
||||
## 已知限制与延期工作
|
||||
## 已知限制与暂缓事项
|
||||
|
||||
- **父级轮次会阻塞到整个工作流结算**:没有后台启动/轮询接口,取消会把局部输出作为错误丢弃。
|
||||
- **`args` 必须是对象,Native 结果文本有界**:调用方把顶层数组/标量包装到字段中;规范工作流结果保持完整,超过 `maxResultChars` 的 JSON 会在面向模型的投影中截断,而不是存入检索句柄之后。
|
||||
- **每次工具注册的工作流政策固定**:提供方选择、上限和工具名称属于部署配置,不是模型调用参数。
|
||||
- **`args` 必须是对象,Native 结果文本有界**:调用方把顶层数组/标量包装到字段中;规范工作流结果保持完整,超过 `maxResultChars` 的 JSON 会在面向模型的投影中截断,而不是存储在检索句柄背后。
|
||||
- **每次工具注册的工作流策略固定**:提供方选择、上限和工具名称属于部署配置,不是模型调用参数。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write
|
||||
# pnpm run verify-translation-pairing --write packages/workflow/workflow-workerthread/README.md
|
||||
README.md: 9da420bdd67ac5b0a4bfffa312c6fe7b2dabf8f5
|
||||
README.zh.md: e280562b95e9dbebb243aad0f765771f4737b312
|
||||
README.zh.md: cc2ed2043db7f6a51088930c07c5cf5a2223f480
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
[English](README.md) | 中文
|
||||
|
||||
本包为 `WorkflowService` 提供实现,每次运行使用一个 Node worker thread。worker 执行编排脚本;子 agent(智能体)留在宿主上,通过带类型的宿主/worker 协议访问 `ctx.subagents`。
|
||||
本包(package)为 `WorkflowService` 提供实现,每次运行使用一个 Node worker thread。worker 执行编排脚本;子 agent(智能体)留在宿主上,脚本通过带类型的宿主/worker 协议经由 `ctx.subagents` 访问它们。
|
||||
|
||||
包根目录默认导出引擎插件及其 `Config`;worker 协议、运行时和会话模块均为实现私有。操作入口 `./worker` 仍是引擎的派生目标。
|
||||
包根目录默认导出引擎插件及其 `Config`;worker 协议、运行时和会话模块均为实现私有。操作入口 `./worker` 仍是引擎的 spawn 目标。
|
||||
|
||||
这种拆分只有一个主要目的:同步脚本循环不能阻塞 harness 事件循环,忽略取消的脚本可以连同其 worker 一起终止。它不是安全沙箱。
|
||||
|
||||
@@ -16,14 +16,14 @@ worker 仍提供实用的隔离:
|
||||
|
||||
- 脚本 CPU 工作和同步自旋不会占用宿主事件循环;
|
||||
- `worker.terminate()` 为 dispose(资源释放)提供真实的最终停止手段;
|
||||
- 除未构建 loader 的管道变量外,worker 以空环境启动,因此环境凭据不会通过 `process.env` 跨越边界;
|
||||
- 除未构建 loader 所需的衔接配置外,worker 以空环境启动,因此环境凭据不会通过 `process.env` 跨越边界;
|
||||
- 宿主/worker 消息使用结构化克隆数据,并在脚本边界执行普通 JSON 校验。
|
||||
|
||||
真正的不可信脚本沙箱需要在同一 workflow seam 后采用不同引擎。
|
||||
真正的不可信脚本沙箱需要在同一工作流 seam 背后采用不同引擎。
|
||||
|
||||
## 脚本契约
|
||||
|
||||
工作流的 `meta` 是宿主提供的数据,而不是待求值的脚本文本。引擎会校验必需的 `name` 和 `description`、拒绝未知字段,并在返回运行前检查函数体能否解析。
|
||||
工作流的 `meta` 是宿主提供的数据,而不是待求值的脚本文本。引擎会校验必需的 `name` 和 `description`、拒绝未知字段,并在返回运行前检查脚本正文能否解析。
|
||||
|
||||
在 worker 内,脚本会收到 `args` 以及以下钩子:
|
||||
|
||||
@@ -32,16 +32,16 @@ worker 仍提供实用的隔离:
|
||||
- `pipeline(items, ...stages)` 在没有跨阶段屏障的情况下传递 `(previous, item, index)`;
|
||||
- `phase(title)` 和 `log(message)` 发出观察器叙述。
|
||||
|
||||
未知选项、格式错误的参数、不支持的 schema、触发的上限、提供方启动失败和基础设施结果失败都属于致命工作流错误。有意不注入 timer、文件系统 API 或 Node 全局变量,但上述信任注意事项仍然适用。
|
||||
未知选项、格式错误的参数、不支持的 schema、超出上限、提供方启动失败和基础设施结果失败都属于致命工作流错误。有意不注入 timer、文件系统 API 或 Node 全局变量,但上述信任注意事项仍然适用。
|
||||
|
||||
## 运行顺序
|
||||
|
||||
`start()` 会校验 meta、解析函数体、解析一个已注册且规范化的提供方路由,并解析每次运行的子 agent 总数上限,然后才创建 worker 或发布 `workflow/start`。请求的 `maxTotalAgents` 必须是正安全整数,且不能超过引擎配置的部署上限。源代码模式通过 data URL bootstrap 安装 TypeScript 转换;构建模式把同级 `lib/worker.cjs` 作为文件系统路径传入,因为 pkg 的 VFS 钩子要求 CommonJS。两者都能在普通 Node 下运行。ready/go 握手可以避免启动信号取消与 worker 启动发生竞态,导致脚本最初的同步片段被执行。
|
||||
`start()` 会校验 meta、解析脚本正文、解析一个已注册且规范化的提供方路由,并解析每次运行的子 agent 总数上限,然后才创建 worker 或发布 `workflow/start`。请求的 `maxTotalAgents` 必须是正安全整数,且不能超过引擎配置的部署上限。源代码模式通过 data URL bootstrap 安装 TypeScript 转换;构建模式把同级 `lib/worker.cjs` 作为文件系统路径传入,因为 pkg 的虚拟文件系统(VFS)钩子要求 CommonJS。两者都能在普通 Node 下运行。ready/go 握手可以避免启动信号取消与 worker 启动发生竞态,导致脚本最初的同步片段被执行。
|
||||
|
||||
对于每次 `agent()` 调用:
|
||||
|
||||
1. worker 发送 `child-start`,其中包含普通数据提示词和选项。
|
||||
2. 宿主通过异步 `SubagentService.start` 调用启动请求中的提供方覆盖值,否则调用已配置提供方;调用会传入工作流父级和每次运行唯一的规范中止信号。提供方选择应用于该次运行的每个子 agent,对脚本不可见。
|
||||
2. 宿主通过异步 `SubagentService.start` 调用启动请求中指定的提供方,否则调用已配置的提供方;调用会传入工作流父级和该次运行共用的唯一中止信号。提供方选择应用于该次运行的每个子 agent,对脚本不可见。
|
||||
3. 如果启动被拒绝,宿主会发送 `child-start-error`;提供方启动已经完全停稳,不会发出子 agent 生命周期事件。
|
||||
4. 如果启动兑现时工作流仍接纳工作,宿主会记录该运行、观察 `result`,然后发送 `child-started`。即使结果已经结算,也只会随后转发,以保持先启动、后结果的顺序。
|
||||
5. worker 发出成对的 `workflow/agent-start` 和 `workflow/agent-end` 叙述,并在收集后请求 dispose 子 agent。
|
||||
@@ -56,9 +56,9 @@ worker 仍提供实用的隔离:
|
||||
|
||||
## 取消与 dispose
|
||||
|
||||
`WorkflowRun.cancel()` 会记录第一个原因、通知 worker 取消、中止每个待处理及已发布子 agent 共享的唯一信号,并启动 `disposeGraceMs` timer。worker 钩子会在下次 await 时抛出 `CANCELLED`。如果运行到期限仍未结算,宿主会将其以已取消状态兑现、为悬空的子 agent 生命周期事件配对,并终止 worker。
|
||||
`WorkflowRun.cancel()` 会记录第一个原因、通知 worker 取消、中止每个待处理及已发布子 agent 共享的唯一信号,并启动 `disposeGraceMs` 定时器。worker 钩子会在下次 await 时抛出 `CANCELLED`。如果运行到期限仍未结算,宿主会将其以已取消状态兑现、为悬空的子 agent 生命周期事件配对,并终止 worker。
|
||||
|
||||
subagent seam 只有一个取消通道:请求信号。不存在单独的子 agent 取消 RPC。已发布子 agent 使用 `run.dispose()` 清理;待处理提供方启动在其 promise 拒绝或兑现前仍由提供方拥有。
|
||||
subagent seam 只有一个取消通道:请求信号。不存在单独的子 agent 取消 RPC。已发布子 agent 使用 `run.dispose()` 清理;待处理的提供方启动在其 promise 拒绝或兑现前仍由提供方负责。
|
||||
|
||||
正常结算也会中止待处理启动,并在结果对外结算前开始 dispose 所有已发布但无需等待的子 agent。宿主的完全停稳条件同时包括待处理启动和已发布子 agent 的 dispose,因此清理不会遗漏异步启动事务。
|
||||
|
||||
@@ -66,9 +66,9 @@ subagent seam 只有一个取消通道:请求信号。不存在单独的子 ag
|
||||
|
||||
## 结果与事件保证
|
||||
|
||||
在宿主主张点,终态结果遵循先到者胜。已接受的外部取消会覆盖后到的非取消 worker 结果;先完成主张的结果或 worker 死亡不能被可重入清理回调改写。
|
||||
在宿主的结果确认点,终态结果遵循先到者胜。已接受的外部取消会覆盖后到的非取消 worker 结果;先完成确认的结果或 worker 死亡不能被可重入清理回调改写。
|
||||
|
||||
worker 错误、消息失败或提前退出会在清理前关闭消息接纳,然后以 `error` 兑现;如果取消已经拥有该运行,则不覆盖取消。后到的排队消息无法在该逻辑边界后创建子 agent 或发出叙述。
|
||||
worker 错误、消息失败或提前退出会在清理前关闭消息接纳,然后以 `error` 兑现;如果取消已经接管该运行,则不覆盖取消。后到的排队消息无法在该逻辑边界后创建子 agent 或发出叙述。
|
||||
|
||||
宿主会维护已转发子 agent 启动的台账。优雅退出的 worker 会提供对应的结束事件;死亡或强制终止会把缺失的结束事件合成为已取消。因此,每个已转发的 `workflow/agent-start` 都会且只会配对一次,不过已经到达的工作流结果之后的清理可能稍后才完成。
|
||||
|
||||
@@ -83,7 +83,7 @@ worker 错误、消息失败或提前退出会在清理前关闭消息接纳,
|
||||
| `syncTimeoutMs` | `5000` | 脚本最初同步片段的 VM 超时时间。 |
|
||||
| `disposeGraceMs` | `5000` | 强制结算/终止之前的期限,也是公开 dispose 的期限。 |
|
||||
|
||||
所属消费方可以为一次运行设置 `WorkflowStartRequest.subagentProvider` 和 `WorkflowStartRequest.maxTotalAgents`。它们属于引擎级政策,不是脚本钩子或面向模型的选项;普通 `workflow` 工具不会设置两者。每次运行的子 agent 总数上限可以降低、但绝不能提高已配置的 `maxTotalAgents` 上限。
|
||||
负责该引擎的消费方可以为一次运行设置 `WorkflowStartRequest.subagentProvider` 和 `WorkflowStartRequest.maxTotalAgents`。它们属于引擎级策略,不是脚本钩子或面向模型的选项;普通 `workflow` 工具不会设置两者。每次运行的子 agent 总数上限可以降低、但绝不能提高已配置的 `maxTotalAgents` 上限。
|
||||
|
||||
## 模型体验
|
||||
|
||||
@@ -91,7 +91,7 @@ worker 错误、消息失败或提前退出会在清理前关闭消息接纳,
|
||||
|
||||
#### 模型看到的内容
|
||||
|
||||
脚本每次调用 `agent()`,都会把提示词逐字发送给 subagent 提供方,并附带可选模型或结构化输出 schema。每个子 agent 看到该提供方自己的上下文;phase 和 log 叙述只留在观察器事件中。
|
||||
脚本每次调用 `agent()`,都会把提示词原样发送给 subagent 提供方,并附带可选模型或结构化输出 schema。每个子 agent 看到该提供方自己的上下文;phase 和 log 叙述只留在观察器事件中。
|
||||
|
||||
#### Token 影响
|
||||
|
||||
@@ -109,16 +109,16 @@ worker 错误、消息失败或提前退出会在清理前关闭消息接纳,
|
||||
|
||||
#### Token 影响
|
||||
|
||||
本引擎不会直接向父级添加 token。最终结果大小由工具消费方限制,并保留到上下文压缩(compaction)为止。
|
||||
本引擎不会直接向父级添加 token。最终结果大小由工具消费方限制,并保留到压缩(compaction)为止。
|
||||
|
||||
#### KV Cache 影响
|
||||
|
||||
仅追加;新增可见内容位于可复用请求前缀之后,不会使现有 KV-cache 条目失效。
|
||||
|
||||
## 已知限制与延期工作
|
||||
## 已知限制与暂缓事项
|
||||
|
||||
- **worker/vm 不是安全边界**:模型编写的代码可以逃逸 `node:vm` 并取得 worker 的进程权限;不可信代码部署需要独立进程或容器引擎。
|
||||
- **每次运行都要支付一个 worker thread 的成本**:没有池、预热运行时或跨运行脚本缓存。
|
||||
- **不注入环境 timer、文件系统或网络,但逃逸代码仍可访问 Node**:缺失的全局变量用于保证 API 可移植性,而非隔离。
|
||||
- **终止只能报告宿主观察到的启动**:`agentsStarted` 不包括仍在 worker 侧排队等待并发、且在强制终止后无法得知的调用。
|
||||
- **不注入默认可用的定时器、文件系统或网络,但逃逸代码仍可访问 Node**:这些缺失的全局变量属于可移植性 API 设计,而非隔离措施。
|
||||
- **终止只能报告宿主观察到的启动**:`agentsStarted` 不包括因并发限制仍在 worker 侧排队、且在强制终止后无法得知的调用。
|
||||
- **跨 realm 错误在脚本内无法通过 `instanceof Error`**:工作流作者必须根据 `name` 和 `code` 等稳定字段分支。
|
||||
|
||||
@@ -548,7 +548,7 @@ describe('dsh-workflow-workerthread', () => {
|
||||
// The rejection VALUE's own coercion throws: a warn built with bare
|
||||
// String(error) would itself throw, skipping the ChildDisposed ack
|
||||
// and wedging the script's finally until the grace/terminate path.
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors -- the non-Error rejection IS the scenario under test
|
||||
// oxlint-disable-next-line typescript/prefer-promise-reject-errors -- the non-Error rejection IS the scenario under test
|
||||
dispose: () => Promise.reject({ toString: () => { throw new Error('coercion trap') } }),
|
||||
}),
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write
|
||||
# pnpm run verify-translation-pairing --write packages/workflow/workflow/README.md
|
||||
README.md: 9d01cfd3d2504d6b5af5a1af6cba735a4abb5795
|
||||
README.zh.md: 85dc6bb1897678f72eb715514ab85cad7f7b9589
|
||||
README.zh.md: 20dde758d82b38658c39157f9d72d1f3fba046a9
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
|
||||
[English](README.md) | 中文
|
||||
|
||||
workflow seam(`ctx.workflows`)执行由模型编写、可扇出 subagent 的编排脚本。该 seam 定义脚本、运行、结果、错误和事件契约;引擎负责决定如何隔离并执行脚本。
|
||||
工作流 seam(`ctx.workflows`)执行由模型编写、可扇出 subagent 的编排脚本。该 seam 定义脚本、运行、结果、错误和事件契约;引擎负责决定如何隔离并执行脚本。
|
||||
|
||||
`@deepseek-ai/dsh-workflow-workerthread` 是当前引擎,`@deepseek-ai/dsh-tool-workflow` 是面向模型的消费方。未来的进程或沙箱引擎可以替换实现,而无需更改工具。
|
||||
|
||||
## 服务与运行契约
|
||||
|
||||
`WorkflowService.start(request): WorkflowRun` 会同步完成足够多的校验,在运行存在前拒绝格式错误的 meta 块、无法解析的脚本、不可用的提供方路由或不受支持的单次运行限制。返回后,`WorkflowRun.result` 绝不拒绝:执行失败以 `stopReason: 'error'` 兑现,取消则在引擎有限的宽限时间内以 `cancelled` 兑现。
|
||||
`WorkflowService.start(request): WorkflowRun` 会同步完成足够多的校验,在运行创建前拒绝格式错误的 meta 块、无法解析的脚本、不可用的提供方路由或不受支持的单次运行限制。返回后,`WorkflowRun.result` 绝不拒绝:执行失败以 `stopReason: 'error'` 兑现,取消则在引擎有限的宽限时间内以 `cancelled` 兑现。
|
||||
|
||||
运行由持有方拥有。引擎插件卸载会阻止新的启动,但不会撤销已接受的运行。持有方必须在每条路径上调用 `dispose()`;dispose 会取消剩余工作,并在文档规定的期限内达到或放弃完全停稳。
|
||||
运行由持有方负责。引擎插件卸载会阻止新的启动,但不会撤销已接受的运行。持有方必须在每条路径上调用 `dispose()`;dispose(资源释放)会取消剩余工作,并在文档规定的期限内达到或放弃完全停稳。
|
||||
|
||||
`WorkflowStartRequest` 包含 `{ meta, script, args?, subagentProvider?, maxTotalAgents?, parent, signal? }`。`parent` 把每个子 agent(智能体)归属于调用 agent。`subagentProvider` 可以为该次运行的所有子 agent 指定路由,同时不向脚本公开提供方选择;省略时使用引擎配置的提供方。`maxTotalAgents` 可以为一次运行降低引擎的部署上限,同样对脚本不可见。实现会同步拒绝无效路由和限制。`meta` 与 `args` 是普通数据,不是脚本片段。
|
||||
|
||||
@@ -18,13 +18,13 @@ workflow seam(`ctx.workflows`)执行由模型编写、可扇出 subagent 的
|
||||
|
||||
## 事件
|
||||
|
||||
工作流事件只供观察。它们携带 `WorkflowRunInfo`(`id` 加 `meta`),而不是实时运行,因此监听器无法取得取消或 dispose(资源释放)权限。
|
||||
工作流事件只供观察。它们携带 `WorkflowRunInfo`(`id` 加 `meta`),而不是活动运行,因此监听器无法取得取消或 dispose 权限。
|
||||
|
||||
- `workflow/start` / `workflow/end` 为运行配对;
|
||||
- `workflow/phase` 和 `workflow/log` 公开脚本叙述;
|
||||
- `workflow/agent-start` / `workflow/agent-end` 按 `seq` 为每次子 agent 调用配对;异步提供方启动被拒绝的子 agent 不会发出其中任何一个事件。
|
||||
- `workflow/agent-start` / `workflow/agent-end` 按 `seq` 为每次子 agent 调用配对;提供方的异步启动调用被拒绝时,该子 agent 不会发出其中任何一个事件。
|
||||
|
||||
同进程事件 payload 是以不可变方式借用的值。每个监听器都独立隔离:同步抛出或返回的 promise 被拒绝时,只会记录日志,不会阻塞同级监听器或改变执行。
|
||||
同进程事件 payload 是以不可变方式借用的值。每个监听器都独立隔离:同步抛出异常或返回的 promise 被拒绝时,只会记录日志,不会阻塞同级监听器或改变执行。
|
||||
|
||||
## 失败纪律
|
||||
|
||||
@@ -33,10 +33,10 @@ workflow seam(`ctx.workflows`)执行由模型编写、可扇出 subagent 的
|
||||
- `SCRIPT_PARSE` / `META_INVALID`:工作流无法启动;
|
||||
- `INVALID_ARGUMENT` / `UNSUPPORTED_OPTION` / `UNSUPPORTED_SCHEMA`:钩子调用违反引擎契约;
|
||||
- `AGENT_CAP` / `ITEM_CAP`:超过已配置的安全上限;
|
||||
- `AGENT_START`:提供方异步启动被拒绝;
|
||||
- `AGENT_START`:提供方的异步启动调用被拒绝;
|
||||
- `AGENT_RESULT`:已就绪子 agent 的结果因基础设施故障而拒绝;
|
||||
- `RESULT_UNSERIALIZABLE`:脚本/worker 值不是普通 JSON 数据;
|
||||
- `CANCELLED`:取消拥有该运行,待处理和未来的钩子都会拒绝。
|
||||
- `CANCELLED`:取消会接管该运行,待处理和未来的钩子都会拒绝。
|
||||
|
||||
子 agent 若以非完成的结束原因正常兑现,并不属于基础设施异常:`agent()` 返回 `null`,使脚本可以处理普通的子 agent 失败。
|
||||
|
||||
@@ -46,14 +46,14 @@ workflow seam(`ctx.workflows`)执行由模型编写、可扇出 subagent 的
|
||||
|
||||
#### KV Cache 影响
|
||||
|
||||
不会直接使缓存失效;具名消费方负责请求前缀的任何变化。
|
||||
不会直接导致 KV Cache 失效;请求前缀的任何变化均由上述消费方负责。
|
||||
|
||||
## 已知限制与延期工作
|
||||
## 已知限制与暂缓事项
|
||||
|
||||
- **仅支持前台收集**:调用方拥有一个实时运行并等待它;后台启动/轮询、spill 句柄和分离收集均延期处理。
|
||||
- **没有日志记录或恢复**:脚本、子 agent 进度和中间值均不设检查点,因此进程重启后无法继续运行。
|
||||
- **仅支持前台收集**:调用方负责一个活动运行并等待它;后台启动/轮询、spill 句柄和分离收集均暂缓处理。
|
||||
- **没有日志化或恢复**:脚本、子 agent 进度和中间值均不设检查点,因此进程重启后无法继续运行。
|
||||
- **没有已保存或嵌套工作流**:该 seam 只启动调用方提供的脚本,工作流脚本不会收到用于递归编排的 `workflow()` 钩子。
|
||||
- **没有 token 预算词汇**:引擎会限制并发、条目和子 agent,但请求与结果都不会统计跨子 agent 的模型 token。
|
||||
- **运行由持有方拥有,不由服务跟踪**:卸载引擎不会发现独立的实时句柄;每个消费方都必须 dispose 自己启动的运行。
|
||||
- **运行由持有方负责,不由服务跟踪**:卸载引擎不会发现独立的活动句柄;每个消费方都必须 dispose 自己启动的运行。
|
||||
|
||||
延期的工作流接口见[动态工作流 Agent Note](../../../.agents/notes/implemented/feature/2026-07-05-dynamic-workflows.md)。
|
||||
暂缓实现的工作流接口见[动态工作流 Agent Note(agent 决策记录)](../../../.agents/notes/implemented/feature/2026-07-05-dynamic-workflows.md)。
|
||||
|
||||
@@ -75,7 +75,7 @@ describe('dsh-workflow (interface)', () => {
|
||||
const warn = vi.spyOn(ctx.logger, 'warn').mockImplementation(() => ctx.logger)
|
||||
const seen: string[] = []
|
||||
// Runtime listeners may return thenables even though the declaration's observable result is void.
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises -- exercises rejected-listener containment
|
||||
// oxlint-disable-next-line typescript/no-misused-promises -- exercises rejected-listener containment
|
||||
ctx.on('workflow/agent-start', async () => { throw new Error('async observer failed') })
|
||||
ctx.on('workflow/agent-start', (_info, agent) => { seen.push(agent.label) })
|
||||
const engine = ctx.workflows as StubEngine
|
||||
|
||||
Reference in New Issue
Block a user