dsh-autotier 0.1.1 → 0.2.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.
Files changed (54) hide show
  1. package/AGENTS.md +26 -3
  2. package/CHANGELOG.md +33 -0
  3. package/THIRD_PARTY_NOTICES.md +31 -0
  4. package/docs/roadmap-v0.2.md +114 -0
  5. package/lib/client.js +5956 -0
  6. package/lib/client.js.map +1 -0
  7. package/lib/index.js +193 -58
  8. package/lib/typert.host.js +26 -0
  9. package/lib/types/client/TierPill.d.ts +24 -0
  10. package/lib/types/client/TierPill.d.ts.map +1 -0
  11. package/lib/types/client/TierSettingsCard.d.ts +31 -0
  12. package/lib/types/client/TierSettingsCard.d.ts.map +1 -0
  13. package/lib/types/client/index.d.ts +30 -0
  14. package/lib/types/client/index.d.ts.map +1 -0
  15. package/lib/types/client/locales.d.ts +60 -0
  16. package/lib/types/client/locales.d.ts.map +1 -0
  17. package/lib/types/client/present.d.ts +61 -0
  18. package/lib/types/client/present.d.ts.map +1 -0
  19. package/lib/types/client/remote.d.ts +313 -0
  20. package/lib/types/client/remote.d.ts.map +1 -0
  21. package/lib/types/client/styles.d.ts +12 -0
  22. package/lib/types/client/styles.d.ts.map +1 -0
  23. package/lib/types/command.d.ts.map +1 -1
  24. package/lib/types/index.d.ts +4 -0
  25. package/lib/types/index.d.ts.map +1 -1
  26. package/lib/types/policy.d.ts +4 -0
  27. package/lib/types/policy.d.ts.map +1 -1
  28. package/lib/types/routing.d.ts +1 -0
  29. package/lib/types/routing.d.ts.map +1 -1
  30. package/lib/types/selection.d.ts +50 -0
  31. package/lib/types/selection.d.ts.map +1 -0
  32. package/lib/types/tier-remote.d.ts +79 -0
  33. package/lib/types/tier-remote.d.ts.map +1 -0
  34. package/lib/types/typert.host.d.ts +300 -0
  35. package/lib/types/typert.host.d.ts.map +1 -0
  36. package/lib/types/wire.d.ts +804 -0
  37. package/lib/types/wire.d.ts.map +1 -0
  38. package/lib/wire-BpKSOHgo.js +5193 -0
  39. package/package.json +53 -2
  40. package/src/client/TierPill.tsx +83 -0
  41. package/src/client/TierSettingsCard.tsx +175 -0
  42. package/src/client/index.ts +146 -0
  43. package/src/client/locales.ts +107 -0
  44. package/src/client/present.ts +124 -0
  45. package/src/client/remote.ts +39 -0
  46. package/src/client/styles.ts +174 -0
  47. package/src/command.ts +1 -0
  48. package/src/index.ts +43 -0
  49. package/src/policy.ts +6 -0
  50. package/src/routing.ts +17 -0
  51. package/src/selection.ts +124 -0
  52. package/src/tier-remote.ts +139 -0
  53. package/src/typert.host.ts +25 -0
  54. package/src/wire.ts +248 -0
package/AGENTS.md CHANGED
@@ -21,9 +21,28 @@ records repo-local decisions.
21
21
  - `src/service.ts` — `ctx.autotier` (`status()` and the live settings scope).
22
22
  - `src/types.ts` — shared vocabulary (tier ids, effort ids, routing modes,
23
23
  scenarios, route/status shapes).
24
+ - `src/wire.ts` — the `tier` Remote wire vocabulary: `TierStatus` (the public
25
+ `AutotierStatus` plus an additive `session` view), `TierCatalog`, their zod v4
26
+ codecs, and the three invocation descriptors shared verbatim by the host
27
+ manifest and the client contribution.
28
+ - `src/typert.host.ts` — the hand-written host Typert manifest exported as
29
+ `./typert`; the harness's typert-loader registers the `tier` invocations from
30
+ it when the plugin mounts.
31
+ - `src/tier-remote.ts` — `TierRemoteService` (`TypertRemoteService`, namespace
32
+ `tier`): `status(agentId?)` / `catalog()` / `setMode(mode, agentId?)`. The
33
+ override it writes is the same per-agent `RouteState.override` the router and
34
+ `/tier` read; `agents` is read optionally through the injected resolver.
35
+ - `src/client/` — browser half: `$mount`s the Remote contribution, registers the
36
+ composer pill into `conversation.input.left` (id `tier-pill`) and the Settings
37
+ card into `settings.plugins.tab` (id `autotier`), with a pure presenter
38
+ (`present.ts`), inline scoped stylesheet (`styles.ts`), and en/zh dictionaries
39
+ (`locales.ts`). The slot registry is read through a local structural
40
+ `SlotsFace` (its owning package differs across host lines).
24
41
  - `tests/` — vitest over the REAL published `0.1.2-rc.1` host packages
25
42
  (`Context`, `SessionStore`, `SystemPrompt`, `ToolRuntime`, `CommandRuntime`,
26
- in-memory `SettingsProvider`) plus one real Loader composition.
43
+ in-memory `SettingsProvider`) plus one real Loader composition. The browser
44
+ half is covered through its pure units (wire codecs, presenter, dictionaries)
45
+ and the `tier` service through a real mount with a scripted `agents` face.
27
46
 
28
47
  ## Hard rules applied here
29
48
 
@@ -63,8 +82,12 @@ table.
63
82
  `typescript` + `tsdown` are regular `dependencies` (the git channel's `prepare`
64
83
  builds with production dependencies alone). `scripts/prepare.mjs` wipes `lib/`,
65
84
  emits tsc declarations into `lib/types`, then runs tsdown (tsdown `clean` stays
66
- OFF so the declarations survive). `pnpm-workspace.yaml` declares
67
- `allowBuilds: { esbuild: true }`.
85
+ OFF so the declarations survive). Two build faces: the node half
86
+ (`lib/index.js` + `lib/typert.host.js`) and the browser half (`lib/client.js`),
87
+ whose CJS factory is wrapped in `window.__ModuleLoader__.load({ id, factory })`
88
+ with the shell's platform modules (`react`, `react/jsx-runtime`, the
89
+ `@deepseek-ai/dsh-client-*` singletons) left external and zod inlined.
90
+ `pnpm-workspace.yaml` declares `allowBuilds: { esbuild: true }`.
68
91
 
69
92
  ## Checks
70
93
 
package/CHANGELOG.md CHANGED
@@ -5,6 +5,39 @@ All notable changes to this project are documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.2.0] - 2026-09-09
9
+
10
+ ### Added
11
+
12
+ - **Browser half: a Settings card and a composer tier pill.** The plugin now
13
+ ships a `tier` Typert Remote service (`tier/status`, `tier/catalog`,
14
+ `tier/setMode`) behind a hand-written `./typert` host manifest, and a client
15
+ bundle that registers the composer pill into `conversation.input.left`
16
+ (id `tier-pill`) and the autotier card into the Plugins settings section
17
+ (`settings.plugins.tab`, id `autotier`). The pill shows the session's effective
18
+ mode (`AUTO` / `STRONG` / `CHEAP` / `OFF`) with an escalation badge and cycles
19
+ the session override on click; the card offers the full
20
+ `auto | strong | cheap | delegated | off` selector, the read-only tier
21
+ landings, the live session state, and a model-catalog dropdown fed by the live
22
+ `ctx.llm` registry. Copy ships in English and Simplified Chinese.
23
+ - `TierStatus` extends the public `AutotierStatus` additively with a `session`
24
+ view (override, effective mode, applied tier and source, escalation, plan
25
+ mode, guard denials); the host still serves `ctx.autotier.status()` unchanged.
26
+ - `zod` is now a runtime dependency (the strict wire codecs on both faces) and
27
+ the client peers (`dsh-api-remotes`, `dsh-client-connection`,
28
+ `dsh-client-locale`, `dsh-client-ui-conversation`, `dsh-client-ui-settings`,
29
+ `dsh-client-ui-slots`, `dsh-typert-protocol`) are declared as optional peers.
30
+
31
+ ### Notes
32
+
33
+ - The session override stays runtime-only: `auto` clears it, and a plugin reload
34
+ restores the `cordis.yml` routing mode. Nothing is persisted.
35
+ - `pnpm run verify:artifacts` now asserts the two new shipped faces
36
+ (`lib/typert.host.js` with its manifest shape, `lib/client.js` with its
37
+ ModuleLoader handshake and external platform modules).
38
+ - The five-language READMEs are not updated in this change; the client copy is
39
+ English + Simplified Chinese only.
40
+
8
41
  ## [0.1.1] - 2026-09-09
9
42
 
10
43
  ### Added
@@ -1,5 +1,36 @@
1
1
  # Third-party notices
2
2
 
3
+ ## zod (https://github.com/colinhacks/zod)
4
+
5
+ The `tier` Remote wire codecs (`src/wire.ts`) are zod v4 schemas. `zod` is a
6
+ declared runtime dependency (MIT) and its library code is inlined into the built
7
+ `lib/index.js`, `lib/typert.host.js` and `lib/client.js` bundles; no zod source
8
+ is modified.
9
+
10
+ ```
11
+ MIT License
12
+
13
+ Copyright (c) 2020 Colin McDonnell
14
+
15
+ Permission is hereby granted, free of charge, to any person obtaining a copy
16
+ of this software and associated documentation files (the "Software"), to deal
17
+ in the Software without restriction, including without limitation the rights
18
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
19
+ copies of the Software, and to permit persons to whom the Software is
20
+ furnished to do so, subject to the following conditions:
21
+
22
+ The above copyright notice and this permission notice shall be included in all
23
+ copies or substantial portions of the Software.
24
+
25
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31
+ SOFTWARE.
32
+ ```
33
+
3
34
  ## dsh-tier-router (`lib/pure.js`)
4
35
 
5
36
  The high-risk command guard in `src/guard-rules.ts` is a port of the rule table
@@ -0,0 +1,114 @@
1
+ # v0.2 roadmap: concrete designs for the deferred items
2
+
3
+ This file records the researched, decision-ready design for every capability
4
+ that is deliberately **not** in 0.1.x. Each entry states the goal, the exact
5
+ mechanism, the acceptance criteria, and why it is not enabled yet. Nothing here
6
+ is speculation: the seams named below were read in the harness checkout
7
+ (`dsh-v0.1.5-alpha.1-13`).
8
+
9
+ ## 1. Browser half (Settings card + composer pill)
10
+
11
+ **Status:** implemented on the `v0.2.0` line (client bundle + Typert Remote
12
+ `tier/status|setMode|catalog`); see `src/client/`, `src/wire.ts`,
13
+ `src/typert.host.ts`.
14
+
15
+ **Why it was deferred:** the host surface was complete first (`ctx.autotier`),
16
+ and the client half needs the browser bundle pipeline (tsdown CJS face wrapped
17
+ in `window.__ModuleLoader__.load`), which is a packaging change rather than a
18
+ routing change.
19
+
20
+ ## 2. Fingerprint posterior persistence
21
+
22
+ **Goal:** a restarted harness keeps the per-shape cheap/strong win rates instead
23
+ of re-learning from the judge's cold start.
24
+
25
+ **Mechanism (chosen):** a dedicated settings namespace, not the user-facing
26
+ `autotier` one:
27
+
28
+ ```ts
29
+ ctx.settings.register('autotier-fingerprints', FingerprintSchema, { applies: 'live' })
30
+ ```
31
+
32
+ - `FingerprintSchema` = `{ version: 1, entries: [{ key, cheapOK, cheapN, strongOK, strongN, observations, lastSeen }] }`, capped at the same LRU 2000.
33
+ - Writes are **debounced** (≥30 s between flushes and one final flush on
34
+ `ctx.effect` disposal) and use `replace()` on the whole section, so a user
35
+ editing the document cannot produce a partial merge.
36
+ - On mount, `PosteriorTable.hydrate(section)` validates and loads; a
37
+ `stateVersion`/`version` mismatch discards the section (the same rule the
38
+ session-projection cache uses).
39
+ - A namespace that is absent (a deployment without settings) degrades to the
40
+ current in-memory behaviour — the plugin never depends on persistence.
41
+
42
+ **Why not the `autotier` namespace:** it is user-facing; writing a large learned
43
+ table into it would surface in the Settings card and collide with the user's
44
+ revision on every flush. A separate namespace keeps the two concerns apart.
45
+
46
+ **Acceptance:** a unit test proving hydrate → record → flush → hydrate is
47
+ idempotent; an integration test proving a refused settings write leaves the
48
+ in-memory table intact.
49
+
50
+ **Why not enabled yet:** it adds a write path to a plugin whose current promise
51
+ is "writes only the `autotier` namespace", and the benefit only appears after a
52
+ long enough session history that the cold-start fallback has already paid for
53
+ itself. Ship it behind `persistence.fingerprints: false` default.
54
+
55
+ ## 3. Attempt-first middle band (default on)
56
+
57
+ **Goal:** start ambiguous turns on the cheap tier and escalate on a signal,
58
+ which the cascade literature shows is cheaper *and* faster than deciding up
59
+ front.
60
+
61
+ **What is missing is calibration, not code:** the band, its `tauLow`, the
62
+ verify-once review, and the effort-first ladder are implemented and tested; the
63
+ shipped default is `intent.attemptBand.enabled: false` because the thresholds
64
+ have never been measured on real traffic.
65
+
66
+ **Mechanism:** `scripts/calibrate.mjs` + a 90-case labelled corpus:
67
+
68
+ - Corpus: `fixtures/intent-corpus.jsonl`, one `{ text, expectedTier, hard }` per
69
+ line, balanced across the eight scenarios (11–12 each) plus 10 adversarial
70
+ "looks simple, is complex" cases.
71
+ - The script replays the corpus through the *shipped* classifier (no LLM), then
72
+ sweeps `ruleThreshold`/`tauLow` and prints a table of PGR, APGR, CPT(50 %),
73
+ CPT(80 %), strong-call % and rule-layer hit rate — the same metrics RouteLLM's
74
+ `calibrate_threshold` reports.
75
+ - CI gate (a vitest suite over the same corpus): **CPT(80 %) ≤ 40 %**,
76
+ **APGR ≥ 0.7**, routing quality ≥ 95 % of an always-strong oracle, rule-layer
77
+ zero-token hit ≥ 60 %.
78
+ - Only when the gate is green does the shipped default flip to
79
+ `attemptBand.enabled: true`.
80
+
81
+ **Why not enabled yet:** enabling an uncalibrated band would spend strong calls
82
+ on turns the rules already classify correctly, which is exactly the cost the
83
+ plugin exists to avoid.
84
+
85
+ ## 4. Long-context lane
86
+
87
+ **Goal:** route very long sessions (est. tokens ≥ 30 k) to a model whose context
88
+ window actually fits, instead of failing or compacting mid-turn.
89
+
90
+ **Mechanism:** the same pre-flight module already answers "does this route
91
+ exist"; extend it with `ctx.llm.resolveModelInfo(...).context?.contextWindow`:
92
+
93
+ - When the assembled request's estimated tokens exceed the target route's
94
+ `contextWindow`, pick the first fallback entry (or the other tier) whose
95
+ window fits, and log the substitution once.
96
+ - If nothing fits, leave the route alone — compaction owns that case, and a
97
+ router must not silently drop the user's context.
98
+
99
+ **Why not yet:** `LlmCallConfig` carries no message payload at the request
100
+ waterfall, so the estimate must come from the session's derived history; the
101
+ read is cheap but it is a new dependency on `session.deriveMessages()`, and the
102
+ harness's own compaction is the primary owner of this problem.
103
+
104
+ ## 5. Multi-router coexistence
105
+
106
+ **Status:** implemented in 0.1.2 — a foreign `provider/model` at the request
107
+ waterfall is detected, logged once per session, and surfaced by `/tier status`
108
+ and `tier_status`.
109
+
110
+ ## 6. Explicit user selection
111
+
112
+ **Status:** implemented in 0.1.2 — the applied tier is mirrored into the
113
+ `agent-default-model` document, and an external change to that document moves
114
+ every live session to `delegated` until `/tier auto`.