dsh-vibe-math 2.0.21 → 2.1.0
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/LICENSE +21 -21
- package/README.md +63 -8
- package/RELEASE-NOTES-2.0.22.md +112 -0
- package/RELEASE-NOTES-2.1.0.md +143 -0
- package/cordis.patch.yml +10 -9
- package/installer.js +315 -290
- package/package.json +17 -6
- package/vibe-math-v2/agent.cordis.yml +256 -212
- package/vibe-math-v2/preset.yml +2 -2
- package/vibe-math-v2/vibe-math-v2.js +243 -36
- package/vibe-math-v3/agent.cordis.yml +278 -226
- package/vibe-math-v3/preset.yml +2 -2
- package/vibe-math-v3/vibe-math-v3.js +2938 -2631
- package/vibe-math-v3//345/256/236/347/216/260/346/226/271/346/241/210.md +540 -540
- package/vibe-math-v4/agent.cordis.yml +300 -239
- package/vibe-math-v4/preset.yml +2 -2
- package/vibe-math-v4/vibe-math-v4.js +1275 -1111
- package/vibe-math-v4//345/256/236/347/216/260/346/226/271/346/241/210.md +647 -636
- package/vibe-math-v5/agent.cordis.yml +342 -0
- package/vibe-math-v5/preset.yml +2 -0
- package/vibe-math-v5/vibe-math-v5.js +3459 -0
- package/vibe-math-v5//345/256/236/347/216/260/346/226/271/346/241/210.md +1133 -0
package/installer.js
CHANGED
|
@@ -1,290 +1,315 @@
|
|
|
1
|
-
// dsh-vibe-math merged bundle installer — VERSIONED AUTO-UPDATE.
|
|
2
|
-
// When this bundle is installed (e.g. `dsh plugin add dsh-vibe-math` or from the
|
|
3
|
-
// dsh-market), this plugin copies ALL THREE agent presets out of the package into
|
|
4
|
-
// the DSH preset root, so the user immediately gets three presets in the picker:
|
|
5
|
-
// vibe-math-v2/ (probability-driven architecture)
|
|
6
|
-
// vibe-math-v3/ (THIRD-generation: paper-style Markdown knowledge base +
|
|
7
|
-
// planner-agent scheduling + universal theory/method library)
|
|
8
|
-
// vibe-math-v4/ (FOURTH-generation: persistent self-organizing resident
|
|
9
|
-
// subagents — message bus / meetings / unanimous-consensus
|
|
10
|
-
// verification / per-resident libraries)
|
|
11
|
-
//
|
|
12
|
-
// (vibe-math-v1 — the classic pipeline — was removed at v2.0.0; this bundle now
|
|
13
|
-
// ships v2/v3/v4 only.)
|
|
14
|
-
//
|
|
15
|
-
// UPDATE POLICY (state recorded in <presetRoot>/.vibe-math-installed.json):
|
|
16
|
-
// - baseline (no state file — e.g. upgrading from an installer that predates
|
|
17
|
-
// this mechanism): every existing owned file is refreshed to the current
|
|
18
|
-
// package version and recorded as package-owned (user policy: auto-update
|
|
19
|
-
// old installs; any manual edits made before this baseline are overwritten
|
|
20
|
-
// once — from then on edits are protected).
|
|
21
|
-
// - upgrade (recorded version != current package.json version): every owned
|
|
22
|
-
// file that is byte-identical to the previously installed copy (i.e. NOT
|
|
23
|
-
// user-edited since) is overwritten with the new version; user-edited files
|
|
24
|
-
// are preserved and reported via the logger.
|
|
25
|
-
// - same version: no-op (idempotent). Missing files are ALWAYS restored.
|
|
26
|
-
// - force a full refresh at any time: delete the preset dirs and restart DSH.
|
|
27
|
-
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync, unlinkSync, rmdirSync } from 'node:fs'
|
|
28
|
-
import { createRequire } from 'node:module'
|
|
29
|
-
import { createHash } from 'node:crypto'
|
|
30
|
-
import { homedir } from 'node:os'
|
|
31
|
-
import { dirname, join } from 'node:path'
|
|
32
|
-
import { fileURLToPath } from 'node:url'
|
|
33
|
-
|
|
34
|
-
export const name = 'vibe-math-preset-installer'
|
|
35
|
-
|
|
36
|
-
const PRESETS = [
|
|
37
|
-
{
|
|
38
|
-
src: 'vibe-math-v2',
|
|
39
|
-
dst: 'vibe-math-v2',
|
|
40
|
-
files: ['agent.cordis.yml', 'preset.yml', 'vibe-math-v2.js', '实现方案.md'],
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
src: 'vibe-math-v3',
|
|
44
|
-
dst: 'vibe-math-v3',
|
|
45
|
-
files: ['agent.cordis.yml', 'preset.yml', 'vibe-math-v3.js', '实现方案.md'],
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
src: 'vibe-math-v4',
|
|
49
|
-
dst: 'vibe-math-v4',
|
|
50
|
-
files: ['agent.cordis.yml', 'preset.yml', 'vibe-math-v4.js', '实现方案.md'],
|
|
51
|
-
},
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
//
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
//
|
|
83
|
-
//
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
//
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
1
|
+
// dsh-vibe-math merged bundle installer — VERSIONED AUTO-UPDATE.
|
|
2
|
+
// When this bundle is installed (e.g. `dsh plugin add dsh-vibe-math` or from the
|
|
3
|
+
// dsh-market), this plugin copies ALL THREE agent presets out of the package into
|
|
4
|
+
// the DSH preset root, so the user immediately gets three presets in the picker:
|
|
5
|
+
// vibe-math-v2/ (probability-driven architecture)
|
|
6
|
+
// vibe-math-v3/ (THIRD-generation: paper-style Markdown knowledge base +
|
|
7
|
+
// planner-agent scheduling + universal theory/method library)
|
|
8
|
+
// vibe-math-v4/ (FOURTH-generation: persistent self-organizing resident
|
|
9
|
+
// subagents — message bus / meetings / unanimous-consensus
|
|
10
|
+
// verification / per-resident libraries)
|
|
11
|
+
//
|
|
12
|
+
// (vibe-math-v1 — the classic pipeline — was removed at v2.0.0; this bundle now
|
|
13
|
+
// ships v2/v3/v4 only.)
|
|
14
|
+
//
|
|
15
|
+
// UPDATE POLICY (state recorded in <presetRoot>/.vibe-math-installed.json):
|
|
16
|
+
// - baseline (no state file — e.g. upgrading from an installer that predates
|
|
17
|
+
// this mechanism): every existing owned file is refreshed to the current
|
|
18
|
+
// package version and recorded as package-owned (user policy: auto-update
|
|
19
|
+
// old installs; any manual edits made before this baseline are overwritten
|
|
20
|
+
// once — from then on edits are protected).
|
|
21
|
+
// - upgrade (recorded version != current package.json version): every owned
|
|
22
|
+
// file that is byte-identical to the previously installed copy (i.e. NOT
|
|
23
|
+
// user-edited since) is overwritten with the new version; user-edited files
|
|
24
|
+
// are preserved and reported via the logger.
|
|
25
|
+
// - same version: no-op (idempotent). Missing files are ALWAYS restored.
|
|
26
|
+
// - force a full refresh at any time: delete the preset dirs and restart DSH.
|
|
27
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync, unlinkSync, rmdirSync } from 'node:fs'
|
|
28
|
+
import { createRequire } from 'node:module'
|
|
29
|
+
import { createHash } from 'node:crypto'
|
|
30
|
+
import { homedir } from 'node:os'
|
|
31
|
+
import { dirname, join } from 'node:path'
|
|
32
|
+
import { fileURLToPath } from 'node:url'
|
|
33
|
+
|
|
34
|
+
export const name = 'vibe-math-preset-installer'
|
|
35
|
+
|
|
36
|
+
const PRESETS = [
|
|
37
|
+
{
|
|
38
|
+
src: 'vibe-math-v2',
|
|
39
|
+
dst: 'vibe-math-v2',
|
|
40
|
+
files: ['agent.cordis.yml', 'preset.yml', 'vibe-math-v2.js', '实现方案.md'],
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
src: 'vibe-math-v3',
|
|
44
|
+
dst: 'vibe-math-v3',
|
|
45
|
+
files: ['agent.cordis.yml', 'preset.yml', 'vibe-math-v3.js', '实现方案.md'],
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
src: 'vibe-math-v4',
|
|
49
|
+
dst: 'vibe-math-v4',
|
|
50
|
+
files: ['agent.cordis.yml', 'preset.yml', 'vibe-math-v4.js', '实现方案.md'],
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
src: 'vibe-math-v5',
|
|
54
|
+
dst: 'vibe-math-v5',
|
|
55
|
+
files: ['agent.cordis.yml', 'preset.yml', 'vibe-math-v5.js', '实现方案.md'],
|
|
56
|
+
},
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
const STATE_FILE = '.vibe-math-installed.json'
|
|
60
|
+
|
|
61
|
+
function sha256(buf) { return createHash('sha256').update(buf).digest('hex') }
|
|
62
|
+
|
|
63
|
+
function readState(path) {
|
|
64
|
+
try {
|
|
65
|
+
const raw = readFileSync(path, 'utf8')
|
|
66
|
+
const obj = JSON.parse(raw)
|
|
67
|
+
if (obj && typeof obj === 'object' && obj.files && typeof obj.files === 'object') return obj
|
|
68
|
+
} catch (e) { /* missing or corrupt — treat as no state (baseline) */ }
|
|
69
|
+
return null
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function writeState(path, state) {
|
|
73
|
+
try {
|
|
74
|
+
const tmp = path + '.tmp'
|
|
75
|
+
writeFileSync(tmp, JSON.stringify(state, null, 2) + '\n', 'utf8')
|
|
76
|
+
renameSync(tmp, path)
|
|
77
|
+
} catch (e) {
|
|
78
|
+
// best-effort: state persistence failure must not break the copy step
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// DSH 适配性自检(能力检测,而非版本号——DSH 不向插件暴露版本)。
|
|
83
|
+
// 检查 preset 运行时需要的宿主服务与关键 API 形状是否可用,缺失时打 warning。
|
|
84
|
+
// Best-effort DSH host-version detection. DSH does NOT expose its version through a documented
|
|
85
|
+
// service/context property or a guaranteed env var, so we probe in order: an explicit env var
|
|
86
|
+
// (future-proofing), then the installed @deepseek-ai/dsh package.json. This is layout-dependent
|
|
87
|
+
// (works for a typical global install where @deepseek-ai/dsh is a sibling of this plugin); when it
|
|
88
|
+
// cannot resolve, the capability self-check below is still the authoritative gate.
|
|
89
|
+
const __require = createRequire(import.meta.url)
|
|
90
|
+
function detectDshVersion() {
|
|
91
|
+
try { const v = process.env.DSH_VERSION; if (v && String(v).trim()) return String(v).trim() } catch (e) {}
|
|
92
|
+
try {
|
|
93
|
+
const p = __require.resolve('@deepseek-ai/dsh/package.json')
|
|
94
|
+
const v = (JSON.parse(readFileSync(p, 'utf8')).version || '').trim()
|
|
95
|
+
if (v) return v
|
|
96
|
+
} catch (e) { /* host package not resolvable from here — rely on capability check */ }
|
|
97
|
+
return undefined
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function checkHostCapabilities(ctx, logger) {
|
|
101
|
+
const problems = []
|
|
102
|
+
// 1) DSH version compatibility (best-effort, only when the version is detectable).
|
|
103
|
+
// Declared under package.json dsh.compatibility.dshReleases (per the DSH STORE contract):
|
|
104
|
+
// each full DSH release maps to 'compatible' | 'incompatible' | 'unknown'. A version that is
|
|
105
|
+
// absent or 'unknown' is a soft warning; 'incompatible' is a hard "please use X" message.
|
|
106
|
+
let dshRel = {}
|
|
107
|
+
try { const m = JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.url)), 'package.json'), 'utf8')); dshRel = (m.dsh && m.dsh.compatibility && m.dsh.compatibility.dshReleases) || {} } catch (e) {}
|
|
108
|
+
const supported = Object.keys(dshRel).sort()
|
|
109
|
+
const dshVersion = detectDshVersion()
|
|
110
|
+
if (dshVersion) {
|
|
111
|
+
const status = dshRel[dshVersion]
|
|
112
|
+
if (status === 'incompatible') {
|
|
113
|
+
problems.push('当前 DSH 版本 v' + dshVersion + ' 被本包声明为 incompatible;请使用 ' + supported.join(' / ') + '。')
|
|
114
|
+
} else if (status === undefined || status === 'unknown') {
|
|
115
|
+
problems.push('当前 DSH 版本 v' + dshVersion + ' 尚未被本包声明为兼容(dshReleases 仅声明 ' + supported.join(' / ') + ');建议使用 ' + supported.join(' / ') + ',或将该版本在 dshReleases 中标注后再自行验证。')
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// 2) capability self-check (the authoritative mounting gate; also covers hosts whose version
|
|
119
|
+
// could not be read). subagents / agents / tools / commands / fs shapes + v4 capabilities.
|
|
120
|
+
// `required: true` services are the mounting gate; the rest are optional services the presets
|
|
121
|
+
// read with ctx.get(). Their absence does not stop a mount but degrades SILENTLY, so report
|
|
122
|
+
// them instead of letting the field discover them: without `subprocess` no directory-creation
|
|
123
|
+
// shell runs, without `sandboxPolicy` writes carry no explicit fence, without `compaction` the
|
|
124
|
+
// v4 real /compact path is inert.
|
|
125
|
+
const checks = [
|
|
126
|
+
// subagents 服务的续做/唤醒方法是 sendMessage(sender, targetId, content, {signal});
|
|
127
|
+
// followup 不是 subagents 服务的方法(它只是 Agent 对象方法)。同时探测两者,能用一个即可。
|
|
128
|
+
{ svc: 'subagents', methods: ['startContinuable', 'interrupt'], required: true },
|
|
129
|
+
{ svc: 'agents', methods: ['roots'], required: true },
|
|
130
|
+
{ svc: 'tools', methods: ['register'], required: true },
|
|
131
|
+
{ svc: 'commands', methods: ['register'], required: true },
|
|
132
|
+
{ svc: 'fs', methods: ['resolve', 'stat', 'readText', 'writeText', 'listDir'], required: true },
|
|
133
|
+
{ svc: 'subprocess', methods: ['spawn'], required: false },
|
|
134
|
+
{ svc: 'sandboxPolicy', methods: ['resolve'], required: false },
|
|
135
|
+
{ svc: 'compaction', methods: ['compactIfNeeded'], required: false },
|
|
136
|
+
// v5 keeps its institute state in a HOST-ONLY session projection unit, so it wants
|
|
137
|
+
// the projection registry and the session store. Both are mounted by dsh-base; if
|
|
138
|
+
// either is absent v5 falls back to a hardened JSON state file, so this is a
|
|
139
|
+
// degradation rather than a mounting gate.
|
|
140
|
+
{ svc: 'sessionProjections', methods: ['register', 'stateOf'], required: false },
|
|
141
|
+
{ svc: 'sessions', methods: ['flush'], required: false },
|
|
142
|
+
]
|
|
143
|
+
const degradations = []
|
|
144
|
+
for (let i = 0; i < checks.length; i++) {
|
|
145
|
+
const svc = checks[i].svc
|
|
146
|
+
const methods = checks[i].methods
|
|
147
|
+
const required = checks[i].required === true
|
|
148
|
+
const report = required ? ((m) => problems.push(m)) : ((m) => degradations.push(m))
|
|
149
|
+
let s
|
|
150
|
+
try { s = (ctx && ctx.get) ? ctx.get(svc) : undefined } catch (e) { s = undefined }
|
|
151
|
+
if (s === undefined) { report('宿主缺少服务 ' + svc); continue }
|
|
152
|
+
for (let j = 0; j < methods.length; j++) {
|
|
153
|
+
if (typeof s[methods[j]] !== 'function') report(svc + '.' + methods[j] + ' 不可用(宿主版本可能过旧)')
|
|
154
|
+
}
|
|
155
|
+
// subagents continuation (wake) API: sendMessage (modern) OR followup (legacy) must exist.
|
|
156
|
+
if (svc === 'subagents' && typeof s.sendMessage !== 'function' && typeof s.followup !== 'function') {
|
|
157
|
+
problems.push('subagents 缺少续做/唤醒方法(需 sendMessage 或 followup 至少其一)')
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// fs API shape: DSH 0.1.1 起 resolve 返回 {targetKey, displayPath} 对象(旧版返回字符串路径)
|
|
161
|
+
try {
|
|
162
|
+
const f = (ctx && ctx.get) ? ctx.get('fs') : undefined
|
|
163
|
+
if (f && typeof f.resolve === 'function') {
|
|
164
|
+
const r = await f.resolve('x', { cwd: process.cwd() })
|
|
165
|
+
if (typeof r !== 'object' || r === null || typeof r.targetKey !== 'string') {
|
|
166
|
+
problems.push('fs.resolve 返回形状不符(期望 {targetKey, displayPath},v3/v4 预设要求 DSH ≥ 0.1.1)')
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
} catch (e) { problems.push('fs.resolve 能力检测失败:' + String((e && e.message) || e)) }
|
|
170
|
+
// v4 依赖 subagents.startContinuable 的 agentOptions / toolFilter 能力(DSH 0.1.2 起由
|
|
171
|
+
// dsh-subagent 声明 SubagentCapabilities.agentOptions;spawn/fork 进程内 provider 均支持。
|
|
172
|
+
// 缺省 provider 名按 spawn 探测;探测失败不视为致命(等价于回退到再试一次、只警告)。
|
|
173
|
+
try {
|
|
174
|
+
const sa = (ctx && ctx.get) ? ctx.get('subagents') : undefined
|
|
175
|
+
if (sa && typeof sa.list === 'function') {
|
|
176
|
+
const names = (sa.list ? sa.list() : [])
|
|
177
|
+
const name = names.indexOf('spawn') !== -1 ? 'spawn' : (names[0] || '')
|
|
178
|
+
if (name && typeof sa.getProvider === 'function') {
|
|
179
|
+
const cap = (sa.getProvider(name) || {}).capabilities
|
|
180
|
+
if (cap && cap.agentOptions === false) problems.push('subagents provider "' + name + '" 不支持 agentOptions(v4 指定常驻模型/路由需要)')
|
|
181
|
+
if (cap && cap.toolFilter === false) problems.push('subagents provider "' + name + '" 不支持 toolFilter(v4 常驻工具权限需要)')
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
} catch (e) { /* 探测失败不致命 */ }
|
|
185
|
+
if (degradations.length > 0) {
|
|
186
|
+
logger?.warn?.('[dsh-vibe-math] 可选宿主服务缺失,功能会静默降级(不影响挂载):' + degradations.join(';') + '。subprocess 缺失则无法用 shell 创建目录树(仅靠 fs 自动建父目录兜底);sandboxPolicy 缺失则插件写入不带显式围栏;compaction 缺失则 v4 的真实 /compact 路径与 v5 的真实压缩不生效(v5 回退到自述浓缩);sessionProjections 缺失则 v5 的研究所状态回退到加固 JSON 文件(权威源从会话日志投影变为 State/<institute>.v5state.json,跨进程恢复能力下降)。')
|
|
187
|
+
}
|
|
188
|
+
if (problems.length > 0) {
|
|
189
|
+
logger?.warn?.('[dsh-vibe-math] 宿主自检:' + problems.length + ' 项不满足(' + problems.join(';') + ')。v2/v3/v4/v5 预设依赖这些宿主服务/API,旧版或未经声明兼容的 DSH 可能无法挂载' + (dshVersion ? '(当前检测到 DSH v' + dshVersion + ',本包适配 ' + (supported.length ? supported.join(' / ') : '(未声明)') + ')' : '') + '。')
|
|
190
|
+
} else {
|
|
191
|
+
logger?.info?.('[dsh-vibe-math] 宿主自检通过:subagents / agents / tools / commands / fs 服务及关键 API 均可用' + (degradations.length === 0 ? ',可选服务 subprocess / sandboxPolicy / compaction / sessionProjections / sessions 亦齐备' : '(可选服务有缺失,见上方警告)') + (dshVersion ? '(当前 DSH v' + dshVersion + ',本包已声明兼容 ' + supported.join(' / ') + ')' : '') + '。')
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export async function apply(ctx) {
|
|
196
|
+
const logger = ctx && ctx.logger
|
|
197
|
+
try {
|
|
198
|
+
const dshHome = process.env.DSH_HOME || join(homedir(), '.dsh')
|
|
199
|
+
const here = dirname(fileURLToPath(import.meta.url))
|
|
200
|
+
const presetRoot = join(dshHome, '.agent-presets')
|
|
201
|
+
const stateFile = join(presetRoot, STATE_FILE)
|
|
202
|
+
|
|
203
|
+
// current package version (the source of truth for "is this an upgrade?")
|
|
204
|
+
let pkgVersion = ''
|
|
205
|
+
try { pkgVersion = String((JSON.parse(readFileSync(join(here, 'package.json'), 'utf8')).version) || '') } catch (e) { pkgVersion = '' }
|
|
206
|
+
|
|
207
|
+
const state = readState(stateFile)
|
|
208
|
+
const prevFiles = (state && state.files) || {}
|
|
209
|
+
const isUpgrade = state !== null && pkgVersion !== '' && state.version !== pkgVersion
|
|
210
|
+
const isBaseline = state === null // no recorded history → refresh everything (user policy: auto-update old installs)
|
|
211
|
+
|
|
212
|
+
const nextFiles = {}
|
|
213
|
+
let installed = 0, updated = 0, kept = 0
|
|
214
|
+
const keptList = []
|
|
215
|
+
|
|
216
|
+
for (const p of PRESETS) {
|
|
217
|
+
const srcDir = join(here, p.src)
|
|
218
|
+
const dstDir = join(presetRoot, p.dst)
|
|
219
|
+
if (!existsSync(srcDir)) continue
|
|
220
|
+
mkdirSync(dstDir, { recursive: true })
|
|
221
|
+
for (const f of p.files) {
|
|
222
|
+
const s = join(srcDir, f)
|
|
223
|
+
const d = join(dstDir, f)
|
|
224
|
+
if (!existsSync(s)) continue
|
|
225
|
+
const key = p.src + '/' + f
|
|
226
|
+
const cur = readFileSync(s)
|
|
227
|
+
const curHash = sha256(cur)
|
|
228
|
+
if (!existsSync(d)) {
|
|
229
|
+
// missing file: always restore, whatever the version
|
|
230
|
+
writeFileSync(d, cur)
|
|
231
|
+
installed += 1
|
|
232
|
+
nextFiles[key] = { hash: curHash, provenance: 'package' }
|
|
233
|
+
continue
|
|
234
|
+
}
|
|
235
|
+
const destHash = sha256(readFileSync(d))
|
|
236
|
+
if (isBaseline) {
|
|
237
|
+
// no recorded history: refresh to the current package (one-time; edits
|
|
238
|
+
// made before this mechanism are overwritten, later edits are protected)
|
|
239
|
+
if (destHash === curHash) { nextFiles[key] = { hash: curHash, provenance: 'package' } }
|
|
240
|
+
else { writeFileSync(d, cur); updated += 1; nextFiles[key] = { hash: curHash, provenance: 'package' } }
|
|
241
|
+
continue
|
|
242
|
+
}
|
|
243
|
+
const prev = prevFiles[key]
|
|
244
|
+
const prevRec = (prev && typeof prev === 'object') ? prev : { hash: prev, provenance: 'package' }
|
|
245
|
+
const prevProv = (prevRec.provenance === 'user') ? 'user' : 'package' // 未知来源按包文件处理
|
|
246
|
+
if (prevProv === 'package' && destHash === prevRec.hash) {
|
|
247
|
+
// 包文件且未被改动 → 可安全升级(内容相同则跳过写入)
|
|
248
|
+
if (destHash !== curHash) { writeFileSync(d, cur); updated += 1 }
|
|
249
|
+
nextFiles[key] = { hash: curHash, provenance: 'package' }
|
|
250
|
+
} else if (prevProv === 'user') {
|
|
251
|
+
// 用户持有 → 永不覆盖
|
|
252
|
+
kept += 1
|
|
253
|
+
if (isUpgrade) keptList.push(key + ' (用户持有)')
|
|
254
|
+
nextFiles[key] = { hash: destHash, provenance: 'user' }
|
|
255
|
+
} else {
|
|
256
|
+
// 包文件但自上次安装后已被用户改动
|
|
257
|
+
kept += 1
|
|
258
|
+
if (isUpgrade) keptList.push(key + ' (已修改)')
|
|
259
|
+
nextFiles[key] = { hash: destHash, provenance: 'user' }
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Clean up preset dirs that this bundle NO LONGER manages (e.g. vibe-math-v1 after it was
|
|
265
|
+
// removed at v2.0.0). The copy loop only adds/updates PRESETS; it never deletes a preset that
|
|
266
|
+
// was dropped, so an old removed preset would linger in the picker forever. Here we remove the
|
|
267
|
+
// files this installer previously recorded as package-owned under a prefix that is no longer in
|
|
268
|
+
// PRESETS, then drop the dir if it became empty. User-owned files (provenance 'user') are kept.
|
|
269
|
+
const currentPrefixes = new Set(PRESETS.map(p => p.src + '/'))
|
|
270
|
+
let removedFiles = 0
|
|
271
|
+
let removedDirs = []
|
|
272
|
+
const stale = new Map() // prefix -> [keys]
|
|
273
|
+
for (const key of Object.keys(prevFiles)) {
|
|
274
|
+
const slash = key.indexOf('/')
|
|
275
|
+
if (slash === -1) continue
|
|
276
|
+
const prefix = key.slice(0, slash + 1)
|
|
277
|
+
if (currentPrefixes.has(prefix)) continue
|
|
278
|
+
if (!stale.has(prefix)) stale.set(prefix, [])
|
|
279
|
+
stale.get(prefix).push(key)
|
|
280
|
+
}
|
|
281
|
+
for (const [prefix, keys] of stale) {
|
|
282
|
+
let dirEmpty = true
|
|
283
|
+
for (const key of keys) {
|
|
284
|
+
const rec = (prevFiles[key] && typeof prevFiles[key] === 'object') ? prevFiles[key] : { provenance: 'package' }
|
|
285
|
+
if (rec.provenance === 'user') { dirEmpty = false; continue } // 用户文件 → 保留
|
|
286
|
+
const f = join(presetRoot, key)
|
|
287
|
+
if (existsSync(f)) { try { unlinkSync(f); removedFiles += 1 } catch (e) {} }
|
|
288
|
+
if (existsSync(f)) dirEmpty = false
|
|
289
|
+
}
|
|
290
|
+
const dir = join(presetRoot, prefix.slice(0, -1))
|
|
291
|
+
if (dirEmpty && existsSync(dir)) { try { rmdirSync(dir); removedDirs.push(dir) } catch (e) {} }
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
writeState(stateFile, { version: pkgVersion, files: nextFiles, updatedAt: Date.now() })
|
|
295
|
+
|
|
296
|
+
if (removedFiles > 0 || removedDirs.length > 0) {
|
|
297
|
+
logger?.info?.('[dsh-vibe-math] preset cleanup: removed ' + removedFiles + ' file(s) from ' + removedDirs.length + ' stale preset dir(s) (' + removedDirs.map(d => d.split(/[\\/]/).pop()).join(', ') + ') that are no longer shipped.')
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (isUpgrade) {
|
|
301
|
+
logger?.info?.('[dsh-vibe-math] preset auto-update: version ' + (state.version || '(none)') + ' → ' + pkgVersion +
|
|
302
|
+
' — 新增 ' + installed + ' 个文件,更新 ' + updated + ' 个文件' +
|
|
303
|
+
(kept > 0 ? ',保留 ' + kept + ' 个未覆盖文件(' + keptList.join('; ') + ')' : '') +
|
|
304
|
+
'。新版本 preset 将在新会话生效。')
|
|
305
|
+
} else if (isBaseline) {
|
|
306
|
+
logger?.info?.('[dsh-vibe-math] preset baseline: refreshed ' + (installed + updated) + ' file(s) to v' + pkgVersion +
|
|
307
|
+
' — 已启用自动更新(后续版本升级将自动替换未被手动修改的 preset 文件)。')
|
|
308
|
+
} else if (installed > 0) {
|
|
309
|
+
logger?.info?.('[dsh-vibe-math] restored ' + installed + ' missing preset file(s)')
|
|
310
|
+
}
|
|
311
|
+
await checkHostCapabilities(ctx, logger)
|
|
312
|
+
} catch (err) {
|
|
313
|
+
logger?.warn?.('[dsh-vibe-math] preset install/update failed: %s', String((err && err.message) || err))
|
|
314
|
+
}
|
|
315
|
+
}
|