@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 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 = () => fetch('/deus/anchor').then((r) => r.json()).then((d) => { if (!dead) setAnchor(d) }).catch(() => {})
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.2.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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dsh-suite/plugin-deus",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "神模扳机 Deus Trigger — 极简提示词触发实验台:预设库 + 一键注入 + 起手识别器 + 触发率统计(Wilson CI)。社区观察未获官方证实,本插件不承诺解锁任何隐藏能力。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",