@dsh-suite/plugin-deus 0.2.0 → 0.3.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/README.md +2 -0
- package/lib/client.js +88 -2
- package/lib/index.js +44 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -26,6 +26,8 @@
|
|
|
26
26
|
- 安装两个锚定 agent presets 到 `~/.dsh/.agent-presets/`(**窄锚** 2 工具 ~90% 触发 / **宽锚** ~8 工具 ~65% 换可用性),设置 > Agent presets 或会话 preset 选择器里直接选用,构成全程恒定;
|
|
27
27
|
- 对 deus/minimal preset 会话**逐轮判定指纹**,面板「锚定维持」区实时显示每轮指纹与漂移状态;
|
|
28
28
|
- 对话页 dock 出现 `⚓ 锚定维持中` 状态 chip;**漂移时变红**,点击发送重锚提示词(可开「漂移自动重锚」);重锚轮日志以 `reanchor` 模式独立记录,便于对比重锚前后维持率。
|
|
29
|
+
6. **未锚定引导(v0.3)** — 当前会话不在 deus/minimal 系 preset 时,dock 显示灰态 `◌ 非神版会话` chip,点击展开切换指引(把「25 工具会话注入≈0%」的实测结论落地为产品引导)。
|
|
30
|
+
7. **统计导出与分享(v0.3)** — 面板一键导出 CSV、**复制 Markdown 摘要**(Wilson CI 表格 + 诚实声明 + 包链接,可直接发帖),并内嵌近期神版率迷你趋势图(最近 40 条日志分桶折线)。
|
|
29
31
|
|
|
30
32
|
## 安装 / Install
|
|
31
33
|
|
package/lib/client.js
CHANGED
|
@@ -42,6 +42,9 @@ window.__ModuleLoader__.load({
|
|
|
42
42
|
colSession: '会话', colPreset: 'preset', colTurns: '轮次', colGodRate: '神版率', colLastFp: '最新指纹', colAnchorState: '状态',
|
|
43
43
|
anchoredOn: '锚定维持中', drifted: '漂移→非神版', reanchor: '⚓ 重锚', autoReanchor: '漂移自动重锚',
|
|
44
44
|
reanchorSent: '已发送重锚提示', dockAnchored: '锚定维持中',
|
|
45
|
+
unanchored: '◌ 非神版会话', unanchoredTitle: '当前会话不是锚定 preset——实测 25 工具会话注入触发率≈0%。点击查看怎么切换',
|
|
46
|
+
guideText: '实测结论:standard(25 工具)会话里文本注入几乎无法触发神版(0%)。想用神版:新建会话时在 Agent preset 选择器选「神模扳机 · 窄锚 / 宽锚」(实测触发率 ~90% / ~65%)。',
|
|
47
|
+
copyMd: '复制 Markdown 摘要', mdCopied: '已复制 ✓ 去发帖吧', trend: '近期神版率趋势',
|
|
45
48
|
}
|
|
46
49
|
const en = {
|
|
47
50
|
nav: 'Deus Trigger', sub: 'Minimal-prompt trigger bench · inject → classify → stats',
|
|
@@ -66,6 +69,9 @@ window.__ModuleLoader__.load({
|
|
|
66
69
|
colSession: 'Session', colPreset: 'preset', colTurns: 'turns', colGodRate: 'god rate', colLastFp: 'latest', colAnchorState: 'state',
|
|
67
70
|
anchoredOn: 'anchored', drifted: 'drifted', reanchor: '⚓ Re-anchor', autoReanchor: 'Auto re-anchor on drift',
|
|
68
71
|
reanchorSent: 'Re-anchor nudge sent', dockAnchored: 'anchored',
|
|
72
|
+
unanchored: '◌ Not a deus session', unanchoredTitle: 'This session is not on an anchored preset — injection measured ≈0% effective on 25-tool sessions. Click for how to switch',
|
|
73
|
+
guideText: 'Measured: text injection barely triggers god-mode in standard (25-tool) sessions (0%). To get it: start a new session and pick "Deus Trigger · narrow / wide anchor" in the Agent preset picker (~90% / ~65% measured trigger rate).',
|
|
74
|
+
copyMd: 'Copy Markdown summary', mdCopied: 'Copied ✓ go post it', trend: 'Recent god-rate trend',
|
|
69
75
|
}
|
|
70
76
|
|
|
71
77
|
const S = {
|
|
@@ -193,6 +199,8 @@ window.__ModuleLoader__.load({
|
|
|
193
199
|
h('div', { style: S.h2 }, t('stats')),
|
|
194
200
|
h('div', { style: S.status }, t('statsHint')),
|
|
195
201
|
h('div', { style: { ...S.status, color: '#7d8590' } }, t('measured')),
|
|
202
|
+
h(Sparkline, { entries }),
|
|
203
|
+
entries.length >= 4 ? h('div', { style: { ...S.status, fontSize: '10px' } }, t('trend')) : null,
|
|
196
204
|
stats && stats.modes.length > 0
|
|
197
205
|
? h('table', { style: S.table },
|
|
198
206
|
h('thead', null, h('tr', null,
|
|
@@ -209,6 +217,15 @@ window.__ModuleLoader__.load({
|
|
|
209
217
|
: h('div', { style: S.status }, t('noData')),
|
|
210
218
|
h('div', { style: S.row },
|
|
211
219
|
h('a', { href: '/deus/log.csv', download: 'deus-log.csv', style: { ...S.btn, textDecoration: 'none', display: 'inline-block' } }, '⬇ ' + t('exportCsv')),
|
|
220
|
+
stats ? h('button', {
|
|
221
|
+
style: copied === '__md' ? S.btnActive : S.btn,
|
|
222
|
+
onClick: async () => {
|
|
223
|
+
try {
|
|
224
|
+
await navigator.clipboard.writeText(buildMarkdownSummary(stats))
|
|
225
|
+
setCopied('__md'); setTimeout(() => setCopied(''), 2500)
|
|
226
|
+
} catch {}
|
|
227
|
+
},
|
|
228
|
+
}, '📋 ' + (copied === '__md' ? t('mdCopied') : t('copyMd'))) : null,
|
|
212
229
|
h('button', { style: { ...S.btn, color: '#f85149', borderColor: '#f85149' }, onClick: resetLog }, '🗑 ' + t('resetLog')),
|
|
213
230
|
),
|
|
214
231
|
|
|
@@ -276,16 +293,25 @@ window.__ModuleLoader__.load({
|
|
|
276
293
|
try { return window.localStorage.getItem('deus.autoReanchor') === '1' } catch { return false }
|
|
277
294
|
})
|
|
278
295
|
const [reanchorMsg, setReanchorMsg] = useState('')
|
|
296
|
+
// v0.3 未锚定引导:当前会话的 agent preset(宿主读会话日志头行)
|
|
297
|
+
const [sessPreset, setSessPreset] = useState(null)
|
|
298
|
+
const [showGuide, setShowGuide] = useState(false)
|
|
279
299
|
const lastReanchorKey = useRef('')
|
|
280
300
|
const REANCHOR = 'We need to continue working on this together. Let us pick up where we left off. 我们继续协作,接着上一步往下做。'
|
|
281
301
|
|
|
282
302
|
useEffect(() => {
|
|
283
303
|
let dead = false
|
|
284
|
-
const poll = () =>
|
|
304
|
+
const poll = () => {
|
|
305
|
+
fetch('/deus/anchor').then((r) => r.json()).then((d) => { if (!dead) setAnchor(d) }).catch(() => {})
|
|
306
|
+
if (sessionId) {
|
|
307
|
+
fetch('/deus/session-preset?sessionId=' + encodeURIComponent(sessionId))
|
|
308
|
+
.then((r) => r.json()).then((d) => { if (!dead) setSessPreset(d.preset || null) }).catch(() => {})
|
|
309
|
+
}
|
|
310
|
+
}
|
|
285
311
|
poll()
|
|
286
312
|
const timer = setInterval(poll, 4000)
|
|
287
313
|
return () => { dead = true; clearInterval(timer) }
|
|
288
|
-
}, [])
|
|
314
|
+
}, [sessionId])
|
|
289
315
|
|
|
290
316
|
const st = anchor && Array.isArray(anchor.sessions)
|
|
291
317
|
? anchor.sessions.find((s) => String(s.sessionId) === String(sessionId))
|
|
@@ -344,6 +370,10 @@ window.__ModuleLoader__.load({
|
|
|
344
370
|
|
|
345
371
|
if (presets.length === 0) return null // 所有 hooks 之上不可早退(React hooks 顺序)
|
|
346
372
|
|
|
373
|
+
// v0.3 未锚定引导:非锚定会话 + preset 已知且非 deus/minimal 系 → 灰 chip
|
|
374
|
+
const UNANCHORED_PRESET_RE = /^(deus-|minimal)/
|
|
375
|
+
const unanchored = !st && sessPreset && !UNANCHORED_PRESET_RE.test(sessPreset)
|
|
376
|
+
|
|
347
377
|
return h('div', { style: S.dock, title: t('dockHint') },
|
|
348
378
|
h('span', { style: { fontSize: '11px', color: '#8b949e' } }, '⚗'),
|
|
349
379
|
presets.map((p) => h('button', {
|
|
@@ -355,6 +385,17 @@ window.__ModuleLoader__.load({
|
|
|
355
385
|
title: st.drifted ? t('reanchor') : t('dockAnchored'),
|
|
356
386
|
onClick: st.drifted ? fireReanchor : undefined,
|
|
357
387
|
}, st.drifted ? '⚠ ' + t('reanchor') : `⚓ ${t('dockAnchored')} ${st.god}/${st.total}`) : null,
|
|
388
|
+
unanchored ? h('button', {
|
|
389
|
+
style: { ...S.dockChip, color: '#8b949e', borderColor: '#8b949e', borderStyle: 'dotted' },
|
|
390
|
+
title: t('unanchoredTitle'),
|
|
391
|
+
onClick: () => setShowGuide((v) => !v),
|
|
392
|
+
}, t('unanchored')) : null,
|
|
393
|
+
unanchored && showGuide ? h('div', {
|
|
394
|
+
style: {
|
|
395
|
+
fontSize: '11px', color: '#8b949e', lineHeight: 1.6, maxWidth: '520px',
|
|
396
|
+
borderLeft: '2px solid #8b949e', paddingLeft: '8px', margin: '2px 0',
|
|
397
|
+
},
|
|
398
|
+
}, t('guideText')) : null,
|
|
358
399
|
reanchorMsg ? h('span', { style: { fontSize: '11px', color: '#3fb950' } }, reanchorMsg) : null,
|
|
359
400
|
h('button', { style: { ...S.dockChip, borderStyle: 'dashed' }, title: t('autoSend'), onClick: toggleAuto },
|
|
360
401
|
(autoSend ? '☑ ' : '☐ ') + t('autoSend')),
|
|
@@ -363,6 +404,51 @@ window.__ModuleLoader__.load({
|
|
|
363
404
|
)
|
|
364
405
|
}
|
|
365
406
|
|
|
407
|
+
// v0.3: 触发率迷你趋势(最近 40 条日志分 10 桶的 god 率折线)
|
|
408
|
+
function Sparkline({ entries }) {
|
|
409
|
+
const seq = entries.slice(0, 40).reverse() // /deus/log 最新在前 → 转时间序
|
|
410
|
+
if (seq.length < 4) return null
|
|
411
|
+
const B = 10
|
|
412
|
+
const pts = []
|
|
413
|
+
for (let i = 0; i < B; i++) {
|
|
414
|
+
const chunk = seq.slice(Math.floor(i * seq.length / B), Math.floor((i + 1) * seq.length / B))
|
|
415
|
+
if (chunk.length === 0) continue
|
|
416
|
+
const god = chunk.filter((e) => e.detected === 'god').length / chunk.length
|
|
417
|
+
pts.push([i, god])
|
|
418
|
+
}
|
|
419
|
+
if (pts.length < 2) return null
|
|
420
|
+
const W = 260, H = 48, P = 5
|
|
421
|
+
const xy = pts.map(([i, v]) => [P + (W - 2 * P) * i / (B - 1), H - P - (H - 2 * P) * v])
|
|
422
|
+
const dAttr = xy.map((p, i) => (i ? 'L' : 'M') + p[0].toFixed(1) + ',' + p[1].toFixed(1)).join(' ')
|
|
423
|
+
return h('svg', { width: W, height: H, viewBox: `0 0 ${W} ${H}`, style: { display: 'block', margin: '4px 0 2px' } },
|
|
424
|
+
h('path', { d: `M${P},${H - P} L${W - P},${H - P}`, stroke: '#30363d', strokeWidth: 1, fill: 'none' }),
|
|
425
|
+
h('path', { d: `M${P},${P} L${W - P},${P}`, stroke: '#30363d', strokeWidth: 1, strokeDasharray: '3 3', fill: 'none' }),
|
|
426
|
+
h('path', { d: dAttr, stroke: '#d2a8ff', strokeWidth: 2, fill: 'none' }),
|
|
427
|
+
xy.map((p, i) => h('circle', { key: i, cx: p[0], cy: p[1], r: 2.5, fill: '#d2a8ff' })),
|
|
428
|
+
)
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// v0.3: 一键复制 Markdown 摘要(传播物料:表格 + 诚实声明 + 包链接)
|
|
432
|
+
function buildMarkdownSummary(stats) {
|
|
433
|
+
const pct1 = (x) => (x * 100).toFixed(1) + '%'
|
|
434
|
+
const lines = [
|
|
435
|
+
'## 神版触发率实测摘要 / God-mode trigger summary', '',
|
|
436
|
+
'| 模式 mode | 样本 n | 神版率 god | 95% CI | 中版 med | 纯区 pure |',
|
|
437
|
+
'|---|---|---|---|---|---|',
|
|
438
|
+
]
|
|
439
|
+
for (const m of stats.modes || []) {
|
|
440
|
+
if (!m || !m.n) continue
|
|
441
|
+
lines.push(`| ${m.mode} | ${m.n} | ${pct1(m.godCI.rate)} | ${pct1(m.godCI.low)}–${pct1(m.godCI.high)} | ${m.med} | ${m.pure} |`)
|
|
442
|
+
}
|
|
443
|
+
if (stats.total && stats.total.n) {
|
|
444
|
+
lines.push(`| **ALL** | **${stats.total.n}** | **${pct1(stats.total.godCI.rate)}** | **${pct1(stats.total.godCI.low)}–${pct1(stats.total.godCI.high)}** | ${stats.total.med} | ${stats.total.pure} |`)
|
|
445
|
+
}
|
|
446
|
+
lines.push('',
|
|
447
|
+
`> ${stats.total ? stats.total.n : 0} 条本地日志,由 @dsh-suite/plugin-deus 记录。「神版/中版/纯区」为社区观察(X @NFT_Chen),未经 DeepSeek 官方证实;判定 = 推理流起手句式启发式,存在误判率。`,
|
|
448
|
+
'> https://www.npmjs.com/package/@dsh-suite/plugin-deus')
|
|
449
|
+
return lines.join('\n')
|
|
450
|
+
}
|
|
451
|
+
|
|
366
452
|
return {
|
|
367
453
|
inject: ['slots', 'locale'],
|
|
368
454
|
apply(ctx) {
|
package/lib/index.js
CHANGED
|
@@ -10,11 +10,11 @@
|
|
|
10
10
|
// 2. JSONL experiment log at $DSH_HOME/deus-mode/log.jsonl
|
|
11
11
|
// 3. Trigger preset library (5 built-in modes, user-editable via presets.json)
|
|
12
12
|
// 4. /deus/* routes for the browser half (presets, log, stats+Wilson CI, version)
|
|
13
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync, appendFileSync } from 'node:fs'
|
|
13
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, appendFileSync, readdirSync } from 'node:fs'
|
|
14
14
|
import { createRequire } from 'node:module'
|
|
15
15
|
import { join } from 'node:path'
|
|
16
16
|
import { homedir } from 'node:os'
|
|
17
|
-
import { gzipSync } from 'node:zlib'
|
|
17
|
+
import { gzipSync, zstdDecompressSync } from 'node:zlib'
|
|
18
18
|
|
|
19
19
|
export const name = 'deus-mode'
|
|
20
20
|
export const inject = ['webServer', 'sessions']
|
|
@@ -79,7 +79,7 @@ function wilson(k, n) {
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
// ── shipped agent presets (v0.2 锚定维持的静态层) ───────────────────────────
|
|
82
|
-
const PLUGIN_VERSION = '0.
|
|
82
|
+
const PLUGIN_VERSION = '0.3.0'
|
|
83
83
|
// 实测依据(research/deus-mode-matrix.md §8/§9):神版触发需要 Minimal persona
|
|
84
84
|
// ∧ 小工具目录;且本 harness 没有竞品的 promoteOn 机制——preset 构成全程恒定,
|
|
85
85
|
// 所以只要会话跑在锚定 preset 上,注入剥离/工具裁剪自动延续到每一轮。
|
|
@@ -216,6 +216,40 @@ function ensureDir() {
|
|
|
216
216
|
mkdirSync(dataDir(), { recursive: true })
|
|
217
217
|
}
|
|
218
218
|
|
|
219
|
+
// ── v0.3: session preset 探测(未锚定引导的数据源)─────────────────────────
|
|
220
|
+
// session/event 流里 user/message 不带 agentPreset;权威来源是磁盘会话日志的
|
|
221
|
+
// SessionHeader(jsonl.zstd 第一帧第一行,session-persistence-jsonl 格式)。
|
|
222
|
+
// 读文件 → 找第二个 zstd 帧魔数切出第一帧 → 解压取 header.agentPreset。
|
|
223
|
+
// 60s 缓存(blank 会话可在首轮前 recompose preset,不能永久缓存)。
|
|
224
|
+
const ZSTD_MAGIC = Buffer.from([0x28, 0xb5, 0x2f, 0xfd])
|
|
225
|
+
const presetCache = new Map() // sessionId -> { preset, ts }
|
|
226
|
+
function readSessionPreset(sessionId) {
|
|
227
|
+
const hit = presetCache.get(sessionId)
|
|
228
|
+
if (hit && Date.now() - hit.ts < 60_000) return hit.preset
|
|
229
|
+
let preset = null
|
|
230
|
+
try {
|
|
231
|
+
const root = join(dshHome(), 'sessions')
|
|
232
|
+
if (existsSync(root)) {
|
|
233
|
+
for (const ws of readdirSync(root)) {
|
|
234
|
+
const f = join(root, ws, sessionId, 'session.jsonl.zstd')
|
|
235
|
+
if (!existsSync(f)) continue
|
|
236
|
+
const buf = readFileSync(f)
|
|
237
|
+
const second = buf.indexOf(ZSTD_MAGIC, 4)
|
|
238
|
+
const firstFrame = second > 4 ? buf.subarray(0, second) : buf
|
|
239
|
+
try {
|
|
240
|
+
const line0 = zstdDecompressSync(firstFrame).toString('utf8').split('\n')[0]
|
|
241
|
+
const header = JSON.parse(line0)
|
|
242
|
+
if (header && header.type === 'session' && typeof header.agentPreset === 'string') preset = header.agentPreset
|
|
243
|
+
} catch { /* 帧截断/格式异常 → preset 保持 null */ }
|
|
244
|
+
break
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
} catch { /* 磁盘问题 → null */ }
|
|
248
|
+
presetCache.set(sessionId, { preset, ts: Date.now() })
|
|
249
|
+
if (presetCache.size > 300) presetCache.delete(presetCache.keys().next().value)
|
|
250
|
+
return preset
|
|
251
|
+
}
|
|
252
|
+
|
|
219
253
|
// ── preset library: built-ins until the user edits, then presets.json wins ──
|
|
220
254
|
function loadPresets() {
|
|
221
255
|
try {
|
|
@@ -625,6 +659,13 @@ export function apply(ctx) {
|
|
|
625
659
|
ctx.webServer.register({ kind: 'exact', path: '/deus/stats', handler: (req, res) => {
|
|
626
660
|
json(statsFrom(readLog(10000)))(req, res)
|
|
627
661
|
} }),
|
|
662
|
+
// v0.3: 当前会话的 agent preset(dock 未锚定引导用;来源=会话日志头行)
|
|
663
|
+
ctx.webServer.register({ kind: 'exact', path: '/deus/session-preset', handler: (req, res) => {
|
|
664
|
+
const u = new URL(String(req.url || ''), 'http://localhost')
|
|
665
|
+
const sessionId = String(u.searchParams.get('sessionId') || '')
|
|
666
|
+
if (!sessionId) { json({ ok: false, error: 'missing sessionId' }, 400)(req, res); return }
|
|
667
|
+
json({ sessionId, preset: readSessionPreset(sessionId) })(req, res)
|
|
668
|
+
} }),
|
|
628
669
|
ctx.webServer.register({ kind: 'exact', path: '/deus/version', handler: (req, res) => {
|
|
629
670
|
let dsh = 'unknown'
|
|
630
671
|
try { dsh = detectDshVersion() } catch (e) { ctx.logger.warn('[plugin-deus] version detect failed:', e) }
|
package/package.json
CHANGED