dsh-plugin-tlrp 0.3.0 → 0.4.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 +28 -5
- package/client.js +306 -12
- package/lib/core.cjs +2636 -2551
- package/lib/schema.cjs +192 -188
- package/locale/en.json +20 -19
- package/locale/zh.json +20 -19
- package/package.json +4 -3
- package/scripts/apply-check.mjs +43 -0
- package/scripts/client-check.mjs +502 -445
- package/test/style.test.mjs +336 -0
package/lib/schema.cjs
CHANGED
|
@@ -1,188 +1,192 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* tlrp 的 Cordis Config(Schemastery)。
|
|
5
|
-
*
|
|
6
|
-
* 这个 schema 同时就是「设置界面」:`@deepseek-ai/dsh-settings` 只把
|
|
7
|
-
* `.volatile()` 字段投影成表单,也只允许写 volatile 路径,所以所有需要
|
|
8
|
-
* 在设置面板里改、或者需要被 `/rp` `/work` `/char` `/scene` 指令改的字段
|
|
9
|
-
* 一律标 `.volatile()`。
|
|
10
|
-
*
|
|
11
|
-
* `.volatile()` 之后字段的解析结果是 live ref(`{ get() }`),运行期读数
|
|
12
|
-
* 必须走 `core.live()`,否则热更新看到的是启动时的旧快照。
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
const core = require('./core.cjs')
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* 构建 Config schema。
|
|
19
|
-
* @param {any} z `@deepseek-ai/schemastery` 的导出(默认导出)
|
|
20
|
-
* @returns {any} Schemastery schema
|
|
21
|
-
*/
|
|
22
|
-
function buildConfig(z) {
|
|
23
|
-
const characterShape = z.object({
|
|
24
|
-
id: z.string().default(''),
|
|
25
|
-
name: z.string().default(''),
|
|
26
|
-
userTitle: z.string().default(''),
|
|
27
|
-
identityLock: z.string().default(''),
|
|
28
|
-
corePersona: z.string().default(''),
|
|
29
|
-
defaultScene: z.string().default(''),
|
|
30
|
-
appearanceAndSensory: z.string().default(''),
|
|
31
|
-
actionPresets: z.string().default(''),
|
|
32
|
-
speechStyle: z.string().default(''),
|
|
33
|
-
dialogueExamples: z.string().default(''),
|
|
34
|
-
statusFooterTemplate: z.string().default(''),
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
const triggerShape = z.object({
|
|
38
|
-
id: z.string().default(''),
|
|
39
|
-
characterId: z.string().default(''),
|
|
40
|
-
keywords: z.array(z.string()).default([]),
|
|
41
|
-
injectText: z.string().default(''),
|
|
42
|
-
priority: z.number().default(0),
|
|
43
|
-
enabled: z.boolean().default(true),
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
const modeShape = z.object({
|
|
47
|
-
id: z.string().default(''),
|
|
48
|
-
name: z.string().default(''),
|
|
49
|
-
commandAliases: z.array(z.string()).default([]),
|
|
50
|
-
autoKeywords: z.string().default(''),
|
|
51
|
-
autoPriority: z.number().default(50),
|
|
52
|
-
directivePrompt: z.string().default(''),
|
|
53
|
-
toolPolicy: z.union(['allow_all', 'whitelist_only', 'block_all']).default('allow_all'),
|
|
54
|
-
allowedTools: z.string().default(''),
|
|
55
|
-
injectCharacterPersona: z.boolean().default(true),
|
|
56
|
-
enableStatusFooter: z.boolean().default(true),
|
|
57
|
-
statusFooterOverride: z.string().default(''),
|
|
58
|
-
bindCharacterId: z.string().default(''),
|
|
59
|
-
isDefaultFallback: z.boolean().default(false),
|
|
60
|
-
styleLayer: z
|
|
61
|
-
.union(['inherit', 'on', 'off'])
|
|
62
|
-
.default('inherit')
|
|
63
|
-
.description(
|
|
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
|
-
.default(
|
|
155
|
-
.description('
|
|
156
|
-
|
|
157
|
-
.
|
|
158
|
-
.default(
|
|
159
|
-
.description('tlmemory
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
.
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
.
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
.array(z.string())
|
|
174
|
-
.default([...core.
|
|
175
|
-
.description('
|
|
176
|
-
|
|
177
|
-
.string()
|
|
178
|
-
.default(
|
|
179
|
-
.description('
|
|
180
|
-
|
|
181
|
-
.string()
|
|
182
|
-
.default(
|
|
183
|
-
.description('
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* tlrp 的 Cordis Config(Schemastery)。
|
|
5
|
+
*
|
|
6
|
+
* 这个 schema 同时就是「设置界面」:`@deepseek-ai/dsh-settings` 只把
|
|
7
|
+
* `.volatile()` 字段投影成表单,也只允许写 volatile 路径,所以所有需要
|
|
8
|
+
* 在设置面板里改、或者需要被 `/rp` `/work` `/char` `/scene` 指令改的字段
|
|
9
|
+
* 一律标 `.volatile()`。
|
|
10
|
+
*
|
|
11
|
+
* `.volatile()` 之后字段的解析结果是 live ref(`{ get() }`),运行期读数
|
|
12
|
+
* 必须走 `core.live()`,否则热更新看到的是启动时的旧快照。
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const core = require('./core.cjs')
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 构建 Config schema。
|
|
19
|
+
* @param {any} z `@deepseek-ai/schemastery` 的导出(默认导出)
|
|
20
|
+
* @returns {any} Schemastery schema
|
|
21
|
+
*/
|
|
22
|
+
function buildConfig(z) {
|
|
23
|
+
const characterShape = z.object({
|
|
24
|
+
id: z.string().default(''),
|
|
25
|
+
name: z.string().default(''),
|
|
26
|
+
userTitle: z.string().default(''),
|
|
27
|
+
identityLock: z.string().default(''),
|
|
28
|
+
corePersona: z.string().default(''),
|
|
29
|
+
defaultScene: z.string().default(''),
|
|
30
|
+
appearanceAndSensory: z.string().default(''),
|
|
31
|
+
actionPresets: z.string().default(''),
|
|
32
|
+
speechStyle: z.string().default(''),
|
|
33
|
+
dialogueExamples: z.string().default(''),
|
|
34
|
+
statusFooterTemplate: z.string().default(''),
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
const triggerShape = z.object({
|
|
38
|
+
id: z.string().default(''),
|
|
39
|
+
characterId: z.string().default(''),
|
|
40
|
+
keywords: z.array(z.string()).default([]),
|
|
41
|
+
injectText: z.string().default(''),
|
|
42
|
+
priority: z.number().default(0),
|
|
43
|
+
enabled: z.boolean().default(true),
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
const modeShape = z.object({
|
|
47
|
+
id: z.string().default(''),
|
|
48
|
+
name: z.string().default(''),
|
|
49
|
+
commandAliases: z.array(z.string()).default([]),
|
|
50
|
+
autoKeywords: z.string().default(''),
|
|
51
|
+
autoPriority: z.number().default(50),
|
|
52
|
+
directivePrompt: z.string().default(''),
|
|
53
|
+
toolPolicy: z.union(['allow_all', 'whitelist_only', 'block_all']).default('allow_all'),
|
|
54
|
+
allowedTools: z.string().default(''),
|
|
55
|
+
injectCharacterPersona: z.boolean().default(true),
|
|
56
|
+
enableStatusFooter: z.boolean().default(true),
|
|
57
|
+
statusFooterOverride: z.string().default(''),
|
|
58
|
+
bindCharacterId: z.string().default(''),
|
|
59
|
+
isDefaultFallback: z.boolean().default(false),
|
|
60
|
+
styleLayer: z
|
|
61
|
+
.union(['inherit', 'on', 'dialog', 'off'])
|
|
62
|
+
.default('inherit')
|
|
63
|
+
.description(
|
|
64
|
+
'风格层开关:on 只带语气、dialog 对话体(只说话,禁旁白/动作/环境/外貌)、off 强制关、inherit 跟随全局 styleLayerDefault。',
|
|
65
|
+
),
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
return z.object({
|
|
69
|
+
/* ── 设置面板字段(全部 volatile) ── */
|
|
70
|
+
enabled: z
|
|
71
|
+
.boolean()
|
|
72
|
+
.default(true)
|
|
73
|
+
.description('插件总开关。关闭后不注入任何角色、形态约束与工具拦截。')
|
|
74
|
+
.volatile(),
|
|
75
|
+
mode: z
|
|
76
|
+
.string()
|
|
77
|
+
.default('auto')
|
|
78
|
+
.description('路由锁定:auto 按各形态的 autoKeywords 自动分流;填任意已注册形态 id 则锁死在该形态。聊天里用 /auto、/mode <id> 或形态别名切换。')
|
|
79
|
+
.volatile(),
|
|
80
|
+
modes: z
|
|
81
|
+
.array(modeShape)
|
|
82
|
+
.default(core.DEFAULT_MODES.map((entry) => ({ ...entry, commandAliases: [...entry.commandAliases] })))
|
|
83
|
+
.description('运行形态矩阵:每个形态含 id / name / commandAliases / autoKeywords / autoPriority / directivePrompt / toolPolicy / allowedTools / injectCharacterPersona / enableStatusFooter / statusFooterOverride / bindCharacterId / isDefaultFallback。增删改后即时生效。')
|
|
84
|
+
.volatile(),
|
|
85
|
+
activeCharacterId: z
|
|
86
|
+
.string()
|
|
87
|
+
.default('firefly')
|
|
88
|
+
.description('当前激活的预设角色 ID(见 characters)。聊天里用 /char <id> 切换。')
|
|
89
|
+
.volatile(),
|
|
90
|
+
workModeKeywords: z
|
|
91
|
+
.union([z.string(), z.array(z.string())])
|
|
92
|
+
.default(core.DEFAULT_WORK_KEYWORDS)
|
|
93
|
+
.description('(0.2.0 兼容字段)行为族为 work 的形态若没写自己的 autoKeywords,则用这里的关键词做自动分流。新形态请直接在形态自己的 autoKeywords 里配置。')
|
|
94
|
+
.volatile(),
|
|
95
|
+
blockShellToolsInRp: z
|
|
96
|
+
.boolean()
|
|
97
|
+
.default(true)
|
|
98
|
+
.description('(0.2.0 兼容字段)行为族为 rp 的形态在 whitelist_only 策略下是否物理拦截工具;0.3.0 起建议改用形态自己的 toolPolicy。')
|
|
99
|
+
.volatile(),
|
|
100
|
+
enableStatusFooter: z
|
|
101
|
+
.boolean()
|
|
102
|
+
.default(true)
|
|
103
|
+
.description('状态面板总开关;与形态自己的 enableStatusFooter 取「与」。')
|
|
104
|
+
.volatile(),
|
|
105
|
+
customSceneOverride: z
|
|
106
|
+
.string()
|
|
107
|
+
.default('')
|
|
108
|
+
.description('当前临时覆盖的剧情情景;留空则使用角色的 defaultScene。聊天里用 /scene <描述> 覆盖。')
|
|
109
|
+
.volatile(),
|
|
110
|
+
characters: z
|
|
111
|
+
.array(characterShape)
|
|
112
|
+
.default(core.DEFAULT_CHARACTERS.map((entry) => ({ ...entry })))
|
|
113
|
+
.description('多角色预设库。每个角色含 id / name / userTitle / identityLock / defaultScene / appearanceAndSensory / actionPresets / speechStyle / statusFooterTemplate。')
|
|
114
|
+
.volatile(),
|
|
115
|
+
triggers: z
|
|
116
|
+
.array(triggerShape)
|
|
117
|
+
.default(core.DEFAULT_TRIGGERS.map((entry) => ({ ...entry, keywords: [...entry.keywords] })))
|
|
118
|
+
.description('提示词触发映射表(动态世界书):命中 keywords 即把 injectText 注入本轮上下文。')
|
|
119
|
+
.volatile(),
|
|
120
|
+
|
|
121
|
+
/* ── 风格层:只带语言风格 + 表情 + 本地表情包/图库 ── */
|
|
122
|
+
styleLayerDefault: z
|
|
123
|
+
.union(['on', 'dialog', 'off'])
|
|
124
|
+
.default('off')
|
|
125
|
+
.description(
|
|
126
|
+
'风格层全局默认:形态自己的 styleLayer 留 inherit 时按这里走。on 只带语气、dialog 只说话(不写旁白与动作)。聊天里用 /style on|dialog|off|default 临时改本会话。',
|
|
127
|
+
)
|
|
128
|
+
.volatile(),
|
|
129
|
+
styleEmojiHint: z
|
|
130
|
+
.string()
|
|
131
|
+
.default('允许适度使用 emoji:一屏最多 2 个,放在句尾,不堆在句首,也不用 emoji 替代文字。')
|
|
132
|
+
.description('风格层的表情规范;留空则完全不出现 emoji。')
|
|
133
|
+
.volatile(),
|
|
134
|
+
stickerDir: z
|
|
135
|
+
.string()
|
|
136
|
+
.default('')
|
|
137
|
+
.description('本地表情包库目录;留空为 ~/.dsh/stickers。子目录名即角色 id,图片文件名即贴纸标签(如 开心.png → 标签「开心」)。')
|
|
138
|
+
.volatile(),
|
|
139
|
+
galleryDir: z
|
|
140
|
+
.string()
|
|
141
|
+
.default('')
|
|
142
|
+
.description('本地图库目录;留空为 ~/.dsh/gallery。图片文件名即图片标签,供「给我看图」这类请求使用。')
|
|
143
|
+
.volatile(),
|
|
144
|
+
maxStickersPerTurn: z
|
|
145
|
+
.number()
|
|
146
|
+
.default(2)
|
|
147
|
+
.description('单轮最多贴几张表情包或图片(0 = 只准用文字描述,不许贴图)。')
|
|
148
|
+
.volatile(),
|
|
149
|
+
|
|
150
|
+
/* ── 以下为普通配置(通过 profile 的 cordis.patch.yml 调整) ── */
|
|
151
|
+
timeZone: z.string().default('Asia/Shanghai').description('{{now}} 与状态块使用的时区。'), statusFooterLabel: z.string().default('流萤状态同步').description('底部状态面板的标题标签。'),
|
|
152
|
+
registerNowVariable: z
|
|
153
|
+
.union(['auto', 'off'])
|
|
154
|
+
.default('auto')
|
|
155
|
+
.description('是否尝试注册 {{now}} / {{cwd}}:auto 表示已被 prompt-clock 占用时自动让位,off 表示完全不注册。'),
|
|
156
|
+
enableMemoryBridge: z
|
|
157
|
+
.boolean()
|
|
158
|
+
.default(true)
|
|
159
|
+
.description('是否每轮从 tlmemory 的 SQLite 只读读取常驻羁绊(pinned)与关键词命中;驱动缺失或库不存在时自动降级为空。'),
|
|
160
|
+
memoryDbPath: z
|
|
161
|
+
.string()
|
|
162
|
+
.default('')
|
|
163
|
+
.description('tlmemory 数据库路径;留空则依次取环境变量 TLMEMORY_DB、~/.dsh/tlmemory.db。'),
|
|
164
|
+
coreSectionEnabled: z.boolean().default(true).description('是否注册 {{tlrp_character}} / {{tlrp_directive}} 所在的核心 prompt section。'),
|
|
165
|
+
coreSectionOrder: z.number().default(10150).description('核心 section 的排序位置(默认在 first-party 指引之后、persona suffix 之前)。'),
|
|
166
|
+
coreSectionText: z
|
|
167
|
+
.string()
|
|
168
|
+
.default('## tlrp 角色与形态装配\n当前时间:{{now}}\n工作目录:{{cwd}}\n\n{{tlrp_character}}\n\n{{tlrp_directive}}')
|
|
169
|
+
.description('核心 section 的模板;只允许 {{now}} / {{cwd}} / {{tlrp_character}} / {{tlrp_directive}} / {{tlrp_memory_context}} 五个占位符,其余会被中和。'),
|
|
170
|
+
contextOrder: z.number().default(300).description('每轮尾部实时状态块的 context 排序位置。'),
|
|
171
|
+
maxTriggerInjections: z.number().default(3).description('单轮最多注入多少组触发器(上限 8)。'),
|
|
172
|
+
blockedToolPatterns: z
|
|
173
|
+
.array(z.string())
|
|
174
|
+
.default([...core.DEFAULT_BLOCKED_TOOL_PATTERNS])
|
|
175
|
+
.description('RP 态下要拦截的工具名正则(deny 语义)。'),
|
|
176
|
+
allowedToolPatterns: z
|
|
177
|
+
.array(z.string())
|
|
178
|
+
.default([...core.DEFAULT_ALLOWED_TOOL_PATTERNS])
|
|
179
|
+
.description('无论什么模式都放行的工具名正则(allow 语义,优先于 deny)。'),
|
|
180
|
+
activeProfileEntryId: z
|
|
181
|
+
.string()
|
|
182
|
+
.default('tlrp')
|
|
183
|
+
.description('settings.mutate 使用的 profile 条目 id;改 bundle 行 id 时必须同步。'),
|
|
184
|
+
rpAllowedTools: z
|
|
185
|
+
.string()
|
|
186
|
+
.default(core.DEFAULT_RP_ALLOWED_TOOLS)
|
|
187
|
+
.description('RP 态放行的工具白名单(逗号分隔的工具名或前缀,默认 tlmemory / ask_user_question / schedule)。命中即放行,优先于拦截规则。')
|
|
188
|
+
.volatile(),
|
|
189
|
+
})
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
module.exports = { buildConfig }
|
package/locale/en.json
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
|
-
{
|
|
2
|
-
"meta": {
|
|
3
|
-
"title": "TLRP · Multi-Mode Matrix Engine",
|
|
4
|
-
"description": "Registers the tlrp settings namespace, an editable matrix of runtime modes, /mode and per-mode alias commands, live {{tlrp_character}} / {{tlrp_directive}} / {{now}} prompt variables, a per-turn tail status block, and physical tool gating driven by each mode's toolPolicy."
|
|
5
|
-
},
|
|
6
|
-
"command": {
|
|
7
|
-
"mode": "Switch to a runtime mode (/mode <mode-id>; auto restores routing)",
|
|
8
|
-
"rp": "Switch to the companion mode",
|
|
9
|
-
"work": "Switch to tactical work mode",
|
|
10
|
-
"sam": "Switch to SAM assault mode",
|
|
11
|
-
"sleep": "Switch to the medbay mode",
|
|
12
|
-
"auto": "Restore automatic semantic routing",
|
|
13
|
-
"char": "Switch the active preset character",
|
|
14
|
-
"scene": "Override the current scene",
|
|
15
|
-
"title": "Rename how the active character addresses you",
|
|
16
|
-
"trigger": "Add a keyword trigger mapping",
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"meta": {
|
|
3
|
+
"title": "TLRP · Multi-Mode Matrix Engine",
|
|
4
|
+
"description": "Registers the tlrp settings namespace, an editable matrix of runtime modes, /mode and per-mode alias commands, live {{tlrp_character}} / {{tlrp_directive}} / {{now}} prompt variables, a per-turn tail status block, and physical tool gating driven by each mode's toolPolicy."
|
|
5
|
+
},
|
|
6
|
+
"command": {
|
|
7
|
+
"mode": "Switch to a runtime mode (/mode <mode-id>; auto restores routing)",
|
|
8
|
+
"rp": "Switch to the companion mode",
|
|
9
|
+
"work": "Switch to tactical work mode",
|
|
10
|
+
"sam": "Switch to SAM assault mode",
|
|
11
|
+
"sleep": "Switch to the medbay mode",
|
|
12
|
+
"auto": "Restore automatic semantic routing",
|
|
13
|
+
"char": "Switch the active preset character",
|
|
14
|
+
"scene": "Override the current scene",
|
|
15
|
+
"title": "Rename how the active character addresses you",
|
|
16
|
+
"trigger": "Add a keyword trigger mapping",
|
|
17
|
+
"style": "Toggle the style layer: character voice plus emoji/stickers; dialog means speech only, no narration, actions or environment (/style on|dialog|off|default|status)",
|
|
18
|
+
"tlrp": "Show tlrp status: current mode, routing chain, character, scene, enabled triggers"
|
|
19
|
+
}
|
|
20
|
+
}
|
package/locale/zh.json
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
|
-
{
|
|
2
|
-
"meta": {
|
|
3
|
-
"title": "TLRP · 多形态矩阵引擎",
|
|
4
|
-
"description": "注册 tlrp 设置命名空间、可自由增删的 modes 形态矩阵、/mode 与形态别名指令、{{tlrp_character}} / {{tlrp_directive}} / {{now}} 动态提示变量、每轮尾部实时状态块,并按各形态的 toolPolicy 物理拦截工具。"
|
|
5
|
-
},
|
|
6
|
-
"command": {
|
|
7
|
-
"mode": "切换到指定运行形态(/mode <形态ID>;auto 恢复自动路由)",
|
|
8
|
-
"rp": "切换到少女陪伴态",
|
|
9
|
-
"work": "切换到战术工作态",
|
|
10
|
-
"sam": "切换到萨姆强袭态",
|
|
11
|
-
"sleep": "切换到医疗舱微凉态",
|
|
12
|
-
"auto": "恢复自动语义分流",
|
|
13
|
-
"char": "切换当前激活的预设角色",
|
|
14
|
-
"scene": "覆盖当前所处情景",
|
|
15
|
-
"title": "修改当前角色对对方的专属称呼",
|
|
16
|
-
"trigger": "新增一组关键词触发映射",
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"meta": {
|
|
3
|
+
"title": "TLRP · 多形态矩阵引擎",
|
|
4
|
+
"description": "注册 tlrp 设置命名空间、可自由增删的 modes 形态矩阵、/mode 与形态别名指令、{{tlrp_character}} / {{tlrp_directive}} / {{now}} 动态提示变量、每轮尾部实时状态块,并按各形态的 toolPolicy 物理拦截工具。"
|
|
5
|
+
},
|
|
6
|
+
"command": {
|
|
7
|
+
"mode": "切换到指定运行形态(/mode <形态ID>;auto 恢复自动路由)",
|
|
8
|
+
"rp": "切换到少女陪伴态",
|
|
9
|
+
"work": "切换到战术工作态",
|
|
10
|
+
"sam": "切换到萨姆强袭态",
|
|
11
|
+
"sleep": "切换到医疗舱微凉态",
|
|
12
|
+
"auto": "恢复自动语义分流",
|
|
13
|
+
"char": "切换当前激活的预设角色",
|
|
14
|
+
"scene": "覆盖当前所处情景",
|
|
15
|
+
"title": "修改当前角色对对方的专属称呼",
|
|
16
|
+
"trigger": "新增一组关键词触发映射",
|
|
17
|
+
"style": "风格层开关:只带角色语言风格与表情/本地贴纸;dialog 为对话体,只说话不写旁白、动作与环境(/style on|dialog|off|default|status)",
|
|
18
|
+
"tlrp": "查看 tlrp 状态:当前形态、路由链路、角色、情景、已启用触发器"
|
|
19
|
+
}
|
|
20
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-plugin-tlrp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "DSH 核心角色扮演与多形态矩阵引擎:注册 tlrp 设置命名空间、可自由增删的 modes 形态矩阵、/mode 与形态别名指令拦截、{{tlrp_character}}/{{tlrp_directive}}/{{tlrp_memory_context}}/{{now}} 动态变量、每轮尾部实时状态与 tlmemory 记忆桥注入,并按形态的 toolPolicy 物理拦截工具。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|
|
@@ -40,7 +40,8 @@
|
|
|
40
40
|
"scripts/rewrite-persona.mjs",
|
|
41
41
|
"scripts/client-check.mjs",
|
|
42
42
|
"test/core.test.mjs",
|
|
43
|
-
"test/memory-bridge.test.mjs"
|
|
43
|
+
"test/memory-bridge.test.mjs",
|
|
44
|
+
"test/style.test.mjs"
|
|
44
45
|
],
|
|
45
46
|
"engines": {
|
|
46
47
|
"node": ">=20"
|
|
@@ -66,7 +67,7 @@
|
|
|
66
67
|
}
|
|
67
68
|
},
|
|
68
69
|
"scripts": {
|
|
69
|
-
"test": "node --test test/core.test.mjs test/memory-bridge.test.mjs",
|
|
70
|
+
"test": "node --test test/core.test.mjs test/memory-bridge.test.mjs test/style.test.mjs",
|
|
70
71
|
"check:apply": "node scripts/apply-check.mjs",
|
|
71
72
|
"check:mutate": "node scripts/mutate-check.mjs",
|
|
72
73
|
"check:client": "node scripts/client-check.mjs",
|
package/scripts/apply-check.mjs
CHANGED
|
@@ -267,6 +267,49 @@ const statusHandler = h.services.commands.defs.find((definition) => definition.n
|
|
|
267
267
|
const status = await statusHandler({ agent: h.agent, rawInput: 'status' })
|
|
268
268
|
check('/tlrp status 返回汇总', status?.kind === 'success' && status.text.includes('已注册形态'), status?.text?.split('\n')[1])
|
|
269
269
|
|
|
270
|
+
/* ── 4b. 风格层开关(/style) ── */
|
|
271
|
+
const styleHandler = h.services.commands.defs.find((definition) => definition.name === 'style')?.handler
|
|
272
|
+
const styleIdle = await styleHandler({ agent: h.agent, rawInput: '' })
|
|
273
|
+
check('/style 无参汇报开关状态', styleIdle?.kind === 'success' && styleIdle.text.includes('风格层'), styleIdle?.text?.split('\n')[0])
|
|
274
|
+
const styleOn = await styleHandler({ agent: h.agent, rawInput: 'on' })
|
|
275
|
+
check('/style on 打开会话级风格层', styleOn?.kind === 'success' && styleOn.text.includes('只带语气'), styleOn?.text)
|
|
276
|
+
const styled = await h.run('这一版跑得怎么样')
|
|
277
|
+
check('风格层打开后尾部 context 声明来源', styled.contexts.includes('风格层:开'), styled.contexts.match(/- 风格层:[^\n]+/)?.[0])
|
|
278
|
+
check('风格层打开后准则里出现风格块', styled.prompt.includes('### 风格层(本轮生效'))
|
|
279
|
+
const styleDialogRp = await styleHandler({ agent: h.agent, rawInput: 'dialog' })
|
|
280
|
+
check('/style dialog 在陪伴态生效', styleDialogRp?.kind === 'success' && styleDialogRp.text.includes('对话体'), styleDialogRp?.text)
|
|
281
|
+
const rpDialogRun = await h.run('陪我坐一会儿')
|
|
282
|
+
check('陪伴态对话体取消斜体动作要求', !rpDialogRun.prompt.includes('自然穿插 *斜体包裹的微动作'), rpDialogRun.prompt.includes('本轮是对话体') ? '已改为只说话' : '未命中')
|
|
283
|
+
check('陪伴态对话体红线改口径', rpDialogRun.contexts.includes('格式红线(陪伴族 · 对话体)'))
|
|
284
|
+
await styleHandler({ agent: h.agent, rawInput: 'on' })
|
|
285
|
+
const workStyle = createHarness({ mode: 'work' })
|
|
286
|
+
const workStyleHandler = workStyle.services.commands.defs.find((definition) => definition.name === 'style')?.handler
|
|
287
|
+
await workStyleHandler({ agent: workStyle.agent, rawInput: 'on' })
|
|
288
|
+
const workStyleRun = await workStyle.run('这一版跑得怎么样')
|
|
289
|
+
check('WORK 形态下风格块补齐语言风格', workStyleRun.prompt.includes('#### 语言风格与禁用项(照此执行)'))
|
|
290
|
+
const styleDialog = await workStyleHandler({ agent: workStyle.agent, rawInput: 'dialog' })
|
|
291
|
+
check('/style dialog 切到对话体', styleDialog?.kind === 'success' && styleDialog.text.includes('对话体'), styleDialog?.text)
|
|
292
|
+
const dialogRun = await workStyle.run('这一版跑得怎么样')
|
|
293
|
+
check(
|
|
294
|
+
'对话体在状态块里写明只说话',
|
|
295
|
+
dialogRun.contexts.includes('对话体:只说话,不写旁白'),
|
|
296
|
+
dialogRun.contexts.includes('对话体') ? 'context 已声明对话体' : String(dialogRun.contexts).slice(0, 80),
|
|
297
|
+
)
|
|
298
|
+
check('对话体标题替换默认标题', dialogRun.prompt.includes('### 风格层(本轮生效:对话体,只说话不演)'))
|
|
299
|
+
check('对话体给出表演强度红线', dialogRun.prompt.includes('#### 本轮的表演强度'))
|
|
300
|
+
check('对话体禁止旁白与环境描写', dialogRun.prompt.includes('禁止环境与天气描写'))
|
|
301
|
+
await workStyleHandler({ agent: workStyle.agent, rawInput: 'default' })
|
|
302
|
+
const afterDialog = await workStyle.run('这一版跑得怎么样')
|
|
303
|
+
check('交回后不再要求对话体', !afterDialog.prompt.includes('#### 本轮的表演强度'))
|
|
304
|
+
const styleOff = await styleHandler({ agent: h.agent, rawInput: 'off' })
|
|
305
|
+
check('/style off 关闭会话级风格层', styleOff?.kind === 'success' && styleOff.text.includes('「关」'), styleOff?.text)
|
|
306
|
+
const withoutStyle = await h.run('这一版跑得怎么样')
|
|
307
|
+
check('关闭后不再注入风格块', !withoutStyle.prompt.includes('### 风格层(本轮生效'))
|
|
308
|
+
const styleBack = await styleHandler({ agent: h.agent, rawInput: 'default' })
|
|
309
|
+
check('/style default 交回形态与全局', styleBack?.kind === 'success' && styleBack.text.includes('交回'), styleBack?.text)
|
|
310
|
+
const styleBad = await styleHandler({ agent: h.agent, rawInput: 'maybe' })
|
|
311
|
+
check('/style 非法参数给用法', styleBad?.kind === 'error' && styleBad.text.includes('用法'), styleBad?.text)
|
|
312
|
+
|
|
270
313
|
/* ── 5. 触发映射 ── */
|
|
271
314
|
const triggered = await h.run('好想吃橡木蛋糕卷啊')
|
|
272
315
|
check('命中橡木蛋糕卷触发器', triggered.contexts.includes('两万信用点'), triggered.contexts.match(/\[oak-cake\][^\n]{0,40}/)?.[0])
|