@dsh-suite/plugin-deus 0.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/README.md +53 -0
- package/cordis.patch.yml +9 -0
- package/lib/client.js +292 -0
- package/lib/index.js +394 -0
- package/package.json +44 -0
package/README.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# @dsh-suite/plugin-deus · 神模扳机(Deus Trigger)
|
|
2
|
+
|
|
3
|
+
> ⚠️ **诚实声明 / Honesty note**
|
|
4
|
+
> 「神模 3 模式」(纯区版 / 中版 / 神版)来自**社区观察**(X @NFT_Chen 推文,72 赞),**未获 DeepSeek 官方证实**。
|
|
5
|
+
> 本插件只做**提示词注入 + 回复起手判定 + 本地日志统计**,把"口耳相传的极简触发词"变成可重复、可量化的实验。
|
|
6
|
+
> 它**不承诺、也不声称**触发任何隐藏能力;观测到的触发率**可能只是随机采样噪音**。
|
|
7
|
+
>
|
|
8
|
+
> The "3 modes" are a **community observation**, NOT confirmed by DeepSeek. This plugin injects candidate
|
|
9
|
+
> minimal prompts, heuristically classifies reply openers, and keeps a local experiment log.
|
|
10
|
+
> It does not promise or claim any hidden capability; observed trigger rates may be sampling noise.
|
|
11
|
+
|
|
12
|
+
## 它做什么 / What it does
|
|
13
|
+
|
|
14
|
+
把"触发神模"从玄学变成可重复实验:
|
|
15
|
+
|
|
16
|
+
1. **触发预设库** — 5 个内置极简提示词模式(空输入 / 单字符 / we-引导 / Let-me 起手 / 纯斜杠),全部可编辑、增删。
|
|
17
|
+
2. **一键注入** — 两档:
|
|
18
|
+
- **档 A(保守)**:Settings 面板一键复制 → 粘贴进输入框发送;宿主按 prompt 精确匹配自动配对判定。
|
|
19
|
+
- **档 B(增强)**:对话输入框上方的芯片行,点击即写入草稿(可选自动发送),并向宿主打点配对。
|
|
20
|
+
3. **起手识别器** — host 半侧监听 `session/event`,对首条回复做启发式判定:
|
|
21
|
+
`Let me…` → 纯区版 · `The user…`(含去冠词变体 `User wants…`)→ 中版 · `we` 起手(或首句 ≥3 个 we)→ 神版 · 其余 → 未判定。
|
|
22
|
+
**实测校准**(`research/deus-mode-matrix.md`,120 次 API 采样):指纹主要出现在**推理流**(`reasoning-delta`,如 "The user is asking…" / "We need answer…"),可见正文多为中文直答;识别器因此**优先判定推理流、可见文本兜底**,并额外识别推理流里的中文 `我们需要/我们应该…` 起手(神版等价)。与 120 例人工标注的神版判定一致率 99.2%。
|
|
23
|
+
4. **实测日志 + 触发率统计** — 每次触发追加一条 JSONL(`~/.dsh/deus-mode/log.jsonl`,纯本地不上传);
|
|
24
|
+
面板给出每模式的 god/med/pure 比例 + **95% Wilson 置信区间**,可导出 CSV。
|
|
25
|
+
|
|
26
|
+
## 安装 / Install
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# npm(竞品方案需要手动拷目录,本插件一条命令)
|
|
30
|
+
npx @deepseek-ai/dsh plugin --profile web add @dsh-suite/plugin-deus
|
|
31
|
+
|
|
32
|
+
# 本地目录开发
|
|
33
|
+
npx @deepseek-ai/dsh plugin --profile web add ./plugin-deus # 在其父目录执行
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
打开 Web UI → Settings → **神模扳机**;对话页输入框上方会出现芯片行(档 B)。
|
|
37
|
+
|
|
38
|
+
## 怎么读统计 / Reading the stats
|
|
39
|
+
|
|
40
|
+
- 报**比例 + Wilson 95% CI**,不是点估计;样本少时区间很宽,别过度解读。
|
|
41
|
+
- **对照组才是关键**:用普通完整提示词采样同量对照——实验组 god 率显著高于对照组才算"有差异",否则就是基线噪音。
|
|
42
|
+
- 识别器是**启发式倾向信号**(把正常 "we" 起手的回答误判为神版是可能的);日志保留 `first_sentence` 原文供人工复核。
|
|
43
|
+
- 同参数**隔天重跑一轮**看稳定性:第一轮高、重跑回落 → 疑似时段/方差影响。
|
|
44
|
+
|
|
45
|
+
## 隐私与边界 / Privacy & boundaries
|
|
46
|
+
|
|
47
|
+
- 日志只写本地 `$DSH_HOME/deus-mode/log.jsonl`,不上传、可一键清空、可导出 CSV。
|
|
48
|
+
- 全部是用户正常交互(prompt 注入 + 回复观测),无越权、无绕过。
|
|
49
|
+
- 若实验证明无差异:本插件的身份即**通用极简提示词 A/B 测试台**,"神模"只是第一个被测假设。
|
|
50
|
+
|
|
51
|
+
## License
|
|
52
|
+
|
|
53
|
+
MIT
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# @dsh-suite/plugin-deus bundle patch. Installed via:
|
|
2
|
+
# dsh plugin --profile web add @dsh-suite/plugin-deus
|
|
3
|
+
# The node half (exports ".") runs the opener detector on session/event, writes
|
|
4
|
+
# the JSONL experiment log, and serves /deus/* routes (presets/log/stats/version);
|
|
5
|
+
# the browser half (exports "./client") injects the 神模扳机 settings section
|
|
6
|
+
# plus the composer dock row for one-click injection.
|
|
7
|
+
- insert:
|
|
8
|
+
- id: deus-mode
|
|
9
|
+
name: '@dsh-suite/plugin-deus'
|
package/lib/client.js
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
// plugin-deus browser half — the 神模扳机 (Deus Trigger) panel + composer dock.
|
|
2
|
+
//
|
|
3
|
+
// Honest framing: the "3 modes" are a community observation (X @NFT_Chen), NOT
|
|
4
|
+
// confirmed by DeepSeek. This plugin only injects candidate minimal prompts and
|
|
5
|
+
// records/classifies the replies. The honesty declaration stays pinned at the
|
|
6
|
+
// top of the panel at all times; no "unlock"/"crack"/"hidden model" wording.
|
|
7
|
+
//
|
|
8
|
+
// Two injection tiers (design doc §2.2):
|
|
9
|
+
// A (conservative): settings section + one-click copy — user pastes into the
|
|
10
|
+
// composer; the host auto-pairs the reply by exact-matching the prompt.
|
|
11
|
+
// B (enhanced): conversation.input.dock chip row — one click writes the prompt
|
|
12
|
+
// into the composer draft via inputActions.setDraft (optional auto-send)
|
|
13
|
+
// and marks the trigger on the host for pairing.
|
|
14
|
+
window.__ModuleLoader__.load({
|
|
15
|
+
id: '@dsh-suite/plugin-deus',
|
|
16
|
+
factory: (require) => {
|
|
17
|
+
const React = require('react')
|
|
18
|
+
const { useState, useEffect } = React
|
|
19
|
+
const h = React.createElement
|
|
20
|
+
|
|
21
|
+
const NS = 'deusMode'
|
|
22
|
+
const zh = {
|
|
23
|
+
nav: '神模扳机', sub: '极简提示词触发实验台 · 注入 → 判定 → 统计',
|
|
24
|
+
honesty: '诚实声明:「神模 3 模式」来自社区观察(X @NFT_Chen),未获 DeepSeek 官方证实。本插件只做提示词注入与结果记录统计,不承诺解锁任何隐藏能力;触发率可能只是随机采样噪音。',
|
|
25
|
+
version: '版本', dshVer: 'DSH 版本', pluginVer: '插件版本', logPath: '日志位置',
|
|
26
|
+
presets: '触发预设库', presetsHint: '点击「复制」后粘贴到输入框发送(档 A);或用对话页输入框上方的芯片行一键注入(档 B)。全部可编辑。',
|
|
27
|
+
empty: '(空输入)', copy: '复制', copied: '已复制 ✓',
|
|
28
|
+
edit: '编辑', save: '保存', cancel: '取消', del: '删除', add: '新增预设', resetPresets: '恢复内置',
|
|
29
|
+
editPrompt: 'prompt 内容(留空 = 空输入模式)', editLabel: '名称', editId: 'id(唯一)',
|
|
30
|
+
stats: '触发率统计', statsHint: '比例 + 95% Wilson 置信区间。只有实验组显著高于对照组(普通完整提示词)才算有差异——请自行保留对照采样。',
|
|
31
|
+
measured: '本团队实测(2026-08-15,deepseek-v4-pro,N=200 次 API 采样):极简首请求构成(Minimal 系统提示 ∧ ≤2 工具 ∧ 剥离注入上下文,三者缺一不可)下,动手类任务神版(we 起手)约 90%;讲解/咨询类任务约 0%;完整 persona + 25 工具对照组 0/20。指纹主要在推理流(reasoning),可见正文多为中文直答。样本量小,请当作倾向信号。',
|
|
32
|
+
colMode: '模式', colN: '采样', colGod: '神版率', colMed: '中版率', colPure: '纯区率',
|
|
33
|
+
exportCsv: '导出 CSV', resetLog: '清空日志', resetLogConfirm: '确定清空全部实验日志?此操作不可撤销。',
|
|
34
|
+
recent: '最近记录', recentHint: '识别器判定是启发式倾向信号,first_sentence 原文供人工复核。',
|
|
35
|
+
noData: '暂无记录——先触发几次再来看看。',
|
|
36
|
+
loadFail: '加载失败', saveFail: '保存失败',
|
|
37
|
+
dockHint: '神模扳机(实验工具·社区观察未证实)', dockSend: '注入并发送', dockFill: '注入',
|
|
38
|
+
autoSend: '注入后自动发送',
|
|
39
|
+
detected_pure: '纯区版', detected_med: '中版', detected_god: '神版', detected_unknown: '未判定',
|
|
40
|
+
}
|
|
41
|
+
const en = {
|
|
42
|
+
nav: 'Deus Trigger', sub: 'Minimal-prompt trigger bench · inject → classify → stats',
|
|
43
|
+
honesty: 'Honesty note: the "3 modes" come from a community observation (X @NFT_Chen), NOT confirmed by DeepSeek. This plugin only injects prompts and records statistics — it does not unlock any hidden capability; observed trigger rates may be sampling noise.',
|
|
44
|
+
version: 'Version', dshVer: 'DSH version', pluginVer: 'Plugin version', logPath: 'Log path',
|
|
45
|
+
presets: 'Trigger presets', presetsHint: 'Copy then paste into the composer (tier A), or use the chip row above the composer for one-click injection (tier B). Fully editable.',
|
|
46
|
+
empty: '(empty input)', copy: 'Copy', copied: 'Copied ✓',
|
|
47
|
+
edit: 'Edit', save: 'Save', cancel: 'Cancel', del: 'Delete', add: 'Add preset', resetPresets: 'Restore built-ins',
|
|
48
|
+
editPrompt: 'prompt text (empty = empty-input mode)', editLabel: 'label', editId: 'id (unique)',
|
|
49
|
+
stats: 'Trigger-rate stats', statsHint: 'Proportion + 95% Wilson CI. Only a significant lead over a control group (ordinary full prompts) counts — keep your own control samples.',
|
|
50
|
+
measured: 'Measured by our team (2026-08-15, deepseek-v4-pro, N=200 API samples): under a minimal first request (minimal system prompt AND ≤2 tools AND stripped context injection — all three required), hands-on tasks hit the god opener ("we…") ~90% of the time; explanatory/Q&A tasks ~0%; the full persona + 25-tool control hit 0/20. The fingerprint lives mostly in the reasoning stream. Small samples — treat as a tendency signal.',
|
|
51
|
+
colMode: 'Mode', colN: 'N', colGod: 'god rate', colMed: 'med rate', colPure: 'pure rate',
|
|
52
|
+
exportCsv: 'Export CSV', resetLog: 'Clear log', resetLogConfirm: 'Clear ALL experiment log entries? This cannot be undone.',
|
|
53
|
+
recent: 'Recent entries', recentHint: 'The detector is a heuristic tendency signal; first_sentence is kept for human review.',
|
|
54
|
+
noData: 'No entries yet — trigger a few times first.',
|
|
55
|
+
loadFail: 'Load failed', saveFail: 'Save failed',
|
|
56
|
+
dockHint: 'Deus Trigger (experiment · community observation, unconfirmed)', dockSend: 'Inject & send', dockFill: 'Inject',
|
|
57
|
+
autoSend: 'Auto-send after inject',
|
|
58
|
+
detected_pure: 'pure', detected_med: 'med', detected_god: 'god', detected_unknown: 'unknown',
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const S = {
|
|
62
|
+
banner: { background: '#2d2406', border: '1px solid #9e6a03', borderRadius: '8px', padding: '10px 12px', fontSize: '12px', color: '#e3b341', lineHeight: '1.6', marginBottom: '14px' },
|
|
63
|
+
status: { fontSize: '11px', color: '#8b949e', marginBottom: '10px' },
|
|
64
|
+
card: { background: '#161b22', border: '1px solid #30363d', borderRadius: '10px', padding: '14px', marginBottom: '12px' },
|
|
65
|
+
row: { display: 'flex', gap: '8px', alignItems: 'center', flexWrap: 'wrap', marginTop: '10px' },
|
|
66
|
+
btn: { background: 'transparent', border: '1px solid #30363d', borderRadius: '6px', color: '#8b949e', padding: '6px 12px', fontSize: '13px', cursor: 'pointer' },
|
|
67
|
+
btnActive: { background: '#21262d', border: '1px solid #8b949e', borderRadius: '6px', color: '#e6edf3', padding: '6px 12px', fontSize: '13px', cursor: 'pointer' },
|
|
68
|
+
chip: { background: '#0d1117', border: '1px solid #30363d', borderRadius: '20px', color: '#8b949e', padding: '2px 10px', fontSize: '11px' },
|
|
69
|
+
name: { fontSize: '14px', fontWeight: '700', color: '#e6edf3' },
|
|
70
|
+
desc: { fontSize: '12px', color: '#8b949e', marginTop: '4px', lineHeight: '1.5' },
|
|
71
|
+
h2: { fontSize: '13px', fontWeight: '600', color: '#e6edf3', margin: '18px 0 8px' },
|
|
72
|
+
table: { width: '100%', borderCollapse: 'collapse', fontSize: '12px', color: '#c9d1d9' },
|
|
73
|
+
th: { textAlign: 'left', padding: '6px 8px', borderBottom: '1px solid #30363d', color: '#8b949e', fontWeight: '600' },
|
|
74
|
+
td: { padding: '6px 8px', borderBottom: '1px solid #21262d' },
|
|
75
|
+
input: { background: '#0d1117', border: '1px solid #30363d', borderRadius: '6px', color: '#e6edf3', padding: '6px 8px', fontSize: '12px', width: '100%', boxSizing: 'border-box' },
|
|
76
|
+
mono: { fontFamily: 'monospace', fontSize: '12px', color: '#c9d1d9' },
|
|
77
|
+
dock: { display: 'flex', gap: '6px', alignItems: 'center', flexWrap: 'wrap', padding: '4px 0' },
|
|
78
|
+
dockChip: { background: '#161b22', border: '1px solid #30363d', borderRadius: '16px', color: '#8b949e', padding: '3px 12px', fontSize: '12px', cursor: 'pointer' },
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const pct = (ci) => `${(ci.rate * 100).toFixed(0)}% [${(ci.low * 100).toFixed(0)}–${(ci.high * 100).toFixed(0)}]`
|
|
82
|
+
|
|
83
|
+
// ── tier A: settings section panel ──────────────────────────────────────
|
|
84
|
+
function Panel(props) {
|
|
85
|
+
const t = props.t
|
|
86
|
+
const [presets, setPresets] = useState([])
|
|
87
|
+
const [stats, setStats] = useState(null)
|
|
88
|
+
const [entries, setEntries] = useState([])
|
|
89
|
+
const [ver, setVer] = useState(null)
|
|
90
|
+
const [err, setErr] = useState('')
|
|
91
|
+
const [copied, setCopied] = useState('')
|
|
92
|
+
const [editing, setEditing] = useState(null) // {id, label_zh, prompt} draft
|
|
93
|
+
|
|
94
|
+
function refresh() {
|
|
95
|
+
fetch('/deus/presets').then((r) => r.json()).then((d) => setPresets(d.presets || [])).catch(() => setErr(t('loadFail')))
|
|
96
|
+
fetch('/deus/stats').then((r) => r.json()).then(setStats).catch(() => {})
|
|
97
|
+
fetch('/deus/log').then((r) => r.json()).then((d) => setEntries(d.entries || [])).catch(() => {})
|
|
98
|
+
fetch('/deus/version').then((r) => r.json()).then(setVer).catch(() => {})
|
|
99
|
+
}
|
|
100
|
+
useEffect(() => { refresh() }, [])
|
|
101
|
+
|
|
102
|
+
async function copyPrompt(p) {
|
|
103
|
+
try {
|
|
104
|
+
await navigator.clipboard.writeText(p.prompt === null ? '' : p.prompt)
|
|
105
|
+
setCopied(p.id)
|
|
106
|
+
setTimeout(() => setCopied(''), 1500)
|
|
107
|
+
} catch { setErr(t('loadFail')) }
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function saveAll(next) {
|
|
111
|
+
setErr('')
|
|
112
|
+
try {
|
|
113
|
+
const r = await fetch('/deus/presets', {
|
|
114
|
+
method: 'POST', headers: { 'content-type': 'application/json' },
|
|
115
|
+
body: JSON.stringify({ presets: next }),
|
|
116
|
+
}).then((x) => x.json())
|
|
117
|
+
if (!r.ok) setErr(t('saveFail'))
|
|
118
|
+
else setPresets(r.presets)
|
|
119
|
+
} catch { setErr(t('saveFail')) }
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function resetPresets() {
|
|
123
|
+
const r = await fetch('/deus/presets/reset', { method: 'POST' }).then((x) => x.json()).catch(() => null)
|
|
124
|
+
if (r && r.ok) setPresets(r.presets)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async function resetLog() {
|
|
128
|
+
if (!window.confirm(t('resetLogConfirm'))) return
|
|
129
|
+
await fetch('/deus/log', { method: 'DELETE' }).catch(() => {})
|
|
130
|
+
refresh()
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function commitEdit() {
|
|
134
|
+
if (!editing || editing.id.trim() === '') return
|
|
135
|
+
const next = presets.filter((p) => p.id !== editing.id)
|
|
136
|
+
next.push({ id: editing.id.trim(), label_zh: editing.label_zh.trim() || editing.id.trim(), label_en: editing.label_zh.trim() || editing.id.trim(), prompt: editing.prompt === '' ? null : editing.prompt, tags: [] })
|
|
137
|
+
saveAll(next)
|
|
138
|
+
setEditing(null)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return h('div', { style: { maxWidth: '860px' } },
|
|
142
|
+
h('div', { style: { fontSize: '14px', fontWeight: '600', color: '#e6edf3', marginBottom: '4px' } }, '⚗ ' + t('nav')),
|
|
143
|
+
h('div', { style: S.status }, t('sub')),
|
|
144
|
+
h('div', { style: S.banner }, '⚠️ ' + t('honesty')),
|
|
145
|
+
err ? h('div', { style: { color: '#f85149', fontSize: '12px', marginBottom: '10px' } }, err) : null,
|
|
146
|
+
ver ? h('div', { style: { ...S.status, fontFamily: 'monospace' } },
|
|
147
|
+
`${t('dshVer')}: ${ver.dsh} · ${t('pluginVer')}: ${ver.plugin} · ${t('logPath')}: ${ver.logPath}`) : null,
|
|
148
|
+
|
|
149
|
+
h('div', { style: S.h2 }, t('presets')),
|
|
150
|
+
h('div', { style: S.status }, t('presetsHint')),
|
|
151
|
+
presets.map((p) => h('div', { key: p.id, style: S.card },
|
|
152
|
+
h('div', { style: { display: 'flex', alignItems: 'baseline', gap: '10px', flexWrap: 'wrap' } },
|
|
153
|
+
h('span', { style: S.name }, p.label_zh),
|
|
154
|
+
h('span', { style: { fontSize: '11px', color: '#8b949e' } }, p.id),
|
|
155
|
+
p.builtin ? h('span', { style: S.chip }, 'built-in') : null,
|
|
156
|
+
),
|
|
157
|
+
h('div', { style: { ...S.mono, marginTop: '6px' } }, p.prompt === null ? t('empty') : JSON.stringify(p.prompt)),
|
|
158
|
+
h('div', { style: S.row },
|
|
159
|
+
h('button', { style: copied === p.id ? S.btnActive : S.btn, onClick: () => copyPrompt(p) },
|
|
160
|
+
copied === p.id ? '📋 ' + t('copied') : '📋 ' + t('copy')),
|
|
161
|
+
h('button', { style: S.btn, onClick: () => setEditing({ id: p.id, label_zh: p.label_zh, prompt: p.prompt === null ? '' : p.prompt }) }, '✏️ ' + t('edit')),
|
|
162
|
+
h('button', { style: S.btn, onClick: () => saveAll(presets.filter((x) => x.id !== p.id)) }, '🗑 ' + t('del')),
|
|
163
|
+
),
|
|
164
|
+
)),
|
|
165
|
+
editing ? h('div', { style: S.card },
|
|
166
|
+
h('div', { style: S.status }, t('editId')),
|
|
167
|
+
h('input', { style: S.input, value: editing.id, onChange: (e) => setEditing({ ...editing, id: e.target.value }) }),
|
|
168
|
+
h('div', { style: { ...S.status, marginTop: '8px' } }, t('editLabel')),
|
|
169
|
+
h('input', { style: S.input, value: editing.label_zh, onChange: (e) => setEditing({ ...editing, label_zh: e.target.value }) }),
|
|
170
|
+
h('div', { style: { ...S.status, marginTop: '8px' } }, t('editPrompt')),
|
|
171
|
+
h('textarea', { style: { ...S.input, minHeight: '60px' }, value: editing.prompt, onChange: (e) => setEditing({ ...editing, prompt: e.target.value }) }),
|
|
172
|
+
h('div', { style: S.row },
|
|
173
|
+
h('button', { style: S.btnActive, onClick: commitEdit }, '💾 ' + t('save')),
|
|
174
|
+
h('button', { style: S.btn, onClick: () => setEditing(null) }, t('cancel')),
|
|
175
|
+
),
|
|
176
|
+
) : h('div', { style: S.row },
|
|
177
|
+
h('button', { style: S.btn, onClick: () => setEditing({ id: '', label_zh: '', prompt: '' }) }, '➕ ' + t('add')),
|
|
178
|
+
h('button', { style: S.btn, onClick: resetPresets }, '↩ ' + t('resetPresets')),
|
|
179
|
+
),
|
|
180
|
+
|
|
181
|
+
h('div', { style: S.h2 }, t('stats')),
|
|
182
|
+
h('div', { style: S.status }, t('statsHint')),
|
|
183
|
+
h('div', { style: { ...S.status, color: '#7d8590' } }, t('measured')),
|
|
184
|
+
stats && stats.modes.length > 0
|
|
185
|
+
? h('table', { style: S.table },
|
|
186
|
+
h('thead', null, h('tr', null,
|
|
187
|
+
h('th', { style: S.th }, t('colMode')), h('th', { style: S.th }, t('colN')),
|
|
188
|
+
h('th', { style: S.th }, t('colGod')), h('th', { style: S.th }, t('colMed')), h('th', { style: S.th }, t('colPure')))),
|
|
189
|
+
h('tbody', null, stats.modes.map((m) => h('tr', { key: m.mode },
|
|
190
|
+
h('td', { style: S.td }, m.mode),
|
|
191
|
+
h('td', { style: S.td }, m.n),
|
|
192
|
+
h('td', { style: { ...S.td, color: '#d2a8ff' } }, pct(m.godCI)),
|
|
193
|
+
h('td', { style: S.td }, pct(m.medCI)),
|
|
194
|
+
h('td', { style: S.td }, pct(m.pureCI)),
|
|
195
|
+
))),
|
|
196
|
+
)
|
|
197
|
+
: h('div', { style: S.status }, t('noData')),
|
|
198
|
+
h('div', { style: S.row },
|
|
199
|
+
h('a', { href: '/deus/log.csv', download: 'deus-log.csv', style: { ...S.btn, textDecoration: 'none', display: 'inline-block' } }, '⬇ ' + t('exportCsv')),
|
|
200
|
+
h('button', { style: { ...S.btn, color: '#f85149', borderColor: '#f85149' }, onClick: resetLog }, '🗑 ' + t('resetLog')),
|
|
201
|
+
),
|
|
202
|
+
|
|
203
|
+
h('div', { style: S.h2 }, t('recent')),
|
|
204
|
+
h('div', { style: S.status }, t('recentHint')),
|
|
205
|
+
entries.length === 0 ? h('div', { style: S.status }, t('noData'))
|
|
206
|
+
: entries.slice(0, 10).map((e, i) => h('div', { key: i, style: { ...S.card, padding: '10px 12px' } },
|
|
207
|
+
h('div', { style: { display: 'flex', gap: '8px', alignItems: 'baseline', flexWrap: 'wrap' } },
|
|
208
|
+
h('span', { style: S.chip }, e.prompt_mode),
|
|
209
|
+
h('span', { style: { ...S.chip, color: e.detected === 'god' ? '#d2a8ff' : e.detected === 'pure' ? '#79c0ff' : e.detected === 'med' ? '#3fb950' : '#8b949e' } },
|
|
210
|
+
t('detected_' + (e.detected || 'unknown'))),
|
|
211
|
+
h('span', { style: { fontSize: '11px', color: '#8b949e' } }, e.ts),
|
|
212
|
+
),
|
|
213
|
+
h('div', { style: { ...S.mono, marginTop: '6px', whiteSpace: 'pre-wrap' } }, e.first_sentence),
|
|
214
|
+
)),
|
|
215
|
+
)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// ── tier B: composer dock chip row ──────────────────────────────────────
|
|
219
|
+
// One click: mark the trigger on the host, then write the prompt into the
|
|
220
|
+
// composer draft (inputActions.setDraft); optional auto-send (submit).
|
|
221
|
+
function Dock(props) {
|
|
222
|
+
const t = props.t
|
|
223
|
+
const [presets, setPresets] = useState([])
|
|
224
|
+
const [autoSend, setAutoSend] = useState(() => {
|
|
225
|
+
try { return window.localStorage.getItem('deus.autoSend') === '1' } catch { return false }
|
|
226
|
+
})
|
|
227
|
+
useEffect(() => {
|
|
228
|
+
fetch('/deus/presets').then((r) => r.json()).then((d) => setPresets(d.presets || [])).catch(() => {})
|
|
229
|
+
}, [])
|
|
230
|
+
if (presets.length === 0) return null
|
|
231
|
+
|
|
232
|
+
const sessionId = props.session && props.session.sessionId
|
|
233
|
+
const actions = props.inputActions
|
|
234
|
+
|
|
235
|
+
async function fire(p) {
|
|
236
|
+
try {
|
|
237
|
+
await fetch('/deus/trigger', {
|
|
238
|
+
method: 'POST', headers: { 'content-type': 'application/json' },
|
|
239
|
+
body: JSON.stringify({ sessionId: String(sessionId || ''), mode: p.id }),
|
|
240
|
+
})
|
|
241
|
+
} catch { /* marking is best-effort; injection still proceeds */ }
|
|
242
|
+
if (!actions || typeof actions.setDraft !== 'function') {
|
|
243
|
+
try { await navigator.clipboard.writeText(p.prompt === null ? '' : p.prompt) } catch { /* ignore */ }
|
|
244
|
+
return
|
|
245
|
+
}
|
|
246
|
+
actions.setDraft(p.prompt === null ? '' : p.prompt)
|
|
247
|
+
if (autoSend && typeof actions.submit === 'function') actions.submit()
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function toggleAuto() {
|
|
251
|
+
const next = !autoSend
|
|
252
|
+
setAutoSend(next)
|
|
253
|
+
try { window.localStorage.setItem('deus.autoSend', next ? '1' : '0') } catch { /* ignore */ }
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return h('div', { style: S.dock, title: t('dockHint') },
|
|
257
|
+
h('span', { style: { fontSize: '11px', color: '#8b949e' } }, '⚗'),
|
|
258
|
+
presets.map((p) => h('button', {
|
|
259
|
+
key: p.id, style: S.dockChip, title: t('dockHint'),
|
|
260
|
+
onClick: () => fire(p),
|
|
261
|
+
}, (autoSend ? '🚀 ' : '⚡ ') + p.label_zh)),
|
|
262
|
+
h('button', { style: { ...S.dockChip, borderStyle: 'dashed' }, title: t('autoSend'), onClick: toggleAuto },
|
|
263
|
+
(autoSend ? '☑ ' : '☐ ') + t('autoSend')),
|
|
264
|
+
)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return {
|
|
268
|
+
inject: ['slots', 'locale'],
|
|
269
|
+
apply(ctx) {
|
|
270
|
+
try {
|
|
271
|
+
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'deus-mode: dicts')
|
|
272
|
+
const t = ctx.locale.bind(NS)
|
|
273
|
+
ctx.slots.inject('settings.section', () => ctx.slots.register({
|
|
274
|
+
name: 'settings.section',
|
|
275
|
+
id: 'deus-mode',
|
|
276
|
+
order: 75,
|
|
277
|
+
label: () => t('nav'),
|
|
278
|
+
locale: NS,
|
|
279
|
+
}, Panel))
|
|
280
|
+
ctx.slots.inject('conversation.input.dock', () => ctx.slots.register({
|
|
281
|
+
name: 'conversation.input.dock',
|
|
282
|
+
id: 'deus-mode',
|
|
283
|
+
order: 60,
|
|
284
|
+
locale: NS,
|
|
285
|
+
}, Dock))
|
|
286
|
+
} catch (e) {
|
|
287
|
+
console.error('[plugin-deus] apply failed:', e)
|
|
288
|
+
}
|
|
289
|
+
},
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
})
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
// plugin-deus host half — the experiment recorder for the "deus mode" hypothesis.
|
|
2
|
+
//
|
|
3
|
+
// Honest framing (kept in sync with the panel declaration): the 3 modes are a
|
|
4
|
+
// community observation (X @NFT_Chen), NOT confirmed by DeepSeek. This half only
|
|
5
|
+
// watches the session/event stream, classifies assistant openers heuristically,
|
|
6
|
+
// and appends to a local JSONL log. Nothing is uploaded; nothing is "unlocked".
|
|
7
|
+
//
|
|
8
|
+
// Responsibilities:
|
|
9
|
+
// 1. Opener detector — first-sentence classification (pure / med / god / unknown)
|
|
10
|
+
// 2. JSONL experiment log at $DSH_HOME/deus-mode/log.jsonl
|
|
11
|
+
// 3. Trigger preset library (5 built-in modes, user-editable via presets.json)
|
|
12
|
+
// 4. /deus/* routes for the browser half (presets, log, stats+Wilson CI, version)
|
|
13
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, appendFileSync } from 'node:fs'
|
|
14
|
+
import { createRequire } from 'node:module'
|
|
15
|
+
import { join } from 'node:path'
|
|
16
|
+
import { homedir } from 'node:os'
|
|
17
|
+
import { gzipSync } from 'node:zlib'
|
|
18
|
+
|
|
19
|
+
export const name = 'deus-mode'
|
|
20
|
+
export const inject = ['webServer', 'sessions']
|
|
21
|
+
|
|
22
|
+
// ── built-in trigger preset library (design doc §2.1) ───────────────────────
|
|
23
|
+
// prompt: null means "empty input" (send with no text). All user-editable.
|
|
24
|
+
const BUILTIN_PRESETS = [
|
|
25
|
+
{ id: 'M-empty', label_zh: '空输入', label_en: 'empty', prompt: null, tags: ['minimal'] },
|
|
26
|
+
{ id: 'M-single', label_zh: '单字符', label_en: 'single char', prompt: 'x', tags: ['minimal'] },
|
|
27
|
+
{ id: 'M-we-lead', label_zh: 'we-起手引导', label_en: 'we-lead prompt', prompt: 'we think and write together.', tags: ['god-lead'] },
|
|
28
|
+
{ id: 'M-letme-lead', label_zh: 'Let-me 起手', label_en: 'let-me prompt', prompt: 'Let me handle this carefully.', tags: ['pure-lead'] },
|
|
29
|
+
{ id: 'M-slash', label_zh: '纯斜杠', label_en: 'slash', prompt: '/', tags: ['minimal'] },
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
// ── opener detector (design doc §2.3, calibrated by实测矩阵 2026-08-15) ──────
|
|
33
|
+
// CALIBRATION (research/deus-mode-matrix.md, 120 API samples): the fingerprint
|
|
34
|
+
// lives in the REASONING stream ("The user is asking…" / "We need answer…"),
|
|
35
|
+
// NOT in the visible text (which is usually Chinese or a direct answer). Real
|
|
36
|
+
// session logs confirm: assistant/chunk carries reasoning-delta 223× vs
|
|
37
|
+
// text-delta 16× in a typical turn. So we classify reasoning first, visible
|
|
38
|
+
// text as fallback. Chinese "我们…" reasoning openers are god-equivalent
|
|
39
|
+
// (observed under minimal conditions) — but ONLY in reasoning, never in
|
|
40
|
+
// visible text (normal Chinese answers start with 我们 all the time).
|
|
41
|
+
// Heuristic on the first ~120 chars, case-insensitive. This is a tendency
|
|
42
|
+
// signal, never proof — the log keeps first_sentence for human review.
|
|
43
|
+
const DETECT_WINDOW = 120
|
|
44
|
+
|
|
45
|
+
export function detectMode(rawText, source = 'text') {
|
|
46
|
+
const head = String(rawText || '').replace(/^\s+/, '').slice(0, DETECT_WINDOW).toLowerCase()
|
|
47
|
+
if (head === '') return 'unknown'
|
|
48
|
+
if (head.startsWith('let me')) return 'pure'
|
|
49
|
+
if (head.startsWith('the user')) return 'med'
|
|
50
|
+
if (/^we[\s,']/.test(head)) return 'god'
|
|
51
|
+
// "User wants/asks…" (article dropped) — soft-med variant observed when
|
|
52
|
+
// injected context is present under a minimal first request (cell M.2.1).
|
|
53
|
+
if (source === 'reasoning' && /^user (wants|asks|is asking|said)\b/.test(head)) return 'med'
|
|
54
|
+
// 中文推理起手 "我们需要/我们应该/我们来" — god-equivalent (reasoning only).
|
|
55
|
+
if (source === 'reasoning' && /^我们(需要|应该|先来|来|先)/.test(head)) return 'god'
|
|
56
|
+
const firstSentence = head.split(/[.!?\n]/)[0] || head
|
|
57
|
+
const weCount = (firstSentence.match(/\bwe\b/g) || []).length
|
|
58
|
+
if (weCount >= 3) return 'god'
|
|
59
|
+
return 'unknown'
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** First sentence (or window excerpt) for human review in the log. */
|
|
63
|
+
function firstSentenceOf(rawText) {
|
|
64
|
+
const text = String(rawText || '').replace(/\s+/g, ' ').trim()
|
|
65
|
+
const m = /^(.{1,160}?[.!?])(\s|$)/.exec(text)
|
|
66
|
+
return (m ? m[1] : text.slice(0, 160)).trim()
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ── Wilson score interval (95%) for a binomial proportion ───────────────────
|
|
70
|
+
function wilson(k, n) {
|
|
71
|
+
if (n <= 0) return { rate: 0, low: 0, high: 0 }
|
|
72
|
+
const z = 1.96
|
|
73
|
+
const p = k / n
|
|
74
|
+
const denom = 1 + (z * z) / n
|
|
75
|
+
const center = (p + (z * z) / (2 * n)) / denom
|
|
76
|
+
const half = (z * Math.sqrt((p * (1 - p)) / n + (z * z) / (4 * n * n))) / denom
|
|
77
|
+
return { rate: p, low: Math.max(0, center - half), high: Math.min(1, center + half) }
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ── storage paths ────────────────────────────────────────────────────────────
|
|
81
|
+
function dshHome() {
|
|
82
|
+
return process.env.DSH_HOME || join(homedir(), '.dsh')
|
|
83
|
+
}
|
|
84
|
+
function dataDir() {
|
|
85
|
+
return join(dshHome(), 'deus-mode')
|
|
86
|
+
}
|
|
87
|
+
function logFile() {
|
|
88
|
+
return join(dataDir(), 'log.jsonl')
|
|
89
|
+
}
|
|
90
|
+
function presetsFile() {
|
|
91
|
+
return join(dataDir(), 'presets.json')
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function ensureDir() {
|
|
95
|
+
mkdirSync(dataDir(), { recursive: true })
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ── preset library: built-ins until the user edits, then presets.json wins ──
|
|
99
|
+
function loadPresets() {
|
|
100
|
+
try {
|
|
101
|
+
if (existsSync(presetsFile())) {
|
|
102
|
+
const list = JSON.parse(readFileSync(presetsFile(), 'utf8'))
|
|
103
|
+
if (Array.isArray(list)) return list
|
|
104
|
+
}
|
|
105
|
+
} catch { /* fall through to built-ins */ }
|
|
106
|
+
return BUILTIN_PRESETS.map((p) => ({ ...p, builtin: true }))
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function savePresets(list) {
|
|
110
|
+
ensureDir()
|
|
111
|
+
const clean = list
|
|
112
|
+
.filter((p) => p && typeof p.id === 'string' && p.id.trim() !== '')
|
|
113
|
+
.map((p) => ({
|
|
114
|
+
id: String(p.id).trim(),
|
|
115
|
+
label_zh: String(p.label_zh || p.id),
|
|
116
|
+
label_en: String(p.label_en || p.id),
|
|
117
|
+
prompt: p.prompt === null || p.prompt === undefined ? null : String(p.prompt),
|
|
118
|
+
tags: Array.isArray(p.tags) ? p.tags.map(String) : [],
|
|
119
|
+
}))
|
|
120
|
+
writeFileSync(presetsFile(), JSON.stringify(clean, null, 2))
|
|
121
|
+
return clean
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ── experiment log ───────────────────────────────────────────────────────────
|
|
125
|
+
function appendLog(entry) {
|
|
126
|
+
ensureDir()
|
|
127
|
+
appendFileSync(logFile(), JSON.stringify(entry) + '\n')
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function readLog(limit = 500) {
|
|
131
|
+
try {
|
|
132
|
+
if (!existsSync(logFile())) return []
|
|
133
|
+
const lines = readFileSync(logFile(), 'utf8').split('\n').filter(Boolean)
|
|
134
|
+
const out = []
|
|
135
|
+
for (const line of lines.slice(-limit)) {
|
|
136
|
+
try { out.push(JSON.parse(line)) } catch { /* skip corrupt line */ }
|
|
137
|
+
}
|
|
138
|
+
return out
|
|
139
|
+
} catch {
|
|
140
|
+
return []
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function statsFrom(entries) {
|
|
145
|
+
const byMode = new Map()
|
|
146
|
+
for (const e of entries) {
|
|
147
|
+
const mode = e.prompt_mode || 'unknown'
|
|
148
|
+
if (!byMode.has(mode)) byMode.set(mode, { mode, n: 0, pure: 0, med: 0, god: 0, unknown: 0 })
|
|
149
|
+
const row = byMode.get(mode)
|
|
150
|
+
row.n += 1
|
|
151
|
+
if (row[e.detected] !== undefined) row[e.detected] += 1
|
|
152
|
+
else row.unknown += 1
|
|
153
|
+
}
|
|
154
|
+
const modes = [...byMode.values()].map((row) => ({
|
|
155
|
+
...row,
|
|
156
|
+
godCI: wilson(row.god, row.n),
|
|
157
|
+
medCI: wilson(row.med, row.n),
|
|
158
|
+
pureCI: wilson(row.pure, row.n),
|
|
159
|
+
}))
|
|
160
|
+
const total = { mode: 'ALL', n: 0, pure: 0, med: 0, god: 0, unknown: 0 }
|
|
161
|
+
for (const row of modes) {
|
|
162
|
+
total.n += row.n; total.pure += row.pure; total.med += row.med; total.god += row.god; total.unknown += row.unknown
|
|
163
|
+
}
|
|
164
|
+
return { modes, total: { ...total, godCI: wilson(total.god, total.n) } }
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function toCsv(entries) {
|
|
168
|
+
const head = 'ts,sessionId,prompt_mode,prompt_text,detected,source,first_sentence,model'
|
|
169
|
+
const esc = (v) => `"${String(v ?? '').replace(/"/g, '""')}"`
|
|
170
|
+
const rows = entries.map((e) =>
|
|
171
|
+
[e.ts, e.sessionId, e.prompt_mode, e.prompt_text, e.detected, e.source, e.first_sentence, e.model].map(esc).join(','))
|
|
172
|
+
return [head, ...rows].join('\n') + '\n'
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// ── version auto-detect (differentiator: no manual checking) ────────────────
|
|
176
|
+
function detectDshVersion() {
|
|
177
|
+
if (process.env.DSH_VERSION) return process.env.DSH_VERSION
|
|
178
|
+
const candidates = [
|
|
179
|
+
'@deepseek-ai/dsh/package.json',
|
|
180
|
+
'@dsh-ai/deepseek-cli/package.json', // published CLI name (verified 0.8.2)
|
|
181
|
+
'@deepseek-ai/dsh-cli/package.json',
|
|
182
|
+
'@deepseek-ai/dsh/apps/cli/package.json',
|
|
183
|
+
]
|
|
184
|
+
for (const base of [import.meta.url, path.join(process.cwd(), 'noop.js')]) {
|
|
185
|
+
try {
|
|
186
|
+
const req = createRequire(base)
|
|
187
|
+
for (const name of candidates) {
|
|
188
|
+
try {
|
|
189
|
+
const pkg = req(name)
|
|
190
|
+
if (pkg && pkg.version) return String(pkg.version)
|
|
191
|
+
} catch { /* next candidate */ }
|
|
192
|
+
}
|
|
193
|
+
} catch { /* next base */ }
|
|
194
|
+
}
|
|
195
|
+
// Filesystem fallback: walk up from likely entry points (process.argv[1] =
|
|
196
|
+
// the CLI entry being executed — works for dev checkouts AND npx cache
|
|
197
|
+
// installs; cwd as a secondary seed) looking for a dsh-named package.json.
|
|
198
|
+
const seeds = [process.argv[1], process.cwd(), process.argv[0]].filter(Boolean)
|
|
199
|
+
for (const seed of seeds) {
|
|
200
|
+
try {
|
|
201
|
+
let dir = fs.statSync(seed).isDirectory() ? seed : join(seed, '..')
|
|
202
|
+
for (let i = 0; i < 8; i++) {
|
|
203
|
+
for (const rel of ['apps/cli/package.json', 'package.json', '@deepseek-ai/dsh/package.json', '@dsh-ai/deepseek-cli/package.json']) {
|
|
204
|
+
try {
|
|
205
|
+
const pkg = JSON.parse(fs.readFileSync(join(dir, rel), 'utf8'))
|
|
206
|
+
if (pkg && pkg.version && /dsh|deepseek/i.test(String(pkg.name || ''))) return String(pkg.version)
|
|
207
|
+
} catch { /* keep walking */ }
|
|
208
|
+
}
|
|
209
|
+
const up = join(dir, '..')
|
|
210
|
+
if (up === dir) break
|
|
211
|
+
dir = up
|
|
212
|
+
}
|
|
213
|
+
} catch { /* next seed */ }
|
|
214
|
+
}
|
|
215
|
+
return 'unknown'
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** Concatenated visible text from content blocks (structural, no type dep). */
|
|
219
|
+
function textOf(content) {
|
|
220
|
+
let out = ''
|
|
221
|
+
if (!Array.isArray(content)) return out
|
|
222
|
+
for (const block of content) {
|
|
223
|
+
if (block && typeof block === 'object' && block.type === 'text' && typeof block.text === 'string') out += block.text
|
|
224
|
+
}
|
|
225
|
+
return out
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export function apply(ctx) {
|
|
229
|
+
// Pending trigger marks: sessionId -> { mode, prompt_text, ts, auto }
|
|
230
|
+
const pending = new Map()
|
|
231
|
+
// Chunk accumulation while a trigger is pending: sessionId -> { turn, step, reasoning, text }
|
|
232
|
+
// (实测校准:指纹在 reasoning-delta 流里,text-delta 兜底;见 detectMode 注释)
|
|
233
|
+
const buffers = new Map()
|
|
234
|
+
|
|
235
|
+
function finalize(sessionId, fallbackText) {
|
|
236
|
+
const mark = pending.get(sessionId)
|
|
237
|
+
if (!mark) return
|
|
238
|
+
const buf = buffers.get(sessionId)
|
|
239
|
+
const reasoning = (buf && buf.reasoning) || ''
|
|
240
|
+
const text = (buf && buf.text) || fallbackText || ''
|
|
241
|
+
if (reasoning.trim() === '' && text.trim() === '' && !fallbackText) return // keep waiting for real content
|
|
242
|
+
pending.delete(sessionId)
|
|
243
|
+
buffers.delete(sessionId)
|
|
244
|
+
// 指纹优先取推理流(实测校准:指纹在 reasoning-delta 里),可见文本兜底
|
|
245
|
+
const source = reasoning.trim() !== '' ? 'reasoning' : 'text'
|
|
246
|
+
const classifyOn = source === 'reasoning' ? reasoning : text
|
|
247
|
+
appendLog({
|
|
248
|
+
ts: new Date().toISOString(),
|
|
249
|
+
sessionId,
|
|
250
|
+
prompt_mode: mark.mode,
|
|
251
|
+
prompt_text: mark.prompt_text,
|
|
252
|
+
detected: detectMode(classifyOn, source),
|
|
253
|
+
source,
|
|
254
|
+
first_sentence: firstSentenceOf(classifyOn),
|
|
255
|
+
model: mark.model || undefined,
|
|
256
|
+
})
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
ctx.on('session/event', (session, event) => {
|
|
260
|
+
const sessionId = String(session.id)
|
|
261
|
+
|
|
262
|
+
// A user message can BE the trigger: exact-match against the preset
|
|
263
|
+
// library covers the conservative "copy + paste + enter" path (档 A)
|
|
264
|
+
// without any explicit marking from the panel.
|
|
265
|
+
if (event.type === 'user/message') {
|
|
266
|
+
const text = textOf(event.data && event.data.content).trim()
|
|
267
|
+
if (text !== '' && !pending.has(sessionId)) {
|
|
268
|
+
const hit = loadPresets().find((p) => p.prompt !== null && p.prompt.trim() === text)
|
|
269
|
+
if (hit) {
|
|
270
|
+
pending.set(sessionId, { mode: hit.id, prompt_text: hit.prompt, ts: Date.now(), auto: true })
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (!pending.has(sessionId)) return
|
|
277
|
+
|
|
278
|
+
if (event.type === 'assistant/chunk') {
|
|
279
|
+
const chunk = event.data && event.data.chunk
|
|
280
|
+
const isReasoning = chunk && chunk.type === 'reasoning-delta' && typeof chunk.text === 'string'
|
|
281
|
+
const isText = chunk && chunk.type === 'text-delta' && typeof chunk.text === 'string'
|
|
282
|
+
if (isReasoning || isText) {
|
|
283
|
+
const key = sessionId
|
|
284
|
+
const cur = buffers.get(key) || { turn: event.data.turn, step: event.data.step, reasoning: '', text: '' }
|
|
285
|
+
if (cur.turn !== event.data.turn || cur.step !== event.data.step) {
|
|
286
|
+
cur.turn = event.data.turn; cur.step = event.data.step; cur.reasoning = ''; cur.text = ''
|
|
287
|
+
}
|
|
288
|
+
if (isReasoning) cur.reasoning += chunk.text
|
|
289
|
+
else cur.text += chunk.text
|
|
290
|
+
buffers.set(key, cur)
|
|
291
|
+
// Early finalize once the opener is decidable. 指纹主要在推理流
|
|
292
|
+
// (实测:reasoning-delta 远多于 text-delta,"The user…/We need…" 起手都在推理里),
|
|
293
|
+
// 推理流优先判定;可见文本兜底。
|
|
294
|
+
const head = cur.reasoning !== '' ? cur.reasoning : cur.text
|
|
295
|
+
if ((head.length >= 8 && /[.!?\n]/.test(head)) || head.length >= DETECT_WINDOW) finalize(sessionId)
|
|
296
|
+
}
|
|
297
|
+
return
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (event.type === 'assistant/message') {
|
|
301
|
+
// 完整消息里也可能带 reasoning block,能拿到就优先用它
|
|
302
|
+
const parts = event.data && event.data.message && event.data.message.content
|
|
303
|
+
let reasoning = ''
|
|
304
|
+
if (Array.isArray(parts)) {
|
|
305
|
+
for (const p of parts) {
|
|
306
|
+
if (p && typeof p === 'object' && p.type === 'reasoning' && typeof p.text === 'string') reasoning += p.text
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
if (reasoning !== '') {
|
|
310
|
+
const cur = buffers.get(sessionId) || { reasoning: '', text: '' }
|
|
311
|
+
if (cur.reasoning === '') { cur.reasoning = reasoning; buffers.set(sessionId, cur) }
|
|
312
|
+
}
|
|
313
|
+
finalize(sessionId, textOf(parts))
|
|
314
|
+
return
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (event.type === 'turn/end') {
|
|
318
|
+
finalize(sessionId, '')
|
|
319
|
+
}
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
ctx.effect(() => {
|
|
323
|
+
const text = (code, body, gz, mime) => (req, res) => {
|
|
324
|
+
const ae = String(req.headers['accept-encoding'] || '')
|
|
325
|
+
const useGz = gz && /\bgzip\b/.test(ae)
|
|
326
|
+
res.writeHead(code, { 'content-type': mime, ...(useGz ? { 'content-encoding': 'gzip' } : {}), 'content-length': useGz ? gz.length : body.length })
|
|
327
|
+
res.end(useGz ? gz : body)
|
|
328
|
+
}
|
|
329
|
+
const json = (body, code = 200) => {
|
|
330
|
+
const buf = Buffer.from(JSON.stringify(body), 'utf8')
|
|
331
|
+
return text(code, buf, gzipSync(buf), 'application/json; charset=utf-8')
|
|
332
|
+
}
|
|
333
|
+
const readBody = (req, cb) => {
|
|
334
|
+
let raw = ''
|
|
335
|
+
req.on('data', (c) => { raw += c })
|
|
336
|
+
req.on('end', () => {
|
|
337
|
+
try { cb(JSON.parse(raw || '{}')) } catch { cb({}) }
|
|
338
|
+
})
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const disposers = [
|
|
342
|
+
ctx.webServer.register({ kind: 'exact', path: '/deus/presets', handler: (req, res) => {
|
|
343
|
+
if (req.method === 'POST') {
|
|
344
|
+
readBody(req, (body) => {
|
|
345
|
+
const list = Array.isArray(body.presets) ? savePresets(body.presets) : null
|
|
346
|
+
json(list ? { ok: true, presets: list } : { ok: false, error: 'missing presets' }, list ? 200 : 400)(req, res)
|
|
347
|
+
})
|
|
348
|
+
return
|
|
349
|
+
}
|
|
350
|
+
json({ presets: loadPresets(), customized: existsSync(presetsFile()) })(req, res)
|
|
351
|
+
} }),
|
|
352
|
+
ctx.webServer.register({ kind: 'exact', path: '/deus/presets/reset', handler: (req, res) => {
|
|
353
|
+
try {
|
|
354
|
+
if (existsSync(presetsFile())) writeFileSync(presetsFile(), JSON.stringify(BUILTIN_PRESETS, null, 2))
|
|
355
|
+
} catch { /* ignore */ }
|
|
356
|
+
json({ ok: true, presets: loadPresets() })(req, res)
|
|
357
|
+
} }),
|
|
358
|
+
ctx.webServer.register({ kind: 'exact', path: '/deus/trigger', handler: (req, res) => {
|
|
359
|
+
readBody(req, (body) => {
|
|
360
|
+
const sessionId = String(body.sessionId || '')
|
|
361
|
+
const mode = String(body.mode || '')
|
|
362
|
+
const preset = loadPresets().find((p) => p.id === mode)
|
|
363
|
+
if (!sessionId || !preset) {
|
|
364
|
+
json({ ok: false, error: 'missing sessionId or unknown mode' }, 400)(req, res)
|
|
365
|
+
return
|
|
366
|
+
}
|
|
367
|
+
pending.set(sessionId, { mode, prompt_text: preset.prompt, ts: Date.now(), auto: false })
|
|
368
|
+
json({ ok: true })(req, res)
|
|
369
|
+
})
|
|
370
|
+
} }),
|
|
371
|
+
ctx.webServer.register({ kind: 'exact', path: '/deus/log', handler: (req, res) => {
|
|
372
|
+
if (req.method === 'DELETE') {
|
|
373
|
+
try { writeFileSync(logFile(), '') } catch { /* ignore */ }
|
|
374
|
+
json({ ok: true })(req, res)
|
|
375
|
+
return
|
|
376
|
+
}
|
|
377
|
+
json({ entries: readLog(500).slice(-100).reverse() })(req, res)
|
|
378
|
+
} }),
|
|
379
|
+
ctx.webServer.register({ kind: 'exact', path: '/deus/log.csv', handler: (req, res) => {
|
|
380
|
+
const buf = Buffer.from(toCsv(readLog(10000)), 'utf8')
|
|
381
|
+
text(200, buf, gzipSync(buf), 'text/csv; charset=utf-8')(req, res)
|
|
382
|
+
} }),
|
|
383
|
+
ctx.webServer.register({ kind: 'exact', path: '/deus/stats', handler: (req, res) => {
|
|
384
|
+
json(statsFrom(readLog(10000)))(req, res)
|
|
385
|
+
} }),
|
|
386
|
+
ctx.webServer.register({ kind: 'exact', path: '/deus/version', handler: (req, res) => {
|
|
387
|
+
let dsh = 'unknown'
|
|
388
|
+
try { dsh = detectDshVersion() } catch (e) { ctx.logger.warn('[plugin-deus] version detect failed:', e) }
|
|
389
|
+
json({ plugin: '0.1.0', dsh, logPath: logFile() })(req, res)
|
|
390
|
+
} }),
|
|
391
|
+
]
|
|
392
|
+
return () => { for (const d of disposers) d() }
|
|
393
|
+
}, 'deus-mode: routes')
|
|
394
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dsh-suite/plugin-deus",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "神模扳机 Deus Trigger — 极简提示词触发实验台:预设库 + 一键注入 + 起手识别器 + 触发率统计(Wilson CI)。社区观察未获官方证实,本插件不承诺解锁任何隐藏能力。",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "lib/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./lib/index.js",
|
|
9
|
+
"./client": "./lib/client.js",
|
|
10
|
+
"./package.json": "./package.json"
|
|
11
|
+
},
|
|
12
|
+
"dsh": {
|
|
13
|
+
"bundle": {
|
|
14
|
+
"patch": "./cordis.patch.yml"
|
|
15
|
+
},
|
|
16
|
+
"client": {
|
|
17
|
+
"inject": [
|
|
18
|
+
"@deepseek-ai/dsh-client-runtime",
|
|
19
|
+
"@deepseek-ai/dsh-client-ui-slots",
|
|
20
|
+
"@deepseek-ai/dsh-client-ui-settings",
|
|
21
|
+
"@deepseek-ai/dsh-client-locale"
|
|
22
|
+
],
|
|
23
|
+
"platform": "web"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"lib",
|
|
28
|
+
"README.md",
|
|
29
|
+
"cordis.patch.yml"
|
|
30
|
+
],
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": "^22.19 || >=24"
|
|
33
|
+
},
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"keywords": [
|
|
36
|
+
"deepseek-harness",
|
|
37
|
+
"dsh-plugin",
|
|
38
|
+
"deus",
|
|
39
|
+
"prompt-experiment",
|
|
40
|
+
"a-b-test",
|
|
41
|
+
"极简提示词",
|
|
42
|
+
"触发率统计"
|
|
43
|
+
]
|
|
44
|
+
}
|