@morlay/dsh-session-mode 0.0.4 → 0.0.6
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 +19 -5
- package/dist/client.cjs +16699 -6
- package/dist/index.d.cts +2 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.mjs +104 -20
- package/dist/rows.mjs +10 -0
- package/package.json +39 -40
- package/src/client/SessionModeSeat.tsx +1 -1
- package/src/client/index.ts +4 -1
- package/src/css-modules.d.ts +6 -0
- package/src/index.ts +3 -1
- package/src/mode-sources.ts +9 -1
- package/src/modes.ts +28 -0
- package/src/rows.ts +5 -0
- package/src/scope.ts +142 -28
package/dist/index.d.cts
CHANGED
|
@@ -23,6 +23,8 @@ interface SessionMode {
|
|
|
23
23
|
readonly persona: SessionModePersona;
|
|
24
24
|
readonly allowTools: string[];
|
|
25
25
|
readonly denyTools: string[];
|
|
26
|
+
readonly allowSkills: string[];
|
|
27
|
+
readonly denySkills: string[];
|
|
26
28
|
readonly allowPolicies: string[];
|
|
27
29
|
readonly denyPolicies: string[];
|
|
28
30
|
readonly instructions: boolean;
|
package/dist/index.d.mts
CHANGED
|
@@ -23,6 +23,8 @@ interface SessionMode {
|
|
|
23
23
|
readonly persona: SessionModePersona;
|
|
24
24
|
readonly allowTools: string[];
|
|
25
25
|
readonly denyTools: string[];
|
|
26
|
+
readonly allowSkills: string[];
|
|
27
|
+
readonly denySkills: string[];
|
|
26
28
|
readonly allowPolicies: string[];
|
|
27
29
|
readonly denyPolicies: string[];
|
|
28
30
|
readonly instructions: boolean;
|
package/dist/index.mjs
CHANGED
|
@@ -54,6 +54,14 @@ const modeSchema = z$1.object({
|
|
|
54
54
|
zh: "这个模式不用的工具(黑名单):从 `allowTools` 定下的那份里减掉(`allowTools` 留空就是从全部里减)。留空 = 一条都不禁;同时命中两份名单时以这里为准。",
|
|
55
55
|
en: "Tools this mode must not use (deny list): subtracted from whatever `allowTools` settled on (with an empty `allowTools`, from everything). Empty denies nothing; a name in both lists is denied."
|
|
56
56
|
})),
|
|
57
|
+
allowSkills: z$1.array(z$1.string()).default([]).description(localized({
|
|
58
|
+
zh: "这个模式能用的技能;其余既不进技能目录,`skill` 工具加载它也被拒。**留空就是不设收窄**:技能注册表里有什么就用什么。",
|
|
59
|
+
en: "Skills this mode may use; everything else leaves the skill catalog and loading it through the `skill` tool is refused. Leave it empty to narrow nothing: the session then sees every skill the registry carries."
|
|
60
|
+
})),
|
|
61
|
+
denySkills: z$1.array(z$1.string()).default([]).description(localized({
|
|
62
|
+
zh: "这个模式不用的技能(黑名单):从 `allowSkills` 定下的那份里减掉(`allowSkills` 留空就是从全部里减)。留空 = 一条都不禁;同时命中两份名单时以这里为准。",
|
|
63
|
+
en: "Skills this mode must not use (deny list): subtracted from whatever `allowSkills` settled on (with an empty `allowSkills`, from everything). Empty denies nothing; a name in both lists is denied."
|
|
64
|
+
})),
|
|
57
65
|
allowPolicies: z$1.array(z$1.string()).default([]).description(localized({
|
|
58
66
|
zh: "上游 policy 规则的生效白名单(`fs/write-intent` / `fs/edit-intent`):留空 = 全部规则照旧生效;有值 = 只有列出的生效,其余的被绕过。",
|
|
59
67
|
en: "Allow list of upstream policy rules that stay in force (`fs/write-intent` / `fs/edit-intent`): empty keeps every rule; when set, only the listed ones stay, and the others are bypassed."
|
|
@@ -135,24 +143,73 @@ const DENYLISTED = (toolName, modeName) => `${toolName} 被「${modeName}」的
|
|
|
135
143
|
function refusalOf(toolName, modeName, denial) {
|
|
136
144
|
return denial === "denylisted" ? DENYLISTED(toolName, modeName) : OUT_OF_SCOPE(toolName, modeName);
|
|
137
145
|
}
|
|
146
|
+
const SKILL_OUT_OF_SCOPE = (skillName, modeName) => `${skillName} 不在「${modeName}」的技能白名单里,加载它不会有结果;按当前模式可用的技能完成任务,或让用户切到别的模式。`;
|
|
147
|
+
const SKILL_DENYLISTED = (skillName, modeName) => `${skillName} 被「${modeName}」的技能黑名单排除,加载它不会有结果;按当前模式可用的技能完成任务,或让用户切到别的模式。`;
|
|
148
|
+
function skillRefusalOf(skillName, modeName, denial) {
|
|
149
|
+
return denial === "denylisted" ? SKILL_DENYLISTED(skillName, modeName) : SKILL_OUT_OF_SCOPE(skillName, modeName);
|
|
150
|
+
}
|
|
138
151
|
const TOOL_SECTION_PREFIX = "tool:";
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
const
|
|
142
|
-
|
|
152
|
+
const SKILL_TOOL_NAME = "skill";
|
|
153
|
+
function gateOfNames(allow, deny) {
|
|
154
|
+
const denied = new Set(deny);
|
|
155
|
+
const allowed = allow.length === 0 ? void 0 : new Set(allow);
|
|
156
|
+
if (allowed === void 0 && denied.size === 0) return void 0;
|
|
143
157
|
return {
|
|
144
|
-
allow,
|
|
145
|
-
deny
|
|
158
|
+
allow: allowed,
|
|
159
|
+
deny: denied
|
|
146
160
|
};
|
|
147
161
|
}
|
|
148
|
-
function denialOf(gate,
|
|
149
|
-
if (gate.deny.has(
|
|
150
|
-
if (gate.allow !== void 0 && !gate.allow.has(
|
|
162
|
+
function denialOf(gate, name) {
|
|
163
|
+
if (gate.deny.has(name)) return "denylisted";
|
|
164
|
+
if (gate.allow !== void 0 && !gate.allow.has(name)) return "not-allowlisted";
|
|
151
165
|
}
|
|
152
166
|
function kindOf(message) {
|
|
153
167
|
const kind = message.source.kind;
|
|
154
168
|
return typeof kind === "string" ? kind : void 0;
|
|
155
169
|
}
|
|
170
|
+
const CATALOG_LINE = /^- `([^`]+)`: /u;
|
|
171
|
+
function narrowCatalog(message, gate, warn) {
|
|
172
|
+
const source = message.source;
|
|
173
|
+
if (source.kind !== "skill-catalog") return void 0;
|
|
174
|
+
const entries = source.entries;
|
|
175
|
+
const denied = new Set(entries.map((entry) => entry.name).filter((name) => denialOf(gate, name) !== void 0));
|
|
176
|
+
if (denied.size === 0) return void 0;
|
|
177
|
+
const [block, ...rest] = message.content;
|
|
178
|
+
if (block?.type !== "text" || rest.length > 0) {
|
|
179
|
+
warn(`skill catalog message is not one text block; denied skills stay listed: ${[...denied].join(", ")}`);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
const removed = /* @__PURE__ */ new Set();
|
|
183
|
+
const lines = block.text.split("\n").filter((line) => {
|
|
184
|
+
const match = CATALOG_LINE.exec(line);
|
|
185
|
+
if (match === null) return true;
|
|
186
|
+
const name = match[1] ?? "";
|
|
187
|
+
if (!denied.has(name)) return true;
|
|
188
|
+
removed.add(name);
|
|
189
|
+
return false;
|
|
190
|
+
});
|
|
191
|
+
const missing = [...denied].filter((name) => !removed.has(name));
|
|
192
|
+
if (missing.length > 0) {
|
|
193
|
+
warn(`skill catalog lines not recognized; denied skills stay listed: ${missing.join(", ")}`);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
return {
|
|
197
|
+
...message,
|
|
198
|
+
content: [{
|
|
199
|
+
...block,
|
|
200
|
+
text: lines.join("\n")
|
|
201
|
+
}],
|
|
202
|
+
source: {
|
|
203
|
+
...source,
|
|
204
|
+
entries: entries.filter((entry) => !denied.has(entry.name))
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
function skillNameOf(arguments_) {
|
|
209
|
+
if (typeof arguments_ !== "object" || arguments_ === null) return void 0;
|
|
210
|
+
const name = arguments_.name;
|
|
211
|
+
return typeof name === "string" && name.length > 0 ? name : void 0;
|
|
212
|
+
}
|
|
156
213
|
var SessionScope = class {
|
|
157
214
|
applied = /* @__PURE__ */ new WeakMap();
|
|
158
215
|
ctx;
|
|
@@ -175,30 +232,54 @@ var SessionScope = class {
|
|
|
175
232
|
if (decision.kind !== "enter") return decision;
|
|
176
233
|
const applied = this.applied.get(payload.agent);
|
|
177
234
|
if (applied === void 0) return decision;
|
|
178
|
-
|
|
235
|
+
let changed = false;
|
|
236
|
+
const kept = [];
|
|
237
|
+
for (const message of decision.messages) {
|
|
179
238
|
const kind = kindOf(message);
|
|
180
|
-
if (kind === "agent-instructions")
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
239
|
+
if (kind === "agent-instructions") {
|
|
240
|
+
if (applied.instructions) kept.push(message);
|
|
241
|
+
else changed = true;
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
if (kind === "skill-catalog") {
|
|
245
|
+
if (!applied.skills) {
|
|
246
|
+
changed = true;
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
const narrowed = applied.skillGate === void 0 ? void 0 : narrowCatalog(message, applied.skillGate, (line) => this.ctx.logger.warn(`session-mode: ${line}`));
|
|
250
|
+
if (narrowed === void 0) kept.push(message);
|
|
251
|
+
else {
|
|
252
|
+
changed = true;
|
|
253
|
+
kept.push(narrowed);
|
|
254
|
+
}
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
kept.push(message);
|
|
258
|
+
}
|
|
259
|
+
return changed ? {
|
|
185
260
|
...decision,
|
|
186
261
|
messages: kept
|
|
187
|
-
};
|
|
262
|
+
} : decision;
|
|
188
263
|
}, { prepend: true });
|
|
189
264
|
}
|
|
190
265
|
apply(agent, definition) {
|
|
191
266
|
const previous = this.applied.get(agent);
|
|
192
267
|
if (previous !== void 0) previous.dispose();
|
|
193
|
-
const gate =
|
|
268
|
+
const gate = gateOfNames(definition.allowTools, definition.denyTools);
|
|
269
|
+
const skillGate = gateOfNames(definition.allowSkills, definition.denySkills);
|
|
194
270
|
const dispose = agent.ctx.effect(() => {
|
|
195
271
|
const stoppers = [];
|
|
196
272
|
if (definition.runtimeContext === false) stoppers.push(agent.ctx.systemPrompt.suppressRuntimeContext());
|
|
197
273
|
const fiber = agent.ctx.inject(["tools"], (scope) => {
|
|
198
|
-
if (gate === void 0) return;
|
|
274
|
+
if (gate === void 0 && skillGate === void 0) return;
|
|
199
275
|
scope.tools.guard((exec) => {
|
|
200
|
-
const denial = denialOf(gate, exec.name);
|
|
201
|
-
|
|
276
|
+
const denial = gate === void 0 ? void 0 : denialOf(gate, exec.name);
|
|
277
|
+
if (denial !== void 0) return refusalOf(exec.name, definition.name, denial);
|
|
278
|
+
if (skillGate === void 0 || exec.name !== SKILL_TOOL_NAME) return void 0;
|
|
279
|
+
const skillName = skillNameOf(exec.arguments);
|
|
280
|
+
if (skillName === void 0) return void 0;
|
|
281
|
+
const skillDenial = denialOf(skillGate, skillName);
|
|
282
|
+
return skillDenial === void 0 ? void 0 : skillRefusalOf(skillName, definition.name, skillDenial);
|
|
202
283
|
});
|
|
203
284
|
});
|
|
204
285
|
return [...stoppers, () => fiber.dispose()];
|
|
@@ -206,6 +287,7 @@ var SessionScope = class {
|
|
|
206
287
|
this.channel()?.setInstructions(agent, definition.instructions);
|
|
207
288
|
this.applied.set(agent, {
|
|
208
289
|
gate,
|
|
290
|
+
skillGate,
|
|
209
291
|
instructions: definition.instructions,
|
|
210
292
|
skills: definition.skills,
|
|
211
293
|
dispose
|
|
@@ -421,6 +503,8 @@ var SessionModes = class extends Service {
|
|
|
421
503
|
name: mode.name,
|
|
422
504
|
allowTools: mode.allowTools,
|
|
423
505
|
denyTools: mode.denyTools,
|
|
506
|
+
allowSkills: mode.allowSkills,
|
|
507
|
+
denySkills: mode.denySkills,
|
|
424
508
|
instructions: mode.instructions,
|
|
425
509
|
skills: mode.skills ?? derivedSkills(mode),
|
|
426
510
|
runtimeContext: mode.runtimeContext
|
package/dist/rows.mjs
CHANGED
|
@@ -11,6 +11,12 @@ const MODE_SOURCES = [{
|
|
|
11
11
|
description: "功能完整的编码 Agent:文件、Shell、检索、联网等工具常驻,其余用法说明按需加载。",
|
|
12
12
|
persona: CODING_PERSONA,
|
|
13
13
|
role: ["main", "subagent"],
|
|
14
|
+
denyTools: ["load_workspace_dependencies"],
|
|
15
|
+
denySkills: [
|
|
16
|
+
"office-docx",
|
|
17
|
+
"office-pptx",
|
|
18
|
+
"office-xlsx"
|
|
19
|
+
],
|
|
14
20
|
denyPolicies: ["fs/edit-intent"]
|
|
15
21
|
}, {
|
|
16
22
|
id: "chat",
|
|
@@ -34,6 +40,8 @@ function list(values) {
|
|
|
34
40
|
function modeConfig(source) {
|
|
35
41
|
const allowTools = list(source.allowTools);
|
|
36
42
|
const denyTools = list(source.denyTools);
|
|
43
|
+
const allowSkills = list(source.allowSkills);
|
|
44
|
+
const denySkills = list(source.denySkills);
|
|
37
45
|
const allowPolicies = list(source.allowPolicies);
|
|
38
46
|
const denyPolicies = list(source.denyPolicies);
|
|
39
47
|
return {
|
|
@@ -44,6 +52,8 @@ function modeConfig(source) {
|
|
|
44
52
|
...source.persona === void 0 ? {} : { persona: { ...source.persona } },
|
|
45
53
|
...allowTools === void 0 ? {} : { allowTools },
|
|
46
54
|
...denyTools === void 0 ? {} : { denyTools },
|
|
55
|
+
...allowSkills === void 0 ? {} : { allowSkills },
|
|
56
|
+
...denySkills === void 0 ? {} : { denySkills },
|
|
47
57
|
...allowPolicies === void 0 ? {} : { allowPolicies },
|
|
48
58
|
...denyPolicies === void 0 ? {} : { denyPolicies },
|
|
49
59
|
...source.instructions === void 0 ? {} : { instructions: source.instructions },
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@morlay/dsh-session-mode",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.0.6",
|
|
4
|
+
"description": "Session modes for dsh: coding / chat as data — a mode is a persona plus its tool lists, its upstream policy lists, its instruction / skill-catalog / runtime-context switches and an optional default model. The host applies the current mode to each session, and the chip in the composer sits beside the official agent-preset entry, which stays.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dsh",
|
|
7
|
-
"dsh-
|
|
7
|
+
"dsh-plugin",
|
|
8
8
|
"persona",
|
|
9
9
|
"session-mode"
|
|
10
10
|
],
|
|
@@ -41,27 +41,28 @@
|
|
|
41
41
|
"@deepseek-ai/cordis-plugin-include": "1.0.9",
|
|
42
42
|
"@deepseek-ai/cordis-plugin-loader": "1.0.5",
|
|
43
43
|
"@deepseek-ai/cosmokit": "1.8.5",
|
|
44
|
-
"@deepseek-ai/dsh-agent": "0.2.0-rc.
|
|
45
|
-
"@deepseek-ai/dsh-agent-loop-testkit": "0.2.0-rc.
|
|
46
|
-
"@deepseek-ai/dsh-agent-preset-registry": "0.2.0-rc.
|
|
47
|
-
"@deepseek-ai/dsh-api-remotes": "0.2.0-rc.
|
|
48
|
-
"@deepseek-ai/dsh-api-session-controller": "0.2.0-rc.
|
|
49
|
-
"@deepseek-ai/dsh-client-locale": "0.2.0-rc.
|
|
50
|
-
"@deepseek-ai/dsh-client-ui-
|
|
51
|
-
"@deepseek-ai/dsh-client-ui-
|
|
52
|
-
"@deepseek-ai/dsh-client-ui-
|
|
53
|
-
"@deepseek-ai/dsh-
|
|
54
|
-
"@deepseek-ai/dsh-fs
|
|
55
|
-
"@deepseek-ai/dsh-fs-
|
|
56
|
-
"@deepseek-ai/dsh-
|
|
57
|
-
"@deepseek-ai/dsh-
|
|
58
|
-
"@deepseek-ai/dsh-session
|
|
59
|
-
"@deepseek-ai/dsh-
|
|
60
|
-
"@deepseek-ai/dsh-
|
|
61
|
-
"@deepseek-ai/dsh-
|
|
62
|
-
"@
|
|
63
|
-
"@
|
|
64
|
-
"@morlay/dsh-
|
|
44
|
+
"@deepseek-ai/dsh-agent": "0.2.0-rc.2",
|
|
45
|
+
"@deepseek-ai/dsh-agent-loop-testkit": "0.2.0-rc.2",
|
|
46
|
+
"@deepseek-ai/dsh-agent-preset-registry": "0.2.0-rc.2",
|
|
47
|
+
"@deepseek-ai/dsh-api-remotes": "0.2.0-rc.2",
|
|
48
|
+
"@deepseek-ai/dsh-api-session-controller": "0.2.0-rc.2",
|
|
49
|
+
"@deepseek-ai/dsh-client-locale": "0.2.0-rc.2",
|
|
50
|
+
"@deepseek-ai/dsh-client-ui-conversation": "0.2.0-rc.2",
|
|
51
|
+
"@deepseek-ai/dsh-client-ui-primitives": "0.2.0-rc.2",
|
|
52
|
+
"@deepseek-ai/dsh-client-ui-renderer": "0.2.0-rc.2",
|
|
53
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.2.0-rc.2",
|
|
54
|
+
"@deepseek-ai/dsh-fs": "0.2.0-rc.2",
|
|
55
|
+
"@deepseek-ai/dsh-fs-local": "0.2.0-rc.2",
|
|
56
|
+
"@deepseek-ai/dsh-fs-observation-policy": "0.2.0-rc.2",
|
|
57
|
+
"@deepseek-ai/dsh-llm": "0.2.0-rc.2",
|
|
58
|
+
"@deepseek-ai/dsh-session": "0.2.0-rc.2",
|
|
59
|
+
"@deepseek-ai/dsh-session-projection": "0.2.0-rc.2",
|
|
60
|
+
"@deepseek-ai/dsh-skill": "0.2.0-rc.2",
|
|
61
|
+
"@deepseek-ai/dsh-system-prompt": "0.2.0-rc.2",
|
|
62
|
+
"@deepseek-ai/dsh-tool-skill": "0.2.0-rc.2",
|
|
63
|
+
"@deepseek-ai/dsh-tools": "0.2.0-rc.2",
|
|
64
|
+
"@morlay/dsh-client-ui-primitives": "0.0.7",
|
|
65
|
+
"@morlay/dsh-context-assembler": "0.0.5",
|
|
65
66
|
"@testing-library/react": "^16.3.0",
|
|
66
67
|
"@types/js-yaml": "^4.0.9",
|
|
67
68
|
"@types/react": "~18.3.1",
|
|
@@ -72,28 +73,26 @@
|
|
|
72
73
|
"peerDependencies": {
|
|
73
74
|
"@deepseek-ai/cordis": "^4.0.4",
|
|
74
75
|
"@deepseek-ai/cosmokit": "^1.8.5",
|
|
75
|
-
"@deepseek-ai/dsh-agent": "^0.2.0-rc.
|
|
76
|
-
"@deepseek-ai/dsh-agent-preset-registry": "^0.2.0-rc.
|
|
77
|
-
"@deepseek-ai/dsh-api-session-controller": "^0.2.0-rc.
|
|
78
|
-
"@deepseek-ai/dsh-client-locale": "^0.2.0-rc.
|
|
79
|
-
"@deepseek-ai/dsh-client-ui-
|
|
80
|
-
"@deepseek-ai/dsh-client-ui-
|
|
81
|
-
"@deepseek-ai/dsh-
|
|
82
|
-
"@deepseek-ai/dsh-
|
|
83
|
-
"@deepseek-ai/dsh-
|
|
84
|
-
"@deepseek-ai/dsh-session
|
|
85
|
-
"@deepseek-ai/dsh-
|
|
86
|
-
"@deepseek-ai/
|
|
87
|
-
"@
|
|
88
|
-
"@morlay/dsh-client-ui-primitives": "0.0.6"
|
|
76
|
+
"@deepseek-ai/dsh-agent": "^0.2.0-rc.2",
|
|
77
|
+
"@deepseek-ai/dsh-agent-preset-registry": "^0.2.0-rc.2",
|
|
78
|
+
"@deepseek-ai/dsh-api-session-controller": "^0.2.0-rc.2",
|
|
79
|
+
"@deepseek-ai/dsh-client-locale": "^0.2.0-rc.2",
|
|
80
|
+
"@deepseek-ai/dsh-client-ui-conversation": "^0.2.0-rc.2",
|
|
81
|
+
"@deepseek-ai/dsh-client-ui-primitives": "^0.2.0-rc.2",
|
|
82
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.2.0-rc.2",
|
|
83
|
+
"@deepseek-ai/dsh-fs": "^0.2.0-rc.2",
|
|
84
|
+
"@deepseek-ai/dsh-llm": "^0.2.0-rc.2",
|
|
85
|
+
"@deepseek-ai/dsh-session": "^0.2.0-rc.2",
|
|
86
|
+
"@deepseek-ai/dsh-session-projection": "^0.2.0-rc.2",
|
|
87
|
+
"@deepseek-ai/dsh-system-prompt": "^0.2.0-rc.2",
|
|
88
|
+
"@deepseek-ai/schemastery": "^3.18.4"
|
|
89
89
|
},
|
|
90
90
|
"dsh": {
|
|
91
91
|
"client": {
|
|
92
92
|
"platform": "web",
|
|
93
93
|
"inject": [
|
|
94
94
|
"@deepseek-ai/dsh-client-locale",
|
|
95
|
-
"@deepseek-ai/dsh-client-ui-slots"
|
|
96
|
-
"@morlay/dsh-client-ui-primitives"
|
|
95
|
+
"@deepseek-ai/dsh-client-ui-slots"
|
|
97
96
|
]
|
|
98
97
|
}
|
|
99
98
|
},
|
|
@@ -12,7 +12,7 @@ import type { PropsLocale, PropsRuntime } from "@deepseek-ai/dsh-client-ui-slots
|
|
|
12
12
|
// Type-only:拉入本包 host 半的 Context / 投影声明(`sessionMode`),让 `projectionValues` 有类型。
|
|
13
13
|
import type {} from "../index.ts";
|
|
14
14
|
// Type-only:拉入 ui-conversation 的 SlotMap 合并(composer 工具行的座位)。
|
|
15
|
-
import type {} from "@
|
|
15
|
+
import type {} from "@deepseek-ai/dsh-client-ui-conversation/client";
|
|
16
16
|
import { selectMode } from "./api.ts";
|
|
17
17
|
import { useRoster } from "./use-roster.ts";
|
|
18
18
|
import css from "./SessionModeSeat.module.css";
|
package/src/client/index.ts
CHANGED
|
@@ -8,7 +8,8 @@ import type {} from "@deepseek-ai/dsh-api-remotes/client";
|
|
|
8
8
|
import type {} from "@deepseek-ai/dsh-client-locale/client";
|
|
9
9
|
import type {} from "@deepseek-ai/dsh-client-ui-slots";
|
|
10
10
|
// Type-only:槽位声明与 standard props(session / session-maybe / global)。
|
|
11
|
-
import type {} from "@
|
|
11
|
+
import type {} from "@deepseek-ai/dsh-client-ui-conversation/client";
|
|
12
|
+
import { apply as installUiPrimitives } from "@morlay/dsh-client-ui-primitives/client";
|
|
12
13
|
import { POLICY_NAMES } from "../shared.ts";
|
|
13
14
|
import { SessionModeSeat } from "./SessionModeSeat.tsx";
|
|
14
15
|
import { en, zh, type SessionModeLocaleKey } from "./locales.ts";
|
|
@@ -62,6 +63,8 @@ function readAt(root: unknown, path: readonly string[]): unknown {
|
|
|
62
63
|
|
|
63
64
|
// 装上会话里的那一个面,以及本行配置页的字段文案。
|
|
64
65
|
export function apply(ctx: Context): void {
|
|
66
|
+
// 基础面随本包 inline(不再是装配行):装上它提供的字典、字段槽与按行配置页;多份副本只装一次。
|
|
67
|
+
installUiPrimitives(ctx);
|
|
65
68
|
const t = ctx.locale.bind(NS);
|
|
66
69
|
ctx.effect(() => ctx.locale.register(NS, { zh, en }), "session-mode: dictionaries");
|
|
67
70
|
|
package/src/index.ts
CHANGED
|
@@ -389,12 +389,14 @@ export class SessionModes extends Service {
|
|
|
389
389
|
for (const dispose of disposers) dispose();
|
|
390
390
|
},
|
|
391
391
|
});
|
|
392
|
-
// 收口要的那几项按模式定义整份推过去:`name` /
|
|
392
|
+
// 收口要的那几项按模式定义整份推过去:`name` / 两份工具名单 / 两份技能名单 / 三个开关。`skills` 缺省时在这里
|
|
393
393
|
// 按这份定义自己的工具名单推导(见 `derivedSkills`)——收口那一侧只认解析后的布尔。
|
|
394
394
|
this.scope.apply(agent, {
|
|
395
395
|
name: mode.name,
|
|
396
396
|
allowTools: mode.allowTools,
|
|
397
397
|
denyTools: mode.denyTools,
|
|
398
|
+
allowSkills: mode.allowSkills,
|
|
399
|
+
denySkills: mode.denySkills,
|
|
398
400
|
instructions: mode.instructions,
|
|
399
401
|
skills: mode.skills ?? derivedSkills(mode),
|
|
400
402
|
runtimeContext: mode.runtimeContext,
|
package/src/mode-sources.ts
CHANGED
|
@@ -20,6 +20,10 @@ export interface ModeSource {
|
|
|
20
20
|
readonly allowTools?: readonly string[];
|
|
21
21
|
// 这个模式不用的工具(黑名单):从 `allowTools` 定的那份里减掉(deny 优先)。
|
|
22
22
|
readonly denyTools?: readonly string[];
|
|
23
|
+
// 这个模式能用的技能;**可选**,不写(留空)就是不设收窄——技能注册表里有什么就用什么。
|
|
24
|
+
readonly allowSkills?: readonly string[];
|
|
25
|
+
// 这个模式不用的技能(黑名单):从 `allowSkills` 定的那份里减掉(deny 优先)。
|
|
26
|
+
readonly denySkills?: readonly string[];
|
|
23
27
|
// 上游 policy 规则的生效白名单:**可选**,不写(留空)= 全部规则生效。
|
|
24
28
|
readonly allowPolicies?: readonly PolicyName[];
|
|
25
29
|
// 上游 policy 规则的黑名单:列出的规则禁用(它在上游那条 waterfall 上的裁决被绕过)。
|
|
@@ -69,7 +73,11 @@ export const MODE_SOURCES: readonly ModeSource[] = [
|
|
|
69
73
|
// 用户可选,也允许作为子代理的 mode(子代理默认继承父 mode,不看角色;这里是"可被指定"的候选集)。
|
|
70
74
|
role: ["main", "subagent"],
|
|
71
75
|
// 不写 `allowTools`:不设收窄——这个会话用它挂着的 preset 提供的全部工具(抄一份清单只会与行清单漂移)。
|
|
72
|
-
//
|
|
76
|
+
// 只排除一件工具:官方 Office 组合(宿主代码挂载,profile 层停不掉它的行)里那个载荷查询。
|
|
77
|
+
denyTools: ["load_workspace_dependencies"],
|
|
78
|
+
// 官方 Office 技能的三个名字同样按会话排除:技能目录里不列它们,`skill` 工具加载它们也被拒。
|
|
79
|
+
// 名单是技能名这一层的模型可见契约(官方 `skill-office` 的 `SKILL_NAMES`),跟着上游改名走。
|
|
80
|
+
denySkills: ["office-docx", "office-pptx", "office-xlsx"],
|
|
73
81
|
//
|
|
74
82
|
// `denyPolicies` 只禁 `fs/edit-intent`(上游那条"先读后改"):改文件不再要求先读过——写路径上的
|
|
75
83
|
// `fs/write-intent`(陈旧版本 CAS 那层安全网)照旧生效,那正是这条配置不写成"两条都禁"的理由。
|
package/src/modes.ts
CHANGED
|
@@ -51,6 +51,12 @@ export interface SessionMode {
|
|
|
51
51
|
// 这个模式**不用**哪些工具(黑名单):从 `allowTools` 定下的那份里减掉(`allowTools` 留空时就是从全部里减)。
|
|
52
52
|
// 留空表示不禁任何工具。同时命中两份名单时以这里为准(deny 优先)。
|
|
53
53
|
readonly denyTools: string[];
|
|
54
|
+
// 这个模式能用哪些**技能**;其余的既不进技能目录,`skill` 工具加载它也被拒。**留空表示不设收窄**:技能注册表
|
|
55
|
+
// 里有什么就用什么(官方 `skill-office` 那类由宿主代码挂载的技能也在这份"全部"里)。
|
|
56
|
+
readonly allowSkills: string[];
|
|
57
|
+
// 这个模式**不用**哪些技能(黑名单):从 `allowSkills` 定下的那份里减掉(`allowSkills` 留空时就是从全部里减)。
|
|
58
|
+
// 留空表示不禁任何技能;同时命中两份名单时以这里为准(deny 优先)。
|
|
59
|
+
readonly denySkills: string[];
|
|
54
60
|
// 上游 policy 规则的**生效白名单**:留空(或不写)表示全部规则照旧生效;有值表示只有列出的那些生效,
|
|
55
61
|
// 其余的按 `denyPolicies` 那一套被绕过。名单见 `POLICY_NAMES`。空数组与不写同义。
|
|
56
62
|
readonly allowPolicies: string[];
|
|
@@ -198,6 +204,24 @@ const modeSchema: z<SessionMode> = z.object({
|
|
|
198
204
|
en: "Tools this mode must not use (deny list): subtracted from whatever `allowTools` settled on (with an empty `allowTools`, from everything). Empty denies nothing; a name in both lists is denied.",
|
|
199
205
|
}),
|
|
200
206
|
),
|
|
207
|
+
allowSkills: z
|
|
208
|
+
.array(z.string())
|
|
209
|
+
.default([])
|
|
210
|
+
.description(
|
|
211
|
+
localized({
|
|
212
|
+
zh: "这个模式能用的技能;其余既不进技能目录,`skill` 工具加载它也被拒。**留空就是不设收窄**:技能注册表里有什么就用什么。",
|
|
213
|
+
en: "Skills this mode may use; everything else leaves the skill catalog and loading it through the `skill` tool is refused. Leave it empty to narrow nothing: the session then sees every skill the registry carries.",
|
|
214
|
+
}),
|
|
215
|
+
),
|
|
216
|
+
denySkills: z
|
|
217
|
+
.array(z.string())
|
|
218
|
+
.default([])
|
|
219
|
+
.description(
|
|
220
|
+
localized({
|
|
221
|
+
zh: "这个模式不用的技能(黑名单):从 `allowSkills` 定下的那份里减掉(`allowSkills` 留空就是从全部里减)。留空 = 一条都不禁;同时命中两份名单时以这里为准。",
|
|
222
|
+
en: "Skills this mode must not use (deny list): subtracted from whatever `allowSkills` settled on (with an empty `allowSkills`, from everything). Empty denies nothing; a name in both lists is denied.",
|
|
223
|
+
}),
|
|
224
|
+
),
|
|
201
225
|
allowPolicies: z
|
|
202
226
|
.array(z.string())
|
|
203
227
|
.default([])
|
|
@@ -298,6 +322,10 @@ interface Validated {
|
|
|
298
322
|
readonly allowTools?: readonly string[];
|
|
299
323
|
// 留空合法:不禁任何工具;与 `allowTools` 同时命中合法(deny 优先)。
|
|
300
324
|
readonly denyTools?: readonly string[];
|
|
325
|
+
// 留空合法:不设技能收窄。
|
|
326
|
+
readonly allowSkills?: readonly string[];
|
|
327
|
+
// 留空合法:不禁任何技能;与 `allowSkills` 同时命中合法(deny 优先)。
|
|
328
|
+
readonly denySkills?: readonly string[];
|
|
301
329
|
// 留空合法:全部规则生效。
|
|
302
330
|
readonly allowPolicies?: readonly string[];
|
|
303
331
|
// 留空合法:一条都不禁;与 `allowPolicies` 同时命中合法(deny 优先)。
|
package/src/rows.ts
CHANGED
|
@@ -24,6 +24,8 @@ function list<T>(values: readonly T[] | undefined): readonly T[] | undefined {
|
|
|
24
24
|
function modeConfig(source: ModeSource): Record<string, unknown> {
|
|
25
25
|
const allowTools = list(source.allowTools);
|
|
26
26
|
const denyTools = list(source.denyTools);
|
|
27
|
+
const allowSkills = list(source.allowSkills);
|
|
28
|
+
const denySkills = list(source.denySkills);
|
|
27
29
|
const allowPolicies = list(source.allowPolicies);
|
|
28
30
|
const denyPolicies = list(source.denyPolicies);
|
|
29
31
|
return {
|
|
@@ -35,9 +37,12 @@ function modeConfig(source: ModeSource): Record<string, unknown> {
|
|
|
35
37
|
...(source.persona === undefined ? {} : { persona: { ...source.persona } }),
|
|
36
38
|
// 四份名单都是可选的:不写(或写成空数组)就是这个键不进 config,schema 默认空数组。
|
|
37
39
|
// `allowTools` 不写 = 不设收窄;`denyTools` 不写 = 一件都不禁(两份同配时 deny 优先);
|
|
40
|
+
// `allowSkills` / `denySkills` 同一套语义,收的是技能名(技能目录与 `skill` 工具两处一起收);
|
|
38
41
|
// `allowPolicies` 不写 = 全部上游 policy 规则生效;`denyPolicies` 不写 = 一条都不禁。
|
|
39
42
|
...(allowTools === undefined ? {} : { allowTools }),
|
|
40
43
|
...(denyTools === undefined ? {} : { denyTools }),
|
|
44
|
+
...(allowSkills === undefined ? {} : { allowSkills }),
|
|
45
|
+
...(denySkills === undefined ? {} : { denySkills }),
|
|
41
46
|
...(allowPolicies === undefined ? {} : { allowPolicies }),
|
|
42
47
|
...(denyPolicies === undefined ? {} : { denyPolicies }),
|
|
43
48
|
...(source.instructions === undefined ? {} : { instructions: source.instructions }),
|