dsh-connect-workbuddy 5.6.3 → 5.8.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/lib/index.d.ts CHANGED
@@ -433,9 +433,21 @@ declare class WorkBuddyCredentialStore {
433
433
  * refuses to use is what made the dropdown look healthy while every request
434
434
  * failed with 401.
435
435
  *
436
+ * THE POOL'S RUNTIME OVERRIDE IS PART OF `current()`, SO IT IS PART OF THIS.
437
+ * `current()` consults `rotatedAccountId` FIRST; this method used to ignore it
438
+ * and answer from `accountId` alone. With the pool on, the two therefore
439
+ * disagreed on every rotation: the shim billed account B while the card marked
440
+ * account A — the reporter's screenshot showed 「当前选用」 on a rate-limited
441
+ * account while the pill's total belonged to the one actually serving. Worse,
442
+ * the usage document takes its `credential` from `current()` and its
443
+ * `account.accountId` from this list, so one document could name two accounts.
444
+ *
436
445
  * So an explicit selection that matches NO local account marks nothing as
437
446
  * selected (no silent fallback to a different account — see {@link current}).
438
447
  * The implicit default is marked only when nothing was explicitly chosen.
448
+ * A live rotation marker wins over both, mirroring `current()`'s precedence,
449
+ * and only while it still names a local account — `current()` falls through in
450
+ * that case, and so does this.
439
451
  */
440
452
  accounts(): Promise<WorkBuddyAccountChoice[]>;
441
453
  /**
@@ -954,6 +966,17 @@ type UpstreamErrorKind = 'hard_credit' |
954
966
  * user; the second one is the actual remedy.
955
967
  */
956
968
  'model_busy' | 'session_dead' | 'policy_reject' |
969
+ /**
970
+ * The upstream restricted THIS account for unusual activity (issue #127).
971
+ *
972
+ * Kept separate from {@link policy_reject}, even though both arrive as the
973
+ * `11140` family with the same `msg: "request illegal"`: that one is a CONTENT
974
+ * decision and switching accounts cannot help, while this is a CREDENTIAL
975
+ * decision and switching is exactly the remedy. `displayMsg` is the only
976
+ * discriminator upstream provides ("账号存在异常行为,已被限制使用" / "restricted
977
+ * due to unusual activity").
978
+ */
979
+ 'account_restricted' |
957
980
  /**
958
981
  * The gateway's channel gate refused the request itself (business code
959
982
  * 11128, "Illegal API invocation from an unapproved channel").
@@ -1310,6 +1333,31 @@ declare function regionOf(domain: string): WorkBuddyRegion;
1310
1333
  * the same decision as admitting `workbuddy.cn`.
1311
1334
  */
1312
1335
  declare function knownRegionOf(domain: string): WorkBuddyRegion | undefined;
1336
+ /**
1337
+ * How the plugin should identify itself where the upstream only RECORDS the
1338
+ * request (issue #132). One switch, because the two halves are one decision.
1339
+ */
1340
+ interface WorkBuddyWireIdentity {
1341
+ /**
1342
+ * Adopt the official client's usage-page identity on chat requests.
1343
+ *
1344
+ * ON adds `X-IDE-Type` / `X-IDE-Name` / `X-IDE-Version` and switches the
1345
+ * conversation-identity triple to the 32-hex UUIDv7 shape the official client
1346
+ * uses (plus `X-Request-ID`, which mirrors the message id there).
1347
+ *
1348
+ * OFF by default, and the reason is the evidence tier, not the mechanism: the
1349
+ * reporter measured HTTP 200 on their own build of this PLUGIN at 5.3.1
1350
+ * (the WorkBuddy client itself was 5.8.3), and this plugin has not
1351
+ * reproduced that against the live gateway. Nothing in either gate is documented
1352
+ * as reading these headers — `11128`'s three doors are the message openings,
1353
+ * the missing conversation id and a non-system first message, none of which a
1354
+ * header can change — but this gateway has repeatedly refused requests for
1355
+ * reasons visible only in the answer, so a display-only alignment ships behind a
1356
+ * switch rather than as a silent upgrade for every account. `User-Agent` and the
1357
+ * request BODY are untouched either way.
1358
+ */
1359
+ usageIdentity?: boolean;
1360
+ }
1313
1361
  /**
1314
1362
  * The model id a prepared body asks for, when it carries one (issue #61).
1315
1363
  *
@@ -1319,6 +1367,43 @@ declare function knownRegionOf(domain: string): WorkBuddyRegion | undefined;
1319
1367
  * measurement that this id is needed for.
1320
1368
  */
1321
1369
  declare function modelIdOfBody(bodyJson: string): string | undefined;
1370
+ /**
1371
+ * How a body should be prepared beyond the rules every request needs (issue #132).
1372
+ *
1373
+ * Both switches default to OFF, and that default is the point: each one changes
1374
+ * bytes the upstream counts, so it must be a deliberate opt-in rather than a
1375
+ * silent behaviour change on upgrade.
1376
+ */
1377
+ interface PrepareChatBodyOptions {
1378
+ /**
1379
+ * Move the harness's injected `user` messages above the real prompt.
1380
+ *
1381
+ * WHY. The usage page's 请求 column is filled from the LAST `user` message in
1382
+ * the body. The DSH harness appends its runtime context (time samples,
1383
+ * `Current runtime context.`, `<system-reminder>` blocks) AFTER the user's real
1384
+ * prompt, as extra `user` messages — so the model reads the real prompt but the
1385
+ * usage page records only the injected tail, and a user comparing their usage
1386
+ * history with what they typed sees rows that look like noise.
1387
+ *
1388
+ * The move is ORDER-ONLY and only within the trailing run: the injected
1389
+ * messages are lifted out and re-inserted immediately BEFORE the real prompt,
1390
+ * which keeps every message, its text and every tool result intact (the one
1391
+ * textual edit is the recorded prompt's leading marker, documented at
1392
+ * {@link ACP_LEADING_TAG}). It is
1393
+ * therefore invisible to the model's instruction order (a runtime-context block
1394
+ * is context wherever it sits) and it is what makes the usage column show what
1395
+ * the user actually typed.
1396
+ *
1397
+ * It also removes the leading `<acp …>mNNNNN</acp>` marker from THAT prompt and no
1398
+ * other message, because otherwise the recorded row still opens with the marker
1399
+ * instead of the user's sentence. Marker only — no text is touched.
1400
+ *
1401
+ * Only messages that MATCH the harness's own injected prefixes are moved. A
1402
+ * body from any other client is untouched: this must never reorder a
1403
+ * conversation whose trailing user messages are real turns.
1404
+ */
1405
+ keepPromptLast?: boolean;
1406
+ }
1322
1407
  /**
1323
1408
  * Normalize an OpenAI chat-completions body for the WorkBuddy upstream: force
1324
1409
  * `stream: true` (the upstream rejects non-streaming), flatten `tool_choice`
@@ -1336,7 +1421,7 @@ declare function modelIdOfBody(bodyJson: string): string | undefined;
1336
1421
  * upstream already answers keeps its prompt as close to byte-for-byte as these
1337
1422
  * rules allow.
1338
1423
  */
1339
- declare function prepareChatBody(source: string): string;
1424
+ declare function prepareChatBody(source: string, options?: PrepareChatBodyOptions): string;
1340
1425
  /** Marker carried by {@link WorkBuddyCredentialRejectedError}; survives bundling. */
1341
1426
  declare const CREDENTIAL_REJECTED_CODE = "WORKBUDDY_CREDENTIAL_REJECTED";
1342
1427
  /**
@@ -1381,6 +1466,7 @@ declare function parseUpstreamModel(value: unknown): WorkBuddyUpstreamModel | un
1381
1466
  */
1382
1467
  declare class WorkBuddyUpstreamClient {
1383
1468
  private readonly onFallback?;
1469
+ private readonly identity;
1384
1470
  /**
1385
1471
  * Reports a fallback the catalog reader had to take.
1386
1472
  *
@@ -1390,7 +1476,7 @@ declare class WorkBuddyUpstreamClient {
1390
1476
  * `ctx.logger.warn` in; without a host the event is dropped, which is the
1391
1477
  * same silence the previous single-source reader had.
1392
1478
  */
1393
- constructor(onFallback?: ((message: string) => void) | undefined);
1479
+ constructor(onFallback?: ((message: string) => void) | undefined, identity?: WorkBuddyWireIdentity);
1394
1480
  /** POST the chat endpoint; a successful answer is the raw SSE response. */
1395
1481
  chatStream(credential: WorkBuddyCredential, bodyJson: string, signal?: AbortSignal): Promise<WorkBuddyChatResult>;
1396
1482
  /**
@@ -1864,6 +1950,29 @@ interface WorkBuddyShimOptions {
1864
1950
  client: Pick<WorkBuddyUpstreamClient, 'chatStream'>;
1865
1951
  catalog: WorkBuddyCatalog;
1866
1952
  logger?: ShimLogger;
1953
+ /**
1954
+ * When to perturb scanned messages with invisible characters.
1955
+ *
1956
+ * `off` keeps the ordinary fail-then-rescue ladder; `failure` (the default)
1957
+ * remembers a successful rescue and starts later requests with it; `always`
1958
+ * starts every request with the dense rescue. `always` is for users whose
1959
+ * gateway has already made the plain attempt a guaranteed 11128 — it trades a
1960
+ * little prompt byte-level purity for avoiding that doomed round trip.
1961
+ */
1962
+ channelPerturbation?: 'off' | 'failure' | 'always';
1963
+ /**
1964
+ * Move the harness's injected `user` blocks above the real prompt (issue #132).
1965
+ *
1966
+ * The WorkBuddy usage page records the LAST `user` message as the request
1967
+ * text, and the harness appends its runtime context after the user's real
1968
+ * prompt — so the usage column shows injected noise instead of what was typed.
1969
+ * The reorder is order-only (every message and its text survive), and it only
1970
+ * ever moves messages that MATCH the harness's own injected prefixes; the single
1971
+ * textual edit is stripping the recorded prompt's own leading marker.
1972
+ *
1973
+ * Default false: it changes bytes the upstream counts, so it is opt-in.
1974
+ */
1975
+ keepPromptLast?: boolean;
1867
1976
  /**
1868
1977
  * Pool failover for chat requests. Absent means no failover at all — the
1869
1978
  * pre-pool behaviour, where the selected account serves and its failure is
@@ -2587,7 +2696,15 @@ declare function probeRequestBody(modelId: string, inputTokens?: number): string
2587
2696
  * instead of what it is — the gateway objecting to the PROMPT, not the account
2588
2697
  * (issues #87–#90).
2589
2698
  */
2590
- type WorkBuddyProbeOutcome = 'ok' | 'rate-limited' | 'model-busy' | 'out-of-credit' | 'credential-rejected' | 'policy-rejected' | 'channel-rejected' | 'unavailable' | 'not-found' | 'failed';
2699
+ type WorkBuddyProbeOutcome = 'ok' | 'rate-limited' | 'model-busy' | 'out-of-credit' | 'credential-rejected' | 'policy-rejected' |
2700
+ /**
2701
+ * The upstream restricted this credential for unusual activity (issue #127).
2702
+ *
2703
+ * Split from `policy-rejected` because the pool's response differs: this is
2704
+ * an account-level exclusion, while a content-policy refusal is about the
2705
+ * request and must not teach the pool that the credential is unusable.
2706
+ */
2707
+ 'account-restricted' | 'channel-rejected' | 'unavailable' | 'not-found' | 'failed';
2591
2708
  /**
2592
2709
  * One probe result, as the card renders it.
2593
2710
  *
@@ -2781,6 +2898,15 @@ interface WorkBuddyPoolAccount {
2781
2898
  id: string;
2782
2899
  /** Human name, or `''` when the desktop app recorded none. */
2783
2900
  accountName: string;
2901
+ /**
2902
+ * This account belongs to an enterprise/education tenant (issue #124/#129).
2903
+ *
2904
+ * A projection fact, carried from the credential's `enterpriseId` rather than
2905
+ * inferred from any benefit or check-in response: upstream can advertise
2906
+ * campaigns and `active:false` for enterprise members while refusing every
2907
+ * write with `10001`.
2908
+ */
2909
+ enterprise?: boolean;
2784
2910
  }
2785
2911
  /**
2786
2912
  * What the pool knows about ONE account's last test of the target model.
@@ -3357,6 +3483,31 @@ type WorkBuddyWebUsage =
3357
3483
  * must not be read as consent.
3358
3484
  */
3359
3485
  cliBridgeEnabled?: boolean;
3486
+ /**
3487
+ * The model the OPTIONAL CodeBuddy CLI route runs on, or `''` for
3488
+ * "nothing is pinned, the CLI uses its own default".
3489
+ *
3490
+ * GLOBAL like `cliBridgeEnabled`. Travels on the Host's answer for the
3491
+ * same measured reason: on the stale-mirror 0.1.7 deployment a picker that
3492
+ * read the settings mirror alone would keep showing a pin that had just
3493
+ * been cleared (or had not landed yet).
3494
+ */
3495
+ cliBridgeModel?: string;
3496
+ /**
3497
+ * The reasoning effort the OPTIONAL CodeBuddy CLI route runs on, or `''`
3498
+ * for "nothing is pinned, the CLI uses its own default".
3499
+ *
3500
+ * GLOBAL and Host-carried for the same reasons as `cliBridgeModel`.
3501
+ */
3502
+ cliBridgeEffort?: string;
3503
+ /**
3504
+ * The order the OPTIONAL CodeBuddy CLI route walks the credential
3505
+ * regions in — a comma-separated region list such as `'cn,global'` — or
3506
+ * `''` for "nothing is pinned, the CLI walks its own default order".
3507
+ *
3508
+ * GLOBAL and Host-carried for the same reasons as `cliBridgeModel`.
3509
+ */
3510
+ cliBridgeRegionOrder?: string;
3360
3511
  /**
3361
3512
  * The LIVE load level per model id, for the card's icon.
3362
3513
  *
@@ -3421,6 +3572,31 @@ type WorkBuddyWebUsage =
3421
3572
  * must not be read as consent.
3422
3573
  */
3423
3574
  cliBridgeEnabled?: boolean;
3575
+ /**
3576
+ * The model the OPTIONAL CodeBuddy CLI route runs on, or `''` for
3577
+ * "nothing is pinned, the CLI uses its own default".
3578
+ *
3579
+ * GLOBAL like `cliBridgeEnabled`. Travels on the Host's answer for the
3580
+ * same measured reason: on the stale-mirror 0.1.7 deployment a picker that
3581
+ * read the settings mirror alone would keep showing a pin that had just
3582
+ * been cleared (or had not landed yet).
3583
+ */
3584
+ cliBridgeModel?: string;
3585
+ /**
3586
+ * The reasoning effort the OPTIONAL CodeBuddy CLI route runs on, or `''`
3587
+ * for "nothing is pinned, the CLI uses its own default".
3588
+ *
3589
+ * GLOBAL and Host-carried for the same reasons as `cliBridgeModel`.
3590
+ */
3591
+ cliBridgeEffort?: string;
3592
+ /**
3593
+ * The order the OPTIONAL CodeBuddy CLI route walks the credential
3594
+ * regions in — a comma-separated region list such as `'cn,global'` — or
3595
+ * `''` for "nothing is pinned, the CLI walks its own default order".
3596
+ *
3597
+ * GLOBAL and Host-carried for the same reasons as `cliBridgeModel`.
3598
+ */
3599
+ cliBridgeRegionOrder?: string;
3424
3600
  /**
3425
3601
  * The LIVE load level per model id, for the card's icon.
3426
3602
  *
@@ -3444,6 +3620,24 @@ type WorkBuddyWebUsage =
3444
3620
  * selection, seeded from the built-in rule the first time it is read.
3445
3621
  */
3446
3622
  offModelIds: readonly string[];
3623
+ /**
3624
+ * Ids of THIS region's roster that this machine first saw inside the
3625
+ * new-model window, oldest first — the data behind the composer pill's
3626
+ * "· N 新模型" segment.
3627
+ *
3628
+ * "新" means "this machine first recorded the id in its catalog", NOT "the
3629
+ * gateway added it": the upstream list carries no listed-since field, so
3630
+ * claiming an upstream fact would be fabricating one. Absent means "nothing
3631
+ * is new right now" or "this Host predates the feature" — never "0".
3632
+ */
3633
+ newModelIds?: readonly string[];
3634
+ /**
3635
+ * A sentence explaining why no new-model readout is shown, present only
3636
+ * when the on-disk first-sighting history is in a version this build cannot
3637
+ * read. Kept separate from the field above so "no new models" is never the
3638
+ * story for a file that simply was not read.
3639
+ */
3640
+ rosterSeenVersionNote?: string;
3447
3641
  credits?: WorkBuddyWebCredits;
3448
3642
  creditsError?: string;
3449
3643
  /**
@@ -3520,6 +3714,13 @@ type WorkBuddyWebProbeOutcome = 'ok' | 'rate-limited' |
3520
3714
  * wait about a minute, or switch to a less busy model.
3521
3715
  */
3522
3716
  'model-busy' | 'out-of-credit' | 'credential-rejected' | 'policy-rejected' |
3717
+ /**
3718
+ * The upstream restricted this credential for unusual activity (issue #127).
3719
+ *
3720
+ * Separate from `policy-rejected`: a content refusal is about the request,
3721
+ * while this is about the credential and needs re-authentication or an appeal.
3722
+ */
3723
+ 'account-restricted' |
3523
3724
  /**
3524
3725
  * The gateway's channel gate refused the request itself (upstream code 11128).
3525
3726
  *
@@ -3595,7 +3796,12 @@ type WorkBuddyWebPoolExclusion = 'unusable' | 'rate-limited' |
3595
3796
  * showing "被限流" next to an ACCOUNT name asserts something the measurement
3596
3797
  * does not say (issue #56).
3597
3798
  */
3598
- 'model-busy' | 'out-of-credit' | 'credential-rejected' | 'not-found' | 'unavailable' | 'failed';
3799
+ 'model-busy' | 'out-of-credit' | 'credential-rejected' |
3800
+ /**
3801
+ * The upstream restricted this account (issue #127); unlike a request policy
3802
+ * refusal, another account can serve and this one should be benched.
3803
+ */
3804
+ 'account-restricted' | 'not-found' | 'unavailable' | 'failed';
3599
3805
  /** One claimable benefit, as the card renders it (issue #79). */
3600
3806
  interface WorkBuddyWebBenefit {
3601
3807
  /** Which campaign: compensation (crash补偿) or gift (新人首月赠送). */
@@ -3624,6 +3830,14 @@ interface WorkBuddyWebBenefit {
3624
3830
  interface WorkBuddyWebPoolAccount {
3625
3831
  accountId: string;
3626
3832
  accountName: string;
3833
+ /**
3834
+ * The credential belongs to an enterprise/education tenant (issues #124/#129).
3835
+ *
3836
+ * Carried through the pool row so the card can render the two actions the
3837
+ * upstream does not support for those accounts (daily check-in and benefit
3838
+ * claiming) as explicitly unavailable rather than as pending work.
3839
+ */
3840
+ enterprise?: boolean;
3627
3841
  /** Total remaining credits, absent when the credits route failed. */
3628
3842
  credits?: number;
3629
3843
  /** Credits expiring within 3 days. */
@@ -3852,6 +4066,14 @@ declare function unwrapVolatileDeep<T>(value: T): T;
3852
4066
  */
3853
4067
  declare const WORKBUDDY_SETTINGS_NS: "workbuddy";
3854
4068
  //#endregion
4069
+ //#region src/roster-seen-store.d.ts
4070
+ /** A first-sighting readout, assembled read-only by the usage route. */
4071
+ interface RosterSeenReadout {
4072
+ newModelIds: readonly string[];
4073
+ /** A sentence when the on-disk history is in a version this build cannot read. */
4074
+ versionNote?: string;
4075
+ }
4076
+ //#endregion
3855
4077
  //#region src/web-status.d.ts
3856
4078
  /** Constructor dependencies. */
3857
4079
  interface WorkBuddyStatusRouteOptions {
@@ -3875,6 +4097,20 @@ interface WorkBuddyStatusRouteOptions {
3875
4097
  contextBudgets(region: WorkBuddyRegion): Readonly<Record<string, number | undefined>>;
3876
4098
  /** Re-read the live catalog of one region from the upstream. */
3877
4099
  discoverModels?(region: WorkBuddyRegion, signal?: AbortSignal): Promise<readonly WorkBuddyModelInfo[]>;
4100
+ /**
4101
+ * Which ids of the requested region's roster THIS machine first saw inside the
4102
+ * new-model window (the "· N 新模型" segment), plus a note when the on-disk
4103
+ * first-sighting history is in a version this build cannot read.
4104
+ *
4105
+ * READ-ONLY: the usage route never writes first-sighting history — only the
4106
+ * catalog reconciler does (`src/index.ts`), so a panel poll can never change
4107
+ * "who is new". Region-scoped because the two regions keep separate catalogs
4108
+ * and therefore separate first-sighting tables.
4109
+ *
4110
+ * Optional, and absent means the Host predates the feature: the pill then
4111
+ * renders no segment at all rather than a made-up "0 新模型".
4112
+ */
4113
+ rosterSeen?(region: WorkBuddyRegion): Promise<RosterSeenReadout>;
3878
4114
  /**
3879
4115
  * Whether the requested region's provider is currently offered to DSH. The
3880
4116
  * card renders this as the tab's on/off checkbox, so the switch reflects the
@@ -3922,6 +4158,31 @@ interface WorkBuddyStatusRouteOptions {
3922
4158
  * deployments, and a stale `true` here would claim a spending capability is on.
3923
4159
  */
3924
4160
  cliBridgeEnabled?(): boolean;
4161
+ /**
4162
+ * The model the OPTIONAL CodeBuddy CLI route runs on, or `''` for "the CLI's
4163
+ * own default".
4164
+ *
4165
+ * GLOBAL like `cliBridgeEnabled` — one route, one pin, so no argument.
4166
+ * Optional, and absent is the same as `''`: the route then runs on the CLI's
4167
+ * default. The card carries it on the Host's answer for the same measured
4168
+ * reason as `cliBridgeEnabled` — the browser's settings mirror is stale for
4169
+ * this namespace on the affected 0.1.7 deployments, so a picker reading the
4170
+ * mirror alone would keep showing a pin that had just been cleared.
4171
+ */
4172
+ cliBridgeModel?(): string;
4173
+ /**
4174
+ * The reasoning effort the OPTIONAL CodeBuddy CLI route runs on, or `''` for
4175
+ * "the CLI's own default". GLOBAL, optional, and absent-as-`''` for the same
4176
+ * reasons as {@link cliBridgeModel}.
4177
+ */
4178
+ cliBridgeEffort?(): string;
4179
+ /**
4180
+ * The order the OPTIONAL CodeBuddy CLI route walks the credential regions in
4181
+ * (a comma-separated list such as `'cn,global'`), or `''` for "the CLI's own
4182
+ * default order". GLOBAL, optional, and absent-as-`''` for the same reasons
4183
+ * as {@link cliBridgeModel}.
4184
+ */
4185
+ cliBridgeRegionOrder?(): string;
3925
4186
  /**
3926
4187
  * The LIVE load level per model id, for the card's icon.
3927
4188
  *
@@ -4235,6 +4496,7 @@ interface WorkBuddyPoolDeps {
4235
4496
  otherAccounts?(region: WorkBuddyRegion): Promise<readonly {
4236
4497
  id: string;
4237
4498
  accountName: string;
4499
+ enterprise?: boolean;
4238
4500
  }[]>;
4239
4501
  /** The account currently billing traffic for the region, when known. */
4240
4502
  currentAccountId?(region: WorkBuddyRegion): Promise<string | undefined>;
@@ -4532,6 +4794,37 @@ interface Config {
4532
4794
  * client-fingerprint workaround, not an account one — see `cli-bridge/runner`.
4533
4795
  */
4534
4796
  cliBridge?: CliBridgeSettings;
4797
+ /**
4798
+ * When scanned messages are perturbed with invisible characters.
4799
+ *
4800
+ * `failure` (default) learns from a successful channel-gate rescue and starts
4801
+ * later requests with the learned form; `always` starts every request with
4802
+ * the dense form for a gateway whose plain attempt is a guaranteed refusal;
4803
+ * `off` keeps only the post-failure rescue ladder.
4804
+ */
4805
+ channelPerturbation?: 'off' | 'failure' | 'always';
4806
+ /**
4807
+ * Adopt the official client's usage-page identity on chat requests (issue #132).
4808
+ *
4809
+ * Adds `X-IDE-Name: WorkBuddy` (which is what fills the upstream's 使用端
4810
+ * column) and sends the conversation-identity triple in the 32-hex UUIDv7
4811
+ * shape the official client uses. Off by default: the reporter measured it at
4812
+ * HTTP 200, this plugin has not reproduced that against the live gateway, and a
4813
+ * display-only change should not be the reason an account starts failing.
4814
+ */
4815
+ usageIdentity?: boolean;
4816
+ /**
4817
+ * Move the harness's injected context above the real prompt (issue #132).
4818
+ *
4819
+ * The WorkBuddy usage page records the last `user` message as the request
4820
+ * text, and DSH appends its runtime context after the user's prompt — so the
4821
+ * usage column shows the injected block instead of what the user typed. The
4822
+ * reorder is order-only and matches only the harness's own injected prefixes.
4823
+ *
4824
+ * Off by default: it changes the message order the upstream counts, so it is
4825
+ * an explicit opt-in rather than a silent change on upgrade.
4826
+ */
4827
+ keepPromptLast?: boolean;
4535
4828
  }
4536
4829
  /**
4537
4830
  * Mark a schema's field as volatile on the DSH lines that support it.