dsh-autotier 0.1.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 (60) hide show
  1. package/AGENTS.md +93 -0
  2. package/CHANGELOG.md +85 -0
  3. package/LICENSE +201 -0
  4. package/README.es.md +247 -0
  5. package/README.hi.md +241 -0
  6. package/README.md +245 -0
  7. package/README.pt.md +246 -0
  8. package/README.zh.md +221 -0
  9. package/SECURITY.md +55 -0
  10. package/THIRD_PARTY_NOTICES.md +63 -0
  11. package/cordis.patch.yml +125 -0
  12. package/docs/preset-row.md +61 -0
  13. package/docs/supporting-lanes.md +45 -0
  14. package/lib/index.js +2848 -0
  15. package/lib/types/command.d.ts +17 -0
  16. package/lib/types/command.d.ts.map +1 -0
  17. package/lib/types/config.d.ts +94 -0
  18. package/lib/types/config.d.ts.map +1 -0
  19. package/lib/types/guard-rules.d.ts +97 -0
  20. package/lib/types/guard-rules.d.ts.map +1 -0
  21. package/lib/types/guard.d.ts +70 -0
  22. package/lib/types/guard.d.ts.map +1 -0
  23. package/lib/types/index.d.ts +60 -0
  24. package/lib/types/index.d.ts.map +1 -0
  25. package/lib/types/intent.d.ts +179 -0
  26. package/lib/types/intent.d.ts.map +1 -0
  27. package/lib/types/judge.d.ts +50 -0
  28. package/lib/types/judge.d.ts.map +1 -0
  29. package/lib/types/policy.d.ts +109 -0
  30. package/lib/types/policy.d.ts.map +1 -0
  31. package/lib/types/routing.d.ts +135 -0
  32. package/lib/types/routing.d.ts.map +1 -0
  33. package/lib/types/schema.d.ts +134 -0
  34. package/lib/types/schema.d.ts.map +1 -0
  35. package/lib/types/service.d.ts +67 -0
  36. package/lib/types/service.d.ts.map +1 -0
  37. package/lib/types/state.d.ts +46 -0
  38. package/lib/types/state.d.ts.map +1 -0
  39. package/lib/types/tiers.d.ts +103 -0
  40. package/lib/types/tiers.d.ts.map +1 -0
  41. package/lib/types/tools.d.ts +26 -0
  42. package/lib/types/tools.d.ts.map +1 -0
  43. package/lib/types/types.d.ts +96 -0
  44. package/lib/types/types.d.ts.map +1 -0
  45. package/package.json +179 -0
  46. package/src/command.ts +73 -0
  47. package/src/config.ts +358 -0
  48. package/src/guard-rules.ts +303 -0
  49. package/src/guard.ts +285 -0
  50. package/src/index.ts +149 -0
  51. package/src/intent.ts +484 -0
  52. package/src/judge.ts +150 -0
  53. package/src/policy.ts +246 -0
  54. package/src/routing.ts +575 -0
  55. package/src/schema.ts +295 -0
  56. package/src/service.ts +131 -0
  57. package/src/state.ts +134 -0
  58. package/src/tiers.ts +212 -0
  59. package/src/tools.ts +128 -0
  60. package/src/types.ts +120 -0
@@ -0,0 +1,125 @@
1
+ # dsh-autotier: automatic model-tier routing for DeepSeek Harness.
2
+ #
3
+ # One user instruction enters, one tier decision comes out. Complex intent is
4
+ # planned on the strong tier and implemented on the cheap tier; simple intent is
5
+ # designed and implemented on the cheap tier directly. High-risk tool calls are
6
+ # denied while the cheap tier executes, and repeated failures escalate to the
7
+ # strong tier with a TTL fallback. Everything below is a Schemastery-validated
8
+ # config field; an invalid value fails loudly at load time.
9
+ - insert:
10
+ - id: autotier
11
+ name: dsh-autotier
12
+ # `sessions` is plural: the host service is `ctx.sessions`, and a
13
+ # non-existent name here would leave this plugin PENDING forever.
14
+ inject: [settings, llm, tools, commands, sessions]
15
+ config:
16
+ # ---- tiers ----------------------------------------------------------
17
+ tiers:
18
+ # The planning/review tier. `effort` is the adapter-owned vocabulary
19
+ # off | low | high | max; `medium` does not exist and would fail every
20
+ # request with UNSUPPORTED_REASONING_EFFORT.
21
+ strong:
22
+ provider: deepseek-official
23
+ model: deepseek-v4-pro
24
+ effort: high
25
+ # false = this tier's effort overrides the session's own.
26
+ followSession: false
27
+ # Ordered fallback landings (provider/model) used when the tier is
28
+ # unavailable. The chain is deduplicated and must not name the tier
29
+ # itself; validation refuses a self-referential chain at load time.
30
+ fallback: []
31
+ # The implementation tier.
32
+ cheap:
33
+ provider: deepseek-official
34
+ model: deepseek-v4-flash
35
+ effort: low
36
+ # true = inherit the session's effort, so an explicit user choice
37
+ # still wins inside the cheap tier.
38
+ followSession: true
39
+ fallback: []
40
+ # Image-capable landing: a turn carrying images is routed here,
41
+ # because a text-only model silently substitutes image placeholders.
42
+ vision:
43
+ provider: deepseek-official
44
+ model: deepseek-v4-flash-vision-exp
45
+ # ---- intent classification -----------------------------------------
46
+ intent:
47
+ # Confidence at or above which the deterministic rule layer decides
48
+ # without calling the judge model.
49
+ ruleThreshold: 0.7
50
+ # Attempt-first middle band: scores in [tauLow, ruleThreshold) start on
51
+ # the cheap tier and escalate on a signal. Disabled until the
52
+ # calibration corpus lands.
53
+ attemptBand:
54
+ enabled: false
55
+ tauLow: 0.45
56
+ # Application-side double threshold that stops tier flapping between
57
+ # turns; the decision threshold above is unchanged.
58
+ hysteresis:
59
+ toStrong: 0.8
60
+ toCheap: 0.6
61
+ # Declarative rule table. Rules are evaluated by descending priority;
62
+ # the first match wins, and a guard denial always outranks a rule.
63
+ # `when.patterns` are regular expressions matched against the user
64
+ # text, `when.tools` against the tool name, `when.cwd` against the
65
+ # workspace path (empty = no constraint). `tier` is cheap | strong.
66
+ # Example entry:
67
+ # - id: plan-first
68
+ # when:
69
+ # patterns: ['^plan\\b', 'architecture']
70
+ # tools: []
71
+ # cwd: ''
72
+ # tier: strong
73
+ # priority: 10
74
+ rules: []
75
+ # The low-confidence judge: a cheap model classifies intent only.
76
+ judge:
77
+ enabled: true
78
+ # Empty = pick the first catalog model whose id contains "flash".
79
+ model: ''
80
+ temperature: 0
81
+ maxTokens: 16
82
+ cooldownMs: 30000
83
+ timeoutMs: 2000
84
+ # Consecutive judge failures after which the current turn skips the
85
+ # judge instead of paying its timeout on every request.
86
+ unavailableSkip: 2
87
+ # Per-scenario switches: coding, review, planning, retrieval, batch,
88
+ # daily, longText, multimodal.
89
+ scenarios:
90
+ coding: true
91
+ review: true
92
+ planning: true
93
+ retrieval: true
94
+ batch: true
95
+ daily: true
96
+ longText: true
97
+ multimodal: true
98
+ # Ambiguity arbitration: cost-first | quality-first | balanced.
99
+ costMode: balanced
100
+ # ---- high-risk guard ------------------------------------------------
101
+ guard:
102
+ enabled: true
103
+ # Tiers whose execution the guard protects (cheap only).
104
+ tiers: [cheap]
105
+ # Commands, tools or path prefixes that never trip the guard.
106
+ whitelist: []
107
+ # Self-modification surfaces that force strong-tier review.
108
+ protectedPaths: ['.dsh', 'AGENTS.md', 'package.json', '.github/workflows']
109
+ # Relationship with dsh-defend: auto audits coexistence, none stays
110
+ # silent. The guard never weakens dsh-defend.
111
+ interopDefend: auto
112
+ # ---- failure escalation ---------------------------------------------
113
+ escalation:
114
+ # Failures within windowMs that reach this count escalate the tier.
115
+ threshold: 2
116
+ windowMs: 60000
117
+ # How long an escalation stays in effect before falling back.
118
+ ttlMs: 180000
119
+ # TTL used after a fallback landing was taken.
120
+ fallbackTtlMs: 300000
121
+ # Count same-signature recurrences instead of every failure.
122
+ signature: true
123
+ # auto | strong | cheap | delegated | off. `delegated` means the session
124
+ # carries an explicit model selection that autotier must not fight.
125
+ routingMode: auto
@@ -0,0 +1,61 @@
1
+ # Optional preset prompt section
2
+
3
+ `dsh-autotier` is a **host-plane** plugin: the profile row applies to every
4
+ session and routing works with no preset change. This page is for users who want
5
+ the model to *know* which tier it is running on.
6
+
7
+ ## Why this is optional
8
+
9
+ The host row is registered on the root scope, so it covers every agent,
10
+ including subagents, without touching any preset. A preset section only adds
11
+ model-facing text — it never changes routing, and the plugin works correctly
12
+ when the section is absent (`/tier status` reports `prompt-section: absent`).
13
+
14
+ This is deliberate: shipping a preset with the package would freeze a snapshot
15
+ of the host's `standard` preset and drift on every harness release.
16
+
17
+ ## The row
18
+
19
+ Add one row to **your own** agent preset's `cordis.patch.yml` (the file that
20
+ declares the preset's plugin rows):
21
+
22
+ ```yaml
23
+ - insert:
24
+ - id: autotier-prompt
25
+ name: '@deepseek-ai/dsh-system-prompt'
26
+ config:
27
+ sections:
28
+ - name: autotier
29
+ text: |-
30
+ You are running under dsh-autotier routing. The active tier is
31
+ chosen per turn: complex work is planned on the strong tier and
32
+ implemented on the cheap tier; simple work runs on the cheap tier
33
+ throughout. If a tool call is denied by the high-risk guard, do not
34
+ retry it — report what you were about to do and let the tier
35
+ escalate.
36
+ ```
37
+
38
+ The exact `sections` shape is owned by the host `system-prompt` package; if your
39
+ harness version expects a different key, follow the error the Loader prints —
40
+ the plugin itself does not depend on this row.
41
+
42
+ ## Verifying it took effect
43
+
44
+ `/tier status` prints the live routing state; the optional section itself is
45
+ visible in the assembled system prompt, not in `/tier` output. If you rename the
46
+ section, keep the name `autotier` so you can find it in the prompt.
47
+
48
+ ## Per-preset isolation
49
+
50
+ If you want a preset to opt out of routing entirely, compose the plugin inside
51
+ an `isolate` realm instead of the root scope:
52
+
53
+ ```yaml
54
+ - isolate:
55
+ autotier: true
56
+ insert:
57
+ - id: autotier
58
+ name: dsh-autotier
59
+ ```
60
+
61
+ A realm-isolated row only affects the sessions of that preset.
@@ -0,0 +1,45 @@
1
+ # Supporting lanes: compaction and title generation
2
+
3
+ `dsh-autotier` routes **conversation requests** — the ones the agent loop
4
+ assembles for a turn. Two other host seams make their own model calls and are
5
+ deliberately left alone:
6
+
7
+ | Lane | Host seam | Owner package |
8
+ |---|---|---|
9
+ | Context compaction | its own model settings | `@deepseek-ai/dsh-compaction` |
10
+ | Session titles | its own model settings | `@deepseek-ai/dsh-session-title-llm` |
11
+
12
+ Routing them from here would be wrong twice over: they are not turn-scoped, so a
13
+ per-turn tier decision does not apply, and rewriting their calls would surprise a
14
+ user who configured them explicitly.
15
+
16
+ ## Aligning their cost profile
17
+
18
+ If you want the whole harness to sit on the cheap model, point each lane's own
19
+ configuration at the same landing as `tiers.cheap`:
20
+
21
+ ```yaml
22
+ - insert:
23
+ - id: compaction
24
+ name: '@deepseek-ai/dsh-compaction'
25
+ config:
26
+ provider: deepseek-official
27
+ model: deepseek-v4-flash
28
+ reasoningEffort: low
29
+
30
+ - id: session-title-llm
31
+ name: '@deepseek-ai/dsh-session-title-first-prompt-llm'
32
+ config:
33
+ provider: deepseek-official
34
+ model: deepseek-v4-flash
35
+ ```
36
+
37
+ The key names above are the host packages' own; check the installed version's
38
+ schema if a row is rejected. `dsh-autotier` never writes these rows.
39
+
40
+ ## Judge calls
41
+
42
+ The low-confidence judge uses `intent.judge.model` (or the first catalog model
43
+ whose id contains `flash`) through the ordinary `ctx.llm` path. It is a separate,
44
+ short request with its own `maxTokens` and `timeoutMs`, so it never inherits a
45
+ tier's landing — pin it to a cheap model on purpose.