dsh-workbuddy-bridge 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -54
- package/README_en.md +61 -63
- package/lib/bin.js +1 -1
- package/lib/client.js +671 -133
- package/lib/index.d.ts +175 -1
- package/lib/index.js +581 -86
- package/lib/{variants-DP0Ho0Tc.js → variants-3SmUZ0e_.js} +40 -18
- package/locale/README.md +10 -0
- package/package.json +40 -28
package/lib/index.d.ts
CHANGED
|
@@ -1,9 +1,51 @@
|
|
|
1
1
|
import z from "@deepseek-ai/schemastery";
|
|
2
2
|
import "@earendil-works/pi-ai";
|
|
3
3
|
import { PiAiAdapter } from "@deepseek-ai/dsh-llm-pi-ai";
|
|
4
|
+
import { IncomingMessage, ServerResponse } from "node:http";
|
|
4
5
|
import { Context, Volatile } from "@deepseek-ai/cordis";
|
|
5
6
|
import { AttachmentStore } from "@deepseek-ai/dsh-attachment";
|
|
6
7
|
//#region src/shared/paths.d.ts
|
|
8
|
+
/**
|
|
9
|
+
* Plugin-owned per-message credit route.
|
|
10
|
+
*
|
|
11
|
+
* A read-only GET taking `?sessionId=…`, answering `messageId → credit` for one
|
|
12
|
+
* Session. It is its own route rather than a field of the status document
|
|
13
|
+
* because the two have different lifetimes: the status document is per
|
|
14
|
+
* *account* and is re-read when the account changes, while credit accounting
|
|
15
|
+
* is per *Session* and grows as messages are sent.
|
|
16
|
+
*
|
|
17
|
+
* **One route for both variants, on purpose.** The accounting is attributed to
|
|
18
|
+
* a DSH `messageId` before it is stored, and a message id belongs to one
|
|
19
|
+
* Session regardless of which provider served it — a conversation can switch
|
|
20
|
+
* between the two WorkBuddy products (or away from both) mid-thread. A route
|
|
21
|
+
* per variant would force the message row to know which provider answered a
|
|
22
|
+
* message *it is merely labelling*, and would strand every message whose turn
|
|
23
|
+
* ran under the other variant.
|
|
24
|
+
*
|
|
25
|
+
* No key is required: the route only reads what this plugin already observed on
|
|
26
|
+
* its own loopback stream, carries no credential material, and answers loopback
|
|
27
|
+
* requests through the same Host/Origin guard as the status route.
|
|
28
|
+
*/
|
|
29
|
+
declare const WORKBUDDY_CREDIT_PATH = "/plugins/dsh-workbuddy-bridge/credit";
|
|
30
|
+
/**
|
|
31
|
+
* The credit route's answer: one Session's per-message costs.
|
|
32
|
+
*
|
|
33
|
+
* `credits` is keyed by DSH `messageId` and is always present (an empty object
|
|
34
|
+
* when nothing is recorded), so the browser half reads one shape instead of
|
|
35
|
+
* distinguishing "no data yet" from "no costs".
|
|
36
|
+
*
|
|
37
|
+
* A message *absent* from `credits` means this plugin observed no cost for it —
|
|
38
|
+
* a message predating the feature, a stream that failed before its final frame,
|
|
39
|
+
* or an upstream answer that carried no `credit` field. It never means zero, so
|
|
40
|
+
* the row renders nothing rather than a confident `0`.
|
|
41
|
+
*
|
|
42
|
+
* Distinct from {@link WorkBuddyWebCredits}, which is the account's *remaining*
|
|
43
|
+
* balance for the card. This one is what individual messages *spent*.
|
|
44
|
+
*/
|
|
45
|
+
interface WorkBuddyWebSessionCredits {
|
|
46
|
+
sessionId: string;
|
|
47
|
+
credits: Readonly<Record<string, number>>;
|
|
48
|
+
}
|
|
7
49
|
/**
|
|
8
50
|
* Why no credential is usable, as a closed enum the browser half switches on.
|
|
9
51
|
*
|
|
@@ -1234,6 +1276,15 @@ interface WorkBuddyShimOptions {
|
|
|
1234
1276
|
client: Pick<WorkBuddyUpstreamClient, 'chatStream'>;
|
|
1235
1277
|
catalog: WorkBuddyCatalog;
|
|
1236
1278
|
logger?: ShimLogger;
|
|
1279
|
+
/**
|
|
1280
|
+
* Observe what one upstream response cost, as the upstream reports it.
|
|
1281
|
+
*
|
|
1282
|
+
* Called at most once per response, at the frame carrying the number, with
|
|
1283
|
+
* the stream's own response id (`cmb-…`) — the same id DSH records on the
|
|
1284
|
+
* assistant message, which is what lets the Host half attribute a cost to a
|
|
1285
|
+
* message instead of guessing.
|
|
1286
|
+
*/
|
|
1287
|
+
observeCredit?: (responseId: string, credit: number) => void;
|
|
1237
1288
|
}
|
|
1238
1289
|
/**
|
|
1239
1290
|
* Start the loopback endpoint. Requests carry any bearer; the loopback bind
|
|
@@ -1292,6 +1343,129 @@ interface WorkBuddyAdapter {
|
|
|
1292
1343
|
*/
|
|
1293
1344
|
declare function createWorkBuddyAdapter(options: WorkBuddyAdapterOptions): WorkBuddyAdapter;
|
|
1294
1345
|
//#endregion
|
|
1346
|
+
//#region src/llm/credit-log.d.ts
|
|
1347
|
+
/**
|
|
1348
|
+
* Per-message credit accounting, as two plain registries the Host half fills.
|
|
1349
|
+
*
|
|
1350
|
+
* WorkBuddy reports what a message actually cost as a `credit` field on the
|
|
1351
|
+
* last usage frame of its OpenAI-compatible SSE stream. That number is
|
|
1352
|
+
* **server-supplied**: the desktop app accumulates `cost.amount` stage
|
|
1353
|
+
* increments and never multiplies tokens by the catalog's `x0.79` multiplier
|
|
1354
|
+
* (the multiplier is display copy). So the plugin must not invent a formula —
|
|
1355
|
+
* it observes the number where it exists.
|
|
1356
|
+
*
|
|
1357
|
+
* The field travels on the stream the shim already proxies, and the same
|
|
1358
|
+
* stream carries the upstream response id (`cmb-…`) that DSH records on the
|
|
1359
|
+
* assistant message as `source.replayState.response.responseId`. That pair is
|
|
1360
|
+
* what makes attribution exact rather than guessed:
|
|
1361
|
+
*
|
|
1362
|
+
* ```text
|
|
1363
|
+
* SSE id + SSE usage.credit ──► creditLog.record(id, credit)
|
|
1364
|
+
* │
|
|
1365
|
+
* session/event assistant/message ─────┴──► index.set(sessionId, messageId, credit)
|
|
1366
|
+
* (messageId, responseId) │
|
|
1367
|
+
* browser row fetches by messageId
|
|
1368
|
+
* ```
|
|
1369
|
+
*
|
|
1370
|
+
* Both structures are pure data with no Host dependencies, so the matching
|
|
1371
|
+
* rules are unit-testable without a Session, a socket, or an upstream.
|
|
1372
|
+
*
|
|
1373
|
+
* @module dsh-workbuddy-bridge/credit-log
|
|
1374
|
+
*/
|
|
1375
|
+
/**
|
|
1376
|
+
* Bounded `responseId → credit` registry, with one-shot waiters.
|
|
1377
|
+
*
|
|
1378
|
+
* The waiter exists because the two writes race in principle: the shim sees the
|
|
1379
|
+
* credit when the last SSE frame arrives, while the Session event that names
|
|
1380
|
+
* the response id is appended when the stream settles. Either order must
|
|
1381
|
+
* produce the same attribution, so a lookup that misses registers interest and
|
|
1382
|
+
* is answered by the next `record` instead of silently reporting "unknown".
|
|
1383
|
+
*/
|
|
1384
|
+
declare class WorkBuddyCreditLog {
|
|
1385
|
+
private readonly limit;
|
|
1386
|
+
/**
|
|
1387
|
+
* Insertion-ordered, so eviction is FIFO. A `Map` is used rather than an
|
|
1388
|
+
* object because the response id is attacker-influenced text: a plain object
|
|
1389
|
+
* would let `__proto__` reach the prototype chain.
|
|
1390
|
+
*/
|
|
1391
|
+
private readonly credits;
|
|
1392
|
+
/** Pending attributions, keyed by response id; each resolves at most once. */
|
|
1393
|
+
private readonly waiters;
|
|
1394
|
+
constructor(limit?: number);
|
|
1395
|
+
/**
|
|
1396
|
+
* Remember what one response cost.
|
|
1397
|
+
*
|
|
1398
|
+
* A non-finite or negative value is refused rather than stored: the field is
|
|
1399
|
+
* remote input, and a `NaN` reaching the browser would render as `NaN` in the
|
|
1400
|
+
* credit label instead of being reported as unknown.
|
|
1401
|
+
*/
|
|
1402
|
+
record(responseId: string, credit: number): void;
|
|
1403
|
+
/** What one response cost, or undefined when it was never observed. */
|
|
1404
|
+
lookup(responseId: string): number | undefined;
|
|
1405
|
+
/**
|
|
1406
|
+
* Call `onCredit` once, when this response's credit becomes known.
|
|
1407
|
+
*
|
|
1408
|
+
* @returns a canceller; calling it after the credit landed is a no-op.
|
|
1409
|
+
*/
|
|
1410
|
+
await(responseId: string, onCredit: (credit: number) => void): () => void;
|
|
1411
|
+
/** Drop the oldest entries until the log is inside its bound. */
|
|
1412
|
+
private evict;
|
|
1413
|
+
}
|
|
1414
|
+
/**
|
|
1415
|
+
* `sessionId → messageId → credit`, the shape the browser row reads.
|
|
1416
|
+
*
|
|
1417
|
+
* Nested rather than a flat `messageId → credit` map so the route can answer
|
|
1418
|
+
* one Session without shipping every Session's accounting, and so a Session's
|
|
1419
|
+
* rows can be dropped as a unit.
|
|
1420
|
+
*/
|
|
1421
|
+
declare class WorkBuddySessionCreditIndex {
|
|
1422
|
+
private readonly perSessionLimit;
|
|
1423
|
+
/** Per-Session maps, each bounded independently. */
|
|
1424
|
+
private readonly sessions;
|
|
1425
|
+
constructor(perSessionLimit?: number);
|
|
1426
|
+
/** Attribute one message's cost, refreshing its position in its Session. */
|
|
1427
|
+
set(sessionId: string, messageId: string, credit: number): void;
|
|
1428
|
+
/**
|
|
1429
|
+
* One Session's accounting as a plain object, or an empty object when the
|
|
1430
|
+
* Session has none. Always an object, never undefined: the browser half then
|
|
1431
|
+
* needs no "no data yet" branch distinct from "no credits recorded".
|
|
1432
|
+
*/
|
|
1433
|
+
forSession(sessionId: string): Record<string, number>;
|
|
1434
|
+
/** Forget one Session's accounting, e.g. when it leaves the store. */
|
|
1435
|
+
delete(sessionId: string): void;
|
|
1436
|
+
}
|
|
1437
|
+
//#endregion
|
|
1438
|
+
//#region src/web/credit.d.ts
|
|
1439
|
+
/** Constructor dependencies. */
|
|
1440
|
+
interface WorkBuddyCreditRouteOptions {
|
|
1441
|
+
/** The accounting to read from; see {@link WorkBuddySessionCreditIndex}. */
|
|
1442
|
+
credits: WorkBuddySessionCreditIndex;
|
|
1443
|
+
/**
|
|
1444
|
+
* Route path to mount, defaulting to the CN variant's. The international
|
|
1445
|
+
* variant passes its own so the two halves never share a route: the browser
|
|
1446
|
+
* bundle and the host bundle are built independently, and a computed path is
|
|
1447
|
+
* one build-config drift away from asking a route that was never mounted.
|
|
1448
|
+
*/
|
|
1449
|
+
path?: string;
|
|
1450
|
+
}
|
|
1451
|
+
/**
|
|
1452
|
+
* One Session's accounting.
|
|
1453
|
+
*
|
|
1454
|
+
* A missing `sessionId` is answered with an empty document rather than an
|
|
1455
|
+
* error: the browser half asks on behalf of whatever Session it is rendering,
|
|
1456
|
+
* and "this Session has no recorded costs" is a normal, silent state — the row
|
|
1457
|
+
* simply shows no label.
|
|
1458
|
+
*
|
|
1459
|
+
* @param deps - the accounting index.
|
|
1460
|
+
* @param sessionId - the Session to answer for.
|
|
1461
|
+
* @returns the document the browser half renders from.
|
|
1462
|
+
*/
|
|
1463
|
+
declare function workBuddySessionCredits(deps: WorkBuddyCreditRouteOptions, sessionId: string): WorkBuddyWebSessionCredits;
|
|
1464
|
+
/** The credit route's request handler, extracted so tests can mount it bare. */
|
|
1465
|
+
declare function workBuddyCreditHandler(deps: WorkBuddyCreditRouteOptions): (req: IncomingMessage, res: ServerResponse) => Promise<void>;
|
|
1466
|
+
/** Mount the GET credit route on an optional webServer context. */
|
|
1467
|
+
declare function registerWorkBuddyCreditRoute(ctx: Context, deps: WorkBuddyCreditRouteOptions): void;
|
|
1468
|
+
//#endregion
|
|
1295
1469
|
//#region src/catalog/store.d.ts
|
|
1296
1470
|
/** Basename of the CN variant's saved catalog inside the Harness home. */
|
|
1297
1471
|
declare const WORKBUDDY_CATALOG_FILENAME = ".workbuddy-catalog.json";
|
|
@@ -1639,4 +1813,4 @@ declare function visibilityAccountOf(credential: Pick<WorkBuddyCredential, 'uid'
|
|
|
1639
1813
|
*/
|
|
1640
1814
|
declare function apply(ctx: Context, refs: ConfigRefs): void;
|
|
1641
1815
|
//#endregion
|
|
1642
|
-
export { AI_VARIANT, type AppVersionInfo, CN_APP_VERSION_FILENAME, CN_VARIANT, type ChatIdentity, Config, ConfigRefs, FALLBACK_CN_APP_VERSION, FALLBACK_WORKBUDDY_AI_MODELS, FALLBACK_WORKBUDDY_MODELS, PROBE_EFFORT_CANDIDATES, type ProbeAttempt, type ProbeOutcome, type ProbeSender, type ResolveChatIdentityOptions, type UpstreamErrorKind, WORKBUDDY_APP_VERSION_FILENAME, WORKBUDDY_AUTH_FILENAME, WORKBUDDY_AUTH_FILE_ENV, WORKBUDDY_CATALOG_FILENAME, WORKBUDDY_HOST_HEARTBEAT_FILENAME, WORKBUDDY_PROBE_FILENAME, WORKBUDDY_PROVIDER, WORKBUDDY_STREAM_IDLE_TIMEOUT_MS, WORKBUDDY_VARIANTS, WORKBUDDY_VISIBILITY_FILENAME, type WorkBuddyAdapter, type WorkBuddyAppVersionSource, type WorkBuddyAuthStatus, WorkBuddyCatalog, type WorkBuddyCatalogFetch, WorkBuddyCatalogStore, type WorkBuddyChatResult, type WorkBuddyCredential, WorkBuddyCredentialStore, type WorkBuddyCredits, type WorkBuddyEffort, type WorkBuddyHostHeartbeat, type WorkBuddyModelBilling, type WorkBuddyModelInfo, type WorkBuddyModelReasoning, type WorkBuddyProbeRecord, WorkBuddyProbeService, type WorkBuddyProbeStatus, WorkBuddyProbeStore, type WorkBuddyProbeValidation, type WorkBuddyPromotion, type WorkBuddyRefreshOutcome, type WorkBuddyShim, WorkBuddyUpstreamClient, type WorkBuddyUpstreamModel, type WorkBuddyVariant, WorkBuddyVisibilityStore, appUserAgent, apply, chatUserAgent, classifyUpstreamError, clearHostHeartbeat, createWorkBuddyAdapter, createWorkBuddyShim, defaultDesktopAuthCandidates, defaultDesktopAuthPath, desktopAuthCandidatesFor, fallbackChatIdentity, fingerprintModel, inject, installedAppVersion, isHeartbeatProcessAlive, modelWithCurrentPromotion, name, normalizeCredits, parseModelCatalog, parseWorkBuddyAuth, prepareChatBody, prepareInternationalChatBody, probeModel, processStartTimeMs, randomSentinel, readBundleVersion, readCliVersion, readHostHeartbeat, regionOf, resolveAppVersion, resolveChatIdentity, validAppVersion, validCliVersion, variantFor, visibilityAccountOf, workbuddyCatalogPath, workbuddyHostHeartbeatPath, workbuddyOwnAuthPath, workbuddyProbePath, workbuddyVisibilityPath };
|
|
1816
|
+
export { AI_VARIANT, type AppVersionInfo, CN_APP_VERSION_FILENAME, CN_VARIANT, type ChatIdentity, Config, ConfigRefs, FALLBACK_CN_APP_VERSION, FALLBACK_WORKBUDDY_AI_MODELS, FALLBACK_WORKBUDDY_MODELS, PROBE_EFFORT_CANDIDATES, type ProbeAttempt, type ProbeOutcome, type ProbeSender, type ResolveChatIdentityOptions, type UpstreamErrorKind, WORKBUDDY_APP_VERSION_FILENAME, WORKBUDDY_AUTH_FILENAME, WORKBUDDY_AUTH_FILE_ENV, WORKBUDDY_CATALOG_FILENAME, WORKBUDDY_CREDIT_PATH, WORKBUDDY_HOST_HEARTBEAT_FILENAME, WORKBUDDY_PROBE_FILENAME, WORKBUDDY_PROVIDER, WORKBUDDY_STREAM_IDLE_TIMEOUT_MS, WORKBUDDY_VARIANTS, WORKBUDDY_VISIBILITY_FILENAME, type WorkBuddyAdapter, type WorkBuddyAppVersionSource, type WorkBuddyAuthStatus, WorkBuddyCatalog, type WorkBuddyCatalogFetch, WorkBuddyCatalogStore, type WorkBuddyChatResult, type WorkBuddyCredential, WorkBuddyCredentialStore, WorkBuddyCreditLog, type WorkBuddyCreditRouteOptions, type WorkBuddyCredits, type WorkBuddyEffort, type WorkBuddyHostHeartbeat, type WorkBuddyModelBilling, type WorkBuddyModelInfo, type WorkBuddyModelReasoning, type WorkBuddyProbeRecord, WorkBuddyProbeService, type WorkBuddyProbeStatus, WorkBuddyProbeStore, type WorkBuddyProbeValidation, type WorkBuddyPromotion, type WorkBuddyRefreshOutcome, WorkBuddySessionCreditIndex, type WorkBuddyShim, WorkBuddyUpstreamClient, type WorkBuddyUpstreamModel, type WorkBuddyVariant, WorkBuddyVisibilityStore, type WorkBuddyWebSessionCredits, appUserAgent, apply, chatUserAgent, classifyUpstreamError, clearHostHeartbeat, createWorkBuddyAdapter, createWorkBuddyShim, defaultDesktopAuthCandidates, defaultDesktopAuthPath, desktopAuthCandidatesFor, fallbackChatIdentity, fingerprintModel, inject, installedAppVersion, isHeartbeatProcessAlive, modelWithCurrentPromotion, name, normalizeCredits, parseModelCatalog, parseWorkBuddyAuth, prepareChatBody, prepareInternationalChatBody, probeModel, processStartTimeMs, randomSentinel, readBundleVersion, readCliVersion, readHostHeartbeat, regionOf, registerWorkBuddyCreditRoute, resolveAppVersion, resolveChatIdentity, validAppVersion, validCliVersion, variantFor, visibilityAccountOf, workBuddyCreditHandler, workBuddySessionCredits, workbuddyCatalogPath, workbuddyHostHeartbeatPath, workbuddyOwnAuthPath, workbuddyProbePath, workbuddyVisibilityPath };
|