dsh-paoding 0.4.3 → 0.4.4
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 +3 -3
- package/README_en.md +3 -3
- package/docs/installation.md +4 -3
- package/docs/installation_en.md +4 -3
- package/package.json +1 -1
- package/plugins/paoding-config-ui/api-core.mjs +89 -22
- package/plugins/paoding-config-ui/package.json +1 -1
- package/presets/orchestrator/agent.cordis.yml +5 -2
- package/presets/orchestrator/role-restrict.mjs +78 -0
- package/tools/lib/compose.mjs +286 -2
- package/tools/lib/doctor.mjs +44 -4
- package/tools/lib/preset-system.mjs +193 -32
- package/tools/lib/state.mjs +18 -10
- package/tools/lib/util.mjs +4 -2
|
@@ -58,6 +58,41 @@ export function parseVersionTriple(text) {
|
|
|
58
58
|
return [Number(m[1]), Number(m[2]), Number(m[3])]
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
/**
|
|
62
|
+
* 解析裸版本号文本为 { triple, prerelease }:三元组同 parseVersionTriple,
|
|
63
|
+
* prerelease 保留原文(`-` 与 `+` 之间,0.2.1-rc.2 → 'rc.2';正式版 null)。
|
|
64
|
+
* 角色委派行形态门(isPresetRoleHost)只在 0.2.1 这一个点上需要分辨
|
|
65
|
+
* prerelease——守卫是 0.2.1-rc.2 才落地的,rc.0/rc.1 仍认内联形状。
|
|
66
|
+
*/
|
|
67
|
+
export function parseVersionInfo(text) {
|
|
68
|
+
const m = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/.exec(String(text ?? '').trim())
|
|
69
|
+
if (!m) return null
|
|
70
|
+
return { triple: [Number(m[1]), Number(m[2]), Number(m[3])], prerelease: m[4] ?? null }
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** prerelease 逐 token 比较(数字 token 按数值,其余按字典序):a≥b 返回正数。 */
|
|
74
|
+
function comparePrerelease(a, b) {
|
|
75
|
+
const as = String(a).split('.')
|
|
76
|
+
const bs = String(b).split('.')
|
|
77
|
+
for (let i = 0; i < Math.max(as.length, bs.length); i++) {
|
|
78
|
+
const av = as[i]
|
|
79
|
+
const bv = bs[i]
|
|
80
|
+
if (av === undefined) return -1 // 前缀更短 = 更小(1.0.0-alpha < 1.0.0-alpha.1)
|
|
81
|
+
if (bv === undefined) return 1
|
|
82
|
+
const an = /^\d+$/.test(av)
|
|
83
|
+
const bn = /^\d+$/.test(bv)
|
|
84
|
+
if (an && bn) {
|
|
85
|
+
const d = Number(av) - Number(bv)
|
|
86
|
+
if (d !== 0) return d
|
|
87
|
+
} else if (an !== bn) {
|
|
88
|
+
return an ? -1 : 1 // semver:数字 token 恒小于字母 token
|
|
89
|
+
} else if (av !== bv) {
|
|
90
|
+
return av < bv ? -1 : 1
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return 0
|
|
94
|
+
}
|
|
95
|
+
|
|
61
96
|
/** 三元组逐段与基准比较:≥ 基准返回 true(prerelease 已在上游剥掉)。 */
|
|
62
97
|
function atOrAfterTriple(triple, base) {
|
|
63
98
|
for (let i = 0; i < 3; i++) {
|
|
@@ -101,22 +136,35 @@ export async function detectPresetSystem({ dshHome, runtimeSystem = null } = {})
|
|
|
101
136
|
}
|
|
102
137
|
|
|
103
138
|
/**
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
|
|
139
|
+
* 计入 argv rung 的宿主包名(显式集合,禁止 `@deepseek-ai/dsh-` 前缀泛匹配:
|
|
140
|
+
* dsh-persona / dsh-tool-subagent 等插件包不是宿主,误认会让插件包版本冒充
|
|
141
|
+
* 宿主版本驱动版本门控)。desktop 宿主与其打包的 dsh runtime 同版本同轨
|
|
142
|
+
* (实测 0.2.1-rc.2 两侧一致),取 dsh-desktop-host 包版本即宿主版本。
|
|
143
|
+
*/
|
|
144
|
+
const HOST_ARGV_PACKAGE_NAMES = new Set(['@deepseek-ai/dsh', '@deepseek-ai/dsh-desktop-host'])
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* 从进程 argv[1](entry 可注入供测试,默认 process.argv[1])推断正在运行的
|
|
148
|
+
* 宿主版本:沿脚本所在目录向上找最近的 package.json,name 是宿主包名
|
|
149
|
+
* (HOST_ARGV_PACKAGE_NAMES:CLI 宿主 @deepseek-ai/dsh,或 desktop 宿主
|
|
150
|
+
* @deepseek-ai/dsh-desktop-host)即取其 version。插件跑在宿主进程里,这比
|
|
151
|
+
* PATH 上的 `dsh --version` 又快又准(无需子进程);找不到(CLI / 测试 /
|
|
152
|
+
* 任意非宿主进程)返回 null。找到的第一个 package.json 不是宿主即止——向上
|
|
153
|
+
* 穿越无关包目录没有意义;中间层没有 package.json 则继续向上(宿主包缺
|
|
154
|
+
* 元数据时仍能命中外层 dsh 包)。desktop 宿主进程 argv[1] 指向
|
|
155
|
+
* .../node_modules/@deepseek-ai/dsh-desktop-host/lib/index.js,向上一级即
|
|
156
|
+
* 命中 dsh-desktop-host 包——此前只认 @deepseek-ai/dsh,desktop 宿主探测
|
|
157
|
+
* 落空、错误回落 PATH 上的 dsh CLI(版本可能远低于正在运行的宿主)。
|
|
109
158
|
*/
|
|
110
|
-
function hostVersionFromArgv() {
|
|
159
|
+
export function hostVersionFromArgv(entry = process.argv[1]) {
|
|
111
160
|
try {
|
|
112
|
-
const entry = process.argv[1]
|
|
113
161
|
if (typeof entry !== 'string' || entry === '') return null
|
|
114
162
|
let dir = path.dirname(path.resolve(entry))
|
|
115
163
|
for (let i = 0; i < 6 && dir !== path.dirname(dir); i++) {
|
|
116
164
|
const pkgPath = path.join(dir, 'package.json')
|
|
117
165
|
if (existsSync(pkgPath)) {
|
|
118
166
|
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'))
|
|
119
|
-
return pkg?.name
|
|
167
|
+
return HOST_ARGV_PACKAGE_NAMES.has(pkg?.name) ? String(pkg.version ?? '') : null
|
|
120
168
|
}
|
|
121
169
|
dir = path.dirname(dir)
|
|
122
170
|
}
|
|
@@ -126,7 +174,14 @@ function hostVersionFromArgv() {
|
|
|
126
174
|
}
|
|
127
175
|
}
|
|
128
176
|
|
|
129
|
-
/**
|
|
177
|
+
/**
|
|
178
|
+
* argv rung 之后的 CLI 回落:PATH 上 `dsh --version` 取裸版本号文本;任何失败
|
|
179
|
+
* (ENOENT / 超时 / 非零退出)返回 null。只在 argv 未命中宿主包时才会走到
|
|
180
|
+
* 这里——desktop 宿主 argv[1] 命中 dsh-desktop-host 包,不会落到本级;而
|
|
181
|
+
* PATH 上的 dsh CLI 版本可能低于正在运行的宿主(用户升了 desktop、PATH 上
|
|
182
|
+
* 还是旧 CLI,实测 desktop 0.2.1-rc.2 在跑而 PATH dsh 是 0.2.0-rc.2),本级
|
|
183
|
+
* 结果只代表「PATH 上的 CLI」,故探测优先级低于 argv。
|
|
184
|
+
*/
|
|
130
185
|
function dshVersionText() {
|
|
131
186
|
try {
|
|
132
187
|
const res = spawnSync('dsh', ['--version'], { timeout: 10_000, encoding: 'utf8' })
|
|
@@ -157,14 +212,19 @@ function dshVersionText() {
|
|
|
157
212
|
* plugins: 落 8 空格、插件条目行落 10 空格。name/description 用 JSON.stringify
|
|
158
213
|
* 落值——JSON 字符串即合法 YAML 双引号标量,换行/引号自动转义。agentYmlText
|
|
159
214
|
* 先把 restrict 行的 `name: ./restrict.mjs` 与 delegation-labels 行的
|
|
160
|
-
* `name: ./delegation-labels.mjs` 替换为对应绝对 file: URL
|
|
161
|
-
* 均无「相对 preset
|
|
215
|
+
* `name: ./delegation-labels.mjs` 替换为对应绝对 file: URL(本地插件文件
|
|
216
|
+
* 均无「相对 preset 目录」语义,必须绝对化);0.2.1+ AgentPreset 形态的产物
|
|
217
|
+
* 另含 `name: ./role-restrict.mjs` 的角色面行(嵌套在 paoding-role-* 子声明
|
|
218
|
+
* 行的 plugins 里),同理经 roleRestrictFileUrl 绝对化——未提供该 URL 而产物
|
|
219
|
+
* 里有该行属硬错误(与 delegation-labels 同口径,防相对名字静默进 patch 让
|
|
220
|
+
* preset 挂掉),提供了而产物里没有则无害(旧形态产物照常构造)。
|
|
221
|
+
* 再整体 +10 缩进;空行保持空行(块标量相对
|
|
162
222
|
* 缩进不变),`!!js` 表达式行原样保留(宿主 patch schema 支持,求值仍由
|
|
163
223
|
* cordis 装载时完成)。文本里出现 delegation-labels 行而未提供
|
|
164
224
|
* delegationLabelsFileUrl 时硬错误——漏传会让相对名字静默进 patch,宿主
|
|
165
225
|
* 解析不了、preset 直接挂掉。
|
|
166
226
|
*/
|
|
167
|
-
export function buildPresetRowText({ presetId, name = null, description = null, order = null, agentYmlText, restrictFileUrl, delegationLabelsFileUrl = null }) {
|
|
227
|
+
export function buildPresetRowText({ presetId, name = null, description = null, order = null, agentYmlText, restrictFileUrl, delegationLabelsFileUrl = null, roleRestrictFileUrl = null }) {
|
|
168
228
|
if (typeof presetId !== 'string' || !/^[a-z0-9][a-z0-9-]*$/.test(presetId)) {
|
|
169
229
|
throw new Error(`buildPresetRowText: 非法 presetId: ${presetId}`)
|
|
170
230
|
}
|
|
@@ -174,12 +234,15 @@ export function buildPresetRowText({ presetId, name = null, description = null,
|
|
|
174
234
|
if (typeof restrictFileUrl !== 'string' || restrictFileUrl === '') {
|
|
175
235
|
throw new Error('buildPresetRowText: restrictFileUrl 不能为空')
|
|
176
236
|
}
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
.some((line) => line.trim() === 'name: ./delegation-labels.mjs')
|
|
237
|
+
const agentLines = String(agentYmlText).split(/\r?\n/)
|
|
238
|
+
const referencesLabelsRow = agentLines.some((line) => line.trim() === 'name: ./delegation-labels.mjs')
|
|
180
239
|
if (referencesLabelsRow && (typeof delegationLabelsFileUrl !== 'string' || delegationLabelsFileUrl === '')) {
|
|
181
240
|
throw new Error('buildPresetRowText: agentYmlText 引用了 ./delegation-labels.mjs,但未提供 delegationLabelsFileUrl')
|
|
182
241
|
}
|
|
242
|
+
const referencesRoleRestrictRow = agentLines.some((line) => line.trim() === 'name: ./role-restrict.mjs')
|
|
243
|
+
if (referencesRoleRestrictRow && (typeof roleRestrictFileUrl !== 'string' || roleRestrictFileUrl === '')) {
|
|
244
|
+
throw new Error('buildPresetRowText: agentYmlText 引用了 ./role-restrict.mjs(0.2.1+ AgentPreset 形态),但未提供 roleRestrictFileUrl')
|
|
245
|
+
}
|
|
183
246
|
const lines = [
|
|
184
247
|
` - id: ${presetRowId(presetId)}`,
|
|
185
248
|
` name: '${PRESET_ROW_NAME}'`,
|
|
@@ -190,7 +253,7 @@ export function buildPresetRowText({ presetId, name = null, description = null,
|
|
|
190
253
|
if (typeof description === 'string' && description !== '') lines.push(` description: ${JSON.stringify(description)}`)
|
|
191
254
|
if (Number.isInteger(order)) lines.push(` order: ${order}`)
|
|
192
255
|
lines.push(' plugins:')
|
|
193
|
-
for (const line of
|
|
256
|
+
for (const line of agentLines) {
|
|
194
257
|
if (line.trim() === '') {
|
|
195
258
|
lines.push('')
|
|
196
259
|
continue
|
|
@@ -200,6 +263,7 @@ export function buildPresetRowText({ presetId, name = null, description = null,
|
|
|
200
263
|
const fileUrl =
|
|
201
264
|
trimmed === 'name: ./restrict.mjs' ? restrictFileUrl
|
|
202
265
|
: trimmed === 'name: ./delegation-labels.mjs' ? delegationLabelsFileUrl
|
|
266
|
+
: trimmed === 'name: ./role-restrict.mjs' ? roleRestrictFileUrl
|
|
203
267
|
: null
|
|
204
268
|
const replaced = fileUrl !== null ? `${line.match(/^[ \t]*/)[0]}name: ${fileUrl}` : line
|
|
205
269
|
lines.push(` ${replaced}`)
|
|
@@ -521,9 +585,36 @@ export function readPresetDirMeta(dir) {
|
|
|
521
585
|
* 不拖垮其余补写(整盘重生成可修复)。
|
|
522
586
|
* 返回实际补写成功的 presetId 列表(既没补也没撤时为空数组)。
|
|
523
587
|
*/
|
|
588
|
+
/**
|
|
589
|
+
* 判定一份 agent.cordis.yml 产物文本的角色委派行形态(backfill 自愈的防毒
|
|
590
|
+
* 守卫用,与标记第四行同词汇表):8 空格缩进的内联 `persona: |-` → inline
|
|
591
|
+
* (0.2.1 宿主的 dsh-tool-subagent 会拒绝);顶层 `agentPreset: paoding-role-`
|
|
592
|
+
* 引用 → agent-preset。两者都无(角色被 roles_remove 清空且无自定义角色等)
|
|
593
|
+
* 返回 null——产物不含任何 0.2.1 守卫键,对两代宿主都安全。两种形状同时在场
|
|
594
|
+
* 视为 inline(混合/损坏产物按旧形状从严处理,交给整盘重生成修复)。
|
|
595
|
+
*/
|
|
596
|
+
export function detectAgentRolesFlavor(agentYmlText) {
|
|
597
|
+
if (/^ persona: \|-/m.test(agentYmlText)) return ROLES_INLINE
|
|
598
|
+
if (/^ agentPreset: paoding-role-/m.test(agentYmlText)) return ROLES_AGENT_PRESET
|
|
599
|
+
return null
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
/**
|
|
603
|
+
* 轨道迁移对账:把 $DSH_HOME/cordis.patch.yml MANAGED 块里缺失的编排类 preset
|
|
604
|
+
* 声明行按磁盘产物补写回来。只补「行缺失」:行在则零写盘(内容对账是
|
|
605
|
+
* ensurePresetInstalled 的整盘重生成路径,不在这里做)。
|
|
606
|
+
*
|
|
607
|
+
* 形态防毒守卫:补行内容 = 磁盘产物全文内联,若产物角色行形态与宿主所需
|
|
608
|
+
* (presetRolesFlavorOf(detectHostVersionTriple()))不符——典型是宿主已升
|
|
609
|
+
* 0.2.1 而磁盘还是内联形状——补出去的声明行会让 0.2.1 宿主挂编排 preset 的
|
|
610
|
+
* 新建会话直接被 tool-subagent 守卫拒绝。此时跳过该 preset 并 warn(可整盘
|
|
611
|
+
* 重生成修复),绝不按旧产物写行;反方向(宿主降级、磁盘已是新形状)同理。
|
|
612
|
+
* 守卫经 DSH_PAODING_FAKE_HOST_VERSION 可测。
|
|
613
|
+
*/
|
|
524
614
|
export function backfillHomePatchRows(dshHome, presetIds) {
|
|
525
615
|
const rowsOnDisk = readHomePatchRows(dshHome)
|
|
526
616
|
const wanted = new Set((Array.isArray(presetIds) ? presetIds : []).filter((id) => ORCHESTRATOR_PRESET_ID_RE.test(id)))
|
|
617
|
+
const hostRolesFlavor = presetRolesFlavorOf(detectHostVersionTriple())
|
|
527
618
|
const rows = []
|
|
528
619
|
const backfilled = []
|
|
529
620
|
for (const presetId of wanted) {
|
|
@@ -531,10 +622,23 @@ export function backfillHomePatchRows(dshHome, presetIds) {
|
|
|
531
622
|
const dir = path.join(dshHome, '.agent-presets', presetId)
|
|
532
623
|
try {
|
|
533
624
|
const agentYmlText = readFileSync(path.join(dir, 'agent.cordis.yml'), 'utf8')
|
|
625
|
+
// 形态防毒守卫(见上方 doc 注释):磁盘产物形态 ≠ 宿主所需 → 不补行。
|
|
626
|
+
const diskRolesFlavor = detectAgentRolesFlavor(agentYmlText)
|
|
627
|
+
if (diskRolesFlavor !== null && diskRolesFlavor !== hostRolesFlavor) {
|
|
628
|
+
throw new Error(
|
|
629
|
+
`磁盘产物角色行形态 ${diskRolesFlavor} 与宿主所需 ${hostRolesFlavor} 不符——按旧产物补行会让 0.2.1 宿主被 tool-subagent 守卫拒绝,请整盘重生成换代`,
|
|
630
|
+
)
|
|
631
|
+
}
|
|
534
632
|
const restrictFile = path.join(dir, 'restrict.mjs')
|
|
535
633
|
if (!existsSync(restrictFile)) throw new Error('restrict.mjs 缺失')
|
|
536
634
|
const labelsFile = path.join(dir, 'delegation-labels.mjs')
|
|
537
635
|
if (!existsSync(labelsFile)) throw new Error('delegation-labels.mjs 缺失')
|
|
636
|
+
// role-restrict.mjs 仅 0.2.1+ AgentPreset 形态产物含(角色面行)——按产物
|
|
637
|
+
// 里有没有该行决定是否必需,目录轨老产物(inline 形态)不受影响。
|
|
638
|
+
const roleRestrictFile = path.join(dir, 'role-restrict.mjs')
|
|
639
|
+
if (agentYmlText.includes('name: ./role-restrict.mjs') && !existsSync(roleRestrictFile)) {
|
|
640
|
+
throw new Error('role-restrict.mjs 缺失')
|
|
641
|
+
}
|
|
538
642
|
const meta = readPresetDirMeta(dir)
|
|
539
643
|
rows.push(
|
|
540
644
|
buildPresetRowText({
|
|
@@ -545,6 +649,7 @@ export function backfillHomePatchRows(dshHome, presetIds) {
|
|
|
545
649
|
agentYmlText,
|
|
546
650
|
restrictFileUrl: pathToFileURL(restrictFile).href,
|
|
547
651
|
delegationLabelsFileUrl: pathToFileURL(labelsFile).href,
|
|
652
|
+
...(existsSync(roleRestrictFile) ? { roleRestrictFileUrl: pathToFileURL(roleRestrictFile).href } : {}),
|
|
548
653
|
}),
|
|
549
654
|
)
|
|
550
655
|
backfilled.push(presetId)
|
|
@@ -560,12 +665,13 @@ export function backfillHomePatchRows(dshHome, presetIds) {
|
|
|
560
665
|
}
|
|
561
666
|
|
|
562
667
|
/**
|
|
563
|
-
* 解析 .generator-version 标记文本为 { version, track, worker }。
|
|
564
|
-
*
|
|
565
|
-
* worker 形态(ptc / worker-thread
|
|
566
|
-
* 0.3
|
|
567
|
-
|
|
568
|
-
*
|
|
668
|
+
* 解析 .generator-version 标记文本为 { version, track, worker, roles }。v4 格式
|
|
669
|
+
* 四行:首行生成器版本、次行预设安装轨道(declarative / directory)、三行
|
|
670
|
+
* workflow worker 形态(ptc / worker-thread)、四行角色委派行形态(inline /
|
|
671
|
+
* agent-preset);v3 三行(0.4.3 及以前)解析出 roles 为 null、v2 两行
|
|
672
|
+
*(0.3.6 及以前)track / worker 也为 null —— null 恰是「宿主已换代但产物版本
|
|
673
|
+
* 未动」的迁移触发态(轨道翻转、worker 换代、角色行换代都靠它触发自愈)。
|
|
674
|
+
* 空文本 / 全空白返回 null(按未生成处理)。
|
|
569
675
|
*/
|
|
570
676
|
export function parseGeneratorMarker(text) {
|
|
571
677
|
const lines = String(text ?? '')
|
|
@@ -575,15 +681,17 @@ export function parseGeneratorMarker(text) {
|
|
|
575
681
|
if (lines.length === 0) return null
|
|
576
682
|
const track = lines[1] === 'declarative' || lines[1] === 'directory' ? lines[1] : null
|
|
577
683
|
const worker = lines[2] === 'ptc' || lines[2] === 'worker-thread' ? lines[2] : null
|
|
578
|
-
|
|
684
|
+
const roles = lines[3] === 'inline' || lines[3] === 'agent-preset' ? lines[3] : null
|
|
685
|
+
return { version: lines[0], track, worker, roles }
|
|
579
686
|
}
|
|
580
687
|
|
|
581
688
|
/**
|
|
582
|
-
* 生成 .generator-version 标记文本(版本 + 轨道 + worker
|
|
583
|
-
* worker 取 WORKER_PTC / WORKER_WORKER_THREAD
|
|
689
|
+
* 生成 .generator-version 标记文本(版本 + 轨道 + worker 形态 + 角色形态四行,
|
|
690
|
+
* v4 格式)。worker 取 WORKER_PTC / WORKER_WORKER_THREAD、roles 取 ROLES_INLINE /
|
|
691
|
+
* ROLES_AGENT_PRESET(允许调用方先探测再传入)。
|
|
584
692
|
*/
|
|
585
|
-
export function formatGeneratorMarker(version, presetSystem, worker) {
|
|
586
|
-
return `${version}\n${presetSystem}\n${worker}\n`
|
|
693
|
+
export function formatGeneratorMarker(version, presetSystem, worker, roles) {
|
|
694
|
+
return `${version}\n${presetSystem}\n${worker}${roles ? `\n${roles}` : ''}\n`
|
|
587
695
|
}
|
|
588
696
|
|
|
589
697
|
// ── workflow worker 形态(宿主版本 → 生成期换行)────────────────────────────
|
|
@@ -592,6 +700,44 @@ export function formatGeneratorMarker(version, presetSystem, worker) {
|
|
|
592
700
|
export const WORKER_PTC = 'ptc'
|
|
593
701
|
export const WORKER_WORKER_THREAD = 'worker-thread'
|
|
594
702
|
|
|
703
|
+
// ── 角色委派行形态(宿主版本 → 生成期换形)──────────────────────────────────
|
|
704
|
+
|
|
705
|
+
/** 生成产物里角色委派行的两种形态(标记第四行的合法取值)。 */
|
|
706
|
+
export const ROLES_INLINE = 'inline'
|
|
707
|
+
export const ROLES_AGENT_PRESET = 'agent-preset'
|
|
708
|
+
|
|
709
|
+
/**
|
|
710
|
+
* 该宿主版本的角色委派行形态:≥0.2.1(含 0.2.1-rc.*,parseVersionTriple 把
|
|
711
|
+
* prerelease 剥成 0.2.1 三元组,逐段比较天然命中)走 AgentPreset 形态——
|
|
712
|
+
* dsh-tool-subagent 从 0.2.1-rc.2 起在 apply() 硬拒配置里的 persona /
|
|
713
|
+
* toolFilter 键(报 "persona is no longer supported; configure persona and
|
|
714
|
+
* tool plugins in an Agent Preset and select it with agentPreset"),内联
|
|
715
|
+
* 形状的委派行让 0.2.1+ 宿主上挂编排 preset 的新建会话直接抛错;< 0.2.1 保持
|
|
716
|
+
* 内联形态(老宿主不认 agentPreset 键,产物零 diff 不变量随之保留)。版本
|
|
717
|
+
* 探测不到时返回 false(保守侧:保留源模板原形状),与 isPtcWorkerHost 的
|
|
718
|
+
* 保守哲学一致。
|
|
719
|
+
*/
|
|
720
|
+
export function isPresetRoleHost(triple, prerelease) {
|
|
721
|
+
if (!Array.isArray(triple) || triple.length !== 3 || triple.some((n) => !Number.isInteger(n))) return false
|
|
722
|
+
if (!atOrAfterTriple(triple, [0, 2, 1])) return false
|
|
723
|
+
// 0.2.1 这一个点上分辨 prerelease:tool-subagent 守卫是 0.2.1-rc.2 落地的,
|
|
724
|
+
// rc.0 / rc.1 仍认内联形状,换 agentPreset 形态反而会被当未知键拒绝。正式版
|
|
725
|
+
// (prerelease null)与更高的 triple 一律新形态;prerelease 未知(缺省
|
|
726
|
+
// undefined = 旧调用方 / 旧测试只传三元组)保持原行为按新形态处理。
|
|
727
|
+
if (triple[0] === 0 && triple[1] === 2 && triple[2] === 1 && prerelease !== null && prerelease !== undefined) {
|
|
728
|
+
return comparePrerelease(prerelease, 'rc.2') >= 0
|
|
729
|
+
}
|
|
730
|
+
return true
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
/**
|
|
734
|
+
* 生成器标记第四行的角色形态取值(composeGenerated 的 isPresetRoleHost 同一
|
|
735
|
+
* 判定,探测不到按 inline 落盘——与保守生成侧一致,重生成后产物可再次对账)。
|
|
736
|
+
*/
|
|
737
|
+
export function presetRolesFlavorOf(hostVersionTriple, prerelease) {
|
|
738
|
+
return isPresetRoleHost(hostVersionTriple, prerelease) ? ROLES_AGENT_PRESET : ROLES_INLINE
|
|
739
|
+
}
|
|
740
|
+
|
|
595
741
|
/**
|
|
596
742
|
* 探测正在运行的宿主版本三元组:进程 argv 宿主版本(插件与宿主同进程时零开销
|
|
597
743
|
* 且反映正在运行的宿主)> PATH 上的 `dsh --version` > null。与
|
|
@@ -599,11 +745,26 @@ export const WORKER_WORKER_THREAD = 'worker-thread'
|
|
|
599
745
|
* 的磁盘形态没有可区分的单数包,探测不到就返回 null 由调用方按未知处理。
|
|
600
746
|
*/
|
|
601
747
|
export function detectHostVersionTriple() {
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
748
|
+
return detectHostVersionInfo()?.triple ?? null
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
/**
|
|
752
|
+
* 宿主版本信息(三元组 + prerelease 原文):探测链与 detectHostVersionTriple
|
|
753
|
+
* 完全同源(假版本钩子 > 进程 argv 宿主版本 > PATH 上的 dsh --version),
|
|
754
|
+
* 只是 prerelease 不剥——角色委派行形态门要分辫 0.2.1-rc.1 与 0.2.1-rc.2
|
|
755
|
+
*(守卫落地点),其余门(轨道 / worker)消费 triple 即可。
|
|
756
|
+
*/
|
|
757
|
+
export function detectHostVersionInfo() {
|
|
758
|
+
// 测试 / 诊断钩子(与 DSH_PAODING_CONFIG / DSH_PAODING_CWD 同款环境变量惯例):
|
|
759
|
+
// 显式假版本号最高优先,让无 dsh 进程的测试与排查能驱动版本门控分支;非法值
|
|
760
|
+
// 忽略、落回正常探测链。
|
|
761
|
+
const fake = process.env.DSH_PAODING_FAKE_HOST_VERSION
|
|
762
|
+
let versionText = null
|
|
763
|
+
if (typeof fake === 'string' && fake.trim() !== '') versionText = fake.trim()
|
|
764
|
+
if (versionText === null) versionText = hostVersionFromArgv()
|
|
765
|
+
if (versionText === null) versionText = dshVersionText()
|
|
766
|
+
if (versionText === null) return null
|
|
767
|
+
return parseVersionInfo(versionText)
|
|
607
768
|
}
|
|
608
769
|
|
|
609
770
|
/**
|
package/tools/lib/state.mjs
CHANGED
|
@@ -48,7 +48,7 @@ import { composeGenerated, extractMainAgentAllow } from './compose.mjs'
|
|
|
48
48
|
import { smartDefaults } from './alloc.mjs'
|
|
49
49
|
import {
|
|
50
50
|
buildPresetRowText,
|
|
51
|
-
|
|
51
|
+
detectHostVersionInfo,
|
|
52
52
|
detectPresetSystem,
|
|
53
53
|
listOrchestratorPresetIds,
|
|
54
54
|
presetRowId,
|
|
@@ -61,10 +61,11 @@ import {
|
|
|
61
61
|
/**
|
|
62
62
|
* 预设源文件清单(generateAndInstall 原样复制件;agent.cordis.yml 走重写,
|
|
63
63
|
* 不在此列)。collectState 的存在性检查与物化复制共用同一份,加源文件只改
|
|
64
|
-
* 这一处。delegation-labels.mjs
|
|
65
|
-
* 插件,声明行轨内联时按绝对 file: URL
|
|
64
|
+
* 这一处。restrict.mjs / delegation-labels.mjs / role-restrict.mjs 同生命周期:
|
|
65
|
+
* 都是本地 cordis 插件,声明行轨内联时按绝对 file: URL 引用(role-restrict.mjs
|
|
66
|
+
* 仅 0.2.1+ AgentPreset 形态产物引用,老宿主复制了不引用、无害)。
|
|
66
67
|
*/
|
|
67
|
-
export const PRESET_ASIS_FILES = ['preset.yml', 'restrict.mjs', 'delegation-labels.mjs']
|
|
68
|
+
export const PRESET_ASIS_FILES = ['preset.yml', 'restrict.mjs', 'delegation-labels.mjs', 'role-restrict.mjs']
|
|
68
69
|
|
|
69
70
|
/**
|
|
70
71
|
* 预设源文件存在性检查(collectState 第一步):全部源文件缺任一件即硬错误,
|
|
@@ -249,9 +250,13 @@ export async function collectState({ dshHome, configFile, profile = null, patche
|
|
|
249
250
|
//(见 preset-system.detectPresetSystem 的降级链)。
|
|
250
251
|
const presetSystem = await detectPresetSystem({ dshHome, runtimeSystem: runtimeFacts?.presetSystem ?? null })
|
|
251
252
|
|
|
252
|
-
//
|
|
253
|
-
// workflow worker 行的生成期形态判定(isPtcWorkerHost)与标记 worker
|
|
254
|
-
|
|
253
|
+
// 宿主版本信息(argv 宿主版本 > dsh --version > null,探测不到按未知处理):
|
|
254
|
+
// workflow worker 行的生成期形态判定(isPtcWorkerHost)与标记 worker 行取
|
|
255
|
+
// triple;角色委派行形态判定(isPresetRoleHost)另取 prerelease——0.2.1 守卫
|
|
256
|
+
// 是 0.2.1-rc.2 落地的,rc.0/rc.1 保持内联形状。
|
|
257
|
+
const hostVersion = detectHostVersionInfo()
|
|
258
|
+
const hostVersionTriple = hostVersion?.triple ?? null
|
|
259
|
+
const hostPrerelease = hostVersion?.prerelease ?? null
|
|
255
260
|
|
|
256
261
|
// 已落盘 preset 目录扫描(编排类):.agent-presets 下的 orchestrator 基础
|
|
257
262
|
// preset 与 orchestrator-<slug> 工作区专属 preset。扫描失败(目录尚不存在、
|
|
@@ -288,6 +293,8 @@ export async function collectState({ dshHome, configFile, profile = null, patche
|
|
|
288
293
|
presetSystem,
|
|
289
294
|
// 宿主版本三元组(null = 探测不到):workflow worker 行生成期形态判定用
|
|
290
295
|
hostVersionTriple,
|
|
296
|
+
// 宿主 prerelease 原文(null = 正式版 / 探测不到):角色委派行形态判定用
|
|
297
|
+
hostPrerelease,
|
|
291
298
|
// 主 persona 尾部追加的默认常量(配置键 main_agent_persona_extra 缺省值):
|
|
292
299
|
// 供 UI state(serializeState → /api/paoding/state)展示与「恢复默认」用。
|
|
293
300
|
mainPersonaExtraDefault: DEFAULT_MAIN_AGENT_PERSONA_EXTRA,
|
|
@@ -391,7 +398,7 @@ export function generateAndInstall(state, assignments, { dryRun = false, saveCon
|
|
|
391
398
|
// Compose the generated config; a composition error is a hard error.
|
|
392
399
|
//(这层包装无增值,让 composeGenerated 的原始错误直接上抛。)宿主版本三元组
|
|
393
400
|
// 驱动 workflow worker 行的生成期换行(≥0.1.6 → workflow-ptc,null 按现代宿主)。
|
|
394
|
-
const composed = composeGenerated(srcText, blocks, personaBlocks, assignments, inventory, restrictBase, mainAgentSkillMetas, state?.hostVersionTriple ?? null)
|
|
401
|
+
const composed = composeGenerated(srcText, blocks, personaBlocks, assignments, inventory, restrictBase, mainAgentSkillMetas, state?.hostVersionTriple ?? null, state?.hostPrerelease)
|
|
395
402
|
const generatedText = composed.text
|
|
396
403
|
const roleResults = composed.roleResults
|
|
397
404
|
|
|
@@ -533,8 +540,8 @@ export function generateAndInstall(state, assignments, { dryRun = false, saveCon
|
|
|
533
540
|
// declarative(0.1.7+):目录扫描机制已删,必须把本 preset 以一条 `- insert:`
|
|
534
541
|
// 声明行写进 home patch 托管块才算安装完成(preset 元信息与写 preset.yml 同
|
|
535
542
|
// 源:显示名取 displayName ?? SRC preset.yml 的 name,description / order 取
|
|
536
|
-
|
|
537
|
-
|
|
543
|
+
// SRC;restrict.mjs / delegation-labels.mjs / role-restrict.mjs 用绝对 file: URL——声明行没有
|
|
544
|
+
// 「相对 preset 目录」语义;role-restrict URL 仅供 0.2.1+ AgentPreset 形态产物的角色面行消费)。
|
|
538
545
|
// directory(≤0.1.6):上面落盘即完成;home patch 里若有残留托管声明块
|
|
539
546
|
//(宿主刚从 0.1.7 降级等场景)整块撤下自愈——残留声明行会让 profile 启动失败。
|
|
540
547
|
if (wrote) {
|
|
@@ -549,6 +556,7 @@ export function generateAndInstall(state, assignments, { dryRun = false, saveCon
|
|
|
549
556
|
agentYmlText: generatedText,
|
|
550
557
|
restrictFileUrl: pathToFileURL(path.join(dstDir, 'restrict.mjs')).href,
|
|
551
558
|
delegationLabelsFileUrl: pathToFileURL(path.join(dstDir, 'delegation-labels.mjs')).href,
|
|
559
|
+
roleRestrictFileUrl: pathToFileURL(path.join(dstDir, 'role-restrict.mjs')).href,
|
|
552
560
|
}),
|
|
553
561
|
])
|
|
554
562
|
} else if (readHomePatchRows(dshHome).length > 0) {
|
package/tools/lib/util.mjs
CHANGED
|
@@ -24,9 +24,11 @@ export const PROFILE_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9._-]*$/
|
|
|
24
24
|
* 主 agent persona 尾部追加规则(默认值)。
|
|
25
25
|
* 用户可在配置 UI 的「人设追加」里编辑/清空;配置键 main_agent_persona_extra:
|
|
26
26
|
* null/缺省 = 用本默认;'' = 明确清空(不追加);其他字符串 = 覆盖。
|
|
27
|
-
* {{cwd}}
|
|
27
|
+
* 禁用 {{cwd}} 占位:该文本拼进部署主代理 persona,DSH 0.2.1 的
|
|
28
|
+
* deployment:persona-prefix 段只注册 provider/model,未知变量直接抛错;
|
|
29
|
+
* 「当前工作目录」由宿主 env 上下文提供,无需模板变量。
|
|
28
30
|
*/
|
|
29
|
-
export const DEFAULT_MAIN_AGENT_PERSONA_EXTRA = `Codegraph MCP default project may be a DIFFERENT repository than
|
|
31
|
+
export const DEFAULT_MAIN_AGENT_PERSONA_EXTRA = `Codegraph MCP default project may be a DIFFERENT repository than your current working directory. Never call codegraph_* tools without passing projectPath = the absolute path of your current working directory. If that directory has no .codegraph index, fall back to glob/grep/read directly and do not loop or comment on project mismatches.`
|
|
30
32
|
|
|
31
33
|
export function warn(message) {
|
|
32
34
|
console.warn(`warning: ${message}`)
|