dsh-connect-workbuddy 4.0.0 → 4.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.d.ts CHANGED
@@ -116,6 +116,12 @@ interface WorkBuddyStoreOptions {
116
116
  * pre-dual-provider location. Injectable for tests.
117
117
  */
118
118
  legacyOwnPath?: string;
119
+ /**
120
+ * The unified credential vault directory for this store. Defaults to the
121
+ * per-region directory under the Harness home; injectable so the vault's
122
+ * merge/scan behaviour is testable without touching a real machine.
123
+ */
124
+ vaultDir?: string;
119
125
  /**
120
126
  * Auth directories to scan, overriding the platform defaults. Injectable so
121
127
  * the multi-account scan is testable without touching a real machine.
@@ -233,11 +239,36 @@ declare class WorkBuddyCredentialStore {
233
239
  private readonly ownPathExplicit;
234
240
  private readonly legacyOwnPath;
235
241
  private readonly legacyOwnPathExplicit;
242
+ private readonly vaultDirExplicit;
243
+ /** The vault directory, resolved ONCE at construction. */
244
+ private readonly vaultDirResolved;
236
245
  private readonly authDirs;
237
246
  private readonly resolveAtRestKey;
238
247
  private desktopPathOverride;
239
248
  private accountId;
240
249
  private inflight;
250
+ /**
251
+ * In-flight/completed scan for the CURRENT `readAll` generation.
252
+ *
253
+ * One page open calls `readAll` six times: the usage route asks for the
254
+ * account list, the sign-in state and the resolved credential, then the
255
+ * per-account credits panel asks once PER ACCOUNT (`credentialFor`), and both
256
+ * region tabs poll on the same timer. Each call re-read every auth file,
257
+ * re-parsed it, and re-ran the vault merge — measured at 6 `readdir` + (6N+12)
258
+ * `readFile` for N accounts, i.e. the cost grew with the account count on a
259
+ * path whose answer cannot change within one tick.
260
+ *
261
+ * This is deliberately NOT a time-based cache: a TTL would make the card show
262
+ * a stale account list after an OAuth add or an import, which is the very
263
+ * class of bug this card keeps chasing. Instead the promise is shared while it
264
+ * is in flight, and every WRITE clears it (see `invalidateScan`) — so a read
265
+ * after a write always scans again, and a read during a scan joins it.
266
+ *
267
+ * Undefined means "no scan in flight and nothing cached".
268
+ */
269
+ private scanInflight;
270
+ /** When the cached scan was STARTED, epoch ms; only meaningful with it set. */
271
+ private scanAtMs;
241
272
  /**
242
273
  * A RUNTIME account override from the pool's rotation, or undefined.
243
274
  *
@@ -268,6 +299,8 @@ declare class WorkBuddyCredentialStore {
268
299
  * the real machine's legacy copy.
269
300
  */
270
301
  private ownCandidates;
302
+ /** The vault directory this store reads and writes (fixed at construction). */
303
+ private vaultDir;
271
304
  /** Repoint the desktop file or directory; applies on the next read. */
272
305
  setDesktopPath(path: string | undefined): void;
273
306
  /**
@@ -320,7 +353,32 @@ declare class WorkBuddyCredentialStore {
320
353
  * region's accounts are invisible to selection, refresh, and status alike,
321
354
  * which is what keeps the two regions' providers from cross-billing.
322
355
  */
356
+ /**
357
+ * The account list, sharing one scan across concurrent and repeated reads.
358
+ *
359
+ * See {@link scanInflight}: the whole performance fix lives in these four
360
+ * lines. Every caller keeps its OWN semantics (`current`, `accounts`,
361
+ * `status`, `selectionLost`), because this returns the same array they used
362
+ * to build themselves — only the filesystem work is shared.
363
+ *
364
+ * A failed scan is NOT cached: the rejection clears the slot so the next read
365
+ * retries, rather than pinning every later caller to one transient failure.
366
+ */
323
367
  private readAll;
368
+ /** Drop the shared scan so the next read re-reads the filesystem. */
369
+ private invalidateScan;
370
+ /** The one real scan: read every candidate file, then merge the vault. */
371
+ private scanAll;
372
+ /**
373
+ * Merge the scan's credentials into the vault and read the vault back.
374
+ *
375
+ * Incremental by contract: a scan that finds nothing new writes nothing.
376
+ * Per account, the vault entry is written only when the scan's read is
377
+ * fresher by {@link isFresherVaultEntry} — so a steady-state scan is
378
+ * mtime-stable and every sync is O(accounts) reads with at most one write
379
+ * per CHANGED account.
380
+ */
381
+ private syncVault;
324
382
  /**
325
383
  * Default when no account is explicitly selected: the live sign-in, else the
326
384
  * freshest credential. Following the app's current sign-in is the documented
@@ -396,6 +454,79 @@ declare class WorkBuddyCredentialStore {
396
454
  private describeMissingCredential;
397
455
  /** Read-only sign-in summary; never refreshes and never throws. */
398
456
  status(): Promise<WorkBuddyAuthStatus>;
457
+ /**
458
+ * Persist one OAuth sign-in into the unified vault.
459
+ *
460
+ * The credential is written with `source: 'dsh'` (it never came from a
461
+ * desktop file) and joins the readable set on the next scan — the vault is
462
+ * read by `readAll()` like any other source, so no separate discovery path
463
+ * exists. The write is idempotent per account id: re-adding an account
464
+ * replaces its entry, which is exactly what re-issuing a login means.
465
+ *
466
+ * Returns the account id so the card can offer to select it right away.
467
+ */
468
+ addOAuthAccount(credential: Omit<WorkBuddyCredential, 'source' | 'filePath'>): Promise<string>;
469
+ /**
470
+ * Drop one id from the dismissal list, if present.
471
+ *
472
+ * Silent by design: called on the ADD paths, where a list that cannot be
473
+ * updated must not fail the action the user asked for. The consequence is the
474
+ * same as before the fix — the account stays hidden until the list is
475
+ * repaired — while throwing here would report a failed sign-in that succeeded.
476
+ */
477
+ private forgetDismissal;
478
+ /**
479
+ * Forget EVERY dismissal, so the next read lists the accounts again.
480
+ *
481
+ * Called by the manual 「re-scan」 action. Dismissal is "hide until I ask to
482
+ * scan again", and this is that ask: the user is requesting what the machine
483
+ * currently has, which includes accounts they hid earlier. Leaving the list
484
+ * intact would make a removed desktop account unrecoverable from the UI — the
485
+ * opposite of what the button promises.
486
+ *
487
+ * PREFERS deleting the file over writing an empty list: an absent file is the
488
+ * normal state and keeps the vault directory free of a file that says nothing.
489
+ * The unlink is not the only way, though, because on Windows removing a file
490
+ * another process holds open fails with `EPERM`/`EBUSY`/`EACCES` — an antivirus
491
+ * scanner, OneDrive, or an editor with it open, the same contention the card's
492
+ * `isFileContentionWriteError` classifies for profile writes and the reason
493
+ * `docs/WINDOWS.md` keeps a register. POSIX unlinks open files happily, so
494
+ * without this the promise 「点重新检测账号即可找回」 would be false, silently,
495
+ * on exactly the platform it matters most on.
496
+ *
497
+ * So the unlink falls back to OVERWRITING the list with an empty one through
498
+ * the same locked atomic write `writeDismissed` uses. For every reader the two
499
+ * outcomes are identical (`readDismissed` yields no ids either way), so the
500
+ * user's ask is honoured even when the file itself cannot be removed.
501
+ *
502
+ * Returns whether the dismissals are now genuinely gone, VERIFIED by reading
503
+ * the list back rather than by trusting the write: the caller renders this, so
504
+ * "it worked" has to mean it. A `false` is the honest answer for a vault that
505
+ * could neither be unlinked nor rewritten, and the rescan still runs — the
506
+ * accounts simply stay hidden, which the UI can now say out loud instead of
507
+ * showing a button that appears to have done nothing.
508
+ */
509
+ clearDismissals(): Promise<boolean>;
510
+ /**
511
+ * Remove ONE account from this region's list, by id.
512
+ *
513
+ * The account is DISMISSED, not merely deleted. Deleting the vault entry alone
514
+ * was undone by the next scan: `syncVault` writes every desktop sign-in it
515
+ * finds back into the vault (that is how a desktop account becomes selectable)
516
+ * and `readAll` merges the vault in afterwards, so a removed desktop account
517
+ * returned on the very next read while `removeAccount` had reported `true`.
518
+ * Measured before the fix: remove → `true`, then `accounts()` still lists it.
519
+ *
520
+ * The desktop app's auth files stay read-only: dismissing hides the account
521
+ * from this plugin without signing the app out, and that is exactly what the
522
+ * card says. Re-adding it (a fresh OAuth sign-in or an import) clears the
523
+ * dismissal, so the account is not lost to a future login.
524
+ *
525
+ * Returns whether a stored vault entry was also removed, so the caller can
526
+ * still tell "removed" from "only a desktop sign-in" — the dismissal happens
527
+ * either way, because only the user gets to decide an account is dismissed.
528
+ */
529
+ removeAccount(accountId: string): Promise<boolean>;
399
530
  /**
400
531
  * Remove every plugin-owned copy this store could read (per-region file,
401
532
  * legacy single file, and their lock siblings); the desktop files are
@@ -406,7 +537,6 @@ declare class WorkBuddyCredentialStore {
406
537
  logout(): Promise<void>;
407
538
  private needsRefresh;
408
539
  private refreshNow;
409
- private saveOwn;
410
540
  /**
411
541
  * Every readable plugin-owned copy, in candidate order; absent or corrupt
412
542
  * files are skipped rather than propagated.
@@ -578,6 +708,30 @@ interface UpstreamErrorDetail {
578
708
  * them at the openresty layer with an HTML 401.
579
709
  */
580
710
  declare function regionOf(domain: string): WorkBuddyRegion;
711
+ /**
712
+ * The STRICT counterpart of {@link regionOf}: a region's domain only when it is
713
+ * positively recognised, `undefined` for anything else.
714
+ *
715
+ * `regionOf`'s catch-all is deliberate — the CN product is single-homed, so an
716
+ * unrecognised spelling is treated as CN and a real (if oddly written) CN
717
+ * credential still resolves. That reasoning holds only for a domain the
718
+ * UPSTREAM issued. It does not hold for a value read out of a file someone
719
+ * handed the plugin: there "unrecognised" means "cannot be classified", and
720
+ * calling it CN would file a foreign or spoofed credential in the CN vault.
721
+ * Import gates must use this, not {@link regionOf}.
722
+ *
723
+ * Both sides are matched on their registrable domain, so a bare host and any
724
+ * subdomain qualify while a lookalike such as `codebuddy.cn.evil.com` cannot.
725
+ *
726
+ * `copilot.tencent.com` is NOT a third product — it is this plugin's OWN CN
727
+ * chat gateway (`CN_CHAT_BASE`), and the credential's `domain` field carries it
728
+ * verbatim on real records. Refusing it here made the plugin reject its own
729
+ * export: the card exported 8 accounts, and re-importing that file skipped the
730
+ * three whose `domain` was the gateway. It cannot be spoofed into the wrong
731
+ * region by being recognised here — it names the CN gateway, so admitting it is
732
+ * the same decision as admitting `workbuddy.cn`.
733
+ */
734
+ declare function knownRegionOf(domain: string): WorkBuddyRegion | undefined;
581
735
  /**
582
736
  * Normalize an OpenAI chat-completions body for the WorkBuddy upstream:
583
737
  * force `stream: true` (the upstream rejects non-streaming), flatten
@@ -1778,6 +1932,59 @@ declare const WORKBUDDY_CHECKIN_PATH = "/plugins/dsh-connect-workbuddy/checkin";
1778
1932
  * browser half.
1779
1933
  */
1780
1934
  declare const WORKBUDDY_PROBE_PATH = "/plugins/dsh-connect-workbuddy/probe";
1935
+ /**
1936
+ * One entry of the transfer PREVIEW: display fields only, never token
1937
+ * material. `hasToken` is the import decision input — the import step skips
1938
+ * records without one — and the indexes are the FILE's positions, so the user
1939
+ * picks by the same numbers the import call sends back.
1940
+ */
1941
+ interface WorkBuddyTransferPreviewEntry {
1942
+ index: number;
1943
+ uid: string;
1944
+ nickname: string;
1945
+ email: string;
1946
+ hasToken: boolean;
1947
+ }
1948
+ /** The preview answer: the desensitized listing plus the file's total count. */
1949
+ interface WorkBuddyTransferPreview {
1950
+ accounts: readonly WorkBuddyTransferPreviewEntry[];
1951
+ total: number;
1952
+ }
1953
+ /**
1954
+ * The import answer. `imported` counts records merged into the vault (new OR
1955
+ * overwritten — both change the stored entry); `skipped` counts what was
1956
+ * left out: missing token, a bad index, or a record whose login domain does
1957
+ * not belong to the addressed region.
1958
+ */
1959
+ interface WorkBuddyTransferImportAnswer {
1960
+ imported: number;
1961
+ skipped: number;
1962
+ }
1963
+ /**
1964
+ * The shared file format's record shape, subset the importer consumes.
1965
+ *
1966
+ * Contract with sibling tools exporting the same format: a JSON array of
1967
+ * objects keyed in snake_case — `access_token` (required), `refresh_token`,
1968
+ * `uid`, `nickname`, `email`, `domain`, `expiresAt` (epoch ms),
1969
+ * `refreshExpiresAt`, `auth_raw`. Extra fields ride along untouched.
1970
+ */
1971
+ interface WorkBuddyTransferRecord {
1972
+ access_token: string;
1973
+ refresh_token?: string;
1974
+ uid?: string;
1975
+ nickname?: string;
1976
+ email?: string;
1977
+ domain?: string;
1978
+ /** Access-token expiry in epoch MILLISECONDS, the sibling format's unit. */
1979
+ expiresAt?: number;
1980
+ refreshExpiresAt?: number;
1981
+ /** The sibling tool's original credential document, preserved verbatim. */
1982
+ auth_raw?: unknown;
1983
+ }
1984
+ /** The export answer: the records themselves, tokens included. */
1985
+ interface WorkBuddyTransferExportAnswer {
1986
+ accounts: WorkBuddyTransferRecord[];
1987
+ }
1781
1988
  /** Query parameter naming the region a card request addresses. */
1782
1989
  declare const WORKBUDDY_REGION_PARAM = "region";
1783
1990
  /** Every region, in card tab order. */
@@ -2410,7 +2617,79 @@ interface WorkBuddyStatusRouteOptions {
2410
2617
  * that needs a working probe dependency.
2411
2618
  */
2412
2619
  pool?: WorkBuddyPoolDeps;
2620
+ /**
2621
+ * OAuth QR sign-in: start a login and poll it to completion.
2622
+ *
2623
+ * Optional as a whole: a Host built without it simply omits the card's
2624
+ * sign-in affordance (an older bundle keeps rendering, minus the button).
2625
+ */
2626
+ oauth?: WorkBuddyOAuthDeps;
2627
+ /**
2628
+ * Batch credential export / import between machines.
2629
+ *
2630
+ * Optional as a whole: a Host built without it simply omits the card's
2631
+ * transfer section (an older bundle keeps rendering, minus the buttons).
2632
+ */
2633
+ transfer?: WorkBuddyTransferDeps;
2634
+ }
2635
+ /**
2636
+ * What the transfer route needs from the Host. Everything is region-scoped:
2637
+ * the two regions' vaults are parallel stacks, and an export of one tab must
2638
+ * never leak the other tab's accounts.
2639
+ */
2640
+ interface WorkBuddyTransferDeps {
2641
+ /**
2642
+ * Export the named accounts' credentials in the shared transfer format.
2643
+ * Unknown ids are simply absent from the answer; an answer with zero
2644
+ * records means nothing named was exportable (the caller decides whether
2645
+ * that is an error).
2646
+ */
2647
+ exportAccounts(region: WorkBuddyRegion, accountIds: readonly string[]): Promise<WorkBuddyTransferExportAnswer>;
2648
+ /** Parse transfer-file text into the desensitized preview. */
2649
+ previewImport(region: WorkBuddyRegion, text: string): Promise<WorkBuddyTransferPreview>;
2650
+ /**
2651
+ * Merge the selected file entries into the region's vault. Returns the
2652
+ * counters the card reports; `skipped` covers missing tokens, bad indexes,
2653
+ * and records belonging to the other region.
2654
+ */
2655
+ importAccounts(region: WorkBuddyRegion, text: string, indexes: readonly number[]): Promise<WorkBuddyTransferImportAnswer>;
2413
2656
  }
2657
+ /**
2658
+ * What the OAuth route needs from the Host: the two session actions, plus the
2659
+ * persistence step the route cannot do itself (only the Host may touch the
2660
+ * credential vault).
2661
+ */
2662
+ interface WorkBuddyOAuthDeps {
2663
+ /**
2664
+ * Start one login for the region. Resolves the URL the user opens to scan
2665
+ * and confirm, or an actionable error.
2666
+ */
2667
+ start(region: WorkBuddyRegion): Promise<WorkBuddyOAuthStartResult>;
2668
+ /**
2669
+ * Poll once. The card repeats until `done` — then either the account was
2670
+ * persisted (the Host saved it to the vault) or an error is reported.
2671
+ */
2672
+ poll(region: WorkBuddyRegion, loginId: string): Promise<WorkBuddyOAuthPollView>;
2673
+ }
2674
+ /** The start answer, token-free (the state never leaves the Host). */
2675
+ type WorkBuddyOAuthStartResult = {
2676
+ loginId: string;
2677
+ verificationUri: string;
2678
+ expiresIn: number;
2679
+ } | {
2680
+ error: string;
2681
+ };
2682
+ /** The poll answer the card sees. `account` carries display fields only. */
2683
+ type WorkBuddyOAuthPollView = {
2684
+ done: false;
2685
+ } | {
2686
+ done: true;
2687
+ accountName: string;
2688
+ accountId: string;
2689
+ } | {
2690
+ done: true;
2691
+ error: string;
2692
+ };
2414
2693
  /**
2415
2694
  * What the pool route needs from the Host.
2416
2695
  *
@@ -2444,6 +2723,22 @@ interface WorkBuddyPoolDeps {
2444
2723
  * checked) instead of passing the guard and running a batch over nothing.
2445
2724
  */
2446
2725
  effectiveMemberAccountIds?(region: WorkBuddyRegion): Promise<readonly string[]>;
2726
+ /**
2727
+ * Test ONE account against the region's target model.
2728
+ *
2729
+ * The per-row button's Host half. Optional: a Host built without it simply
2730
+ * omits the row button (an older bundle keeps rendering, minus the control).
2731
+ * Returns undefined when the region cannot name a target model — the same
2732
+ * refusal the batch route reports as 409.
2733
+ */
2734
+ testOne?(region: WorkBuddyRegion, accountId: string): Promise<WorkBuddyPoolTestRow | undefined>;
2735
+ /**
2736
+ * Forget ONE account's plugin-stored credential. Only the vault entry is
2737
+ * removed; the desktop app's files are never touched. Returns whether an
2738
+ * entry existed — false means the account exists only as a desktop sign-in
2739
+ * (or was already gone), which the card reports instead of a silent no-op.
2740
+ */
2741
+ removeOne?(region: WorkBuddyRegion, accountId: string): Promise<boolean>;
2447
2742
  /**
2448
2743
  * Today's check-in state per account id.
2449
2744
  *
@@ -2772,4 +3067,4 @@ declare function legacyAttributionRegion(value: Config, accountsFor: (region: Wo
2772
3067
  */
2773
3068
  declare function apply(ctx: Context, config: Config): void;
2774
3069
  //#endregion
2775
- export { CREDENTIAL_REJECTED_CODE, Config, DEFAULT_PROBE_INPUT_TOKENS, ENCRYPTED_CREDENTIAL_CODE, FALLBACK_WORKBUDDY_MODELS, FALLBACK_WORKBUDDY_MODELS_GLOBAL, NATIVE_MODALITY_BY_MODEL_ID, PROBE_INPUT_TOKEN_CHOICES, PROBE_MAX_TOKENS, PROBE_SYSTEM_PROMPT, REFUSES_OFF_MODEL_IDS, type UpstreamErrorKind, WORKBUDDY_ACCOUNTS_REFRESH_PATH, WORKBUDDY_APP_EXECUTABLE_ENV, WORKBUDDY_AUTH_FILENAME, WORKBUDDY_AUTH_FILE_ENV, WORKBUDDY_CHECKIN_PATH, WORKBUDDY_CONNECT_VERSION, WORKBUDDY_GLOBAL_PROVIDER, WORKBUDDY_HOST_HEARTBEAT_FILENAME, WORKBUDDY_MODELS_REFRESH_PATH, WORKBUDDY_PROBE_PATH, WORKBUDDY_PROVIDER, WORKBUDDY_PROVIDERS, WORKBUDDY_PROVIDER_DISPLAY_NAMES, WORKBUDDY_REGIONS, WORKBUDDY_REGION_PARAM, WORKBUDDY_SETTINGS_NS, WORKBUDDY_STREAM_IDLE_TIMEOUT_MS, WORKBUDDY_USAGE_PATH, type WorkBuddyAccountChoice, type WorkBuddyAdapter, type WorkBuddyAuthStatus, type WorkBuddyCandidateFailure, WorkBuddyCatalog, type WorkBuddyChatResult, type WorkBuddyCredential, type WorkBuddyCredentialRecovery, WorkBuddyCredentialRejectedError, WorkBuddyCredentialStore, type WorkBuddyCreditPackage, type WorkBuddyCredits, WorkBuddyEncryptedCredentialError, type WorkBuddyEncryptedField, type WorkBuddyHostHeartbeat, type WorkBuddyModelInfo, type WorkBuddyNativeModality, WorkBuddyPoolPreferences, type WorkBuddyProbeClient, type WorkBuddyProbeOutcome, type WorkBuddyProbeResult, type WorkBuddyProbeStore, type WorkBuddyReasoning, type WorkBuddyRecoveryCandidate, type WorkBuddyRefreshOutcome, WorkBuddyRegionState, type WorkBuddyShim, type WorkBuddyStatusRouteOptions, type WorkBuddyStoreOptions, WorkBuddyUpstreamClient, type WorkBuddyUpstreamModel, type WorkBuddyUsabilityProbeOptions, apply, asVolatile, authFileName, classifyUpstreamError, clearAtRestKeyCache, clearHostHeartbeat, cooldownOf, createAccountUsabilityProbe, createWorkBuddyAdapter, createWorkBuddyShim, creditOfStream, defaultDesktopAuthCandidates, defaultDesktopAuthDirs, defaultDesktopAuthPath, defaultOffModelIds, deriveAtRestKey, deriveAtRestKeyId, deriveCatalog, fallbackModelsFor, fetchAtRestKeyPayload, findWorkbuddyAppExecutable, hasEncryptedCredentialFields, imageDefaultFor, inject, isCredentialRejectedError, isEncryptedCredentialError, isEncryptedFieldWrapper, isHeartbeatProcessAlive, isWorkbuddyBundle, legacyAttributionRegion, legacyWorkbuddyOwnAuthPath, macosBundleExecutable, macosNestedAppBundles, name, nativeModalityOf, nextRegionEnabled, nextRegionSlots, offDefaultFor, openEncryptedField, outcomeOfFailure, parseCreditMultiplier, parseReasoning, parseRetryAfter, parseUpstreamModel, parseWorkBuddyAuth, prepareChatBody, probeModel, probeRequestBody, probeSucceeded, processStartTimeMs, readAtRestKey, readHostHeartbeat, regionCleared, regionEnabled, regionEnabledOf, regionOf, regionOfProvider, regionOfStatusUrl, regionStateOf, registerWorkBuddyStatusRoute, resolveCredentialRecovery, selectAccountFor, settingsNamespaceOf, toPersistedWorkBuddyModel, unwrapVolatileDeep, withEffectiveOff, withWorkBuddyRegion, workBuddyDisplayName, workBuddyModelInput, workBuddyThinkingLevelMap, workBuddyWebStatus, workbuddyAccountId, workbuddyAppExecutableCandidates, workbuddyHostHeartbeatPath, workbuddyOwnAuthPath, writeHostHeartbeat };
3070
+ export { CREDENTIAL_REJECTED_CODE, Config, DEFAULT_PROBE_INPUT_TOKENS, ENCRYPTED_CREDENTIAL_CODE, FALLBACK_WORKBUDDY_MODELS, FALLBACK_WORKBUDDY_MODELS_GLOBAL, NATIVE_MODALITY_BY_MODEL_ID, PROBE_INPUT_TOKEN_CHOICES, PROBE_MAX_TOKENS, PROBE_SYSTEM_PROMPT, REFUSES_OFF_MODEL_IDS, type UpstreamErrorKind, WORKBUDDY_ACCOUNTS_REFRESH_PATH, WORKBUDDY_APP_EXECUTABLE_ENV, WORKBUDDY_AUTH_FILENAME, WORKBUDDY_AUTH_FILE_ENV, WORKBUDDY_CHECKIN_PATH, WORKBUDDY_CONNECT_VERSION, WORKBUDDY_GLOBAL_PROVIDER, WORKBUDDY_HOST_HEARTBEAT_FILENAME, WORKBUDDY_MODELS_REFRESH_PATH, WORKBUDDY_PROBE_PATH, WORKBUDDY_PROVIDER, WORKBUDDY_PROVIDERS, WORKBUDDY_PROVIDER_DISPLAY_NAMES, WORKBUDDY_REGIONS, WORKBUDDY_REGION_PARAM, WORKBUDDY_SETTINGS_NS, WORKBUDDY_STREAM_IDLE_TIMEOUT_MS, WORKBUDDY_USAGE_PATH, type WorkBuddyAccountChoice, type WorkBuddyAdapter, type WorkBuddyAuthStatus, type WorkBuddyCandidateFailure, WorkBuddyCatalog, type WorkBuddyChatResult, type WorkBuddyCredential, type WorkBuddyCredentialRecovery, WorkBuddyCredentialRejectedError, WorkBuddyCredentialStore, type WorkBuddyCreditPackage, type WorkBuddyCredits, WorkBuddyEncryptedCredentialError, type WorkBuddyEncryptedField, type WorkBuddyHostHeartbeat, type WorkBuddyModelInfo, type WorkBuddyNativeModality, WorkBuddyPoolPreferences, type WorkBuddyProbeClient, type WorkBuddyProbeOutcome, type WorkBuddyProbeResult, type WorkBuddyProbeStore, type WorkBuddyReasoning, type WorkBuddyRecoveryCandidate, type WorkBuddyRefreshOutcome, WorkBuddyRegionState, type WorkBuddyShim, type WorkBuddyStatusRouteOptions, type WorkBuddyStoreOptions, WorkBuddyUpstreamClient, type WorkBuddyUpstreamModel, type WorkBuddyUsabilityProbeOptions, apply, asVolatile, authFileName, classifyUpstreamError, clearAtRestKeyCache, clearHostHeartbeat, cooldownOf, createAccountUsabilityProbe, createWorkBuddyAdapter, createWorkBuddyShim, creditOfStream, defaultDesktopAuthCandidates, defaultDesktopAuthDirs, defaultDesktopAuthPath, defaultOffModelIds, deriveAtRestKey, deriveAtRestKeyId, deriveCatalog, fallbackModelsFor, fetchAtRestKeyPayload, findWorkbuddyAppExecutable, hasEncryptedCredentialFields, imageDefaultFor, inject, isCredentialRejectedError, isEncryptedCredentialError, isEncryptedFieldWrapper, isHeartbeatProcessAlive, isWorkbuddyBundle, knownRegionOf, legacyAttributionRegion, legacyWorkbuddyOwnAuthPath, macosBundleExecutable, macosNestedAppBundles, name, nativeModalityOf, nextRegionEnabled, nextRegionSlots, offDefaultFor, openEncryptedField, outcomeOfFailure, parseCreditMultiplier, parseReasoning, parseRetryAfter, parseUpstreamModel, parseWorkBuddyAuth, prepareChatBody, probeModel, probeRequestBody, probeSucceeded, processStartTimeMs, readAtRestKey, readHostHeartbeat, regionCleared, regionEnabled, regionEnabledOf, regionOf, regionOfProvider, regionOfStatusUrl, regionStateOf, registerWorkBuddyStatusRoute, resolveCredentialRecovery, selectAccountFor, settingsNamespaceOf, toPersistedWorkBuddyModel, unwrapVolatileDeep, withEffectiveOff, withWorkBuddyRegion, workBuddyDisplayName, workBuddyModelInput, workBuddyThinkingLevelMap, workBuddyWebStatus, workbuddyAccountId, workbuddyAppExecutableCandidates, workbuddyHostHeartbeatPath, workbuddyOwnAuthPath, writeHostHeartbeat };