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.
- package/CHANGELOG.md +55 -1
- package/lib/bin.js +1 -1
- package/lib/client.js +371 -12
- package/lib/{host-heartbeat-_EvGiAek.js → host-heartbeat-D5FWM_8k.js} +174 -79
- package/lib/index.d.ts +56 -1
- package/lib/index.js +7238 -6808
- package/package.json +1 -1
|
@@ -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
|
|
2021
|
+
* The wordings that identify the OTHER two members of the `11128` family.
|
|
2079
2022
|
*
|
|
2080
|
-
*
|
|
2081
|
-
*
|
|
2082
|
-
*
|
|
2083
|
-
*
|
|
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
|
|
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
|
|
2115
|
-
* `11128` ({@link conversationHeaders}' `conversation id missing`).
|
|
2057
|
+
* Whether a failure body belongs to the `11128` gate family AT ALL.
|
|
2116
2058
|
*
|
|
2117
|
-
*
|
|
2118
|
-
*
|
|
2119
|
-
*
|
|
2120
|
-
*
|
|
2121
|
-
|
|
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
|
-
|
|
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
|
-
/**
|
|
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.
|
|
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 {
|
|
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
|
-
/**
|
|
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.
|