@morlay/dsh-session-mode 0.0.1 → 0.0.3
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 +51 -110
- package/dist/client.cjs +16 -70
- package/dist/client.d.cts +0 -15
- package/dist/client.d.mts +0 -15
- package/dist/index.d.cts +10 -171
- package/dist/index.d.mts +10 -171
- package/dist/index.mjs +149 -162
- package/dist/rows.d.cts +1 -27
- package/dist/rows.d.mts +1 -27
- package/dist/rows.mjs +15 -452
- package/dist/shared-DX2Giyh3.mjs +5 -0
- package/locale/en.json +2 -2
- package/locale/zh.json +1 -1
- package/package.json +36 -34
- package/src/client/SessionModeSeat.tsx +7 -24
- package/src/client/api.ts +4 -14
- package/src/client/index.ts +34 -31
- package/src/client/locales.ts +2 -6
- package/src/client/use-roster.ts +3 -5
- package/src/index.ts +124 -147
- package/src/mode-sources.ts +42 -46
- package/src/modes.ts +136 -115
- package/src/persona.ts +3 -16
- package/src/rows.ts +26 -14
- package/src/scope.ts +197 -0
- package/src/shared.ts +18 -14
- package/dist/shared-ChCbbJa8.mjs +0 -14
package/dist/index.d.cts
CHANGED
|
@@ -4,134 +4,60 @@ import { Session, SessionEvent, SessionId } from "@deepseek-ai/dsh-session";
|
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import z$1 from "@deepseek-ai/schemastery";
|
|
6
6
|
//#region src/modes.d.ts
|
|
7
|
-
/** 一个模式的提示词:两段文本,注册成 agent 作用域的 `deployment:persona-prefix` / `-suffix` section。 */
|
|
8
7
|
interface SessionModePersona {
|
|
9
|
-
/** 系统提示词最前的一段;空串表示不遮蔽部署级那层。 */
|
|
10
8
|
readonly prefix: string;
|
|
11
|
-
/** 系统提示词最后的一段;空串表示不写。 */
|
|
12
9
|
readonly suffix: string;
|
|
13
10
|
}
|
|
14
|
-
/**
|
|
15
|
-
* 一个模式对谁可见:`main` 进用户选择器(会话级选择),`subagent` 表示它**可以**作为子代理的 mode。
|
|
16
|
-
* 两个角色可以同时声明;不写默认 `["main"]`——没写角色的模式不该悄悄变成子代理候选。
|
|
17
|
-
*
|
|
18
|
-
* `subagent` 目前只是候选集的声明:子代理默认继承父 mode(不看角色),"按角色指派 mode" 还没做。
|
|
19
|
-
*/
|
|
20
11
|
type SessionModeRole = "main" | "subagent";
|
|
21
|
-
/** 一个模式的默认模型;省略的字段跟着 provider 默认走(与全局 `agent-default-model` 同形状)。 */
|
|
22
12
|
interface SessionModeModel {
|
|
23
13
|
readonly provider: string;
|
|
24
14
|
readonly model: string;
|
|
25
15
|
readonly reasoningEffort?: string;
|
|
26
16
|
}
|
|
27
|
-
/** 退役的顶层形状:模式 id → 模型(默认模型现在住在各自的模式里)。 */
|
|
28
17
|
type SessionModeModels = Readonly<Record<string, SessionModeModel>>;
|
|
29
|
-
/**
|
|
30
|
-
* 一个模式:提示词 + 能力开关。
|
|
31
|
-
*
|
|
32
|
-
* 这份形状是 **schema 归一化之后**的:每个字段都有值(写配置时可以不写,schema 用默认补上——`description`
|
|
33
|
-
* 补空串、`persona` 补两段空文本、两个布尔开关补 `true`)。配置里能省略哪些字段看 `modeSchema` 的 default,
|
|
34
|
-
* 不看这里。
|
|
35
|
-
*/
|
|
36
18
|
interface SessionMode {
|
|
37
|
-
/**
|
|
38
|
-
* 这个模式挂在哪个 **agent preset** 上(官方 `standard` / `ptc` / `minimal` / `cordis`,或本部署自己
|
|
39
|
-
* 注册的那一份的 `id`,见 `mode-sources.ts` 的 `MODE_PRESET_ID`)。
|
|
40
|
-
*
|
|
41
|
-
* preset 决定这个 agent 有哪些行(工具 / 命令 / 压缩 / 委派…),模式只决定"这些行怎么被用":
|
|
42
|
-
* `allowTools` 里 preset 没有的工具自动跳过,其余扩展(persona / 注入开关 / 默认模型)照常应用。
|
|
43
|
-
* **可以共享**(本部署就是两个模式挂同一份 preset,差异全在会话级收口);共享时 preset → 模式的反查无从下手,
|
|
44
|
-
* 见 `SessionModes.modeForPreset`。空串表示不挂(只能由 applyTo / 继承使用)。
|
|
45
|
-
*/
|
|
46
19
|
readonly preset: string;
|
|
47
|
-
/** 模式的展示名(选择面归官方 roster;这里留着做事实文案)。 */
|
|
48
20
|
readonly name: string;
|
|
49
|
-
/** 一句话说明这个模式干什么;空串表示没写。 */
|
|
50
21
|
readonly description: string;
|
|
51
|
-
/** 这个模式归谁用:`main`(用户选择器)/ `subagent`(可作子代理 mode)。至少一个。 */
|
|
52
22
|
readonly role: SessionModeRole[];
|
|
53
|
-
/** 该模式的提示词。 */
|
|
54
23
|
readonly persona: SessionModePersona;
|
|
55
|
-
/**
|
|
56
|
-
* 这个模式能用哪些工具;其余工具既不进模型目录、调用也被执行层拒绝,它们自己的说明 section
|
|
57
|
-
* (`tool:<工具名>`)也不留在提示词里。**至少给一个**——想要"全都要"就列出全部,别留空。
|
|
58
|
-
*/
|
|
59
24
|
readonly allowTools: string[];
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
*/
|
|
25
|
+
readonly denyTools: string[];
|
|
26
|
+
readonly allowPolicies: string[];
|
|
27
|
+
readonly denyPolicies: string[];
|
|
64
28
|
readonly instructions: boolean;
|
|
65
|
-
|
|
66
|
-
* 是否要 runtime context(文件沙箱策略、审批策略那两条动态快照)。缺省要;`false` 表示这个模式不要它们
|
|
67
|
-
* ——对话模式没有文件与 shell 工具,"能改工作区哪些文件、要不要走审批"对它全是噪音。
|
|
68
|
-
*/
|
|
29
|
+
readonly skills?: boolean;
|
|
69
30
|
readonly runtimeContext: boolean;
|
|
70
|
-
/**
|
|
71
|
-
* 这个模式的默认模型;省略就跟全局 `agent-default-model`。**可选**:没配的模式在页面上不出现在这一行
|
|
72
|
-
* (`defaultModel` 是它所在模式的一个可加字段)。
|
|
73
|
-
*/
|
|
74
31
|
readonly defaultModel?: SessionModeModel;
|
|
75
32
|
}
|
|
76
|
-
/** 本包 config 的**源码形状**:装配层与设置页写的那个形状。 */
|
|
77
33
|
interface Config {
|
|
78
|
-
/** 新会话(还没选过模式的会话)用哪个模式。必须是 `modes` 里的一个 id。 */
|
|
79
34
|
readonly default: string;
|
|
80
|
-
/** 模式清单:id → 定义(含各自的 `defaultModel`)。顺序即选择器里的顺序(`Object.entries` 的插入序)。 */
|
|
81
35
|
readonly modes: Record<string, SessionMode>;
|
|
82
|
-
/**
|
|
83
|
-
* 各模式默认模型曾经住在这里(模式 id → 模型)。现在住在**每个模式自己的 `defaultModel`** 里,这个字段
|
|
84
|
-
* 只剩一件事:装配期看见它还配着值就报错,提醒把它挪进对应的模式——否则它会静静地失效。
|
|
85
|
-
*/
|
|
86
36
|
readonly models?: SessionModeModels;
|
|
87
37
|
}
|
|
88
|
-
/**
|
|
89
|
-
* schema 解析之后的形状:volatile 字段被换成**稳定引用**,读它要过 `.get()`(设置页改的就是同一份)。
|
|
90
|
-
*
|
|
91
|
-
* `default` 与 `modes` 都是 volatile:默认模式与整份模式清单(含各自的 `defaultModel`)都在行配置页上。
|
|
92
|
-
*/
|
|
93
38
|
interface ResolvedConfig {
|
|
94
39
|
readonly default: Volatile<string>;
|
|
95
40
|
readonly modes: Volatile<Record<string, SessionMode>>;
|
|
96
|
-
/** 退役的顶层字段:解析后仍在这儿(普通值,不 volatile),装配期据此发现"还配着值"并报错。 */
|
|
97
41
|
readonly models: SessionModeModels;
|
|
98
42
|
}
|
|
99
43
|
declare const Config: z$1<Config, ResolvedConfig>;
|
|
100
44
|
//#endregion
|
|
101
45
|
//#region src/shared.d.ts
|
|
102
|
-
/**
|
|
103
|
-
* host 半与 client 半共用的接口面:一条 HTTP 路径、模式行的对外形状、请求与响应体。
|
|
104
|
-
*
|
|
105
|
-
* 为什么不是 Typert Remote:客户端的 remote 清单(`@deepseek-ai/dsh-api-remotes/client`)由上游硬编码,
|
|
106
|
-
* 我们的服务不在里面,`ctx.remote.sessionModes` 解析不到。仓库既有的跨半通路是 HTTP 路由
|
|
107
|
-
* (见 `@morlay/ui-conversation-message-actions` 的 `/session-editor`),这里沿用同一种。
|
|
108
|
-
*
|
|
109
|
-
* 会话当前模式不走这条通路:它是一条 session 投影(`sessionMode`),随会话列表一起到页面。
|
|
110
|
-
*/
|
|
111
|
-
/** 模式清单与切换的路由路径:宿主(web 与桌面)在同一张路由表上服务它。 */
|
|
112
46
|
declare const SESSION_MODE_PATH = "/session-mode";
|
|
113
|
-
|
|
47
|
+
declare const POLICY_NAMES: readonly ["fs/write-intent", "fs/edit-intent"];
|
|
48
|
+
type PolicyName = (typeof POLICY_NAMES)[number];
|
|
114
49
|
interface SessionModeRow {
|
|
115
50
|
readonly id: string;
|
|
116
51
|
readonly name: string;
|
|
117
52
|
readonly description?: string;
|
|
118
53
|
}
|
|
119
|
-
/** `GET` 的响应体:清单与默认模式。 */
|
|
120
54
|
interface SessionModeRoster {
|
|
121
55
|
readonly default: string;
|
|
122
56
|
readonly modes: readonly SessionModeRow[];
|
|
123
57
|
}
|
|
124
58
|
//#endregion
|
|
125
59
|
//#region src/index.d.ts
|
|
126
|
-
/** Cordis 插件名:与行 id 一致。 */
|
|
127
60
|
declare const name = "session-mode";
|
|
128
|
-
/**
|
|
129
|
-
* 依赖:投影服务(登记 `sessionMode`、读 `turnBoundary`)、读会话与 agent 的两个注册表,以及提示词注册表
|
|
130
|
-
* (persona 的 section 注册在它上面)。
|
|
131
|
-
*
|
|
132
|
-
* 这些名字必须在 `inject` 里点名:cordis 的**属性访问**(`this.ctx.sessions`)要求本 fiber 声明过那个服务,
|
|
133
|
-
* 未声明会抛 `cannot get property "sessions" without inject`(`ctx.get(name)` 才不需要声明)。
|
|
134
|
-
*/
|
|
135
61
|
declare const inject: string[];
|
|
136
62
|
declare module "@deepseek-ai/cordis" {
|
|
137
63
|
interface Context {
|
|
@@ -140,10 +66,6 @@ declare module "@deepseek-ai/cordis" {
|
|
|
140
66
|
}
|
|
141
67
|
declare module "@deepseek-ai/dsh-session/types" {
|
|
142
68
|
interface SessionEventMap {
|
|
143
|
-
/**
|
|
144
|
-
* 会话在空白窗口里换了模式。**log-only**:它记录此后每一步实际运行的模式,恢复与 fork 时据此重建
|
|
145
|
-
* (模式决定模型看到的工具与提示词,所以它必须进日志)。
|
|
146
|
-
*/
|
|
147
69
|
"session-mode/selected": {
|
|
148
70
|
sessionMode: string;
|
|
149
71
|
};
|
|
@@ -155,13 +77,10 @@ declare module "@deepseek-ai/dsh-session-projection/types" {
|
|
|
155
77
|
sessionModeEditable: boolean;
|
|
156
78
|
}
|
|
157
79
|
interface SessionProjectionMap {
|
|
158
|
-
/** 会话当前模式;`null` 表示没选过(用部署默认)。 */
|
|
159
80
|
sessionMode: string | null;
|
|
160
|
-
/** 会话还能不能换模式:`true` 是选择器,`false` 是只读标签(client 那个 chip 据此变形)。 */
|
|
161
81
|
sessionModeEditable: boolean;
|
|
162
82
|
}
|
|
163
83
|
}
|
|
164
|
-
/** 会话模式的投影:初值来自空日志(没选过就是 `null`),只被选择事件推进。 */
|
|
165
84
|
declare const sessionModeProjection: {
|
|
166
85
|
key: "sessionMode";
|
|
167
86
|
stateSchema: z.ZodType<string | null, unknown, z.core.$ZodTypeInternals<string | null, unknown>>;
|
|
@@ -173,13 +92,6 @@ declare const sessionModeProjection: {
|
|
|
173
92
|
};
|
|
174
93
|
stateVersion: number;
|
|
175
94
|
};
|
|
176
|
-
/**
|
|
177
|
-
* 这个会话能不能换模式的投影:空白会话为 `true`,一旦 `turn/start` 落库就永远 `false`(换模式要的是**整段
|
|
178
|
-
* 历史**的模式一致,所以"开过 turn"之后连正在跑的那个 turn 也算)。
|
|
179
|
-
*
|
|
180
|
-
* **判据只有这一处**:服务端 {@link SessionModes.select} 的拒绝与 client chip 的只读形态都读它——以前
|
|
181
|
-
* client 只能等服务端报错,现在连入口都不给,而两边的结论来自同一份会话事实。
|
|
182
|
-
*/
|
|
183
95
|
declare const sessionModeEditableProjection: {
|
|
184
96
|
key: "sessionModeEditable";
|
|
185
97
|
stateSchema: z.ZodType<boolean, unknown, z.core.$ZodTypeInternals<boolean, unknown>>;
|
|
@@ -191,113 +103,40 @@ declare const sessionModeEditableProjection: {
|
|
|
191
103
|
};
|
|
192
104
|
stateVersion: number;
|
|
193
105
|
};
|
|
194
|
-
/** 模式清单、默认模式、按会话读取与切换。 */
|
|
195
106
|
declare class SessionModes extends Service {
|
|
196
|
-
/** 每个 agent 已经装上的那一份(persona + 默认模型兜底;模式变了就换一份)。 */
|
|
197
107
|
private readonly installs;
|
|
198
|
-
|
|
108
|
+
private readonly scope;
|
|
199
109
|
readonly config: {
|
|
200
110
|
default: string;
|
|
201
111
|
modes: Record<string, SessionMode>;
|
|
202
112
|
};
|
|
203
113
|
constructor(ctx: Context, config: ResolvedConfig);
|
|
204
|
-
/** 新会话用它:config 里的 `default`。 */
|
|
205
114
|
get defaultId(): string;
|
|
206
|
-
/** 选择器要的清单:只列 `main` 角色的模式(id、展示名、说明,顺序即 config 里 `modes` 的插入序)。 */
|
|
207
115
|
list(): SessionModeRow[];
|
|
208
|
-
/**
|
|
209
|
-
* 声明了某个角色的模式 id(顺序即 config 的插入序):`main` 给用户选择器,`subagent` 给子代理候选。
|
|
210
|
-
* @param role - 目标角色。
|
|
211
|
-
* @returns 该角色下的模式 id。
|
|
212
|
-
*/
|
|
213
116
|
idsFor(role: SessionModeRole): string[];
|
|
214
|
-
/**
|
|
215
|
-
* 同 {@link idsFor},给的是定义——"指定 mode" 那条接缝要拿候选集。
|
|
216
|
-
* @param role - 目标角色。
|
|
217
|
-
* @returns 该角色下的模式与其定义。
|
|
218
|
-
*/
|
|
219
117
|
modesFor(role: SessionModeRole): {
|
|
220
118
|
id: string;
|
|
221
119
|
mode: SessionMode;
|
|
222
120
|
}[];
|
|
223
|
-
/** 页面用的清单 + 默认模式。 */
|
|
224
121
|
roster(): SessionModeRoster;
|
|
225
|
-
/** 按 id 取定义;未知 id 直接抛(切换路径上它就是用户的错)。 */
|
|
226
122
|
definition(id?: string): SessionMode;
|
|
227
|
-
/** 会话当前模式:投影上有就用它,否则是部署默认。 */
|
|
228
123
|
modeOf(session: Session): string;
|
|
229
|
-
/** 会话当前模式的定义。 */
|
|
230
124
|
modeOfSession(session: Session): SessionMode;
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
*
|
|
234
|
-
* 模式带着它的 preset(`preset` 决定行清单),所以这里**先把 agent preset 换成模式声明的那个**,再落我们的
|
|
235
|
-
* 会话事实:否则 preset realm 还是旧那一套的行,而它的注入在我们的开关之外(旧形态 `chat` 挂 `minimal`、
|
|
236
|
-
* 会话的 preset 却还是 `standard` 时,上游 `agent-instructions` 会照旧把工作区指令注进这个"不要注入"的会话)。
|
|
237
|
-
*
|
|
238
|
-
* 目标 preset 与当前挂着的**相同时不切**(本部署两个模式共享同一份 preset,所以切模式通常走不到这一步):
|
|
239
|
-
* 换 preset 是一次重挂(卸旧行、装新行),没有变化就没有理由付出这个代价。
|
|
240
|
-
* @param sessionId - 目标会话(必须还没有开过 turn)。
|
|
241
|
-
* @param mode - 目标模式 id。
|
|
242
|
-
* @returns 提交后的模式 id。
|
|
243
|
-
*/
|
|
125
|
+
private policyInForce;
|
|
126
|
+
private decidePolicy;
|
|
244
127
|
select(sessionId: SessionId, mode: string): Promise<string>;
|
|
245
|
-
/**
|
|
246
|
-
* 某个 agent 当前挂着的 preset(registry 的 `composedPreset`)。
|
|
247
|
-
*
|
|
248
|
-
* 读不到时返回 `undefined`(按"未知"处理 → 该切就切):`composedPreset` 是 registry 较新的读面,替身与老
|
|
249
|
-
* 版本可能没有它;而"没挂任何 preset"(返回值 `undefined`)与"读不到"在这里是同一个结论。
|
|
250
|
-
*/
|
|
251
128
|
private presetOfAgent;
|
|
252
|
-
/**
|
|
253
|
-
* 官方 preset registry:行清单与它的选择面住在那一行。不在 `inject` 里点名(headless 部署没有它,
|
|
254
|
-
* 点名会让本行永不激活),所以按"可能拿不到"读——`ctx.get` 在当前 ctx 没声明那个服务时会抛。
|
|
255
|
-
*/
|
|
256
129
|
private presetRegistry;
|
|
257
|
-
/**
|
|
258
|
-
* 把某个活着的 agent 切到某个模式。与 {@link select} 的差别:它不要求空白会话——子代理创建时的继承
|
|
259
|
-
* 走这里,**未来的"指定 mode"入口(模型侧或配置侧)也走这里**(那条接缝还没做)。
|
|
260
|
-
* @param agent - 目标 agent。
|
|
261
|
-
* @param mode - 目标模式 id。
|
|
262
|
-
* @param options.record - 是否把这次切换写进会话日志(缺省写;只想改当前进程时给 `false`)。
|
|
263
|
-
*/
|
|
264
130
|
applyTo(agent: Agent, mode: string, options?: {
|
|
265
131
|
record?: boolean;
|
|
266
132
|
}): void;
|
|
267
|
-
/**
|
|
268
|
-
* 该 agent 用哪个模式:会话选过(投影上有)优先,子代理继承父,其余用部署默认。
|
|
269
|
-
*
|
|
270
|
-
* 继承要**写进子会话日志**:它是一条会话事实,冷恢复与 fork 都要靠它重建({@link modeOf} 只读投影)。
|
|
271
|
-
*/
|
|
272
133
|
private resolveModeId;
|
|
273
|
-
/** 官方 roster 选定的 preset(没选过、或 registry 没装时为 `undefined`)。 */
|
|
274
134
|
private presetOf;
|
|
275
|
-
/**
|
|
276
|
-
* 某个 agent preset 对应的模式 id——**只在映射唯一时**回答(几个模式挂同一份 preset 时返回
|
|
277
|
-
* `undefined`,不反查)。
|
|
278
|
-
*
|
|
279
|
-
* 本部署的两个模式共享同一个 preset(`MODE_PRESET_ID`),差异全在会话级收口,所以 preset → 模式的反查在
|
|
280
|
-
* 这里无意义:模式由**会话事实**决定(`session-mode/selected` 投影 → 子代理继承 → 部署默认),
|
|
281
|
-
* 官方 roster 选了什么 preset 不改变这个会话是哪个模式。这条反查留给"一对一映射"的部署形态。
|
|
282
|
-
* @param preset - preset id(`undefined` 表示没选过、或 registry 没装)。
|
|
283
|
-
* @returns 该 preset 唯一对应的模式 id;没配扩展、或由多个模式共享时 `undefined`。
|
|
284
|
-
*/
|
|
285
135
|
modeForPreset(preset: string | undefined): string | undefined;
|
|
286
|
-
/** 子代理(有 durable 父会话)继承父当前模式;父不在场、或不是子代理时没有可继承的。 */
|
|
287
136
|
private inheritedModeId;
|
|
288
|
-
/** 装或换该 agent 的那一份(幂等:同一模式不重复注册)。 */
|
|
289
137
|
private installFor;
|
|
290
|
-
/**
|
|
291
|
-
* 模式的默认模型(`modes.<模式 id>.defaultModel`)兜底:只在会话**尚无任何模型事实**(没选过模型、也
|
|
292
|
-
* 还没跑过请求)时接管这一请求的路由;一旦用户选过(投影 `pending`)或会话已经落过 header,就不再插手。
|
|
293
|
-
*
|
|
294
|
-
* 它是**配置事实**,不写会话事件——重启后仍由 config 决定;设置页里那条会话级选择才是会话事实。读的是构造
|
|
295
|
-
* 时那份模式清单快照:设置页保存会让这一行重挂(`reconcileProfilePatches`),新定义随重挂生效。
|
|
296
|
-
*/
|
|
297
138
|
private installDefaultModel;
|
|
298
|
-
/** 收口服务由 `@morlay/dsh-context-assembler/scope` 那一行发布;没装它就只有 persona。 */
|
|
299
|
-
private toolScope;
|
|
300
139
|
}
|
|
301
140
|
declare function apply(ctx: Context, config: ResolvedConfig): void;
|
|
302
141
|
//#endregion
|
|
303
|
-
export { Config, type ResolvedConfig, SESSION_MODE_PATH, type SessionMode, type SessionModeModel, type SessionModePersona, type SessionModeRole, type SessionModeRoster, type SessionModeRow, SessionModes, apply, inject, name, sessionModeEditableProjection, sessionModeProjection };
|
|
142
|
+
export { Config, POLICY_NAMES, type PolicyName, type ResolvedConfig, SESSION_MODE_PATH, type SessionMode, type SessionModeModel, type SessionModePersona, type SessionModeRole, type SessionModeRoster, type SessionModeRow, SessionModes, apply, inject, name, sessionModeEditableProjection, sessionModeProjection };
|
package/dist/index.d.mts
CHANGED
|
@@ -4,134 +4,60 @@ import z$1 from "@deepseek-ai/schemastery";
|
|
|
4
4
|
import { Agent } from "@deepseek-ai/dsh-agent";
|
|
5
5
|
import { Session, SessionEvent, SessionId } from "@deepseek-ai/dsh-session";
|
|
6
6
|
//#region src/modes.d.ts
|
|
7
|
-
/** 一个模式的提示词:两段文本,注册成 agent 作用域的 `deployment:persona-prefix` / `-suffix` section。 */
|
|
8
7
|
interface SessionModePersona {
|
|
9
|
-
/** 系统提示词最前的一段;空串表示不遮蔽部署级那层。 */
|
|
10
8
|
readonly prefix: string;
|
|
11
|
-
/** 系统提示词最后的一段;空串表示不写。 */
|
|
12
9
|
readonly suffix: string;
|
|
13
10
|
}
|
|
14
|
-
/**
|
|
15
|
-
* 一个模式对谁可见:`main` 进用户选择器(会话级选择),`subagent` 表示它**可以**作为子代理的 mode。
|
|
16
|
-
* 两个角色可以同时声明;不写默认 `["main"]`——没写角色的模式不该悄悄变成子代理候选。
|
|
17
|
-
*
|
|
18
|
-
* `subagent` 目前只是候选集的声明:子代理默认继承父 mode(不看角色),"按角色指派 mode" 还没做。
|
|
19
|
-
*/
|
|
20
11
|
type SessionModeRole = "main" | "subagent";
|
|
21
|
-
/** 一个模式的默认模型;省略的字段跟着 provider 默认走(与全局 `agent-default-model` 同形状)。 */
|
|
22
12
|
interface SessionModeModel {
|
|
23
13
|
readonly provider: string;
|
|
24
14
|
readonly model: string;
|
|
25
15
|
readonly reasoningEffort?: string;
|
|
26
16
|
}
|
|
27
|
-
/** 退役的顶层形状:模式 id → 模型(默认模型现在住在各自的模式里)。 */
|
|
28
17
|
type SessionModeModels = Readonly<Record<string, SessionModeModel>>;
|
|
29
|
-
/**
|
|
30
|
-
* 一个模式:提示词 + 能力开关。
|
|
31
|
-
*
|
|
32
|
-
* 这份形状是 **schema 归一化之后**的:每个字段都有值(写配置时可以不写,schema 用默认补上——`description`
|
|
33
|
-
* 补空串、`persona` 补两段空文本、两个布尔开关补 `true`)。配置里能省略哪些字段看 `modeSchema` 的 default,
|
|
34
|
-
* 不看这里。
|
|
35
|
-
*/
|
|
36
18
|
interface SessionMode {
|
|
37
|
-
/**
|
|
38
|
-
* 这个模式挂在哪个 **agent preset** 上(官方 `standard` / `ptc` / `minimal` / `cordis`,或本部署自己
|
|
39
|
-
* 注册的那一份的 `id`,见 `mode-sources.ts` 的 `MODE_PRESET_ID`)。
|
|
40
|
-
*
|
|
41
|
-
* preset 决定这个 agent 有哪些行(工具 / 命令 / 压缩 / 委派…),模式只决定"这些行怎么被用":
|
|
42
|
-
* `allowTools` 里 preset 没有的工具自动跳过,其余扩展(persona / 注入开关 / 默认模型)照常应用。
|
|
43
|
-
* **可以共享**(本部署就是两个模式挂同一份 preset,差异全在会话级收口);共享时 preset → 模式的反查无从下手,
|
|
44
|
-
* 见 `SessionModes.modeForPreset`。空串表示不挂(只能由 applyTo / 继承使用)。
|
|
45
|
-
*/
|
|
46
19
|
readonly preset: string;
|
|
47
|
-
/** 模式的展示名(选择面归官方 roster;这里留着做事实文案)。 */
|
|
48
20
|
readonly name: string;
|
|
49
|
-
/** 一句话说明这个模式干什么;空串表示没写。 */
|
|
50
21
|
readonly description: string;
|
|
51
|
-
/** 这个模式归谁用:`main`(用户选择器)/ `subagent`(可作子代理 mode)。至少一个。 */
|
|
52
22
|
readonly role: SessionModeRole[];
|
|
53
|
-
/** 该模式的提示词。 */
|
|
54
23
|
readonly persona: SessionModePersona;
|
|
55
|
-
/**
|
|
56
|
-
* 这个模式能用哪些工具;其余工具既不进模型目录、调用也被执行层拒绝,它们自己的说明 section
|
|
57
|
-
* (`tool:<工具名>`)也不留在提示词里。**至少给一个**——想要"全都要"就列出全部,别留空。
|
|
58
|
-
*/
|
|
59
24
|
readonly allowTools: string[];
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
*/
|
|
25
|
+
readonly denyTools: string[];
|
|
26
|
+
readonly allowPolicies: string[];
|
|
27
|
+
readonly denyPolicies: string[];
|
|
64
28
|
readonly instructions: boolean;
|
|
65
|
-
|
|
66
|
-
* 是否要 runtime context(文件沙箱策略、审批策略那两条动态快照)。缺省要;`false` 表示这个模式不要它们
|
|
67
|
-
* ——对话模式没有文件与 shell 工具,"能改工作区哪些文件、要不要走审批"对它全是噪音。
|
|
68
|
-
*/
|
|
29
|
+
readonly skills?: boolean;
|
|
69
30
|
readonly runtimeContext: boolean;
|
|
70
|
-
/**
|
|
71
|
-
* 这个模式的默认模型;省略就跟全局 `agent-default-model`。**可选**:没配的模式在页面上不出现在这一行
|
|
72
|
-
* (`defaultModel` 是它所在模式的一个可加字段)。
|
|
73
|
-
*/
|
|
74
31
|
readonly defaultModel?: SessionModeModel;
|
|
75
32
|
}
|
|
76
|
-
/** 本包 config 的**源码形状**:装配层与设置页写的那个形状。 */
|
|
77
33
|
interface Config {
|
|
78
|
-
/** 新会话(还没选过模式的会话)用哪个模式。必须是 `modes` 里的一个 id。 */
|
|
79
34
|
readonly default: string;
|
|
80
|
-
/** 模式清单:id → 定义(含各自的 `defaultModel`)。顺序即选择器里的顺序(`Object.entries` 的插入序)。 */
|
|
81
35
|
readonly modes: Record<string, SessionMode>;
|
|
82
|
-
/**
|
|
83
|
-
* 各模式默认模型曾经住在这里(模式 id → 模型)。现在住在**每个模式自己的 `defaultModel`** 里,这个字段
|
|
84
|
-
* 只剩一件事:装配期看见它还配着值就报错,提醒把它挪进对应的模式——否则它会静静地失效。
|
|
85
|
-
*/
|
|
86
36
|
readonly models?: SessionModeModels;
|
|
87
37
|
}
|
|
88
|
-
/**
|
|
89
|
-
* schema 解析之后的形状:volatile 字段被换成**稳定引用**,读它要过 `.get()`(设置页改的就是同一份)。
|
|
90
|
-
*
|
|
91
|
-
* `default` 与 `modes` 都是 volatile:默认模式与整份模式清单(含各自的 `defaultModel`)都在行配置页上。
|
|
92
|
-
*/
|
|
93
38
|
interface ResolvedConfig {
|
|
94
39
|
readonly default: Volatile<string>;
|
|
95
40
|
readonly modes: Volatile<Record<string, SessionMode>>;
|
|
96
|
-
/** 退役的顶层字段:解析后仍在这儿(普通值,不 volatile),装配期据此发现"还配着值"并报错。 */
|
|
97
41
|
readonly models: SessionModeModels;
|
|
98
42
|
}
|
|
99
43
|
declare const Config: z$1<Config, ResolvedConfig>;
|
|
100
44
|
//#endregion
|
|
101
45
|
//#region src/shared.d.ts
|
|
102
|
-
/**
|
|
103
|
-
* host 半与 client 半共用的接口面:一条 HTTP 路径、模式行的对外形状、请求与响应体。
|
|
104
|
-
*
|
|
105
|
-
* 为什么不是 Typert Remote:客户端的 remote 清单(`@deepseek-ai/dsh-api-remotes/client`)由上游硬编码,
|
|
106
|
-
* 我们的服务不在里面,`ctx.remote.sessionModes` 解析不到。仓库既有的跨半通路是 HTTP 路由
|
|
107
|
-
* (见 `@morlay/ui-conversation-message-actions` 的 `/session-editor`),这里沿用同一种。
|
|
108
|
-
*
|
|
109
|
-
* 会话当前模式不走这条通路:它是一条 session 投影(`sessionMode`),随会话列表一起到页面。
|
|
110
|
-
*/
|
|
111
|
-
/** 模式清单与切换的路由路径:宿主(web 与桌面)在同一张路由表上服务它。 */
|
|
112
46
|
declare const SESSION_MODE_PATH = "/session-mode";
|
|
113
|
-
|
|
47
|
+
declare const POLICY_NAMES: readonly ["fs/write-intent", "fs/edit-intent"];
|
|
48
|
+
type PolicyName = (typeof POLICY_NAMES)[number];
|
|
114
49
|
interface SessionModeRow {
|
|
115
50
|
readonly id: string;
|
|
116
51
|
readonly name: string;
|
|
117
52
|
readonly description?: string;
|
|
118
53
|
}
|
|
119
|
-
/** `GET` 的响应体:清单与默认模式。 */
|
|
120
54
|
interface SessionModeRoster {
|
|
121
55
|
readonly default: string;
|
|
122
56
|
readonly modes: readonly SessionModeRow[];
|
|
123
57
|
}
|
|
124
58
|
//#endregion
|
|
125
59
|
//#region src/index.d.ts
|
|
126
|
-
/** Cordis 插件名:与行 id 一致。 */
|
|
127
60
|
declare const name = "session-mode";
|
|
128
|
-
/**
|
|
129
|
-
* 依赖:投影服务(登记 `sessionMode`、读 `turnBoundary`)、读会话与 agent 的两个注册表,以及提示词注册表
|
|
130
|
-
* (persona 的 section 注册在它上面)。
|
|
131
|
-
*
|
|
132
|
-
* 这些名字必须在 `inject` 里点名:cordis 的**属性访问**(`this.ctx.sessions`)要求本 fiber 声明过那个服务,
|
|
133
|
-
* 未声明会抛 `cannot get property "sessions" without inject`(`ctx.get(name)` 才不需要声明)。
|
|
134
|
-
*/
|
|
135
61
|
declare const inject: string[];
|
|
136
62
|
declare module "@deepseek-ai/cordis" {
|
|
137
63
|
interface Context {
|
|
@@ -140,10 +66,6 @@ declare module "@deepseek-ai/cordis" {
|
|
|
140
66
|
}
|
|
141
67
|
declare module "@deepseek-ai/dsh-session/types" {
|
|
142
68
|
interface SessionEventMap {
|
|
143
|
-
/**
|
|
144
|
-
* 会话在空白窗口里换了模式。**log-only**:它记录此后每一步实际运行的模式,恢复与 fork 时据此重建
|
|
145
|
-
* (模式决定模型看到的工具与提示词,所以它必须进日志)。
|
|
146
|
-
*/
|
|
147
69
|
"session-mode/selected": {
|
|
148
70
|
sessionMode: string;
|
|
149
71
|
};
|
|
@@ -155,13 +77,10 @@ declare module "@deepseek-ai/dsh-session-projection/types" {
|
|
|
155
77
|
sessionModeEditable: boolean;
|
|
156
78
|
}
|
|
157
79
|
interface SessionProjectionMap {
|
|
158
|
-
/** 会话当前模式;`null` 表示没选过(用部署默认)。 */
|
|
159
80
|
sessionMode: string | null;
|
|
160
|
-
/** 会话还能不能换模式:`true` 是选择器,`false` 是只读标签(client 那个 chip 据此变形)。 */
|
|
161
81
|
sessionModeEditable: boolean;
|
|
162
82
|
}
|
|
163
83
|
}
|
|
164
|
-
/** 会话模式的投影:初值来自空日志(没选过就是 `null`),只被选择事件推进。 */
|
|
165
84
|
declare const sessionModeProjection: {
|
|
166
85
|
key: "sessionMode";
|
|
167
86
|
stateSchema: z.ZodType<string | null, unknown, z.core.$ZodTypeInternals<string | null, unknown>>;
|
|
@@ -173,13 +92,6 @@ declare const sessionModeProjection: {
|
|
|
173
92
|
};
|
|
174
93
|
stateVersion: number;
|
|
175
94
|
};
|
|
176
|
-
/**
|
|
177
|
-
* 这个会话能不能换模式的投影:空白会话为 `true`,一旦 `turn/start` 落库就永远 `false`(换模式要的是**整段
|
|
178
|
-
* 历史**的模式一致,所以"开过 turn"之后连正在跑的那个 turn 也算)。
|
|
179
|
-
*
|
|
180
|
-
* **判据只有这一处**:服务端 {@link SessionModes.select} 的拒绝与 client chip 的只读形态都读它——以前
|
|
181
|
-
* client 只能等服务端报错,现在连入口都不给,而两边的结论来自同一份会话事实。
|
|
182
|
-
*/
|
|
183
95
|
declare const sessionModeEditableProjection: {
|
|
184
96
|
key: "sessionModeEditable";
|
|
185
97
|
stateSchema: z.ZodType<boolean, unknown, z.core.$ZodTypeInternals<boolean, unknown>>;
|
|
@@ -191,113 +103,40 @@ declare const sessionModeEditableProjection: {
|
|
|
191
103
|
};
|
|
192
104
|
stateVersion: number;
|
|
193
105
|
};
|
|
194
|
-
/** 模式清单、默认模式、按会话读取与切换。 */
|
|
195
106
|
declare class SessionModes extends Service {
|
|
196
|
-
/** 每个 agent 已经装上的那一份(persona + 默认模型兜底;模式变了就换一份)。 */
|
|
197
107
|
private readonly installs;
|
|
198
|
-
|
|
108
|
+
private readonly scope;
|
|
199
109
|
readonly config: {
|
|
200
110
|
default: string;
|
|
201
111
|
modes: Record<string, SessionMode>;
|
|
202
112
|
};
|
|
203
113
|
constructor(ctx: Context, config: ResolvedConfig);
|
|
204
|
-
/** 新会话用它:config 里的 `default`。 */
|
|
205
114
|
get defaultId(): string;
|
|
206
|
-
/** 选择器要的清单:只列 `main` 角色的模式(id、展示名、说明,顺序即 config 里 `modes` 的插入序)。 */
|
|
207
115
|
list(): SessionModeRow[];
|
|
208
|
-
/**
|
|
209
|
-
* 声明了某个角色的模式 id(顺序即 config 的插入序):`main` 给用户选择器,`subagent` 给子代理候选。
|
|
210
|
-
* @param role - 目标角色。
|
|
211
|
-
* @returns 该角色下的模式 id。
|
|
212
|
-
*/
|
|
213
116
|
idsFor(role: SessionModeRole): string[];
|
|
214
|
-
/**
|
|
215
|
-
* 同 {@link idsFor},给的是定义——"指定 mode" 那条接缝要拿候选集。
|
|
216
|
-
* @param role - 目标角色。
|
|
217
|
-
* @returns 该角色下的模式与其定义。
|
|
218
|
-
*/
|
|
219
117
|
modesFor(role: SessionModeRole): {
|
|
220
118
|
id: string;
|
|
221
119
|
mode: SessionMode;
|
|
222
120
|
}[];
|
|
223
|
-
/** 页面用的清单 + 默认模式。 */
|
|
224
121
|
roster(): SessionModeRoster;
|
|
225
|
-
/** 按 id 取定义;未知 id 直接抛(切换路径上它就是用户的错)。 */
|
|
226
122
|
definition(id?: string): SessionMode;
|
|
227
|
-
/** 会话当前模式:投影上有就用它,否则是部署默认。 */
|
|
228
123
|
modeOf(session: Session): string;
|
|
229
|
-
/** 会话当前模式的定义。 */
|
|
230
124
|
modeOfSession(session: Session): SessionMode;
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
*
|
|
234
|
-
* 模式带着它的 preset(`preset` 决定行清单),所以这里**先把 agent preset 换成模式声明的那个**,再落我们的
|
|
235
|
-
* 会话事实:否则 preset realm 还是旧那一套的行,而它的注入在我们的开关之外(旧形态 `chat` 挂 `minimal`、
|
|
236
|
-
* 会话的 preset 却还是 `standard` 时,上游 `agent-instructions` 会照旧把工作区指令注进这个"不要注入"的会话)。
|
|
237
|
-
*
|
|
238
|
-
* 目标 preset 与当前挂着的**相同时不切**(本部署两个模式共享同一份 preset,所以切模式通常走不到这一步):
|
|
239
|
-
* 换 preset 是一次重挂(卸旧行、装新行),没有变化就没有理由付出这个代价。
|
|
240
|
-
* @param sessionId - 目标会话(必须还没有开过 turn)。
|
|
241
|
-
* @param mode - 目标模式 id。
|
|
242
|
-
* @returns 提交后的模式 id。
|
|
243
|
-
*/
|
|
125
|
+
private policyInForce;
|
|
126
|
+
private decidePolicy;
|
|
244
127
|
select(sessionId: SessionId, mode: string): Promise<string>;
|
|
245
|
-
/**
|
|
246
|
-
* 某个 agent 当前挂着的 preset(registry 的 `composedPreset`)。
|
|
247
|
-
*
|
|
248
|
-
* 读不到时返回 `undefined`(按"未知"处理 → 该切就切):`composedPreset` 是 registry 较新的读面,替身与老
|
|
249
|
-
* 版本可能没有它;而"没挂任何 preset"(返回值 `undefined`)与"读不到"在这里是同一个结论。
|
|
250
|
-
*/
|
|
251
128
|
private presetOfAgent;
|
|
252
|
-
/**
|
|
253
|
-
* 官方 preset registry:行清单与它的选择面住在那一行。不在 `inject` 里点名(headless 部署没有它,
|
|
254
|
-
* 点名会让本行永不激活),所以按"可能拿不到"读——`ctx.get` 在当前 ctx 没声明那个服务时会抛。
|
|
255
|
-
*/
|
|
256
129
|
private presetRegistry;
|
|
257
|
-
/**
|
|
258
|
-
* 把某个活着的 agent 切到某个模式。与 {@link select} 的差别:它不要求空白会话——子代理创建时的继承
|
|
259
|
-
* 走这里,**未来的"指定 mode"入口(模型侧或配置侧)也走这里**(那条接缝还没做)。
|
|
260
|
-
* @param agent - 目标 agent。
|
|
261
|
-
* @param mode - 目标模式 id。
|
|
262
|
-
* @param options.record - 是否把这次切换写进会话日志(缺省写;只想改当前进程时给 `false`)。
|
|
263
|
-
*/
|
|
264
130
|
applyTo(agent: Agent, mode: string, options?: {
|
|
265
131
|
record?: boolean;
|
|
266
132
|
}): void;
|
|
267
|
-
/**
|
|
268
|
-
* 该 agent 用哪个模式:会话选过(投影上有)优先,子代理继承父,其余用部署默认。
|
|
269
|
-
*
|
|
270
|
-
* 继承要**写进子会话日志**:它是一条会话事实,冷恢复与 fork 都要靠它重建({@link modeOf} 只读投影)。
|
|
271
|
-
*/
|
|
272
133
|
private resolveModeId;
|
|
273
|
-
/** 官方 roster 选定的 preset(没选过、或 registry 没装时为 `undefined`)。 */
|
|
274
134
|
private presetOf;
|
|
275
|
-
/**
|
|
276
|
-
* 某个 agent preset 对应的模式 id——**只在映射唯一时**回答(几个模式挂同一份 preset 时返回
|
|
277
|
-
* `undefined`,不反查)。
|
|
278
|
-
*
|
|
279
|
-
* 本部署的两个模式共享同一个 preset(`MODE_PRESET_ID`),差异全在会话级收口,所以 preset → 模式的反查在
|
|
280
|
-
* 这里无意义:模式由**会话事实**决定(`session-mode/selected` 投影 → 子代理继承 → 部署默认),
|
|
281
|
-
* 官方 roster 选了什么 preset 不改变这个会话是哪个模式。这条反查留给"一对一映射"的部署形态。
|
|
282
|
-
* @param preset - preset id(`undefined` 表示没选过、或 registry 没装)。
|
|
283
|
-
* @returns 该 preset 唯一对应的模式 id;没配扩展、或由多个模式共享时 `undefined`。
|
|
284
|
-
*/
|
|
285
135
|
modeForPreset(preset: string | undefined): string | undefined;
|
|
286
|
-
/** 子代理(有 durable 父会话)继承父当前模式;父不在场、或不是子代理时没有可继承的。 */
|
|
287
136
|
private inheritedModeId;
|
|
288
|
-
/** 装或换该 agent 的那一份(幂等:同一模式不重复注册)。 */
|
|
289
137
|
private installFor;
|
|
290
|
-
/**
|
|
291
|
-
* 模式的默认模型(`modes.<模式 id>.defaultModel`)兜底:只在会话**尚无任何模型事实**(没选过模型、也
|
|
292
|
-
* 还没跑过请求)时接管这一请求的路由;一旦用户选过(投影 `pending`)或会话已经落过 header,就不再插手。
|
|
293
|
-
*
|
|
294
|
-
* 它是**配置事实**,不写会话事件——重启后仍由 config 决定;设置页里那条会话级选择才是会话事实。读的是构造
|
|
295
|
-
* 时那份模式清单快照:设置页保存会让这一行重挂(`reconcileProfilePatches`),新定义随重挂生效。
|
|
296
|
-
*/
|
|
297
138
|
private installDefaultModel;
|
|
298
|
-
/** 收口服务由 `@morlay/dsh-context-assembler/scope` 那一行发布;没装它就只有 persona。 */
|
|
299
|
-
private toolScope;
|
|
300
139
|
}
|
|
301
140
|
declare function apply(ctx: Context, config: ResolvedConfig): void;
|
|
302
141
|
//#endregion
|
|
303
|
-
export { Config, type ResolvedConfig, SESSION_MODE_PATH, type SessionMode, type SessionModeModel, type SessionModePersona, type SessionModeRole, type SessionModeRoster, type SessionModeRow, SessionModes, apply, inject, name, sessionModeEditableProjection, sessionModeProjection };
|
|
142
|
+
export { Config, POLICY_NAMES, type PolicyName, type ResolvedConfig, SESSION_MODE_PATH, type SessionMode, type SessionModeModel, type SessionModePersona, type SessionModeRole, type SessionModeRoster, type SessionModeRow, SessionModes, apply, inject, name, sessionModeEditableProjection, sessionModeProjection };
|