@morlay/dsh-session-mode 0.0.6 → 0.0.8
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 +33 -16
- package/dist/catalog-remote-B-NMi_vS.mjs +21 -0
- package/dist/client.cjs +2623 -1714
- package/dist/client.d.cts +473 -4
- package/dist/client.d.mts +473 -4
- package/dist/index.d.cts +3 -1
- package/dist/index.d.mts +3 -1
- package/dist/index.mjs +160 -24
- package/dist/rows.mjs +39 -28
- package/package.json +15 -3
- package/src/catalog-remote.ts +45 -0
- package/src/catalog.ts +75 -0
- package/src/client/BundleConfigPage.tsx +630 -0
- package/src/client/SessionModeSeat.tsx +21 -19
- package/src/client/bundle-config.ts +701 -0
- package/src/client/bundle-locales.ts +214 -0
- package/src/client/index.ts +180 -72
- package/src/client/llm-directory.ts +146 -0
- package/src/client/locales.ts +2 -2
- package/src/index.ts +25 -11
- package/src/mode-sources.ts +20 -6
- package/src/modes.ts +46 -21
- package/src/rows.ts +3 -2
- package/src/scope.ts +4 -5
- package/dist/shared-DX2Giyh3.mjs +0 -5
- package/src/client/SessionModeSeat.module.css +0 -74
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { i as SESSION_MODE_PATH, r as POLICY_NAMES, t as CATALOG_NS } from "./catalog-remote-B-NMi_vS.mjs";
|
|
2
2
|
import { Service } from "@deepseek-ai/cordis";
|
|
3
3
|
import { ReasoningEffortId } from "@deepseek-ai/dsh-llm";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import z$1 from "@deepseek-ai/schemastery";
|
|
6
|
+
import { Remote, TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
|
|
6
7
|
import { PERSONA_PREFIX_SECTION, PERSONA_SUFFIX_SECTION } from "@deepseek-ai/dsh-system-prompt";
|
|
7
8
|
//#region src/modes.ts
|
|
8
9
|
function derivedSkills(mode) {
|
|
@@ -24,14 +25,15 @@ const modelSchema = z$1.object({
|
|
|
24
25
|
en: "Model id."
|
|
25
26
|
})),
|
|
26
27
|
reasoningEffort: z$1.string().description(localized({
|
|
27
|
-
zh: "
|
|
28
|
-
en: "Reasoning effort;
|
|
28
|
+
zh: "思考档位;默认跟服务商自己的默认。",
|
|
29
|
+
en: "Reasoning effort; the default is the provider's own."
|
|
29
30
|
}))
|
|
30
31
|
});
|
|
31
32
|
const modeSchema = z$1.object({
|
|
32
|
-
preset: z$1.string().
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
preset: z$1.string().hidden(),
|
|
34
|
+
presetsOnly: z$1.array(z$1.string()).role("select", { source: "agent-presets" }).default([]).description(localized({
|
|
35
|
+
zh: "这个模式只允许挂哪些 agent preset(它们的 id,官方或本部署自建的):行清单由挂着的那份提供,几个模式可以共享同一份名单。留空表示**允许全部**——哪份 preset 都行,选这个模式也不动会话当前挂着的那份;有值时当前的已经在名单里就不动,否则换成名单里的第一个。模式自己的提示词、工具收口与开关照常生效。",
|
|
36
|
+
en: "Which agent presets this mode may ride on (their ids, shipped or deployment-owned): that preset supplies the row list, and several modes may share one list. Empty means **every preset is allowed** — selecting the mode then leaves the session's current preset alone; when set, the current one stays if it is listed, otherwise the session switches to the first entry. The mode's persona, tool narrowing, and switches apply either way."
|
|
35
37
|
})),
|
|
36
38
|
name: z$1.string().required().description(localized({
|
|
37
39
|
zh: "模式名(事实文案;选择面归官方 roster)。",
|
|
@@ -46,27 +48,27 @@ const modeSchema = z$1.object({
|
|
|
46
48
|
en: "Who may use this mode: `main` (selectable in sessions) or `subagent` (inherited by subagents)."
|
|
47
49
|
})),
|
|
48
50
|
persona: personaSchema.default({}),
|
|
49
|
-
allowTools: z$1.array(z$1.string()).default([]).description(localized({
|
|
51
|
+
allowTools: z$1.array(z$1.string()).role("select", { source: "catalog-tools" }).default([]).description(localized({
|
|
50
52
|
zh: "这个会话能用的工具;其余既不进目录,调用也被拒。**留空就是不设收窄**:用这个会话挂着的 preset 提供的全部工具。",
|
|
51
53
|
en: "Tools this session may use; everything else leaves the catalog and calls are refused. Leave it empty to narrow nothing: the session then uses every tool its preset provides."
|
|
52
54
|
})),
|
|
53
|
-
denyTools: z$1.array(z$1.string()).default([]).description(localized({
|
|
55
|
+
denyTools: z$1.array(z$1.string()).role("select", { source: "catalog-tools" }).default([]).description(localized({
|
|
54
56
|
zh: "这个模式不用的工具(黑名单):从 `allowTools` 定下的那份里减掉(`allowTools` 留空就是从全部里减)。留空 = 一条都不禁;同时命中两份名单时以这里为准。",
|
|
55
57
|
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
58
|
})),
|
|
57
|
-
allowSkills: z$1.array(z$1.string()).default([]).description(localized({
|
|
59
|
+
allowSkills: z$1.array(z$1.string()).role("select", { source: "catalog-skills" }).default([]).description(localized({
|
|
58
60
|
zh: "这个模式能用的技能;其余既不进技能目录,`skill` 工具加载它也被拒。**留空就是不设收窄**:技能注册表里有什么就用什么。",
|
|
59
61
|
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
62
|
})),
|
|
61
|
-
denySkills: z$1.array(z$1.string()).default([]).description(localized({
|
|
63
|
+
denySkills: z$1.array(z$1.string()).role("select", { source: "catalog-skills" }).default([]).description(localized({
|
|
62
64
|
zh: "这个模式不用的技能(黑名单):从 `allowSkills` 定下的那份里减掉(`allowSkills` 留空就是从全部里减)。留空 = 一条都不禁;同时命中两份名单时以这里为准。",
|
|
63
65
|
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
66
|
})),
|
|
65
|
-
allowPolicies: z$1.array(z$1.string()).default([]).description(localized({
|
|
67
|
+
allowPolicies: z$1.array(z$1.string()).role("select", { source: "policies" }).default([]).description(localized({
|
|
66
68
|
zh: "上游 policy 规则的生效白名单(`fs/write-intent` / `fs/edit-intent`):留空 = 全部规则照旧生效;有值 = 只有列出的生效,其余的被绕过。",
|
|
67
69
|
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."
|
|
68
70
|
})),
|
|
69
|
-
denyPolicies: z$1.array(z$1.string()).default([]).description(localized({
|
|
71
|
+
denyPolicies: z$1.array(z$1.string()).role("select", { source: "policies" }).default([]).description(localized({
|
|
70
72
|
zh: "上游 policy 规则的黑名单:列出的规则禁用(它的裁决连拒绝一起丢掉,调用按没有这条规则继续)。留空 = 一条都不禁;同时命中两份名单时以这里为准。",
|
|
71
73
|
en: "Deny list of upstream policy rules: the listed ones are disabled — their verdict (rejection included) is dropped and the call proceeds as if the rule were absent. Empty denies nothing; a name in both lists is denied."
|
|
72
74
|
})),
|
|
@@ -75,16 +77,16 @@ const modeSchema = z$1.object({
|
|
|
75
77
|
en: "Whether instruction-class injections apply (workspace instructions, guidance): `false` drops the official `agent-instructions` injection and turns off the channel's own demoted delivery."
|
|
76
78
|
})),
|
|
77
79
|
skills: z$1.boolean().description(localized({
|
|
78
|
-
zh: "是否要技能目录(官方 `skill-catalog`
|
|
79
|
-
en: "Whether the skill catalog applies (the official `skill-catalog` injection).
|
|
80
|
+
zh: "是否要技能目录(官方 `skill-catalog` 的注入)。**默认就按这个模式自己的工具名单推导**:`(allowTools 留空 ? 全部 : allowTools) − denyTools` 里含 `skill` 就要;写 `false` 就丢掉官方 `skill-catalog` 的注入(`skill` 工具的可见性仍归 `allowTools`)。",
|
|
81
|
+
en: "Whether the skill catalog applies (the official `skill-catalog` injection). The default derives it from this mode's own tool lists: it applies when `(empty allowTools ? every tool : allowTools) − denyTools` contains `skill`; `false` drops the official `skill-catalog` injection (`skill` tool visibility still belongs to `allowTools`)."
|
|
80
82
|
})),
|
|
81
83
|
runtimeContext: z$1.boolean().default(true).description(localized({
|
|
82
84
|
zh: "是否要动态快照(文件沙箱策略、审批策略)。",
|
|
83
85
|
en: "Whether the runtime snapshot applies (sandbox and approval policy)."
|
|
84
86
|
})),
|
|
85
87
|
defaultModel: modelSchema.default(null).description(localized({
|
|
86
|
-
zh: "
|
|
87
|
-
en: "Default model for this mode;
|
|
88
|
+
zh: "这个模式的默认模型;不设就跟全局默认模型。只在会话还没有模型事实时接管。",
|
|
89
|
+
en: "Default model for this mode; empty follows the global default. Applies only while a session has no model fact yet."
|
|
88
90
|
}))
|
|
89
91
|
});
|
|
90
92
|
const Config = z$1.object({
|
|
@@ -110,6 +112,8 @@ function configProblem(config) {
|
|
|
110
112
|
const unknownPolicies = Object.entries(config.modes).flatMap(([id, mode]) => [...mode.allowPolicies ?? [], ...mode.denyPolicies ?? []].filter((policy) => !POLICY_NAME_SET.has(policy)).map((policy) => `${id}: ${policy}`));
|
|
111
113
|
if (unknownPolicies.length > 0) return `session-mode: mode(s) ${unknownPolicies.join(", ")} name unknown policies; the known ones are ${POLICY_NAMES.join(", ")}`;
|
|
112
114
|
if (Object.keys(config.models ?? {}).length > 0) return "session-mode: `models` has moved into each mode's `defaultModel`; move the entries there and drop the top-level `models`";
|
|
115
|
+
const retiredPresets = Object.entries(config.modes).filter(([, mode]) => mode.preset !== void 0).map(([id]) => id);
|
|
116
|
+
if (retiredPresets.length > 0) return `session-mode: mode(s) ${retiredPresets.join(", ")} still declare \`preset\`; it has moved to \`presetsOnly\` (the presets that mode may ride on)`;
|
|
113
117
|
const partial = Object.entries(config.modes).filter(([, mode]) => {
|
|
114
118
|
const model = mode.defaultModel;
|
|
115
119
|
if (model === void 0) return false;
|
|
@@ -118,6 +122,134 @@ function configProblem(config) {
|
|
|
118
122
|
if (partial.length > 0) return `session-mode: mode(s) ${partial.join(", ")} declare \`defaultModel\` without both \`provider\` and \`model\``;
|
|
119
123
|
}
|
|
120
124
|
//#endregion
|
|
125
|
+
//#region src/catalog.ts
|
|
126
|
+
var __create = Object.create;
|
|
127
|
+
var __defProp = Object.defineProperty;
|
|
128
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
129
|
+
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name);
|
|
130
|
+
var __typeError = (msg) => {
|
|
131
|
+
throw TypeError(msg);
|
|
132
|
+
};
|
|
133
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, {
|
|
134
|
+
enumerable: true,
|
|
135
|
+
configurable: true,
|
|
136
|
+
writable: true,
|
|
137
|
+
value
|
|
138
|
+
}) : obj[key] = value;
|
|
139
|
+
var __name = (target, value) => __defProp(target, "name", {
|
|
140
|
+
value,
|
|
141
|
+
configurable: true
|
|
142
|
+
});
|
|
143
|
+
var __decoratorStart = (base) => [
|
|
144
|
+
,
|
|
145
|
+
,
|
|
146
|
+
,
|
|
147
|
+
__create(base?.[__knownSymbol("metadata")] ?? null)
|
|
148
|
+
];
|
|
149
|
+
var __decoratorStrings = [
|
|
150
|
+
"class",
|
|
151
|
+
"method",
|
|
152
|
+
"getter",
|
|
153
|
+
"setter",
|
|
154
|
+
"accessor",
|
|
155
|
+
"field",
|
|
156
|
+
"value",
|
|
157
|
+
"get",
|
|
158
|
+
"set"
|
|
159
|
+
];
|
|
160
|
+
var __expectFn = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError("Function expected") : fn;
|
|
161
|
+
var __decoratorContext = (kind, name, done, metadata, fns) => ({
|
|
162
|
+
kind: __decoratorStrings[kind],
|
|
163
|
+
name,
|
|
164
|
+
metadata,
|
|
165
|
+
addInitializer: (fn) => done._ ? __typeError("Already initialized") : fns.push(__expectFn(fn || null))
|
|
166
|
+
});
|
|
167
|
+
var __decoratorMetadata = (array, target) => __defNormalProp(target, __knownSymbol("metadata"), array[3]);
|
|
168
|
+
var __runInitializers = (array, flags, self, value) => {
|
|
169
|
+
for (var i = 0, fns = array[flags >> 1], n = fns && fns.length; i < n; i++) flags & 1 ? fns[i].call(self) : value = fns[i].call(self, value);
|
|
170
|
+
return value;
|
|
171
|
+
};
|
|
172
|
+
var __decorateElement = (array, flags, name, decorators, target, extra) => {
|
|
173
|
+
var fn, it, done, ctx, access, k = flags & 7, s = !!(flags & 8), p = !!(flags & 16);
|
|
174
|
+
var j = k > 3 ? array.length + 1 : k ? s ? 1 : 2 : 0, key = __decoratorStrings[k + 5];
|
|
175
|
+
var initializers = k > 3 && (array[j - 1] = []), extraInitializers = array[j] || (array[j] = []);
|
|
176
|
+
var desc = k && (!p && !s && (target = target.prototype), k < 5 && (k > 3 || !p) && __getOwnPropDesc(k < 4 ? target : {
|
|
177
|
+
get [name]() {
|
|
178
|
+
return __privateGet(this, extra);
|
|
179
|
+
},
|
|
180
|
+
set [name](x) {
|
|
181
|
+
return __privateSet(this, extra, x);
|
|
182
|
+
}
|
|
183
|
+
}, name));
|
|
184
|
+
k ? p && k < 4 && __name(extra, (k > 2 ? "set " : k > 1 ? "get " : "") + name) : __name(target, name);
|
|
185
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
186
|
+
ctx = __decoratorContext(k, name, done = {}, array[3], extraInitializers);
|
|
187
|
+
if (k) {
|
|
188
|
+
ctx.static = s, ctx.private = p, access = ctx.access = { has: p ? (x) => __privateIn(target, x) : (x) => name in x };
|
|
189
|
+
if (k ^ 3) access.get = p ? (x) => (k ^ 1 ? __privateGet : __privateMethod)(x, target, k ^ 4 ? extra : desc.get) : (x) => x[name];
|
|
190
|
+
if (k > 2) access.set = p ? (x, y) => __privateSet(x, target, y, k ^ 4 ? extra : desc.set) : (x, y) => x[name] = y;
|
|
191
|
+
}
|
|
192
|
+
it = (0, decorators[i])(k ? k < 4 ? p ? extra : desc[key] : k > 4 ? void 0 : {
|
|
193
|
+
get: desc.get,
|
|
194
|
+
set: desc.set
|
|
195
|
+
} : target, ctx), done._ = 1;
|
|
196
|
+
if (k ^ 4 || it === void 0) __expectFn(it) && (k > 4 ? initializers.unshift(it) : k ? p ? extra = it : desc[key] = it : target = it);
|
|
197
|
+
else if (typeof it !== "object" || it === null) __typeError("Object expected");
|
|
198
|
+
else __expectFn(fn = it.get) && (desc.get = fn), __expectFn(fn = it.set) && (desc.set = fn), __expectFn(fn = it.init) && initializers.unshift(fn);
|
|
199
|
+
}
|
|
200
|
+
return k || __decoratorMetadata(array, target), desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target;
|
|
201
|
+
};
|
|
202
|
+
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
203
|
+
var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError("Cannot use the \"in\" operator on this value") : member.has(obj);
|
|
204
|
+
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
205
|
+
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
206
|
+
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
207
|
+
var _remoteExportList_dec;
|
|
208
|
+
var _a;
|
|
209
|
+
var _init;
|
|
210
|
+
function cwdOf(agent) {
|
|
211
|
+
const cwd = agent.session?.header?.cwd;
|
|
212
|
+
return typeof cwd === "string" ? cwd : void 0;
|
|
213
|
+
}
|
|
214
|
+
var SessionModeCatalog = class extends (_a = TypertRemoteService, _remoteExportList_dec = [Remote("list")], _a) {
|
|
215
|
+
/**
|
|
216
|
+
* @param ctx - owning context (the `session-mode` row).
|
|
217
|
+
*/
|
|
218
|
+
constructor(ctx) {
|
|
219
|
+
super(ctx, CATALOG_NS);
|
|
220
|
+
__runInitializers(_init, 5, this);
|
|
221
|
+
}
|
|
222
|
+
async remoteExportList() {
|
|
223
|
+
return {
|
|
224
|
+
tools: this.toolNames(),
|
|
225
|
+
skills: await this.skillNames()
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
toolNames() {
|
|
229
|
+
const tools = this.ctx.get("tools");
|
|
230
|
+
if (tools === void 0) return [];
|
|
231
|
+
const names = new Set(tools.schemas().map((schema) => String(schema.name)));
|
|
232
|
+
for (const agent of this.agents()) for (const schema of tools.schemas(agent)) names.add(String(schema.name));
|
|
233
|
+
return [...names];
|
|
234
|
+
}
|
|
235
|
+
async skillNames() {
|
|
236
|
+
const skills = this.ctx.get("skills");
|
|
237
|
+
if (skills === void 0) return [];
|
|
238
|
+
const names = new Set((await skills.list()).map((row) => row.name));
|
|
239
|
+
for (const agent of this.agents()) for (const row of await skills.list({
|
|
240
|
+
scope: agent,
|
|
241
|
+
cwd: cwdOf(agent)
|
|
242
|
+
})) names.add(row.name);
|
|
243
|
+
return [...names];
|
|
244
|
+
}
|
|
245
|
+
agents() {
|
|
246
|
+
return this.ctx.get("agents")?.list() ?? [];
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
_init = __decoratorStart(_a);
|
|
250
|
+
__decorateElement(_init, 1, "remoteExportList", _remoteExportList_dec, SessionModeCatalog);
|
|
251
|
+
__decoratorMetadata(_init, SessionModeCatalog);
|
|
252
|
+
//#endregion
|
|
121
253
|
//#region src/persona.ts
|
|
122
254
|
const PREFIX_ORDER = "DEPLOYMENT_PERSONA_PREFIX";
|
|
123
255
|
const SUFFIX_ORDER = "DEPLOYMENT_PERSONA_SUFFIX";
|
|
@@ -198,11 +330,7 @@ function narrowCatalog(message, gate, warn) {
|
|
|
198
330
|
content: [{
|
|
199
331
|
...block,
|
|
200
332
|
text: lines.join("\n")
|
|
201
|
-
}]
|
|
202
|
-
source: {
|
|
203
|
-
...source,
|
|
204
|
-
entries: entries.filter((entry) => !denied.has(entry.name))
|
|
205
|
-
}
|
|
333
|
+
}]
|
|
206
334
|
};
|
|
207
335
|
}
|
|
208
336
|
function skillNameOf(arguments_) {
|
|
@@ -427,13 +555,20 @@ var SessionModes = class extends Service {
|
|
|
427
555
|
if (this.ctx.sessionProjections.stateOf(session, "sessionModeEditable") === false) throw new Error("这个会话已经开始,模式不能再改;要换模式请新开一个会话。");
|
|
428
556
|
const agent = this.ctx.agents.get(sessionId);
|
|
429
557
|
const registry = this.presetRegistry();
|
|
430
|
-
if (agent !== void 0 && registry !== void 0
|
|
431
|
-
|
|
558
|
+
if (agent !== void 0 && registry !== void 0) {
|
|
559
|
+
const target = this.presetTarget(definition.presetsOnly, this.presetOfAgent(registry, agent));
|
|
560
|
+
if (target !== void 0) await registry.select(agent, target);
|
|
432
561
|
}
|
|
433
562
|
if (this.ctx.sessionProjections.stateOf(session, "sessionMode") !== mode) session.append("session-mode/selected", { sessionMode: mode });
|
|
434
563
|
if (agent !== void 0) this.installFor(agent);
|
|
435
564
|
return mode;
|
|
436
565
|
}
|
|
566
|
+
presetTarget(allowed, current) {
|
|
567
|
+
if (allowed.length === 0) return void 0;
|
|
568
|
+
if (current !== void 0 && allowed.includes(current)) return void 0;
|
|
569
|
+
const target = allowed[0];
|
|
570
|
+
return target === current ? void 0 : target;
|
|
571
|
+
}
|
|
437
572
|
presetOfAgent(registry, agent) {
|
|
438
573
|
const read = registry.composedPreset;
|
|
439
574
|
if (typeof read !== "function") return void 0;
|
|
@@ -478,7 +613,7 @@ var SessionModes = class extends Service {
|
|
|
478
613
|
}
|
|
479
614
|
modeForPreset(preset) {
|
|
480
615
|
if (preset === void 0) return void 0;
|
|
481
|
-
const owners = Object.keys(this.config.modes).filter((id) => this.config.modes[id]?.preset
|
|
616
|
+
const owners = Object.keys(this.config.modes).filter((id) => this.config.modes[id]?.presetsOnly.includes(preset));
|
|
482
617
|
return owners.length === 1 ? owners[0] : void 0;
|
|
483
618
|
}
|
|
484
619
|
inheritedModeId(agent) {
|
|
@@ -529,6 +664,7 @@ var SessionModes = class extends Service {
|
|
|
529
664
|
};
|
|
530
665
|
function apply(ctx, config) {
|
|
531
666
|
registerHttpRoutes(ctx, new SessionModes(ctx, config));
|
|
667
|
+
ctx.plugin(SessionModeCatalog);
|
|
532
668
|
}
|
|
533
669
|
function respondJson(response, status, body) {
|
|
534
670
|
response.writeHead(status, { "content-type": "application/json; charset=utf-8" });
|
package/dist/rows.mjs
CHANGED
|
@@ -4,40 +4,51 @@ const CODING_PERSONA = {
|
|
|
4
4
|
suffix: "你的工作目录在 `{{cwd}}`"
|
|
5
5
|
};
|
|
6
6
|
const CHAT_PERSONA = { prefix: "你是一个助手。全程用中文(专有名词除外),包括但不限于思考,回答,工具描述;思考不要陷入重复循环,一旦循环立即退出。" };
|
|
7
|
+
const NOOP_MODE_ID = "noop";
|
|
7
8
|
const DEFAULT_MODE = "coding";
|
|
8
|
-
const MODE_SOURCES = [
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
9
|
+
const MODE_SOURCES = [
|
|
10
|
+
{
|
|
11
|
+
id: "coding",
|
|
12
|
+
name: "编码模式",
|
|
13
|
+
description: "功能完整的编码 Agent:文件、Shell、检索、联网等工具常驻,其余用法说明按需加载。",
|
|
14
|
+
persona: CODING_PERSONA,
|
|
15
|
+
role: ["main", "subagent"],
|
|
16
|
+
denyTools: ["load_workspace_dependencies"],
|
|
17
|
+
denySkills: [
|
|
18
|
+
"office-docx",
|
|
19
|
+
"office-pptx",
|
|
20
|
+
"office-xlsx"
|
|
21
|
+
],
|
|
22
|
+
denyPolicies: ["fs/edit-intent"]
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: "chat",
|
|
26
|
+
name: "对话模式",
|
|
27
|
+
description: "只做对话:提问与联网(搜索、抓取)三件工具,不注入系统提示词、工作区指令与技能目录。",
|
|
28
|
+
persona: CHAT_PERSONA,
|
|
29
|
+
role: ["main"],
|
|
30
|
+
allowTools: [
|
|
31
|
+
"ask_user_question",
|
|
32
|
+
"web_search",
|
|
33
|
+
"web_fetch"
|
|
34
|
+
],
|
|
35
|
+
instructions: false,
|
|
36
|
+
runtimeContext: false
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
id: NOOP_MODE_ID,
|
|
40
|
+
name: "原样模式",
|
|
41
|
+
description: "与上游一致:不加人格提示词、不收窄工具与技能、policy 规则全开、官方注入面照旧。",
|
|
42
|
+
role: ["main", "subagent"]
|
|
43
|
+
}
|
|
44
|
+
];
|
|
35
45
|
//#endregion
|
|
36
46
|
//#region src/rows.ts
|
|
37
47
|
function list(values) {
|
|
38
48
|
return values === void 0 || values.length === 0 ? void 0 : [...values];
|
|
39
49
|
}
|
|
40
50
|
function modeConfig(source) {
|
|
51
|
+
const presetsOnly = list(source.presetsOnly);
|
|
41
52
|
const allowTools = list(source.allowTools);
|
|
42
53
|
const denyTools = list(source.denyTools);
|
|
43
54
|
const allowSkills = list(source.allowSkills);
|
|
@@ -45,7 +56,7 @@ function modeConfig(source) {
|
|
|
45
56
|
const allowPolicies = list(source.allowPolicies);
|
|
46
57
|
const denyPolicies = list(source.denyPolicies);
|
|
47
58
|
return {
|
|
48
|
-
...
|
|
59
|
+
...presetsOnly === void 0 ? {} : { presetsOnly },
|
|
49
60
|
name: source.name,
|
|
50
61
|
description: source.description,
|
|
51
62
|
...source.role === void 0 ? {} : { role: [...source.role] },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@morlay/dsh-session-mode",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
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",
|
|
@@ -47,9 +47,12 @@
|
|
|
47
47
|
"@deepseek-ai/dsh-api-remotes": "0.2.0-rc.2",
|
|
48
48
|
"@deepseek-ai/dsh-api-session-controller": "0.2.0-rc.2",
|
|
49
49
|
"@deepseek-ai/dsh-client-locale": "0.2.0-rc.2",
|
|
50
|
+
"@deepseek-ai/dsh-client-store": "0.2.0-rc.2",
|
|
50
51
|
"@deepseek-ai/dsh-client-ui-conversation": "0.2.0-rc.2",
|
|
52
|
+
"@deepseek-ai/dsh-client-ui-plugin-manager": "0.2.0-rc.2",
|
|
51
53
|
"@deepseek-ai/dsh-client-ui-primitives": "0.2.0-rc.2",
|
|
52
54
|
"@deepseek-ai/dsh-client-ui-renderer": "0.2.0-rc.2",
|
|
55
|
+
"@deepseek-ai/dsh-client-ui-settings": "0.2.0-rc.2",
|
|
53
56
|
"@deepseek-ai/dsh-client-ui-slots": "0.2.0-rc.2",
|
|
54
57
|
"@deepseek-ai/dsh-fs": "0.2.0-rc.2",
|
|
55
58
|
"@deepseek-ai/dsh-fs-local": "0.2.0-rc.2",
|
|
@@ -61,8 +64,9 @@
|
|
|
61
64
|
"@deepseek-ai/dsh-system-prompt": "0.2.0-rc.2",
|
|
62
65
|
"@deepseek-ai/dsh-tool-skill": "0.2.0-rc.2",
|
|
63
66
|
"@deepseek-ai/dsh-tools": "0.2.0-rc.2",
|
|
64
|
-
"@
|
|
65
|
-
"@morlay/dsh-
|
|
67
|
+
"@deepseek-ai/dsh-typert-protocol": "0.2.0-rc.2",
|
|
68
|
+
"@morlay/dsh-client-ui-primitives": "0.0.8",
|
|
69
|
+
"@morlay/dsh-context-assembler": "0.0.6",
|
|
66
70
|
"@testing-library/react": "^16.3.0",
|
|
67
71
|
"@types/js-yaml": "^4.0.9",
|
|
68
72
|
"@types/react": "~18.3.1",
|
|
@@ -77,14 +81,20 @@
|
|
|
77
81
|
"@deepseek-ai/dsh-agent-preset-registry": "^0.2.0-rc.2",
|
|
78
82
|
"@deepseek-ai/dsh-api-session-controller": "^0.2.0-rc.2",
|
|
79
83
|
"@deepseek-ai/dsh-client-locale": "^0.2.0-rc.2",
|
|
84
|
+
"@deepseek-ai/dsh-client-store": "^0.2.0-rc.2",
|
|
80
85
|
"@deepseek-ai/dsh-client-ui-conversation": "^0.2.0-rc.2",
|
|
86
|
+
"@deepseek-ai/dsh-client-ui-plugin-manager": "^0.2.0-rc.2",
|
|
81
87
|
"@deepseek-ai/dsh-client-ui-primitives": "^0.2.0-rc.2",
|
|
88
|
+
"@deepseek-ai/dsh-client-ui-settings": "^0.2.0-rc.2",
|
|
82
89
|
"@deepseek-ai/dsh-client-ui-slots": "^0.2.0-rc.2",
|
|
83
90
|
"@deepseek-ai/dsh-fs": "^0.2.0-rc.2",
|
|
84
91
|
"@deepseek-ai/dsh-llm": "^0.2.0-rc.2",
|
|
85
92
|
"@deepseek-ai/dsh-session": "^0.2.0-rc.2",
|
|
86
93
|
"@deepseek-ai/dsh-session-projection": "^0.2.0-rc.2",
|
|
94
|
+
"@deepseek-ai/dsh-skill": "^0.2.0-rc.2",
|
|
87
95
|
"@deepseek-ai/dsh-system-prompt": "^0.2.0-rc.2",
|
|
96
|
+
"@deepseek-ai/dsh-tools": "^0.2.0-rc.2",
|
|
97
|
+
"@deepseek-ai/dsh-typert-protocol": "^0.2.0-rc.2",
|
|
88
98
|
"@deepseek-ai/schemastery": "^3.18.4"
|
|
89
99
|
},
|
|
90
100
|
"dsh": {
|
|
@@ -92,6 +102,8 @@
|
|
|
92
102
|
"platform": "web",
|
|
93
103
|
"inject": [
|
|
94
104
|
"@deepseek-ai/dsh-client-locale",
|
|
105
|
+
"@deepseek-ai/dsh-client-ui-plugin-manager",
|
|
106
|
+
"@deepseek-ai/dsh-client-ui-settings",
|
|
95
107
|
"@deepseek-ai/dsh-client-ui-slots"
|
|
96
108
|
]
|
|
97
109
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// 配置页要的"全局目录"(工具名 / 技能名)走一条**本包自己开的 Remote 面**:工具清单只在 host(`ctx.tools`),技能面
|
|
2
|
+
// 在客户端只有按会话的 `remote.skills.list({ sessionId })`——而这个配置页是全局设置面,没有会话。官方没有现成的 api,
|
|
3
|
+
// 所以这条面由本包自己注册:host 半提供服务([`./catalog.ts`](./catalog.ts) 的 `@Remote('list')`),client 半把下面这份
|
|
4
|
+
// 贡献 mount 上去([`./client/index.ts`](./client/index.ts))。
|
|
5
|
+
//
|
|
6
|
+
// 本文件只有类型与那份贡献常量(不带 host 依赖),两半都能 import。
|
|
7
|
+
|
|
8
|
+
import type { RemoteResult, TypertRemoteContribution } from "@deepseek-ai/dsh-typert-protocol";
|
|
9
|
+
|
|
10
|
+
// 线上命名空间与它的 Cordis 服务键:客户端 mount 之后按 `remote.<这个名字>` 取服务。
|
|
11
|
+
export const CATALOG_NS = "sessionModeCatalog";
|
|
12
|
+
|
|
13
|
+
// 部署里可用的工具名与技能名。
|
|
14
|
+
export interface SessionModeCatalogValue {
|
|
15
|
+
readonly tools: readonly string[];
|
|
16
|
+
readonly skills: readonly string[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// 这条 Remote 面的读侧。
|
|
20
|
+
export interface SessionModeCatalogRemote {
|
|
21
|
+
list: () => Promise<RemoteResult<SessionModeCatalogValue>>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
declare module "@deepseek-ai/dsh-typert-protocol" {
|
|
25
|
+
interface TypertRemoteNamespaceMap {
|
|
26
|
+
sessionModeCatalog: SessionModeCatalogRemote;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// 客户端挂载用的贡献:`result` 用 `src-json`(结果就是普通 JSON,不走严格解码)。
|
|
31
|
+
export const SESSION_MODE_CATALOG_REMOTE: TypertRemoteContribution = {
|
|
32
|
+
package: "@morlay/dsh-session-mode",
|
|
33
|
+
descriptors: [
|
|
34
|
+
{
|
|
35
|
+
id: "@morlay/dsh-session-mode#sessionModeCatalog/list",
|
|
36
|
+
service: CATALOG_NS,
|
|
37
|
+
namespace: CATALOG_NS,
|
|
38
|
+
method: "list",
|
|
39
|
+
implementation: "remoteExportList",
|
|
40
|
+
invocation: { kind: "direct" },
|
|
41
|
+
parameters: [],
|
|
42
|
+
result: { mode: "src-json" },
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
};
|
package/src/catalog.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// 全局目录的 host 半:把部署里**当前可见**的工具名与技能名列出来,供配置页当候选(工具 / 技能名单字段)。
|
|
2
|
+
//
|
|
3
|
+
// 为什么要有这一层:这两份清单只有 host 侧读得到(工具面在 `ctx.tools`、技能面在 `ctx.skills`),而客户端只有按会话的
|
|
4
|
+
// `remote.skills.list`——bundle 配置页却是全局设置面。所以本包自己开一条 Remote 命名空间(贡献常量在
|
|
5
|
+
// [`./catalog-remote.ts`](./catalog-remote.ts),由 client 半自己 mount)。
|
|
6
|
+
//
|
|
7
|
+
// 清单的范围是"**部署里当前可见的那些**",不是某一份静态名单:
|
|
8
|
+
// - 工具按 agent 收口——实测本部署的工具注册都发生在 agent 层,全局视图(`schemas()` 不传 scope)是空的;
|
|
9
|
+
// - 技能按 agent 的收口与工作目录扫——工作区技能要 `cwd` 才看得到,而本行没有自己的工作区上下文。
|
|
10
|
+
// 所以两份都按当前活跃 agent 逐个读、去重合并;没有活跃 agent(刚启动、会话一个都没开)时只剩全局层那份。
|
|
11
|
+
//
|
|
12
|
+
// 不按 preset 分别取:preset 到工具名的映射没有任何服务暴露(工具名由各自的行注册进 agent 层,行清单里只有包名),
|
|
13
|
+
// 要"按 preset 分别取"就得为每个 preset 各起一个探针 agent;而候选在页面上是一维标签输入,分组也没有落点。
|
|
14
|
+
|
|
15
|
+
import type { Context } from "@deepseek-ai/cordis";
|
|
16
|
+
import type { Agent } from "@deepseek-ai/dsh-agent";
|
|
17
|
+
import type {} from "@deepseek-ai/dsh-skill";
|
|
18
|
+
import type {} from "@deepseek-ai/dsh-tools";
|
|
19
|
+
import { Remote, TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
|
|
20
|
+
import { CATALOG_NS, type SessionModeCatalogValue } from "./catalog-remote.ts";
|
|
21
|
+
|
|
22
|
+
// 一个 agent 的工作目录:技能面按 `cwd` 才会扫工作区技能。
|
|
23
|
+
function cwdOf(agent: Agent): string | undefined {
|
|
24
|
+
const session = (agent as unknown as { session?: { header?: { cwd?: unknown } } }).session;
|
|
25
|
+
const cwd = session?.header?.cwd;
|
|
26
|
+
return typeof cwd === "string" ? cwd : undefined;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// 部署当前可见的工具与技能。
|
|
30
|
+
export class SessionModeCatalog extends TypertRemoteService {
|
|
31
|
+
/**
|
|
32
|
+
* @param ctx - owning context (the `session-mode` row).
|
|
33
|
+
*/
|
|
34
|
+
constructor(ctx: Context) {
|
|
35
|
+
super(ctx, CATALOG_NS);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* List the tool and skill names this deployment currently shows.
|
|
40
|
+
* @returns tool names from the tool registry, and skill names from the skill registry.
|
|
41
|
+
*/
|
|
42
|
+
@Remote("list")
|
|
43
|
+
async remoteExportList(): Promise<SessionModeCatalogValue> {
|
|
44
|
+
return { tools: this.toolNames(), skills: await this.skillNames() };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// 工具名:全局层 + 每个活跃 agent 的可见面(同一名字只留一次)。
|
|
48
|
+
private toolNames(): readonly string[] {
|
|
49
|
+
const tools = this.ctx.get("tools");
|
|
50
|
+
if (tools === undefined) return [];
|
|
51
|
+
const names = new Set(tools.schemas().map((schema) => String(schema.name)));
|
|
52
|
+
for (const agent of this.agents()) {
|
|
53
|
+
for (const schema of tools.schemas(agent)) names.add(String(schema.name));
|
|
54
|
+
}
|
|
55
|
+
return [...names];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// 技能名:全局层 + 每个活跃 agent 按其收口与工作目录看到的那份。
|
|
59
|
+
private async skillNames(): Promise<readonly string[]> {
|
|
60
|
+
const skills = this.ctx.get("skills");
|
|
61
|
+
if (skills === undefined) return [];
|
|
62
|
+
const names = new Set((await skills.list()).map((row) => row.name));
|
|
63
|
+
for (const agent of this.agents()) {
|
|
64
|
+
for (const row of await skills.list({ scope: agent, cwd: cwdOf(agent) })) {
|
|
65
|
+
names.add(row.name);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return [...names];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// 当前活跃的 agent:清单按它们逐个读。服务缺席(极简装配)时按没有处理。
|
|
72
|
+
private agents(): readonly Agent[] {
|
|
73
|
+
return this.ctx.get("agents")?.list() ?? [];
|
|
74
|
+
}
|
|
75
|
+
}
|