@morlay/dsh-session-mode 0.0.1-alpha.4
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/LICENSE +21 -0
- package/README.md +116 -0
- package/cordis.patch.yml +69 -0
- package/dist/client.cjs +1008 -0
- package/dist/client.d.cts +210 -0
- package/dist/client.d.mts +210 -0
- package/dist/index.d.cts +129 -0
- package/dist/index.d.mts +129 -0
- package/dist/index.mjs +368 -0
- package/dist/shared-ChCbbJa8.mjs +14 -0
- package/dist/shared-FNWe-_rS.d.cts +106 -0
- package/dist/shared-FNWe-_rS.d.mts +106 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +108 -0
- package/src/client/ModelDefaultsCard.styles.ts +134 -0
- package/src/client/ModelDefaultsCard.tsx +233 -0
- package/src/client/SessionModeLabel.module.css +24 -0
- package/src/client/SessionModeLabel.tsx +44 -0
- package/src/client/SessionModeSeat.module.css +74 -0
- package/src/client/SessionModeSeat.tsx +109 -0
- package/src/client/api.ts +45 -0
- package/src/client/index.ts +142 -0
- package/src/client/locales.ts +88 -0
- package/src/client/model-defaults-card-controller.ts +408 -0
- package/src/client/use-roster.ts +30 -0
- package/src/index.ts +425 -0
- package/src/modes.ts +184 -0
- package/src/persona.ts +44 -0
- package/src/shared.ts +36 -0
- package/tool/modes.ts +85 -0
- package/tool/patch.ts +91 -0
package/src/shared.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* host 半与 client 半共用的接口面:一条 HTTP 路径、模式行的对外形状、请求与响应体。
|
|
3
|
+
*
|
|
4
|
+
* 为什么不是 Typert Remote:客户端的 remote 清单(`@deepseek-ai/dsh-api-remotes/client`)由上游硬编码,
|
|
5
|
+
* 我们的服务不在里面,`ctx.remote.sessionModes` 解析不到。仓库既有的跨半通路是 HTTP 路由
|
|
6
|
+
* (见 `@morlay/ui-conversation-message-actions` 的 `/session-editor`),这里沿用同一种。
|
|
7
|
+
*
|
|
8
|
+
* 会话当前模式不走这条通路:它是一条 session 投影(`sessionMode`),随会话列表一起到页面。
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/** 模式清单与切换的路由路径:宿主(web 与桌面)在同一张路由表上服务它。 */
|
|
12
|
+
export const SESSION_MODE_PATH = "/session-mode";
|
|
13
|
+
|
|
14
|
+
/** 一个模式对外的那部分:选择器要的名字与说明。 */
|
|
15
|
+
export interface SessionModeRow {
|
|
16
|
+
readonly id: string;
|
|
17
|
+
readonly name: string;
|
|
18
|
+
readonly description?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** `GET` 的响应体:清单与默认模式。 */
|
|
22
|
+
export interface SessionModeRoster {
|
|
23
|
+
readonly default: string;
|
|
24
|
+
readonly modes: readonly SessionModeRow[];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** `POST` 的请求体:把某个空白会话切到某个模式。 */
|
|
28
|
+
export interface SessionModeSelectRequest {
|
|
29
|
+
readonly sessionId: string;
|
|
30
|
+
readonly mode: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** `POST` 的响应体:切换后提交的模式 id。 */
|
|
34
|
+
export interface SessionModeSelectResult {
|
|
35
|
+
readonly mode: string;
|
|
36
|
+
}
|
package/tool/modes.ts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 模式定义的真源:`cordis.patch.yml` 里 `session-mode` 行的 `config` 由这里渲染。
|
|
3
|
+
*
|
|
4
|
+
* "自定义"就落在这份数据上——装配层(profile 的用户 patch 层)可以整体改写 `config.modes`,也可以只给
|
|
5
|
+
* 某个模式换提示词或工具白名单,不需要任何插件行。
|
|
6
|
+
*
|
|
7
|
+
* `allowTools` 从 [`@morlay/dsh-agent-toolkit/rows`](../../../dsh-agent-toolkit/src/rows.ts) 的
|
|
8
|
+
* `TOOLKIT_TOOL_NAMES` 派生(工具集与汉化同源):toolkit 的 bundle 在 profile 平面把工具行装一次,模式
|
|
9
|
+
* 只声明"我这些会话能用哪些"。想改某一个模式的名单,直接在这条源数据里加/减。
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { TOOLKIT_TOOL_NAMES } from "@morlay/dsh-agent-toolkit/rows";
|
|
13
|
+
|
|
14
|
+
/** 一个模式的源定义:就是 `session-mode` 行 `config.modes` 里的一项。 */
|
|
15
|
+
export interface ModeSource {
|
|
16
|
+
readonly id: string;
|
|
17
|
+
readonly name: string;
|
|
18
|
+
readonly description: string;
|
|
19
|
+
/** 归谁用:`main`(用户选择器,缺省)、`subagent`(可作子代理 mode 的候选)。 */
|
|
20
|
+
readonly role?: readonly ("main" | "subagent")[];
|
|
21
|
+
readonly persona?: { readonly prefix?: string; readonly suffix?: string };
|
|
22
|
+
readonly allowTools: readonly string[];
|
|
23
|
+
readonly instructions?: boolean;
|
|
24
|
+
readonly runtimeContext?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** 一个模式的默认模型源定义:就是 `session-mode` 行 `config.models[<模式 id>]`。 */
|
|
28
|
+
export interface ModeModelSource {
|
|
29
|
+
readonly provider: string;
|
|
30
|
+
readonly model: string;
|
|
31
|
+
readonly reasoningEffort?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** 编码模式:编程专家 + 语言与思考纪律 + 工作目录提醒。 */
|
|
35
|
+
const CODING_PERSONA = {
|
|
36
|
+
prefix: [
|
|
37
|
+
"你是一个经验丰富的编程专家,YAGNI 是你的编程哲学,PDCA 是你的行为规范。",
|
|
38
|
+
"全程用中文(专有名词除外),包括但不限于思考,回答,工具描述,subagent 提示词;思考不要陷入重复循环,一旦循环立即退出;思考聚焦需求理解与方案设计,不预演具体代码实现,正确性由验证环节确认。",
|
|
39
|
+
].join("\n"),
|
|
40
|
+
suffix: "你的工作目录在 `{{cwd}}`",
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/** 对话模式:一个助手,保留语言与思考纪律,没有 suffix。 */
|
|
44
|
+
const CHAT_PERSONA = {
|
|
45
|
+
prefix:
|
|
46
|
+
"你是一个助手。全程用中文(专有名词除外),包括但不限于思考,回答,工具描述;思考不要陷入重复循环,一旦循环立即退出。",
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/** 新会话用哪个模式(`session-mode` 行的 `config.default`)。 */
|
|
50
|
+
export const DEFAULT_MODE = "coding";
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 各模式的默认模型(`session-mode` 行 `config.models`:模式 id → 模型)。
|
|
54
|
+
*
|
|
55
|
+
* 当前为空——两个模式都跟着全局 `agent-default-model`。要改某个模式的默认模型就写在这里(键必须是
|
|
56
|
+
* `MODE_SOURCES` 里的 id,装配期校验会拒绝孤儿键);用户在设置页写的那份由 settings 的用户层叠在它上面。
|
|
57
|
+
*/
|
|
58
|
+
export const MODE_MODELS: Readonly<Record<string, ModeModelSource>> = {};
|
|
59
|
+
|
|
60
|
+
/** 两个模式:编码与对话。 */
|
|
61
|
+
export const MODE_SOURCES: readonly ModeSource[] = [
|
|
62
|
+
{
|
|
63
|
+
id: "coding",
|
|
64
|
+
name: "编码模式",
|
|
65
|
+
description: "功能完整的编码 Agent:文件、Shell、检索、联网等工具常驻,其余用法说明按需加载。",
|
|
66
|
+
persona: CODING_PERSONA,
|
|
67
|
+
// 用户可选,也允许作为子代理的 mode(子代理默认继承父 mode,不看角色;这里是"可被指定"的候选集)。
|
|
68
|
+
role: ["main", "subagent"],
|
|
69
|
+
allowTools: [...TOOLKIT_TOOL_NAMES],
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
id: "chat",
|
|
73
|
+
name: "对话模式",
|
|
74
|
+
description:
|
|
75
|
+
"只做对话:提问与联网(搜索、抓取)三件工具,不注入系统提示词、工作区指令与技能目录。",
|
|
76
|
+
persona: CHAT_PERSONA,
|
|
77
|
+
// 只做用户侧对话:不做子代理的候选(父在 chat 里派发的子代理仍继承 chat,见 README 的"角色"一节)。
|
|
78
|
+
role: ["main"],
|
|
79
|
+
// 提问与联网三件:工具行由 toolkit 的 bundle 装,这里只收口。
|
|
80
|
+
allowTools: ["ask_user_question", "web_search", "web_fetch"],
|
|
81
|
+
// 没有文件与 shell 工具,"能改工作区哪些文件、要不要走审批"对它全是噪音。
|
|
82
|
+
instructions: false,
|
|
83
|
+
runtimeContext: false,
|
|
84
|
+
},
|
|
85
|
+
];
|
package/tool/patch.ts
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { writeFile } from "node:fs/promises";
|
|
2
|
+
import { dirname, join, resolve } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { entryListSchema } from "@deepseek-ai/cordis-plugin-include";
|
|
5
|
+
import { scopeRow } from "@morlay/dsh-context-assembler/rows";
|
|
6
|
+
import yaml from "js-yaml";
|
|
7
|
+
import { DEFAULT_MODE, MODE_MODELS, MODE_SOURCES, type ModeSource } from "./modes.ts";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* bundle patch 的行清单:**两行**——模式本身,与模式的工具收口。
|
|
11
|
+
*
|
|
12
|
+
* | 行 | 是什么 |
|
|
13
|
+
* | ------------------------- | ------------------------------------------------------------------------------------------ |
|
|
14
|
+
* | `session-mode` | 模式清单、默认模式与各模式的默认模型(`config.modes` / `config.default` / `config.models`)+ 按会话应用 persona + 页面用的路由 |
|
|
15
|
+
* | `context-assembler-scope` | `@morlay/dsh-context-assembler/scope`:读模式定义,按会话收口工具、instruction 与动态快照 |
|
|
16
|
+
*
|
|
17
|
+
* 工具行与注入通道**都不在这里**——它们由各自的 bundle 在 profile 平面装一次
|
|
18
|
+
* (`dsh.profile.bundles` 里的 [`@morlay/dsh-agent-toolkit`](../dsh-agent-toolkit/README.md) 与
|
|
19
|
+
* [`@morlay/dsh-context-assembler`](../../context/dsh-context-assembler/README.md))。模式只声明"我要哪些"。
|
|
20
|
+
*
|
|
21
|
+
* 这里是生成物的真源:`cordis.patch.yml` 由 `renderPatch()` 写出,改动请改这份 TS,
|
|
22
|
+
* 一致性由 `patch.spec.ts` 的断言兜住。
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
function modeConfig(source: ModeSource): Record<string, unknown> {
|
|
26
|
+
return {
|
|
27
|
+
name: source.name,
|
|
28
|
+
description: source.description,
|
|
29
|
+
...(source.role === undefined ? {} : { role: [...source.role] }),
|
|
30
|
+
...(source.persona === undefined ? {} : { persona: { ...source.persona } }),
|
|
31
|
+
allowTools: [...source.allowTools],
|
|
32
|
+
...(source.instructions === undefined ? {} : { instructions: source.instructions }),
|
|
33
|
+
...(source.runtimeContext === undefined ? {} : { runtimeContext: source.runtimeContext }),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const PATCH_ROWS: readonly Record<string, unknown>[] = [
|
|
38
|
+
{
|
|
39
|
+
insert: [
|
|
40
|
+
{
|
|
41
|
+
id: "session-mode",
|
|
42
|
+
name: "@morlay/dsh-session-mode",
|
|
43
|
+
config: {
|
|
44
|
+
default: DEFAULT_MODE,
|
|
45
|
+
modes: Object.fromEntries(MODE_SOURCES.map((source) => [source.id, modeConfig(source)])),
|
|
46
|
+
// 各模式的默认模型(volatile:设置页那张卡片写的就是这个路径)。当前没有配任何一条,
|
|
47
|
+
// 仍显式渲染成空对象——这份 patch 是装配层的真源,字段在不在要看得见。
|
|
48
|
+
models: Object.fromEntries(
|
|
49
|
+
Object.entries(MODE_MODELS).map(([id, model]) => [id, { ...model }]),
|
|
50
|
+
),
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
// 行 id 与 name 的真源在 `@morlay/dsh-context-assembler/rows` 的 `scopeRow()`:模式的收口是那个包的
|
|
54
|
+
// 出口,这里只是把它装进装配(与模式定义同一份 patch,行序上服务在前)。
|
|
55
|
+
scopeRow(),
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
export const PATCH_FILE = "cordis.patch.yml";
|
|
61
|
+
|
|
62
|
+
const HEADER = `# 本文件由 packages/profile/dsh-session-mode/tool/patch.ts 生成,请勿手工编辑
|
|
63
|
+
`;
|
|
64
|
+
|
|
65
|
+
export function renderPatch(): string {
|
|
66
|
+
const body = yaml.dump([...PATCH_ROWS], {
|
|
67
|
+
schema: entryListSchema,
|
|
68
|
+
lineWidth: -1,
|
|
69
|
+
quotingType: '"',
|
|
70
|
+
});
|
|
71
|
+
return `${HEADER}${body}`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** 把 bundle patch 落到包根:它是发布产物的一部分(`files` 里有它,装配按出口解析)。 */
|
|
75
|
+
export async function generatePatch(): Promise<string> {
|
|
76
|
+
const path = join(resolve(dirname(fileURLToPath(import.meta.url)), ".."), PATCH_FILE);
|
|
77
|
+
await writeFile(path, renderPatch());
|
|
78
|
+
return path;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* tsdown 的 `build:done` 钩子:每次构建重写 `cordis.patch.yml`,入库的那份因此永远等于
|
|
83
|
+
* `PATCH_ROWS`(`patch.spec.ts` 比对文件与 `renderPatch()`)。
|
|
84
|
+
*/
|
|
85
|
+
export function patchHooks(): { "build:done": () => Promise<void> } {
|
|
86
|
+
return {
|
|
87
|
+
"build:done": async () => {
|
|
88
|
+
process.stdout.write(`generated ${await generatePatch()}\n`);
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
}
|