@dsh-plus/subagent-model 0.1.10 → 0.2.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/lib/index.d.ts CHANGED
@@ -28,7 +28,6 @@ declare const Config: z<SubagentModelConfigInput, SubagentModelConfig>;
28
28
  interface InjectedOptions {
29
29
  provider?: string;
30
30
  model?: string;
31
- /** 私有标记:随 agentOptions 抵达子代理 options,由 agent/request 监听器消费。 */
32
31
  reasoningEffort?: string;
33
32
  }
34
33
  //#endregion
@@ -44,13 +43,11 @@ declare class SubagentModelService extends Service {
44
43
  constructor(ctx: Context, config: SubagentModelConfig);
45
44
  /** 当前生效配置(settings 用户层解析结果或 cordis 行级 config)。 */
46
45
  currentConfig(): SubagentModelConfig;
47
- /** 已注册的子代理 provider 名(配置卡片据此合成行)。 */
48
- subagentProviders(): string[];
49
46
  }
50
47
  //#endregion
51
48
  //#region src/index.d.ts
52
49
  declare const name = "dsh-plus-subagent-model";
53
- declare const inject: readonly ["subagents", "llm"];
50
+ declare const inject: readonly ["subagents"];
54
51
  declare function apply(ctx: Context, config: SubagentModelConfig): void;
55
52
  //#endregion
56
53
  export { Config, type EntryConfig, type InjectedOptions, type SubagentModelConfig, SubagentModelService, apply, inject, name };
package/lib/index.js CHANGED
@@ -1,33 +1,37 @@
1
- import { installSettingsSection, settingsNamespace } from "@deepseek-ai/dsh-settings";
2
1
  import z from "@deepseek-ai/schemastery";
3
2
  import { Context, Service } from "@deepseek-ai/cordis";
4
3
  //#endregion
5
4
  //#region src/config.ts
6
5
  /**
7
- * 配置单一事实源:cordis 行级 Config(组合默认值,dev/prod patch 层可覆盖)
8
- * 与 settings namespace(用户层,经 dsh-settings-file 持久化到 $DSH_HOME/settings.yaml)
9
- * 共用同一 schemastery schema。条目按【子代理 provider 名】键控
10
- * (standard preset 事实映射:subagent↔spawn、subagent_fork↔fork)。
6
+ * 配置单一事实源:cordis 行级 Config(组合默认值)与 settings namespace
7
+ * (用户层,经 dsh-settings-file 持久化到 $DSH_HOME/settings.yaml)共用同一
8
+ * schemastery schema。条目按【子代理 provider 名】键控(standard preset
9
+ * 事实映射:subagent↔spawn、subagent_fork↔fork),特殊键 `default` 作为
10
+ * 未命中具体 provider 时的兜底(spawn/fork 共享同一路由的便捷写法)。
11
11
  *
12
- * 运行时语义(详见 delegation.ts):
13
- * - provider/model 显式时注入子代理 `agentOptions`(覆盖"继承主代理"快照);
14
- * - reasoningEffort 显式时经私有字段随 agentOptions 抵达子代理,再由
15
- * `agent/request` 瀑布监听器应用(dsh 原生 AgentOptions 不含 effort 字段)。
12
+ * 运行时语义(详见 delegation.ts):provider/model/reasoningEffort 显式时
13
+ * 注入子代理 `request.agentOptions`——当前平台(0.1.2-rc.1)的
14
+ * resolveChildAgentOptions 原生读取 provider/model/reasoningEffort 三个字段
15
+ * (含冷恢复 descriptor),不再需要旧版 effort 瀑布的私有字段搬运。
16
16
  *
17
- * 类型说明:z.dict 的推断类型引用 cosmokit 的 Dict,直接导出会触发
18
- * dts 的 TS2742(不可具名引用);按官方 dsh-permission-presets 模式,
19
- * 显式声明输入/输出接口并注解 schema(输出为规范型,输入为可空宽松型)。
20
- * @module subagent-model/config
17
+ * 与官方 subagent-model-selection 的关系:官方机制是"主代理模型可选白名单",
18
+ * 不强制任何模型(未显式选择时子代理继承主代理路由);本插件补的是
19
+ * "管理端强制默认路由"——主代理未显式选择时才注入,显式选择仍优先。
20
+ * @module @dsh-plus/subagent-model/config
21
21
  */
22
- /** settings 命名空间;webui 配置卡片与插件运行期读取同一份(字面量见 ./ns.ts)。 */
23
- const SETTINGS_NS = settingsNamespace("dsh-plus-subagent-model");
22
+ /** settings 命名空间(字面量即合法命名空间;webui 配置卡片与插件运行期读取同一份)。 */
23
+ const SETTINGS_NS = "dsh-plus-subagent-model";
24
24
  /** 思考程度哨兵值:继承主代理(不注入 effort 字段)。 */
25
25
  const EFFORT_INHERIT = "inherit";
26
+ /** 思考程度哨兵值:跟随模型默认(显式剥离任何继承/推导出的 effort)。 */
27
+ const EFFORT_DEFAULT = "default";
28
+ /** 兜底条目键:未命中具体 provider 名时生效(spawn/fork 共享同一路由的便捷写法)。 */
29
+ const DEFAULT_ENTRY = "default";
26
30
  const EntrySchema = z.object({
27
31
  enabled: z.boolean().description("该子代理 provider 行的开关").default(false),
28
32
  provider: z.string().description("LLM 提供商 id;留空 = 继承主代理").default(""),
29
33
  model: z.string().description("模型 id;留空 = 继承主代理").default(""),
30
- reasoningEffort: z.string().description(`思考程度:${EFFORT_INHERIT}(继承主代理)/ default(跟随模型默认)/ 提供商目录档位 id`).default(EFFORT_INHERIT)
34
+ reasoningEffort: z.string().description(`思考程度:${EFFORT_INHERIT}(继承主代理)/ ${EFFORT_DEFAULT}(跟随模型默认)/ 提供商目录档位 id`).default(EFFORT_INHERIT)
31
35
  });
32
36
  const Config = z.object({
33
37
  enabled: z.boolean().description("总开关").default(false),
@@ -45,9 +49,18 @@ function resolveEntry(entry) {
45
49
  if (entry.reasoningEffort !== "inherit") out.reasoningEffort = entry.reasoningEffort;
46
50
  return out.provider !== void 0 || out.model !== void 0 || out.reasoningEffort !== void 0 ? out : void 0;
47
51
  }
52
+ /** 按 provider 名解析条目,未命中时回落 default 兜底条目。 */
53
+ function entryFor(current, name) {
54
+ if (name === void 0 || !current().enabled) return void 0;
55
+ const entries = current().entries;
56
+ const direct = entries[name];
57
+ if (direct !== void 0) return resolveEntry(direct);
58
+ const fallback = entries[DEFAULT_ENTRY];
59
+ return fallback === void 0 ? void 0 : resolveEntry(fallback);
60
+ }
48
61
  /**
49
- * 合并注入片段与工具行已有的 agentOptions:部署级显式配置优先,
50
- * 插件只补空缺(standard preset 的工具行无 agentOptions,插件即生效)。
62
+ * 合并注入片段与委托请求已有的 agentOptions:显式(工具行配置 / 主代理
63
+ * 显式选择的路由)优先,插件只补空缺——与官方 agentOptions 合并语义一致。
51
64
  */
52
65
  function mergeAgentOptions(injected, existing) {
53
66
  return {
@@ -55,23 +68,6 @@ function mergeAgentOptions(injected, existing) {
55
68
  ...existing
56
69
  };
57
70
  }
58
- /**
59
- * 把条目的 effort 决策应用到一次 LLM 请求配置上(纯函数):
60
- * - undefined:直通(未配置 effort,保持原生推导);
61
- * - EFFORT_DEFAULT:剥离 reasoningEffort(跟随模型默认,覆盖 fork 的 seed 继承);
62
- * - 其余:显式覆盖。
63
- */
64
- function applyEffort(resolved, effort) {
65
- if (effort === void 0) return resolved;
66
- if (effort === "default") {
67
- const { reasoningEffort: _dropped, ...rest } = resolved;
68
- return rest;
69
- }
70
- return {
71
- ...resolved,
72
- reasoningEffort: effort
73
- };
74
- }
75
71
  /** 校验一条用户提交的条目;返回错误消息,合法时返回 null。 */
76
72
  function validateEntry(entry) {
77
73
  const model = entry.model ?? "";
@@ -90,119 +86,12 @@ function validateEntries(entries) {
90
86
  return null;
91
87
  }
92
88
  //#endregion
93
- //#region src/catalog.ts
94
- function toEffort(effort) {
95
- return {
96
- id: String(effort.id),
97
- name: effort.name,
98
- ...effort.description === void 0 ? {} : { description: effort.description }
99
- };
100
- }
101
- /**
102
- * 构建目录。单个提供商失败只进入 failures,不拖垮其余组;
103
- * 无模型的组被丢弃(与官方 buildModelCatalog 同语义)。
104
- */
105
- async function buildModelCatalog(ctx) {
106
- const llm = ctx.get("llm");
107
- if (llm === void 0) return {
108
- providers: [],
109
- failures: [],
110
- subagentProviders: subagentProvidersOf(ctx)
111
- };
112
- const catalog = await Promise.all(llm.listProviders().map(async (provider) => {
113
- try {
114
- const models = await llm.listModels(provider.id);
115
- const entries = await Promise.all(models.map(async (model) => {
116
- const resolved = await llm.resolveModelInfo(provider.id, model.id);
117
- const reasoning = resolved.reasoning === void 0 ? void 0 : {
118
- efforts: resolved.reasoning.efforts.map(toEffort),
119
- ...resolved.reasoning.defaultEffort === void 0 ? {} : { defaultEffort: String(resolved.reasoning.defaultEffort) }
120
- };
121
- return {
122
- id: model.id,
123
- name: model.name,
124
- ...model.description === void 0 ? {} : { description: model.description },
125
- ...reasoning === void 0 ? {} : { reasoning }
126
- };
127
- }));
128
- return {
129
- kind: "group",
130
- group: {
131
- id: provider.id,
132
- name: provider.name,
133
- models: entries
134
- }
135
- };
136
- } catch (error) {
137
- return {
138
- kind: "failure",
139
- failure: {
140
- id: provider.id,
141
- name: provider.name,
142
- message: error instanceof Error ? error.message : String(error)
143
- }
144
- };
145
- }
146
- }));
147
- return {
148
- providers: catalog.filter((item) => item.kind === "group" && item.group.models.length > 0).map((item) => item.kind === "group" ? item.group : void 0).filter((group) => group !== void 0),
149
- failures: catalog.filter((item) => item.kind === "failure").map((item) => item.kind === "failure" ? item.failure : void 0).filter((failure) => failure !== void 0),
150
- subagentProviders: subagentProvidersOf(ctx)
151
- };
152
- }
153
- /** 已注册的子代理 provider 名(llm 服务缺失时同样返回,卡片仍可渲染)。 */
154
- function subagentProvidersOf(ctx) {
155
- const subagents = ctx.get("subagents");
156
- return subagents === void 0 ? [] : [...subagents.list()];
157
- }
158
- //#endregion
159
- //#region src/config-api.ts
160
- const ROUTE_CATALOG = "/dsh-plus/subagent-model/catalog";
161
- function sendJson(res, status, body) {
162
- res.writeHead(status, { "content-type": "application/json; charset=utf-8" });
163
- res.end(JSON.stringify(body));
164
- }
165
- async function handleCatalog(ctx, res) {
166
- sendJson(res, 200, await buildModelCatalog(ctx));
167
- }
168
- /** 注册目录路由;返回是否注册成功(webServer 缺失时由调用方保证不调用)。 */
169
- function registerCatalogApi(ctx) {
170
- const logger = ctx.logger("subagent-model");
171
- const guard = (handler) => {
172
- return async (req, res) => {
173
- try {
174
- await handler(req, res);
175
- } catch (error) {
176
- const message = error instanceof Error ? error.message : String(error);
177
- logger.warn(`catalog api ${req.method ?? "?"} ${req.url ?? "?"} failed: ${message}`);
178
- if (!res.headersSent) sendJson(res, 400, { error: message });
179
- else res.end();
180
- }
181
- };
182
- };
183
- ctx.webServer.register({
184
- kind: "exact",
185
- path: ROUTE_CATALOG,
186
- handler: guard(async (req, res) => {
187
- if (req.method !== "GET") {
188
- sendJson(res, 405, { error: "method not allowed" });
189
- return;
190
- }
191
- await handleCatalog(ctx, res);
192
- })
193
- });
194
- }
195
- //#endregion
196
89
  //#region src/delegation.ts
197
90
  const WRAPPED = Symbol("dsh-plus-subagent-model.wrapped");
198
- function entryFor(current, name) {
199
- if (name === void 0) return void 0;
200
- const entry = current().entries[name];
201
- return entry === void 0 ? void 0 : resolveEntry(entry);
202
- }
203
91
  /**
204
- * 包装 `ctx.subagents.start` / `startContinuable`:按 provider 名命中配置条目,
205
- * 向委托请求注入 agentOptions(显式工具行配置优先,插件只补空缺)。
92
+ * 包装 `ctx.subagents.start` / `startContinuable`:按 provider 名命中配置条目
93
+ * (未命中回落 default 条目),向委托请求注入 agentOptions——调用方显式携带
94
+ * 的 agentOptions(工具行配置 / 主代理显式选择)优先,插件只补空缺。
206
95
  * 未命中条目时完全直通(原生继承主代理行为)。
207
96
  * 返回注销函数(恢复原方法),随调用方 fiber 释放。
208
97
  */
@@ -245,77 +134,46 @@ function installDelegationHook(ctx, current) {
245
134
  delete marked[WRAPPED];
246
135
  }, "subagent-model: delegation hook");
247
136
  }
248
- /**
249
- * 读取一次 LLM 请求的发起 agent 上本插件携带的 effort 私有标记:
250
- * 仅对子代理(session header origin==='subagent')生效,主代理不受影响。
251
- * 纯函数(agent 为最小结构面),便于单测。
252
- */
253
- function childEffortOf(agent) {
254
- if (agent.session.header.origin !== "subagent") return void 0;
255
- return agent.options.reasoningEffort;
256
- }
257
- /**
258
- * 根 ctx 监听 `agent/request` 瀑布:对子代理应用条目配置的思考程度。
259
- * provider/model 无需在此处理(路由直接读 options)。
260
- * 根级监听覆盖 HMR 重载后已存在子代理的后续请求。
261
- */
262
- function installChildRoute(ctx) {
263
- ctx.on("agent/request", async (payload, next) => {
264
- const resolved = await next();
265
- const effort = childEffortOf(payload.agent);
266
- if (effort === void 0) return resolved;
267
- return applyEffort(resolved, effort);
268
- });
269
- }
270
137
  //#endregion
271
138
  //#region src/service.ts
272
139
  /**
273
- * SubagentModelService:子代理模型配置中枢。
274
- * - 配置:installSettingsSection 接入 settings 用户层($DSH_HOME/settings.yaml,热生效),
140
+ * SubagentModelService:子代理模型配置中枢(host 半)。
141
+ * - 配置:installSection 接入 settings 用户层($DSH_HOME/settings.yaml,热生效),
275
142
  * 无 settings provider 时退化为 cordis 行级 config;写入经 validate 钩子
276
143
  * 走 validateEntries 规则(model 不能脱离 provider 等),非法即拒。
277
- * - 委托挂钩:包装 ctx.subagents.start/startContinuable,按 provider 名注入
278
- * agentOptions(provider/model/effort 私有标记),未命中条目完全直通。
279
- * - 子代理路由:根 ctx 的 agent/request 瀑布监听,把条目 effort 应用到子代理请求。
280
- * - 自定义端点:ctx.webServer 仅注册模型目录路由(配置读写已走官方 settings RPC)。
144
+ * - 委托挂钩:包装 ctx.subagents.start/startContinuable,按 provider 名
145
+ * (未命中回落 default 条目)注入 agentOptions(provider/model/effort)。
281
146
  * @module @dsh-plus/subagent-model
282
147
  */
283
148
  var SubagentModelService = class extends Service {
284
- static [Context.inject] = ["subagents", "llm"];
149
+ static [Context.inject] = ["subagents"];
285
150
  current;
286
151
  constructor(ctx, config) {
287
152
  super(ctx, "subagentModel");
288
153
  this.current = () => config;
289
- installSettingsSection(ctx, SETTINGS_NS, Config, config, {
290
- validate: (cfg) => {
291
- const error = validateEntries(cfg.entries);
292
- if (error !== null) throw new Error(`subagent-model: ${error}`);
293
- },
294
- setSource: (source) => {
295
- this.current = source;
296
- },
297
- onChange: () => {}
154
+ ctx.inject(["settings"], (settingsCtx) => {
155
+ settingsCtx.settings.installSection(ctx, SETTINGS_NS, Config, config, {
156
+ validate: (cfg) => {
157
+ const error = validateEntries(cfg.entries);
158
+ if (error !== null) throw new Error(`subagent-model: ${error}`);
159
+ },
160
+ setSource: (source) => {
161
+ this.current = source;
162
+ },
163
+ onChange: () => {}
164
+ });
298
165
  });
299
166
  installDelegationHook(ctx, () => this.current());
300
- installChildRoute(ctx);
301
- ctx.inject(["webServer"], (webCtx) => {
302
- registerCatalogApi(webCtx);
303
- });
304
167
  }
305
168
  /** 当前生效配置(settings 用户层解析结果或 cordis 行级 config)。 */
306
169
  currentConfig() {
307
170
  return this.current();
308
171
  }
309
- /** 已注册的子代理 provider 名(配置卡片据此合成行)。 */
310
- subagentProviders() {
311
- const subagents = this.ctx.get("subagents");
312
- return subagents === void 0 ? [] : [...subagents.list()];
313
- }
314
172
  };
315
173
  //#endregion
316
174
  //#region src/index.ts
317
175
  const name = "dsh-plus-subagent-model";
318
- const inject = ["subagents", "llm"];
176
+ const inject = ["subagents"];
319
177
  function apply(ctx, config) {
320
178
  ctx.plugin(SubagentModelService, config);
321
179
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@dsh-plus/subagent-model",
3
- "version": "0.1.10",
4
- "description": "dsh-plus service+ui plugin: 为 subagent/subagent_fork 等子代理按提供商配置模型与思考程度(或继承主代理)",
3
+ "version": "0.2.1",
4
+ "description": "dsh-plus service plugin: 为 subagent/subagent_fork 等子代理按 provider 配置模型与思考程度(或继承主代理)",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/index.d.ts",
@@ -10,9 +10,6 @@
10
10
  "types": "./lib/index.d.ts",
11
11
  "default": "./lib/index.js"
12
12
  },
13
- "./client": {
14
- "default": "./lib/client.js"
15
- },
16
13
  "./src/*": "./src/*",
17
14
  "./package.json": "./package.json"
18
15
  },
@@ -20,37 +17,17 @@
20
17
  "lib",
21
18
  "src"
22
19
  ],
23
- "dsh": {
24
- "client": {
25
- "inject": [
26
- "@deepseek-ai/dsh-client-runtime",
27
- "@deepseek-ai/dsh-client-locale",
28
- "@deepseek-ai/dsh-client-connection",
29
- "@deepseek-ai/dsh-api-remotes"
30
- ],
31
- "platform": "web"
32
- }
33
- },
34
- "dependencies": {
35
- "@dsh-plus/shared": "0.1.5"
36
- },
37
20
  "peerDependencies": {
38
21
  "@deepseek-ai/cordis": "^4.0.1",
39
- "@deepseek-ai/dsh-settings": "^0.1.0-rc.8",
22
+ "@deepseek-ai/dsh-settings": "^0.1.3-alpha.2",
23
+ "@deepseek-ai/dsh-subagent": "^0.1.3-alpha.2",
40
24
  "@deepseek-ai/schemastery": "^3.18.1"
41
25
  },
42
26
  "devDependencies": {
43
- "@deepseek-ai/cordis": "4.0.1",
44
- "@deepseek-ai/cosmokit": "1.8.2",
45
- "@deepseek-ai/dsh-agent": "0.1.1-rc.2",
46
- "@deepseek-ai/dsh-host-webserver": "0.1.1-rc.2",
47
- "@deepseek-ai/dsh-llm": "0.1.1-rc.2",
48
- "@deepseek-ai/dsh-session": "0.1.1-rc.2",
49
- "@deepseek-ai/dsh-settings": "0.1.1-rc.2",
50
- "@deepseek-ai/dsh-subagent": "0.1.1-rc.2",
51
- "@deepseek-ai/schemastery": "3.18.1",
52
- "@types/react": "~18.3.1",
53
- "react": "^18.2.0"
27
+ "@deepseek-ai/cordis": "4.0.2",
28
+ "@deepseek-ai/dsh-settings": "0.1.3-alpha.2",
29
+ "@deepseek-ai/dsh-subagent": "0.1.3-alpha.2",
30
+ "@deepseek-ai/schemastery": "3.18.2"
54
31
  },
55
32
  "publishConfig": {
56
33
  "access": "public"
package/src/config.ts CHANGED
@@ -1,32 +1,34 @@
1
1
  /**
2
- * 配置单一事实源:cordis 行级 Config(组合默认值,dev/prod patch 层可覆盖)
3
- * 与 settings namespace(用户层,经 dsh-settings-file 持久化到 $DSH_HOME/settings.yaml)
4
- * 共用同一 schemastery schema。条目按【子代理 provider 名】键控
5
- * (standard preset 事实映射:subagent↔spawn、subagent_fork↔fork)。
2
+ * 配置单一事实源:cordis 行级 Config(组合默认值)与 settings namespace
3
+ * (用户层,经 dsh-settings-file 持久化到 $DSH_HOME/settings.yaml)共用同一
4
+ * schemastery schema。条目按【子代理 provider 名】键控(standard preset
5
+ * 事实映射:subagent↔spawn、subagent_fork↔fork),特殊键 `default` 作为
6
+ * 未命中具体 provider 时的兜底(spawn/fork 共享同一路由的便捷写法)。
6
7
  *
7
- * 运行时语义(详见 delegation.ts):
8
- * - provider/model 显式时注入子代理 `agentOptions`(覆盖"继承主代理"快照);
9
- * - reasoningEffort 显式时经私有字段随 agentOptions 抵达子代理,再由
10
- * `agent/request` 瀑布监听器应用(dsh 原生 AgentOptions 不含 effort 字段)。
8
+ * 运行时语义(详见 delegation.ts):provider/model/reasoningEffort 显式时
9
+ * 注入子代理 `request.agentOptions`——当前平台(0.1.2-rc.1)的
10
+ * resolveChildAgentOptions 原生读取 provider/model/reasoningEffort 三个字段
11
+ * (含冷恢复 descriptor),不再需要旧版 effort 瀑布的私有字段搬运。
11
12
  *
12
- * 类型说明:z.dict 的推断类型引用 cosmokit 的 Dict,直接导出会触发
13
- * dts 的 TS2742(不可具名引用);按官方 dsh-permission-presets 模式,
14
- * 显式声明输入/输出接口并注解 schema(输出为规范型,输入为可空宽松型)。
15
- * @module subagent-model/config
13
+ * 与官方 subagent-model-selection 的关系:官方机制是"主代理模型可选白名单",
14
+ * 不强制任何模型(未显式选择时子代理继承主代理路由);本插件补的是
15
+ * "管理端强制默认路由"——主代理未显式选择时才注入,显式选择仍优先。
16
+ * @module @dsh-plus/subagent-model/config
16
17
  */
17
18
 
18
- import { settingsNamespace } from '@deepseek-ai/dsh-settings'
19
19
  import z from '@deepseek-ai/schemastery'
20
20
 
21
21
  import { SETTINGS_NS as NS_LITERAL } from './ns.ts'
22
22
 
23
- /** settings 命名空间;webui 配置卡片与插件运行期读取同一份(字面量见 ./ns.ts)。 */
24
- export const SETTINGS_NS = settingsNamespace(NS_LITERAL)
23
+ /** settings 命名空间(字面量即合法命名空间;webui 配置卡片与插件运行期读取同一份)。 */
24
+ export const SETTINGS_NS = NS_LITERAL
25
25
 
26
26
  /** 思考程度哨兵值:继承主代理(不注入 effort 字段)。 */
27
27
  export const EFFORT_INHERIT = 'inherit'
28
28
  /** 思考程度哨兵值:跟随模型默认(显式剥离任何继承/推导出的 effort)。 */
29
29
  export const EFFORT_DEFAULT = 'default'
30
+ /** 兜底条目键:未命中具体 provider 名时生效(spawn/fork 共享同一路由的便捷写法)。 */
31
+ export const DEFAULT_ENTRY = 'default'
30
32
 
31
33
  /** 一条条目的规范输出形态。 */
32
34
  export interface EntryConfig {
@@ -77,7 +79,6 @@ export const Config: z<SubagentModelConfigInput, SubagentModelConfig> = z.object
77
79
  export interface InjectedOptions {
78
80
  provider?: string
79
81
  model?: string
80
- /** 私有标记:随 agentOptions 抵达子代理 options,由 agent/request 监听器消费。 */
81
82
  reasoningEffort?: string
82
83
  }
83
84
 
@@ -96,9 +97,22 @@ export function resolveEntry(entry: EntryConfig): InjectedOptions | undefined {
96
97
  : undefined
97
98
  }
98
99
 
100
+ /** 按 provider 名解析条目,未命中时回落 default 兜底条目。 */
101
+ export function entryFor(
102
+ current: () => SubagentModelConfig,
103
+ name: string | undefined,
104
+ ): InjectedOptions | undefined {
105
+ if (name === undefined || !current().enabled) return undefined
106
+ const entries = current().entries
107
+ const direct = entries[name]
108
+ if (direct !== undefined) return resolveEntry(direct)
109
+ const fallback = entries[DEFAULT_ENTRY]
110
+ return fallback === undefined ? undefined : resolveEntry(fallback)
111
+ }
112
+
99
113
  /**
100
- * 合并注入片段与工具行已有的 agentOptions:部署级显式配置优先,
101
- * 插件只补空缺(standard preset 的工具行无 agentOptions,插件即生效)。
114
+ * 合并注入片段与委托请求已有的 agentOptions:显式(工具行配置 / 主代理
115
+ * 显式选择的路由)优先,插件只补空缺——与官方 agentOptions 合并语义一致。
102
116
  */
103
117
  export function mergeAgentOptions(
104
118
  injected: InjectedOptions,
@@ -107,30 +121,7 @@ export function mergeAgentOptions(
107
121
  return { ...injected, ...existing }
108
122
  }
109
123
 
110
- /** 请求配置的形状(与 dsh agent/request 瀑布产物的相关字段)。 */
111
- export interface RequestConfig {
112
- provider: string
113
- model: string
114
- reasoningEffort?: string
115
- maxTokens?: number
116
- }
117
-
118
- /**
119
- * 把条目的 effort 决策应用到一次 LLM 请求配置上(纯函数):
120
- * - undefined:直通(未配置 effort,保持原生推导);
121
- * - EFFORT_DEFAULT:剥离 reasoningEffort(跟随模型默认,覆盖 fork 的 seed 继承);
122
- * - 其余:显式覆盖。
123
- */
124
- export function applyEffort<T extends RequestConfig>(resolved: T, effort: string | undefined): T {
125
- if (effort === undefined) return resolved
126
- if (effort === EFFORT_DEFAULT) {
127
- const { reasoningEffort: _dropped, ...rest } = resolved
128
- return rest as T
129
- }
130
- return { ...resolved, reasoningEffort: effort } as T
131
- }
132
-
133
- /** 条目校验的输入面(卡片提交的可能为宽松形态)。 */
124
+ /** 条目校验的输入面(settings 写入的 validate 钩子用)。 */
134
125
  export interface EntryValidationInput {
135
126
  provider?: string | null
136
127
  model?: string | null
package/src/delegation.ts CHANGED
@@ -1,53 +1,49 @@
1
1
  /**
2
2
  * 运行时挂钩:子代理模型路由的注入点。
3
3
  *
4
- * 机制依据(官方 dsh-subagent / dsh-agent-loop):
5
- * - 子代理的模型由【创建时】的 AgentOptions 决定:`resolveChildAgentOptions(parent,
6
- * request.agentOptions, depth)` = 父 options 快照 + 显式 agentOptions(后者胜出)。
7
- * 子代理不经 api-proxy 的 create/resume setup,不安装模型 selection,
8
- * `agent/request` 瀑布默认无人覆盖 —— 因此 provider/model 只需注入
9
- * `request.agentOptions`,路由即命中。
10
- * - reasoningEffort 不在 AgentOptions 读取路径上(AgentLoop 只读 provider/model/maxTokens,
11
- * 子代理 buildRequest 的 effort 仅来自自身 session 折叠的 request/header)。
12
- * 故 effort 以私有字段随 agentOptions 抵达子代理 options,再由根 ctx 的
13
- * `agent/request` 瀑布监听器应用(origin==='subagent' 过滤)。
14
- * - 冷恢复(continuable resume):descriptor 持久化 agentProvider/agentModel
15
- * (来自 request.agentOptions),恢复后模型保持;effort 经 session log 的
16
- * request/header 保留 —— 与热路径注入结果一致。
4
+ * 机制依据(官方 dsh-subagent 0.1.2-rc.1):子代理的模型由【创建时】的
5
+ * AgentOptions 决定——`resolveChildAgentOptions(parent, request.agentOptions,
6
+ * childDepth)` = 父 options 快照 + 显式 agentOptions(后者胜出),且原生读取
7
+ * provider/model/reasoningEffort 三个字段(materialize → agents.create/resume
8
+ * → agentLoop.options),冷恢复(continuable resume)经 descriptor 持久化保持。
9
+ * 因此只需在 `request.agentOptions` 注入 provider/model/reasoningEffort,
10
+ * 路由即命中,无需旧版 effort 瀑布的私有字段搬运。
17
11
  *
18
12
  * 两个挂钩均幂等(Symbol 标记 + dispose 恢复),HMR 重载安全。
19
- * @module subagent-model/delegation
13
+ * @module @dsh-plus/subagent-model/delegation
20
14
  */
21
15
  import type { Context } from '@deepseek-ai/cordis'
22
- import type { AgentOptions } from '@deepseek-ai/dsh-agent'
23
- import type { LlmCallConfig } from '@deepseek-ai/dsh-llm'
24
- import type { ContinuableStartSpec, SubagentStartRequest } from '@deepseek-ai/dsh-subagent'
25
16
 
26
17
  import {
27
- applyEffort,
18
+ entryFor,
19
+ type InjectedOptions,
28
20
  mergeAgentOptions,
29
- type RequestConfig,
30
- resolveEntry,
31
21
  type SubagentModelConfig,
32
22
  } from './config.ts'
33
23
 
34
24
  const WRAPPED = Symbol('dsh-plus-subagent-model.wrapped')
35
25
 
36
26
  /** 子代理服务的最小运行期面(仅包装用到的两个入口)。 */
37
- interface SubagentsLike {
27
+ export interface SubagentsLike {
38
28
  start(name: string, request: SubagentStartRequest): Promise<unknown>
39
29
  startContinuable(spec: ContinuableStartSpec): Promise<unknown>
40
30
  }
41
31
 
42
- function entryFor(current: () => SubagentModelConfig, name: string | undefined) {
43
- if (name === undefined) return undefined
44
- const entry = current().entries[name]
45
- return entry === undefined ? undefined : resolveEntry(entry)
32
+ /** 子代理创建请求的窄面(只读 agentOptions)。 */
33
+ export interface SubagentStartRequest {
34
+ agentOptions?: InjectedOptions
35
+ }
36
+
37
+ /** 可继续子代理启动规范的窄面。 */
38
+ export interface ContinuableStartSpec {
39
+ provider: string
40
+ request: SubagentStartRequest
46
41
  }
47
42
 
48
43
  /**
49
- * 包装 `ctx.subagents.start` / `startContinuable`:按 provider 名命中配置条目,
50
- * 向委托请求注入 agentOptions(显式工具行配置优先,插件只补空缺)。
44
+ * 包装 `ctx.subagents.start` / `startContinuable`:按 provider 名命中配置条目
45
+ * (未命中回落 default 条目),向委托请求注入 agentOptions——调用方显式携带
46
+ * 的 agentOptions(工具行配置 / 主代理显式选择)优先,插件只补空缺。
51
47
  * 未命中条目时完全直通(原生继承主代理行为)。
52
48
  * 返回注销函数(恢复原方法),随调用方 fiber 释放。
53
49
  */
@@ -96,30 +92,3 @@ export function installDelegationHook(
96
92
  'subagent-model: delegation hook',
97
93
  )
98
94
  }
99
-
100
- /**
101
- * 读取一次 LLM 请求的发起 agent 上本插件携带的 effort 私有标记:
102
- * 仅对子代理(session header origin==='subagent')生效,主代理不受影响。
103
- * 纯函数(agent 为最小结构面),便于单测。
104
- */
105
- export function childEffortOf(agent: {
106
- session: { header: { origin?: 'subagent' } }
107
- options: AgentOptions & { reasoningEffort?: string }
108
- }): string | undefined {
109
- if (agent.session.header.origin !== 'subagent') return undefined
110
- return agent.options.reasoningEffort
111
- }
112
-
113
- /**
114
- * 根 ctx 监听 `agent/request` 瀑布:对子代理应用条目配置的思考程度。
115
- * provider/model 无需在此处理(路由直接读 options)。
116
- * 根级监听覆盖 HMR 重载后已存在子代理的后续请求。
117
- */
118
- export function installChildRoute(ctx: Context): void {
119
- ctx.on('agent/request', async (payload, next) => {
120
- const resolved = await next()
121
- const effort = childEffortOf(payload.agent)
122
- if (effort === undefined) return resolved
123
- return applyEffort(resolved as RequestConfig, effort) as LlmCallConfig
124
- })
125
- }
package/src/index.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  /**
2
2
  * dsh 插件:子代理独立模型配置(subagent / subagent_fork 等)。
3
3
  * 为子代理按 provider 名配置 LLM 提供商、模型与思考程度(reasoningEffort),
4
- * 或选择从主代理继承;只负责模型选定,其余参数(maxTokens/persona/toolFilter/
5
- * maxDepth 等)保持 dsh 原生行为。配置 UI 位于 webui 设置-插件-插件配置,
6
- * 持久化到 $DSH_HOME/settings.yaml 并热生效。
4
+ * 或选择从主代理继承;`default` 条目可让 spawn/fork 共享同一路由。
5
+ * 主代理显式选择的路由(官方 subagent-model-selection 的 model 字段)优先,
6
+ * 本插件只补"未选择时的强制默认"。
7
+ * 配置经 settings 用户层($DSH_HOME/settings.yaml)持久化并热生效。
7
8
  * @module @dsh-plus/subagent-model
8
9
  */
9
10
  import type { Context } from '@deepseek-ai/cordis'
@@ -13,7 +14,7 @@ import { SubagentModelService } from './service.ts'
13
14
 
14
15
  export const name = 'dsh-plus-subagent-model'
15
16
 
16
- export const inject = ['subagents', 'llm'] as const
17
+ export const inject = ['subagents'] as const
17
18
 
18
19
  export type {
19
20
  EntryConfig,
package/src/ns.ts CHANGED
@@ -1,9 +1,6 @@
1
1
  /**
2
- * settings 命名空间字面量(纯常量,零依赖,浏览器半可安全引入)。
3
- * 服务端经 settingsNamespace() 品牌化后使用(config.ts),浏览器半将其作为
4
- * settings.plugin.item keyed 槽位的 key —— 同一字面量两处共用,防止漂移。
5
- * 契约:dsh rc7 起该槽位按「卡片编辑的 settings 命名空间」作 key 分发,
6
- * 官方配置页只渲染 key 命中 Host 已注册命名空间的卡片。
7
- * @module subagent-model/ns
2
+ * settings 命名空间字面量(纯常量,零依赖)。沿用旧版插件命名空间,
3
+ * 已存在于生产 settings.yaml 的旧配置可直接续用。
4
+ * @module @dsh-plus/subagent-model/ns
8
5
  */
9
6
  export const SETTINGS_NS = 'dsh-plus-subagent-model'