Tool payload: stop advertising 37 deprecated-alias schemas (41,998 B / ~24 % of payload) #48

Closed
opened 2026-10-24 20:29:38 +00:00 by dsh · 1 comment
Owner

Problem

lib/index.js:420 registers every entry of LEGACY_MAP as a full tool definition. Each one deep-copies the canonical tool's parameter schema (only action is removed from required) and adds a [Deprecated alias for …] description — while the canonical tool stays registered next to it. The same schema is therefore advertised twice.

Measured on the live plugin (dsh-gitea 0.8.4, DSH 0.2.1-rc.2, profile web):

Slice Names Schema bytes ≈ tokens Share of tool payload
All tools in the session 191 174,372 B ~47k 100 %
dsh-gitea, all names 133 107,157 B ~29k 61.5 %
├ deprecated aliases 37 41,998 B ~11k 24.1 %
└ canonical 96 65,159 B ~17.6k 37.4 %

The whole tool payload is ~70 % of a 67,048-token first-turn prompt (cross-checked against the session's own usage record: 17,640 fresh + 49,408 cached tokens). The aliases are the largest removable block in the plugin and add zero capability.

Method: bytes are JSON.stringify(tool).length summed over the data.header.tools array of the session's request/header record; token ratio ~3.7 B/token. Exact numbers drift as tools are added — the code block below regenerates them.

Impact

  • Every session pays ~11k fewer tokens and ~42 KB less payload, whatever the workspace or task.
  • No behaviour change for canonical tools; legacyAliases: true restores the old surface for any external consumer.
  • The preset work (#49) starts from a 65,159 B baseline instead of 107,157 B.

Proposal

  1. Add legacyAliases: z.boolean().default(false); run the loop at lib/index.js:420 only when it is true.
  2. Keep LEGACY_MAP and keep legacy-name dispatch in runHandler, so a replayed old session (or a model echoing an alias name from stale history) still resolves server-side instead of UNKNOWN_TOOL.
  3. Document the change in README + CHANGELOG. The canonical names are what the tool defs, settings UI and docs use today; the aliases date from #305.

The plugin is published on npm (docs + mirror), so a hard delete without a flag is a compatibility decision the operator should make; a default-off flag gets the win now and keeps the switch available.

Scope

  • Handler behaviour, canonical tool defs and formatters untouched.
  • LEGACY_MAP content unchanged — only whether it is advertised.

Acceptance criteria

  • Default boot payload drops by ~41,998 B (measured before/after, numbers posted here)
  • legacyAliases: true restores the 0.8.4 tool set byte-for-byte
  • Canonical tools still work (gitea_labels {action:"set"} covers the gitea_issue_set_labels case, etc.)
  • A stale alias call resolves through dispatch (or the loss is explicitly documented in CHANGELOG)
  • README config table documents legacyAliases; CHANGELOG entry
  • Unit test: alias names absent from the projected schema by default, present with the flag

Verification Plan

zstd -dc ~/.dsh/sessions/<ws>/<session>/session.v4.jsonl.zstd > /tmp/s.jsonl
node -e 'const fs=require("fs");for(const l of fs.readFileSync("/tmp/s.jsonl","utf8").split("\n")){if(!l.includes("\"request/header\""))continue;const t=JSON.parse(l).data.header.tools;console.log(t.length,"tools",t.reduce((a,x)=>a+JSON.stringify(x).length,0),"B");break}'

Then live-check one issue + one label operation and compare the advertised alias count (37 → 0).

Dependencies

None. Establishes the canonical baseline used by #49.

## Problem `lib/index.js:420` registers every entry of `LEGACY_MAP` as a full tool definition. Each one deep-copies the canonical tool's parameter schema (only `action` is removed from `required`) and adds a `[Deprecated alias for …]` description — while the canonical tool stays registered next to it. The same schema is therefore advertised twice. Measured on the live plugin (dsh-gitea 0.8.4, DSH 0.2.1-rc.2, profile `web`): | Slice | Names | Schema bytes | ≈ tokens | Share of tool payload | |---|---|---|---|---| | All tools in the session | 191 | 174,372 B | ~47k | 100 % | | dsh-gitea, all names | 133 | 107,157 B | ~29k | 61.5 % | | ├ deprecated aliases | 37 | **41,998 B** | **~11k** | **24.1 %** | | └ canonical | 96 | 65,159 B | ~17.6k | 37.4 % | The whole tool payload is ~70 % of a 67,048-token first-turn prompt (cross-checked against the session's own `usage` record: 17,640 fresh + 49,408 cached tokens). The aliases are the largest removable block in the plugin and add **zero** capability. Method: bytes are `JSON.stringify(tool).length` summed over the `data.header.tools` array of the session's `request/header` record; token ratio ~3.7 B/token. Exact numbers drift as tools are added — the code block below regenerates them. ## Impact - Every session pays ~11k fewer tokens and ~42 KB less payload, whatever the workspace or task. - No behaviour change for canonical tools; `legacyAliases: true` restores the old surface for any external consumer. - The preset work (#49) starts from a 65,159 B baseline instead of 107,157 B. ## Proposal 1. Add `legacyAliases: z.boolean().default(false)`; run the loop at `lib/index.js:420` only when it is `true`. 2. Keep `LEGACY_MAP` and keep legacy-name dispatch in `runHandler`, so a replayed old session (or a model echoing an alias name from stale history) still resolves server-side instead of `UNKNOWN_TOOL`. 3. Document the change in README + CHANGELOG. The canonical names are what the tool defs, settings UI and docs use today; the aliases date from #305. The plugin is published on npm (docs + mirror), so a hard delete without a flag is a compatibility decision the operator should make; a default-off flag gets the win now and keeps the switch available. ## Scope - Handler behaviour, canonical tool defs and formatters untouched. - `LEGACY_MAP` content unchanged — only whether it is advertised. ## Acceptance criteria - [ ] Default boot payload drops by ~41,998 B (measured before/after, numbers posted here) - [ ] `legacyAliases: true` restores the 0.8.4 tool set byte-for-byte - [ ] Canonical tools still work (`gitea_labels {action:"set"}` covers the `gitea_issue_set_labels` case, etc.) - [ ] A stale alias call resolves through dispatch (or the loss is explicitly documented in CHANGELOG) - [ ] README config table documents `legacyAliases`; CHANGELOG entry - [ ] Unit test: alias names absent from the projected schema by default, present with the flag ## Verification Plan ```bash zstd -dc ~/.dsh/sessions/<ws>/<session>/session.v4.jsonl.zstd > /tmp/s.jsonl node -e 'const fs=require("fs");for(const l of fs.readFileSync("/tmp/s.jsonl","utf8").split("\n")){if(!l.includes("\"request/header\""))continue;const t=JSON.parse(l).data.header.tools;console.log(t.length,"tools",t.reduce((a,x)=>a+JSON.stringify(x).length,0),"B");break}' ``` Then live-check one issue + one label operation and compare the advertised alias count (37 → 0). ## Dependencies None. Establishes the canonical baseline used by #49.
Author
Owner

Fixed in 3d3537b, released as 0.8.5.

  • legacyAliases: z.boolean().default(false) — the flag defaults to off, so the default boot payload drops by the measured 41,998 B / ~11k tokens.
  • Everything stays resolvable: LEGACY_MAP is untouched, runHandler still maps an alias to its canonical tool + default action (lib/handlers.js:50), so a replayed session or a model echoing an alias name from stale history still works. Only the advertised surface shrinks.
  • legacyAliases: true restores the 0.8.4 name set: test/tool-surface.test.mjs boots apply() against a stub registry and asserts 96 names by default, 133 with the flag, plus the exact canonical order.
  • Aliases are also filtered against the selected canonical set (#49), so a narrowed preset never advertises an alias for a tool it excluded.

Measured after the change: canonical full = 96 names / 65,159 B (byte-identical to 0.8.4), full + aliases = 107,157 B. npm run check: 180 tests, leak scan and syntax clean. README config table + CHANGELOG entry document the flag.

Fixed in `3d3537b`, released as **0.8.5**. - `legacyAliases: z.boolean().default(false)` — the flag defaults to off, so the default boot payload drops by the measured 41,998 B / ~11k tokens. - Everything stays resolvable: `LEGACY_MAP` is untouched, `runHandler` still maps an alias to its canonical tool + default action (`lib/handlers.js:50`), so a replayed session or a model echoing an alias name from stale history still works. Only the *advertised* surface shrinks. - `legacyAliases: true` restores the 0.8.4 name set: `test/tool-surface.test.mjs` boots `apply()` against a stub registry and asserts 96 names by default, 133 with the flag, plus the exact canonical order. - Aliases are also filtered against the selected canonical set (#49), so a narrowed preset never advertises an alias for a tool it excluded. Measured after the change: canonical `full` = 96 names / 65,159 B (byte-identical to 0.8.4), `full` + aliases = 107,157 B. `npm run check`: 180 tests, leak scan and syntax clean. README config table + CHANGELOG entry document the flag.
dsh closed this issue 2026-10-24 20:45:18 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
dsh-plugins/dsh-gitea#48
No description provided.