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
package/README.pt.md ADDED
@@ -0,0 +1,246 @@
1
+ # dsh-autotier
2
+
3
+ Roteamento automático por níveis de modelo para o DeepSeek Harness: entra uma
4
+ instrução do usuário, sai uma decisão de nível — sem trocar de modelo à mão.
5
+
6
+ A intenção complexa (arquitetura, planejamento, depuração, engenharia de várias
7
+ etapas) é planejada no nível **strong** e depois implementada no nível **cheap**.
8
+ A intenção simples (perguntas, recuperação, tarefas em lote, trabalho diário) é
9
+ projetada e implementada diretamente no nível **cheap**. Enquanto o nível cheap
10
+ executa, chamadas de ferramenta de alto risco são negadas por um guard
11
+ determinístico, e falhas repetidas escalam para o nível strong com um TTL de
12
+ retorno.
13
+
14
+ - **Repositório oficial**: <https://github.com/PerryLink/dsh-autotier>
15
+ - **npm**: `dsh-autotier` (nome simples, sem scope)
16
+
17
+ ## Compatibilidade
18
+
19
+ | Harness | Estado |
20
+ |---|---|
21
+ | `@deepseek-ai/dsh` `0.1.2-rc.1` | compatível (é o que a CI verifica e o que o fluxo compat instala) |
22
+ | `0.1.5-alpha.1` (a linha atual do checkout) | verificado por tipos contra suas faces publicadas; o smoke ponta a ponta roda em `0.1.2-rc.1` |
23
+ | `@deepseek-ai/cordis` `^4.0.2`, `@deepseek-ai/schemastery` `^3.18.2` | base de peers |
24
+
25
+ O plugin vive apenas no plano host e não precisa de preset próprio: a linha host
26
+ vale para todas as sessões. Uma seção de prompt no *seu* preset é opcional e
27
+ apenas torna as decisões visíveis ao modelo (veja [Instalação e desinstalação](#instalação-e-desinstalação)).
28
+
29
+ ## O que você recebe
30
+
31
+ - **Portão de intenção** — cada turno é classificado por sinais determinísticos
32
+ (texto da mensagem, nomes de ferramentas, presença de imagens, tamanho da
33
+ conversa). A camada de regras decide sem gastar tokens quando tem confiança;
34
+ apenas um turno de baixa confiança chama o modelo juiz barato, e nunca dentro do
35
+ cooldown.
36
+ - **Aterrissagem no seam oficial** — a decisão é aplicada na waterfall
37
+ `agent/request` retornando uma tripla provider/model/effort de substituição. Os
38
+ escalares de amostragem já escolhidos pela sessão (`temperature`, `maxTokens`,
39
+ `stop`) são preservados.
40
+ - **Passagem para o modo plano** — uma instrução complexa entra em modo plano no
41
+ nível strong; ao sair, volta ao nível cheap para implementar.
42
+ - **Guard de alto risco** — durante a execução cheap, comandos destrutivos
43
+ (`rm -rf`, `sudo`, `mkfs`, `git push --force`, escrita de arquivos de
44
+ credenciais, …) são negados com uma mensagem corretiva pedindo escalonamento.
45
+ - **Escalonamento por falhas** — falhas repetidas (opcionalmente com a mesma
46
+ assinatura) elevam o nível por um TTL; uma falha de modelo/rota percorre a
47
+ cadeia de fallback configurada.
48
+ - **Válvulas manuais** — `/tier auto|strong|cheap|off` e as ferramentas
49
+ `tier_status` / `tier_route`. Definir `routingMode: delegated` (ou `/tier off`)
50
+ interrompe o roteamento para uma sessão que precisa manter o próprio modelo.
51
+ - **Serviço `ctx.autotier`** — uma superfície de leitura (`status`) mais a
52
+ waterfall de veto `autotier/route` e o evento `autotier/tier-changed`, para que
53
+ outros plugins observem ou anulem uma decisão.
54
+
55
+ ## Início rápido
56
+
57
+ ```bash
58
+ npm i -g dsh1024
59
+ dsh1024 plugin --profile web add dsh-autotier
60
+ ```
61
+
62
+ Depois inicie (ou reinicie) o harness. A linha é acrescentada ao seu
63
+ `cordis.patch.yml`; o roteamento começa no próximo turno.
64
+
65
+ ## Instalação e desinstalação
66
+
67
+ **Canal npm**
68
+
69
+ ```bash
70
+ npm i -g dsh1024
71
+ dsh1024 plugin --profile web add dsh-autotier
72
+ ```
73
+
74
+ **Canal git**
75
+
76
+ ```bash
77
+ git clone https://github.com/PerryLink/dsh-autotier.git
78
+ cd dsh-autotier && pnpm install && pnpm run build
79
+ dsh plugin --profile web add .
80
+ ```
81
+
82
+ **Seção de prompt opcional no preset.** O router funciona sem ela. Para que o
83
+ modelo saiba em qual nível está, acrescente uma linha ao *seu* preset
84
+ (`docs/preset-row.md` tem o bloco exato):
85
+
86
+ ```yaml
87
+ - insert:
88
+ - id: autotier-prompt
89
+ name: '@deepseek-ai/dsh-system-prompt'
90
+ # sections: [...] — veja docs/preset-row.md
91
+ ```
92
+
93
+ **Desinstalação**
94
+
95
+ ```bash
96
+ dsh plugin --profile web remove dsh-autotier
97
+ ```
98
+
99
+ A linha, seu namespace de settings, seu comando, suas ferramentas e seus
100
+ listeners são removidos com o plugin; nada é escrito fora do documento de
101
+ settings.
102
+
103
+ ## Configuração
104
+
105
+ Cada chave é validada na carga; um valor inválido falha ruidosamente em vez de
106
+ desativar o roteamento em silêncio. O `cordis.patch.yml` deste repositório
107
+ documenta as mesmas chaves em linha.
108
+
109
+ | Chave | Padrão | Significado |
110
+ |---|---|---|
111
+ | `tiers.strong.provider` | `deepseek-official` | Provider do nível de planejamento/revisão. |
112
+ | `tiers.strong.model` | `deepseek-v4-pro` | Id de catálogo do modelo strong. |
113
+ | `tiers.strong.effort` | `high` | Vocabulário do adaptador `off` \| `low` \| `high` \| `max`. |
114
+ | `tiers.strong.followSession` | `false` | `false` = o effort deste nível substitui o da sessão. |
115
+ | `tiers.strong.fallback` | `[]` | Aterrissagens provider/model ordenadas se o nível estiver indisponível. |
116
+ | `tiers.cheap.provider` | `deepseek-official` | Provider do nível de implementação. |
117
+ | `tiers.cheap.model` | `deepseek-v4-flash` | Id de catálogo do modelo cheap. |
118
+ | `tiers.cheap.effort` | `low` | Vocabulário do adaptador `off` \| `low` \| `high` \| `max`. |
119
+ | `tiers.cheap.followSession` | `true` | `true` = herda o effort da sessão e a escolha explícita vence. |
120
+ | `tiers.cheap.fallback` | `[]` | Aterrissagens provider/model ordenadas se o nível estiver indisponível. |
121
+ | `tiers.vision.provider` | `deepseek-official` | Provider para turnos com imagens. |
122
+ | `tiers.vision.model` | `deepseek-v4-flash-vision-exp` | O único modelo do catálogo com modalidade de imagem. |
123
+ | `intent.ruleThreshold` | `0.7` | Confiança a partir da qual a camada de regras decide sozinha. |
124
+ | `intent.attemptBand.enabled` | `false` | Começar a faixa média no cheap e escalar ao primeiro sinal. |
125
+ | `intent.attemptBand.tauLow` | `0.45` | Limite inferior da faixa attempt-first. |
126
+ | `intent.hysteresis.toStrong` | `0.8` | Pontuação que muda um turno cheap para strong. |
127
+ | `intent.hysteresis.toCheap` | `0.6` | Pontuação abaixo da qual um turno strong volta para cheap. |
128
+ | `intent.rules` | `[]` | Tabela declarativa de regras (`when.patterns` / `when.tools` / `when.cwd`, `tier`, `priority`). |
129
+ | `intent.judge.enabled` | `true` | Permitir o juiz de baixa confiança. |
130
+ | `intent.judge.model` | `''` | Id do modelo juiz; vazio = primeiro modelo do catálogo que contenha `flash`. |
131
+ | `intent.judge.temperature` | `0` | Temperatura de amostragem do juiz. |
132
+ | `intent.judge.maxTokens` | `16` | Limite de saída do juiz (responde com uma palavra). |
133
+ | `intent.judge.cooldownMs` | `30000` | Intervalo mínimo entre duas chamadas ao juiz. |
134
+ | `intent.judge.timeoutMs` | `2000` | Tempo limite da chamada ao juiz. |
135
+ | `intent.judge.unavailableSkip` | `2` | Falhas consecutivas após as quais o turno omite o juiz. |
136
+ | `intent.scenarios` | todos `true` | Interruptores por cenário: `coding`, `review`, `planning`, `retrieval`, `batch`, `daily`, `longText`, `multimodal`. |
137
+ | `intent.costMode` | `balanced` | Arbitragem na ambiguidade: `cost-first` \| `quality-first` \| `balanced`. |
138
+ | `guard.enabled` | `true` | Ativar o guard determinístico de alto risco. |
139
+ | `guard.tiers` | `[cheap]` | Níveis que o guard protege. |
140
+ | `guard.whitelist` | `[]` | Comandos, ferramentas ou prefixos de caminho que nunca disparam o guard. |
141
+ | `guard.protectedPaths` | `['.dsh','AGENTS.md','package.json','.github/workflows']` | Superfícies de automodificação que forçam revisão strong. |
142
+ | `guard.interopDefend` | `auto` | Relação com o `dsh-defend`: `auto` audita a convivência, `none` fica silencioso. |
143
+ | `escalation.threshold` | `2` | Falhas dentro da janela que elevam o nível. |
144
+ | `escalation.windowMs` | `60000` | Janela de contagem de falhas. |
145
+ | `escalation.ttlMs` | `180000` | Quanto tempo um escalonamento permanece. |
146
+ | `escalation.fallbackTtlMs` | `300000` | TTL usado após tomar uma aterrissagem de fallback. |
147
+ | `escalation.signature` | `true` | Contar recorrências da mesma assinatura em vez de cada falha. |
148
+ | `routingMode` | `auto` | `auto` \| `strong` \| `cheap` \| `delegated` \| `off`. |
149
+
150
+ Todas as chaves também podem ser editadas a quente pelo namespace de settings
151
+ `autotier` (`$DSH_HOME/settings.yaml`); uma escrita que viole um requisito
152
+ cruzado é recusada ao salvar e a última política válida continua em vigor.
153
+
154
+ ## Ferramentas e superfícies
155
+
156
+ | Superfície | Tipo | Propósito |
157
+ |---|---|---|
158
+ | `/tier` | comando | `auto` \| `strong` \| `cheap` \| `off` \| `status`; substituição por sessão. |
159
+ | `tier_status` | ferramenta | Nível atual, modo, TTL de escalonamento e estado do guard. |
160
+ | `tier_route` | ferramenta | Roteia uma intenção a seco, sem enviar requisição. |
161
+ | `ctx.autotier` | serviço | Superfície `status()` para outros plugins. |
162
+ | `autotier/route` | evento serial | Terceiros podem vetar o nível proposto. |
163
+ | `autotier/tier-changed` | evento emit | Observabilidade quando o nível efetivo muda. |
164
+
165
+ ## Permissões e dados
166
+
167
+ - **Arquivos** — o plugin não lê nem escreve nada além do serviço compartilhado
168
+ de settings (o namespace `autotier`).
169
+ - **Rede** — o único tráfego de saída é a chamada ao juiz, que passa pelo caminho
170
+ normal de `ctx.llm` e pelo provider configurado.
171
+ - **Log de sessão** — o plugin não anexa eventos de sessão próprios. O rastro de
172
+ roteamento é o logger do plugin mais o evento vivo `autotier/tier-changed`; a
173
+ única anexação é o `plan/mode` de reserva quando o serviço de modo plano não
174
+ está disponível. Tipos de evento próprios são fail-closed a partir de
175
+ `0.1.2-alpha.1`, então nenhum registro durável do plugin é escrito.
176
+ - **Segredos** — este plugin não lê, registra nem armazena credenciais.
177
+
178
+ ## Limites de segurança
179
+
180
+ - O guard é **defesa em profundidade**, não uma sandbox. Ele nega os padrões que
181
+ conhece no nível cheap e nunca enfraquece o `dsh-defend`, o serviço de aprovação
182
+ ou a política de sandbox. Mantenha-os ativos.
183
+ - O guard protege apenas os níveis de `guard.tiers` (cheap por padrão). Um turno
184
+ strong não é bloqueado por design: o modelo strong é o revisor.
185
+ - Se o próprio guard lançar, a chamada é escalada para strong em vez de permitida —
186
+ um guard quebrado não deve virar uma porta aberta.
187
+ - `/tier off` desativa o roteamento por completo; o harness se comporta exatamente
188
+ como antes da instalação.
189
+
190
+ ## Limitações conhecidas
191
+
192
+ - A camada de regras é determinística e portanto finita: uma frase nova para um
193
+ pedido complexo pode começar no cheap e escalar só após uma falha ou negação do
194
+ guard. A chamada ao juiz cobre o meio de baixa confiança.
195
+ - O escalonamento é por agente e em memória; um reinício do harness volta a `auto`.
196
+ - Trocar de nível reinicia o cache de prompt do provider para aquela requisição,
197
+ então sessões muito ativas podem ver um pequeno custo de falha de cache no turno
198
+ da troca; os limites de histerese existem para tornar isso raro.
199
+ - O plugin roteia requisições de conversa. Compactação e geração de título são
200
+ seams separados do host; alinhe as configurações de modelo deles com o nível
201
+ cheap para o mesmo perfil de custo (`docs/supporting-lanes.md`).
202
+ - `followSession: true` no nível cheap significa que uma escolha explícita de
203
+ modelo na sessão vence; nesse caso o nível cheap não pode impor o seu.
204
+ - **Ainda não há cartão de Settings nem pílula do compositor.** O roteamento é
205
+ totalmente automático e a superfície host (`ctx.autotier.status()` /
206
+ `catalog()`, `/tier`, `tier_status`, `tier_route`) está completa; a metade do
207
+ navegador está planejada para v0.2.
208
+ - **Um modelo escolhido na GUI não é detectado automaticamente.** Use
209
+ `routingMode: delegated` ou `/tier off` para interromper o roteamento.
210
+ - **As posteriores por impressão digital ficam em memória** e reiniciam a cada
211
+ reinício.
212
+ - **A faixa intermediária attempt-first vem desativada** até o corpus de
213
+ calibração existir (v0.2).
214
+
215
+ ## Desenvolvimento
216
+
217
+ ```bash
218
+ pnpm install
219
+ pnpm run typecheck # contra as faces de tipo do checkout local do harness
220
+ pnpm run typecheck:ci # contra as faces publicadas 0.1.2-rc.1 (o que a CI executa)
221
+ pnpm test
222
+ pnpm run build
223
+ pnpm run verify:self-contained
224
+ pnpm run verify:artifacts
225
+ pnpm pack
226
+ ```
227
+
228
+ `pnpm run build` emite `lib/types` (declarações tsc) e `lib/index.js` (bundle
229
+ tsdown). Os testes usam diretamente os pacotes host publicados — `Context` real,
230
+ serviços reais de session/tools/commands/settings — mais uma composição real do
231
+ Loader sobre um `cordis.yml` temporário.
232
+
233
+ ## Tópicos
234
+
235
+ `dsh`, `dsh-plugin`, `deepseek-harness`, `deepseek`, `cordis`, `router`,
236
+ `model-tier`, `cost`, `auto`.
237
+
238
+ ## Contribuidores
239
+
240
+ PerryLink. Issues e pull requests em
241
+ <https://github.com/PerryLink/dsh-autotier/issues>.
242
+
243
+ ## Licença
244
+
245
+ Apache-2.0. Veja [LICENSE](./LICENSE) e
246
+ [THIRD_PARTY_NOTICES.md](./THIRD_PARTY_NOTICES.md).
package/README.zh.md ADDED
@@ -0,0 +1,221 @@
1
+ # dsh-autotier
2
+
3
+ DeepSeek Harness 的自动模型分档路由:一条用户指令进来,一个档位决策出去 ——
4
+ 无需手动切换模型。
5
+
6
+ 复杂意图(架构、规划、调试、多步工程)先由**强档**规划,再由**弱档**实施;
7
+ 简单意图(问答、检索、批量杂务、日常)直接由**弱档**设计并实施。弱档执行期间,
8
+ 高危工具调用会被确定性守卫拦截;连续失败则按 TTL 自动升级到强档。
9
+
10
+ - **官方仓库**:<https://github.com/PerryLink/dsh-autotier>
11
+ - **npm**:`dsh-autotier`(裸名,无 scope)
12
+
13
+ ## 兼容性
14
+
15
+ | Harness | 状态 |
16
+ |---|---|
17
+ | `@deepseek-ai/dsh` `0.1.2-rc.1` | 兼容(CI 的类型检查与 compat 工作流安装的就是它) |
18
+ | `0.1.5-alpha.1`(当前宿主 checkout 线) | 对照其已发布类型面做类型检查;端到端冒烟跑在 `0.1.2-rc.1` 上 |
19
+ | `@deepseek-ai/cordis` `^4.0.2`、`@deepseek-ai/schemastery` `^3.18.2` | peer 基线 |
20
+
21
+ 本插件只驻留 host 平面,不需要自带 agent preset:host 行对所有会话生效。
22
+ 在你的 preset 中加一段提示是可选项,仅用于让模型看见路由决策
23
+ (见[安装与卸载](#安装与卸载))。
24
+
25
+ ## 功能
26
+
27
+ - **意图门控** —— 每个 turn 都用确定性信号(消息文本、工具名、是否含图、
28
+ 对话长度)分类。零 token 的规则层有把握时直接决策;只有低置信 turn 才会调用
29
+ 廉价的裁判模型,且受冷却时间约束。
30
+ - **落点在官方 seam** —— 决策在 `agent/request` waterfall 上通过返回替换后的
31
+ provider/model/effort 三元组生效;会话已选定的采样标量(`temperature`、
32
+ `maxTokens`、`stop`)保持不变。
33
+ - **计划模式交接** —— 复杂指令在强档进入计划模式;退出计划模式后回到弱档实施。
34
+ - **高危守卫** —— 弱档执行期间,破坏性命令(`rm -rf`、`sudo`、`mkfs`、
35
+ `git push --force`、写凭据文件等)会被拒绝,并给出"请先升级"的纠正信息。
36
+ - **失败升级** —— 连续失败(可按同签名计数)会临时提升档位;模型/链路故障会沿
37
+ 配置的回退链换档。
38
+ - **手动逃生舱** —— `/tier auto|strong|cheap|off` 与 `tier_status` /
39
+ `tier_route` 工具。设置 `routingMode: delegated`(或 `/tier off`)可为必须保留
40
+ 自身模型的会话关闭路由。
41
+ - **`ctx.autotier` 服务** —— 精简的只读面(`status`),外加
42
+ `autotier/route` 否决 waterfall 与 `autotier/tier-changed` 事件,便于其他
43
+ 插件观察或覆盖决策。
44
+
45
+ ## 快速开始
46
+
47
+ ```bash
48
+ npm i -g dsh1024
49
+ dsh1024 plugin --profile web add dsh-autotier
50
+ ```
51
+
52
+ 随后启动(或重启)harness。该行会被追加到你的 profile `cordis.patch.yml`;
53
+ 下一个 turn 起自动路由,无需其他配置。
54
+
55
+ ## 安装与卸载
56
+
57
+ **npm 通道**
58
+
59
+ ```bash
60
+ npm i -g dsh1024
61
+ dsh1024 plugin --profile web add dsh-autotier
62
+ ```
63
+
64
+ **git 通道**
65
+
66
+ ```bash
67
+ git clone https://github.com/PerryLink/dsh-autotier.git
68
+ cd dsh-autotier && pnpm install && pnpm run build
69
+ dsh plugin --profile web add .
70
+ ```
71
+
72
+ **可选的 preset 提示段**:不加也能工作。若想让模型知道自己跑在哪一档,在
73
+ *你自己的* agent preset 中加一行(完整块见 `docs/preset-row.md`):
74
+
75
+ ```yaml
76
+ - insert:
77
+ - id: autotier-prompt
78
+ name: '@deepseek-ai/dsh-system-prompt'
79
+ # sections: [...] — 见 docs/preset-row.md
80
+ ```
81
+
82
+ **卸载**
83
+
84
+ ```bash
85
+ dsh plugin --profile web remove dsh-autotier
86
+ ```
87
+
88
+ 该行、settings 命名空间、命令、工具与全部监听器都随插件移除;除设置文档外
89
+ 不会在别处留下任何写入。
90
+
91
+ ## 配置
92
+
93
+ 每个键都在加载期校验;非法值会响亮失败,而不是静默关闭路由。本仓
94
+ `cordis.patch.yml` 内联注释了同一组键。
95
+
96
+ | 键 | 默认值 | 含义 |
97
+ |---|---|---|
98
+ | `tiers.strong.provider` | `deepseek-official` | 规划/复审档的 provider。 |
99
+ | `tiers.strong.model` | `deepseek-v4-pro` | 强档模型的目录 id。 |
100
+ | `tiers.strong.effort` | `high` | 适配器词表 `off` \| `low` \| `high` \| `max`。 |
101
+ | `tiers.strong.followSession` | `false` | `false` = 本档 effort 覆盖会话自身的设置。 |
102
+ | `tiers.strong.fallback` | `[]` | 该档不可用时的有序 provider/model 落点。 |
103
+ | `tiers.cheap.provider` | `deepseek-official` | 实施档的 provider。 |
104
+ | `tiers.cheap.model` | `deepseek-v4-flash` | 弱档模型的目录 id。 |
105
+ | `tiers.cheap.effort` | `low` | 适配器词表 `off` \| `low` \| `high` \| `max`。 |
106
+ | `tiers.cheap.followSession` | `true` | `true` = 继承会话 effort,让显式选择优先。 |
107
+ | `tiers.cheap.fallback` | `[]` | 该档不可用时的有序 provider/model 落点。 |
108
+ | `tiers.vision.provider` | `deepseek-official` | 含图 turn 的 provider。 |
109
+ | `tiers.vision.model` | `deepseek-v4-flash-vision-exp` | 目录中唯一支持图像模态的模型。 |
110
+ | `intent.ruleThreshold` | `0.7` | 规则层可独立决策的置信度下限。 |
111
+ | `intent.attemptBand.enabled` | `false` | 中间波段:先跑弱档,出现信号再升级。 |
112
+ | `intent.attemptBand.tauLow` | `0.45` | attempt-first 波段的下界。 |
113
+ | `intent.hysteresis.toStrong` | `0.8` | 弱档 turn 切到强档的分数。 |
114
+ | `intent.hysteresis.toCheap` | `0.6` | 强档 turn 回落到弱档的分数。 |
115
+ | `intent.rules` | `[]` | 声明式规则表(`when.patterns` / `when.tools` / `when.cwd`、`tier`、`priority`)。 |
116
+ | `intent.judge.enabled` | `true` | 是否允许低置信裁判。 |
117
+ | `intent.judge.model` | `''` | 裁判模型 id;空 = 取目录中第一个含 `flash` 的模型。 |
118
+ | `intent.judge.temperature` | `0` | 裁判采样温度。 |
119
+ | `intent.judge.maxTokens` | `16` | 裁判输出上限(只回答一个词)。 |
120
+ | `intent.judge.cooldownMs` | `30000` | 两次裁判调用的最小间隔。 |
121
+ | `intent.judge.timeoutMs` | `2000` | 裁判调用超时。 |
122
+ | `intent.judge.unavailableSkip` | `2` | 连续失败多少次后本 turn 跳过裁判。 |
123
+ | `intent.scenarios` | 全 `true` | 分场景开关:`coding`、`review`、`planning`、`retrieval`、`batch`、`daily`、`longText`、`multimodal`。 |
124
+ | `intent.costMode` | `balanced` | 歧义裁决方向:`cost-first` \| `quality-first` \| `balanced`。 |
125
+ | `guard.enabled` | `true` | 启用确定性高危守卫。 |
126
+ | `guard.tiers` | `[cheap]` | 守卫保护的档位。 |
127
+ | `guard.whitelist` | `[]` | 永不触发守卫的命令、工具或路径前缀。 |
128
+ | `guard.protectedPaths` | `['.dsh','AGENTS.md','package.json','.github/workflows']` | 触发强档复核的自修改表面。 |
129
+ | `guard.interopDefend` | `auto` | 与 `dsh-defend` 的关系:`auto` 审计共存,`none` 保持安静。 |
130
+ | `escalation.threshold` | `2` | 窗口内达到该失败数即升级档位。 |
131
+ | `escalation.windowMs` | `60000` | 失败计数窗口。 |
132
+ | `escalation.ttlMs` | `180000` | 升级保持有效的时长。 |
133
+ | `escalation.fallbackTtlMs` | `300000` | 已走回退落点后使用的 TTL。 |
134
+ | `escalation.signature` | `true` | 按同签名复发计数,而非统计每次失败。 |
135
+ | `routingMode` | `auto` | `auto` \| `strong` \| `cheap` \| `delegated` \| `off`。 |
136
+
137
+ 所有键也可通过 `autotier` settings 命名空间(`$DSH_HOME/settings.yaml`)热改;
138
+ 违反跨字段约束的写入会在保存期被拒绝,并保留上一份可用策略。
139
+
140
+ ## 工具与表面
141
+
142
+ | 表面 | 类型 | 用途 |
143
+ |---|---|---|
144
+ | `/tier` | 命令 | `auto` \| `strong` \| `cheap` \| `off` \| `status`;会话级覆盖。 |
145
+ | `tier_status` | 工具 | 当前档位、模式、升级 TTL 与守卫状态。 |
146
+ | `tier_route` | 工具 | 对一段意图做干跑路由,不发送请求。 |
147
+ | `ctx.autotier` | 服务 | 供其他插件读取的 `status()` 面。 |
148
+ | `autotier/route` | serial 事件 | 第三方可否决拟定的档位。 |
149
+ | `autotier/tier-changed` | emit 事件 | 生效档位变化时的可观测性事件。 |
150
+
151
+ ## 权限与数据
152
+
153
+ - **文件** —— 本插件不读文件、不写文件,唯一的写入路径是共享 settings 服务的
154
+ `autotier` 命名空间。
155
+ - **网络** —— 唯一的出站流量是裁判调用,走正常的 `ctx.llm` 路径与所配置的
156
+ provider。
157
+ - **会话日志** —— 本插件不追加任何自定义会话事件。路由留痕 = 插件 logger 与
158
+ 实时 `autotier/tier-changed` 总线事件;唯一的写入是 plan-mode 服务缺失时回退
159
+ 追加的 `plan/mode`。`0.1.2-alpha.1` 起自定义事件类型 fail-closed,因此不会留下
160
+ 插件自有的持久记录。
161
+ - **凭据** —— 本插件不读取、不记录、不存储任何凭据。
162
+
163
+ ## 安全边界
164
+
165
+ - 守卫是**纵深防御**,不是沙箱。它只拒绝弱档上已知的模式,绝不削弱
166
+ `dsh-defend`、审批服务或 sandbox 策略 —— 请保持它们开启。
167
+ - 守卫只保护 `guard.tiers` 列出的档位(默认 cheap)。强档 turn 按设计不被拦截:
168
+ 强模型本身就是复核者。
169
+ - 守卫自身抛错时,调用会被升级到强档而不是放行 —— 坏掉的守卫不能变成敞开的门。
170
+ - `/tier off` 完全关闭路由;harness 的行为与安装本插件之前完全一致。
171
+
172
+ ## 已知限制
173
+
174
+ - 规则层是确定性的、因而也是有限的:复杂请求若换了说法,可能先落到弱档,只有
175
+ 失败或守卫拒绝之后才升级。低置信的中间地带由裁判调用覆盖。
176
+ - 升级状态按 agent 保存在内存中;harness 重启后从 `auto` 重新开始。
177
+ - 切换档位会重置该请求的 provider 提示缓存,因此极高频会话在切换 turn 可能看到
178
+ 少量缓存未命中成本;迟滞阈值的存在就是为了让这件事少见。
179
+ - 本插件只路由对话请求。压缩与标题生成是宿主独立的 seam;若想获得同样的成本
180
+ 画像,请把它们各自的模型设置对齐到弱档(`docs/supporting-lanes.md`)。
181
+ - 弱档 `followSession: true` 意味着会话中显式选择的模型优先;此时弱档无法强制
182
+ 使用自己的模型。
183
+ - **暂无 Settings 卡片与 composer 胶囊**。路由已完全自动,host 面
184
+ (`ctx.autotier.status()` / `catalog()`、`/tier`、`tier_status`、
185
+ `tier_route`)已完整;浏览器半计划于 v0.2。
186
+ - **GUI 里改选模型不会被自动识别**。请用 `routingMode: delegated` 或
187
+ `/tier off` 关闭路由。
188
+ - **指纹后验仅存于内存**,重启后重新学习。
189
+ - **attempt-first 中间波段默认关闭**,待校准语料落地后(v0.2)开启。
190
+
191
+ ## 开发
192
+
193
+ ```bash
194
+ pnpm install
195
+ pnpm run typecheck # 对照本地 harness checkout 的类型面
196
+ pnpm run typecheck:ci # 对照已发布的 0.1.2-rc.1 类型面(CI 实际执行)
197
+ pnpm test
198
+ pnpm run build
199
+ pnpm run verify:self-contained
200
+ pnpm run verify:artifacts
201
+ pnpm pack
202
+ ```
203
+
204
+ `pnpm run build` 产出 `lib/types`(tsc 声明)与 `lib/index.js`(tsdown 打包)。
205
+ 测试直接使用已发布的宿主包 —— 真实 `Context`、真实 session/tools/commands/
206
+ settings 服务 —— 外加一次针对临时 `cordis.yml` 的真实 Loader 组合。
207
+
208
+ ## 主题
209
+
210
+ `dsh`、`dsh-plugin`、`deepseek-harness`、`deepseek`、`cordis`、`router`、
211
+ `model-tier`、`cost`、`auto`。
212
+
213
+ ## 贡献者
214
+
215
+ PerryLink。欢迎在 <https://github.com/PerryLink/dsh-autotier/issues> 提 issue
216
+ 或 PR。
217
+
218
+ ## 许可证
219
+
220
+ Apache-2.0。见 [LICENSE](./LICENSE) 与
221
+ [THIRD_PARTY_NOTICES.md](./THIRD_PARTY_NOTICES.md)。
package/SECURITY.md ADDED
@@ -0,0 +1,55 @@
1
+ # Security policy
2
+
3
+ ## Reporting a vulnerability
4
+
5
+ Please **do not** open a public issue for security vulnerabilities.
6
+
7
+ Report privately through GitHub's private vulnerability reporting:
8
+
9
+ **https://github.com/PerryLink/dsh-autotier/security/advisories/new**
10
+
11
+ That flow keeps the report confidential while we triage, and it is the channel we
12
+ watch first.
13
+
14
+ ## Before you report
15
+
16
+ - **Redact sensitive data** from any logs, session excerpts, or config files you
17
+ attach: tokens, API keys, secrets, webhook URLs with credentials,
18
+ Authorization headers, personal paths, and account identifiers.
19
+ - Include, when possible: the plugin version, the harness (`dsh`) version, Node
20
+ and OS versions, and the minimal steps to reproduce.
21
+
22
+ ## What to expect
23
+
24
+ - **Acknowledgment**: within 5 business days.
25
+ - **Assessment**: we reproduce and classify the report, then tell you whether it
26
+ is a vulnerability in this plugin, in the harness, or a configuration issue.
27
+ - **Fix**: security fixes ship as a patch release with a CHANGELOG entry; we
28
+ credit reporters who ask to be credited.
29
+
30
+ ## Scope
31
+
32
+ In scope:
33
+
34
+ - The high-risk guard bypassing or weakening a denial it claims to enforce.
35
+ - A routing decision that silently sends data to an unintended provider.
36
+ - Credential or secret leakage through logs, events, or the settings namespace.
37
+ - A listener, tool, command or settings namespace surviving plugin disposal.
38
+
39
+ Out of scope:
40
+
41
+ - The guard not knowing a destructive command you did not configure. The guard is
42
+ defence in depth and never replaces `dsh-defend`, the approval service, or the
43
+ sandbox policy.
44
+ - Denial-of-service through a deliberately hostile settings document; invalid
45
+ values fail loudly at load time by design.
46
+ - Vulnerabilities in the harness itself — report those to
47
+ <https://github.com/deepseek-ai/deepseek-harness/security>.
48
+
49
+ ## Hardening advice
50
+
51
+ - Keep `dsh-defend`, the approval service and the sandbox policy enabled.
52
+ - Review `guard.protectedPaths` for your own workspace and add the paths whose
53
+ modification should always be reviewed on the strong tier.
54
+ - Use `/tier off` to disable routing entirely if you need the pre-install
55
+ behavior.
@@ -0,0 +1,63 @@
1
+ # Third-party notices
2
+
3
+ ## dsh-tier-router (`lib/pure.js`)
4
+
5
+ The high-risk command guard in `src/guard-rules.ts` is a port of the rule table
6
+ in `lib/pure.js` from
7
+ [dsh-tier-router](https://github.com/BruceLanLan/dsh-tier-router) (v0.5.0),
8
+ licensed **MIT**:
9
+
10
+ ```
11
+ MIT License
12
+
13
+ Copyright (c) 2026 BruceLanLan
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
+
34
+ The port keeps the upstream command/path vocabulary and pattern semantics and
35
+ adds: tier-conditional enforcement (the guard protects the cheap tier only), a
36
+ configurable whitelist, `guard.protectedPaths` review escalation, and a
37
+ structured denial reason. No upstream file is bundled verbatim; the port is
38
+ annotated per module with the source file and version.
39
+
40
+ ### Deliberate deltas over upstream
41
+
42
+ These are this plugin's own additions (Apache-2.0), not upstream code:
43
+
44
+ 1. **Shell-wrapper re-scan.** Upstream misses `sh -c "rm -rf /"` (the payload is
45
+ quoted, so the command-position anchor never sees `rm`). `matchCommand` first
46
+ applies the upstream rules unchanged, then re-scans the `-c` payload of
47
+ `sh|bash|zsh|dash|ksh` (including combined flag clusters such as `-lc`,
48
+ case-insensitive names, an optional leading backslash, and a preceding runner
49
+ with its own arguments), bounded to three nesting levels. An inner match is
50
+ reported as `shell-wrapper:<inner rule id>`. `hasRecursiveForceRm` itself
51
+ stays byte-for-byte upstream, and the upstream false negative is asserted in
52
+ the test suite as a documented regression.
53
+ 2. **Fallback classification split.** Upstream's `classifyFallback` tri-state is
54
+ split into `permanent`/`transient`/`ignore`/`unknown` so the request-error
55
+ handler can honour its division of labour with `dsh-llm-retry` (permanent
56
+ codes switch the chain immediately; transient codes wait for retry
57
+ exhaustion).
58
+ 3. **Effort vocabulary.** Upstream's `['medium', 'high', 'max']` ladder is
59
+ replaced by the adapter-owned `off | low | high | max`; `medium` does not
60
+ exist on this host and would fail every request.
61
+
62
+ The read-only reference checkout used for the port lives outside this repository
63
+ and is never shipped.