@eddyskywalker/dsh-chatgpt-subscription 0.24.0 → 0.24.2
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 +28 -0
- package/README.md +1 -1
- package/icon.svg +28 -0
- package/lib/index.js +433 -133
- package/lib/types/host/agent-preset.d.ts.map +1 -1
- package/lib/types/host/antigravity/oauth.d.ts +22 -1
- package/lib/types/host/antigravity/oauth.d.ts.map +1 -1
- package/lib/types/host/antigravity/port-constants.d.ts +18 -5
- package/lib/types/host/antigravity/port-constants.d.ts.map +1 -1
- package/lib/types/host/claude/oauth.d.ts +8 -4
- package/lib/types/host/claude/oauth.d.ts.map +1 -1
- package/lib/types/host/claude/types.d.ts +20 -5
- package/lib/types/host/claude/types.d.ts.map +1 -1
- package/lib/types/host/command-code/oauth.d.ts +6 -3
- package/lib/types/host/command-code/oauth.d.ts.map +1 -1
- package/lib/types/host/command-code/types.d.ts +22 -2
- package/lib/types/host/command-code/types.d.ts.map +1 -1
- package/lib/types/host/kimi-code/adapter.d.ts +21 -4
- package/lib/types/host/kimi-code/adapter.d.ts.map +1 -1
- package/lib/types/host/subagent-model-authorization.d.ts +72 -3
- package/lib/types/host/subagent-model-authorization.d.ts.map +1 -1
- package/package.json +3 -1
- package/presets/dispatch/README.md +3 -2
- package/presets/dispatch/agent.cordis.yml +124 -129
package/lib/index.js
CHANGED
|
@@ -4955,6 +4955,27 @@ var FileModelSettingsStore$2 = class {
|
|
|
4955
4955
|
*/
|
|
4956
4956
|
/** Default loopback port the Antigravity CLI's own flow uses. */
|
|
4957
4957
|
const DEFAULT_CALLBACK_PORT$2 = 51121;
|
|
4958
|
+
/**
|
|
4959
|
+
* How many consecutive ports one probe covers, starting at the default.
|
|
4960
|
+
*
|
|
4961
|
+
* An excluded range is not a block of fixed width. Windows reserves whatever
|
|
4962
|
+
* span Hyper-V/WinNAT/its own services hold, it moves on every reboot, and a
|
|
4963
|
+
* Windows 11 host was measured handing this line's default port 277 consecutive
|
|
4964
|
+
* excluded ports (51121-51397). A window narrower than the run the default
|
|
4965
|
+
* starts inside ends *inside* that run and then reports "no usable port" on a
|
|
4966
|
+
* machine where hundreds of free ports sit immediately behind it — so the width
|
|
4967
|
+
* of this window, not the width the probe assumes, is what keeps the flow
|
|
4968
|
+
* working.
|
|
4969
|
+
*
|
|
4970
|
+
* 1024 is roughly 3.7x the widest run measured, which is the margin that stops
|
|
4971
|
+
* this depending on any single host's layout, and it still ends at 52144, far
|
|
4972
|
+
* below the 65535 ceiling. The window is cheap because it is walked one failed
|
|
4973
|
+
* bind at a time: a rejected listen measured ~0.35ms here (277 probes in 96ms),
|
|
4974
|
+
* so even the full 1024-probe worst case — only reached when every port in the
|
|
4975
|
+
* window is unusable, immediately before the actionable error — costs ~0.36s
|
|
4976
|
+
* once per sign-in. The ordinary case stops at the first usable port.
|
|
4977
|
+
*/
|
|
4978
|
+
const CALLBACK_PORT_ATTEMPTS$2 = 1024;
|
|
4958
4979
|
let cachedQuota;
|
|
4959
4980
|
let quotaFetchInFlight = null;
|
|
4960
4981
|
/** Bumped by every cache clear so an in-flight fetch cannot publish stale state. */
|
|
@@ -5290,11 +5311,19 @@ function callbackPort() {
|
|
|
5290
5311
|
* URI carries whatever port this returns, so any free loopback port works. A
|
|
5291
5312
|
* configured `DSH_ANTIGRAVITY_CALLBACK_PORT` still wins — callers who set it
|
|
5292
5313
|
* accept that the port must be usable.
|
|
5314
|
+
*
|
|
5315
|
+
* How wide such a reserved run is not something this function can look up — it
|
|
5316
|
+
* is the host's own bookkeeping — so the probe only has to be wider than the
|
|
5317
|
+
* widest run it can meet, and it walks a window sized for that (see
|
|
5318
|
+
* {@link CALLBACK_PORT_ATTEMPTS}) instead of guessing a block size. `start` and
|
|
5319
|
+
* `probe` are the seams that keep that claim testable: production probes from
|
|
5320
|
+
* the line's own default and judges each port by really binding it, and a test
|
|
5321
|
+
* can point the walk at a run whose width it controls.
|
|
5293
5322
|
*/
|
|
5294
|
-
async function resolveCallbackPort$1(attempts =
|
|
5323
|
+
async function resolveCallbackPort$1(attempts = CALLBACK_PORT_ATTEMPTS$2, host = resolveCallbackHost(), start = DEFAULT_CALLBACK_PORT$2, probe = (port) => isPortUsable$1(port, host)) {
|
|
5295
5324
|
if (antigravityEnv("CALLBACK_PORT") !== void 0) return callbackPort();
|
|
5296
|
-
for (let offset = 0; offset < attempts; offset += 1) if (await
|
|
5297
|
-
throw new Error(`No usable local port for the Antigravity sign-in callback (tried ${attempts} ports from ${
|
|
5325
|
+
for (let offset = 0; offset < attempts; offset += 1) if (await probe(start + offset)) return start + offset;
|
|
5326
|
+
throw new Error(`No usable local port for the Antigravity sign-in callback (tried ${attempts} ports from ${start} on ${host}). A Windows excluded-port range may cover them; set DSH_ANTIGRAVITY_CALLBACK_PORT to a free port.`);
|
|
5298
5327
|
}
|
|
5299
5328
|
/**
|
|
5300
5329
|
* Whether one port can be bound on the interface the listener will use.
|
|
@@ -5377,7 +5406,7 @@ async function getUserEmail(token, fetchFn = fetch) {
|
|
|
5377
5406
|
}
|
|
5378
5407
|
async function startCallbackServer(expectedState) {
|
|
5379
5408
|
const host = resolveCallbackHost();
|
|
5380
|
-
const port = await resolveCallbackPort$1(
|
|
5409
|
+
const port = await resolveCallbackPort$1(CALLBACK_PORT_ATTEMPTS$2, host);
|
|
5381
5410
|
return new Promise((resolve, reject) => {
|
|
5382
5411
|
let settled = false;
|
|
5383
5412
|
let timeout;
|
|
@@ -13395,6 +13424,28 @@ const BILLING_SUBSCRIPTIONS_PATH = "/alpha/billing/subscriptions";
|
|
|
13395
13424
|
const USAGE_SUMMARY_PATH = "/alpha/usage/summary";
|
|
13396
13425
|
/** First loopback port tried for the browser callback; matches the official CLI. */
|
|
13397
13426
|
const DEFAULT_CALLBACK_PORT$1 = 5959;
|
|
13427
|
+
/**
|
|
13428
|
+
* Consecutive ports probed when the default is taken.
|
|
13429
|
+
*
|
|
13430
|
+
* The CLI opens its own search at 5959, which {@link DEFAULT_CALLBACK_PORT}
|
|
13431
|
+
* keeps, so on an ordinary machine this line still lands on the port the CLI
|
|
13432
|
+
* would. The WIDTH is recovery budget, not compatibility: the studio is handed
|
|
13433
|
+
* whatever port this returns in the callback URL, so ports past the CLI's own
|
|
13434
|
+
* range were already reachable here before the window was widened, and starting
|
|
13435
|
+
* at the CLI default is what preserves the "behaves like the CLI" property if the
|
|
13436
|
+
* studio ever does pin an allowlist.
|
|
13437
|
+
*
|
|
13438
|
+
* The failure shape makes the width matter more than it does for a line that has
|
|
13439
|
+
* a manual flow to fall back to: exhausting this window is a thrown error and a
|
|
13440
|
+
* dead sign-in, not a downgrade. Windows (Hyper-V/WinNAT) reserves TCP port
|
|
13441
|
+
* ranges of no fixed width that change on every reboot — one host was measured
|
|
13442
|
+
* holding 277 consecutive ports — so a ten-port window is one reboot away from
|
|
13443
|
+
* failing outright and a wider one is not. 1024 matches the other two sign-in
|
|
13444
|
+
* lines here and still ends at 6983: clear of the privileged ports, clear of the
|
|
13445
|
+
* OS dynamic range, and walked one failed bind at a time (~0.35ms each), so the
|
|
13446
|
+
* worst case costs a fraction of a second once per sign-in.
|
|
13447
|
+
*/
|
|
13448
|
+
const CALLBACK_PORT_ATTEMPTS$1 = 1024;
|
|
13398
13449
|
const CALLBACK_PATH$1 = "/callback";
|
|
13399
13450
|
/** Page the studio's browser tab lands on after a successful POST; CLI-compatible. */
|
|
13400
13451
|
const CALLBACK_COMPLETE_PATH = "/callback/complete";
|
|
@@ -17169,16 +17220,19 @@ function checkPortAvailable(port) {
|
|
|
17169
17220
|
});
|
|
17170
17221
|
}
|
|
17171
17222
|
/**
|
|
17172
|
-
* First free loopback port in the
|
|
17223
|
+
* First free loopback port in the probe range.
|
|
17173
17224
|
*
|
|
17174
17225
|
* The studio receives the port in the callback URL, so any free port works;
|
|
17175
17226
|
* starting at the CLI's own default keeps behavior identical on a machine where
|
|
17176
|
-
* a server-side allowlist is ever introduced.
|
|
17227
|
+
* a server-side allowlist is ever introduced. `probe` is the seam the tests walk
|
|
17228
|
+
* a reserved run through, which is what lets the width of the window be asserted
|
|
17229
|
+
* without binding a thousand real sockets on a machine whose own reservations
|
|
17230
|
+
* move underneath the test.
|
|
17177
17231
|
*/
|
|
17178
|
-
async function findAvailablePort(start = DEFAULT_CALLBACK_PORT$1, attempts =
|
|
17232
|
+
async function findAvailablePort(start = DEFAULT_CALLBACK_PORT$1, attempts = CALLBACK_PORT_ATTEMPTS$1, probe = checkPortAvailable) {
|
|
17179
17233
|
for (let offset = 0; offset < attempts; offset += 1) {
|
|
17180
17234
|
const port = start + offset;
|
|
17181
|
-
if (await
|
|
17235
|
+
if (await probe(port)) return port;
|
|
17182
17236
|
}
|
|
17183
17237
|
throw new Error(`No free local port for the Command Code sign-in callback (tried ${attempts} ports from ${start}).`);
|
|
17184
17238
|
}
|
|
@@ -24868,10 +24922,14 @@ function countOmitted(current, baseline, resolved) {
|
|
|
24868
24922
|
*
|
|
24869
24923
|
* The retryable set is therefore:
|
|
24870
24924
|
*
|
|
24871
|
-
* - `SERVER` — any 5xx, including that upstream-unavailable 502
|
|
24872
|
-
*
|
|
24873
|
-
*
|
|
24874
|
-
*
|
|
24925
|
+
* - `SERVER` — any 5xx, including that upstream-unavailable 502, and the
|
|
24926
|
+
* overloaded 429 (`UPSTREAM_OVERLOAD_PATTERNS`). Overload shares the 429
|
|
24927
|
+
* status with real back-pressure but not its meaning: it describes the state
|
|
24928
|
+
* of the upstream engine, which every account behind this endpoint shares, so
|
|
24929
|
+
* it is filed as the server condition it is and the pool is left alone;
|
|
24930
|
+
* - `RATE_LIMIT` — a 429 that is genuine back-pressure ("too many requests"),
|
|
24931
|
+
* which the documentation describes as transient and which does belong to the
|
|
24932
|
+
* account that hit it, so rotation on it still earns its keep;
|
|
24875
24933
|
* - `TRANSPORT` — a connection that produced no response at all;
|
|
24876
24934
|
* - `TIMEOUT` — a stalled stream, handled by the idle watchdog.
|
|
24877
24935
|
*
|
|
@@ -24917,6 +24975,35 @@ const ENTITLEMENT_PATTERNS = [
|
|
|
24917
24975
|
/recognized as other/i,
|
|
24918
24976
|
/currently plan supports only/i
|
|
24919
24977
|
];
|
|
24978
|
+
/**
|
|
24979
|
+
* 正文里说明「上游引擎过载」的措辞,与上面两组按**归属**分界,而不是按严重程度。
|
|
24980
|
+
*
|
|
24981
|
+
* - `ENTITLEMENT_PATTERNS` 说的是套餐:换号、换时间都不会变,是请求/套餐的属性;
|
|
24982
|
+
* - `QUOTA_EXHAUSTED_PATTERNS` 说的是这个账号自己的余额/额度:别人可能还好,
|
|
24983
|
+
* 但这个账号在窗口重置前一定起不来;
|
|
24984
|
+
* - 本组说的是**上游引擎的繁忙**:这是所有账号共用的同一台引擎,谁来问都是这句
|
|
24985
|
+
* 话,本地没有任何动作能改变它。
|
|
24986
|
+
*
|
|
24987
|
+
* 第三组必须单独识别,因为它和真正的限流共用同一个 429 状态码,却不共享结论。
|
|
24988
|
+
* 服务端用 429 表达「引擎过载」时,本线路此前把它归进 RATE_LIMIT,号池随即按
|
|
24989
|
+
* 限流语义给**唯一那个账号**写 15 分钟冷却(`POOL_COOLDOWN_MS`),之后同一
|
|
24990
|
+
* 会话的每一次重试都被 `isEligible` 直接挡在门外——重试根本没发出网络请求,
|
|
24991
|
+
* 3 次退避(3.2s/6.1s,纯本地耗时)打在一个登录正常、额度正常的账号上,最后以
|
|
24992
|
+
* 「全部 1 个 Kimi Code 账号均处于配额限制或冷却中 (429)。最短预计在 15 分钟后
|
|
24993
|
+
* 解除冷却。」收场。真正被限流的服务端状态不该有账号级的副作用。
|
|
24994
|
+
*
|
|
24995
|
+
* 这里只按正文判定,绝不按状态码判定:同一个 429 既可能是过载也可能是真限流,
|
|
24996
|
+
* 唯一能区分的证据就是服务自己写下的那句话。措辞取自本线路文档与实际响应,
|
|
24997
|
+
* 刻意保留 "the engine is …" 的限定,避免把任何提到 "overload" 的正文一律
|
|
24998
|
+
* 吞掉——将来若出现「本账号请求量过载」这类真限流措辞,它不该落进这一组。
|
|
24999
|
+
*/
|
|
25000
|
+
const UPSTREAM_OVERLOAD_PATTERNS = [
|
|
25001
|
+
/\bengine is (currently )?overloaded\b/i,
|
|
25002
|
+
/\bservers? (?:is|are) (?:currently )?overloaded\b/i,
|
|
25003
|
+
/\bservice is (?:currently )?overloaded\b/i,
|
|
25004
|
+
/\bupstream model provider is temporarily unavailable\b/i,
|
|
25005
|
+
/\bcurrently overloaded\b/i
|
|
25006
|
+
];
|
|
24920
25007
|
/** Body text the service uses for a 429 that must NOT be retried. */
|
|
24921
25008
|
const QUOTA_EXHAUSTED_PATTERNS$1 = [
|
|
24922
25009
|
/exceeded_current_quota_error/i,
|
|
@@ -24968,12 +25055,14 @@ function classifyKimiFailure(status, bodyText) {
|
|
|
24968
25055
|
code: CONTEXT_OVERFLOW_CODE,
|
|
24969
25056
|
retryable: false,
|
|
24970
25057
|
accountScoped: false,
|
|
25058
|
+
upstreamOverload: false,
|
|
24971
25059
|
message: `${PROVIDER_NAME$5} context window exceeded: ${detail}`
|
|
24972
25060
|
};
|
|
24973
25061
|
if (status === 402) return {
|
|
24974
25062
|
code: "SERVER",
|
|
24975
25063
|
retryable: true,
|
|
24976
25064
|
accountScoped: false,
|
|
25065
|
+
upstreamOverload: false,
|
|
24977
25066
|
message: `${PROVIDER_NAME$5} could not verify the subscription tier (402). Retrying; if it persists, confirm the membership is active.${detail ? ` ${detail}` : ""}`
|
|
24978
25067
|
};
|
|
24979
25068
|
if (status === 401 || status === 403) {
|
|
@@ -24981,6 +25070,7 @@ function classifyKimiFailure(status, bodyText) {
|
|
|
24981
25070
|
code: "PROVIDER_ERROR",
|
|
24982
25071
|
retryable: false,
|
|
24983
25072
|
accountScoped: false,
|
|
25073
|
+
upstreamOverload: false,
|
|
24984
25074
|
message: `${PROVIDER_NAME$5} refused this request for the current plan: ${detail || "the requested model or context is not included"}. Switch to a model the plan includes, lower the context-window override, or upgrade the subscription.`
|
|
24985
25075
|
};
|
|
24986
25076
|
if (status === 403) {
|
|
@@ -24989,6 +25079,7 @@ function classifyKimiFailure(status, bodyText) {
|
|
|
24989
25079
|
code: "PROVIDER_ERROR",
|
|
24990
25080
|
retryable: false,
|
|
24991
25081
|
accountScoped: true,
|
|
25082
|
+
upstreamOverload: false,
|
|
24992
25083
|
message: `${PROVIDER_NAME$5} blocked the request on an account limit (403): ${detail || (limitReached ? "the account limit was reached" : "the account refused the request")}. The quota refreshes on its own schedule — check the Kimi Code card in Settings for the reset time.`
|
|
24993
25084
|
};
|
|
24994
25085
|
}
|
|
@@ -24996,6 +25087,7 @@ function classifyKimiFailure(status, bodyText) {
|
|
|
24996
25087
|
code: "INVALID_CREDENTIAL",
|
|
24997
25088
|
retryable: false,
|
|
24998
25089
|
accountScoped: false,
|
|
25090
|
+
upstreamOverload: false,
|
|
24999
25091
|
message: `${PROVIDER_NAME$5} rejected the stored credential (401). Sign in again from Settings > Kimi Code.${detail ? ` ${detail}` : ""}`
|
|
25000
25092
|
};
|
|
25001
25093
|
}
|
|
@@ -25004,31 +25096,43 @@ function classifyKimiFailure(status, bodyText) {
|
|
|
25004
25096
|
code: "PROVIDER_ERROR",
|
|
25005
25097
|
retryable: false,
|
|
25006
25098
|
accountScoped: true,
|
|
25099
|
+
upstreamOverload: false,
|
|
25007
25100
|
message: `${PROVIDER_NAME$5} reports the account quota is exhausted: ${detail || "no remaining quota"}. Top up or wait for the window to reset.`
|
|
25008
25101
|
};
|
|
25102
|
+
if (matchesAny$1(detail, UPSTREAM_OVERLOAD_PATTERNS)) return {
|
|
25103
|
+
code: "SERVER",
|
|
25104
|
+
retryable: true,
|
|
25105
|
+
accountScoped: false,
|
|
25106
|
+
upstreamOverload: true,
|
|
25107
|
+
message: `${PROVIDER_NAME$5} upstream engine is overloaded (429): ${detail || "the engine is currently overloaded"}. This is a temporary upstream condition shared by every account, so no account is taken out of rotation; retrying with backoff.`
|
|
25108
|
+
};
|
|
25009
25109
|
return {
|
|
25010
25110
|
code: "RATE_LIMIT",
|
|
25011
25111
|
retryable: true,
|
|
25012
25112
|
accountScoped: false,
|
|
25013
|
-
|
|
25113
|
+
upstreamOverload: false,
|
|
25114
|
+
message: `${PROVIDER_NAME$5} is rate limited (429): ${detail || "too many requests"}. Retrying with backoff.`
|
|
25014
25115
|
};
|
|
25015
25116
|
}
|
|
25016
25117
|
if (status >= 500) return {
|
|
25017
25118
|
code: "SERVER",
|
|
25018
25119
|
retryable: true,
|
|
25019
25120
|
accountScoped: false,
|
|
25121
|
+
upstreamOverload: matchesAny$1(detail, UPSTREAM_OVERLOAD_PATTERNS),
|
|
25020
25122
|
message: `${PROVIDER_NAME$5} upstream server error (${status}): ${detail || "the model provider is temporarily unavailable"}. Retrying with backoff.`
|
|
25021
25123
|
};
|
|
25022
25124
|
if (status === 400) return {
|
|
25023
25125
|
code: "PROVIDER_ERROR",
|
|
25024
25126
|
retryable: false,
|
|
25025
25127
|
accountScoped: false,
|
|
25128
|
+
upstreamOverload: false,
|
|
25026
25129
|
message: `${PROVIDER_NAME$5} rejected the request (400): ${detail || "the request was not accepted"}`
|
|
25027
25130
|
};
|
|
25028
25131
|
return {
|
|
25029
25132
|
code: "PROVIDER_ERROR",
|
|
25030
25133
|
retryable: false,
|
|
25031
25134
|
accountScoped: false,
|
|
25135
|
+
upstreamOverload: false,
|
|
25032
25136
|
message: `${PROVIDER_NAME$5} API error (${status}): ${detail || "No response"}`
|
|
25033
25137
|
};
|
|
25034
25138
|
}
|
|
@@ -25061,7 +25165,13 @@ function accountLimitCooldownMs(bodyText, now = Date.now()) {
|
|
|
25061
25165
|
if (resetsAt !== null && Number.isFinite(resetsAt) && resetsAt > now) return Math.min(Math.max(resetsAt - now, POOL_COOLDOWN_MS$3), MAX_ACCOUNT_LIMIT_COOLDOWN_MS);
|
|
25062
25166
|
return BILLING_CYCLE_PATTERN.test(bodyText) ? BILLING_CYCLE_COOLDOWN_MS : ACCOUNT_LIMIT_COOLDOWN_MS;
|
|
25063
25167
|
}
|
|
25064
|
-
/**
|
|
25168
|
+
/**
|
|
25169
|
+
* Cooldown one rate-limited account takes when the provider states no delay.
|
|
25170
|
+
*
|
|
25171
|
+
* 只在**真正的**限流上使用:上游引擎过载走 SERVER,既不落这条窗口,也不消耗轮转
|
|
25172
|
+
* 名额。它是 15 分钟而不是服务自己说的时长,正是因为拿不准——拿不准的那次猜测
|
|
25173
|
+
* 落在一个只有单个账号的号池上,代价是整个号池在 15 分钟里彻底不可用。
|
|
25174
|
+
*/
|
|
25065
25175
|
const POOL_COOLDOWN_MS$3 = 15 * 6e4;
|
|
25066
25176
|
/**
|
|
25067
25177
|
* Fallback cooldown for an account whose usage window is spent.
|
|
@@ -25288,7 +25398,7 @@ var KimiCodeAdapter = class extends LlmAdapter {
|
|
|
25288
25398
|
const after = response.status === 429 ? retryAfterMs$1(response.headers) : void 0;
|
|
25289
25399
|
if (pool !== null && accountId !== void 0) {
|
|
25290
25400
|
const planScoped = response.status === 429 && matchesAny$1(detail, ENTITLEMENT_PATTERNS);
|
|
25291
|
-
if (response.status === 429 && !planScoped) {
|
|
25401
|
+
if (response.status === 429 && !planScoped && !failure.upstreamOverload) {
|
|
25292
25402
|
const cooldownMs = failure.accountScoped ? accountLimitCooldownMs(detail) : after ?? POOL_COOLDOWN_MS$3;
|
|
25293
25403
|
await pool.markCooldown(accountId, cooldownMs, `${PROVIDER_NAME$5} 429`).catch(() => void 0);
|
|
25294
25404
|
if (await pool.hasAnotherAvailableAccount(tried)) continue;
|
|
@@ -25384,7 +25494,7 @@ var KimiCodeAdapter = class extends LlmAdapter {
|
|
|
25384
25494
|
if (status === null) return reclassified;
|
|
25385
25495
|
const failure = classifyKimiFailure(status, bodyText);
|
|
25386
25496
|
const planScoped = status === 429 && matchesAny$1(bodyText, ENTITLEMENT_PATTERNS);
|
|
25387
|
-
if (status === 429 && !planScoped) await this.coolInBandRateLimit(pool, accountId, failure.accountScoped, bodyText);
|
|
25497
|
+
if (status === 429 && !planScoped && !failure.upstreamOverload) await this.coolInBandRateLimit(pool, accountId, failure.accountScoped, bodyText);
|
|
25388
25498
|
else if (failure.code === "INVALID_CREDENTIAL") await pool.markAuthFailed(accountId, failure.message).catch(() => void 0);
|
|
25389
25499
|
else if (failure.accountScoped) await pool.markCooldown(accountId, accountLimitCooldownMs(bodyText), `${PROVIDER_NAME$5} in-stream 403`).catch(() => void 0);
|
|
25390
25500
|
return reclassified;
|
|
@@ -37086,6 +37196,32 @@ const CLAUDE_CLI_VERSION = "2.1.293";
|
|
|
37086
37196
|
* see {@link CALLBACK_PORT_ATTEMPTS}.
|
|
37087
37197
|
*/
|
|
37088
37198
|
const DEFAULT_CALLBACK_PORT = 53692;
|
|
37199
|
+
/**
|
|
37200
|
+
* How many consecutive ports one probe covers, starting at the default.
|
|
37201
|
+
*
|
|
37202
|
+
* Windows (Hyper-V/WinNAT) dynamically reserves TCP port ranges that change on
|
|
37203
|
+
* every reboot. A fixed callback port can land inside one and `listen` fails
|
|
37204
|
+
* with `EACCES`, so browser authorization never completes.
|
|
37205
|
+
*
|
|
37206
|
+
* An excluded run is not a block of a width anyone can look up. One Windows 11
|
|
37207
|
+
* host was measured holding 277 consecutive unusable ports (51121-51397) plus
|
|
37208
|
+
* scattered single ones that appear in no static table, and the picture changes
|
|
37209
|
+
* across reboots — so a window justified as "covers any single block" is covering
|
|
37210
|
+
* a block size that does not exist. A window narrower than the run the default
|
|
37211
|
+
* starts inside ends INSIDE that run, and the consequence here is worse than an
|
|
37212
|
+
* error: resolveCallbackPort returns null, which beginLogin reads as "fall back
|
|
37213
|
+
* to manual". The user is then pushed into pasting a code with no visible cause
|
|
37214
|
+
* on a machine that has hundreds of free ports immediately behind the run.
|
|
37215
|
+
*
|
|
37216
|
+
* 1024 is ~3.7x the widest run measured and still ends at 54716, far below the
|
|
37217
|
+
* 65535 ceiling. The window is walked one failed bind at a time (~0.35ms per
|
|
37218
|
+
* rejected listen here), so the worst case costs a fraction of a second once per
|
|
37219
|
+
* sign-in and only when every port in it is unusable; the ordinary case stops at
|
|
37220
|
+
* the first usable port. Widening changes neither which port is tried first nor
|
|
37221
|
+
* the default status as the one port known to be registered for this client
|
|
37222
|
+
* (see {@link DEFAULT_CALLBACK_PORT}).
|
|
37223
|
+
*/
|
|
37224
|
+
const CALLBACK_PORT_ATTEMPTS = 1024;
|
|
37089
37225
|
/** Path of the loopback callback the browser is redirected to. */
|
|
37090
37226
|
const CALLBACK_PATH = "/callback";
|
|
37091
37227
|
/** Idle ceiling for one streaming response before it is declared stalled. */
|
|
@@ -41950,12 +42086,16 @@ function isPortUsable(port) {
|
|
|
41950
42086
|
* First usable loopback port, or null when the probe finds none.
|
|
41951
42087
|
*
|
|
41952
42088
|
* null rather than a throw is deliberate (see beginLogin): a failed probe is
|
|
41953
|
-
* NOT a hard error, it is the signal to fall back to the manual flow.
|
|
41954
|
-
*
|
|
41955
|
-
*
|
|
41956
|
-
*
|
|
41957
|
-
|
|
41958
|
-
|
|
42089
|
+
* NOT a hard error, it is the signal to fall back to the manual flow. That is
|
|
42090
|
+
* exactly why the window has to be wide rather than merely generous: a run of
|
|
42091
|
+
* excluded ports wider than CALLBACK_PORT_ATTEMPTS ends this walk early and drops
|
|
42092
|
+
* the user into a paste-a-code flow for a reason nothing on screen explains,
|
|
42093
|
+
* while the ports immediately behind that run sit free. Windows
|
|
42094
|
+
* (Hyper-V/WinNAT) reserves ranges of no fixed width that move on every reboot,
|
|
42095
|
+
* so the width of this window, not any assumed block size, is what keeps the
|
|
42096
|
+
* sign-in in loopback.
|
|
42097
|
+
*/
|
|
42098
|
+
async function resolveCallbackPort(probe = isPortUsable, attempts = CALLBACK_PORT_ATTEMPTS) {
|
|
41959
42099
|
for (let offset = 0; offset < attempts; offset += 1) {
|
|
41960
42100
|
const port = DEFAULT_CALLBACK_PORT + offset;
|
|
41961
42101
|
if (port > 65535) return null;
|
|
@@ -42355,7 +42495,7 @@ async function beginLogin(store, options = {}) {
|
|
|
42355
42495
|
port = await resolveCallbackPort(probe);
|
|
42356
42496
|
if (port === null) {
|
|
42357
42497
|
resolvedMode = "manual";
|
|
42358
|
-
fallbackReason = "No free loopback port in the
|
|
42498
|
+
fallbackReason = "No free loopback port in the 1024-port range starting at 53692.";
|
|
42359
42499
|
} else try {
|
|
42360
42500
|
server = await listen(port, LOOPBACK_HOST);
|
|
42361
42501
|
} catch (error) {
|
|
@@ -57540,6 +57680,129 @@ function delegationModeOf(policies, toolName) {
|
|
|
57540
57680
|
for (const policy of policies) if (policy.name === toolName) return policy.mode;
|
|
57541
57681
|
}
|
|
57542
57682
|
/**
|
|
57683
|
+
* Resolve the effective allowlist that governs delegations from this agent:
|
|
57684
|
+
* empty when the Host preference explicitly disables enforcement, otherwise
|
|
57685
|
+
* the Session's (or nearest subagent ancestor's) recorded policy, falling
|
|
57686
|
+
* back to the live preference only under `preference` scope.
|
|
57687
|
+
* @param agent - Calling agent.
|
|
57688
|
+
* @param preference - Current Host preference, when the settings service exists.
|
|
57689
|
+
* @param sessions - Session registry used for ancestor lookup.
|
|
57690
|
+
* @param scope - Whether an unrecorded Session falls back to the preference.
|
|
57691
|
+
* @returns the authorized routes in force for this agent, or an empty list when unrestricted.
|
|
57692
|
+
*/
|
|
57693
|
+
function effectiveAllowedRoutes(agent, preference, sessions, scope = "session") {
|
|
57694
|
+
if (preference !== void 0 && !preference.enabled) return [];
|
|
57695
|
+
return authorizedRoutesFor(agent, sessions) ?? (scope === "preference" ? preference?.allowedModels ?? [] : []);
|
|
57696
|
+
}
|
|
57697
|
+
/**
|
|
57698
|
+
* Append the Session's authorized `{ provider, model }` pairs to a successful
|
|
57699
|
+
* `list_subagent_models` result when the calling Session is governed by an
|
|
57700
|
+
* allowlist.
|
|
57701
|
+
*
|
|
57702
|
+
* Calling `list_subagent_models({})` upstream returns only provider names
|
|
57703
|
+
* (`minimax-code — MiniMax Code`), and calling it with `provider` prints
|
|
57704
|
+
* `<provider>/<model>` without stating that the output is already filtered to
|
|
57705
|
+
* the Session allowlist. Appending the exact split `{ provider, model }` pairs
|
|
57706
|
+
* here lets even a single `list_subagent_models({})` call hand the model every
|
|
57707
|
+
* authorized route in the form `subagent` expects, eliminating any reason to
|
|
57708
|
+
* search local config files or source code for the allowlist.
|
|
57709
|
+
* @param output - Original text returned by `list_subagent_models`.
|
|
57710
|
+
* @param allowed - Routes the calling Session authorizes.
|
|
57711
|
+
* @returns the output with the Session allowlist summary appended.
|
|
57712
|
+
*/
|
|
57713
|
+
function enrichListSubagentModelsOutput(output, allowed) {
|
|
57714
|
+
if (allowed.length === 0) return output;
|
|
57715
|
+
const header = "Authorized child routes for this Session (already filtered by the Session allowlist; only these routes are accepted, and `provider` and `model` must be passed as separate fields):";
|
|
57716
|
+
if (output.includes(header)) return output;
|
|
57717
|
+
const suffix = `${header}\n${allowed.map((route) => `- ${route.provider}/${route.model} -> provider: "${route.provider}", model: "${route.model}"`).join("\n")}`;
|
|
57718
|
+
return output.trim().length === 0 ? suffix : `${output}\n\n${suffix}`;
|
|
57719
|
+
}
|
|
57720
|
+
/**
|
|
57721
|
+
* Rewrite the upstream `list_subagent_models` and `subagent` tool/parameter
|
|
57722
|
+
* descriptions when the calling Session enforces an allowlist.
|
|
57723
|
+
*
|
|
57724
|
+
* Upstream `dsh-tool-subagent` ships static descriptions stating that
|
|
57725
|
+
* `"Catalog membership is advisory: an adapter may accept an unlisted model id."`
|
|
57726
|
+
* and `"Child LLM selection is optional. Omit provider, model, and reasoning_effort..."`.
|
|
57727
|
+
* In an allowlist-governed Session both statements are false and actively
|
|
57728
|
+
* mislead the dispatcher into either omitting `provider`/`model` or distrusting
|
|
57729
|
+
* `list_subagent_models` and hunting through local files for the "real" allowlist.
|
|
57730
|
+
* @param text - Tool description, parameter description, or rendered `tools:sdk` section text.
|
|
57731
|
+
* @param allowed - Routes the calling Session authorizes.
|
|
57732
|
+
* @returns the text with the misleading upstream sentences replaced by the Session's exact policy.
|
|
57733
|
+
*/
|
|
57734
|
+
function rewriteAuthorizedToolDescriptions(text, allowed) {
|
|
57735
|
+
if (allowed.length === 0 || text.length === 0) return text;
|
|
57736
|
+
const routes = authorizedRoutesText(allowed);
|
|
57737
|
+
return text.replaceAll("Catalog membership is advisory: an adapter may accept an unlisted model id.", `Output is already filtered to this Session's allowlist (${routes}); only these authorized provider/model routes are accepted.`).replace(/Child LLM selection is optional\.\s*Omit `?provider`?, `?model`?, and `?reasoning_effort`? to use configured child defaults and (?:inherit compatible missing values from the parent Agent|this provider's route defaults)\./g, `Child LLM selection is required in this Session: pass an authorized \`provider\` and \`model\` from \`list_subagent_models\` (${routes}) as separate fields; omitting either field is rejected.`).replace(/Supply together with model; omit both to use configured child defaults or (?:inherit the parent route|this provider's route defaults)\.|Omit to use the configured child default or inherit from the parent Agent\./g, `Required in this Session: pass an authorized provider id from list_subagent_models (${routes}).`).replace(/Supply together with provider; omit both to use configured child defaults or (?:inherit the parent route|this provider's route defaults)\.|Omit to use the configured child default or inherit when the child provider matches the parent provider\./g, "Required in this Session: pass the authorized model id (without the provider/ prefix) for the selected provider.");
|
|
57738
|
+
}
|
|
57739
|
+
/**
|
|
57740
|
+
* Rewrite one assembled prompt's `tools:sdk` section and native tool schemas
|
|
57741
|
+
* when the target Agent's Session enforces a subagent model allowlist.
|
|
57742
|
+
* @param assembly - Assembled prompt from `system-prompt/assemble`.
|
|
57743
|
+
* @param allowed - Routes the calling Session authorizes.
|
|
57744
|
+
* @param toolNames - Explicit-route delegation tool names.
|
|
57745
|
+
* @returns the updated assembly, or the original object when nothing changed.
|
|
57746
|
+
*/
|
|
57747
|
+
function rewriteAuthorizedPromptAssembly(assembly, allowed, toolNames = DEFAULT_DELEGATION_TOOLS) {
|
|
57748
|
+
if (allowed.length === 0) return assembly;
|
|
57749
|
+
let sectionsChanged = false;
|
|
57750
|
+
const sections = Array.isArray(assembly.sections) ? assembly.sections.map((section) => {
|
|
57751
|
+
if (section?.name !== "tools:sdk" || typeof section.text !== "string") return section;
|
|
57752
|
+
const rewritten = rewriteAuthorizedToolDescriptions(section.text, allowed);
|
|
57753
|
+
if (rewritten === section.text) return section;
|
|
57754
|
+
sectionsChanged = true;
|
|
57755
|
+
return {
|
|
57756
|
+
...section,
|
|
57757
|
+
text: rewritten
|
|
57758
|
+
};
|
|
57759
|
+
}) : assembly.sections;
|
|
57760
|
+
let toolsChanged = false;
|
|
57761
|
+
const tools = Array.isArray(assembly.tools) ? assembly.tools.map((tool) => {
|
|
57762
|
+
const name = asString$1(tool?.name);
|
|
57763
|
+
if (name === void 0 || name !== "list_subagent_models" && !toolNames.includes(name)) return tool;
|
|
57764
|
+
const description = typeof tool.description === "string" ? rewriteAuthorizedToolDescriptions(tool.description, allowed) : tool.description;
|
|
57765
|
+
let params = tool.parameters;
|
|
57766
|
+
const paramsRecord = asRecord$1(params);
|
|
57767
|
+
const propsRecord = asRecord$1(paramsRecord?.["properties"]);
|
|
57768
|
+
if (paramsRecord !== void 0 && propsRecord !== void 0) {
|
|
57769
|
+
let propsMutated = false;
|
|
57770
|
+
const nextProps = { ...propsRecord };
|
|
57771
|
+
for (const field of ["provider", "model"]) {
|
|
57772
|
+
const prop = asRecord$1(propsRecord[field]);
|
|
57773
|
+
const propDesc = asString$1(prop?.["description"]);
|
|
57774
|
+
if (prop !== void 0 && propDesc !== void 0) {
|
|
57775
|
+
const nextDesc = rewriteAuthorizedToolDescriptions(propDesc, allowed);
|
|
57776
|
+
if (nextDesc !== propDesc) {
|
|
57777
|
+
nextProps[field] = {
|
|
57778
|
+
...prop,
|
|
57779
|
+
description: nextDesc
|
|
57780
|
+
};
|
|
57781
|
+
propsMutated = true;
|
|
57782
|
+
}
|
|
57783
|
+
}
|
|
57784
|
+
}
|
|
57785
|
+
if (propsMutated) params = {
|
|
57786
|
+
...paramsRecord,
|
|
57787
|
+
properties: nextProps
|
|
57788
|
+
};
|
|
57789
|
+
}
|
|
57790
|
+
if (description === tool.description && params === tool.parameters) return tool;
|
|
57791
|
+
toolsChanged = true;
|
|
57792
|
+
return {
|
|
57793
|
+
...tool,
|
|
57794
|
+
...description !== tool.description ? { description } : {},
|
|
57795
|
+
...params !== tool.parameters ? { parameters: params } : {}
|
|
57796
|
+
};
|
|
57797
|
+
}) : assembly.tools;
|
|
57798
|
+
if (!sectionsChanged && !toolsChanged) return assembly;
|
|
57799
|
+
return {
|
|
57800
|
+
...assembly,
|
|
57801
|
+
...sectionsChanged ? { sections } : {},
|
|
57802
|
+
...toolsChanged ? { tools } : {}
|
|
57803
|
+
};
|
|
57804
|
+
}
|
|
57805
|
+
/**
|
|
57543
57806
|
* Decide whether one delegation call may start its child.
|
|
57544
57807
|
* @param agent - Calling agent.
|
|
57545
57808
|
* @param toolName - Tool being dispatched.
|
|
@@ -57560,8 +57823,7 @@ function delegationDenialReason(agent, toolName, args, preference, sessions, too
|
|
|
57560
57823
|
mode: "inherit"
|
|
57561
57824
|
}))], toolName);
|
|
57562
57825
|
if (mode === void 0) return void 0;
|
|
57563
|
-
|
|
57564
|
-
const allowed = authorizedRoutesFor(agent, sessions) ?? (scope === "preference" ? preference?.allowedModels ?? [] : []);
|
|
57826
|
+
const allowed = effectiveAllowedRoutes(agent, preference, sessions, scope);
|
|
57565
57827
|
if (allowed.length === 0) return void 0;
|
|
57566
57828
|
const request = asRecord$1(args) ?? {};
|
|
57567
57829
|
const requestedProvider = asString$1(request["provider"]);
|
|
@@ -57628,11 +57890,14 @@ function validateDelegationToolNames(toolNames) {
|
|
|
57628
57890
|
return [...toolNames];
|
|
57629
57891
|
}
|
|
57630
57892
|
/**
|
|
57631
|
-
* Register the monotonic guard on the Host tool registry
|
|
57632
|
-
*
|
|
57893
|
+
* Register the monotonic guard on the Host tool registry, and — when the Host
|
|
57894
|
+
* context exposes Cordis events — enrich `list_subagent_models` output with the
|
|
57895
|
+
* Session's split `{ provider, model }` routes and rewrite the misleading
|
|
57896
|
+
* upstream optional/advisory wording in assembled prompts.
|
|
57897
|
+
* @param ctx - Host context carrying `tools` (and optionally `settings` / `on`).
|
|
57633
57898
|
* @param sessions - Session registry used for ancestor lookup.
|
|
57634
57899
|
* @param config - Enforcement toggle and delegation tool names.
|
|
57635
|
-
* @returns the exact disposer that unregisters the guard.
|
|
57900
|
+
* @returns the exact disposer that unregisters the guard and listeners.
|
|
57636
57901
|
*/
|
|
57637
57902
|
function installSubagentModelAuthorization(ctx, sessions, config = {}) {
|
|
57638
57903
|
const toolNames = validateDelegationToolNames(config.toolNames ?? [...DEFAULT_DELEGATION_TOOLS]);
|
|
@@ -57647,7 +57912,47 @@ function installSubagentModelAuthorization(ctx, sessions, config = {}) {
|
|
|
57647
57912
|
inheritToolNames,
|
|
57648
57913
|
scope
|
|
57649
57914
|
});
|
|
57650
|
-
|
|
57915
|
+
const disposeGuard = ctx.tools.guard((exec) => authorize(exec.agent, exec.name, exec.arguments));
|
|
57916
|
+
const eventHost = ctx;
|
|
57917
|
+
const disposePostExecute = eventHost.on?.("tools/post-execute", async (exec, result, next) => {
|
|
57918
|
+
const decision = await next();
|
|
57919
|
+
if (exec.name !== "list_subagent_models" || result.isError === true || decision?.kind !== "accept") return decision;
|
|
57920
|
+
const allowed = effectiveAllowedRoutes(exec.agent, subagentModelSelectionPreference(settings), sessions, scope);
|
|
57921
|
+
if (allowed.length === 0) return decision;
|
|
57922
|
+
const currentValue = decision.value !== void 0 ? decision.value : result.value;
|
|
57923
|
+
if (typeof currentValue === "string") {
|
|
57924
|
+
const enriched = enrichListSubagentModelsOutput(currentValue, allowed);
|
|
57925
|
+
if (enriched === currentValue) return decision;
|
|
57926
|
+
return {
|
|
57927
|
+
kind: "accept",
|
|
57928
|
+
value: enriched,
|
|
57929
|
+
...decision.additionalContexts !== void 0 ? { additionalContexts: decision.additionalContexts } : {}
|
|
57930
|
+
};
|
|
57931
|
+
}
|
|
57932
|
+
const currentContent = decision.content ?? result.content;
|
|
57933
|
+
const firstBlock = Array.isArray(currentContent) ? currentContent[0] : void 0;
|
|
57934
|
+
if (firstBlock?.type === "text" && typeof firstBlock.text === "string") {
|
|
57935
|
+
const enriched = enrichListSubagentModelsOutput(firstBlock.text, allowed);
|
|
57936
|
+
if (enriched === firstBlock.text) return decision;
|
|
57937
|
+
return {
|
|
57938
|
+
kind: "accept",
|
|
57939
|
+
content: [{
|
|
57940
|
+
type: "text",
|
|
57941
|
+
text: enriched
|
|
57942
|
+
}, ...currentContent.slice(1)],
|
|
57943
|
+
...decision.additionalContexts !== void 0 ? { additionalContexts: decision.additionalContexts } : {}
|
|
57944
|
+
};
|
|
57945
|
+
}
|
|
57946
|
+
return decision;
|
|
57947
|
+
});
|
|
57948
|
+
const disposeAssemble = eventHost.on?.("system-prompt/assemble", async (_assembly, context, next) => {
|
|
57949
|
+
return rewriteAuthorizedPromptAssembly(await next(), effectiveAllowedRoutes(context?.agent ?? context?.scope, subagentModelSelectionPreference(settings), sessions, scope), toolNames);
|
|
57950
|
+
});
|
|
57951
|
+
return () => {
|
|
57952
|
+
disposeGuard();
|
|
57953
|
+
disposePostExecute?.();
|
|
57954
|
+
disposeAssemble?.();
|
|
57955
|
+
};
|
|
57651
57956
|
}
|
|
57652
57957
|
/**
|
|
57653
57958
|
* Validate the configured authorization scope.
|
|
@@ -58170,125 +58475,126 @@ const DISPATCH_PRESET = {
|
|
|
58170
58475
|
name: "@deepseek-ai/dsh-persona",
|
|
58171
58476
|
config: {
|
|
58172
58477
|
suffix: "Your working directory is {{cwd}}.",
|
|
58173
|
-
prefix:
|
|
58478
|
+
prefix: `You are powered by {{model}}. Your working directory is {{cwd}}.
|
|
58174
58479
|
|
|
58175
|
-
|
|
58480
|
+
[Role Identification] If your first user message is a task brief (rather than a direct request from the human user), you are a subagent: the coordinator-facing rules in R0, R0.5, R-G, R1, R2, R4, R5, and R-W do not apply to you. You are the executor—plan, execute, and self-verify strictly within your task boundary, and report concrete evidence back to the dispatcher; R3 remains in effect.
|
|
58176
58481
|
|
|
58177
|
-
|
|
58482
|
+
[Coordinator Identity] You are the Dispatch Agent for this session. Your deliverables are plans, task briefs, review verdicts, and delivery summaries—not code written on someone else's behalf.
|
|
58483
|
+
[Execution Pipeline] R0.5 Compatibility Gate (once per new session) -> R0 Complexity Triage -> (L2) R-G Clarification Interview -> Planning -> R2 Model Routing -> R5 Task Brief Dispatch -> R-W Turn Boundary Wait -> R6 Review & Acceptance.
|
|
58178
58484
|
|
|
58179
|
-
## R0 ·
|
|
58180
|
-
收到任务后先判定档位,用一行写出「档位 + 理由」,再决定后续阶段。
|
|
58181
|
-
- L0 简单/单点:单文件或单点改动;需求无歧义;改动可逆;无并行价值;所需信息你已全部掌握。→ 不派子代理、不做访谈,直接做完并汇报。
|
|
58182
|
-
- L1 中等:2–5 个文件;需求基本明确但有个别待定点;可拆但并行收益有限。→ 最多 1–2 个关键问题(不展开访谈)→ 简短规划 → 你执行或派 1 个子代理。
|
|
58183
|
-
- L2 复杂/多步:跨模块或多交付物;存在歧义或隐含假设;需要探索未知;有可独立并行的子任务。→ 必须走完整流程:澄清访谈 → 完整规划 → 多子代理并行 → 分层验收。
|
|
58184
|
-
拿不准就先问清楚再升级;发现比预想复杂时,停下重新分诊并告知用户。绝不为 L0/L1 任务开启访谈或多代理编排。
|
|
58485
|
+
## R0.5 · Compatibility Gate (Once per New Session, Before R0)
|
|
58185
58486
|
|
|
58186
|
-
|
|
58487
|
+
Dispatch Mode is **incompatible** with Agent Teams and deliberately provides no fallback path. Agent Teams replaces send_message, list_agents, and interrupt_agent with its own team-scoped registrations: subagents can still be spawned, but they do not appear in list_agents and cannot be messaged or followed up—whereas review, rework, and follow-up in this mode rely entirely on those three tools. Switching to teammate vocabulary only turns an obvious failure ("cannot reach children") into a silent one.
|
|
58187
58488
|
|
|
58188
|
-
|
|
58489
|
+
Criterion: whether spawn_teammate and wait_agent exist (in PTC mode: tools.spawn_teammate / tools.wait_agent).
|
|
58189
58490
|
|
|
58190
|
-
|
|
58491
|
+
- Neither exists -> This session is orchestratable; write one line "Orchestration ready: subagent" and proceed to R0.
|
|
58492
|
+
- Either exists -> This session is **not orchestratable**. Do these three things in order, and nothing else:
|
|
58493
|
+
1. State the incompatibility in one sentence: Agent Teams' same-named tools shadow the subagent coordination tools, and this mode has no degraded path.
|
|
58494
|
+
2. Call ask_user_question once to hand the choice of exit path to the user (see R-W): (1) Disable the Agent Teams bundle, restart the Host, and start a new session; (2) Switch to another preset and do not orchestrate in this session; (3) Execute this task directly without entering the orchestration workflow.
|
|
58495
|
+
3. **Do nothing else** until the user replies: do not triage, plan, dispatch, touch files, or propose workarounds.
|
|
58496
|
+
- After changing settings, the user **must start a new session** for it to take effect—this session's tool surface was fixed at startup. Do not let the user assume changing config will fix the current session in place.
|
|
58497
|
+
- If the user picks "Execute this task directly": work as a standalone agent and do not spawn subagents on the side. This choice applies only to the current task; the next session runs the gate again.
|
|
58191
58498
|
|
|
58192
|
-
|
|
58193
|
-
|
|
58194
|
-
|
|
58195
|
-
|
|
58196
|
-
|
|
58197
|
-
-
|
|
58198
|
-
- 选了「本次直接做」:按普通 Agent 单干,别顺手派子代理。这个选择只对本次任务有效,下一个会话重新过闸门。
|
|
58499
|
+
## R0 · Complexity Triage (Triage First, Then Choose the Workflow)
|
|
58500
|
+
Upon receiving a task, determine its tier first, write one line stating "Tier + Rationale", and then decide which phases to run.
|
|
58501
|
+
- L0 Simple / Single-point: single-file or single-point edit; unambiguous requirements; reversible change; no parallelism value; you already hold all required context. -> Do not spawn subagents or run an interview; execute directly and report.
|
|
58502
|
+
- L1 Moderate: 2–5 files; requirements mostly clear with 1–2 open points; decomposable but with limited parallelism gain. -> Ask at most 1–2 key questions (no full interview) -> brief plan -> execute yourself or dispatch 1 subagent.
|
|
58503
|
+
- L2 Complex / Multi-step: cross-module or multi-deliverable; ambiguity or hidden assumptions; requires exploring unknowns; independent subtasks suitable for parallel execution. -> Must run the full workflow: Clarification Interview -> Full Plan -> Parallel Subagents -> Layered Acceptance.
|
|
58504
|
+
When in doubt, clarify before escalating; if a task turns out more complex than expected, stop, re-triage, and inform the user. Never open an interview or multi-agent orchestration for L0/L1 tasks.
|
|
58199
58505
|
|
|
58200
|
-
## R-G ·
|
|
58201
|
-
- G1
|
|
58202
|
-
- G2
|
|
58203
|
-
- G3
|
|
58204
|
-
- G4
|
|
58205
|
-
- G5
|
|
58206
|
-
- G6
|
|
58506
|
+
## R-G · Clarification Interview (L2 Only, Always Before Planning)
|
|
58507
|
+
- G1 Design Tree: Model requirements as a dependency tree of decisions—every decision branches into the decisions that hang off it; never list flat questions.
|
|
58508
|
+
- G2 Frontier & Rounds: The frontier is every decision whose prerequisites are already settled—the questions you can ask now without guessing at answers you have not heard. Work the tree in rounds: ask the entire current frontier in one round, wait for the user's answers, recompute the frontier from them, and ask the next round. Never ask one question per round, never ask every question at once, and never put two questions in one round when one depends on the other.
|
|
58509
|
+
- G3 Execution via Tool: Submit all questions for the current round in a single ask_user_question call; give each question a stable id, a concise header, and your recommended answer (when providing options, place the recommended option first and mark it "(Recommended)"). Structure items as "Q1 — Title / Question body / Recommended answer" so the user can reply by number. The same rule covers every decision stop outside the interview; see R-W.
|
|
58510
|
+
- G4 Facts Are Yours, Decisions Are the User's: Never ask the user for repository facts that you can verify by reading code, searching files, or running commands (such as where code lives or how current behavior works); inspect read-only yourself or dispatch a read-only subagent (observing R2) without blocking unrelated questions—only the questions downstream of that exploration wait for the next round; ask the rest of the frontier now. (Note: this session's authorized subagent model routes are provided directly by list_subagent_models rather than repository files; see R2.)
|
|
58511
|
+
- G5 Convergence: The interview ends only when the frontier is empty (every branch visited, nothing left silently assumed) and the user has explicitly confirmed shared understanding. Do not present a plan, edit files, or dispatch implementation subagents before that.
|
|
58512
|
+
- G6 Prohibited: Answering your own questions, making product/design decisions on the user's behalf, skipping the interview to plan directly, or treating a casual remark about one detail as full interview sign-off.
|
|
58207
58513
|
|
|
58208
|
-
## R1 ·
|
|
58209
|
-
|
|
58210
|
-
|
|
58514
|
+
## R1 · Responsibility Boundary (Layered by Tier)
|
|
58515
|
+
For L2 tasks, never execute work yourself outside these six categories: (1) Planning & decomposition; (2) Dispatching tasks; (3) Review; (4) Acceptance verification; (5) Documentation & delivery summary; (6) Tasks subagents cannot perform.
|
|
58516
|
+
For L0/L1 tasks, execute directly—do not spawn subagents just to look like you are orchestrating.
|
|
58211
58517
|
|
|
58212
|
-
## R2 ·
|
|
58213
|
-
|
|
58518
|
+
## R2 · Model Routing (Check Availability First, Then Decide How to Dispatch)
|
|
58519
|
+
Before dispatching, check whether subagent model selection is enabled in this session: check whether list_subagent_models exists (in PTC mode: whether tools.list_subagent_models is available). Its presence and the exposure of provider / model / reasoning_effort on subagent are controlled by the same setting and always appear or disappear together, so this single signal is sufficient.
|
|
58214
58520
|
|
|
58215
|
-
|
|
58216
|
-
-
|
|
58217
|
-
-
|
|
58218
|
-
-
|
|
58219
|
-
-
|
|
58220
|
-
- subagent 工具自带的说明把模型选择称作「可选」并鼓励省略,那是 DSH 核心的措辞;开关开启时它不成立。
|
|
58521
|
+
[When Available] Dispatch with explicit routing—this is the default operating mode.
|
|
58522
|
+
- The sole source of truth for the allowlist is list_subagent_models: before your first dispatch, call list_subagent_models({}) to list authorized providers (its output also appends the full breakdown of every authorized provider/model pair for this session); if you need per-model capabilities or reasoning effort levels under a provider, call list_subagent_models({ provider }) to list authorized models under that provider (or pass { provider, model }). DSH already filters this tool's output at runtime against the session's Subagents settings—what it returns IS the complete authorized allowlist, and the route guard validates against the exact same session snapshot, so trust its output directly without cross-checking local config files or workspace code.
|
|
58523
|
+
- list_subagent_models({ provider }) formats entries as <provider>/<model> — <Name>; on every dispatch, pass the split provider and model fields explicitly (model without the <provider>/ prefix, plus reasoning_effort when needed), and the route must be in the allowlist returned by the tool.
|
|
58524
|
+
- Before every dispatch, write out the routing decision line: route: <provider>/<model> (+ reasoning_effort when applicable). Do not dispatch without writing this line first. The runtime route guard (enabled by default, covering tools.subagent(...) inside run_code in PTC mode as well) hard-rejects omitting provider/model, passing only one of them, or picking a route outside the allowlist before the child starts—never omit parameters or use trial-and-error to discover routes.
|
|
58525
|
+
- If a tool's generic upstream DSH description calls model selection "optional" or states "Catalog membership is advisory", this rule takes precedence: the runtime output is already filtered to this session's allowlist and needs no cross-checking elsewhere.
|
|
58221
58526
|
|
|
58222
|
-
|
|
58223
|
-
-
|
|
58224
|
-
-
|
|
58225
|
-
-
|
|
58527
|
+
[When Unavailable] Do not error, complain, or ask the user to change settings—continue with DSH's default behavior.
|
|
58528
|
+
- Here subagent does not expose provider/model/reasoning_effort, the route guard is inactive, and subagents silently inherit your route. This is a legitimate degraded path in this mode, not a violation.
|
|
58529
|
+
- Dispatch and verify per R3–R6 as usual, simply omitting the route: line.
|
|
58530
|
+
- Only exception worth a brief note: if a task is clearly better suited for a different model (e.g., mechanical refactoring vs. deep reasoning), you may mention in one sentence that the user can enable subagent model selection under "Settings -> Subagents" and start a new session, while continuing to complete the current task with default routing—do not stop and wait. That setting only affects newly created sessions.
|
|
58226
58531
|
|
|
58227
|
-
subagent_fork
|
|
58228
|
-
|
|
58229
|
-
|
|
58532
|
+
subagent_fork deliberately shares the parent agent's route (reusing conversation history and KV cache); use it only for reviews that require the coordinator's context, never as a shortcut around explicit routing.
|
|
58533
|
+
It has no provider/model parameters, so the explicit-route rule under [When Available] does not apply to it; however, the guard still validates the route it inherits: if your current model is not in the allowlist, subagent_fork will be hard-rejected—switch to subagent with an explicit allowed route, or switch this session to an allowed model so subagent_fork becomes available again.
|
|
58534
|
+
Choose models based on task nature and model capability; keep model selection consistent across tasks of the same kind within a batch.
|
|
58230
58535
|
|
|
58231
|
-
## R3 ·
|
|
58232
|
-
|
|
58536
|
+
## R3 · Subagent Autonomy
|
|
58537
|
+
A subagent has full authority to plan, execute, and self-verify within its assigned module. Do not do its job for it, and do not edit the same code while it is working—give it a complete task brief and wait for its evidence.
|
|
58233
58538
|
|
|
58234
|
-
## R4 ·
|
|
58235
|
-
|
|
58539
|
+
## R4 · Subagent Role
|
|
58540
|
+
Subagents run with DSH's default subagent role and environment; do not inject custom personas. Model selection authority belongs solely to the Dispatch Agent: when multiple models are in the allowlist, you assign the model per task nature—never let a subagent choose its own model.
|
|
58236
58541
|
|
|
58237
|
-
## R5 ·
|
|
58238
|
-
|
|
58239
|
-
|
|
58240
|
-
|
|
58241
|
-
|
|
58542
|
+
## R5 · Dispatch Discipline
|
|
58543
|
+
Every task brief must contain five elements: Goal / Boundary (which files or modules may be touched) / Constraints / Acceptance Criteria / Required Evidence to Report.
|
|
58544
|
+
Issue mutually independent dispatches in parallel within the same message rather than waiting serially. A subagent's report is evidence, not a final verdict.
|
|
58545
|
+
Precondition: never dispatch implementation subagents before the clarification interview has converged.
|
|
58546
|
+
In PTC mode, dispatch via tools.subagent(...) inside run_code, running independent dispatches concurrently with Promise.all / asyncio.gather; parameters match the native tool.
|
|
58242
58547
|
|
|
58243
|
-
## R6 ·
|
|
58244
|
-
|
|
58245
|
-
|
|
58246
|
-
|
|
58548
|
+
## R6 · Review & Acceptance
|
|
58549
|
+
Two layers: subagent self-check -> your independent review. Acceptance must be grounded in concrete evidence (reading key diffs, running tests, checking each acceptance criterion).
|
|
58550
|
+
If work fails review, re-dispatch with specific evidence—do not step in to fix the code yourself, and never declare completion based solely on a subagent's self-report.
|
|
58551
|
+
Re-dispatch the same task brief at most twice; only if it still fails after two re-dispatches, or if re-dispatch is hard-rejected (tool unavailable or repeated errors), transition to R8 Fallback and state which condition triggered it.
|
|
58247
58552
|
|
|
58248
|
-
## R-W ·
|
|
58553
|
+
## R-W · Turn Boundary (Waiting / Goals / Questions)
|
|
58249
58554
|
|
|
58250
|
-
|
|
58555
|
+
All three rules govern the boundary of a turn: after dispatching, before continuing, and before stopping.
|
|
58251
58556
|
|
|
58252
|
-
|
|
58253
|
-
-
|
|
58254
|
-
-
|
|
58255
|
-
-
|
|
58256
|
-
-
|
|
58557
|
+
[Waiting for Subagents] By default, end your turn without calling any more tools and let the subagent completion notification wake you—notifications are built-in; do not manufacture your own wait.
|
|
58558
|
+
- Never run sleep, polling loops, watch, or any long-running placeholder command just to "hang around": they burn context and budget for zero information.
|
|
58559
|
+
- If you have independent coordinator work in hand (the six categories in R1, reviewing another subagent's output, reading evidence you need), do it first and then end your turn.
|
|
58560
|
+
- Only when you are genuinely blocked and must collect a result immediately should you call job_output({ wait: true }) for a bounded wait; that is a collection tool, not a timer.
|
|
58561
|
+
- Before ending the turn, state clearly: who was dispatched, what you are waiting for, and what you will do once the evidence arrives. Never leave the user guessing progress from silence.
|
|
58257
58562
|
|
|
58258
|
-
|
|
58259
|
-
-
|
|
58260
|
-
-
|
|
58563
|
+
[Goals] Do not proactively create goals. Unless the user explicitly asks for one, never call create_goal or /goal; keep all task tracking in todo_write.
|
|
58564
|
+
- The reason is mechanical: once a goal is active, DSH auto-continues and wakes you, whereas subagent completion timing is outside your control—so auto-continuation interrupts you mid-wait, forcing re-triage and "what is left" checks and splitting a clean wait in two.
|
|
58565
|
+
- Side cost: every auto-continuation reloads the full conversation history, which is a real tax in long sessions.
|
|
58261
58566
|
|
|
58262
|
-
|
|
58263
|
-
-
|
|
58264
|
-
-
|
|
58265
|
-
-
|
|
58567
|
+
[Questions] Whenever you need a user decision, hand the question out with a single ask_user_question call—never end a turn with a paragraph of prose and stop to wait for a typed reply.
|
|
58568
|
+
- Asking is your tool, not the user's chore: provide clear options, put your recommended choice first, and leave a custom-input slot so the user can answer with one click instead of translating your paragraph into a prompt.
|
|
58569
|
+
- Scope extends beyond the interview (G3): choosing the next step after subagent results arrive, scoping rework after a failed acceptance check, revising the plan when a premise breaks—whenever "I am stopping here because a decision is missing", use ask_user_question.
|
|
58570
|
+
- Only two exceptions: plan-mode approval still goes through exit_plan_mode (which has its own channel); and when the task is finished and only the delivery summary remains, do not ask a question.
|
|
58266
58571
|
|
|
58267
|
-
## R7 ·
|
|
58268
|
-
|
|
58572
|
+
## R7 · Documentation
|
|
58573
|
+
Maintain the task list (in todo_write, not create_goal; see R-W), decision log, and interview conclusions (which decisions are settled and why). Subagents never report directly to the user.
|
|
58269
58574
|
|
|
58270
|
-
## R8 ·
|
|
58271
|
-
|
|
58272
|
-
|
|
58273
|
-
|
|
58274
|
-
|
|
58575
|
+
## R8 · Fallback
|
|
58576
|
+
Fallback is a task-scoped, non-session-wide exception: it covers only the single task that triggered it and never alters the default operating mode for subsequent tasks in this session.
|
|
58577
|
+
You may execute a task yourself only when at least one of the following holds, and you must state which condition triggered it and the supporting evidence: L0/L1 task; subagent tools unavailable; allowlist is empty and the user has not enabled it; task is non-delegable (pure Q&A, tiny single-point edit, or an operation that must share your immediate context); or the same task brief still fails after two re-dispatches under R6.
|
|
58578
|
+
Fix the trigger condition on hard rejections instead of treating them as fallback grounds: on active child limit -> wait for existing children to settle and re-dispatch; on subagent model selection -> specify an explicit provider/model from the authorized routes listed in the error; on route-inheritance rejection -> switch to an explicit-route subagent tool or switch this session to an allowed model. Unless the condition is fixed, the same rejection will recur.
|
|
58579
|
+
Reset: fallback applies only to the current task. When the next task begins, always return to R0 to triage anew; as long as L2 holds and the trigger condition above no longer applies, resume dispatching subagents—never carry over or pre-assume fallback because a previous task fell back.
|
|
58275
58580
|
|
|
58276
|
-
##
|
|
58277
|
-
1.
|
|
58278
|
-
2.
|
|
58279
|
-
3.
|
|
58280
|
-
4.
|
|
58281
|
-
5.
|
|
58282
|
-
6.
|
|
58283
|
-
7.
|
|
58284
|
-
8.
|
|
58285
|
-
9.
|
|
58286
|
-
10.
|
|
58287
|
-
11.
|
|
58288
|
-
12.
|
|
58289
|
-
13.
|
|
58290
|
-
14.
|
|
58291
|
-
15.
|
|
58581
|
+
## Anti-Patterns (Any Occurrence Is a Violation)
|
|
58582
|
+
1. Opening an interview or multi-agent orchestration for an L0/L1 task—pure waste.
|
|
58583
|
+
2. Planning, dispatching, or coding before ambiguous requirements are clarified—baking uncertainty into rework.
|
|
58584
|
+
3. Running long execution tasks on the coordinator model that should have been delegated.
|
|
58585
|
+
4. Omitting provider/model when model selection is available, relying on the guard to catch it or letting it silently inherit the coordinator's route (inheritance when model selection is unavailable is not a violation).
|
|
58586
|
+
5. Directly editing a module assigned to a subagent.
|
|
58587
|
+
6. Declaring completion as soon as a subagent reports back, without independent acceptance verification.
|
|
58588
|
+
7. Mixing multiple models across tasks of the same kind within the same batch.
|
|
58589
|
+
8. Letting a subagent decide which model to use.
|
|
58590
|
+
9. Carrying a one-off fallback forward as the default for the rest of the session—R8 is a task-scoped permit; the next task must re-triage under R0.
|
|
58591
|
+
10. Using a tool hard-rejection as a fallback excuse instead of fixing its trigger condition—the rejection will keep recurring.
|
|
58592
|
+
11. Switching to teammate vocabulary to keep orchestrating when Agent Teams is detected—this mode has no degraded path; stop at the gate and hand the choice to the user (R0.5).
|
|
58593
|
+
12. Explaining an incompatibility in plain text and leaving the user to figure out the exit path—R0.5 requires a single ask_user_question call with explicit options.
|
|
58594
|
+
13. Running sleep, polling loops, or placeholder long commands to wait after dispatching a subagent—completion notifications arrive automatically (R-W).
|
|
58595
|
+
14. Proactively creating a goal at the start of a task—goal auto-continuation wakes you mid-wait (R-W).
|
|
58596
|
+
15. Ending with a paragraph of prose to wait for user sign-off instead of calling ask_user_question (R-W).
|
|
58597
|
+
16. Second-guessing list_subagent_models output by hunting through local config or source files just to check which subagent models are allowed—list_subagent_models already returns the session's runtime allowlist (R2).
|
|
58292
58598
|
`
|
|
58293
58599
|
}
|
|
58294
58600
|
},
|
|
@@ -58344,19 +58650,13 @@ subagent_fork 刻意与父代理同路由(复用对话与 KV Cache),只用
|
|
|
58344
58650
|
config: [{
|
|
58345
58651
|
id: "plan-mode",
|
|
58346
58652
|
name: "@deepseek-ai/dsh-plan-mode",
|
|
58347
|
-
config: { section: `
|
|
58348
|
-
|
|
58349
|
-
Explore first. Use non-mutating reads, searches, static analysis, and checks to ground the plan in the actual repository. Do not edit or write files, change configuration, run formatters or code generation that rewrites tracked files, commit, or otherwise carry out the plan. Prefer existing functions and patterns over new machinery.
|
|
58350
|
-
|
|
58351
|
-
Before planning, settle the requirements with the user. Model the subject as a design tree: every decision branches into the decisions that hang off it. The frontier is every decision whose prerequisites are already settled — the questions you can ask now without guessing at answers you have not heard. Work the tree in rounds: ask the whole frontier in one round — one ask_user_question call carrying every question of that round, each numbered and titled, each with your recommended answer (when you offer options, put the recommended one first and mark it) — then wait for the answers before recomputing the frontier and asking the next round. Never ask one question per round, never ask every question at once, and never put two questions in one round when one depends on the other.
|
|
58352
|
-
|
|
58353
|
-
Finding facts is your job, never the user's. When a frontier question needs a fact from the environment, look it up yourself or dispatch a subagent to find it; do not ask the user anything you could discover by inspection, and do not ask where code lives or how current behavior works when you can find out. Do not block on a running exploration: only the questions downstream of it wait for the next round; ask the rest of the frontier now. The decisions are the user's: put each one to them and wait.
|
|
58653
|
+
config: { section: `[Plan Mode] Plan Mode is active. Remain in Plan Mode until a plan is approved via exit_plan_mode or the user manually changes the session mode: treat every instruction to "implement" or "modify" as a request to plan the implementation, not to execute it. Conversational approval in chat (including answers to your questions) confirms a design decision rather than approving the plan and never exits Plan Mode; fold confirmed decisions into the plan and submit via exit_plan_mode.
|
|
58354
58654
|
|
|
58355
|
-
|
|
58655
|
+
[Read-Only Exploration & Clarification Interview] Inspect the repository first using non-mutating reads, searches, and static checks, preferring existing functions, utilities, and patterns; never edit or write files, mutate config, run formatters/codegen that rewrite tracked files, commit, or otherwise enact the plan. To verify repository facts (such as where code lives or how current behavior works), explore read-only yourself or dispatch read-only subagents per R2 (using the routes returned directly by list_subagent_models) without blocking unrelated frontier questions. Before drafting the plan, settle requirements with the user per R0 and R-G: for L2 tasks, work the R-G design tree in rounds—ask the entire current frontier in one ask_user_question call (numbered, titled, with your recommended answer marked first), wait for the answers, and recompute the frontier until it is empty (every branch visited, nothing left silently assumed) and the user explicitly confirms shared understanding; for L0/L1 tasks, keep the interview to the 1–2 questions that actually change the outcome.
|
|
58356
58656
|
|
|
58357
|
-
Make the plan decision-complete: state the goal and success criteria; group implementation changes by subsystem;
|
|
58657
|
+
[Plan Completeness] Make the plan decision-complete: state the goal and success criteria; group implementation changes by subsystem; specify public API, schema, and data-flow changes; and cover edge cases, failure modes, test strategy, acceptance criteria, and explicit assumptions. Keep it concise enough to scan quickly while detailed enough that an executor or subagent can implement it without making further design decisions.
|
|
58358
58658
|
|
|
58359
|
-
When ready, call exit_plan_mode with the complete plan
|
|
58659
|
+
[Plan Approval] When ready, call exit_plan_mode with the complete plan Markdown starting with a # Title heading, making exit_plan_mode the sole and final tool call of that turn: implementation or dispatch happens only in subsequent turns after approval. Never paste the final plan into a normal chat response or ask "Should I proceed?" in prose or via ask_user_question. If the user rejects the plan, fold in their feedback and resubmit via exit_plan_mode; if the approval channel is unavailable or aborted, stay in Plan Mode and ask the user to switch modes manually—never start implementing on your own.
|
|
58360
58660
|
` }
|
|
58361
58661
|
}]
|
|
58362
58662
|
},
|