@dsh-plus/subagent-model 0.1.9 → 0.2.0
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 +1 -4
- package/lib/index.js +51 -193
- package/package.json +8 -31
- package/src/config.ts +33 -42
- package/src/delegation.ts +23 -54
- package/src/index.ts +5 -4
- package/src/ns.ts +3 -6
- package/src/service.ts +20 -29
- package/lib/client.js +0 -820
- package/src/catalog.ts +0 -127
- package/src/client/api.ts +0 -45
- package/src/client/card.tsx +0 -340
- package/src/client/client.ts +0 -78
- package/src/client/draft.ts +0 -69
- package/src/client/i18n.ts +0 -65
- package/src/client/styles.ts +0 -30
- package/src/config-api.ts +0 -53
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"
|
|
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
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
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
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
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
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* @module subagent-model/config
|
|
17
|
+
* 与官方 subagent-model-selection 的关系:官方机制是"主代理模型可选白名单",
|
|
18
|
+
* 不强制任何模型(未显式选择时子代理继承主代理路由);本插件补的是
|
|
19
|
+
* "管理端强制默认路由"——主代理未显式选择时才注入,显式选择仍优先。
|
|
20
|
+
* @module @dsh-plus/subagent-model/config
|
|
21
21
|
*/
|
|
22
|
-
/** settings
|
|
23
|
-
const SETTINGS_NS =
|
|
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}(继承主代理)/
|
|
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
|
-
*
|
|
50
|
-
*
|
|
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
|
-
*
|
|
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
|
-
* - 配置:
|
|
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"
|
|
149
|
+
static [Context.inject] = ["subagents"];
|
|
285
150
|
current;
|
|
286
151
|
constructor(ctx, config) {
|
|
287
152
|
super(ctx, "subagentModel");
|
|
288
153
|
this.current = () => config;
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
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"
|
|
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.
|
|
4
|
-
"description": "dsh-plus service
|
|
3
|
+
"version": "0.2.0",
|
|
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.3"
|
|
36
|
-
},
|
|
37
20
|
"peerDependencies": {
|
|
38
21
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
39
|
-
"@deepseek-ai/dsh-settings": "^0.1.
|
|
22
|
+
"@deepseek-ai/dsh-settings": "^0.1.2-alpha.2",
|
|
23
|
+
"@deepseek-ai/dsh-subagent": "^0.1.2-alpha.2",
|
|
40
24
|
"@deepseek-ai/schemastery": "^3.18.1"
|
|
41
25
|
},
|
|
42
26
|
"devDependencies": {
|
|
43
|
-
"@deepseek-ai/cordis": "4.0.
|
|
44
|
-
"@deepseek-ai/
|
|
45
|
-
"@deepseek-ai/dsh-
|
|
46
|
-
"@deepseek-ai/
|
|
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.2-alpha.2",
|
|
29
|
+
"@deepseek-ai/dsh-subagent": "0.1.2-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
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
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
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
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
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
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
|
|
24
|
-
export const SETTINGS_NS =
|
|
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
|
-
*
|
|
101
|
-
*
|
|
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
|
-
/**
|
|
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
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
*
|
|
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
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
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'
|
|
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
|
-
*
|
|
4
|
-
*
|
|
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'
|