@mars-sea/dsh-commandcode-provider 0.11.4 → 0.11.5
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 +9 -0
- package/lib/client.js +66 -18
- package/lib/client.js.map +1 -1
- package/package.json +39 -38
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.11.5] - 2026-09-18
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
- **The composer session-cost figure survives dsh 0.1.6-alpha.2.** That release deleted the `data-composer-stats` marker from the shipped stats row (`StatsPills`) while leaving the row's markup otherwise identical, and the readout's pill lookup *required* that attribute — so there the lookup matched nothing on every sync and the amount silently stopped appearing beside the token pill (after three misses it logged a single console line), leaving only the usage dialog still carrying per-row prices. `STATS_ROOT` is now a PREFERENCE: when the attribute is absent the lookup falls back to the dock OUTLET (`[data-slot="conversation.composer.dock"]`), the `display:contents` div holding exactly that slot's entries — the shipped `stats` cell and ours — whose LAST dialog trigger is therefore still the token pill. The fallback is deliberately the outlet and never the outlet's PARENT: the same release moved the composer's context-usage ring out of the card into a new footer `div` beside the dock, and that ring's trigger is itself a `button[aria-haspopup="dialog"]` rendered AFTER the dock, so the parent-scoped lookup this previously depended on would have appended the cost to the context meter instead. `tests/session-cost-display.test.ts` now builds both the unmarked 0.1.6-alpha.2 row and a footer-sibling meter, and fails if the cost stops landing on the token pill or the meter becomes the host; the earlier suite constructed its own marked row and accepted a missing anchor as a no-op, which is exactly why the deletion could go dark with every check green.
|
|
13
|
+
- **A second composer's usage dialog can no longer be priced with this session's figures.** dsh 0.1.6-alpha.2 mounts an embedded Conversation in the sidebar, so two composers — each with its own session, dock and dialog — can be live and open at once. The dialog is portaled onto `body` and carries no id or `aria-controls` tying it back to its trigger, so the document-level lookup was exact only while a single composer was guaranteed: the per-row shape confirmation compares against THIS entry's counts, so a dialog belonging to the other session that happened to match would have been decorated with the wrong figures. `resolveDialog()` now decorates only while the document holds EXACTLY ONE `[data-session-stats-usage]` and otherwise declines — the same rule the shape check already follows one level down. Pinned by two tests: with two dialogs open neither is priced, and the survivor is priced once the other composer's dialog unmounts.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
- **dsh 0.1.6-alpha.2 is a declared-compatible engine.** The engine's other changes need no adapter work, and each was verified against the installed release rather than inferred: the Typert strict result codec's member swap (`schema` → the lazy `create()` factory) was already covered by 0.11.4's two-member codec — both the Host and Client contributions register on that release's real registry, which is worth restating because 0.1.6-alpha.2 is the first PUBLISHED engine to require `create()`; `npm run test:engine` passes against it (the bundle links and evaluates there, and the request-image policy is the durable one); every slot this plugin registers into or re-states (`conversation.composer.dock`, `main`, `sidebar.footer.action`, `settings.section`, `settings.models.provider-card`) has an unchanged declaration and the dock still supplies `sessionId`/`useProjection`/`useChat`; the platform seed table, the host packages this plugin imports and the sidebar/footer class stems are unchanged; and the release's new plugin-management page asks nothing of a plugin whose registrations are already cordis effects — which is also what its runtime unload needs. The one genuinely new surface it exposes, a sidebar-mounted embedded Conversation, is what the second fix above defends against.
|
|
17
|
+
|
|
9
18
|
## [0.11.4] - 2026-09-17
|
|
10
19
|
|
|
11
20
|
### Fixed
|
package/lib/client.js
CHANGED
|
@@ -2157,7 +2157,7 @@ window.__ModuleLoader__.load({
|
|
|
2157
2157
|
}
|
|
2158
2158
|
//#endregion
|
|
2159
2159
|
//#region package.json
|
|
2160
|
-
var version = "0.11.
|
|
2160
|
+
var version = "0.11.5";
|
|
2161
2161
|
var repository = {
|
|
2162
2162
|
"type": "git",
|
|
2163
2163
|
"url": "git+https://github.com/Mars-Sea/dsh-commandcode-provider.git"
|
|
@@ -5205,12 +5205,25 @@ window.__ModuleLoader__.load({
|
|
|
5205
5205
|
}
|
|
5206
5206
|
//#endregion
|
|
5207
5207
|
//#region src/client/session-cost-display.ts
|
|
5208
|
-
/**
|
|
5208
|
+
/**
|
|
5209
|
+
* The shipped composer stats row (dsh-client-ui-chat `StatsPills`).
|
|
5210
|
+
*
|
|
5211
|
+
* A PREFERENCE, not a requirement: 0.1.6-alpha.2 dropped this attribute from the
|
|
5212
|
+
* row's root while leaving the rest of the markup identical, so a lookup that
|
|
5213
|
+
* insisted on it would silently stop finding the row on that engine. When it is
|
|
5214
|
+
* absent the search falls back to the dock OUTLET — deliberately the outlet and
|
|
5215
|
+
* never the outlet's parent, because from 0.1.6-alpha.2 the parent is the new
|
|
5216
|
+
* composer footer, which holds the `ContextMeter` as well, and that meter's
|
|
5217
|
+
* trigger is itself a `button[aria-haspopup="dialog"]` rendered AFTER the dock.
|
|
5218
|
+
* A parent-scoped "last trigger wins" would therefore append the cost to the
|
|
5219
|
+
* context ring instead of the token pill.
|
|
5220
|
+
*/
|
|
5209
5221
|
const STATS_ROOT = "[data-composer-stats]";
|
|
5210
5222
|
/**
|
|
5211
|
-
* The shipped token-usage dialog's `<dl>`. Unique
|
|
5212
|
-
*
|
|
5213
|
-
*
|
|
5223
|
+
* The shipped token-usage dialog's `<dl>`. Unique WITHIN one `StatsPills`
|
|
5224
|
+
* render: the per-message turn-usage panel has its own dialog with different
|
|
5225
|
+
* markup, so a single composer's usage dialog is unambiguous. It is not unique
|
|
5226
|
+
* across the document once two composers are live — see {@link resolveDialog}.
|
|
5214
5227
|
*/
|
|
5215
5228
|
const USAGE_DIALOG = "[data-session-stats-usage]";
|
|
5216
5229
|
/**
|
|
@@ -5311,7 +5324,7 @@ window.__ModuleLoader__.load({
|
|
|
5311
5324
|
const button = this.resolvePillButton();
|
|
5312
5325
|
if (button === null) {
|
|
5313
5326
|
this.pillMisses += 1;
|
|
5314
|
-
if (this.pillMisses === MISSES_BEFORE_WARNING) console.warn(
|
|
5327
|
+
if (this.pillMisses === MISSES_BEFORE_WARNING) console.warn("[dsh-commandcode-provider] no shipped token pill in this composer's dock outlet to append the session cost to; the figure stays in the usage dialog only");
|
|
5315
5328
|
return;
|
|
5316
5329
|
}
|
|
5317
5330
|
this.pillMisses = 0;
|
|
@@ -5335,11 +5348,24 @@ window.__ModuleLoader__.load({
|
|
|
5335
5348
|
if (this.pillA11y.textContent !== described) this.pillA11y.textContent = described;
|
|
5336
5349
|
}
|
|
5337
5350
|
}
|
|
5338
|
-
/**
|
|
5351
|
+
/**
|
|
5352
|
+
* The shipped token pill, scoped to this entry's own composer.
|
|
5353
|
+
*
|
|
5354
|
+
* The scope is the dock OUTLET (`display: contents`, one per slot), which holds
|
|
5355
|
+
* exactly that slot's entries — the shipped `stats` cell and ours, in their
|
|
5356
|
+
* registration order. {@link STATS_ROOT} narrows that to the stats row on the
|
|
5357
|
+
* engines that still mark it; on the engines that do not, the outlet is
|
|
5358
|
+
* already the narrowest correct container, because the composer footer's
|
|
5359
|
+
* `ContextMeter` sits BESIDE the outlet rather than inside it.
|
|
5360
|
+
*
|
|
5361
|
+
* A missing scope is "no pill", never a document-wide search: the outlet is
|
|
5362
|
+
* what makes this lookup per-composer, and a second composer can be live at
|
|
5363
|
+
* once (0.1.6-alpha.2 mounts an embedded Conversation in the sidebar).
|
|
5364
|
+
*/
|
|
5339
5365
|
resolvePillButton() {
|
|
5340
|
-
const
|
|
5341
|
-
if (
|
|
5342
|
-
const triggers =
|
|
5366
|
+
const scope = this.scope();
|
|
5367
|
+
if (scope === null) return null;
|
|
5368
|
+
const triggers = (scope.querySelector(STATS_ROOT) ?? scope).querySelectorAll(DIALOG_TRIGGER);
|
|
5343
5369
|
return triggers.length === 0 ? null : triggers[triggers.length - 1] ?? null;
|
|
5344
5370
|
}
|
|
5345
5371
|
/**
|
|
@@ -5462,18 +5488,30 @@ window.__ModuleLoader__.load({
|
|
|
5462
5488
|
this.dialogPrices.delete(dd);
|
|
5463
5489
|
}
|
|
5464
5490
|
/**
|
|
5465
|
-
* The shipped usage dialog, or null while it is closed.
|
|
5491
|
+
* The shipped usage dialog, or null while it is closed (or ambiguous).
|
|
5466
5492
|
*
|
|
5467
5493
|
* The dialog is portaled onto `body`, so unlike the pill it cannot be scoped
|
|
5468
|
-
* from this entry
|
|
5469
|
-
*
|
|
5470
|
-
*
|
|
5471
|
-
*
|
|
5494
|
+
* from this entry, and the attribute is unique per DIALOG rather than per
|
|
5495
|
+
* document. While one composer was guaranteed, a document-level lookup was
|
|
5496
|
+
* exact. 0.1.6-alpha.2 ends that guarantee: it mounts an embedded Conversation
|
|
5497
|
+
* in the sidebar, so a second composer — with its own session, its own dock
|
|
5498
|
+
* and its own dialog — can be live and open at the same time.
|
|
5499
|
+
*
|
|
5500
|
+
* There is no DOM link from a dialog back to its trigger (the portaled panel
|
|
5501
|
+
* carries no id and no `aria-controls`), so with two dialogs open document
|
|
5502
|
+
* order says nothing about ownership. The shape confirmation in
|
|
5503
|
+
* {@link dialogShapeMatches} would usually reject the stranger, but it compares
|
|
5504
|
+
* against THIS entry's counts, so a coincidentally matching dialog would be
|
|
5505
|
+
* priced with another session's figures. Ambiguity is therefore answered by
|
|
5506
|
+
* declining to decorate at all — the same rule the shape check already
|
|
5507
|
+
* follows, applied one level up: a dialog we cannot prove is ours is left
|
|
5508
|
+
* alone.
|
|
5472
5509
|
*/
|
|
5473
5510
|
resolveDialog() {
|
|
5474
5511
|
if (this.dialogHost?.isConnected === true) return this.dialogHost;
|
|
5475
5512
|
this.clearDialog();
|
|
5476
|
-
|
|
5513
|
+
const dialogs = this.doc.querySelectorAll(USAGE_DIALOG);
|
|
5514
|
+
return dialogs.length === 1 ? dialogs[0] ?? null : null;
|
|
5477
5515
|
}
|
|
5478
5516
|
/** Give the dialog back: every price removed, every hidden row restored. */
|
|
5479
5517
|
clearDialog() {
|
|
@@ -5544,7 +5582,17 @@ window.__ModuleLoader__.load({
|
|
|
5544
5582
|
}
|
|
5545
5583
|
//#endregion
|
|
5546
5584
|
//#region src/client/session-cost-view.tsx
|
|
5547
|
-
/**
|
|
5585
|
+
/**
|
|
5586
|
+
* The dock outlet this entry renders inside, i.e. what it scopes itself from.
|
|
5587
|
+
*
|
|
5588
|
+
* The OUTLET itself, not its parent. The outlet is one `display:contents` div
|
|
5589
|
+
* holding exactly this slot's entries (the shipped `stats` cell and ours), so it
|
|
5590
|
+
* is the narrowest container that is still per-composer. Its parent is NOT:
|
|
5591
|
+
* from 0.1.6-alpha.2 the composer stacks the dock inside a footer that also
|
|
5592
|
+
* holds the `ContextMeter`, whose trigger is another
|
|
5593
|
+
* `button[aria-haspopup="dialog"]` rendered after the dock — scoping to the
|
|
5594
|
+
* parent would let the cost land on the context ring.
|
|
5595
|
+
*/
|
|
5548
5596
|
const DOCK_ANCHOR = "[data-slot=\"conversation.composer.dock\"]";
|
|
5549
5597
|
/** Module-level constant so the marker's `style` prop never diffs. */
|
|
5550
5598
|
const HIDDEN_STYLE = { display: "none" };
|
|
@@ -5582,7 +5630,7 @@ window.__ModuleLoader__.load({
|
|
|
5582
5630
|
if (typeof document === "undefined") return;
|
|
5583
5631
|
const display = new SessionCostDisplay({
|
|
5584
5632
|
doc: document,
|
|
5585
|
-
scope: () => markerRef.current?.closest(DOCK_ANCHOR)
|
|
5633
|
+
scope: () => markerRef.current?.closest(DOCK_ANCHOR) ?? null
|
|
5586
5634
|
});
|
|
5587
5635
|
displayRef.current = display;
|
|
5588
5636
|
display.start();
|