@klhapp/skillmux 1.6.0 → 1.7.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/CHANGELOG.md +14 -0
- package/README.md +7 -7
- package/config.example.toml +5 -0
- package/config.remote.example.toml +4 -7
- package/docs/README.md +4 -4
- package/docs/assets/architecture.svg +1 -1
- package/docs/cli.md +4 -57
- package/docs/concepts.md +12 -14
- package/docs/configuration.md +10 -11
- package/docs/deployment.md +11 -8
- package/docs/getting-started.md +1 -1
- package/docs/mcp-routing.md +28 -23
- package/docs/ranked-shortlist-migration.md +160 -0
- package/docs/schema.json +37 -139
- package/docs/skill-management.md +7 -2
- package/docs/troubleshooting.md +12 -14
- package/package.json +1 -1
- package/src/adapters.ts +1 -536
- package/src/audit.ts +1 -3
- package/src/cli.ts +19 -254
- package/src/completions.ts +0 -4
- package/src/config-service.ts +19 -32
- package/src/config-watcher.ts +2 -6
- package/src/config.ts +61 -60
- package/src/db.ts +32 -18
- package/src/doctor.ts +1 -84
- package/src/eval.ts +143 -60
- package/src/init.ts +4 -5
- package/src/metrics.ts +1 -13
- package/src/router-core.ts +42 -149
- package/src/server.ts +13 -27
- package/src/stats.ts +126 -57
- package/src/types.ts +8 -39
- package/docs/calibration.md +0 -198
- package/src/calibrate.ts +0 -1775
- package/src/config-mutation.ts +0 -65
- package/src/dataset-generator.ts +0 -119
- package/src/decision.ts +0 -45
package/docs/schema.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://klh.app/schemas/skillmux/router-core.json",
|
|
4
4
|
"title": "Skillmux — router-core contract",
|
|
5
|
-
"description": "Typed contract for the skillmux MCP server: tool inputs/outputs
|
|
5
|
+
"description": "Typed contract for the skillmux MCP server: tool inputs/outputs and config.toml. The SKILL.md body must appear exactly once on the wire.",
|
|
6
6
|
"$defs": {
|
|
7
7
|
"SkillId": {
|
|
8
8
|
"type": "string",
|
|
@@ -33,15 +33,26 @@
|
|
|
33
33
|
"reranker_protocol_error"
|
|
34
34
|
]
|
|
35
35
|
},
|
|
36
|
-
"
|
|
36
|
+
"RankedCandidate": {
|
|
37
37
|
"type": "object",
|
|
38
|
-
"description": "One
|
|
38
|
+
"description": "One ranked candidate in the resolution shortlist.",
|
|
39
39
|
"properties": {
|
|
40
|
+
"rank": {
|
|
41
|
+
"type": "integer",
|
|
42
|
+
"minimum": 1,
|
|
43
|
+
"description": "One-based position in the returned ranked list."
|
|
44
|
+
},
|
|
40
45
|
"skill_id": { "$ref": "#/$defs/SkillId" },
|
|
41
|
-
"
|
|
42
|
-
|
|
46
|
+
"description": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"description": "Compact skill description used by the caller to choose."
|
|
49
|
+
},
|
|
50
|
+
"score": {
|
|
51
|
+
"type": ["number", "null"],
|
|
52
|
+
"description": "Final ranking score (null when score values are unavailable for the retrieval lane)."
|
|
53
|
+
}
|
|
43
54
|
},
|
|
44
|
-
"required": ["
|
|
55
|
+
"required": ["rank", "skill_id", "description", "score"],
|
|
45
56
|
"additionalProperties": false
|
|
46
57
|
},
|
|
47
58
|
"ResolveSkillInput": {
|
|
@@ -51,89 +62,32 @@
|
|
|
51
62
|
"type": "string",
|
|
52
63
|
"description": "Natural-language task description to route. Normalized (NFC) by the server; never semantically rewritten.",
|
|
53
64
|
"minLength": 1
|
|
54
|
-
}
|
|
55
|
-
},
|
|
56
|
-
"required": ["query"],
|
|
57
|
-
"additionalProperties": false
|
|
58
|
-
},
|
|
59
|
-
"MatchedResult": {
|
|
60
|
-
"type": "object",
|
|
61
|
-
"description": "One reranked candidate clearly dominates. Full SKILL.md is delivered inline.",
|
|
62
|
-
"properties": {
|
|
63
|
-
"outcome": { "const": "matched" },
|
|
64
|
-
"retrieval": { "enum": ["exact", "reranked"] },
|
|
65
|
-
"degraded_from": { "enum": ["reranked", "hybrid"] },
|
|
66
|
-
"degradation_reason": { "$ref": "#/$defs/DegradationReason" },
|
|
67
|
-
"skill_id": { "$ref": "#/$defs/SkillId" },
|
|
68
|
-
"title": { "type": "string", "minLength": 1, "maxLength": 512 },
|
|
69
|
-
"content_sha256": { "$ref": "#/$defs/Sha256Hex" },
|
|
70
|
-
"score": {
|
|
71
|
-
"type": "number",
|
|
72
|
-
"description": "Top configured-reranker score. Satisfies score >= config thresholds.match_score."
|
|
73
|
-
},
|
|
74
|
-
"margin": {
|
|
75
|
-
"type": "number",
|
|
76
|
-
"description": "Top minus second configured-reranker score. Equals score when there is exactly one candidate.",
|
|
77
|
-
"minimum": 0
|
|
78
|
-
},
|
|
79
|
-
"body": {
|
|
80
|
-
"type": "string",
|
|
81
|
-
"description": "Verbatim UTF-8 content of SKILL.md. sha256(body bytes) == content_sha256 == hash of the on-disk file read at delivery time.",
|
|
82
|
-
"minLength": 1
|
|
83
65
|
},
|
|
84
|
-
"
|
|
85
|
-
"type": "
|
|
86
|
-
"
|
|
87
|
-
"
|
|
88
|
-
"uniqueItems": true
|
|
66
|
+
"top_k": {
|
|
67
|
+
"type": "integer",
|
|
68
|
+
"minimum": 1,
|
|
69
|
+
"description": "Optional maximum number of ranked candidates to return, bounded by server output.max_top_k."
|
|
89
70
|
}
|
|
90
71
|
},
|
|
91
|
-
"required": ["
|
|
72
|
+
"required": ["query"],
|
|
92
73
|
"additionalProperties": false
|
|
93
74
|
},
|
|
94
|
-
"
|
|
75
|
+
"ResolveSkillResult": {
|
|
95
76
|
"type": "object",
|
|
96
|
-
"description": "
|
|
77
|
+
"description": "Ranked candidates response from resolve_skill.",
|
|
97
78
|
"properties": {
|
|
98
|
-
"outcome": { "const": "ambiguous" },
|
|
99
79
|
"retrieval": { "enum": ["exact", "reranked", "hybrid", "lexical"] },
|
|
100
80
|
"degraded_from": { "enum": ["reranked", "hybrid"] },
|
|
101
81
|
"degradation_reason": { "$ref": "#/$defs/DegradationReason" },
|
|
102
82
|
"candidates": {
|
|
103
83
|
"type": "array",
|
|
104
|
-
"items": { "$ref": "#/$defs/
|
|
105
|
-
"
|
|
106
|
-
"maxItems": 5
|
|
84
|
+
"items": { "$ref": "#/$defs/RankedCandidate" },
|
|
85
|
+
"description": "Shortlist of candidates in descending score order with contiguous 1-based ranks."
|
|
107
86
|
}
|
|
108
87
|
},
|
|
109
|
-
"required": ["
|
|
88
|
+
"required": ["retrieval", "candidates"],
|
|
110
89
|
"additionalProperties": false
|
|
111
90
|
},
|
|
112
|
-
"NoMatchResult": {
|
|
113
|
-
"type": "object",
|
|
114
|
-
"description": "AC4: no skill passed the relevance floor. Caller proceeds under its normal workflow; must not load an unrelated skill.",
|
|
115
|
-
"properties": {
|
|
116
|
-
"outcome": { "const": "no_match" },
|
|
117
|
-
"retrieval": { "enum": ["exact", "reranked", "hybrid", "lexical"] },
|
|
118
|
-
"degraded_from": { "enum": ["reranked", "hybrid"] },
|
|
119
|
-
"degradation_reason": { "$ref": "#/$defs/DegradationReason" },
|
|
120
|
-
"message": {
|
|
121
|
-
"type": "string",
|
|
122
|
-
"description": "Fixed guidance string telling the caller to proceed normally.",
|
|
123
|
-
"minLength": 1
|
|
124
|
-
}
|
|
125
|
-
},
|
|
126
|
-
"required": ["outcome", "retrieval", "message"],
|
|
127
|
-
"additionalProperties": false
|
|
128
|
-
},
|
|
129
|
-
"ResolveSkillResult": {
|
|
130
|
-
"description": "AC1: exactly three outcomes, discriminated by 'outcome'.",
|
|
131
|
-
"oneOf": [
|
|
132
|
-
{ "$ref": "#/$defs/MatchedResult" },
|
|
133
|
-
{ "$ref": "#/$defs/AmbiguousResult" },
|
|
134
|
-
{ "$ref": "#/$defs/NoMatchResult" }
|
|
135
|
-
]
|
|
136
|
-
},
|
|
137
91
|
"FetchSkillInput": {
|
|
138
92
|
"type": "object",
|
|
139
93
|
"properties": {
|
|
@@ -144,7 +98,7 @@
|
|
|
144
98
|
},
|
|
145
99
|
"FetchSkillResult": {
|
|
146
100
|
"type": "object",
|
|
147
|
-
"description": "AC5: verbatim delivery independent of any prior
|
|
101
|
+
"description": "AC5: verbatim delivery independent of any prior resolve_skill call. Unknown skill_id raises ToolError with code SKILL_NOT_FOUND instead of returning this shape.",
|
|
148
102
|
"properties": {
|
|
149
103
|
"skill_id": { "$ref": "#/$defs/SkillId" },
|
|
150
104
|
"title": { "type": "string", "minLength": 1, "maxLength": 512 },
|
|
@@ -208,34 +162,26 @@
|
|
|
208
162
|
"output": {
|
|
209
163
|
"type": "object",
|
|
210
164
|
"properties": {
|
|
211
|
-
"
|
|
165
|
+
"top_k": {
|
|
212
166
|
"type": "integer",
|
|
213
167
|
"minimum": 1,
|
|
214
|
-
"
|
|
215
|
-
"
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
},
|
|
219
|
-
"additionalProperties": false
|
|
220
|
-
},
|
|
221
|
-
"thresholds": {
|
|
222
|
-
"type": "object",
|
|
223
|
-
"description": "Caller-facing shortlist policy (deprecated in favor of output.ambiguous_candidate_limit). Reranker thresholds live under inference.thresholds.",
|
|
224
|
-
"properties": {
|
|
225
|
-
"candidate_limit": {
|
|
168
|
+
"default": 10,
|
|
169
|
+
"description": "Default maximum number of candidates returned by resolve_skill."
|
|
170
|
+
},
|
|
171
|
+
"max_top_k": {
|
|
226
172
|
"type": "integer",
|
|
227
173
|
"minimum": 1,
|
|
228
|
-
"
|
|
229
|
-
"
|
|
230
|
-
"description": "Deprecated: use output.ambiguous_candidate_limit instead."
|
|
174
|
+
"default": 50,
|
|
175
|
+
"description": "Upper bound for per-request top_k override."
|
|
231
176
|
}
|
|
232
177
|
},
|
|
178
|
+
"required": ["top_k", "max_top_k"],
|
|
233
179
|
"additionalProperties": false
|
|
234
180
|
},
|
|
235
181
|
"inference": { "$ref": "#/$defs/InferenceConfig" },
|
|
236
182
|
"server": { "$ref": "#/$defs/ServerConfig" }
|
|
237
183
|
},
|
|
238
|
-
"required": ["vault_path", "state_dir", "recall", "inference"],
|
|
184
|
+
"required": ["vault_path", "state_dir", "recall", "output", "inference"],
|
|
239
185
|
"additionalProperties": false
|
|
240
186
|
},
|
|
241
187
|
"InferenceConfig": {
|
|
@@ -296,17 +242,6 @@
|
|
|
296
242
|
},
|
|
297
243
|
"required": ["adapter", "endpoint", "model"],
|
|
298
244
|
"additionalProperties": false
|
|
299
|
-
},
|
|
300
|
-
"thresholds": {
|
|
301
|
-
"type": "object",
|
|
302
|
-
"description": "Model-specific calibrated values; required when reranker is configured.",
|
|
303
|
-
"properties": {
|
|
304
|
-
"match_score": { "type": "number" },
|
|
305
|
-
"match_margin": { "type": "number", "minimum": 0 },
|
|
306
|
-
"candidate_floor": { "type": "number" }
|
|
307
|
-
},
|
|
308
|
-
"required": ["match_score", "match_margin", "candidate_floor"],
|
|
309
|
-
"additionalProperties": false
|
|
310
245
|
}
|
|
311
246
|
},
|
|
312
247
|
"required": ["mode", "timeout_ms", "embedding"],
|
|
@@ -332,43 +267,6 @@
|
|
|
332
267
|
},
|
|
333
268
|
"required": ["auth_enabled", "auth_token_env", "allowed_origins"],
|
|
334
269
|
"additionalProperties": false
|
|
335
|
-
},
|
|
336
|
-
"AuditRow": {
|
|
337
|
-
"type": "object",
|
|
338
|
-
"description": "AC10: one row per resolve_skill call, appended to the audit table in the state-dir SQLite. Raw query stored deliberately (single-user private homelab).",
|
|
339
|
-
"properties": {
|
|
340
|
-
"id": { "type": "integer", "minimum": 1 },
|
|
341
|
-
"ts": { "type": "string", "format": "date-time" },
|
|
342
|
-
"query": { "type": "string" },
|
|
343
|
-
"outcome": { "type": "string", "enum": ["matched", "ambiguous", "no_match"] },
|
|
344
|
-
"retrieval": { "enum": ["exact", "reranked", "hybrid", "lexical"] },
|
|
345
|
-
"degraded_from": {
|
|
346
|
-
"oneOf": [{ "enum": ["reranked", "hybrid"] }, { "type": "null" }]
|
|
347
|
-
},
|
|
348
|
-
"degradation_reason": {
|
|
349
|
-
"oneOf": [{ "$ref": "#/$defs/DegradationReason" }, { "type": "null" }]
|
|
350
|
-
},
|
|
351
|
-
"candidates": {
|
|
352
|
-
"type": "array",
|
|
353
|
-
"description": "skill_id + score of every reranked (or FTS5-ranked) candidate considered, best first.",
|
|
354
|
-
"items": {
|
|
355
|
-
"type": "object",
|
|
356
|
-
"properties": {
|
|
357
|
-
"skill_id": { "$ref": "#/$defs/SkillId" },
|
|
358
|
-
"score": { "type": ["number", "null"] }
|
|
359
|
-
},
|
|
360
|
-
"required": ["skill_id", "score"],
|
|
361
|
-
"additionalProperties": false
|
|
362
|
-
}
|
|
363
|
-
},
|
|
364
|
-
"selected_skill_id": {
|
|
365
|
-
"oneOf": [{ "$ref": "#/$defs/SkillId" }, { "type": "null" }],
|
|
366
|
-
"description": "Set iff outcome=matched."
|
|
367
|
-
},
|
|
368
|
-
"latency_ms": { "type": "integer", "minimum": 0 }
|
|
369
|
-
},
|
|
370
|
-
"required": ["id", "ts", "query", "outcome", "retrieval", "candidates", "selected_skill_id", "latency_ms"],
|
|
371
|
-
"additionalProperties": false
|
|
372
270
|
}
|
|
373
271
|
}
|
|
374
272
|
}
|
package/docs/skill-management.md
CHANGED
|
@@ -170,8 +170,13 @@ skillmux report --since 7d
|
|
|
170
170
|
skillmux report --server http://host:3000 --since 7d
|
|
171
171
|
```
|
|
172
172
|
|
|
173
|
-
|
|
174
|
-
|
|
173
|
+
`skillmux report` aggregates total requests, empty shortlist count and rate,
|
|
174
|
+
retrieval totals across lanes (`exact`, `reranked`, `hybrid`, `lexical`), degraded
|
|
175
|
+
request counts, average latency in milliseconds, per-skill `candidate_count`, and
|
|
176
|
+
`top_empty_shortlist_queries`.
|
|
177
|
+
|
|
178
|
+
Skills with high candidate counts may belong in `[core]` or a project group.
|
|
179
|
+
Top empty shortlist queries point to missing skills or weak skill descriptions.
|
|
175
180
|
|
|
176
181
|
`--since` accepts windows such as `1h`, `7d`, and `1m`, plus absolute dates and
|
|
177
182
|
timestamps.
|
package/docs/troubleshooting.md
CHANGED
|
@@ -17,7 +17,7 @@ file or an existing `~/.config/skillmux` directory. If you need a specific
|
|
|
17
17
|
vault, remote inference, or HTTP policy, create a config with `skillmux config
|
|
18
18
|
init --vault ~/skills --yes`; otherwise the server uses its defaults. A missing
|
|
19
19
|
optional config parent disables live reload until the next server start. For a
|
|
20
|
-
malformed watched config, check the reported reload error
|
|
20
|
+
malformed watched config, check the reported reload error: the running server
|
|
21
21
|
continues with its last known good configuration.
|
|
22
22
|
|
|
23
23
|
## Docker rejected a CLI command
|
|
@@ -25,7 +25,7 @@ continues with its last known good configuration.
|
|
|
25
25
|
The server image can inspect and serve a mounted vault, but it cannot manage
|
|
26
26
|
agent directories or mutate host configuration. Its help lists the supported
|
|
27
27
|
server commands. For `init`, `sync`, install or pin management, model
|
|
28
|
-
downloads, contexts,
|
|
28
|
+
downloads, contexts, evaluation, project/target/local-vault
|
|
29
29
|
management, or `config init`/`config set`, install and use the host CLI:
|
|
30
30
|
|
|
31
31
|
```sh
|
|
@@ -149,20 +149,15 @@ overlay.
|
|
|
149
149
|
|
|
150
150
|
## Retrieval failures
|
|
151
151
|
|
|
152
|
-
###
|
|
152
|
+
### Candidates are missing or irrelevant
|
|
153
153
|
|
|
154
|
-
|
|
155
|
-
|
|
154
|
+
`resolve_skill` returns ranked candidate summaries for the calling model to
|
|
155
|
+
review. If the shortlist is empty (`candidates: []`) or missing an expected skill:
|
|
156
156
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
-
|
|
160
|
-
-
|
|
161
|
-
- increasing recall depth when the relevant skill falls outside the fused
|
|
162
|
-
candidate set.
|
|
163
|
-
|
|
164
|
-
Use a labelled dataset and [Policy calibration](calibration.md) before enabling
|
|
165
|
-
automatic matches.
|
|
157
|
+
- write a concrete skill description with specific task vocabulary and user intents in `SKILL.md`;
|
|
158
|
+
- verify embeddings are active with `skillmux doctor`;
|
|
159
|
+
- increase candidate recall depth in `[recall]` (`k_lexical`, `k_vector`, or `k_rerank`);
|
|
160
|
+
- evaluate ranking quality against a labelled dataset with `skillmux eval` (see [Ranked-shortlist migration](ranked-shortlist-migration.md)).
|
|
166
161
|
|
|
167
162
|
### Server reports lexical mode
|
|
168
163
|
|
|
@@ -256,6 +251,9 @@ checking for a retry loop or shared-token traffic.
|
|
|
256
251
|
|
|
257
252
|
Skillmux rejects removed fields with migration guidance:
|
|
258
253
|
|
|
254
|
+
- replace obsolete `[thresholds]` table and `inference.thresholds` with `[output]` and `top_k`;
|
|
255
|
+
- replace obsolete `output.ambiguous_candidate_limit` with `output.top_k`;
|
|
256
|
+
- remove obsolete `inference.calibration` and use `skillmux eval` for ranking evaluation;
|
|
259
257
|
- replace `[targets.<name>].project` with `project_groups = [...]`;
|
|
260
258
|
- rename `[project.<group>].repos` to `paths`;
|
|
261
259
|
- use `skillmux core pin|unpin` instead of removed `manifest pin|unpin`;
|
package/package.json
CHANGED