dsh-plugin-agentflow 0.3.1 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # AgentFlow · DSH 插件
2
2
 
3
- > 在 DeepSeek Harness 里用**画布**编排 agent 工作流,一键生成 DSH 预设 —— 每一步由你定义,而不是塞进一个黑盒循环。
3
+ > 在 DeepSeek Harness 里用**画布**编排 agent 工作流,一键生成 `/命令` —— 每一步由你定义,而不是塞进一个黑盒循环。
4
4
 
5
5
  [![npm](https://img.shields.io/npm/v/dsh-plugin-agentflow?color=24543F&label=npm)](https://www.npmjs.com/package/dsh-plugin-agentflow)
6
6
  [![license](https://img.shields.io/badge/license-MIT-24543F)](./LICENSE)
@@ -12,12 +12,12 @@
12
12
 
13
13
  DSH 自带的 agent 是「一个模型 + 一堆工具」的循环,中间怎么想、调什么,你看不见也改不了。
14
14
 
15
- AgentFlow 把它掰开:**画布上的每张卡片就是一个 agent 步骤**。你决定有几步、谁先谁后、谁并行、每步用哪个模型、能不能调 MCP 工具、做完怎么验收。编排好之后导出成一个 DSH 预设,在对话里直接选中它。
15
+ AgentFlow 把它掰开:**画布上的每张卡片就是一个 agent 步骤**。你决定有几步、谁先谁后、谁并行、每步用哪个模型、能不能调 MCP 工具、做完怎么验收。编排好之后把整张画布生成成一个 DSH 命令,在对话里输入 `/命令名` 就能用。
16
16
 
17
17
  - **节点**:LLM(调模型)/ MCP(直接调工具,不经过模型)/ 调度(根,对话入口)
18
18
  - **执行**:纯 DAG,无上游先跑、同层并行、失败不阻塞其它分支
19
19
  - **模型**:全部走 DSH 自己的模型通道(`ctx.llm`),推理强度和兜底模型可逐节点配
20
- - **预设**:根节点承载人格,导出时自动附上子节点清单,写进 DSH profile
20
+ - **命令**:整张画布生成成 `/命令名`,插件热注册、即时生效,不写 DSH profile
21
21
 
22
22
  ## 安装
23
23
 
@@ -45,36 +45,92 @@ CC19990113/dsh-plugin-agentflow
45
45
 
46
46
  ![画布列表](docs/images/boards-list.png)
47
47
 
48
- 每张画布对应一个 DSH 预设。卡片上会标出「对话入口」(哪张卡是和用户对话的根节点),改过画布但没更新预设时显示 **预设未更新**。
48
+ 每张画布可以生成一个 DSH 命令。卡片上会标出「对话入口」(哪张卡是和用户对话的根节点)。
49
49
 
50
50
  ### 2. 编排
51
51
 
52
52
  - 拖卡片改位置,从卡片边缘拖线连上下游
53
53
  - 点卡片改模型、推理强度、兜底模型、技能、MCP 工具
54
54
  - 「加下游 / 加上游」快速派生新卡
55
- - 编辑**自动保存**,点「保存」才会把你的编排**生成/覆盖成 DSH 预设**
55
+ - 编辑**自动保存**,点「保存」把编排落库;点「生成命令」把画布变成 `/命令`
56
56
 
57
57
  ### 3. 验证
58
58
 
59
59
  顶栏「验证 AgentFlow」会真实跑一遍整张图(会消耗 token),用来看流程通不通。卡片上只显示配置摘要,不堆运行产出。
60
60
 
61
- ### 4. 导出成预设
61
+ ### 4. 生成命令
62
62
 
63
- 点「保存」(或 ⌘S)→ 画布落库 + 预设写入 `~/.dsh/profiles/<profile>/cordis.patch.yml`。重启 DSH 后,新会话的预设列表里就能选到它。
63
+ 点画布上的「生成命令」(或插件页 → 添加插件 →「从 AgentFlow 画布生成命令」,或设置页 AgentFlow 分区),得到 `/agentflow-<slug>`:
64
64
 
65
- 预设长这样:
65
+ - **生成即生效**:命令由插件热注册,无需重启 DSH,也不写 `~/.dsh/profiles/<profile>/cordis.patch.yml`
66
+ - **命令名**:默认 `agentflow-<画布 slug>`;同名会覆盖
67
+ - **落盘**:`<数据目录>/commands/<name>.json`,只存画布 id
68
+ - **跟当前画布**:画布内联打开哪张,设置页/菜单就生成哪张(画布会通过 `agentflow-board-active` 事件回传当前 id);没有回传时才退回索引第一张
66
69
 
70
+ 在对话输入框里:
71
+
72
+ | 输入 | 行为 |
73
+ |---|---|
74
+ | `/agentflow-<slug>` | 只看画布:返回画布名、节点列表(label / 任务 / 上游)与用法,不消耗 token |
75
+ | `/agentflow-<slug> 要做的事` | 进入该画布的工作流:你的话作为**入口节点(调度卡)的输入**,之后对话被画布 DAG 推着走 |
76
+ | `/agentflow-<slug> off` | 退出该画布的工作流 |
77
+
78
+ ### 5. 输入命令之后,对话怎么走
79
+
80
+ 不是「把画布说明塞给模型让它自觉」,而是插件按 DAG 推进:
81
+
82
+ 1. 你的话进**根节点**(调度卡;优先认引擎的 `type: 'orchestrator'`),模型只做这个节点,做完必须调 `agentflow_node_done(node_id, output)`
83
+ 2. 插件校验(非空、≥10 字、前置已完成、只有发起这次工作流的 agent 能标记),通过后算下一层
84
+ 3. 这一层有几个节点,就**强制**为每个节点建一个 teammate:
85
+ - teammate 名由插件分配:`<节点id清洗>-<本次 run 的随机后缀>`。Agent Teams 的 roster 名字**永不复用**(实测连 DSH 重启后都还记着),所以每次 run 都换后缀;同一 run 内清洗撞名再加 `-2`
86
+ - `tools/pre-execute` 只放行这一套名字(报错里列出当前能派谁)
87
+ - 派发指令里带每个节点的任务、验收标准、上游产出摘要
88
+ 4. **非根节点必须先真的 spawn 过才能标完成**(`tools/post-execute` 观察到成功的 `spawn_teammate` 才算),所以模型没法自己把下游做完
89
+ 5. 每个 teammate 回报后,用它的实际产出去调 `agentflow_node_done`;一层全部完成才进下一层
90
+ 6. 全部完成 → 收束并给产出清单;连线成环 → 判定卡住并提示
91
+
92
+ 画布上给节点配的**模型 / 推理档 / 技能 / 工具**会写进派发指令(`spawn_teammate` 没法指定模型,所以是「按这套配置干活」的提示,不是强制);`type: mcp` 的节点在命令模式暂不执行,指令末尾会明确列出「本次不执行:…」,不会静默吞掉。
93
+
94
+ 每一步都会注入一行进行态(已完成 / 已派发待回报 / 待派发)。模型想收工但还没派发时,`turn-stopping` 会在同一回合先催一次(同一个 turn 不重复刷),超过 120s 再补提醒。
95
+
96
+ 同一张画布**已有进行中的工作流**时,再输入 `/命令 文本` 会被拒绝(提示先 `off` 或等结束),不会静默覆盖。
97
+
98
+ 工作流状态会落到 `<数据目录>/runs/<会话>.json`:DSH 重启后能接着跑(恢复时会换新的 teammate 名重新派发,因为旧 teammate 已经不在)。`/命令 off` 除了退出,还会**中断已经派出去的 teammate**(需要装 Agent Teams;服务不在时只退出不中断)。
99
+
100
+ teammate 名撞车(概率极低)会自动换名重试一次,并提示模型用新名字重新 spawn。
101
+
102
+ 指令有预算:规则永远先占位,用户输入/节点详情按剩余额度截断并提示「已截断」。没有任务(或只有空白)的占位节点自动跳过;整张画布没有可执行节点时直接判结束,不会留一个永远不动的 run。
103
+
104
+ > 前提:Agent Teams(`spawn_teammate` 等工具)要在 profile 里;没装时命令仍可用,只是派发那一层会停在对话里,由你手动推进。
105
+
106
+ ### 6. 想跑多层 / 多节点:先调 teammate 名额
107
+
108
+ 画布上**每个节点都会建一个 teammate**,所以节点多、层数深的画布会很快撞上限。默认上限不是代码里的 16,而是被 profile bundle 压到 **8**:
109
+
110
+ | 层 | 值 | 位置 |
111
+ |---|---|---|
112
+ | 主包默认 | `DEFAULT_MAX_MEMBERS = 16` | `@deepseek-ai/dsh-experimental-agent-team/lib/index.js` |
113
+ | bundle 覆写 | `maxMembers: 8` | `@deepseek-ai/dsh-experimental-agent-team-profile/cordis.patch.yml` |
114
+ | desktop profile | bundles 里挂了这个 bundle | `~/.dsh/profiles/desktop/package.json` |
115
+
116
+ 要开大,在**自己的 profile** `cordis.patch.yml` 末尾追加同 id 覆写(`<profile>` 换成你的,桌面版是 `desktop`):
117
+
118
+ ```yaml
119
+ - id: agent-team
120
+ config:
121
+ maxMembers: 32
122
+ maxTasks: 256
123
+ maxMessageBytes: 65536
124
+ disposalTimeoutMs: 5000
67
125
  ```
68
- 你是「AgentFlow」的对话入口。
69
- 你只负责和用户对话:问清需求、确认关键信息。
70
- 需求明确后,严格按照后续节点的 AgentFlow 流去执行,不要自己直接产出结果。
71
-
72
- 【完成标准】…
73
- 【子节点】共 4 个
74
- - 大纲:根据给定的题材,写一个三幕剧的简要大纲
75
- - 对白版:为关键场景写几句有张力的对白
76
- …
77
- ```
126
+
127
+ 三个坑:
128
+
129
+ 1. **config 是整体替换、不深合并**(`dsh-app-boot` 原文 "config is replaced wholesale, not deep-merged")。只写 `maxMembers` 会把其余键打回 schema 默认,所以四个键必须写全(后三个与 bundle 原值一致)。
130
+ 2. **必须重启 DSH**:`TeamService` 在构造时读 config,热改不生效。
131
+ 3. **注意内存**:每个 teammate 都是独立 agent(自己的会话与上下文),十几个同时活着时内存和 token 都明显上涨;32 已接近 24 GB 机器的实际承受力。另外名额**用完不回收**——Agent Teams 的成员名永不复用,所以一次 App 会话里能建的 teammate 总数是有上限的,宽画布建议拆小或分层跑。
132
+
133
+ 画布删掉时对应命令文件会被一起清掉(不再注册也不会留死文件);配置 `commands: false` 可关掉注册(只生成不挂命令)。历史遗留的预设不再生成,插件设置页只留「旧预设清理」用于删除 profile 里的旧行。
78
134
 
79
135
  ## 深色模式
80
136
 
@@ -107,16 +163,34 @@ DSH 桌面
107
163
  |---|---|---|
108
164
  | 数据目录 | `~/.dsh/agent-flow-data` | 首次启动会自动从 `~/.dsh/agent-flow/data` 迁移 |
109
165
  | 后端端口 | `8791` | 可用 `agentflowPort` 插件配置改 |
110
- | 预设 profile | `desktop` | 用环境变量 `AGENTFLOW_PROFILE` 指定别的 profile |
166
+ | 旧预设 profile | `desktop` | 读/删历史预设(`/api/presets`)用;可用环境变量 `AGENTFLOW_PROFILE` 指定别的 profile |
167
+ | `commands` | `true` | 是否把 `<数据目录>/commands/*.json` 注册成对话命令;关闭则只生成不注册 |
111
168
  | `agentflowRenderMode` | `native` | 画布渲染方式:`native` 把画布编进客户端 bundle 直接渲染;`iframe` 回滚到嵌入独立画布页面 |
112
169
  | `agentflowUrl` | `http://127.0.0.1:8791` | `native` 下作为 API 基地址;`iframe` 下作为画布页面地址 |
170
+ | `observe` | `true` | 观察器:只记每一步的消息形状与每次工具调用,不拦不改 |
171
+ | `observeDir` | `<数据目录>/observe` | 观察日志落盘目录 |
172
+
173
+ ## 观察器
174
+
175
+ `observe` 打开时,插件挂在 `agent/pre-step`、`tools/post-execute`、`agent/turn-stopping` 三个钩子上,
176
+ 把每个 agent 的一次对话记成 `<observeDir>/<session>.jsonl`,一行一个事件(工具耗时取自 `tools/execute` 的真实执行时间):
177
+
178
+ | `t` | 字段 | 说明 |
179
+ |---|---|---|
180
+ | `step` | `turn` `step` `msgs[]` `decision{}` | 这一步进了哪些消息(只记 `source.kind` / `form` / 字符数,不存正文)、最终决策是 `enter` 还是 `reject` |
181
+ | `tool` | `turn` `step` `name` `args` `ms` `isError` `text` | 每一次工具调用;被 `pre-execute` deny 的也会出现,`isError` 为真 |
182
+ | `stop` | `turn` | 模型认为自己干完了(`turn-stopping`)的地方 |
183
+
184
+ 用途:在做「强制工作流」之前,先看清模型实际怎么调工具、在哪收工、被拒之后怎么反应。
185
+ 纪律:写盘 fire-and-forget,任何异常只 `warn`——观察器坏了不能连累对话。
113
186
 
114
187
  ## 限制
115
188
 
116
189
  - **只在 macOS 上验证过**,Windows 的路径与命令未测
190
+ - teammate 名额默认 8(bundle 覆写),宽画布先按 §6 调大;名额用完不回收,重启 DSH 才重置
117
191
  - 运行态是全局的:跑图时切换画布,事件会被丢弃;「停止」不分画布
118
192
  - 验证跑图会真实调用模型,注意 token 消耗
119
- - 预设依赖 DSH profile 的 patch 机制,profile 结构变了要跟着调整
193
+ - 命令只注册已存在的画布:画布删了,对应命令下次扫描时自动消失
120
194
  - 宿主客户端运行时是 **React 18**;画布源码来自 React 19,但只使用两者共有的 API(不用 19-only API)
121
195
  - 画布样式靠 `.af-canvas` 作用域隔离(Tailwind 的 `:root` 变量收进容器内),不污染宿主页面
122
196
  - **`npm run sync` 会清空重建 `src/canvas/`**:为内联渲染做的适配改动会被引擎源码覆盖,改完需要重做
@@ -127,10 +201,11 @@ DSH 桌面
127
201
  npm run build # 构建客户端 bundle(lib/client.js,含画布)
128
202
  npm run sync # 从 ~/.dsh/agent-flow 同步引擎到 runtime/,并把画布源码同步到 src/canvas/
129
203
  npm run check # 语法检查 + tsc --noEmit
130
- npm test # 插件自测(152 项:bridge 83 + client bundle 69)
204
+ npm test # 插件自测(479 项:bridge 91 + client 86 + observe 28 + commands 150 + flow 124)
205
+ npm run observe # 把观察日志读成时间线(默认 ~/.dsh/agent-flow-data/observe)
131
206
  ```
132
207
 
133
- 引擎侧测试在 `~/.dsh/agent-flow/test`(85 项)。
208
+ 引擎侧测试在 `~/.dsh/agent-flow/test`(72 项)。
134
209
 
135
210
  ## 发布
136
211
 
package/cordis.patch.yml CHANGED
@@ -6,190 +6,3 @@
6
6
  - insert:
7
7
  - id: agentflow
8
8
  name: 'dsh-plugin-agentflow'
9
-
10
- # >>> AgentFlow 预设(自动生成,重新导出会整体替换这段)
11
- # 本文件由 AgentFlow 自动生成,请勿手改 —— 重新生成会整体覆盖。
12
- # 来源画布:《AgentFlow》(id: demo)
13
- # 生成器:~/.dsh/agent-flow/preset-gen.mjs
14
- # 注意:delegation 组里的 tool-subagent 是分层下发子 agent 的唯一途径,不要删。
15
-
16
- - insert:
17
- - id: preset-flow-agentflow
18
- name: '@deepseek-ai/dsh-agent-preset'
19
- config:
20
- id: flow-agentflow
21
- name: 'AgentFlow'
22
- description: '由 AgentFlow 画布《AgentFlow》生成:6 个节点 / 5 层'
23
- order: 50
24
- plugins:
25
- - id: persona
26
- name: '@deepseek-ai/dsh-persona'
27
- config:
28
- prefix: |-
29
- 你是一个按固定流程组织子 agent 的调度者。
30
-
31
- 【你是谁】
32
- 你是「AgentFlow」。
33
- 这个流程共 6 个节点、5 层:大纲 → 对白版 / 下游卡片 → 人物版 → 合并 → 下游卡片。
34
-
35
- 【第一步:对齐需求】
36
- 动手前必须先把需求问清楚,最多问 3 个问题、一次问完:
37
- - 这次的题材(主题)是什么?
38
- - 期望的篇幅大概多少?
39
- - 主要人物有哪些已有设定?
40
- 信息够了就说「开始执行」,不要边问边跑。
41
-
42
- 【流程】
43
- 第 1 层(串行)
44
- ▸ 大纲 —— 根据给定的题材,写一个三幕剧的简要大纲,每幕两三句话用修仙类的。11
45
- 完成标准:三幕结构清晰,每幕有一个明确转折.
46
- 用模型:deepseek-flash
47
- 参考技能:caveman、grill-me
48
-
49
- 第 2 层(并行,两个同时下发)
50
- ▸ 对白版 —— 基于大纲,为关键场景写几句有张力的对白。
51
- 完成标准:对白有潜台词,不要直白解释情绪
52
- 用模型:deepseek-flash
53
-
54
- ▸ 下游卡片
55
- 完成标准:(未填写)
56
- 用模型:deepseek-flash
57
-
58
- 第 3 层(等第 2 层全部完成)
59
- ▸ 人物版 —— 基于大纲,塑造主要人物的动机与成长弧线。
60
- 完成标准:主角有明确欲望、阻碍与转变
61
- 用模型:deepseek-flash
62
-
63
- 第 4 层(等第 3 层全部完成)
64
- ▸ 合并 —— 把两份材料合并成一份完整的三幕剧梗概,约 300 字。
65
- 完成标准:读起来是一个连贯故事,不是拼接
66
- 用模型:deepseek-flash
67
-
68
- 第 5 层(独立节点,可单独下发)
69
- ▸ 下游卡片
70
- 完成标准:(未填写)
71
- 用模型:deepseek-flash
72
-
73
- 【怎么下发】
74
- - 每个节点用一次 `subagent` 工具;prompt 里给全:任务 + 完成标准 + 上游产出
75
- - 同一层的多个节点同时下发,不要一个个等
76
- - 上游产出原样传给下游,不要自己改写或概括
77
- - 子 agent 如果还有自己的子任务,把它的子任务一并写进它的 prompt,让它继续嵌套
78
-
79
- 【收尾】
80
- 全部跑完,把最终产出交给用户。中间有节点失败要说清楚是哪个、为什么,不要假装成功。
81
- - id: agent-instructions
82
- name: '@deepseek-ai/dsh-agent-instructions'
83
- config:
84
- maxBytes: 65536
85
- - id: time-context
86
- name: '@deepseek-ai/dsh-time-context'
87
- - id: tool-bash
88
- name: '@deepseek-ai/dsh-tool-bash'
89
- disabled: !!js process.platform === 'win32'
90
- - id: tool-pwsh
91
- name: '@deepseek-ai/dsh-tool-pwsh'
92
- disabled: !!js process.platform !== 'win32'
93
- - id: tool-fs
94
- name: '@deepseek-ai/dsh-tool-fs'
95
- - id: tool-fs-search
96
- name: '@deepseek-ai/dsh-tool-fs-search'
97
- config:
98
- sampleOverCapGlobResults: false
99
- - id: tool-jobs
100
- name: '@deepseek-ai/dsh-tool-jobs'
101
- - id: tool-schedule
102
- name: '@deepseek-ai/dsh-tool-schedule'
103
- - id: skill-filesystem
104
- name: '@deepseek-ai/dsh-skill-filesystem'
105
- - id: tool-skill
106
- name: '@deepseek-ai/dsh-tool-skill'
107
- - id: command-goal
108
- name: '@deepseek-ai/dsh-command-goal'
109
- - id: tool-goal
110
- name: '@deepseek-ai/dsh-tool-goal'
111
- - id: planning
112
- name: cordis:group
113
- group: true
114
- isolate:
115
- planMode: true
116
- config:
117
- - id: plan-mode
118
- name: '@deepseek-ai/dsh-plan-mode'
119
- config:
120
- section: |
121
- You are in plan mode. Stay in plan mode until the user approves your plan through exit_plan_mode or switches the session mode. Imperative language to implement changes means plan the implementation, not execute it. A user's conversational agreement — including an answer confirming something you asked — approves nothing and does not end plan mode; fold the confirmed decision into the plan and submit it through exit_plan_mode.
122
-
123
- Explore first. Use non-mutating reads, searches, static analysis, and checks to ground the plan in the actual repository. Do not edit or write files, change configuration, run formatters or code generation that rewrites tracked files, commit, or otherwise carry out the plan. Prefer existing functions and patterns over new machinery.
124
-
125
- The tool catalog stays the same across modes for request-cache stability. These plan-mode rules override any later tool description or guidance that suggests using mutation tools; those tools remain listed to keep the tool catalog unchanged. Do not use todo_write to track this planning phase: it tracks implementation after an approved plan, while the plan itself belongs in exit_plan_mode.
126
-
127
- Resolve discoverable facts by inspection. Use ask_user_question only for user-owned choices or material ambiguity that inspection cannot answer. Do not ask the user where code lives or how current behavior works when you can find out.
128
-
129
- Make the plan decision-complete: state the goal and success criteria; group implementation changes by subsystem; identify public API, schema, and data-flow changes; cover edge cases, failure modes, tests, acceptance criteria, and explicit assumptions. Keep it concise enough to review but detailed enough that another engineer can implement it without making design decisions.
130
-
131
- When ready, call exit_plan_mode with the complete plan markdown, starting with a # title. Make exit_plan_mode the only and final tool call in that assistant response: it presents the plan for approval, and implementation begins only in a later step after approval. Do not paste the final plan as a plain reply or ask "should I proceed?" through prose or ask_user_question. If review rejects it, incorporate the feedback and present again. If the review channel is unavailable or aborted, stay in plan mode and ask the user to switch modes manually; do not proceed with implementation.
132
- - id: compaction
133
- name: cordis:group
134
- group: true
135
- isolate:
136
- compaction: true
137
- toolResultPruner: true
138
- config:
139
- - id: compaction-basic
140
- name: '@deepseek-ai/dsh-compaction-basic'
141
- - id: command-compact
142
- name: '@deepseek-ai/dsh-command-compact'
143
- - id: tool-result-pruner
144
- name: '@deepseek-ai/dsh-compaction-tool-result-pruner'
145
- config:
146
- thresholdChars: 8192
147
- headChars: 4096
148
- tailChars: 1024
149
- - id: delegation
150
- name: cordis:group
151
- group: true
152
- isolate:
153
- workflowEngine: true
154
- config:
155
- - id: tool-subagent-control
156
- name: '@deepseek-ai/dsh-tool-subagent-control'
157
- - id: tool-subagent-list-agents
158
- name: '@deepseek-ai/dsh-tool-subagent-control/list-agents'
159
- - id: tool-subagent
160
- name: '@deepseek-ai/dsh-tool-subagent'
161
- config:
162
- provider: spawn
163
- toolName: subagent
164
- modelSelectionSettings: true
165
- - id: tool-subagent-fork
166
- name: '@deepseek-ai/dsh-tool-subagent'
167
- config:
168
- provider: fork
169
- toolName: subagent_fork
170
- - id: workflow-ptc
171
- name: '@deepseek-ai/dsh-workflow-ptc'
172
- config:
173
- provider: spawn
174
- - id: tool-workflow
175
- name: '@deepseek-ai/dsh-tool-workflow'
176
- - id: tool-ask-user
177
- name: '@deepseek-ai/dsh-tool-ask-user'
178
- - id: tool-todo
179
- name: '@deepseek-ai/dsh-tool-todo'
180
- config:
181
- allowParallelInProgress: true
182
- - id: tool-web
183
- name: '@deepseek-ai/dsh-tool-web'
184
- config:
185
- fetch: false
186
- searchTimeoutMs: 60000
187
- - id: present
188
- name: '@deepseek-ai/dsh-tool-present'
189
- - id: tool-plugin-manager
190
- name: '@deepseek-ai/dsh-plugin-manager/tools'
191
- disabled: true
192
- - id: tool-plugin-market
193
- name: '@deepseek-ai/dsh-plugin-market/tools'
194
- disabled: true
195
- # <<< AgentFlow 预设结束
package/index.js CHANGED
@@ -23,6 +23,8 @@ import { spawn } from 'node:child_process';
23
23
  import { fileURLToPath, pathToFileURL } from 'node:url';
24
24
  import { defineTool } from '@deepseek-ai/dsh-tools';
25
25
  import { startBridge } from './lib/bridge.js';
26
+ import { startObserver } from './lib/observe.js';
27
+ import { startCommands, createNodeDoneTool } from './lib/commands.js';
26
28
 
27
29
  export const name = 'tool-agentflow';
28
30
 
@@ -300,6 +302,47 @@ export function apply(ctx, config = {}) {
300
302
  // 再用 ctx.llm 调模型;启动失败只 warn,模型通道不可用不影响两个工具加载。
301
303
  ctx.effect(() => startBridge(ctx, { dir: bridgeDir }), 'agentflow: bridge');
302
304
 
305
+ // 观察器(阶段 1):只记每步消息与每次工具调用,不拦不改。
306
+ // 用来在真正做「强制工作流」之前,看清模型实际怎么调工具、在哪收工。
307
+ // `observe: false` 关掉;`observeDir` 换落盘目录。
308
+ if (config.observe !== false) {
309
+ const observeRoot =
310
+ typeof config.observeDir === 'string' && config.observeDir
311
+ ? path.resolve(config.observeDir)
312
+ : path.join(dataDir, 'observe');
313
+ ctx.effect(() => startObserver(ctx, { root: observeRoot }).dispose, 'agentflow: observe');
314
+ }
315
+
316
+ // 命令(阶段 2):引擎侧在 <dataDir>/commands/ 下写命令文件,这里热注册成
317
+ // DSH 的 /agentflow-xxx。宿主没有 commands 服务时 startCommands 内部只 warn,
318
+ // 插件照常加载;`commands: false` 关掉。
319
+ let commandsApi = null;
320
+ if (config.commands !== false) {
321
+ ctx.effect(() => {
322
+ const api = startCommands(ctx, { dir, dataDir });
323
+ commandsApi = api;
324
+ return () => {
325
+ try {
326
+ api.dispose();
327
+ } finally {
328
+ if (commandsApi === api) commandsApi = null;
329
+ }
330
+ };
331
+ }, 'agentflow: commands');
332
+ }
333
+
334
+ // ---------- agentflow_node_done ----------
335
+ // 画布工作流的显式完成信号:工具全局注册,只有发起这次 run 的 agent 能调。
336
+ // 没有进行中的 run 时只回一句「当前没有工作流」,不报错。
337
+ ctx.tools.register(
338
+ createNodeDoneTool(
339
+ (agentId) => (commandsApi ? commandsApi.getRun(agentId) : null),
340
+ (agentId, run) => {
341
+ if (commandsApi && typeof commandsApi.persistRun === 'function') commandsApi.persistRun(agentId, run);
342
+ },
343
+ ),
344
+ );
345
+
303
346
  // ---------- agentflow_list ----------
304
347
  ctx.tools.register(
305
348
  defineTool({