@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 +201 -0
- package/README.i18n.yaml +6 -0
- package/README.md +157 -0
- package/README.zh.md +91 -0
- package/lib/index.d.ts +20 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +19 -0
- package/lib/index.js.map +1 -0
- package/lib/resolver.d.ts +57 -0
- package/lib/resolver.d.ts.map +1 -0
- package/lib/resolver.js +107 -0
- package/lib/resolver.js.map +1 -0
- package/lib/schema.d.ts +70 -0
- package/lib/schema.d.ts.map +1 -0
- package/lib/schema.js +48 -0
- package/lib/schema.js.map +1 -0
- package/lib/service.d.ts +41 -0
- package/lib/service.d.ts.map +1 -0
- package/lib/service.js +51 -0
- package/lib/service.js.map +1 -0
- package/lib/types.d.ts +44 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +15 -0
- package/lib/types.js.map +1 -0
- package/package.json +48 -0
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.
|
package/README.i18n.yaml
ADDED
|
@@ -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
|
package/lib/index.js.map
ADDED
|
@@ -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"}
|
package/lib/resolver.js
ADDED
|
@@ -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"}
|
package/lib/schema.d.ts
ADDED
|
@@ -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"}
|
package/lib/service.d.ts
ADDED
|
@@ -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
|
package/lib/types.js.map
ADDED
|
@@ -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
|
+
}
|