@mars-sea/dsh-commandcode-provider 0.12.0 → 0.12.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/lib/index.d.ts CHANGED
@@ -6,8 +6,30 @@ import { WebRuntime, WebSearchProvider, WebSearchRequest, WebSearchResult } from
6
6
  import { Context } from "@deepseek-ai/cordis";
7
7
  import { AttachmentStore } from "@deepseek-ai/dsh-attachment";
8
8
  import { CommandDefinition } from "@deepseek-ai/dsh-commands";
9
+ //#region src/request-timing.d.ts
10
+ type Phase = 'credentials' | 'images' | 'body' | 'serialization' | 'headers';
11
+ type Outcome = 'finished' | 'error' | 'aborted' | 'cancelled';
12
+ interface RequestTimingSummary {
13
+ version: 1;
14
+ startedAt: number;
15
+ model: string;
16
+ outcome: Outcome;
17
+ errorCode?: string;
18
+ totalMs: number;
19
+ phasesMs: Record<Phase, number>;
20
+ firstByteMs?: number;
21
+ firstContentMs?: number;
22
+ attempts: Array<{
23
+ protocol: string;
24
+ bodyBytes: number;
25
+ headersMs: number;
26
+ status?: number;
27
+ }>;
28
+ }
29
+ type RequestTimingSink = (summary: RequestTimingSummary) => void | Promise<void>;
30
+ //#endregion
9
31
  //#region src/adapter.d.ts
10
- declare const COMMAND_CODE_CLI_VERSION = "1.68.0";
32
+ declare const COMMAND_CODE_CLI_VERSION = "1.73.0";
11
33
  declare const DEFAULT_API_BASE = "https://api.commandcode.ai";
12
34
  /**
13
35
  * The output budget this bundle asks for, and the ceiling it will never exceed.
@@ -25,9 +47,16 @@ declare const DEFAULT_API_BASE = "https://api.commandcode.ai";
25
47
  * (a 64 000 request answered headers in 1.28 s against 1.57 s for 131 072) but
26
48
  * it did cap what a model could ever say in one turn.
27
49
  *
28
- * This is only an UPPER bound. `requestContextBudget` lowers it per request
29
- * from the prompt actually being sent, so raising the ceiling cannot push a
30
- * large session over its window.
50
+ * No client-side clamp is applied on top of this: an earlier version pre-shrank
51
+ * `max_tokens` from an estimate of the prompt already sent (`requestContextBudget()`,
52
+ * issue #67), but that estimate over-corrected on ordinary long conversations —
53
+ * exactly the "output gets cut short as the conversation grows" symptom issue #74
54
+ * reported — while a live A/B probe (2026-09-30, `stealth/space-bunny-alpha`,
55
+ * ~19.6% over its 1,000,000-token window with no client-side shrink) showed the
56
+ * endpoint answers a genuine overflow with an unambiguous `400 … exceeds the
57
+ * model's context window`, which `isContextOverflowDetail()` below already
58
+ * classifies as `CONTEXT_WINDOW_EXCEEDED` independently of this constant. See
59
+ * [决策记录](../docs/决策记录.md).
31
60
  */
32
61
  declare const DEFAULT_GENERATE_MAX_TOKENS = 131072;
33
62
  /**
@@ -42,8 +71,18 @@ declare const DEFAULT_GENERATE_MAX_TOKENS = 131072;
42
71
  declare const DEFAULT_MAX_OUTPUT_TOKENS = 131072;
43
72
  /** How long the picker's plan-filter billing facts stay cached before refetching. */
44
73
  declare const BILLING_ACCESS_TTL_MS: number;
45
- /** Endpoint protocol selected for one generate call. */
46
- type CommandCodeProtocol = 'cli' | 'openai';
74
+ /**
75
+ * Endpoint protocol selected for one generate call.
76
+ *
77
+ * `messages` is the Anthropic Messages surface at
78
+ * `{apiBase}/provider/v1/messages`, the only Provider API route the Claude
79
+ * family answers (posted to `/chat/completions` they refuse with `400 Model
80
+ * "<id>" must be called via /provider/v1/messages`). `openai` is the
81
+ * documented Chat Completions surface; `cli` is Command Code's private
82
+ * `/alpha/generate` transport, kept for Go-plan keys (the one plan without
83
+ * Provider API access) and as the `upgrade_required` fallback.
84
+ */
85
+ type CommandCodeProtocol = 'cli' | 'openai' | 'messages';
47
86
  /**
48
87
  * Head-of-request timeout: how long to wait for the first response byte.
49
88
  *
@@ -181,6 +220,8 @@ interface AccountRotationContext {
181
220
  }
182
221
  /** Everything the adapter needs beyond the request itself. */
183
222
  interface CommandCodeAdapterDeps<C extends CommandCodeConnectionOptions = CommandCodeConnectionOptions> {
223
+ /** 可选的数值耗时接收器;不接收提示词、凭据或错误正文。 */
224
+ onRequestTiming?: RequestTimingSink;
184
225
  /** Resolve the current connection facts (fresh per request, settings-aware). */
185
226
  options: () => C;
186
227
  /**
@@ -326,6 +367,7 @@ interface CommandCodeUsageReport {
326
367
  declare class CommandCodeAdapter<C extends CommandCodeConnectionOptions = CommandCodeConnectionOptions> extends LlmAdapter {
327
368
  private readonly deps;
328
369
  private catalog;
370
+ private catalogState;
329
371
  private readonly fetchImpl;
330
372
  private readonly resolveAttachments;
331
373
  /**
@@ -336,24 +378,32 @@ declare class CommandCodeAdapter<C extends CommandCodeConnectionOptions = Comman
336
378
  private readonly billingAccess;
337
379
  private readonly billingAccessInflight;
338
380
  private readonly protocolCache;
381
+ /** Namespace account-derived facts by gateway without exposing the key. */
382
+ private accountCacheKey;
339
383
  /**
340
384
  * Consecutive header timeouts for the last request shape, counted ACROSS
341
385
  * `stream()` calls: dsh-llm-retry re-invokes this same instance, so this is
342
- * the only place a streak can live. One slot rather than a map is the point —
343
- * the claim is "consecutive", and a single record cannot grow.
344
- */
345
- private headerTimeoutStreak;
386
+ * the only place a streak can live. Keyed by `sessionId` (not by `agent`:
387
+ * `GenerateOptions` carries no agent reference, only `sessionId`) so
388
+ * concurrent sessions on this one shared adapter instance cannot clear or
389
+ * inflate each other's count. A call with no `sessionId` (a one-shot,
390
+ * session-less caller) falls back to {@link GLOBAL_HEADER_TIMEOUT_KEY},
391
+ * matching the old single-slot behavior for exactly that case. Bounded by
392
+ * {@link MAX_HEADER_TIMEOUT_STREAK_ENTRIES} with FIFO eviction (`Map`
393
+ * preserves insertion order) since the key space is per-session rather than
394
+ * per-account/model, so it is not naturally small like `protocolCache`.
395
+ */
396
+ private readonly headerTimeoutStreaks;
397
+ private static readonly MAX_HEADER_TIMEOUT_STREAK_ENTRIES;
346
398
  constructor(deps: CommandCodeAdapterDeps<C>);
347
399
  /**
348
400
  * Grade one failed attempt: a headers timeout that repeats on an unchanged
349
401
  * payload eventually leaves the retryable whitelist.
350
402
  *
351
403
  * `providerRetryPolicy()` cannot express this itself — dsh-llm's
352
- * `NormalRetryPolicyConfig` carries a single scalar `maxRetries` next to the
353
- * code list, so there is no per-code limit to lower. Escalating from here is
354
- * the only way to keep `RATE_LIMIT` / `SERVER` / `TRANSPORT` retryable (each
355
- * carries a real "try again later" signal) while stopping the one code that,
356
- * repeated unchanged, only burns the user's session.
404
+ * `NormalRetryPolicyConfig` carries one `maxRetries` for every listed code.
405
+ * Escalating here stops repeated identical header timeouts with a specific
406
+ * diagnosis; the agent listener separately caps ordinary transient errors.
357
407
  *
358
408
  * @returns the error to surface: unchanged for any other failure, the
359
409
  * original timeout below the threshold, and a terminal one at it.
@@ -368,12 +418,11 @@ declare class CommandCodeAdapter<C extends CommandCodeConnectionOptions = Comman
368
418
  */
369
419
  providerInfo(provider: string): LlmProviderInfo;
370
420
  /**
371
- * Near-unbounded retry for transient failures only (`mode: 'normal'` with
372
- * an explicit 1000-attempt cap — opencode-style persistence without the
373
- * unbounded loop): `RATE_LIMIT`/`SERVER`/`TIMEOUT`/`TRANSPORT`/
374
- * `EMPTY_RESPONSE` retry up to 1000 times with waits doubling from 500 ms
375
- * and capping at 15 minutes (±10% jitter), so an exhausted 5-hour window
376
- * recovers in-session instead of failing after two tries. Permanent failures
421
+ * The official executor owns backoff and provider reset waits. Its high
422
+ * route-wide cap lets a confirmed `RATE_LIMIT` window recover in-session;
423
+ * our request-error listener limits `SERVER`/`TIMEOUT`/`EMPTY_RESPONSE`/
424
+ * `THROTTLED` to three retries and `TRANSPORT` to its separate budget.
425
+ * Waits double from 500 ms and cap at 15 minutes (±10% jitter). Permanent failures
377
426
  * (an invalid key's `INVALID_CREDENTIAL`, `UNSUPPORTED_CONTENT`, plan
378
427
  * rejections) are absent from the whitelist and surface immediately instead
379
428
  * of looping. Waits the pool/adapter attach as `providerRetryAfterMs` are
@@ -397,7 +446,11 @@ declare class CommandCodeAdapter<C extends CommandCodeConnectionOptions = Comman
397
446
  * requirement — the meter throws when the counts differ.
398
447
  */
399
448
  imageRequestPricing(_provider: string, model: string): LlmImageRequestPricing | undefined;
400
- /** Refresh the catalog (live fetch, cache fallback) and return it. */
449
+ /** 地址或缓存文件变化时丢弃旧目录,防止旧网关的路由声明影响新请求。 */
450
+ private currentCatalogState;
451
+ /** 直接生成也使用本地可信目录,不额外联网;并发冷请求共用一次文件读取。 */
452
+ private catalogForGenerate;
453
+ /** 共享一次刷新;调用者取消只结束自己的等待,不取消其他调用者的请求。 */
401
454
  private loadCatalog;
402
455
  listModels(provider: string, opts?: {
403
456
  unfiltered?: boolean;
@@ -406,9 +459,8 @@ declare class CommandCodeAdapter<C extends CommandCodeConnectionOptions = Comman
406
459
  * The per-model allowance bracket this account POOL falls into, or undefined
407
460
  * when there is nothing honest to show.
408
461
  *
409
- * The pricing page publishes a per-model monthly allowance for GOAT and Pro
410
- * only — its own words are "the boost is a per-model allowance, so it lives on
411
- * the plans that have them" — so a pool whose highest plan is Go, Provider,
462
+ * The pricing page publishes per-model monthly allowances for Go, GOAT and
463
+ * Pro. A pool whose highest plan is Provider,
412
464
  * Max or Ultra gets undefined rather than a neighbouring tier's figure. Taking
413
465
  * the HIGHEST tier matches the question the picker already answers for a pool
414
466
  * (see `modelVisibleForAnyAccount`): what the user can reach, not what the one
@@ -416,7 +468,7 @@ declare class CommandCodeAdapter<C extends CommandCodeConnectionOptions = Comman
416
468
  * too — an unreachable billing endpoint yields undefined, which hides the
417
469
  * allowance instead of inventing one.
418
470
  */
419
- allowanceTier(): Promise<'goat' | 'pro' | undefined>;
471
+ allowanceTier(): Promise<'go' | 'goat' | 'pro' | undefined>;
420
472
  resolveModel(provider: string, model: string, signal?: AbortSignal): Promise<LlmResolvedModelInfo>;
421
473
  /** The headers every authenticated account endpoint shares. */
422
474
  private accountHeaders;
@@ -466,25 +518,36 @@ declare class CommandCodeAdapter<C extends CommandCodeConnectionOptions = Comman
466
518
  /** Fresh cached billing tier weight for a key, or undefined when not known. */
467
519
  private cachedBillingTierWeight;
468
520
  /** Cached protocol decision for a key, or undefined when expired/unknown. */
469
- private cachedProtocolUseCli;
521
+ private cachedProtocol;
470
522
  private rememberProtocol;
471
523
  /**
472
- * Choose the initial protocol for one request. A fresh protocol cache entry
473
- * wins; otherwise a cached (not network-fetched) billing tier of Go is
474
- * treated as CLI-only. Unknown accounts default to Provider API and fall
475
- * back only after an `upgrade_required` rejection.
524
+ * True when the catalog routes `model` to `/provider/v1/messages`.
476
525
  *
477
- * Models the Provider API serves ONLY through `/provider/v1/messages` (the
478
- * Claude family — `requiresMessagesEndpoint()`) always take the CLI
479
- * transport, on any tier and even under a forced `'openai'` preference:
480
- * that option is documented as "prefer the Provider API, still fall back",
481
- * and the 400 these models answer with is not a preference to be honoured
482
- * but a hard refusal. `/alpha/generate` routes every one of them, so this
483
- * costs nothing.
526
+ * The catalog's own `supported_endpoints` is authoritative (it is what
527
+ * upstream publishes per model); `requiresMessagesEndpoint()` — the
528
+ * `claude-*` prefix rule — is the fallback for a catalog entry that carries
529
+ * no route list, which is the shape every pre-1.55 cache file has.
530
+ */
531
+ private routesToMessages;
532
+ /**
533
+ * Choose the initial protocol for one request.
534
+ *
535
+ * Models the Provider API serves only through `/provider/v1/messages` (the
536
+ * Claude family) take the Messages transport on any tier and even under a
537
+ * forced `'openai'` preference: that option is documented as "prefer the
538
+ * Provider API, still fall back", and the 400 these models answer with on
539
+ * Chat Completions (`Model "<id>" must be called via /provider/v1/messages`)
540
+ * is a hard refusal, not a preference to honour.
484
541
  *
485
542
  * That decision is deliberately NOT written to `protocolCache`, which is
486
- * keyed by API key alone: remembering it would pin the whole ACCOUNT to the
487
- * CLI transport and drag every other model off it until the entry expired.
543
+ * keyed by gateway and API key: it depends on the model, not just the account, so
544
+ * remembering it would pin the whole ACCOUNT to one transport and drag
545
+ * every other model along (issue #46).
546
+ *
547
+ * Otherwise a fresh protocol cache entry wins, then a cached (not
548
+ * network-fetched) billing tier of Go — the one plan without Provider API
549
+ * access. Unknown accounts default to Chat Completions and fall back to the
550
+ * CLI transport only after an `upgrade_required` rejection.
488
551
  */
489
552
  private resolveProtocol;
490
553
  /**
@@ -535,6 +598,26 @@ declare class CommandCodeAdapter<C extends CommandCodeConnectionOptions = Comman
535
598
  resetAt: number;
536
599
  } | undefined>;
537
600
  stream(options: GenerateOptions): AsyncIterable<StreamChunk>;
601
+ /**
602
+ * One turn, with at most one output-ceiling downshift.
603
+ *
604
+ * The ceiling a model accepts is not published anywhere the request can be
605
+ * built from (see `modelOutputTokenLimit`), and the endpoint states it only
606
+ * by refusing. That refusal is worth one retry rather than a failed turn: the
607
+ * harness's own recovery for it — compact the context — cannot help, because
608
+ * nothing about the context is wrong, and resending the identical request
609
+ * would be refused identically (issue #71). Reading the stated ceiling off
610
+ * the rejection turns one wasted turn into one, for good: it is recorded
611
+ * process-wide, so the next request for this model is built correctly
612
+ * without spending the round trip again.
613
+ *
614
+ * Only BEFORE the first chunk reaches the host. Once a delta has been
615
+ * delivered the answer is already partly on screen, and a second request
616
+ * would duplicate it — so from that point the error stands and says what is
617
+ * actually wrong. One downshift per turn, and a second refusal surfaces.
618
+ */
619
+ private streamRequest;
620
+ private streamAttempt;
538
621
  }
539
622
  //#endregion
540
623
  //#region src/accounts.d.ts
@@ -728,6 +811,26 @@ declare class CommandCodeAccountPool {
728
811
  key: string;
729
812
  slot: CommandCodeAccountSlot;
730
813
  } | undefined>;
814
+ /**
815
+ * Which account is serving right now, for display (the settings page's
816
+ * account card, the sidebar usage stats) — without consuming a request's
817
+ * `tried` budget and without throwing when nothing can serve.
818
+ *
819
+ * Reads and self-heals exactly like {@link resolveKey} would: an `unknown`
820
+ * mark on the pinned account is re-probed (throttled the same way, sharing
821
+ * the same per-key timestamp), and a pool where nothing currently resolves
822
+ * gets one batch probe pass too. Without this, the badge read a possibly
823
+ * stale mark forever — nothing but an actual generate request ever cleared
824
+ * an `unknown` mark, so a page left open with no chat activity kept
825
+ * showing "switched to the fallback account" long after a real request
826
+ * would have quietly recovered (issue #51's follow-up report: the account
827
+ * card and the sidebar stats both stuck on the wrong account).
828
+ *
829
+ * No `model` parameter: the badge describes the pool as a whole, not one
830
+ * request, so model-routing rules are deliberately not consulted here —
831
+ * matching what the direct `selectActiveAccount` call this replaces did.
832
+ */
833
+ activeAccount(): Promise<ResolvedAccount | undefined>;
731
834
  /**
732
835
  * Record a rejection against one key. A `throttled` rejection (the plain 429
733
836
  * that named no window) marks the key with the `throttle` cause, leaving
@@ -793,6 +896,26 @@ declare class CommandCodeAccountPool {
793
896
  * read as "never usable again" to {@link accountUsable}, so `unknown` it is.
794
897
  */
795
898
  private stampWindowMark;
899
+ /**
900
+ * The explicit-account fallback re-check: when `chosen` (what rotation
901
+ * would currently serve) is not the account the user actually asked for,
902
+ * re-probe that explicit account's mark before accepting the fallback —
903
+ * so a rate-limit mark never demotes the user's own selection for good
904
+ * (issue #51). Returns the revived account, or `undefined` when there is
905
+ * nothing to revive (explicit account already serving, not due for a
906
+ * probe, or the probe found it still exceeded). Shared by {@link resolveKey}
907
+ * (the request path) and {@link activeAccount} (the display path), so both
908
+ * agree on what "currently usable" means instead of drifting apart.
909
+ */
910
+ private reviveExplicit;
911
+ /**
912
+ * Probe every marked account's real usage window when nothing currently
913
+ * resolves. Disabled (401) keys are not probed — an invalid key stays
914
+ * invalid. A throwing probe counts as "unknown" (like a failed one): it
915
+ * must not turn the all-exhausted path into a raw rejection instead of
916
+ * RATE_LIMIT. Shared by {@link resolveKey} and {@link activeAccount}.
917
+ */
918
+ private probeAllMarked;
796
919
  /** Hand out the chosen account's key. */
797
920
  private pick;
798
921
  }
@@ -821,7 +944,7 @@ declare const KNOWN_EFFORTS: Readonly<Record<string, readonly string[]>>;
821
944
  declare const KNOWN_IMAGE_MODELS: ReadonlySet<string>;
822
945
  /**
823
946
  * Models WITHOUT a zero-data-retention upstream, per the official CLI's own
824
- * registry (`command-code@1.68.0` `dist/cli.mjs`): `modelSupportsZdr(id)` is
947
+ * registry (`command-code@1.73.0` `dist/cli.mjs`): `modelSupportsZdr(id)` is
825
948
  * exactly `!nonZdrSet.has(canonicalize(id))`, and `knownModelSupportsZdr`
826
949
  * carries the same membership in the sibling route table — the UNION of both
827
950
  * is this set. Reading only the sibling route table would drop `meituan/
@@ -882,7 +1005,7 @@ declare const KNOWN_THINKING_MODELS: ReadonlySet<string>;
882
1005
  * `/docs/plans/max` and `/docs/resources/pricing-limits`). Each plan's model
883
1006
  * list is a superset of the one below it: Go ⊂ GOAT ⊂ Pro ⊂ Provider/Max.
884
1007
  * Models absent from every plan list (Claude Opus/Fable, Fugu Ultra) are
885
- * Provider-tier. Re-verified at command-code@1.68.0 (2026-09-29): 84 catalog
1008
+ * Provider-tier. Re-verified at command-code@1.73.0 (2026-10-01): 86 catalog
886
1009
  * ids at 53/62/76/84 cumulative, a strict superset chain — every release since
887
1010
  * 1.49.0 has been additive with no tier move, and per-entry tags below name the
888
1011
  * release that added each row.
@@ -911,7 +1034,7 @@ declare function compareByPlan(a: {
911
1034
  /**
912
1035
  * Subscription plan table, synced from the official CLI bundle's plan maps
913
1036
  * (located by the `"individual-go"` key in `dist/cli.mjs`, re-verified unchanged
914
- * through command-code@1.68.0): subscription `planId` prefix → display name and
1037
+ * through command-code@1.73.0): subscription `planId` prefix → display name and
915
1038
  * the plan's monthly credit total. This is the account's own subscription
916
1039
  * (from `/alpha/billing/subscriptions`) — distinct from {@link KNOWN_PLANS},
917
1040
  * which maps catalog models to their minimum tier.
@@ -1107,11 +1230,11 @@ interface CommandCodeCatalogModel {
1107
1230
  * Per-model MONTHLY allowance in USD: how much of the plan's monthly credit
1108
1231
  * pool this one model may draw, already resolved by the Host against the
1109
1232
  * account pool's highest plan (the pricing page publishes an allowance for
1110
- * GOAT and Pro only). Undefined when that plan has no published allowance
1233
+ * Go, GOAT and Pro). Undefined when that plan has no published allowance
1111
1234
  * (Go, Provider, Max, Ultra) or when billing could not be read — the browser
1112
1235
  * shows nothing rather than guessing a neighbouring tier's figure.
1113
1236
  *
1114
- * Deliberately a plain number and not the `{ goat, pro }` pair: the bracket is
1237
+ * Deliberately a plain number and not the `{ go, goat, pro }` map: the bracket is
1115
1238
  * a Host decision that already depends on facts the browser does not hold, and
1116
1239
  * shipping both figures would invite a second, divergent rule here. Note this
1117
1240
  * is dollars per MONTH, unlike every rate in `CommandCodePriceTable`.
@@ -1365,7 +1488,7 @@ declare class CommandCodeUsageService<C extends CommandCodeConnectionOptions = C
1365
1488
  declare function applyUsageRemote<C extends CommandCodeConnectionOptions>(ctx: Context, deps: CommandCodeUsageDeps<C>): void;
1366
1489
  //#endregion
1367
1490
  //#region src/login.d.ts
1368
- /** Give up on the browser after this long without a callback (mirrors the CLI). */
1491
+ /** 登录总期限包含浏览器回调、密钥验证与存储,不能在收到回调后撤掉保护。 */
1369
1492
  declare const LOGIN_TIMEOUT_MS = 120000;
1370
1493
  /** First local port the flow tries (mirrors the CLI). */
1371
1494
  declare const LOGIN_START_PORT = 5959;
@@ -1428,7 +1551,7 @@ declare function studioBaseForApiBase(apiBase: string): string;
1428
1551
  * tests). Mirrors the CLI's verdicts: 401 → invalid_key, other non-OK →
1429
1552
  * server_error, transport failure → network_error.
1430
1553
  */
1431
- declare function validateCommandApiKey(fetchImpl: typeof fetch, apiBase: string, apiKey: string): Promise<ApiKeyValidation>;
1554
+ declare function validateCommandApiKey(fetchImpl: typeof fetch, apiBase: string, apiKey: string, signal?: AbortSignal): Promise<ApiKeyValidation>;
1432
1555
  /**
1433
1556
  * One browser-login attempt machine. Single-flight by design: `begin()` while
1434
1557
  * waiting returns the live attempt's status instead of starting a second one;
@@ -1443,6 +1566,7 @@ declare class CommandCodeLoginFlow {
1443
1566
  private statusValue;
1444
1567
  private server;
1445
1568
  private timer;
1569
+ private attemptAbort;
1446
1570
  /** Settle hooks of the live attempt's callback promise. */
1447
1571
  private settle;
1448
1572
  /**
@@ -1522,7 +1646,9 @@ declare class CommandCodeLoginFlow {
1522
1646
  /** Map a tagged settle rejection onto the status face. */
1523
1647
  private failFrom;
1524
1648
  private clearTimer;
1525
- /** Close the server and watchdog without touching the published status. */
1649
+ /** 消费一次回调后关闭监听;保留总期限以约束后续验证。 */
1650
+ private closeCallback;
1651
+ /** 结束整个尝试,同时中止验证请求,迟到结果由所有权检查拦截。 */
1526
1652
  private teardown;
1527
1653
  }
1528
1654
  //#endregion
@@ -1798,9 +1924,9 @@ interface Config {
1798
1924
  offloadSeenImagesForCache?: boolean;
1799
1925
  /**
1800
1926
  * Transport failures one request absorbs before the failure is surfaced;
1801
- * defaults to 5. The route's retry policy is near-unbounded on purpose
1802
- * (1000 attempts, waits doubling to 15 minutes) because that shape is for
1803
- * the failures a provider asks to have retried; a transport failure is not
1927
+ * defaults to 5. The route's policy allows long waits for confirmed usage
1928
+ * windows, while ordinary transient errors have a separate three-retry
1929
+ * budget. A transport failure is not
1804
1930
  * one of those, and after ~15.5 s of grace the wait is pure stall, so it is
1805
1931
  * capped here (issue #39). 0 surfaces every transport failure immediately.
1806
1932
  */