dsh-auto-model-routing 0.3.0 → 0.4.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/CHANGELOG.md +11 -0
- package/README.md +56 -25
- package/README.zh.md +56 -25
- package/docs/feedback-evaluation.md +18 -0
- package/docs/host-capability-report.md +91 -0
- package/docs/jev-policy-v2-validation.md +37 -0
- package/docs/magpie-live-acceptance.md +27 -0
- package/docs/policy-v2-development.md +68 -0
- package/docs/policy-v2-real-evaluation-plan.md +32 -0
- package/docs/routing-community-research-2026-10-24.md +98 -0
- package/docs/routing-intent-and-gaps.md +56 -0
- package/docs/routing-strategy-plan.md +101 -0
- package/lib/client/control.d.ts +9 -1
- package/lib/client/control.js +16 -1
- package/lib/client/index.d.ts +2 -0
- package/lib/client/index.js +2 -2
- package/lib/client/label.d.ts +4 -0
- package/lib/client/label.js +24 -8
- package/lib/client/settings-section.js +53 -3
- package/lib/client/settings-ui.d.ts +1 -1
- package/lib/client/strategy-details.d.ts +4 -0
- package/lib/client/strategy-details.js +8 -0
- package/lib/client.js +332 -47
- package/lib/decision-service.d.ts +39 -0
- package/lib/decision-service.js +28 -0
- package/lib/endpoints.d.ts +2 -1
- package/lib/endpoints.js +22 -1
- package/lib/feedback.d.ts +69 -0
- package/lib/feedback.js +65 -0
- package/lib/hints.js +1 -1
- package/lib/index.js +29 -7
- package/lib/jev.d.ts +27 -1
- package/lib/jev.js +51 -23
- package/lib/policy-host.d.ts +68 -0
- package/lib/policy-host.js +274 -0
- package/lib/policy-store.d.ts +96 -0
- package/lib/policy-store.js +60 -0
- package/lib/policy-v2-rubric.json +21 -0
- package/lib/policy-v2.d.ts +20 -0
- package/lib/policy-v2.js +53 -0
- package/lib/settings-contract.d.ts +4 -1
- package/lib/settings-contract.js +1 -1
- package/lib/settings-host.d.ts +2 -2
- package/lib/settings-host.js +26 -14
- package/lib/settings.js +14 -1
- package/lib/subagent-tool.js +32 -5
- package/package.json +3 -3
- package/skills/subagent-model-routing/SKILL.md +15 -0
- package/lib/selection-projection.d.ts +0 -8
- package/lib/selection-projection.js +0 -7
package/lib/settings-host.js
CHANGED
|
@@ -2,18 +2,20 @@ import { credentialRef } from '@deepseek-ai/dsh-credentials';
|
|
|
2
2
|
import { launchEnvironmentOf } from '@deepseek-ai/dsh-launch-environment';
|
|
3
3
|
import { DEFAULT_SETTINGS, SettingsValidationError } from './settings.js';
|
|
4
4
|
import rubric from './rubric.json' with { type: 'json' };
|
|
5
|
-
|
|
5
|
+
import { JevClient } from './jev.js';
|
|
6
|
+
import { DECISION_SERVICES } from './decision-service.js';
|
|
7
|
+
const keyRef = (provider = 'openrouter') => credentialRef(DECISION_SERVICES[provider].credential);
|
|
6
8
|
/** Only the fixed credential reference and public catalog metadata cross this adapter. */
|
|
7
9
|
export function settingsHost(ctx, store) {
|
|
8
10
|
const environment = launchEnvironmentOf(ctx);
|
|
9
11
|
const credentials = () => ctx.get('credentials');
|
|
10
|
-
const catalog = async () => {
|
|
11
|
-
const models = (await Promise.all(ctx.llm.listProviders().filter(p => p.id === 'claude' || p.id === 'codex').map(async (provider) => {
|
|
12
|
+
const catalog = async (includeMagpie = store.read().settings.policy === 'policy-v2') => {
|
|
13
|
+
const models = (await Promise.all(ctx.llm.listProviders().filter(p => p.id === 'claude' || p.id === 'codex' || (includeMagpie && p.id === 'dsh-magpie-connect')).map(async (provider) => {
|
|
12
14
|
try {
|
|
13
15
|
return await Promise.all((await ctx.llm.listModels(provider.id)).map(async (model) => {
|
|
14
16
|
try {
|
|
15
17
|
const info = await ctx.llm.resolveModelInfo(provider.id, model.id);
|
|
16
|
-
return { provider: provider.id, model: model.id, name: model.name, reasoningEfforts: (info.reasoning?.efforts ?? []).map(e => String(e.id)) };
|
|
18
|
+
return { provider: provider.id, model: model.id, name: model.name, reasoningEfforts: (info.reasoning?.efforts ?? []).map(e => String(e.id)), ...(info.reasoning?.defaultEffort ? { defaultEffort: String(info.reasoning.defaultEffort) } : {}) };
|
|
17
19
|
}
|
|
18
20
|
catch {
|
|
19
21
|
return undefined;
|
|
@@ -29,23 +31,24 @@ export function settingsHost(ctx, store) {
|
|
|
29
31
|
try {
|
|
30
32
|
if (service) {
|
|
31
33
|
const current = service.current();
|
|
32
|
-
return { models, childAllowlist: { status: current.enabled ? 'enabled' : 'disabled', allowedModels: current.allowedModels.filter(r => r.provider === 'claude' || r.provider === 'codex') } };
|
|
34
|
+
return { models, childAllowlist: { status: current.enabled ? 'enabled' : 'disabled', allowedModels: current.allowedModels.filter(r => r.provider === 'claude' || r.provider === 'codex' || (includeMagpie && r.provider === 'dsh-magpie-connect')) } };
|
|
33
35
|
}
|
|
34
36
|
}
|
|
35
37
|
catch { /* An unavailable policy must never broaden the allowed routes. */ }
|
|
36
38
|
return { models, childAllowlist: { status: 'unavailable', allowedModels: [] } };
|
|
37
39
|
};
|
|
38
|
-
const keyStatus = async () => {
|
|
40
|
+
const keyStatus = async (provider = store.read().settings.decisionService?.provider ?? 'openrouter') => {
|
|
41
|
+
const ref = keyRef(provider);
|
|
39
42
|
const service = credentials();
|
|
40
43
|
if (service) {
|
|
41
44
|
try {
|
|
42
|
-
return { ...await service.describe(
|
|
45
|
+
return { ...await service.describe(ref), available: true };
|
|
43
46
|
}
|
|
44
47
|
catch {
|
|
45
48
|
return { configured: false, writable: false, available: false };
|
|
46
49
|
}
|
|
47
50
|
}
|
|
48
|
-
return { configured: Boolean(environment.get(
|
|
51
|
+
return { configured: Boolean(environment.get(ref)?.value), writable: false, available: false };
|
|
49
52
|
};
|
|
50
53
|
const read = async () => {
|
|
51
54
|
const current = await catalog();
|
|
@@ -55,6 +58,11 @@ export function settingsHost(ctx, store) {
|
|
|
55
58
|
const endpoint = async (operation, body) => {
|
|
56
59
|
if (operation === 'settings')
|
|
57
60
|
return Response.json(await read());
|
|
61
|
+
if (operation === 'testDecision') {
|
|
62
|
+
const client = new JevClient({ get: async (name) => credentials() ? credentials().resolve(credentialRef(name)) : environment.get(name) }, undefined, 4500, () => store.read().settings.decisionService);
|
|
63
|
+
const result = await client.judgePolicy('Public connection test: sort the numbers [3,1,2] in ascending order.', new AbortController().signal, 'child');
|
|
64
|
+
return Response.json(result);
|
|
65
|
+
}
|
|
58
66
|
if (operation === 'saveSettings') {
|
|
59
67
|
try {
|
|
60
68
|
store.save(body, await catalog());
|
|
@@ -69,19 +77,23 @@ export function settingsHost(ctx, store) {
|
|
|
69
77
|
const service = credentials();
|
|
70
78
|
if (!service)
|
|
71
79
|
return Response.json({ error: '凭据服务不可用' }, { status: 503 });
|
|
72
|
-
if (typeof body !== 'object' || body === null || Array.isArray(body) || Object.keys(body).
|
|
80
|
+
if (typeof body !== 'object' || body === null || Array.isArray(body) || Object.keys(body).some(k => !['value', 'provider'].includes(k)) || !('value' in body))
|
|
73
81
|
return Response.json({ error: '无效的凭据请求' }, { status: 400 });
|
|
82
|
+
const provider = 'provider' in body ? body.provider : 'openrouter';
|
|
83
|
+
if (typeof provider !== 'string' || !Object.hasOwn(DECISION_SERVICES, provider))
|
|
84
|
+
return Response.json({ error: '无效的服务商' }, { status: 400 });
|
|
85
|
+
const ref = keyRef(provider);
|
|
74
86
|
const value = body.value;
|
|
75
87
|
if (value !== null && (typeof value !== 'string' || !value || value.length > 512 || /\s/.test(value)))
|
|
76
88
|
return Response.json({ error: 'API key 必须为不含空白的非空字符串,且不超过 512 字符' }, { status: 400 });
|
|
77
89
|
try {
|
|
78
|
-
if (!(await service.describe(
|
|
90
|
+
if (!(await service.describe(ref)).writable)
|
|
79
91
|
return Response.json({ error: '该 key 来自启动环境,需先从启动环境移除并重启才能在此管理' }, { status: 409 });
|
|
80
92
|
if (value === null)
|
|
81
|
-
await service.unset(
|
|
93
|
+
await service.unset(ref);
|
|
82
94
|
else
|
|
83
|
-
await service.set(
|
|
84
|
-
return Response.json({ key: await keyStatus() });
|
|
95
|
+
await service.set(ref, value);
|
|
96
|
+
return Response.json({ key: await keyStatus(provider) });
|
|
85
97
|
}
|
|
86
98
|
catch {
|
|
87
99
|
return Response.json({ error: '无法更新 API key;请检查凭据服务与只读来源' }, { status: 500 });
|
|
@@ -91,7 +103,7 @@ export function settingsHost(ctx, store) {
|
|
|
91
103
|
catalog, read, endpoint,
|
|
92
104
|
credential: async (name) => {
|
|
93
105
|
const service = credentials();
|
|
94
|
-
return service ? service.resolve(
|
|
106
|
+
return service ? service.resolve(credentialRef(name)) : environment.get(name);
|
|
95
107
|
},
|
|
96
108
|
};
|
|
97
109
|
}
|
package/lib/settings.js
CHANGED
|
@@ -2,11 +2,20 @@ import { chmodSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSy
|
|
|
2
2
|
import { dirname } from 'node:path';
|
|
3
3
|
import { randomUUID } from 'node:crypto';
|
|
4
4
|
import { z } from 'zod';
|
|
5
|
+
import { resolveDecisionService } from './decision-service.js';
|
|
5
6
|
import { tiers } from './effort.js';
|
|
6
7
|
import { AUTO_EFFORTS, DEFAULT_SETTINGS } from './settings-contract.js';
|
|
7
8
|
import { isRecord, issue, MODEL_ID_PATTERN, validateChildRoute, validateFamilyMainEffort, validateMainRoute } from './settings-validation.js';
|
|
8
9
|
export { DEFAULT_SETTINGS } from './settings-contract.js';
|
|
9
10
|
const ordinary = {
|
|
11
|
+
decisionService: z.object({ provider: z.enum(['openrouter', 'typesafe', 'concentrate', 'custom']), model: z.string().regex(/^[A-Za-z0-9._:/~-]{1,128}$/).optional(), endpoint: z.string().max(2048).optional() }).strict().refine(value => { try {
|
|
12
|
+
resolveDecisionService(value);
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return false;
|
|
17
|
+
} }, 'Invalid decision service'),
|
|
18
|
+
policy: z.enum(['legacy', 'policy-v2']),
|
|
10
19
|
decisionToast: z.boolean(), defaultMode: z.enum(['MANUAL', 'AUTO']),
|
|
11
20
|
effortCap: z.enum(AUTO_EFFORTS), jevTimeoutMs: z.number().int().min(1000).max(4500),
|
|
12
21
|
};
|
|
@@ -183,8 +192,12 @@ export class SettingsStore {
|
|
|
183
192
|
const parsed = schema.safeParse(raw[key]);
|
|
184
193
|
if (parsed.success)
|
|
185
194
|
Object.assign(settings, { [key]: parsed.data });
|
|
186
|
-
else
|
|
195
|
+
else {
|
|
187
196
|
issues.push(...schemaIssues(parsed.error, key));
|
|
197
|
+
// A malformed explicit transport must never fall back to OpenRouter.
|
|
198
|
+
if (key === 'decisionService')
|
|
199
|
+
Object.assign(settings, { decisionService: { provider: 'custom', endpoint: '' } });
|
|
200
|
+
}
|
|
188
201
|
}
|
|
189
202
|
for (const kind of ['mainEfforts', 'childRoutes']) {
|
|
190
203
|
if (!Object.hasOwn(raw, kind))
|
package/lib/subagent-tool.js
CHANGED
|
@@ -5,6 +5,8 @@ import { DEFAULT_SETTINGS } from './settings-contract.js';
|
|
|
5
5
|
import { validateChildRoute } from './settings-validation.js';
|
|
6
6
|
import { CATEGORY_HINTS } from './jev.js';
|
|
7
7
|
import { buildState } from './context.js';
|
|
8
|
+
import { choosePolicyRoute, modelFamily } from './policy-v2.js';
|
|
9
|
+
import { policyVersion } from './jev.js';
|
|
8
10
|
/** The ordinary session a caller belongs to; undefined when any link is no longer live (treated as MANUAL). */
|
|
9
11
|
function rootOf(caller, agents) {
|
|
10
12
|
let current = caller;
|
|
@@ -21,12 +23,15 @@ const MANUAL_TEXT = '主会话为 MANUAL:不判档,按官方默认委派;
|
|
|
21
23
|
export function routeSubagentTool(jev, deps) {
|
|
22
24
|
return defineTool({
|
|
23
25
|
name: 'route_subagent',
|
|
24
|
-
description: 'Call before every sub-agent delegation, whatever the task size. Picks provider/model/reasoning_effort with the Jev decision model; pass provider, model and reasoning_effort explicitly to the sub-agent tool, or the sub-agent inherits this session\'s effort. Sends the task text (redacted, up to 8000 characters) to OpenRouter/TypeSafe only while the main session is AUTO; otherwise returns routed=false: delegate with the official defaults. If the user or lead already chose the sub-agent model, pass it as model: only reasoning_effort is routed and the model is kept. On error: pause delegation and ask the user; never guess a tier.',
|
|
26
|
+
description: 'When policy-v2 is selected, this tool is an optional preview only: use the official fresh creation tool directly for exact-child automatic routing, do not turn preview output into explicit user constraints; fork is unsupported. Under the legacy policy: Call before every sub-agent delegation, whatever the task size. Picks provider/model/reasoning_effort with the Jev decision model; pass provider, model and reasoning_effort explicitly to the sub-agent tool, or the sub-agent inherits this session\'s effort. Sends the task text (redacted, up to 8000 characters) to OpenRouter/TypeSafe only while the main session is AUTO; otherwise returns routed=false: delegate with the official defaults. If the user or lead already chose the sub-agent model, pass it as model: only reasoning_effort is routed and the model is kept. On error: pause delegation and ask the user; never guess a tier.',
|
|
25
27
|
timeoutMs: 5000,
|
|
26
28
|
parameters: {
|
|
27
29
|
task: { type: 'string', required: true, description: 'The task the sub-agent will do.' },
|
|
28
|
-
provider: { type: 'string', required: true, enum: ['claude', 'codex'], description: '
|
|
30
|
+
provider: { type: 'string', required: true, enum: ['claude', 'codex', 'dsh-magpie-connect'], description: 'Exact transport provider. Magpie is available only under policy-v2.' },
|
|
29
31
|
model: { type: 'string', description: 'Sub-agent model already chosen by the user or lead; when given, only reasoning_effort is routed.' },
|
|
32
|
+
reasoning_effort: { type: 'string', description: 'Explicit effort constraint for policy-v2; preserved and validated, never silently replaced.' },
|
|
33
|
+
family: { type: 'string', enum: ['claude', 'codex'], description: 'Magpie automatic capability family; required when no model is explicitly supplied.' },
|
|
34
|
+
path: { type: 'string', enum: ['ordinary', 'fork', 'teammate'], description: 'policy-v2 creation path; fork remains unsupported until real history compatibility is verified.' },
|
|
30
35
|
},
|
|
31
36
|
output: {
|
|
32
37
|
schema: {
|
|
@@ -34,6 +39,10 @@ export function routeSubagentTool(jev, deps) {
|
|
|
34
39
|
additionalProperties: false,
|
|
35
40
|
properties: {
|
|
36
41
|
routed: { type: 'boolean' },
|
|
42
|
+
status: { type: 'string' },
|
|
43
|
+
policyVersion: { type: 'string' },
|
|
44
|
+
capability: { type: 'string' },
|
|
45
|
+
depth: { type: 'string' },
|
|
37
46
|
reason: { type: 'string' },
|
|
38
47
|
tier: { type: 'string' },
|
|
39
48
|
provider: { type: 'string' },
|
|
@@ -41,9 +50,12 @@ export function routeSubagentTool(jev, deps) {
|
|
|
41
50
|
reasoning_effort: { type: 'string' },
|
|
42
51
|
},
|
|
43
52
|
},
|
|
44
|
-
render: (_args, v) => [{ type: 'text', text: v.
|
|
45
|
-
?
|
|
46
|
-
: `
|
|
53
|
+
render: (_args, v) => [{ type: 'text', text: v.status ? (v.status === 'recommended'
|
|
54
|
+
? `policy-v2 recommended only (not applied): ${v.provider}/${v.model}/${v.reasoning_effort}. Use the official creation tool; AUTO judges and binds the exact child before its first request. Do not copy this recommendation as a new explicit user constraint. Query the child routing status for applied evidence.`
|
|
55
|
+
: `policy-v2 ${v.status}: ${v.reason}. 暂停委派并询问用户;不将推荐冒充实际应用。`)
|
|
56
|
+
: v.routed === false
|
|
57
|
+
? MANUAL_TEXT
|
|
58
|
+
: `Delegate with provider=${v.provider} model=${v.model} reasoning_effort=${v.reasoning_effort} (tier ${v.tier}); label the description [${v.model}·${v.reasoning_effort}].` }],
|
|
47
59
|
},
|
|
48
60
|
execute: async (args, exec) => {
|
|
49
61
|
const caller = exec.agent;
|
|
@@ -51,6 +63,21 @@ export function routeSubagentTool(jev, deps) {
|
|
|
51
63
|
// MANUAL (or disabled, or an unreachable root): no judgement and nothing leaves the machine.
|
|
52
64
|
if (!root || deps.state.read(root.id).mode !== 'AUTO')
|
|
53
65
|
return { routed: false, reason: 'manual' };
|
|
66
|
+
if (deps.readSettings?.().policy === 'policy-v2') {
|
|
67
|
+
if (args.path === 'fork')
|
|
68
|
+
return { routed: false, status: 'unsupported', policyVersion, reason: 'fork 真实历史兼容尚未验证;选择 fresh 或由用户关闭试验后继承' };
|
|
69
|
+
const explicit = { ...(args.model ? { model: args.model } : {}), ...(args.reasoning_effort ? { reasoningEffort: args.reasoning_effort } : {}) };
|
|
70
|
+
const result = explicit.model && explicit.reasoningEffort ? { kind: 'ok', depth: 'baseline', capability: 'bounded' }
|
|
71
|
+
: await jev.judgePolicy(buildState({ text: args.task }), exec.signal, 'child', explicit.model ? 'depth' : explicit.reasoningEffort ? 'capability' : 'both');
|
|
72
|
+
if (result.kind !== 'ok')
|
|
73
|
+
throw new Error('policy-v2 Jev 判断失败或取消;暂停委派并询问用户');
|
|
74
|
+
if (result.capability === 'insufficient_context')
|
|
75
|
+
return { routed: false, status: 'needs-context', policyVersion, reason: '请补充任务目标与范围' };
|
|
76
|
+
const route = choosePolicyRoute(args.provider, result.capability ?? 'bounded', result.depth, await deps.catalog(), deps.readSettings().effortCap, explicit, args.family ?? (args.model ? modelFamily(args.provider, args.model) : undefined));
|
|
77
|
+
return { status: 'recommended', policyVersion, ...(result.capability ? { capability: result.capability } : {}), depth: result.depth, provider: route.provider, model: route.model, reasoning_effort: route.reasoningEffort };
|
|
78
|
+
}
|
|
79
|
+
if (args.provider === 'dsh-magpie-connect')
|
|
80
|
+
throw new Error('Magpie 路由需显式选择 policy-v2;原策略不变');
|
|
54
81
|
const result = await jev.judge(buildState({ text: args.task }), exec.signal);
|
|
55
82
|
if (result.kind === 'cancelled')
|
|
56
83
|
throw new Error('route_subagent 已取消。');
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-auto-model-routing",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "DeepSeek Harness plugin:
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "DeepSeek Harness plugin: independent Jev capability and effort decisions, enforced fresh-child routes, decision visibility and allocation feedback",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "prgrmrwy",
|
|
7
7
|
"repository": { "type": "git", "url": "git+https://github.com/prgrmrwy/dsh-auto-model-routing.git" },
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
"@deepseek-ai/dsh-credentials": "^0.2.0-rc.2",
|
|
100
100
|
"@deepseek-ai/dsh-llm": "^0.2.0-rc.2",
|
|
101
101
|
"@deepseek-ai/dsh-session-projection": "^0.2.0-rc.2",
|
|
102
|
-
"react": "^19.0.0",
|
|
102
|
+
"react": "^18.3.1 || ^19.0.0",
|
|
103
103
|
"@deepseek-ai/dsh-launch-environment": "^0.2.0-rc.2",
|
|
104
104
|
"@deepseek-ai/dsh-skill-filesystem": "^0.2.0-rc.2",
|
|
105
105
|
"@deepseek-ai/dsh-tools": "^0.2.0-rc.2"
|
|
@@ -7,6 +7,21 @@ description: 创建或复用子智能体前,调用 route_subagent(Jev 判档
|
|
|
7
7
|
|
|
8
8
|
判档由插件工具 `route_subagent` 完成(与主会话 AUTO 共用同一个 Jev 判断),主 Agent 不自行估档。工具跟随所属主会话的模式:只有主会话为 AUTO 时才判档,并把脱敏后的任务文本发送给 OpenRouter/TypeSafe;主会话为 MANUAL 时不判档、不外发。
|
|
9
9
|
|
|
10
|
+
## 默认 AUTO(优先于下方旧策略回滚规则)
|
|
11
|
+
|
|
12
|
+
无需选择版本;未显式配置 legacy 回滚且根会话为 AUTO 时:Agent 仍决定拆解、派发和验收;AUTO 在官方创建工具的精确 child 身份上选配资源,主子问题不再相同。
|
|
13
|
+
|
|
14
|
+
- Magpie 使用精确 provider `dsh-magpie-connect` 和完整带前缀 model ID,不改成本地 claude/codex;自动槽支持 Claude/GPT 家族,其他网关模型需显式模型与可验证 effort。当前本机测试仅走 Magpie,OpenRouter 只用于 Jev,不直接调用本地 Claude/codex。
|
|
15
|
+
- 使用官方 fresh `subagent` / `spawn_teammate` 创建,提供清楚的目标和范围即可;不要为自动任务先调用推荐工具再把推荐伪装成用户显式约束。请求首次发送前 AUTO 会判断能力与投入、验证目录并应用。
|
|
16
|
+
- 用户显式指定 model/effort 时照实传递;只指定一个维度仅判断另一个,两者指定则不调用 Jev。只指定 effort 也不能沿用下方“完全不判”的原策略规则。
|
|
17
|
+
- `route_subagent` 在此分支仅做可选预览,`status: recommended` 不是 applied;fork 返回 unsupported,应暂停并改用 fresh,或让用户明确选择关闭试验后继承。不要擅自将 fork 改为 fresh。
|
|
18
|
+
- 通过 `childRouting` 状态接口检查对应 sessionId 的 recommended/applied/unsupported/needs-context/failed;应用证据来自实际 request/header,不来自工具推荐。未绑定的创建路径不冒充已路由。
|
|
19
|
+
- 已创建成员收到后续消息不重选;需要升级视作 Agent 明确发起的新建资源决策,说明原因并保留显式约束,不进行自治重试。
|
|
20
|
+
- 验收时区分任务描述不足、信息缺失、推理投入不足、能力不足与基础设施故障;网络错误不是自动升级理由。
|
|
21
|
+
- MANUAL 停止新决策,已创建实例保持初始路线;关闭试验回到原策略,不删除已绑定子会话的恢复记录。
|
|
22
|
+
|
|
23
|
+
以下规则只适用于原策略。
|
|
24
|
+
|
|
10
25
|
## 创建
|
|
11
26
|
|
|
12
27
|
1. 尊重用户或 lead 显式指定的模型与 effort:
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { Selection } from './state.js';
|
|
2
|
-
import type { ProjectionDefinition } from '@deepseek-ai/dsh-session-projection';
|
|
3
|
-
declare module '@deepseek-ai/dsh-session-projection/types' {
|
|
4
|
-
interface SessionProjectionStateMap {
|
|
5
|
-
autoRoutingSelection: Selection | null;
|
|
6
|
-
}
|
|
7
|
-
}
|
|
8
|
-
export declare const selectionProjection: ProjectionDefinition<'autoRoutingSelection'>;
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
export const selectionProjection = {
|
|
3
|
-
key: 'autoRoutingSelection', stateVersion: 1,
|
|
4
|
-
stateSchema: z.object({ provider: z.string(), model: z.string(), reasoningEffort: z.enum(['low', 'medium', 'high', 'xhigh', 'max']).optional() }).transform(value => ({ provider: value.provider, model: value.model, ...(value.reasoningEffort ? { reasoningEffort: value.reasoningEffort } : {}) })).nullable(),
|
|
5
|
-
init: () => null,
|
|
6
|
-
apply: (state, event) => event.type === 'model/selection' ? event.data : state,
|
|
7
|
-
};
|