dsh-connect-trae 3.7.0 → 3.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +261 -0
- package/README.en.md +17 -11
- package/README.md +26 -16
- package/lib/client.js +1992 -154
- package/lib/index.d.ts +1589 -716
- package/lib/index.js +2676 -307
- package/package.json +16 -15
package/lib/index.d.ts
CHANGED
|
@@ -61,6 +61,29 @@ declare function traeStorageCandidates(platform?: NodeJS.Platform, home?: string
|
|
|
61
61
|
//#region src/region.d.ts
|
|
62
62
|
/** Storage/routing bucket: the CN service or the international (ai) service. */
|
|
63
63
|
type TraeRegion = 'cn' | 'ai';
|
|
64
|
+
/**
|
|
65
|
+
* One section of the plugin card: a provider the user can switch, bind an
|
|
66
|
+
* account to, and pick models for, INDEPENDENTLY.
|
|
67
|
+
*
|
|
68
|
+
* A panel is not a region. `cn` and `ai` happen to be one-to-one with regions,
|
|
69
|
+
* but the CN enterprise pool shares the `cn` credential while billing a
|
|
70
|
+
* different account and serving a different roster — it needs its own switch,
|
|
71
|
+
* its own account binding and its own model selection, which is exactly what a
|
|
72
|
+
* panel is. Keeping panels a separate concept from regions is what lets
|
|
73
|
+
* `enterprise` be handled by the same settings plumbing with no new storage
|
|
74
|
+
* shape: each panel is just one more slot in the `regions` map.
|
|
75
|
+
*/
|
|
76
|
+
type TraePanel = 'cn' | 'ai' | 'enterprise';
|
|
77
|
+
/** Every panel, in card tab order. */
|
|
78
|
+
declare const TRAE_PANELS: readonly TraePanel[];
|
|
79
|
+
/** The region whose credential/account pool a panel draws on. */
|
|
80
|
+
declare function regionOfPanel(panel: TraePanel): TraeRegion;
|
|
81
|
+
/** The provider id a panel publishes. */
|
|
82
|
+
declare function providerOfPanel(panel: TraePanel): string;
|
|
83
|
+
/** The panel that owns a provider id, or undefined for an unknown id. */
|
|
84
|
+
declare function panelOfProvider(provider: string): TraePanel | undefined;
|
|
85
|
+
/** Narrow a stored settings key to a panel. */
|
|
86
|
+
declare function panelOfKey(value: unknown): TraePanel | undefined;
|
|
64
87
|
/** One region's upstream gateway bases. */
|
|
65
88
|
interface TraeRegionGateways {
|
|
66
89
|
/** Chat + agent API base (the `llm_utils_chat` / raw-chat family). */
|
|
@@ -191,8 +214,15 @@ declare function traeInputModalities(model: Pick<TraeModelInfo, 'input'>): TraeI
|
|
|
191
214
|
* (wire resolution, callable-key filtering) must keep matching the
|
|
192
215
|
* undecorated name; only the model rows handed to DSH (adapter catalog and
|
|
193
216
|
* model discovery) use this decorated name.
|
|
217
|
+
*
|
|
218
|
+
* `withRate` is false for the ENTERPRISE panel. The multiplier is a
|
|
219
|
+
* consumer-pool figure — personal credits per token, read from the consumer
|
|
220
|
+
* directory — and the tenant pool does not bill against it, so printing it in
|
|
221
|
+
* the model picker would price a model the tenant is not charged for. The rate
|
|
222
|
+
* is part of the NAME that reaches DSH, which is why this is a parameter here
|
|
223
|
+
* and not a rendering decision downstream.
|
|
194
224
|
*/
|
|
195
|
-
declare function traeModelDisplayName(model: Pick<TraeModelInfo, 'name' | 'creditMultiplier'
|
|
225
|
+
declare function traeModelDisplayName(model: Pick<TraeModelInfo, 'name' | 'creditMultiplier'>, withRate?: boolean): string;
|
|
196
226
|
/** Apply the user's explicit image opt-ins plus Trae's own multimodal flag. */
|
|
197
227
|
declare function applyImageSelection(models: readonly TraeModelInfo[], selected: ReadonlySet<string>): TraeModelInfo[];
|
|
198
228
|
/** One row from `get_detail_param`: the authoritative llm_utils_chat wire id + display name. */
|
|
@@ -296,167 +326,1036 @@ declare class TraeCatalog {
|
|
|
296
326
|
set(models: readonly TraeModelInfo[]): void;
|
|
297
327
|
}
|
|
298
328
|
//#endregion
|
|
299
|
-
//#region src/
|
|
329
|
+
//#region src/status-paths.d.ts
|
|
330
|
+
/** Plugin-owned usage endpoint consumed by its browser half. */
|
|
331
|
+
declare const TRAE_USAGE_PATH = "/plugins/dsh-connect-trae/usage";
|
|
300
332
|
/**
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
*
|
|
304
|
-
*
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
* -
|
|
309
|
-
* once, so what reaches a provider is a normalized *transcript*:
|
|
310
|
-
* `{ messages: [ { role: 'system', content, toolsAdded }, … ] }`. The system
|
|
311
|
-
* prompt and the tool declarations live ONLY on that leading system message;
|
|
312
|
-
* the top-level `systemPrompt` / `tools` fields are gone, and the api never
|
|
313
|
-
* reads them back.
|
|
314
|
-
* - **legacy** (≤0.85): expects `{ systemPrompt, tools, messages }` and has no
|
|
315
|
-
* branch for a `system` message inside `messages` at all. Its token estimator
|
|
316
|
-
* crashes on one — `estimateMessageTokens` iterates the string `content`
|
|
317
|
-
* character by character and reads `.name` off a character — and its wire
|
|
318
|
-
* converter silently drops it.
|
|
319
|
-
*
|
|
320
|
-
* The mismatch is reachable in BOTH directions, because DSH itself spans both
|
|
321
|
-
* generations (`dsh-llm-pi-ai` ≤0.2.0-rc.1 declares `^0.85.1`; 0.2.0-rc.2 and
|
|
322
|
-
* later declare `^0.87.1`, 0.2.1-alpha.2 declares `^1.0.2`, and 0.2.1-rc.1 —
|
|
323
|
-
* the current desktop host — declares **`^1.1.0`**, which also stopped reading
|
|
324
|
-
* the top-level fields entirely), while this plugin accepts the whole range
|
|
325
|
-
* (`>=0.1.7-rc.1 <0.3.0-0`). Whichever side is newer, the other one is wrong:
|
|
326
|
-
*
|
|
327
|
-
* | host | resolved api | outcome |
|
|
328
|
-
* |---|---|---|
|
|
329
|
-
* | legacy | legacy | fine |
|
|
330
|
-
* | legacy | modern | no crash, but prompt + tools silently LOST |
|
|
331
|
-
* | modern | legacy | CRASH: `Cannot read properties of undefined (reading 'length')` |
|
|
332
|
-
* | modern | modern | fine |
|
|
333
|
-
*
|
|
334
|
-
* THE BOUNDARY IS 0.86, NOT 0.87. Checked against the published tarballs:
|
|
335
|
-
* `dist/utils/transcript.js` — and with it `normalizeContext`, the
|
|
336
|
-
* `role === 'system'` estimator branch, and the switch away from reading
|
|
337
|
-
* `systemPrompt`/`tools` — first appears in **0.86.0**. A `>= 0.87` gate
|
|
338
|
-
* misclassifies 0.86.x as legacy and folds a context the modern api consumes
|
|
339
|
-
* natively, which deletes the only carrier of the prompt and the tools.
|
|
333
|
+
* Plugin-owned availability probe: POST the model ids to test, get one verdict
|
|
334
|
+
* per model. Unlike every read-only sibling this route SPENDS quota — a real
|
|
335
|
+
* (minimal) chat call per model — so it is user-initiated only, and the card
|
|
336
|
+
* labels the button as consuming a small amount.
|
|
337
|
+
*/
|
|
338
|
+
declare const TRAE_MODELS_TEST_PATH = "/plugins/dsh-connect-trae/models/test";
|
|
339
|
+
/**
|
|
340
|
+
* Per-account status (check-in / credits / subscription) for the account table.
|
|
340
341
|
*
|
|
341
|
-
*
|
|
342
|
+
* Each row of the account table reads its OWN state through this route — a CN
|
|
343
|
+
* row carries its check-in state and balance, an ai row its subscription.
|
|
344
|
+
* POST with the accountId in the body: one upstream read per account, paid
|
|
345
|
+
* only when the table is actually shown.
|
|
346
|
+
*/
|
|
347
|
+
declare const TRAE_ACCOUNT_STATUS_PATH = "/plugins/dsh-connect-trae/account-status";
|
|
348
|
+
/**
|
|
349
|
+
* Plugin-owned daily check-in claim endpoint. POST, loopback-only, and the only
|
|
350
|
+
* route in this plugin that changes upstream account state. The accountId in
|
|
351
|
+
* the body names WHICH account to claim for, so the button can sit in each
|
|
352
|
+
* account row.
|
|
353
|
+
*/
|
|
354
|
+
declare const TRAE_CHECKIN_PATH = "/plugins/dsh-connect-trae/checkin";
|
|
355
|
+
/**
|
|
356
|
+
* Outcome vocabulary of one account probe, shared by the Host and the card.
|
|
342
357
|
*
|
|
343
|
-
*
|
|
344
|
-
*
|
|
345
|
-
*
|
|
346
|
-
*
|
|
347
|
-
*
|
|
348
|
-
*
|
|
349
|
-
* published generation, 0.82 through 1.x).
|
|
358
|
+
* Mirrors `TraePoolOutcome` in `./account-pool.ts`, and is DECLARED again here
|
|
359
|
+
* rather than imported for the same reason workbuddy repeats it: this module is
|
|
360
|
+
* the node-free bridge for the browser build target and must not pull in the
|
|
361
|
+
* Host-side pool module (which imports the credential type). The two lists must
|
|
362
|
+
* stay in step — the card renders one label per value, and a value Host can send
|
|
363
|
+
* that the card has no label for renders as the raw token.
|
|
350
364
|
*
|
|
351
|
-
*
|
|
352
|
-
*
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
*
|
|
357
|
-
* fooled that way.
|
|
365
|
+
* `plan-gated` is Trae-only (upstream `1005`: this ACCOUNT's plan does not cover
|
|
366
|
+
* the model) and has no workbuddy counterpart.
|
|
367
|
+
*/
|
|
368
|
+
type TraeWebPoolOutcome = 'ok' | 'rate-limited' |
|
|
369
|
+
/**
|
|
370
|
+
* The MODEL is loaded, not the account.
|
|
358
371
|
*
|
|
359
|
-
*
|
|
360
|
-
*
|
|
361
|
-
*
|
|
372
|
+
* Kept apart from `rate-limited` because the two need OPPOSITE advice:
|
|
373
|
+
* `rate-limited` is this account's volume wall (wait, or bill another account),
|
|
374
|
+
* while `model-busy` follows the model everywhere — every account hits the same
|
|
375
|
+
* wall, so switching only spends a request per account to learn what the first
|
|
376
|
+
* already said.
|
|
377
|
+
*/
|
|
378
|
+
'model-busy' | 'out-of-credit' | 'credential-rejected' | 'policy-rejected' | 'plan-gated' | 'unavailable' | 'not-found' | 'failed';
|
|
379
|
+
/** Why one account is currently out of the pool. */
|
|
380
|
+
type TraeWebPoolExclusion = 'unusable' | 'rate-limited' | 'model-busy' | 'out-of-credit' | 'credential-rejected' | 'policy-rejected' | 'plan-gated' | 'not-found' | 'unavailable' | 'failed';
|
|
381
|
+
/**
|
|
382
|
+
* One account's row in the pool, as the card renders it.
|
|
362
383
|
*
|
|
363
|
-
*
|
|
384
|
+
* Ties together the three facts the card shows per account — credits, the last
|
|
385
|
+
* test, and whether it can serve right now — so the ranking the card displays
|
|
386
|
+
* comes from ONE Host answer rather than being re-derived in the browser. The
|
|
387
|
+
* browser re-deriving it is how a card ends up calling an excluded account "in
|
|
388
|
+
* use", which is the inconsistency this document exists to prevent.
|
|
364
389
|
*/
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
generation: PiAiGeneration;
|
|
371
|
-
/** Version of the resolved copy, when the manifest could be read. */
|
|
372
|
-
version: string | undefined;
|
|
373
|
-
/** Package directory of the resolved copy, when it could be located. */
|
|
374
|
-
resolvedFrom: string | undefined;
|
|
390
|
+
interface TraeWebPoolAccount {
|
|
391
|
+
accountId: string;
|
|
392
|
+
accountName: string;
|
|
393
|
+
/** Total remaining credit, absent when this account's balance read failed. */
|
|
394
|
+
credits?: number;
|
|
375
395
|
/**
|
|
376
|
-
*
|
|
377
|
-
*
|
|
378
|
-
*
|
|
379
|
-
*
|
|
380
|
-
*
|
|
396
|
+
* The GENERAL bucket — the credit this plugin's chat can actually spend.
|
|
397
|
+
*
|
|
398
|
+
* Trae-only, because a Trae balance is TWO buckets and SOLO chat only drains
|
|
399
|
+
* General (measured on the live `available_endpoint` split). A row whose total
|
|
400
|
+
* looks rich but whose General reads zero cannot serve a request, which is the
|
|
401
|
+
* same trap workbuddy hit with its expiry rule: a ranking keyed on a figure that
|
|
402
|
+
* the billing path cannot reach is not a ranking. The card shows both.
|
|
381
403
|
*/
|
|
382
|
-
|
|
404
|
+
generalAvailable?: number;
|
|
405
|
+
/** The WORK bucket, kept for display: this plugin's chat never spends it. */
|
|
406
|
+
workAvailable?: number;
|
|
407
|
+
/** Credit lapsing inside 3 days. Zero means "measured, nothing at risk". */
|
|
408
|
+
expiringSoon?: number;
|
|
409
|
+
/** When the nearest pack lapses, epoch ms. */
|
|
410
|
+
nearestExpiryMs?: number;
|
|
411
|
+
/**
|
|
412
|
+
* This account's stored access token has lapsed, so its balance cannot be read
|
|
413
|
+
* until it is refreshed or the user signs in again.
|
|
414
|
+
*
|
|
415
|
+
* A reason for the dash, not a second dash: without it "0 credit" and "cannot
|
|
416
|
+
* read the balance" look identical on the card, and only one of them is the
|
|
417
|
+
* user's problem to fix.
|
|
418
|
+
*/
|
|
419
|
+
tokenExpired?: boolean;
|
|
420
|
+
/**
|
|
421
|
+
* The last test of this account, absent when never tested.
|
|
422
|
+
*
|
|
423
|
+
* `modelId` lives INSIDE the measurement rather than being assumed from the
|
|
424
|
+
* row's current setting: the setting can change after the test, and a result
|
|
425
|
+
* must keep naming what it actually measured.
|
|
426
|
+
*/
|
|
427
|
+
probe?: {
|
|
428
|
+
outcome: TraeWebPoolOutcome;
|
|
429
|
+
atMs: number;
|
|
430
|
+
/**
|
|
431
|
+
* What wrote this measurement, so the card can explain a change the user
|
|
432
|
+
* watched happen: `test-batch` is a deliberate test, `live-request` is a real
|
|
433
|
+
* request that failed and overwrote it. ABSENT for records written before the
|
|
434
|
+
* field existed — the card must then say "unknown", not guess.
|
|
435
|
+
*/
|
|
436
|
+
source?: 'test-batch' | 'live-request';
|
|
437
|
+
/** WHICH model this measurement is about; absent = unknown scope. */
|
|
438
|
+
modelId?: string;
|
|
439
|
+
/** Only when the upstream named a time; never invented locally. */
|
|
440
|
+
retryAtMs?: number;
|
|
441
|
+
/** Upstream's own words, already redacted. Absent on success. */
|
|
442
|
+
message?: string;
|
|
443
|
+
};
|
|
444
|
+
/** Set when this account cannot serve right now. */
|
|
445
|
+
excludedBy?: TraeWebPoolExclusion;
|
|
446
|
+
/**
|
|
447
|
+
* Whether this is the account billing traffic for this panel RIGHT NOW.
|
|
448
|
+
*
|
|
449
|
+
* The pool's runtime pick when enabled, else the saved selection. It is the
|
|
450
|
+
* HOST's answer, not the card's guess: a card that derived "in use" from the
|
|
451
|
+
* saved setting would contradict the pool after a rotation.
|
|
452
|
+
*/
|
|
453
|
+
current: boolean;
|
|
454
|
+
/**
|
|
455
|
+
* Whether the user checked this account into this panel's pool.
|
|
456
|
+
*
|
|
457
|
+
* Listed rows include UNCHECKED accounts so the card can offer them to check:
|
|
458
|
+
* membership is an explicit opt-in and a user can only opt in to something they
|
|
459
|
+
* can see. Only checked rows can bill or be batched.
|
|
460
|
+
*/
|
|
461
|
+
member: boolean;
|
|
462
|
+
/**
|
|
463
|
+
* Whether this machine still holds a usable sign-in for the account.
|
|
464
|
+
*
|
|
465
|
+
* `false` marks a GHOST: checked or listed, but no local credential resolves, so
|
|
466
|
+
* a batch can neither test it nor check it in. It is kept as a row (rather than
|
|
467
|
+
* filtered out) because the only way to stop carrying a broken entry is to see
|
|
468
|
+
* it and remove it — a filtered row would leave the saved list permanently dirty
|
|
469
|
+
* with nothing on screen to explain why the pool runs on fewer accounts than the
|
|
470
|
+
* checkbox count says.
|
|
471
|
+
*/
|
|
472
|
+
reachable?: boolean;
|
|
473
|
+
/**
|
|
474
|
+
* Today's check-in state for this account.
|
|
475
|
+
*
|
|
476
|
+
* ABSENT means it was not read — the card must then say so rather than render a
|
|
477
|
+
* definite "not checked in", which would contradict a claim the user just
|
|
478
|
+
* watched succeed.
|
|
479
|
+
*/
|
|
480
|
+
checkedInToday?: boolean;
|
|
481
|
+
/**
|
|
482
|
+
* Whether today's check-in is spent on this MACHINE for this account.
|
|
483
|
+
*
|
|
484
|
+
* Trae-only, because Trae keys the daily claim to `x-device-id` (code `9095`).
|
|
485
|
+
* `checkedInToday === false` together with this true is the "switched account"
|
|
486
|
+
* case the check-in card already documents: this device has no claim left.
|
|
487
|
+
*/
|
|
488
|
+
deviceCheckedIn?: boolean;
|
|
383
489
|
}
|
|
384
|
-
/** Resolve (once) which pi-ai copy this plugin runs against. */
|
|
385
|
-
declare function piAiRuntimeInfo(): PiAiRuntimeInfo;
|
|
386
490
|
/**
|
|
387
|
-
* The
|
|
388
|
-
* rather than by a comment here — including the two this development tree can
|
|
389
|
-
* never reach (no local copy, and a copy with no readable version), which are
|
|
390
|
-
* exactly the states a market install founds its gate on.
|
|
491
|
+
* The pool's state for one panel.
|
|
391
492
|
*
|
|
392
|
-
*
|
|
393
|
-
*
|
|
394
|
-
*
|
|
493
|
+
* `targetModelId`/`targetModelSource` exist so the card can state WHICH model a
|
|
494
|
+
* test would use and WHERE that choice came from. `none` is a real, common
|
|
495
|
+
* answer for Trae — the CN static fallback catalog carries no `creditMultiplier`
|
|
496
|
+
* at all until its first live refresh, and "missing multiplier ≠ free" means the
|
|
497
|
+
* card must render "no free model" rather than silently substituting a paid one.
|
|
395
498
|
*/
|
|
396
|
-
|
|
499
|
+
interface TraeWebPool {
|
|
500
|
+
enabled: boolean;
|
|
501
|
+
/**
|
|
502
|
+
* Whether this panel offers the daily check-in at all.
|
|
503
|
+
*
|
|
504
|
+
* Only `cn` does. The ai gateways answer 404 for the whole `/trae/api/v2/ug/*`
|
|
505
|
+
* family (probed 2026-09-24) and the enterprise panel has no personal reward,
|
|
506
|
+
* so the card hides the action entirely rather than rendering a button whose
|
|
507
|
+
* only outcome is a refusal.
|
|
508
|
+
*/
|
|
509
|
+
checkinSupported: boolean;
|
|
510
|
+
/** The model a test would use right now; absent when none can be resolved. */
|
|
511
|
+
targetModelId?: string;
|
|
512
|
+
/**
|
|
513
|
+
* Where the target came from. `'stale'` means the SAVED id is no longer in the
|
|
514
|
+
* catalog — a distinct state from `'none'` (no free model exists), because the
|
|
515
|
+
* two need opposite advice (offer a way back to automatic / say there is no
|
|
516
|
+
* free model at all).
|
|
517
|
+
*/
|
|
518
|
+
targetModelSource: 'preferred' | 'free' | 'none' | 'stale';
|
|
519
|
+
/**
|
|
520
|
+
* How much text a pool test sends (0 = the minimal probe). Always a number so
|
|
521
|
+
* the card's select has a value to show; the saved preference is the default.
|
|
522
|
+
*/
|
|
523
|
+
probeInputTokens: number;
|
|
524
|
+
/** The saved target id that this panel's catalog no longer offers. */
|
|
525
|
+
staleTargetModelId?: string;
|
|
526
|
+
/** The account ids the user has checked into this panel's pool. */
|
|
527
|
+
memberAccountIds?: readonly string[];
|
|
528
|
+
/**
|
|
529
|
+
* The subset of {@link TraeWebPool.memberAccountIds} that resolves to a real
|
|
530
|
+
* local credential — i.e. what a batch actually runs on.
|
|
531
|
+
*
|
|
532
|
+
* Sent so the card never re-derives it. Counting the raw saved list is what
|
|
533
|
+
* lets a UI claim "1 of 1 selected" and enable both buttons while the Host ran
|
|
534
|
+
* on ZERO accounts and reported success.
|
|
535
|
+
*/
|
|
536
|
+
effectiveMemberAccountIds?: readonly string[];
|
|
537
|
+
/**
|
|
538
|
+
* Last measured display name per saved member id, including members that have
|
|
539
|
+
* since stopped resolving.
|
|
540
|
+
*
|
|
541
|
+
* A member whose sign-in disappeared would otherwise be listed as a bare id
|
|
542
|
+
* and an unexplained count, and the user could not tell WHICH one they unchecked.
|
|
543
|
+
*/
|
|
544
|
+
memberNames?: Record<string, string>;
|
|
545
|
+
/** Rows in the Host's ranking order: candidates first, excluded rows last. */
|
|
546
|
+
accounts: readonly TraeWebPoolAccount[];
|
|
547
|
+
/**
|
|
548
|
+
* Why the pool could not pick anybody, when it could not.
|
|
549
|
+
*
|
|
550
|
+
* Set when every member is excluded, so the card can explain that traffic is
|
|
551
|
+
* going to the saved selection rather than leaving the user to assume the pool
|
|
552
|
+
* is serving the top row.
|
|
553
|
+
*/
|
|
554
|
+
noCandidate?: TraeWebPoolExclusion | 'empty';
|
|
555
|
+
}
|
|
397
556
|
/**
|
|
398
|
-
* The
|
|
399
|
-
* cannot be read as a version at all.
|
|
557
|
+
* The pool's saved preferences for one panel, as stored in `regions[panel].pool`.
|
|
400
558
|
*
|
|
401
|
-
*
|
|
402
|
-
*
|
|
403
|
-
*
|
|
559
|
+
* Declared in the node-free bridge because BOTH halves touch it: the Host reads
|
|
560
|
+
* it to decide membership and ranking, the card drafts and saves it. Keeping one
|
|
561
|
+
* declaration is what stops the card from writing a field the Host ignores (or
|
|
562
|
+
* vice versa) — the failure workbuddy hit when its client and Host each had a
|
|
563
|
+
* private copy of this shape.
|
|
564
|
+
*
|
|
565
|
+
* Every field is optional because this is a user-edited draft: a partially filled
|
|
566
|
+
* slot must still load, and a missing `enabled` means OFF rather than ON.
|
|
404
567
|
*/
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
568
|
+
interface TraePoolPreferences {
|
|
569
|
+
/** Whether this panel bills through the pool at all. Default OFF. */
|
|
570
|
+
enabled?: boolean;
|
|
571
|
+
/**
|
|
572
|
+
* The model a test batch probes. Absent or `''` means "automatic": pick the
|
|
573
|
+
* free model (see `pickFreeModel` in `./account-pool.ts`). An id this panel no
|
|
574
|
+
* longer offers is PRESERVED so the user's choice survives a catalog blip, and
|
|
575
|
+
* reported as `stale` rather than silently replaced.
|
|
576
|
+
*/
|
|
577
|
+
targetModelId?: string;
|
|
578
|
+
/**
|
|
579
|
+
* The accounts the user checked into this panel's pool, in the order they were
|
|
580
|
+
* checked.
|
|
581
|
+
*
|
|
582
|
+
* Explicit opt-in with NO default: an account never joins the pool because it
|
|
583
|
+
* exists. Order is only a display hint — the ranking is the Host's.
|
|
584
|
+
*
|
|
585
|
+
* A MUTABLE array, not `readonly`: this interface is also the shape the
|
|
586
|
+
* settings schema declares (`z.array(z.string())`), and the schema rebuilds the
|
|
587
|
+
* config document on every save — a readonly type there is a compile-time
|
|
588
|
+
* mismatch that would take the whole Config schema down. Readers still treat it
|
|
589
|
+
* as immutable; only the owner of a draft copies it.
|
|
590
|
+
*/
|
|
591
|
+
memberAccountIds?: string[];
|
|
592
|
+
/**
|
|
593
|
+
* How much text a pool TEST sends, in tokens. `0` (the default) keeps the
|
|
594
|
+
* minimal one-word probe.
|
|
595
|
+
*
|
|
596
|
+
* Exists because a verdict earned by a one-word request says nothing about the
|
|
597
|
+
* long turn the user is about to send: an upstream that refuses large requests
|
|
598
|
+
* answers the small one happily. Padding the probe therefore buys a truthful
|
|
599
|
+
* "works at my size" answer at the cost of spending real credit per test.
|
|
600
|
+
*/
|
|
601
|
+
probeInputTokens?: number;
|
|
420
602
|
}
|
|
603
|
+
/** Query parameter naming the region a card request addresses. */
|
|
604
|
+
declare const TRAE_REGION_PARAM = "region";
|
|
605
|
+
/** Every region, in card tab order. */
|
|
606
|
+
declare const TRAE_REGIONS: readonly TraeRegion[];
|
|
421
607
|
/**
|
|
422
|
-
*
|
|
423
|
-
*
|
|
608
|
+
* Address one region's status route. The two regions are separate provider
|
|
609
|
+
* stacks; every card request carries the region whose tab the user is on.
|
|
424
610
|
*/
|
|
425
|
-
declare
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
611
|
+
declare function withTraeRegion(path: string, panel: TraePanel): string;
|
|
612
|
+
/**
|
|
613
|
+
* Read the region parameter off a status-route URL. Absent means the domestic
|
|
614
|
+
* tab (`cn`); a present-but-unknown value returns undefined so the route can
|
|
615
|
+
* answer 400 instead of guessing.
|
|
616
|
+
*/
|
|
617
|
+
declare function regionOfTraeStatusUrl(url: string): TraePanel | undefined;
|
|
618
|
+
/** One credit pack and its remaining credit. */
|
|
619
|
+
interface TraeWebCreditAccount {
|
|
620
|
+
displayDesc: string;
|
|
621
|
+
remain: number;
|
|
622
|
+
size: number;
|
|
623
|
+
/**
|
|
624
|
+
* When this pack lapses, in epoch milliseconds, taken from the upstream's own
|
|
625
|
+
* `end_time`. Absent when the upstream reported no usable time (0 / missing),
|
|
626
|
+
* so a consumer can tell "no date" apart from "1970".
|
|
627
|
+
*/
|
|
628
|
+
expiresAtMs?: number;
|
|
629
|
+
/**
|
|
630
|
+
* Raw upstream `available_endpoint`: which bucket this pack lives in.
|
|
631
|
+
*
|
|
632
|
+
* Measured on the live response — `0` is the GENERAL bucket that SOLO chat
|
|
633
|
+
* actually spends, `1` is the WORK bucket it cannot reach. The projection
|
|
634
|
+
* dropped this field until the account pool needed it: the pool ranks accounts
|
|
635
|
+
* by what they can SERVE, and credit lapsing in the WORK bucket is not a
|
|
636
|
+
* reason to bill that account first, because nothing here can ever spend it.
|
|
637
|
+
* Absent, not guessed, when the upstream omits it.
|
|
638
|
+
*/
|
|
639
|
+
endpoint?: number;
|
|
439
640
|
}
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
641
|
+
/** Credit lapsing inside one horizon, as the card reports it. */
|
|
642
|
+
interface TraeWebExpiringCredits {
|
|
643
|
+
/** Sum of the contributing packs' remaining credit. */
|
|
644
|
+
total: number;
|
|
645
|
+
/** How many packs contribute. */
|
|
646
|
+
packs: number;
|
|
647
|
+
/** Soonest expiry among them, epoch milliseconds. */
|
|
648
|
+
earliestExpiresAtMs?: number;
|
|
444
649
|
}
|
|
445
|
-
declare function createTraeShim(options: TraeShimOptions): TraeShim;
|
|
446
|
-
//#endregion
|
|
447
|
-
//#region src/adapter.d.ts
|
|
448
|
-
/** Provider route this bundle owns for the domestic (CN) gateway. */
|
|
449
|
-
declare const TRAE_PROVIDER = "trae";
|
|
450
650
|
/**
|
|
451
|
-
*
|
|
452
|
-
*
|
|
453
|
-
*
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
651
|
+
* Credit at risk, bucketed by how soon it lapses.
|
|
652
|
+
*
|
|
653
|
+
* Only present when at least one pack carried a usable `expiresAtMs` — a card
|
|
654
|
+
* must never read "we were not told when anything expires" as "nothing is
|
|
655
|
+
* about to lapse".
|
|
656
|
+
*/
|
|
657
|
+
interface TraeWebCreditExpiry {
|
|
658
|
+
within24h: TraeWebExpiringCredits;
|
|
659
|
+
within3d: TraeWebExpiringCredits;
|
|
660
|
+
within7d: TraeWebExpiringCredits;
|
|
661
|
+
/** Packs already past their end time but still carrying credit. */
|
|
662
|
+
expired: TraeWebExpiringCredits;
|
|
663
|
+
}
|
|
664
|
+
/** Aggregated usage/credit answer rendered by the plugin card. */
|
|
665
|
+
interface TraeWebCredits {
|
|
666
|
+
total: number;
|
|
667
|
+
consumed: number;
|
|
668
|
+
available: number;
|
|
669
|
+
workAvailable: number;
|
|
670
|
+
generalAvailable: number;
|
|
671
|
+
accounts: readonly TraeWebCreditAccount[];
|
|
672
|
+
/**
|
|
673
|
+
* Credit lapsing soon, for the card's warning banner.
|
|
674
|
+
*
|
|
675
|
+
* Derived from the per-pack {@link TraeWebCreditAccount.expiresAtMs} the
|
|
676
|
+
* `/usage` route already carries, so it costs no extra upstream read.
|
|
677
|
+
*/
|
|
678
|
+
expiry?: TraeWebCreditExpiry;
|
|
679
|
+
}
|
|
680
|
+
/** Editable Trae model row rendered by the plugin-owned settings card. */
|
|
681
|
+
interface TraeWebModel {
|
|
682
|
+
id: string;
|
|
683
|
+
name: string;
|
|
684
|
+
contextWindow?: number;
|
|
685
|
+
maxTokens?: number;
|
|
686
|
+
input?: ('text' | 'image')[];
|
|
687
|
+
creditMultiplier?: number;
|
|
688
|
+
reasoningSupported?: boolean;
|
|
689
|
+
reasoning?: {
|
|
690
|
+
supported: string[];
|
|
691
|
+
defaultEffort?: string;
|
|
692
|
+
};
|
|
693
|
+
maxContextWindow?: number;
|
|
694
|
+
/**
|
|
695
|
+
* Trae's own image-capability flag, as the runtime catalog carries it. The
|
|
696
|
+
* card renders these rows with their image checkbox already ticked so the
|
|
697
|
+
* control matches what DSH is actually told (issue #16); absent means the
|
|
698
|
+
* directory did not declare it, which is not a capability claim either way.
|
|
699
|
+
*/
|
|
700
|
+
multimodal?: boolean;
|
|
701
|
+
/**
|
|
702
|
+
* The availability verdict from the one-click test, when this card session
|
|
703
|
+
* has run one. Absent = not tested. Deliberately NOT persisted with the
|
|
704
|
+
* settings: it describes one moment on one account, and a stale verdict shown
|
|
705
|
+
* as current is worse than a blank field.
|
|
706
|
+
*/
|
|
707
|
+
probe?: TraeModelProbeVerdict;
|
|
708
|
+
/** Upstream's own words for the verdict, when it gave any. */
|
|
709
|
+
probeDetail?: string;
|
|
710
|
+
}
|
|
711
|
+
/**
|
|
712
|
+
* One model's availability verdict from the one-click test.
|
|
713
|
+
*
|
|
714
|
+
* `unknown` is a first-class outcome, not a failure: a timeout, a proxy drop or
|
|
715
|
+
* a throttle says nothing about the model, and reporting it as unusable would
|
|
716
|
+
* make the user delete a model that works. Only an upstream refusal is negative.
|
|
717
|
+
*/
|
|
718
|
+
type TraeModelProbeVerdict = 'available' | 'gated' | 'unsupported' | 'rate_limited' | 'auth' | 'unknown';
|
|
719
|
+
/** What the probe route answers with. */
|
|
720
|
+
interface TraeModelsTestResponse {
|
|
721
|
+
/** One entry per model tested, in the order they were sent. */
|
|
722
|
+
results: {
|
|
723
|
+
id: string;
|
|
724
|
+
verdict: TraeModelProbeVerdict;
|
|
725
|
+
detail: string;
|
|
726
|
+
}[];
|
|
727
|
+
/** Models skipped because the region has no live catalog row for them. */
|
|
728
|
+
skipped?: string[];
|
|
729
|
+
}
|
|
730
|
+
/**
|
|
731
|
+
* Daily check-in state rendered below the credit stats.
|
|
732
|
+
*
|
|
733
|
+
* The two booleans answer DIFFERENT questions and must never be collapsed into
|
|
734
|
+
* one "done" flag — doing that was a shipped bug (see CHANGELOG 2.3.1):
|
|
735
|
+
*
|
|
736
|
+
* - `checkedIn` is ACCOUNT-level: today's reward pack exists for this account,
|
|
737
|
+
* so there is nothing left to claim and the balance already includes it.
|
|
738
|
+
* - `didCheckedIn` is DEVICE-level: this MACHINE already spent its one
|
|
739
|
+
* check-in for the day, whichever account spent it. The upstream keys it on
|
|
740
|
+
* the `x-device-id` in the request and answers it per device, not per
|
|
741
|
+
* account (measured 2026-09-26: the same account and Beijing day answered
|
|
742
|
+
* `false` with the header omitted, with a synthetic id, and with another
|
|
743
|
+
* install's id, and `true` for the device that actually claimed).
|
|
744
|
+
*
|
|
745
|
+
* So `didCheckedIn && !checkedIn` is the "switched account" case: the device's
|
|
746
|
+
* daily check-in is used up, but THIS account was never rewarded. Claiming
|
|
747
|
+
* again is refused upstream (business code 9095, "该设备今日已参与签到"), so
|
|
748
|
+
* the card disables the button — but it must say that, not "claimed today".
|
|
749
|
+
*/
|
|
750
|
+
interface TraeWebCheckin {
|
|
751
|
+
checkedIn: boolean;
|
|
752
|
+
didCheckedIn: boolean;
|
|
753
|
+
credits: number;
|
|
754
|
+
enabled: boolean;
|
|
755
|
+
extraCredits?: number;
|
|
756
|
+
}
|
|
757
|
+
/** The claim route's answer, so the card can report a business refusal verbatim. */
|
|
758
|
+
interface TraeWebCheckinClaim {
|
|
759
|
+
/** False when the upstream answered a non-zero business code (e.g. 9004). */
|
|
760
|
+
claimed: boolean;
|
|
761
|
+
/**
|
|
762
|
+
* True only when THIS ACCOUNT's reward for today already exists, so nothing
|
|
763
|
+
* was sent upstream. Distinct from {@link TraeWebCheckinClaim.deviceCheckedIn}:
|
|
764
|
+
* a device that already claimed for another account leaves this false.
|
|
765
|
+
*/
|
|
766
|
+
alreadyCheckedIn: boolean;
|
|
767
|
+
/**
|
|
768
|
+
* True when today's check-in was already spent on this DEVICE (by this or
|
|
769
|
+
* another account), so no claim was sent or the upstream refused it with
|
|
770
|
+
* 9095. Not an error: it is the truthful answer to "can this account claim
|
|
771
|
+
* here today?" — no, and the reason is the machine, not the account.
|
|
772
|
+
*/
|
|
773
|
+
deviceCheckedIn?: boolean;
|
|
774
|
+
code?: number;
|
|
775
|
+
message?: string;
|
|
776
|
+
/** The refreshed check-in state, present whenever the claim path ran. */
|
|
777
|
+
checkin?: TraeWebCheckin;
|
|
778
|
+
}
|
|
779
|
+
/** One reward activity rule (name + work/general credits when present). */
|
|
780
|
+
interface TraeWebActivity {
|
|
781
|
+
activityId: string;
|
|
782
|
+
enabled: boolean;
|
|
783
|
+
workCredits?: number;
|
|
784
|
+
generalCredits?: number;
|
|
785
|
+
}
|
|
786
|
+
interface TraeWebAccount {
|
|
787
|
+
id: string;
|
|
788
|
+
accountName: string;
|
|
789
|
+
edition: 'cn' | 'sg' | 'solo' | 'solo-sg';
|
|
790
|
+
/**
|
|
791
|
+
* Whether this account belongs to a Trae tenant, i.e. may bill the enterprise
|
|
792
|
+
* pool. The enterprise tab lists only these, because the tenant gateway
|
|
793
|
+
* serves a directory to any CN token but refuses a personal account's chat
|
|
794
|
+
* with `4019` — so nothing else reveals the difference until a turn fails.
|
|
795
|
+
*/
|
|
796
|
+
enterprise: boolean;
|
|
797
|
+
/** Routing bucket of this account, derived from its credential. */
|
|
798
|
+
region: TraeRegion;
|
|
799
|
+
source: 'desktop' | 'dsh' | 'cli';
|
|
800
|
+
tokenExpiresAtMs: number;
|
|
801
|
+
/**
|
|
802
|
+
* When this account's REFRESH token expires, when the credential carries one.
|
|
803
|
+
*
|
|
804
|
+
* The access token above is renewed automatically — the plugin refreshes it
|
|
805
|
+
* five minutes before it lapses — so by itself it answers "when does this
|
|
806
|
+
* need attention", which is "never, while the refresh token lives". The
|
|
807
|
+
* refresh token is the real horizon: once IT expires the automatic renewal
|
|
808
|
+
* stops working and the user must sign in to Trae again. Before this field the
|
|
809
|
+
* card could only say "auto-renewal", which stays true right up to the moment
|
|
810
|
+
* it silently stops being true.
|
|
811
|
+
*/
|
|
812
|
+
refreshExpiresAtMs?: number;
|
|
813
|
+
selected: boolean;
|
|
814
|
+
}
|
|
815
|
+
/**
|
|
816
|
+
* The account the Trae desktop app is signed in to right now, when it differs
|
|
817
|
+
* from the one this plugin is bound to (issue #29).
|
|
818
|
+
*
|
|
819
|
+
* Reported on the usage document so the card can offer one-click follow without
|
|
820
|
+
* an extra round trip; also written back on a POST to the accounts refresh
|
|
821
|
+
* route when the client confirms it saw the hint. Absent whenever the two
|
|
822
|
+
* agree or the app is not readable — a healthy document carries no extra
|
|
823
|
+
* fields and the plugin never bills from this value directly: the FOLLOW is a
|
|
824
|
+
* settings write of `accounts.<region>`, made by the card or by the host's
|
|
825
|
+
* follow switch, never a silent rebind here.
|
|
826
|
+
*/
|
|
827
|
+
interface TraeWebAppLogin {
|
|
828
|
+
id: string;
|
|
829
|
+
accountName: string;
|
|
830
|
+
edition: 'cn' | 'sg' | 'solo' | 'solo-sg';
|
|
831
|
+
region: TraeRegion;
|
|
832
|
+
}
|
|
833
|
+
/**
|
|
834
|
+
* The per-account state shown inside an account row of the card's table.
|
|
835
|
+
*
|
|
836
|
+
* Every locally detected account in a region is rendered as a row; the
|
|
837
|
+
* interesting per-account facts differ by region, which is why this is a union
|
|
838
|
+
* of the three surfaces the card previously showed only for the BOUND account:
|
|
839
|
+
*
|
|
840
|
+
* - CN rows carry their own check-in state (Trae keys the daily reward per
|
|
841
|
+
* ACCOUNT, and the one-per-device tally per DEVICE, see {@link TraeWebCheckin})
|
|
842
|
+
* and their Work/General balances.
|
|
843
|
+
* - ai rows carry their own subscription / trial status.
|
|
844
|
+
*
|
|
845
|
+
* Each field is optional: a read that fails keeps the row visible (perhaps
|
|
846
|
+
* without a figure) rather than dropping the account and its switch.
|
|
847
|
+
*/
|
|
848
|
+
interface TraeWebAccountState {
|
|
849
|
+
/** The account this state belongs to. */
|
|
850
|
+
id: string;
|
|
851
|
+
/** CN: today's check-in state of this account (absent for ai rows). */
|
|
852
|
+
checkin?: TraeWebCheckin;
|
|
853
|
+
/** CN: this account's Work/General balances (absent for ai rows). */
|
|
854
|
+
credits?: TraeWebCredits;
|
|
855
|
+
/** ai: this account's subscription / trial status (absent for CN rows). */
|
|
856
|
+
payStatus?: TraeWebPayStatus;
|
|
857
|
+
}
|
|
858
|
+
interface TraeWebCreditAlternative {
|
|
859
|
+
id: string;
|
|
860
|
+
accountName: string;
|
|
861
|
+
edition: TraeWebAccount['edition'];
|
|
862
|
+
workAvailable: number;
|
|
863
|
+
generalAvailable: number;
|
|
864
|
+
}
|
|
865
|
+
/** One probed candidate path and why it did not yield an account. */
|
|
866
|
+
interface TraeWebSearchPath {
|
|
867
|
+
path: string;
|
|
868
|
+
edition: 'cn' | 'sg' | 'solo' | 'solo-sg';
|
|
869
|
+
source: 'desktop' | 'cli';
|
|
870
|
+
reason: 'missing' | 'unreadable' | 'invalid';
|
|
871
|
+
message?: string;
|
|
872
|
+
}
|
|
873
|
+
/**
|
|
874
|
+
* Peel ONE `{get(): T}` live reference, the shape DSH 0.1.7 delivers a
|
|
875
|
+
* volatile-marked settings field as (see {@link unwrapVolatileDeep}).
|
|
876
|
+
*/
|
|
877
|
+
declare function unwrapVolatile<T>(value: T): T;
|
|
878
|
+
/**
|
|
879
|
+
* Deep copy of a value with every `{get(): T}` live reference replaced by the
|
|
880
|
+
* value it resolves to.
|
|
881
|
+
*
|
|
882
|
+
* DSH 0.1.7 hands volatile-marked fields back as live references, and a live
|
|
883
|
+
* reference is still `typeof === 'object'` — so it passes a naive object check
|
|
884
|
+
* and every lookup on it is `undefined`. Worse, spreading one (`{ ...ref }`)
|
|
885
|
+
* does NOT read the field: it produces `{ get: <function> }`. Any merge that
|
|
886
|
+
* spreads a resolved field to preserve its siblings — the card's "write one
|
|
887
|
+
* region, keep the other" write, or the account map's spread — would otherwise
|
|
888
|
+
* DROP every sibling and leak a function into the document.
|
|
889
|
+
*
|
|
890
|
+
* Both halves therefore unwrap before touching a resolved field. This lives in
|
|
891
|
+
* the node-free host↔client bridge because the browser half needs it too.
|
|
892
|
+
*
|
|
893
|
+
* Non-reference values are recursed into so a nested volatile field is caught
|
|
894
|
+
* as well; arrays and objects are rebuilt rather than mutated, so the caller's
|
|
895
|
+
* value is never touched.
|
|
896
|
+
*/
|
|
897
|
+
declare function unwrapVolatileDeep<T>(value: T): T;
|
|
898
|
+
/**
|
|
899
|
+
* Read the per-region account selections out of a settings value.
|
|
900
|
+
*
|
|
901
|
+
* SHARED because `accounts` now has TWO writers: the card, and the composer
|
|
902
|
+
* panel's account table (2.14.0). Two writers of one field must use one merge
|
|
903
|
+
* rule, or they drift — and the rule is not obvious.
|
|
904
|
+
*
|
|
905
|
+
* Deep-unwraps first: on DSH 0.1.7 a volatile field arrives as a `{get(): T}`
|
|
906
|
+
* live reference, which passes the `typeof === 'object'` check and would be
|
|
907
|
+
* returned as if it were the map. Every lookup on it is then `undefined`, and
|
|
908
|
+
* the merge (`{ ...configured, [region]: id }`) would spread a reference into
|
|
909
|
+
* `{get: <function>}` — dropping the other region's selection and leaking a
|
|
910
|
+
* function into the settings document.
|
|
911
|
+
*/
|
|
912
|
+
declare function configuredAccountsOf(configured: unknown): Record<string, string>;
|
|
913
|
+
/**
|
|
914
|
+
* Whether the host-side "follow the Trae app's sign-in" switch is on (issue
|
|
915
|
+
* #29). Absent reads as OFF, so an existing configuration keeps the strict
|
|
916
|
+
* bind it shipped with and the switch is an opt-in — the same opt-out
|
|
917
|
+
* semantics as {@link regionEnabledOf}, for the same reason: a config written
|
|
918
|
+
* before this field existed cannot ask for behavior it never declared.
|
|
919
|
+
*
|
|
920
|
+
* Reads the whole settings section (or the bare field); deep-unwraps first
|
|
921
|
+
* because on DSH 0.1.7 a volatile field arrives as a `{get(): T}` live
|
|
922
|
+
* reference, whose `typeof === 'object'` would otherwise pass a naive check.
|
|
923
|
+
*/
|
|
924
|
+
declare function followAppLoginEnabled(configured: unknown): boolean;
|
|
925
|
+
/**
|
|
926
|
+
* Build the next `regions` settings value for the card's save. The write
|
|
927
|
+
* targets ONLY the signed-in account's region slot; every other region's slot
|
|
928
|
+
* is carried over untouched, so switching accounts never clobbers the other
|
|
929
|
+
* region's picks. Tolerates any stored shape (absent, non-object) by starting
|
|
930
|
+
* from an empty document.
|
|
931
|
+
*/
|
|
932
|
+
declare function nextRegionSlots<Slot extends object>(regions: unknown, panel: TraePanel, slot: Slot): Record<string, unknown>;
|
|
933
|
+
/**
|
|
934
|
+
* Whether one region's provider is switched on. Opt-out semantics: only an
|
|
935
|
+
* explicit `false` disables it, so a config written before this switch existed
|
|
936
|
+
* (and the pre-region-split flat fields, which never carry `enabled`) keep both
|
|
937
|
+
* providers running exactly as before. The Host reads the same rule through
|
|
938
|
+
* `regionStateOf`, so card and Host can never disagree about a region's state.
|
|
939
|
+
*
|
|
940
|
+
* `value` may be the whole settings section or the `regions` map (see
|
|
941
|
+
* {@link regionsMapOf}).
|
|
942
|
+
*/
|
|
943
|
+
declare function regionEnabledOf(value: unknown, panel: TraePanel): boolean;
|
|
944
|
+
/**
|
|
945
|
+
* Build the next `regions` settings value for a provider on/off toggle. ONLY
|
|
946
|
+
* the target region's `enabled` flag changes: every other field of that slot
|
|
947
|
+
* (its directory, selection, image opt-ins, context budgets) and every other
|
|
948
|
+
* region's slot are carried over verbatim, so switching a provider off never
|
|
949
|
+
* discards the user's model picks and switching it back on restores them.
|
|
950
|
+
*
|
|
951
|
+
* This is deliberately separate from {@link nextRegionSlots}: that helper
|
|
952
|
+
* writes a whole slot from a signed-in tab's draft, while this one must work
|
|
953
|
+
* for a region that is signed OUT — which is precisely the region a user wants
|
|
954
|
+
* to switch off (no international install, no international account).
|
|
955
|
+
*
|
|
956
|
+
* `value` may be the whole settings section or the `regions` map; the RETURN
|
|
957
|
+
* value is always the `regions` map, i.e. exactly what `settingsScope.set(
|
|
958
|
+
* 'regions', ...)` needs.
|
|
959
|
+
*/
|
|
960
|
+
declare function nextRegionEnabled(value: unknown, panel: TraePanel, enabled: boolean): Record<string, unknown>;
|
|
961
|
+
/**
|
|
962
|
+
* The two seats this region's credit readout can occupy.
|
|
963
|
+
*
|
|
964
|
+
* `composer` is the composer tool row (`conversation.input.left`); `stats` is
|
|
965
|
+
* the ambient dock below the composer card (`conversation.composer.dock`),
|
|
966
|
+
* beside the shell's own round/step/token pills.
|
|
967
|
+
*
|
|
968
|
+
* WHY THE SEAT IS A CHOICE AT ALL (learned from dsh-connect-workbuddy issue
|
|
969
|
+
* #62): the composer tool row is a LIST seat, so it is shared not only with the
|
|
970
|
+
* shell's own controls but with any other plugin that registers there — and a
|
|
971
|
+
* list seat cannot promise that every occupant is visible. The report behind
|
|
972
|
+
* that issue is exactly that: with a third-party plugin installed the readout
|
|
973
|
+
* stopped appearing. A fixed seat therefore lets one plugin's presence decide
|
|
974
|
+
* another's, so the seat belongs to the user.
|
|
975
|
+
*
|
|
976
|
+
* Named as a union rather than a boolean because a third seat is plausible and
|
|
977
|
+
* a `showAtTop: boolean` would have to be MIGRATED rather than extended.
|
|
978
|
+
*/
|
|
979
|
+
type TraeCreditPosition = 'composer' | 'stats';
|
|
980
|
+
/**
|
|
981
|
+
* Which seat one region's readout uses, defaulting to the composer row.
|
|
982
|
+
*
|
|
983
|
+
* Anything other than an explicit `'stats'` reads as `'composer'`, which covers
|
|
984
|
+
* the two cases that matter: a profile written BEFORE this setting existed (the
|
|
985
|
+
* composer row was the only seat, so an upgrade moves nothing on screen), and a
|
|
986
|
+
* hand-edited value the schema does not know. Falling back rather than
|
|
987
|
+
* rendering nowhere is deliberate — an unrecognized value must not make the
|
|
988
|
+
* readout disappear, which is the very defect this setting exists to fix.
|
|
989
|
+
*
|
|
990
|
+
* `value` may be the whole settings section or the `regions` map (see
|
|
991
|
+
* {@link regionsMapOf}).
|
|
992
|
+
*/
|
|
993
|
+
declare function regionCreditPositionOf(value: unknown, panel: TraePanel): TraeCreditPosition;
|
|
994
|
+
/**
|
|
995
|
+
* Build the next `regions` settings value for one region's credit seat.
|
|
996
|
+
*
|
|
997
|
+
* Same merge rule as every sibling region write: only the target region's
|
|
998
|
+
* `creditPosition` changes, so a seat change cannot drop that slot's model
|
|
999
|
+
* directory, selection, budgets or enabled flag, nor touch the other region.
|
|
1000
|
+
*
|
|
1001
|
+
* `value` may be the whole settings section or the `regions` map; the RETURN
|
|
1002
|
+
* value is always the `regions` map, i.e. exactly what `settingsScope.set(
|
|
1003
|
+
* 'regions', ...)` needs.
|
|
1004
|
+
*/
|
|
1005
|
+
declare function nextRegionCreditPosition(value: unknown, panel: TraePanel, position: TraeCreditPosition): Record<string, unknown>;
|
|
1006
|
+
/** Subscription status of an international (ai) account, rendered instead of the CN credit packs. */
|
|
1007
|
+
interface TraeWebPayStatus {
|
|
1008
|
+
isDollarUsageBilling: boolean;
|
|
1009
|
+
hasPackage: boolean;
|
|
1010
|
+
isPayFreshman: boolean;
|
|
1011
|
+
inTrial: boolean;
|
|
1012
|
+
trialEndTimeMs: number;
|
|
1013
|
+
enableSoloLite: boolean;
|
|
1014
|
+
enableSoloBuilder: boolean;
|
|
1015
|
+
enableSoloCoder: boolean;
|
|
1016
|
+
enableSoloWeb: boolean;
|
|
1017
|
+
/** Trae's authoritative tier number (`user_pay_identity`); `0` means Free. */
|
|
1018
|
+
payIdentity?: number;
|
|
1019
|
+
/** Trae's authoritative tier label (`user_pay_identity_str`), e.g. `Free`. */
|
|
1020
|
+
payIdentityStr?: string;
|
|
1021
|
+
fission?: {
|
|
1022
|
+
startTimeMs: number;
|
|
1023
|
+
expireTimeMs: number;
|
|
1024
|
+
maxUsage: number;
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
/** The JSON document the plugin card renders. */
|
|
1028
|
+
type TraeWebUsage = {
|
|
1029
|
+
status: 'signed-out';
|
|
1030
|
+
accounts: readonly TraeWebAccount[];
|
|
1031
|
+
message?: string;
|
|
1032
|
+
searched?: readonly TraeWebSearchPath[];
|
|
1033
|
+
enabled?: boolean;
|
|
1034
|
+
} | {
|
|
1035
|
+
status: 'signed-in';
|
|
1036
|
+
accountId: string;
|
|
1037
|
+
accountName: string;
|
|
1038
|
+
tokenExpiresAtMs: number;
|
|
1039
|
+
/**
|
|
1040
|
+
* When the BOUND account's refresh token expires (absent when the credential
|
|
1041
|
+
* carries none). See {@link TraeWebAccount.refreshExpiresAtMs} — this is the
|
|
1042
|
+
* horizon that actually matters, because the access token above renews
|
|
1043
|
+
* itself until this one lapses.
|
|
1044
|
+
*/
|
|
1045
|
+
refreshExpiresAtMs?: number;
|
|
1046
|
+
/** Which per-region model directory and selection this account owns. */
|
|
1047
|
+
region: TraeRegion;
|
|
1048
|
+
/** Whether this region's provider is currently offered to DSH. */
|
|
1049
|
+
enabled?: boolean;
|
|
1050
|
+
accounts: readonly TraeWebAccount[];
|
|
1051
|
+
models: readonly TraeWebModel[];
|
|
1052
|
+
enabledModelIds: readonly string[];
|
|
1053
|
+
rawChat?: {
|
|
1054
|
+
state: 'disabled' | 'unchecked' | 'available' | 'protocol-gated' | 'authentication' | 'credit' | 'rate' | 'transport' | 'server';
|
|
1055
|
+
checkedAtMs?: number;
|
|
1056
|
+
status?: number;
|
|
1057
|
+
};
|
|
1058
|
+
credits?: TraeWebCredits;
|
|
1059
|
+
creditsError?: string;
|
|
1060
|
+
/**
|
|
1061
|
+
* Set when this panel has no readable balance by design, rather than by
|
|
1062
|
+
* failure. Only `enterprise` does: a tenant's spend is not reported by any
|
|
1063
|
+
* endpoint the plugin can reach (every tenant quota path answers 404), so
|
|
1064
|
+
* the figure exists solely in the enterprise console. The card must say so
|
|
1065
|
+
* instead of rendering the consumer packs, which belong to the personal
|
|
1066
|
+
* balance and would be read as the tenant's.
|
|
1067
|
+
*/
|
|
1068
|
+
creditsUnavailable?: 'enterprise';
|
|
1069
|
+
/**
|
|
1070
|
+
* Present only when the bound account is out of credit AND another account
|
|
1071
|
+
* on this machine has some (issue #25). The card then offers a one-click
|
|
1072
|
+
* switch instead of leaving a permanently empty panel. Absent in every
|
|
1073
|
+
* other case so a healthy account never pays for the extra read.
|
|
1074
|
+
*/
|
|
1075
|
+
alternatives?: readonly TraeWebCreditAlternative[];
|
|
1076
|
+
/**
|
|
1077
|
+
* Present only when the Trae app's CURRENT sign-in differs from the
|
|
1078
|
+
* account this plugin is bound to (issue #29): the card then offers a
|
|
1079
|
+
* one-click follow instead of making the user hunt the account dropdown
|
|
1080
|
+
* after every app-side sign-out. Absent in every other case — a healthy
|
|
1081
|
+
* document costs no extra read beyond the accounts scan the document
|
|
1082
|
+
* already runs.
|
|
1083
|
+
*/
|
|
1084
|
+
appLogin?: TraeWebAppLogin;
|
|
1085
|
+
/**
|
|
1086
|
+
* Daily check-in state (CN only). Absent on the international region,
|
|
1087
|
+
* whose check-in surface does not exist — probed 2026-09-24: the
|
|
1088
|
+
* `/trae/api/v2/ug/*` family answers 404 on every ai gateway, so the card
|
|
1089
|
+
* shows no claim button there rather than a button that cannot work.
|
|
1090
|
+
*/
|
|
1091
|
+
checkin?: TraeWebCheckin;
|
|
1092
|
+
checkinError?: string;
|
|
1093
|
+
/** Subscription status of an international account (region ai only). */
|
|
1094
|
+
payStatus?: TraeWebPayStatus;
|
|
1095
|
+
payStatusError?: string;
|
|
1096
|
+
/**
|
|
1097
|
+
* This panel's account-pool state, when the Host build supports it.
|
|
1098
|
+
*
|
|
1099
|
+
* Absent on a Host without pool support, so the card renders no pool section
|
|
1100
|
+
* instead of an empty one. Assembled LAST in the status document and never
|
|
1101
|
+
* allowed to fail it: the pool reads per-account credit, and one dead account
|
|
1102
|
+
* must not blank out the panel the user is looking at.
|
|
1103
|
+
*/
|
|
1104
|
+
pool?: TraeWebPool;
|
|
1105
|
+
} | {
|
|
1106
|
+
status: 'error';
|
|
1107
|
+
message: string;
|
|
1108
|
+
};
|
|
1109
|
+
//#endregion
|
|
1110
|
+
//#region src/pi-ai-runtime.d.ts
|
|
1111
|
+
/**
|
|
1112
|
+
* Which `@earendil-works/pi-ai` copy THIS plugin resolves at runtime, and which
|
|
1113
|
+
* context shape that copy consumes (issue #27).
|
|
1114
|
+
*
|
|
1115
|
+
* WHY THIS EXISTS. The plugin declares pi-ai as a peer, so the module it
|
|
1116
|
+
* imports is whatever the install provides — and the two generations consume
|
|
1117
|
+
* DIFFERENT context shapes:
|
|
1118
|
+
*
|
|
1119
|
+
* - **modern** (0.86+): every public entry point calls `normalizeContext()`
|
|
1120
|
+
* once, so what reaches a provider is a normalized *transcript*:
|
|
1121
|
+
* `{ messages: [ { role: 'system', content, toolsAdded }, … ] }`. The system
|
|
1122
|
+
* prompt and the tool declarations live ONLY on that leading system message;
|
|
1123
|
+
* the top-level `systemPrompt` / `tools` fields are gone, and the api never
|
|
1124
|
+
* reads them back.
|
|
1125
|
+
* - **legacy** (≤0.85): expects `{ systemPrompt, tools, messages }` and has no
|
|
1126
|
+
* branch for a `system` message inside `messages` at all. Its token estimator
|
|
1127
|
+
* crashes on one — `estimateMessageTokens` iterates the string `content`
|
|
1128
|
+
* character by character and reads `.name` off a character — and its wire
|
|
1129
|
+
* converter silently drops it.
|
|
1130
|
+
*
|
|
1131
|
+
* The mismatch is reachable in BOTH directions, because DSH itself spans both
|
|
1132
|
+
* generations (`dsh-llm-pi-ai` ≤0.2.0-rc.1 declares `^0.85.1`; 0.2.0-rc.2 and
|
|
1133
|
+
* later declare `^0.87.1`, 0.2.1-alpha.2 declares `^1.0.2`, and 0.2.1-rc.1 —
|
|
1134
|
+
* the current desktop host — declares **`^1.1.0`**, which also stopped reading
|
|
1135
|
+
* the top-level fields entirely), while this plugin accepts the whole range
|
|
1136
|
+
* (`>=0.1.7-rc.1 <0.3.0-0`). Whichever side is newer, the other one is wrong:
|
|
1137
|
+
*
|
|
1138
|
+
* | host | resolved api | outcome |
|
|
1139
|
+
* |---|---|---|
|
|
1140
|
+
* | legacy | legacy | fine |
|
|
1141
|
+
* | legacy | modern | no crash, but prompt + tools silently LOST |
|
|
1142
|
+
* | modern | legacy | CRASH: `Cannot read properties of undefined (reading 'length')` |
|
|
1143
|
+
* | modern | modern | fine |
|
|
1144
|
+
*
|
|
1145
|
+
* THE BOUNDARY IS 0.86, NOT 0.87. Checked against the published tarballs:
|
|
1146
|
+
* `dist/utils/transcript.js` — and with it `normalizeContext`, the
|
|
1147
|
+
* `role === 'system'` estimator branch, and the switch away from reading
|
|
1148
|
+
* `systemPrompt`/`tools` — first appears in **0.86.0**. A `>= 0.87` gate
|
|
1149
|
+
* misclassifies 0.86.x as legacy and folds a context the modern api consumes
|
|
1150
|
+
* natively, which deletes the only carrier of the prompt and the tools.
|
|
1151
|
+
*
|
|
1152
|
+
* HOW the generation is decided, in order:
|
|
1153
|
+
*
|
|
1154
|
+
* 1. **The manifest version** of the copy this plugin resolved. Numeric semver
|
|
1155
|
+
* comparison (`minor >= 86`), so `0.86.0` and the already-shipped `1.x` line
|
|
1156
|
+
* both land on modern, and `0.9.0` correctly sorts below `0.86.0`.
|
|
1157
|
+
* 2. **The package's own layout**, when no version could be read: the transcript
|
|
1158
|
+
* module sits at `dist/utils/transcript.js` (the exports map's
|
|
1159
|
+
* `"./utils/*" → "./dist/utils/*.js"` convention is identical in every
|
|
1160
|
+
* published generation, 0.82 through 1.x).
|
|
1161
|
+
*
|
|
1162
|
+
* Both checks are deliberately resolver-independent. `import.meta.resolve()` is
|
|
1163
|
+
* NOT used to answer this question: bundler-style resolvers (vite, and equally
|
|
1164
|
+
* `vitest` under it) expand the `"./utils/*"` wildcard to a path and return it
|
|
1165
|
+
* WITHOUT checking that the file exists, so a probe built on it answers "modern"
|
|
1166
|
+
* for 0.85.1 — the exact false positive that would leave issue #27 unfixed while
|
|
1167
|
+
* claiming to be capability-based. Reading the package directory cannot be
|
|
1168
|
+
* fooled that way.
|
|
1169
|
+
*
|
|
1170
|
+
* The version is therefore the primary signal, and it also keeps the gate
|
|
1171
|
+
* forward-compatible for free: a future 2.x needs no change here, whereas a
|
|
1172
|
+
* file-name check alone would have to know about it.
|
|
1173
|
+
*
|
|
1174
|
+
* @module dsh-connect-trae/pi-ai-runtime
|
|
1175
|
+
*/
|
|
1176
|
+
/** The context generation a resolved pi-ai api module speaks. */
|
|
1177
|
+
type PiAiGeneration = 'legacy' | 'modern';
|
|
1178
|
+
/** What {@link piAiRuntimeInfo} reports about the resolved pi-ai copy. */
|
|
1179
|
+
interface PiAiRuntimeInfo {
|
|
1180
|
+
/** `modern` (0.86+: consumes normalized transcripts) or `legacy` (≤0.85 shape). */
|
|
1181
|
+
generation: PiAiGeneration;
|
|
1182
|
+
/** Version of the resolved copy, when the manifest could be read. */
|
|
1183
|
+
version: string | undefined;
|
|
1184
|
+
/** Package directory of the resolved copy, when it could be located. */
|
|
1185
|
+
resolvedFrom: string | undefined;
|
|
1186
|
+
/**
|
|
1187
|
+
* Whether the copy could be inspected at all. `false` means pi-ai does not
|
|
1188
|
+
* resolve from this plugin — the normal state of a market install probed
|
|
1189
|
+
* OUTSIDE the DSH host, where the host supplies the module at plugin runtime.
|
|
1190
|
+
* Inside DSH the copy is always inspectable: a nested one shadows host
|
|
1191
|
+
* injection, and without a nested one the host supplies its own.
|
|
1192
|
+
*/
|
|
1193
|
+
inspected: boolean;
|
|
1194
|
+
}
|
|
1195
|
+
/** Resolve (once) which pi-ai copy this plugin runs against. */
|
|
1196
|
+
declare function piAiRuntimeInfo(): PiAiRuntimeInfo;
|
|
1197
|
+
/**
|
|
1198
|
+
* The generation decision table, exported so every branch is pinned by tests
|
|
1199
|
+
* rather than by a comment here — including the two this development tree can
|
|
1200
|
+
* never reach (no local copy, and a copy with no readable version), which are
|
|
1201
|
+
* exactly the states a market install founds its gate on.
|
|
1202
|
+
*
|
|
1203
|
+
* @param version - manifest version of the resolved copy, if readable.
|
|
1204
|
+
* @param transcriptPresent - whether that copy actually ships
|
|
1205
|
+
* `dist/utils/transcript.js`; `undefined` when nothing was inspected.
|
|
1206
|
+
*/
|
|
1207
|
+
declare function decidePiAiGeneration(version: string | undefined, transcriptPresent: boolean | undefined): PiAiGeneration;
|
|
1208
|
+
/**
|
|
1209
|
+
* The generation a version string belongs to, or `undefined` when the string
|
|
1210
|
+
* cannot be read as a version at all.
|
|
1211
|
+
*
|
|
1212
|
+
* Only the minor of the `0.x` line matters for the boundary, and `major >= 1`
|
|
1213
|
+
* is modern by definition (1.0.x is already in use by `dsh-llm-pi-ai@0.2.1-alpha.2`
|
|
1214
|
+
* and consumes transcripts natively).
|
|
1215
|
+
*/
|
|
1216
|
+
declare function generationOf(version: string | undefined): PiAiGeneration | undefined;
|
|
1217
|
+
//#endregion
|
|
1218
|
+
//#region src/upstream.d.ts
|
|
1219
|
+
type TraeUpstreamErrorKind = 'authentication' | 'hard_credit' | 'soft_rate' | 'not_found' | 'server' | 'client' | 'unconfigured';
|
|
1220
|
+
type TraeChatResult = {
|
|
1221
|
+
ok: true;
|
|
1222
|
+
response: Response;
|
|
1223
|
+
} | {
|
|
1224
|
+
ok: false;
|
|
1225
|
+
status: number;
|
|
1226
|
+
kind: TraeUpstreamErrorKind;
|
|
1227
|
+
message: string;
|
|
1228
|
+
};
|
|
1229
|
+
interface TraeUpstreamClient {
|
|
1230
|
+
chatStream(bodyJson: string, signal?: AbortSignal): Promise<TraeChatResult>;
|
|
1231
|
+
}
|
|
1232
|
+
/**
|
|
1233
|
+
* Safe first-stage upstream. Real Trae traffic stays disabled until one
|
|
1234
|
+
* edition-specific endpoint and wire codec are verified with fixtures.
|
|
1235
|
+
*/
|
|
1236
|
+
declare class UnconfiguredTraeUpstreamClient implements TraeUpstreamClient {
|
|
1237
|
+
chatStream(_bodyJson: string, _signal?: AbortSignal): Promise<TraeChatResult>;
|
|
1238
|
+
}
|
|
1239
|
+
//#endregion
|
|
1240
|
+
//#region src/shim.d.ts
|
|
1241
|
+
interface ShimLogger {
|
|
1242
|
+
warn(...args: unknown[]): void;
|
|
1243
|
+
error(...args: unknown[]): void;
|
|
1244
|
+
}
|
|
1245
|
+
interface TraeShim {
|
|
1246
|
+
ready: Promise<void>;
|
|
1247
|
+
baseUrl(): string;
|
|
1248
|
+
token(): string;
|
|
1249
|
+
/** Bearer secret of one named route (defaults to the first route). */
|
|
1250
|
+
tokenOf(label?: string): string;
|
|
1251
|
+
close(): Promise<void>;
|
|
1252
|
+
}
|
|
1253
|
+
/** One bearer-authenticated chat route served by the shim. */
|
|
1254
|
+
interface TraeShimRoute {
|
|
1255
|
+
/** Stable label used to look the route's secret up (`solo` | `enterprise`). */
|
|
1256
|
+
label: string;
|
|
1257
|
+
client: TraeUpstreamClient;
|
|
1258
|
+
/**
|
|
1259
|
+
* Account-pool hooks for this route (issue #44). Absent means "one fixed
|
|
1260
|
+
* account, no rotation and no failover" — the behaviour before the pool
|
|
1261
|
+
* existed, and still the behaviour when the pool is switched off.
|
|
1262
|
+
*/
|
|
1263
|
+
pool?: TraeShimPool;
|
|
1264
|
+
}
|
|
1265
|
+
/**
|
|
1266
|
+
* What an account pool tells the shim around one chat attempt.
|
|
1267
|
+
*
|
|
1268
|
+
* The shim owns the HTTP surface and the pool owns "who bills", so the two meet
|
|
1269
|
+
* here: the pool may re-point the next request, hand back a client for a
|
|
1270
|
+
* different account after a failure, and hear about failures that only a real
|
|
1271
|
+
* request — not a probe — can produce.
|
|
1272
|
+
*
|
|
1273
|
+
* Every method must resolve rather than throw: a pool failure must never turn a
|
|
1274
|
+
* chat request into a 500 when a plain answer was possible.
|
|
1275
|
+
*/
|
|
1276
|
+
interface TraeShimPool {
|
|
1277
|
+
/**
|
|
1278
|
+
* Choose the account for this request and report which one it is. Called once,
|
|
1279
|
+
* before the first attempt; `undefined` means "the pool chose nobody", which
|
|
1280
|
+
* leaves the route's own client to bill the bound account.
|
|
1281
|
+
*/
|
|
1282
|
+
prepare(): Promise<string | undefined>;
|
|
1283
|
+
/**
|
|
1284
|
+
* The client for the next account worth trying after the current one failed,
|
|
1285
|
+
* or `undefined` when the pool has nothing left to try.
|
|
1286
|
+
*
|
|
1287
|
+
* The returned client must be bound to that account's OWN credential:
|
|
1288
|
+
* failing over is not a rebind, so it must not move the account the user
|
|
1289
|
+
* selected (the same rule per-account probes already follow).
|
|
1290
|
+
*/
|
|
1291
|
+
next(tried: readonly string[]): Promise<{
|
|
1292
|
+
accountId: string;
|
|
1293
|
+
client: TraeUpstreamClient;
|
|
1294
|
+
} | undefined>;
|
|
1295
|
+
/** Record a real request's failure against the account that hit it. */
|
|
1296
|
+
failed(accountId: string, kind: TraeUpstreamErrorKind, message: string): Promise<void>;
|
|
1297
|
+
/** One clause naming why no other account can serve, for the error text. */
|
|
1298
|
+
shortfall(tried: readonly string[]): Promise<string | undefined>;
|
|
1299
|
+
}
|
|
1300
|
+
interface TraeShimOptions {
|
|
1301
|
+
catalog: TraeCatalog;
|
|
1302
|
+
/**
|
|
1303
|
+
* Single-route form, kept for the existing call sites and tests.
|
|
1304
|
+
*
|
|
1305
|
+
* A shim serves exactly as many chat routes as the region publishes provider
|
|
1306
|
+
* ids, and they must not be interchangeable: the consumer and enterprise
|
|
1307
|
+
* pools bill different accounts, so the route is chosen by the bearer token
|
|
1308
|
+
* the adapter was built with, and an unknown token is rejected rather than
|
|
1309
|
+
* defaulting to "some" pool.
|
|
1310
|
+
*/
|
|
1311
|
+
client?: TraeUpstreamClient;
|
|
1312
|
+
routes?: readonly TraeShimRoute[];
|
|
1313
|
+
logger?: ShimLogger;
|
|
1314
|
+
}
|
|
1315
|
+
declare function createTraeShim(options: TraeShimOptions): TraeShim;
|
|
1316
|
+
//#endregion
|
|
1317
|
+
//#region src/adapter.d.ts
|
|
1318
|
+
/** Provider route this bundle owns for the domestic (CN) gateway. */
|
|
1319
|
+
declare const TRAE_PROVIDER = "trae";
|
|
1320
|
+
/**
|
|
1321
|
+
* Provider route this bundle owns for the CN **enterprise (tenant)** gateway.
|
|
1322
|
+
*
|
|
1323
|
+
* A separate provider id, not a mode switch, because the two pools bill
|
|
1324
|
+
* differently: a tenant account (`tob_online`) is served by BOTH gateways with
|
|
1325
|
+
* the SAME credential, and only the enterprise one draws on the enterprise
|
|
1326
|
+
* pool. Exposing one id would force a per-call guess about who pays.
|
|
1327
|
+
*
|
|
1328
|
+
* It never falls back to the consumer pool: picking this provider is the user
|
|
1329
|
+
* saying "bill the tenant", so a refusal is reported as an error instead of
|
|
1330
|
+
* being silently re-run against the personal quota.
|
|
1331
|
+
*/
|
|
1332
|
+
declare const TRAE_ENTERPRISE_PROVIDER = "trae-enterprise";
|
|
1333
|
+
/**
|
|
1334
|
+
* Provider route this bundle owns for the international (trae.ai) gateway.
|
|
1335
|
+
* Named `-global` to match the plugin family's convention (`workbuddy-global`);
|
|
1336
|
+
* the internal region bucket stays `ai`, which is the gateway/protocol name.
|
|
1337
|
+
*/
|
|
1338
|
+
declare const TRAE_AI_PROVIDER = "trae-global";
|
|
1339
|
+
/** The provider ids one region publishes, in model-picker order. */
|
|
1340
|
+
declare const TRAE_PROVIDER_IDS: Readonly<Record<TraeRegion, readonly string[]>>;
|
|
1341
|
+
/** Each region's default (first) provider id. */
|
|
1342
|
+
declare const TRAE_PROVIDERS: Readonly<Record<TraeRegion, string>>;
|
|
1343
|
+
/**
|
|
1344
|
+
* Which chat pool a provider bills against.
|
|
1345
|
+
*
|
|
1346
|
+
* `trae` and `trae-global` keep the behaviour they always had (the consumer
|
|
1347
|
+
* SOLO pool); `trae-enterprise` is the only id that reaches the tenant gateway.
|
|
1348
|
+
*/
|
|
1349
|
+
type TraeProviderRoute = 'solo' | 'enterprise';
|
|
1350
|
+
declare const TRAE_PROVIDER_ROUTES: Readonly<Record<string, TraeProviderRoute>>;
|
|
1351
|
+
/** Chat pool a provider id bills against; `undefined` for an unknown id. */
|
|
1352
|
+
declare function routeOfTraeProvider(provider: string): TraeProviderRoute | undefined;
|
|
1353
|
+
/** Human-readable provider names, shown in the DSH model picker. */
|
|
459
1354
|
declare const TRAE_PROVIDER_DISPLAY_NAMES: Readonly<Record<TraeRegion, string>>;
|
|
1355
|
+
/** Display name per provider id (the region table above only names the default). */
|
|
1356
|
+
declare const TRAE_PROVIDER_ID_DISPLAY_NAMES: Readonly<Record<string, string>>;
|
|
1357
|
+
/** Display name of a provider id. */
|
|
1358
|
+
declare function displayNameOfTraeProvider(provider: string): string;
|
|
460
1359
|
/** Region a provider route id belongs to. */
|
|
461
1360
|
declare function regionOfTraeProvider(provider: string): TraeRegion | undefined;
|
|
462
1361
|
declare const TRAE_STREAM_IDLE_TIMEOUT_MS = 300000;
|
|
@@ -553,6 +1452,14 @@ interface TraeAdapterOptions {
|
|
|
553
1452
|
provider?: string;
|
|
554
1453
|
/** pi-ai provider name and profile display name; defaults to the CN name. */
|
|
555
1454
|
displayName?: string;
|
|
1455
|
+
/**
|
|
1456
|
+
* Bearer secret this provider presents to the shim.
|
|
1457
|
+
*
|
|
1458
|
+
* A region can publish more than one provider (consumer + enterprise), and
|
|
1459
|
+
* the shim picks the billing pool from that bearer. Defaults to the shim's
|
|
1460
|
+
* first route, which keeps the single-provider case unchanged.
|
|
1461
|
+
*/
|
|
1462
|
+
secret?: string;
|
|
556
1463
|
resolveAttachments?: () => AttachmentStore | undefined;
|
|
557
1464
|
/**
|
|
558
1465
|
* Override the resolved pi-ai generation (see {@link withContextAdaptation});
|
|
@@ -573,6 +1480,19 @@ interface TraeCredential {
|
|
|
573
1480
|
refreshToken?: string;
|
|
574
1481
|
userId: string;
|
|
575
1482
|
accountName?: string;
|
|
1483
|
+
/**
|
|
1484
|
+
* Trae tenant this account belongs to (`account.tenant_id`, e.g. `272193024`
|
|
1485
|
+
* for a `tob_online` enterprise), absent for a personal account.
|
|
1486
|
+
*
|
|
1487
|
+
* This is what decides whether the ENTERPRISE pool can serve the account at
|
|
1488
|
+
* all. The tenant gateway accepts any CN token for its DIRECTORY read — a
|
|
1489
|
+
* personal account gets the full 49-model roster back — so "the roster
|
|
1490
|
+
* loaded" proves nothing about billing rights. Its chat endpoint is what
|
|
1491
|
+
* refuses: a personal account answers `4019` (risk control), a tenant account
|
|
1492
|
+
* streams normally. Because the two are indistinguishable from the roster,
|
|
1493
|
+
* the account list itself has to carry the flag.
|
|
1494
|
+
*/
|
|
1495
|
+
tenantId?: string;
|
|
576
1496
|
host: string;
|
|
577
1497
|
/**
|
|
578
1498
|
* Region claim from the decrypted storage document (`userRegion.region`,
|
|
@@ -603,6 +1523,27 @@ interface TraeCredentialStoreOptions {
|
|
|
603
1523
|
* stores run side by side without seeing each other's accounts.
|
|
604
1524
|
*/
|
|
605
1525
|
region?: TraeRegion;
|
|
1526
|
+
/**
|
|
1527
|
+
* Only consider credentials that belong to a Trae tenant.
|
|
1528
|
+
*
|
|
1529
|
+
* The enterprise panel's store runs with this on: a personal account cannot
|
|
1530
|
+
* bill that pool, and because the tenant gateway still serves it a directory,
|
|
1531
|
+
* binding it here would fail only at the first chat (`4019`). With the flag
|
|
1532
|
+
* set, an explicit binding to a personal account falls through to the preferred
|
|
1533
|
+
* TENANT account instead of being silently honoured.
|
|
1534
|
+
*/
|
|
1535
|
+
requireTenant?: boolean;
|
|
1536
|
+
/**
|
|
1537
|
+
* Withhold TENANT (enterprise) accounts from this store's account list.
|
|
1538
|
+
*
|
|
1539
|
+
* Set on the consumer panels: an enterprise login belongs to the enterprise
|
|
1540
|
+
* panel, and offering it here would let a tenant account be bound to the
|
|
1541
|
+
* personal pool without the user ever seeing which pool they picked. A
|
|
1542
|
+
* machine that has ONLY a tenant login still resolves it — an empty list must
|
|
1543
|
+
* not turn a working install into "signed out" — so the filter is applied to
|
|
1544
|
+
* the OFFERED list, never to the resolved credential.
|
|
1545
|
+
*/
|
|
1546
|
+
hideTenantAccounts?: boolean;
|
|
606
1547
|
/** Legacy single-copy path read as a migration source; injectable for tests. */
|
|
607
1548
|
legacyOwnPath?: string;
|
|
608
1549
|
refresh: (credential: TraeCredential) => Promise<TraeRefreshOutcome>;
|
|
@@ -612,6 +1553,16 @@ interface TraeAccountChoice {
|
|
|
612
1553
|
id: string;
|
|
613
1554
|
accountName: string;
|
|
614
1555
|
edition: TraeEdition;
|
|
1556
|
+
/**
|
|
1557
|
+
* Whether this account may bill the CN ENTERPRISE pool.
|
|
1558
|
+
*
|
|
1559
|
+
* Derived from the credential's own tenant claim, because nothing else can
|
|
1560
|
+
* tell: the tenant gateway answers a directory read for ANY CN token, so a
|
|
1561
|
+
* personal account looks perfectly serviceable right up to its first chat,
|
|
1562
|
+
* which it answers with `4019`. The enterprise panel filters its account list
|
|
1563
|
+
* and its default binding on this flag.
|
|
1564
|
+
*/
|
|
1565
|
+
enterprise: boolean;
|
|
615
1566
|
/** Routing bucket of this account (`cn` | `ai`), derived from its credential. */
|
|
616
1567
|
region: TraeRegion;
|
|
617
1568
|
source: 'desktop' | 'dsh' | 'cli';
|
|
@@ -649,17 +1600,60 @@ declare function traeOwnAuthPath(region: TraeRegion): string;
|
|
|
649
1600
|
*/
|
|
650
1601
|
declare function legacyTraeOwnAuthPath(): string;
|
|
651
1602
|
declare function normalizeTraeCredential(raw: unknown, edition: TraeEdition, source: TraeCredential['source']): TraeCredential | undefined;
|
|
1603
|
+
/**
|
|
1604
|
+
* Whether this account belongs to a Trae tenant, i.e. may bill the enterprise
|
|
1605
|
+
* pool.
|
|
1606
|
+
*
|
|
1607
|
+
* Evidence (2026-10-17, live): the tenant's own account carries
|
|
1608
|
+
* `account.tenant_id` / `tenant_name` and `scope: 'saas'`; the personal SOLO
|
|
1609
|
+
* account carries neither and reports `scope: 'marscode'`. Only the tenant one
|
|
1610
|
+
* streams on the enterprise gateway — the other answers `4019` risk control.
|
|
1611
|
+
*/
|
|
1612
|
+
declare function isEnterpriseAccount(credential: Pick<TraeCredential, 'tenantId'> | undefined): boolean;
|
|
652
1613
|
declare class TraeCredentialStore {
|
|
653
1614
|
private storagePathOverride;
|
|
654
1615
|
private edition;
|
|
655
1616
|
private accountId;
|
|
656
1617
|
private readonly region;
|
|
1618
|
+
private readonly requireTenant;
|
|
1619
|
+
private readonly hideTenantAccounts;
|
|
657
1620
|
private readonly ownPathExplicit;
|
|
658
1621
|
private readonly legacyOwnPath;
|
|
659
1622
|
private readonly legacyOwnPathExplicit;
|
|
660
1623
|
private readonly refresh;
|
|
661
1624
|
private readonly refreshMarginMs;
|
|
662
1625
|
private inflight;
|
|
1626
|
+
/**
|
|
1627
|
+
* In-flight refreshes of individual accounts, one per account id.
|
|
1628
|
+
*
|
|
1629
|
+
* The card reads every member's balance in one poll and each balance can
|
|
1630
|
+
* decide to refresh, so without this dedupe the same account would exchange
|
|
1631
|
+
* its token several times in a second and the last writer would win over a
|
|
1632
|
+
* copy another read already holds.
|
|
1633
|
+
*/
|
|
1634
|
+
private readonly inflightByAccount;
|
|
1635
|
+
/**
|
|
1636
|
+
* When a per-account refresh last FAILED, so a dead refresh token is not
|
|
1637
|
+
* exchanged again on every poll.
|
|
1638
|
+
*
|
|
1639
|
+
* The card reads every member's balance on each refresh, and a revoked refresh
|
|
1640
|
+
* token fails forever: without this memo each poll would spend two upstream
|
|
1641
|
+
* calls (the current contract and the superseded one) on an account no retry
|
|
1642
|
+
* can fix. Five minutes keeps a transient network failure cheap to retry while
|
|
1643
|
+
* a revoked sign-in costs almost nothing.
|
|
1644
|
+
*/
|
|
1645
|
+
private readonly refreshFailedAtMs;
|
|
1646
|
+
/**
|
|
1647
|
+
* The account the NEXT request should bill, when a pool — not the user's
|
|
1648
|
+
* binding — is choosing (issue #44).
|
|
1649
|
+
*
|
|
1650
|
+
* Runtime only, never persisted: the binding in settings is what a save
|
|
1651
|
+
* writes and what the card's account table reports as selected, while this is
|
|
1652
|
+
* "who serves right now". Keeping them apart is what lets the pool rotate
|
|
1653
|
+
* without silently rewriting the user's choice, and what makes turning the
|
|
1654
|
+
* pool off restore the bound account with no rebind at all.
|
|
1655
|
+
*/
|
|
1656
|
+
private rotatedAccountId;
|
|
663
1657
|
constructor(options: TraeCredentialStoreOptions);
|
|
664
1658
|
/** Whether a credential's own claim belongs to this store's region. */
|
|
665
1659
|
private matchesRegion;
|
|
@@ -713,7 +1707,80 @@ declare class TraeCredentialStore {
|
|
|
713
1707
|
* binding itself already fell through to the live account.
|
|
714
1708
|
*/
|
|
715
1709
|
liveLogin(): Promise<TraeAccountChoice | undefined>;
|
|
1710
|
+
/**
|
|
1711
|
+
* The accounts this store is willing to OFFER.
|
|
1712
|
+
*
|
|
1713
|
+
* Scoped to who this store may bill:
|
|
1714
|
+
* - a consumer store withholds tenant accounts (the enterprise pool is a
|
|
1715
|
+
* separate tab and a separate billing decision), and
|
|
1716
|
+
* - a tenant store withholds personal accounts (they cannot bill that
|
|
1717
|
+
* pool; the tenant gateway answers their chat with 4019).
|
|
1718
|
+
*
|
|
1719
|
+
* Either filter backs off when it would hide EVERYTHING, so a machine whose
|
|
1720
|
+
* only login is on the other side keeps working instead of reading as
|
|
1721
|
+
* "signed out".
|
|
1722
|
+
*/
|
|
1723
|
+
private offered;
|
|
716
1724
|
accounts(): Promise<TraeAccountChoice[]>;
|
|
1725
|
+
/**
|
|
1726
|
+
* The credential this store bills, given every credential it can see.
|
|
1727
|
+
*
|
|
1728
|
+
* This is the ONE resolution rule, and it exists so that `current()` and
|
|
1729
|
+
* `accounts()` cannot disagree: `current()` applies it to `readAll()`, and
|
|
1730
|
+
* `accounts()` applies the SAME verdict to decide which row reads as
|
|
1731
|
+
* `selected`. Deriving that flag independently is exactly how the two drifted
|
|
1732
|
+
* apart — and a card that marks a different account than the one the chat
|
|
1733
|
+
* bills is worse than no card at all, because a press that "saves the
|
|
1734
|
+
* selected account" then rewrites the binding to the account that was only
|
|
1735
|
+
* ever DISPLAYED.
|
|
1736
|
+
*
|
|
1737
|
+
* Order of business:
|
|
1738
|
+
* 1. No binding — the detected default (`preferred`) is in use, chosen from
|
|
1739
|
+
* the OFFERED list rather than from every login on the machine. A
|
|
1740
|
+
* consumer store that withholds tenant accounts must not default its
|
|
1741
|
+
* billing to the tenant pool merely because that login sorts first;
|
|
1742
|
+
* withholding is a billing decision, not a display detail.
|
|
1743
|
+
* 2. A binding — honour it verbatim, even when the OFFERED list hides that
|
|
1744
|
+
* account. Resolution is never scoped by `hideTenantAccounts`: the user's
|
|
1745
|
+
* explicit choice outranks a display filter.
|
|
1746
|
+
* 3. A tenant-scoped store cannot bill through a personal account, and the
|
|
1747
|
+
* tenant gateway only reveals that at the first chat (`4019`). Honour the
|
|
1748
|
+
* binding when it is eligible; otherwise fall back to the preferred
|
|
1749
|
+
* tenant account, which is the one the user's enterprise tab is meant to
|
|
1750
|
+
* use.
|
|
1751
|
+
* 4. A saved account can disappear when Trae replaces its local login. Do
|
|
1752
|
+
* NOT silently fall back to a different account: that would bill a
|
|
1753
|
+
* different account than the one the user explicitly selected. Return
|
|
1754
|
+
* undefined so the caller surfaces "no signed-in account" and the user
|
|
1755
|
+
* can re-select, instead of the plugin quietly switching accounts behind
|
|
1756
|
+
* their back.
|
|
1757
|
+
*/
|
|
1758
|
+
private inUse;
|
|
1759
|
+
/**
|
|
1760
|
+
* Point this store at one specific account for the NEXT request, without
|
|
1761
|
+
* touching the user's saved binding (issue #44).
|
|
1762
|
+
*
|
|
1763
|
+
* Called by the account pool before each chat request. `undefined` clears the
|
|
1764
|
+
* override, which is how switching the pool off immediately restores the
|
|
1765
|
+
* binding — no rebind, no settings write, nothing to undo.
|
|
1766
|
+
*
|
|
1767
|
+
* A rotation is a REQUEST, not an assertion: `current()` honours it only when
|
|
1768
|
+
* the account still exists locally and this store's own eligibility rules
|
|
1769
|
+
* allow it, and otherwise falls back to the binding. So a rotation can never
|
|
1770
|
+
* invent a credential that {@link current} would refuse.
|
|
1771
|
+
*/
|
|
1772
|
+
setRotatedAccount(accountId: string | undefined): void;
|
|
1773
|
+
/** The rotation currently in effect, for diagnostics and the pool's own card. */
|
|
1774
|
+
rotatedAccount(): string | undefined;
|
|
1775
|
+
/**
|
|
1776
|
+
* The credential a rotation points at, or `undefined` when it cannot be
|
|
1777
|
+
* honoured (the login was removed from this machine, or a tenant-scoped store
|
|
1778
|
+
* refuses a personal account).
|
|
1779
|
+
*
|
|
1780
|
+
* Never substitutes a different account here: the caller then applies the
|
|
1781
|
+
* user's binding, which is the honest answer when the pool's choice is gone.
|
|
1782
|
+
*/
|
|
1783
|
+
private rotated;
|
|
717
1784
|
current(): Promise<TraeCredential | undefined>;
|
|
718
1785
|
/**
|
|
719
1786
|
* The stored credential of one specific account, without changing which
|
|
@@ -731,6 +1798,22 @@ declare class TraeCredentialStore {
|
|
|
731
1798
|
* fallback, for the same reason {@link current} refuses to fall back.
|
|
732
1799
|
*/
|
|
733
1800
|
credentialOf(accountId: string): Promise<TraeCredential | undefined>;
|
|
1801
|
+
/**
|
|
1802
|
+
* A SPECIFIC account's credential, refreshed when its access token is due.
|
|
1803
|
+
*
|
|
1804
|
+
* {@link credentialOf} is a pure lookup and hands back an expired token as-is,
|
|
1805
|
+
* which is right for the chat path (that request fails honestly) but wrong for
|
|
1806
|
+
* a READ of that account's own data: Trae's access token lives for days while
|
|
1807
|
+
* the refresh token lives for months, so a pooled account the user has not
|
|
1808
|
+
* used this week reads as "no balance" — the card showed a bare `—` for an
|
|
1809
|
+
* account whose refresh token was still good. Only the refresh margin decides
|
|
1810
|
+
* here, and a FAILED refresh still yields the stored credential so the caller
|
|
1811
|
+
* reports the failure it actually hit instead of inventing "not signed in".
|
|
1812
|
+
*
|
|
1813
|
+
* This never touches the binding or the rotation: refreshing account B must
|
|
1814
|
+
* not make B the account the next chat request bills.
|
|
1815
|
+
*/
|
|
1816
|
+
resolveCredentialOf(accountId: string): Promise<TraeCredential | undefined>;
|
|
734
1817
|
/**
|
|
735
1818
|
* Store imported credentials (issue #37), replacing any entry with the same
|
|
736
1819
|
* account id and leaving every other entry alone.
|
|
@@ -1524,9 +2607,22 @@ interface TraeSoloClientOptions {
|
|
|
1524
2607
|
credential(): Promise<TraeCredential>;
|
|
1525
2608
|
identity(): Promise<TraeIdentity>;
|
|
1526
2609
|
baseUrl?: string;
|
|
2610
|
+
/**
|
|
2611
|
+
* Which chat gateway this client is pinned to. Omitted = `solo`, the consumer
|
|
2612
|
+
* pool. `enterprise` targets the tenant-billed `console.enterprise` gateway
|
|
2613
|
+
* and stamps the per-call `session_id` it requires (see
|
|
2614
|
+
* {@link TRAE_ENTERPRISE_HOST}).
|
|
2615
|
+
*
|
|
2616
|
+
* Pinned per CLIENT, never chosen per call: a turn must not be able to drift
|
|
2617
|
+
* from the pool the user picked, so the route is fixed when the provider is
|
|
2618
|
+
* built and every request that client makes stays on it.
|
|
2619
|
+
*/
|
|
2620
|
+
route?: TraeChatRoute;
|
|
1527
2621
|
fetchImpl?: typeof fetch;
|
|
1528
2622
|
log?: (message: string, detail?: unknown) => void;
|
|
1529
2623
|
}
|
|
2624
|
+
/** Which chat pool a client is pinned to. */
|
|
2625
|
+
type TraeChatRoute = 'solo' | 'enterprise';
|
|
1530
2626
|
declare class TraeSoloUpstreamClient {
|
|
1531
2627
|
private readonly options;
|
|
1532
2628
|
private readonly fetchImpl;
|
|
@@ -1547,9 +2643,22 @@ declare class TraeSoloUpstreamClient {
|
|
|
1547
2643
|
/** Merge one function's config list into the shared catalogue (first wins). */
|
|
1548
2644
|
private collectModels;
|
|
1549
2645
|
chatStream(bodyJson: string, signal?: AbortSignal, functionName?: string): Promise<TraeChatResult>;
|
|
2646
|
+
/** Chat gateway for this client's pinned route. */
|
|
2647
|
+
private chatBaseOf;
|
|
1550
2648
|
}
|
|
1551
2649
|
//#endregion
|
|
1552
2650
|
//#region src/solo-bridge.d.ts
|
|
2651
|
+
/**
|
|
2652
|
+
* One upstream failure as Trae reports it inside a 200 SSE body: an `error`
|
|
2653
|
+
* event, or any event carrying a `code` at or above Trae's error band.
|
|
2654
|
+
*/
|
|
2655
|
+
interface TraeFailure {
|
|
2656
|
+
code?: number;
|
|
2657
|
+
message: string;
|
|
2658
|
+
extra?: unknown;
|
|
2659
|
+
}
|
|
2660
|
+
/** A readable message for an in-stream Trae failure, with its code preserved. */
|
|
2661
|
+
declare function describeTraeFailure(failure: TraeFailure): string;
|
|
1553
2662
|
/** Convert Trae's named SSE events into OpenAI chat-completion SSE chunks. */
|
|
1554
2663
|
declare function bridgeTraeSoloStream(response: Response, model: string): Response;
|
|
1555
2664
|
/**
|
|
@@ -1790,573 +2899,307 @@ declare class TraeUsageClient {
|
|
|
1790
2899
|
payStatus(signal?: AbortSignal): Promise<TraePayStatus>;
|
|
1791
2900
|
/**
|
|
1792
2901
|
* The Work-credit endpoints are CN-only. The international region is
|
|
1793
|
-
* subscription-based and must read {@link payStatus} instead; guarding here
|
|
1794
|
-
* keeps the card's degraded `creditsError` message diagnosable rather than
|
|
1795
|
-
* letting an ai credential hit an unverified path on its pay gateway.
|
|
1796
|
-
*/
|
|
1797
|
-
private requireCnRegion;
|
|
1798
|
-
/** Total entitlements / credits and per-pack breakdown. */
|
|
1799
|
-
snapshot(signal?: AbortSignal): Promise<TraeUsageSnapshot>;
|
|
1800
|
-
/**
|
|
1801
|
-
* Daily check-in status. Read-only, and answered with or without the device
|
|
1802
|
-
* header — the claim is what needs it.
|
|
1803
|
-
*
|
|
1804
|
-
* The read DOES carry the header whenever this installation has one, and
|
|
1805
|
-
* that is deliberate rather than incidental: `did_checked_in` is answered
|
|
1806
|
-
* per device, so omitting the header would make a device that already claimed
|
|
1807
|
-
* today look identical to one that never has (measured 2026-09-26, see
|
|
1808
|
-
* {@link TraeCheckinStatus.didCheckedIn}).
|
|
1809
|
-
*/
|
|
1810
|
-
checkinStatus(signal?: AbortSignal): Promise<TraeCheckinStatus>;
|
|
1811
|
-
/**
|
|
1812
|
-
* Claim today's check-in reward. The ONLY state-changing call in this client.
|
|
1813
|
-
*
|
|
1814
|
-
* The upstream is idempotent per Beijing day (verified 2026-09-24: repeating
|
|
1815
|
-
* the call on an already-claimed day answers `code: 0` while the entitlement
|
|
1816
|
-
* total stays byte-identical), so a double click cannot double-grant. The
|
|
1817
|
-
* card and its route still guard, because "cannot double-grant" is a property
|
|
1818
|
-
* of the upstream we verify rather than one we rely on.
|
|
1819
|
-
*
|
|
1820
|
-
* A business refusal arrives as HTTP 200 with a non-zero `code` — most often
|
|
1821
|
-
* `9004` when the request carries no `x-device-id`. That is reported as
|
|
1822
|
-
* `claimed: false` rather than thrown, so the caller can tell "the upstream
|
|
1823
|
-
* refused this" apart from "the request never arrived".
|
|
1824
|
-
*/
|
|
1825
|
-
claimCheckin(signal?: AbortSignal): Promise<TraeCheckinClaim>;
|
|
1826
|
-
/** Rewards / activity rules. */
|
|
1827
|
-
activities(signal?: AbortSignal): Promise<TraeActivityRule[]>;
|
|
1828
|
-
/** Convenience: snapshot + check-in + activities in one call (best-effort, non-fatal on missing). */
|
|
1829
|
-
view(signal?: AbortSignal): Promise<TraeUsageView>;
|
|
1830
|
-
}
|
|
1831
|
-
//#endregion
|
|
1832
|
-
//#region src/status-paths.d.ts
|
|
1833
|
-
/** Plugin-owned usage endpoint consumed by its browser half. */
|
|
1834
|
-
declare const TRAE_USAGE_PATH = "/plugins/dsh-connect-trae/usage";
|
|
1835
|
-
/**
|
|
1836
|
-
* Plugin-owned availability probe: POST the model ids to test, get one verdict
|
|
1837
|
-
* per model. Unlike every read-only sibling this route SPENDS quota — a real
|
|
1838
|
-
* (minimal) chat call per model — so it is user-initiated only, and the card
|
|
1839
|
-
* labels the button as consuming a small amount.
|
|
1840
|
-
*/
|
|
1841
|
-
declare const TRAE_MODELS_TEST_PATH = "/plugins/dsh-connect-trae/models/test";
|
|
1842
|
-
/**
|
|
1843
|
-
* Per-account status (check-in / credits / subscription) for the account table.
|
|
1844
|
-
*
|
|
1845
|
-
* Each row of the account table reads its OWN state through this route — a CN
|
|
1846
|
-
* row carries its check-in state and balance, an ai row its subscription.
|
|
1847
|
-
* POST with the accountId in the body: one upstream read per account, paid
|
|
1848
|
-
* only when the table is actually shown.
|
|
1849
|
-
*/
|
|
1850
|
-
declare const TRAE_ACCOUNT_STATUS_PATH = "/plugins/dsh-connect-trae/account-status";
|
|
1851
|
-
/**
|
|
1852
|
-
* Plugin-owned daily check-in claim endpoint. POST, loopback-only, and the only
|
|
1853
|
-
* route in this plugin that changes upstream account state. The accountId in
|
|
1854
|
-
* the body names WHICH account to claim for, so the button can sit in each
|
|
1855
|
-
* account row.
|
|
1856
|
-
*/
|
|
1857
|
-
declare const TRAE_CHECKIN_PATH = "/plugins/dsh-connect-trae/checkin";
|
|
1858
|
-
/** Query parameter naming the region a card request addresses. */
|
|
1859
|
-
declare const TRAE_REGION_PARAM = "region";
|
|
1860
|
-
/** Every region, in card tab order. */
|
|
1861
|
-
declare const TRAE_REGIONS: readonly TraeRegion[];
|
|
1862
|
-
/**
|
|
1863
|
-
* Address one region's status route. The two regions are separate provider
|
|
1864
|
-
* stacks; every card request carries the region whose tab the user is on.
|
|
1865
|
-
*/
|
|
1866
|
-
declare function withTraeRegion(path: string, region: TraeRegion): string;
|
|
1867
|
-
/**
|
|
1868
|
-
* Read the region parameter off a status-route URL. Absent means the domestic
|
|
1869
|
-
* tab (`cn`); a present-but-unknown value returns undefined so the route can
|
|
1870
|
-
* answer 400 instead of guessing.
|
|
1871
|
-
*/
|
|
1872
|
-
declare function regionOfTraeStatusUrl(url: string): TraeRegion | undefined;
|
|
1873
|
-
/** One credit pack and its remaining credit. */
|
|
1874
|
-
interface TraeWebCreditAccount {
|
|
1875
|
-
displayDesc: string;
|
|
1876
|
-
remain: number;
|
|
1877
|
-
size: number;
|
|
1878
|
-
/**
|
|
1879
|
-
* When this pack lapses, in epoch milliseconds, taken from the upstream's own
|
|
1880
|
-
* `end_time`. Absent when the upstream reported no usable time (0 / missing),
|
|
1881
|
-
* so a consumer can tell "no date" apart from "1970".
|
|
1882
|
-
*/
|
|
1883
|
-
expiresAtMs?: number;
|
|
1884
|
-
}
|
|
1885
|
-
/** Credit lapsing inside one horizon, as the card reports it. */
|
|
1886
|
-
interface TraeWebExpiringCredits {
|
|
1887
|
-
/** Sum of the contributing packs' remaining credit. */
|
|
1888
|
-
total: number;
|
|
1889
|
-
/** How many packs contribute. */
|
|
1890
|
-
packs: number;
|
|
1891
|
-
/** Soonest expiry among them, epoch milliseconds. */
|
|
1892
|
-
earliestExpiresAtMs?: number;
|
|
1893
|
-
}
|
|
1894
|
-
/**
|
|
1895
|
-
* Credit at risk, bucketed by how soon it lapses.
|
|
1896
|
-
*
|
|
1897
|
-
* Only present when at least one pack carried a usable `expiresAtMs` — a card
|
|
1898
|
-
* must never read "we were not told when anything expires" as "nothing is
|
|
1899
|
-
* about to lapse".
|
|
1900
|
-
*/
|
|
1901
|
-
interface TraeWebCreditExpiry {
|
|
1902
|
-
within24h: TraeWebExpiringCredits;
|
|
1903
|
-
within3d: TraeWebExpiringCredits;
|
|
1904
|
-
within7d: TraeWebExpiringCredits;
|
|
1905
|
-
/** Packs already past their end time but still carrying credit. */
|
|
1906
|
-
expired: TraeWebExpiringCredits;
|
|
1907
|
-
}
|
|
1908
|
-
/** Aggregated usage/credit answer rendered by the plugin card. */
|
|
1909
|
-
interface TraeWebCredits {
|
|
1910
|
-
total: number;
|
|
1911
|
-
consumed: number;
|
|
1912
|
-
available: number;
|
|
1913
|
-
workAvailable: number;
|
|
1914
|
-
generalAvailable: number;
|
|
1915
|
-
accounts: readonly TraeWebCreditAccount[];
|
|
1916
|
-
/**
|
|
1917
|
-
* Credit lapsing soon, for the card's warning banner.
|
|
1918
|
-
*
|
|
1919
|
-
* Derived from the per-pack {@link TraeWebCreditAccount.expiresAtMs} the
|
|
1920
|
-
* `/usage` route already carries, so it costs no extra upstream read.
|
|
1921
|
-
*/
|
|
1922
|
-
expiry?: TraeWebCreditExpiry;
|
|
1923
|
-
}
|
|
1924
|
-
/** Editable Trae model row rendered by the plugin-owned settings card. */
|
|
1925
|
-
interface TraeWebModel {
|
|
1926
|
-
id: string;
|
|
1927
|
-
name: string;
|
|
1928
|
-
contextWindow?: number;
|
|
1929
|
-
maxTokens?: number;
|
|
1930
|
-
input?: ('text' | 'image')[];
|
|
1931
|
-
creditMultiplier?: number;
|
|
1932
|
-
reasoningSupported?: boolean;
|
|
1933
|
-
reasoning?: {
|
|
1934
|
-
supported: string[];
|
|
1935
|
-
defaultEffort?: string;
|
|
1936
|
-
};
|
|
1937
|
-
maxContextWindow?: number;
|
|
2902
|
+
* subscription-based and must read {@link payStatus} instead; guarding here
|
|
2903
|
+
* keeps the card's degraded `creditsError` message diagnosable rather than
|
|
2904
|
+
* letting an ai credential hit an unverified path on its pay gateway.
|
|
2905
|
+
*/
|
|
2906
|
+
private requireCnRegion;
|
|
2907
|
+
/** Total entitlements / credits and per-pack breakdown. */
|
|
2908
|
+
snapshot(signal?: AbortSignal): Promise<TraeUsageSnapshot>;
|
|
1938
2909
|
/**
|
|
1939
|
-
*
|
|
1940
|
-
*
|
|
1941
|
-
*
|
|
1942
|
-
*
|
|
2910
|
+
* Daily check-in status. Read-only, and answered with or without the device
|
|
2911
|
+
* header — the claim is what needs it.
|
|
2912
|
+
*
|
|
2913
|
+
* The read DOES carry the header whenever this installation has one, and
|
|
2914
|
+
* that is deliberate rather than incidental: `did_checked_in` is answered
|
|
2915
|
+
* per device, so omitting the header would make a device that already claimed
|
|
2916
|
+
* today look identical to one that never has (measured 2026-09-26, see
|
|
2917
|
+
* {@link TraeCheckinStatus.didCheckedIn}).
|
|
1943
2918
|
*/
|
|
1944
|
-
|
|
2919
|
+
checkinStatus(signal?: AbortSignal): Promise<TraeCheckinStatus>;
|
|
1945
2920
|
/**
|
|
1946
|
-
*
|
|
1947
|
-
*
|
|
1948
|
-
*
|
|
1949
|
-
*
|
|
2921
|
+
* Claim today's check-in reward. The ONLY state-changing call in this client.
|
|
2922
|
+
*
|
|
2923
|
+
* The upstream is idempotent per Beijing day (verified 2026-09-24: repeating
|
|
2924
|
+
* the call on an already-claimed day answers `code: 0` while the entitlement
|
|
2925
|
+
* total stays byte-identical), so a double click cannot double-grant. The
|
|
2926
|
+
* card and its route still guard, because "cannot double-grant" is a property
|
|
2927
|
+
* of the upstream we verify rather than one we rely on.
|
|
2928
|
+
*
|
|
2929
|
+
* A business refusal arrives as HTTP 200 with a non-zero `code` — most often
|
|
2930
|
+
* `9004` when the request carries no `x-device-id`. That is reported as
|
|
2931
|
+
* `claimed: false` rather than thrown, so the caller can tell "the upstream
|
|
2932
|
+
* refused this" apart from "the request never arrived".
|
|
1950
2933
|
*/
|
|
1951
|
-
|
|
1952
|
-
/**
|
|
1953
|
-
|
|
2934
|
+
claimCheckin(signal?: AbortSignal): Promise<TraeCheckinClaim>;
|
|
2935
|
+
/** Rewards / activity rules. */
|
|
2936
|
+
activities(signal?: AbortSignal): Promise<TraeActivityRule[]>;
|
|
2937
|
+
/** Convenience: snapshot + check-in + activities in one call (best-effort, non-fatal on missing). */
|
|
2938
|
+
view(signal?: AbortSignal): Promise<TraeUsageView>;
|
|
1954
2939
|
}
|
|
2940
|
+
//#endregion
|
|
2941
|
+
//#region src/account-pool.d.ts
|
|
1955
2942
|
/**
|
|
1956
|
-
*
|
|
2943
|
+
* Which of the pool's writers produced a measurement.
|
|
1957
2944
|
*
|
|
1958
|
-
* `
|
|
1959
|
-
* a
|
|
1960
|
-
*
|
|
2945
|
+
* `test-batch` — the user pressed "test" (or the check-in batch), so the outcome
|
|
2946
|
+
* is a deliberate measurement of the target model.
|
|
2947
|
+
* `live-request` — a real chat request failed and its failure was recorded so the
|
|
2948
|
+
* NEXT request starts from a measurement instead of rediscovering the same wall.
|
|
2949
|
+
*
|
|
2950
|
+
* The distinction is diagnostic, not behavioural: both feed the same routing and
|
|
2951
|
+
* cooling logic. It exists so a user who watched an account measure `ok` and
|
|
2952
|
+
* later saw it limited can tell whether that was a later live failure (expected)
|
|
2953
|
+
* or an older value winning (a bug).
|
|
1961
2954
|
*/
|
|
1962
|
-
type
|
|
1963
|
-
/**
|
|
1964
|
-
interface
|
|
1965
|
-
/**
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
detail: string;
|
|
1970
|
-
}[];
|
|
1971
|
-
/** Models skipped because the region has no live catalog row for them. */
|
|
1972
|
-
skipped?: string[];
|
|
2955
|
+
type TraePoolProbeSource = 'test-batch' | 'live-request';
|
|
2956
|
+
/** One account's pool entry: what the picker needs, plus its last measurement. */
|
|
2957
|
+
interface TraePoolAccount {
|
|
2958
|
+
/** Stable account id (the same one `TraeWebAccount` carries). */
|
|
2959
|
+
id: string;
|
|
2960
|
+
/** Human name, or `''` when the desktop app recorded none. */
|
|
2961
|
+
accountName: string;
|
|
1973
2962
|
}
|
|
1974
2963
|
/**
|
|
1975
|
-
*
|
|
1976
|
-
*
|
|
1977
|
-
* The two booleans answer DIFFERENT questions and must never be collapsed into
|
|
1978
|
-
* one "done" flag — doing that was a shipped bug (see CHANGELOG 2.3.1):
|
|
1979
|
-
*
|
|
1980
|
-
* - `checkedIn` is ACCOUNT-level: today's reward pack exists for this account,
|
|
1981
|
-
* so there is nothing left to claim and the balance already includes it.
|
|
1982
|
-
* - `didCheckedIn` is DEVICE-level: this MACHINE already spent its one
|
|
1983
|
-
* check-in for the day, whichever account spent it. The upstream keys it on
|
|
1984
|
-
* the `x-device-id` in the request and answers it per device, not per
|
|
1985
|
-
* account (measured 2026-09-26: the same account and Beijing day answered
|
|
1986
|
-
* `false` with the header omitted, with a synthetic id, and with another
|
|
1987
|
-
* install's id, and `true` for the device that actually claimed).
|
|
2964
|
+
* What the pool knows about ONE account's last test of the target model.
|
|
1988
2965
|
*
|
|
1989
|
-
*
|
|
1990
|
-
*
|
|
1991
|
-
*
|
|
1992
|
-
*
|
|
2966
|
+
* `undefined` means "never tested", which is deliberately distinct from every
|
|
2967
|
+
* measured outcome: an untested account is a candidate (the user may want to use
|
|
2968
|
+
* it now) whereas a measured failure is not. Collapsing the two would make a
|
|
2969
|
+
* fresh pool look empty until the first test.
|
|
1993
2970
|
*/
|
|
1994
|
-
interface
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
}
|
|
2001
|
-
/** The claim route's answer, so the card can report a business refusal verbatim. */
|
|
2002
|
-
interface TraeWebCheckinClaim {
|
|
2003
|
-
/** False when the upstream answered a non-zero business code (e.g. 9004). */
|
|
2004
|
-
claimed: boolean;
|
|
2971
|
+
interface TraePoolProbe {
|
|
2972
|
+
outcome: TraePoolOutcome;
|
|
2973
|
+
/** When this measurement was taken, in epoch ms. */
|
|
2974
|
+
atMs: number;
|
|
2975
|
+
/** WHAT WROTE this measurement; absent for records written before it existed. */
|
|
2976
|
+
source?: TraePoolProbeSource;
|
|
2005
2977
|
/**
|
|
2006
|
-
*
|
|
2007
|
-
*
|
|
2008
|
-
*
|
|
2978
|
+
* When this account may be usable again, when the upstream stated a time.
|
|
2979
|
+
*
|
|
2980
|
+
* Present for a `rate-limited` / `out-of-credit` outcome whose failure carried
|
|
2981
|
+
* a reset sentence. ABSENT when the upstream named no time — that absence is
|
|
2982
|
+
* meaningful and must never be turned into a locally invented countdown.
|
|
2009
2983
|
*/
|
|
2010
|
-
|
|
2984
|
+
retryAtMs?: number;
|
|
2011
2985
|
/**
|
|
2012
|
-
*
|
|
2013
|
-
*
|
|
2014
|
-
*
|
|
2015
|
-
*
|
|
2986
|
+
* The upstream's own words on the failure, already redacted.
|
|
2987
|
+
*
|
|
2988
|
+
* Kept so the card can say WHY an account left the pool instead of repeating
|
|
2989
|
+
* one generic sentence for a DNS failure, a gateway 502 and a cancelled request
|
|
2990
|
+
* alike.
|
|
2016
2991
|
*/
|
|
2017
|
-
deviceCheckedIn?: boolean;
|
|
2018
|
-
code?: number;
|
|
2019
2992
|
message?: string;
|
|
2020
|
-
/** The refreshed check-in state, present whenever the claim path ran. */
|
|
2021
|
-
checkin?: TraeWebCheckin;
|
|
2022
|
-
}
|
|
2023
|
-
/** One reward activity rule (name + work/general credits when present). */
|
|
2024
|
-
interface TraeWebActivity {
|
|
2025
|
-
activityId: string;
|
|
2026
|
-
enabled: boolean;
|
|
2027
|
-
workCredits?: number;
|
|
2028
|
-
generalCredits?: number;
|
|
2029
|
-
}
|
|
2030
|
-
interface TraeWebAccount {
|
|
2031
|
-
id: string;
|
|
2032
|
-
accountName: string;
|
|
2033
|
-
edition: 'cn' | 'sg' | 'solo' | 'solo-sg';
|
|
2034
|
-
/** Routing bucket of this account, derived from its credential. */
|
|
2035
|
-
region: TraeRegion;
|
|
2036
|
-
source: 'desktop' | 'dsh' | 'cli';
|
|
2037
|
-
tokenExpiresAtMs: number;
|
|
2038
2993
|
/**
|
|
2039
|
-
*
|
|
2994
|
+
* WHICH MODEL this measurement is about.
|
|
2040
2995
|
*
|
|
2041
|
-
*
|
|
2042
|
-
*
|
|
2043
|
-
*
|
|
2044
|
-
*
|
|
2045
|
-
*
|
|
2046
|
-
*
|
|
2047
|
-
* it
|
|
2996
|
+
* `1005` (plan gate), `4001` (no SOLO function serves this `config_name`) and
|
|
2997
|
+
* `4011` (throttle) are all ACCOUNT × MODEL conditions: the same credential
|
|
2998
|
+
* answers other models at the same second. A row rendered as plain "可用"
|
|
2999
|
+
* therefore claims more than the measurement says. Storing the model id is what
|
|
3000
|
+
* makes the claim checkable.
|
|
3001
|
+
*
|
|
3002
|
+
* Deliberately NOT inherited when an update omits it — the opposite of
|
|
3003
|
+
* `accountName` and `retryAtMs`. Those describe the ACCOUNT (still true when a
|
|
3004
|
+
* later writer cannot see them); the model describes THIS measurement, and
|
|
3005
|
+
* carrying an older id forward would relabel a fresh measurement with a model
|
|
3006
|
+
* it was not taken on.
|
|
2048
3007
|
*/
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
* fields and the plugin never bills from this value directly: the FOLLOW is a
|
|
2061
|
-
* settings write of `accounts.<region>`, made by the card or by the host's
|
|
2062
|
-
* follow switch, never a silent rebind here.
|
|
2063
|
-
*/
|
|
2064
|
-
interface TraeWebAppLogin {
|
|
2065
|
-
id: string;
|
|
2066
|
-
accountName: string;
|
|
2067
|
-
edition: 'cn' | 'sg' | 'solo' | 'solo-sg';
|
|
2068
|
-
region: TraeRegion;
|
|
2069
|
-
}
|
|
2070
|
-
/**
|
|
2071
|
-
* The per-account state shown inside an account row of the card's table.
|
|
2072
|
-
*
|
|
2073
|
-
* Every locally detected account in a region is rendered as a row; the
|
|
2074
|
-
* interesting per-account facts differ by region, which is why this is a union
|
|
2075
|
-
* of the three surfaces the card previously showed only for the BOUND account:
|
|
2076
|
-
*
|
|
2077
|
-
* - CN rows carry their own check-in state (Trae keys the daily reward per
|
|
2078
|
-
* ACCOUNT, and the one-per-device tally per DEVICE, see {@link TraeWebCheckin})
|
|
2079
|
-
* and their Work/General balances.
|
|
2080
|
-
* - ai rows carry their own subscription / trial status.
|
|
2081
|
-
*
|
|
2082
|
-
* Each field is optional: a read that fails keeps the row visible (perhaps
|
|
2083
|
-
* without a figure) rather than dropping the account and its switch.
|
|
2084
|
-
*/
|
|
2085
|
-
interface TraeWebAccountState {
|
|
2086
|
-
/** The account this state belongs to. */
|
|
2087
|
-
id: string;
|
|
2088
|
-
/** CN: today's check-in state of this account (absent for ai rows). */
|
|
2089
|
-
checkin?: TraeWebCheckin;
|
|
2090
|
-
/** CN: this account's Work/General balances (absent for ai rows). */
|
|
2091
|
-
credits?: TraeWebCredits;
|
|
2092
|
-
/** ai: this account's subscription / trial status (absent for CN rows). */
|
|
2093
|
-
payStatus?: TraeWebPayStatus;
|
|
3008
|
+
modelId?: string;
|
|
3009
|
+
/**
|
|
3010
|
+
* The account's display name AT THE MOMENT IT WAS MEASURED.
|
|
3011
|
+
*
|
|
3012
|
+
* Stored because a saved pool member can outlive its sign-in: once the desktop
|
|
3013
|
+
* app drops a login, that id resolves to no listed account and the card has
|
|
3014
|
+
* nothing left to render for it. The name was in hand at write time and nowhere
|
|
3015
|
+
* afterwards. Snapshots are never refreshed from a LISTING — only a real
|
|
3016
|
+
* measurement writes this.
|
|
3017
|
+
*/
|
|
3018
|
+
accountName?: string;
|
|
2094
3019
|
}
|
|
2095
|
-
|
|
3020
|
+
/** One account's live credit situation, as the pool ranks it. */
|
|
3021
|
+
interface TraePoolCredits {
|
|
3022
|
+
/** Remaining credit across every pack (`available` of the upstream snapshot). */
|
|
3023
|
+
total: number;
|
|
3024
|
+
/** The Work bucket, kept for display: SOLO chat does NOT spend it. */
|
|
3025
|
+
workAvailable?: number;
|
|
3026
|
+
/** The General bucket, kept for display: this is what SOLO chat spends. */
|
|
3027
|
+
generalAvailable?: number;
|
|
3028
|
+
/** Credits expiring within the card's "soon" window (3 days). */
|
|
3029
|
+
expiringSoon: number;
|
|
3030
|
+
/** When the nearest at-risk pack expires, if any does. */
|
|
3031
|
+
nearestExpiryMs?: number;
|
|
3032
|
+
}
|
|
3033
|
+
/**
|
|
3034
|
+
* Everything the ranking rule reads about one account. Assembled by the Host from
|
|
3035
|
+
* the credential store, the per-account credit read, and the runtime probe store
|
|
3036
|
+
* — this module only consumes it.
|
|
3037
|
+
*/
|
|
3038
|
+
interface TraePoolMember {
|
|
3039
|
+
account: TraePoolAccount;
|
|
3040
|
+
credits?: TraePoolCredits;
|
|
3041
|
+
probe?: TraePoolProbe;
|
|
3042
|
+
/** When the stored credential expires, used as the final tie-break. */
|
|
3043
|
+
tokenExpiresAtMs?: number;
|
|
3044
|
+
}
|
|
3045
|
+
/**
|
|
3046
|
+
* How one account answered the target model.
|
|
3047
|
+
*
|
|
3048
|
+
* The first nine values are the workbuddy taxonomy verbatim so the two cards read
|
|
3049
|
+
* the same. The tenth, `plan-gated`, is Trae-only: upstream `1005` says THIS
|
|
3050
|
+
* account's plan does not cover this model while another account's plan may, and
|
|
3051
|
+
* no amount of waiting changes a plan — so it must not be folded into
|
|
3052
|
+
* `credential-rejected` (which would demand a re-login) nor into
|
|
3053
|
+
* `policy-rejected` (which means a content filter tripped on ONE request).
|
|
3054
|
+
*/
|
|
3055
|
+
type TraePoolOutcome = 'ok' | 'rate-limited' | 'model-busy' | 'out-of-credit' | 'credential-rejected' | 'policy-rejected' | 'plan-gated' | 'unavailable' | 'not-found' | 'failed';
|
|
3056
|
+
//#endregion
|
|
3057
|
+
//#region src/model-probe.d.ts
|
|
3058
|
+
/** How one model answered. */
|
|
3059
|
+
type TraeProbeVerdict = 'available' | 'gated' | 'unsupported' | 'rate_limited' | 'auth' | 'unknown';
|
|
3060
|
+
interface TraeProbeResult {
|
|
3061
|
+
/** The model id as the card knows it. */
|
|
2096
3062
|
id: string;
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
}
|
|
2102
|
-
/** One probed candidate path and why it did not yield an account. */
|
|
2103
|
-
interface TraeWebSearchPath {
|
|
2104
|
-
path: string;
|
|
2105
|
-
edition: 'cn' | 'sg' | 'solo' | 'solo-sg';
|
|
2106
|
-
source: 'desktop' | 'cli';
|
|
2107
|
-
reason: 'missing' | 'unreadable' | 'invalid';
|
|
2108
|
-
message?: string;
|
|
2109
|
-
}
|
|
2110
|
-
/**
|
|
2111
|
-
* Peel ONE `{get(): T}` live reference, the shape DSH 0.1.7 delivers a
|
|
2112
|
-
* volatile-marked settings field as (see {@link unwrapVolatileDeep}).
|
|
2113
|
-
*/
|
|
2114
|
-
declare function unwrapVolatile<T>(value: T): T;
|
|
2115
|
-
/**
|
|
2116
|
-
* Deep copy of a value with every `{get(): T}` live reference replaced by the
|
|
2117
|
-
* value it resolves to.
|
|
2118
|
-
*
|
|
2119
|
-
* DSH 0.1.7 hands volatile-marked fields back as live references, and a live
|
|
2120
|
-
* reference is still `typeof === 'object'` — so it passes a naive object check
|
|
2121
|
-
* and every lookup on it is `undefined`. Worse, spreading one (`{ ...ref }`)
|
|
2122
|
-
* does NOT read the field: it produces `{ get: <function> }`. Any merge that
|
|
2123
|
-
* spreads a resolved field to preserve its siblings — the card's "write one
|
|
2124
|
-
* region, keep the other" write, or the account map's spread — would otherwise
|
|
2125
|
-
* DROP every sibling and leak a function into the document.
|
|
2126
|
-
*
|
|
2127
|
-
* Both halves therefore unwrap before touching a resolved field. This lives in
|
|
2128
|
-
* the node-free host↔client bridge because the browser half needs it too.
|
|
2129
|
-
*
|
|
2130
|
-
* Non-reference values are recursed into so a nested volatile field is caught
|
|
2131
|
-
* as well; arrays and objects are rebuilt rather than mutated, so the caller's
|
|
2132
|
-
* value is never touched.
|
|
2133
|
-
*/
|
|
2134
|
-
declare function unwrapVolatileDeep<T>(value: T): T;
|
|
2135
|
-
/**
|
|
2136
|
-
* Read the per-region account selections out of a settings value.
|
|
2137
|
-
*
|
|
2138
|
-
* SHARED because `accounts` now has TWO writers: the card, and the composer
|
|
2139
|
-
* panel's account table (2.14.0). Two writers of one field must use one merge
|
|
2140
|
-
* rule, or they drift — and the rule is not obvious.
|
|
2141
|
-
*
|
|
2142
|
-
* Deep-unwraps first: on DSH 0.1.7 a volatile field arrives as a `{get(): T}`
|
|
2143
|
-
* live reference, which passes the `typeof === 'object'` check and would be
|
|
2144
|
-
* returned as if it were the map. Every lookup on it is then `undefined`, and
|
|
2145
|
-
* the merge (`{ ...configured, [region]: id }`) would spread a reference into
|
|
2146
|
-
* `{get: <function>}` — dropping the other region's selection and leaking a
|
|
2147
|
-
* function into the settings document.
|
|
2148
|
-
*/
|
|
2149
|
-
declare function configuredAccountsOf(configured: unknown): Record<string, string>;
|
|
2150
|
-
/**
|
|
2151
|
-
* Whether the host-side "follow the Trae app's sign-in" switch is on (issue
|
|
2152
|
-
* #29). Absent reads as OFF, so an existing configuration keeps the strict
|
|
2153
|
-
* bind it shipped with and the switch is an opt-in — the same opt-out
|
|
2154
|
-
* semantics as {@link regionEnabledOf}, for the same reason: a config written
|
|
2155
|
-
* before this field existed cannot ask for behavior it never declared.
|
|
2156
|
-
*
|
|
2157
|
-
* Reads the whole settings section (or the bare field); deep-unwraps first
|
|
2158
|
-
* because on DSH 0.1.7 a volatile field arrives as a `{get(): T}` live
|
|
2159
|
-
* reference, whose `typeof === 'object'` would otherwise pass a naive check.
|
|
2160
|
-
*/
|
|
2161
|
-
declare function followAppLoginEnabled(configured: unknown): boolean;
|
|
3063
|
+
verdict: TraeProbeVerdict;
|
|
3064
|
+
/** Upstream's own words (already translated), or a short note. */
|
|
3065
|
+
detail: string;
|
|
3066
|
+
}
|
|
2162
3067
|
/**
|
|
2163
|
-
*
|
|
2164
|
-
*
|
|
2165
|
-
* is carried over untouched, so switching accounts never clobbers the other
|
|
2166
|
-
* region's picks. Tolerates any stored shape (absent, non-object) by starting
|
|
2167
|
-
* from an empty document.
|
|
3068
|
+
* How long one probe may take. Trae answers a one-word prompt in a few seconds;
|
|
3069
|
+
* beyond this the answer is not worth waiting for and is reported as unknown.
|
|
2168
3070
|
*/
|
|
2169
|
-
declare
|
|
3071
|
+
declare const TRAE_PROBE_TIMEOUT_MS = 30000;
|
|
2170
3072
|
/**
|
|
2171
|
-
*
|
|
2172
|
-
*
|
|
2173
|
-
* (and the pre-region-split flat fields, which never carry `enabled`) keep both
|
|
2174
|
-
* providers running exactly as before. The Host reads the same rule through
|
|
2175
|
-
* `regionStateOf`, so card and Host can never disagree about a region's state.
|
|
3073
|
+
* Classify a failure that arrived as a thrown stream error rather than a
|
|
3074
|
+
* returned result.
|
|
2176
3075
|
*
|
|
2177
|
-
*
|
|
2178
|
-
*
|
|
3076
|
+
* The bridge errors the stream (instead of returning a result) when a refusal
|
|
3077
|
+
* interrupts an answer that had already started; its message is the same
|
|
3078
|
+
* translated text, so a named code is still decisive. Anything without a
|
|
3079
|
+
* recognised code — an abort, a socket reset, a proxy — stays `unknown`.
|
|
2179
3080
|
*/
|
|
2180
|
-
declare function
|
|
3081
|
+
declare function classifyProbeError(message: string): {
|
|
3082
|
+
verdict: TraeProbeVerdict;
|
|
3083
|
+
detail: string;
|
|
3084
|
+
};
|
|
2181
3085
|
/**
|
|
2182
|
-
*
|
|
2183
|
-
* the target region's `enabled` flag changes: every other field of that slot
|
|
2184
|
-
* (its directory, selection, image opt-ins, context budgets) and every other
|
|
2185
|
-
* region's slot are carried over verbatim, so switching a provider off never
|
|
2186
|
-
* discards the user's model picks and switching it back on restores them.
|
|
2187
|
-
*
|
|
2188
|
-
* This is deliberately separate from {@link nextRegionSlots}: that helper
|
|
2189
|
-
* writes a whole slot from a signed-in tab's draft, while this one must work
|
|
2190
|
-
* for a region that is signed OUT — which is precisely the region a user wants
|
|
2191
|
-
* to switch off (no international install, no international account).
|
|
3086
|
+
* Classify a completed upstream call.
|
|
2192
3087
|
*
|
|
2193
|
-
* `
|
|
2194
|
-
*
|
|
2195
|
-
*
|
|
3088
|
+
* `result` is what `TraeSoloBridge.chatStream` returned, so a refusal that
|
|
3089
|
+
* arrived inside a 200 body has already been turned into a real error result
|
|
3090
|
+
* (and its code translated into `message`) by the bridge — this function only
|
|
3091
|
+
* has to read it back.
|
|
2196
3092
|
*/
|
|
2197
|
-
declare function
|
|
3093
|
+
declare function classifyProbeResult(result: TraeChatResult, outputSeen: boolean): {
|
|
3094
|
+
verdict: TraeProbeVerdict;
|
|
3095
|
+
detail: string;
|
|
3096
|
+
};
|
|
2198
3097
|
/**
|
|
2199
|
-
* The
|
|
2200
|
-
*
|
|
2201
|
-
* `composer` is the composer tool row (`conversation.input.left`); `stats` is
|
|
2202
|
-
* the ambient dock below the composer card (`conversation.composer.dock`),
|
|
2203
|
-
* beside the shell's own round/step/token pills.
|
|
3098
|
+
* The chat call one probe makes.
|
|
2204
3099
|
*
|
|
2205
|
-
*
|
|
2206
|
-
*
|
|
2207
|
-
*
|
|
2208
|
-
*
|
|
2209
|
-
*
|
|
2210
|
-
*
|
|
2211
|
-
*
|
|
3100
|
+
* `inputTokens` pads the user turn so the probe carries roughly as much text as
|
|
3101
|
+
* a real conversation. Size matters because an upstream that refuses LARGE
|
|
3102
|
+
* requests will happily answer a one-word probe, and a verdict earned by a
|
|
3103
|
+
* 1-token request says nothing about the 30k-token turn the user is about to
|
|
3104
|
+
* send. `0` keeps the historical minimal probe (one word) — that is the default
|
|
3105
|
+
* because it spends the least credit and answers the question the pool ranks on
|
|
3106
|
+
* ("can this account talk at all").
|
|
2212
3107
|
*
|
|
2213
|
-
*
|
|
2214
|
-
*
|
|
3108
|
+
* The filler is `x` words: one ASCII word is about one token, so the body size
|
|
3109
|
+
* is predictable (tokens ≈ inputTokens × 2 characters) without a tokenizer.
|
|
2215
3110
|
*/
|
|
2216
|
-
type TraeCreditPosition = 'composer' | 'stats';
|
|
2217
3111
|
/**
|
|
2218
|
-
*
|
|
3112
|
+
* The largest probe this plugin will pad.
|
|
2219
3113
|
*
|
|
2220
|
-
*
|
|
2221
|
-
* the
|
|
2222
|
-
*
|
|
2223
|
-
*
|
|
2224
|
-
* rendering nowhere is deliberate — an unrecognized value must not make the
|
|
2225
|
-
* readout disappear, which is the very defect this setting exists to fix.
|
|
2226
|
-
*
|
|
2227
|
-
* `value` may be the whole settings section or the `regions` map (see
|
|
2228
|
-
* {@link regionsMapOf}).
|
|
3114
|
+
* A 100k-token probe is already ~200 KB of JSON; anything above that is a request
|
|
3115
|
+
* the user's real turn would not plausibly reach in this product, and a config
|
|
3116
|
+
* file edited by hand must not turn one "test all" click into a multi-megabyte
|
|
3117
|
+
* upload.
|
|
2229
3118
|
*/
|
|
2230
|
-
declare
|
|
3119
|
+
declare const MAX_PROBE_INPUT_TOKENS = 100000;
|
|
3120
|
+
declare function probeBody(model: string, inputTokens?: number): string;
|
|
2231
3121
|
/**
|
|
2232
|
-
*
|
|
2233
|
-
*
|
|
2234
|
-
*
|
|
2235
|
-
|
|
2236
|
-
|
|
3122
|
+
* Probe one model through the bridge the plugin actually chats with, so the
|
|
3123
|
+
* recorded verdict describes the user's real path (same wire resolution, same
|
|
3124
|
+
* function stamping, same error translation).
|
|
3125
|
+
*/
|
|
3126
|
+
declare function probeOneModel(chat: (body: string, signal?: AbortSignal) => Promise<TraeChatResult>, id: string, timeoutMs?: number, inputTokens?: number): Promise<TraeProbeResult>;
|
|
3127
|
+
/**
|
|
3128
|
+
* Probe a list of models ONE AT A TIME.
|
|
2237
3129
|
*
|
|
2238
|
-
*
|
|
2239
|
-
*
|
|
2240
|
-
*
|
|
3130
|
+
* Sequential on purpose: Trae throttles bursts, and a burst of parallel probes
|
|
3131
|
+
* would manufacture the very `rate_limited`/`unknown` verdicts this feature
|
|
3132
|
+
* must avoid. An authentication failure stops the run — every later probe would
|
|
3133
|
+
* fail the same way, and spending calls to learn that again is waste.
|
|
2241
3134
|
*/
|
|
2242
|
-
declare function
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
isPayFreshman: boolean;
|
|
2248
|
-
inTrial: boolean;
|
|
2249
|
-
trialEndTimeMs: number;
|
|
2250
|
-
enableSoloLite: boolean;
|
|
2251
|
-
enableSoloBuilder: boolean;
|
|
2252
|
-
enableSoloCoder: boolean;
|
|
2253
|
-
enableSoloWeb: boolean;
|
|
2254
|
-
/** Trae's authoritative tier number (`user_pay_identity`); `0` means Free. */
|
|
2255
|
-
payIdentity?: number;
|
|
2256
|
-
/** Trae's authoritative tier label (`user_pay_identity_str`), e.g. `Free`. */
|
|
2257
|
-
payIdentityStr?: string;
|
|
2258
|
-
fission?: {
|
|
2259
|
-
startTimeMs: number;
|
|
2260
|
-
expireTimeMs: number;
|
|
2261
|
-
maxUsage: number;
|
|
2262
|
-
};
|
|
2263
|
-
}
|
|
2264
|
-
/** The JSON document the plugin card renders. */
|
|
2265
|
-
type TraeWebUsage = {
|
|
2266
|
-
status: 'signed-out';
|
|
2267
|
-
accounts: readonly TraeWebAccount[];
|
|
2268
|
-
message?: string;
|
|
2269
|
-
searched?: readonly TraeWebSearchPath[];
|
|
2270
|
-
enabled?: boolean;
|
|
2271
|
-
} | {
|
|
2272
|
-
status: 'signed-in';
|
|
3135
|
+
declare function probeModelsSequentially(chat: (body: string, signal?: AbortSignal) => Promise<TraeChatResult>, ids: readonly string[], onResult?: (result: TraeProbeResult, index: number) => void): Promise<TraeProbeResult[]>;
|
|
3136
|
+
//#endregion
|
|
3137
|
+
//#region src/account-pool-run.d.ts
|
|
3138
|
+
/** One account's check-in outcome. */
|
|
3139
|
+
interface TraePoolCheckinRow {
|
|
2273
3140
|
accountId: string;
|
|
2274
3141
|
accountName: string;
|
|
2275
|
-
tokenExpiresAtMs: number;
|
|
2276
|
-
/**
|
|
2277
|
-
* When the BOUND account's refresh token expires (absent when the credential
|
|
2278
|
-
* carries none). See {@link TraeWebAccount.refreshExpiresAtMs} — this is the
|
|
2279
|
-
* horizon that actually matters, because the access token above renews
|
|
2280
|
-
* itself until this one lapses.
|
|
2281
|
-
*/
|
|
2282
|
-
refreshExpiresAtMs?: number;
|
|
2283
|
-
/** Which per-region model directory and selection this account owns. */
|
|
2284
|
-
region: TraeRegion;
|
|
2285
|
-
/** Whether this region's provider is currently offered to DSH. */
|
|
2286
|
-
enabled?: boolean;
|
|
2287
|
-
accounts: readonly TraeWebAccount[];
|
|
2288
|
-
models: readonly TraeWebModel[];
|
|
2289
|
-
enabledModelIds: readonly string[];
|
|
2290
|
-
rawChat?: {
|
|
2291
|
-
state: 'disabled' | 'unchecked' | 'available' | 'protocol-gated' | 'authentication' | 'credit' | 'rate' | 'transport' | 'server';
|
|
2292
|
-
checkedAtMs?: number;
|
|
2293
|
-
status?: number;
|
|
2294
|
-
};
|
|
2295
|
-
credits?: TraeWebCredits;
|
|
2296
|
-
creditsError?: string;
|
|
2297
|
-
/**
|
|
2298
|
-
* Present only when the bound account is out of credit AND another account
|
|
2299
|
-
* on this machine has some (issue #25). The card then offers a one-click
|
|
2300
|
-
* switch instead of leaving a permanently empty panel. Absent in every
|
|
2301
|
-
* other case so a healthy account never pays for the extra read.
|
|
2302
|
-
*/
|
|
2303
|
-
alternatives?: readonly TraeWebCreditAlternative[];
|
|
2304
|
-
/**
|
|
2305
|
-
* Present only when the Trae app's CURRENT sign-in differs from the
|
|
2306
|
-
* account this plugin is bound to (issue #29): the card then offers a
|
|
2307
|
-
* one-click follow instead of making the user hunt the account dropdown
|
|
2308
|
-
* after every app-side sign-out. Absent in every other case — a healthy
|
|
2309
|
-
* document costs no extra read beyond the accounts scan the document
|
|
2310
|
-
* already runs.
|
|
2311
|
-
*/
|
|
2312
|
-
appLogin?: TraeWebAppLogin;
|
|
2313
3142
|
/**
|
|
2314
|
-
*
|
|
2315
|
-
*
|
|
2316
|
-
*
|
|
2317
|
-
*
|
|
3143
|
+
* - `claimed` — the upstream accepted today's claim for this account.
|
|
3144
|
+
* - `already` — this ACCOUNT's reward for today already exists, so nothing was sent.
|
|
3145
|
+
* - `device-already` — the MACHINE already spent today (9095). Not an account
|
|
3146
|
+
* failure and not a success: a truthful "no, this device is used up".
|
|
3147
|
+
* - `disabled` — the upstream's check-in activity is not enabled/active for
|
|
3148
|
+
* this account. Reported separately rather than as `failed`, because claiming
|
|
3149
|
+
* is impossible by policy and calling it an error would read as a broken
|
|
3150
|
+
* plugin.
|
|
3151
|
+
* - `failed` — the request never landed, or the credential could not be read.
|
|
2318
3152
|
*/
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
}
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
3153
|
+
status: 'claimed' | 'already' | 'device-already' | 'disabled' | 'failed';
|
|
3154
|
+
/** Credits the upstream says this claim carries, when it stated a figure. */
|
|
3155
|
+
credit?: number;
|
|
3156
|
+
/** The upstream's own words on failure/refusal, redacted by the caller. */
|
|
3157
|
+
message?: string;
|
|
3158
|
+
}
|
|
3159
|
+
/** One account's test outcome. */
|
|
3160
|
+
interface TraePoolTestRow {
|
|
3161
|
+
accountId: string;
|
|
3162
|
+
accountName: string;
|
|
3163
|
+
/** The model this row was probed on — one model per batch, recorded per row. */
|
|
3164
|
+
modelId: string;
|
|
3165
|
+
outcome: TraeProbeResult['verdict'];
|
|
3166
|
+
/** Upstream's own words / the plugin's reading of the code. */
|
|
3167
|
+
detail: string;
|
|
3168
|
+
/** A time the upstream named, when it named one. */
|
|
3169
|
+
retryAtMs?: number;
|
|
3170
|
+
}
|
|
2328
3171
|
//#endregion
|
|
2329
3172
|
//#region src/web-status.d.ts
|
|
2330
3173
|
/**
|
|
2331
|
-
* Constructor dependencies. Everything
|
|
2332
|
-
*
|
|
3174
|
+
* Constructor dependencies. Everything panel-specific is addressed by the
|
|
3175
|
+
* panel the request names: the two regions are separate provider stacks, so
|
|
2333
3176
|
* the card must be served the directory, selection, and credits of the tab the
|
|
2334
3177
|
* user is on.
|
|
2335
3178
|
*/
|
|
2336
3179
|
interface TraeUsageRouteOptions {
|
|
2337
|
-
/** The
|
|
2338
|
-
store(
|
|
2339
|
-
/** The
|
|
2340
|
-
client(
|
|
3180
|
+
/** The panel-scoped credential store backing each region's requests. */
|
|
3181
|
+
store(panel: TraePanel): TraeCredentialStore;
|
|
3182
|
+
/** The panel-scoped usage client (CN work credits / international pay status). */
|
|
3183
|
+
client(panel: TraePanel): TraeUsageClient;
|
|
2341
3184
|
/**
|
|
2342
3185
|
* CN credit balances of an account OTHER than the bound one, read without
|
|
2343
3186
|
* changing which account is bound (issue #25). Optional: when absent the
|
|
2344
3187
|
* exhausted-account hint is simply not offered.
|
|
2345
3188
|
*/
|
|
2346
|
-
creditsOfAccount?(
|
|
3189
|
+
creditsOfAccount?(panel: TraePanel, accountId: string): Promise<TraeWebCredits | undefined>;
|
|
2347
3190
|
/**
|
|
2348
3191
|
* Per-account status for one row of the card's account table: CN → check-in
|
|
2349
3192
|
* + balances, ai → subscription. Read without changing which account is
|
|
2350
3193
|
* bound. Optional: when absent the account rows show no check-in/credits/
|
|
2351
3194
|
* subscription figures (the table still lists each account and its switch).
|
|
2352
3195
|
*/
|
|
2353
|
-
accountStateOf?(
|
|
3196
|
+
accountStateOf?(panel: TraePanel, accountId: string): Promise<TraeWebAccountState | undefined>;
|
|
2354
3197
|
/**
|
|
2355
3198
|
* Claim today's check-in reward for ONE named account, without moving the
|
|
2356
3199
|
* binding. Optional: absent means the per-row claim button is disabled (the
|
|
2357
3200
|
* check-in surface simply is not offered).
|
|
2358
3201
|
*/
|
|
2359
|
-
claimCheckinOf?(
|
|
3202
|
+
claimCheckinOf?(panel: TraePanel, accountId: string): Promise<{
|
|
2360
3203
|
claim: TraeCheckinClaim;
|
|
2361
3204
|
checkin: TraeCheckinStatus;
|
|
2362
3205
|
} | undefined>;
|
|
@@ -2367,24 +3210,24 @@ interface TraeUsageRouteOptions {
|
|
|
2367
3210
|
* Read-only — the HOST never rebinds from this value; the follow is a
|
|
2368
3211
|
* settings write, made by the card or by the host's follow switch.
|
|
2369
3212
|
*/
|
|
2370
|
-
liveLogin?(
|
|
3213
|
+
liveLogin?(panel: TraePanel): Promise<TraeWebAppLogin | undefined>;
|
|
2371
3214
|
/** The requested region's last-refreshed raw directory (one entry per upstream model). */
|
|
2372
|
-
displayModels(
|
|
2373
|
-
/** The user's model selection in the requested
|
|
2374
|
-
enabledModelIds(
|
|
3215
|
+
displayModels(panel: TraePanel): readonly TraeModelInfo[];
|
|
3216
|
+
/** The user's model selection in the requested panel, stored as model id (= Trae name). */
|
|
3217
|
+
enabledModelIds(panel: TraePanel): readonly string[];
|
|
2375
3218
|
/**
|
|
2376
3219
|
* Whether the requested region's provider is currently offered to DSH. The
|
|
2377
3220
|
* card renders this as the tab's on/off checkbox, so the switch reflects the
|
|
2378
3221
|
* committed settings value rather than a local guess.
|
|
2379
3222
|
*/
|
|
2380
|
-
regionEnabled(
|
|
3223
|
+
regionEnabled(panel: TraePanel): boolean;
|
|
2381
3224
|
/** Re-read one region's live directory from the upstream. */
|
|
2382
|
-
discoverModels?(
|
|
3225
|
+
discoverModels?(panel: TraePanel, signal?: AbortSignal): Promise<readonly TraeModelInfo[]>;
|
|
2383
3226
|
/**
|
|
2384
3227
|
* Test the given model ids for availability, one real (minimal) chat call
|
|
2385
3228
|
* each. Present only when the region's chat path is wired.
|
|
2386
3229
|
*/
|
|
2387
|
-
probeModels?(
|
|
3230
|
+
probeModels?(panel: TraePanel, ids: readonly string[]): Promise<{
|
|
2388
3231
|
results: {
|
|
2389
3232
|
id: string;
|
|
2390
3233
|
verdict: string;
|
|
@@ -2392,78 +3235,87 @@ interface TraeUsageRouteOptions {
|
|
|
2392
3235
|
}[];
|
|
2393
3236
|
skipped?: string[];
|
|
2394
3237
|
}>;
|
|
2395
|
-
/** Raw-Chat capability state of the requested
|
|
2396
|
-
rawDiagnostic?(
|
|
3238
|
+
/** Raw-Chat capability state of the requested panel. */
|
|
3239
|
+
rawDiagnostic?(panel: TraePanel): TraeRawDiagnostic;
|
|
3240
|
+
/**
|
|
3241
|
+
* The account pool's host side, when the panel can run one. Optional on
|
|
3242
|
+
* purpose: the pool is an add-on to the status document, and a harness without
|
|
3243
|
+
* it (a test host, or a panel stack that failed to build) must still get a
|
|
3244
|
+
* complete usage answer with no `pool` field.
|
|
3245
|
+
*/
|
|
3246
|
+
pool?: TraePoolDeps;
|
|
3247
|
+
}
|
|
3248
|
+
/**
|
|
3249
|
+
* What the pool surface needs from the Host, addressed by panel.
|
|
3250
|
+
*
|
|
3251
|
+
* The split is deliberate. The card never learns a credential and never writes a
|
|
3252
|
+
* measurement: it sends an ACTION (`checkin` / `test` / `remove`) and reads the
|
|
3253
|
+
* ranking back out of the status document. Everything here is therefore either a
|
|
3254
|
+
* cheap local read (preferences, roster, saved names) or an explicitly user-
|
|
3255
|
+
* triggered spend (the batches). The one thing that must never happen on a GET is
|
|
3256
|
+
* an upstream call made for every row on every poll — the card already lazy-loads
|
|
3257
|
+
* per-account state, so `checkedInToday` is cached and skippable by design.
|
|
3258
|
+
*/
|
|
3259
|
+
interface TraePoolDeps {
|
|
3260
|
+
/** The panel's saved pool preferences, read from live settings. */
|
|
3261
|
+
preferences(panel: TraePanel): TraePoolPreferences;
|
|
3262
|
+
/**
|
|
3263
|
+
* The full pool membership WITH live credit reads — the expensive version.
|
|
3264
|
+
* Only the status document's own assembly and an explicit batch call it.
|
|
3265
|
+
*/
|
|
3266
|
+
members(panel: TraePanel): Promise<readonly TraePoolMember[]>;
|
|
3267
|
+
/**
|
|
3268
|
+
* The checked ids that still resolve to a local sign-in, from a local scan only.
|
|
3269
|
+
* The batch route's empty-pool guard runs this on every request, so it must not
|
|
3270
|
+
* cost an upstream call — {@link members} is not suitable there.
|
|
3271
|
+
*/
|
|
3272
|
+
effectiveMemberAccountIds?(panel: TraePanel): Promise<readonly string[]>;
|
|
3273
|
+
/** Last measured display name per saved id, for members whose sign-in is gone. */
|
|
3274
|
+
memberNames?(panel: TraePanel): Promise<Record<string, string>>;
|
|
3275
|
+
/** Local sign-ins the pool does NOT cover, so the card can offer them to check. */
|
|
3276
|
+
otherAccounts?(panel: TraePanel): Promise<readonly TraePoolAccount[]>;
|
|
3277
|
+
/** Which account the request path would bill right now, after applying the pool. */
|
|
3278
|
+
currentAccountId?(panel: TraePanel): Promise<string | undefined>;
|
|
3279
|
+
/** Whether this panel's upstream rewards a daily check-in at all. */
|
|
3280
|
+
checkinSupported?(panel: TraePanel): boolean;
|
|
3281
|
+
/**
|
|
3282
|
+
* Today's check-in state per account, from the cached per-account reads.
|
|
3283
|
+
*
|
|
3284
|
+
* A missing entry means "not read", which the card renders as unknown rather
|
|
3285
|
+
* than as a definite "not checked in". `deviceCheckedIn` is the Trae half of the
|
|
3286
|
+
* pair: the daily claim is keyed to `x-device-id`, so an account can be unpaid
|
|
3287
|
+
* TODAY because this machine already spent its claim on another account.
|
|
3288
|
+
*/
|
|
3289
|
+
checkinState?(panel: TraePanel): Promise<Record<string, TraePoolCheckinState>>;
|
|
3290
|
+
/** Run the check-in batch. Persists nothing to settings; drops its own caches. */
|
|
3291
|
+
checkin?(panel: TraePanel): Promise<readonly TraePoolCheckinRow[]>;
|
|
3292
|
+
/** Run the model-availability batch and persist its measurements. */
|
|
3293
|
+
test?(panel: TraePanel, modelId: string, onRow?: (row: TraePoolTestRow) => void): Promise<readonly TraePoolTestRow[]>;
|
|
3294
|
+
/** Probe ONE account against the target model (the row's own test button). */
|
|
3295
|
+
testOne?(panel: TraePanel, accountId: string): Promise<TraePoolTestRow | undefined>;
|
|
3296
|
+
/** Forget an account's measurement — used when the user removes it from the pool. */
|
|
3297
|
+
removeOne?(panel: TraePanel, accountId: string): Promise<boolean>;
|
|
3298
|
+
/** The panel's catalog, the source of the free-model default and the stale check. */
|
|
3299
|
+
catalog?(panel: TraePanel): readonly TraeModelInfo[];
|
|
3300
|
+
}
|
|
3301
|
+
/** One row's cached check-in facts; see {@link TraePoolDeps.checkinState}. */
|
|
3302
|
+
interface TraePoolCheckinState {
|
|
3303
|
+
checkedIn: boolean;
|
|
3304
|
+
deviceCheckedIn?: boolean;
|
|
3305
|
+
/** Whether this account is offered the reward at all; false renders `disabled`. */
|
|
3306
|
+
enabled?: boolean;
|
|
2397
3307
|
}
|
|
2398
3308
|
/**
|
|
2399
|
-
* Assemble one region's card document. `
|
|
2400
|
-
*
|
|
3309
|
+
* Assemble one region's card document. `panel` is the tab the card is on; the
|
|
3310
|
+
* panel-scoped store already answers with only that region's accounts, so the
|
|
2401
3311
|
* document's model slots and account list are that region's by construction.
|
|
2402
3312
|
* Sign-in state is read-only; credit is a live billing answer whose failure
|
|
2403
3313
|
* degrades to `creditsError` rather than failing the whole document.
|
|
2404
3314
|
*/
|
|
2405
|
-
declare function traeWebUsage(deps: TraeUsageRouteOptions,
|
|
3315
|
+
declare function traeWebUsage(deps: TraeUsageRouteOptions, panel: TraePanel): Promise<TraeWebUsage>;
|
|
2406
3316
|
/** Mount the GET usage route on an optional webServer context. */
|
|
2407
3317
|
declare function registerTraeUsageRoute(ctx: Context$1, deps: TraeUsageRouteOptions): void;
|
|
2408
3318
|
//#endregion
|
|
2409
|
-
//#region src/model-probe.d.ts
|
|
2410
|
-
/** How one model answered. */
|
|
2411
|
-
type TraeProbeVerdict = 'available' | 'gated' | 'unsupported' | 'rate_limited' | 'auth' | 'unknown';
|
|
2412
|
-
interface TraeProbeResult {
|
|
2413
|
-
/** The model id as the card knows it. */
|
|
2414
|
-
id: string;
|
|
2415
|
-
verdict: TraeProbeVerdict;
|
|
2416
|
-
/** Upstream's own words (already translated), or a short note. */
|
|
2417
|
-
detail: string;
|
|
2418
|
-
}
|
|
2419
|
-
/**
|
|
2420
|
-
* How long one probe may take. Trae answers a one-word prompt in a few seconds;
|
|
2421
|
-
* beyond this the answer is not worth waiting for and is reported as unknown.
|
|
2422
|
-
*/
|
|
2423
|
-
declare const TRAE_PROBE_TIMEOUT_MS = 30000;
|
|
2424
|
-
/**
|
|
2425
|
-
* Classify a failure that arrived as a thrown stream error rather than a
|
|
2426
|
-
* returned result.
|
|
2427
|
-
*
|
|
2428
|
-
* The bridge errors the stream (instead of returning a result) when a refusal
|
|
2429
|
-
* interrupts an answer that had already started; its message is the same
|
|
2430
|
-
* translated text, so a named code is still decisive. Anything without a
|
|
2431
|
-
* recognised code — an abort, a socket reset, a proxy — stays `unknown`.
|
|
2432
|
-
*/
|
|
2433
|
-
declare function classifyProbeError(message: string): {
|
|
2434
|
-
verdict: TraeProbeVerdict;
|
|
2435
|
-
detail: string;
|
|
2436
|
-
};
|
|
2437
|
-
/**
|
|
2438
|
-
* Classify a completed upstream call.
|
|
2439
|
-
*
|
|
2440
|
-
* `result` is what `TraeSoloBridge.chatStream` returned, so a refusal that
|
|
2441
|
-
* arrived inside a 200 body has already been turned into a real error result
|
|
2442
|
-
* (and its code translated into `message`) by the bridge — this function only
|
|
2443
|
-
* has to read it back.
|
|
2444
|
-
*/
|
|
2445
|
-
declare function classifyProbeResult(result: TraeChatResult, outputSeen: boolean): {
|
|
2446
|
-
verdict: TraeProbeVerdict;
|
|
2447
|
-
detail: string;
|
|
2448
|
-
};
|
|
2449
|
-
/** The minimal chat call one probe makes. */
|
|
2450
|
-
declare function probeBody(model: string): string;
|
|
2451
|
-
/**
|
|
2452
|
-
* Probe one model through the bridge the plugin actually chats with, so the
|
|
2453
|
-
* recorded verdict describes the user's real path (same wire resolution, same
|
|
2454
|
-
* function stamping, same error translation).
|
|
2455
|
-
*/
|
|
2456
|
-
declare function probeOneModel(chat: (body: string, signal?: AbortSignal) => Promise<TraeChatResult>, id: string, timeoutMs?: number): Promise<TraeProbeResult>;
|
|
2457
|
-
/**
|
|
2458
|
-
* Probe a list of models ONE AT A TIME.
|
|
2459
|
-
*
|
|
2460
|
-
* Sequential on purpose: Trae throttles bursts, and a burst of parallel probes
|
|
2461
|
-
* would manufacture the very `rate_limited`/`unknown` verdicts this feature
|
|
2462
|
-
* must avoid. An authentication failure stops the run — every later probe would
|
|
2463
|
-
* fail the same way, and spending calls to learn that again is waste.
|
|
2464
|
-
*/
|
|
2465
|
-
declare function probeModelsSequentially(chat: (body: string, signal?: AbortSignal) => Promise<TraeChatResult>, ids: readonly string[], onResult?: (result: TraeProbeResult, index: number) => void): Promise<TraeProbeResult[]>;
|
|
2466
|
-
//#endregion
|
|
2467
3319
|
//#region src/index.d.ts
|
|
2468
3320
|
declare const name = "dsh-connect-trae";
|
|
2469
3321
|
declare const inject: string[];
|
|
@@ -2535,6 +3387,15 @@ interface TraeRegionState {
|
|
|
2535
3387
|
* beside dsh-connect-workbuddy's rows — so the setting applies here too.
|
|
2536
3388
|
*/
|
|
2537
3389
|
creditPosition?: TraeCreditPosition;
|
|
3390
|
+
/**
|
|
3391
|
+
* This region's account pool (issue #44), as the card saves it.
|
|
3392
|
+
*
|
|
3393
|
+
* Read through {@link regionStateOf} and written by the card's pool section.
|
|
3394
|
+
* `enabled` absent means OFF: an account pool decides where money is spent, so
|
|
3395
|
+
* it is opt-in and a config written before it existed must keep billing the
|
|
3396
|
+
* account the user picked.
|
|
3397
|
+
*/
|
|
3398
|
+
pool?: TraePoolPreferences;
|
|
2538
3399
|
}
|
|
2539
3400
|
interface Config {
|
|
2540
3401
|
authFile?: string;
|
|
@@ -2550,7 +3411,7 @@ interface Config {
|
|
|
2550
3411
|
* Per-region account selections, keyed `cn` | `ai`. Each region's tab writes
|
|
2551
3412
|
* its own slot; tokens remain outside settings.
|
|
2552
3413
|
*/
|
|
2553
|
-
accounts?: Partial<Record<
|
|
3414
|
+
accounts?: Partial<Record<TraePanel, string>>;
|
|
2554
3415
|
/**
|
|
2555
3416
|
* Show the credit readout in the composer tool row: `Trae CN · <n>`, rendered
|
|
2556
3417
|
* ONLY while the session's selected model belongs to this plugin, clickable
|
|
@@ -2593,7 +3454,7 @@ interface Config {
|
|
|
2593
3454
|
* expose different rosters, so each keeps its own directory and selection
|
|
2594
3455
|
* and switching accounts never drops the other region's picks.
|
|
2595
3456
|
*/
|
|
2596
|
-
regions?: Partial<Record<
|
|
3457
|
+
regions?: Partial<Record<TraePanel, TraeRegionState>>;
|
|
2597
3458
|
/**
|
|
2598
3459
|
* @deprecated Legacy single-slot fields from before the region split. They
|
|
2599
3460
|
* predate international support and are read as the CN region's state when
|
|
@@ -2610,24 +3471,36 @@ interface Config {
|
|
|
2610
3471
|
models?: TraeModelInfo[];
|
|
2611
3472
|
}
|
|
2612
3473
|
/**
|
|
2613
|
-
* One
|
|
3474
|
+
* One panel's saved state. A config written before the region split has only
|
|
2614
3475
|
* the flat fields: those were always captured from the CN endpoint (the
|
|
2615
3476
|
* plugin had no international support), so they are read as the CN state and
|
|
2616
3477
|
* only when no explicit CN slot exists. The ai region never inherits them —
|
|
2617
3478
|
* that inheritance is exactly the bug where a stale CN directory would be
|
|
2618
3479
|
* intersected with the international catalog and silently drop the user's
|
|
2619
3480
|
* picks (the same failure workbuddy fixed with its region split).
|
|
3481
|
+
*
|
|
3482
|
+
* Keyed by PANEL (`cn` | `enterprise` | `ai`), because each panel carries its
|
|
3483
|
+
* own switch, account binding and model selection. `enterprise` has no legacy
|
|
3484
|
+
* equivalent: it starts from an EMPTY state (its own directory, its own model
|
|
3485
|
+
* picks) and never inherits CN's, so switching it on cannot silently activate
|
|
3486
|
+
* a model list the user never chose for the tenant pool.
|
|
2620
3487
|
*/
|
|
2621
|
-
declare function regionStateOf(config: Config,
|
|
3488
|
+
declare function regionStateOf(config: Config, panel: TraePanel): TraeRegionState;
|
|
2622
3489
|
/**
|
|
2623
|
-
* Whether one
|
|
3490
|
+
* Whether one panel's provider is switched on (issue #11). Opt-out semantics:
|
|
2624
3491
|
* only an explicit `false` disables it, so every config written before this
|
|
2625
3492
|
* switch existed — including the pre-region-split flat fields, which never
|
|
2626
3493
|
* carry `enabled` — keeps both providers running exactly as before. The card
|
|
2627
3494
|
* reads the same rule through `regionEnabledOf`, so the two halves can never
|
|
2628
3495
|
* disagree about a region's state.
|
|
3496
|
+
*
|
|
3497
|
+
* The ENTERPRISE panel is the one deliberate exception: it defaults OFF. A
|
|
3498
|
+
* consumer-only account (`tenant 7o2d894p7dr0o4`) cannot use that pool at all,
|
|
3499
|
+
* so opting every existing user into a second CN provider would put a route in
|
|
3500
|
+
* their model picker that fails on first use. It is switched on from its card
|
|
3501
|
+
* tab, by someone whose account actually belongs to a tenant.
|
|
2629
3502
|
*/
|
|
2630
|
-
declare function regionEnabled(config: Config,
|
|
3503
|
+
declare function regionEnabled(config: Config, panel: TraePanel): boolean;
|
|
2631
3504
|
/**
|
|
2632
3505
|
* WHERE one region's credit readout sits (learned from workbuddy #62).
|
|
2633
3506
|
*
|
|
@@ -2658,4 +3531,4 @@ declare function asVolatile<S>(schema: S): S;
|
|
|
2658
3531
|
declare const Config: z<Config>;
|
|
2659
3532
|
declare function apply(ctx: Context$1, config: Config): void;
|
|
2660
3533
|
//#endregion
|
|
2661
|
-
export { Config, FALLBACK_TRAE_MODELS, FALLBACK_TRAE_MODELS_AI, type PiAiGeneration, type PiAiRuntimeInfo, REGION_GATEWAYS, type RawChatDelta, type RawChatMessage, type RawChatTool, SseDecoder, type SseEvent, TRAE_ACCOUNT_STATUS_PATH, TRAE_AI_PROVIDER, TRAE_CHECKIN_PATH, TRAE_CN_AGENT_TASK_PATH, TRAE_CN_TITLE_PATH, TRAE_MODELS_TEST_PATH, TRAE_MODEL_DETAIL_FUNCTIONS, TRAE_MODEL_DETAIL_PATH, TRAE_PAY_BASE, TRAE_PROBE_TIMEOUT_MS, TRAE_PROVIDER, TRAE_PROVIDERS, TRAE_PROVIDER_DISPLAY_NAMES, TRAE_RAW_CHAT_V1_PATH, TRAE_RAW_CHAT_V2_PATH, TRAE_REASONING_EFFORTS, TRAE_REGIONS, TRAE_REGION_PARAM, TRAE_SETTINGS_NS, TRAE_SOLO_CHAT_PATH, TRAE_SOLO_FUNCTION, TRAE_SOLO_MODELS_PATH, TRAE_SOLO_REMOTE_BASE, TRAE_STATE_DB_FILENAME, TRAE_STREAM_IDLE_TIMEOUT_MS, TRAE_USAGE_PATH, TRAE_VERSION_CODE_FALLBACK, type TraeActivityRule, type TraeAdapter, type TraeCachedModelConfig, TraeCatalog, type TraeChatResult, type TraeCheckinClaim, type TraeCheckinStatus, type TraeContextBudget, type TraeCredential, TraeCredentialStore, TraeDelegatingUpstreamClient, type TraeDiscoveredModel, type TraeDiscoveredReasoning, type TraeEdition, TraeFallbackUpstreamClient, type TraeFallbackUpstreamOptions, type TraeFusionRawChatEnvelope, TraeGatedUpstreamClient, type TraeGatedUpstreamOptions, type TraeIdentity, type TraeInputModality, type TraeModelDetailRequest, type TraeModelInfo, type TraeModelProbeVerdict, type TraeModelsTestResponse, type TraeObservedModelConfig, type TraeProbeResult, type TraeProbeVerdict, TraeRawCapabilityController, type TraeRawCapabilityControllerOptions, type TraeRawCapabilitySnapshot, TraeRawCapabilityState, type TraeRawChatBehaviorConfig, type TraeRawChatCapability, type TraeRawChatClientOptions, type TraeRawChatConfig, type TraeRawChatFailureReason, type TraeRawChatRuntimeConfig, TraeRawChatUpstreamClient, type TraeRawDiagnostic, type TraeRawDiagnosticState, type TraeRawGateway, type TraeRawGatewayOptions, type TraeRawResolverResult, type TraeReasoningCapability, type TraeReasoningEffort, type TraeRefreshDevice, type TraeRegion, type TraeRegionGateways, TraeRegionState, type TraeShim, TraeSoloBridge, type TraeSoloClientOptions, TraeSoloRemoteCatalogClient, type TraeSoloRemoteCatalogOptions, TraeSoloUpstreamClient, type TraeStorageCandidate, type TraeStreamEvent, type TraeUpstreamClient, TraeUsageClient, type TraeUsageOptions, type TraeUsagePack, type TraeUsageRouteOptions, type TraeUsageSnapshot, type TraeUsageSummary, type TraeUsageView, type TraeWebAccountState, type TraeWebActivity, type TraeWebAppLogin, type TraeWebCheckin, type TraeWebCheckinClaim, type TraeWebCredits, type TraeWebUsage, type TraeWireModel, UnconfiguredTraeUpstreamClient, adaptLegacyPiAiContext, adaptModernPiAiContext, apply, applyContextBudgets, applyImageSelection, applyReasoningEffort, asVolatile, bridgeTraeSoloStream, buildTraeAgentTaskBody, buildTraeCnHeaders, buildTraeFusionRawChatEnvelope, buildTraeModelDetailRequest, buildTraeRawChatDraft, buildTraeRawChatRuntimeConfig, classifyProbeError, classifyProbeResult, classifyTraeRawChatFailure, configuredAccountsOf, createTraeAdapter, createTraeRawGateway, createTraeShim, creditPosition, decidePiAiGeneration, decodeRawChatChunk, decodeTraeEvent, decryptTraeStorageValue, deriveCatalog, describeIdShape, describeNameShape, discoveredCatalog, fallbackModelsFor, followAppLoginEnabled, generationOf, hashTraeRawChatArg, identityHeaders, inject, legacyTraeOwnAuthPath, maskUserPath, mergeTraeModelSources, name, nextRegionCreditPosition, nextRegionEnabled, nextRegionSlots, normalizeTraeCredential, normalizeTraeVersionCode, parseObservedModelConfig, parseReasoningCapability, parseTraeAuthValue, parseTraeCachedModel, parseTraeModelExtraConfigLogLine, parseTraeRawChatBehaviorConfig, parseTraeRemoteModel, parseTraeStorageDocument, piAiRuntimeInfo, pickTraeStorageIdentity, prepareSoloBody, probeBody, probeModelsSequentially, probeOneModel, probeTraeRawChatCapability, rawCapabilityDiagnostic, rawCapabilityFingerprint, readTraeCachedModel, readTraeCliIdentity, readTraeIdentity, refreshTraeCredential, regionCreditPositionOf, regionEnabled, regionEnabledOf, regionOfCredential, regionOfEdition, regionOfHost, regionOfTraeProvider, regionOfTraeStatusUrl, regionOfUserRegion, regionStateOf, registerTraeUsageRoute, resolveTraeIdentity, resolveTraeRawRuntime, sanitizeCatalog, settingsNamespaceOf, traeEndpoint, traeInputModalities, traeModelDisplayName, traeOwnAuthPath, traeRawChatExtraInfo, traeStateDatabaseCandidates, traeStorageCandidates, traeWebUsage, traeWindowsAppNames, unwrapVolatile, unwrapVolatileDeep, withContextAdaptation, withTraeRegion };
|
|
3534
|
+
export { Config, FALLBACK_TRAE_MODELS, FALLBACK_TRAE_MODELS_AI, MAX_PROBE_INPUT_TOKENS, type PiAiGeneration, type PiAiRuntimeInfo, REGION_GATEWAYS, type RawChatDelta, type RawChatMessage, type RawChatTool, SseDecoder, type SseEvent, TRAE_ACCOUNT_STATUS_PATH, TRAE_AI_PROVIDER, TRAE_CHECKIN_PATH, TRAE_CN_AGENT_TASK_PATH, TRAE_CN_TITLE_PATH, TRAE_ENTERPRISE_PROVIDER, TRAE_MODELS_TEST_PATH, TRAE_MODEL_DETAIL_FUNCTIONS, TRAE_MODEL_DETAIL_PATH, TRAE_PANELS, TRAE_PAY_BASE, TRAE_PROBE_TIMEOUT_MS, TRAE_PROVIDER, TRAE_PROVIDERS, TRAE_PROVIDER_DISPLAY_NAMES, TRAE_PROVIDER_IDS, TRAE_PROVIDER_ID_DISPLAY_NAMES, TRAE_PROVIDER_ROUTES, TRAE_RAW_CHAT_V1_PATH, TRAE_RAW_CHAT_V2_PATH, TRAE_REASONING_EFFORTS, TRAE_REGIONS, TRAE_REGION_PARAM, TRAE_SETTINGS_NS, TRAE_SOLO_CHAT_PATH, TRAE_SOLO_FUNCTION, TRAE_SOLO_MODELS_PATH, TRAE_SOLO_REMOTE_BASE, TRAE_STATE_DB_FILENAME, TRAE_STREAM_IDLE_TIMEOUT_MS, TRAE_USAGE_PATH, TRAE_VERSION_CODE_FALLBACK, type TraeActivityRule, type TraeAdapter, type TraeCachedModelConfig, TraeCatalog, type TraeChatResult, type TraeCheckinClaim, type TraeCheckinStatus, type TraeContextBudget, type TraeCredential, TraeCredentialStore, TraeDelegatingUpstreamClient, type TraeDiscoveredModel, type TraeDiscoveredReasoning, type TraeEdition, type TraeFailure, TraeFallbackUpstreamClient, type TraeFallbackUpstreamOptions, type TraeFusionRawChatEnvelope, TraeGatedUpstreamClient, type TraeGatedUpstreamOptions, type TraeIdentity, type TraeInputModality, type TraeModelDetailRequest, type TraeModelInfo, type TraeModelProbeVerdict, type TraeModelsTestResponse, type TraeObservedModelConfig, type TraePanel, type TraeProbeResult, type TraeProbeVerdict, type TraeProviderRoute, TraeRawCapabilityController, type TraeRawCapabilityControllerOptions, type TraeRawCapabilitySnapshot, TraeRawCapabilityState, type TraeRawChatBehaviorConfig, type TraeRawChatCapability, type TraeRawChatClientOptions, type TraeRawChatConfig, type TraeRawChatFailureReason, type TraeRawChatRuntimeConfig, TraeRawChatUpstreamClient, type TraeRawDiagnostic, type TraeRawDiagnosticState, type TraeRawGateway, type TraeRawGatewayOptions, type TraeRawResolverResult, type TraeReasoningCapability, type TraeReasoningEffort, type TraeRefreshDevice, type TraeRegion, type TraeRegionGateways, TraeRegionState, type TraeShim, TraeSoloBridge, type TraeSoloClientOptions, TraeSoloRemoteCatalogClient, type TraeSoloRemoteCatalogOptions, TraeSoloUpstreamClient, type TraeStorageCandidate, type TraeStreamEvent, type TraeUpstreamClient, TraeUsageClient, type TraeUsageOptions, type TraeUsagePack, type TraeUsageRouteOptions, type TraeUsageSnapshot, type TraeUsageSummary, type TraeUsageView, type TraeWebAccountState, type TraeWebActivity, type TraeWebAppLogin, type TraeWebCheckin, type TraeWebCheckinClaim, type TraeWebCredits, type TraeWebUsage, type TraeWireModel, UnconfiguredTraeUpstreamClient, adaptLegacyPiAiContext, adaptModernPiAiContext, apply, applyContextBudgets, applyImageSelection, applyReasoningEffort, asVolatile, bridgeTraeSoloStream, buildTraeAgentTaskBody, buildTraeCnHeaders, buildTraeFusionRawChatEnvelope, buildTraeModelDetailRequest, buildTraeRawChatDraft, buildTraeRawChatRuntimeConfig, classifyProbeError, classifyProbeResult, classifyTraeRawChatFailure, configuredAccountsOf, createTraeAdapter, createTraeRawGateway, createTraeShim, creditPosition, decidePiAiGeneration, decodeRawChatChunk, decodeTraeEvent, decryptTraeStorageValue, deriveCatalog, describeIdShape, describeNameShape, describeTraeFailure, discoveredCatalog, displayNameOfTraeProvider, fallbackModelsFor, followAppLoginEnabled, generationOf, hashTraeRawChatArg, identityHeaders, inject, isEnterpriseAccount, legacyTraeOwnAuthPath, maskUserPath, mergeTraeModelSources, name, nextRegionCreditPosition, nextRegionEnabled, nextRegionSlots, normalizeTraeCredential, normalizeTraeVersionCode, panelOfKey, panelOfProvider, parseObservedModelConfig, parseReasoningCapability, parseTraeAuthValue, parseTraeCachedModel, parseTraeModelExtraConfigLogLine, parseTraeRawChatBehaviorConfig, parseTraeRemoteModel, parseTraeStorageDocument, piAiRuntimeInfo, pickTraeStorageIdentity, prepareSoloBody, probeBody, probeModelsSequentially, probeOneModel, probeTraeRawChatCapability, providerOfPanel, rawCapabilityDiagnostic, rawCapabilityFingerprint, readTraeCachedModel, readTraeCliIdentity, readTraeIdentity, refreshTraeCredential, regionCreditPositionOf, regionEnabled, regionEnabledOf, regionOfCredential, regionOfEdition, regionOfHost, regionOfPanel, regionOfTraeProvider, regionOfTraeStatusUrl, regionOfUserRegion, regionStateOf, registerTraeUsageRoute, resolveTraeIdentity, resolveTraeRawRuntime, routeOfTraeProvider, sanitizeCatalog, settingsNamespaceOf, traeEndpoint, traeInputModalities, traeModelDisplayName, traeOwnAuthPath, traeRawChatExtraInfo, traeStateDatabaseCandidates, traeStorageCandidates, traeWebUsage, traeWindowsAppNames, unwrapVolatile, unwrapVolatileDeep, withContextAdaptation, withTraeRegion };
|