@eddyskywalker/dsh-chatgpt-subscription 0.46.1 → 0.48.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.
Files changed (104) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/README.md +22 -1
  3. package/lib/client.js +2639 -571
  4. package/lib/client.js.map +1 -1
  5. package/lib/index.js +15598 -5902
  6. package/lib/plan-mode-pipeline.js +419 -0
  7. package/lib/plan-mode.js +783 -0
  8. package/lib/tool-result-pruner.js +400 -0
  9. package/lib/types/client/hub/brand-icons.d.ts +1 -1
  10. package/lib/types/client/hub/brand-icons.d.ts.map +1 -1
  11. package/lib/types/client/hub/brand-svg.d.ts +4 -0
  12. package/lib/types/client/hub/brand-svg.d.ts.map +1 -1
  13. package/lib/types/client/hub/providers.d.ts.map +1 -1
  14. package/lib/types/client/index.d.ts +2 -0
  15. package/lib/types/client/index.d.ts.map +1 -1
  16. package/lib/types/client/locales-ru/index.d.ts.map +1 -1
  17. package/lib/types/client/locales-ru/progress-updates.d.ts.map +1 -1
  18. package/lib/types/client/locales-ru/workbuddy.d.ts +10 -0
  19. package/lib/types/client/locales-ru/workbuddy.d.ts.map +1 -0
  20. package/lib/types/client/progress-updates/locales.d.ts +23 -5
  21. package/lib/types/client/progress-updates/locales.d.ts.map +1 -1
  22. package/lib/types/client/progress-updates/settings-binding.d.ts +13 -1
  23. package/lib/types/client/progress-updates/settings-binding.d.ts.map +1 -1
  24. package/lib/types/client/progress-updates/settings-section.d.ts +1 -1
  25. package/lib/types/client/progress-updates/settings-section.d.ts.map +1 -1
  26. package/lib/types/client/workbuddy/WorkBuddyComposerQuota.d.ts +34 -0
  27. package/lib/types/client/workbuddy/WorkBuddyComposerQuota.d.ts.map +1 -0
  28. package/lib/types/client/workbuddy/WorkBuddySection.d.ts +91 -0
  29. package/lib/types/client/workbuddy/WorkBuddySection.d.ts.map +1 -0
  30. package/lib/types/client/workbuddy/locales.d.ts +768 -0
  31. package/lib/types/client/workbuddy/locales.d.ts.map +1 -0
  32. package/lib/types/client/workbuddy/styles.d.ts +21 -0
  33. package/lib/types/client/workbuddy/styles.d.ts.map +1 -0
  34. package/lib/types/client/workbuddy/useWorkBuddySection.d.ts +149 -0
  35. package/lib/types/client/workbuddy/useWorkBuddySection.d.ts.map +1 -0
  36. package/lib/types/compat.d.ts +1 -1
  37. package/lib/types/entry-plan-mode-pipeline.d.ts +206 -0
  38. package/lib/types/entry-plan-mode-pipeline.d.ts.map +1 -0
  39. package/lib/types/entry-plan-mode.d.ts +353 -0
  40. package/lib/types/entry-plan-mode.d.ts.map +1 -0
  41. package/lib/types/entry-tool-result-pruner.d.ts +255 -0
  42. package/lib/types/entry-tool-result-pruner.d.ts.map +1 -0
  43. package/lib/types/host/agent-preset.d.ts.map +1 -1
  44. package/lib/types/host/cache-warmup/contracts.d.ts +5 -1
  45. package/lib/types/host/cache-warmup/contracts.d.ts.map +1 -1
  46. package/lib/types/host/cache-warmup/install.d.ts.map +1 -1
  47. package/lib/types/host/cache-warmup/projection.d.ts.map +1 -1
  48. package/lib/types/host/cache-warmup/retrieval-intent.d.ts +18 -0
  49. package/lib/types/host/cache-warmup/retrieval-intent.d.ts.map +1 -0
  50. package/lib/types/host/cache-warmup/settings.d.ts +9 -2
  51. package/lib/types/host/cache-warmup/settings.d.ts.map +1 -1
  52. package/lib/types/host/common/capabilities.d.ts.map +1 -1
  53. package/lib/types/host/common/model-request-control.d.ts +1 -1
  54. package/lib/types/host/common/model-request-control.d.ts.map +1 -1
  55. package/lib/types/host/common/request-diagnostics.d.ts +1 -1
  56. package/lib/types/host/common/request-diagnostics.d.ts.map +1 -1
  57. package/lib/types/host/compaction-policy-guard.d.ts +58 -0
  58. package/lib/types/host/compaction-policy-guard.d.ts.map +1 -0
  59. package/lib/types/host/compaction-prompt.d.ts +129 -0
  60. package/lib/types/host/compaction-prompt.d.ts.map +1 -0
  61. package/lib/types/host/plan-mode-cache-guard.d.ts +16 -0
  62. package/lib/types/host/plan-mode-cache-guard.d.ts.map +1 -0
  63. package/lib/types/host/preset-sync.d.ts.map +1 -1
  64. package/lib/types/host/ptc-sdk-cache-guard.d.ts +80 -0
  65. package/lib/types/host/ptc-sdk-cache-guard.d.ts.map +1 -0
  66. package/lib/types/host/tool-result-pruner-guard.d.ts +34 -0
  67. package/lib/types/host/tool-result-pruner-guard.d.ts.map +1 -0
  68. package/lib/types/host/workbuddy/account-pool.d.ts +389 -0
  69. package/lib/types/host/workbuddy/account-pool.d.ts.map +1 -0
  70. package/lib/types/host/workbuddy/adapter.d.ts +249 -0
  71. package/lib/types/host/workbuddy/adapter.d.ts.map +1 -0
  72. package/lib/types/host/workbuddy/atrest.d.ts +98 -0
  73. package/lib/types/host/workbuddy/atrest.d.ts.map +1 -0
  74. package/lib/types/host/workbuddy/checkin.d.ts +150 -0
  75. package/lib/types/host/workbuddy/checkin.d.ts.map +1 -0
  76. package/lib/types/host/workbuddy/client.d.ts +214 -0
  77. package/lib/types/host/workbuddy/client.d.ts.map +1 -0
  78. package/lib/types/host/workbuddy/dsml.d.ts +55 -0
  79. package/lib/types/host/workbuddy/dsml.d.ts.map +1 -0
  80. package/lib/types/host/workbuddy/guardrails.d.ts +111 -0
  81. package/lib/types/host/workbuddy/guardrails.d.ts.map +1 -0
  82. package/lib/types/host/workbuddy/identity.d.ts +71 -0
  83. package/lib/types/host/workbuddy/identity.d.ts.map +1 -0
  84. package/lib/types/host/workbuddy/mapper.d.ts +238 -0
  85. package/lib/types/host/workbuddy/mapper.d.ts.map +1 -0
  86. package/lib/types/host/workbuddy/model-catalog.d.ts +217 -0
  87. package/lib/types/host/workbuddy/model-catalog.d.ts.map +1 -0
  88. package/lib/types/host/workbuddy/oauth.d.ts +47 -0
  89. package/lib/types/host/workbuddy/oauth.d.ts.map +1 -0
  90. package/lib/types/host/workbuddy/outbound-identity.d.ts +139 -0
  91. package/lib/types/host/workbuddy/outbound-identity.d.ts.map +1 -0
  92. package/lib/types/host/workbuddy/routes.d.ts +56 -0
  93. package/lib/types/host/workbuddy/routes.d.ts.map +1 -0
  94. package/lib/types/host/workbuddy/token-store.d.ts +370 -0
  95. package/lib/types/host/workbuddy/token-store.d.ts.map +1 -0
  96. package/lib/types/host/workbuddy/types.d.ts +179 -0
  97. package/lib/types/host/workbuddy/types.d.ts.map +1 -0
  98. package/lib/types/index.d.ts +35 -0
  99. package/lib/types/index.d.ts.map +1 -1
  100. package/lib/types/shared/workbuddy-contracts.d.ts +531 -0
  101. package/lib/types/shared/workbuddy-contracts.d.ts.map +1 -0
  102. package/package.json +14 -2
  103. package/presets/dispatch/README.md +57 -3
  104. package/presets/dispatch/agent.cordis.yml +63 -3
@@ -0,0 +1,389 @@
1
+ /**
2
+ * WorkBuddy's account pool.
3
+ *
4
+ * This line is the one that adopts accounts it does not own: alongside the
5
+ * accounts a user signs in through this plugin, it reads the CodeBuddy desktop
6
+ * client's own `*.info` credentials so an existing IDE sign-in needs no second
7
+ * login. Both kinds take part in scheduling through the shared
8
+ * {@link AccountPoolCore}, and the difference is enforced here:
9
+ *
10
+ * - **managed** accounts were signed in through this plugin. They live in the
11
+ * pool's encrypted storage and may be deleted.
12
+ * - **desktop** accounts belong to the IDE. Their credential is re-read from the
13
+ * IDE's file on every pick, because the IDE rotates it on its own schedule and
14
+ * a cached copy would go stale; they are never deleted, only hidden.
15
+ */
16
+ import { LlmError } from '@deepseek-ai/dsh-llm';
17
+ import { AccountPoolCore, type PoolAccountShape, type PoolData } from '../common/account-pool.ts';
18
+ import type { CredentialStore } from '../token-store.ts';
19
+ import { FileCredentialStore, type WorkBuddyCredentials } from './token-store.ts';
20
+ import type { PoolAccountQuotaDto } from '../../shared/account-pool-contracts.ts';
21
+ import { type WorkBuddyAccountQuota, type WorkBuddyAccountRestrictionDto, type WorkBuddyAccountSummaryDto, type WorkBuddyPoolStateDto, type WorkBuddyRegion } from '../../shared/workbuddy-contracts.ts';
22
+ /** One pooled WorkBuddy account: the credential plus the facts the card renders. */
23
+ export interface WorkBuddyPoolAccount extends PoolAccountShape<WorkBuddyCredentials> {
24
+ region: WorkBuddyRegion;
25
+ nickname?: string;
26
+ uin?: string;
27
+ /** Which store owns the credential; only managed accounts may be deleted. */
28
+ source: 'desktop' | 'managed';
29
+ /** IDE file a desktop account came from; absent for managed accounts. */
30
+ sourceFile?: string;
31
+ }
32
+ /** Encrypted pool file this line owns. */
33
+ export declare function workBuddyPoolPath(): string;
34
+ /**
35
+ * Error code the adapter routes on when the WHOLE pool is paused.
36
+ *
37
+ * Deliberately not `RATE_LIMIT`: a pause says the pool will not be served at
38
+ * all — the upstream barred accounts, it did not answer 429 for one request —
39
+ * and confusing the two would send the user to wait out a window that has
40
+ * nothing to do with their accounts. The pause lifts by itself and the RESTRICTED
41
+ * accounts do not, so the remedy is also different.
42
+ */
43
+ export declare const WORKBUDDY_POOL_PAUSED_CODE: "WORKBUDDY_POOL_PAUSED";
44
+ /**
45
+ * Thrown instead of selecting an account while the whole pool is paused.
46
+ *
47
+ * Exported so the protocol worker's caller can tell "upstream barred two
48
+ * accounts" (wait for the pause, then sign the restricted ones in again) from
49
+ * "this request was rate limited". It is never reported as an upstream 429.
50
+ */
51
+ export declare class WorkBuddyPoolPausedError extends LlmError {
52
+ /** Unix milliseconds the pause lifts on its own; absent when nothing named one. */
53
+ readonly pausedUntil: number | undefined;
54
+ constructor(message: string, pausedUntil?: number);
55
+ }
56
+ /**
57
+ * How long a second barred account stops the whole pool.
58
+ *
59
+ * Thirty minutes is the confirmed Wave 2A policy: long enough that an upstream
60
+ * refusing every account is not hammered once per turn, short enough that a
61
+ * transient bar does not take the line offline for a working day. When it
62
+ * passes the pool resumes by itself — the restricted accounts do not, because
63
+ * nothing upstream said they may serve again.
64
+ */
65
+ export declare const WORKBUDDY_POOL_PAUSE_MS: number;
66
+ /**
67
+ * Window in which a second, DIFFERENT barred account stops the whole pool.
68
+ *
69
+ * Measured from each refusal to the next. One barred account is that account's
70
+ * problem and the rest still serve; two different accounts barred inside this
71
+ * window is the pool's problem, because the same thing is about to happen to
72
+ * the third.
73
+ */
74
+ export declare const WORKBUDDY_RESTRICTION_ESCALATION_WINDOW_MS: number;
75
+ /** One account's `11140` as this pool's own document keeps it. */
76
+ export interface WorkBuddyPoolRestrictionRecord {
77
+ /** Gateway code; {@link WORKBUDDY_RESTRICTED_ACCOUNT_CODE} is the only one recorded. */
78
+ code: number;
79
+ /** Upstream `msg` when one came with the code. Never a credential. */
80
+ message?: string;
81
+ /** Instant the gateway named, when it named one. Reported, never enforced. */
82
+ until?: number;
83
+ /** When this pool observed the refusal; drives the escalation window. */
84
+ observedAt: number;
85
+ }
86
+ /** A pause of the WHOLE pool, as its own document keeps it. */
87
+ export interface WorkBuddyPoolPauseRecord {
88
+ /** Composed sentence for the card; never a token. */
89
+ reason: string;
90
+ pausedSince: number;
91
+ /** Unix milliseconds the pause lifts by itself. */
92
+ pausedUntil: number;
93
+ /** Account ids whose `11140` caused the pause, in observation order. */
94
+ triggeredBy: string[];
95
+ }
96
+ /** Whole-pool state kept inside the pool document. */
97
+ export interface WorkBuddyPoolDocumentState {
98
+ /** Account id → its `11140`. A restricted account is out of rotation. */
99
+ restrictions: Record<string, WorkBuddyPoolRestrictionRecord>;
100
+ pause?: WorkBuddyPoolPauseRecord;
101
+ }
102
+ /**
103
+ * This line's pool document: the shared shape plus the pool-wide state.
104
+ *
105
+ * The state lives in the SAME document rather than a second store, so one
106
+ * encrypted write keeps "which account may serve" and "may the pool serve at
107
+ * all" in step: two documents could be observed half-updated, and the pool
108
+ * would then select an account its own restriction record bars.
109
+ */
110
+ export interface WorkBuddyPoolData extends PoolData<WorkBuddyPoolAccount> {
111
+ pool?: WorkBuddyPoolDocumentState;
112
+ }
113
+ /**
114
+ * Read whole-pool state out of a stored document.
115
+ *
116
+ * Absent means "no pool-wide state", which is exactly what a document written
117
+ * before Wave 2A holds — and what an untouched pool still writes, so an install
118
+ * that never met `11140` carries no extra keys at all. A malformed ENTRY is
119
+ * dropped rather than failing the document: losing one memory of a refusal must
120
+ * not make the accounts unreadable, while silently keeping an unparseable pause
121
+ * could take the line offline forever.
122
+ */
123
+ export declare function parseWorkBuddyPoolState(value: unknown): WorkBuddyPoolDocumentState | undefined;
124
+ /**
125
+ * Validate and normalize one whole pool document.
126
+ *
127
+ * A record whose credential is unusable is dropped rather than repaired: the
128
+ * account can always be adopted again from its file or by signing in again, and
129
+ * keeping a credential-less row would offer the user an account nothing can use.
130
+ */
131
+ export declare function parseWorkBuddyPoolData(value: unknown): WorkBuddyPoolData;
132
+ /**
133
+ * One cached snapshot as the shared account card renders it.
134
+ *
135
+ * Only what the DTO states is published: every meter that measured a share
136
+ * becomes a window, and a meter that measured none is dropped rather than drawn
137
+ * at 0 — which would claim an unspent allowance no reading supports.
138
+ *
139
+ * Exported for the pool's regression tests; the card only reaches it through
140
+ * the summary hook.
141
+ */
142
+ export declare function workBuddyPoolQuota(snapshot: WorkBuddyAccountQuota): PoolAccountQuotaDto | undefined;
143
+ export interface WorkBuddyAccountPoolOptions {
144
+ /** Credential store the desktop scan reads through; the pool owns scheduling. */
145
+ store?: FileCredentialStore;
146
+ /** Test seam; production builds the platform backend from the pool path. */
147
+ backend?: CredentialStore<PoolData<WorkBuddyPoolAccount>>;
148
+ maxAccounts?: number;
149
+ /**
150
+ * The settings selection: the account the user pinned and the ones hidden.
151
+ *
152
+ * Read on every pick rather than captured once, because both change from the
153
+ * settings card while the adapter is serving requests.
154
+ */
155
+ selection?: () => {
156
+ selectedAccountId: string | null;
157
+ hiddenAccountIds: readonly string[];
158
+ };
159
+ }
160
+ /**
161
+ * WorkBuddy's account pool.
162
+ *
163
+ * Desktop accounts are adopted into the pool so they can be scheduled and
164
+ * cooled down like any other, but they stay owned by the IDE: the pool refreshes
165
+ * their credential from the IDE's own file on every pick, and deletion is
166
+ * refused for them.
167
+ */
168
+ export declare class WorkBuddyAccountPool extends AccountPoolCore<WorkBuddyCredentials, WorkBuddyPoolAccount, WorkBuddyAccountSummaryDto> {
169
+ private readonly store;
170
+ private readonly options_selection;
171
+ /**
172
+ * Older settings key → the id the account has now.
173
+ *
174
+ * A pin or a hide written before the identity fix names its account by the
175
+ * old name-derived key. Kept in step by {@link read}, which every routing
176
+ * decision goes through, so the mapping is never stale when it is consulted.
177
+ */
178
+ private readonly legacyIds;
179
+ /**
180
+ * Per-model parks, in memory only: account id → model → the instant it lifts.
181
+ *
182
+ * The subscription meters an account × MODEL over a 24h window (429 `code
183
+ * 6004` names when it lifts), so cooling the whole ACCOUNT for one exhausted
184
+ * model throws away every other model that account can still serve. Process
185
+ * local on purpose, like the sibling gateway's model cooldowns: the window is
186
+ * short, and a restart re-measures it with the first request rather than
187
+ * trusting a stale timestamp.
188
+ */
189
+ private readonly modelCooldowns;
190
+ /**
191
+ * Whole-pool state as of the LAST load through {@link loadPoolData}.
192
+ *
193
+ * A cache rather than a read per check: eligibility runs once per account per
194
+ * pick, and asking the encrypted store each time would pay a DPAPI unprotect
195
+ * per candidate. Every read and every mutation loads the document first, so
196
+ * this cannot be stale for longer than one storage round trip.
197
+ */
198
+ private restrictions;
199
+ private pause;
200
+ constructor(options?: WorkBuddyAccountPoolOptions);
201
+ /**
202
+ * Load the pool, refreshing the legacy-key index on the way.
203
+ *
204
+ * Every routing decision and mutation goes through this method before it
205
+ * consults the settings selection, so building the index here is what keeps
206
+ * a pin written before the identity fix resolvable when
207
+ * {@link resolveSelectionId} is asked for it.
208
+ */
209
+ protected loadPoolData(): Promise<PoolData<WorkBuddyPoolAccount>>;
210
+ /** Keep the pool-wide cache in step with the document just loaded. */
211
+ private observePoolState;
212
+ /** This account's live `11140`, matched through every key it answers to. */
213
+ private restrictionFor;
214
+ /** The pause that is still running, or undefined when it lifted or never began. */
215
+ private activePause;
216
+ /** The error every selection raises while the pool is paused. */
217
+ private pausedError;
218
+ /**
219
+ * Record an account-scoped refusal reported by the protocol worker.
220
+ *
221
+ * This is the ONE entry point for `11140`, and the policy it implements is
222
+ * the confirmed Wave 2A one:
223
+ *
224
+ * - the first barred account leaves rotation immediately and its restriction
225
+ * is PERSISTED (in the pool document, so a second pool instance over the
226
+ * same store sees it), while every other account keeps serving;
227
+ * - a SECOND, different account barred within
228
+ * {@link WORKBUDDY_RESTRICTION_ESCALATION_WINDOW_MS} pauses the whole pool
229
+ * for {@link WORKBUDDY_POOL_PAUSE_MS} — during which no account is selected
230
+ * at all — and that pause is persisted too;
231
+ * - the pause lifts by itself, the restricted accounts do NOT: only a
232
+ * completed sign-in ({@link addAccount}) puts one back in rotation.
233
+ *
234
+ * Only {@link WORKBUDDY_RESTRICTED_ACCOUNT_CODE} is accepted. `11128` is the
235
+ * channel/request-shape block and `11158` is one model an account has no
236
+ * entitlement for; neither says the ACCOUNT may not serve, so recording either
237
+ * would take a working account out of rotation and — with two inside the
238
+ * window — stop the whole pool over a request-shape mistake. The call is
239
+ * therefore refused loudly, which keeps that misrouting visible in a test or a
240
+ * log instead of silently barring a healthy account.
241
+ */
242
+ markAccountRestriction(accountId: string, restriction: WorkBuddyAccountRestrictionDto): Promise<void>;
243
+ /** Clear one account's restriction; a completed sign-in is what does this. */
244
+ private clearRestriction;
245
+ /**
246
+ * Whole-pool state for the wire: whether the pool is paused, why, until when,
247
+ * which accounts are barred, and where this document lives.
248
+ *
249
+ * `pausedUntil` is the pause's remaining time in absolute form, which is what
250
+ * a card can count down and a route can answer without a second call. An
251
+ * expired pause is reported as not paused — it never needs a write to lift.
252
+ */
253
+ poolState(): Promise<WorkBuddyPoolStateDto>;
254
+ /** Map a settings key from before the identity fix onto the account's real id. */
255
+ private resolveSelectionId;
256
+ /**
257
+ * Adopt any desktop credential the pool does not know yet.
258
+ *
259
+ * The IDE's directory is the source of truth for its own accounts, so a
260
+ * credential that appears there joins the pool. Nothing is ever removed: a
261
+ * file that disappears (the IDE signed out, a profile was moved) leaves the
262
+ * account in place, where the user can hide or delete the managed ones.
263
+ */
264
+ syncDesktopAccounts(): Promise<WorkBuddyAccountSummaryDto[]>;
265
+ /**
266
+ * A stale `authStatus` is cleared once the credential in force has moved on.
267
+ *
268
+ * The marker records that ONE token was refused; it does not record that the
269
+ * sign-in is gone. This line can still produce it from a token-endpoint
270
+ * refusal, and the copy that refusal was about can be replaced afterwards by
271
+ * whoever owns it — the IDE rotates a desktop account's token on its own
272
+ * schedule, and the daily check-in renews a managed one and writes it back to
273
+ * this plugin's store. Nothing else in the system ever revisits an
274
+ * `authStatus`, so without this a row stays out of rotation for good and the
275
+ * card keeps saying "需要重新登录" for an account that is signed in.
276
+ *
277
+ * The verdict is re-checked against what the store holds NOW: a row whose
278
+ * account has a DIFFERENT, unexpired token is routable again and adopts it,
279
+ * while a row still holding the refused token keeps its marker — that one
280
+ * really does need a sign-in.
281
+ */
282
+ private healStaleAuthFailures;
283
+ /** Account summaries, with a marker the credentials have outlived cleared. */
284
+ listAccounts(): Promise<WorkBuddyAccountSummaryDto[]>;
285
+ /**
286
+ * Pick an account, healing a stale marker first.
287
+ *
288
+ * The healing belongs on the request path, not only on the card: an account
289
+ * marked expired is excluded by the core's eligibility rule, so until the
290
+ * marker is gone the next request is answered with "全部 N 个账号都需要重新登录"
291
+ * for a credential the store has already replaced.
292
+ */
293
+ getEffectiveAccount(excludeIds?: ReadonlySet<string>, fetchFn?: typeof fetch, forceRefresh?: boolean): Promise<{
294
+ account: WorkBuddyPoolAccount;
295
+ credentials: WorkBuddyCredentials;
296
+ }>;
297
+ /**
298
+ * Pick the account for the next request.
299
+ *
300
+ * A desktop account's credential is re-read from the IDE's file first: the IDE
301
+ * refreshes its own token, so the copy the pool adopted can be older than what
302
+ * is on disk.
303
+ */
304
+ getEffectiveCredential(excludeIds?: ReadonlySet<string>, fetchFn?: typeof fetch, model?: string): Promise<{
305
+ account: WorkBuddyPoolAccount;
306
+ credentials: WorkBuddyCredentials;
307
+ }>;
308
+ /**
309
+ * Park ONE model on one account; every other model keeps serving.
310
+ *
311
+ * Returns the instant the park lifts, or undefined for a non-positive
312
+ * duration (which clears any existing park rather than extending it).
313
+ */
314
+ markModelCooldown(accountId: string, model: string, durationMs: number): number | undefined;
315
+ /** When this account's park on this model lifts, or undefined when it may serve. */
316
+ modelCooldownUntil(accountId: string, model: string, now?: number): number | undefined;
317
+ /**
318
+ * The models this account is parked on right now, with the instant each park
319
+ * lifts.
320
+ *
321
+ * Only LIVE parks are reported: a window that has passed is dropped here, so
322
+ * the card never shows a model that may already serve. The lift instant is
323
+ * the one the upstream named, stored verbatim by
324
+ * {@link markModelCooldown}; it is never recomputed from a duration that has
325
+ * since drifted, because a re-derived instant would drift with it.
326
+ */
327
+ private liveModelCooldowns;
328
+ /**
329
+ * Clear one account's cooldown AND the models it is parked on.
330
+ *
331
+ * The card's "reset cooldown" button promises the account is usable again. A
332
+ * model park is a cooldown too — the account stays eligible, but it still
333
+ * refuses the model the request wants — so leaving the park behind would
334
+ * report a reset that did not happen, and the park would outlive the only
335
+ * control the user has over it.
336
+ *
337
+ * The base write comes first: it is the half that can fail, and a failed
338
+ * write must leave the account's state as it was rather than half-cleared.
339
+ */
340
+ clearCooldown(accountId: string): Promise<void>;
341
+ /** Accounts currently parked on one model, with lifted entries pruned on the way. */
342
+ private parkedAccountIds;
343
+ /**
344
+ * Whether another account besides `triedAccountIds` can serve THIS model.
345
+ *
346
+ * The plain {@link hasAnotherAvailableAccount} answers for any model, so a
347
+ * rotation loop that consulted it would keep trying while every remaining
348
+ * account is parked on the model it is asking for — turning one exhausted
349
+ * window into a burst of refused requests.
350
+ */
351
+ hasAnotherAvailableAccountFor(triedAccountIds: ReadonlySet<string>, model: string): Promise<boolean>;
352
+ /**
353
+ * Whether another account may serve right now.
354
+ *
355
+ * Answered from ONE read that also carries the pool-wide state, and false
356
+ * while the pool is paused: the rotation loop consults this before retrying,
357
+ * so a pause has to end the loop there rather than after another selection it
358
+ * would only refuse.
359
+ */
360
+ hasAnotherAvailableAccount(triedAccountIds: ReadonlySet<string>): Promise<boolean>;
361
+ /**
362
+ * Whether an account may serve a request.
363
+ *
364
+ * A hidden account keeps its place, its alias and its history, but the user
365
+ * has taken it out of this plugin's rotation, so it is not eligible.
366
+ */
367
+ protected isEligible(account: WorkBuddyPoolAccount, now: number, triedAccountIds?: ReadonlySet<string>): boolean;
368
+ private selection;
369
+ /**
370
+ * Add or re-authorize one account, settling its identity first.
371
+ *
372
+ * A completed browser sign-in states the display name but not always the uid,
373
+ * while the IDE's own file for the same account states the uid. Passing the
374
+ * raw response through would key the login under the name and the file under
375
+ * the uid, so the pool would hold the same account twice — the duplicate this
376
+ * fix exists to remove. Resolving from the credential's own token before the
377
+ * dedupe key is computed is what makes the two paths agree.
378
+ */
379
+ addAccount(credentials: WorkBuddyCredentials, alias?: string): Promise<WorkBuddyPoolAccount>;
380
+ /**
381
+ * Remove one account inside a transaction.
382
+ *
383
+ * A desktop account is refused: the plugin does not own that credential file,
384
+ * so removing it from the pool would strand the IDE's session while telling
385
+ * the user their account was deleted. Hiding is the supported action there.
386
+ */
387
+ protected deleteAccountFromPool(data: PoolData<WorkBuddyPoolAccount>, accountId: string): void;
388
+ }
389
+ //# sourceMappingURL=account-pool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account-pool.d.ts","sourceRoot":"","sources":["../../../../src/host/workbuddy/account-pool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,249 @@
1
+ import { LlmAdapter, LlmError, ReasoningEffortId, type GenerateOptions, type LlmModelInfo, type LlmProviderInfo, type LlmResolvedModelInfo, type PreparedAdapterCall, type ResolvedRetryPolicy, type StreamChunk } from '@deepseek-ai/dsh-llm';
2
+ import type { ImageAttachmentRef } from '@deepseek-ai/dsh-attachment';
3
+ import { PROVIDER_ID, PROVIDER_NAME } from './types.ts';
4
+ import { FileCredentialStore, FileModelSettingsStore, type WorkBuddyPreferenceStore } from './token-store.ts';
5
+ import { type WorkBuddyModelEntry } from './model-catalog.ts';
6
+ import { type AttachmentImageReader } from './mapper.ts';
7
+ import { type WorkBuddyAccountRestrictionDto } from '../../shared/workbuddy-contracts.ts';
8
+ import type { WorkBuddyCredentials } from './token-store.ts';
9
+ import { type WorkBuddyUsageLedger } from './guardrails.ts';
10
+ /**
11
+ * Effort this route materializes when the caller names none.
12
+ *
13
+ * Preference order: the user's configured level when the model accepts it,
14
+ * otherwise the level the gateway's own catalog declares for that model (the
15
+ * same default the official CLI applies), otherwise nothing — which leaves the
16
+ * provider's own default in charge. A configured level the model does not
17
+ * accept is ignored rather than sent, since the endpoint rejects an unsupported
18
+ * level with `code 11150`.
19
+ */
20
+ export declare function resolveDefaultReasoningEffort(efforts: readonly string[], configuredEffort?: string | null, modelDefaultEffort?: string | null): ReasoningEffortId | undefined;
21
+ /** Cooldown a 429 imposes when the service names no reset instant. */
22
+ export declare const POOL_COOLDOWN_MS: number;
23
+ /**
24
+ * The account pool this adapter drives, declared STRUCTURALLY.
25
+ *
26
+ * The pool module (`account-pool.ts`) and the settings routes are outside this
27
+ * file's boundary — Wave 3 owns them — so the adapter cannot import the class.
28
+ * Declaring the shape it calls has one property that matters here: this file is
29
+ * typechecked against the CONTRACT, so a method renamed on the pool side fails
30
+ * the build instead of failing at run time in the middle of a rotation loop.
31
+ *
32
+ * `markAccountRestriction` is the one member the pool worker FROZE for Wave 2B:
33
+ * code 11140 is an account verdict (who is asking), not a model verdict, and a
34
+ * `cooldownUntil` on the account or on a model cannot express "this account is
35
+ * barred from everything".
36
+ */
37
+ export interface WorkBuddyAccountPool {
38
+ listAccounts(): Promise<readonly {
39
+ id: string;
40
+ }[]>;
41
+ getEffectiveCredential(tried: ReadonlySet<string>, fetchFn: typeof fetch, model?: string): Promise<{
42
+ account: {
43
+ id: string;
44
+ };
45
+ credentials: WorkBuddyCredentials;
46
+ }>;
47
+ renewCredential(accountId: string, fetchFn: typeof fetch, current: WorkBuddyCredentials): Promise<WorkBuddyCredentials>;
48
+ hasAnotherAvailableAccount(tried: ReadonlySet<string>): Promise<boolean>;
49
+ hasAnotherAvailableAccountFor(tried: ReadonlySet<string>, model: string): Promise<boolean>;
50
+ markCooldown(accountId: string, ms: number, reason: string): Promise<unknown>;
51
+ markModelCooldown(accountId: string, model: string, until: number): void;
52
+ /** Freeze this account out of every model after code 11140. */
53
+ markAccountRestriction(accountId: string, restriction: WorkBuddyAccountRestrictionDto): Promise<void>;
54
+ }
55
+ /**
56
+ * The pool's own "everything is stopped" error.
57
+ *
58
+ * Read structurally (by `name`) rather than by `instanceof`: the class lives in
59
+ * the pool module this file may not import, and a plugin build that loaded the
60
+ * pool and the adapter through two module instances would fail an `instanceof`
61
+ * check that the name comparison passes.
62
+ */
63
+ export interface WorkBuddyPoolPausedErrorLike extends Error {
64
+ name: 'WorkBuddyPoolPausedError';
65
+ /** Why the whole pool is stopped; composed by the host, never a token. */
66
+ pauseReason?: string;
67
+ /** Unix milliseconds the pause lifts on its own, when it does. */
68
+ pausedUntil?: number;
69
+ }
70
+ /** Whether one thrown value is the pool's whole-pool pause. */
71
+ export declare function isPoolPausedError(error: unknown): error is WorkBuddyPoolPausedErrorLike;
72
+ /**
73
+ * Which of the account's models the picker may offer.
74
+ *
75
+ * The stored list is kept relative to what the ACCOUNT can serve, and the
76
+ * shipped default is not a stored preference: a user who never visited the
77
+ * picker has exactly the shipped defaults stored, and that state has to mean
78
+ * "everything this account can serve" — otherwise a model the catalog gained
79
+ * after the last visit would be hidden from someone who never hid it.
80
+ */
81
+ export declare function resolveEnabledModelIds(stored: readonly string[], available: readonly string[], enabled?: boolean): string[];
82
+ /** Most system texts this process remembers (FIFO beyond it). */
83
+ export declare const REFUSED_SYSTEM_MEMORY_LIMIT = 256;
84
+ /** Remember one refused system text, evicting the oldest past the limit. */
85
+ export declare function rememberRefusedSystemKey(key: string): void;
86
+ /** Whether this process has already been refused for that system text. */
87
+ export declare function refusedSystemKeyKnown(key: string): boolean;
88
+ /** How many system texts are remembered (test and diagnostics seam). */
89
+ export declare function refusedSystemKeyCount(): number;
90
+ /** Forget every remembered refusal (test seam; never called by the adapter). */
91
+ export declare function clearRefusedSystemKeys(): void;
92
+ export interface WorkBuddyAdapterOptions {
93
+ /**
94
+ * Per-account daily token counters behind the local guardrails.
95
+ *
96
+ * Absent means the guardrails cannot be evaluated, so a deployment without it
97
+ * never refuses a request for a reason nothing is counting.
98
+ */
99
+ usageLedger?: WorkBuddyUsageLedger;
100
+ fetchFn?: typeof fetch;
101
+ attachments?: AttachmentImageReader;
102
+ /** Live catalog loader seam; defaults to the gateway `/v3/config` call. */
103
+ loadCatalog?: (credentials: WorkBuddyCredentials) => Promise<WorkBuddyModelEntry[]>;
104
+ /**
105
+ * Multi-account pool this adapter rotates through.
106
+ *
107
+ * Absent leaves the line on its single-credential path, which is what the
108
+ * headless tests and any caller that predates the pool use.
109
+ */
110
+ accountPool?: WorkBuddyAccountPool;
111
+ }
112
+ export declare class WorkBuddyAdapter extends LlmAdapter {
113
+ private readonly store;
114
+ private readonly modelSettings;
115
+ private readonly preferences?;
116
+ private readonly options;
117
+ /** Null keeps the pre-pool single-credential path. */
118
+ private readonly accountPool;
119
+ constructor(store?: FileCredentialStore, modelSettings?: FileModelSettingsStore, preferences?: WorkBuddyPreferenceStore | undefined, options?: WorkBuddyAdapterOptions);
120
+ providerInfo(provider: string): LlmProviderInfo;
121
+ providerRetryPolicy(): ResolvedRetryPolicy;
122
+ imageRequestPricing(): undefined;
123
+ /**
124
+ * Request version the harness should hand this route for one image.
125
+ *
126
+ * This is the other half of the request image budget and it has to come from
127
+ * the same place: the shared kernel decides the target (long edge 1024,
128
+ * 256 KiB per image) and the shared budget decides what still fits, so a
129
+ * mapper cannot ask for a target the harness does not produce, and cannot
130
+ * send an image the kernel already paid to downscale. Answering undefined
131
+ * here used to mean send-as-stored while the mapper applied its own private
132
+ * 12 MB ceiling, which is exactly the drift this pairing removes.
133
+ */
134
+ imageRequestTarget(_provider: string, _model: string, image: ImageAttachmentRef): {
135
+ width: number;
136
+ height: number;
137
+ maxBytes: number;
138
+ };
139
+ private settings;
140
+ private credentials;
141
+ /**
142
+ * Catalog for the picker: the live gateway listing when reachable, the
143
+ * shipped table otherwise.
144
+ *
145
+ * The gateway is the authority on capabilities, so its answer replaces the
146
+ * fallback rather than merging with it — a model whose context window changed
147
+ * upstream must not keep a stale local value.
148
+ */
149
+ private catalog;
150
+ /**
151
+ * Models the picker may offer.
152
+ *
153
+ * The catalog is filtered by the account's own region: asking a region for a
154
+ * model it does not serve answers 400 `code 11102`, so offering one would
155
+ * hand the user a model that cannot work.
156
+ */
157
+ listModels(provider?: string): Promise<readonly LlmModelInfo[]>;
158
+ resolveModel(provider: string, modelId: string, signal?: AbortSignal): Promise<LlmResolvedModelInfo>;
159
+ prepareCall(provider: string, model: string, signal?: AbortSignal): Promise<PreparedAdapterCall>;
160
+ stream(options: GenerateOptions): AsyncIterable<StreamChunk>;
161
+ /**
162
+ * Effort this request should carry, or `undefined` to send none.
163
+ *
164
+ * Order: the caller's explicit choice, then the user's configured level, then
165
+ * the level the gateway catalog declares for this model. The last step
166
+ * matters because the endpoint returns an **empty `reasoning_content`** when
167
+ * no `reasoning_effort` is present, even for a reasoning-only model: measured
168
+ * on `deepseek-v4.1-flash`, the same prompt yields 0 reasoning characters
169
+ * with no field and 130-215 with one. Omitting it therefore silently drops
170
+ * the model's thinking instead of letting the provider pick, so the catalog's
171
+ * own default (which is what the official CLI sends) is materialized here.
172
+ *
173
+ * A candidate the model does not accept is discarded rather than sent, since
174
+ * the endpoint rejects an unsupported level with `code 11150`.
175
+ */
176
+ /**
177
+ * Accounts the LOCAL guardrails refuse for this model right now.
178
+ *
179
+ * Reads only numbers this plugin already owns — the billing snapshot's
180
+ * remaining credits and this line's own token counters — so it never becomes a
181
+ * second, disagreeing source of truth about the upstream allowance.
182
+ */
183
+ private guardrailBlockedAccounts;
184
+ private effortFor;
185
+ private requestStream;
186
+ /**
187
+ * Re-read the stored credential, for a token another process may have written.
188
+ *
189
+ * The desktop app and any sibling plugin share this directory, and a token
190
+ * written there is already the newest one available: refreshing over it would
191
+ * rotate the account's refresh token out from under whoever signed in last.
192
+ * A failed read answers null — it is a fallback, not a verdict.
193
+ */
194
+ private rereadCredential;
195
+ /**
196
+ * Count this request's billed tokens off its usage frame.
197
+ *
198
+ * Both protocols send exactly one usage frame at the end; the cache counts are
199
+ * added back because the aggregate the surfaces report excludes them, so a
200
+ * cached turn would otherwise be recorded as almost free.
201
+ */
202
+ private countedChunks;
203
+ /**
204
+ * Read one SSE body into chunks, protocol-agnostically.
205
+ *
206
+ * Bytes on the wire are the liveness signal: a heartbeat or status frame that
207
+ * produces no displayable chunk still proves the connection is alive, so the
208
+ * watchdog timer is reset at the transport layer, not at the parser.
209
+ */
210
+ private drainStream;
211
+ }
212
+ /**
213
+ * The failure a 11140 is reported as.
214
+ *
215
+ * Account-scoped, so it is classified apart from every model-scoped refusal: the
216
+ * message says the ACCOUNT is barred (not that a model is unavailable, not that
217
+ * a quota window is full), because the fix is a different account or a release
218
+ * from the service, and no retry can help.
219
+ */
220
+ export declare function accountRestrictedFailure(detail: string, status: number): LlmError;
221
+ /**
222
+ * Map one failed HTTP response onto a DSH failure.
223
+ *
224
+ * The subscription reports its own conditions inside a JSON `code` while the
225
+ * status line stays generic, so the body is read for the specific cases the
226
+ * status alone cannot express — notably the quota signal, whose message carries
227
+ * the instant the allowance resets.
228
+ */
229
+ export declare function classifyFailure(status: number, detail: string, headers?: Headers): LlmError;
230
+ /**
231
+ * The reset instant a quota refusal names, as a delay from now.
232
+ *
233
+ * A 24h allowance that is exhausted answers 429 / `code 6004` with the wall
234
+ * clock it lifts — but only in the MESSAGE, never in a header, and in two
235
+ * realm-specific wordings (measured: intl "… resets at 2026-09-20 10:50:48
236
+ * UTC+8", cn "… 将在 2026-10-09 14:44:59 UTC+8 重置"). Reading the status alone
237
+ * therefore loses the only fact that says how long the account is out, and the
238
+ * pool parks a 24-hour limit for five minutes before trying it again.
239
+ *
240
+ * Deliberately tolerant, in the same direction as their gateway: an unparseable
241
+ * body yields `undefined` (callers keep their default cooldown) rather than an
242
+ * error, because this runs on a failure path. An absent `UTC±h` marker means
243
+ * UTC, which is how the sibling implementation reads it too.
244
+ */
245
+ export declare function readRateLimitReset(detail: string, now?: number): number | undefined;
246
+ /** Read the subscription's own numeric error code out of a response body. */
247
+ export declare function readErrorCode(detail: string): number | null;
248
+ export { PROVIDER_ID, PROVIDER_NAME };
249
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../../src/host/workbuddy/adapter.ts"],"names":[],"mappings":"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"}