dsh-paoding 0.3.6 → 0.3.7

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
@@ -19,7 +19,7 @@ dsh-paoding(中文品牌「庖丁」,典出《庄子》——庖丁顺纹理
19
19
  - **图形界面配置**:不习惯改配置文件的话,点开左侧栏底部动作条(设置行上方)的「庖丁配置」入口,整页点选即可,「保存并应用」即时重生成预设——与首装自动化同一条生成管线。
20
20
  - **版本更新有提醒,面板一键升级**:面板会对比 npm registry(npmmirror 镜像、GitHub release 依次兜底)上的最新版本,发现新版就地提示并附上发布页链接;点「升级」就地执行 `dsh plugin update dsh-paoding`(profile 自动探测)换上新版,重启 DSH 后生效,编排预设会在重启时按新版自动重生成。开发检出的 link 形态无法就地升级,面板会提示切回 registry 版的办法;检测失败时页头留一行「版本检测失败 · 重试」(完整原因与代理排查提示在悬浮提示里),不弹窗打扰。
21
21
  - **来去自由**:不改 DSH 一行源码;装完新会话即用,卸载即删目录。
22
- - **两代宿主都兼容**:DSH 0.1.5 / 0.1.6 / 0.1.7-rc.1 / 0.1.7-rc.2 全支持——两代宿主发现预设的机制不同(目录扫描 vs patch 声明行),安装器自动检测宿主版本选对落点,宿主升级、降级后的首次保存/自愈自动迁移,全程无感。
22
+ - **两代宿主都兼容**:DSH 0.1.5 – 0.2.0-rc.2 全支持(0.1.5 / 0.1.6 / 0.1.7-rc.1 / 0.1.7-rc.2 / 0.2.0-rc.1 / 0.2.0-rc.2)——两代宿主发现预设的机制不同(目录扫描 vs patch 声明行),安装器自动检测宿主版本选对落点;编排组合里的 workflow worker 行也按宿主版本自动适配(0.1.5 用 workflow-worker-thread,0.1.6+ 换 workflow-ptc——旧包 0.1.6 起已删除,残留行正是 0.2.0 面板上「加载失败」的元凶),宿主升级、降级后的首次保存/自愈自动迁移,全程无感。
23
23
 
24
24
  ## 插件截图
25
25
 
@@ -27,7 +27,7 @@ dsh-paoding(中文品牌「庖丁」,典出《庄子》——庖丁顺纹理
27
27
 
28
28
  ## 快速开始
29
29
 
30
- 前置:**Node.js ≥ 18**、**pnpm** 与一台已装好的 DSH host(**@deepseek-ai/dsh 0.1.5 – 0.1.7-rc.2** 全支持,预设落点自动适配两代宿主机制)。
30
+ 前置:**Node.js ≥ 18**、**pnpm** 与一台已装好的 DSH host(**@deepseek-ai/dsh 0.1.5 – 0.2.0-rc.2** 全支持,预设落点自动适配两代宿主机制)。
31
31
 
32
32
  **官方插件通道(唯一安装入口)**——不用克隆仓库,一条命令,装完重启即用:
33
33
 
@@ -80,7 +80,7 @@ dsh plugin --profile web add link:"$PWD/dsh-paoding"
80
80
  | 文档 | 内容 |
81
81
  |---|---|
82
82
  | [架构](docs/architecture.md) | 为什么这样设计——概述、成本账、角色与工具、对应 DSH 原生机制、Token 治理、已知边界。 |
83
- | [安装](docs/installation.md) | 官方插件通道一条命令安装、版本支持与预设落点双轨(DSH 0.1.5 – 0.1.7-rc.2)、首装自动化与版本标记、升级、开发检出自装、host patch 检测机制、庖丁配置面板、卸载、排查。 |
83
+ | [安装](docs/installation.md) | 官方插件通道一条命令安装、版本支持与预设落点双轨(DSH 0.1.5 – 0.2.0-rc.2)、首装自动化与版本标记、升级、开发检出自装、host patch 检测机制、庖丁配置面板、卸载、排查。 |
84
84
  | [编排](docs/orchestration.md) | 编排总览、失败如何呈现与三层处理、one-shot vs continuable、上下文隔离。 |
85
85
  | [配置](docs/configuration.md) | 配置键全参考、内置角色微调、主 agent 工具与技能、自定义角色、按角色分模型、角色会话模式。 |
86
86
 
package/README_en.md CHANGED
@@ -19,7 +19,7 @@ dsh-paoding (Chinese brand 「庖丁」/ Pao Ding, from the Zhuangzi parable of
19
19
  - **A visual configurator**: prefer clicking to editing YAML? the 庖丁配置 (Paoding Config) entry in the sidebar bottom action bar (above the Settings row) covers everything; Save & Apply regenerates the preset on the spot — the same generation pipeline as first-install automation.
20
20
  - **Notified of new releases, one-click upgrade in the panel**: the panel compares your local version against the latest npm registry publish (npmmirror and GitHub release as successive fallbacks) and points to the release page when a newer one is out. The Upgrade button runs `dsh plugin update dsh-paoding` in place (profile auto-detected) to swap in the new version; a DSH restart applies it, and the orchestrator preset regenerates itself against the new version on startup. A dev checkout in link: form cannot be upgraded in place — the panel tells you how to switch back to the registry version. If the check fails (offline, rate-limited), a subdued "version check failed · retry" row stays in the header (full causes and proxy hints in its tooltip) without popping anything up.
21
21
  - **Installs and uninstalls cleanly**: no DSH source changes; active in a new session right after install, gone when you delete one directory.
22
- - **Works on both host generations**: DSH 0.1.5 / 0.1.6 / 0.1.7-rc.1 / 0.1.7-rc.2 are all supported — the two generations discover presets differently (directory scan vs patch declaration rows), and the installer picks the right landing automatically, then migrates on the first save/self-heal after a host upgrade or downgrade, all without user action.
22
+ - **Works on both host generations**: DSH 0.1.5 through 0.2.0-rc.2 are all supported (0.1.5 / 0.1.6 / 0.1.7-rc.1 / 0.1.7-rc.2 / 0.2.0-rc.1 / 0.2.0-rc.2) — the two generations discover presets differently (directory scan vs patch declaration rows), and the installer picks the right landing automatically; from 0.2.0 the workflow worker row in the orchestration composition also adapts to the host version at generation time (0.1.5 uses workflow-worker-thread, 0.1.6+ swaps to workflow-ptc). Migration happens on the first save/self-heal after a host upgrade or downgrade, all without user action.
23
23
 
24
24
  ## Screenshots
25
25
 
@@ -27,7 +27,7 @@ dsh-paoding (Chinese brand 「庖丁」/ Pao Ding, from the Zhuangzi parable of
27
27
 
28
28
  ## Quick Start
29
29
 
30
- Prerequisites: **Node.js ≥ 18**, **pnpm**, and a working DSH host (**@deepseek-ai/dsh 0.1.5 – 0.1.7-rc.2**, all supported — the preset landing adapts to the host generation automatically).
30
+ Prerequisites: **Node.js ≥ 18**, **pnpm**, and a working DSH host (**@deepseek-ai/dsh 0.1.5 – 0.2.0-rc.2**, all supported — the preset landing adapts to the host generation automatically).
31
31
 
32
32
  **The official plugin channel (the only install route)** — no clone needed; one command, then a restart:
33
33
 
@@ -80,7 +80,7 @@ All preferences live in `~/.dsh/dsh-paoding.config.yml`: role add/remove/tweak,
80
80
  | Document | Contents |
81
81
  |---|---|
82
82
  | [Architecture](docs/architecture_en.md) | Why it is designed this way — overview, the cost ledger, roles and tools, mapping to DSH's native mechanisms, token governance, known limits. |
83
- | [Installation](docs/installation_en.md) | The single official channel (`dsh plugin add`), version support and the dual preset landing (DSH 0.1.5 – 0.1.7-rc.2), first-install automation and the version marker, upgrading, dev-checkout install, host patch detection, the Paoding Config panel, uninstalling, troubleshooting. |
83
+ | [Installation](docs/installation_en.md) | The single official channel (`dsh plugin add`), version support and the dual preset landing (DSH 0.1.5 – 0.2.0-rc.2), first-install automation and the version marker, upgrading, dev-checkout install, host patch detection, the Paoding Config panel, uninstalling, troubleshooting. |
84
84
  | [Orchestration](docs/orchestration_en.md) | Orchestration overview, how failures surface and the three recovery layers, one-shot vs continuable, context isolation. |
85
85
  | [Configuration](docs/configuration_en.md) | The full key reference, tuning built-in roles, main-agent tools and skills, custom roles, per-role models, per-role session modes. |
86
86
 
@@ -269,7 +269,7 @@ grep inline cap 与对照同设计)后,根因锁定为:**默认压缩从
269
269
  触发),配合 retainRatio 默认保留约 16% 尾部(≈42K)+ 摘要 checkpoint,稳态平均回落到 ~80K 量级。
270
270
  压缩每次触发调用一次 LLM 摘要——复用前缀缓存,成本极低——因此触发频繁一点也几乎不额外花钱;
271
271
  0.45 是「省 token 与不频繁摘要」之间的平衡点。compaction-basic 另显式钉了 `maxTokens: 8192`:
272
- DSH 0.1.7 起宿主默认升到 65536,显式值让 0.1.5 – 0.1.7-rc.2 全系的摘要预算行为保持一致。
272
+ DSH 0.1.7 起宿主默认升到 65536,显式值让 0.1.5 – 0.2.0-rc.2 全系的摘要预算行为保持一致。
273
273
 
274
274
  **参数与效果。**
275
275
 
@@ -352,7 +352,7 @@ performs one LLM summarization call that reuses the prefix cache, so its cost is
352
352
  somewhat more often costs almost nothing extra; 0.45 is the balance point between saving tokens and not
353
353
  summarizing too frequently. compaction-basic also pins `maxTokens: 8192` explicitly: from DSH 0.1.7 the host
354
354
  default rises to 65536, and the explicit value keeps the summary-budget behavior identical across
355
- 0.1.5 – 0.1.7-rc.2.
355
+ 0.1.5 – 0.2.0-rc.2.
356
356
 
357
357
  **Parameters and effect.**
358
358
 
@@ -12,7 +12,7 @@
12
12
 
13
13
  - **Node.js ≥ 18**。DSH host 与插件链路都跑在 Node 上;版本过低时 `dsh` 本身就起不来。可用 `node --version` 自查。
14
14
  - **pnpm 可用**。`dsh plugin` 是 pnpm 的透明转发器:插件包装进 profile 时由 pnpm 完成。缺失时安装命令会报 pnpm 相关错误,装好 pnpm(`npm i -g pnpm` 或 `corepack enable`)后重试。
15
- - **已安装 DSH host**(`dsh` 可用,含其 Web/agent 运行时;**@deepseek-ai/dsh 0.1.5 – 0.1.7-rc.2** 全支持,安装器自动适配两代预设机制,详见第 2 节「版本支持与预设落点双轨」)。本插件不改 DSH 源码,只往 DSH 的 preset roster 写入编排预设、并把「庖丁配置」面板挂进 Web 左侧栏,因此先要有可用的 DSH 家目录。
15
+ - **已安装 DSH host**(`dsh` 可用,含其 Web/agent 运行时;**@deepseek-ai/dsh 0.1.5 – 0.2.0-rc.2** 全支持,安装器自动适配两代预设机制,详见第 2 节「版本支持与预设落点双轨」)。本插件不改 DSH 源码,只往 DSH 的 preset roster 写入编排预设、并把「庖丁配置」面板挂进 Web 左侧栏,因此先要有可用的 DSH 家目录。
16
16
  - **路径事实**(生成管线内建常量,写死前先了解它们):
17
17
 
18
18
  | 项 | 默认值 | 说明 |
@@ -50,9 +50,9 @@ dsh plugin --profile web add dsh-paoding
50
50
 
51
51
  `--profile web` 决定插件装进哪个 profile:「庖丁配置」面板只在以该 profile 启动的 DSH Web 里出现。`web` 是 `dsh web` 的默认 profile,多数机器无需改动;多 profile 用户对每个需要的 profile 各执行一次 `dsh plugin add`。编排预设本体落在 `$DSH_HOME/.agent-presets/`,全机共享、不随 profile 走——DSH ≥ 0.1.7 的声明行写在 home 层 `$DSH_HOME/cordis.patch.yml`,同样与 profile 无关——在任何 profile 的面板里「保存并应用」,写的都是同一份预设。
52
52
 
53
- ### 版本支持与预设落点双轨(DSH 0.1.5 – 0.1.7-rc.2)
53
+ ### 版本支持与预设落点双轨(DSH 0.1.5 – 0.2.0-rc.2)
54
54
 
55
- 本插件兼容两代宿主:**DSH 0.1.5 / 0.1.6 / 0.1.7-rc.1 / 0.1.7-rc.2 全部支持**,安装与使用流程完全一致。插件 0.3.4 起,package.json 显式声明 `peerDependencies: @deepseek-ai/dsh >= 0.1.5`——0.1.7 起宿主装插件前做兼容性预检,无声明等于默认放行,显式声明后按真实兼容面把关;0.1.5 / 0.1.6 不读这个字段,声明纯属元数据,不影响安装。
55
+ 本插件兼容两代宿主:**DSH 0.1.5 / 0.1.6 / 0.1.7-rc.1 / 0.1.7-rc.2 / 0.2.0-rc.1 / 0.2.0-rc.2 全部支持**,安装与使用流程完全一致。插件 0.3.4 起,package.json 显式声明 `peerDependencies: @deepseek-ai/dsh >= 0.1.5`——0.1.7 起宿主装插件前做兼容性预检,无声明等于默认放行,显式声明后按真实兼容面把关;0.1.5 / 0.1.6 不读这个字段,声明纯属元数据,不影响安装。
56
56
 
57
57
  两代宿主发现「本地 authored preset」的机制不同,安装器据此分双轨落点,宿主检测全自动、用户无感:
58
58
 
@@ -84,9 +84,10 @@ dsh plugin --profile web add dsh-paoding
84
84
 
85
85
  ### 版本标记与自动重生成
86
86
 
87
- `.generator-version` 标记由插件启动自愈在生成 preset 后写入,内容两行:**生成器版本 + 预设安装轨道**(旧版只写版本一行,读到时按「轨道未知」处理,恰是宿主升级后的迁移触发态)。三类触发:
87
+ `.generator-version` 标记由插件启动自愈在生成 preset 后写入,内容三行:**生成器版本 + 预设安装轨道 + workflow worker 形态**(0.3.7 起;v2 旧标记只有前两行、更早只有版本一行,缺行按「未知」处理,恰是宿主升级后的迁移触发态)。四类触发:
88
88
 
89
89
  - **标记缺失 / 版本不符**(首装、插件升级)→ 整盘重生成。面板「保存并应用」与兜底 CLI 的生成走整目录重建、不写回标记——重建即移除旧标记,下次 DSH 启动自愈发现标记缺失会多补跑一次生成并重写标记(产物相同,只是多跑一轮)。插件升级后标记与包版本不符,下次 DSH 启动自动按新版重生成,「升级后 preset 还是旧版产物」的漂移就此消除。
90
+ - **版本一致、worker 形态分叉**(宿主跨过 0.1.5/0.1.6 的 worker 边界:0.1.5 用 `workflow-worker-thread`,0.1.6+ 换 `workflow-ptc`;旧标记无第三行、而宿主已升到 ptc 侧时同样算分叉)→ 整盘重生成:该行长在产物正文里,轻量补行覆盖不到。
90
91
  - **版本一致、轨道翻转**(只升 dsh 不动插件——升级最常见路径)→ 轻量迁移,不重生成内容:声明轨上按磁盘产物补写缺失的声明行(含各工作区预设),directory 轨上若有残留声明行则整盘重生成撤下(残留会让 0.1.6 profile 启动失败)。
91
92
  - **版本轨道都一致** → 对账声明行:缺行补行、目录已删的孤儿行撤行,齐则什么都不动。
92
93
 
@@ -12,7 +12,7 @@ Check the following before installing:
12
12
 
13
13
  - **Node.js ≥ 18**. The DSH host and the plugin chain both run on Node; with an older version `dsh` won't start in the first place. Check with `node --version`.
14
14
  - **pnpm available**. `dsh plugin` is a transparent pass-through to pnpm: installing a plugin package into a profile is done by pnpm. If pnpm is missing the install command fails with a pnpm error — install pnpm (`npm i -g pnpm` or `corepack enable`) and retry.
15
- - **A working DSH host** (the `dsh` command, including its Web/agent runtime; **@deepseek-ai/dsh 0.1.5 – 0.1.7-rc.2**, all supported — the installer adapts to the two preset mechanisms automatically; see §2 "Version support and the dual preset landing"). This plugin does not modify DSH source code; it only writes the orchestrator preset into DSH's preset roster and mounts the Paoding Config page into the Web sidebar, so a usable DSH home directory must exist first.
15
+ - **A working DSH host** (the `dsh` command, including its Web/agent runtime; **@deepseek-ai/dsh 0.1.5 – 0.2.0-rc.2**, all supported — the installer adapts to the two preset mechanisms automatically; see §2 "Version support and the dual preset landing"). This plugin does not modify DSH source code; it only writes the orchestrator preset into DSH's preset roster and mounts the Paoding Config page into the Web sidebar, so a usable DSH home directory must exist first.
16
16
  - **Path facts** (constants built into the generation pipeline; know them before customizing):
17
17
 
18
18
  | Item | Default | Description |
@@ -50,9 +50,9 @@ The orchestrator preset needs no separate install — the plugin fills it in aut
50
50
 
51
51
  `--profile web` decides which profile the plugin is installed into: the Paoding Config page appears only in a DSH Web launched with that profile. `web` is the default profile of `dsh web`, so most machines need no change; multi-profile users run `dsh plugin add` once per profile they want it in. The orchestrator preset itself lives under `$DSH_HOME/.agent-presets/` and is shared machine-wide, independent of profiles — under DSH ≥ 0.1.7 the declaration rows go into the home-layer `$DSH_HOME/cordis.patch.yml`, equally profile-independent — Save & Apply from any profile's panel writes the same preset.
52
52
 
53
- ### Version support and the dual preset landing (DSH 0.1.5 – 0.1.7-rc.2)
53
+ ### Version support and the dual preset landing (DSH 0.1.5 – 0.2.0-rc.2)
54
54
 
55
- The plugin supports two host generations: **DSH 0.1.5 / 0.1.6 / 0.1.7-rc.1 / 0.1.7-rc.2 are all supported**, with an identical install and usage flow. As of plugin 0.3.4, package.json declares `peerDependencies: @deepseek-ai/dsh >= 0.1.5` explicitly — from 0.1.7 the host runs a plugin-compatibility precheck before installing: no declaration meant a default pass, an explicit one gets checked against the real compatibility surface; 0.1.5 / 0.1.6 never read the field, where the declaration is pure metadata and changes nothing about the install.
55
+ The plugin supports two host generations: **DSH 0.1.5 / 0.1.6 / 0.1.7-rc.1 / 0.1.7-rc.2 / 0.2.0-rc.1 / 0.2.0-rc.2 are all supported**, with an identical install and usage flow. As of plugin 0.3.4, package.json declares `peerDependencies: @deepseek-ai/dsh >= 0.1.5` explicitly — from 0.1.7 the host runs a plugin-compatibility precheck before installing: no declaration meant a default pass, an explicit one gets checked against the real compatibility surface; 0.1.5 / 0.1.6 never read the field, where the declaration is pure metadata and changes nothing about the install.
56
56
 
57
57
  The two generations discover "local authored presets" differently, so the installer maintains a dual landing, detecting the host automatically — nothing for the user to do:
58
58
 
@@ -84,9 +84,10 @@ So **`dsh plugin add` + restart = a complete install**: preset generated, config
84
84
 
85
85
  ### The version marker and auto-regeneration
86
86
 
87
- The `.generator-version` marker is written by the plugin's startup self-heal after it generates the preset. Its content is two lines: **the generator version and the preset system track** (older releases wrote only the version; such markers read back as "track unknown", which is exactly the migration trigger after a host upgrade). Three triggers:
87
+ The `.generator-version` marker is written by the plugin's startup self-heal after it generates the preset. Its content is three lines: **the generator version, the preset system track, and the workflow worker flavor** (0.3.7 onward; v2 markers carried only the first two lines and earlier ones only the version — missing lines read back as "unknown", which is exactly the migration trigger after a host upgrade). Four triggers:
88
88
 
89
89
  - **Marker missing or version-mismatched** (first install, plugin upgrade) → full regeneration. The panel's Save & Apply (and the fallback CLI) do a full-directory rebuild and write no marker — the rebuild removes the old marker, so the next DSH start's self-heal finds it missing, runs one extra generation, and rewrites the marker (same output, just one extra round). After a plugin upgrade the marker no longer matches the package version, so the next DSH start regenerates the preset against the new version — the "upgraded the plugin but the preset is stale" drift is eliminated.
90
+ - **Version matching but the worker flavor diverged** (the host crossed the 0.1.5/0.1.6 worker boundary: 0.1.5 uses `workflow-worker-thread`, 0.1.6+ swaps to `workflow-ptc`; an old marker without the third line on a host that now needs ptc counts as diverged too) → full regeneration: the row lives in the artifact body, out of reach of the lightweight backfill.
90
91
  - **Version matching but the track flipped** (dsh upgraded, plugin untouched — the most common upgrade path) → lightweight migration with no content regeneration: on the declarative track, missing declaration rows are backfilled from the on-disk artifacts (per-workspace presets included); on the directory track, any leftover declaration rows force a full regeneration that strips them (leftovers would break 0.1.6 profile startup).
91
92
  - **Version and track both matching** → declaration-row reconciliation: missing rows are backfilled, orphan rows whose directories are gone are removed; if everything lines up, nothing is touched.
92
93
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-paoding",
3
- "version": "0.3.6",
3
+ "version": "0.3.7",
4
4
  "description": "庖丁 — DSH (DeepSeek Harness) 编排模式安装器:host 感知的角色工具过滤 + Web 设置页可视化配置器",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -14,13 +14,33 @@ import { assignSlugs, collectState, generateAndInstall, presetIdOf } from '../..
14
14
  import { autoAssignments } from '../../tools/lib/alloc.mjs'
15
15
  import {
16
16
  backfillHomePatchRows,
17
+ detectHostVersionTriple,
17
18
  detectPresetSystem,
18
19
  formatGeneratorMarker,
20
+ isPtcWorkerHost,
19
21
  listOrchestratorPresetIds,
20
22
  parseGeneratorMarker,
21
23
  readHomePatchRows,
24
+ WORKER_PTC,
25
+ WORKER_WORKER_THREAD,
22
26
  } from '../../tools/lib/preset-system.mjs'
23
27
 
28
+ /** 宿主版本三元组 → workflow worker 标记行(生成产物行的形态,与 compose 对齐)。 */
29
+ function workerFlavorOf(hostVersionTriple) {
30
+ return isPtcWorkerHost(hostVersionTriple) ? WORKER_PTC : WORKER_WORKER_THREAD
31
+ }
32
+
33
+ /**
34
+ * 标记 worker 行与当前宿主形态是否兼容:只有「产物形态」与「宿主所需形态」在
35
+ * ptc/worker-thread 上分叉(其中一边是 ptc)才算换代——null(v2/v1 旧标记)与
36
+ * worker-thread 同侧:旧标记 + 保守 worker-thread 现状 = 产物本就按旧行生成且
37
+ * 宿主探测不出新形态要求,不触发重生成(v2 标记 + 真实 ptc 宿主仍会触发,
38
+ * 0.1.5→0.2.0 升级与 v2→v3 标记升级都靠这条路径)。
39
+ */
40
+ function workerFlavorCompatible(markerWorker, currentWorker) {
41
+ return (markerWorker === WORKER_PTC) === (currentWorker === WORKER_PTC)
42
+ }
43
+
24
44
  export function resolveDshHome() {
25
45
  return process.env.DSH_HOME || path.join(os.homedir(), '.dsh')
26
46
  }
@@ -151,10 +171,11 @@ export function installAssignments(state, assignments, { dryRun = false, saveCon
151
171
  //
152
172
  // 唯一官方安装通道是插件通道(dsh plugin add dsh-paoding),包不再携带 CLI:
153
173
  // preset 的落盘改由插件启动时自愈。对账基准是 preset 目录里的 .generator-version
154
- // 标记文件(v2 起内容 = 生成器版本 + 预设安装轨道两行):插件包版本变、或宿主
155
- // 轨道翻转(只升 dsh 不动插件——升级最常见路径,旧标记只记版本时同样解析为
156
- // 轨道不符)→ 下次启动自动自愈,一举解决「升级后 preset 还是旧版产物」与
157
- // 「升 dsh 后 preset 静默消失」两类漂移。
174
+ // 标记文件(v3 起内容 = 生成器版本 + 预设安装轨道 + workflow worker 形态三行):
175
+ // 插件包版本变、或宿主轨道翻转 / worker 形态换代(只升 dsh 不动插件——升级最
176
+ // 常见路径,旧标记缺行时同样解析为不符)→ 下次启动自动自愈,一举解决「升级后
177
+ // preset 还是旧版产物」「升 dsh 后 preset 静默消失」与「升 dsh 后 worker 行
178
+ // 失配 never started」三类漂移。
158
179
 
159
180
  // 生成器版本(标记对账基准):插件包根 package.json 读一次即缓存。读失败兜底
160
181
  // '0.0.0'——最坏后果只是每次启动多重生成一次 preset,不会装坏任何东西。
@@ -178,10 +199,13 @@ const PRESET_ENSURE_ABORTED = '插件已卸载(dispose),本次 preset 生
178
199
  *
179
200
  * 判定(满足其一即动盘):
180
201
  * - preset 目录 / 标记缺失,或标记版本与 generatorVersion 不符 → 整盘重生成;
181
- * - 版本一致但标记轨道与当前宿主轨道不符(只升 dsh 不动插件 / 旧格式标记)→
182
- * 轻量迁移:声明轨上按磁盘产物补写缺失声明行(内容字节不动,不重生成),
183
- * directory 轨上落回整盘重生成(残留声明行会让 0.1.6 profile 启动失败,
184
- * 撤块自愈由其 directory 分支完成);
202
+ * - 版本一致但标记 worker 形态与当前宿主所需分叉(宿主跨过 0.1.5/0.1.6
203
+ * worker 边界;v2/v1 旧标记在真实 ptc 宿主上同样分叉)→ 整盘重生成(该行
204
+ * 长在产物正文里,轻量路径覆盖不到);
205
+ * - 版本 worker 都一致但标记轨道与当前宿主轨道不符(只升 dsh 不动插件 /
206
+ * 旧格式标记)→ 轻量迁移:声明轨上按磁盘产物补写缺失声明行(内容字节
207
+ * 不动,不重生成),directory 轨上落回整盘重生成(残留声明行会让 0.1.6
208
+ * profile 启动失败,撤块自愈由其 directory 分支完成);
185
209
  * - 版本轨道都一致 → 对账声明行(缺行补行、孤儿行撤行),齐则跳过。
186
210
  * 局部 collectState() 不带运行时事实(首装触发点在插件启动、宿主服务未必就绪,
187
211
  * 且与 CLI --auto 语义对齐;预设安装轨道由 collectState 内部按 argv 宿主版本 /
@@ -210,31 +234,38 @@ export async function ensurePresetInstalled({ dshHome = resolveDshHome(), genera
210
234
  } catch { /* 目录或标记缺失:按未生成处理 */ }
211
235
 
212
236
  if (marker !== null && marker.version === generatorVersion) {
213
- // 版本一致:内容无需重生成,但宿主轨道可能已随 dsh 升降级翻转(只升 dsh
214
- // 不动插件是升级常态),声明行也可能被外部动过——这里做轻量对账。
215
- const system = await detectPresetSystem({ dshHome })
216
- if (shouldAbort?.()) return { installed: false, reason: PRESET_ENSURE_ABORTED }
217
- const presetIds = listOrchestratorPresetIds(dshHome)
218
- if (system === 'declarative') {
219
- const backfilled = backfillHomePatchRows(dshHome, presetIds)
220
- if (marker.track === 'declarative' && backfilled.length === 0) {
221
- return { installed: false, reason: 'preset 已由当前版本生成器产出,无需重生成' }
237
+ // 版本一致:内容原则上无需重生成,但宿主可能已随 dsh 升降级翻转轨道或
238
+ // 跨过 workflow worker 形态边界(只升 dsh 不动插件是升级常态),声明行也
239
+ // 可能被外部动过——这里做轻量对账。worker 形态不符(v2/v1 旧标记没有第
240
+ // 三行,一律视为不符)不能走轻量路径:该行长在产物正文里,补行/撤行覆盖
241
+ // 不到,必须整盘重生成——落到下方重生成分支。
242
+ const currentWorker = workerFlavorOf(detectHostVersionTriple())
243
+ if (workerFlavorCompatible(marker.worker, currentWorker)) {
244
+ const system = await detectPresetSystem({ dshHome })
245
+ if (shouldAbort?.()) return { installed: false, reason: PRESET_ENSURE_ABORTED }
246
+ const presetIds = listOrchestratorPresetIds(dshHome)
247
+ if (system === 'declarative') {
248
+ const backfilled = backfillHomePatchRows(dshHome, presetIds)
249
+ if (marker.track === 'declarative' && backfilled.length === 0) {
250
+ return { installed: false, reason: 'preset 已由当前版本生成器产出,无需重生成' }
251
+ }
252
+ writeFileSync(markerFile, formatGeneratorMarker(generatorVersion, 'declarative', currentWorker), 'utf8')
253
+ return backfilled.length > 0
254
+ ? { installed: false, backfilled, reason: `宿主已切换声明行轨,补写 ${backfilled.length} 条预设声明行(preset 内容未重生成)` }
255
+ : { installed: false, reason: '预设安装轨道标记已更新为声明行轨(内容无需重生成)' }
222
256
  }
223
- writeFileSync(markerFile, formatGeneratorMarker(generatorVersion, 'declarative'), 'utf8')
224
- return backfilled.length > 0
225
- ? { installed: false, backfilled, reason: `宿主已切换声明行轨,补写 ${backfilled.length} 条预设声明行(preset 内容未重生成)` }
226
- : { installed: false, reason: '预设安装轨道标记已更新为声明行轨(内容无需重生成)' }
227
- }
228
- // directory 轨:声明行残留会让 0.1.6 profile 启动失败——有残留走整盘
229
- // 重生成(directory 分支撤块自愈);行清白时只升级标记格式,不动内容。
230
- if (readHomePatchRows(dshHome).length === 0) {
231
- if (marker.track === 'directory') {
232
- return { installed: false, reason: 'preset 已由当前版本生成器产出,无需重生成' }
257
+ // directory 轨:声明行残留会让 0.1.6 profile 启动失败——有残留走整盘
258
+ // 重生成(directory 分支撤块自愈);行清白时只升级标记格式,不动内容。
259
+ if (readHomePatchRows(dshHome).length === 0) {
260
+ if (marker.track === 'directory') {
261
+ return { installed: false, reason: 'preset 已由当前版本生成器产出,无需重生成' }
262
+ }
263
+ writeFileSync(markerFile, formatGeneratorMarker(generatorVersion, 'directory', currentWorker), 'utf8')
264
+ return { installed: false, reason: '预设安装轨道标记已升级为带轨道格式(内容无需重生成)' }
233
265
  }
234
- writeFileSync(markerFile, formatGeneratorMarker(generatorVersion, 'directory'), 'utf8')
235
- return { installed: false, reason: '预设安装轨道标记已升级为带轨道格式(内容无需重生成)' }
266
+ // 声明行残留 + directory 宿主:落到下方整盘重生成
236
267
  }
237
- // 声明行残留 + directory 宿主:落到下方整盘重生成
268
+ // worker 形态不符(含旧格式标记升级到 v3):落到下方整盘重生成
238
269
  }
239
270
 
240
271
  if (shouldAbort?.()) return { installed: false, reason: PRESET_ENSURE_ABORTED }
@@ -262,7 +293,7 @@ export async function ensurePresetInstalled({ dshHome = resolveDshHome(), genera
262
293
  if (shouldAbort?.()) return { installed: false, reason: PRESET_ENSURE_ABORTED }
263
294
  mkdirSync(presetDir, { recursive: true })
264
295
  const track = result.presetSystem === 'declarative' ? 'declarative' : 'directory'
265
- writeFileSync(markerFile, formatGeneratorMarker(generatorVersion, track), 'utf8')
296
+ writeFileSync(markerFile, formatGeneratorMarker(generatorVersion, track, workerFlavorOf(state.hostVersionTriple)), 'utf8')
266
297
  // 声明轨上整盘重生成只重建本 preset 的行:其余工作区 preset 目录若在而
267
298
  // 声明行缺(轨道翻转后首次触发本路径),一并补齐。
268
299
  const backfilled = track === 'declarative' ? backfillHomePatchRows(dshHome, listOrchestratorPresetIds(dshHome)) : []
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "paoding-config-ui",
3
- "version": "0.3.6",
3
+ "version": "0.3.7",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "main": "index.mjs",
@@ -175,7 +175,10 @@
175
175
  - id: tool-subagent-list-agents
176
176
  name: '@deepseek-ai/dsh-tool-subagent-control/list-agents'
177
177
 
178
- # subagent / subagent_fork / workflow-worker-thread / workflow / ralph 仅当用户经 main_agent_extra 显式开启时对主 agent 可达;保留以对齐 standard 全量组合。
178
+ # subagent / subagent_fork / workflow worker / workflow / ralph 仅当用户经 main_agent_extra
179
+ # 显式开启时对主 agent 可达;保留以对齐 standard 全量组合。workflow worker 行
180
+ # 由生成器按宿主版本适配(0.1.5 = workflow-worker-thread;0.1.6+ 换成
181
+ # workflow-ptc——旧包 0.1.6 起已删除,残留会让预设报 never started)。
179
182
  - id: tool-subagent
180
183
  name: '@deepseek-ai/dsh-tool-subagent'
181
184
  config:
@@ -16,6 +16,9 @@
16
16
  * allow 过滤走白名单语义(usableWith / presetUniverse ∪ inventory,见
17
17
  * filterUsableTools 上方说明):手写进配置、既不在 preset 自带工具面也不在
18
18
  * 检测库存的名字一律从 allow 丢弃,不再「非 host 依赖名直通」。
19
+ * workflow worker 行按宿主版本生成期适配(adaptWorkflowWorkerRow:≥0.1.6 把
20
+ * 0.1.5-only 的 workflow-worker-thread 条目换成 workflow-ptc;探测不到版本
21
+ * 保守保留源行,产物零 diff 性质随之保留)。
19
22
  * 自定义角色另有三道防护/自动化:toolName 保留名校验(assertCustomToolName,
20
23
  * 内置角色 / restrictBase / subagent* / mcp__ 前缀 / YAML 字面量一律 throw);
21
24
  * tools 为空时拒绝安装零工具角色(renderCustomRoleBlocks 直接 throw,防
@@ -29,6 +32,43 @@
29
32
  import { DEFAULT_MAIN_AGENT_PERSONA_EXTRA, ROLES, warn } from './util.mjs'
30
33
  import { normalizeModelRef, yamlScalar } from './config.mjs'
31
34
  import { isHostDependent } from './host.mjs'
35
+ import { isPtcWorkerHost } from './preset-system.mjs'
36
+
37
+ // ── workflow worker 行的宿主版本适配 ─────────────────────────────────────────
38
+
39
+ // 源模板里 workflow worker 行的原样块(delegation 组 config 内,4 空格条目缩进)。
40
+ // 宿主 ≥0.1.6 时整块换成 workflow-ptc(0.1.6-alpha.1 起
41
+ // @deepseek-ai/dsh-workflow-worker-thread 包已删除,残留行让 preset 的
42
+ // delegation 组报 never started,0.2.0 Plugin Manager 显示「加载失败」;
43
+ // workflow-ptc 是 0.1.6–0.2.0 各版 standard 组合的统一形态,ptcRuntime 服务由
44
+ // base bundle 提供,preset 内不需要再列 ptc-runtime 行)。行不在任何 span 定位
45
+ // 区间内,调用点在全部 edits 应用之后,与偏移量零交互。
46
+ const WORKER_THREAD_BLOCK = [
47
+ ' - id: workflow-worker-thread',
48
+ " name: '@deepseek-ai/dsh-workflow-worker-thread'",
49
+ ' config:',
50
+ ' provider: spawn',
51
+ ].join('\n')
52
+ const WORKER_PTC_BLOCK = [
53
+ ' - id: workflow-ptc',
54
+ " name: '@deepseek-ai/dsh-workflow-ptc'",
55
+ ' config:',
56
+ ' provider: spawn',
57
+ ].join('\n')
58
+
59
+ /**
60
+ * Host-version adaptation of the delegation group's workflow worker row:
61
+ * ≥0.1.6 swaps the 0.1.5-only workflow-worker-thread entry for workflow-ptc
62
+ * (see isPtcWorkerHost). A missing source block means template drift — a hard
63
+ * error, mirroring how missing role blocks abort generation.
64
+ */
65
+ export function adaptWorkflowWorkerRow(text, hostVersionTriple) {
66
+ if (!isPtcWorkerHost(hostVersionTriple)) return text
67
+ if (!text.includes(WORKER_THREAD_BLOCK)) {
68
+ throw new Error('workflow-worker-thread block not found in source agent.cordis.yml')
69
+ }
70
+ return text.replace(WORKER_THREAD_BLOCK, WORKER_PTC_BLOCK)
71
+ }
32
72
 
33
73
  // ── restrict.mjs main-agent allow base ──────────────────────────────────────
34
74
 
@@ -559,7 +599,7 @@ export function filterUsableTools(tools, presetUniverse, inventory) {
559
599
  * block / restrict config.allow injection) plus the per-role diff info for
560
600
  * the report.
561
601
  */
562
- export function composeGenerated(srcText, blocks, personaBlocks, assignments, inventory, restrictBase, mainAgentSkillMetas = {}) {
602
+ export function composeGenerated(srcText, blocks, personaBlocks, assignments, inventory, restrictBase, mainAgentSkillMetas = {}, hostVersionTriple = null) {
563
603
  const skillsMap = assignments.skills ?? {}
564
604
  const customToolNames = Object.keys(assignments.roles).filter((n) => !ROLES.includes(n))
565
605
  // roles_remove:被删除的内置角色不产 allow/persona edits(委派行整条删除时避免重叠),
@@ -713,6 +753,9 @@ export function composeGenerated(srcText, blocks, personaBlocks, assignments, in
713
753
  edits.sort((a, b) => b.start - a.start)
714
754
  for (const edit of edits) out = out.slice(0, edit.start) + edit.text + out.slice(edit.end)
715
755
 
756
+ // workflow worker 行宿主版本适配(≥0.1.6 换 workflow-ptc;见 adaptWorkflowWorkerRow)。
757
+ out = adaptWorkflowWorkerRow(out, hostVersionTriple)
758
+
716
759
  const customBlock = renderCustomRoleBlocks(assignments.roles, skillsMap, restrictBase)
717
760
  if (customBlock !== '') out = insertCustomRoles(out, customBlock)
718
761
 
@@ -20,8 +20,10 @@
20
20
  * 写盘并明确报错,与仓库「配置不可信时拒绝落盘」口径一致。
21
21
  * 4) 轨道迁移对账 backfillHomePatchRows / listOrchestratorPresetIds /
22
22
  * parseGeneratorMarker / formatGeneratorMarker:宿主只升 dsh 不动插件时
23
- * (标记版本一致、轨道翻转——升级最常见路径),按磁盘产物补写缺失声明行
24
- * 而不整盘重生成;生成器标记记「版本 + 轨道」两行,任一不符即触发自愈。
23
+ * (标记版本一致、轨道翻转或 workflow worker 形态换代——升级最常见路径),
24
+ * 按磁盘产物补写缺失声明行而不整盘重生成;生成器标记记「版本 + 轨道 +
25
+ * worker 形态」三行,任一不符即触发自愈(worker 形态换代必须整盘重生成,
26
+ * 见 detectHostVersionTriple / isPtcWorkerHost)。
25
27
  * 零运行时依赖(node:child_process / node:fs / node:path / node:url);被 state 引用。
26
28
  */
27
29
  import { spawnSync } from 'node:child_process'
@@ -55,16 +57,20 @@ export function parseVersionTriple(text) {
55
57
  return [Number(m[1]), Number(m[2]), Number(m[3])]
56
58
  }
57
59
 
58
- /** 三元组是否达到声明行轨(0.1.7+):逐段数值比较,prerelease 已在上游剥掉。 */
59
- export function isDeclarativeTriple(triple) {
60
- if (!Array.isArray(triple) || triple.length !== 3 || triple.some((n) => !Number.isInteger(n))) return false
61
- const base = [0, 1, 7]
60
+ /** 三元组逐段与基准比较:≥ 基准返回 true(prerelease 已在上游剥掉)。 */
61
+ function atOrAfterTriple(triple, base) {
62
62
  for (let i = 0; i < 3; i++) {
63
63
  if (triple[i] !== base[i]) return triple[i] > base[i]
64
64
  }
65
65
  return true
66
66
  }
67
67
 
68
+ /** 三元组是否达到声明行轨(0.1.7+):逐段数值比较,prerelease 已在上游剥掉。 */
69
+ export function isDeclarativeTriple(triple) {
70
+ if (!Array.isArray(triple) || triple.length !== 3 || triple.some((n) => !Number.isInteger(n))) return false
71
+ return atOrAfterTriple(triple, [0, 1, 7])
72
+ }
73
+
68
74
  /**
69
75
  * 探测当前宿主的预设安装轨道,返回 'declarative' | 'directory'。逐级降级:
70
76
  * 1) runtimeSystem 直通:Web 插件运行时对宿主服务的实时探测结果最权威;
@@ -536,10 +542,12 @@ export function backfillHomePatchRows(dshHome, presetIds) {
536
542
  }
537
543
 
538
544
  /**
539
- * 解析 .generator-version 标记文本为 { version, track }。v2 格式两行:首行
540
- * 生成器版本、次行预设安装轨道(declarative / directory);旧格式(单行版本,
541
- * 0.3.4 及以前)解析出 track: null —— 恰是「宿主轨道已翻转但产物版本未动」的
542
- * 迁移触发态。空文本 / 全空白返回 null(按未生成处理)。
545
+ * 解析 .generator-version 标记文本为 { version, track, worker }。v3 格式三行:
546
+ * 首行生成器版本、次行预设安装轨道(declarative / directory)、三行 workflow
547
+ * worker 形态(ptc / worker-thread);旧格式(v2 两行 = 0.3.6 及以前、单行 =
548
+ * 0.3.4 及以前)解析出 track / worker 为 null —— 恰是「宿主已换代但产物版本
549
+ * 未动」的迁移触发态(轨道翻转、worker 换代都靠它触发自愈)。空文本 / 全空白
550
+ * 返回 null(按未生成处理)。
543
551
  */
544
552
  export function parseGeneratorMarker(text) {
545
553
  const lines = String(text ?? '')
@@ -548,10 +556,49 @@ export function parseGeneratorMarker(text) {
548
556
  .filter((l) => l !== '')
549
557
  if (lines.length === 0) return null
550
558
  const track = lines[1] === 'declarative' || lines[1] === 'directory' ? lines[1] : null
551
- return { version: lines[0], track }
559
+ const worker = lines[2] === 'ptc' || lines[2] === 'worker-thread' ? lines[2] : null
560
+ return { version: lines[0], track, worker }
561
+ }
562
+
563
+ /**
564
+ * 生成 .generator-version 标记文本(版本 + 轨道 + worker 形态三行,v3 格式)。
565
+ * worker 取 WORKER_PTC / WORKER_WORKER_THREAD(允许调用方先探测再传入)。
566
+ */
567
+ export function formatGeneratorMarker(version, presetSystem, worker) {
568
+ return `${version}\n${presetSystem}\n${worker}\n`
552
569
  }
553
570
 
554
- /** 生成 .generator-version 标记文本(版本 + 轨道两行,v2 格式)。 */
555
- export function formatGeneratorMarker(version, presetSystem) {
556
- return `${version}\n${presetSystem}\n`
571
+ // ── workflow worker 形态(宿主版本 → 生成期换行)────────────────────────────
572
+
573
+ /** 生成产物里 workflow worker 行的两种形态(标记第三行的合法取值)。 */
574
+ export const WORKER_PTC = 'ptc'
575
+ export const WORKER_WORKER_THREAD = 'worker-thread'
576
+
577
+ /**
578
+ * 探测正在运行的宿主版本三元组:进程 argv 宿主版本(插件与宿主同进程时零开销
579
+ * 且反映正在运行的宿主)> PATH 上的 `dsh --version` > null。与
580
+ * detectPresetSystem 的前两级同源复用,但不再往下走文件探测——0.1.5 与 0.1.6
581
+ * 的磁盘形态没有可区分的单数包,探测不到就返回 null 由调用方按未知处理。
582
+ */
583
+ export function detectHostVersionTriple() {
584
+ const argvVersion = hostVersionFromArgv()
585
+ if (argvVersion !== null) return parseVersionTriple(argvVersion)
586
+ const versionText = dshVersionText()
587
+ if (versionText !== null) return parseVersionTriple(versionText)
588
+ return null
589
+ }
590
+
591
+ /**
592
+ * 该宿主版本的 workflow worker 行形态:≥0.1.6 用 @deepseek-ai/dsh-workflow-ptc
593
+ * (0.1.6-alpha.1 起 workflow-worker-thread 包已删除,残留行会让 preset 的
594
+ * delegation 组报 never started,0.2.0 Plugin Manager 显示为「加载失败」);
595
+ * 0.1.5 只认 @deepseek-ai/dsh-workflow-worker-thread。版本探测不到时返回 false
596
+ *(保守侧:保留源模板原行)——「探测不到」走不出插件运行时(argv 必命中),
597
+ * 多见于无 dsh 在 PATH 的原始环境;保守默认让 0.1.5 全场景可用,也与
598
+ * detectPresetSystem 的安全侧兜底哲学一致(产物零 diff 的 golden 性质随之保留:
599
+ * 显式 null 不改源文)。
600
+ */
601
+ export function isPtcWorkerHost(triple) {
602
+ if (!Array.isArray(triple) || triple.length !== 3 || triple.some((n) => !Number.isInteger(n))) return false
603
+ return atOrAfterTriple(triple, [0, 1, 6])
557
604
  }
@@ -48,6 +48,7 @@ import { composeGenerated, extractMainAgentAllow } from './compose.mjs'
48
48
  import { smartDefaults } from './alloc.mjs'
49
49
  import {
50
50
  buildPresetRowText,
51
+ detectHostVersionTriple,
51
52
  detectPresetSystem,
52
53
  listOrchestratorPresetIds,
53
54
  presetRowId,
@@ -232,6 +233,10 @@ export async function collectState({ dshHome, configFile, profile = null, patche
232
233
  //(见 preset-system.detectPresetSystem 的降级链)。
233
234
  const presetSystem = await detectPresetSystem({ dshHome, runtimeSystem: runtimeFacts?.presetSystem ?? null })
234
235
 
236
+ // 宿主版本三元组(argv 宿主版本 > dsh --version > null,探测不到按未知处理):
237
+ // workflow worker 行的生成期形态判定(isPtcWorkerHost)与标记 worker 行都从这里取。
238
+ const hostVersionTriple = detectHostVersionTriple()
239
+
235
240
  // 已落盘 preset 目录扫描(编排类):.agent-presets 下的 orchestrator 基础
236
241
  // preset 与 orchestrator-<slug> 工作区专属 preset。扫描失败(目录尚不存在、
237
242
  // 无读权限等)一律降级为空列表,绝不影响检测结果本体;generateAndInstall
@@ -265,6 +270,8 @@ export async function collectState({ dshHome, configFile, profile = null, patche
265
270
  installedPresets,
266
271
  // 预设安装轨道(generateAndInstall 据此双轨收尾;serializeState 透传给面板)
267
272
  presetSystem,
273
+ // 宿主版本三元组(null = 探测不到):workflow worker 行生成期形态判定用
274
+ hostVersionTriple,
268
275
  // 主 persona 尾部追加的默认常量(配置键 main_agent_persona_extra 缺省值):
269
276
  // 供 UI state(serializeState → /api/paoding/state)展示与「恢复默认」用。
270
277
  mainPersonaExtraDefault: DEFAULT_MAIN_AGENT_PERSONA_EXTRA,
@@ -366,8 +373,9 @@ export function generateAndInstall(state, assignments, { dryRun = false, saveCon
366
373
  }
367
374
 
368
375
  // Compose the generated config; a composition error is a hard error.
369
- //(这层包装无增值,让 composeGenerated 的原始错误直接上抛。)
370
- const composed = composeGenerated(srcText, blocks, personaBlocks, assignments, inventory, restrictBase, mainAgentSkillMetas)
376
+ //(这层包装无增值,让 composeGenerated 的原始错误直接上抛。)宿主版本三元组
377
+ // 驱动 workflow worker 行的生成期换行(≥0.1.6 → workflow-ptc,null 按现代宿主)。
378
+ const composed = composeGenerated(srcText, blocks, personaBlocks, assignments, inventory, restrictBase, mainAgentSkillMetas, state?.hostVersionTriple ?? null)
371
379
  const generatedText = composed.text
372
380
  const roleResults = composed.roleResults
373
381