@morlay/dsh-session-mode 0.0.1-alpha.4 → 0.0.1

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 CHANGED
@@ -1,21 +1,25 @@
1
1
  # @morlay/dsh-session-mode
2
2
 
3
- 会话模式:`coding` 与 `chat` 各是**一份数据**——一段提示词(persona)、一组能力开关与一个**角色**
4
- (`role`:谁可以用它);各模式的**默认模型**是同一份 config 顶层的 `models`。本包把它按会话应用到会话自己的
5
- 作用域上,并提供页面上的选择面(会话里的切换 chip 与设置页那张卡片)。**模式不是 Cordis 子树**:官方 agent preset 那一整套在装配层被
6
- 关掉,禁哪些行归 [`@morlay/dsh-profile`](../dsh-profile/README.md)(真源 `tool/patch.ts` 的 `PATCH_ROWS`),
7
- 本包不复述清单。
8
-
9
- `cordis.patch.yml` 是这里唯一装配的东西:两行。
10
-
11
- | 行 | 是什么 |
12
- | ------------------------- | -------------------------------------------------------------------------------------------------- |
13
- | `session-mode` | 模式清单、默认模式与各模式的默认模型(`config.modes` / `config.default` / `config.models`)+ 按会话应用 persona + 清单与切换的 HTTP 路由 |
14
- | `context-assembler-scope` | [`@morlay/dsh-context-assembler/scope`](../../context/dsh-context-assembler/README.md):按会话收口 |
15
-
16
- 工具行、注入通道与压缩都不在这里——它们由各自的 bundle 在 profile 平面装一次(`dsh.profile.bundles` 里的
17
- [`@morlay/dsh-agent-toolkit`](../dsh-agent-toolkit/README.md) 与
18
- [`@morlay/dsh-context-assembler`](../../context/dsh-context-assembler/README.md))。
3
+ **模式 = agent preset 的会话级扩展**:每个模式声明它挂在哪份 preset 上(`preset`,行清单由那份 preset 提供),
4
+ 再给这个会话加四样东西——一段 persona、一组工具白名单(收口)、instruction / 动态快照开关、可选默认模型。
5
+ 本部署的 preset 是**我们自己注册的那一份**(`mode-switch`,见
6
+ [`@morlay/session-mode-profile`](../../bundles/session-mode-profile/README.md) 与
7
+ [ADR-自己注册preset](../../bundles/session-mode-profile/.agents/adrs/20260929-自己注册preset.md)),
8
+ 两个模式共享它——**差异全由会话级收口表达**。选择面由我们提供(composer 里的模式 chip 与
9
+ `GET/POST /session-mode`);模式的选择落成**会话事实**(`session-mode/selected` 事件 + `sessionMode` 投影)。
10
+ **模式不是 Cordis 子树**。
11
+
12
+ 本包不装配任何行:行 config 由 `src/rows.ts` 渲染,装配入口在
13
+ [`@morlay/session-mode-profile`](../../bundles/session-mode-profile/README.md)(那里插两行)。
14
+
15
+ | 行 | 是什么 |
16
+ | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
17
+ | `session-mode` | 各模式的扩展定义(`config.modes.<id>.preset` + persona / `allowTools` / 开关 / `defaultModel`)与默认模式,按会话应用 + preset→模式反查 |
18
+ | `context-assembler-scope` | [`@morlay/dsh-context-assembler/scope`](../../context/dsh-context-assembler/README.md):按会话收口 |
19
+
20
+ 工具行、注入通道与压缩都不在这里:行清单由 preset 的 `config.plugins` 提供(本部署那份由
21
+ [`@morlay/session-mode-profile`](../../bundles/session-mode-profile/README.md) 声明,官方四个 shipped preset
22
+ 照旧可选),注入通道与工具说明由各自的 bundle 在 host 平面装一次。
19
23
 
20
24
  ## 一个模式是什么
21
25
 
@@ -24,10 +28,10 @@
24
28
  name: "@morlay/dsh-session-mode"
25
29
  config:
26
30
  default: coding
27
- models: # 各模式的默认模型(顶层;键必须是 modes 里的 id)
28
- chat: { provider: ollama, model: deepseek-v4.1-flash, reasoningEffort: high }
29
31
  modes:
30
32
  chat:
33
+ # 挂哪份 preset:行清单由它的 config.plugins 提供(本部署这份两个模式共享)。
34
+ preset: mode-switch
31
35
  name: 对话模式
32
36
  description: 只做对话:提问与联网(搜索、抓取)三件工具,不注入系统提示词、工作区指令与技能目录。
33
37
  role: [main]
@@ -36,81 +40,90 @@
36
40
  allowTools: [ask_user_question, web_search, web_fetch]
37
41
  instructions: false
38
42
  runtimeContext: false
43
+ # 这个模式的默认模型(可选;省略就跟全局 agent-default-model):
44
+ defaultModel: { provider: ollama, model: deepseek-v4.1-flash, reasoningEffort: high }
39
45
  ```
40
46
 
41
- | 字段 | 落到哪 |
42
- | ---------------------- | -------------------------------------------------------------------------------------------- |
43
- | `name` / `description` | 选择器与头部标签的文案(HTTP 清单里给页面) |
44
- | `role` | 归谁用:`main` 进用户选择器,`subagent` 表示可作为子代理的 mode(候选集);缺省 `["main"]` |
45
- | `persona` | 装配前注册到**该 agent 的 scope**(`deployment:persona-prefix` / `-suffix`,遮蔽部署级那层) |
46
- | `allowTools` | `context-assembler-scope` 收口:模型目录、`tool:<名字>` 说明、执行层 guard |
47
- | `instructions` | 同上:`false` 表示这个会话不要任何 instruction 类注入(工作区指令、技能目录、用法正文) |
48
- | `runtimeContext` | 同上:`false` 表示不要动态快照(文件沙箱策略、审批策略) |
47
+ | 字段 | 落到哪 |
48
+ | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
49
+ | `preset` | 挂哪份 agent preset(官方或本部署自建的 `id`):**可以共享**(本部署两个模式共享同一份,差异由会话级收口表达);空串表示不挂 |
50
+ | `name` / `description` | 模式的事实文案(选择面上显示的是官方 roster 的文案) |
51
+ | `role` | 归谁用:`main`(用户选择器)/ `subagent`(可作子代理 mode 的候选);缺省 `["main"]` |
52
+ | `persona` | 装配前注册到**该 agent 的 scope**(`deployment:persona-prefix` / `-suffix`,遮蔽部署级那层) |
53
+ | `allowTools` | `context-assembler-scope` 收口:模型目录、`tool:<名字>` 说明、执行层 guard、常驻用法正文;**preset 没有的工具自动跳过** |
54
+ | `instructions` | 同上:`false` 表示这个会话不要任何 instruction 类注入(工作区指令、技能目录、用法正文) |
55
+ | `runtimeContext` | 同上:`false` 表示不要动态快照(文件沙箱策略、审批策略) |
56
+ | `defaultModel` | 这个模式的默认模型(可选):会话还没有模型事实时接管请求路由,不写会话事件 |
57
+
58
+ 顶层还有 `default`(不在模式里):它是新会话的起始模式;各模式的默认模型在模式自己的 `defaultModel` 里
59
+ (`provider` / `model` / `reasoningEffort?`,不写就跟全局 `agent-default-model`)。这些字段都是 config 的
60
+ **volatile** 字段,`@morlay/dsh-client-ui-primitives` 为这一行(`session-mode`)生成的行配置页编辑的就是它们。
49
61
 
50
- 顶层还有个 `models`(**不在模式里**):键是模式 id,值是那个模式的默认模型——`provider` / `model` /
51
- `reasoningEffort?`,不写就跟全局 `agent-default-model`。它是 config 的 **volatile** 字段,设置页那张卡片
52
- 编辑的就是它;用户在卡片里写的值叠在装配层这份之上(清空 = 回到装配层)。
62
+ 两类字段的生效方式不同:
53
63
 
54
- 模式名与说明是数据、不做语言翻译(`tool/modes.ts` 里只有中文)——取舍如此,不是漂移。
64
+ | 字段 | 改了之后 |
65
+ | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
66
+ | `default` / `modes` | 等 Loader 重挂这一行(settings 写完会重装被改的行);**已运行会话不自动换定义**,重挂后新建的会话或重新应用模式的会话才用新定义(各模式的 `defaultModel` 在 `modes` 里,同一条) |
55
67
 
56
- `models` 为什么在顶层、不在模式里:设置面只编辑 volatile 字段、且只认固定路径(dict 内部一律 blocked)。
57
- 判据与取舍见 [ADR 模式默认模型搬到顶层 volatile](./.agents/adrs/20260925-模式默认模型搬到顶层volatile.md)。
68
+ 模式名与说明是数据、不做语言翻译(`src/mode-sources.ts` 里只有中文)——取舍如此,不是漂移。
58
69
 
59
- **自定义就是改这份 config**:profile 的用户 patch 层可以整体改写 `config.modes`,也可以只给某个模式换提示词或
60
- 白名单——不需要任何插件行。默认模式(`default`)也在这里:它与模式清单是同一个事实的两半。装配期的判据
61
- (默认模式必须在清单里、每个模式至少给一个工具)由 `modes.ts` 的 `configProblem` 兜住,写错直接拒绝装载。
70
+ 默认模型为什么能住在模式里:`modes` 整段 volatile,整棵子树都在设置面的投影里,`defaultModel` 作为它下面的普通
71
+ 字段跟着上页面(自己不必、也不许再标一层 volatile)。判据见
72
+ [ADR 默认模型住在模式定义里](./.agents/adrs/20260925-默认模型住在模式定义里.md)。
73
+
74
+ **自定义就是改这份 config**:profile 的用户 patch 层可以整体改写 `config.modes`,也可以只给某个模式换提示词、
75
+ 白名单或它挂的 preset——不需要任何插件行。装配期的判据(默认模式必须在清单里、每个模式至少给一个工具、每个
76
+ 模式的 `defaultModel` 要给全)由 `modes.ts` 的 `configProblem` 兜住,写错直接拒绝装载;`preset` 只要求"非空即
77
+ 声明了挂载",它是否存在由 preset 注册表自己回答。
62
78
 
63
79
  ## 会话与模式
64
80
 
65
81
  - 新会话用 `config.default`;`agent/created` 时把模式应用到该 agent(persona + 收口),早于它的第一次装配。
66
- - 会话可以**在空白窗口里**换模式:`select` 记一条 session 事件(`session-mode/selected`)并立刻重新应用。
67
- 跑过 turn 之后拒绝——那段历史是在旧模式的工具与提示词下产生的,改了它,日志与实际装配就对不上。
68
- - 当前模式读的是 session 投影 `sessionMode`(`null` 表示没选过):恢复与 fork 都据此重建,页面也从会话列表
69
- 里直接读到它。
82
+ - **选模式就换 preset**(`select`):目标 preset 与当前挂着的不同时,先把它换掉(行清单与模式一起换),再写一条
83
+ `session-mode/selected` 并立刻按新模式重新应用;**相同时不换**——本部署两个模式共享同一份 preset,切 chip 因此
84
+ 不再重挂行清单。官方 preset 的选择(空白窗口里直接选一个 shipped preset)在这条路上只于**映射唯一**时才反查
85
+ 到模式,其余时候模式由会话事实决定。换 preset 只在**空白窗口**成立——跑过 turn 的那段历史是在旧工具集与提示词
86
+ 下产生的,换了 preset,日志与实际装配就对不上。取舍见
87
+ [ADR 选模式就换 preset](./.agents/adrs/20260928-选模式就换preset.md)。
88
+ - 当前模式读的是 session 投影 `sessionMode`:恢复与 fork 都据此重建(官方的 `agentPreset` 说的是"挂了哪套行",
89
+ 两者不是同一个事实)。
70
90
  - **子代理继承父的模式**:子代理是新会话,创建时(`agent/created`)取父当前模式并写进子会话日志——继承也是
71
91
  一条会话事实,恢复与 fork 照样能重建。父不在场时回落部署默认。继承**不看 `role`**:`subagent` 角色声明的
72
92
  是"可被指定"的候选,不是继承白名单。
73
93
 
74
94
  ## 角色与默认模型
75
95
 
76
- - **角色**:`main` = 用户侧可选(选择器与 `select` 只认它);`subagent` = 可作为子代理 mode 的候选。
96
+ - **角色**:`main` = 用户侧可选(官方 roster 的清单按它列);`subagent` = 可作为子代理 mode 的候选。
77
97
  「按角色指派 mode」还没做——子代理现在只有"继承父"与预留的服务接缝
78
98
  (`ctx.sessionModes.applyTo(agent, mode)` / `modesFor("subagent")`)。
79
- - **默认模型**:`config.models[<模式 id>]` 只在会话**尚无模型事实**时接管请求路由(投影 `modelSelection`
80
- 没有 `pending`、`requestHeader()` 还没落);一旦用户选过模型或会话跑过请求,就不再插手。它是**配置事实**,
81
- 不写会话事件——重启后仍由 config 决定,与用户在设置里做的那条会话级选择(`model/selection`)是两件事。
82
- 读的是 volatile **引用**(`config.models.get()`):设置页保存只换引用里的值,这行不重挂。
83
-
84
- ## 页面上的两个位置
85
-
86
- | 槽位 | 呈现 |
87
- | ------------------------------------- | ------------------------------------------- |
88
- | `conversation.hero.agentPreset` | 新会话屏幕的顶部占位:chip 点开就是切换列表 |
89
- | `conversation.session.header.actions` | 会话头部的只读模式标签 |
99
+ - **默认模型**:`config.modes[<模式 id>].defaultModel` 只在会话**尚无模型事实**时接管请求路由(投影
100
+ `modelSelection` 没有 `pending`、`requestHeader()` 还没落);一旦用户选过模型或会话跑过请求,就不再插手。
101
+ 它是**配置事实**,不写会话事件——重启后仍由 config 决定,与用户在设置里做的那条会话级选择
102
+ (`model/selection`)是两件事。读的是构造时那份模式清单快照:设置页保存会让这一行重挂,新定义随重挂生效。
90
103
 
91
- 这两块都由本包的 `./client` 出口提供(与 host 半同包、同一次构建);清单与切换走 HTTP 路由
92
- `GET/POST /session-mode`,当前值走上面那条投影。
104
+ ## 页面上的选择面
93
105
 
94
- 设置页还有一个面:**本行**的配置入口 `plugins.row.config`(key `@morlay/dsh-session-mode#session-mode`),每个模式一行
95
- 「默认模型」——provider / model / 思考档位,外加"恢复默认"。它编辑的是 `config.models`(配置事实),不是
96
- 模式清单(装配数据)。两条读取从外面注入:清单走 `GET /session-mode`,模型目录走
97
- `ctx.remote.session.modelCatalog()`。保存是 staged 的一次 `mutate`:
106
+ 选择面是**本包的 client 半**:一个模式 chip 挂在 composer 工具行左侧(`conversation.input.left`,list + session
107
+ scope),点开就是清单,清单与切换走 HTTP 路由 `GET/POST /session-mode`。它有两个形态:**空白期是选择器**;
108
+ 会话开过 turn 之后**只读**(只写当前模式,点不动、没有下拉面,悬停说明"换模式请新开一个会话")——判据是 host 的
109
+ 投影 `sessionModeEditable`,与服务端拒绝切换读的是同一份事实,所以不会出现"看起来能选、点了报错"。
98
110
 
99
- | 手势 | 写 |
100
- | ---------------- | --------------------------------------------------------------- |
101
- | 给某个模式选模型 | `{ op: 'set', path: ['models', <模式 id>], value: {…} }` |
102
- | 恢复默认 | `{ op: 'unset', path: ['models', <模式 id>] }`(回到装配层那份) |
111
+ 挂 composer 而不是会话头部:一个会话的模式只在它跑第一轮**之前**能改(host 会拒绝给已在跑的会话换模式),而
112
+ 新会话屏也是一个 blank session 的 composer——头部槽位在那里根本不存在。头部的标签**已去掉**:chip 本来就把
113
+ 当前模式写在脸上,右上角再写一遍是同一句话的复读。
103
114
 
104
- 官方 `@deepseek-ai/dsh-client-ui-agent-preset` 带来的第三个面(设置页那份 roster 面板)不做——模式清单是装配
105
- 配置,改它不需要页面。
115
+ 官方 `ui-agent-preset` 那套**保留**(它的 roster 座位在 `conversation.hero.agentPreset`,是单注册槽位)——两套入口
116
+ 并存:官方管"挂哪套行"的选择面,我们管"会话级扩展"的选择面。`modeForPreset` 只在 preset → 模式的映射**唯一**
117
+ 时回答(本部署两个模式共享一份 preset,所以它对本部署的 preset 返回 `undefined`:模式由会话事实决定,preset
118
+ 选了什么不改变这个会话是哪个模式)。
106
119
 
107
120
  ## 文档
108
121
 
109
122
  - 设计与取舍:[设计 会话模式](./.agents/designs/20260924-会话模式.md)、
110
- [ADR 模式默认模型搬到顶层 volatile](./.agents/adrs/20260925-模式默认模型搬到顶层volatile.md)、
111
- [ADR 模式的角色与默认模型](./.agents/adrs/20260923-模式角色与默认模型.md)、
112
- [ADR 模式不再是 Cordis 子树](./.agents/adrs/20260924-模式不再是cordis子树.md)
123
+ [ADR 模式是 preset 的会话级扩展](./.agents/adrs/20260928-模式是preset的会话级扩展.md)、
124
+ [ADR 默认模型住在模式定义里](./.agents/adrs/20260925-默认模型住在模式定义里.md)、
125
+ [ADR 模式的角色与默认模型](./.agents/adrs/20260923-模式角色与默认模型.md)
113
126
  - 验证判据:[本包规范 how-to-verify](./.agents/standards/how-to-verify.md)
114
127
  - 形态沿革(已作废):[ADR preset 改用上游声明式行](./.agents/adrs/20260922-preset改用上游声明式行.md)、
115
- [ADR 通道与注入行按模式 isolate 装配](./.agents/adrs/20260922-通道与注入行按模式isolate装配.md)、
128
+ [ADR 通道与注入行按模式isolate装配](./.agents/adrs/20260922-通道与注入行按模式isolate装配.md)、
116
129
  [设计 预设生成与装配](./.agents/designs/20260917-预设生成与装配.md)