dsh-connect-workbuddy 5.3.0 → 5.4.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.
@@ -1684,63 +1684,6 @@ function batchModelIds(ids, size = 50) {
1684
1684
  for (let index = 0; index < unique.length; index += size) batches.push(unique.slice(index, index + size));
1685
1685
  return batches;
1686
1686
  }
1687
- /** The app's own Chinese wording, per level. `normal` and `unknown` have none. */
1688
- const BUSY_LABELS_ZH = {
1689
- idle: "空闲",
1690
- busy: "轻微拥堵",
1691
- congested: "极度拥堵",
1692
- unavailable: "不可用"
1693
- };
1694
- /** The app's own English wording, per level. */
1695
- const BUSY_LABELS_EN = {
1696
- idle: "Idle",
1697
- busy: "Slightly congested",
1698
- congested: "Extremely congested",
1699
- unavailable: "Unavailable"
1700
- };
1701
- /**
1702
- * The word this level is drawn with, or `undefined` when it is not worth drawing.
1703
- *
1704
- * `normal` returns `undefined` on purpose: the app's selector shows a busy line
1705
- * only for a level that differs from the healthy default, and a badge that reads
1706
- * "正常" on fifteen rows out of twenty is noise, not information. `unknown`
1707
- * returns `undefined` for the same reason plus one more — the plugin must not
1708
- * invent a claim the upstream never made.
1709
- */
1710
- function busyLabel(level, locale = "zh") {
1711
- return (locale === "en" ? BUSY_LABELS_EN : BUSY_LABELS_ZH)[level];
1712
- }
1713
- /**
1714
- * Suffix appended to a model's DSH-facing display name, prefixed with a middle
1715
- * dot, or `''` when the level is not worth drawing.
1716
- *
1717
- * This is the "badge to the RIGHT of the model name" the DSH model selector
1718
- * already does for its own variants (`deepseek-v4.1-flash · 1017`): DSH renders
1719
- * `name` verbatim and offers no separate badge slot to a provider, so the only
1720
- * way to put load beside the name — instead of in a second line, or inside the
1721
- * name — is to extend the name. The name stays display-only: every DSH-side join
1722
- * keys on the model ID, and `workBuddyDisplayName` is the single place that
1723
- * builds it.
1724
- *
1725
- * ONLY A LEVEL THAT ASKS FOR A DECISION IS DRAWN — `busy`, `congested`,
1726
- * `unavailable`. `idle` deliberately gets no suffix here even though it has a
1727
- * word: on the measured CN roster seven of twenty models are idle, and a picker
1728
- * whose every row carries a badge reads as decoration rather than as a warning.
1729
- * That is also how the app's own selector behaves in its compact mode, where only
1730
- * `congested` is drawn at all. The card's detail panel is where the full scale
1731
- * belongs.
1732
- */
1733
- function busyDisplaySuffix(level, locale = "zh") {
1734
- if (level === void 0 || !DRAWN_IN_PICKER.has(level)) return "";
1735
- const label = busyLabel(level, locale);
1736
- return label === void 0 ? "" : ` · ${label}`;
1737
- }
1738
- /** The levels that change a model-picking decision; see {@link busyDisplaySuffix}. */
1739
- const DRAWN_IN_PICKER = /* @__PURE__ */ new Set([
1740
- "busy",
1741
- "congested",
1742
- "unavailable"
1743
- ]);
1744
1687
  /**
1745
1688
  * Attach each model's level to its catalog entry.
1746
1689
  *
@@ -2075,21 +2018,21 @@ const CONTEXT_OVERFLOW_MARKER = "prompt is too long (upstream code 11115)";
2075
2018
  */
2076
2019
  const CHANNEL_REJECT_MARKERS = ["unapproved channel", "illegal api invocation"];
2077
2020
  /**
2078
- * The other gates that answer the SAME business code.
2021
+ * The wordings that identify the OTHER two members of the `11128` family.
2079
2022
  *
2080
- * Excluded explicitly so a body that also carries the channel wording can never
2081
- * be read as the prompt rule: a confidently wrong diagnosis is worse here than
2082
- * the generic `client` reply, because it sends the user to rewrite a system
2083
- * prompt that is not at fault.
2023
+ * Used ONLY to NAME a member ({@link channelGateMemberOf}), never to exclude a
2024
+ * body from the family. The old code used them as an exclusion list, which is
2025
+ * how issue #113's misreported refusal was kept out of the plugin's retry
2026
+ * ladder: the upstream wrote `conversation id missing` over a content refusal,
2027
+ * and the exclusion believed it.
2084
2028
  *
2085
- * Each is also exported as its own predicate ({@link isConversationIdGateBody},
2029
+ * Each is exported as its own predicate ({@link isConversationIdGateBody},
2086
2030
  * {@link isFirstMessageGateBody}) so the reply can NAME the gate instead of
2087
2031
  * relaying the upstream's `displayTips` — which is the SAME misleading
2088
2032
  * sentence for all three members of the `11128` family.
2089
2033
  */
2090
2034
  const CONVERSATION_ID_GATE_MARKER = "conversation id missing";
2091
2035
  const FIRST_MESSAGE_GATE_MARKER = "first message is not system prompt";
2092
- const OTHER_CHANNEL_CODE_GATES = [CONVERSATION_ID_GATE_MARKER, FIRST_MESSAGE_GATE_MARKER];
2093
2036
  /**
2094
2037
  * Whether a failure body is the MODEL-scoped throttle rather than an account
2095
2038
  * limit.
@@ -2111,20 +2054,50 @@ function isModelBusyBody(body) {
2111
2054
  return body.includes(MODEL_BUSY_CODE);
2112
2055
  }
2113
2056
  /**
2114
- * Whether a failure body is the gateway's CHANNEL gate rather than the other
2115
- * `11128` ({@link conversationHeaders}' `conversation id missing`).
2057
+ * Whether a failure body belongs to the `11128` gate family AT ALL.
2116
2058
  *
2117
- * Both halves are required. The code alone would claim the conversation-id gate
2118
- * too, and its reply would then blame the prompt's opening for a request-header
2119
- * problem the plugin already fixed — a confidently wrong diagnosis is worse
2120
- * here than the generic `client` reply, because it sends the user to rewrite a
2121
- * system prompt that is not at fault.
2059
+ * The code is matched in its quoted-key form (`"code":11128`): a request id is
2060
+ * hexadecimal and can contain the digits `11128` by coincidence, and a bare
2061
+ * substring search over an arbitrary failure body is how a red herring gets
2062
+ * promoted into a diagnosis.
2063
+ */
2064
+ function isChannelGateFamilyBody(body) {
2065
+ return body.includes(CHANNEL_REJECT_CODE);
2066
+ }
2067
+ /**
2068
+ * Whether a failure body is the gateway's CHANNEL gate — i.e. whether it
2069
+ * belongs to the `11128` FAMILY.
2070
+ *
2071
+ * ISSUE #113, AND WHY THIS NOW ANSWERS "IS IT THE FAMILY" RATHER THAN "IS IT
2072
+ * THE PROMPT MEMBER". The family has three members (prompt fingerprint,
2073
+ * missing conversation id, missing system head) sharing one business code and
2074
+ * one `displayTips` sentence, so the `msg` wording used to be the only thing
2075
+ * that could tell them apart — and the wording is NOT reliable. Issue #113
2076
+ * measured a CN refusal whose `msg` read `conversation id missing` while the
2077
+ * three conversation-id headers were all present and correct; the refusal was
2078
+ * about the request's CONTENT.
2079
+ *
2080
+ * The old rule required the channel wording AND the absence of the sibling
2081
+ * wordings, so such a body was excluded from the channel gate entirely and
2082
+ * filed as a plain `client`. That was not cosmetic: `client` never enters the
2083
+ * plugin's two-step retry ladder (`forceNeutralHead`, then the invisible
2084
+ * perturbation), so the rescue path that exists for precisely this refusal
2085
+ * never ran. That is very likely why the reporters in issue #98's thread found
2086
+ * that upgrading changed nothing.
2087
+ *
2088
+ * Every member of this family is a CHANNEL-verification refusal — the
2089
+ * upstream's own shared sentence says so — so acting on the family is correct
2090
+ * whoever the member is. Which member it is decides only what the reply
2091
+ * ADVISES, and that is {@link channelGateMemberOf}'s job, decided by the
2092
+ * plugin's own evidence rather than by a wording the upstream may misreport.
2093
+ *
2094
+ * The code is matched in its quoted-key form (`"code":11128`): a request id is
2095
+ * hexadecimal and can contain the digits `11128` by coincidence, and a bare
2096
+ * substring search over an arbitrary failure body is how a red herring gets
2097
+ * promoted into a diagnosis.
2122
2098
  */
2123
2099
  function isChannelRejectBody(body) {
2124
- if (!body.includes(CHANNEL_REJECT_CODE)) return false;
2125
- const lower = body.toLowerCase();
2126
- if (OTHER_CHANNEL_CODE_GATES.some((marker) => lower.includes(marker))) return false;
2127
- return CHANNEL_REJECT_MARKERS.some((marker) => lower.includes(marker));
2100
+ return isChannelGateFamilyBody(body);
2128
2101
  }
2129
2102
  /**
2130
2103
  * Whether a failure body is the CONVERSATION-ID gate (`11128`).
@@ -2167,6 +2140,46 @@ function isFirstMessageGateBody(body) {
2167
2140
  return lower.includes(CHANNEL_REJECT_CODE) && lower.includes(FIRST_MESSAGE_GATE_MARKER);
2168
2141
  }
2169
2142
  /**
2143
+ * Which member of the `11128` family a body is, decided by the plugin's OWN
2144
+ * evidence rather than by the upstream's wording.
2145
+ *
2146
+ * THE EVIDENCE, IN ORDER OF STRENGTH.
2147
+ *
2148
+ * 1. The body carries the channel wording (`unapproved channel` /
2149
+ * `illegal api invocation`). That is the upstream naming the PROMPT gate
2150
+ * explicitly, so it is trusted.
2151
+ * 2. The body says `conversation id missing`. The wording is only believable
2152
+ * when the request did NOT carry the conversation-id triple; the plugin's
2153
+ * `chatHeaders` adds it to every chat request, so in practice it is always
2154
+ * there and the wording is always a misreport — which is issue #113's
2155
+ * exact measurement. The refusal is then about the CONTENT, i.e. the
2156
+ * PROMPT gate, which is also the only member the plugin can act on.
2157
+ * 3. The body names the first-message gate. That gate is about the request's
2158
+ * SHAPE, and `ensureSystemHead` guarantees a `system` head whenever the
2159
+ * request carries messages, so it is only reported as that gate when the
2160
+ * request genuinely had none.
2161
+ * 4. Otherwise: `unknown`. The family is still the family — the retry ladder
2162
+ * applies — but the reply says it cannot name the member rather than
2163
+ * guessing, because a confidently wrong diagnosis sends the user to edit a
2164
+ * prompt that is not at fault.
2165
+ *
2166
+ * @param body The failure body.
2167
+ * @param request What the plugin knows about the request it just sent.
2168
+ */
2169
+ function channelGateMemberOf(body, request) {
2170
+ const lower = body.toLowerCase();
2171
+ if (CHANNEL_REJECT_MARKERS.some((marker) => lower.includes(marker))) return "prompt";
2172
+ if (lower.includes(CONVERSATION_ID_GATE_MARKER)) {
2173
+ if (request?.hasConversationHeaders === false) return "headers";
2174
+ return "prompt";
2175
+ }
2176
+ if (lower.includes(FIRST_MESSAGE_GATE_MARKER)) {
2177
+ if (request?.hasSystemHead === false) return "first_message";
2178
+ return "unknown";
2179
+ }
2180
+ return "unknown";
2181
+ }
2182
+ /**
2170
2183
  * The OPENINGS of the `system` messages in a prepared request body.
2171
2184
  *
2172
2185
  * REMOVED in favour of {@link systemOpeningEvidenceOfBody}, which answers the
@@ -2181,7 +2194,15 @@ const SYSTEM_OPENING_LIMIT = 120;
2181
2194
  function clip(text, limit) {
2182
2195
  return text.length <= limit ? text : `${text.slice(0, limit - 1)}…`;
2183
2196
  }
2184
- /** Classify an upstream failure from its HTTP status and body excerpt. */
2197
+ /**
2198
+ * Classify an upstream failure from its HTTP status and body excerpt.
2199
+ *
2200
+ * The `11128` family is classified BY ITS CODE, not by the `msg` wording —
2201
+ * see {@link isChannelRejectBody} and {@link channelGateMemberOf}. Which
2202
+ * member of the family it is decides the ADVICE in the reply, never the
2203
+ * classification: every member is a channel-verification refusal that no
2204
+ * account, region or top-up can change.
2205
+ */
2185
2206
  function classifyUpstreamError(status, body) {
2186
2207
  if (status === 402) return "hard_credit";
2187
2208
  const lower = body.toLowerCase();
@@ -2233,6 +2254,7 @@ function parseUpstreamErrorDetail(body) {
2233
2254
  const detail = {};
2234
2255
  if (typeof raw.code === "number") detail.upstreamCode = raw.code;
2235
2256
  if (typeof raw.requestId === "string") detail.requestId = raw.requestId;
2257
+ if (typeof raw.msg === "string" && raw.msg !== "") detail.rawMsg = raw.msg;
2236
2258
  if (displayMsg !== void 0) detail.displayMsg = displayMsg;
2237
2259
  if (tips !== void 0) detail.tips = tips;
2238
2260
  const ext = raw.extError;
@@ -2711,6 +2733,28 @@ function forceNeutralHead(bodyJson) {
2711
2733
  */
2712
2734
  const ZERO_WIDTH_SPACE = "​";
2713
2735
  /**
2736
+ * Where an invisible break is inserted INSIDE a capitalised word pair, so a
2737
+ * bare product name stops matching as an exact substring.
2738
+ *
2739
+ * WHY THE PUNCTUATION RULE IS NOT ENOUGH. The boundary rules above only land
2740
+ * where there is a `.`, `!`, `?`, `,`, `;` — so a scanned message that ends
2741
+ * with a bare product name and no trailing punctuation (``… powered by
2742
+ * DeepSeek Harness ``, the host's own agent line, ending at end-of-text) is
2743
+ * left COMPLETELY UNCHANGED. `perturbChannelFingerprints` then answers
2744
+ * `undefined`, the shim spends no retry, and the fingerprint survives the
2745
+ * rescue — measured, and it is the shape issue #113's reporter is most likely
2746
+ * to hit, because that line is the first line of every DSH agent prompt.
2747
+ *
2748
+ * This rule breaks between a capitalised word and the following capitalised
2749
+ * word (“DeepSeek|Harness”, “Visual|Studio”), which covers product names
2750
+ * without touching ordinary prose: a sentence rarely has two capitalised
2751
+ * words adjacent UNLESS they are a proper name, and breaking a proper name is
2752
+ * exactly the intent. The model reads straight through U+200B, so what it
2753
+ * understands is unchanged — that is the whole point of using it for this
2754
+ * rather than rewriting the text.
2755
+ */
2756
+ const WORD_INTERNAL_BREAK = /([A-Z][A-Za-z0-9]*)(\s+)(?=[A-Z][A-Za-z0-9])/gu;
2757
+ /**
2714
2758
  * A body whose scanned message TEXT is perturbed just enough to break an exact
2715
2759
  * content fingerprint.
2716
2760
  *
@@ -2766,7 +2810,7 @@ function perturbChannelFingerprints(bodyJson) {
2766
2810
  let changed = false;
2767
2811
  const perturb = (text) => {
2768
2812
  if (text.includes(ZERO_WIDTH_SPACE)) return void 0;
2769
- const next = text.replace(/([.!?])(\s+)/gu, `$1${ZERO_WIDTH_SPACE}$2`).replace(/([。!?])/gu, `$1${ZERO_WIDTH_SPACE}`).replace(/([,;])(\s+)/gu, `$1${ZERO_WIDTH_SPACE}$2`);
2813
+ const next = text.replace(/([.!?])(\s+)/gu, `$1${ZERO_WIDTH_SPACE}$2`).replace(/([。!?])/gu, `$1${ZERO_WIDTH_SPACE}`).replace(/([,;])(\s+)/gu, `$1${ZERO_WIDTH_SPACE}$2`).replace(WORD_INTERNAL_BREAK, (whole, head, tail) => `${head}${ZERO_WIDTH_SPACE}${tail}`);
2770
2814
  return next === text ? void 0 : next;
2771
2815
  };
2772
2816
  for (const value of messages) {
@@ -3335,6 +3379,57 @@ function withPromos(models, data) {
3335
3379
  return promos.length === 0 ? models : attachPromos(models, promos);
3336
3380
  }
3337
3381
  /**
3382
+ * What the plugin can tell about the request it just sent, for telling the
3383
+ * `11128` family apart.
3384
+ *
3385
+ * TWO FACTS, both read from the request body rather than from the upstream's
3386
+ * `msg` — which issue #113 measured to be unreliable (a content refusal
3387
+ * reported as `conversation id missing`).
3388
+ *
3389
+ * - `hasSystemHead`: does the request open with a `system` message? The
3390
+ * plugin guarantees it does whenever the request carries messages
3391
+ * (`ensureSystemHead`), so a body claiming the first-message gate is only
3392
+ * believable when this is false.
3393
+ * - `hasConversationHeaders`: did the request carry the conversation-id
3394
+ * triple? `chatHeaders` always adds it, so this is true in practice — and
3395
+ * that is exactly what makes a `conversation id missing` wording
3396
+ * unbelievable.
3397
+ *
3398
+ * Never throws: a body that cannot be read answers "unknown", which is the
3399
+ * safe direction (the wording is then believed).
3400
+ */
3401
+ function requestEvidenceOf(bodyJson) {
3402
+ const hasConversationHeaders = true;
3403
+ let parsed;
3404
+ try {
3405
+ parsed = JSON.parse(bodyJson);
3406
+ } catch {
3407
+ return {
3408
+ hasSystemHead: false,
3409
+ hasConversationHeaders
3410
+ };
3411
+ }
3412
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return {
3413
+ hasSystemHead: false,
3414
+ hasConversationHeaders
3415
+ };
3416
+ const messages = parsed["messages"];
3417
+ if (!Array.isArray(messages)) return {
3418
+ hasSystemHead: false,
3419
+ hasConversationHeaders
3420
+ };
3421
+ const first = messages[0];
3422
+ if (typeof first !== "object" || first === null || Array.isArray(first)) return {
3423
+ hasSystemHead: false,
3424
+ hasConversationHeaders
3425
+ };
3426
+ const role = first["role"];
3427
+ return {
3428
+ hasSystemHead: role === "system" || role === "developer",
3429
+ hasConversationHeaders
3430
+ };
3431
+ }
3432
+ /**
3338
3433
  * Upstream HTTP client. One instance serves the whole plugin; requests take
3339
3434
  * the credential explicitly so token refreshes apply on the next call.
3340
3435
  */
@@ -5861,7 +5956,7 @@ function generationOf(version) {
5861
5956
  //#endregion
5862
5957
  //#region src/version.ts
5863
5958
  /** The npm package version this build was produced from. */
5864
- const WORKBUDDY_CONNECT_VERSION = "5.3.0";
5959
+ const WORKBUDDY_CONNECT_VERSION = "5.4.0";
5865
5960
  //#endregion
5866
5961
  //#region src/host-heartbeat.ts
5867
5962
  /**
@@ -6019,4 +6114,4 @@ async function clearHostHeartbeat() {
6019
6114
  await rm(workbuddyHostHeartbeatPath(), { force: true });
6020
6115
  }
6021
6116
  //#endregion
6022
- export { regionOf as $, parseWorkBuddyAuth as A, workbuddyAppExecutableCandidates as At, forceNeutralHead as B, defaultDesktopAuthCandidates as C, findWorkbuddyAppExecutableWithSource as Ct, isEncryptedCredentialError as D, macosNestedAppBundles as Dt, hasEncryptedCredentialFields as E, macosBundleExecutable as Et, WORKBUDDY_FALLBACK_SYSTEM_PROMPT as F, knownRegionOf as G, isConversationIdGateBody as H, WorkBuddyCredentialRejectedError as I, parseCreditMultiplier as J, modelIdOfBody as K, WorkBuddyUpstreamClient as L, workbuddyOwnAuthPath as M, resolveDshHome as Mt, CONTEXT_OVERFLOW_MARKER as N, withFileLock as Nt, isVaultAccountId as O, openEncryptedField as Ot, CREDENTIAL_REJECTED_CODE as P, writeFileAtomic as Pt, prepareChatBody as Q, classifyUpstreamError as R, authFileName as S, findWorkbuddyAppExecutable as St, defaultDesktopAuthPath as T, isWorkbuddyBundle as Tt, isCredentialRejectedError as U, isContextTooLongBody as V, isFirstMessageGateBody as W, parseUpstreamModel as X, parseReasoning as Y, perturbChannelFingerprints as Z, ENCRYPTED_CREDENTIAL_CODE as _, WORKBUDDY_APP_EXECUTABLE_ENV as _t, readHostHeartbeat as a, offDefaultFor as at, WorkBuddyCredentialStore as b, deriveAtRestKeyId as bt, WORKBUDDY_CONNECT_VERSION as c, withEffectiveOff as ct, FALLBACK_WORKBUDDY_MODELS_GLOBAL as d, pickImageModel as dt, attachBusyLevels as et, WorkBuddyCatalog as f, parseQueueAnswer as ft, withoutRouters as g, nativeModalityOf as gt, fallbackModelsFor as h, imageDefaultFor as ht, processStartTimeMs as i, lastRefusalFor as it, workbuddyAccountId as j, hostPeersInfo as jt, legacyWorkbuddyOwnAuthPath as k, readAtRestKey as kt, piAiRuntimeInfo as l, DEFAULT_IMAGE_SIZE as lt, deriveCatalog as m, NATIVE_MODALITY_BY_MODEL_ID as mt, clearHostHeartbeat as n, REFUSES_OFF_MODEL_IDS as nt, workbuddyHostHeartbeatPath as o, offRefused as ot, applyContextBudgets as p, queueNoticeText as pt, openingEvidenceOfBody as q, isHeartbeatProcessAlive as r, defaultOffModelIds as rt, writeHostHeartbeat as s, recordRefusal as st, WORKBUDDY_HOST_HEARTBEAT_FILENAME as t, busyDisplaySuffix as tt, FALLBACK_WORKBUDDY_MODELS as u, WorkBuddyImageError as ut, WORKBUDDY_AUTH_FILENAME as v, clearAtRestKeyCache as vt, defaultDesktopAuthDirs as w, isEncryptedFieldWrapper as wt, WorkBuddyEncryptedCredentialError as x, fetchAtRestKeyPayload as xt, WORKBUDDY_AUTH_FILE_ENV as y, deriveAtRestKey as yt, declaredTools as z };
6117
+ export { prepareChatBody as $, parseWorkBuddyAuth as A, readAtRestKey as At, declaredTools as B, defaultDesktopAuthCandidates as C, findWorkbuddyAppExecutable as Ct, isEncryptedCredentialError as D, macosBundleExecutable as Dt, hasEncryptedCredentialFields as E, isWorkbuddyBundle as Et, WORKBUDDY_FALLBACK_SYSTEM_PROMPT as F, writeFileAtomic as Ft, isFirstMessageGateBody as G, isContextTooLongBody as H, WorkBuddyCredentialRejectedError as I, openingEvidenceOfBody as J, knownRegionOf as K, WorkBuddyUpstreamClient as L, workbuddyOwnAuthPath as M, hostPeersInfo as Mt, CONTEXT_OVERFLOW_MARKER as N, resolveDshHome as Nt, isVaultAccountId as O, macosNestedAppBundles as Ot, CREDENTIAL_REJECTED_CODE as P, withFileLock as Pt, perturbChannelFingerprints as Q, channelGateMemberOf as R, authFileName as S, fetchAtRestKeyPayload as St, defaultDesktopAuthPath as T, isEncryptedFieldWrapper as Tt, isConversationIdGateBody as U, forceNeutralHead as V, isCredentialRejectedError as W, parseReasoning as X, parseCreditMultiplier as Y, parseUpstreamModel as Z, ENCRYPTED_CREDENTIAL_CODE as _, nativeModalityOf as _t, readHostHeartbeat as a, lastRefusalFor as at, WorkBuddyCredentialStore as b, deriveAtRestKey as bt, WORKBUDDY_CONNECT_VERSION as c, recordRefusal as ct, FALLBACK_WORKBUDDY_MODELS_GLOBAL as d, WorkBuddyImageError as dt, regionOf as et, WorkBuddyCatalog as f, pickImageModel as ft, withoutRouters as g, imageDefaultFor as gt, fallbackModelsFor as h, NATIVE_MODALITY_BY_MODEL_ID as ht, processStartTimeMs as i, defaultOffModelIds as it, workbuddyAccountId as j, workbuddyAppExecutableCandidates as jt, legacyWorkbuddyOwnAuthPath as k, openEncryptedField as kt, piAiRuntimeInfo as l, withEffectiveOff as lt, deriveCatalog as m, queueNoticeText as mt, clearHostHeartbeat as n, attachBusyLevels as nt, workbuddyHostHeartbeatPath as o, offDefaultFor as ot, applyContextBudgets as p, parseQueueAnswer as pt, modelIdOfBody as q, isHeartbeatProcessAlive as r, REFUSES_OFF_MODEL_IDS as rt, writeHostHeartbeat as s, offRefused as st, WORKBUDDY_HOST_HEARTBEAT_FILENAME as t, requestEvidenceOf as tt, FALLBACK_WORKBUDDY_MODELS as u, DEFAULT_IMAGE_SIZE as ut, WORKBUDDY_AUTH_FILENAME as v, WORKBUDDY_APP_EXECUTABLE_ENV as vt, defaultDesktopAuthDirs as w, findWorkbuddyAppExecutableWithSource as wt, WorkBuddyEncryptedCredentialError as x, deriveAtRestKeyId as xt, WORKBUDDY_AUTH_FILE_ENV as y, clearAtRestKeyCache as yt, classifyUpstreamError as z };
package/lib/index.d.ts CHANGED
@@ -1118,7 +1118,15 @@ interface WorkBuddyProbeAnswer {
1118
1118
  /** The live stream, only when `ok` is true. The caller drains and discards it. */
1119
1119
  response?: Response;
1120
1120
  }
1121
- /** Classify an upstream failure from its HTTP status and body excerpt. */
1121
+ /**
1122
+ * Classify an upstream failure from its HTTP status and body excerpt.
1123
+ *
1124
+ * The `11128` family is classified BY ITS CODE, not by the `msg` wording —
1125
+ * see {@link isChannelRejectBody} and {@link channelGateMemberOf}. Which
1126
+ * member of the family it is decides the ADVICE in the reply, never the
1127
+ * classification: every member is a channel-verification refusal that no
1128
+ * account, region or top-up can change.
1129
+ */
1122
1130
  declare function classifyUpstreamError(status: number, body: string): UpstreamErrorKind;
1123
1131
  /** Structured fields lifted out of a JSON upstream failure body. */
1124
1132
  interface UpstreamErrorDetail {
@@ -1126,6 +1134,17 @@ interface UpstreamErrorDetail {
1126
1134
  upstreamCode?: number;
1127
1135
  /** Upstream request id — the handle support asks for. */
1128
1136
  requestId?: string;
1137
+ /**
1138
+ * The upstream's RAW `msg`, kept beside {@link displayMsg}.
1139
+ *
1140
+ * On the `11128` family this is the DIAGNOSTIC half — it is what names which
1141
+ * gate fired ("conversation id missing", "first message is not system
1142
+ * prompt"), while `displayMsg` is the user-facing sentence the plugin must
1143
+ * NOT use as a diagnosis (issue #113 measured it to mislabel a content
1144
+ * refusal). Keeping both means a reply can echo what arrived AND say why it
1145
+ * is or is not believable, without one overwriting the other.
1146
+ */
1147
+ rawMsg?: string;
1129
1148
  /** Best human-facing message: `displayMsg.zh`, falling back to `.en`, then `msg`. */
1130
1149
  displayMsg?: string;
1131
1150
  /**
@@ -2974,6 +2993,18 @@ interface WorkBuddyWebModel {
2974
2993
  nativeContextWindow: number;
2975
2994
  maxTokens: number;
2976
2995
  creditMultiplier?: number;
2996
+ /**
2997
+ * The model's load level, as read from the upstream's own probe.
2998
+ *
2999
+ * Carried so the CARD can draw it as an ICON. The host's model picker cannot
3000
+ * (its name is a plain string), which is why the picker gets a character and
3001
+ * the card gets a picture.
3002
+ *
3003
+ * ABSENT means "not measured right now" and is NOT the same as `normal`: the
3004
+ * card must not draw a level the upstream never stated. It is deliberately
3005
+ * not persisted into `lastCatalog` — load is a fact about this minute.
3006
+ */
3007
+ busyLevel?: WorkBuddyBusyLevel;
2977
3008
  /**
2978
3009
  * Upstream's own image-input default from the latest refresh. Used ONLY to
2979
3010
  * pre-fill the image checkboxes when "Refresh from WorkBuddy" overwrites the
@@ -3141,6 +3172,14 @@ type WorkBuddyWebUsage =
3141
3172
  * must not be read as consent.
3142
3173
  */
3143
3174
  cliBridgeEnabled?: boolean;
3175
+ /**
3176
+ * The LIVE load level per model id, for the card's icon.
3177
+ *
3178
+ * A SEPARATE field from `models`, because the card's model list is the
3179
+ * SAVED roster (the one the user edits) and a saved roster has no load
3180
+ * level in it. Absent means "not measured right now" — never "normal".
3181
+ */
3182
+ busyLevels?: Readonly<Record<string, WorkBuddyBusyLevel>>;
3144
3183
  /** WHERE this region's readout sits (issue #62); absent means `composer`. */
3145
3184
  creditPosition?: WorkBuddyCreditPosition;
3146
3185
  message?: string;
@@ -3197,6 +3236,14 @@ type WorkBuddyWebUsage =
3197
3236
  * must not be read as consent.
3198
3237
  */
3199
3238
  cliBridgeEnabled?: boolean;
3239
+ /**
3240
+ * The LIVE load level per model id, for the card's icon.
3241
+ *
3242
+ * A SEPARATE field from `models`, because the card's model list is the
3243
+ * SAVED roster (the one the user edits) and a saved roster has no load
3244
+ * level in it. Absent means "not measured right now" — never "normal".
3245
+ */
3246
+ busyLevels?: Readonly<Record<string, WorkBuddyBusyLevel>>;
3200
3247
  /** WHERE this region's readout sits (issue #62); absent means `composer`. */
3201
3248
  creditPosition?: WorkBuddyCreditPosition;
3202
3249
  source?: 'desktop' | 'dsh';
@@ -3690,6 +3737,14 @@ interface WorkBuddyStatusRouteOptions {
3690
3737
  * deployments, and a stale `true` here would claim a spending capability is on.
3691
3738
  */
3692
3739
  cliBridgeEnabled?(): boolean;
3740
+ /**
3741
+ * The LIVE load level per model id, for the card's icon.
3742
+ *
3743
+ * Optional, and absent means "not measured": the card draws no badge rather
3744
+ * than a made-up one. A SEPARATE read because the card's model list is the
3745
+ * SAVED roster, which carries no load level (`src/busy.ts`).
3746
+ */
3747
+ busyLevels?(region: WorkBuddyRegion): Readonly<Record<string, WorkBuddyBusyLevel>>;
3693
3748
  /**
3694
3749
  * Send one real-volume request to each named model of a region and report what
3695
3750
  * came back.