@jianxx/dsh-cc-model-aliases 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.
package/LICENSE ADDED
@@ -0,0 +1,201 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright [yyyy] [name of copyright owner]
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
@@ -0,0 +1,6 @@
1
+ # Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
2
+ # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
+ # after editing either side, bring the other along and re-record with:
4
+ # pnpm run verify-translation-pairing --write packages/compat/cc-model-aliases/README.md
5
+ README.md: 3b8fb6f22176a9e87eb137f9f3a2b5442f4917de
6
+ README.zh.md: df9e7ff2eee16a757155c5eac994d50dc50a1854
package/README.md ADDED
@@ -0,0 +1,157 @@
1
+ # @jianxx/dsh-cc-model-aliases
2
+
3
+ English | [中文](README.zh.md)
4
+
5
+ Claude Code-compatible model alias resolution for the DeepSeek Harness. It maps Claude Code
6
+ frontmatter model aliases (`model: opus`, `model: sonnet`) onto dsh `{provider, model}`
7
+ routes. The package now ships in two shapes:
8
+
9
+ - a **`ccModelRoutes` host service** (the cordis plugin entry) that owns the `model-aliases`
10
+ settings namespace registration and exposes a spawn-time resolver, and
11
+ - the **pure helpers** (`mergeAliasMaps` / `createModelResolver`) for embedding the same
12
+ resolution semantics without mounting the service.
13
+
14
+ ## Why
15
+
16
+ Claude Code agent/CLAUDE.md frontmatter names models by alias. Without an alias
17
+ layer, `model: opus` was passed through verbatim as a provider model id, so an
18
+ adapter that does not know the id (e.g. `llm-pi-ai`) threw `UNKNOWN_MODEL`, and
19
+ `model: inherit` — a valid CC sentinel meaning "use my parent's model" — was
20
+ also handed to `prepareCall` as a literal id and errored.
21
+
22
+ This package adds that layer: an alias resolves to a `{provider, model}` route,
23
+ the unresolvable cases fall back to *inheriting the parent route* (no override),
24
+ and literal ids like `deepseek-chat` keep passing through untouched.
25
+
26
+ ## The `ccModelRoutes` service
27
+
28
+ The plugin entry (`name: 'cc-model-routes'`, `apply`) is what the CC preset mounts via the
29
+ `cc-model-routes` row. It:
30
+
31
+ - registers the `model-aliases` **settings namespace** — but **only when** a settings
32
+ provider (`ctx.get('settings')`) is mounted, so a settings-less host degrades to the config
33
+ defaults plus the builtin fallback (this namespace registration is the single owner of that
34
+ name; the harness throws on a duplicate registration, see `dsh-settings`);
35
+ - registers the namespace with a **write-time `validate`** that rejects a half-written
36
+ `{provider, model}` route (a non-empty cross-field check the dict schema itself cannot
37
+ express);
38
+ - and provides the spawn-time resolver as the **`ccModelRoutes`** value (`ctx.provide`) whose
39
+ `resolve(model)` reads the **live** settings scope on every invocation — a settings write
40
+ applies to the next spawn with no re-registration.
41
+
42
+ Consumers `ctx.get('ccModelRoutes')` **lazily** on every spawn. Lazily means mount order does
43
+ not matter: before the provider's fiber is active, `ctx.get` returns `undefined`, which
44
+ resolves to "inherit the parent route" (the same no-override behavior as before).
45
+
46
+ ## How the cc-shell bundle wires it
47
+
48
+ - `Config.modelAliases` provides **deployment defaults** (alias name → model id
49
+ or `{provider, model}`).
50
+ - The `model-aliases` **settings namespace** registration now lives in the `ccModelRoutes`
51
+ service, layered exactly like every other settings section (user/project/local/flags).
52
+ - cc-shell's `AgentProvider` gains its `resolveModel` as a **trampoline** over the service:
53
+ `(model) => ctx.get('ccModelRoutes')?.resolve(model)` — queried **lazily on every spawn**,
54
+ read fresh (no snapshot captured at apply time), and degrading to inherit when the service
55
+ is not mounted (`undefined` resolution = inherit the parent route, byte-compatible with the
56
+ old no-resolver fallback). cc-shell no longer registers the namespace itself.
57
+ - The Task tool (`@jianxx/dsh-cc-subagent-task`) is the other consumer: it resolves a
58
+ subagent definition's frontmatter `model` through the same `ccModelRoutes` resolver at
59
+ spawn time.
60
+
61
+ ## Configuration
62
+
63
+ ```yaml
64
+ # ccModelRoutes service config (the preset's cc-model-routes row)
65
+ modelAliases:
66
+ sonnet: deepseek-v4-flash # string form: model only, provider inherits
67
+ opus: { provider: deepseek-official, model: deepseek-v4-pro } # explicit route
68
+ ```
69
+
70
+ ```jsonc
71
+ // settings namespace "model-aliases"
72
+ {
73
+ "fable": { "provider": "anthropic", "model": "claude-fable-5" },
74
+ "sonnet": null // null = delete the same-named config-default alias
75
+ }
76
+ ```
77
+
78
+ ## Resolution semantics
79
+
80
+ Lookup order for an alias: **settings overlay → config defaults → builtin
81
+ fallback**. Alias key matching is **case-insensitive** (keys are folded to
82
+ lowercase at merge and at lookup).
83
+
84
+ | `model` frontmatter | Result |
85
+ |---|---|
86
+ | `undefined` / blank | no override — child inherits the parent route |
87
+ | `inherit` (any case) | no override — child inherits the parent route (**fixes the old pass-through bug**) |
88
+ | configured alias (string form) | `{ model: <target> }` |
89
+ | configured alias (object form) | `{ provider: <p>, model: <m> }` |
90
+ | builtin alias unconfigured (`fable`/`opus`/`sonnet`/`haiku`) | no override — child inherits the parent route ("current model") |
91
+ | anything else | passed through **verbatim** as a literal model id; a bare-lowercase-word form (e.g. `turbo`) logs a warning that it looks like an unconfigured alias |
92
+
93
+ ### Null deletion
94
+
95
+ Only the **settings** layer may set an entry to `null`; that deletes a
96
+ same-named **config-default** entry (entry-shallow). Deleting a *builtin* alias
97
+ still falls through to the builtin fallback — `null` cannot turn `sonnet` into an
98
+ error, because the builtin fallback is **inherit the parent route**. Config may
99
+ never hold `null` (rejected by the config schema).
100
+
101
+ ### Merge rules
102
+
103
+ - **Config vs settings is entry-shallow**: a settings entry replaces the config
104
+ entry wholesale. It never field-merges `{provider, model}` objects — so
105
+ `config { provider: A, model: X }` + `settings { model: Y }` yields
106
+ `{ model: Y }`, never a blended `{ provider: A, model: Y }` that no single
107
+ layer declared.
108
+ - **Within the settings 5-cascade the merge is recursively deep** (existing
109
+ cascade behavior). Consequently an object-form alias must be written **whole
110
+ or not at all** across settings layers, or the cascade will field-blend
111
+ `{provider, model}` just as described above — this is a cascade-level
112
+ behavior this package does not change. Prefer string-form aliases, or repeat
113
+ the full route on every layer that mentions the alias.
114
+
115
+ ### Builtin fallback
116
+
117
+ Fresh installs with **zero configuration** still work: `model: sonnet` /
118
+ `model: opus` agents resolve to *inherit the parent's current model* instead of
119
+ erroring. Only the four builtin names get this fallback; a custom alias
120
+ (`turbo`, `gpt`, …) that is unconfigured has **no** fallback and is passed
121
+ through verbatim (with the warning above).
122
+
123
+ ## `inherit` fix note
124
+
125
+ Prior to this package, `model: inherit` was forwarded as the literal model id
126
+ all the way to `prepareCall`, where it failed. In CC mode the spawn-time resolver
127
+ (from the `ccModelRoutes` service, consumed via the cc-shell trampoline and the
128
+ Task tool) maps `inherit` to "no override", so the child inherits the parent
129
+ route, matching CC semantics. When no resolver is mounted (non-cc consumers of
130
+ `@jianxx/dsh-cc-plugin-loader` that do not set `resolveModel`), behavior stays
131
+ byte-identical to before — including the old `inherit` pass-through — because the
132
+ no-resolver fallback is preserved exactly. The CC preset unconditionally mounts
133
+ cc-shell (and the routes service), so in CC mode the fix is always active.
134
+
135
+ ## API
136
+
137
+ - `apply(ctx, config?)` / `name` — **cordis plugin entry** (plugin id `cc-model-routes`,
138
+ also re-exported as `applyRoutes` / `routesPluginName`). Mounts the service; config shape is
139
+ `{ modelAliases?: Record<string, AliasTarget> }` (deployment defaults).
140
+ - `ModelRoutes` — the value type of `ctx.get('ccModelRoutes')` (`resolve(model):
141
+ { provider?, model? } | undefined`).
142
+ - `mergeAliasMaps(config, settings)` — entry-shallow merge with `null` deletion
143
+ and case-insensitive key folding; returns an effective `ReadonlyMap` of only
144
+ the configured aliases.
145
+ - `createModelResolver(getAliases, { warn })` — build a `resolveModel` closure.
146
+ `getAliases` is a thunk evaluated **per invocation** (liveness). Optional
147
+ `warn` replaces the default `console.warn` used for the unconfigured-custom-alias
148
+ warning.
149
+ - `BUILTIN_ALIASES` — `['fable', 'opus', 'sonnet', 'haiku']`.
150
+ - `ConfigAliasesSchema` / `SettingsAliasesSchema` (and their record forms) —
151
+ schemastery schemas for the config layer (no `null`) and settings layer
152
+ (`null` allowed), plus `AliasTarget` / `ResolvedRoute` types.
153
+
154
+ ## Non-goals (tracked in the parity matrix)
155
+
156
+ `/model` interactive command, `ANTHROPIC_MODEL` / `ANTHROPIC_SMALL_FAST_MODEL`
157
+ env vars, and aliasing the main session's default model remain follow-ups.
package/README.zh.md ADDED
@@ -0,0 +1,91 @@
1
+ # @jianxx/dsh-cc-model-aliases
2
+
3
+ [English](README.md) | 中文
4
+
5
+ 面向 DeepSeek Harness 的 Claude Code 兼容 model alias 解析。把 CC frontmatter 的 model alias(`model: opus`、`model: sonnet`)映射为 dsh 的 `{provider, model}` 路由。本 package 现在以两种形态提供:
6
+
7
+ - **`ccModelRoutes` 宿主服务**(cordis 插件入口),拥有 `model-aliases` settings 命名空间的注册,并暴露派发时的解析器;
8
+ - **纯 helper**(`mergeAliasMaps` / `createModelResolver`),在不挂载服务的情况下内嵌同样的解析语义。
9
+
10
+ ## 为什么
11
+
12
+ Claude Code 的 agent/CLAUDE.md frontmatter 用 alias 命名模型。没有 alias 层时,`model: opus` 会被原样当作 provider model id 透传,因此不认识该 id 的 adapter(如 `llm-pi-ai`)抛 `UNKNOWN_MODEL`;而 `model: inherit`——CC 中意为「用父模型」的合法哨兵——也会被当作字面 id 交给 `prepareCall` 然后报错。
13
+
14
+ 本 package 补上这层:alias 解析为 `{provider, model}` 路由,不可解析的情形回退为*继承父路由*(不 override),而 `deepseek-chat` 之类的字面 id 原样透传不受影响。
15
+
16
+ ## `ccModelRoutes` 服务
17
+
18
+ 插件入口(`name: 'cc-model-routes'`、`apply`)即 CC preset 通过 `cc-model-routes` 行挂载的东西。它:
19
+
20
+ - 注册 `model-aliases` **settings 命名空间**——但**仅当**存在 settings provider(`ctx.get('settings')`)时,因此无 settings 的宿主降级为 config 默认 + builtin fallback(该命名空间注册是此名的唯一属主;重复注册 harness 会 throw,见 `dsh-settings`);
21
+ - 以**写时 `validate`** 注册该命名空间,拒绝半写的 `{provider, model}` 路由(dict schema 本身无法表达的非空跨字段校验);
22
+ - 并把派发时解析器以 **`ccModelRoutes`** 值(`ctx.provide`)提供,其 `resolve(model)` 每次调用**现读** settings scope——settings 写回即在下次派发生效,无需重新注册。
23
+
24
+ 消费者在每次派发时**惰性** `ctx.get('ccModelRoutes')`。惰性意味着挂载顺序无关:provider fiber 激活之前,`ctx.get` 返回 `undefined`,解析为「继承父路由」(与之前的 no-override 行为相同)。
25
+
26
+ ## How the cc-shell bundle wires it(如何接线)
27
+
28
+ - `Config.modelAliases` 提供**部署默认**(alias 名 → model id 或 `{provider, model}`)。
29
+ - `model-aliases` **settings 命名空间**的注册现在住在 `ccModelRoutes` 服务里,与其它 settings section 一样分层(user/project/local/flags)。
30
+ - cc-shell 的 `AgentProvider` 通过**trampoline** 获得 `resolveModel`:`(model) => ctx.get('ccModelRoutes')?.resolve(model)`——**每次派发惰性查询**、现读(apply 时不快照),服务未挂载时降级为继承(`undefined` 解析 = 继承父路由,与旧 no-resolver fallback 字节兼容)。cc-shell 自己不再注册该命名空间。
31
+ - Task 工具(`@jianxx/dsh-cc-subagent-task`)是另一消费方:它在派发时以同一个 `ccModelRoutes` 解析器解析 subagent 定义 frontmatter 的 `model`。
32
+
33
+ ## 配置
34
+
35
+ ```yaml
36
+ # ccModelRoutes 服务配置(预设的 cc-model-routes 行)
37
+ modelAliases:
38
+ sonnet: deepseek-v4-flash # 字符串形式:仅 model,provider 继承
39
+ opus: { provider: deepseek-official, model: deepseek-v4-pro } # 显式路由
40
+ ```
41
+
42
+ ```jsonc
43
+ // settings 命名空间 "model-aliases"
44
+ {
45
+ "fable": { "provider": "anthropic", "model": "claude-fable-5" },
46
+ "sonnet": null // null = 删除同名的 config-default alias
47
+ }
48
+ ```
49
+
50
+ ## 解析语义
51
+
52
+ alias 的查找顺序:**settings overlay → config 默认 → builtin fallback**。alias key 匹配**大小写不敏感**(merge 与 lookup 时 key 折叠为小写)。
53
+
54
+ | `model` frontmatter | 结果 |
55
+ |---|---|
56
+ | `undefined` / 空 | 无 override——child 继承父路由 |
57
+ | `inherit`(任意大小写) | 无 override——child 继承父路由(**修复旧的透传 bug**) |
58
+ | 已配置 alias(字符串形式) | `{ model: <target> }` |
59
+ | 已配置 alias(对象形式) | `{ provider: <p>, model: <m> }` |
60
+ | 未配置的 builtin alias(`fable`/`opus`/`sonnet`/`haiku`) | 无 override——child 继承父路由(「当前模型」) |
61
+ | 其它 | **原样**作为字面 model id 透传;裸小写单词形式(如 `turbo`)记录一条「看起来像未配置 alias」的告警 |
62
+
63
+ ### 空删除
64
+
65
+ 只有 **settings** 层可把条目置为 `null`;这会**整条删除同名 config-default 条目**(entry-shallow)。删除 *builtin* alias 仍落到 builtin fallback——`null` 无法让 `sonnet` 变成错误,因为 builtin fallback 是**继承父路由**。config 层永不允许 `null`(被 config schema 拒绝)。
66
+
67
+ ### Merge 规则
68
+
69
+ - **config vs settings 是 entry-shallow**:settings 条目整体替换 config 条目,绝不 field-merge `{provider, model}` 对象——因此 `config { provider: A, model: X }` + `settings { model: Y }` 得到 `{ model: Y }`,而不会是没有任何单层声明的混合 `{ provider: A, model: Y }`。
70
+ - **settings 5 级 cascade 内是递归深 merge**(既有 cascade 行为)。因此对象形式 alias 跨 settings 层必须**整写或完全不写**,否则 cascade 会像上面描述的那样 field-blend `{provider, model}`——这是本 package 不改变的 cascade 层行为。优先用字符串形式 alias,或在提及该 alias 的每一层重复完整路由。
71
+
72
+ ### Builtin fallback
73
+
74
+ **零配置**的全新安装也能工作:`model: sonnet` / `model: opus` 的 agent 解析为*继承父的当前模型*而非报错。只有四个 builtin 名享受该 fallback;未配置的自定义 alias(`turbo`、`gpt`、…)**没有** fallback,原样透传(带上面的告警)。
75
+
76
+ ## `inherit` 修复说明
77
+
78
+ 本 package 之前,`model: inherit` 会一路原样当作字面 model id 传给 `prepareCall` 然后失败。在 CC 模式下,派发时解析器(来自 `ccModelRoutes` 服务,经 cc-shell trampoline 与 Task 工具消费)把 `inherit` 映射为「无 override」,child 因此继承父路由,符合 CC 语义。未挂载任何解析器时(`@jianxx/dsh-cc-plugin-loader` 中不设置 `resolveModel` 的非 cc 消费者),行为与之前字节一致——包括旧的 `inherit` 透传——因为无解析器 fallback 被原样保留。CC preset 无条件挂载 cc-shell(以及 routes 服务),因此 CC 模式下该修复始终生效。
79
+
80
+ ## API
81
+
82
+ - `apply(ctx, config?)` / `name` — **cordis 插件入口**(插件 id `cc-model-routes`,另以 `applyRoutes` / `routesPluginName` 再导出)。挂载服务;config 形状为 `{ modelAliases?: Record<string, AliasTarget> }`(部署默认)。
83
+ - `ModelRoutes` — `ctx.get('ccModelRoutes')` 的值类型(`resolve(model): { provider?, model? } | undefined`)。
84
+ - `mergeAliasMaps(config, settings)` — 带 `null` 删除与大小写不敏感 key 折叠的 entry-shallow merge;返回只含已配置 alias 的有效 `ReadonlyMap`。
85
+ - `createModelResolver(getAliases, { warn })` — 构建 `resolveModel` 闭包。`getAliases` 是**每次调用**求值的 thunk(liveness)。可选 `warn` 替换默认的 `console.warn`(用于未配置自定义 alias 告警)。
86
+ - `BUILTIN_ALIASES` — `['fable', 'opus', 'sonnet', 'haiku']`。
87
+ - `ConfigAliasesSchema` / `SettingsAliasesSchema`(及其 record 形式)— config 层(无 `null`)与 settings 层(允许 `null`)的 schemastery schema,外加 `AliasTarget` / `ResolvedRoute` 类型。
88
+
89
+ ## 非目标(在 parity matrix 中跟踪)
90
+
91
+ `/model` 交互命令、`ANTHROPIC_MODEL` / `ANTHROPIC_SMALL_FAST_MODEL` 环境变量、以及给主会话默认模型加 alias 仍是 follow-up。
package/lib/index.d.ts ADDED
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Claude Code-compatible model alias resolution for the DeepSeek Harness.
3
+ *
4
+ * Two consumption shapes:
5
+ * - **Service** (`ccModelRoutes`): the plugin entry (`apply`) owns the
6
+ * `model-aliases` settings namespace registration and provides a spawn-time
7
+ * resolver over it; consumers `ctx.get('ccModelRoutes')` lazily.
8
+ * - **Pure helpers**: `mergeAliasMaps` / `createModelResolver` for embedding
9
+ * the resolution semantics without mounting the service.
10
+ *
11
+ * See the README for configuration, merge, and fallback semantics.
12
+ *
13
+ * @module @jianxx/dsh-cc-model-aliases
14
+ */
15
+ export { BUILTIN_ALIASES, mergeAliasMaps, createModelResolver } from './resolver.ts';
16
+ export { ConfigAliasSchema, ConfigAliasesSchema, SettingsAliasSchema, SettingsAliasesSchema, type ConfigAliasEntry, type SettingsAliasEntry, } from './schema.ts';
17
+ export type { AliasTarget, ResolvedRoute } from './types.ts';
18
+ export { apply as applyRoutes, name as routesPluginName, type ModelRoutes } from './service.ts';
19
+ export { apply, name } from './service.ts';
20
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA;AACpF,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,EACrB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,GACxB,MAAM,aAAa,CAAA;AACpB,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC5D,OAAO,EAAE,KAAK,IAAI,WAAW,EAAE,IAAI,IAAI,gBAAgB,EAAE,KAAK,WAAW,EAAE,MAAM,cAAc,CAAA;AAC/F,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAA"}
package/lib/index.js ADDED
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Claude Code-compatible model alias resolution for the DeepSeek Harness.
3
+ *
4
+ * Two consumption shapes:
5
+ * - **Service** (`ccModelRoutes`): the plugin entry (`apply`) owns the
6
+ * `model-aliases` settings namespace registration and provides a spawn-time
7
+ * resolver over it; consumers `ctx.get('ccModelRoutes')` lazily.
8
+ * - **Pure helpers**: `mergeAliasMaps` / `createModelResolver` for embedding
9
+ * the resolution semantics without mounting the service.
10
+ *
11
+ * See the README for configuration, merge, and fallback semantics.
12
+ *
13
+ * @module @jianxx/dsh-cc-model-aliases
14
+ */
15
+ export { BUILTIN_ALIASES, mergeAliasMaps, createModelResolver } from "./resolver.js";
16
+ export { ConfigAliasSchema, ConfigAliasesSchema, SettingsAliasSchema, SettingsAliasesSchema, } from "./schema.js";
17
+ export { apply as applyRoutes, name as routesPluginName } from "./service.js";
18
+ export { apply, name } from "./service.js";
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA;AACpF,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,GAGtB,MAAM,aAAa,CAAA;AAEpB,OAAO,EAAE,KAAK,IAAI,WAAW,EAAE,IAAI,IAAI,gBAAgB,EAAoB,MAAM,cAAc,CAAA;AAC/F,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAA"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Claude Code model-alias merge and resolution.
3
+ *
4
+ * `mergeAliasMaps` folds the deployment `config` defaults and the live
5
+ * `settings` overlay into one effective alias map — entry-shallow, so a
6
+ * settings value replaces a same-named config value wholesale rather than
7
+ * field-merging `{provider, model}` objects, with a settings `null` deleting
8
+ * the config entry. `createModelResolver` turns a per-invocation alias source
9
+ * into the `resolveModel` closure an agent provider calls at spawn time, so
10
+ * live settings edits take effect on the next spawn without re-registering
11
+ * anything.
12
+ *
13
+ * Only the four builtin alias names have a fallback when they are unconfigured
14
+ * — they resolve to "inherit the parent route" (`undefined`), matching the
15
+ * deployment decision that a zero-config `model: sonnet` agent silently uses
16
+ * the parent's current model. A custom (open-set) alias that is unconfigured
17
+ * has no fallback: it passes through verbatim as a literal model id, with a
18
+ * warning when it looks like an intended alias.
19
+ *
20
+ * @module @jianxx/dsh-cc-model-aliases/resolver
21
+ */
22
+ import type { AliasTarget, ResolvedRoute } from './types.ts';
23
+ /**
24
+ * The builtin alias names that fall back to "inherit the parent route" when
25
+ * unconfigured. Case-insensitive; a configured value still wins over this
26
+ * fallback.
27
+ */
28
+ export declare const BUILTIN_ALIASES: readonly string[];
29
+ /**
30
+ * Merge the deployment `config` defaults and the settings overlay into one
31
+ * effective alias map (aliases keyed by lowercased name). Entry-shallow:
32
+ * a settings value replaces a config value wholesale and never field-merges a
33
+ * `{provider, model}` object; a settings `null` deletes the config entry.
34
+ * @param config - deployment config `modelAliases` (values never `null` at the
35
+ * schema level), or `undefined`.
36
+ * @param settings - live settings `model-aliases` section (values may be `null`
37
+ * to delete), or `undefined`.
38
+ * @returns a fresh map of only the configured aliases, keyed lowercased.
39
+ */
40
+ export declare function mergeAliasMaps(config: Readonly<Record<string, AliasTarget>> | undefined, settings: Readonly<Record<string, AliasTarget | null>> | undefined): ReadonlyMap<string, AliasTarget>;
41
+ /**
42
+ * Build a `resolveModel` closure for an agent provider. The alias source is a
43
+ * thunk evaluated on every invocation, so the closure reads the live settings
44
+ * and merges fresh each spawn (see the plan's liveness requirement).
45
+ * @param getAliases - returns the effective alias map for this invocation (the
46
+ * caller composes config + live settings via {@link mergeAliasMaps}).
47
+ * @param options - optional warning hook for unsigned custom aliases; the
48
+ * message is emitted (defaulting to `console.warn`) when a model that is not
49
+ * a configured alias and not a builtin looks like an intended alias (a bare
50
+ * lowercase alphabetic word) and is passed through verbatim.
51
+ * @returns the resolution function mapping a frontmatter `model` to a route, or
52
+ * `undefined` for no override.
53
+ */
54
+ export declare function createModelResolver(getAliases: () => ReadonlyMap<string, AliasTarget>, options?: {
55
+ warn?: (message: string) => void;
56
+ }): (model: string | undefined) => ResolvedRoute | undefined;
57
+ //# sourceMappingURL=resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE5D;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,SAAS,MAAM,EAAyC,CAAA;AAKtF;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,GAAG,SAAS,EACzD,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAAC,CAAC,GAAG,SAAS,GACjE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,CAalC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,MAAM,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,EAClD,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,GAC7C,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,KAAK,aAAa,GAAG,SAAS,CAyB1D"}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Claude Code model-alias merge and resolution.
3
+ *
4
+ * `mergeAliasMaps` folds the deployment `config` defaults and the live
5
+ * `settings` overlay into one effective alias map — entry-shallow, so a
6
+ * settings value replaces a same-named config value wholesale rather than
7
+ * field-merging `{provider, model}` objects, with a settings `null` deleting
8
+ * the config entry. `createModelResolver` turns a per-invocation alias source
9
+ * into the `resolveModel` closure an agent provider calls at spawn time, so
10
+ * live settings edits take effect on the next spawn without re-registering
11
+ * anything.
12
+ *
13
+ * Only the four builtin alias names have a fallback when they are unconfigured
14
+ * — they resolve to "inherit the parent route" (`undefined`), matching the
15
+ * deployment decision that a zero-config `model: sonnet` agent silently uses
16
+ * the parent's current model. A custom (open-set) alias that is unconfigured
17
+ * has no fallback: it passes through verbatim as a literal model id, with a
18
+ * warning when it looks like an intended alias.
19
+ *
20
+ * @module @jianxx/dsh-cc-model-aliases/resolver
21
+ */
22
+ /**
23
+ * The builtin alias names that fall back to "inherit the parent route" when
24
+ * unconfigured. Case-insensitive; a configured value still wins over this
25
+ * fallback.
26
+ */
27
+ export const BUILTIN_ALIASES = ['fable', 'opus', 'sonnet', 'haiku'];
28
+ /** The set of builtin names, lowercased, for O(1) membership checks. */
29
+ const BUILTIN_SET = new Set(BUILTIN_ALIASES);
30
+ /**
31
+ * Merge the deployment `config` defaults and the settings overlay into one
32
+ * effective alias map (aliases keyed by lowercased name). Entry-shallow:
33
+ * a settings value replaces a config value wholesale and never field-merges a
34
+ * `{provider, model}` object; a settings `null` deletes the config entry.
35
+ * @param config - deployment config `modelAliases` (values never `null` at the
36
+ * schema level), or `undefined`.
37
+ * @param settings - live settings `model-aliases` section (values may be `null`
38
+ * to delete), or `undefined`.
39
+ * @returns a fresh map of only the configured aliases, keyed lowercased.
40
+ */
41
+ export function mergeAliasMaps(config, settings) {
42
+ const merged = new Map();
43
+ for (const [key, value] of foldKeys(config)) {
44
+ // Config never carries `null` at the schema level, but schemastery dicts are
45
+ // lenient about stored values; silently treat a stray config `null` as
46
+ // absent so it can never reach the resolver as a route.
47
+ if (value !== null)
48
+ merged.set(key, value);
49
+ }
50
+ for (const [key, value] of foldKeys(settings)) {
51
+ if (value === null)
52
+ merged.delete(key);
53
+ else
54
+ merged.set(key, value);
55
+ }
56
+ return merged;
57
+ }
58
+ /**
59
+ * Build a `resolveModel` closure for an agent provider. The alias source is a
60
+ * thunk evaluated on every invocation, so the closure reads the live settings
61
+ * and merges fresh each spawn (see the plan's liveness requirement).
62
+ * @param getAliases - returns the effective alias map for this invocation (the
63
+ * caller composes config + live settings via {@link mergeAliasMaps}).
64
+ * @param options - optional warning hook for unsigned custom aliases; the
65
+ * message is emitted (defaulting to `console.warn`) when a model that is not
66
+ * a configured alias and not a builtin looks like an intended alias (a bare
67
+ * lowercase alphabetic word) and is passed through verbatim.
68
+ * @returns the resolution function mapping a frontmatter `model` to a route, or
69
+ * `undefined` for no override.
70
+ */
71
+ export function createModelResolver(getAliases, options) {
72
+ const warn = options?.warn ?? ((message) => console.warn(message));
73
+ return (model) => {
74
+ if (model === undefined || model.trim().length === 0)
75
+ return undefined;
76
+ const trimmed = model.trim();
77
+ const folded = trimmed.toLowerCase();
78
+ if (folded === 'inherit')
79
+ return undefined;
80
+ const aliases = getAliases();
81
+ const hit = aliases.get(folded);
82
+ if (hit !== undefined && hit !== null) {
83
+ return typeof hit === 'string' ? { model: hit } : { provider: hit.provider, model: hit.model };
84
+ }
85
+ // Unconfigured builtin alias → inherit the parent route ("current model").
86
+ if (BUILTIN_SET.has(folded))
87
+ return undefined;
88
+ // Custom alias that is unconfigured: warn when it looks like an intended
89
+ // alias, then pass through verbatim as a literal model id (no regression
90
+ // for literal ids such as `deepseek-chat`).
91
+ if (/^[a-z]+$/.test(folded)) {
92
+ warn(`cc-model-aliases: model "${trimmed}" is not a configured alias and is not builtin; passing through verbatim as a literal model id`);
93
+ }
94
+ return { model: trimmed };
95
+ };
96
+ }
97
+ /** Compact-fill a record's own string keys, folding each to lowercase. */
98
+ function foldKeys(record) {
99
+ const out = new Map();
100
+ if (record === undefined)
101
+ return out;
102
+ for (const [key, value] of Object.entries(record)) {
103
+ out.set(key.toLowerCase(), value);
104
+ }
105
+ return out;
106
+ }
107
+ //# sourceMappingURL=resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolver.js","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAA;AAEtF,wEAAwE;AACxE,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,eAAe,CAAC,CAAA;AAE5C;;;;;;;;;;GAUG;AACH,MAAM,UAAU,cAAc,CAC5B,MAAyD,EACzD,QAAkE;IAElE,MAAM,MAAM,GAAG,IAAI,GAAG,EAAuB,CAAA;IAC7C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAC5C,6EAA6E;QAC7E,uEAAuE;QACvE,wDAAwD;QACxD,IAAI,KAAK,KAAK,IAAI;YAAE,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;IAC5C,CAAC;IACD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC9C,IAAI,KAAK,KAAK,IAAI;YAAE,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;;YACjC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;IAC7B,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,mBAAmB,CACjC,UAAkD,EAClD,OAA8C;IAE9C,MAAM,IAAI,GAAG,OAAO,EAAE,IAAI,IAAI,CAAC,CAAC,OAAe,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAA;IAC1E,OAAO,CAAC,KAAK,EAAE,EAAE;QACf,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,SAAS,CAAA;QACtE,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAA;QAC5B,MAAM,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAA;QACpC,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,SAAS,CAAA;QAE1C,MAAM,OAAO,GAAG,UAAU,EAAE,CAAA;QAC5B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;QAC/B,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YACtC,OAAO,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,CAAA;QAChG,CAAC;QAED,2EAA2E;QAC3E,IAAI,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC;YAAE,OAAO,SAAS,CAAA;QAE7C,yEAAyE;QACzE,yEAAyE;QACzE,4CAA4C;QAC5C,IAAI,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;YAC5B,IAAI,CAAC,4BAA4B,OAAO,gGAAgG,CAAC,CAAA;QAC3I,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAA;IAC3B,CAAC,CAAA;AACH,CAAC;AAED,0EAA0E;AAC1E,SAAS,QAAQ,CAAC,MAAgE;IAChF,MAAM,GAAG,GAAG,IAAI,GAAG,EAA8B,CAAA;IACjD,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,GAAG,CAAA;IACpC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAClD,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,EAAE,KAAK,CAAC,CAAA;IACnC,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Configuration schemas for the CC model-alias layer.
3
+ *
4
+ * Two schemas exist because the two configuration layers differ in what they
5
+ * may express. The deployment `config` layer (cc-shell `modelAliases`) cannot
6
+ * delete an alias — values are a model id or an explicit `{provider, model}`
7
+ * route. The settings overlay (`model-aliases` namespace) may additionally be
8
+ * `null` for a key, which deletes a same-named config-default entry during
9
+ * merge. Object-form targets require a non-empty `provider` and `model` so a
10
+ * half-written route is rejected at schema-validation time rather than
11
+ * surfacing as an empty-field override at spawn.
12
+ *
13
+ * The settings overlay is a shallow record keyed by alias name; the 5-cascade
14
+ * layer merge inside settings is recursively deep (existing cascade
15
+ * behaviour), so an object-form alias must be written whole or not at all
16
+ * across layers to avoid `{provider, model}` field blending — see the package
17
+ * README.
18
+ *
19
+ * @module @jianxx/dsh-cc-model-aliases/schema
20
+ */
21
+ import z from '@deepseek-ai/schemastery';
22
+ import type { AliasTarget } from './types.ts';
23
+ /**
24
+ * Schema for a deployment `config` alias value: a model id or an explicit
25
+ * route, but never `null` (config cannot delete an alias; only the settings
26
+ * overlay can).
27
+ */
28
+ export declare const ConfigAliasSchema: z<string | ({
29
+ provider?: string | null;
30
+ model?: string | null;
31
+ } & import("@deepseek-ai/cosmokit").Dict), string | Schemastery.ObjectT<{
32
+ provider: z<string, string>;
33
+ model: z<string, string>;
34
+ }>>;
35
+ /**
36
+ * Schema for a settings `model-aliases` value: a model id, an explicit route,
37
+ * or `null` meaning "delete a same-named config-default entry".
38
+ */
39
+ export declare const SettingsAliasSchema: z<string | ({
40
+ provider?: string | null;
41
+ model?: string | null;
42
+ } & import("@deepseek-ai/cosmokit").Dict) | null, string | Schemastery.ObjectT<{
43
+ provider: z<string, string>;
44
+ model: z<string, string>;
45
+ }> | null>;
46
+ /**
47
+ * Schema for the whole config `modelAliases` record.
48
+ */
49
+ export declare const ConfigAliasesSchema: z<import("@deepseek-ai/cosmokit").Dict<string | ({
50
+ provider?: string | null;
51
+ model?: string | null;
52
+ } & import("@deepseek-ai/cosmokit").Dict), string>, import("@deepseek-ai/cosmokit").Dict<string | Schemastery.ObjectT<{
53
+ provider: z<string, string>;
54
+ model: z<string, string>;
55
+ }>, string>>;
56
+ /**
57
+ * Schema for the whole settings `model-aliases` section.
58
+ */
59
+ export declare const SettingsAliasesSchema: z<import("@deepseek-ai/cosmokit").Dict<string | ({
60
+ provider?: string | null;
61
+ model?: string | null;
62
+ } & import("@deepseek-ai/cosmokit").Dict) | null, string>, import("@deepseek-ai/cosmokit").Dict<string | Schemastery.ObjectT<{
63
+ provider: z<string, string>;
64
+ model: z<string, string>;
65
+ }> | null, string>>;
66
+ /** The validated shape of one config entry (modelled on the schema). */
67
+ export type ConfigAliasEntry = Exclude<AliasTarget, null>;
68
+ /** The validated shape of one settings entry (may be `null` to delete). */
69
+ export type SettingsAliasEntry = AliasTarget;
70
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,CAAC,MAAM,0BAA0B,CAAA;AACxC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAW7C;;;;GAIG;AACH,eAAO,MAAM,iBAAiB;;;;;;GAAwC,CAAA;AAEtE;;;GAGG;AACH,eAAO,MAAM,mBAAmB;;;;;;UAAuD,CAAA;AAEvF;;GAEG;AACH,eAAO,MAAM,mBAAmB;;;;;;YAA4B,CAAA;AAE5D;;GAEG;AACH,eAAO,MAAM,qBAAqB;;;;;;mBAA8B,CAAA;AAEhE,wEAAwE;AACxE,MAAM,MAAM,gBAAgB,GAAG,OAAO,CAAC,WAAW,EAAE,IAAI,CAAC,CAAA;AACzD,2EAA2E;AAC3E,MAAM,MAAM,kBAAkB,GAAG,WAAW,CAAA"}
package/lib/schema.js ADDED
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Configuration schemas for the CC model-alias layer.
3
+ *
4
+ * Two schemas exist because the two configuration layers differ in what they
5
+ * may express. The deployment `config` layer (cc-shell `modelAliases`) cannot
6
+ * delete an alias — values are a model id or an explicit `{provider, model}`
7
+ * route. The settings overlay (`model-aliases` namespace) may additionally be
8
+ * `null` for a key, which deletes a same-named config-default entry during
9
+ * merge. Object-form targets require a non-empty `provider` and `model` so a
10
+ * half-written route is rejected at schema-validation time rather than
11
+ * surfacing as an empty-field override at spawn.
12
+ *
13
+ * The settings overlay is a shallow record keyed by alias name; the 5-cascade
14
+ * layer merge inside settings is recursively deep (existing cascade
15
+ * behaviour), so an object-form alias must be written whole or not at all
16
+ * across layers to avoid `{provider, model}` field blending — see the package
17
+ * README.
18
+ *
19
+ * @module @jianxx/dsh-cc-model-aliases/schema
20
+ */
21
+ import z from '@deepseek-ai/schemastery';
22
+ /** A target that only names a model id (provider inherits the parent route). */
23
+ const MODEL_ONLY = z.string().min(1);
24
+ /** A target that pins an explicit `{provider, model}` route. */
25
+ const EXPLICIT_ROUTE = z.object({
26
+ provider: z.string().min(1),
27
+ model: z.string().min(1),
28
+ });
29
+ /**
30
+ * Schema for a deployment `config` alias value: a model id or an explicit
31
+ * route, but never `null` (config cannot delete an alias; only the settings
32
+ * overlay can).
33
+ */
34
+ export const ConfigAliasSchema = z.union([MODEL_ONLY, EXPLICIT_ROUTE]);
35
+ /**
36
+ * Schema for a settings `model-aliases` value: a model id, an explicit route,
37
+ * or `null` meaning "delete a same-named config-default entry".
38
+ */
39
+ export const SettingsAliasSchema = z.union([MODEL_ONLY, EXPLICIT_ROUTE, z.const(null)]);
40
+ /**
41
+ * Schema for the whole config `modelAliases` record.
42
+ */
43
+ export const ConfigAliasesSchema = z.dict(ConfigAliasSchema);
44
+ /**
45
+ * Schema for the whole settings `model-aliases` section.
46
+ */
47
+ export const SettingsAliasesSchema = z.dict(SettingsAliasSchema);
48
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,CAAC,MAAM,0BAA0B,CAAA;AAGxC,gFAAgF;AAChF,MAAM,UAAU,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;AAEpC,gEAAgE;AAChE,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9B,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CACzB,CAAC,CAAA;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC,CAAA;AAEtE;;;GAGG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,UAAU,EAAE,cAAc,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AAEvF;;GAEG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAA;AAE5D;;GAEG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAA"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * The `ccModelRoutes` host service: the single owner of the `model-aliases`
3
+ * settings namespace registration.
4
+ *
5
+ * CC frontmatter names models by alias (`model: opus`); this package maps an
6
+ * alias to a dsh `{provider, model}` route. Consumers (the cc-shell glue's
7
+ * `AgentProvider` and the Task tool) `ctx.get('ccModelRoutes')` lazily on
8
+ * every spawn — the resolver re-reads the live settings scope each call, so a
9
+ * settings write applies to the next spawn without re-registration.
10
+ *
11
+ * When no settings provider is mounted the resolver degrades to the config
12
+ * defaults plus the builtin fallback (an unconfigured builtin alias inherits
13
+ * the parent route), so a settings-less host still resolves.
14
+ *
15
+ * @module @jianxx/dsh-cc-model-aliases/service
16
+ */
17
+ import z from '@deepseek-ai/schemastery';
18
+ import type { Context } from '@deepseek-ai/cordis';
19
+ import type { AliasTarget, ResolvedRoute } from './types.ts';
20
+ /** Plugin configuration: deployment-default alias map. */
21
+ export interface Config {
22
+ /** Deployment defaults (alias name → model id or `{provider, model}` route). */
23
+ modelAliases?: Record<string, Exclude<AliasTarget, null>>;
24
+ }
25
+ /** Runtime config schema (config-layer aliases never hold null). */
26
+ export declare const Config: z<Config>;
27
+ /** The shape consumers resolve through. */
28
+ export interface ModelRoutes {
29
+ /** Resolve one frontmatter `model` to a dsh route, or undefined to inherit. */
30
+ resolve(model: string | undefined): ResolvedRoute | undefined;
31
+ }
32
+ /** Cordis plugin id. */
33
+ export declare const name = "cc-model-routes";
34
+ /**
35
+ * Register the `model-aliases` settings namespace (when a settings provider is
36
+ * mounted) and expose the spawn-time resolver as the `ccModelRoutes` value.
37
+ * @param ctx - the plug context.
38
+ * @param config - deployment defaults.
39
+ */
40
+ export declare function apply(ctx: Context, config?: Config): void;
41
+ //# sourceMappingURL=service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,CAAC,MAAM,0BAA0B,CAAA;AACxC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAKlD,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE5D,0DAA0D;AAC1D,MAAM,WAAW,MAAM;IACrB,gFAAgF;IAChF,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,CAAA;CAC1D;AAED,oEAAoE;AACpE,eAAO,MAAM,MAAM,EAAE,CAAC,CAAC,MAAM,CAAmD,CAAA;AAKhF,2CAA2C;AAC3C,MAAM,WAAW,WAAW;IAC1B,+EAA+E;IAC/E,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,aAAa,GAAG,SAAS,CAAA;CAC9D;AAED,wBAAwB;AACxB,eAAO,MAAM,IAAI,oBAAoB,CAAA;AAErC;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,GAAE,MAAW,GAAG,IAAI,CAuB7D"}
package/lib/service.js ADDED
@@ -0,0 +1,51 @@
1
+ /**
2
+ * The `ccModelRoutes` host service: the single owner of the `model-aliases`
3
+ * settings namespace registration.
4
+ *
5
+ * CC frontmatter names models by alias (`model: opus`); this package maps an
6
+ * alias to a dsh `{provider, model}` route. Consumers (the cc-shell glue's
7
+ * `AgentProvider` and the Task tool) `ctx.get('ccModelRoutes')` lazily on
8
+ * every spawn — the resolver re-reads the live settings scope each call, so a
9
+ * settings write applies to the next spawn without re-registration.
10
+ *
11
+ * When no settings provider is mounted the resolver degrades to the config
12
+ * defaults plus the builtin fallback (an unconfigured builtin alias inherits
13
+ * the parent route), so a settings-less host still resolves.
14
+ *
15
+ * @module @jianxx/dsh-cc-model-aliases/service
16
+ */
17
+ import z from '@deepseek-ai/schemastery';
18
+ import { settingsNamespace } from '@deepseek-ai/dsh-settings';
19
+ import { ConfigAliasesSchema, SettingsAliasesSchema } from "./schema.js";
20
+ import { createModelResolver, mergeAliasMaps } from "./resolver.js";
21
+ /** Runtime config schema (config-layer aliases never hold null). */
22
+ export const Config = z.object({ modelAliases: ConfigAliasesSchema });
23
+ /** The settings namespace carrying the live `model-aliases` overlay. */
24
+ const MODEL_ALIASES_NAMESPACE = settingsNamespace('model-aliases');
25
+ /** Cordis plugin id. */
26
+ export const name = 'cc-model-routes';
27
+ /**
28
+ * Register the `model-aliases` settings namespace (when a settings provider is
29
+ * mounted) and expose the spawn-time resolver as the `ccModelRoutes` value.
30
+ * @param ctx - the plug context.
31
+ * @param config - deployment defaults.
32
+ */
33
+ export function apply(ctx, config = {}) {
34
+ const settings = ctx.get('settings');
35
+ const scope = settings?.register(MODEL_ALIASES_NAMESPACE, SettingsAliasesSchema, {
36
+ // Reject a half-written object route at write time (the dict schema cannot
37
+ // express a non-empty cross-field check).
38
+ validate: (value) => {
39
+ for (const [alias, target] of Object.entries(value)) {
40
+ if (target !== null && typeof target === 'object') {
41
+ if (target.provider.trim().length === 0 || target.model.trim().length === 0) {
42
+ throw new Error(`cc-model-aliases: model alias "${alias}" must specify a non-empty provider and model`);
43
+ }
44
+ }
45
+ }
46
+ },
47
+ });
48
+ const resolve = createModelResolver(() => mergeAliasMaps(config.modelAliases, scope?.get?.()), { warn: message => ctx.logger.warn(message) });
49
+ ctx.provide('ccModelRoutes', { resolve });
50
+ }
51
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,CAAC,MAAM,0BAA0B,CAAA;AAGxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AAC7D,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AACxE,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AASnE,oEAAoE;AACpE,MAAM,CAAC,MAAM,MAAM,GAAc,CAAC,CAAC,MAAM,CAAC,EAAE,YAAY,EAAE,mBAAmB,EAAE,CAAC,CAAA;AAEhF,wEAAwE;AACxE,MAAM,uBAAuB,GAAG,iBAAiB,CAAC,eAAe,CAAC,CAAA;AAQlE,wBAAwB;AACxB,MAAM,CAAC,MAAM,IAAI,GAAG,iBAAiB,CAAA;AAErC;;;;;GAKG;AACH,MAAM,UAAU,KAAK,CAAC,GAAY,EAAE,SAAiB,EAAE;IACrD,MAAM,QAAQ,GAAG,GAAG,CAAC,GAAG,CAAC,UAAU,CAAiC,CAAA;IACpE,MAAM,KAAK,GAAG,QAAQ,EAAE,QAAQ,CAAC,uBAAuB,EAAE,qBAAqB,EAAE;QAC/E,2EAA2E;QAC3E,0CAA0C;QAC1C,QAAQ,EAAE,CAAC,KAAyC,EAAE,EAAE;YACtD,KAAK,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;gBACpD,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;oBAClD,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;wBAC5E,MAAM,IAAI,KAAK,CAAC,kCAAkC,KAAK,+CAA+C,CAAC,CAAA;oBACzG,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;KACF,CAAC,CAAA;IACF,MAAM,OAAO,GAAG,mBAAmB,CACjC,GAAG,EAAE,CAAC,cAAc,CAClB,MAAM,CAAC,YAAY,EACnB,KAAK,EAAE,GAAG,EAAE,EAAoD,CACjE,EACD,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAC9C,CAAA;IACD,GAAG,CAAC,OAAO,CAAC,eAAe,EAAE,EAAE,OAAO,EAAE,CAAC,CAAA;AAC3C,CAAC"}
package/lib/types.d.ts ADDED
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Shared vocabulary for Claude Code-compatible model alias resolution.
3
+ *
4
+ * Claude Code frontmatter (`model: opus`, `model: sonnet`) names aliases rather
5
+ * than literal provider model IDs. This package layers those aliases onto the
6
+ * harness: it maps an alias to a dsh `{provider, model}` route, or leaves the
7
+ * frontmatter untouched so the child inherits its parent's route. The types
8
+ * here are the pure record shapes the resolution functions consume and emit;
9
+ * the merge and lookup semantics live in `resolver.ts` and the configuration
10
+ * schemas in `schema.ts`.
11
+ *
12
+ * @module @jianxx/dsh-cc-model-aliases/types
13
+ */
14
+ /**
15
+ * A resolved dsh model route for one spawn.
16
+ *
17
+ * When `start()` passes this on as `agentOptions`, only the fields that are
18
+ * present (`provider` and/or `model`) override the delegation; a route omitting
19
+ * `provider` inherits the parent's provider, and one omitting `model` inherits
20
+ * the parent's model. `undefined` means "no override" — the child inherits the
21
+ * parent route wholesale (the `inherit` sentinel and the builtin fallback both
22
+ * resolve to this).
23
+ */
24
+ export interface ResolvedRoute {
25
+ /** The provider to route to; omit to inherit the parent's provider. */
26
+ readonly provider?: string | undefined;
27
+ /** The model id (or resolved alias model) to use; omit to inherit. */
28
+ readonly model?: string | undefined;
29
+ }
30
+ /**
31
+ * One alias target as authored in config or settings.
32
+ *
33
+ * A string form names only a model id (`sonnet: deepseek-chat`); the provider
34
+ * inherits the parent route. An object form additionally pins a provider
35
+ * (`opus: { provider: ..., model: ... }`), breaking the current frontmatter
36
+ * limitation of never being able to override the provider. In the settings
37
+ * layer a configured entry may instead be `null`, which deletes a same-named
38
+ * config-default entry (see {@link mergeAliasMaps}).
39
+ */
40
+ export type AliasTarget = string | {
41
+ readonly provider: string;
42
+ readonly model: string;
43
+ } | null;
44
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH;;;;;;;;;GASG;AACH,MAAM,WAAW,aAAa;IAC5B,uEAAuE;IACvE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACtC,sEAAsE;IACtE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACpC;AAED;;;;;;;;;GASG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAA"}
package/lib/types.js ADDED
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Shared vocabulary for Claude Code-compatible model alias resolution.
3
+ *
4
+ * Claude Code frontmatter (`model: opus`, `model: sonnet`) names aliases rather
5
+ * than literal provider model IDs. This package layers those aliases onto the
6
+ * harness: it maps an alias to a dsh `{provider, model}` route, or leaves the
7
+ * frontmatter untouched so the child inherits its parent's route. The types
8
+ * here are the pure record shapes the resolution functions consume and emit;
9
+ * the merge and lookup semantics live in `resolver.ts` and the configuration
10
+ * schemas in `schema.ts`.
11
+ *
12
+ * @module @jianxx/dsh-cc-model-aliases/types
13
+ */
14
+ export {};
15
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG"}
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@jianxx/dsh-cc-model-aliases",
3
+ "description": "Claude Code-compatible model alias resolution for the DeepSeek Harness",
4
+ "version": "0.1.0",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/jianxx/dsh-cc-plugins.git",
8
+ "directory": "packages/compat/cc-model-aliases"
9
+ },
10
+ "type": "module",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./lib/index.d.ts",
14
+ "default": "./lib/index.js"
15
+ },
16
+ "./types": {
17
+ "types": "./lib/types.d.ts",
18
+ "default": "./lib/types.js"
19
+ },
20
+ "./schema": {
21
+ "types": "./lib/schema.d.ts",
22
+ "default": "./lib/schema.js"
23
+ },
24
+ "./resolver": {
25
+ "types": "./lib/resolver.d.ts",
26
+ "default": "./lib/resolver.js"
27
+ },
28
+ "./src/*": "./src/*",
29
+ "./package.json": "./package.json"
30
+ },
31
+ "files": [
32
+ "lib"
33
+ ],
34
+ "license": "Apache-2.0",
35
+ "peerDependencies": {
36
+ "@deepseek-ai/cordis": ">=0.1.1-rc.2",
37
+ "@deepseek-ai/dsh-settings": ">=0.1.1-rc.2",
38
+ "@deepseek-ai/schemastery": "^3.18.1"
39
+ },
40
+ "devDependencies": {
41
+ "@deepseek-ai/schemastery": "link:../../../../deepseek-harness/vendor/schemastery",
42
+ "@deepseek-ai/cordis": "link:../../../../deepseek-harness/vendor/cordis",
43
+ "@deepseek-ai/dsh-settings": "link:../../../../deepseek-harness/packages/settings/settings"
44
+ },
45
+ "publishConfig": {
46
+ "access": "public"
47
+ }
48
+ }