@mars-sea/dsh-commandcode-provider 0.11.16 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.d.ts CHANGED
@@ -7,16 +7,63 @@ 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
9
  //#region src/adapter.d.ts
10
- declare const COMMAND_CODE_CLI_VERSION = "1.65.2";
10
+ declare const COMMAND_CODE_CLI_VERSION = "1.68.0";
11
11
  declare const DEFAULT_API_BASE = "https://api.commandcode.ai";
12
- declare const DEFAULT_GENERATE_MAX_TOKENS = 64000;
13
- declare const DEFAULT_MAX_OUTPUT_TOKENS = 65536;
12
+ /**
13
+ * The output budget this bundle asks for, and the ceiling it will never exceed.
14
+ *
15
+ * 131 072, chosen against a measured provider limit rather than a round guess.
16
+ * Probing `/alpha/generate` directly on 2026-09-28: 64 000 → 200, 131 072 → 200,
17
+ * 196 608 → 200, **200 000 → 200, 200 704 → 400**, 1 000 000 → 400. The server
18
+ * therefore caps `max_tokens` at 200 000, and it does so identically for a
19
+ * 262 144-window model (`stealth/pixel-canary`) and a 1 000 000-window one
20
+ * (`stealth/space-bunny-alpha`) — the cap is global, not per model. 131 072
21
+ * keeps a 35 % margin under it while covering the real output ceiling of the
22
+ * models that have one (Claude 64 K, Gemini 64 K, several at 128 K).
23
+ *
24
+ * The old 64 000 was never a provider requirement: it cost nothing in latency
25
+ * (a 64 000 request answered headers in 1.28 s against 1.57 s for 131 072) but
26
+ * it did cap what a model could ever say in one turn.
27
+ *
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.
31
+ */
32
+ declare const DEFAULT_GENERATE_MAX_TOKENS = 131072;
33
+ /**
34
+ * The fallback when the catalog carries no output ceiling for a model.
35
+ *
36
+ * `/provider/v1/models` publishes only `context_length` — no output limit — so
37
+ * the catalog cannot learn one per model and derives a stand-in from the
38
+ * window instead. Kept equal to {@link DEFAULT_GENERATE_MAX_TOKENS} on purpose:
39
+ * a lower value here would silently re-cap every request through
40
+ * `Math.min(contextLength, this)` before the ceiling above is ever consulted.
41
+ */
42
+ declare const DEFAULT_MAX_OUTPUT_TOKENS = 131072;
14
43
  /** How long the picker's plan-filter billing facts stay cached before refetching. */
15
44
  declare const BILLING_ACCESS_TTL_MS: number;
16
45
  /** Endpoint protocol selected for one generate call. */
17
46
  type CommandCodeProtocol = 'cli' | 'openai';
18
- /** Head-of-request timeout: how long to wait for the first response byte. */
19
- declare const DEFAULT_REQUEST_TIMEOUT_MS = 60000;
47
+ /**
48
+ * Head-of-request timeout: how long to wait for the first response byte.
49
+ *
50
+ * 300 s, and that number is the official CLI's, not a round choice. Decompiled
51
+ * from `command-code@1.66.0`: its `createNodeTransport` fetch passes only the
52
+ * caller's `signal` and sets no timeout at all, so the model request inherits
53
+ * Node/undici's own default — measured at 301.0 s against a local server that
54
+ * accepts the connection and never sends headers. The previous 60 s default was
55
+ * five times stricter than the CLI it emulates, and being stricter only ever
56
+ * costs legitimate requests: a slow upstream first token on the Provider API
57
+ * route can outlast 60 s, and the official CLI simply waits it out. Measured
58
+ * headers (2026-09-28, 26 250-token prompt, `max_tokens: 16` to separate the
59
+ * header from the generation): `/alpha/generate` 2.12 / 2.56 / 2.75 s,
60
+ * `/provider/v1/chat/completions` 5.52 / 2.98 / 3.70 s — a ~1.1 s median gap
61
+ * with the Provider API route varying over a 2.5 s span against the CLI route's
62
+ * 0.6 s. A gateway that is genuinely dead now takes longer to name itself,
63
+ * which is what the consecutive-timeout streak exists to bound (three
64
+ * attempts, then a diagnosis instead of another re-send).
65
+ */
66
+ declare const DEFAULT_REQUEST_TIMEOUT_MS = 300000;
20
67
  /** Stream idle timeout: a generation that stalls this long is a dead connection. */
21
68
  declare const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 300000;
22
69
  declare function projectSlugFromPath(pathName: string): string;
@@ -46,13 +93,23 @@ interface CommandCodeConnectionOptions {
46
93
  /** Model catalog cache path. */
47
94
  modelsCachePath: string;
48
95
  /**
49
- * Milliseconds to wait for generate response headers / first byte (default 60s).
96
+ * Milliseconds to wait for generate response headers / first byte (default
97
+ * 300s, the official CLI's own budget — see
98
+ * {@link DEFAULT_REQUEST_TIMEOUT_MS}).
50
99
  * Must not bound the subsequent body stream — long generations are gated by
51
100
  * {@link streamIdleTimeoutMs} and the caller AbortSignal instead.
52
101
  */
53
102
  requestTimeoutMs: number;
54
103
  /** Milliseconds a stream may stall before it is treated as a dead connection (default 300s). */
55
104
  streamIdleTimeoutMs: number;
105
+ /**
106
+ * Opt-in CLI cache mitigation: after this same model has answered with an
107
+ * image in its history, ask dsh's durable image-offload surface to replace
108
+ * that image before the next replay. This protects the growing text prefix
109
+ * when Command Code's multimodal cache retreats to the first image, but the
110
+ * model cannot inspect the old pixels again without a fresh tool/user image.
111
+ */
112
+ offloadSeenImagesForCache?: boolean;
56
113
  /**
57
114
  * Whether the picker hides models above the account's subscription tier
58
115
  * (default true). The filter fails open: unknown plan, billing-endpoint
@@ -93,8 +150,6 @@ interface CommandCodeConnectionOptions {
93
150
  * `CMD_ZDR=1`). Default false. Every chat request carries the header when
94
151
  * enabled. The provider refuses a model without a ZDR-capable upstream with
95
152
  * 422 `cmd_zdr_no_providers`; never retry that request without the header.
96
- * The decision endpoint (`/provider/v1/systemone`) is deliberately never
97
- * reached through this connection option — see `./systemone.ts`.
98
153
  */
99
154
  zdr?: boolean;
100
155
  }
@@ -110,12 +165,11 @@ type ResolveAttachments = () => AttachmentStore | undefined;
110
165
  * `invalid-credential` are the three the pool records as marks; `unavailable`
111
166
  * is an account-scoped rejection that must NOT become a mark — the account's
112
167
  * key is valid and its windows may be open, the ACCOUNT just cannot serve THIS
113
- * request (no credits, a model outside its plan) — so the pool moves on
114
- * without remembering anything.
168
+ * request — so the pool moves on without remembering anything.
115
169
  *
116
170
  * The window/throttle split decides what the pool may CLAIM, never whether it
117
- * rotates: both leave rotation and mark the key, but only a named window lets
118
- * the pool report an exhausted usage window (issue #54).
171
+ * rotates (issue #54); the evidence that separates them is in
172
+ * {@link classifyAccountRejection}.
119
173
  */
120
174
  type AccountRotationReason = 'rate-limit' | 'throttled' | 'invalid-credential' | 'unavailable';
121
175
  /** What the rotation hook knows about the request it is rotating within. */
@@ -149,11 +203,10 @@ interface CommandCodeAdapterDeps<C extends CommandCodeConnectionOptions = Comman
149
203
  * with several accounts a rejection that does not mark the key (a plan or
150
204
  * balance rejection is model-specific, not account-fatal) would otherwise
151
205
  * re-offer the same account on every attempt and the pool could never reach
152
- * the accounts behind it.
153
- *
154
- * `rotation.resetAtMs` carries the provider's own reset time when the
155
- * rejection body published one, so the host can hold the key out until then
156
- * instead of waiting for a billing probe to learn the same fact.
206
+ * the accounts behind it. `rotation.resetAtMs` carries the provider's own
207
+ * reset time when the rejection body published one, so the host can hold the
208
+ * key out until then instead of waiting for a billing probe to learn the
209
+ * same fact.
157
210
  */
158
211
  rotateApiKey?: (rejectedKey: string, rejection: AccountRotationReason, connection: C, model?: string, rotation?: AccountRotationContext) => Promise<string | undefined>;
159
212
  /**
@@ -283,7 +336,29 @@ declare class CommandCodeAdapter<C extends CommandCodeConnectionOptions = Comman
283
336
  private readonly billingAccess;
284
337
  private readonly billingAccessInflight;
285
338
  private readonly protocolCache;
339
+ /**
340
+ * Consecutive header timeouts for the last request shape, counted ACROSS
341
+ * `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;
286
346
  constructor(deps: CommandCodeAdapterDeps<C>);
347
+ /**
348
+ * Grade one failed attempt: a headers timeout that repeats on an unchanged
349
+ * payload eventually leaves the retryable whitelist.
350
+ *
351
+ * `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.
357
+ *
358
+ * @returns the error to surface: unchanged for any other failure, the
359
+ * original timeout below the threshold, and a terminal one at it.
360
+ */
361
+ private gradeHeaderTimeout;
287
362
  /**
288
363
  * Display metadata for the picker's provider group header. The base class
289
364
  * returns the raw route id (`commandcode`, all lowercase) as the name, which
@@ -298,13 +373,13 @@ declare class CommandCodeAdapter<C extends CommandCodeConnectionOptions = Comman
298
373
  * unbounded loop): `RATE_LIMIT`/`SERVER`/`TIMEOUT`/`TRANSPORT`/
299
374
  * `EMPTY_RESPONSE` retry up to 1000 times with waits doubling from 500 ms
300
375
  * and capping at 15 minutes (±10% jitter), so an exhausted 5-hour window
301
- * recovers in-session instead of failing after two tries. Permanent
302
- * failures (an invalid key's `INVALID_CREDENTIAL`, `UNSUPPORTED_CONTENT`,
303
- * plan rejections) are absent from the whitelist and surface immediately
304
- * instead of looping. Waits the pool/adapter attach as
305
- * `providerRetryAfterMs` are honored verbatim at or below the 15-minute
306
- * cap and never attached above it (in normal mode a longer attached wait
307
- * makes the executor abandon the retry outright — see RETRY_MAX_DELAY_MS).
376
+ * recovers in-session instead of failing after two tries. Permanent failures
377
+ * (an invalid key's `INVALID_CREDENTIAL`, `UNSUPPORTED_CONTENT`, plan
378
+ * rejections) are absent from the whitelist and surface immediately instead
379
+ * of looping. Waits the pool/adapter attach as `providerRetryAfterMs` are
380
+ * honored verbatim at or below the 15-minute cap and never attached above it
381
+ * (in normal mode a longer attached wait makes the executor abandon the
382
+ * retry outright — see RETRY_MAX_DELAY_MS).
308
383
  *
309
384
  * Captured once at route registration (dsh-llm snapshots this value), so a
310
385
  * future config knob for it would apply on profile restart, not per request.
@@ -318,12 +393,8 @@ declare class CommandCodeAdapter<C extends CommandCodeConnectionOptions = Comman
318
393
  * session reports far less context than it is actually carrying. The price is
319
394
  * computed at the dimensions this route would send (the same request target
320
395
  * `readImageRequest` encodes to), so the estimate tracks the wire rather than
321
- * the stored original.
322
- *
323
- * Both payload generations are handled, because they disagree: ≤0.1.5 hands
324
- * over bare `ImageAttachmentRef`s, ≥0.1.6 hands over `ImageBlock`s that also
325
- * carry the surface's `offloaded` mark. Returning a price per occurrence, in
326
- * order, is a hard requirement — the meter throws when the counts differ.
396
+ * the stored original. One price per occurrence, in order, is a hard
397
+ * requirement — the meter throws when the counts differ.
327
398
  */
328
399
  imageRequestPricing(_provider: string, model: string): LlmImageRequestPricing | undefined;
329
400
  /** Refresh the catalog (live fetch, cache fallback) and return it. */
@@ -331,22 +402,35 @@ declare class CommandCodeAdapter<C extends CommandCodeConnectionOptions = Comman
331
402
  listModels(provider: string, opts?: {
332
403
  unfiltered?: boolean;
333
404
  }): Promise<readonly LlmModelInfo[]>;
405
+ /**
406
+ * The per-model allowance bracket this account POOL falls into, or undefined
407
+ * when there is nothing honest to show.
408
+ *
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,
412
+ * Max or Ultra gets undefined rather than a neighbouring tier's figure. Taking
413
+ * the HIGHEST tier matches the question the picker already answers for a pool
414
+ * (see `modelVisibleForAnyAccount`): what the user can reach, not what the one
415
+ * account serving this request happens to be. The fail-open shape is the same
416
+ * too — an unreachable billing endpoint yields undefined, which hides the
417
+ * allowance instead of inventing one.
418
+ */
419
+ allowanceTier(): Promise<'goat' | 'pro' | undefined>;
334
420
  resolveModel(provider: string, model: string, signal?: AbortSignal): Promise<LlmResolvedModelInfo>;
335
421
  /** The headers every authenticated account endpoint shares. */
336
422
  private accountHeaders;
337
423
  /**
338
- * Fetch one account endpoint and parse its JSON body. Returns the HTTP
339
- * status alongside the parsed record so each caller applies its own
340
- * failure accounting: the billing probe fails open silently, the usage
341
- * report books failures per endpoint. Non-2xx and invalid JSON bodies come
342
- * back without a record; only a fetch failure propagates to the caller.
424
+ * Fetch one JSON GET and return its HTTP status with a parsed record. The
425
+ * catalog, billing probe, and usage report each apply their own failure
426
+ * policy. Non-2xx and invalid JSON bodies come back without a record; only
427
+ * a fetch failure propagates to the caller.
343
428
  *
344
- * `timeoutMs` is the caller's budget for this one request. The default is the
345
- * CATALOG probe's short cap, which fits the picker's fail-open reads; the
346
- * usage report deliberately passes the connection's own request budget so an
347
- * account query is not held to a stricter (and invisible) limit than chat.
429
+ * `timeoutMs` is the caller's budget, unless it provides its own signal.
430
+ * The usage report passes the connection's request budget rather than the
431
+ * catalog/picker's shorter default.
348
432
  */
349
- private fetchEndpointJson;
433
+ private fetchJson;
350
434
  /**
351
435
  * Every account's billing facts behind the picker's plan filter, in the
352
436
  * pool's rotation order and cached per key for {@link BILLING_ACCESS_TTL_MS}
@@ -354,9 +438,6 @@ declare class CommandCodeAdapter<C extends CommandCodeConnectionOptions = Comman
354
438
  * per picker load). `undefined` means the filter cannot be evaluated at all
355
439
  * — no key resolved — which {@link modelVisibleForAnyAccount} reads as
356
440
  * "show everything".
357
- *
358
- * The serving account is only the first entry: with several accounts the
359
- * model list must not depend on which of them rotation happens to be using.
360
441
  */
361
442
  private loadPoolBillingAccess;
362
443
  /**
@@ -401,10 +482,9 @@ declare class CommandCodeAdapter<C extends CommandCodeConnectionOptions = Comman
401
482
  * but a hard refusal. `/alpha/generate` routes every one of them, so this
402
483
  * costs nothing.
403
484
  *
404
- * That decision is deliberately NOT written to `protocolCache`. The cache is
405
- * keyed by API key alone, so remembering it would pin the whole ACCOUNT to
406
- * the CLI transport and drag every other model — DeepSeek, GLM, Qwen, all of
407
- * which the Provider API serves correctly — off it until the entry expired.
485
+ * 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.
408
488
  */
409
489
  private resolveProtocol;
410
490
  /**
@@ -418,18 +498,14 @@ declare class CommandCodeAdapter<C extends CommandCodeConnectionOptions = Comman
418
498
  * the default resolves the currently active account.
419
499
  *
420
500
  * Two failure modes are NOT "the network is down", and the report must say
421
- * so rather than let {@link classifyTotalFailure} blame the connection:
422
- *
423
- * - A key that no HTTP header can carry (a stray newline from a paste, a
424
- * full-width character) makes `fetch` throw a `TypeError` **before any
425
- * I/O**, for every endpoint at once. The chat path refuses such a key
426
- * through `assertUsableApiKey`; this path must too, and it answers
427
- * `blocked: 'invalid-key'` instead of four phantom transport failures.
428
- * - The four endpoints get the SAME per-request budget as a chat call
429
- * (`connection.requestTimeoutMs`, default 60 s), not the catalog's 10 s
430
- * probe budget: on a slow link a 10 s cap made every account query time
431
- * out while chat kept working, which is exactly a "check your network"
432
- * banner that never clears.
501
+ * so rather than let {@link classifyTotalFailure} blame the connection: a
502
+ * key that no HTTP header can carry (a stray newline from a paste, a
503
+ * full-width character) makes `fetch` throw a `TypeError` before any I/O,
504
+ * for every endpoint at once, so this path runs `assertUsableApiKey` too and
505
+ * answers `blocked: 'invalid-key'`; and the four endpoints get the SAME
506
+ * per-request budget as a chat call (`connection.requestTimeoutMs`, default
507
+ * 60 s), not the catalog's 10 s probe budget, because on a slow link a 10 s
508
+ * cap made every account query time out while chat kept working.
433
509
  */
434
510
  getUsage(apiKey?: string): Promise<CommandCodeUsageReport>;
435
511
  /**
@@ -443,17 +519,16 @@ declare class CommandCodeAdapter<C extends CommandCodeConnectionOptions = Comman
443
519
  * the same time (the two are metered separately), and reading only the
444
520
  * shorter one revived such an account as "usable" — the pool then handed it
445
521
  * out, the provider rejected the request again, and the next all-marked pass
446
- * revived it once more, so a used-up account kept coming back (issue #51's
447
- * follow-up: "切到一个不可用账号"). `exceeded` is therefore true when ANY
448
- * published window is exceeded, and `resetAt` is the LATEST reset among the
449
- * exceeded ones — the binding constraint, because a clear five-hour window
450
- * buys nothing while the weekly quota is spent.
522
+ * revived it once more (issue #51's follow-up: "切到一个不可用账号").
523
+ * `exceeded` is therefore true when ANY published window is exceeded, and
524
+ * `resetAt` is the LATEST reset among the exceeded ones — the binding
525
+ * constraint, because a clear five-hour window buys nothing while the weekly
526
+ * quota is spent.
451
527
  *
452
528
  * Returns `undefined` when the probe itself failed (transport, non-200, or a
453
529
  * payload with no window limits at all) — a failed probe never changes pool
454
- * state. The monthly/purchased/free credit balances are deliberately NOT part
455
- * of this answer: they are balances rather than windows, they carry no reset
456
- * time, and the server remains the final gate on them.
530
+ * state. The credit balances are deliberately NOT part of this answer: they
531
+ * carry no reset time, and the server remains the final gate on them.
457
532
  */
458
533
  probeWindowLimits(apiKey: string): Promise<{
459
534
  exceeded: boolean;
@@ -486,18 +561,17 @@ interface CommandCodeAccountSlot {
486
561
  allowAuthFile: boolean;
487
562
  }
488
563
  /**
489
- * Why a key stopped serving requests.
564
+ * Why a key stopped serving requests, split by EVIDENCE — what the pool may
565
+ * claim about it — rather than by what it does.
490
566
  *
491
567
  * `rate-limit` is a USAGE-WINDOW rejection: the provider named one of its
492
568
  * metered windows (`error.rateLimit.window`, or a message saying the plan's
493
569
  * usage limit was reached — the only two shapes the official CLI's own
494
- * `parseWindowLimitError`/`resolveWindowLabel` accept). `throttled` is the
495
- * plain 429: the provider refused the request without saying anything about a
496
- * window, so the key leaves rotation for now but the pool must not describe it
497
- * as an exhausted window. Collapsing the two is issue #54: the reporter's
498
- * account panel showed the five-hour window at 2% and the weekly one at 10%
499
- * while the turn failed with "all 1 Command Code account(s) have exhausted
500
- * their usage window".
570
+ * `parseWindowLimitError`/`resolveWindowLabel` accept). `throttled` is the plain
571
+ * 429: the key leaves rotation for now, but the provider said nothing about a
572
+ * window, so the pool must not describe one as exhausted. Collapsing the two is
573
+ * issue #54: a bare 429 was reported as a spent window while the account card
574
+ * showed both windows barely used.
501
575
  */
502
576
  type AccountRejection = 'rate-limit' | 'throttled' | 'invalid-credential';
503
577
  /** One key's rotation state. */
@@ -510,11 +584,11 @@ interface CommandCodeAccountState {
510
584
  /** Marked by a 401: skipped until the stored credential changes. */
511
585
  'disabled';
512
586
  /**
513
- * The evidence behind the mark — what the pool may CLAIM when it reports
514
- * that no account can serve. `window`: the provider named a usage window, or
515
- * a `/alpha/billing/credits` probe read one as exceeded. `throttle`: a 429
516
- * that said nothing about a window (a burst limiter, or a model- or
517
- * spend-level limit the billing endpoint cannot show). `auth`: a 401.
587
+ * The evidence behind the mark — what the pool may CLAIM when it reports that
588
+ * no account can serve. `window`: the provider named a usage window, or a
589
+ * `/alpha/billing/credits` probe read one as exceeded. `throttle`: a 429 that
590
+ * said nothing about a window (a burst limiter, or a model-/spend-level limit
591
+ * the billing endpoint cannot show). `auth`: a 401.
518
592
  */
519
593
  cause: 'window' | 'throttle' | 'auth';
520
594
  /** Human-readable reason for the mark (e.g. `rate limited (429)`). */
@@ -547,43 +621,34 @@ interface CommandCodeAccountPoolDeps {
547
621
  resolveRef(ref: CredentialRef): Promise<string | undefined>;
548
622
  /** The official CLI auth-file key (`~/.commandcode/auth.json`); default slot only. */
549
623
  authFileKey(): string | undefined;
550
- /**
551
- * Probe one key's usage windows — BOTH windows the endpoint publishes (the
552
- * five-hour and the weekly one), not just the five-hour one; see
553
- * {@link AccountWindowProbe}. Undefined when the probe itself failed.
554
- */
624
+ /** Probe one key's usage windows (BOTH windows the endpoint publishes); undefined when it failed. */
555
625
  probeWindow(apiKey: string): Promise<AccountWindowProbe | undefined>;
556
626
  /**
557
- * The manually selected account (a slot id, e.g. `default` or an extra's
558
- * credential reference), re-read per resolution. The preferred account
559
- * serves whenever it is usable; an unknown id or an exhausted preferred
560
- * account falls back to the first usable slot — and an exhausted one is
561
- * re-probed on the way (see {@link CommandCodeAccountPool.resolveKey}), so
562
- * the fallback lasts only as long as the window really is exceeded.
627
+ * The manually selected account slot id, re-read per resolution. It serves
628
+ * whenever it is usable; an unknown id or an exhausted preferred account falls
629
+ * back to the first usable slot — and an exhausted one is re-probed on the way
630
+ * (see {@link CommandCodeAccountPool.resolveKey}), so the fallback lasts only
631
+ * as long as the window really is exceeded.
563
632
  */
564
633
  preferredId?(): string | undefined;
565
634
  /**
566
635
  * Model → account routing rules, re-read per resolution so settings changes
567
- * apply live. Each rule lists catalog model ids (see
568
- * {@link CommandCodeModelAccountRule}) to an account slot id. When the
569
- * request's model matches a rule and that account is usable, it serves
570
- * before the preferred/rotation selection; an unusable routed account falls
571
- * back to the normal selection (the router is a hint, never a hard gate).
636
+ * apply live. A matching, usable routed account serves before the
637
+ * preferred/rotation selection; an unusable one falls back to the normal
638
+ * selection — the router is a hint, never a hard gate.
572
639
  */
573
640
  modelAccountRules?(): readonly CommandCodeModelAccountRule[];
574
641
  /**
575
- * Clock seam for the explicit-account probe throttle. Tests drive it so the
576
- * "the interval elapsed, probe again" half of
577
- * {@link CommandCodeAccountPool.canProbeExplicit} is reachable without
578
- * waiting a real minute; production reads `Date.now()`.
642
+ * Clock seam for the explicit-account probe throttle, so the "the interval
643
+ * elapsed" half of {@link CommandCodeAccountPool.canProbeExplicit} is testable
644
+ * without waiting a real minute. Production reads `Date.now()`.
579
645
  */
580
646
  now?(): number;
581
647
  }
582
648
  /**
583
649
  * One "route these models to that account" rule. `models` lists catalog ids
584
- * (`deepseek/deepseek-v4-pro`, …); `account` is a slot id (`default` or an
585
- * extra account's credential reference). A request whose model id is in the
586
- * list routes to that account. The first matching rule in list order wins.
650
+ * (`deepseek/deepseek-v4-pro`, …); `account` is a slot id (`default` or an extra
651
+ * account's credential reference). The first matching rule in list order wins.
587
652
  */
588
653
  interface CommandCodeModelAccountRule {
589
654
  /** Catalog model ids to match against the request's model. */
@@ -599,10 +664,10 @@ interface CommandCodeModelAccountRule {
599
664
  */
600
665
  declare function accountUsable(state: CommandCodeAccountState | undefined): boolean;
601
666
  /**
602
- * Pick the account that should serve now: the manually preferred slot when it
603
- * is usable, otherwise the first usable account in rotation order; undefined
604
- * when no account is usable. Shared by the pool (request path) and the plugin
605
- * entry (the usage view's active badge) so both always agree.
667
+ * Pick the account that should serve now: the manually preferred slot when it is
668
+ * usable, otherwise the first usable account in rotation order; undefined when
669
+ * no account is usable. Shared by the pool (request path) and the plugin entry
670
+ * (the usage view's active badge) so both always agree.
606
671
  */
607
672
  declare function selectActiveAccount(accounts: readonly ResolvedAccount[], preferredId: string | undefined): ResolvedAccount | undefined;
608
673
  /**
@@ -643,27 +708,18 @@ declare class CommandCodeAccountPool {
643
708
  */
644
709
  describeAccounts(): Promise<ResolvedAccount[]>;
645
710
  /**
646
- * Hand out the key for a request: the model-routed account when the
647
- * request's model matches a rule (and that account is usable), else the
648
- * manually preferred account when usable, else the first usable account in
649
- * rotation order. Returns `undefined` when no account resolves any key at
650
- * all (the caller then reports the missing credential). Throws
651
- * `RATE_LIMIT` — naming the earliest window reset — or
652
- * `INVALID_CREDENTIAL` when accounts exist but none can serve.
653
- *
654
- * `options.model` is the request's model id; routing rules re-read per
655
- * resolution, so a settings change applies live.
656
- *
657
- * `options.tried` lists the keys this request has already used — the
658
- * just-rejected one included. They are removed from the resolution entirely,
659
- * which is what lets one request walk a four-account pool: an account-scoped
660
- * rejection that does not mark the key (no credits, a model outside the
661
- * account's plan) would otherwise be offered again on every attempt, and the
662
- * accounts behind it would never be reached.
711
+ * Hand out the key for a request: the model-routed account when usable, else
712
+ * the manually preferred account when usable, else the first usable account
713
+ * in rotation order. Returns `undefined` when no account resolves any key,
714
+ * or when an account this request already used is still UNMARKED — the
715
+ * caller's own rejection is the honest answer then. Throws `RATE_LIMIT` /
716
+ * `INVALID_CREDENTIAL` when every account is marked.
663
717
  *
664
- * An explicit selection (the pin or a model rule) that a rate-limit mark
665
- * would demote is probed before the fallback serves, so "falls back while
666
- * exhausted" never becomes "stays demoted until the process restarts".
718
+ * `options.tried` lists the keys this request already used; they are removed
719
+ * from the resolution entirely, which is what lets one request walk a
720
+ * four-account pool. An explicit selection (pin or model rule) that a
721
+ * rate-limit mark would demote is probed first, so a fallback never becomes
722
+ * permanent (issue #51). Rules re-read per resolution, so settings apply live.
667
723
  */
668
724
  resolveKey(options?: {
669
725
  tried?: readonly string[];
@@ -673,53 +729,46 @@ declare class CommandCodeAccountPool {
673
729
  slot: CommandCodeAccountSlot;
674
730
  } | undefined>;
675
731
  /**
676
- * Record a rejection against one key. `rate-limit` marks the key's usage
677
- * WINDOW exhausted — as a `cooldown` until `resetAtMs` when the rejection
678
- * body named the provider's own reset time, otherwise as an `unknown` mark
679
- * whose window is probed lazily. `throttled` (the plain 429 that named no
680
- * window) marks the key with the `throttle` cause instead: it leaves rotation
681
- * exactly like a window mark does, but the pool's own diagnosis keeps saying
682
- * "rate limited" rather than inventing an exhausted window. A 401
683
- * (`invalid-credential`) disables the key until the stored credential
732
+ * Record a rejection against one key. A `throttled` rejection (the plain 429
733
+ * that named no window) marks the key with the `throttle` cause, leaving
734
+ * rotation exactly like a window mark but keeping the pool's own diagnosis at
735
+ * "rate limited" rather than inventing an exhausted window. `rate-limit`
736
+ * marks a `cooldown` until `resetAtMs` when the body named the provider's own
737
+ * reset, otherwise an `unknown` mark whose window is probed lazily.
738
+ * `invalid-credential` (401) disables the key until the stored credential
684
739
  * changes.
685
740
  *
686
- * `resetAtMs` is seconds-to-millis converted by the adapter from the
687
- * provider's `error.rateLimit.reset`: knowing the real reset immediately is
688
- * what keeps an exhausted account out of rotation for exactly as long as the
689
- * provider said. It only applies to a window mark; a plain throttle keeps the
690
- * window unknown on purpose, so a probe can still find out.
741
+ * `resetAtMs` is seconds-to-millis converted by the adapter from
742
+ * `error.rateLimit.reset`; it applies to a window mark only. A plain throttle
743
+ * keeps its window unknown on purpose, so a probe can still find out.
691
744
  */
692
745
  markRejected(apiKey: string, rejection: AccountRejection, resetAtMs?: number): void;
693
746
  /**
694
747
  * One account's key: literal → credential seam → auth file (default slot).
695
748
  *
696
749
  * Every source is normalized here, at the single point where a slot's key
697
- * enters the pool. The adapter sends the key through the harness's
698
- * `assertUsableApiKey()`, which trims it — a stored key from the credentials
699
- * seam, a `.env` line, or a shell export all pick up surrounding whitespace
700
- * — and reports that trimmed form back to `markRejected()`. Returning the
701
- * raw value would file every 429/401 mark under a key no later lookup can
702
- * find: rotation would re-offer the same account, the account card would show
703
- * no mark, and the usage endpoints would 401 while chat kept working.
704
- * Normalizing once makes resolution, probing, marking, and the request path
705
- * agree on one string.
750
+ * enters the pool. The adapter sends the key through
751
+ * `assertUsableApiKey()`, which trims it, and reports that trimmed form back
752
+ * to {@link markRejected}: returning the raw value would file every 429/401
753
+ * mark under a key no later lookup can find — rotation would re-offer the same
754
+ * account, the account card would show no mark, and the usage endpoints would
755
+ * 401 while chat kept working.
706
756
  */
707
757
  private resolveSlotKey;
708
758
  /**
709
- * The account the user explicitly asked for: the slot a model rule routes
710
- * the request to when that id exists among the resolved slots, else the
711
- * manually pinned slot. Consulted only on the fallback path — a usable
712
- * routed account already returned above — so unlike
713
- * {@link selectAccountForModel} it does NOT require the account to be
714
- * usable, which is exactly what {@link resolveKey} re-probes.
759
+ * The account the user explicitly asked for: the slot a model rule routes the
760
+ * request to when that id exists among the resolved slots, else the manually
761
+ * pinned slot. Consulted only on the fallback path, so — unlike
762
+ * {@link selectAccountForModel} — it does NOT require the account to be usable,
763
+ * which is exactly what {@link resolveKey} re-probes.
715
764
  */
716
765
  private explicitAccount;
717
766
  /**
718
767
  * Whether an explicitly selected account's mark is due for a window probe.
719
768
  * Only an `unknown` mark (a 429 whose reset was never learned) is worth
720
- * re-probing: a `cooldown` already carries its reset time and expires by
721
- * itself, and a `disabled` (401) key stays out until the stored credential
722
- * changes. The interval bounds a probe endpoint that keeps failing.
769
+ * re-probing: a `cooldown` carries its reset and expires by itself, and a
770
+ * `disabled` (401) key stays out until the stored credential changes. The
771
+ * interval bounds a probe endpoint that keeps failing.
723
772
  */
724
773
  private canProbeExplicit;
725
774
  /** The clock the probe throttle reads; injected so a test can travel in time. */
@@ -727,49 +776,32 @@ declare class CommandCodeAccountPool {
727
776
  /**
728
777
  * Probe one explicitly selected account's window and apply the answer. A
729
778
  * window that is no longer exceeded drops the mark, so the user's own
730
- * selection serves again on this very request. An exceeded one is stamped
731
- * as a cooldown carrying the provider's reset time, after which
779
+ * selection serves again on this very request; an exceeded one is stamped as
780
+ * a cooldown carrying the provider's reset time, after which
732
781
  * {@link accountUsable} lets the account back in with no further probe. A
733
782
  * probe that fails changes nothing: the mark stays `unknown` and the next
734
783
  * attempt waits out the interval.
735
784
  */
736
785
  private probeExplicit;
786
+ /**
787
+ * Stamp a probe-read window onto one key. A known reset becomes a cooldown
788
+ * that expires by itself; without one the mark stays `unknown`, so a later
789
+ * probe can still learn it — and a mark that already carries a cooldown
790
+ * keeps it, never trading a known reset for an unknown one. The cause is
791
+ * `window` either way: a probe that read a window as exceeded IS window
792
+ * evidence, whatever the key was marked for. An `until: 0` cooldown would
793
+ * read as "never usable again" to {@link accountUsable}, so `unknown` it is.
794
+ */
795
+ private stampWindowMark;
737
796
  /** Hand out the chosen account's key. */
738
797
  private pick;
739
798
  }
740
799
  //#endregion
741
- //#region src/command-guard.d.ts
742
- /**
743
- * The auto-approve strictness a user picks (`Config.commandGuardLevel`). Each
744
- * level is the probability every verdict must reach before the guard grants
745
- * `allowed-once`; a higher level approves less, on stronger evidence. Three
746
- * presets replace a free-form probability because the useful range is narrow
747
- * and a hand-typed 0.6 silently turns the guard into a rubber stamp.
748
- */
749
- type CommandGuardLevel = 'high' | 'medium' | 'low';
800
+ //#region src/plan-tiers.d.ts
801
+ declare const PLAN_LABELS: Readonly<Record<string, string>>;
802
+ declare const PLAN_ORDER: Readonly<Record<string, number>>;
750
803
  //#endregion
751
804
  //#region src/capabilities.d.ts
752
- /**
753
- * Static capability snapshot for the Command Code provider: model →
754
- * reasoning-effort levels, vision/thinking flags, model → minimum plan tier,
755
- * subscription-plan labels, deals, and hourly (peak/off-peak) pricing.
756
- *
757
- * Everything in this module is synced from official sources (the command-code
758
- * CLI bundle's model table and the official plan/pricing/model docs — see the
759
- * dsh-commandcode-upstream skill for the exact extraction procedures), and
760
- * changes whenever an upstream CLI release reshuffles models/plans/prices.
761
- * Keeping the snapshot in its own module confines those frequent sync diffs
762
- * here: src/adapter.ts holds only the stable wire/runtime logic and imports
763
- * these tables + read helpers.
764
- *
765
- * Snapshot read helpers (planLabel, dealLabel, formatContext,
766
- * capabilityDescription, peakPricing*, compareByPlan, modelVisibleInPlan,
767
- * subscriptionPlanInfo, isFreeModel) live here too — they exist only to read
768
- * the tables, so a sync never has to touch src/adapter.ts.
769
- *
770
- * Ported from pi-commandcode-provider (MIT); originally part of src/adapter.ts
771
- * and split out so upstream syncs stay reviewable.
772
- */
773
805
  declare const KNOWN_EFFORTS: Readonly<Record<string, readonly string[]>>;
774
806
  /**
775
807
  * Models whose Capabilities include Vision, per the official Command Code
@@ -789,20 +821,23 @@ declare const KNOWN_EFFORTS: Readonly<Record<string, readonly string[]>>;
789
821
  declare const KNOWN_IMAGE_MODELS: ReadonlySet<string>;
790
822
  /**
791
823
  * Models WITHOUT a zero-data-retention upstream, per the official CLI's own
792
- * registry (`command-code@1.65.2` `dist/cli.mjs`, unchanged from 1.65.0:
793
- * `modelSupportsZdr(id)` is exactly `!nonZdrSet.has(canonicalize(id))`, and
794
- * `knownModelSupportsZdr`
795
- * carries the same membership in the sibling route table — the union is this
796
- * set). The official docs (commandcode.ai/docs/resources/zdr) put it in prose
797
- * — "99% of our models have ZDR-capable upstreams … only a small handful of
798
- * models are affected" — so the CLI's exclusion list is the only per-model
799
- * evidence there is; a ZDR request naming one of these fails with HTTP 422
800
- * `cmd_zdr_no_providers` instead of routing through a provider that retains.
824
+ * registry (`command-code@1.68.0` `dist/cli.mjs`): `modelSupportsZdr(id)` is
825
+ * exactly `!nonZdrSet.has(canonicalize(id))`, and `knownModelSupportsZdr`
826
+ * carries the same membership in the sibling route table — the UNION of both
827
+ * is this set. Reading only the sibling route table would drop `meituan/
828
+ * LongCat-2.0`, which appears in `modelSupportsZdr` alone (re-read from the
829
+ * 1.66.0 / 1.67.0 / 1.68.0 artifacts on 2026-09-29: the two stealth-preview
830
+ * rows sit in BOTH sets, so LongCat is the only divergence). The official docs (commandcode.ai/docs/resources/
831
+ * zdr) put it in prose — "99% of our models have ZDR-capable upstreams … only
832
+ * a small handful of models are affected" — so the CLI's exclusion list is
833
+ * the only per-model evidence there is; a ZDR request naming one of these
834
+ * fails with HTTP 422 `cmd_zdr_no_providers` instead of routing through a
835
+ * provider that retains.
801
836
  *
802
837
  * Why a NEGATIVE set, and why "not listed" answers TRUE: 99% of the catalog is
803
838
  * covered, so the maintained difference is the exception list. This helper is
804
839
  * informational; the adapter sends the ZDR header for EVERY request when the
805
- * switch is on. The provider remains the routing authority and refuses an
840
+ * switch is on — the provider remains the routing authority and refuses an
806
841
  * unsupported model rather than silently dropping the privacy guarantee.
807
842
  *
808
843
  * `minimax/minimax-m3-free` is the one entry the public catalog
@@ -813,16 +848,11 @@ declare const KNOWN_IMAGE_MODELS: ReadonlySet<string>;
813
848
  * Keep in sync via the dsh-commandcode-upstream skill: the CLI's registry data
814
849
  * (its `zdr:{only:[…]}` provider routes and the per-provider `zdr`/`noTraining`
815
850
  * flags) is upstream-internal routing, not a per-model contract, so this table
816
- * is the snapshot of the exclusion set and nothing more. The 1.62.0 → 1.64.0
817
- * diff of that union is EMPTY: both anchors were extracted from both bundles
818
- * during the 2026-09-23 check and each carries the same 20 members — which is
819
- * how rare a change here is expected to be. The 1.64.0 → 1.65.0 diff (2026-09-24)
820
- * is the counterexample that proves the check still runs: exactly one member
821
- * joins (`stealth/space-bunny-alpha`), and it is in both anchors. `meituan/
822
- * LongCat-2.0` is the one member the two anchors disagree about (it is in
823
- * `modelSupportsZdr`'s set in both releases and in neither `knownModelSupportsZdr`
824
- * set), so the union is
825
- * what this table follows; reading only the sibling route table would drop it.
851
+ * is the snapshot of the exclusion set and nothing more. It is a rare change:
852
+ * 20 members held across 1.62.0 → 1.64.0, 1.65.0 and 1.66.0 each added exactly
853
+ * one (the two stealth-preview models below), 1.67.0 added one
854
+ * (`deepseek/deepseek-v4.1-flash-fast`), and 1.68.0 changed nothing — 23
855
+ * members as of 2026-09-29.
826
856
  */
827
857
  declare const KNOWN_NON_ZDR_MODELS: ReadonlySet<string>;
828
858
  /**
@@ -832,65 +862,30 @@ declare const KNOWN_NON_ZDR_MODELS: ReadonlySet<string>;
832
862
  */
833
863
  declare function supportsZeroDataRetention(modelId: string): boolean;
834
864
  /**
835
- * Models the official CLI's model table (command-code@1.53.0) marks
836
- * `reasoning:!0` but defines no selectable `reasoning_effort` levels — they
837
- * think automatically, with Command Code driving the depth. This is the
838
- * authoritative "thinks, effort not adjustable" set: `KNOWN_EFFORTS` (which
839
- * mirrors the CLI's effort map exactly) stays the sole source for selectable
840
- * effort levels, and this snapshot is not surfaced in the picker's compact
841
- * description — it exists for programmatic consumers.
865
+ * Models the official CLI's model table marks `reasoning:!0` but defines no
866
+ * selectable `reasoning_effort` levels — they think automatically, with
867
+ * Command Code driving the depth. `KNOWN_EFFORTS` (which mirrors the CLI's
868
+ * effort map exactly) stays the sole source for selectable effort levels, and
869
+ * this snapshot is not surfaced in the picker's compact description — it exists
870
+ * for programmatic consumers.
842
871
  *
843
- * Source: the command-code@1.53.0 bundled model table (dist/cli.mjs),
844
- * cross-checked with https://commandcode.ai/docs/reference/cli/models.
845
- * (`stealth/ox-alpha` left this set in command-code@1.32.1, which gave it
846
- * selectable `['low', 'high', 'max']` efforts; the preview then ended in
847
- * 1.34.0, removing the model from the catalog entirely. `tencent/hy4-preview`
848
- * joined this set in command-code@1.37.0 — reasoning:!0, no efforts, 1M
849
- * context, routed through OpenRouter — then gained selectable
850
- * `['low', 'medium', 'high']` efforts in command-code@1.38.0 and moved to
851
- * `KNOWN_EFFORTS`. `moonshotai/Kimi-K3` followed the same path in
852
- * command-code@1.39.3 — it gained `['low', 'high', 'max']` efforts and moved
853
- * to `KNOWN_EFFORTS`. command-code@1.42.0 added LongCat 2.0 (then
854
- * `meituan/LongCat-2.0:free`, now the paid `meituan/LongCat-2.0`; reasoning:!0,
855
- * no efforts). command-code@1.45.0 gave the Muse Spark family
856
- * (1.1, 1.2, 1.2-contributor, 1.3, 1.3-contributor) selectable
857
- * `['low', 'medium', 'high', 'xhigh']` efforts — they moved to `KNOWN_EFFORTS`.
858
- * command-code@1.51.3 gave `MiniMaxAI/MiniMax-M3` selectable
859
- * `['low', 'medium', 'high']` efforts — it moved to `KNOWN_EFFORTS` too.
860
- * command-code@1.52.0 added `inclusionai/ling-3.0-flash-sante:free`
861
- * (reasoning:!0, no efforts).)
862
- * command-code@1.53.0 added `deepseek/deepseek-v4.1-flash` with selectable
863
- * ['low', 'high', 'max'] efforts, so it lives in `KNOWN_EFFORTS`, not here.)
864
- * Keep in sync via the dsh-commandcode-upstream skill.
872
+ * Source: the bundled model table (dist/cli.mjs), cross-checked with
873
+ * https://commandcode.ai/docs/reference/cli/models. Keep in sync via the
874
+ * dsh-commandcode-upstream skill; a model that GAINS selectable efforts leaves
875
+ * this set for `KNOWN_EFFORTS` (Tencent Hy4 Preview, Kimi K3, the Muse Spark
876
+ * family and MiniMax M3 all took that path).
865
877
  */
866
878
  declare const KNOWN_THINKING_MODELS: ReadonlySet<string>;
867
879
  /**
868
880
  * The minimum subscription plan a model is included in, per the official plan
869
- * pages (`/docs/plans/go`, `/docs/plans/goat`, `/docs/plans/pro`, `/docs/plans/max`
870
- * and `/docs/resources/pricing-limits`). Each plan's model list is a superset of
871
- * the one below it: Go ⊂ GOAT ⊂ Pro ⊂ Provider/Max. Models absent from every
872
- * plan list (Claude Opus/Fable, Fugu Ultra) are Provider-tier.
873
- * `claude-fable-5-1` (Claude Fable 5.1, added in command-code@1.40.0) is
874
- * Provider/Max-tier exactly like `claude-fable-5` — its availability matrix on
875
- * the official plan/pricing pages grants individual-provider/max/ultra and
876
- * teams-pro only, and the CLI's plan-access map blocks it on Go/GOAT/Pro.
877
- * command-code@1.41.0 added `Qwen/Qwen3.8-Max-0902` (Go) and 1.42.0 added
878
- * LongCat 2.0 (Go — a free promo until 2026-09-19, when the backend renamed
879
- * `meituan/LongCat-2.0:free` to the paid `meituan/LongCat-2.0`); 1.43.0 added
880
- * `google/gemini-3.8-flash` (GOAT) and 1.44.0 added `meta/muse-spark-1.3`
881
- * (GOAT) plus its Contributor sibling (Go); command-code@1.52.0 added the
882
- * free `inclusionai/ling-3.0-flash-sante:free` (Go); command-code@1.53.0
883
- * added `deepseek/deepseek-v4.1-flash` (Go); command-code@1.56.0 added
884
- * `Qwen/Qwen3.8-Omni-Flash` (Go); command-code@1.57.0 added
885
- * `z-ai/glm-5.3-flashx` (Go); command-code@1.59.0 added `xai/grok-4.7` (GOAT)
886
- * and 1.60.0 added `stepfun/Step-5-Preview` (Go); command-code@1.62.0 added the
887
- * MiMo V2.6 family — `xiaomi/mimo-v2.6-flash` + `xiaomi/mimo-v2.6-pro` (Go) and
888
- * `xiaomi/mimo-v2.6-pro-ultraspeed` (GOAT) — so the 1.58.0 -> 1.62.0 window's
889
- * only tier changes are additions and the superset chain still holds. The
890
- * 1.62.0 -> 1.65.0 window continues the pattern: 1.64.0 added
891
- * `claude-opus-5-5` (Provider/Max), `gpt-6-sol` (Pro) and `gpt-6-luna` (Go),
892
- * and 1.65.0 added `stealth/space-bunny-alpha` (Go, every tier) — additions
893
- * only, no tier moves.
881
+ * pages (`/docs/plans/go`, `/docs/plans/goat`, `/docs/plans/pro`,
882
+ * `/docs/plans/max` and `/docs/resources/pricing-limits`). Each plan's model
883
+ * list is a superset of the one below it: Go ⊂ GOAT ⊂ Pro ⊂ Provider/Max.
884
+ * 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
886
+ * ids at 53/62/76/84 cumulative, a strict superset chain — every release since
887
+ * 1.49.0 has been additive with no tier move, and per-entry tags below name the
888
+ * release that added each row.
894
889
  *
895
890
  * The Provider API exposes no plan metadata, so this snapshot is the source of
896
891
  * truth for the picker's plan annotation — it answers "which plan do I need to
@@ -901,13 +896,6 @@ declare const KNOWN_THINKING_MODELS: ReadonlySet<string>;
901
896
  * dsh-commandcode-upstream skill).
902
897
  */
903
898
  declare const KNOWN_PLANS: Readonly<Record<string, string>>;
904
- /** Official display labels for each plan tier. */
905
- declare const PLAN_LABELS: Readonly<Record<string, string>>;
906
- /**
907
- * Plan-tier sort weights, low to high. Models outside the snapshot (unknown
908
- * plans) sort after every known tier, keeping known models predictable.
909
- */
910
- declare const PLAN_ORDER: Readonly<Record<string, number>>;
911
899
  /**
912
900
  * Comparator for the model picker: free models first (zero credit cost, usable
913
901
  * by every account), then by plan tier (lowest first), then by model name,
@@ -922,11 +910,11 @@ declare function compareByPlan(a: {
922
910
  }): number;
923
911
  /**
924
912
  * Subscription plan table, synced from the official CLI bundle's plan maps
925
- * (located by the `"individual-go"` key in command-code@1.53.0 `dist/cli.mjs`,
926
- * re-verified unchanged through 1.53.0): subscription `planId`
927
- * prefix → display name and the plan's monthly credit total. This is the
928
- * account's own subscription (from `/alpha/billing/subscriptions`) — distinct
929
- * from {@link KNOWN_PLANS}, which maps catalog models to their minimum tier.
913
+ * (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
915
+ * the plan's monthly credit total. This is the account's own subscription
916
+ * (from `/alpha/billing/subscriptions`) — distinct from {@link KNOWN_PLANS},
917
+ * which maps catalog models to their minimum tier.
930
918
  *
931
919
  * `tierWeight` is plugin-added (not from the CLI maps): the plan's rank on
932
920
  * the {@link PLAN_ORDER} scale, used by the picker's plan filter
@@ -982,8 +970,13 @@ declare function modelVisibleInPlan(modelId: string, access: CommandCodeBillingA
982
970
  * render time against `Date.now()`), the deal label is hidden until the
983
971
  * snapshot is refreshed from the official page. `undefined` means
984
972
  * "no expiry" (permanent).
985
- * - `free` marks models whose requests cost no credits (Laguna S 2.1), shown
986
- * as a `FREE` badge; it degrades to a plain discount once the deal lapses.
973
+ * - `free` marks models whose requests cost no credits, shown as a `FREE`
974
+ * badge; it degrades to a plain discount once the deal lapses.
975
+ *
976
+ * A deal whose promo ENDS is removed here rather than left to lapse on
977
+ * `expiresAt`: the pricing page drops the entry, so a lapsed row would badge a
978
+ * model the catalog now serves at full price (Grok 4.7 reverts to $2.00 in /
979
+ * $6.00 out after its 2026-09-27 window).
987
980
  *
988
981
  * Keep in sync with the official pricing page when deals change (see the
989
982
  * dsh-commandcode-upstream skill).
@@ -1003,24 +996,11 @@ declare const KNOWN_DEALS: Readonly<Record<string, KnownDeal>>;
1003
996
  * charges by the hour: peak hours are 01:00–04:00 and 06:00–10:00 UTC (7h per
1004
997
  * weekday, full price) **Monday to Friday only**; the other 17 hours of a
1005
998
  * weekday and every hour of Saturday/Sunday (UTC) are off-peak at half price.
1006
- * The V4 Flash Vision (exp) variant (command-code@1.32.0) shares the V4 Flash
1007
- * rates exactly — $0.15/$0.60 off-peak and $0.30/$1.20 peak, per the page's own
1008
- * `timeOfDay` block, not merely 2× its own off-peak figures: a rate that is
1009
- * internally consistent can still be the wrong row, which is why the vendored
1010
- * price table (`./model-prices.ts`) is synced from the page and not hand-kept.
1011
- * The picker shows the
1012
- * *current* state as a compact
1013
- * label (`Peak`/`Half`) matching the English noun style of the other markers
1014
- * (`Image`, `FREE`), so a developer can tell at a glance whether calling the
1015
- * model right now is cheap or expensive.
1016
- *
1017
- * Authoritative extraction: the pricing page embeds a model JSON array whose
1018
- * hourly-priced entries carry a `timeOfDay` block
1019
- * (`{ windows: "01–04 & 06–10 UTC, Mon–Fri", peakHoursPerDay: 7,
1020
- * offPeakHoursPerDay: 17, peak: {...}, offPeak: {...} }`). Exactly four models
1021
- * carry it: V4 Pro, V4 Flash, V4 Flash Vision (exp), and V4.1 Flash (added in
1022
- * command-code@1.53.0 at $0.15/$0.60 off-peak, $0.30/$1.20 peak — the same
1023
- * schedule as the other three).
999
+ * Exactly five models carry the page's `timeOfDay` block (the five rows below).
1000
+ * The picker shows the *current* state as a compact label (`Peak`/`Half`)
1001
+ * matching the English noun style of the other markers (`Image`, `FREE`), so a
1002
+ * developer can tell at a glance whether calling the model right now is cheap
1003
+ * or expensive.
1024
1004
  *
1025
1005
  * Extraction caution: the rendered HTML rows are a trap. Each annotation div
1026
1006
  * sits inside its OWN row's container, immediately before the NEXT row starts,
@@ -1028,18 +1008,19 @@ declare const KNOWN_DEALS: Readonly<Record<string, KnownDeal>>;
1028
1008
  * to the model printed after it — that is how `deepseek/deepseek-v4-flash-fast`
1029
1009
  * was wrongly added here (its row is flat-priced at $0.28/$0.56/$0.07 and has
1030
1010
  * no `timeOfDay` block). Trust the embedded JSON's `timeOfDay` membership and
1031
- * the 2× price relation, never the flat-text neighbor.
1011
+ * the 2× price relation, never the flat-text neighbor. A rate that is
1012
+ * internally consistent can still be the wrong row (the V4 Flash Vision (exp)
1013
+ * variant is priced from the page's own `timeOfDay` block, not from 2× its own
1014
+ * off-peak figures), which is why the vendored price table
1015
+ * (`./model-prices.ts`) is synced from the page and not hand-kept.
1032
1016
  *
1033
1017
  * Keep in sync with the official pricing page when the model set, the peak
1034
1018
  * windows, or the weekday rule change (see the dsh-commandcode-upstream skill).
1035
1019
  */
1036
1020
  declare const KNOWN_PEAK_PRICING: ReadonlySet<string>;
1037
1021
  /**
1038
- * Whether `now` (defaults to `Date.now()`) falls in a peak-pricing window for
1039
- * time-of-day-priced models. Peak rates apply Monday–Friday (UTC) only: the
1040
- * official rule charges Saturday and Sunday completely off-peak for all 24
1041
- * hours, so a weekend timestamp is off-peak even inside `PEAK_HOUR_RANGES`.
1042
- * `undefined` for models outside the snapshot.
1022
+ * As {@link isPeakPricingHour}, plus the {@link KNOWN_PEAK_PRICING} membership
1023
+ * test; `undefined` for models outside the snapshot.
1043
1024
  */
1044
1025
  declare function peakPricingState(modelId: string, now?: number): 'peak' | 'off-peak' | undefined;
1045
1026
  /**
@@ -1122,6 +1103,20 @@ interface CommandCodeCatalogModel {
1122
1103
  * capability snapshot, so the Host stamps it per entry.
1123
1104
  */
1124
1105
  tier?: string;
1106
+ /**
1107
+ * Per-model MONTHLY allowance in USD: how much of the plan's monthly credit
1108
+ * pool this one model may draw, already resolved by the Host against the
1109
+ * 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
1111
+ * (Go, Provider, Max, Ultra) or when billing could not be read — the browser
1112
+ * shows nothing rather than guessing a neighbouring tier's figure.
1113
+ *
1114
+ * Deliberately a plain number and not the `{ goat, pro }` pair: the bracket is
1115
+ * a Host decision that already depends on facts the browser does not hold, and
1116
+ * shipping both figures would invite a second, divergent rule here. Note this
1117
+ * is dollars per MONTH, unlike every rate in `CommandCodePriceTable`.
1118
+ */
1119
+ allowance?: number;
1125
1120
  }
1126
1121
  /** The model-catalog Remote result: the full catalog, sorted for picking. */
1127
1122
  interface CommandCodeCatalog {
@@ -1193,19 +1188,16 @@ interface CommandCodePriceTable {
1193
1188
  //#endregion
1194
1189
  //#region src/command-locales.d.ts
1195
1190
  /**
1196
- * Locale copy for the `/commandcode` usage command and the friendly
1197
- * image-gate error rewrite. Distinct from `./client/locales.ts` (the
1198
- * settings-page namespace `settings.commandcode`): the command runs on the
1199
- * Host and has no access to the client's `ctx.locale`, so the dictionaries
1200
- * are exposed as plain constants for direct lookup; the resolver lives in
1201
- * `pickCommandLocale()`. The image-gate wrapper also lives on the client
1202
- * but is reached from a non-React path that has no `t` in scope, so the
1203
- * same dictionaries serve both surfaces.
1191
+ * zh/en copy for the Host-side `/commandcode` usage command, as plain
1192
+ * constants: the command runs on the Host and has no access to the client's
1193
+ * `ctx.locale`, so the active locale is resolved by `pickCommandLocale()` and
1194
+ * the dictionaries are read by direct lookup. Distinct from
1195
+ * `./client/locales.ts` (the settings-page `settings.commandcode` namespace).
1204
1196
  *
1205
- * zh is the source of truth for the key set; en must carry the exact same
1206
- * keys — a mismatch is a compile error at the lookup site.
1197
+ * zh is the source of truth for the key set; `en` must carry exactly the same
1198
+ * keys, which the `Record` type makes a compile error.
1207
1199
  */
1208
- /** Active locale id recognized by the command and the image-gate wrapper. */
1200
+ /** Active locale id for the `/commandcode` command. */
1209
1201
  type LocaleId = 'zh' | 'en';
1210
1202
  //#endregion
1211
1203
  //#region src/commands.d.ts
@@ -1220,11 +1212,9 @@ interface CommandCodeCommandDeps<C extends CommandCodeConnectionOptions = Comman
1220
1212
  */
1221
1213
  reports?: () => Promise<CommandCodeAccountsReport>;
1222
1214
  /**
1223
- * Resolve the active locale for one command run. The plugin entry wires
1224
- * this from `Config.lang` and the shell's `LC_ALL`/`LANG`. Absent in
1225
- * programmatic setups (notably the existing test), the command renders
1226
- * with the default locale (`'zh'`) — historically the only language the
1227
- * command ever shipped in.
1215
+ * Resolve the active locale for one command run; the plugin entry wires it
1216
+ * from `Config.lang` and the shell's `LC_ALL`/`LANG`. Absent (notably in
1217
+ * tests), the command renders with the default locale `'zh'`.
1228
1218
  */
1229
1219
  getLocale?: () => LocaleId;
1230
1220
  }
@@ -1286,9 +1276,9 @@ declare const loginStatusSchema: TypertSchema<CommandCodeLoginStatus>;
1286
1276
  /**
1287
1277
  * The browser-login face the usage service exposes (`commandcode/login*`).
1288
1278
  * Backed by the Host-half {@link !CommandCodeLoginFlow} when the plugin entry
1289
- * wired one; absent, `status`/`cancel` degrade to the idle status while
1290
- * `begin` rejects with a plain message (so the page's manual paste path
1291
- * stays the fallback instead of hanging).
1279
+ * wired one; absent, `status`/`cancel` degrade to the idle status and `begin`
1280
+ * rejects with a plain message, so the page's manual paste path stays the
1281
+ * fallback instead of hanging.
1292
1282
  */
1293
1283
  interface LoginFlowFacade {
1294
1284
  /** Start (or rejoin) an attempt; rejects when it cannot start at all. */
@@ -1309,10 +1299,9 @@ interface CommandCodeUsageDeps<C extends CommandCodeConnectionOptions = CommandC
1309
1299
  */
1310
1300
  reports?: () => Promise<CommandCodeAccountsReport>;
1311
1301
  /**
1312
- * Model-catalog source for the settings page's model editors (the
1313
- * routing-rule editor and the visible-models filter; wired by the plugin
1314
- * entry). Absent, the `models` endpoint answers an empty list — the page's
1315
- * editors degrade to the empty state.
1302
+ * Model-catalog source for the settings page's model editors. Absent, the
1303
+ * `models` endpoint answers an empty list — the page's editors degrade to
1304
+ * the empty state.
1316
1305
  */
1317
1306
  listModels?: () => Promise<CommandCodeCatalog>;
1318
1307
  /**
@@ -1322,11 +1311,7 @@ interface CommandCodeUsageDeps<C extends CommandCodeConnectionOptions = CommandC
1322
1311
  * only to stub it in a test.
1323
1312
  */
1324
1313
  prices?: () => CommandCodePriceTable;
1325
- /**
1326
- * The browser-login flow (wired by the plugin entry). Absent means the
1327
- * login endpoints answer `idle` / reject with a plain message — the page's
1328
- * manual paste path stays the fallback.
1329
- */
1314
+ /** The browser-login flow (wired by the plugin entry); see {@link LoginFlowFacade}. */
1330
1315
  login?: LoginFlowFacade;
1331
1316
  }
1332
1317
  /**
@@ -1340,34 +1325,30 @@ declare class CommandCodeUsageService<C extends CommandCodeConnectionOptions = C
1340
1325
  private readonly deps;
1341
1326
  constructor(ctx: Context, deps: CommandCodeUsageDeps<C>);
1342
1327
  /**
1343
- * Account, usage, and credit state for the settings page's account card —
1344
- * one entry per pool account when the plugin entry wired `reports`, a
1345
- * single default-account entry otherwise. Degrades per endpoint like the
1346
- * `/commandcode` command (failures land in `report.failures`); throws
1347
- * `MISSING_CREDENTIAL` when no key resolves, which the Gateway folds into
1348
- * the failure branch the page renders as a hint.
1328
+ * Account, usage, and credit state for the settings page's account card.
1329
+ * Degrades per endpoint like the `/commandcode` command (failures land in
1330
+ * `report.failures`); throws `MISSING_CREDENTIAL` when no key resolves, which
1331
+ * the Gateway folds into the failure branch the page renders as a hint.
1349
1332
  */
1350
1333
  report(): Promise<CommandCodeAccountsReport>;
1351
1334
  /**
1352
- * The full model catalog for the settings page's model editors (the
1353
- * routing-rule editor and the visible-models filter). The browser never
1354
- * calls the Command Code API directly — the Host serves the catalog
1355
- * (already fetched/cached by the adapter) so models can be picked from
1356
- * the live list instead of typed by hand.
1335
+ * The full model catalog for the settings page's model editors. The browser
1336
+ * never calls the Command Code API directly — the Host serves the catalog
1337
+ * (already fetched/cached by the adapter) so models can be picked from the
1338
+ * live list instead of typed by hand.
1357
1339
  */
1358
1340
  models(): Promise<CommandCodeCatalog>;
1359
1341
  /**
1360
1342
  * The model price table the composer prices an in-progress session with.
1361
- * Static vendored data (the official pricing page's rates), served Host-side
1362
- * so the browser bundle never carries a copy that could drift from the
1363
- * snapshot, and so a price update reaches an open page without a rebuild.
1343
+ * Static vendored data, served Host-side so the browser bundle never carries
1344
+ * a copy that could drift from the snapshot, and so a price update reaches an
1345
+ * open page without a rebuild.
1364
1346
  */
1365
1347
  prices(): Promise<CommandCodePriceTable>;
1366
1348
  /**
1367
1349
  * Start (or rejoin) a browser-login attempt and return its fresh status —
1368
1350
  * `waiting` carrying the Studio URL. Rejects when the flow cannot start
1369
- * (no free loopback port, disposed plugin); the Gateway folds the throw
1370
- * into the failure branch the page renders.
1351
+ * (no free loopback port, disposed plugin).
1371
1352
  */
1372
1353
  loginBegin(targetRef?: string): Promise<CommandCodeLoginStatus>;
1373
1354
  /** Poll a login attempt's status. */
@@ -1469,16 +1450,15 @@ declare class CommandCodeLoginFlow {
1469
1450
  * asynchronously (`complete()`), and that window is open to a cancel or a
1470
1451
  * fresh `begin()`; the generation lets a late completion recognize that it
1471
1452
  * no longer owns the status face and stop instead of storing a credential
1472
- * the user cancelled and flipping the page back to success.
1453
+ * the user cancelled.
1473
1454
  */
1474
1455
  private attemptSeq;
1475
1456
  /**
1476
- * The start currently binding a port, if any. Every `begin()` that arrives
1477
- * before it settles joins it: two independent starts would each bind their
1478
- * own loopback server, and only the LAST one is reachable by
1479
- * {@link CommandCodeLoginFlow.teardown} — the orphan keeps listening and
1480
- * answering `/callback` for the process's lifetime, and ten of them exhaust
1481
- * the port window so browser login dies until the Host restarts.
1457
+ * The start currently binding a port, if any. Every `begin()` arriving before
1458
+ * it settles joins it: two independent starts would each bind a loopback
1459
+ * server, and only the LAST one is reachable by `teardown()` — the orphan
1460
+ * keeps answering `/callback` for the process's lifetime, and ten of them
1461
+ * exhaust the port window so browser login dies until the Host restarts.
1482
1462
  */
1483
1463
  private starting;
1484
1464
  /** A live attempt's destination; different rows may not rejoin it. */
@@ -1551,32 +1531,25 @@ declare class CommandCodeLoginFlow {
1551
1531
  declare const COMMANDCODE_SEARCH_PROVIDER_ID = "commandcode";
1552
1532
  /**
1553
1533
  * The factory-declared search provider id dsh ships by default (from
1554
- * `dsh-base`'s cordis patch `web.config.searchProvider`). Kept as a
1555
- * documented reference only: disabling this plugin's `webSearch` toggle
1556
- * restores the previously selected backend (see
1557
- * {@link applyCommandCodeSearchSelection}) — it never forces this default,
1558
- * because forcing it is what used to silence sibling search plugins such as
1559
- * modsearch even with Command Code search turned off (issue #26).
1534
+ * `dsh-base`'s cordis patch `web.config.searchProvider`). A documented
1535
+ * reference only: disabling this plugin's `webSearch` toggle restores the
1536
+ * PREVIOUSLY selected backend, never this default (issue #26).
1560
1537
  */
1561
1538
  declare const DEFAULT_WEB_SEARCH_PROVIDER_ID = "deepseek-official";
1562
1539
  /**
1563
1540
  * Tracked web-search selection state for one mounted `WebRuntime`.
1564
1541
  *
1565
- * `owner` marks whether this plugin currently owns the selection (i.e. it
1566
- * wrote `commandcode` and has not given it back yet). `displaced` is the
1567
- * backend id the plugin displaced when it took over — restored when the
1568
- * toggle turns off or the plugin unloads. `undefined` means "nothing was
1569
- * configured, leave auto-select" and must round-trip untouched: writing the
1570
- * factory default instead would still override a sibling plugin's own
1571
- * constructor-time pin.
1542
+ * `owner` marks whether this plugin currently owns the selection. `displaced`
1543
+ * is the backend it took over — restored on disable or unload — and
1544
+ * `undefined` means "nothing was configured, leave auto-select" and must
1545
+ * round-trip untouched.
1572
1546
  *
1573
1547
  * `preexisting` is the one fact `displaced` cannot carry: an `undefined`
1574
1548
  * `displaced` means EITHER "the field was unset when we took over" (give
1575
- * `undefined` back on disable) OR "the field already read `commandcode`"
1576
- * (touch nothing on disable — see {@link applyCommandCodeSearchSelection}).
1577
- * Collapsing the two is what turned a user's own `searchProvider: commandcode`
1578
- * pin into an auto-select — and then into `WEB_PROVIDER_AMBIGUOUS` on every
1579
- * search — the moment this plugin was disabled or unloaded.
1549
+ * `undefined` back) OR "the field already read `commandcode`" (touch
1550
+ * nothing). Collapsing the two is what turned a user's own
1551
+ * `searchProvider: commandcode` pin into an auto-select, and then into
1552
+ * `WEB_PROVIDER_AMBIGUOUS` on every search.
1580
1553
  */
1581
1554
  interface CommandCodeSearchSelection {
1582
1555
  owner: boolean;
@@ -1591,24 +1564,15 @@ declare function commandCodeSearchSelection(): CommandCodeSearchSelection;
1591
1564
  * providers (issue #26).
1592
1565
  *
1593
1566
  * - Enabling writes `commandcode` and remembers whatever it displaced. When
1594
- * the plugin already owns the selection (e.g. a settings save while still
1595
- * on), the original `displaced` value is kept — the field currently holds
1596
- * our own id, which must never be mistaken for the user's backend.
1597
- * - Disabling hands the selection back to the remembered backend. When the
1598
- * state holds no memory (a fresh boot straight into `webSearch: false`),
1599
- * the field is left alone: the runtime's current value — a sibling's
1600
- * cordis pin such as `searchProvider: modsearch`, or unset for
1601
- * auto-select — already says what the user wants.
1602
- * - When the field already reads `commandcode` at first touch (e.g. a
1603
- * surviving runtime the plugin did not set, or a manual
1604
- * `searchProvider: commandcode` pin), `displaced` stays undefined and
1605
- * `preexisting` is set, so the later disable is a no-op rather than a guess
1606
- * at the factory default. A "no-op" means the field is left ALONE: writing
1607
- * that `undefined` back would destroy the user's own pin, and dsh-web reads
1608
- * a cleared `searchProviderId` as auto-select — where a second usable
1609
- * provider (the shipped `deepseek-official` is usable whenever a DeepSeek
1610
- * key resolves) makes EVERY later search throw
1611
- * `WEB_PROVIDER_AMBIGUOUS`.
1567
+ * the plugin already owns the selection, the original `displaced` is kept —
1568
+ * the field currently holds our own id, which must never be mistaken for the
1569
+ * user's backend.
1570
+ * - Disabling hands the selection back to the remembered backend only when
1571
+ * this plugin actually took it over. A fresh boot straight into
1572
+ * `webSearch: false`, or a `preexisting` field, leaves it ALONE: writing the
1573
+ * empty memory back would clear the user's own `searchProvider: commandcode`
1574
+ * pin and hand the selection to dsh-web's auto-select, where a second usable
1575
+ * provider makes every later search throw `WEB_PROVIDER_AMBIGUOUS`.
1612
1576
  *
1613
1577
  * Never throws: like the low-level rewrite, a hardened runtime shape degrades
1614
1578
  * to registered-but-unselected.
@@ -1624,11 +1588,11 @@ interface CommandCodeSearchProviderDeps {
1624
1588
  fetchImpl?: typeof fetch;
1625
1589
  }
1626
1590
  /**
1627
- * A `ctx.web` search provider backed by the Command Code Provider API. Reuses
1628
- * the plugin's credential chain and `apiBase`, so search "just works" with the
1629
- * existing key — the model-facing `web_search` tool needs no separate
1630
- * configuration. Selection between multiple search providers is the web seam's
1631
- * job (pin `searchProvider: commandcode` if ambiguous).
1591
+ * A `ctx.web` search provider backed by the Command Code Provider API, reusing
1592
+ * the plugin's credential chain and `apiBase` so the model-facing `web_search`
1593
+ * tool needs no separate configuration. Selection between multiple search
1594
+ * providers is the web seam's job (pin `searchProvider: commandcode` if
1595
+ * ambiguous).
1632
1596
  */
1633
1597
  declare class CommandCodeSearchProvider implements WebSearchProvider {
1634
1598
  private readonly deps;
@@ -1650,11 +1614,9 @@ interface TuiLocalizedText {
1650
1614
  type TuiSettingsFieldKind = 'text' | 'number' | 'boolean' | 'select';
1651
1615
  /** One choice of an options-bearing field. */
1652
1616
  interface TuiSettingsFieldOption {
1653
- /** Stored value. */
1654
1617
  readonly value: string;
1655
1618
  /** Display label (English; also the fallback). */
1656
1619
  readonly label: string;
1657
- /** Provider-owned translations for the label. */
1658
1620
  readonly descriptions?: TuiLocalizedText;
1659
1621
  }
1660
1622
  /** The write one field's draft stages when the section is saved. */
@@ -1670,11 +1632,9 @@ interface TuiSettingsField {
1670
1632
  readonly path: readonly string[];
1671
1633
  /** Short field label (English; also the fallback). */
1672
1634
  readonly label: string;
1673
- /** Provider-owned translations for the label. */
1674
1635
  readonly descriptions?: TuiLocalizedText;
1675
1636
  /** Optional one-line help rendered under the field. */
1676
1637
  readonly hint?: string;
1677
- /** Provider-owned translations for the hint. */
1678
1638
  readonly hintDescriptions?: TuiLocalizedText;
1679
1639
  /** Optional group id; grouped fields render on that group's subpage. */
1680
1640
  readonly group?: string;
@@ -1706,7 +1666,6 @@ interface TuiSettingsGroup {
1706
1666
  readonly id: string;
1707
1667
  /** Group title (English; also the fallback). */
1708
1668
  readonly title: string;
1709
- /** Provider-owned translations for the title. */
1710
1669
  readonly descriptions?: TuiLocalizedText;
1711
1670
  }
1712
1671
  /** One plugin's section inside the dsh-TUI settings screen. */
@@ -1715,7 +1674,6 @@ interface TuiSettingsSection {
1715
1674
  readonly ns: string;
1716
1675
  /** Section title (English; also the fallback). */
1717
1676
  readonly title: string;
1718
- /** Provider-owned translations for the title. */
1719
1677
  readonly descriptions?: TuiLocalizedText;
1720
1678
  /** Optional navigation groups, in display order. */
1721
1679
  readonly groups?: readonly TuiSettingsGroup[];
@@ -1783,25 +1741,22 @@ interface CommandCodeTuiSettingsDeps {
1783
1741
  * Build the section descriptor. Pure, so tests can pin the exact fields
1784
1742
  * without a dsh-TUI host.
1785
1743
  *
1786
- * Field choices worth keeping: the two option-bearing fields (`activeAccount`,
1787
- * `lang`) are `text` + `options` rather than `select`, because a `select`
1788
- * cannot express "unset" — cycling only ever lands on a declared option, so a
1789
- * `select` would strand the user on a pinned value with no way back to
1790
- * automatic. The `auto` sentinel plus a `parse` that clears the path keeps the
1791
- * unset state reachable. `filterModelsByPlan` formats its EFFECTIVE default
1792
- * (unset means true at the adapter), so a fresh install reads true instead of
1793
- * the screen's "(empty)".
1744
+ * The two option-bearing fields (`activeAccount`, `lang`) are `text` +
1745
+ * `options` rather than `select`, because a `select` cannot express "unset" —
1746
+ * cycling only ever lands on a declared option, so it would strand the user on
1747
+ * a pinned value with no way back to automatic. The `auto` sentinel plus a
1748
+ * `parse` that clears the path keeps unset reachable. `filterModelsByPlan`
1749
+ * formats its EFFECTIVE default (unset means true at the adapter) so a fresh
1750
+ * install reads true instead of the screen's "(empty)".
1794
1751
  */
1795
1752
  declare function buildCommandCodeTuiSection(deps: CommandCodeTuiSettingsDeps): TuiSettingsSection;
1796
1753
  /**
1797
1754
  * Register the Command Code section on a dsh-TUI host.
1798
1755
  *
1799
- * @param ctx - the context of an activated `tuiSettingsSections` injection.
1800
- * @param deps - plugin-owned facts the section reads.
1801
1756
  * @returns a refresh function that re-registers the section when a fact it
1802
- * renders changed (the plugin entry calls it from its settings `onChange`
1803
- * hook), or `undefined` when the seam is unusable. The returned function is
1804
- * inert after the fiber is torn down.
1757
+ * renders changed — the plugin entry calls it from its
1758
+ * `loader/volatile-update` listener — or `undefined` when the seam is
1759
+ * unusable. The returned function is inert after the fiber is torn down.
1805
1760
  */
1806
1761
  declare function applyCommandCodeTuiSettings(ctx: Context, deps: CommandCodeTuiSettingsDeps): (() => void) | undefined;
1807
1762
  //#endregion
@@ -1834,16 +1789,20 @@ interface Config {
1834
1789
  requestTimeoutMs?: number;
1835
1790
  /** Milliseconds a stream may stall before being treated as a dead connection; defaults to 300s. */
1836
1791
  streamIdleTimeoutMs?: number;
1792
+ /**
1793
+ * Opt-in cache mitigation for the CLI route: after this model has answered,
1794
+ * durably offload its earlier images before later turns. Old pixels then
1795
+ * require a fresh read/attachment if the model needs to inspect them again.
1796
+ * Defaults to false to preserve full image history.
1797
+ */
1798
+ offloadSeenImagesForCache?: boolean;
1837
1799
  /**
1838
1800
  * Transport failures one request absorbs before the failure is surfaced;
1839
- * defaults to 5. The route's retry policy is near-unbounded on purpose (1000
1840
- * attempts, waits doubling to 15 minutes) because that shape is for the
1841
- * failures a provider asks to have retried — an exhausted rate-limit window,
1842
- * a gateway 520. A transport failure is not one of those: the first attempts
1843
- * recover an ordinary blip (the default 5 retries are scheduled 0.5/1/2/4/8 s
1844
- * after the failures before them, so ~15.5 s of grace), and after that the
1845
- * wait is pure stall, so the retries are capped here. Raise it on a genuinely
1846
- * flaky link; 0 surfaces every transport failure immediately.
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
1804
+ * one of those, and after ~15.5 s of grace the wait is pure stall, so it is
1805
+ * capped here (issue #39). 0 surfaces every transport failure immediately.
1847
1806
  */
1848
1807
  transportMaxRetries?: number;
1849
1808
  /**
@@ -1855,8 +1814,7 @@ interface Config {
1855
1814
  filterModelsByPlan?: boolean;
1856
1815
  /**
1857
1816
  * Visible-model allowlist: catalog model ids shown in pickers. Empty or
1858
- * unset means "show everything". Persisted by the settings page's model
1859
- * filter card; applies after the subscription-tier filter.
1817
+ * unset means "show everything"; applied after the subscription-tier filter.
1860
1818
  */
1861
1819
  visibleModels?: string[];
1862
1820
  /**
@@ -1864,117 +1822,82 @@ interface Config {
1864
1822
  * list, keyed by catalog id (`true` = listed, `false` = hidden). An id here
1865
1823
  * decides that model on its own; an id absent here follows `visibleModels`.
1866
1824
  * dsh-TUI keys a staged edit by the field's path, so the checkboxes need one
1867
- * path per model — a map — because a boolean field cannot express "this id
1868
- * is a member of the array".
1825
+ * path per model — a map.
1869
1826
  */
1870
1827
  modelVisibility?: Record<string, boolean>;
1871
1828
  /**
1872
1829
  * Extra accounts for multi-account rotation. The top-level
1873
1830
  * `apiKey`/`apiKeyEnv` (plus the CLI auth file) always form the first
1874
- * (`default`) account; each entry here adds one more. When a request is
1875
- * rejected pre-stream with 429 (usage window exhausted) or 401, the next
1876
- * account's key retried transparently; when every account is exhausted the
1877
- * request fails with a `RATE_LIMIT` error naming the earliest window
1878
- * reset. Entries without `apiKey` or `apiKeyEnv` are ignored.
1831
+ * (`default`) account; each entry here adds one more, and an entry with
1832
+ * neither `apiKey` nor `apiKeyEnv` is ignored. A pre-stream 429/401 marks
1833
+ * the key and the next account's key is retried transparently.
1879
1834
  */
1880
1835
  accounts?: CommandCodeAccountConfig[];
1881
1836
  /**
1882
1837
  * Manually selected active account: a slot id — `default`, or an extra
1883
- * account's credential reference (e.g. `COMMANDCODE_API_KEY_2`). The
1884
- * selected account serves whenever it is usable; an unknown id or an
1885
- * exhausted selected account falls back to the first usable slot (automatic
1886
- * rotation still applies). Unset means "first usable account".
1838
+ * account's credential reference (e.g. `COMMANDCODE_API_KEY_2`). It serves
1839
+ * whenever usable; an unknown id or an exhausted one falls back to rotation
1840
+ * order. Unset means "first usable account".
1887
1841
  */
1888
1842
  activeAccount?: string;
1889
1843
  /**
1890
- * Model → account routing rules. Each rule lists catalog model ids to an
1891
- * account slot id (`default`, or an extra account's credential reference).
1892
- * When a request's model is in a rule's list and the routed account is
1893
- * usable, that account serves — before the manual {@link activeAccount} and
1894
- * the passive rotation order. A routed account that is exhausted or invalid
1895
- * falls back to the normal selection, so the router is a hint, never a hard
1896
- * gate. The first matching rule wins.
1844
+ * Model → account routing rules, each listing catalog model ids for an
1845
+ * account slot id. A matching rule's account serves before {@link
1846
+ * activeAccount} and the passive rotation order; an unusable routed account
1847
+ * falls back, so the router is a hint, never a hard gate. First match wins.
1897
1848
  */
1898
1849
  modelAccountRules?: CommandCodeModelAccountRule[];
1899
1850
  /**
1900
1851
  * Whether to use Command Code as the backend for dsh's model-facing
1901
- * `web_search` tool. When enabled, the plugin registers a `commandcode`
1902
- * search provider on `ctx.web` AND selects `commandcode` in the web seam
1903
- * (so it wins over the shipped `deepseek-official` or a sibling search
1904
- * plugin's pin), using the SAME Command Code API key/base as chat. When
1905
- * disabled, the selection is handed back to whichever backend was there
1906
- * before — turning it off never forces the factory default, so a sibling
1907
- * search plugin (e.g. modsearch) keeps working (issue #26). The rewrite
1908
- * rides dsh's internal `searchProviderId`, which is read per search call,
1909
- * so a setting change lands on the next search without a restart.
1910
- * Defaults to true.
1852
+ * `web_search` tool. Enabled registers a `commandcode` search provider on
1853
+ * `ctx.web` AND selects it over the shipped `deepseek-official` (or a
1854
+ * sibling plugin's pin), reusing the SAME key and apiBase as chat. Disabling
1855
+ * hands the selection back to whichever backend was there before, so a
1856
+ * sibling search plugin (e.g. modsearch) keeps working (issue #26). The
1857
+ * write rides dsh's internal `searchProviderId`, read per search call, so a
1858
+ * setting change lands on the next search without a restart. Defaults true.
1911
1859
  */
1912
1860
  webSearch?: boolean;
1913
1861
  /**
1914
1862
  * Whether the Web sidebar shows the plans & quota card
1915
- * (`sidebar.footer.action`). Defaults to false: the card is opt-in, so an
1916
- * unset document renders no quota surface in the sidebar and mounts no
1917
- * background usage poll for it. Only the sidebar entry is affected — the
1918
- * dashboard cell behind it stays registered, it simply has no trigger until
1919
- * the toggle is on. Read by the browser client; the adapter ignores it.
1863
+ * (`sidebar.footer.action`). Defaults to false, so an unset document mounts
1864
+ * no sidebar quota surface and no background usage poll for it; the
1865
+ * dashboard cell behind it stays registered. Read by the browser client; the
1866
+ * adapter ignores it.
1920
1867
  */
1921
1868
  showSidebarQuota?: boolean;
1922
- /**
1923
- * Whether the Command Code decision model (`typesafe/jev`) may auto-approve
1924
- * shell commands that dsh was about to ask about. Defaults to FALSE: the
1925
- * guard turns a model's opinion into a one-shot approval grant, so it is
1926
- * opt-in, and the command text (plus the agent's own description of it) is
1927
- * sent to Command Code to judge. Only commands a policy already wanted a
1928
- * human to look at are ever judged, and only a confident "safe" verdict
1929
- * (`commandGuardLevel`) skips the prompt — every other outcome, including
1930
- * any failure of the decision call itself, delegates to the normal approval
1931
- * flow. See `./command-guard.ts`.
1932
- */
1933
- commandGuard?: boolean;
1934
- /**
1935
- * How confident every verdict must be before the guard skips the approval
1936
- * prompt: `high` (0.95), `medium` (0.9, the default) or `low` (0.8). A lower
1937
- * level approves more, on less evidence. The decision budget is fixed
1938
- * (`COMMAND_GUARD_TIMEOUT_MS`), so this is the guard's only tuning knob.
1939
- */
1940
- commandGuardLevel?: CommandGuardLevel;
1941
1869
  /**
1942
1870
  * Whether requests enforce zero data retention: the provider then routes
1943
1871
  * them only through upstreams that keep no prompts/completions and never
1944
1872
  * train on them (its own opt-in, `CMD_ZDR=1` in the CLI / `x-cmd-zdr: 1` on
1945
1873
  * the Provider API). Defaults to FALSE: `zdr` changes WHERE a request is
1946
- * served. Every chat request carries the header when enabled; a model with
1947
- * no ZDR-capable upstream fails with 422 `cmd_zdr_no_providers` instead of
1948
- * being routed through an upstream that retains data. ZDR capacity is
1949
- * priced pass-through and usually costs more, and the price readout keeps
1950
- * quoting the ordinary catalog rates (the real per-request price shows in
1951
- * Command Code's Studio). The decision endpoint behind the command guard is
1952
- * never ZDR-enforced — see `./systemone.ts`.
1874
+ * served. Every chat request carries the header when enabled, and a model
1875
+ * with no ZDR-capable upstream fails with 422 `cmd_zdr_no_providers` rather
1876
+ * than losing the guarantee. ZDR capacity is priced pass-through and usually
1877
+ * costs more; the price readout keeps quoting the ordinary catalog rates
1878
+ * (the real per-request price shows in Command Code's Studio).
1953
1879
  */
1954
1880
  zdr?: boolean;
1955
1881
  /**
1956
1882
  * Language override for the `/commandcode` Host-side command's user-facing
1957
- * copy. Host commands cannot read the client's `ctx.locale`, so this is
1958
- * the explicit knob: `'zh'` or `'en'`. Unset means the command reads
1959
- * `LC_ALL`/`LANG` from the launching shell, falling back to `'zh'`. The
1960
- * web settings page is unaffected — it follows the browser's language
1961
- * preference on its own. Two surfaces, two independent locales. The
1962
- * declared type is `string` (the schemastery `pattern` cannot narrow
1963
- * literal types); an unknown value is treated as "unset" by
1964
- * `pickCommandLocale`.
1883
+ * copy. Host commands cannot read the client's `ctx.locale`, so this is the
1884
+ * explicit knob: `'zh'` or `'en'`, defaulting to `'zh'`. The web settings
1885
+ * page is unaffected — it follows the browser's language preference on its
1886
+ * own. The declared type is `string` (the schemastery `pattern` cannot
1887
+ * narrow literal types); `pickCommandLocale` treats an unknown value as
1888
+ * "unset" and then reads `LC_ALL`/`LANG`, which is only reachable when
1889
+ * `lang` is absent from a programmatically built config.
1965
1890
  */
1966
1891
  lang?: string;
1967
1892
  }
1968
1893
  /**
1969
1894
  * The Config schema: every field volatile except the composition-only `apiKey`
1970
- * secret.
1971
- *
1972
- * dsh 0.1.7's settings forms are projected from the schema's `meta.volatile`
1973
- * nodes, so an unmarked field would be invisible to AND unwritable from the
1974
- * settings page and refused by form-edit path validation — and the loader
1975
- * hands `apply()` a live reference per marked field, committing later writes
1976
- * without remounting this fiber (the `loader/volatile-update` listener at the
1977
- * bottom of `apply` covers the facts that are not re-derived per read).
1895
+ * secret. 0.1.7's settings forms are projected from the schema's
1896
+ * `meta.volatile` nodes, so an unmarked field would be invisible to AND
1897
+ * unwritable from the settings page. The loader also hands `apply()` a live
1898
+ * reference per marked field, so writes commit in place without remounting
1899
+ * this fiber — the `loader/volatile-update` listener at the bottom of `apply`
1900
+ * re-derives the two facts that are not re-read per request.
1978
1901
  */
1979
1902
  declare const Config: z<Config>;
1980
1903
  /** One resolution's complete request facts: connection plus credential reference. */
@@ -1985,7 +1908,7 @@ interface ResolvedCommandCodeOptions extends CommandCodeConnectionOptions {
1985
1908
  * The one explicit resolve step from raw config to validated connection
1986
1909
  * facts. Programmatic construction may bypass Schemastery normalization, so
1987
1910
  * every default is re-judged here — for the composition entry at load and for
1988
- * each settings snapshot at its first use.
1911
+ * every settings-backed read.
1989
1912
  */
1990
1913
  declare function resolveAdapterOptions(config: Config): ResolvedCommandCodeOptions;
1991
1914
  declare function apply(ctx: Context, config: Config): void;