dsh-connect-workbuddy 5.8.1 → 5.8.3
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 +66 -0
- package/README.en.md +1 -0
- package/README.md +1 -0
- package/lib/bin.js +1 -1
- package/lib/client.js +347 -8
- package/lib/{host-heartbeat-CM-09T4U.js → host-heartbeat-DzfWuVsm.js} +147 -13
- package/lib/index.d.ts +61 -1
- package/lib/index.js +189 -40
- package/package.json +1 -1
|
@@ -2115,6 +2115,27 @@ function normalizeVerdictText(text) {
|
|
|
2115
2115
|
return text.normalize("NFKC").replace(/[\u200b-\u200d\ufeff]/gu, "").replace(/\s+/gu, " ").trim().toLowerCase();
|
|
2116
2116
|
}
|
|
2117
2117
|
/**
|
|
2118
|
+
* A `11140` body whose display fields decode to NOTHING at all.
|
|
2119
|
+
*
|
|
2120
|
+
* THE FAILURE MODE THIS CATCHES. `isAccountRestrictedBody` needs the business
|
|
2121
|
+
* code AND a phrase in `displayMsg` / `displayTips` / `msg` — deliberately, so a
|
|
2122
|
+
* shared sentence cannot bench an account. The mirror risk is a MISS: if the
|
|
2123
|
+
* upstream renames those fields, escapes them differently, or moves the
|
|
2124
|
+
* restriction wording somewhere new, every restriction degrades silently to
|
|
2125
|
+
* `policy_reject` and a benched-worthy account keeps taking traffic with no log
|
|
2126
|
+
* line anywhere. A genuine content refusal always carries the fields (its whole
|
|
2127
|
+
* point is a human-facing sentence), so a `11140` that decodes to no display
|
|
2128
|
+
* text at all is an anomalous envelope shape, not a normal refusal — exactly the
|
|
2129
|
+
* narrow condition worth a warning. Bodies WITH display text that merely lack
|
|
2130
|
+
* the restriction phrase are the everyday `policy_reject` and stay silent.
|
|
2131
|
+
*
|
|
2132
|
+
* Pure and side-effect-free: the caller (the transport layer, which holds the
|
|
2133
|
+
* injected logger) decides what to do with the answer.
|
|
2134
|
+
*/
|
|
2135
|
+
function isUndecodablePolicyBody(body) {
|
|
2136
|
+
return isContentPolicyBody(body) && accountVerdictText(body) === "";
|
|
2137
|
+
}
|
|
2138
|
+
/**
|
|
2118
2139
|
* Business code of the MODEL-scoped throttle (`14003`, "too many requests").
|
|
2119
2140
|
*
|
|
2120
2141
|
* The quoted-key form for the same reason as the policy markers: a request id is
|
|
@@ -3342,12 +3363,22 @@ function modelIdOfBody(bodyJson) {
|
|
|
3342
3363
|
}
|
|
3343
3364
|
}
|
|
3344
3365
|
/**
|
|
3366
|
+
* The optional run of `billion-context` markers every injected-prefix match
|
|
3367
|
+
* tolerates in front of the marker text, and the built-in marker alternatives.
|
|
3368
|
+
*/
|
|
3369
|
+
const INJECTED_ACP_MARKER_RUN = "(?:<acp\\b[^>]*>m\\d{1,8}<\\/acp>[ \\t]*\\r?\\n?)*";
|
|
3370
|
+
const INJECTED_BUILT_IN_PREFIXES = "Time sampled while preparing turn \\d+, step \\d+:|Current runtime context\\.|<system-reminder>|\\[Compressed conversation section\\]|This is an efficiency nudge";
|
|
3371
|
+
/**
|
|
3345
3372
|
* Prefixes the DSH harness uses for the context it injects as `user` messages.
|
|
3346
3373
|
*
|
|
3347
3374
|
* MEASURED shapes, not guesses: the harness's time-context provider appends
|
|
3348
3375
|
* `Time sampled while preparing turn N, step M: …`, its runtime-context
|
|
3349
3376
|
* provider appends `Current runtime context. …`, and its reminders arrive as
|
|
3350
|
-
* `<system-reminder>` blocks. `billion-context`
|
|
3377
|
+
* `<system-reminder>` blocks. `billion-context` injects its compression record
|
|
3378
|
+
* and its efficiency note as `user` messages too (issue #133): the compressed
|
|
3379
|
+
* section opens with a bracketed `[Compressed conversation section]` heading
|
|
3380
|
+
* line, and the nudge opens with `This is an efficiency …`. `billion-context`
|
|
3381
|
+
* may additionally wrap each
|
|
3351
3382
|
* message in an `<acp …>mNNNNN</acp>` marker, so the pattern TOLERATES that
|
|
3352
3383
|
* marker in front of an injected block — the marker itself is never removed
|
|
3353
3384
|
* from an injected message (see {@link ACP_LEADING_TAG} for where a marker IS
|
|
@@ -3357,7 +3388,24 @@ function modelIdOfBody(bodyJson) {
|
|
|
3357
3388
|
* as it arrived — the alternative (moving "the last non-empty user message")
|
|
3358
3389
|
* would reorder real conversations from other clients.
|
|
3359
3390
|
*/
|
|
3360
|
-
const INJECTED_USER_PREFIX =
|
|
3391
|
+
const INJECTED_USER_PREFIX = new RegExp(`^${INJECTED_ACP_MARKER_RUN}(?:${INJECTED_BUILT_IN_PREFIXES})`, "u");
|
|
3392
|
+
/**
|
|
3393
|
+
* Escape a user-supplied prefix so it can be embedded as a literal alternative.
|
|
3394
|
+
*/
|
|
3395
|
+
function escapeRegExp(text) {
|
|
3396
|
+
return text.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
3397
|
+
}
|
|
3398
|
+
/**
|
|
3399
|
+
* The prefix pattern for one request: the built-ins plus any configured
|
|
3400
|
+
* extras, each extra matched literally at the same position the built-ins
|
|
3401
|
+
* match (after optional leading whitespace and the `billion-context` marker).
|
|
3402
|
+
* Returns the shared built-in pattern when there are no usable extras.
|
|
3403
|
+
*/
|
|
3404
|
+
function injectedPrefixPattern(extras) {
|
|
3405
|
+
const literals = (extras ?? []).filter((extra) => typeof extra === "string" && extra !== "");
|
|
3406
|
+
if (literals.length === 0) return INJECTED_USER_PREFIX;
|
|
3407
|
+
return new RegExp(`^${INJECTED_ACP_MARKER_RUN}(?:${INJECTED_BUILT_IN_PREFIXES}|${literals.map(escapeRegExp).join("|")})`, "u");
|
|
3408
|
+
}
|
|
3361
3409
|
/**
|
|
3362
3410
|
* The per-message marker `billion-context` puts in front of every message.
|
|
3363
3411
|
*
|
|
@@ -3431,9 +3479,9 @@ function stripLeadingAcpTag(content) {
|
|
|
3431
3479
|
};
|
|
3432
3480
|
}
|
|
3433
3481
|
/** Whether this message is one of the harness's injected context blocks. */
|
|
3434
|
-
function isInjectedUserMessage(message) {
|
|
3482
|
+
function isInjectedUserMessage(message, pattern) {
|
|
3435
3483
|
if (message["role"] !== "user") return false;
|
|
3436
|
-
return
|
|
3484
|
+
return pattern.test(textOfContent(message["content"]).replace(/^\s+/u, ""));
|
|
3437
3485
|
}
|
|
3438
3486
|
/**
|
|
3439
3487
|
* Move the trailing injected `user` messages above the real prompt.
|
|
@@ -3441,22 +3489,22 @@ function isInjectedUserMessage(message) {
|
|
|
3441
3489
|
* Returns `true` when the order changed, so the caller can serialize only once.
|
|
3442
3490
|
* Never throws, and a body with nothing to move keeps its exact message order.
|
|
3443
3491
|
*/
|
|
3444
|
-
function keepPromptLast(messages) {
|
|
3492
|
+
function keepPromptLast(messages, pattern) {
|
|
3445
3493
|
let promptIndex = -1;
|
|
3446
3494
|
for (let index = messages.length - 1; index >= 0; index--) {
|
|
3447
3495
|
const value = messages[index];
|
|
3448
3496
|
if (typeof value !== "object" || value === null || Array.isArray(value)) continue;
|
|
3449
3497
|
const message = value;
|
|
3450
3498
|
if (message["role"] !== "user") continue;
|
|
3451
|
-
if (isInjectedUserMessage(message)) continue;
|
|
3499
|
+
if (isInjectedUserMessage(message, pattern)) continue;
|
|
3452
3500
|
if (textOfContent(message["content"]).trim() === "") continue;
|
|
3453
3501
|
promptIndex = index;
|
|
3454
3502
|
break;
|
|
3455
3503
|
}
|
|
3456
3504
|
if (promptIndex < 0) return false;
|
|
3457
|
-
const trailing = messages.slice(promptIndex + 1).filter((value) => typeof value === "object" && value !== null && !Array.isArray(value) ? isInjectedUserMessage(value) : false);
|
|
3505
|
+
const trailing = messages.slice(promptIndex + 1).filter((value) => typeof value === "object" && value !== null && !Array.isArray(value) ? isInjectedUserMessage(value, pattern) : false);
|
|
3458
3506
|
if (trailing.length === 0) return false;
|
|
3459
|
-
const kept = messages.filter((value, index) => !(index > promptIndex && typeof value === "object" && value !== null && !Array.isArray(value) && isInjectedUserMessage(value)));
|
|
3507
|
+
const kept = messages.filter((value, index) => !(index > promptIndex && typeof value === "object" && value !== null && !Array.isArray(value) && isInjectedUserMessage(value, pattern)));
|
|
3460
3508
|
kept.splice(promptIndex, 0, ...trailing);
|
|
3461
3509
|
const prompt = kept[promptIndex + trailing.length];
|
|
3462
3510
|
if (typeof prompt === "object" && prompt !== null && !Array.isArray(prompt)) {
|
|
@@ -3501,13 +3549,97 @@ function prepareChatBody(source, options) {
|
|
|
3501
3549
|
if (message["role"] === "developer") message["role"] = "system";
|
|
3502
3550
|
}
|
|
3503
3551
|
ensureSystemHead(obj);
|
|
3504
|
-
if (options?.keepPromptLast === true && Array.isArray(obj["messages"])) keepPromptLast(obj["messages"]);
|
|
3552
|
+
if (options?.keepPromptLast === true && Array.isArray(obj["messages"])) keepPromptLast(obj["messages"], injectedPrefixPattern(options.extraInjectedPrefixes));
|
|
3505
3553
|
neutralizeChannelFingerprints(obj);
|
|
3506
3554
|
normalizeToolChoice(obj);
|
|
3507
3555
|
dropRefusedReasoning(obj);
|
|
3508
3556
|
return JSON.stringify(obj);
|
|
3509
3557
|
}
|
|
3510
3558
|
/**
|
|
3559
|
+
* The host's session-title prompt template, verbatim up to the JSON payload.
|
|
3560
|
+
*
|
|
3561
|
+
* MEASURED in the installed DSH desktop 0.2.1-rc.2 `app.asar`, twice: the
|
|
3562
|
+
* request builder interpolates exactly this prefix before the JSON array of
|
|
3563
|
+
* `{seq, text}` records, and the host's own format validator asserts the
|
|
3564
|
+
* session-event carries the same text. Every title plugin the host bundles
|
|
3565
|
+
* (`dsh-session-title-llm`, `…-first-prompt-llm`, `…-all-prompts-llm`) shares
|
|
3566
|
+
* the template, so one prefix covers all variants. `purpose: 'session-title'`
|
|
3567
|
+
* exists on the host's `GenerateOptions` type but does not travel on the
|
|
3568
|
+
* OpenAI wire, so body shape is the only discriminator the shim has.
|
|
3569
|
+
*/
|
|
3570
|
+
const SESSION_TITLE_PROMPT_PREFIX = "Generate the session title from this JSON array of human messages:\n";
|
|
3571
|
+
const SESSION_TITLE_PROMPT_HEAD = "Generate the session title from this JSON array of human messages:";
|
|
3572
|
+
/**
|
|
3573
|
+
* The ASCII-only head of the template, up to (not including) its newline.
|
|
3574
|
+
*
|
|
3575
|
+
* The full prefix contains a raw newline, which a serialized body escapes to
|
|
3576
|
+
* `\n`, so `prepared.includes(SESSION_TITLE_PROMPT_PREFIX)` would MISS every
|
|
3577
|
+
* real title. This head is all plain ASCII, so it appears verbatim in the
|
|
3578
|
+
* serialized body whenever the host sends the template as a single text part
|
|
3579
|
+
* — which its own format validator asserts (`content.length === 1`). It
|
|
3580
|
+
* therefore makes a prefilter without false negatives for the real shape; a
|
|
3581
|
+
* body split mid-template across array parts would slip past it and simply
|
|
3582
|
+
* keep the pre-rewrite behaviour. False positives (a conversation quoting the
|
|
3583
|
+
* template) are fine: the full detection below rejects them — the prefilter
|
|
3584
|
+
* only skips work, never changes a verdict.
|
|
3585
|
+
*/
|
|
3586
|
+
/**
|
|
3587
|
+
* Rewrite a recognized session-title request onto the hidden lightweight model.
|
|
3588
|
+
*
|
|
3589
|
+
* WHY (issues #132/#133). The host asks its title plugins' prompt through the
|
|
3590
|
+
* same provider the conversation uses, so the title of every new session is
|
|
3591
|
+
* billed at the main paid model's rate. The reporter measured the upstream's
|
|
3592
|
+
* hidden `wb-lite` at 0 credits for exactly these requests (real upstream
|
|
3593
|
+
* calls, counted toward request volume, only fired once per new session), so
|
|
3594
|
+
* the rewrite moves the title to that model.
|
|
3595
|
+
*
|
|
3596
|
+
* DETECTION is fail-closed: the body is rewritten only when it has NO
|
|
3597
|
+
* assistant or tool messages and EXACTLY ONE `user` message, and that message
|
|
3598
|
+
* opens with the host's title template above. A real conversation can hardly
|
|
3599
|
+
* collide with the template (the host's own validator asserts it), and the
|
|
3600
|
+
* failure direction if the host ever rewords it is safe: the rewrite simply
|
|
3601
|
+
* stops, and the title is billed as before. `keepPromptLast` is unaffected —
|
|
3602
|
+
* the template prefix is not an injected-context marker, so a title body is
|
|
3603
|
+
* never reordered.
|
|
3604
|
+
*
|
|
3605
|
+
* WHAT is rewritten: the model id and the reasoning fields the lightweight
|
|
3606
|
+
* model does not accept. Everything else — `stream`, `max_tokens`, the
|
|
3607
|
+
* identity headers, every message byte — is untouched, so the other switches
|
|
3608
|
+
* keep behaving exactly as they do for conversation requests.
|
|
3609
|
+
*
|
|
3610
|
+
* Returns the original string unchanged when not enabled or not a title
|
|
3611
|
+
* request, so the caller does not need a separate shape check.
|
|
3612
|
+
*/
|
|
3613
|
+
function rewriteSessionTitleModel(prepared, options) {
|
|
3614
|
+
if (options?.enabled !== true) return prepared;
|
|
3615
|
+
if (!prepared.includes(SESSION_TITLE_PROMPT_HEAD)) return prepared;
|
|
3616
|
+
let body;
|
|
3617
|
+
try {
|
|
3618
|
+
body = JSON.parse(prepared);
|
|
3619
|
+
} catch {
|
|
3620
|
+
return prepared;
|
|
3621
|
+
}
|
|
3622
|
+
if (typeof body !== "object" || body === null || Array.isArray(body)) return prepared;
|
|
3623
|
+
const obj = body;
|
|
3624
|
+
const messages = obj["messages"];
|
|
3625
|
+
if (!Array.isArray(messages)) return prepared;
|
|
3626
|
+
let titleText;
|
|
3627
|
+
for (const value of messages) {
|
|
3628
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return prepared;
|
|
3629
|
+
const message = value;
|
|
3630
|
+
if (message["role"] === "assistant" || message["role"] === "tool") return prepared;
|
|
3631
|
+
if (message["role"] !== "user") continue;
|
|
3632
|
+
if (titleText !== void 0) return prepared;
|
|
3633
|
+
titleText = textOfContent(message["content"]);
|
|
3634
|
+
}
|
|
3635
|
+
if (titleText === void 0) return prepared;
|
|
3636
|
+
if (!titleText.replace(/^\s+/u, "").startsWith(SESSION_TITLE_PROMPT_PREFIX)) return prepared;
|
|
3637
|
+
obj["model"] = typeof options.model === "string" && options.model.trim() !== "" ? options.model.trim() : "wb-lite";
|
|
3638
|
+
delete obj["reasoning_effort"];
|
|
3639
|
+
delete obj["reasoning"];
|
|
3640
|
+
return JSON.stringify(obj);
|
|
3641
|
+
}
|
|
3642
|
+
/**
|
|
3511
3643
|
* Remove `reasoning_effort: "off"` when it would only ever fail (issue #83).
|
|
3512
3644
|
*
|
|
3513
3645
|
* The refusal table is keyed by exact model id, but the model a request
|
|
@@ -3957,7 +4089,7 @@ var WorkBuddyUpstreamClient = class {
|
|
|
3957
4089
|
onFallback;
|
|
3958
4090
|
identity;
|
|
3959
4091
|
/**
|
|
3960
|
-
* Reports
|
|
4092
|
+
* Reports an anomaly the reader or classifier had to tolerate.
|
|
3961
4093
|
*
|
|
3962
4094
|
* Injected rather than logged here because this module holds no logger on
|
|
3963
4095
|
* purpose — it is pure transport + parsing, so it stays importable from
|
|
@@ -3995,10 +4127,12 @@ var WorkBuddyUpstreamClient = class {
|
|
|
3995
4127
|
response
|
|
3996
4128
|
};
|
|
3997
4129
|
const text = (await response.text()).slice(0, ERROR_BODY_LIMIT);
|
|
4130
|
+
const kind = classifyUpstreamError(response.status, text);
|
|
4131
|
+
if (isUndecodablePolicyBody(text)) this.onFallback?.(`11140 失败体没有任何可解码的 displayMsg/displayTips/msg 字段,若上游调整了限制措辞或字段名,账号停权判定会全部漏判。失败体片段:${text.slice(0, 200)}`);
|
|
3998
4132
|
return {
|
|
3999
4133
|
ok: false,
|
|
4000
4134
|
status: response.status,
|
|
4001
|
-
kind
|
|
4135
|
+
kind,
|
|
4002
4136
|
message: text,
|
|
4003
4137
|
detail: parseUpstreamErrorDetail(text)
|
|
4004
4138
|
};
|
|
@@ -6806,7 +6940,7 @@ function describeRosterSeenPayload(raw) {
|
|
|
6806
6940
|
//#endregion
|
|
6807
6941
|
//#region src/version.ts
|
|
6808
6942
|
/** The npm package version this build was produced from. */
|
|
6809
|
-
const WORKBUDDY_CONNECT_VERSION = "5.8.
|
|
6943
|
+
const WORKBUDDY_CONNECT_VERSION = "5.8.3";
|
|
6810
6944
|
//#endregion
|
|
6811
6945
|
//#region src/host-heartbeat.ts
|
|
6812
6946
|
/**
|
|
@@ -6964,4 +7098,4 @@ async function clearHostHeartbeat() {
|
|
|
6964
7098
|
await rm(workbuddyHostHeartbeatPath(), { force: true });
|
|
6965
7099
|
}
|
|
6966
7100
|
//#endregion
|
|
6967
|
-
export { parseCreditMultiplier as $, isEncryptedCredentialError as A,
|
|
7101
|
+
export { parseCreditMultiplier as $, isEncryptedCredentialError as A, isEncryptedFieldWrapper as At, WorkBuddyCredentialRejectedError as B, writeFileAtomic as Bt, WorkBuddyCredentialStore as C, WORKBUDDY_APP_EXECUTABLE_ENV as Ct, defaultDesktopAuthDirs as D, fetchAtRestKeyPayload as Dt, defaultDesktopAuthCandidates as E, deriveAtRestKeyId as Et, workbuddyAccountId as F, readAtRestKey as Ft, forceNeutralHead as G, channelGateMemberOf as H, workbuddyOwnAuthPath as I, workbuddyAppExecutableCandidates as It, isCredentialRejectedError as J, isContextTooLongBody as K, CONTEXT_OVERFLOW_MARKER as L, hostPeersInfo as Lt, legacyWorkbuddyOwnAuthPath as M, macosBundleExecutable as Mt, parseWorkBuddyAuth as N, macosNestedAppBundles as Nt, defaultDesktopAuthPath as O, findWorkbuddyAppExecutable as Ot, preferredAccountName as P, openEncryptedField as Pt, openingEvidenceOfBody as Q, CREDENTIAL_REJECTED_CODE as R, resolveDshHome as Rt, WORKBUDDY_AUTH_FILE_ENV as S, nativeModalityOf as St, authFileName as T, deriveAtRestKey as Tt, classifyUpstreamError as U, WorkBuddyUpstreamClient as V, declaredTools as W, knownRegionOf as X, isFirstMessageGateBody as Y, modelIdOfBody as Z, deriveCatalog as _, pickImageModel as _t, readHostHeartbeat as a, requestEvidenceOf as at, ENCRYPTED_CREDENTIAL_CODE as b, NATIVE_MODALITY_BY_MODEL_ID as bt, WORKBUDDY_CONNECT_VERSION as c, REFUSES_OFF_MODEL_IDS as ct, rosterSeenPath as d, offDefaultFor as dt, parseReasoning as et, piAiRuntimeInfo as f, offRefused as ft, applyContextBudgets as g, WorkBuddyImageError as gt, WorkBuddyCatalog as h, DEFAULT_IMAGE_SIZE as ht, processStartTimeMs as i, regionOf as it, isVaultAccountId as j, isWorkbuddyBundle as jt, hasEncryptedCredentialFields as k, findWorkbuddyAppExecutableWithSource as kt, createFileRosterSeenStore as l, defaultOffModelIds as lt, FALLBACK_WORKBUDDY_MODELS_GLOBAL as m, withEffectiveOff as mt, clearHostHeartbeat as n, perturbChannelFingerprints as nt, workbuddyHostHeartbeatPath as o, rewriteSessionTitleModel as ot, FALLBACK_WORKBUDDY_MODELS as p, recordRefusal as pt, isConversationIdGateBody as q, isHeartbeatProcessAlive as r, prepareChatBody as rt, writeHostHeartbeat as s, attachBusyLevels as st, WORKBUDDY_HOST_HEARTBEAT_FILENAME as t, parseUpstreamModel as tt, describeRosterSeenPayload as u, lastRefusalFor as ut, fallbackModelsFor as v, parseQueueAnswer as vt, WorkBuddyEncryptedCredentialError as w, clearAtRestKeyCache as wt, WORKBUDDY_AUTH_FILENAME as x, imageDefaultFor as xt, withoutRouters as y, queueNoticeText as yt, WORKBUDDY_FALLBACK_SYSTEM_PROMPT as z, withFileLock as zt };
|
package/lib/index.d.ts
CHANGED
|
@@ -1403,6 +1403,20 @@ interface PrepareChatBodyOptions {
|
|
|
1403
1403
|
* conversation whose trailing user messages are real turns.
|
|
1404
1404
|
*/
|
|
1405
1405
|
keepPromptLast?: boolean;
|
|
1406
|
+
/**
|
|
1407
|
+
* Additional leading markers that mark a trailing `user` message as injected
|
|
1408
|
+
* context, one literal prefix per entry.
|
|
1409
|
+
*
|
|
1410
|
+
* WHY. The built-in pattern at {@link INJECTED_USER_PREFIX} knows only the
|
|
1411
|
+
* prefixes measured at the time it was written; a companion plugin (or a DSH
|
|
1412
|
+
* update) that starts injecting new text would silently fall back to having
|
|
1413
|
+
* its blocks recorded as the prompt. Each entry is matched LITERALLY at the
|
|
1414
|
+
* start of the message text (after leading whitespace and the
|
|
1415
|
+
* `billion-context` marker, which the built-in pattern tolerates) and OR'ed
|
|
1416
|
+
* with the built-ins; entries are escaped, so no configuration value can turn
|
|
1417
|
+
* the pattern invalid.
|
|
1418
|
+
*/
|
|
1419
|
+
extraInjectedPrefixes?: readonly string[];
|
|
1406
1420
|
}
|
|
1407
1421
|
/**
|
|
1408
1422
|
* Normalize an OpenAI chat-completions body for the WorkBuddy upstream: force
|
|
@@ -1468,7 +1482,7 @@ declare class WorkBuddyUpstreamClient {
|
|
|
1468
1482
|
private readonly onFallback?;
|
|
1469
1483
|
private readonly identity;
|
|
1470
1484
|
/**
|
|
1471
|
-
* Reports
|
|
1485
|
+
* Reports an anomaly the reader or classifier had to tolerate.
|
|
1472
1486
|
*
|
|
1473
1487
|
* Injected rather than logged here because this module holds no logger on
|
|
1474
1488
|
* purpose — it is pure transport + parsing, so it stays importable from
|
|
@@ -1973,6 +1987,24 @@ interface WorkBuddyShimOptions {
|
|
|
1973
1987
|
* Default false: it changes bytes the upstream counts, so it is opt-in.
|
|
1974
1988
|
*/
|
|
1975
1989
|
keepPromptLast?: boolean;
|
|
1990
|
+
/**
|
|
1991
|
+
* Extra literal prefixes (issue #133) treated like the harness's injected
|
|
1992
|
+
* markers under `keepPromptLast`, for context blocks a companion plugin
|
|
1993
|
+
* injects that the built-in list does not know about. Only read when
|
|
1994
|
+
* `keepPromptLast` is on.
|
|
1995
|
+
*/
|
|
1996
|
+
extraInjectedPrefixes?: readonly string[];
|
|
1997
|
+
/**
|
|
1998
|
+
* Move the host's session-title request onto the hidden lightweight model
|
|
1999
|
+
* (issues #132/#133). Disabled by default; when enabled, a body recognized
|
|
2000
|
+
* as the one-message title prompt is re-pointed at `model` (default
|
|
2001
|
+
* `wb-lite`, measured 0 credits) instead of billing the main paid model.
|
|
2002
|
+
* See `rewriteSessionTitleModel` for the fail-closed detection rules.
|
|
2003
|
+
*/
|
|
2004
|
+
sessionTitle?: {
|
|
2005
|
+
enabled?: boolean;
|
|
2006
|
+
model?: string;
|
|
2007
|
+
};
|
|
1976
2008
|
/**
|
|
1977
2009
|
* Pool failover for chat requests. Absent means no failover at all — the
|
|
1978
2010
|
* pre-pool behaviour, where the selected account serves and its failure is
|
|
@@ -4825,6 +4857,34 @@ interface Config {
|
|
|
4825
4857
|
* an explicit opt-in rather than a silent change on upgrade.
|
|
4826
4858
|
*/
|
|
4827
4859
|
keepPromptLast?: boolean;
|
|
4860
|
+
/**
|
|
4861
|
+
* Extra literal prefixes (issue #133) treated like the harness's injected
|
|
4862
|
+
* context markers when `keepPromptLast` is on, for injected blocks a
|
|
4863
|
+
* companion plugin produces that the built-in list does not recognize. Each
|
|
4864
|
+
* entry is matched literally at the start of a trailing `user` message.
|
|
4865
|
+
*/
|
|
4866
|
+
extraInjectedPrefixes?: string[];
|
|
4867
|
+
/**
|
|
4868
|
+
* Move the host's session-title request onto the hidden lightweight model
|
|
4869
|
+
* (issues #132/#133). Off by default: it is a real upstream request either
|
|
4870
|
+
* way, and the rewrite changes which bucket it is billed against.
|
|
4871
|
+
*/
|
|
4872
|
+
sessionTitle?: {
|
|
4873
|
+
/**
|
|
4874
|
+
* Whether title requests are re-pointed at {@link sessionTitle.model}.
|
|
4875
|
+
* The host still fires the request exactly once per new session; the
|
|
4876
|
+
* reporter measured the lightweight model at 0 credits for these calls
|
|
4877
|
+
* (request volume still counts, and the evidence is reporter-level, not
|
|
4878
|
+
* reproduced by this plugin).
|
|
4879
|
+
*/
|
|
4880
|
+
enabled?: boolean;
|
|
4881
|
+
/**
|
|
4882
|
+
* Target model for the rewritten title request. Defaults to `wb-lite`,
|
|
4883
|
+
* the hidden model the reporter measured; configurable in case the id
|
|
4884
|
+
* changes or a region exposes a different one.
|
|
4885
|
+
*/
|
|
4886
|
+
model?: string;
|
|
4887
|
+
};
|
|
4828
4888
|
}
|
|
4829
4889
|
/**
|
|
4830
4890
|
* Mark a schema's field as volatile on the DSH lines that support it.
|